diff --git a/CMakeLists.txt b/CMakeLists.txt index 79c10bd2..68d8a478 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,6 +7,8 @@ include(GenerateExportHeader) find_package(KDE4 4.22.0 REQUIRED) include(KDE4Defaults) +set(LIBRARY_TYPE SHARED) + find_package(ZLIB) set_package_properties(ZLIB PROPERTIES DESCRIPTION "Support for gzip compressed files and data streams" @@ -30,6 +32,14 @@ set_package_properties(SharedMimeInfo PROPERTIES TYPE REQUIRED ) +find_package(Sudo) +set_package_properties(Sudo PROPERTIES + DESCRIPTION "Sudo allows a system administrator to delegate authority to give certain users" + URL "https://www.sudo.ws/" + TYPE RUNTIME + PURPOSE "Needed for kdesudo to operate" +) + if(Q_WS_X11) find_package(XCB REQUIRED) set_package_properties(XCB PROPERTIES @@ -120,14 +130,6 @@ set_package_properties(Fontconfig PROPERTIES TYPE OPTIONAL ) -macro_optional_find_package(LibKonq) -set_package_properties(LibKonq PROPERTIES - DESCRIPTION "Konqueror library" - URL "http://fluxer.github.io/katana/" - PURPOSE "folderview plasma applets" - TYPE OPTIONAL -) - macro_optional_find_package(LibUSB) set_package_properties(LibUSB PROPERTIES DESCRIPTION "User level access to USB devices" @@ -272,6 +274,30 @@ add_feature_info(gdb_or_lldb "Backtraces support" ) +find_program(CTAGS_EXECUTABLE ctags ctags-universal ctags-exuberant exctags uctags) +add_feature_info(ctags + CTAGS_EXECUTABLE + "Code indexing support in Kate" +) + +find_program(CPPCHECK_EXECUTABLE cppcheck) +add_feature_info(cppcheck + CPPCHECK_EXECUTABLE + "Code analysis support in Kate" +) + +find_program(RZSZ_EXECUTABLE NAMES rz lrz) +add_feature_info(rzsz + RZSZ_EXECUTABLE + "ZModem support in Konsole" +) + +find_program(LOCALE_EXECUTABLE NAMES locate) +add_feature_info(locate + LOCALE_EXECUTABLE + "Search indexing support in KFind" +) + check_include_files(sys/wait.h HAVE_SYS_WAIT_H) check_include_files(sys/time.h HAVE_SYS_TIME_H) @@ -346,6 +372,16 @@ add_subdirectory(kquitapp) if (Q_WS_X11) add_subdirectory(kstart) endif (Q_WS_X11) +# imported from kde-baseapps +add_subdirectory(dolphin) +add_subdirectory(kate) +add_subdirectory(kdepasswd) +add_subdirectory(kdesudo) +add_subdirectory(kdialog) +add_subdirectory(keditbookmarks) +add_subdirectory(kfind) +add_subdirectory(kmediaplayer) +add_subdirectory(konsole) # Background processes add_subdirectory(kdontchangethehostname) add_subdirectory(kglobalaccel) diff --git a/appveyor.yml b/appveyor.yml index ee225753..a8ddaa6b 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -26,7 +26,8 @@ build_script: libglu1-mesa-dev mesa-common-dev libmtp-dev libusb-1.0-0-dev libssh-dev \ libsmbclient-dev libdrm-dev libraw1394-dev libsensors4-dev \ libegl-dev libpci-dev libopenexr-dev liblzma-dev libbz2-dev libgphoto2-dev \ - liblightdm-gobject-1-dev libkmod-dev libdbusmenu-katie ccache + liblightdm-gobject-1-dev libkmod-dev libdbusmenu-katie \ + sudo ctags cppcheck lrzsz locate ccache export PATH="/usr/lib/ccache/:$PATH" diff --git a/dolphin/AUTHORS b/dolphin/AUTHORS new file mode 100644 index 00000000..9019e9fb --- /dev/null +++ b/dolphin/AUTHORS @@ -0,0 +1,6 @@ +Peter Penz +David Faure +Aaron J. Seigo +Rafael Fernández López +Kevin Ottens +Holger Freyther \ No newline at end of file diff --git a/dolphin/CMakeLists.txt b/dolphin/CMakeLists.txt new file mode 100644 index 00000000..24a879e2 --- /dev/null +++ b/dolphin/CMakeLists.txt @@ -0,0 +1,13 @@ +include(CheckStructHasMember) + +check_struct_has_member("struct dirent" d_type dirent.h HAVE_DIRENT_D_TYPE) + +configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/config-dolphin.h.cmake + ${CMAKE_CURRENT_BINARY_DIR}/config-dolphin.h +) + +include_directories(${CMAKE_CURRENT_BINARY_DIR}) + +add_subdirectory(src) +add_subdirectory(plugins) diff --git a/dolphin/COPYING b/dolphin/COPYING new file mode 100644 index 00000000..5185fd3f --- /dev/null +++ b/dolphin/COPYING @@ -0,0 +1,346 @@ +NOTE! The GPL below is copyrighted by the Free Software Foundation, but +the instance of code that it refers to (the kde programs) are copyrighted +by the authors who actually wrote it. + +--------------------------------------------------------------------------- + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/dolphin/COPYING.DOC b/dolphin/COPYING.DOC new file mode 100644 index 00000000..4a0fe1c8 --- /dev/null +++ b/dolphin/COPYING.DOC @@ -0,0 +1,397 @@ + GNU Free Documentation License + Version 1.2, November 2002 + + + Copyright (C) 2000,2001,2002 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + +0. PREAMBLE + +The purpose of this License is to make a manual, textbook, or other +functional and useful document "free" in the sense of freedom: to +assure everyone the effective freedom to copy and redistribute it, +with or without modifying it, either commercially or noncommercially. +Secondarily, this License preserves for the author and publisher a way +to get credit for their work, while not being considered responsible +for modifications made by others. + +This License is a kind of "copyleft", which means that derivative +works of the document must themselves be free in the same sense. It +complements the GNU General Public License, which is a copyleft +license designed for free software. + +We have designed this License in order to use it for manuals for free +software, because free software needs free documentation: a free +program should come with manuals providing the same freedoms that the +software does. But this License is not limited to software manuals; +it can be used for any textual work, regardless of subject matter or +whether it is published as a printed book. We recommend this License +principally for works whose purpose is instruction or reference. + + +1. APPLICABILITY AND DEFINITIONS + +This License applies to any manual or other work, in any medium, that +contains a notice placed by the copyright holder saying it can be +distributed under the terms of this License. Such a notice grants a +world-wide, royalty-free license, unlimited in duration, to use that +work under the conditions stated herein. The "Document", below, +refers to any such manual or work. Any member of the public is a +licensee, and is addressed as "you". You accept the license if you +copy, modify or distribute the work in a way requiring permission +under copyright law. + +A "Modified Version" of the Document means any work containing the +Document or a portion of it, either copied verbatim, or with +modifications and/or translated into another language. + +A "Secondary Section" is a named appendix or a front-matter section of +the Document that deals exclusively with the relationship of the +publishers or authors of the Document to the Document's overall subject +(or to related matters) and contains nothing that could fall directly +within that overall subject. (Thus, if the Document is in part a +textbook of mathematics, a Secondary Section may not explain any +mathematics.) The relationship could be a matter of historical +connection with the subject or with related matters, or of legal, +commercial, philosophical, ethical or political position regarding +them. + +The "Invariant Sections" are certain Secondary Sections whose titles +are designated, as being those of Invariant Sections, in the notice +that says that the Document is released under this License. If a +section does not fit the above definition of Secondary then it is not +allowed to be designated as Invariant. The Document may contain zero +Invariant Sections. If the Document does not identify any Invariant +Sections then there are none. + +The "Cover Texts" are certain short passages of text that are listed, +as Front-Cover Texts or Back-Cover Texts, in the notice that says that +the Document is released under this License. A Front-Cover Text may +be at most 5 words, and a Back-Cover Text may be at most 25 words. + +A "Transparent" copy of the Document means a machine-readable copy, +represented in a format whose specification is available to the +general public, that is suitable for revising the document +straightforwardly with generic text editors or (for images composed of +pixels) generic paint programs or (for drawings) some widely available +drawing editor, and that is suitable for input to text formatters or +for automatic translation to a variety of formats suitable for input +to text formatters. A copy made in an otherwise Transparent file +format whose markup, or absence of markup, has been arranged to thwart +or discourage subsequent modification by readers is not Transparent. +An image format is not Transparent if used for any substantial amount +of text. A copy that is not "Transparent" is called "Opaque". + +Examples of suitable formats for Transparent copies include plain +ASCII without markup, Texinfo input format, LaTeX input format, SGML +or XML using a publicly available DTD, and standard-conforming simple +HTML, PostScript or PDF designed for human modification. Examples of +transparent image formats include PNG, XCF and JPG. Opaque formats +include proprietary formats that can be read and edited only by +proprietary word processors, SGML or XML for which the DTD and/or +processing tools are not generally available, and the +machine-generated HTML, PostScript or PDF produced by some word +processors for output purposes only. + +The "Title Page" means, for a printed book, the title page itself, +plus such following pages as are needed to hold, legibly, the material +this License requires to appear in the title page. For works in +formats which do not have any title page as such, "Title Page" means +the text near the most prominent appearance of the work's title, +preceding the beginning of the body of the text. + +A section "Entitled XYZ" means a named subunit of the Document whose +title either is precisely XYZ or contains XYZ in parentheses following +text that translates XYZ in another language. (Here XYZ stands for a +specific section name mentioned below, such as "Acknowledgements", +"Dedications", "Endorsements", or "History".) To "Preserve the Title" +of such a section when you modify the Document means that it remains a +section "Entitled XYZ" according to this definition. + +The Document may include Warranty Disclaimers next to the notice which +states that this License applies to the Document. These Warranty +Disclaimers are considered to be included by reference in this +License, but only as regards disclaiming warranties: any other +implication that these Warranty Disclaimers may have is void and has +no effect on the meaning of this License. + + +2. VERBATIM COPYING + +You may copy and distribute the Document in any medium, either +commercially or noncommercially, provided that this License, the +copyright notices, and the license notice saying this License applies +to the Document are reproduced in all copies, and that you add no other +conditions whatsoever to those of this License. You may not use +technical measures to obstruct or control the reading or further +copying of the copies you make or distribute. However, you may accept +compensation in exchange for copies. If you distribute a large enough +number of copies you must also follow the conditions in section 3. + +You may also lend copies, under the same conditions stated above, and +you may publicly display copies. + + +3. COPYING IN QUANTITY + +If you publish printed copies (or copies in media that commonly have +printed covers) of the Document, numbering more than 100, and the +Document's license notice requires Cover Texts, you must enclose the +copies in covers that carry, clearly and legibly, all these Cover +Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on +the back cover. Both covers must also clearly and legibly identify +you as the publisher of these copies. The front cover must present +the full title with all words of the title equally prominent and +visible. You may add other material on the covers in addition. +Copying with changes limited to the covers, as long as they preserve +the title of the Document and satisfy these conditions, can be treated +as verbatim copying in other respects. + +If the required texts for either cover are too voluminous to fit +legibly, you should put the first ones listed (as many as fit +reasonably) on the actual cover, and continue the rest onto adjacent +pages. + +If you publish or distribute Opaque copies of the Document numbering +more than 100, you must either include a machine-readable Transparent +copy along with each Opaque copy, or state in or with each Opaque copy +a computer-network location from which the general network-using +public has access to download using public-standard network protocols +a complete Transparent copy of the Document, free of added material. +If you use the latter option, you must take reasonably prudent steps, +when you begin distribution of Opaque copies in quantity, to ensure +that this Transparent copy will remain thus accessible at the stated +location until at least one year after the last time you distribute an +Opaque copy (directly or through your agents or retailers) of that +edition to the public. + +It is requested, but not required, that you contact the authors of the +Document well before redistributing any large number of copies, to give +them a chance to provide you with an updated version of the Document. + + +4. MODIFICATIONS + +You may copy and distribute a Modified Version of the Document under +the conditions of sections 2 and 3 above, provided that you release +the Modified Version under precisely this License, with the Modified +Version filling the role of the Document, thus licensing distribution +and modification of the Modified Version to whoever possesses a copy +of it. In addition, you must do these things in the Modified Version: + +A. Use in the Title Page (and on the covers, if any) a title distinct + from that of the Document, and from those of previous versions + (which should, if there were any, be listed in the History section + of the Document). You may use the same title as a previous version + if the original publisher of that version gives permission. +B. List on the Title Page, as authors, one or more persons or entities + responsible for authorship of the modifications in the Modified + Version, together with at least five of the principal authors of the + Document (all of its principal authors, if it has fewer than five), + unless they release you from this requirement. +C. State on the Title page the name of the publisher of the + Modified Version, as the publisher. +D. Preserve all the copyright notices of the Document. +E. Add an appropriate copyright notice for your modifications + adjacent to the other copyright notices. +F. Include, immediately after the copyright notices, a license notice + giving the public permission to use the Modified Version under the + terms of this License, in the form shown in the Addendum below. +G. Preserve in that license notice the full lists of Invariant Sections + and required Cover Texts given in the Document's license notice. +H. Include an unaltered copy of this License. +I. Preserve the section Entitled "History", Preserve its Title, and add + to it an item stating at least the title, year, new authors, and + publisher of the Modified Version as given on the Title Page. If + there is no section Entitled "History" in the Document, create one + stating the title, year, authors, and publisher of the Document as + given on its Title Page, then add an item describing the Modified + Version as stated in the previous sentence. +J. Preserve the network location, if any, given in the Document for + public access to a Transparent copy of the Document, and likewise + the network locations given in the Document for previous versions + it was based on. These may be placed in the "History" section. + You may omit a network location for a work that was published at + least four years before the Document itself, or if the original + publisher of the version it refers to gives permission. +K. For any section Entitled "Acknowledgements" or "Dedications", + Preserve the Title of the section, and preserve in the section all + the substance and tone of each of the contributor acknowledgements + and/or dedications given therein. +L. Preserve all the Invariant Sections of the Document, + unaltered in their text and in their titles. Section numbers + or the equivalent are not considered part of the section titles. +M. Delete any section Entitled "Endorsements". Such a section + may not be included in the Modified Version. +N. Do not retitle any existing section to be Entitled "Endorsements" + or to conflict in title with any Invariant Section. +O. Preserve any Warranty Disclaimers. + +If the Modified Version includes new front-matter sections or +appendices that qualify as Secondary Sections and contain no material +copied from the Document, you may at your option designate some or all +of these sections as invariant. To do this, add their titles to the +list of Invariant Sections in the Modified Version's license notice. +These titles must be distinct from any other section titles. + +You may add a section Entitled "Endorsements", provided it contains +nothing but endorsements of your Modified Version by various +parties--for example, statements of peer review or that the text has +been approved by an organization as the authoritative definition of a +standard. + +You may add a passage of up to five words as a Front-Cover Text, and a +passage of up to 25 words as a Back-Cover Text, to the end of the list +of Cover Texts in the Modified Version. Only one passage of +Front-Cover Text and one of Back-Cover Text may be added by (or +through arrangements made by) any one entity. If the Document already +includes a cover text for the same cover, previously added by you or +by arrangement made by the same entity you are acting on behalf of, +you may not add another; but you may replace the old one, on explicit +permission from the previous publisher that added the old one. + +The author(s) and publisher(s) of the Document do not by this License +give permission to use their names for publicity for or to assert or +imply endorsement of any Modified Version. + + +5. COMBINING DOCUMENTS + +You may combine the Document with other documents released under this +License, under the terms defined in section 4 above for modified +versions, provided that you include in the combination all of the +Invariant Sections of all of the original documents, unmodified, and +list them all as Invariant Sections of your combined work in its +license notice, and that you preserve all their Warranty Disclaimers. + +The combined work need only contain one copy of this License, and +multiple identical Invariant Sections may be replaced with a single +copy. If there are multiple Invariant Sections with the same name but +different contents, make the title of each such section unique by +adding at the end of it, in parentheses, the name of the original +author or publisher of that section if known, or else a unique number. +Make the same adjustment to the section titles in the list of +Invariant Sections in the license notice of the combined work. + +In the combination, you must combine any sections Entitled "History" +in the various original documents, forming one section Entitled +"History"; likewise combine any sections Entitled "Acknowledgements", +and any sections Entitled "Dedications". You must delete all sections +Entitled "Endorsements". + + +6. COLLECTIONS OF DOCUMENTS + +You may make a collection consisting of the Document and other documents +released under this License, and replace the individual copies of this +License in the various documents with a single copy that is included in +the collection, provided that you follow the rules of this License for +verbatim copying of each of the documents in all other respects. + +You may extract a single document from such a collection, and distribute +it individually under this License, provided you insert a copy of this +License into the extracted document, and follow this License in all +other respects regarding verbatim copying of that document. + + +7. AGGREGATION WITH INDEPENDENT WORKS + +A compilation of the Document or its derivatives with other separate +and independent documents or works, in or on a volume of a storage or +distribution medium, is called an "aggregate" if the copyright +resulting from the compilation is not used to limit the legal rights +of the compilation's users beyond what the individual works permit. +When the Document is included in an aggregate, this License does not +apply to the other works in the aggregate which are not themselves +derivative works of the Document. + +If the Cover Text requirement of section 3 is applicable to these +copies of the Document, then if the Document is less than one half of +the entire aggregate, the Document's Cover Texts may be placed on +covers that bracket the Document within the aggregate, or the +electronic equivalent of covers if the Document is in electronic form. +Otherwise they must appear on printed covers that bracket the whole +aggregate. + + +8. TRANSLATION + +Translation is considered a kind of modification, so you may +distribute translations of the Document under the terms of section 4. +Replacing Invariant Sections with translations requires special +permission from their copyright holders, but you may include +translations of some or all Invariant Sections in addition to the +original versions of these Invariant Sections. You may include a +translation of this License, and all the license notices in the +Document, and any Warranty Disclaimers, provided that you also include +the original English version of this License and the original versions +of those notices and disclaimers. In case of a disagreement between +the translation and the original version of this License or a notice +or disclaimer, the original version will prevail. + +If a section in the Document is Entitled "Acknowledgements", +"Dedications", or "History", the requirement (section 4) to Preserve +its Title (section 1) will typically require changing the actual +title. + + +9. TERMINATION + +You may not copy, modify, sublicense, or distribute the Document except +as expressly provided for under this License. Any other attempt to +copy, modify, sublicense or distribute the Document is void, and will +automatically terminate your rights under this License. However, +parties who have received copies, or rights, from you under this +License will not have their licenses terminated so long as such +parties remain in full compliance. + + +10. FUTURE REVISIONS OF THIS LICENSE + +The Free Software Foundation may publish new, revised versions +of the GNU Free Documentation 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. See +http://www.gnu.org/copyleft/. + +Each version of the License is given a distinguishing version number. +If the Document specifies that a particular numbered version of this +License "or any later version" applies to it, you have the option of +following the terms and conditions either of that specified version or +of any later version that has been published (not as a draft) by the +Free Software Foundation. If the Document does not specify a version +number of this License, you may choose any version ever published (not +as a draft) by the Free Software Foundation. + + +ADDENDUM: How to use this License for your documents + +To use this License in a document you have written, include a copy of +the License in the document and put the following copyright and +license notices just after the title page: + + Copyright (c) YEAR YOUR NAME. + Permission is granted to copy, distribute and/or modify this document + under the terms of the GNU Free Documentation License, Version 1.2 + or any later version published by the Free Software Foundation; + with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. + A copy of the license is included in the section entitled "GNU + Free Documentation License". + +If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts, +replace the "with...Texts." line with this: + + with the Invariant Sections being LIST THEIR TITLES, with the + Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST. + +If you have Invariant Sections without Cover Texts, or some other +combination of the three, merge those two alternatives to suit the +situation. + +If your document contains nontrivial examples of program code, we +recommend releasing these examples in parallel under your choice of +free software license, such as the GNU General Public License, +to permit their use in free software. diff --git a/dolphin/README b/dolphin/README new file mode 100644 index 00000000..9c2c9131 --- /dev/null +++ b/dolphin/README @@ -0,0 +1,2 @@ +See http://dolphin.kde.org for information about Dolphin. + diff --git a/dolphin/config-dolphin.h.cmake b/dolphin/config-dolphin.h.cmake new file mode 100644 index 00000000..004c8a5f --- /dev/null +++ b/dolphin/config-dolphin.h.cmake @@ -0,0 +1,4 @@ +/* config.h. Generated by cmake from config.h.cmake */ + +/* Defined to 1 if you have a d_type member in struct dirent */ +#cmakedefine HAVE_DIRENT_D_TYPE 1 diff --git a/dolphin/plugins/CMakeLists.txt b/dolphin/plugins/CMakeLists.txt new file mode 100644 index 00000000..52f8302b --- /dev/null +++ b/dolphin/plugins/CMakeLists.txt @@ -0,0 +1,10 @@ +include_directories( + ${CMAKE_SOURCE_DIR}/libs/konq + ${CMAKE_BINARY_DIR}/libs/konq +) + +add_subdirectory(svn) +add_subdirectory(git) +add_subdirectory(hg) +add_subdirectory(bazaar) +add_subdirectory(dropbox) diff --git a/dolphin/plugins/COPYING b/dolphin/plugins/COPYING new file mode 100644 index 00000000..5185fd3f --- /dev/null +++ b/dolphin/plugins/COPYING @@ -0,0 +1,346 @@ +NOTE! The GPL below is copyrighted by the Free Software Foundation, but +the instance of code that it refers to (the kde programs) are copyrighted +by the authors who actually wrote it. + +--------------------------------------------------------------------------- + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/dolphin/plugins/bazaar/CMakeLists.txt b/dolphin/plugins/bazaar/CMakeLists.txt new file mode 100644 index 00000000..46748b9f --- /dev/null +++ b/dolphin/plugins/bazaar/CMakeLists.txt @@ -0,0 +1,16 @@ +project(fileviewbazaarplugin) + +kde4_add_plugin(fileviewbazaarplugin fileviewbazaarplugin.cpp) + +target_link_libraries(fileviewbazaarplugin ${KDE4_KIO_LIBS} konq) + +install( + FILES + fileviewbazaarplugin.desktop + DESTINATION ${KDE4_SERVICES_INSTALL_DIR} +) + +install( + TARGETS fileviewbazaarplugin + DESTINATION ${KDE4_PLUGIN_INSTALL_DIR} +) diff --git a/dolphin/plugins/bazaar/Messages.sh b/dolphin/plugins/bazaar/Messages.sh new file mode 100644 index 00000000..3dc3881e --- /dev/null +++ b/dolphin/plugins/bazaar/Messages.sh @@ -0,0 +1,2 @@ +#! /bin/sh +$XGETTEXT *.cpp -o $podir/fileviewbazaarplugin.pot diff --git a/dolphin/plugins/bazaar/fileviewbazaarplugin.cpp b/dolphin/plugins/bazaar/fileviewbazaarplugin.cpp new file mode 100644 index 00000000..66f667c3 --- /dev/null +++ b/dolphin/plugins/bazaar/fileviewbazaarplugin.cpp @@ -0,0 +1,464 @@ +/*************************************************************************** + * Copyright (C) 2009-2010 by Peter Penz * + * Copyright (C) 2011 Canonical Ltd. * + * By Jonathan Riddell * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "fileviewbazaarplugin.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +K_PLUGIN_FACTORY(FileViewBazaarPluginFactory,registerPlugin();) +K_EXPORT_PLUGIN(FileViewBazaarPluginFactory("fileviewbazaarplugin")) + +FileViewBazaarPlugin::FileViewBazaarPlugin(QObject* parent, const QList& args) : + KVersionControlPlugin(parent), + m_pendingOperation(false), + m_versionInfoHash(), + m_updateAction(0), + m_pullAction(0), + m_pushAction(0), + m_showLocalChangesAction(0), + m_commitAction(0), + m_addAction(0), + m_removeAction(0), + m_logAction(0), + m_command(), + m_arguments(), + m_errorMsg(), + m_operationCompletedMsg(), + m_contextDir(), + m_contextItems(), + m_process(), + m_tempFile() +{ + Q_UNUSED(args); + + m_updateAction = new KAction(this); + m_updateAction->setIcon(KIcon("go-down")); + m_updateAction->setText(i18nc("@item:inmenu", "Bazaar Update")); + connect(m_updateAction, SIGNAL(triggered()), + this, SLOT(updateFiles())); + + m_pullAction = new KAction(this); + m_pullAction->setIcon(KIcon("go-bottom")); + m_pullAction->setText(i18nc("@item:inmenu", "Bazaar Pull")); + connect(m_pullAction, SIGNAL(triggered()), + this, SLOT(pullFiles())); + + m_pushAction = new KAction(this); + m_pushAction->setIcon(KIcon("go-top")); + m_pushAction->setText(i18nc("@item:inmenu", "Bazaar Push")); + connect(m_pushAction, SIGNAL(triggered()), + this, SLOT(pushFiles())); + + m_showLocalChangesAction = new KAction(this); + m_showLocalChangesAction->setIcon(KIcon("view-split-left-right")); + m_showLocalChangesAction->setText(i18nc("@item:inmenu", "Show Local Bazaar Changes")); + connect(m_showLocalChangesAction, SIGNAL(triggered()), + this, SLOT(showLocalChanges())); + + m_commitAction = new KAction(this); + m_commitAction->setIcon(KIcon("svn-commit")); + m_commitAction->setText(i18nc("@item:inmenu", "Bazaar Commit...")); + connect(m_commitAction, SIGNAL(triggered()), + this, SLOT(commitFiles())); + + m_addAction = new KAction(this); + m_addAction->setIcon(KIcon("list-add")); + m_addAction->setText(i18nc("@item:inmenu", "Bazaar Add...")); + connect(m_addAction, SIGNAL(triggered()), + this, SLOT(addFiles())); + + m_removeAction = new KAction(this); + m_removeAction->setIcon(KIcon("list-remove")); + m_removeAction->setText(i18nc("@item:inmenu", "Bazaar Delete")); + connect(m_removeAction, SIGNAL(triggered()), + this, SLOT(removeFiles())); + + m_logAction = new KAction(this); + m_logAction->setIcon(KIcon("format-list-ordered")); + m_logAction->setText(i18nc("@item:inmenu", "Bazaar Log")); + connect(m_logAction, SIGNAL(triggered()), + this, SLOT(log())); + + connect(&m_process, SIGNAL(finished(int, QProcess::ExitStatus)), + this, SLOT(slotOperationCompleted(int, QProcess::ExitStatus))); + connect(&m_process, SIGNAL(error(QProcess::ProcessError)), + this, SLOT(slotOperationError())); +} + +FileViewBazaarPlugin::~FileViewBazaarPlugin() +{ +} + +QString FileViewBazaarPlugin::fileName() const +{ + return QLatin1String(".bzr"); +} + +bool FileViewBazaarPlugin::beginRetrieval(const QString& directory) +{ + Q_ASSERT(directory.endsWith(QLatin1Char('/'))); + + QString baseDir; + QProcess process1; + process1.setWorkingDirectory(directory); + process1.start(QLatin1String("bzr root")); + while (process1.waitForReadyRead()) { + char buffer[512]; + while (process1.readLine(buffer, sizeof(buffer)) > 0) { + baseDir = QString(buffer).trimmed(); + } + } + // if bzr is not installed + if (baseDir == "") { + return false; + } + + // Clear all entries for this directory including the entries + // for sub directories + QMutableHashIterator it(m_versionInfoHash); + while (it.hasNext()) { + it.next(); + if (it.key().startsWith(directory) || !it.key().startsWith(baseDir)) { + it.remove(); + } + } + + QProcess process2; + process2.setWorkingDirectory(directory); + process2.start(QLatin1String("bzr ignored")); + while (process2.waitForReadyRead()) { + char buffer[512]; + while (process2.readLine(buffer, sizeof(buffer)) > 0) { + QString line = QString(buffer).trimmed(); + QStringList list = line.split(" "); + QString file = baseDir + "/" + list[0]; + m_versionInfoHash.insert(file, UnversionedVersion); + } + } + + QStringList arguments; + arguments << QLatin1String("status") << QLatin1String("-S"); + arguments << baseDir; + + QProcess process; + process.start(QLatin1String("bzr"), arguments); + while (process.waitForReadyRead()) { + char buffer[1024]; + while (process.readLine(buffer, sizeof(buffer)) > 0) { + ItemVersion state = NormalVersion; + QString filePath = QString::fromUtf8(buffer); + + // This could probably do with being more consistent + switch (buffer[0]) { + case '?': state = UnversionedVersion; break; + case ' ': if (buffer[1] == 'M') {state = LocallyModifiedVersion;} break; + case '+': state = AddedVersion; break; + case '-': state = RemovedVersion; break; + case 'C': state = ConflictingVersion; break; + default: + if (filePath.contains('*')) { + state = UpdateRequiredVersion; + } + break; + } + + // Only values with a different state as 'NormalVersion' + // are added to the hash table. If a value is not in the + // hash table, it is automatically defined as 'NormalVersion' + // (see FileViewBazaarPlugin::itemVersion()). + if (state != NormalVersion) { + int pos = 4; + const int length = filePath.length() - pos - 1; + //conflicts annoyingly have a human readable text before the filename + //TODO cover other conflict types + if (filePath.startsWith("C Text conflict")) { + filePath = filePath.mid(17, length); + } + filePath = baseDir + "/" + filePath.mid(pos, length); + //remove type symbols from directories, links and executables + if (filePath.endsWith("/") || filePath.endsWith("@") || filePath.endsWith("*")) { + filePath = filePath.left(filePath.length() - 1); + } + if (!filePath.isEmpty()) { + m_versionInfoHash.insert(filePath, state); + } + } + } + } + if ((process.exitCode() != 0 || process.exitStatus() != QProcess::NormalExit)) { + return false; + } + + return true; +} + +void FileViewBazaarPlugin::endRetrieval() +{ +} + +KVersionControlPlugin::ItemVersion FileViewBazaarPlugin::itemVersion(const KFileItem& item) const +{ + const QString itemUrl = item.localPath(); + if (m_versionInfoHash.contains(itemUrl)) { + return m_versionInfoHash.value(itemUrl); + } + + if (!item.isDir()) { + // files that have not been listed by 'bzr status' or 'bzr ignored' (= m_versionInfoHash) + // are under version control per definition + return NormalVersion; + } + + // The item is a directory. Check whether an item listed by 'bzr status' (= m_versionInfoHash) + // is part of this directory. In this case a local modification should be indicated in the + // directory already. + QHash::const_iterator it = m_versionInfoHash.constBegin(); + while (it != m_versionInfoHash.constEnd()) { + if (it.key().startsWith(itemUrl)) { + const ItemVersion state = m_versionInfoHash.value(it.key()); + if (state == LocallyModifiedVersion) { + return LocallyModifiedVersion; + } + } + ++it; + } + + return NormalVersion; +} + +QList FileViewBazaarPlugin::actions(const KFileItemList& items) const +{ + Q_ASSERT(!items.isEmpty()); + foreach (const KFileItem& item, items) { + m_contextItems.append(item); + } + m_contextDir.clear(); + + const bool noPendingOperation = !m_pendingOperation; + if (noPendingOperation) { + // iterate all items and check the version state to know which + // actions can be enabled + const int itemsCount = items.count(); + int versionedCount = 0; + int editingCount = 0; + foreach (const KFileItem& item, items) { + const ItemVersion state = itemVersion(item); + if (state != UnversionedVersion) { + ++versionedCount; + } + + switch (state) { + case LocallyModifiedVersion: + case ConflictingVersion: + ++editingCount; + break; + default: + break; + } + } + m_commitAction->setEnabled(editingCount > 0); + m_addAction->setEnabled(versionedCount == 0); + m_removeAction->setEnabled(versionedCount == itemsCount); + } else { + m_commitAction->setEnabled(false); + m_addAction->setEnabled(false); + m_removeAction->setEnabled(false); + } + m_updateAction->setEnabled(noPendingOperation); + m_pullAction->setEnabled(noPendingOperation); + m_pushAction->setEnabled(noPendingOperation); + m_showLocalChangesAction->setEnabled(noPendingOperation); + m_logAction->setEnabled(noPendingOperation); + + QList actions; + actions.append(m_updateAction); + actions.append(m_pullAction); + actions.append(m_pushAction); + actions.append(m_commitAction); + actions.append(m_addAction); + actions.append(m_removeAction); + actions.append(m_showLocalChangesAction); + actions.append(m_logAction); + return actions; +} + +void FileViewBazaarPlugin::updateFiles() +{ + execBazaarCommand("qupdate", QStringList(), + i18nc("@info:status", "Updating Bazaar repository..."), + i18nc("@info:status", "Update of Bazaar repository failed."), + i18nc("@info:status", "Updated Bazaar repository.")); +} + +void FileViewBazaarPlugin::pullFiles() +{ + QStringList arguments = QStringList(); + arguments << "-d"; + execBazaarCommand("qpull", arguments, + i18nc("@info:status", "Pulling Bazaar repository..."), + i18nc("@info:status", "Pull of Bazaar repository failed."), + i18nc("@info:status", "Pulled Bazaar repository.")); +} + +void FileViewBazaarPlugin::pushFiles() +{ + QStringList arguments = QStringList(); + arguments << "-d"; + execBazaarCommand("qpush", arguments, + i18nc("@info:status", "Pushing Bazaar repository..."), + i18nc("@info:status", "Push of Bazaar repository failed."), + i18nc("@info:status", "Pushed Bazaar repository.")); +} + +void FileViewBazaarPlugin::showLocalChanges() +{ + execBazaarCommand("qdiff", QStringList(), + i18nc("@info:status", "Reviewing Changes..."), + i18nc("@info:status", "Review Changes failed."), + i18nc("@info:status", "Reviewed Changes.")); +} + +void FileViewBazaarPlugin::commitFiles() +{ + execBazaarCommand("qcommit", QStringList(), + i18nc("@info:status", "Committing Bazaar changes..."), + i18nc("@info:status", "Commit of Bazaar changes failed."), + i18nc("@info:status", "Committed Bazaar changes.")); +} + +void FileViewBazaarPlugin::addFiles() +{ + execBazaarCommand(QLatin1String("qadd"), QStringList(), + i18nc("@info:status", "Adding files to Bazaar repository..."), + i18nc("@info:status", "Adding of files to Bazaar repository failed."), + i18nc("@info:status", "Added files to Bazaar repository.")); +} + +void FileViewBazaarPlugin::removeFiles() +{ + execBazaarCommand(QLatin1String("remove"), QStringList(), + i18nc("@info:status", "Removing files from Bazaar repository..."), + i18nc("@info:status", "Removing of files from Bazaar repository failed."), + i18nc("@info:status", "Removed files from Bazaar repository.")); +} + +void FileViewBazaarPlugin::log() +{ + execBazaarCommand(QLatin1String("qlog"), QStringList(), + i18nc("@info:status", "Running Bazaar Log..."), + i18nc("@info:status", "Running Bazaar Log failed."), + i18nc("@info:status", "Bazaar Log closed.")); +} + +void FileViewBazaarPlugin::slotOperationCompleted(int exitCode, QProcess::ExitStatus exitStatus) +{ + m_pendingOperation = false; + + if ((exitStatus != QProcess::NormalExit) || (exitCode != 0)) { + emit errorMessage(m_errorMsg); + } else if (m_contextItems.isEmpty()) { + emit operationCompletedMessage(m_operationCompletedMsg); + emit itemVersionsChanged(); + } else { + startBazaarCommandProcess(); + } +} + +void FileViewBazaarPlugin::slotOperationError() +{ + // don't do any operation on other items anymore + m_contextItems.clear(); + m_pendingOperation = false; + + emit errorMessage(m_errorMsg); +} + +void FileViewBazaarPlugin::execBazaarCommand(const QString& command, + const QStringList& arguments, + const QString& infoMsg, + const QString& errorMsg, + const QString& operationCompletedMsg) +{ + emit infoMessage(infoMsg); + + QProcess process; + process.start(QLatin1String("bzr plugins")); + bool foundQbzr = false; + while (process.waitForReadyRead()) { + char buffer[512]; + while (process.readLine(buffer, sizeof(buffer)) > 0) { + QString output = QString(buffer).trimmed(); + if (output.startsWith("qbzr")) { + foundQbzr = true; + break; + } + } + } + + if (!foundQbzr) { + emit infoMessage("Please Install QBzr"); + return; + } + + m_command = command; + m_arguments = arguments; + m_errorMsg = errorMsg; + m_operationCompletedMsg = operationCompletedMsg; + + startBazaarCommandProcess(); +} + +void FileViewBazaarPlugin::startBazaarCommandProcess() +{ + Q_ASSERT(m_process.state() == QProcess::NotRunning); + m_pendingOperation = true; + + const QString program(QLatin1String("bzr")); + QStringList arguments; + arguments << m_command << m_arguments; + if (!m_contextDir.isEmpty()) { + arguments << m_contextDir; + m_contextDir.clear(); + } else { + const KFileItem item = m_contextItems.takeLast(); + arguments << item.localPath(); + // the remaining items of m_contextItems will be executed + // after the process has finished (see slotOperationFinished()) + } + m_process.start(program, arguments); +} diff --git a/dolphin/plugins/bazaar/fileviewbazaarplugin.desktop b/dolphin/plugins/bazaar/fileviewbazaarplugin.desktop new file mode 100644 index 00000000..cceee0c2 --- /dev/null +++ b/dolphin/plugins/bazaar/fileviewbazaarplugin.desktop @@ -0,0 +1,48 @@ +[Desktop Entry] +Type=Service +Name=Bazaar +Name[ar]=Bazaar +Name[bs]=Bazaar +Name[ca]=Bazaar +Name[ca@valencia]=Bazaar +Name[cs]=Bazaar +Name[da]=Bazaar +Name[de]=Bazaar +Name[el]=Bazaar +Name[en_GB]=Bazaar +Name[es]=Bazaar +Name[et]=Bazaar +Name[fi]=Bazaar +Name[fr]=Bazaar +Name[gl]=Bazaar +Name[hu]=Bazaar +Name[it]=Bazaar +Name[kk]=Bazaar +Name[ko]=Bazaar +Name[lt]=Bazaar +Name[mr]=बझार +Name[nb]=Bazaar +Name[nds]=Bazaar +Name[nl]=Bazaar +Name[pa]=ਬਜ਼ਾਰ +Name[pl]=Bazaar +Name[pt]=Bazaar +Name[pt_BR]=Bazaar +Name[ro]=Bazaar +Name[ru]=Bazaar +Name[sk]=Bazaar +Name[sl]=Bazaar +Name[sr]=Базар +Name[sr@ijekavian]=Базар +Name[sr@ijekavianlatin]=Bazaar +Name[sr@latin]=Bazaar +Name[sv]=Bazaar +Name[tr]=Bazaar +Name[ug]=Bazaar +Name[uk]=Bazaar +Name[x-test]=xxBazaarxx +Name[zh_CN]=Bazaar +Name[zh_TW]=Bazaar +X-KDE-ServiceTypes=FileViewVersionControlPlugin +MimeType=text/plain; +X-KDE-Library=fileviewbazaarplugin diff --git a/dolphin/plugins/bazaar/fileviewbazaarplugin.h b/dolphin/plugins/bazaar/fileviewbazaarplugin.h new file mode 100644 index 00000000..22bb58a9 --- /dev/null +++ b/dolphin/plugins/bazaar/fileviewbazaarplugin.h @@ -0,0 +1,104 @@ +/*************************************************************************** + * Copyright (C) 2009-2010 by Peter Penz * + * Copyright (C) 2011 Canonical Ltd. * + * By Jonathan Riddell * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef FILEVIEWBAZAARPLUGIN_H +#define FILEVIEWBAZAARPLUGIN_H + +#include +#include +#include +#include +#include + +/** + * @brief Bazaar (bzr) implementation for the KVersionControlPlugin interface. + */ +class FileViewBazaarPlugin : public KVersionControlPlugin +{ + Q_OBJECT + +public: + FileViewBazaarPlugin(QObject* parent, const QList& args); + virtual ~FileViewBazaarPlugin(); + virtual QString fileName() const; + virtual bool beginRetrieval(const QString& directory); + virtual void endRetrieval(); + virtual KVersionControlPlugin::ItemVersion itemVersion(const KFileItem& item) const; + virtual QList actions(const KFileItemList& items) const; + +private slots: + void updateFiles(); + void pullFiles(); + void pushFiles(); + void showLocalChanges(); + void commitFiles(); + void addFiles(); + void removeFiles(); + void log(); + + void slotOperationCompleted(int exitCode, QProcess::ExitStatus exitStatus); + void slotOperationError(); + +private: + /** + * Executes the command "bzr {bzrCommand}" for the files that have been + * set by getting the context menu actions (see contextMenuActions()). + * @param infoMsg Message that should be shown before the command is executed. + * @param errorMsg Message that should be shown if the execution of the command + * has been failed. + * @param operationCompletedMsg + * Message that should be shown if the execution of the command + * has been completed successfully. + */ + void execBazaarCommand(const QString& bzrCommand, + const QStringList& arguments, + const QString& infoMsg, + const QString& errorMsg, + const QString& operationCompletedMsg); + + void startBazaarCommandProcess(); + +private: + bool m_pendingOperation; + QHash m_versionInfoHash; + + QAction* m_updateAction; + QAction* m_pullAction; + QAction* m_pushAction; + QAction* m_showLocalChangesAction; + QAction* m_commitAction; + QAction* m_addAction; + QAction* m_removeAction; + QAction* m_logAction; + + QString m_command; + QStringList m_arguments; + QString m_errorMsg; + QString m_operationCompletedMsg; + + mutable QString m_contextDir; + mutable KFileItemList m_contextItems; + + QProcess m_process; + QTemporaryFile m_tempFile; +}; +#endif // FILEVIEWBAZAARPLUGIN_H + diff --git a/dolphin/plugins/dropbox/CMakeLists.txt b/dolphin/plugins/dropbox/CMakeLists.txt new file mode 100644 index 00000000..1668fc98 --- /dev/null +++ b/dolphin/plugins/dropbox/CMakeLists.txt @@ -0,0 +1,15 @@ +project(fileviewdropboxplugin) + +kde4_add_plugin(fileviewdropboxplugin fileviewdropboxplugin.cpp) + +target_link_libraries(fileviewdropboxplugin ${KDE4_KIO_LIBS} konq) + +install( + FILES fileviewdropboxplugin.desktop + DESTINATION ${KDE4_SERVICES_INSTALL_DIR} +) + +install( + TARGETS fileviewdropboxplugin + DESTINATION ${KDE4_PLUGIN_INSTALL_DIR} +) \ No newline at end of file diff --git a/dolphin/plugins/dropbox/fileviewdropboxplugin.cpp b/dolphin/plugins/dropbox/fileviewdropboxplugin.cpp new file mode 100644 index 00000000..4274b19c --- /dev/null +++ b/dolphin/plugins/dropbox/fileviewdropboxplugin.cpp @@ -0,0 +1,222 @@ +/***************************************************************************** + * Copyright (C) 2014 by Emmanuel Pescosta * + * Copyright (C) 2012 by Sergei Stolyarov * + * Copyright (C) 2010 by Thomas Richard * + * Copyright (C) 2009-2010 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + *****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "fileviewdropboxplugin.h" + +K_PLUGIN_FACTORY(FileViewDropboxPluginFactory, registerPlugin();) +K_EXPORT_PLUGIN(FileViewDropboxPluginFactory("fileviewdropboxplugin")) + +class FileViewDropboxPlugin::Private +{ +public: + Private(FileViewDropboxPlugin* parent) : + contextFilePaths(), + controlSocketPath(), + controlSocket(new QLocalSocket(parent)), + databaseFileWatcher(new QFileSystemWatcher(parent)), + contextActions(new KActionCollection(parent)) + { + } + + QStringList contextFilePaths; + QString controlSocketPath; + QPointer controlSocket; + QPointer itemStateSocket; + QPointer databaseFileWatcher; + QPointer contextActions; +}; + +QMap FileViewDropboxPlugin::m_itemVersions; + +FileViewDropboxPlugin::FileViewDropboxPlugin(QObject* parent, const QVariantList& args): + KVersionControlPlugin(parent), + d(new Private(this)) +{ + Q_UNUSED(args); + + if (m_itemVersions.isEmpty()) { + m_itemVersions.insert("up to date", KVersionControlPlugin::NormalVersion); + m_itemVersions.insert("syncing", KVersionControlPlugin::UpdateRequiredVersion); + m_itemVersions.insert("unsyncable", KVersionControlPlugin::ConflictingVersion); + m_itemVersions.insert("unwatched", KVersionControlPlugin::UnversionedVersion); + } + + const QString dropboxDir = QDir::home().path() + QDir::separator() + fileName() + QDir::separator(); + d->controlSocketPath = QDir::toNativeSeparators(dropboxDir + QLatin1String("command_socket")); + d->controlSocket->connectToServer(d->controlSocketPath); + + connect(d->databaseFileWatcher, SIGNAL(fileChanged(QString)), SIGNAL(itemVersionsChanged())); + d->databaseFileWatcher->addPath(QDir::toNativeSeparators(dropboxDir + QLatin1String("aggregation.dbx"))); + + connect(d->contextActions, SIGNAL(actionTriggered(QAction*)), SLOT(handleContextAction(QAction*))); +} + +FileViewDropboxPlugin::~FileViewDropboxPlugin() +{ + delete d; +} + +QString FileViewDropboxPlugin::fileName() const +{ + return QLatin1String(".dropbox"); +} + +bool FileViewDropboxPlugin::beginRetrieval(const QString& directory) +{ + Q_UNUSED(directory); + Q_ASSERT(directory.endsWith(QLatin1Char('/'))); + + qRegisterMetaType("QAbstractSocket::SocketError"); + qRegisterMetaType("QAbstractSocket::SocketState"); + + d->itemStateSocket = new QLocalSocket; + + return connectWithDropbox(d->itemStateSocket, LongTimeout); +} + +KVersionControlPlugin::ItemVersion FileViewDropboxPlugin::itemVersion(const KFileItem& item) const +{ + const QStringList reply = sendCommand("icon_overlay_file_status\npath\t", QStringList() << QDir(item.localPath()).canonicalPath(), + d->itemStateSocket, WaitForReply, LongTimeout); + if(reply.count() < 2) { + // file/dir is not served by dropbox + return KVersionControlPlugin::UnversionedVersion; + } + + return m_itemVersions.value(reply.at(1), KVersionControlPlugin::UnversionedVersion); +} + +void FileViewDropboxPlugin::endRetrieval() +{ + delete d->itemStateSocket; +} + +QList FileViewDropboxPlugin::actions(const KFileItemList& items) const +{ + Q_ASSERT(!items.isEmpty()); + + d->contextActions->clear(); + d->contextFilePaths.clear(); + + const KFileItemListProperties properties(items); + if (!properties.isLocal()) { + // not all files/dirs are local files/dirs + return QList(); + } + + foreach (const KFileItem& item, items) { + d->contextFilePaths << QDir(item.localPath()).canonicalPath(); + } + + const QStringList reply = sendCommand("icon_overlay_context_options\npaths\t", d->contextFilePaths, d->controlSocket, WaitForReply); + if (reply.count() < 2) { + // files/dirs are not served by dropbox + return QList(); + } + + // analyze item options and dynamically form a menu + foreach (const QString& replyLine, reply) { + const QStringList options = replyLine.split("~"); + + if (options.count() > 2) { + KAction* action = d->contextActions->addAction(options.at(2)); + action->setText(options.at(0)); + action->setToolTip(options.at(1)); + action->setIcon(KIcon("dropbox")); + } + } + + return d->contextActions->actions(); +} + +void FileViewDropboxPlugin::handleContextAction(QAction* action) +{ + sendCommand("icon_overlay_context_action\nverb\t" + action->objectName() + "\npaths\t", d->contextFilePaths, d->controlSocket); +} + +QStringList FileViewDropboxPlugin::sendCommand(const QString& command, + const QStringList& paths, + const QPointer& socket, + SendCommandMode mode, + SendCommandTimeout timeout) const +{ + if (!connectWithDropbox(socket, timeout)) { + return QStringList(); + } + + static const QString parameterSeperator('\t'); + static const QString done("\ndone\n"); + static const QString ok("ok\n"); + + const QString request = command + paths.join(parameterSeperator) + done; + + socket->readAll(); + socket->write(request.toUtf8()); + socket->flush(); + + if (mode == SendCommandOnly) { + return QStringList(); + } + + QString reply; + while (socket->waitForReadyRead(timeout == ShortTimeout ? 100 : 500)) { + reply.append(QString::fromUtf8(socket->readAll())); + + if (reply.endsWith(done)) { + break; + } + } + + reply.remove(done); + reply.remove(ok); + + return reply.split(parameterSeperator, QString::SkipEmptyParts); +} + +bool FileViewDropboxPlugin::connectWithDropbox(const QPointer& socket, SendCommandTimeout timeout) const +{ + if (socket->state() != QLocalSocket::ConnectedState) { + socket->connectToServer(d->controlSocketPath); + + if (!socket->waitForConnected(timeout == ShortTimeout ? 100 : 500)) { + socket->abort(); + return false; + } + } + + return true; +} + +#include "moc_fileviewdropboxplugin.cpp" diff --git a/dolphin/plugins/dropbox/fileviewdropboxplugin.desktop b/dolphin/plugins/dropbox/fileviewdropboxplugin.desktop new file mode 100644 index 00000000..8bed63f1 --- /dev/null +++ b/dolphin/plugins/dropbox/fileviewdropboxplugin.desktop @@ -0,0 +1,39 @@ +[Desktop Entry] +Type=Service +Name=Dropbox +Name[ar]=Dropbox +Name[ca]=Dropbox +Name[ca@valencia]=Dropbox +Name[da]=Dropbox +Name[de]=Dropbox +Name[el]=Dropbox +Name[en_GB]=Dropbox +Name[es]=Dropbox +Name[et]=Dropbox +Name[fi]=Dropbox +Name[fr]=DropBox +Name[hu]=Dropbox +Name[it]=Dropbox +Name[ko]=Dropbox +Name[lt]=Dropbox +Name[nb]=Dropbox +Name[nl]=Dropbox +Name[pl]=Dropbox +Name[pt]=Dropbox +Name[pt_BR]=Dropbox +Name[ro]=Dropbox +Name[ru]=Dropbox +Name[sk]=Dropbox +Name[sl]=Dropbox +Name[sr]=Дропбокс +Name[sr@ijekavian]=Дропбокс +Name[sr@ijekavianlatin]=Dropbox +Name[sr@latin]=Dropbox +Name[sv]=Dropbox +Name[tr]=Dropbox +Name[uk]=Dropbox +Name[x-test]=xxDropboxxx +Name[zh_TW]=Dropbox +X-KDE-ServiceTypes=FileViewVersionControlPlugin +MimeType=text/plain; +X-KDE-Library=fileviewdropboxplugin diff --git a/dolphin/plugins/dropbox/fileviewdropboxplugin.h b/dolphin/plugins/dropbox/fileviewdropboxplugin.h new file mode 100644 index 00000000..50181947 --- /dev/null +++ b/dolphin/plugins/dropbox/fileviewdropboxplugin.h @@ -0,0 +1,90 @@ +/***************************************************************************** + * Copyright (C) 2014 by Emmanuel Pescosta * + * Copyright (C) 2012 by Sergei Stolyarov * + * Copyright (C) 2010 by Thomas Richard * + * Copyright (C) 2009-2010 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + *****************************************************************************/ + +#ifndef FILEVIEWDROPBOXPLUGIN_H +#define FILEVIEWDROPBOXPLUGIN_H + +#include + +#include +#include +#include + +#include + +//The dropbox protocol info can be found in the dropboxd-protocol file +//it can be found here http://dl.dropbox.com/u/4577542/dropbox/dropboxd-protocol.txt +//Thanks Steffen Schuldenzucker! + +/** + * @brief Dropbox implementation for the KVersionControlPlugin interface. + */ +class FileViewDropboxPlugin : public KVersionControlPlugin +{ + Q_OBJECT + +private: + enum SendCommandMode + { + WaitForReply, + SendCommandOnly + }; + + enum SendCommandTimeout + { + ShortTimeout, + LongTimeout + }; + +public: + FileViewDropboxPlugin(QObject* parent, const QVariantList& args); + virtual ~FileViewDropboxPlugin(); + + virtual QString fileName() const; + + virtual bool beginRetrieval(const QString& directory); + virtual KVersionControlPlugin::ItemVersion itemVersion(const KFileItem& item) const; + virtual void endRetrieval(); + + virtual QList actions(const KFileItemList& items) const; + +private slots: + void handleContextAction(QAction* action); + +private: + QStringList sendCommand(const QString& command, + const QStringList& paths, + const QPointer& socket, + SendCommandMode mode = SendCommandOnly, + SendCommandTimeout timeout = ShortTimeout) const; + + bool connectWithDropbox(const QPointer& socket, SendCommandTimeout timeout) const; + +private: + class Private; + Private* const d; + + static QMap m_itemVersions; +}; + +#endif // FILEVIEWDROPBOXPLUGIN_H + diff --git a/dolphin/plugins/git/CMakeLists.txt b/dolphin/plugins/git/CMakeLists.txt new file mode 100644 index 00000000..f3271b05 --- /dev/null +++ b/dolphin/plugins/git/CMakeLists.txt @@ -0,0 +1,32 @@ +project(fileviewgitplugin) + +set(fileviewgitplugin_SRCS + fileviewgitplugin.cpp + checkoutdialog.cpp + commitdialog.cpp + tagdialog.cpp + pushdialog.cpp + pulldialog.cpp + gitwrapper.cpp +) + +kde4_add_kcfg_files(fileviewgitplugin_SRCS fileviewgitpluginsettings.kcfgc) + +kde4_add_plugin(fileviewgitplugin ${fileviewgitplugin_SRCS}) + +target_link_libraries(fileviewgitplugin ${KDE4_KIO_LIBS} konq) + +install( + FILES fileviewgitplugin.desktop + DESTINATION ${KDE4_SERVICES_INSTALL_DIR} +) + +install( + FILES fileviewgitpluginsettings.kcfg + DESTINATION ${KDE4_KCFG_INSTALL_DIR} +) + +install( + TARGETS fileviewgitplugin + DESTINATION ${KDE4_PLUGIN_INSTALL_DIR} +) diff --git a/dolphin/plugins/git/Messages.sh b/dolphin/plugins/git/Messages.sh new file mode 100644 index 00000000..3e4decb8 --- /dev/null +++ b/dolphin/plugins/git/Messages.sh @@ -0,0 +1,3 @@ +#! /bin/sh +$EXTRACTRC *.kcfg >> rc.cpp +$XGETTEXT *.cpp -o $podir/fileviewgitplugin.pot diff --git a/dolphin/plugins/git/checkoutdialog.cpp b/dolphin/plugins/git/checkoutdialog.cpp new file mode 100644 index 00000000..2df4ecb9 --- /dev/null +++ b/dolphin/plugins/git/checkoutdialog.cpp @@ -0,0 +1,256 @@ +/****************************************************************************** + * Copyright (C) 2010 by Sebastian Doerner * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ******************************************************************************/ + +#include "checkoutdialog.h" +#include "gitwrapper.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +CheckoutDialog::CheckoutDialog(QWidget* parent): + KDialog(parent, Qt::Dialog), + m_userEditedNewBranchName(false) +{ + //branch/tag selection + this->setCaption(i18nc("@title:window", "Git Checkout")); + this->setButtons(KDialog::Ok | KDialog::Cancel); + this->setDefaultButton(KDialog::Ok); + this->setButtonText(KDialog::Ok, i18nc("@action:button", "Checkout")); + + KVBox * vbox = new KVBox(this); + this->setMainWidget(vbox); + + m_branchSelectGroupBox = new QGroupBox(vbox); + + QGridLayout * gridLayout = new QGridLayout(m_branchSelectGroupBox); + m_branchSelectGroupBox->setLayout(gridLayout); + + m_branchRadioButton = new QRadioButton(i18nc("@option:radio Git Checkout","Branch:"), m_branchSelectGroupBox); + m_branchRadioButton->setChecked(true); + gridLayout->addWidget(m_branchRadioButton,0,0); + m_branchRadioButton->setFocus(); + m_branchComboBox = new KComboBox(false, m_branchSelectGroupBox); + gridLayout->addWidget(m_branchComboBox,0,1); + + QRadioButton * tagRadioButton = new QRadioButton(i18nc("@option:radio Git Checkout", "Tag:"), m_branchSelectGroupBox); + gridLayout->addWidget(tagRadioButton,1,0); + m_tagComboBox = new KComboBox(false, m_branchSelectGroupBox); + m_tagComboBox->setEnabled(false); + gridLayout->addWidget(m_tagComboBox,1,1); + + //options + QGroupBox * optionsGroupBox = new QGroupBox(vbox); + optionsGroupBox->setTitle(i18nc("@title:group", "Options")); + + QGridLayout * optionsGridLayout = new QGridLayout(optionsGroupBox); + optionsGroupBox->setLayout(optionsGridLayout); + + m_newBranchCheckBox = new QCheckBox(i18nc("@option:check", "Create New Branch: "), optionsGroupBox); + m_newBranchCheckBox->setToolTip(i18nc("@info:tooltip", "Create a new branch based on a selected branch or tag.")); + optionsGridLayout->addWidget(m_newBranchCheckBox, 0,0); + + m_newBranchName = new KLineEdit(optionsGroupBox); + m_newBranchName->setMinimumWidth(150); + m_newBranchName->setClearButtonShown(true); + optionsGridLayout->addWidget(m_newBranchName, 0, 1); + + //initialize alternate color scheme for errors + m_errorColors = m_newBranchName->palette(); + m_errorColors.setColor(QPalette::Normal, QPalette::Base, Qt::red); + m_errorColors.setColor(QPalette::Inactive, QPalette::Base, Qt::red); + + m_forceCheckBox = new QCheckBox(i18nc("@option:check", "Force"), optionsGroupBox); + m_forceCheckBox->setToolTip(i18nc("@info:tooltip", "Discard local changes.")); + optionsGridLayout->addWidget(m_forceCheckBox, 1,0); + + //get branch names + GitWrapper * gitWrapper = GitWrapper::instance(); + int currentBranchIndex; + const QStringList branches = gitWrapper->branches(¤tBranchIndex); + + m_branchComboBox->addItems(branches); + if (currentBranchIndex == -1) { + m_branchComboBox->insertItem(0, "(no branch)"); + m_branchComboBox->setCurrentIndex(0); + } else { + m_branchComboBox->setCurrentIndex(currentBranchIndex); + } + setDefaultNewBranchName(m_branchComboBox->currentText()); + + //keep local branches to prevent creating an equally named new branch + foreach (const QString& b, branches) { + if (!b.startsWith(QLatin1String("remotes/"))) { + //you CAN create local branches called "remotes/...", but since no sane person + //would do that, we save the effort of another call to "git branch" + m_branchNames.insert(b); + } + } + + //get tag names + const QStringList tags = gitWrapper->tags(); + m_tagComboBox->addItems(tags); + m_tagComboBox->setCurrentIndex(m_tagComboBox->count()-1); + if (m_tagComboBox->count() == 0){ + tagRadioButton->setEnabled(false); + const QString tooltip = i18nc("@info:tooltip", "There are no tags in this repository."); + tagRadioButton->setToolTip(tooltip); + m_tagComboBox->setToolTip(tooltip); + } + + //signals + connect(m_branchRadioButton, SIGNAL(toggled(bool)), + this, SLOT(branchRadioButtonToggled(bool))); + connect(m_branchComboBox, SIGNAL(currentIndexChanged(QString)), + this, SLOT(setDefaultNewBranchName(QString))); + connect(m_branchComboBox, SIGNAL(currentIndexChanged(QString)), + this, SLOT(setOkButtonState())); + connect(m_tagComboBox, SIGNAL(currentIndexChanged(QString)), + this, SLOT(setDefaultNewBranchName(QString))); + connect(m_newBranchCheckBox, SIGNAL(stateChanged(int)), + this, SLOT(newBranchCheckBoxStateToggled(int))); + connect(m_newBranchName, SIGNAL(textChanged(QString)), + this, SLOT(setOkButtonState())); + connect(m_newBranchName, SIGNAL(textEdited(QString)), + this, SLOT(noteUserEditedNewBranchName())); + //set initial widget states + newBranchCheckBoxStateToggled(Qt::Unchecked); +} + +QString CheckoutDialog::checkoutIdentifier() const +{ + QString identifier = m_branchComboBox->isEnabled() ? m_branchComboBox->currentText() : m_tagComboBox->currentText(); + if (identifier.length()==0 || identifier.at(0)=='(') { + identifier = ""; + } + return identifier; +} + +bool CheckoutDialog::force() const +{ + return m_forceCheckBox->isChecked(); +} + +QString CheckoutDialog::newBranchName() const +{ + if (m_newBranchCheckBox->isChecked()) { + return m_newBranchName->text().trimmed(); + } else { + return QString(); + } +} + +void CheckoutDialog::branchRadioButtonToggled(bool checked) +{ + m_branchComboBox->setEnabled(checked); + m_tagComboBox->setEnabled(!checked); + setDefaultNewBranchName(checked ? m_branchComboBox->currentText() : m_tagComboBox->currentText()); + setOkButtonState(); +} + +void CheckoutDialog::newBranchCheckBoxStateToggled(int state) +{ + m_newBranchName->setEnabled(state == Qt::Checked); + m_branchSelectGroupBox->setTitle( + state == Qt::Checked ? + i18nc("@title:group", "Branch Base") : + i18nc("@title:group", "Checkout")); + if (state == Qt::Checked) { + m_newBranchName->setFocus(Qt::TabFocusReason); + } + setOkButtonState(); +} + +void CheckoutDialog::setOkButtonState() +{ + //default to enabled + bool enableButton = true; + bool newNameError = false; + + //------------disable on these conditions---------------// + if (m_newBranchCheckBox->isChecked()) { + const QString newBranchName = m_newBranchName->text().trimmed(); + if (newBranchName.isEmpty()){ + enableButton = false; + newNameError = true; + const QString tt = i18nc("@info:tooltip", "You must enter a valid name for the new branch first."); + m_newBranchName->setToolTip(tt); + this->setButtonToolTip(KDialog::Ok, tt); + } + if (m_branchNames.contains(newBranchName)) { + enableButton = false; + newNameError = true; + const QString tt = i18nc("@info:tooltip", "A branch with the name '%1' already exists.", newBranchName); + m_newBranchName->setToolTip(tt); + this->setButtonToolTip(KDialog::Ok, tt); + } + if (newBranchName.contains(QRegExp("\\s"))) { + enableButton = false; + newNameError = true; + const QString tt = i18nc("@info:tooltip", "Branch names may not contain any whitespace."); + m_newBranchName->setToolTip(tt); + this->setButtonToolTip(KDialog::Ok, tt); + } + } //if we create a new branch and no valid branch is selected we create one based on the currently checked out version + else if (m_branchRadioButton->isChecked() && m_branchComboBox->currentText().at(0) == '('){ + enableButton = false; + this->setButtonToolTip(KDialog::Ok, i18nc("@info:tooltip", "You must select a valid branch first.")); + } + //------------------------------------------------------// + + setLineEditErrorModeActive(newNameError); + this->enableButtonOk(enableButton); + if (!newNameError) { + m_newBranchName->setToolTip(QString()); + } + if (enableButton) { + this->setButtonToolTip(KDialog::Ok, QString()); + } +} + +void CheckoutDialog::setDefaultNewBranchName(const QString& baseBranchName) +{ + if (!m_userEditedNewBranchName) { + if (baseBranchName.startsWith('(')) { + m_newBranchName->setText(""); + } else { + m_newBranchName->setText(i18nc("@item:intext Prepended to the current branch name " + "to get the default name for a newly created branch", "branch") + '_' + baseBranchName); + } + } +} + +void CheckoutDialog::noteUserEditedNewBranchName() +{ + m_userEditedNewBranchName = true; +} + +void CheckoutDialog::setLineEditErrorModeActive(bool active) +{ + m_newBranchName->setPalette(active ? m_errorColors : QPalette()); +} + +#include "moc_checkoutdialog.cpp" diff --git a/dolphin/plugins/git/checkoutdialog.h b/dolphin/plugins/git/checkoutdialog.h new file mode 100644 index 00000000..473065eb --- /dev/null +++ b/dolphin/plugins/git/checkoutdialog.h @@ -0,0 +1,88 @@ +/****************************************************************************** + * Copyright (C) 2010 by Sebastian Doerner * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ******************************************************************************/ + +#ifndef CHECKOUTDIALOG_H +#define CHECKOUTDIALOG_H + +#include +#include +#include + +class KComboBox; +class KLineEdit; +#include +#include +#include + +/** + * @brief The dialog for checking out Branches or Tags in Git. + */ +class CheckoutDialog : public KDialog +{ + Q_OBJECT + +public: + CheckoutDialog(QWidget* parent = 0); + /** + * Returns the name of the selected tag or branch to be checkout out + * @returns The name of the selected tag or branch + */ + QString checkoutIdentifier() const; + /** + * @returns True if the user selected a forced checkout, false otherwise. + */ + bool force() const; + /** + * @returns The user selected name of the new branch, if a new branch is to be + * created, empty String otherwise. + */ + QString newBranchName() const; + +private slots: + void branchRadioButtonToggled(bool checked); + void newBranchCheckBoxStateToggled(int state); + /** + * Checks whether the values of all relevant widgets are valid. + * Enables or disables the OK button and sets tooltips accordingly. + */ + void setOkButtonState(); + void noteUserEditedNewBranchName(); + /** + * Inserts a default name for the new branch into m_newBranchName unless the user + * has already edited the content. + * @param baseBranchName The base name to derive the new name of. + */ + void setDefaultNewBranchName(const QString & baseBranchName); +private: + inline void setLineEditErrorModeActive(bool active); +private: + ///@brief true if the user has manually edited the branchName, false otherwise + bool m_userEditedNewBranchName; + QSet m_branchNames; + QPalette m_errorColors; + QGroupBox * m_branchSelectGroupBox; + QRadioButton * m_branchRadioButton; + KComboBox * m_branchComboBox; + KComboBox * m_tagComboBox; + QCheckBox * m_newBranchCheckBox; + KLineEdit * m_newBranchName; + QCheckBox * m_forceCheckBox; +}; + +#endif // CHECKOUTDIALOG_H diff --git a/dolphin/plugins/git/commitdialog.cpp b/dolphin/plugins/git/commitdialog.cpp new file mode 100644 index 00000000..fb8b0a89 --- /dev/null +++ b/dolphin/plugins/git/commitdialog.cpp @@ -0,0 +1,134 @@ +/****************************************************************************** + * Copyright (C) 2010 by Sebastian Doerner * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ******************************************************************************/ + +#include "commitdialog.h" +#include "fileviewgitpluginsettings.h" +#include "gitwrapper.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +CommitDialog::CommitDialog (QWidget* parent ): + KDialog (parent, Qt::Dialog), + m_localCodec(QTextCodec::codecForLocale()) +{ + this->setCaption(i18nc("@title:window", "Git Commit")); + this->setButtons(KDialog::Ok | KDialog::Cancel); + this->setDefaultButton(KDialog::Ok); + this->setButtonText(KDialog::Ok, i18nc("@action:button", "Commit")); + + KVBox* vbox = new KVBox(this); + this->setMainWidget(vbox); + + QGroupBox* messageGroupBox = new QGroupBox(vbox); + messageGroupBox->setTitle(i18nc("@title:group", "Commit message")); + + QVBoxLayout * messageVBox = new QVBoxLayout(messageGroupBox); + messageGroupBox->setLayout(messageVBox); + + m_commitMessageTextEdit = new KTextEdit(messageGroupBox); + m_commitMessageTextEdit->setLineWrapMode(QTextEdit::FixedColumnWidth); + m_commitMessageTextEdit->setLineWrapColumnOrWidth(72); + messageVBox->addWidget(m_commitMessageTextEdit); + setOkButtonState(); + connect(m_commitMessageTextEdit, SIGNAL(textChanged()), this, SLOT(setOkButtonState())); + + QHBoxLayout* messageHBox = new QHBoxLayout(); + messageVBox->addLayout(messageHBox); + + m_amendCheckBox = new QCheckBox(i18nc("@option:check", "Amend last commit"), messageGroupBox); + messageHBox->addWidget(m_amendCheckBox); + //read last commit message + m_alternativeMessage = GitWrapper::instance()->lastCommitMessage(); + if (m_alternativeMessage.isNull()) { + m_amendCheckBox->setEnabled(false); + m_amendCheckBox->setToolTip(i18nc("@info:tooltip", "There is nothing to amend.")); + } else { + connect(m_amendCheckBox, SIGNAL(stateChanged(int)), + this, SLOT(amendCheckBoxStateChanged())); + } + + QPushButton * signOffButton = new QPushButton( + i18nc("@action:button Add Signed-Off line to the message widget", "Sign off"),messageGroupBox); + signOffButton->setToolTip(i18nc("@info:tooltip", "Add Signed-off-by line at the end of the commit message.")); + messageHBox->addStretch(); + messageHBox->addWidget(signOffButton); + + //restore dialog size + FileViewGitPluginSettings* settings = FileViewGitPluginSettings::self(); + this->setInitialSize(QSize(settings->commitDialogWidth(), settings->commitDialogHeight())); + + connect(this, SIGNAL(finished()), this, SLOT(saveDialogSize())); + connect(signOffButton, SIGNAL(clicked(bool)), this, SLOT(signOffButtonClicked())); +} + +void CommitDialog::signOffButtonClicked() +{ + if (m_userName.isNull()) { + GitWrapper * gitWrapper= GitWrapper::instance(); + m_userName = gitWrapper->userName(); + m_userEmail = gitWrapper->userEmail(); + } + //append Signed-off line + QString lastline = m_commitMessageTextEdit->document()->lastBlock().text(); + bool noNewLine = lastline.startsWith(QLatin1String("Signed-off")) || lastline.isEmpty(); + m_commitMessageTextEdit->append(QString(noNewLine ? "" : "\n") + "Signed-off-by: " + + m_userName + " <" + m_userEmail + '>'); +} + +QByteArray CommitDialog::commitMessage() const +{ + return m_localCodec->fromUnicode(m_commitMessageTextEdit->toPlainText()); +} + +bool CommitDialog::amend() const +{ + return m_amendCheckBox->isChecked(); +} + +void CommitDialog::amendCheckBoxStateChanged() +{ + QString tmp = m_commitMessageTextEdit->toPlainText(); + m_commitMessageTextEdit->setText(m_alternativeMessage); + m_alternativeMessage = tmp; +} + +void CommitDialog::saveDialogSize() +{ + FileViewGitPluginSettings* settings = FileViewGitPluginSettings::self(); + settings->setCommitDialogHeight(this->height()); + settings->setCommitDialogWidth(this->width()); + settings->writeConfig(); +} + +void CommitDialog::setOkButtonState() +{ + bool enable = !m_commitMessageTextEdit->toPlainText().isEmpty(); + this->enableButtonOk(enable); + this->setButtonToolTip(KDialog::Ok, enable ? + "" : i18nc("@info:tooltip", "You must enter a commit message first.")); +} + +#include "moc_commitdialog.cpp" diff --git a/dolphin/plugins/git/commitdialog.h b/dolphin/plugins/git/commitdialog.h new file mode 100644 index 00000000..6133b43b --- /dev/null +++ b/dolphin/plugins/git/commitdialog.h @@ -0,0 +1,65 @@ +/****************************************************************************** + * Copyright (C) 2010 by Sebastian Doerner * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ******************************************************************************/ + +#ifndef COMMITDIALOG_H +#define COMMITDIALOG_H + +#include + +#include +#include +class KTextEdit; + +class CommitDialog : public KDialog +{ + Q_OBJECT + +public: + CommitDialog(QWidget* parent = 0); + /** + * Returns the commit message given by the user. + * @returns The commit message. + */ + QByteArray commitMessage() const; + /** + * Indicated whether the user wants to amend the last commit. + * @returns True if the last commit is to be amended, false otherwise + */ + bool amend() const; +private slots: + void signOffButtonClicked(); + void amendCheckBoxStateChanged(); + void saveDialogSize(); + void setOkButtonState(); +private: + KTextEdit* m_commitMessageTextEdit; + QCheckBox* m_amendCheckBox; + /** + * @brief Holds an alternative message, that is not displayed currently. + * One message is the amend message, the other one for normal commits. + */ + QString m_alternativeMessage; + QTextCodec* m_localCodec; + ///Cache for GitWrapper::userName(); + QString m_userName; + ///Cache for GitWrapper::userEmail(); + QString m_userEmail; +}; + +#endif // COMMITDIALOG_H diff --git a/dolphin/plugins/git/fileviewgitplugin.cpp b/dolphin/plugins/git/fileviewgitplugin.cpp new file mode 100644 index 00000000..46ab5a71 --- /dev/null +++ b/dolphin/plugins/git/fileviewgitplugin.cpp @@ -0,0 +1,629 @@ +/****************************************************************************** + * Copyright (C) 2010 by Peter Penz * + * Copyright (C) 2010 by Sebastian Doerner * + * Copyright (C) 2010 by Johannes Steffen * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ******************************************************************************/ + +#include "fileviewgitplugin.h" +#include "checkoutdialog.h" +#include "commitdialog.h" +#include "tagdialog.h" +#include "pushdialog.h" +#include "gitwrapper.h" +#include "pulldialog.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +K_PLUGIN_FACTORY(FileViewGitPluginFactory, registerPlugin();) +K_EXPORT_PLUGIN(FileViewGitPluginFactory("fileviewgitplugin")) + +FileViewGitPlugin::FileViewGitPlugin(QObject* parent, const QList& args) : + KVersionControlPlugin(parent), + m_pendingOperation(false), + m_addAction(0), + m_removeAction(0), + m_checkoutAction(0), + m_commitAction(0), + m_tagAction(0), + m_pushAction(0), + m_pullAction(0) +{ + Q_UNUSED(args); + + m_addAction = new KAction(this); + m_addAction->setIcon(KIcon("list-add")); + m_addAction->setText(i18nc("@action:inmenu", "Git Add")); + connect(m_addAction, SIGNAL(triggered()), + this, SLOT(addFiles())); + + m_removeAction = new KAction(this); + m_removeAction->setIcon(KIcon("list-remove")); + m_removeAction->setText(i18nc("@action:inmenu", "Git Remove")); + connect(m_removeAction, SIGNAL(triggered()), + this, SLOT(removeFiles())); + + m_checkoutAction = new KAction(this); + m_checkoutAction->setIcon(KIcon("debug-step-into")); + m_checkoutAction->setText(i18nc("@action:inmenu", "Git Checkout...")); + connect(m_checkoutAction, SIGNAL(triggered()), + this, SLOT(checkout())); + + m_commitAction = new KAction(this); + m_commitAction->setIcon(KIcon("svn-commit")); + m_commitAction->setText(i18nc("@action:inmenu", "Git Commit...")); + connect(m_commitAction, SIGNAL(triggered()), + this, SLOT(commit())); + + m_tagAction = new KAction(this); + m_tagAction->setIcon(KIcon("bookmark-new")); + m_tagAction->setText(i18nc("@action:inmenu", "Git Create Tag...")); + connect(m_tagAction, SIGNAL(triggered()), + this, SLOT(createTag())); + m_pushAction = new KAction(this); + m_pushAction->setIcon(KIcon("go-top")); + m_pushAction->setText(i18nc("@action:inmenu", "Git Push...")); + connect(m_pushAction, SIGNAL(triggered()), + this, SLOT(push())); + m_pullAction = new KAction(this); + m_pullAction->setIcon(KIcon("go-bottom")); + m_pullAction->setText(i18nc("@action:inmenu", "Git Pull...")); + connect(m_pullAction, SIGNAL(triggered()), + this, SLOT(pull())); + + connect(&m_process, SIGNAL(finished(int, QProcess::ExitStatus)), + this, SLOT(slotOperationCompleted(int, QProcess::ExitStatus))); + connect(&m_process, SIGNAL(error(QProcess::ProcessError)), + this, SLOT(slotOperationError())); +} + +FileViewGitPlugin::~FileViewGitPlugin() +{ + GitWrapper::freeInstance(); +} + +QString FileViewGitPlugin::fileName() const +{ + return QLatin1String(".git"); +} + +int FileViewGitPlugin::readUntilZeroChar(QIODevice* device, char* buffer, const int maxChars) { + if (buffer == 0) { // discard until next \0 + char c; + while (device->getChar(&c) && c != '\0') + ; + return 0; + } + int index = -1; + while (++index < maxChars) { + if (!device->getChar(&buffer[index])) { + buffer[index] = '\0'; + return index == 0 ? 0 : index + 1; + } + if (buffer[index] == '\0') { // line end or we put it there (see above) + return index + 1; + } + } + return maxChars; +} + +bool FileViewGitPlugin::beginRetrieval(const QString& directory) +{ + Q_ASSERT(directory.endsWith('/')); + + GitWrapper::instance()->setWorkingDirectory(directory); + m_currentDir = directory; + + // ----- find path below git base dir ----- + QProcess process; + process.setWorkingDirectory(directory); + process.start(QLatin1String("git rev-parse --show-prefix")); + QString dirBelowBaseDir = ""; + while (process.waitForReadyRead()) { + char buffer[512]; + while (process.readLine(buffer, sizeof(buffer)) > 0) { + dirBelowBaseDir = QString(buffer).trimmed(); // ends in "/" or is empty + } + } + + m_versionInfoHash.clear(); + + // ----- find files with special status ----- + process.start(QLatin1String("git status --porcelain -z --ignored")); + while (process.waitForReadyRead()) { + char buffer[1024]; + while (readUntilZeroChar(&process, buffer, sizeof(buffer)) > 0 ) { + QString line = QTextCodec::codecForLocale()->toUnicode(buffer); + // ----- recognize file status ----- + char X = line[0].toAscii(); // X and Y from the table in `man git-status` + char Y = line[1].toAscii(); + const QString fileName= line.mid(3); + ItemVersion state = NormalVersion; + switch (X) { + case '!': + state = IgnoredVersion; + break; + case '?': + state = UnversionedVersion; + break; + case 'C': // handle copied as added version + case 'A': + state = AddedVersion; + break; + case 'D': + state = RemovedVersion; + break; + case 'M': + state = LocallyModifiedVersion; + break; + case 'R': + state = LocallyModifiedVersion; + // Renames list the old file name directly afterwards, separated by \0. + readUntilZeroChar(&process, 0, 0); // discard old file name + break; + } + // overwrite status depending on the working tree + switch (Y) { + case 'D': // handle "deleted in working tree" as "modified in working tree" + case 'M': + state = LocallyModifiedUnstagedVersion; + break; + } + // overwrite status in case of conflicts (lower part of the table in `man git-status`) + if (X == 'U' || + Y == 'U' || + (X == 'A' && Y == 'A') || + (X == 'D' && Y == 'D')) { + state = ConflictingVersion; + } + + // ----- decide what to record about that file ----- + if (state == NormalVersion || !fileName.startsWith(dirBelowBaseDir)) { + continue; + } + /// File name relative to the current working directory. + const QString relativeFileName = fileName.mid(dirBelowBaseDir.length()); + //if file is part of a sub-directory, record the directory + if (relativeFileName.contains('/')) { + if (state == IgnoredVersion) + continue; + if (state == AddedVersion || state == RemovedVersion) { + state = LocallyModifiedVersion; + } + const QString absoluteDirName = directory + relativeFileName.left(relativeFileName.indexOf('/')); + if (m_versionInfoHash.contains(absoluteDirName)) { + ItemVersion oldState = m_versionInfoHash.value(absoluteDirName); + //only keep the most important state for a directory + if (oldState == ConflictingVersion) + continue; + if (oldState == LocallyModifiedUnstagedVersion && state != ConflictingVersion) + continue; + if (oldState == LocallyModifiedVersion && + state != LocallyModifiedUnstagedVersion && state != ConflictingVersion) + continue; + m_versionInfoHash.insert(absoluteDirName, state); + } else { + m_versionInfoHash.insert(absoluteDirName, state); + } + } else { //normal file, no directory + m_versionInfoHash.insert(directory + relativeFileName, state); + } + } + } + return true; +} + +void FileViewGitPlugin::endRetrieval() +{ +} + +KVersionControlPlugin::ItemVersion FileViewGitPlugin::itemVersion(const KFileItem& item) const +{ + const QString itemUrl = item.localPath(); + if (m_versionInfoHash.contains(itemUrl)) { + return m_versionInfoHash.value(itemUrl); + } else { + // files that are not in our map are normal, tracked files by definition + return NormalVersion; + } +} + +QList FileViewGitPlugin::actions(const KFileItemList &items) const +{ + if (items.count() == 1 && items.first().isDir() && + items.first().url().path(KUrl::AddTrailingSlash) == m_currentDir) { + return contextMenuDirectoryActions(items.first().url().path(KUrl::AddTrailingSlash)); + } else { + return contextMenuFilesActions(items); + } +} + +QList FileViewGitPlugin::contextMenuFilesActions(const KFileItemList& items) const +{ + Q_ASSERT(!items.isEmpty()); + + if (!m_pendingOperation){ + m_contextDir.clear(); + m_contextItems.clear(); + foreach(const KFileItem& item, items){ + m_contextItems.append(item); + } + + //see which actions should be enabled + int versionedCount = 0; + int addableCount = 0; + foreach(const KFileItem& item, items){ + const ItemVersion state = itemVersion(item); + if (state != UnversionedVersion && state != RemovedVersion && + state != IgnoredVersion) { + ++versionedCount; + } + if (state == UnversionedVersion || state == LocallyModifiedUnstagedVersion || + state == IgnoredVersion) { + ++addableCount; + } + } + + m_addAction->setEnabled(addableCount == items.count()); + m_removeAction->setEnabled(versionedCount == items.count()); + } + else{ + m_addAction->setEnabled(false); + m_removeAction->setEnabled(false); + } + + QList actions; + actions.append(m_addAction); + actions.append(m_removeAction); + return actions; +} + +QList FileViewGitPlugin::contextMenuDirectoryActions(const QString& directory) const +{ + QList actions; + if (!m_pendingOperation){ + m_contextDir = directory; + } + m_checkoutAction->setEnabled(!m_pendingOperation); + actions.append(m_checkoutAction); + + bool canCommit = false; + QHash::const_iterator it = m_versionInfoHash.constBegin(); + while (it != m_versionInfoHash.constEnd()) { + const ItemVersion state = it.value(); + if (state == LocallyModifiedVersion || state == AddedVersion || state == RemovedVersion) { + canCommit = true; + } + if (state == ConflictingVersion) { + canCommit = false; + break; + } + ++it; + } + + m_commitAction->setEnabled(!m_pendingOperation && canCommit); + actions.append(m_commitAction); + + m_tagAction->setEnabled(!m_pendingOperation); + actions.append(m_tagAction); + m_pushAction->setEnabled(!m_pendingOperation); + actions.append(m_pushAction); + m_pullAction->setEnabled(!m_pendingOperation); + actions.append(m_pullAction); + + return actions; +} + +void FileViewGitPlugin::addFiles() +{ + execGitCommand(QLatin1String("add"), QStringList(), + i18nc("@info:status", "Adding files to Git repository..."), + i18nc("@info:status", "Adding files to Git repository failed."), + i18nc("@info:status", "Added files to Git repository.")); +} + +void FileViewGitPlugin::removeFiles() +{ + QStringList arguments; + arguments << "-r"; //recurse through directories + arguments << "--force"; //also remove files that have not been committed yet + execGitCommand(QLatin1String("rm"), arguments, + i18nc("@info:status", "Removing files from Git repository..."), + i18nc("@info:status", "Removing files from Git repository failed."), + i18nc("@info:status", "Removed files from Git repository.")); + +} + +void FileViewGitPlugin::checkout() +{ + CheckoutDialog dialog; + if (dialog.exec() == QDialog::Accepted){ + QProcess process; + process.setWorkingDirectory(m_contextDir); + QStringList arguments; + arguments << "checkout"; + if (dialog.force()) { + arguments << "-f"; + } + const QString newBranchName = dialog.newBranchName(); + if (!newBranchName.isEmpty()) { + arguments << "-b"; + arguments << newBranchName; + } + const QString checkoutIdentifier = dialog.checkoutIdentifier(); + if (!checkoutIdentifier.isEmpty()) { + arguments << checkoutIdentifier; + } + //to appear in messages + const QString currentBranchName = newBranchName.isEmpty() ? checkoutIdentifier : newBranchName; + process.start(QLatin1String("git"), arguments); + process.setReadChannel(QProcess::StandardError); //git writes info messages to stderr as well + QString completedMessage; + while (process.waitForReadyRead()) { + char buffer[512]; + while (process.readLine(buffer, sizeof(buffer)) > 0){ + const QString currentLine(buffer); + if (currentLine.startsWith(QLatin1String("Switched to branch"))) { + completedMessage = i18nc("@info:status", "Switched to branch '%1'", currentBranchName); + } + if (currentLine.startsWith(QLatin1String("HEAD is now at"))) { + const QString headIdentifier = currentLine. + mid(QString("HEAD is now at ").length()).trimmed(); + completedMessage = i18nc("@info:status Git HEAD pointer, parameter includes " + "short SHA-1 & commit message ", "HEAD is now at %1", headIdentifier); + } + //special output for checkout -b + if (currentLine.startsWith(QLatin1String("Switched to a new branch"))) { + completedMessage = i18nc("@info:status", "Switched to a new branch '%1'", currentBranchName); + } + } + } + if (process.exitCode() == 0 && process.exitStatus() == QProcess::NormalExit) { + if (!completedMessage.isEmpty()) { + emit operationCompletedMessage(completedMessage); + emit itemVersionsChanged(); + } + } + else { + emit errorMessage(i18nc("@info:status", "Git Checkout failed." + " Maybe your working directory is dirty.")); + } + } +} + +void FileViewGitPlugin::commit() +{ + CommitDialog dialog; + if (dialog.exec() == QDialog::Accepted) { + KTemporaryFile tmpCommitMessageFile; + tmpCommitMessageFile.open(); + tmpCommitMessageFile.write(dialog.commitMessage()); + tmpCommitMessageFile.close(); + QProcess process; + process.setWorkingDirectory(m_contextDir); + process.start(QString("git commit") + (dialog.amend() ? " --amend" : "")+ " -F " + tmpCommitMessageFile.fileName()); + QString completedMessage; + while (process.waitForReadyRead()){ + char buffer[512]; + while (process.readLine(buffer, sizeof(buffer)) > 0) { + if (strlen(buffer) > 0 && buffer[0] == '[') { + completedMessage = QTextCodec::codecForLocale()->toUnicode(buffer).trimmed(); + break; + } + } + } + if (!completedMessage.isEmpty()) { + emit operationCompletedMessage(completedMessage); + emit itemVersionsChanged(); + } + } +} + +void FileViewGitPlugin::createTag() +{ + TagDialog dialog; + if (dialog.exec() == QDialog::Accepted) { + KTemporaryFile tempTagMessageFile; + tempTagMessageFile.open(); + tempTagMessageFile.write(dialog.tagMessage()); + tempTagMessageFile.close(); + QProcess process; + process.setWorkingDirectory(m_contextDir); + process.setReadChannel(QProcess::StandardError); + process.start(QString("git tag -a -F %1 %2 %3").arg(tempTagMessageFile.fileName()). + arg(dialog.tagName()).arg(dialog.baseBranch())); + QString completedMessage; + bool gotTagAlreadyExistsMessage = false; + while (process.waitForReadyRead()) { + char buffer[512]; + while (process.readLine(buffer, sizeof(buffer)) > 0) { + QString line(buffer); + if (line.contains("already exists")) { + gotTagAlreadyExistsMessage = true; + } + } + } + if (process.exitCode() == 0 && process.exitStatus() == QProcess::NormalExit) { + completedMessage = i18nc("@info:status","Successfully created tag '%1'", dialog.tagName()); + emit operationCompletedMessage(completedMessage); + } else { + //I don't know any other error, but in case one occurs, the user doesn't get FALSE error messages + emit errorMessage(gotTagAlreadyExistsMessage ? + i18nc("@info:status", "Git tag creation failed." + " A tag with the name '%1' already exists.", dialog.tagName()) : + i18nc("@info:status", "Git tag creation failed.") + ); + } + } +} + +void FileViewGitPlugin::push() +{ + PushDialog dialog; + if (dialog.exec() == QDialog::Accepted) { + m_process.setWorkingDirectory(m_contextDir); + + m_errorMsg = i18nc("@info:status", "Pushing branch %1 to %2:%3 failed.", + dialog.localBranch(), dialog.destination(), dialog.remoteBranch()); + m_operationCompletedMsg = i18nc("@info:status", "Pushed branch %1 to %2:%3.", + dialog.localBranch(), dialog.destination(), dialog.remoteBranch()); + emit infoMessage(i18nc("@info:status", "Pushing branch %1 to %2:%3...", + dialog.localBranch(), dialog.destination(), dialog.remoteBranch())); + + m_command = "push"; + m_pendingOperation = true; + m_process.start(QString("git push%4 %1 %2:%3").arg(dialog.destination()). + arg(dialog.localBranch()).arg(dialog.remoteBranch()). + arg(dialog.force() ? QLatin1String(" --force") : QLatin1String(""))); + } +} + +void FileViewGitPlugin::pull() +{ + PullDialog dialog; + if (dialog.exec() == QDialog::Accepted) { + m_process.setWorkingDirectory(m_contextDir); + + m_errorMsg = i18nc("@info:status", "Pulling branch %1 from %2 failed.", + dialog.remoteBranch(), dialog.source()); + m_operationCompletedMsg = i18nc("@info:status", "Pulled branch %1 from %2 successfully.", + dialog.remoteBranch(), dialog.source()); + emit infoMessage(i18nc("@info:status", "Pulling branch %1 from %2...", dialog.remoteBranch(), + dialog.source())); + + m_command = "pull"; + m_pendingOperation = true; + m_process.start(QString("git pull %1 %2").arg(dialog.source()).arg(dialog.remoteBranch())); + } +} + +void FileViewGitPlugin::slotOperationCompleted(int exitCode, QProcess::ExitStatus exitStatus) +{ + m_pendingOperation = false; + + QString message; + if (m_command == QLatin1String("push")) { //output parsing for push + message = parsePushOutput(); + m_command = ""; + } + if (m_command == QLatin1String("pull")) { + message = parsePullOutput(); + m_command = ""; + } + + if ((exitStatus != QProcess::NormalExit) || (exitCode != 0)) { + emit errorMessage(message.isNull() ? m_errorMsg : message); + } else if (m_contextItems.isEmpty()) { + emit operationCompletedMessage(message.isNull() ? m_operationCompletedMsg : message); + emit itemVersionsChanged(); + } else { + startGitCommandProcess(); + } +} + +void FileViewGitPlugin::slotOperationError() +{ + // don't do any operation on other items anymore + m_contextItems.clear(); + m_pendingOperation = false; + + emit errorMessage(m_errorMsg); +} + +QString FileViewGitPlugin::parsePushOutput() +{ + m_process.setReadChannel(QProcess::StandardError); + QString message; + char buffer[256]; + while (m_process.readLine(buffer, sizeof(buffer)) > 0) { + const QString line(buffer); + if (line.contains("->") || (line.contains("fatal") && message.isNull())) { + message = line.trimmed(); + } + if (line.contains("Everything up-to-date") && message.isNull()) { + message = i18nc("@info:status", "Branch is already up-to-date."); + } + } + return message; +} + +QString FileViewGitPlugin::parsePullOutput() +{ + char buffer[256]; + while (m_process.readLine(buffer, sizeof(buffer)) > 0) { + const QString line(buffer); + if (line.contains("Already up-to-date")) { + return i18nc("@info:status", "Branch is already up-to-date."); + } + if (line.contains("CONFLICT")) { + emit itemVersionsChanged(); + return i18nc("@info:status", "Merge conflicts occurred. Fix them and commit the result."); + } + } + return QString(); +} + +void FileViewGitPlugin::execGitCommand(const QString& gitCommand, + const QStringList& arguments, + const QString& infoMsg, + const QString& errorMsg, + const QString& operationCompletedMsg) +{ + emit infoMessage(infoMsg); + + m_command = gitCommand; + m_arguments = arguments; + m_errorMsg = errorMsg; + m_operationCompletedMsg = operationCompletedMsg; + + startGitCommandProcess(); +} + + +void FileViewGitPlugin::startGitCommandProcess() +{ + Q_ASSERT(!m_contextItems.isEmpty()); + Q_ASSERT(m_process.state() == QProcess::NotRunning); + m_pendingOperation = true; + + const KFileItem item = m_contextItems.takeLast(); + m_process.setWorkingDirectory(item.url().directory()); + QStringList arguments; + arguments << m_command; + arguments << m_arguments; + //force explicitly selected files but no files in selected directories + if (m_command == "add" && !item.isDir()){ + arguments<< QLatin1String("-f"); + } + arguments << item.url().fileName(); + m_process.start(QLatin1String("git"), arguments); + // the remaining items of m_contextItems will be executed + // after the process has finished (see slotOperationFinished()) +} + +#include "moc_fileviewgitplugin.cpp" diff --git a/dolphin/plugins/git/fileviewgitplugin.desktop b/dolphin/plugins/git/fileviewgitplugin.desktop new file mode 100644 index 00000000..9f2a4d58 --- /dev/null +++ b/dolphin/plugins/git/fileviewgitplugin.desktop @@ -0,0 +1,52 @@ +[Desktop Entry] +Type=Service +Name=Git +Name[ar]=Git +Name[ast]=Git +Name[bg]=Git +Name[bs]=Git +Name[ca]=Git +Name[ca@valencia]=Git +Name[cs]=Git +Name[da]=Git +Name[de]=Git +Name[el]=Git +Name[en_GB]=Git +Name[es]=Git +Name[et]=Git +Name[fi]=Git +Name[fr]=Git +Name[ga]=Git +Name[gl]=Git +Name[hu]=Git +Name[it]=Git +Name[kk]=Git +Name[km]=Git​ +Name[ko]=Git +Name[lt]=Git +Name[mr]=जिट +Name[nb]=Git +Name[nds]=Git +Name[nl]=Git +Name[pa]=Git +Name[pl]=Git +Name[pt]=Git +Name[pt_BR]=Git +Name[ro]=Git +Name[ru]=Git +Name[sk]=Git +Name[sl]=Git +Name[sr]=Гит +Name[sr@ijekavian]=Гит +Name[sr@ijekavianlatin]=Git +Name[sr@latin]=Git +Name[sv]=Git +Name[tr]=Git +Name[ug]=Git +Name[uk]=Git +Name[x-test]=xxGitxx +Name[zh_CN]=Git +Name[zh_TW]=Git +X-KDE-ServiceTypes=FileViewVersionControlPlugin +MimeType=text/plain; +X-KDE-Library=fileviewgitplugin diff --git a/dolphin/plugins/git/fileviewgitplugin.h b/dolphin/plugins/git/fileviewgitplugin.h new file mode 100644 index 00000000..19d92fc1 --- /dev/null +++ b/dolphin/plugins/git/fileviewgitplugin.h @@ -0,0 +1,122 @@ +/****************************************************************************** + * Copyright (C) 2010 by Peter Penz * + * Copyright (C) 2010 by Sebastian Doerner * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ******************************************************************************/ + +#ifndef FILEVIEWGITPLUGIN_H +#define FILEVIEWGITPLUGIN_H + +#include +#include +#include +#include +#include +#include + +/** + * @brief Git implementation for the KVersionControlPlugin interface. + */ +class FileViewGitPlugin : public KVersionControlPlugin +{ + Q_OBJECT + +public: + FileViewGitPlugin(QObject* parent, const QList& args); + virtual ~FileViewGitPlugin(); + virtual QString fileName() const; + virtual bool beginRetrieval(const QString& directory); + virtual void endRetrieval(); + virtual KVersionControlPlugin::ItemVersion itemVersion(const KFileItem& item) const; + virtual QList actions(const KFileItemList &items) const; + +private slots: + void addFiles(); + void removeFiles(); + void checkout(); + void commit(); + void createTag(); + void push(); + void pull(); + + void slotOperationCompleted(int exitCode, QProcess::ExitStatus exitStatus); + void slotOperationError(); +private: + QList contextMenuFilesActions(const KFileItemList& items) const; + QList contextMenuDirectoryActions(const QString& directory) const; + /** + * Reads into buffer from device until we reach the next \0 or maxChars have been read. + * @returns The number of characters read. + */ + int readUntilZeroChar(QIODevice* device, char* buffer, const int maxChars); + /** + * Parses the output of the git push command and returns an appropriate message, + * that should be displayed to the user. + * @returns The error or success message to be printed to the user + */ + QString parsePushOutput(); + /** + * Parses the output of the git pull command and returns an appropriate message, + * that should be displayed to the user. + * @returns The error or success message to be printed to the user + */ + QString parsePullOutput(); + /** + * Executes the command "git {svnCommand}" for the files that have been + * set by getting the context menu actions (see contextMenuActions()). + * @param infoMsg Message that should be shown before the command is executed. + * @param errorMsg Message that should be shown if the execution of the command + * has failed. + * @param operationCompletedMsg + * Message that should be shown if the execution of the command + * has been completed successfully. + */ + void execGitCommand(const QString& gitCommand, + const QStringList& arguments, + const QString& infoMsg, + const QString& errorMsg, + const QString& operationCompletedMsg); + void startGitCommandProcess(); +private: + bool m_pendingOperation; + /** + * Contains all files in the current directory, whose version state is not + * NormalVersion and directories containing such files (except for directories + * whose only special contained file type is IgnoredVersion). + */ + QHash m_versionInfoHash; + QAction* m_addAction; + QAction* m_removeAction; + QAction* m_checkoutAction; + QAction* m_commitAction; + QAction* m_tagAction; + QAction* m_pushAction; + QAction* m_pullAction; + + QString m_currentDir; + QProcess m_process; + QString m_command; + QStringList m_arguments; + QString m_operationCompletedMsg; + QString m_errorMsg; + + //Current targets. m_contextItems is used if and only if m_contextDir is empty. + mutable QString m_contextDir; + mutable KFileItemList m_contextItems; +}; +#endif // FILEVIEWGITPLUGIN_H + diff --git a/dolphin/plugins/git/fileviewgitpluginsettings.kcfg b/dolphin/plugins/git/fileviewgitpluginsettings.kcfg new file mode 100644 index 00000000..79c1918b --- /dev/null +++ b/dolphin/plugins/git/fileviewgitpluginsettings.kcfg @@ -0,0 +1,18 @@ + + + + + + + + 300 + 50 + + + + 500 + 50 + + + + diff --git a/dolphin/plugins/git/fileviewgitpluginsettings.kcfgc b/dolphin/plugins/git/fileviewgitpluginsettings.kcfgc new file mode 100644 index 00000000..00494dcc --- /dev/null +++ b/dolphin/plugins/git/fileviewgitpluginsettings.kcfgc @@ -0,0 +1,5 @@ +File=fileviewgitpluginsettings.kcfg +ClassName=FileViewGitPluginSettings +Singleton=true +Mutators=true + diff --git a/dolphin/plugins/git/gitwrapper.cpp b/dolphin/plugins/git/gitwrapper.cpp new file mode 100644 index 00000000..719e1568 --- /dev/null +++ b/dolphin/plugins/git/gitwrapper.cpp @@ -0,0 +1,171 @@ +/****************************************************************************** + * Copyright (C) 2010 by Sebastian Doerner * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ******************************************************************************/ + +#include "gitwrapper.h" + +#include + +GitWrapper* GitWrapper::m_instance = 0; +const int GitWrapper::BUFFER_SIZE = 256; +const int GitWrapper::SMALL_BUFFER_SIZE = 128; + +GitWrapper::GitWrapper() +{ + m_localCodec = QTextCodec::codecForLocale(); +} + + +GitWrapper* GitWrapper::instance() +{ + if (m_instance == 0) { + m_instance = new GitWrapper(); + } + return m_instance; +} + +void GitWrapper::freeInstance() +{ + delete m_instance; + m_instance = 0; +} + +QString GitWrapper::userName() +{ + QString result(""); + char buffer[SMALL_BUFFER_SIZE]; + m_process.start("git config --get user.name"); + while (m_process.waitForReadyRead()) { + if (m_process.readLine(buffer, sizeof(buffer)) > 0) { + result = m_localCodec->toUnicode(buffer).trimmed(); + } + } + return result; +} + +QString GitWrapper::userEmail() +{ + QString result(""); + char buffer[SMALL_BUFFER_SIZE]; + m_process.start("git config --get user.email"); + while (m_process.waitForReadyRead()) { + if (m_process.readLine(buffer, sizeof(buffer)) > 0) { + result = m_localCodec->toUnicode(buffer).trimmed(); + } + } + return result; +} + + +QStringList GitWrapper::branches(int* currentBranchIndex) +{ + QStringList result; + if (currentBranchIndex != 0) { + *currentBranchIndex = -1; + } + m_process.start(QLatin1String("git branch -a")); + while (m_process.waitForReadyRead()) { + char buffer[BUFFER_SIZE]; + while (m_process.readLine(buffer, sizeof(buffer)) > 0){ + const QString branchName = m_localCodec->toUnicode(buffer).mid(2).trimmed(); + //don't list non-branches and HEAD-branches directly pointing to other branches + if (!branchName.contains("->") && !branchName.startsWith('(')) { + result.append(branchName); + if (currentBranchIndex !=0 && buffer[0]=='*') { + *currentBranchIndex = result.size() - 1; + } + } + } + } + return result; +} + +void GitWrapper::tagSet(QSet& result) +{ + m_process.start(QLatin1String("git tag")); + while (m_process.waitForReadyRead()) { + char buffer[BUFFER_SIZE]; + while (m_process.readLine(buffer, sizeof(buffer)) > 0){ + const QString tagName = m_localCodec->toUnicode(buffer).trimmed(); + result.insert(tagName); + } + } +} + +QStringList GitWrapper::tags() +{ + QStringList result; + m_process.start(QLatin1String("git tag")); + while (m_process.waitForReadyRead()) { + char buffer[BUFFER_SIZE]; + while (m_process.readLine(buffer, sizeof(buffer)) > 0){ + const QString tagName = m_localCodec->toUnicode(buffer).trimmed(); + result.append(tagName); + } + } + return result; +} + +inline QStringList GitWrapper::remotes(QLatin1String lineEnd) +{ + QStringList result; + m_process.start(QLatin1String("git remote -v")); + while (m_process.waitForReadyRead()) { + char buffer[BUFFER_SIZE]; + while (m_process.readLine(buffer, sizeof(buffer)) > 0){ + const QString line = QString(buffer).simplified(); + if (line.endsWith(lineEnd)) { + result.append(line.section(' ', 0, 0)); + } + } + } + return result; +} + +QStringList GitWrapper::pullRemotes() +{ + return remotes(QLatin1String("(fetch)")); +} + +QStringList GitWrapper::pushRemotes() +{ + return remotes(QLatin1String("(push)")); +} + +QString GitWrapper::lastCommitMessage() +{ + QString result; + char buffer[BUFFER_SIZE]; + m_process.start("git log -1"); + while (m_process.waitForReadyRead()) { + bool inMessage = false; + QStringList message; + while (m_process.readLine(buffer, sizeof(buffer)) > 0) { + const QString currentLine(buffer); + if (inMessage){ + message << m_localCodec->toUnicode(buffer).trimmed(); + } + else if (currentLine.startsWith(QLatin1String("Date:"))) { + m_process.readLine(); + inMessage = true; + } + } + result = message.join("\n"); + } + return result; +} diff --git a/dolphin/plugins/git/gitwrapper.h b/dolphin/plugins/git/gitwrapper.h new file mode 100644 index 00000000..19312770 --- /dev/null +++ b/dolphin/plugins/git/gitwrapper.h @@ -0,0 +1,105 @@ +/****************************************************************************** + * Copyright (C) 2010 by Sebastian Doerner * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ******************************************************************************/ + + +#ifndef GITWRAPPER_H +#define GITWRAPPER_H + +#include +#include +#include + +#include + +/** + * @brief A git wrapper class to access git functions conveniently using a Singleton interface. + */ +class GitWrapper +{ + private: + GitWrapper(); + static GitWrapper* m_instance; + ///size of the line buffers when parsing git output + static const int BUFFER_SIZE; + ///size of the line buffers when parsing shorter git output + static const int SMALL_BUFFER_SIZE; + QProcess m_process; + QTextCodec * m_localCodec; + /** + * Gets a list of all remote hosts, whose entry ends with \a lineEnd + * @param lineEnd The end of the lines, which should be returned as entries. + * Should be "(pull)" or "(push)". + */ + QStringList remotes(QLatin1String lineEnd); + public: + static GitWrapper* instance(); + static void freeInstance(); + void setWorkingDirectory(const QString& workingDirectory) { + m_process.setWorkingDirectory(workingDirectory); + } + + /** + * @returns The configured user.name of the repository, + the empty String (which is NOT null) if not configured. + */ + QString userName(); + /** + * @returns The configured user.email of the repository, + the empty String (which is NOT null) if not configured. + */ + QString userEmail(); + /** + * Gets a list of all branches in the repository. + * @param currentBranchIndex + * Pointer to the location in which to store the index of the current branch + * within the returned QStringList. Set to -1 if currently not on any branch. + * @returns A StringList containing the names of all local and remote branches. + */ + QStringList branches(int * currentBranchIndex = 0); + + /** + * Gets a list of all tags in the repository. + * @returns A list containing the tag names + */ + QStringList tags(); + /** + * Gets a set of all tags in the repository. + * @param result Set to store the results in. + */ + void tagSet(QSet& result); + + /** + * Gets a list of all remote hosts we can pull from. + * @returns The list containing the remote hosts. + */ + QStringList pullRemotes(); + + /** + * Gets a list of all remote hosts we can push to. + * @returns The list containing the remote hosts. + */ + QStringList pushRemotes(); + + /** + * @returns The last commit message. Null-String if there is no last commit. + */ + QString lastCommitMessage(); +}; + +#endif // GITWRAPPER_H diff --git a/dolphin/plugins/git/pulldialog.cpp b/dolphin/plugins/git/pulldialog.cpp new file mode 100644 index 00000000..ecc0c7dd --- /dev/null +++ b/dolphin/plugins/git/pulldialog.cpp @@ -0,0 +1,102 @@ +/****************************************************************************** + * Copyright (C) 2010 by Sebastian Doerner * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ******************************************************************************/ + +#include "pulldialog.h" +#include "gitwrapper.h" + +#include +#include +#include +#include +#include +#include + +PullDialog::PullDialog(QWidget* parent): + KDialog(parent, Qt::Dialog) +{ + this->setCaption(i18nc("@title:window", "Git Pull")); + this->setButtons(KDialog::Ok | KDialog::Cancel); + this->setDefaultButton(KDialog::Ok); + this->setButtonText(KDialog::Ok, i18nc("@action:button", "Pull")); + + KVBox * vbox = new KVBox(this); + this->setMainWidget(vbox); + + QGroupBox * sourceGroupBox = new QGroupBox(vbox); + sourceGroupBox->setTitle(i18nc("@title:group The source to pull from", "Source")); + QHBoxLayout * sourceHBox = new QHBoxLayout(sourceGroupBox); + sourceGroupBox->setLayout(sourceHBox); + + QLabel * remoteLabel = new QLabel(i18nc("@label:listbox a git remote", "Remote:"), sourceGroupBox); + sourceHBox->addWidget(remoteLabel); + m_remoteComboBox = new KComboBox(false, sourceGroupBox); + sourceHBox->addWidget(m_remoteComboBox); + + QLabel * remoteBranchLabel = new QLabel(i18nc("@label:listbox", "Remote branch:"), sourceGroupBox); + sourceHBox->addWidget(remoteBranchLabel); + m_remoteBranchComboBox = new KComboBox(false, sourceGroupBox); + sourceHBox->addWidget(m_remoteBranchComboBox); + + //populate UI + GitWrapper * gitWrapper = GitWrapper::instance(); + + //get sources + m_remoteComboBox->addItems(gitWrapper->pullRemotes()); + + //get branch names + int currentBranchIndex; + QStringList branches = gitWrapper->branches(¤tBranchIndex); + + for (int i=0; i < branches.size(); ++i) { + if (branches[i].startsWith(QLatin1String("remotes/")) && + branches[i].count(QChar('/')) > 1) { + const QStringList sections = branches[i].split('/'); + QHash::iterator entry = m_remoteBranches.find(sections.at(1)); + if (entry == m_remoteBranches.end()) { + m_remoteBranches.insert(sections.at(1), QStringList() << sections.at(2)); + } else { + entry.value().append(sections.at(2)); + } + } + } + remoteSelectionChanged(m_remoteComboBox->currentText()); + + //Signals + connect(m_remoteComboBox, SIGNAL(currentIndexChanged(QString)), + this, SLOT(remoteSelectionChanged(QString))); +} + +QString PullDialog::source() const +{ + return m_remoteComboBox->currentText(); +} + +QString PullDialog::remoteBranch() const +{ + return m_remoteBranchComboBox->currentText(); +} + +void PullDialog::remoteSelectionChanged(const QString& newRemote) +{ + m_remoteBranchComboBox->clear(); + m_remoteBranchComboBox->addItems(m_remoteBranches.value(newRemote)); + this->enableButtonOk(m_remoteBranchComboBox->count() > 0); +} + +#include "moc_pulldialog.cpp" diff --git a/dolphin/plugins/git/pulldialog.h b/dolphin/plugins/git/pulldialog.h new file mode 100644 index 00000000..861cb802 --- /dev/null +++ b/dolphin/plugins/git/pulldialog.h @@ -0,0 +1,42 @@ +/****************************************************************************** + * Copyright (C) 2010 by Sebastian Doerner * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ******************************************************************************/ + +#ifndef PULLDIALOG_H +#define PULLDIALOG_H + +#include + +class KComboBox; +class PullDialog : public KDialog +{ + Q_OBJECT + +public: + PullDialog(QWidget* parent = 0); + QString source() const; + QString remoteBranch() const; +private: + KComboBox * m_remoteComboBox; + KComboBox * m_remoteBranchComboBox; + QHash m_remoteBranches; +private slots: + void remoteSelectionChanged(const QString& newRemote); +}; + +#endif // PULLDIALOG_H diff --git a/dolphin/plugins/git/pushdialog.cpp b/dolphin/plugins/git/pushdialog.cpp new file mode 100644 index 00000000..d5ab5a1e --- /dev/null +++ b/dolphin/plugins/git/pushdialog.cpp @@ -0,0 +1,155 @@ +/****************************************************************************** + * Copyright (C) 2010 by Sebastian Doerner * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ******************************************************************************/ + +#include "pushdialog.h" +#include "gitwrapper.h" + +#include +#include +#include +#include +#include +#include +#include + +PushDialog::PushDialog (QWidget* parent ): + KDialog (parent, Qt::Dialog) +{ + this->setCaption(i18nc("@title:window", "Git Push")); + this->setButtons(KDialog::Ok | KDialog::Cancel); + this->setDefaultButton(KDialog::Ok); + this->setButtonText(KDialog::Ok, i18nc("@action:button", "Push")); + + KVBox * vbox = new KVBox(this); + this->setMainWidget(vbox); + + //Destination + QGroupBox * destinationGroupBox = new QGroupBox(vbox); + destinationGroupBox->setTitle(i18nc("@title:group The remote host", "Destination")); + QHBoxLayout * destinationHBox = new QHBoxLayout(destinationGroupBox); + destinationGroupBox->setLayout(destinationHBox); + + QLabel * remoteLabel = new QLabel(i18nc("@label:listbox a git remote", "Remote:"), destinationGroupBox); + destinationHBox->addWidget(remoteLabel); + m_remoteComboBox = new KComboBox(false, destinationGroupBox); + destinationHBox->addWidget(m_remoteComboBox); + destinationHBox->addStretch(); + + //Branches + QGroupBox* branchesGroupBox = new QGroupBox(vbox); + branchesGroupBox->setTitle(i18nc("@title:group", "Branches")); + QHBoxLayout * branchesHBox = new QHBoxLayout(branchesGroupBox); + branchesGroupBox->setLayout(branchesHBox); + + QLabel * localBranchLabel = new QLabel(i18nc("@label:listbox", "Local Branch:"), branchesGroupBox); + branchesHBox->addWidget(localBranchLabel); + m_localBranchComboBox = new KComboBox(false, branchesGroupBox); + branchesHBox->addWidget(m_localBranchComboBox); + + branchesHBox->addStretch(); + + QLabel * remoteBranchLabel = new QLabel(i18nc("@label:listbox", "Remote Branch:"), branchesGroupBox); + branchesHBox->addWidget(remoteBranchLabel); + m_remoteBranchComboBox = new KComboBox(false, branchesGroupBox); + branchesHBox->addWidget(m_remoteBranchComboBox); + + QGroupBox* optionsGroupBox = new QGroupBox(vbox); + optionsGroupBox->setTitle(i18nc("@title:group", "Options")); + QHBoxLayout * optionsHBox = new QHBoxLayout(optionsGroupBox); + optionsGroupBox->setLayout(optionsHBox); + m_forceCheckBox = new QCheckBox(i18nc("@option:check", "Force"), optionsGroupBox); + m_forceCheckBox->setToolTip(i18nc("@info:tooltip", "Proceed even if the remote branch is not an ancestor of the local branch.")); + optionsHBox->addWidget(m_forceCheckBox); + + //populate UI + GitWrapper * gitWrapper = GitWrapper::instance(); + + //get destinations + QStringList remotes = gitWrapper->pushRemotes(); + m_remoteComboBox->addItems(remotes); + + //get branch names + int currentBranchIndex; + QStringList branches = gitWrapper->branches(¤tBranchIndex); + + for (int i=0; i < branches.size(); ++i){ + if (branches[i].startsWith(QLatin1String("remotes/"))) { + const QStringList sections = branches[i].split('/'); + if (sections.size() > 2) { + QHash::iterator entry = m_remoteBranches.find(sections.at(1)); + if (entry == m_remoteBranches.end()) { + m_remoteBranches.insert(sections.at(1), QStringList() << sections.at(2)); + } else { + entry.value().append(sections.at(2)); + } + } + } else { + m_localBranchComboBox->addItem(branches[i]); + if (i == currentBranchIndex) { + m_localBranchComboBox->setCurrentIndex(m_localBranchComboBox->count()-1); + } + } + } + remoteSelectionChanged(m_remoteComboBox->currentText()); + + //Signals + connect(m_remoteComboBox, SIGNAL(currentIndexChanged(QString)), + this, SLOT(remoteSelectionChanged(QString))); + connect(m_localBranchComboBox, SIGNAL(currentIndexChanged(QString)), + this, SLOT(localBranchSelectionChanged(QString))); +} + +QString PushDialog::destination() const +{ + return m_remoteComboBox->currentText(); +} + +QString PushDialog::localBranch() const +{ + return m_localBranchComboBox->currentText(); +} + +QString PushDialog::remoteBranch() const +{ + return m_remoteBranchComboBox->currentText(); +} + +bool PushDialog::force() const +{ + return m_forceCheckBox->isChecked(); +} + +void PushDialog::remoteSelectionChanged(const QString& newRemote) +{ + m_remoteBranchComboBox->clear(); + m_remoteBranchComboBox->addItems(m_remoteBranches.value(newRemote)); + localBranchSelectionChanged(m_localBranchComboBox->currentText()); +} + +void PushDialog::localBranchSelectionChanged(const QString& newLocalBranch) +{ + //select matching remote branch if possible + const int index = m_remoteBranchComboBox->findText(newLocalBranch); + if (index != -1) { + m_remoteBranchComboBox->setCurrentIndex(index); + } + this->enableButtonOk(m_remoteBranchComboBox->count() > 0); +} + +#include "moc_pushdialog.cpp" diff --git a/dolphin/plugins/git/pushdialog.h b/dolphin/plugins/git/pushdialog.h new file mode 100644 index 00000000..8cffc8f2 --- /dev/null +++ b/dolphin/plugins/git/pushdialog.h @@ -0,0 +1,50 @@ +/****************************************************************************** + * Copyright (C) 2010 by Sebastian Doerner * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ******************************************************************************/ + +#ifndef PUSHDIALOG_H +#define PUSHDIALOG_H + +#include + +class KComboBox; +#include + +class PushDialog : public KDialog +{ + Q_OBJECT + +public: + PushDialog(QWidget* parent = 0); + QString destination() const; + QString localBranch() const; + QString remoteBranch() const; + bool force() const; +private slots: + void remoteSelectionChanged(const QString& newRemote); + void localBranchSelectionChanged(const QString& newLocalBranch); +private: + QHash m_remoteBranches; + + KComboBox * m_remoteComboBox; + KComboBox * m_localBranchComboBox; + KComboBox * m_remoteBranchComboBox; + QCheckBox * m_forceCheckBox; +}; + +#endif // PUSHDIALOG_H diff --git a/dolphin/plugins/git/tagdialog.cpp b/dolphin/plugins/git/tagdialog.cpp new file mode 100644 index 00000000..086101f8 --- /dev/null +++ b/dolphin/plugins/git/tagdialog.cpp @@ -0,0 +1,141 @@ +/****************************************************************************** + * Copyright (C) 2010 by Johannes Steffen * + * Copyright (C) 2010 by Sebastian Doerner * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ******************************************************************************/ + +#include "tagdialog.h" +#include "gitwrapper.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +TagDialog::TagDialog (QWidget* parent ): + KDialog (parent, Qt::Dialog), + m_localCodec(QTextCodec::codecForLocale()) +{ + this->setCaption(i18nc("@title:window", "Git Create Tag")); + this->setButtons(KDialog::Ok | KDialog::Cancel); + this->setDefaultButton(KDialog::Ok); + this->setButtonText(KDialog::Ok, i18nc("@action:button", "Create Tag")); + + KVBox* vbox = new KVBox(this); + this->setMainWidget(vbox); + + QGroupBox* tagInformationGroupBox = new QGroupBox(vbox); + tagInformationGroupBox->setTitle(i18nc("@title:group", "Tag Information")); + QVBoxLayout * tagInformationLayout = new QVBoxLayout(tagInformationGroupBox); + tagInformationGroupBox->setLayout(tagInformationLayout); + + QLabel* nameLabel = new QLabel(i18nc("@label:textbox", "Tag Name:"), tagInformationGroupBox); + tagInformationLayout->addWidget(nameLabel); + + m_tagNameTextEdit = new KLineEdit(tagInformationGroupBox); + tagInformationLayout->addWidget(m_tagNameTextEdit); + setOkButtonState(); + connect(m_tagNameTextEdit, SIGNAL(textChanged(QString)), this, SLOT(setOkButtonState())); + + QLabel* messageLabel = new QLabel(i18nc("@label:textbox", "Tag Message:"), tagInformationGroupBox); + tagInformationLayout->addWidget(messageLabel); + + m_tagMessageTextEdit = new KTextEdit(tagInformationGroupBox); + m_tagMessageTextEdit->setLineWrapMode(QTextEdit::FixedColumnWidth); + m_tagMessageTextEdit->setLineWrapColumnOrWidth(72); + tagInformationLayout->addWidget(m_tagMessageTextEdit); + + QGroupBox* attachToGroupBox = new QGroupBox(vbox); + attachToGroupBox->setTitle(i18nc("@title:group", "Attach to")); + + QHBoxLayout* attachToLayout = new QHBoxLayout(); + attachToGroupBox->setLayout(attachToLayout); + + QLabel* branchLabel = new QLabel(i18nc("@label:listbox", "Branch:"), attachToGroupBox); + attachToLayout->addWidget(branchLabel); + + m_branchComboBox = new KComboBox(false, attachToGroupBox); + attachToLayout->addWidget(m_branchComboBox); + attachToLayout->addStretch(); + + this->setInitialSize(QSize(300,200)); + + //initialize alternate color scheme for errors + m_errorColors = m_tagNameTextEdit->palette(); + m_errorColors.setColor(QPalette::Normal, QPalette::Base, Qt::red); + m_errorColors.setColor(QPalette::Inactive, QPalette::Base, Qt::red); + + //get branch & tag names + GitWrapper * gitWrapper = GitWrapper::instance(); + + int currentIndex; + const QStringList branches = gitWrapper->branches(¤tIndex); + m_branchComboBox->addItems(branches); + m_branchComboBox->setCurrentIndex(currentIndex); + + gitWrapper->tagSet(m_tagNames); +} + + +QByteArray TagDialog::tagMessage() const +{ + return m_localCodec->fromUnicode(m_tagMessageTextEdit->toPlainText()); +} + +QString TagDialog::tagName() const +{ + return m_tagNameTextEdit->text().trimmed(); +} + +QString TagDialog::baseBranch() const +{ + return m_branchComboBox->currentText(); +} + +void TagDialog::setOkButtonState() +{ + const QString tagName = m_tagNameTextEdit->text().trimmed(); + QString toolTip; + if (tagName.isEmpty()) { + toolTip = i18nc("@info:tooltip", "You must enter a tag name first."); + } + else if (tagName.contains(QRegExp("\\s"))) { + toolTip = i18nc("@info:tooltip", "Tag names may not contain any whitespace."); + } + else if (m_tagNames.contains(tagName)) { + toolTip = i18nc("@info:tooltip", "A tag named '%1' already exists.", tagName); + } + this->enableButtonOk(toolTip.isEmpty()); + setLineEditErrorModeActive(!toolTip.isEmpty()); + m_tagNameTextEdit->setToolTip(toolTip); + this->setButtonToolTip(KDialog::Ok, toolTip); + +} + +void TagDialog::setLineEditErrorModeActive(bool active) +{ + m_tagNameTextEdit->setPalette(active ? m_errorColors : QPalette()); +} + +#include "moc_tagdialog.cpp" diff --git a/dolphin/plugins/git/tagdialog.h b/dolphin/plugins/git/tagdialog.h new file mode 100644 index 00000000..271c2311 --- /dev/null +++ b/dolphin/plugins/git/tagdialog.h @@ -0,0 +1,67 @@ +/****************************************************************************** + * Copyright (C) 2010 by Johannes Steffen * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ******************************************************************************/ + +#ifndef TAGDIALOG_H +#define TAGDIALOG_H + +#include +#include + +class KTextEdit; +class KLineEdit; +class KComboBox; +#include +#include + +class TagDialog : public KDialog +{ + Q_OBJECT + +public: + TagDialog(QWidget* parent = 0); + /** + * Returns the tag message given by the user. + * @returns The tag message. + */ + QByteArray tagMessage() const; + /** + * Returns the tag name given by the user. + * @return The tag name. + */ + QString tagName() const; + /** + * @returns The name of the branch the tag should point to or HEAD if + the tag should point to the current HEAD. + */ + + QString baseBranch() const; +private slots: + void setOkButtonState(); +private: + inline void setLineEditErrorModeActive(bool active); +private: + QSet m_tagNames; + KTextEdit* m_tagMessageTextEdit; + KLineEdit* m_tagNameTextEdit; + KComboBox* m_branchComboBox; + QTextCodec* m_localCodec; + QPalette m_errorColors; +}; + +#endif // TAGDIALOG_H diff --git a/dolphin/plugins/hg/CMakeLists.txt b/dolphin/plugins/hg/CMakeLists.txt new file mode 100644 index 00000000..3f3938a4 --- /dev/null +++ b/dolphin/plugins/hg/CMakeLists.txt @@ -0,0 +1,54 @@ +project(fileviewhgplugin) + +set(fileviewhgplugin_SRCS + fileviewhgplugin.cpp + renamedialog.cpp + commitdialog.cpp + hgwrapper.cpp + statuslist.cpp + branchdialog.cpp + tagdialog.cpp + updatedialog.cpp + clonedialog.cpp + createdialog.cpp + syncdialogbase.cpp + mergedialog.cpp + pushdialog.cpp + pulldialog.cpp + hgconfig.cpp + configdialog.cpp + commititemdelegate.cpp + commitinfowidget.cpp + pathselector.cpp + bundledialog.cpp + exportdialog.cpp + importdialog.cpp + servedialog.cpp + servewrapper.cpp + backoutdialog.cpp + config-widgets/generalconfig.cpp + config-widgets/pathconfig.cpp + config-widgets/ignorewidget.cpp + config-widgets/pluginsettings.cpp +) + +kde4_add_kcfg_files(fileviewhgplugin_SRCS fileviewhgpluginsettings.kcfgc) + +kde4_add_plugin(fileviewhgplugin ${fileviewhgplugin_SRCS}) + +target_link_libraries(fileviewhgplugin ${KDE4_KIO_LIBS} ${KDE4_KTEXTEDITOR_LIBS} konq) + +install( + FILES fileviewhgplugin.desktop + DESTINATION ${KDE4_SERVICES_INSTALL_DIR} +) + +install( + FILES fileviewhgpluginsettings.kcfg + DESTINATION ${KDE4_KCFG_INSTALL_DIR} +) + +install( + TARGETS fileviewhgplugin + DESTINATION ${KDE4_PLUGIN_INSTALL_DIR} +) diff --git a/dolphin/plugins/hg/Messages.sh b/dolphin/plugins/hg/Messages.sh new file mode 100644 index 00000000..e140d8f2 --- /dev/null +++ b/dolphin/plugins/hg/Messages.sh @@ -0,0 +1,3 @@ +#! /bin/sh +$EXTRACTRC *.kcfg >> rc.cpp +$XGETTEXT *.cpp -o $podir/fileviewhgplugin.pot diff --git a/dolphin/plugins/hg/backoutdialog.cpp b/dolphin/plugins/hg/backoutdialog.cpp new file mode 100644 index 00000000..07b62e0b --- /dev/null +++ b/dolphin/plugins/hg/backoutdialog.cpp @@ -0,0 +1,226 @@ +/*************************************************************************** + * Copyright (C) 2011 by Vishesh Yadav * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "backoutdialog.h" +#include "commitinfowidget.h" +#include "fileviewhgpluginsettings.h" +#include "hgwrapper.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +HgBackoutDialog::HgBackoutDialog(QWidget *parent) : + KDialog(parent, Qt::Dialog) +{ + // dialog properties + this->setCaption(i18nc("@title:window", + "Hg Backout")); + this->setButtons(KDialog::Ok | KDialog::Cancel); + this->setDefaultButton(KDialog::Ok); + this->setButtonText(KDialog::Ok, i18nc("@action:button", "Backout")); + this->enableButtonOk(false); + + // + setupUI(); + + // Load saved settings + FileViewHgPluginSettings *settings = FileViewHgPluginSettings::self(); + this->setInitialSize(QSize(settings->backoutDialogWidth(), + settings->backoutDialogHeight())); + + // connections + connect(this, SIGNAL(finished()), this, SLOT(saveGeometry())); + connect(m_selectBaseCommitButton, SIGNAL(clicked()), + this, SLOT(slotSelectBaseChangeset())); + connect(m_selectParentCommitButton, SIGNAL(clicked()), + this, SLOT(slotSelectParentChangeset())); + connect(m_baseRevision, SIGNAL(textChanged(const QString&)), + this, SLOT(slotUpdateOkButton(const QString&))); +} + +void HgBackoutDialog::setupUI() +{ + m_mainGroup = new QGroupBox; + m_baseRevision = new KLineEdit; + m_parentRevision = new KLineEdit; + m_optMerge = new QCheckBox(i18nc("@label:checkbox", + "Merge with old dirstate parent after backout")); + m_selectParentCommitButton = new KPushButton(i18nc("@label:button", + "Select Changeset")); + m_selectBaseCommitButton = new KPushButton(i18nc("@label:button", + "Select Changeset")); + QGridLayout *mainGroupLayout = new QGridLayout; + + mainGroupLayout->addWidget(new QLabel(i18nc("@label", + "Revision to Backout: ")), 0, 0); + mainGroupLayout->addWidget(m_baseRevision, 0, 1); + mainGroupLayout->addWidget(m_selectBaseCommitButton, 0, 2); + + mainGroupLayout->addWidget(new QLabel(i18nc("@label", + "Parent Revision (optional): ")), 1, 0); + mainGroupLayout->addWidget(m_parentRevision, 1, 1); + mainGroupLayout->addWidget(m_selectParentCommitButton, 1, 2); + + mainGroupLayout->addWidget(m_optMerge, 2, 0, 1, 0); + + m_mainGroup->setLayout(mainGroupLayout); + + QWidget *widget = new QWidget; + QVBoxLayout *layout = new QVBoxLayout; + layout->addWidget(m_mainGroup); + widget->setLayout(layout); + setMainWidget(widget); +} + +void HgBackoutDialog::saveGeometry() +{ + FileViewHgPluginSettings *settings = FileViewHgPluginSettings::self(); + settings->setBackoutDialogHeight(this->height()); + settings->setBackoutDialogWidth(this->width()); + settings->writeConfig(); +} + +void HgBackoutDialog::loadCommits() +{ + HgWrapper *hgWrapper = HgWrapper::instance(); + + // update heads list + QProcess process; + process.setWorkingDirectory(hgWrapper->getBaseDir()); + + QStringList args; + args << QLatin1String("log"); + args << QLatin1String("--template"); + args << QLatin1String("{rev}\n{node|short}\n{branch}\n" + "{author}\n{desc|firstline}\n"); + + process.start(QLatin1String("hg"), args); + process.waitForFinished(); + m_commitInfo->clear(); + + const int FINAL = 5; + char buffer[FINAL][1024]; + int count = 0; + while (process.readLine(buffer[count], sizeof(buffer[count])) > 0) { + if (count == FINAL - 1) { + QString rev = QTextCodec::codecForLocale()->toUnicode(buffer[0]).trimmed(); + QString changeset = QTextCodec::codecForLocale()->toUnicode(buffer[1]).trimmed(); + QString branch = QTextCodec::codecForLocale()->toUnicode(buffer[2]).trimmed(); + QString author = QTextCodec::codecForLocale()->toUnicode(buffer[3]).trimmed(); + QString log = QTextCodec::codecForLocale()->toUnicode(buffer[4]).trimmed(); + + QListWidgetItem *item = new QListWidgetItem; + item->setData(Qt::DisplayRole, changeset); + item->setData(Qt::UserRole + 1, rev); + item->setData(Qt::UserRole + 2, branch); + item->setData(Qt::UserRole + 3, author); + item->setData(Qt::UserRole + 4, log); + m_commitInfo->addItem(item); + } + count = (count + 1)%FINAL; + } +} + +QString HgBackoutDialog::selectChangeset() +{ + KDialog diag; + diag.setCaption(i18nc("@title:window", + "Select Changeset")); + diag.setButtons(KDialog::Ok | KDialog::Cancel); + diag.setDefaultButton(KDialog::Ok); + diag.setButtonText(KDialog::Ok, i18nc("@action:button", "Select")); + + diag.setMinimumWidth(700); + + m_commitInfo = new HgCommitInfoWidget; + loadCommits(); + diag.setMainWidget(m_commitInfo); + + if (diag.exec() == KDialog::Accepted) { + return m_commitInfo->selectedChangeset(); + } + return QString(); +} + +void HgBackoutDialog::slotSelectBaseChangeset() +{ + QString changeset = selectChangeset(); + if (!changeset.isEmpty()) { + m_baseRevision->setText(changeset); + } +} + +void HgBackoutDialog::slotSelectParentChangeset() +{ + QString changeset = selectChangeset(); + if (!changeset.isEmpty()) { + m_parentRevision->setText(changeset); + } +} + +void HgBackoutDialog::done(int r) +{ + if (r == KDialog::Accepted) { + HgWrapper *hgw = HgWrapper::instance(); + QStringList args; + args << QLatin1String("--rev"); + args << m_baseRevision->text(); + + if (!m_parentRevision->text().isEmpty()) { + args << QLatin1String("--parent"); + args << m_parentRevision->text(); + } + + if (m_optMerge->checkState() == Qt::Checked) { + args << QLatin1String("--merge"); + } + + if (hgw->executeCommandTillFinished(QLatin1String("backout"), args)) { + KMessageBox::information(this, hgw->readAllStandardOutput()); + KDialog::done(r); + } + else { + KMessageBox::error(this, hgw->readAllStandardError()); + } + } + else { + KDialog::done(r); + } +} + +void HgBackoutDialog::slotUpdateOkButton(const QString &text) +{ + kDebug() << "text"; + enableButtonOk(!text.isEmpty()); +} + +#include "moc_backoutdialog.cpp" + diff --git a/dolphin/plugins/hg/backoutdialog.h b/dolphin/plugins/hg/backoutdialog.h new file mode 100644 index 00000000..5586a636 --- /dev/null +++ b/dolphin/plugins/hg/backoutdialog.h @@ -0,0 +1,79 @@ +/*************************************************************************** + * Copyright (C) 2011 by Vishesh Yadav * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef HG_BACKOUT_DIALOG +#define HG_BACKOUT_DIALOG + +#include + +#include +#include +class KLineEdit; +class HgCommitInfoWidget; +class KPushButton; + +/** + * Implements dialog for Mercurial's backout feature. + */ +class HgBackoutDialog : public KDialog +{ + Q_OBJECT + +public: + HgBackoutDialog(QWidget *parent = 0); + +public slots: + void done(int r); + +private slots: + void saveGeometry(); + void slotSelectBaseChangeset(); + void slotSelectParentChangeset(); + void slotUpdateOkButton(const QString &text); + +private: + void setupUI(); + + /** + * Find appropriate changesets in repository and show them in + * Commit Selector (CommitInfoWidget) + */ + void loadCommits(); + + /** + * Opens a dialog showing all changesets in a list and their respective + * information when highlighted. + */ + QString selectChangeset(); + +private: + QGroupBox *m_mainGroup; + HgCommitInfoWidget *m_commitInfo; + + KPushButton *m_selectBaseCommitButton; + KLineEdit *m_baseRevision; + + KPushButton *m_selectParentCommitButton; + KLineEdit *m_parentRevision; + + QCheckBox *m_optMerge; +}; + +#endif /* HG_BACKOUT_DIALOG */ + diff --git a/dolphin/plugins/hg/branchdialog.cpp b/dolphin/plugins/hg/branchdialog.cpp new file mode 100644 index 00000000..d218cc1d --- /dev/null +++ b/dolphin/plugins/hg/branchdialog.cpp @@ -0,0 +1,147 @@ +/*************************************************************************** + * Copyright (C) 2011 by Vishesh Yadav * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "branchdialog.h" +#include "hgwrapper.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +HgBranchDialog::HgBranchDialog(QWidget *parent): + KDialog(parent, Qt::Dialog) +{ + // dialog properties + this->setCaption(i18nc("@title:window", + "Hg Branch")); + this->setButtons(KDialog::None); + + // UI + QFrame *frame = new QFrame; + QVBoxLayout *vbox = new QVBoxLayout; + + m_currentBranchLabel = new QLabel; + vbox->addWidget(m_currentBranchLabel); + + m_branchComboBox = new KComboBox; + m_branchComboBox->setEditable(true); + vbox->addWidget(m_branchComboBox); + + QHBoxLayout *buttonLayout = new QHBoxLayout; + m_createBranch = new KPushButton(i18n("Create New Branch")); + m_updateBranch = new KPushButton(i18n("Switch Branch")); + buttonLayout->addWidget(m_createBranch); + buttonLayout->addWidget(m_updateBranch); + vbox->addLayout(buttonLayout); + + m_createBranch->setEnabled(false); + m_updateBranch->setEnabled(false); + + frame->setLayout(vbox); + updateInitialDialog(); + slotUpdateDialog(QString()); + setMainWidget(frame); + + slotUpdateDialog(m_branchComboBox->currentText()); + QLineEdit *m_lineEdit = m_branchComboBox->lineEdit(); + + // connections + connect(m_createBranch, SIGNAL(clicked()), + this, SLOT(slotCreateBranch())); + connect(m_updateBranch, SIGNAL(clicked()), + this, SLOT(slotSwitch())); + connect(m_branchComboBox, SIGNAL(editTextChanged(const QString&)), + this, SLOT(slotUpdateDialog(const QString &))); + connect(m_lineEdit, SIGNAL(textChanged(const QString&)), + this, SLOT(slotUpdateDialog(const QString&))); +} + +void HgBranchDialog::updateInitialDialog() +{ + HgWrapper *hgWrapper = HgWrapper::instance(); + + // update label - current branch + QString out; + hgWrapper->executeCommand(QLatin1String("branch"), QStringList(), out); + out = i18n("Current Branch: ") + out; + m_currentBranchLabel->setText(out); + + // update combo box + m_branchList = hgWrapper->getBranches(); + m_branchComboBox->addItems(m_branchList); + +} + +void HgBranchDialog::slotUpdateDialog(const QString &text) +{ + // update pushbuttons + if (text.length() == 0) { + m_createBranch->setEnabled(false); + m_updateBranch->setEnabled(false); + } + else if (m_branchList.contains(text)) { + m_createBranch->setEnabled(false); + m_updateBranch->setEnabled(true); + } + else { + m_createBranch->setEnabled(true); + m_updateBranch->setEnabled(false); + } +} + +void HgBranchDialog::slotSwitch() +{ + HgWrapper *hgWrapper = HgWrapper::instance(); + QString out; + QStringList args; + args << QLatin1String("-c"); + args << m_branchComboBox->currentText(); + if (hgWrapper->executeCommand(QLatin1String("update"), args, out)) { + //KMessageBox::information(this, i18n("Updated working directory!")); + done(KDialog::Ok); + } + else { + KMessageBox::error(this, i18n("Some error occurred")); + } +} + +void HgBranchDialog::slotCreateBranch() +{ + HgWrapper *hgWrapper = HgWrapper::instance(); + QString out; + QStringList args; + args << m_branchComboBox->currentText(); + if (hgWrapper->executeCommand(QLatin1String("branch"), args, out)) { + //KMessageBox::information(this, i18n("Created branch successfully!")); + done(KDialog::Ok); + } + else { + KMessageBox::error(this, i18n("Some error occurred")); + } +} + +#include "moc_branchdialog.cpp" + diff --git a/dolphin/plugins/hg/branchdialog.h b/dolphin/plugins/hg/branchdialog.h new file mode 100644 index 00000000..58c1dd94 --- /dev/null +++ b/dolphin/plugins/hg/branchdialog.h @@ -0,0 +1,59 @@ +/*************************************************************************** + * Copyright (C) 2011 by Vishesh Yadav * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef HGBRANCH_H +#define HGBRANCH_H + +#include +#include + +class KComboBox; +class KPushButton; +#include + +/** + * Implements dialog to list & create branches and update/switch working + * directory to differnt branch. + */ +class HgBranchDialog : public KDialog +{ + Q_OBJECT + +public: + HgBranchDialog(QWidget *parent = 0); + +public slots: + void slotUpdateDialog(const QString &text); + void slotCreateBranch(); + void slotSwitch(); + +private: + void updateInitialDialog(); + +private: + KComboBox *m_branchComboBox; + KPushButton *m_createBranch; + KPushButton *m_updateBranch; + QLabel *m_currentBranchLabel; + + QStringList m_branchList; +}; + +#endif // HGBRANCH_H + diff --git a/dolphin/plugins/hg/bundledialog.cpp b/dolphin/plugins/hg/bundledialog.cpp new file mode 100644 index 00000000..47451c4c --- /dev/null +++ b/dolphin/plugins/hg/bundledialog.cpp @@ -0,0 +1,237 @@ +/*************************************************************************** + * Copyright (C) 2011 by Vishesh Yadav * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "bundledialog.h" +#include "commitinfowidget.h" +#include "pathselector.h" +#include "fileviewhgpluginsettings.h" +#include "hgwrapper.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +HgBundleDialog::HgBundleDialog(QWidget *parent) : + KDialog(parent, Qt::Dialog) +{ + this->setCaption(i18nc("@title:window", + "Hg Bundle")); + this->setButtons(KDialog::Ok | KDialog::Cancel); + this->setDefaultButton(KDialog::Ok); + this->setButtonText(KDialog::Ok, i18nc("@action:button", "Bundle")); + + // Load saved settings + FileViewHgPluginSettings *settings = FileViewHgPluginSettings::self(); + this->setInitialSize(QSize(settings->bundleDialogWidth(), + settings->bundleDialogHeight())); + // + setupUI(); + + // connections + connect(this, SIGNAL(finished()), this, SLOT(saveGeometry())); + connect(m_selectCommitButton, SIGNAL(clicked()), + this, SLOT(slotSelectChangeset())); + connect(m_allChangesets, SIGNAL(stateChanged(int)), + this, SLOT(slotAllChangesCheckToggled(int))); +} + +void HgBundleDialog::setupUI() +{ + QVBoxLayout *layout = new QVBoxLayout; + + // main + m_pathSelect = new HgPathSelector; + m_baseRevision = new KLineEdit; + m_selectCommitButton = new KPushButton(i18nc("@label:button", + "Select Changeset")); + QLabel *baseRevisionLabel = new QLabel(i18nc("@label", + "Base Revision (optional): ")); + m_allChangesets = new QCheckBox(i18nc("@label", + "Bundle all changesets in repository.")); + + QGridLayout *bodyLayout = new QGridLayout; + bodyLayout->addWidget(m_pathSelect, 0, 0, 2, 0); + bodyLayout->addWidget(baseRevisionLabel, 2, 0); + bodyLayout->addWidget(m_baseRevision, 2, 1); + bodyLayout->addWidget(m_selectCommitButton, 2, 2); + bodyLayout->addWidget(m_allChangesets, 3, 0, 2, 0); + + m_mainGroup = new QGroupBox; + m_mainGroup->setLayout(bodyLayout); + + layout->addWidget(m_mainGroup); + + // options + m_optionGroup = new QGroupBox(i18nc("@label:group", "Options")); + m_optForce = new QCheckBox(i18nc("@label:checkbox", + "Run even when the destination is " + "unrelated (force)")); + m_optInsecure = new QCheckBox(i18nc("@label:checkbox", + "Do not verify server certificate")); + + QVBoxLayout *optionLayout = new QVBoxLayout; + optionLayout->addWidget(m_optForce); + optionLayout->addWidget(m_optInsecure); + m_optionGroup->setLayout(optionLayout); + + layout->addWidget(m_optionGroup); + //end options + + QWidget *widget = new QWidget; + widget->setLayout(layout); + setMainWidget(widget); +} + +void HgBundleDialog::done(int r) +{ + if (r == KDialog::Accepted) { + QString result = KFileDialog::getSaveFileName(); + if (result.length() > 0) { + createBundle(result); + KDialog::done(r); + } + } + else { + KDialog::done(r); + } +} + +void HgBundleDialog::createBundle(const QString &fileName) +{ + HgWrapper *hgw = HgWrapper::instance(); + QStringList args; + + if (m_allChangesets->checkState() == Qt::Checked) { + args << QLatin1String("--all"); + } + else { + if (m_baseRevision->text().trimmed().length() > 0) { + args << QLatin1String("--base"); + args << m_baseRevision->text().trimmed(); + } + } + + if (m_optForce->checkState() == Qt::Checked) { + args << QLatin1String("--force"); + } + if (m_optInsecure->checkState() == Qt::Checked) { + args << QLatin1String("--insecure"); + } + + args << fileName; + args << m_pathSelect->remote(); + + hgw->executeCommand(QLatin1String("bundle"), args); +} + +void HgBundleDialog::saveGeometry() +{ + FileViewHgPluginSettings *settings = FileViewHgPluginSettings::self(); + settings->setBundleDialogHeight(this->height()); + settings->setBundleDialogWidth(this->width()); + settings->writeConfig(); +} + +void HgBundleDialog::loadCommits() +{ + HgWrapper *hgWrapper = HgWrapper::instance(); + + // update heads list + QProcess process; + process.setWorkingDirectory(hgWrapper->getBaseDir()); + + QStringList args; + args << QLatin1String("log"); + args << QLatin1String("--template"); + args << QLatin1String("{rev}\n{node|short}\n{branch}\n" + "{author}\n{desc|firstline}\n"); + + process.start(QLatin1String("hg"), args); + process.waitForFinished(); + m_commitInfo->clear(); + + const int FINAL = 5; + char buffer[FINAL][1024]; + int count = 0; + while (process.readLine(buffer[count], sizeof(buffer[count])) > 0) { + if (count == FINAL - 1) { + QString rev = QTextCodec::codecForLocale()->toUnicode(buffer[0]).trimmed(); + QString changeset = QTextCodec::codecForLocale()->toUnicode(buffer[1]).trimmed(); + QString branch = QTextCodec::codecForLocale()->toUnicode(buffer[2]).trimmed(); + QString author = QTextCodec::codecForLocale()->toUnicode(buffer[3]).trimmed(); + QString log = QTextCodec::codecForLocale()->toUnicode(buffer[4]).trimmed(); + + QListWidgetItem *item = new QListWidgetItem; + item->setData(Qt::DisplayRole, changeset); + item->setData(Qt::UserRole + 1, rev); + item->setData(Qt::UserRole + 2, branch); + item->setData(Qt::UserRole + 3, author); + item->setData(Qt::UserRole + 4, log); + m_commitInfo->addItem(item); + } + count = (count + 1)%FINAL; + } +} + +void HgBundleDialog::slotSelectChangeset() +{ + KDialog diag; + diag.setCaption(i18nc("@title:window", + "Select Changeset")); + diag.setButtons(KDialog::Ok | KDialog::Cancel); + diag.setDefaultButton(KDialog::Ok); + diag.setButtonText(KDialog::Ok, i18nc("@action:button", "Select")); + + diag.setMinimumWidth(700); + + m_commitInfo = new HgCommitInfoWidget; + loadCommits(); + diag.setMainWidget(m_commitInfo); + + if (diag.exec() == KDialog::Accepted) { + m_baseRevision->setText(m_commitInfo->selectedChangeset()); + } +} + +void HgBundleDialog::slotAllChangesCheckToggled(int state) +{ + if (state == Qt::Checked) { + m_selectCommitButton->setEnabled(false); + m_baseRevision->setEnabled(false); + } + else { + m_selectCommitButton->setEnabled(true); + m_baseRevision->setEnabled(true); + } +} + +#include "moc_bundledialog.cpp" + diff --git a/dolphin/plugins/hg/bundledialog.h b/dolphin/plugins/hg/bundledialog.h new file mode 100644 index 00000000..a12a0f0e --- /dev/null +++ b/dolphin/plugins/hg/bundledialog.h @@ -0,0 +1,92 @@ +/*************************************************************************** + * Copyright (C) 2011 by Vishesh Yadav * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef HGBUNDLEDIALOG_H +#define HGBUNDLEDIALOG_H + +#include + +#include +#include +class KLineEdit; +class HgCommitInfoWidget; +class HgPathSelector; +class KPushButton; + +/** + * Dialog which implements bundle feature of Mercurial. Bundle enables + * user to creates a file containing all the selected/desired changesets + * in mercurial's internal format rather than patches. + * + * Changesets can either be selected by user or after being compared by + * remote repository selected. + * + */ +class HgBundleDialog : public KDialog +{ + Q_OBJECT + +public: + HgBundleDialog(QWidget *parent=0); + +public slots: + void done(int r); + +private slots: + void saveGeometry(); + + /** + * Opens a dialog listing all changeset from which user will select a + * changeset for base revision. + */ + void slotSelectChangeset(); + void slotAllChangesCheckToggled(int state); + +private: + void setupUI(); + + /** + * Creates bundle file. + * + * @param fileName Path to file where bundle will be created. + */ + void createBundle(const QString &fileName); + + /** + * Find all changesets in respository and show them in Commit Selector in + * Base Changeset selector. + */ + void loadCommits(); + +private: + QGroupBox *m_mainGroup; + HgPathSelector *m_pathSelect; + HgCommitInfoWidget *m_commitInfo; + KPushButton *m_selectCommitButton; + KLineEdit *m_baseRevision; + QCheckBox *m_allChangesets; + + //options + QGroupBox *m_optionGroup; + QCheckBox *m_optForce; + QCheckBox *m_optInsecure; +}; + +#endif /* HGBUNDLEDIALOG_H */ + diff --git a/dolphin/plugins/hg/clonedialog.cpp b/dolphin/plugins/hg/clonedialog.cpp new file mode 100644 index 00000000..392ed595 --- /dev/null +++ b/dolphin/plugins/hg/clonedialog.cpp @@ -0,0 +1,248 @@ +/*************************************************************************** + * Copyright (C) 2011 by Vishesh Yadav * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "clonedialog.h" +#include "fileviewhgpluginsettings.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +HgCloneDialog::HgCloneDialog(const QString &directory, QWidget *parent): + KDialog(parent, Qt::Dialog), + m_cloned(false), + m_terminated(true), + m_workingDirectory(directory) +{ + // dialog properties + this->setCaption(i18nc("@title:window", + "Hg Clone")); + this->setButtons(KDialog::Ok | KDialog::Cancel); + this->setDefaultButton(KDialog::Ok); + this->setButtonText(KDialog::Ok, i18nc("@action:button", "Clone")); + this->enableButtonOk(false); + + + ////////////// + // Setup UI // + ////////////// + + QGroupBox *urlGroup = new QGroupBox(i18n("URLs")); + QGridLayout *urlLayout = new QGridLayout; + QLabel *sourceLabel = new QLabel(i18nc("@label", "Source")); + QLabel *destLabel = new QLabel(i18nc("@lobel", "Destination")); + KPushButton *m_browse_dest = new KPushButton(i18nc("@button", "Browse")); + KPushButton *m_browse_source = new KPushButton(i18nc("@button", "Browse")); + m_source = new KLineEdit; + m_destination = new KLineEdit; + urlLayout->addWidget(sourceLabel, 0, 0); + urlLayout->addWidget(m_source, 0, 1); + urlLayout->addWidget(m_browse_source, 0, 2); + urlLayout->addWidget(destLabel, 1, 0); + urlLayout->addWidget(m_destination, 1, 1); + urlLayout->addWidget(m_browse_dest, 1, 2); + urlGroup->setLayout(urlLayout); + + // Options Group + QGroupBox *optionGroup = new QGroupBox(i18nc("@label", "Options")); + QVBoxLayout *optionLayout = new QVBoxLayout; + + m_optNoUpdate = new QCheckBox(i18n("Do not update the new working directory.")); + m_optUsePull = new QCheckBox(i18n("Use pull protocol to copy metadata.")); + m_optUseUncmprdTrans = new QCheckBox(i18n("Use uncompressed transfer.")); + m_optNoVerifyServCert = new QCheckBox(i18n("Do not verify server certificate (ignoring web.cacerts config).")); + + optionLayout->addWidget(m_optNoUpdate); + optionLayout->addWidget(m_optUsePull); + optionLayout->addWidget(m_optUseUncmprdTrans); + optionLayout->addWidget(m_optNoVerifyServCert); + optionGroup->setLayout(optionLayout); + // end options + + QFrame *frame = new QFrame; + QVBoxLayout *mainLayout = new QVBoxLayout; + mainLayout->addWidget(urlGroup); + mainLayout->addWidget(optionGroup); + mainLayout->addStretch(); + frame->setLayout(mainLayout); + + m_stackLayout = new QStackedLayout; + m_outputEdit = new KTextEdit; + m_outputEdit->setReadOnly(true); + m_outputEdit->setFontFamily(QLatin1String(KDE_DEFAULT_FIXED_FONT)); + m_stackLayout->addWidget(frame); + m_stackLayout->addWidget(m_outputEdit); + + QFrame *mainFrame = new QFrame; + mainFrame->setLayout(m_stackLayout); + m_stackLayout->setCurrentIndex(0); + + setMainWidget(mainFrame); + // Load saved settings + FileViewHgPluginSettings *settings = FileViewHgPluginSettings::self(); + this->setInitialSize(QSize(settings->cloneDialogWidth(), + settings->cloneDialogHeight())); + + connect(this, SIGNAL(finished()), this, SLOT(saveGeometry())); + connect(m_source, SIGNAL(textChanged(const QString&)), + this, SLOT(slotUpdateOkButton())); + connect(m_browse_dest, SIGNAL(clicked()), + this, SLOT(slotBrowseDestClicked())); + connect(m_browse_source, SIGNAL(clicked()), + this, SLOT(slotBrowseSourceClicked())); + connect(&m_process, SIGNAL(started()), this, SLOT(slotCloningStarted())); + connect(&m_process, SIGNAL(finished(int, QProcess::ExitStatus)), + this, SLOT(slotCloningFinished(int, QProcess::ExitStatus))); + connect(&m_process, SIGNAL(readyReadStandardOutput()), + this, SLOT(slotUpdateCloneOutput())); +} + +void HgCloneDialog::browseDirectory(KLineEdit *dest) +{ + QString result = KFileDialog::getExistingDirectory(); + if (result.length() > 0) { + dest->setText(result); + } +} + +void HgCloneDialog::slotBrowseDestClicked() +{ + browseDirectory(m_destination); +} + +void HgCloneDialog::slotBrowseSourceClicked() +{ + browseDirectory(m_source); +} + +void HgCloneDialog::done(int r) +{ + if (r == KDialog::Accepted && !m_cloned) { + // Will execute 'stdbuf' command to make the output of + // mercurial command line buffered and enable us to show + // output of cloning as soon as new line is available + QStringList args; + args << QLatin1String("-oL"); //argument for stdbuf. + args << QLatin1String("hg"); + args << QLatin1String("clone"); + args << QLatin1String("--verbose"); + appendOptionArguments(args); + args << m_source->text(); + + if (!m_destination->text().isEmpty()) { + args << m_destination->text(); + } + + m_outputEdit->clear(); + m_stackLayout->setCurrentIndex(1); + QApplication::processEvents(); + enableButtonOk(false); + + m_process.setWorkingDirectory(m_workingDirectory); + m_process.start(QLatin1String("stdbuf"), args); + } + else if (r == KDialog::Accepted && m_cloned) { + KDialog::done(r); + } + else { + if (m_process.state() == QProcess::Running) { + KMessageBox::error(this, i18n("Terminating cloning!")); + enableButtonOk(true); + m_terminated = true; + m_process.terminate(); + m_stackLayout->setCurrentIndex(0); + } + else { + KDialog::done(r); + } + } +} + +void HgCloneDialog::slotCloningStarted() +{ + m_terminated = false; +} + +void HgCloneDialog::slotUpdateCloneOutput() +{ + m_outputEdit->insertPlainText(QTextCodec::codecForLocale()->toUnicode(m_process.readAllStandardOutput())); +} + +void HgCloneDialog::slotCloningFinished(int exitCode, QProcess::ExitStatus exitStatus) +{ + if (exitCode == 0 && exitStatus == QProcess::NormalExit) { + m_cloned = true; + this->setButtonText(KDialog::Ok, i18nc("@action:button", "Close")); + this->enableButtonOk(true); + } + else if (!m_terminated) { + KMessageBox::error(this, i18nc("@message:error", + "Error Cloning Repository!")); + } +} + +void HgCloneDialog::appendOptionArguments(QStringList &args) +{ + if (m_optNoUpdate->checkState() == Qt::Checked) { + args << QLatin1String("-U"); + } + if (m_optUsePull->checkState() == Qt::Checked) { + args << QLatin1String("--pull"); + } + if (m_optUseUncmprdTrans->checkState() == Qt::Checked) { + args << QLatin1String("--uncompressed"); + } + if (m_optNoVerifyServCert->checkState() == Qt::Checked) { + args << QLatin1String("--insecure"); + } +} + +void HgCloneDialog::saveGeometry() +{ + FileViewHgPluginSettings *settings = FileViewHgPluginSettings::self(); + settings->setCloneDialogHeight(this->height()); + settings->setCloneDialogWidth(this->width()); + settings->writeConfig(); +} + +void HgCloneDialog::slotUpdateOkButton() +{ + if (m_source->text().length() > 0) { + enableButtonOk(true); + } + else { + enableButtonOk(false); + } +} + +#include "moc_clonedialog.cpp" + diff --git a/dolphin/plugins/hg/clonedialog.h b/dolphin/plugins/hg/clonedialog.h new file mode 100644 index 00000000..d9dad273 --- /dev/null +++ b/dolphin/plugins/hg/clonedialog.h @@ -0,0 +1,92 @@ +/*************************************************************************** + * Copyright (C) 2011 by Vishesh Yadav * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef HGCLONEDILAOG_H +#define HGCLONEDILAOG_H + +#include +#include +#include + +class KLineEdit; +class KPushButton; +class KTextEdit; +#include +#include + +//TODO: Enable to enter username/passwords if not found in config as well +// as override within dialog + +/** + * Implements dialog to clone repository. + */ +class HgCloneDialog : public KDialog +{ + Q_OBJECT + +public: + HgCloneDialog(const QString &directory, QWidget *parent = 0); + void setWorkingDirectory(const QString &directory); + +private slots: + void saveGeometry(); + + /** + * Enables dialog's Ok button when user has entered some input in + * source LineEdit + */ + void slotUpdateOkButton(); + void slotBrowseDestClicked(); + void slotBrowseSourceClicked(); + void slotCloningStarted(); + void slotCloningFinished(int exitCode, QProcess::ExitStatus); + + /** + * Show output of clone operation in TextEdit component. + */ + void slotUpdateCloneOutput(); + +private: + void done(int r); + void browseDirectory(KLineEdit *dest); + void appendOptionArguments(QStringList &args); + +private: + KLineEdit *m_source; + KLineEdit *m_destination; + KPushButton *m_browse_dest; + KPushButton *m_browse_source; + KTextEdit *m_outputEdit; + QStackedLayout *m_stackLayout; + + bool m_cloned; + bool m_terminated; + QString m_workingDirectory; + QProcess m_process; + + // option checkboxes + QCheckBox *m_optNoUpdate; + QCheckBox *m_optUsePull; + QCheckBox *m_optUseUncmprdTrans; + QCheckBox *m_optNoVerifyServCert; + +}; + +#endif // HGCLONEDILAOG_H + diff --git a/dolphin/plugins/hg/commitdialog.cpp b/dolphin/plugins/hg/commitdialog.cpp new file mode 100644 index 00000000..cb9b02f7 --- /dev/null +++ b/dolphin/plugins/hg/commitdialog.cpp @@ -0,0 +1,370 @@ +/*************************************************************************** + * Copyright (C) 2011 by Vishesh Yadav * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "commitdialog.h" +#include "hgwrapper.h" +#include "fileviewhgpluginsettings.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +HgCommitDialog::HgCommitDialog(QWidget *parent): + KDialog(parent, Qt::Dialog) +{ + // dialog properties + this->setCaption(i18nc("@title:window", + "Hg Commit")); + this->setButtons(KDialog::Ok | KDialog::Cancel); + this->setDefaultButton(KDialog::Ok); + this->setButtonText(KDialog::Ok, i18nc("@action:button", "Commit")); + this->enableButtonOk(false); // since commit message is empty when loaded + + // To show diff between commit + KTextEditor::Editor *editor = KTextEditor::EditorChooser::editor(); + if (!editor) { + KMessageBox::error(this, + i18n("A KDE text-editor component could not be found;" + "\nplease check your KDE installation.")); + return; + } + m_fileDiffDoc = editor->createDocument(0); + m_fileDiffView = qobject_cast(m_fileDiffDoc->createView(this)); + m_fileDiffDoc->setReadWrite(false); + + // Setup actions + m_useCurrentBranch= new KAction(this); + m_useCurrentBranch->setCheckable(true); + m_useCurrentBranch->setText(i18nc("@action:inmenu", + "Commit to current branch")); + + m_newBranch = new KAction(this); + m_newBranch->setCheckable(true); + m_newBranch->setText(i18nc("@action:inmenu", + "Create new branch")); + + m_closeBranch = new KAction(this); + m_closeBranch->setCheckable(true); + m_closeBranch->setText(i18nc("@action:inmenu", + "Close current branch")); + + m_branchMenu = new KMenu(this); + m_branchMenu->addAction(m_useCurrentBranch); + m_branchMenu->addAction(m_newBranch); + m_branchMenu->addAction(m_closeBranch); + + QActionGroup *branchActionGroup = new QActionGroup(this); + branchActionGroup->addAction(m_useCurrentBranch); + branchActionGroup->addAction(m_newBranch); + branchActionGroup->addAction(m_closeBranch); + m_useCurrentBranch->setChecked(true); + connect(branchActionGroup, SIGNAL(triggered(QAction *)), + this, SLOT(slotBranchActions(QAction *))); + + + ////////////// + // Setup UI // + ////////////// + + // Top bar of buttons + QHBoxLayout *topBarLayout = new QHBoxLayout; + m_copyMessageButton = new KPushButton(i18n("Copy Message")); + m_branchButton = new KPushButton(i18n("Branch")); + + m_copyMessageMenu = new KMenu(this); + createCopyMessageMenu(); + + topBarLayout->addWidget(new QLabel(getParentForLabel())); + topBarLayout->addStretch(); + topBarLayout->addWidget(m_branchButton); + topBarLayout->addWidget(m_copyMessageButton); + m_branchButton->setMenu(m_branchMenu); + m_copyMessageButton->setMenu(m_copyMessageMenu); + + // the commit box itself + QGroupBox *messageGroupBox = new QGroupBox; + QVBoxLayout *commitLayout = new QVBoxLayout; + m_commitMessage = new QPlainTextEdit; + commitLayout->addWidget(m_commitMessage); + messageGroupBox->setTitle(i18nc("@title:group", "Commit Message")); + messageGroupBox->setLayout(commitLayout); + + // Show diff here + QGroupBox *diffGroupBox = new QGroupBox; + QVBoxLayout *diffLayout = new QVBoxLayout(diffGroupBox); + diffLayout->addWidget(m_fileDiffView); + diffGroupBox->setTitle(i18nc("@title:group", "Diff/Content")); + diffGroupBox->setLayout(diffLayout); + + // Set up layout for Status, Commit and Diff boxes + QGridLayout *bodyLayout = new QGridLayout; + m_statusList = new HgStatusList; + bodyLayout->addWidget(m_statusList, 0, 0, 0, 1); + bodyLayout->addWidget(messageGroupBox, 0, 1); + bodyLayout->addWidget(diffGroupBox, 1, 1); + bodyLayout->setColumnStretch(0, 1); + bodyLayout->setColumnStretch(1, 2); + bodyLayout->setRowStretch(0, 1); + bodyLayout->setRowStretch(1, 1); + + // Set up layout and container for main dialog + QFrame *frame = new QFrame; + QVBoxLayout *mainLayout = new QVBoxLayout; + mainLayout->addLayout(topBarLayout); + mainLayout->addLayout(bodyLayout); + frame->setLayout(mainLayout); + setMainWidget(frame); + + slotBranchActions(m_useCurrentBranch); + slotInitDiffOutput(); // initialise with whole repo diff + + // Load saved settings + FileViewHgPluginSettings *settings = FileViewHgPluginSettings::self(); + this->setInitialSize(QSize(settings->commitDialogWidth(), + settings->commitDialogHeight())); + // + connect(m_statusList, SIGNAL(itemSelectionChanged(const char, const QString &)), + this, SLOT(slotItemSelectionChanged(const char, const QString &))); + connect(m_commitMessage, SIGNAL(textChanged()), + this, SLOT(slotMessageChanged())); + connect(this, SIGNAL(finished()), this, SLOT(saveGeometry())); +} + +QString HgCommitDialog::getParentForLabel() +{ + HgWrapper *hgWrapper = HgWrapper::instance(); + QString line("parents: "); + line += hgWrapper->getParentsOfHead(); + return line; +} + +void HgCommitDialog::slotInitDiffOutput() +{ + m_fileDiffDoc->setReadWrite(true); + m_fileDiffDoc->setModified(false); + m_fileDiffDoc->closeUrl(true); + + QString diffOut; + HgWrapper *hgWrapper = HgWrapper::instance(); + hgWrapper->executeCommand(QLatin1String("diff"), QStringList(), diffOut); + m_fileDiffDoc->setHighlightingMode("diff"); + m_fileDiffDoc->setText(diffOut); + m_fileDiffView->setCursorPosition( KTextEditor::Cursor(0, 0) ); + m_fileDiffDoc->setReadWrite(false); +} + +void HgCommitDialog::slotItemSelectionChanged(const char status, + const QString &fileName) +{ + m_fileDiffDoc->setReadWrite(true); + m_fileDiffDoc->setModified(false); + m_fileDiffDoc->closeUrl(true); + + if (status != '?') { + QStringList arguments; + QString diffOut; + HgWrapper *hgWrapper = HgWrapper::instance(); + + arguments << fileName; + hgWrapper->executeCommand(QLatin1String("diff"), arguments, diffOut); + m_fileDiffDoc->setText(diffOut); + m_fileDiffDoc->setHighlightingMode("diff"); + } + else { + KUrl url(HgWrapper::instance()->getBaseDir()); + url.addPath(fileName); + m_fileDiffDoc->openUrl(url); + } + + m_fileDiffDoc->setReadWrite(false); + m_fileDiffView->setCursorPosition( KTextEditor::Cursor(0, 0) ); +} + +void HgCommitDialog::slotMessageChanged() +{ + enableButtonOk(!m_commitMessage->toPlainText().isEmpty()); +} + +void HgCommitDialog::done(int r) +{ + if (r == KDialog::Accepted) { + QStringList files; + if (m_statusList->getSelectionForCommit(files)) { + HgWrapper *hgWrapper = HgWrapper::instance(); + if (m_branchAction == NewBranch) { + if (!hgWrapper->createBranch(m_newBranchName)) { + KMessageBox::error(this, + i18n("Could not create branch! Aborting commit!")); + return; + } + } + bool success = hgWrapper->commit(m_commitMessage->toPlainText(), + files, m_branchAction==CloseBranch); + if (success) { + KDialog::done(r); + } + else { + KMessageBox::error(this, i18n("Commit unsuccessful!")); + } + } + else { + KMessageBox::error(this, i18n("No files for commit!")); + } + } + else { + KDialog::done(r); + } +} + +void HgCommitDialog::saveGeometry() +{ + FileViewHgPluginSettings *settings = FileViewHgPluginSettings::self(); + settings->setCommitDialogHeight(this->height()); + settings->setCommitDialogWidth(this->width()); + settings->writeConfig(); +} + +void HgCommitDialog::slotBranchActions(QAction *action) +{ + if (action == m_useCurrentBranch) { + m_branchAction = NoChanges; + m_branchButton->setText(i18n("Branch: Current Branch")); + } + else if (action == m_newBranch) { + NewBranchDialog diag; + if (diag.exec() == KDialog::Accepted) { + m_branchAction = NewBranch; + m_newBranchName = diag.getBranchName(); + m_branchButton->setText(i18n("Branch: ") + m_newBranchName); + } + else { // restore previous check state + if (m_branchAction == NoChanges) { + m_useCurrentBranch->setChecked(true); + } + else if (m_branchAction == CloseBranch) { + m_closeBranch->setChecked(true); + } + } + } + else if (action == m_closeBranch) { + m_branchAction = CloseBranch; + m_branchButton->setText(i18n("Branch: Close Current")); + } +} + +/*****************/ +/* Branch Dialog */ +/*****************/ + +NewBranchDialog::NewBranchDialog(QWidget *parent): + KDialog(parent, Qt::Dialog) +{ + // dialog properties + this->setCaption(i18nc("@title:window", + "Hg Commit: New Branch")); + this->setButtons(KDialog::Ok | KDialog::Cancel); + this->setDefaultButton(KDialog::Ok); + this->enableButtonOk(false); // since commit message is empty when loaded + + m_branchList = HgWrapper::instance()->getBranches(); + + QLabel *message = new QLabel(i18nc("@label", "Enter new branch name")); + m_branchNameInput = new KLineEdit; + m_errorLabel = new QLabel; + + QVBoxLayout *layout = new QVBoxLayout; + layout->addWidget(message); + layout->addWidget(m_branchNameInput); + layout->addWidget(m_errorLabel); + + QFrame *frame = new QFrame; + frame->setLayout(layout); + setMainWidget(frame); + + connect(m_branchNameInput, SIGNAL(textChanged(const QString&)), + this, SLOT(slotTextChanged(const QString&))); +} + +void NewBranchDialog::slotTextChanged(const QString &text) +{ + if (m_branchList.contains(text)) { + m_errorLabel->setText(i18nc("@label", "Branch already exists!")); + enableButtonOk(false); + } + else if (text.length() > 0) { + m_errorLabel->clear(); + enableButtonOk(true); + } + else { + m_errorLabel->setText(i18nc("@label", "Enter some text!")); + enableButtonOk(false); + } +} + +QString NewBranchDialog::getBranchName() const +{ + return m_branchNameInput->text(); +} + +void HgCommitDialog::createCopyMessageMenu() +{ + QActionGroup *actionGroup = new QActionGroup(this); + connect(actionGroup, SIGNAL(triggered(QAction *)), + this, SLOT(slotInsertCopyMessage(QAction *))); + + QStringList args; + args << QLatin1String("--limit"); + args << QLatin1String("5"); + args << QLatin1String("--template"); + args << QLatin1String("{desc|short}\n"); + + HgWrapper *hgw = HgWrapper::instance(); + QString output; + hgw->executeCommand(QLatin1String("log"), args, output); + + QStringList messages = output.split('\n', QString::SkipEmptyParts); + foreach (QString msg, messages) { + QAction *action = m_copyMessageMenu->addAction(msg); + actionGroup->addAction(action); + } +} + +void HgCommitDialog::slotInsertCopyMessage(QAction *action) +{ + m_commitMessage->insertPlainText(action->text()); +} + +#include "moc_commitdialog.cpp" + diff --git a/dolphin/plugins/hg/commitdialog.h b/dolphin/plugins/hg/commitdialog.h new file mode 100644 index 00000000..92b8ba4c --- /dev/null +++ b/dolphin/plugins/hg/commitdialog.h @@ -0,0 +1,132 @@ +/*************************************************************************** + * Copyright (C) 2011 by Vishesh Yadav * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef HGCOMMITDIALOG_H +#define HGCOMMITDIALOG_H + +#include "statuslist.h" + +#include +#include + + +#include +class KAction; +class KMenu; +#include + +namespace KTextEditor { + class View; + class Document; +}; + +// TODO: Filter in HgStatusList. + +/** + * Implements dialog for Commit changes. User is presented with a list of + * changes/added/removed files, their diffs, a TextEdit to enter + * commit message, options to change/create/close branch and last 5 commit + * messages. + */ +class HgCommitDialog : public KDialog +{ + Q_OBJECT + +public: + HgCommitDialog(QWidget *parent = 0); + +private slots: + /** + * Shows diff of selected file in a KTextEditor widget when user selects + * one of the entry in HgStatusList widget. + */ + void slotItemSelectionChanged(const char status, const QString &fileName); + + /** + * Will enable 'Ok' button of dialog if some message text is available or + * disables it. + */ + void slotMessageChanged(); + void saveGeometry(); + void slotBranchActions(QAction *action); + + /** + * Shows diff of whole working directory together in KTextEditor widget. + * Equivalent to plain 'hg diff' + */ + void slotInitDiffOutput(); + void slotInsertCopyMessage(QAction *action); + +private: + QString getParentForLabel(); + void createCopyMessageMenu(); + void done(int r); + +private: + QString m_hgBaseDir; + + QPlainTextEdit *m_commitMessage; + HgStatusList *m_statusList; + + KTextEditor::View *m_fileDiffView; + KTextEditor::Document *m_fileDiffDoc; + + KPushButton *m_branchButton; + KPushButton *m_copyMessageButton; + + KAction *m_closeBranch; + KAction *m_newBranch; + KAction *m_useCurrentBranch; + KMenu *m_branchMenu; + KMenu *m_copyMessageMenu; + + /** What will commit do with branch. + * + * CloseBranch: Close the current branch + * NewBranch : Creates new branch for this commit + * NoChanges : No changes to branch are made. + */ + enum {CloseBranch, NewBranch, NoChanges} m_branchAction; + QString m_newBranchName; +}; + +/** + * Dialog which asks for the new branch name in commit dialog + */ +class NewBranchDialog : public KDialog +{ + Q_OBJECT + + public: + NewBranchDialog(QWidget *parent = 0); + QString getBranchName() const; + + private slots: + void slotTextChanged(const QString &text); + + private: + QLabel *m_errorLabel; + KLineEdit *m_branchNameInput; + QStringList m_branchList; + +}; + +#endif // HGCOMMITDIALOG_H + + diff --git a/dolphin/plugins/hg/commitinfowidget.cpp b/dolphin/plugins/hg/commitinfowidget.cpp new file mode 100644 index 00000000..ca8ac03e --- /dev/null +++ b/dolphin/plugins/hg/commitinfowidget.cpp @@ -0,0 +1,134 @@ +/*************************************************************************** + * Copyright (C) 2011 by Vishesh Yadav * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "commitinfowidget.h" +#include "commititemdelegate.h" +#include "hgwrapper.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +HgCommitInfoWidget::HgCommitInfoWidget(QWidget *parent) : + QWidget(parent) +{ + setupUI(); + + connect(m_commitListWidget, SIGNAL(itemSelectionChanged()), + this, SLOT(slotUpdateInfo())); +} + +void HgCommitInfoWidget::setupUI() +{ + m_commitListWidget = new QListWidget; + m_commitListWidget->setItemDelegate(new CommitItemDelegate); + + KTextEditor::Editor *editor = KTextEditor::EditorChooser::editor(); + if (!editor) { + KMessageBox::error(this, + i18n("A KDE text-editor component could not be found;" + "\nplease check your KDE installation.")); + return; + } + m_editorDoc = editor->createDocument(0); + m_editorView = qobject_cast(m_editorDoc->createView(this)); + m_editorDoc->setReadWrite(false); + + QHBoxLayout *layout = new QHBoxLayout; + layout->addWidget(m_commitListWidget, 1); + layout->addWidget(m_editorView, 2); + + setLayout(layout); +} + +void HgCommitInfoWidget::addItem(const QString &revision, + const QString &changeset, + const QString &branch, + const QString &author, + const QString &log) +{ + + QListWidgetItem *item = new QListWidgetItem; + item->setData(Qt::DisplayRole, changeset); + item->setData(Qt::UserRole + 1, revision); + item->setData(Qt::UserRole + 2, branch); + item->setData(Qt::UserRole + 3, author); + item->setData(Qt::UserRole + 4, log); + m_commitListWidget->addItem(item); +} + +void HgCommitInfoWidget::addItem(QListWidgetItem *item) +{ + m_commitListWidget->addItem(item); +} + +QListWidgetItem* HgCommitInfoWidget::currentItem() const +{ + return m_commitListWidget->currentItem(); +} + +const QString HgCommitInfoWidget::selectedChangeset() const +{ + return m_commitListWidget->currentItem()->data(Qt::DisplayRole).toString(); +} + +void HgCommitInfoWidget::clear() const +{ + m_commitListWidget->clear(); +} + +void HgCommitInfoWidget::slotUpdateInfo() +{ + HgWrapper *hgw = HgWrapper::instance(); + QString changeset = selectedChangeset(); + QString output; + QStringList args; + + args << QLatin1String("-p"); + args << QLatin1String("-v"); + args << QLatin1String("-r"); + args << changeset; + hgw->executeCommand(QLatin1String("log"), args, output); + + m_editorDoc->setReadWrite(true); + m_editorDoc->setModified(false); + m_editorDoc->closeUrl(true); + m_editorDoc->setText(output); + m_editorDoc->setHighlightingMode("diff"); + m_editorView->setCursorPosition( KTextEditor::Cursor(0, 0) ); + m_editorDoc->setReadWrite(false); +} + +void HgCommitInfoWidget::setSelectionMode(QAbstractItemView::SelectionMode mode) +{ + m_commitListWidget->setSelectionMode(mode); +} + +QList HgCommitInfoWidget::selectedItems() const +{ + return m_commitListWidget->selectedItems(); +} + diff --git a/dolphin/plugins/hg/commitinfowidget.h b/dolphin/plugins/hg/commitinfowidget.h new file mode 100644 index 00000000..e37c3ee2 --- /dev/null +++ b/dolphin/plugins/hg/commitinfowidget.h @@ -0,0 +1,122 @@ +/*************************************************************************** + * Copyright (C) 2011 by Vishesh Yadav * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef HGCOMMITINFOWIDGET_H +#define HGCOMMITINFOWIDGET_H + +#include +#include +#include + +namespace KTextEditor { + class View; + class Document; +}; + +#include +#include + +/** + * Shows Changesets using a custom deltegate CommitItemDelegate. Shows + * changeset information in a KTextEditor widget when a changeset entry is + * highlighted. + * */ +class HgCommitInfoWidget : public QWidget +{ + Q_OBJECT +public: + HgCommitInfoWidget(QWidget *parent=0); + + /** + * Adds a new entry in the ListWidget with the chageset parameters + * provided in its parameter. + * + * @param revision Revision number of changeset + * @param changeset Changeset identification hash (shortened) + * @param author Author of the CommitItemDelegate + * @param log Commit Message of that changeset + * + */ + void addItem(const QString &revision, + const QString &changeset, + const QString &branch, + const QString &author, + const QString &log); + + /** + * Adds a new QListWidgetItem into list. Expects the changeset information + * in stored in different roles of data. + * + * DisplayRole => Changeset Identification Hash + * UserRole + 1 => Revision Number + * UserRole + 2 => Branch + * UserRole + 3 => Author + * UserRole + 4 => Log/Commit Message + * + * @param item Pointer to the QListWidgetItem object to be added + */ + void addItem(QListWidgetItem *item); + + /** + * Returns changeset identification hash of selected changeset in ListWidget. + * + * @return String containing the changeset identification hash of selected + * chageset + * + */ + const QString selectedChangeset() const; + + /** + * @return Returns a list of selected changesets + */ + QList selectedItems() const; + + /** + * @return Returns pointer to QListWidgetItem of selected changeset. + */ + QListWidgetItem* currentItem() const; + + /** + * Calls QListWidget::setSelectionMode(QAbstractItemView::SelectionMode) + * on the m_commitListWidget + */ + void setSelectionMode(QAbstractItemView::SelectionMode mode); + + /** + * Clears all entries in m_commitListWidget + */ + void clear() const; + +private slots: + /** + * Show selected changeset information when an entry is selected + */ + void slotUpdateInfo(); + +private: + void setupUI(); + +private: + KTextEditor::View *m_editorView; + KTextEditor::Document *m_editorDoc; + QListWidget *m_commitListWidget; +}; + +#endif /* HGCOMMITINFOWIDGET_H */ + diff --git a/dolphin/plugins/hg/commititemdelegate.cpp b/dolphin/plugins/hg/commititemdelegate.cpp new file mode 100644 index 00000000..db05f29a --- /dev/null +++ b/dolphin/plugins/hg/commititemdelegate.cpp @@ -0,0 +1,90 @@ +/*************************************************************************** + * Copyright (C) 2011 by Vishesh Yadav * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "commititemdelegate.h" + +#include +#include + +CommitItemDelegate::CommitItemDelegate(QObject *parent) : + QItemDelegate(parent) +{ +} + +void CommitItemDelegate::paint(QPainter *painter, + const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + QString changeset = index.data(Qt::DisplayRole).toString(); + QString revision = index.data(Qt::UserRole + 1).toString(); + QString branch = index.data(Qt::UserRole + 2).toString(); + QString authorName = index.data(Qt::UserRole + 3).toString(); + QString commitLog = index.data(Qt::UserRole + 4).toString(); + + if (option.state & QStyle::State_Selected) { + painter->fillRect(option.rect, option.palette.color(QPalette::Highlight)); + } + + QFont font = option.font; + QFontMetrics fm(font); + QRect rect = option.rect.adjusted(4, 4, 4, 4); + + QString top; + if (!revision.isEmpty()) { + top = QString("%1:").arg(revision); + } + top += changeset; + + if (!branch.isEmpty()) { + top += QString(" (%1)").arg(branch); + } + font.setBold(true); + painter->setFont(font); + painter->drawText(rect, Qt::AlignLeft, top); + + font.setPixelSize(0.60f * static_cast(fm.height())); + font.setBold(false); + painter->setFont(font); + rect = rect.adjusted(0, fm.height(), 0, fm.height()); + painter->drawText(rect, Qt::AlignLeft, authorName, &rect); + + int fs = 0.60f * static_cast(fm.height()); + font.setPixelSize(fs); + font.setBold(false); + painter->setFont(font); + rect = rect.adjusted(0, fs+4, 0, fs+4); + painter->drawText(rect, Qt::AlignLeft, commitLog, &rect); +} + + +QSize CommitItemDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + QSize size = QItemDelegate::sizeHint(option, index); + QFont font = option.font; + font.setBold(true); + QFontMetrics fm(font); + int height = static_cast(option.fontMetrics.height()) * (1.2f) + fm.height() + 15; + size.setHeight(height); + + return size; +} + +#include "moc_commititemdelegate.cpp" + + diff --git a/dolphin/plugins/hg/commititemdelegate.h b/dolphin/plugins/hg/commititemdelegate.h new file mode 100644 index 00000000..c276cf8e --- /dev/null +++ b/dolphin/plugins/hg/commititemdelegate.h @@ -0,0 +1,46 @@ +/*************************************************************************** + * Copyright (C) 2011 by Vishesh Yadav * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef COMMITITEMDELEGATE_H +#define COMMITITEMDELEGATE_H + +#include + +/** + * Custom Delgate to show Commit info in three lines + * - Revision:Changeset (branch) + * - Author + * - Commit Log | First Line + */ +class CommitItemDelegate : public QItemDelegate +{ + Q_OBJECT +public: + CommitItemDelegate(QObject *parent = 0); + void paint(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index) const; + QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const; + +signals: + +public slots: + +}; + +#endif // COMMITITEMDELEGATE_H diff --git a/dolphin/plugins/hg/config-widgets/generalconfig.cpp b/dolphin/plugins/hg/config-widgets/generalconfig.cpp new file mode 100644 index 00000000..72678e65 --- /dev/null +++ b/dolphin/plugins/hg/config-widgets/generalconfig.cpp @@ -0,0 +1,97 @@ +/*************************************************************************** + * Copyright (C) 2011 by Vishesh Yadav * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "generalconfig.h" + +#include +#include +#include +#include +#include +#include +#include + +HgGeneralConfigWidget::HgGeneralConfigWidget(HgConfig::ConfigType type, QWidget *parent): + QWidget(parent), + m_configType(type) +{ + setupUI(); + loadConfig(); +} + +void HgGeneralConfigWidget::setupUI() +{ + m_userEdit = new KLineEdit; + m_editorEdit = new KLineEdit; + m_mergeEdit = new KLineEdit; + m_verboseCheck = new QCheckBox(i18nc("@label:checkbox", "Verbose Output")); + + QLabel *userLabel = new QLabel(i18nc("@label", "Username")); + QLabel *editorLabel = new QLabel(i18nc("@label", "Default Editor")); + QLabel *mergeLabel = new QLabel(i18nc("@label", "Default Merge Tool")); + + QGridLayout *mainLayout = new QGridLayout; + mainLayout->addWidget(userLabel, 0, 0); + mainLayout->addWidget(m_userEdit, 0, 1); + mainLayout->addWidget(editorLabel, 1, 0); + mainLayout->addWidget(m_editorEdit, 1, 1); + mainLayout->addWidget(mergeLabel, 2, 0); + mainLayout->addWidget(m_mergeEdit, 2, 1); + mainLayout->addWidget(m_verboseCheck, 3, 0, 2, 0); + mainLayout->setRowStretch(mainLayout->rowCount(), 1); + + setLayout(mainLayout); +} + +void HgGeneralConfigWidget::loadConfig() +{ + HgConfig hgc(m_configType); + + m_userEdit->setText(hgc.username()); + m_editorEdit->setText(hgc.editor()); + m_mergeEdit->setText(hgc.merge()); + + QString verbose = hgc.property(QLatin1String("ui"), QLatin1String("verobose")); + if (verbose.isEmpty() || verbose == "False") { + m_verboseCheck->setChecked(false); + } + else if (verbose == "True") { + m_verboseCheck->setChecked(true); + } +} + +void HgGeneralConfigWidget::saveConfig() +{ + HgConfig hgc(m_configType); + hgc.setUsername(m_userEdit->text()); + hgc.setEditor(m_editorEdit->text()); + hgc.setMerge(m_mergeEdit->text()); + + if (m_verboseCheck->checkState() == Qt::Checked) { + hgc.setProperty(QLatin1String("ui"), QLatin1String("verbose"), + QLatin1String("True")); + } + else { + hgc.setProperty(QLatin1String("ui"), QLatin1String("verbose"), + QLatin1String("False")); + } +} + +#include "moc_generalconfig.cpp" + diff --git a/dolphin/plugins/hg/config-widgets/generalconfig.h b/dolphin/plugins/hg/config-widgets/generalconfig.h new file mode 100644 index 00000000..0d682ff1 --- /dev/null +++ b/dolphin/plugins/hg/config-widgets/generalconfig.h @@ -0,0 +1,60 @@ +/*************************************************************************** + * Copyright (C) 2011 by Vishesh Yadav * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef HGGENERAL_CONFIG_WIDGET_H +#define HGGENERAL_CONFIG_WIDGET_H + +#include +#include "hgconfig.h" + +class KLineEdit; +#include + +/** + * General configuration options, usually found [ui] group of hgrc file. + * Can be used with both, repository hgrc as well as global hgrc + */ +class HgGeneralConfigWidget : public QWidget +{ + Q_OBJECT + +public: + /** + * @param type Which configuration file to use, Repo or Global + */ + HgGeneralConfigWidget(HgConfig::ConfigType type, QWidget *parent = 0); + +public slots: + void saveConfig(); + void loadConfig(); + +private: + void setupUI(); + +private: + KLineEdit *m_userEdit; + KLineEdit *m_editorEdit; + KLineEdit *m_mergeEdit; + QCheckBox *m_verboseCheck; + + HgConfig::ConfigType m_configType; +}; + +#endif // HGGENERAL_CONFIG_WIDGET_H + diff --git a/dolphin/plugins/hg/config-widgets/ignorewidget.cpp b/dolphin/plugins/hg/config-widgets/ignorewidget.cpp new file mode 100644 index 00000000..86a28abe --- /dev/null +++ b/dolphin/plugins/hg/config-widgets/ignorewidget.cpp @@ -0,0 +1,185 @@ +/*************************************************************************** + * Copyright (C) 2011 by Vishesh Yadav * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "ignorewidget.h" +#include "../hgwrapper.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +HgIgnoreWidget::HgIgnoreWidget(QWidget *parent) : + QWidget(parent) +{ + setupUI(); + loadConfig(); +} + +void HgIgnoreWidget::setupUI() +{ + QVBoxLayout *sideBar = new QVBoxLayout; + m_addFiles = new KPushButton(i18nc("@label:button", "Add Files")); + m_addPattern = new KPushButton(i18nc("@label:button", "Add Pattern")); + m_editEntry = new KPushButton(i18nc("@label:button", "Edit Entry")); + m_removeEntries = new KPushButton(i18nc("@label:button", "Remove Entries")); + sideBar->addWidget(m_addFiles); + sideBar->addWidget(m_addPattern); + sideBar->addWidget(m_editEntry); + sideBar->addWidget(m_removeEntries); + sideBar->addStretch(); + + m_ignoreTable = new QListWidget; + m_untrackedList = new QListWidget; + setupUntrackedList(); + + m_ignoreTable->setSelectionMode(QListWidget::ExtendedSelection); + m_untrackedList->setSelectionMode(QListWidget::ExtendedSelection); + + QHBoxLayout *mainLayout = new QHBoxLayout; + mainLayout->addWidget(m_untrackedList); + mainLayout->addWidget(m_ignoreTable); + mainLayout->addLayout(sideBar); + setLayout(mainLayout); + + connect(m_addFiles, SIGNAL(clicked()), this, SLOT(slotAddFiles())); + connect(m_removeEntries, SIGNAL(clicked()), this, SLOT(slotRemoveEntries())); + connect(m_addPattern, SIGNAL(clicked()), this, SLOT(slotAddPattern())); + connect(m_editEntry, SIGNAL(clicked()), this, SLOT(slotEditEntry())); +} + +void HgIgnoreWidget::setupUntrackedList() +{ + HgWrapper *hgw = HgWrapper::instance(); + QStringList args; + args << QLatin1String("--unknown"); + QString output; + hgw->executeCommand(QLatin1String("status"), args, output); + + QStringList result = output.split('\n', QString::SkipEmptyParts); + foreach (QString file, result) { + m_untrackedList->addItem(file.mid(2)); + } +} + +void HgIgnoreWidget::loadConfig() +{ + KUrl url(HgWrapper::instance()->getBaseDir()); + url.addPath(QLatin1String(".hgignore")); + + QFile file(url.path()); + if (!file.open(QFile::ReadOnly)) { + return; + } + + QTextStream fileStream(&file); + + do { + QString buffer; + buffer = fileStream.readLine(); + if (!buffer.isEmpty()) { + m_ignoreTable->addItem(buffer); + } + } while (!fileStream.atEnd()); + + file.close(); +} + +void HgIgnoreWidget::saveConfig() +{ + KUrl url(HgWrapper::instance()->getBaseDir()); + url.addPath(QLatin1String(".hgignore")); + + QFile file(url.path()); + if (!file.open(QFile::WriteOnly | QFile::Truncate)) { + return; + } + + QTextStream fileStream(&file); + int count = m_ignoreTable->count(); + for (int i=0; iitem(i); + fileStream << item->text() << QLatin1String("\n"); + } + + file.close(); +} + +void HgIgnoreWidget::slotAddFiles() +{ + QList selectedItems = m_untrackedList->selectedItems(); + foreach (QListWidgetItem *item, selectedItems) { + m_ignoreTable->addItem(item->text()); + m_untrackedList->takeItem(m_untrackedList->row(item)); + } +} + +void HgIgnoreWidget::slotAddPattern() +{ + bool ok; + QString input = QInputDialog::getText(this, + i18nc("@title:dialog", "Add Pattern"), + QString(), + QLineEdit::Normal, + QString(), + &ok); + if (ok && !input.isEmpty()) { + m_ignoreTable->addItem(input); + } +} + +void HgIgnoreWidget::slotRemoveEntries() +{ + QList selectedItems = m_ignoreTable->selectedItems(); + foreach (QListWidgetItem *item, selectedItems) { + m_ignoreTable->takeItem(m_ignoreTable->row(item)); + } +} +void HgIgnoreWidget::slotEditEntry() +{ + if (m_ignoreTable->currentItem() == 0) { + KMessageBox::error(this, i18nc("@message:error", + "No entry selected for edit!")); + return; + } + + bool ok; + QString input = QInputDialog::getText(this, + i18nc("@title:dialog", "Edit Pattern"), + QString(), + QLineEdit::Normal, + m_ignoreTable->currentItem()->text(), + &ok); + if (ok && !input.isEmpty()) { + m_ignoreTable->currentItem()->setText(input); + } +} + + +#include "moc_ignorewidget.cpp" + diff --git a/dolphin/plugins/hg/config-widgets/ignorewidget.h b/dolphin/plugins/hg/config-widgets/ignorewidget.h new file mode 100644 index 00000000..4db88c6d --- /dev/null +++ b/dolphin/plugins/hg/config-widgets/ignorewidget.h @@ -0,0 +1,62 @@ +/*************************************************************************** + * Copyright (C) 2011 by Vishesh Yadav * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef HGCONFIG_IGNOREWIDGET_H +#define HGCONFIG_IGNOREWIDGET_H + +#include + +#include +class KPushButton; +#include + +/** + * Widget to manage ignored files. Used .hgignore file in repository. + * Repository only configuration + */ +class HgIgnoreWidget : public QWidget +{ + Q_OBJECT +public: + HgIgnoreWidget(QWidget *parent = 0); + + void loadConfig(); + void saveConfig(); + +private slots: + void slotAddFiles(); + void slotAddPattern(); + void slotRemoveEntries(); + void slotEditEntry(); + +private: + void setupUI(); + void setupUntrackedList(); + +private: + QListWidget *m_ignoreTable; + QListWidget *m_untrackedList; + KPushButton *m_addFiles; + KPushButton *m_addPattern; + KPushButton *m_removeEntries; + KPushButton *m_editEntry; +}; + +#endif /* HGCONFIG_IGNOREWIDGET_H */ + diff --git a/dolphin/plugins/hg/config-widgets/pathconfig.cpp b/dolphin/plugins/hg/config-widgets/pathconfig.cpp new file mode 100644 index 00000000..9941d20f --- /dev/null +++ b/dolphin/plugins/hg/config-widgets/pathconfig.cpp @@ -0,0 +1,260 @@ +/*************************************************************************** + * Copyright (C) 2011 by Vishesh Yadav * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "pathconfig.h" +#include "hgconfig.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +HgPathConfigWidget::HgPathConfigWidget(QWidget *parent): + QWidget(parent), + m_loadingCell(false), + m_allValidData(true), + m_newAdd(false) +{ + setupUI(); + loadConfig(); +} + +void HgPathConfigWidget::setupContextMenu() +{ + m_addAction = new KAction(this); + m_addAction->setIcon(KIcon("add")); + m_addAction->setText(i18nc("@action:inmenu", + "Add")); + connect(m_addAction, SIGNAL(triggered()), this, SLOT(slotAddPath())); + + m_modifyAction = new KAction(this); + m_modifyAction->setIcon(KIcon("edit")); + m_modifyAction->setText(i18nc("@action:inmenu", + "Edit")); + connect(m_modifyAction, SIGNAL(triggered()), this, SLOT(slotModifyPath())); + + m_deleteAction = new KAction(this); + m_deleteAction->setIcon(KIcon("remove")); + m_deleteAction->setText(i18nc("@action:inmenu", + "Remove")); + connect(m_deleteAction, SIGNAL(triggered()), this, SLOT(slotDeletePath())); + + m_contextMenu = new KMenu(this); + m_contextMenu->addAction(m_addAction); + m_contextMenu->addAction(m_modifyAction); + m_contextMenu->addAction(m_deleteAction); + + connect(m_pathsListWidget, SIGNAL(cellChanged(int, int)), + this, SLOT(slotCellChanged(int, int))); + connect(m_pathsListWidget, SIGNAL(itemSelectionChanged()), + this, SLOT(slotSelectionChanged())); + connect(m_pathsListWidget, SIGNAL(customContextMenuRequested(const QPoint&)), + this, SLOT(slotContextMenuRequested(const QPoint&))); +} + +void HgPathConfigWidget::setupUI() +{ + // add, remove, modify buttons goes here + QHBoxLayout *actionsLayout = new QHBoxLayout; + m_addPathButton = new KPushButton(i18nc("@label:button", "Add")); + m_modifyPathButton = new KPushButton(i18nc("@label:button", "Edit")); + m_deletePathButton = new KPushButton(i18nc("@label:button", "Remove")); + + actionsLayout->addWidget(m_addPathButton); + actionsLayout->addWidget(m_modifyPathButton); + actionsLayout->addWidget(m_deletePathButton); + + connect(m_addPathButton, SIGNAL(clicked()), this, SLOT(slotAddPath())); + connect(m_modifyPathButton, SIGNAL(clicked()), this, SLOT(slotModifyPath())); + connect(m_deletePathButton, SIGNAL(clicked()), this, SLOT(slotDeletePath())); + + /// create and setup the Table Widget + m_pathsListWidget = new QTableWidget; + setupContextMenu(); // make context menu + + m_pathsListWidget->setColumnCount(2); + m_pathsListWidget->verticalHeader()->hide(); + m_pathsListWidget->horizontalHeader()->hide(); + m_pathsListWidget->setSelectionBehavior(QAbstractItemView::SelectRows); + m_pathsListWidget->setSelectionMode(QAbstractItemView::SingleSelection); + m_pathsListWidget->setEditTriggers(QAbstractItemView::DoubleClicked); + m_pathsListWidget->horizontalHeader()->setStretchLastSection(true); + m_pathsListWidget->setContextMenuPolicy(Qt::CustomContextMenu); + + // setup main layout + QVBoxLayout *mainLayout = new QVBoxLayout; + mainLayout->addLayout(actionsLayout); + mainLayout->addWidget(m_pathsListWidget); + setLayout(mainLayout); +} + +void HgPathConfigWidget::loadConfig() +{ + HgConfig hgc(HgConfig::RepoConfig); + m_remotePathMap = hgc.repoRemotePathList(); + m_loadingCell = true; + + m_pathsListWidget->clearContents(); + m_removeList.clear(); + + QMutableMapIterator it(m_remotePathMap); + int count = 0; + while (it.hasNext()) { + it.next(); + + QTableWidgetItem *alias = new QTableWidgetItem; + QTableWidgetItem *path = new QTableWidgetItem; + + alias->setText(it.key()); + path->setText(it.value()); + + m_pathsListWidget->insertRow(count); + m_pathsListWidget->setItem(count, 0, alias); + m_pathsListWidget->setItem(count, 1, path); + } + + m_pathsListWidget->resizeRowsToContents(); + m_loadingCell = false; +} + +void HgPathConfigWidget::saveConfig() +{ + HgConfig hgc(HgConfig::RepoConfig); + + if (!m_allValidData) { + return; + } + + // first delete the alias in remove list from hgrc + foreach(QString alias, m_removeList) { + hgc.deleteRepoRemotePath(alias); + } + + // now save the new map in table to hgrc + QMutableMapIterator it(m_remotePathMap); + while (it.hasNext()) { + it.next(); + QString alias = it.key(); + QString url = it.value(); + hgc.setRepoRemotePath(alias, url); + } +} + +void HgPathConfigWidget::slotContextMenuRequested(const QPoint &pos) +{ + if (m_pathsListWidget->indexAt(pos).isValid()) { + m_deleteAction->setEnabled(true); + m_modifyAction->setEnabled(true); + } else { + m_deleteAction->setEnabled(false); + m_modifyAction->setEnabled(false); + } + m_addAction->setEnabled(true); + m_contextMenu->exec(m_pathsListWidget->mapToGlobal(pos)); +} + +void HgPathConfigWidget::slotAddPath() +{ + QTableWidgetItem *alias = new QTableWidgetItem; + QTableWidgetItem *path = new QTableWidgetItem; + + int count = m_pathsListWidget->rowCount(); + m_loadingCell = true; + m_pathsListWidget->insertRow(count); + m_pathsListWidget->setItem(count, 0, alias); + m_pathsListWidget->setItem(count, 1, path); + m_pathsListWidget->resizeRowsToContents(); + m_pathsListWidget->setCurrentItem(alias); + m_pathsListWidget->editItem(m_pathsListWidget->item(count, 0)); + m_loadingCell = false; + m_newAdd = true; +} + +void HgPathConfigWidget::slotDeletePath() +{ + int currentRow = m_pathsListWidget->currentRow(); + m_removeList << m_pathsListWidget->item(currentRow, 0)->text(); + m_remotePathMap.remove(m_pathsListWidget->item(currentRow, 0)->text()); + m_pathsListWidget->removeRow(currentRow); +} + +void HgPathConfigWidget::slotModifyPath() +{ + m_pathsListWidget->editItem(m_pathsListWidget->currentItem()); +} + +void HgPathConfigWidget::slotCellChanged(int row, int col) +{ + if (m_loadingCell || + m_oldSelValue == m_pathsListWidget->currentItem()->text()) { + return; + } + + QTableWidgetItem *alias = m_pathsListWidget->item(row, 0); + QTableWidgetItem *url = m_pathsListWidget->item(row, 1); + if (alias->text().isEmpty() || url->text().isEmpty()) { + alias->setBackground(Qt::red); + url->setBackground(Qt::red); + m_allValidData = false; + return; + } + else if (m_remotePathMap.contains(alias->text()) && m_newAdd) { + m_oldSelValue = m_pathsListWidget->currentItem()->text(); + alias->setBackground(Qt::red); + url->setBackground(Qt::red); + m_allValidData = false; + return; + } + else if (m_remotePathMap.contains(alias->text()) && col == 0) { + m_oldSelValue = m_pathsListWidget->currentItem()->text(); + alias->setBackground(Qt::red); + url->setBackground(Qt::red); + m_allValidData = false; + return; + } + else { + kDebug() << "bingo"; + if (!m_newAdd && col == 0) { + m_remotePathMap.remove(m_oldSelValue); + m_removeList << m_oldSelValue; + } + m_remotePathMap.insert(alias->text(), url->text()); + m_oldSelValue = m_pathsListWidget->currentItem()->text(); + alias->setBackground(Qt::NoBrush); + url->setBackground(Qt::NoBrush); + m_allValidData = true; + } + + m_newAdd = false; +} + +void HgPathConfigWidget::slotSelectionChanged() +{ + m_oldSelValue = m_pathsListWidget->currentItem()->text(); +} + +#include "moc_pathconfig.cpp" + diff --git a/dolphin/plugins/hg/config-widgets/pathconfig.h b/dolphin/plugins/hg/config-widgets/pathconfig.h new file mode 100644 index 00000000..a9267f23 --- /dev/null +++ b/dolphin/plugins/hg/config-widgets/pathconfig.h @@ -0,0 +1,90 @@ +/*************************************************************************** + * Copyright (C) 2011 by Vishesh Yadav * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef HGPATH_CONFIG_H +#define HGPATH_CONFIG_H + +#include +#include +#include + +#include +#include +class KPushButton; +class KAction; +class KMenu; + +/** + * UI to add, remove and modify paths in repository's hgrc file. Can be used with + * repository hgrc file only. + */ +class HgPathConfigWidget : public QWidget +{ + Q_OBJECT + +public: + HgPathConfigWidget(QWidget *parent = 0); + +public slots: + void saveConfig(); + void loadConfig(); + +private: + void setupUI(); + + /** + * Prepare context menu and its actions for table widget showing path. + */ + void setupContextMenu(); + +private slots: + /** + * Show context menu and changed enabled status of actions according + * to the position where menu is requested. + */ + void slotContextMenuRequested(const QPoint &pos); + void slotCellChanged(int row, int col); + void slotSelectionChanged(); + + void slotAddPath(); + void slotModifyPath(); + void slotDeletePath(); + +private: + QTableWidget *m_pathsListWidget; + bool m_loadingCell; + bool m_allValidData; + bool m_newAdd; + QString m_oldSelValue; + + KPushButton *m_addPathButton; + KPushButton *m_deletePathButton; + KPushButton *m_modifyPathButton; + + KAction *m_addAction; + KAction *m_modifyAction; + KAction *m_deleteAction; + KMenu *m_contextMenu; + + QMap m_remotePathMap; + QStringList m_removeList; +}; + +#endif // HGPATH_CONFIG_H + diff --git a/dolphin/plugins/hg/config-widgets/pluginsettings.cpp b/dolphin/plugins/hg/config-widgets/pluginsettings.cpp new file mode 100644 index 00000000..70d4d002 --- /dev/null +++ b/dolphin/plugins/hg/config-widgets/pluginsettings.cpp @@ -0,0 +1,91 @@ +/*************************************************************************** + * Copyright (C) 2011 by Vishesh Yadav * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "pluginsettings.h" +#include "hgconfig.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +HgPluginSettingsWidget::HgPluginSettingsWidget(QWidget *parent) : + QWidget(parent), + m_config(0) +{ + setupUI(); + loadConfig(); + + connect(m_diffBrowseButton, SIGNAL(clicked()), + this, SLOT(browse_diff())); +} + +void HgPluginSettingsWidget::saveConfig() +{ + KConfigGroup group(m_config, QLatin1String("diff")); + group.writeEntry(QLatin1String("exec"), m_diffProg->text()); + group.config()->sync(); +} + +void HgPluginSettingsWidget::loadConfig() +{ + KUrl url = KUrl::fromPath(QDir::homePath()); + url.addPath(".dolphin-hg"); + m_config = new KConfig(url.path(), KConfig::SimpleConfig); + + KConfigGroup group(m_config, QLatin1String("diff")); + QString diffExec = group.readEntry(QLatin1String("exec"), QString()).trimmed(); + m_diffProg->setText(diffExec); +} + +void HgPluginSettingsWidget::setupUI() +{ + m_diffProg = new KLineEdit; + m_diffBrowseButton = new KPushButton(i18nc("@label", "Browse")); + QLabel *diffProgLabel = new QLabel(i18nc("@label", + "Visual Diff Executable")); + + QGridLayout *layout = new QGridLayout; + layout->addWidget(diffProgLabel, 0, 0); + layout->addWidget(m_diffProg, 0, 1); + layout->addWidget(m_diffBrowseButton, 0, 2); + layout->setRowStretch(layout->rowCount(), 1); + + setLayout(layout); +} + +void HgPluginSettingsWidget::browse_diff() +{ + QString path = KFileDialog::getOpenFileName(); + if (path.isEmpty()) { + return; + } + + m_diffProg->setText(path); +} + +#include "moc_pluginsettings.cpp" + diff --git a/dolphin/plugins/hg/config-widgets/pluginsettings.h b/dolphin/plugins/hg/config-widgets/pluginsettings.h new file mode 100644 index 00000000..cb652d19 --- /dev/null +++ b/dolphin/plugins/hg/config-widgets/pluginsettings.h @@ -0,0 +1,58 @@ +/*************************************************************************** + * Copyright (C) 2011 by Vishesh Yadav * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef HG_PLUGIN_SETTINGS_WIDGET_H +#define HG_PLUGIN_SETTINGS_WIDGET_H + +#include +#include "hgconfig.h" + +class KLineEdit; +class KConfig; +class KPushButton; + +/** + * Plugin Specific settings. Not those supposed to be saved in + * .hgrc file, but in $HOME/.dolphin-hg + */ +class HgPluginSettingsWidget : public QWidget +{ + Q_OBJECT + +public: + HgPluginSettingsWidget(QWidget *parent = 0); + +public slots: + void saveConfig(); + void loadConfig(); + +private slots: + void browse_diff(); + +private: + void setupUI(); + +private: + KLineEdit *m_diffProg; + KConfig *m_config; + KPushButton *m_diffBrowseButton; +}; + +#endif // HG_PLUGIN_SETTINGS_WIDGET_H + diff --git a/dolphin/plugins/hg/configdialog.cpp b/dolphin/plugins/hg/configdialog.cpp new file mode 100644 index 00000000..c075547f --- /dev/null +++ b/dolphin/plugins/hg/configdialog.cpp @@ -0,0 +1,114 @@ +/*************************************************************************** + * Copyright (C) 2011 by Vishesh Yadav * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "configdialog.h" +#include "hgwrapper.h" +#include "fileviewhgpluginsettings.h" + +#include "config-widgets/generalconfig.h" +#include "config-widgets/pathconfig.h" +#include "config-widgets/ignorewidget.h" +#include "config-widgets/pluginsettings.h" + +#include +#include +#include + +HgConfigDialog::HgConfigDialog(HgConfig::ConfigType type, QWidget *parent): + KPageDialog(parent, Qt::Dialog), + m_configType(type) +{ + // dialog properties + if (m_configType == HgConfig::RepoConfig) { + this->setCaption(i18nc("@title:window", + "Hg Repository Configuration")); + } else { + this->setCaption(i18nc("@title:window", + "Hg Global Configuration")); + } + this->setButtons(KDialog::Ok | KDialog::Apply | KDialog::Cancel); + this->setDefaultButton(KDialog::Ok); + //this->enableButtonOk(false); + + setupUI(); + loadGeometry(); + + connect(this, SIGNAL(applyClicked()), this, SLOT(saveSettings())); + connect(this, SIGNAL(finished()), this, SLOT(saveGeometry())); +} + +void HgConfigDialog::setupUI() +{ + m_generalConfig = new HgGeneralConfigWidget(m_configType); + addPage(m_generalConfig, i18nc("@label:group", "General Settings")); + + if (m_configType == HgConfig::RepoConfig) { + m_pathConfig = new HgPathConfigWidget; + addPage(m_pathConfig, i18nc("@label:group", "Repository Paths")); + + m_ignoreWidget = new HgIgnoreWidget; + addPage(m_ignoreWidget, i18nc("@label:group", "Ignored Files")); + } + else if (m_configType == HgConfig::GlobalConfig) { + m_pluginSetting = new HgPluginSettingsWidget; + addPage(m_pluginSetting, i18nc("@label:group", "Plugin Settings")); + } +} + +void HgConfigDialog::saveSettings() +{ + kDebug() << "Saving Mercurial configuration"; + m_generalConfig->saveConfig(); + if (m_configType == HgConfig::RepoConfig) { + m_pathConfig->saveConfig(); + m_ignoreWidget->saveConfig(); + } + else if (m_configType == HgConfig::GlobalConfig) { + m_pluginSetting->saveConfig(); + } +} + +void HgConfigDialog::done(int r) +{ + if (r == KDialog::Accepted) { + saveSettings(); + KDialog::done(r); + } + else { + KDialog::done(r); + } +} + +void HgConfigDialog::loadGeometry() +{ + FileViewHgPluginSettings *settings = FileViewHgPluginSettings::self(); + this->setInitialSize(QSize(settings->configDialogWidth(), + settings->configDialogHeight())); +} + +void HgConfigDialog::saveGeometry() +{ + FileViewHgPluginSettings *settings = FileViewHgPluginSettings::self(); + settings->setConfigDialogHeight(this->height()); + settings->setConfigDialogWidth(this->width()); + settings->writeConfig(); +} + +#include "moc_configdialog.cpp" + diff --git a/dolphin/plugins/hg/configdialog.h b/dolphin/plugins/hg/configdialog.h new file mode 100644 index 00000000..f87a7b4e --- /dev/null +++ b/dolphin/plugins/hg/configdialog.h @@ -0,0 +1,63 @@ +/*************************************************************************** + * Copyright (C) 2011 by Vishesh Yadav * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef HGCONFIGDILAOG_H +#define HGCONFIGDILAOG_H + +#include "hgconfig.h" +#include + +class HgGeneralConfigWidget; +class HgPathConfigWidget; +class HgIgnoreWidget; +class HgPluginSettingsWidget; + +/** + * Implemets a dialog which provides an easy way to edit several + * configuration options for Mercurial and the plugin. + */ +class HgConfigDialog : public KPageDialog +{ + Q_OBJECT + +public: + HgConfigDialog(HgConfig::ConfigType type, QWidget *parent = 0); + +private: + void done(int r); + + // user interface + void setupUI(); + +private slots: + void saveSettings(); + void saveGeometry(); + void loadGeometry(); + +private: + HgGeneralConfigWidget *m_generalConfig; + HgPathConfigWidget *m_pathConfig; + HgIgnoreWidget *m_ignoreWidget; + HgPluginSettingsWidget *m_pluginSetting; + + HgConfig::ConfigType m_configType; +}; + +#endif // HGCONFIGDILAOG_H + diff --git a/dolphin/plugins/hg/createdialog.cpp b/dolphin/plugins/hg/createdialog.cpp new file mode 100644 index 00000000..60154c7e --- /dev/null +++ b/dolphin/plugins/hg/createdialog.cpp @@ -0,0 +1,87 @@ +/*************************************************************************** + * Copyright (C) 2011 by Vishesh Yadav * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "createdialog.h" +#include "fileviewhgpluginsettings.h" + +#include +#include +#include +#include +#include +#include +#include + +HgCreateDialog::HgCreateDialog(const QString &directory, QWidget *parent): + KDialog(parent, Qt::Dialog), + m_workingDirectory(directory) +{ + // dialog properties + this->setCaption(i18nc("@title:window", + "Hg Initialize Repository")); + this->setButtons(KDialog::Ok | KDialog::Cancel); + this->setDefaultButton(KDialog::Ok); + this->setButtonText(KDialog::Ok, i18nc("@action:button", "Initialize Repository")); + //this->enableButtonOk(false); + + + ////////////// + // Setup UI // + ////////////// + + m_directory = new QLabel("" + m_workingDirectory + ""); + m_repoNameEdit = new KLineEdit; + + QHBoxLayout *mainLayout = new QHBoxLayout; + mainLayout->addWidget(m_directory); + mainLayout->addWidget(m_repoNameEdit); + + QFrame *frame = new QFrame; + frame->setLayout(mainLayout); + setMainWidget(frame); + m_repoNameEdit->setFocus(); +} + +void HgCreateDialog::done(int r) +{ + if (r == KDialog::Accepted) { + QProcess process; + QStringList args; + args << QLatin1String("init"); + if (!m_repoNameEdit->text().isEmpty()) { + args << m_repoNameEdit->text(); + } + process.setWorkingDirectory(m_workingDirectory); + process.start(QLatin1String("hg"), args); + process.waitForFinished(); + + if (process.exitCode() == 0 && process.exitStatus() == QProcess::NormalExit) { + KDialog::done(r); + } + else { + KMessageBox::error(this, i18nc("error message", "Error creating repository!")); + } + } + else { + KDialog::done(r); + } +} + +#include "moc_createdialog.cpp" + diff --git a/dolphin/plugins/hg/createdialog.h b/dolphin/plugins/hg/createdialog.h new file mode 100644 index 00000000..209fa349 --- /dev/null +++ b/dolphin/plugins/hg/createdialog.h @@ -0,0 +1,51 @@ +/*************************************************************************** + * Copyright (C) 2011 by Vishesh Yadav * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef HGCREATEDILAOG_H +#define HGCREATEDILAOG_H + +#include +#include + +class KLineEdit; +#include + +/** + * Dialog to initialize new mercurial repository + */ +class HgCreateDialog : public KDialog +{ + Q_OBJECT + +public: + HgCreateDialog(const QString &directory, QWidget *parent = 0); + +private: + void done(int r); + void setWorkingDirectory(const QString &directory); + +private: + QString m_workingDirectory; + KLineEdit *m_repoNameEdit; + QLabel *m_directory; + +}; + +#endif // HGCREATEDILAOG_H + diff --git a/dolphin/plugins/hg/exportdialog.cpp b/dolphin/plugins/hg/exportdialog.cpp new file mode 100644 index 00000000..ce9bae2b --- /dev/null +++ b/dolphin/plugins/hg/exportdialog.cpp @@ -0,0 +1,190 @@ +/*************************************************************************** + * Copyright (C) 2011 by Vishesh Yadav * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "exportdialog.h" +#include "fileviewhgpluginsettings.h" +#include "commitinfowidget.h" +#include "hgwrapper.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +HgExportDialog::HgExportDialog(QWidget *parent) : + KDialog(parent, Qt::Dialog) +{ + // dialog properties + this->setCaption(i18nc("@title:window", + "Hg Export")); + this->setButtons(KDialog::Ok | KDialog::Cancel); + this->setDefaultButton(KDialog::Ok); + this->setButtonText(KDialog::Ok, i18nc("@action:button", "Export")); + + // + setupUI(); + loadCommits(); + + // Load saved settings + FileViewHgPluginSettings *settings = FileViewHgPluginSettings::self(); + this->setInitialSize(QSize(settings->exportDialogWidth(), + settings->exportDialogHeight())); + + // + connect(this, SIGNAL(finished()), this, SLOT(saveGeometry())); +} + +void HgExportDialog::setupUI() +{ + QGroupBox *mainGroup = new QGroupBox; + QGridLayout *mainLayout = new QGridLayout; + m_commitInfoWidget = new HgCommitInfoWidget; + + m_commitInfoWidget->setSelectionMode(QAbstractItemView::ExtendedSelection); + mainLayout->addWidget(m_commitInfoWidget); + mainGroup->setLayout(mainLayout); + + // options + m_optionGroup = new QGroupBox(i18nc("@label:group", "Options")); + m_optText = new QCheckBox(i18nc("@label", "Treat all files as text")); + m_optGit = new QCheckBox(i18nc("@label", "Use Git extended diff format")); + m_optNoDates = new QCheckBox(i18nc("@label", "Omit dates from diff headers")); + QVBoxLayout *optionLayout = new QVBoxLayout; + optionLayout->addWidget(m_optText); + optionLayout->addWidget(m_optGit); + optionLayout->addWidget(m_optNoDates); + m_optionGroup->setLayout(optionLayout); + + //setup main dialog widget + QWidget *widget = new QWidget; + QVBoxLayout *layout = new QVBoxLayout; + layout->addWidget(mainGroup); + layout->addWidget(m_optionGroup); + widget->setLayout(layout); + setMainWidget(widget); +} + +void HgExportDialog::loadCommits() +{ + HgWrapper *hgWrapper = HgWrapper::instance(); + + // update heads list + QProcess process; + process.setWorkingDirectory(hgWrapper->getBaseDir()); + + QStringList args; + args << QLatin1String("log"); + args << QLatin1String("--template"); + args << QLatin1String("{rev}\n{node|short}\n{branch}\n" + "{author}\n{desc|firstline}\n"); + + process.start(QLatin1String("hg"), args); + process.waitForFinished(); + m_commitInfoWidget->clear(); + + const int FINAL = 5; + char buffer[FINAL][1024]; + int count = 0; + while (process.readLine(buffer[count], sizeof(buffer[count])) > 0) { + if (count == FINAL - 1) { + QString rev = QTextCodec::codecForLocale()->toUnicode(buffer[0]).trimmed(); + QString changeset = QTextCodec::codecForLocale()->toUnicode(buffer[1]).trimmed(); + QString branch = QTextCodec::codecForLocale()->toUnicode(buffer[2]).trimmed(); + QString author = QTextCodec::codecForLocale()->toUnicode(buffer[3]).trimmed(); + QString log = QTextCodec::codecForLocale()->toUnicode(buffer[4]).trimmed(); + + QListWidgetItem *item = new QListWidgetItem; + item->setData(Qt::DisplayRole, changeset); + item->setData(Qt::UserRole + 1, rev); + item->setData(Qt::UserRole + 2, branch); + item->setData(Qt::UserRole + 3, author); + item->setData(Qt::UserRole + 4, log); + m_commitInfoWidget->addItem(item); + } + count = (count + 1)%FINAL; + } +} + +void HgExportDialog::saveGeometry() +{ + FileViewHgPluginSettings *settings = FileViewHgPluginSettings::self(); + settings->setExportDialogHeight(this->height()); + settings->setExportDialogWidth(this->width()); + settings->writeConfig(); +} + +void HgExportDialog::done(int r) +{ + if (r == KDialog::Accepted) { + QList items = m_commitInfoWidget->selectedItems(); + if (items.empty()) { + KMessageBox::error(this, i18nc("@message:error", + "Please select at least one changeset to be exported!")); + return; + } + + QStringList args; + if (m_optText->checkState() == Qt::Checked) { + args << QLatin1String("--text"); + } + if (m_optGit->checkState() == Qt::Checked) { + args << QLatin1String("--git"); + } + if (m_optNoDates->checkState() == Qt::Checked) { + args << QLatin1String("--nodates"); + } + + args << QLatin1String("-r"); + foreach (QListWidgetItem *item, items) { + args << item->data(Qt::DisplayRole).toString(); + } + + QString directory = KFileDialog::getExistingDirectory(); + if (directory.isEmpty()) { + return; + } + if (!directory.endsWith('/')) { + directory.append('/'); + } + + args << QLatin1String("--output"); + args << directory + QLatin1String("%b_%h.patch"); + + HgWrapper *hgw = HgWrapper::instance(); + if (hgw->executeCommandTillFinished(QLatin1String("export"), args)) { + KDialog::done(r); + } + else { + KMessageBox::error(this, hgw->readAllStandardError()); + } + } + else { + KDialog::done(r); + } +} + +#include "moc_exportdialog.cpp" + diff --git a/dolphin/plugins/hg/exportdialog.h b/dolphin/plugins/hg/exportdialog.h new file mode 100644 index 00000000..80f043f8 --- /dev/null +++ b/dolphin/plugins/hg/exportdialog.h @@ -0,0 +1,66 @@ +/*************************************************************************** + * Copyright (C) 2011 by Vishesh Yadav * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef HGEXPORTDIALOG_H +#define HGEXPORTDIALOG_H + +#include + +class HgCommitInfoWidget; +#include +#include +class KLineEdit; + +//TODO: Some helper for writing patterns +// +/** + * Dialog to implment mercurial export feature. Dialogs presents list of + * changesets from which the user will select entries and export a series of + * patch files for each changeset. + */ +class HgExportDialog : public KDialog +{ + Q_OBJECT + +public: + HgExportDialog(QWidget *parent=0); + +public slots: + void done(int r); + +private slots: + void saveGeometry(); + +private: + void setupUI(); + void loadCommits(); + +private: + HgCommitInfoWidget *m_commitInfoWidget; + + //options + QGroupBox *m_optionGroup; + QCheckBox *m_optText; + QCheckBox *m_optGit; + QCheckBox *m_optNoDates; + +}; + +#endif /* HGEXPORTDIALOG_H */ + diff --git a/dolphin/plugins/hg/fileviewhgplugin.cpp b/dolphin/plugins/hg/fileviewhgplugin.cpp new file mode 100644 index 00000000..39504d76 --- /dev/null +++ b/dolphin/plugins/hg/fileviewhgplugin.cpp @@ -0,0 +1,841 @@ +/*************************************************************************** + * Copyright (C) 2011 by Vishesh Yadav * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "fileviewhgplugin.h" +#include "hgconfig.h" +#include "configdialog.h" +#include "renamedialog.h" +#include "commitdialog.h" +#include "branchdialog.h" +#include "tagdialog.h" +#include "updatedialog.h" +#include "clonedialog.h" +#include "createdialog.h" +#include "pushdialog.h" +#include "pulldialog.h" +#include "mergedialog.h" +#include "bundledialog.h" +#include "exportdialog.h" +#include "importdialog.h" +#include "servedialog.h" +#include "backoutdialog.h" + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +K_PLUGIN_FACTORY(FileViewHgPluginFactory, registerPlugin();) +K_EXPORT_PLUGIN(FileViewHgPluginFactory("fileviewhgplugin")) + + +//TODO: Build a proper status signal system to sync HgWrapper/Dialgs with this +//TODO: Show error messages and set their message approproately(hg output) +//TODO: Use i18nc rather thn i18c throughout plugin + +FileViewHgPlugin::FileViewHgPlugin(QObject *parent, const QList &args): + KVersionControlPlugin(parent), + m_mainContextMenu(0), + m_addAction(0), + m_removeAction(0), + m_renameAction(0), + m_commitAction(0), + m_branchAction(0), + m_tagAction(0), + m_updateAction(0), + m_cloneAction(0), + m_createAction(0), + m_globalConfigAction(0), + m_repoConfigAction(0), + m_pushAction(0), + m_pullAction(0), + m_revertAction(0), + m_revertAllAction(0), + m_rollbackAction(0), + m_mergeAction(0), + m_bundleAction(0), + m_exportAction(0), + m_unbundleAction(0), + m_importAction(0), + m_diffAction(0), + m_serveAction(0), + m_backoutAction(0), + m_hgWrapper(0), + m_retrievalHgw(0) +{ + Q_UNUSED(args); + + m_addAction = new KAction(this); + m_addAction->setIcon(KIcon("list-add")); + m_addAction->setText(i18nc("@action:inmenu", + "Hg Add")); + connect(m_addAction, SIGNAL(triggered()), + this, SLOT(addFiles())); + + m_removeAction = new KAction(this); + m_removeAction->setIcon(KIcon("list-remove")); + m_removeAction->setText(i18nc("@action:inmenu", + "Hg Remove")); + connect(m_removeAction, SIGNAL(triggered()), + this, SLOT(removeFiles())); + + m_renameAction = new KAction(this); + m_renameAction->setIcon(KIcon("list-rename")); + m_renameAction->setText(i18nc("@action:inmenu", + "Hg Rename")); + connect(m_renameAction, SIGNAL(triggered()), + this, SLOT(renameFile())); + + m_commitAction = new KAction(this); + m_commitAction->setIcon(KIcon("svn-commit")); + m_commitAction->setText(i18nc("@action:inmenu", + "Hg Commit")); + connect(m_commitAction, SIGNAL(triggered()), + this, SLOT(commit())); + + m_tagAction = new KAction(this); + m_tagAction->setIcon(KIcon("bookmark-new")); + m_tagAction->setText(i18nc("@action:inmenu", + "Hg Tag")); + connect(m_tagAction, SIGNAL(triggered()), + this, SLOT(tag())); + + m_branchAction = new KAction(this); + m_branchAction->setIcon(KIcon("bookmark-new")); + m_branchAction->setText(i18nc("@action:inmenu", + "Hg Branch")); + connect(m_branchAction, SIGNAL(triggered()), + this, SLOT(branch())); + + m_cloneAction = new KAction(this); + m_cloneAction->setIcon(KIcon("hg-clone")); + m_cloneAction->setText(i18nc("@action:inmenu", + "Hg Clone")); + connect(m_cloneAction, SIGNAL(triggered()), + this, SLOT(clone())); + + m_createAction = new KAction(this); + m_createAction->setIcon(KIcon("hg-create")); + m_createAction->setText(i18nc("@action:inmenu", + "Hg Init")); + connect(m_createAction, SIGNAL(triggered()), + this, SLOT(create())); + + m_updateAction = new KAction(this); + m_updateAction->setIcon(KIcon("svn-update")); + m_updateAction->setText(i18nc("@action:inmenu", + "Hg Update")); + connect(m_updateAction, SIGNAL(triggered()), + this, SLOT(update())); + + m_globalConfigAction = new KAction(this); + m_globalConfigAction->setIcon(KIcon("hg-config")); + m_globalConfigAction->setText(i18nc("@action:inmenu", + "Hg Global Config")); + connect(m_globalConfigAction, SIGNAL(triggered()), + this, SLOT(global_config())); + + m_repoConfigAction = new KAction(this); + m_repoConfigAction->setIcon(KIcon("hg-config")); + m_repoConfigAction->setText(i18nc("@action:inmenu", + "Hg Repository Config")); + connect(m_repoConfigAction, SIGNAL(triggered()), + this, SLOT(repo_config())); + + m_pushAction = new KAction(this); + m_pushAction->setIcon(KIcon("git-push")); + m_pushAction->setText(i18nc("@action:inmenu", + "Hg Push")); + connect(m_pushAction, SIGNAL(triggered()), + this, SLOT(push())); + + m_pullAction = new KAction(this); + m_pullAction->setIcon(KIcon("git-pull")); + m_pullAction->setText(i18nc("@action:inmenu", + "Hg Pull")); + connect(m_pullAction, SIGNAL(triggered()), + this, SLOT(pull())); + + m_revertAction = new KAction(this); + m_revertAction->setIcon(KIcon("hg-revert")); + m_revertAction->setText(i18nc("@action:inmenu", + "Hg Revert")); + connect(m_revertAction, SIGNAL(triggered()), + this, SLOT(revert())); + + m_revertAllAction = new KAction(this); + m_revertAllAction->setIcon(KIcon("hg-revert")); + m_revertAllAction->setText(i18nc("@action:inmenu", + "Hg Revert All")); + connect(m_revertAllAction, SIGNAL(triggered()), + this, SLOT(revertAll())); + + m_rollbackAction = new KAction(this); + m_rollbackAction->setIcon(KIcon("hg-rollback")); + m_rollbackAction->setText(i18nc("@action:inmenu", + "Hg Rollback")); + connect(m_rollbackAction, SIGNAL(triggered()), + this, SLOT(rollback())); + + m_mergeAction = new KAction(this); + m_mergeAction->setIcon(KIcon("hg-merge")); + m_mergeAction->setText(i18nc("@action:inmenu", + "Hg Merge")); + connect(m_mergeAction, SIGNAL(triggered()), + this, SLOT(merge())); + + m_bundleAction = new KAction(this); + m_bundleAction->setIcon(KIcon("hg-bundle")); + m_bundleAction->setText(i18nc("@action:inmenu", + "Hg Bundle")); + connect(m_bundleAction, SIGNAL(triggered()), + this, SLOT(bundle())); + + m_exportAction = new KAction(this); + m_exportAction->setIcon(KIcon("hg-export")); + m_exportAction->setText(i18nc("@action:inmenu", + "Hg Export")); + connect(m_exportAction, SIGNAL(triggered()), + this, SLOT(exportChangesets())); + + m_importAction = new KAction(this); + m_importAction->setIcon(KIcon("hg-import")); + m_importAction->setText(i18nc("@action:inmenu", + "Hg Import")); + connect(m_importAction, SIGNAL(triggered()), + this, SLOT(importChangesets())); + + m_unbundleAction = new KAction(this); + m_unbundleAction->setIcon(KIcon("hg-unbundle")); + m_unbundleAction->setText(i18nc("@action:inmenu", + "Hg Unbundle")); + connect(m_unbundleAction, SIGNAL(triggered()), + this, SLOT(unbundle())); + + m_serveAction = new KAction(this); + m_serveAction->setIcon(KIcon("hg-serve")); + m_serveAction->setText(i18nc("@action:inmenu", + "Hg Serve")); + connect(m_serveAction, SIGNAL(triggered()), + this, SLOT(serve())); + + m_backoutAction = new KAction(this); + m_backoutAction->setIcon(KIcon("hg-backout")); + m_backoutAction->setText(i18nc("@action:inmenu", + "Hg Backout")); + connect(m_backoutAction, SIGNAL(triggered()), + this, SLOT(backout())); + + m_diffAction = new KAction(this); + m_diffAction->setIcon(KIcon("hg-diff")); + m_diffAction->setText(i18nc("@action:inmenu", + "Hg Diff")); + connect(m_diffAction, SIGNAL(triggered()), + this, SLOT(diff())); + + /* Submenu to make the main menu less cluttered */ + m_mainContextMenu = new KMenu; + m_mainContextMenu->addAction(m_updateAction); + m_mainContextMenu->addAction(m_branchAction); + m_mainContextMenu->addAction(m_tagAction); + m_mainContextMenu->addAction(m_mergeAction); + m_mainContextMenu->addAction(m_revertAllAction); + m_mainContextMenu->addAction(m_rollbackAction); + m_mainContextMenu->addAction(m_backoutAction); + m_mainContextMenu->addAction(m_bundleAction); + m_mainContextMenu->addAction(m_unbundleAction); + m_mainContextMenu->addAction(m_exportAction); + m_mainContextMenu->addAction(m_importAction); + m_mainContextMenu->addAction(m_serveAction); + m_mainContextMenu->addAction(m_globalConfigAction); + m_mainContextMenu->addAction(m_repoConfigAction); + + m_menuAction = new KAction(this); + m_menuAction->setIcon(KIcon("hg-main")); + m_menuAction->setText(i18nc("@action:inmenu", + "Mercurial")); + m_menuAction->setMenu(m_mainContextMenu); +} + +FileViewHgPlugin::~FileViewHgPlugin() +{ +} + +void FileViewHgPlugin::createHgWrapper() const +{ + static bool created = false; + + if (created && m_hgWrapper != 0) { + return; + } + + created = true; + + m_hgWrapper = HgWrapper::instance(); + + connect(m_hgWrapper, + SIGNAL(primaryOperationFinished(int, QProcess::ExitStatus)), + this, SLOT(slotOperationCompleted(int, QProcess::ExitStatus))); + connect(m_hgWrapper, SIGNAL(primaryOperationError(QProcess::ProcessError)), + this, SLOT(slotOperationError())); +} + +QString FileViewHgPlugin::fileName() const +{ + return QLatin1String(".hg"); +} + +bool FileViewHgPlugin::beginRetrieval(const QString &directory) +{ + clearMessages(); + m_currentDir = directory; + m_versionInfoHash.clear(); + //createHgWrapper(); + //m_hgWrapper->setCurrentDir(directory); + //m_hgWrapper->getItemVersions(m_versionInfoHash); + if (m_retrievalHgw == 0) { + m_retrievalHgw = new HgWrapper; + } + m_retrievalHgw->setCurrentDir(directory); + m_retrievalHgw->getItemVersions(m_versionInfoHash); + return true; +} + +void FileViewHgPlugin::endRetrieval() +{ +} + +KVersionControlPlugin::ItemVersion FileViewHgPlugin::itemVersion(const KFileItem &item) const +{ + //FIXME: When folder is empty or all files within untracked. + const QString itemUrl = item.localPath(); + if (item.isDir()) { + QHash::const_iterator it + = m_versionInfoHash.constBegin(); + while (it != m_versionInfoHash.constEnd()) { + if (it.key().startsWith(itemUrl)) { + const ItemVersion state = m_versionInfoHash.value(it.key()); + if (state == LocallyModifiedVersion || + state == AddedVersion || + state == RemovedVersion) { + return LocallyModifiedVersion; + } + } + ++it; + } + + // Making folders with all files within untracked 'Unversioned' + // will disable the context menu there. Will enable recursive + // add however. + QDir dir(item.localPath()); + QStringList filesInside = dir.entryList(); + foreach (const QString &fileName, filesInside) { + if (fileName == "." || fileName == ".." ) { + continue; + } + KUrl tempUrl(dir.absoluteFilePath(fileName)); + KFileItem tempFileItem(KFileItem::Unknown, + KFileItem::Unknown, tempUrl); + if (itemVersion(tempFileItem) == NormalVersion) { + return NormalVersion; + } + } + return UnversionedVersion; + } + if (m_versionInfoHash.contains(itemUrl)) { + return m_versionInfoHash.value(itemUrl); + } + return NormalVersion; +} + +QList FileViewHgPlugin::actions(const KFileItemList &items) const +{ + //TODO: Make it work with universal context menu when imlpemented + // in dolphin + kDebug() << items.count(); + if (items.count() == 1 && items.first().isDir()) { + return directoryContextMenu(m_currentDir); + } + else { + return itemContextMenu(items); + } + return QList(); +} + +QList FileViewHgPlugin::universalContextMenuActions(const QString &directory) const +{ + QList result; + m_universalCurrentDirectory = directory; + result.append(m_createAction); + result.append(m_cloneAction); + return result; +} + +QList FileViewHgPlugin::itemContextMenu(const KFileItemList &items) const +{ + Q_ASSERT(!items.isEmpty()); + + clearMessages(); + createHgWrapper(); + m_hgWrapper->setCurrentDir(m_currentDir); + if (!m_hgWrapper->isBusy()) { + m_contextItems.clear(); + foreach (const KFileItem &item, items) { + m_contextItems.append(item); + } + + //see which actions should be enabled + int versionedCount = 0; + int addableCount = 0; + int revertableCount = 0; + foreach (const KFileItem &item, items) { + const ItemVersion state = itemVersion(item); + if (state != UnversionedVersion && state != RemovedVersion) { + ++versionedCount; + } + if (state == UnversionedVersion || + state == LocallyModifiedUnstagedVersion) { + ++addableCount; + } + if (state == LocallyModifiedVersion || + state == AddedVersion || + state == RemovedVersion) { + ++revertableCount; + } + } + + m_addAction->setEnabled(addableCount == items.count()); + m_removeAction->setEnabled(versionedCount == items.count()); + m_revertAction->setEnabled(revertableCount == items.count()); + m_diffAction->setEnabled(revertableCount == items.count() && + items.size() == 1); + m_renameAction->setEnabled(items.size() == 1 && + itemVersion(items.first()) != UnversionedVersion); + } + else { + m_addAction->setEnabled(false); + m_removeAction->setEnabled(false); + m_renameAction->setEnabled(false); + m_revertAction->setEnabled(false); + m_diffAction->setEnabled(false); + } + + QList actions; + actions.append(m_addAction); + actions.append(m_removeAction); + actions.append(m_renameAction); + actions.append(m_revertAction); + actions.append(m_diffAction); + + return actions; +} + +QList FileViewHgPlugin::directoryContextMenu(const QString &directory) const +{ + QList actions; + clearMessages(); + createHgWrapper(); + m_hgWrapper->setCurrentDir(directory); + if (!m_hgWrapper->isBusy()) { + actions.append(m_commitAction); + } + actions.append(m_pushAction); + actions.append(m_pullAction); + actions.append(m_diffAction); + actions.append(m_menuAction); + return actions; +} + +void FileViewHgPlugin::addFiles() +{ + Q_ASSERT(!m_contextItems.isEmpty()); + QString infoMsg = i18nc("@info:status", + "Adding files to Hg repository..."); + m_errorMsg = i18nc("@info:status", + "Adding files to Hg repository failed."); + m_operationCompletedMsg = i18nc("@info:status", + "Added files to Hg repository."); + + emit infoMessage(infoMsg); + m_hgWrapper->addFiles(m_contextItems); +} + +void FileViewHgPlugin::removeFiles() +{ + Q_ASSERT(!m_contextItems.isEmpty()); + + int answer = KMessageBox::questionYesNo(0, i18nc("@message:yesorno", + "Would you like to remove selected files " + "from the repository?")); + if (answer == KMessageBox::No) { + return; + } + + QString infoMsg = i18nc("@info:status", + "Removing files from Hg repository..."); + m_errorMsg = i18nc("@info:status", + "Removing files from Hg repository failed."); + m_operationCompletedMsg = i18nc("@info:status", + "Removed files from Hg repository."); + + emit infoMessage(infoMsg); + m_hgWrapper->removeFiles(m_contextItems); +} + + +void FileViewHgPlugin::renameFile() +{ + Q_ASSERT(m_contextItems.size() == 1); + + m_errorMsg = i18nc("@info:status", + "Renaming of file in Hg repository failed."); + m_operationCompletedMsg = i18nc("@info:status", + "Renamed file in Hg repository successfully."); + emit infoMessage(i18nc("@info:status", + "Renaming file in Hg repository.")); + + HgRenameDialog dialog(m_contextItems.first()); + dialog.exec(); + m_contextItems.clear(); +} + + +void FileViewHgPlugin::commit() +{ + if (m_hgWrapper->isWorkingDirectoryClean()) { + KMessageBox::information(0, i18nc("@message", "No changes for commit!")); + return; + } + //FIXME: Disable emitting of status messages when executing sub tasks. + m_errorMsg = i18nc("@info:status", + "Commit to Hg repository failed."); + m_operationCompletedMsg = i18nc("@info:status", + "Committed to Hg repository."); + emit infoMessage(i18nc("@info:status", + "Commit Hg repository.")); + + HgCommitDialog dialog; + if (dialog.exec() == QDialog::Accepted) { + emit itemVersionsChanged(); + }; +} + +void FileViewHgPlugin::tag() +{ + m_errorMsg = i18nc("@info:status", + "Tag operation in Hg repository failed."); + m_operationCompletedMsg = i18nc("@info:status", + "Tagging operation in Hg repository is successful."); + emit infoMessage(i18nc("@info:status", + "Tagging operation in Hg repository.")); + + HgTagDialog dialog; + dialog.exec(); +} + +void FileViewHgPlugin::update() +{ + m_errorMsg = i18nc("@info:status", + "Update of Hg working directory failed."); + m_operationCompletedMsg = i18nc("@info:status", + "Update of Hg working directory is successful."); + emit infoMessage(i18nc("@info:status", + "Updating Hg working directory.")); + + HgUpdateDialog dialog; + dialog.exec(); +} + +void FileViewHgPlugin::branch() +{ + m_errorMsg = i18nc("@info:status", + "Branch operation on Hg repository failed."); + m_operationCompletedMsg = i18nc("@info:status", + "Branch operation on Hg repository completed successfully."); + emit infoMessage(i18nc("@info:status", + "Branch operation on Hg repository.")); + + HgBranchDialog dialog; + dialog.exec(); +} + +void FileViewHgPlugin::clone() +{ + clearMessages(); + HgCloneDialog dialog(m_universalCurrentDirectory); + dialog.exec(); +} + +void FileViewHgPlugin::create() +{ + clearMessages(); + HgCreateDialog dialog(m_universalCurrentDirectory); + dialog.exec(); +} + +void FileViewHgPlugin::global_config() +{ + clearMessages(); + HgConfigDialog diag(HgConfig::GlobalConfig); + diag.exec(); +} + +void FileViewHgPlugin::repo_config() +{ + clearMessages(); + HgConfigDialog diag(HgConfig::RepoConfig); + diag.exec(); +} + +void FileViewHgPlugin::push() +{ + clearMessages(); + HgPushDialog diag; + diag.exec(); +} + +void FileViewHgPlugin::pull() +{ + clearMessages(); + HgPullDialog diag; + diag.exec(); +} + +void FileViewHgPlugin::merge() +{ + clearMessages(); + HgMergeDialog diag; + diag.exec(); +} + +void FileViewHgPlugin::bundle() +{ + clearMessages(); + HgBundleDialog diag; + diag.exec(); +} + +void FileViewHgPlugin::unbundle() +{ + clearMessages(); + QString bundle = KFileDialog::getOpenFileName(); + if (bundle.isEmpty()) { + return; + } + + QStringList args; + args << bundle; + if (m_hgWrapper->executeCommandTillFinished(QLatin1String("unbundle"), args)) { + } + else { + KMessageBox::error(0, m_hgWrapper->readAllStandardError()); + } +} + +void FileViewHgPlugin::importChangesets() +{ + clearMessages(); + HgImportDialog diag; + diag.exec(); +} + +void FileViewHgPlugin::exportChangesets() +{ + clearMessages(); + HgExportDialog diag; + diag.exec(); +} + +void FileViewHgPlugin::revert() +{ + clearMessages(); + int answer = KMessageBox::questionYesNo(0, i18nc("@message:yesorno", + "Would you like to revert changes " + "made to selected files?")); + if (answer == KMessageBox::No) { + return; + } + + QString infoMsg = i18nc("@info:status", + "Reverting files in Hg repository..."); + m_errorMsg = i18nc("@info:status", + "Reverting files in Hg repository failed."); + m_operationCompletedMsg = i18nc("@info:status", + "Reverting files in Hg repository completed successfully."); + + emit infoMessage(infoMsg); + m_hgWrapper->revert(m_contextItems); +} + +void FileViewHgPlugin::revertAll() +{ + int answer = KMessageBox::questionYesNo(0, i18nc("@message:yesorno", + "Would you like to revert all changes " + "made to current working directory?")); + if (answer == KMessageBox::No) { + return; + } + + QString infoMsg = i18nc("@info:status", + "Reverting files in Hg repository..."); + m_errorMsg = i18nc("@info:status", + "Reverting files in Hg repository failed."); + m_operationCompletedMsg = i18nc("@info:status", + "Reverting files in Hg repository completed successfully."); + + emit infoMessage(infoMsg); + m_hgWrapper->revertAll(); +} + +void FileViewHgPlugin::diff() +{ + QString infoMsg = i18nc("@info:status", + "Generating diff for Hg repository..."); + m_errorMsg = i18nc("@info:status", + "Could not get Hg repository diff."); + m_operationCompletedMsg = i18nc("@info:status", + "Generated Hg diff successfully."); + + emit infoMessage(infoMsg); + + QStringList args; + args << QLatin1String("--config"); + args << QLatin1String("extensions.hgext.extdiff="); + args << QLatin1String("-p"); + args << this->visualDiffExecPath(); + + if (m_contextItems.length() == 1) { + args << m_contextItems.takeFirst().localPath(); + } + + m_hgWrapper->executeCommand(QLatin1String("extdiff"), args); +} + +void FileViewHgPlugin::serve() +{ + clearMessages(); + HgServeDialog diag; + diag.exec(); +} + +void FileViewHgPlugin::backout() +{ + clearMessages(); + m_hgWrapper = HgWrapper::instance(); + if (!m_hgWrapper->isWorkingDirectoryClean()) { + KMessageBox::error(0, i18nc("@message:error", + "abort: Uncommitted changes in working directory!")); + return; + } + + HgBackoutDialog diag; + diag.exec(); +} + +void FileViewHgPlugin::rollback() +{ + // execute a dry run rollback first to see if there is anything to + // be rolled back, or check what will be rolled back + if (!m_hgWrapper->rollback(true)) { + KMessageBox::error(0, i18nc("@info:message", "No rollback " + "information available!")); + return; + } + // get what will be rolled back + QString lastTransaction = m_hgWrapper->readAllStandardOutput(); + int cutOfFrom = lastTransaction.indexOf(QRegExp("\\d")); + lastTransaction = lastTransaction.mid(cutOfFrom); + + // ask + int answer = KMessageBox::questionYesNo(0, i18nc("@message:yesorno", + "Would you like to rollback last transaction?") + + "\nrevision: " + lastTransaction); + if (answer == KMessageBox::No) { + return; + } + + QString infoMsg = i18nc("@info:status", + "Executing Rollback Hg repository..."); + m_errorMsg = i18nc("@info:status", + "Rollback of Hg repository failed."); + m_operationCompletedMsg = i18nc("@info:status", + "Rollback of Hg repository completed successfully."); + + emit infoMessage(infoMsg); + m_hgWrapper->rollback(); + KMessageBox::information(0, m_hgWrapper->readAllStandardOutput()); + emit itemVersionsChanged(); +} + +void FileViewHgPlugin::slotOperationCompleted(int exitCode, QProcess::ExitStatus exitStatus) +{ + if ((exitStatus != QProcess::NormalExit) || (exitCode != 0)) { + emit errorMessage(m_errorMsg); + } + else { + m_contextItems.clear(); + emit operationCompletedMessage(m_operationCompletedMsg); + emit itemVersionsChanged(); + } +} + +void FileViewHgPlugin::slotOperationError() +{ + m_contextItems.clear(); + emit errorMessage(m_errorMsg); +} + +void FileViewHgPlugin::clearMessages() const +{ + m_operationCompletedMsg.clear(); + m_errorMsg.clear(); +} + +QString FileViewHgPlugin::visualDiffExecPath() +{ + KUrl url = KUrl::fromPath(QDir::homePath()); + url.addPath(".dolphin-hg"); + KConfig config(url.path(), KConfig::SimpleConfig); + + KConfigGroup group(&config, QLatin1String("diff")); + QString result = group.readEntry(QLatin1String("exec"), QString()).trimmed(); + + if (result.length() > 0) { + return result; + } + + KService::List services = KMimeTypeTrader::self()->query("text/x-diff"); + return services.first()->exec().split(' ').takeFirst(); +} + +#include "moc_fileviewhgplugin.cpp" + diff --git a/dolphin/plugins/hg/fileviewhgplugin.desktop b/dolphin/plugins/hg/fileviewhgplugin.desktop new file mode 100644 index 00000000..038eaa8b --- /dev/null +++ b/dolphin/plugins/hg/fileviewhgplugin.desktop @@ -0,0 +1,46 @@ +[Desktop Entry] +Type=Service +Name=Mercurial +Name[ar]=Mercurial +Name[bs]=Mercurial +Name[ca]=Mercurial +Name[ca@valencia]=Mercurial +Name[cs]=Mercurial +Name[da]=Mercurial +Name[de]=Mercurial +Name[el]=Mercurial +Name[en_GB]=Mercurial +Name[es]=Mercurial +Name[et]=Mercurial +Name[fi]=Mercurial +Name[fr]=Mercurial +Name[gl]=Mercurial +Name[hu]=Mercurial +Name[it]=Mercurial +Name[kk]=Mercurial +Name[ko]=Mercurial +Name[lt]=Mercurial +Name[mr]=मर्क्युरियल +Name[nb]=Mercurial +Name[nl]=Mercurial +Name[pa]=ਮਰਕਰੀਅਲ +Name[pl]=Mercurial +Name[pt]=Mercurial +Name[pt_BR]=Mercurial +Name[ro]=Mercurial +Name[ru]=Mercurial +Name[sk]=Mercurial +Name[sl]=Mercurial +Name[sr]=Меркјуријал +Name[sr@ijekavian]=Меркјуријал +Name[sr@ijekavianlatin]=Mercurial +Name[sr@latin]=Mercurial +Name[sv]=Mercurial +Name[tr]=Mercurial +Name[uk]=Mercurial +Name[x-test]=xxMercurialxx +Name[zh_CN]=Mercurial +Name[zh_TW]=Mercurial +X-KDE-ServiceTypes=FileViewVersionControlPlugin +MimeType=text/plain; +X-KDE-Library=fileviewhgplugin diff --git a/dolphin/plugins/hg/fileviewhgplugin.h b/dolphin/plugins/hg/fileviewhgplugin.h new file mode 100644 index 00000000..75906a25 --- /dev/null +++ b/dolphin/plugins/hg/fileviewhgplugin.h @@ -0,0 +1,142 @@ +/*************************************************************************** + * Copyright (C) 2011 by Vishesh Yadav * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef FILEVIEWHGPLUGIN_H +#define FILEVIEWHGPLUGIN_H + +#include "hgwrapper.h" + +#include +#include +#include +#include +#include + +class KAction; +class KMenu; + +class FileViewHgPlugin : public KVersionControlPlugin +{ + Q_OBJECT + +public: + FileViewHgPlugin(QObject *parent, const QList &args); + virtual ~FileViewHgPlugin(); + virtual QString fileName() const; + virtual bool beginRetrieval(const QString &directory); + virtual void endRetrieval(); + virtual KVersionControlPlugin::ItemVersion itemVersion(const KFileItem &item) const; + virtual QList actions(const KFileItemList &items) const; + +private: + /** + * Check if HgWrapper is created and connect some signals/slots. Created + * to ensure that HgWrapper singleton is instantiated not during + * plugin contruction hence not in other thread which ends up giving + * a lot of warnings. + */ + void createHgWrapper() const; + + /** + * Simply clear status messages ie m_errorMsg and m_operationCompletedMsg + */ + void clearMessages() const; + + /** + * Read executable file path to open diff patches with from + * $HOME/.dolphin-hg file in INI format + */ + QString visualDiffExecPath(); + + QList itemContextMenu(const KFileItemList &items) const; + QList directoryContextMenu(const QString &directory) const; + QList universalContextMenuActions(const QString &directory) const; + +private slots: + void addFiles(); + void removeFiles(); + void renameFile(); + void commit(); + void branch(); + void tag(); + void update(); + void clone(); + void create(); + void global_config(); + void repo_config(); + void push(); + void pull(); + void revert(); + void revertAll(); + void rollback(); + void backout(); + void diff(); + void serve(); + void merge(); + void bundle(); + void unbundle(); + void exportChangesets(); + void importChangesets(); + + void slotOperationCompleted(int exitCode, QProcess::ExitStatus exitStatus); + void slotOperationError(); + +private: + QHash m_versionInfoHash; + + KMenu *m_mainContextMenu; + + KAction *m_menuAction; + KAction *m_addAction; + KAction *m_removeAction; + KAction *m_renameAction; + KAction *m_commitAction; + KAction *m_branchAction; + KAction *m_tagAction; + KAction *m_updateAction; + KAction *m_cloneAction; + KAction *m_createAction; + KAction *m_globalConfigAction; + KAction *m_repoConfigAction; + KAction *m_pushAction; + KAction *m_pullAction; + KAction *m_revertAction; + KAction *m_revertAllAction; + KAction *m_rollbackAction; + KAction *m_mergeAction; + KAction *m_bundleAction; + KAction *m_exportAction; + KAction *m_unbundleAction; + KAction *m_importAction; + KAction *m_diffAction; + KAction *m_serveAction; + KAction *m_backoutAction; + + mutable KFileItemList m_contextItems; + mutable QString m_universalCurrentDirectory; + mutable QString m_currentDir; + + mutable QString m_operationCompletedMsg; + mutable QString m_errorMsg; + mutable HgWrapper *m_hgWrapper; + HgWrapper *m_retrievalHgw; +}; + +#endif // FILEVIEWHGPLUGIN_H + diff --git a/dolphin/plugins/hg/fileviewhgpluginsettings.kcfg b/dolphin/plugins/hg/fileviewhgpluginsettings.kcfg new file mode 100644 index 00000000..d36ef2f9 --- /dev/null +++ b/dolphin/plugins/hg/fileviewhgpluginsettings.kcfg @@ -0,0 +1,120 @@ + + + + + + + + 550 + 400 + + + + 900 + 500 + + + + + + 550 + 400 + + + + 900 + 500 + + + + + + 550 + + + + 900 + + + + + + 550 + + + + 900 + + + + + + 550 + + + + 900 + + + + + + 550 + + + + 900 + + + + + + 550 + + + + 900 + + + + + + 550 + + + + 900 + + + + + + 540 + + + + 500 + + + + + + 600 + + + + 300 + + + + + + 346 + + + + 537 + + + + diff --git a/dolphin/plugins/hg/fileviewhgpluginsettings.kcfgc b/dolphin/plugins/hg/fileviewhgpluginsettings.kcfgc new file mode 100644 index 00000000..533971b8 --- /dev/null +++ b/dolphin/plugins/hg/fileviewhgpluginsettings.kcfgc @@ -0,0 +1,4 @@ +File=fileviewhgpluginsettings.kcfg +ClassName=FileViewHgPluginSettings +Singleton=true +Mutators=true diff --git a/dolphin/plugins/hg/hgconfig.cpp b/dolphin/plugins/hg/hgconfig.cpp new file mode 100644 index 00000000..18a79c27 --- /dev/null +++ b/dolphin/plugins/hg/hgconfig.cpp @@ -0,0 +1,158 @@ +/*************************************************************************** + * Copyright (C) 2011 by Vishesh Yadav * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "hgconfig.h" +#include "hgwrapper.h" + +#include +#include +#include +#include +#include + +HgConfig::HgConfig(ConfigType configType) : + m_configType(configType), + m_config(0) +{ + getConfigFilePath(); + loadConfig(); +} + +HgConfig::~HgConfig() +{ + delete m_config; +} + +QString HgConfig::configFilePath() const +{ + return m_configFilePath; +} + +bool HgConfig::getConfigFilePath() +{ + switch (m_configType) { + case RepoConfig: + { + KUrl repoBase = KUrl(HgWrapper::instance()->getBaseDir()); + repoBase.addPath(QLatin1String(".hg/hgrc")); + m_configFilePath = repoBase.path(); + break; + } + case GlobalConfig: + { + KUrl homeUrl = KUrl(QDir::homePath()); + homeUrl.addPath(QLatin1String(".hgrc")); + m_configFilePath = homeUrl.path(); + break; + } + case TempConfig: + break; + } + return true; +} + +bool HgConfig::loadConfig() +{ + m_config = new KConfig(m_configFilePath, KConfig::SimpleConfig); + return true; +} + +QString HgConfig::property(const QString §ion, + const QString &propertyName) const +{ + KConfigGroup group(m_config, section); + return group.readEntry(propertyName, QString()).trimmed(); +} + +void HgConfig::setProperty(const QString §ion, + const QString &propertyName, + const QString &propertyValue) +{ + KConfigGroup uiGroup(m_config, section); + if (propertyValue.isEmpty()) { + uiGroup.deleteEntry(propertyName, KConfigGroup::Normal); + return; + } + uiGroup.writeEntry(propertyName, propertyValue.trimmed()); +} + +/* User Interface [ui] Section */ + +QString HgConfig::username() const +{ + return property(QLatin1String("ui"), QLatin1String("username")); +} + +void HgConfig::setUsername(const QString &userName) +{ + setProperty(QLatin1String("ui"), QLatin1String("username"), userName); +} + +QString HgConfig::editor() const +{ + return property(QLatin1String("ui"), QLatin1String("editor")); +} + +void HgConfig::setEditor(const QString &pathToEditor) +{ + setProperty(QLatin1String("ui"), QLatin1String("editor"), pathToEditor); +} + +QString HgConfig::merge() const +{ + return property(QLatin1String("ui"), QLatin1String("merge")); +} + +void HgConfig::setMerge(const QString &pathToMergeTool) +{ + setProperty(QLatin1String("ui"), QLatin1String("merge"), pathToMergeTool); +} + +/* Repo specific */ + +void HgConfig::setRepoRemotePath(const QString &alias, const QString &url) +{ + Q_ASSERT(m_configType == RepoConfig); + setProperty(QLatin1String("paths"), alias, url); +} + +void HgConfig::deleteRepoRemotePath(const QString &alias) +{ + Q_ASSERT(m_configType == RepoConfig); + + KConfigGroup group(m_config, QLatin1String("paths")); + group.deleteEntry(alias); +} + +QString HgConfig::repoRemotePath(const QString &alias) const +{ + Q_ASSERT(m_configType == RepoConfig); + return property(QLatin1String("paths"), alias); +} + +QMap HgConfig::repoRemotePathList() const +{ + Q_ASSERT(m_configType == RepoConfig); + + KConfigGroup group(m_config, QLatin1String("paths")); + return group.entryMap(); +} + +// + diff --git a/dolphin/plugins/hg/hgconfig.h b/dolphin/plugins/hg/hgconfig.h new file mode 100644 index 00000000..5f20ba2c --- /dev/null +++ b/dolphin/plugins/hg/hgconfig.h @@ -0,0 +1,132 @@ +/*************************************************************************** + * Copyright (C) 2011 by Vishesh Yadav * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef HGCONFIG_H +#define HGCONFIG_H + +#include +#include +#include + +class KConfig; + +/** + * Provides a wrapper to read and write Mercurial settings in hgrc files. + * Also provides interface to read/write other settings as well, for which + * a direct function is not provided. + */ +class HgConfig +{ +public: + /** + * Type of hgrc file. + * + * RepoConfig: hgrc of the local/current repository. + * GlobalConfig: Global hgrc file. Under *nix, ~/.hgrc file + * TempConfig: A temporary config file, that can be used by some commands + * for just one time. Usually after taking input from user to + * override default settings. + */ + enum ConfigType { + RepoConfig, GlobalConfig, TempConfig + }; + + HgConfig(ConfigType configFile); + ~HgConfig(); + + // Related to config file + + /** + * Return the path of hgrc file currently loaded + */ + QString configFilePath() const; + + // Repo specific + + /** + * Set path of repository associated with given given alias + */ + void setRepoRemotePath(const QString &alias, const QString &url); + + /** + * Delete alias/path of repository associated with given given alias + */ + void deleteRepoRemotePath(const QString &alias); + + /** + * Return path of repository associated with given given alias + */ + QString repoRemotePath(const QString &alias) const; + + /** + * Return a list of alias-path pair of remote repository paths + */ + QMap repoRemotePathList() const; + + /////////// + + /** + * Get the value of a property in a given section. + * + * @param section The settings group in hgrc file. + * @param propertyName Name of the property in section whose value is\ + * required + * @return Value of property in the section given. + */ + QString property(const QString §ion, const QString &propertyName)const; + + /** + * Set the value of a property in a given section. + * + * @param section The settings group in hgrc file. + * @param propertyName Name of the property in section whose value is + * to be modified. + * @param propertyValue Value to be set of given propery. Deletes the + * entry if empty. + */ + void setProperty(const QString §ion, const QString &propertyName, + const QString &propertyValue); + + // user interface section + QString username() const; + void setUsername(const QString &userName); + + QString editor() const; + void setEditor(const QString &pathToEditor); + + QString merge() const; + void setMerge(const QString &pathToMergeTool); + +private: + /** + * Used during construction to determine the path of config files + * based on given ConfigType enum + */ + bool getConfigFilePath(); + bool loadConfig(); + +private: + ConfigType m_configType; + QString m_configFilePath; + KConfig *m_config; + +}; + +#endif //HGCONFIG_H + diff --git a/dolphin/plugins/hg/hgwrapper.cpp b/dolphin/plugins/hg/hgwrapper.cpp new file mode 100644 index 00000000..f084bdf9 --- /dev/null +++ b/dolphin/plugins/hg/hgwrapper.cpp @@ -0,0 +1,410 @@ +/*************************************************************************** + * Copyright (C) 2011 by Vishesh Yadav * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "hgwrapper.h" + +#include +#include +#include +#include + +//TODO: Replace start() with executeCommand functions wherever possible. +//FIXME: Add/Remove/Revert argument length limit. Divide the list. +//FIXME: Cannot create thread for parent that is in different thread. + +HgWrapper *HgWrapper::m_instance = 0; + +HgWrapper::HgWrapper(QObject *parent) : + QObject(parent) +{ + m_localCodec = QTextCodec::codecForLocale(); + + // re-emit QProcess signals + connect(&m_process, SIGNAL(error(QProcess::ProcessError)), + this, SIGNAL(error(QProcess::ProcessError))); + connect(&m_process, SIGNAL(finished(int, QProcess::ExitStatus)), + this, SIGNAL(finished(int, QProcess::ExitStatus))), + connect(&m_process, SIGNAL(stateChanged(QProcess::ProcessState)), + this, SIGNAL(stateChanged(QProcess::ProcessState))); + connect(&m_process, SIGNAL(started()), + this, SIGNAL(started())); + + connect(&m_process, SIGNAL(finished(int, QProcess::ExitStatus)), + this, SLOT(slotOperationCompleted(int, QProcess::ExitStatus))); + connect(&m_process, SIGNAL(error(QProcess::ProcessError)), + this, SLOT(slotOperationError(QProcess::ProcessError))); + +} + +HgWrapper *HgWrapper::instance() +{ + if (!m_instance) { + m_instance = new HgWrapper; + } + return m_instance; +} + +void HgWrapper::freeInstance() +{ + delete m_instance; + m_instance = 0; +} + +void HgWrapper::slotOperationCompleted(int exitCode, + QProcess::ExitStatus exitStatus) +{ + kDebug() << "'hg' Exit Code: " << exitCode << " Exit Status: " + << exitStatus; + if (m_primaryOperation) { + emit primaryOperationFinished(exitCode, exitStatus); + } +} + +void HgWrapper::slotOperationError(QProcess::ProcessError error) +{ + kDebug() << "Error occurred while executing 'hg' with arguments "; + if (m_primaryOperation) { + emit primaryOperationError(error); + } +} + +bool HgWrapper::executeCommand(const QString &hgCommand, + const QStringList &arguments, + QString &output, + bool primaryOperation) +{ + Q_ASSERT(m_process.state() == QProcess::NotRunning); + + executeCommand(hgCommand, arguments, primaryOperation); + m_process.waitForFinished(); + output = QTextCodec::codecForLocale()->toUnicode(m_process.readAllStandardOutput()); + + return (m_process.exitStatus() == QProcess::NormalExit && + m_process.exitCode() == 0); +} + +void HgWrapper::executeCommand(const QString &hgCommand, + const QStringList &arguments, + bool primaryOperation) +{ + Q_ASSERT(m_process.state() == QProcess::NotRunning); + + m_primaryOperation = primaryOperation; + if (m_primaryOperation) { + kDebug() << "Primary operation"; + } + + QStringList args; + args << hgCommand; + args << arguments; + m_process.setWorkingDirectory(m_currentDir); + m_process.start(QLatin1String("hg"), args); +} + +bool HgWrapper::executeCommandTillFinished(const QString &hgCommand, + const QStringList &arguments, + bool primaryOperation) +{ + Q_ASSERT(m_process.state() == QProcess::NotRunning); + + m_primaryOperation = primaryOperation; + + QStringList args; + args << hgCommand; + args << arguments; + m_process.setWorkingDirectory(m_currentDir); + m_process.start(QLatin1String("hg"), args); + m_process.waitForFinished(); + + return (m_process.exitStatus() == QProcess::NormalExit && + m_process.exitCode() == 0); +} + +QString HgWrapper::getBaseDir() const +{ + return m_hgBaseDir; +} + +QString HgWrapper::getCurrentDir() const +{ + return m_currentDir; +} + +void HgWrapper::updateBaseDir() +{ + m_process.setWorkingDirectory(m_currentDir); + m_process.start(QLatin1String("hg root")); + m_process.waitForFinished(); + m_hgBaseDir = QString(m_process.readAllStandardOutput()).trimmed(); +} + +void HgWrapper::setCurrentDir(const QString &directory) +{ + m_currentDir = directory; + updateBaseDir(); //now get root directory of repository +} + +void HgWrapper::setBaseAsWorkingDir() +{ + m_process.setWorkingDirectory(getBaseDir()); +} + +void HgWrapper::addFiles(const KFileItemList &fileList) +{ + Q_ASSERT(m_process.state() == QProcess::NotRunning); + + QStringList args; + args << QLatin1String("add"); + foreach (const KFileItem &item, fileList) { + args << item.localPath(); + } + m_process.start(QLatin1String("hg"), args); +} + +bool HgWrapper::renameFile(const QString &source, const QString &destination) +{ + Q_ASSERT(m_process.state() == QProcess::NotRunning); + + QStringList args; + args << source << destination; + executeCommand(QLatin1String("rename"), args, true); + + m_process.waitForFinished(); + return (m_process.exitStatus() == QProcess::NormalExit && + m_process.exitCode() == 0); +} + +void HgWrapper::removeFiles(const KFileItemList &fileList) +{ + Q_ASSERT(m_process.state() == QProcess::NotRunning); + + QStringList args; + args << QLatin1String("remove"); + args << QLatin1String("--force"); + foreach (const KFileItem &item, fileList) { + args << item.localPath(); + } + m_process.start(QLatin1String("hg"), args); +} + +bool HgWrapper::commit(const QString &message, const QStringList &files, + bool closeCurrentBranch) +{ + QStringList args; + args << files; + args << QLatin1String("-m") << message; + if (closeCurrentBranch) { + args << "--close-branch"; + } + executeCommand(QLatin1String("commit"), args, true); + m_process.waitForFinished(); + return (m_process.exitCode() == 0 && + m_process.exitStatus() == QProcess::NormalExit); +} + +bool HgWrapper::createBranch(const QString &name) +{ + QStringList args; + args << name; + executeCommand(QLatin1String("branch"), args, true); + m_process.waitForFinished(); + return (m_process.exitCode() == 0 && + m_process.exitStatus() == QProcess::NormalExit); +} + +bool HgWrapper::switchBranch(const QString &name) +{ + QStringList args; + args << QLatin1String("-c") << name; + executeCommand(QLatin1String("update"), args, true); + m_process.waitForFinished(); + return (m_process.exitCode() == 0 && + m_process.exitStatus() == QProcess::NormalExit); +} + +bool HgWrapper::createTag(const QString &name) +{ + QStringList args; + args << name; + executeCommand(QLatin1String("tag"), args, true); + m_process.waitForFinished(); + return (m_process.exitCode() == 0 && + m_process.exitStatus() == QProcess::NormalExit); +} + +bool HgWrapper::revertAll() +{ + QStringList args; + args << "--all"; + return executeCommandTillFinished(QLatin1String("revert"), args, true); +} + + +bool HgWrapper::revert(const KFileItemList &fileList) +{ + QStringList arguments; + foreach (const KFileItem &item, fileList) { + arguments << item.localPath(); + } + return executeCommandTillFinished(QLatin1String("revert"), arguments, true); +} + +bool HgWrapper::rollback(bool dryRun) +{ + QStringList args; + if (dryRun) { + args << QLatin1String("-n"); + } + return executeCommandTillFinished(QLatin1String("rollback"), args, true); +} + +bool HgWrapper::switchTag(const QString &name) +{ + QStringList args; + args << QLatin1String("-c") << name; + executeCommand(QLatin1String("update"), args, true); + m_process.waitForFinished(); + return (m_process.exitCode() == 0 && + m_process.exitStatus() == QProcess::NormalExit); +} + +//TODO: Make it return QStringList. +QString HgWrapper::getParentsOfHead() +{ + Q_ASSERT(m_process.state() == QProcess::NotRunning); + + QString output; + QStringList args; + args << QLatin1String("--template"); + args << QLatin1String("{rev}:{node|short} "); + executeCommand(QLatin1String("parents"), args, output); + return output; +} + +QStringList HgWrapper::getTags() +{ + QStringList result; + executeCommand(QLatin1String("tags")); + while (m_process.waitForReadyRead()) { + char buffer[1048]; + while (m_process.readLine(buffer, sizeof(buffer)) > 0) { + result << QString(buffer).split(QRegExp("\\s+"), + QString::SkipEmptyParts).first(); + } + } + return result; +} + +QStringList HgWrapper::getBranches() +{ + QStringList result; + executeCommand(QLatin1String("branches")); + while (m_process.waitForReadyRead()) { + char buffer[1048]; + while (m_process.readLine(buffer, sizeof(buffer)) > 0) { + // 'hg branches' command lists the branches in following format + // [(inactive)] + // Extract just the branchname + result << QString(buffer).remove(QRegExp("[\\s]+[\\d:a-zA-Z\\(\\)]*")); + } + } + return result; +} + +void HgWrapper::getItemVersions(QHash &result) +{ + /*int nTrimOutLeft = m_hgBaseDir.length(); + QString relativePrefix = m_currentDir.right(m_currentDir.length() - + nTrimOutLeft - 1); + kDebug() << m_hgBaseDir << " " << relativePrefix;*/ + + // Get status of files + QStringList args; + args << QLatin1String("status"); + args << QLatin1String("--modified"); + args << QLatin1String("--added"); + args << QLatin1String("--removed"); + args << QLatin1String("--deleted"); + args << QLatin1String("--unknown"); + args << QLatin1String("--ignored"); + m_process.setWorkingDirectory(m_currentDir); + m_process.start(QLatin1String("hg"), args); + while (m_process.waitForReadyRead()) { + char buffer[1024]; + while (m_process.readLine(buffer, sizeof(buffer)) > 0) { + const QString currentLine(QTextCodec::codecForLocale()->toUnicode(buffer).trimmed()); + char currentStatus = buffer[0]; + QString currentFile = currentLine.mid(2); + KVersionControlPlugin::ItemVersion vs = KVersionControlPlugin::NormalVersion; + switch (currentStatus) { + case 'A': + vs = KVersionControlPlugin::AddedVersion; + break; + case 'M': + vs = KVersionControlPlugin::LocallyModifiedVersion; + break; + case '?': + vs = KVersionControlPlugin::UnversionedVersion; + break; + case 'R': + vs = KVersionControlPlugin::RemovedVersion; + break; + case 'I': + vs = KVersionControlPlugin::IgnoredVersion; + break; + case 'C': + vs = KVersionControlPlugin::NormalVersion; + break; + case '!': + vs = KVersionControlPlugin::MissingVersion; + break; + } + if (vs != KVersionControlPlugin::NormalVersion) { + // Get full path to file and insert it to result + KUrl url = KUrl::fromPath(m_hgBaseDir); + url.addPath(currentFile); + QString filePath = url.path(); + result.insert(filePath, vs); + } + } + } +} + +void HgWrapper::terminateCurrentProcess() +{ + kDebug() << "terminating"; + m_process.terminate(); +} + +bool HgWrapper::isWorkingDirectoryClean() +{ + QStringList args; + args << QLatin1String("--modified"); + args << QLatin1String("--added"); + args << QLatin1String("--removed"); + args << QLatin1String("--deleted"); + + QString output; + executeCommand(QLatin1String("status"), args, output); + + return output.trimmed().isEmpty(); +} + +#include "moc_hgwrapper.cpp" + diff --git a/dolphin/plugins/hg/hgwrapper.h b/dolphin/plugins/hg/hgwrapper.h new file mode 100644 index 00000000..9a5f4967 --- /dev/null +++ b/dolphin/plugins/hg/hgwrapper.h @@ -0,0 +1,279 @@ +/*************************************************************************** + * Copyright (C) 2011 by Vishesh Yadav * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef HGWRAPPER_H +#define HGWRAPPER_H + + +#include +#include +#include +#include +#include +#include + +#include + + +//TODO: Create signals for infoMessage and errorMessage which will be +// caught by main plugin interface. + +/** + * A singleton class providing implementation of many Mercurial commands + */ +class HgWrapper : public QObject +{ + Q_OBJECT +public: + HgWrapper(QObject *parent = 0); + + static HgWrapper *instance(); + static void freeInstance(); + + /** + * Start a mercurial command with given arguments. + * + * @param hgCommand Command to be executed. eg. diff, status + * @param arguments Arguments for the given command + * @param primaryOperation Will emit primaryOperationFinished, + * primaryOperationError signals. + */ + void executeCommand(const QString &hgCommand, + const QStringList &arguments = QStringList(), + bool primaryOperation=false); + /** + * Start a mercurial command with given arguments and return until + * process completes. + * + * @param hgCommand Command to be executed. eg. diff, status + * @param arguments Arguments for the given command + * @param primaryOperation Will emit primaryOperationCompleted, + * primaryOperationError signals. + * @return true if operations completed successfully, otherwise false + */ + bool executeCommandTillFinished(const QString &hgCommand, + const QStringList &arguments = QStringList(), + bool primaryOperation=false); + /** + * Start a mercurial command with given arguments, write standard output + * to output parameter and return till finished. + * + * @param hgCommand Command to be executed. eg. diff, status + * @param arguments Arguments for the given command + * @param output Append standard output of process to this string + * @param primaryOperation Will emit primaryOperationCompleted, + * primaryOperationError signals. + * @return true if operations completed successfully, otherwise false + */ + bool executeCommand(const QString &hgCommand, + const QStringList &arguments, + QString &output, + bool primaryOperation=false); + + /** + * Get the root directory of Mercurial repository. Using 'hg root' + * + * @return String containing path of the root directory. + */ + QString getBaseDir() const; + + /** + * Sets the current directory being browsed with plugin enabled. + * Updates base directory of repository accordingly + */ + void setCurrentDir(const QString &directory); + + /** + * Get the directory path that is currently used as the working directory + * to execute the commands by the HgWrapper. + */ + QString getCurrentDir() const; + + /** + * Set the root directory of repository as working directory. + */ + void setBaseAsWorkingDir(); + + /** + * Get FileName-ItemVersion pairs of the repository returned by + * + * $hg status --modified --added --removed --deleted --unknown --ignored + * + * Hence returns files with ItemVersion + * - LocallyModifiedVersion + * - AddedVersion + * - RemovedVersion + * - RemovedVersion + * - UnversionedVersion + * - IgnoredVersion + * - MissingVersion + * + * @param result A hashmap containing FileName-ItemVersion pairs + * + */ + void getItemVersions(QHash &result); + + void addFiles(const KFileItemList &fileList); + void removeFiles(const KFileItemList &fileList); + bool renameFile(const QString &source, const QString &destination); + + /** + * Commits changes made to the working directory. + * @param message Commit message. Should not be empty. + * @param files List of files to be committed. Files changed but not + * listed here will be ignored during commit. + * If the list is empty, all modified files will be + * committed, the deault behovior. + * @param closeCurrentBranch Closes the current branch after commit. + * @return true if successful, otherwise false + */ + bool commit(const QString &message, + const QStringList &files = QStringList(), + bool closeCurrentBranch = false); + + /** + * Create a new branch + * @param name Name of new branch to be createdialog + * @return true if successfully created, otherwise false + */ + bool createBranch(const QString &name); + + /** + * Update current working directory to another branch + * @param name Name of the branch to which working directory + * has to be updated. + * @return true if successful, otherwise false + */ + bool switchBranch(const QString &name); + + /** + * Create tag for current changeset(the changeset of working directory) + * @param name Name of the new tag to be createdialog + * @return true if successful, otherwise false + */ + bool createTag(const QString &name); + + /** + * Update working directory to a changeset named by given tag + * @param name Tag of the changeset to which working directory + * has to be updated. + * @return true if successful, otherwise false + */ + bool switchTag(const QString &name); + + /** + * Reverts all local changes made to working directory. Will update to + * last changeset of current branch, ie state just after last commit. + */ + bool revertAll(); + + /** + * Reverts local changes made to selected files. All changes made to + * these files after last commit will be lost. + */ + bool revert(const KFileItemList &fileList); + + /** + * Undo's last transaction. Like commit, pull, push(to this repo). + * Does not alter working directory. + * + * Use with care. Rollback cant be undone. See Mercurial man page FOR + * more info. + * + * @param dryRun Do not actually perform action, but just print output + * Used to check if Rollback can be done, and if yes then + * what will be rolled back. + * @return true if successful, otherwise false + */ + bool rollback(bool dryRun=false); + + /** + * Checks if the working directory is clean, ie there are no + * uncommitted changes present. + * + * @return true if clean otherwise false + */ + bool isWorkingDirectoryClean(); + + QString getParentsOfHead(); + + /** + * Returns list of all branch names. + */ + QStringList getBranches(); + + /** + * Returns list of all tags + */ + QStringList getTags(); + + inline QString readAllStandardOutput() { + return QTextCodec::codecForLocale()->toUnicode(m_process.readAllStandardOutput()); + } + + inline QString readAllStandardError() { + return QTextCodec::codecForLocale()->toUnicode(m_process.readAllStandardError()); + } + + /** + * Check if some Mercurial operation is currently being executed or + * about to be started. + */ + inline bool isBusy() { + return (m_process.state() == QProcess::Running || + m_process.state() == QProcess::Starting); + } + +public slots: + /** + * Try to terminate the currently running operation. + */ + void terminateCurrentProcess(); + +signals: + ///equivalent to the signals of QProcess + void finished(int exitCode, QProcess::ExitStatus exitStatus); + void error(QProcess::ProcessError error); + void started(); + void stateChanged(QProcess::ProcessState state); + void primaryOperationFinished(int exitCode, QProcess::ExitStatus exitStatus); + void primaryOperationError(QProcess::ProcessError error); + +private: + ///Get and update m_hgBaseDir + void updateBaseDir(); + +private slots: + void slotOperationCompleted(int exitCode, QProcess::ExitStatus exitStatus); + void slotOperationError(QProcess::ProcessError error); + +private: + static HgWrapper *m_instance; + + QProcess m_process; + QTextCodec *m_localCodec; + + QString m_hgBaseDir; + QString m_currentDir; + + bool m_primaryOperation; // to differentiate intermediate process +}; + +#endif // HGWRAPPER_H + diff --git a/dolphin/plugins/hg/importdialog.cpp b/dolphin/plugins/hg/importdialog.cpp new file mode 100644 index 00000000..b3b9934d --- /dev/null +++ b/dolphin/plugins/hg/importdialog.cpp @@ -0,0 +1,221 @@ +/*************************************************************************** + * Copyright (C) 2011 by Vishesh Yadav * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "importdialog.h" +#include "fileviewhgpluginsettings.h" +#include "commititemdelegate.h" +#include "hgwrapper.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +HgImportDialog::HgImportDialog(QWidget *parent) : + KDialog(parent, Qt::Dialog) +{ + // dialog properties + this->setCaption(i18nc("@title:window", + "Hg Import")); + this->setButtons(KDialog::Ok | KDialog::Cancel); + this->setDefaultButton(KDialog::Ok); + this->setButtonText(KDialog::Ok, i18nc("@action:button", "Import")); + + // + setupUI(); + + // Load saved settings + FileViewHgPluginSettings *settings = FileViewHgPluginSettings::self(); + this->setInitialSize(QSize(settings->importDialogWidth(), + settings->importDialogHeight())); + + // + connect(this, SIGNAL(finished()), this, SLOT(saveGeometry())); + connect(m_addPatches, SIGNAL(clicked()), + this, SLOT(slotAddPatches())); + connect(m_removePatches, SIGNAL(clicked()), + this, SLOT(slotRemovePatches())); +} + +void HgImportDialog::setupUI() +{ + QGroupBox *mainGroup = new QGroupBox; + QGridLayout *mainLayout = new QGridLayout; + m_patchList = new QListWidget; + + m_patchList->setSelectionMode(QAbstractItemView::ExtendedSelection); + m_patchList->setItemDelegate(new CommitItemDelegate); + mainLayout->addWidget(m_patchList); + mainGroup->setLayout(mainLayout); + + // options + m_optionGroup = new QGroupBox(i18nc("@label:group", "Options")); + m_optNoCommit = new QCheckBox(i18nc("@label", + "Do not commit, just update the working directory")); + m_optForce = new QCheckBox(i18nc("@label", + "Skip test for outstanding uncommitted changes")); + m_optExact = new QCheckBox(i18nc("@label", + "Apply patch to the nodes from which it was generated")); + m_optBypass = new QCheckBox(i18nc("@label", + "Apply patch without touching working directory")); + + QVBoxLayout *optionLayout = new QVBoxLayout; + optionLayout->addWidget(m_optNoCommit); + optionLayout->addWidget(m_optForce); + optionLayout->addWidget(m_optExact); + optionLayout->addWidget(m_optBypass); + m_optionGroup->setLayout(optionLayout); + + // top buttons + QHBoxLayout *topButtons = new QHBoxLayout; + m_addPatches = new KPushButton(i18nc("@label:button", + "Add Patches")); + m_removePatches = new KPushButton(i18nc("@label:button", + "Remove Patches")); + topButtons->addWidget(m_addPatches); + topButtons->addWidget(m_removePatches); + topButtons->addStretch(); + + //setup main dialog widget + QWidget *widget = new QWidget; + QVBoxLayout *layout = new QVBoxLayout; + layout->addLayout(topButtons); + layout->addWidget(mainGroup); + layout->addWidget(m_optionGroup); + widget->setLayout(layout); + setMainWidget(widget); +} + +void HgImportDialog::saveGeometry() +{ + FileViewHgPluginSettings *settings = FileViewHgPluginSettings::self(); + settings->setImportDialogHeight(this->height()); + settings->setImportDialogWidth(this->width()); + settings->writeConfig(); +} + +void HgImportDialog::done(int r) +{ + if (r == KDialog::Accepted) { + QStringList args; + if (m_optForce->checkState() == Qt::Checked) { + args << QLatin1String("--force"); + } + + if (m_optBypass->checkState() == Qt::Checked) { + args << QLatin1String("--bypass"); + } + + if (m_optNoCommit->checkState() == Qt::Checked) { + args << QLatin1String("--no-commit"); + } + if (m_optExact->checkState() == Qt::Checked) { + args << QLatin1String("--exact"); + } + + int countRows = m_patchList->count(); + for (int i=0; iitem(i); + args << item->data(Qt::UserRole + 5).toString(); + } + + HgWrapper *hgw = HgWrapper::instance(); + if (hgw->executeCommandTillFinished(QLatin1String("import"), args)) { + KDialog::done(r); + } + else { + KMessageBox::error(this, hgw->readAllStandardError()); + } + } + else { + KDialog::done(r); + } +} + +void HgImportDialog::getPatchInfo(const QString &fileName) +{ + QFile file(fileName); + file.open(QFile::ReadOnly); + QTextStream fileStream(&file); + + QListWidgetItem *item = new QListWidgetItem; + item->setData(Qt::UserRole + 1, QString()); + item->setData(Qt::UserRole + 2, QString()); + item->setData(Qt::UserRole + 5, fileName); + + bool gotInfo = false; + + do { + QString line = fileStream.readLine(); + if (line.startsWith(QLatin1String("diff"))) { + break; + } + else if (line.startsWith(QLatin1String("# User"))) { + item->setData(Qt::UserRole + 3, + line.remove(QLatin1String("# User")).trimmed()); + } + else if (line.startsWith(QLatin1String("# Node ID"))) { + QString node = line.remove(QLatin1String("# Node ID")).trimmed(); + if (!m_patchList->findItems(node, Qt::MatchExactly).empty()) { + return; + } + item->setData(Qt::DisplayRole, node); + } + else if (line.startsWith(QLatin1String("# Parent"))) { + gotInfo = true; + } + else if (gotInfo) { + item->setData(Qt::UserRole + 4, line.trimmed()); + break; + } + } while (!fileStream.atEnd()); + + m_patchList->addItem(item); + + file.close(); +} + +void HgImportDialog::slotAddPatches() +{ + QStringList patches = KFileDialog::getOpenFileNames(); + foreach (QString fileName, patches) { + getPatchInfo(fileName); + } +} + +void HgImportDialog::slotRemovePatches() +{ + int count = m_patchList->count(); + for (int i=0; itakeItem(i); + } +} + +#include "moc_importdialog.cpp" + diff --git a/dolphin/plugins/hg/importdialog.h b/dolphin/plugins/hg/importdialog.h new file mode 100644 index 00000000..b4c62cba --- /dev/null +++ b/dolphin/plugins/hg/importdialog.h @@ -0,0 +1,67 @@ +/*************************************************************************** + * Copyright (C) 2011 by Vishesh Yadav * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef HGIMPORTDIALOG_H +#define HGIMPORTDIALOG_H + +#include + +#include +#include +#include +class KLineEdit; + +/** + * Implemets dialog to import changesets from patch files generated by + * Mercurial's export command + */ +class HgImportDialog : public KDialog +{ + Q_OBJECT + +public: + HgImportDialog(QWidget *parent=0); + +public slots: + void done(int r); + +private slots: + void saveGeometry(); + void slotAddPatches(); + void slotRemovePatches(); + +private: + void setupUI(); + void getPatchInfo(const QString &fileName); + +private: + QListWidget *m_patchList; + KPushButton *m_addPatches; + KPushButton *m_removePatches; + + //options + QGroupBox *m_optionGroup; + QCheckBox *m_optNoCommit; + QCheckBox *m_optBypass; + QCheckBox *m_optExact; + QCheckBox *m_optForce; +}; + +#endif /* HGIMPORTDIALOG_H */ + diff --git a/dolphin/plugins/hg/mergedialog.cpp b/dolphin/plugins/hg/mergedialog.cpp new file mode 100644 index 00000000..878c8a87 --- /dev/null +++ b/dolphin/plugins/hg/mergedialog.cpp @@ -0,0 +1,162 @@ +/*************************************************************************** + * Copyright (C) 2011 by Vishesh Yadav * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "mergedialog.h" +#include "hgwrapper.h" +#include "commititemdelegate.h" +#include "commitinfowidget.h" +#include "fileviewhgpluginsettings.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +HgMergeDialog::HgMergeDialog(QWidget *parent): + KDialog(parent, Qt::Dialog) +{ + // dialog properties + this->setCaption(i18nc("@title:window", + "Hg Merge")); + this->setButtons(KDialog::Ok | KDialog::Cancel); + this->setButtonText(KDialog::Ok, i18nc("@label:button", "Merge")); + + // UI + + m_currentChangeset = new QLabel; + m_commitInfoWidget = new HgCommitInfoWidget; + + QVBoxLayout *vbox = new QVBoxLayout; + vbox->addWidget(m_currentChangeset); + vbox->addWidget(m_commitInfoWidget); + + QWidget *widget = new QWidget; + widget->setLayout(vbox); + setMainWidget(widget); + + updateInitialDialog(); + + // load saved geometry + FileViewHgPluginSettings *settings = FileViewHgPluginSettings::self(); + this->setInitialSize(QSize(settings->mergeDialogWidth(), + settings->mergeDialogHeight())); + + // connections + connect(this, SIGNAL(finished()), this, SLOT(saveGeometry())); +} + +void HgMergeDialog::updateInitialDialog() +{ + HgWrapper *hgWrapper = HgWrapper::instance(); + + // update label - current branch + QString line("parents: "); + line += hgWrapper->getParentsOfHead(); + m_currentChangeset->setText(line); + + // update heads list + QProcess process; + process.setWorkingDirectory(hgWrapper->getBaseDir()); + + QStringList args; + args << QLatin1String("heads"); + args << QLatin1String("--template"); + args << QLatin1String("{rev}\n{node|short}\n{branch}\n" + "{author}\n{desc|firstline}\n"); + + process.start(QLatin1String("hg"), args); + m_commitInfoWidget->clear(); + + const int FINAL = 5; + char buffer[FINAL][1024]; + int count = 0; + while (process.waitForReadyRead()) { + while (process.readLine(buffer[count], sizeof(buffer[count])) > 0) { + if (count == FINAL - 1) { + QString rev = QTextCodec::codecForLocale()->toUnicode(buffer[0]).trimmed(); + QString changeset = QTextCodec::codecForLocale()->toUnicode(buffer[1]).trimmed(); + QString branch = QTextCodec::codecForLocale()->toUnicode(buffer[2]).trimmed(); + QString author = QTextCodec::codecForLocale()->toUnicode(buffer[3]).trimmed(); + QString log = QTextCodec::codecForLocale()->toUnicode(buffer[4]).trimmed(); + + QListWidgetItem *item = new QListWidgetItem; + item->setData(Qt::DisplayRole, changeset); + item->setData(Qt::UserRole + 1, rev); + item->setData(Qt::UserRole + 2, branch); + item->setData(Qt::UserRole + 3, author); + item->setData(Qt::UserRole + 4, log); + m_commitInfoWidget->addItem(item); + + } + count = (count + 1)%FINAL; + } + } +} + +void HgMergeDialog::done(int r) +{ + if (r == KDialog::Accepted) { + HgWrapper *hgw = HgWrapper::instance(); + + QListWidgetItem *currentItem = m_commitInfoWidget->currentItem(); + if (currentItem == 0) { + KMessageBox::error(this, + i18nc("@message", "No head selected for merge!")); + return; + } + + QString changeset = m_commitInfoWidget->selectedChangeset(); + QStringList args; + + args << QLatin1String("-r"); + args << changeset; + + if (hgw->executeCommandTillFinished(QLatin1String("merge"), args)) { + KMessageBox::information(this, hgw->readAllStandardOutput()); + KDialog::done(r); + } + else { + KMessageBox::error(this, hgw->readAllStandardError()); + return; + } + } + else { + KDialog::done(r); + } +} + +void HgMergeDialog::saveGeometry() +{ + FileViewHgPluginSettings *settings = FileViewHgPluginSettings::self(); + settings->setMergeDialogHeight(this->height()); + settings->setMergeDialogWidth(this->width()); + settings->writeConfig(); +} + +#include "moc_mergedialog.cpp" + diff --git a/dolphin/plugins/hg/mergedialog.h b/dolphin/plugins/hg/mergedialog.h new file mode 100644 index 00000000..49681acc --- /dev/null +++ b/dolphin/plugins/hg/mergedialog.h @@ -0,0 +1,54 @@ +/*************************************************************************** + * Copyright (C) 2011 by Vishesh Yadav * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef HGMERGE_H +#define HGMERGE_H + +#include +#include + +class KComboBox; +class KPushButton; +#include +class HgCommitInfoWidget; + +/** + * Implements dialog to perform merge operations + */ +class HgMergeDialog : public KDialog +{ + Q_OBJECT + +public: + HgMergeDialog(QWidget *parent = 0); + void done(int r); + +private slots: + void saveGeometry(); + +private: + void updateInitialDialog(); + +private: + QLabel *m_currentChangeset; + HgCommitInfoWidget *m_commitInfoWidget; +}; + +#endif // HGMERGE_H + diff --git a/dolphin/plugins/hg/pathselector.cpp b/dolphin/plugins/hg/pathselector.cpp new file mode 100644 index 00000000..302755be --- /dev/null +++ b/dolphin/plugins/hg/pathselector.cpp @@ -0,0 +1,98 @@ +/*************************************************************************** + * Copyright (C) 2011 by Vishesh Yadav * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "pathselector.h" +#include "hgconfig.h" + +#include +#include +#include +#include +#include + +HgPathSelector::HgPathSelector(QWidget *parent) : + QWidget(parent) +{ + setupUI(); + reload(); + + // connections + connect(m_selectPathAlias, SIGNAL(currentIndexChanged(int)), + this, SLOT(slotChangeEditUrl(int))); + connect(m_selectPathAlias, SIGNAL(highlighted(int)), + this, SLOT(slotChangeEditUrl(int))); +} + +void HgPathSelector::setupUI() +{ + QHBoxLayout *urlLayout = new QHBoxLayout; + m_selectPathAlias = new KComboBox; + m_urlEdit = new KLineEdit; + m_urlEdit->setReadOnly(true); + + urlLayout->addWidget(m_selectPathAlias); + urlLayout->addWidget(m_urlEdit); + + setLayout(urlLayout); +} + +void HgPathSelector::reload() +{ + HgConfig hgc(HgConfig::RepoConfig); + m_pathList = hgc.repoRemotePathList(); + + m_selectPathAlias->clear(); + + QMutableMapIterator it(m_pathList); + while (it.hasNext()) { + it.next(); + if (it.key() == QLatin1String("default")) { + m_selectPathAlias->insertItem(0, it.key()); + } + else { + m_selectPathAlias->addItem(it.key()); + } + } + + m_selectPathAlias->addItem(i18nc("@label:combobox", "")); + slotChangeEditUrl(0); +} + + +void HgPathSelector::slotChangeEditUrl(int index) +{ + if (index == m_selectPathAlias->count() - 1) { ///enter URL manually + m_urlEdit->setReadOnly(false); + m_urlEdit->clear(); + m_urlEdit->setFocus(); + } + else { + QString url = m_pathList[m_selectPathAlias->itemText(index)]; + m_urlEdit->setText(url); + m_urlEdit->setReadOnly(true); + } +} + +const QString HgPathSelector::remote() const +{ + return (m_selectPathAlias->currentIndex() == m_selectPathAlias->count()-1)?m_urlEdit->text():m_selectPathAlias->currentText(); +} + +#include "moc_pathselector.cpp" + diff --git a/dolphin/plugins/hg/pathselector.h b/dolphin/plugins/hg/pathselector.h new file mode 100644 index 00000000..e1a607be --- /dev/null +++ b/dolphin/plugins/hg/pathselector.h @@ -0,0 +1,62 @@ +/*************************************************************************** + * Copyright (C) 2011 by Vishesh Yadav * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef HGPATHSELECTOR_H +#define HGPATHSELECTOR_H + +#include +#include + +class KComboBox; +class KLineEdit; + +/** + * A simple widget which presents a ComboBox to select list of Path aliases + * stored in .hgrc file and show their URL. URL's can be entered manually + * as well. + */ +class HgPathSelector : public QWidget +{ + Q_OBJECT + +public: + HgPathSelector(QWidget *parent=0); + + /** + * @return Return QString containing the selected/entered alias/URL + */ + const QString remote() const; + +public slots: + void reload(); + +private: + void setupUI(); + +private slots: + void slotChangeEditUrl(int index); + +private: + QMap m_pathList; + KComboBox *m_selectPathAlias; + KLineEdit *m_urlEdit; +}; + +#endif /* HGPATHSELECTOR_H */ + diff --git a/dolphin/plugins/hg/pulldialog.cpp b/dolphin/plugins/hg/pulldialog.cpp new file mode 100644 index 00000000..c486cdd5 --- /dev/null +++ b/dolphin/plugins/hg/pulldialog.cpp @@ -0,0 +1,172 @@ +/*************************************************************************** + * Copyright (C) 2011 by Vishesh Yadav * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "pulldialog.h" +#include "hgwrapper.h" +#include "hgconfig.h" +#include "pathselector.h" +#include "fileviewhgpluginsettings.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +HgPullDialog::HgPullDialog(QWidget *parent): + HgSyncBaseDialog(HgSyncBaseDialog::PullDialog, parent) +{ + // dialog properties + this->setCaption(i18nc("@title:window", + "Hg Pull Repository")); + this->setButtons(KDialog::Ok | KDialog::Details | KDialog::Cancel); + this->setDefaultButton(KDialog::Ok); + this->setButtonText(KDialog::Ok, i18nc("@action:button", "Pull")); + this->setButtonText(KDialog::Details, i18nc("@action:button", "Options")); + + setup(); +} + +void HgPullDialog::setOptions() +{ + m_optUpdate = new QCheckBox(i18nc("@label:checkbox", + "Update to new branch head if changesets were pulled")); + m_optInsecure = new QCheckBox(i18nc("@label:checkbox", + "Do not verify server certificate")); + m_optForce = new QCheckBox(i18nc("@label:checkbox", + "Force Pull")); + m_optionGroup = new QGroupBox(i18nc("@label:group", + "Options")); + + m_options << m_optForce; + m_options << m_optUpdate; + m_options << m_optInsecure; +} + +void HgPullDialog::createChangesGroup() +{ + m_changesGroup = new QGroupBox(i18nc("@label:group", + "Incoming Changes")); + QHBoxLayout *hbox = new QHBoxLayout; + m_changesList = new QTableWidget; + + m_changesList->setColumnCount(4); + m_changesList->verticalHeader()->hide(); + m_changesList->horizontalHeader()->hide(); + m_changesList->setSelectionBehavior(QAbstractItemView::SelectRows); + m_changesList->setEditTriggers(QAbstractItemView::NoEditTriggers); + + hbox->addWidget(m_changesList); + + m_changesGroup->setLayout(hbox); + m_changesGroup->setVisible(false); + + connect(this, SIGNAL(changeListAvailable()), + this, SLOT(slotUpdateChangesGeometry())); +} + +void HgPullDialog::getHgChangesArguments(QStringList &args) +{ + args << QLatin1String("incoming"); + args << m_pathSelector->remote(); + args << QLatin1String("--config"); + args << QLatin1String("ui.verbose=False"); + args << QLatin1String("--template"); + args << QLatin1String("Commit: {rev}:{node|short} " + "{author} " + "{date|isodate} {desc|firstline}\n"); +} + +void HgPullDialog::parseUpdateChanges(const QString &input) +{ + QStringList list = input.split(" ", QString::SkipEmptyParts); + QTableWidgetItem *author = new QTableWidgetItem; + QTableWidgetItem *changeset = new QTableWidgetItem; + QTableWidgetItem *date = new QTableWidgetItem; + QTableWidgetItem *summary = new QTableWidgetItem; + + author->setForeground(Qt::darkRed); + changeset->setForeground(Qt::red); + date->setForeground(Qt::blue); + + author->setText(list.takeFirst()); + changeset->setText(list.takeFirst()); + date->setText(list.takeFirst()); + summary->setText(list.takeFirst()); + + int rowCount = m_changesList->rowCount(); + m_changesList->insertRow(rowCount); + m_changesList->setItem(rowCount, 0, author); + m_changesList->setItem(rowCount, 1, changeset); + m_changesList->setItem(rowCount, 2, date); + m_changesList->setItem(rowCount, 3, summary); +} + +void HgPullDialog::appendOptionArguments(QStringList &args) +{ + if (m_optForce->isChecked()) { + args << QLatin1String("--force"); + } + if (m_optUpdate->isChecked()) { + args << QLatin1String("--update"); + } + if (m_optInsecure->isChecked()) { + args << QLatin1String("--insecure"); + } +} + +void HgPullDialog::slotUpdateChangesGeometry() +{ + m_changesList->resizeColumnsToContents(); + m_changesList->resizeRowsToContents(); + m_changesList->horizontalHeader()->setStretchLastSection(true); +} + +void HgPullDialog::readBigSize() +{ + FileViewHgPluginSettings *settings = FileViewHgPluginSettings::self(); + m_bigSize = QSize(settings->pullDialogBigWidth(), settings->pushDialogBigHeight()); +} + +void HgPullDialog::writeBigSize() +{ + kDebug() << "Saving geometry"; + FileViewHgPluginSettings *settings = FileViewHgPluginSettings::self(); + settings->setPullDialogBigWidth(m_bigSize.width()); + settings->setPullDialogBigHeight(m_bigSize.height()); + settings->writeConfig(); +} + +void HgPullDialog::noChangesMessage() +{ + KMessageBox::information(this, i18nc("@message:info", + "No incoming changes!")); +} + +#include "moc_pulldialog.cpp" + diff --git a/dolphin/plugins/hg/pulldialog.h b/dolphin/plugins/hg/pulldialog.h new file mode 100644 index 00000000..8c952cb4 --- /dev/null +++ b/dolphin/plugins/hg/pulldialog.h @@ -0,0 +1,67 @@ +/*************************************************************************** + * Copyright (C) 2011 by Vishesh Yadav * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef HGPULLDILAOG_H +#define HGPULLDILAOG_H + +#include "syncdialogbase.h" + + +#include +#include +class KTextEdit; +class KComboBox; +#include + +/** + * Dialog to implement pull operation + */ +class HgPullDialog : public HgSyncBaseDialog +{ + Q_OBJECT + +public: + HgPullDialog(QWidget *parent = 0); + +private: + void setOptions(); + void parseUpdateChanges(const QString &input); + void appendOptionArguments(QStringList &args); + void createChangesGroup(); + void getHgChangesArguments(QStringList &args); + void noChangesMessage(); + +private slots: + void slotUpdateChangesGeometry(); + void readBigSize(); + void writeBigSize(); + +private: + // Options + QCheckBox *m_optUpdate; + QCheckBox *m_optInsecure; + QCheckBox *m_optForce; + QGroupBox *m_optionGroup; + + // incoming Changes + QTableWidget *m_changesList; +}; + +#endif // HGPULLDILAOG_H + diff --git a/dolphin/plugins/hg/pushdialog.cpp b/dolphin/plugins/hg/pushdialog.cpp new file mode 100644 index 00000000..4a00fec4 --- /dev/null +++ b/dolphin/plugins/hg/pushdialog.cpp @@ -0,0 +1,193 @@ +/*************************************************************************** + * Copyright (C) 2011 by Vishesh Yadav * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "pushdialog.h" +#include "hgconfig.h" +#include "pathselector.h" +#include "fileviewhgpluginsettings.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +HgPushDialog::HgPushDialog(QWidget *parent): + HgSyncBaseDialog(HgSyncBaseDialog::PushDialog, parent) +{ + // dialog properties + this->setCaption(i18nc("@title:window", + "Hg Push Repository")); + this->setButtons(KDialog::Ok | KDialog::Details | KDialog::Cancel); + this->setDefaultButton(KDialog::Ok); + this->setButtonText(KDialog::Ok, i18nc("@action:button", "Push")); + this->setButtonText(KDialog::Details, i18nc("@action:button", "Options")); + + setup(); +} + +void HgPushDialog::setOptions() +{ + m_optAllowNewBranch = new QCheckBox(i18nc("@label:checkbox", + "Allow pushing a new branch")); + m_optInsecure = new QCheckBox(i18nc("@label:checkbox", + "Do not verify server certificate")); + m_optForce = new QCheckBox(i18nc("@label:checkbox", + "Force Push")); + m_optionGroup = new QGroupBox(i18nc("@label:group", + "Options")); + + m_options << m_optForce; + m_options << m_optAllowNewBranch; + m_options << m_optInsecure; +} + +void HgPushDialog::createChangesGroup() +{ + m_changesGroup = new QGroupBox(i18nc("@label:group", + "Outgoing Changes")); + QHBoxLayout *hbox = new QHBoxLayout; + m_outChangesList = new QTableWidget; + m_changesetInfo = new KTextEdit; + + m_outChangesList->setColumnCount(3); + m_outChangesList->verticalHeader()->hide(); + m_outChangesList->horizontalHeader()->hide(); + m_outChangesList->setSelectionBehavior(QAbstractItemView::SelectRows); + m_outChangesList->setEditTriggers(QAbstractItemView::NoEditTriggers); + + m_changesetInfo->setFontFamily(QLatin1String(KDE_DEFAULT_FIXED_FONT)); + + hbox->addWidget(m_outChangesList); + hbox->addWidget(m_changesetInfo); + + m_changesGroup->setLayout(hbox); + m_changesGroup->setVisible(false); + + connect(m_outChangesList, SIGNAL(itemSelectionChanged()), + this, SLOT(slotOutSelChanged())); + connect(this, SIGNAL(changeListAvailable()), + this, SLOT(slotUpdateChangesGeometry())); +} + +void HgPushDialog::slotOutSelChanged() +{ + if (m_hgw->isBusy()) { + return; + } + + QString changeset = m_outChangesList->item(m_outChangesList->currentRow(), 0)->text().split(' ', QString::SkipEmptyParts).takeLast(); + + QStringList args; + args << QLatin1String("-r"); + args << changeset; + args << QLatin1String("-v"); + args << QLatin1String("-p"); + + QString output; + m_hgw->executeCommand(QLatin1String("log"), args, output); + m_changesetInfo->clear(); + m_changesetInfo->setText(output); +} + +void HgPushDialog::getHgChangesArguments(QStringList &args) +{ + args << QLatin1String("outgoing"); + args << m_pathSelector->remote(); + args << QLatin1String("--config"); + args << QLatin1String("ui.verbose=False"); + args << QLatin1String("--template"); + args << QLatin1String("Commit: {rev}:{node|short} " + "{date|isodate} {desc|firstline}\n"); +} + +void HgPushDialog::parseUpdateChanges(const QString &input) +{ + QStringList list = input.split(" ", QString::SkipEmptyParts); + QTableWidgetItem *changeset = new QTableWidgetItem; + QTableWidgetItem *date = new QTableWidgetItem; + QTableWidgetItem *summary = new QTableWidgetItem; + + changeset->setForeground(Qt::red); + date->setForeground(Qt::blue); + + changeset->setText(list.takeFirst()); + date->setText(list.takeFirst()); + summary->setText(list.takeFirst()); + + int rowCount = m_outChangesList->rowCount(); + m_outChangesList->insertRow(rowCount); + m_outChangesList->setItem(rowCount, 0, changeset); + m_outChangesList->setItem(rowCount, 1, date); + m_outChangesList->setItem(rowCount, 2, summary); +} + +void HgPushDialog::appendOptionArguments(QStringList &args) +{ + if (m_optForce->isChecked()) { + args << QLatin1String("--force"); + } + if (m_optAllowNewBranch->isChecked()) { + args << QLatin1String("--new-branch"); + } + if (m_optInsecure->isChecked()) { + args << QLatin1String("--insecure"); + } +} + +void HgPushDialog::slotUpdateChangesGeometry() +{ + m_outChangesList->resizeColumnsToContents(); + m_outChangesList->resizeRowsToContents(); + m_outChangesList->horizontalHeader()->setStretchLastSection(true); +} + +void HgPushDialog::readBigSize() +{ + FileViewHgPluginSettings *settings = FileViewHgPluginSettings::self(); + m_bigSize = QSize(settings->pushDialogBigWidth(), settings->pushDialogBigHeight()); +} + +void HgPushDialog::writeBigSize() +{ + kDebug() << "Saving geometry"; + FileViewHgPluginSettings *settings = FileViewHgPluginSettings::self(); + settings->setPushDialogBigWidth(m_bigSize.width()); + settings->setPushDialogBigHeight(m_bigSize.height()); + settings->writeConfig(); +} + +void HgPushDialog::noChangesMessage() +{ + KMessageBox::information(this, i18nc("@message:info", + "No outgoing changes!")); +} + +#include "moc_pushdialog.cpp" + diff --git a/dolphin/plugins/hg/pushdialog.h b/dolphin/plugins/hg/pushdialog.h new file mode 100644 index 00000000..0ce67037 --- /dev/null +++ b/dolphin/plugins/hg/pushdialog.h @@ -0,0 +1,68 @@ +/*************************************************************************** + * Copyright (C) 2011 by Vishesh Yadav * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef HGPUSHDILAOG_H +#define HGPUSHDILAOG_H + +#include "hgwrapper.h" +#include "syncdialogbase.h" + +#include +#include +class KTextEdit; +#include + +/** + * Dialog to implement Push operation + */ +class HgPushDialog : public HgSyncBaseDialog +{ + Q_OBJECT + +public: + HgPushDialog(QWidget *parent = 0); + +private: + void setOptions(); + void createChangesGroup(); + void parseUpdateChanges(const QString &input); + void appendOptionArguments(QStringList &args); + void getHgChangesArguments(QStringList &args); + void noChangesMessage(); + +private slots: + void slotOutSelChanged(); + void slotUpdateChangesGeometry(); + void readBigSize(); + void writeBigSize(); + +private: + // Options + QCheckBox *m_optAllowNewBranch; + QCheckBox *m_optInsecure; + QCheckBox *m_optForce; + QGroupBox *m_optionGroup; + + // outgoing Changes + QTableWidget *m_outChangesList; + KTextEdit *m_changesetInfo; +}; + +#endif // HGPUSHDILAOG_H + diff --git a/dolphin/plugins/hg/renamedialog.cpp b/dolphin/plugins/hg/renamedialog.cpp new file mode 100644 index 00000000..0a83156d --- /dev/null +++ b/dolphin/plugins/hg/renamedialog.cpp @@ -0,0 +1,94 @@ +/*************************************************************************** + * Copyright (C) 2011 by Vishesh Yadav * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "renamedialog.h" +#include "hgwrapper.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +HgRenameDialog::HgRenameDialog(const KFileItem &source, QWidget *parent): + KDialog(parent, Qt::Dialog), + m_source(source.name()), + m_source_dir(source.url().directory()) +{ + this->setCaption(i18nc("@title:window", + "Hg Rename")); + this->setButtons(KDialog::Ok | KDialog::Cancel); + this->setDefaultButton(KDialog::Ok); + this->setButtonText(KDialog::Ok, i18nc("@action:button", "Rename")); + + QFrame *frame = new QFrame(this); + QGridLayout *mainLayout = new QGridLayout(frame); + + QLabel *sourceLabel = new QLabel(i18nc("@label:label to source file", + "Source "), frame); + QLabel *sourceFileLabel = new QLabel("" + m_source + ""); + mainLayout->addWidget(sourceLabel, 0, 0); + mainLayout->addWidget(sourceFileLabel, 0, 1); + + QLabel *destinationLabel + = new QLabel(i18nc("@label:rename", "Rename to "), frame); + m_destinationFile = new KLineEdit(m_source, frame); + mainLayout->addWidget(destinationLabel, 1, 0); + mainLayout->addWidget(m_destinationFile, 1, 1); + + frame->setLayout(mainLayout); + setMainWidget(frame); + + m_destinationFile->setFocus(); + m_destinationFile->selectAll(); + + connect(m_destinationFile, SIGNAL(textChanged(const QString &)), + this, SLOT(slotTextChanged(const QString &))); +} + +void HgRenameDialog::slotTextChanged(const QString &text) +{ + enableButtonOk(text.length() != 0); +} + +void HgRenameDialog::done(int r) +{ + if (r == KDialog::Accepted) { + HgWrapper *hgi = HgWrapper::instance(); + hgi->renameFile(source(), destination()); + } + KDialog::done(r); +} + +QString HgRenameDialog::source() const +{ + return m_source; +} + +QString HgRenameDialog::destination() const +{ + return m_destinationFile->text(); +} + +#include "moc_renamedialog.cpp" + diff --git a/dolphin/plugins/hg/renamedialog.h b/dolphin/plugins/hg/renamedialog.h new file mode 100644 index 00000000..921ab3f5 --- /dev/null +++ b/dolphin/plugins/hg/renamedialog.h @@ -0,0 +1,51 @@ +/*************************************************************************** + * Copyright (C) 2011 by Vishesh Yadav * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef HGRENAMEDIALOG_H +#define HGRENAMEDIALOG_H + +#include +#include + +class KLineEdit; +class KFileItem; + +/** + * Dialog to rename files Mercurial way + */ +class HgRenameDialog : public KDialog +{ + Q_OBJECT + +public: + HgRenameDialog(const KFileItem &source, QWidget *parent = 0); + QString source() const; + QString destination() const; + void done(int r); + +private slots: + void slotTextChanged(const QString &text); + +private: + QString m_source; + QString m_source_dir; + KLineEdit *m_destinationFile; +}; + +#endif // HGRENAMEDIALOG_H diff --git a/dolphin/plugins/hg/servedialog.cpp b/dolphin/plugins/hg/servedialog.cpp new file mode 100644 index 00000000..ac26caa7 --- /dev/null +++ b/dolphin/plugins/hg/servedialog.cpp @@ -0,0 +1,169 @@ +/*************************************************************************** + * Copyright (C) 2011 by Vishesh Yadav * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "servedialog.h" +#include "servewrapper.h" +#include "fileviewhgpluginsettings.h" +#include "hgwrapper.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +HgServeDialog::HgServeDialog(QWidget *parent) : + KDialog(parent, Qt::Dialog) +{ + // dialog properties + this->setCaption(i18nc("@title:window", + "Hg Serve")); + this->setButtons(KDialog::None); + + // + m_serverWrapper = HgServeWrapper::instance(); + + // + setupUI(); + loadConfig(); + + // Load saved settings + FileViewHgPluginSettings *settings = FileViewHgPluginSettings::self(); + this->setInitialSize(QSize(settings->serveDialogWidth(), + settings->serveDialogHeight())); + + // connections + connect(this, SIGNAL(finished()), this, SLOT(saveGeometry())); + connect(m_startButton, SIGNAL(clicked()), this, SLOT(slotStart())); + connect(m_stopButton, SIGNAL(clicked()), this, SLOT(slotStop())); + connect(m_serverWrapper, SIGNAL(finished()), + this, SLOT(slotUpdateButtons())); + connect(m_serverWrapper, SIGNAL(started()), + this, SLOT(slotUpdateButtons())); + connect(m_serverWrapper, SIGNAL(error()), + this, SLOT(slotUpdateButtons())); + connect(m_serverWrapper, SIGNAL(error()), + this, SLOT(slotServerError())); + connect(m_serverWrapper, + SIGNAL(readyReadLine(const QString&, const QString&)), + this, + SLOT(appendServerOutput(const QString&, const QString&))); +} + +void HgServeDialog::setupUI() +{ + m_portNumber = new QSpinBox; + m_portNumber->setMinimum(0); + m_portNumber->setMaximum(65535); + m_portNumber->setValue(8000); + + m_startButton = new KPushButton(i18nc("@label:button", "Start Server")); + m_stopButton = new KPushButton(i18nc("@label:button", "Stop Server")); + + m_logEdit = new QTextEdit; + m_repoPathLabel = new QLabel; + m_logEdit->setReadOnly(true); + m_logEdit->setFontFamily(QLatin1String(KDE_DEFAULT_FIXED_FONT)); + + QVBoxLayout *buttonLayout = new QVBoxLayout; + buttonLayout->addWidget(m_startButton); + buttonLayout->addWidget(m_stopButton); + buttonLayout->addStretch(); + + QHBoxLayout *portLayout = new QHBoxLayout; + portLayout->addWidget(new QLabel(i18nc("@label", "Port"))); + portLayout->addWidget(m_portNumber); + portLayout->addStretch(); + + QHBoxLayout *midLayout = new QHBoxLayout; + midLayout->addWidget(m_logEdit); + midLayout->addLayout(buttonLayout); + + QVBoxLayout *layout = new QVBoxLayout; + layout->addWidget(m_repoPathLabel); + layout->addLayout(portLayout); + layout->addLayout(midLayout); + + QWidget *widget = new QWidget; + widget->setLayout(layout); + setMainWidget(widget); +} + +void HgServeDialog::loadConfig() +{ + HgWrapper *hgw = HgWrapper::instance(); + m_repoPathLabel->setText("" + hgw->getBaseDir() + ""); + + slotUpdateButtons(); +} + +void HgServeDialog::slotUpdateButtons() +{ + if (m_serverWrapper->running(HgWrapper::instance()->getBaseDir())) { + m_startButton->setEnabled(false); + m_stopButton->setEnabled(true); + m_portNumber->setEnabled(false); + } + else { + m_startButton->setEnabled(true); + m_stopButton->setEnabled(false); + m_portNumber->setEnabled(true); + m_serverWrapper->cleanUnused(); + } +} + +void HgServeDialog::slotStart() +{ + m_serverWrapper->startServer(HgWrapper::instance()->getBaseDir(), + m_portNumber->value()); +} + +void HgServeDialog::slotStop() +{ + m_serverWrapper->stopServer(HgWrapper::instance()->getBaseDir()); +} + +void HgServeDialog::slotServerError() +{ + m_serverWrapper->cleanUnused(); +} + +void HgServeDialog::appendServerOutput(const QString &repoLocation, const QString &line) +{ + if (HgWrapper::instance()->getBaseDir() == repoLocation) { + m_logEdit->append(line); + } +} + +void HgServeDialog::saveGeometry() +{ + FileViewHgPluginSettings *settings = FileViewHgPluginSettings::self(); + settings->setServeDialogHeight(this->height()); + settings->setServeDialogWidth(this->width()); + settings->writeConfig(); +} + +#include "moc_servedialog.cpp" + + diff --git a/dolphin/plugins/hg/servedialog.h b/dolphin/plugins/hg/servedialog.h new file mode 100644 index 00000000..4f1f1317 --- /dev/null +++ b/dolphin/plugins/hg/servedialog.h @@ -0,0 +1,69 @@ +/*************************************************************************** + * Copyright (C) 2011 by Vishesh Yadav * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef HG_SERVE_DIALOG +#define HG_SERVE_DIALOG + +#include + +#include +class KPushButton; +#include +#include +class HgServeWrapper; + +/** + * Implements dialog to Start and Stop Mercurial web server. + * Several server instances can be handled. + */ +class HgServeDialog : public KDialog +{ + Q_OBJECT + +public: + HgServeDialog(QWidget *parent = 0); + void setupUI(); + void loadConfig(); + +public slots: + void slotStart(); + void slotStop(); + +private slots: + void slotUpdateButtons(); + void slotServerError(); + void saveGeometry(); + + /** + * Append stdout and stderr to m_logEdit + */ + void appendServerOutput(const QString &repoLocation, const QString &line); + +private: + QSpinBox *m_portNumber; + KPushButton *m_startButton; + KPushButton *m_stopButton; + QTextEdit *m_logEdit; + QLabel *m_repoPathLabel; + + HgServeWrapper *m_serverWrapper; +}; + +#endif /* HG_SERVE_DIALOG */ + diff --git a/dolphin/plugins/hg/servewrapper.cpp b/dolphin/plugins/hg/servewrapper.cpp new file mode 100644 index 00000000..8db5fbd4 --- /dev/null +++ b/dolphin/plugins/hg/servewrapper.cpp @@ -0,0 +1,150 @@ +/*************************************************************************** + * Copyright (C) 2011 by Vishesh Yadav * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "servewrapper.h" +#include "hgwrapper.h" + +#include +#include +#include + +HgServeWrapper *HgServeWrapper::m_instance = 0; + +HgServeWrapper::HgServeWrapper(QObject *parent) : + QObject(parent) +{ +} + +HgServeWrapper::~HgServeWrapper() +{ + QMutableHashIterator it(m_serverList); + while (it.hasNext()) { + it.next(); + ///terminate server if not terminated already + if (it.value()->process.state() != QProcess::NotRunning) { + it.value()->process.terminate(); + } + it.value()->deleteLater(); + it.remove(); + } +} + +HgServeWrapper *HgServeWrapper::instance() +{ + if (m_instance == 0) { + m_instance = new HgServeWrapper; + } + return m_instance; +} + +void HgServeWrapper::startServer(const QString &repoLocation, int portNumber) +{ + ServerProcessType *server = m_serverList.value(repoLocation, 0); + if (server != 0) { + m_serverList.remove(repoLocation); + server->deleteLater(); + } + server = new ServerProcessType; + m_serverList.insert(repoLocation, server); + server->port = portNumber; + server->process.setWorkingDirectory(HgWrapper::instance()->getBaseDir()); + + connect(&server->process, SIGNAL(started()), + this, SIGNAL(started())); + connect(&server->process, SIGNAL(finished(int, QProcess::ExitStatus)), + this, SLOT(slotFinished(int, QProcess::ExitStatus))); + connect(server, SIGNAL(readyReadLine(const QString&, const QString&)), + this, SIGNAL(readyReadLine(const QString&, const QString&))); + + QStringList args; + args << QLatin1String("-oL"); + args << QLatin1String("hg"); + args << QLatin1String("serve"); + args << QLatin1String("--port"); + args << QString::number(portNumber); + server->process.start(QLatin1String("stdbuf"), args); + emit readyReadLine(repoLocation, + i18n("## Starting Server ##")); + emit readyReadLine(repoLocation, + QString("% hg serve --port %1").arg(portNumber)); +} + +void HgServeWrapper::stopServer(const QString &repoLocation) +{ + ServerProcessType *server = m_serverList.value(repoLocation, 0); + if (server == 0) { + return; + } + server->process.terminate(); +} + +bool HgServeWrapper::running(const QString &repoLocation) +{ + ServerProcessType *server = m_serverList.value(repoLocation, 0); + if (server == 0) { + return false; + } + return ( server->process.state() == QProcess::Running || + server->process.state() == QProcess::Starting); +} + +void HgServeWrapper::slotFinished(int exitCode, QProcess::ExitStatus status) +{ + if (exitCode == 0 && status == QProcess::NormalExit) { + emit finished(); + } + else { + emit error(); + } +} + +QString HgServeWrapper::errorMessage(const QString &repoLocation) +{ + ServerProcessType *server = m_serverList.value(repoLocation, 0); + if (server == 0) { + return QString(); + } + return QTextCodec::codecForLocale()->toUnicode(server->process.readAllStandardError()); +} + +bool HgServeWrapper::normalExit(const QString &repoLocation) +{ + ServerProcessType *server = m_serverList.value(repoLocation, 0); + if (server == 0) { + return true; + } + + return (server->process.exitStatus() == QProcess::NormalExit && + server->process.exitCode() == 0); +} + +void HgServeWrapper::cleanUnused() +{ + QMutableHashIterator it(m_serverList); + while (it.hasNext()) { + it.next(); + if (it.value()->process.state() == QProcess::NotRunning) { + it.value()->deleteLater(); + it.remove(); + } + } +} + +#include "moc_servewrapper.cpp" + diff --git a/dolphin/plugins/hg/servewrapper.h b/dolphin/plugins/hg/servewrapper.h new file mode 100644 index 00000000..1248434a --- /dev/null +++ b/dolphin/plugins/hg/servewrapper.h @@ -0,0 +1,170 @@ +/*************************************************************************** + * Copyright (C) 2011 by Vishesh Yadav * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef HG_SERVE_WRAPPER_H +#define HG_SERVE_WRAPPER_H + +#include +#include +#include +#include +#include + +class ServerProcessType; + +/** + * Wrapper to manage web server instaces of mercurial repository. More than one + * server can be handled. + * + * This wrapper should be singleton hence only one instance should be created + * and used. Hence, never create an object statically or use 'new' operator. + * Use instance() method to get(create) an instance. + */ +class HgServeWrapper : public QObject +{ + Q_OBJECT + +public: + HgServeWrapper(QObject *parent=0); + ~HgServeWrapper(); + + /** + * Returns pointer to singleton instance of the wrapper. An instance is + * created if none exists. + * + * @return Pointer to the instance + */ + static HgServeWrapper *instance(); + + /** + * Starts a new instance of server. + * + * @param repoLocation Path to repository which is to be hosted on server + * @param portNumber Port to create server on. + * + */ + void startServer(const QString &repoLocation, int portNumber); + + /** + * Stops a server. + * + * @param repoLocation Path to repository whose server instance in this + * has to be stopped. + */ + void stopServer(const QString &repoLocation); + + /** + * Checks whether a web server for given repository is running under this + * wrapper. + * + * @param repoLocation Path to repository + * @return true if runnning/started else false + */ + bool running(const QString &repoLocation); + + /** + * Cleans all resources in this wrapper used by non-running servers + */ + void cleanUnused(); + + /** + * If terminated, get error message of that server instaces + */ + QString errorMessage(const QString &repoLocation); + + /** + * Check if the just stopped server for given repository was + * exited successfully. + * + * @param repoLocation Path to repository + * @return true if exitCode == 0 and exitStatus == Normal, otherwise false + */ + bool normalExit(const QString &repoLocation); + +signals: + // These signals are emitted for every ServerProcessType in list. Its upto + // the owner of this wrapper to decide which server was updated. If any of + // these signals are emitted cleanUnused() is called and every initiater + // of server should check status and act according. + void finished(); + void error(); + void started(); + void readyReadLine(const QString &repoLocation, const QString &line); + +private: + +private slots: + void slotFinished(int exitCode, QProcess::ExitStatus status); + +private: + QHash m_serverList; + static HgServeWrapper *m_instance; +}; + +//FIXME: Had to change struct to class and make it unnested. +// Hide member variables. + +/** + * Represents a Mercurial Server instace. + */ +class ServerProcessType : public QObject { + Q_OBJECT + +public: + QProcess process; + int port; + + ServerProcessType() + { + connect(&process, SIGNAL(readyReadStandardOutput()), + this, SLOT(slotAppendOutput())); + connect(&process, SIGNAL(readyReadStandardError()), + this, SLOT(slotAppendRemainingOutput())); + connect(&process, SIGNAL(finished(int, QProcess::ExitStatus)), + this, SLOT(slotFinished())); + } + +signals: + void readyReadLine(const QString &repoLocation, const QString &line); + +private slots: + void slotAppendOutput() + { + if (process.canReadLine()) { + emit readyReadLine(process.workingDirectory(), + QTextCodec::codecForLocale()->toUnicode(process.readAllStandardOutput()).trimmed()); + } + } + + void slotAppendRemainingOutput() + { + emit readyReadLine(process.workingDirectory(), + QTextCodec::codecForLocale()->toUnicode(process.readAllStandardError()).trimmed()); + } + + void slotFinished() + { + emit readyReadLine(process.workingDirectory(), + i18n("## Server Stopped! ##\n")); + } +}; + +#endif /* HG_SERVE_WRAPPER_H */ + + diff --git a/dolphin/plugins/hg/statuslist.cpp b/dolphin/plugins/hg/statuslist.cpp new file mode 100644 index 00000000..c64bb3ae --- /dev/null +++ b/dolphin/plugins/hg/statuslist.cpp @@ -0,0 +1,177 @@ +/*************************************************************************** + * Copyright (C) 2011 by Vishesh Yadav * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "statuslist.h" +#include "hgwrapper.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +HgStatusList::HgStatusList(QWidget *parent): + QGroupBox(parent) +{ + QVBoxLayout *mainLayout = new QVBoxLayout(this); + //m_filter = new KLineEdit(this); + m_statusTable = new QTableWidget(this); + + m_statusTable->setColumnCount(3); + QStringList headers; + headers << "*" << "S" << i18n("Filename"); + m_statusTable->setHorizontalHeaderLabels(headers); + m_statusTable->verticalHeader()->hide(); + m_statusTable->setEditTriggers(QAbstractItemView::NoEditTriggers); + m_statusTable->setSelectionBehavior(QAbstractItemView::SelectRows); + m_statusTable->setSelectionMode(QAbstractItemView::SingleSelection); + + //mainLayout->addWidget(m_filter); + mainLayout->addWidget(m_statusTable); + + setTitle(i18nc("@title:group", "File Status")); + setLayout(mainLayout); + + reloadStatusTable(); + + connect(m_statusTable, + SIGNAL(currentItemChanged(QTableWidgetItem*, QTableWidgetItem*)), + this, SLOT(currentItemChangedSlot())); +} + +void HgStatusList::currentItemChangedSlot() +{ + emit itemSelectionChanged( + m_statusTable->item(m_statusTable->currentRow(), 1)->text()[0].toLatin1(), + m_statusTable->item(m_statusTable->currentRow(), 2)->text()); +} + +void HgStatusList::reloadStatusTable() +{ + m_statusTable->clearContents(); + m_statusTable->resizeRowsToContents(); + m_statusTable->resizeColumnsToContents(); + m_statusTable->horizontalHeader()->setStretchLastSection(true); + + HgWrapper *hgWrapper = HgWrapper::instance(); + QHash hgVsState; + hgWrapper->getItemVersions(hgVsState); + QMutableHashIterator it(hgVsState); + int rowCount = 0; + while (it.hasNext()) { + it.next(); + KVersionControlPlugin::ItemVersion currentStatus = it.value(); + // Get path relative to root directory of repository + // FIXME: preferred method, but not working :| bad hack below + // QString currentFile + // = KUrl::relativeUrl(hgWrapper->getBaseDir(), it.key()); + QString currentFile = it.key().mid(hgWrapper->getBaseDir().length()+1); + QString currentStatusString; //one character status indicator + + // Temporarily ignoring + // TODO: Ask to add file if this is checked by user + if (currentStatus == KVersionControlPlugin::UnversionedVersion || + currentStatus == KVersionControlPlugin::IgnoredVersion) { + continue; + } + + QTableWidgetItem *check = new QTableWidgetItem; + QTableWidgetItem *status = new QTableWidgetItem; + QTableWidgetItem *fileName = new QTableWidgetItem; + + switch (currentStatus) { + case KVersionControlPlugin::AddedVersion: + status->setForeground(Qt::darkCyan); + fileName->setForeground(Qt::darkCyan); + check->setCheckState(Qt::Checked); + currentStatusString = QLatin1String("A"); + break; + case KVersionControlPlugin::LocallyModifiedVersion: + status->setForeground(Qt::blue); + fileName->setForeground(Qt::blue); + check->setCheckState(Qt::Checked); + currentStatusString = QLatin1String("M"); + break; + case KVersionControlPlugin::RemovedVersion: + status->setForeground(Qt::red); + fileName->setForeground(Qt::red); + check->setCheckState(Qt::Checked); + currentStatusString = QLatin1String("R"); + break; + case KVersionControlPlugin::UnversionedVersion: + status->setForeground(Qt::darkMagenta); + fileName->setForeground(Qt::darkMagenta); + currentStatusString = QLatin1String("?"); + break; + case KVersionControlPlugin::IgnoredVersion: + status->setForeground(Qt::black); + fileName->setForeground(Qt::black); + currentStatusString = QLatin1String("I"); + break; + case KVersionControlPlugin::MissingVersion: + status->setForeground(Qt::black); + fileName->setForeground(Qt::black); + currentStatusString = QLatin1String("!"); + break; + default: + break; + } + + status->setText(QString(currentStatusString)); + fileName->setText(currentFile); + + m_statusTable->insertRow(rowCount); + check->setCheckState(Qt::Checked); //Change. except untracked, ignored + m_statusTable->setItem(rowCount, 0, check); + m_statusTable->setItem(rowCount, 1, status); + m_statusTable->setItem(rowCount, 2, fileName); + + ++rowCount; + } +} + +bool HgStatusList::getSelectionForCommit(QStringList &files) +{ + int nChecked = 0; + int nRowCount = m_statusTable->rowCount(); + for (int row = 0; row < nRowCount; row++) { + QTableWidgetItem *item = m_statusTable->item(row, 0); + if (item->checkState() == Qt::Checked) { + nChecked++; + files << m_statusTable->item(row, 2)->text(); + } + } + // if all files are selected, clear the list + if (nChecked == nRowCount) { + files.clear(); + } + // at least one file is checked + if (nChecked > 0) { + return true; + } + //nothing is selected + return false; +} + +#include "moc_statuslist.cpp" + diff --git a/dolphin/plugins/hg/statuslist.h b/dolphin/plugins/hg/statuslist.h new file mode 100644 index 00000000..3c215762 --- /dev/null +++ b/dolphin/plugins/hg/statuslist.h @@ -0,0 +1,69 @@ +/*************************************************************************** + * Copyright (C) 2011 by Vishesh Yadav * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef STATUSLIST_H +#define STATUSLIST_H + +#include +#include + +class KLineEdit; +#include + +/** + * Shows a list of files and their corresponding version states in a table. + * Used in commit dialog. + */ +class HgStatusList : public QGroupBox +{ + Q_OBJECT + +public: + HgStatusList(QWidget *parent = 0); + + /** + * Appends the list of selected files whose changes should be + * committed. If all files are selected, nothing is appended and true + * is returned. If no files are selected, false is returned. + * + * @param files Append all the selected files to this. If all files are + * selected, nothing is appended + * @return If at least one file is selected, true is returned; otherwise + * false. + */ + bool getSelectionForCommit(QStringList &files); + +private slots: + void reloadStatusTable(); + +private slots: + void currentItemChangedSlot(); + +signals: + void itemSelectionChanged(const char status, const QString &fileName); + +private: + QString m_hgBaseDir; + + QTableWidget *m_statusTable; + //KLineEdit *m_filter; +}; + +#endif // STATUSLIST_H + diff --git a/dolphin/plugins/hg/syncdialogbase.cpp b/dolphin/plugins/hg/syncdialogbase.cpp new file mode 100644 index 00000000..bd801fa7 --- /dev/null +++ b/dolphin/plugins/hg/syncdialogbase.cpp @@ -0,0 +1,316 @@ +/*************************************************************************** + * Copyright (C) 2011 by Vishesh Yadav * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "syncdialogbase.h" +#include "hgconfig.h" +#include "pathselector.h" +#include "fileviewhgpluginsettings.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +HgSyncBaseDialog::HgSyncBaseDialog(DialogType dialogType, QWidget *parent): + KDialog(parent, Qt::Dialog), + m_haveChanges(false), + m_terminated(false), + m_dialogType(dialogType) +{ + m_hgw = HgWrapper::instance(); +} + +void HgSyncBaseDialog::setup() +{ + createChangesGroup(); + readBigSize(); + setupUI(); + + connect(m_changesButton, SIGNAL(clicked()), + this, SLOT(slotGetChanges())); + connect(&m_process, SIGNAL(stateChanged(QProcess::ProcessState)), + this, SLOT(slotUpdateBusy(QProcess::ProcessState))); + connect(&m_main_process, SIGNAL(stateChanged(QProcess::ProcessState)), + this, SLOT(slotUpdateBusy(QProcess::ProcessState))); + connect(&m_main_process, SIGNAL(finished(int, QProcess::ExitStatus)), + this, SLOT(slotOperationComplete(int, QProcess::ExitStatus))); + connect(&m_main_process, SIGNAL(error(QProcess::ProcessError)), + this, SLOT(slotOperationError())); + connect(&m_process, SIGNAL(error(QProcess::ProcessError)), + this, SLOT(slotChangesProcessError())); + connect(&m_process, SIGNAL(finished(int, QProcess::ExitStatus)), + this, SLOT(slotChangesProcessComplete(int, QProcess::ExitStatus))); + connect(this, SIGNAL(finished()), this, SLOT(slotWriteBigSize())); +} + +void HgSyncBaseDialog::createOptionGroup() +{ + setOptions(); + QVBoxLayout *layout = new QVBoxLayout; + + foreach (QCheckBox *cb, m_options) { + layout->addWidget(cb); + } + + m_optionGroup = new QGroupBox; + m_optionGroup->setLayout(layout); + setDetailsWidget(m_optionGroup); +} + +void HgSyncBaseDialog::setupUI() +{ + // top url bar + m_pathSelector = new HgPathSelector; + + // changes button + //FIXME not very good idea. Bad HACK + if (m_dialogType == PullDialog) { + m_changesButton = new KPushButton(i18nc("@label:button", + "Show Incoming Changes")); + } + else { + m_changesButton = new KPushButton(i18nc("@label:button", + "Show Outgoing Changes")); + } + m_changesButton->setSizePolicy(QSizePolicy::Fixed, + QSizePolicy::Fixed); + m_changesButton->setCheckable(true); + + // dialog's main widget + QWidget *widget = new QWidget; + QVBoxLayout *mainLayout = new QVBoxLayout; + mainLayout->addWidget(m_pathSelector); + + // changes + m_changesGroup->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + mainLayout->addWidget(m_changesGroup); + + // bottom bar + QHBoxLayout *bottomLayout = new QHBoxLayout; + m_statusProg = new QProgressBar; + m_statusProg->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + bottomLayout->addWidget(m_changesButton, Qt::AlignLeft); + bottomLayout->addStretch(); + bottomLayout->addWidget(m_statusProg); + + // + mainLayout->addLayout(bottomLayout); + widget->setLayout(mainLayout); + + createOptionGroup(); + setMainWidget(widget); +} + +void HgSyncBaseDialog::slotGetChanges() +{ + if (m_haveChanges) { + m_changesGroup->setVisible(!m_changesGroup->isVisible()); + m_changesButton->setChecked(m_changesGroup->isVisible()); + if (m_changesGroup->isVisible()) { + loadBigSize(); + } + else { + loadSmallSize(); + } + return; + } + if (m_process.state() == QProcess::Running) { + return; + } + + QStringList args; + getHgChangesArguments(args); + m_process.setWorkingDirectory(m_hgw->getBaseDir()); + m_process.start(QLatin1String("hg"), args); +} + + +void HgSyncBaseDialog::slotChangesProcessComplete(int exitCode, QProcess::ExitStatus status) +{ + + if (exitCode != 0 || status != QProcess::NormalExit) { + QString message = QTextCodec::codecForLocale()->toUnicode(m_process.readAllStandardError()); + if (message.isEmpty()) { + message = i18nc("@message", "No changes found!"); + } + KMessageBox::error(this, message); + return; + } + + char buffer[512]; + + /** + * unwantedRead boolean to ensure that unwanted information messages + * by mercurial are filtered out. + * eg. Comparing with /path/to/repository + * Searching for changes + */ + bool unwantedRead = false; + + /** + * hasChanges boolean checks whether there are any changes to be sent + * or received and invoke noChangesMessage() if its false + */ + bool hasChanges = false; + + while (m_process.readLine(buffer, sizeof(buffer)) > 0) { + QString line(QTextCodec::codecForLocale()->toUnicode(buffer)); + if (unwantedRead ) { + line.remove(QLatin1String("Commit: ")); + parseUpdateChanges(line.trimmed()); + hasChanges = true;; + } + else if (line.startsWith(QLatin1String("Commit: "))) { + unwantedRead = true; + line.remove(QLatin1String("Commit: ")); + parseUpdateChanges(line.trimmed()); + hasChanges = true; + } + } + + if (!hasChanges) { + noChangesMessage(); + } + + m_changesGroup->setVisible(true); + m_changesButton->setChecked(true); + loadBigSize(); + m_haveChanges = true; + emit changeListAvailable(); +} + +void HgSyncBaseDialog::slotChangesProcessError() +{ + kDebug() << "Cant get changes"; + KMessageBox::error(this, i18n("Error!")); +} + +void HgSyncBaseDialog::loadSmallSize() +{ + m_bigSize = size(); + resize(m_smallSize); +} + +void HgSyncBaseDialog::loadBigSize() +{ + m_smallSize = size(); + resize(m_bigSize); +} + +void HgSyncBaseDialog::slotWriteBigSize() +{ + if (m_changesGroup->isVisible()) { + m_bigSize = size(); + } + writeBigSize(); +} + +void HgSyncBaseDialog::done(int r) +{ + if (r == KDialog::Accepted) { + if (m_main_process.state() == QProcess::Running || + m_main_process.state() == QProcess::Starting) { + kDebug() << "HgWrapper already busy"; + return; + } + + QStringList args; + QString command = (m_dialogType==PullDialog)?"pull":"push"; + args << command; + args << m_pathSelector->remote(); + appendOptionArguments(args); + + m_terminated = false; + + m_main_process.setWorkingDirectory(m_hgw->getBaseDir()); + m_main_process.start(QLatin1String("hg"), args); + } + else { + if (m_process.state() == QProcess::Running || + m_process.state() == QProcess::Starting || + m_main_process.state() == QProcess::Running || + m_main_process.state() == QProcess::Starting) + { + if (m_process.state() == QProcess::Running || + m_process.state() == QProcess::Starting) { + m_process.terminate(); + } + if (m_main_process.state() == QProcess::Running || + m_main_process.state() == QProcess::Starting) { + kDebug() << "terminating pull/push process"; + m_terminated = true; + m_main_process.terminate(); + } + } + else { + KDialog::done(r); + } + } +} + +void HgSyncBaseDialog::slotOperationComplete(int exitCode, QProcess::ExitStatus status) +{ + if (exitCode == 0 && status == QProcess::NormalExit) { + KDialog::done(KDialog::Accepted); + } + else { + if (!m_terminated) { + KMessageBox::error(this, i18n("Error!")); + } + } +} + +void HgSyncBaseDialog::slotOperationError() +{ + KMessageBox::error(this, i18n("Error!")); +} + +void HgSyncBaseDialog::slotUpdateBusy(QProcess::ProcessState state) +{ + if (state == QProcess::Running || state == QProcess::Starting) { + m_statusProg->setRange(0, 0); + m_changesButton->setEnabled(false); + m_changesButton->setChecked(true); + this->enableButtonOk(false); + } + else { + m_statusProg->setRange(0, 100); + m_changesButton->setEnabled(true); + this->enableButtonOk(true); + } + m_statusProg->repaint(); + QApplication::processEvents(); +} + +#include "moc_syncdialogbase.cpp" + diff --git a/dolphin/plugins/hg/syncdialogbase.h b/dolphin/plugins/hg/syncdialogbase.h new file mode 100644 index 00000000..719a37ae --- /dev/null +++ b/dolphin/plugins/hg/syncdialogbase.h @@ -0,0 +1,110 @@ +/*************************************************************************** + * Copyright (C) 2011 by Vishesh Yadav * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef HGSYNCBASEDIALOG_H +#define HGSYNCBASEDIALOG_H + +#include "hgwrapper.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +class KLineEdit; +class KTextEdit; +class KComboBox; +class KPushButton; +class HgPathSelector; + +//TODO: Save/Load dialog geometry +//TODO: HTTPS login +// + +/** + * Abstract class which implements common features of Push and Pull dialog. + * Inherited by HgPushDialog and HgPullDialog. + */ +class HgSyncBaseDialog : public KDialog +{ + Q_OBJECT + +public: + enum DialogType {PushDialog, PullDialog}; + + HgSyncBaseDialog(DialogType dialogType, QWidget *parent = 0); + +signals: + void changeListAvailable(); + +protected: + void done(int r); + void setupUI(); + void createOptionGroup(); + void setup(); + void loadSmallSize(); + void loadBigSize(); + virtual void setOptions() = 0; + virtual void createChangesGroup() = 0; + virtual void parseUpdateChanges(const QString &input) = 0; + virtual void appendOptionArguments(QStringList &args) = 0; + virtual void getHgChangesArguments(QStringList &args) = 0; + virtual void noChangesMessage() = 0; + +protected slots: + void slotGetChanges(); + void slotChangesProcessComplete(int exitCode, QProcess::ExitStatus status); + void slotChangesProcessError(); + void slotOperationComplete(int exitCode, QProcess::ExitStatus status); + void slotOperationError(); + void slotUpdateBusy(QProcess::ProcessState state); + void slotWriteBigSize(); + virtual void writeBigSize() = 0; + virtual void readBigSize() = 0; + +protected: + HgPathSelector *m_pathSelector; + QProgressBar *m_statusProg; + bool m_haveChanges; + bool m_terminated; + HgWrapper *m_hgw; + DialogType m_dialogType; + + // Options + QList m_options; + QGroupBox *m_optionGroup; + + // geometry + QSize m_smallSize; + QSize m_bigSize; + + // changes + KPushButton *m_changesButton; + QGroupBox *m_changesGroup; + QProcess m_process; + QProcess m_main_process; //should I use another process? +}; + +#endif // HGSYNCBASEDIALOG_H + diff --git a/dolphin/plugins/hg/tagdialog.cpp b/dolphin/plugins/hg/tagdialog.cpp new file mode 100644 index 00000000..6f0a2abd --- /dev/null +++ b/dolphin/plugins/hg/tagdialog.cpp @@ -0,0 +1,161 @@ +/*************************************************************************** + * Copyright (C) 2011 by Vishesh Yadav * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "tagdialog.h" +#include "hgwrapper.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +HgTagDialog::HgTagDialog(QWidget *parent): + KDialog(parent, Qt::Dialog) +{ + // dialog properties + this->setCaption(i18nc("@title:window", + "Hg Tag")); + this->setButtons(KDialog::None); + + // UI + QFrame *frame = new QFrame; + QVBoxLayout *vbox = new QVBoxLayout; + + m_tagComboBox = new KComboBox; + m_tagComboBox->setEditable(true); + vbox->addWidget(m_tagComboBox); + + QHBoxLayout *buttonLayout = new QHBoxLayout; + m_createTag = new KPushButton(i18n("Create New Tag")); + m_removeTag = new KPushButton(i18n("Remove Tag")); + m_updateTag = new KPushButton(i18n("Switch Tag")); + buttonLayout->addWidget(m_createTag); + buttonLayout->addWidget(m_removeTag); + buttonLayout->addWidget(m_updateTag); + vbox->addLayout(buttonLayout); + + m_createTag->setEnabled(false); + m_updateTag->setEnabled(false); + m_removeTag->setEnabled(false); + + frame->setLayout(vbox); + updateInitialDialog(); + slotUpdateDialog(QString()); + setMainWidget(frame); + + slotUpdateDialog(m_tagComboBox->currentText()); + + QLineEdit *m_lineEdit = m_tagComboBox->lineEdit(); + + // connections + connect(m_createTag, SIGNAL(clicked()), + this, SLOT(slotCreateTag())); + connect(m_removeTag, SIGNAL(clicked()), + this, SLOT(slotRemoveTag())); + connect(m_updateTag, SIGNAL(clicked()), + this, SLOT(slotSwitch())); + connect(m_tagComboBox, SIGNAL(editTextChanged(const QString&)), + this, SLOT(slotUpdateDialog(const QString&))); + connect(m_lineEdit, SIGNAL(textChanged(const QString&)), + this, SLOT(slotUpdateDialog(const QString&))); +} + +void HgTagDialog::updateInitialDialog() +{ + HgWrapper *hgWrapper = HgWrapper::instance(); + // update combo box + m_tagList = hgWrapper->getTags(); + m_tagComboBox->addItems(m_tagList); + +} + +void HgTagDialog::slotUpdateDialog(const QString &text) +{ + // update pushbuttons + if (text.length() == 0) { + m_createTag->setEnabled(false); + m_updateTag->setEnabled(false); + m_removeTag->setEnabled(false); + } + else if (m_tagList.contains(text)) { + m_createTag->setEnabled(false); + m_updateTag->setEnabled(true); + m_removeTag->setEnabled(true); + } + else { + m_createTag->setEnabled(true); + m_updateTag->setEnabled(false); + m_removeTag->setEnabled(false); + } +} + +void HgTagDialog::slotSwitch() +{ + HgWrapper *hgWrapper = HgWrapper::instance(); + QString out; + QStringList args; + args << QLatin1String("-c"); + args << m_tagComboBox->currentText(); + if (hgWrapper->executeCommand(QLatin1String("update"), args, out)) { + //KMessageBox::information(this, i18n("Updated working directory!")); + done(KDialog::Ok); + } + else { + KMessageBox::error(this, i18n("Some error occurred")); + } +} + +void HgTagDialog::slotRemoveTag() +{ + HgWrapper *hgWrapper = HgWrapper::instance(); + QString out; + QStringList args; + args << QLatin1String("--remove"); + args << m_tagComboBox->currentText(); + if (hgWrapper->executeCommand(QLatin1String("tag"), args, out)) { + //KMessageBox::information(this, i18n("Removed tag successfully!")); + done(KDialog::Ok); + } + else { + KMessageBox::error(this, i18n("Some error occurred")); + } +} + +void HgTagDialog::slotCreateTag() +{ + HgWrapper *hgWrapper = HgWrapper::instance(); + QString out; + QStringList args; + args << m_tagComboBox->currentText(); + if (hgWrapper->executeCommand(QLatin1String("tag"), args, out)) { + KMessageBox::information(this, i18n("Created tag successfully!")); + done(KDialog::Ok); + } + else { + KMessageBox::error(this, i18n("Some error occurred")); + } +} + +#include "moc_tagdialog.cpp" + diff --git a/dolphin/plugins/hg/tagdialog.h b/dolphin/plugins/hg/tagdialog.h new file mode 100644 index 00000000..f70c660e --- /dev/null +++ b/dolphin/plugins/hg/tagdialog.h @@ -0,0 +1,58 @@ +/*************************************************************************** + * Copyright (C) 2011 by Vishesh Yadav * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef HGTAGDIALOG_H +#define HGTAGDIALOG_H + +#include +#include + +class KComboBox; +class KPushButton; + +/** + * Dialog to create/delete/list tags and update working directory to revision + * represented by a specific tag. + */ +class HgTagDialog : public KDialog +{ + Q_OBJECT + +public: + HgTagDialog(QWidget *parent = 0); + +public slots: + void slotUpdateDialog(const QString &text); + void slotCreateTag(); + void slotSwitch(); + void slotRemoveTag(); + +private: + void updateInitialDialog(); + +private: + KComboBox *m_tagComboBox; + KPushButton *m_createTag; + KPushButton *m_updateTag; + KPushButton *m_removeTag; + QStringList m_tagList; +}; + +#endif // HGTAGDIALOG_H + diff --git a/dolphin/plugins/hg/updatedialog.cpp b/dolphin/plugins/hg/updatedialog.cpp new file mode 100644 index 00000000..f0e17a80 --- /dev/null +++ b/dolphin/plugins/hg/updatedialog.cpp @@ -0,0 +1,155 @@ +/*************************************************************************** + * Copyright (C) 2011 by Vishesh Yadav * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "updatedialog.h" +#include "hgwrapper.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +HgUpdateDialog::HgUpdateDialog(QWidget *parent): + KDialog(parent, Qt::Dialog) +{ + // dialog properties + this->setCaption(i18nc("@title:window", + "Hg Update")); + this->setButtons(KDialog::None); + this->setButtons(KDialog::Ok | KDialog::Cancel); + this->setDefaultButton(KDialog::Ok); + this->setButtonText(KDialog::Ok, i18nc("@action:button", "Update")); + + // UI + QGroupBox *selectGroup = new QGroupBox(i18n("New working directory")); + QVBoxLayout *selectLayout = new QVBoxLayout; + m_selectType = new KComboBox; + m_selectFinal = new KComboBox; + m_selectType->addItem(i18n("Branch")); + m_selectType->addItem(i18n("Tag")); + m_selectType->addItem(i18n("Changeset/Revision")); + selectLayout->addWidget(m_selectType); + selectLayout->addWidget(m_selectFinal); + selectGroup->setLayout(selectLayout); + + QGroupBox *infoGroup = new QGroupBox(i18n("Current Parent")); + QVBoxLayout *infoLayout = new QVBoxLayout; + m_currentInfo = new QLabel; + infoLayout->addWidget(m_currentInfo); + infoGroup->setLayout(infoLayout); + + QGroupBox *optionGroup = new QGroupBox(i18n("Options")); + QVBoxLayout *optionLayout = new QVBoxLayout; + m_discardChanges = new QCheckBox("Discard uncommitted changes"); + m_discardChanges->setCheckState(Qt::Unchecked); + optionLayout->addWidget(m_discardChanges); + optionGroup->setLayout(optionLayout); + + QFrame *frame = new QFrame; + QVBoxLayout *mainLayout = new QVBoxLayout; + mainLayout->addWidget(infoGroup); + mainLayout->addWidget(selectGroup); + mainLayout->addWidget(optionGroup); + frame->setLayout(mainLayout); + + slotUpdateDialog(0); + setMainWidget(frame); + + // connections + connect(m_selectType, SIGNAL(currentIndexChanged(int)), this, + SLOT(slotUpdateDialog(int))); +} + +void HgUpdateDialog::slotUpdateDialog(int index) +{ + HgWrapper *hgWrapper = HgWrapper::instance(); + m_selectFinal->clear(); + if (index == 0) { + m_updateTo = ToBranch; + m_selectFinal->setEditable(false); + m_selectFinal->addItems(hgWrapper->getBranches()); + } + else if (index == 1) { + m_updateTo = ToTag; + m_selectFinal->setEditable(false); + m_selectFinal->addItems(hgWrapper->getTags()); + } + else if (index == 2) { + m_updateTo = ToRevision; + m_selectFinal->setEditable(true); + } + m_selectFinal->setFocus(); + + /// get parents of current working directory + /// more precise informtaion using 'hg summary' + /// but no proper way to retrieve needed data + QString output; + QStringList args; + args << QLatin1String("--template"); + args << QLatin1String("{rev}:{node|short} ({branch})\n"); + hgWrapper->executeCommand(QLatin1String("parents"), args, output); + output.replace(QLatin1String("\n"), QLatin1String("
")); + if (output.contains(QLatin1String("()"))) { + output.replace(QLatin1String("()"), QLatin1String("(default)")); + } + m_currentInfo->setText(output); +} + +void HgUpdateDialog::done(int r) +{ + if (r == KDialog::Accepted) { + QStringList args; + // Should we discard uncommitted changes + if (m_discardChanges->checkState() == Qt::Checked) { + args << "-C"; + } + else { + args << "-c"; + } + if (m_updateTo == ToRevision) { + args << "-r"; + } + + // update to + args << m_selectFinal->currentText(); + + // execute mercurial command + HgWrapper *hgw = HgWrapper::instance(); + if (hgw->executeCommandTillFinished(QLatin1String("update"), args)) { + KDialog::done(r); + } + else { + KMessageBox::error(this, i18n("Some error occurred! " + "\nMaybe there are uncommitted changes.")); + } + } + else { + KDialog::done(r); + } +} +#include "moc_updatedialog.cpp" + diff --git a/dolphin/plugins/hg/updatedialog.h b/dolphin/plugins/hg/updatedialog.h new file mode 100644 index 00000000..886f5b6a --- /dev/null +++ b/dolphin/plugins/hg/updatedialog.h @@ -0,0 +1,57 @@ +/*************************************************************************** + * Copyright (C) 2011 by Vishesh Yadav * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef HGUPDATEDIALOG_H +#define HGUPDATEDIALOG_H + +#include +#include + +class KComboBox; +#include +#include + +/** + * Dialog to update working directory to specific revision/changeset/branch/tag. + * Also shows working directory summary. + */ +class HgUpdateDialog : public KDialog +{ + Q_OBJECT + +public: + HgUpdateDialog(QWidget *parent = 0); + +public slots: + void slotUpdateDialog(int index); + +private: + void done(int r); + +private: + enum {ToBranch, ToTag, ToRevision} m_updateTo; + KComboBox *m_selectType; + KComboBox *m_selectFinal; + QLabel *m_currentInfo; + QStringList m_selectList; + QCheckBox *m_discardChanges; +}; + +#endif // HGUPDATEDIALOG_H + diff --git a/dolphin/plugins/svn/CMakeLists.txt b/dolphin/plugins/svn/CMakeLists.txt new file mode 100644 index 00000000..f69b529c --- /dev/null +++ b/dolphin/plugins/svn/CMakeLists.txt @@ -0,0 +1,24 @@ +project(fileviewsvnplugin) + +set(fileviewsvnplugin_SRCS fileviewsvnplugin.cpp) + +kde4_add_kcfg_files(fileviewsvnplugin_SRCS fileviewsvnpluginsettings.kcfgc) + +kde4_add_plugin(fileviewsvnplugin ${fileviewsvnplugin_SRCS}) + +target_link_libraries(fileviewsvnplugin ${KDE4_KIO_LIBS} konq) + +install( + FILES fileviewsvnplugin.desktop + DESTINATION ${KDE4_SERVICES_INSTALL_DIR} +) + +install( + FILES fileviewsvnpluginsettings.kcfg + DESTINATION ${KDE4_KCFG_INSTALL_DIR} +) + +install( + TARGETS fileviewsvnplugin + DESTINATION ${KDE4_PLUGIN_INSTALL_DIR} +) diff --git a/dolphin/plugins/svn/Messages.sh b/dolphin/plugins/svn/Messages.sh new file mode 100644 index 00000000..9e687755 --- /dev/null +++ b/dolphin/plugins/svn/Messages.sh @@ -0,0 +1,3 @@ +#! /bin/sh +$EXTRACTRC *.kcfg >> rc.cpp +$XGETTEXT *.cpp -o $podir/fileviewsvnplugin.pot diff --git a/dolphin/plugins/svn/fileviewsvnplugin.cpp b/dolphin/plugins/svn/fileviewsvnplugin.cpp new file mode 100644 index 00000000..594cde6f --- /dev/null +++ b/dolphin/plugins/svn/fileviewsvnplugin.cpp @@ -0,0 +1,431 @@ +/*************************************************************************** + * Copyright (C) 2009-2011 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "fileviewsvnplugin.h" + +#include "fileviewsvnpluginsettings.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +K_PLUGIN_FACTORY(FileViewSvnPluginFactory, registerPlugin();) +K_EXPORT_PLUGIN(FileViewSvnPluginFactory("fileviewsvnplugin")) + +FileViewSvnPlugin::FileViewSvnPlugin(QObject* parent, const QList& args) : + KVersionControlPlugin(parent), + m_pendingOperation(false), + m_versionInfoHash(), + m_updateAction(0), + m_commitAction(0), + m_addAction(0), + m_removeAction(0), + m_showUpdatesAction(0), + m_command(), + m_arguments(), + m_errorMsg(), + m_operationCompletedMsg(), + m_contextDir(), + m_contextItems(), + m_process(), + m_tempFile() +{ + Q_UNUSED(args); + + m_updateAction = new KAction(this); + m_updateAction->setIcon(KIcon("view-refresh")); + m_updateAction->setText(i18nc("@item:inmenu", "SVN Update")); + connect(m_updateAction, SIGNAL(triggered()), + this, SLOT(updateFiles())); + + m_commitAction = new KAction(this); + m_commitAction->setIcon(KIcon("svn-commit")); + m_commitAction->setText(i18nc("@item:inmenu", "SVN Commit...")); + connect(m_commitAction, SIGNAL(triggered()), + this, SLOT(commitFiles())); + + m_addAction = new KAction(this); + m_addAction->setIcon(KIcon("list-add")); + m_addAction->setText(i18nc("@item:inmenu", "SVN Add")); + connect(m_addAction, SIGNAL(triggered()), + this, SLOT(addFiles())); + + m_removeAction = new KAction(this); + m_removeAction->setIcon(KIcon("list-remove")); + m_removeAction->setText(i18nc("@item:inmenu", "SVN Delete")); + connect(m_removeAction, SIGNAL(triggered()), + this, SLOT(removeFiles())); + + m_showUpdatesAction = new KAction(this); + m_showUpdatesAction->setCheckable(true); + m_showUpdatesAction->setText(i18nc("@item:inmenu", "Show SVN Updates")); + m_showUpdatesAction->setChecked(FileViewSvnPluginSettings::showUpdates()); + connect(m_showUpdatesAction, SIGNAL(toggled(bool)), + this, SLOT(slotShowUpdatesToggled(bool))); + connect(this, SIGNAL(setShowUpdatesChecked(bool)), + m_showUpdatesAction, SLOT(setChecked(bool))); + + connect(&m_process, SIGNAL(finished(int, QProcess::ExitStatus)), + this, SLOT(slotOperationCompleted(int, QProcess::ExitStatus))); + connect(&m_process, SIGNAL(error(QProcess::ProcessError)), + this, SLOT(slotOperationError())); +} + +FileViewSvnPlugin::~FileViewSvnPlugin() +{ +} + +QString FileViewSvnPlugin::fileName() const +{ + return QLatin1String(".svn"); +} + +bool FileViewSvnPlugin::beginRetrieval(const QString& directory) +{ + Q_ASSERT(directory.endsWith(QLatin1Char('/'))); + + // Clear all entries for this directory including the entries + // for sub directories + QMutableHashIterator it(m_versionInfoHash); + while (it.hasNext()) { + it.next(); + if (it.key().startsWith(directory)) { + it.remove(); + } + } + + QStringList arguments; + arguments << QLatin1String("status"); + if (FileViewSvnPluginSettings::showUpdates()) { + arguments << QLatin1String("--show-updates"); + } + arguments << QLatin1String("--no-ignore") << directory; + + QProcess process; + process.start(QLatin1String("svn"), arguments); + while (process.waitForReadyRead()) { + char buffer[1024]; + while (process.readLine(buffer, sizeof(buffer)) > 0) { + ItemVersion version = NormalVersion; + QString filePath(buffer); + + switch (buffer[0]) { + case 'I': + case '?': version = UnversionedVersion; break; + case 'M': version = LocallyModifiedVersion; break; + case 'A': version = AddedVersion; break; + case 'D': version = RemovedVersion; break; + case 'C': version = ConflictingVersion; break; + default: + if (filePath.contains('*')) { + version = UpdateRequiredVersion; + } + break; + } + + // Only values with a different version as 'NormalVersion' + // are added to the hash table. If a value is not in the + // hash table, it is automatically defined as 'NormalVersion' + // (see FileViewSvnPlugin::itemVersion()). + if (version != NormalVersion) { + int pos = filePath.indexOf('/'); + const int length = filePath.length() - pos - 1; + filePath = filePath.mid(pos, length); + if (!filePath.isEmpty()) { + m_versionInfoHash.insert(filePath, version); + } + } + } + } + if ((process.exitCode() != 0 || process.exitStatus() != QProcess::NormalExit)) { + if (FileViewSvnPluginSettings::showUpdates()) { + // Network update failed. Unset ShowUpdates option, which triggers a refresh + emit infoMessage(i18nc("@info:status", "SVN status update failed. Disabling Option " + "\"Show SVN Updates\".")); + emit setShowUpdatesChecked(false); + // this is no fail, we just try again differently + // furthermore returning false shows an error message that would override our info + return true; + } else { + return false; + } + } + + return true; +} + +void FileViewSvnPlugin::endRetrieval() +{ +} + +KVersionControlPlugin::ItemVersion FileViewSvnPlugin::itemVersion(const KFileItem& item) const +{ + const QString itemUrl = item.localPath(); + if (m_versionInfoHash.contains(itemUrl)) { + return m_versionInfoHash.value(itemUrl); + } + + if (!item.isDir()) { + // files that have not been listed by 'svn status' (= m_versionInfoHash) + // are under version control per definition + return NormalVersion; + } + + // The item is a directory. Check whether an item listed by 'svn status' (= m_versionInfoHash) + // is part of this directory. In this case a local modification should be indicated in the + // directory already. + QHash::const_iterator it = m_versionInfoHash.constBegin(); + while (it != m_versionInfoHash.constEnd()) { + if (it.key().startsWith(itemUrl)) { + const ItemVersion version = m_versionInfoHash.value(it.key()); + if (version == LocallyModifiedVersion) { + return LocallyModifiedVersion; + } + } + ++it; + } + + return NormalVersion; +} + +QList FileViewSvnPlugin::actions(const KFileItemList& items) const +{ + if (items.count() == 1 && items.first().isDir()) { + const QString directory = items.first().url().path(KUrl::AddTrailingSlash); + return directoryActions(directory); + } + + foreach (const KFileItem& item, items) { + m_contextItems.append(item); + } + m_contextDir.clear(); + + const bool noPendingOperation = !m_pendingOperation; + if (noPendingOperation) { + // iterate all items and check the version version to know which + // actions can be enabled + const int itemsCount = items.count(); + int versionedCount = 0; + int editingCount = 0; + foreach (const KFileItem& item, items) { + const ItemVersion version = itemVersion(item); + if (version != UnversionedVersion) { + ++versionedCount; + } + + switch (version) { + case LocallyModifiedVersion: + case ConflictingVersion: + ++editingCount; + break; + default: + break; + } + } + m_commitAction->setEnabled(editingCount > 0); + m_addAction->setEnabled(versionedCount == 0); + m_removeAction->setEnabled(versionedCount == itemsCount); + } else { + m_commitAction->setEnabled(false); + m_addAction->setEnabled(false); + m_removeAction->setEnabled(false); + } + m_updateAction->setEnabled(noPendingOperation); + + QList actions; + actions.append(m_updateAction); + actions.append(m_commitAction); + actions.append(m_addAction); + actions.append(m_removeAction); + actions.append(m_showUpdatesAction); + return actions; +} + + +void FileViewSvnPlugin::updateFiles() +{ + execSvnCommand("update", QStringList(), + i18nc("@info:status", "Updating SVN repository..."), + i18nc("@info:status", "Update of SVN repository failed."), + i18nc("@info:status", "Updated SVN repository.")); +} + +void FileViewSvnPlugin::commitFiles() +{ + KDialog dialog(0, Qt::Dialog); + + KVBox* box = new KVBox(&dialog); + new QLabel(i18nc("@label", "Description:"), box); + QPlainTextEdit* editor = new QPlainTextEdit(box); + + dialog.setMainWidget(box); + dialog.setCaption(i18nc("@title:window", "SVN Commit")); + dialog.setButtons(KDialog::Ok | KDialog::Cancel); + dialog.setDefaultButton(KDialog::Ok); + dialog.setButtonText(KDialog::Ok, i18nc("@action:button", "Commit")); + + KConfigGroup dialogConfig(KSharedConfig::openConfig("dolphinrc"), + "SvnCommitDialog"); + dialog.restoreDialogSize(dialogConfig); + + if (dialog.exec() == QDialog::Accepted) { + // Write the commit description into a temporary file, so + // that it can be read by the command "svn commit -F". The temporary + // file must stay alive until slotOperationCompleted() is invoked and will + // be destroyed when the version plugin is destructed. + if (!m_tempFile.open()) { + emit errorMessage(i18nc("@info:status", "Commit of SVN changes failed.")); + return; + } + + QTextStream out(&m_tempFile); + const QString fileName = m_tempFile.fileName(); + out << editor->toPlainText(); + m_tempFile.close(); + + QStringList arguments; + arguments << "-F" << fileName; + execSvnCommand("commit", arguments, + i18nc("@info:status", "Committing SVN changes..."), + i18nc("@info:status", "Commit of SVN changes failed."), + i18nc("@info:status", "Committed SVN changes.")); + } + + dialog.saveDialogSize(dialogConfig, KConfigBase::Persistent); +} + +void FileViewSvnPlugin::addFiles() +{ + execSvnCommand(QLatin1String("add"), QStringList(), + i18nc("@info:status", "Adding files to SVN repository..."), + i18nc("@info:status", "Adding of files to SVN repository failed."), + i18nc("@info:status", "Added files to SVN repository.")); +} + +void FileViewSvnPlugin::removeFiles() +{ + execSvnCommand(QLatin1String("remove"), QStringList(), + i18nc("@info:status", "Removing files from SVN repository..."), + i18nc("@info:status", "Removing of files from SVN repository failed."), + i18nc("@info:status", "Removed files from SVN repository.")); +} + +void FileViewSvnPlugin::slotOperationCompleted(int exitCode, QProcess::ExitStatus exitStatus) +{ + m_pendingOperation = false; + + if ((exitStatus != QProcess::NormalExit) || (exitCode != 0)) { + emit errorMessage(m_errorMsg); + } else if (m_contextItems.isEmpty()) { + emit operationCompletedMessage(m_operationCompletedMsg); + emit itemVersionsChanged(); + } else { + startSvnCommandProcess(); + } +} + +void FileViewSvnPlugin::slotOperationError() +{ + // don't do any operation on other items anymore + m_contextItems.clear(); + m_pendingOperation = false; + + emit errorMessage(m_errorMsg); +} + +void FileViewSvnPlugin::slotShowUpdatesToggled(bool checked) +{ + FileViewSvnPluginSettings* settings = FileViewSvnPluginSettings::self(); + Q_ASSERT(settings != 0); + settings->setShowUpdates(checked); + settings->writeConfig(); + + emit itemVersionsChanged(); +} + +void FileViewSvnPlugin::execSvnCommand(const QString& svnCommand, + const QStringList& arguments, + const QString& infoMsg, + const QString& errorMsg, + const QString& operationCompletedMsg) +{ + emit infoMessage(infoMsg); + + m_command = svnCommand; + m_arguments = arguments; + m_errorMsg = errorMsg; + m_operationCompletedMsg = operationCompletedMsg; + + startSvnCommandProcess(); +} + +void FileViewSvnPlugin::startSvnCommandProcess() +{ + Q_ASSERT(m_process.state() == QProcess::NotRunning); + m_pendingOperation = true; + + const QString program(QLatin1String("svn")); + QStringList arguments; + arguments << m_command << m_arguments; + if (!m_contextDir.isEmpty()) { + arguments << m_contextDir; + m_contextDir.clear(); + } else { + const KFileItem item = m_contextItems.takeLast(); + arguments << item.localPath(); + // the remaining items of m_contextItems will be executed + // after the process has finished (see slotOperationFinished()) + } + m_process.start(program, arguments); +} + +QList FileViewSvnPlugin::directoryActions(const QString& directory) const +{ + m_contextDir = directory; + m_contextItems.clear(); + + // Only enable the SVN actions if no SVN commands are + // executed currently (see slotOperationCompleted() and + // startSvnCommandProcess()). + const bool enabled = !m_pendingOperation; + m_updateAction->setEnabled(enabled); + m_commitAction->setEnabled(enabled); + + QList actions; + actions.append(m_updateAction); + actions.append(m_commitAction); + actions.append(m_showUpdatesAction); + return actions; +} + +#include "moc_fileviewsvnplugin.cpp" diff --git a/dolphin/plugins/svn/fileviewsvnplugin.desktop b/dolphin/plugins/svn/fileviewsvnplugin.desktop new file mode 100644 index 00000000..cdceb515 --- /dev/null +++ b/dolphin/plugins/svn/fileviewsvnplugin.desktop @@ -0,0 +1,53 @@ +[Desktop Entry] +Type=Service +Name=Subversion +Name[ar]=Subversion +Name[ast]=Subversion +Name[bg]=Subversion +Name[bs]=Subversion +Name[ca]=Subversion +Name[ca@valencia]=Subversion +Name[cs]=Subversion +Name[da]=Subversion +Name[de]=Subversion +Name[el]=Subversion +Name[en_GB]=Subversion +Name[es]=Subversion +Name[et]=Subversion +Name[fi]=Subversion +Name[fr]=Subversion +Name[ga]=Subversion +Name[gl]=Subversion +Name[hu]=Subversion +Name[it]=Subversion +Name[kk]=Subversion +Name[km]=កំណែ​រង​ +Name[ko]=서브버전 +Name[lt]=Subversion +Name[mr]=सबव्हर्जन +Name[nb]=Subversion +Name[nds]=Subversion +Name[nl]=Subversion +Name[nn]=Subversion +Name[pa]=ਸਬ-ਵਰਜਨ +Name[pl]=Subversion +Name[pt]=Subversion +Name[pt_BR]=Subversion +Name[ro]=Subversion +Name[ru]=Subversion +Name[sk]=Subversion +Name[sl]=Subversion +Name[sr]=Субверзија +Name[sr@ijekavian]=Субверзија +Name[sr@ijekavianlatin]=Subversion +Name[sr@latin]=Subversion +Name[sv]=Subversion +Name[tr]=Subversion +Name[ug]=Subversion +Name[uk]=Subversion +Name[x-test]=xxSubversionxx +Name[zh_CN]=Subversion +Name[zh_TW]=Subversion +X-KDE-ServiceTypes=FileViewVersionControlPlugin +MimeType=text/plain; +X-KDE-Library=fileviewsvnplugin diff --git a/dolphin/plugins/svn/fileviewsvnplugin.h b/dolphin/plugins/svn/fileviewsvnplugin.h new file mode 100644 index 00000000..24b7f0e3 --- /dev/null +++ b/dolphin/plugins/svn/fileviewsvnplugin.h @@ -0,0 +1,102 @@ +/*************************************************************************** + * Copyright (C) 2009-2011 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef FILEVIEWSVNPLUGIN_H +#define FILEVIEWSVNPLUGIN_H + +#include +#include +#include +#include +#include + +/** + * @brief Subversion implementation for the KVersionControlPlugin interface. + */ +class FileViewSvnPlugin : public KVersionControlPlugin +{ + Q_OBJECT + +public: + FileViewSvnPlugin(QObject* parent, const QList& args); + virtual ~FileViewSvnPlugin(); + virtual QString fileName() const; + virtual bool beginRetrieval(const QString& directory); + virtual void endRetrieval(); + virtual ItemVersion itemVersion(const KFileItem& item) const; + virtual QList actions(const KFileItemList& items) const; + +signals: + /// Invokes m_showUpdatesAction->setChecked(checked) on the UI thread. + void setShowUpdatesChecked(bool checked); +private slots: + void updateFiles(); + void commitFiles(); + void addFiles(); + void removeFiles(); + + void slotOperationCompleted(int exitCode, QProcess::ExitStatus exitStatus); + void slotOperationError(); + + void slotShowUpdatesToggled(bool checked); + +private: + /** + * Executes the command "svn {svnCommand}" for the files that have been + * set by getting the context menu actions (see contextMenuActions()). + * @param infoMsg Message that should be shown before the command is executed. + * @param errorMsg Message that should be shown if the execution of the command + * has been failed. + * @param operationCompletedMsg + * Message that should be shown if the execution of the command + * has been completed successfully. + */ + void execSvnCommand(const QString& svnCommand, + const QStringList& arguments, + const QString& infoMsg, + const QString& errorMsg, + const QString& operationCompletedMsg); + + void startSvnCommandProcess(); + + QList directoryActions(const QString& directory) const; + +private: + bool m_pendingOperation; + QHash m_versionInfoHash; + + QAction* m_updateAction; + QAction* m_commitAction; + QAction* m_addAction; + QAction* m_removeAction; + QAction* m_showUpdatesAction; + + QString m_command; + QStringList m_arguments; + QString m_errorMsg; + QString m_operationCompletedMsg; + + mutable QString m_contextDir; + mutable KFileItemList m_contextItems; + + QProcess m_process; + QTemporaryFile m_tempFile; +}; +#endif // FILEVIEWSVNPLUGIN_H + diff --git a/dolphin/plugins/svn/fileviewsvnpluginsettings.kcfg b/dolphin/plugins/svn/fileviewsvnpluginsettings.kcfg new file mode 100644 index 00000000..43987cb0 --- /dev/null +++ b/dolphin/plugins/svn/fileviewsvnpluginsettings.kcfg @@ -0,0 +1,11 @@ + + + + + + + + false + + + diff --git a/dolphin/plugins/svn/fileviewsvnpluginsettings.kcfgc b/dolphin/plugins/svn/fileviewsvnpluginsettings.kcfgc new file mode 100644 index 00000000..0d0e16c0 --- /dev/null +++ b/dolphin/plugins/svn/fileviewsvnpluginsettings.kcfgc @@ -0,0 +1,4 @@ +File=fileviewsvnpluginsettings.kcfg +ClassName=FileViewSvnPluginSettings +Singleton=true +Mutators=true diff --git a/dolphin/src/CMakeLists.txt b/dolphin/src/CMakeLists.txt new file mode 100644 index 00000000..9dbbb4f7 --- /dev/null +++ b/dolphin/src/CMakeLists.txt @@ -0,0 +1,335 @@ +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/libs/konq + ${CMAKE_BINARY_DIR}/libs/konq +) + +if(ENABLE_TESTING) + add_subdirectory(tests) +endif() + +########### next target ############### + +set(dolphinprivate_LIB_SRCS + kitemviews/kfileitemlistview.cpp + kitemviews/kfileitemlistwidget.cpp + kitemviews/kfileitemmodel.cpp + kitemviews/kfileitemmodelrolesupdater.cpp + kitemviews/kitemlistcontainer.cpp + kitemviews/kitemlistcontroller.cpp + kitemviews/kitemlistgroupheader.cpp + kitemviews/kitemlistheader.cpp + kitemviews/kitemlistselectionmanager.cpp + kitemviews/kitemliststyleoption.cpp + kitemviews/kitemlistview.cpp + kitemviews/kitemlistwidget.cpp + kitemviews/kitemmodelbase.cpp + kitemviews/kitemset.cpp + kitemviews/kstandarditem.cpp + kitemviews/kstandarditemlistgroupheader.cpp + kitemviews/kstandarditemlistwidget.cpp + kitemviews/kstandarditemlistview.cpp + kitemviews/kstandarditemmodel.cpp + kitemviews/private/kdirectorycontentscounter.cpp + kitemviews/private/kdirectorycontentscounterworker.cpp + kitemviews/private/kfileitemclipboard.cpp + kitemviews/private/kfileitemmodeldirlister.cpp + kitemviews/private/kfileitemmodelfilter.cpp + kitemviews/private/kitemlistheaderwidget.cpp + kitemviews/private/kitemlistkeyboardsearchmanager.cpp + kitemviews/private/kitemlistroleeditor.cpp + kitemviews/private/kitemlistrubberband.cpp + kitemviews/private/kitemlistselectiontoggle.cpp + kitemviews/private/kitemlistsizehintresolver.cpp + kitemviews/private/kitemlistsmoothscroller.cpp + kitemviews/private/kitemlistviewanimation.cpp + kitemviews/private/kitemlistviewlayouter.cpp + settings/additionalinfodialog.cpp + settings/applyviewpropsjob.cpp + settings/viewmodes/viewmodesettings.cpp + settings/viewpropertiesdialog.cpp + settings/viewpropsprogressinfo.cpp + views/dolphinfileitemlistwidget.cpp + views/dolphinitemlistview.cpp + views/dolphinnewfilemenuobserver.cpp + views/dolphinremoteencoding.cpp + views/dolphinview.cpp + views/dolphinviewactionhandler.cpp + views/draganddrophelper.cpp + views/renamedialog.cpp + views/tooltips/filemetadatatooltip.cpp + views/tooltips/tooltipmanager.cpp + views/versioncontrol/updateitemstatesthread.cpp + views/versioncontrol/versioncontrolobserver.cpp + views/viewmodecontroller.cpp + views/viewproperties.cpp + views/zoomlevelinfo.cpp + dolphinremoveaction.cpp + dolphinnewfilemenu.cpp +) + +kde4_add_kcfg_files(dolphinprivate_LIB_SRCS + settings/dolphin_compactmodesettings.kcfgc + settings/dolphin_directoryviewpropertysettings.kcfgc + settings/dolphin_detailsmodesettings.kcfgc + settings/dolphin_iconsmodesettings.kcfgc + settings/dolphin_generalsettings.kcfgc + settings/dolphin_versioncontrolsettings.kcfgc +) + +add_library(dolphinprivate SHARED ${dolphinprivate_LIB_SRCS}) + +target_link_libraries(dolphinprivate + ${KDE4_KFILE_LIBS} + konq +) + +set_target_properties(dolphinprivate PROPERTIES + VERSION ${GENERIC_LIB_VERSION} + SOVERSION ${GENERIC_LIB_SOVERSION} +) + +generate_export_header(dolphinprivate) + +install( + TARGETS dolphinprivate + ${INSTALL_TARGETS_DEFAULT_ARGS} +) + +########################################## + +set(dolphinpart_SRCS + dolphinpart.cpp + dolphinpart_ext.cpp +) + +kde4_add_plugin(dolphinpart ${dolphinpart_SRCS}) + +target_link_libraries(dolphinpart + ${KDE4_KPARTS_LIBS} + ${KDE4_KFILE_LIBS} + dolphinprivate + konq +) + +install( + TARGETS dolphinpart + DESTINATION ${KDE4_PLUGIN_INSTALL_DIR} +) + +install( + FILES dolphinpart.rc + DESTINATION ${KDE4_DATA_INSTALL_DIR}/dolphinpart +) +install( + FILES dolphinpart.desktop + DESTINATION ${KDE4_SERVICES_INSTALL_DIR} +) +install( + FILES views/versioncontrol/fileviewversioncontrolplugin.desktop + DESTINATION ${KDE4_SERVICETYPES_INSTALL_DIR} +) + +########################################## + +set(dolphin_SRCS + dolphinapplication.cpp + dolphindockwidget.cpp + dolphinmainwindow.cpp + dolphinviewcontainer.cpp + dolphincontextmenu.cpp + dolphinrecenttabsmenu.cpp + dolphintabpage.cpp + filterbar/filterbar.cpp + main.cpp + panels/information/filemetadataconfigurationdialog.cpp + panels/information/informationpanel.cpp + panels/information/informationpanelcontent.cpp + panels/information/pixmapviewer.cpp + panels/places/placespanel.cpp + panels/places/placesitem.cpp + panels/places/placesitemeditdialog.cpp + panels/places/placesitemlistgroupheader.cpp + panels/places/placesitemlistwidget.cpp + panels/places/placesitemmodel.cpp + panels/places/placesitemsignalhandler.cpp + panels/places/placesview.cpp + panels/panel.cpp + panels/folders/foldersitemlistwidget.cpp + panels/folders/treeviewcontextmenu.cpp + panels/folders/folderspanel.cpp + search/dolphinfacetswidget.cpp + search/dolphinsearchbox.cpp + settings/general/behaviorsettingspage.cpp + settings/general/configurepreviewplugindialog.cpp + settings/general/confirmationssettingspage.cpp + settings/general/generalsettingspage.cpp + settings/general/previewssettingspage.cpp + settings/general/statusbarsettingspage.cpp + settings/dolphinsettingsdialog.cpp + settings/navigation/navigationsettingspage.cpp + settings/services/servicessettingspage.cpp + settings/settingspagebase.cpp + settings/serviceitemdelegate.cpp + settings/servicemodel.cpp + settings/startup/startupsettingspage.cpp + settings/trash/trashsettingspage.cpp + settings/viewmodes/dolphinfontrequester.cpp + settings/viewmodes/viewsettingspage.cpp + settings/viewmodes/viewmodesettings.cpp + settings/viewmodes/viewsettingstab.cpp + statusbar/dolphinstatusbar.cpp + statusbar/mountpointobserver.cpp + statusbar/mountpointobservercache.cpp + statusbar/spaceinfoobserver.cpp + statusbar/statusbarspaceinfo.cpp + views/zoomlevelinfo.cpp +) + +kde4_add_kcfg_files(dolphin_SRCS + panels/folders/dolphin_folderspanelsettings.kcfgc + panels/information/dolphin_informationpanelsettings.kcfgc + panels/places/dolphin_placespanelsettings.kcfgc + settings/dolphin_compactmodesettings.kcfgc + settings/dolphin_detailsmodesettings.kcfgc + settings/dolphin_generalsettings.kcfgc + settings/dolphin_iconsmodesettings.kcfgc + search/dolphin_searchsettings.kcfgc + settings/dolphin_versioncontrolsettings.kcfgc +) + +set(dolphin_SRCS ${dolphin_SRCS} panels/terminal/terminalpanel.cpp) + +add_executable(dolphin ${dolphin_SRCS}) + +target_link_libraries(dolphin + ${KDE4_KFILE_LIBS} + ${KDE4_KPARTS_LIBS} + ${KDE4_KCMUTILS_LIBS} + ${KDE4_SOLID_LIBS} + ${KDE4_KMEDIAPLAYER_LIBS} + konq + dolphinprivate +) + +install( + TARGETS dolphin + ${INSTALL_TARGETS_DEFAULT_ARGS} +) + +########################################## + +set(kcm_dolphinviewmodes_PART_SRCS + settings/kcm/kcmdolphinviewmodes.cpp + settings/viewmodes/dolphinfontrequester.cpp + settings/viewmodes/viewmodesettings.cpp + settings/viewmodes/viewsettingstab.cpp + views/zoomlevelinfo.cpp +) + +set(kcm_dolphinnavigation_PART_SRCS + settings/kcm/kcmdolphinnavigation.cpp + settings/navigation/navigationsettingspage.cpp + settings/settingspagebase.cpp +) + +set(kcm_dolphinservices_PART_SRCS + settings/kcm/kcmdolphinservices.cpp + settings/services/servicessettingspage.cpp + settings/settingspagebase.cpp + settings/serviceitemdelegate.cpp + settings/servicemodel.cpp +) + +set(kcm_dolphingeneral_PART_SRCS + settings/kcm/kcmdolphingeneral.cpp + settings/general/behaviorsettingspage.cpp + settings/general/previewssettingspage.cpp + settings/general/configurepreviewplugindialog.cpp + settings/general/confirmationssettingspage.cpp + settings/settingspagebase.cpp + settings/serviceitemdelegate.cpp + settings/servicemodel.cpp +) + +kde4_add_kcfg_files(kcm_dolphinviewmodes_PART_SRCS + settings/dolphin_compactmodesettings.kcfgc + settings/dolphin_directoryviewpropertysettings.kcfgc + settings/dolphin_detailsmodesettings.kcfgc + settings/dolphin_iconsmodesettings.kcfgc + settings/dolphin_generalsettings.kcfgc + settings/dolphin_versioncontrolsettings.kcfgc +) + +kde4_add_kcfg_files(kcm_dolphinnavigation_PART_SRCS + settings/dolphin_generalsettings.kcfgc +) + +kde4_add_kcfg_files(kcm_dolphinservices_PART_SRCS + settings/dolphin_generalsettings.kcfgc + settings/dolphin_versioncontrolsettings.kcfgc +) + +kde4_add_kcfg_files(kcm_dolphingeneral_PART_SRCS + settings/dolphin_generalsettings.kcfgc +) + +kde4_add_plugin(kcm_dolphinviewmodes ${kcm_dolphinviewmodes_PART_SRCS}) +kde4_add_plugin(kcm_dolphinnavigation ${kcm_dolphinnavigation_PART_SRCS}) +kde4_add_plugin(kcm_dolphinservices ${kcm_dolphinservices_PART_SRCS}) +kde4_add_plugin(kcm_dolphingeneral ${kcm_dolphingeneral_PART_SRCS}) + +target_link_libraries(kcm_dolphinviewmodes ${KDE4_KDEUI_LIBS} ${KDE4_KFILE_LIBS} dolphinprivate) +target_link_libraries(kcm_dolphinnavigation ${KDE4_KDEUI_LIBS} ${KDE4_KFILE_LIBS} dolphinprivate) +target_link_libraries(kcm_dolphinservices ${KDE4_KDEUI_LIBS} ${KDE4_KFILE_LIBS} ${KDE4_KIO_LIBS} dolphinprivate) +target_link_libraries(kcm_dolphingeneral ${KDE4_KDEUI_LIBS} ${KDE4_KFILE_LIBS} ${KDE4_KIO_LIBS} dolphinprivate) + +install( + TARGETS + kcm_dolphinviewmodes + kcm_dolphinnavigation + kcm_dolphinservices + kcm_dolphingeneral + DESTINATION ${KDE4_PLUGIN_INSTALL_DIR} +) + +######################################### + +kde4_add_plugin(kio_filenamesearch search/filenamesearchprotocol.cpp) + +target_link_libraries(kio_filenamesearch ${KDE4_KIO_LIBS}) + +install( + TARGETS kio_filenamesearch + DESTINATION ${KDE4_PLUGIN_INSTALL_DIR} +) + +########### install files ############### + +install( + PROGRAMS dolphin.desktop + DESTINATION ${KDE4_XDG_APPS_INSTALL_DIR} +) +install( + FILES + settings/dolphin_directoryviewpropertysettings.kcfg + settings/dolphin_generalsettings.kcfg + settings/dolphin_compactmodesettings.kcfg + settings/dolphin_iconsmodesettings.kcfg + settings/dolphin_detailsmodesettings.kcfg + settings/dolphin_versioncontrolsettings.kcfg + DESTINATION ${KDE4_KCFG_INSTALL_DIR} +) +install( + FILES dolphinui.rc + DESTINATION ${KDE4_DATA_INSTALL_DIR}/dolphin +) +install( + FILES + search/filenamesearch.protocol + settings/kcm/kcmdolphinviewmodes.desktop + settings/kcm/kcmdolphinnavigation.desktop + settings/kcm/kcmdolphinservices.desktop + settings/kcm/kcmdolphingeneral.desktop + DESTINATION ${KDE4_SERVICES_INSTALL_DIR} +) diff --git a/dolphin/src/Messages.sh b/dolphin/src/Messages.sh new file mode 100755 index 00000000..1b46bbbb --- /dev/null +++ b/dolphin/src/Messages.sh @@ -0,0 +1,4 @@ +#! /usr/bin/env bash +$EXTRACTRC `find . -name \*.ui -o -name \*.rc -o -name \*.kcfg` >> rc.cpp +$XGETTEXT `find . -name \*.cpp` -o $podir/dolphin.pot +rm -f rc.cpp diff --git a/dolphin/src/dolphin.desktop b/dolphin/src/dolphin.desktop new file mode 100755 index 00000000..747b3fbf --- /dev/null +++ b/dolphin/src/dolphin.desktop @@ -0,0 +1,189 @@ +[Desktop Entry] +Name=Dolphin +Name[af]=Dolphin +Name[ar]=دولفين +Name[as]=Dolphin +Name[ast]=Dolphin +Name[be]=Dolphin +Name[be@latin]=Dolphin +Name[bg]=Dolphin +Name[bn]=ডলফিন +Name[bn_IN]=Dolphin +Name[bs]=Delfin +Name[ca]=Dolphin +Name[ca@valencia]=Dolphin +Name[cs]=Dolphin +Name[csb]=Dolphin +Name[da]=Dolphin +Name[de]=Dolphin +Name[el]=Dolphin +Name[en_GB]=Dolphin +Name[eo]=Dolphin +Name[es]=Dolphin +Name[et]=Dolphin +Name[eu]=Dolphin +Name[fi]=Dolphin +Name[fr]=Dolphin +Name[fy]=Dolfyn +Name[ga]=Dolphin +Name[gl]=Dolphin +Name[gu]=ડોલ્ફિન +Name[he]=Dolphin +Name[hi]=डॉल्फ़िन +Name[hne]=डाल्फिन +Name[hr]=Dolphin +Name[hsb]=Dolphin +Name[hu]=Dolphin +Name[ia]=Dolphin +Name[id]=Dolphin +Name[is]=Dolphin +Name[it]=Dolphin +Name[ja]=Dolphin +Name[ka]=Dolphin +Name[kk]=Dolphin +Name[km]=Dolphin +Name[kn]=ಡಾಲ್ಫಿನ್ +Name[ko]=Dolphin +Name[ku]=Dolphin +Name[lt]=Dolphin +Name[lv]=Dolphin +Name[mai]=डाल्फिन +Name[mk]=Делфин +Name[ml]=ഡോള്‍ഫിന്‍ +Name[mr]=डॉल्फिन +Name[ms]=Dolphin +Name[nb]=Dolphin +Name[nds]=Dolphin +Name[ne]=डल्फिन +Name[nl]=Dolphin +Name[nn]=Dolphin +Name[oc]=Dolphin +Name[or]=ଡଲଫିନ +Name[pa]=ਡਾਲਫਿਨ +Name[pl]=Dolphin +Name[pt]=Dolphin +Name[pt_BR]=Dolphin +Name[ro]=Dolphin +Name[ru]=Dolphin +Name[se]=Dolphin +Name[si]=ඩොල්ෆින් +Name[sk]=Dolphin +Name[sl]=Dolphin +Name[sr]=Делфин +Name[sr@ijekavian]=Делфин +Name[sr@ijekavianlatin]=Dolphin +Name[sr@latin]=Dolphin +Name[sv]=Dolphin +Name[ta]=டால்பின் +Name[te]=డాల్ఫిన్ +Name[tg]=Dolphin +Name[th]=ดอลฟิน +Name[tr]=Dolphin +Name[ug]=Dolphin +Name[uk]=Dolphin +Name[uz]=Dolphin +Name[uz@cyrillic]=Dolphin +Name[vi]=Dolphin +Name[wa]=Dolphin +Name[x-test]=xxDolphinxx +Name[zh_CN]=Dolphin +Name[zh_TW]=Dolphin +Exec=dolphin --icon '%i' --caption '%c' %u +Icon=system-file-manager +Type=Application +X-DocPath=dolphin/index.html +Categories=Qt;KDE;System;FileTools;FileManager; +GenericName=File Manager +GenericName[af]=Lêerbestuurder +GenericName[ar]=مدير ملفات +GenericName[as]=নথিপত্ৰৰ পৰিচালক +GenericName[ast]=Xestor de ficheros +GenericName[be]=Кіраўнік файлаў +GenericName[be@latin]=Kiraŭnik fajłaŭ +GenericName[bg]=Файлов мениджър +GenericName[bn]=ফাইল ম্যানেজার +GenericName[bn_IN]=ফাইল পরিচালন ব্যবস্থা +GenericName[bs]=Menadžer datoteka +GenericName[ca]=Gestor de fitxers +GenericName[ca@valencia]=Gestor de fitxers +GenericName[cs]=Správce souborů +GenericName[csb]=Menadżera lopków +GenericName[da]=Filhåndtering +GenericName[de]=Dateimanager +GenericName[el]=Διαχειριστής αρχείων +GenericName[en_GB]=File Manager +GenericName[eo]=Dosieradministrilo +GenericName[es]=Gestor de archivos +GenericName[et]=Failihaldur +GenericName[eu]=Fitxategi-kudeatzailea +GenericName[fa]=مدیر پرونده +GenericName[fi]=Tiedostonhallinta +GenericName[fr]=Gestionnaire de fichiers +GenericName[fy]=Triembehearder +GenericName[ga]=Bainisteoir Comhad +GenericName[gl]=Xestor de ficheiros +GenericName[gu]=ફાઇલ વ્યવસ્થાપક +GenericName[he]=מנהל קבצים +GenericName[hi]=फ़ाइल प्रबंधक +GenericName[hne]=फाइल प्रबंधक +GenericName[hr]=Upravitelj datoteka +GenericName[hsb]=Datajowy manager +GenericName[hu]=Fájlkezelő +GenericName[ia]=Gerente de file +GenericName[id]=Manajer Berkas +GenericName[is]=Skráastjóri +GenericName[it]=Gestore dei file +GenericName[ja]=ファイルマネージャ +GenericName[ka]=ფაილების მმართველი +GenericName[kk]=Файл менеджері +GenericName[km]=កម្មវិធី​គ្រប់គ្រង​ឯកសារ +GenericName[kn]=ಕಡತ ವ್ಯವಸ್ಥಾಪಕ +GenericName[ko]=파일 관리자 +GenericName[ku]=Rêveberê Pelan +GenericName[lt]=Failų tvarkyklė +GenericName[lv]=Failu pārvaldnieks +GenericName[mai]=फाइल प्रबंधक +GenericName[mk]=Менаџер на датотеки +GenericName[ml]=ഫയല്‍ നടത്തിപ്പുകാരന്‍ +GenericName[mr]=फाईल व्यवस्थापक +GenericName[ms]=Pengurus Fail +GenericName[nb]=Filbehandler +GenericName[nds]=Dateipleger +GenericName[ne]=फाइल प्रबन्धक +GenericName[nl]=Bestandsbeheerder +GenericName[nn]=Filhandsamar +GenericName[oc]=Gestionari de fichièrs +GenericName[or]=ଫାଇଲ ପରିଚାଳକ +GenericName[pa]=ਫਾਇਲ ਮੈਨੇਜਰ +GenericName[pl]=Zarządzanie plikami +GenericName[pt]=Gestor de Ficheiros +GenericName[pt_BR]=Gerenciador de arquivos +GenericName[ro]=Gestionar de fișiere +GenericName[ru]=Диспетчер файлов +GenericName[se]=Fiilagieđahalli +GenericName[si]=ගොනු කළමනාකරු +GenericName[sk]=Správca súborov +GenericName[sl]=Upravljalnik datotek +GenericName[sr]=Менаџер фајлова +GenericName[sr@ijekavian]=Менаџер фајлова +GenericName[sr@ijekavianlatin]=Menadžer fajlova +GenericName[sr@latin]=Menadžer fajlova +GenericName[sv]=Filhanterare +GenericName[ta]=கோப்பு மேலாளர் +GenericName[te]=దస్త్రాల అభికర్త +GenericName[tg]=Мудири файлҳо +GenericName[th]=เครื่องมือจัดการแฟ้ม +GenericName[tr]=Dosya Yönetici +GenericName[ug]=ھۆججەت باشقۇرغۇ +GenericName[uk]=Менеджер файлів +GenericName[uz]=Fayl boshqaruvchisi +GenericName[uz@cyrillic]=Файл бошқарувчиси +GenericName[vi]=Bộ quản lý tập tin +GenericName[wa]=Manaedjeu di fitchîs +GenericName[x-test]=xxFile Managerxx +GenericName[zh_CN]=文件管理器 +GenericName[zh_TW]=檔案管理員 +Terminal=false +MimeType=inode/directory; +InitialPreference=10 +X-KDE-MediaPlayer=informationpanel diff --git a/dolphin/src/dolphinapplication.cpp b/dolphin/src/dolphinapplication.cpp new file mode 100644 index 00000000..da2e1b7a --- /dev/null +++ b/dolphin/src/dolphinapplication.cpp @@ -0,0 +1,106 @@ +/*************************************************************************** + * Copyright (C) 2006-2011 by Peter Penz * + * Copyright (C) 2006 by Holger 'zecke' Freyther * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "dolphinapplication.h" +#include "dolphinmainwindow.h" +#include "dolphin_generalsettings.h" + +#include +#include +#include +#include + +DolphinApplication::DolphinApplication() : + m_mainWindow(0) +{ + KGlobal::locale()->insertCatalog("libkonq"); // Needed for applications using libkonq + + m_mainWindow = new DolphinMainWindow(); + m_mainWindow->setAttribute(Qt::WA_DeleteOnClose); + + KCmdLineArgs* args = KCmdLineArgs::parsedArgs(); + + const int argsCount = args->count(); + + QList urls; + for (int i = 0; i < argsCount; ++i) { + const KUrl url = args->url(i); + if (url.isValid()) { + urls.append(url); + } + } + + bool resetSplitSettings = false; + if (args->isSet("split") && !GeneralSettings::splitView()) { + // Dolphin should be opened with a split view although this is not + // set in the GeneralSettings. Temporary adjust the setting until + // all passed URLs have been opened. + GeneralSettings::setSplitView(true); + resetSplitSettings = true; + + // We need 2 URLs to open Dolphin in split view mode + if (urls.isEmpty()) { // No URL given - Open home URL in all two views + urls.append(GeneralSettings::homeUrl()); + urls.append(GeneralSettings::homeUrl()); + } else if (urls.length() == 1) { // Only 1 URL given - Open given URL in all two views + urls.append(urls.at(0)); + } + } + + if (!urls.isEmpty()) { + if (args->isSet("select")) { + m_mainWindow->openFiles(urls); + } else { + m_mainWindow->openDirectories(urls); + } + } else { + const KUrl homeUrl(GeneralSettings::homeUrl()); + m_mainWindow->openNewActivatedTab(homeUrl); + } + + if (resetSplitSettings) { + GeneralSettings::setSplitView(false); + } + + args->clear(); + + m_mainWindow->show(); +} + +DolphinApplication::~DolphinApplication() +{ +} + +DolphinApplication* DolphinApplication::app() +{ + return qobject_cast(qApp); +} + +void DolphinApplication::restoreSession() +{ + const QString className = KXmlGuiWindow::classNameOfToplevel(1); + if (className == QLatin1String("DolphinMainWindow")) { + m_mainWindow->restore(1); + } else { + kWarning() << "Unknown class " << className << " in session saved data!"; + } +} + +#include "moc_dolphinapplication.cpp" diff --git a/dolphin/src/dolphinapplication.h b/dolphin/src/dolphinapplication.h new file mode 100644 index 00000000..69d07c36 --- /dev/null +++ b/dolphin/src/dolphinapplication.h @@ -0,0 +1,44 @@ +/*************************************************************************** + * Copyright (C) 2006-2011 by Peter Penz * + * Copyright (C) 2006 by Holger 'zecke' Freyther * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef DOLPHIN_APPLICATION_H +#define DOLPHIN_APPLICATION_H + +#include + +class DolphinMainWindow; + +class DolphinApplication : public KApplication +{ + Q_OBJECT + +public: + DolphinApplication(); + virtual ~DolphinApplication(); + + static DolphinApplication* app(); + + void restoreSession(); + +private: + DolphinMainWindow* m_mainWindow; +}; + +#endif diff --git a/dolphin/src/dolphincontextmenu.cpp b/dolphin/src/dolphincontextmenu.cpp new file mode 100644 index 00000000..1ffe6067 --- /dev/null +++ b/dolphin/src/dolphincontextmenu.cpp @@ -0,0 +1,526 @@ +/*************************************************************************** + * Copyright (C) 2006 by Peter Penz (peter.penz@gmx.at) and * + * Cvetoslav Ludmiloff * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "dolphincontextmenu.h" + +#include "dolphinmainwindow.h" +#include "dolphinnewfilemenu.h" +#include "dolphinviewcontainer.h" +#include "dolphin_generalsettings.h" +#include "dolphinremoveaction.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include "views/dolphinview.h" +#include "views/viewmodecontroller.h" + +DolphinContextMenu::DolphinContextMenu(DolphinMainWindow* parent, + const QPoint& pos, + const KFileItem& fileInfo, + const KUrl& baseUrl) : + KMenu(parent), + m_pos(pos), + m_mainWindow(parent), + m_fileInfo(fileInfo), + m_baseUrl(baseUrl), + m_baseFileItem(0), + m_selectedItems(), + m_selectedItemsProperties(0), + m_context(NoContext), + m_copyToMenu(parent), + m_customActions(), + m_command(None), + m_removeAction(0) +{ + // The context menu either accesses the URLs of the selected items + // or the items itself. To increase the performance both lists are cached. + const DolphinView* view = m_mainWindow->activeViewContainer()->view(); + m_selectedItems = view->selectedItems(); +} + +DolphinContextMenu::~DolphinContextMenu() +{ + delete m_selectedItemsProperties; + m_selectedItemsProperties = 0; +} + +void DolphinContextMenu::setCustomActions(const QList& actions) +{ + m_customActions = actions; +} + +DolphinContextMenu::Command DolphinContextMenu::open() +{ + // get the context information + if (m_baseUrl.protocol() == QLatin1String("trash")) { + m_context |= TrashContext; + } + + if (!m_fileInfo.isNull() && !m_selectedItems.isEmpty()) { + m_context |= ItemContext; + // TODO: handle other use cases like devices + desktop files + } + + // open the corresponding popup for the context + if (m_context & TrashContext) { + if (m_context & ItemContext) { + openTrashItemContextMenu(); + } else { + openTrashContextMenu(); + } + } else if (m_context & ItemContext) { + openItemContextMenu(); + } else { + Q_ASSERT(m_context == NoContext); + openViewportContextMenu(); + } + + return m_command; +} + +void DolphinContextMenu::keyPressEvent(QKeyEvent *ev) +{ + if (m_removeAction && ev->key() == Qt::Key_Shift) { + m_removeAction->update(); + } + KMenu::keyPressEvent(ev); +} + +void DolphinContextMenu::keyReleaseEvent(QKeyEvent *ev) +{ + if (m_removeAction && ev->key() == Qt::Key_Shift) { + m_removeAction->update(); + } + KMenu::keyReleaseEvent(ev); +} + +void DolphinContextMenu::openTrashContextMenu() +{ + Q_ASSERT(m_context & TrashContext); + + QAction* emptyTrashAction = new QAction(KIcon("trash-empty"), i18nc("@action:inmenu", "Empty Trash"), this); + KSettings trashConfig("trashrc", KSettings::SimpleConfig); + emptyTrashAction->setEnabled(!trashConfig.value("Status/Empty", true).toBool()); + addAction(emptyTrashAction); + + addCustomActions(); + + QAction* propertiesAction = m_mainWindow->actionCollection()->action("properties"); + addAction(propertiesAction); + + addShowMenuBarAction(); + + if (exec(m_pos) == emptyTrashAction) { + KonqOperations::emptyTrash(m_mainWindow); + } +} + +void DolphinContextMenu::openTrashItemContextMenu() +{ + Q_ASSERT(m_context & TrashContext); + Q_ASSERT(m_context & ItemContext); + + QAction* restoreAction = new QAction(i18nc("@action:inmenu", "Restore"), m_mainWindow); + addAction(restoreAction); + + QAction* deleteAction = m_mainWindow->actionCollection()->action("delete"); + addAction(deleteAction); + + QAction* propertiesAction = m_mainWindow->actionCollection()->action("properties"); + addAction(propertiesAction); + + if (exec(m_pos) == restoreAction) { + KUrl::List selectedUrls; + foreach (const KFileItem &item, m_selectedItems) { + selectedUrls.append(item.url()); + } + + KonqOperations::restoreTrashedItems(selectedUrls, m_mainWindow); + } +} + +void DolphinContextMenu::openItemContextMenu() +{ + Q_ASSERT(!m_fileInfo.isNull()); + + QAction* openParentInNewWindowAction = 0; + QAction* openParentInNewTabAction = 0; + QAction* addToPlacesAction = 0; + const KFileItemListProperties& selectedItemsProps = selectedItemsProperties(); + + if (m_selectedItems.count() == 1) { + if (m_fileInfo.isDir()) { + // setup 'Create New' menu + DolphinNewFileMenu* newFileMenu = new DolphinNewFileMenu(m_mainWindow->actionCollection(), m_mainWindow); + const DolphinView* view = m_mainWindow->activeViewContainer()->view(); + newFileMenu->setViewShowsHiddenFiles(view->hiddenFilesShown()); + newFileMenu->checkUpToDate(); + newFileMenu->setPopupFiles(m_fileInfo.url()); + newFileMenu->setEnabled(selectedItemsProps.supportsWriting()); + connect(newFileMenu, SIGNAL(fileCreated(KUrl)), newFileMenu, SLOT(deleteLater())); + connect(newFileMenu, SIGNAL(directoryCreated(KUrl)), newFileMenu, SLOT(deleteLater())); + + KMenu* menu = newFileMenu->menu(); + menu->setTitle(i18nc("@title:menu Create new folder, file, link, etc.", "Create New")); + menu->setIcon(KIcon("document-new")); + addMenu(menu); + addSeparator(); + + // insert 'Open in new window' and 'Open in new tab' entries + addAction(m_mainWindow->actionCollection()->action("open_in_new_window")); + addAction(m_mainWindow->actionCollection()->action("open_in_new_tab")); + + // insert 'Add to Places' entry + if (!placeExists(m_fileInfo.url())) { + addToPlacesAction = addAction(KIcon("bookmark-new"), + i18nc("@action:inmenu Add selected folder to places", + "Add to Places")); + } + + addSeparator(); + } else if (m_baseUrl.protocol() == "filenamesearch") { + openParentInNewWindowAction = new QAction(KIcon("window-new"), + i18nc("@action:inmenu", + "Open Path in New Window"), + this); + addAction(openParentInNewWindowAction); + + openParentInNewTabAction = new QAction(KIcon("tab-new"), + i18nc("@action:inmenu", + "Open Path in New Tab"), + this); + addAction(openParentInNewTabAction); + + addSeparator(); + } else if (!DolphinView::openItemAsFolderUrl(m_fileInfo).isEmpty()) { + // insert 'Open in new window' and 'Open in new tab' entries + addAction(m_mainWindow->actionCollection()->action("open_in_new_window")); + addAction(m_mainWindow->actionCollection()->action("open_in_new_tab")); + + addSeparator(); + } + } else { + bool selectionHasOnlyDirs = true; + foreach (const KFileItem& item, m_selectedItems) { + const KUrl& url = DolphinView::openItemAsFolderUrl(item); + if (url.isEmpty()) { + selectionHasOnlyDirs = false; + break; + } + } + + if (selectionHasOnlyDirs) { + // insert 'Open in new tab' entry + addAction(m_mainWindow->actionCollection()->action("open_in_new_tabs")); + addSeparator(); + } + } + + insertDefaultItemActions(selectedItemsProps); + + addSeparator(); + + KFileItemActions fileItemActions; + fileItemActions.setItemListProperties(selectedItemsProps); + addServiceActions(fileItemActions); + + addFileItemPluginActions(); + + addVersionControlPluginActions(); + + // insert 'Copy To' and 'Move To' sub menus + if (GeneralSettings::showCopyMoveMenu()) { + m_copyToMenu.setItems(m_selectedItems); + m_copyToMenu.setReadOnly(!selectedItemsProps.supportsWriting()); + m_copyToMenu.addActionsTo(this); + } + + // insert 'Properties...' entry + QAction* propertiesAction = m_mainWindow->actionCollection()->action("properties"); + addAction(propertiesAction); + + QAction* activatedAction = exec(m_pos); + if (activatedAction) { + if (activatedAction == addToPlacesAction) { + const KUrl selectedUrl(m_fileInfo.url()); + if (selectedUrl.isValid()) { + PlacesItemModel model; + const QString text = selectedUrl.fileName(); + PlacesItem* item = model.createPlacesItem(text, selectedUrl); + model.appendItemToGroup(item); + } + } else if (activatedAction == openParentInNewWindowAction) { + m_command = OpenParentFolderInNewWindow; + } else if (activatedAction == openParentInNewTabAction) { + m_command = OpenParentFolderInNewTab; + } + } +} + +void DolphinContextMenu::openViewportContextMenu() +{ + // setup 'Create New' menu + KNewFileMenu* newFileMenu = m_mainWindow->newFileMenu(); + const DolphinView* view = m_mainWindow->activeViewContainer()->view(); + newFileMenu->setViewShowsHiddenFiles(view->hiddenFilesShown()); + newFileMenu->checkUpToDate(); + newFileMenu->setPopupFiles(m_baseUrl); + addMenu(newFileMenu->menu()); + addSeparator(); + + // Insert 'New Window' and 'New Tab' entries. Don't use "open_in_new_window" and + // "open_in_new_tab" here, as the current selection should get ignored. + addAction(m_mainWindow->actionCollection()->action("new_window")); + addAction(m_mainWindow->actionCollection()->action("new_tab")); + + // Insert 'Add to Places' entry if exactly one item is selected + QAction* addToPlacesAction = 0; + if (!placeExists(m_mainWindow->activeViewContainer()->url())) { + addToPlacesAction = addAction(KIcon("bookmark-new"), + i18nc("@action:inmenu Add current folder to places", "Add to Places")); + } + + addSeparator(); + + QAction* pasteAction = createPasteAction(); + addAction(pasteAction); + addSeparator(); + + // Insert service actions + const KFileItemListProperties baseUrlProperties(KFileItemList() << baseFileItem()); + KFileItemActions fileItemActions; + fileItemActions.setItemListProperties(baseUrlProperties); + addServiceActions(fileItemActions); + + addFileItemPluginActions(); + + addVersionControlPluginActions(); + + addCustomActions(); + + QAction* propertiesAction = m_mainWindow->actionCollection()->action("properties"); + addAction(propertiesAction); + + addShowMenuBarAction(); + + QAction* action = exec(m_pos); + if (addToPlacesAction && (action == addToPlacesAction)) { + const DolphinViewContainer* container = m_mainWindow->activeViewContainer(); + if (container->url().isValid()) { + PlacesItemModel model; + PlacesItem* item = model.createPlacesItem(container->placesText(), + container->url()); + model.appendItemToGroup(item); + } + } +} + +void DolphinContextMenu::insertDefaultItemActions(const KFileItemListProperties& properties) +{ + const KActionCollection* collection = m_mainWindow->actionCollection(); + + // Insert 'Cut', 'Copy' and 'Paste' + addAction(collection->action(KStandardAction::name(KStandardAction::Cut))); + addAction(collection->action(KStandardAction::name(KStandardAction::Copy))); + addAction(createPasteAction()); + + addSeparator(); + + // Insert 'Rename' + QAction* renameAction = collection->action("rename"); + addAction(renameAction); + + // Insert 'Move to Trash' and/or 'Delete' + if (properties.supportsDeleting()) { + const bool showDeleteAction = (KGlobal::config()->group("KDE").readEntry("ShowDeleteCommand", false) || + !properties.isLocal()); + const bool showMoveToTrashAction = (properties.isLocal() && + properties.supportsMoving()); + + if (showDeleteAction && showMoveToTrashAction) { + delete m_removeAction; + m_removeAction = 0; + addAction(m_mainWindow->actionCollection()->action("move_to_trash")); + addAction(m_mainWindow->actionCollection()->action("delete")); + } else if (showDeleteAction && !showMoveToTrashAction) { + addAction(m_mainWindow->actionCollection()->action("delete")); + } else { + if (!m_removeAction) { + m_removeAction = new DolphinRemoveAction(this, m_mainWindow->actionCollection()); + } + addAction(m_removeAction); + m_removeAction->update(); + } + } +} + +void DolphinContextMenu::addShowMenuBarAction() +{ + const KActionCollection* ac = m_mainWindow->actionCollection(); + QAction* showMenuBar = ac->action(KStandardAction::name(KStandardAction::ShowMenubar)); + if (!m_mainWindow->menuBar()->isVisible() && !m_mainWindow->toolBar()->isVisible()) { + addSeparator(); + addAction(showMenuBar); + } +} + +bool DolphinContextMenu::placeExists(const KUrl& url) const +{ + PlacesItemModel model; + + const int count = model.count(); + for (int i = 0; i < count; ++i) { + const KUrl placeUrl = model.placesItem(i)->url(); + if (placeUrl.equals(url, KUrl::CompareWithoutTrailingSlash)) { + return true; + } + } + + return false; +} + +QAction* DolphinContextMenu::createPasteAction() +{ + QAction* action = 0; + const bool isDir = !m_fileInfo.isNull() && m_fileInfo.isDir(); + if (isDir && (m_selectedItems.count() == 1)) { + const QPair pasteInfo = KonqOperations::pasteInfo(m_fileInfo.url()); + action = new QAction(KIcon("edit-paste"), i18nc("@action:inmenu", "Paste Into Folder"), this); + action->setEnabled(pasteInfo.first); + connect(action, SIGNAL(triggered()), m_mainWindow, SLOT(pasteIntoFolder())); + } else { + action = m_mainWindow->actionCollection()->action(KStandardAction::name(KStandardAction::Paste)); + } + + return action; +} + +KFileItemListProperties& DolphinContextMenu::selectedItemsProperties() const +{ + if (!m_selectedItemsProperties) { + m_selectedItemsProperties = new KFileItemListProperties(m_selectedItems); + } + return *m_selectedItemsProperties; +} + +KFileItem DolphinContextMenu::baseFileItem() +{ + if (!m_baseFileItem) { + m_baseFileItem = new KFileItem(KFileItem::Unknown, KFileItem::Unknown, m_baseUrl); + } + return *m_baseFileItem; +} + +void DolphinContextMenu::addServiceActions(KFileItemActions& fileItemActions) +{ + fileItemActions.setParentWidget(m_mainWindow); + + // insert 'Open With...' action or sub menu + fileItemActions.addOpenWithActionsTo(this, "DesktopEntryName != 'dolphin'"); + + // insert 'Actions' sub menu + fileItemActions.addServiceActionsTo(this); +} + +void DolphinContextMenu::addFileItemPluginActions() +{ + KFileItemListProperties props; + if (m_selectedItems.isEmpty()) { + props.setItems(KFileItemList() << baseFileItem()); + } else { + props = selectedItemsProperties(); + } + + QString commonMimeType = props.mimeType(); + if (commonMimeType.isEmpty()) { + commonMimeType = QLatin1String("application/octet-stream"); + } + + const KService::List pluginServices = KMimeTypeTrader::self()->query(commonMimeType, "KFileItemAction/Plugin", "exist Library"); + if (pluginServices.isEmpty()) { + return; + } + + const KConfig config("kservicemenurc", KConfig::NoGlobals); + const KConfigGroup showGroup = config.group("Show"); + + foreach (const KSharedPtr& service, pluginServices) { + if (!showGroup.readEntry(service->desktopEntryName(), true)) { + // The plugin has been disabled + continue; + } + + // New API (kdelibs >= 4.6.1) + KAbstractFileItemActionPlugin* abstractPlugin = service->createInstance(); + if (abstractPlugin) { + abstractPlugin->setParent(this); + addActions(abstractPlugin->actions(props, m_mainWindow)); + } + } +} + +void DolphinContextMenu::addVersionControlPluginActions() +{ + const DolphinView* view = m_mainWindow->activeViewContainer()->view(); + const QList versionControlActions = view->versionControlActions(m_selectedItems); + if (!versionControlActions.isEmpty()) { + foreach (QAction* action, versionControlActions) { + addAction(action); + } + addSeparator(); + } +} + +void DolphinContextMenu::addCustomActions() +{ + foreach (QAction* action, m_customActions) { + addAction(action); + } +} + +#include "moc_dolphincontextmenu.cpp" diff --git a/dolphin/src/dolphincontextmenu.h b/dolphin/src/dolphincontextmenu.h new file mode 100644 index 00000000..f067a2fd --- /dev/null +++ b/dolphin/src/dolphincontextmenu.h @@ -0,0 +1,183 @@ +/*************************************************************************** + * Copyright (C) 2006 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef DOLPHINCONTEXTMENU_H +#define DOLPHINCONTEXTMENU_H + +#include +#include +#include +#include +#include + +#include + +#include + +#include + +#include +class DolphinMainWindow; +class KFileItemActions; +class KFileItemListProperties; +class DolphinRemoveAction; + +/** + * @brief Represents the context menu which appears when doing a right + * click on an item or the viewport of the file manager. + * + * Beside static menu entries (e. g. 'Paste' or 'Properties') two + * dynamic sub menus are shown when opening a context menu above + * an item: + * - 'Open With': Contains all applications which are registered to + * open items of the given MIME type. + * - 'Actions': Contains all actions which can be applied to the + * given item. + */ +class DolphinContextMenu : public KMenu +{ + Q_OBJECT + +public: + enum Command + { + None, + OpenParentFolderInNewWindow, + OpenParentFolderInNewTab + }; + + /** + * @parent Pointer to the main window the context menu + * belongs to. + * @pos Position in screen coordinates. + * @fileInfo Pointer to the file item the context menu + * is applied. If 0 is passed, the context menu + * is above the viewport. + * @baseUrl Base URL of the viewport where the context menu + * should be opened. + */ + DolphinContextMenu(DolphinMainWindow* parent, + const QPoint& pos, + const KFileItem& fileInfo, + const KUrl& baseUrl); + + virtual ~DolphinContextMenu(); + + void setCustomActions(const QList& actions); + + /** + * Opens the context menu model and returns the requested + * command, that should be triggered by the caller. If + * Command::None has been returned, either the context-menu + * had been closed without executing an action or an + * already available action from the main-window has been + * executed. + */ + Command open(); + +protected: + virtual void keyPressEvent(QKeyEvent *ev); + virtual void keyReleaseEvent(QKeyEvent *ev); + +private: + void openTrashContextMenu(); + void openTrashItemContextMenu(); + void openItemContextMenu(); + void openViewportContextMenu(); + + void insertDefaultItemActions(const KFileItemListProperties&); + + /** + * Adds the "Show menubar" action to the menu if the + * menubar is hidden. + */ + void addShowMenuBarAction(); + + bool placeExists(const KUrl& url) const; + + QAction* createPasteAction(); + + KFileItemListProperties& selectedItemsProperties() const; + + /** + * Returns the file item for m_baseUrl. + */ + KFileItem baseFileItem(); + + /** + * Adds actions that have been installed as service-menu. + * (see http://techbase.kde.org/index.php?title=Development/Tutorials/Creating_Konqueror_Service_Menus) + */ + void addServiceActions(KFileItemActions& fileItemActions); + + /** + * Adds actions that are provided by a KFileItemActionPlugin. + */ + void addFileItemPluginActions(); + + /** + * Adds actions that are provided by a KVersionControlPlugin. + */ + void addVersionControlPluginActions(); + + /** + * Adds custom actions e.g. like the "[x] Expandable Folders"-action + * provided in the details view. + */ + void addCustomActions(); + +private: + struct Entry + { + int type; + QString name; + QString filePath; // empty for separator + QString templatePath; // same as filePath for template + QString icon; + QString comment; + }; + + enum ContextType + { + NoContext = 0, + ItemContext = 1, + TrashContext = 2 + }; + + QPoint m_pos; + DolphinMainWindow* m_mainWindow; + + KFileItem m_fileInfo; + + KUrl m_baseUrl; + KFileItem* m_baseFileItem; /// File item for m_baseUrl + + KFileItemList m_selectedItems; + mutable KFileItemListProperties* m_selectedItemsProperties; + + int m_context; + KonqCopyToMenu m_copyToMenu; + QList m_customActions; + + Command m_command; + + DolphinRemoveAction* m_removeAction; // Action that represents either 'Move To Trash' or 'Delete' +}; + +#endif diff --git a/dolphin/src/dolphindockwidget.cpp b/dolphin/src/dolphindockwidget.cpp new file mode 100644 index 00000000..0d2124bf --- /dev/null +++ b/dolphin/src/dolphindockwidget.cpp @@ -0,0 +1,94 @@ +/*************************************************************************** + * Copyright (C) 2010 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "dolphindockwidget.h" + +#include + +namespace { + // Disable the 'Floatable' feature, i.e., the possibility to drag the + // dock widget out of the main window. This works around problems like + // https://bugs.kde.org/show_bug.cgi?id=288629 + // https://bugs.kde.org/show_bug.cgi?id=322299 + const QDockWidget::DockWidgetFeatures DefaultDockWidgetFeatures = QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetClosable; +} + + // Empty titlebar for the dock widgets when "Lock Layout" has been activated. +class DolphinDockTitleBar : public QWidget +{ +public: + DolphinDockTitleBar(QWidget* parent = 0) : QWidget(parent) {} + virtual ~DolphinDockTitleBar() {} + + virtual QSize minimumSizeHint() const + { + const int border = style()->pixelMetric(QStyle::PM_DockWidgetTitleBarButtonMargin); + return QSize(border, border); + } + + virtual QSize sizeHint() const + { + return minimumSizeHint(); + } +}; + +DolphinDockWidget::DolphinDockWidget(const QString& title, QWidget* parent, Qt::WindowFlags flags) : + QDockWidget(title, parent, flags), + m_locked(false), + m_dockTitleBar(0) +{ + setFeatures(DefaultDockWidgetFeatures); +} + +DolphinDockWidget::DolphinDockWidget(QWidget* parent, Qt::WindowFlags flags) : + QDockWidget(parent, flags), + m_locked(false), + m_dockTitleBar(0) +{ + setFeatures(DefaultDockWidgetFeatures); +} + +DolphinDockWidget::~DolphinDockWidget() +{ +} + +void DolphinDockWidget::setLocked(bool lock) +{ + if (lock != m_locked) { + m_locked = lock; + + if (lock) { + if (!m_dockTitleBar) { + m_dockTitleBar = new DolphinDockTitleBar(this); + } + setTitleBarWidget(m_dockTitleBar); + setFeatures(QDockWidget::NoDockWidgetFeatures); + } else { + setTitleBarWidget(0); + setFeatures(DefaultDockWidgetFeatures); + } + } +} + +bool DolphinDockWidget::isLocked() const +{ + return m_locked; +} + +#include "moc_dolphindockwidget.cpp" diff --git a/dolphin/src/dolphindockwidget.h b/dolphin/src/dolphindockwidget.h new file mode 100644 index 00000000..8f491295 --- /dev/null +++ b/dolphin/src/dolphindockwidget.h @@ -0,0 +1,49 @@ +/*************************************************************************** + * Copyright (C) 2010 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef DOLPHIN_DOCK_WIDGET_H +#define DOLPHIN_DOCK_WIDGET_H + +#include + +/** + * @brief Extends QDockWidget to be able to get locked. + */ +class DolphinDockWidget : public QDockWidget +{ + Q_OBJECT + +public: + explicit DolphinDockWidget(const QString& title, QWidget* parent = 0, Qt::WindowFlags flags = 0); + explicit DolphinDockWidget(QWidget* parent = 0, Qt::WindowFlags flags = 0); + virtual ~DolphinDockWidget(); + + /** + * @param lock If \a lock is true, the title bar of the dock-widget will get hidden so + * that it is not possible for the user anymore to move or undock the dock-widget. + */ + void setLocked(bool lock); + bool isLocked() const; + +private: + bool m_locked; + QWidget* m_dockTitleBar; +}; + +#endif diff --git a/dolphin/src/dolphinmainwindow.cpp b/dolphin/src/dolphinmainwindow.cpp new file mode 100644 index 00000000..39805cf5 --- /dev/null +++ b/dolphin/src/dolphinmainwindow.cpp @@ -0,0 +1,1797 @@ +/*************************************************************************** + * Copyright (C) 2006 by Peter Penz * + * Copyright (C) 2006 by Stefan Monov * + * Copyright (C) 2006 by Cvetoslav Ludmiloff * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "dolphinmainwindow.h" + +#include "dolphinapplication.h" +#include "dolphindockwidget.h" +#include "dolphincontextmenu.h" +#include "dolphinnewfilemenu.h" +#include "dolphinrecenttabsmenu.h" +#include "dolphinviewcontainer.h" +#include "dolphintabpage.h" +#include "panels/folders/folderspanel.h" +#include "panels/places/placespanel.h" +#include "panels/information/informationpanel.h" +#include "settings/dolphinsettingsdialog.h" +#include "statusbar/dolphinstatusbar.h" +#include "views/dolphinviewactionhandler.h" +#include "views/dolphinremoteencoding.h" +#include "views/draganddrophelper.h" +#include "views/viewproperties.h" +#include "views/dolphinnewfilemenuobserver.h" + +#include "panels/terminal/terminalpanel.h" + +#include "dolphin_generalsettings.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace { + // Used for GeneralSettings::version() to determine whether + // an updated version of Dolphin is running. + const int CurrentDolphinVersion = 200; +}; + +DolphinMainWindow::DolphinMainWindow() : + KXmlGuiWindow(0), + m_newFileMenu(0), + m_tabBar(0), + m_activeViewContainer(0), + m_centralWidgetLayout(0), + m_tabIndex(-1), + m_viewTab(), + m_actionHandler(0), + m_remoteEncoding(0), + m_settingsDialog(), + m_controlButton(0), + m_updateToolBarTimer(0), + m_lastHandleUrlStatJob(0) +{ + setObjectName("Dolphin#"); + + connect(&DolphinNewFileMenuObserver::instance(), SIGNAL(errorMessage(QString)), + this, SLOT(showErrorMessage(QString))); + + KIO::FileUndoManager* undoManager = KIO::FileUndoManager::self(); + undoManager->setUiInterface(new UndoUiInterface()); + + connect(undoManager, SIGNAL(undoAvailable(bool)), + this, SLOT(slotUndoAvailable(bool))); + connect(undoManager, SIGNAL(undoTextChanged(QString)), + this, SLOT(slotUndoTextChanged(QString))); + connect(undoManager, SIGNAL(jobRecordingStarted(CommandType)), + this, SLOT(clearStatusBar())); + connect(undoManager, SIGNAL(jobRecordingFinished(CommandType)), + this, SLOT(showCommand(CommandType))); + + GeneralSettings* generalSettings = GeneralSettings::self(); + const bool firstRun = (generalSettings->version() < 200); + if (firstRun) { + generalSettings->setViewPropsTimestamp(QDateTime::currentDateTime()); + } + + setAcceptDrops(true); + + setupActions(); + + m_actionHandler = new DolphinViewActionHandler(actionCollection(), this); + connect(m_actionHandler, SIGNAL(actionBeingHandled()), SLOT(clearStatusBar())); + connect(m_actionHandler, SIGNAL(createDirectory()), SLOT(createDirectory())); + + m_remoteEncoding = new DolphinRemoteEncoding(this, m_actionHandler); + connect(this, SIGNAL(urlChanged(KUrl)), + m_remoteEncoding, SLOT(slotAboutToOpenUrl())); + + m_tabBar = new KTabBar(this); + m_tabBar->setMovable(true); + m_tabBar->setTabsClosable(true); + connect(m_tabBar, SIGNAL(currentChanged(int)), + this, SLOT(setActiveTab(int))); + connect(m_tabBar, SIGNAL(tabCloseRequested(int)), + this, SLOT(closeTab(int))); + connect(m_tabBar, SIGNAL(contextMenu(int,QPoint)), + this, SLOT(openTabContextMenu(int,QPoint))); + connect(m_tabBar, SIGNAL(newTabRequest()), + this, SLOT(openNewTab())); + connect(m_tabBar, SIGNAL(testCanDecode(const QDragMoveEvent*,bool&)), + this, SLOT(slotTestCanDecode(const QDragMoveEvent*,bool&))); + connect(m_tabBar, SIGNAL(mouseMiddleClick(int)), + this, SLOT(closeTab(int))); + connect(m_tabBar, SIGNAL(tabMoved(int,int)), + this, SLOT(slotTabMoved(int,int))); + connect(m_tabBar, SIGNAL(receivedDropEvent(int,QDropEvent*)), + this, SLOT(tabDropEvent(int,QDropEvent*))); + + m_tabBar->blockSignals(true); // signals get unblocked after at least 2 tabs are open + m_tabBar->hide(); + + QWidget* centralWidget = new QWidget(this); + m_centralWidgetLayout = new QVBoxLayout(centralWidget); + m_centralWidgetLayout->setSpacing(0); + m_centralWidgetLayout->setMargin(0); + m_centralWidgetLayout->addWidget(m_tabBar); + + setCentralWidget(centralWidget); + setupDockWidgets(); + + setupGUI(Keys | Save | Create | ToolBar); + stateChanged("new_file"); + + QClipboard* clipboard = QApplication::clipboard(); + connect(clipboard, SIGNAL(dataChanged()), + this, SLOT(updatePasteAction())); + + QAction* showFilterBarAction = actionCollection()->action("show_filter_bar"); + showFilterBarAction->setChecked(generalSettings->filterBar()); + + if (firstRun) { + menuBar()->setVisible(true); + // Assure a proper default size if Dolphin runs the first time + resize(750, 500); + } + + const bool showMenu = !menuBar()->isHidden(); + QAction* showMenuBarAction = actionCollection()->action(KStandardAction::name(KStandardAction::ShowMenubar)); + showMenuBarAction->setChecked(showMenu); // workaround for bug #171080 + if (!showMenu) { + createControlButton(); + } +} + +DolphinMainWindow::~DolphinMainWindow() +{ +} + +void DolphinMainWindow::openDirectories(const QList& dirs) +{ + const bool hasSplitView = GeneralSettings::splitView(); + + // Open each directory inside a new tab. If the "split view" option has been enabled, + // always show two directories within one tab. + QList::const_iterator it = dirs.constBegin(); + while (it != dirs.constEnd()) { + const KUrl& primaryUrl = *(it++); + if (hasSplitView && (it != dirs.constEnd())) { + const KUrl& secondaryUrl = *(it++); + openNewTab(primaryUrl, secondaryUrl); + } else { + openNewTab(primaryUrl); + } + } +} + +void DolphinMainWindow::openFiles(const QList& files) +{ + if (files.isEmpty()) { + return; + } + + // Get all distinct directories from 'files' and open a tab + // for each directory. If the "split view" option is enabled, two + // directories are shown inside one tab (see openDirectories()). + QList dirs; + foreach (const KUrl& url, files) { + const KUrl dir(url.directory()); + if (!dirs.contains(dir)) { + dirs.append(dir); + } + } + + openDirectories(dirs); + + // Select the files. Although the files can be split between several + // tabs, there is no need to split 'files' accordingly, as + // the DolphinView will just ignore invalid selections. + foreach (DolphinTabPage* tabPage, m_viewTab) { + tabPage->markUrlsAsSelected(files); + tabPage->markUrlAsCurrent(files.first()); + } +} + +void DolphinMainWindow::showCommand(CommandType command) +{ + DolphinStatusBar* statusBar = m_activeViewContainer->statusBar(); + switch (command) { + case KIO::FileUndoManager::Copy: + statusBar->setText(i18nc("@info:status", "Successfully copied.")); + break; + case KIO::FileUndoManager::Move: + statusBar->setText(i18nc("@info:status", "Successfully moved.")); + break; + case KIO::FileUndoManager::Link: + statusBar->setText(i18nc("@info:status", "Successfully linked.")); + break; + case KIO::FileUndoManager::Trash: + statusBar->setText(i18nc("@info:status", "Successfully moved to trash.")); + break; + case KIO::FileUndoManager::Rename: + statusBar->setText(i18nc("@info:status", "Successfully renamed.")); + break; + + case KIO::FileUndoManager::Mkdir: + statusBar->setText(i18nc("@info:status", "Created folder.")); + break; + + default: + break; + } +} + +void DolphinMainWindow::pasteIntoFolder() +{ + m_activeViewContainer->view()->pasteIntoFolder(); +} + +void DolphinMainWindow::changeUrl(const KUrl& url) +{ + if (!KProtocolManager::supportsListing(url)) { + // The URL navigator only checks for validity, not + // if the URL can be listed. An error message is + // shown due to DolphinViewContainer::restoreView(). + return; + } + + DolphinViewContainer* view = activeViewContainer(); + if (view) { + view->setUrl(url); + updateEditActions(); + updatePasteAction(); + updateViewActions(); + updateGoActions(); + setUrlAsCaption(url); + + const QString iconName = KMimeType::iconNameForUrl(url); + m_tabBar->setTabIcon(m_tabIndex, KIcon(iconName)); + m_tabBar->setTabText(m_tabIndex, squeezedText(tabName(view->url()))); + + emit urlChanged(url); + } +} + +void DolphinMainWindow::slotTerminalDirectoryChanged(const KUrl& url) +{ + m_activeViewContainer->setAutoGrabFocus(false); + changeUrl(url); + m_activeViewContainer->setAutoGrabFocus(true); +} + +void DolphinMainWindow::slotEditableStateChanged(bool editable) +{ + KToggleAction* editableLocationAction = + static_cast(actionCollection()->action("editable_location")); + editableLocationAction->setChecked(editable); +} + +void DolphinMainWindow::slotSelectionChanged(const KFileItemList& selection) +{ + updateEditActions(); + + emit selectionChanged(selection); +} + +void DolphinMainWindow::slotRequestItemInfo(const KFileItem& item) +{ + emit requestItemInfo(item); +} + +void DolphinMainWindow::updateHistory() +{ + const KUrlNavigator* urlNavigator = m_activeViewContainer->urlNavigator(); + const int index = urlNavigator->historyIndex(); + + QAction* backAction = actionCollection()->action("go_back"); + if (backAction) { + backAction->setToolTip(i18nc("@info", "Go back")); + backAction->setEnabled(index < urlNavigator->historySize() - 1); + } + + QAction* forwardAction = actionCollection()->action("go_forward"); + if (forwardAction) { + forwardAction->setToolTip(i18nc("@info", "Go forward")); + forwardAction->setEnabled(index > 0); + } +} + +void DolphinMainWindow::updateFilterBarAction(bool show) +{ + QAction* showFilterBarAction = actionCollection()->action("show_filter_bar"); + showFilterBarAction->setChecked(show); +} + +void DolphinMainWindow::openNewMainWindow() +{ + KRun::run("dolphin %u", KUrl::List(), this); +} + +void DolphinMainWindow::openNewTab() +{ + const bool isUrlEditable = m_activeViewContainer->urlNavigator()->isUrlEditable(); + + openNewTab(m_activeViewContainer->url()); + m_tabBar->setCurrentIndex(m_viewTab.count() - 1); + + // The URL navigator of the new tab should have the same editable state + // as the current tab + KUrlNavigator* navigator = m_activeViewContainer->urlNavigator(); + navigator->setUrlEditable(isUrlEditable); + + if (isUrlEditable) { + // If a new tab is opened and the URL is editable, assure that + // the user can edit the URL without manually setting the focus + navigator->setFocus(); + } +} + +void DolphinMainWindow::openNewTab(const KUrl& primaryUrl, const KUrl& secondaryUrl) +{ + QWidget* focusWidget = QApplication::focusWidget(); + + DolphinTabPage* tabPage = new DolphinTabPage(primaryUrl, secondaryUrl, this); + m_viewTab.append(tabPage); + + connect(tabPage, SIGNAL(activeViewChanged()), + this, SLOT(activeViewChanged())); + + // The places-selector from the URL navigator should only be shown + // if the places dock is invisible + QDockWidget* placesDock = findChild("placesDock"); + const bool placesSelectorVisible = !placesDock || !placesDock->isVisible(); + tabPage->setPlacesSelectorVisible(placesSelectorVisible); + + tabPage->hide(); + + m_tabBar->addTab(KIcon(KMimeType::iconNameForUrl(primaryUrl)), + squeezedText(tabName(primaryUrl))); + + if (m_viewTab.count() > 1) { + actionCollection()->action("close_tab")->setEnabled(true); + actionCollection()->action("activate_prev_tab")->setEnabled(true); + actionCollection()->action("activate_next_tab")->setEnabled(true); + m_tabBar->show(); + m_tabBar->blockSignals(false); + } + + if (focusWidget) { + // The DolphinViewContainer grabbed the keyboard focus. As the tab is opened + // in background, assure that the previous focused widget gets the focus back. + focusWidget->setFocus(); + } +} + +void DolphinMainWindow::openNewActivatedTab(const KUrl& primaryUrl, const KUrl& secondaryUrl) +{ + openNewTab(primaryUrl, secondaryUrl); + setActiveTab(m_viewTab.count() - 1); +} + +void DolphinMainWindow::activateNextTab() +{ + if (m_viewTab.count() >= 2) { + const int tabIndex = (m_tabBar->currentIndex() + 1) % m_tabBar->count(); + setActiveTab(tabIndex); + } +} + +void DolphinMainWindow::activatePrevTab() +{ + if (m_viewTab.count() >= 2) { + int tabIndex = m_tabBar->currentIndex() - 1; + if (tabIndex == -1) { + tabIndex = m_tabBar->count() - 1; + } + setActiveTab(tabIndex); + } +} + +void DolphinMainWindow::openInNewTab() +{ + const KFileItemList& list = m_activeViewContainer->view()->selectedItems(); + if (list.isEmpty()) { + openNewTab(m_activeViewContainer->url()); + } else { + foreach (const KFileItem& item, list) { + const KUrl& url = DolphinView::openItemAsFolderUrl(item); + if (!url.isEmpty()) { + openNewTab(url); + } + } + } +} + +void DolphinMainWindow::openInNewWindow() +{ + KUrl newWindowUrl; + + const KFileItemList list = m_activeViewContainer->view()->selectedItems(); + if (list.isEmpty()) { + newWindowUrl = m_activeViewContainer->url(); + } else if (list.count() == 1) { + const KFileItem& item = list.first(); + newWindowUrl = DolphinView::openItemAsFolderUrl(item); + } + + if (!newWindowUrl.isEmpty()) { + KRun::run("dolphin %u", KUrl::List() << newWindowUrl, this); + } +} + +void DolphinMainWindow::showEvent(QShowEvent* event) +{ + KXmlGuiWindow::showEvent(event); + + if (!m_activeViewContainer && m_viewTab.count() > 0) { + // If we have no active view container yet, we set the primary view container + // of the first tab as active view container. + setActiveTab(0); + } + + if (!event->spontaneous()) { + m_activeViewContainer->view()->setFocus(); + } +} + +void DolphinMainWindow::closeEvent(QCloseEvent* event) +{ + // Find out if Dolphin is closed directly by the user or + // by the session manager because the session is closed + bool closedByUser = true; + DolphinApplication *application = qobject_cast(qApp); + if (application && application->sessionSaving()) { + closedByUser = false; + } + + if (m_viewTab.count() > 1 && GeneralSettings::confirmClosingMultipleTabs() && closedByUser) { + // Ask the user if he really wants to quit and close all tabs. + // Open a confirmation dialog with 3 buttons: + // KDialog::Yes -> Quit + // KDialog::No -> Close only the current tab + // KDialog::Cancel -> do nothing + KDialog *dialog = new KDialog(this, Qt::Dialog); + dialog->setCaption(i18nc("@title:window", "Confirmation")); + dialog->setButtons(KDialog::Yes | KDialog::No | KDialog::Cancel); + dialog->setModal(true); + dialog->setButtonGuiItem(KDialog::Yes, KStandardGuiItem::quit()); + dialog->setButtonGuiItem(KDialog::No, KGuiItem(i18n("C&lose Current Tab"), KIcon("tab-close"))); + dialog->setButtonGuiItem(KDialog::Cancel, KStandardGuiItem::cancel()); + dialog->setDefaultButton(KDialog::Yes); + + bool doNotAskAgainCheckboxResult = false; + + const int result = KMessageBox::createKMessageBox(dialog, + QMessageBox::Warning, + i18n("You have multiple tabs open in this window, are you sure you want to quit?"), + QStringList(), + i18n("Do not ask again"), + &doNotAskAgainCheckboxResult, + KMessageBox::Notify); + + if (doNotAskAgainCheckboxResult) { + GeneralSettings::setConfirmClosingMultipleTabs(false); + } + + switch (result) { + case KDialog::Yes: + // Quit + break; + case KDialog::No: + // Close only the current tab + closeTab(); + default: + event->ignore(); + return; + } + } + + GeneralSettings::setVersion(CurrentDolphinVersion); + GeneralSettings::self()->writeConfig(); + + KXmlGuiWindow::closeEvent(event); +} + +void DolphinMainWindow::saveProperties(KConfigGroup& group) +{ + const int tabCount = m_viewTab.count(); + group.writeEntry("Tab Count", tabCount); + group.writeEntry("Active Tab Index", m_tabBar->currentIndex()); + + for (int i = 0; i < tabCount; ++i) { + const DolphinTabPage* tabPage = m_viewTab.at(i); + group.writeEntry("Tab " + QString::number(i), tabPage->saveState()); + } +} + +void DolphinMainWindow::readProperties(const KConfigGroup& group) +{ + const int tabCount = group.readEntry("Tab Count", 1); + for (int i = 0; i < tabCount; ++i) { + const QByteArray state = group.readEntry("Tab " + QString::number(i), QByteArray()); + DolphinTabPage* tabPage = m_viewTab.at(i); + tabPage->restoreState(state); + + // openNewTab() needs to be called only tabCount - 1 times + if (i != tabCount - 1) { + openNewTab(); + } + } + + const int index = group.readEntry("Active Tab Index", 0); + m_tabBar->setCurrentIndex(index); +} + +void DolphinMainWindow::updateNewMenu() +{ + m_newFileMenu->setViewShowsHiddenFiles(activeViewContainer()->view()->hiddenFilesShown()); + m_newFileMenu->checkUpToDate(); + m_newFileMenu->setPopupFiles(activeViewContainer()->url()); +} + +void DolphinMainWindow::createDirectory() +{ + m_newFileMenu->setViewShowsHiddenFiles(activeViewContainer()->view()->hiddenFilesShown()); + m_newFileMenu->setPopupFiles(activeViewContainer()->url()); + m_newFileMenu->createDirectory(); +} + +void DolphinMainWindow::quit() +{ + close(); +} + +void DolphinMainWindow::showErrorMessage(const QString& message) +{ + m_activeViewContainer->showMessage(message, DolphinViewContainer::Error); +} + +void DolphinMainWindow::slotUndoAvailable(bool available) +{ + QAction* undoAction = actionCollection()->action(KStandardAction::name(KStandardAction::Undo)); + if (undoAction) { + undoAction->setEnabled(available); + } +} + +void DolphinMainWindow::slotUndoTextChanged(const QString& text) +{ + QAction* undoAction = actionCollection()->action(KStandardAction::name(KStandardAction::Undo)); + if (undoAction) { + undoAction->setText(text); + } +} + +void DolphinMainWindow::undo() +{ + clearStatusBar(); + KIO::FileUndoManager::self()->uiInterface()->setParentWidget(this); + KIO::FileUndoManager::self()->undo(); +} + +void DolphinMainWindow::cut() +{ + m_activeViewContainer->view()->cutSelectedItems(); +} + +void DolphinMainWindow::copy() +{ + m_activeViewContainer->view()->copySelectedItems(); +} + +void DolphinMainWindow::paste() +{ + m_activeViewContainer->view()->paste(); +} + +void DolphinMainWindow::find() +{ + m_activeViewContainer->setSearchModeEnabled(true); +} + +void DolphinMainWindow::updatePasteAction() +{ + QAction* pasteAction = actionCollection()->action(KStandardAction::name(KStandardAction::Paste)); + QPair pasteInfo = m_activeViewContainer->view()->pasteInfo(); + pasteAction->setEnabled(pasteInfo.first); + pasteAction->setText(pasteInfo.second); +} + +void DolphinMainWindow::selectAll() +{ + clearStatusBar(); + + // if the URL navigator is editable and focused, select the whole + // URL instead of all items of the view + + KUrlNavigator* urlNavigator = m_activeViewContainer->urlNavigator(); + QLineEdit* lineEdit = urlNavigator->editor()->lineEdit(); // krazy:exclude=qclasses + const bool selectUrl = urlNavigator->isUrlEditable() && + lineEdit->hasFocus(); + if (selectUrl) { + lineEdit->selectAll(); + } else { + m_activeViewContainer->view()->selectAll(); + } +} + +void DolphinMainWindow::invertSelection() +{ + clearStatusBar(); + m_activeViewContainer->view()->invertSelection(); +} + +void DolphinMainWindow::toggleSplitView() +{ + DolphinTabPage* tabPage = m_viewTab.at(m_tabIndex); + tabPage->setSplitViewEnabled(!tabPage->splitViewEnabled()); + + updateViewActions(); +} + +void DolphinMainWindow::reloadView() +{ + clearStatusBar(); + m_activeViewContainer->view()->reload(); +} + +void DolphinMainWindow::stopLoading() +{ + m_activeViewContainer->view()->stopLoading(); +} + +void DolphinMainWindow::enableStopAction() +{ + actionCollection()->action("stop")->setEnabled(true); +} + +void DolphinMainWindow::disableStopAction() +{ + actionCollection()->action("stop")->setEnabled(false); +} + +void DolphinMainWindow::showFilterBar() +{ + m_activeViewContainer->setFilterBarVisible(true); +} + +void DolphinMainWindow::toggleEditLocation() +{ + clearStatusBar(); + + QAction* action = actionCollection()->action("editable_location"); + KUrlNavigator* urlNavigator = m_activeViewContainer->urlNavigator(); + urlNavigator->setUrlEditable(action->isChecked()); +} + +void DolphinMainWindow::replaceLocation() +{ + KUrlNavigator* navigator = m_activeViewContainer->urlNavigator(); + navigator->setUrlEditable(true); + navigator->setFocus(); + + // select the whole text of the combo box editor + QLineEdit* lineEdit = navigator->editor()->lineEdit(); // krazy:exclude=qclasses + lineEdit->selectAll(); +} + +void DolphinMainWindow::togglePanelLockState() +{ + const bool newLockState = !GeneralSettings::lockPanels(); + foreach (QObject* child, children()) { + DolphinDockWidget* dock = qobject_cast(child); + if (dock) { + dock->setLocked(newLockState); + } + } + + GeneralSettings::setLockPanels(newLockState); +} + +void DolphinMainWindow::slotPlacesPanelVisibilityChanged(bool visible) +{ + foreach (DolphinTabPage* tabPage, m_viewTab) { + // The Places selector in the location bar should be shown if and only if the Places panel is hidden. + tabPage->setPlacesSelectorVisible(!visible); + } +} + +void DolphinMainWindow::goBack() +{ + KUrlNavigator* urlNavigator = m_activeViewContainer->urlNavigator(); + urlNavigator->goBack(); + + if (urlNavigator->locationState().isEmpty()) { + // An empty location state indicates a redirection URL, + // which must be skipped too + urlNavigator->goBack(); + } +} + +void DolphinMainWindow::goForward() +{ + m_activeViewContainer->urlNavigator()->goForward(); +} + +void DolphinMainWindow::goUp() +{ + m_activeViewContainer->urlNavigator()->goUp(); +} + +void DolphinMainWindow::goHome() +{ + m_activeViewContainer->urlNavigator()->goHome(); +} + +void DolphinMainWindow::goBack(Qt::MouseButtons buttons) +{ + // The default case (left button pressed) is handled in goBack(). + if (buttons == Qt::MiddleButton) { + KUrlNavigator* urlNavigator = activeViewContainer()->urlNavigator(); + const int index = urlNavigator->historyIndex() + 1; + openNewTab(urlNavigator->locationUrl(index)); + } +} + +void DolphinMainWindow::goForward(Qt::MouseButtons buttons) +{ + // The default case (left button pressed) is handled in goForward(). + if (buttons == Qt::MiddleButton) { + KUrlNavigator* urlNavigator = activeViewContainer()->urlNavigator(); + const int index = urlNavigator->historyIndex() - 1; + openNewTab(urlNavigator->locationUrl(index)); + } +} + +void DolphinMainWindow::goUp(Qt::MouseButtons buttons) +{ + // The default case (left button pressed) is handled in goUp(). + if (buttons == Qt::MiddleButton) { + openNewTab(activeViewContainer()->url().upUrl()); + } +} + +void DolphinMainWindow::goHome(Qt::MouseButtons buttons) +{ + // The default case (left button pressed) is handled in goHome(). + if (buttons == Qt::MiddleButton) { + openNewTab(GeneralSettings::self()->homeUrl()); + } +} + +void DolphinMainWindow::toggleShowMenuBar() +{ + const bool visible = menuBar()->isVisible(); + menuBar()->setVisible(!visible); + if (visible) { + createControlButton(); + } else { + deleteControlButton(); + } +} + +void DolphinMainWindow::openTerminal() +{ + QString dir(QDir::homePath()); + + // If the given directory is not local, it can still be the URL of an + // ioslave using UDS_LOCAL_PATH which to be converted first. + KUrl url = KIO::NetAccess::mostLocalUrl(m_activeViewContainer->url(), this); + + //If the URL is local after the above conversion, set the directory. + if (url.isLocalFile()) { + dir = url.toLocalFile(); + } + + KToolInvocation::invokeTerminal(QString(), dir); +} + +void DolphinMainWindow::editSettings() +{ + if (!m_settingsDialog) { + DolphinViewContainer* container = activeViewContainer(); + container->view()->writeSettings(); + + const KUrl url = container->url(); + DolphinSettingsDialog* settingsDialog = new DolphinSettingsDialog(url, this); + connect(settingsDialog, SIGNAL(settingsChanged()), this, SLOT(refreshViews())); + settingsDialog->setAttribute(Qt::WA_DeleteOnClose); + settingsDialog->show(); + m_settingsDialog = settingsDialog; + } else { + m_settingsDialog.data()->raise(); + } +} + +void DolphinMainWindow::setActiveTab(int index) +{ + Q_ASSERT(index >= 0); + Q_ASSERT(index < m_viewTab.count()); + if (index == m_tabIndex) { + return; + } + + m_tabBar->setCurrentIndex(index); + + // hide current tab content + if (m_tabIndex >= 0) { + DolphinTabPage* hiddenTabPage = m_viewTab.at(m_tabIndex); + hiddenTabPage->hide(); + m_centralWidgetLayout->removeWidget(hiddenTabPage); + } + + // show active tab content + m_tabIndex = index; + + DolphinTabPage* tabPage = m_viewTab.at(index); + m_centralWidgetLayout->addWidget(tabPage, 1); + tabPage->show(); + + setActiveViewContainer(tabPage->activeViewContainer()); +} + +void DolphinMainWindow::closeTab() +{ + closeTab(m_tabBar->currentIndex()); +} + +void DolphinMainWindow::closeTab(int index) +{ + Q_ASSERT(index >= 0); + Q_ASSERT(index < m_viewTab.count()); + if (m_viewTab.count() == 1) { + // the last tab may never get closed + return; + } + + if (index == m_tabIndex) { + // The tab that should be closed is the active tab. Activate the + // previous tab before closing the tab. + m_tabBar->setCurrentIndex((index > 0) ? index - 1 : 1); + } + + DolphinTabPage* tabPage = m_viewTab.at(index); + + if (tabPage->splitViewEnabled()) { + emit rememberClosedTab(tabPage->primaryViewContainer()->url(), + tabPage->secondaryViewContainer()->url()); + } else { + emit rememberClosedTab(tabPage->primaryViewContainer()->url(), KUrl()); + } + + // delete tab + m_viewTab.removeAt(index); + tabPage->deleteLater(); + + m_tabBar->blockSignals(true); + m_tabBar->removeTab(index); + + if (m_tabIndex > index) { + m_tabIndex--; + Q_ASSERT(m_tabIndex >= 0); + } + + // if only one tab is left, also remove the tab entry so that + // closing the last tab is not possible + if (m_viewTab.count() < 2) { + actionCollection()->action("close_tab")->setEnabled(false); + actionCollection()->action("activate_prev_tab")->setEnabled(false); + actionCollection()->action("activate_next_tab")->setEnabled(false); + m_tabBar->hide(); + } else { + m_tabBar->blockSignals(false); + } +} + +void DolphinMainWindow::openTabContextMenu(int index, const QPoint& pos) +{ + KMenu menu(this); + + QAction* newTabAction = menu.addAction(KIcon("tab-new"), i18nc("@action:inmenu", "New Tab")); + newTabAction->setShortcut(actionCollection()->action("new_tab")->shortcut()); + + QAction* detachTabAction = menu.addAction(KIcon("tab-detach"), i18nc("@action:inmenu", "Detach Tab")); + + QAction* closeOtherTabsAction = menu.addAction(KIcon("tab-close-other"), i18nc("@action:inmenu", "Close Other Tabs")); + + QAction* closeTabAction = menu.addAction(KIcon("tab-close"), i18nc("@action:inmenu", "Close Tab")); + closeTabAction->setShortcut(actionCollection()->action("close_tab")->shortcut()); + QAction* selectedAction = menu.exec(pos); + if (selectedAction == newTabAction) { + const KUrl url = m_viewTab.at(index)->activeViewContainer()->url(); + openNewTab(url); + m_tabBar->setCurrentIndex(m_viewTab.count() - 1); + } else if (selectedAction == detachTabAction) { + const QString separator(QLatin1Char(' ')); + QString command = QLatin1String("dolphin"); + + const DolphinTabPage* tabPage = m_viewTab.at(index); + + command += separator + tabPage->primaryViewContainer()->url().url(); + if (tabPage->splitViewEnabled()) { + command += separator + tabPage->secondaryViewContainer()->url().url(); + command += separator + QLatin1String("-split"); + } + + KRun::runCommand(command, this); + + closeTab(index); + } else if (selectedAction == closeOtherTabsAction) { + const int count = m_tabBar->count(); + for (int i = 0; i < index; ++i) { + closeTab(0); + } + for (int i = index + 1; i < count; ++i) { + closeTab(1); + } + } else if (selectedAction == closeTabAction) { + closeTab(index); + } +} + +void DolphinMainWindow::slotTabMoved(int from, int to) +{ + m_viewTab.move(from, to); + m_tabIndex = m_tabBar->currentIndex(); +} + +void DolphinMainWindow::slotTestCanDecode(const QDragMoveEvent* event, bool& canDecode) +{ + canDecode = KUrl::List::canDecode(event->mimeData()); +} + +void DolphinMainWindow::handleUrl(const KUrl& url) +{ + delete m_lastHandleUrlStatJob; + m_lastHandleUrlStatJob = 0; + + if (url.isLocalFile() && QFileInfo(url.toLocalFile()).isDir()) { + activeViewContainer()->setUrl(url); + } else if (KProtocolManager::supportsListing(url)) { + // stat the URL to see if it is a dir or not + m_lastHandleUrlStatJob = KIO::stat(url, KIO::HideProgressInfo); + if (m_lastHandleUrlStatJob->ui()) { + m_lastHandleUrlStatJob->ui()->setWindow(this); + } + connect(m_lastHandleUrlStatJob, SIGNAL(result(KJob*)), + this, SLOT(slotHandleUrlStatFinished(KJob*))); + + } else { + new KRun(url, this); // Automatically deletes itself after being finished + } +} + +void DolphinMainWindow::slotHandleUrlStatFinished(KJob* job) +{ + m_lastHandleUrlStatJob = 0; + const KIO::UDSEntry entry = static_cast(job)->statResult(); + const KUrl url = static_cast(job)->url(); + if (entry.isDir()) { + activeViewContainer()->setUrl(url); + } else { + new KRun(url, this); // Automatically deletes itself after being finished + } +} + +void DolphinMainWindow::tabDropEvent(int tab, QDropEvent* event) +{ + const KUrl::List urls = KUrl::List::fromMimeData(event->mimeData()); + if (!urls.isEmpty() && tab != -1) { + const DolphinView* view = m_viewTab.at(tab)->activeViewContainer()->view(); + + QString error; + DragAndDropHelper::dropUrls(view->rootItem(), view->url(), event, error); + if (!error.isEmpty()) { + activeViewContainer()->showMessage(error, DolphinViewContainer::Error); + } + } +} + +void DolphinMainWindow::slotWriteStateChanged(bool isFolderWritable) +{ + newFileMenu()->setEnabled(isFolderWritable); +} + +void DolphinMainWindow::openContextMenu(const QPoint& pos, + const KFileItem& item, + const KUrl& url, + const QList& customActions) +{ + QWeakPointer contextMenu = new DolphinContextMenu(this, pos, item, url); + contextMenu.data()->setCustomActions(customActions); + const DolphinContextMenu::Command command = contextMenu.data()->open(); + + switch (command) { + case DolphinContextMenu::OpenParentFolderInNewWindow: { + KRun::run("dolphin %u", KUrl::List() << item.url().upUrl(), this); + break; + } + + case DolphinContextMenu::OpenParentFolderInNewTab: + openNewTab(item.url().upUrl()); + break; + + case DolphinContextMenu::None: + default: + break; + } + + delete contextMenu.data(); +} + +void DolphinMainWindow::updateControlMenu() +{ + KMenu* menu = qobject_cast(sender()); + Q_ASSERT(menu); + + // All actions get cleared by KMenu::clear(). The sub-menus are deleted + // by connecting to the aboutToHide() signal from the parent-menu. + menu->clear(); + + KActionCollection* ac = actionCollection(); + + // Add "Edit" actions + bool added = addActionToMenu(ac->action(KStandardAction::name(KStandardAction::Undo)), menu) | + addActionToMenu(ac->action(KStandardAction::name(KStandardAction::Find)), menu) | + addActionToMenu(ac->action("select_all"), menu) | + addActionToMenu(ac->action("invert_selection"), menu); + + if (added) { + menu->addSeparator(); + } + + // Add "View" actions + if (!GeneralSettings::showZoomSlider()) { + addActionToMenu(ac->action(KStandardAction::name(KStandardAction::ZoomIn)), menu); + addActionToMenu(ac->action(KStandardAction::name(KStandardAction::ZoomOut)), menu); + menu->addSeparator(); + } + + added = addActionToMenu(ac->action("view_mode"), menu) | + addActionToMenu(ac->action("sort"), menu) | + addActionToMenu(ac->action("additional_info"), menu) | + addActionToMenu(ac->action("show_preview"), menu) | + addActionToMenu(ac->action("show_in_groups"), menu) | + addActionToMenu(ac->action("show_hidden_files"), menu); + + if (added) { + menu->addSeparator(); + } + + added = addActionToMenu(ac->action("split_view"), menu) | + addActionToMenu(ac->action("reload"), menu) | + addActionToMenu(ac->action("view_properties"), menu); + if (added) { + menu->addSeparator(); + } + + addActionToMenu(ac->action("panels"), menu); + KMenu* locationBarMenu = new KMenu(i18nc("@action:inmenu", "Location Bar"), menu); + locationBarMenu->addAction(ac->action("editable_location")); + locationBarMenu->addAction(ac->action("replace_location")); + menu->addMenu(locationBarMenu); + + menu->addSeparator(); + + // Add "Go" menu + KMenu* goMenu = new KMenu(i18nc("@action:inmenu", "Go"), menu); + connect(menu, SIGNAL(aboutToHide()), goMenu, SLOT(deleteLater())); + goMenu->addAction(ac->action(KStandardAction::name(KStandardAction::Back))); + goMenu->addAction(ac->action(KStandardAction::name(KStandardAction::Forward))); + goMenu->addAction(ac->action(KStandardAction::name(KStandardAction::Up))); + goMenu->addAction(ac->action(KStandardAction::name(KStandardAction::Home))); + goMenu->addAction(ac->action("closed_tabs")); + menu->addMenu(goMenu); + + // Add "Tool" menu + KMenu* toolsMenu = new KMenu(i18nc("@action:inmenu", "Tools"), menu); + connect(menu, SIGNAL(aboutToHide()), toolsMenu, SLOT(deleteLater())); + toolsMenu->addAction(ac->action("show_filter_bar")); + toolsMenu->addAction(ac->action("open_terminal")); + toolsMenu->addAction(ac->action("change_remote_encoding")); + menu->addMenu(toolsMenu); + + // Add "Settings" menu entries + addActionToMenu(ac->action(KStandardAction::name(KStandardAction::KeyBindings)), menu); + addActionToMenu(ac->action(KStandardAction::name(KStandardAction::ConfigureToolbars)), menu); + addActionToMenu(ac->action(KStandardAction::name(KStandardAction::Preferences)), menu); + + // Add "Help" menu + KMenu* helpMenu = new KMenu(i18nc("@action:inmenu", "Help"), menu); + connect(menu, SIGNAL(aboutToHide()), helpMenu, SLOT(deleteLater())); + helpMenu->addAction(ac->action(KStandardAction::name(KStandardAction::HelpContents))); + helpMenu->addAction(ac->action(KStandardAction::name(KStandardAction::WhatsThis))); + helpMenu->addSeparator(); + helpMenu->addAction(ac->action(KStandardAction::name(KStandardAction::ReportBug))); + helpMenu->addSeparator(); + helpMenu->addAction(ac->action(KStandardAction::name(KStandardAction::SwitchApplicationLanguage))); + helpMenu->addSeparator(); + helpMenu->addAction(ac->action(KStandardAction::name(KStandardAction::AboutApp))); + helpMenu->addAction(ac->action(KStandardAction::name(KStandardAction::AboutKDE))); + menu->addMenu(helpMenu); + + menu->addSeparator(); + addActionToMenu(ac->action(KStandardAction::name(KStandardAction::ShowMenubar)), menu); +} + +void DolphinMainWindow::updateToolBar() +{ + if (!menuBar()->isVisible()) { + createControlButton(); + } +} + +void DolphinMainWindow::slotControlButtonDeleted() +{ + m_controlButton = 0; + m_updateToolBarTimer->start(); +} + +void DolphinMainWindow::slotPanelErrorMessage(const QString& error) +{ + activeViewContainer()->showMessage(error, DolphinViewContainer::Error); +} + +void DolphinMainWindow::slotPlaceActivated(const KUrl& url) +{ + DolphinViewContainer* view = activeViewContainer(); + + if (view->url() == url) { + // We can end up here if the user clicked a device in the Places Panel + // which had been unmounted earlier, see https://bugs.kde.org/show_bug.cgi?id=161385. + reloadView(); + } else { + changeUrl(url); + } +} + +void DolphinMainWindow::activeViewChanged() +{ + const DolphinTabPage* tabPage = m_viewTab.at(m_tabIndex); + setActiveViewContainer(tabPage->activeViewContainer()); +} + +void DolphinMainWindow::setActiveViewContainer(DolphinViewContainer* viewContainer) +{ + Q_ASSERT(viewContainer); + Q_ASSERT((viewContainer == m_viewTab.at(m_tabIndex)->primaryViewContainer()) || + (viewContainer == m_viewTab.at(m_tabIndex)->secondaryViewContainer())); + if (m_activeViewContainer == viewContainer) { + return; + } + + if (m_activeViewContainer) { + // Disconnect all signals between the old view container (container, + // view and url navigator) and main window. + m_activeViewContainer->disconnect(this); + m_activeViewContainer->view()->disconnect(this); + m_activeViewContainer->urlNavigator()->disconnect(this); + } + + m_activeViewContainer = viewContainer; + connectViewSignals(viewContainer); + + m_actionHandler->setCurrentView(viewContainer->view()); + + updateHistory(); + updateEditActions(); + updatePasteAction(); + updateViewActions(); + updateGoActions(); + + const KUrl url = m_activeViewContainer->url(); + setUrlAsCaption(url); + m_tabBar->setTabText(m_tabIndex, squeezedText(tabName(url))); + m_tabBar->setTabIcon(m_tabIndex, KIcon(KMimeType::iconNameForUrl(url))); + + emit urlChanged(url); +} + +void DolphinMainWindow::setupActions() +{ + // setup 'File' menu + m_newFileMenu = new DolphinNewFileMenu(actionCollection(), this); + KMenu* menu = m_newFileMenu->menu(); + menu->setTitle(i18nc("@title:menu Create new folder, file, link, etc.", "Create New")); + menu->setIcon(KIcon("document-new")); + m_newFileMenu->setDelayed(false); + connect(menu, SIGNAL(aboutToShow()), + this, SLOT(updateNewMenu())); + + KAction* newWindow = actionCollection()->addAction("new_window"); + newWindow->setIcon(KIcon("window-new")); + newWindow->setText(i18nc("@action:inmenu File", "New &Window")); + newWindow->setShortcut(Qt::CTRL | Qt::Key_N); + connect(newWindow, SIGNAL(triggered()), this, SLOT(openNewMainWindow())); + + KAction* newTab = actionCollection()->addAction("new_tab"); + newTab->setIcon(KIcon("tab-new")); + newTab->setText(i18nc("@action:inmenu File", "New Tab")); + newTab->setShortcut(KShortcut(Qt::CTRL | Qt::Key_T, Qt::CTRL | Qt::SHIFT | Qt::Key_N)); + connect(newTab, SIGNAL(triggered()), this, SLOT(openNewTab())); + + KAction* closeTab = actionCollection()->addAction("close_tab"); + closeTab->setIcon(KIcon("tab-close")); + closeTab->setText(i18nc("@action:inmenu File", "Close Tab")); + closeTab->setShortcut(Qt::CTRL | Qt::Key_W); + closeTab->setEnabled(false); + connect(closeTab, SIGNAL(triggered()), this, SLOT(closeTab())); + + KStandardAction::quit(this, SLOT(quit()), actionCollection()); + + // setup 'Edit' menu + KStandardAction::undo(this, + SLOT(undo()), + actionCollection()); + + // need to remove shift+del from cut action, else the shortcut for deletejob + // doesn't work + KAction* cut = KStandardAction::cut(this, SLOT(cut()), actionCollection()); + KShortcut cutShortcut = cut->shortcut(); + cutShortcut.remove(Qt::SHIFT | Qt::Key_Delete, KShortcut::KeepEmpty); + cut->setShortcut(cutShortcut); + KStandardAction::copy(this, SLOT(copy()), actionCollection()); + KAction* paste = KStandardAction::paste(this, SLOT(paste()), actionCollection()); + // The text of the paste-action is modified dynamically by Dolphin + // (e. g. to "Paste One Folder"). To prevent that the size of the toolbar changes + // due to the long text, the text "Paste" is used: + paste->setIconText(i18nc("@action:inmenu Edit", "Paste")); + + KStandardAction::find(this, SLOT(find()), actionCollection()); + + KAction* selectAll = actionCollection()->addAction("select_all"); + selectAll->setText(i18nc("@action:inmenu Edit", "Select All")); + selectAll->setShortcut(Qt::CTRL | Qt::Key_A); + connect(selectAll, SIGNAL(triggered()), this, SLOT(selectAll())); + + KAction* invertSelection = actionCollection()->addAction("invert_selection"); + invertSelection->setText(i18nc("@action:inmenu Edit", "Invert Selection")); + invertSelection->setShortcut(Qt::CTRL | Qt::SHIFT | Qt::Key_A); + connect(invertSelection, SIGNAL(triggered()), this, SLOT(invertSelection())); + + // setup 'View' menu + // (note that most of it is set up in DolphinViewActionHandler) + + KAction* split = actionCollection()->addAction("split_view"); + split->setShortcut(Qt::Key_F3); + connect(split, SIGNAL(triggered()), this, SLOT(toggleSplitView())); + + KAction* reload = actionCollection()->addAction("reload"); + reload->setText(i18nc("@action:inmenu View", "Reload")); + reload->setShortcut(Qt::Key_F5); + reload->setIcon(KIcon("view-refresh")); + connect(reload, SIGNAL(triggered()), this, SLOT(reloadView())); + + KAction* stop = actionCollection()->addAction("stop"); + stop->setText(i18nc("@action:inmenu View", "Stop")); + stop->setToolTip(i18nc("@info", "Stop loading")); + stop->setIcon(KIcon("process-stop")); + connect(stop, SIGNAL(triggered()), this, SLOT(stopLoading())); + + KToggleAction* editableLocation = actionCollection()->add("editable_location"); + editableLocation->setText(i18nc("@action:inmenu Navigation Bar", "Editable Location")); + editableLocation->setShortcut(Qt::Key_F6); + connect(editableLocation, SIGNAL(triggered()), this, SLOT(toggleEditLocation())); + + KAction* replaceLocation = actionCollection()->addAction("replace_location"); + replaceLocation->setText(i18nc("@action:inmenu Navigation Bar", "Replace Location")); + replaceLocation->setShortcut(Qt::CTRL | Qt::Key_L); + connect(replaceLocation, SIGNAL(triggered()), this, SLOT(replaceLocation())); + + // setup 'Go' menu + KAction* backAction = KStandardAction::back(this, SLOT(goBack()), actionCollection()); + connect(backAction, SIGNAL(triggered(Qt::MouseButtons,Qt::KeyboardModifiers)), this, SLOT(goBack(Qt::MouseButtons))); + KShortcut backShortcut = backAction->shortcut(); + backShortcut.setAlternate(Qt::Key_Backspace); + backAction->setShortcut(backShortcut); + + DolphinRecentTabsMenu* recentTabsMenu = new DolphinRecentTabsMenu(this); + actionCollection()->addAction("closed_tabs", recentTabsMenu); + connect(this, SIGNAL(rememberClosedTab(KUrl,KUrl)), + recentTabsMenu, SLOT(rememberClosedTab(KUrl,KUrl))); + connect(recentTabsMenu, SIGNAL(restoreClosedTab(KUrl,KUrl)), + this, SLOT(openNewActivatedTab(KUrl,KUrl))); + + KAction* forwardAction = KStandardAction::forward(this, SLOT(goForward()), actionCollection()); + connect(forwardAction, SIGNAL(triggered(Qt::MouseButtons,Qt::KeyboardModifiers)), this, SLOT(goForward(Qt::MouseButtons))); + + KAction* upAction = KStandardAction::up(this, SLOT(goUp()), actionCollection()); + connect(upAction, SIGNAL(triggered(Qt::MouseButtons,Qt::KeyboardModifiers)), this, SLOT(goUp(Qt::MouseButtons))); + + KAction* homeAction = KStandardAction::home(this, SLOT(goHome()), actionCollection()); + connect(homeAction, SIGNAL(triggered(Qt::MouseButtons,Qt::KeyboardModifiers)), this, SLOT(goHome(Qt::MouseButtons))); + + // setup 'Tools' menu + KAction* showFilterBar = actionCollection()->addAction("show_filter_bar"); + showFilterBar->setText(i18nc("@action:inmenu Tools", "Show Filter Bar")); + showFilterBar->setIcon(KIcon("view-filter")); + showFilterBar->setShortcut(Qt::CTRL | Qt::Key_I); + connect(showFilterBar, SIGNAL(triggered()), this, SLOT(showFilterBar())); + + KAction* openTerminal = actionCollection()->addAction("open_terminal"); + openTerminal->setText(i18nc("@action:inmenu Tools", "Open Terminal")); + openTerminal->setIcon(KIcon("utilities-terminal")); + openTerminal->setShortcut(Qt::SHIFT | Qt::Key_F4); + connect(openTerminal, SIGNAL(triggered()), this, SLOT(openTerminal())); + + // setup 'Settings' menu + KToggleAction* showMenuBar = KStandardAction::showMenubar(0, 0, actionCollection()); + connect(showMenuBar, SIGNAL(triggered(bool)), // Fixes #286822 + this, SLOT(toggleShowMenuBar()), Qt::QueuedConnection); + KStandardAction::preferences(this, SLOT(editSettings()), actionCollection()); + + // not in menu actions + QList nextTabKeys; + nextTabKeys.append(KStandardShortcut::tabNext().primary()); + nextTabKeys.append(QKeySequence(Qt::CTRL | Qt::Key_Tab)); + + QList prevTabKeys; + prevTabKeys.append(KStandardShortcut::tabPrev().primary()); + prevTabKeys.append(QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_Tab)); + + KAction* activateNextTab = actionCollection()->addAction("activate_next_tab"); + activateNextTab->setIconText(i18nc("@action:inmenu", "Next Tab")); + activateNextTab->setText(i18nc("@action:inmenu", "Activate Next Tab")); + activateNextTab->setEnabled(false); + connect(activateNextTab, SIGNAL(triggered()), SLOT(activateNextTab())); + activateNextTab->setShortcuts(QApplication::isRightToLeft() ? prevTabKeys : nextTabKeys); + + KAction* activatePrevTab = actionCollection()->addAction("activate_prev_tab"); + activatePrevTab->setIconText(i18nc("@action:inmenu", "Previous Tab")); + activatePrevTab->setText(i18nc("@action:inmenu", "Activate Previous Tab")); + activatePrevTab->setEnabled(false); + connect(activatePrevTab, SIGNAL(triggered()), SLOT(activatePrevTab())); + activatePrevTab->setShortcuts(QApplication::isRightToLeft() ? nextTabKeys : prevTabKeys); + + // for context menu + KAction* openInNewTab = actionCollection()->addAction("open_in_new_tab"); + openInNewTab->setText(i18nc("@action:inmenu", "Open in New Tab")); + openInNewTab->setIcon(KIcon("tab-new")); + connect(openInNewTab, SIGNAL(triggered()), this, SLOT(openInNewTab())); + + KAction* openInNewTabs = actionCollection()->addAction("open_in_new_tabs"); + openInNewTabs->setText(i18nc("@action:inmenu", "Open in New Tabs")); + openInNewTabs->setIcon(KIcon("tab-new")); + connect(openInNewTabs, SIGNAL(triggered()), this, SLOT(openInNewTab())); + + KAction* openInNewWindow = actionCollection()->addAction("open_in_new_window"); + openInNewWindow->setText(i18nc("@action:inmenu", "Open in New Window")); + openInNewWindow->setIcon(KIcon("window-new")); + connect(openInNewWindow, SIGNAL(triggered()), this, SLOT(openInNewWindow())); +} + +void DolphinMainWindow::setupDockWidgets() +{ + const bool lock = GeneralSettings::lockPanels(); + + KDualAction* lockLayoutAction = actionCollection()->add("lock_panels"); + lockLayoutAction->setActiveText(i18nc("@action:inmenu Panels", "Unlock Panels")); + lockLayoutAction->setActiveIcon(KIcon("object-unlocked")); + lockLayoutAction->setInactiveText(i18nc("@action:inmenu Panels", "Lock Panels")); + lockLayoutAction->setInactiveIcon(KIcon("object-locked")); + lockLayoutAction->setActive(lock); + connect(lockLayoutAction, SIGNAL(triggered()), this, SLOT(togglePanelLockState())); + + // Setup "Information" + DolphinDockWidget* infoDock = new DolphinDockWidget(i18nc("@title:window", "Information")); + infoDock->setLocked(lock); + infoDock->setObjectName("infoDock"); + infoDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea); + Panel* infoPanel = new InformationPanel(infoDock); + infoPanel->setCustomContextMenuActions(QList() << lockLayoutAction); + connect(infoPanel, SIGNAL(urlActivated(KUrl)), this, SLOT(handleUrl(KUrl))); + infoDock->setWidget(infoPanel); + + QAction* infoAction = infoDock->toggleViewAction(); + createPanelAction(KIcon("dialog-information"), Qt::Key_F11, infoAction, "show_information_panel"); + + addDockWidget(Qt::RightDockWidgetArea, infoDock); + connect(this, SIGNAL(urlChanged(KUrl)), + infoPanel, SLOT(setUrl(KUrl))); + connect(this, SIGNAL(selectionChanged(KFileItemList)), + infoPanel, SLOT(setSelection(KFileItemList))); + connect(this, SIGNAL(requestItemInfo(KFileItem)), + infoPanel, SLOT(requestDelayedItemInfo(KFileItem))); + + // Setup "Folders" + DolphinDockWidget* foldersDock = new DolphinDockWidget(i18nc("@title:window", "Folders")); + foldersDock->setLocked(lock); + foldersDock->setObjectName("foldersDock"); + foldersDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea); + FoldersPanel* foldersPanel = new FoldersPanel(foldersDock); + foldersPanel->setCustomContextMenuActions(QList() << lockLayoutAction); + foldersDock->setWidget(foldersPanel); + + QAction* foldersAction = foldersDock->toggleViewAction(); + createPanelAction(KIcon("folder"), Qt::Key_F7, foldersAction, "show_folders_panel"); + + addDockWidget(Qt::LeftDockWidgetArea, foldersDock); + connect(this, SIGNAL(urlChanged(KUrl)), + foldersPanel, SLOT(setUrl(KUrl))); + connect(foldersPanel, SIGNAL(folderActivated(KUrl)), + this, SLOT(changeUrl(KUrl))); + connect(foldersPanel, SIGNAL(folderMiddleClicked(KUrl)), + this, SLOT(openNewTab(KUrl))); + connect(foldersPanel, SIGNAL(errorMessage(QString)), + this, SLOT(slotPanelErrorMessage(QString))); + + // Setup "Terminal" + DolphinDockWidget* terminalDock = new DolphinDockWidget(i18nc("@title:window Shell terminal", "Terminal")); + terminalDock->setLocked(lock); + terminalDock->setObjectName("terminalDock"); + terminalDock->setAllowedAreas(Qt::TopDockWidgetArea | Qt::BottomDockWidgetArea); + Panel* terminalPanel = new TerminalPanel(terminalDock); + terminalPanel->setCustomContextMenuActions(QList() << lockLayoutAction); + terminalDock->setWidget(terminalPanel); + + connect(terminalPanel, SIGNAL(hideTerminalPanel()), terminalDock, SLOT(hide())); + connect(terminalPanel, SIGNAL(changeUrl(KUrl)), this, SLOT(slotTerminalDirectoryChanged(KUrl))); + connect(terminalDock, SIGNAL(visibilityChanged(bool)), + terminalPanel, SLOT(dockVisibilityChanged())); + + QAction* terminalAction = terminalDock->toggleViewAction(); + createPanelAction(KIcon("utilities-terminal"), Qt::Key_F4, terminalAction, "show_terminal_panel"); + + addDockWidget(Qt::BottomDockWidgetArea, terminalDock); + connect(this, SIGNAL(urlChanged(KUrl)), + terminalPanel, SLOT(setUrl(KUrl))); + + if (GeneralSettings::version() < 200) { + infoDock->hide(); + foldersDock->hide(); + terminalDock->hide(); + } + + // Setup "Places" + DolphinDockWidget* placesDock = new DolphinDockWidget(i18nc("@title:window", "Places")); + placesDock->setLocked(lock); + placesDock->setObjectName("placesDock"); + placesDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea); + + PlacesPanel* placesPanel = new PlacesPanel(placesDock); + placesPanel->setCustomContextMenuActions(QList() << lockLayoutAction); + placesDock->setWidget(placesPanel); + + QAction* placesAction = placesDock->toggleViewAction(); + createPanelAction(KIcon("bookmarks"), Qt::Key_F9, placesAction, "show_places_panel"); + + addDockWidget(Qt::LeftDockWidgetArea, placesDock); + connect(placesPanel, SIGNAL(placeActivated(KUrl)), + this, SLOT(slotPlaceActivated(KUrl))); + connect(placesPanel, SIGNAL(placeMiddleClicked(KUrl)), + this, SLOT(openNewTab(KUrl))); + connect(placesPanel, SIGNAL(errorMessage(QString)), + this, SLOT(slotPanelErrorMessage(QString))); + connect(this, SIGNAL(urlChanged(KUrl)), + placesPanel, SLOT(setUrl(KUrl))); + connect(placesDock, SIGNAL(visibilityChanged(bool)), + this, SLOT(slotPlacesPanelVisibilityChanged(bool))); + connect(this, SIGNAL(settingsChanged()), + placesPanel, SLOT(readSettings())); + + // Add actions into the "Panels" menu + KActionMenu* panelsMenu = new KActionMenu(i18nc("@action:inmenu View", "Panels"), this); + actionCollection()->addAction("panels", panelsMenu); + panelsMenu->setDelayed(false); + const KActionCollection* ac = actionCollection(); + panelsMenu->addAction(ac->action("show_places_panel")); + panelsMenu->addAction(ac->action("show_information_panel")); + panelsMenu->addAction(ac->action("show_folders_panel")); + panelsMenu->addAction(ac->action("show_terminal_panel")); + panelsMenu->addSeparator(); + panelsMenu->addAction(lockLayoutAction); +} + +void DolphinMainWindow::updateEditActions() +{ + const KFileItemList list = m_activeViewContainer->view()->selectedItems(); + if (list.isEmpty()) { + stateChanged("has_no_selection"); + } else { + stateChanged("has_selection"); + + KActionCollection* col = actionCollection(); + QAction* renameAction = col->action("rename"); + QAction* moveToTrashAction = col->action("move_to_trash"); + QAction* deleteAction = col->action("delete"); + QAction* cutAction = col->action(KStandardAction::name(KStandardAction::Cut)); + QAction* deleteWithTrashShortcut = col->action("delete_shortcut"); // see DolphinViewActionHandler + + KFileItemListProperties capabilities(list); + const bool enableMoveToTrash = capabilities.isLocal() && capabilities.supportsMoving(); + + renameAction->setEnabled(capabilities.supportsMoving()); + moveToTrashAction->setEnabled(enableMoveToTrash); + deleteAction->setEnabled(capabilities.supportsDeleting()); + deleteWithTrashShortcut->setEnabled(capabilities.supportsDeleting() && !enableMoveToTrash); + cutAction->setEnabled(capabilities.supportsMoving()); + } +} + +void DolphinMainWindow::updateViewActions() +{ + m_actionHandler->updateViewActions(); + + QAction* showFilterBarAction = actionCollection()->action("show_filter_bar"); + showFilterBarAction->setChecked(m_activeViewContainer->isFilterBarVisible()); + + updateSplitAction(); + + QAction* editableLocactionAction = actionCollection()->action("editable_location"); + const KUrlNavigator* urlNavigator = m_activeViewContainer->urlNavigator(); + editableLocactionAction->setChecked(urlNavigator->isUrlEditable()); +} + +void DolphinMainWindow::updateGoActions() +{ + QAction* goUpAction = actionCollection()->action(KStandardAction::name(KStandardAction::Up)); + const KUrl currentUrl = m_activeViewContainer->url(); + goUpAction->setEnabled(currentUrl.upUrl() != currentUrl); +} + +void DolphinMainWindow::createControlButton() +{ + if (m_controlButton) { + return; + } + Q_ASSERT(!m_controlButton); + + m_controlButton = new QToolButton(this); + m_controlButton->setIcon(KIcon("applications-system")); + m_controlButton->setText(i18nc("@action", "Control")); + m_controlButton->setPopupMode(QToolButton::InstantPopup); + m_controlButton->setToolButtonStyle(toolBar()->toolButtonStyle()); + + KMenu* controlMenu = new KMenu(m_controlButton); + connect(controlMenu, SIGNAL(aboutToShow()), this, SLOT(updateControlMenu())); + + m_controlButton->setMenu(controlMenu); + + toolBar()->addWidget(m_controlButton); + connect(toolBar(), SIGNAL(iconSizeChanged(QSize)), + m_controlButton, SLOT(setIconSize(QSize))); + connect(toolBar(), SIGNAL(toolButtonStyleChanged(Qt::ToolButtonStyle)), + m_controlButton, SLOT(setToolButtonStyle(Qt::ToolButtonStyle))); + + // The added widgets are owned by the toolbar and may get deleted when e.g. the toolbar + // gets edited. In this case we must add them again. The adding is done asynchronously by + // m_updateToolBarTimer. + connect(m_controlButton, SIGNAL(destroyed()), this, SLOT(slotControlButtonDeleted())); + m_updateToolBarTimer = new QTimer(this); + m_updateToolBarTimer->setInterval(500); + connect(m_updateToolBarTimer, SIGNAL(timeout()), this, SLOT(updateToolBar())); +} + +void DolphinMainWindow::deleteControlButton() +{ + delete m_controlButton; + m_controlButton = 0; + + delete m_updateToolBarTimer; + m_updateToolBarTimer = 0; +} + +bool DolphinMainWindow::addActionToMenu(QAction* action, KMenu* menu) +{ + Q_ASSERT(action); + Q_ASSERT(menu); + + const KToolBar* toolBarWidget = toolBar(); + foreach (const QWidget* widget, action->associatedWidgets()) { + if (widget == toolBarWidget) { + return false; + } + } + + menu->addAction(action); + return true; +} + +void DolphinMainWindow::refreshViews() +{ + foreach (DolphinTabPage* tabPage, m_viewTab) { + tabPage->refreshViews(); + } + + if (GeneralSettings::modifiedStartupSettings()) { + // The startup settings have been changed by the user (see bug #254947). + // Synchronize the split-view setting with the active view: + const bool splitView = GeneralSettings::splitView(); + m_viewTab.at(m_tabIndex)->setSplitViewEnabled(splitView); + updateSplitAction(); + } + + emit settingsChanged(); +} + +void DolphinMainWindow::clearStatusBar() +{ + m_activeViewContainer->statusBar()->resetToDefaultText(); +} + +void DolphinMainWindow::connectViewSignals(DolphinViewContainer* container) +{ + connect(container, SIGNAL(showFilterBarChanged(bool)), + this, SLOT(updateFilterBarAction(bool))); + connect(container, SIGNAL(writeStateChanged(bool)), + this, SLOT(slotWriteStateChanged(bool))); + + const DolphinView* view = container->view(); + connect(view, SIGNAL(selectionChanged(KFileItemList)), + this, SLOT(slotSelectionChanged(KFileItemList))); + connect(view, SIGNAL(requestItemInfo(KFileItem)), + this, SLOT(slotRequestItemInfo(KFileItem))); + connect(view, SIGNAL(tabRequested(KUrl)), + this, SLOT(openNewTab(KUrl))); + connect(view, SIGNAL(requestContextMenu(QPoint,KFileItem,KUrl,QList)), + this, SLOT(openContextMenu(QPoint,KFileItem,KUrl,QList))); + connect(view, SIGNAL(directoryLoadingStarted()), + this, SLOT(enableStopAction())); + connect(view, SIGNAL(directoryLoadingCompleted()), + this, SLOT(disableStopAction())); + + const KUrlNavigator* navigator = container->urlNavigator(); + connect(navigator, SIGNAL(urlChanged(KUrl)), + this, SLOT(changeUrl(KUrl))); + connect(navigator, SIGNAL(historyChanged()), + this, SLOT(updateHistory())); + connect(navigator, SIGNAL(editableStateChanged(bool)), + this, SLOT(slotEditableStateChanged(bool))); + connect(navigator, SIGNAL(tabRequested(KUrl)), + this, SLOT(openNewTab(KUrl))); +} + +void DolphinMainWindow::updateSplitAction() +{ + QAction* splitAction = actionCollection()->action("split_view"); + const DolphinTabPage* tabPage = m_viewTab.at(m_tabIndex); + if (tabPage->splitViewEnabled()) { + if (tabPage->primaryViewActive()) { + splitAction->setText(i18nc("@action:intoolbar Close left view", "Close")); + splitAction->setToolTip(i18nc("@info", "Close left view")); + splitAction->setIcon(KIcon("view-left-close")); + } else { + splitAction->setText(i18nc("@action:intoolbar Close right view", "Close")); + splitAction->setToolTip(i18nc("@info", "Close right view")); + splitAction->setIcon(KIcon("view-right-close")); + } + } else { + splitAction->setText(i18nc("@action:intoolbar Split view", "Split")); + splitAction->setToolTip(i18nc("@info", "Split view")); + splitAction->setIcon(KIcon("view-right-new")); + } +} + +QString DolphinMainWindow::tabName(const KUrl& url) const +{ + QString name; + if (url.equals(KUrl("file:///"))) { + name = '/'; + } else { + name = url.fileName(); + if (name.isEmpty()) { + name = url.protocol(); + } else { + // Make sure that a '&' inside the directory name is displayed correctly + // and not misinterpreted as a keyboard shortcut in QTabBar::setTabText() + name.replace('&', "&&"); + } + } + return name; +} + +void DolphinMainWindow::setUrlAsCaption(const KUrl& url) +{ + QString caption; + if (!url.isLocalFile()) { + caption.append(url.protocol() + " - "); + if (url.hasHost()) { + caption.append(url.host() + " - "); + } + } + + const QString fileName = url.fileName().isEmpty() ? "/" : url.fileName(); + caption.append(fileName); + + setCaption(caption); +} + +QString DolphinMainWindow::squeezedText(const QString& text) const +{ + const QFontMetrics fm = fontMetrics(); + return fm.elidedText(text, Qt::ElideMiddle, fm.maxWidth() * 10); +} + +void DolphinMainWindow::createPanelAction(const KIcon& icon, + const QKeySequence& shortcut, + QAction* dockAction, + const QString& actionName) +{ + KAction* panelAction = actionCollection()->addAction(actionName); + panelAction->setCheckable(true); + panelAction->setChecked(dockAction->isChecked()); + panelAction->setText(dockAction->text()); + panelAction->setIcon(icon); + panelAction->setShortcut(shortcut); + + connect(panelAction, SIGNAL(triggered()), dockAction, SLOT(trigger())); + connect(dockAction, SIGNAL(toggled(bool)), panelAction, SLOT(setChecked(bool))); +} + +DolphinMainWindow::UndoUiInterface::UndoUiInterface() : + KIO::FileUndoManager::UiInterface() +{ +} + +DolphinMainWindow::UndoUiInterface::~UndoUiInterface() +{ +} + +void DolphinMainWindow::UndoUiInterface::jobError(KIO::Job* job) +{ + DolphinMainWindow* mainWin= qobject_cast(parentWidget()); + if (mainWin) { + DolphinViewContainer* container = mainWin->activeViewContainer(); + container->showMessage(job->errorString(), DolphinViewContainer::Error); + } else { + KIO::FileUndoManager::UiInterface::jobError(job); + } +} + +#include "moc_dolphinmainwindow.cpp" diff --git a/dolphin/src/dolphinmainwindow.h b/dolphin/src/dolphinmainwindow.h new file mode 100644 index 00000000..c9d4bb8b --- /dev/null +++ b/dolphin/src/dolphinmainwindow.h @@ -0,0 +1,576 @@ +/*************************************************************************** + * Copyright (C) 2006 by Peter Penz * + * Copyright (C) 2006 by Stefan Monov * + * Copyright (C) 2006 by Cvetoslav Ludmiloff * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef DOLPHIN_MAINWINDOW_H +#define DOLPHIN_MAINWINDOW_H + +#include +#include +#include +#include +#include + +typedef KIO::FileUndoManager::CommandType CommandType; + +class DolphinViewActionHandler; +class DolphinApplication; +class DolphinSettingsDialog; +class DolphinViewContainer; +class DolphinRemoteEncoding; +class DolphinTabPage; +class KAction; +class KFileItem; +class KFileItemList; +class KJob; +class KNewFileMenu; +class KTabBar; +class KUrl; +#include +#include +#include + +/** + * @short Main window for Dolphin. + * + * Handles the menus, toolbars and Dolphin views. + */ +class DolphinMainWindow: public KXmlGuiWindow +{ + Q_OBJECT + Q_CLASSINFO("D-Bus Interface", "org.kde.dolphin.MainWindow") + Q_PROPERTY(int id READ getId SCRIPTABLE true) + friend class DolphinApplication; + +public: + DolphinMainWindow(); + virtual ~DolphinMainWindow(); + + /** + * Returns the currently active view. + * All menu actions are applied to this view. When + * having a split view setup, the nonactive view + * is usually shown in darker colors. + */ + DolphinViewContainer* activeViewContainer() const; + + /** + * Opens each directory in \p dirs in a separate tab. If the "split view" + * option is enabled, 2 directories are collected within one tab. + */ + void openDirectories(const QList& dirs); + + /** + * Opens the directory which contains the files \p files + * and selects all files (implements the --select option + * of Dolphin). + */ + void openFiles(const QList& files); + + /** + * Returns the 'Create New...' sub menu which also can be shared + * with other menus (e. g. a context menu). + */ + KNewFileMenu* newFileMenu() const; + +public slots: + /** + * Pastes the clipboard data into the currently selected folder + * of the active view. If not exactly one folder is selected, + * no pasting is done at all. + */ + void pasteIntoFolder(); + + /** + * Returns the main window ID used through DBus. + */ + int getId() const; + + /** + * Implementation of the MainWindowAdaptor/QDBusAbstractAdaptor interface. + * Inform all affected dolphin components (panels, views) of an URL + * change. + */ + void changeUrl(const KUrl& url); + + /** + * The current directory of the Terminal Panel has changed, probably because + * the user entered a 'cd' command. This slot calls changeUrl(url) and makes + * sure that the panel keeps the keyboard focus. + */ + void slotTerminalDirectoryChanged(const KUrl& url); + + /** Stores all settings and quits Dolphin. */ + void quit(); + +signals: + /** + * Is sent if the selection of the currently active view has + * been changed. + */ + void selectionChanged(const KFileItemList& selection); + + /** + * Is sent if the url of the currently active view has + * been changed. + */ + void urlChanged(const KUrl& url); + + /** + * Is emitted if information of an item is requested to be shown e. g. in the panel. + * If item is null, no item information request is pending. + */ + void requestItemInfo(const KFileItem& item); + + /** + * Is emitted if the settings have been changed. + */ + void settingsChanged(); + + /** + * Is emitted when a tab has been closed. + */ + void rememberClosedTab(const KUrl& primaryUrl, const KUrl& secondaryUrl); + +protected: + /** @see QWidget::showEvent() */ + virtual void showEvent(QShowEvent* event); + + /** @see QMainWindow::closeEvent() */ + virtual void closeEvent(QCloseEvent* event); + + /** @see KMainWindow::saveProperties() */ + virtual void saveProperties(KConfigGroup& group); + + /** @see KMainWindow::readProperties() */ + virtual void readProperties(const KConfigGroup& group); + +private slots: + /** + * Refreshes the views of the main window by recreating them according to + * the given Dolphin settings. + */ + void refreshViews(); + + void clearStatusBar(); + + /** Updates the 'Create New...' sub menu. */ + void updateNewMenu(); + + void createDirectory(); + + /** Shows the error message in the status bar of the active view. */ + void showErrorMessage(const QString& message); + + /** + * Updates the state of the 'Undo' menu action dependent + * on the parameter \a available. + */ + void slotUndoAvailable(bool available); + + /** Sets the text of the 'Undo' menu action to \a text. */ + void slotUndoTextChanged(const QString& text); + + /** Performs the current undo operation. */ + void undo(); + + /** + * Copies all selected items to the clipboard and marks + * the items as cut. + */ + void cut(); + + /** Copies all selected items to the clipboard. */ + void copy(); + + /** Pastes the clipboard data to the active view. */ + void paste(); + + /** Replaces the URL navigator by a search box to find files. */ + void find(); + + /** + * Updates the text of the paste action dependent on + * the number of items which are in the clipboard. + */ + void updatePasteAction(); + + /** Selects all items from the active view. */ + void selectAll(); + + /** + * Inverts the selection of all items of the active view: + * Selected items get nonselected and nonselected items get + * selected. + */ + void invertSelection(); + + /** + * Switches between one and two views: + * If one view is visible, it will get split into two views. + * If already two views are visible, the active view gets closed. + */ + void toggleSplitView(); + + /** Reloads the currently active view. */ + void reloadView(); + + /** Stops the loading process for the currently active view. */ + void stopLoading(); + + void enableStopAction(); + void disableStopAction(); + + void showFilterBar(); + + /** + * Toggles between edit and browse mode of the navigation bar. + */ + void toggleEditLocation(); + + /** + * Switches to the edit mode of the navigation bar and selects + * the whole URL, so that it can be replaced by the user. If the edit mode is + * already active, it is assured that the navigation bar get focused. + */ + void replaceLocation(); + + /** + * Toggles the state of the panels between a locked and unlocked layout. + */ + void togglePanelLockState(); + + /** + * Is invoked if the Places panel got visible/invisible and takes care + * that the places-selector of all views is only shown if the Places panel + * is invisible. + */ + void slotPlacesPanelVisibilityChanged(bool visible); + + /** Goes back one step of the URL history. */ + void goBack(); + + /** Goes forward one step of the URL history. */ + void goForward(); + + /** Goes up one hierarchy of the current URL. */ + void goUp(); + + /** Changes the location to the home URL. */ + void goHome(); + + /** + * Open the previous URL in the URL history in a new tab + * if the middle mouse button is clicked. + */ + void goBack(Qt::MouseButtons buttons); + + /** + * Open the next URL in the URL history in a new tab + * if the middle mouse button is clicked. + */ + void goForward(Qt::MouseButtons buttons); + + /** + * Open the URL one hierarchy above the current URL in a new tab + * if the middle mouse button is clicked. + */ + void goUp(Qt::MouseButtons buttons); + + /** + * Open the home URL in a new tab + */ + void goHome(Qt::MouseButtons buttons); + + /** + * Hides the menu bar if it is visible, makes the menu bar + * visible if it is hidden. + */ + void toggleShowMenuBar(); + + /** Opens a terminal window for the current location. */ + void openTerminal(); + + /** Opens the settings dialog for Dolphin. */ + void editSettings(); + + /** Updates the state of the 'Show Full Location' action. */ + void slotEditableStateChanged(bool editable); + + /** + * Updates the state of the 'Edit' menu actions and emits + * the signal selectionChanged(). + */ + void slotSelectionChanged(const KFileItemList& selection); + + /** Emits the signal requestItemInfo(). */ + void slotRequestItemInfo(const KFileItem&); + + /** + * Updates the state of the 'Back' and 'Forward' menu + * actions corresponding to the current history. + */ + void updateHistory(); + + /** Updates the state of the 'Show filter bar' menu action. */ + void updateFilterBarAction(bool show); + + /** Open a new main window. */ + void openNewMainWindow(); + + /** Opens a new view with the current URL that is part of a tab. */ + void openNewTab(); + + /** + * Opens a new tab in the background showing the URL \a primaryUrl and the + * optional URL \a secondaryUrl. + */ + void openNewTab(const KUrl& primaryUrl, const KUrl& secondaryUrl = KUrl()); + + /** + * Opens a new tab showing the URL \a primaryUrl and the optional URL + * \a secondaryUrl and activates the tab. + */ + void openNewActivatedTab(const KUrl& primaryUrl, const KUrl& secondaryUrl = KUrl()); + + void activateNextTab(); + + void activatePrevTab(); + + /** + * Opens the selected folder in a new tab. + */ + void openInNewTab(); + + /** + * Opens the selected folder in a new window. + */ + void openInNewWindow(); + + /** + * Indicates in the statusbar that the execution of the command \a command + * has been finished. + */ + void showCommand(CommandType command); + + /** + * Activates the tab with the index \a index, which means that the current view + * is replaced by the view of the given tab. + */ + void setActiveTab(int index); + + /** Closes the currently active tab. */ + void closeTab(); + + /** + * Closes the tab with the index \a index and activates the tab with index - 1. + */ + void closeTab(int index); + + /** + * Opens a context menu for the tab with the index \a index + * on the position \a pos. + */ + void openTabContextMenu(int index, const QPoint& pos); + + /** + * Is connected to the QTabBar signal tabMoved(int from, int to). + * Reorders the list of tabs after a tab was moved in the tab bar + * and sets m_tabIndex to the new index of the current tab. + */ + void slotTabMoved(int from, int to); + + /** + * Is connected to the KTabBar signal testCanDecode() and adjusts + * the output parameter \a accept. + */ + void slotTestCanDecode(const QDragMoveEvent* event, bool& accept); + + /** + * If the URL can be listed, open it in the current view, otherwise + * run it through KRun. + */ + void handleUrl(const KUrl& url); + + /** + * handleUrl() can trigger a stat job to see if the url can actually + * be listed. + */ + void slotHandleUrlStatFinished(KJob* job); + + /** + * Is connected to the KTabBar signal receivedDropEvent. + * Allows dragging and dropping files onto tabs. + */ + void tabDropEvent(int tab, QDropEvent* event); + + /** + * Is invoked when the write state of a folder has been changed and + * enables/disables the "Create New..." menu entry. + */ + void slotWriteStateChanged(bool isFolderWritable); + + /** + * Opens the context menu on the current mouse position. + * @pos Position in screen coordinates. + * @item File item context. If item is null, the context menu + * should be applied to \a url. + * @url URL which contains \a item. + * @customActions Actions that should be added to the context menu, + * if the file item is null. + */ + void openContextMenu(const QPoint& pos, + const KFileItem& item, + const KUrl& url, + const QList& customActions); + + void updateControlMenu(); + void updateToolBar(); + void slotControlButtonDeleted(); + + /** + * Is called if a panel emits an error-message and shows + * the error-message in the active view-container. + */ + void slotPanelErrorMessage(const QString& error); + + /** + * Is called if the user clicked an item in the Places Panel. + * Reloads the view if \a url is the current URL already, and changes the + * current URL otherwise. + */ + void slotPlaceActivated(const KUrl& url); + + void activeViewChanged(); + +private: + /** + * Activates the given view, which means that + * all menu actions are applied to this view. When + * having a split view setup, the nonactive view + * is usually shown in darker colors. + */ + void setActiveViewContainer(DolphinViewContainer* view); + + void setupActions(); + void setupDockWidgets(); + void updateEditActions(); + void updateViewActions(); + void updateGoActions(); + + void createControlButton(); + void deleteControlButton(); + + /** + * Adds the action \p action to the menu \p menu in + * case if it has not added already to the toolbar. + * @return True if the action has been added to the menu. + */ + bool addActionToMenu(QAction* action, KMenu* menu); + + /** + * Connects the signals from the created DolphinView with + * the DolphinViewContainer \a container with the corresponding slots of + * the DolphinMainWindow. This method must be invoked each + * time a DolphinView has been created. + */ + void connectViewSignals(DolphinViewContainer* container); + + /** + * Updates the text of the split action: + * If two views are shown, the text is set to "Split", + * otherwise the text is set to "Join". The icon + * is updated to match with the text and the currently active view. + */ + void updateSplitAction(); + + /** Returns the name of the tab for the URL \a url. */ + QString tabName(const KUrl& url) const; + + /** + * Sets the window caption to url.fileName() if this is non-empty, + * "/" if the URL is "file:///", and url.protocol() otherwise. + */ + void setUrlAsCaption(const KUrl& url); + + QString squeezedText(const QString& text) const; + + /** + * Creates an action for showing/hiding a panel, that is accessible + * in "Configure toolbars..." and "Configure shortcuts...". This is necessary + * as the action for toggling the dock visibility is done by Qt which + * is no KAction instance. + */ + void createPanelAction(const KIcon& icon, + const QKeySequence& shortcut, + QAction* dockAction, + const QString& actionName); + +private: + /** + * Implements a custom error handling for the undo manager. This + * assures that all errors are shown in the status bar of Dolphin + * instead as modal error dialog with an OK button. + */ + class UndoUiInterface : public KIO::FileUndoManager::UiInterface + { + public: + UndoUiInterface(); + virtual ~UndoUiInterface(); + virtual void jobError(KIO::Job* job); + }; + + KNewFileMenu* m_newFileMenu; + KTabBar* m_tabBar; + DolphinViewContainer* m_activeViewContainer; + QVBoxLayout* m_centralWidgetLayout; + int m_id; + + int m_tabIndex; + QList m_viewTab; + + DolphinViewActionHandler* m_actionHandler; + DolphinRemoteEncoding* m_remoteEncoding; + QWeakPointer m_settingsDialog; + + // Members for the toolbar menu that is shown when the menubar is hidden: + QToolButton* m_controlButton; + QTimer* m_updateToolBarTimer; + + KIO::Job* m_lastHandleUrlStatJob; +}; + +inline DolphinViewContainer* DolphinMainWindow::activeViewContainer() const +{ + return m_activeViewContainer; +} + +inline KNewFileMenu* DolphinMainWindow::newFileMenu() const +{ + return m_newFileMenu; +} + +inline int DolphinMainWindow::getId() const +{ + return m_id; +} + +#endif // DOLPHIN_MAINWINDOW_H + diff --git a/dolphin/src/dolphinnewfilemenu.cpp b/dolphin/src/dolphinnewfilemenu.cpp new file mode 100644 index 00000000..5fa5a792 --- /dev/null +++ b/dolphin/src/dolphinnewfilemenu.cpp @@ -0,0 +1,48 @@ +/*************************************************************************** + * Copyright (C) 2006 by Peter Penz * + * peter.penz@gmx.at * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "dolphinnewfilemenu.h" + +#include "views/dolphinnewfilemenuobserver.h" + +#include +#include + +DolphinNewFileMenu::DolphinNewFileMenu(KActionCollection* collection, QObject* parent) : + KNewFileMenu(collection, "new_menu", parent) +{ + DolphinNewFileMenuObserver::instance().attach(this); +} + +DolphinNewFileMenu::~DolphinNewFileMenu() +{ + DolphinNewFileMenuObserver::instance().detach(this); +} + +void DolphinNewFileMenu::slotResult(KJob* job) +{ + if (job->error()) { + emit errorMessage(job->errorString()); + } else { + KNewFileMenu::slotResult(job); + } +} + +#include "moc_dolphinnewfilemenu.cpp" diff --git a/dolphin/src/dolphinnewfilemenu.h b/dolphin/src/dolphinnewfilemenu.h new file mode 100644 index 00000000..f1408fd6 --- /dev/null +++ b/dolphin/src/dolphinnewfilemenu.h @@ -0,0 +1,54 @@ +/*************************************************************************** + * Copyright (C) 2006 by Peter Penz * + * peter.penz@gmx.at * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef DOLPHINNEWFILEMENU_H +#define DOLPHINNEWFILEMENU_H + +#include + +#include "dolphinprivate_export.h" + +class KJob; + +/** + * @brief Represents the 'Create New...' sub menu for the File menu + * and the context menu. + * + * The only difference to KNewFileMenu is the custom error handling. + * All errors are shown in the status bar of Dolphin + * instead as modal error dialog with an OK button. + */ +class DOLPHINPRIVATE_EXPORT DolphinNewFileMenu : public KNewFileMenu +{ + Q_OBJECT + +public: + DolphinNewFileMenu(KActionCollection* collection, QObject* parent); + virtual ~DolphinNewFileMenu(); + +signals: + void errorMessage(const QString& error); + +protected slots: + /** @see KNewFileMenu::slotResult() */ + virtual void slotResult(KJob* job); +}; + +#endif diff --git a/dolphin/src/dolphinpart.cpp b/dolphin/src/dolphinpart.cpp new file mode 100644 index 00000000..7f4acd32 --- /dev/null +++ b/dolphin/src/dolphinpart.cpp @@ -0,0 +1,613 @@ +/* This file is part of the KDE project + Copyright (c) 2007 David Faure + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "dolphinpart.h" +#include "dolphinremoveaction.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include "dolphinpart_ext.h" +#include "dolphinnewfilemenu.h" +#include "views/dolphinview.h" +#include "views/dolphinviewactionhandler.h" +#include "views/dolphinnewfilemenuobserver.h" +#include "views/dolphinremoteencoding.h" +#include "kitemviews/kfileitemmodel.h" +#include "kitemviews/private/kfileitemmodeldirlister.h" + +#include +#include +#include +#include +#include + +K_PLUGIN_FACTORY(DolphinPartFactory, registerPlugin();) +K_EXPORT_PLUGIN(DolphinPartFactory("dolphinpart", "dolphin")) + +DolphinPart::DolphinPart(QWidget* parentWidget, QObject* parent, const QVariantList& args) + : KParts::ReadOnlyPart(parent) + ,m_openTerminalAction(0) + ,m_removeAction(0) +{ + Q_UNUSED(args) + setComponentData(DolphinPartFactory::componentData(), false); + m_extension = new DolphinPartBrowserExtension(this); + + // make sure that other apps using this part find Dolphin's view-file-columns icons + KIconLoader::global()->addAppDir("dolphin"); + + m_view = new DolphinView(KUrl(), parentWidget); + m_view->setTabsForFilesEnabled(true); + setWidget(m_view); + + connect(&DolphinNewFileMenuObserver::instance(), SIGNAL(errorMessage(QString)), + this, SLOT(slotErrorMessage(QString))); + + connect(m_view, SIGNAL(directoryLoadingCompleted()), this, SIGNAL(completed())); + connect(m_view, SIGNAL(directoryLoadingProgress(int)), this, SLOT(updateProgress(int))); + connect(m_view, SIGNAL(errorMessage(QString)), this, SLOT(slotErrorMessage(QString))); + + setXMLFile("dolphinpart.rc"); + + connect(m_view, SIGNAL(infoMessage(QString)), + this, SLOT(slotMessage(QString))); + connect(m_view, SIGNAL(operationCompletedMessage(QString)), + this, SLOT(slotMessage(QString))); + connect(m_view, SIGNAL(errorMessage(QString)), + this, SLOT(slotErrorMessage(QString))); + connect(m_view, SIGNAL(itemActivated(KFileItem)), + this, SLOT(slotItemActivated(KFileItem))); + connect(m_view, SIGNAL(itemsActivated(KFileItemList)), + this, SLOT(slotItemsActivated(KFileItemList))); + connect(m_view, SIGNAL(tabRequested(KUrl)), + this, SLOT(createNewWindow(KUrl))); + connect(m_view, SIGNAL(requestContextMenu(QPoint,KFileItem,KUrl,QList)), + this, SLOT(slotOpenContextMenu(QPoint,KFileItem,KUrl,QList))); + connect(m_view, SIGNAL(selectionChanged(KFileItemList)), + m_extension, SIGNAL(selectionInfo(KFileItemList))); + connect(m_view, SIGNAL(selectionChanged(KFileItemList)), + this, SLOT(slotSelectionChanged(KFileItemList))); + connect(m_view, SIGNAL(requestItemInfo(KFileItem)), + this, SLOT(slotRequestItemInfo(KFileItem))); + connect(m_view, SIGNAL(modeChanged(DolphinView::Mode,DolphinView::Mode)), + this, SIGNAL(viewModeChanged())); // relay signal + connect(m_view, SIGNAL(redirection(KUrl,KUrl)), + this, SLOT(slotDirectoryRedirection(KUrl,KUrl))); + + // Watch for changes that should result in updates to the + // status bar text. + connect(m_view, SIGNAL(itemCountChanged()), this, SLOT(updateStatusBar())); + connect(m_view, SIGNAL(selectionChanged(KFileItemList)), this, SLOT(updateStatusBar())); + + m_actionHandler = new DolphinViewActionHandler(actionCollection(), this); + m_actionHandler->setCurrentView(m_view); + connect(m_actionHandler, SIGNAL(createDirectory()), SLOT(createDirectory())); + + m_remoteEncoding = new DolphinRemoteEncoding(this, m_actionHandler); + connect(this, SIGNAL(aboutToOpenURL()), + m_remoteEncoding, SLOT(slotAboutToOpenUrl())); + + QClipboard* clipboard = QApplication::clipboard(); + connect(clipboard, SIGNAL(dataChanged()), + this, SLOT(updatePasteAction())); + + // Create file info and listing filter extensions. + // NOTE: Listing filter needs to be instantiated after the creation of the view. + new DolphinPartFileInfoExtension(this); + + new DolphinPartListingFilterExtension(this); + + KDirLister* lister = m_view->m_model->m_dirLister; + if (lister) { + DolphinPartListingNotificationExtension* notifyExt = new DolphinPartListingNotificationExtension(this); + connect(lister, SIGNAL(newItems(KFileItemList)), notifyExt, SLOT(slotNewItems(KFileItemList))); + connect(lister, SIGNAL(itemsDeleted(KFileItemList)), notifyExt, SLOT(slotItemsDeleted(KFileItemList))); + } else { + kWarning() << "NULL KDirLister object! KParts::ListingNotificationExtension will NOT be supported"; + } + + createActions(); + m_actionHandler->updateViewActions(); + slotSelectionChanged(KFileItemList()); // initially disable selection-dependent actions + + // Listen to events from the app so we can update the remove key by + // checking for a Shift key press. + qApp->installEventFilter(this); + + // TODO there was a "always open a new window" (when clicking on a directory) setting in konqueror + // (sort of spacial navigation) + + loadPlugins(this, this, componentData()); +} + +DolphinPart::~DolphinPart() +{ +} + +void DolphinPart::createActions() +{ + // Edit menu + + m_newFileMenu = new DolphinNewFileMenu(actionCollection(), this); + m_newFileMenu->setParentWidget(widget()); + connect(m_newFileMenu->menu(), SIGNAL(aboutToShow()), + this, SLOT(updateNewMenu())); + + KAction *editMimeTypeAction = actionCollection()->addAction( "editMimeType" ); + editMimeTypeAction->setText( i18nc("@action:inmenu Edit", "&Edit File Type..." ) ); + connect(editMimeTypeAction, SIGNAL(triggered()), SLOT(slotEditMimeType())); + + KAction* selectItemsMatching = actionCollection()->addAction("select_items_matching"); + selectItemsMatching->setText(i18nc("@action:inmenu Edit", "Select Items Matching...")); + selectItemsMatching->setShortcut(Qt::CTRL | Qt::Key_S); + connect(selectItemsMatching, SIGNAL(triggered()), this, SLOT(slotSelectItemsMatchingPattern())); + + KAction* unselectItemsMatching = actionCollection()->addAction("unselect_items_matching"); + unselectItemsMatching->setText(i18nc("@action:inmenu Edit", "Unselect Items Matching...")); + connect(unselectItemsMatching, SIGNAL(triggered()), this, SLOT(slotUnselectItemsMatchingPattern())); + + actionCollection()->addAction(KStandardAction::SelectAll, "select_all", m_view, SLOT(selectAll())); + + KAction* unselectAll = actionCollection()->addAction("unselect_all"); + unselectAll->setText(i18nc("@action:inmenu Edit", "Unselect All")); + connect(unselectAll, SIGNAL(triggered()), m_view, SLOT(clearSelection())); + + KAction* invertSelection = actionCollection()->addAction("invert_selection"); + invertSelection->setText(i18nc("@action:inmenu Edit", "Invert Selection")); + invertSelection->setShortcut(Qt::CTRL | Qt::SHIFT | Qt::Key_A); + connect(invertSelection, SIGNAL(triggered()), m_view, SLOT(invertSelection())); + + // View menu: all done by DolphinViewActionHandler + + // Go menu + + QActionGroup* goActionGroup = new QActionGroup(this); + connect(goActionGroup, SIGNAL(triggered(QAction*)), + this, SLOT(slotGoTriggered(QAction*))); + + createGoAction("go_applications", "start-here-kde", + i18nc("@action:inmenu Go", "App&lications"), QString("programs:/"), + goActionGroup); + createGoAction("go_network_folders", "folder-remote", + i18nc("@action:inmenu Go", "&Network Folders"), QString("remote:/"), + goActionGroup); + createGoAction("go_settings", "preferences-system", + i18nc("@action:inmenu Go", "Sett&ings"), QString("settings:/"), + goActionGroup); + createGoAction("go_trash", "user-trash", + i18nc("@action:inmenu Go", "Trash"), QString("trash:/"), + goActionGroup); + createGoAction("go_autostart", "", + i18nc("@action:inmenu Go", "Autostart"), KGlobalSettings::autostartPath(), + goActionGroup); + + // Tools menu + m_findFileAction = actionCollection()->addAction("find_file"); + m_findFileAction->setText(i18nc("@action:inmenu Tools", "Find File...")); + m_findFileAction->setShortcut(Qt::CTRL | Qt::Key_F); + m_findFileAction->setIcon(KIcon("edit-find")); + connect(m_findFileAction, SIGNAL(triggered()), this, SLOT(slotFindFile())); + + m_openTerminalAction = actionCollection()->addAction("open_terminal"); + m_openTerminalAction->setIcon(KIcon("utilities-terminal")); + m_openTerminalAction->setText(i18nc("@action:inmenu Tools", "Open &Terminal")); + connect(m_openTerminalAction, SIGNAL(triggered()), SLOT(slotOpenTerminal())); + m_openTerminalAction->setShortcut(Qt::Key_F4); +} + +void DolphinPart::createGoAction(const char* name, const char* iconName, + const QString& text, const QString& url, + QActionGroup* actionGroup) +{ + KAction* action = actionCollection()->addAction(name); + action->setIcon(KIcon(iconName)); + action->setText(text); + action->setData(url); + action->setActionGroup(actionGroup); +} + +void DolphinPart::slotGoTriggered(QAction* action) +{ + const QString url = action->data().toString(); + emit m_extension->openUrlRequest(KUrl(url)); +} + +void DolphinPart::slotSelectionChanged(const KFileItemList& selection) +{ + const bool hasSelection = !selection.isEmpty(); + + QAction* renameAction = actionCollection()->action("rename"); + QAction* moveToTrashAction = actionCollection()->action("move_to_trash"); + QAction* deleteAction = actionCollection()->action("delete"); + QAction* editMimeTypeAction = actionCollection()->action("editMimeType"); + QAction* propertiesAction = actionCollection()->action("properties"); + QAction* deleteWithTrashShortcut = actionCollection()->action("delete_shortcut"); // see DolphinViewActionHandler + + if (!hasSelection) { + stateChanged("has_no_selection"); + + emit m_extension->enableAction("cut", false); + emit m_extension->enableAction("copy", false); + deleteWithTrashShortcut->setEnabled(false); + editMimeTypeAction->setEnabled(false); + } else { + stateChanged("has_selection"); + + // TODO share this code with DolphinMainWindow::updateEditActions (and the desktop code) + // in libkonq + KFileItemListProperties capabilities(selection); + const bool enableMoveToTrash = capabilities.isLocal() && capabilities.supportsMoving(); + + renameAction->setEnabled(capabilities.supportsMoving()); + moveToTrashAction->setEnabled(enableMoveToTrash); + deleteAction->setEnabled(capabilities.supportsDeleting()); + deleteWithTrashShortcut->setEnabled(capabilities.supportsDeleting() && !enableMoveToTrash); + editMimeTypeAction->setEnabled(true); + propertiesAction->setEnabled(true); + emit m_extension->enableAction("cut", capabilities.supportsMoving()); + emit m_extension->enableAction("copy", true); + } +} + +void DolphinPart::updatePasteAction() +{ + QPair pasteInfo = m_view->pasteInfo(); + emit m_extension->enableAction( "paste", pasteInfo.first ); + emit m_extension->setActionText( "paste", pasteInfo.second ); +} + +KAboutData* DolphinPart::createAboutData() +{ + return new KAboutData("dolphinpart", "dolphin", ki18nc("@title", "Dolphin Part"), "0.1"); +} + +bool DolphinPart::openUrl(const KUrl& url) +{ + bool reload = arguments().reload(); + // A bit of a workaround so that changing the namefilter works: force reload. + // Otherwise DolphinView wouldn't relist the URL, so nothing would happen. + if (m_nameFilter != m_view->nameFilter()) + reload = true; + if (m_view->url() == url && !reload) { // DolphinView won't do anything in that case, so don't emit started + return true; + } + setUrl(url); // remember it at the KParts level + KUrl visibleUrl(url); + if (!m_nameFilter.isEmpty()) { + visibleUrl.addPath(m_nameFilter); + } + QString prettyUrl = visibleUrl.pathOrUrl(); + emit setWindowCaption(prettyUrl); + emit m_extension->setLocationBarUrl(prettyUrl); + emit started(0); // get the wheel to spin + m_view->setNameFilter(m_nameFilter); + m_view->setUrl(url); + updatePasteAction(); + emit aboutToOpenURL(); + if (reload) + m_view->reload(); + // Disable "Find File" and "Open Terminal" actions for non-file URLs, + // e.g. ftp, smb, etc. #279283 + const bool isLocalUrl = url.isLocalFile(); + m_findFileAction->setEnabled(isLocalUrl); + if (m_openTerminalAction) { + m_openTerminalAction->setEnabled(isLocalUrl); + } + return true; +} + +void DolphinPart::slotMessage(const QString& msg) +{ + emit setStatusBarText(msg); +} + +void DolphinPart::slotErrorMessage(const QString& msg) +{ + kDebug() << msg; + emit canceled(msg); + //KMessageBox::error(m_view, msg); +} + +void DolphinPart::slotRequestItemInfo(const KFileItem& item) +{ + emit m_extension->mouseOverInfo(item); + if (item.isNull()) { + updateStatusBar(); + } else { + const QString escapedText = Qt::convertFromPlainText(item.getStatusBarInfo()); + ReadOnlyPart::setStatusBarText(QString("%1").arg(escapedText)); + } +} + +void DolphinPart::slotItemActivated(const KFileItem& item) +{ + KParts::OpenUrlArguments args; + // Forget about the known mimetype if a target URL is used. + // Testcase: network:/ with a item (mimetype "inode/some-foo-service") pointing to a http URL (html) + if (item.targetUrl() == item.url()) { + args.setMimeType(item.mimetype()); + } + + // Ideally, konqueror should be changed to not require trustedSource for directory views, + // since the idea was not to need BrowserArguments for non-browser stuff... + KParts::BrowserArguments browserArgs; + browserArgs.trustedSource = true; + emit m_extension->openUrlRequest(item.targetUrl(), args, browserArgs); +} + +void DolphinPart::slotItemsActivated(const KFileItemList& items) +{ + foreach (const KFileItem& item, items) { + slotItemActivated(item); + } +} + +void DolphinPart::createNewWindow(const KUrl& url) +{ + // TODO: Check issue N176832 for the missing QAIV signal; task 177399 - maybe this code + // should be moved into DolphinPart::slotItemActivated() + emit m_extension->createNewWindow(url); +} + +void DolphinPart::slotOpenContextMenu(const QPoint& pos, + const KFileItem& _item, + const KUrl&, + const QList& customActions) +{ + KParts::BrowserExtension::PopupFlags popupFlags = KParts::BrowserExtension::DefaultPopupItems + | KParts::BrowserExtension::ShowProperties + | KParts::BrowserExtension::ShowUrlOperations; + + KFileItem item(_item); + + if (item.isNull()) { // viewport context menu + popupFlags |= KParts::BrowserExtension::ShowNavigationItems | KParts::BrowserExtension::ShowUp; + item = m_view->rootItem(); + if (item.isNull()) + item = KFileItem( S_IFDIR, (mode_t)-1, url() ); + else + item.setUrl(url()); // ensure we use the view url, not the canonical path (#213799) + } + + // TODO: We should change the signature of the slots (and signals) for being able + // to tell for which items we want a popup. + KFileItemList items; + if (m_view->selectedItems().isEmpty()) { + items.append(item); + } else { + items = m_view->selectedItems(); + } + + KFileItemListProperties capabilities(items); + + KParts::BrowserExtension::ActionGroupMap actionGroups; + QList editActions; + editActions += m_view->versionControlActions(m_view->selectedItems()); + editActions += customActions; + + if (!_item.isNull()) { // only for context menu on one or more items + const bool supportsMoving = capabilities.supportsMoving(); + + if (capabilities.supportsDeleting()) { + const bool showDeleteAction = (KGlobal::config()->group("KDE").readEntry("ShowDeleteCommand", false) || + !item.isLocalFile()); + const bool showMoveToTrashAction = capabilities.isLocal() && supportsMoving; + + if (showDeleteAction && showMoveToTrashAction) { + delete m_removeAction; + m_removeAction = 0; + editActions.append(actionCollection()->action("move_to_trash")); + editActions.append(actionCollection()->action("delete")); + } else if (showDeleteAction && !showMoveToTrashAction) { + editActions.append(actionCollection()->action("delete")); + } else { + if (!m_removeAction) + m_removeAction = new DolphinRemoveAction(this, actionCollection()); + editActions.append(m_removeAction); + m_removeAction->update(); + } + } else { + popupFlags |= KParts::BrowserExtension::NoDeletion; + } + + if (supportsMoving) { + editActions.append(actionCollection()->action("rename")); + } + + // Normally KonqPopupMenu only shows the "Create new" submenu in the current view + // since otherwise the created file would not be visible. + // But in treeview mode we should allow it. + if (m_view->itemsExpandable()) + popupFlags |= KParts::BrowserExtension::ShowCreateDirectory; + + } + + actionGroups.insert("editactions", editActions); + + emit m_extension->popupMenu(pos, + items, + KParts::OpenUrlArguments(), + KParts::BrowserArguments(), + popupFlags, + actionGroups); +} + +void DolphinPart::slotDirectoryRedirection(const KUrl& oldUrl, const KUrl& newUrl) +{ + //kDebug() << oldUrl << newUrl << "currentUrl=" << url(); + if (oldUrl.equals(url(), KUrl::CompareWithoutTrailingSlash /* #207572 */)) { + KParts::ReadOnlyPart::setUrl(newUrl); + const QString prettyUrl = newUrl.pathOrUrl(); + emit m_extension->setLocationBarUrl(prettyUrl); + } +} + + +void DolphinPart::slotEditMimeType() +{ + const KFileItemList items = m_view->selectedItems(); + if (!items.isEmpty()) { + KonqOperations::editMimeType(items.first().mimetype(), m_view); + } +} + +void DolphinPart::slotSelectItemsMatchingPattern() +{ + openSelectionDialog(i18nc("@title:window", "Select"), + i18n("Select all items matching this pattern:"), + true); +} + +void DolphinPart::slotUnselectItemsMatchingPattern() +{ + openSelectionDialog(i18nc("@title:window", "Unselect"), + i18n("Unselect all items matching this pattern:"), + false); +} + +void DolphinPart::openSelectionDialog(const QString& title, const QString& text, bool selectItems) +{ + bool okClicked; + QString pattern = KInputDialog::getText(title, text, "*", &okClicked, m_view); + + if (okClicked && !pattern.isEmpty()) { + QRegExp patternRegExp(pattern, Qt::CaseSensitive, QRegExp::Wildcard); + m_view->selectItems(patternRegExp, selectItems); + } +} + +void DolphinPart::setCurrentViewMode(const QString& viewModeName) +{ + QAction* action = actionCollection()->action(viewModeName); + Q_ASSERT(action); + action->trigger(); +} + +QString DolphinPart::currentViewMode() const +{ + return m_actionHandler->currentViewModeActionName(); +} + +void DolphinPart::setNameFilter(const QString& nameFilter) +{ + // This is the "/home/dfaure/*.diff" kind of name filter (KDirLister::setNameFilter) + // which is unrelated to DolphinView::setNameFilter which is substring filtering in a proxy. + m_nameFilter = nameFilter; + // TODO save/restore name filter in saveState/restoreState like KonqDirPart did in kde3? +} + +void DolphinPart::slotOpenTerminal() +{ + QString dir(QDir::homePath()); + + KUrl u(url()); + + // If the given directory is not local, it can still be the URL of an + // ioslave using UDS_LOCAL_PATH which to be converted first. + u = KIO::NetAccess::mostLocalUrl(u, widget()); + + //If the URL is local after the above conversion, set the directory. + if (u.isLocalFile()) { + dir = u.toLocalFile(); + } + + KToolInvocation::invokeTerminal(QString(), dir); +} + +void DolphinPart::slotFindFile() +{ + KRun::run("kfind", url(), widget()); +} + +void DolphinPart::updateNewMenu() +{ + // As requested by KNewFileMenu : + m_newFileMenu->checkUpToDate(); + m_newFileMenu->setViewShowsHiddenFiles(m_view->hiddenFilesShown()); + // And set the files that the menu apply on : + m_newFileMenu->setPopupFiles(url()); +} + +void DolphinPart::updateStatusBar() +{ + const QString escapedText = Qt::convertFromPlainText(m_view->statusBarText()); + emit ReadOnlyPart::setStatusBarText(QString("%1").arg(escapedText)); +} + +void DolphinPart::updateProgress(int percent) +{ + m_extension->loadingProgress(percent); +} + +void DolphinPart::createDirectory() +{ + m_newFileMenu->setViewShowsHiddenFiles(m_view->hiddenFilesShown()); + m_newFileMenu->setPopupFiles(url()); + m_newFileMenu->createDirectory(); +} + +void DolphinPart::setFilesToSelect(const KUrl::List& files) +{ + if (files.isEmpty()) { + return; + } + + m_view->markUrlsAsSelected(files); + m_view->markUrlAsCurrent(files.at(0)); +} + +bool DolphinPart::eventFilter(QObject* obj, QEvent* event) +{ + const int type = event->type(); + + if ((type == QEvent::KeyPress || type == QEvent::KeyRelease) && m_removeAction) { + QMenu* menu = qobject_cast(obj); + if (menu && menu->parent() == m_view) { + QKeyEvent* ev = static_cast(event); + if (ev->key() == Qt::Key_Shift) { + m_removeAction->update(); + } + } + } + + return KParts::ReadOnlyPart::eventFilter(obj, event); +} + +#include "moc_dolphinpart.cpp" diff --git a/dolphin/src/dolphinpart.desktop b/dolphin/src/dolphinpart.desktop new file mode 100644 index 00000000..29fd7235 --- /dev/null +++ b/dolphin/src/dolphinpart.desktop @@ -0,0 +1,341 @@ +[Desktop Entry] +Type=Service +Name=Dolphin View +Name[af]=Dolphin Deel +Name[ar]=عرض دولفين +Name[as]=Dolphin প্ৰদৰ্শন +Name[ast]=Vista de Dolphin +Name[be@latin]=Vyhlad „Dolphin” +Name[bg]=Преглед в Dolphin +Name[bn]=ডলফিন ভিউ +Name[bn_IN]=Dolphin প্রদর্শন +Name[bs]=Delfinov prikaz +Name[ca]=Vista del Dolphin +Name[ca@valencia]=Vista del Dolphin +Name[cs]=Pohled Dolphin +Name[csb]=Wëzdrzatk Dolphina +Name[da]=Dolphin-visning +Name[de]=Dolphin-Ansicht +Name[el]=Προβολή του Dolphin +Name[en_GB]=Dolphin View +Name[eo]=Dolphin-rigardo +Name[es]=Vista de Dolphin +Name[et]=Dolphini vaade +Name[eu]=Dolphin ikuspegia +Name[fi]=Dolphin-näkymä +Name[fr]=Vue de Dolphin +Name[fy]=Dolfyn werjefte +Name[ga]=Amharc Dolphin +Name[gl]=Vista de Dolphin +Name[gu]=ડોલ્ફિન દેખાવ +Name[he]=הגדרות תצוגה ב־Dolphin +Name[hi]=डॉल्फ़िन दृश्य +Name[hne]=डाल्फिन दृस्य +Name[hr]=Dolphinov prikaz +Name[hsb]=Napohlad w Dolphinje +Name[hu]=Dolphin-nézet +Name[ia]=Vista de Dolphin +Name[id]=Tampilan Dolphin +Name[is]=Dolphin sýn +Name[it]=Vista di Dolphin +Name[ja]=Dolphin ビュー +Name[kk]=Dolphin көрінісі +Name[km]=ទិដ្ឋភាព Dolphin +Name[kn]=ಡಾಲ್ಫಿನ್ ನೋಟ +Name[ko]=Dolphin 보기 +Name[ku]=Bergehê Dolphin +Name[lt]=Dolphin žiūryklė +Name[lv]=Dolphin skats +Name[mai]=डाल्फिन दृश्य +Name[mk]=Преглед со Делфин +Name[ml]=ഡോള്‍ഫിന്‍ അവതരണരീതി +Name[mr]=डॉल्फिन दृश्य +Name[ms]=Lihat Dolphin +Name[nb]=Dolphin visning +Name[nds]=Dolphin-Ansicht +Name[nl]=Dolphin-weergave +Name[nn]=Dolphin-vising +Name[or]=ଡଲଫିନ ଦୃଶ୍ୟ +Name[pa]=ਡਾਲਫਿਨ ਝਲਕ +Name[pl]=Widok Dolphina +Name[pt]=Área do Dolphin +Name[pt_BR]=Visualização do Dolphin +Name[ro]=Vizualizare Dolphin +Name[ru]=Представление Dolphin +Name[se]=Dolphinčájeheapmi +Name[si]=ඩොල්ෆින් දසුන +Name[sk]=Dolphin pohľad +Name[sl]=Dolphin - pogled +Name[sr]=Делфинов приказ +Name[sr@ijekavian]=Делфинов приказ +Name[sr@ijekavianlatin]=Dolphinov prikaz +Name[sr@latin]=Dolphinov prikaz +Name[sv]=Vy i Dolphin +Name[ta]=டால்பின் காட்சி +Name[te]=డాల్ఫిన్ వీక్షణం +Name[tg]=Намоиши Dolphin +Name[th]=มุมมองของดอลฟิน +Name[tr]=Dolphin Görünümü +Name[ug]=Dolphin كۆرۈنۈش +Name[uk]=Перегляд Dolphin +Name[wa]=Vuwe di Dolphin +Name[x-test]=xxDolphin Viewxx +Name[zh_CN]=Dolphin 视图 +Name[zh_TW]=Dolphin 檢視 +MimeType=inode/directory; +X-KDE-ServiceTypes=KParts/ReadOnlyPart +X-KDE-Library=dolphinpart +#X-KDE-BrowserView-Args=Icon +X-KDE-BrowserView-HideFromMenus=true +X-KDE-BrowserView-Built-Into=konqueror +Icon=view-icon +InitialPreference=7 + +# Provide info about the view modes using the Actions mechanism so that KService parses it. +# Konqueror then queries KService to get hold of the translated texts for the view modes +Actions=icons;details;compact; + +[Desktop Action icons] +Name=Icons +Name[af]=Ikone +Name[ar]=أيقونات +Name[as]=আইকন +Name[ast]=Iconos +Name[be]=Значкі +Name[be@latin]=Ikony +Name[bg]=Икони +Name[bn]=আইকন +Name[bn_IN]=আইকন +Name[br]=Arlunioù +Name[bs]=Ikone +Name[ca]=Icones +Name[ca@valencia]=Icones +Name[cs]=Ikony +Name[csb]=Ikònë +Name[cy]=Eicon +Name[da]=Ikoner +Name[de]=Symbole +Name[el]=Εικονίδια +Name[en_GB]=Icons +Name[eo]=Piktogramoj +Name[es]=Iconos +Name[et]=Ikoonid +Name[eu]=Ikonoak +Name[fa]=شمایلها +Name[fi]=Kuvakkeet +Name[fr]=Icônes +Name[fy]=Byldkaikes +Name[ga]=Deilbhíní +Name[gl]=Iconas +Name[gu]=ચિહ્નો +Name[he]=סמלים +Name[hi]=प्रतीक +Name[hne]=चिनहा +Name[hr]=Ikone +Name[hsb]=Piktogramy +Name[hu]=Ikonok +Name[ia]=Icones +Name[id]=Ikon +Name[is]=Táknmyndir +Name[it]=Icone +Name[ja]=アイコン +Name[ka]=ხატულები +Name[kk]=Таңбашалар +Name[km]=រូប​តំណាង +Name[kn]=ಚಿಹ್ನೆಗಳು +Name[ko]=아이콘 +Name[ku]=Îkon +Name[lt]=Ženkliukai +Name[lv]=Ikonas +Name[mai]=प्रतीक +Name[mk]=Икони +Name[ml]=ചിഹ്നങ്ങള്‍ +Name[mr]=चिन्ह +Name[ms]=Ikon +Name[nb]=Ikoner +Name[nds]=Lüttbiller +Name[ne]=प्रतिमा +Name[nl]=Pictogrammen +Name[nn]=Ikon +Name[oc]=Icònas +Name[or]=ଚିତ୍ର ସଂକେତଗୁଡ଼ିକ +Name[pa]=ਆਈਕਾਨ +Name[pl]=Ikony +Name[pt]=Ícones +Name[pt_BR]=Ícones +Name[ro]=Pictograme +Name[ru]=Значки +Name[se]=Govažat +Name[si]=අයිකන +Name[sk]=Ikony +Name[sl]=Ikone +Name[sr]=Иконе +Name[sr@ijekavian]=Иконе +Name[sr@ijekavianlatin]=Ikone +Name[sr@latin]=Ikone +Name[sv]=Ikoner +Name[ta]=சின்னங்கள் +Name[te]=ప్రతిమలు +Name[tg]=Нишонаҳо +Name[th]=ไอคอน +Name[tr]=Simgeler +Name[ug]=سىنبەلگىلەر +Name[uk]=Піктограми +Name[uz]=Nishonchalar +Name[uz@cyrillic]=Нишончалар +Name[vi]=Biểu tượng +Name[wa]=Imådjetes +Name[xh]=Imphawu zemmifanekiso +Name[x-test]=xxIconsxx +Name[zh_CN]=图标 +Name[zh_TW]=圖示 +Icon=view-list-icons +# Dummy +Exec=dolphin + +[Desktop Action compact] +Name=Compact +Name[ar]=مُتضامّ +Name[bg]=Компактно +Name[bs]=Sabij +Name[ca]=Compacte +Name[ca@valencia]=Compacte +Name[cs]=Kompaktní +Name[da]=Kompakt +Name[de]=Kompakt +Name[el]=Σύμπτυξη +Name[en_GB]=Compact +Name[es]=Compacta +Name[et]=Kompaktne +Name[fi]=Tiivis +Name[fr]=Concis +Name[ga]=Dlúth +Name[gl]=Compacto +Name[he]=מרוכז +Name[hu]=Kompakt +Name[ia]=Compacte +Name[id]=Kompak +Name[is]=Þjappað +Name[it]=Compatta +Name[kk]=Ықшамды +Name[km]=តូច​ល្មម +Name[ko]=축소됨 +Name[lt]=Kompaktiškas +Name[lv]=Kompakts +Name[mr]=संक्षिप्त +Name[nb]=Kompakt +Name[nds]=Drang +Name[nl]=Compact +Name[pa]=ਸੰਖੇਪ +Name[pl]=Kompaktowo +Name[pt]=Compacto +Name[pt_BR]=Compacto +Name[ro]=Compact +Name[ru]=Столбцы +Name[sk]=Kompaktný +Name[sl]=Strnjeno +Name[sr]=Сажето +Name[sr@ijekavian]=Сажето +Name[sr@ijekavianlatin]=Sažeto +Name[sr@latin]=Sažeto +Name[sv]=Kompakt +Name[tr]=Sıkışık +Name[ug]=ئىخچام +Name[uk]=Компактний +Name[wa]=Rastrindou +Name[x-test]=xxCompactxx +Name[zh_CN]=简洁视图 +Name[zh_TW]=簡潔模式 +Icon=view-list-details +# Dummy +Exec=dolphin + +[Desktop Action details] +Name=Details +Name[af]=Besonderhede +Name[ar]=تفاصيل +Name[as]=বিৱৰণ +Name[ast]=Detalles +Name[be@latin]=Detali +Name[bg]=Подробности +Name[bn]=বিস্তারিত +Name[bn_IN]=বিবরণ +Name[bs]=Detalji +Name[ca]=Detalls +Name[ca@valencia]=Detalls +Name[cs]=Podrobnosti +Name[csb]=Detale +Name[da]=Detaljer +Name[de]=Details +Name[el]=Λεπτομέρειες +Name[en_GB]=Details +Name[eo]=Detaloj +Name[es]=Detalles +Name[et]=Üksikasjad +Name[eu]=Xehetasunak +Name[fi]=Yksityiskohdat +Name[fr]=Détails +Name[fy]=Details +Name[ga]=Mionsonraí +Name[gl]=Detalles +Name[gu]=વિગતો +Name[he]=פרטים +Name[hi]=विवरण +Name[hne]=विवरन +Name[hr]=Detalji +Name[hsb]=Nadrobnosće +Name[hu]=Részletek +Name[ia]=Detalios +Name[id]=Detail +Name[is]=Smáatriði +Name[it]=Dettagli +Name[ja]=詳細 +Name[ka]=დეტალები +Name[kk]=Егжей-тегжейі +Name[km]=សេចក្ដី​លម្អិត +Name[kn]=ವಿವರಗಳು +Name[ko]=자세히 +Name[ku]=Kitekit +Name[lt]=Detalės +Name[lv]=Detaļas +Name[mai]=विवरण +Name[mk]=Детали +Name[ml]=വിശദവിവരങ്ങള്‍ +Name[mr]=तपशील +Name[ms]=Perincian +Name[nb]=Detaljer +Name[nds]=Enkelheiten +Name[nl]=Details +Name[nn]=Detaljar +Name[oc]=Detalhs +Name[or]=ବିସ୍ତୃତ ବିବରଣୀ +Name[pa]=ਵੇਰਵਾ +Name[pl]=Szczegóły +Name[pt]=Detalhes +Name[pt_BR]=Detalhes +Name[ro]=Detalii +Name[ru]=Таблица +Name[si]=විස්තර +Name[sk]=Podrobnosti +Name[sl]=Podrobnosti +Name[sr]=Детаљи +Name[sr@ijekavian]=Детаљи +Name[sr@ijekavianlatin]=Detalji +Name[sr@latin]=Detalji +Name[sv]=Detaljinformation +Name[ta]=விவரங்கள் +Name[te]=వివరాలు +Name[tg]=Тафсилотҳо +Name[th]=รายละเอียด +Name[tr]=Ayrıntılar +Name[ug]=تەپسىلاتلار +Name[uk]=Подробиці +Name[uz]=Tafsilotlar +Name[uz@cyrillic]=Тафсилотлар +Name[wa]=Detays +Name[x-test]=xxDetailsxx +Name[zh_CN]=细节 +Name[zh_TW]=詳細模式 +Icon=view-list-tree +# Dummy +Exec=dolphin diff --git a/dolphin/src/dolphinpart.h b/dolphin/src/dolphinpart.h new file mode 100644 index 00000000..c5a9fa0a --- /dev/null +++ b/dolphin/src/dolphinpart.h @@ -0,0 +1,253 @@ +/* This file is part of the KDE project + Copyright (c) 2007 David Faure + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef DOLPHINPART_H +#define DOLPHINPART_H + +#include + +#include + +class DolphinNewFileMenu; +class DolphinViewActionHandler; +#include +class KAction; +class KFileItemList; +class KFileItem; +class DolphinPartBrowserExtension; +class DolphinSortFilterProxyModel; +class DolphinRemoteEncoding; +class DolphinModel; +class KDirLister; +class DolphinView; +class KAboutData; +class DolphinRemoveAction; + +class DolphinPart : public KParts::ReadOnlyPart +{ + Q_OBJECT + // Used by konqueror. Technically it means "we want undo enabled if + // there are things in the undo history and the current part is a dolphin part". + // Even though it's konqueror doing the undo... + Q_PROPERTY( bool supportsUndo READ supportsUndo ) + + Q_PROPERTY( QString currentViewMode READ currentViewMode WRITE setCurrentViewMode ) + + // Used by konqueror when typing something like /home/dfaure/*.diff in the location bar + Q_PROPERTY( QString nameFilter READ nameFilter WRITE setNameFilter ) + + // Used by konqueror to implement the --select command-line option + Q_PROPERTY( KUrl::List filesToSelect READ filesToSelect WRITE setFilesToSelect ) + +public: + explicit DolphinPart(QWidget* parentWidget, QObject* parent, const QVariantList& args); + ~DolphinPart(); + + static KAboutData* createAboutData(); + + /** + * Standard KParts::ReadOnlyPart openUrl method. + * Called by Konqueror to view a directory in DolphinPart. + */ + virtual bool openUrl(const KUrl& url); + + /// see the supportsUndo property + bool supportsUndo() const { return true; } + + /** + * Used by konqueror for setting the view mode + * @param viewModeName internal name for the view mode, like "icons" + * Those names come from the Actions line in dolphinpart.desktop, + * and have to match the name of the KActions. + */ + void setCurrentViewMode(const QString& viewModeName); + + /** + * Used by konqueror for displaying the current view mode. + * @see setCurrentViewMode + */ + QString currentViewMode() const; + + /// Returns the view owned by this part; used by DolphinPartBrowserExtension + DolphinView* view() { return m_view; } + + /** + * Sets a name filter, like *.diff + */ + void setNameFilter(const QString& nameFilter); + + /** + * Returns the current name filter. Used by konqueror to show it in the URL. + */ + QString nameFilter() const { return m_nameFilter; } + +protected: + /** + * We reimplement openUrl so no need to implement openFile. + */ + virtual bool openFile() { return true; } + +Q_SIGNALS: + /** + * Emitted when the view mode changes. Used by konqueror. + */ + void viewModeChanged(); + + + /** + * Emitted whenever the current URL is about to be changed. + */ + void aboutToOpenURL(); + +private Q_SLOTS: + void slotMessage(const QString& msg); + void slotErrorMessage(const QString& msg); + /** + * Shows the information for the item \a item inside the statusbar. If the + * item is null, the default statusbar information is shown. + */ + void slotRequestItemInfo(const KFileItem& item); + /** + * Handles clicking on an item + */ + void slotItemActivated(const KFileItem& item); + /** + * Handles activation of multiple items + */ + void slotItemsActivated(const KFileItemList& items); + /** + * Creates a new window showing the content of \a url. + */ + void createNewWindow(const KUrl& url); + /** + * Opens the context menu on the current mouse position. + * @pos Position in screen coordinates. + * @item File item context. If item is null, the context menu + * should be applied to \a url. + * @url URL which contains \a item. + * @customActions Actions that should be added to the context menu, + * if the file item is null. + */ + void slotOpenContextMenu(const QPoint& pos, + const KFileItem& item, + const KUrl& url, + const QList& customActions); + + /** + * Informs the host that we are opening \a url (e.g. after a redirection + * coming from KDirLister). + * Testcase 1: fish://localhost + * Testcase 2: showing a directory that is being renamed by another window (#180156) + */ + void slotDirectoryRedirection(const KUrl& oldUrl, const KUrl& newUrl); + + /** + * Updates the state of the 'Edit' menu actions and emits + * the signal selectionChanged(). + */ + void slotSelectionChanged(const KFileItemList& selection); + + /** + * Updates the text of the paste action dependent from + * the number of items which are in the clipboard. + */ + void updatePasteAction(); + + /** + * Connected to all "Go" menu actions provided by DolphinPart + */ + void slotGoTriggered(QAction* action); + + /** + * Connected to the "editMimeType" action + */ + void slotEditMimeType(); + + /** + * Connected to the "select_items_matching" action. + * Opens a dialog which permits to select all items matching a pattern like "*.jpg". + */ + void slotSelectItemsMatchingPattern(); + + /** + * Connected to the "unselect_items_matching" action. + * Opens a dialog which permits to unselect all items matching a pattern like "*.jpg". + */ + void slotUnselectItemsMatchingPattern(); + + /** + * Open a terminal window, starting with the current directory. + */ + void slotOpenTerminal(); + + /** + * Open KFind with the current path. + */ + void slotFindFile(); + + /** + * Updates the 'Create New...' sub menu, just before it's shown. + */ + void updateNewMenu(); + + /** + * Updates the number of items (= number of files + number of + * directories) in the statusbar. If files are selected, the number + * of selected files and the sum of the filesize is shown. + */ + void updateStatusBar(); + + /** + * Notify container of folder loading progress. + */ + void updateProgress(int percent); + + void createDirectory(); + + /** + * Called by konqueror --select + */ + void setFilesToSelect(const KUrl::List& files); + KUrl::List filesToSelect() const { return KUrl::List(); } // silence moc + + virtual bool eventFilter(QObject*, QEvent*); + +private: + void createActions(); + void createGoAction(const char* name, const char* iconName, + const QString& text, const QString& url, + QActionGroup* actionGroup); + + void openSelectionDialog(const QString& title, const QString& text, + bool selectItems); + +private: + DolphinView* m_view; + DolphinViewActionHandler* m_actionHandler; + DolphinRemoteEncoding* m_remoteEncoding; + DolphinPartBrowserExtension* m_extension; + DolphinNewFileMenu* m_newFileMenu; + KAction* m_findFileAction; + KAction* m_openTerminalAction; + QString m_nameFilter; + DolphinRemoveAction* m_removeAction; + Q_DISABLE_COPY(DolphinPart) +}; + +#endif /* DOLPHINPART_H */ diff --git a/dolphin/src/dolphinpart.rc b/dolphin/src/dolphinpart.rc new file mode 100644 index 00000000..d24c7405 --- /dev/null +++ b/dolphin/src/dolphinpart.rc @@ -0,0 +1,65 @@ + + + + &Edit + + + + + + + + + + Selection + + + + + + + + + &View + + + + + + + + + &Go + + + + + + + + Tools + + + + + + +Dolphin Toolbar + + + + + + + + + + + + + + + + + + diff --git a/dolphin/src/dolphinpart_ext.cpp b/dolphin/src/dolphinpart_ext.cpp new file mode 100644 index 00000000..dd2a21dc --- /dev/null +++ b/dolphin/src/dolphinpart_ext.cpp @@ -0,0 +1,193 @@ +/* This file is part of the KDE project + * Copyright (c) 2012 Dawit Alemayehu + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "dolphinpart_ext.h" + +#include "dolphinpart.h" +#include "views/dolphinview.h" + +#include + +#include + + +DolphinPartBrowserExtension::DolphinPartBrowserExtension(DolphinPart* part) + :KParts::BrowserExtension( part ) + ,m_part(part) +{ + +} + +void DolphinPartBrowserExtension::restoreState(QDataStream &stream) +{ + KParts::BrowserExtension::restoreState(stream); + m_part->view()->restoreState(stream); +} + +void DolphinPartBrowserExtension::saveState(QDataStream &stream) +{ + KParts::BrowserExtension::saveState(stream); + m_part->view()->saveState(stream); +} + +void DolphinPartBrowserExtension::cut() +{ + m_part->view()->cutSelectedItems(); +} + +void DolphinPartBrowserExtension::copy() +{ + m_part->view()->copySelectedItems(); +} + +void DolphinPartBrowserExtension::paste() +{ + m_part->view()->paste(); +} + +void DolphinPartBrowserExtension::pasteTo(const KUrl&) +{ + m_part->view()->pasteIntoFolder(); +} + +void DolphinPartBrowserExtension::reparseConfiguration() +{ + m_part->view()->readSettings(); +} + + +DolphinPartFileInfoExtension::DolphinPartFileInfoExtension(DolphinPart* part) + :KParts::FileInfoExtension(part) + ,m_part(part) +{ +} + +bool DolphinPartFileInfoExtension::hasSelection() const +{ + return m_part->view()->selectedItemsCount() > 0; +} + +KParts::FileInfoExtension::QueryModes DolphinPartFileInfoExtension::supportedQueryModes() const +{ + return (KParts::FileInfoExtension::AllItems | KParts::FileInfoExtension::SelectedItems); +} + +KFileItemList DolphinPartFileInfoExtension::queryFor(KParts::FileInfoExtension::QueryMode mode) const +{ + KFileItemList list; + + if (mode == KParts::FileInfoExtension::None) + return list; + + if (!(supportedQueryModes() & mode)) + return list; + + switch (mode) { + case KParts::FileInfoExtension::SelectedItems: + if (hasSelection()) + return m_part->view()->selectedItems(); + break; + case KParts::FileInfoExtension::AllItems: + return m_part->view()->items(); + default: + break; + } + + return list; +} + +DolphinPartListingFilterExtension::DolphinPartListingFilterExtension(DolphinPart* part) + : KParts::ListingFilterExtension(part) + , m_part(part) +{ +} + +KParts::ListingFilterExtension::FilterModes DolphinPartListingFilterExtension::supportedFilterModes() const +{ + return (KParts::ListingFilterExtension::MimeType | + KParts::ListingFilterExtension::SubString | + KParts::ListingFilterExtension::WildCard); +} + +bool DolphinPartListingFilterExtension::supportsMultipleFilters(KParts::ListingFilterExtension::FilterMode mode) const +{ + if (mode == KParts::ListingFilterExtension::MimeType) + return true; + + return false; +} + +QVariant DolphinPartListingFilterExtension::filter(KParts::ListingFilterExtension::FilterMode mode) const +{ + QVariant result; + + switch (mode) { + case KParts::ListingFilterExtension::MimeType: + result = m_part->view()->mimeTypeFilters(); + break; + case KParts::ListingFilterExtension::SubString: + case KParts::ListingFilterExtension::WildCard: + result = m_part->view()->nameFilter(); + break; + default: + break; + } + + return result; +} + +void DolphinPartListingFilterExtension::setFilter(KParts::ListingFilterExtension::FilterMode mode, const QVariant& filter) +{ + switch (mode) { + case KParts::ListingFilterExtension::MimeType: + m_part->view()->setMimeTypeFilters(filter.toStringList()); + break; + case KParts::ListingFilterExtension::SubString: + case KParts::ListingFilterExtension::WildCard: + m_part->view()->setNameFilter(filter.toString()); + break; + default: + break; + } +} + +//// + +DolphinPartListingNotificationExtension::DolphinPartListingNotificationExtension(DolphinPart* part) + : KParts::ListingNotificationExtension(part) +{ +} + +KParts::ListingNotificationExtension::NotificationEventTypes DolphinPartListingNotificationExtension::supportedNotificationEventTypes() const +{ + return (KParts::ListingNotificationExtension::ItemsAdded | + KParts::ListingNotificationExtension::ItemsDeleted); +} + +void DolphinPartListingNotificationExtension::slotNewItems(const KFileItemList& items) +{ + emit listingEvent(KParts::ListingNotificationExtension::ItemsAdded, items); +} + +void DolphinPartListingNotificationExtension::slotItemsDeleted(const KFileItemList& items) +{ + emit listingEvent(KParts::ListingNotificationExtension::ItemsDeleted, items); +} + +#include "moc_dolphinpart_ext.cpp" diff --git a/dolphin/src/dolphinpart_ext.h b/dolphin/src/dolphinpart_ext.h new file mode 100644 index 00000000..c05962cd --- /dev/null +++ b/dolphin/src/dolphinpart_ext.h @@ -0,0 +1,92 @@ +/* This file is part of the KDE project + * Copyright (c) 2012 Dawit Alemayehu + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef DOLPHINPART_EXT_H +#define DOLPHINPART_EXT_H + +#include +#include +#include + +class DolphinPart; + +class DolphinPartBrowserExtension : public KParts::BrowserExtension +{ + Q_OBJECT +public: + DolphinPartBrowserExtension( DolphinPart* part ); + virtual void restoreState(QDataStream &stream); + virtual void saveState(QDataStream &stream); + +public Q_SLOTS: + void cut(); + void copy(); + void paste(); + void pasteTo(const KUrl&); + void reparseConfiguration(); + +private: + DolphinPart* m_part; +}; + +class DolphinPartFileInfoExtension : public KParts::FileInfoExtension +{ + Q_OBJECT + +public: + DolphinPartFileInfoExtension(DolphinPart* part); + + virtual QueryModes supportedQueryModes() const; + virtual bool hasSelection() const; + + virtual KFileItemList queryFor(QueryMode mode) const; + +private: + DolphinPart* m_part; +}; + +class DolphinPartListingFilterExtension : public KParts::ListingFilterExtension +{ + Q_OBJECT + +public: + DolphinPartListingFilterExtension(DolphinPart* part); + virtual FilterModes supportedFilterModes() const; + virtual bool supportsMultipleFilters(FilterMode mode) const; + virtual QVariant filter(FilterMode mode) const; + virtual void setFilter(FilterMode mode, const QVariant& filter); + +private: + DolphinPart* m_part; +}; + +class DolphinPartListingNotificationExtension : public KParts::ListingNotificationExtension +{ + Q_OBJECT + +public: + DolphinPartListingNotificationExtension(DolphinPart* part); + virtual NotificationEventTypes supportedNotificationEventTypes() const; + +public Q_SLOTS: + void slotNewItems(const KFileItemList&); + void slotItemsDeleted(const KFileItemList&); +}; + +#endif diff --git a/dolphin/src/dolphinrecenttabsmenu.cpp b/dolphin/src/dolphinrecenttabsmenu.cpp new file mode 100644 index 00000000..d6c49701 --- /dev/null +++ b/dolphin/src/dolphinrecenttabsmenu.cpp @@ -0,0 +1,97 @@ +/*************************************************************************** + * Copyright (C) 2014 by Emmanuel Pescosta * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "dolphinrecenttabsmenu.h" + +#include +#include +#include +#include +#include + +DolphinRecentTabsMenu::DolphinRecentTabsMenu(QObject* parent) : + KActionMenu(KIcon("edit-undo"), i18n("Recently Closed Tabs"), parent) +{ + setDelayed(false); + setEnabled(false); + + m_clearListAction = new QAction(i18n("Empty Recently Closed Tabs"), this); + m_clearListAction->setIcon(KIcon("edit-clear-list")); + addAction(m_clearListAction); + + addSeparator(); + + connect(menu(), SIGNAL(triggered(QAction*)), + this, SLOT(handleAction(QAction*))); +} + +void DolphinRecentTabsMenu::rememberClosedTab(const KUrl& primaryUrl, const KUrl& secondaryUrl) +{ + QAction* action = new QAction(menu()); + action->setText(primaryUrl.path()); + + const QString iconName = KMimeType::iconNameForUrl(primaryUrl); + action->setIcon(KIcon(iconName)); + + KUrl::List urls; + urls << primaryUrl; + urls << secondaryUrl; + action->setData(QVariant::fromValue(urls)); + + // Add the closed tab menu entry after the separator and + // "Empty Recently Closed Tabs" entry + if (menu()->actions().size() == 2) { + addAction(action); + } else { + insertAction(menu()->actions().at(2), action); + } + + // Assure that only up to 6 closed tabs are shown in the menu. + // 8 because of clear action + separator + 6 closed tabs + if (menu()->actions().size() > 8) { + removeAction(menu()->actions().last()); + } + setEnabled(true); + KAcceleratorManager::manage(menu()); +} + +void DolphinRecentTabsMenu::handleAction(QAction* action) +{ + if (action == m_clearListAction) { + // Clear all actions except the "Empty Recently Closed Tabs" + // action and the separator + QList actions = menu()->actions(); + const int count = actions.size(); + for (int i = 2; i < count; ++i) { + removeAction(actions.at(i)); + } + } else { + const KUrl::List urls = action->data().value(); + if (urls.count() == 2) { + emit restoreClosedTab(urls.first(), urls.last()); + } + removeAction(action); + delete action; + action = 0; + } + + if (menu()->actions().count() <= 2) { + setEnabled(false); + } +} diff --git a/dolphin/src/dolphinrecenttabsmenu.h b/dolphin/src/dolphinrecenttabsmenu.h new file mode 100644 index 00000000..ee11a3a1 --- /dev/null +++ b/dolphin/src/dolphinrecenttabsmenu.h @@ -0,0 +1,49 @@ +/*************************************************************************** + * Copyright (C) 2014 by Emmanuel Pescosta * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef DOLPHIN_RECENT_TABS_MENU_H +#define DOLPHIN_RECENT_TABS_MENU_H + +#include +#include + +class DolphinTabPage; +#include + +class DolphinRecentTabsMenu : public KActionMenu +{ + Q_OBJECT + +public: + explicit DolphinRecentTabsMenu(QObject* parent); + +public slots: + void rememberClosedTab(const KUrl& primaryUrl, const KUrl& secondaryUrl); + +signals: + void restoreClosedTab(const KUrl& primaryUrl, const KUrl& secondaryUrl); + +private slots: + void handleAction(QAction* action); + +private: + QAction* m_clearListAction; +}; + +#endif diff --git a/dolphin/src/dolphinremoveaction.cpp b/dolphin/src/dolphinremoveaction.cpp new file mode 100644 index 00000000..7d7c2f04 --- /dev/null +++ b/dolphin/src/dolphinremoveaction.cpp @@ -0,0 +1,61 @@ +/*************************************************************************** + * Copyright (C) 2013 by Dawit Alemayehu + +#include + + +DolphinRemoveAction::DolphinRemoveAction(QObject* parent, KActionCollection* collection) : + QAction(parent), + m_collection(collection) +{ + update(); + connect(this, SIGNAL(triggered()), this, SLOT(slotRemoveActionTriggered())); +} + +void DolphinRemoveAction::slotRemoveActionTriggered() +{ + if (m_action) { + m_action->trigger(); + } +} + +void DolphinRemoveAction::update() +{ + Q_ASSERT(m_collection); + // Using setText(action->text()) does not apply the &-shortcut. + // This is only done until the original action has been shown at least once. To + // bypass this issue, the text and &-shortcut is applied manually. + if (qApp->keyboardModifiers() & Qt::ShiftModifier) { + m_action = m_collection ? m_collection->action("delete") : 0; + setText(i18nc("@action:inmenu", "&Delete")); + } else { + m_action = m_collection ? m_collection->action("move_to_trash") : 0; + setText(i18nc("@action:inmenu", "&Move to Trash")); + } + + if (m_action) { + setIcon(m_action->icon()); + setShortcuts(m_action->shortcuts()); + setEnabled(m_action->isEnabled()); + } +} diff --git a/dolphin/src/dolphinremoveaction.h b/dolphin/src/dolphinremoveaction.h new file mode 100644 index 00000000..a6e3c058 --- /dev/null +++ b/dolphin/src/dolphinremoveaction.h @@ -0,0 +1,55 @@ +/*************************************************************************** + * Copyright (C) 2013 by Dawit Alemayehu +#include + +#include + +/** + * A QAction that manages the delete based on the current state of + * the Shift key or the parameter passed to update. + * + * This class expects the presence of both the "move_to_trash" and "delete" + * actions in @ref collection. + */ +class DOLPHINPRIVATE_EXPORT DolphinRemoveAction : public QAction +{ + Q_OBJECT +public: + DolphinRemoveAction(QObject* parent, KActionCollection* collection); + /** + * Updates this action key based on the state of the Shift key. + */ + void update(); + +private Q_SLOTS: + void slotRemoveActionTriggered(); + +private: + QPointer m_collection; + QPointer m_action; +}; + +#endif diff --git a/dolphin/src/dolphintabpage.cpp b/dolphin/src/dolphintabpage.cpp new file mode 100644 index 00000000..4c49869f7 --- /dev/null +++ b/dolphin/src/dolphintabpage.cpp @@ -0,0 +1,266 @@ +/*************************************************************************** + * Copyright (C) 2014 by Emmanuel Pescosta * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "dolphintabpage.h" + +#include "dolphinviewcontainer.h" +#include "dolphin_generalsettings.h" + +#include + +DolphinTabPage::DolphinTabPage(const KUrl& primaryUrl, const KUrl& secondaryUrl, QWidget* parent) : + QWidget(parent), + m_primaryViewActive(true), + m_splitViewEnabled(false) +{ + QVBoxLayout* layout = new QVBoxLayout(this); + layout->setSpacing(0); + layout->setMargin(0); + + m_splitter = new QSplitter(Qt::Horizontal, this); + m_splitter->setChildrenCollapsible(false); + layout->addWidget(m_splitter); + + // Create a new primary view + m_primaryViewContainer = createViewContainer(primaryUrl); + connect(m_primaryViewContainer->view(), SIGNAL(urlChanged(KUrl)), + this, SIGNAL(activeViewUrlChanged(KUrl))); + + m_splitter->addWidget(m_primaryViewContainer); + m_primaryViewContainer->show(); + + if (secondaryUrl.isValid() || GeneralSettings::splitView()) { + // Provide a secondary view, if the given secondary url is valid or if the + // startup settings are set this way (use the url of the primary view). + m_splitViewEnabled = true; + const KUrl& url = secondaryUrl.isValid() ? secondaryUrl : primaryUrl; + m_secondaryViewContainer = createViewContainer(url); + m_splitter->addWidget(m_secondaryViewContainer); + m_secondaryViewContainer->show(); + } + + m_primaryViewContainer->setActive(true); +} + +bool DolphinTabPage::primaryViewActive() const +{ + return m_primaryViewActive; +} + +bool DolphinTabPage::splitViewEnabled() const +{ + return m_splitViewEnabled; +} + +void DolphinTabPage::setSplitViewEnabled(bool enabled) +{ + if (m_splitViewEnabled != enabled) { + m_splitViewEnabled = enabled; + + if (enabled) { + const KUrl& url = m_primaryViewContainer->url(); + m_secondaryViewContainer = createViewContainer(url); + + const bool placesSelectorVisible = m_primaryViewContainer->urlNavigator()->isPlacesSelectorVisible(); + m_secondaryViewContainer->urlNavigator()->setPlacesSelectorVisible(placesSelectorVisible); + + m_splitter->addWidget(m_secondaryViewContainer); + m_secondaryViewContainer->show(); + m_secondaryViewContainer->setActive(true); + } else { + // Close the view which is active. + DolphinViewContainer* view = activeViewContainer(); + if (m_primaryViewActive) { + // If the primary view is active, we have to swap the pointers + // because the secondary view will be the new primary view. + qSwap(m_primaryViewContainer, m_secondaryViewContainer); + } + m_primaryViewContainer->setActive(true); + view->close(); + view->deleteLater(); + } + } +} + +DolphinViewContainer* DolphinTabPage::primaryViewContainer() const +{ + return m_primaryViewContainer; +} + +DolphinViewContainer* DolphinTabPage::secondaryViewContainer() const +{ + return m_secondaryViewContainer; +} + +DolphinViewContainer* DolphinTabPage::activeViewContainer() const +{ + return m_primaryViewActive ? m_primaryViewContainer : + m_secondaryViewContainer; +} + +KFileItemList DolphinTabPage::selectedItems() const +{ + KFileItemList items = m_primaryViewContainer->view()->selectedItems(); + if (m_splitViewEnabled) { + items += m_secondaryViewContainer->view()->selectedItems(); + } + return items; +} + +int DolphinTabPage::selectedItemsCount() const +{ + int selectedItemsCount = m_primaryViewContainer->view()->selectedItemsCount(); + if (m_splitViewEnabled) { + selectedItemsCount += m_secondaryViewContainer->view()->selectedItemsCount(); + } + return selectedItemsCount; +} + +void DolphinTabPage::markUrlsAsSelected(const QList& urls) +{ + m_primaryViewContainer->view()->markUrlsAsSelected(urls); + if (m_splitViewEnabled) { + m_secondaryViewContainer->view()->markUrlsAsSelected(urls); + } +} + +void DolphinTabPage::markUrlAsCurrent(const KUrl& url) +{ + m_primaryViewContainer->view()->markUrlAsCurrent(url); + if (m_splitViewEnabled) { + m_secondaryViewContainer->view()->markUrlAsCurrent(url); + } +} + +void DolphinTabPage::setPlacesSelectorVisible(bool visible) +{ + m_primaryViewContainer->urlNavigator()->setPlacesSelectorVisible(visible); + if (m_splitViewEnabled) { + m_secondaryViewContainer->urlNavigator()->setPlacesSelectorVisible(visible); + } +} + +void DolphinTabPage::refreshViews() +{ + m_primaryViewContainer->readSettings(); + if (m_splitViewEnabled) { + m_secondaryViewContainer->readSettings(); + } +} + +QByteArray DolphinTabPage::saveState() const +{ + QByteArray state; + QDataStream stream(&state, QIODevice::WriteOnly); + + stream << m_splitViewEnabled; + + stream << m_primaryViewContainer->url(); + stream << m_primaryViewContainer->urlNavigator()->isUrlEditable(); + + if (m_splitViewEnabled) { + stream << m_secondaryViewContainer->url(); + stream << m_secondaryViewContainer->urlNavigator()->isUrlEditable(); + } + + stream << m_primaryViewActive; + stream << m_splitter->saveState(); + + return state; +} + +void DolphinTabPage::restoreState(const QByteArray& state) +{ + if (state.isEmpty()) { + return; + } + + QByteArray sd = state; + QDataStream stream(&sd, QIODevice::ReadOnly); + + bool isSplitViewEnabled = false; + stream >> isSplitViewEnabled; + setSplitViewEnabled(isSplitViewEnabled); + + KUrl primaryUrl; + stream >> primaryUrl; + m_primaryViewContainer->setUrl(primaryUrl); + bool primaryUrlEditable; + stream >> primaryUrlEditable; + m_primaryViewContainer->urlNavigator()->setUrlEditable(primaryUrlEditable); + + if (isSplitViewEnabled) { + KUrl secondaryUrl; + stream >> secondaryUrl; + m_secondaryViewContainer->setUrl(secondaryUrl); + bool secondaryUrlEditable; + stream >> secondaryUrlEditable; + m_secondaryViewContainer->urlNavigator()->setUrlEditable(secondaryUrlEditable); + } + + stream >> m_primaryViewActive; + if (m_primaryViewActive) { + m_primaryViewContainer->setActive(true); + } else { + Q_ASSERT(m_splitViewEnabled); + m_secondaryViewContainer->setActive(true); + } + + QByteArray splitterState; + stream >> splitterState; + m_splitter->restoreState(splitterState); +} + +void DolphinTabPage::slotViewActivated() +{ + const DolphinView* oldActiveView = activeViewContainer()->view(); + + // Set the view, which was active before, to inactive + // and update the active view type. + if (m_splitViewEnabled) { + activeViewContainer()->setActive(false); + m_primaryViewActive = !m_primaryViewActive; + } else { + m_primaryViewActive = true; + } + + const DolphinView* newActiveView = activeViewContainer()->view(); + + if (newActiveView != oldActiveView) { + disconnect(oldActiveView, SIGNAL(urlChanged(KUrl)), + this, SIGNAL(activeViewUrlChanged(KUrl))); + connect(newActiveView, SIGNAL(urlChanged(KUrl)), + this, SIGNAL(activeViewUrlChanged(KUrl))); + } + + emit activeViewUrlChanged(activeViewContainer()->url()); + emit activeViewChanged(); +} + +DolphinViewContainer* DolphinTabPage::createViewContainer(const KUrl& url) const +{ + DolphinViewContainer* container = new DolphinViewContainer(url, m_splitter); + container->setActive(false); + + const DolphinView* view = container->view(); + connect(view, SIGNAL(activated()), + this, SLOT(slotViewActivated())); + + return container; +} diff --git a/dolphin/src/dolphintabpage.h b/dolphin/src/dolphintabpage.h new file mode 100644 index 00000000..a65f8914 --- /dev/null +++ b/dolphin/src/dolphintabpage.h @@ -0,0 +1,152 @@ +/*************************************************************************** + * Copyright (C) 2014 by Emmanuel Pescosta * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef DOLPHIN_TAB_PAGE_H +#define DOLPHIN_TAB_PAGE_H + +#include +#include +#include + +#include +class DolphinViewContainer; +class KFileItemList; + +class DolphinTabPage : public QWidget +{ + Q_OBJECT + +public: + explicit DolphinTabPage(const KUrl& primaryUrl, const KUrl& secondaryUrl = KUrl(), QWidget* parent = 0); + + /** + * @return True if primary view is the active view in this tab. + */ + bool primaryViewActive() const; + + /** + * @return True if split view is enabled. + */ + bool splitViewEnabled() const; + + /** + * Enables or disables the split view mode. + * + * If \a enabled is true, it creates a secondary view with the url of the primary view. + */ + void setSplitViewEnabled(bool enabled); + + /** + * @return The primary view containter. + */ + DolphinViewContainer* primaryViewContainer() const; + + /** + * @return The secondary view containter, can be 0 if split view is disabled. + */ + DolphinViewContainer* secondaryViewContainer() const; + + /** + * @return DolphinViewContainer of the active view + */ + DolphinViewContainer* activeViewContainer() const; + + /** + * Returns the selected items. The list is empty if no item has been + * selected. + */ + KFileItemList selectedItems() const; + + /** + * Returns the number of selected items (this is faster than + * invoking selectedItems().count()). + */ + int selectedItemsCount() const; + + /** + * Marks the items indicated by \p urls to get selected after the + * directory DolphinView::url() has been loaded. Note that nothing + * gets selected if no loading of a directory has been triggered + * by DolphinView::setUrl() or DolphinView::reload(). + */ + void markUrlsAsSelected(const QList& urls); + + /** + * Marks the item indicated by \p url to be scrolled to and as the + * current item after directory DolphinView::url() has been loaded. + */ + void markUrlAsCurrent(const KUrl& url); + + /** + * Sets the places selector visible, if \a visible is true. + * The places selector allows to select the places provided + * by the places model passed in the constructor. Per default + * the places selector is visible. + */ + void setPlacesSelectorVisible(bool visible); + + /** + * Refreshes the views of the main window by recreating them according to + * the given Dolphin settings. + */ + void refreshViews(); + + /** + * Saves all tab related properties (urls, splitter layout, ...). + * + * @return A byte-array which contains all properties. + */ + QByteArray saveState() const; + + /** + * Restores all tab related properties (urls, splitter layout, ...) from + * the given \a state. + */ + void restoreState(const QByteArray& state); + +signals: + void activeViewChanged(); + void activeViewUrlChanged(const KUrl& url); + +private slots: + /** + * Handles the view activated event. + * + * It sets the previous active view to inactive, updates the current + * active view type and triggers the activeViewChanged event. + */ + void slotViewActivated(); + +private: + /** + * Creates a new view container and does the default initialization. + */ + DolphinViewContainer* createViewContainer(const KUrl& url) const; + +private: + QSplitter* m_splitter; + + QPointer m_primaryViewContainer; + QPointer m_secondaryViewContainer; + + bool m_primaryViewActive; + bool m_splitViewEnabled; +}; + +#endif // DOLPHIN_TAB_PAGE_H diff --git a/dolphin/src/dolphinui.rc b/dolphin/src/dolphinui.rc new file mode 100644 index 00000000..23b98e60 --- /dev/null +++ b/dolphin/src/dolphinui.rc @@ -0,0 +1,114 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Location Bar + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Main Toolbar + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dolphin/src/dolphinviewcontainer.cpp b/dolphin/src/dolphinviewcontainer.cpp new file mode 100644 index 00000000..db73cb69 --- /dev/null +++ b/dolphin/src/dolphinviewcontainer.cpp @@ -0,0 +1,672 @@ +/*************************************************************************** + * Copyright (C) 2007 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "dolphinviewcontainer.h" +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include "dolphin_generalsettings.h" +#include "filterbar/filterbar.h" +#include "search/dolphinsearchbox.h" +#include "statusbar/dolphinstatusbar.h" +#include "views/draganddrophelper.h" +#include "views/viewmodecontroller.h" +#include "views/viewproperties.h" + +DolphinViewContainer::DolphinViewContainer(const KUrl& url, QWidget* parent) : + QWidget(parent), + m_topLayout(0), + m_urlNavigator(0), + m_searchBox(0), + m_messageWidget(0), + m_view(0), + m_filterBar(0), + m_statusBar(0), + m_statusBarTimer(0), + m_statusBarTimestamp(), + m_autoGrabFocus(true), + m_dropDestination(), + m_dropEvent(0) +{ + hide(); + + m_topLayout = new QVBoxLayout(this); + m_topLayout->setSpacing(0); + m_topLayout->setMargin(0); + + m_urlNavigator = new KUrlNavigator(new KFilePlacesModel(this), url, this); + connect(m_urlNavigator, SIGNAL(urlsDropped(KUrl,QDropEvent*)), + this, SLOT(dropUrls(KUrl,QDropEvent*))); + connect(m_urlNavigator, SIGNAL(activated()), + this, SLOT(activate())); + connect(m_urlNavigator->editor(), SIGNAL(completionModeChanged(KGlobalSettings::Completion)), + this, SLOT(saveUrlCompletionMode(KGlobalSettings::Completion))); + + const GeneralSettings* settings = GeneralSettings::self(); + m_urlNavigator->setUrlEditable(settings->editableUrl()); + m_urlNavigator->setShowFullPath(settings->showFullPath()); + m_urlNavigator->setHomeUrl(KUrl(settings->homeUrl())); + KUrlComboBox* editor = m_urlNavigator->editor(); + editor->setCompletionMode(KGlobalSettings::Completion(settings->urlCompletionMode())); + + m_searchBox = new DolphinSearchBox(this); + m_searchBox->hide(); + connect(m_searchBox, SIGNAL(activated()), this, SLOT(activate())); + connect(m_searchBox, SIGNAL(closeRequest()), this, SLOT(closeSearchBox())); + connect(m_searchBox, SIGNAL(searchRequest()), this, SLOT(startSearching())); + connect(m_searchBox, SIGNAL(returnPressed(QString)), this, SLOT(requestFocus())); + + m_messageWidget = new KMessageWidget(this); + m_messageWidget->setCloseButtonVisible(true); + m_messageWidget->hide(); + + m_view = new DolphinView(url, this); + connect(m_view, SIGNAL(urlChanged(KUrl)), m_urlNavigator, SLOT(setLocationUrl(KUrl))); + connect(m_view, SIGNAL(urlChanged(KUrl)), m_messageWidget, SLOT(hide())); + connect(m_view, SIGNAL(directoryLoadingCompleted()), m_messageWidget, SLOT(hide())); + connect(m_view, SIGNAL(writeStateChanged(bool)), this, SIGNAL(writeStateChanged(bool))); + connect(m_view, SIGNAL(requestItemInfo(KFileItem)), this, SLOT(showItemInfo(KFileItem))); + connect(m_view, SIGNAL(itemActivated(KFileItem)), this, SLOT(slotItemActivated(KFileItem))); + connect(m_view, SIGNAL(itemsActivated(KFileItemList)), this, SLOT(slotItemsActivated(KFileItemList))); + connect(m_view, SIGNAL(redirection(KUrl,KUrl)), this, SLOT(redirect(KUrl,KUrl))); + connect(m_view, SIGNAL(directoryLoadingStarted()), this, SLOT(slotDirectoryLoadingStarted())); + connect(m_view, SIGNAL(directoryLoadingCompleted()), this, SLOT(slotDirectoryLoadingCompleted())); + connect(m_view, SIGNAL(directoryLoadingCanceled()), this, SLOT(slotDirectoryLoadingCanceled())); + connect(m_view, SIGNAL(itemCountChanged()), this, SLOT(delayedStatusBarUpdate())); + connect(m_view, SIGNAL(directoryLoadingProgress(int)), this, SLOT(updateDirectoryLoadingProgress(int))); + connect(m_view, SIGNAL(directorySortingProgress(int)), this, SLOT(updateDirectorySortingProgress(int))); + connect(m_view, SIGNAL(selectionChanged(KFileItemList)), this, SLOT(delayedStatusBarUpdate())); + connect(m_view, SIGNAL(urlAboutToBeChanged(KUrl)), this, SLOT(slotViewUrlAboutToBeChanged(KUrl))); + connect(m_view, SIGNAL(errorMessage(QString)), this, SLOT(showErrorMessage(QString))); + connect(m_view, SIGNAL(urlIsFileError(KUrl)), this, SLOT(slotUrlIsFileError(KUrl))); + connect(m_view, SIGNAL(activated()), this, SLOT(activate())); + + connect(m_urlNavigator, SIGNAL(urlAboutToBeChanged(KUrl)), + this, SLOT(slotUrlNavigatorLocationAboutToBeChanged(KUrl))); + connect(m_urlNavigator, SIGNAL(urlChanged(KUrl)), + this, SLOT(slotUrlNavigatorLocationChanged(KUrl))); + connect(m_urlNavigator, SIGNAL(historyChanged()), + this, SLOT(slotHistoryChanged())); + connect(m_urlNavigator, SIGNAL(returnPressed()), + this, SLOT(slotReturnPressed())); + + // Initialize status bar + m_statusBar = new DolphinStatusBar(this); + m_statusBar->setUrl(m_view->url()); + m_statusBar->setZoomLevel(m_view->zoomLevel()); + connect(m_view, SIGNAL(urlChanged(KUrl)), m_statusBar, SLOT(setUrl(KUrl))); + connect(m_view, SIGNAL(zoomLevelChanged(int,int)), m_statusBar, SLOT(setZoomLevel(int))); + connect(m_view, SIGNAL(infoMessage(QString)), m_statusBar, SLOT(setText(QString))); + connect(m_view, SIGNAL(operationCompletedMessage(QString)), m_statusBar, SLOT(setText(QString))); + connect(m_statusBar, SIGNAL(stopPressed()), this, SLOT(stopDirectoryLoading())); + connect(m_statusBar, SIGNAL(zoomLevelChanged(int)), this, SLOT(slotStatusBarZoomLevelChanged(int))); + + m_statusBarTimer = new QTimer(this); + m_statusBarTimer->setSingleShot(true); + m_statusBarTimer->setInterval(300); + connect(m_statusBarTimer, SIGNAL(timeout()), this, SLOT(updateStatusBar())); + + KIO::FileUndoManager* undoManager = KIO::FileUndoManager::self(); + connect(undoManager, SIGNAL(jobRecordingFinished(CommandType)), + this, SLOT(delayedStatusBarUpdate())); + + // Initialize filter bar + m_filterBar = new FilterBar(this); + m_filterBar->setVisible(settings->filterBar()); + connect(m_filterBar, SIGNAL(filterChanged(QString)), + this, SLOT(setNameFilter(QString))); + connect(m_filterBar, SIGNAL(closeRequest()), + this, SLOT(closeFilterBar())); + connect(m_filterBar, SIGNAL(focusViewRequest()), + this, SLOT(requestFocus())); + connect(m_view, SIGNAL(urlChanged(KUrl)), + m_filterBar, SLOT(slotUrlChanged())); + + m_topLayout->addWidget(m_urlNavigator); + m_topLayout->addWidget(m_searchBox); + m_topLayout->addWidget(m_messageWidget); + m_topLayout->addWidget(m_view); + m_topLayout->addWidget(m_filterBar); + m_topLayout->addWidget(m_statusBar); + + setSearchModeEnabled(isSearchUrl(url)); + + // Initialize kactivities resource instance + +} + +DolphinViewContainer::~DolphinViewContainer() +{ +} + +KUrl DolphinViewContainer::url() const +{ + return m_view->url(); +} + +void DolphinViewContainer::setActive(bool active) +{ + m_searchBox->setActive(active); + m_urlNavigator->setActive(active); + m_view->setActive(active); + +} + +bool DolphinViewContainer::isActive() const +{ + Q_ASSERT(m_view->isActive() == m_urlNavigator->isActive()); + return m_view->isActive(); +} + +void DolphinViewContainer::setAutoGrabFocus(bool grab) +{ + m_autoGrabFocus = grab; +} + +bool DolphinViewContainer::autoGrabFocus() const +{ + return m_autoGrabFocus; +} + +const DolphinStatusBar* DolphinViewContainer::statusBar() const +{ + return m_statusBar; +} + +DolphinStatusBar* DolphinViewContainer::statusBar() +{ + return m_statusBar; +} + +const KUrlNavigator* DolphinViewContainer::urlNavigator() const +{ + return m_urlNavigator; +} + +KUrlNavigator* DolphinViewContainer::urlNavigator() +{ + return m_urlNavigator; +} + +const DolphinView* DolphinViewContainer::view() const +{ + return m_view; +} + +DolphinView* DolphinViewContainer::view() +{ + return m_view; +} + +void DolphinViewContainer::showMessage(const QString& msg, MessageType type) +{ + if (msg.isEmpty()) { + return; + } + + m_messageWidget->setText(msg); + + switch (type) { + case Information: m_messageWidget->setMessageType(KMessageWidget::Information); break; + case Warning: m_messageWidget->setMessageType(KMessageWidget::Warning); break; + case Error: m_messageWidget->setMessageType(KMessageWidget::Error); break; + default: + Q_ASSERT(false); + break; + } + + m_messageWidget->setWordWrap(false); + const int unwrappedWidth = m_messageWidget->sizeHint().width(); + m_messageWidget->setWordWrap(unwrappedWidth > size().width()); + + if (m_messageWidget->isVisible()) { + m_messageWidget->hide(); + } + m_messageWidget->animatedShow(); +} + +void DolphinViewContainer::readSettings() +{ + if (GeneralSettings::modifiedStartupSettings()) { + // The startup settings should only get applied if they have been + // modified by the user. Otherwise keep the (possibly) different current + // settings of the URL navigator and the filterbar. + m_urlNavigator->setUrlEditable(GeneralSettings::editableUrl()); + m_urlNavigator->setShowFullPath(GeneralSettings::showFullPath()); + m_urlNavigator->setHomeUrl(KUrl(GeneralSettings::homeUrl())); + setFilterBarVisible(GeneralSettings::filterBar()); + } + + m_view->readSettings(); + m_statusBar->readSettings(); +} + +bool DolphinViewContainer::isFilterBarVisible() const +{ + return m_filterBar->isVisible(); +} + +void DolphinViewContainer::setSearchModeEnabled(bool enabled) +{ + if (enabled == isSearchModeEnabled()) { + if (enabled && !m_searchBox->hasFocus()) { + m_searchBox->setFocus(); + m_searchBox->selectAll(); + } + return; + } + + m_searchBox->setVisible(enabled); + m_urlNavigator->setVisible(!enabled); + + if (enabled) { + const KUrl& locationUrl = m_urlNavigator->locationUrl(); + m_searchBox->fromSearchUrl(locationUrl); + } else { + m_view->setViewPropertiesContext(QString()); + + // Restore the URL for the URL navigator. If Dolphin has been + // started with a search-URL, the home URL is used as fallback. + KUrl url = m_searchBox->searchPath(); + if (url.isEmpty() || !url.isValid() || isSearchUrl(url)) { + url = GeneralSettings::self()->homeUrl(); + } + m_urlNavigator->setLocationUrl(url); + } +} + +bool DolphinViewContainer::isSearchModeEnabled() const +{ + return m_searchBox->isVisible(); +} + +QString DolphinViewContainer::placesText() const +{ + QString text; + + if (isSearchModeEnabled()) { + text = m_searchBox->searchPath().fileName() + QLatin1String(": ") + m_searchBox->text(); + } else { + text = url().fileName(); + if (text.isEmpty()) { + text = url().host(); + } + } + + return text; +} + +void DolphinViewContainer::setUrl(const KUrl& newUrl) +{ + if (newUrl != m_urlNavigator->locationUrl()) { + m_urlNavigator->setLocationUrl(newUrl); + } + +} + +void DolphinViewContainer::setFilterBarVisible(bool visible) +{ + Q_ASSERT(m_filterBar); + if (visible) { + m_filterBar->show(); + m_filterBar->setFocus(); + m_filterBar->selectAll(); + } else { + closeFilterBar(); + } +} + +void DolphinViewContainer::delayedStatusBarUpdate() +{ + if (m_statusBarTimer->isActive() && (m_statusBarTimestamp.elapsed() > 2000)) { + // No update of the statusbar has been done during the last 2 seconds, + // although an update has been requested. Trigger an immediate update. + m_statusBarTimer->stop(); + updateStatusBar(); + } else { + // Invoke updateStatusBar() with a small delay. This assures that + // when a lot of delayedStatusBarUpdates() are done in a short time, + // no bottleneck is given. + m_statusBarTimer->start(); + } +} + +void DolphinViewContainer::updateStatusBar() +{ + m_statusBarTimestamp.start(); + + const QString text = m_view->statusBarText(); + m_statusBar->setDefaultText(text); + m_statusBar->resetToDefaultText(); +} + +void DolphinViewContainer::updateDirectoryLoadingProgress(int percent) +{ + if (m_statusBar->progressText().isEmpty()) { + m_statusBar->setProgressText(i18nc("@info:progress", "Loading folder...")); + } + m_statusBar->setProgress(percent); +} + +void DolphinViewContainer::updateDirectorySortingProgress(int percent) +{ + if (m_statusBar->progressText().isEmpty()) { + m_statusBar->setProgressText(i18nc("@info:progress", "Sorting...")); + } + m_statusBar->setProgress(percent); +} + +void DolphinViewContainer::slotDirectoryLoadingStarted() +{ + if (isSearchUrl(url())) { + // Search KIO-slaves usually don't provide any progress information. Give + // a hint to the user that a searching is done: + updateStatusBar(); + m_statusBar->setProgressText(i18nc("@info", "Searching...")); + m_statusBar->setProgress(-1); + } else { + // Trigger an undetermined progress indication. The progress + // information in percent will be triggered by the percent() signal + // of the directory lister later. + updateDirectoryLoadingProgress(-1); + } +} + +void DolphinViewContainer::slotDirectoryLoadingCompleted() +{ + if (!m_statusBar->progressText().isEmpty()) { + m_statusBar->setProgressText(QString()); + m_statusBar->setProgress(100); + } + + if (isSearchUrl(url()) && m_view->itemsCount() == 0) { + // Instead of showing the default status bar information ("0 items") + // a more helpful information is given: + m_statusBar->setText(i18nc("@info:status", "No items found.")); + } else { + updateStatusBar(); + } +} + +void DolphinViewContainer::slotDirectoryLoadingCanceled() +{ + if (!m_statusBar->progressText().isEmpty()) { + m_statusBar->setProgressText(QString()); + m_statusBar->setProgress(100); + } + + m_statusBar->setText(QString()); +} + +void DolphinViewContainer::slotUrlIsFileError(const KUrl& url) +{ + const KFileItem item(KFileItem::Unknown, KFileItem::Unknown, url); + + // Find out if the file can be opened in the view (for example, this is the + // case if the file is an archive). The mime type must be known for that. + item.determineMimeType(); + const KUrl& folderUrl = DolphinView::openItemAsFolderUrl(item, true); + if (!folderUrl.isEmpty()) { + m_view->setUrl(folderUrl); + } else { + slotItemActivated(item); + } +} + +void DolphinViewContainer::slotItemActivated(const KFileItem& item) +{ + // It is possible to activate items on inactive views by + // drag & drop operations. Assure that activating an item always + // results in an active view. + m_view->setActive(true); + + const KUrl& url = DolphinView::openItemAsFolderUrl(item, GeneralSettings::browseThroughArchives()); + if (!url.isEmpty()) { + m_view->setUrl(url); + return; + } + + item.run(); +} + +void DolphinViewContainer::slotItemsActivated(const KFileItemList& items) +{ + Q_ASSERT(items.count() >= 2); + + KFileItemActions fileItemActions(this); + fileItemActions.runPreferredApplications(items, QString()); +} + +void DolphinViewContainer::showItemInfo(const KFileItem& item) +{ + if (item.isNull()) { + m_statusBar->resetToDefaultText(); + } else { + m_statusBar->setText(item.getStatusBarInfo()); + } +} + +void DolphinViewContainer::closeFilterBar() +{ + m_filterBar->closeFilterBar(); + m_view->setFocus(); + emit showFilterBarChanged(false); +} + +void DolphinViewContainer::setNameFilter(const QString& nameFilter) +{ + m_view->setNameFilter(nameFilter); + delayedStatusBarUpdate(); +} + +void DolphinViewContainer::activate() +{ + setActive(true); +} + +void DolphinViewContainer::slotViewUrlAboutToBeChanged(const KUrl& url) +{ + // URL changes of the view can happen in two ways: + // 1. The URL navigator gets changed and will trigger the view to update its URL + // 2. The URL of the view gets changed and will trigger the URL navigator to update + // its URL (e.g. by clicking on an item) + // In this scope the view-state may only get saved in case 2: + if (url != m_urlNavigator->locationUrl()) { + saveViewState(); + } +} + +void DolphinViewContainer::slotUrlNavigatorLocationAboutToBeChanged(const KUrl& url) +{ + // URL changes of the view can happen in two ways: + // 1. The URL navigator gets changed and will trigger the view to update its URL + // 2. The URL of the view gets changed and will trigger the URL navigator to update + // its URL (e.g. by clicking on an item) + // In this scope the view-state may only get saved in case 1: + if (url != m_view->url()) { + saveViewState(); + } +} + +void DolphinViewContainer::slotUrlNavigatorLocationChanged(const KUrl& url) +{ + slotReturnPressed(); + + if (KProtocolManager::supportsListing(url)) { + setSearchModeEnabled(isSearchUrl(url)); + m_view->setUrl(url); + + if (m_autoGrabFocus && isActive() && !isSearchUrl(url)) { + // When an URL has been entered, the view should get the focus. + // The focus must be requested asynchronously, as changing the URL might create + // a new view widget. + QTimer::singleShot(0, this, SLOT(requestFocus())); + } + } else { + showMessage(i18nc("@info:status", "Invalid protocol"), Error); + } +} + +void DolphinViewContainer::dropUrls(const KUrl& destination, QDropEvent* event) +{ + m_dropDestination = destination; + + const QMimeData* mimeData = event->mimeData(); + QMimeData* mimeDataCopy = new QMimeData; + foreach (const QString& format, mimeData->formats()) { + mimeDataCopy->setData(format, mimeData->data(format)); + } + + m_dropEvent.reset(new QDropEvent(event->pos(), + event->possibleActions(), + mimeDataCopy, + event->mouseButtons(), + event->keyboardModifiers())); + + QTimer::singleShot(0, this, SLOT(dropUrlsDelayed())); +} + +void DolphinViewContainer::dropUrlsDelayed() +{ + if (m_dropEvent.isNull()) { + return; + } + + QString error; + DragAndDropHelper::dropUrls(KFileItem(), m_dropDestination, m_dropEvent.data(), error); + if (!error.isEmpty()) { + showMessage(error, Error); + } + + delete m_dropEvent->mimeData(); + m_dropEvent.reset(); +} + +void DolphinViewContainer::redirect(const KUrl& oldUrl, const KUrl& newUrl) +{ + Q_UNUSED(oldUrl); + const bool block = m_urlNavigator->signalsBlocked(); + m_urlNavigator->blockSignals(true); + + // Assure that the location state is reset for redirection URLs. This + // allows to skip redirection URLs when going back or forward in the + // URL history. + m_urlNavigator->saveLocationState(QByteArray()); + m_urlNavigator->setLocationUrl(newUrl); + setSearchModeEnabled(isSearchUrl(newUrl)); + + m_urlNavigator->blockSignals(block); +} + +void DolphinViewContainer::requestFocus() +{ + m_view->setFocus(); +} + +void DolphinViewContainer::saveUrlCompletionMode(KGlobalSettings::Completion completion) +{ + GeneralSettings::setUrlCompletionMode(completion); +} + +void DolphinViewContainer::slotHistoryChanged() +{ + QByteArray locationState = m_urlNavigator->locationState(); + if (!locationState.isEmpty()) { + QDataStream stream(&locationState, QIODevice::ReadOnly); + m_view->restoreState(stream); + } +} + +void DolphinViewContainer::slotReturnPressed() +{ + if (!GeneralSettings::editableUrl()) { + m_urlNavigator->setUrlEditable(false); + } +} + +void DolphinViewContainer::startSearching() +{ + const KUrl url = m_searchBox->urlForSearching(); + if (url.isValid() && !url.isEmpty()) { + m_view->setViewPropertiesContext("search"); + m_urlNavigator->setLocationUrl(url); + } +} + +void DolphinViewContainer::closeSearchBox() +{ + setSearchModeEnabled(false); +} + +void DolphinViewContainer::stopDirectoryLoading() +{ + m_view->stopLoading(); + m_statusBar->setProgress(100); +} + +void DolphinViewContainer::slotStatusBarZoomLevelChanged(int zoomLevel) +{ + m_view->setZoomLevel(zoomLevel); +} + +void DolphinViewContainer::showErrorMessage(const QString& msg) +{ + showMessage(msg, Error); +} + +bool DolphinViewContainer::isSearchUrl(const KUrl& url) const +{ + const QString protocol = url.protocol(); + return protocol.contains("search"); +} + +void DolphinViewContainer::saveViewState() +{ + QByteArray locationState; + QDataStream stream(&locationState, QIODevice::WriteOnly); + m_view->saveState(stream); + m_urlNavigator->saveLocationState(locationState); +} + +#include "moc_dolphinviewcontainer.cpp" diff --git a/dolphin/src/dolphinviewcontainer.h b/dolphin/src/dolphinviewcontainer.h new file mode 100644 index 00000000..1f9779f3 --- /dev/null +++ b/dolphin/src/dolphinviewcontainer.h @@ -0,0 +1,343 @@ +/*************************************************************************** + * Copyright (C) 2007 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef DOLPHINVIEWCONTAINER_H +#define DOLPHINVIEWCONTAINER_H + +#include +#include +#include +#include + +#include + +#include +#include + +#include + +class FilterBar; +class KMessageWidget; +class KUrl; +class KUrlNavigator; +class DolphinSearchBox; +class DolphinStatusBar; + +/** + * @short Represents a view for the directory content + * including the navigation bar, filter bar and status bar. + * + * View modes for icons, compact and details are supported. Currently + * Dolphin allows to have up to two views inside the main window. + * + * @see DolphinView + * @see FilterBar + * @see KUrlNavigator + * @see DolphinStatusBar + */ +class DolphinViewContainer : public QWidget +{ + Q_OBJECT + +public: + enum MessageType + { + Information, + Warning, + Error + }; + + DolphinViewContainer(const KUrl& url, QWidget* parent); + virtual ~DolphinViewContainer(); + + /** + * Returns the current active URL, where all actions are applied. + * The URL navigator is synchronized with this URL. + */ + KUrl url() const; + + /** + * If \a active is true, the view container will marked as active. The active + * view container is defined as view where all actions are applied to. + */ + void setActive(bool active); + bool isActive() const; + + /** + * If \a grab is set to true, the container automatically grabs the focus + * as soon as the URL has been changed. Per default the grabbing + * of the focus is enabled. + */ + void setAutoGrabFocus(bool grab); + bool autoGrabFocus() const; + + const DolphinStatusBar* statusBar() const; + DolphinStatusBar* statusBar(); + + const KUrlNavigator* urlNavigator() const; + KUrlNavigator* urlNavigator(); + + const DolphinView* view() const; + DolphinView* view(); + + /** + * Shows the message \msg with the given type non-modal above + * the view-content. + */ + void showMessage(const QString& msg, MessageType type); + + /** + * Refreshes the view container to get synchronized with the (updated) Dolphin settings. + */ + void readSettings(); + + /** Returns true, if the filter bar is visible. */ + bool isFilterBarVisible() const; + + /** + * Enables the search mode, if \p enabled is true. In the search mode the URL navigator + * will be hidden and replaced by a line editor that allows to enter a search term. + */ + void setSearchModeEnabled(bool enabled); + bool isSearchModeEnabled() const; + + /** + * @return Text that should be used for the current URL when creating + * a new place. + */ + QString placesText() const; + +public slots: + /** + * Sets the current active URL, where all actions are applied. The + * URL navigator is synchronized with this URL. The signals + * KUrlNavigator::urlChanged() and KUrlNavigator::historyChanged() + * are emitted. + * @see DolphinViewContainer::urlNavigator() + */ + void setUrl(const KUrl& url); + + /** + * Popups the filter bar above the status bar if \a visible is true. + * It \a visible is true, it is assured that the filter bar gains + * the keyboard focus. + */ + void setFilterBarVisible(bool visible); + +signals: + /** + * Is emitted whenever the filter bar has changed its visibility state. + */ + void showFilterBarChanged(bool shown); + + /** + * Is emitted when the write state of the folder has been changed. The application + * should disable all actions like "Create New..." that depend on the write + * state. + */ + void writeStateChanged(bool isFolderWritable); + +private slots: + /** + * Updates the number of items (= number of files + number of + * directories) in the statusbar. If files are selected, the number + * of selected files and the sum of the filesize is shown. The update + * is done asynchronously, as getting the sum of the + * filesizes can be an expensive operation. + * Unless a previous OperationCompletedMessage was set very shortly before + * calling this method, it will be overwritten (see DolphinStatusBar::setMessage). + * Previous ErrorMessages however are always preserved. + */ + void delayedStatusBarUpdate(); + + /** + * Is invoked by DolphinViewContainer::delayedStatusBarUpdate() and + * updates the status bar synchronously. + */ + void updateStatusBar(); + + void updateDirectoryLoadingProgress(int percent); + + void updateDirectorySortingProgress(int percent); + + /** + * Updates the statusbar to show an undetermined progress with the correct + * context information whether a searching or a directory loading is done. + */ + void slotDirectoryLoadingStarted(); + + /** + * Assures that the viewport position is restored and updates the + * statusbar to reflect the current content. + */ + void slotDirectoryLoadingCompleted(); + + /** + * Updates the statusbar to show, that the directory loading has + * been canceled. + */ + void slotDirectoryLoadingCanceled(); + + /** + * Is called if the URL set by DolphinView::setUrl() represents + * a file and not a directory. Takes care to activate the file. + */ + void slotUrlIsFileError(const KUrl& url); + + /** + * Handles clicking on an item. If the item is a directory, the + * directory is opened in the view. If the item is a file, the file + * gets started by the corresponding application. + */ + void slotItemActivated(const KFileItem& item); + + /** + * Handles activation of multiple files. The files get started by + * the corresponding applications. + */ + void slotItemsActivated(const KFileItemList& items); + + /** + * Shows the information for the item \a item inside the statusbar. If the + * item is null, the default statusbar information is shown. + */ + void showItemInfo(const KFileItem& item); + + void closeFilterBar(); + + /** + * Filters the currently shown items by \a nameFilter. All items + * which contain the given filter string will be shown. + */ + void setNameFilter(const QString& nameFilter); + + /** + * Marks the view container as active + * (see DolphinViewContainer::setActive()). + */ + void activate(); + + /** + * Is invoked if the signal urlAboutToBeChanged() from the DolphinView + * is emitted. Tries to save the view-state. + */ + void slotViewUrlAboutToBeChanged(const KUrl& url); + + /** + * Is invoked if the signal urlAboutToBeChanged() from the URL navigator + * is emitted. Tries to save the view-state. + */ + void slotUrlNavigatorLocationAboutToBeChanged(const KUrl& url); + + /** + * Restores the current view to show \a url and assures + * that the root URL of the view is respected. + */ + void slotUrlNavigatorLocationChanged(const KUrl& url); + + /** + * Is connected with the URL navigator and drops the URLs + * above the destination \a destination. + * + * Creates a copy of \a event and invokes \a dropUrlsDelayed with a + * queued connection. + */ + void dropUrls(const KUrl& destination, QDropEvent* event); + + /** + * Is invoked with a queued connection by \a dropUrls to prevent that the + * drop actions are executed in the URL navigator menu's nested event loop, + * which might cause a crash. Simply using a queued connection from the URL + * navigator to \a dropUrls would not work because the \a event pointer + * would be dangling then. + */ + void dropUrlsDelayed(); + + /** + * Is invoked when a redirection is done and changes the + * URL of the URL navigator to \a newUrl without triggering + * a reloading of the directory. + */ + void redirect(const KUrl& oldUrl, const KUrl& newUrl); + + /** Requests the focus for the view \a m_view. */ + void requestFocus(); + + /** + * Saves the currently used URL completion mode of + * the URL navigator. + */ + void saveUrlCompletionMode(KGlobalSettings::Completion completion); + + void slotHistoryChanged(); + + void slotReturnPressed(); + + /** + * Gets the search URL from the searchbox and starts searching. + */ + void startSearching(); + void closeSearchBox(); + + /** + * Stops the loading of a directory. Is connected with the "stopPressed" signal + * from the statusbar. + */ + void stopDirectoryLoading(); + + void slotStatusBarZoomLevelChanged(int zoomLevel); + + /** + * Slot that calls showMessage(msg, Error). + */ + void showErrorMessage(const QString& msg); + +private: + /** + * @return True if the URL protocol is a search URL (e. g. baloosearch:// or filenamesearch://). + */ + bool isSearchUrl(const KUrl& url) const; + + /** + * Saves the state of the current view: contents position, + * root URL, ... + */ + void saveViewState(); + +private: + QVBoxLayout* m_topLayout; + KUrlNavigator* m_urlNavigator; + DolphinSearchBox* m_searchBox; + KMessageWidget* m_messageWidget; + + DolphinView* m_view; + + FilterBar* m_filterBar; + + DolphinStatusBar* m_statusBar; + QTimer* m_statusBarTimer; // Triggers a delayed update + QElapsedTimer m_statusBarTimestamp; // Time in ms since last update + bool m_autoGrabFocus; + + KUrl m_dropDestination; + QScopedPointer m_dropEvent; + +}; + +#endif // DOLPHINVIEWCONTAINER_H diff --git a/dolphin/src/filterbar/filterbar.cpp b/dolphin/src/filterbar/filterbar.cpp new file mode 100644 index 00000000..7522c35a --- /dev/null +++ b/dolphin/src/filterbar/filterbar.cpp @@ -0,0 +1,143 @@ +/*************************************************************************** + * Copyright (C) 2006-2010 by Peter Penz * + * Copyright (C) 2006 by Gregor Kališnik * + * Copyright (C) 2012 by Stuart Citrin * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ +#include "filterbar.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + +FilterBar::FilterBar(QWidget* parent) : + QWidget(parent) +{ + // Create close button + QToolButton *closeButton = new QToolButton(this); + closeButton->setAutoRaise(true); + closeButton->setIcon(KIcon("dialog-close")); + closeButton->setToolTip(i18nc("@info:tooltip", "Hide Filter Bar")); + connect(closeButton, SIGNAL(clicked()), this, SIGNAL(closeRequest())); + + // Create button to lock text when changing folders + m_lockButton = new QToolButton(this); + m_lockButton->setAutoRaise(true); + m_lockButton->setCheckable(true); + m_lockButton->setIcon(KIcon("object-unlocked")); + m_lockButton->setToolTip(i18nc("@info:tooltip", "Keep Filter When Changing Folders")); + connect(m_lockButton, SIGNAL(toggled(bool)), this, SLOT(slotToggleLockButton(bool))); + + // Create label + QLabel* filterLabel = new QLabel(i18nc("@label:textbox", "Filter:"), this); + + // Create filter editor + m_filterInput = new KLineEdit(this); + m_filterInput->setLayoutDirection(Qt::LeftToRight); + m_filterInput->setClearButtonShown(true); + connect(m_filterInput, SIGNAL(textChanged(QString)), + this, SIGNAL(filterChanged(QString))); + setFocusProxy(m_filterInput); + + // Apply layout + QHBoxLayout* hLayout = new QHBoxLayout(this); + hLayout->setMargin(0); + hLayout->addWidget(closeButton); + hLayout->addWidget(filterLabel); + hLayout->addWidget(m_filterInput); + hLayout->addWidget(m_lockButton); + + filterLabel->setBuddy(m_filterInput); +} + +FilterBar::~FilterBar() +{ +} + +void FilterBar::closeFilterBar() +{ + hide(); + clear(); + if (m_lockButton) { + m_lockButton->setChecked(false); + } +} + +void FilterBar::selectAll() +{ + m_filterInput->selectAll(); +} + +void FilterBar::clear() +{ + m_filterInput->clear(); +} + +void FilterBar::slotUrlChanged() +{ + if (!m_lockButton || !(m_lockButton->isChecked())) { + clear(); + } +} + +void FilterBar::slotToggleLockButton(bool checked) +{ + if (checked) { + m_lockButton->setIcon(KIcon("object-locked")); + } else { + m_lockButton->setIcon(KIcon("object-unlocked")); + clear(); + } +} + +void FilterBar::showEvent(QShowEvent* event) +{ + if (!event->spontaneous()) { + m_filterInput->setFocus(); + } +} + +void FilterBar::keyReleaseEvent(QKeyEvent* event) +{ + QWidget::keyReleaseEvent(event); + + switch (event->key()) { + case Qt::Key_Escape: + if (m_filterInput->text().isEmpty()) { + emit closeRequest(); + } else { + m_filterInput->clear(); + } + break; + + case Qt::Key_Enter: + case Qt::Key_Return: + emit focusViewRequest(); + break; + + default: + break; + } +} + +#include "moc_filterbar.cpp" diff --git a/dolphin/src/filterbar/filterbar.h b/dolphin/src/filterbar/filterbar.h new file mode 100644 index 00000000..71bb3f65 --- /dev/null +++ b/dolphin/src/filterbar/filterbar.h @@ -0,0 +1,85 @@ +/*************************************************************************** + * Copyright (C) 2006-2010 by Peter Penz * + * Copyright (C) 2006 by Gregor Kališnik * + * Copyright (C) 2012 by Stuart Citrin * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef FILTERBAR_H +#define FILTERBAR_H + +#include + +class KLineEdit; +#include + +/** + * @brief Provides an input field for filtering the currently shown items. + * + * @author Gregor Kališnik + */ +class FilterBar : public QWidget +{ + Q_OBJECT + +public: + explicit FilterBar(QWidget* parent = 0); + virtual ~FilterBar(); + + /** Called by view container to hide this **/ + void closeFilterBar(); + + /** + * Selects the whole text of the filter bar. + */ + void selectAll(); + +public slots: + /** Clears the input field. */ + void clear(); + /** Clears the input field if the "lock button" is disabled. */ + void slotUrlChanged(); + /** The input field is cleared also if the "lock button" is released. */ + void slotToggleLockButton(bool checked); + +signals: + /** + * Signal that reports the name filter has been + * changed to \a nameFilter. + */ + void filterChanged(const QString& nameFilter); + + /** + * Emitted as soon as the filterbar should get closed. + */ + void closeRequest(); + + /* + * Emitted as soon as the focus should be returned back to the view. + */ + void focusViewRequest(); + +protected: + virtual void showEvent(QShowEvent* event); + virtual void keyReleaseEvent(QKeyEvent* event); + +private: + KLineEdit* m_filterInput; + QToolButton* m_lockButton; +}; + +#endif diff --git a/dolphin/src/kitemviews/kfileitemlistview.cpp b/dolphin/src/kitemviews/kfileitemlistview.cpp new file mode 100644 index 00000000..1faad774 --- /dev/null +++ b/dolphin/src/kitemviews/kfileitemlistview.cpp @@ -0,0 +1,411 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "kfileitemlistview.h" + +#include "kfileitemmodelrolesupdater.h" +#include "kfileitemlistwidget.h" +#include "kfileitemmodel.h" +#include +#include + +#include +#include +#include + +#include +#include +#include + +// #define KFILEITEMLISTVIEW_DEBUG + +namespace { + // If the visible index range changes, KFileItemModelRolesUpdater is not + // informed immediatetly, but with a short delay. This ensures that scrolling + // always feels smooth and is not interrupted by icon loading (which can be + // quite expensive if a disk access is required to determine the final icon). + const int ShortInterval = 50; + + // If the icon size changes, a longer delay is used. This prevents that + // the expensive re-generation of all previews is triggered repeatedly when + // chaning the zoom level. + const int LongInterval = 300; +} + +KFileItemListView::KFileItemListView(QGraphicsWidget* parent) : + KStandardItemListView(parent), + m_modelRolesUpdater(0), + m_updateVisibleIndexRangeTimer(0), + m_updateIconSizeTimer(0) +{ + setAcceptDrops(true); + + setScrollOrientation(Qt::Vertical); + + m_updateVisibleIndexRangeTimer = new QTimer(this); + m_updateVisibleIndexRangeTimer->setSingleShot(true); + m_updateVisibleIndexRangeTimer->setInterval(ShortInterval); + connect(m_updateVisibleIndexRangeTimer, SIGNAL(timeout()), this, SLOT(updateVisibleIndexRange())); + + m_updateIconSizeTimer = new QTimer(this); + m_updateIconSizeTimer->setSingleShot(true); + m_updateIconSizeTimer->setInterval(LongInterval); + connect(m_updateIconSizeTimer, SIGNAL(timeout()), this, SLOT(updateIconSize())); + + setVisibleRoles(QList() << "text"); +} + +KFileItemListView::~KFileItemListView() +{ +} + +void KFileItemListView::setPreviewsShown(bool show) +{ + if (!m_modelRolesUpdater) { + return; + } + + if (m_modelRolesUpdater->previewsShown() != show) { + beginTransaction(); + m_modelRolesUpdater->setPreviewsShown(show); + onPreviewsShownChanged(show); + endTransaction(); + } +} + +bool KFileItemListView::previewsShown() const +{ + return m_modelRolesUpdater ? m_modelRolesUpdater->previewsShown() : false; +} + +void KFileItemListView::setEnabledPlugins(const QStringList& list) +{ + if (m_modelRolesUpdater) { + m_modelRolesUpdater->setEnabledPlugins(list); + } +} + +QStringList KFileItemListView::enabledPlugins() const +{ + return m_modelRolesUpdater ? m_modelRolesUpdater->enabledPlugins() : QStringList(); +} + +QPixmap KFileItemListView::createDragPixmap(const KItemSet& indexes) const +{ + if (!model()) { + return QPixmap(); + } + + const int itemCount = indexes.count(); + Q_ASSERT(itemCount > 0); + if (itemCount == 1) { + return KItemListView::createDragPixmap(indexes); + } + + // If more than one item is dragged, align the items inside a + // rectangular grid. The maximum grid size is limited to 5 x 5 items. + int xCount; + int size; + if (itemCount > 16) { + xCount = 5; + size = KIconLoader::SizeSmall; + } else if (itemCount > 9) { + xCount = 4; + size = KIconLoader::SizeSmallMedium; + } else { + xCount = 3; + size = KIconLoader::SizeMedium; + } + + if (itemCount < xCount) { + xCount = itemCount; + } + + int yCount = itemCount / xCount; + if (itemCount % xCount != 0) { + ++yCount; + } + if (yCount > xCount) { + yCount = xCount; + } + + // Draw the selected items into the grid cells. + QPixmap dragPixmap(xCount * size + xCount, yCount * size + yCount); + dragPixmap.fill(Qt::transparent); + + QPainter painter(&dragPixmap); + int x = 0; + int y = 0; + + foreach (int index, indexes) { + QPixmap pixmap = model()->data(index).value("iconPixmap").value(); + if (pixmap.isNull()) { + KIcon icon(model()->data(index).value("iconName").toString()); + pixmap = icon.pixmap(size, size); + } else { + pixmap = pixmap.scaled(QSize(size, size), Qt::KeepAspectRatio, Qt::SmoothTransformation); + } + + painter.drawPixmap(x, y, pixmap); + + x += size + 1; + if (x >= dragPixmap.width()) { + x = 0; + y += size + 1; + } + + if (y >= dragPixmap.height()) { + break; + } + } + + return dragPixmap; +} + +KItemListWidgetCreatorBase* KFileItemListView::defaultWidgetCreator() const +{ + return new KItemListWidgetCreator(); +} + +void KFileItemListView::initializeItemListWidget(KItemListWidget* item) +{ + KStandardItemListView::initializeItemListWidget(item); + + // Make sure that the item has an icon. + QHash data = item->data(); + if (!data.contains("iconName") && data["iconPixmap"].value().isNull()) { + Q_ASSERT(qobject_cast(model())); + KFileItemModel* fileItemModel = static_cast(model()); + + const KFileItem fileItem = fileItemModel->fileItem(item->index()); + data.insert("iconName", fileItem.iconName()); + item->setData(data, QSet() << "iconName"); + } +} + +void KFileItemListView::onPreviewsShownChanged(bool shown) +{ + Q_UNUSED(shown); +} + +void KFileItemListView::onItemLayoutChanged(ItemLayout current, ItemLayout previous) +{ + KStandardItemListView::onItemLayoutChanged(current, previous); + triggerVisibleIndexRangeUpdate(); +} + +void KFileItemListView::onModelChanged(KItemModelBase* current, KItemModelBase* previous) +{ + Q_ASSERT(qobject_cast(current)); + KStandardItemListView::onModelChanged(current, previous); + + delete m_modelRolesUpdater; + m_modelRolesUpdater = 0; + + if (current) { + m_modelRolesUpdater = new KFileItemModelRolesUpdater(static_cast(current), this); + m_modelRolesUpdater->setIconSize(availableIconSize()); + + applyRolesToModel(); + } +} + +void KFileItemListView::onScrollOrientationChanged(Qt::Orientation current, Qt::Orientation previous) +{ + KStandardItemListView::onScrollOrientationChanged(current, previous); + triggerVisibleIndexRangeUpdate(); +} + +void KFileItemListView::onItemSizeChanged(const QSizeF& current, const QSizeF& previous) +{ + Q_UNUSED(current); + Q_UNUSED(previous); + triggerVisibleIndexRangeUpdate(); +} + +void KFileItemListView::onScrollOffsetChanged(qreal current, qreal previous) +{ + KStandardItemListView::onScrollOffsetChanged(current, previous); + triggerVisibleIndexRangeUpdate(); +} + +void KFileItemListView::onVisibleRolesChanged(const QList& current, const QList& previous) +{ + KStandardItemListView::onVisibleRolesChanged(current, previous); + applyRolesToModel(); +} + +void KFileItemListView::onStyleOptionChanged(const KItemListStyleOption& current, const KItemListStyleOption& previous) +{ + KStandardItemListView::onStyleOptionChanged(current, previous); + triggerIconSizeUpdate(); +} + +void KFileItemListView::onSupportsItemExpandingChanged(bool supportsExpanding) +{ + applyRolesToModel(); + KStandardItemListView::onSupportsItemExpandingChanged(supportsExpanding); + triggerVisibleIndexRangeUpdate(); +} + +void KFileItemListView::onTransactionBegin() +{ + if (m_modelRolesUpdater) { + m_modelRolesUpdater->setPaused(true); + } +} + +void KFileItemListView::onTransactionEnd() +{ + if (!m_modelRolesUpdater) { + return; + } + + // Only unpause the model-roles-updater if no timer is active. If one + // timer is still active the model-roles-updater will be unpaused later as + // soon as the timer has been exceeded. + const bool timerActive = m_updateVisibleIndexRangeTimer->isActive() || + m_updateIconSizeTimer->isActive(); + if (!timerActive) { + m_modelRolesUpdater->setPaused(false); + } +} + +void KFileItemListView::resizeEvent(QGraphicsSceneResizeEvent* event) +{ + KStandardItemListView::resizeEvent(event); + triggerVisibleIndexRangeUpdate(); +} + +void KFileItemListView::slotItemsRemoved(const KItemRangeList& itemRanges) +{ + KStandardItemListView::slotItemsRemoved(itemRanges); +} + +void KFileItemListView::slotSortRoleChanged(const QByteArray& current, const QByteArray& previous) +{ + const QByteArray sortRole = model()->sortRole(); + if (!visibleRoles().contains(sortRole)) { + applyRolesToModel(); + } + + KStandardItemListView::slotSortRoleChanged(current, previous); +} + +void KFileItemListView::triggerVisibleIndexRangeUpdate() +{ + if (!model()) { + return; + } + m_modelRolesUpdater->setPaused(true); + + // If the icon size has been changed recently, wait until + // m_updateIconSizeTimer expires. + if (!m_updateIconSizeTimer->isActive()) { + m_updateVisibleIndexRangeTimer->start(); + } +} + +void KFileItemListView::updateVisibleIndexRange() +{ + if (!m_modelRolesUpdater) { + return; + } + + const int index = firstVisibleIndex(); + const int count = lastVisibleIndex() - index + 1; + m_modelRolesUpdater->setMaximumVisibleItems(maximumVisibleItems()); + m_modelRolesUpdater->setVisibleIndexRange(index, count); + m_modelRolesUpdater->setPaused(isTransactionActive()); +} + +void KFileItemListView::triggerIconSizeUpdate() +{ + if (!model()) { + return; + } + m_modelRolesUpdater->setPaused(true); + m_updateIconSizeTimer->start(); + + // The visible index range will be updated when m_updateIconSizeTimer expires. + // Stop m_updateVisibleIndexRangeTimer to prevent an expensive re-generation + // of all previews (note that the user might change the icon size again soon). + m_updateVisibleIndexRangeTimer->stop(); +} + +void KFileItemListView::updateIconSize() +{ + if (!m_modelRolesUpdater) { + return; + } + + m_modelRolesUpdater->setIconSize(availableIconSize()); + + // Update the visible index range (which has most likely changed after the + // icon size change) before unpausing m_modelRolesUpdater. + const int index = firstVisibleIndex(); + const int count = lastVisibleIndex() - index + 1; + m_modelRolesUpdater->setVisibleIndexRange(index, count); + + m_modelRolesUpdater->setPaused(isTransactionActive()); +} + +void KFileItemListView::applyRolesToModel() +{ + if (!model()) { + return; + } + + Q_ASSERT(qobject_cast(model())); + KFileItemModel* fileItemModel = static_cast(model()); + + // KFileItemModel does not distinct between "visible" and "invisible" roles. + // Add all roles that are mandatory for having a working KFileItemListView: + QSet roles = visibleRoles().toSet(); + roles.insert("iconPixmap"); + roles.insert("iconName"); + roles.insert("text"); + roles.insert("isDir"); + roles.insert("isLink"); + if (supportsItemExpanding()) { + roles.insert("isExpanded"); + roles.insert("isExpandable"); + roles.insert("expandedParentsCount"); + } + + // Assure that the role that is used for sorting will be determined + roles.insert(fileItemModel->sortRole()); + + fileItemModel->setRoles(roles); + m_modelRolesUpdater->setRoles(roles); +} + +QSize KFileItemListView::availableIconSize() const +{ + const KItemListStyleOption& option = styleOption(); + const int iconSize = option.iconSize; + if (itemLayout() == IconsLayout) { + const int maxIconWidth = itemSize().width() - 2 * option.padding; + return QSize(maxIconWidth, iconSize); + } + + return QSize(iconSize, iconSize); +} + +#include "moc_kfileitemlistview.cpp" diff --git a/dolphin/src/kitemviews/kfileitemlistview.h b/dolphin/src/kitemviews/kfileitemlistview.h new file mode 100644 index 00000000..f7eb659e --- /dev/null +++ b/dolphin/src/kitemviews/kfileitemlistview.h @@ -0,0 +1,123 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef KFILEITEMLISTVIEW_H +#define KFILEITEMLISTVIEW_H + +#include + +#include + +class KFileItemModelRolesUpdater; +#include + +/** + * @brief View that allows to show the content of file-items. + * + * The corresponding model set by the controller must be an instance + * of KFileItemModel. Per default KFileItemListWidget is set as widget creator + * value and KItemListGroupHeader as group-header creator value. Use + * KItemListView::setWidgetCreator() and KItemListView::setGroupHeaderCreator() + * to apply customized generators. + */ +class DOLPHINPRIVATE_EXPORT KFileItemListView : public KStandardItemListView +{ + Q_OBJECT + +public: + KFileItemListView(QGraphicsWidget* parent = 0); + virtual ~KFileItemListView(); + + void setPreviewsShown(bool show); + bool previewsShown() const; + + /** + * Sets the list of enabled thumbnail plugins that are used for previews. + * Per default all plugins enabled in the KConfigGroup "PreviewSettings" + * are used. + * + * For a list of available plugins, call KServiceTypeTrader::self()->query("ThumbCreator"). + * + * @see enabledPlugins + */ + void setEnabledPlugins(const QStringList& list); + + /** + * Returns the list of enabled thumbnail plugins. + * @see setEnabledPlugins + */ + QStringList enabledPlugins() const; + + /** @reimp */ + virtual QPixmap createDragPixmap(const KItemSet& indexes) const; + +protected: + virtual KItemListWidgetCreatorBase* defaultWidgetCreator() const; + virtual void initializeItemListWidget(KItemListWidget* item); + virtual void onPreviewsShownChanged(bool shown); + virtual void onItemLayoutChanged(ItemLayout current, ItemLayout previous); + virtual void onModelChanged(KItemModelBase* current, KItemModelBase* previous); + virtual void onScrollOrientationChanged(Qt::Orientation current, Qt::Orientation previous); + virtual void onItemSizeChanged(const QSizeF& current, const QSizeF& previous); + virtual void onScrollOffsetChanged(qreal current, qreal previous); + virtual void onVisibleRolesChanged(const QList& current, const QList& previous); + virtual void onStyleOptionChanged(const KItemListStyleOption& current, const KItemListStyleOption& previous); + virtual void onSupportsItemExpandingChanged(bool supportsExpanding); + virtual void onTransactionBegin(); + virtual void onTransactionEnd(); + virtual void resizeEvent(QGraphicsSceneResizeEvent* event); + +protected slots: + virtual void slotItemsRemoved(const KItemRangeList& itemRanges); + virtual void slotSortRoleChanged(const QByteArray& current, const QByteArray& previous); + +private slots: + void triggerVisibleIndexRangeUpdate(); + void updateVisibleIndexRange(); + + void triggerIconSizeUpdate(); + void updateIconSize(); + +private: + /** + * Applies the roles defined by KItemListView::visibleRoles() to the + * KFileItemModel and KFileItemModelRolesUpdater. As the model does not + * distinct between visible and invisible roles also internal roles + * are applied that are mandatory for having a working KFileItemModel. + */ + void applyRolesToModel(); + + /** + * @return Size that is available for the icons. The size might be larger than specified by + * KItemListStyleOption::iconSize: With the IconsLayout also the empty left area left + * and right of an icon will be included. + */ + QSize availableIconSize() const; + +private: + KFileItemModelRolesUpdater* m_modelRolesUpdater; + QTimer* m_updateVisibleIndexRangeTimer; + QTimer* m_updateIconSizeTimer; + + friend class KFileItemListViewTest; // For unit testing +}; + +#endif + + diff --git a/dolphin/src/kitemviews/kfileitemlistwidget.cpp b/dolphin/src/kitemviews/kfileitemlistwidget.cpp new file mode 100644 index 00000000..0b06abd7 --- /dev/null +++ b/dolphin/src/kitemviews/kfileitemlistwidget.cpp @@ -0,0 +1,164 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "kfileitemlistwidget.h" +#include "kfileitemmodel.h" +#include "kitemlistview.h" + +#include +#include +#include +#include +#include +#include + +KFileItemListWidgetInformant::KFileItemListWidgetInformant() : + KStandardItemListWidgetInformant() +{ +} + +KFileItemListWidgetInformant::~KFileItemListWidgetInformant() +{ +} + +QString KFileItemListWidgetInformant::itemText(int index, const KItemListView* view) const +{ + Q_ASSERT(qobject_cast(view->model())); + KFileItemModel* fileItemModel = static_cast(view->model()); + + const KFileItem item = fileItemModel->fileItem(index); + return item.text(); +} + +bool KFileItemListWidgetInformant::itemIsLink(int index, const KItemListView* view) const +{ + Q_ASSERT(qobject_cast(view->model())); + KFileItemModel* fileItemModel = static_cast(view->model()); + + const KFileItem item = fileItemModel->fileItem(index); + return item.isLink(); +} + +QString KFileItemListWidgetInformant::roleText(const QByteArray& role, + const QHash& values) const +{ + QString text; + const QVariant roleValue = values.value(role); + + // Implementation note: In case if more roles require a custom handling + // use a hash + switch for a linear runtime. + + if (role == "size") { + if (values.value("isDir").toBool()) { + // The item represents a directory. Show the number of sub directories + // instead of the file size of the directory. + if (!roleValue.isNull()) { + const int count = roleValue.toInt(); + if (count < 0) { + text = i18nc("@item:intable", "Unknown"); + } else { + text = i18ncp("@item:intable", "%1 item", "%1 items", count); + } + } + } else { + const KIO::filesize_t size = roleValue.value(); + text = KGlobal::locale()->formatByteSize(size); + } + } else if (role == "date") { + const QDateTime dateTime = roleValue.toDateTime(); + text = KGlobal::locale()->formatDateTime(dateTime); + } else { + text = KStandardItemListWidgetInformant::roleText(role, values); + } + + return text; +} + +QFont KFileItemListWidgetInformant::customizedFontForLinks(const QFont& baseFont) const +{ + // The customized font should be italic if the file is a symbolic link. + QFont font(baseFont); + font.setItalic(true); + return font; +} + + +KFileItemListWidget::KFileItemListWidget(KItemListWidgetInformant* informant, QGraphicsItem* parent) : + KStandardItemListWidget(informant, parent) +{ +} + +KFileItemListWidget::~KFileItemListWidget() +{ +} + +KItemListWidgetInformant* KFileItemListWidget::createInformant() +{ + return new KFileItemListWidgetInformant(); +} + +bool KFileItemListWidget::isRoleRightAligned(const QByteArray& role) const +{ + return role == "size"; +} + +bool KFileItemListWidget::isHidden() const +{ + return data().value("text").toString().startsWith(QLatin1Char('.')); +} + +QFont KFileItemListWidget::customizedFont(const QFont& baseFont) const +{ + // The customized font should be italic if the file is a symbolic link. + QFont font(baseFont); + font.setItalic(data().value("isLink").toBool()); + return font; +} + +int KFileItemListWidget::selectionLength(const QString& text) const +{ + // Select the text without MIME-type extension + int selectionLength = text.length(); + + // If item is a directory, use the whole text length for + // selection (ignore all points) + if(data().value("isDir").toBool()) { + return selectionLength; + } + + const QString extension = KMimeType::extractKnownExtension(text); + if (extension.isEmpty()) { + // For an unknown extension just exclude the extension after + // the last point. This does not work for multiple extensions like + // *.tar.gz but usually this is anyhow a known extension. + selectionLength = text.lastIndexOf(QLatin1Char('.')); + + // If no point could be found, use whole text length for selection. + if (selectionLength < 1) { + selectionLength = text.length(); + } + + } else { + selectionLength -= extension.length() + 1; + } + + return selectionLength; +} + +#include "moc_kfileitemlistwidget.cpp" diff --git a/dolphin/src/kitemviews/kfileitemlistwidget.h b/dolphin/src/kitemviews/kfileitemlistwidget.h new file mode 100644 index 00000000..0cec346c --- /dev/null +++ b/dolphin/src/kitemviews/kfileitemlistwidget.h @@ -0,0 +1,63 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef KFILEITEMLISTWIDGET_H +#define KFILEITEMLISTWIDGET_H + +#include + +#include + +class DOLPHINPRIVATE_EXPORT KFileItemListWidgetInformant : public KStandardItemListWidgetInformant +{ +public: + KFileItemListWidgetInformant(); + virtual ~KFileItemListWidgetInformant(); + +protected: + virtual QString itemText(int index, const KItemListView* view) const; + virtual bool itemIsLink(int index, const KItemListView* view) const; + virtual QString roleText(const QByteArray& role, const QHash& values) const; + virtual QFont customizedFontForLinks(const QFont& baseFont) const; +}; + +class DOLPHINPRIVATE_EXPORT KFileItemListWidget : public KStandardItemListWidget +{ + Q_OBJECT + +public: + KFileItemListWidget(KItemListWidgetInformant* informant, QGraphicsItem* parent); + virtual ~KFileItemListWidget(); + + static KItemListWidgetInformant* createInformant(); + +protected: + virtual bool isRoleRightAligned(const QByteArray& role) const; + virtual bool isHidden() const; + virtual QFont customizedFont(const QFont& baseFont) const; + + /** + * @return Selection length without MIME-type extension + */ + virtual int selectionLength(const QString& text) const; +}; + +#endif + + diff --git a/dolphin/src/kitemviews/kfileitemmodel.cpp b/dolphin/src/kitemviews/kfileitemmodel.cpp new file mode 100644 index 00000000..60881855 --- /dev/null +++ b/dolphin/src/kitemviews/kfileitemmodel.cpp @@ -0,0 +1,2176 @@ +/***************************************************************************** + * Copyright (C) 2011 by Peter Penz * + * Copyright (C) 2013 by Frank Reininghaus * + * Copyright (C) 2013 by Emmanuel Pescosta * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + *****************************************************************************/ + +#include "kfileitemmodel.h" + +#include +#include +#include +#include + +#include "private/kfileitemmodelsortalgorithm.h" +#include "private/kfileitemmodeldirlister.h" + +#include +#include +#include +#include +#include +#include + +#include +#include + +// #define KFILEITEMMODEL_DEBUG + +KFileItemModel::KFileItemModel(QObject* parent) : + KItemModelBase("text", parent), + m_dirLister(0), + m_naturalSorting(KGlobalSettings::naturalSorting()), + m_sortDirsFirst(true), + m_sortRole(NameRole), + m_sortingProgressPercent(-1), + m_roles(), + m_caseSensitivity(Qt::CaseInsensitive), + m_itemData(), + m_items(), + m_filter(), + m_filteredItems(), + m_requestRole(), + m_maximumUpdateIntervalTimer(0), + m_resortAllItemsTimer(0), + m_pendingItemsToInsert(), + m_groups(), + m_expandedDirs(), + m_urlsToExpand() +{ + m_dirLister = new KFileItemModelDirLister(this); + m_dirLister->setDelayedMimeTypes(true); + + const QWidget* parentWidget = qobject_cast(parent); + if (parentWidget) { + m_dirLister->setMainWindow(parentWidget->window()); + } + + connect(m_dirLister, SIGNAL(started(KUrl)), this, SIGNAL(directoryLoadingStarted())); + connect(m_dirLister, SIGNAL(canceled()), this, SLOT(slotCanceled())); + connect(m_dirLister, SIGNAL(completed(KUrl)), this, SLOT(slotCompleted())); + connect(m_dirLister, SIGNAL(itemsAdded(KUrl,KFileItemList)), this, SLOT(slotItemsAdded(KUrl,KFileItemList))); + connect(m_dirLister, SIGNAL(itemsDeleted(KFileItemList)), this, SLOT(slotItemsDeleted(KFileItemList))); + connect(m_dirLister, SIGNAL(refreshItems(QList >)), this, SLOT(slotRefreshItems(QList >))); + connect(m_dirLister, SIGNAL(clear()), this, SLOT(slotClear())); + connect(m_dirLister, SIGNAL(infoMessage(QString)), this, SIGNAL(infoMessage(QString))); + connect(m_dirLister, SIGNAL(errorMessage(QString)), this, SIGNAL(errorMessage(QString))); + connect(m_dirLister, SIGNAL(redirection(KUrl,KUrl)), this, SIGNAL(directoryRedirection(KUrl,KUrl))); + connect(m_dirLister, SIGNAL(urlIsFileError(KUrl)), this, SIGNAL(urlIsFileError(KUrl))); + + // Apply default roles that should be determined + resetRoles(); + m_requestRole[NameRole] = true; + m_requestRole[IsDirRole] = true; + m_requestRole[IsLinkRole] = true; + m_roles.insert("text"); + m_roles.insert("isDir"); + m_roles.insert("isLink"); + + // For slow KIO-slaves like used for searching it makes sense to show results periodically even + // before the completed() or canceled() signal has been emitted. + m_maximumUpdateIntervalTimer = new QTimer(this); + m_maximumUpdateIntervalTimer->setInterval(2000); + m_maximumUpdateIntervalTimer->setSingleShot(true); + connect(m_maximumUpdateIntervalTimer, SIGNAL(timeout()), this, SLOT(dispatchPendingItemsToInsert())); + + // When changing the value of an item which represents the sort-role a resorting must be + // triggered. Especially in combination with KFileItemModelRolesUpdater this might be done + // for a lot of items within a quite small timeslot. To prevent expensive resortings the + // resorting is postponed until the timer has been exceeded. + m_resortAllItemsTimer = new QTimer(this); + m_resortAllItemsTimer->setInterval(500); + m_resortAllItemsTimer->setSingleShot(true); + connect(m_resortAllItemsTimer, SIGNAL(timeout()), this, SLOT(resortAllItems())); + + connect(KGlobalSettings::self(), SIGNAL(naturalSortingChanged()), this, SLOT(slotNaturalSortingChanged())); +} + +KFileItemModel::~KFileItemModel() +{ + qDeleteAll(m_itemData); + qDeleteAll(m_filteredItems.values()); + qDeleteAll(m_pendingItemsToInsert); +} + +void KFileItemModel::loadDirectory(const KUrl& url) +{ + m_dirLister->openUrl(url); +} + +void KFileItemModel::refreshDirectory(const KUrl& url) +{ + // Refresh all expanded directories first (Bug 295300) + QHashIterator expandedDirs(m_expandedDirs); + while (expandedDirs.hasNext()) { + expandedDirs.next(); + m_dirLister->openUrl(expandedDirs.value(), KDirLister::Reload); + } + + m_dirLister->openUrl(url, KDirLister::Reload); +} + +KUrl KFileItemModel::directory() const +{ + return m_dirLister->url(); +} + +void KFileItemModel::cancelDirectoryLoading() +{ + m_dirLister->stop(); +} + +int KFileItemModel::count() const +{ + return m_itemData.count(); +} + +QHash KFileItemModel::data(int index) const +{ + if (index >= 0 && index < count()) { + ItemData* data = m_itemData.at(index); + if (data->values.isEmpty()) { + data->values = retrieveData(data->item, data->parent); + } + + return data->values; + } + return QHash(); +} + +bool KFileItemModel::setData(int index, const QHash& values) +{ + if (index < 0 || index >= count()) { + return false; + } + + QHash currentValues = data(index); + + // Determine which roles have been changed + QSet changedRoles; + QHashIterator it(values); + while (it.hasNext()) { + it.next(); + const QByteArray role = sharedValue(it.key()); + const QVariant value = it.value(); + + if (currentValues[role] != value) { + currentValues[role] = value; + changedRoles.insert(role); + } + } + + if (changedRoles.isEmpty()) { + return false; + } + + m_itemData[index]->values = currentValues; + if (changedRoles.contains("text")) { + KUrl url = m_itemData[index]->item.url(); + url.setFileName(currentValues["text"].toString()); + m_itemData[index]->item.setUrl(url); + } + + emitItemsChangedAndTriggerResorting(KItemRangeList() << KItemRange(index, 1), changedRoles); + + return true; +} + +void KFileItemModel::setSortDirectoriesFirst(bool dirsFirst) +{ + if (dirsFirst != m_sortDirsFirst) { + m_sortDirsFirst = dirsFirst; + resortAllItems(); + } +} + +bool KFileItemModel::sortDirectoriesFirst() const +{ + return m_sortDirsFirst; +} + +void KFileItemModel::setShowHiddenFiles(bool show) +{ + m_dirLister->setShowingDotFiles(show); + m_dirLister->emitChanges(); + if (show) { + dispatchPendingItemsToInsert(); + } +} + +bool KFileItemModel::showHiddenFiles() const +{ + return m_dirLister->showingDotFiles(); +} + +void KFileItemModel::setShowDirectoriesOnly(bool enabled) +{ + m_dirLister->setDirOnlyMode(enabled); +} + +bool KFileItemModel::showDirectoriesOnly() const +{ + return m_dirLister->dirOnlyMode(); +} + +QMimeData* KFileItemModel::createMimeData(const KItemSet& indexes) const +{ + QMimeData* data = new QMimeData(); + + // The following code has been taken from KDirModel::mimeData() + // (kdelibs/kio/kio/kdirmodel.cpp) + // Copyright (C) 2006 David Faure + KUrl::List urls; + KUrl::List mostLocalUrls; + bool canUseMostLocalUrls = true; + const ItemData* lastAddedItem = 0; + + foreach (int index, indexes) { + const ItemData* itemData = m_itemData.at(index); + const ItemData* parent = itemData->parent; + + while (parent && parent != lastAddedItem) { + parent = parent->parent; + } + + if (parent && parent == lastAddedItem) { + // A parent of 'itemData' has been added already. + continue; + } + + lastAddedItem = itemData; + const KFileItem& item = itemData->item; + if (!item.isNull()) { + urls << item.targetUrl(); + + bool isLocal; + mostLocalUrls << item.mostLocalUrl(isLocal); + if (!isLocal) { + canUseMostLocalUrls = false; + } + } + } + + const bool different = canUseMostLocalUrls && mostLocalUrls != urls; + if (different) { + urls.populateMimeData(mostLocalUrls, data); + } else { + urls.populateMimeData(data); + } + + return data; +} + +int KFileItemModel::indexForKeyboardSearch(const QString& text, int startFromIndex) const +{ + startFromIndex = qMax(0, startFromIndex); + for (int i = startFromIndex; i < count(); ++i) { + if (fileItem(i).text().startsWith(text, Qt::CaseInsensitive)) { + return i; + } + } + for (int i = 0; i < startFromIndex; ++i) { + if (fileItem(i).text().startsWith(text, Qt::CaseInsensitive)) { + return i; + } + } + return -1; +} + +bool KFileItemModel::supportsDropping(int index) const +{ + const KFileItem item = fileItem(index); + return !item.isNull() && (item.isDir() || item.isDesktopFile()); +} + +QString KFileItemModel::roleDescription(const QByteArray& role) const +{ + static QHash description; + if (description.isEmpty()) { + int count = 0; + const RoleInfoMap* map = rolesInfoMap(count); + for (int i = 0; i < count; ++i) { + description.insert(map[i].role, i18nc(map[i].roleTranslationContext, map[i].roleTranslation)); + } + } + + return description.value(role); +} + +QList > KFileItemModel::groups() const +{ + if (!m_itemData.isEmpty() && m_groups.isEmpty()) { +#ifdef KFILEITEMMODEL_DEBUG + QElapsedTimer timer; + timer.start(); +#endif + switch (typeForRole(sortRole())) { + case NameRole: m_groups = nameRoleGroups(); break; + case SizeRole: m_groups = sizeRoleGroups(); break; + case DateRole: m_groups = dateRoleGroups(); break; + case PermissionsRole: m_groups = permissionRoleGroups(); break; + default: m_groups = genericStringRoleGroups(sortRole()); break; + } + +#ifdef KFILEITEMMODEL_DEBUG + kDebug() << "[TIME] Calculating groups for" << count() << "items:" << timer.elapsed(); +#endif + } + + return m_groups; +} + +KFileItem KFileItemModel::fileItem(int index) const +{ + if (index >= 0 && index < count()) { + return m_itemData.at(index)->item; + } + + return KFileItem(); +} + +KFileItem KFileItemModel::fileItem(const KUrl& url) const +{ + const int indexForUrl = index(url); + if (indexForUrl >= 0) { + return m_itemData.at(indexForUrl)->item; + } + return KFileItem(); +} + +int KFileItemModel::index(const KFileItem& item) const +{ + return index(item.url()); +} + +int KFileItemModel::index(const KUrl& url) const +{ + KUrl urlToFind = url; + urlToFind.adjustPath(KUrl::RemoveTrailingSlash); + + const int itemCount = m_itemData.count(); + int itemsInHash = m_items.count(); + + int index = m_items.value(urlToFind, -1); + while (index < 0 && itemsInHash < itemCount) { + // Not all URLs are stored yet in m_items. We grow m_items until either + // urlToFind is found, or all URLs have been stored in m_items. + // Note that we do not add the URLs to m_items one by one, but in + // larger blocks. After each block, we check if urlToFind is in + // m_items. We could in principle compare urlToFind with each URL while + // we are going through m_itemData, but comparing two QUrls will, + // unlike calling qHash for the URLs, trigger a parsing of the URLs + // which costs both CPU cycles and memory. + const int blockSize = 1000; + const int currentBlockEnd = qMin(itemsInHash + blockSize, itemCount); + for (int i = itemsInHash; i < currentBlockEnd; ++i) { + const KUrl nextUrl = m_itemData.at(i)->item.url(); + m_items.insert(nextUrl, i); + } + + itemsInHash = currentBlockEnd; + index = m_items.value(urlToFind, -1); + } + + if (index < 0) { + // The item could not be found, even though all items from m_itemData + // should be in m_items now. We print some diagnostic information which + // might help to find the cause of the problem, but only once. This + // prevents that obtaining and printing the debugging information + // wastes CPU cycles and floods the shell or .xsession-errors. + static bool printDebugInfo = true; + + if (m_items.count() != m_itemData.count() && printDebugInfo) { + printDebugInfo = false; + + kWarning() << "The model is in an inconsistent state."; + kWarning() << "m_items.count() ==" << m_items.count(); + kWarning() << "m_itemData.count() ==" << m_itemData.count(); + + // Check if there are multiple items with the same URL. + QMultiHash indexesForUrl; + for (int i = 0; i < m_itemData.count(); ++i) { + indexesForUrl.insert(m_itemData.at(i)->item.url(), i); + } + + foreach (const KUrl& url, indexesForUrl.uniqueKeys()) { + if (indexesForUrl.count(url) > 1) { + kWarning() << "Multiple items found with the URL" << url; + foreach (int index, indexesForUrl.values(url)) { + const ItemData* data = m_itemData.at(index); + kWarning() << "index" << index << ":" << data->item; + if (data->parent) { + kWarning() << "parent" << data->parent->item; + } + } + } + } + } + } + + return index; +} + +KFileItem KFileItemModel::rootItem() const +{ + return m_dirLister->rootItem(); +} + +void KFileItemModel::clear() +{ + slotClear(); +} + +void KFileItemModel::setRoles(const QSet& roles) +{ + if (m_roles == roles) { + return; + } + + const QSet changedRoles = (roles - m_roles) + (m_roles - roles); + m_roles = roles; + + if (count() > 0) { + const bool supportedExpanding = m_requestRole[ExpandedParentsCountRole]; + const bool willSupportExpanding = roles.contains("expandedParentsCount"); + if (supportedExpanding && !willSupportExpanding) { + // No expanding is supported anymore. Take care to delete all items that have an expansion level + // that is not 0 (and hence are part of an expanded item). + removeExpandedItems(); + } + } + + m_groups.clear(); + resetRoles(); + + QSetIterator it(roles); + while (it.hasNext()) { + const QByteArray& role = it.next(); + m_requestRole[typeForRole(role)] = true; + } + + if (count() > 0) { + // Update m_data with the changed requested roles + const int maxIndex = count() - 1; + for (int i = 0; i <= maxIndex; ++i) { + m_itemData[i]->values = retrieveData(m_itemData.at(i)->item, m_itemData.at(i)->parent); + } + + emit itemsChanged(KItemRangeList() << KItemRange(0, count()), changedRoles); + } + + // Clear the 'values' of all filtered items. They will be re-populated with the + // correct roles the next time 'values' will be accessed via data(int). + QHash::iterator filteredIt = m_filteredItems.begin(); + const QHash::iterator filteredEnd = m_filteredItems.end(); + while (filteredIt != filteredEnd) { + (*filteredIt)->values.clear(); + ++filteredIt; + } +} + +QSet KFileItemModel::roles() const +{ + return m_roles; +} + +bool KFileItemModel::setExpanded(int index, bool expanded) +{ + if (!isExpandable(index) || isExpanded(index) == expanded) { + return false; + } + + QHash values; + values.insert(sharedValue("isExpanded"), expanded); + if (!setData(index, values)) { + return false; + } + + const KFileItem item = m_itemData.at(index)->item; + const KUrl url = item.url(); + const KUrl targetUrl = item.targetUrl(); + if (expanded) { + m_expandedDirs.insert(targetUrl, url); + m_dirLister->openUrl(url, KDirLister::Keep); + + const KUrl::List previouslyExpandedChildren = m_itemData.at(index)->values.value("previouslyExpandedChildren").value(); + foreach (const KUrl& url, previouslyExpandedChildren) { + m_urlsToExpand.insert(url); + } + } else { + // Note that there might be (indirect) children of the folder which is to be collapsed in + // m_pendingItemsToInsert. To prevent that they will be inserted into the model later, + // possibly without a parent, which might result in a crash, we insert all pending items + // right now. All new items which would be without a parent will then be removed. + dispatchPendingItemsToInsert(); + + // Check if the index of the collapsed folder has changed. If that is the case, then items + // were inserted before the collapsed folder, and its index needs to be updated. + if (m_itemData.at(index)->item != item) { + index = this->index(item); + } + + m_expandedDirs.remove(targetUrl); + m_dirLister->stop(url); + + const int parentLevel = expandedParentsCount(index); + const int itemCount = m_itemData.count(); + const int firstChildIndex = index + 1; + + KUrl::List expandedChildren; + + int childIndex = firstChildIndex; + while (childIndex < itemCount && expandedParentsCount(childIndex) > parentLevel) { + ItemData* itemData = m_itemData.at(childIndex); + if (itemData->values.value("isExpanded").toBool()) { + const KUrl targetUrl = itemData->item.targetUrl(); + const KUrl url = itemData->item.url(); + m_expandedDirs.remove(targetUrl); + m_dirLister->stop(url); // TODO: try to unit-test this, see https://bugs.kde.org/show_bug.cgi?id=332102#c11 + expandedChildren.append(targetUrl); + } + ++childIndex; + } + const int childrenCount = childIndex - firstChildIndex; + + removeFilteredChildren(KItemRangeList() << KItemRange(index, 1 + childrenCount)); + removeItems(KItemRangeList() << KItemRange(firstChildIndex, childrenCount), DeleteItemData); + + m_itemData.at(index)->values.insert("previouslyExpandedChildren", expandedChildren); + } + + return true; +} + +bool KFileItemModel::isExpanded(int index) const +{ + if (index >= 0 && index < count()) { + return m_itemData.at(index)->values.value("isExpanded").toBool(); + } + return false; +} + +bool KFileItemModel::isExpandable(int index) const +{ + if (index >= 0 && index < count()) { + // Call data (instead of accessing m_itemData directly) + // to ensure that the value is initialized. + return data(index).value("isExpandable").toBool(); + } + return false; +} + +int KFileItemModel::expandedParentsCount(int index) const +{ + if (index >= 0 && index < count()) { + return expandedParentsCount(m_itemData.at(index)); + } + return 0; +} + +QSet KFileItemModel::expandedDirectories() const +{ + return m_expandedDirs.values().toSet(); +} + +void KFileItemModel::restoreExpandedDirectories(const QSet& urls) +{ + m_urlsToExpand = urls; +} + +void KFileItemModel::expandParentDirectories(const KUrl& url) +{ + const int pos = m_dirLister->url().path().length(); + + // Assure that each sub-path of the URL that should be + // expanded is added to m_urlsToExpand. KDirLister + // does not care whether the parent-URL has already been + // expanded. + KUrl urlToExpand = m_dirLister->url(); + const QStringList subDirs = url.path().mid(pos).split(QDir::separator()); + for (int i = 0; i < subDirs.count() - 1; ++i) { + urlToExpand.addPath(subDirs.at(i)); + m_urlsToExpand.insert(urlToExpand); + } + + // KDirLister::open() must called at least once to trigger an initial + // loading. The pending URLs that must be restored are handled + // in slotCompleted(). + QSetIterator it2(m_urlsToExpand); + while (it2.hasNext()) { + const int idx = index(it2.next()); + if (idx >= 0 && !isExpanded(idx)) { + setExpanded(idx, true); + break; + } + } +} + +void KFileItemModel::setNameFilter(const QString& nameFilter) +{ + if (m_filter.pattern() != nameFilter) { + dispatchPendingItemsToInsert(); + m_filter.setPattern(nameFilter); + applyFilters(); + } +} + +QString KFileItemModel::nameFilter() const +{ + return m_filter.pattern(); +} + +void KFileItemModel::setMimeTypeFilters(const QStringList& filters) +{ + if (m_filter.mimeTypes() != filters) { + dispatchPendingItemsToInsert(); + m_filter.setMimeTypes(filters); + applyFilters(); + } +} + +QStringList KFileItemModel::mimeTypeFilters() const +{ + return m_filter.mimeTypes(); +} + + +void KFileItemModel::applyFilters() +{ + // Check which shown items from m_itemData must get + // hidden and hence moved to m_filteredItems. + QVector newFilteredIndexes; + + const int itemCount = m_itemData.count(); + for (int index = 0; index < itemCount; ++index) { + ItemData* itemData = m_itemData.at(index); + + // Only filter non-expanded items as child items may never + // exist without a parent item + if (!itemData->values.value("isExpanded").toBool()) { + const KFileItem item = itemData->item; + if (!m_filter.matches(item)) { + newFilteredIndexes.append(index); + m_filteredItems.insert(item, itemData); + } + } + } + + const KItemRangeList removedRanges = KItemRangeList::fromSortedContainer(newFilteredIndexes); + removeItems(removedRanges, KeepItemData); + + // Check which hidden items from m_filteredItems should + // get visible again and hence removed from m_filteredItems. + QList newVisibleItems; + + QHash::iterator it = m_filteredItems.begin(); + while (it != m_filteredItems.end()) { + if (m_filter.matches(it.key())) { + newVisibleItems.append(it.value()); + it = m_filteredItems.erase(it); + } else { + ++it; + } + } + + insertItems(newVisibleItems); +} + +void KFileItemModel::removeFilteredChildren(const KItemRangeList& itemRanges) +{ + if (m_filteredItems.isEmpty() || !m_requestRole[ExpandedParentsCountRole]) { + // There are either no filtered items, or it is not possible to expand + // folders -> there cannot be any filtered children. + return; + } + + QSet parents; + foreach (const KItemRange& range, itemRanges) { + for (int index = range.index; index < range.index + range.count; ++index) { + parents.insert(m_itemData.at(index)); + } + } + + QHash::iterator it = m_filteredItems.begin(); + while (it != m_filteredItems.end()) { + if (parents.contains(it.value()->parent)) { + delete it.value(); + it = m_filteredItems.erase(it); + } else { + ++it; + } + } +} + +QList KFileItemModel::rolesInformation() +{ + static QList rolesInfo; + if (rolesInfo.isEmpty()) { + int count = 0; + const RoleInfoMap* map = rolesInfoMap(count); + for (int i = 0; i < count; ++i) { + if (map[i].roleType != NoRole) { + RoleInfo info; + info.role = map[i].role; + info.translation = i18nc(map[i].roleTranslationContext, map[i].roleTranslation); + info.group = QString(); + rolesInfo.append(info); + } + } + } + + return rolesInfo; +} + +void KFileItemModel::onGroupedSortingChanged(bool current) +{ + Q_UNUSED(current); + m_groups.clear(); +} + +void KFileItemModel::onSortRoleChanged(const QByteArray& current, const QByteArray& previous) +{ + Q_UNUSED(previous); + m_sortRole = typeForRole(current); + + if (!m_requestRole[m_sortRole]) { + QSet newRoles = m_roles; + newRoles << current; + setRoles(newRoles); + } + + resortAllItems(); +} + +void KFileItemModel::onSortOrderChanged(Qt::SortOrder current, Qt::SortOrder previous) +{ + Q_UNUSED(current); + Q_UNUSED(previous); + resortAllItems(); +} + +void KFileItemModel::resortAllItems() +{ + m_resortAllItemsTimer->stop(); + + const int itemCount = count(); + if (itemCount <= 0) { + return; + } + +#ifdef KFILEITEMMODEL_DEBUG + QElapsedTimer timer; + timer.start(); + kDebug() << "==========================================================="; + kDebug() << "Resorting" << itemCount << "items"; +#endif + + // Remember the order of the current URLs so + // that it can be determined which indexes have + // been moved because of the resorting. + QList oldUrls; + oldUrls.reserve(itemCount); + foreach (const ItemData* itemData, m_itemData) { + oldUrls.append(itemData->item.url()); + } + + m_items.clear(); + m_items.reserve(itemCount); + + // Resort the items + sort(m_itemData.begin(), m_itemData.end()); + for (int i = 0; i < itemCount; ++i) { + m_items.insert(m_itemData.at(i)->item.url(), i); + } + + // Determine the first index that has been moved. + int firstMovedIndex = 0; + while (firstMovedIndex < itemCount + && firstMovedIndex == m_items.value(oldUrls.at(firstMovedIndex))) { + ++firstMovedIndex; + } + + const bool itemsHaveMoved = firstMovedIndex < itemCount; + if (itemsHaveMoved) { + m_groups.clear(); + + int lastMovedIndex = itemCount - 1; + while (lastMovedIndex > firstMovedIndex + && lastMovedIndex == m_items.value(oldUrls.at(lastMovedIndex))) { + --lastMovedIndex; + } + + Q_ASSERT(firstMovedIndex <= lastMovedIndex); + + // Create a list movedToIndexes, which has the property that + // movedToIndexes[i] is the new index of the item with the old index + // firstMovedIndex + i. + const int movedItemsCount = lastMovedIndex - firstMovedIndex + 1; + QList movedToIndexes; + movedToIndexes.reserve(movedItemsCount); + for (int i = firstMovedIndex; i <= lastMovedIndex; ++i) { + const int newIndex = m_items.value(oldUrls.at(i)); + movedToIndexes.append(newIndex); + } + + emit itemsMoved(KItemRange(firstMovedIndex, movedItemsCount), movedToIndexes); + } else if (groupedSorting()) { + // The groups might have changed even if the order of the items has not. + const QList > oldGroups = m_groups; + m_groups.clear(); + if (groups() != oldGroups) { + emit groupsChanged(); + } + } + +#ifdef KFILEITEMMODEL_DEBUG + kDebug() << "[TIME] Resorting of" << itemCount << "items:" << timer.elapsed(); +#endif +} + +void KFileItemModel::slotCompleted() +{ + dispatchPendingItemsToInsert(); + + if (!m_urlsToExpand.isEmpty()) { + // Try to find a URL that can be expanded. + // Note that the parent folder must be expanded before any of its subfolders become visible. + // Therefore, some URLs in m_restoredExpandedUrls might not be visible yet + // -> we expand the first visible URL we find in m_restoredExpandedUrls. + foreach (const KUrl& url, m_urlsToExpand) { + const int indexForUrl = index(url); + if (indexForUrl >= 0) { + m_urlsToExpand.remove(url); + if (setExpanded(indexForUrl, true)) { + // The dir lister has been triggered. This slot will be called + // again after the directory has been expanded. + return; + } + } + } + + // None of the URLs in m_restoredExpandedUrls could be found in the model. This can happen + // if these URLs have been deleted in the meantime. + m_urlsToExpand.clear(); + } + + emit directoryLoadingCompleted(); +} + +void KFileItemModel::slotCanceled() +{ + m_maximumUpdateIntervalTimer->stop(); + dispatchPendingItemsToInsert(); + + emit directoryLoadingCanceled(); +} + +void KFileItemModel::slotItemsAdded(const KUrl& directoryUrl, const KFileItemList& items) +{ + Q_ASSERT(!items.isEmpty()); + + KUrl parentUrl; + if (m_expandedDirs.contains(directoryUrl)) { + parentUrl = m_expandedDirs.value(directoryUrl); + } else { + parentUrl = directoryUrl; + parentUrl.adjustPath(KUrl::RemoveTrailingSlash); + } + + if (m_requestRole[ExpandedParentsCountRole]) { + // If the expanding of items is enabled, the call + // dirLister->openUrl(url, KDirLister::Keep) in KFileItemModel::setExpanded() + // might result in emitting the same items twice due to the Keep-parameter. + // This case happens if an item gets expanded, collapsed and expanded again + // before the items could be loaded for the first expansion. + if (index(items.first().url()) >= 0) { + // The items are already part of the model. + return; + } + + if (directoryUrl != directory()) { + // To be able to compare whether the new items may be inserted as children + // of a parent item the pending items must be added to the model first. + dispatchPendingItemsToInsert(); + } + + // KDirLister keeps the children of items that got expanded once even if + // they got collapsed again with KFileItemModel::setExpanded(false). So it must be + // checked whether the parent for new items is still expanded. + const int parentIndex = index(parentUrl); + if (parentIndex >= 0 && !m_itemData[parentIndex]->values.value("isExpanded").toBool()) { + // The parent is not expanded. + return; + } + } + + QList itemDataList = createItemDataList(parentUrl, items); + + if (!m_filter.hasSetFilters()) { + m_pendingItemsToInsert.append(itemDataList); + } else { + // The name or type filter is active. Hide filtered items + // before inserting them into the model and remember + // the filtered items in m_filteredItems. + foreach (ItemData* itemData, itemDataList) { + if (m_filter.matches(itemData->item)) { + m_pendingItemsToInsert.append(itemData); + } else { + m_filteredItems.insert(itemData->item, itemData); + } + } + } + + if (useMaximumUpdateInterval() && !m_maximumUpdateIntervalTimer->isActive()) { + // Assure that items get dispatched if no completed() or canceled() signal is + // emitted during the maximum update interval. + m_maximumUpdateIntervalTimer->start(); + } +} + +void KFileItemModel::slotItemsDeleted(const KFileItemList& items) +{ + dispatchPendingItemsToInsert(); + + QVector indexesToRemove; + indexesToRemove.reserve(items.count()); + + foreach (const KFileItem& item, items) { + const int indexForItem = index(item); + if (indexForItem >= 0) { + indexesToRemove.append(indexForItem); + } else { + // Probably the item has been filtered. + QHash::iterator it = m_filteredItems.find(item); + if (it != m_filteredItems.end()) { + delete it.value(); + m_filteredItems.erase(it); + } + } + } + + std::sort(indexesToRemove.begin(), indexesToRemove.end()); + + if (m_requestRole[ExpandedParentsCountRole] && !m_expandedDirs.isEmpty()) { + // Assure that removing a parent item also results in removing all children + QVector indexesToRemoveWithChildren; + indexesToRemoveWithChildren.reserve(m_itemData.count()); + + const int itemCount = m_itemData.count(); + foreach (int index, indexesToRemove) { + indexesToRemoveWithChildren.append(index); + + const int parentLevel = expandedParentsCount(index); + int childIndex = index + 1; + while (childIndex < itemCount && expandedParentsCount(childIndex) > parentLevel) { + indexesToRemoveWithChildren.append(childIndex); + ++childIndex; + } + } + + indexesToRemove = indexesToRemoveWithChildren; + } + + const KItemRangeList itemRanges = KItemRangeList::fromSortedContainer(indexesToRemove); + removeFilteredChildren(itemRanges); + removeItems(itemRanges, DeleteItemData); +} + +void KFileItemModel::slotRefreshItems(const QList >& items) +{ + Q_ASSERT(!items.isEmpty()); +#ifdef KFILEITEMMODEL_DEBUG + kDebug() << "Refreshing" << items.count() << "items"; +#endif + + // Get the indexes of all items that have been refreshed + QList indexes; + indexes.reserve(items.count()); + + QSet changedRoles; + + QListIterator > it(items); + while (it.hasNext()) { + const QPair& itemPair = it.next(); + const KFileItem& oldItem = itemPair.first; + const KFileItem& newItem = itemPair.second; + const int indexForItem = index(oldItem); + if (indexForItem >= 0) { + m_itemData[indexForItem]->item = newItem; + + // Keep old values as long as possible if they could not retrieved synchronously yet. + // The update of the values will be done asynchronously by KFileItemModelRolesUpdater. + QHashIterator it(retrieveData(newItem, m_itemData.at(indexForItem)->parent)); + QHash& values = m_itemData[indexForItem]->values; + while (it.hasNext()) { + it.next(); + const QByteArray& role = it.key(); + if (values.value(role) != it.value()) { + values.insert(role, it.value()); + changedRoles.insert(role); + } + } + + m_items.remove(oldItem.url()); + m_items.insert(newItem.url(), indexForItem); + indexes.append(indexForItem); + } else { + // Check if 'oldItem' is one of the filtered items. + QHash::iterator it = m_filteredItems.find(oldItem); + if (it != m_filteredItems.end()) { + ItemData* itemData = it.value(); + itemData->item = newItem; + + // The data stored in 'values' might have changed. Therefore, we clear + // 'values' and re-populate it the next time it is requested via data(int). + itemData->values.clear(); + + m_filteredItems.erase(it); + m_filteredItems.insert(newItem, itemData); + } + } + } + + // If the changed items have been created recently, they might not be in m_items yet. + // In that case, the list 'indexes' might be empty. + if (indexes.isEmpty()) { + return; + } + + // Extract the item-ranges out of the changed indexes + qSort(indexes); + const KItemRangeList itemRangeList = KItemRangeList::fromSortedContainer(indexes); + emitItemsChangedAndTriggerResorting(itemRangeList, changedRoles); +} + +void KFileItemModel::slotClear() +{ +#ifdef KFILEITEMMODEL_DEBUG + kDebug() << "Clearing all items"; +#endif + + qDeleteAll(m_filteredItems.values()); + m_filteredItems.clear(); + m_groups.clear(); + + m_maximumUpdateIntervalTimer->stop(); + m_resortAllItemsTimer->stop(); + + qDeleteAll(m_pendingItemsToInsert); + m_pendingItemsToInsert.clear(); + + const int removedCount = m_itemData.count(); + if (removedCount > 0) { + qDeleteAll(m_itemData); + m_itemData.clear(); + m_items.clear(); + emit itemsRemoved(KItemRangeList() << KItemRange(0, removedCount)); + } + + m_expandedDirs.clear(); +} + +void KFileItemModel::slotNaturalSortingChanged() +{ + m_naturalSorting = KGlobalSettings::naturalSorting(); + resortAllItems(); +} + +void KFileItemModel::dispatchPendingItemsToInsert() +{ + if (!m_pendingItemsToInsert.isEmpty()) { + insertItems(m_pendingItemsToInsert); + m_pendingItemsToInsert.clear(); + } +} + +void KFileItemModel::insertItems(QList& newItems) +{ + if (newItems.isEmpty()) { + return; + } + +#ifdef KFILEITEMMODEL_DEBUG + QElapsedTimer timer; + timer.start(); + kDebug() << "==========================================================="; + kDebug() << "Inserting" << newItems.count() << "items"; +#endif + + m_groups.clear(); + prepareItemsForSorting(newItems); + + sort(newItems.begin(), newItems.end()); + +#ifdef KFILEITEMMODEL_DEBUG + kDebug() << "[TIME] Sorting:" << timer.elapsed(); +#endif + + KItemRangeList itemRanges; + const int existingItemCount = m_itemData.count(); + const int newItemCount = newItems.count(); + const int totalItemCount = existingItemCount + newItemCount; + + if (existingItemCount == 0) { + // Optimization for the common special case that there are no + // items in the model yet. Happens, e.g., when entering a folder. + m_itemData = newItems; + itemRanges << KItemRange(0, newItemCount); + } else { + m_itemData.reserve(totalItemCount); + for (int i = existingItemCount; i < totalItemCount; ++i) { + m_itemData.append(0); + } + + // We build the new list m_itemData in reverse order to minimize + // the number of moves and guarantee O(N) complexity. + int targetIndex = totalItemCount - 1; + int sourceIndexExistingItems = existingItemCount - 1; + int sourceIndexNewItems = newItemCount - 1; + + int rangeCount = 0; + + while (sourceIndexNewItems >= 0) { + ItemData* newItem = newItems.at(sourceIndexNewItems); + if (sourceIndexExistingItems >= 0 && lessThan(newItem, m_itemData.at(sourceIndexExistingItems))) { + // Move an existing item to its new position. If any new items + // are behind it, push the item range to itemRanges. + if (rangeCount > 0) { + itemRanges << KItemRange(sourceIndexExistingItems + 1, rangeCount); + rangeCount = 0; + } + + m_itemData[targetIndex] = m_itemData.at(sourceIndexExistingItems); + --sourceIndexExistingItems; + } else { + // Insert a new item into the list. + ++rangeCount; + m_itemData[targetIndex] = newItem; + --sourceIndexNewItems; + } + --targetIndex; + } + + // Push the final item range to itemRanges. + if (rangeCount > 0) { + itemRanges << KItemRange(sourceIndexExistingItems + 1, rangeCount); + } + + // Note that itemRanges is still sorted in reverse order. + std::reverse(itemRanges.begin(), itemRanges.end()); + } + + // The indexes in m_items are not correct anymore. Therefore, we clear m_items. + // It will be re-populated with the updated indices if index(const KUrl&) is called. + m_items.clear(); + + emit itemsInserted(itemRanges); + +#ifdef KFILEITEMMODEL_DEBUG + kDebug() << "[TIME] Inserting of" << newItems.count() << "items:" << timer.elapsed(); +#endif +} + +void KFileItemModel::removeItems(const KItemRangeList& itemRanges, RemoveItemsBehavior behavior) +{ + if (itemRanges.isEmpty()) { + return; + } + + m_groups.clear(); + + // Step 1: Remove the items from m_itemData, and free the ItemData. + int removedItemsCount = 0; + foreach (const KItemRange& range, itemRanges) { + removedItemsCount += range.count; + + for (int index = range.index; index < range.index + range.count; ++index) { + if (behavior == DeleteItemData) { + delete m_itemData.at(index); + } + + m_itemData[index] = 0; + } + } + + // Step 2: Remove the ItemData pointers from the list m_itemData. + int target = itemRanges.at(0).index; + int source = itemRanges.at(0).index + itemRanges.at(0).count; + int nextRange = 1; + + const int oldItemDataCount = m_itemData.count(); + while (source < oldItemDataCount) { + m_itemData[target] = m_itemData[source]; + ++target; + ++source; + + if (nextRange < itemRanges.count() && source == itemRanges.at(nextRange).index) { + // Skip the items in the next removed range. + source += itemRanges.at(nextRange).count; + ++nextRange; + } + } + + m_itemData.erase(m_itemData.end() - removedItemsCount, m_itemData.end()); + + // The indexes in m_items are not correct anymore. Therefore, we clear m_items. + // It will be re-populated with the updated indices if index(const KUrl&) is called. + m_items.clear(); + + emit itemsRemoved(itemRanges); +} + +QList KFileItemModel::createItemDataList(const KUrl& parentUrl, const KFileItemList& items) const +{ + if (m_sortRole == TypeRole) { + // Try to resolve the MIME-types synchronously to prevent a reordering of + // the items when sorting by type (per default MIME-types are resolved + // asynchronously by KFileItemModelRolesUpdater). + determineMimeTypes(items, 200); + } + + const int parentIndex = index(parentUrl); + ItemData* parentItem = parentIndex < 0 ? 0 : m_itemData.at(parentIndex); + + QList itemDataList; + itemDataList.reserve(items.count()); + + foreach (const KFileItem& item, items) { + ItemData* itemData = new ItemData(); + itemData->item = item; + itemData->parent = parentItem; + itemDataList.append(itemData); + } + + return itemDataList; +} + +void KFileItemModel::prepareItemsForSorting(QList& itemDataList) +{ + switch (m_sortRole) { + case PermissionsRole: + case OwnerRole: + case GroupRole: + case DestinationRole: + case PathRole: + // These roles can be determined with retrieveData, and they have to be stored + // in the QHash "values" for the sorting. + foreach (ItemData* itemData, itemDataList) { + if (itemData->values.isEmpty()) { + itemData->values = retrieveData(itemData->item, itemData->parent); + } + } + break; + + case TypeRole: + // At least store the data including the file type for items with known MIME type. + foreach (ItemData* itemData, itemDataList) { + if (itemData->values.isEmpty()) { + const KFileItem item = itemData->item; + if (item.isDir() || item.isMimeTypeKnown()) { + itemData->values = retrieveData(itemData->item, itemData->parent); + } + } + } + break; + + default: + // The other roles are either resolved by KFileItemModelRolesUpdater + // (this includes the SizeRole for directories), or they do not need + // to be stored in the QHash "values" for sorting because the data can + // be retrieved directly from the KFileItem (NameRole, SizeRole for files, + // DateRole). + break; + } +} + +int KFileItemModel::expandedParentsCount(const ItemData* data) +{ + // The hash 'values' is only guaranteed to contain the key "expandedParentsCount" + // if the corresponding item is expanded, and it is not a top-level item. + const ItemData* parent = data->parent; + if (parent) { + if (parent->parent) { + Q_ASSERT(parent->values.contains("expandedParentsCount")); + return parent->values.value("expandedParentsCount").toInt() + 1; + } else { + return 1; + } + } else { + return 0; + } +} + +void KFileItemModel::removeExpandedItems() +{ + QVector indexesToRemove; + + const int maxIndex = m_itemData.count() - 1; + for (int i = 0; i <= maxIndex; ++i) { + const ItemData* itemData = m_itemData.at(i); + if (itemData->parent) { + indexesToRemove.append(i); + } + } + + removeItems(KItemRangeList::fromSortedContainer(indexesToRemove), DeleteItemData); + m_expandedDirs.clear(); + + // Also remove all filtered items which have a parent. + QHash::iterator it = m_filteredItems.begin(); + const QHash::iterator end = m_filteredItems.end(); + + while (it != end) { + if (it.value()->parent) { + delete it.value(); + it = m_filteredItems.erase(it); + } else { + ++it; + } + } +} + +void KFileItemModel::emitItemsChangedAndTriggerResorting(const KItemRangeList& itemRanges, const QSet& changedRoles) +{ + emit itemsChanged(itemRanges, changedRoles); + + // Trigger a resorting if necessary. Note that this can happen even if the sort + // role has not changed at all because the file name can be used as a fallback. + if (changedRoles.contains(sortRole()) || changedRoles.contains(roleForType(NameRole))) { + foreach (const KItemRange& range, itemRanges) { + bool needsResorting = false; + + const int first = range.index; + const int last = range.index + range.count - 1; + + // Resorting the model is necessary if + // (a) The first item in the range is "lessThan" its predecessor, + // (b) the successor of the last item is "lessThan" the last item, or + // (c) the internal order of the items in the range is incorrect. + if (first > 0 + && lessThan(m_itemData.at(first), m_itemData.at(first - 1))) { + needsResorting = true; + } else if (last < count() - 1 + && lessThan(m_itemData.at(last + 1), m_itemData.at(last))) { + needsResorting = true; + } else { + for (int index = first; index < last; ++index) { + if (lessThan(m_itemData.at(index + 1), m_itemData.at(index))) { + needsResorting = true; + break; + } + } + } + + if (needsResorting) { + m_resortAllItemsTimer->start(); + return; + } + } + } + + if (groupedSorting() && changedRoles.contains(sortRole())) { + // The position is still correct, but the groups might have changed + // if the changed item is either the first or the last item in a + // group. + // In principle, we could try to find out if the item really is the + // first or last one in its group and then update the groups + // (possibly with a delayed timer to make sure that we don't + // re-calculate the groups very often if items are updated one by + // one), but starting m_resortAllItemsTimer is easier. + m_resortAllItemsTimer->start(); + } +} + +void KFileItemModel::resetRoles() +{ + for (int i = 0; i < RolesCount; ++i) { + m_requestRole[i] = false; + } +} + +KFileItemModel::RoleType KFileItemModel::typeForRole(const QByteArray& role) const +{ + static QHash roles; + if (roles.isEmpty()) { + // Insert user visible roles that can be accessed with + // KFileItemModel::roleInformation() + int count = 0; + const RoleInfoMap* map = rolesInfoMap(count); + for (int i = 0; i < count; ++i) { + roles.insert(map[i].role, map[i].roleType); + } + + // Insert internal roles (take care to synchronize the implementation + // with KFileItemModel::roleForType() in case if a change is done). + roles.insert("isDir", IsDirRole); + roles.insert("isLink", IsLinkRole); + roles.insert("isExpanded", IsExpandedRole); + roles.insert("isExpandable", IsExpandableRole); + roles.insert("expandedParentsCount", ExpandedParentsCountRole); + + Q_ASSERT(roles.count() == RolesCount); + } + + return roles.value(role, NoRole); +} + +QByteArray KFileItemModel::roleForType(RoleType roleType) const +{ + static QHash roles; + if (roles.isEmpty()) { + // Insert user visible roles that can be accessed with + // KFileItemModel::roleInformation() + int count = 0; + const RoleInfoMap* map = rolesInfoMap(count); + for (int i = 0; i < count; ++i) { + roles.insert(map[i].roleType, map[i].role); + } + + // Insert internal roles (take care to synchronize the implementation + // with KFileItemModel::typeForRole() in case if a change is done). + roles.insert(IsDirRole, "isDir"); + roles.insert(IsLinkRole, "isLink"); + roles.insert(IsExpandedRole, "isExpanded"); + roles.insert(IsExpandableRole, "isExpandable"); + roles.insert(ExpandedParentsCountRole, "expandedParentsCount"); + + Q_ASSERT(roles.count() == RolesCount); + }; + + return roles.value(roleType); +} + +QHash KFileItemModel::retrieveData(const KFileItem& item, const ItemData* parent) const +{ + // It is important to insert only roles that are fast to retrieve. E.g. + // KFileItem::iconName() can be very expensive if the MIME-type is unknown + // and hence will be retrieved asynchronously by KFileItemModelRolesUpdater. + QHash data; + data.insert(sharedValue("url"), item.url()); + + const bool isDir = item.isDir(); + if (m_requestRole[IsDirRole] && isDir) { + data.insert(sharedValue("isDir"), true); + } + + if (m_requestRole[IsLinkRole] && item.isLink()) { + data.insert(sharedValue("isLink"), true); + } + + if (m_requestRole[NameRole]) { + data.insert(sharedValue("text"), item.text()); + } + + if (m_requestRole[SizeRole] && !isDir) { + data.insert(sharedValue("size"), item.size()); + } + + if (m_requestRole[DateRole]) { + // Don't use KFileItem::timeString() as this is too expensive when + // having several thousands of items. Instead the formatting of the + // date-time will be done on-demand by the view when the date will be shown. + const KDateTime dateTime = item.time(KFileItem::ModificationTime); + data.insert(sharedValue("date"), dateTime.dateTime()); + } + + if (m_requestRole[PermissionsRole]) { + data.insert(sharedValue("permissions"), item.permissionsString()); + } + + if (m_requestRole[OwnerRole]) { + data.insert(sharedValue("owner"), item.user()); + } + + if (m_requestRole[GroupRole]) { + data.insert(sharedValue("group"), item.group()); + } + + if (m_requestRole[DestinationRole]) { + QString destination = item.linkDest(); + if (destination.isEmpty()) { + destination = QLatin1String("-"); + } + data.insert(sharedValue("destination"), destination); + } + + if (m_requestRole[PathRole]) { + QString path; + if (item.url().protocol() == QLatin1String("trash")) { + path = item.entry().stringValue(KIO::UDSEntry::UDS_EXTRA); + } else { + // For performance reasons cache the home-path in a static QString + // (see QDir::homePath() for more details) + static QString homePath; + if (homePath.isEmpty()) { + homePath = QDir::homePath(); + } + + path = item.localPath(); + if (path.startsWith(homePath)) { + path.replace(0, homePath.length(), QLatin1Char('~')); + } + } + + const int index = path.lastIndexOf(item.text()); + path = path.mid(0, index - 1); + data.insert(sharedValue("path"), path); + } + + if (m_requestRole[IsExpandableRole] && isDir) { + data.insert(sharedValue("isExpandable"), true); + } + + if (m_requestRole[ExpandedParentsCountRole]) { + if (parent) { + const int level = expandedParentsCount(parent) + 1; + data.insert(sharedValue("expandedParentsCount"), level); + } + } + + if (item.isMimeTypeKnown()) { + data.insert(sharedValue("iconName"), item.iconName()); + + if (m_requestRole[TypeRole]) { + data.insert(sharedValue("type"), item.mimeComment()); + } + } else if (m_requestRole[TypeRole] && isDir) { + static const QString folderMimeType = item.mimeComment(); + data.insert(sharedValue("type"), folderMimeType); + } + + return data; +} + +bool KFileItemModel::lessThan(const ItemData* a, const ItemData* b) const +{ + int result = 0; + + if (a->parent != b->parent) { + const int expansionLevelA = expandedParentsCount(a); + const int expansionLevelB = expandedParentsCount(b); + + // If b has a higher expansion level than a, check if a is a parent + // of b, and make sure that both expansion levels are equal otherwise. + for (int i = expansionLevelB; i > expansionLevelA; --i) { + if (b->parent == a) { + return true; + } + b = b->parent; + } + + // If a has a higher expansion level than a, check if b is a parent + // of a, and make sure that both expansion levels are equal otherwise. + for (int i = expansionLevelA; i > expansionLevelB; --i) { + if (a->parent == b) { + return false; + } + a = a->parent; + } + + Q_ASSERT(expandedParentsCount(a) == expandedParentsCount(b)); + + // Compare the last parents of a and b which are different. + while (a->parent != b->parent) { + a = a->parent; + b = b->parent; + } + } + + if (m_sortDirsFirst || m_sortRole == SizeRole) { + const bool isDirA = a->item.isDir(); + const bool isDirB = b->item.isDir(); + if (isDirA && !isDirB) { + return true; + } else if (!isDirA && isDirB) { + return false; + } + } + + result = sortRoleCompare(a, b); + + return (sortOrder() == Qt::AscendingOrder) ? result < 0 : result > 0; +} + +/** + * Helper class for KFileItemModel::sort(). + */ +class KFileItemModelLessThan +{ +public: + KFileItemModelLessThan(const KFileItemModel* model) : + m_model(model) + { + } + + bool operator()(const KFileItemModel::ItemData* a, const KFileItemModel::ItemData* b) const + { + return m_model->lessThan(a, b); + } + +private: + const KFileItemModel* m_model; +}; + +void KFileItemModel::sort(QList::iterator begin, + QList::iterator end) const +{ + KFileItemModelLessThan lessThan(this); + + // Use only one thread to prevent problems caused by non-reentrant + // comparison functions, see https://bugs.kde.org/show_bug.cgi?id=312679 + mergeSort(begin, end, lessThan); +} + +int KFileItemModel::sortRoleCompare(const ItemData* a, const ItemData* b) const +{ + const KFileItem& itemA = a->item; + const KFileItem& itemB = b->item; + + int result = 0; + + switch (m_sortRole) { + case NameRole: + // The name role is handled as default fallback after the switch + break; + + case SizeRole: { + if (itemA.isDir()) { + // See "if (m_sortFoldersFirst || m_sortRole == SizeRole)" in KFileItemModel::lessThan(): + Q_ASSERT(itemB.isDir()); + + const QVariant valueA = a->values.value("size"); + const QVariant valueB = b->values.value("size"); + if (valueA.isNull() && valueB.isNull()) { + result = 0; + } else if (valueA.isNull()) { + result = -1; + } else if (valueB.isNull()) { + result = +1; + } else { + result = valueA.toInt() - valueB.toInt(); + } + } else { + // See "if (m_sortFoldersFirst || m_sortRole == SizeRole)" in KFileItemModel::lessThan(): + Q_ASSERT(!itemB.isDir()); + const KIO::filesize_t sizeA = itemA.size(); + const KIO::filesize_t sizeB = itemB.size(); + if (sizeA > sizeB) { + result = +1; + } else if (sizeA < sizeB) { + result = -1; + } else { + result = 0; + } + } + break; + } + + case DateRole: { + const KDateTime dateTimeA = itemA.time(KFileItem::ModificationTime); + const KDateTime dateTimeB = itemB.time(KFileItem::ModificationTime); + if (dateTimeA < dateTimeB) { + result = -1; + } else if (dateTimeA > dateTimeB) { + result = +1; + } + break; + } + + default: { + const QByteArray role = roleForType(m_sortRole); + result = QString::compare(a->values.value(role).toString(), + b->values.value(role).toString()); + break; + } + + } + + if (result != 0) { + // The current sort role was sufficient to define an order + return result; + } + + // Fallback #1: Compare the text of the items + result = stringCompare(itemA.text(), itemB.text()); + if (result != 0) { + return result; + } + + // Fallback #2: KFileItem::text() may not be unique in case UDS_DISPLAY_NAME is used + result = stringCompare(itemA.name(m_caseSensitivity == Qt::CaseInsensitive), + itemB.name(m_caseSensitivity == Qt::CaseInsensitive)); + if (result != 0) { + return result; + } + + // Fallback #3: It must be assured that the sort order is always unique even if two values have been + // equal. In this case a comparison of the URL is done which is unique in all cases + // within KDirLister. + return QString::compare(itemA.url().url(), itemB.url().url(), Qt::CaseSensitive); +} + +int KFileItemModel::stringCompare(const QString& a, const QString& b) const +{ + // Taken from KDirSortFilterProxyModel (kdelibs/kfile/kdirsortfilterproxymodel.*) + // Copyright (C) 2006 by Peter Penz + // Copyright (C) 2006 by Dominic Battre + // Copyright (C) 2006 by Martin Pool + + if (m_caseSensitivity == Qt::CaseInsensitive) { + const int result = m_naturalSorting ? KStringHandler::naturalCompare(a, b, Qt::CaseInsensitive) + : QString::compare(a, b, Qt::CaseInsensitive); + if (result != 0) { + // Only return the result, if the strings are not equal. If they are equal by a case insensitive + // comparison, still a deterministic sort order is required. A case sensitive + // comparison is done as fallback. + return result; + } + } + + return m_naturalSorting ? KStringHandler::naturalCompare(a, b, Qt::CaseSensitive) + : QString::compare(a, b, Qt::CaseSensitive); +} + +bool KFileItemModel::useMaximumUpdateInterval() const +{ + return !m_dirLister->url().isLocalFile(); +} + +static bool localeAwareLessThan(const QChar& c1, const QChar& c2) +{ + return QString::localeAwareCompare(c1, c2) < 0; +} + +QList > KFileItemModel::nameRoleGroups() const +{ + Q_ASSERT(!m_itemData.isEmpty()); + + const int maxIndex = count() - 1; + QList > groups; + + QString groupValue; + QChar firstChar; + for (int i = 0; i <= maxIndex; ++i) { + if (isChildItem(i)) { + continue; + } + + const QString name = m_itemData.at(i)->item.text(); + + // Use the first character of the name as group indication + QChar newFirstChar = name.at(0).toUpper(); + if (newFirstChar == QLatin1Char('~') && name.length() > 1) { + newFirstChar = name.at(1).toUpper(); + } + + if (firstChar != newFirstChar) { + QString newGroupValue; + if (newFirstChar.isLetter()) { + // Try to find a matching group in the range 'A' to 'Z'. + static std::vector lettersAtoZ; + if (lettersAtoZ.empty()) { + for (char c = 'A'; c <= 'Z'; ++c) { + lettersAtoZ.push_back(QLatin1Char(c)); + } + } + + std::vector::iterator it = std::lower_bound(lettersAtoZ.begin(), lettersAtoZ.end(), newFirstChar, localeAwareLessThan); + if (it != lettersAtoZ.end()) { + if (localeAwareLessThan(newFirstChar, *it) && it != lettersAtoZ.begin()) { + // newFirstChar belongs to the group preceding *it. + // Example: for an umlaut 'A' in the German locale, *it would be 'B' now. + --it; + } + newGroupValue = *it; + } else { + newGroupValue = newFirstChar; + } + } else if (newFirstChar >= QLatin1Char('0') && newFirstChar <= QLatin1Char('9')) { + // Apply group '0 - 9' for any name that starts with a digit + newGroupValue = i18nc("@title:group Groups that start with a digit", "0 - 9"); + } else { + newGroupValue = i18nc("@title:group", "Others"); + } + + if (newGroupValue != groupValue) { + groupValue = newGroupValue; + groups.append(QPair(i, newGroupValue)); + } + + firstChar = newFirstChar; + } + } + return groups; +} + +QList > KFileItemModel::sizeRoleGroups() const +{ + Q_ASSERT(!m_itemData.isEmpty()); + + const int maxIndex = count() - 1; + QList > groups; + + QString groupValue; + for (int i = 0; i <= maxIndex; ++i) { + if (isChildItem(i)) { + continue; + } + + const KFileItem& item = m_itemData.at(i)->item; + const KIO::filesize_t fileSize = !item.isNull() ? item.size() : ~0U; + QString newGroupValue; + if (!item.isNull() && item.isDir()) { + newGroupValue = i18nc("@title:group Size", "Folders"); + } else if (fileSize < 5 * 1024 * 1024) { + newGroupValue = i18nc("@title:group Size", "Small"); + } else if (fileSize < 10 * 1024 * 1024) { + newGroupValue = i18nc("@title:group Size", "Medium"); + } else { + newGroupValue = i18nc("@title:group Size", "Big"); + } + + if (newGroupValue != groupValue) { + groupValue = newGroupValue; + groups.append(QPair(i, newGroupValue)); + } + } + + return groups; +} + +QList > KFileItemModel::dateRoleGroups() const +{ + Q_ASSERT(!m_itemData.isEmpty()); + + const int maxIndex = count() - 1; + QList > groups; + + const QDate currentDate = KDateTime::currentLocalDateTime().date(); + + QDate previousModifiedDate; + QString groupValue; + for (int i = 0; i <= maxIndex; ++i) { + if (isChildItem(i)) { + continue; + } + + const KDateTime modifiedTime = m_itemData.at(i)->item.time(KFileItem::ModificationTime); + const QDate modifiedDate = modifiedTime.date(); + if (modifiedDate == previousModifiedDate) { + // The current item is in the same group as the previous item + continue; + } + previousModifiedDate = modifiedDate; + + const int daysDistance = modifiedDate.daysTo(currentDate); + + QString newGroupValue; + if (currentDate.year() == modifiedDate.year() && currentDate.month() == modifiedDate.month()) { + switch (daysDistance / 7) { + case 0: + switch (daysDistance) { + case 0: newGroupValue = i18nc("@title:group Date", "Today"); break; + case 1: newGroupValue = i18nc("@title:group Date", "Yesterday"); break; + default: newGroupValue = modifiedTime.toString(i18nc("@title:group The week day name: %A", "%A")); + } + break; + case 1: + newGroupValue = i18nc("@title:group Date", "One Week Ago"); + break; + case 2: + newGroupValue = i18nc("@title:group Date", "Two Weeks Ago"); + break; + case 3: + newGroupValue = i18nc("@title:group Date", "Three Weeks Ago"); + break; + case 4: + case 5: + newGroupValue = i18nc("@title:group Date", "Earlier this Month"); + break; + default: + Q_ASSERT(false); + } + } else { + const QDate lastMonthDate = currentDate.addMonths(-1); + if (lastMonthDate.year() == modifiedDate.year() && lastMonthDate.month() == modifiedDate.month()) { + if (daysDistance == 1) { + newGroupValue = modifiedTime.toString(i18nc("@title:group Date: %B is full month name in current locale, and %Y is full year number", "Yesterday (%B, %Y)")); + } else if (daysDistance <= 7) { + newGroupValue = modifiedTime.toString(i18nc("@title:group The week day name: %A, %B is full month name in current locale, and %Y is full year number", "%A (%B, %Y)")); + } else if (daysDistance <= 7 * 2) { + newGroupValue = modifiedTime.toString(i18nc("@title:group Date: %B is full month name in current locale, and %Y is full year number", "One Week Ago (%B, %Y)")); + } else if (daysDistance <= 7 * 3) { + newGroupValue = modifiedTime.toString(i18nc("@title:group Date: %B is full month name in current locale, and %Y is full year number", "Two Weeks Ago (%B, %Y)")); + } else if (daysDistance <= 7 * 4) { + newGroupValue = modifiedTime.toString(i18nc("@title:group Date: %B is full month name in current locale, and %Y is full year number", "Three Weeks Ago (%B, %Y)")); + } else { + newGroupValue = modifiedTime.toString(i18nc("@title:group Date: %B is full month name in current locale, and %Y is full year number", "Earlier on %B, %Y")); + } + } else { + newGroupValue = modifiedTime.toString(i18nc("@title:group The month and year: %B is full month name in current locale, and %Y is full year number", "%B, %Y")); + } + } + + if (newGroupValue != groupValue) { + groupValue = newGroupValue; + groups.append(QPair(i, newGroupValue)); + } + } + + return groups; +} + +QList > KFileItemModel::permissionRoleGroups() const +{ + Q_ASSERT(!m_itemData.isEmpty()); + + const int maxIndex = count() - 1; + QList > groups; + + QString permissionsString; + QString groupValue; + for (int i = 0; i <= maxIndex; ++i) { + if (isChildItem(i)) { + continue; + } + + const ItemData* itemData = m_itemData.at(i); + const QString newPermissionsString = itemData->values.value("permissions").toString(); + if (newPermissionsString == permissionsString) { + continue; + } + permissionsString = newPermissionsString; + + const QFileInfo info(itemData->item.url().pathOrUrl()); + + // Set user string + QString user; + if (info.permission(QFile::ReadUser)) { + user = i18nc("@item:intext Access permission, concatenated", "Read, "); + } + if (info.permission(QFile::WriteUser)) { + user += i18nc("@item:intext Access permission, concatenated", "Write, "); + } + if (info.permission(QFile::ExeUser)) { + user += i18nc("@item:intext Access permission, concatenated", "Execute, "); + } + user = user.isEmpty() ? i18nc("@item:intext Access permission, concatenated", "Forbidden") : user.mid(0, user.count() - 2); + + // Set group string + QString group; + if (info.permission(QFile::ReadGroup)) { + group = i18nc("@item:intext Access permission, concatenated", "Read, "); + } + if (info.permission(QFile::WriteGroup)) { + group += i18nc("@item:intext Access permission, concatenated", "Write, "); + } + if (info.permission(QFile::ExeGroup)) { + group += i18nc("@item:intext Access permission, concatenated", "Execute, "); + } + group = group.isEmpty() ? i18nc("@item:intext Access permission, concatenated", "Forbidden") : group.mid(0, group.count() - 2); + + // Set others string + QString others; + if (info.permission(QFile::ReadOther)) { + others = i18nc("@item:intext Access permission, concatenated", "Read, "); + } + if (info.permission(QFile::WriteOther)) { + others += i18nc("@item:intext Access permission, concatenated", "Write, "); + } + if (info.permission(QFile::ExeOther)) { + others += i18nc("@item:intext Access permission, concatenated", "Execute, "); + } + others = others.isEmpty() ? i18nc("@item:intext Access permission, concatenated", "Forbidden") : others.mid(0, others.count() - 2); + + const QString newGroupValue = i18nc("@title:group Files and folders by permissions", "User: %1 | Group: %2 | Others: %3", user, group, others); + if (newGroupValue != groupValue) { + groupValue = newGroupValue; + groups.append(QPair(i, newGroupValue)); + } + } + + return groups; +} + +QList > KFileItemModel::genericStringRoleGroups(const QByteArray& role) const +{ + Q_ASSERT(!m_itemData.isEmpty()); + + const int maxIndex = count() - 1; + QList > groups; + + bool isFirstGroupValue = true; + QString groupValue; + for (int i = 0; i <= maxIndex; ++i) { + if (isChildItem(i)) { + continue; + } + const QString newGroupValue = m_itemData.at(i)->values.value(role).toString(); + if (newGroupValue != groupValue || isFirstGroupValue) { + groupValue = newGroupValue; + groups.append(QPair(i, newGroupValue)); + isFirstGroupValue = false; + } + } + + return groups; +} + +void KFileItemModel::emitSortProgress(int resolvedCount) +{ + // Be tolerant against a resolvedCount with a wrong range. + // Although there should not be a case where KFileItemModelRolesUpdater + // (= caller) provides a wrong range, it is important to emit + // a useful progress information even if there is an unexpected + // implementation issue. + + const int itemCount = count(); + if (resolvedCount >= itemCount) { + m_sortingProgressPercent = -1; + if (m_resortAllItemsTimer->isActive()) { + m_resortAllItemsTimer->stop(); + resortAllItems(); + } + + emit directorySortingProgress(100); + } else if (itemCount > 0) { + resolvedCount = qBound(0, resolvedCount, itemCount); + + const int progress = resolvedCount * 100 / itemCount; + if (m_sortingProgressPercent != progress) { + m_sortingProgressPercent = progress; + emit directorySortingProgress(progress); + } + } +} + +const KFileItemModel::RoleInfoMap* KFileItemModel::rolesInfoMap(int& count) +{ + static const RoleInfoMap rolesInfoMap[] = { + // | role | roleType | role translation + { 0, NoRole, 0, 0 }, + { "text", NameRole, I18N_NOOP2_NOSTRIP("@label", "Name") }, + { "size", SizeRole, I18N_NOOP2_NOSTRIP("@label", "Size") }, + { "date", DateRole, I18N_NOOP2_NOSTRIP("@label", "Date") }, + { "type", TypeRole, I18N_NOOP2_NOSTRIP("@label", "Type") }, + { "path", PathRole, I18N_NOOP2_NOSTRIP("@label", "Path") }, + { "destination", DestinationRole, I18N_NOOP2_NOSTRIP("@label", "Link Destination") }, + { "permissions", PermissionsRole, I18N_NOOP2_NOSTRIP("@label", "Permissions") }, + { "owner", OwnerRole, I18N_NOOP2_NOSTRIP("@label", "Owner") }, + { "group", GroupRole, I18N_NOOP2_NOSTRIP("@label", "User Group") }, + }; + + count = sizeof(rolesInfoMap) / sizeof(RoleInfoMap); + return rolesInfoMap; +} + +void KFileItemModel::determineMimeTypes(const KFileItemList& items, int timeout) +{ + QElapsedTimer timer; + timer.start(); + foreach (const KFileItem& item, items) { // krazy:exclude=foreach + // Only determine mime types for files here. For directories, + // KFileItem::determineMimeType() reads the .directory file inside to + // load the icon, but this is not necessary at all if we just need the + // type. Some special code for setting the correct mime type for + // directories is in retrieveData(). + if (!item.isDir()) { + item.determineMimeType(); + } + + if (timer.elapsed() > timeout) { + // Don't block the user interface, let the remaining items + // be resolved asynchronously. + return; + } + } +} + +QByteArray KFileItemModel::sharedValue(const QByteArray& value) +{ + static QSet pool; + const QSet::const_iterator it = pool.constFind(value); + + if (it != pool.constEnd()) { + return *it; + } else { + pool.insert(value); + return value; + } +} + +bool KFileItemModel::isConsistent() const +{ + // m_items may contain less items than m_itemData because m_items + // is populated lazily, see KFileItemModel::index(const KUrl& url). + if (m_items.count() > m_itemData.count()) { + return false; + } + + for (int i = 0; i < count(); ++i) { + // Check if m_items and m_itemData are consistent. + const KFileItem item = fileItem(i); + if (item.isNull()) { + qWarning() << "Item" << i << "is null"; + return false; + } + + const int itemIndex = index(item); + if (itemIndex != i) { + qWarning() << "Item" << i << "has a wrong index:" << itemIndex; + return false; + } + + // Check if the items are sorted correctly. + if (i > 0 && !lessThan(m_itemData.at(i - 1), m_itemData.at(i))) { + qWarning() << "The order of items" << i - 1 << "and" << i << "is wrong:" + << fileItem(i - 1) << fileItem(i); + return false; + } + + // Check if all parent-child relationships are consistent. + const ItemData* data = m_itemData.at(i); + const ItemData* parent = data->parent; + if (parent) { + if (expandedParentsCount(data) != expandedParentsCount(parent) + 1) { + qWarning() << "expandedParentsCount is inconsistent for parent" << parent->item << "and child" << data->item; + return false; + } + + const int parentIndex = index(parent->item); + if (parentIndex >= i) { + qWarning() << "Index" << parentIndex << "of parent" << parent->item << "is not smaller than index" << i << "of child" << data->item; + return false; + } + } + } + + return true; +} + +#include "moc_kfileitemmodel.cpp" diff --git a/dolphin/src/kitemviews/kfileitemmodel.h b/dolphin/src/kitemviews/kfileitemmodel.h new file mode 100644 index 00000000..64cbfc01 --- /dev/null +++ b/dolphin/src/kitemviews/kfileitemmodel.h @@ -0,0 +1,510 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef KFILEITEMMODEL_H +#define KFILEITEMMODEL_H + +#include +#include +#include +#include +#include + +#include +#include + +class KFileItemModelDirLister; +#include + +/** + * @brief KItemModelBase implementation for KFileItems. + * + * Allows to load items of a directory. Sorting and grouping of + * items are supported. Roles that are not part of KFileItem can + * be added with KFileItemModel::setData(). + * + * Recursive expansion of sub-directories is supported by + * KFileItemModel::setExpanded(). + */ +class DOLPHINPRIVATE_EXPORT KFileItemModel : public KItemModelBase +{ + Q_OBJECT + +public: + explicit KFileItemModel(QObject* parent = 0); + virtual ~KFileItemModel(); + + /** + * Loads the directory specified by \a url. The signals + * directoryLoadingStarted(), directoryLoadingProgress() and directoryLoadingCompleted() + * indicate the current state of the loading process. The items + * of the directory are added after the loading has been completed. + */ + void loadDirectory(const KUrl& url); + + /** + * Throws away all currently loaded items and refreshes the directory + * by reloading all items again. + */ + void refreshDirectory(const KUrl& url); + + /** + * @return Parent directory of the items that are shown. In case + * if a directory tree is shown, KFileItemModel::dir() returns + * the root-parent of all items. + * @see rootItem() + */ + KUrl directory() const; + + /** + * Cancels the loading of a directory which has been started by either + * loadDirectory() or refreshDirectory(). + */ + void cancelDirectoryLoading(); + + virtual int count() const; + virtual QHash data(int index) const; + virtual bool setData(int index, const QHash& values); + + /** + * Sets a separate sorting with directories first (true) or a mixed + * sorting of files and directories (false). + */ + void setSortDirectoriesFirst(bool dirsFirst); + bool sortDirectoriesFirst() const; + + void setShowHiddenFiles(bool show); + bool showHiddenFiles() const; + + /** + * If set to true, only directories are shown as items of the model. Files + * are ignored. + */ + void setShowDirectoriesOnly(bool enabled); + bool showDirectoriesOnly() const; + + /** @reimp */ + virtual QMimeData* createMimeData(const KItemSet& indexes) const; + + /** @reimp */ + virtual int indexForKeyboardSearch(const QString& text, int startFromIndex = 0) const; + + /** @reimp */ + virtual bool supportsDropping(int index) const; + + /** @reimp */ + virtual QString roleDescription(const QByteArray& role) const; + + /** @reimp */ + virtual QList > groups() const; + + /** + * @return The file-item for the index \a index. If the index is in a valid + * range it is assured that the file-item is not null. The runtime + * complexity of this call is O(1). + */ + KFileItem fileItem(int index) const; + + /** + * @return The file-item for the url \a url. If no file-item with the given + * URL is found KFileItem::isNull() will be true for the returned + * file-item. The runtime complexity of this call is O(1). + */ + KFileItem fileItem(const KUrl& url) const; + + /** + * @return The index for the file-item \a item. -1 is returned if no file-item + * is found or if the file-item is null. The amortized runtime + * complexity of this call is O(1). + */ + int index(const KFileItem& item) const; + + /** + * @return The index for the URL \a url. -1 is returned if no file-item + * is found. The amortized runtime complexity of this call is O(1). + */ + int index(const KUrl& url) const; + + /** + * @return Root item of all items representing the item + * for KFileItemModel::dir(). + */ + KFileItem rootItem() const; + + /** + * Clears all items of the model. + */ + void clear(); + + /** + * Sets the roles that should be shown for each item. + */ + void setRoles(const QSet& roles); + QSet roles() const; + + virtual bool setExpanded(int index, bool expanded); + virtual bool isExpanded(int index) const; + virtual bool isExpandable(int index) const; + virtual int expandedParentsCount(int index) const; + + QSet expandedDirectories() const; + + /** + * Marks the URLs in \a urls as sub-directories which were expanded previously. + * After calling loadDirectory() or refreshDirectory() the marked sub-directories + * will be expanded step-by-step. + */ + void restoreExpandedDirectories(const QSet& urls); + + /** + * Expands all parent-directories of the item \a url. + */ + void expandParentDirectories(const KUrl& url); + + void setNameFilter(const QString& nameFilter); + QString nameFilter() const; + + void setMimeTypeFilters(const QStringList& filters); + QStringList mimeTypeFilters() const; + + struct RoleInfo + { QByteArray role; + QString translation; + QString group; + }; + + /** + * @return Provides static information for all available roles that + * are supported by KFileItemModel. Some roles are conditional. + */ + static QList rolesInformation(); + +signals: + /** + * Is emitted if the loading of a directory has been started. It is + * assured that a signal directoryLoadingCompleted() will be send after + * the loading has been finished. For tracking the loading progress + * the signal directoryLoadingProgress() gets emitted in between. + */ + void directoryLoadingStarted(); + + /** + * Is emitted after the loading of a directory has been completed or new + * items have been inserted to an already loaded directory. Usually + * one or more itemsInserted() signals are emitted before loadingCompleted() + * (the only exception is loading an empty directory, where only a + * loadingCompleted() signal gets emitted). + */ + void directoryLoadingCompleted(); + + /** + * Is emitted after the loading of a directory has been canceled. + */ + void directoryLoadingCanceled(); + + /** + * Informs about the progress in percent when loading a directory. It is assured + * that the signal directoryLoadingStarted() has been emitted before. + */ + void directoryLoadingProgress(int percent); + + /** + * Is emitted if the sort-role gets resolved asynchronously and provides + * the progress-information of the sorting in percent. It is assured + * that the last sortProgress-signal contains 100 as value. + */ + void directorySortingProgress(int percent); + + /** + * Is emitted if an information message (e.g. "Connecting to host...") + * should be shown. + */ + void infoMessage(const QString& message); + + /** + * Is emitted if an error message (e.g. "Unknown location") + * should be shown. + */ + void errorMessage(const QString& message); + + /** + * Is emitted if a redirection from the current URL \a oldUrl + * to the new URL \a newUrl has been done. + */ + void directoryRedirection(const KUrl& oldUrl, const KUrl& newUrl); + + /** + * Is emitted when the URL passed by KFileItemModel::setUrl() represents a file. + * In this case no signal errorMessage() will be emitted. + */ + void urlIsFileError(const KUrl& url); + +protected: + virtual void onGroupedSortingChanged(bool current); + virtual void onSortRoleChanged(const QByteArray& current, const QByteArray& previous); + virtual void onSortOrderChanged(Qt::SortOrder current, Qt::SortOrder previous); + +private slots: + /** + * Resorts all items dependent on the set sortRole(), sortOrder() + * and foldersFirst() settings. + */ + void resortAllItems(); + + void slotCompleted(); + void slotCanceled(); + void slotItemsAdded(const KUrl& directoryUrl, const KFileItemList& items); + void slotItemsDeleted(const KFileItemList& items); + void slotRefreshItems(const QList >& items); + void slotClear(); + void slotNaturalSortingChanged(); + + void dispatchPendingItemsToInsert(); + +private: + enum RoleType { + // User visible roles: + NoRole, NameRole, SizeRole, DateRole, PermissionsRole, OwnerRole, + GroupRole, TypeRole, DestinationRole, PathRole, + // Non-visible roles: + IsDirRole, IsLinkRole, IsExpandedRole, IsExpandableRole, ExpandedParentsCountRole, + // Mandatory last entry: + RolesCount + }; + + struct ItemData + { + KFileItem item; + QHash values; + ItemData* parent; + }; + + enum RemoveItemsBehavior { + KeepItemData, + DeleteItemData + }; + + void insertItems(QList& items); + void removeItems(const KItemRangeList& itemRanges, RemoveItemsBehavior behavior); + + /** + * Helper method for insertItems() and removeItems(): Creates + * a list of ItemData elements based on the given items. + * Note that the ItemData instances are created dynamically and + * must be deleted by the caller. + */ + QList createItemDataList(const KUrl& parentUrl, const KFileItemList& items) const; + + /** + * Prepares the items for sorting. Normally, the hash 'values' in ItemData is filled + * lazily to save time and memory, but for some sort roles, it is expected that the + * sort role data is stored in 'values'. + */ + void prepareItemsForSorting(QList& itemDataList); + + static int expandedParentsCount(const ItemData* data); + + void removeExpandedItems(); + + /** + * This function is called by setData() and slotRefreshItems(). It emits + * the itemsChanged() signal, checks if the sort order is still correct, + * and starts m_resortAllItemsTimer if that is not the case. + */ + void emitItemsChangedAndTriggerResorting(const KItemRangeList& itemRanges, const QSet& changedRoles); + + /** + * Resets all values from m_requestRole to false. + */ + void resetRoles(); + + /** + * @return Role-type for the given role. + * Runtime complexity is O(1). + */ + RoleType typeForRole(const QByteArray& role) const; + + /** + * @return Role-byte-array for the given role-type. + * Runtime complexity is O(1). + */ + QByteArray roleForType(RoleType roleType) const; + + QHash retrieveData(const KFileItem& item, const ItemData* parent) const; + + /** + * @return True if \a a has a KFileItem whose text is 'less than' the one + * of \a b according to QString::operator<(const QString&). + */ + static bool nameLessThan(const ItemData* a, const ItemData* b); + + /** + * @return True if the item-data \a a should be ordered before the item-data + * \b. The item-data may have different parent-items. + */ + bool lessThan(const ItemData* a, const ItemData* b) const; + + /** + * Sorts the items between \a begin and \a end using the comparison + * function lessThan(). + */ + void sort(QList::iterator begin, QList::iterator end) const; + + /** + * Helper method for lessThan() and expandedParentsCountCompare(): Compares + * the passed item-data using m_sortRole as criteria. Both items must + * have the same parent item, otherwise the comparison will be wrong. + */ + int sortRoleCompare(const ItemData* a, const ItemData* b) const; + + int stringCompare(const QString& a, const QString& b) const; + + bool useMaximumUpdateInterval() const; + + QList > nameRoleGroups() const; + QList > sizeRoleGroups() const; + QList > dateRoleGroups() const; + QList > permissionRoleGroups() const; + QList > genericStringRoleGroups(const QByteArray& typeForRole) const; + + /** + * Helper method for all xxxRoleGroups() methods to check whether the + * item with the given index is a child-item. A child-item is defined + * as item having an expansion-level > 0. All xxxRoleGroups() methods + * should skip the grouping if the item is a child-item (although + * KItemListView would be capable to show sub-groups in groups this + * results in visual clutter for most usecases). + */ + bool isChildItem(int index) const; + + /** + * Is invoked by KFileItemModelRolesUpdater and results in emitting the + * sortProgress signal with a percent-value of the progress. + */ + void emitSortProgress(int resolvedCount); + + /** + * Applies the filters set through @ref setNameFilter and @ref setMimeTypeFilters. + */ + void applyFilters(); + + /** + * Removes filtered items whose expanded parents have been deleted + * or collapsed via setExpanded(parentIndex, false). + */ + void removeFilteredChildren(const KItemRangeList& parents); + + /** + * Maps the QByteArray-roles to RoleTypes and provides translation- and + * group-contexts. + */ + struct RoleInfoMap + { + const char* const role; + const RoleType roleType; + const char* const roleTranslationContext; + const char* const roleTranslation; + }; + + /** + * @return Map of user visible roles that are accessible by KFileItemModel::rolesInformation(). + */ + static const RoleInfoMap* rolesInfoMap(int& count); + + /** + * Determines the MIME-types of all items that can be done within + * the given timeout. + */ + static void determineMimeTypes(const KFileItemList& items, int timeout); + + /** + * @return Returns a copy of \a value that is implicitly shared + * with other users to save memory. + */ + static QByteArray sharedValue(const QByteArray& value); + + /** + * Checks if the model's internal data structures are consistent. + */ + bool isConsistent() const; + +private: + KFileItemModelDirLister* m_dirLister; + + bool m_naturalSorting; + bool m_sortDirsFirst; + + RoleType m_sortRole; + int m_sortingProgressPercent; // Value of directorySortingProgress() signal + QSet m_roles; + Qt::CaseSensitivity m_caseSensitivity; + + QList m_itemData; + + // m_items is a cache for the method index(const KUrl&). If it contains N + // entries, it is guaranteed that these correspond to the first N items in + // the model, i.e., that (for every i between 0 and N - 1) + // m_items.value(fileItem(i).url()) == i + mutable QHash m_items; + + KFileItemModelFilter m_filter; + QHash m_filteredItems; // Items that got hidden by KFileItemModel::setNameFilter() + + bool m_requestRole[RolesCount]; + + QTimer* m_maximumUpdateIntervalTimer; + QTimer* m_resortAllItemsTimer; + QList m_pendingItemsToInsert; + + // Cache for KFileItemModel::groups() + mutable QList > m_groups; + + // Stores the URLs (key: target url, value: url) of the expanded directories. + QHash m_expandedDirs; + + // URLs that must be expanded. The expanding is initially triggered in setExpanded() + // and done step after step in slotCompleted(). + QSet m_urlsToExpand; + + friend class KFileItemModelLessThan; // Accesses lessThan() method + friend class KFileItemModelRolesUpdater; // Accesses emitSortProgress() method + friend class KFileItemModelTest; // For unit testing + friend class KFileItemModelBenchmark; // For unit testing + friend class KFileItemListViewTest; // For unit testing + friend class DolphinPart; // Accesses m_dirLister +}; + +inline bool KFileItemModel::nameLessThan(const ItemData* a, const ItemData* b) +{ + return a->item.text() < b->item.text(); +} + + +inline bool KFileItemModel::isChildItem(int index) const +{ + if (m_itemData.at(index)->parent) { + return true; + } else { + return false; + } +} + +#endif + + diff --git a/dolphin/src/kitemviews/kfileitemmodelrolesupdater.cpp b/dolphin/src/kitemviews/kfileitemmodelrolesupdater.cpp new file mode 100644 index 00000000..fced7fb0 --- /dev/null +++ b/dolphin/src/kitemviews/kfileitemmodelrolesupdater.cpp @@ -0,0 +1,1012 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "kfileitemmodelrolesupdater.h" + +#include "kfileitemmodel.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "private/kdirectorycontentscounter.h" + +#include +#include +#include +#include +#include + +#include + + +// #define KFILEITEMMODELROLESUPDATER_DEBUG + +namespace { + // Maximum time in ms that the KFileItemModelRolesUpdater + // may perform a blocking operation + const int MaxBlockTimeout = 200; + + // If the number of items is smaller than ResolveAllItemsLimit, + // the roles of all items will be resolved. + const int ResolveAllItemsLimit = 500; + + // Not only the visible area, but up to ReadAheadPages before and after + // this area will be resolved. + const int ReadAheadPages = 5; +} + +KFileItemModelRolesUpdater::KFileItemModelRolesUpdater(KFileItemModel* model, QObject* parent) : + QObject(parent), + m_state(Idle), + m_previewChangedDuringPausing(false), + m_iconSizeChangedDuringPausing(false), + m_rolesChangedDuringPausing(false), + m_previewShown(false), + m_clearPreviews(false), + m_finishedItems(), + m_model(model), + m_iconSize(), + m_firstVisibleIndex(0), + m_lastVisibleIndex(-1), + m_maximumVisibleItems(50), + m_roles(), + m_resolvableRoles(), + m_enabledPlugins(), + m_pendingSortRoleItems(), + m_pendingIndexes(), + m_pendingPreviewItems(), + m_previewJob(), + m_recentlyChangedItemsTimer(0), + m_recentlyChangedItems(), + m_changedItems(), + m_directoryContentsCounter(0) +{ + Q_ASSERT(model); + + QStringList enabledByDefault; + const KService::List plugins = KServiceTypeTrader::self()->query(QLatin1String("ThumbCreator")); + foreach (const KSharedPtr& service, plugins) { + const bool enabled = service->property("X-KDE-PluginInfo-EnabledByDefault", QVariant::Bool).toBool(); + if (enabled) { + enabledByDefault << service->desktopEntryName(); + } + } + + const KConfigGroup globalConfig(KGlobal::config(), "PreviewSettings"); + m_enabledPlugins = globalConfig.readEntry("Plugins", enabledByDefault); + + connect(m_model, SIGNAL(itemsInserted(KItemRangeList)), + this, SLOT(slotItemsInserted(KItemRangeList))); + connect(m_model, SIGNAL(itemsRemoved(KItemRangeList)), + this, SLOT(slotItemsRemoved(KItemRangeList))); + connect(m_model, SIGNAL(itemsChanged(KItemRangeList,QSet)), + this, SLOT(slotItemsChanged(KItemRangeList,QSet))); + connect(m_model, SIGNAL(itemsMoved(KItemRange,QList)), + this, SLOT(slotItemsMoved(KItemRange,QList))); + connect(m_model, SIGNAL(sortRoleChanged(QByteArray,QByteArray)), + this, SLOT(slotSortRoleChanged(QByteArray,QByteArray))); + + // Use a timer to prevent that each call of slotItemsChanged() results in a synchronous + // resolving of the roles. Postpone the resolving until no update has been done for 1 second. + m_recentlyChangedItemsTimer = new QTimer(this); + m_recentlyChangedItemsTimer->setInterval(1000); + m_recentlyChangedItemsTimer->setSingleShot(true); + connect(m_recentlyChangedItemsTimer, SIGNAL(timeout()), this, SLOT(resolveRecentlyChangedItems())); + + m_resolvableRoles.insert("size"); + m_resolvableRoles.insert("type"); + m_resolvableRoles.insert("isExpandable"); + + m_directoryContentsCounter = new KDirectoryContentsCounter(m_model, this); + connect(m_directoryContentsCounter, SIGNAL(result(QString,int)), + this, SLOT(slotDirectoryContentsCountReceived(QString,int))); +} + +KFileItemModelRolesUpdater::~KFileItemModelRolesUpdater() +{ + killPreviewJob(); +} + +void KFileItemModelRolesUpdater::setIconSize(const QSize& size) +{ + if (size != m_iconSize) { + m_iconSize = size; + if (m_state == Paused) { + m_iconSizeChangedDuringPausing = true; + } else if (m_previewShown) { + // An icon size change requires the regenerating of + // all previews + m_finishedItems.clear(); + startUpdating(); + } + } +} + +QSize KFileItemModelRolesUpdater::iconSize() const +{ + return m_iconSize; +} + +void KFileItemModelRolesUpdater::setVisibleIndexRange(int index, int count) +{ + if (index < 0) { + index = 0; + } + if (count < 0) { + count = 0; + } + + if (index == m_firstVisibleIndex && count == m_lastVisibleIndex - m_firstVisibleIndex + 1) { + // The range has not been changed + return; + } + + m_firstVisibleIndex = index; + m_lastVisibleIndex = qMin(index + count - 1, m_model->count() - 1); + + startUpdating(); +} + +void KFileItemModelRolesUpdater::setMaximumVisibleItems(int count) +{ + m_maximumVisibleItems = count; +} + +void KFileItemModelRolesUpdater::setPreviewsShown(bool show) +{ + if (show == m_previewShown) { + return; + } + + m_previewShown = show; + if (!show) { + m_clearPreviews = true; + } + + updateAllPreviews(); +} + +bool KFileItemModelRolesUpdater::previewsShown() const +{ + return m_previewShown; +} + +void KFileItemModelRolesUpdater::setEnabledPlugins(const QStringList& list) +{ + if (m_enabledPlugins != list) { + m_enabledPlugins = list; + if (m_previewShown) { + updateAllPreviews(); + } + } +} + +void KFileItemModelRolesUpdater::setPaused(bool paused) +{ + if (paused == (m_state == Paused)) { + return; + } + + if (paused) { + m_state = Paused; + killPreviewJob(); + } else { + const bool updatePreviews = (m_iconSizeChangedDuringPausing && m_previewShown) || + m_previewChangedDuringPausing; + const bool resolveAll = updatePreviews || m_rolesChangedDuringPausing; + if (resolveAll) { + m_finishedItems.clear(); + } + + m_iconSizeChangedDuringPausing = false; + m_previewChangedDuringPausing = false; + m_rolesChangedDuringPausing = false; + + if (!m_pendingSortRoleItems.isEmpty()) { + m_state = ResolvingSortRole; + resolveNextSortRole(); + } else { + m_state = Idle; + } + + startUpdating(); + } +} + +void KFileItemModelRolesUpdater::setRoles(const QSet& roles) +{ + if (m_roles != roles) { + m_roles = roles; + + + if (m_state == Paused) { + m_rolesChangedDuringPausing = true; + } else { + startUpdating(); + } + } +} + +QSet KFileItemModelRolesUpdater::roles() const +{ + return m_roles; +} + +bool KFileItemModelRolesUpdater::isPaused() const +{ + return m_state == Paused; +} + +QStringList KFileItemModelRolesUpdater::enabledPlugins() const +{ + return m_enabledPlugins; +} + +void KFileItemModelRolesUpdater::slotItemsInserted(const KItemRangeList& itemRanges) +{ + QElapsedTimer timer; + timer.start(); + + // Determine the sort role synchronously for as many items as possible. + if (m_resolvableRoles.contains(m_model->sortRole())) { + int insertedCount = 0; + foreach (const KItemRange& range, itemRanges) { + const int lastIndex = insertedCount + range.index + range.count - 1; + for (int i = insertedCount + range.index; i <= lastIndex; ++i) { + if (timer.elapsed() < MaxBlockTimeout) { + applySortRole(i); + } else { + m_pendingSortRoleItems.insert(m_model->fileItem(i)); + } + } + insertedCount += range.count; + } + + applySortProgressToModel(); + + // If there are still items whose sort role is unknown, check if the + // asynchronous determination of the sort role is already in progress, + // and start it if that is not the case. + if (!m_pendingSortRoleItems.isEmpty() && m_state != ResolvingSortRole) { + killPreviewJob(); + m_state = ResolvingSortRole; + resolveNextSortRole(); + } + } + + startUpdating(); +} + +void KFileItemModelRolesUpdater::slotItemsRemoved(const KItemRangeList& itemRanges) +{ + Q_UNUSED(itemRanges); + + const bool allItemsRemoved = (m_model->count() == 0); + + + if (allItemsRemoved) { + m_state = Idle; + + m_finishedItems.clear(); + m_pendingSortRoleItems.clear(); + m_pendingIndexes.clear(); + m_pendingPreviewItems.clear(); + m_recentlyChangedItems.clear(); + m_recentlyChangedItemsTimer->stop(); + m_changedItems.clear(); + + killPreviewJob(); + } else { + // Only remove the items from m_finishedItems. They will be removed + // from the other sets later on. + QSet::iterator it = m_finishedItems.begin(); + while (it != m_finishedItems.end()) { + if (m_model->index(*it) < 0) { + it = m_finishedItems.erase(it); + } else { + ++it; + } + } + + // The visible items might have changed. + startUpdating(); + } +} + +void KFileItemModelRolesUpdater::slotItemsMoved(const KItemRange& itemRange, QList movedToIndexes) +{ + Q_UNUSED(itemRange); + Q_UNUSED(movedToIndexes); + + // The visible items might have changed. + startUpdating(); +} + +void KFileItemModelRolesUpdater::slotItemsChanged(const KItemRangeList& itemRanges, + const QSet& roles) +{ + Q_UNUSED(roles); + + // Find out if slotItemsChanged() has been done recently. If that is the + // case, resolving the roles is postponed until a timer has exceeded + // to prevent expensive repeated updates if files are updated frequently. + const bool itemsChangedRecently = m_recentlyChangedItemsTimer->isActive(); + + QSet& targetSet = itemsChangedRecently ? m_recentlyChangedItems : m_changedItems; + + foreach (const KItemRange& itemRange, itemRanges) { + int index = itemRange.index; + for (int count = itemRange.count; count > 0; --count) { + const KFileItem item = m_model->fileItem(index); + targetSet.insert(item); + ++index; + } + } + + m_recentlyChangedItemsTimer->start(); + + if (!itemsChangedRecently) { + updateChangedItems(); + } +} + +void KFileItemModelRolesUpdater::slotSortRoleChanged(const QByteArray& current, + const QByteArray& previous) +{ + Q_UNUSED(current); + Q_UNUSED(previous); + + if (m_resolvableRoles.contains(current)) { + m_pendingSortRoleItems.clear(); + m_finishedItems.clear(); + + const int count = m_model->count(); + QElapsedTimer timer; + timer.start(); + + // Determine the sort role synchronously for as many items as possible. + for (int index = 0; index < count; ++index) { + if (timer.elapsed() < MaxBlockTimeout) { + applySortRole(index); + } else { + m_pendingSortRoleItems.insert(m_model->fileItem(index)); + } + } + + applySortProgressToModel(); + + if (!m_pendingSortRoleItems.isEmpty()) { + // Trigger the asynchronous determination of the sort role. + killPreviewJob(); + m_state = ResolvingSortRole; + resolveNextSortRole(); + } + } else { + m_state = Idle; + m_pendingSortRoleItems.clear(); + applySortProgressToModel(); + } +} + +void KFileItemModelRolesUpdater::slotGotPreview(const KFileItem& item, const QPixmap& pixmap) +{ + if (m_state != PreviewJobRunning) { + return; + } + + m_changedItems.remove(item); + + const int index = m_model->index(item); + if (index < 0) { + return; + } + + QPixmap scaledPixmap = pixmap.scaled(m_iconSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);; + + QHash data = rolesData(item); + + const QStringList overlays = data["iconOverlays"].toStringList(); + // Strangely KFileItem::overlays() returns empty string-values, so + // we need to check first whether an overlay must be drawn at all. + // It is more efficient to do it here, as KIconLoader::drawOverlays() + // assumes that an overlay will be drawn and has some additional + // setup time. + foreach (const QString& overlay, overlays) { + if (!overlay.isEmpty()) { + // There is at least one overlay, draw all overlays above m_pixmap + // and cancel the check + KIconLoader::global()->drawOverlays(overlays, scaledPixmap, KIconLoader::Desktop); + break; + } + } + + data.insert("iconPixmap", scaledPixmap); + + disconnect(m_model, SIGNAL(itemsChanged(KItemRangeList,QSet)), + this, SLOT(slotItemsChanged(KItemRangeList,QSet))); + m_model->setData(index, data); + connect(m_model, SIGNAL(itemsChanged(KItemRangeList,QSet)), + this, SLOT(slotItemsChanged(KItemRangeList,QSet))); + + m_finishedItems.insert(item); +} + +void KFileItemModelRolesUpdater::slotPreviewFailed(const KFileItem& item) +{ + if (m_state != PreviewJobRunning) { + return; + } + + m_changedItems.remove(item); + + const int index = m_model->index(item); + if (index >= 0) { + QHash data; + data.insert("iconPixmap", QPixmap()); + + disconnect(m_model, SIGNAL(itemsChanged(KItemRangeList,QSet)), + this, SLOT(slotItemsChanged(KItemRangeList,QSet))); + m_model->setData(index, data); + connect(m_model, SIGNAL(itemsChanged(KItemRangeList,QSet)), + this, SLOT(slotItemsChanged(KItemRangeList,QSet))); + + applyResolvedRoles(index, ResolveAll); + m_finishedItems.insert(item); + } +} + +void KFileItemModelRolesUpdater::slotPreviewJobFinished() +{ + m_previewJob = 0; + + if (m_state != PreviewJobRunning) { + return; + } + + m_state = Idle; + + if (!m_pendingPreviewItems.isEmpty()) { + startPreviewJob(); + } else { + if (!m_changedItems.isEmpty()) { + updateChangedItems(); + } + } +} + +void KFileItemModelRolesUpdater::resolveNextSortRole() +{ + if (m_state != ResolvingSortRole) { + return; + } + + QSet::iterator it = m_pendingSortRoleItems.begin(); + while (it != m_pendingSortRoleItems.end()) { + const KFileItem item = *it; + const int index = m_model->index(item); + + // Continue if the sort role has already been determined for the + // item, and the item has not been changed recently. + if (!m_changedItems.contains(item) && m_model->data(index).contains(m_model->sortRole())) { + it = m_pendingSortRoleItems.erase(it); + continue; + } + + applySortRole(index); + m_pendingSortRoleItems.erase(it); + break; + } + + if (!m_pendingSortRoleItems.isEmpty()) { + applySortProgressToModel(); + QTimer::singleShot(0, this, SLOT(resolveNextSortRole())); + } else { + m_state = Idle; + + // Prevent that we try to update the items twice. + disconnect(m_model, SIGNAL(itemsMoved(KItemRange,QList)), + this, SLOT(slotItemsMoved(KItemRange,QList))); + applySortProgressToModel(); + connect(m_model, SIGNAL(itemsMoved(KItemRange,QList)), + this, SLOT(slotItemsMoved(KItemRange,QList))); + startUpdating(); + } +} + +void KFileItemModelRolesUpdater::resolveNextPendingRoles() +{ + if (m_state != ResolvingAllRoles) { + return; + } + + while (!m_pendingIndexes.isEmpty()) { + const int index = m_pendingIndexes.takeFirst(); + const KFileItem item = m_model->fileItem(index); + + if (m_finishedItems.contains(item)) { + continue; + } + + applyResolvedRoles(index, ResolveAll); + m_finishedItems.insert(item); + m_changedItems.remove(item); + break; + } + + if (!m_pendingIndexes.isEmpty()) { + QTimer::singleShot(0, this, SLOT(resolveNextPendingRoles())); + } else { + m_state = Idle; + + if (m_clearPreviews) { + // Only go through the list if there are items which might still have previews. + if (m_finishedItems.count() != m_model->count()) { + QHash data; + data.insert("iconPixmap", QPixmap()); + + disconnect(m_model, SIGNAL(itemsChanged(KItemRangeList,QSet)), + this, SLOT(slotItemsChanged(KItemRangeList,QSet))); + for (int index = 0; index <= m_model->count(); ++index) { + if (m_model->data(index).contains("iconPixmap")) { + m_model->setData(index, data); + } + } + connect(m_model, SIGNAL(itemsChanged(KItemRangeList,QSet)), + this, SLOT(slotItemsChanged(KItemRangeList,QSet))); + + } + m_clearPreviews = false; + } + + if (!m_changedItems.isEmpty()) { + updateChangedItems(); + } + } +} + +void KFileItemModelRolesUpdater::resolveRecentlyChangedItems() +{ + m_changedItems += m_recentlyChangedItems; + m_recentlyChangedItems.clear(); + updateChangedItems(); +} + +void KFileItemModelRolesUpdater::slotDirectoryContentsCountReceived(const QString& path, int count) +{ + const bool getSizeRole = m_roles.contains("size"); + const bool getIsExpandableRole = m_roles.contains("isExpandable"); + + if (getSizeRole || getIsExpandableRole) { + const int index = m_model->index(KUrl(path)); + if (index >= 0) { + QHash data; + + if (getSizeRole) { + data.insert("size", count); + } + if (getIsExpandableRole) { + data.insert("isExpandable", count > 0); + } + + disconnect(m_model, SIGNAL(itemsChanged(KItemRangeList,QSet)), + this, SLOT(slotItemsChanged(KItemRangeList,QSet))); + m_model->setData(index, data); + connect(m_model, SIGNAL(itemsChanged(KItemRangeList,QSet)), + this, SLOT(slotItemsChanged(KItemRangeList,QSet))); + } + } +} + +void KFileItemModelRolesUpdater::startUpdating() +{ + if (m_state == Paused) { + return; + } + + if (m_finishedItems.count() == m_model->count()) { + // All roles have been resolved already. + m_state = Idle; + return; + } + + // Terminate all updates that are currently active. + killPreviewJob(); + m_pendingIndexes.clear(); + + QElapsedTimer timer; + timer.start(); + + // Determine the icons for the visible items synchronously. + updateVisibleIcons(); + + // A detailed update of the items in and near the visible area + // only makes sense if sorting is finished. + if (m_state == ResolvingSortRole) { + return; + } + + // Start the preview job or the asynchronous resolving of all roles. + QList indexes = indexesToResolve(); + + if (m_previewShown) { + m_pendingPreviewItems.clear(); + m_pendingPreviewItems.reserve(indexes.count()); + + foreach (int index, indexes) { + const KFileItem item = m_model->fileItem(index); + if (!m_finishedItems.contains(item)) { + m_pendingPreviewItems.append(item); + } + } + + startPreviewJob(); + } else { + m_pendingIndexes = indexes; + // Trigger the asynchronous resolving of all roles. + m_state = ResolvingAllRoles; + QTimer::singleShot(0, this, SLOT(resolveNextPendingRoles())); + } +} + +void KFileItemModelRolesUpdater::updateVisibleIcons() +{ + int lastVisibleIndex = m_lastVisibleIndex; + if (lastVisibleIndex <= 0) { + // Guess a reasonable value for the last visible index if the view + // has not told us about the real value yet. + lastVisibleIndex = qMin(m_firstVisibleIndex + m_maximumVisibleItems, m_model->count() - 1); + if (lastVisibleIndex <= 0) { + lastVisibleIndex = qMin(200, m_model->count() - 1); + } + } + + QElapsedTimer timer; + timer.start(); + + // Try to determine the final icons for all visible items. + int index; + for (index = m_firstVisibleIndex; index <= lastVisibleIndex && timer.elapsed() < MaxBlockTimeout; ++index) { + applyResolvedRoles(index, ResolveFast); + } + + // KFileItemListView::initializeItemListWidget(KItemListWidget*) will load + // preliminary icons (i.e., without mime type determination) for the + // remaining items. +} + +void KFileItemModelRolesUpdater::startPreviewJob() +{ + m_state = PreviewJobRunning; + + if (m_pendingPreviewItems.isEmpty()) { + QTimer::singleShot(0, this, SLOT(slotPreviewJobFinished())); + return; + } + + // PreviewJob internally caches items always with the size of + // 128 x 128 pixels or 256 x 256 pixels. A (slow) downscaling is done + // by PreviewJob if a smaller size is requested. For images KFileItemModelRolesUpdater must + // do a downscaling anyhow because of the frame, so in this case only the provided + // cache sizes are requested. + const QSize cacheSize = QSize(128, 128); + + // KIO::filePreview() will request the MIME-type of all passed items, which (in the + // worst case) might block the application for several seconds. To prevent such + // a blocking, we only pass items with known mime type to the preview job. + const int count = m_pendingPreviewItems.count(); + KFileItemList itemSubSet; + itemSubSet.reserve(count); + + if (m_pendingPreviewItems.first().isMimeTypeKnown()) { + // Some mime types are known already, probably because they were + // determined when loading the icons for the visible items. Start + // a preview job for all items at the beginning of the list which + // have a known mime type. + do { + itemSubSet.append(m_pendingPreviewItems.takeFirst()); + } while (!m_pendingPreviewItems.isEmpty() && m_pendingPreviewItems.first().isMimeTypeKnown()); + } else { + // Determine mime types for MaxBlockTimeout ms, and start a preview + // job for the corresponding items. + QElapsedTimer timer; + timer.start(); + + do { + const KFileItem item = m_pendingPreviewItems.takeFirst(); + item.determineMimeType(); + itemSubSet.append(item); + } while (!m_pendingPreviewItems.isEmpty() && timer.elapsed() < MaxBlockTimeout); + } + + KIO::PreviewJob* job = new KIO::PreviewJob(itemSubSet, cacheSize, &m_enabledPlugins); + + job->setIgnoreMaximumSize(itemSubSet.first().isLocalFile()); + if (job->ui()) { + job->ui()->setWindow(qApp->activeWindow()); + } + + connect(job, SIGNAL(gotPreview(KFileItem,QPixmap)), + this, SLOT(slotGotPreview(KFileItem,QPixmap))); + connect(job, SIGNAL(failed(KFileItem)), + this, SLOT(slotPreviewFailed(KFileItem))); + connect(job, SIGNAL(finished(KJob*)), + this, SLOT(slotPreviewJobFinished())); + + m_previewJob = job; +} + +void KFileItemModelRolesUpdater::updateChangedItems() +{ + if (m_state == Paused) { + return; + } + + if (m_changedItems.isEmpty()) { + return; + } + + m_finishedItems -= m_changedItems; + + if (m_resolvableRoles.contains(m_model->sortRole())) { + m_pendingSortRoleItems += m_changedItems; + + if (m_state != ResolvingSortRole) { + // Stop the preview job if necessary, and trigger the + // asynchronous determination of the sort role. + killPreviewJob(); + m_state = ResolvingSortRole; + QTimer::singleShot(0, this, SLOT(resolveNextSortRole())); + } + + return; + } + + QList visibleChangedIndexes; + QList invisibleChangedIndexes; + + foreach (const KFileItem& item, m_changedItems) { + const int index = m_model->index(item); + + if (index < 0) { + m_changedItems.remove(item); + continue; + } + + if (index >= m_firstVisibleIndex && index <= m_lastVisibleIndex) { + visibleChangedIndexes.append(index); + } else { + invisibleChangedIndexes.append(index); + } + } + + std::sort(visibleChangedIndexes.begin(), visibleChangedIndexes.end()); + + if (m_previewShown) { + foreach (int index, visibleChangedIndexes) { + m_pendingPreviewItems.append(m_model->fileItem(index)); + } + + foreach (int index, invisibleChangedIndexes) { + m_pendingPreviewItems.append(m_model->fileItem(index)); + } + + if (!m_previewJob) { + startPreviewJob(); + } + } else { + const bool resolvingInProgress = !m_pendingIndexes.isEmpty(); + m_pendingIndexes = visibleChangedIndexes + m_pendingIndexes + invisibleChangedIndexes; + if (!resolvingInProgress) { + // Trigger the asynchronous resolving of the changed roles. + m_state = ResolvingAllRoles; + QTimer::singleShot(0, this, SLOT(resolveNextPendingRoles())); + } + } +} + +void KFileItemModelRolesUpdater::applySortRole(int index) +{ + QHash data; + const KFileItem item = m_model->fileItem(index); + + if (m_model->sortRole() == "type") { + if (!item.isMimeTypeKnown()) { + item.determineMimeType(); + } + + data.insert("type", item.mimeComment()); + } else if (m_model->sortRole() == "size" && item.isLocalFile() && item.isDir()) { + const QString path = item.localPath(); + data.insert("size", m_directoryContentsCounter->countDirectoryContentsSynchronously(path)); + } else { + // Probably the sort role is a baloo role - just determine all roles. + data = rolesData(item); + } + + disconnect(m_model, SIGNAL(itemsChanged(KItemRangeList,QSet)), + this, SLOT(slotItemsChanged(KItemRangeList,QSet))); + m_model->setData(index, data); + connect(m_model, SIGNAL(itemsChanged(KItemRangeList,QSet)), + this, SLOT(slotItemsChanged(KItemRangeList,QSet))); +} + +void KFileItemModelRolesUpdater::applySortProgressToModel() +{ + // Inform the model about the progress of the resolved items, + // so that it can give an indication when the sorting has been finished. + const int resolvedCount = m_model->count() - m_pendingSortRoleItems.count(); + m_model->emitSortProgress(resolvedCount); +} + +bool KFileItemModelRolesUpdater::applyResolvedRoles(int index, ResolveHint hint) +{ + const KFileItem item = m_model->fileItem(index); + const bool resolveAll = (hint == ResolveAll); + + bool iconChanged = false; + if (!item.isMimeTypeKnown() || !item.isFinalIconKnown()) { + item.determineMimeType(); + iconChanged = true; + } else if (!m_model->data(index).contains("iconName")) { + iconChanged = true; + } + + if (iconChanged || resolveAll || m_clearPreviews) { + if (index < 0) { + return false; + } + + QHash data; + if (resolveAll) { + data = rolesData(item); + } + + data.insert("iconName", item.iconName()); + + if (m_clearPreviews) { + data.insert("iconPixmap", QPixmap()); + } + + disconnect(m_model, SIGNAL(itemsChanged(KItemRangeList,QSet)), + this, SLOT(slotItemsChanged(KItemRangeList,QSet))); + m_model->setData(index, data); + connect(m_model, SIGNAL(itemsChanged(KItemRangeList,QSet)), + this, SLOT(slotItemsChanged(KItemRangeList,QSet))); + return true; + } + + return false; +} + +QHash KFileItemModelRolesUpdater::rolesData(const KFileItem& item) +{ + QHash data; + + const bool getSizeRole = m_roles.contains("size"); + const bool getIsExpandableRole = m_roles.contains("isExpandable"); + + if ((getSizeRole || getIsExpandableRole) && item.isDir()) { + if (item.isLocalFile()) { + // Tell m_directoryContentsCounter that we want to count the items + // inside the directory. The result will be received in slotDirectoryContentsCountReceived. + const QString path = item.localPath(); + m_directoryContentsCounter->addDirectory(path); + } else if (getSizeRole) { + data.insert("size", -1); // -1 indicates an unknown number of items + } + } + + if (m_roles.contains("type")) { + data.insert("type", item.mimeComment()); + } + + data.insert("iconOverlays", item.overlays()); + + return data; +} + +void KFileItemModelRolesUpdater::updateAllPreviews() +{ + if (m_state == Paused) { + m_previewChangedDuringPausing = true; + } else { + m_finishedItems.clear(); + startUpdating(); + } +} + +void KFileItemModelRolesUpdater::killPreviewJob() +{ + if (m_previewJob) { + disconnect(m_previewJob, SIGNAL(gotPreview(KFileItem,QPixmap)), + this, SLOT(slotGotPreview(KFileItem,QPixmap))); + disconnect(m_previewJob, SIGNAL(failed(KFileItem)), + this, SLOT(slotPreviewFailed(KFileItem))); + disconnect(m_previewJob, SIGNAL(finished(KJob*)), + this, SLOT(slotPreviewJobFinished())); + m_previewJob->kill(); + m_previewJob = 0; + m_pendingPreviewItems.clear(); + } +} + +QList KFileItemModelRolesUpdater::indexesToResolve() const +{ + const int count = m_model->count(); + + QList result; + result.reserve(ResolveAllItemsLimit); + + // Add visible items. + for (int i = m_firstVisibleIndex; i <= m_lastVisibleIndex; ++i) { + result.append(i); + } + + // We need a reasonable upper limit for number of items to resolve after + // and before the visible range. m_maximumVisibleItems can be quite large + // when using Compace View. + const int readAheadItems = qMin(ReadAheadPages * m_maximumVisibleItems, ResolveAllItemsLimit / 2); + + // Add items after the visible range. + const int endExtendedVisibleRange = qMin(m_lastVisibleIndex + readAheadItems, count - 1); + for (int i = m_lastVisibleIndex + 1; i <= endExtendedVisibleRange; ++i) { + result.append(i); + } + + // Add items before the visible range in reverse order. + const int beginExtendedVisibleRange = qMax(0, m_firstVisibleIndex - readAheadItems); + for (int i = m_firstVisibleIndex - 1; i >= beginExtendedVisibleRange; --i) { + result.append(i); + } + + // Add items on the last page. + const int beginLastPage = qMax(qMin(endExtendedVisibleRange + 1, count - 1), count - m_maximumVisibleItems); + for (int i = beginLastPage; i < count; ++i) { + result.append(i); + } + + // Add items on the first page. + const int endFirstPage = qMin(qMax(beginExtendedVisibleRange - 1, 0), m_maximumVisibleItems); + for (int i = 0; i <= endFirstPage; ++i) { + result.append(i); + } + + // Continue adding items until ResolveAllItemsLimit is reached. + int remainingItems = ResolveAllItemsLimit - result.count(); + + for (int i = endExtendedVisibleRange + 1; i < beginLastPage && remainingItems > 0; ++i) { + result.append(i); + --remainingItems; + } + + for (int i = beginExtendedVisibleRange - 1; i > endFirstPage && remainingItems > 0; --i) { + result.append(i); + --remainingItems; + } + + return result; +} + +#include "moc_kfileitemmodelrolesupdater.cpp" diff --git a/dolphin/src/kitemviews/kfileitemmodelrolesupdater.h b/dolphin/src/kitemviews/kfileitemmodelrolesupdater.h new file mode 100644 index 00000000..bfc35775 --- /dev/null +++ b/dolphin/src/kitemviews/kfileitemmodelrolesupdater.h @@ -0,0 +1,316 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef KFILEITEMMODELROLESUPDATER_H +#define KFILEITEMMODELROLESUPDATER_H + +#include +#include + +#include + +#include +#include +#include +#include + +class KDirectoryContentsCounter; +class KFileItemModel; +class KJob; +#include +#include + + +/** + * @brief Resolves expensive roles asynchronously and applies them to the KFileItemModel. + * + * KFileItemModel only resolves roles that are inexpensive like e.g. the file name or + * the permissions. Creating previews or determining the MIME-type can be quite expensive + * and KFileItemModelRolesUpdater takes care to update such roles asynchronously. + * + * To prevent a huge CPU and I/O load, these roles are not updated for all + * items, but only for the visible items, some items around the visible area, + * and the items on the first and last pages of the view. This is a compromise + * that aims to minimize the risk that the user sees items with unknown icons + * in the view when scrolling or pressing Home or End. + * + * Determining the roles is done in several phases: + * + * 1. If the sort role is "slow", it is determined for all items. If this + * cannot be finished synchronously in 200 ms, the remaining items are + * handled asynchronously by \a resolveNextSortRole(). + * + * 2. The function startUpdating(), which is called if either the sort role + * has been successfully determined for all items, or items are inserted + * in the view, or the visible items might have changed because items + * were removed or moved, tries to determine the icons for all visible + * items synchronously for 200 ms. Then: + * + * (a) If previews are disabled, icons and all other roles are determined + * asynchronously for the interesting items. This is done by the + * function \a resolveNextPendingRoles(). + * + * (b) If previews are enabled, a \a KIO::PreviewJob is started that loads + * the previews for the interesting items. At the same time, the icons + * for these items are determined asynchronously as fast as possible + * by \a resolveNextPendingRoles(). This minimizes the risk that the + * user sees "unknown" icons when scrolling before the previews have + * arrived. + * + * 3. Finally, the entire process is repeated for any items that might have + * changed in the mean time. + */ +class DOLPHINPRIVATE_EXPORT KFileItemModelRolesUpdater : public QObject +{ + Q_OBJECT + +public: + explicit KFileItemModelRolesUpdater(KFileItemModel* model, QObject* parent = 0); + virtual ~KFileItemModelRolesUpdater(); + + void setIconSize(const QSize& size); + QSize iconSize() const; + + /** + * Sets the range of items that are visible currently. The roles + * of visible items are resolved first. + */ + void setVisibleIndexRange(int index, int count); + + void setMaximumVisibleItems(int count); + + /** + * If \a show is set to true, the "iconPixmap" role will be filled with a preview + * of the file. If \a show is false the MIME type icon will be used for the "iconPixmap" + * role. + */ + void setPreviewsShown(bool show); + bool previewsShown() const; + + /** + * If \a paused is set to true the asynchronous resolving of roles will be paused. + * State changes during pauses like changing the icon size or the preview-shown + * will be remembered and handled after unpausing. + */ + void setPaused(bool paused); + bool isPaused() const; + + /** + * Sets the roles that should be resolved asynchronously. + */ + void setRoles(const QSet& roles); + QSet roles() const; + + /** + * Sets the list of enabled thumbnail plugins that are used for previews. + * Per default all plugins enabled in the KConfigGroup "PreviewSettings" + * are used. + * + * For a list of available plugins, call KServiceTypeTrader::self()->query("ThumbCreator"). + * + * @see enabledPlugins + */ + void setEnabledPlugins(const QStringList& list); + + /** + * Returns the list of enabled thumbnail plugins. + * @see setEnabledPlugins + */ + QStringList enabledPlugins() const; + +private slots: + void slotItemsInserted(const KItemRangeList& itemRanges); + void slotItemsRemoved(const KItemRangeList& itemRanges); + void slotItemsMoved(const KItemRange& itemRange, QList movedToIndexes); + void slotItemsChanged(const KItemRangeList& itemRanges, + const QSet& roles); + void slotSortRoleChanged(const QByteArray& current, + const QByteArray& previous); + + /** + * Is invoked after a preview has been received successfully. + * @see startPreviewJob() + */ + void slotGotPreview(const KFileItem& item, const QPixmap& pixmap); + + /** + * Is invoked after generating a preview has failed. + * @see startPreviewJob() + */ + void slotPreviewFailed(const KFileItem& item); + + /** + * Is invoked when the preview job has been finished. Starts a new preview + * job if there are any interesting items without previews left, or updates + * the changed items otherwise. * + * @see startPreviewJob() + */ + void slotPreviewJobFinished(); + + /** + * Resolves the sort role of the next item in m_pendingSortRole, applies it + * to the model, and invokes itself if there are any pending items left. If + * that is not the case, \a startUpdating() is called. + */ + void resolveNextSortRole(); + + /** + * Resolves the icon name and (if previews are disabled) all other roles + * for the next interesting item. If there are no pending items left, any + * changed items are updated. + */ + void resolveNextPendingRoles(); + + /** + * Resolves items that have not been resolved yet after the change has been + * notified by slotItemsChanged(). Is invoked if the m_changedItemsTimer + * expires. + */ + void resolveRecentlyChangedItems(); + + void slotDirectoryContentsCountReceived(const QString& path, int count); + +private: + /** + * Starts the updating of all roles. The visible items are handled first. + */ + void startUpdating(); + + /** + * Loads the icons for the visible items. After 200 ms, the function + * stops determining mime types and only loads preliminary icons. + * This is a compromise that prevents that + * (a) the GUI is blocked for more than 200 ms, and + * (b) "unknown" icons could be shown in the view. + */ + void updateVisibleIcons(); + + /** + * Creates previews for the items starting from the first item in + * m_pendingPreviewItems. + * @see slotGotPreview() + * @see slotPreviewFailed() + * @see slotPreviewJobFinished() + */ + void startPreviewJob(); + + /** + * Ensures that icons, previews, and other roles are determined for any + * items that have been changed. + */ + void updateChangedItems(); + + /** + * Resolves the sort role of the item and applies it to the model. + */ + void applySortRole(int index); + + void applySortProgressToModel(); + + enum ResolveHint { + ResolveFast, + ResolveAll + }; + bool applyResolvedRoles(int index, ResolveHint hint); + QHash rolesData(const KFileItem& item); + + /** + * @return The number of items of the path \a path. + */ + int subItemsCount(const QString& path) const; + + /** + * Must be invoked if a property has been changed that affects + * the look of the preview. Takes care to update all previews. + */ + void updateAllPreviews(); + + void killPreviewJob(); + + QList indexesToResolve() const; + +private: + enum State { + Idle, + Paused, + ResolvingSortRole, + ResolvingAllRoles, + PreviewJobRunning + }; + + State m_state; + + // Property changes during pausing must be remembered to be able + // to react when unpausing again: + bool m_previewChangedDuringPausing; + bool m_iconSizeChangedDuringPausing; + bool m_rolesChangedDuringPausing; + + // Property for setPreviewsShown()/previewsShown(). + bool m_previewShown; + + // True if the role "iconPixmap" should be cleared when resolving the next + // role with resolveRole(). Is necessary if the preview gets disabled + // during the roles-updater has been paused by setPaused(). + bool m_clearPreviews; + + // Remembers which items have been handled already, to prevent that + // previews and other expensive roles are determined again. + QSet m_finishedItems; + + KFileItemModel* m_model; + QSize m_iconSize; + int m_firstVisibleIndex; + int m_lastVisibleIndex; + int m_maximumVisibleItems; + QSet m_roles; + QSet m_resolvableRoles; + QStringList m_enabledPlugins; + + // Items for which the sort role still has to be determined. + QSet m_pendingSortRoleItems; + + // Indexes of items which still have to be handled by + // resolveNextPendingRoles(). + QList m_pendingIndexes; + + // Items which have been left over from the last call of startPreviewJob(). + // A new preview job will be started from them once the first one finishes. + KFileItemList m_pendingPreviewItems; + + KJob* m_previewJob; + + // When downloading or copying large files, the slot slotItemsChanged() + // will be called periodically within a quite short delay. To prevent + // a high CPU-load by generating e.g. previews for each notification, the update + // will be postponed until no file change has been done within a longer period + // of time. + QTimer* m_recentlyChangedItemsTimer; + QSet m_recentlyChangedItems; + + // Items which have not been changed repeatedly recently. + QSet m_changedItems; + + KDirectoryContentsCounter* m_directoryContentsCounter; + +}; + +#endif + + diff --git a/dolphin/src/kitemviews/kitemlistcontainer.cpp b/dolphin/src/kitemviews/kitemlistcontainer.cpp new file mode 100644 index 00000000..cea688a0 --- /dev/null +++ b/dolphin/src/kitemviews/kitemlistcontainer.cpp @@ -0,0 +1,406 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz * + * * + * Based on the Itemviews NG project from Trolltech Labs: * + * http://qt.gitorious.org/qt-labs/itemviews-ng * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "kitemlistcontainer.h" + +#include "kitemlistcontroller.h" +#include "kitemlistview.h" +#include "kitemmodelbase.h" + +#include "private/kitemlistsmoothscroller.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +/** + * Replaces the default viewport of KItemListContainer by a + * non-scrollable viewport. The scrolling is done in an optimized + * way by KItemListView internally. + */ +class KItemListContainerViewport : public QGraphicsView +{ +public: + KItemListContainerViewport(QGraphicsScene* scene, QWidget* parent); +protected: + virtual void wheelEvent(QWheelEvent* event); +}; + +KItemListContainerViewport::KItemListContainerViewport(QGraphicsScene* scene, QWidget* parent) : + QGraphicsView(scene, parent) +{ + setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + setViewportMargins(0, 0, 0, 0); + setFrameShape(QFrame::NoFrame); +} + +void KItemListContainerViewport::wheelEvent(QWheelEvent* event) +{ + // Assure that the wheel-event gets forwarded to the parent + // and not handled at all by QGraphicsView. + event->ignore(); +} + +KItemListContainer::KItemListContainer(KItemListController* controller, QWidget* parent) : + QAbstractScrollArea(parent), + m_controller(controller), + m_horizontalSmoothScroller(0), + m_verticalSmoothScroller(0) +{ + Q_ASSERT(controller); + controller->setParent(this); + + QGraphicsView* graphicsView = new KItemListContainerViewport(new QGraphicsScene(this), this); + setViewport(graphicsView); + + m_horizontalSmoothScroller = new KItemListSmoothScroller(horizontalScrollBar(), this); + m_verticalSmoothScroller = new KItemListSmoothScroller(verticalScrollBar(), this); + + if (controller->model()) { + slotModelChanged(controller->model(), 0); + } + if (controller->view()) { + slotViewChanged(controller->view(), 0); + } + + connect(controller, SIGNAL(modelChanged(KItemModelBase*,KItemModelBase*)), + this, SLOT(slotModelChanged(KItemModelBase*,KItemModelBase*))); + connect(controller, SIGNAL(viewChanged(KItemListView*,KItemListView*)), + this, SLOT(slotViewChanged(KItemListView*,KItemListView*))); +} + +KItemListContainer::~KItemListContainer() +{ + // Don't rely on the QObject-order to delete the controller, otherwise + // the QGraphicsScene might get deleted before the view. + delete m_controller; + m_controller = 0; +} + +KItemListController* KItemListContainer::controller() const +{ + return m_controller; +} + +void KItemListContainer::setEnabledFrame(bool enable) +{ + QGraphicsView* graphicsView = qobject_cast(viewport()); + if (enable) { + setFrameShape(QFrame::StyledPanel); + graphicsView->setPalette(palette()); + graphicsView->viewport()->setAutoFillBackground(true); + } else { + setFrameShape(QFrame::NoFrame); + // Make the background of the container transparent and apply the window-text color + // to the text color, so that enough contrast is given for all color + // schemes + QPalette p = graphicsView->palette(); + p.setColor(QPalette::Active, QPalette::Text, p.color(QPalette::Active, QPalette::WindowText)); + p.setColor(QPalette::Inactive, QPalette::Text, p.color(QPalette::Inactive, QPalette::WindowText)); + p.setColor(QPalette::Disabled, QPalette::Text, p.color(QPalette::Disabled, QPalette::WindowText)); + graphicsView->setPalette(p); + graphicsView->viewport()->setAutoFillBackground(false); + } +} + +bool KItemListContainer::enabledFrame() const +{ + const QGraphicsView* graphicsView = qobject_cast(viewport()); + return graphicsView->autoFillBackground(); +} + +void KItemListContainer::keyPressEvent(QKeyEvent* event) +{ + // TODO: We should find a better way to handle the key press events in the view. + // The reasons why we need this hack are: + // 1. Without reimplementing keyPressEvent() here, the event would not reach the QGraphicsView. + // 2. By default, the KItemListView does not have the keyboard focus in the QGraphicsScene, so + // simply sending the event to the QGraphicsView which is the KItemListContainer's viewport + // does not work. + KItemListView* view = m_controller->view(); + if (view) { + QApplication::sendEvent(view, event); + } +} + +void KItemListContainer::showEvent(QShowEvent* event) +{ + QAbstractScrollArea::showEvent(event); + updateGeometries(); +} + +void KItemListContainer::resizeEvent(QResizeEvent* event) +{ + QAbstractScrollArea::resizeEvent(event); + updateGeometries(); +} + +void KItemListContainer::scrollContentsBy(int dx, int dy) +{ + m_horizontalSmoothScroller->scrollContentsBy(dx); + m_verticalSmoothScroller->scrollContentsBy(dy); +} + +void KItemListContainer::wheelEvent(QWheelEvent* event) +{ + if (event->modifiers().testFlag(Qt::ControlModifier)) { + event->ignore(); + return; + } + + KItemListView* view = m_controller->view(); + if (!view) { + event->ignore(); + return; + } + + const bool scrollHorizontally = (event->orientation() == Qt::Horizontal) || + (event->orientation() == Qt::Vertical && !verticalScrollBar()->isVisible()); + KItemListSmoothScroller* smoothScroller = scrollHorizontally ? + m_horizontalSmoothScroller : m_verticalSmoothScroller; + + const int numDegrees = event->delta() / 8; + const int numSteps = numDegrees / 15; + + const QScrollBar* scrollBar = smoothScroller->scrollBar(); + smoothScroller->scrollTo(scrollBar->value() - numSteps * scrollBar->pageStep() / 4); + + event->accept(); +} + +void KItemListContainer::slotScrollOrientationChanged(Qt::Orientation current, Qt::Orientation previous) +{ + Q_UNUSED(previous); + updateSmoothScrollers(current); +} + +void KItemListContainer::slotModelChanged(KItemModelBase* current, KItemModelBase* previous) +{ + Q_UNUSED(current); + Q_UNUSED(previous); +} + +void KItemListContainer::slotViewChanged(KItemListView* current, KItemListView* previous) +{ + QGraphicsScene* scene = static_cast(viewport())->scene(); + if (previous) { + scene->removeItem(previous); + disconnect(previous, SIGNAL(scrollOrientationChanged(Qt::Orientation,Qt::Orientation)), this, SLOT(slotScrollOrientationChanged(Qt::Orientation,Qt::Orientation))); + disconnect(previous, SIGNAL(scrollOffsetChanged(qreal,qreal)), this, SLOT(updateScrollOffsetScrollBar())); + disconnect(previous, SIGNAL(maximumScrollOffsetChanged(qreal,qreal)), this, SLOT(updateScrollOffsetScrollBar())); + disconnect(previous, SIGNAL(itemOffsetChanged(qreal,qreal)), this, SLOT(updateItemOffsetScrollBar())); + disconnect(previous, SIGNAL(maximumItemOffsetChanged(qreal,qreal)), this, SLOT(updateItemOffsetScrollBar())); + disconnect(previous, SIGNAL(scrollTo(qreal)), this, SLOT(scrollTo(qreal))); + m_horizontalSmoothScroller->setTargetObject(0); + m_verticalSmoothScroller->setTargetObject(0); + } + if (current) { + scene->addItem(current); + connect(current, SIGNAL(scrollOrientationChanged(Qt::Orientation,Qt::Orientation)), this, SLOT(slotScrollOrientationChanged(Qt::Orientation,Qt::Orientation))); + connect(current, SIGNAL(scrollOffsetChanged(qreal,qreal)), this, SLOT(updateScrollOffsetScrollBar())); + connect(current, SIGNAL(maximumScrollOffsetChanged(qreal,qreal)), this, SLOT(updateScrollOffsetScrollBar())); + connect(current, SIGNAL(itemOffsetChanged(qreal,qreal)), this, SLOT(updateItemOffsetScrollBar())); + connect(current, SIGNAL(maximumItemOffsetChanged(qreal,qreal)), this, SLOT(updateItemOffsetScrollBar())); + connect(current, SIGNAL(scrollTo(qreal)), this, SLOT(scrollTo(qreal))); + m_horizontalSmoothScroller->setTargetObject(current); + m_verticalSmoothScroller->setTargetObject(current); + updateSmoothScrollers(current->scrollOrientation()); + } +} + +void KItemListContainer::scrollTo(qreal offset) +{ + const KItemListView* view = m_controller->view(); + if (view) { + if (view->scrollOrientation() == Qt::Vertical) { + m_verticalSmoothScroller->scrollTo(offset); + } else { + m_horizontalSmoothScroller->scrollTo(offset); + } + } +} + +void KItemListContainer::updateScrollOffsetScrollBar() +{ + const KItemListView* view = m_controller->view(); + if (!view) { + return; + } + + KItemListSmoothScroller* smoothScroller = 0; + QScrollBar* scrollOffsetScrollBar = 0; + int singleStep = 0; + int pageStep = 0; + int maximum = 0; + if (view->scrollOrientation() == Qt::Vertical) { + smoothScroller = m_verticalSmoothScroller; + scrollOffsetScrollBar = verticalScrollBar(); + singleStep = view->itemSize().height(); + // We cannot use view->size().height() because this height might + // include the header widget, which is not part of the scrolled area. + pageStep = view->verticalPageStep(); + + // However, the total height of the view must be considered for the + // maximum value of the scroll bar. Note that the view's scrollOffset() + // refers to the offset of the top part of the view, which might be + // hidden behind the header. + maximum = qMax(0, int(view->maximumScrollOffset() - view->size().height())); + } else { + smoothScroller = m_horizontalSmoothScroller; + scrollOffsetScrollBar = horizontalScrollBar(); + singleStep = view->itemSize().width(); + pageStep = view->size().width(); + maximum = qMax(0, int(view->maximumScrollOffset() - view->size().width())); + } + + const int value = view->scrollOffset(); + if (smoothScroller->requestScrollBarUpdate(maximum)) { + const bool updatePolicy = (scrollOffsetScrollBar->maximum() > 0 && maximum == 0) + || horizontalScrollBarPolicy() == Qt::ScrollBarAlwaysOn; + + scrollOffsetScrollBar->setSingleStep(singleStep); + scrollOffsetScrollBar->setPageStep(pageStep); + scrollOffsetScrollBar->setMinimum(0); + scrollOffsetScrollBar->setMaximum(maximum); + scrollOffsetScrollBar->setValue(value); + + if (updatePolicy) { + // Prevent a potential endless layout loop (see bug #293318). + updateScrollOffsetScrollBarPolicy(); + } + } +} + +void KItemListContainer::updateItemOffsetScrollBar() +{ + const KItemListView* view = m_controller->view(); + if (!view) { + return; + } + + KItemListSmoothScroller* smoothScroller = 0; + QScrollBar* itemOffsetScrollBar = 0; + int singleStep = 0; + int pageStep = 0; + if (view->scrollOrientation() == Qt::Vertical) { + smoothScroller = m_horizontalSmoothScroller; + itemOffsetScrollBar = horizontalScrollBar(); + singleStep = view->size().width() / 10; + pageStep = view->size().width(); + } else { + smoothScroller = m_verticalSmoothScroller; + itemOffsetScrollBar = verticalScrollBar(); + singleStep = view->size().height() / 10; + pageStep = view->size().height(); + } + + const int value = view->itemOffset(); + const int maximum = qMax(0, int(view->maximumItemOffset()) - pageStep); + if (smoothScroller->requestScrollBarUpdate(maximum)) { + itemOffsetScrollBar->setSingleStep(singleStep); + itemOffsetScrollBar->setPageStep(pageStep); + itemOffsetScrollBar->setMinimum(0); + itemOffsetScrollBar->setMaximum(maximum); + itemOffsetScrollBar->setValue(value); + } +} + +void KItemListContainer::updateGeometries() +{ + QRect rect = geometry(); + + int extra = frameWidth() * 2; + QStyleOption option; + option.initFrom(this); + int scrollbarSpacing = 0; + if (style()->styleHint(QStyle::SH_ScrollView_FrameOnlyAroundContents, &option, this)) { + scrollbarSpacing = style()->pixelMetric(QStyle::PM_ScrollView_ScrollBarSpacing, &option, this); + } + + const int widthDec = verticalScrollBar()->isVisible() + ? extra + scrollbarSpacing + style()->pixelMetric(QStyle::PM_ScrollBarExtent, &option, this) + : extra; + + const int heightDec = horizontalScrollBar()->isVisible() + ? extra + scrollbarSpacing + style()->pixelMetric(QStyle::PM_ScrollBarExtent, &option, this) + : extra; + + const QRectF newGeometry(0, 0, rect.width() - widthDec, + rect.height() - heightDec); + if (m_controller->view()->geometry() != newGeometry) { + m_controller->view()->setGeometry(newGeometry); + + // Get the real geometry of the view again since the scrollbars + // visibilities and the view geometry may have changed in re-layout. + static_cast(viewport())->scene()->setSceneRect(m_controller->view()->geometry()); + static_cast(viewport())->viewport()->setGeometry(m_controller->view()->geometry().toRect()); + + updateScrollOffsetScrollBar(); + updateItemOffsetScrollBar(); + } +} + +void KItemListContainer::updateSmoothScrollers(Qt::Orientation orientation) +{ + if (orientation == Qt::Vertical) { + m_verticalSmoothScroller->setPropertyName("scrollOffset"); + m_horizontalSmoothScroller->setPropertyName("itemOffset"); + } else { + m_horizontalSmoothScroller->setPropertyName("scrollOffset"); + m_verticalSmoothScroller->setPropertyName("itemOffset"); + } +} + +void KItemListContainer::updateScrollOffsetScrollBarPolicy() +{ + const KItemListView* view = m_controller->view(); + Q_ASSERT(view); + const bool vertical = (view->scrollOrientation() == Qt::Vertical); + + QStyleOption option; + option.initFrom(this); + const int scrollBarInc = style()->pixelMetric(QStyle::PM_ScrollBarExtent, &option, this); + + QSizeF newViewSize = m_controller->view()->size(); + if (vertical) { + newViewSize.rwidth() += scrollBarInc; + } else { + newViewSize.rheight() += scrollBarInc; + } + + const Qt::ScrollBarPolicy policy = view->scrollBarRequired(newViewSize) + ? Qt::ScrollBarAlwaysOn : Qt::ScrollBarAsNeeded; + if (vertical) { + setVerticalScrollBarPolicy(policy); + } else { + setHorizontalScrollBarPolicy(policy); + } +} + +#include "moc_kitemlistcontainer.cpp" diff --git a/dolphin/src/kitemviews/kitemlistcontainer.h b/dolphin/src/kitemviews/kitemlistcontainer.h new file mode 100644 index 00000000..5c0dcc23 --- /dev/null +++ b/dolphin/src/kitemviews/kitemlistcontainer.h @@ -0,0 +1,97 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz * + * * + * Based on the Itemviews NG project from Trolltech Labs: * + * http://qt.gitorious.org/qt-labs/itemviews-ng * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef KITEMLISTCONTAINER_H +#define KITEMLISTCONTAINER_H + +#include + +#include +#include + +class KItemListController; +class KItemListSmoothScroller; +class KItemListView; +class KItemModelBase; +#include + +/** + * @brief Provides a QWidget based scrolling view for a KItemListController. + * + * The model and view are maintained by the KItemListController. + * + * @see KItemListController + */ +class DOLPHINPRIVATE_EXPORT KItemListContainer : public QAbstractScrollArea +{ + Q_OBJECT + +public: + /** + * @param controller Controller that maintains the model and the view. + * The KItemListContainer takes ownership of the controller + * (the parent will be set to the KItemListContainer). + * @param parent Optional parent widget. + */ + explicit KItemListContainer(KItemListController* controller, QWidget* parent = 0); + virtual ~KItemListContainer(); + KItemListController* controller() const; + + void setEnabledFrame(bool enable); + bool enabledFrame() const; + +protected: + virtual void keyPressEvent(QKeyEvent* event); + virtual void showEvent(QShowEvent* event); + virtual void resizeEvent(QResizeEvent* event); + virtual void scrollContentsBy(int dx, int dy); + virtual void wheelEvent(QWheelEvent* event); + +private slots: + void slotScrollOrientationChanged(Qt::Orientation current, Qt::Orientation previous); + void slotModelChanged(KItemModelBase* current, KItemModelBase* previous); + void slotViewChanged(KItemListView* current, KItemListView* previous); + void scrollTo(qreal offset); + void updateScrollOffsetScrollBar(); + void updateItemOffsetScrollBar(); + +private: + void updateGeometries(); + void updateSmoothScrollers(Qt::Orientation orientation); + + /** + * Helper method for updateScrollOffsetScrollBar(). Updates the scrollbar-policy + * to Qt::ScrollBarAlwaysOn for cases where turning off the scrollbar might lead + * to an endless layout loop (see bug #293318). + */ + void updateScrollOffsetScrollBarPolicy(); + +private: + KItemListController* m_controller; + + KItemListSmoothScroller* m_horizontalSmoothScroller; + KItemListSmoothScroller* m_verticalSmoothScroller; +}; + +#endif + + diff --git a/dolphin/src/kitemviews/kitemlistcontroller.cpp b/dolphin/src/kitemviews/kitemlistcontroller.cpp new file mode 100644 index 00000000..172add0d --- /dev/null +++ b/dolphin/src/kitemviews/kitemlistcontroller.cpp @@ -0,0 +1,1294 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz * + * Copyright (C) 2012 by Frank Reininghaus * + * * + * Based on the Itemviews NG project from Trolltech Labs: * + * http://qt.gitorious.org/qt-labs/itemviews-ng * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "kitemlistcontroller.h" + +#include +#include + +#include "kitemlistview.h" +#include "kitemlistselectionmanager.h" + +#include "private/kitemlistrubberband.h" +#include "private/kitemlistkeyboardsearchmanager.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +KItemListController::KItemListController(KItemModelBase* model, KItemListView* view, QObject* parent) : + QObject(parent), + m_singleClickActivationEnforced(false), + m_selectionTogglePressed(false), + m_clearSelectionIfItemsAreNotDragged(false), + m_selectionBehavior(NoSelection), + m_autoActivationBehavior(ActivationAndExpansion), + m_mouseDoubleClickAction(ActivateItemOnly), + m_model(0), + m_view(0), + m_selectionManager(new KItemListSelectionManager(this)), + m_keyboardManager(new KItemListKeyboardSearchManager(this)), + m_pressedIndex(-1), + m_pressedMousePos(), + m_autoActivationTimer(0), + m_oldSelection(), + m_keyboardAnchorIndex(-1), + m_keyboardAnchorPos(0) +{ + connect(m_keyboardManager, SIGNAL(changeCurrentItem(QString,bool)), + this, SLOT(slotChangeCurrentItem(QString,bool))); + connect(m_selectionManager, SIGNAL(currentChanged(int,int)), + m_keyboardManager, SLOT(slotCurrentChanged(int,int))); + + m_autoActivationTimer = new QTimer(this); + m_autoActivationTimer->setSingleShot(true); + m_autoActivationTimer->setInterval(-1); + connect(m_autoActivationTimer, SIGNAL(timeout()), this, SLOT(slotAutoActivationTimeout())); + + setModel(model); + setView(view); +} + +KItemListController::~KItemListController() +{ + setView(0); + Q_ASSERT(!m_view); + + setModel(0); + Q_ASSERT(!m_model); +} + +void KItemListController::setModel(KItemModelBase* model) +{ + if (m_model == model) { + return; + } + + KItemModelBase* oldModel = m_model; + if (oldModel) { + oldModel->deleteLater(); + } + + m_model = model; + if (m_model) { + m_model->setParent(this); + } + + if (m_view) { + m_view->setModel(m_model); + } + + m_selectionManager->setModel(m_model); + + emit modelChanged(m_model, oldModel); +} + +KItemModelBase* KItemListController::model() const +{ + return m_model; +} + +KItemListSelectionManager* KItemListController::selectionManager() const +{ + return m_selectionManager; +} + +void KItemListController::setView(KItemListView* view) +{ + if (m_view == view) { + return; + } + + KItemListView* oldView = m_view; + if (oldView) { + disconnect(oldView, SIGNAL(scrollOffsetChanged(qreal,qreal)), this, SLOT(slotViewScrollOffsetChanged(qreal,qreal))); + oldView->deleteLater(); + } + + m_view = view; + + if (m_view) { + m_view->setParent(this); + m_view->setController(this); + m_view->setModel(m_model); + connect(m_view, SIGNAL(scrollOffsetChanged(qreal,qreal)), this, SLOT(slotViewScrollOffsetChanged(qreal,qreal))); + updateExtendedSelectionRegion(); + } + + emit viewChanged(m_view, oldView); +} + +KItemListView* KItemListController::view() const +{ + return m_view; +} + +void KItemListController::setSelectionBehavior(SelectionBehavior behavior) +{ + m_selectionBehavior = behavior; + updateExtendedSelectionRegion(); +} + +KItemListController::SelectionBehavior KItemListController::selectionBehavior() const +{ + return m_selectionBehavior; +} + +void KItemListController::setAutoActivationBehavior(AutoActivationBehavior behavior) +{ + m_autoActivationBehavior = behavior; +} + +KItemListController::AutoActivationBehavior KItemListController::autoActivationBehavior() const +{ + return m_autoActivationBehavior; +} + +void KItemListController::setMouseDoubleClickAction(MouseDoubleClickAction action) +{ + m_mouseDoubleClickAction = action; +} + +KItemListController::MouseDoubleClickAction KItemListController::mouseDoubleClickAction() const +{ + return m_mouseDoubleClickAction; +} + +void KItemListController::setAutoActivationDelay(int delay) +{ + m_autoActivationTimer->setInterval(delay); +} + +int KItemListController::autoActivationDelay() const +{ + return m_autoActivationTimer->interval(); +} + +void KItemListController::setSingleClickActivationEnforced(bool singleClick) +{ + m_singleClickActivationEnforced = singleClick; +} + +bool KItemListController::singleClickActivationEnforced() const +{ + return m_singleClickActivationEnforced; +} + +bool KItemListController::showEvent(QShowEvent* event) +{ + Q_UNUSED(event); + return false; +} + +bool KItemListController::hideEvent(QHideEvent* event) +{ + Q_UNUSED(event); + return false; +} + +bool KItemListController::keyPressEvent(QKeyEvent* event) +{ + int index = m_selectionManager->currentItem(); + int key = event->key(); + + // Handle the expanding/collapsing of items + if (m_view->supportsItemExpanding() && m_model->isExpandable(index)) { + if (key == Qt::Key_Right) { + if (m_model->setExpanded(index, true)) { + return true; + } + } else if (key == Qt::Key_Left) { + if (m_model->setExpanded(index, false)) { + return true; + } + } + } + + const bool shiftPressed = event->modifiers() & Qt::ShiftModifier; + const bool controlPressed = event->modifiers() & Qt::ControlModifier; + const bool shiftOrControlPressed = shiftPressed || controlPressed; + + const int itemCount = m_model->count(); + + // For horizontal scroll orientation, transform + // the arrow keys to simplify the event handling. + if (m_view->scrollOrientation() == Qt::Horizontal) { + switch (key) { + case Qt::Key_Up: key = Qt::Key_Left; break; + case Qt::Key_Down: key = Qt::Key_Right; break; + case Qt::Key_Left: key = Qt::Key_Up; break; + case Qt::Key_Right: key = Qt::Key_Down; break; + default: break; + } + } + + const bool selectSingleItem = m_selectionBehavior != NoSelection && + itemCount == 1 && + (key == Qt::Key_Home || key == Qt::Key_End || + key == Qt::Key_Up || key == Qt::Key_Down || + key == Qt::Key_Left || key == Qt::Key_Right); + if (selectSingleItem) { + const int current = m_selectionManager->currentItem(); + m_selectionManager->setSelected(current); + return true; + } + + switch (key) { + case Qt::Key_Home: + index = 0; + m_keyboardAnchorIndex = index; + m_keyboardAnchorPos = keyboardAnchorPos(index); + break; + + case Qt::Key_End: + index = itemCount - 1; + m_keyboardAnchorIndex = index; + m_keyboardAnchorPos = keyboardAnchorPos(index); + break; + + case Qt::Key_Left: + if (index > 0) { + const int expandedParentsCount = m_model->expandedParentsCount(index); + if (expandedParentsCount == 0) { + --index; + } else { + // Go to the parent of the current item. + do { + --index; + } while (index > 0 && m_model->expandedParentsCount(index) == expandedParentsCount); + } + m_keyboardAnchorIndex = index; + m_keyboardAnchorPos = keyboardAnchorPos(index); + } + break; + + case Qt::Key_Right: + if (index < itemCount - 1) { + ++index; + m_keyboardAnchorIndex = index; + m_keyboardAnchorPos = keyboardAnchorPos(index); + } + break; + + case Qt::Key_Up: + updateKeyboardAnchor(); + index = previousRowIndex(index); + break; + + case Qt::Key_Down: + updateKeyboardAnchor(); + index = nextRowIndex(index); + break; + + case Qt::Key_PageUp: + if (m_view->scrollOrientation() == Qt::Horizontal) { + // The new current index should correspond to the first item in the current column. + int newIndex = qMax(index - 1, 0); + while (newIndex != index && m_view->itemRect(newIndex).topLeft().y() < m_view->itemRect(index).topLeft().y()) { + index = newIndex; + newIndex = qMax(index - 1, 0); + } + m_keyboardAnchorIndex = index; + m_keyboardAnchorPos = keyboardAnchorPos(index); + } else { + const qreal currentItemBottom = m_view->itemRect(index).bottomLeft().y(); + const qreal height = m_view->geometry().height(); + + // The new current item should be the first item in the current + // column whose itemRect's top coordinate is larger than targetY. + const qreal targetY = currentItemBottom - height; + + updateKeyboardAnchor(); + int newIndex = previousRowIndex(index); + do { + index = newIndex; + updateKeyboardAnchor(); + newIndex = previousRowIndex(index); + } while (m_view->itemRect(newIndex).topLeft().y() > targetY && newIndex != index); + } + break; + + case Qt::Key_PageDown: + if (m_view->scrollOrientation() == Qt::Horizontal) { + // The new current index should correspond to the last item in the current column. + int newIndex = qMin(index + 1, m_model->count() - 1); + while (newIndex != index && m_view->itemRect(newIndex).topLeft().y() > m_view->itemRect(index).topLeft().y()) { + index = newIndex; + newIndex = qMin(index + 1, m_model->count() - 1); + } + m_keyboardAnchorIndex = index; + m_keyboardAnchorPos = keyboardAnchorPos(index); + } else { + const qreal currentItemTop = m_view->itemRect(index).topLeft().y(); + const qreal height = m_view->geometry().height(); + + // The new current item should be the last item in the current + // column whose itemRect's bottom coordinate is smaller than targetY. + const qreal targetY = currentItemTop + height; + + updateKeyboardAnchor(); + int newIndex = nextRowIndex(index); + do { + index = newIndex; + updateKeyboardAnchor(); + newIndex = nextRowIndex(index); + } while (m_view->itemRect(newIndex).bottomLeft().y() < targetY && newIndex != index); + } + break; + + case Qt::Key_Enter: + case Qt::Key_Return: { + const KItemSet selectedItems = m_selectionManager->selectedItems(); + if (selectedItems.count() >= 2) { + emit itemsActivated(selectedItems); + } else if (selectedItems.count() == 1) { + emit itemActivated(selectedItems.first()); + } else { + emit itemActivated(index); + } + break; + } + + case Qt::Key_Menu: { + // Emit the signal itemContextMenuRequested() in case if at least one + // item is selected. Otherwise the signal viewContextMenuRequested() will be emitted. + const KItemSet selectedItems = m_selectionManager->selectedItems(); + int index = -1; + if (selectedItems.count() >= 2) { + const int currentItemIndex = m_selectionManager->currentItem(); + index = selectedItems.contains(currentItemIndex) + ? currentItemIndex : selectedItems.first(); + } else if (selectedItems.count() == 1) { + index = selectedItems.first(); + } + + if (index >= 0) { + const QRectF contextRect = m_view->itemContextRect(index); + const QPointF pos(m_view->scene()->views().first()->mapToGlobal(contextRect.bottomRight().toPoint())); + emit itemContextMenuRequested(index, pos); + } else { + emit viewContextMenuRequested(QCursor::pos()); + } + break; + } + + case Qt::Key_Escape: + if (m_selectionBehavior != SingleSelection) { + m_selectionManager->clearSelection(); + } + m_keyboardManager->cancelSearch(); + emit escapePressed(); + break; + + case Qt::Key_Space: + if (m_selectionBehavior == MultiSelection) { + if (controlPressed) { + // Toggle the selection state of the current item. + m_selectionManager->endAnchoredSelection(); + m_selectionManager->setSelected(index, 1, KItemListSelectionManager::Toggle); + m_selectionManager->beginAnchoredSelection(index); + break; + } else { + // Select the current item if it is not selected yet. + const int current = m_selectionManager->currentItem(); + if (!m_selectionManager->isSelected(current)) { + m_selectionManager->setSelected(current); + break; + } + } + } + // Fall through to the default case and add the Space to the current search string. + + default: + m_keyboardManager->addKeys(event->text()); + // Make sure unconsumed events get propagated up the chain. #302329 + event->ignore(); + return false; + } + + if (m_selectionManager->currentItem() != index) { + switch (m_selectionBehavior) { + case NoSelection: + m_selectionManager->setCurrentItem(index); + break; + + case SingleSelection: + m_selectionManager->setCurrentItem(index); + m_selectionManager->clearSelection(); + m_selectionManager->setSelected(index, 1); + break; + + case MultiSelection: + if (controlPressed) { + m_selectionManager->endAnchoredSelection(); + } + + m_selectionManager->setCurrentItem(index); + + if (!shiftOrControlPressed) { + m_selectionManager->clearSelection(); + m_selectionManager->setSelected(index, 1); + } + + if (!shiftPressed) { + m_selectionManager->beginAnchoredSelection(index); + } + break; + } + + m_view->scrollToItem(index); + } + return true; +} + +void KItemListController::slotChangeCurrentItem(const QString& text, bool searchFromNextItem) +{ + if (!m_model || m_model->count() == 0) { + return; + } + const int currentIndex = m_selectionManager->currentItem(); + int index; + if (searchFromNextItem) { + index = m_model->indexForKeyboardSearch(text, (currentIndex + 1) % m_model->count()); + } else { + index = m_model->indexForKeyboardSearch(text, currentIndex); + } + if (index >= 0) { + m_selectionManager->setCurrentItem(index); + + if (m_selectionBehavior != NoSelection) { + m_selectionManager->clearSelection(); + m_selectionManager->setSelected(index, 1); + m_selectionManager->beginAnchoredSelection(index); + } + + m_view->scrollToItem(index); + } +} + +void KItemListController::slotAutoActivationTimeout() +{ + if (!m_model || !m_view) { + return; + } + + const int index = m_autoActivationTimer->property("index").toInt(); + if (index < 0 || index >= m_model->count()) { + return; + } + + /* m_view->isUnderMouse() fixes a bug in the Folder-View-Panel and in the + * Places-Panel. + * + * Bug: When you drag a file onto a Folder-View-Item or a Places-Item and + * then move away before the auto-activation timeout triggers, than the + * item still becomes activated/expanded. + * + * See Bug 293200 and 305783 + */ + if (m_model->supportsDropping(index) && m_view->isUnderMouse()) { + if (m_view->supportsItemExpanding() && m_model->isExpandable(index)) { + const bool expanded = m_model->isExpanded(index); + m_model->setExpanded(index, !expanded); + } else if (m_autoActivationBehavior != ExpansionOnly) { + emit itemActivated(index); + } + } +} + +bool KItemListController::mousePressEvent(QGraphicsSceneMouseEvent* event, const QTransform& transform) +{ + if (!m_view) { + return false; + } + + m_pressedMousePos = transform.map(event->pos()); + m_pressedIndex = m_view->itemAt(m_pressedMousePos); + emit mouseButtonPressed(m_pressedIndex, event->buttons()); + + if (m_view->isAboveExpansionToggle(m_pressedIndex, m_pressedMousePos)) { + m_selectionManager->endAnchoredSelection(); + m_selectionManager->setCurrentItem(m_pressedIndex); + m_selectionManager->beginAnchoredSelection(m_pressedIndex); + return true; + } + + m_selectionTogglePressed = m_view->isAboveSelectionToggle(m_pressedIndex, m_pressedMousePos); + if (m_selectionTogglePressed) { + m_selectionManager->setSelected(m_pressedIndex, 1, KItemListSelectionManager::Toggle); + // The previous anchored selection has been finished already in + // KItemListSelectionManager::setSelected(). We can safely change + // the current item and start a new anchored selection now. + m_selectionManager->setCurrentItem(m_pressedIndex); + m_selectionManager->beginAnchoredSelection(m_pressedIndex); + return true; + } + + const bool shiftPressed = event->modifiers() & Qt::ShiftModifier; + const bool controlPressed = event->modifiers() & Qt::ControlModifier; + + // The previous selection is cleared if either + // 1. The selection mode is SingleSelection, or + // 2. the selection mode is MultiSelection, and *none* of the following conditions are met: + // a) Shift or Control are pressed. + // b) The clicked item is selected already. In that case, the user might want to: + // - start dragging multiple items, or + // - open the context menu and perform an action for all selected items. + const bool shiftOrControlPressed = shiftPressed || controlPressed; + const bool pressedItemAlreadySelected = m_pressedIndex >= 0 && m_selectionManager->isSelected(m_pressedIndex); + const bool clearSelection = m_selectionBehavior == SingleSelection || + (!shiftOrControlPressed && !pressedItemAlreadySelected); + if (clearSelection) { + m_selectionManager->clearSelection(); + } else if (pressedItemAlreadySelected && !shiftOrControlPressed && (event->buttons() & Qt::LeftButton)) { + // The user might want to start dragging multiple items, but if he clicks the item + // in order to trigger it instead, the other selected items must be deselected. + // However, we do not know yet what the user is going to do. + // -> remember that the user pressed an item which had been selected already and + // clear the selection in mouseReleaseEvent(), unless the items are dragged. + m_clearSelectionIfItemsAreNotDragged = true; + } + + if (!shiftPressed) { + // Finish the anchored selection before the current index is changed + m_selectionManager->endAnchoredSelection(); + } + + if (m_pressedIndex >= 0) { + m_selectionManager->setCurrentItem(m_pressedIndex); + + switch (m_selectionBehavior) { + case NoSelection: + break; + + case SingleSelection: + m_selectionManager->setSelected(m_pressedIndex); + break; + + case MultiSelection: + if (controlPressed && !shiftPressed) { + m_selectionManager->setSelected(m_pressedIndex, 1, KItemListSelectionManager::Toggle); + m_selectionManager->beginAnchoredSelection(m_pressedIndex); + } else if (!shiftPressed || !m_selectionManager->isAnchoredSelectionActive()) { + // Select the pressed item and start a new anchored selection + m_selectionManager->setSelected(m_pressedIndex, 1, KItemListSelectionManager::Select); + m_selectionManager->beginAnchoredSelection(m_pressedIndex); + } + break; + + default: + Q_ASSERT(false); + break; + } + + if (event->buttons() & Qt::RightButton) { + emit itemContextMenuRequested(m_pressedIndex, event->screenPos()); + } + + return true; + } + + if (event->buttons() & Qt::RightButton) { + const QRectF headerBounds = m_view->headerBoundaries(); + if (headerBounds.contains(event->pos())) { + emit headerContextMenuRequested(event->screenPos()); + } else { + emit viewContextMenuRequested(event->screenPos()); + } + return true; + } + + if (m_selectionBehavior == MultiSelection) { + QPointF startPos = m_pressedMousePos; + if (m_view->scrollOrientation() == Qt::Vertical) { + startPos.ry() += m_view->scrollOffset(); + if (m_view->itemSize().width() < 0) { + // Use a special rubberband for views that have only one column and + // expand the rubberband to use the whole width of the view. + startPos.setX(0); + } + } else { + startPos.rx() += m_view->scrollOffset(); + } + + m_oldSelection = m_selectionManager->selectedItems(); + KItemListRubberBand* rubberBand = m_view->rubberBand(); + rubberBand->setStartPosition(startPos); + rubberBand->setEndPosition(startPos); + rubberBand->setActive(true); + connect(rubberBand, SIGNAL(endPositionChanged(QPointF,QPointF)), this, SLOT(slotRubberBandChanged())); + m_view->setAutoScroll(true); + } + + return false; +} + +bool KItemListController::mouseMoveEvent(QGraphicsSceneMouseEvent* event, const QTransform& transform) +{ + if (!m_view) { + return false; + } + + if (m_pressedIndex >= 0) { + // Check whether a dragging should be started + if (event->buttons() & Qt::LeftButton) { + const QPointF pos = transform.map(event->pos()); + if ((pos - m_pressedMousePos).manhattanLength() >= QApplication::startDragDistance()) { + if (!m_selectionManager->isSelected(m_pressedIndex)) { + // Always assure that the dragged item gets selected. Usually this is already + // done on the mouse-press event, but when using the selection-toggle on a + // selected item the dragged item is not selected yet. + m_selectionManager->setSelected(m_pressedIndex, 1, KItemListSelectionManager::Toggle); + } else { + // A selected item has been clicked to drag all selected items + // -> the selection should not be cleared when the mouse button is released. + m_clearSelectionIfItemsAreNotDragged = false; + } + + startDragging(); + } + } + } else { + KItemListRubberBand* rubberBand = m_view->rubberBand(); + if (rubberBand->isActive()) { + QPointF endPos = transform.map(event->pos()); + + // Update the current item. + const int newCurrent = m_view->itemAt(endPos); + if (newCurrent >= 0) { + // It's expected that the new current index is also the new anchor (bug 163451). + m_selectionManager->endAnchoredSelection(); + m_selectionManager->setCurrentItem(newCurrent); + m_selectionManager->beginAnchoredSelection(newCurrent); + } + + if (m_view->scrollOrientation() == Qt::Vertical) { + endPos.ry() += m_view->scrollOffset(); + if (m_view->itemSize().width() < 0) { + // Use a special rubberband for views that have only one column and + // expand the rubberband to use the whole width of the view. + endPos.setX(m_view->size().width()); + } + } else { + endPos.rx() += m_view->scrollOffset(); + } + rubberBand->setEndPosition(endPos); + } + } + + return false; +} + +bool KItemListController::mouseReleaseEvent(QGraphicsSceneMouseEvent* event, const QTransform& transform) +{ + if (!m_view) { + return false; + } + + emit mouseButtonReleased(m_pressedIndex, event->buttons()); + + const bool isAboveSelectionToggle = m_view->isAboveSelectionToggle(m_pressedIndex, m_pressedMousePos); + if (isAboveSelectionToggle) { + m_selectionTogglePressed = false; + return true; + } + + if (!isAboveSelectionToggle && m_selectionTogglePressed) { + m_selectionManager->setSelected(m_pressedIndex, 1, KItemListSelectionManager::Toggle); + m_selectionTogglePressed = false; + return true; + } + + const bool shiftOrControlPressed = event->modifiers() & Qt::ShiftModifier || + event->modifiers() & Qt::ControlModifier; + + KItemListRubberBand* rubberBand = m_view->rubberBand(); + if (rubberBand->isActive()) { + disconnect(rubberBand, SIGNAL(endPositionChanged(QPointF,QPointF)), this, SLOT(slotRubberBandChanged())); + rubberBand->setActive(false); + m_oldSelection.clear(); + m_view->setAutoScroll(false); + } + + const QPointF pos = transform.map(event->pos()); + const int index = m_view->itemAt(pos); + + if (index >= 0 && index == m_pressedIndex) { + // The release event is done above the same item as the press event + + if (m_clearSelectionIfItemsAreNotDragged) { + // A selected item has been clicked, but no drag operation has been started + // -> clear the rest of the selection. + m_selectionManager->clearSelection(); + m_selectionManager->setSelected(m_pressedIndex, 1, KItemListSelectionManager::Select); + m_selectionManager->beginAnchoredSelection(m_pressedIndex); + } + + if (event->button() & Qt::LeftButton) { + bool emitItemActivated = true; + if (m_view->isAboveExpansionToggle(index, pos)) { + const bool expanded = m_model->isExpanded(index); + m_model->setExpanded(index, !expanded); + + emit itemExpansionToggleClicked(index); + emitItemActivated = false; + } else if (shiftOrControlPressed) { + // The mouse click should only update the selection, not trigger the item + emitItemActivated = false; + } else if (!(KGlobalSettings::singleClick() || m_singleClickActivationEnforced)) { + emitItemActivated = false; + } + if (emitItemActivated) { + emit itemActivated(index); + } + } else if (event->button() & Qt::MiddleButton) { + emit itemMiddleClicked(index); + } + } + + m_pressedMousePos = QPointF(); + m_pressedIndex = -1; + m_clearSelectionIfItemsAreNotDragged = false; + return false; +} + +bool KItemListController::mouseDoubleClickEvent(QGraphicsSceneMouseEvent* event, const QTransform& transform) +{ + const QPointF pos = transform.map(event->pos()); + const int index = m_view->itemAt(pos); + + // Expand item if desired - See Bug 295573 + if (m_mouseDoubleClickAction != ActivateItemOnly) { + if (m_view && m_model && m_view->supportsItemExpanding() && m_model->isExpandable(index)) { + const bool expanded = m_model->isExpanded(index); + m_model->setExpanded(index, !expanded); + } + } + + bool emitItemActivated = !(KGlobalSettings::singleClick() || m_singleClickActivationEnforced) && + (event->button() & Qt::LeftButton) && + index >= 0 && index < m_model->count(); + if (emitItemActivated) { + emit itemActivated(index); + } + return false; +} + +bool KItemListController::dragEnterEvent(QGraphicsSceneDragDropEvent* event, const QTransform& transform) +{ + Q_UNUSED(event); + Q_UNUSED(transform); + return false; +} + +bool KItemListController::dragLeaveEvent(QGraphicsSceneDragDropEvent* event, const QTransform& transform) +{ + Q_UNUSED(event); + Q_UNUSED(transform); + + m_view->setAutoScroll(false); + m_view->hideDropIndicator(); + + KItemListWidget* widget = hoveredWidget(); + if (widget) { + widget->setHovered(false); + emit itemUnhovered(widget->index()); + } + return false; +} + +bool KItemListController::dragMoveEvent(QGraphicsSceneDragDropEvent* event, const QTransform& transform) +{ + if (!m_model || !m_view) { + return false; + } + + event->acceptProposedAction(); + + KItemListWidget* oldHoveredWidget = hoveredWidget(); + + const QPointF pos = transform.map(event->pos()); + KItemListWidget* newHoveredWidget = widgetForPos(pos); + + if (oldHoveredWidget != newHoveredWidget) { + m_autoActivationTimer->stop(); + + if (oldHoveredWidget) { + oldHoveredWidget->setHovered(false); + emit itemUnhovered(oldHoveredWidget->index()); + } + } + + if (newHoveredWidget) { + bool droppingBetweenItems = false; + if (m_model->sortRole().isEmpty()) { + // The model supports inserting items between other items. + droppingBetweenItems = (m_view->showDropIndicator(pos) >= 0); + } + + const int index = newHoveredWidget->index(); + if (!droppingBetweenItems) { + if (m_model->supportsDropping(index)) { + // Something has been dragged on an item. + m_view->hideDropIndicator(); + if (!newHoveredWidget->isHovered()) { + newHoveredWidget->setHovered(true); + emit itemHovered(index); + } + + if (!m_autoActivationTimer->isActive() && m_autoActivationTimer->interval() >= 0) { + m_autoActivationTimer->setProperty("index", index); + m_autoActivationTimer->start(); + } + } + } else { + m_autoActivationTimer->stop(); + if (newHoveredWidget && newHoveredWidget->isHovered()) { + newHoveredWidget->setHovered(false); + emit itemUnhovered(index); + } + } + } else { + m_view->hideDropIndicator(); + } + + return false; +} + +bool KItemListController::dropEvent(QGraphicsSceneDragDropEvent* event, const QTransform& transform) +{ + if (!m_view) { + return false; + } + + m_autoActivationTimer->stop(); + m_view->setAutoScroll(false); + + const QPointF pos = transform.map(event->pos()); + + int dropAboveIndex = -1; + if (m_model->sortRole().isEmpty()) { + // The model supports inserting of items between other items. + dropAboveIndex = m_view->showDropIndicator(pos); + } + + if (dropAboveIndex >= 0) { + // Something has been dropped between two items. + m_view->hideDropIndicator(); + emit aboveItemDropEvent(dropAboveIndex, event); + } else { + // Something has been dropped on an item or on an empty part of the view. + emit itemDropEvent(m_view->itemAt(pos), event); + } + + return true; +} + +bool KItemListController::hoverEnterEvent(QGraphicsSceneHoverEvent* event, const QTransform& transform) +{ + Q_UNUSED(event); + Q_UNUSED(transform); + return false; +} + +bool KItemListController::hoverMoveEvent(QGraphicsSceneHoverEvent* event, const QTransform& transform) +{ + Q_UNUSED(transform); + if (!m_model || !m_view) { + return false; + } + + KItemListWidget* oldHoveredWidget = hoveredWidget(); + const QPointF pos = transform.map(event->pos()); + KItemListWidget* newHoveredWidget = widgetForPos(pos); + + if (oldHoveredWidget != newHoveredWidget) { + if (oldHoveredWidget) { + oldHoveredWidget->setHovered(false); + emit itemUnhovered(oldHoveredWidget->index()); + } + + if (newHoveredWidget) { + newHoveredWidget->setHovered(true); + const QPointF mappedPos = newHoveredWidget->mapFromItem(m_view, pos); + newHoveredWidget->setHoverPosition(mappedPos); + emit itemHovered(newHoveredWidget->index()); + } + } else if (oldHoveredWidget) { + const QPointF mappedPos = oldHoveredWidget->mapFromItem(m_view, pos); + oldHoveredWidget->setHoverPosition(mappedPos); + } + + return false; +} + +bool KItemListController::hoverLeaveEvent(QGraphicsSceneHoverEvent* event, const QTransform& transform) +{ + Q_UNUSED(event); + Q_UNUSED(transform); + + if (!m_model || !m_view) { + return false; + } + + foreach (KItemListWidget* widget, m_view->visibleItemListWidgets()) { + if (widget->isHovered()) { + widget->setHovered(false); + emit itemUnhovered(widget->index()); + } + } + return false; +} + +bool KItemListController::wheelEvent(QGraphicsSceneWheelEvent* event, const QTransform& transform) +{ + Q_UNUSED(event); + Q_UNUSED(transform); + return false; +} + +bool KItemListController::resizeEvent(QGraphicsSceneResizeEvent* event, const QTransform& transform) +{ + Q_UNUSED(event); + Q_UNUSED(transform); + return false; +} + +bool KItemListController::processEvent(QEvent* event, const QTransform& transform) +{ + if (!event) { + return false; + } + + switch (event->type()) { + case QEvent::KeyPress: + return keyPressEvent(static_cast(event)); + case QEvent::GraphicsSceneMousePress: + return mousePressEvent(static_cast(event), QTransform()); + case QEvent::GraphicsSceneMouseMove: + return mouseMoveEvent(static_cast(event), QTransform()); + case QEvent::GraphicsSceneMouseRelease: + return mouseReleaseEvent(static_cast(event), QTransform()); + case QEvent::GraphicsSceneMouseDoubleClick: + return mouseDoubleClickEvent(static_cast(event), QTransform()); + case QEvent::GraphicsSceneWheel: + return wheelEvent(static_cast(event), QTransform()); + case QEvent::GraphicsSceneDragEnter: + return dragEnterEvent(static_cast(event), QTransform()); + case QEvent::GraphicsSceneDragLeave: + return dragLeaveEvent(static_cast(event), QTransform()); + case QEvent::GraphicsSceneDragMove: + return dragMoveEvent(static_cast(event), QTransform()); + case QEvent::GraphicsSceneDrop: + return dropEvent(static_cast(event), QTransform()); + case QEvent::GraphicsSceneHoverEnter: + return hoverEnterEvent(static_cast(event), QTransform()); + case QEvent::GraphicsSceneHoverMove: + return hoverMoveEvent(static_cast(event), QTransform()); + case QEvent::GraphicsSceneHoverLeave: + return hoverLeaveEvent(static_cast(event), QTransform()); + case QEvent::GraphicsSceneResize: + return resizeEvent(static_cast(event), transform); + default: + break; + } + + return false; +} + +void KItemListController::slotViewScrollOffsetChanged(qreal current, qreal previous) +{ + if (!m_view) { + return; + } + + KItemListRubberBand* rubberBand = m_view->rubberBand(); + if (rubberBand->isActive()) { + const qreal diff = current - previous; + // TODO: Ideally just QCursor::pos() should be used as + // new end-position but it seems there is no easy way + // to have something like QWidget::mapFromGlobal() for QGraphicsWidget + // (... or I just missed an easy way to do the mapping) + QPointF endPos = rubberBand->endPosition(); + if (m_view->scrollOrientation() == Qt::Vertical) { + endPos.ry() += diff; + } else { + endPos.rx() += diff; + } + + rubberBand->setEndPosition(endPos); + } +} + +void KItemListController::slotRubberBandChanged() +{ + if (!m_view || !m_model || m_model->count() <= 0) { + return; + } + + const KItemListRubberBand* rubberBand = m_view->rubberBand(); + const QPointF startPos = rubberBand->startPosition(); + const QPointF endPos = rubberBand->endPosition(); + QRectF rubberBandRect = QRectF(startPos, endPos).normalized(); + + const bool scrollVertical = (m_view->scrollOrientation() == Qt::Vertical); + if (scrollVertical) { + rubberBandRect.translate(0, -m_view->scrollOffset()); + } else { + rubberBandRect.translate(-m_view->scrollOffset(), 0); + } + + if (!m_oldSelection.isEmpty()) { + // Clear the old selection that was available before the rubberband has + // been activated in case if no Shift- or Control-key are pressed + const bool shiftOrControlPressed = QApplication::keyboardModifiers() & Qt::ShiftModifier || + QApplication::keyboardModifiers() & Qt::ControlModifier; + if (!shiftOrControlPressed) { + m_oldSelection.clear(); + } + } + + KItemSet selectedItems; + + // Select all visible items that intersect with the rubberband + foreach (const KItemListWidget* widget, m_view->visibleItemListWidgets()) { + const int index = widget->index(); + + const QRectF widgetRect = m_view->itemRect(index); + if (widgetRect.intersects(rubberBandRect)) { + const QRectF iconRect = widget->iconRect().translated(widgetRect.topLeft()); + const QRectF textRect = widget->textRect().translated(widgetRect.topLeft()); + if (iconRect.intersects(rubberBandRect) || textRect.intersects(rubberBandRect)) { + selectedItems.insert(index); + } + } + } + + // Select all invisible items that intersect with the rubberband. Instead of + // iterating all items only the area which might be touched by the rubberband + // will be checked. + const bool increaseIndex = scrollVertical ? + startPos.y() > endPos.y(): startPos.x() > endPos.x(); + + int index = increaseIndex ? m_view->lastVisibleIndex() + 1 : m_view->firstVisibleIndex() - 1; + bool selectionFinished = false; + do { + const QRectF widgetRect = m_view->itemRect(index); + if (widgetRect.intersects(rubberBandRect)) { + selectedItems.insert(index); + } + + if (increaseIndex) { + ++index; + selectionFinished = (index >= m_model->count()) || + ( scrollVertical && widgetRect.top() > rubberBandRect.bottom()) || + (!scrollVertical && widgetRect.left() > rubberBandRect.right()); + } else { + --index; + selectionFinished = (index < 0) || + ( scrollVertical && widgetRect.bottom() < rubberBandRect.top()) || + (!scrollVertical && widgetRect.right() < rubberBandRect.left()); + } + } while (!selectionFinished); + + if (QApplication::keyboardModifiers() & Qt::ControlModifier) { + // If Control is pressed, the selection state of all items in the rubberband is toggled. + // Therefore, the new selection contains: + // 1. All previously selected items which are not inside the rubberband, and + // 2. all items inside the rubberband which have not been selected previously. + m_selectionManager->setSelectedItems(m_oldSelection ^ selectedItems); + } + else { + m_selectionManager->setSelectedItems(selectedItems + m_oldSelection); + } +} + +void KItemListController::startDragging() +{ + if (!m_view || !m_model) { + return; + } + + const KItemSet selectedItems = m_selectionManager->selectedItems(); + if (selectedItems.isEmpty()) { + return; + } + + QMimeData* data = m_model->createMimeData(selectedItems); + if (!data) { + return; + } + + // The created drag object will be owned and deleted + // by QApplication::activeWindow(). + QDrag* drag = new QDrag(QApplication::activeWindow()); + drag->setMimeData(data); + + const QPixmap pixmap = m_view->createDragPixmap(selectedItems); + drag->setPixmap(pixmap); + + const QPoint hotSpot(pixmap.width() / 2, 0); + drag->setHotSpot(hotSpot); + + drag->exec(Qt::MoveAction | Qt::CopyAction | Qt::LinkAction, Qt::CopyAction); +} + +KItemListWidget* KItemListController::hoveredWidget() const +{ + Q_ASSERT(m_view); + + foreach (KItemListWidget* widget, m_view->visibleItemListWidgets()) { + if (widget->isHovered()) { + return widget; + } + } + + return 0; +} + +KItemListWidget* KItemListController::widgetForPos(const QPointF& pos) const +{ + Q_ASSERT(m_view); + + foreach (KItemListWidget* widget, m_view->visibleItemListWidgets()) { + const QPointF mappedPos = widget->mapFromItem(m_view, pos); + + const bool hovered = widget->contains(mappedPos) && + !widget->expansionToggleRect().contains(mappedPos); + if (hovered) { + return widget; + } + } + + return 0; +} + +void KItemListController::updateKeyboardAnchor() +{ + const bool validAnchor = m_keyboardAnchorIndex >= 0 && + m_keyboardAnchorIndex < m_model->count() && + keyboardAnchorPos(m_keyboardAnchorIndex) == m_keyboardAnchorPos; + if (!validAnchor) { + const int index = m_selectionManager->currentItem(); + m_keyboardAnchorIndex = index; + m_keyboardAnchorPos = keyboardAnchorPos(index); + } +} + +int KItemListController::nextRowIndex(int index) const +{ + if (m_keyboardAnchorIndex < 0) { + return index; + } + + const int maxIndex = m_model->count() - 1; + if (index == maxIndex) { + return index; + } + + // Calculate the index of the last column inside the row of the current index + int lastColumnIndex = index; + while (keyboardAnchorPos(lastColumnIndex + 1) > keyboardAnchorPos(lastColumnIndex)) { + ++lastColumnIndex; + if (lastColumnIndex >= maxIndex) { + return index; + } + } + + // Based on the last column index go to the next row and calculate the nearest index + // that is below the current index + int nextRowIndex = lastColumnIndex + 1; + int searchIndex = nextRowIndex; + qreal minDiff = qAbs(m_keyboardAnchorPos - keyboardAnchorPos(nextRowIndex)); + while (searchIndex < maxIndex && keyboardAnchorPos(searchIndex + 1) > keyboardAnchorPos(searchIndex)) { + ++searchIndex; + const qreal searchDiff = qAbs(m_keyboardAnchorPos - keyboardAnchorPos(searchIndex)); + if (searchDiff < minDiff) { + minDiff = searchDiff; + nextRowIndex = searchIndex; + } + } + + return nextRowIndex; +} + +int KItemListController::previousRowIndex(int index) const +{ + if (m_keyboardAnchorIndex < 0 || index == 0) { + return index; + } + + // Calculate the index of the first column inside the row of the current index + int firstColumnIndex = index; + while (keyboardAnchorPos(firstColumnIndex - 1) < keyboardAnchorPos(firstColumnIndex)) { + --firstColumnIndex; + if (firstColumnIndex <= 0) { + return index; + } + } + + // Based on the first column index go to the previous row and calculate the nearest index + // that is above the current index + int previousRowIndex = firstColumnIndex - 1; + int searchIndex = previousRowIndex; + qreal minDiff = qAbs(m_keyboardAnchorPos - keyboardAnchorPos(previousRowIndex)); + while (searchIndex > 0 && keyboardAnchorPos(searchIndex - 1) < keyboardAnchorPos(searchIndex)) { + --searchIndex; + const qreal searchDiff = qAbs(m_keyboardAnchorPos - keyboardAnchorPos(searchIndex)); + if (searchDiff < minDiff) { + minDiff = searchDiff; + previousRowIndex = searchIndex; + } + } + + return previousRowIndex; +} + +qreal KItemListController::keyboardAnchorPos(int index) const +{ + const QRectF itemRect = m_view->itemRect(index); + if (!itemRect.isEmpty()) { + return (m_view->scrollOrientation() == Qt::Vertical) ? itemRect.x() : itemRect.y(); + } + + return 0; +} + +void KItemListController::updateExtendedSelectionRegion() +{ + if (m_view) { + const bool extend = (m_selectionBehavior != MultiSelection); + KItemListStyleOption option = m_view->styleOption(); + if (option.extendedSelectionRegion != extend) { + option.extendedSelectionRegion = extend; + m_view->setStyleOption(option); + } + } +} + +#include "moc_kitemlistcontroller.cpp" diff --git a/dolphin/src/kitemviews/kitemlistcontroller.h b/dolphin/src/kitemviews/kitemlistcontroller.h new file mode 100644 index 00000000..f96ed63b --- /dev/null +++ b/dolphin/src/kitemviews/kitemlistcontroller.h @@ -0,0 +1,351 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz * + * * + * Based on the Itemviews NG project from Trolltech Labs: * + * http://qt.gitorious.org/qt-labs/itemviews-ng * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef KITEMLISTCONTROLLER_H +#define KITEMLISTCONTROLLER_H + +#include + +#include "kitemset.h" + +#include +#include +#include +#include + +class KItemModelBase; +class KItemListKeyboardSearchManager; +class KItemListSelectionManager; +class KItemListView; +class KItemListWidget; +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * @brief Controls the view, model and selection of an item-list. + * + * For a working item-list it is mandatory to set a compatible view and model + * with KItemListController::setView() and KItemListController::setModel(). + * + * @see KItemListView + * @see KItemModelBase + * @see KItemListSelectionManager + */ +class DOLPHINPRIVATE_EXPORT KItemListController : public QObject +{ + Q_OBJECT + Q_ENUMS(SelectionBehavior) + Q_PROPERTY(KItemModelBase* model READ model WRITE setModel) + Q_PROPERTY(KItemListView *view READ view WRITE setView) + Q_PROPERTY(SelectionBehavior selectionBehavior READ selectionBehavior WRITE setSelectionBehavior) + Q_PROPERTY(AutoActivationBehavior autoActivationBehavior READ autoActivationBehavior WRITE setAutoActivationBehavior) + Q_PROPERTY(MouseDoubleClickAction mouseDoubleClickAction READ mouseDoubleClickAction WRITE setMouseDoubleClickAction) + +public: + enum SelectionBehavior { + NoSelection, + SingleSelection, + MultiSelection + }; + + enum AutoActivationBehavior { + ActivationAndExpansion, + ExpansionOnly + }; + + enum MouseDoubleClickAction { + ActivateAndExpandItem, + ActivateItemOnly + }; + + /** + * @param model Model of the controller. The ownership is passed to the controller. + * @param view View of the controller. The ownership is passed to the controller. + * @param parent Optional parent object. + */ + KItemListController(KItemModelBase* model, KItemListView* view, QObject* parent = 0); + virtual ~KItemListController(); + + void setModel(KItemModelBase* model); + KItemModelBase* model() const; + + void setView(KItemListView* view); + KItemListView* view() const; + + KItemListSelectionManager* selectionManager() const; + + void setSelectionBehavior(SelectionBehavior behavior); + SelectionBehavior selectionBehavior() const; + + void setAutoActivationBehavior(AutoActivationBehavior behavior); + AutoActivationBehavior autoActivationBehavior() const; + + void setMouseDoubleClickAction(MouseDoubleClickAction action); + MouseDoubleClickAction mouseDoubleClickAction() const; + + /** + * Sets the delay in milliseconds when dragging an object above an item + * until the item gets activated automatically. A value of -1 indicates + * that no automatic activation will be done at all (= default value). + * + * The hovered item must support dropping (see KItemModelBase::supportsDropping()), + * otherwise the automatic activation is not available. + * + * After activating the item the signal itemActivated() will be + * emitted. If the view supports the expanding of items + * (KItemListView::supportsItemExpanding() returns true) and the item + * itself is expandable (see KItemModelBase::isExpandable()) then instead + * of activating the item it gets expanded instead (see + * KItemModelBase::setExpanded()). + */ + void setAutoActivationDelay(int delay); + int autoActivationDelay() const; + + /** + * If set to true, the signals itemActivated() and itemsActivated() are emitted + * after a single-click of the left mouse button. If set to false (the default), + * the setting from KGlobalSettings::singleClick() is used. + */ + void setSingleClickActivationEnforced(bool singleClick); + bool singleClickActivationEnforced() const; + + virtual bool showEvent(QShowEvent* event); + virtual bool hideEvent(QHideEvent* event); + virtual bool keyPressEvent(QKeyEvent* event); + virtual bool mousePressEvent(QGraphicsSceneMouseEvent* event, const QTransform& transform); + virtual bool mouseMoveEvent(QGraphicsSceneMouseEvent* event, const QTransform& transform); + virtual bool mouseReleaseEvent(QGraphicsSceneMouseEvent* event, const QTransform& transform); + virtual bool mouseDoubleClickEvent(QGraphicsSceneMouseEvent* event, const QTransform& transform); + virtual bool dragEnterEvent(QGraphicsSceneDragDropEvent* event, const QTransform& transform); + virtual bool dragLeaveEvent(QGraphicsSceneDragDropEvent* event, const QTransform& transform); + virtual bool dragMoveEvent(QGraphicsSceneDragDropEvent* event, const QTransform& transform); + virtual bool dropEvent(QGraphicsSceneDragDropEvent* event, const QTransform& transform); + virtual bool hoverEnterEvent(QGraphicsSceneHoverEvent* event, const QTransform& transform); + virtual bool hoverMoveEvent(QGraphicsSceneHoverEvent* event, const QTransform& transform); + virtual bool hoverLeaveEvent(QGraphicsSceneHoverEvent* event, const QTransform& transform); + virtual bool wheelEvent(QGraphicsSceneWheelEvent* event, const QTransform& transform); + virtual bool resizeEvent(QGraphicsSceneResizeEvent* event, const QTransform& transform); + virtual bool processEvent(QEvent* event, const QTransform& transform); + +signals: + /** + * Is emitted if exactly one item has been activated by e.g. a mouse-click + * or by pressing Return/Enter. + */ + void itemActivated(int index); + + /** + * Is emitted if more than one item has been activated by pressing Return/Enter + * when having a selection. + */ + void itemsActivated(const KItemSet& indexes); + + void itemMiddleClicked(int index); + + /** + * Emitted if a context-menu is requested for the item with + * the index \a index. It is assured that the index is valid. + */ + void itemContextMenuRequested(int index, const QPointF& pos); + + /** + * Emitted if a context-menu is requested for the KItemListView. + */ + void viewContextMenuRequested(const QPointF& pos); + + /** + * Emitted if a context-menu is requested for the header of the KItemListView. + */ + void headerContextMenuRequested(const QPointF& pos); + + /** + * Is emitted if the item with the index \p index gets hovered. + */ + void itemHovered(int index); + + /** + * Is emitted if the item with the index \p index gets unhovered. + * It is assured that the signal itemHovered() for this index + * has been emitted before. + */ + void itemUnhovered(int index); + + /** + * Is emitted if a mouse-button has been pressed above an item. + * If the index is smaller than 0, the mouse-button has been pressed + * above the viewport. + */ + void mouseButtonPressed(int itemIndex, Qt::MouseButtons buttons); + + /** + * Is emitted if a mouse-button has been released above an item. + * It is assured that the signal mouseButtonPressed() has been emitted before. + * If the index is smaller than 0, the mouse-button has been pressed + * above the viewport. + */ + void mouseButtonReleased(int itemIndex, Qt::MouseButtons buttons); + + void itemExpansionToggleClicked(int index); + + /** + * Is emitted if a drop event is done above the item with the index + * \a index. If \a index is < 0 the drop event is done above an + * empty area of the view. + * TODO: Introduce a new signal viewDropEvent(QGraphicsSceneDragDropEvent), + * which is emitted if the drop event occurs on an empty area in + * the view, and make sure that index is always >= 0 in itemDropEvent(). + */ + void itemDropEvent(int index, QGraphicsSceneDragDropEvent* event); + + /** + * Is emitted if a drop event is done between the item with the index + * \a index and the previous item. + */ + void aboveItemDropEvent(int index, QGraphicsSceneDragDropEvent* event); + + /** + * Is emitted if the Escape key is pressed. + */ + void escapePressed(); + + void modelChanged(KItemModelBase* current, KItemModelBase* previous); + void viewChanged(KItemListView* current, KItemListView* previous); + +private slots: + void slotViewScrollOffsetChanged(qreal current, qreal previous); + + /** + * Is invoked when the rubberband boundaries have been changed and will select + * all items that are touched by the rubberband. + */ + void slotRubberBandChanged(); + + void slotChangeCurrentItem(const QString& text, bool searchFromNextItem); + + void slotAutoActivationTimeout(); + +private: + /** + * Creates a QDrag object and initiates a drag-operation. + */ + void startDragging(); + + /** + * @return Widget that is currently in the hovered state. 0 is returned + * if no widget is marked as hovered. + */ + KItemListWidget* hoveredWidget() const; + + /** + * @return Widget that is below the position \a pos. 0 is returned + * if no widget is below the position. + */ + KItemListWidget* widgetForPos(const QPointF& pos) const; + + /** + * Updates m_keyboardAnchorIndex and m_keyboardAnchorPos. If no anchor is + * set, it will be adjusted to the current item. If it is set it will be + * checked whether it is still valid, otherwise it will be reset to the + * current item. + */ + void updateKeyboardAnchor(); + + /** + * @return Index for the next row based on \a index. + * If there is no next row \a index will be returned. + */ + int nextRowIndex(int index) const; + + /** + * @return Index for the previous row based on \a index. + * If there is no previous row \a index will be returned. + */ + int previousRowIndex(int index) const; + + /** + * Helper method for updateKeyboardAnchor(), previousRowIndex() and nextRowIndex(). + * @return The position of the keyboard anchor for the item with the index \a index. + * If a horizontal scrolling is used the y-position of the item will be returned, + * for the vertical scrolling the x-position will be returned. + */ + qreal keyboardAnchorPos(int index) const; + + /** + * Dependent on the selection-behavior the extendedSelectionRegion-property + * of the KItemListStyleOption from the view should be adjusted: If no + * rubberband selection is used the property should be enabled. + */ + void updateExtendedSelectionRegion(); + +private: + bool m_singleClickActivationEnforced; + bool m_selectionTogglePressed; + bool m_clearSelectionIfItemsAreNotDragged; + SelectionBehavior m_selectionBehavior; + AutoActivationBehavior m_autoActivationBehavior; + MouseDoubleClickAction m_mouseDoubleClickAction; + KItemModelBase* m_model; + KItemListView* m_view; + KItemListSelectionManager* m_selectionManager; + KItemListKeyboardSearchManager* m_keyboardManager; + int m_pressedIndex; + QPointF m_pressedMousePos; + + QTimer* m_autoActivationTimer; + + /** + * When starting a rubberband selection during a Shift- or Control-key has been + * pressed the current selection should never be deleted. To be able to restore + * the current selection it is remembered in m_oldSelection before the + * rubberband gets activated. + */ + KItemSet m_oldSelection; + + /** + * Assuming a view is given with a vertical scroll-orientation, grouped items and + * a maximum of 4 columns: + * + * 1 2 3 4 + * 5 6 7 + * 8 9 10 11 + * 12 13 14 + * + * If the current index is on 4 and key-down is pressed, then item 7 gets the current + * item. Now when pressing key-down again item 11 should get the current item and not + * item 10. This makes it necessary to keep track of the requested column to have a + * similar behavior like in a text editor: + */ + int m_keyboardAnchorIndex; + qreal m_keyboardAnchorPos; +}; + +#endif + + diff --git a/dolphin/src/kitemviews/kitemlistgroupheader.cpp b/dolphin/src/kitemviews/kitemlistgroupheader.cpp new file mode 100644 index 00000000..610796eb --- /dev/null +++ b/dolphin/src/kitemviews/kitemlistgroupheader.cpp @@ -0,0 +1,236 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz * + * * + * Based on the Itemviews NG project from Trolltech Labs: * + * http://qt.gitorious.org/qt-labs/itemviews-ng * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "kstandarditemlistgroupheader.h" + +#include "kitemlistview.h" + +#include +#include +#include +#include + +KItemListGroupHeader::KItemListGroupHeader(QGraphicsWidget* parent) : + QGraphicsWidget(parent, 0), + m_dirtyCache(true), + m_role(), + m_data(), + m_styleOption(), + m_scrollOrientation(Qt::Vertical), + m_itemIndex(-1), + m_separatorColor(), + m_roleColor(), + m_roleBounds() +{ +} + +KItemListGroupHeader::~KItemListGroupHeader() +{ +} + +void KItemListGroupHeader::setRole(const QByteArray& role) +{ + if (m_role != role) { + const QByteArray previous = m_role; + m_role = role; + update(); + roleChanged(role, previous); + } +} + +QByteArray KItemListGroupHeader::role() const +{ + return m_role; +} + +void KItemListGroupHeader::setData(const QVariant& data) +{ + if (m_data != data) { + const QVariant previous = m_data; + m_data = data; + update(); + dataChanged(data, previous); + } +} + +QVariant KItemListGroupHeader::data() const +{ + return m_data; +} + +void KItemListGroupHeader::setStyleOption(const KItemListStyleOption& option) +{ + const KItemListStyleOption previous = m_styleOption; + m_styleOption = option; + m_dirtyCache = true; + styleOptionChanged(option, previous); +} + +const KItemListStyleOption& KItemListGroupHeader::styleOption() const +{ + return m_styleOption; +} + +void KItemListGroupHeader::setScrollOrientation(Qt::Orientation orientation) +{ + if (m_scrollOrientation != orientation) { + const Qt::Orientation previous = m_scrollOrientation; + m_scrollOrientation = orientation; + if (orientation == Qt::Vertical) { + m_dirtyCache = true; + } + scrollOrientationChanged(orientation, previous); + } +} + +void KItemListGroupHeader::setItemIndex(int index) +{ + if (m_itemIndex != index) { + const int previous = m_itemIndex; + m_itemIndex = index; + m_dirtyCache = true; + itemIndexChanged(m_itemIndex, previous); + } +} + +int KItemListGroupHeader::itemIndex() const +{ + return m_itemIndex; +} + +Qt::Orientation KItemListGroupHeader::scrollOrientation() const +{ + return m_scrollOrientation; +} + +void KItemListGroupHeader::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) +{ + Q_UNUSED(painter); + Q_UNUSED(option); + Q_UNUSED(widget); + + if (m_dirtyCache) { + updateCache(); + } + + paintSeparator(painter, m_separatorColor); + paintRole(painter, m_roleBounds, m_roleColor); +} + +void KItemListGroupHeader::roleChanged(const QByteArray& current, const QByteArray& previous) +{ + Q_UNUSED(current); + Q_UNUSED(previous); +} + +void KItemListGroupHeader::dataChanged(const QVariant& current, const QVariant& previous) +{ + Q_UNUSED(current); + Q_UNUSED(previous); +} + +void KItemListGroupHeader::styleOptionChanged(const KItemListStyleOption& current, const KItemListStyleOption& previous) +{ + Q_UNUSED(current); + Q_UNUSED(previous); +} + +void KItemListGroupHeader::scrollOrientationChanged(Qt::Orientation current, Qt::Orientation previous) +{ + Q_UNUSED(current); + Q_UNUSED(previous); +} + +void KItemListGroupHeader::itemIndexChanged(int current, int previous) +{ + Q_UNUSED(current); + Q_UNUSED(previous); +} + +void KItemListGroupHeader::resizeEvent(QGraphicsSceneResizeEvent* event) +{ + QGraphicsWidget::resizeEvent(event); + if (event->oldSize().height() != event->newSize().height()) { + m_dirtyCache = true; + } +} + +void KItemListGroupHeader::updateCache() +{ + Q_ASSERT(m_dirtyCache); + + // Calculate the role- and line-color. No alphablending is used for + // performance reasons. + const QColor c1 = textColor(); + const QColor c2 = baseColor(); + m_separatorColor = mixedColor(c1, c2, 10); + m_roleColor = mixedColor(c1, c2, 60); + + const int padding = qMax(1, m_styleOption.padding); + const int horizontalMargin = qMax(2, m_styleOption.horizontalMargin); + + const QFontMetrics fontMetrics(m_styleOption.font); + const qreal roleHeight = fontMetrics.height(); + + const int y = (m_scrollOrientation == Qt::Vertical) ? padding : horizontalMargin; + + m_roleBounds = QRectF(horizontalMargin + padding, + y, + size().width() - 2 * padding - horizontalMargin, + roleHeight); + + m_dirtyCache = false; +} + +QColor KItemListGroupHeader::mixedColor(const QColor& c1, const QColor& c2, int c1Percent) +{ + Q_ASSERT(c1Percent >= 0 && c1Percent <= 100); + + const int c2Percent = 100 - c1Percent; + return QColor((c1.red() * c1Percent + c2.red() * c2Percent) / 100, + (c1.green() * c1Percent + c2.green() * c2Percent) / 100, + (c1.blue() * c1Percent + c2.blue() * c2Percent) / 100); +} + +QPalette::ColorRole KItemListGroupHeader::normalTextColorRole() const +{ + return QPalette::Text; +} + +QPalette::ColorRole KItemListGroupHeader::normalBaseColorRole() const +{ + return QPalette::Window; +} + +QColor KItemListGroupHeader::textColor() const +{ + const QPalette::ColorGroup group = isActiveWindow() ? QPalette::Active : QPalette::Inactive; + return styleOption().palette.color(group, normalTextColorRole()); +} + +QColor KItemListGroupHeader::baseColor() const +{ + const QPalette::ColorGroup group = isActiveWindow() ? QPalette::Active : QPalette::Inactive; + return styleOption().palette.color(group, normalBaseColorRole()); +} + +#include "moc_kitemlistgroupheader.cpp" diff --git a/dolphin/src/kitemviews/kitemlistgroupheader.h b/dolphin/src/kitemviews/kitemlistgroupheader.h new file mode 100644 index 00000000..714bec88 --- /dev/null +++ b/dolphin/src/kitemviews/kitemlistgroupheader.h @@ -0,0 +1,132 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef KITEMLISTGROUPHEADER_H +#define KITEMLISTGROUPHEADER_H + +#include + +#include + +#include +#include +#include + +class KItemListView; + +/** + * @brief Base class for group headers. + * + * Draws a default header background. Derived classes must reimplement + * the method paint() and draw the role within the given roleBounds() with + * the color roleColor(). + */ +class DOLPHINPRIVATE_EXPORT KItemListGroupHeader : public QGraphicsWidget +{ + Q_OBJECT + +public: + KItemListGroupHeader(QGraphicsWidget* parent = 0); + virtual ~KItemListGroupHeader(); + + void setRole(const QByteArray& role); + QByteArray role() const; + + void setData(const QVariant& data); + QVariant data() const; + + void setStyleOption(const KItemListStyleOption& option); + const KItemListStyleOption& styleOption() const; + + /** + * Sets the scroll orientation that is used by the KItemListView. + * This allows the group header to use a modified look dependent + * on the orientation. + */ + void setScrollOrientation(Qt::Orientation orientation); + Qt::Orientation scrollOrientation() const; + + void setItemIndex(int index); + int itemIndex() const; + + virtual void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget = 0); + +protected: + virtual void paintRole(QPainter* painter, const QRectF& roleBounds, const QColor& color) = 0; + virtual void paintSeparator(QPainter* painter, const QColor& color) = 0; + + /** + * Is called after the role has been changed and allows the derived class + * to react on this change. + */ + virtual void roleChanged(const QByteArray& current, const QByteArray& previous); + + /** + * Is called after the role has been changed and allows the derived class + * to react on this change. + */ + virtual void dataChanged(const QVariant& current, const QVariant& previous); + + /** + * Is called after the style option has been changed and allows the derived class + * to react on this change. + */ + virtual void styleOptionChanged(const KItemListStyleOption& current, const KItemListStyleOption& previous); + + /** + * Is called after the scroll orientation has been changed and allows the derived class + * to react on this change. + */ + virtual void scrollOrientationChanged(Qt::Orientation current, Qt::Orientation previous); + + /** + * Is called after the item index has been changed and allows the derived class to react on + * this change. + */ + virtual void itemIndexChanged(int current, int previous); + + /** @reimp */ + virtual void resizeEvent(QGraphicsSceneResizeEvent* event); + + virtual QPalette::ColorRole normalTextColorRole() const; + virtual QPalette::ColorRole normalBaseColorRole() const; + +private: + void updateCache(); + + static QColor mixedColor(const QColor& c1, const QColor& c2, int c1Percent = 50); + + QColor textColor() const; + QColor baseColor() const; + +private: + bool m_dirtyCache; + QByteArray m_role; + QVariant m_data; + KItemListStyleOption m_styleOption; + Qt::Orientation m_scrollOrientation; + int m_itemIndex; + + QColor m_separatorColor; + QColor m_roleColor; + QRectF m_roleBounds; +}; +#endif + + diff --git a/dolphin/src/kitemviews/kitemlistheader.cpp b/dolphin/src/kitemviews/kitemlistheader.cpp new file mode 100644 index 00000000..bb41c551 --- /dev/null +++ b/dolphin/src/kitemviews/kitemlistheader.cpp @@ -0,0 +1,88 @@ +/*************************************************************************** + * Copyright (C) 2012 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "kitemlistheader.h" +#include "kitemlistview.h" + +#include "private/kitemlistheaderwidget.h" + +KItemListHeader::~KItemListHeader() +{ +} + +void KItemListHeader::setAutomaticColumnResizing(bool automatic) +{ + if (m_headerWidget->automaticColumnResizing() != automatic) { + m_headerWidget->setAutomaticColumnResizing(automatic); + if (automatic) { + m_view->applyAutomaticColumnWidths(); + m_view->doLayout(KItemListView::NoAnimation); + } + } +} + +bool KItemListHeader::automaticColumnResizing() const +{ + return m_headerWidget->automaticColumnResizing(); +} + +void KItemListHeader::setColumnWidth(const QByteArray& role, qreal width) +{ + if (!m_headerWidget->automaticColumnResizing()) { + m_headerWidget->setColumnWidth(role, width); + m_view->applyColumnWidthsFromHeader(); + m_view->doLayout(KItemListView::NoAnimation); + } +} + +qreal KItemListHeader::columnWidth(const QByteArray& role) const +{ + return m_headerWidget->columnWidth(role); +} + +void KItemListHeader::setColumnWidths(const QHash& columnWidths) +{ + if (!m_headerWidget->automaticColumnResizing()) { + foreach (const QByteArray& role, m_view->visibleRoles()) { + const qreal width = columnWidths.value(role); + m_headerWidget->setColumnWidth(role, width); + } + + m_view->applyColumnWidthsFromHeader(); + m_view->doLayout(KItemListView::NoAnimation); + } +} + +qreal KItemListHeader::preferredColumnWidth(const QByteArray& role) const +{ + return m_headerWidget->preferredColumnWidth(role); +} + +KItemListHeader::KItemListHeader(KItemListView* listView) : + QObject(listView->parent()), + m_view(listView) +{ + m_headerWidget = m_view->m_headerWidget; + Q_ASSERT(m_headerWidget); + + connect(m_headerWidget, SIGNAL(columnWidthChanged(QByteArray,qreal,qreal)), + this, SIGNAL(columnWidthChanged(QByteArray,qreal,qreal))); +} + +#include "moc_kitemlistheader.cpp" diff --git a/dolphin/src/kitemviews/kitemlistheader.h b/dolphin/src/kitemviews/kitemlistheader.h new file mode 100644 index 00000000..2c66959b --- /dev/null +++ b/dolphin/src/kitemviews/kitemlistheader.h @@ -0,0 +1,94 @@ +/*************************************************************************** + * Copyright (C) 2012 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef KITEMLISTHEADER_H +#define KITEMLISTHEADER_H + +#include +#include +#include + +class KItemListHeaderWidget; +class KItemListView; + +/** + * @brief Provides access to the header of a KItemListView. + * + * Each column of the header represents a visible role + * accessible by KItemListView::visibleRoles(). + */ +class DOLPHINPRIVATE_EXPORT KItemListHeader : public QObject +{ + Q_OBJECT + +public: + virtual ~KItemListHeader(); + + /** + * If set to true, KItemListView will automatically adjust the + * widths of the columns. If set to false, the size can be + * manually adjusted by KItemListHeader::setColumnWidth(). + */ + void setAutomaticColumnResizing(bool automatic); + bool automaticColumnResizing() const; + + /** + * Sets the width of the column for the given role. Note that + * the width only gets applied if KItemListHeader::automaticColumnResizing() + * has been turned off. + */ + void setColumnWidth(const QByteArray& role, qreal width); + qreal columnWidth(const QByteArray& role) const; + + /** + * Sets the widths of the columns for all roles. From a performance point of + * view calling this method should be preferred over several setColumnWidth() + * calls in case if the width of more than one column should be changed. + * Note that the widths only get applied if KItemListHeader::automaticColumnResizing() + * has been turned off. + */ + void setColumnWidths(const QHash& columnWidths); + + /** + * @return The column width that is required to show the role unclipped. + */ + qreal preferredColumnWidth(const QByteArray& role) const; + +signals: + /** + * Is emitted if the width of a column has been adjusted by the user with the mouse + * (no signal is emitted if KItemListHeader::setColumnWidth() is invoked). + */ + void columnWidthChanged(const QByteArray& role, + qreal currentWidth, + qreal previousWidth); + +private: + KItemListHeader(KItemListView* listView); + +private: + KItemListView* m_view; + KItemListHeaderWidget* m_headerWidget; + + friend class KItemListView; // Constructs the KItemListHeader instance +}; + +#endif + + diff --git a/dolphin/src/kitemviews/kitemlistselectionmanager.cpp b/dolphin/src/kitemviews/kitemlistselectionmanager.cpp new file mode 100644 index 00000000..09bdc87c --- /dev/null +++ b/dolphin/src/kitemviews/kitemlistselectionmanager.cpp @@ -0,0 +1,399 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz * + * Copyright (C) 2011 by Frank Reininghaus * + * * + * Based on the Itemviews NG project from Trolltech Labs: * + * http://qt.gitorious.org/qt-labs/itemviews-ng * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "kitemlistselectionmanager.h" + +#include "kitemmodelbase.h" +#include + +KItemListSelectionManager::KItemListSelectionManager(QObject* parent) : + QObject(parent), + m_currentItem(-1), + m_anchorItem(-1), + m_selectedItems(), + m_isAnchoredSelectionActive(false), + m_model(0) +{ +} + +KItemListSelectionManager::~KItemListSelectionManager() +{ +} + +void KItemListSelectionManager::setCurrentItem(int current) +{ + const int previous = m_currentItem; + const KItemSet previousSelection = selectedItems(); + + if (m_model && current >= 0 && current < m_model->count()) { + m_currentItem = current; + } else { + m_currentItem = -1; + } + + if (m_currentItem != previous) { + emit currentChanged(m_currentItem, previous); + + if (m_isAnchoredSelectionActive) { + const KItemSet selection = selectedItems(); + if (selection != previousSelection) { + emit selectionChanged(selection, previousSelection); + } + } + } +} + +int KItemListSelectionManager::currentItem() const +{ + return m_currentItem; +} + +void KItemListSelectionManager::setSelectedItems(const KItemSet& items) +{ + if (m_selectedItems != items) { + const KItemSet previous = m_selectedItems; + m_selectedItems = items; + emit selectionChanged(m_selectedItems, previous); + } +} + +KItemSet KItemListSelectionManager::selectedItems() const +{ + KItemSet selectedItems = m_selectedItems; + + if (m_isAnchoredSelectionActive && m_anchorItem != m_currentItem) { + Q_ASSERT(m_anchorItem >= 0); + Q_ASSERT(m_currentItem >= 0); + const int from = qMin(m_anchorItem, m_currentItem); + const int to = qMax(m_anchorItem, m_currentItem); + + for (int index = from; index <= to; ++index) { + selectedItems.insert(index); + } + } + + return selectedItems; +} + +bool KItemListSelectionManager::isSelected(int index) const +{ + if (m_selectedItems.contains(index)) { + return true; + } + + if (m_isAnchoredSelectionActive && m_anchorItem != m_currentItem) { + Q_ASSERT(m_anchorItem >= 0); + Q_ASSERT(m_currentItem >= 0); + const int from = qMin(m_anchorItem, m_currentItem); + const int to = qMax(m_anchorItem, m_currentItem); + + if (from <= index && index <= to) { + return true; + } + } + + return false; +} + +bool KItemListSelectionManager::hasSelection() const +{ + return !m_selectedItems.isEmpty() || (m_isAnchoredSelectionActive && m_anchorItem != m_currentItem); +} + +void KItemListSelectionManager::setSelected(int index, int count, SelectionMode mode) +{ + if (index < 0 || count < 1 || !m_model || index >= m_model->count()) { + return; + } + + endAnchoredSelection(); + const KItemSet previous = selectedItems(); + + count = qMin(count, m_model->count() - index); + + const int endIndex = index + count -1; + switch (mode) { + case Select: + for (int i = index; i <= endIndex; ++i) { + m_selectedItems.insert(i); + } + break; + + case Deselect: + for (int i = index; i <= endIndex; ++i) { + m_selectedItems.remove(i); + } + break; + + case Toggle: + for (int i = index; i <= endIndex; ++i) { + if (m_selectedItems.contains(i)) { + m_selectedItems.remove(i); + } else { + m_selectedItems.insert(i); + } + } + break; + + default: + Q_ASSERT(false); + break; + } + + const KItemSet selection = selectedItems(); + if (selection != previous) { + emit selectionChanged(selection, previous); + } +} + +void KItemListSelectionManager::clearSelection() +{ + const KItemSet previous = selectedItems(); + if (!previous.isEmpty()) { + m_selectedItems.clear(); + m_isAnchoredSelectionActive = false; + emit selectionChanged(KItemSet(), previous); + } +} + +void KItemListSelectionManager::beginAnchoredSelection(int anchor) +{ + if (anchor >= 0 && m_model && anchor < m_model->count()) { + m_isAnchoredSelectionActive = true; + m_anchorItem = anchor; + } +} + +void KItemListSelectionManager::endAnchoredSelection() +{ + if (m_isAnchoredSelectionActive && (m_anchorItem != m_currentItem)) { + Q_ASSERT(m_anchorItem >= 0); + Q_ASSERT(m_currentItem >= 0); + const int from = qMin(m_anchorItem, m_currentItem); + const int to = qMax(m_anchorItem, m_currentItem); + + for (int index = from; index <= to; ++index) { + m_selectedItems.insert(index); + } + } + + m_isAnchoredSelectionActive = false; +} + +bool KItemListSelectionManager::isAnchoredSelectionActive() const +{ + return m_isAnchoredSelectionActive; +} + +KItemModelBase* KItemListSelectionManager::model() const +{ + return m_model; +} + +void KItemListSelectionManager::setModel(KItemModelBase* model) +{ + m_model = model; + if (model && model->count() > 0) { + m_currentItem = 0; + } +} + +void KItemListSelectionManager::itemsInserted(const KItemRangeList& itemRanges) +{ + // Store the current selection (needed in the selectionChanged() signal) + const KItemSet previousSelection = selectedItems(); + + // Update the current item + if (m_currentItem < 0) { + setCurrentItem(0); + } else { + const int previousCurrent = m_currentItem; + int inc = 0; + foreach (const KItemRange& itemRange, itemRanges) { + if (m_currentItem < itemRange.index) { + break; + } + inc += itemRange.count; + } + // Calling setCurrentItem would trigger the selectionChanged signal, but we want to + // emit it only once in this function -> change the current item manually and emit currentChanged + m_currentItem += inc; + emit currentChanged(m_currentItem, previousCurrent); + } + + // Update the anchor item + if (m_anchorItem < 0) { + m_anchorItem = 0; + } else { + int inc = 0; + foreach (const KItemRange& itemRange, itemRanges) { + if (m_anchorItem < itemRange.index) { + break; + } + inc += itemRange.count; + } + m_anchorItem += inc; + } + + // Update the selections + if (!m_selectedItems.isEmpty()) { + const KItemSet previous = m_selectedItems; + m_selectedItems.clear(); + + foreach (int index, previous) { + int inc = 0; + foreach (const KItemRange& itemRange, itemRanges) { + if (index < itemRange.index) { + break; + } + inc += itemRange.count; + } + m_selectedItems.insert(index + inc); + } + } + + const KItemSet selection = selectedItems(); + if (selection != previousSelection) { + emit selectionChanged(selection, previousSelection); + } +} + +void KItemListSelectionManager::itemsRemoved(const KItemRangeList& itemRanges) +{ + // Store the current selection (needed in the selectionChanged() signal) + const KItemSet previousSelection = selectedItems(); + const int previousCurrent = m_currentItem; + + // Update the current item + m_currentItem = indexAfterRangesRemoving(m_currentItem, itemRanges, DiscardRemovedIndex); + if (m_currentItem != previousCurrent) { + emit currentChanged(m_currentItem, previousCurrent); + if (m_currentItem < 0) { + // Calling setCurrentItem() would trigger the selectionChanged signal, but we want to + // emit it only once in this function -> change the current item manually and emit currentChanged + m_currentItem = indexAfterRangesRemoving(previousCurrent, itemRanges, AdjustRemovedIndex); + emit currentChanged(m_currentItem, -1); + } + } + + // Update the anchor item + if (m_anchorItem >= 0) { + m_anchorItem = indexAfterRangesRemoving(m_anchorItem, itemRanges, DiscardRemovedIndex); + if (m_anchorItem < 0) { + m_isAnchoredSelectionActive = false; + } + } + + // Update the selections and the anchor item + if (!m_selectedItems.isEmpty()) { + const KItemSet previous = m_selectedItems; + m_selectedItems.clear(); + + foreach (int oldIndex, previous) { + const int index = indexAfterRangesRemoving(oldIndex, itemRanges, DiscardRemovedIndex); + if (index >= 0) { + m_selectedItems.insert(index); + } + } + } + + const KItemSet selection = selectedItems(); + if (selection != previousSelection) { + emit selectionChanged(selection, previousSelection); + } + + Q_ASSERT(m_currentItem < m_model->count()); + Q_ASSERT(m_anchorItem < m_model->count()); +} + +void KItemListSelectionManager::itemsMoved(const KItemRange& itemRange, const QList& movedToIndexes) +{ + // Store the current selection (needed in the selectionChanged() signal) + const KItemSet previousSelection = selectedItems(); + + // endAnchoredSelection() adds all items between m_currentItem and + // m_anchorItem to m_selectedItems. They can then be moved + // individually later in this function. + endAnchoredSelection(); + + // Update the current item + if (m_currentItem >= itemRange.index && m_currentItem < itemRange.index + itemRange.count) { + const int previousCurrentItem = m_currentItem; + const int newCurrentItem = movedToIndexes.at(previousCurrentItem - itemRange.index); + + // Calling setCurrentItem would trigger the selectionChanged signal, but we want to + // emit it only once in this function -> change the current item manually and emit currentChanged + m_currentItem = newCurrentItem; + emit currentChanged(newCurrentItem, previousCurrentItem); + } + + // Start a new anchored selection. + beginAnchoredSelection(m_currentItem); + + // Update the selections + if (!m_selectedItems.isEmpty()) { + const KItemSet previous = m_selectedItems; + m_selectedItems.clear(); + + foreach (int index, previous) { + if (index >= itemRange.index && index < itemRange.index + itemRange.count) { + m_selectedItems.insert(movedToIndexes.at(index - itemRange.index)); + } + else { + m_selectedItems.insert(index); + } + } + } + + const KItemSet selection = selectedItems(); + if (selection != previousSelection) { + emit selectionChanged(selection, previousSelection); + } +} + +int KItemListSelectionManager::indexAfterRangesRemoving(int index, const KItemRangeList& itemRanges, + const RangesRemovingBehaviour behaviour) const +{ + int dec = 0; + foreach (const KItemRange& itemRange, itemRanges) { + if (index < itemRange.index) { + break; + } + + dec += itemRange.count; + + const int firstIndexAfterRange = itemRange.index + itemRange.count; + if (index < firstIndexAfterRange) { + // The index is part of the removed range + if (behaviour == DiscardRemovedIndex) { + return -1; + } else { + // Use the first item after the range as new index + index = firstIndexAfterRange; + break; + } + } + } + return qBound(-1, index - dec, m_model->count() - 1); +} +#include "moc_kitemlistselectionmanager.cpp" diff --git a/dolphin/src/kitemviews/kitemlistselectionmanager.h b/dolphin/src/kitemviews/kitemlistselectionmanager.h new file mode 100644 index 00000000..1742d307 --- /dev/null +++ b/dolphin/src/kitemviews/kitemlistselectionmanager.h @@ -0,0 +1,104 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz * + * * + * Based on the Itemviews NG project from Trolltech Labs: * + * http://qt.gitorious.org/qt-labs/itemviews-ng * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef KITEMLISTSELECTIONMANAGER_H +#define KITEMLISTSELECTIONMANAGER_H + +#include + +#include +#include + +#include + +class KItemModelBase; + +/** + * @brief Allows to select and deselect items of a KItemListView. + */ +class DOLPHINPRIVATE_EXPORT KItemListSelectionManager : public QObject +{ + Q_OBJECT + + enum RangesRemovingBehaviour { + DiscardRemovedIndex, + AdjustRemovedIndex + }; + +public: + enum SelectionMode { + Select, + Deselect, + Toggle + }; + + KItemListSelectionManager(QObject* parent = 0); + virtual ~KItemListSelectionManager(); + + void setCurrentItem(int current); + int currentItem() const; + + void setSelectedItems(const KItemSet& items); + KItemSet selectedItems() const; + bool isSelected(int index) const; + bool hasSelection() const; + + void setSelected(int index, int count = 1, SelectionMode mode = Select); + void clearSelection(); + + void beginAnchoredSelection(int anchor); + void endAnchoredSelection(); + bool isAnchoredSelectionActive() const; + + KItemModelBase* model() const; + +signals: + void currentChanged(int current, int previous); + void selectionChanged(const KItemSet& current, const KItemSet& previous); + +private: + void setModel(KItemModelBase* model); + void itemsInserted(const KItemRangeList& itemRanges); + void itemsRemoved(const KItemRangeList& itemRanges); + void itemsMoved(const KItemRange& itemRange, const QList& movedToIndexes); + + + /** + * Helper method for itemsRemoved. Returns the changed index after removing + * the given range. If the index is part of the range, -1 will be returned. + */ + int indexAfterRangesRemoving(int index, const KItemRangeList& itemRanges, const RangesRemovingBehaviour behaviour) const; + +private: + int m_currentItem; + int m_anchorItem; + KItemSet m_selectedItems; + bool m_isAnchoredSelectionActive; + + KItemModelBase* m_model; + + friend class KItemListController; // Calls setModel() + friend class KItemListView; // Calls itemsInserted(), itemsRemoved() and itemsMoved() + friend class KItemListSelectionManagerTest; +}; + +#endif diff --git a/dolphin/src/kitemviews/kitemliststyleoption.cpp b/dolphin/src/kitemviews/kitemliststyleoption.cpp new file mode 100644 index 00000000..edd6363c --- /dev/null +++ b/dolphin/src/kitemviews/kitemliststyleoption.cpp @@ -0,0 +1,56 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "kitemliststyleoption.h" + +#include + +KItemListStyleOption::KItemListStyleOption() : + rect(), + font(), + fontMetrics(QFont()), + palette(), + padding(-1), + horizontalMargin(-1), + verticalMargin(-1), + iconSize(-1), + extendedSelectionRegion(false), + maxTextLines(0), + maxTextWidth(0) +{ +} + +KItemListStyleOption::KItemListStyleOption(const KItemListStyleOption& other) : + rect(other.rect), + font(other.font), + fontMetrics(other.fontMetrics), + palette(other.palette), + padding(other.padding), + horizontalMargin(other.horizontalMargin), + verticalMargin(other.verticalMargin), + iconSize(other.iconSize), + extendedSelectionRegion(other.extendedSelectionRegion), + maxTextLines(other.maxTextLines), + maxTextWidth(other.maxTextWidth) +{ +} + +KItemListStyleOption::~KItemListStyleOption() +{ +} diff --git a/dolphin/src/kitemviews/kitemliststyleoption.h b/dolphin/src/kitemviews/kitemliststyleoption.h new file mode 100644 index 00000000..6c74d433 --- /dev/null +++ b/dolphin/src/kitemviews/kitemliststyleoption.h @@ -0,0 +1,51 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef KITEMLISTSTYLEOPTION_H +#define KITEMLISTSTYLEOPTION_H + +#include + +#include +#include +#include +#include + +class DOLPHINPRIVATE_EXPORT KItemListStyleOption +{ +public: + KItemListStyleOption(); + KItemListStyleOption(const KItemListStyleOption& other); + virtual ~KItemListStyleOption(); + + QRect rect; + QFont font; + QFontMetrics fontMetrics; + QPalette palette; + int padding; + int horizontalMargin; + int verticalMargin; + int iconSize; + bool extendedSelectionRegion; + int maxTextLines; + int maxTextWidth; +}; +#endif + + diff --git a/dolphin/src/kitemviews/kitemlistview.cpp b/dolphin/src/kitemviews/kitemlistview.cpp new file mode 100644 index 00000000..7d575505 --- /dev/null +++ b/dolphin/src/kitemviews/kitemlistview.cpp @@ -0,0 +1,2700 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz * + * * + * Based on the Itemviews NG project from Trolltech Labs: * + * http://qt.gitorious.org/qt-labs/itemviews-ng * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "kitemlistview.h" + +#include +#include "kitemlistcontainer.h" +#include "kitemlistcontroller.h" +#include "kitemlistheader.h" +#include "kitemlistselectionmanager.h" +#include "kitemlistwidget.h" + +#include "private/kitemlistheaderwidget.h" +#include "private/kitemlistrubberband.h" +#include "private/kitemlistsizehintresolver.h" +#include "private/kitemlistviewlayouter.h" +#include "private/kitemlistviewanimation.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace { + // Time in ms until reaching the autoscroll margin triggers + // an initial autoscrolling + const int InitialAutoScrollDelay = 700; + + // Delay in ms for triggering the next autoscroll + const int RepeatingAutoScrollDelay = 1000 / 60; +} + +KItemListView::KItemListView(QGraphicsWidget* parent) : + QGraphicsWidget(parent), + m_enabledSelectionToggles(false), + m_grouped(false), + m_supportsItemExpanding(false), + m_editingRole(false), + m_activeTransactions(0), + m_endTransactionAnimationHint(Animation), + m_itemSize(), + m_controller(0), + m_model(0), + m_visibleRoles(), + m_widgetCreator(0), + m_groupHeaderCreator(0), + m_styleOption(), + m_visibleItems(), + m_visibleGroups(), + m_visibleCells(), + m_sizeHintResolver(0), + m_layouter(0), + m_animation(0), + m_layoutTimer(0), + m_oldScrollOffset(0), + m_oldMaximumScrollOffset(0), + m_oldItemOffset(0), + m_oldMaximumItemOffset(0), + m_skipAutoScrollForRubberBand(false), + m_rubberBand(0), + m_mousePos(), + m_autoScrollIncrement(0), + m_autoScrollTimer(0), + m_header(0), + m_headerWidget(0), + m_dropIndicator() +{ + setAcceptHoverEvents(true); + + m_sizeHintResolver = new KItemListSizeHintResolver(this); + + m_layouter = new KItemListViewLayouter(m_sizeHintResolver, this); + + m_animation = new KItemListViewAnimation(this); + connect(m_animation, SIGNAL(finished(QGraphicsWidget*,KItemListViewAnimation::AnimationType)), + this, SLOT(slotAnimationFinished(QGraphicsWidget*,KItemListViewAnimation::AnimationType))); + + m_layoutTimer = new QTimer(this); + m_layoutTimer->setInterval(300); + m_layoutTimer->setSingleShot(true); + connect(m_layoutTimer, SIGNAL(timeout()), this, SLOT(slotLayoutTimerFinished())); + + m_rubberBand = new KItemListRubberBand(this); + connect(m_rubberBand, SIGNAL(activationChanged(bool)), this, SLOT(slotRubberBandActivationChanged(bool))); + + m_headerWidget = new KItemListHeaderWidget(this); + m_headerWidget->setVisible(false); + + m_header = new KItemListHeader(this); +} + +KItemListView::~KItemListView() +{ + // The group headers are children of the widgets created by + // widgetCreator(). So it is mandatory to delete the group headers + // first. + delete m_groupHeaderCreator; + m_groupHeaderCreator = 0; + + delete m_widgetCreator; + m_widgetCreator = 0; + + delete m_sizeHintResolver; + m_sizeHintResolver = 0; +} + +void KItemListView::setScrollOffset(qreal offset) +{ + if (offset < 0) { + offset = 0; + } + + const qreal previousOffset = m_layouter->scrollOffset(); + if (offset == previousOffset) { + return; + } + + m_layouter->setScrollOffset(offset); + m_animation->setScrollOffset(offset); + + // Don't check whether the m_layoutTimer is active: Changing the + // scroll offset must always trigger a synchronous layout, otherwise + // the smooth-scrolling might get jerky. + doLayout(NoAnimation); + onScrollOffsetChanged(offset, previousOffset); +} + +qreal KItemListView::scrollOffset() const +{ + return m_layouter->scrollOffset(); +} + +qreal KItemListView::maximumScrollOffset() const +{ + return m_layouter->maximumScrollOffset(); +} + +void KItemListView::setItemOffset(qreal offset) +{ + if (m_layouter->itemOffset() == offset) { + return; + } + + m_layouter->setItemOffset(offset); + if (m_headerWidget->isVisible()) { + m_headerWidget->setOffset(offset); + } + + // Don't check whether the m_layoutTimer is active: Changing the + // item offset must always trigger a synchronous layout, otherwise + // the smooth-scrolling might get jerky. + doLayout(NoAnimation); +} + +qreal KItemListView::itemOffset() const +{ + return m_layouter->itemOffset(); +} + +qreal KItemListView::maximumItemOffset() const +{ + return m_layouter->maximumItemOffset(); +} + +int KItemListView::maximumVisibleItems() const +{ + return m_layouter->maximumVisibleItems(); +} + +void KItemListView::setVisibleRoles(const QList& roles) +{ + const QList previousRoles = m_visibleRoles; + m_visibleRoles = roles; + onVisibleRolesChanged(roles, previousRoles); + + m_sizeHintResolver->clearCache(); + m_layouter->markAsDirty(); + + if (m_itemSize.isEmpty()) { + m_headerWidget->setColumns(roles); + updatePreferredColumnWidths(); + if (!m_headerWidget->automaticColumnResizing()) { + // The column-width of new roles are still 0. Apply the preferred + // column-width as default with. + foreach (const QByteArray& role, m_visibleRoles) { + if (m_headerWidget->columnWidth(role) == 0) { + const qreal width = m_headerWidget->preferredColumnWidth(role); + m_headerWidget->setColumnWidth(role, width); + } + } + + applyColumnWidthsFromHeader(); + } + } + + const bool alternateBackgroundsChanged = m_itemSize.isEmpty() && + ((roles.count() > 1 && previousRoles.count() <= 1) || + (roles.count() <= 1 && previousRoles.count() > 1)); + + QHashIterator it(m_visibleItems); + while (it.hasNext()) { + it.next(); + KItemListWidget* widget = it.value(); + widget->setVisibleRoles(roles); + if (alternateBackgroundsChanged) { + updateAlternateBackgroundForWidget(widget); + } + } + + doLayout(NoAnimation); +} + +QList KItemListView::visibleRoles() const +{ + return m_visibleRoles; +} + +void KItemListView::setAutoScroll(bool enabled) +{ + if (enabled && !m_autoScrollTimer) { + m_autoScrollTimer = new QTimer(this); + m_autoScrollTimer->setSingleShot(true); + connect(m_autoScrollTimer, SIGNAL(timeout()), this, SLOT(triggerAutoScrolling())); + m_autoScrollTimer->start(InitialAutoScrollDelay); + } else if (!enabled && m_autoScrollTimer) { + delete m_autoScrollTimer; + m_autoScrollTimer = 0; + } +} + +bool KItemListView::autoScroll() const +{ + return m_autoScrollTimer != 0; +} + +void KItemListView::setEnabledSelectionToggles(bool enabled) +{ + if (m_enabledSelectionToggles != enabled) { + m_enabledSelectionToggles = enabled; + + QHashIterator it(m_visibleItems); + while (it.hasNext()) { + it.next(); + it.value()->setEnabledSelectionToggle(enabled); + } + } +} + +bool KItemListView::enabledSelectionToggles() const +{ + return m_enabledSelectionToggles; +} + +KItemListController* KItemListView::controller() const +{ + return m_controller; +} + +KItemModelBase* KItemListView::model() const +{ + return m_model; +} + +void KItemListView::setWidgetCreator(KItemListWidgetCreatorBase* widgetCreator) +{ + if (m_widgetCreator) { + delete m_widgetCreator; + } + m_widgetCreator = widgetCreator; +} + +KItemListWidgetCreatorBase* KItemListView::widgetCreator() const +{ + if (!m_widgetCreator) { + m_widgetCreator = defaultWidgetCreator(); + } + return m_widgetCreator; +} + +void KItemListView::setGroupHeaderCreator(KItemListGroupHeaderCreatorBase* groupHeaderCreator) +{ + if (m_groupHeaderCreator) { + delete m_groupHeaderCreator; + } + m_groupHeaderCreator = groupHeaderCreator; +} + +KItemListGroupHeaderCreatorBase* KItemListView::groupHeaderCreator() const +{ + if (!m_groupHeaderCreator) { + m_groupHeaderCreator = defaultGroupHeaderCreator(); + } + return m_groupHeaderCreator; +} + +QSizeF KItemListView::itemSize() const +{ + return m_itemSize; +} + +const KItemListStyleOption& KItemListView::styleOption() const +{ + return m_styleOption; +} + +void KItemListView::setGeometry(const QRectF& rect) +{ + QGraphicsWidget::setGeometry(rect); + + if (!m_model) { + return; + } + + const QSizeF newSize = rect.size(); + if (m_itemSize.isEmpty()) { + m_headerWidget->resize(rect.width(), m_headerWidget->size().height()); + if (m_headerWidget->automaticColumnResizing()) { + applyAutomaticColumnWidths(); + } else { + const qreal requiredWidth = columnWidthsSum(); + const QSizeF dynamicItemSize(qMax(newSize.width(), requiredWidth), + m_itemSize.height()); + m_layouter->setItemSize(dynamicItemSize); + } + + // Triggering a synchronous layout is fine from a performance point of view, + // as with dynamic item sizes no moving animation must be done. + m_layouter->setSize(newSize); + doLayout(NoAnimation); + } else { + const bool animate = !changesItemGridLayout(newSize, + m_layouter->itemSize(), + m_layouter->itemMargin()); + m_layouter->setSize(newSize); + + if (animate) { + // Trigger an asynchronous relayout with m_layoutTimer to prevent + // performance bottlenecks. If the timer is exceeded, an animated layout + // will be triggered. + if (!m_layoutTimer->isActive()) { + m_layoutTimer->start(); + } + } else { + m_layoutTimer->stop(); + doLayout(NoAnimation); + } + } +} + +qreal KItemListView::verticalPageStep() const +{ + qreal headerHeight = 0; + if (m_headerWidget->isVisible()) { + headerHeight = m_headerWidget->size().height(); + } + return size().height() - headerHeight; +} + +int KItemListView::itemAt(const QPointF& pos) const +{ + QHashIterator it(m_visibleItems); + while (it.hasNext()) { + it.next(); + + const KItemListWidget* widget = it.value(); + const QPointF mappedPos = widget->mapFromItem(this, pos); + if (widget->contains(mappedPos)) { + return it.key(); + } + } + + return -1; +} + +bool KItemListView::isAboveSelectionToggle(int index, const QPointF& pos) const +{ + if (!m_enabledSelectionToggles) { + return false; + } + + const KItemListWidget* widget = m_visibleItems.value(index); + if (widget) { + const QRectF selectionToggleRect = widget->selectionToggleRect(); + if (!selectionToggleRect.isEmpty()) { + const QPointF mappedPos = widget->mapFromItem(this, pos); + return selectionToggleRect.contains(mappedPos); + } + } + return false; +} + +bool KItemListView::isAboveExpansionToggle(int index, const QPointF& pos) const +{ + const KItemListWidget* widget = m_visibleItems.value(index); + if (widget) { + const QRectF expansionToggleRect = widget->expansionToggleRect(); + if (!expansionToggleRect.isEmpty()) { + const QPointF mappedPos = widget->mapFromItem(this, pos); + return expansionToggleRect.contains(mappedPos); + } + } + return false; +} + +int KItemListView::firstVisibleIndex() const +{ + return m_layouter->firstVisibleIndex(); +} + +int KItemListView::lastVisibleIndex() const +{ + return m_layouter->lastVisibleIndex(); +} + +void KItemListView::calculateItemSizeHints(QVector& logicalHeightHints, qreal& logicalWidthHint) const +{ + widgetCreator()->calculateItemSizeHints(logicalHeightHints, logicalWidthHint, this); +} + +void KItemListView::setSupportsItemExpanding(bool supportsExpanding) +{ + if (m_supportsItemExpanding != supportsExpanding) { + m_supportsItemExpanding = supportsExpanding; + updateSiblingsInformation(); + onSupportsItemExpandingChanged(supportsExpanding); + } +} + +bool KItemListView::supportsItemExpanding() const +{ + return m_supportsItemExpanding; +} + +QRectF KItemListView::itemRect(int index) const +{ + return m_layouter->itemRect(index); +} + +QRectF KItemListView::itemContextRect(int index) const +{ + QRectF contextRect; + + const KItemListWidget* widget = m_visibleItems.value(index); + if (widget) { + contextRect = widget->iconRect() | widget->textRect(); + contextRect.translate(itemRect(index).topLeft()); + } + + return contextRect; +} + +void KItemListView::scrollToItem(int index) +{ + QRectF viewGeometry = geometry(); + if (m_headerWidget->isVisible()) { + const qreal headerHeight = m_headerWidget->size().height(); + viewGeometry.adjust(0, headerHeight, 0, 0); + } + QRectF currentRect = itemRect(index); + + // Fix for Bug 311099 - View the underscore when using Ctrl + PagDown + currentRect.adjust(-m_styleOption.horizontalMargin, -m_styleOption.verticalMargin, + m_styleOption.horizontalMargin, m_styleOption.verticalMargin); + + if (!viewGeometry.contains(currentRect)) { + qreal newOffset = scrollOffset(); + if (scrollOrientation() == Qt::Vertical) { + if (currentRect.top() < viewGeometry.top()) { + newOffset += currentRect.top() - viewGeometry.top(); + } else if (currentRect.bottom() > viewGeometry.bottom()) { + newOffset += currentRect.bottom() - viewGeometry.bottom(); + } + } else { + if (currentRect.left() < viewGeometry.left()) { + newOffset += currentRect.left() - viewGeometry.left(); + } else if (currentRect.right() > viewGeometry.right()) { + newOffset += currentRect.right() - viewGeometry.right(); + } + } + + if (newOffset != scrollOffset()) { + emit scrollTo(newOffset); + } + } +} + +void KItemListView::beginTransaction() +{ + ++m_activeTransactions; + if (m_activeTransactions == 1) { + onTransactionBegin(); + } +} + +void KItemListView::endTransaction() +{ + --m_activeTransactions; + if (m_activeTransactions < 0) { + m_activeTransactions = 0; + kWarning() << "Mismatch between beginTransaction()/endTransaction()"; + } + + if (m_activeTransactions == 0) { + onTransactionEnd(); + doLayout(m_endTransactionAnimationHint); + m_endTransactionAnimationHint = Animation; + } +} + +bool KItemListView::isTransactionActive() const +{ + return m_activeTransactions > 0; +} + +void KItemListView::setHeaderVisible(bool visible) +{ + if (visible && !m_headerWidget->isVisible()) { + QStyleOptionHeader option; + const QSize headerSize = style()->sizeFromContents(QStyle::CT_HeaderSection, + &option, QSize()); + + m_headerWidget->setPos(0, 0); + m_headerWidget->resize(size().width(), headerSize.height()); + m_headerWidget->setModel(m_model); + m_headerWidget->setColumns(m_visibleRoles); + m_headerWidget->setZValue(1); + + connect(m_headerWidget, SIGNAL(columnWidthChanged(QByteArray,qreal,qreal)), + this, SLOT(slotHeaderColumnWidthChanged(QByteArray,qreal,qreal))); + connect(m_headerWidget, SIGNAL(columnMoved(QByteArray,int,int)), + this, SLOT(slotHeaderColumnMoved(QByteArray,int,int))); + connect(m_headerWidget, SIGNAL(sortOrderChanged(Qt::SortOrder,Qt::SortOrder)), + this, SIGNAL(sortOrderChanged(Qt::SortOrder,Qt::SortOrder))); + connect(m_headerWidget, SIGNAL(sortRoleChanged(QByteArray,QByteArray)), + this, SIGNAL(sortRoleChanged(QByteArray,QByteArray))); + + m_layouter->setHeaderHeight(headerSize.height()); + m_headerWidget->setVisible(true); + } else if (!visible && m_headerWidget->isVisible()) { + disconnect(m_headerWidget, SIGNAL(columnWidthChanged(QByteArray,qreal,qreal)), + this, SLOT(slotHeaderColumnWidthChanged(QByteArray,qreal,qreal))); + disconnect(m_headerWidget, SIGNAL(columnMoved(QByteArray,int,int)), + this, SLOT(slotHeaderColumnMoved(QByteArray,int,int))); + disconnect(m_headerWidget, SIGNAL(sortOrderChanged(Qt::SortOrder,Qt::SortOrder)), + this, SIGNAL(sortOrderChanged(Qt::SortOrder,Qt::SortOrder))); + disconnect(m_headerWidget, SIGNAL(sortRoleChanged(QByteArray,QByteArray)), + this, SIGNAL(sortRoleChanged(QByteArray,QByteArray))); + + m_layouter->setHeaderHeight(0); + m_headerWidget->setVisible(false); + } +} + +bool KItemListView::isHeaderVisible() const +{ + return m_headerWidget->isVisible(); +} + +KItemListHeader* KItemListView::header() const +{ + return m_header; +} + +QPixmap KItemListView::createDragPixmap(const KItemSet& indexes) const +{ + QPixmap pixmap; + + if (indexes.count() == 1) { + KItemListWidget* item = m_visibleItems.value(indexes.first()); + QGraphicsView* graphicsView = scene()->views()[0]; + if (item && graphicsView) { + pixmap = item->createDragPixmap(0, graphicsView); + } + } else { + // TODO: Not implemented yet. Probably extend the interface + // from KItemListWidget::createDragPixmap() to return a pixmap + // that can be used for multiple indexes. + } + + return pixmap; +} + +void KItemListView::editRole(int index, const QByteArray& role) +{ + KItemListWidget* widget = m_visibleItems.value(index); + if (!widget || m_editingRole) { + return; + } + + m_editingRole = true; + widget->setEditedRole(role); + + connect(widget, SIGNAL(roleEditingCanceled(int,QByteArray,QVariant)), + this, SLOT(slotRoleEditingCanceled(int,QByteArray,QVariant))); + connect(widget, SIGNAL(roleEditingFinished(int,QByteArray,QVariant)), + this, SLOT(slotRoleEditingFinished(int,QByteArray,QVariant))); +} + +void KItemListView::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) +{ + QGraphicsWidget::paint(painter, option, widget); + + if (m_rubberBand->isActive()) { + QRectF rubberBandRect = QRectF(m_rubberBand->startPosition(), + m_rubberBand->endPosition()).normalized(); + + const QPointF topLeft = rubberBandRect.topLeft(); + if (scrollOrientation() == Qt::Vertical) { + rubberBandRect.moveTo(topLeft.x(), topLeft.y() - scrollOffset()); + } else { + rubberBandRect.moveTo(topLeft.x() - scrollOffset(), topLeft.y()); + } + + QStyleOptionRubberBand opt; + opt.initFrom(widget); + opt.shape = QRubberBand::Rectangle; + opt.opaque = false; + opt.rect = rubberBandRect.toRect(); + style()->drawControl(QStyle::CE_RubberBand, &opt, painter); + } + + if (!m_dropIndicator.isEmpty()) { + const QRectF r = m_dropIndicator.toRect(); + + QColor color = palette().brush(QPalette::Normal, QPalette::Highlight).color(); + painter->setPen(color); + + // TODO: The following implementation works only for a vertical scroll-orientation + // and assumes a height of the m_draggingInsertIndicator of 1. + Q_ASSERT(r.height() == 1); + painter->drawLine(r.left() + 1, r.top(), r.right() - 1, r.top()); + + color.setAlpha(128); + painter->setPen(color); + painter->drawRect(r.left(), r.top() - 1, r.width() - 1, 2); + } +} + +QVariant KItemListView::itemChange(GraphicsItemChange change, const QVariant &value) +{ + if (change == QGraphicsItem::ItemSceneHasChanged && scene()) { + if (!scene()->views().isEmpty()) { + m_styleOption.palette = scene()->views().at(0)->palette(); + } + } + return QGraphicsItem::itemChange(change, value); +} + +void KItemListView::setItemSize(const QSizeF& size) +{ + const QSizeF previousSize = m_itemSize; + if (size == previousSize) { + return; + } + + // Skip animations when the number of rows or columns + // are changed in the grid layout. Although the animation + // engine can handle this usecase, it looks obtrusive. + const bool animate = !changesItemGridLayout(m_layouter->size(), + size, + m_layouter->itemMargin()); + + const bool alternateBackgroundsChanged = (m_visibleRoles.count() > 1) && + (( m_itemSize.isEmpty() && !size.isEmpty()) || + (!m_itemSize.isEmpty() && size.isEmpty())); + + m_itemSize = size; + + if (alternateBackgroundsChanged) { + // For an empty item size alternate backgrounds are drawn if more than + // one role is shown. Assure that the backgrounds for visible items are + // updated when changing the size in this context. + updateAlternateBackgrounds(); + } + + if (size.isEmpty()) { + if (m_headerWidget->automaticColumnResizing()) { + updatePreferredColumnWidths(); + } else { + // Only apply the changed height and respect the header widths + // set by the user + const qreal currentWidth = m_layouter->itemSize().width(); + const QSizeF newSize(currentWidth, size.height()); + m_layouter->setItemSize(newSize); + } + } else { + m_layouter->setItemSize(size); + } + + m_sizeHintResolver->clearCache(); + doLayout(animate ? Animation : NoAnimation); + onItemSizeChanged(size, previousSize); +} + +void KItemListView::setStyleOption(const KItemListStyleOption& option) +{ + const KItemListStyleOption previousOption = m_styleOption; + m_styleOption = option; + + bool animate = true; + const QSizeF margin(option.horizontalMargin, option.verticalMargin); + if (margin != m_layouter->itemMargin()) { + // Skip animations when the number of rows or columns + // are changed in the grid layout. Although the animation + // engine can handle this usecase, it looks obtrusive. + animate = !changesItemGridLayout(m_layouter->size(), + m_layouter->itemSize(), + margin); + m_layouter->setItemMargin(margin); + } + + if (m_grouped) { + updateGroupHeaderHeight(); + } + + if (animate && + (previousOption.maxTextLines != option.maxTextLines || previousOption.maxTextWidth != option.maxTextWidth)) { + // Animating a change of the maximum text size just results in expensive + // temporary eliding and clipping operations and does not look good visually. + animate = false; + } + + QHashIterator it(m_visibleItems); + while (it.hasNext()) { + it.next(); + it.value()->setStyleOption(option); + } + + m_sizeHintResolver->clearCache(); + m_layouter->markAsDirty(); + doLayout(animate ? Animation : NoAnimation); + + if (m_itemSize.isEmpty()) { + updatePreferredColumnWidths(); + } + + onStyleOptionChanged(option, previousOption); +} + +void KItemListView::setScrollOrientation(Qt::Orientation orientation) +{ + const Qt::Orientation previousOrientation = m_layouter->scrollOrientation(); + if (orientation == previousOrientation) { + return; + } + + m_layouter->setScrollOrientation(orientation); + m_animation->setScrollOrientation(orientation); + m_sizeHintResolver->clearCache(); + + if (m_grouped) { + QMutableHashIterator it (m_visibleGroups); + while (it.hasNext()) { + it.next(); + it.value()->setScrollOrientation(orientation); + } + updateGroupHeaderHeight(); + + } + + doLayout(NoAnimation); + + onScrollOrientationChanged(orientation, previousOrientation); + emit scrollOrientationChanged(orientation, previousOrientation); +} + +Qt::Orientation KItemListView::scrollOrientation() const +{ + return m_layouter->scrollOrientation(); +} + +KItemListWidgetCreatorBase* KItemListView::defaultWidgetCreator() const +{ + return 0; +} + +KItemListGroupHeaderCreatorBase* KItemListView::defaultGroupHeaderCreator() const +{ + return 0; +} + +void KItemListView::initializeItemListWidget(KItemListWidget* item) +{ + Q_UNUSED(item); +} + +bool KItemListView::itemSizeHintUpdateRequired(const QSet& changedRoles) const +{ + Q_UNUSED(changedRoles); + return true; +} + +void KItemListView::onControllerChanged(KItemListController* current, KItemListController* previous) +{ + Q_UNUSED(current); + Q_UNUSED(previous); +} + +void KItemListView::onModelChanged(KItemModelBase* current, KItemModelBase* previous) +{ + Q_UNUSED(current); + Q_UNUSED(previous); +} + +void KItemListView::onScrollOrientationChanged(Qt::Orientation current, Qt::Orientation previous) +{ + Q_UNUSED(current); + Q_UNUSED(previous); +} + +void KItemListView::onItemSizeChanged(const QSizeF& current, const QSizeF& previous) +{ + Q_UNUSED(current); + Q_UNUSED(previous); +} + +void KItemListView::onScrollOffsetChanged(qreal current, qreal previous) +{ + Q_UNUSED(current); + Q_UNUSED(previous); +} + +void KItemListView::onVisibleRolesChanged(const QList& current, const QList& previous) +{ + Q_UNUSED(current); + Q_UNUSED(previous); +} + +void KItemListView::onStyleOptionChanged(const KItemListStyleOption& current, const KItemListStyleOption& previous) +{ + Q_UNUSED(current); + Q_UNUSED(previous); +} + +void KItemListView::onSupportsItemExpandingChanged(bool supportsExpanding) +{ + Q_UNUSED(supportsExpanding); +} + +void KItemListView::onTransactionBegin() +{ +} + +void KItemListView::onTransactionEnd() +{ +} + +bool KItemListView::event(QEvent* event) +{ + switch (event->type()) { + case QEvent::PaletteChange: + updatePalette(); + break; + + case QEvent::FontChange: + updateFont(); + break; + + default: + // Forward all other events to the controller and handle them there + if (!m_editingRole && m_controller && m_controller->processEvent(event, transform())) { + event->accept(); + return true; + } + } + + return QGraphicsWidget::event(event); +} + +void KItemListView::mousePressEvent(QGraphicsSceneMouseEvent* event) +{ + m_mousePos = transform().map(event->pos()); + event->accept(); +} + +void KItemListView::mouseMoveEvent(QGraphicsSceneMouseEvent* event) +{ + QGraphicsWidget::mouseMoveEvent(event); + + m_mousePos = transform().map(event->pos()); + if (m_autoScrollTimer && !m_autoScrollTimer->isActive()) { + m_autoScrollTimer->start(InitialAutoScrollDelay); + } +} + +void KItemListView::dragEnterEvent(QGraphicsSceneDragDropEvent* event) +{ + event->setAccepted(true); + setAutoScroll(true); +} + +void KItemListView::dragMoveEvent(QGraphicsSceneDragDropEvent *event) +{ + QGraphicsWidget::dragMoveEvent(event); + + m_mousePos = transform().map(event->pos()); + if (m_autoScrollTimer && !m_autoScrollTimer->isActive()) { + m_autoScrollTimer->start(InitialAutoScrollDelay); + } +} + +void KItemListView::dragLeaveEvent(QGraphicsSceneDragDropEvent *event) +{ + QGraphicsWidget::dragLeaveEvent(event); + setAutoScroll(false); +} + +void KItemListView::dropEvent(QGraphicsSceneDragDropEvent* event) +{ + QGraphicsWidget::dropEvent(event); + setAutoScroll(false); +} + +QList KItemListView::visibleItemListWidgets() const +{ + return m_visibleItems.values(); +} + +void KItemListView::updateFont() +{ + if (scene() && !scene()->views().isEmpty()) { + KItemListStyleOption option = styleOption(); + option.font = scene()->views().first()->font(); + option.fontMetrics = QFontMetrics(option.font); + + setStyleOption(option); + } +} + +void KItemListView::updatePalette() +{ + if (scene() && !scene()->views().isEmpty()) { + KItemListStyleOption option = styleOption(); + option.palette = scene()->views().first()->palette(); + + setStyleOption(option); + } +} + +void KItemListView::slotItemsInserted(const KItemRangeList& itemRanges) +{ + if (m_itemSize.isEmpty()) { + updatePreferredColumnWidths(itemRanges); + } + + const bool hasMultipleRanges = (itemRanges.count() > 1); + if (hasMultipleRanges) { + beginTransaction(); + } + + m_layouter->markAsDirty(); + + m_sizeHintResolver->itemsInserted(itemRanges); + + int previouslyInsertedCount = 0; + foreach (const KItemRange& range, itemRanges) { + // range.index is related to the model before anything has been inserted. + // As in each loop the current item-range gets inserted the index must + // be increased by the already previously inserted items. + const int index = range.index + previouslyInsertedCount; + const int count = range.count; + if (index < 0 || count <= 0) { + kWarning() << "Invalid item range (index:" << index << ", count:" << count << ")"; + continue; + } + previouslyInsertedCount += count; + + // Determine which visible items must be moved + QList itemsToMove; + QHashIterator it(m_visibleItems); + while (it.hasNext()) { + it.next(); + const int visibleItemIndex = it.key(); + if (visibleItemIndex >= index) { + itemsToMove.append(visibleItemIndex); + } + } + + // Update the indexes of all KItemListWidget instances that are located + // after the inserted items. It is important to adjust the indexes in the order + // from the highest index to the lowest index to prevent overlaps when setting the new index. + qSort(itemsToMove); + for (int i = itemsToMove.count() - 1; i >= 0; --i) { + KItemListWidget* widget = m_visibleItems.value(itemsToMove[i]); + Q_ASSERT(widget); + const int newIndex = widget->index() + count; + if (hasMultipleRanges) { + setWidgetIndex(widget, newIndex); + } else { + // Try to animate the moving of the item + moveWidgetToIndex(widget, newIndex); + } + } + + if (m_model->count() == count && m_activeTransactions == 0) { + // Check whether a scrollbar is required to show the inserted items. In this case + // the size of the layouter will be decreased before calling doLayout(): This prevents + // an unnecessary temporary animation due to the geometry change of the inserted scrollbar. + const bool verticalScrollOrientation = (scrollOrientation() == Qt::Vertical); + const bool decreaseLayouterSize = ( verticalScrollOrientation && maximumScrollOffset() > size().height()) || + (!verticalScrollOrientation && maximumScrollOffset() > size().width()); + if (decreaseLayouterSize) { + const int scrollBarExtent = style()->pixelMetric(QStyle::PM_ScrollBarExtent); + + int scrollbarSpacing = 0; + if (style()->styleHint(QStyle::SH_ScrollView_FrameOnlyAroundContents)) { + scrollbarSpacing = style()->pixelMetric(QStyle::PM_ScrollView_ScrollBarSpacing); + } + + QSizeF layouterSize = m_layouter->size(); + if (verticalScrollOrientation) { + layouterSize.rwidth() -= scrollBarExtent + scrollbarSpacing; + } else { + layouterSize.rheight() -= scrollBarExtent + scrollbarSpacing; + } + m_layouter->setSize(layouterSize); + } + } + + if (!hasMultipleRanges) { + doLayout(animateChangedItemCount(count) ? Animation : NoAnimation, index, count); + updateSiblingsInformation(); + } + } + + if (m_controller) { + m_controller->selectionManager()->itemsInserted(itemRanges); + } + + if (hasMultipleRanges) { + m_endTransactionAnimationHint = NoAnimation; + endTransaction(); + + updateSiblingsInformation(); + } + + if (m_grouped && (hasMultipleRanges || itemRanges.first().count < m_model->count())) { + // In case if items of the same group have been inserted before an item that + // currently represents the first item of the group, the group header of + // this item must be removed. + updateVisibleGroupHeaders(); + } + + if (useAlternateBackgrounds()) { + updateAlternateBackgrounds(); + } +} + +void KItemListView::slotItemsRemoved(const KItemRangeList& itemRanges) +{ + if (m_itemSize.isEmpty()) { + // Don't pass the item-range: The preferred column-widths of + // all items must be adjusted when removing items. + updatePreferredColumnWidths(); + } + + const bool hasMultipleRanges = (itemRanges.count() > 1); + if (hasMultipleRanges) { + beginTransaction(); + } + + m_layouter->markAsDirty(); + + m_sizeHintResolver->itemsRemoved(itemRanges); + + for (int i = itemRanges.count() - 1; i >= 0; --i) { + const KItemRange& range = itemRanges[i]; + const int index = range.index; + const int count = range.count; + if (index < 0 || count <= 0) { + kWarning() << "Invalid item range (index:" << index << ", count:" << count << ")"; + continue; + } + + const int firstRemovedIndex = index; + const int lastRemovedIndex = index + count - 1; + + // Remeber which items have to be moved because they are behind the removed range. + QVector itemsToMove; + + // Remove all KItemListWidget instances that got deleted + foreach (KItemListWidget* widget, m_visibleItems) { + const int i = widget->index(); + if (i < firstRemovedIndex) { + continue; + } else if (i > lastRemovedIndex) { + itemsToMove.append(i); + continue; + } + + m_animation->stop(widget); + // Stopping the animation might lead to recycling the widget if + // it is invisible (see slotAnimationFinished()). + // Check again whether it is still visible: + if (!m_visibleItems.contains(i)) { + continue; + } + + if (m_model->count() == 0 || hasMultipleRanges || !animateChangedItemCount(count)) { + // Remove the widget without animation + recycleWidget(widget); + } else { + // Animate the removing of the items. Special case: When removing an item there + // is no valid model index available anymore. For the + // remove-animation the item gets removed from m_visibleItems but the widget + // will stay alive until the animation has been finished and will + // be recycled (deleted) in KItemListView::slotAnimationFinished(). + m_visibleItems.remove(i); + widget->setIndex(-1); + m_animation->start(widget, KItemListViewAnimation::DeleteAnimation); + } + } + + // Update the indexes of all KItemListWidget instances that are located + // after the deleted items. It is important to update them in ascending + // order to prevent overlaps when setting the new index. + std::sort(itemsToMove.begin(), itemsToMove.end()); + foreach (int i, itemsToMove) { + KItemListWidget* widget = m_visibleItems.value(i); + Q_ASSERT(widget); + const int newIndex = i - count; + if (hasMultipleRanges) { + setWidgetIndex(widget, newIndex); + } else { + // Try to animate the moving of the item + moveWidgetToIndex(widget, newIndex); + } + } + + if (!hasMultipleRanges) { + // The decrease-layout-size optimization in KItemListView::slotItemsInserted() + // assumes an updated geometry. If items are removed during an active transaction, + // the transaction will be temporary deactivated so that doLayout() triggers a + // geometry update if necessary. + const int activeTransactions = m_activeTransactions; + m_activeTransactions = 0; + doLayout(animateChangedItemCount(count) ? Animation : NoAnimation, index, -count); + m_activeTransactions = activeTransactions; + updateSiblingsInformation(); + } + } + + if (m_controller) { + m_controller->selectionManager()->itemsRemoved(itemRanges); + } + + if (hasMultipleRanges) { + m_endTransactionAnimationHint = NoAnimation; + endTransaction(); + updateSiblingsInformation(); + } + + if (m_grouped && (hasMultipleRanges || m_model->count() > 0)) { + // In case if the first item of a group has been removed, the group header + // must be applied to the next visible item. + updateVisibleGroupHeaders(); + } + + if (useAlternateBackgrounds()) { + updateAlternateBackgrounds(); + } +} + +void KItemListView::slotItemsMoved(const KItemRange& itemRange, const QList& movedToIndexes) +{ + m_sizeHintResolver->itemsMoved(itemRange, movedToIndexes); + m_layouter->markAsDirty(); + + if (m_controller) { + m_controller->selectionManager()->itemsMoved(itemRange, movedToIndexes); + } + + const int firstVisibleMovedIndex = qMax(firstVisibleIndex(), itemRange.index); + const int lastVisibleMovedIndex = qMin(lastVisibleIndex(), itemRange.index + itemRange.count - 1); + + for (int index = firstVisibleMovedIndex; index <= lastVisibleMovedIndex; ++index) { + KItemListWidget* widget = m_visibleItems.value(index); + if (widget) { + updateWidgetProperties(widget, index); + initializeItemListWidget(widget); + } + } + + doLayout(NoAnimation); + updateSiblingsInformation(); +} + +void KItemListView::slotItemsChanged(const KItemRangeList& itemRanges, + const QSet& roles) +{ + const bool updateSizeHints = itemSizeHintUpdateRequired(roles); + if (updateSizeHints && m_itemSize.isEmpty()) { + updatePreferredColumnWidths(itemRanges); + } + + foreach (const KItemRange& itemRange, itemRanges) { + const int index = itemRange.index; + const int count = itemRange.count; + + if (updateSizeHints) { + m_sizeHintResolver->itemsChanged(index, count, roles); + m_layouter->markAsDirty(); + + if (!m_layoutTimer->isActive()) { + m_layoutTimer->start(); + } + } + + // Apply the changed roles to the visible item-widgets + const int lastIndex = index + count - 1; + for (int i = index; i <= lastIndex; ++i) { + KItemListWidget* widget = m_visibleItems.value(i); + if (widget) { + widget->setData(m_model->data(i), roles); + } + } + + if (m_grouped && roles.contains(m_model->sortRole())) { + // The sort-role has been changed which might result + // in modified group headers + updateVisibleGroupHeaders(); + doLayout(NoAnimation); + } + } +} + +void KItemListView::slotGroupsChanged() +{ + updateVisibleGroupHeaders(); + doLayout(NoAnimation); + updateSiblingsInformation(); +} + +void KItemListView::slotGroupedSortingChanged(bool current) +{ + m_grouped = current; + m_layouter->markAsDirty(); + + if (m_grouped) { + updateGroupHeaderHeight(); + } else { + // Clear all visible headers. Note that the QHashIterator takes a copy of + // m_visibleGroups. Therefore, it remains valid even if items are removed + // from m_visibleGroups in recycleGroupHeaderForWidget(). + QHashIterator it(m_visibleGroups); + while (it.hasNext()) { + it.next(); + recycleGroupHeaderForWidget(it.key()); + } + Q_ASSERT(m_visibleGroups.isEmpty()); + } + + if (useAlternateBackgrounds()) { + // Changing the group mode requires to update the alternate backgrounds + // as with the enabled group mode the altering is done on base of the first + // group item. + updateAlternateBackgrounds(); + } + updateSiblingsInformation(); + doLayout(NoAnimation); +} + +void KItemListView::slotSortOrderChanged(Qt::SortOrder current, Qt::SortOrder previous) +{ + Q_UNUSED(current); + Q_UNUSED(previous); + if (m_grouped) { + updateVisibleGroupHeaders(); + doLayout(NoAnimation); + } +} + +void KItemListView::slotSortRoleChanged(const QByteArray& current, const QByteArray& previous) +{ + Q_UNUSED(current); + Q_UNUSED(previous); + if (m_grouped) { + updateVisibleGroupHeaders(); + doLayout(NoAnimation); + } +} + +void KItemListView::slotCurrentChanged(int current, int previous) +{ + Q_UNUSED(previous); + + // In SingleSelection mode (e.g., in the Places Panel), the current item is + // always the selected item. It is not necessary to highlight the current item then. + if (m_controller->selectionBehavior() != KItemListController::SingleSelection) { + KItemListWidget* previousWidget = m_visibleItems.value(previous, 0); + if (previousWidget) { + previousWidget->setCurrent(false); + } + + KItemListWidget* currentWidget = m_visibleItems.value(current, 0); + if (currentWidget) { + currentWidget->setCurrent(true); + } + } +} + +void KItemListView::slotSelectionChanged(const KItemSet& current, const KItemSet& previous) +{ + Q_UNUSED(previous); + + QHashIterator it(m_visibleItems); + while (it.hasNext()) { + it.next(); + const int index = it.key(); + KItemListWidget* widget = it.value(); + widget->setSelected(current.contains(index)); + } +} + +void KItemListView::slotAnimationFinished(QGraphicsWidget* widget, + KItemListViewAnimation::AnimationType type) +{ + KItemListWidget* itemListWidget = qobject_cast(widget); + Q_ASSERT(itemListWidget); + + switch (type) { + case KItemListViewAnimation::DeleteAnimation: { + // As we recycle the widget in this case it is important to assure that no + // other animation has been started. This is a convention in KItemListView and + // not a requirement defined by KItemListViewAnimation. + Q_ASSERT(!m_animation->isStarted(itemListWidget)); + + // All KItemListWidgets that are animated by the DeleteAnimation are not maintained + // by m_visibleWidgets and must be deleted manually after the animation has + // been finished. + recycleGroupHeaderForWidget(itemListWidget); + widgetCreator()->recycle(itemListWidget); + break; + } + + case KItemListViewAnimation::CreateAnimation: + case KItemListViewAnimation::MovingAnimation: + case KItemListViewAnimation::ResizeAnimation: { + const int index = itemListWidget->index(); + const bool invisible = (index < m_layouter->firstVisibleIndex()) || + (index > m_layouter->lastVisibleIndex()); + if (invisible && !m_animation->isStarted(itemListWidget)) { + recycleWidget(itemListWidget); + } + break; + } + + default: break; + } +} + +void KItemListView::slotLayoutTimerFinished() +{ + m_layouter->setSize(geometry().size()); + doLayout(Animation); +} + +void KItemListView::slotRubberBandPosChanged() +{ + update(); +} + +void KItemListView::slotRubberBandActivationChanged(bool active) +{ + if (active) { + connect(m_rubberBand, SIGNAL(startPositionChanged(QPointF,QPointF)), this, SLOT(slotRubberBandPosChanged())); + connect(m_rubberBand, SIGNAL(endPositionChanged(QPointF,QPointF)), this, SLOT(slotRubberBandPosChanged())); + m_skipAutoScrollForRubberBand = true; + } else { + disconnect(m_rubberBand, SIGNAL(startPositionChanged(QPointF,QPointF)), this, SLOT(slotRubberBandPosChanged())); + disconnect(m_rubberBand, SIGNAL(endPositionChanged(QPointF,QPointF)), this, SLOT(slotRubberBandPosChanged())); + m_skipAutoScrollForRubberBand = false; + } + + update(); +} + +void KItemListView::slotHeaderColumnWidthChanged(const QByteArray& role, + qreal currentWidth, + qreal previousWidth) +{ + Q_UNUSED(role); + Q_UNUSED(currentWidth); + Q_UNUSED(previousWidth); + + m_headerWidget->setAutomaticColumnResizing(false); + applyColumnWidthsFromHeader(); + doLayout(NoAnimation); +} + +void KItemListView::slotHeaderColumnMoved(const QByteArray& role, + int currentIndex, + int previousIndex) +{ + Q_ASSERT(m_visibleRoles[previousIndex] == role); + + const QList previous = m_visibleRoles; + + QList current = m_visibleRoles; + current.removeAt(previousIndex); + current.insert(currentIndex, role); + + setVisibleRoles(current); + + emit visibleRolesChanged(current, previous); +} + +void KItemListView::triggerAutoScrolling() +{ + if (!m_autoScrollTimer) { + return; + } + + int pos = 0; + int visibleSize = 0; + if (scrollOrientation() == Qt::Vertical) { + pos = m_mousePos.y(); + visibleSize = size().height(); + } else { + pos = m_mousePos.x(); + visibleSize = size().width(); + } + + if (m_autoScrollTimer->interval() == InitialAutoScrollDelay) { + m_autoScrollIncrement = 0; + } + + m_autoScrollIncrement = calculateAutoScrollingIncrement(pos, visibleSize, m_autoScrollIncrement); + if (m_autoScrollIncrement == 0) { + // The mouse position is not above an autoscroll margin (the autoscroll timer + // will be restarted in mouseMoveEvent()) + m_autoScrollTimer->stop(); + return; + } + + if (m_rubberBand->isActive() && m_skipAutoScrollForRubberBand) { + // If a rubberband selection is ongoing the autoscrolling may only get triggered + // if the direction of the rubberband is similar to the autoscroll direction. This + // prevents that starting to create a rubberband within the autoscroll margins starts + // an autoscrolling. + + const qreal minDiff = 4; // Ignore any autoscrolling if the rubberband is very small + const qreal diff = (scrollOrientation() == Qt::Vertical) + ? m_rubberBand->endPosition().y() - m_rubberBand->startPosition().y() + : m_rubberBand->endPosition().x() - m_rubberBand->startPosition().x(); + if (qAbs(diff) < minDiff || (m_autoScrollIncrement < 0 && diff > 0) || (m_autoScrollIncrement > 0 && diff < 0)) { + // The rubberband direction is different from the scroll direction (e.g. the rubberband has + // been moved up although the autoscroll direction might be down) + m_autoScrollTimer->stop(); + return; + } + } + + // As soon as the autoscrolling has been triggered at least once despite having an active rubberband, + // the autoscrolling may not get skipped anymore until a new rubberband is created + m_skipAutoScrollForRubberBand = false; + + const qreal maxVisibleOffset = qMax(qreal(0), maximumScrollOffset() - visibleSize); + const qreal newScrollOffset = qMin(scrollOffset() + m_autoScrollIncrement, maxVisibleOffset); + setScrollOffset(newScrollOffset); + + // Trigger the autoscroll timer which will periodically call + // triggerAutoScrolling() + m_autoScrollTimer->start(RepeatingAutoScrollDelay); +} + +void KItemListView::slotGeometryOfGroupHeaderParentChanged() +{ + KItemListWidget* widget = qobject_cast(sender()); + Q_ASSERT(widget); + KItemListGroupHeader* groupHeader = m_visibleGroups.value(widget); + Q_ASSERT(groupHeader); + Q_UNUSED(groupHeader); + updateGroupHeaderLayout(widget); +} + +void KItemListView::slotRoleEditingCanceled(int index, const QByteArray& role, const QVariant& value) +{ + disconnectRoleEditingSignals(index); + + emit roleEditingCanceled(index, role, value); + m_editingRole = false; +} + +void KItemListView::slotRoleEditingFinished(int index, const QByteArray& role, const QVariant& value) +{ + disconnectRoleEditingSignals(index); + + emit roleEditingFinished(index, role, value); + m_editingRole = false; +} + +void KItemListView::setController(KItemListController* controller) +{ + if (m_controller != controller) { + KItemListController* previous = m_controller; + if (previous) { + KItemListSelectionManager* selectionManager = previous->selectionManager(); + disconnect(selectionManager, SIGNAL(currentChanged(int,int)), this, SLOT(slotCurrentChanged(int,int))); + disconnect(selectionManager, SIGNAL(selectionChanged(KItemSet,KItemSet)), this, SLOT(slotSelectionChanged(KItemSet,KItemSet))); + } + + m_controller = controller; + + if (controller) { + KItemListSelectionManager* selectionManager = controller->selectionManager(); + connect(selectionManager, SIGNAL(currentChanged(int,int)), this, SLOT(slotCurrentChanged(int,int))); + connect(selectionManager, SIGNAL(selectionChanged(KItemSet,KItemSet)), this, SLOT(slotSelectionChanged(KItemSet,KItemSet))); + } + + onControllerChanged(controller, previous); + } +} + +void KItemListView::setModel(KItemModelBase* model) +{ + if (m_model == model) { + return; + } + + KItemModelBase* previous = m_model; + + if (m_model) { + disconnect(m_model, SIGNAL(itemsChanged(KItemRangeList,QSet)), + this, SLOT(slotItemsChanged(KItemRangeList,QSet))); + disconnect(m_model, SIGNAL(itemsInserted(KItemRangeList)), + this, SLOT(slotItemsInserted(KItemRangeList))); + disconnect(m_model, SIGNAL(itemsRemoved(KItemRangeList)), + this, SLOT(slotItemsRemoved(KItemRangeList))); + disconnect(m_model, SIGNAL(itemsMoved(KItemRange,QList)), + this, SLOT(slotItemsMoved(KItemRange,QList))); + disconnect(m_model, SIGNAL(groupsChanged()), + this, SLOT(slotGroupsChanged())); + disconnect(m_model, SIGNAL(groupedSortingChanged(bool)), + this, SLOT(slotGroupedSortingChanged(bool))); + disconnect(m_model, SIGNAL(sortOrderChanged(Qt::SortOrder,Qt::SortOrder)), + this, SLOT(slotSortOrderChanged(Qt::SortOrder,Qt::SortOrder))); + disconnect(m_model, SIGNAL(sortRoleChanged(QByteArray,QByteArray)), + this, SLOT(slotSortRoleChanged(QByteArray,QByteArray))); + + m_sizeHintResolver->itemsRemoved(KItemRangeList() << KItemRange(0, m_model->count())); + } + + m_model = model; + m_layouter->setModel(model); + m_grouped = model->groupedSorting(); + + if (m_model) { + connect(m_model, SIGNAL(itemsChanged(KItemRangeList,QSet)), + this, SLOT(slotItemsChanged(KItemRangeList,QSet))); + connect(m_model, SIGNAL(itemsInserted(KItemRangeList)), + this, SLOT(slotItemsInserted(KItemRangeList))); + connect(m_model, SIGNAL(itemsRemoved(KItemRangeList)), + this, SLOT(slotItemsRemoved(KItemRangeList))); + connect(m_model, SIGNAL(itemsMoved(KItemRange,QList)), + this, SLOT(slotItemsMoved(KItemRange,QList))); + connect(m_model, SIGNAL(groupsChanged()), + this, SLOT(slotGroupsChanged())); + connect(m_model, SIGNAL(groupedSortingChanged(bool)), + this, SLOT(slotGroupedSortingChanged(bool))); + connect(m_model, SIGNAL(sortOrderChanged(Qt::SortOrder,Qt::SortOrder)), + this, SLOT(slotSortOrderChanged(Qt::SortOrder,Qt::SortOrder))); + connect(m_model, SIGNAL(sortRoleChanged(QByteArray,QByteArray)), + this, SLOT(slotSortRoleChanged(QByteArray,QByteArray))); + + const int itemCount = m_model->count(); + if (itemCount > 0) { + slotItemsInserted(KItemRangeList() << KItemRange(0, itemCount)); + } + } + + onModelChanged(model, previous); +} + +KItemListRubberBand* KItemListView::rubberBand() const +{ + return m_rubberBand; +} + +void KItemListView::doLayout(LayoutAnimationHint hint, int changedIndex, int changedCount) +{ + if (m_layoutTimer->isActive()) { + m_layoutTimer->stop(); + } + + if (m_activeTransactions > 0) { + if (hint == NoAnimation) { + // As soon as at least one property change should be done without animation, + // the whole transaction will be marked as not animated. + m_endTransactionAnimationHint = NoAnimation; + } + return; + } + + if (!m_model || m_model->count() < 0) { + return; + } + + int firstVisibleIndex = m_layouter->firstVisibleIndex(); + if (firstVisibleIndex < 0) { + emitOffsetChanges(); + return; + } + + // Do a sanity check of the scroll-offset property: When properties of the itemlist-view have been changed + // it might be possible that the maximum offset got changed too. Assure that the full visible range + // is still shown if the maximum offset got decreased. + const qreal visibleOffsetRange = (scrollOrientation() == Qt::Horizontal) ? size().width() : size().height(); + const qreal maxOffsetToShowFullRange = maximumScrollOffset() - visibleOffsetRange; + if (scrollOffset() > maxOffsetToShowFullRange) { + m_layouter->setScrollOffset(qMax(qreal(0), maxOffsetToShowFullRange)); + firstVisibleIndex = m_layouter->firstVisibleIndex(); + } + + const int lastVisibleIndex = m_layouter->lastVisibleIndex(); + + int firstSibblingIndex = -1; + int lastSibblingIndex = -1; + const bool supportsExpanding = supportsItemExpanding(); + + QList reusableItems = recycleInvisibleItems(firstVisibleIndex, lastVisibleIndex, hint); + + // Assure that for each visible item a KItemListWidget is available. KItemListWidget + // instances from invisible items are reused. If no reusable items are + // found then new KItemListWidget instances get created. + const bool animate = (hint == Animation); + for (int i = firstVisibleIndex; i <= lastVisibleIndex; ++i) { + bool applyNewPos = true; + bool wasHidden = false; + + const QRectF itemBounds = m_layouter->itemRect(i); + const QPointF newPos = itemBounds.topLeft(); + KItemListWidget* widget = m_visibleItems.value(i); + if (!widget) { + wasHidden = true; + if (!reusableItems.isEmpty()) { + // Reuse a KItemListWidget instance from an invisible item + const int oldIndex = reusableItems.takeLast(); + widget = m_visibleItems.value(oldIndex); + setWidgetIndex(widget, i); + updateWidgetProperties(widget, i); + initializeItemListWidget(widget); + } else { + // No reusable KItemListWidget instance is available, create a new one + widget = createWidget(i); + } + widget->resize(itemBounds.size()); + + if (animate && changedCount < 0) { + // Items have been deleted. + if (i >= changedIndex) { + // The item is located behind the removed range. Move the + // created item to the imaginary old position outside the + // view. It will get animated to the new position later. + const int previousIndex = i - changedCount; + const QRectF itemRect = m_layouter->itemRect(previousIndex); + if (itemRect.isEmpty()) { + const QPointF invisibleOldPos = (scrollOrientation() == Qt::Vertical) + ? QPointF(0, size().height()) : QPointF(size().width(), 0); + widget->setPos(invisibleOldPos); + } else { + widget->setPos(itemRect.topLeft()); + } + applyNewPos = false; + } + } + + if (supportsExpanding && changedCount == 0) { + if (firstSibblingIndex < 0) { + firstSibblingIndex = i; + } + lastSibblingIndex = i; + } + } + + if (animate) { + if (m_animation->isStarted(widget, KItemListViewAnimation::MovingAnimation)) { + m_animation->start(widget, KItemListViewAnimation::MovingAnimation, newPos); + applyNewPos = false; + } + + const bool itemsRemoved = (changedCount < 0); + const bool itemsInserted = (changedCount > 0); + if (itemsRemoved && (i >= changedIndex)) { + // The item is located after the removed items. Animate the moving of the position. + applyNewPos = !moveWidget(widget, newPos); + } else if (itemsInserted && i >= changedIndex) { + // The item is located after the first inserted item + if (i <= changedIndex + changedCount - 1) { + // The item is an inserted item. Animate the appearing of the item. + // For performance reasons no animation is done when changedCount is equal + // to all available items. + if (changedCount < m_model->count()) { + m_animation->start(widget, KItemListViewAnimation::CreateAnimation); + } + } else if (!m_animation->isStarted(widget, KItemListViewAnimation::CreateAnimation)) { + // The item was already there before, so animate the moving of the position. + // No moving animation is done if the item is animated by a create animation: This + // prevents a "move animation mess" when inserting several ranges in parallel. + applyNewPos = !moveWidget(widget, newPos); + } + } else if (!itemsRemoved && !itemsInserted && !wasHidden) { + // The size of the view might have been changed. Animate the moving of the position. + applyNewPos = !moveWidget(widget, newPos); + } + } else { + m_animation->stop(widget); + } + + if (applyNewPos) { + widget->setPos(newPos); + } + + Q_ASSERT(widget->index() == i); + widget->setVisible(true); + + if (widget->size() != itemBounds.size()) { + // Resize the widget for the item to the changed size. + if (animate) { + // If a dynamic item size is used then no animation is done in the direction + // of the dynamic size. + if (m_itemSize.width() <= 0) { + // The width is dynamic, apply the new width without animation. + widget->resize(itemBounds.width(), widget->size().height()); + } else if (m_itemSize.height() <= 0) { + // The height is dynamic, apply the new height without animation. + widget->resize(widget->size().width(), itemBounds.height()); + } + m_animation->start(widget, KItemListViewAnimation::ResizeAnimation, itemBounds.size()); + } else { + widget->resize(itemBounds.size()); + } + } + + // Updating the cell-information must be done as last step: The decision whether the + // moving-animation should be started at all is based on the previous cell-information. + const Cell cell(m_layouter->itemColumn(i), m_layouter->itemRow(i)); + m_visibleCells.insert(i, cell); + } + + // Delete invisible KItemListWidget instances that have not been reused + foreach (int index, reusableItems) { + recycleWidget(m_visibleItems.value(index)); + } + + if (supportsExpanding && firstSibblingIndex >= 0) { + Q_ASSERT(lastSibblingIndex >= 0); + updateSiblingsInformation(firstSibblingIndex, lastSibblingIndex); + } + + if (m_grouped) { + // Update the layout of all visible group headers + QHashIterator it(m_visibleGroups); + while (it.hasNext()) { + it.next(); + updateGroupHeaderLayout(it.key()); + } + } + + emitOffsetChanges(); +} + +QList KItemListView::recycleInvisibleItems(int firstVisibleIndex, + int lastVisibleIndex, + LayoutAnimationHint hint) +{ + // Determine all items that are completely invisible and might be + // reused for items that just got (at least partly) visible. If the + // animation hint is set to 'Animation' items that do e.g. an animated + // moving of their position are not marked as invisible: This assures + // that a scrolling inside the view can be done without breaking an animation. + + QList items; + + QHashIterator it(m_visibleItems); + while (it.hasNext()) { + it.next(); + + KItemListWidget* widget = it.value(); + const int index = widget->index(); + const bool invisible = (index < firstVisibleIndex) || (index > lastVisibleIndex); + + if (invisible) { + if (m_animation->isStarted(widget)) { + if (hint == NoAnimation) { + // Stopping the animation will call KItemListView::slotAnimationFinished() + // and the widget will be recycled if necessary there. + m_animation->stop(widget); + } + } else { + widget->setVisible(false); + items.append(index); + + if (m_grouped) { + recycleGroupHeaderForWidget(widget); + } + } + } + } + + return items; +} + +bool KItemListView::moveWidget(KItemListWidget* widget,const QPointF& newPos) +{ + if (widget->pos() == newPos) { + return false; + } + + bool startMovingAnim = false; + + if (m_itemSize.isEmpty()) { + // The items are not aligned in a grid but either as columns or rows. + startMovingAnim = true; + } else { + // When having a grid the moving-animation should only be started, if it is done within + // one row in the vertical scroll-orientation or one column in the horizontal scroll-orientation. + // Otherwise instead of a moving-animation a create-animation on the new position will be used + // instead. This is done to prevent overlapping (and confusing) moving-animations. + const int index = widget->index(); + const Cell cell = m_visibleCells.value(index); + if (cell.column >= 0 && cell.row >= 0) { + if (scrollOrientation() == Qt::Vertical) { + startMovingAnim = (cell.row == m_layouter->itemRow(index)); + } else { + startMovingAnim = (cell.column == m_layouter->itemColumn(index)); + } + } + } + + if (startMovingAnim) { + m_animation->start(widget, KItemListViewAnimation::MovingAnimation, newPos); + return true; + } + + m_animation->stop(widget); + m_animation->start(widget, KItemListViewAnimation::CreateAnimation); + return false; +} + +void KItemListView::emitOffsetChanges() +{ + const qreal newScrollOffset = m_layouter->scrollOffset(); + if (m_oldScrollOffset != newScrollOffset) { + emit scrollOffsetChanged(newScrollOffset, m_oldScrollOffset); + m_oldScrollOffset = newScrollOffset; + } + + const qreal newMaximumScrollOffset = m_layouter->maximumScrollOffset(); + if (m_oldMaximumScrollOffset != newMaximumScrollOffset) { + emit maximumScrollOffsetChanged(newMaximumScrollOffset, m_oldMaximumScrollOffset); + m_oldMaximumScrollOffset = newMaximumScrollOffset; + } + + const qreal newItemOffset = m_layouter->itemOffset(); + if (m_oldItemOffset != newItemOffset) { + emit itemOffsetChanged(newItemOffset, m_oldItemOffset); + m_oldItemOffset = newItemOffset; + } + + const qreal newMaximumItemOffset = m_layouter->maximumItemOffset(); + if (m_oldMaximumItemOffset != newMaximumItemOffset) { + emit maximumItemOffsetChanged(newMaximumItemOffset, m_oldMaximumItemOffset); + m_oldMaximumItemOffset = newMaximumItemOffset; + } +} + +KItemListWidget* KItemListView::createWidget(int index) +{ + KItemListWidget* widget = widgetCreator()->create(this); + widget->setFlag(QGraphicsItem::ItemStacksBehindParent); + + m_visibleItems.insert(index, widget); + m_visibleCells.insert(index, Cell()); + updateWidgetProperties(widget, index); + initializeItemListWidget(widget); + return widget; +} + +void KItemListView::recycleWidget(KItemListWidget* widget) +{ + if (m_grouped) { + recycleGroupHeaderForWidget(widget); + } + + const int index = widget->index(); + m_visibleItems.remove(index); + m_visibleCells.remove(index); + + widgetCreator()->recycle(widget); +} + +void KItemListView::setWidgetIndex(KItemListWidget* widget, int index) +{ + const int oldIndex = widget->index(); + m_visibleItems.remove(oldIndex); + m_visibleCells.remove(oldIndex); + + m_visibleItems.insert(index, widget); + m_visibleCells.insert(index, Cell()); + + widget->setIndex(index); +} + +void KItemListView::moveWidgetToIndex(KItemListWidget* widget, int index) +{ + const int oldIndex = widget->index(); + const Cell oldCell = m_visibleCells.value(oldIndex); + + setWidgetIndex(widget, index); + + const Cell newCell(m_layouter->itemColumn(index), m_layouter->itemRow(index)); + const bool vertical = (scrollOrientation() == Qt::Vertical); + const bool updateCell = (vertical && oldCell.row == newCell.row) || + (!vertical && oldCell.column == newCell.column); + if (updateCell) { + m_visibleCells.insert(index, newCell); + } +} + +void KItemListView::setLayouterSize(const QSizeF& size, SizeType sizeType) +{ + switch (sizeType) { + case LayouterSize: m_layouter->setSize(size); break; + case ItemSize: m_layouter->setItemSize(size); break; + default: break; + } +} + +void KItemListView::updateWidgetProperties(KItemListWidget* widget, int index) +{ + widget->setVisibleRoles(m_visibleRoles); + updateWidgetColumnWidths(widget); + widget->setStyleOption(m_styleOption); + + const KItemListSelectionManager* selectionManager = m_controller->selectionManager(); + + // In SingleSelection mode (e.g., in the Places Panel), the current item is + // always the selected item. It is not necessary to highlight the current item then. + if (m_controller->selectionBehavior() != KItemListController::SingleSelection) { + widget->setCurrent(index == selectionManager->currentItem()); + } + widget->setSelected(selectionManager->isSelected(index)); + widget->setHovered(false); + widget->setEnabledSelectionToggle(enabledSelectionToggles()); + widget->setIndex(index); + widget->setData(m_model->data(index)); + widget->setSiblingsInformation(QBitArray()); + updateAlternateBackgroundForWidget(widget); + + if (m_grouped) { + updateGroupHeaderForWidget(widget); + } +} + +void KItemListView::updateGroupHeaderForWidget(KItemListWidget* widget) +{ + Q_ASSERT(m_grouped); + + const int index = widget->index(); + if (!m_layouter->isFirstGroupItem(index)) { + // The widget does not represent the first item of a group + // and hence requires no header + recycleGroupHeaderForWidget(widget); + return; + } + + const QList > groups = model()->groups(); + if (groups.isEmpty() || !groupHeaderCreator()) { + return; + } + + KItemListGroupHeader* groupHeader = m_visibleGroups.value(widget); + if (!groupHeader) { + groupHeader = groupHeaderCreator()->create(this); + groupHeader->setParentItem(widget); + m_visibleGroups.insert(widget, groupHeader); + connect(widget, SIGNAL(geometryChanged()), this, SLOT(slotGeometryOfGroupHeaderParentChanged())); + } + Q_ASSERT(groupHeader->parentItem() == widget); + + const int groupIndex = groupIndexForItem(index); + Q_ASSERT(groupIndex >= 0); + groupHeader->setData(groups.at(groupIndex).second); + groupHeader->setRole(model()->sortRole()); + groupHeader->setStyleOption(m_styleOption); + groupHeader->setScrollOrientation(scrollOrientation()); + groupHeader->setItemIndex(index); + + groupHeader->show(); +} + +void KItemListView::updateGroupHeaderLayout(KItemListWidget* widget) +{ + KItemListGroupHeader* groupHeader = m_visibleGroups.value(widget); + Q_ASSERT(groupHeader); + + const int index = widget->index(); + const QRectF groupHeaderRect = m_layouter->groupHeaderRect(index); + const QRectF itemRect = m_layouter->itemRect(index); + + // The group-header is a child of the itemlist widget. Translate the + // group header position to the relative position. + if (scrollOrientation() == Qt::Vertical) { + // In the vertical scroll orientation the group header should always span + // the whole width no matter which temporary position the parent widget + // has. In this case the x-position and width will be adjusted manually. + const qreal x = -widget->x() - itemOffset(); + const qreal width = maximumItemOffset(); + groupHeader->setPos(x, -groupHeaderRect.height()); + groupHeader->resize(width, groupHeaderRect.size().height()); + } else { + groupHeader->setPos(groupHeaderRect.x() - itemRect.x(), -widget->y()); + groupHeader->resize(groupHeaderRect.size()); + } +} + +void KItemListView::recycleGroupHeaderForWidget(KItemListWidget* widget) +{ + KItemListGroupHeader* header = m_visibleGroups.value(widget); + if (header) { + header->setParentItem(0); + groupHeaderCreator()->recycle(header); + m_visibleGroups.remove(widget); + disconnect(widget, SIGNAL(geometryChanged()), this, SLOT(slotGeometryOfGroupHeaderParentChanged())); + } +} + +void KItemListView::updateVisibleGroupHeaders() +{ + Q_ASSERT(m_grouped); + m_layouter->markAsDirty(); + + QHashIterator it(m_visibleItems); + while (it.hasNext()) { + it.next(); + updateGroupHeaderForWidget(it.value()); + } +} + +int KItemListView::groupIndexForItem(int index) const +{ + Q_ASSERT(m_grouped); + + const QList > groups = model()->groups(); + if (groups.isEmpty()) { + return -1; + } + + int min = 0; + int max = groups.count() - 1; + int mid = 0; + do { + mid = (min + max) / 2; + if (index > groups[mid].first) { + min = mid + 1; + } else { + max = mid - 1; + } + } while (groups[mid].first != index && min <= max); + + if (min > max) { + while (groups[mid].first > index && mid > 0) { + --mid; + } + } + + return mid; +} + +void KItemListView::updateAlternateBackgrounds() +{ + QHashIterator it(m_visibleItems); + while (it.hasNext()) { + it.next(); + updateAlternateBackgroundForWidget(it.value()); + } +} + +void KItemListView::updateAlternateBackgroundForWidget(KItemListWidget* widget) +{ + bool enabled = useAlternateBackgrounds(); + if (enabled) { + const int index = widget->index(); + enabled = (index & 0x1) > 0; + if (m_grouped) { + const int groupIndex = groupIndexForItem(index); + if (groupIndex >= 0) { + const QList > groups = model()->groups(); + const int indexOfFirstGroupItem = groups[groupIndex].first; + const int relativeIndex = index - indexOfFirstGroupItem; + enabled = (relativeIndex & 0x1) > 0; + } + } + } + widget->setAlternateBackground(enabled); +} + +bool KItemListView::useAlternateBackgrounds() const +{ + return m_itemSize.isEmpty() && m_visibleRoles.count() > 1; +} + +QHash KItemListView::preferredColumnWidths(const KItemRangeList& itemRanges) const +{ + QElapsedTimer timer; + timer.start(); + + QHash widths; + + // Calculate the minimum width for each column that is required + // to show the headline unclipped. + const QFontMetricsF fontMetrics(m_headerWidget->font()); + const int gripMargin = m_headerWidget->style()->pixelMetric(QStyle::PM_HeaderGripMargin); + const int headerMargin = m_headerWidget->style()->pixelMetric(QStyle::PM_HeaderMargin); + foreach (const QByteArray& visibleRole, visibleRoles()) { + const QString headerText = m_model->roleDescription(visibleRole); + const qreal headerWidth = fontMetrics.width(headerText) + gripMargin + headerMargin * 2; + widths.insert(visibleRole, headerWidth); + } + + // Calculate the preferred column withs for each item and ignore values + // smaller than the width for showing the headline unclipped. + const KItemListWidgetCreatorBase* creator = widgetCreator(); + int calculatedItemCount = 0; + bool maxTimeExceeded = false; + foreach (const KItemRange& itemRange, itemRanges) { + const int startIndex = itemRange.index; + const int endIndex = startIndex + itemRange.count - 1; + + for (int i = startIndex; i <= endIndex; ++i) { + foreach (const QByteArray& visibleRole, visibleRoles()) { + qreal maxWidth = widths.value(visibleRole, 0); + const qreal width = creator->preferredRoleColumnWidth(visibleRole, i, this); + maxWidth = qMax(width, maxWidth); + widths.insert(visibleRole, maxWidth); + } + + if (calculatedItemCount > 100 && timer.elapsed() > 200) { + // When having several thousands of items calculating the sizes can get + // very expensive. We accept a possibly too small role-size in favour + // of having no blocking user interface. + maxTimeExceeded = true; + break; + } + ++calculatedItemCount; + } + if (maxTimeExceeded) { + break; + } + } + + return widths; +} + +void KItemListView::applyColumnWidthsFromHeader() +{ + // Apply the new size to the layouter + const qreal requiredWidth = columnWidthsSum(); + const QSizeF dynamicItemSize(qMax(size().width(), requiredWidth), + m_itemSize.height()); + m_layouter->setItemSize(dynamicItemSize); + + // Update the role sizes for all visible widgets + QHashIterator it(m_visibleItems); + while (it.hasNext()) { + it.next(); + updateWidgetColumnWidths(it.value()); + } +} + +void KItemListView::updateWidgetColumnWidths(KItemListWidget* widget) +{ + foreach (const QByteArray& role, m_visibleRoles) { + widget->setColumnWidth(role, m_headerWidget->columnWidth(role)); + } +} + +void KItemListView::updatePreferredColumnWidths(const KItemRangeList& itemRanges) +{ + Q_ASSERT(m_itemSize.isEmpty()); + const int itemCount = m_model->count(); + int rangesItemCount = 0; + foreach (const KItemRange& range, itemRanges) { + rangesItemCount += range.count; + } + + if (itemCount == rangesItemCount) { + const QHash preferredWidths = preferredColumnWidths(itemRanges); + foreach (const QByteArray& role, m_visibleRoles) { + m_headerWidget->setPreferredColumnWidth(role, preferredWidths.value(role)); + } + } else { + // Only a sub range of the roles need to be determined. + // The chances are good that the widths of the sub ranges + // already fit into the available widths and hence no + // expensive update might be required. + bool changed = false; + + const QHash updatedWidths = preferredColumnWidths(itemRanges); + QHashIterator it(updatedWidths); + while (it.hasNext()) { + it.next(); + const QByteArray& role = it.key(); + const qreal updatedWidth = it.value(); + const qreal currentWidth = m_headerWidget->preferredColumnWidth(role); + if (updatedWidth > currentWidth) { + m_headerWidget->setPreferredColumnWidth(role, updatedWidth); + changed = true; + } + } + + if (!changed) { + // All the updated sizes are smaller than the current sizes and no change + // of the stretched roles-widths is required + return; + } + } + + if (m_headerWidget->automaticColumnResizing()) { + applyAutomaticColumnWidths(); + } +} + +void KItemListView::updatePreferredColumnWidths() +{ + if (m_model) { + updatePreferredColumnWidths(KItemRangeList() << KItemRange(0, m_model->count())); + } +} + +void KItemListView::applyAutomaticColumnWidths() +{ + Q_ASSERT(m_itemSize.isEmpty()); + Q_ASSERT(m_headerWidget->automaticColumnResizing()); + if (m_visibleRoles.isEmpty()) { + return; + } + + // Calculate the maximum size of an item by considering the + // visible role sizes and apply them to the layouter. If the + // size does not use the available view-size the size of the + // first role will get stretched. + + foreach (const QByteArray& role, m_visibleRoles) { + const qreal preferredWidth = m_headerWidget->preferredColumnWidth(role); + m_headerWidget->setColumnWidth(role, preferredWidth); + } + + const QByteArray firstRole = m_visibleRoles.first(); + qreal firstColumnWidth = m_headerWidget->columnWidth(firstRole); + QSizeF dynamicItemSize = m_itemSize; + + qreal requiredWidth = columnWidthsSum(); + const qreal availableWidth = size().width(); + if (requiredWidth < availableWidth) { + // Stretch the first column to use the whole remaining width + firstColumnWidth += availableWidth - requiredWidth; + m_headerWidget->setColumnWidth(firstRole, firstColumnWidth); + } else if (requiredWidth > availableWidth && m_visibleRoles.count() > 1) { + // Shrink the first column to be able to show as much other + // columns as possible + qreal shrinkedFirstColumnWidth = firstColumnWidth - requiredWidth + availableWidth; + + // TODO: A proper calculation of the minimum width depends on the implementation + // of KItemListWidget. Probably a kind of minimum size-hint should be introduced + // later. + const qreal minWidth = qMin(firstColumnWidth, qreal(m_styleOption.iconSize * 2 + 200)); + if (shrinkedFirstColumnWidth < minWidth) { + shrinkedFirstColumnWidth = minWidth; + } + + m_headerWidget->setColumnWidth(firstRole, shrinkedFirstColumnWidth); + requiredWidth -= firstColumnWidth - shrinkedFirstColumnWidth; + } + + dynamicItemSize.rwidth() = qMax(requiredWidth, availableWidth); + + m_layouter->setItemSize(dynamicItemSize); + + // Update the role sizes for all visible widgets + QHashIterator it(m_visibleItems); + while (it.hasNext()) { + it.next(); + updateWidgetColumnWidths(it.value()); + } +} + +qreal KItemListView::columnWidthsSum() const +{ + qreal widthsSum = 0; + foreach (const QByteArray& role, m_visibleRoles) { + widthsSum += m_headerWidget->columnWidth(role); + } + return widthsSum; +} + +QRectF KItemListView::headerBoundaries() const +{ + return m_headerWidget->isVisible() ? m_headerWidget->geometry() : QRectF(); +} + +bool KItemListView::changesItemGridLayout(const QSizeF& newGridSize, + const QSizeF& newItemSize, + const QSizeF& newItemMargin) const +{ + if (newItemSize.isEmpty() || newGridSize.isEmpty()) { + return false; + } + + if (m_layouter->scrollOrientation() == Qt::Vertical) { + const qreal itemWidth = m_layouter->itemSize().width(); + if (itemWidth > 0) { + const int newColumnCount = itemsPerSize(newGridSize.width(), + newItemSize.width(), + newItemMargin.width()); + if (m_model->count() > newColumnCount) { + const int oldColumnCount = itemsPerSize(m_layouter->size().width(), + itemWidth, + m_layouter->itemMargin().width()); + return oldColumnCount != newColumnCount; + } + } + } else { + const qreal itemHeight = m_layouter->itemSize().height(); + if (itemHeight > 0) { + const int newRowCount = itemsPerSize(newGridSize.height(), + newItemSize.height(), + newItemMargin.height()); + if (m_model->count() > newRowCount) { + const int oldRowCount = itemsPerSize(m_layouter->size().height(), + itemHeight, + m_layouter->itemMargin().height()); + return oldRowCount != newRowCount; + } + } + } + + return false; +} + +bool KItemListView::animateChangedItemCount(int changedItemCount) const +{ + if (m_itemSize.isEmpty()) { + // We have only columns or only rows, but no grid: An animation is usually + // welcome when inserting or removing items. + return !supportsItemExpanding(); + } + + if (m_layouter->size().isEmpty() || m_layouter->itemSize().isEmpty()) { + return false; + } + + const int maximum = (scrollOrientation() == Qt::Vertical) + ? m_layouter->size().width() / m_layouter->itemSize().width() + : m_layouter->size().height() / m_layouter->itemSize().height(); + // Only animate if up to 2/3 of a row or column are inserted or removed + return changedItemCount <= maximum * 2 / 3; +} + + +bool KItemListView::scrollBarRequired(const QSizeF& size) const +{ + const QSizeF oldSize = m_layouter->size(); + + m_layouter->setSize(size); + const qreal maxOffset = m_layouter->maximumScrollOffset(); + m_layouter->setSize(oldSize); + + return m_layouter->scrollOrientation() == Qt::Vertical ? maxOffset > size.height() + : maxOffset > size.width(); +} + +int KItemListView::showDropIndicator(const QPointF& pos) +{ + QHashIterator it(m_visibleItems); + while (it.hasNext()) { + it.next(); + const KItemListWidget* widget = it.value(); + + const QPointF mappedPos = widget->mapFromItem(this, pos); + const QRectF rect = itemRect(widget->index()); + if (mappedPos.y() >= 0 && mappedPos.y() <= rect.height()) { + if (m_model->supportsDropping(widget->index())) { + // Keep 30% of the rectangle as the gap instead of always having a fixed gap + const int gap = qMax(4.0, 0.3 * rect.height()); + if (mappedPos.y() >= gap && mappedPos.y() <= rect.height() - gap) { + return -1; + } + } + + const bool isAboveItem = (mappedPos.y () < rect.height() / 2); + const qreal y = isAboveItem ? rect.top() : rect.bottom(); + + const QRectF draggingInsertIndicator(rect.left(), y, rect.width(), 1); + if (m_dropIndicator != draggingInsertIndicator) { + m_dropIndicator = draggingInsertIndicator; + update(); + } + + int index = widget->index(); + if (!isAboveItem) { + ++index; + } + return index; + } + } + + const QRectF firstItemRect = itemRect(firstVisibleIndex()); + return (pos.y() <= firstItemRect.top()) ? 0 : -1; +} + +void KItemListView::hideDropIndicator() +{ + if (!m_dropIndicator.isNull()) { + m_dropIndicator = QRectF(); + update(); + } +} + +void KItemListView::updateGroupHeaderHeight() +{ + qreal groupHeaderHeight = m_styleOption.fontMetrics.height(); + qreal groupHeaderMargin = 0; + + if (scrollOrientation() == Qt::Horizontal) { + // The vertical margin above and below the header should be + // equal to the horizontal margin, not the vertical margin + // from m_styleOption. + groupHeaderHeight += 2 * m_styleOption.horizontalMargin; + groupHeaderMargin = m_styleOption.horizontalMargin; + } else if (m_itemSize.isEmpty()){ + groupHeaderHeight += 4 * m_styleOption.padding; + groupHeaderMargin = m_styleOption.iconSize / 2; + } else { + groupHeaderHeight += 2 * m_styleOption.padding + m_styleOption.verticalMargin; + groupHeaderMargin = m_styleOption.iconSize / 4; + } + m_layouter->setGroupHeaderHeight(groupHeaderHeight); + m_layouter->setGroupHeaderMargin(groupHeaderMargin); + + updateVisibleGroupHeaders(); +} + +void KItemListView::updateSiblingsInformation(int firstIndex, int lastIndex) +{ + if (!supportsItemExpanding() || !m_model) { + return; + } + + if (firstIndex < 0 || lastIndex < 0) { + firstIndex = m_layouter->firstVisibleIndex(); + lastIndex = m_layouter->lastVisibleIndex(); + } else { + const bool isRangeVisible = (firstIndex <= m_layouter->lastVisibleIndex() && + lastIndex >= m_layouter->firstVisibleIndex()); + if (!isRangeVisible) { + return; + } + } + + int previousParents = 0; + QBitArray previousSiblings; + + // The rootIndex describes the first index where the siblings get + // calculated from. For the calculation the upper most parent item + // is required. For performance reasons it is checked first whether + // the visible items before or after the current range already + // contain a siblings information which can be used as base. + int rootIndex = firstIndex; + + KItemListWidget* widget = m_visibleItems.value(firstIndex - 1); + if (!widget) { + // There is no visible widget before the range, check whether there + // is one after the range: + widget = m_visibleItems.value(lastIndex + 1); + if (widget) { + // The sibling information of the widget may only be used if + // all items of the range have the same number of parents. + const int parents = m_model->expandedParentsCount(lastIndex + 1); + for (int i = lastIndex; i >= firstIndex; --i) { + if (m_model->expandedParentsCount(i) != parents) { + widget = 0; + break; + } + } + } + } + + if (widget) { + // Performance optimization: Use the sibling information of the visible + // widget beside the given range. + previousSiblings = widget->siblingsInformation(); + if (previousSiblings.isEmpty()) { + return; + } + previousParents = previousSiblings.count() - 1; + previousSiblings.truncate(previousParents); + } else { + // Potentially slow path: Go back to the upper most parent of firstIndex + // to be able to calculate the initial value for the siblings. + while (rootIndex > 0 && m_model->expandedParentsCount(rootIndex) > 0) { + --rootIndex; + } + } + + Q_ASSERT(previousParents >= 0); + for (int i = rootIndex; i <= lastIndex; ++i) { + // Update the parent-siblings in case if the current item represents + // a child or an upper parent. + const int currentParents = m_model->expandedParentsCount(i); + Q_ASSERT(currentParents >= 0); + if (previousParents < currentParents) { + previousParents = currentParents; + previousSiblings.resize(currentParents); + previousSiblings.setBit(currentParents - 1, hasSiblingSuccessor(i - 1)); + } else if (previousParents > currentParents) { + previousParents = currentParents; + previousSiblings.truncate(currentParents); + } + + if (i >= firstIndex) { + // The index represents a visible item. Apply the parent-siblings + // and update the sibling of the current item. + KItemListWidget* widget = m_visibleItems.value(i); + if (!widget) { + continue; + } + + QBitArray siblings = previousSiblings; + siblings.resize(siblings.count() + 1); + siblings.setBit(siblings.count() - 1, hasSiblingSuccessor(i)); + + widget->setSiblingsInformation(siblings); + } + } +} + +bool KItemListView::hasSiblingSuccessor(int index) const +{ + bool hasSuccessor = false; + const int parentsCount = m_model->expandedParentsCount(index); + int successorIndex = index + 1; + + // Search the next sibling + const int itemCount = m_model->count(); + while (successorIndex < itemCount) { + const int currentParentsCount = m_model->expandedParentsCount(successorIndex); + if (currentParentsCount == parentsCount) { + hasSuccessor = true; + break; + } else if (currentParentsCount < parentsCount) { + break; + } + ++successorIndex; + } + + if (m_grouped && hasSuccessor) { + // If the sibling is part of another group, don't mark it as + // successor as the group header is between the sibling connections. + for (int i = index + 1; i <= successorIndex; ++i) { + if (m_layouter->isFirstGroupItem(i)) { + hasSuccessor = false; + break; + } + } + } + + return hasSuccessor; +} + +void KItemListView::disconnectRoleEditingSignals(int index) +{ + KItemListWidget* widget = m_visibleItems.value(index); + if (!widget) { + return; + } + + widget->disconnect(SIGNAL(roleEditingCanceled(int,QByteArray,QVariant)), this); + widget->disconnect(SIGNAL(roleEditingFinished(int,QByteArray,QVariant)), this); +} + +int KItemListView::calculateAutoScrollingIncrement(int pos, int range, int oldInc) +{ + int inc = 0; + + const int minSpeed = 4; + const int maxSpeed = 128; + const int speedLimiter = 96; + const int autoScrollBorder = 64; + + // Limit the increment that is allowed to be added in comparison to 'oldInc'. + // This assures that the autoscrolling speed grows gradually. + const int incLimiter = 1; + + if (pos < autoScrollBorder) { + inc = -minSpeed + qAbs(pos - autoScrollBorder) * (pos - autoScrollBorder) / speedLimiter; + inc = qMax(inc, -maxSpeed); + inc = qMax(inc, oldInc - incLimiter); + } else if (pos > range - autoScrollBorder) { + inc = minSpeed + qAbs(pos - range + autoScrollBorder) * (pos - range + autoScrollBorder) / speedLimiter; + inc = qMin(inc, maxSpeed); + inc = qMin(inc, oldInc + incLimiter); + } + + return inc; +} + +int KItemListView::itemsPerSize(qreal size, qreal itemSize, qreal itemMargin) +{ + const qreal availableSize = size - itemMargin; + const int count = availableSize / (itemSize + itemMargin); + return count; +} + + + +KItemListCreatorBase::~KItemListCreatorBase() +{ + qDeleteAll(m_recycleableWidgets); + qDeleteAll(m_createdWidgets); +} + +void KItemListCreatorBase::addCreatedWidget(QGraphicsWidget* widget) +{ + m_createdWidgets.insert(widget); +} + +void KItemListCreatorBase::pushRecycleableWidget(QGraphicsWidget* widget) +{ + Q_ASSERT(m_createdWidgets.contains(widget)); + m_createdWidgets.remove(widget); + + if (m_recycleableWidgets.count() < 100) { + m_recycleableWidgets.append(widget); + widget->setVisible(false); + } else { + delete widget; + } +} + +QGraphicsWidget* KItemListCreatorBase::popRecycleableWidget() +{ + if (m_recycleableWidgets.isEmpty()) { + return 0; + } + + QGraphicsWidget* widget = m_recycleableWidgets.takeLast(); + m_createdWidgets.insert(widget); + return widget; +} + +KItemListWidgetCreatorBase::~KItemListWidgetCreatorBase() +{ +} + +void KItemListWidgetCreatorBase::recycle(KItemListWidget* widget) +{ + widget->setParentItem(0); + widget->setOpacity(1.0); + pushRecycleableWidget(widget); +} + +KItemListGroupHeaderCreatorBase::~KItemListGroupHeaderCreatorBase() +{ +} + +void KItemListGroupHeaderCreatorBase::recycle(KItemListGroupHeader* header) +{ + header->setOpacity(1.0); + pushRecycleableWidget(header); +} + +#include "moc_kitemlistview.cpp" diff --git a/dolphin/src/kitemviews/kitemlistview.h b/dolphin/src/kitemviews/kitemlistview.h new file mode 100644 index 00000000..80bae340 --- /dev/null +++ b/dolphin/src/kitemviews/kitemlistview.h @@ -0,0 +1,913 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz * + * * + * Based on the Itemviews NG project from Trolltech Labs: * + * http://qt.gitorious.org/qt-labs/itemviews-ng * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef KITEMLISTVIEW_H +#define KITEMLISTVIEW_H + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +class KItemListController; +class KItemListGroupHeaderCreatorBase; +class KItemListHeader; +class KItemListHeaderWidget; +class KItemListSizeHintResolver; +class KItemListRubberBand; +class KItemListViewAnimation; +class KItemListViewLayouter; +class KItemListWidget; +class KItemListWidgetInformant; +class KItemListWidgetCreatorBase; +class KItemListViewCreatorBase; + +/** + * @brief Represents the view of an item-list. + * + * The view is responsible for showing the items of the model within + * a GraphicsItem. Each visible item is represented by a KItemListWidget. + * + * The created view must be applied to the KItemListController with + * KItemListController::setView() or with the constructor of + * KItemListController. + * + * @see KItemListWidget + * @see KItemModelBase + */ +class DOLPHINPRIVATE_EXPORT KItemListView : public QGraphicsWidget +{ + Q_OBJECT + + Q_PROPERTY(qreal scrollOffset READ scrollOffset WRITE setScrollOffset) + Q_PROPERTY(qreal itemOffset READ itemOffset WRITE setItemOffset) + +public: + KItemListView(QGraphicsWidget* parent = 0); + virtual ~KItemListView(); + + /** + * Offset of the scrollbar that represents the scroll-orientation + * (see setScrollOrientation()). + */ + void setScrollOffset(qreal offset); + qreal scrollOffset() const; + + qreal maximumScrollOffset() const; + + /** + * Offset related to an item, that does not fit into the available + * size of the listview. If the scroll-orientation is vertical + * the item-offset describes the offset of the horizontal axe, if + * the scroll-orientation is horizontal the item-offset describes + * the offset of the vertical axe. + */ + void setItemOffset(qreal scrollOffset); + qreal itemOffset() const; + + qreal maximumItemOffset() const; + + int maximumVisibleItems() const; + + void setVisibleRoles(const QList& roles); + QList visibleRoles() const; + + /** + * If set to true an automatic scrolling is done as soon as the + * mouse is moved near the borders of the view. Per default + * the automatic scrolling is turned off. + */ + void setAutoScroll(bool enabled); + bool autoScroll() const; + + /** + * If set to true selection-toggles will be shown when hovering + * an item. Per default the selection-toggles are disabled. + */ + void setEnabledSelectionToggles(bool enabled); + bool enabledSelectionToggles() const; + + /** + * @return Controller of the item-list. The controller gets + * initialized by KItemListController::setView() and will + * result in calling KItemListController::onControllerChanged(). + */ + KItemListController* controller() const; + + /** + * @return Model of the item-list. The model gets + * initialized by KItemListController::setModel() and will + * result in calling KItemListController::onModelChanged(). + */ + KItemModelBase* model() const; + + /** + * Sets the creator that creates a widget showing the + * content of one model-item. Usually it is sufficient + * to implement a custom widget X derived from KItemListWidget and + * set the creator by: + * + * itemListView->setWidgetCreator(new KItemListWidgetCreator()); + * + * The ownership of the widget creator is transferred to + * the item-list view. + **/ + void setWidgetCreator(KItemListWidgetCreatorBase* widgetCreator); + KItemListWidgetCreatorBase* widgetCreator() const; + + /** + * Sets the creator that creates a group header. Usually it is sufficient + * to implement a custom header widget X derived from KItemListGroupHeader and + * set the creator by: + * + * itemListView->setGroupHeaderCreator(new KItemListGroupHeaderCreator()); + * + * The ownership of the gropup header creator is transferred to + * the item-list view. + **/ + void setGroupHeaderCreator(KItemListGroupHeaderCreatorBase* groupHeaderCreator); + KItemListGroupHeaderCreatorBase* groupHeaderCreator() const; + + /** + * @return The basic size of all items. The size of an item may be larger than + * the basic size (see KItemListView::itemSizeHint() and KItemListView::itemRect()). + */ + QSizeF itemSize() const; + + const KItemListStyleOption& styleOption() const; + + /** @reimp */ + virtual void setGeometry(const QRectF& rect); + + /** + * @return The page step which should be used by the vertical scroll bar. + * This is the height of the view except for the header widget. + */ + qreal verticalPageStep() const; + + /** + * @return Index of the item that is below the point \a pos. + * The position is relative to the upper right of + * the visible area. Only (at least partly) visible + * items are considered. -1 is returned if no item is + * below the position. + */ + int itemAt(const QPointF& pos) const; + bool isAboveSelectionToggle(int index, const QPointF& pos) const; + bool isAboveExpansionToggle(int index, const QPointF& pos) const; + + /** + * @return Index of the first item that is at least partly visible. + * -1 is returned if the model contains no items. + */ + int firstVisibleIndex() const; + + /** + * @return Index of the last item that is at least partly visible. + * -1 is returned if the model contains no items. + */ + int lastVisibleIndex() const; + + /** + * @return Calculates the required size for all items in the model. + * It might be larger than KItemListView::itemSize(). + * In this case the layout grid will be stretched to assure an + * unclipped item. + * NOTE: the logical height (width) is actually the + * width (height) if the scroll orientation is Qt::Vertical! + */ + void calculateItemSizeHints(QVector& logicalHeightHints, qreal& logicalWidthHint) const; + + /** + * If set to true, items having child-items can be expanded to show the child-items as + * part of the view. Per default the expanding of items is is disabled. If expanding of + * items is enabled, the methods KItemModelBase::setExpanded(), KItemModelBase::isExpanded(), + * KItemModelBase::isExpandable() and KItemModelBase::expandedParentsCount() + * must be reimplemented. The view-implementation + * has to take care itself how to visually represent the expanded items provided + * by the model. + */ + void setSupportsItemExpanding(bool supportsExpanding); + bool supportsItemExpanding() const; + + /** + * @return The rectangle of the item relative to the top/left of + * the currently visible area (see KItemListView::offset()). + */ + QRectF itemRect(int index) const; + + /** + * @return The context rectangle of the item relative to the top/left of + * the currently visible area (see KItemListView::offset()). The + * context rectangle is defined by by the united rectangle of + * the icon rectangle and the text rectangle (see KItemListWidget::iconRect() + * and KItemListWidget::textRect()) and is useful as reference for e.g. aligning + * a tooltip or a context-menu for an item. Note that a context rectangle will + * only be returned for (at least partly) visible items. An empty rectangle will + * be returned for fully invisible items. + */ + QRectF itemContextRect(int index) const; + + /** + * Scrolls to the item with the index \a index so that the item + * will be fully visible. + */ + void scrollToItem(int index); + + /** + * If several properties of KItemListView are changed synchronously, it is + * recommended to encapsulate the calls between beginTransaction() and endTransaction(). + * This prevents unnecessary and expensive layout-calculations. + */ + void beginTransaction(); + + /** + * Counterpart to beginTransaction(). The layout changes will only be animated if + * all property changes between beginTransaction() and endTransaction() support + * animations. + */ + void endTransaction(); + + bool isTransactionActive() const; + + /** + * Turns on the header if \p visible is true. Per default the + * header is not visible. Usually the header is turned on when + * showing a classic "table-view" to describe the shown columns. + */ + void setHeaderVisible(bool visible); + bool isHeaderVisible() const; + + /** + * @return Header of the list. The header is also available if it is not shown + * (see KItemListView::setHeaderShown()). + */ + KItemListHeader* header() const; + + /** + * @return Pixmap that is used for a drag operation based on the + * items given by \a indexes. + */ + virtual QPixmap createDragPixmap(const KItemSet& indexes) const; + + /** + * Lets the user edit the role \a role for item with the index \a index. + */ + void editRole(int index, const QByteArray& role); + + /** + * @reimp + */ + virtual void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget = 0); + +signals: + void scrollOrientationChanged(Qt::Orientation current, Qt::Orientation previous); + void scrollOffsetChanged(qreal current, qreal previous); + void maximumScrollOffsetChanged(qreal current, qreal previous); + void itemOffsetChanged(qreal current, qreal previous); + void maximumItemOffsetChanged(qreal current, qreal previous); + void scrollTo(qreal newOffset); + + /** + * Is emitted if the user has changed the sort order by clicking on a + * header item (see KItemListView::setHeaderShown()). The sort order + * of the model has already been adjusted to + * the current sort order. Note that no signal will be emitted if the + * sort order of the model has been changed without user interaction. + */ + void sortOrderChanged(Qt::SortOrder current, Qt::SortOrder previous); + + /** + * Is emitted if the user has changed the sort role by clicking on a + * header item (see KItemListView::setHeaderShown()). The sort role + * of the model has already been adjusted to + * the current sort role. Note that no signal will be emitted if the + * sort role of the model has been changed without user interaction. + */ + void sortRoleChanged(const QByteArray& current, const QByteArray& previous); + + /** + * Is emitted if the user has changed the visible roles by moving a header + * item (see KItemListView::setHeaderShown()). Note that no signal will be + * emitted if the roles have been changed without user interaction by + * KItemListView::setVisibleRoles(). + */ + void visibleRolesChanged(const QList& current, const QList& previous); + + void roleEditingCanceled(int index, const QByteArray& role, const QVariant& value); + void roleEditingFinished(int index, const QByteArray& role, const QVariant& value); + +protected: + virtual QVariant itemChange(GraphicsItemChange change, const QVariant &value); + void setItemSize(const QSizeF& size); + void setStyleOption(const KItemListStyleOption& option); + + /** + * If the scroll-orientation is vertical, the items are ordered + * from top to bottom (= default setting). If the scroll-orientation + * is horizontal, the items are ordered from left to right. + */ + void setScrollOrientation(Qt::Orientation orientation); + Qt::Orientation scrollOrientation() const; + + /** + * Factory method for creating a default widget-creator. The method will be used + * in case if setWidgetCreator() has not been set by the application. + * @return New instance of the widget-creator that should be used per + * default. + */ + virtual KItemListWidgetCreatorBase* defaultWidgetCreator() const; + + /** + * Factory method for creating a default group-header-creator. The method will be used + * in case if setGroupHeaderCreator() has not been set by the application. + * @return New instance of the group-header-creator that should be used per + * default. + */ + virtual KItemListGroupHeaderCreatorBase* defaultGroupHeaderCreator() const; + + /** + * Is called when creating a new KItemListWidget instance and allows derived + * classes to do a custom initialization. + */ + virtual void initializeItemListWidget(KItemListWidget* item); + + /** + * @return True if at least one of the changed roles \p changedRoles might result + * in the need to update the item-size hint (see KItemListView::itemSizeHint()). + * Per default true is returned which means on each role-change of existing items + * the item-size hints are recalculated. For performance reasons it is recommended + * to return false in case if a role-change will not result in a changed + * item-size hint. + */ + virtual bool itemSizeHintUpdateRequired(const QSet& changedRoles) const; + + virtual void onControllerChanged(KItemListController* current, KItemListController* previous); + virtual void onModelChanged(KItemModelBase* current, KItemModelBase* previous); + + virtual void onScrollOrientationChanged(Qt::Orientation current, Qt::Orientation previous); + virtual void onItemSizeChanged(const QSizeF& current, const QSizeF& previous); + virtual void onScrollOffsetChanged(qreal current, qreal previous); + virtual void onVisibleRolesChanged(const QList& current, const QList& previous); + virtual void onStyleOptionChanged(const KItemListStyleOption& current, const KItemListStyleOption& previous); + virtual void onSupportsItemExpandingChanged(bool supportsExpanding); + + virtual void onTransactionBegin(); + virtual void onTransactionEnd(); + + virtual bool event(QEvent* event); + virtual void mousePressEvent(QGraphicsSceneMouseEvent* event); + virtual void mouseMoveEvent(QGraphicsSceneMouseEvent* event); + virtual void dragEnterEvent(QGraphicsSceneDragDropEvent* event); + virtual void dragMoveEvent(QGraphicsSceneDragDropEvent* event); + virtual void dragLeaveEvent(QGraphicsSceneDragDropEvent* event); + virtual void dropEvent(QGraphicsSceneDragDropEvent* event); + + QList visibleItemListWidgets() const; + + virtual void updateFont(); + virtual void updatePalette(); + +protected slots: + virtual void slotItemsInserted(const KItemRangeList& itemRanges); + virtual void slotItemsRemoved(const KItemRangeList& itemRanges); + virtual void slotItemsMoved(const KItemRange& itemRange, const QList& movedToIndexes); + virtual void slotItemsChanged(const KItemRangeList& itemRanges, + const QSet& roles); + virtual void slotGroupsChanged(); + + virtual void slotGroupedSortingChanged(bool current); + virtual void slotSortOrderChanged(Qt::SortOrder current, Qt::SortOrder previous); + virtual void slotSortRoleChanged(const QByteArray& current, const QByteArray& previous); + virtual void slotCurrentChanged(int current, int previous); + virtual void slotSelectionChanged(const KItemSet& current, const KItemSet& previous); + +private slots: + void slotAnimationFinished(QGraphicsWidget* widget, + KItemListViewAnimation::AnimationType type); + void slotLayoutTimerFinished(); + + void slotRubberBandPosChanged(); + void slotRubberBandActivationChanged(bool active); + + /** + * Is invoked if the column-width of one role in the header has + * been changed by the user. The automatic resizing of columns + * will be turned off as soon as this method has been called at + * least once. + */ + void slotHeaderColumnWidthChanged(const QByteArray& role, + qreal currentWidth, + qreal previousWidth); + + /** + * Is invoked if a column has been moved by the user. Applies + * the moved role to the view. + */ + void slotHeaderColumnMoved(const QByteArray& role, + int currentIndex, + int previousIndex); + + /** + * Triggers the autoscrolling if autoScroll() is enabled by checking the + * current mouse position. If the mouse position is within the autoscroll + * margins a timer will be started that periodically triggers the autoscrolling. + */ + void triggerAutoScrolling(); + + /** + * Is invoked if the geometry of the parent-widget from a group-header has been + * changed. The x-position and width of the group-header gets adjusted to assure + * that it always spans the whole width even during temporary transitions of the + * parent widget. + */ + void slotGeometryOfGroupHeaderParentChanged(); + + void slotRoleEditingCanceled(int index, const QByteArray& role, const QVariant& value); + void slotRoleEditingFinished(int index, const QByteArray& role, const QVariant& value); + +private: + enum LayoutAnimationHint + { + NoAnimation, + Animation + }; + + enum SizeType + { + LayouterSize, + ItemSize + }; + + void setController(KItemListController* controller); + void setModel(KItemModelBase* model); + + KItemListRubberBand* rubberBand() const; + + void doLayout(LayoutAnimationHint hint, int changedIndex = 0, int changedCount = 0); + + /** + * Helper method for doLayout: Returns a list of items that can be reused for the visible + * area. Invisible group headers get recycled. The reusable items are items that are + * invisible. If the animation hint is 'Animation' then items that are currently animated + * won't be reused. Reusing items is faster in comparison to deleting invisible + * items and creating a new instance for visible items. + */ + QList recycleInvisibleItems(int firstVisibleIndex, + int lastVisibleIndex, + LayoutAnimationHint hint); + + /** + * Helper method for doLayout: Starts a moving-animation for the widget to the given + * new position. The moving-animation is only started if the new position is within + * the same row or column, otherwise the create-animation is used instead. + * @return True if the moving-animation has been applied. + */ + bool moveWidget(KItemListWidget* widget, const QPointF& newPos); + + void emitOffsetChanges(); + + KItemListWidget* createWidget(int index); + void recycleWidget(KItemListWidget* widget); + + /** + * Changes the index of the widget to \a index and assures a consistent + * update for m_visibleItems and m_visibleCells. The cell-information + * for the new index will not be updated and be initialized as empty cell. + */ + void setWidgetIndex(KItemListWidget* widget, int index); + + /** + * Changes the index of the widget to \a index. In opposite to + * setWidgetIndex() the cell-information for the widget gets updated. + * This update gives doLayout() the chance to animate the moving + * of the item visually (see moveWidget()). + */ + void moveWidgetToIndex(KItemListWidget* widget, int index); + + /** + * Helper method for prepareLayoutForIncreasedItemCount(). + */ + void setLayouterSize(const QSizeF& size, SizeType sizeType); + + /** + * Helper method for createWidget() and setWidgetIndex() to update the properties + * of the itemlist widget. + */ + void updateWidgetProperties(KItemListWidget* widget, int index); + + /** + * Helper method for updateWidgetPropertes() to create or update + * the itemlist group-header. + */ + void updateGroupHeaderForWidget(KItemListWidget* widget); + + /** + * Updates the position and size of the group-header that belongs + * to the itemlist widget \a widget. The given widget must represent + * the first item of a group. + */ + void updateGroupHeaderLayout(KItemListWidget* widget); + + /** + * Recycles the group-header for the widget. + */ + void recycleGroupHeaderForWidget(KItemListWidget* widget); + + /** + * Helper method for slotGroupedSortingChanged(), slotSortOrderChanged() + * and slotSortRoleChanged(): Iterates through all visible items and updates + * the group-header widgets. + */ + void updateVisibleGroupHeaders(); + + /** + * @return Index for the item in the list returned by KItemModelBase::groups() + * that represents the group where the item with the index \a index + * belongs to. -1 is returned if no groups are available. + */ + int groupIndexForItem(int index) const; + + /** + * Updates the alternate background for all visible items. + * @see updateAlternateBackgroundForWidget() + */ + void updateAlternateBackgrounds(); + + /** + * Updates the alternateBackground-property of the widget dependent + * on the state of useAlternateBackgrounds() and the grouping state. + */ + void updateAlternateBackgroundForWidget(KItemListWidget* widget); + + /** + * @return True if alternate backgrounds should be used for the items. + * This is the case if an empty item-size is given and if there + * is more than one visible role. + */ + bool useAlternateBackgrounds() const; + + /** + * @param itemRanges Items that must be checked for getting the widths of columns. + * @return The preferred width of the column of each visible role. The width will + * be respected if the width of the item size is <= 0 (see + * KItemListView::setItemSize()). Per default an empty hash + * is returned. + */ + QHash preferredColumnWidths(const KItemRangeList& itemRanges) const; + + /** + * Applies the column-widths from m_headerWidget to the layout + * of the view. + */ + void applyColumnWidthsFromHeader(); + + /** + * Applies the column-widths from m_headerWidget to \a widget. + */ + void updateWidgetColumnWidths(KItemListWidget* widget); + + /** + * Updates the preferred column-widths of m_groupHeaderWidget by + * invoking KItemListView::columnWidths(). + */ + void updatePreferredColumnWidths(const KItemRangeList& itemRanges); + + /** + * Convenience method for + * updatePreferredColumnWidths(KItemRangeList() << KItemRange(0, m_model->count()). + */ + void updatePreferredColumnWidths(); + + /** + * Resizes the column-widths of m_headerWidget based on the preferred widths + * and the vailable view-size. + */ + void applyAutomaticColumnWidths(); + + /** + * @return Sum of the widths of all columns. + */ + qreal columnWidthsSum() const; + + /** + * @return Boundaries of the header. An empty rectangle is returned + * if no header is shown. + */ + QRectF headerBoundaries() const; + + /** + * @return True if the number of columns or rows will be changed when applying + * the new grid- and item-size. Used to determine whether an animation + * should be done when applying the new layout. + */ + bool changesItemGridLayout(const QSizeF& newGridSize, + const QSizeF& newItemSize, + const QSizeF& newItemMargin) const; + + /** + * @param changedItemCount Number of inserted or removed items. + * @return True if the inserting or removing of items should be animated. + * No animation should be done if the number of items is too large + * to provide a pleasant animation. + */ + bool animateChangedItemCount(int changedItemCount) const; + + /** + * @return True if a scrollbar for the given scroll-orientation is required + * when using a size of \p size for the view. Calling the method is rather + * expansive as a temporary relayout needs to be done. + */ + bool scrollBarRequired(const QSizeF& size) const; + + /** + * Shows a drop-indicator between items dependent on the given + * cursor position. The cursor position is relative the the upper left + * edge of the view. + * @return Index of the item where the dropping is done. An index of -1 + * indicates that the item has been dropped after the last item. + */ + int showDropIndicator(const QPointF& pos); + void hideDropIndicator(); + + /** + * Applies the height of the group header to the layouter. The height + * depends on the used scroll orientation. + */ + void updateGroupHeaderHeight(); + + /** + * Updates the siblings-information for all visible items that are inside + * the range of \p firstIndex and \p lastIndex. If firstIndex or lastIndex + * is smaller than 0, the siblings-information for all visible items gets + * updated. + * @see KItemListWidget::setSiblingsInformation() + */ + void updateSiblingsInformation(int firstIndex = -1, int lastIndex = -1); + + /** + * Helper method for updateExpansionIndicators(). + * @return True if the item with the index \a index has a sibling successor + * (= the item is not the last item of the current hierarchy). + */ + bool hasSiblingSuccessor(int index) const; + + /** + * Helper method for slotRoleEditingCanceled() and slotRoleEditingFinished(). + * Disconnects the two Signals "roleEditingCanceled" and + * "roleEditingFinished" + */ + void disconnectRoleEditingSignals(int index); + + /** + * Helper function for triggerAutoScrolling(). + * @param pos Logical position of the mouse relative to the range. + * @param range Range of the visible area. + * @param oldInc Previous increment. Is used to assure that the increment + * increases only gradually. + * @return Scroll increment that should be added to the offset(). + * As soon as \a pos is inside the autoscroll-margin a + * value != 0 will be returned. + */ + static int calculateAutoScrollingIncrement(int pos, int range, int oldInc); + + /** + * Helper functions for changesItemCount(). + * @return The number of items that fit into the available size by + * respecting the size of the item and the margin between the items. + */ + static int itemsPerSize(qreal size, qreal itemSize, qreal itemMargin); + +private: + bool m_enabledSelectionToggles; + bool m_grouped; + bool m_supportsItemExpanding; + bool m_editingRole; + int m_activeTransactions; // Counter for beginTransaction()/endTransaction() + LayoutAnimationHint m_endTransactionAnimationHint; + + QSizeF m_itemSize; + KItemListController* m_controller; + KItemModelBase* m_model; + QList m_visibleRoles; + mutable KItemListWidgetCreatorBase* m_widgetCreator; + mutable KItemListGroupHeaderCreatorBase* m_groupHeaderCreator; + KItemListStyleOption m_styleOption; + + QHash m_visibleItems; + QHash m_visibleGroups; + + struct Cell + { + Cell() : column(-1), row(-1) {} + Cell(int c, int r) : column(c), row(r) {} + int column; + int row; + }; + QHash m_visibleCells; + + int m_scrollBarExtent; + KItemListSizeHintResolver* m_sizeHintResolver; + KItemListViewLayouter* m_layouter; + KItemListViewAnimation* m_animation; + + QTimer* m_layoutTimer; // Triggers an asynchronous doLayout() call. + qreal m_oldScrollOffset; + qreal m_oldMaximumScrollOffset; + qreal m_oldItemOffset; + qreal m_oldMaximumItemOffset; + + bool m_skipAutoScrollForRubberBand; + KItemListRubberBand* m_rubberBand; + + QPointF m_mousePos; + int m_autoScrollIncrement; + QTimer* m_autoScrollTimer; + + KItemListHeader* m_header; + KItemListHeaderWidget* m_headerWidget; + + // When dragging items into the view where the sort-role of the model + // is empty, a visual indicator should be shown during dragging where + // the dropping will happen. This indicator is specified by an index + // of the item. -1 means that no indicator will be shown at all. + // The m_dropIndicator is set by the KItemListController + // by KItemListView::showDropIndicator() and KItemListView::hideDropIndicator(). + QRectF m_dropIndicator; + + friend class KItemListContainer; // Accesses scrollBarRequired() + friend class KItemListHeader; // Accesses m_headerWidget + friend class KItemListController; + friend class KItemListControllerTest; +}; + +/** + * Allows to do a fast logical creation and deletion of QGraphicsWidgets + * by recycling existing QGraphicsWidgets instances. Is used by + * KItemListWidgetCreatorBase and KItemListGroupHeaderCreatorBase. + * @internal + */ +class DOLPHINPRIVATE_EXPORT KItemListCreatorBase +{ +public: + virtual ~KItemListCreatorBase(); + +protected: + void addCreatedWidget(QGraphicsWidget* widget); + void pushRecycleableWidget(QGraphicsWidget* widget); + QGraphicsWidget* popRecycleableWidget(); + +private: + QSet m_createdWidgets; + QList m_recycleableWidgets; +}; + +/** + * @brief Base class for creating KItemListWidgets. + * + * It is recommended that applications simply use the KItemListWidgetCreator-template class. + * For a custom implementation the methods create(), itemSizeHint() and preferredColumnWith() + * must be reimplemented. The intention of the widget creator is to prevent repetitive and + * expensive instantiations and deletions of KItemListWidgets by recycling existing widget + * instances. + */ +class DOLPHINPRIVATE_EXPORT KItemListWidgetCreatorBase : public KItemListCreatorBase +{ +public: + virtual ~KItemListWidgetCreatorBase(); + + virtual KItemListWidget* create(KItemListView* view) = 0; + + virtual void recycle(KItemListWidget* widget); + + virtual void calculateItemSizeHints(QVector& logicalHeightHints, qreal& logicalWidthHint, const KItemListView* view) const = 0; + + virtual qreal preferredRoleColumnWidth(const QByteArray& role, + int index, + const KItemListView* view) const = 0; +}; + +/** + * @brief Template class for creating KItemListWidgets. + */ +template +class KItemListWidgetCreator : public KItemListWidgetCreatorBase +{ +public: + KItemListWidgetCreator(); + virtual ~KItemListWidgetCreator(); + + virtual KItemListWidget* create(KItemListView* view); + + virtual void calculateItemSizeHints(QVector& logicalHeightHints, qreal& logicalWidthHint, const KItemListView* view) const; + + virtual qreal preferredRoleColumnWidth(const QByteArray& role, + int index, + const KItemListView* view) const; +private: + KItemListWidgetInformant* m_informant; +}; + +template +KItemListWidgetCreator::KItemListWidgetCreator() : + m_informant(T::createInformant()) +{ +} + +template +KItemListWidgetCreator::~KItemListWidgetCreator() +{ + delete m_informant; +} + +template +KItemListWidget* KItemListWidgetCreator::create(KItemListView* view) +{ + KItemListWidget* widget = static_cast(popRecycleableWidget()); + if (!widget) { + widget = new T(m_informant, view); + addCreatedWidget(widget); + } + return widget; +} + +template +void KItemListWidgetCreator::calculateItemSizeHints(QVector& logicalHeightHints, qreal& logicalWidthHint, const KItemListView* view) const +{ + return m_informant->calculateItemSizeHints(logicalHeightHints, logicalWidthHint, view); +} + +template +qreal KItemListWidgetCreator::preferredRoleColumnWidth(const QByteArray& role, + int index, + const KItemListView* view) const +{ + return m_informant->preferredRoleColumnWidth(role, index, view); +} + +/** + * @brief Base class for creating KItemListGroupHeaders. + * + * It is recommended that applications simply use the KItemListGroupHeaderCreator-template class. + * For a custom implementation the methods create() and recyle() must be reimplemented. + * The intention of the group-header creator is to prevent repetitive and expensive instantiations and + * deletions of KItemListGroupHeaders by recycling existing header instances. + */ +class DOLPHINPRIVATE_EXPORT KItemListGroupHeaderCreatorBase : public KItemListCreatorBase +{ +public: + virtual ~KItemListGroupHeaderCreatorBase(); + virtual KItemListGroupHeader* create(KItemListView* view) = 0; + virtual void recycle(KItemListGroupHeader* header); +}; + +template +class KItemListGroupHeaderCreator : public KItemListGroupHeaderCreatorBase +{ +public: + virtual ~KItemListGroupHeaderCreator(); + virtual KItemListGroupHeader* create(KItemListView* view); +}; + +template +KItemListGroupHeaderCreator::~KItemListGroupHeaderCreator() +{ +} + +template +KItemListGroupHeader* KItemListGroupHeaderCreator::create(KItemListView* view) +{ + KItemListGroupHeader* widget = static_cast(popRecycleableWidget()); + if (!widget) { + widget = new T(view); + addCreatedWidget(widget); + } + return widget; +} + +#endif diff --git a/dolphin/src/kitemviews/kitemlistwidget.cpp b/dolphin/src/kitemviews/kitemlistwidget.cpp new file mode 100644 index 00000000..d3d8bae5 --- /dev/null +++ b/dolphin/src/kitemviews/kitemlistwidget.cpp @@ -0,0 +1,529 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz * + * * + * Based on the Itemviews NG project from Trolltech Labs: * + * http://qt.gitorious.org/qt-labs/itemviews-ng * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "kitemlistwidget.h" + +#include "kitemlistview.h" +#include "kitemmodelbase.h" + +#include "private/kitemlistselectiontoggle.h" + +#include + +#include +#include +#include +#include +#include + +KItemListWidgetInformant::KItemListWidgetInformant() +{ +} + +KItemListWidgetInformant::~KItemListWidgetInformant() +{ +} + +KItemListWidget::KItemListWidget(KItemListWidgetInformant* informant, QGraphicsItem* parent) : + QGraphicsWidget(parent, 0), + m_informant(informant), + m_index(-1), + m_selected(false), + m_current(false), + m_hovered(false), + m_alternateBackground(false), + m_enabledSelectionToggle(false), + m_data(), + m_visibleRoles(), + m_columnWidths(), + m_styleOption(), + m_siblingsInfo(), + m_hoverOpacity(0), + m_hoverCache(0), + m_hoverAnimation(0), + m_selectionToggle(0), + m_editedRole() +{ +} + +KItemListWidget::~KItemListWidget() +{ + clearHoverCache(); +} + +void KItemListWidget::setIndex(int index) +{ + if (m_index != index) { + delete m_selectionToggle; + m_selectionToggle = 0; + + if (m_hoverAnimation) { + m_hoverAnimation->stop(); + m_hoverOpacity = 0; + } + clearHoverCache(); + + m_index = index; + } +} + +int KItemListWidget::index() const +{ + return m_index; +} + +void KItemListWidget::setData(const QHash& data, + const QSet& roles) +{ + clearHoverCache(); + if (roles.isEmpty()) { + m_data = data; + dataChanged(m_data); + } else { + foreach (const QByteArray& role, roles) { + m_data[role] = data[role]; + } + dataChanged(m_data, roles); + } + update(); +} + +QHash KItemListWidget::data() const +{ + return m_data; +} + +void KItemListWidget::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) +{ + Q_UNUSED(option); + + if (m_alternateBackground) { + const QColor backgroundColor = m_styleOption.palette.color(QPalette::AlternateBase); + const QRectF backgroundRect(0, 0, size().width(), size().height()); + painter->fillRect(backgroundRect, backgroundColor); + } + + if (m_selected && m_editedRole.isEmpty()) { + const QStyle::State activeState(isActiveWindow() ? QStyle::State_Active : 0); + drawItemStyleOption(painter, widget, activeState | + QStyle::State_Enabled | + QStyle::State_Selected | + QStyle::State_Item); + } + + if (m_current && m_editedRole.isEmpty()) { + QStyleOptionFocusRect focusRectOption; + focusRectOption.initFrom(widget); + focusRectOption.rect = textFocusRect().toRect(); + focusRectOption.state = QStyle::State_Enabled | QStyle::State_Item | QStyle::State_KeyboardFocusChange; + if (m_selected) { + focusRectOption.state |= QStyle::State_Selected; + } + + style()->drawPrimitive(QStyle::PE_FrameFocusRect, &focusRectOption, painter, widget); + } + + if (m_hoverOpacity > 0.0) { + if (!m_hoverCache) { + // Initialize the m_hoverCache pixmap to improve the drawing performance + // when fading the hover background + m_hoverCache = new QPixmap(size().toSize()); + m_hoverCache->fill(Qt::transparent); + + QPainter pixmapPainter(m_hoverCache); + const QStyle::State activeState(isActiveWindow() ? QStyle::State_Active : 0); + drawItemStyleOption(&pixmapPainter, widget, activeState | + QStyle::State_Enabled | + QStyle::State_MouseOver | + QStyle::State_Item); + } + + const qreal opacity = painter->opacity(); + painter->setOpacity(m_hoverOpacity * opacity); + painter->drawPixmap(0, 0, *m_hoverCache); + painter->setOpacity(opacity); + } +} + +void KItemListWidget::setVisibleRoles(const QList& roles) +{ + const QList previousRoles = m_visibleRoles; + m_visibleRoles = roles; + + visibleRolesChanged(roles, previousRoles); + update(); +} + +QList KItemListWidget::visibleRoles() const +{ + return m_visibleRoles; +} + + +void KItemListWidget::setColumnWidth(const QByteArray& role, qreal width) +{ + if (m_columnWidths.value(role) != width) { + const qreal previousWidth = width; + m_columnWidths.insert(role, width); + columnWidthChanged(role, width, previousWidth); + update(); + } +} + +qreal KItemListWidget::columnWidth(const QByteArray& role) const +{ + return m_columnWidths.value(role); +} + +void KItemListWidget::setStyleOption(const KItemListStyleOption& option) +{ + const KItemListStyleOption previous = m_styleOption; + clearHoverCache(); + m_styleOption = option; + + styleOptionChanged(option, previous); + update(); +} + +const KItemListStyleOption& KItemListWidget::styleOption() const +{ + return m_styleOption; +} + +void KItemListWidget::setSelected(bool selected) +{ + if (m_selected != selected) { + m_selected = selected; + if (m_selectionToggle) { + m_selectionToggle->setChecked(selected); + } + selectedChanged(selected); + update(); + } +} + +bool KItemListWidget::isSelected() const +{ + return m_selected; +} + +void KItemListWidget::setCurrent(bool current) +{ + if (m_current != current) { + m_current = current; + currentChanged(current); + update(); + } +} + +bool KItemListWidget::isCurrent() const +{ + return m_current; +} + +void KItemListWidget::setHovered(bool hovered) +{ + if (hovered == m_hovered) { + return; + } + + m_hovered = hovered; + + if (!m_hoverAnimation) { + m_hoverAnimation = new QPropertyAnimation(this, "hoverOpacity", this); + const int duration = (KGlobalSettings::graphicEffectsLevel() == KGlobalSettings::NoEffects) ? 1 : 200; + m_hoverAnimation->setDuration(duration); + connect(m_hoverAnimation, SIGNAL(finished()), this, SLOT(slotHoverAnimationFinished())); + } + m_hoverAnimation->stop(); + + if (hovered) { + const qreal startValue = qMax(hoverOpacity(), qreal(0.1)); + m_hoverAnimation->setStartValue(startValue); + m_hoverAnimation->setEndValue(1.0); + if (m_enabledSelectionToggle && !(QApplication::mouseButtons() & Qt::LeftButton)) { + initializeSelectionToggle(); + } + } else { + m_hoverAnimation->setStartValue(hoverOpacity()); + m_hoverAnimation->setEndValue(0.0); + } + + m_hoverAnimation->start(); + + hoveredChanged(hovered); + update(); +} + +bool KItemListWidget::isHovered() const +{ + return m_hovered; +} + +void KItemListWidget::setHoverPosition(const QPointF& pos) +{ + if (m_selectionToggle) { + m_selectionToggle->setHovered(selectionToggleRect().contains(pos)); + } +} + +void KItemListWidget::setAlternateBackground(bool enable) +{ + if (m_alternateBackground != enable) { + m_alternateBackground = enable; + alternateBackgroundChanged(enable); + update(); + } +} + +bool KItemListWidget::alternateBackground() const +{ + return m_alternateBackground; +} + +void KItemListWidget::setEnabledSelectionToggle(bool enable) +{ + if (m_enabledSelectionToggle != enable) { + m_enabledSelectionToggle = enable; + update(); + } +} + +bool KItemListWidget::enabledSelectionToggle() const +{ + return m_enabledSelectionToggle; +} + +void KItemListWidget::setSiblingsInformation(const QBitArray& siblings) +{ + const QBitArray previous = m_siblingsInfo; + m_siblingsInfo = siblings; + siblingsInformationChanged(m_siblingsInfo, previous); + update(); +} + +QBitArray KItemListWidget::siblingsInformation() const +{ + return m_siblingsInfo; +} + +void KItemListWidget::setEditedRole(const QByteArray& role) +{ + if (m_editedRole != role) { + const QByteArray previous = m_editedRole; + m_editedRole = role; + editedRoleChanged(role, previous); + } +} + +QByteArray KItemListWidget::editedRole() const +{ + return m_editedRole; +} + +bool KItemListWidget::contains(const QPointF& point) const +{ + if (!QGraphicsWidget::contains(point)) { + return false; + } + + return iconRect().contains(point) || + textRect().contains(point) || + expansionToggleRect().contains(point) || + selectionToggleRect().contains(point); +} + +QRectF KItemListWidget::textFocusRect() const +{ + return textRect(); +} + +QRectF KItemListWidget::selectionToggleRect() const +{ + return QRectF(); +} + +QRectF KItemListWidget::expansionToggleRect() const +{ + return QRectF(); +} + +QPixmap KItemListWidget::createDragPixmap(const QStyleOptionGraphicsItem* option, + QWidget* widget) +{ + QPixmap pixmap(size().toSize()); + pixmap.fill(Qt::transparent); + + QPainter painter(&pixmap); + + const bool oldAlternateBackground = m_alternateBackground; + const bool wasSelected = m_selected; + const bool wasHovered = m_hovered; + + setAlternateBackground(false); + setSelected(false); + setHovered(false); + + paint(&painter, option, widget); + + setAlternateBackground(oldAlternateBackground); + setSelected(wasSelected); + setHovered(wasHovered); + + return pixmap; +} + +void KItemListWidget::dataChanged(const QHash& current, + const QSet& roles) +{ + Q_UNUSED(current); + Q_UNUSED(roles); +} + +void KItemListWidget::visibleRolesChanged(const QList& current, + const QList& previous) +{ + Q_UNUSED(current); + Q_UNUSED(previous); +} + +void KItemListWidget::columnWidthChanged(const QByteArray& role, + qreal current, + qreal previous) +{ + Q_UNUSED(role); + Q_UNUSED(current); + Q_UNUSED(previous); +} + +void KItemListWidget::styleOptionChanged(const KItemListStyleOption& current, + const KItemListStyleOption& previous) +{ + Q_UNUSED(current); + Q_UNUSED(previous); +} + +void KItemListWidget::currentChanged(bool current) +{ + Q_UNUSED(current); +} + +void KItemListWidget::selectedChanged(bool selected) +{ + Q_UNUSED(selected); +} + +void KItemListWidget::hoveredChanged(bool hovered) +{ + Q_UNUSED(hovered); +} + +void KItemListWidget::alternateBackgroundChanged(bool enabled) +{ + Q_UNUSED(enabled); +} + +void KItemListWidget::siblingsInformationChanged(const QBitArray& current, const QBitArray& previous) +{ + Q_UNUSED(current); + Q_UNUSED(previous); +} + +void KItemListWidget::editedRoleChanged(const QByteArray& current, const QByteArray& previous) +{ + Q_UNUSED(current); + Q_UNUSED(previous); +} + +void KItemListWidget::resizeEvent(QGraphicsSceneResizeEvent* event) +{ + QGraphicsWidget::resizeEvent(event); + clearHoverCache(); + + if (m_selectionToggle) { + const QRectF& toggleRect = selectionToggleRect(); + m_selectionToggle->setPos(toggleRect.topLeft()); + m_selectionToggle->resize(toggleRect.size()); + } +} + +qreal KItemListWidget::hoverOpacity() const +{ + return m_hoverOpacity; +} + +void KItemListWidget::slotHoverAnimationFinished() +{ + if (!m_hovered) { + delete m_selectionToggle; + m_selectionToggle = 0; + } +} + +void KItemListWidget::initializeSelectionToggle() +{ + Q_ASSERT(m_enabledSelectionToggle); + + if (!m_selectionToggle) { + m_selectionToggle = new KItemListSelectionToggle(this); + } + + const QRectF toggleRect = selectionToggleRect(); + m_selectionToggle->setPos(toggleRect.topLeft()); + m_selectionToggle->resize(toggleRect.size()); + + m_selectionToggle->setChecked(isSelected()); +} + +void KItemListWidget::setHoverOpacity(qreal opacity) +{ + m_hoverOpacity = opacity; + if (m_selectionToggle) { + m_selectionToggle->setOpacity(opacity); + } + + if (m_hoverOpacity <= 0.0) { + delete m_hoverCache; + m_hoverCache = 0; + } + + update(); +} + +void KItemListWidget::clearHoverCache() +{ + delete m_hoverCache; + m_hoverCache = 0; +} + +void KItemListWidget::drawItemStyleOption(QPainter* painter, QWidget* widget, QStyle::State styleState) +{ + QStyleOptionViewItemV4 viewItemOption; + viewItemOption.initFrom(widget); + viewItemOption.state = styleState; + viewItemOption.viewItemPosition = QStyleOptionViewItemV4::OnlyOne; + viewItemOption.showDecorationSelected = true; + viewItemOption.rect = selectionRect().toRect(); + widget->style()->drawPrimitive(QStyle::PE_PanelItemViewItem, &viewItemOption, painter, widget); +} + +#include "moc_kitemlistwidget.cpp" diff --git a/dolphin/src/kitemviews/kitemlistwidget.h b/dolphin/src/kitemviews/kitemlistwidget.h new file mode 100644 index 00000000..20621bbe --- /dev/null +++ b/dolphin/src/kitemviews/kitemlistwidget.h @@ -0,0 +1,257 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz * + * * + * Based on the Itemviews NG project from Trolltech Labs: * + * http://qt.gitorious.org/qt-labs/itemviews-ng * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef KITEMLISTWIDGET_H +#define KITEMLISTWIDGET_H + +#include + +#include + +#include +#include +#include + +class KItemListSelectionToggle; +class KItemListView; +#include + +/** + * @brief Provides information for creating an instance of KItemListWidget. + * + * KItemListView only creates KItemListWidget instances for the visible + * area. For calculating the required size of all items the expected + * size for the invisible items must be accessible. KItemListWidgetInformant + * provides this information. + */ +class DOLPHINPRIVATE_EXPORT KItemListWidgetInformant +{ +public: + KItemListWidgetInformant(); + virtual ~KItemListWidgetInformant(); + + virtual void calculateItemSizeHints(QVector& logicalHeightHints, qreal& logicalWidthHint, const KItemListView* view) const = 0; + + virtual qreal preferredRoleColumnWidth(const QByteArray& role, + int index, + const KItemListView* view) const = 0; +}; + +/** + * @brief Widget that shows a visible item from the model. + * + * For showing an item from a custom model it is required to at least overwrite KItemListWidget::paint(). + * All properties are set by KItemListView, for each property there is a corresponding + * virtual protected method that allows to react on property changes. + */ +class DOLPHINPRIVATE_EXPORT KItemListWidget : public QGraphicsWidget +{ + Q_OBJECT + +public: + KItemListWidget(KItemListWidgetInformant* informant, QGraphicsItem* parent); + virtual ~KItemListWidget(); + + void setIndex(int index); + int index() const; + + void setData(const QHash& data, const QSet& roles = QSet()); + QHash data() const; + + /** + * Draws the hover-rectangle if the item is hovered. Overwrite this method + * to show the data of the custom model provided by KItemListWidget::data(). + * @reimp + */ + virtual void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget = 0); + + void setVisibleRoles(const QList& roles); + QList visibleRoles() const; + + /** + * Sets the width of a role that should be used if the alignment of the content + * should be done in columns. + */ + void setColumnWidth(const QByteArray& role, qreal width); + qreal columnWidth(const QByteArray& role) const; + + void setStyleOption(const KItemListStyleOption& option); + const KItemListStyleOption& styleOption() const; + + // TODO: Hides QGraphicsItem::setSelected()/isSelected(). Replace + // this by using the default mechanism. + void setSelected(bool selected); + bool isSelected() const; + + void setCurrent(bool current); + bool isCurrent() const; + + void setHovered(bool hovered); + bool isHovered() const; + + void setHoverPosition(const QPointF& pos); + + void setAlternateBackground(bool enable); + bool alternateBackground() const; + + void setEnabledSelectionToggle(bool enabled); + bool enabledSelectionToggle() const; + + /** + * Sets the sibling information for the item and all of its parents. + * The sibling information of the upper most parent is represented by + * the first bit, the sibling information of the item by the last bit. + * The sibling information is useful for drawing the branches in + * tree views. + */ + void setSiblingsInformation(const QBitArray& siblings); + QBitArray siblingsInformation() const; + + /** + * Allows the user to edit the role \a role. The signals + * roleEditingCanceled() or roleEditingFinished() will be + * emitted after editing. An ongoing editing gets canceled if + * the role is empty. Derived classes must implement + * editedRoleChanged(). + */ + void setEditedRole(const QByteArray& role); + QByteArray editedRole() const; + + /** + * @return True if \a point is inside KItemListWidget::hoverRect(), + * KItemListWidget::textRect(), KItemListWidget::selectionToggleRect() + * or KItemListWidget::expansionToggleRect(). + * @reimp + */ + virtual bool contains(const QPointF& point) const; + + /** + * @return Rectangle for the area that shows the icon. + */ + virtual QRectF iconRect() const = 0; + + /** + * @return Rectangle for the area that contains the text-properties. + */ + virtual QRectF textRect() const = 0; + + /** + * @return Focus rectangle for indicating the current item. Per default + * textRect() will be returned. Overwrite this method if textRect() + * provides a larger rectangle than the actual text (e.g. to + * be aligned with the iconRect()). The textFocusRect() may not be + * outside the boundaries of textRect(). + */ + virtual QRectF textFocusRect() const; + + /** + * @return Rectangle around which a selection box should be drawn if the item is selected. + */ + virtual QRectF selectionRect() const = 0; + + /** + * @return Rectangle for the selection-toggle that is used to select or deselect an item. + * Per default an empty rectangle is returned which means that no selection-toggle + * is available. + */ + virtual QRectF selectionToggleRect() const; + + /** + * @return Rectangle for the expansion-toggle that is used to open a sub-tree of the model. + * Per default an empty rectangle is returned which means that no opening of sub-trees + * is supported. + */ + virtual QRectF expansionToggleRect() const; + + /** + * @return Pixmap that is used when dragging an item. Per default the current state of the + * widget is returned as pixmap. + */ + virtual QPixmap createDragPixmap(const QStyleOptionGraphicsItem* option, QWidget* widget = 0); + +signals: + void roleEditingCanceled(int index, const QByteArray& role, const QVariant& value); + void roleEditingFinished(int index, const QByteArray& role, const QVariant& value); + +protected: + virtual void dataChanged(const QHash& current, const QSet& roles = QSet()); + virtual void visibleRolesChanged(const QList& current, const QList& previous); + virtual void columnWidthChanged(const QByteArray& role, qreal current, qreal previous); + virtual void styleOptionChanged(const KItemListStyleOption& current, const KItemListStyleOption& previous); + virtual void currentChanged(bool current); + virtual void selectedChanged(bool selected); + virtual void hoveredChanged(bool hovered); + virtual void alternateBackgroundChanged(bool enabled); + virtual void siblingsInformationChanged(const QBitArray& current, const QBitArray& previous); + virtual void editedRoleChanged(const QByteArray& current, const QByteArray& previous); + virtual void resizeEvent(QGraphicsSceneResizeEvent* event); + + /** + * @return The current opacity of the hover-animation. When implementing a custom painting-code for a hover-state + * this opacity value should be respected. + */ + qreal hoverOpacity() const; + + const KItemListWidgetInformant* informant() const; + +private slots: + void slotHoverAnimationFinished(); + +private: + void initializeSelectionToggle(); + void setHoverOpacity(qreal opacity); + void clearHoverCache(); + void drawItemStyleOption(QPainter* painter, QWidget* widget, QStyle::State styleState); + +private: + Q_PROPERTY(qreal hoverOpacity READ hoverOpacity WRITE setHoverOpacity) + + KItemListWidgetInformant* m_informant; + int m_index; + bool m_selected; + bool m_current; + bool m_hovered; + bool m_alternateBackground; + bool m_enabledSelectionToggle; + QHash m_data; + QList m_visibleRoles; + QHash m_columnWidths; + KItemListStyleOption m_styleOption; + QBitArray m_siblingsInfo; + + qreal m_hoverOpacity; + mutable QPixmap* m_hoverCache; + QPropertyAnimation* m_hoverAnimation; + + KItemListSelectionToggle* m_selectionToggle; + + QByteArray m_editedRole; +}; + +inline const KItemListWidgetInformant* KItemListWidget::informant() const +{ + return m_informant; +} + +#endif + + diff --git a/dolphin/src/kitemviews/kitemmodelbase.cpp b/dolphin/src/kitemviews/kitemmodelbase.cpp new file mode 100644 index 00000000..db80824e --- /dev/null +++ b/dolphin/src/kitemviews/kitemmodelbase.cpp @@ -0,0 +1,162 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz * + * * + * Based on the Itemviews NG project from Trolltech Labs: * + * http://qt.gitorious.org/qt-labs/itemviews-ng * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "kitemmodelbase.h" + +KItemModelBase::KItemModelBase(QObject* parent) : + QObject(parent), + m_groupedSorting(false), + m_sortRole(), + m_sortOrder(Qt::AscendingOrder) +{ +} + +KItemModelBase::KItemModelBase(const QByteArray& sortRole, QObject* parent) : + QObject(parent), + m_groupedSorting(false), + m_sortRole(sortRole), + m_sortOrder(Qt::AscendingOrder) +{ +} + +KItemModelBase::~KItemModelBase() +{ +} + +bool KItemModelBase::setData(int index, const QHash &values) +{ + Q_UNUSED(index); + Q_UNUSED(values); + return false; +} + +void KItemModelBase::setGroupedSorting(bool grouped) +{ + if (m_groupedSorting != grouped) { + m_groupedSorting = grouped; + onGroupedSortingChanged(grouped); + emit groupedSortingChanged(grouped); + } +} + +bool KItemModelBase::groupedSorting() const +{ + return m_groupedSorting; +} + +void KItemModelBase::setSortRole(const QByteArray& role) +{ + if (role != m_sortRole) { + const QByteArray previous = m_sortRole; + m_sortRole = role; + onSortRoleChanged(role, previous); + emit sortRoleChanged(role, previous); + } +} + +QByteArray KItemModelBase::sortRole() const +{ + return m_sortRole; +} + +void KItemModelBase::setSortOrder(Qt::SortOrder order) +{ + if (order != m_sortOrder) { + const Qt::SortOrder previous = m_sortOrder; + m_sortOrder = order; + onSortOrderChanged(order, previous); + emit sortOrderChanged(order, previous); + } +} + +QString KItemModelBase::roleDescription(const QByteArray& role) const +{ + return role; +} + +QList > KItemModelBase::groups() const +{ + return QList >(); +} + +bool KItemModelBase::setExpanded(int index, bool expanded) +{ + Q_UNUSED(index); + Q_UNUSED(expanded); + return false; +} + +bool KItemModelBase::isExpanded(int index) const +{ + Q_UNUSED(index); + return false; +} + +bool KItemModelBase::isExpandable(int index) const +{ + Q_UNUSED(index); + return false; +} + +int KItemModelBase::expandedParentsCount(int index) const +{ + Q_UNUSED(index); + return 0; +} + +QMimeData* KItemModelBase::createMimeData(const KItemSet& indexes) const +{ + Q_UNUSED(indexes); + return 0; +} + +int KItemModelBase::indexForKeyboardSearch(const QString& text, int startFromIndex) const +{ + Q_UNUSED(text); + Q_UNUSED(startFromIndex); + return -1; +} + +bool KItemModelBase::supportsDropping(int index) const +{ + Q_UNUSED(index); + return false; +} + +void KItemModelBase::onGroupedSortingChanged(bool current) +{ + Q_UNUSED(current); +} + +void KItemModelBase::onSortRoleChanged(const QByteArray& current, const QByteArray& previous) +{ + Q_UNUSED(current); + Q_UNUSED(previous); +} + +void KItemModelBase::onSortOrderChanged(Qt::SortOrder current, Qt::SortOrder previous) +{ + Q_UNUSED(current); + Q_UNUSED(previous); +} + +#include "moc_kitemmodelbase.cpp" diff --git a/dolphin/src/kitemviews/kitemmodelbase.h b/dolphin/src/kitemviews/kitemmodelbase.h new file mode 100644 index 00000000..5c9ee1c2 --- /dev/null +++ b/dolphin/src/kitemviews/kitemmodelbase.h @@ -0,0 +1,271 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz * + * * + * Based on the Itemviews NG project from Trolltech Labs: * + * http://qt.gitorious.org/qt-labs/itemviews-ng * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef KITEMMODELBASE_H +#define KITEMMODELBASE_H + +#include + +#include +#include + +#include +#include +#include + +#include + +/** + * @brief Base class for model implementations used by KItemListView and KItemListController. + * + * An item-model consists of a variable number of items. The number of items + * is given by KItemModelBase::count(). The data of an item is accessed by a unique index + * with KItemModelBase::data(). The indexes are integer-values counting from 0 to the + * KItemModelBase::count() - 1. + * + * One item consists of a variable number of role/value-pairs. + * + * A model can optionally provide sorting- and grouping-capabilities. + * + * Also optionally it is possible to provide a tree of items by implementing the methods + * setExpanded(), isExpanded(), isExpandable() and expandedParentsCount(). + */ +class DOLPHINPRIVATE_EXPORT KItemModelBase : public QObject +{ + Q_OBJECT + +public: + KItemModelBase(QObject* parent = 0); + explicit KItemModelBase(const QByteArray& sortRole, QObject* parent = 0); + virtual ~KItemModelBase(); + + /** @return The number of items. */ + virtual int count() const = 0; + + virtual QHash data(int index) const = 0; + + /** + * Sets the data for the item at \a index to the given \a values. Returns true + * if the data was set on the item; returns false otherwise. + * + * The default implementation does not set the data, and will always return + * false. + */ + virtual bool setData(int index, const QHash& values); + + /** + * Enables/disables the grouped sorting. The method KItemModelBase::onGroupedSortingChanged() will be + * called so that model-implementations can react on the grouped-sorting change. Afterwards the + * signal groupedSortingChanged() will be emitted. If the grouped sorting is enabled, the method + * KItemModelBase::groups() must be implemented. + */ + void setGroupedSorting(bool grouped); + bool groupedSorting() const; + + /** + * Sets the sort-role to \a role. The method KItemModelBase::onSortRoleChanged() will be + * called so that model-implementations can react on the sort-role change. Afterwards the + * signal sortRoleChanged() will be emitted. + */ + void setSortRole(const QByteArray& role); + QByteArray sortRole() const; + + /** + * Sets the sort order to \a order. The method KItemModelBase::onSortOrderChanged() will be + * called so that model-implementations can react on the sort order change. Afterwards the + * signal sortOrderChanged() will be emitted. + */ + void setSortOrder(Qt::SortOrder order); + Qt::SortOrder sortOrder() const; + + /** + * @return Translated description for the \p role. The description is e.g. used + * for the header in KItemListView. + */ + virtual QString roleDescription(const QByteArray& role) const; + + /** + * @return List of group headers. Each list-item consists of the index of the item + * that represents the first item of a group and a value represented + * as QVariant. The value is shown by an instance of KItemListGroupHeader. + * Per default an empty list is returned. + */ + virtual QList > groups() const; + + /** + * Expands the item with the index \a index if \a expanded is true. + * If \a expanded is false the item will be collapsed. + * + * Per default no expanding of items is implemented. When implementing + * this method it is mandatory to overwrite KItemModelBase::isExpandable() + * and KItemListView::supportsExpandableItems() to return true. + * + * @return True if the operation has been successful. + */ + virtual bool setExpanded(int index, bool expanded); + + /** + * @return True if the item with the index \a index is expanded. + * Per default no expanding of items is implemented. When implementing + * this method it is mandatory to overwrite KItemModelBase::isExpandable() + * and KItemListView::supportsExpandableItems() to return true. + */ + virtual bool isExpanded(int index) const; + + /** + * @return True if expanding and collapsing of the item with the index \a index + * is supported. Per default false is returned. + */ + virtual bool isExpandable(int index) const; + + /** + * @return Number of expanded parent items for the item with the given index. + * Per default 0 is returned. + */ + virtual int expandedParentsCount(int index) const; + + /** + * @return MIME-data for the items given by \a indexes. The default implementation + * returns 0. The ownership of the returned instance is in the hand of the + * caller of this method. The method must be implemented if dragging of + * items should be possible. + */ + virtual QMimeData* createMimeData(const KItemSet& indexes) const; + + /** + * @return Reimplement this to return the index for the first item + * beginning with string typed in through the keyboard, -1 if not found. + * @param text the text which has been typed in through the keyboard + * @param startFromIndex the index from which to start searching from + */ + virtual int indexForKeyboardSearch(const QString& text, int startFromIndex = 0) const; + + /** + * @return True, if the item with the index \a index basically supports dropping. + * Per default false is returned. + * + * The information is used only to give a visual feedback during a drag operation + * and not to decide whether a drop event gets emitted. It is it is still up to + * the receiver of KItemListController::itemDropEvent() to decide how to handle + * the drop event. + */ + // TODO: Should the MIME-data be passed too so that the model can do a more specific + // decision whether it accepts the drop? + virtual bool supportsDropping(int index) const; + +signals: + /** + * Is emitted if one or more items have been inserted. Each item-range consists + * of: + * - an index where items have been inserted + * - the number of inserted items. + * The index of each item-range represents the index of the model + * before the items have been inserted. + * + * For the item-ranges it is assured that: + * - They don't overlap + * - The index of item-range n is smaller than the index of item-range n + 1. + */ + void itemsInserted(const KItemRangeList& itemRanges); + + /** + * Is emitted if one or more items have been removed. Each item-range consists + * of: + * - an index where items have been inserted + * - the number of inserted items. + * The index of each item-range represents the index of the model + * before the items have been removed. + * + * For the item-ranges it is assured that: + * - They don't overlap + * - The index of item-range n is smaller than the index of item-range n + 1. + */ + void itemsRemoved(const KItemRangeList& itemRanges); + + /** + * Is emitted if one ore more items get moved. + * @param itemRange Item-range that gets moved to a new position. + * @param movedToIndexes New positions for each element of the item-range. + * + * For example if the model has 10 items and the items 0 and 1 get exchanged + * with the items 5 and 6 then the parameters look like this: + * - itemRange: has the index 0 and a count of 7. + * - movedToIndexes: Contains the seven values 5, 6, 2, 3, 4, 0, 1 + * + * This signal implies that the groups might have changed. Therefore, + * gropusChanged() is not emitted if this signal is emitted. + */ + void itemsMoved(const KItemRange& itemRange, const QList& movedToIndexes); + + void itemsChanged(const KItemRangeList& itemRanges, const QSet& roles); + + /** + * Is emitted if the groups have changed, even though the order of the + * items has not been modified. + */ + void groupsChanged(); + + void groupedSortingChanged(bool current); + void sortRoleChanged(const QByteArray& current, const QByteArray& previous); + void sortOrderChanged(Qt::SortOrder current, Qt::SortOrder previous); + +protected: + /** + * Is invoked if the grouped sorting has been changed by KItemModelBase::setGroupedSorting(). Allows + * to react on the changed grouped sorting before the signal groupedSortingChanged() will be emitted. + */ + virtual void onGroupedSortingChanged(bool current); + + /** + * Is invoked if the sort role has been changed by KItemModelBase::setSortRole(). Allows + * to react on the changed sort role before the signal sortRoleChanged() will be emitted. + * The implementation must assure that the items are sorted by the role given by \a current. + * Usually the most efficient way is to emit a + * itemsRemoved() signal for all items, reorder the items internally and to emit a + * itemsInserted() signal afterwards. + */ + virtual void onSortRoleChanged(const QByteArray& current, const QByteArray& previous); + + /** + * Is invoked if the sort order has been changed by KItemModelBase::setSortOrder(). Allows + * to react on the changed sort order before the signal sortOrderChanged() will be emitted. + * The implementation must assure that the items are sorted by the order given by \a current. + * Usually the most efficient way is to emit a + * itemsRemoved() signal for all items, reorder the items internally and to emit a + * itemsInserted() signal afterwards. + */ + virtual void onSortOrderChanged(Qt::SortOrder current, Qt::SortOrder previous); + +private: + bool m_groupedSorting; + QByteArray m_sortRole; + Qt::SortOrder m_sortOrder; +}; + +inline Qt::SortOrder KItemModelBase::sortOrder() const +{ + return m_sortOrder; +} + +#endif + + diff --git a/dolphin/src/kitemviews/kitemrange.h b/dolphin/src/kitemviews/kitemrange.h new file mode 100644 index 00000000..ecc03988 --- /dev/null +++ b/dolphin/src/kitemviews/kitemrange.h @@ -0,0 +1,106 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz * + * Copyright (C) 2013 by Frank Reininghaus * + * * + * Based on the Itemviews NG project from Trolltech Labs: * + * http://qt.gitorious.org/qt-labs/itemviews-ng * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef KITEMRANGE_H +#define KITEMRANGE_H + +#include + +struct KItemRange +{ + KItemRange(int index = 0, int count = 0); + int index; + int count; + + bool operator == (const KItemRange& other) const; +}; + +inline KItemRange::KItemRange(int index, int count) : + index(index), + count(count) +{ +} + +inline bool KItemRange::operator == (const KItemRange& other) const +{ + return index == other.index && count == other.count; +} + + +class KItemRangeList : public QList +{ +public: + KItemRangeList() : QList() {} + KItemRangeList(const QList& list) : QList(list) {} + + template + static KItemRangeList fromSortedContainer(const Container& container); + + KItemRangeList& operator<<(const KItemRange& range) + { + append(range); + return *this; + } +}; + +template +KItemRangeList KItemRangeList::fromSortedContainer(const Container& container) +{ + typename Container::const_iterator it = container.constBegin(); + const typename Container::const_iterator end = container.constEnd(); + + if (it == end) { + return KItemRangeList(); + } + + KItemRangeList result; + + int index = *it; + int count = 1; + + // Remove duplicates, see https://bugs.kde.org/show_bug.cgi?id=335672 + while (it != end && *it == index) { + ++it; + } + + while (it != end) { + if (*it == index + count) { + ++count; + } else { + result << KItemRange(index, count); + index = *it; + count = 1; + } + ++it; + + // Remove duplicates, see https://bugs.kde.org/show_bug.cgi?id=335672 + while (it != end && *it == *(it - 1)) { + ++it; + } + } + + result << KItemRange(index, count); + return result; +} + +#endif diff --git a/dolphin/src/kitemviews/kitemset.cpp b/dolphin/src/kitemviews/kitemset.cpp new file mode 100644 index 00000000..f855368c --- /dev/null +++ b/dolphin/src/kitemviews/kitemset.cpp @@ -0,0 +1,348 @@ +/*************************************************************************** + * Copyright (C) 2013 by Frank Reininghaus * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "kitemset.h" + +#include + +#include + +KItemSet::iterator KItemSet::insert(int i) +{ + if (m_itemRanges.empty()) { + m_itemRanges.push_back(KItemRange(i, 1)); + return iterator(m_itemRanges.begin(), 0); + } + + KItemRangeList::iterator rangeBegin = m_itemRanges.begin(); + if (i < rangeBegin->index) { + // The inserted index is smaller than all existing items. + if (i == rangeBegin->index - 1) { + // Move the beginning of the first range one item to the front. + --rangeBegin->index; + ++rangeBegin->count; + } else { + // Insert a new range at the beginning. + rangeBegin = m_itemRanges.insert(rangeBegin, KItemRange(i, 1)); + } + + return iterator(rangeBegin, 0); + } + + KItemRangeList::iterator rangeEnd = m_itemRanges.end(); + KItemRangeList::iterator lastRange = rangeEnd - 1; + if (i >= lastRange->index) { + // i either belongs to the last range, or it is larger than all existing items. + const int lastItemPlus1 = lastRange->index + lastRange->count; + if (i == lastItemPlus1) { + // Move the end of the last range one item to the back. + ++lastRange->count; + } else if (i > lastItemPlus1) { + // Append a new range. + lastRange = m_itemRanges.insert(rangeEnd, KItemRange(i, 1)); + } + + return iterator(lastRange, i - lastRange->index); + } + + // We know that i is between the smallest existing item and the first item + // of the last range. Find the lowest range whose 'index' is smaller than i. + KItemRangeList::iterator low = rangeBegin; + KItemRangeList::iterator high = lastRange; + + while (low + 1 != high) { + const int span = high - low; + Q_ASSERT(span >= 2); + + KItemRangeList::iterator mid = low + span / 2; + if (mid->index > i) { + high = mid; + } else { + low = mid; + } + } + + Q_ASSERT(low->index <= i && high->index > i); + + if (i == low->index + low->count) { + // i is just one item behind the range low. + if (i == high->index - 1) { + // i closes the gap between low and high. Merge the two ranges. + const int newRangeCount = low->count + 1 + high->count; + KItemRangeList::iterator behindNewRange = m_itemRanges.erase(high); + KItemRangeList::iterator newRange = behindNewRange - 1; + newRange->count = newRangeCount; + return iterator(newRange, i - newRange->index); + } else { + // Extend low by one item. + ++low->count; + return iterator(low, low->count - 1); + } + } else if (i > low->index + low->count) { + if (i == high->index - 1) { + // Extend high by one item to the front. + --high->index; + ++high->count; + return iterator(high, 0); + } else { + // Insert a new range between low and high. + KItemRangeList::iterator newRange = m_itemRanges.insert(high, KItemRange(i, 1)); + return iterator(newRange, 0); + } + } else { + // The range low already contains i. + return iterator(low, i - low->index); + } +} + +KItemSet::iterator KItemSet::erase(iterator it) +{ + KItemRangeList::iterator rangeIt = it.m_rangeIt; + + if (it.m_offset == 0) { + // Removed index is at the beginning of a range. + if (rangeIt->count > 1) { + ++rangeIt->index; + --rangeIt->count; + } else { + // The range only contains the removed index. + rangeIt = m_itemRanges.erase(rangeIt); + } + return iterator(rangeIt, 0); + } else if (it.m_offset == rangeIt->count - 1) { + // Removed index is at the end of a range. + --rangeIt->count; + ++rangeIt; + return iterator(rangeIt, 0); + } else { + // The removed index is in the middle of a range. + const int newRangeIndex = *it + 1; + const int newRangeCount = rangeIt->count - it.m_offset - 1; + const KItemRange newRange(newRangeIndex, newRangeCount); + + rangeIt->count = it.m_offset; + ++rangeIt; + rangeIt = m_itemRanges.insert(rangeIt, newRange); + + return iterator(rangeIt, 0); + } +} + +KItemSet KItemSet::operator+(const KItemSet& other) const +{ + KItemSet sum; + + KItemRangeList::const_iterator it1 = m_itemRanges.constBegin(); + KItemRangeList::const_iterator it2 = other.m_itemRanges.constBegin(); + + const KItemRangeList::const_iterator end1 = m_itemRanges.constEnd(); + const KItemRangeList::const_iterator end2 = other.m_itemRanges.constEnd(); + + while (it1 != end1 || it2 != end2) { + if (it1 == end1) { + // We are past the end of 'this' already. Append all remaining + // item ranges from 'other'. + while (it2 != end2) { + sum.m_itemRanges.append(*it2); + ++it2; + } + } else if (it2 == end2) { + // We are past the end of 'other' already. Append all remaining + // item ranges from 'this'. + while (it1 != end1) { + sum.m_itemRanges.append(*it1); + ++it1; + } + } else { + // Find the beginning of the next range. + int index = qMin(it1->index, it2->index); + int count = 0; + + do { + if (it1 != end1 && it1->index <= index + count) { + // The next range from 'this' overlaps with the current range in the sum. + count = qMax(count, it1->index + it1->count - index); + ++it1; + } + + if (it2 != end2 && it2->index <= index + count) { + // The next range from 'other' overlaps with the current range in the sum. + count = qMax(count, it2->index + it2->count - index); + ++it2; + } + } while ((it1 != end1 && it1->index <= index + count) + || (it2 != end2 && it2->index <= index + count)); + + sum.m_itemRanges.append(KItemRange(index, count)); + } + } + + return sum; +} + +KItemSet KItemSet::operator^(const KItemSet& other) const +{ + // We are looking for all ints which are either in *this or in other, + // but not in both. + KItemSet result; + + // When we go through all integers from INT_MIN to INT_MAX and start + // in the state "do not add to result", every beginning/end of a range + // of *this and other toggles the "add/do not add to result" state. + // Therefore, we just have to put ints where any range starts/ends to + // a sorted array, and then we can calculate the result quite easily. + QVector rangeBoundaries; + rangeBoundaries.resize(2 * (m_itemRanges.count() + other.m_itemRanges.count())); + const QVector::iterator begin = rangeBoundaries.begin(); + const QVector::iterator end = rangeBoundaries.end(); + QVector::iterator it = begin; + + foreach (const KItemRange& range, m_itemRanges) { + *it++ = range.index; + *it++ = range.index + range.count; + } + + const QVector::iterator middle = it; + + foreach (const KItemRange& range, other.m_itemRanges) { + *it++ = range.index; + *it++ = range.index + range.count; + } + Q_ASSERT(it == end); + + std::inplace_merge(begin, middle, end); + + it = begin; + while (it != end) { + const int rangeBegin = *it; + ++it; + + if (*it == rangeBegin) { + // It seems that ranges from both *this and other start at + // rangeBegin. Do not start a new range, but read the next int. + // + // Example: Consider the symmetric difference of the sets + // {1, 2, 3, 4} and {1, 2}. The sorted list of range boundaries is + // 1 1 3 5. Discarding the duplicate 1 yields the result + // rangeBegin = 3, rangeEnd = 5, which corresponds to the set {3, 4}. + ++it; + } else { + // The end of the current range is the next *single* int that we + // find. If an int appears twice in rangeBoundaries, the range does + // not end. + // + // Example: Consider the symmetric difference of the sets + // {1, 2, 3, 4, 8, 9, 10} and {5, 6, 7}. The sorted list of range + // boundaries is 1 5 5 8 8 11, and discarding all duplicates yields + // the result rangeBegin = 1, rangeEnd = 11, which corresponds to + // the set {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}. + bool foundEndOfRange = false; + int rangeEnd; + do { + rangeEnd = *it; + ++it; + + if (it == end || *it != rangeEnd) { + foundEndOfRange = true; + } else { + ++it; + } + } while (!foundEndOfRange); + + result.m_itemRanges.append(KItemRange(rangeBegin, rangeEnd - rangeBegin)); + } + } + + return result; +} + +bool KItemSet::isValid() const +{ + const KItemRangeList::const_iterator begin = m_itemRanges.constBegin(); + const KItemRangeList::const_iterator end = m_itemRanges.constEnd(); + + for (KItemRangeList::const_iterator it = begin; it != end; ++it) { + if (it->count <= 0) { + return false; + } + + if (it != begin) { + const KItemRangeList::const_iterator previous = it - 1; + if (previous->index + previous->count >= it->index) { + return false; + } + } + } + + return true; +} + +KItemRangeList::iterator KItemSet::rangeForItem(int i) +{ + const KItemRangeList::iterator end = m_itemRanges.end(); + KItemRangeList::iterator low = m_itemRanges.begin(); + KItemRangeList::iterator high = end; + + if (low == end || low->index > i) { + return end; + } + + while (low != high && low + 1 != high) { + KItemRangeList::iterator mid = low + (high - low) / 2; + if (mid->index > i) { + high = mid; + } else { + low = mid; + } + } + + Q_ASSERT(low->index <= i); + if (low->index + low->count > i) { + return low; + } + + return end; +} + +KItemRangeList::const_iterator KItemSet::constRangeForItem(int i) const +{ + const KItemRangeList::const_iterator end = m_itemRanges.constEnd(); + KItemRangeList::const_iterator low = m_itemRanges.constBegin(); + KItemRangeList::const_iterator high = end; + + if (low == end || low->index > i) { + return end; + } + + while (low != high && low + 1 != high) { + KItemRangeList::const_iterator mid = low + (high - low) / 2; + if (mid->index > i) { + high = mid; + } else { + low = mid; + } + } + + Q_ASSERT(low->index <= i); + if (low->index + low->count > i) { + return low; + } + + return end; +} diff --git a/dolphin/src/kitemviews/kitemset.h b/dolphin/src/kitemviews/kitemset.h new file mode 100644 index 00000000..385010f7 --- /dev/null +++ b/dolphin/src/kitemviews/kitemset.h @@ -0,0 +1,413 @@ +/*************************************************************************** + * Copyright (C) 2013 by Frank Reininghaus * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef KITEMSET_H +#define KITEMSET_H + +#include + +/** + * @brief Stores a set of integer numbers in a space-efficient way. + * + * This class is similar to QSet, but it has the following advantages: + * + * 1. It uses less memory than a QSet if many consecutive numbers are + * stored. This is achieved by not storing each number separately, but + * "ranges" of numbers. + * + * Example: The set {1, 2, 3, 4, 5} is represented by a single range which + * starts at 1 and has the length 5. + * + * 2. When iterating through a KItemSet using KItemSet::iterator or + * KItemSet::const_iterator, the numbers are traversed in ascending order. + * + * The complexity of most operations depends on the number of ranges. + */ + +class KItemSet +{ +public: + KItemSet(); + KItemSet(const KItemSet& other); + + /** + * Returns the number of items in the set. + * Complexity: O(log(number of ranges)). + */ + int count() const; + + bool isEmpty() const; + void clear(); + + bool operator==(const KItemSet& other) const; + bool operator!=(const KItemSet& other) const; + + class iterator + { + iterator(const KItemRangeList::iterator& rangeIt, int offset) : + m_rangeIt(rangeIt), + m_offset(offset) + { + } + + public: + iterator(const iterator& other) : + m_rangeIt(other.m_rangeIt), + m_offset(other.m_offset) + { + } + + iterator& operator=(const iterator& other) + { + m_rangeIt = other.m_rangeIt; + m_offset = other.m_offset; + return *this; + } + + int operator*() const + { + return m_rangeIt->index + m_offset; + } + + inline bool operator==(const iterator& other) const + { + return m_rangeIt == other.m_rangeIt && m_offset == other.m_offset; + } + + inline bool operator!=(const iterator& other) const + { + return !(*this == other); + } + + inline iterator& operator++() + { + ++m_offset; + + if (m_offset == m_rangeIt->count) { + ++m_rangeIt; + m_offset = 0; + } + + return *this; + } + + inline iterator operator++(int) + { + iterator r = *this; + ++(*this); + return r; + } + + inline iterator& operator--() + { + if (m_offset == 0) { + --m_rangeIt; + m_offset = m_rangeIt->count - 1; + } else { + --m_offset; + } + + return *this; + } + + inline iterator operator--(int) + { + iterator r = *this; + --(*this); + return r; + } + + private: + KItemRangeList::iterator m_rangeIt; + int m_offset; + + friend class const_iterator; + friend class KItemSet; + }; + + + class const_iterator + { + const_iterator(KItemRangeList::const_iterator rangeIt, int offset) : + m_rangeIt(rangeIt), + m_offset(offset) + { + } + + public: + const_iterator(const const_iterator& other) : + m_rangeIt(other.m_rangeIt), + m_offset(other.m_offset) + { + } + + const_iterator(const iterator& other) : + m_rangeIt(other.m_rangeIt), + m_offset(other.m_offset) + { + } + + const_iterator& operator=(const const_iterator& other) + { + m_rangeIt = other.m_rangeIt; + m_offset = other.m_offset; + return *this; + } + + int operator*() const + { + return m_rangeIt->index + m_offset; + } + + inline bool operator==(const const_iterator& other) const + { + return m_rangeIt == other.m_rangeIt && m_offset == other.m_offset; + } + + inline bool operator!=(const const_iterator& other) const + { + return !(*this == other); + } + + inline const_iterator& operator++() + { + ++m_offset; + + if (m_offset == m_rangeIt->count) { + ++m_rangeIt; + m_offset = 0; + } + + return *this; + } + + inline const_iterator operator++(int) + { + const_iterator r = *this; + ++(*this); + return r; + } + + inline const_iterator& operator--() + { + if (m_offset == 0) { + --m_rangeIt; + m_offset = m_rangeIt->count - 1; + } else { + --m_offset; + } + + return *this; + } + + inline const_iterator operator--(int) + { + const_iterator r = *this; + --(*this); + return r; + } + + private: + KItemRangeList::const_iterator m_rangeIt; + int m_offset; + + friend class KItemSet; + }; + + iterator begin(); + const_iterator begin() const; + const_iterator constBegin() const; + iterator end(); + const_iterator end() const; + const_iterator constEnd() const; + + int first() const; + int last() const; + + bool contains(int i) const; + iterator insert(int i); + iterator find(int i); + const_iterator constFind(int i) const; + bool remove(int i); + iterator erase(iterator it); + + /** + * Returns a new set which contains all items that are contained in this + * KItemSet, in \a other, or in both. + */ + KItemSet operator+(const KItemSet& other) const; + + /** + * Returns a new set which contains all items that are contained either in + * this KItemSet, or in \a other, but not in both (the symmetric difference + * of both KItemSets). + */ + KItemSet operator^(const KItemSet& other) const; + + KItemSet& operator<<(int i); + +private: + /** + * Returns true if the KItemSet is valid, and false otherwise. + * A valid KItemSet must store the item ranges in ascending order, and + * the ranges must not overlap. + */ + bool isValid() const; + + /** + * This function returns an iterator that points to the KItemRange which + * contains i, or m_itemRanges.end() if no such range exists. + */ + KItemRangeList::iterator rangeForItem(int i); + + /** + * This function returns an iterator that points to the KItemRange which + * contains i, or m_itemRanges.constEnd() if no such range exists. + */ + KItemRangeList::const_iterator constRangeForItem(int i) const; + + KItemRangeList m_itemRanges; + + friend class KItemSetTest; +}; + +inline KItemSet::KItemSet() : + m_itemRanges() +{ +} + +inline KItemSet::KItemSet(const KItemSet& other) : + m_itemRanges(other.m_itemRanges) +{ +} + +inline int KItemSet::count() const +{ + int result = 0; + foreach (const KItemRange& range, m_itemRanges) { + result += range.count; + } + return result; +} + +inline bool KItemSet::isEmpty() const +{ + return m_itemRanges.isEmpty(); +} + +inline void KItemSet::clear() +{ + m_itemRanges.clear(); +} + +inline bool KItemSet::operator==(const KItemSet& other) const +{ + return m_itemRanges == other.m_itemRanges; +} + +inline bool KItemSet::operator!=(const KItemSet& other) const +{ + return m_itemRanges != other.m_itemRanges; +} + +inline bool KItemSet::contains(int i) const +{ + const KItemRangeList::const_iterator it = constRangeForItem(i); + return it != m_itemRanges.end(); +} + +inline KItemSet::iterator KItemSet::find(int i) +{ + const KItemRangeList::iterator it = rangeForItem(i); + if (it != m_itemRanges.end()) { + return iterator(it, i - it->index); + } else { + return end(); + } +} + +inline KItemSet::const_iterator KItemSet::constFind(int i) const +{ + const KItemRangeList::const_iterator it = constRangeForItem(i); + if (it != m_itemRanges.constEnd()) { + return const_iterator(it, i - it->index); + } else { + return constEnd(); + } +} + +inline bool KItemSet::remove(int i) +{ + iterator it = find(i); + if (it != end()) { + erase(it); + return true; + } else { + return false; + } +} + +inline KItemSet::iterator KItemSet::begin() +{ + return iterator(m_itemRanges.begin(), 0); +} + +inline KItemSet::const_iterator KItemSet::begin() const +{ + return const_iterator(m_itemRanges.begin(), 0); +} + +inline KItemSet::const_iterator KItemSet::constBegin() const +{ + return const_iterator(m_itemRanges.constBegin(), 0); +} + +inline KItemSet::iterator KItemSet::end() +{ + return iterator(m_itemRanges.end(), 0); +} + +inline KItemSet::const_iterator KItemSet::end() const +{ + return const_iterator(m_itemRanges.end(), 0); +} + +inline KItemSet::const_iterator KItemSet::constEnd() const +{ + return const_iterator(m_itemRanges.constEnd(), 0); +} + +inline int KItemSet::first() const +{ + return m_itemRanges.first().index; +} + +inline int KItemSet::last() const +{ + const KItemRange& lastRange = m_itemRanges.last(); + return lastRange.index + lastRange.count - 1; +} + +inline KItemSet& KItemSet::operator<<(int i) +{ + insert(i); + return *this; +} + +#endif diff --git a/dolphin/src/kitemviews/kstandarditem.cpp b/dolphin/src/kitemviews/kstandarditem.cpp new file mode 100644 index 00000000..6cb5b049 --- /dev/null +++ b/dolphin/src/kitemviews/kstandarditem.cpp @@ -0,0 +1,171 @@ +/*************************************************************************** + * Copyright (C) 2012 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "kstandarditem.h" + +#include +#include "kstandarditemmodel.h" + +KStandardItem::KStandardItem(KStandardItem* parent) : + m_parent(parent), + m_children(), + m_model(0), + m_data() +{ +} + +KStandardItem::KStandardItem(const QString& text, KStandardItem* parent) : + m_parent(parent), + m_children(), + m_model(0), + m_data() +{ + setText(text); +} + +KStandardItem::KStandardItem(const QString& icon, const QString& text, KStandardItem* parent) : + m_parent(parent), + m_children(), + m_model(0), + m_data() +{ + setIcon(icon); + setText(text); +} + +KStandardItem::KStandardItem(const KStandardItem& item) : + m_parent(item.m_parent), + m_children(item.m_children), + m_model(item.m_model), + m_data(item.m_data) +{ +} + +KStandardItem::~KStandardItem() +{ +} + +void KStandardItem::setText(const QString& text) +{ + setDataValue("text", text); +} + +QString KStandardItem::text() const +{ + return m_data["text"].toString(); +} + +void KStandardItem::setIcon(const QString& icon) +{ + setDataValue("iconName", icon); +} + +QString KStandardItem::icon() const +{ + return m_data["iconName"].toString(); +} + +void KStandardItem::setIconOverlays(const QStringList& overlays) +{ + setDataValue("iconOverlays", overlays); +} + +QStringList KStandardItem::iconOverlays() const +{ + return m_data["iconOverlays"].toStringList(); +} + +void KStandardItem::setGroup(const QString& group) +{ + setDataValue("group", group); +} + +QString KStandardItem::group() const +{ + return m_data["group"].toString(); +} + +void KStandardItem::setDataValue(const QByteArray& role, const QVariant& value) +{ + const QVariant previous = m_data.value(role); + if (previous == value) { + return; + } + + m_data.insert(role, value); + onDataValueChanged(role, value, previous); + + if (m_model) { + const int index = m_model->index(this); + QSet changedRoles; + changedRoles.insert(role); + m_model->onItemChanged(index, changedRoles); + emit m_model->itemsChanged(KItemRangeList() << KItemRange(index, 1), changedRoles); + } +} + +QVariant KStandardItem::dataValue(const QByteArray& role) const +{ + return m_data[role]; +} + +void KStandardItem::setParent(KStandardItem* parent) +{ + // TODO: not implemented yet + m_parent = parent; +} + +KStandardItem* KStandardItem::parent() const +{ + return m_parent; +} + +void KStandardItem::setData(const QHash& values) +{ + const QHash previous = m_data; + m_data = values; + onDataChanged(values, previous); +} + +QHash KStandardItem::data() const +{ + return m_data; +} + +QList KStandardItem::children() const +{ + return m_children; +} + +void KStandardItem::onDataValueChanged(const QByteArray& role, + const QVariant& current, + const QVariant& previous) +{ + Q_UNUSED(role); + Q_UNUSED(current); + Q_UNUSED(previous); +} + +void KStandardItem::onDataChanged(const QHash& current, + const QHash& previous) +{ + Q_UNUSED(current); + Q_UNUSED(previous); +} + diff --git a/dolphin/src/kitemviews/kstandarditem.h b/dolphin/src/kitemviews/kstandarditem.h new file mode 100644 index 00000000..cfe404b8 --- /dev/null +++ b/dolphin/src/kitemviews/kstandarditem.h @@ -0,0 +1,102 @@ +/*************************************************************************** + * Copyright (C) 2012 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef KSTANDARDITEM_H +#define KSTANDARDITEM_H + +#include + +#include +#include +#include +#include +#include + +class KStandardItemModel; + +/** + * @brief Represents and item of KStandardItemModel. + * + * Provides setter- and getter-methods for the most commonly + * used roles. It is possible to assign values for custom + * roles by using setDataValue(). + */ +class DOLPHINPRIVATE_EXPORT KStandardItem +{ + +public: + explicit KStandardItem(KStandardItem* parent = 0); + explicit KStandardItem(const QString& text, KStandardItem* parent = 0); + KStandardItem(const QString& icon, const QString& text, KStandardItem* parent = 0); + KStandardItem(const KStandardItem& item); + virtual ~KStandardItem(); + + /** + * Sets the text for the "text"-role. + */ + void setText(const QString& text); + QString text() const; + + /** + * Sets the icon for the "iconName"-role. + */ + void setIcon(const QString& icon); + QString icon() const; + + void setIconOverlays(const QStringList& overlays); + QStringList iconOverlays() const; + + /** + * Sets the group for the "group"-role. + */ + void setGroup(const QString& group); + QString group() const; + + void setDataValue(const QByteArray& role, const QVariant& value); + QVariant dataValue(const QByteArray& role) const; + + void setParent(KStandardItem* parent); + KStandardItem* parent() const; + + void setData(const QHash& values); + QHash data() const; + + QList children() const; + +protected: + virtual void onDataValueChanged(const QByteArray& role, + const QVariant& current, + const QVariant& previous); + + virtual void onDataChanged(const QHash& current, + const QHash& previous); + +private: + KStandardItem* m_parent; + QList m_children; + KStandardItemModel* m_model; + + QHash m_data; + + friend class KStandardItemModel; +}; + +#endif + + diff --git a/dolphin/src/kitemviews/kstandarditemlistgroupheader.cpp b/dolphin/src/kitemviews/kstandarditemlistgroupheader.cpp new file mode 100644 index 00000000..eaae709d --- /dev/null +++ b/dolphin/src/kitemviews/kstandarditemlistgroupheader.cpp @@ -0,0 +1,96 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz * + * * + * Based on the Itemviews NG project from Trolltech Labs: * + * http://qt.gitorious.org/qt-labs/itemviews-ng * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "kstandarditemlistgroupheader.h" + +#include + +KStandardItemListGroupHeader::KStandardItemListGroupHeader(QGraphicsWidget* parent) : + KItemListGroupHeader(parent), + m_dirtyCache(true), + m_text() +{ +} + +KStandardItemListGroupHeader::~KStandardItemListGroupHeader() +{ +} + +void KStandardItemListGroupHeader::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) +{ + if (m_dirtyCache) { + updateCache(); + } + KItemListGroupHeader::paint(painter, option, widget); +} + +void KStandardItemListGroupHeader::paintRole(QPainter* painter, const QRectF& roleBounds, const QColor& color) +{ + painter->setPen(color); + painter->drawText(roleBounds, m_text); +} + +void KStandardItemListGroupHeader::paintSeparator(QPainter* painter, const QColor& color) +{ + if (itemIndex() == 0) { + // No top- or left-line should be drawn for the first group-header + return; + } + + painter->setPen(color); + + if (scrollOrientation() == Qt::Horizontal) { + painter->drawLine(0, 0, 0, size().height() - 1); + } else { + painter->drawLine(0, 0, size().width() - 1, 0); + } +} + +void KStandardItemListGroupHeader::roleChanged(const QByteArray ¤t, const QByteArray &previous) +{ + Q_UNUSED(current); + Q_UNUSED(previous); + m_dirtyCache = true; +} + +void KStandardItemListGroupHeader::dataChanged(const QVariant& current, const QVariant& previous) +{ + Q_UNUSED(current); + Q_UNUSED(previous); + m_dirtyCache = true; +} + +void KStandardItemListGroupHeader::resizeEvent(QGraphicsSceneResizeEvent* event) +{ + QGraphicsWidget::resizeEvent(event); + m_dirtyCache = true; +} + +void KStandardItemListGroupHeader::updateCache() +{ + Q_ASSERT(m_dirtyCache); + m_dirtyCache = false; + + m_text = data().toString(); +} + +#include "moc_kstandarditemlistgroupheader.cpp" diff --git a/dolphin/src/kitemviews/kstandarditemlistgroupheader.h b/dolphin/src/kitemviews/kstandarditemlistgroupheader.h new file mode 100644 index 00000000..d299c021 --- /dev/null +++ b/dolphin/src/kitemviews/kstandarditemlistgroupheader.h @@ -0,0 +1,56 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef KSTANDARDITEMLISTGROUPHEADER_H +#define KSTANDARDITEMLISTGROUPHEADER_H + +#include + +#include + +#include +#include + +class DOLPHINPRIVATE_EXPORT KStandardItemListGroupHeader : public KItemListGroupHeader +{ + Q_OBJECT + +public: + KStandardItemListGroupHeader(QGraphicsWidget* parent = 0); + virtual ~KStandardItemListGroupHeader(); + + virtual void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget = 0); + +protected: + virtual void paintRole(QPainter* painter, const QRectF& roleBounds, const QColor& color); + virtual void paintSeparator(QPainter* painter, const QColor& color); + virtual void roleChanged(const QByteArray ¤t, const QByteArray &previous); + virtual void dataChanged(const QVariant& current, const QVariant& previous); + virtual void resizeEvent(QGraphicsSceneResizeEvent* event); + +private: + void updateCache(); + +private: + bool m_dirtyCache; + QString m_text; +}; +#endif + + diff --git a/dolphin/src/kitemviews/kstandarditemlistview.cpp b/dolphin/src/kitemviews/kstandarditemlistview.cpp new file mode 100644 index 00000000..81111bcd --- /dev/null +++ b/dolphin/src/kitemviews/kstandarditemlistview.cpp @@ -0,0 +1,182 @@ +/*************************************************************************** + * Copyright (C) 2012 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "kstandarditemlistview.h" + +#include +#include +#include "kstandarditemlistwidget.h" +#include "kstandarditemlistgroupheader.h" + +KStandardItemListView::KStandardItemListView(QGraphicsWidget* parent) : + KItemListView(parent), + m_itemLayout(DetailsLayout) +{ + setAcceptDrops(true); + setScrollOrientation(Qt::Vertical); + setVisibleRoles(QList() << "text"); +} + +KStandardItemListView::~KStandardItemListView() +{ +} + +void KStandardItemListView::setItemLayout(ItemLayout layout) +{ + if (m_itemLayout == layout) { + return; + } + + beginTransaction(); + + const ItemLayout previous = m_itemLayout; + m_itemLayout = layout; + + setSupportsItemExpanding(itemLayoutSupportsItemExpanding(layout)); + setScrollOrientation(layout == CompactLayout ? Qt::Horizontal : Qt::Vertical); + + onItemLayoutChanged(layout, previous); + + endTransaction(); +} + +KStandardItemListView::ItemLayout KStandardItemListView::itemLayout() const +{ + return m_itemLayout; +} + +KItemListWidgetCreatorBase* KStandardItemListView::defaultWidgetCreator() const +{ + return new KItemListWidgetCreator(); +} + +KItemListGroupHeaderCreatorBase* KStandardItemListView::defaultGroupHeaderCreator() const +{ + return new KItemListGroupHeaderCreator(); +} + +void KStandardItemListView::initializeItemListWidget(KItemListWidget* item) +{ + KStandardItemListWidget* standardItemListWidget = qobject_cast(item); + Q_ASSERT(standardItemListWidget); + + switch (itemLayout()) { + case IconsLayout: standardItemListWidget->setLayout(KStandardItemListWidget::IconsLayout); break; + case CompactLayout: standardItemListWidget->setLayout(KStandardItemListWidget::CompactLayout); break; + case DetailsLayout: standardItemListWidget->setLayout(KStandardItemListWidget::DetailsLayout); break; + default: Q_ASSERT(false); break; + } + + standardItemListWidget->setSupportsItemExpanding(supportsItemExpanding()); +} + + +bool KStandardItemListView::itemSizeHintUpdateRequired(const QSet& changedRoles) const +{ + // The only thing that can modify the item's size hint is the amount of space + // needed to display the text for the visible roles. + // Even if the icons have a different size they are always aligned within + // the area defined by KItemStyleOption.iconSize and hence result in no + // change of the item-size. + foreach (const QByteArray& role, visibleRoles()) { + if (changedRoles.contains(role)) { + return true; + } + } + return false; +} + +bool KStandardItemListView::itemLayoutSupportsItemExpanding(ItemLayout layout) const +{ + return layout == DetailsLayout; +} + +void KStandardItemListView::onItemLayoutChanged(ItemLayout current, ItemLayout previous) +{ + Q_UNUSED(current); + Q_UNUSED(previous); + updateLayoutOfVisibleItems(); +} + +void KStandardItemListView::onScrollOrientationChanged(Qt::Orientation current, Qt::Orientation previous) +{ + Q_UNUSED(current); + Q_UNUSED(previous); + updateLayoutOfVisibleItems(); +} + +void KStandardItemListView::onSupportsItemExpandingChanged(bool supportsExpanding) +{ + Q_UNUSED(supportsExpanding); + updateLayoutOfVisibleItems(); +} + + +void KStandardItemListView::polishEvent() +{ + switch (m_itemLayout) { + case IconsLayout: applyDefaultStyleOption(style()->pixelMetric(QStyle::PM_LargeIconSize), 2, 4, 8); break; + case CompactLayout: applyDefaultStyleOption(style()->pixelMetric(QStyle::PM_SmallIconSize), 2, 8, 0); break; + case DetailsLayout: applyDefaultStyleOption(style()->pixelMetric(QStyle::PM_SmallIconSize), 2, 0, 0); break; + default: Q_ASSERT(false); break; + } + + QGraphicsWidget::polishEvent(); +} + +void KStandardItemListView::applyDefaultStyleOption(int iconSize, + int padding, + int horizontalMargin, + int verticalMargin) +{ + KItemListStyleOption option = styleOption(); + + bool changed = false; + if (option.iconSize < 0) { + option.iconSize = iconSize; + changed = true; + } + if (option.padding < 0) { + option.padding = padding; + changed = true; + } + if (option.horizontalMargin < 0) { + option.horizontalMargin = horizontalMargin; + changed = true; + } + if (option.verticalMargin < 0) { + option.verticalMargin = verticalMargin; + changed = true; + } + + if (changed) { + setStyleOption(option); + } +} + +void KStandardItemListView::updateLayoutOfVisibleItems() +{ + if (model()) { + foreach (KItemListWidget* widget, visibleItemListWidgets()) { + initializeItemListWidget(widget); + } + } +} + +#include "moc_kstandarditemlistview.cpp" diff --git a/dolphin/src/kitemviews/kstandarditemlistview.h b/dolphin/src/kitemviews/kstandarditemlistview.h new file mode 100644 index 00000000..3183f3eb --- /dev/null +++ b/dolphin/src/kitemviews/kstandarditemlistview.h @@ -0,0 +1,82 @@ +/*************************************************************************** + * Copyright (C) 2012 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef KSTANDARDITEMLISTVIEW_H +#define KSTANDARDITEMLISTVIEW_H + +#include + +#include + +/** + * @brief Provides layouts for icons-, compact- and details-view. + * + * Together with the KStandardItemModel lists for standard usecases + * can be created in a straight forward way. + * + * Example code: + * + * KStandardItemListView* view = new KStandardItemListView(); + * KStandardItemModel* model = new KStandardItemModel(); + * model->appendItem(new KStandardItem("Item 1")); + * model->appendItem(new KStandardItem("Item 2")); + * KItemListController* controller = new KItemListController(model, view); + * KItemListContainer* container = new KItemListContainer(controller, parentWidget); + * + */ +class DOLPHINPRIVATE_EXPORT KStandardItemListView : public KItemListView +{ + Q_OBJECT + +public: + enum ItemLayout + { + IconsLayout, + CompactLayout, + DetailsLayout + }; + + KStandardItemListView(QGraphicsWidget* parent = 0); + virtual ~KStandardItemListView(); + + void setItemLayout(ItemLayout layout); + ItemLayout itemLayout() const; + +protected: + virtual KItemListWidgetCreatorBase* defaultWidgetCreator() const; + virtual KItemListGroupHeaderCreatorBase* defaultGroupHeaderCreator() const; + virtual void initializeItemListWidget(KItemListWidget* item); + virtual bool itemSizeHintUpdateRequired(const QSet& changedRoles) const; + virtual bool itemLayoutSupportsItemExpanding(ItemLayout layout) const; + virtual void onItemLayoutChanged(ItemLayout current, ItemLayout previous); + virtual void onScrollOrientationChanged(Qt::Orientation current, Qt::Orientation previous); + virtual void onSupportsItemExpandingChanged(bool supportsExpanding); + virtual void polishEvent(); + +private: + void applyDefaultStyleOption(int iconSize, int padding, int horizontalMargin, int verticalMargin); + void updateLayoutOfVisibleItems(); + +private: + ItemLayout m_itemLayout; +}; + +#endif + + diff --git a/dolphin/src/kitemviews/kstandarditemlistwidget.cpp b/dolphin/src/kitemviews/kstandarditemlistwidget.cpp new file mode 100644 index 00000000..6df35b0d --- /dev/null +++ b/dolphin/src/kitemviews/kstandarditemlistwidget.cpp @@ -0,0 +1,1580 @@ +/*************************************************************************** + * Copyright (C) 2012 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "kstandarditemlistwidget.h" + +#include "kfileitemlistview.h" +#include "kfileitemmodel.h" + +#include +#include +#include +#include +#include +#include + +#include "private/kfileitemclipboard.h" +#include "private/kitemlistroleeditor.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// #define KSTANDARDITEMLISTWIDGET_DEBUG + +KStaticText::KStaticText() + : m_textwidth(0.0), + mixguard(false) +{ +} + +QString KStaticText::text() const +{ + return m_text; +} + +void KStaticText::setText(const QString &text) +{ + brect = QRectF(); + mixguard = false; + m_text = text; +} + +void KStaticText::setTextWidth(const qreal textwidth) +{ + brect = QRectF(); + mixguard = false; + m_textwidth = textwidth; +} + +// detailed/compact mode only getter +QSizeF KStaticText::size() const +{ +#ifndef QT_NO_DEBUG + if (mixguard) { + kWarning() << "Mixing size overloads, discarding cache"; + } +#endif + + if (!mixguard && !brect.isNull()) { + // qDebug() << Q_FUNC_INFO << "cache hit"; + return brect.size(); + } + + QTextLayout textlayout(m_text, m_font); + textlayout.setTextOption(m_textoption); + textlayout.beginLayout(); + QTextLine textline = textlayout.createLine(); + textline.setLineWidth(m_textwidth); + textlayout.endLayout(); + + brect = textlayout.boundingRect(); + return brect.size(); +} + +// multi-line (wrapped) text aware overload +QSizeF KStaticText::size(const QFontMetricsF &fontmetrics, const int maxlines) const +{ + mixguard = true; + + if (!brect.isNull()) { + // qDebug() << Q_FUNC_INFO << "cache hit"; + return brect.size(); + } + + QString text = m_text; + redo: + QTextLayout textlayout(text, m_font); + textlayout.setTextOption(m_textoption); + textlayout.beginLayout(); + QTextLine textline = textlayout.createLine(); + int linecount = 1; + const qreal fontleading = fontmetrics.leading(); + qreal lineheight = -fontleading; + while (textline.isValid()) { + textline.setLineWidth(m_textwidth); + lineheight += fontleading; + textline.setPosition(QPointF(0, lineheight)); + if (maxlines != -1 && linecount == maxlines && textline.naturalTextWidth() > m_textwidth) { + text = fontmetrics.elidedText(text, Qt::ElideRight, m_textwidth); + goto redo; + } + lineheight += textline.height(); + textline = textlayout.createLine(); + linecount++; + } + textlayout.endLayout(); + + brect = textlayout.boundingRect(); + return brect.size(); +} + +QTextOption KStaticText::textOption() const +{ + return m_textoption; +} + +void KStaticText::setTextOption(const QTextOption &textoption) +{ + m_textoption = textoption; +} + +void KStaticText::setFont(const QFont &font) +{ + m_font = font; +} + +void KStaticText::paint(QPainter *painter, const QPointF position, const QFontMetricsF &fontmetrics, const int maxlines) const +{ + QString text = m_text; + redo: + QTextLayout textlayout(text, m_font); + textlayout.setTextOption(m_textoption); + textlayout.beginLayout(); + QTextLine textline = textlayout.createLine(); + int linecount = 1; + qreal lineheight = 0; + while (textline.isValid()) { + textline.setLineWidth(m_textwidth); + textline.setPosition(QPointF(0, lineheight)); + if (maxlines != -1 && linecount == maxlines && textline.naturalTextWidth() > m_textwidth) { + text = fontmetrics.elidedText(text, Qt::ElideRight, m_textwidth); + goto redo; + } + lineheight += textline.height(); + textline = textlayout.createLine(); + linecount++; + } + textlayout.endLayout(); + + textlayout.draw(painter, position); +} + +KStandardItemListWidgetInformant::KStandardItemListWidgetInformant() : + KItemListWidgetInformant() +{ +} + +KStandardItemListWidgetInformant::~KStandardItemListWidgetInformant() +{ +} + +void KStandardItemListWidgetInformant::calculateItemSizeHints(QVector& logicalHeightHints, qreal& logicalWidthHint, const KItemListView* view) const +{ + switch (static_cast(view)->itemLayout()) { + case KStandardItemListView::IconsLayout: + calculateIconsLayoutItemSizeHints(logicalHeightHints, logicalWidthHint, view); + break; + + case KStandardItemListView::CompactLayout: + calculateCompactLayoutItemSizeHints(logicalHeightHints, logicalWidthHint, view); + break; + + case KStandardItemListView::DetailsLayout: + calculateDetailsLayoutItemSizeHints(logicalHeightHints, logicalWidthHint, view); + break; + + default: + Q_ASSERT(false); + break; + } +} + +qreal KStandardItemListWidgetInformant::preferredRoleColumnWidth(const QByteArray& role, + int index, + const KItemListView* view) const +{ + const QHash values = view->model()->data(index); + const KItemListStyleOption& option = view->styleOption(); + + const QString text = roleText(role, values); + qreal width = KStandardItemListWidget::columnPadding(option); + + const QFontMetrics& normalFontMetrics = option.fontMetrics; + const QFontMetrics linkFontMetrics(customizedFontForLinks(option.font)); + + // If current item is a link, we use the customized link font metrics instead of the normal font metrics. + const QFontMetrics& fontMetrics = itemIsLink(index, view) ? linkFontMetrics : normalFontMetrics; + + width += fontMetrics.width(text); + + if (role == "text") { + if (view->supportsItemExpanding()) { + // Increase the width by the expansion-toggle and the current expansion level + const int expandedParentsCount = values.value("expandedParentsCount", 0).toInt(); + const qreal height = option.padding * 2 + qMax(option.iconSize, fontMetrics.height()); + width += (expandedParentsCount + 1) * height; + } + + // Increase the width by the required space for the icon + width += option.padding * 2 + option.iconSize; + } + + return width; +} + +QString KStandardItemListWidgetInformant::itemText(int index, const KItemListView* view) const +{ + return view->model()->data(index).value("text").toString(); +} + +bool KStandardItemListWidgetInformant::itemIsLink(int index, const KItemListView* view) const +{ + return false; +} + +QString KStandardItemListWidgetInformant::roleText(const QByteArray& role, + const QHash& values) const +{ + return values.value(role).toString(); +} + +QFont KStandardItemListWidgetInformant::customizedFontForLinks(const QFont& baseFont) const +{ + return baseFont; +} + +void KStandardItemListWidgetInformant::calculateIconsLayoutItemSizeHints(QVector& logicalHeightHints, qreal& logicalWidthHint, const KItemListView* view) const +{ + const KItemListStyleOption& option = view->styleOption(); + const QFont& normalFont = option.font; + const int additionalRolesCount = qMax(view->visibleRoles().count() - 1, 0); + + const qreal itemWidth = view->itemSize().width(); + const qreal maxWidth = itemWidth - 2 * option.padding; + const qreal additionalRolesSpacing = additionalRolesCount * option.fontMetrics.lineSpacing(); + const qreal spacingAndIconHeight = option.iconSize + option.padding * 3; + + const QFont linkFont = customizedFontForLinks(normalFont); + + QTextOption textOption(Qt::AlignHCenter); + textOption.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); + + for (int index = 0; index < logicalHeightHints.count(); ++index) { + if (logicalHeightHints.at(index) > 0.0) { + continue; + } + + // If the current item is a link, we use the customized link font instead of the normal font. + const QFont& font = itemIsLink(index, view) ? linkFont : normalFont; + + const QString& text = KStringHandler::preProcessWrap(itemText(index, view)); + + // Calculate the number of lines required for wrapping the name + qreal textHeight = 0; + QTextLayout layout(text, font); + layout.setTextOption(textOption); + layout.beginLayout(); + QTextLine line; + int lineCount = 0; + while ((line = layout.createLine()).isValid()) { + line.setLineWidth(maxWidth); + line.naturalTextWidth(); + textHeight += line.height(); + + ++lineCount; + if (lineCount == option.maxTextLines) { + break; + } + } + layout.endLayout(); + + // Add one line for each additional information + textHeight += additionalRolesSpacing; + + logicalHeightHints[index] = textHeight + spacingAndIconHeight; + } + + logicalWidthHint = itemWidth; +} + +void KStandardItemListWidgetInformant::calculateCompactLayoutItemSizeHints(QVector& logicalHeightHints, qreal& logicalWidthHint, const KItemListView* view) const +{ + const KItemListStyleOption& option = view->styleOption(); + const QFontMetrics& normalFontMetrics = option.fontMetrics; + const int additionalRolesCount = qMax(view->visibleRoles().count() - 1, 0); + + const QList& visibleRoles = view->visibleRoles(); + const bool showOnlyTextRole = (visibleRoles.count() == 1) && (visibleRoles.first() == "text"); + const qreal maxWidth = option.maxTextWidth; + const qreal paddingAndIconWidth = option.padding * 4 + option.iconSize; + const qreal height = option.padding * 2 + qMax(option.iconSize, (1 + additionalRolesCount) * normalFontMetrics.lineSpacing()); + + const QFontMetrics linkFontMetrics(customizedFontForLinks(option.font)); + + for (int index = 0; index < logicalHeightHints.count(); ++index) { + if (logicalHeightHints.at(index) > 0.0) { + continue; + } + + // If the current item is a link, we use the customized link font metrics instead of the normal font metrics. + const QFontMetrics& fontMetrics = itemIsLink(index, view) ? linkFontMetrics : normalFontMetrics; + + // For each row exactly one role is shown. Calculate the maximum required width that is necessary + // to show all roles without horizontal clipping. + qreal maximumRequiredWidth = 0.0; + + if (showOnlyTextRole) { + maximumRequiredWidth = fontMetrics.width(itemText(index, view)); + } else { + const QHash& values = view->model()->data(index); + foreach (const QByteArray& role, visibleRoles) { + const QString& text = roleText(role, values); + const qreal requiredWidth = fontMetrics.width(text); + maximumRequiredWidth = qMax(maximumRequiredWidth, requiredWidth); + } + } + + qreal width = paddingAndIconWidth + maximumRequiredWidth; + if (maxWidth > 0 && width > maxWidth) { + width = maxWidth; + } + + logicalHeightHints[index] = width; + } + + logicalWidthHint = height; +} + +void KStandardItemListWidgetInformant::calculateDetailsLayoutItemSizeHints(QVector& logicalHeightHints, qreal& logicalWidthHint, const KItemListView* view) const +{ + const KItemListStyleOption& option = view->styleOption(); + const qreal height = option.padding * 2 + qMax(option.iconSize, option.fontMetrics.height()); + logicalHeightHints.fill(height); + logicalWidthHint = -1.0; +} + +KStandardItemListWidget::KStandardItemListWidget(KItemListWidgetInformant* informant, QGraphicsItem* parent) : + KItemListWidget(informant, parent), + m_isCut(false), + m_isHidden(false), + m_customizedFont(), + m_customizedFontMetrics(m_customizedFont), + m_isExpandable(false), + m_supportsItemExpanding(false), + m_dirtyLayout(true), + m_dirtyContent(true), + m_dirtyContentRoles(), + m_layout(IconsLayout), + m_pixmapPos(), + m_pixmap(), + m_scaledPixmapSize(), + m_iconRect(), + m_hoverPixmap(), + m_textInfo(), + m_textRect(), + m_sortedVisibleRoles(), + m_expansionArea(), + m_customTextColor(), + m_additionalInfoTextColor(), + m_overlay(), + m_roleEditor(0), + m_oldRoleEditor(0) +{ +} + +KStandardItemListWidget::~KStandardItemListWidget() +{ + qDeleteAll(m_textInfo); + m_textInfo.clear(); + + if (m_roleEditor) { + m_roleEditor->deleteLater(); + } + + if (m_oldRoleEditor) { + m_oldRoleEditor->deleteLater(); + } +} + +void KStandardItemListWidget::setLayout(Layout layout) +{ + if (m_layout != layout) { + m_layout = layout; + m_dirtyLayout = true; + updateAdditionalInfoTextColor(); + update(); + } +} + +KStandardItemListWidget::Layout KStandardItemListWidget::layout() const +{ + return m_layout; +} + +void KStandardItemListWidget::setSupportsItemExpanding(bool supportsItemExpanding) +{ + if (m_supportsItemExpanding != supportsItemExpanding) { + m_supportsItemExpanding = supportsItemExpanding; + m_dirtyLayout = true; + update(); + } +} + +bool KStandardItemListWidget::supportsItemExpanding() const +{ + return m_supportsItemExpanding; +} + +void KStandardItemListWidget::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) +{ + const_cast(this)->triggerCacheRefreshing(); + + KItemListWidget::paint(painter, option, widget); + + if (!m_expansionArea.isEmpty()) { + drawSiblingsInformation(painter); + } + + const KItemListStyleOption& itemListStyleOption = styleOption(); + if (isHovered()) { + if (hoverOpacity() < 1.0) { + /* + * Linear interpolation between m_pixmap and m_hoverPixmap. + * + * Note that this cannot be achieved by painting m_hoverPixmap over + * m_pixmap, even if the opacities are adjusted. For details see + * https://git.reviewboard.kde.org/r/109614/ + */ + // Paint pixmap1 so that pixmap1 = m_pixmap * (1.0 - hoverOpacity()) + QPixmap pixmap1(m_pixmap.size()); + pixmap1.fill(Qt::transparent); + { + QPainter p(&pixmap1); + p.setOpacity(1.0 - hoverOpacity()); + p.drawPixmap(0, 0, m_pixmap); + } + + // Paint pixmap2 so that pixmap2 = m_hoverPixmap * hoverOpacity() + QPixmap pixmap2(pixmap1.size()); + pixmap2.fill(Qt::transparent); + { + QPainter p(&pixmap2); + p.setOpacity(hoverOpacity()); + p.drawPixmap(0, 0, m_hoverPixmap); + } + + // Paint pixmap2 on pixmap1 using CompositionMode_Plus + // Now pixmap1 = pixmap2 + m_pixmap * (1.0 - hoverOpacity()) + // = m_hoverPixmap * hoverOpacity() + m_pixmap * (1.0 - hoverOpacity()) + { + QPainter p(&pixmap1); + p.setCompositionMode(QPainter::CompositionMode_Plus); + p.drawPixmap(0, 0, pixmap2); + } + + // Finally paint pixmap1 on the widget + drawPixmap(painter, pixmap1); + } else { + drawPixmap(painter, m_hoverPixmap); + } + } else { + drawPixmap(painter, m_pixmap); + } + + painter->setFont(m_customizedFont); + painter->setPen(textColor()); + const TextInfo* textInfo = m_textInfo.value("text"); + + if (!textInfo) { + // It seems that we can end up here even if m_textInfo does not contain + // the key "text", see bug 306167. According to triggerCacheRefreshing(), + // this can only happen if the index is negative. This can happen when + // the item is about to be removed, see KItemListView::slotItemsRemoved(). + // TODO: try to reproduce the crash and find a better fix. + return; + } + + textInfo->staticText.paint(painter, textInfo->pos, m_customizedFontMetrics, itemListStyleOption.maxTextLines); + + bool clipAdditionalInfoBounds = false; + if (m_supportsItemExpanding) { + // Prevent a possible overlapping of the additional-information texts + // with the icon. This can happen if the user has minimized the width + // of the name-column to a very small value. + const qreal minX = m_pixmapPos.x() + m_pixmap.width() + 4 * itemListStyleOption.padding; + if (textInfo->pos.x() + columnWidth("text") > minX) { + clipAdditionalInfoBounds = true; + painter->save(); + painter->setClipRect(minX, 0, size().width() - minX, size().height(), Qt::IntersectClip); + } + } + + painter->setPen(m_additionalInfoTextColor); + painter->setFont(m_customizedFont); + + for (int i = 1; i < m_sortedVisibleRoles.count(); ++i) { + const TextInfo* textInfo = m_textInfo.value(m_sortedVisibleRoles[i]); + textInfo->staticText.paint(painter, textInfo->pos, m_customizedFontMetrics); + } + + if (clipAdditionalInfoBounds) { + painter->restore(); + } + +#ifdef KSTANDARDITEMLISTWIDGET_DEBUG + painter->setBrush(Qt::NoBrush); + painter->setPen(Qt::green); + painter->drawRect(m_iconRect); + + painter->setPen(Qt::blue); + painter->drawRect(m_textRect); + + painter->setPen(Qt::red); + painter->drawText(QPointF(0, m_customizedFontMetrics.height()), QString::number(index())); + painter->drawRect(rect()); +#endif +} + +QRectF KStandardItemListWidget::iconRect() const +{ + const_cast(this)->triggerCacheRefreshing(); + return m_iconRect; +} + +QRectF KStandardItemListWidget::textRect() const +{ + const_cast(this)->triggerCacheRefreshing(); + return m_textRect; +} + +QRectF KStandardItemListWidget::textFocusRect() const +{ + // In the compact- and details-layout a larger textRect() is returned to be aligned + // with the iconRect(). This is useful to have a larger selection/hover-area + // when having a quite large icon size but only one line of text. Still the + // focus rectangle should be shown as narrow as possible around the text. + + const_cast(this)->triggerCacheRefreshing(); + + switch (m_layout) { + case CompactLayout: { + QRectF rect = m_textRect; + const TextInfo* topText = m_textInfo.value(m_sortedVisibleRoles.first()); + const TextInfo* bottomText = m_textInfo.value(m_sortedVisibleRoles.last()); + rect.setTop(topText->pos.y()); + rect.setBottom(bottomText->pos.y() + bottomText->staticText.size().height()); + return rect; + } + + case DetailsLayout: { + QRectF rect = m_textRect; + const TextInfo* textInfo = m_textInfo.value(m_sortedVisibleRoles.first()); + rect.setTop(textInfo->pos.y()); + rect.setBottom(textInfo->pos.y() + textInfo->staticText.size().height()); + + const KItemListStyleOption& option = styleOption(); + if (option.extendedSelectionRegion) { + const QString text = textInfo->staticText.text(); + rect.setWidth(m_customizedFontMetrics.width(text) + 2 * option.padding); + } + + return rect; + } + + default: + break; + } + + return m_textRect; +} + +QRectF KStandardItemListWidget::selectionRect() const +{ + const_cast(this)->triggerCacheRefreshing(); + + switch (m_layout) { + case IconsLayout: + return m_textRect; + + case CompactLayout: + case DetailsLayout: { + const int padding = styleOption().padding; + QRectF adjustedIconRect = iconRect().adjusted(-padding, -padding, padding, padding); + return adjustedIconRect | m_textRect; + } + + default: + Q_ASSERT(false); + break; + } + + return m_textRect; +} + +QRectF KStandardItemListWidget::expansionToggleRect() const +{ + const_cast(this)->triggerCacheRefreshing(); + return m_isExpandable ? m_expansionArea : QRectF(); +} + +QRectF KStandardItemListWidget::selectionToggleRect() const +{ + const_cast(this)->triggerCacheRefreshing(); + + const int iconHeight = styleOption().iconSize; + + int toggleSize = KIconLoader::SizeSmall; + if (iconHeight >= KIconLoader::SizeEnormous) { + toggleSize = KIconLoader::SizeMedium; + } else if (iconHeight >= KIconLoader::SizeLarge) { + toggleSize = KIconLoader::SizeSmallMedium; + } + + QPointF pos = iconRect().topLeft(); + + // If the selection toggle has a very small distance to the + // widget borders, the size of the selection toggle will get + // increased to prevent an accidental clicking of the item + // when trying to hit the toggle. + const int widgetHeight = size().height(); + const int widgetWidth = size().width(); + const int minMargin = 2; + + if (toggleSize + minMargin * 2 >= widgetHeight) { + pos.rx() -= (widgetHeight - toggleSize) / 2; + toggleSize = widgetHeight; + pos.setY(0); + } + if (toggleSize + minMargin * 2 >= widgetWidth) { + pos.ry() -= (widgetWidth - toggleSize) / 2; + toggleSize = widgetWidth; + pos.setX(0); + } + + return QRectF(pos, QSizeF(toggleSize, toggleSize)); +} + +QPixmap KStandardItemListWidget::createDragPixmap(const QStyleOptionGraphicsItem* option, + QWidget* widget) +{ + QPixmap pixmap = KItemListWidget::createDragPixmap(option, widget); + if (m_layout != DetailsLayout) { + return pixmap; + } + + // Only return the content of the text-column as pixmap + const int leftClip = m_pixmapPos.x(); + + const TextInfo* textInfo = m_textInfo.value("text"); + const int rightClip = textInfo->pos.x() + + textInfo->staticText.size().width() + + 2 * styleOption().padding; + + QPixmap clippedPixmap(rightClip - leftClip + 1, pixmap.height()); + clippedPixmap.fill(Qt::transparent); + + QPainter painter(&clippedPixmap); + painter.drawPixmap(-leftClip, 0, pixmap); + + return clippedPixmap; +} + + +KItemListWidgetInformant* KStandardItemListWidget::createInformant() +{ + return new KStandardItemListWidgetInformant(); +} + +void KStandardItemListWidget::invalidateCache() +{ + m_dirtyLayout = true; + m_dirtyContent = true; +} + +void KStandardItemListWidget::refreshCache() +{ +} + +bool KStandardItemListWidget::isRoleRightAligned(const QByteArray& role) const +{ + Q_UNUSED(role); + return false; +} + +bool KStandardItemListWidget::isHidden() const +{ + return false; +} + +QFont KStandardItemListWidget::customizedFont(const QFont& baseFont) const +{ + return baseFont; +} + +QPalette::ColorRole KStandardItemListWidget::normalTextColorRole() const +{ + return QPalette::Text; +} + +void KStandardItemListWidget::setTextColor(const QColor& color) +{ + if (color != m_customTextColor) { + m_customTextColor = color; + updateAdditionalInfoTextColor(); + update(); + } +} + +QColor KStandardItemListWidget::textColor() const +{ + if (!isSelected()) { + if (m_isHidden) { + return m_additionalInfoTextColor; + } else if (m_customTextColor.isValid()) { + return m_customTextColor; + } + } + + const QPalette::ColorGroup group = isActiveWindow() ? QPalette::Active : QPalette::Inactive; + const QPalette::ColorRole role = isSelected() ? QPalette::HighlightedText : normalTextColorRole(); + return styleOption().palette.color(group, role); +} + +void KStandardItemListWidget::setOverlay(const QPixmap& overlay) +{ + m_overlay = overlay; + m_dirtyContent = true; + update(); +} + +QPixmap KStandardItemListWidget::overlay() const +{ + return m_overlay; +} + + +QString KStandardItemListWidget::roleText(const QByteArray& role, + const QHash& values) const +{ + return static_cast(informant())->roleText(role, values); +} + +void KStandardItemListWidget::dataChanged(const QHash& current, + const QSet& roles) +{ + Q_UNUSED(current); + + m_dirtyContent = true; + + QSet dirtyRoles; + if (roles.isEmpty()) { + dirtyRoles = visibleRoles().toSet(); + } else { + dirtyRoles = roles; + } + + // The URL might have changed (i.e., if the sort order of the items has + // been changed). Therefore, the "is cut" state must be updated. + KFileItemClipboard* clipboard = KFileItemClipboard::instance(); + const KUrl itemUrl = data().value("url").value(); + m_isCut = clipboard->isCut(itemUrl); + + // The icon-state might depend from other roles and hence is + // marked as dirty whenever a role has been changed + dirtyRoles.insert("iconPixmap"); + dirtyRoles.insert("iconName"); + + QSetIterator it(dirtyRoles); + while (it.hasNext()) { + const QByteArray& role = it.next(); + m_dirtyContentRoles.insert(role); + } +} + +void KStandardItemListWidget::visibleRolesChanged(const QList& current, + const QList& previous) +{ + Q_UNUSED(previous); + m_sortedVisibleRoles = current; + m_dirtyLayout = true; +} + +void KStandardItemListWidget::columnWidthChanged(const QByteArray& role, + qreal current, + qreal previous) +{ + Q_UNUSED(role); + Q_UNUSED(current); + Q_UNUSED(previous); + m_dirtyLayout = true; +} + +void KStandardItemListWidget::styleOptionChanged(const KItemListStyleOption& current, + const KItemListStyleOption& previous) +{ + Q_UNUSED(current); + Q_UNUSED(previous); + updateAdditionalInfoTextColor(); + m_dirtyLayout = true; +} + +void KStandardItemListWidget::hoveredChanged(bool hovered) +{ + Q_UNUSED(hovered); + m_dirtyLayout = true; +} + +void KStandardItemListWidget::selectedChanged(bool selected) +{ + Q_UNUSED(selected); + updateAdditionalInfoTextColor(); + m_dirtyContent = true; +} + +void KStandardItemListWidget::siblingsInformationChanged(const QBitArray& current, const QBitArray& previous) +{ + Q_UNUSED(current); + Q_UNUSED(previous); + m_dirtyLayout = true; +} + +int KStandardItemListWidget::selectionLength(const QString& text) const +{ + return text.length(); +} + +void KStandardItemListWidget::editedRoleChanged(const QByteArray& current, const QByteArray& previous) +{ + Q_UNUSED(previous); + + QGraphicsView* parent = scene()->views()[0]; + if (current.isEmpty() || !parent || current != "text") { + if (m_roleEditor) { + emit roleEditingCanceled(index(), current, data().value(current)); + + disconnect(m_roleEditor, SIGNAL(roleEditingCanceled(QByteArray,QVariant)), + this, SLOT(slotRoleEditingCanceled(QByteArray,QVariant))); + disconnect(m_roleEditor, SIGNAL(roleEditingFinished(QByteArray,QVariant)), + this, SLOT(slotRoleEditingFinished(QByteArray,QVariant))); + + if (m_oldRoleEditor) { + m_oldRoleEditor->deleteLater(); + } + m_oldRoleEditor = m_roleEditor; + m_roleEditor->hide(); + m_roleEditor = 0; + } + return; + } + + Q_ASSERT(!m_roleEditor); + + const TextInfo* textInfo = m_textInfo.value("text"); + + m_roleEditor = new KItemListRoleEditor(parent); + m_roleEditor->setRole(current); + m_roleEditor->setFont(styleOption().font); + + const QString text = data().value(current).toString(); + m_roleEditor->setPlainText(text); + + QTextOption textOption = textInfo->staticText.textOption(); + m_roleEditor->document()->setDefaultTextOption(textOption); + + const int textSelectionLength = selectionLength(text); + + if (textSelectionLength > 0) { + QTextCursor cursor = m_roleEditor->textCursor(); + cursor.movePosition(QTextCursor::StartOfBlock); + cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, textSelectionLength); + m_roleEditor->setTextCursor(cursor); + } + + connect(m_roleEditor, SIGNAL(roleEditingCanceled(QByteArray,QVariant)), + this, SLOT(slotRoleEditingCanceled(QByteArray,QVariant))); + connect(m_roleEditor, SIGNAL(roleEditingFinished(QByteArray,QVariant)), + this, SLOT(slotRoleEditingFinished(QByteArray,QVariant))); + + // Adjust the geometry of the editor + QRectF rect = roleEditingRect(current); + const int frameWidth = m_roleEditor->frameWidth(); + rect.adjust(-frameWidth, -frameWidth, frameWidth, frameWidth); + rect.translate(pos()); + if (rect.right() > parent->width()) { + rect.setWidth(parent->width() - rect.left()); + } + m_roleEditor->setGeometry(rect.toRect()); + m_roleEditor->show(); + m_roleEditor->setFocus(); +} + +void KStandardItemListWidget::resizeEvent(QGraphicsSceneResizeEvent* event) +{ + if (m_roleEditor) { + setEditedRole(QByteArray()); + Q_ASSERT(!m_roleEditor); + } + + KItemListWidget::resizeEvent(event); + + m_dirtyLayout = true; +} + +void KStandardItemListWidget::showEvent(QShowEvent* event) +{ + KItemListWidget::showEvent(event); + + // Listen to changes of the clipboard to mark the item as cut/uncut + KFileItemClipboard* clipboard = KFileItemClipboard::instance(); + + const KUrl itemUrl = data().value("url").value(); + m_isCut = clipboard->isCut(itemUrl); + + connect(clipboard, SIGNAL(cutItemsChanged()), + this, SLOT(slotCutItemsChanged())); +} + +void KStandardItemListWidget::hideEvent(QHideEvent* event) +{ + disconnect(KFileItemClipboard::instance(), SIGNAL(cutItemsChanged()), + this, SLOT(slotCutItemsChanged())); + + KItemListWidget::hideEvent(event); +} + +void KStandardItemListWidget::slotCutItemsChanged() +{ + const KUrl itemUrl = data().value("url").value(); + const bool isCut = KFileItemClipboard::instance()->isCut(itemUrl); + if (m_isCut != isCut) { + m_isCut = isCut; + m_pixmap = QPixmap(); + m_dirtyContent = true; + update(); + } +} + +void KStandardItemListWidget::slotRoleEditingCanceled(const QByteArray& role, + const QVariant& value) +{ + closeRoleEditor(); + emit roleEditingCanceled(index(), role, value); + setEditedRole(QByteArray()); +} + +void KStandardItemListWidget::slotRoleEditingFinished(const QByteArray& role, + const QVariant& value) +{ + closeRoleEditor(); + emit roleEditingFinished(index(), role, value); + setEditedRole(QByteArray()); +} + +void KStandardItemListWidget::triggerCacheRefreshing() +{ + if ((!m_dirtyContent && !m_dirtyLayout) || index() < 0) { + return; + } + + refreshCache(); + + const QHash values = data(); + m_isExpandable = m_supportsItemExpanding && values["isExpandable"].toBool(); + m_isHidden = isHidden(); + m_customizedFont = customizedFont(styleOption().font); + m_customizedFontMetrics = QFontMetrics(m_customizedFont); + + updateExpansionArea(); + updateTextsCache(); + updatePixmapCache(); + + m_dirtyLayout = false; + m_dirtyContent = false; + m_dirtyContentRoles.clear(); +} + +void KStandardItemListWidget::updateExpansionArea() +{ + if (m_supportsItemExpanding) { + const QHash values = data(); + const int expandedParentsCount = values.value("expandedParentsCount", 0).toInt(); + if (expandedParentsCount >= 0) { + const KItemListStyleOption& option = styleOption(); + const qreal widgetHeight = size().height(); + const qreal inc = (widgetHeight - option.iconSize) / 2; + const qreal x = expandedParentsCount * widgetHeight + inc; + const qreal y = inc; + m_expansionArea = QRectF(x, y, option.iconSize, option.iconSize); + return; + } + } + + m_expansionArea = QRectF(); +} + +void KStandardItemListWidget::updatePixmapCache() +{ + // Precondition: Requires already updated m_textPos values to calculate + // the remaining height when the alignment is vertical. + + const QSizeF widgetSize = size(); + const bool iconOnTop = (m_layout == IconsLayout); + const KItemListStyleOption& option = styleOption(); + const qreal padding = option.padding; + + const int maxIconWidth = iconOnTop ? widgetSize.width() - 2 * padding : option.iconSize; + const int maxIconHeight = option.iconSize; + + const QHash values = data(); + + bool updatePixmap = (m_pixmap.width() != maxIconWidth || m_pixmap.height() != maxIconHeight); + if (!updatePixmap && m_dirtyContent) { + updatePixmap = m_dirtyContentRoles.isEmpty() + || m_dirtyContentRoles.contains("iconPixmap") + || m_dirtyContentRoles.contains("iconName") + || m_dirtyContentRoles.contains("iconOverlays"); + } + + if (updatePixmap) { + m_pixmap = values["iconPixmap"].value(); + if (m_pixmap.isNull()) { + // Use the icon that fits to the MIME-type + QString iconName = values["iconName"].toString(); + if (iconName.isEmpty()) { + // The icon-name has not been not resolved by KFileItemModelRolesUpdater, + // use a generic icon as fallback + iconName = QLatin1String("unknown"); + } + const QStringList overlays = values["iconOverlays"].toStringList(); + m_pixmap = pixmapForIcon(iconName, overlays, maxIconHeight); + } else if (m_pixmap.width() != maxIconWidth || m_pixmap.height() != maxIconHeight) { + // A custom pixmap has been applied. Assure that the pixmap + // is scaled to the maximum available size. + m_pixmap = m_pixmap.scaled(QSize(maxIconWidth, maxIconHeight), Qt::KeepAspectRatio, Qt::SmoothTransformation); + } + + if (m_isCut) { + KIconEffect* effect = KIconLoader::global()->iconEffect(); + m_pixmap = effect->apply(m_pixmap, KIconLoader::Desktop, KIconLoader::DisabledState); + } + + if (m_isHidden) { + KIconEffect::semiTransparent(m_pixmap); + } + + if (m_layout == IconsLayout && isSelected()) { + const QColor color = palette().brush(QPalette::Normal, QPalette::Highlight).color(); + QImage image = m_pixmap.toImage(); + KIconEffect::colorize(image, color, 0.8f); + m_pixmap = QPixmap::fromImage(image); + } + } + + if (!m_overlay.isNull()) { + QPainter painter(&m_pixmap); + painter.drawPixmap(0, m_pixmap.height() - m_overlay.height(), m_overlay); + } + + int scaledIconSize = 0; + if (iconOnTop) { + const TextInfo* textInfo = m_textInfo.value("text"); + scaledIconSize = static_cast(textInfo->pos.y() - 2 * padding); + } else { + const int textRowsCount = (m_layout == CompactLayout) ? visibleRoles().count() : 1; + const qreal requiredTextHeight = textRowsCount * m_customizedFontMetrics.height(); + scaledIconSize = (requiredTextHeight < maxIconHeight) ? + widgetSize.height() - 2 * padding : maxIconHeight; + } + + const int maxScaledIconWidth = iconOnTop ? widgetSize.width() - 2 * padding : scaledIconSize; + const int maxScaledIconHeight = scaledIconSize; + + m_scaledPixmapSize = m_pixmap.size(); + m_scaledPixmapSize.scale(maxScaledIconWidth, maxScaledIconHeight, Qt::KeepAspectRatio); + + if (iconOnTop) { + // Center horizontally and align on bottom within the icon-area + m_pixmapPos.setX((widgetSize.width() - m_scaledPixmapSize.width()) / 2); + m_pixmapPos.setY(padding + scaledIconSize - m_scaledPixmapSize.height()); + } else { + // Center horizontally and vertically within the icon-area + const TextInfo* textInfo = m_textInfo.value("text"); + m_pixmapPos.setX(textInfo->pos.x() - 2 * padding + - (scaledIconSize + m_scaledPixmapSize.width()) / 2); + m_pixmapPos.setY(padding + + (scaledIconSize - m_scaledPixmapSize.height()) / 2); + } + + m_iconRect = QRectF(m_pixmapPos, QSizeF(m_scaledPixmapSize)); + + // Prepare the pixmap that is used when the item gets hovered + if (isHovered()) { + m_hoverPixmap = m_pixmap; + KIconEffect* effect = KIconLoader::global()->iconEffect(); + // In the KIconLoader terminology, active = hover. + if (effect->hasEffect(KIconLoader::Desktop, KIconLoader::ActiveState)) { + m_hoverPixmap = effect->apply(m_pixmap, KIconLoader::Desktop, KIconLoader::ActiveState); + } else { + m_hoverPixmap = m_pixmap; + } + } else if (hoverOpacity() <= 0.0) { + // No hover animation is ongoing. Clear m_hoverPixmap to save memory. + m_hoverPixmap = QPixmap(); + } +} + +void KStandardItemListWidget::updateTextsCache() +{ + QTextOption textOption; + switch (m_layout) { + case IconsLayout: + textOption.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); + textOption.setAlignment(Qt::AlignHCenter); + break; + case CompactLayout: + case DetailsLayout: + textOption.setAlignment(Qt::AlignLeft); + textOption.setWrapMode(QTextOption::NoWrap); + break; + default: + Q_ASSERT(false); + break; + } + + qDeleteAll(m_textInfo); + m_textInfo.clear(); + for (int i = 0; i < m_sortedVisibleRoles.count(); ++i) { + TextInfo* textInfo = new TextInfo(); + textInfo->staticText.setTextOption(textOption); + textInfo->staticText.setFont(m_customizedFont); + m_textInfo.insert(m_sortedVisibleRoles[i], textInfo); + } + + switch (m_layout) { + case IconsLayout: updateIconsLayoutTextCache(); break; + case CompactLayout: updateCompactLayoutTextCache(); break; + case DetailsLayout: updateDetailsLayoutTextCache(); break; + default: Q_ASSERT(false); break; + } +} + +void KStandardItemListWidget::updateIconsLayoutTextCache() +{ + // +------+ + // | Icon | + // +------+ + // + // Name role that + // might get wrapped above + // several lines. + // Additional role 1 + // Additional role 2 + + const QHash values = data(); + + const KItemListStyleOption& option = styleOption(); + const qreal padding = option.padding; + const qreal maxWidth = size().width() - 2 * padding; + const qreal widgetHeight = size().height(); + const qreal lineSpacing = m_customizedFontMetrics.lineSpacing(); + + // Initialize properties for the "text" role. It will be used as anchor + // for initializing the position of the other roles. + TextInfo* nameTextInfo = m_textInfo.value("text"); + const QString nameText = KStringHandler::preProcessWrap(values["text"].toString()); + nameTextInfo->staticText.setText(nameText); + + // Calculate the number of lines required for the name and the required width + qreal nameWidth = 0; + qreal nameHeight = 0; + QTextLine line; + + QTextLayout layout(nameTextInfo->staticText.text(), m_customizedFont); + layout.setTextOption(nameTextInfo->staticText.textOption()); + layout.beginLayout(); + int nameLineIndex = 0; + while ((line = layout.createLine()).isValid()) { + line.setLineWidth(maxWidth); + nameWidth = qMax(nameWidth, line.naturalTextWidth()); + nameHeight += line.height(); + + ++nameLineIndex; + if (nameLineIndex == option.maxTextLines) { + // The maximum number of textlines has been reached. If this is + // the case provide an elided text if necessary. + const int textLength = line.textStart() + line.textLength(); + if (textLength < nameText.length()) { + // Elide the last line of the text + qreal elidingWidth = maxWidth; + qreal lastLineWidth; + do { + QString lastTextLine = nameText.mid(line.textStart()); + lastTextLine = m_customizedFontMetrics.elidedText(lastTextLine, + Qt::ElideRight, + elidingWidth); + const QString elidedText = nameText.left(line.textStart()) + lastTextLine; + nameTextInfo->staticText.setText(elidedText); + + lastLineWidth = m_customizedFontMetrics.boundingRect(lastTextLine).width(); + + // We do the text eliding in a loop with decreasing width (1 px / iteration) + // to avoid problems related to different width calculation code paths + // within Qt. (see bug 337104) + elidingWidth -= 1.0; + } while (lastLineWidth > maxWidth); + + nameWidth = qMax(nameWidth, lastLineWidth); + } + break; + } + } + layout.endLayout(); + + // Use one line for each additional information + const int additionalRolesCount = qMax(visibleRoles().count() - 1, 0); + nameTextInfo->staticText.setTextWidth(maxWidth); + nameTextInfo->pos = QPointF(padding, widgetHeight - + nameHeight - + additionalRolesCount * lineSpacing - + padding); + m_textRect = QRectF(padding + (maxWidth - nameWidth) / 2, + nameTextInfo->pos.y(), + nameWidth, + nameHeight); + + // Calculate the position for each additional information + qreal y = nameTextInfo->pos.y() + nameHeight; + foreach (const QByteArray& role, m_sortedVisibleRoles) { + if (role == "text") { + continue; + } + + const QString text = roleText(role, values); + TextInfo* textInfo = m_textInfo.value(role); + textInfo->staticText.setText(text); + + qreal requiredWidth = 0; + + QTextLayout layout(text, m_customizedFont); + QTextOption textOption; + textOption.setWrapMode(QTextOption::NoWrap); + layout.setTextOption(textOption); + + layout.beginLayout(); + QTextLine textLine = layout.createLine(); + if (textLine.isValid()) { + textLine.setLineWidth(maxWidth); + requiredWidth = textLine.naturalTextWidth(); + if (requiredWidth > maxWidth) { + const QString elidedText = m_customizedFontMetrics.elidedText(text, Qt::ElideRight, maxWidth); + textInfo->staticText.setText(elidedText); + requiredWidth = m_customizedFontMetrics.width(elidedText);; + } + } + layout.endLayout(); + + textInfo->pos = QPointF(padding, y); + textInfo->staticText.setTextWidth(maxWidth); + + const QRectF textRect(padding + (maxWidth - requiredWidth) / 2, y, requiredWidth, lineSpacing); + m_textRect |= textRect; + + y += lineSpacing; + } + + // Add a padding to the text rectangle + m_textRect.adjust(-padding, -padding, padding, padding); +} + +void KStandardItemListWidget::updateCompactLayoutTextCache() +{ + // +------+ Name role + // | Icon | Additional role 1 + // +------+ Additional role 2 + + const QHash values = data(); + + const KItemListStyleOption& option = styleOption(); + const qreal widgetHeight = size().height(); + const qreal lineSpacing = m_customizedFontMetrics.lineSpacing(); + const qreal textLinesHeight = qMax(visibleRoles().count(), 1) * lineSpacing; + const int scaledIconSize = (textLinesHeight < option.iconSize) ? widgetHeight - 2 * option.padding : option.iconSize; + + qreal maximumRequiredTextWidth = 0; + const qreal x = option.padding * 3 + scaledIconSize; + qreal y = qRound((widgetHeight - textLinesHeight) / 2); + const qreal maxWidth = size().width() - x - option.padding; + foreach (const QByteArray& role, m_sortedVisibleRoles) { + const QString text = roleText(role, values); + TextInfo* textInfo = m_textInfo.value(role); + textInfo->staticText.setText(text); + textInfo->staticText.setFont(m_customizedFont); + + qreal requiredWidth = m_customizedFontMetrics.width(text); + if (requiredWidth > maxWidth) { + requiredWidth = maxWidth; + const QString elidedText = m_customizedFontMetrics.elidedText(text, Qt::ElideRight, maxWidth); + textInfo->staticText.setText(elidedText); + } + + textInfo->pos = QPointF(x, y); + textInfo->staticText.setTextWidth(maxWidth); + + maximumRequiredTextWidth = qMax(maximumRequiredTextWidth, requiredWidth); + + y += lineSpacing; + } + + m_textRect = QRectF(x - 2 * option.padding, 0, maximumRequiredTextWidth + 3 * option.padding, widgetHeight); +} + +void KStandardItemListWidget::updateDetailsLayoutTextCache() +{ + // Precondition: Requires already updated m_expansionArea + // to determine the left position. + + // +------+ + // | Icon | Name role Additional role 1 Additional role 2 + // +------+ + m_textRect = QRectF(); + + const KItemListStyleOption& option = styleOption(); + const QHash values = data(); + + const qreal widgetHeight = size().height(); + const int scaledIconSize = widgetHeight - 2 * option.padding; + const int fontHeight = m_customizedFontMetrics.height(); + + const qreal columnWidthInc = columnPadding(option); + qreal firstColumnInc = scaledIconSize; + if (m_supportsItemExpanding) { + firstColumnInc += (m_expansionArea.left() + m_expansionArea.right() + widgetHeight) / 2; + } else { + firstColumnInc += option.padding; + } + + qreal x = firstColumnInc; + const qreal y = qMax(qreal(option.padding), (widgetHeight - fontHeight) / 2); + + foreach (const QByteArray& role, m_sortedVisibleRoles) { + QString text = roleText(role, values); + + // Elide the text in case it does not fit into the available column-width + qreal requiredWidth = m_customizedFontMetrics.width(text); + const qreal roleWidth = columnWidth(role); + qreal availableTextWidth = roleWidth - columnWidthInc; + + const bool isTextRole = (role == "text"); + if (isTextRole) { + availableTextWidth -= firstColumnInc; + } + + if (requiredWidth > availableTextWidth) { + text = m_customizedFontMetrics.elidedText(text, Qt::ElideRight, availableTextWidth); + requiredWidth = m_customizedFontMetrics.width(text); + } + + TextInfo* textInfo = m_textInfo.value(role); + textInfo->staticText.setText(text); + textInfo->pos = QPointF(x + columnWidthInc / 2, y); + x += roleWidth; + + if (isTextRole) { + const qreal textWidth = option.extendedSelectionRegion + ? size().width() - textInfo->pos.x() + : requiredWidth + 2 * option.padding; + m_textRect = QRectF(textInfo->pos.x() - 2 * option.padding, 0, + textWidth + option.padding, size().height()); + + // The column after the name should always be aligned on the same x-position independent + // from the expansion-level shown in the name column + x -= firstColumnInc; + } else if (isRoleRightAligned(role)) { + textInfo->pos.rx() += roleWidth - requiredWidth - columnWidthInc; + } + } +} + +void KStandardItemListWidget::updateAdditionalInfoTextColor() +{ + QColor c1; + if (m_customTextColor.isValid()) { + c1 = m_customTextColor; + } else if (isSelected() && m_layout != DetailsLayout) { + c1 = styleOption().palette.highlightedText().color(); + } else { + c1 = styleOption().palette.text().color(); + } + + // For the color of the additional info the inactive text color + // is not used as this might lead to unreadable text for some color schemes. Instead + // the text color c1 is slightly mixed with the background color. + const QColor c2 = styleOption().palette.base().color(); + const int p1 = 70; + const int p2 = 100 - p1; + m_additionalInfoTextColor = QColor((c1.red() * p1 + c2.red() * p2) / 100, + (c1.green() * p1 + c2.green() * p2) / 100, + (c1.blue() * p1 + c2.blue() * p2) / 100); +} + +void KStandardItemListWidget::drawPixmap(QPainter* painter, const QPixmap& pixmap) +{ + if (m_scaledPixmapSize != pixmap.size()) { + QPixmap scaledPixmap = pixmap.scaled(m_scaledPixmapSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);; + painter->drawPixmap(m_pixmapPos, scaledPixmap); + +#ifdef KSTANDARDITEMLISTWIDGET_DEBUG + painter->setPen(Qt::blue); + painter->drawRect(QRectF(m_pixmapPos, QSizeF(m_scaledPixmapSize))); +#endif + } else { + painter->drawPixmap(m_pixmapPos, pixmap); + } +} + +void KStandardItemListWidget::drawSiblingsInformation(QPainter* painter) +{ + const int siblingSize = size().height(); + const int x = (m_expansionArea.left() + m_expansionArea.right() - siblingSize) / 2; + QRect siblingRect(x, 0, siblingSize, siblingSize); + + QStyleOption option; + option.palette.setColor(QPalette::Text, option.palette.color(normalTextColorRole())); + bool isItemSibling = true; + + const QBitArray siblings = siblingsInformation(); + for (int i = siblings.count() - 1; i >= 0; --i) { + option.rect = siblingRect; + option.state = siblings.at(i) ? QStyle::State_Sibling : QStyle::State_None; + + if (isItemSibling) { + option.state |= QStyle::State_Item; + if (m_isExpandable) { + option.state |= QStyle::State_Children; + } + if (data()["isExpanded"].toBool()) { + option.state |= QStyle::State_Open; + } + isItemSibling = false; + } + + style()->drawPrimitive(QStyle::PE_IndicatorBranch, &option, painter); + + siblingRect.translate(-siblingRect.width(), 0); + } +} + +QRectF KStandardItemListWidget::roleEditingRect(const QByteArray& role) const +{ + const TextInfo* textInfo = m_textInfo.value(role); + if (!textInfo) { + return QRectF(); + } + + QRectF rect; + if (m_layout == IconsLayout) { + const KItemListStyleOption& option = styleOption(); + rect = QRectF(textInfo->pos, textInfo->staticText.size(m_customizedFontMetrics, option.maxTextLines)); + } else { + rect = QRectF(textInfo->pos, textInfo->staticText.size()); + } + + if (m_layout == DetailsLayout) { + rect.setWidth(columnWidth(role) - rect.x()); + } + + return rect; +} + +void KStandardItemListWidget::closeRoleEditor() +{ + disconnect(m_roleEditor, SIGNAL(roleEditingCanceled(QByteArray,QVariant)), + this, SLOT(slotRoleEditingCanceled(QByteArray,QVariant))); + disconnect(m_roleEditor, SIGNAL(roleEditingFinished(QByteArray,QVariant)), + this, SLOT(slotRoleEditingFinished(QByteArray,QVariant))); + + if (m_roleEditor->hasFocus()) { + // If the editing was not ended by a FocusOut event, we have + // to transfer the keyboard focus back to the KItemListContainer. + scene()->views()[0]->parentWidget()->setFocus(); + } + + if (m_oldRoleEditor) { + m_oldRoleEditor->deleteLater(); + } + m_oldRoleEditor = m_roleEditor; + m_roleEditor->hide(); + m_roleEditor = 0; +} + +QPixmap KStandardItemListWidget::pixmapForIcon(const QString& name, const QStringList& overlays, int size) +{ + const QString key = "KStandardItemListWidget:" + name + ":" + overlays.join(":") + ":" + QString::number(size); + QPixmap pixmap; + + if (!QPixmapCache::find(key, pixmap)) { + const KIcon icon(name); + + int requestedSize; + if (size <= KIconLoader::SizeSmall) { + requestedSize = KIconLoader::SizeSmall; + } else if (size <= KIconLoader::SizeSmallMedium) { + requestedSize = KIconLoader::SizeSmallMedium; + } else if (size <= KIconLoader::SizeMedium) { + requestedSize = KIconLoader::SizeMedium; + } else if (size <= KIconLoader::SizeLarge) { + requestedSize = KIconLoader::SizeLarge; + } else if (size <= KIconLoader::SizeHuge) { + requestedSize = KIconLoader::SizeHuge; + } else if (size <= KIconLoader::SizeEnormous) { + requestedSize = KIconLoader::SizeEnormous; + } else if (size <= KIconLoader::SizeEnormous * 2) { + requestedSize = KIconLoader::SizeEnormous * 2; + } else { + requestedSize = size; + } + + pixmap = icon.pixmap(requestedSize, requestedSize); + if (requestedSize != size) { + pixmap = pixmap.scaled(QSize(size, size), Qt::KeepAspectRatio, Qt::SmoothTransformation);; + } + + // Strangely KFileItem::overlays() returns empty string-values, so + // we need to check first whether an overlay must be drawn at all. + // It is more efficient to do it here, as KIconLoader::drawOverlays() + // assumes that an overlay will be drawn and has some additional + // setup time. + foreach (const QString& overlay, overlays) { + if (!overlay.isEmpty()) { + // There is at least one overlay, draw all overlays above m_pixmap + // and cancel the check + KIconLoader::global()->drawOverlays(overlays, pixmap, KIconLoader::Desktop); + break; + } + } + + QPixmapCache::insert(key, pixmap); + } + + return pixmap; +} + +qreal KStandardItemListWidget::columnPadding(const KItemListStyleOption& option) +{ + return option.padding * 6; +} + +#include "moc_kstandarditemlistwidget.cpp" diff --git a/dolphin/src/kitemviews/kstandarditemlistwidget.h b/dolphin/src/kitemviews/kstandarditemlistwidget.h new file mode 100644 index 00000000..5ccd7115 --- /dev/null +++ b/dolphin/src/kitemviews/kstandarditemlistwidget.h @@ -0,0 +1,301 @@ +/*************************************************************************** + * Copyright (C) 2012 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef KSTANDARDITEMLISTWIDGET_H +#define KSTANDARDITEMLISTWIDGET_H + +#include + +#include + +#include +#include +#include + +class KItemListRoleEditor; +class KItemListStyleOption; +class KItemListView; + +class KStaticText { +public: + KStaticText(); + + QString text() const; + void setText(const QString &text); + + void setTextWidth(const qreal textwidth); + + QSizeF size() const; + QSizeF size(const QFontMetricsF &fontmetrics, const int maxlines = -1) const; + + QTextOption textOption() const; + void setTextOption(const QTextOption &textoption); + + void setFont(const QFont &font); + + void paint( + QPainter *painter, const QPointF position, + const QFontMetricsF &fontmetrics, const int maxlines = -1 + ) const; + +private: + QString m_text; + QTextOption m_textoption; + QFont m_font; + qreal m_textwidth; + mutable QRectF brect; + mutable bool mixguard; +}; + +class DOLPHINPRIVATE_EXPORT KStandardItemListWidgetInformant : public KItemListWidgetInformant +{ +public: + KStandardItemListWidgetInformant(); + virtual ~KStandardItemListWidgetInformant(); + + virtual void calculateItemSizeHints(QVector& logicalHeightHints, qreal& logicalWidthHint, const KItemListView* view) const; + + virtual qreal preferredRoleColumnWidth(const QByteArray& role, + int index, + const KItemListView* view) const; +protected: + /** + * @return The value of the "text" role. The default implementation returns + * view->model()->data(index)["text"]. If a derived class can + * prevent the (possibly expensive) construction of the + * QHash returned by KItemModelBase::data(int), + * it can reimplement this function. + */ + virtual QString itemText(int index, const KItemListView* view) const; + + /** + * @return The value of the "isLink" role. The default implementation returns false. + * The derived class should reimplement this function, when information about + * links is available and in usage. + */ + virtual bool itemIsLink(int index, const KItemListView* view) const; + + /** + * @return String representation of the role \a role. The representation of + * a role might depend on other roles, so the values of all roles + * are passed as parameter. + */ + virtual QString roleText(const QByteArray& role, + const QHash& values) const; + + /** + * @return A font based on baseFont which is customized for symlinks. + */ + virtual QFont customizedFontForLinks(const QFont& baseFont) const; + + void calculateIconsLayoutItemSizeHints(QVector& logicalHeightHints, qreal& logicalWidthHint, const KItemListView* view) const; + void calculateCompactLayoutItemSizeHints(QVector& logicalHeightHints, qreal& logicalWidthHint, const KItemListView* view) const; + void calculateDetailsLayoutItemSizeHints(QVector& logicalHeightHints, qreal& logicalWidthHint, const KItemListView* view) const; + + friend class KStandardItemListWidget; // Accesses roleText() +}; + +/** + * @brief Itemlist widget implementation for KStandardItemView and KStandardItemModel. + */ +class DOLPHINPRIVATE_EXPORT KStandardItemListWidget : public KItemListWidget +{ + Q_OBJECT + +public: + enum Layout + { + IconsLayout, + CompactLayout, + DetailsLayout + }; + + KStandardItemListWidget(KItemListWidgetInformant* informant, QGraphicsItem* parent); + virtual ~KStandardItemListWidget(); + + void setLayout(Layout layout); + Layout layout() const; + + void setSupportsItemExpanding(bool supportsItemExpanding); + bool supportsItemExpanding() const; + + virtual void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget = 0); + + virtual QRectF iconRect() const; + virtual QRectF textRect() const; + virtual QRectF textFocusRect() const; + virtual QRectF selectionRect() const; + virtual QRectF expansionToggleRect() const; + virtual QRectF selectionToggleRect() const; + virtual QPixmap createDragPixmap(const QStyleOptionGraphicsItem* option, QWidget* widget = 0); + + static KItemListWidgetInformant* createInformant(); + +protected: + /** + * Invalidates the cache which results in calling KStandardItemListWidget::refreshCache() as + * soon as the item need to gets repainted. + */ + void invalidateCache(); + + /** + * Is called if the cache got invalidated by KStandardItemListWidget::invalidateCache(). + * The default implementation is empty. + */ + virtual void refreshCache(); + + /** + * @return True if the give role should be right aligned when showing it inside a column. + * Per default false is returned. + */ + virtual bool isRoleRightAligned(const QByteArray& role) const; + + /** + * @return True if the item should be visually marked as hidden item. Per default + * false is returned. + */ + virtual bool isHidden() const; + + /** + * @return A font based on baseFont which is customized according to the data shown in the widget. + */ + virtual QFont customizedFont(const QFont& baseFont) const; + + virtual QPalette::ColorRole normalTextColorRole() const; + + void setTextColor(const QColor& color); + QColor textColor() const; + + void setOverlay(const QPixmap& overlay); + QPixmap overlay() const; + + /** + * @see KStandardItemListWidgetInformant::roleText(). + */ + QString roleText(const QByteArray& role, const QHash& values) const; + + /** + * Fixes: + * Select the text without MIME-type extension + * This is file-item-specific and should be moved + * into KFileItemListWidget. + * + * Inherited classes can define, if the MIME-type extension + * should be selected or not. + * + * @return Selection length (with or without MIME-type extension) + */ + virtual int selectionLength(const QString& text) const; + + virtual void dataChanged(const QHash& current, const QSet& roles = QSet()); + virtual void visibleRolesChanged(const QList& current, const QList& previous); + virtual void columnWidthChanged(const QByteArray& role, qreal current, qreal previous); + virtual void styleOptionChanged(const KItemListStyleOption& current, const KItemListStyleOption& previous); + virtual void hoveredChanged(bool hovered); + virtual void selectedChanged(bool selected); + virtual void siblingsInformationChanged(const QBitArray& current, const QBitArray& previous); + virtual void editedRoleChanged(const QByteArray& current, const QByteArray& previous); + virtual void resizeEvent(QGraphicsSceneResizeEvent* event); + virtual void showEvent(QShowEvent* event); + virtual void hideEvent(QHideEvent* event); + +private slots: + void slotCutItemsChanged(); + void slotRoleEditingCanceled(const QByteArray& role, const QVariant& value); + void slotRoleEditingFinished(const QByteArray& role, const QVariant& value); + +private: + void triggerCacheRefreshing(); + void updateExpansionArea(); + void updatePixmapCache(); + + void updateTextsCache(); + void updateIconsLayoutTextCache(); + void updateCompactLayoutTextCache(); + void updateDetailsLayoutTextCache(); + + void updateAdditionalInfoTextColor(); + + void drawPixmap(QPainter* painter, const QPixmap& pixmap); + void drawSiblingsInformation(QPainter* painter); + + QRectF roleEditingRect(const QByteArray &role) const; + + /** + * Closes the role editor and returns the focus back + * to the KItemListContainer. + */ + void closeRoleEditor(); + + static QPixmap pixmapForIcon(const QString& name, const QStringList& overlays, int size); + + /** + * @return Horizontal padding in pixels that is added to the required width of + * a column to display the content. + */ + static qreal columnPadding(const KItemListStyleOption& option); + +private: + bool m_isCut; + bool m_isHidden; + QFont m_customizedFont; + QFontMetrics m_customizedFontMetrics; + bool m_isExpandable; + bool m_supportsItemExpanding; + + bool m_dirtyLayout; + bool m_dirtyContent; + QSet m_dirtyContentRoles; + + Layout m_layout; + QPointF m_pixmapPos; + QPixmap m_pixmap; + QSize m_scaledPixmapSize; + + QRectF m_iconRect; // Cache for KItemListWidget::iconRect() + QPixmap m_hoverPixmap; // Cache for modified m_pixmap when hovering the item + + struct TextInfo + { + QPointF pos; + KStaticText staticText; + }; + QHash m_textInfo; + + QRectF m_textRect; + + QList m_sortedVisibleRoles; + + QRectF m_expansionArea; + + QColor m_customTextColor; + QColor m_additionalInfoTextColor; + + QPixmap m_overlay; + + KItemListRoleEditor* m_roleEditor; + KItemListRoleEditor* m_oldRoleEditor; + + friend class KStandardItemListWidgetInformant; // Accesses private static methods to be able to + // share a common layout calculation +}; + +#endif + + diff --git a/dolphin/src/kitemviews/kstandarditemmodel.cpp b/dolphin/src/kitemviews/kstandarditemmodel.cpp new file mode 100644 index 00000000..f80bbabf --- /dev/null +++ b/dolphin/src/kitemviews/kstandarditemmodel.cpp @@ -0,0 +1,241 @@ +/*************************************************************************** + * Copyright (C) 2012 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "kstandarditemmodel.h" + +#include +#include "kstandarditem.h" + +KStandardItemModel::KStandardItemModel(QObject* parent) : + KItemModelBase(parent), + m_items(), + m_indexesForItems() +{ +} + +KStandardItemModel::~KStandardItemModel() +{ + qDeleteAll(m_items); + m_items.clear(); + m_indexesForItems.clear(); +} + +void KStandardItemModel::insertItem(int index, KStandardItem* item) +{ + if (index < 0 || index > count() || !item) { + delete item; + return; + } + + if (!m_indexesForItems.contains(item)) { + item->m_model = this; + m_items.insert(index, item); + m_indexesForItems.insert(item, index); + + // Inserting an item requires to update the indexes + // afterwards from m_indexesForItems. + for (int i = index + 1; i < m_items.count(); ++i) { + m_indexesForItems.insert(m_items[i], i); + } + + // TODO: no hierarchical items are handled yet + + onItemInserted(index); + emit itemsInserted(KItemRangeList() << KItemRange(index, 1)); + } +} + +void KStandardItemModel::changeItem(int index, KStandardItem* item) +{ + if (index < 0 || index >= count() || !item) { + delete item; + return; + } + + item->m_model = this; + + QSet changedRoles; + + KStandardItem* oldItem = m_items[index]; + const QHash oldData = oldItem->data(); + const QHash newData = item->data(); + + // Determine which roles have been changed + QHashIterator it(oldData); + while (it.hasNext()) { + it.next(); + const QByteArray role = it.key(); + const QVariant oldValue = it.value(); + if (newData.contains(role) && newData.value(role) != oldValue) { + changedRoles.insert(role); + } + } + + m_indexesForItems.remove(oldItem); + delete oldItem; + oldItem = 0; + + m_items[index] = item; + m_indexesForItems.insert(item, index); + + onItemChanged(index, changedRoles); + emit itemsChanged(KItemRangeList() << KItemRange(index, 1), changedRoles); +} + +void KStandardItemModel::removeItem(int index) +{ + if (index >= 0 && index < count()) { + KStandardItem* item = m_items[index]; + m_indexesForItems.remove(item); + m_items.removeAt(index); + + // Removing an item requires to update the indexes + // afterwards from m_indexesForItems. + for (int i = index; i < m_items.count(); ++i) { + m_indexesForItems.insert(m_items[i], i); + } + + onItemRemoved(index, item); + + delete item; + item = 0; + + emit itemsRemoved(KItemRangeList() << KItemRange(index, 1)); + + // TODO: no hierarchical items are handled yet + } +} + +void KStandardItemModel::clear() +{ + int size = m_items.size(); + m_items.clear(); + m_indexesForItems.clear(); + + emit itemsRemoved(KItemRangeList() << KItemRange(0, size)); +} + +KStandardItem* KStandardItemModel::item(int index) const +{ + if (index < 0 || index >= m_items.count()) { + return 0; + } + return m_items[index]; +} + +int KStandardItemModel::index(const KStandardItem* item) const +{ + return m_indexesForItems.value(item, -1); +} + +void KStandardItemModel::appendItem(KStandardItem *item) +{ + insertItem(m_items.count(), item); +} + +int KStandardItemModel::count() const +{ + return m_items.count(); +} + +QHash KStandardItemModel::data(int index) const +{ + if (index >= 0 && index < count()) { + const KStandardItem* item = m_items[index]; + if (item) { + return item->data(); + } + } + return QHash(); +} + +bool KStandardItemModel::setData(int index, const QHash& values) +{ + Q_UNUSED(values); + if (index < 0 || index >= count()) { + return false; + } + + return true; +} + +QMimeData* KStandardItemModel::createMimeData(const KItemSet& indexes) const +{ + Q_UNUSED(indexes); + return 0; +} + +int KStandardItemModel::indexForKeyboardSearch(const QString& text, int startFromIndex) const +{ + Q_UNUSED(text); + Q_UNUSED(startFromIndex); + return -1; +} + +bool KStandardItemModel::supportsDropping(int index) const +{ + Q_UNUSED(index); + return false; +} + +QString KStandardItemModel::roleDescription(const QByteArray& role) const +{ + Q_UNUSED(role); + return QString(); +} + +QList > KStandardItemModel::groups() const +{ + QList > groups; + + const QByteArray role = sortRole().isEmpty() ? "group" : sortRole(); + bool isFirstGroupValue = true; + QString groupValue; + const int maxIndex = count() - 1; + for (int i = 0; i <= maxIndex; ++i) { + const QString newGroupValue = m_items.at(i)->dataValue(role).toString(); + if (newGroupValue != groupValue || isFirstGroupValue) { + groupValue = newGroupValue; + groups.append(QPair(i, newGroupValue)); + isFirstGroupValue = false; + } + } + + return groups; +} + +void KStandardItemModel::onItemInserted(int index) +{ + Q_UNUSED(index); +} + +void KStandardItemModel::onItemChanged(int index, const QSet& changedRoles) +{ + Q_UNUSED(index); + Q_UNUSED(changedRoles); +} + +void KStandardItemModel::onItemRemoved(int index, KStandardItem* removedItem) +{ + Q_UNUSED(index); + Q_UNUSED(removedItem); +} + + +#include "moc_kstandarditemmodel.cpp" diff --git a/dolphin/src/kitemviews/kstandarditemmodel.h b/dolphin/src/kitemviews/kstandarditemmodel.h new file mode 100644 index 00000000..a4fab866 --- /dev/null +++ b/dolphin/src/kitemviews/kstandarditemmodel.h @@ -0,0 +1,113 @@ +/*************************************************************************** + * Copyright (C) 2012 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef KSTANDARDITEMMODEL_H +#define KSTANDARDITEMMODEL_H + +#include +#include +#include +#include + +class KStandardItem; + +/** + * @brief Model counterpart for KStandardItemView. + * + * Allows to add items to the model in an easy way by the + * class KStandardItem. + * + * @see KStandardItem + */ +class DOLPHINPRIVATE_EXPORT KStandardItemModel : public KItemModelBase +{ + Q_OBJECT + +public: + explicit KStandardItemModel(QObject* parent = 0); + virtual ~KStandardItemModel(); + + /** + * Inserts the item \a item at the index \a index. If the index + * is equal to the number of items of the model, the item + * gets appended as last element. KStandardItemModel takes + * the ownership of the item. If the index is invalid, the item + * gets deleted. + */ + void insertItem(int index, KStandardItem* item); + + /** + * Changes the item on the index \a index to \a item. + * KStandardItemModel takes the ownership of the item. The + * old item gets deleted. If the index is invalid, the item + * gets deleted. + */ + void changeItem(int index, KStandardItem* item); + + void removeItem(int index); + KStandardItem* item(int index) const; + int index(const KStandardItem* item) const; + + /** + * Convenience method for insertItem(count(), item). + */ + void appendItem(KStandardItem* item); + + virtual int count() const; + virtual QHash data(int index) const; + virtual bool setData(int index, const QHash& values); + virtual QMimeData* createMimeData(const KItemSet& indexes) const; + virtual int indexForKeyboardSearch(const QString& text, int startFromIndex = 0) const; + virtual bool supportsDropping(int index) const; + virtual QString roleDescription(const QByteArray& role) const; + virtual QList > groups() const; + + virtual void clear(); +protected: + /** + * Is invoked after an item has been inserted and before the signal + * itemsInserted() gets emitted. + */ + virtual void onItemInserted(int index); + + /** + * Is invoked after an item or one of its roles has been changed and + * before the signal itemsChanged() gets emitted. + */ + virtual void onItemChanged(int index, const QSet& changedRoles); + + /** + * Is invoked after an item has been removed and before the signal + * itemsRemoved() gets emitted. The item \a removedItem has already + * been removed from the model and will get deleted after the + * execution of onItemRemoved(). + */ + virtual void onItemRemoved(int index, KStandardItem* removedItem); + +private: + QList m_items; + QHash m_indexesForItems; + + friend class KStandardItem; + friend class KStandardItemModelTest; // For unit testing +}; + +#endif + + diff --git a/dolphin/src/kitemviews/private/kdirectorycontentscounter.cpp b/dolphin/src/kitemviews/private/kdirectorycontentscounter.cpp new file mode 100644 index 00000000..7d1e7699 --- /dev/null +++ b/dolphin/src/kitemviews/private/kdirectorycontentscounter.cpp @@ -0,0 +1,184 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz * + * Copyright (C) 2013 by Frank Reininghaus * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "kdirectorycontentscounter.h" + +#include "kdirectorycontentscounterworker.h" +#include + +#include +#include + +KDirectoryContentsCounter::KDirectoryContentsCounter(KFileItemModel* model, QObject* parent) : + QObject(parent), + m_model(model), + m_queue(), + m_worker(0), + m_workerIsBusy(false), + m_dirWatcher(0), + m_watchedDirs() +{ + connect(m_model, SIGNAL(itemsRemoved(KItemRangeList)), + this, SLOT(slotItemsRemoved())); + + if (!m_workerThread) { + m_workerThread = new QThread(); + m_workerThread->start(); + } + + m_worker = new KDirectoryContentsCounterWorker(); + m_worker->moveToThread(m_workerThread); + ++m_workersCount; + + connect(this, SIGNAL(requestDirectoryContentsCount(QString,KDirectoryContentsCounterWorker::Options)), + m_worker, SLOT(countDirectoryContents(QString,KDirectoryContentsCounterWorker::Options))); + connect(m_worker, SIGNAL(result(QString,int)), + this, SLOT(slotResult(QString,int))); + + m_dirWatcher = new KDirWatch(this); + connect(m_dirWatcher, SIGNAL(dirty(QString)), this, SLOT(slotDirWatchDirty(QString))); +} + +KDirectoryContentsCounter::~KDirectoryContentsCounter() +{ + --m_workersCount; + + if (m_workersCount > 0) { + // The worker thread will continue running. It could even be running + // a method of m_worker at the moment, so we delete it using + // deleteLater() to prevent a crash. + m_worker->deleteLater(); + } else { + // There are no remaining workers -> stop the worker thread. + m_workerThread->quit(); + m_workerThread->wait(); + delete m_workerThread; + m_workerThread = 0; + + // The worker thread has finished running now, so it's safe to delete + // m_worker. deleteLater() would not work at all because the event loop + // which would deliver the event to m_worker is not running any more. + delete m_worker; + } +} + +void KDirectoryContentsCounter::addDirectory(const QString& path) +{ + startWorker(path); +} + +int KDirectoryContentsCounter::countDirectoryContentsSynchronously(const QString& path) +{ + if (!m_dirWatcher->contains(path)) { + m_dirWatcher->addDir(path); + m_watchedDirs.insert(path); + } + + KDirectoryContentsCounterWorker::Options options; + + if (m_model->showHiddenFiles()) { + options |= KDirectoryContentsCounterWorker::CountHiddenFiles; + } + + if (m_model->showDirectoriesOnly()) { + options |= KDirectoryContentsCounterWorker::CountDirectoriesOnly; + } + + return KDirectoryContentsCounterWorker::subItemsCount(path, options); +} + +void KDirectoryContentsCounter::slotResult(const QString& path, int count) +{ + m_workerIsBusy = false; + + if (!m_dirWatcher->contains(path)) { + m_dirWatcher->addDir(path); + m_watchedDirs.insert(path); + } + + if (!m_queue.isEmpty()) { + startWorker(m_queue.dequeue()); + } + + emit result(path, count); +} + +void KDirectoryContentsCounter::slotDirWatchDirty(const QString& path) +{ + const int index = m_model->index(KUrl(path)); + if (index >= 0) { + if (!m_model->fileItem(index).isDir()) { + // If INotify is used, KDirWatch issues the dirty() signal + // also for changed files inside the directory, even if we + // don't enable this behavior explicitly (see bug 309740). + return; + } + + startWorker(path); + } +} + +void KDirectoryContentsCounter::slotItemsRemoved() +{ + const bool allItemsRemoved = (m_model->count() == 0); + + if (!m_watchedDirs.isEmpty()) { + // Don't let KDirWatch watch for removed items + if (allItemsRemoved) { + foreach (const QString& path, m_watchedDirs) { + m_dirWatcher->removeDir(path); + } + m_watchedDirs.clear(); + m_queue.clear(); + } else { + QMutableSetIterator it(m_watchedDirs); + while (it.hasNext()) { + const QString& path = it.next(); + if (m_model->index(KUrl(path)) < 0) { + m_dirWatcher->removeDir(path); + it.remove(); + } + } + } + } +} + +void KDirectoryContentsCounter::startWorker(const QString& path) +{ + if (m_workerIsBusy) { + m_queue.enqueue(path); + } else { + KDirectoryContentsCounterWorker::Options options; + + if (m_model->showHiddenFiles()) { + options |= KDirectoryContentsCounterWorker::CountHiddenFiles; + } + + if (m_model->showDirectoriesOnly()) { + options |= KDirectoryContentsCounterWorker::CountDirectoriesOnly; + } + + emit requestDirectoryContentsCount(path, options); + m_workerIsBusy = true; + } +} + +QThread* KDirectoryContentsCounter::m_workerThread = 0; +int KDirectoryContentsCounter::m_workersCount = 0; \ No newline at end of file diff --git a/dolphin/src/kitemviews/private/kdirectorycontentscounter.h b/dolphin/src/kitemviews/private/kdirectorycontentscounter.h new file mode 100644 index 00000000..53c0ab7c --- /dev/null +++ b/dolphin/src/kitemviews/private/kdirectorycontentscounter.h @@ -0,0 +1,92 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz * + * Copyright (C) 2013 by Frank Reininghaus * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef KDIRECTORYCONTENTSCOUNTER_H +#define KDIRECTORYCONTENTSCOUNTER_H + +#include "kdirectorycontentscounterworker.h" + +#include +#include + +class KDirWatch; +class KFileItemModel; +#include + +class KDirectoryContentsCounter : public QObject +{ + Q_OBJECT + +public: + explicit KDirectoryContentsCounter(KFileItemModel* model, QObject* parent = 0); + ~KDirectoryContentsCounter(); + + /** + * Requests the number of items inside the directory \a path. The actual + * counting is done asynchronously, and the result is announced via the + * signal \a result. + * + * The directory \a path is watched for changes, and the signal is emitted + * again if a change occurs. + */ + void addDirectory(const QString& path); + + /** + * In contrast to \a addDirectory, this function counts the items inside + * the directory \a path synchronously and returns the result. + * + * The directory is watched for changes, and the signal \a result is + * emitted if a change occurs. + */ + int countDirectoryContentsSynchronously(const QString& path); + +signals: + /** + * Signals that the directory \a path contains \a count items. + */ + void result(const QString& path, int count); + + void requestDirectoryContentsCount(const QString& path, KDirectoryContentsCounterWorker::Options options); + +private slots: + void slotResult(const QString& path, int count); + void slotDirWatchDirty(const QString& path); + void slotItemsRemoved(); + +private: + void startWorker(const QString& path); + +private: + KFileItemModel* m_model; + + QQueue m_queue; + + static QThread* m_workerThread; + static int m_workersCount; + + KDirectoryContentsCounterWorker* m_worker; + bool m_workerIsBusy; + + KDirWatch* m_dirWatcher; + QSet m_watchedDirs; // Required as sadly KDirWatch does not offer a getter method + // to get all watched directories. +}; + +#endif diff --git a/dolphin/src/kitemviews/private/kdirectorycontentscounterworker.cpp b/dolphin/src/kitemviews/private/kdirectorycontentscounterworker.cpp new file mode 100644 index 00000000..16120da3 --- /dev/null +++ b/dolphin/src/kitemviews/private/kdirectorycontentscounterworker.cpp @@ -0,0 +1,93 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz * + * Copyright (C) 2013 by Frank Reininghaus * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "config-dolphin.h" +#include "kdirectorycontentscounterworker.h" + +// Required includes for subItemsCount(): +#include +#include +#include + +KDirectoryContentsCounterWorker::KDirectoryContentsCounterWorker(QObject* parent) : + QObject(parent) +{ + qRegisterMetaType(); +} + +int KDirectoryContentsCounterWorker::subItemsCount(const QString& path, Options options) +{ + const bool countHiddenFiles = options & CountHiddenFiles; + const bool countDirectoriesOnly = options & CountDirectoriesOnly; + + // Taken from kdelibs/kio/kio/kdirmodel.cpp + // Copyright (C) 2006 David Faure + + int count = -1; + DIR* dir = ::opendir(QFile::encodeName(path)); + if (dir) { // krazy:exclude=syscalls + count = 0; + struct dirent *dirEntry = 0; + while ((dirEntry = ::readdir(dir))) { + if (dirEntry->d_name[0] == '.') { + if (dirEntry->d_name[1] == '\0' || !countHiddenFiles) { + // Skip "." or hidden files + continue; + } + if (dirEntry->d_name[1] == '.' && dirEntry->d_name[2] == '\0') { + // Skip ".." + continue; + } + } + + // If only directories are counted, consider an unknown file type and links also + // as directory instead of trying to do an expensive stat() + // (see bugs 292642 and 299997). +#if defined(HAVE_DIRENT_D_TYPE) + const bool countEntry = !countDirectoriesOnly || + dirEntry->d_type == DT_DIR || + dirEntry->d_type == DT_LNK || + dirEntry->d_type == DT_UNKNOWN; +#else + QByteArray fullpath = QFile::encodeName(path); + fullpath += dirEntry->d_name; + + KDE_struct_stat statbuf; + if (KDE_lstat(fullpath.constData(), &statbuf) == -1) { + continue; + } + + const bool countEntry = !countDirectoriesOnly || + S_ISDIR(statbuf.st_mode) || + S_ISLNK(statbuf.st_mode); +#endif + if (countEntry) { + ++count; + } + } + ::closedir(dir); + } + return count; +} + +void KDirectoryContentsCounterWorker::countDirectoryContents(const QString& path, Options options) +{ + emit result(path, subItemsCount(path, options)); +} diff --git a/dolphin/src/kitemviews/private/kdirectorycontentscounterworker.h b/dolphin/src/kitemviews/private/kdirectorycontentscounterworker.h new file mode 100644 index 00000000..c7846406 --- /dev/null +++ b/dolphin/src/kitemviews/private/kdirectorycontentscounterworker.h @@ -0,0 +1,71 @@ +/*************************************************************************** + * Copyright (C) 2013 by Frank Reininghaus * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef KDIRECTORYCONTENTENTSCOUNTERWORKER_H +#define KDIRECTORYCONTENTENTSCOUNTERWORKER_H + +#include +#include +#include + +#include + +class KDirectoryContentsCounterWorker : public QObject +{ + Q_OBJECT + +public: + enum Option { + NoOptions = 0x0, + CountHiddenFiles = 0x1, + CountDirectoriesOnly = 0x2 + }; + Q_DECLARE_FLAGS(Options, Option) + + explicit KDirectoryContentsCounterWorker(QObject* parent = 0); + + /** + * Counts the items inside the directory \a path using the options + * \a options. + * + * @return The number of items. + */ + static int subItemsCount(const QString& path, Options options); + +signals: + /** + * Signals that the directory \a path contains \a count items. + */ + void result(const QString& path, int count); + +public slots: + /** + * Requests the number of items inside the directory \a path using the + * options \a options. The result is announced via the signal \a result. + */ + // Note that the full type name KDirectoryContentsCounterWorker::Options + // is needed here. Just using 'Options' is OK for the compiler, but + // confuses moc. + void countDirectoryContents(const QString& path, KDirectoryContentsCounterWorker::Options options); +}; + +Q_DECLARE_METATYPE(KDirectoryContentsCounterWorker::Options) +Q_DECLARE_OPERATORS_FOR_FLAGS(KDirectoryContentsCounterWorker::Options) + +#endif diff --git a/dolphin/src/kitemviews/private/kfileitemclipboard.cpp b/dolphin/src/kitemviews/private/kfileitemclipboard.cpp new file mode 100644 index 00000000..421ac875 --- /dev/null +++ b/dolphin/src/kitemviews/private/kfileitemclipboard.cpp @@ -0,0 +1,86 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "kfileitemclipboard.h" + +#include +#include +#include +#include + +class KFileItemClipboardSingleton +{ +public: + KFileItemClipboard instance; +}; +K_GLOBAL_STATIC(KFileItemClipboardSingleton, s_KFileItemClipboard) + + + +KFileItemClipboard* KFileItemClipboard::instance() +{ + return &s_KFileItemClipboard->instance; +} + +bool KFileItemClipboard::isCut(const KUrl& url) const +{ + return m_cutItems.contains(url); +} + +QList KFileItemClipboard::cutItems() const +{ + return m_cutItems.toList(); +} + +KFileItemClipboard::~KFileItemClipboard() +{ +} + +void KFileItemClipboard::updateCutItems() +{ + const QMimeData* mimeData = QApplication::clipboard()->mimeData(); + + // mimeData can be 0 according to https://bugs.kde.org/show_bug.cgi?id=335053 + if (!mimeData) { + m_cutItems.clear(); + emit cutItemsChanged(); + return; + } + + const QByteArray data = mimeData->data("application/x-kde-cutselection"); + const bool isCutSelection = (!data.isEmpty() && data.at(0) == QLatin1Char('1')); + if (isCutSelection) { + m_cutItems = KUrl::List::fromMimeData(mimeData).toSet(); + } else { + m_cutItems.clear(); + } + emit cutItemsChanged(); +} + +KFileItemClipboard::KFileItemClipboard() : + QObject(0), + m_cutItems() +{ + updateCutItems(); + + connect(QApplication::clipboard(), SIGNAL(dataChanged()), + this, SLOT(updateCutItems())); +} + +#include "moc_kfileitemclipboard.cpp" diff --git a/dolphin/src/kitemviews/private/kfileitemclipboard.h b/dolphin/src/kitemviews/private/kfileitemclipboard.h new file mode 100644 index 00000000..f89e739d --- /dev/null +++ b/dolphin/src/kitemviews/private/kfileitemclipboard.h @@ -0,0 +1,62 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef KFILEITEMCLIPBOARD_H +#define KFILEITEMCLIPBOARD_H + +#include +#include +#include +#include + +#include "dolphinprivate_export.h" + +/** + * @brief Wrapper for QClipboard to provide fast access for checking + * whether a KFileItem has been clipped. + */ +class DOLPHINPRIVATE_EXPORT KFileItemClipboard : public QObject +{ + Q_OBJECT + +public: + static KFileItemClipboard* instance(); + + bool isCut(const KUrl& url) const; + + QList cutItems() const; + +signals: + void cutItemsChanged(); + +protected: + virtual ~KFileItemClipboard(); + +private slots: + void updateCutItems(); + +private: + KFileItemClipboard(); + + QSet m_cutItems; + + friend class KFileItemClipboardSingleton; +}; + +#endif diff --git a/dolphin/src/kitemviews/private/kfileitemmodeldirlister.cpp b/dolphin/src/kitemviews/private/kfileitemmodeldirlister.cpp new file mode 100644 index 00000000..d3698d19 --- /dev/null +++ b/dolphin/src/kitemviews/private/kfileitemmodeldirlister.cpp @@ -0,0 +1,48 @@ +/*************************************************************************** + * Copyright (C) 2006-2012 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "kfileitemmodeldirlister.h" +#include +#include + +KFileItemModelDirLister::KFileItemModelDirLister(QObject* parent) : + KDirLister(parent) +{ + setAutoErrorHandlingEnabled(false, 0); +} + +KFileItemModelDirLister::~KFileItemModelDirLister() +{ +} + +void KFileItemModelDirLister::handleError(KIO::Job* job) +{ + if (job->error() == KIO::ERR_IS_FILE) { + emit urlIsFileError(url()); + } else { + const QString errorString = job->errorString(); + if (errorString.isEmpty()) { + emit errorMessage(i18nc("@info:status", "Unknown error.")); + } else { + emit errorMessage(errorString); + } + } +} + +#include "moc_kfileitemmodeldirlister.cpp" diff --git a/dolphin/src/kitemviews/private/kfileitemmodeldirlister.h b/dolphin/src/kitemviews/private/kfileitemmodeldirlister.h new file mode 100644 index 00000000..fb030abc --- /dev/null +++ b/dolphin/src/kitemviews/private/kfileitemmodeldirlister.h @@ -0,0 +1,53 @@ +/*************************************************************************** + * Copyright (C) 2006-2012 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef KFILEITEMMODELDIRLISTER_H +#define KFILEITEMMODELDIRLISTER_H + +#include +#include + +/** + * @brief Extends the class KDirLister by emitting a signal when an + * error occurred instead of showing an error dialog. + * KDirLister::autoErrorHandlingEnabled() is set to false. + */ +class DOLPHINPRIVATE_EXPORT KFileItemModelDirLister : public KDirLister +{ + Q_OBJECT + +public: + KFileItemModelDirLister(QObject* parent = 0); + virtual ~KFileItemModelDirLister(); + +signals: + /** Is emitted whenever an error has occurred. */ + void errorMessage(const QString& msg); + + /** + * Is emitted when the URL of the directory lister represents a file. + * In this case no signal errorMessage() will be emitted. + */ + void urlIsFileError(const KUrl& url); + +protected: + virtual void handleError(KIO::Job* job); +}; + +#endif diff --git a/dolphin/src/kitemviews/private/kfileitemmodelfilter.cpp b/dolphin/src/kitemviews/private/kfileitemmodelfilter.cpp new file mode 100644 index 00000000..2e320f2d --- /dev/null +++ b/dolphin/src/kitemviews/private/kfileitemmodelfilter.cpp @@ -0,0 +1,122 @@ +/*************************************************************************** + * Copyright (C) 2011 by Janardhan Reddy * + * * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "kfileitemmodelfilter.h" + +#include +#include + + +KFileItemModelFilter::KFileItemModelFilter() : + m_useRegExp(false), + m_regExp(0), + m_lowerCasePattern(), + m_pattern() +{ +} + +KFileItemModelFilter::~KFileItemModelFilter() +{ + delete m_regExp; + m_regExp = 0; +} + +void KFileItemModelFilter::setPattern(const QString& filter) +{ + m_pattern = filter; + m_lowerCasePattern = filter.toLower(); + + m_useRegExp = filter.contains('*') || + filter.contains('?') || + filter.contains('['); + if (m_useRegExp) { + if (!m_regExp) { + m_regExp = new QRegExp(); + m_regExp->setCaseSensitivity(Qt::CaseInsensitive); + m_regExp->setMinimal(false); + m_regExp->setPatternSyntax(QRegExp::WildcardUnix); + } + m_regExp->setPattern(filter); + } +} + +QString KFileItemModelFilter::pattern() const +{ + return m_pattern; +} + +void KFileItemModelFilter::setMimeTypes(const QStringList& types) +{ + m_mimeTypes = types; +} + +QStringList KFileItemModelFilter::mimeTypes() const +{ + return m_mimeTypes; +} + +bool KFileItemModelFilter::hasSetFilters() const +{ + return (!m_pattern.isEmpty() || !m_mimeTypes.isEmpty()); +} + + +bool KFileItemModelFilter::matches(const KFileItem& item) const +{ + const bool hasPatternFilter = !m_pattern.isEmpty(); + const bool hasMimeTypesFilter = !m_mimeTypes.isEmpty(); + + // If no filter is set, return true. + if (!hasPatternFilter && !hasMimeTypesFilter) { + return true; + } + + // If both filters are set, return true when both filters are matched + if (hasPatternFilter && hasMimeTypesFilter) { + return (matchesPattern(item) && matchesType(item)); + } + + // If only one filter is set, return true when that filter is matched + if (hasPatternFilter) { + return matchesPattern(item); + } + + return matchesType(item); +} + +bool KFileItemModelFilter::matchesPattern(const KFileItem& item) const +{ + if (m_useRegExp) { + return m_regExp->exactMatch(item.text()); + } else { + return item.text().toLower().contains(m_lowerCasePattern); + } +} + +bool KFileItemModelFilter::matchesType(const KFileItem& item) const +{ + foreach (const QString& mimeType, m_mimeTypes) { + if (item.mimetype() == mimeType) { + return true; + } + } + + return m_mimeTypes.isEmpty(); +} diff --git a/dolphin/src/kitemviews/private/kfileitemmodelfilter.h b/dolphin/src/kitemviews/private/kfileitemmodelfilter.h new file mode 100644 index 00000000..1d0d0eb4 --- /dev/null +++ b/dolphin/src/kitemviews/private/kfileitemmodelfilter.h @@ -0,0 +1,93 @@ +/*************************************************************************** + * Copyright (C) 2011 by Janardhan Reddy * + * * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef KFILEITEMMODELFILTER_H +#define KFILEITEMMODELFILTER_H + +#include +#include + +class KFileItem; +#include + +/** + * @brief Allows to check whether an item of the KFileItemModel + * matches with a set filter-string. + * + * Currently the filter is only checked for the KFileItem::text() + * property of the KFileItem, but this might get extended in + * future. + */ +class DOLPHINPRIVATE_EXPORT KFileItemModelFilter +{ + +public: + KFileItemModelFilter(); + virtual ~KFileItemModelFilter(); + + /** + * Sets the pattern that is used for a comparison with the item + * in KFileItemModelFilter::matches(). Per default the pattern + * defines a sub-string. As soon as the pattern contains at least + * a '*', '?' or '[' the pattern represents a regular expression. + */ + void setPattern(const QString& pattern); + QString pattern() const; + + /** + * Set the list of mimetypes that are used for comparison with the + * item in KFileItemModelFilter::matchesMimeType. + */ + void setMimeTypes(const QStringList& types); + QStringList mimeTypes() const; + + /** + * @return True if either the pattern or mimetype filters has been set. + */ + bool hasSetFilters() const; + + /** + * @return True if the item matches with the pattern defined by + * @ref setPattern() or @ref setMimeTypes + */ + bool matches(const KFileItem& item) const; + +private: + /** + * @return True if item matches pattern set by @ref setPattern. + */ + bool matchesPattern(const KFileItem& item) const; + + /** + * @return True if item matches mimetypes set by @ref setMimeTypes. + */ + bool matchesType(const KFileItem& item) const; + + bool m_useRegExp; // If true, m_regExp is used for filtering, + // otherwise m_lowerCaseFilter is used. + QRegExp* m_regExp; + QString m_lowerCasePattern; // Lowercase version of m_filter for + // faster comparison in matches(). + QString m_pattern; // Property set by setPattern(). + QStringList m_mimeTypes; // Property set by setMimeTypes() +}; +#endif + + diff --git a/dolphin/src/kitemviews/private/kfileitemmodelsortalgorithm.h b/dolphin/src/kitemviews/private/kfileitemmodelsortalgorithm.h new file mode 100644 index 00000000..a77296f0 --- /dev/null +++ b/dolphin/src/kitemviews/private/kfileitemmodelsortalgorithm.h @@ -0,0 +1,108 @@ +/***************************************************************************** + * Copyright (C) 2012 by Peter Penz * + * Copyright (C) 2012 by Emmanuel Pescosta * + * Copyright (C) 2013 by Frank Reininghaus * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + *****************************************************************************/ + +#ifndef KFILEITEMMODELSORTALGORITHM_H +#define KFILEITEMMODELSORTALGORITHM_H + +#include + +/** + * Sorts the items using the merge sort algorithm is used to assure a + * worst-case of O(n * log(n)) and to keep the number of comparisons low. + * + * The implementation is based on qStableSortHelper() from qalgorithms.h + * Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). + */ + +template +static void mergeSort(RandomAccessIterator begin, + RandomAccessIterator end, + LessThan lessThan) +{ + // The implementation is based on qStableSortHelper() from qalgorithms.h + // Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). + + const int span = end - begin; + if (span < 2) { + return; + } + + const RandomAccessIterator middle = begin + span / 2; + mergeSort(begin, middle, lessThan); + mergeSort(middle, end, lessThan); + merge(begin, middle, end, lessThan); +} + +/** + * Merges the sorted item ranges between \a begin and \a pivot and + * between \a pivot and \a end into a single sorted range between + * \a begin and \a end. + * + * The implementation is based on qMerge() from qalgorithms.h + * Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). + */ + +template +static void merge(RandomAccessIterator begin, + RandomAccessIterator pivot, + RandomAccessIterator end, + LessThan lessThan) +{ + // The implementation is based on qMerge() from qalgorithms.h + // Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). + + const int len1 = pivot - begin; + const int len2 = end - pivot; + + if (len1 == 0 || len2 == 0) { + return; + } + + if (len1 + len2 == 2) { + if (lessThan(*(begin + 1), *(begin))) { + qSwap(*begin, *(begin + 1)); + } + return; + } + + RandomAccessIterator firstCut; + RandomAccessIterator secondCut; + int len2Half; + if (len1 > len2) { + const int len1Half = len1 / 2; + firstCut = begin + len1Half; + secondCut = std::lower_bound(pivot, end, *firstCut, lessThan); + len2Half = secondCut - pivot; + } else { + len2Half = len2 / 2; + secondCut = pivot + len2Half; + firstCut = std::upper_bound(begin, pivot, *secondCut, lessThan); + } + + std::rotate(firstCut, pivot, secondCut); + + RandomAccessIterator newPivot = firstCut + len2Half; + merge(begin, firstCut, newPivot, lessThan); + merge(newPivot, secondCut, end, lessThan); +} + +#endif + diff --git a/dolphin/src/kitemviews/private/kitemlistheaderwidget.cpp b/dolphin/src/kitemviews/private/kitemlistheaderwidget.cpp new file mode 100644 index 00000000..b12111ac --- /dev/null +++ b/dolphin/src/kitemviews/private/kitemlistheaderwidget.cpp @@ -0,0 +1,572 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "kitemlistheaderwidget.h" + +#include +#include +#include + +#include +#include +#include +#include + +#include + +KItemListHeaderWidget::KItemListHeaderWidget(QGraphicsWidget* parent) : + QGraphicsWidget(parent), + m_automaticColumnResizing(true), + m_model(0), + m_offset(0), + m_columns(), + m_columnWidths(), + m_preferredColumnWidths(), + m_hoveredRoleIndex(-1), + m_pressedRoleIndex(-1), + m_roleOperation(NoRoleOperation), + m_pressedMousePos(), + m_movingRole() +{ + m_movingRole.x = 0; + m_movingRole.xDec = 0; + m_movingRole.index = -1; + + setAcceptHoverEvents(true); +} + +KItemListHeaderWidget::~KItemListHeaderWidget() +{ +} + +void KItemListHeaderWidget::setModel(KItemModelBase* model) +{ + if (m_model == model) { + return; + } + + if (m_model) { + disconnect(m_model, SIGNAL(sortRoleChanged(QByteArray,QByteArray)), + this, SLOT(slotSortRoleChanged(QByteArray,QByteArray))); + disconnect(m_model, SIGNAL(sortOrderChanged(Qt::SortOrder,Qt::SortOrder)), + this, SLOT(slotSortOrderChanged(Qt::SortOrder,Qt::SortOrder))); + } + + m_model = model; + + if (m_model) { + connect(m_model, SIGNAL(sortRoleChanged(QByteArray,QByteArray)), + this, SLOT(slotSortRoleChanged(QByteArray,QByteArray))); + connect(m_model, SIGNAL(sortOrderChanged(Qt::SortOrder,Qt::SortOrder)), + this, SLOT(slotSortOrderChanged(Qt::SortOrder,Qt::SortOrder))); + } +} + +KItemModelBase* KItemListHeaderWidget::model() const +{ + return m_model; +} + +void KItemListHeaderWidget::setAutomaticColumnResizing(bool automatic) +{ + m_automaticColumnResizing = automatic; +} + +bool KItemListHeaderWidget::automaticColumnResizing() const +{ + return m_automaticColumnResizing; +} + +void KItemListHeaderWidget::setColumns(const QList& roles) +{ + foreach (const QByteArray& role, roles) { + if (!m_columnWidths.contains(role)) { + m_columnWidths.remove(role); + m_preferredColumnWidths.remove(role); + } + } + + m_columns = roles; + update(); +} + +QList KItemListHeaderWidget::columns() const +{ + return m_columns; +} + +void KItemListHeaderWidget::setColumnWidth(const QByteArray& role, qreal width) +{ + const qreal minWidth = minimumColumnWidth(); + if (width < minWidth) { + width = minWidth; + } + + if (m_columnWidths.value(role) != width) { + m_columnWidths.insert(role, width); + update(); + } +} + +qreal KItemListHeaderWidget::columnWidth(const QByteArray& role) const +{ + return m_columnWidths.value(role); +} + +void KItemListHeaderWidget::setPreferredColumnWidth(const QByteArray& role, qreal width) +{ + m_preferredColumnWidths.insert(role, width); +} + +qreal KItemListHeaderWidget::preferredColumnWidth(const QByteArray& role) const +{ + return m_preferredColumnWidths.value(role); +} + +void KItemListHeaderWidget::setOffset(qreal offset) +{ + if (m_offset != offset) { + m_offset = offset; + update(); + } +} + +qreal KItemListHeaderWidget::offset() const +{ + return m_offset; +} + +qreal KItemListHeaderWidget::minimumColumnWidth() const +{ + QFontMetricsF fontMetrics(font()); + return fontMetrics.height() * 4; +} + +void KItemListHeaderWidget::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) +{ + Q_UNUSED(option); + Q_UNUSED(widget); + + if (!m_model) { + return; + } + + // Draw roles + painter->setFont(font()); + painter->setPen(palette().text().color()); + + qreal x = -m_offset; + int orderIndex = 0; + foreach (const QByteArray& role, m_columns) { + const qreal roleWidth = m_columnWidths.value(role); + const QRectF rect(x, 0, roleWidth, size().height()); + paintRole(painter, role, rect, orderIndex, widget); + x += roleWidth; + ++orderIndex; + } + + if (!m_movingRole.pixmap.isNull()) { + Q_ASSERT(m_roleOperation == MoveRoleOperation); + painter->drawPixmap(m_movingRole.x, 0, m_movingRole.pixmap); + } +} + +void KItemListHeaderWidget::mousePressEvent(QGraphicsSceneMouseEvent* event) +{ + if (event->button() & Qt::LeftButton) { + updatePressedRoleIndex(event->pos()); + m_pressedMousePos = event->pos(); + m_roleOperation = isAboveRoleGrip(m_pressedMousePos, m_pressedRoleIndex) ? + ResizeRoleOperation : NoRoleOperation; + event->accept(); + } else { + event->ignore(); + } +} + +void KItemListHeaderWidget::mouseReleaseEvent(QGraphicsSceneMouseEvent* event) +{ + QGraphicsWidget::mouseReleaseEvent(event); + + if (m_pressedRoleIndex == -1) { + return; + } + + switch (m_roleOperation) { + case NoRoleOperation: { + // Only a click has been done and no moving or resizing has been started + const QByteArray sortRole = m_model->sortRole(); + const int sortRoleIndex = m_columns.indexOf(sortRole); + if (m_pressedRoleIndex == sortRoleIndex) { + // Toggle the sort order + const Qt::SortOrder previous = m_model->sortOrder(); + const Qt::SortOrder current = (m_model->sortOrder() == Qt::AscendingOrder) ? + Qt::DescendingOrder : Qt::AscendingOrder; + m_model->setSortOrder(current); + emit sortOrderChanged(current, previous); + } else { + // Change the sort role and reset to the ascending order + const QByteArray previous = m_model->sortRole(); + const QByteArray current = m_columns[m_pressedRoleIndex]; + m_model->setSortRole(current); + emit sortRoleChanged(current, previous); + + if (m_model->sortOrder() == Qt::DescendingOrder) { + m_model->setSortOrder(Qt::AscendingOrder); + emit sortOrderChanged(Qt::AscendingOrder, Qt::DescendingOrder); + } + } + break; + } + + case MoveRoleOperation: + m_movingRole.pixmap = QPixmap(); + m_movingRole.x = 0; + m_movingRole.xDec = 0; + m_movingRole.index = -1; + break; + + default: + break; + } + + m_pressedRoleIndex = -1; + m_roleOperation = NoRoleOperation; + update(); + + QApplication::restoreOverrideCursor(); +} + +void KItemListHeaderWidget::mouseMoveEvent(QGraphicsSceneMouseEvent* event) +{ + QGraphicsWidget::mouseMoveEvent(event); + + switch (m_roleOperation) { + case NoRoleOperation: + if ((event->pos() - m_pressedMousePos).manhattanLength() >= QApplication::startDragDistance()) { + // A role gets dragged by the user. Create a pixmap of the role that will get + // synchronized on each furter mouse-move-event with the mouse-position. + m_roleOperation = MoveRoleOperation; + const int roleIndex = roleIndexAt(m_pressedMousePos); + m_movingRole.index = roleIndex; + if (roleIndex == 0) { + // TODO: It should be configurable whether moving the first role is allowed. + // In the context of Dolphin this is not required, however this should be + // changed if KItemViews are used in a more generic way. + QApplication::setOverrideCursor(QCursor(Qt::ForbiddenCursor)); + } else { + m_movingRole.pixmap = createRolePixmap(roleIndex); + + qreal roleX = -m_offset; + for (int i = 0; i < roleIndex; ++i) { + const QByteArray role = m_columns[i]; + roleX += m_columnWidths.value(role); + } + + m_movingRole.xDec = event->pos().x() - roleX; + m_movingRole.x = roleX; + update(); + } + } + break; + + case ResizeRoleOperation: { + const QByteArray pressedRole = m_columns[m_pressedRoleIndex]; + + qreal previousWidth = m_columnWidths.value(pressedRole); + qreal currentWidth = previousWidth; + currentWidth += event->pos().x() - event->lastPos().x(); + currentWidth = qMax(minimumColumnWidth(), currentWidth); + + m_columnWidths.insert(pressedRole, currentWidth); + update(); + + emit columnWidthChanged(pressedRole, currentWidth, previousWidth); + break; + } + + case MoveRoleOperation: { + // TODO: It should be configurable whether moving the first role is allowed. + // In the context of Dolphin this is not required, however this should be + // changed if KItemViews are used in a more generic way. + if (m_movingRole.index > 0) { + m_movingRole.x = event->pos().x() - m_movingRole.xDec; + update(); + + const int targetIndex = targetOfMovingRole(); + if (targetIndex > 0 && targetIndex != m_movingRole.index) { + const QByteArray role = m_columns[m_movingRole.index]; + const int previousIndex = m_movingRole.index; + m_movingRole.index = targetIndex; + emit columnMoved(role, targetIndex, previousIndex); + + m_movingRole.xDec = event->pos().x() - roleXPosition(role); + } + } + break; + } + + default: + break; + } +} + +void KItemListHeaderWidget::mouseDoubleClickEvent(QGraphicsSceneMouseEvent* event) +{ + QGraphicsItem::mouseDoubleClickEvent(event); + + const int roleIndex = roleIndexAt(event->pos()); + if (roleIndex >= 0 && isAboveRoleGrip(event->pos(), roleIndex)) { + const QByteArray role = m_columns.at(roleIndex); + + qreal previousWidth = columnWidth(role); + setColumnWidth(role, preferredColumnWidth(role)); + qreal currentWidth = columnWidth(role); + + emit columnWidthChanged(role, currentWidth, previousWidth); + } +} + +void KItemListHeaderWidget::hoverEnterEvent(QGraphicsSceneHoverEvent* event) +{ + QGraphicsWidget::hoverEnterEvent(event); + updateHoveredRoleIndex(event->pos()); +} + +void KItemListHeaderWidget::hoverLeaveEvent(QGraphicsSceneHoverEvent* event) +{ + QGraphicsWidget::hoverLeaveEvent(event); + if (m_hoveredRoleIndex != -1) { + m_hoveredRoleIndex = -1; + update(); + } +} + +void KItemListHeaderWidget::hoverMoveEvent(QGraphicsSceneHoverEvent* event) +{ + QGraphicsWidget::hoverMoveEvent(event); + + const QPointF& pos = event->pos(); + updateHoveredRoleIndex(pos); + if (m_hoveredRoleIndex >= 0 && isAboveRoleGrip(pos, m_hoveredRoleIndex)) { + setCursor(Qt::SplitHCursor); + } else { + unsetCursor(); + } +} + +void KItemListHeaderWidget::slotSortRoleChanged(const QByteArray& current, const QByteArray& previous) +{ + Q_UNUSED(current); + Q_UNUSED(previous); + update(); +} + +void KItemListHeaderWidget::slotSortOrderChanged(Qt::SortOrder current, Qt::SortOrder previous) +{ + Q_UNUSED(current); + Q_UNUSED(previous); + update(); +} + +void KItemListHeaderWidget::paintRole(QPainter* painter, + const QByteArray& role, + const QRectF& rect, + int orderIndex, + QWidget* widget) const +{ + // The following code is based on the code from QHeaderView::paintSection(). + // Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). + QStyleOptionHeader option; + option.section = orderIndex; + option.state = QStyle::State_None | QStyle::State_Raised | QStyle::State_Horizontal; + if (isEnabled()) { + option.state |= QStyle::State_Enabled; + } + if (window() && window()->isActiveWindow()) { + option.state |= QStyle::State_Active; + } + if (m_hoveredRoleIndex == orderIndex) { + option.state |= QStyle::State_MouseOver; + } + if (m_pressedRoleIndex == orderIndex) { + option.state |= QStyle::State_Sunken; + } + if (m_model->sortRole() == role) { + option.sortIndicator = (m_model->sortOrder() == Qt::AscendingOrder) ? + QStyleOptionHeader::SortDown : QStyleOptionHeader::SortUp; + } + option.rect = rect.toRect(); + + bool paintBackgroundForEmptyArea = false; + + if (m_columns.count() == 1) { + option.position = QStyleOptionHeader::OnlyOneSection; + } else if (orderIndex == 0) { + option.position = QStyleOptionHeader::Beginning; + } else if (orderIndex == m_columns.count() - 1) { + // We are just painting the header for the last column. Check if there + // is some empty space to the right which needs to be filled. + if (rect.right() < size().width()) { + option.position = QStyleOptionHeader::Middle; + paintBackgroundForEmptyArea = true; + } else { + option.position = QStyleOptionHeader::End; + } + } else { + option.position = QStyleOptionHeader::Middle; + } + + option.orientation = Qt::Horizontal; + option.selectedPosition = QStyleOptionHeader::NotAdjacent; + option.text = m_model->roleDescription(role); + + style()->drawControl(QStyle::CE_Header, &option, painter, widget); + + if (paintBackgroundForEmptyArea) { + option.state = QStyle::State_None | QStyle::State_Raised | QStyle::State_Horizontal; + option.section = m_columns.count(); + option.sortIndicator = QStyleOptionHeader::None; + + qreal backgroundRectX = rect.x() + rect.width(); + QRectF backgroundRect(backgroundRectX, 0.0, size().width() - backgroundRectX, rect.height()); + option.rect = backgroundRect.toRect(); + option.position = QStyleOptionHeader::End; + option.text = QString(); + + style()->drawControl(QStyle::CE_Header, &option, painter, widget); + } +} + +void KItemListHeaderWidget::updatePressedRoleIndex(const QPointF& pos) +{ + const int pressedIndex = roleIndexAt(pos); + if (m_pressedRoleIndex != pressedIndex) { + m_pressedRoleIndex = pressedIndex; + update(); + } +} + +void KItemListHeaderWidget::updateHoveredRoleIndex(const QPointF& pos) +{ + const int hoverIndex = roleIndexAt(pos); + if (m_hoveredRoleIndex != hoverIndex) { + m_hoveredRoleIndex = hoverIndex; + update(); + } +} + +int KItemListHeaderWidget::roleIndexAt(const QPointF& pos) const +{ + int index = -1; + + qreal x = -m_offset; + foreach (const QByteArray& role, m_columns) { + ++index; + x += m_columnWidths.value(role); + if (pos.x() <= x) { + break; + } + } + + return index; +} + +bool KItemListHeaderWidget::isAboveRoleGrip(const QPointF& pos, int roleIndex) const +{ + qreal x = -m_offset; + for (int i = 0; i <= roleIndex; ++i) { + const QByteArray role = m_columns[i]; + x += m_columnWidths.value(role); + } + + const int grip = style()->pixelMetric(QStyle::PM_HeaderGripMargin); + return pos.x() >= (x - grip) && pos.x() <= x; +} + +QPixmap KItemListHeaderWidget::createRolePixmap(int roleIndex) const +{ + const QByteArray role = m_columns[roleIndex]; + const qreal roleWidth = m_columnWidths.value(role); + const QRect rect(0, 0, roleWidth, size().height()); + + QImage image(rect.size(), QImage::Format_ARGB32_Premultiplied); + + QPainter painter(&image); + paintRole(&painter, role, rect, roleIndex); + + // Apply a highlighting-color + const QPalette::ColorGroup group = isActiveWindow() ? QPalette::Active : QPalette::Inactive; + QColor highlightColor = palette().color(group, QPalette::Highlight); + highlightColor.setAlpha(64); + painter.fillRect(rect, highlightColor); + + // Make the image transparent + painter.setCompositionMode(QPainter::CompositionMode_DestinationIn); + painter.fillRect(0, 0, image.width(), image.height(), QColor(0, 0, 0, 192)); + + return QPixmap::fromImage(image); +} + +int KItemListHeaderWidget::targetOfMovingRole() const +{ + const int movingWidth = m_movingRole.pixmap.width(); + const int movingLeft = m_movingRole.x; + const int movingRight = movingLeft + movingWidth - 1; + + int targetIndex = 0; + qreal targetLeft = -m_offset; + while (targetIndex < m_columns.count()) { + const QByteArray role = m_columns[targetIndex]; + const qreal targetWidth = m_columnWidths.value(role); + const qreal targetRight = targetLeft + targetWidth - 1; + + const bool isInTarget = (targetWidth >= movingWidth && + movingLeft >= targetLeft && + movingRight <= targetRight) || + (targetWidth < movingWidth && + movingLeft <= targetLeft && + movingRight >= targetRight); + + if (isInTarget) { + return targetIndex; + } + + targetLeft += targetWidth; + ++targetIndex; + } + + return m_movingRole.index; +} + +qreal KItemListHeaderWidget::roleXPosition(const QByteArray& role) const +{ + qreal x = -m_offset; + foreach (const QByteArray& visibleRole, m_columns) { + if (visibleRole == role) { + return x; + } + + x += m_columnWidths.value(visibleRole); + } + + return -1; +} + +#include "moc_kitemlistheaderwidget.cpp" diff --git a/dolphin/src/kitemviews/private/kitemlistheaderwidget.h b/dolphin/src/kitemviews/private/kitemlistheaderwidget.h new file mode 100644 index 00000000..4762d3f2 --- /dev/null +++ b/dolphin/src/kitemviews/private/kitemlistheaderwidget.h @@ -0,0 +1,172 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef KITEMLISTHEADERWIDGET_H +#define KITEMLISTHEADERWIDGET_H + +#include +#include +#include +#include + +class KItemModelBase; + +/** + * @brief Widget the implements the header for KItemListView showing the currently used roles. + * + * The widget is an internal API, the user of KItemListView may only access the + * class KItemListHeader. + */ +class DOLPHINPRIVATE_EXPORT KItemListHeaderWidget : public QGraphicsWidget +{ + Q_OBJECT + +public: + KItemListHeaderWidget(QGraphicsWidget* parent = 0); + virtual ~KItemListHeaderWidget(); + + void setModel(KItemModelBase* model); + KItemModelBase* model() const; + + void setAutomaticColumnResizing(bool automatic); + bool automaticColumnResizing() const; + + void setColumns(const QList& roles); + QList columns() const; + + void setColumnWidth(const QByteArray& role, qreal width); + qreal columnWidth(const QByteArray& role) const; + + /** + * Sets the column-width that is required to show the role unclipped. + */ + void setPreferredColumnWidth(const QByteArray& role, qreal width); + qreal preferredColumnWidth(const QByteArray& role) const; + + void setOffset(qreal offset); + qreal offset() const; + + qreal minimumColumnWidth() const; + + virtual void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget = 0); + +signals: + /** + * Is emitted if the width of a visible role has been adjusted by the user with the mouse + * (no signal is emitted if KItemListHeader::setVisibleRoleWidth() is invoked). + */ + void columnWidthChanged(const QByteArray& role, + qreal currentWidth, + qreal previousWidth); + + /** + * Is emitted if the position of the column has been changed. + */ + void columnMoved(const QByteArray& role, int currentIndex, int previousIndex); + + /** + * Is emitted if the user has changed the sort order by clicking on a + * header item. The sort order of the model has already been adjusted to + * the current sort order. Note that no signal will be emitted if the + * sort order of the model has been changed without user interaction. + */ + void sortOrderChanged(Qt::SortOrder current, Qt::SortOrder previous); + + /** + * Is emitted if the user has changed the sort role by clicking on a + * header item. The sort role of the model has already been adjusted to + * the current sort role. Note that no signal will be emitted if the + * sort role of the model has been changed without user interaction. + */ + void sortRoleChanged(const QByteArray& current, const QByteArray& previous); + +protected: + virtual void mousePressEvent(QGraphicsSceneMouseEvent* event); + virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent* event); + virtual void mouseMoveEvent(QGraphicsSceneMouseEvent* event); + virtual void mouseDoubleClickEvent(QGraphicsSceneMouseEvent* event); + virtual void hoverEnterEvent(QGraphicsSceneHoverEvent* event); + virtual void hoverLeaveEvent(QGraphicsSceneHoverEvent* event); + virtual void hoverMoveEvent(QGraphicsSceneHoverEvent* event); + +private slots: + void slotSortRoleChanged(const QByteArray& current, const QByteArray& previous); + void slotSortOrderChanged(Qt::SortOrder current, Qt::SortOrder previous); + +private: + void paintRole(QPainter* painter, + const QByteArray& role, + const QRectF& rect, + int orderIndex, + QWidget* widget = 0) const; + + void updatePressedRoleIndex(const QPointF& pos); + void updateHoveredRoleIndex(const QPointF& pos); + int roleIndexAt(const QPointF& pos) const; + bool isAboveRoleGrip(const QPointF& pos, int roleIndex) const; + + /** + * Creates a pixmap of the role with the index \a roleIndex that is shown + * during moving a role. + */ + QPixmap createRolePixmap(int roleIndex) const; + + /** + * @return Target index of the currently moving visible role based on the current + * state of m_movingRole. + */ + int targetOfMovingRole() const; + + /** + * @return x-position of the left border of the role \a role. + */ + qreal roleXPosition(const QByteArray& role) const; + +private: + enum RoleOperation + { + NoRoleOperation, + ResizeRoleOperation, + MoveRoleOperation + }; + + bool m_automaticColumnResizing; + KItemModelBase* m_model; + qreal m_offset; + QList m_columns; + QHash m_columnWidths; + QHash m_preferredColumnWidths; + + int m_hoveredRoleIndex; + int m_pressedRoleIndex; + RoleOperation m_roleOperation; + QPointF m_pressedMousePos; + + struct MovingRole + { + QPixmap pixmap; + int x; + int xDec; + int index; + } m_movingRole; +}; + +#endif + + diff --git a/dolphin/src/kitemviews/private/kitemlistkeyboardsearchmanager.cpp b/dolphin/src/kitemviews/private/kitemlistkeyboardsearchmanager.cpp new file mode 100644 index 00000000..3bd1b01f --- /dev/null +++ b/dolphin/src/kitemviews/private/kitemlistkeyboardsearchmanager.cpp @@ -0,0 +1,96 @@ +/*************************************************************************** + * Copyright (C) 2011 by Tirtha Chatterjee * + * * + * Based on the Itemviews NG project from Trolltech Labs: * + * http://qt.gitorious.org/qt-labs/itemviews-ng * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "kitemlistkeyboardsearchmanager.h" + +#include +#include + +KItemListKeyboardSearchManager::KItemListKeyboardSearchManager(QObject* parent) : + QObject(parent), + m_timeout(1000) +{ + m_keyboardInputTime.invalidate(); +} + +KItemListKeyboardSearchManager::~KItemListKeyboardSearchManager() +{ +} + +void KItemListKeyboardSearchManager::addKeys(const QString& keys) +{ + const bool keyboardTimeWasValid = m_keyboardInputTime.isValid(); + const qint64 keyboardInputTimeElapsed = m_keyboardInputTime.restart(); + if (keyboardInputTimeElapsed > m_timeout || !keyboardTimeWasValid) { + m_searchedString.clear(); + } + + const bool newSearch = m_searchedString.isEmpty(); + + // Do not start a new search if the user pressed Space. Only add + // it to the search string if a search is in progress already. + if (newSearch && keys == QLatin1String(" ")) { + return; + } + + if (!keys.isEmpty()) { + m_searchedString.append(keys); + + // Special case: + // If the same key is pressed repeatedly, the next item matching that key should be highlighted + const QChar firstKey = m_searchedString.length() > 0 ? m_searchedString.at(0) : QChar(); + const bool sameKey = m_searchedString.length() > 1 && m_searchedString.count(firstKey) == m_searchedString.length(); + + // Searching for a matching item should start from the next item if either + // 1. a new search is started, or + // 2. a 'repeated key' search is done. + const bool searchFromNextItem = newSearch || sameKey; + + emit changeCurrentItem(sameKey ? firstKey : m_searchedString, searchFromNextItem); + } + m_keyboardInputTime.start(); +} + +void KItemListKeyboardSearchManager::setTimeout(qint64 milliseconds) +{ + m_timeout = milliseconds; +} + +qint64 KItemListKeyboardSearchManager::timeout() const +{ + return m_timeout; +} + +void KItemListKeyboardSearchManager::cancelSearch() +{ + m_searchedString.clear(); +} + +void KItemListKeyboardSearchManager::slotCurrentChanged(int current, int previous) +{ + Q_UNUSED(previous); + + if (current < 0) { + // The current item has been removed. We should cancel the search. + cancelSearch(); + } +} diff --git a/dolphin/src/kitemviews/private/kitemlistkeyboardsearchmanager.h b/dolphin/src/kitemviews/private/kitemlistkeyboardsearchmanager.h new file mode 100644 index 00000000..86e6a5ae --- /dev/null +++ b/dolphin/src/kitemviews/private/kitemlistkeyboardsearchmanager.h @@ -0,0 +1,87 @@ +/*************************************************************************** + * Copyright (C) 2011 by Tirtha Chatterjee * + * * + * Based on the Itemviews NG project from Trolltech Labs: * + * http://qt.gitorious.org/qt-labs/itemviews-ng * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef KITEMLISTKEYBOARDSEARCHMANAGER_H +#define KITEMLISTKEYBOARDSEARCHMANAGER_H + +#include + +#include +#include +#include + +/** + * @brief Controls the keyboard searching ability for a KItemListController. + * + * @see KItemListController + * @see KItemModelBase + */ +class DOLPHINPRIVATE_EXPORT KItemListKeyboardSearchManager : public QObject +{ + Q_OBJECT + +public: + + KItemListKeyboardSearchManager(QObject* parent = 0); + virtual ~KItemListKeyboardSearchManager(); + + /** + * Add \a keys to the text buffer used for searching. + */ + void addKeys(const QString& keys); + + /** + * Sets the delay after which the search is cancelled to \a milliseconds. + * If the time interval between two calls of addKeys(const QString&) is + * larger than this, the second call will start a new search, rather than + * combining the keys received from both calls to a single search string. + */ + void setTimeout(qint64 milliseconds); + qint64 timeout() const; + + void cancelSearch(); + +public slots: + + void slotCurrentChanged(int current, int previous); + +signals: + /** + * Is emitted if the current item should be changed corresponding + * to \a text. + * @param searchFromNextItem If true start searching from item next to the + * current item. Otherwise, search from the + * current item. + */ + // TODO: Think about getting rid of the bool parameter + // (see http://doc.qt.nokia.com/qq/qq13-apis.html#thebooleanparametertrap) + void changeCurrentItem(const QString& string, bool searchFromNextItem); + +private: + QString m_searchedString; + QElapsedTimer m_keyboardInputTime; + qint64 m_timeout; +}; + +#endif + + diff --git a/dolphin/src/kitemviews/private/kitemlistroleeditor.cpp b/dolphin/src/kitemviews/private/kitemlistroleeditor.cpp new file mode 100644 index 00000000..6e907192 --- /dev/null +++ b/dolphin/src/kitemviews/private/kitemlistroleeditor.cpp @@ -0,0 +1,151 @@ +/*************************************************************************** + * Copyright (C) 2012 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "kitemlistroleeditor.h" + +#include +#include +#include + +KItemListRoleEditor::KItemListRoleEditor(QWidget *parent) : + KTextEdit(parent), + m_role(), + m_blockFinishedSignal(false) +{ + setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + setAcceptRichText(false); + enableFindReplace(false); + document()->setDocumentMargin(0); + + if (parent) { + parent->installEventFilter(this); + } + + connect(this, SIGNAL(textChanged()), this, SLOT(autoAdjustSize())); +} + +KItemListRoleEditor::~KItemListRoleEditor() +{ +} + +void KItemListRoleEditor::setRole(const QByteArray& role) +{ + m_role = role; +} + +QByteArray KItemListRoleEditor::role() const +{ + return m_role; +} + +bool KItemListRoleEditor::eventFilter(QObject* watched, QEvent* event) +{ + if (watched == parentWidget() && event->type() == QEvent::Resize) { + emitRoleEditingFinished(); + } + + return KTextEdit::eventFilter(watched, event); +} + +bool KItemListRoleEditor::event(QEvent* event) +{ + if (event->type() == QEvent::FocusOut) { + QFocusEvent* focusEvent = static_cast(event); + if (focusEvent->reason() != Qt::PopupFocusReason) { + emitRoleEditingFinished(); + } + } + return KTextEdit::event(event); +} + +void KItemListRoleEditor::keyPressEvent(QKeyEvent* event) +{ + switch (event->key()) { + case Qt::Key_Escape: + // Emitting the signal roleEditingCanceled might result + // in losing the focus. Per default losing the focus emits + // a roleEditingFinished signal (see KItemListRoleEditor::event), + // which is not wanted in this case. + m_blockFinishedSignal = true; + emit roleEditingCanceled(m_role, KIO::encodeFileName(toPlainText())); + m_blockFinishedSignal = false; + event->accept(); + return; + case Qt::Key_Enter: + case Qt::Key_Return: + emitRoleEditingFinished(); + event->accept(); + return; + case Qt::Key_Left: + case Qt::Key_Right: { + QTextCursor cursor = textCursor(); + if (event->modifiers() == Qt::NoModifier && cursor.hasSelection()) { + if (event->key() == Qt::Key_Left) { + cursor.setPosition(cursor.selectionStart()); + } else { + cursor.setPosition(cursor.selectionEnd()); + } + cursor.clearSelection(); + setTextCursor(cursor); + event->accept(); + return; + } + break; + } + default: + break; + } + + KTextEdit::keyPressEvent(event); +} + +void KItemListRoleEditor::autoAdjustSize() +{ + const qreal frameBorder = 2 * frameWidth(); + + const qreal requiredWidth = document()->size().width(); + const qreal availableWidth = size().width() - frameBorder; + if (requiredWidth > availableWidth) { + qreal newWidth = requiredWidth + frameBorder; + if (parentWidget() && pos().x() + newWidth > parentWidget()->width()) { + newWidth = parentWidget()->width() - pos().x(); + } + resize(newWidth, size().height()); + } + + const qreal requiredHeight = document()->size().height(); + const qreal availableHeight = size().height() - frameBorder; + if (requiredHeight > availableHeight) { + qreal newHeight = requiredHeight + frameBorder; + if (parentWidget() && pos().y() + newHeight > parentWidget()->height()) { + newHeight = parentWidget()->height() - pos().y(); + } + resize(size().width(), newHeight); + } +} + +void KItemListRoleEditor::emitRoleEditingFinished() +{ + if (!m_blockFinishedSignal) { + emit roleEditingFinished(m_role, KIO::encodeFileName(toPlainText())); + } +} + +#include "moc_kitemlistroleeditor.cpp" diff --git a/dolphin/src/kitemviews/private/kitemlistroleeditor.h b/dolphin/src/kitemviews/private/kitemlistroleeditor.h new file mode 100644 index 00000000..e44c269f --- /dev/null +++ b/dolphin/src/kitemviews/private/kitemlistroleeditor.h @@ -0,0 +1,76 @@ +/*************************************************************************** + * Copyright (C) 2012 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef KITEMLISTROLEEDITOR_H +#define KITEMLISTROLEEDITOR_H + +#include "dolphinprivate_export.h" + +#include + +/** + * @brief Editor for renaming roles of a KItemListWidget. + * + * Provides signals when the editing got cancelled (e.g. by + * pressing Escape or when losing the focus) or when the editing + * got finished (e.g. by pressing Enter or Return). + * + * The size automatically gets increased if the text does not fit. + */ +class DOLPHINPRIVATE_EXPORT KItemListRoleEditor : public KTextEdit +{ + Q_OBJECT + +public: + explicit KItemListRoleEditor(QWidget* parent); + virtual ~KItemListRoleEditor(); + + void setRole(const QByteArray& role); + QByteArray role() const; + + virtual bool eventFilter(QObject* watched, QEvent* event); + +signals: + void roleEditingFinished(const QByteArray& role, const QVariant& value); + void roleEditingCanceled(const QByteArray& role, const QVariant& value); + +protected: + virtual bool event(QEvent* event); + virtual void keyPressEvent(QKeyEvent* event); + +private slots: + /** + * Increases the size of the editor in case if there is not + * enough room for the text. + */ + void autoAdjustSize(); + +private: + /** + * Emits the signal roleEditingFinished if m_blockFinishedSignal + * is false. + */ + void emitRoleEditingFinished(); + +private: + QByteArray m_role; + bool m_blockFinishedSignal; +}; + +#endif diff --git a/dolphin/src/kitemviews/private/kitemlistrubberband.cpp b/dolphin/src/kitemviews/private/kitemlistrubberband.cpp new file mode 100644 index 00000000..a61dafe0 --- /dev/null +++ b/dolphin/src/kitemviews/private/kitemlistrubberband.cpp @@ -0,0 +1,91 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "kitemlistrubberband.h" + +KItemListRubberBand::KItemListRubberBand(QObject* parent) : + QObject(parent), + m_active(false), + m_startPos(), + m_endPos() +{ +} + +KItemListRubberBand::~KItemListRubberBand() +{ +} + +void KItemListRubberBand::setStartPosition(const QPointF& pos) +{ + if (m_startPos != pos) { + const QPointF previous = m_startPos; + m_startPos = pos; + emit startPositionChanged(m_startPos, previous); + } +} + +QPointF KItemListRubberBand::startPosition() const +{ + return m_startPos; +} + +void KItemListRubberBand::setEndPosition(const QPointF& pos) +{ + if (m_endPos != pos) { + const QPointF previous = m_endPos; + m_endPos = pos; + + if (m_startPos.x() == m_endPos.x()) { + if (previous.x() < m_startPos.x()) { + m_endPos.rx() = m_startPos.x() - 1.0; + } else { + m_endPos.rx() = m_startPos.x() + 1.0; + } + } + if (m_startPos.y() == m_endPos.y()) { + if (previous.y() < m_startPos.y()) { + m_endPos.ry() = m_startPos.y() - 1.0; + } else { + m_endPos.ry() = m_startPos.y() + 1.0; + } + } + + emit endPositionChanged(m_endPos, previous); + } +} + +QPointF KItemListRubberBand::endPosition() const +{ + return m_endPos; +} + +void KItemListRubberBand::setActive(bool active) +{ + if (m_active != active) { + m_active = active; + emit activationChanged(active); + } +} + +bool KItemListRubberBand::isActive() const +{ + return m_active; +} + +#include "moc_kitemlistrubberband.cpp" diff --git a/dolphin/src/kitemviews/private/kitemlistrubberband.h b/dolphin/src/kitemviews/private/kitemlistrubberband.h new file mode 100644 index 00000000..0b387874 --- /dev/null +++ b/dolphin/src/kitemviews/private/kitemlistrubberband.h @@ -0,0 +1,60 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef KITEMLISTRUBBERBAND_H +#define KITEMLISTRUBBERBAND_H + +#include +#include +#include + +/** + * @brief Manages the rubberband when selecting items. + */ +class DOLPHINPRIVATE_EXPORT KItemListRubberBand : public QObject +{ + Q_OBJECT + +public: + explicit KItemListRubberBand(QObject* parent = 0); + virtual ~KItemListRubberBand(); + + void setStartPosition(const QPointF& pos); + QPointF startPosition() const; + + void setEndPosition(const QPointF& pos); + QPointF endPosition() const; + + void setActive(bool active); + bool isActive() const; + +signals: + void activationChanged(bool active); + void startPositionChanged(const QPointF& current, const QPointF& previous); + void endPositionChanged(const QPointF& current, const QPointF& previous); + +private: + bool m_active; + QPointF m_startPos; + QPointF m_endPos; +}; + +#endif + + diff --git a/dolphin/src/kitemviews/private/kitemlistselectiontoggle.cpp b/dolphin/src/kitemviews/private/kitemlistselectiontoggle.cpp new file mode 100644 index 00000000..1fab7f2c --- /dev/null +++ b/dolphin/src/kitemviews/private/kitemlistselectiontoggle.cpp @@ -0,0 +1,118 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "kitemlistselectiontoggle.h" + +#include +#include +#include + +#include + +KItemListSelectionToggle::KItemListSelectionToggle(QGraphicsItem* parent) : + QGraphicsWidget(parent, 0), + m_checked(false), + m_hovered(false) +{ +} + +KItemListSelectionToggle::~KItemListSelectionToggle() +{ +} + +void KItemListSelectionToggle::setChecked(bool checked) +{ + if (m_checked != checked) { + m_checked = checked; + m_pixmap = QPixmap(); + update(); + } +} + +bool KItemListSelectionToggle::isChecked() const +{ + return m_checked; +} + +void KItemListSelectionToggle::setHovered(bool hovered) +{ + if (m_hovered != hovered) { + m_hovered = hovered; + m_pixmap = QPixmap(); + update(); + } +} + +void KItemListSelectionToggle::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) +{ + Q_UNUSED(option); + Q_UNUSED(widget); + + if (m_pixmap.isNull()) { + updatePixmap(); + } + + const qreal x = (size().width() - qreal(m_pixmap.width())) / 2; + const qreal y = (size().height() - qreal(m_pixmap.height())) / 2; + painter->drawPixmap(x, y, m_pixmap); +} + +void KItemListSelectionToggle::resizeEvent(QGraphicsSceneResizeEvent* event) +{ + QGraphicsWidget::resizeEvent(event); + + if (!m_pixmap.isNull()) { + const int pixmapSize = m_pixmap.size().width(); // Pixmap width is always equal pixmap height + + if (pixmapSize != iconSize()) { + // If the required icon size is different from the actual pixmap size, + // overwrite the m_pixmap with an empty pixmap and reload the new + // icon on next re-painting. + m_pixmap = QPixmap(); + } + } +} + +void KItemListSelectionToggle::updatePixmap() +{ + const QString icon = m_checked ? "list-remove" : "list-add"; + const KIconLoader::States state = m_hovered ? KIconLoader::ActiveState : KIconLoader::DisabledState; + m_pixmap = KIconLoader::global()->loadIcon(icon, KIconLoader::Desktop, iconSize(), state); +} + +int KItemListSelectionToggle::iconSize() const +{ + const int iconSize = qMin(size().width(), size().height()); + + if (iconSize < KIconLoader::SizeSmallMedium) { + return KIconLoader::SizeSmall; + } else if (iconSize < KIconLoader::SizeMedium) { + return KIconLoader::SizeSmallMedium; + } else if (iconSize < KIconLoader::SizeLarge) { + return KIconLoader::SizeMedium; + } else if (iconSize < KIconLoader::SizeHuge) { + return KIconLoader::SizeLarge; + } else if (iconSize < KIconLoader::SizeEnormous) { + return KIconLoader::SizeHuge; + } + + return iconSize; +} + +#include "moc_kitemlistselectiontoggle.cpp" diff --git a/dolphin/src/kitemviews/private/kitemlistselectiontoggle.h b/dolphin/src/kitemviews/private/kitemlistselectiontoggle.h new file mode 100644 index 00000000..a23cf761 --- /dev/null +++ b/dolphin/src/kitemviews/private/kitemlistselectiontoggle.h @@ -0,0 +1,63 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef KITEMLISTSELECTIONTOGGLE_H +#define KITEMLISTSELECTIONTOGGLE_H + +#include + +#include +#include + +#include + +/** + * @brief Allows to toggle between the selected and unselected state of an item. + */ +class DOLPHINPRIVATE_EXPORT KItemListSelectionToggle : public QGraphicsWidget +{ + Q_OBJECT + +public: + KItemListSelectionToggle(QGraphicsItem* parent); + virtual ~KItemListSelectionToggle(); + + void setChecked(bool checked); + bool isChecked() const; + + void setHovered(bool hovered); + + virtual void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget = 0); + +protected: + virtual void resizeEvent(QGraphicsSceneResizeEvent* event); + +private: + void updatePixmap(); + int iconSize() const; + +private: + bool m_checked; + bool m_hovered; + QPixmap m_pixmap; +}; + +#endif + + diff --git a/dolphin/src/kitemviews/private/kitemlistsizehintresolver.cpp b/dolphin/src/kitemviews/private/kitemlistsizehintresolver.cpp new file mode 100644 index 00000000..1d806702 --- /dev/null +++ b/dolphin/src/kitemviews/private/kitemlistsizehintresolver.cpp @@ -0,0 +1,154 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "kitemlistsizehintresolver.h" + +#include + +KItemListSizeHintResolver::KItemListSizeHintResolver(const KItemListView* itemListView) : + m_itemListView(itemListView), + m_logicalHeightHintCache(), + m_logicalWidthHint(0.0), + m_needsResolving(false) +{ +} + +KItemListSizeHintResolver::~KItemListSizeHintResolver() +{ +} + +QSizeF KItemListSizeHintResolver::sizeHint(int index) +{ + updateCache(); + return QSizeF(m_logicalWidthHint, m_logicalHeightHintCache.at(index)); +} + +void KItemListSizeHintResolver::itemsInserted(const KItemRangeList& itemRanges) +{ + int insertedCount = 0; + foreach (const KItemRange& range, itemRanges) { + insertedCount += range.count; + } + + const int currentCount = m_logicalHeightHintCache.count(); + m_logicalHeightHintCache.reserve(currentCount + insertedCount); + + // We build the new list from the end to the beginning to mimize the + // number of moves. + m_logicalHeightHintCache.insert(m_logicalHeightHintCache.end(), insertedCount, 0.0); + + int sourceIndex = currentCount - 1; + int targetIndex = m_logicalHeightHintCache.count() - 1; + int itemsToInsertBeforeCurrentRange = insertedCount; + + for (int rangeIndex = itemRanges.count() - 1; rangeIndex >= 0; --rangeIndex) { + const KItemRange& range = itemRanges.at(rangeIndex); + itemsToInsertBeforeCurrentRange -= range.count; + + // First: move all existing items that must be put behind 'range'. + while (targetIndex >= itemsToInsertBeforeCurrentRange + range.index + range.count) { + m_logicalHeightHintCache[targetIndex] = m_logicalHeightHintCache[sourceIndex]; + --sourceIndex; + --targetIndex; + } + + // Then: insert QSizeF() for the items which are inserted into 'range'. + while (targetIndex >= itemsToInsertBeforeCurrentRange + range.index) { + m_logicalHeightHintCache[targetIndex] = 0.0; + --targetIndex; + } + } + + m_needsResolving = true; + + Q_ASSERT(m_logicalHeightHintCache.count() == m_itemListView->model()->count()); +} + +void KItemListSizeHintResolver::itemsRemoved(const KItemRangeList& itemRanges) +{ + const QVector::iterator begin = m_logicalHeightHintCache.begin(); + const QVector::iterator end = m_logicalHeightHintCache.end(); + + KItemRangeList::const_iterator rangeIt = itemRanges.constBegin(); + const KItemRangeList::const_iterator rangeEnd = itemRanges.constEnd(); + + QVector::iterator destIt = begin + rangeIt->index; + QVector::iterator srcIt = destIt + rangeIt->count; + + ++rangeIt; + + while (srcIt != end) { + *destIt = *srcIt; + ++destIt; + ++srcIt; + + if (rangeIt != rangeEnd && srcIt == begin + rangeIt->index) { + // Skip the items in the next removed range. + srcIt += rangeIt->count; + ++rangeIt; + } + } + + m_logicalHeightHintCache.erase(destIt, end); + + // Note that the cache size might temporarily not match the model size if + // this function is called from KItemListView::setModel() to empty the cache. + if (!m_logicalHeightHintCache.isEmpty() && m_itemListView->model()) { + Q_ASSERT(m_logicalHeightHintCache.count() == m_itemListView->model()->count()); + } +} + +void KItemListSizeHintResolver::itemsMoved(const KItemRange& range, const QList& movedToIndexes) +{ + QVector newLogicalHeightHintCache(m_logicalHeightHintCache); + + const int movedRangeEnd = range.index + range.count; + for (int i = range.index; i < movedRangeEnd; ++i) { + const int newIndex = movedToIndexes.at(i - range.index); + newLogicalHeightHintCache[newIndex] = m_logicalHeightHintCache.at(i); + } + + m_logicalHeightHintCache = newLogicalHeightHintCache; +} + +void KItemListSizeHintResolver::itemsChanged(int index, int count, const QSet& roles) +{ + Q_UNUSED(roles); + while (count) { + m_logicalHeightHintCache[index] = 0.0; + ++index; + --count; + } + + m_needsResolving = true; +} + +void KItemListSizeHintResolver::clearCache() +{ + m_logicalHeightHintCache.fill(0.0); + m_needsResolving = true; +} + +void KItemListSizeHintResolver::updateCache() +{ + if (m_needsResolving) { + m_itemListView->calculateItemSizeHints(m_logicalHeightHintCache, m_logicalWidthHint); + m_needsResolving = false; + } +} diff --git a/dolphin/src/kitemviews/private/kitemlistsizehintresolver.h b/dolphin/src/kitemviews/private/kitemlistsizehintresolver.h new file mode 100644 index 00000000..591d99ac --- /dev/null +++ b/dolphin/src/kitemviews/private/kitemlistsizehintresolver.h @@ -0,0 +1,56 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef KITEMLISTSIZEHINTRESOLVER_H +#define KITEMLISTSIZEHINTRESOLVER_H + +#include + +#include +#include +#include + +class KItemListView; + +/** + * @brief Calculates and caches the sizehints of items in KItemListView. + */ +class DOLPHINPRIVATE_EXPORT KItemListSizeHintResolver +{ +public: + KItemListSizeHintResolver(const KItemListView* itemListView); + virtual ~KItemListSizeHintResolver(); + QSizeF sizeHint(int index); + + void itemsInserted(const KItemRangeList& itemRanges); + void itemsRemoved(const KItemRangeList& itemRanges); + void itemsMoved(const KItemRange& range, const QList& movedToIndexes); + void itemsChanged(int index, int count, const QSet& roles); + + void clearCache(); + void updateCache(); + +private: + const KItemListView* m_itemListView; + mutable QVector m_logicalHeightHintCache; + mutable qreal m_logicalWidthHint; + bool m_needsResolving; +}; + +#endif diff --git a/dolphin/src/kitemviews/private/kitemlistsmoothscroller.cpp b/dolphin/src/kitemviews/private/kitemlistsmoothscroller.cpp new file mode 100644 index 00000000..7617684a --- /dev/null +++ b/dolphin/src/kitemviews/private/kitemlistsmoothscroller.cpp @@ -0,0 +1,212 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "kitemlistsmoothscroller.h" + +#include +#include +#include +#include +#include + +#include + +KItemListSmoothScroller::KItemListSmoothScroller(QScrollBar* scrollBar, + QObject* parent) : + QObject(parent), + m_scrollBarPressed(false), + m_smoothScrolling(true), + m_scrollBar(scrollBar), + m_animation(0) +{ + m_animation = new QPropertyAnimation(this); + const int duration = (KGlobalSettings::graphicEffectsLevel() == KGlobalSettings::NoEffects) ? 1 : 100; + m_animation->setDuration(duration); + connect(m_animation, SIGNAL(stateChanged(QAbstractAnimation::State,QAbstractAnimation::State)), + this, SLOT(slotAnimationStateChanged(QAbstractAnimation::State,QAbstractAnimation::State))); + + m_scrollBar->installEventFilter(this); +} + +KItemListSmoothScroller::~KItemListSmoothScroller() +{ +} + +void KItemListSmoothScroller::setScrollBar(QScrollBar *scrollBar) +{ + m_scrollBar = scrollBar; +} + +QScrollBar* KItemListSmoothScroller::scrollBar() const +{ + return m_scrollBar; +} + +void KItemListSmoothScroller::setTargetObject(QObject* target) +{ + m_animation->setTargetObject(target); +} + +QObject* KItemListSmoothScroller::targetObject() const +{ + return m_animation->targetObject(); +} + +void KItemListSmoothScroller::setPropertyName(const QByteArray& propertyName) +{ + m_animation->setPropertyName(propertyName); +} + +QByteArray KItemListSmoothScroller::propertyName() const +{ + return m_animation->propertyName(); +} + +void KItemListSmoothScroller::scrollContentsBy(qreal distance) +{ + QObject* target = targetObject(); + if (!target) { + return; + } + + const QByteArray name = propertyName(); + const qreal currentOffset = target->property(name).toReal(); + if (static_cast(currentOffset) == m_scrollBar->value()) { + // The current offset is already synchronous to the scrollbar + return; + } + + const bool animRunning = (m_animation->state() == QAbstractAnimation::Running); + if (animRunning) { + // Stopping a running animation means skipping the range from the current offset + // until the target offset. To prevent skipping of the range the difference + // is added to the new target offset. + const qreal oldEndOffset = m_animation->endValue().toReal(); + distance += (currentOffset - oldEndOffset); + } + + const qreal endOffset = currentOffset - distance; + if (m_smoothScrolling || animRunning) { + qreal startOffset = currentOffset; + if (animRunning) { + // If the animation was running and has been interrupted by assigning a new end-offset + // one frame must be added to the start-offset to keep the animation smooth. This also + // assures that animation proceeds even in cases where new end-offset are triggered + // within a very short timeslots. + startOffset += (endOffset - currentOffset) * 1000 / (m_animation->duration() * 60); + if (currentOffset < endOffset) { + startOffset = qMin(startOffset, endOffset); + } else { + startOffset = qMax(startOffset, endOffset); + } + } + + m_animation->stop(); + m_animation->setStartValue(startOffset); + m_animation->setEndValue(endOffset); + m_animation->setEasingCurve(animRunning ? QEasingCurve::OutQuad : QEasingCurve::InOutQuad); + m_animation->start(); + target->setProperty(name, startOffset); + } else { + target->setProperty(name, endOffset); + } +} + +void KItemListSmoothScroller::scrollTo(qreal position) +{ + int newValue = position; + newValue = qBound(0, newValue, m_scrollBar->maximum()); + + if (newValue != m_scrollBar->value()) { + m_smoothScrolling = true; + m_scrollBar->setValue(newValue); + } +} + +bool KItemListSmoothScroller::requestScrollBarUpdate(int newMaximum) +{ + if (m_animation->state() == QAbstractAnimation::Running) { + if (newMaximum == m_scrollBar->maximum()) { + // The value has been changed by the animation, no update + // of the scrollbars is required as their target state will be + // reached with the end of the animation. + return false; + } + + // The maximum has been changed which indicates that the content + // of the view has been changed. Stop the animation in any case and + // update the scrollbars immediately. + m_animation->stop(); + } + return true; +} + +bool KItemListSmoothScroller::eventFilter(QObject* obj, QEvent* event) +{ + Q_ASSERT(obj == m_scrollBar); + + switch (event->type()) { + case QEvent::MouseButtonPress: + m_scrollBarPressed = true; + m_smoothScrolling = true; + break; + + case QEvent::MouseButtonRelease: + m_scrollBarPressed = false; + m_smoothScrolling = false; + break; + + case QEvent::Wheel: + handleWheelEvent(static_cast(event)); + break; + + default: + break; + } + + return QObject::eventFilter(obj, event); +} + +void KItemListSmoothScroller::slotAnimationStateChanged(QAbstractAnimation::State newState, + QAbstractAnimation::State oldState) +{ + Q_UNUSED(oldState); + if (newState == QAbstractAnimation::Stopped && m_smoothScrolling && !m_scrollBarPressed) { + m_smoothScrolling = false; + } +} + +void KItemListSmoothScroller::handleWheelEvent(QWheelEvent* event) +{ + const int numDegrees = event->delta() / 8; + const int numSteps = numDegrees / 15; + + const bool previous = m_smoothScrolling; + + m_smoothScrolling = true; + const int value = m_scrollBar->value(); + const int pageStep = m_scrollBar->pageStep(); + m_scrollBar->setValue(value - numSteps * pageStep); + + m_smoothScrolling = previous; + + event->accept(); +} + +#include "moc_kitemlistsmoothscroller.cpp" diff --git a/dolphin/src/kitemviews/private/kitemlistsmoothscroller.h b/dolphin/src/kitemviews/private/kitemlistsmoothscroller.h new file mode 100644 index 00000000..8e85b342 --- /dev/null +++ b/dolphin/src/kitemviews/private/kitemlistsmoothscroller.h @@ -0,0 +1,103 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef KITEMLISTSMOOTHSCROLLER_H +#define KITEMLISTSMOOTHSCROLLER_H + +#include + +#include +#include + +#include +#include +#include + +/** + * @brief Helper class for KItemListContainer to have a smooth + * scrolling when adjusting the scrollbars. + */ +class DOLPHINPRIVATE_EXPORT KItemListSmoothScroller : public QObject +{ + Q_OBJECT + +public: + explicit KItemListSmoothScroller(QScrollBar* scrollBar, + QObject* parent = 0); + virtual ~KItemListSmoothScroller(); + + void setScrollBar(QScrollBar* scrollBar); + QScrollBar* scrollBar() const; + + void setTargetObject(QObject* target); + QObject* targetObject() const; + + void setPropertyName(const QByteArray& propertyName); + QByteArray propertyName() const; + + /** + * Adjusts the position of the target by \p distance + * pixels. Is invoked in the context of QAbstractScrollArea::scrollContentsBy() + * where the scrollbars already have the new position but the content + * has not been scrolled yet. + */ + void scrollContentsBy(qreal distance); + + /** + * Does a smooth-scrolling to the position \p position + * on the target and also adjusts the corresponding scrollbar + * to the new position. + */ + void scrollTo(qreal position); + + /** + * Must be invoked before the scrollbar should get updated to have a new + * maximum. True is returned if the new maximum can be applied. If false + * is returned the maximum has already been reached and the value will + * be reached at the end of the animation. + */ + // TODO: This interface is tricky to understand. Try to make this more + // generic/readable if the corresponding code in KItemListContainer got + // stable. + bool requestScrollBarUpdate(int newMaximum); + +protected: + virtual bool eventFilter(QObject* obj, QEvent* event); + +private slots: + void slotAnimationStateChanged(QAbstractAnimation::State newState, + QAbstractAnimation::State oldState); + +private: + /** + * Results into a smooth-scrolling of the target dependent on the direction + * of the wheel event. + */ + void handleWheelEvent(QWheelEvent* event); + +private: + bool m_scrollBarPressed; + bool m_smoothScrolling; + QScrollBar* m_scrollBar; + QPropertyAnimation* m_animation; +}; + +#endif + + diff --git a/dolphin/src/kitemviews/private/kitemlistviewanimation.cpp b/dolphin/src/kitemviews/private/kitemlistviewanimation.cpp new file mode 100644 index 00000000..4c7ba61f --- /dev/null +++ b/dolphin/src/kitemviews/private/kitemlistviewanimation.cpp @@ -0,0 +1,245 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "kitemlistviewanimation.h" + +#include + +#include +#include + +#include +#include + +KItemListViewAnimation::KItemListViewAnimation(QObject* parent) : + QObject(parent), + m_animationDuration(200), + m_scrollOrientation(Qt::Vertical), + m_scrollOffset(0), + m_animation() +{ + if (KGlobalSettings::graphicEffectsLevel() == KGlobalSettings::NoEffects) { + m_animationDuration = 1; + } +} + +KItemListViewAnimation::~KItemListViewAnimation() +{ + for (int type = 0; type < AnimationTypeCount; ++type) { + qDeleteAll(m_animation[type]); + } +} + +void KItemListViewAnimation::setScrollOrientation(Qt::Orientation orientation) +{ + m_scrollOrientation = orientation; +} + +Qt::Orientation KItemListViewAnimation::scrollOrientation() const +{ + return m_scrollOrientation; +} + +void KItemListViewAnimation::setScrollOffset(qreal offset) +{ + const qreal diff = m_scrollOffset - offset; + m_scrollOffset = offset; + + // The change of the offset requires that the position of all + // animated QGraphicsWidgets get adjusted. An exception is made + // for the delete animation that should just fade away on the + // existing position. + for (int type = 0; type < AnimationTypeCount; ++type) { + if (type == DeleteAnimation) { + continue; + } + + QHashIterator it(m_animation[type]); + while (it.hasNext()) { + it.next(); + + QGraphicsWidget* widget = it.key(); + QPropertyAnimation* propertyAnim = it.value(); + + QPointF currentPos = widget->pos(); + if (m_scrollOrientation == Qt::Vertical) { + currentPos.ry() += diff; + } else { + currentPos.rx() += diff; + } + + if (type == MovingAnimation) { + // Stop the animation, calculate the moved start- and end-value + // and restart the animation for the remaining duration. + const int remainingDuration = propertyAnim->duration() + - propertyAnim->currentTime(); + + const bool block = propertyAnim->signalsBlocked(); + propertyAnim->blockSignals(true); + propertyAnim->stop(); + + QPointF endPos = propertyAnim->endValue().toPointF(); + if (m_scrollOrientation == Qt::Vertical) { + endPos.ry() += diff; + } else { + endPos.rx() += diff; + } + + propertyAnim->setDuration(remainingDuration); + propertyAnim->setStartValue(currentPos); + propertyAnim->setEndValue(endPos); + propertyAnim->start(); + propertyAnim->blockSignals(block); + } else { + widget->setPos(currentPos); + } + } + } +} + +qreal KItemListViewAnimation::scrollOffset() const +{ + return m_scrollOffset; +} + +void KItemListViewAnimation::start(QGraphicsWidget* widget, AnimationType type, const QVariant& endValue) +{ + stop(widget, type); + + QPropertyAnimation* propertyAnim = 0; + + switch (type) { + case MovingAnimation: { + const QPointF newPos = endValue.toPointF(); + if (newPos == widget->pos()) { + return; + } + + propertyAnim = new QPropertyAnimation(widget, "pos"); + propertyAnim->setDuration(m_animationDuration); + propertyAnim->setEndValue(newPos); + break; + } + + case CreateAnimation: { + propertyAnim = new QPropertyAnimation(widget, "opacity"); + propertyAnim->setEasingCurve(QEasingCurve::InQuart); + propertyAnim->setDuration(m_animationDuration); + propertyAnim->setStartValue(0.0); + propertyAnim->setEndValue(1.0); + break; + } + + case DeleteAnimation: { + propertyAnim = new QPropertyAnimation(widget, "opacity"); + propertyAnim->setEasingCurve(QEasingCurve::OutQuart); + propertyAnim->setDuration(m_animationDuration); + propertyAnim->setStartValue(1.0); + propertyAnim->setEndValue(0.0); + break; + } + + case ResizeAnimation: { + const QSizeF newSize = endValue.toSizeF(); + if (newSize == widget->size()) { + return; + } + + propertyAnim = new QPropertyAnimation(widget, "size"); + propertyAnim->setDuration(m_animationDuration); + propertyAnim->setEndValue(newSize); + break; + } + + default: + break; + } + + Q_ASSERT(propertyAnim); + connect(propertyAnim, SIGNAL(finished()), this, SLOT(slotFinished())); + m_animation[type].insert(widget, propertyAnim); + + propertyAnim->start(); +} + +void KItemListViewAnimation::stop(QGraphicsWidget* widget, AnimationType type) +{ + QPropertyAnimation* propertyAnim = m_animation[type].value(widget); + if (propertyAnim) { + propertyAnim->stop(); + + switch (type) { + case MovingAnimation: break; + case CreateAnimation: widget->setOpacity(1.0); break; + case DeleteAnimation: widget->setOpacity(0.0); break; + case ResizeAnimation: break; + default: break; + } + + m_animation[type].remove(widget); + delete propertyAnim; + + emit finished(widget, type); + } +} + +void KItemListViewAnimation::stop(QGraphicsWidget* widget) +{ + for (int type = 0; type < AnimationTypeCount; ++type) { + stop(widget, static_cast(type)); + } +} + +bool KItemListViewAnimation::isStarted(QGraphicsWidget *widget, AnimationType type) const +{ + return m_animation[type].value(widget); +} + +bool KItemListViewAnimation::isStarted(QGraphicsWidget* widget) const +{ + for (int type = 0; type < AnimationTypeCount; ++type) { + if (isStarted(widget, static_cast(type))) { + return true; + } + } + return false; +} + +void KItemListViewAnimation::slotFinished() +{ + QPropertyAnimation* finishedAnim = qobject_cast(sender()); + for (int type = 0; type < AnimationTypeCount; ++type) { + QMutableHashIterator it(m_animation[type]); + while (it.hasNext()) { + it.next(); + QPropertyAnimation* propertyAnim = it.value(); + if (propertyAnim == finishedAnim) { + QGraphicsWidget* widget = it.key(); + it.remove(); + finishedAnim->deleteLater(); + + emit finished(widget, static_cast(type)); + return; + } + } + } + Q_ASSERT(false); +} + +#include "moc_kitemlistviewanimation.cpp" diff --git a/dolphin/src/kitemviews/private/kitemlistviewanimation.h b/dolphin/src/kitemviews/private/kitemlistviewanimation.h new file mode 100644 index 00000000..d9c3e7a3 --- /dev/null +++ b/dolphin/src/kitemviews/private/kitemlistviewanimation.h @@ -0,0 +1,106 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef KITEMLISTVIEWANIMATION_H +#define KITEMLISTVIEWANIMATION_H + +#include + +#include +#include +#include + +class KItemListView; +#include +#include +#include + +/** + * @brief Internal helper class for KItemListView to animate the items. + * + * Supports item animations for moving, creating, deleting and resizing + * an item. Several applications can be applied to one item in parallel. + */ +class DOLPHINPRIVATE_EXPORT KItemListViewAnimation : public QObject +{ + Q_OBJECT + +public: + enum AnimationType { + MovingAnimation, + CreateAnimation, + DeleteAnimation, + ResizeAnimation + }; + + KItemListViewAnimation(QObject* parent = 0); + virtual ~KItemListViewAnimation(); + + void setScrollOrientation(Qt::Orientation orientation); + Qt::Orientation scrollOrientation() const; + + void setScrollOffset(qreal scrollOffset); + qreal scrollOffset() const; + + /** + * Starts the animation of the type \a type for the widget \a widget. If an animation + * of the type is already running, this animation will be stopped before starting + * the new animation. + */ + void start(QGraphicsWidget* widget, AnimationType type, const QVariant& endValue = QVariant()); + + /** + * Stops the animation of the type \a type for the widget \a widget. + */ + void stop(QGraphicsWidget* widget, AnimationType type); + + /** + * Stops all animations that have been applied to the widget \a widget. + */ + void stop(QGraphicsWidget* widget); + + /** + * @return True if the animation of the type \a type has been started + * for the widget \a widget.. + */ + bool isStarted(QGraphicsWidget *widget, AnimationType type) const; + + /** + * @return True if any animation has been started for the widget. + */ + bool isStarted(QGraphicsWidget* widget) const; + +signals: + void finished(QGraphicsWidget* widget, KItemListViewAnimation::AnimationType type); + +private slots: + void slotFinished(); + +private: + enum { AnimationTypeCount = 4 }; + + int m_animationDuration; + Qt::Orientation m_scrollOrientation; + qreal m_scrollOffset; + QHash m_animation[AnimationTypeCount]; +}; + +#endif + + diff --git a/dolphin/src/kitemviews/private/kitemlistviewlayouter.cpp b/dolphin/src/kitemviews/private/kitemlistviewlayouter.cpp new file mode 100644 index 00000000..cdb41d35 --- /dev/null +++ b/dolphin/src/kitemviews/private/kitemlistviewlayouter.cpp @@ -0,0 +1,624 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "kitemlistviewlayouter.h" + +#include +#include "kitemlistsizehintresolver.h" + +#include + +// #define KITEMLISTVIEWLAYOUTER_DEBUG + +KItemListViewLayouter::KItemListViewLayouter(KItemListSizeHintResolver* sizeHintResolver, QObject* parent) : + QObject(parent), + m_dirty(true), + m_visibleIndexesDirty(true), + m_scrollOrientation(Qt::Vertical), + m_size(), + m_itemSize(128, 128), + m_itemMargin(), + m_headerHeight(0), + m_model(0), + m_sizeHintResolver(sizeHintResolver), + m_scrollOffset(0), + m_maximumScrollOffset(0), + m_itemOffset(0), + m_maximumItemOffset(0), + m_firstVisibleIndex(-1), + m_lastVisibleIndex(-1), + m_columnWidth(0), + m_xPosInc(0), + m_columnCount(0), + m_rowOffsets(), + m_columnOffsets(), + m_groupItemIndexes(), + m_groupHeaderHeight(0), + m_groupHeaderMargin(0), + m_itemInfos() +{ + Q_ASSERT(m_sizeHintResolver); +} + +KItemListViewLayouter::~KItemListViewLayouter() +{ +} + +void KItemListViewLayouter::setScrollOrientation(Qt::Orientation orientation) +{ + if (m_scrollOrientation != orientation) { + m_scrollOrientation = orientation; + m_dirty = true; + } +} + +Qt::Orientation KItemListViewLayouter::scrollOrientation() const +{ + return m_scrollOrientation; +} + +void KItemListViewLayouter::setSize(const QSizeF& size) +{ + if (m_size != size) { + if (m_scrollOrientation == Qt::Vertical) { + if (m_size.width() != size.width()) { + m_dirty = true; + } + } else if (m_size.height() != size.height()) { + m_dirty = true; + } + + m_size = size; + m_visibleIndexesDirty = true; + } +} + +QSizeF KItemListViewLayouter::size() const +{ + return m_size; +} + +void KItemListViewLayouter::setItemSize(const QSizeF& size) +{ + if (m_itemSize != size) { + m_itemSize = size; + m_dirty = true; + } +} + +QSizeF KItemListViewLayouter::itemSize() const +{ + return m_itemSize; +} + +void KItemListViewLayouter::setItemMargin(const QSizeF& margin) +{ + if (m_itemMargin != margin) { + m_itemMargin = margin; + m_dirty = true; + } +} + +QSizeF KItemListViewLayouter::itemMargin() const +{ + return m_itemMargin; +} + +void KItemListViewLayouter::setHeaderHeight(qreal height) +{ + if (m_headerHeight != height) { + m_headerHeight = height; + m_dirty = true; + } +} + +qreal KItemListViewLayouter::headerHeight() const +{ + return m_headerHeight; +} + +void KItemListViewLayouter::setGroupHeaderHeight(qreal height) +{ + if (m_groupHeaderHeight != height) { + m_groupHeaderHeight = height; + m_dirty = true; + } +} + +qreal KItemListViewLayouter::groupHeaderHeight() const +{ + return m_groupHeaderHeight; +} + +void KItemListViewLayouter::setGroupHeaderMargin(qreal margin) +{ + if (m_groupHeaderMargin != margin) { + m_groupHeaderMargin = margin; + m_dirty = true; + } +} + +qreal KItemListViewLayouter::groupHeaderMargin() const +{ + return m_groupHeaderMargin; +} + +void KItemListViewLayouter::setScrollOffset(qreal offset) +{ + if (m_scrollOffset != offset) { + m_scrollOffset = offset; + m_visibleIndexesDirty = true; + } +} + +qreal KItemListViewLayouter::scrollOffset() const +{ + return m_scrollOffset; +} + +qreal KItemListViewLayouter::maximumScrollOffset() const +{ + const_cast(this)->doLayout(); + return m_maximumScrollOffset; +} + +void KItemListViewLayouter::setItemOffset(qreal offset) +{ + if (m_itemOffset != offset) { + m_itemOffset = offset; + m_visibleIndexesDirty = true; + } +} + +qreal KItemListViewLayouter::itemOffset() const +{ + return m_itemOffset; +} + +qreal KItemListViewLayouter::maximumItemOffset() const +{ + const_cast(this)->doLayout(); + return m_maximumItemOffset; +} + +void KItemListViewLayouter::setModel(const KItemModelBase* model) +{ + if (m_model != model) { + m_model = model; + m_dirty = true; + } +} + +const KItemModelBase* KItemListViewLayouter::model() const +{ + return m_model; +} + +int KItemListViewLayouter::firstVisibleIndex() const +{ + const_cast(this)->doLayout(); + return m_firstVisibleIndex; +} + +int KItemListViewLayouter::lastVisibleIndex() const +{ + const_cast(this)->doLayout(); + return m_lastVisibleIndex; +} + +QRectF KItemListViewLayouter::itemRect(int index) const +{ + const_cast(this)->doLayout(); + if (index < 0 || index >= m_itemInfos.count()) { + return QRectF(); + } + + QSizeF sizeHint = m_sizeHintResolver->sizeHint(index); + + const qreal x = m_columnOffsets.at(m_itemInfos.at(index).column); + const qreal y = m_rowOffsets.at(m_itemInfos.at(index).row); + + if (m_scrollOrientation == Qt::Horizontal) { + // Rotate the logical direction which is always vertical by 90° + // to get the physical horizontal direction + QPointF pos(y, x); + pos.rx() -= m_scrollOffset; + sizeHint.transpose(); + return QRectF(pos, sizeHint); + } + + if (sizeHint.width() <= 0) { + // In Details View, a size hint with negative width is used internally. + sizeHint.rwidth() = m_itemSize.width(); + } + + const QPointF pos(x - m_itemOffset, y - m_scrollOffset); + return QRectF(pos, sizeHint); +} + +QRectF KItemListViewLayouter::groupHeaderRect(int index) const +{ + const_cast(this)->doLayout(); + + const QRectF firstItemRect = itemRect(index); + QPointF pos = firstItemRect.topLeft(); + if (pos.isNull()) { + return QRectF(); + } + + QSizeF size; + if (m_scrollOrientation == Qt::Vertical) { + pos.rx() = 0; + pos.ry() -= m_groupHeaderHeight; + size = QSizeF(m_size.width(), m_groupHeaderHeight); + } else { + pos.rx() -= m_itemMargin.width(); + pos.ry() = 0; + + // Determine the maximum width used in the current column. As the + // scroll-direction is Qt::Horizontal and m_itemRects is accessed + // directly, the logical height represents the visual width, and + // the logical row represents the column. + qreal headerWidth = minimumGroupHeaderWidth(); + const int row = m_itemInfos[index].row; + const int maxIndex = m_itemInfos.count() - 1; + while (index <= maxIndex) { + if (m_itemInfos[index].row != row) { + break; + } + + const qreal itemWidth = (m_scrollOrientation == Qt::Vertical) + ? m_sizeHintResolver->sizeHint(index).width() + : m_sizeHintResolver->sizeHint(index).height(); + + if (itemWidth > headerWidth) { + headerWidth = itemWidth; + } + + ++index; + } + + size = QSizeF(headerWidth, m_size.height()); + } + return QRectF(pos, size); +} + +int KItemListViewLayouter::itemColumn(int index) const +{ + const_cast(this)->doLayout(); + if (index < 0 || index >= m_itemInfos.count()) { + return -1; + } + + return (m_scrollOrientation == Qt::Vertical) + ? m_itemInfos[index].column + : m_itemInfos[index].row; +} + +int KItemListViewLayouter::itemRow(int index) const +{ + const_cast(this)->doLayout(); + if (index < 0 || index >= m_itemInfos.count()) { + return -1; + } + + return (m_scrollOrientation == Qt::Vertical) + ? m_itemInfos[index].row + : m_itemInfos[index].column; +} + +int KItemListViewLayouter::maximumVisibleItems() const +{ + const_cast(this)->doLayout(); + + const int height = static_cast(m_size.height()); + const int rowHeight = static_cast(m_itemSize.height()); + int rows = height / rowHeight; + if (height % rowHeight != 0) { + ++rows; + } + + return rows * m_columnCount; +} + +bool KItemListViewLayouter::isFirstGroupItem(int itemIndex) const +{ + const_cast(this)->doLayout(); + return m_groupItemIndexes.contains(itemIndex); +} + +void KItemListViewLayouter::markAsDirty() +{ + m_dirty = true; +} + + +#ifndef QT_NO_DEBUG + bool KItemListViewLayouter::isDirty() + { + return m_dirty; + } +#endif + +void KItemListViewLayouter::doLayout() +{ + if (m_dirty) { +#ifdef KITEMLISTVIEWLAYOUTER_DEBUG + QElapsedTimer timer; + timer.start(); +#endif + m_visibleIndexesDirty = true; + + QSizeF itemSize = m_itemSize; + QSizeF itemMargin = m_itemMargin; + QSizeF size = m_size; + + const bool grouped = createGroupHeaders(); + + const bool horizontalScrolling = (m_scrollOrientation == Qt::Horizontal); + if (horizontalScrolling) { + // Flip everything so that the layout logically can work like having + // a vertical scrolling + itemSize.transpose(); + itemMargin.transpose(); + size.transpose(); + + if (grouped) { + // In the horizontal scrolling case all groups are aligned + // at the top, which decreases the available height. For the + // flipped data this means that the width must be decreased. + size.rwidth() -= m_groupHeaderHeight; + } + } + + m_columnWidth = itemSize.width() + itemMargin.width(); + const qreal widthForColumns = size.width() - itemMargin.width(); + m_columnCount = qMax(1, int(widthForColumns / m_columnWidth)); + m_xPosInc = itemMargin.width(); + + const int itemCount = m_model->count(); + if (itemCount > m_columnCount && m_columnWidth >= 32) { + // Apply the unused width equally to each column + const qreal unusedWidth = widthForColumns - m_columnCount * m_columnWidth; + if (unusedWidth > 0) { + const qreal columnInc = unusedWidth / (m_columnCount + 1); + m_columnWidth += columnInc; + m_xPosInc += columnInc; + } + } + + m_itemInfos.resize(itemCount); + + // Calculate the offset of each column, i.e., the x-coordinate where the column starts. + m_columnOffsets.resize(m_columnCount); + qreal currentOffset = m_xPosInc; + + if (grouped && horizontalScrolling) { + // All group headers will always be aligned on the top and not + // flipped like the other properties. + currentOffset += m_groupHeaderHeight; + } + + for (int column = 0; column < m_columnCount; ++column) { + m_columnOffsets[column] = currentOffset; + currentOffset += m_columnWidth; + } + + // Prepare the QVector which stores the y-coordinate for each new row. + int numberOfRows = (itemCount + m_columnCount - 1) / m_columnCount; + if (grouped && m_columnCount > 1) { + // In the worst case, a new row will be started for every group. + // We could calculate the exact number of rows now to prevent that we reserve + // too much memory, but the code required to do that might need much more + // memory than it would save in the average case. + numberOfRows += m_groupItemIndexes.count(); + } + m_rowOffsets.resize(numberOfRows); + + qreal y = m_headerHeight + itemMargin.height(); + int row = 0; + + int index = 0; + while (index < itemCount) { + qreal maxItemHeight = itemSize.height(); + + if (grouped) { + if (m_groupItemIndexes.contains(index)) { + // The item is the first item of a group. + // Increase the y-position to provide space + // for the group header. + if (index > 0) { + // Only add a margin if there has been added another + // group already before + y += m_groupHeaderMargin; + } else if (!horizontalScrolling) { + // The first group header should be aligned on top + y -= itemMargin.height(); + } + + if (!horizontalScrolling) { + y += m_groupHeaderHeight; + } + } + } + + m_rowOffsets[row] = y; + + int column = 0; + while (index < itemCount && column < m_columnCount) { + qreal requiredItemHeight = itemSize.height(); + const QSizeF sizeHint = m_sizeHintResolver->sizeHint(index); + const qreal sizeHintHeight = sizeHint.height(); + if (sizeHintHeight > requiredItemHeight) { + requiredItemHeight = sizeHintHeight; + } + + ItemInfo& itemInfo = m_itemInfos[index]; + itemInfo.column = column; + itemInfo.row = row; + + if (grouped && horizontalScrolling) { + // When grouping is enabled in the horizontal mode, the header alignment + // looks like this: + // Header-1 Header-2 Header-3 + // Item 1 Item 4 Item 7 + // Item 2 Item 5 Item 8 + // Item 3 Item 6 Item 9 + // In this case 'requiredItemHeight' represents the column-width. We don't + // check the content of the header in the layouter to determine the required + // width, hence assure that at least a minimal width of 15 characters is given + // (in average a character requires the halve width of the font height). + // + // TODO: Let the group headers provide a minimum width and respect this width here + const qreal headerWidth = minimumGroupHeaderWidth(); + if (requiredItemHeight < headerWidth) { + requiredItemHeight = headerWidth; + } + } + + maxItemHeight = qMax(maxItemHeight, requiredItemHeight); + ++index; + ++column; + + if (grouped && m_groupItemIndexes.contains(index)) { + // The item represents the first index of a group + // and must aligned in the first column + break; + } + } + + y += maxItemHeight + itemMargin.height(); + ++row; + } + + if (itemCount > 0) { + m_maximumScrollOffset = y; + m_maximumItemOffset = m_columnCount * m_columnWidth; + } else { + m_maximumScrollOffset = 0; + m_maximumItemOffset = 0; + } + +#ifdef KITEMLISTVIEWLAYOUTER_DEBUG + kDebug() << "[TIME] doLayout() for " << m_model->count() << "items:" << timer.elapsed(); +#endif + m_dirty = false; + } + + updateVisibleIndexes(); +} + +void KItemListViewLayouter::updateVisibleIndexes() +{ + if (!m_visibleIndexesDirty) { + return; + } + + Q_ASSERT(!m_dirty); + + if (m_model->count() <= 0) { + m_firstVisibleIndex = -1; + m_lastVisibleIndex = -1; + m_visibleIndexesDirty = false; + return; + } + + const int maxIndex = m_model->count() - 1; + + // Calculate the first visible index that is fully visible + int min = 0; + int max = maxIndex; + int mid = 0; + do { + mid = (min + max) / 2; + if (m_rowOffsets.at(m_itemInfos[mid].row) < m_scrollOffset) { + min = mid + 1; + } else { + max = mid - 1; + } + } while (min <= max); + + if (mid > 0) { + // Include the row before the first fully visible index, as it might + // be partly visible + if (m_rowOffsets.at(m_itemInfos[mid].row) >= m_scrollOffset) { + --mid; + Q_ASSERT(m_rowOffsets.at(m_itemInfos[mid].row) < m_scrollOffset); + } + + const int firstVisibleRow = m_itemInfos[mid].row; + while (mid > 0 && m_itemInfos[mid - 1].row == firstVisibleRow) { + --mid; + } + } + m_firstVisibleIndex = mid; + + // Calculate the last visible index that is (at least partly) visible + const int visibleHeight = (m_scrollOrientation == Qt::Horizontal) ? m_size.width() : m_size.height(); + qreal bottom = m_scrollOffset + visibleHeight; + if (m_model->groupedSorting()) { + bottom += m_groupHeaderHeight; + } + + min = m_firstVisibleIndex; + max = maxIndex; + do { + mid = (min + max) / 2; + if (m_rowOffsets.at(m_itemInfos[mid].row) <= bottom) { + min = mid + 1; + } else { + max = mid - 1; + } + } while (min <= max); + + while (mid > 0 && m_rowOffsets.at(m_itemInfos[mid].row) > bottom) { + --mid; + } + m_lastVisibleIndex = mid; + + m_visibleIndexesDirty = false; +} + +bool KItemListViewLayouter::createGroupHeaders() +{ + if (!m_model->groupedSorting()) { + return false; + } + + m_groupItemIndexes.clear(); + + const QList > groups = m_model->groups(); + if (groups.isEmpty()) { + return false; + } + + for (int i = 0; i < groups.count(); ++i) { + const int firstItemIndex = groups.at(i).first; + m_groupItemIndexes.insert(firstItemIndex); + } + + return true; +} + +qreal KItemListViewLayouter::minimumGroupHeaderWidth() const +{ + return 100; +} + +#include "moc_kitemlistviewlayouter.cpp" diff --git a/dolphin/src/kitemviews/private/kitemlistviewlayouter.h b/dolphin/src/kitemviews/private/kitemlistviewlayouter.h new file mode 100644 index 00000000..68fc3a66 --- /dev/null +++ b/dolphin/src/kitemviews/private/kitemlistviewlayouter.h @@ -0,0 +1,240 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef KITEMLISTVIEWLAYOUTER_H +#define KITEMLISTVIEWLAYOUTER_H + +#include + +#include +#include +#include +#include +#include + +class KItemModelBase; +class KItemListSizeHintResolver; + +/** + * @brief Internal helper class for KItemListView to layout the items. + * + * The layouter is capable to align the items within a grid. If the + * scroll-direction is horizontal the column-width of the grid can be + * variable. If the scroll-direction is vertical the row-height of + * the grid can be variable. + * + * The layouter is implemented in a way that it postpones the expensive + * layout operation until a property is read the first time after + * marking the layouter as dirty (see markAsDirty()). This means that + * changing properties of the layouter is not expensive, only the + * first read of a property can get expensive. + */ +class DOLPHINPRIVATE_EXPORT KItemListViewLayouter : public QObject +{ + Q_OBJECT + +public: + KItemListViewLayouter(KItemListSizeHintResolver* sizeHintResolver, QObject* parent = 0); + virtual ~KItemListViewLayouter(); + + void setScrollOrientation(Qt::Orientation orientation); + Qt::Orientation scrollOrientation() const; + + void setSize(const QSizeF& size); + QSizeF size() const; + + void setItemSize(const QSizeF& size); + QSizeF itemSize() const; + + /** + * Margin between the rows and columns of items. + */ + void setItemMargin(const QSizeF& margin); + QSizeF itemMargin() const; + + /** + * Sets the height of the header that is always aligned + * at the top. A height of <= 0.0 means that no header is + * used. + */ + void setHeaderHeight(qreal height); + qreal headerHeight() const; + + /** + * Sets the height of the group header that is used + * to indicate a new item group. + */ + void setGroupHeaderHeight(qreal height); + qreal groupHeaderHeight() const; + + /** + * Sets the margin between the last items of the group n and + * the group header for the group n + 1. + */ + void setGroupHeaderMargin(qreal margin); + qreal groupHeaderMargin() const; + + void setScrollOffset(qreal scrollOffset); + qreal scrollOffset() const; + + qreal maximumScrollOffset() const; + + void setItemOffset(qreal scrollOffset); + qreal itemOffset() const; + + qreal maximumItemOffset() const; + + void setModel(const KItemModelBase* model); + const KItemModelBase* model() const; + + /** + * @return The first (at least partly) visible index. -1 is returned + * if the item count is 0. + */ + int firstVisibleIndex() const; + + /** + * @return The last (at least partly) visible index. -1 is returned + * if the item count is 0. + */ + int lastVisibleIndex() const; + + /** + * @return Rectangle of the item with the index \a index. + * The top/left of the bounding rectangle is related to + * the top/left of the KItemListView. An empty rectangle + * is returned if an invalid index is given. + */ + QRectF itemRect(int index) const; + + /** + * @return Rectangle of the group header for the item with the + * index \a index. Note that the layouter does not check + * whether the item really has a header: Usually only + * the first item of a group gets a header (see + * isFirstGroupItem()). + */ + QRectF groupHeaderRect(int index) const; + + /** + * @return Column of the item with the index \a index. + * -1 is returned if an invalid index is given. + */ + int itemColumn(int index) const; + + /** + * @return Row of the item with the index \a index. + * -1 is returned if an invalid index is given. + */ + int itemRow(int index) const; + + /** + * @return Maximum number of (at least partly) visible items for + * the given size. + */ + int maximumVisibleItems() const; + + /** + * @return True if the item with the index \p itemIndex + * is the first item within a group. + */ + bool isFirstGroupItem(int itemIndex) const; + + /** + * Marks the layouter as dirty. This means as soon as a property of + * the layouter gets read, an expensive relayout will be done. + */ + void markAsDirty(); + + inline int columnCount() const + { + return m_columnCount; + } + +#ifndef QT_NO_DEBUG + /** + * @return True if the layouter has been marked as dirty and hence has + * not called yet doLayout(). Is enabled only in the debugging + * mode, as it is not useful to check the dirty state otherwise. + */ + bool isDirty(); +#endif + +private: + void doLayout(); + void updateVisibleIndexes(); + bool createGroupHeaders(); + + /** + * @return Minimum width of group headers when grouping is enabled in the horizontal + * alignment mode. The header alignment is done like this: + * Header-1 Header-2 Header-3 + * Item 1 Item 4 Item 7 + * Item 2 Item 5 Item 8 + * Item 3 Item 6 Item 9 + */ + qreal minimumGroupHeaderWidth() const; + +private: + bool m_dirty; + bool m_visibleIndexesDirty; + + Qt::Orientation m_scrollOrientation; + QSizeF m_size; + + QSizeF m_itemSize; + QSizeF m_itemMargin; + qreal m_headerHeight; + const KItemModelBase* m_model; + KItemListSizeHintResolver* m_sizeHintResolver; + + qreal m_scrollOffset; + qreal m_maximumScrollOffset; + + qreal m_itemOffset; + qreal m_maximumItemOffset; + + int m_firstVisibleIndex; + int m_lastVisibleIndex; + + qreal m_columnWidth; + qreal m_xPosInc; + int m_columnCount; + + QVector m_rowOffsets; + QVector m_columnOffsets; + + // Stores all item indexes that are the first item of a group. + // Assures fast access for KItemListViewLayouter::isFirstGroupItem(). + QSet m_groupItemIndexes; + qreal m_groupHeaderHeight; + qreal m_groupHeaderMargin; + + struct ItemInfo { + int column; + int row; + }; + QVector m_itemInfos; + + friend class KItemListControllerTest; +}; + +#endif + + diff --git a/dolphin/src/main.cpp b/dolphin/src/main.cpp new file mode 100644 index 00000000..30c32300 --- /dev/null +++ b/dolphin/src/main.cpp @@ -0,0 +1,97 @@ +/*************************************************************************** + * Copyright (C) 2006 by Peter Penz * + * Copyright (C) 2006 by Stefan Monov * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "dolphinapplication.h" + +#include "dolphinmainwindow.h" + +#include +#include +#include +#include +#include +#include + +int main(int argc, char **argv) +{ + KAboutData about("dolphin", 0, + ki18nc("@title", "Dolphin"), + KDE_VERSION_STRING, + ki18nc("@title", "File Manager"), + KAboutData::License_GPL, + ki18nc("@info:credit", "(C) 2006-2014 Peter Penz and Frank Reininghaus")); + about.setHomepage("http://dolphin.kde.org"); + about.addAuthor(ki18nc("@info:credit", "Frank Reininghaus"), + ki18nc("@info:credit", "Maintainer (since 2012) and developer"), + "frank78ac@googlemail.com"); + about.addAuthor(ki18nc("@info:credit", "Peter Penz"), + ki18nc("@info:credit", "Maintainer and developer (2006-2012)"), + "peter.penz19@gmail.com"); + about.addAuthor(ki18nc("@info:credit", "Sebastian Trüg"), + ki18nc("@info:credit", "Developer"), + "trueg@kde.org"), + about.addAuthor(ki18nc("@info:credit", "David Faure"), + ki18nc("@info:credit", "Developer"), + "faure@kde.org"); + about.addAuthor(ki18nc("@info:credit", "Emmanuel Pescosta"), + ki18nc("@info:credit", "Developer"), + "emmanuelpescosta099@gmail.com"); + about.addAuthor(ki18nc("@info:credit", "Aaron J. Seigo"), + ki18nc("@info:credit", "Developer"), + "aseigo@kde.org"); + about.addAuthor(ki18nc("@info:credit", "Rafael Fernández López"), + ki18nc("@info:credit", "Developer"), + "ereslibre@kde.org"); + about.addAuthor(ki18nc("@info:credit", "Kevin Ottens"), + ki18nc("@info:credit", "Developer"), + "ervin@kde.org"); + about.addAuthor(ki18nc("@info:credit", "Holger Freyther"), + ki18nc("@info:credit", "Developer"), + "freyther@gmx.net"); + about.addAuthor(ki18nc("@info:credit", "Max Blazejak"), + ki18nc("@info:credit", "Developer"), + "m43ksrocks@gmail.com"); + about.addAuthor(ki18nc("@info:credit", "Michael Austin"), + ki18nc("@info:credit", "Documentation"), + "tuxedup@users.sourceforge.net"); + // the .desktop file is not taken into account when launching manually, so + // set the icon precautionally: + about.setProgramIconName("system-file-manager"); + + KCmdLineArgs::init(argc, argv, &about); + + KCmdLineOptions options; + + options.add("select", ki18nc("@info:shell", "The files and directories passed as arguments " + "will be selected.")); + options.add("split", ki18nc("@info:shell", "Dolphin will get started with a split view.")); + options.add("+[Url]", ki18nc("@info:shell", "Document to open")); + KCmdLineArgs::addCmdLineOptions(options); + + { + DolphinApplication app; + if (app.isSessionRestored()) { + app.restoreSession(); + } + app.exec(); // krazy:exclude=crashy + } + + return 0; +} diff --git a/dolphin/src/panels/folders/dolphin_folderspanelsettings.kcfg b/dolphin/src/panels/folders/dolphin_folderspanelsettings.kcfg new file mode 100644 index 00000000..8b8ca66f --- /dev/null +++ b/dolphin/src/panels/folders/dolphin_folderspanelsettings.kcfg @@ -0,0 +1,18 @@ + + + + + + + + false + + + + true + + + diff --git a/dolphin/src/panels/folders/dolphin_folderspanelsettings.kcfgc b/dolphin/src/panels/folders/dolphin_folderspanelsettings.kcfgc new file mode 100644 index 00000000..f73e8807 --- /dev/null +++ b/dolphin/src/panels/folders/dolphin_folderspanelsettings.kcfgc @@ -0,0 +1,4 @@ +File=dolphin_folderspanelsettings.kcfg +ClassName=FoldersPanelSettings +Singleton=true +Mutators=true diff --git a/dolphin/src/panels/folders/foldersitemlistwidget.cpp b/dolphin/src/panels/folders/foldersitemlistwidget.cpp new file mode 100644 index 00000000..19693b06 --- /dev/null +++ b/dolphin/src/panels/folders/foldersitemlistwidget.cpp @@ -0,0 +1,36 @@ +/*************************************************************************** + * Copyright (C) 2012 by Emmanuel Pescosta * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "foldersitemlistwidget.h" + +FoldersItemListWidget::FoldersItemListWidget(KItemListWidgetInformant* informant, QGraphicsItem* parent) : + KFileItemListWidget(informant, parent) +{ +} + +FoldersItemListWidget::~FoldersItemListWidget() +{ +} + +QPalette::ColorRole FoldersItemListWidget::normalTextColorRole() const +{ + return QPalette::WindowText; +} + +#include "moc_foldersitemlistwidget.cpp" diff --git a/dolphin/src/panels/folders/foldersitemlistwidget.h b/dolphin/src/panels/folders/foldersitemlistwidget.h new file mode 100644 index 00000000..08d41b82 --- /dev/null +++ b/dolphin/src/panels/folders/foldersitemlistwidget.h @@ -0,0 +1,42 @@ +/*************************************************************************** + * Copyright (C) 2012 by Emmanuel Pescosta * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef FOLDERSITEMLISTWIDGET_H +#define FOLDERSITEMLISTWIDGET_H + +#include + +/** + * @brief Extends KFileItemListWidget to use the right text color. +*/ +class FoldersItemListWidget : public KFileItemListWidget +{ + Q_OBJECT + +public: + FoldersItemListWidget(KItemListWidgetInformant* informant, QGraphicsItem* parent); + virtual ~FoldersItemListWidget(); + +protected: + virtual QPalette::ColorRole normalTextColorRole() const; +}; + +#endif + + diff --git a/dolphin/src/panels/folders/folderspanel.cpp b/dolphin/src/panels/folders/folderspanel.cpp new file mode 100644 index 00000000..59ec452c --- /dev/null +++ b/dolphin/src/panels/folders/folderspanel.cpp @@ -0,0 +1,331 @@ +/*************************************************************************** + * Copyright (C) 2006-2010 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "folderspanel.h" + +#include "dolphin_folderspanelsettings.h" +#include "dolphin_generalsettings.h" +#include "treeviewcontextmenu.h" +#include "foldersitemlistwidget.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +FoldersPanel::FoldersPanel(QWidget* parent) : + Panel(parent), + m_updateCurrentItem(false), + m_controller(0), + m_model(0) +{ + setLayoutDirection(Qt::LeftToRight); +} + +FoldersPanel::~FoldersPanel() +{ + FoldersPanelSettings::self()->writeConfig(); + + if (m_controller) { + KItemListView* view = m_controller->view(); + m_controller->setView(0); + delete view; + } +} + +void FoldersPanel::setShowHiddenFiles(bool show) +{ + FoldersPanelSettings::setHiddenFilesShown(show); + m_model->setShowHiddenFiles(show); +} + +bool FoldersPanel::showHiddenFiles() const +{ + return FoldersPanelSettings::hiddenFilesShown(); +} + +void FoldersPanel::setAutoScrolling(bool enable) +{ + // TODO: Not supported yet in Dolphin 2.0 + FoldersPanelSettings::setAutoScrolling(enable); +} + +bool FoldersPanel::autoScrolling() const +{ + return FoldersPanelSettings::autoScrolling(); +} + +void FoldersPanel::rename(const KFileItem& item) +{ + if (GeneralSettings::renameInline()) { + const int index = m_model->index(item); + m_controller->view()->editRole(index, "text"); + } else { + RenameDialog* dialog = new RenameDialog(this, KFileItemList() << item); + dialog->setAttribute(Qt::WA_DeleteOnClose); + dialog->show(); + dialog->raise(); + dialog->activateWindow(); + } +} + +bool FoldersPanel::urlChanged() +{ + if (!url().isValid() || url().protocol() == "filenamesearch") { + // Skip results shown by a search, as possible identical + // directory names are useless without parent-path information. + return false; + } + + if (m_controller) { + loadTree(url()); + } + + return true; +} + +void FoldersPanel::showEvent(QShowEvent* event) +{ + if (event->spontaneous()) { + Panel::showEvent(event); + return; + } + + if (!m_controller) { + // Postpone the creating of the controller to the first show event. + // This assures that no performance and memory overhead is given when the folders panel is not + // used at all and stays invisible. + KFileItemListView* view = new KFileItemListView(); + view->setWidgetCreator(new KItemListWidgetCreator()); + view->setSupportsItemExpanding(true); + // Set the opacity to 0 initially. The opacity will be increased after the loading of the initial tree + // has been finished in slotLoadingCompleted(). This prevents an unnecessary animation-mess when + // opening the folders panel. + view->setOpacity(0); + + connect(view, SIGNAL(roleEditingFinished(int,QByteArray,QVariant)), + this, SLOT(slotRoleEditingFinished(int,QByteArray,QVariant))); + + m_model = new KFileItemModel(this); + m_model->setShowDirectoriesOnly(true); + m_model->setShowHiddenFiles(FoldersPanelSettings::hiddenFilesShown()); + // Use a QueuedConnection to give the view the possibility to react first on the + // finished loading. + connect(m_model, SIGNAL(directoryLoadingCompleted()), this, SLOT(slotLoadingCompleted()), Qt::QueuedConnection); + + m_controller = new KItemListController(m_model, view, this); + m_controller->setSelectionBehavior(KItemListController::SingleSelection); + m_controller->setAutoActivationBehavior(KItemListController::ExpansionOnly); + m_controller->setMouseDoubleClickAction(KItemListController::ActivateAndExpandItem); + m_controller->setAutoActivationDelay(750); + m_controller->setSingleClickActivationEnforced(true); + + connect(m_controller, SIGNAL(itemActivated(int)), this, SLOT(slotItemActivated(int))); + connect(m_controller, SIGNAL(itemMiddleClicked(int)), this, SLOT(slotItemMiddleClicked(int))); + connect(m_controller, SIGNAL(itemContextMenuRequested(int,QPointF)), this, SLOT(slotItemContextMenuRequested(int,QPointF))); + connect(m_controller, SIGNAL(viewContextMenuRequested(QPointF)), this, SLOT(slotViewContextMenuRequested(QPointF))); + connect(m_controller, SIGNAL(itemDropEvent(int,QGraphicsSceneDragDropEvent*)), this, SLOT(slotItemDropEvent(int,QGraphicsSceneDragDropEvent*))); + + KItemListContainer* container = new KItemListContainer(m_controller, this); + container->setEnabledFrame(false); + + QVBoxLayout* layout = new QVBoxLayout(this); + layout->setMargin(0); + layout->addWidget(container); + } + + loadTree(url()); + Panel::showEvent(event); +} + +void FoldersPanel::keyPressEvent(QKeyEvent* event) +{ + const int key = event->key(); + if ((key == Qt::Key_Enter) || (key == Qt::Key_Return)) { + event->accept(); + } else { + Panel::keyPressEvent(event); + } +} + +void FoldersPanel::slotItemActivated(int index) +{ + const KFileItem item = m_model->fileItem(index); + if (!item.isNull()) { + emit folderActivated(item.url()); + } +} + +void FoldersPanel::slotItemMiddleClicked(int index) +{ + const KFileItem item = m_model->fileItem(index); + if (!item.isNull()) { + emit folderMiddleClicked(item.url()); + } +} + +void FoldersPanel::slotItemContextMenuRequested(int index, const QPointF& pos) +{ + Q_UNUSED(pos); + + const KFileItem fileItem = m_model->fileItem(index); + + QWeakPointer contextMenu = new TreeViewContextMenu(this, fileItem); + contextMenu.data()->open(); + if (contextMenu.data()) { + delete contextMenu.data(); + } +} + +void FoldersPanel::slotViewContextMenuRequested(const QPointF& pos) +{ + Q_UNUSED(pos); + + QWeakPointer contextMenu = new TreeViewContextMenu(this, KFileItem()); + contextMenu.data()->open(); + if (contextMenu.data()) { + delete contextMenu.data(); + } +} + +void FoldersPanel::slotItemDropEvent(int index, QGraphicsSceneDragDropEvent* event) +{ + if (index >= 0) { + KFileItem destItem = m_model->fileItem(index); + if (destItem.isNull()) { + return; + } + + QDropEvent dropEvent(event->pos().toPoint(), + event->possibleActions(), + event->mimeData(), + event->buttons(), + event->modifiers()); + + QString error; + DragAndDropHelper::dropUrls(destItem, destItem.url(), &dropEvent, error); + if (!error.isEmpty()) { + emit errorMessage(error); + } + } +} + +void FoldersPanel::slotRoleEditingFinished(int index, const QByteArray& role, const QVariant& value) +{ + if (role == "text") { + const KFileItem item = m_model->fileItem(index); + const QString newName = value.toString(); + if (!newName.isEmpty() && newName != item.text() && newName != QLatin1String(".") && newName != QLatin1String("..")) { + KonqOperations::rename(this, item.url(), newName); + } + } +} + +void FoldersPanel::slotLoadingCompleted() +{ + if (m_controller->view()->opacity() == 0) { + // The loading of the initial tree after opening the Folders panel + // has been finished. Trigger the increasing of the opacity after + // a short delay to give the view the chance to finish its internal + // animations. + // TODO: Check whether it makes sense to allow accessing the + // view-internal delay for usecases like this. + QTimer::singleShot(250, this, SLOT(startFadeInAnimation())); + } + + if (!m_updateCurrentItem) { + return; + } + + const int index = m_model->index(url()); + updateCurrentItem(index); + m_updateCurrentItem = false; +} + +void FoldersPanel::startFadeInAnimation() +{ + QPropertyAnimation* anim = new QPropertyAnimation(m_controller->view(), "opacity", this); + anim->setStartValue(0); + anim->setEndValue(1); + anim->setEasingCurve(QEasingCurve::InOutQuad); + anim->start(QAbstractAnimation::DeleteWhenStopped); + anim->setDuration(200); +} + +void FoldersPanel::loadTree(const KUrl& url) +{ + Q_ASSERT(m_controller); + + m_updateCurrentItem = false; + + KUrl baseUrl; + if (url.isLocalFile()) { + // Use the root directory as base for local URLs (#150941) + baseUrl = QDir::rootPath(); + } else { + // Clear the path for non-local URLs and use it as base + baseUrl = url; + baseUrl.setPath(QString('/')); + } + + if (m_model->directory() != baseUrl) { + m_updateCurrentItem = true; + m_model->refreshDirectory(baseUrl); + } + + const int index = m_model->index(url); + if (index >= 0) { + updateCurrentItem(index); + } else { + m_updateCurrentItem = true; + m_model->expandParentDirectories(url); + // slotLoadingCompleted() will be invoked after the model has + // expanded the url + } +} + +void FoldersPanel::updateCurrentItem(int index) +{ + KItemListSelectionManager* selectionManager = m_controller->selectionManager(); + selectionManager->setCurrentItem(index); + selectionManager->clearSelection(); + selectionManager->setSelected(index); + + m_controller->view()->scrollToItem(index); +} + +#include "moc_folderspanel.cpp" diff --git a/dolphin/src/panels/folders/folderspanel.h b/dolphin/src/panels/folders/folderspanel.h new file mode 100644 index 00000000..0015df08 --- /dev/null +++ b/dolphin/src/panels/folders/folderspanel.h @@ -0,0 +1,104 @@ +/*************************************************************************** + * Copyright (C) 2006 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef FOLDERSPANEL_H +#define FOLDERSPANEL_H + +#include +#include + +class KFileItemModel; +class KItemListController; +#include + +/** + * @brief Shows a tree view of the directories starting from + * the currently selected place. + * + * The tree view is always synchronized with the currently active view + * from the main window. + */ +class FoldersPanel : public Panel +{ + Q_OBJECT + +public: + FoldersPanel(QWidget* parent = 0); + virtual ~FoldersPanel(); + + void setShowHiddenFiles(bool show); + bool showHiddenFiles() const; + + void setAutoScrolling(bool enable); + bool autoScrolling() const; + + void rename(const KFileItem& item); + +signals: + void folderActivated(const KUrl& url); + void folderMiddleClicked(const KUrl& url); + void errorMessage(const QString& error); + +protected: + /** @see Panel::urlChanged() */ + virtual bool urlChanged(); + + /** @see QWidget::showEvent() */ + virtual void showEvent(QShowEvent* event); + + /** @see QWidget::keyPressEvent() */ + virtual void keyPressEvent(QKeyEvent* event); + +private slots: + void slotItemActivated(int index); + void slotItemMiddleClicked(int index); + void slotItemContextMenuRequested(int index, const QPointF& pos); + void slotViewContextMenuRequested(const QPointF& pos); + void slotItemDropEvent(int index, QGraphicsSceneDragDropEvent* event); + void slotRoleEditingFinished(int index, const QByteArray& role, const QVariant& value); + + void slotLoadingCompleted(); + + /** + * Increases the opacity of the view step by step until it is fully + * opaque. + */ + void startFadeInAnimation(); + +private: + /** + * Initializes the base URL of the tree and expands all + * directories until \a url. + * @param url URL of the leaf directory that should get expanded. + */ + void loadTree(const KUrl& url); + + /** + * Sets the item with the index \a index as current item, selects + * the item and assures that the item will be visible. + */ + void updateCurrentItem(int index); + +private: + bool m_updateCurrentItem; + KItemListController* m_controller; + KFileItemModel* m_model; +}; + +#endif // FOLDERSPANEL_H diff --git a/dolphin/src/panels/folders/treeviewcontextmenu.cpp b/dolphin/src/panels/folders/treeviewcontextmenu.cpp new file mode 100644 index 00000000..7f4c7f32 --- /dev/null +++ b/dolphin/src/panels/folders/treeviewcontextmenu.cpp @@ -0,0 +1,209 @@ +/*************************************************************************** + * Copyright (C) 2006-2010 by Peter Penz * + * Copyright (C) 2006 by Cvetoslav Ludmiloff * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "treeviewcontextmenu.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "folderspanel.h" + +#include +#include + +TreeViewContextMenu::TreeViewContextMenu(FoldersPanel* parent, + const KFileItem& fileInfo) : + QObject(parent), + m_parent(parent), + m_fileItem(fileInfo) +{ +} + +TreeViewContextMenu::~TreeViewContextMenu() +{ +} + +void TreeViewContextMenu::open() +{ + KMenu* popup = new KMenu(m_parent); + + if (!m_fileItem.isNull()) { + KFileItemListProperties capabilities(KFileItemList() << m_fileItem); + + // insert 'Cut', 'Copy' and 'Paste' + QAction* cutAction = new QAction(KIcon("edit-cut"), i18nc("@action:inmenu", "Cut"), this); + cutAction->setEnabled(capabilities.supportsMoving()); + connect(cutAction, SIGNAL(triggered()), this, SLOT(cut())); + + QAction* copyAction = new QAction(KIcon("edit-copy"), i18nc("@action:inmenu", "Copy"), this); + connect(copyAction, SIGNAL(triggered()), this, SLOT(copy())); + + const QPair pasteInfo = KonqOperations::pasteInfo(m_fileItem.url()); + QAction* pasteAction = new QAction(KIcon("edit-paste"), pasteInfo.second, this); + connect(pasteAction, SIGNAL(triggered()), this, SLOT(paste())); + pasteAction->setEnabled(pasteInfo.first); + + popup->addAction(cutAction); + popup->addAction(copyAction); + popup->addAction(pasteAction); + popup->addSeparator(); + + // insert 'Rename' + QAction* renameAction = new QAction(i18nc("@action:inmenu", "Rename..."), this); + renameAction->setEnabled(capabilities.supportsMoving()); + renameAction->setIcon(KIcon("edit-rename")); + connect(renameAction, SIGNAL(triggered()), this, SLOT(rename())); + popup->addAction(renameAction); + + // insert 'Move to Trash' and (optionally) 'Delete' + KSharedConfig::Ptr globalConfig = KSharedConfig::openConfig("kdeglobals", KConfig::IncludeGlobals); + KConfigGroup configGroup(globalConfig, "KDE"); + bool showDeleteCommand = configGroup.readEntry("ShowDeleteCommand", false); + + const KUrl url = m_fileItem.url(); + if (url.isLocalFile()) { + QAction* moveToTrashAction = new QAction(KIcon("user-trash"), + i18nc("@action:inmenu", "Move to Trash"), this); + const bool enableMoveToTrash = capabilities.isLocal() && capabilities.supportsMoving(); + moveToTrashAction->setEnabled(enableMoveToTrash); + connect(moveToTrashAction, SIGNAL(triggered()), this, SLOT(moveToTrash())); + popup->addAction(moveToTrashAction); + } else { + showDeleteCommand = true; + } + + if (showDeleteCommand) { + QAction* deleteAction = new QAction(KIcon("edit-delete"), i18nc("@action:inmenu", "Delete"), this); + deleteAction->setEnabled(capabilities.supportsDeleting()); + connect(deleteAction, SIGNAL(triggered()), this, SLOT(deleteItem())); + popup->addAction(deleteAction); + } + + popup->addSeparator(); + } + + // insert 'Show Hidden Files' + QAction* showHiddenFilesAction = new QAction(i18nc("@action:inmenu", "Show Hidden Files"), this); + showHiddenFilesAction->setCheckable(true); + showHiddenFilesAction->setChecked(m_parent->showHiddenFiles()); + popup->addAction(showHiddenFilesAction); + connect(showHiddenFilesAction, SIGNAL(toggled(bool)), this, SLOT(setShowHiddenFiles(bool))); + + // insert 'Automatic Scrolling' + QAction* autoScrollingAction = new QAction(i18nc("@action:inmenu", "Automatic Scrolling"), this); + autoScrollingAction->setCheckable(true); + autoScrollingAction->setChecked(m_parent->autoScrolling()); + // TODO: Temporary disabled. Horizontal autoscrolling will be implemented later either + // in KItemViews or manually as part of the FoldersPanel + //popup->addAction(autoScrollingAction); + connect(autoScrollingAction, SIGNAL(toggled(bool)), this, SLOT(setAutoScrolling(bool))); + + if (!m_fileItem.isNull()) { + // insert 'Properties' entry + QAction* propertiesAction = new QAction(i18nc("@action:inmenu", "Properties"), this); + propertiesAction->setIcon(KIcon("document-properties")); + connect(propertiesAction, SIGNAL(triggered()), this, SLOT(showProperties())); + popup->addAction(propertiesAction); + } + + QList customActions = m_parent->customContextMenuActions(); + if (!customActions.isEmpty()) { + popup->addSeparator(); + foreach (QAction* action, customActions) { + popup->addAction(action); + } + } + + QWeakPointer popupPtr = popup; + popup->exec(QCursor::pos()); + if (popupPtr.data()) { + popupPtr.data()->deleteLater(); + } +} + +void TreeViewContextMenu::populateMimeData(QMimeData* mimeData, bool cut) +{ + KUrl::List kdeUrls; + kdeUrls.append(m_fileItem.url()); + KUrl::List mostLocalUrls; + bool dummy; + mostLocalUrls.append(m_fileItem.mostLocalUrl(dummy)); + KonqMimeData::populateMimeData(mimeData, kdeUrls, mostLocalUrls, cut); +} + +void TreeViewContextMenu::cut() +{ + QMimeData* mimeData = new QMimeData(); + populateMimeData(mimeData, true); + QApplication::clipboard()->setMimeData(mimeData); +} + +void TreeViewContextMenu::copy() +{ + QMimeData* mimeData = new QMimeData(); + populateMimeData(mimeData, false); + QApplication::clipboard()->setMimeData(mimeData); +} + +void TreeViewContextMenu::paste() +{ + KonqOperations::doPaste(m_parent, m_fileItem.url()); +} + +void TreeViewContextMenu::rename() +{ + m_parent->rename(m_fileItem); +} + +void TreeViewContextMenu::moveToTrash() +{ + KonqOperations::del(m_parent, KonqOperations::TRASH, m_fileItem.url()); +} + +void TreeViewContextMenu::deleteItem() +{ + KonqOperations::del(m_parent, KonqOperations::DEL, m_fileItem.url()); +} + +void TreeViewContextMenu::showProperties() +{ + KPropertiesDialog* dialog = new KPropertiesDialog(m_fileItem.url(), m_parent); + dialog->setAttribute(Qt::WA_DeleteOnClose); + dialog->show(); +} + +void TreeViewContextMenu::setShowHiddenFiles(bool show) +{ + m_parent->setShowHiddenFiles(show); +} + +void TreeViewContextMenu::setAutoScrolling(bool enable) +{ + m_parent->setAutoScrolling(enable); +} + +#include "moc_treeviewcontextmenu.cpp" diff --git a/dolphin/src/panels/folders/treeviewcontextmenu.h b/dolphin/src/panels/folders/treeviewcontextmenu.h new file mode 100644 index 00000000..0b3fd79b --- /dev/null +++ b/dolphin/src/panels/folders/treeviewcontextmenu.h @@ -0,0 +1,94 @@ +/*************************************************************************** + * Copyright (C) 2006 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef TREEVIEWCONTEXTMENU_H +#define TREEVIEWCONTEXTMENU_H + +#include +#include + +class FoldersPanel; + +/** + * @brief Represents the context menu which appears when doing a right + * click on an item of the treeview. + */ +class TreeViewContextMenu : public QObject +{ + Q_OBJECT + +public: + /** + * @parent Pointer to the folders panel the context menu + * belongs to. + * @fileInfo Pointer to the file item the context menu + * is applied. If 0 is passed, the context menu + * is above the viewport. + */ + TreeViewContextMenu(FoldersPanel* parent, + const KFileItem& fileInfo); + + virtual ~TreeViewContextMenu(); + + /** Opens the context menu modal. */ + void open(); + +private slots: + /** Cuts the item m_fileItem. */ + void cut(); + + /** Copies the item m_fileItem. */ + void copy(); + + /** Paste the clipboard to m_fileItem. */ + void paste(); + + /** Renames the item m_fileItem. */ + void rename(); + + /** Moves the item m_fileItem to the trash. */ + void moveToTrash(); + + /** Deletes the item m_fileItem. */ + void deleteItem(); + + /** Shows the properties of the item m_fileItem. */ + void showProperties(); + + /** + * Sets the 'Show Hidden Files' setting for the + * folders panel to \a show. + */ + void setShowHiddenFiles(bool show); + + /** + * Sets the 'Automatic Scrolling' setting for the + * folders panel to \a enable. + */ + void setAutoScrolling(bool enable); + +private: + void populateMimeData(QMimeData* mimeData, bool cut); + +private: + FoldersPanel* m_parent; + KFileItem m_fileItem; +}; + +#endif diff --git a/dolphin/src/panels/information/dolphin_informationpanelsettings.kcfg b/dolphin/src/panels/information/dolphin_informationpanelsettings.kcfg new file mode 100644 index 00000000..53c756d2 --- /dev/null +++ b/dolphin/src/panels/information/dolphin_informationpanelsettings.kcfg @@ -0,0 +1,14 @@ + + + + + + + + true + + + diff --git a/dolphin/src/panels/information/dolphin_informationpanelsettings.kcfgc b/dolphin/src/panels/information/dolphin_informationpanelsettings.kcfgc new file mode 100644 index 00000000..84a2cff5 --- /dev/null +++ b/dolphin/src/panels/information/dolphin_informationpanelsettings.kcfgc @@ -0,0 +1,4 @@ +File=dolphin_informationpanelsettings.kcfg +ClassName=InformationPanelSettings +Singleton=true +Mutators=true diff --git a/dolphin/src/panels/information/filemetadataconfigurationdialog.cpp b/dolphin/src/panels/information/filemetadataconfigurationdialog.cpp new file mode 100644 index 00000000..320722fc --- /dev/null +++ b/dolphin/src/panels/information/filemetadataconfigurationdialog.cpp @@ -0,0 +1,94 @@ +/*************************************************************************** + * Copyright (C) 2010 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "filemetadataconfigurationdialog.h" + +#include + +#include +#include +#include + +FileMetaDataConfigurationDialog::FileMetaDataConfigurationDialog(QWidget* parent) : + KDialog(parent), + m_descriptionLabel(0), + m_configWidget(0) + +{ + setCaption(i18nc("@title:window", "Configure Shown Data")); + setButtons(KDialog::Ok | KDialog::Cancel); + setDefaultButton(KDialog::Ok); + + m_descriptionLabel = new QLabel(i18nc("@label::textbox", + "Select which data should " + "be shown:"), this); + m_descriptionLabel->setWordWrap(true); + + m_configWidget = new KFileMetaDataConfigurationWidget(this); + + + QWidget* mainWidget = new QWidget(this); + QVBoxLayout* topLayout = new QVBoxLayout(mainWidget); + topLayout->addWidget(m_descriptionLabel); + topLayout->addWidget(m_configWidget); + setMainWidget(mainWidget); + + const KConfigGroup dialogConfig(KSharedConfig::openConfig("dolphinrc"), + "FileMetaDataConfigurationDialog"); + restoreDialogSize(dialogConfig); +} + +FileMetaDataConfigurationDialog::~FileMetaDataConfigurationDialog() +{ + KConfigGroup dialogConfig(KSharedConfig::openConfig("dolphinrc"), + "FileMetaDataConfigurationDialog"); + saveDialogSize(dialogConfig, KConfigBase::Persistent); +} + +void FileMetaDataConfigurationDialog::setItems(const KFileItemList& items) +{ + m_configWidget->setItems(items); +} + +KFileItemList FileMetaDataConfigurationDialog::items() const +{ + return m_configWidget->items(); +} + +void FileMetaDataConfigurationDialog::slotButtonClicked(int button) +{ + if (button == KDialog::Ok) { + m_configWidget->save(); + accept(); + } else { + KDialog::slotButtonClicked(button); + } +} + +void FileMetaDataConfigurationDialog::setDescription(const QString& description) +{ + m_descriptionLabel->setText(description); +} + +QString FileMetaDataConfigurationDialog::description() const +{ + return m_descriptionLabel->text(); +} + +#include "moc_filemetadataconfigurationdialog.cpp" diff --git a/dolphin/src/panels/information/filemetadataconfigurationdialog.h b/dolphin/src/panels/information/filemetadataconfigurationdialog.h new file mode 100644 index 00000000..0b89d917 --- /dev/null +++ b/dolphin/src/panels/information/filemetadataconfigurationdialog.h @@ -0,0 +1,70 @@ +/*************************************************************************** + * Copyright (C) 2010 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef FILEMETADATACONFIGURATIONDIALOG_H +#define FILEMETADATACONFIGURATIONDIALOG_H + +#include +#include + +class KFileMetaDataConfigurationWidget; + +#include + +/** + * @brief Dialog which allows to configure which meta data should be shown + * in the KFileMetaDataWidget. + */ +class FileMetaDataConfigurationDialog : public KDialog +{ + Q_OBJECT + +public: + explicit FileMetaDataConfigurationDialog(QWidget* parent = 0); + virtual ~FileMetaDataConfigurationDialog(); + + /** + * Sets the items, for which the visibility of the meta data should + * be configured. Note that the visibility of the meta data is not + * bound to the items itself, the items are only used to determine + * which meta data should be configurable. For example when a JPEG image + * is set as item, it will be configurable which EXIF data should be + * shown. If an audio file is set as item, it will be configurable + * whether the artist, album name, ... should be shown. + */ + void setItems(const KFileItemList& items); + KFileItemList items() const; + + /** + * Sets the description that is shown above the list + * of meta data. Per default the translated text for + * "Select which data should be shown." is set. + */ + void setDescription(const QString& description); + QString description() const; + +protected slots: + virtual void slotButtonClicked(int button); + +private: + QLabel* m_descriptionLabel; + KFileMetaDataConfigurationWidget* m_configWidget; +}; + +#endif diff --git a/dolphin/src/panels/information/informationpanel.cpp b/dolphin/src/panels/information/informationpanel.cpp new file mode 100644 index 00000000..d74c61dc --- /dev/null +++ b/dolphin/src/panels/information/informationpanel.cpp @@ -0,0 +1,363 @@ +/*************************************************************************** + * Copyright (C) 2006-2009 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "informationpanel.h" + +#include "informationpanelcontent.h" +#include +#include +#include +#include +#include +#include +#include + +InformationPanel::InformationPanel(QWidget* parent) : + Panel(parent), + m_initialized(false), + m_infoTimer(0), + m_urlChangedTimer(0), + m_resetUrlTimer(0), + m_shownUrl(), + m_urlCandidate(), + m_invalidUrlCandidate(), + m_fileItem(), + m_selection(), + m_folderStatJob(0), + m_content(0) +{ +} + +InformationPanel::~InformationPanel() +{ +} + +void InformationPanel::setSelection(const KFileItemList& selection) +{ + m_selection = selection; + m_fileItem = KFileItem(); + + if (!isVisible()) { + return; + } + + const int count = selection.count(); + if (count == 0) { + if (!isEqualToShownUrl(url())) { + m_shownUrl = url(); + showItemInfo(); + } + } else { + if ((count == 1) && !selection.first().url().isEmpty()) { + m_urlCandidate = selection.first().url(); + } + m_infoTimer->start(); + } +} + +void InformationPanel::requestDelayedItemInfo(const KFileItem& item) +{ + if (!isVisible() || (item.isNull() && m_fileItem.isNull())) { + return; + } + + if (QApplication::mouseButtons() & Qt::LeftButton) { + // Ignore the request of an item information when a rubberband + // selection is ongoing. + return; + } + + cancelRequest(); + + if (item.isNull()) { + // The cursor is above the viewport. If files are selected, + // show information regarding the selection. + if (m_selection.size() > 0) { + m_fileItem = KFileItem(); + m_infoTimer->start(); + } + } else if (item.url().isValid() && !isEqualToShownUrl(item.url())) { + // The cursor is above an item that is not shown currently + m_urlCandidate = item.url(); + m_fileItem = item; + m_infoTimer->start(); + } +} + +bool InformationPanel::urlChanged() +{ + if (!url().isValid()) { + return false; + } + + if (!isVisible()) { + return true; + } + + cancelRequest(); + m_selection.clear(); + + if (!isEqualToShownUrl(url())) { + m_shownUrl = url(); + m_fileItem = KFileItem(); + + // Update the content with a delay. This gives + // the directory lister the chance to show the content + // before expensive operations are done to show + // meta information. + m_urlChangedTimer->start(); + } + + return true; +} + +void InformationPanel::showEvent(QShowEvent* event) +{ + Panel::showEvent(event); + if (!event->spontaneous()) { + if (!m_initialized) { + // do a delayed initialization so that no performance + // penalty is given when Dolphin is started with a closed + // Information Panel + init(); + } + + m_shownUrl = url(); + showItemInfo(); + } +} + +void InformationPanel::resizeEvent(QResizeEvent* event) +{ + if (isVisible()) { + m_urlCandidate = m_shownUrl; + m_infoTimer->start(); + } + Panel::resizeEvent(event); +} + +void InformationPanel::contextMenuEvent(QContextMenuEvent* event) +{ + // TODO: Move code from InformationPanelContent::configureSettings() here + m_content->configureSettings(customContextMenuActions()); + Panel::contextMenuEvent(event); +} + +void InformationPanel::showItemInfo() +{ + if (!isVisible()) { + return; + } + + cancelRequest(); + + if (m_fileItem.isNull() && (m_selection.count() > 1)) { + // The information for a selection of items should be shown + m_content->showItems(m_selection); + } else { + // The information for exactly one item should be shown + KFileItem item; + if (!m_fileItem.isNull()) { + item = m_fileItem; + } else if (!m_selection.isEmpty()) { + Q_ASSERT(m_selection.count() == 1); + item = m_selection.first(); + } + + if (item.isNull()) { + // No item is hovered and no selection has been done: provide + // an item for the currently shown directory. + m_folderStatJob = KIO::stat(url(), KIO::HideProgressInfo); + if (m_folderStatJob->ui()) { + m_folderStatJob->ui()->setWindow(this); + } + connect(m_folderStatJob, SIGNAL(result(KJob*)), + this, SLOT(slotFolderStatFinished(KJob*))); + } else { + m_content->showItem(item); + } + } +} + +void InformationPanel::slotFolderStatFinished(KJob* job) +{ + m_folderStatJob = 0; + const KIO::UDSEntry entry = static_cast(job)->statResult(); + m_content->showItem(KFileItem(entry, m_shownUrl)); +} + +void InformationPanel::slotInfoTimeout() +{ + m_shownUrl = m_urlCandidate; + m_urlCandidate.clear(); + showItemInfo(); +} + +void InformationPanel::reset() +{ + if (m_invalidUrlCandidate == m_shownUrl) { + m_invalidUrlCandidate = KUrl(); + + // The current URL is still invalid. Reset + // the content to show the directory URL. + m_selection.clear(); + m_shownUrl = url(); + m_fileItem = KFileItem(); + showItemInfo(); + } +} + +void InformationPanel::slotFileRenamed(const QString& source, const QString& dest) +{ + if (m_shownUrl == KUrl(source)) { + m_shownUrl = KUrl(dest); + m_fileItem = KFileItem(KFileItem::Unknown, KFileItem::Unknown, m_shownUrl); + + if ((m_selection.count() == 1) && (m_selection[0].url() == KUrl(source))) { + m_selection[0] = m_fileItem; + // Implementation note: Updating the selection is only required if exactly one + // item is selected, as the name of the item is shown. If this should change + // in future: Before parsing the whole selection take care to test possible + // performance bottlenecks when renaming several hundreds of files. + } + + showItemInfo(); + } +} + +void InformationPanel::slotFilesAdded(const QString& directory) +{ + if (m_shownUrl == KUrl(directory)) { + // If the 'trash' icon changes because the trash has been emptied or got filled, + // the signal filesAdded("trash:/") will be emitted. + KFileItem item(KFileItem::Unknown, KFileItem::Unknown, KUrl(directory)); + requestDelayedItemInfo(item); + } +} + +void InformationPanel::slotFilesChanged(const QStringList& files) +{ + foreach (const QString& fileName, files) { + if (m_shownUrl == KUrl(fileName)) { + showItemInfo(); + break; + } + } +} + +void InformationPanel::slotFilesRemoved(const QStringList& files) +{ + foreach (const QString& fileName, files) { + if (m_shownUrl == KUrl(fileName)) { + // the currently shown item has been removed, show + // the parent directory as fallback + markUrlAsInvalid(); + break; + } + } +} + +void InformationPanel::slotEnteredDirectory(const QString& directory) +{ + if (m_shownUrl == KUrl(directory)) { + KFileItem item(KFileItem::Unknown, KFileItem::Unknown, KUrl(directory)); + requestDelayedItemInfo(item); + } +} + +void InformationPanel::slotLeftDirectory(const QString& directory) +{ + if (m_shownUrl == KUrl(directory)) { + // The signal 'leftDirectory' is also emitted when a media + // has been unmounted. In this case no directory change will be + // done in Dolphin, but the Information Panel must be updated to + // indicate an invalid directory. + markUrlAsInvalid(); + } +} + +void InformationPanel::cancelRequest() +{ + delete m_folderStatJob; + m_folderStatJob = 0; + + m_infoTimer->stop(); + m_resetUrlTimer->stop(); + // Don't reset m_urlChangedTimer. As it is assured that the timeout of m_urlChangedTimer + // has the smallest interval (see init()), it is not possible that the exceeded timer + // would overwrite an information provided by a selection or hovering. + + m_invalidUrlCandidate.clear(); + m_urlCandidate.clear(); +} + +bool InformationPanel::isEqualToShownUrl(const KUrl& url) const +{ + return m_shownUrl.equals(url, KUrl::CompareWithoutTrailingSlash); +} + +void InformationPanel::markUrlAsInvalid() +{ + m_invalidUrlCandidate = m_shownUrl; + m_resetUrlTimer->start(); +} + +void InformationPanel::init() +{ + m_infoTimer = new QTimer(this); + m_infoTimer->setInterval(300); + m_infoTimer->setSingleShot(true); + connect(m_infoTimer, SIGNAL(timeout()), + this, SLOT(slotInfoTimeout())); + + m_urlChangedTimer = new QTimer(this); + m_urlChangedTimer->setInterval(200); + m_urlChangedTimer->setSingleShot(true); + connect(m_urlChangedTimer, SIGNAL(timeout()), + this, SLOT(showItemInfo())); + + m_resetUrlTimer = new QTimer(this); + m_resetUrlTimer->setInterval(1000); + m_resetUrlTimer->setSingleShot(true); + connect(m_resetUrlTimer, SIGNAL(timeout()), + this, SLOT(reset())); + + Q_ASSERT(m_urlChangedTimer->interval() < m_infoTimer->interval()); + Q_ASSERT(m_urlChangedTimer->interval() < m_resetUrlTimer->interval()); + + org::kde::KDirNotify* dirNotify = new org::kde::KDirNotify(QString(), QString(), + QDBusConnection::sessionBus(), this); + connect(dirNotify, SIGNAL(FileRenamed(QString,QString)), SLOT(slotFileRenamed(QString,QString))); + connect(dirNotify, SIGNAL(FilesAdded(QString)), SLOT(slotFilesAdded(QString))); + connect(dirNotify, SIGNAL(FilesChanged(QStringList)), SLOT(slotFilesChanged(QStringList))); + connect(dirNotify, SIGNAL(FilesRemoved(QStringList)), SLOT(slotFilesRemoved(QStringList))); + connect(dirNotify, SIGNAL(enteredDirectory(QString)), SLOT(slotEnteredDirectory(QString))); + connect(dirNotify, SIGNAL(leftDirectory(QString)), SLOT(slotLeftDirectory(QString))); + + m_content = new InformationPanelContent(this); + connect(m_content, SIGNAL(urlActivated(KUrl)), this, SIGNAL(urlActivated(KUrl))); + + QVBoxLayout* layout = new QVBoxLayout(this); + layout->setContentsMargins(0, 0, 0, 0); + layout->addWidget(m_content); + + m_initialized = true; +} + +#include "moc_informationpanel.cpp" diff --git a/dolphin/src/panels/information/informationpanel.h b/dolphin/src/panels/information/informationpanel.h new file mode 100644 index 00000000..7809a0a8 --- /dev/null +++ b/dolphin/src/panels/information/informationpanel.h @@ -0,0 +1,160 @@ +/*************************************************************************** + * Copyright (C) 2006-2009 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef INFORMATIONPANEL_H +#define INFORMATIONPANEL_H + +#include +#include + +class InformationPanelContent; +namespace KIO +{ + class Job; +} + +/** + * @brief Panel for showing meta information of one ore more selected items. + */ +class InformationPanel : public Panel +{ + Q_OBJECT + +public: + explicit InformationPanel(QWidget* parent = 0); + virtual ~InformationPanel(); + +signals: + void urlActivated(const KUrl& url); + +public slots: + /** + * This is invoked to inform the panel that the user has selected a new + * set of items. + */ + void setSelection(const KFileItemList& selection); + + /** + * Does a delayed request of information for the item \a item. + * If within this delay InformationPanel::setUrl() or InformationPanel::setSelection() + * are invoked, then the request will be skipped. Requesting a delayed item information + * makes sense when hovering items. + */ + void requestDelayedItemInfo(const KFileItem& item); + +protected: + /** @see Panel::urlChanged() */ + virtual bool urlChanged(); + + /** @see QWidget::showEvent() */ + virtual void showEvent(QShowEvent* event); + + /** @see QWidget::resizeEvent() */ + virtual void resizeEvent(QResizeEvent* event); + + /** @see QWidget::contextMenuEvent() */ + virtual void contextMenuEvent(QContextMenuEvent* event); + +private slots: + /** + * Shows the information for the item of the URL which has been provided by + * InformationPanel::requestItemInfo() and provides default actions. + */ + void showItemInfo(); + + /** + * Shows the information for the currently displayed folder as a result from + * a stat job issued in showItemInfo(). + */ + void slotFolderStatFinished(KJob* job); + + /** + * Triggered if the request for item information has timed out. + * @see InformationPanel::requestDelayedItemInfo() + */ + void slotInfoTimeout(); + + /** + * Resets the information panel to show the current + * URL (InformationPanel::url()). Is called by + * DolphinInformationPanel::markUrlAsInvalid(). + */ + void reset(); + + void slotFileRenamed(const QString& source, const QString& dest); + void slotFilesAdded(const QString& directory); + void slotFilesChanged(const QStringList& files); + void slotFilesRemoved(const QStringList& files); + void slotEnteredDirectory(const QString& directory); + void slotLeftDirectory(const QString& directory); + +private: + /** Assures that any pending item information request is cancelled. */ + void cancelRequest(); + + /** + * Shows the meta information for the current shown item inside + * a label. + */ + void showMetaInfo(); + + /** + * Returns true, if \a url is equal to the shown URL m_shownUrl. + */ + bool isEqualToShownUrl(const KUrl& url) const; + + /** + * Marks the URL as invalid and will reset the Information Panel + * after a short delay. The reset is not done synchronously to + * prevent expensive updates during temporary invalid URLs by + * e. g. changing the directory. + */ + void markUrlAsInvalid(); + + void init(); + +private: + bool m_initialized; + bool m_pendingPreview; + QTimer* m_infoTimer; + QTimer* m_urlChangedTimer; + QTimer* m_resetUrlTimer; + + // URL that is currently shown in the Information Panel. + KUrl m_shownUrl; + + // URL candidate that will replace m_shownURL after a delay. + // Used to remember URLs when hovering items. + KUrl m_urlCandidate; + + // URL candidate that is marked as invalid (e. g. because the directory + // has been deleted or the shown item has been renamed). The Information + // Panel will be reset asynchronously to prevent unnecessary resets when + // a directory has been changed. + KUrl m_invalidUrlCandidate; + + KFileItem m_fileItem; // file item for m_shownUrl if available (otherwise null) + KFileItemList m_selection; + + KIO::Job* m_folderStatJob; + + InformationPanelContent* m_content; +}; + +#endif // INFORMATIONPANEL_H diff --git a/dolphin/src/panels/information/informationpanelcontent.cpp b/dolphin/src/panels/information/informationpanelcontent.cpp new file mode 100644 index 00000000..e2a57baa --- /dev/null +++ b/dolphin/src/panels/information/informationpanelcontent.cpp @@ -0,0 +1,429 @@ +/*************************************************************************** + * Copyright (C) 2009 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "informationpanelcontent.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dolphin_informationpanelsettings.h" +#include "filemetadataconfigurationdialog.h" +#include "pixmapviewer.h" + +InformationPanelContent::InformationPanelContent(QWidget* parent) : + QWidget(parent), + m_item(), + m_previewJob(0), + m_outdatedPreviewTimer(0), + m_preview(0), + m_playerWidget(0), + m_nameLabel(0), + m_metaDataWidget(0), + m_metaDataArea(0), + m_placesItemModel(0) +{ + parent->installEventFilter(this); + + // Initialize timer for disabling an outdated preview with a small + // delay. This prevents flickering if the new preview can be generated + // within a very small timeframe. + m_outdatedPreviewTimer = new QTimer(this); + m_outdatedPreviewTimer->setInterval(300); + m_outdatedPreviewTimer->setSingleShot(true); + connect(m_outdatedPreviewTimer, SIGNAL(timeout()), + this, SLOT(markOutdatedPreview())); + + QVBoxLayout* layout = new QVBoxLayout(this); + layout->setSpacing(KDialog::spacingHint()); + + // preview + const int minPreviewWidth = KIconLoader::SizeEnormous + KIconLoader::SizeMedium; + + m_preview = new PixmapViewer(parent); + m_preview->setMinimumWidth(minPreviewWidth); + m_preview->setMinimumHeight(KIconLoader::SizeEnormous); + + m_playerWidget = new KMediaWidget(parent); + m_playerWidget->player()->setPlayerID("informationpanel"); + m_playerWidget->hide(); + m_playerWidget->setMinimumWidth(minPreviewWidth); + + // name + m_nameLabel = new QLabel(parent); + QFont font = m_nameLabel->font(); + font.setBold(true); + m_nameLabel->setFont(font); + m_nameLabel->setTextFormat(Qt::PlainText); + m_nameLabel->setAlignment(Qt::AlignHCenter); + m_nameLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + + m_preview->setVisible(InformationPanelSettings::previewsShown()); + + m_metaDataWidget = new KFileMetaDataWidget(parent); + m_metaDataWidget->setFont(KGlobalSettings::smallestReadableFont()); + m_metaDataWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum); + connect(m_metaDataWidget, SIGNAL(urlActivated(KUrl)), this, SIGNAL(urlActivated(KUrl))); + + // Encapsulate the MetaDataWidget inside a container that has a dummy widget + // at the bottom. This prevents that the meta data widget gets vertically stretched + // in the case where the height of m_metaDataArea > m_metaDataWidget. + QWidget* metaDataWidgetContainer = new QWidget(parent); + QVBoxLayout* containerLayout = new QVBoxLayout(metaDataWidgetContainer); + containerLayout->setContentsMargins(0, 0, 0, 0); + containerLayout->setSpacing(0); + containerLayout->addWidget(m_metaDataWidget); + containerLayout->addStretch(); + + m_metaDataArea = new QScrollArea(parent); + m_metaDataArea->setWidget(metaDataWidgetContainer); + m_metaDataArea->setWidgetResizable(true); + m_metaDataArea->setFrameShape(QFrame::NoFrame); + + QWidget* viewport = m_metaDataArea->viewport(); + viewport->installEventFilter(this); + + QPalette palette = viewport->palette(); + palette.setColor(viewport->backgroundRole(), QColor(Qt::transparent)); + viewport->setPalette(palette); + + layout->addWidget(m_preview); + layout->addWidget(m_playerWidget); + layout->addWidget(m_nameLabel); + layout->addWidget(new KSeparator()); + layout->addWidget(m_metaDataArea); + + m_placesItemModel = new PlacesItemModel(this); +} + +InformationPanelContent::~InformationPanelContent() +{ + InformationPanelSettings::self()->writeConfig(); +} + +void InformationPanelContent::showItem(const KFileItem& item) +{ + // If there is a preview job, kill it to prevent that we have jobs for + // multiple items running, and thus a race condition (bug 250787). + if (m_previewJob) { + m_previewJob->kill(); + } + + const KUrl itemUrl = item.url(); + const bool isSearchUrl = itemUrl.protocol().contains("search") && item.localPath().isEmpty(); + if (!applyPlace(itemUrl)) { + setNameLabelText(item.text()); + if (isSearchUrl) { + // in the case of a search-URL the URL is not readable for humans + // (at least not useful to show in the Information Panel) + KIconLoader iconLoader; + QPixmap icon = iconLoader.loadIcon("nepomuk", + KIconLoader::NoGroup, + KIconLoader::SizeEnormous); + m_preview->setPixmap(icon); + } else { + // try to get a preview pixmap from the item... + + // Mark the currently shown preview as outdated. This is done + // with a small delay to prevent a flickering when the next preview + // can be shown within a short timeframe. This timer is not started + // for directories, as directory previews might fail and return the + // same icon. + if (!item.isDir()) { + m_outdatedPreviewTimer->start(); + } + + m_previewJob = new KIO::PreviewJob(KFileItemList() << item, QSize(m_preview->width(), m_preview->height())); + m_previewJob->setScaleType(KIO::PreviewJob::Unscaled); + m_previewJob->setIgnoreMaximumSize(item.isLocalFile()); + if (m_previewJob->ui()) { + m_previewJob->ui()->setWindow(this); + } + + connect(m_previewJob, SIGNAL(gotPreview(KFileItem,QPixmap)), + this, SLOT(showPreview(KFileItem,QPixmap))); + connect(m_previewJob, SIGNAL(failed(KFileItem)), + this, SLOT(showIcon(KFileItem))); + } + } + + if (m_metaDataWidget) { + m_metaDataWidget->show(); + m_metaDataWidget->setItems(KFileItemList() << item); + } + + if (InformationPanelSettings::previewsShown()) { + const QString mimeType = item.mimetype(); + if (m_playerWidget->player()->isMimeSupported(mimeType)) { + m_playerWidget->show(); + m_playerWidget->open(item.targetUrl().url()); + if (m_preview->isVisible()) { + m_playerWidget->setMaximumSize(m_preview->size()); + } + m_preview->setVisible(false); + } else { + m_playerWidget->player()->stop(); + m_playerWidget->hide(); + m_preview->setVisible(true); + } + } else { + m_playerWidget->player()->stop(); + m_playerWidget->hide(); + } + + m_item = item; +} + +void InformationPanelContent::showItems(const KFileItemList& items) +{ + // If there is a preview job, kill it to prevent that we have jobs for + // multiple items running, and thus a race condition (bug 250787). + if (m_previewJob) { + m_previewJob->kill(); + } + + KIconLoader iconLoader; + QPixmap icon = iconLoader.loadIcon("dialog-information", + KIconLoader::NoGroup, + KIconLoader::SizeEnormous); + m_preview->setPixmap(icon); + setNameLabelText(i18ncp("@label", "%1 item selected", "%1 items selected", items.count())); + + if (m_metaDataWidget) { + m_metaDataWidget->setItems(items); + } + + m_playerWidget->player()->stop(); + m_playerWidget->hide(); + + m_item = KFileItem(); +} + +bool InformationPanelContent::eventFilter(QObject* obj, QEvent* event) +{ + switch (event->type()) { + case QEvent::Resize: { + QResizeEvent* resizeEvent = static_cast(event); + if (obj == m_metaDataArea->viewport()) { + // The size of the meta text area has changed. Adjust the fixed + // width in a way that no horizontal scrollbar needs to be shown. + m_metaDataWidget->setFixedWidth(resizeEvent->size().width()); + } else if (obj == parent()) { + adjustWidgetSizes(resizeEvent->size().width()); + } + break; + } + + case QEvent::Polish: + adjustWidgetSizes(parentWidget()->width()); + break; + + case QEvent::FontChange: + m_metaDataWidget->setFont(KGlobalSettings::smallestReadableFont()); + break; + + case QEvent::Hide: + if (m_playerWidget) + m_playerWidget->player()->stop(); + break; + + default: + break; + } + + return QWidget::eventFilter(obj, event); +} + +void InformationPanelContent::configureSettings(const QList& customContextMenuActions) +{ + KMenu popup(this); + + QAction* previewAction = popup.addAction(i18nc("@action:inmenu", "Preview")); + previewAction->setIcon(KIcon("view-preview")); + previewAction->setCheckable(true); + previewAction->setChecked(InformationPanelSettings::previewsShown()); + + QAction* configureAction = popup.addAction(i18nc("@action:inmenu", "Configure...")); + configureAction->setIcon(KIcon("configure")); + + popup.addSeparator(); + foreach (QAction* action, customContextMenuActions) { + popup.addAction(action); + } + + // Open the popup and adjust the settings for the + // selected action. + QAction* action = popup.exec(QCursor::pos()); + if (!action) { + return; + } + + const bool isChecked = action->isChecked(); + if (action == previewAction) { + if (!isChecked) { + m_playerWidget->player()->stop(); + m_playerWidget->hide(); + } + m_preview->setVisible(isChecked); + InformationPanelSettings::setPreviewsShown(isChecked); + } else if (action == configureAction) { + FileMetaDataConfigurationDialog* dialog = new FileMetaDataConfigurationDialog(); + dialog->setDescription(i18nc("@label::textbox", + "Select which data should be shown in the information panel:")); + dialog->setItems(m_metaDataWidget->items()); + dialog->setAttribute(Qt::WA_DeleteOnClose); + dialog->show(); + dialog->raise(); + dialog->activateWindow(); + connect(dialog, SIGNAL(destroyed()), this, SLOT(refreshMetaData())); + } +} + +void InformationPanelContent::showIcon(const KFileItem& item) +{ + m_outdatedPreviewTimer->stop(); + if (!applyPlace(item.targetUrl())) { + KIcon icon(item.iconName(), KIconLoader::global(), item.overlays()); + m_preview->setPixmap(icon.pixmap(KIconLoader::SizeEnormous)); + } +} + +void InformationPanelContent::showPreview(const KFileItem& item, + const QPixmap& pixmap) +{ + m_outdatedPreviewTimer->stop(); + + QPixmap p = pixmap; + KIconLoader::global()->drawOverlays(item.overlays(), p, KIconLoader::Desktop); + m_preview->setPixmap(p); +} + +void InformationPanelContent::markOutdatedPreview() +{ + KIconEffect *iconEffect = KIconLoader::global()->iconEffect(); + QPixmap disabledPixmap = iconEffect->apply(m_preview->pixmap(), + KIconLoader::Desktop, + KIconLoader::DisabledState); + m_preview->setPixmap(disabledPixmap); +} + +void InformationPanelContent::refreshMetaData() +{ + if (!m_item.isNull()) { + showItem(m_item); + } +} + +bool InformationPanelContent::applyPlace(const KUrl& url) +{ + const int count = m_placesItemModel->count(); + for (int i = 0; i < count; ++i) { + const PlacesItem* item = m_placesItemModel->placesItem(i); + if (item->url().equals(url, KUrl::CompareWithoutTrailingSlash)) { + setNameLabelText(item->text()); + m_preview->setPixmap(KIcon(item->icon()).pixmap(128, 128)); + return true; + } + } + + return false; +} + +void InformationPanelContent::setNameLabelText(const QString& text) +{ + QTextOption textOption; + textOption.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); + + const QString processedText = Qt::mightBeRichText(text) ? text : KStringHandler::preProcessWrap(text); + + QTextLayout textLayout(processedText); + textLayout.setFont(m_nameLabel->font()); + textLayout.setTextOption(textOption); + + QString wrappedText; + wrappedText.reserve(processedText.length()); + + // wrap the text to fit into the width of m_nameLabel + textLayout.beginLayout(); + QTextLine line = textLayout.createLine(); + while (line.isValid()) { + line.setLineWidth(m_nameLabel->width()); + wrappedText += processedText.mid(line.textStart(), line.textLength()); + + line = textLayout.createLine(); + if (line.isValid()) { + wrappedText += QChar::LineSeparator; + } + } + textLayout.endLayout(); + + m_nameLabel->setText(wrappedText); +} + +void InformationPanelContent::adjustWidgetSizes(int width) +{ + // If the text inside the name label or the info label cannot + // get wrapped, then the maximum width of the label is increased + // so that the width of the information panel gets increased. + // To prevent this, the maximum width is adjusted to + // the current width of the panel. + const int maxWidth = width - KDialog::spacingHint() * 4; + m_nameLabel->setMaximumWidth(maxWidth); + + // The metadata widget also contains a text widget which may return + // a large preferred width. + if (m_metaDataWidget) { + m_metaDataWidget->setMaximumWidth(maxWidth); + } + + // try to increase the preview as large as possible + m_preview->setSizeHint(QSize(maxWidth, maxWidth)); + + if (m_playerWidget->isVisible()) { + // assure that the size of the video player is the same as the preview size + m_playerWidget->setMaximumSize(maxWidth, maxWidth); + } +} + +#include "moc_informationpanelcontent.cpp" diff --git a/dolphin/src/panels/information/informationpanelcontent.h b/dolphin/src/panels/information/informationpanelcontent.h new file mode 100644 index 00000000..0756086e --- /dev/null +++ b/dolphin/src/panels/information/informationpanelcontent.h @@ -0,0 +1,146 @@ +/*************************************************************************** + * Copyright (C) 2009-2010 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef INFORMATIONPANELCONTENT_H +#define INFORMATIONPANELCONTENT_H + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +class KFileItemList; +class PixmapViewer; +class PlacesItemModel; + +namespace KIO { + class PreviewJob; +} + +class KFileMetaDataWidget; + +/** + * @brief Manages the widgets that display the meta information +* for file items of the Information Panel. + */ +class InformationPanelContent : public QWidget +{ + Q_OBJECT + +public: + explicit InformationPanelContent(QWidget* parent = 0); + virtual ~InformationPanelContent(); + + /** + * Shows the meta information for the item \p item. + * The preview of the item is generated asynchronously, + * the other meta information are fetched synchronously. + */ + void showItem(const KFileItem& item); + + /** + * Shows the meta information for the items \p items. + */ + void showItems(const KFileItemList& items); + + /** + * Opens a menu which allows to configure which meta information + * should be shown. + * + * TODO: Move this code to the class InformationPanel + */ + void configureSettings(const QList& customContextMenuActions); + +signals: + void urlActivated( const KUrl& url ); + +protected: + /** @see QObject::eventFilter() */ + virtual bool eventFilter(QObject* obj, QEvent* event); + +private slots: + /** + * Is invoked if no preview is available for the item. In this + * case the icon will be shown. + */ + void showIcon(const KFileItem& item); + + /** + * Is invoked if a preview is available for the item. The preview + * \a pixmap is shown inside the info page. + */ + void showPreview(const KFileItem& item, const QPixmap& pixmap); + + /** + * Marks the currently shown preview as outdated + * by greying the content. + */ + void markOutdatedPreview(); + + /** + * Is invoked after the file meta data configuration dialog has been + * closed and refreshes the visibility of the meta data. + */ + void refreshMetaData(); + +private: + /** + * Checks whether the an URL is repesented by a place. If yes, + * then the place icon and name are shown instead of a preview. + * @return True, if the URL represents exactly a place. + * @param url The url to check. + */ + bool applyPlace(const KUrl& url); + + /** + * Sets the text for the label \a m_nameLabel and assures that the + * text is split in a way that it can be wrapped within the + * label width (QLabel::setWordWrap() does not work if the + * text represents one extremely long word). + */ + void setNameLabelText(const QString& text); + + /** + * Adjusts the sizes of the widgets dependent on the available + * width given by \p width. + */ + void adjustWidgetSizes(int width); + +private: + KFileItem m_item; + + QPointer m_previewJob; + QTimer* m_outdatedPreviewTimer; + + PixmapViewer* m_preview; + KMediaWidget* m_playerWidget; + QLabel* m_nameLabel; + KFileMetaDataWidget* m_metaDataWidget; + QScrollArea* m_metaDataArea; + + PlacesItemModel* m_placesItemModel; +}; + +#endif // INFORMATIONPANELCONTENT_H diff --git a/dolphin/src/panels/information/pixmapviewer.cpp b/dolphin/src/panels/information/pixmapviewer.cpp new file mode 100644 index 00000000..3b2d6c5a --- /dev/null +++ b/dolphin/src/panels/information/pixmapviewer.cpp @@ -0,0 +1,131 @@ +/*************************************************************************** + * Copyright (C) 2006 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "pixmapviewer.h" + +#include + +#include +#include +#include +#include + +PixmapViewer::PixmapViewer(QWidget* parent, Transition transition) : + QWidget(parent), + m_transition(transition), + m_sizeHint() +{ + setMinimumWidth(KIconLoader::SizeEnormous); + setMinimumHeight(KIconLoader::SizeEnormous); + + m_animation.setDuration(150); + m_animation.setCurveShape(QTimeLine::LinearCurve); + + if (m_transition != NoTransition) { + connect(&m_animation, SIGNAL(valueChanged(qreal)), this, SLOT(update())); + connect(&m_animation, SIGNAL(finished()), this, SLOT(checkPendingPixmaps())); + } +} + +PixmapViewer::~PixmapViewer() +{ +} + +void PixmapViewer::setPixmap(const QPixmap& pixmap) +{ + if (pixmap.isNull()) { + return; + } + + if ((m_transition != NoTransition) && (m_animation.state() == QTimeLine::Running)) { + m_pendingPixmaps.enqueue(pixmap); + if (m_pendingPixmaps.count() > 5) { + // don't queue more than 5 pixmaps + m_pendingPixmaps.takeFirst(); + } + return; + } + + m_oldPixmap = m_pixmap.isNull() ? pixmap : m_pixmap; + m_pixmap = pixmap; + update(); + + const bool animate = (m_transition != NoTransition) && + (m_pixmap.size() != m_oldPixmap.size()); + if (animate) { + m_animation.start(); + } +} + +void PixmapViewer::setSizeHint(const QSize& size) +{ + m_sizeHint = size; + updateGeometry(); +} + +QSize PixmapViewer::sizeHint() const +{ + return m_sizeHint; +} + +void PixmapViewer::paintEvent(QPaintEvent* event) +{ + QWidget::paintEvent(event); + + QPainter painter(this); + + if (m_transition != NoTransition) { + const float value = m_animation.currentValue(); + const int scaledWidth = static_cast((m_oldPixmap.width() * (1.0 - value)) + (m_pixmap.width() * value)); + const int scaledHeight = static_cast((m_oldPixmap.height() * (1.0 - value)) + (m_pixmap.height() * value)); + + const int x = (width() - scaledWidth ) / 2; + const int y = (height() - scaledHeight) / 2; + + const bool useOldPixmap = (m_transition == SizeTransition) && + (m_oldPixmap.width() > m_pixmap.width()); + const QPixmap& largePixmap = useOldPixmap ? m_oldPixmap : m_pixmap; + if (!largePixmap.isNull()) { + const QPixmap scaledPixmap = largePixmap.scaled(scaledWidth, + scaledHeight, + Qt::IgnoreAspectRatio, + Qt::FastTransformation); + painter.drawPixmap(x, y, scaledPixmap); + } + } else { + const int x = (width() - m_pixmap.width() ) / 2; + const int y = (height() - m_pixmap.height()) / 2; + painter.drawPixmap(x, y, m_pixmap); + } +} + +void PixmapViewer::checkPendingPixmaps() +{ + if (m_pendingPixmaps.count() > 0) { + QPixmap pixmap = m_pendingPixmaps.dequeue(); + m_oldPixmap = m_pixmap.isNull() ? pixmap : m_pixmap; + m_pixmap = pixmap; + update(); + m_animation.start(); + } else { + m_oldPixmap = m_pixmap; + } +} + +#include "moc_pixmapviewer.cpp" diff --git a/dolphin/src/panels/information/pixmapviewer.h b/dolphin/src/panels/information/pixmapviewer.h new file mode 100644 index 00000000..a7018074 --- /dev/null +++ b/dolphin/src/panels/information/pixmapviewer.h @@ -0,0 +1,97 @@ +/*************************************************************************** + * Copyright (C) 2006 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef PIXMAPVIEWER_H +#define PIXMAPVIEWER_H + +#include +#include +#include +#include + +#include + +/** + * @brief Widget which shows a pixmap centered inside the boundaries. + * + * When the pixmap is changed, a smooth transition is done from the old pixmap + * to the new pixmap. + */ +class PixmapViewer : public QWidget +{ + Q_OBJECT + +public: + enum Transition + { + /** No transition is done when the pixmap is changed. */ + NoTransition, + + /** + * The old pixmap is replaced by the new pixmap and the size is + * adjusted smoothly to the size of the new pixmap. + */ + DefaultTransition, + + /** + * If the old pixmap and the new pixmap have the same content, but + * a different size it is recommended to use Transition::SizeTransition + * instead of Transition::DefaultTransition. In this case it is assured + * that the larger pixmap is used for downscaling, which leads + * to an improved scaling output. + */ + SizeTransition + }; + + explicit PixmapViewer(QWidget* parent, + Transition transition = DefaultTransition); + + virtual ~PixmapViewer(); + void setPixmap(const QPixmap& pixmap); + QPixmap pixmap() const; + + /** + * Sets the size hint to \a size and triggers a relayout + * of the parent widget. Per default no size hint is given. + */ + void setSizeHint(const QSize& size); + virtual QSize sizeHint() const; + +protected: + virtual void paintEvent(QPaintEvent* event); + +private Q_SLOTS: + void checkPendingPixmaps(); + +private: + QPixmap m_pixmap; + QPixmap m_oldPixmap; + QQueue m_pendingPixmaps; + QTimeLine m_animation; + Transition m_transition; + QSize m_sizeHint; +}; + +inline QPixmap PixmapViewer::pixmap() const +{ + return m_pixmap; +} + + +#endif diff --git a/dolphin/src/panels/panel.cpp b/dolphin/src/panels/panel.cpp new file mode 100644 index 00000000..61fda9d5 --- /dev/null +++ b/dolphin/src/panels/panel.cpp @@ -0,0 +1,79 @@ +/*************************************************************************** + * Copyright (C) 2006 by Cvetoslav Ludmiloff * + * Copyright (C) 2006-2010 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "panel.h" +#include + +Panel::Panel(QWidget* parent) : + QWidget(parent), + m_url(), + m_customContextMenuActions() +{ +} + +Panel::~Panel() +{ +} + +KUrl Panel::url() const +{ + return m_url; +} + +void Panel::setCustomContextMenuActions(const QList& actions) +{ + m_customContextMenuActions = actions; +} + +QList Panel::customContextMenuActions() const +{ + return m_customContextMenuActions; +} + +QSize Panel::sizeHint() const +{ + // The size hint will be requested already when starting Dolphin even + // if the panel is invisible. For performance reasons most panels delay + // the creation and initialization of widgets until a showEvent() is called. + // Because of this the size-hint of the embedded widgets cannot be used + // and a default size is provided: + return QSize(180, 180); +} + +void Panel::setUrl(const KUrl& url) +{ + if (url.equals(m_url, KUrl::CompareWithoutTrailingSlash)) { + return; + } + + const KUrl oldUrl = m_url; + m_url = url; + const bool accepted = urlChanged(); + if (!accepted) { + m_url = oldUrl; + } +} + +void Panel::readSettings() +{ + +} + +#include "moc_panel.cpp" diff --git a/dolphin/src/panels/panel.h b/dolphin/src/panels/panel.h new file mode 100644 index 00000000..a0b25d6c --- /dev/null +++ b/dolphin/src/panels/panel.h @@ -0,0 +1,83 @@ +/*************************************************************************** + * Copyright (C) 2006 by Cvetoslav Ludmiloff * + * Copyright (C) 2006-2010 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef PANEL_H +#define PANEL_H + +#include +#include +#include + +/** + * @brief Base widget for all panels that can be docked on the window borders. + * + * Derived panels should provide a context menu that at least offers the + * actions from Panel::customContextMenuActions(). + */ +class Panel : public QWidget +{ + Q_OBJECT + +public: + explicit Panel(QWidget* parent = 0); + virtual ~Panel(); + + /** Returns the current set URL of the active Dolphin view. */ + KUrl url() const; + + /** + * Sets custom context menu actions that are added to the panel specific + * context menu actions. Allows an application to apply custom actions to + * the panel. + */ + void setCustomContextMenuActions(const QList& actions); + QList customContextMenuActions() const; + + /** @see QWidget::sizeHint() */ + virtual QSize sizeHint() const; + +public slots: + /** + * This is invoked every time the folder being displayed in the + * active Dolphin view changes. + */ + void setUrl(const KUrl& url); + + /** + * Refreshes the view to get synchronized with the settings. + */ + virtual void readSettings(); + +protected: + /** + * Must be implemented by derived classes and is invoked when + * the URL has been changed (see Panel::setUrl()). + * @return True, if the new URL will get accepted by the derived + * class. If false is returned, + * the URL will be reset to the previous URL. + */ + virtual bool urlChanged() = 0; + +private: + KUrl m_url; + QList m_customContextMenuActions; +}; + +#endif // PANEL_H diff --git a/dolphin/src/panels/places/dolphin_placespanelsettings.kcfg b/dolphin/src/panels/places/dolphin_placespanelsettings.kcfg new file mode 100644 index 00000000..10efcf70 --- /dev/null +++ b/dolphin/src/panels/places/dolphin_placespanelsettings.kcfg @@ -0,0 +1,15 @@ + + + + kiconloader.h + + + + + KIconLoader::SizeSmallMedium + + + diff --git a/dolphin/src/panels/places/dolphin_placespanelsettings.kcfgc b/dolphin/src/panels/places/dolphin_placespanelsettings.kcfgc new file mode 100644 index 00000000..65a77ec3 --- /dev/null +++ b/dolphin/src/panels/places/dolphin_placespanelsettings.kcfgc @@ -0,0 +1,4 @@ +File=dolphin_placespanelsettings.kcfg +ClassName=PlacesPanelSettings +Singleton=true +Mutators=true diff --git a/dolphin/src/panels/places/placesitem.cpp b/dolphin/src/panels/places/placesitem.cpp new file mode 100644 index 00000000..9c89c534 --- /dev/null +++ b/dolphin/src/panels/places/placesitem.cpp @@ -0,0 +1,312 @@ +/*************************************************************************** + * Copyright (C) 2012 by Peter Penz * + * * + * Based on KFilePlacesItem from kdelibs: * + * Copyright (C) 2007 Kevin Ottens * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "placesitem.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include "placesitemsignalhandler.h" +#include +#include + +PlacesItem::PlacesItem(const KBookmark& bookmark, PlacesItem* parent) : + KStandardItem(parent), + m_device(), + m_access(), + m_volume(), + m_disc(), + m_mtp(), + m_signalHandler(0), + m_bookmark() +{ + m_signalHandler = new PlacesItemSignalHandler(this); + setBookmark(bookmark); +} + +PlacesItem::~PlacesItem() +{ + delete m_signalHandler; +} + +void PlacesItem::setUrl(const KUrl& url) +{ + + // The default check in KStandardItem::setDataValue() + // for equal values does not work with a custom value + // like KUrl. Hence do a manual check to prevent that + // setting an equal URL results in an itemsChanged() + // signal. + if (dataValue("url").value() != url) { + setDataValue("url", url); + } + + if (url.protocol() == QLatin1String("trash")) { + KDirWatch::self()->addFile(KStandardDirs::locateLocal("config", "trashrc")); + QObject::connect(KDirWatch::self(), SIGNAL(dirty(QString)), + m_signalHandler, SLOT(onTrashConfigChange(QString))); + onTrashConfigChange("trash"); + } +} + +KUrl PlacesItem::url() const +{ + return dataValue("url").value(); +} + +void PlacesItem::setUdi(const QString& udi) +{ + setDataValue("udi", udi); +} + +QString PlacesItem::udi() const +{ + return dataValue("udi").toString(); +} + +void PlacesItem::setHidden(bool hidden) +{ + setDataValue("isHidden", hidden); +} + +bool PlacesItem::isHidden() const +{ + return dataValue("isHidden").toBool(); +} + +void PlacesItem::setSystemItem(bool isSystemItem) +{ + setDataValue("isSystemItem", isSystemItem); +} + +bool PlacesItem::isSystemItem() const +{ + return dataValue("isSystemItem").toBool(); +} + +Solid::Device PlacesItem::device() const +{ + return m_device; +} + +void PlacesItem::setBookmark(const KBookmark& bookmark) +{ + if (bookmark == m_bookmark) { + return; + } + + m_bookmark = bookmark; + + delete m_access; + delete m_volume; + delete m_disc; + delete m_mtp; + + + const QString udi = bookmark.metaDataItem("UDI"); + if (udi.isEmpty()) { + setIcon(bookmark.icon()); + setText(i18nc("KFile System Bookmarks", bookmark.text().toUtf8().data())); + setUrl(bookmark.url()); + } else { + initializeDevice(udi); + } + + const GroupType type = groupType(); + if (icon().isEmpty()) { + switch (type) { + case PlacesType: + default: setIcon("folder"); + } + + } + + switch (type) { + case PlacesType: setGroup(i18nc("@item", "Places")); break; + case DevicesType: setGroup(i18nc("@item", "Devices")); break; + default: Q_ASSERT(false); break; + } + + setHidden(bookmark.metaDataItem("IsHidden") == QLatin1String("true")); +} + +KBookmark PlacesItem::bookmark() const +{ + return m_bookmark; +} + +PlacesItem::GroupType PlacesItem::groupType() const +{ + if (udi().isEmpty()) { + const QString protocol = url().protocol(); + if (protocol == QLatin1String("bluetooth")) { + return DevicesType; + } + + return PlacesType; + } + + return DevicesType; +} + +bool PlacesItem::storageSetupNeeded() const +{ + return m_access ? !m_access->isAccessible() : false; +} + +KBookmark PlacesItem::createBookmark(KBookmarkManager* manager, + const QString& text, + const KUrl& url, + const QString& iconName) +{ + KBookmarkGroup root = manager->root(); + if (root.isNull()) { + return KBookmark(); + } + + KBookmark bookmark = root.addBookmark(text, url, iconName); + bookmark.setFullText(text); + bookmark.setMetaDataItem("ID", generateNewId()); + + return bookmark; +} + +KBookmark PlacesItem::createDeviceBookmark(KBookmarkManager* manager, + const QString& udi) +{ + KBookmarkGroup root = manager->root(); + if (root.isNull()) { + return KBookmark(); + } + + KBookmark bookmark = root.createNewSeparator(); + bookmark.setMetaDataItem("UDI", udi); + bookmark.setMetaDataItem("isSystemItem", "true"); + return bookmark; +} + +void PlacesItem::onDataValueChanged(const QByteArray& role, + const QVariant& current, + const QVariant& previous) +{ + Q_UNUSED(current); + Q_UNUSED(previous); + + if (!m_bookmark.isNull()) { + updateBookmarkForRole(role); + } +} + +void PlacesItem::onDataChanged(const QHash& current, + const QHash& previous) +{ + Q_UNUSED(previous); + + if (!m_bookmark.isNull()) { + QHashIterator it(current); + while (it.hasNext()) { + it.next(); + updateBookmarkForRole(it.key()); + } + } +} + +void PlacesItem::initializeDevice(const QString& udi) +{ + m_device = Solid::Device(udi); + if (!m_device.isValid()) { + return; + } + + m_access = m_device.as(); + m_mtp = m_device.as(); + + setText(m_device.description()); + setIcon(m_device.icon()); + setIconOverlays(m_device.emblems()); + setUdi(udi); + + if (m_access) { + setUrl(m_access->filePath()); + QObject::connect(m_access, SIGNAL(accessibilityChanged(bool,QString)), + m_signalHandler, SLOT(onAccessibilityChanged())); + } else if (m_mtp) { + setUrl(QString("mtp:udi=%1").arg(m_device.udi())); + } +} + +void PlacesItem::onAccessibilityChanged() +{ + setIconOverlays(m_device.emblems()); + setUrl(m_access->filePath()); +} + +void PlacesItem::onTrashConfigChange(const QString &config) +{ + Q_ASSERT(url().protocol() == QLatin1String("trash")); + + KSettings trashConfig("trashrc", KSettings::SimpleConfig); + const bool isTrashEmpty = trashConfig.value("Status/Empty", true).toBool(); + setIcon(isTrashEmpty ? "user-trash" : "user-trash-full"); +} + +void PlacesItem::updateBookmarkForRole(const QByteArray& role) +{ + Q_ASSERT(!m_bookmark.isNull()); + if (role == "iconName") { + m_bookmark.setIcon(icon()); + } else if (role == "text") { + // Only store the text in the KBookmark if it is not the translation of + // the current text. This makes sure that the text is re-translated if + // the user chooses another language, or the translation itself changes. + // + // NOTE: It is important to use "KFile System Bookmarks" as context + // (see PlacesItemModel::createSystemBookmarks()). + if (text() != i18nc("KFile System Bookmarks", m_bookmark.text().toUtf8().data())) { + m_bookmark.setFullText(text()); + } + } else if (role == "url") { + m_bookmark.setUrl(url()); + } else if (role == "udi)") { + m_bookmark.setMetaDataItem("UDI", udi()); + } else if (role == "isSystemItem") { + m_bookmark.setMetaDataItem("isSystemItem", isSystemItem() ? "true" : "false"); + } else if (role == "isHidden") { + m_bookmark.setMetaDataItem("IsHidden", isHidden() ? "true" : "false"); + } +} + +QString PlacesItem::generateNewId() +{ + // The ID-generation must be different as done in KFilePlacesItem from kdelibs + // to prevent identical IDs, because 'count' is of course not shared. We append a + // " (V2)" to indicate that the ID has been generated by + // a new version of the places view. + static int count = 0; + return QString::number(QDateTime::currentDateTime().toTime_t()) + + '/' + QString::number(count++) + " (V2)"; +} diff --git a/dolphin/src/panels/places/placesitem.h b/dolphin/src/panels/places/placesitem.h new file mode 100644 index 00000000..9ba9208d --- /dev/null +++ b/dolphin/src/panels/places/placesitem.h @@ -0,0 +1,125 @@ +/*************************************************************************** + * Copyright (C) 2012 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef PLACESITEM_H +#define PLACESITEM_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class PlacesItemSignalHandler; + +/** + * @brief Extends KStandardItem by places-specific properties. + */ +class PlacesItem : public KStandardItem +{ + +public: + enum GroupType + { + PlacesType, + DevicesType + }; + + explicit PlacesItem(const KBookmark& bookmark, PlacesItem* parent = 0); + virtual ~PlacesItem(); + + void setUrl(const KUrl& url); + KUrl url() const; + + void setUdi(const QString& udi); + QString udi() const; + + void setHidden(bool hidden); + bool isHidden() const; + + void setSystemItem(bool isSystemItem); + bool isSystemItem() const; + + Solid::Device device() const; + + void setBookmark(const KBookmark& bookmark); + KBookmark bookmark() const; + + GroupType groupType() const; + + bool storageSetupNeeded() const; + + static KBookmark createBookmark(KBookmarkManager* manager, + const QString& text, + const KUrl& url, + const QString& iconName); + static KBookmark createDeviceBookmark(KBookmarkManager* manager, + const QString& udi); + +protected: + virtual void onDataValueChanged(const QByteArray& role, + const QVariant& current, + const QVariant& previous); + + virtual void onDataChanged(const QHash& current, + const QHash& previous); + +private: + PlacesItem(const PlacesItem& item); + + void initializeDevice(const QString& udi); + + /** + * Is invoked if the accessibility of the storage access + * m_access has been changed and updates the emblem. + */ + void onAccessibilityChanged(); + + /** + * Is invoked if the trash state config changes. + * Updates the state of the trash-icon to be empty or full. + */ + void onTrashConfigChange(const QString &config); + + /** + * Applies the data-value from the role to m_bookmark. + */ + void updateBookmarkForRole(const QByteArray& role); + + static QString generateNewId(); + +private: + Solid::Device m_device; + QPointer m_access; + QPointer m_volume; + QPointer m_disc; + QPointer m_mtp; + QPointer m_signalHandler; + KBookmark m_bookmark; + + friend class PlacesItemSignalHandler; // Calls onAccessibilityChanged() +}; + +#endif + + diff --git a/dolphin/src/panels/places/placesitemeditdialog.cpp b/dolphin/src/panels/places/placesitemeditdialog.cpp new file mode 100644 index 00000000..9cf48da6 --- /dev/null +++ b/dolphin/src/panels/places/placesitemeditdialog.cpp @@ -0,0 +1,170 @@ +/*************************************************************************** + * Copyright (C) 2012 by Peter Penz * + * * + * Based on KFilePlaceEditDialog from kdelibs: * + * Copyright (C) 2001,2002,2003 Carsten Pfeiffer * + * Copyright (C) 2007 Kevin Ottens * * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "placesitemeditdialog.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +PlacesItemEditDialog::PlacesItemEditDialog(QWidget* parent) : + KDialog(parent), + m_icon(), + m_text(), + m_url(), + m_allowGlobal(false), + m_urlEdit(0), + m_textEdit(0), + m_iconButton(0), + m_appLocal(0) +{ + setButtons( Ok | Cancel ); + setModal(true); + setDefaultButton(Ok); +} + +void PlacesItemEditDialog::setIcon(const QString& icon) +{ + m_icon = icon; +} + +QString PlacesItemEditDialog::icon() const +{ + return m_iconButton->icon(); +} + +void PlacesItemEditDialog::setText(const QString& text) +{ + m_text = text; +} + +QString PlacesItemEditDialog::text() const +{ + QString text = m_textEdit->text(); + if (text.isEmpty()) { + const KUrl url = m_urlEdit->url(); + text = url.fileName().isEmpty() ? url.prettyUrl() : url.fileName(); + } + return text; +} + +void PlacesItemEditDialog::setUrl(const KUrl& url) +{ + m_url = url; +} + +KUrl PlacesItemEditDialog::url() const +{ + return m_urlEdit->url(); +} + +void PlacesItemEditDialog::setAllowGlobal(bool allow) +{ + m_allowGlobal = allow; +} + +bool PlacesItemEditDialog::allowGlobal() const +{ + return m_allowGlobal; +} + +bool PlacesItemEditDialog::event(QEvent* event) +{ + if (event->type() == QEvent::Polish) { + initialize(); + } + return QWidget::event(event); +} + +void PlacesItemEditDialog::slotUrlChanged(const QString& text) +{ + enableButtonOk(!text.isEmpty()); +} + +PlacesItemEditDialog::~PlacesItemEditDialog() +{ +} + +void PlacesItemEditDialog::initialize() +{ + QWidget* mainWidget = new QWidget(this); + QVBoxLayout* vBox = new QVBoxLayout(mainWidget); + + QFormLayout* formLayout = new QFormLayout(); + vBox->addLayout( formLayout ); + + m_textEdit = new KLineEdit(mainWidget); + formLayout->addRow(i18nc("@label", "Label:"), m_textEdit); + m_textEdit->setText(m_text); + m_textEdit->setClickMessage(i18n("Enter descriptive label here")); + + m_urlEdit = new KUrlRequester(m_url.prettyUrl(), mainWidget); + m_urlEdit->setMode(KFile::Directory); + formLayout->addRow(i18nc("@label", "Location:"), m_urlEdit); + // Provide room for at least 40 chars (average char width is half of height) + m_urlEdit->setMinimumWidth(m_urlEdit->fontMetrics().height() * (40 / 2)); + connect(m_urlEdit->lineEdit(), SIGNAL(textChanged(QString)), this, SLOT(slotUrlChanged(QString))); + + m_iconButton = new KIconButton(mainWidget); + formLayout->addRow(i18nc("@label", "Choose an icon:"), m_iconButton); + m_iconButton->setIconSize(IconSize(KIconLoader::Desktop)); + m_iconButton->setIconType(KIconLoader::NoGroup, KIconLoader::Place); + if (m_icon.isEmpty()) { + m_iconButton->setIcon(KMimeType::iconNameForUrl(m_url)); + } else { + m_iconButton->setIcon(m_icon); + } + + if (m_allowGlobal) { + QString appName; + if (KGlobal::mainComponent().aboutData()) { + appName = KGlobal::mainComponent().aboutData()->programName(); + } + if (appName.isEmpty()) { + appName = KGlobal::mainComponent().componentName(); + } + m_appLocal = new QCheckBox( i18n("&Only show when using this application (%1)", appName ), mainWidget ); + m_appLocal->setChecked(false); + vBox->addWidget(m_appLocal); + } + + if (m_text.isEmpty()) { + m_urlEdit->setFocus(); + } else { + m_textEdit->setFocus(); + } + + setMainWidget(mainWidget); +} + +#include "moc_placesitemeditdialog.cpp" diff --git a/dolphin/src/panels/places/placesitemeditdialog.h b/dolphin/src/panels/places/placesitemeditdialog.h new file mode 100644 index 00000000..aee89498 --- /dev/null +++ b/dolphin/src/panels/places/placesitemeditdialog.h @@ -0,0 +1,76 @@ +/*************************************************************************** + * Copyright (C) 2012 by Peter Penz * + * * + * Based on KFilePlaceEditDialog from kdelibs: * + * Copyright (C) 2001,2002,2003 Carsten Pfeiffer * + * Copyright (C) 2007 Kevin Ottens * * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef PLACESITEMEDITDIALOG_H +#define PLACESITEMEDITDIALOG_H + +#include +#include + +class KIconButton; +class KLineEdit; +class KUrlRequester; +#include + +class PlacesItemEditDialog: public KDialog +{ + Q_OBJECT + +public: + explicit PlacesItemEditDialog(QWidget* parent = 0); + virtual ~PlacesItemEditDialog(); + + void setIcon(const QString& icon); + QString icon() const; + + void setText(const QString& text); + QString text() const; + + void setUrl(const KUrl& url); + KUrl url() const; + + void setAllowGlobal(bool allow); + bool allowGlobal() const; + +protected: + virtual bool event(QEvent* event); + +private slots: + void slotUrlChanged(const QString& text); + +private: + void initialize(); + +private: + QString m_icon; + QString m_text; + KUrl m_url; + bool m_allowGlobal; + + KUrlRequester* m_urlEdit; + KLineEdit* m_textEdit; + KIconButton* m_iconButton; + QCheckBox* m_appLocal; +}; + +#endif diff --git a/dolphin/src/panels/places/placesitemlistgroupheader.cpp b/dolphin/src/panels/places/placesitemlistgroupheader.cpp new file mode 100644 index 00000000..8d319eb8 --- /dev/null +++ b/dolphin/src/panels/places/placesitemlistgroupheader.cpp @@ -0,0 +1,45 @@ +/*************************************************************************** + * Copyright (C) 2012 by Peter Penz * + * * + * Based on the Itemviews NG project from Trolltech Labs: * + * http://qt.gitorious.org/qt-labs/itemviews-ng * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "placesitemlistgroupheader.h" + +PlacesItemListGroupHeader::PlacesItemListGroupHeader(QGraphicsWidget* parent) : + KStandardItemListGroupHeader(parent) +{ +} + +PlacesItemListGroupHeader::~PlacesItemListGroupHeader() +{ +} + +void PlacesItemListGroupHeader::paintSeparator(QPainter* painter, const QColor& color) +{ + Q_UNUSED(painter); + Q_UNUSED(color); +} + +QPalette::ColorRole PlacesItemListGroupHeader::normalTextColorRole() const +{ + return QPalette::WindowText; +} + +#include "moc_placesitemlistgroupheader.cpp" diff --git a/dolphin/src/panels/places/placesitemlistgroupheader.h b/dolphin/src/panels/places/placesitemlistgroupheader.h new file mode 100644 index 00000000..1c2d5cc2 --- /dev/null +++ b/dolphin/src/panels/places/placesitemlistgroupheader.h @@ -0,0 +1,40 @@ +/*************************************************************************** + * Copyright (C) 2012 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef PLACESITEMLISTGROUPHEADER_H +#define PLACESITEMLISTGROUPHEADER_H + +#include + +class PlacesItemListGroupHeader : public KStandardItemListGroupHeader +{ + Q_OBJECT + +public: + PlacesItemListGroupHeader(QGraphicsWidget* parent = 0); + virtual ~PlacesItemListGroupHeader(); + +protected: + virtual void paintSeparator(QPainter* painter, const QColor& color); + + virtual QPalette::ColorRole normalTextColorRole() const; +}; +#endif + + diff --git a/dolphin/src/panels/places/placesitemlistwidget.cpp b/dolphin/src/panels/places/placesitemlistwidget.cpp new file mode 100644 index 00000000..01d88860 --- /dev/null +++ b/dolphin/src/panels/places/placesitemlistwidget.cpp @@ -0,0 +1,43 @@ +/*************************************************************************** + * Copyright (C) 2012 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "placesitemlistwidget.h" + +#include "kdebug.h" + +PlacesItemListWidget::PlacesItemListWidget(KItemListWidgetInformant* informant, QGraphicsItem* parent) : + KStandardItemListWidget(informant, parent) +{ +} + +PlacesItemListWidget::~PlacesItemListWidget() +{ +} + +bool PlacesItemListWidget::isHidden() const +{ + return data().value("isHidden").toBool(); +} + +QPalette::ColorRole PlacesItemListWidget::normalTextColorRole() const +{ + return QPalette::WindowText; +} + +#include "moc_placesitemlistwidget.cpp" diff --git a/dolphin/src/panels/places/placesitemlistwidget.h b/dolphin/src/panels/places/placesitemlistwidget.h new file mode 100644 index 00000000..a2a88c1f --- /dev/null +++ b/dolphin/src/panels/places/placesitemlistwidget.h @@ -0,0 +1,44 @@ +/*************************************************************************** + * Copyright (C) 2012 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef PLACESITEMLISTWIDGET_H +#define PLACESITEMLISTWIDGET_H + +#include + +/** + * @brief Extends KStandardItemListWidget to interpret the hidden + * property of the PlacesModel and use the right text color. +*/ +class PlacesItemListWidget : public KStandardItemListWidget +{ + Q_OBJECT + +public: + PlacesItemListWidget(KItemListWidgetInformant* informant, QGraphicsItem* parent); + virtual ~PlacesItemListWidget(); + +protected: + virtual bool isHidden() const; + virtual QPalette::ColorRole normalTextColorRole() const; +}; + +#endif + + diff --git a/dolphin/src/panels/places/placesitemmodel.cpp b/dolphin/src/panels/places/placesitemmodel.cpp new file mode 100644 index 00000000..30e9b928 --- /dev/null +++ b/dolphin/src/panels/places/placesitemmodel.cpp @@ -0,0 +1,1052 @@ +/*************************************************************************** + * Copyright (C) 2012 by Peter Penz * + * * + * Based on KFilePlacesModel from kdelibs: * + * Copyright (C) 2007 Kevin Ottens * + * Copyright (C) 2007 David Faure * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "placesitemmodel.h" + +#include "dolphin_generalsettings.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "placesitem.h" +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + + +namespace { + // As long as KFilePlacesView from kdelibs is available in parallel, the + // system-bookmarks for "Recently Accessed" and "Search For" should be + // shown only inside the Places Panel. + const char* AppNamePrefix = "-places-panel"; +} + +PlacesItemModel::PlacesItemModel(QObject* parent) : + KStandardItemModel(parent), + m_hiddenItemsShown(false), + m_availableDevices(), + m_predicate(), + m_bookmarkManager(0), + m_systemBookmarks(), + m_systemBookmarksIndexes(), + m_bookmarkedItems(), + m_hiddenItemToRemove(-1), + m_saveBookmarksTimer(0), + m_updateBookmarksTimer(0), + m_storageSetupInProgress() +{ + const QString file = KStandardDirs::locateLocal("data", "kfileplaces/bookmarks.xml"); + m_bookmarkManager = KBookmarkManager::managerForFile(file, "kfilePlaces"); + + createSystemBookmarks(); + initializeAvailableDevices(); + loadBookmarks(); + + const int syncBookmarksTimeout = 100; + + m_saveBookmarksTimer = new QTimer(this); + m_saveBookmarksTimer->setInterval(syncBookmarksTimeout); + m_saveBookmarksTimer->setSingleShot(true); + connect(m_saveBookmarksTimer, SIGNAL(timeout()), this, SLOT(saveBookmarks())); + + m_updateBookmarksTimer = new QTimer(this); + m_updateBookmarksTimer->setInterval(syncBookmarksTimeout); + m_updateBookmarksTimer->setSingleShot(true); + connect(m_updateBookmarksTimer, SIGNAL(timeout()), this, SLOT(updateBookmarks())); + + connect(m_bookmarkManager, SIGNAL(changed(QString,QString)), + m_updateBookmarksTimer, SLOT(start())); + connect(m_bookmarkManager, SIGNAL(bookmarksChanged(QString)), + m_updateBookmarksTimer, SLOT(start())); +} + +PlacesItemModel::~PlacesItemModel() +{ + saveBookmarks(); + qDeleteAll(m_bookmarkedItems); + m_bookmarkedItems.clear(); +} + +PlacesItem* PlacesItemModel::createPlacesItem(const QString& text, + const KUrl& url, + const QString& iconName) +{ + const KBookmark bookmark = PlacesItem::createBookmark(m_bookmarkManager, text, url, iconName); + return new PlacesItem(bookmark); +} + +PlacesItem* PlacesItemModel::placesItem(int index) const +{ + return dynamic_cast(item(index)); +} + +int PlacesItemModel::hiddenCount() const +{ + int modelIndex = 0; + int hiddenItemCount = 0; + foreach (const PlacesItem* item, m_bookmarkedItems) { + if (item) { + ++hiddenItemCount; + } else { + if (placesItem(modelIndex)->isHidden()) { + ++hiddenItemCount; + } + ++modelIndex; + } + } + + return hiddenItemCount; +} + +void PlacesItemModel::setHiddenItemsShown(bool show) +{ + if (m_hiddenItemsShown == show) { + return; + } + + m_hiddenItemsShown = show; + + if (show) { + // Move all items that are part of m_bookmarkedItems to the model. + QList itemsToInsert; + QList insertPos; + int modelIndex = 0; + for (int i = 0; i < m_bookmarkedItems.count(); ++i) { + if (m_bookmarkedItems[i]) { + itemsToInsert.append(m_bookmarkedItems[i]); + m_bookmarkedItems[i] = 0; + insertPos.append(modelIndex); + } + ++modelIndex; + } + + // Inserting the items will automatically insert an item + // to m_bookmarkedItems in PlacesItemModel::onItemsInserted(). + // The items are temporary saved in itemsToInsert, so + // m_bookmarkedItems can be shrinked now. + m_bookmarkedItems.erase(m_bookmarkedItems.begin(), + m_bookmarkedItems.begin() + itemsToInsert.count()); + + for (int i = 0; i < itemsToInsert.count(); ++i) { + insertItem(insertPos[i], itemsToInsert[i]); + } + + Q_ASSERT(m_bookmarkedItems.count() == count()); + } else { + // Move all items of the model, where the "isHidden" property is true, to + // m_bookmarkedItems. + Q_ASSERT(m_bookmarkedItems.count() == count()); + for (int i = count() - 1; i >= 0; --i) { + if (placesItem(i)->isHidden()) { + hideItem(i); + } + } + } + +#ifdef PLACESITEMMODEL_DEBUG + kDebug() << "Changed visibility of hidden items"; + showModelState(); +#endif +} + +bool PlacesItemModel::hiddenItemsShown() const +{ + return m_hiddenItemsShown; +} + +int PlacesItemModel::closestItem(const KUrl& url) const +{ + int foundIndex = -1; + int maxLength = 0; + + for (int i = 0; i < count(); ++i) { + const KUrl itemUrl = placesItem(i)->url(); + if (itemUrl.isParentOf(url)) { + const int length = itemUrl.prettyUrl().length(); + if (length > maxLength) { + foundIndex = i; + maxLength = length; + } + } + } + + return foundIndex; +} + +void PlacesItemModel::appendItemToGroup(PlacesItem* item) +{ + if (!item) { + return; + } + + int i = 0; + while (i < count() && placesItem(i)->group() != item->group()) { + ++i; + } + + bool inserted = false; + while (!inserted && i < count()) { + if (placesItem(i)->group() != item->group()) { + insertItem(i, item); + inserted = true; + } + ++i; + } + + if (!inserted) { + appendItem(item); + } +} + + +QAction* PlacesItemModel::ejectAction(int index) const +{ + const PlacesItem* item = placesItem(index); + if (item && item->device().is()) { + return new QAction(KIcon("media-eject"), i18nc("@item", "Eject '%1'", item->text()), 0); + } + + return 0; +} + +QAction* PlacesItemModel::teardownAction(int index) const +{ + const PlacesItem* item = placesItem(index); + if (!item) { + return 0; + } + + Solid::Device device = item->device(); + const bool providesTearDown = device.is() && + device.as()->isAccessible(); + if (!providesTearDown) { + return 0; + } + + Solid::StorageDrive* drive = device.as(); + + bool hotPluggable = false; + bool removable = false; + if (drive) { + hotPluggable = drive->isHotpluggable(); + removable = drive->isRemovable(); + } + + QString iconName; + QString text; + const QString label = item->text(); + if (device.is()) { + text = i18nc("@item", "Release '%1'", label); + } else if (removable || hotPluggable) { + text = i18nc("@item", "Safely Remove '%1'", label); + iconName = "media-eject"; + } else { + text = i18nc("@item", "Unmount '%1'", label); + iconName = "media-eject"; + } + + if (iconName.isEmpty()) { + return new QAction(text, 0); + } + + return new QAction(KIcon(iconName), text, 0); +} + +void PlacesItemModel::requestEject(int index) +{ + const PlacesItem* item = placesItem(index); + if (item) { + Solid::OpticalDrive* drive = item->device().as(); + if (drive) { + connect(drive, SIGNAL(ejectDone(Solid::ErrorType,QVariant,QString)), + this, SLOT(slotStorageTeardownDone(Solid::ErrorType,QVariant))); + drive->eject(); + } else { + const QString label = item->text(); + const QString message = i18nc("@info", "The device '%1' is not a disk and cannot be ejected.", label); + emit errorMessage(message); + } + } +} + +void PlacesItemModel::requestTeardown(int index) +{ + const PlacesItem* item = placesItem(index); + if (item) { + Solid::StorageAccess* access = item->device().as(); + if (access) { + connect(access, SIGNAL(teardownDone(Solid::ErrorType,QVariant,QString)), + this, SLOT(slotStorageTeardownDone(Solid::ErrorType,QVariant))); + access->teardown(); + } + } +} + +bool PlacesItemModel::storageSetupNeeded(int index) const +{ + const PlacesItem* item = placesItem(index); + return item ? item->storageSetupNeeded() : false; +} + +void PlacesItemModel::requestStorageSetup(int index) +{ + const PlacesItem* item = placesItem(index); + if (!item) { + return; + } + + Solid::Device device = item->device(); + const bool setup = device.is() + && !m_storageSetupInProgress.contains(device.as()) + && !device.as()->isAccessible(); + if (setup) { + Solid::StorageAccess* access = device.as(); + + m_storageSetupInProgress[access] = index; + + connect(access, SIGNAL(setupDone(Solid::ErrorType,QVariant,QString)), + this, SLOT(slotStorageSetupDone(Solid::ErrorType,QVariant,QString))); + + access->setup(); + } +} + +QMimeData* PlacesItemModel::createMimeData(const KItemSet& indexes) const +{ + KUrl::List urls; + QByteArray itemData; + + QDataStream stream(&itemData, QIODevice::WriteOnly); + + foreach (int index, indexes) { + const KUrl itemUrl = placesItem(index)->url(); + if (itemUrl.isValid()) { + urls << itemUrl; + } + stream << index; + } + + QMimeData* mimeData = new QMimeData(); + if (!urls.isEmpty()) { + urls.populateMimeData(mimeData); + } + mimeData->setData(internalMimeType(), itemData); + + return mimeData; +} + +bool PlacesItemModel::supportsDropping(int index) const +{ + return index >= 0 && index < count(); +} + +void PlacesItemModel::dropMimeDataBefore(int index, const QMimeData* mimeData) +{ + if (mimeData->hasFormat(internalMimeType())) { + // The item has been moved inside the view + QByteArray itemData = mimeData->data(internalMimeType()); + QDataStream stream(&itemData, QIODevice::ReadOnly); + int oldIndex; + stream >> oldIndex; + if (oldIndex == index || oldIndex == index - 1) { + // No moving has been done + return; + } + + PlacesItem* oldItem = placesItem(oldIndex); + if (!oldItem) { + return; + } + + PlacesItem* newItem = new PlacesItem(oldItem->bookmark()); + removeItem(oldIndex); + + if (oldIndex < index) { + --index; + } + + const int dropIndex = groupedDropIndex(index, newItem); + insertItem(dropIndex, newItem); + } else if (mimeData->hasFormat("text/uri-list")) { + // One or more items must be added to the model + const KUrl::List urls = KUrl::List::fromMimeData(mimeData); + for (int i = urls.count() - 1; i >= 0; --i) { + const KUrl& url = urls[i]; + + QString text = url.fileName(); + if (text.isEmpty()) { + text = url.host(); + } + + if ((url.isLocalFile() && !QFileInfo(url.toLocalFile()).isDir()) + || url.protocol() == "trash") { + // Only directories outside the trash are allowed + continue; + } + + PlacesItem* newItem = createPlacesItem(text, url); + const int dropIndex = groupedDropIndex(index, newItem); + insertItem(dropIndex, newItem); + } + } +} + +void PlacesItemModel::onItemInserted(int index) +{ + const PlacesItem* insertedItem = placesItem(index); + if (insertedItem) { + // Take care to apply the PlacesItemModel-order of the inserted item + // also to the bookmark-manager. + const KBookmark insertedBookmark = insertedItem->bookmark(); + + const PlacesItem* previousItem = placesItem(index - 1); + KBookmark previousBookmark; + if (previousItem) { + previousBookmark = previousItem->bookmark(); + } + + m_bookmarkManager->root().moveBookmark(insertedBookmark, previousBookmark); + } + + if (index == count() - 1) { + // The item has been appended as last item to the list. In this + // case assure that it is also appended after the hidden items and + // not before (like done otherwise). + m_bookmarkedItems.append(0); + } else { + + int modelIndex = -1; + int bookmarkIndex = 0; + while (bookmarkIndex < m_bookmarkedItems.count()) { + if (!m_bookmarkedItems[bookmarkIndex]) { + ++modelIndex; + if (modelIndex + 1 == index) { + break; + } + } + ++bookmarkIndex; + } + m_bookmarkedItems.insert(bookmarkIndex, 0); + } + + triggerBookmarksSaving(); + +#ifdef PLACESITEMMODEL_DEBUG + kDebug() << "Inserted item" << index; + showModelState(); +#endif +} + +void PlacesItemModel::onItemRemoved(int index, KStandardItem* removedItem) +{ + PlacesItem* placesItem = dynamic_cast(removedItem); + if (placesItem) { + const KBookmark bookmark = placesItem->bookmark(); + m_bookmarkManager->root().deleteBookmark(bookmark); + } + + const int boomarkIndex = bookmarkIndex(index); + Q_ASSERT(!m_bookmarkedItems[boomarkIndex]); + m_bookmarkedItems.removeAt(boomarkIndex); + + triggerBookmarksSaving(); + +#ifdef PLACESITEMMODEL_DEBUG + kDebug() << "Removed item" << index; + showModelState(); +#endif +} + +void PlacesItemModel::onItemChanged(int index, const QSet& changedRoles) +{ + const PlacesItem* changedItem = placesItem(index); + if (changedItem) { + // Take care to apply the PlacesItemModel-order of the changed item + // also to the bookmark-manager. + const KBookmark insertedBookmark = changedItem->bookmark(); + + const PlacesItem* previousItem = placesItem(index - 1); + KBookmark previousBookmark; + if (previousItem) { + previousBookmark = previousItem->bookmark(); + } + + m_bookmarkManager->root().moveBookmark(insertedBookmark, previousBookmark); + } + + if (changedRoles.contains("isHidden")) { + if (!m_hiddenItemsShown && changedItem->isHidden()) { + m_hiddenItemToRemove = index; + QTimer::singleShot(0, this, SLOT(hideItem())); + } + } + + triggerBookmarksSaving(); +} + +void PlacesItemModel::slotDeviceAdded(const QString& udi) +{ + const Solid::Device device(udi); + + if (!m_predicate.matches(device)) { + return; + } + + m_availableDevices << udi; + const KBookmark bookmark = PlacesItem::createDeviceBookmark(m_bookmarkManager, udi); + appendItem(new PlacesItem(bookmark)); +} + +void PlacesItemModel::slotDeviceRemoved(const QString& udi) +{ + if (!m_availableDevices.contains(udi)) { + return; + } + + for (int i = 0; i < m_bookmarkedItems.count(); ++i) { + PlacesItem* item = m_bookmarkedItems[i]; + if (item && item->udi() == udi) { + m_bookmarkedItems.removeAt(i); + delete item; + return; + } + } + + for (int i = 0; i < count(); ++i) { + if (placesItem(i)->udi() == udi) { + removeItem(i); + return; + } + } +} + +void PlacesItemModel::slotContentChanged(const QString& udi, const bool hascontent) +{ + if (hascontent) { + slotDeviceRemoved(udi); + slotDeviceAdded(udi); + } else { + slotDeviceRemoved(udi); + } +} + +void PlacesItemModel::slotStorageTeardownDone(Solid::ErrorType error, const QVariant& errorData) +{ + if (error && errorData.isValid()) { + emit errorMessage(errorData.toString()); + } +} + +void PlacesItemModel::slotStorageSetupDone(Solid::ErrorType error, + const QVariant& errorData, + const QString& udi) +{ + Q_UNUSED(udi); + + const int index = m_storageSetupInProgress.take(sender()); + const PlacesItem* item = placesItem(index); + if (!item) { + return; + } + + if (error) { + if (errorData.isValid()) { + emit errorMessage(i18nc("@info", "An error occurred while accessing '%1', the system responded: %2", + item->text(), + errorData.toString())); + } else { + emit errorMessage(i18nc("@info", "An error occurred while accessing '%1'", + item->text())); + } + emit storageSetupDone(index, false); + } else { + emit storageSetupDone(index, true); + } +} + +void PlacesItemModel::hideItem() +{ + hideItem(m_hiddenItemToRemove); + m_hiddenItemToRemove = -1; +} + +void PlacesItemModel::updateBookmarks() +{ + // Verify whether new bookmarks have been added or existing + // bookmarks have been changed. + KBookmarkGroup root = m_bookmarkManager->root(); + KBookmark newBookmark = root.first(); + while (!newBookmark.isNull()) { + if (acceptBookmark(newBookmark, m_availableDevices)) { + bool found = false; + int modelIndex = 0; + for (int i = 0; i < m_bookmarkedItems.count(); ++i) { + PlacesItem* item = m_bookmarkedItems[i]; + if (!item) { + item = placesItem(modelIndex); + ++modelIndex; + } + + const KBookmark oldBookmark = item->bookmark(); + if (equalBookmarkIdentifiers(newBookmark, oldBookmark)) { + // The bookmark has been found in the model or as + // a hidden item. The content of the bookmark might + // have been changed, so an update is done. + found = true; + if (newBookmark.metaDataItem("UDI").isEmpty()) { + item->setBookmark(newBookmark); + item->setText(i18nc("KFile System Bookmarks", newBookmark.text().toUtf8().data())); + } + break; + } + } + + if (!found) { + const QString udi = newBookmark.metaDataItem("UDI"); + + /* + * See Bug 304878 + * Only add a new places item, if the item text is not empty + * and if the device is available. Fixes the strange behaviour - + * add a places item without text in the Places section - when you + * remove a device (e.g. a usb stick) without unmounting. + */ + if (udi.isEmpty() || Solid::Device(udi).isValid()) { + PlacesItem* item = new PlacesItem(newBookmark); + if (item->isHidden() && !m_hiddenItemsShown) { + m_bookmarkedItems.append(item); + } else { + appendItemToGroup(item); + } + } + } + } + + newBookmark = root.next(newBookmark); + } + + // Remove items that are not part of the bookmark-manager anymore + int modelIndex = 0; + for (int i = m_bookmarkedItems.count() - 1; i >= 0; --i) { + PlacesItem* item = m_bookmarkedItems[i]; + const bool itemIsPartOfModel = (item == 0); + if (itemIsPartOfModel) { + item = placesItem(modelIndex); + } + + bool hasBeenRemoved = true; + const KBookmark oldBookmark = item->bookmark(); + KBookmark newBookmark = root.first(); + while (!newBookmark.isNull()) { + if (equalBookmarkIdentifiers(newBookmark, oldBookmark)) { + hasBeenRemoved = false; + break; + } + newBookmark = root.next(newBookmark); + } + + if (hasBeenRemoved) { + if (m_bookmarkedItems[i]) { + delete m_bookmarkedItems[i]; + m_bookmarkedItems.removeAt(i); + } else { + removeItem(modelIndex); + --modelIndex; + } + } + + if (itemIsPartOfModel) { + ++modelIndex; + } + } +} + +void PlacesItemModel::saveBookmarks() +{ + m_bookmarkManager->emitChanged(m_bookmarkManager->root()); +} + +void PlacesItemModel::loadBookmarks() +{ + KBookmarkGroup root = m_bookmarkManager->root(); + KBookmark bookmark = root.first(); + QSet devices = m_availableDevices; + + QSet missingSystemBookmarks; + foreach (const SystemBookmarkData& data, m_systemBookmarks) { + missingSystemBookmarks.insert(data.url); + } + + // The bookmarks might have a mixed order of places and devices due + // to the compatibility with the KFilePlacesPanel. In Dolphin's places panel the + // items should always be collected in one group so the items are collected first + // in separate lists before inserting them. + QList placesItems; + QList devicesItems; + + while (!bookmark.isNull()) { + if (acceptBookmark(bookmark, devices)) { + PlacesItem* item = new PlacesItem(bookmark); + if (item->groupType() == PlacesItem::DevicesType) { + devices.remove(item->udi()); + devicesItems.append(item); + } else { + const KUrl url = bookmark.url(); + if (missingSystemBookmarks.contains(url)) { + missingSystemBookmarks.remove(url); + + // Try to retranslate the text of system bookmarks to have translated + // items when changing the language. In case if the user has applied a custom + // text, the retranslation will fail and the users custom text is still used. + // It is important to use "KFile System Bookmarks" as context (see + // createSystemBookmarks()). + item->setText(i18nc("KFile System Bookmarks", bookmark.text().toUtf8().data())); + item->setSystemItem(true); + } + + switch (item->groupType()) { + case PlacesItem::PlacesType: placesItems.append(item); break; + case PlacesItem::DevicesType: + default: Q_ASSERT(false); break; + } + } + } + + bookmark = root.next(bookmark); + } + + if (!missingSystemBookmarks.isEmpty()) { + // The current bookmarks don't contain all system-bookmarks. Add the missing + // bookmarks. + foreach (const SystemBookmarkData& data, m_systemBookmarks) { + if (missingSystemBookmarks.contains(data.url)) { + PlacesItem* item = createSystemPlacesItem(data); + switch (item->groupType()) { + case PlacesItem::PlacesType: placesItems.append(item); break; + case PlacesItem::DevicesType: + default: Q_ASSERT(false); break; + } + } + } + } + + // Create items for devices that have not been stored as bookmark yet + foreach (const QString& udi, devices) { + const KBookmark bookmark = PlacesItem::createDeviceBookmark(m_bookmarkManager, udi); + devicesItems.append(new PlacesItem(bookmark)); + } + + QList items; + items.append(placesItems); + items.append(devicesItems); + + foreach (PlacesItem* item, items) { + if (!m_hiddenItemsShown && item->isHidden()) { + m_bookmarkedItems.append(item); + } else { + appendItem(item); + } + } + +#ifdef PLACESITEMMODEL_DEBUG + kDebug() << "Loaded bookmarks"; + showModelState(); +#endif +} + +bool PlacesItemModel::acceptBookmark(const KBookmark& bookmark, + const QSet& availableDevices) const +{ + const QString udi = bookmark.metaDataItem("UDI"); + const KUrl url = bookmark.url(); + const QString appName = bookmark.metaDataItem("OnlyInApp"); + const bool deviceAvailable = availableDevices.contains(udi); + + const bool allowedHere = (appName.isEmpty() + || appName == KGlobal::mainComponent().componentName() + || appName == KGlobal::mainComponent().componentName() + AppNamePrefix); + + return (udi.isEmpty() && allowedHere) || deviceAvailable; +} + +PlacesItem* PlacesItemModel::createSystemPlacesItem(const SystemBookmarkData& data) +{ + KBookmark bookmark = PlacesItem::createBookmark(m_bookmarkManager, + data.text, + data.url, + data.icon); + + PlacesItem* item = new PlacesItem(bookmark); + item->setSystemItem(true); + + return item; +} + +void PlacesItemModel::createSystemBookmarks() +{ + Q_ASSERT(m_systemBookmarks.isEmpty()); + Q_ASSERT(m_systemBookmarksIndexes.isEmpty()); + + // Note: The context of the I18N_NOOP2 must be "KFile System Bookmarks". The real + // i18nc call is done after reading the bookmark. The reason why the i18nc call is not + // done here is because otherwise switching the language would not result in retranslating the + // bookmarks. + m_systemBookmarks.append(SystemBookmarkData(KUrl(KUser().homeDir()), + "user-home", + I18N_NOOP2("KFile System Bookmarks", "Home"))); + m_systemBookmarks.append(SystemBookmarkData(KUrl("remote:/"), + "network-workgroup", + I18N_NOOP2("KFile System Bookmarks", "Network"))); + m_systemBookmarks.append(SystemBookmarkData(KUrl("/"), + "folder-red", + I18N_NOOP2("KFile System Bookmarks", "Root"))); + m_systemBookmarks.append(SystemBookmarkData(KUrl("trash:/"), + "user-trash", + I18N_NOOP2("KFile System Bookmarks", "Trash"))); + + for (int i = 0; i < m_systemBookmarks.count(); ++i) { + m_systemBookmarksIndexes.insert(m_systemBookmarks[i].url, i); + } +} + +void PlacesItemModel::clear() { + m_bookmarkedItems.clear(); + KStandardItemModel::clear(); +} + +void PlacesItemModel::initializeAvailableDevices() +{ + QString predicate("[[[[ StorageVolume.ignored == false AND [ StorageVolume.usage == 'FileSystem' OR StorageVolume.usage == 'Encrypted' ]]" + " OR " + "[ IS StorageAccess AND StorageDrive.driveType == 'Floppy' ]]" + " OR " + "OpticalDisc.availableContent & 'Audio' ]" + " OR " + "StorageAccess.ignored == false ]"); + + + if (KProtocolInfo::isKnownProtocol("mtp")) { + predicate.prepend("["); + predicate.append(" OR PortableMediaPlayer.supportedProtocols == 'mtp']"); + } + + m_predicate = Solid::Predicate::fromString(predicate); + Q_ASSERT(m_predicate.isValid()); + + Solid::DeviceNotifier* notifier = Solid::DeviceNotifier::instance(); + connect(notifier, SIGNAL(deviceAdded(QString)), this, SLOT(slotDeviceAdded(QString))); + connect(notifier, SIGNAL(deviceRemoved(QString)), this, SLOT(slotDeviceRemoved(QString))); + connect(notifier, SIGNAL(contentChanged(QString,bool)), this, SLOT(slotContentChanged(QString,bool))); + + const QList& deviceList = Solid::Device::listFromQuery(m_predicate); + foreach (const Solid::Device& device, deviceList) { + m_availableDevices << device.udi(); + } +} + +int PlacesItemModel::bookmarkIndex(int index) const +{ + int bookmarkIndex = 0; + int modelIndex = 0; + while (bookmarkIndex < m_bookmarkedItems.count()) { + if (!m_bookmarkedItems[bookmarkIndex]) { + if (modelIndex == index) { + break; + } + ++modelIndex; + } + ++bookmarkIndex; + } + + return bookmarkIndex >= m_bookmarkedItems.count() ? -1 : bookmarkIndex; +} + +void PlacesItemModel::hideItem(int index) +{ + PlacesItem* shownItem = placesItem(index); + if (!shownItem) { + return; + } + + shownItem->setHidden(true); + if (m_hiddenItemsShown) { + // Removing items from the model is not allowed if all hidden + // items should be shown. + return; + } + + const int newIndex = bookmarkIndex(index); + if (newIndex >= 0) { + const KBookmark hiddenBookmark = shownItem->bookmark(); + PlacesItem* hiddenItem = new PlacesItem(hiddenBookmark); + + const PlacesItem* previousItem = placesItem(index - 1); + KBookmark previousBookmark; + if (previousItem) { + previousBookmark = previousItem->bookmark(); + } + + const bool updateBookmark = (m_bookmarkManager->root().indexOf(hiddenBookmark) >= 0); + removeItem(index); + + if (updateBookmark) { + // removeItem() also removed the bookmark from m_bookmarkManager in + // PlacesItemModel::onItemRemoved(). However for hidden items the + // bookmark should still be remembered, so readd it again: + m_bookmarkManager->root().addBookmark(hiddenBookmark); + m_bookmarkManager->root().moveBookmark(hiddenBookmark, previousBookmark); + triggerBookmarksSaving(); + } + + m_bookmarkedItems.insert(newIndex, hiddenItem); + } +} + +void PlacesItemModel::triggerBookmarksSaving() +{ + if (m_saveBookmarksTimer) { + m_saveBookmarksTimer->start(); + } +} + +QString PlacesItemModel::internalMimeType() const +{ + return "application/x-dolphinplacesmodel-" + + QString::number((qptrdiff)this); +} + +int PlacesItemModel::groupedDropIndex(int index, const PlacesItem* item) const +{ + Q_ASSERT(item); + + int dropIndex = index; + const PlacesItem::GroupType type = item->groupType(); + + const int itemCount = count(); + if (index < 0) { + dropIndex = itemCount; + } + + // Search nearest previous item with the same group + int previousIndex = -1; + for (int i = dropIndex - 1; i >= 0; --i) { + if (placesItem(i)->groupType() == type) { + previousIndex = i; + break; + } + } + + // Search nearest next item with the same group + int nextIndex = -1; + for (int i = dropIndex; i < count(); ++i) { + if (placesItem(i)->groupType() == type) { + nextIndex = i; + break; + } + } + + // Adjust the drop-index to be inserted to the + // nearest item with the same group. + if (previousIndex >= 0 && nextIndex >= 0) { + dropIndex = (dropIndex - previousIndex < nextIndex - dropIndex) ? + previousIndex + 1 : nextIndex; + } else if (previousIndex >= 0) { + dropIndex = previousIndex + 1; + } else if (nextIndex >= 0) { + dropIndex = nextIndex; + } + + return dropIndex; +} + +bool PlacesItemModel::equalBookmarkIdentifiers(const KBookmark& b1, const KBookmark& b2) +{ + const QString udi1 = b1.metaDataItem("UDI"); + const QString udi2 = b2.metaDataItem("UDI"); + if (!udi1.isEmpty() && !udi2.isEmpty()) { + return udi1 == udi2; + } else { + return b1.metaDataItem("ID") == b2.metaDataItem("ID"); + } +} + +#ifdef PLACESITEMMODEL_DEBUG +void PlacesItemModel::showModelState() +{ + kDebug() << "================================="; + kDebug() << "Model:"; + kDebug() << "hidden-index model-index text"; + int modelIndex = 0; + for (int i = 0; i < m_bookmarkedItems.count(); ++i) { + if (m_bookmarkedItems[i]) { + kDebug() << i << "(Hidden) " << " " << m_bookmarkedItems[i]->dataValue("text").toString(); + } else { + if (item(modelIndex)) { + kDebug() << i << " " << modelIndex << " " << item(modelIndex)->dataValue("text").toString(); + } else { + kDebug() << i << " " << modelIndex << " " << "(not available yet)"; + } + ++modelIndex; + } + } + + kDebug(); + kDebug() << "Bookmarks:"; + + int bookmarkIndex = 0; + KBookmarkGroup root = m_bookmarkManager->root(); + KBookmark bookmark = root.first(); + while (!bookmark.isNull()) { + const QString udi = bookmark.metaDataItem("UDI"); + const QString text = udi.isEmpty() ? bookmark.text() : udi; + if (bookmark.metaDataItem("IsHidden") == QLatin1String("true")) { + kDebug() << bookmarkIndex << "(Hidden)" << text; + } else { + kDebug() << bookmarkIndex << " " << text; + } + + bookmark = root.next(bookmark); + ++bookmarkIndex; + } +} +#endif + +#include "moc_placesitemmodel.cpp" diff --git a/dolphin/src/panels/places/placesitemmodel.h b/dolphin/src/panels/places/placesitemmodel.h new file mode 100644 index 00000000..eb951a64 --- /dev/null +++ b/dolphin/src/panels/places/placesitemmodel.h @@ -0,0 +1,260 @@ +/*************************************************************************** + * Copyright (C) 2012 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef PLACESITEMMODEL_H +#define PLACESITEMMODEL_H + +#include + +#include +#include +#include +#include +#include +#include + +class KBookmark; +class KBookmarkManager; +class PlacesItem; +#include +#include + +// #define PLACESITEMMODEL_DEBUG + +/** + * @brief Model for maintaining the bookmarks of the places panel. + * + * It is compatible to the KFilePlacesModel from kdelibs but adds + * the ability to have groups for places. + */ +class PlacesItemModel: public KStandardItemModel +{ + Q_OBJECT + +public: + explicit PlacesItemModel(QObject* parent = 0); + virtual ~PlacesItemModel(); + + /** + * @return A new instance of a places item with the given + * attributes. + */ + PlacesItem* createPlacesItem(const QString& text, + const KUrl& url, + const QString& iconName = QString()); + + PlacesItem* placesItem(int index) const; + + /** + * If set to true, all items that are marked as hidden + * will be shown in the view. The items will + * stay marked as hidden, which is visually indicated + * by the view by desaturating the icon and the text. + */ + void setHiddenItemsShown(bool show); + bool hiddenItemsShown() const; + + /** + * @return Number of items that are marked as hidden. + * Note that this does not mean that the items + * are really hidden + * (see PlacesItemModel::setHiddenItemsShown()). + */ + int hiddenCount() const; + + /** + * Search the item which is equal to the URL or at least + * is a parent URL. If there are more than one possible + * candidates, return the item which covers the biggest + * range of the URL. -1 is returned if no closest item + * could be found. + */ + int closestItem(const KUrl& url) const; + + /** + * Appends the item \a item as last element of the group + * the item belongs to. If no item with the same group is + * present, the item gets appended as last element of the + * model. PlacesItemModel takes the ownership + * of the item. + */ + void appendItemToGroup(PlacesItem* item); + + QAction* ejectAction(int index) const; + QAction* teardownAction(int index) const; + + void requestEject(int index); + void requestTeardown(int index); + + bool storageSetupNeeded(int index) const; + void requestStorageSetup(int index); + + /** @reimp */ + virtual QMimeData* createMimeData(const KItemSet& indexes) const; + + /** @reimp */ + virtual bool supportsDropping(int index) const; + + void dropMimeDataBefore(int index, const QMimeData* mimeData); + + virtual void clear(); +signals: + void errorMessage(const QString& message); + void storageSetupDone(int index, bool success); + +protected: + virtual void onItemInserted(int index); + virtual void onItemRemoved(int index, KStandardItem* removedItem); + virtual void onItemChanged(int index, const QSet& changedRoles); + +private slots: + void slotDeviceAdded(const QString& udi); + void slotDeviceRemoved(const QString& udi); + void slotContentChanged(const QString& udi, const bool hascontent); + void slotStorageTeardownDone(Solid::ErrorType error, const QVariant& errorData); + void slotStorageSetupDone(Solid::ErrorType error, const QVariant& errorData, const QString& udi); + void hideItem(); + + /** + * Updates the bookmarks from the model corresponding to the changed + * bookmarks stored by the bookmark-manager. Is called whenever the bookmarks + * have been changed by another application. + */ + void updateBookmarks(); + + /** + * Saves the bookmarks and indicates to other applications that the + * state of the bookmarks has been changed. Is only called by the + * timeout of m_saveBookmarksTimer to prevent unnecessary savings. + */ + void saveBookmarks(); +private: + struct SystemBookmarkData; + + /** + * Loads the bookmarks from the bookmark-manager and creates items for + * the model or moves hidden items to m_bookmarkedItems. + */ + void loadBookmarks(); + + /** + * @return True, if the bookmark can be accepted in the context of the + * current application (e.g. bookmarks from other applications + * will be ignored). + */ + bool acceptBookmark(const KBookmark& bookmark, + const QSet& availableDevices) const; + + /** + * Creates a PlacesItem for a system-bookmark: + * - PlacesItem::isSystemItem() will return true + * - Default view-properties will be created for "Search For" items + * The item is not inserted to the model yet. + */ + PlacesItem* createSystemPlacesItem(const SystemBookmarkData& data); + + /** + * Creates system bookmarks that are shown per default and can + * only be hidden but not removed. The result will be stored + * in m_systemBookmarks. + */ + void createSystemBookmarks(); + + void initializeAvailableDevices(); + + /** + * @param index Item index related to the model. + * @return Corresponding index related to m_bookmarkedItems. + */ + int bookmarkIndex(int index) const; + + /** + * Marks the item with the index \a index as hidden and + * removes it from the model so that it gets invisible. + */ + void hideItem(int index); + + /** + * Triggers a delayed saving of bookmarks by starting + * m_saveBookmarksTimer. + */ + void triggerBookmarksSaving(); + + QString internalMimeType() const; + + /** + * @return Adjusted drop index which assures that the item is aligned + * into the same group as specified by PlacesItem::groupType(). + */ + int groupedDropIndex(int index, const PlacesItem* item) const; + + /** + * @return True if the bookmarks have the same identifiers. The identifier + * is the unique "ID"-property in case if no UDI is set, otherwise + * the UDI is used as identifier. + */ + static bool equalBookmarkIdentifiers(const KBookmark& b1, const KBookmark& b2); + +#ifdef PLACESITEMMODEL_DEBUG + void showModelState(); +#endif + +private: + bool m_hiddenItemsShown; + + QSet m_availableDevices; + Solid::Predicate m_predicate; + KBookmarkManager* m_bookmarkManager; + + struct SystemBookmarkData + { + SystemBookmarkData(const KUrl& url, + const QString& icon, + const QString& text) : + url(url), icon(icon), text(text) {} + KUrl url; + QString icon; + QString text; + }; + + QList m_systemBookmarks; + QHash m_systemBookmarksIndexes; + + // Contains hidden and unhidden items that are stored as + // bookmark (the model itself only contains items that + // are shown in the view). If an entry is 0, then the + // places-item is part of the model. If an entry is not + // 0, the item is hidden and not part of the model. + QList m_bookmarkedItems; + + // Index of the hidden item that should be removed in + // removeHiddenItem(). The removing must be done + // asynchronously as in the scope of onItemChanged() + // removing an item is not allowed. + int m_hiddenItemToRemove; + + QTimer* m_saveBookmarksTimer; + QTimer* m_updateBookmarksTimer; + + QHash m_storageSetupInProgress; +}; + +#endif + + diff --git a/dolphin/src/panels/places/placesitemsignalhandler.cpp b/dolphin/src/panels/places/placesitemsignalhandler.cpp new file mode 100644 index 00000000..9f71421b --- /dev/null +++ b/dolphin/src/panels/places/placesitemsignalhandler.cpp @@ -0,0 +1,50 @@ +/*************************************************************************** + * Copyright (C) 2012 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "placesitemsignalhandler.h" + +#include "placesitem.h" +#include + +PlacesItemSignalHandler::PlacesItemSignalHandler(PlacesItem* item, + QObject* parent) : + QObject(parent), + m_item(item) +{ +} + +PlacesItemSignalHandler::~PlacesItemSignalHandler() +{ +} + +void PlacesItemSignalHandler::onAccessibilityChanged() +{ + if (m_item) { + m_item->onAccessibilityChanged(); + } +} + +void PlacesItemSignalHandler::onTrashConfigChange(const QString &config) +{ + if (m_item) { + m_item->onTrashConfigChange(config); + } +} + +#include "moc_placesitemsignalhandler.cpp" diff --git a/dolphin/src/panels/places/placesitemsignalhandler.h b/dolphin/src/panels/places/placesitemsignalhandler.h new file mode 100644 index 00000000..1ab2b24e --- /dev/null +++ b/dolphin/src/panels/places/placesitemsignalhandler.h @@ -0,0 +1,68 @@ +/*************************************************************************** + * Copyright (C) 2012 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef PLACESITEMSIGNALHANDLER_H +#define PLACESITEMSIGNALHANDLER_H + +#include + +class PlacesItem; + +/** + * @brief Helper class for PlacesItem to be able to listen to signals + * and performing a corresponding action. + * + * PlacesItem is derived from KStandardItem, which is no QObject-class + * on purpose. To be able to internally listening to signals and performing a + * corresponding action, PlacesItemSignalHandler is used. + * + * E.g. if the PlacesItem wants to react on accessibility-changes of a storage-access, + * the signal-handler can be used like this: + * + * QObject::connect(storageAccess, SIGNAL(accessibilityChanged(bool,QString)), + * signalHandler, SLOT(onAccessibilityChanged())); + * + * + * The slot PlacesItemSignalHandler::onAccessibilityChanged() will call + * the method PlacesItem::onAccessibilityChanged(). + */ +class PlacesItemSignalHandler: public QObject +{ + Q_OBJECT + +public: + explicit PlacesItemSignalHandler(PlacesItem* item, QObject* parent = 0); + virtual ~PlacesItemSignalHandler(); + +public slots: + /** + * Calls PlacesItem::onAccessibilityChanged() + */ + void onAccessibilityChanged(); + + /** + * Calls PlacesItem::onTrashDirListerCompleted() + */ + void onTrashConfigChange(const QString &config); + +private: + PlacesItem* m_item; +}; + +#endif diff --git a/dolphin/src/panels/places/placespanel.cpp b/dolphin/src/panels/places/placespanel.cpp new file mode 100644 index 00000000..ff4ce730 --- /dev/null +++ b/dolphin/src/panels/places/placespanel.cpp @@ -0,0 +1,543 @@ +/*************************************************************************** + * Copyright (C) 2008-2012 by Peter Penz * + * * + * Based on KFilePlacesView from kdelibs: * + * Copyright (C) 2007 Kevin Ottens * + * Copyright (C) 2007 David Faure * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "placespanel.h" + +#include "dolphin_generalsettings.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "placesitem.h" +#include "placesitemeditdialog.h" +#include "placesitemlistgroupheader.h" +#include "placesitemlistwidget.h" +#include "placesitemmodel.h" +#include "placesview.h" +#include +#include +#include +#include + +PlacesPanel::PlacesPanel(QWidget* parent) : + Panel(parent), + m_controller(0), + m_model(0), + m_storageSetupFailedUrl(), + m_triggerStorageSetupButton(), + m_itemDropEventIndex(-1), + m_itemDropEventMimeData(0), + m_itemDropEvent(0) +{ +} + +PlacesPanel::~PlacesPanel() +{ +} + +bool PlacesPanel::urlChanged() +{ + if (!url().isValid() || url().protocol() == "filenamesearch") { + // Skip results shown by a search, as possible identical + // directory names are useless without parent-path information. + return false; + } + + if (m_controller) { + selectClosestItem(); + } + + return true; +} + +void PlacesPanel::readSettings() +{ + if (m_controller) { + const int delay = GeneralSettings::autoExpandFolders() ? 750 : -1; + m_controller->setAutoActivationDelay(delay); + } +} + +void PlacesPanel::showEvent(QShowEvent* event) +{ + if (event->spontaneous()) { + Panel::showEvent(event); + return; + } + + if (!m_controller) { + // Postpone the creating of the controller to the first show event. + // This assures that no performance and memory overhead is given when the folders panel is not + // used at all and stays invisible. + m_model = new PlacesItemModel(this); + m_model->setGroupedSorting(true); + connect(m_model, SIGNAL(errorMessage(QString)), + this, SIGNAL(errorMessage(QString))); + + m_view = new PlacesView(); + m_view->setWidgetCreator(new KItemListWidgetCreator()); + m_view->setGroupHeaderCreator(new KItemListGroupHeaderCreator()); + + m_controller = new KItemListController(m_model, m_view, this); + m_controller->setSelectionBehavior(KItemListController::SingleSelection); + m_controller->setSingleClickActivationEnforced(true); + + readSettings(); + + connect(m_controller, SIGNAL(itemActivated(int)), this, SLOT(slotItemActivated(int))); + connect(m_controller, SIGNAL(itemMiddleClicked(int)), this, SLOT(slotItemMiddleClicked(int))); + connect(m_controller, SIGNAL(itemContextMenuRequested(int,QPointF)), this, SLOT(slotItemContextMenuRequested(int,QPointF))); + connect(m_controller, SIGNAL(viewContextMenuRequested(QPointF)), this, SLOT(slotViewContextMenuRequested(QPointF))); + connect(m_controller, SIGNAL(itemDropEvent(int,QGraphicsSceneDragDropEvent*)), this, SLOT(slotItemDropEvent(int,QGraphicsSceneDragDropEvent*))); + connect(m_controller, SIGNAL(aboveItemDropEvent(int,QGraphicsSceneDragDropEvent*)), this, SLOT(slotAboveItemDropEvent(int,QGraphicsSceneDragDropEvent*))); + + KItemListContainer* container = new KItemListContainer(m_controller, this); + container->setEnabledFrame(false); + + QVBoxLayout* layout = new QVBoxLayout(this); + layout->setMargin(0); + layout->addWidget(container); + + selectClosestItem(); + } + + Panel::showEvent(event); +} + +void PlacesPanel::slotItemActivated(int index) +{ + triggerItem(index, Qt::LeftButton); +} + +void PlacesPanel::slotItemMiddleClicked(int index) +{ + triggerItem(index, Qt::MiddleButton); +} + +void PlacesPanel::slotItemContextMenuRequested(int index, const QPointF& pos) +{ + PlacesItem* item = m_model->placesItem(index); + if (!item) { + return; + } + + KMenu menu(this); + + QAction* emptyTrashAction = 0; + QAction* addAction = 0; + QAction* mainSeparator = 0; + QAction* editAction = 0; + QAction* teardownAction = 0; + QAction* ejectAction = 0; + + const QString label = item->text(); + + const bool isDevice = !item->udi().isEmpty(); + if (isDevice) { + ejectAction = m_model->ejectAction(index); + if (ejectAction) { + ejectAction->setParent(&menu); + menu.addAction(ejectAction); + } + + teardownAction = m_model->teardownAction(index); + if (teardownAction) { + teardownAction->setParent(&menu); + menu.addAction(teardownAction); + } + + if (teardownAction || ejectAction) { + mainSeparator = menu.addSeparator(); + } + } else { + if (item->url() == KUrl("trash:/")) { + emptyTrashAction = menu.addAction(KIcon("trash-empty"), i18nc("@action:inmenu", "Empty Trash")); + emptyTrashAction->setEnabled(item->icon() == "user-trash-full"); + menu.addSeparator(); + } + addAction = menu.addAction(KIcon("document-new"), i18nc("@item:inmenu", "Add Entry...")); + mainSeparator = menu.addSeparator(); + editAction = menu.addAction(KIcon("document-properties"), i18nc("@item:inmenu", "Edit '%1'...", label)); + } + + if (!addAction) { + addAction = menu.addAction(KIcon("document-new"), i18nc("@item:inmenu", "Add Entry...")); + } + + QAction* openInNewTabAction = menu.addAction(i18nc("@item:inmenu", "Open '%1' in New Tab", label)); + openInNewTabAction->setIcon(KIcon("tab-new")); + + QAction* removeAction = 0; + if (!isDevice && !item->isSystemItem()) { + removeAction = menu.addAction(KIcon("edit-delete"), i18nc("@item:inmenu", "Remove '%1'", label)); + } + + QAction* hideAction = menu.addAction(i18nc("@item:inmenu", "Hide '%1'", label)); + hideAction->setCheckable(true); + hideAction->setChecked(item->isHidden()); + + QAction* showAllAction = 0; + if (m_model->hiddenCount() > 0) { + if (!mainSeparator) { + mainSeparator = menu.addSeparator(); + } + showAllAction = menu.addAction(i18nc("@item:inmenu", "Show All Entries")); + showAllAction->setCheckable(true); + showAllAction->setChecked(m_model->hiddenItemsShown()); + } + + menu.addSeparator(); + KMenu* iconSizeSubMenu = new KMenu(i18nc("@item:inmenu", "Icon Size"), &menu); + + struct IconSizeInfo + { + int size; + const char* context; + const char* text; + }; + + const int iconSizeCount = 4; + static const IconSizeInfo iconSizes[iconSizeCount] = { + {KIconLoader::SizeSmall, I18N_NOOP2_NOSTRIP("Small icon size", "Small (%1x%2)")}, + {KIconLoader::SizeSmallMedium, I18N_NOOP2_NOSTRIP("Medium icon size", "Medium (%1x%2)")}, + {KIconLoader::SizeMedium, I18N_NOOP2_NOSTRIP("Large icon size", "Large (%1x%2)")}, + {KIconLoader::SizeLarge, I18N_NOOP2_NOSTRIP("Huge icon size", "Huge (%1x%2)")} + }; + + QMap iconSizeActionMap; + QActionGroup* iconSizeGroup = new QActionGroup(iconSizeSubMenu); + + for (int i = 0; i < iconSizeCount; ++i) { + const int size = iconSizes[i].size; + const QString text = i18nc(iconSizes[i].context, iconSizes[i].text, + size, size); + + QAction* action = iconSizeSubMenu->addAction(text); + iconSizeActionMap.insert(action, size); + action->setActionGroup(iconSizeGroup); + action->setCheckable(true); + action->setChecked(m_view->iconSize() == size); + } + + menu.addMenu(iconSizeSubMenu); + + menu.addSeparator(); + foreach (QAction* action, customContextMenuActions()) { + menu.addAction(action); + } + + QAction* action = menu.exec(pos.toPoint()); + if (action) { + if (action == emptyTrashAction) { + emptyTrash(); + } else if (action == addAction) { + addEntry(); + } else if (action == showAllAction) { + m_model->setHiddenItemsShown(showAllAction->isChecked()); + } else if (iconSizeActionMap.contains(action)) { + m_view->setIconSize(iconSizeActionMap.value(action)); + } else { + // The index might have changed if devices were added/removed while + // the context menu was open. + index = m_model->index(item); + if (index < 0) { + // The item is not in the model any more, probably because it was an + // external device that has been removed while the context menu was open. + return; + } + + if (action == editAction) { + editEntry(index); + } else if (action == removeAction) { + m_model->removeItem(index); + } else if (action == hideAction) { + item->setHidden(hideAction->isChecked()); + } else if (action == openInNewTabAction) { + // TriggerItem does set up the storage first and then it will + // emit the slotItemMiddleClicked signal, because of Qt::MiddleButton. + triggerItem(index, Qt::MiddleButton); + } else if (action == teardownAction) { + m_model->requestTeardown(index); + } else if (action == ejectAction) { + m_model->requestEject(index); + } + } + } + + selectClosestItem(); +} + +void PlacesPanel::slotViewContextMenuRequested(const QPointF& pos) +{ + KMenu menu(this); + + QAction* addAction = menu.addAction(KIcon("document-new"), i18nc("@item:inmenu", "Add Entry...")); + + QAction* showAllAction = 0; + if (m_model->hiddenCount() > 0) { + showAllAction = menu.addAction(i18nc("@item:inmenu", "Show All Entries")); + showAllAction->setCheckable(true); + showAllAction->setChecked(m_model->hiddenItemsShown()); + } + + menu.addSeparator(); + foreach (QAction* action, customContextMenuActions()) { + menu.addAction(action); + } + + QAction* action = menu.exec(pos.toPoint()); + if (action) { + if (action == addAction) { + addEntry(); + } else if (action == showAllAction) { + m_model->setHiddenItemsShown(showAllAction->isChecked()); + } + } + + selectClosestItem(); +} + +void PlacesPanel::slotItemDropEvent(int index, QGraphicsSceneDragDropEvent* event) +{ + if (index < 0) { + return; + } + + const PlacesItem* destItem = m_model->placesItem(index); + const PlacesItem::GroupType group = destItem->groupType(); + + if (m_model->storageSetupNeeded(index)) { + connect(m_model, SIGNAL(storageSetupDone(int,bool)), + this, SLOT(slotItemDropEventStorageSetupDone(int,bool))); + + m_itemDropEventIndex = index; + + // Make a full copy of the Mime-Data + m_itemDropEventMimeData = new QMimeData; + m_itemDropEventMimeData->setText(event->mimeData()->text()); + m_itemDropEventMimeData->setHtml(event->mimeData()->html()); + m_itemDropEventMimeData->setUrls(event->mimeData()->urls()); + m_itemDropEventMimeData->setImageData(event->mimeData()->imageData()); + m_itemDropEventMimeData->setColorData(event->mimeData()->colorData()); + + m_itemDropEvent = new QDropEvent(event->pos().toPoint(), + event->possibleActions(), + m_itemDropEventMimeData, + event->buttons(), + event->modifiers()); + + m_model->requestStorageSetup(index); + return; + } + + KUrl destUrl = destItem->url(); + QDropEvent dropEvent(event->pos().toPoint(), + event->possibleActions(), + event->mimeData(), + event->buttons(), + event->modifiers()); + + QString error; + DragAndDropHelper::dropUrls(KFileItem(), destUrl, &dropEvent, error); + if (!error.isEmpty()) { + emit errorMessage(error); + } +} + +void PlacesPanel::slotItemDropEventStorageSetupDone(int index, bool success) +{ + disconnect(m_model, SIGNAL(storageSetupDone(int,bool)), + this, SLOT(slotItemDropEventStorageSetupDone(int,bool))); + + if ((index == m_itemDropEventIndex) && m_itemDropEvent && m_itemDropEventMimeData) { + if (success) { + KUrl destUrl = m_model->placesItem(index)->url(); + + QString error; + DragAndDropHelper::dropUrls(KFileItem(), destUrl, m_itemDropEvent, error); + if (!error.isEmpty()) { + emit errorMessage(error); + } + } + + delete m_itemDropEventMimeData; + delete m_itemDropEvent; + + m_itemDropEventIndex = -1; + m_itemDropEventMimeData = 0; + m_itemDropEvent = 0; + } +} + +void PlacesPanel::slotAboveItemDropEvent(int index, QGraphicsSceneDragDropEvent* event) +{ + m_model->dropMimeDataBefore(index, event->mimeData()); +} + +void PlacesPanel::slotUrlsDropped(const KUrl& dest, QDropEvent* event, QWidget* parent) +{ + Q_UNUSED(parent); + QString error; + DragAndDropHelper::dropUrls(KFileItem(), dest, event, error); + if (!error.isEmpty()) { + emit errorMessage(error); + } + +} + +void PlacesPanel::slotTrashUpdated(KJob* job) +{ + if (job->error()) { + emit errorMessage(job->errorString()); + } + org::kde::KDirNotify::emitFilesAdded("trash:/"); +} + +void PlacesPanel::slotStorageSetupDone(int index, bool success) +{ + disconnect(m_model, SIGNAL(storageSetupDone(int,bool)), + this, SLOT(slotStorageSetupDone(int,bool))); + + if (m_triggerStorageSetupButton == Qt::NoButton) { + return; + } + + if (success) { + Q_ASSERT(!m_model->storageSetupNeeded(index)); + triggerItem(index, m_triggerStorageSetupButton); + m_triggerStorageSetupButton = Qt::NoButton; + } else { + setUrl(m_storageSetupFailedUrl); + m_storageSetupFailedUrl = KUrl(); + } +} + +void PlacesPanel::emptyTrash() +{ + const QString text = i18nc("@info", "Do you really want to empty the Trash? All items will be deleted."); + const bool del = KMessageBox::warningContinueCancel(window(), + text, + QString(), + KGuiItem(i18nc("@action:button", "Empty Trash"), + KIcon("user-trash")) + ) == KMessageBox::Continue; + if (del) { + QByteArray packedArgs; + QDataStream stream(&packedArgs, QIODevice::WriteOnly); + stream << int(1); + KIO::Job *job = KIO::special(KUrl("trash:/"), packedArgs); + KNotification::event("Trash: emptied", QString() , QPixmap() , 0, KNotification::DefaultEvent); + job->ui()->setWindow(parentWidget()); + connect(job, SIGNAL(result(KJob*)), SLOT(slotTrashUpdated(KJob*))); + } +} + +void PlacesPanel::addEntry() +{ + const int index = m_controller->selectionManager()->currentItem(); + const KUrl url = m_model->data(index).value("url").value(); + + QPointer dialog = new PlacesItemEditDialog(this); + dialog->setCaption(i18nc("@title:window", "Add Places Entry")); + dialog->setAllowGlobal(true); + dialog->setUrl(url); + if (dialog->exec() == QDialog::Accepted) { + PlacesItem* item = m_model->createPlacesItem(dialog->text(), dialog->url(), dialog->icon()); + m_model->appendItemToGroup(item); + } + + delete dialog; +} + +void PlacesPanel::editEntry(int index) +{ + QHash data = m_model->data(index); + + QPointer dialog = new PlacesItemEditDialog(this); + dialog->setCaption(i18nc("@title:window", "Edit Places Entry")); + dialog->setIcon(data.value("iconName").toString()); + dialog->setText(data.value("text").toString()); + dialog->setUrl(data.value("url").value()); + dialog->setAllowGlobal(true); + if (dialog->exec() == QDialog::Accepted) { + PlacesItem* oldItem = m_model->placesItem(index); + if (oldItem) { + oldItem->setText(dialog->text()); + oldItem->setUrl(dialog->url()); + oldItem->setIcon(dialog->icon()); + } + } + + delete dialog; +} + +void PlacesPanel::selectClosestItem() +{ + const int index = m_model->closestItem(url()); + KItemListSelectionManager* selectionManager = m_controller->selectionManager(); + selectionManager->setCurrentItem(index); + selectionManager->clearSelection(); + selectionManager->setSelected(index); +} + +void PlacesPanel::triggerItem(int index, Qt::MouseButton button) +{ + const PlacesItem* item = m_model->placesItem(index); + if (!item) { + return; + } + + if (m_model->storageSetupNeeded(index)) { + m_triggerStorageSetupButton = button; + m_storageSetupFailedUrl = url(); + + connect(m_model, SIGNAL(storageSetupDone(int,bool)), + this, SLOT(slotStorageSetupDone(int,bool))); + + m_model->requestStorageSetup(index); + } else { + m_triggerStorageSetupButton = Qt::NoButton; + + const KUrl url = m_model->data(index).value("url").value(); + if (!url.isEmpty()) { + if (button == Qt::MiddleButton) { + emit placeMiddleClicked(url); + } else { + emit placeActivated(url); + } + } + } +} + + +#include "moc_placespanel.cpp" diff --git a/dolphin/src/panels/places/placespanel.h b/dolphin/src/panels/places/placespanel.h new file mode 100644 index 00000000..1ab64c45 --- /dev/null +++ b/dolphin/src/panels/places/placespanel.h @@ -0,0 +1,95 @@ +/*************************************************************************** + * Copyright (C) 2008-2012 by Peter Penz * + * Copyright (C) 2010 by Christian Muehlhaeuser * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef PLACESPANEL_H +#define PLACESPANEL_H + +#include +#include + +class KItemListController; +class PlacesItemEditDialog; +class PlacesItem; +class PlacesItemModel; +class PlacesView; +#include + +/** + * @brief Combines bookmarks and mounted devices as list. + */ +class PlacesPanel : public Panel +{ + Q_OBJECT + +public: + PlacesPanel(QWidget* parent); + virtual ~PlacesPanel(); + +signals: + void placeActivated(const KUrl& url); + void placeMiddleClicked(const KUrl& url); + void errorMessage(const QString& error); + +protected: + virtual bool urlChanged(); + virtual void showEvent(QShowEvent* event); + +public slots: + virtual void readSettings(); + +private slots: + void slotItemActivated(int index); + void slotItemMiddleClicked(int index); + void slotItemContextMenuRequested(int index, const QPointF& pos); + void slotViewContextMenuRequested(const QPointF& pos); + void slotItemDropEvent(int index, QGraphicsSceneDragDropEvent* event); + void slotItemDropEventStorageSetupDone(int index, bool success); + void slotAboveItemDropEvent(int index, QGraphicsSceneDragDropEvent* event); + void slotUrlsDropped(const KUrl& dest, QDropEvent* event, QWidget* parent); + void slotTrashUpdated(KJob* job); + void slotStorageSetupDone(int index, bool success); + +private: + void emptyTrash(); + void addEntry(); + void editEntry(int index); + + /** + * Selects the item that has the closest URL for the URL set + * for the panel (see Panel::setUrl()). + */ + void selectClosestItem(); + + void triggerItem(int index, Qt::MouseButton button); + +private: + KItemListController* m_controller; + PlacesItemModel* m_model; + PlacesView* m_view; + + KUrl m_storageSetupFailedUrl; + Qt::MouseButton m_triggerStorageSetupButton; + + int m_itemDropEventIndex; + QMimeData* m_itemDropEventMimeData; + QDropEvent* m_itemDropEvent; +}; + +#endif // PLACESPANEL_H diff --git a/dolphin/src/panels/places/placesview.cpp b/dolphin/src/panels/places/placesview.cpp new file mode 100644 index 00000000..2f9bae85 --- /dev/null +++ b/dolphin/src/panels/places/placesview.cpp @@ -0,0 +1,52 @@ +/*************************************************************************** + * Copyright (C) 2012 by Frank Reininghaus * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "placesview.h" + +#include "dolphin_placespanelsettings.h" + +PlacesView::PlacesView(QGraphicsWidget* parent) : + KStandardItemListView(parent) +{ + const int iconSize = PlacesPanelSettings::iconSize(); + if (iconSize >= 0) { + setIconSize(iconSize); + } +} + +void PlacesView::setIconSize(int size) +{ + if (size != iconSize()) { + PlacesPanelSettings* settings = PlacesPanelSettings::self(); + settings->setIconSize(size); + settings->writeConfig(); + + KItemListStyleOption option = styleOption(); + option.iconSize = size; + setStyleOption(option); + } +} + +int PlacesView::iconSize() const +{ + const KItemListStyleOption option = styleOption(); + return option.iconSize; +} + +#include "moc_placesview.cpp" diff --git a/dolphin/src/panels/places/placesview.h b/dolphin/src/panels/places/placesview.h new file mode 100644 index 00000000..19c99c39 --- /dev/null +++ b/dolphin/src/panels/places/placesview.h @@ -0,0 +1,41 @@ +/*************************************************************************** + * Copyright (C) 2012 by Frank Reininghaus * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef PLACESVIEW_H +#define PLACESVIEW_H + +#include + +/** + * @brief View class for the Places Panel. + * + * Reads the icon size from GeneralSettings::placesPanelIconSize(). + */ +class PlacesView : public KStandardItemListView +{ + Q_OBJECT + +public: + explicit PlacesView(QGraphicsWidget* parent = 0); + + void setIconSize(int size); + int iconSize() const; +}; + +#endif diff --git a/dolphin/src/panels/terminal/terminalpanel.cpp b/dolphin/src/panels/terminal/terminalpanel.cpp new file mode 100644 index 00000000..b00264cf --- /dev/null +++ b/dolphin/src/panels/terminal/terminalpanel.cpp @@ -0,0 +1,193 @@ +/*************************************************************************** + * Copyright (C) 2007-2010 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "terminalpanel.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +TerminalPanel::TerminalPanel(QWidget* parent) : + Panel(parent), + m_clearTerminal(true), + m_mostLocalUrlJob(0), + m_layout(0), + m_terminal(0), + m_terminalWidget(0), + m_konsolePart(0), + m_konsolePartCurrentDirectory() +{ + m_layout = new QVBoxLayout(this); + m_layout->setMargin(0); +} + +TerminalPanel::~TerminalPanel() +{ +} + +void TerminalPanel::terminalExited() +{ + m_terminal = 0; + emit hideTerminalPanel(); +} + +void TerminalPanel::dockVisibilityChanged() +{ + // Only react when the DockWidget itself (not some parent) is hidden. This way we don't + // respond when e.g. Dolphin is minimized. + if (parentWidget() && parentWidget()->isHidden() && + m_terminal && (m_terminal->foregroundProcessId() == -1)) { + // Make sure that the following "cd /" command will not affect the view. + disconnect(m_konsolePart, SIGNAL(currentDirectoryChanged(QString)), + this, SLOT(slotKonsolePartCurrentDirectoryChanged(QString))); + + // Make sure this terminal does not prevent unmounting any removable drives + changeDir(KUrl::fromPath("/")); + + // Because we have disconnected from the part's currentDirectoryChanged() + // signal, we have to update m_konsolePartCurrentDirectory manually. If this + // was not done, showing the panel again might not set the part's working + // directory correctly. + m_konsolePartCurrentDirectory = '/'; + } +} + +bool TerminalPanel::urlChanged() +{ + if (!url().isValid()) { + return false; + } + + const bool sendInput = m_terminal && (m_terminal->foregroundProcessId() == -1) && isVisible(); + if (sendInput) { + changeDir(url()); + } + + return true; +} + +void TerminalPanel::showEvent(QShowEvent* event) +{ + if (event->spontaneous()) { + Panel::showEvent(event); + return; + } + + if (!m_terminal) { + m_clearTerminal = true; + KPluginFactory* factory = KPluginLoader("konsolepart").factory(); + m_konsolePart = factory ? (factory->create(this)) : 0; + if (m_konsolePart) { + connect(m_konsolePart, SIGNAL(destroyed(QObject*)), this, SLOT(terminalExited())); + m_terminalWidget = m_konsolePart->widget(); + m_layout->addWidget(m_terminalWidget); + m_terminal = qobject_cast(m_konsolePart); + } + } + if (m_terminal) { + m_terminal->showShellInDir(url().toLocalFile()); + changeDir(url()); + m_terminalWidget->setFocus(); + connect(m_konsolePart, SIGNAL(currentDirectoryChanged(QString)), + this, SLOT(slotKonsolePartCurrentDirectoryChanged(QString))); + } + + Panel::showEvent(event); +} + +void TerminalPanel::changeDir(const KUrl& url) +{ + delete m_mostLocalUrlJob; + m_mostLocalUrlJob = 0; + + if (url.isLocalFile()) { + sendCdToTerminal(url.toLocalFile()); + } else { + m_mostLocalUrlJob = KIO::mostLocalUrl(url, KIO::HideProgressInfo); + if (m_mostLocalUrlJob->ui()) { + m_mostLocalUrlJob->ui()->setWindow(this); + } + connect(m_mostLocalUrlJob, SIGNAL(result(KJob*)), this, SLOT(slotMostLocalUrlResult(KJob*))); + } +} + +void TerminalPanel::sendCdToTerminal(const QString& dir) +{ + if (dir == m_konsolePartCurrentDirectory) { + m_clearTerminal = false; + return; + } + + if (!m_clearTerminal) { + // The Terminal interface does not provide a way to delete the + // current line before sending a new input. This is mandatory, + // otherwise sending a 'cd x' to a existing 'rm -rf *' might + // result in data loss. As workaround SIGINT is send. + const int processId = m_terminal->terminalProcessId(); + if (processId > 0) { + kill(processId, SIGINT); + } + } + + m_terminal->sendInput(" cd " + KShell::quoteArg(dir) + '\n'); + m_konsolePartCurrentDirectory = dir; + + if (m_clearTerminal) { + m_terminal->sendInput(" clear\n"); + m_clearTerminal = false; + } +} + +void TerminalPanel::slotMostLocalUrlResult(KJob* job) +{ + KIO::StatJob* statJob = static_cast(job); + const KUrl url = statJob->mostLocalUrl(); + if (url.isLocalFile()) { + sendCdToTerminal(url.toLocalFile()); + } + + m_mostLocalUrlJob = 0; +} + +void TerminalPanel::slotKonsolePartCurrentDirectoryChanged(const QString& dir) +{ + m_konsolePartCurrentDirectory = dir; + + // Only change the view URL if 'dir' is different from the current view URL. + // Note that the current view URL could also be a symbolic link to 'dir' + // -> use QDir::canonicalPath() to check that. + const KUrl oldUrl(url()); + const KUrl newUrl(dir); + if (newUrl != oldUrl && dir != QDir(oldUrl.path()).canonicalPath()) { + emit changeUrl(newUrl); + } +} + +#include "moc_terminalpanel.cpp" diff --git a/dolphin/src/panels/terminal/terminalpanel.h b/dolphin/src/panels/terminal/terminalpanel.h new file mode 100644 index 00000000..d146cef7 --- /dev/null +++ b/dolphin/src/panels/terminal/terminalpanel.h @@ -0,0 +1,87 @@ +/*************************************************************************** + * Copyright (C) 2007-2010 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef TERMINALPANEL_H +#define TERMINALPANEL_H + +#include + +class TerminalInterface; +#include +#include + +namespace KIO { + class StatJob; +} + +namespace KParts { + class ReadOnlyPart; +} + +/** + * @brief Shows the terminal which is synchronized with the URL of the + * active view. + */ +class TerminalPanel : public Panel +{ + Q_OBJECT + +public: + TerminalPanel(QWidget* parent = 0); + virtual ~TerminalPanel(); + +public slots: + void terminalExited(); + void dockVisibilityChanged(); + +signals: + void hideTerminalPanel(); + + /** + * Is emitted if the an URL change is requested. + */ + void changeUrl(const KUrl& url); + +protected: + /** @see Panel::urlChanged() */ + virtual bool urlChanged(); + + /** @see QWidget::showEvent() */ + virtual void showEvent(QShowEvent* event); + +private slots: + void slotMostLocalUrlResult(KJob* job); + void slotKonsolePartCurrentDirectoryChanged(const QString& dir); + +private: + void changeDir(const KUrl& url); + void sendCdToTerminal(const QString& path); + +private: + bool m_clearTerminal; + KIO::StatJob* m_mostLocalUrlJob; + + QVBoxLayout* m_layout; + TerminalInterface* m_terminal; + QWidget* m_terminalWidget; + KParts::ReadOnlyPart* m_konsolePart; + QString m_konsolePartCurrentDirectory; +}; + +#endif // TERMINALPANEL_H diff --git a/dolphin/src/search/dolphin_searchsettings.kcfg b/dolphin/src/search/dolphin_searchsettings.kcfg new file mode 100644 index 00000000..f8b3bb75 --- /dev/null +++ b/dolphin/src/search/dolphin_searchsettings.kcfg @@ -0,0 +1,26 @@ + + + + + + + + FromHere + + + + false + + + + FileName + + + + false + + + diff --git a/dolphin/src/search/dolphin_searchsettings.kcfgc b/dolphin/src/search/dolphin_searchsettings.kcfgc new file mode 100644 index 00000000..13b4e910 --- /dev/null +++ b/dolphin/src/search/dolphin_searchsettings.kcfgc @@ -0,0 +1,4 @@ +File=dolphin_searchsettings.kcfg +ClassName=SearchSettings +Singleton=yes +Mutators=true diff --git a/dolphin/src/search/dolphinfacetswidget.cpp b/dolphin/src/search/dolphinfacetswidget.cpp new file mode 100644 index 00000000..3d4a7a36 --- /dev/null +++ b/dolphin/src/search/dolphinfacetswidget.cpp @@ -0,0 +1,112 @@ +/*************************************************************************** +* Copyright (C) 2012 by Peter Penz * +* * +* This program is free software; you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation; either version 2 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program; if not, write to the * +* Free Software Foundation, Inc., * +* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * +* **************************************************************************/ + +#include "dolphinfacetswidget.h" + +#include +#include +#include +#include +#include +#include +#include + +DolphinFacetsWidget::DolphinFacetsWidget(QWidget* parent) : + QWidget(parent), + m_anyType(0), + m_documents(0), + m_images(0), + m_audio(0), + m_videos(0), + m_type("") +{ + m_anyType = createRadioButton(i18nc("@option:check", "Any"), this); + m_documents = createRadioButton(i18nc("@option:check", "Documents"), this); + m_images = createRadioButton(i18nc("@option:check", "Images"), this); + m_audio = createRadioButton(i18nc("@option:check", "Audio Files"), this); + m_videos = createRadioButton(i18nc("@option:check", "Videos"), this); + + QHBoxLayout* topLayout = new QHBoxLayout(this); + topLayout->addWidget(m_anyType); + topLayout->addStretch(); + topLayout->addWidget(m_documents); + topLayout->addStretch(); + topLayout->addWidget(m_images); + topLayout->addStretch(); + topLayout->addWidget(m_audio); + topLayout->addStretch(); + topLayout->addWidget(m_videos); + topLayout->addStretch(); +} + +DolphinFacetsWidget::~DolphinFacetsWidget() +{ +} + +const QString DolphinFacetsWidget::types() { + return m_type; +} + +void DolphinFacetsWidget::facetChange() +{ + // most of the types referenced from http://en.wikipedia.org/wiki/Internet_media_type + if (m_documents->isChecked()) { + m_type = "application/x-dvi;application/postscript;application/pdf;image/x-eps"; + } else if (m_images->isChecked()) { + m_type = ""; + foreach (const KMimeType::Ptr &mime, KMimeType::allMimeTypes()) { + if (mime->name().startsWith("image/")) { + m_type.append(mime->name()); + m_type.append(";"); + } + } + m_type.chop(1); + } else if (m_audio->isChecked()) { + m_type = ""; + foreach (const KMimeType::Ptr &mime, KMimeType::allMimeTypes()) { + if (mime->name().startsWith("audio/")) { + m_type.append(mime->name()); + m_type.append(";"); + } + } + m_type.chop(1); + } else if (m_videos->isChecked()) { + m_type = ""; + foreach (const KMimeType::Ptr &mime, KMimeType::allMimeTypes()) { + if (mime->name().startsWith("video/")) { + m_type.append(mime->name()); + m_type.append(";"); + } + } + m_type.chop(1); + } else { + m_type = ""; + } +} + +QRadioButton* DolphinFacetsWidget::createRadioButton(const QString& text, + QWidget* parent) +{ + QRadioButton* button = new QRadioButton(text, parent); + connect(button, SIGNAL(clicked()), this, SIGNAL(facetChanged())); + connect(button, SIGNAL(toggled(bool)), this, SLOT(facetChange())); + return button; +} + +#include "moc_dolphinfacetswidget.cpp" diff --git a/dolphin/src/search/dolphinfacetswidget.h b/dolphin/src/search/dolphinfacetswidget.h new file mode 100644 index 00000000..ea4f5908 --- /dev/null +++ b/dolphin/src/search/dolphinfacetswidget.h @@ -0,0 +1,84 @@ +/*************************************************************************** + * Copyright (C) 2012 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef DOLPHINFACETSWIDGET_H +#define DOLPHINFACETSWIDGET_H + +#include + +#include +#include +#include +#include + +/** + * @brief Allows to filter search-queries by facets. + * + * TODO: The current implementation is a temporary + * workaround for the 4.9 release and represents no + * real facets-implementation yet: There have been + * some Dolphin specific user-interface and interaction + * issues since 4.6 by embedding the Nepomuk facet-widget + * into a QDockWidget (this is unrelated to the + * Nepomuk facet-widget itself). Now in combination + * with the search-shortcuts in the Places Panel some + * existing issues turned into real showstoppers. + * + * So the longterm plan is to use the Nepomuk facets + * again as soon as possible. + */ +class DolphinFacetsWidget : public QWidget +{ + Q_OBJECT + +public: + explicit DolphinFacetsWidget(QWidget* parent = 0); + virtual ~DolphinFacetsWidget(); + + /** + * @return Types of files to be looked for + * + */ + const QString types(); + +signals: + void facetChanged(); + +public slots: + void facetChange(); + + /** + * @return New radiobutton which is connected to the + * slotFacedChanged() slot whenever it has + * been toggled. + */ + QRadioButton* createRadioButton(const QString& text, + QWidget* parent = 0); + +private: + QRadioButton* m_anyType; + QRadioButton* m_documents; + QRadioButton* m_images; + QRadioButton* m_audio; + QRadioButton* m_videos; + + QString m_type; +}; + +#endif diff --git a/dolphin/src/search/dolphinsearchbox.cpp b/dolphin/src/search/dolphinsearchbox.cpp new file mode 100644 index 00000000..5de9d57f --- /dev/null +++ b/dolphin/src/search/dolphinsearchbox.cpp @@ -0,0 +1,438 @@ +/*************************************************************************** +* Copyright (C) 2010 by Peter Penz * +* * +* This program is free software; you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation; either version 2 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program; if not, write to the * +* Free Software Foundation, Inc., * +* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * +* **************************************************************************/ + +#include "dolphinsearchbox.h" + +#include "dolphin_searchsettings.h" +#include "dolphinfacetswidget.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +DolphinSearchBox::DolphinSearchBox(QWidget* parent) : + QWidget(parent), + m_startedSearching(false), + m_active(true), + m_topLayout(0), + m_searchLabel(0), + m_searchInput(0), + m_optionsScrollArea(0), + m_fileNameButton(0), + m_contentButton(0), + m_separator(0), + m_fromHereButton(0), + m_everywhereButton(0), + m_literalBox(0), + m_facetsToggleButton(0), + m_facetsWidget(0), + m_searchPath(), + m_startSearchTimer(0) +{ +} + +DolphinSearchBox::~DolphinSearchBox() +{ + saveSettings(); +} + +void DolphinSearchBox::setText(const QString& text) +{ + m_searchInput->setText(text); +} + +QString DolphinSearchBox::text() const +{ + return m_searchInput->text(); +} + +void DolphinSearchBox::setSearchPath(const KUrl& url) +{ + m_searchPath = url; + + QFontMetrics metrics(m_fromHereButton->font()); + const int maxWidth = metrics.height() * 8; + + QString location = url.fileName(); + if (location.isEmpty()) { + if (url.isLocalFile()) { + location = QLatin1String("/"); + } else { + location = url.protocol() + QLatin1String(" - ") + url.host(); + } + } + + const QString elidedLocation = metrics.elidedText(location, Qt::ElideMiddle, maxWidth); + m_fromHereButton->setText(i18nc("action:button", "From Here (%1)", elidedLocation)); + + const bool showSearchFromButtons = url.isLocalFile(); + m_separator->setVisible(showSearchFromButtons); + m_fromHereButton->setVisible(showSearchFromButtons); + m_everywhereButton->setVisible(showSearchFromButtons); +} + +KUrl DolphinSearchBox::searchPath() const +{ + return m_searchPath; +} + +KUrl DolphinSearchBox::urlForSearching() const +{ + KUrl url; + url.setProtocol("filenamesearch"); + url.addQueryItem("search", m_searchInput->text()); + if (m_contentButton->isChecked()) { + url.addQueryItem("checkContent", "yes"); + } + + if(m_literalBox->isChecked()) { + url.addQueryItem("literal", "yes"); + } + + if (!m_facetsWidget->types().isEmpty()) { + url.addQueryItem("checkType", m_facetsWidget->types()); + } + + QString encodedUrl; + if (m_everywhereButton->isChecked()) { + // It is very unlikely, that the majority of Dolphins target users + // mean "the whole harddisk" instead of "my home folder" when + // selecting the "Everywhere" button. + encodedUrl = QDir::homePath(); + } else { + encodedUrl = m_searchPath.url(); + } + url.addQueryItem("url", encodedUrl); + + return url; +} + +void DolphinSearchBox::fromSearchUrl(const KUrl& url) +{ + if (url.protocol() == "filenamesearch") { + const QMap& queryItems = url.queryItems(); + setText(queryItems.value("search")); + setSearchPath(queryItems.value("url")); + m_contentButton->setChecked(queryItems.value("checkContent") == "yes"); + m_literalBox->setChecked(queryItems.value("literal") == "yes"); + } else { + setText(QString()); + setSearchPath(url); + } +} + +void DolphinSearchBox::selectAll() +{ + m_searchInput->selectAll(); +} + +void DolphinSearchBox::setActive(bool active) +{ + if (active != m_active) { + m_active = active; + + if (active) { + emit activated(); + } + } +} + +bool DolphinSearchBox::isActive() const +{ + return m_active; +} + +bool DolphinSearchBox::event(QEvent* event) +{ + if (event->type() == QEvent::Polish) { + init(); + } + return QWidget::event(event); +} + +void DolphinSearchBox::showEvent(QShowEvent* event) +{ + if (!event->spontaneous()) { + m_searchInput->setFocus(); + m_startedSearching = false; + } +} + +void DolphinSearchBox::keyReleaseEvent(QKeyEvent* event) +{ + QWidget::keyReleaseEvent(event); + if (event->key() == Qt::Key_Escape) { + if (m_searchInput->text().isEmpty()) { + emit closeRequest(); + } else { + m_searchInput->clear(); + } + } +} + +bool DolphinSearchBox::eventFilter(QObject* obj, QEvent* event) +{ + switch (event->type()) { + case QEvent::FocusIn: + setActive(true); + setFocus(); + break; + + default: + break; + } + + return QObject::eventFilter(obj, event); +} + +void DolphinSearchBox::emitSearchRequest() +{ + m_startSearchTimer->stop(); + m_startedSearching = true; + emit searchRequest(); +} + +void DolphinSearchBox::emitCloseRequest() +{ + m_startSearchTimer->stop(); + m_startedSearching = false; + emit closeRequest(); +} + +void DolphinSearchBox::slotConfigurationChanged() +{ + saveSettings(); + if (m_startedSearching) { + emitSearchRequest(); + } +} + +void DolphinSearchBox::slotSearchTextChanged(const QString& text) +{ + if (text.isEmpty()) { + m_startSearchTimer->stop(); + } else { + m_startSearchTimer->start(); + } + emit searchTextChanged(text); +} + +void DolphinSearchBox::slotReturnPressed(const QString& text) +{ + emitSearchRequest(); + emit returnPressed(text); +} + +void DolphinSearchBox::slotFacetsButtonToggled() +{ + const bool facetsIsVisible = !m_facetsWidget->isVisible(); + m_facetsWidget->setVisible(facetsIsVisible); + updateFacetsToggleButton(); +} + +void DolphinSearchBox::slotFacetChanged() +{ + m_startedSearching = true; + m_startSearchTimer->stop(); + emit searchRequest(); +} + +void DolphinSearchBox::initButton(QToolButton* button) +{ + button->installEventFilter(this); + button->setAutoExclusive(true); + button->setAutoRaise(true); + button->setCheckable(true); + connect(button, SIGNAL(clicked(bool)), this, SLOT(slotConfigurationChanged())); +} + +void DolphinSearchBox::loadSettings() +{ + if (SearchSettings::location() == QLatin1String("Everywhere")) { + m_everywhereButton->setChecked(true); + } else { + m_fromHereButton->setChecked(true); + } + + if (SearchSettings::what() == QLatin1String("Content")) { + m_contentButton->setChecked(true); + } else { + m_fileNameButton->setChecked(true); + } + + m_literalBox->setChecked(SearchSettings::literal()); + + m_facetsWidget->setVisible(SearchSettings::showFacetsWidget()); +} + +void DolphinSearchBox::saveSettings() +{ + SearchSettings::setLocation(m_fromHereButton->isChecked() ? "FromHere" : "Everywhere"); + SearchSettings::setWhat(m_fileNameButton->isChecked() ? "FileName" : "Content"); + SearchSettings::setLiteral(m_literalBox->isChecked()); + SearchSettings::setShowFacetsWidget(m_facetsToggleButton->isChecked()); + SearchSettings::self()->writeConfig(); +} + +void DolphinSearchBox::init() +{ + // Create close button + QToolButton* closeButton = new QToolButton(this); + closeButton->setAutoRaise(true); + closeButton->setIcon(KIcon("dialog-close")); + closeButton->setToolTip(i18nc("@info:tooltip", "Quit searching")); + connect(closeButton, SIGNAL(clicked()), this, SLOT(emitCloseRequest())); + + // Create search label + m_searchLabel = new QLabel(this); + + // Create search box + m_searchInput = new KLineEdit(this); + m_searchInput->installEventFilter(this); + m_searchInput->setClearButtonShown(true); + m_searchInput->setFont(KGlobalSettings::generalFont()); + m_searchInput->setToolTip(i18nc("@info:tooltip", "Enter rich Perl-like pattern matching syntax")); + setFocusProxy(m_searchInput); + connect(m_searchInput, SIGNAL(returnPressed(QString)), + this, SLOT(slotReturnPressed(QString))); + connect(m_searchInput, SIGNAL(textChanged(QString)), + this, SLOT(slotSearchTextChanged(QString))); + + // Apply layout for the search input + QHBoxLayout* searchInputLayout = new QHBoxLayout(); + searchInputLayout->setMargin(0); + searchInputLayout->addWidget(closeButton); + searchInputLayout->addWidget(m_searchLabel); + searchInputLayout->addWidget(m_searchInput); + + // Create "Filename" and "Content" button + m_fileNameButton = new QToolButton(this); + m_fileNameButton->setText(i18nc("action:button", "Filename")); + initButton(m_fileNameButton); + + m_contentButton = new QToolButton(); + m_contentButton->setText(i18nc("action:button", "Content")); + initButton(m_contentButton); + + QButtonGroup* searchWhatGroup = new QButtonGroup(this); + searchWhatGroup->addButton(m_fileNameButton); + searchWhatGroup->addButton(m_contentButton); + + m_separator = new KSeparator(Qt::Vertical, this); + + // Create "From Here" and "Everywhere"button + m_fromHereButton = new QToolButton(this); + m_fromHereButton->setText(i18nc("action:button", "From Here")); + m_fromHereButton->setToolTip(i18nc("@info:tooltip", "Search only in the current directory")); + initButton(m_fromHereButton); + + m_everywhereButton = new QToolButton(this); + m_everywhereButton->setText(i18nc("action:button", "Everywhere")); + m_everywhereButton->setToolTip(i18nc("@info:tooltip", "Search in the Home directory")); + initButton(m_everywhereButton); + + QButtonGroup* searchLocationGroup = new QButtonGroup(this); + searchLocationGroup->addButton(m_fromHereButton); + searchLocationGroup->addButton(m_everywhereButton); + + // Create "Literal" widget + m_literalBox = new QCheckBox(this); + m_literalBox->setText(i18nc("action:button", "Literal")); + m_literalBox->setToolTip(i18nc("@info:tooltip", "Escape the regular expression sequence")); + connect(m_literalBox, SIGNAL(stateChanged(int)), this, SLOT(slotConfigurationChanged())); + + // Create "Facets" widgets + m_facetsToggleButton = new QToolButton(this); + m_facetsToggleButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + initButton(m_facetsToggleButton); + connect(m_facetsToggleButton, SIGNAL(clicked()), this, SLOT(slotFacetsButtonToggled())); + + m_facetsWidget = new DolphinFacetsWidget(this); + m_facetsWidget->installEventFilter(this); + m_facetsWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum); + connect(m_facetsWidget, SIGNAL(facetChanged()), this, SLOT(slotFacetChanged())); + + // Apply layout for the options + QHBoxLayout* optionsLayout = new QHBoxLayout(); + optionsLayout->setMargin(0); + optionsLayout->addWidget(m_fileNameButton); + optionsLayout->addWidget(m_contentButton); + optionsLayout->addWidget(m_separator); + optionsLayout->addWidget(m_fromHereButton); + optionsLayout->addWidget(m_everywhereButton); + optionsLayout->addWidget(m_separator); + optionsLayout->addWidget(m_literalBox); + optionsLayout->addStretch(1); + optionsLayout->addWidget(m_facetsToggleButton); + + // Put the options into a QScrollArea. This prevents increasing the view width + // in case that not enough width for the options is available. + QWidget* optionsContainer = new QWidget(this); + optionsContainer->setLayout(optionsLayout); + + m_optionsScrollArea = new QScrollArea(this); + m_optionsScrollArea->setFrameShape(QFrame::NoFrame); + m_optionsScrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + m_optionsScrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + m_optionsScrollArea->setMaximumHeight(optionsContainer->sizeHint().height()); + m_optionsScrollArea->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); + m_optionsScrollArea->setWidget(optionsContainer); + m_optionsScrollArea->setWidgetResizable(true); + + m_topLayout = new QVBoxLayout(this); + m_topLayout->setMargin(0); + m_topLayout->addLayout(searchInputLayout); + m_topLayout->addWidget(m_optionsScrollArea); + m_topLayout->addWidget(m_facetsWidget); + + loadSettings(); + + // The searching should be started automatically after the user did not change + // the text within one second + m_startSearchTimer = new QTimer(this); + m_startSearchTimer->setSingleShot(true); + m_startSearchTimer->setInterval(1000); + connect(m_startSearchTimer, SIGNAL(timeout()), this, SLOT(emitSearchRequest())); + + updateFacetsToggleButton(); +} + +void DolphinSearchBox::updateFacetsToggleButton() +{ + const bool facetsIsVisible = SearchSettings::showFacetsWidget(); + m_facetsToggleButton->setChecked(facetsIsVisible ? true : false); + m_facetsToggleButton->setIcon(KIcon(facetsIsVisible ? "arrow-up-double" : "arrow-down-double")); + m_facetsToggleButton->setText(facetsIsVisible ? i18nc("action:button", "Fewer Options") : i18nc("action:button", "More Options")); +} + +#include "moc_dolphinsearchbox.cpp" diff --git a/dolphin/src/search/dolphinsearchbox.h b/dolphin/src/search/dolphinsearchbox.h new file mode 100644 index 00000000..cb40d974 --- /dev/null +++ b/dolphin/src/search/dolphinsearchbox.h @@ -0,0 +1,172 @@ +/*************************************************************************** + * Copyright (C) 2010 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef DOLPHINSEARCHBOX_H +#define DOLPHINSEARCHBOX_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class DolphinFacetsWidget; +class KLineEdit; +class KSeparator; + +/** + * @brief Input box for searching files. + * + * The widget allows to specify: + * - Where to search: Everywhere or below the current directory + * - What to search: Filenames or content + * + */ +class DolphinSearchBox : public QWidget { + Q_OBJECT + +public: + explicit DolphinSearchBox(QWidget* parent = 0); + virtual ~DolphinSearchBox(); + + /** + * Sets the text that should be used as input for + * searching. + */ + void setText(const QString& text); + + /** + * Returns the text that should be used as input + * for searching. + */ + QString text() const; + + /** + * Sets the current path that is used as root for + * searching files, if "From Here" has been selected. + */ + void setSearchPath(const KUrl& url); + KUrl searchPath() const; + + /** @return URL that will start the searching of files. */ + KUrl urlForSearching() const; + + /** + * Extracts information from the given search \a url to + * initialize the search box properly. + */ + void fromSearchUrl(const KUrl& url); + + /** + * Selects the whole text of the search box. + */ + void selectAll(); + + /** + * Set the search box to the active mode, if \a active + * is true. The active mode is default. The inactive mode only differs + * visually from the active mode, no change of the behavior is given. + * + * Using the search box in the inactive mode is useful when having split views, + * where the inactive view is indicated by an search box visually. + */ + void setActive(bool active); + + /** + * @return True, if the search box is in the active mode. + * @see DolphinSearchBox::setActive() + */ + bool isActive() const; + +protected: + virtual bool event(QEvent* event); + virtual void showEvent(QShowEvent* event); + virtual void keyReleaseEvent(QKeyEvent* event); + virtual bool eventFilter(QObject* obj, QEvent* event); + +signals: + /** + * Is emitted when a searching should be triggered. + */ + void searchRequest(); + + /** + * Is emitted when the user has changed a character of + * the text that should be used as input for searching. + */ + void searchTextChanged(const QString& text); + + void returnPressed(const QString& text); + + /** + * Emitted as soon as the search box should get closed. + */ + void closeRequest(); + + /** + * Is emitted, if the searchbox has been activated by + * an user interaction + * @see DolphinSearchBox::setActive() + */ + void activated(); + +private slots: + void emitSearchRequest(); + void emitCloseRequest(); + void slotConfigurationChanged(); + void slotSearchTextChanged(const QString& text); + void slotReturnPressed(const QString& text); + void slotFacetsButtonToggled(); + void slotFacetChanged(); + +private: + void initButton(QToolButton* button); + void loadSettings(); + void saveSettings(); + void init(); + + void updateFacetsToggleButton(); +private: + bool m_startedSearching; + bool m_active; + + QVBoxLayout* m_topLayout; + + QLabel* m_searchLabel; + KLineEdit* m_searchInput; + QScrollArea* m_optionsScrollArea; + QToolButton* m_fileNameButton; + QToolButton* m_contentButton; + KSeparator* m_separator; + QToolButton* m_fromHereButton; + QToolButton* m_everywhereButton; + QCheckBox* m_literalBox; + QToolButton* m_facetsToggleButton; + DolphinFacetsWidget* m_facetsWidget; + + KUrl m_searchPath; + + QTimer* m_startSearchTimer; +}; + +#endif diff --git a/dolphin/src/search/filenamesearch.protocol b/dolphin/src/search/filenamesearch.protocol new file mode 100644 index 00000000..06aec0e6 --- /dev/null +++ b/dolphin/src/search/filenamesearch.protocol @@ -0,0 +1,17 @@ +[Protocol] +exec=kio_filenamesearch +protocol=filenamesearch +input=none +output=filesystem +reading=true +writing=false +deleting=true +linking=false +makedir=false +moving=false +listing=Name,Type,Size,Date,AccessDate,Access,Owner,Group,Link +source=false +Icon=edit-find +Class=:local +determineMimetypeFromExtension=false +maxInstances=10 diff --git a/dolphin/src/search/filenamesearchprotocol.cpp b/dolphin/src/search/filenamesearchprotocol.cpp new file mode 100644 index 00000000..4fb3e835 --- /dev/null +++ b/dolphin/src/search/filenamesearchprotocol.cpp @@ -0,0 +1,210 @@ +/*************************************************************************** + * Copyright (C) 2010 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "filenamesearchprotocol.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +FileNameSearchProtocol::FileNameSearchProtocol( const QByteArray &pool, const QByteArray &app ) : + SlaveBase("search", pool, app), + m_checkContent(""), + m_checkType(""), + m_regExp(0), + m_iteratedDirs() +{ +} + +FileNameSearchProtocol::~FileNameSearchProtocol() +{ + cleanup(); +} + +void FileNameSearchProtocol::listDir(const KUrl& url) +{ + cleanup(); + + m_checkContent = url.queryItem("checkContent"); + + m_literal = url.queryItem("literal"); + + m_checkType = url.queryItem("checkType"); + + + QString search = url.queryItem("search"); + if (!search.isEmpty() && m_literal == "yes") { + search = QRegExp::escape(search); + } + + if (!search.isEmpty()) { + m_regExp = new QRegExp(search, Qt::CaseInsensitive); + } + + const QString urlString = url.queryItem("url"); + searchDirectory(KUrl(urlString)); + + cleanup(); + finished(); +} + +void FileNameSearchProtocol::searchDirectory(const KUrl& directory) +{ + // Don't try to iterate the pseudo filesystem directories of Linux + if (directory.path() == QLatin1String("/dev") + || directory.path() == QLatin1String("/proc") + || directory.path() == QLatin1String("/sys")) { + return; + } + + // Get all items of the directory + KDirLister *dirLister = new KDirLister(); + dirLister->setDelayedMimeTypes(false); + dirLister->setAutoErrorHandlingEnabled(false, 0); + + QEventLoop eventLoop; + QObject::connect(dirLister, SIGNAL(canceled()), &eventLoop, SLOT(quit())); + QObject::connect(dirLister, SIGNAL(completed()), &eventLoop, SLOT(quit())); + dirLister->openUrl(directory); + eventLoop.exec(); + + // Visualize all items that match the search pattern + QList pendingDirs; + const KFileItemList items = dirLister->items(); + foreach (const KFileItem& item, items) { + bool addItem = false; + if (!m_regExp || item.name().contains(*m_regExp)) { + addItem = true; + if (!m_checkType.isEmpty()) { + addItem = false; + const QStringList types = m_checkType.split(";"); + const KSharedPtr mime = item.determineMimeType(); + foreach (const QString& t, types) { + if (mime->is(t)) { + addItem = true; + } + } + } + } else if (!m_checkContent.isEmpty() && item.determineMimeType()->is(QLatin1String("text/plain"))) { + addItem = contentContainsPattern(item.url()); + } + + if (addItem) { + KIO::UDSEntry entry = item.entry(); + entry.insert(KIO::UDSEntry::UDS_URL, item.url().url() ); + listEntry(entry,false); + } + + if (item.isDir()) { + if (item.isLink()) { + // Assure that no endless searching is done in directories that + // have already been iterated. + const KUrl linkDest(item.url(), item.linkDest()); + if (!m_iteratedDirs.contains(linkDest.path())) { + pendingDirs.append(linkDest); + } + } else { + pendingDirs.append(item.url()); + } + } + } + listEntry(KIO::UDSEntry(), true); + + m_iteratedDirs.insert(directory.path()); + + delete dirLister; + dirLister = 0; + + // Recursively iterate all sub directories + foreach (const KUrl& pendingDir, pendingDirs) { + searchDirectory(pendingDir); + } +} + +bool FileNameSearchProtocol::contentContainsPattern(const KUrl& fileName) const +{ + Q_ASSERT(m_regExp); + + QString path; + KTemporaryFile tempFile; + + if (fileName.isLocalFile()) { + path = fileName.path(); + } else if (tempFile.open()) { + KIO::Job* getJob = KIO::file_copy(fileName, + tempFile.fileName(), + -1, + KIO::Overwrite | KIO::HideProgressInfo); + if (!KIO::NetAccess::synchronousRun(getJob, 0)) { + // The non-local file could not be downloaded + return false; + } + path = tempFile.fileName(); + } else { + // No temporary file could be created for downloading non-local files + return false; + } + + QFile file(path); + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + return false; + } + + QTextStream in(&file); + while (!in.atEnd()) { + const QString line = in.readLine(); + if (line.contains(*m_regExp)) { + return true; + } + } + + return false; +} + +void FileNameSearchProtocol::cleanup() +{ + delete m_regExp; + m_regExp = 0; + m_iteratedDirs.clear(); +} + +extern "C" int Q_DECL_EXPORT kdemain( int argc, char **argv ) +{ + KComponentData instance("kio_search"); + QCoreApplication app(argc, argv); + + if (argc != 4) { + fprintf(stderr, "Usage: kio_filenamesearch protocol domain-socket1 domain-socket2\n"); + exit(-1); + } + + FileNameSearchProtocol slave(argv[2], argv[3]); + slave.dispatchLoop(); + + return 0; +} diff --git a/dolphin/src/search/filenamesearchprotocol.h b/dolphin/src/search/filenamesearchprotocol.h new file mode 100644 index 00000000..661940bd --- /dev/null +++ b/dolphin/src/search/filenamesearchprotocol.h @@ -0,0 +1,63 @@ +/*************************************************************************** + * Copyright (C) 2010 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef FILENAMESEARCHPROTOCOL_H +#define FILENAMESEARCHPROTOCOL_H + +#include + +class KFileItem; +class KUrl; +#include +#include + +/** + * @brief Lists files where the filename matches do a given query. + * + * The query is defined as part of the "search" query item of the URL. + * The directory where the searching is started is defined in the "url" query + * item. If the query item "checkContent" is set to "yes", all files with + * a text MIME type will be checked for the content. + */ +class FileNameSearchProtocol : public KIO::SlaveBase { +public: + FileNameSearchProtocol(const QByteArray& pool, const QByteArray& app); + virtual ~FileNameSearchProtocol(); + + virtual void listDir(const KUrl& url); + +private: + void searchDirectory(const KUrl& directory); + + /** + * @return True, if the pattern m_searchPattern is part of + * the file \a fileName. + */ + bool contentContainsPattern(const KUrl& fileName) const; + + void cleanup(); + + QString m_checkContent; + QString m_literal; + QString m_checkType; + QRegExp* m_regExp; + QSet m_iteratedDirs; +}; + +#endif diff --git a/dolphin/src/settings/additionalinfodialog.cpp b/dolphin/src/settings/additionalinfodialog.cpp new file mode 100644 index 00000000..06d48bf3 --- /dev/null +++ b/dolphin/src/settings/additionalinfodialog.cpp @@ -0,0 +1,93 @@ +/*************************************************************************** + * Copyright (C) 2007-2012 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "additionalinfodialog.h" + +#include +#include "kitemviews/kfileitemmodel.h" +#include +#include +#include + + +AdditionalInfoDialog::AdditionalInfoDialog(QWidget* parent, + const QList& visibleRoles) : + KDialog(parent), + m_visibleRoles(visibleRoles), + m_listWidget(0) +{ + setCaption(i18nc("@title:window", "Additional Information")); + setButtons(Ok | Cancel); + setDefaultButton(Ok); + + QWidget* mainWidget = new QWidget(this); + mainWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum); + + // Add header + QLabel* header = new QLabel(mainWidget); + header->setText(i18nc("@label", "Select which additional information should be shown:")); + header->setWordWrap(true); + + m_listWidget = new QListWidget(mainWidget); + m_listWidget->setSelectionMode(QAbstractItemView::NoSelection); + const QList rolesInfo = KFileItemModel::rolesInformation(); + foreach (const KFileItemModel::RoleInfo& info, rolesInfo) { + QListWidgetItem* item = new QListWidgetItem(info.translation, m_listWidget); + item->setCheckState(visibleRoles.contains(info.role) ? Qt::Checked : Qt::Unchecked); + } + + QVBoxLayout* layout = new QVBoxLayout(mainWidget); + layout->addWidget(header); + layout->addWidget(m_listWidget); + + setMainWidget(mainWidget); + + const KConfigGroup dialogConfig(KSharedConfig::openConfig("dolphinrc"), "AdditionalInfoDialog"); + restoreDialogSize(dialogConfig); + + connect(this, SIGNAL(okClicked()), this, SLOT(slotOk())); +} + +AdditionalInfoDialog::~AdditionalInfoDialog() +{ + KConfigGroup dialogConfig(KSharedConfig::openConfig("dolphinrc"), "AdditionalInfoDialog"); + saveDialogSize(dialogConfig, KConfigBase::Persistent); +} + +QList AdditionalInfoDialog::visibleRoles() const +{ + return m_visibleRoles; +} + +void AdditionalInfoDialog::slotOk() +{ + m_visibleRoles.clear(); + + int index = 0; + const QList rolesInfo = KFileItemModel::rolesInformation(); + foreach (const KFileItemModel::RoleInfo& info, rolesInfo) { + const QListWidgetItem* item = m_listWidget->item(index); + if (item->checkState() == Qt::Checked) { + m_visibleRoles.append(info.role); + } + ++index; + } +} + +#include "moc_additionalinfodialog.cpp" diff --git a/dolphin/src/settings/additionalinfodialog.h b/dolphin/src/settings/additionalinfodialog.h new file mode 100644 index 00000000..70f6984a --- /dev/null +++ b/dolphin/src/settings/additionalinfodialog.h @@ -0,0 +1,49 @@ +/*************************************************************************** + * Copyright (C) 2007-2012 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef ADDITIONALINFODIALOG_H +#define ADDITIONALINFODIALOG_H + +#include +#include +#include + +#include + +/** + * @brief Dialog for changing the additional information shown in the view. + */ +class AdditionalInfoDialog : public KDialog +{ + Q_OBJECT + +public: + AdditionalInfoDialog(QWidget* parent, const QList& visibleRoles); + virtual ~AdditionalInfoDialog(); + QList visibleRoles() const; + +private slots: + void slotOk(); + +private: + QList m_visibleRoles; + QListWidget* m_listWidget; +}; + +#endif diff --git a/dolphin/src/settings/applyviewpropsjob.cpp b/dolphin/src/settings/applyviewpropsjob.cpp new file mode 100644 index 00000000..4cc64254 --- /dev/null +++ b/dolphin/src/settings/applyviewpropsjob.cpp @@ -0,0 +1,79 @@ +/*************************************************************************** + * Copyright (C) 2006 by Peter Penz * + * * + * The code is based on kdelibs/kio/directorysizejob.* * + * (C) 2006 by David Faure * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "applyviewpropsjob.h" +#include + +ApplyViewPropsJob::ApplyViewPropsJob(const KUrl& dir, + const ViewProperties& viewProps) : + KIO::Job(), + m_viewProps(0), + m_progress(0), + m_dir(dir) +{ + m_viewProps = new ViewProperties(dir); + m_viewProps->setViewMode(viewProps.viewMode()); + m_viewProps->setPreviewsShown(viewProps.previewsShown()); + m_viewProps->setHiddenFilesShown(viewProps.hiddenFilesShown()); + m_viewProps->setSortRole(viewProps.sortRole()); + m_viewProps->setSortOrder(viewProps.sortOrder()); + + KIO::ListJob* listJob = KIO::listRecursive(dir, KIO::HideProgressInfo); + connect(listJob, SIGNAL(entries(KIO::Job*,KIO::UDSEntryList)), + SLOT(slotEntries(KIO::Job*,KIO::UDSEntryList))); + addSubjob(listJob); +} + +ApplyViewPropsJob::~ApplyViewPropsJob() +{ + delete m_viewProps; // the properties are written by the destructor + m_viewProps = 0; +} + +void ApplyViewPropsJob::slotEntries(KIO::Job*, const KIO::UDSEntryList& list) +{ + foreach (const KIO::UDSEntry& entry, list) { + const QString name = entry.stringValue(KIO::UDSEntry::UDS_NAME); + if (name != QLatin1String(".") && name != QLatin1String("..") && entry.isDir()) { + ++m_progress; + + KUrl url(m_dir); + url.addPath(name); + + Q_ASSERT(m_viewProps); + + ViewProperties props(url); + props.setDirProperties(*m_viewProps); + } + } +} + +void ApplyViewPropsJob::slotResult(KJob* job) +{ + if (job->error()) { + setError(job->error()); + setErrorText(job->errorText()); + } + emitResult(); +} + +#include "moc_applyviewpropsjob.cpp" diff --git a/dolphin/src/settings/applyviewpropsjob.h b/dolphin/src/settings/applyviewpropsjob.h new file mode 100644 index 00000000..f7cd5483 --- /dev/null +++ b/dolphin/src/settings/applyviewpropsjob.h @@ -0,0 +1,82 @@ +/*************************************************************************** + * Copyright (C) 2006 by Peter Penz * + * * + * The code is based on kdelibs/kio/kio/directorysizejob.* * + * (C) 2006 by David Faure * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef APPLYVIEWPROPSJOB_H +#define APPLYVIEWPROPSJOB_H + +#include +#include +#include + +class ViewProperties; + +/** + * @brief Applies view properties recursively to directories. + * + * Usage: + * \code + * KJob* job = new ApplyViewPropsJob(dir, viewProps); + * connect(job, SIGNAL(result(KJob*)), + * this, SLOT(slotResult(KJob*))); + * \endcode + * + * To be able to show a progress of the operation, the following steps + * are recommended: + * - Use a DirectorySizeJob to count the number of directories. + * - Use a timer to show the current count of directories by invoking + * DirectorySizeJob::totalSubdirs() until the result signal is emitted. + * - Use the ApplyViewPropsJob. + * - Use a timer to show the progress by invoking ApplyViwePropsJob::progress(). + * In combination with the total directory count it is possible to show a + * progress bar now. + */ +class ApplyViewPropsJob : public KIO::Job +{ + Q_OBJECT + +public: + /** + * @param dir Directory where the view properties should be applied to + * (including sub directories). + * @param viewProps View properties for the directory \a dir including its + * sub directories. + */ + ApplyViewPropsJob(const KUrl& dir, const ViewProperties& viewProps); + virtual ~ApplyViewPropsJob(); + int progress() const; + +private slots: + virtual void slotResult(KJob* job); + void slotEntries(KIO::Job*, const KIO::UDSEntryList&); + +private: + ViewProperties* m_viewProps; + int m_progress; + KUrl m_dir; +}; + +inline int ApplyViewPropsJob::progress() const +{ + return m_progress; +} + +#endif diff --git a/dolphin/src/settings/dolphin_compactmodesettings.kcfg b/dolphin/src/settings/dolphin_compactmodesettings.kcfg new file mode 100644 index 00000000..b9000c8e --- /dev/null +++ b/dolphin/src/settings/dolphin_compactmodesettings.kcfg @@ -0,0 +1,44 @@ + + + + kglobalsettings.h + kiconloader.h + + + + + true + + + + KGlobalSettings::generalFont().family() + + + + KGlobalSettings::generalFont().pointSizeF() + + + + false + + + + 0 + + + + KIconLoader::SizeSmall + + + + KIconLoader::SizeLarge + + + + 0 + + + diff --git a/dolphin/src/settings/dolphin_compactmodesettings.kcfgc b/dolphin/src/settings/dolphin_compactmodesettings.kcfgc new file mode 100644 index 00000000..8341185f --- /dev/null +++ b/dolphin/src/settings/dolphin_compactmodesettings.kcfgc @@ -0,0 +1,4 @@ +File=dolphin_compactmodesettings.kcfg +ClassName=CompactModeSettings +Singleton=yes +Mutators=true diff --git a/dolphin/src/settings/dolphin_detailsmodesettings.kcfg b/dolphin/src/settings/dolphin_detailsmodesettings.kcfg new file mode 100644 index 00000000..64e99898 --- /dev/null +++ b/dolphin/src/settings/dolphin_detailsmodesettings.kcfg @@ -0,0 +1,48 @@ + + + + kiconloader.h + kglobalsettings.h + + + + + KGlobalSettings::generalFont().family() + + + + true + + + + KGlobalSettings::generalFont().pointSizeF() + + + + false + + + + 0 + + + + KIconLoader::SizeSmall + + + + KIconLoader::SizeLarge + + + + 0,1,2,3,4,5,6,7,8 + + + + true + + + diff --git a/dolphin/src/settings/dolphin_detailsmodesettings.kcfgc b/dolphin/src/settings/dolphin_detailsmodesettings.kcfgc new file mode 100644 index 00000000..7acfa3c7 --- /dev/null +++ b/dolphin/src/settings/dolphin_detailsmodesettings.kcfgc @@ -0,0 +1,4 @@ +File=dolphin_detailsmodesettings.kcfg +ClassName=DetailsModeSettings +Singleton=yes +Mutators=true diff --git a/dolphin/src/settings/dolphin_directoryviewpropertysettings.kcfg b/dolphin/src/settings/dolphin_directoryviewpropertysettings.kcfg new file mode 100644 index 00000000..9584fc8b --- /dev/null +++ b/dolphin/src/settings/dolphin_directoryviewpropertysettings.kcfg @@ -0,0 +1,83 @@ + + + kfileitemdelegate.h + + + + + + When this option is enabled hidden files, such as those starting with a '.', will be shown in the file view. + false + + + + + + + This option defines the used version of the view properties. + -1 + + + + + This option controls the style of the view. Currently supported values include icons (0), details (1) and column (2) views. + DolphinView::IconsView + + + + + When this option is enabled, a preview of the file content is shown as an icon. + false + + + + + When this option is enabled, the sorted items are categorized into groups. + false + + + + + This option defines which attribute (text, size, date, etc.) sorting is performed on. + text + + + + + Qt::AscendingOrder + Qt::AscendingOrder + Qt::DescendingOrder + + + + + true + + + + + + + + + + + + + + + The last time these properties were changed by the user. + + + + + + + + + + + diff --git a/dolphin/src/settings/dolphin_directoryviewpropertysettings.kcfgc b/dolphin/src/settings/dolphin_directoryviewpropertysettings.kcfgc new file mode 100644 index 00000000..b938ab3e --- /dev/null +++ b/dolphin/src/settings/dolphin_directoryviewpropertysettings.kcfgc @@ -0,0 +1,6 @@ +File=dolphin_directoryviewpropertysettings.kcfg +Singleton=false +ClassName=ViewPropertySettings +Mutators=true +GlobalEnums=true +IncludeFiles=views/dolphinview.h,qnamespace.h diff --git a/dolphin/src/settings/dolphin_generalsettings.kcfg b/dolphin/src/settings/dolphin_generalsettings.kcfg new file mode 100644 index 00000000..1dabbdf7 --- /dev/null +++ b/dolphin/src/settings/dolphin_generalsettings.kcfg @@ -0,0 +1,92 @@ + + + + QDir + KUrl + kglobalsettings.h + + + + + false + + + + KGlobalSettings::completionMode() + + + + false + + + + 0 + + + + false + + + + KUrl(QDir::homePath()).prettyUrl() + + + + false + + + + false + + + + false + + + + false + + + + true + + + + true + + + + true + + + + false + + + + false + + + + + + + false + + + + true + + + + false + + + + true + + + diff --git a/dolphin/src/settings/dolphin_generalsettings.kcfgc b/dolphin/src/settings/dolphin_generalsettings.kcfgc new file mode 100644 index 00000000..7090dbce --- /dev/null +++ b/dolphin/src/settings/dolphin_generalsettings.kcfgc @@ -0,0 +1,4 @@ +File=dolphin_generalsettings.kcfg +ClassName=GeneralSettings +Singleton=yes +Mutators=true diff --git a/dolphin/src/settings/dolphin_iconsmodesettings.kcfg b/dolphin/src/settings/dolphin_iconsmodesettings.kcfg new file mode 100644 index 00000000..79100030 --- /dev/null +++ b/dolphin/src/settings/dolphin_iconsmodesettings.kcfg @@ -0,0 +1,48 @@ + + + + kglobalsettings.h + kiconloader.h + + + + + true + + + + KGlobalSettings::generalFont().family() + + + + KGlobalSettings::generalFont().pointSizeF() + + + + false + + + + 0 + + + + KIconLoader::SizeLarge + + + + KIconLoader::SizeHuge + + + + 1 + + + + 2 + + + diff --git a/dolphin/src/settings/dolphin_iconsmodesettings.kcfgc b/dolphin/src/settings/dolphin_iconsmodesettings.kcfgc new file mode 100644 index 00000000..9ab145bc --- /dev/null +++ b/dolphin/src/settings/dolphin_iconsmodesettings.kcfgc @@ -0,0 +1,4 @@ +File=dolphin_iconsmodesettings.kcfg +ClassName=IconsModeSettings +Singleton=yes +Mutators=true diff --git a/dolphin/src/settings/dolphin_versioncontrolsettings.kcfg b/dolphin/src/settings/dolphin_versioncontrolsettings.kcfg new file mode 100644 index 00000000..89eb45b6 --- /dev/null +++ b/dolphin/src/settings/dolphin_versioncontrolsettings.kcfg @@ -0,0 +1,14 @@ + + + + + + + + + + + diff --git a/dolphin/src/settings/dolphin_versioncontrolsettings.kcfgc b/dolphin/src/settings/dolphin_versioncontrolsettings.kcfgc new file mode 100644 index 00000000..232268fe --- /dev/null +++ b/dolphin/src/settings/dolphin_versioncontrolsettings.kcfgc @@ -0,0 +1,4 @@ +File=dolphin_versioncontrolsettings.kcfg +ClassName=VersionControlSettings +Singleton=true +Mutators=true diff --git a/dolphin/src/settings/dolphinsettingsdialog.cpp b/dolphin/src/settings/dolphinsettingsdialog.cpp new file mode 100644 index 00000000..f9aec097 --- /dev/null +++ b/dolphin/src/settings/dolphinsettingsdialog.cpp @@ -0,0 +1,152 @@ +/*************************************************************************** + * Copyright (C) 2006 by Peter Penz * + * peter.penz@gmx.at * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "dolphinsettingsdialog.h" + +#include +#include +#include "dolphin_generalsettings.h" +#include "general/generalsettingspage.h" +#include "navigation/navigationsettingspage.h" +#include "services/servicessettingspage.h" +#include "startup/startupsettingspage.h" +#include "viewmodes/viewsettingspage.h" +#include "trash/trashsettingspage.h" + +#include +#include +#include + +DolphinSettingsDialog::DolphinSettingsDialog(const KUrl& url, QWidget* parent) : + KPageDialog(parent), + m_pages() + +{ + const QSize minSize = minimumSize(); + setMinimumSize(QSize(512, minSize.height())); + + setFaceType(List); + setCaption(i18nc("@title:window", "Dolphin Preferences")); + setButtons(Ok | Apply | Cancel | Default); + enableButtonApply(false); + setDefaultButton(Ok); + + // Startup + StartupSettingsPage* startupSettingsPage = new StartupSettingsPage(url, this); + KPageWidgetItem* startupSettingsFrame = addPage(startupSettingsPage, + i18nc("@title:group", "Startup")); + startupSettingsFrame->setIcon(KIcon("go-home")); + connect(startupSettingsPage, SIGNAL(changed()), this, SLOT(enableApply())); + + // View Modes + ViewSettingsPage* viewSettingsPage = new ViewSettingsPage(this); + KPageWidgetItem* viewSettingsFrame = addPage(viewSettingsPage, + i18nc("@title:group", "View Modes")); + viewSettingsFrame->setIcon(KIcon("view-choose")); + connect(viewSettingsPage, SIGNAL(changed()), this, SLOT(enableApply())); + + // Navigation + NavigationSettingsPage* navigationSettingsPage = new NavigationSettingsPage(this); + KPageWidgetItem* navigationSettingsFrame = addPage(navigationSettingsPage, + i18nc("@title:group", "Navigation")); + navigationSettingsFrame->setIcon(KIcon("input-mouse")); + connect(navigationSettingsPage, SIGNAL(changed()), this, SLOT(enableApply())); + + // Services + ServicesSettingsPage* servicesSettingsPage = new ServicesSettingsPage(this); + KPageWidgetItem* servicesSettingsFrame = addPage(servicesSettingsPage, + i18nc("@title:group", "Services")); + servicesSettingsFrame->setIcon(KIcon("services")); + connect(servicesSettingsPage, SIGNAL(changed()), this, SLOT(enableApply())); + + // Trash + TrashSettingsPage* trashSettingsPage = new TrashSettingsPage(this); + KPageWidgetItem* trashSettingsFrame = addPage(trashSettingsPage, + i18nc("@title:group", "Trash")); + trashSettingsFrame->setIcon(KIcon("user-trash")); + connect(trashSettingsPage, SIGNAL(changed()), this, SLOT(enableApply())); + + // General + GeneralSettingsPage* generalSettingsPage = new GeneralSettingsPage(url, this); + KPageWidgetItem* generalSettingsFrame = addPage(generalSettingsPage, + i18nc("@title:group General settings", "General")); + generalSettingsFrame->setIcon(KIcon("system-run")); + connect(generalSettingsPage, SIGNAL(changed()), this, SLOT(enableApply())); + + const KConfigGroup dialogConfig(KSharedConfig::openConfig("dolphinrc"), "SettingsDialog"); + restoreDialogSize(dialogConfig); + + m_pages.append(startupSettingsPage); + m_pages.append(viewSettingsPage); + m_pages.append(navigationSettingsPage); + m_pages.append(servicesSettingsPage); + m_pages.append(trashSettingsPage); + m_pages.append(generalSettingsPage); +} + +DolphinSettingsDialog::~DolphinSettingsDialog() +{ + KConfigGroup dialogConfig(KSharedConfig::openConfig("dolphinrc"), "SettingsDialog"); + saveDialogSize(dialogConfig); +} + +void DolphinSettingsDialog::slotButtonClicked(int button) +{ + if ((button == Ok) || (button == Apply)) { + applySettings(); + } else if (button == Default) { + restoreDefaults(); + } + + KPageDialog::slotButtonClicked(button); +} + +void DolphinSettingsDialog::enableApply() +{ + enableButtonApply(true); +} + +void DolphinSettingsDialog::applySettings() +{ + foreach (SettingsPageBase* page, m_pages) { + page->applySettings(); + } + + emit settingsChanged(); + + GeneralSettings* settings = GeneralSettings::self(); + if (settings->modifiedStartupSettings()) { + // Reset the modified startup settings hint. The changed startup settings + // have been applied already due to emitting settingsChanged(). + settings->setModifiedStartupSettings(false); + settings->writeConfig(); + } + + enableButtonApply(false); +} + +void DolphinSettingsDialog::restoreDefaults() +{ + foreach (SettingsPageBase* page, m_pages) { + page->restoreDefaults(); + } +} + +#include "moc_dolphinsettingsdialog.cpp" diff --git a/dolphin/src/settings/dolphinsettingsdialog.h b/dolphin/src/settings/dolphinsettingsdialog.h new file mode 100644 index 00000000..2de19501 --- /dev/null +++ b/dolphin/src/settings/dolphinsettingsdialog.h @@ -0,0 +1,61 @@ +/*************************************************************************** + * Copyright (C) 2006 by Peter Penz * + * peter.penz@gmx.at * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef DOLPHINSETTINGSDIALOG_H +#define DOLPHINSETTINGSDIALOG_H + +#include + +class KUrl; +class SettingsPageBase; + +/** + * @brief Settings dialog for Dolphin. + * + * Contains the pages for Startup, View Modes, Navigation, Services, General, and Trash. + */ +class DolphinSettingsDialog : public KPageDialog +{ + Q_OBJECT + +public: + explicit DolphinSettingsDialog(const KUrl& url, QWidget* parent = 0); + virtual ~DolphinSettingsDialog(); + +signals: + void settingsChanged(); + +protected slots: + /** @see KDialog::slotButtonClicked() */ + virtual void slotButtonClicked(int button); + +private slots: + /** Enables the Apply button. */ + void enableApply(); + +private: + void applySettings(); + void restoreDefaults(); + +private: + QList m_pages; +}; + +#endif diff --git a/dolphin/src/settings/general/behaviorsettingspage.cpp b/dolphin/src/settings/general/behaviorsettingspage.cpp new file mode 100644 index 00000000..d13b603f --- /dev/null +++ b/dolphin/src/settings/general/behaviorsettingspage.cpp @@ -0,0 +1,144 @@ +/*************************************************************************** + * Copyright (C) 2006 by Peter Penz (peter.penz@gmx.at) and * + * and Patrice Tremblay * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "behaviorsettingspage.h" + +#include "dolphin_generalsettings.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +BehaviorSettingsPage::BehaviorSettingsPage(const KUrl& url, QWidget* parent) : + SettingsPageBase(parent), + m_url(url), + m_localViewProps(0), + m_globalViewProps(0), + m_showToolTips(0), + m_showSelectionToggle(0), + m_naturalSorting(0), + m_renameInline(0) +{ + QVBoxLayout* topLayout = new QVBoxLayout(this); + + // View properties + QGroupBox* viewPropsBox = new QGroupBox(i18nc("@title:group", "View"), this); + viewPropsBox->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum); + + m_localViewProps = new QRadioButton(i18nc("@option:radio", "Remember properties for each folder"), viewPropsBox); + m_globalViewProps = new QRadioButton(i18nc("@option:radio", "Use common properties for all folders"), viewPropsBox); + + QVBoxLayout* viewPropsLayout = new QVBoxLayout(viewPropsBox); + viewPropsLayout->addWidget(m_localViewProps); + viewPropsLayout->addWidget(m_globalViewProps); + + // 'Show tooltips' + m_showToolTips = new QCheckBox(i18nc("@option:check", "Show tooltips"), this); + + // 'Show selection marker' + m_showSelectionToggle = new QCheckBox(i18nc("@option:check", "Show selection marker"), this); + + // 'Natural sorting of items' + m_naturalSorting = new QCheckBox(i18nc("option:check", "Natural sorting of items"), this); + + // 'Inline renaming of items' + m_renameInline = new QCheckBox(i18nc("option:check", "Rename inline"), this); + + topLayout->addWidget(viewPropsBox); + topLayout->addWidget(m_showToolTips); + topLayout->addWidget(m_showSelectionToggle); + topLayout->addWidget(m_naturalSorting); + topLayout->addWidget(m_renameInline); + topLayout->addStretch(); + + loadSettings(); + + connect(m_localViewProps, SIGNAL(toggled(bool)), this, SIGNAL(changed())); + connect(m_globalViewProps, SIGNAL(toggled(bool)), this, SIGNAL(changed())); + connect(m_showToolTips, SIGNAL(toggled(bool)), this, SIGNAL(changed())); + connect(m_showSelectionToggle, SIGNAL(toggled(bool)), this, SIGNAL(changed())); + connect(m_naturalSorting, SIGNAL(toggled(bool)), this, SIGNAL(changed())); + connect(m_renameInline, SIGNAL(toggled(bool)), this, SIGNAL(changed())); +} + +BehaviorSettingsPage::~BehaviorSettingsPage() +{ +} + +void BehaviorSettingsPage::applySettings() +{ + GeneralSettings* settings = GeneralSettings::self(); + ViewProperties props(m_url); // read current view properties + + const bool useGlobalViewProps = m_globalViewProps->isChecked(); + settings->setGlobalViewProps(useGlobalViewProps); + + settings->setShowToolTips(m_showToolTips->isChecked()); + settings->setShowSelectionToggle(m_showSelectionToggle->isChecked()); + settings->setRenameInline(m_renameInline->isChecked()); + settings->writeConfig(); + + if (useGlobalViewProps) { + // Remember the global view properties by applying the current view properties. + // It is important that GeneralSettings::globalViewProps() is set before + // the class ViewProperties is used, as ViewProperties uses this setting + // to find the destination folder for storing the view properties. + ViewProperties globalProps(m_url); + globalProps.setDirProperties(props); + } + + const bool naturalSorting = m_naturalSorting->isChecked(); + if (KGlobalSettings::naturalSorting() != naturalSorting) { + KConfigGroup group(KGlobal::config(), "KDE"); + group.writeEntry("NaturalSorting", naturalSorting, KConfig::Persistent | KConfig::Global); + KGlobalSettings::emitChange(KGlobalSettings::NaturalSortingChanged); + } +} + +void BehaviorSettingsPage::restoreDefaults() +{ + GeneralSettings* settings = GeneralSettings::self(); + settings->useDefaults(true); + loadSettings(); + settings->useDefaults(false); +} + +void BehaviorSettingsPage::loadSettings() +{ + const bool useGlobalViewProps = GeneralSettings::globalViewProps(); + m_localViewProps->setChecked(!useGlobalViewProps); + m_globalViewProps->setChecked(useGlobalViewProps); + + m_showToolTips->setChecked(GeneralSettings::showToolTips()); + m_showSelectionToggle->setChecked(GeneralSettings::showSelectionToggle()); + m_naturalSorting->setChecked(KGlobalSettings::naturalSorting()); + m_renameInline->setChecked(GeneralSettings::renameInline()); +} + +#include "moc_behaviorsettingspage.cpp" diff --git a/dolphin/src/settings/general/behaviorsettingspage.h b/dolphin/src/settings/general/behaviorsettingspage.h new file mode 100644 index 00000000..21384436 --- /dev/null +++ b/dolphin/src/settings/general/behaviorsettingspage.h @@ -0,0 +1,63 @@ +/*************************************************************************** + * Copyright (C) 2006 by Peter Penz * + * peter.penz@gmx.at * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ +#ifndef BEHAVIORSETTINGSPAGE_H +#define BEHAVIORSETTINGSPAGE_H + +#include +#include + +class KComboBox; +#include +#include +#include + +/** + * @brief Tab page for the 'Behavior' settings of the Dolphin settings dialog. + */ +class BehaviorSettingsPage : public SettingsPageBase +{ + Q_OBJECT + +public: + BehaviorSettingsPage(const KUrl& url, QWidget* parent); + virtual ~BehaviorSettingsPage(); + + /** @see SettingsPageBase::applySettings() */ + virtual void applySettings(); + + /** @see SettingsPageBase::restoreDefaults() */ + virtual void restoreDefaults(); + +private: + void loadSettings(); + +private: + KUrl m_url; + + QRadioButton* m_localViewProps; + QRadioButton* m_globalViewProps; + + QCheckBox* m_showToolTips; + QCheckBox* m_showSelectionToggle; + QCheckBox* m_naturalSorting; + QCheckBox* m_renameInline; +}; + +#endif diff --git a/dolphin/src/settings/general/configurepreviewplugindialog.cpp b/dolphin/src/settings/general/configurepreviewplugindialog.cpp new file mode 100644 index 00000000..4afa517d --- /dev/null +++ b/dolphin/src/settings/general/configurepreviewplugindialog.cpp @@ -0,0 +1,81 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "configurepreviewplugindialog.h" + +#include +#include +#include + +#include +#include +#include +#include + +ConfigurePreviewPluginDialog::ConfigurePreviewPluginDialog(const QString& pluginName, + const QString& desktopEntryName, + QWidget* parent) : + KDialog(parent), + m_configurationWidget(0), + m_previewPlugin(0) +{ + QLibrary library(desktopEntryName); + if (library.load()) { + newCreator create = (newCreator)library.resolve("new_creator"); + if (create) { + m_previewPlugin = dynamic_cast(create()); + } + } + + setCaption(i18nc("@title:window", "Configure Preview for %1", pluginName)); + setMinimumWidth(400); + setButtons(Ok | Cancel); + setDefaultButton(Ok); + + QWidget* mainWidget = new QWidget(this); + mainWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum); + QVBoxLayout* layout = new QVBoxLayout(mainWidget); + if (m_previewPlugin) { + m_configurationWidget = m_previewPlugin->createConfigurationWidget(); + layout->addWidget(m_configurationWidget); + } + layout->addStretch(1); + + setMainWidget(mainWidget); + + connect(this, SIGNAL(okClicked()), this, SLOT(slotOk())); +} + +ConfigurePreviewPluginDialog::~ConfigurePreviewPluginDialog() +{ +} + +void ConfigurePreviewPluginDialog::slotOk() +{ + m_previewPlugin->writeConfiguration(m_configurationWidget); + // TODO: It would be great having a mechanism to tell PreviewJob that only previews + // for a specific MIME-type should be regenerated. As this is not available yet we + // delete the whole thumbnails directory. + QApplication::changeOverrideCursor(Qt::BusyCursor); + KIO::NetAccess::del(QString(QDir::homePath() + "/.thumbnails/"), this); + QApplication::restoreOverrideCursor(); + +} + +#include "moc_configurepreviewplugindialog.cpp" diff --git a/dolphin/src/settings/general/configurepreviewplugindialog.h b/dolphin/src/settings/general/configurepreviewplugindialog.h new file mode 100644 index 00000000..836dde60 --- /dev/null +++ b/dolphin/src/settings/general/configurepreviewplugindialog.h @@ -0,0 +1,55 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef CONFIGUREPREVIEWPLUGINDIALOG_H +#define CONFIGUREPREVIEWPLUGINDIALOG_H + +#include + +class ThumbCreator; + +/** + * @brief Dialog for configuring preview-plugins. + */ +class ConfigurePreviewPluginDialog : public KDialog +{ + Q_OBJECT + +public: + /** + * @param pluginName User visible name of the plugin + * @param desktopEntryName The name of the plugin that is noted in the desktopentry. + * Is used to instantiate the plugin to get the configuration + * widget. + * @param parent Parent widget. + */ + explicit ConfigurePreviewPluginDialog(const QString& pluginName, + const QString& desktopEntryName, + QWidget* parent = 0); + virtual ~ConfigurePreviewPluginDialog(); + +private slots: + void slotOk(); + +private: + QWidget* m_configurationWidget; + ThumbCreator* m_previewPlugin; +}; + +#endif diff --git a/dolphin/src/settings/general/confirmationssettingspage.cpp b/dolphin/src/settings/general/confirmationssettingspage.cpp new file mode 100644 index 00000000..3185bfbf --- /dev/null +++ b/dolphin/src/settings/general/confirmationssettingspage.cpp @@ -0,0 +1,114 @@ +/*************************************************************************** + * Copyright (C) 2012 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "confirmationssettingspage.h" + +#include + +#include +#include + +#include +#include +#include + +namespace { + const bool ConfirmTrash = false; + const bool ConfirmDelete = true; +} + +ConfirmationsSettingsPage::ConfirmationsSettingsPage(QWidget* parent) : + SettingsPageBase(parent), + m_confirmMoveToTrash(0), + m_confirmDelete(0), + m_confirmClosingMultipleTabs(0) +{ + QVBoxLayout* topLayout = new QVBoxLayout(this); + + QLabel* confirmLabelKde = new QLabel(i18nc("@title:group", "Ask for confirmation in all KDE applications when:"), this); + confirmLabelKde->setWordWrap(true); + + m_confirmMoveToTrash = new QCheckBox(i18nc("@option:check Ask for confirmation when", + "Moving files or folders to trash"), this); + m_confirmDelete = new QCheckBox(i18nc("@option:check Ask for confirmation when", + "Deleting files or folders"), this); + + QLabel* confirmLabelDolphin = new QLabel(i18nc("@title:group", "Ask for confirmation when:"), this); + confirmLabelDolphin->setWordWrap(true); + + m_confirmClosingMultipleTabs = new QCheckBox(i18nc("@option:check Ask for confirmation when", + "Closing Dolphin windows with multiple tabs"), this); + + topLayout->addSpacing(KDialog::spacingHint()); + topLayout->addWidget(confirmLabelKde); + topLayout->addSpacing(KDialog::spacingHint()); + topLayout->addWidget(m_confirmMoveToTrash); + topLayout->addWidget(m_confirmDelete); + topLayout->addSpacing(KDialog::spacingHint()); + topLayout->addWidget(confirmLabelDolphin); + topLayout->addSpacing(KDialog::spacingHint()); + topLayout->addWidget(m_confirmClosingMultipleTabs); + topLayout->addStretch(); + + loadSettings(); + + connect(m_confirmMoveToTrash, SIGNAL(toggled(bool)), this, SIGNAL(changed())); + connect(m_confirmDelete, SIGNAL(toggled(bool)), this, SIGNAL(changed())); + connect(m_confirmClosingMultipleTabs, SIGNAL(toggled(bool)), this, SIGNAL(changed())); +} + +ConfirmationsSettingsPage::~ConfirmationsSettingsPage() +{ +} + +void ConfirmationsSettingsPage::applySettings() +{ + KSharedConfig::Ptr kioConfig = KSharedConfig::openConfig("kiorc", KConfig::NoGlobals); + KConfigGroup confirmationGroup(kioConfig, "Confirmations"); + confirmationGroup.writeEntry("ConfirmTrash", m_confirmMoveToTrash->isChecked()); + confirmationGroup.writeEntry("ConfirmDelete", m_confirmDelete->isChecked()); + confirmationGroup.sync(); + + GeneralSettings* settings = GeneralSettings::self(); + settings->setConfirmClosingMultipleTabs(m_confirmClosingMultipleTabs->isChecked()); + settings->writeConfig(); +} + +void ConfirmationsSettingsPage::restoreDefaults() +{ + GeneralSettings* settings = GeneralSettings::self(); + settings->useDefaults(true); + loadSettings(); + settings->useDefaults(false); + + m_confirmMoveToTrash->setChecked(ConfirmTrash); + m_confirmDelete->setChecked(ConfirmDelete); +} + +void ConfirmationsSettingsPage::loadSettings() +{ + KSharedConfig::Ptr kioConfig = KSharedConfig::openConfig("kiorc", KConfig::IncludeGlobals); + const KConfigGroup confirmationGroup(kioConfig, "Confirmations"); + m_confirmMoveToTrash->setChecked(confirmationGroup.readEntry("ConfirmTrash", ConfirmTrash)); + m_confirmDelete->setChecked(confirmationGroup.readEntry("ConfirmDelete", ConfirmDelete)); + + m_confirmClosingMultipleTabs->setChecked(GeneralSettings::confirmClosingMultipleTabs()); +} + +#include "moc_confirmationssettingspage.cpp" diff --git a/dolphin/src/settings/general/confirmationssettingspage.h b/dolphin/src/settings/general/confirmationssettingspage.h new file mode 100644 index 00000000..f30a3bc1 --- /dev/null +++ b/dolphin/src/settings/general/confirmationssettingspage.h @@ -0,0 +1,52 @@ +/*************************************************************************** + * Copyright (C) 2012 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ +#ifndef CONFIRMATIONSSETTINGSPAGE_H +#define CONFIRMATIONSSETTINGSPAGE_H + +#include + +#include + +/** + * @brief Page for the enabling or disabling confirmation dialogs. + */ +class ConfirmationsSettingsPage : public SettingsPageBase +{ + Q_OBJECT + +public: + ConfirmationsSettingsPage(QWidget* parent); + virtual ~ConfirmationsSettingsPage(); + + /** @see SettingsPageBase::applySettings() */ + virtual void applySettings(); + + /** @see SettingsPageBase::restoreDefaults() */ + virtual void restoreDefaults(); + +private: + void loadSettings(); + +private: + QCheckBox* m_confirmMoveToTrash; + QCheckBox* m_confirmDelete; + QCheckBox* m_confirmClosingMultipleTabs; +}; + +#endif diff --git a/dolphin/src/settings/general/generalsettingspage.cpp b/dolphin/src/settings/general/generalsettingspage.cpp new file mode 100644 index 00000000..31162743 --- /dev/null +++ b/dolphin/src/settings/general/generalsettingspage.cpp @@ -0,0 +1,92 @@ +/*************************************************************************** + * Copyright (C) 2006 by Peter Penz * + * peter.penz@gmx.at * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "generalsettingspage.h" + +#include "behaviorsettingspage.h" +#include "confirmationssettingspage.h" +#include "previewssettingspage.h" +#include +#include "statusbarsettingspage.h" + +#include +#include +#include +#include + +#include + +GeneralSettingsPage::GeneralSettingsPage(const KUrl& url, QWidget* parent) : + SettingsPageBase(parent), + m_pages() +{ + QVBoxLayout* topLayout = new QVBoxLayout(this); + topLayout->setMargin(0); + topLayout->setSpacing(KDialog::spacingHint()); + + KTabWidget* tabWidget = new KTabWidget(this); + + // initialize 'Behavior' tab + BehaviorSettingsPage* behaviorPage = new BehaviorSettingsPage(url, tabWidget); + tabWidget->addTab(behaviorPage, i18nc("@title:tab Behavior settings", "Behavior")); + connect(behaviorPage, SIGNAL(changed()), this, SIGNAL(changed())); + + // initialize 'Previews' tab + PreviewsSettingsPage* previewsPage = new PreviewsSettingsPage(tabWidget); + tabWidget->addTab(previewsPage, i18nc("@title:tab Previews settings", "Previews")); + connect(previewsPage, SIGNAL(changed()), this, SIGNAL(changed())); + + // initialize 'Context Menu' tab + ConfirmationsSettingsPage* confirmationsPage = new ConfirmationsSettingsPage(tabWidget); + tabWidget->addTab(confirmationsPage, i18nc("@title:tab Confirmations settings", "Confirmations")); + connect(confirmationsPage, SIGNAL(changed()), this, SIGNAL(changed())); + + // initialize 'Status Bar' tab + StatusBarSettingsPage* statusBarPage = new StatusBarSettingsPage(tabWidget); + tabWidget->addTab(statusBarPage, i18nc("@title:tab Status Bar settings", "Status Bar")); + connect(statusBarPage, SIGNAL(changed()), this, SIGNAL(changed())); + + m_pages.append(behaviorPage); + m_pages.append(previewsPage); + m_pages.append(confirmationsPage); + m_pages.append(statusBarPage); + + topLayout->addWidget(tabWidget, 0, 0); +} + +GeneralSettingsPage::~GeneralSettingsPage() +{ +} + +void GeneralSettingsPage::applySettings() +{ + foreach (SettingsPageBase* page, m_pages) { + page->applySettings(); + } +} + +void GeneralSettingsPage::restoreDefaults() +{ + foreach (SettingsPageBase* page, m_pages) { + page->restoreDefaults(); + } +} + +#include "moc_generalsettingspage.cpp" diff --git a/dolphin/src/settings/general/generalsettingspage.h b/dolphin/src/settings/general/generalsettingspage.h new file mode 100644 index 00000000..0d28664f --- /dev/null +++ b/dolphin/src/settings/general/generalsettingspage.h @@ -0,0 +1,56 @@ +/*************************************************************************** + * Copyright (C) 2006 by Peter Penz * + * peter.penz@gmx.at * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ +#ifndef GENERALSETTINGSPAGE_H +#define GENERALSETTINGSPAGE_H + +#include +#include + +class KUrl; +class SettingsPageBase; + +/** + * @brief Page for the 'General' settings of the Dolphin settings dialog. + * + * The general settings include: + * - Behavior + * - Previews + * - Context Menu + * - Status Bar + */ +class GeneralSettingsPage : public SettingsPageBase +{ + Q_OBJECT + +public: + GeneralSettingsPage(const KUrl& url, QWidget* parent); + virtual ~GeneralSettingsPage(); + + /** @see SettingsPageBase::applySettings() */ + virtual void applySettings(); + + /** @see SettingsPageBase::restoreDefaults() */ + virtual void restoreDefaults(); + +private: + QList m_pages; +}; + +#endif diff --git a/dolphin/src/settings/general/previewssettingspage.cpp b/dolphin/src/settings/general/previewssettingspage.cpp new file mode 100644 index 00000000..d414fa05 --- /dev/null +++ b/dolphin/src/settings/general/previewssettingspage.cpp @@ -0,0 +1,220 @@ +/*************************************************************************** + * Copyright (C) 2006 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "previewssettingspage.h" + +#include "dolphin_generalsettings.h" +#include "configurepreviewplugindialog.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// default settings +// NOTE: keep in sync with kdelibs/kio/kio/previewjob.cpp +enum MaxPreviewSizes { + MaxLocalSize = 5, // 5 MB + MaxRemoteSize = 1 // 1 MB +}; + +PreviewsSettingsPage::PreviewsSettingsPage(QWidget* parent) : + SettingsPageBase(parent), + m_initialized(false), + m_listView(0), + m_enabledPreviewPlugins(), + m_localFileSizeBox(0), + m_remoteFileSizeBox(0) +{ + QVBoxLayout* topLayout = new QVBoxLayout(this); + + QLabel* showPreviewsLabel = new QLabel(i18nc("@title:group", "Show previews for:"), this); + + m_listView = new QListView(this); + + ServiceItemDelegate* delegate = new ServiceItemDelegate(m_listView, m_listView); + connect(delegate, SIGNAL(requestServiceConfiguration(QModelIndex)), + this, SLOT(configureService(QModelIndex))); + + ServiceModel* serviceModel = new ServiceModel(this); + QSortFilterProxyModel* proxyModel = new QSortFilterProxyModel(this); + proxyModel->setSourceModel(serviceModel); + proxyModel->setSortRole(Qt::DisplayRole); + + m_listView->setModel(proxyModel); + m_listView->setItemDelegate(delegate); + m_listView->setVerticalScrollMode(QListView::ScrollPerPixel); + + QLabel* localFileSizeLabel = new QLabel(i18nc("@label", "Skip previews for local files above:"), this); + QLabel* remoteFileSizeLabel = new QLabel(i18nc("@label", "Skip previews for remote files above:"), this); + + m_localFileSizeBox = new KIntSpinBox(this); + m_localFileSizeBox->setSingleStep(1); + m_localFileSizeBox->setSuffix(QLatin1String(" MB")); + m_localFileSizeBox->setRange(0, 9999999); /* MB */ + + m_remoteFileSizeBox = new KIntSpinBox(this); + m_remoteFileSizeBox->setSingleStep(1); + m_remoteFileSizeBox->setSuffix(QLatin1String(" MB")); + m_remoteFileSizeBox->setRange(0, 9999999); /* MB */ + + QGridLayout* fileSizeLayout = new QGridLayout(this); + fileSizeLayout->addWidget(localFileSizeLabel, 0, 0); + fileSizeLayout->addWidget(m_localFileSizeBox, 0, 1, Qt::AlignRight); + fileSizeLayout->addWidget(remoteFileSizeLabel, 1, 0); + fileSizeLayout->addWidget(m_remoteFileSizeBox, 1, 1, Qt::AlignRight); + + topLayout->addSpacing(KDialog::spacingHint()); + topLayout->addWidget(showPreviewsLabel); + topLayout->addWidget(m_listView); + topLayout->addLayout(fileSizeLayout); + + loadSettings(); + + connect(m_listView, SIGNAL(clicked(QModelIndex)), this, SIGNAL(changed())); + connect(m_localFileSizeBox, SIGNAL(valueChanged(int)), this, SIGNAL(changed())); + connect(m_remoteFileSizeBox, SIGNAL(valueChanged(int)), this, SIGNAL(changed())); +} + +PreviewsSettingsPage::~PreviewsSettingsPage() +{ +} + +void PreviewsSettingsPage::applySettings() +{ + const QAbstractItemModel* model = m_listView->model(); + const int rowCount = model->rowCount(); + if (rowCount > 0) { + m_enabledPreviewPlugins.clear(); + for (int i = 0; i < rowCount; ++i) { + const QModelIndex index = model->index(i, 0); + const bool checked = model->data(index, Qt::CheckStateRole).toBool(); + if (checked) { + const QString enabledPlugin = model->data(index, Qt::UserRole).toString(); + m_enabledPreviewPlugins.append(enabledPlugin); + } + } + } + + KConfigGroup globalConfig(KGlobal::config(), QLatin1String("PreviewSettings")); + globalConfig.writeEntry("Plugins", m_enabledPreviewPlugins, + KConfigBase::Normal | KConfigBase::Global); + + const qulonglong maximumLocalSize = static_cast(m_localFileSizeBox->value()) * 1024 * 1024; + globalConfig.writeEntry("MaximumSize", + maximumLocalSize, + KConfigBase::Normal | KConfigBase::Global); + + const qulonglong maximumRemoteSize = static_cast(m_remoteFileSizeBox->value()) * 1024 * 1024; + globalConfig.writeEntry("MaximumRemoteSize", + maximumRemoteSize, + KConfigBase::Normal | KConfigBase::Global); + globalConfig.sync(); +} + +void PreviewsSettingsPage::restoreDefaults() +{ + m_localFileSizeBox->setValue(MaxPreviewSizes::MaxLocalSize); + m_remoteFileSizeBox->setValue(MaxPreviewSizes::MaxRemoteSize); +} + +void PreviewsSettingsPage::showEvent(QShowEvent* event) +{ + if (!event->spontaneous() && !m_initialized) { + loadPreviewPlugins(); + m_initialized = true; + } + SettingsPageBase::showEvent(event); +} + +void PreviewsSettingsPage::configureService(const QModelIndex& index) +{ + const QAbstractItemModel* model = index.model(); + const QString pluginName = model->data(index).toString(); + const QString desktopEntryName = model->data(index, ServiceModel::DesktopEntryNameRole).toString(); + + ConfigurePreviewPluginDialog* dialog = new ConfigurePreviewPluginDialog(pluginName, desktopEntryName, this); + dialog->setAttribute(Qt::WA_DeleteOnClose); + dialog->show(); +} + +void PreviewsSettingsPage::loadPreviewPlugins() +{ + QAbstractItemModel* model = m_listView->model(); + + const KService::List plugins = KServiceTypeTrader::self()->query(QLatin1String("ThumbCreator")); + foreach (const KSharedPtr& service, plugins) { + const bool configurable = service->property("Configurable", QVariant::Bool).toBool(); + const bool show = m_enabledPreviewPlugins.contains(service->desktopEntryName()); + + model->insertRow(0); + const QModelIndex index = model->index(0, 0); + model->setData(index, show, Qt::CheckStateRole); + model->setData(index, configurable, ServiceModel::ConfigurableRole); + model->setData(index, service->name(), Qt::DisplayRole); + model->setData(index, service->desktopEntryName(), ServiceModel::DesktopEntryNameRole); + } + + model->sort(Qt::DisplayRole); +} + +void PreviewsSettingsPage::loadSettings() +{ + KConfigGroup globalConfig(KGlobal::config(), "PreviewSettings"); + + QStringList enabledByDefault; + const KService::List plugins = KServiceTypeTrader::self()->query(QLatin1String("ThumbCreator")); + foreach (const KSharedPtr& service, plugins) { + const bool enabled = service->property("X-KDE-PluginInfo-EnabledByDefault", QVariant::Bool).toBool(); + if (enabled) { + enabledByDefault << service->desktopEntryName(); + } + } + + m_enabledPreviewPlugins = globalConfig.readEntry("Plugins", enabledByDefault); + + const qulonglong defaultLocalPreview = static_cast(MaxPreviewSizes::MaxLocalSize) * 1024 * 1024; + const qulonglong defaultRemotePreview = static_cast(MaxPreviewSizes::MaxRemoteSize) * 1024 * 1024; + const qulonglong maxLocalByteSize = globalConfig.readEntry("MaximumSize", defaultLocalPreview); + const qulonglong maxRemoteByteSize = globalConfig.readEntry("MaximumRemoteSize", defaultRemotePreview); + const int maxLocalMByteSize = maxLocalByteSize / (1024 * 1024); + const int maxRemoteMByteSize = maxRemoteByteSize / (1024 * 1024); + m_localFileSizeBox->setValue(maxLocalMByteSize); + m_remoteFileSizeBox->setValue(maxRemoteMByteSize); +} + +#include "moc_previewssettingspage.cpp" diff --git a/dolphin/src/settings/general/previewssettingspage.h b/dolphin/src/settings/general/previewssettingspage.h new file mode 100644 index 00000000..0214ca1c --- /dev/null +++ b/dolphin/src/settings/general/previewssettingspage.h @@ -0,0 +1,68 @@ +/*************************************************************************** + * Copyright (C) 2006 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef PREVIEWSSETTINGSPAGE_H +#define PREVIEWSSETTINGSPAGE_H + +#include + +class KIntSpinBox; +#include +#include + +/** + * @brief Allows the configuration of file previews. + */ +class PreviewsSettingsPage : public SettingsPageBase +{ + Q_OBJECT + +public: + PreviewsSettingsPage(QWidget* parent); + virtual ~PreviewsSettingsPage(); + + /** + * Applies the general settings for the view modes + * The settings are persisted automatically when + * closing Dolphin. + */ + virtual void applySettings(); + + /** Restores the settings to default values. */ + virtual void restoreDefaults(); + +protected: + virtual void showEvent(QShowEvent* event); + +private slots: + void configureService(const QModelIndex& index); + +private: + void loadPreviewPlugins(); + void loadSettings(); + +private: + bool m_initialized; + QListView *m_listView; + QStringList m_enabledPreviewPlugins; + KIntSpinBox* m_localFileSizeBox; + KIntSpinBox* m_remoteFileSizeBox; +}; + +#endif diff --git a/dolphin/src/settings/general/statusbarsettingspage.cpp b/dolphin/src/settings/general/statusbarsettingspage.cpp new file mode 100644 index 00000000..8f4203ae --- /dev/null +++ b/dolphin/src/settings/general/statusbarsettingspage.cpp @@ -0,0 +1,76 @@ +/*************************************************************************** + * Copyright (C) 2009 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "statusbarsettingspage.h" + +#include + +#include +#include + +#include +#include + +StatusBarSettingsPage::StatusBarSettingsPage(QWidget* parent) : + SettingsPageBase(parent), + m_showZoomSlider(0), + m_showSpaceInfo(0) +{ + m_showZoomSlider = new QCheckBox(i18nc("@option:check", "Show zoom slider"), this); + m_showSpaceInfo = new QCheckBox(i18nc("@option:check", "Show space information"), this); + + QVBoxLayout* topLayout = new QVBoxLayout(this); + topLayout->addSpacing(KDialog::spacingHint()); + topLayout->addWidget(m_showZoomSlider); + topLayout->addWidget(m_showSpaceInfo); + topLayout->addStretch(); + + loadSettings(); + + connect(m_showZoomSlider, SIGNAL(toggled(bool)), this, SIGNAL(changed())); + connect(m_showSpaceInfo, SIGNAL(toggled(bool)), this, SIGNAL(changed())); +} + +StatusBarSettingsPage::~StatusBarSettingsPage() +{ +} + +void StatusBarSettingsPage::applySettings() +{ + GeneralSettings* settings = GeneralSettings::self(); + settings->setShowZoomSlider(m_showZoomSlider->isChecked()); + settings->setShowSpaceInfo(m_showSpaceInfo->isChecked()); + settings->writeConfig(); +} + +void StatusBarSettingsPage::restoreDefaults() +{ + GeneralSettings* settings = GeneralSettings::self(); + settings->useDefaults(true); + loadSettings(); + settings->useDefaults(false); +} + +void StatusBarSettingsPage::loadSettings() +{ + m_showZoomSlider->setChecked(GeneralSettings::showZoomSlider()); + m_showSpaceInfo->setChecked(GeneralSettings::showSpaceInfo()); +} + +#include "moc_statusbarsettingspage.cpp" diff --git a/dolphin/src/settings/general/statusbarsettingspage.h b/dolphin/src/settings/general/statusbarsettingspage.h new file mode 100644 index 00000000..ab9af52b --- /dev/null +++ b/dolphin/src/settings/general/statusbarsettingspage.h @@ -0,0 +1,51 @@ +/*************************************************************************** + * Copyright (C) 2009 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ +#ifndef STATUSBARSETTINGSPAGE_H +#define STATUSBARSETTINGSPAGE_H + +#include + +#include + +/** + * @brief Tab page for the 'Status Bar' settings of the Dolphin settings dialog. + */ +class StatusBarSettingsPage : public SettingsPageBase +{ + Q_OBJECT + +public: + StatusBarSettingsPage(QWidget* parent); + virtual ~StatusBarSettingsPage(); + + /** @see SettingsPageBase::applySettings() */ + virtual void applySettings(); + + /** @see SettingsPageBase::restoreDefaults() */ + virtual void restoreDefaults(); + +private: + void loadSettings(); + +private: + QCheckBox* m_showZoomSlider; + QCheckBox* m_showSpaceInfo; +}; + +#endif diff --git a/dolphin/src/settings/kcm/kcmdolphingeneral.cpp b/dolphin/src/settings/kcm/kcmdolphingeneral.cpp new file mode 100644 index 00000000..5070ef1f --- /dev/null +++ b/dolphin/src/settings/kcm/kcmdolphingeneral.cpp @@ -0,0 +1,94 @@ +/*************************************************************************** + * Copyright (C) 2009 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "kcmdolphingeneral.h" + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +K_PLUGIN_FACTORY(KCMDolphinGeneralConfigFactory, registerPlugin("dolphingeneral");) +K_EXPORT_PLUGIN(KCMDolphinGeneralConfigFactory("kcmdolphingeneral")) + +DolphinGeneralConfigModule::DolphinGeneralConfigModule(QWidget* parent, const QVariantList& args) : + KCModule(KCMDolphinGeneralConfigFactory::componentData(), parent), + m_pages() +{ + Q_UNUSED(args); + + KGlobal::locale()->insertCatalog("dolphin"); + + setButtons(KCModule::Default | KCModule::Help); + + QVBoxLayout* topLayout = new QVBoxLayout(this); + topLayout->setMargin(0); + topLayout->setSpacing(KDialog::spacingHint()); + + KTabWidget* tabWidget = new KTabWidget(this); + + // initialize 'Behavior' tab + BehaviorSettingsPage* behaviorPage = new BehaviorSettingsPage(QDir::homePath(), tabWidget); + tabWidget->addTab(behaviorPage, i18nc("@title:tab Behavior settings", "Behavior")); + connect(behaviorPage, SIGNAL(changed()), this, SLOT(changed())); + + // initialize 'Previews' tab + PreviewsSettingsPage* previewsPage = new PreviewsSettingsPage(tabWidget); + tabWidget->addTab(previewsPage, i18nc("@title:tab Previews settings", "Previews")); + connect(previewsPage, SIGNAL(changed()), this, SLOT(changed())); + + // initialize 'Confirmations' tab + ConfirmationsSettingsPage* confirmationsPage = new ConfirmationsSettingsPage(tabWidget); + tabWidget->addTab(confirmationsPage, i18nc("@title:tab Confirmations settings", "Confirmations")); + connect(confirmationsPage, SIGNAL(changed()), this, SLOT(changed())); + + m_pages.append(behaviorPage); + m_pages.append(previewsPage); + m_pages.append(confirmationsPage); + + topLayout->addWidget(tabWidget, 0, 0); +} + +DolphinGeneralConfigModule::~DolphinGeneralConfigModule() +{ +} + +void DolphinGeneralConfigModule::save() +{ + foreach (SettingsPageBase* page, m_pages) { + page->applySettings(); + } +} + +void DolphinGeneralConfigModule::defaults() +{ + foreach (SettingsPageBase* page, m_pages) { + page->applySettings(); + } +} + +#include "moc_kcmdolphingeneral.cpp" diff --git a/dolphin/src/settings/kcm/kcmdolphingeneral.desktop b/dolphin/src/settings/kcm/kcmdolphingeneral.desktop new file mode 100644 index 00000000..3573b63a --- /dev/null +++ b/dolphin/src/settings/kcm/kcmdolphingeneral.desktop @@ -0,0 +1,348 @@ +Name=Dolphin General +Name[ast]=Dolphin xeneral +Name[bg]=Общо за програмата +Name[bn]=ডলফিন সাধারণ +Name[bs]=Delfinovo opšte +Name[ca]=General del Dolphin +Name[ca@valencia]=General del Dolphin +Name[cs]=Obecný Dolphin +Name[csb]=Spòldowi nastôw Dolphina +Name[da]=Dolphin generelt +Name[de]=Dolphin allgemein +Name[el]=Γενικά Dolphin +Name[en_GB]=Dolphin General +Name[eo]=Dolphin-Ĝeneralo +Name[es]=Dolphin general +Name[et]=Dolphini üldine +Name[eu]=Dolphin orokorra +Name[fi]=Dolphin – yleiset +Name[fr]=Dolphin général +Name[fy]=Dolfyn algemien +Name[ga]=Dolphin Ginearálta +Name[gl]=Configuración xeral de Dolphin +Name[gu]=ડોલ્ફિન સામાન્ય +Name[he]=הגדרות כלליות של Dolphin +Name[hi]=डॉल्फ़िन सामान्य +Name[hr]=Općenito o Dolphinu +Name[hu]=Dolphin - Általános +Name[ia]=Dolphin General +Name[id]=Dolphin Umum +Name[is]=Almennar stillingar Dolphin +Name[it]=Impostazioni generali di Dolphin +Name[ja]=Dolphin 全般 +Name[kk]=Dolphin жалпы +Name[km]=ភាព​ទូទៅ​របស់ Dolphin +Name[kn]=ಡಾಲ್ಫಿನ್ ಸಾಮಾನ್ಯ +Name[ko]=Dolphin 일반 +Name[lt]=Dolphin bendrieji +Name[lv]=Dolphin pamata +Name[mai]=सामान्य डाल्फिन +Name[mk]=Делфин - општо +Name[ml]=ഡോള്‍ഫിന്‍ പൊതുവെ +Name[mr]=डॉल्फिन सामान्य +Name[ms]=Dolphin Umum +Name[nb]=Dolphin generelt +Name[nds]=Dolphin allmeen +Name[nl]=Dolphin algemeen +Name[nn]=Generelt for Dolphin +Name[pa]=ਡਾਲਫਿਨ ਆਮ +Name[pl]=Ustawienia Dolphina +Name[pt]=Geral do Dolphin +Name[pt_BR]=Geral do Dolphin +Name[ro]=Dolphin General +Name[ru]=Основные параметры Dolphin +Name[se]=Dolphinoppalaččat +Name[si]=Dolphin සාමාන්‍ය +Name[sk]=Všeobecný Dolphin +Name[sl]=Dolphin - splošno +Name[sr]=Делфиново опште +Name[sr@ijekavian]=Делфиново опште +Name[sr@ijekavianlatin]=Dolphinovo opšte +Name[sr@latin]=Dolphinovo opšte +Name[sv]=Dolphin allmänt +Name[tg]=Танзимоти умумии Dolphin +Name[th]=ค่าทั่วไปของดอลฟิน +Name[tr]=Dolphin Genel +Name[ug]=Dolphin ئادەتتىكى +Name[uk]=«Загальне» Dolphin +Name[wa]=Dolphin djenerå +Name[x-test]=xxDolphin Generalxx +Name[zh_CN]=Dolphin 常规 +Name[zh_TW]=Dolphin 一般 +Comment=This service allows configuration of general Dolphin settings. +Comment[ar]=تسمح هذه الخدمة بضبك إعدادات دولفين العامّة. +Comment[ast]=Esti serviciu déxate facer la configuración xeneral de Dolphin. +Comment[bg]=Това ви позволява да зададете общите настройки на програмата. +Comment[bs]=Ovaj servis omogućava podešavanje opštih Delfinovih postavki. +Comment[ca]=Aquest servei permet la configuració de l'arranjament general del Dolphin. +Comment[ca@valencia]=Este servei permet la configuració de l'arranjament general del Dolphin. +Comment[cs]=Tato služba umožňuje obecné nastavení Dolphinu. +Comment[csb]=Na ùsłëżnota pòzwôlô na spòdlową kònfigùracëjã nastôwów Dolphina. +Comment[da]=Denne tjeneste muliggør konfiguration af generelle Dolphin-indstillinger. +Comment[de]=Mit diesem Dienst können allgemeine Einstellungen von Dolphin eingerichtet werden. +Comment[el]=Αυτή η υπηρεσία επιτρέπει τη διαμόρφωση των γενικών επιλογών του Dolphin. +Comment[en_GB]=This service allows configuration of general Dolphin settings. +Comment[eo]=Ĉi tiu servo permesas agordi la ĝeneralajn Dolphin-agordojn. +Comment[es]=Este servicio le permite hacer la configuración general de Dolphin. +Comment[et]=See teenus võimaldab seadistada Dolphini üldisi seadistusi. +Comment[eu]=Zerbitzu honen bitartez Dolphin-en ezarpen orokorrak konfiguratu daitezke. +Comment[fi]=Tällä palvelulla voi muokata Dolphinin yleisasetuksia. +Comment[fr]=Ce service permet de configurer les paramètres généraux de Dolphin. +Comment[fy]=Mei dizze tsjinst kinne jo de algemiene dolfyn opsjes ynstelle. +Comment[ga]=Leis an tseirbhís seo is féidir na socruithe ginearálta Dolphin a chumrú. +Comment[gl]=Este servizo permite modificar a configuración xeral de Dolphin. +Comment[he]=שירות זה מאפשר להגדיר את ההגדרות הכלליות של Dolphin. +Comment[hi]=यह सेवा आपको डॉल्फ़िन सेटिंग कॉन्फ़िगर करने देता है. +Comment[hr]=Ova usluga dozvoljava konfiguraciju općenitih postavki za Dolphin. +Comment[hu]=Ez a szolgáltatás a Dolphin általános beállításainak módosítását teszi lehetővé. +Comment[ia]=Iste servicio permitte configuration del preferentias general de Dolphin. +Comment[id]=Layanan ini memungkinkan konfigurasi pengaturan Dolphin umum. +Comment[is]=Þessi þjónusta leyfir breytingar á almennum stillingum á Dolphin. +Comment[it]=Questo servizio permette di configurare le impostazioni generali di Dolphin. +Comment[ja]=Dolphin の全般的な設定を行います +Comment[kk]=Бұл Dolphin жалпы параметрлерін баптауға мүмкіндік беретін қызмет. +Comment[km]=សេវា​នេះ​អនុញ្ញាត​ឲ្យ​កំណត់​រចនាសម្ព័ន្ធ​នៃ​ការ​កំណត់​របស់ Dolphin ទូទៅ ។ +Comment[kn]=ಈ ಸೇವೆಯು ಡಾಲ್ಫಿನ್‌ನ ಸಾಮಾನ್ಯ ಸಂಯೋಜನೆಗಳ ಸಂರಚನೆಗೆ ಅನುವು ಮಾಡಿಕೊಡುತ್ತದೆ. +Comment[ko]=이 서비스는 일반 Dolphin 설정을 담당합니다. +Comment[lt]=Ši tarnyba leidžia konfigūruoti Dolphin bendruosius nustatymus. +Comment[lv]=Šis serviss ļauj konfigurēt Dolphin pamata iestatījumus. +Comment[mai]=ई सेवा अहाँकेँ सामान्य डॉल्फिन सेटिंग केँ विन्यस्त करै दैछ. +Comment[mk]=Овој сервис овозможува конфигурација на општите поставувања на Делфин. +Comment[ml]=പൊതുവായ ഡോള്‍ഫിന്‍ സജ്ജീകരണങ്ങള്‍ ക്രമീകരിയ്ക്കാന്‍ ഈ സേവനം അനുവദിയ്ക്കുന്നു. +Comment[mr]=ही सेवा तुम्हाला डॉल्फिनच्या सामान्य व्यवस्थेचे संयोजन करण्यास मदत करते. +Comment[ms]=Servis ini membenarkan tetapan bagi tetapan umum Dolphin. +Comment[nb]=Denne tjenesten tilbyr oppsett av generelle Dolphin-innstillinger. +Comment[nds]=Mit dissen Deenst laat sik allmeen Dolphin-Optschonen instellen. +Comment[nl]=Met deze dienst kunt u algemene Dolphin-instellingen configureren. +Comment[nn]=Denne tenesta lèt deg setja opp generelle innstillingar for Dolphin. +Comment[pa]=ਇਹ ਸਰਵਿਸ ਆਮ ਡਾਲਫਿਨ ਸੈਟਿੰਗ ਦੀ ਸੰਰਚਨਾ ਵਾਸਤੇ ਹੈ। +Comment[pl]=Ta usługa pozwala na ogólne ustawienie Dolphina. +Comment[pt]=Este serviço permite-lhe configurar as opções gerais do Dolphin. +Comment[pt_BR]=Este serviço permite configurar as opções gerais do Dolphin. +Comment[ro]=Acest serviciu permite configurarea opțiunilor generale ale lui Dolphin. +Comment[ru]=Эта служба позволяет настраивать основные параметры Dolphin +Comment[si]=මෙම සේවාව Dolphin දසුන් සැකසීමට ඉඩ සලසයි. +Comment[sk]=Táto služba umožňuje všeobecné nastavenie Dolphinu. +Comment[sl]=Ta storitev omogoča spreminjanje splošnih nastavitev programa Dolphin. +Comment[sr]=Овај сервис омогућава подешавање општих Делфинових поставки. +Comment[sr@ijekavian]=Овај сервис омогућава подешавање општих Делфинових поставки. +Comment[sr@ijekavianlatin]=Ovaj servis omogućava podešavanje opštih Dolphinovih postavki. +Comment[sr@latin]=Ovaj servis omogućava podešavanje opštih Dolphinovih postavki. +Comment[sv]=Den här tjänsten låter dig anpassa allmänna inställningar i Dolphin. +Comment[tg]=Ин хидмат барои танзимоти умумии Dolphin иҷозат медиҳад. +Comment[th]=บริการนี้อนุญาตให้ทำการปรับแต่งการตั้งค่าทั่วไปต่าง ๆ ของดอลฟิน +Comment[tr]=Bu servis genel Dolphin ayarlarını yapılandırmanızı sağlar. +Comment[ug]=بۇ مۇلازىمەت Dolphin نىڭ ئادەتتىكى تەڭشىكىنى سەپلىشىڭىزگە يول قويىدۇ. +Comment[uk]=Ця служба надасть змогу налаштувати загальні параметри Dolphin. +Comment[wa]=Ci siervice permet l' apontiaedje des tchuzes djeneråles di Dolhpin. +Comment[x-test]=xxThis service allows configuration of general Dolphin settings.xx +Comment[zh_CN]=此服务允许您配置 Dolphin 的常规设置。 +Comment[zh_TW]=此服務允許設定 Dolphin 的一般設定。 + +[Desktop Entry] +Icon=system-run +Type=Service +X-KDE-ServiceTypes=KCModule +Exec=kcmshell4 kcmdolphingeneral + +X-KDE-Library=kcm_dolphingeneral +X-KDE-PluginKeyword=dolphingeneral +X-KDE-ParentApp=kcontrol +X-DocPath=dolphin/index.html#preferences-dialog-general +# ctxt: Random file browsing settings. +Name=General +Name[ar]=عامّ +Name[ast]=Xeneral +Name[bg]=Общи +Name[bn]=সাধারণ +Name[bs]=Opšte +Name[ca]=General +Name[ca@valencia]=General +Name[cs]=Obecné +Name[csb]=Spòdlowé +Name[da]=Generelt +Name[de]=Allgemein +Name[el]=Γενικά +Name[en_GB]=General +Name[eo]=Ĝenerale +Name[es]=General +Name[et]=Üldine +Name[eu]=Orokorra +Name[fa]=عمومی +Name[fi]=Yleiset +Name[fr]=Général +Name[fy]=Algemien +Name[ga]=Ginearálta +Name[gl]=Xeral +Name[gu]=સામાન્ય +Name[he]=כללי +Name[hi]=सामान्य +Name[hr]=Općenito +Name[hu]=Általános +Name[ia]=General +Name[id]=Umum +Name[is]=Almennt +Name[it]=Generale +Name[ja]=全般 +Name[ka]=ძირითადი +Name[kk]=Жалпы +Name[km]=ទូទៅ +Name[kn]=ಸಾಮಾನ್ಯ +Name[ko]=일반 +Name[lt]=Bendri +Name[lv]=Pamata +Name[mai]=सामान्य +Name[mk]=Општо +Name[ml]=പൊതുവായ +Name[mr]=सामान्य +Name[ms]=Umum +Name[nb]=Generelt +Name[nds]=Allmeen +Name[nl]=Algemeen +Name[nn]=Generelt +Name[pa]=ਆਮ +Name[pl]=Ogólne +Name[pt]=Geral +Name[pt_BR]=Geral +Name[ro]=General +Name[ru]=Основное +Name[si]=සාමාන්‍ය +Name[sk]=Všeobecné +Name[sl]=Splošno +Name[sr]=Опште +Name[sr@ijekavian]=Опште +Name[sr@ijekavianlatin]=Opšte +Name[sr@latin]=Opšte +Name[sv]=Allmänt +Name[tg]=Умумӣ +Name[th]=ทั่วไป +Name[tr]=Genel +Name[ug]=ئادەتتىكى +Name[uk]=Загальне +Name[wa]=Djenerå +Name[x-test]=xxGeneralxx +Name[zh_CN]=常规 +Name[zh_TW]=一般 +Comment=Configure general file manager settings +Comment[ar]=اضبط إعدادات مدير الملفات العامّة +Comment[ast]=Configurar les preferencies del xestor de ficheros +Comment[bg]=Общи настройки на файловия мениджър +Comment[bn]=সাধারণ ফাইল ম্যানেজার সেটিংস কনফিগার করুন +Comment[bs]=Podešavanje opštih postavki menadžera datoteka +Comment[ca]=Configura les opcions generals del gestor de fitxers +Comment[ca@valencia]=Configura les opcions generals del gestor de fitxers +Comment[cs]=Obecné nastavení správce souborů +Comment[csb]=Kònfigùracëjô spòdlowégò nastôwù menadżera lopków +Comment[da]=Konfiguration af generelle indstillinger for filhåndtering +Comment[de]=Allgemeine Einstellungen am Dateimanager vornehmen +Comment[el]=Διαμόρφωση γενικών επιλογών διαχείρισης αρχείων +Comment[en_GB]=Configure general file manager settings +Comment[eo]=Agordi la ĝeneralan dosieradministrilon +Comment[es]=Configurar las preferencias del gestor de archivos +Comment[et]=Failihalduri üldiste seadistuste seadistamine +Comment[eu]=Konfiguratu fitxategi kudeatzailearen ezarpen orokorrak +Comment[fi]=Tiedostonhallinnan yleisasetukset +Comment[fr]=Configuration des paramètres généraux du gestionnaire de fichiers +Comment[fy]=Algemiene triembehearopsjes ynstelle +Comment[ga]=Cumraigh socruithe ginearálta bhainisteoir na gcomhad +Comment[gl]=Configura as opcións xerais do xestor de ficheiros +Comment[gu]=સામાન્ય ફાઇલ વ્યવસ્થાપક ગોઠવણીઓ રૂપરેખાંકિત કરો +Comment[he]=הגדרת הגדרות כלליות של מנהל הקבצים +Comment[hi]=सामान्य फ़ाइल प्रबंधक सेटिंग कॉन्फ़िगर करें +Comment[hr]=Konfiguriranje općih postavki upravitelja datotekama +Comment[hu]=A fájlkezelő általános beállításainak módosítását teszi lehetővé +Comment[ia]=Configura preferentias del gerente general de file +Comment[id]=Atur pengaturan manajer berkas umum +Comment[is]=Almennar stillingar skráastjóra +Comment[it]=Configura le impostazioni generali del gestore dei file +Comment[ja]=ファイルマネージャの全般的な設定を行います +Comment[kk]=Файл менеджер жалпы параметрлерін баптау +Comment[km]=កំណត់​រចនាសម្ព័ន្ធ​នៃ​ការ​កំណត់​កម្មវិធី​គ្រប់គ្រង​ឯកសារ​ទូទៅ +Comment[kn]=ಸಾಮಾನ್ಯ ಕಡತ ವ್ಯವಸ್ಥಾಪಕದ ಸಂಯೋಜನೆಗಳನ್ನು ಸಂರಚಿಸಿ +Comment[ko]=일반 파일 관리자 설정 +Comment[lt]=Bendrųjų failų tvarkyklės nustatymų konfigūravimas +Comment[lv]=Konfigurē failu pārvaldnieka pamata iestatījumus +Comment[mk]=Конфигурирајте ги општите поставувања за менаџерот на датотеки +Comment[ml]=ഫയല്‍ നടത്തിപ്പുകാരന്റെ പൊതുവായ സജ്ജീകരണങ്ങള്‍ ക്രമീകരിയ്ക്കുക +Comment[mr]=फाईल व्यवस्थापकाच्या सामान्य व्यवस्थेचे संयोजन करा +Comment[ms]=Selaraskan tetapan pengurus fail +Comment[nb]=Tilpass generelle filbehandler-innstillinger +Comment[nds]=Den Dateipleger allmeen instellen +Comment[nl]=Algemene bestandsbeheerderinstellingen configureren +Comment[nn]=Set opp generelle innstillingar for filhandsamaren +Comment[pa]=ਆਮ ਫਾਇਲ ਮੈਨੇਜਰ ਸੈਟਿੰਗ ਸੰਰਚਨਾ +Comment[pl]=Ustawienia ogólne zarządzania plikami +Comment[pt]=Configurar as opções gerais do gestor de ficheiros +Comment[pt_BR]=Configura as opções gerais do gerenciador de arquivos +Comment[ro]=Configurează opțiunile generale ale gestionarul de fișiere +Comment[ru]=Настройка диспетчера файлов +Comment[se]=Heivet oppalaš fiilagieđahallanheivehusat +Comment[si]=සාමාන්‍ය ගොනු කළමාණාකරන සැකසුම් සකසන්න +Comment[sk]=Všeobecné nastavenie správcu súborov +Comment[sl]=Splošne nastavitve upravljalnika datotek +Comment[sr]=Подешавање општих поставки менаџера фајлова +Comment[sr@ijekavian]=Подешавање општих поставки менаџера фајлова +Comment[sr@ijekavianlatin]=Podešavanje opštih postavki menadžera fajlova +Comment[sr@latin]=Podešavanje opštih postavki menadžera fajlova +Comment[sv]=Anpassa filhanterarens allmänna inställningar +Comment[tg]=Танзимоти умумии мудири файлҳо +Comment[th]=ปรับแต่งการตั้งค่าทั่วไปของเครื่องมือจัดการแฟ้ม +Comment[tr]=Genel dosya yöneticisi ayarlarını yapılandır +Comment[ug]=ئادەتتىكى ھۆججەت باشقۇرغۇچ تەڭشەك سەپلىمىسى +Comment[uk]=Налаштувати загальні параметри менеджера файлів +Comment[wa]=Apontyî les tchuzes djeneråles do manaedjeu di fitchîs +Comment[x-test]=xxConfigure general file manager settingsxx +Comment[zh_CN]=配置常规文件管理器设置 +Comment[zh_TW]=設定一般檔案管理員 +X-KDE-Keywords=file manager +X-KDE-Keywords[ar]=مدير الملفات +X-KDE-Keywords[bg]=преглед на файлове +X-KDE-Keywords[bs]=upravitelj datoteka +X-KDE-Keywords[ca]=gestor de fitxers +X-KDE-Keywords[ca@valencia]=gestor de fitxers +X-KDE-Keywords[cs]=správce souborů +X-KDE-Keywords[da]=filhåndtering +X-KDE-Keywords[de]=Dateimanager +X-KDE-Keywords[el]=διαχειριστής αρχείων +X-KDE-Keywords[en_GB]=file manager +X-KDE-Keywords[es]=gestor de archivos +X-KDE-Keywords[et]=failihaldur +X-KDE-Keywords[fi]=tiedostonhallinta +X-KDE-Keywords[fr]=gestionnaire de fichiers +X-KDE-Keywords[ga]=bainisteoir comhad +X-KDE-Keywords[gl]=xestor de ficheiros +X-KDE-Keywords[he]=מנהל קבצים +X-KDE-Keywords[hu]=fájlkezelő +X-KDE-Keywords[ia]=gerente de file +X-KDE-Keywords[id]=manajer berkas +X-KDE-Keywords[is]=skráastjóri +X-KDE-Keywords[it]=gestore dei file +X-KDE-Keywords[kk]=file manager +X-KDE-Keywords[km]=កម្មវិធី​គ្រប់គ្រង​ឯកសារ +X-KDE-Keywords[ko]=파일 관리자 +X-KDE-Keywords[lt]=Failų tvarkyklė +X-KDE-Keywords[lv]=failu pārvaldnieks +X-KDE-Keywords[mr]=फाईल व्यवस्थापक +X-KDE-Keywords[nb]=filbehandler +X-KDE-Keywords[nds]=Dateipleger +X-KDE-Keywords[nl]=bestandsbeheerder +X-KDE-Keywords[nn]=filhandsamar +X-KDE-Keywords[pa]=ਫਾਇਲ ਮੈਨੇਜਰ +X-KDE-Keywords[pl]=zarządzanie plikami +X-KDE-Keywords[pt]=gestor de ficheiros +X-KDE-Keywords[pt_BR]=gerenciador de arquivos +X-KDE-Keywords[ro]=gestionar de fișiere +X-KDE-Keywords[ru]=диспетчер файлов +X-KDE-Keywords[sk]=správca súborov +X-KDE-Keywords[sl]=upravljalnik datotek +X-KDE-Keywords[sr]=file manager,менаџер фајлова +X-KDE-Keywords[sr@ijekavian]=file manager,менаџер фајлова +X-KDE-Keywords[sr@ijekavianlatin]=file manager,menadžer fajlova +X-KDE-Keywords[sr@latin]=file manager,menadžer fajlova +X-KDE-Keywords[sv]=filhanterare +X-KDE-Keywords[tr]=dosya yönetici +X-KDE-Keywords[uk]=менеджер,керування,файл,файли +X-KDE-Keywords[wa]=manaedjeu di fitchîs +X-KDE-Keywords[x-test]=xxfile managerxx +X-KDE-Keywords[zh_CN]=file manager,文件管理器 +X-KDE-Keywords[zh_TW]=檔案管理員 diff --git a/dolphin/src/settings/kcm/kcmdolphingeneral.h b/dolphin/src/settings/kcm/kcmdolphingeneral.h new file mode 100644 index 00000000..6b844c9a --- /dev/null +++ b/dolphin/src/settings/kcm/kcmdolphingeneral.h @@ -0,0 +1,46 @@ +/*************************************************************************** + * Copyright (C) 2009 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef KCMDOLPHINGENERAL_H +#define KCMDOLPHINGENERAL_H + +#include +#include + +class SettingsPageBase; + +/** + * @brief Allow to configure general Dolphin settings. + */ +class DolphinGeneralConfigModule : public KCModule +{ + Q_OBJECT + +public: + DolphinGeneralConfigModule(QWidget* parent, const QVariantList& args); + virtual ~DolphinGeneralConfigModule(); + + virtual void save(); + virtual void defaults(); + +private: + QList m_pages; +}; + +#endif diff --git a/dolphin/src/settings/kcm/kcmdolphinnavigation.cpp b/dolphin/src/settings/kcm/kcmdolphinnavigation.cpp new file mode 100644 index 00000000..f79b80ec --- /dev/null +++ b/dolphin/src/settings/kcm/kcmdolphinnavigation.cpp @@ -0,0 +1,68 @@ +/*************************************************************************** + * Copyright (C) 2009 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "kcmdolphinnavigation.h" + +#include +#include +#include +#include +#include + +#include + +#include + +K_PLUGIN_FACTORY(KCMDolphinNavigationConfigFactory, registerPlugin("dolphinnavigation");) +K_EXPORT_PLUGIN(KCMDolphinNavigationConfigFactory("kcmdolphinnavigation")) + +DolphinNavigationConfigModule::DolphinNavigationConfigModule(QWidget* parent, const QVariantList& args) : + KCModule(KCMDolphinNavigationConfigFactory::componentData(), parent), + m_navigation(0) +{ + Q_UNUSED(args); + + KGlobal::locale()->insertCatalog("dolphin"); + + setButtons(KCModule::Default | KCModule::Help); + + QVBoxLayout* topLayout = new QVBoxLayout(this); + topLayout->setMargin(0); + topLayout->setSpacing(KDialog::spacingHint()); + + m_navigation = new NavigationSettingsPage(this); + connect(m_navigation, SIGNAL(changed()), this, SLOT(changed())); + topLayout->addWidget(m_navigation, 0, 0); +} + +DolphinNavigationConfigModule::~DolphinNavigationConfigModule() +{ +} + +void DolphinNavigationConfigModule::save() +{ + m_navigation->applySettings(); +} + +void DolphinNavigationConfigModule::defaults() +{ + m_navigation->restoreDefaults(); +} + +#include "moc_kcmdolphinnavigation.cpp" diff --git a/dolphin/src/settings/kcm/kcmdolphinnavigation.desktop b/dolphin/src/settings/kcm/kcmdolphinnavigation.desktop new file mode 100644 index 00000000..372df9ee --- /dev/null +++ b/dolphin/src/settings/kcm/kcmdolphinnavigation.desktop @@ -0,0 +1,348 @@ +Name=Dolphin Navigation +Name[ast]=Navegación de Dolphin +Name[bg]=Навигация в програмата +Name[bn]=ডলফিন ন্যাভিগেশন +Name[bs]=Delfinova navigacija +Name[ca]=Navegació del Dolphin +Name[ca@valencia]=Navegació del Dolphin +Name[cs]=Navigace Dolphinu +Name[csb]=Nawigacëjô Dolphina +Name[da]=Dolphin-navigation +Name[de]=Dolphin-Navigation +Name[el]=Πλοήγηση Dolphin +Name[en_GB]=Dolphin Navigation +Name[eo]=Foliumado de Dolphin +Name[es]=Navegación de Dolphin +Name[et]=Dolphini liikumine +Name[eu]=Dolphin nabigazioa +Name[fa]=ناوبری دلفین +Name[fi]=Dolphin – selaus +Name[fr]=Navigation avec Dolphin +Name[fy]=Dolfyn navigaasje +Name[ga]=Nascleanúint Dolphin +Name[gl]=Configuración da navegación de Dolphin +Name[gu]=ડોલ્ફિન શોધખોળ +Name[he]=הגדרות ניווט ב־Dolphin +Name[hi]=डॉल्फ़िन नेविगेशन +Name[hr]=Dolphinova navigacija +Name[hu]=Dolphin - Navigáció +Name[ia]=Navigation de Dolphin +Name[id]=Navigasi Dolphin +Name[is]=Dolphin stýringar +Name[it]=Navigazione di Dolphin +Name[ja]=Dolphin ナビゲーション +Name[kk]=Dolphin шарлауы +Name[km]=ការ​រុករក​របស់ Dolphin +Name[kn]=ಡಾಲ್ಫಿನ್ ನ್ಯಾವಿಗೇಶನ್ +Name[ko]=Dolphin 탐색 +Name[lt]=Dolphin navigacija +Name[lv]=Dolphin navigācija +Name[mai]=डाल्फिन संचरण +Name[mk]=Навигација во Делфин +Name[ml]=ഡോള്‍ഫിന്‍ നാവിഗേഷന്‍ +Name[mr]=डॉल्फिन संचारण +Name[ms]=Navigasi Dolphin +Name[nb]=Navigasjon i Dolphin +Name[nds]=Dolphin-Navigeren +Name[nl]=Dolphin-navigatie +Name[nn]=Navigasjon i Dolphin +Name[pa]=ਡਾਲਫਿਨ ਏਧਰ-ਓਧਰ +Name[pl]=Nawigacja Dolphina +Name[pt]=Navegação do Dolphin +Name[pt_BR]=Navegação do Dolphin +Name[ro]=Dolphin Navigare +Name[ru]=Навигация в Dolphin +Name[se]=Dolphinnavigašuvdna +Name[si]=Dolphin සැරිසැරුම +Name[sk]=Navigácia Dolphinu +Name[sl]=Dolphin - krmarjenje +Name[sr]=Делфинова навигација +Name[sr@ijekavian]=Делфинова навигација +Name[sr@ijekavianlatin]=Dolphinova navigacija +Name[sr@latin]=Dolphinova navigacija +Name[sv]=Dolphin navigering +Name[tg]=Идоракунии Dolphin +Name[th]=การนำทางของดอลฟิน +Name[tr]=Dolphin Gezintisi +Name[ug]=Dolphin يولباشچى +Name[uk]=«Навігація» Dolphin +Name[wa]=Naiviaedje di Dolphin +Name[x-test]=xxDolphin Navigationxx +Name[zh_CN]=Dolphin 导航 +Name[zh_TW]=Dolphin 導覽 +Comment=This service allows configuration of the Dolphin navigation. +Comment[ar]=هذه الخدمة تتيح التحكم بتصفح دولفين +Comment[ast]=Esti serviciu permítete configurar la navegación de Dolphin. +Comment[bg]=Това ви позволява да настроите навигацията в Dolphin. +Comment[bs]=Ovaj servis omogućava podešavanje navigacije u Delfinu. +Comment[ca]=Aquest servei permet la configuració de la navegació del Dolphin. +Comment[ca@valencia]=Este servei permet la configuració de la navegació del Dolphin. +Comment[cs]=Tato služba umožňuje nastavení navigace v Dolphinu. +Comment[csb]=Na ùsłëżnota pòzwôlô na kònfigùracëjã nawigacëji Dolphina, +Comment[da]=Denne tjeneste muliggør konfiguration af Dolphin-navigation. +Comment[de]=Mit diesem Dienst kann die Navigation für Dolphin eingerichtet werden. +Comment[el]=Αυτή η υπηρεσία επιτρέπει τη διαμόρφωση της πλοήγησης του του Dolphin. +Comment[en_GB]=This service allows configuration of the Dolphin navigation. +Comment[eo]=Ĉi tiu servo permesas agordi la Dolphin foliumadon. +Comment[es]=Este servicio le permite configurar la navegación de Dolphin. +Comment[et]=See teenus võimaldab seadistada Dolphini liikumist. +Comment[eu]=Zerbitzu honen bitartez Dolphin-en nabigazioa konfiguratu daiteke. +Comment[fi]=Tällä palvelulla voi muokata Dolphinin selausasetuksia. +Comment[fr]=Ce service permet de configurer la navigation avec Dolphin. +Comment[fy]=Mei dizze tsjinst kinne jo de dolfyn navigaasje ynstelle. +Comment[ga]=Leis an tseirbhís seo is féidir nascleanúint Dolphin a chumrú. +Comment[gl]=Este servizo permite modificar a configuración da navegación de Dolphin. +Comment[he]=שירות זה מאפשר להגדיר את הגדרות הניווט של Dolphin. +Comment[hi]=यह सेवा आपको डॉल्फ़िन नेविगेशन को कॉन्फ़िगर करने देता है. +Comment[hr]=Ova usluga dozvoljava konfiguraciju Dolphinove navigacije. +Comment[hu]=Ez a szolgáltatás a Dolphin navigálási beállításainak módosítását teszi lehetővé. +Comment[ia]=Iste servicio permitte le configuration del navigation de Dolphin. +Comment[id]=Layanan ini memungkinkan konfigurasi navigasi Dolphin. +Comment[is]=Þessi þjónusta leyfir stillingar á leiðarstýringum Dolphin. +Comment[it]=Questo servizio permette di configurare la navigazione con Dolphin. +Comment[ja]=Dolphin でのナビゲーションを設定します +Comment[kk]=Бұл Dolphin шарлауын баптауға мүмкіндік беретін қызмет. +Comment[km]=សេវា​នេះ​អនុញ្ញាត​ឲ្យ​កំណត់​រចនាសម្ព័ន្ធ​កា​ររុករក​​របស់ Dolphin ។ +Comment[kn]=ಈ ಸೇವೆಯು ಡಾಲ್ಫಿನ್ ನ್ಯಾವಿಗೇಶನ್‌ ಸಂರಚನೆಗೆ ಅನುವುಮಾಡಿಕೊಡುತ್ತದೆ. +Comment[ko]=이 서비스는 Dolphin 탐색을 설정합니다. +Comment[lt]=Ši tarnyba leidžia konfigūruoti Dolphin navigaciją. +Comment[lv]=Šis serviss ļauj konfigurēt Dolphin navigācijas iestatījumus. +Comment[mai]=ई सेवा अहाँकेँ डॉल्फिन संचरण केँ विन्यस्त करै दैछ +Comment[mk]=Овој сервис овозможува конфигурација на навигацијата во Делфин. +Comment[ml]=ഡോള്‍ഫിനകത്തു് നീങ്ങുന്നതു് ക്രമീകരിയ്ക്കാന്‍ ഈ സേവനം അനുവദിയ്ക്കുന്നു. +Comment[mr]=ही सेवा तुम्हाला डॉल्फिन संचारणाचे संयोजन करण्यास मदत करते. +Comment[ms]=Servis ini membenarkan tetapan bagi pengemudian Dolphin. +Comment[nb]=Denne tjenesten tilbyr oppsett av Dolphin-navigasjon +Comment[nds]=Mit dissen Deenst lett sik de Dolphin-Navigeren instellen. +Comment[nl]=Met deze dienst kunt u Dolphin-navigatie configureren. +Comment[nn]=Denne tenesta lèt deg setja opp navigasjonen for Dolphin. +Comment[pa]=ਇਹ ਸਰਵਿਸ ਡਾਲਫਿਨ ਨੇਵੀਗੇਸ਼ਨ ਦੀ ਸੰਰਚਨਾ ਵਾਸਤੇ ਹੈ। +Comment[pl]=Ta usługa umożliwia ustawienie nawigacji w Dolphinie. +Comment[pt]=Este serviço permite-lhe configurar a navegação do Dolphin. +Comment[pt_BR]=Este serviço permite configurar a navegação do Dolphin. +Comment[ro]=Acest serviciu permite configurarea navigării Dolphin. +Comment[ru]=Эта служба позволяет настраивать навигацию в Dolphin +Comment[si]=මෙම සේවාව Dolphin සැරිසැරුම සැකසීමට ඉඩ දේ. +Comment[sk]=Táto služba umožňuje nastavenie navigácie v Dolphine. +Comment[sl]=Ta storitev omogoča spreminjanje nastavitev krmarjenja programa Dolphin. +Comment[sr]=Овај сервис омогућава подешавање навигације у Делфину. +Comment[sr@ijekavian]=Овај сервис омогућава подешавање навигације у Делфину. +Comment[sr@ijekavianlatin]=Ovaj servis omogućava podešavanje navigacije u Dolphinu. +Comment[sr@latin]=Ovaj servis omogućava podešavanje navigacije u Dolphinu. +Comment[sv]=Den här tjänsten låter dig anpassa navigering i Dolphin. +Comment[tg]=Ин хидмат барои танзимоти идоракунии Dolphin иҷозат медиҳад. +Comment[th]=บริการนี้อนุญาตให้ทำการปรับแต่งการนำทางของดอลฟิน +Comment[tr]=Bu servis Dolphin gezintisini yapılandırmanızı sağlar. +Comment[ug]=بۇ مۇلازىمەت Dolphin نىڭ يولباشچىنى سەپلىشىڭىزگە يول قويىدۇ. +Comment[uk]=Ця служба надасть змогу налаштувати навігацію у Dolphin. +Comment[wa]=Ci siervice permet l' apontiaedje do naiviaedje di Dolhpin. +Comment[x-test]=xxThis service allows configuration of the Dolphin navigation.xx +Comment[zh_CN]=此服务允许您配置 Dolphin 的导航。 +Comment[zh_TW]=此服務允許設定 Dolphin 的導覽。 + +[Desktop Entry] +Icon=input-mouse +Type=Service +X-KDE-ServiceTypes=KCModule +Exec=kcmshell4 kcmdolphinnavigation + +X-KDE-Library=kcm_dolphinnavigation +X-KDE-PluginKeyword=dolphinnavigation +X-KDE-ParentApp=kcontrol +X-DocPath=dolphin/index.html#preferences-dialog-navigation +Name=Navigation +Name[ar]=التصفح +Name[ast]=Navegación +Name[bg]=Навигация +Name[bn]=ন্যাভিগেশন +Name[bs]=Navigacija +Name[ca]=Navegació +Name[ca@valencia]=Navegació +Name[cs]=Navigace +Name[csb]=Nawigacëjô +Name[da]=Navigation +Name[de]=Navigation +Name[el]=Πλοήγηση +Name[en_GB]=Navigation +Name[eo]=Navigado +Name[es]=Navegación +Name[et]=Liikumine +Name[eu]=Nabigazioa +Name[fi]=Selaus +Name[fr]=Navigation +Name[fy]=Navigaasje +Name[ga]=Nascleanúint +Name[gl]=Navegación +Name[gu]=શોધખોળ +Name[he]=ניווט +Name[hi]=नेविगेशन +Name[hr]=Navigacija +Name[hu]=Navigáció +Name[ia]=Navigation +Name[id]=Navigasi +Name[is]=Leiðarstýring +Name[it]=Navigazione +Name[ja]=ナビゲーション +Name[ka]=ნავიგაცია +Name[kk]=Шарлау +Name[km]=ការ​​​​រុករក +Name[kn]=ನ್ಯಾವಿಗೇಶನ್ +Name[ko]=탐색 +Name[lt]=Navigacija +Name[lv]=Navigācija +Name[mai]=नेविगेशन +Name[mk]=Навигација +Name[ml]=നാവിഗേഷന്‍ +Name[mr]=संचारण +Name[ms]=Navigasi +Name[nb]=Navigasjon +Name[nds]=Navigeren +Name[nl]=Navigatie +Name[nn]=Navigasjon +Name[pa]=ਨੇਵੀਗੇਸ਼ਨ +Name[pl]=Nawigacja +Name[pt]=Navegação +Name[pt_BR]=Navegação +Name[ro]=Navigare +Name[ru]=Навигация +Name[se]=Navigašuvdna +Name[si]=සැරිසැරුම +Name[sk]=Navigácia +Name[sl]=Krmarjenje +Name[sr]=Навигација +Name[sr@ijekavian]=Навигација +Name[sr@ijekavianlatin]=Navigacija +Name[sr@latin]=Navigacija +Name[sv]=Navigering +Name[tg]=Идоракунӣ +Name[th]=การนำทาง +Name[tr]=Gezinti +Name[ug]=يولباشچى +Name[uk]=Навігація +Name[wa]=Naiviaedje +Name[x-test]=xxNavigationxx +Name[zh_CN]=导航 +Name[zh_TW]=導覽 +Comment=Configure file manager navigation +Comment[ar]=اضبط تصفح مدير الملفات +Comment[ast]=Configurar les preferencies de navegación del xestor de ficheros +Comment[bg]=Настройване навигацията във файловия мениджър +Comment[bn]=ফাইল ম্যানেজার ন্যাভিগেশন কনফিগার করুন +Comment[bs]=Podešavanje navigacije u menadžeru datoteka +Comment[ca]=Configura la navegació del gestor de fitxers +Comment[ca@valencia]=Configura la navegació del gestor de fitxers +Comment[cs]=Nastavení navigace správce souborů +Comment[csb]=Kònfigùracëjô nawigacëji menadżera lopków +Comment[da]=Indstil navigation i filhåndtering +Comment[de]=Einstellungen zur Navigation mit dem Dateimanager +Comment[el]=Διαμόρφωση πλοήγησης διαχείρισης αρχείων +Comment[en_GB]=Configure file manager navigation +Comment[eo]=Agordi la dosieradministrilan foliumadon +Comment[es]=Configurar las preferencias de navegación del gestor de archivos +Comment[et]=Failihalduri liikumise seadistamine +Comment[eu]=Konfiguratu fitxategi-kudeatzailearen nabigazioa +Comment[fi]=Tiedostonhallinnan selausasetukset +Comment[fr]=Configuration la navigation avec le gestionnaire de fichiers +Comment[fy]=Triembehearnavigaasje ynstelle +Comment[ga]=Cumraigh nascleanúint bhainisteoir na gcomhad +Comment[gl]=Configura as navegación co xestor de ficheiros +Comment[he]=הגדרת הגדרות ניווט של מנהל הקבצים +Comment[hi]=फ़ाइल प्रबंधक नेविगेशन कॉन्फ़िगर करें +Comment[hr]=Podešavanje postavki navigacije upravitelja datoteka +Comment[hu]=A fájlkezelő navigálási beállításai +Comment[ia]=Configura navigation del gerente de file +Comment[id]=Atur navigasi manajer berkas +Comment[is]=Stilla leiðarstýringu í skráastjóra +Comment[it]=Configura la navigazione col gestore dei file +Comment[ja]=ファイルマネージャでのナビゲーションを設定します +Comment[kk]=Файл менеджердің шарлауын баптау +Comment[km]=កំណត់​រចនាសម្ព័ន្ធការ​រុករក​កម្មវិធី​គ្រប់គ្រង​ឯកសារ +Comment[kn]=ಕಡತ ವ್ಯವಸ್ಥಾಪಕದ ನೇವಿಗೇಶನ್‌ ಅನ್ನು ಸಂರಚಿಸಿ +Comment[ko]=파일 관리자 탐색 설정 +Comment[lt]=Konfigūruokite failų tvarkyklės navigaciją +Comment[lv]=Konfigurēē failu pārvaldnieka navigāciju +Comment[mai]=फाइलक प्रबंधक संचरणकेँ बिन्यस्त करू +Comment[mk]=Конфигурирајте ја навигацијата за менаџерот на датотеки +Comment[ml]=ഫയല്‍ നടത്തിപ്പുകാരന്റെ നീക്കങ്ങള്‍ ക്രമീകരിയ്ക്കുക +Comment[mr]=फाईल व्यवस्थापक संचारण संयोजीत करा +Comment[ms]=Selaraskan tetapan pengurus fail +Comment[nb]=Sett opp navigasjon i filbehandleren +Comment[nds]=De Dateipleger-Navigeren instellen +Comment[nl]=Bestandsbeheerdernavigatie configureren +Comment[nn]=Set opp navigasjonen i filhandsamaren +Comment[pa]=ਫਾਇਲ ਮੈਨੇਜਰ ਨੇਵੀਗੇਸ਼ਨ ਸੰਰਚਨਾ +Comment[pl]=Ustawienia nawigacji w zarządzaniu plikami +Comment[pt]=Configurar a navegação do gestor de ficheiros +Comment[pt_BR]=Configura a navegação do gerenciador de arquivos +Comment[ro]=Configurează navigarea gestionarului de fișiere +Comment[ru]=Настройка навигации в диспетчере файлов +Comment[se]=Heivet fiilagieđahallannavigašuvdna +Comment[si]=ගොනු කළමණාකරන සැරිසැරුම සැකසීම +Comment[sk]=Nastavenie navigácie správcu súborov +Comment[sl]=Nastavitve krmarjenja v upravljalniku datotek +Comment[sr]=Подешавање навигације у менаџеру фајлова +Comment[sr@ijekavian]=Подешавање навигације у менаџеру фајлова +Comment[sr@ijekavianlatin]=Podešavanje navigacije u menadžeru fajlova +Comment[sr@latin]=Podešavanje navigacije u menadžeru fajlova +Comment[sv]=Anpassa filhanterarens navigering +Comment[tg]=Танзимоти идоракунии мудири файлҳо +Comment[th]=ปรับแต่งการนำทางแบบต่าง ๆ ของเครื่องมือจัดการแฟ้ม +Comment[tr]=Dosya yöneticisi gezintisini yapılandır +Comment[ug]=ھۆججەت باشقۇرغۇچ يولباشچى سەپلىمىسى +Comment[uk]=Налаштувати навігацію у менеджері файлів +Comment[wa]=Apontyî li naiviaedje do manaedjeu di fitchîs +Comment[x-test]=xxConfigure file manager navigationxx +Comment[zh_CN]=配置文件管理器导航 +Comment[zh_TW]=設定檔案管理員導覽 +X-KDE-Keywords=file manager +X-KDE-Keywords[ar]=مدير الملفات +X-KDE-Keywords[bg]=преглед на файлове +X-KDE-Keywords[bs]=upravitelj datoteka +X-KDE-Keywords[ca]=gestor de fitxers +X-KDE-Keywords[ca@valencia]=gestor de fitxers +X-KDE-Keywords[cs]=správce souborů +X-KDE-Keywords[da]=filhåndtering +X-KDE-Keywords[de]=Dateimanager +X-KDE-Keywords[el]=διαχειριστής αρχείων +X-KDE-Keywords[en_GB]=file manager +X-KDE-Keywords[es]=gestor de archivos +X-KDE-Keywords[et]=failihaldur +X-KDE-Keywords[fi]=tiedostonhallinta +X-KDE-Keywords[fr]=gestionnaire de fichiers +X-KDE-Keywords[ga]=bainisteoir comhad +X-KDE-Keywords[gl]=xestor de ficheiros +X-KDE-Keywords[he]=מנהל קבצים +X-KDE-Keywords[hu]=fájlkezelő +X-KDE-Keywords[ia]=gerente de file +X-KDE-Keywords[id]=manajer berkas +X-KDE-Keywords[is]=skráastjóri +X-KDE-Keywords[it]=gestore dei file +X-KDE-Keywords[kk]=file manager +X-KDE-Keywords[km]=កម្មវិធី​គ្រប់គ្រង​ឯកសារ +X-KDE-Keywords[ko]=파일 관리자 +X-KDE-Keywords[lt]=Failų tvarkyklė +X-KDE-Keywords[lv]=failu pārvaldnieks +X-KDE-Keywords[mr]=फाईल व्यवस्थापक +X-KDE-Keywords[nb]=filbehandler +X-KDE-Keywords[nds]=Dateipleger +X-KDE-Keywords[nl]=bestandsbeheerder +X-KDE-Keywords[nn]=filhandsamar +X-KDE-Keywords[pa]=ਫਾਇਲ ਮੈਨੇਜਰ +X-KDE-Keywords[pl]=zarządzanie plikami +X-KDE-Keywords[pt]=gestor de ficheiros +X-KDE-Keywords[pt_BR]=gerenciador de arquivos +X-KDE-Keywords[ro]=gestionar de fișiere +X-KDE-Keywords[ru]=диспетчер файлов +X-KDE-Keywords[sk]=správca súborov +X-KDE-Keywords[sl]=upravljalnik datotek +X-KDE-Keywords[sr]=file manager,менаџер фајлова +X-KDE-Keywords[sr@ijekavian]=file manager,менаџер фајлова +X-KDE-Keywords[sr@ijekavianlatin]=file manager,menadžer fajlova +X-KDE-Keywords[sr@latin]=file manager,menadžer fajlova +X-KDE-Keywords[sv]=filhanterare +X-KDE-Keywords[tr]=dosya yönetici +X-KDE-Keywords[uk]=менеджер,керування,файл,файли +X-KDE-Keywords[wa]=manaedjeu di fitchîs +X-KDE-Keywords[x-test]=xxfile managerxx +X-KDE-Keywords[zh_CN]=file manager,文件管理器 +X-KDE-Keywords[zh_TW]=檔案管理員 diff --git a/dolphin/src/settings/kcm/kcmdolphinnavigation.h b/dolphin/src/settings/kcm/kcmdolphinnavigation.h new file mode 100644 index 00000000..e7634d66 --- /dev/null +++ b/dolphin/src/settings/kcm/kcmdolphinnavigation.h @@ -0,0 +1,45 @@ +/*************************************************************************** + * Copyright (C) 2009 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef KCMDOLPHINNAVIGATION_H +#define KCMDOLPHINNAVIGATION_H + +#include + +class NavigationSettingsPage; + +/** + * @brief Allow to configure the Dolphin navigation. + */ +class DolphinNavigationConfigModule : public KCModule +{ + Q_OBJECT + +public: + DolphinNavigationConfigModule(QWidget* parent, const QVariantList& args); + virtual ~DolphinNavigationConfigModule(); + + virtual void save(); + virtual void defaults(); + +private: + NavigationSettingsPage* m_navigation; +}; + +#endif diff --git a/dolphin/src/settings/kcm/kcmdolphinservices.cpp b/dolphin/src/settings/kcm/kcmdolphinservices.cpp new file mode 100644 index 00000000..cbf9fbc5 --- /dev/null +++ b/dolphin/src/settings/kcm/kcmdolphinservices.cpp @@ -0,0 +1,68 @@ +/*************************************************************************** + * Copyright (C) 2009 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "kcmdolphinservices.h" + +#include +#include +#include +#include +#include + +#include + +#include + +K_PLUGIN_FACTORY(KCMDolphinServicesConfigFactory, registerPlugin("dolphinservices");) +K_EXPORT_PLUGIN(KCMDolphinServicesConfigFactory("kcmdolphinservices")) + +DolphinServicesConfigModule::DolphinServicesConfigModule(QWidget* parent, const QVariantList& args) : + KCModule(KCMDolphinServicesConfigFactory::componentData(), parent), + m_services(0) +{ + Q_UNUSED(args); + + KGlobal::locale()->insertCatalog("dolphin"); + + setButtons(KCModule::Default | KCModule::Help); + + QVBoxLayout* topLayout = new QVBoxLayout(this); + topLayout->setMargin(0); + topLayout->setSpacing(KDialog::spacingHint()); + + m_services = new ServicesSettingsPage(this); + connect(m_services, SIGNAL(changed()), this, SLOT(changed())); + topLayout->addWidget(m_services, 0, 0); +} + +DolphinServicesConfigModule::~DolphinServicesConfigModule() +{ +} + +void DolphinServicesConfigModule::save() +{ + m_services->applySettings(); +} + +void DolphinServicesConfigModule::defaults() +{ + m_services->restoreDefaults(); +} + +#include "moc_kcmdolphinservices.cpp" diff --git a/dolphin/src/settings/kcm/kcmdolphinservices.desktop b/dolphin/src/settings/kcm/kcmdolphinservices.desktop new file mode 100644 index 00000000..81d3ce38 --- /dev/null +++ b/dolphin/src/settings/kcm/kcmdolphinservices.desktop @@ -0,0 +1,298 @@ +Name=Dolphin Services +Name[ar]=خدمات دولفين +Name[ast]=Servicios de Dolphin +Name[bg]=Услуги в Dolphin +Name[bn]=ডলফিন সার্ভিসসমূহ +Name[bs]=Delfinovi servisi +Name[ca]=Serveis del Dolphin +Name[ca@valencia]=Serveis del Dolphin +Name[cs]=Služby Dolphinu +Name[csb]=Ùsłëżnotë Dolphina +Name[da]=Dolphin-tjenester +Name[de]=Dolphin-Dienste +Name[el]=Υπηρεσίες Dolphin +Name[en_GB]=Dolphin Services +Name[eo]=Dolphin-servoj +Name[es]=Servicios de Dolphin +Name[et]=Dolphini teenused +Name[eu]=Dolphin zerbitzuak +Name[fi]=Dolphin – palvelut +Name[fr]=Services de Dolphin +Name[fy]=Dolfyn tsjinsten +Name[ga]=Seirbhísí Dolphin +Name[gl]=Servizos de Dolphin +Name[gu]=ડોલ્ફિન સેવાઓ +Name[he]=השירותים של Dolphin +Name[hi]=डॉल्फ़िन सेवाएँ +Name[hr]=Dolphinove usluge +Name[hu]=Dolphin - Szolgáltatások +Name[ia]=Servicios de Dolphin +Name[id]=Layanan Dolphin +Name[is]=Dolphin þjónustur +Name[it]=Servizi di Dolphin +Name[ja]=Dolphin サービス +Name[kk]=Dolphin қызметтері +Name[km]=សេវា​ Dolphin +Name[kn]=ಡಾಲ್ಫಿನ್ ಸೇವೆಗಳು +Name[ko]=Dolphin 서비스 +Name[lt]=Dolphin tarnybos +Name[lv]=Dolphin servisi +Name[mai]=डाल्फिन सेवा +Name[mk]=Сервиси во Делфин +Name[ml]=ഡോള്‍ഫിന്‍ സേവനങ്ങള്‍ +Name[mr]=डॉल्फिन सेवा +Name[ms]=Servis Dolphin +Name[nb]=Dolphin-tjenester +Name[nds]=Dolphin-Deensten +Name[nl]=Dolphin-services +Name[nn]=Dolphin-tenester +Name[pa]=ਡਾਲਫਿਨ ਸਰਵਿਸਾਂ +Name[pl]=Usługi Dolphina +Name[pt]=Serviços do Dolphin +Name[pt_BR]=Serviços do Dolphin +Name[ro]=Servicii Dolphin +Name[ru]=Действия Dolphin +Name[si]=Dolphin සේවා +Name[sk]=Služby Dolphinu +Name[sl]=Dolphin - storitve +Name[sr]=Делфинови сервиси +Name[sr@ijekavian]=Делфинови сервиси +Name[sr@ijekavianlatin]=Dolphinovi servisi +Name[sr@latin]=Dolphinovi servisi +Name[sv]=Dolphin-tjänster +Name[tg]=Хизматҳои Dolphin +Name[th]=บริการต่าง ๆ ของดอลฟิน +Name[tr]=Dolphin Servisleri +Name[ug]=Dolphin مۇلازىمەتلىرى +Name[uk]=Служби Dolphin +Name[wa]=Siervices di Dolphin +Name[x-test]=xxDolphin Servicesxx +Name[zh_CN]=Dolphin 服务 +Name[zh_TW]=Dolphin 服務 + +[Desktop Entry] +Icon=services +Type=Service +X-KDE-ServiceTypes=KCModule +Exec=kcmshell4 kcmdolphinservices + +X-KDE-Library=kcm_dolphinservices +X-KDE-PluginKeyword=dolphinservices +X-KDE-ParentApp=kcontrol +X-DocPath=dolphin/index.html#preferences-dialog-services +Name=Services +Name[af]=Dienste +Name[ar]=الخدمات +Name[as]=সেৱা +Name[ast]=Servicios +Name[be]=Сервісы +Name[be@latin]=Słužby +Name[bg]=Услуги +Name[bn]=সার্ভিসসমূহ +Name[bn_IN]=পরিসেবা +Name[br]=Servijoù +Name[bs]=Servisi +Name[ca]=Serveis +Name[ca@valencia]=Serveis +Name[cs]=Služby +Name[csb]=Ùsłëżnotë +Name[cy]=Gwasanaethau +Name[da]=Tjenester +Name[de]=KDE-Dienste +Name[el]=Υπηρεσίες +Name[en_GB]=Services +Name[eo]=Servoj +Name[es]=Servicios +Name[et]=Teenused +Name[eu]=Zerbitzuak +Name[fa]=خدمات +Name[fi]=Palvelut +Name[fr]=Services +Name[fy]=Tsjinsten +Name[ga]=Seirbhísí +Name[gl]=Servizos +Name[gu]=સેવાઓ +Name[he]=שירותים +Name[hi]=सेवाएँ +Name[hne]=सेवा +Name[hr]=Usluge +Name[hsb]=Słužby +Name[hu]=Szolgáltatások +Name[ia]=Servicios +Name[id]=Layanan +Name[is]=Þjónustur +Name[it]=Servizi +Name[ja]=サービス +Name[ka]=სერვისები +Name[kk]=Қызметтер +Name[km]=សេវា +Name[kn]=ಸೇವೆಗಳು +Name[ko]=서비스 +Name[ku]=Xizmet +Name[lt]=Tarnybos +Name[lv]=Servisi +Name[mai]=सेवासभ +Name[mk]=Сервиси +Name[ml]=സേവനങ്ങള്‍ +Name[mr]=सेवा +Name[ms]=Servis +Name[nb]=Tjenester +Name[nds]=KDE-Deensten +Name[ne]=सेवा +Name[nl]=Services +Name[nn]=Tenester +Name[oc]=Servicis +Name[or]=ସର୍ଭିସଗୁଡ଼ିକ +Name[pa]=ਸਰਵਿਸਾਂ +Name[pl]=Usługi +Name[pt]=Serviços +Name[pt_BR]=Serviços +Name[ro]=Servicii +Name[ru]=Действия +Name[se]=Bálvalusat +Name[si]=සේවා +Name[sk]=Služby +Name[sl]=Storitve +Name[sr]=Сервиси +Name[sr@ijekavian]=Сервиси +Name[sr@ijekavianlatin]=Servisi +Name[sr@latin]=Servisi +Name[sv]=Tjänster +Name[ta]=சேவைகள் +Name[te]=సేవలు +Name[tg]=Хизматҳо +Name[th]=บริการต่าง ๆ +Name[tr]=Servisler +Name[ug]=مۇلازىمەتلەر +Name[uk]=Служби +Name[uz]=Xizmatlar +Name[uz@cyrillic]=Хизматлар +Name[vi]=Dịch vụ +Name[wa]=Siervices +Name[xh]=Iinkonzo +Name[x-test]=xxServicesxx +Name[zh_CN]=服务 +Name[zh_TW]=服務 +Comment=Configure file manager services +Comment[ar]=اضبط خدمات مدير الملفات +Comment[ast]=Configurar les preferencies del xestor de ficheros +Comment[bg]=Настройване услугите на файловия мениджър +Comment[bn]=ফাইল ম্যানেজার সার্ভিসসমূহ কনফিগার করুন +Comment[bs]=Podešavanje servisa menadžera datoteka +Comment[ca]=Configura els serveis del gestor de fitxers +Comment[ca@valencia]=Configura els serveis del gestor de fitxers +Comment[cs]=Nastavení služeb správce souborů +Comment[csb]=Kònfigùracëjô ùsłëżnotów menadżera lopków +Comment[da]=Indstil filhåndteringstjenester +Comment[de]=Dateimanager-Dienste einrichten +Comment[el]=Διαμόρφωση υπηρεσιών διαχείρισης αρχείων +Comment[en_GB]=Configure file manager services +Comment[eo]=Agordi la dosieradministrilajn servojn +Comment[es]=Configurar las preferencias del gestor de archivos +Comment[et]=Failihalduri teenuste seadistamine +Comment[eu]=Konfiguratu fitxategi-kudeatzaile zerbitzuak +Comment[fi]=Tiedostonhallinnan palveluasetukset +Comment[fr]=Configuration des services du gestionnaire de fichiers +Comment[fy]=Triembeheartsjinsten ynstelle +Comment[ga]=Cumraigh seirbhísí bhainisteoir na gcomhad +Comment[gl]=Configura os servizos do xestor de ficheiros +Comment[gu]=ફાઇલ વ્યવસ્થાપક સેવાઓ રૂપરેખાંકિત કરો +Comment[he]=הגדרת שירותי מנהל הקבצים +Comment[hi]=फ़ाइल प्रबंधक सेवाएँ कॉन्फ़िगर करें +Comment[hr]=Podešavanje postavki usluga upravitelja datoteka +Comment[hu]=A fájlkezelő szolgáltatások beállításai +Comment[ia]=Configura servicios del gerente de file +Comment[id]=Atur layanan manajer berkas +Comment[is]=Stilla þjónustur skráastjóra +Comment[it]=Configura i servizi del gestore dei file +Comment[ja]=ファイルマネージャのサービスを設定します +Comment[kk]=Файл менеджердің қызметтерін баптау +Comment[km]=កំណត់​រចនាសម្ព័ន្ធ​សេវា​របស់​កម្មវិធី​គ្រប់គ្រង​ឯកសារ +Comment[kn]=ಕಡತ ವ್ಯವಸ್ಥಾಪಕದ ಸೇವೆಗಳನ್ನು ಸಂರಚಿಸಿ +Comment[ko]=파일 관리자 서비스 설정 +Comment[lt]=Konfigūruokite failų tvarkyklės tarnybas +Comment[lv]=Konfigurē failu pārvaldnieka servisus +Comment[mai]=फाइलक प्रबंधकक सेवा बिन्यस्त करू +Comment[mk]=Конфигурирајте ги сервисите за менаџерот на датотеки +Comment[ml]=ഫയല്‍ നടത്തിപ്പുകാരന്റെ സേവനങ്ങള്‍ ക്രമീകരിയ്ക്കുക +Comment[mr]=फाईल व्यवस्थापक सेवा संयोजीत करा +Comment[ms]=Selaraskan tetapan pengurus fail +Comment[nb]=Sett opp tjenester i filbehandleren +Comment[nds]=Dateipleger-Deensten instellen +Comment[nl]=Bestandsbeheerderservices configureren +Comment[nn]=Set opp tenester i filhandsamaren +Comment[pa]=ਫਾਇਲ ਮੈਨੇਜਰ ਸਰਵਿਸ ਸੰਰਚਨਾ +Comment[pl]=Ustawienia usług zarządzania plikami +Comment[pt]=Configurar os serviços do gestor de ficheiros +Comment[pt_BR]=Configura os serviços do gerenciador de arquivos +Comment[ro]=Configurează serviciile gestionarului de fișiere +Comment[ru]=Настройка действий в диспетчере файлов +Comment[si]=ගොනු කළමණාකරුගේ සේවා සකසන්න +Comment[sk]=Nastavenie služieb správcu súborov +Comment[sl]=Nastavitve storitev upravljalnika datotek +Comment[sr]=Подешавање сервиса менаџера фајлова +Comment[sr@ijekavian]=Подешавање сервиса менаџера фајлова +Comment[sr@ijekavianlatin]=Podešavanje servisa menadžera fajlova +Comment[sr@latin]=Podešavanje servisa menadžera fajlova +Comment[sv]=Anpassa filhanterarens tjänster +Comment[tg]=Танзимоти хизматҳои мудири файлҳо +Comment[th]=ปรับแต่งบริการต่าง ๆ ของเครื่องมือจัดการแฟ้ม +Comment[tr]=Dosya yöneticisi servislerini yapılandır +Comment[ug]=ھۆججەت باشقۇرغۇچ مۇلازىمەت سەپلىمىسى +Comment[uk]=Налаштувати служби менеджера файлів +Comment[wa]=Apontyî les siervices do manaedjeu di fitchîs +Comment[x-test]=xxConfigure file manager servicesxx +Comment[zh_CN]=配置文件管理器服务 +Comment[zh_TW]=設定檔案管理員服務 +X-KDE-Keywords=file manager +X-KDE-Keywords[ar]=مدير الملفات +X-KDE-Keywords[bg]=преглед на файлове +X-KDE-Keywords[bs]=upravitelj datoteka +X-KDE-Keywords[ca]=gestor de fitxers +X-KDE-Keywords[ca@valencia]=gestor de fitxers +X-KDE-Keywords[cs]=správce souborů +X-KDE-Keywords[da]=filhåndtering +X-KDE-Keywords[de]=Dateimanager +X-KDE-Keywords[el]=διαχειριστής αρχείων +X-KDE-Keywords[en_GB]=file manager +X-KDE-Keywords[es]=gestor de archivos +X-KDE-Keywords[et]=failihaldur +X-KDE-Keywords[fi]=tiedostonhallinta +X-KDE-Keywords[fr]=gestionnaire de fichiers +X-KDE-Keywords[ga]=bainisteoir comhad +X-KDE-Keywords[gl]=xestor de ficheiros +X-KDE-Keywords[he]=מנהל קבצים +X-KDE-Keywords[hu]=fájlkezelő +X-KDE-Keywords[ia]=gerente de file +X-KDE-Keywords[id]=manajer berkas +X-KDE-Keywords[is]=skráastjóri +X-KDE-Keywords[it]=gestore dei file +X-KDE-Keywords[kk]=file manager +X-KDE-Keywords[km]=កម្មវិធី​គ្រប់គ្រង​ឯកសារ +X-KDE-Keywords[ko]=파일 관리자 +X-KDE-Keywords[lt]=Failų tvarkyklė +X-KDE-Keywords[lv]=failu pārvaldnieks +X-KDE-Keywords[mr]=फाईल व्यवस्थापक +X-KDE-Keywords[nb]=filbehandler +X-KDE-Keywords[nds]=Dateipleger +X-KDE-Keywords[nl]=bestandsbeheerder +X-KDE-Keywords[nn]=filhandsamar +X-KDE-Keywords[pa]=ਫਾਇਲ ਮੈਨੇਜਰ +X-KDE-Keywords[pl]=zarządzanie plikami +X-KDE-Keywords[pt]=gestor de ficheiros +X-KDE-Keywords[pt_BR]=gerenciador de arquivos +X-KDE-Keywords[ro]=gestionar de fișiere +X-KDE-Keywords[ru]=диспетчер файлов +X-KDE-Keywords[sk]=správca súborov +X-KDE-Keywords[sl]=upravljalnik datotek +X-KDE-Keywords[sr]=file manager,менаџер фајлова +X-KDE-Keywords[sr@ijekavian]=file manager,менаџер фајлова +X-KDE-Keywords[sr@ijekavianlatin]=file manager,menadžer fajlova +X-KDE-Keywords[sr@latin]=file manager,menadžer fajlova +X-KDE-Keywords[sv]=filhanterare +X-KDE-Keywords[tr]=dosya yönetici +X-KDE-Keywords[uk]=менеджер,керування,файл,файли +X-KDE-Keywords[wa]=manaedjeu di fitchîs +X-KDE-Keywords[x-test]=xxfile managerxx +X-KDE-Keywords[zh_CN]=file manager,文件管理器 +X-KDE-Keywords[zh_TW]=檔案管理員 diff --git a/dolphin/src/settings/kcm/kcmdolphinservices.h b/dolphin/src/settings/kcm/kcmdolphinservices.h new file mode 100644 index 00000000..70ed366f --- /dev/null +++ b/dolphin/src/settings/kcm/kcmdolphinservices.h @@ -0,0 +1,45 @@ +/*************************************************************************** + * Copyright (C) 2009 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef KCMDOLPHINSERVICES_H +#define KCMDOLPHINSERVICES_H + +#include + +class ServicesSettingsPage; + +/** + * @brief Allow to configure the Dolphin services. + */ +class DolphinServicesConfigModule : public KCModule +{ + Q_OBJECT + +public: + DolphinServicesConfigModule(QWidget* parent, const QVariantList& args); + virtual ~DolphinServicesConfigModule(); + + virtual void save(); + virtual void defaults(); + +private: + ServicesSettingsPage* m_services; +}; + +#endif diff --git a/dolphin/src/settings/kcm/kcmdolphinviewmodes.cpp b/dolphin/src/settings/kcm/kcmdolphinviewmodes.cpp new file mode 100644 index 00000000..a64a1fe9 --- /dev/null +++ b/dolphin/src/settings/kcm/kcmdolphinviewmodes.cpp @@ -0,0 +1,109 @@ +/*************************************************************************** + * Copyright (C) 2008 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "kcmdolphinviewmodes.h" + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +K_PLUGIN_FACTORY(KCMDolphinViewModesConfigFactory, registerPlugin("dolphinviewmodes");) +K_EXPORT_PLUGIN(KCMDolphinViewModesConfigFactory("kcmdolphinviewmodes")) + +DolphinViewModesConfigModule::DolphinViewModesConfigModule(QWidget* parent, const QVariantList& args) : + KCModule(KCMDolphinViewModesConfigFactory::componentData(), parent), + m_tabs() +{ + Q_UNUSED(args); + + KGlobal::locale()->insertCatalog("dolphin"); + + setButtons(KCModule::Default | KCModule::Help); + + QVBoxLayout* topLayout = new QVBoxLayout(this); + topLayout->setMargin(0); + topLayout->setSpacing(KDialog::spacingHint()); + + KTabWidget* tabWidget = new KTabWidget(this); + + // Initialize 'Icons' tab + ViewSettingsTab* iconsTab = new ViewSettingsTab(ViewSettingsTab::IconsMode, tabWidget); + tabWidget->addTab(iconsTab, KIcon("view-list-icons"), i18nc("@title:tab", "Icons")); + connect(iconsTab, SIGNAL(changed()), this, SLOT(viewModeChanged())); + + // Initialize 'Compact' tab + ViewSettingsTab* compactTab = new ViewSettingsTab(ViewSettingsTab::CompactMode, tabWidget); + tabWidget->addTab(compactTab, KIcon("view-list-details"), i18nc("@title:tab", "Compact")); + connect(compactTab, SIGNAL(changed()), this, SLOT(viewModeChanged())); + + // Initialize 'Details' tab + ViewSettingsTab* detailsTab = new ViewSettingsTab(ViewSettingsTab::DetailsMode, tabWidget); + tabWidget->addTab(detailsTab, KIcon("view-list-tree"), i18nc("@title:tab", "Details")); + connect(detailsTab, SIGNAL(changed()), this, SLOT(viewModeChanged())); + + m_tabs.append(iconsTab); + m_tabs.append(compactTab); + m_tabs.append(detailsTab); + + topLayout->addWidget(tabWidget, 0, 0); +} + +DolphinViewModesConfigModule::~DolphinViewModesConfigModule() +{ +} + +void DolphinViewModesConfigModule::save() +{ + foreach (ViewSettingsTab* tab, m_tabs) { + tab->applySettings(); + } + reparseConfiguration(); +} + +void DolphinViewModesConfigModule::defaults() +{ + foreach (ViewSettingsTab* tab, m_tabs) { + tab->restoreDefaultSettings(); + } + reparseConfiguration(); +} + +void DolphinViewModesConfigModule::reparseConfiguration() +{ + QDBusMessage message = QDBusMessage::createSignal("/KonqMain", "org.kde.Konqueror.Main", "reparseConfiguration"); + QDBusConnection::sessionBus().send(message); +} + +void DolphinViewModesConfigModule::viewModeChanged() +{ + emit changed(true); +} + +#include "moc_kcmdolphinviewmodes.cpp" diff --git a/dolphin/src/settings/kcm/kcmdolphinviewmodes.desktop b/dolphin/src/settings/kcm/kcmdolphinviewmodes.desktop new file mode 100644 index 00000000..f5f1fbd2 --- /dev/null +++ b/dolphin/src/settings/kcm/kcmdolphinviewmodes.desktop @@ -0,0 +1,347 @@ +Name=Dolphin View Modes +Name[ar]=أنماط عرض دولفين +Name[ast]=Moos de vista de Dolphin +Name[bg]=Режими на преглед +Name[bn]=ডলফিন ভিউ মোড +Name[bs]=Delfinovi režimi prikaza +Name[ca]=Modes de vista del Dolphin +Name[ca@valencia]=Modes de vista del Dolphin +Name[cs]=Režimy pohledů Dolphinu +Name[csb]=Ôrtë wëzdrzatkù Dolphina +Name[da]=Dolphins visningstilstande +Name[de]=Dolphin-Ansichtsmodi +Name[el]=Λειτουργίες προβολής Dolphin +Name[en_GB]=Dolphin View Modes +Name[eo]=Dolphin-rigardaj stiloj +Name[es]=Modos de vista de Dolphin +Name[et]=Dolphini vaaterežiimid +Name[eu]=Dolphin-en ikuspegi moduak +Name[fi]=Dolphin – näkymät +Name[fr]=Modes d'affichage de Dolphin +Name[fy]=Dolfyn werjeftemodus +Name[ga]=Móid Amhairc Dolphin +Name[gl]=Modos de vista de Dolphin +Name[gu]=ડોલ્ફિન દેખાવ સ્થિતિઓ +Name[he]=אפשרויות תצוגה ב־Dolphin +Name[hi]=डॉल्फ़िन दृश्य मोड +Name[hr]=Dolphinovi načini prikaza +Name[hu]=Dolphin nézetmódok +Name[ia]=Modos de vista de Dolphin +Name[id]=Mode Tampilan Dolphin +Name[is]=Dolphin skoðunarhamir +Name[it]=Viste di Dolphin +Name[ja]=Dolphin 表示モード +Name[kk]=Dolphin көрінісінің режімдері +Name[km]=របៀប​មើល Dolphin +Name[kn]=ಡಾಲ್ಫಿನ್ ನೋಟದ ವಿಧಾನಗಳು +Name[ko]=Dolphin 보기 모드 +Name[lt]=Dolphin rodymo būdai +Name[lv]=Dolphin skata režīmi +Name[mai]=डाल्फिनक दृश्य विधि +Name[mk]=Режими на преглед во Делфин +Name[ml]=ഡോള്‍ഫിന്‍ അവതരണദശകള്‍ +Name[mr]=डॉल्फिन दृश्य पद्धत +Name[ms]=Mod Paparan Dolphin +Name[nb]=Dolphin visningsmåter +Name[nds]=Dolphin-Ansichten +Name[nl]=Dolphin-weergavemodussen +Name[nn]=Dolphin-visingar +Name[pa]=ਡਾਲਫਿਨ ਝਲਕ ਮੋਡ +Name[pl]=Tryby widoku Dolphina +Name[pt]=Modos de Visualização do Dolphin +Name[pt_BR]=Modos de exibição do Dolphin +Name[ro]=Regimuri de vizualizare Dolphin +Name[ru]=Режимы просмотра папок в Dolphin +Name[se]=Dolphin čájehanmodusat +Name[si]=Dolphin දසුන් ආකාර +Name[sk]=Režimy zobrazenia Dolphinu +Name[sl]=Dolphin - načini prikaza +Name[sr]=Делфинови режими приказа +Name[sr@ijekavian]=Делфинови режими приказа +Name[sr@ijekavianlatin]=Dolphinovi režimi prikaza +Name[sr@latin]=Dolphinovi režimi prikaza +Name[sv]=Dolphins visningslägen +Name[tg]=Ҳолатҳои намоиши Dolphin +Name[th]=โหมดมุมมองของดอลฟิน +Name[tr]=Dolphin Görünüm Kipleri +Name[ug]=Dolphin كۆرۈنۈش ھالىتى +Name[uk]=Режими перегляду Dolphin +Name[wa]=Môdes di vuwe di Dolphin +Name[x-test]=xxDolphin View Modesxx +Name[zh_CN]=Dolphin 视图模式 +Name[zh_TW]=Dolphin 檢視模式 +Comment=This service allows configuration of the Dolphin view modes. +Comment[ar]=هذه الخدمة تتيح التحكم بأنماط عرض دولفين +Comment[ast]=Esti serviciu permítete configurar los moos de vista de Dolphin. +Comment[bg]=Това ви позволява да настроите режимите на показване в Dolphin. +Comment[bs]=Ovaj servis omogućava podešavanje Delfinovih režima prikaza. +Comment[ca]=Aquest servei permet la configuració dels modes de vista del Dolphin. +Comment[ca@valencia]=Este servei permet la configuració dels modes de vista del Dolphin. +Comment[cs]=Tato služba umožňuje nastavení režimů pohledu Dolphinu. +Comment[csb]=Na ùsłëżnota pòzwôlô na kònfigùracëjã ôrtów wëzdrzatkù Dolphina. +Comment[da]=Denne tjeneste muliggør konfiguration af Dolphins visningstilstande. +Comment[de]=Mit diesem Dienst können Dolphin-Ansichtsmodi eingerichtet werden. +Comment[el]=Αυτή η υπηρεσία επιτρέπει τη διαμόρφωση των λειτουργιών προβολής του Dolphin. +Comment[en_GB]=This service allows configuration of the Dolphin view modes. +Comment[eo]=Ĉi tiu servo permesas agordi la Dolphin rigardajn stilojn. +Comment[es]=Este servicio le permite configurar los modos de vista de Dolphin. +Comment[et]=See teenus võimaldab seadistada Dolphini vaaterežiime. +Comment[eu]=Zerbitzu honen bitartez Dolphin-en ikuspegi moduak aldatu daitezke. +Comment[fi]=Tällä palvelulla voi muokata Dolphinin katselutilojen asetuksia. +Comment[fr]=Ce service permet de configurer les modes d'affichage de Dolphin. +Comment[fy]=Mei dizze tsjinst kinne jo de dolfyn werjeftemodus ynstelle. +Comment[ga]=Leis an tseirbhís seo is féidir na hamhairc Dolphin a chumrú. +Comment[gl]=Este servizo permite modificar a configuración das vistas de Dolphin. +Comment[he]=שירות זה מאפשר הגדרה של אפשרויות התצוגה ב־Dolphin. +Comment[hi]=यह सेवा आपको डॉल्फ़िन दृश्य मोड को कॉन्फ़िगर करने देता है. +Comment[hr]=Ova usluga dozvoljava konfiguraciju Dolphinovih načina prikaza. +Comment[hu]=Ez a szolgáltatás a Dolphin nézetmódjainak beállítását teszi lehetővé. +Comment[ia]=Iste servicio permitte configuration de le modos de vista de Dolphin +Comment[id]=Layanan ini memungkinkan konfigurasi mode tampilan Dolphin. +Comment[is]=Þessi þjónusta leyfir stillingar á skoðunarham í Dolphin. +Comment[it]=Questo servizio permette di configurare le viste di Dolphin. +Comment[ja]=Dolphin の表示モードを設定します +Comment[kk]=Бұл Dolphin көрінісін баптауға мүмкіндік беретін қызмет. +Comment[km]=សេវា​នេះ​អនុញ្ញាត​ឲ្យ​កំណត់​រចនាសម្ព័ន្ធ​នៃ​របៀប​មើល​របស់ Dolphin ។ +Comment[kn]=ಈ ಸೇವೆಯು ಡಾಲ್ಫಿನ್‌ನ ಸಾಮಾನ್ಯ ನೋಟ ವಿಧಾನಗಳ ಸಂರಚನೆಗೆ ಅನುವು ಮಾಡಿಕೊಡುತ್ತದೆ. +Comment[ko]=이 서비스는 Dolphin 보기 모드를 설정합니다. +Comment[lt]=Ši tarnyba leidžia konfigūruoti Dolphin rodymo būdus. +Comment[lv]=Šis serviss ļauj konfigurēt Dolphin skata režīmus. +Comment[mai]=ई सेवा अहाँकेँ डॉल्फिन दृश्य मोड केँ विन्यस्त करै दैछ +Comment[mk]=Овој сервис овозможува конфигурација на режимите за преглед во Делфин. +Comment[ml]=ഡോള്‍ഫിന്‍ അവതരണദശകള്‍ ക്രമീകരിയ്ക്കാന്‍ ഈ സേവനം അനുവദിയ്ക്കുന്നു. +Comment[mr]=ही सेवा तुम्हाला डॉल्फिन दृश्य पद्धतीचे संयोजन करण्यास मदत करते. +Comment[nb]=Denne tjenesten tilbyr oppsett av Dolphin visningsmåter. +Comment[nds]=Mit dissen Deenst laat sik de Dolphin-Ansichten instellen. +Comment[nl]=Met deze dienst kunt u Dolphin-weergavemodussen configureren. +Comment[nn]=Denne tenesta lèt deg setja opp Dolphin-visingar. +Comment[pa]=ਇਹ ਸਰਵਿਸ ਡਾਲਫਿਨ ਝਲਕ ਮੋਡ ਦੀ ਸੰਰਚਨਾ ਵਾਸਤੇ ਹੈ। +Comment[pl]=Ta usługa umożliwia ustawienie trybów widoku Dolphina. +Comment[pt]=Este serviço permite-lhe configurar os modos de visualização do Dolphin. +Comment[pt_BR]=Este serviço permite configurar os modos de exibição do Dolphin. +Comment[ro]=Acest serviciu permite configurarea regimurilor de vizualizare Dolphin. +Comment[ru]=Эта служба позволяет настраивать режимы просмотра папок в Dolphin +Comment[si]=මෙම සේවාව Dolphin දසුන් ආකාර සැකසීමට ඉඩ දේ. +Comment[sk]=Táto služba umožňuje nastavenie režimov zobrazenia Dolphinu. +Comment[sl]=Ta storitev omogoča spreminjanje nastavitev načinov prikaza programa Dolphin. +Comment[sr]=Овај сервис омогућава подешавање Делфинових режима приказа. +Comment[sr@ijekavian]=Овај сервис омогућава подешавање Делфинових режима приказа. +Comment[sr@ijekavianlatin]=Ovaj servis omogućava podešavanje Dolphinovih režima prikaza. +Comment[sr@latin]=Ovaj servis omogućava podešavanje Dolphinovih režima prikaza. +Comment[sv]=Den här tjänsten låter dig anpassa visningslägen i Dolphin. +Comment[tg]=Ин хидмат барои танзимоти ҳолати намоиши Dolphin иҷозат медиҳад. +Comment[th]=บริการนี้อนุญาตให้ทำการปรับแต่งโหมดมุมมองต่าง ๆ ของดอลฟิน +Comment[tr]=Bu servis Dolphin görünüm kiplerini yapılandırmanızı sağlar. +Comment[ug]=بۇ مۇلازىمەت Dolphin نىڭ كۆرۈنۈش ھالىتىنى سەپلىشىڭىزگە يول قويىدۇ. +Comment[uk]=Ця служба надасть змогу налаштувати режими перегляду Dolphin. +Comment[wa]=Ci siervice permet l' apontiaedje des môdes di vuwe di Dolhpin. +Comment[x-test]=xxThis service allows configuration of the Dolphin view modes.xx +Comment[zh_CN]=此服务允许您配置 Dolphin 的视图模式。 +Comment[zh_TW]=此服務允許設定 Dolphin 的檢視模式。 + +[Desktop Entry] +Icon=view-choose +Type=Service +X-KDE-ServiceTypes=KCModule +Exec=kcmshell4 kcmdolphinviewmodes + +X-KDE-Library=kcm_dolphinviewmodes +X-KDE-PluginKeyword=dolphinviewmodes +X-KDE-ParentApp=kcontrol +X-DocPath=dolphin/index.html#preferences-dialog-viewmodes +Name=View Modes +Name[ar]=أنماط العرض +Name[ast]=Moos de vista +Name[bg]=Режими на преглед +Name[bn]=ভিউ মোড +Name[bs]=Režimi prikaza +Name[ca]=Modes de vista +Name[ca@valencia]=Modes de vista +Name[cs]=Režimy pohledu +Name[csb]=Ôrtë wëzdrzatków +Name[da]=Visningstilstande +Name[de]=Ansichts-Modi +Name[el]=Λειτουργίες προβολής +Name[en_GB]=View Modes +Name[eo]=Modeloj de Vidoj +Name[es]=Modos de vista +Name[et]=Vaaterežiimid +Name[eu]=Ikuspegi moduak +Name[fi]=Näkymät +Name[fr]=Modes d'affichage +Name[fy]=Werjeftemodus +Name[ga]=Móid Amhairc +Name[gl]=Modos da vista +Name[gu]=દેખાવ સ્થિતિઓ +Name[he]=אפשרויות תצוגה +Name[hi]=दृश्य मोड +Name[hr]=Načini prikaza +Name[hu]=Nézetmódok +Name[ia]=Modos de vista +Name[id]=Mode Tampilan +Name[is]=Skoðunarhamir +Name[it]=Viste +Name[ja]=表示モード +Name[kk]=Көрініс режімдері +Name[km]=របៀប​មើល +Name[kn]=ನೋಟದ ವಿಧಾನಗಳು +Name[ko]=보기 모드 +Name[lt]=Rodymo būdai +Name[lv]=Skata režīmi +Name[mai]=दृश्यक विधि +Name[mk]=Режими на преглед +Name[ml]=അവതരണ ദശകള്‍ +Name[mr]=दृश्य पद्धती +Name[ms]=Mod Paparan +Name[nb]=Visningsmåter +Name[nds]=Ansichten +Name[nl]=Weergavemodussen +Name[nn]=Visingsmodusar +Name[pa]=ਝਲਕ ਮੋਡ +Name[pl]=Tryby widoku +Name[pt]=Modos de Visualização +Name[pt_BR]=Modos de exibição +Name[ro]=Regimuri de vizualizare +Name[ru]=Режимы просмотра +Name[se]=Čájehanmodusat +Name[si]=දසුන් ආකාර +Name[sk]=Režimy zobrazenia +Name[sl]=Načini prikaza +Name[sr]=Режими приказа +Name[sr@ijekavian]=Режими приказа +Name[sr@ijekavianlatin]=Režimi prikaza +Name[sr@latin]=Režimi prikaza +Name[sv]=Visningslägen +Name[tg]=Ҳолатҳои намоиш +Name[th]=โหมดมุมมอง +Name[tr]=Görünüm Kipleri +Name[ug]=كۆرۈنۈش ھالىتى +Name[uk]=Режими перегляду +Name[wa]=Môdes di vuwe +Name[x-test]=xxView Modesxx +Name[zh_CN]=视图模式 +Name[zh_TW]=檢視模式 +Comment=Configure file manager view modes +Comment[ar]=اضبط إعدادات أنماط عرض مدير الملفات +Comment[ast]=Configurar los moos de vista del xestor de ficheros +Comment[bg]=Настройване режимите на преглед във файловия мениджър +Comment[bn]=ফাইল ম্যানেজার ভিউ মোড কনফিগার করুন +Comment[bs]=Podešavanje režima prikaza u menadžeru datoteka +Comment[ca]=Configura els modes de vista del gestor de fitxers +Comment[ca@valencia]=Configura els modes de vista del gestor de fitxers +Comment[cs]=Nastavení režimů pohledu správce souborů +Comment[csb]=Kònfigùracëjô ôrtów wëzdrzatkù menadżera lopków +Comment[da]=Indstil filhåndteringens visningstilstande +Comment[de]=Dateimanager-Ansichten einrichten +Comment[el]=Διαμόρφωση λειτουργιών προβολής της διαχείρισης αρχείων +Comment[en_GB]=Configure file manager view modes +Comment[eo]=Agordi la dosieradministrilan rigardajn stilojn +Comment[es]=Configurar los modos de vista del gestor de archivos +Comment[et]=Failihalduri vaaterežiimide seadistamine +Comment[eu]=Konfiguratu fitxategi-kudeatzailearen ikuspegi moduak +Comment[fi]=Tiedostonhallinnan katselutilojen asetukset +Comment[fr]=Configuration les modes d'affichage du gestionnaire de fichiers +Comment[fy]=Triembehear werjeftemodus ynstelle +Comment[ga]=Cumraigh amhairc bhainisteoir na gcomhad +Comment[gl]=Configura os modos de visualización do xestor de ficheiros +Comment[gu]=ફાઇલ વ્યવસ્થાપક દેખાવ સ્થિતિઓ રૂપરેખાંકિત કરો +Comment[he]=הגדרת אפשרויות תצוגת מנהל הקבצים +Comment[hi]=फ़ाइल प्रबंधक दृश्य मोड कॉन्फ़िगर करें +Comment[hr]=Podešavanje postavki načina prikaza upravitelja datoteka +Comment[hu]=A fájlkezelő nézetmódjainak beállításai +Comment[ia]=Configura le modos de vista del gerente de file +Comment[id]=Atur mode tampilan manajer berkas +Comment[is]=Stilla skoðunarhami í skráastjóra +Comment[it]=Configura le viste del gestore dei file +Comment[ja]=ファイルマネージャの表示モードを設定します +Comment[kk]=Файл менеджердің көрініс режімдерін баптау +Comment[km]=កំណត់​រចនាសម្ព័ន្ធ​របៀប​មើល​កម្មវិធី​គ្រប់គ្រង​ឯកសារ +Comment[kn]=ಕಡತ ವ್ಯವಸ್ಥಾಪಕದ ನೋಟ ವಿಧಾನಗಳನ್ನು ಸಂರಚಿಸಿ +Comment[ko]=파일 관리자 보기 모드 설정 +Comment[lt]=Failų tvarkyklės rodymo būdų konfigūravimas +Comment[lv]=Konfigurē failu pārvaldnieka skata režīmus +Comment[mai]=फाइलक प्रबंधक दृश्य विधि बिन्यस्त करू +Comment[mk]=Конфигурирајте ги режимите за преглед за менаџерот на датотеки +Comment[ml]=ഫയല്‍ നടത്തിപ്പുകാരന്റെ അവതരണ ദശകള്‍ ക്രമീകരിയ്ക്കുക +Comment[mr]=फाईल व्यवस्थापक दृश्य पद्धत संयोजीत करा +Comment[ms]=Selaraskan mod pengurus fail +Comment[nb]=Tilpass filbehandlerens visningsmåter +Comment[nds]=Den Dateipleger sien Ansichten instellen +Comment[nl]=Bestandsbeheerderweergavemodussen configureren +Comment[nn]=Set opp visingsmodusane i filhandsamaren +Comment[pa]=ਫਾਇਲ ਮੈਨੇਜਰ ਝਲਕ ਮੋਡ ਸੰਰਚਨਾ +Comment[pl]=Ustawienia trybów widoku zarządzania plikami +Comment[pt]=Configurar os modos de visualização do gestor de ficheiros +Comment[pt_BR]=Configura os modos de exibição do gerenciador de arquivos +Comment[ro]=Configurează regimurile de vizualizare ale gestionarului de fișiere +Comment[ru]=Настройка режимов просмотра папок в диспетчере файлов +Comment[se]=Heivet fiilagieđahalli čájehanmodusat +Comment[si]=ගොනු කළමණාකරුගේ සැසුම් සකසන්න +Comment[sk]=Nastavenie režimov zobrazenia správcu súborov +Comment[sl]=Nastavitve načinov prikaza upravljalnika datotek +Comment[sr]=Подешавање режима приказа у менаџеру фајлова +Comment[sr@ijekavian]=Подешавање режима приказа у менаџеру фајлова +Comment[sr@ijekavianlatin]=Podešavanje režima prikaza u menadžeru fajlova +Comment[sr@latin]=Podešavanje režima prikaza u menadžeru fajlova +Comment[sv]=Anpassa filhanterarens visninglägen +Comment[tg]=Танзимоти ҳолати намоиши мудири файлҳо +Comment[th]=ปรับแต่งโหมดมุมมองต่าง ๆ ของเครื่องมือจัดการแฟ้ม +Comment[tr]=Dosya yöneticisi görünüm ayarlarını yapılandır +Comment[ug]=ھۆججەت باشقۇرغۇچ كۆرۈنۈش ھالىتى سەپلىمىسى +Comment[uk]=Налаштувати режими перегляду менеджера файлів +Comment[wa]=Apontyî les môdes di vuwe do manaedjeu di fitchîs +Comment[x-test]=xxConfigure file manager view modesxx +Comment[zh_CN]=配置文件管理器视图模式 +Comment[zh_TW]=設定檔案管理員檢視模式 +X-KDE-Keywords=file manager +X-KDE-Keywords[ar]=مدير الملفات +X-KDE-Keywords[bg]=преглед на файлове +X-KDE-Keywords[bs]=upravitelj datoteka +X-KDE-Keywords[ca]=gestor de fitxers +X-KDE-Keywords[ca@valencia]=gestor de fitxers +X-KDE-Keywords[cs]=správce souborů +X-KDE-Keywords[da]=filhåndtering +X-KDE-Keywords[de]=Dateimanager +X-KDE-Keywords[el]=διαχειριστής αρχείων +X-KDE-Keywords[en_GB]=file manager +X-KDE-Keywords[es]=gestor de archivos +X-KDE-Keywords[et]=failihaldur +X-KDE-Keywords[fi]=tiedostonhallinta +X-KDE-Keywords[fr]=gestionnaire de fichiers +X-KDE-Keywords[ga]=bainisteoir comhad +X-KDE-Keywords[gl]=xestor de ficheiros +X-KDE-Keywords[he]=מנהל קבצים +X-KDE-Keywords[hu]=fájlkezelő +X-KDE-Keywords[ia]=gerente de file +X-KDE-Keywords[id]=manajer berkas +X-KDE-Keywords[is]=skráastjóri +X-KDE-Keywords[it]=gestore dei file +X-KDE-Keywords[kk]=file manager +X-KDE-Keywords[km]=កម្មវិធី​គ្រប់គ្រង​ឯកសារ +X-KDE-Keywords[ko]=파일 관리자 +X-KDE-Keywords[lt]=Failų tvarkyklė +X-KDE-Keywords[lv]=failu pārvaldnieks +X-KDE-Keywords[mr]=फाईल व्यवस्थापक +X-KDE-Keywords[nb]=filbehandler +X-KDE-Keywords[nds]=Dateipleger +X-KDE-Keywords[nl]=bestandsbeheerder +X-KDE-Keywords[nn]=filhandsamar +X-KDE-Keywords[pa]=ਫਾਇਲ ਮੈਨੇਜਰ +X-KDE-Keywords[pl]=zarządzanie plikami +X-KDE-Keywords[pt]=gestor de ficheiros +X-KDE-Keywords[pt_BR]=gerenciador de arquivos +X-KDE-Keywords[ro]=gestionar de fișiere +X-KDE-Keywords[ru]=диспетчер файлов +X-KDE-Keywords[sk]=správca súborov +X-KDE-Keywords[sl]=upravljalnik datotek +X-KDE-Keywords[sr]=file manager,менаџер фајлова +X-KDE-Keywords[sr@ijekavian]=file manager,менаџер фајлова +X-KDE-Keywords[sr@ijekavianlatin]=file manager,menadžer fajlova +X-KDE-Keywords[sr@latin]=file manager,menadžer fajlova +X-KDE-Keywords[sv]=filhanterare +X-KDE-Keywords[tr]=dosya yönetici +X-KDE-Keywords[uk]=менеджер,керування,файл,файли +X-KDE-Keywords[wa]=manaedjeu di fitchîs +X-KDE-Keywords[x-test]=xxfile managerxx +X-KDE-Keywords[zh_CN]=file manager,文件管理器 +X-KDE-Keywords[zh_TW]=檔案管理員 diff --git a/dolphin/src/settings/kcm/kcmdolphinviewmodes.h b/dolphin/src/settings/kcm/kcmdolphinviewmodes.h new file mode 100644 index 00000000..3181198f --- /dev/null +++ b/dolphin/src/settings/kcm/kcmdolphinviewmodes.h @@ -0,0 +1,51 @@ +/*************************************************************************** + * Copyright (C) 2008 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef KCMDOLPHINVIEWMODES_H +#define KCMDOLPHINVIEWMODES_H + +#include + +class ViewSettingsTab; + +/** + * @brief Allow to configure the Dolphin views. + */ +class DolphinViewModesConfigModule : public KCModule +{ + Q_OBJECT + +public: + DolphinViewModesConfigModule(QWidget* parent, const QVariantList& args); + virtual ~DolphinViewModesConfigModule(); + + virtual void save(); + virtual void defaults(); + +private: + void reparseConfiguration(); + +private Q_SLOTS: + void viewModeChanged(); + +private: + QList m_tabs; +}; + +#endif diff --git a/dolphin/src/settings/navigation/navigationsettingspage.cpp b/dolphin/src/settings/navigation/navigationsettingspage.cpp new file mode 100644 index 00000000..41c6f68e --- /dev/null +++ b/dolphin/src/settings/navigation/navigationsettingspage.cpp @@ -0,0 +1,118 @@ +/*************************************************************************** + * Copyright (C) 2009 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "navigationsettingspage.h" + +#include "dolphin_generalsettings.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +NavigationSettingsPage::NavigationSettingsPage(QWidget* parent) : + SettingsPageBase(parent), + m_openArchivesAsFolder(0), + m_autoExpandFolders(0) +{ + const int spacing = KDialog::spacingHint(); + + QVBoxLayout* topLayout = new QVBoxLayout(this); + KVBox* vBox = new KVBox(this); + vBox->setSpacing(spacing); + + // create 'Mouse' group + QGroupBox* mouseBox = new QGroupBox(i18nc("@title:group", "Mouse"), vBox); + mouseBox->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum); + m_singleClick = new QRadioButton(i18nc("@option:check Mouse Settings", + "Single-click to open files and folders"), mouseBox); + m_doubleClick = new QRadioButton(i18nc("@option:check Mouse Settings", + "Double-click to open files and folders"), mouseBox); + + QVBoxLayout* mouseBoxLayout = new QVBoxLayout(mouseBox); + mouseBoxLayout->addWidget(m_singleClick); + mouseBoxLayout->addWidget(m_doubleClick); + + m_openArchivesAsFolder = new QCheckBox(i18nc("@option:check", "Open archives as folder"), vBox); + + m_autoExpandFolders = new QCheckBox(i18nc("option:check", "Open folders during drag operations"), vBox); + + // Add a dummy widget with no restriction regarding + // a vertical resizing. This assures that the dialog layout + // is not stretched vertically. + new QWidget(vBox); + + topLayout->addWidget(vBox); + + loadSettings(); + + connect(m_singleClick, SIGNAL(toggled(bool)), this, SIGNAL(changed())); + connect(m_doubleClick, SIGNAL(toggled(bool)), this, SIGNAL(changed())); + connect(m_openArchivesAsFolder, SIGNAL(toggled(bool)), this, SIGNAL(changed())); + connect(m_autoExpandFolders, SIGNAL(toggled(bool)), this, SIGNAL(changed())); +} + +NavigationSettingsPage::~NavigationSettingsPage() +{ +} + +void NavigationSettingsPage::applySettings() +{ + KConfig config("kcminputrc"); + KConfigGroup group = config.group("KDE"); + group.writeEntry("SingleClick", m_singleClick->isChecked(), KConfig::Persistent|KConfig::Global); + config.sync(); + KGlobalSettings::self()->emitChange(KGlobalSettings::SettingsChanged, KGlobalSettings::SETTINGS_MOUSE); + + GeneralSettings* settings = GeneralSettings::self(); + settings->setBrowseThroughArchives(m_openArchivesAsFolder->isChecked()); + settings->setAutoExpandFolders(m_autoExpandFolders->isChecked()); + + settings->writeConfig(); +} + +void NavigationSettingsPage::restoreDefaults() +{ + GeneralSettings* settings = GeneralSettings::self(); + settings->useDefaults(true); + loadSettings(); + settings->useDefaults(false); + + // The mouse settings stored in KGlobalSettings must be reset to + // the default values (= single click) manually. + m_singleClick->setChecked(true); + m_doubleClick->setChecked(false); +} + +void NavigationSettingsPage::loadSettings() +{ + const bool singleClick = KGlobalSettings::singleClick(); + m_singleClick->setChecked(singleClick); + m_doubleClick->setChecked(!singleClick); + m_openArchivesAsFolder->setChecked(GeneralSettings::browseThroughArchives()); + m_autoExpandFolders->setChecked(GeneralSettings::autoExpandFolders()); +} + +#include "moc_navigationsettingspage.cpp" diff --git a/dolphin/src/settings/navigation/navigationsettingspage.h b/dolphin/src/settings/navigation/navigationsettingspage.h new file mode 100644 index 00000000..3aecd98b --- /dev/null +++ b/dolphin/src/settings/navigation/navigationsettingspage.h @@ -0,0 +1,54 @@ +/*************************************************************************** + * Copyright (C) 2009 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ +#ifndef NAVIGATIONSETTINGSPAGE_H +#define NAVIGATIONSETTINGSPAGE_H + +#include + +#include +#include + +/** + * @brief Page for the 'Navigation' settings of the Dolphin settings dialog. + */ +class NavigationSettingsPage : public SettingsPageBase +{ + Q_OBJECT + +public: + NavigationSettingsPage(QWidget* parent); + virtual ~NavigationSettingsPage(); + + /** @see SettingsPageBase::applySettings() */ + virtual void applySettings(); + + /** @see SettingsPageBase::restoreDefaults() */ + virtual void restoreDefaults(); + +private: + void loadSettings(); + +private: + QRadioButton* m_singleClick; + QRadioButton* m_doubleClick; + QCheckBox* m_openArchivesAsFolder; + QCheckBox* m_autoExpandFolders; +}; + +#endif diff --git a/dolphin/src/settings/serviceitemdelegate.cpp b/dolphin/src/settings/serviceitemdelegate.cpp new file mode 100644 index 00000000..16f44c16 --- /dev/null +++ b/dolphin/src/settings/serviceitemdelegate.cpp @@ -0,0 +1,132 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "serviceitemdelegate.h" + +#include +#include +#include + +#include "servicemodel.h" + +#include +#include +#include +#include + +ServiceItemDelegate::ServiceItemDelegate(QAbstractItemView* itemView, QObject* parent) : + KWidgetItemDelegate(itemView, parent) +{ +} + +ServiceItemDelegate::~ServiceItemDelegate() +{ +} + +QSize ServiceItemDelegate::sizeHint(const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + Q_UNUSED(index); + + const QStyle *style = itemView()->style(); + const int buttonHeight = style->pixelMetric(QStyle::PM_ButtonMargin) * 2 + + style->pixelMetric(QStyle::PM_ButtonIconSize); + const int fontHeight = option.fontMetrics.height(); + return QSize(100, qMax(buttonHeight, fontHeight)); +} + +void ServiceItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, + const QModelIndex& index) const +{ + Q_UNUSED(index); + painter->save(); + + itemView()->style()->drawPrimitive(QStyle::PE_PanelItemViewItem, &option, painter); + + if (option.state & QStyle::State_Selected) { + painter->setPen(option.palette.highlightedText().color()); + } + + painter->restore(); +} + +QList ServiceItemDelegate::createItemWidgets() const +{ + QCheckBox* checkBox = new QCheckBox(); + QPalette palette = checkBox->palette(); + palette.setColor(QPalette::WindowText, palette.color(QPalette::Text)); + checkBox->setPalette(palette); + connect(checkBox, SIGNAL(clicked(bool)), this, SLOT(slotCheckBoxClicked(bool))); + + KPushButton* configureButton = new KPushButton(); + connect(configureButton, SIGNAL(clicked()), this, SLOT(slotConfigureButtonClicked())); + + return QList() << checkBox << configureButton; +} + +void ServiceItemDelegate::updateItemWidgets(const QList widgets, + const QStyleOptionViewItem& option, + const QPersistentModelIndex& index) const +{ + QCheckBox* checkBox = static_cast(widgets[0]); + KPushButton *configureButton = static_cast(widgets[1]); + + const int itemHeight = sizeHint(option, index).height(); + + // Update the checkbox showing the service name and icon + const QAbstractItemModel* model = index.model(); + checkBox->setText(model->data(index).toString()); + const QString iconName = model->data(index, Qt::DecorationRole).toString(); + if (!iconName.isEmpty()) { + checkBox->setIcon(KIcon(iconName)); + } + checkBox->setChecked(model->data(index, Qt::CheckStateRole).toBool()); + + const bool configurable = model->data(index, ServiceModel::ConfigurableRole).toBool(); + + int checkBoxWidth = option.rect.width(); + if (configurable) { + checkBoxWidth -= configureButton->sizeHint().width(); + } + checkBox->resize(checkBoxWidth, checkBox->sizeHint().height()); + checkBox->move(0, (itemHeight - checkBox->height()) / 2); + + // Update the configuration button + if (configurable) { + configureButton->setEnabled(checkBox->isChecked()); + configureButton->setIcon(KIcon("configure")); + configureButton->resize(configureButton->sizeHint()); + configureButton->move(option.rect.right() - configureButton->width(), + (itemHeight - configureButton->height()) / 2); + } + configureButton->setVisible(configurable); +} + +void ServiceItemDelegate::slotCheckBoxClicked(bool checked) +{ + QAbstractItemModel* model = const_cast(focusedIndex().model()); + model->setData(focusedIndex(), checked, Qt::CheckStateRole); +} + +void ServiceItemDelegate::slotConfigureButtonClicked() +{ + emit requestServiceConfiguration(focusedIndex()); +} + +#include "moc_serviceitemdelegate.cpp" diff --git a/dolphin/src/settings/serviceitemdelegate.h b/dolphin/src/settings/serviceitemdelegate.h new file mode 100644 index 00000000..ea9681a5 --- /dev/null +++ b/dolphin/src/settings/serviceitemdelegate.h @@ -0,0 +1,59 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef SERVICEITEMDELEGATE_H +#define SERVICEITEMDELEGATE_H + +#include + +/** + * @brief Widget item delegate for a service that can be enabled or disabled. + * + * Additionally it is possible to configure a service. + * @see ServiceModel + */ +class ServiceItemDelegate : public KWidgetItemDelegate +{ + Q_OBJECT + +public: + explicit ServiceItemDelegate(QAbstractItemView* itemView, QObject* parent = 0); + virtual ~ServiceItemDelegate(); + + virtual QSize sizeHint(const QStyleOptionViewItem &option, + const QModelIndex &index) const; + + virtual void paint(QPainter* painter, const QStyleOptionViewItem& option, + const QModelIndex& index) const; + + virtual QList createItemWidgets() const; + + virtual void updateItemWidgets(const QList widgets, + const QStyleOptionViewItem& option, + const QPersistentModelIndex& index) const; + +signals: + void requestServiceConfiguration(const QModelIndex& index); + +private slots: + void slotCheckBoxClicked(bool checked); + void slotConfigureButtonClicked(); +}; + +#endif diff --git a/dolphin/src/settings/servicemodel.cpp b/dolphin/src/settings/servicemodel.cpp new file mode 100644 index 00000000..618567e5 --- /dev/null +++ b/dolphin/src/settings/servicemodel.cpp @@ -0,0 +1,108 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "servicemodel.h" + +ServiceModel::ServiceModel(QObject* parent) : + QAbstractListModel(parent), + m_items() +{ +} + +ServiceModel::~ServiceModel() +{ +} + +bool ServiceModel::insertRows(int row, int count, const QModelIndex& parent) +{ + if (row > rowCount()) { + return false; + } + + if (count <= 0) { + count = 1; + } + + beginInsertRows(parent, row, row + count - 1); + for (int i = 0; i < count; ++i) { + ServiceItem item; + item.checked = false; + item.configurable = false; + m_items.insert(row, item); + } + endInsertRows(); + + return true; +} + +bool ServiceModel::setData(const QModelIndex& index, const QVariant& value, int role) +{ + const int row = index.row(); + if (row >= rowCount()) { + return false; + } + + switch (role) { + case Qt::CheckStateRole: + m_items[row].checked = value.toBool(); + break; + case ConfigurableRole: + m_items[row].configurable = value.toBool(); + break; + case Qt::DecorationRole: + m_items[row].icon = value.toString(); + break; + case Qt::DisplayRole: + m_items[row].text = value.toString(); + break; + case DesktopEntryNameRole: + m_items[row].desktopEntryName = value.toString(); + break; + default: + return false; + } + + emit dataChanged(index, index); + return true; +} + +QVariant ServiceModel::data(const QModelIndex& index, int role) const +{ + const int row = index.row(); + if (row < rowCount()) { + switch (role) { + case ConfigurableRole: return m_items[row].configurable; + case Qt::CheckStateRole: return m_items[row].checked; + case Qt::DecorationRole: return m_items[row].icon; + case Qt::DisplayRole: return m_items[row].text; + case DesktopEntryNameRole: return m_items[row].desktopEntryName; + default: break; + } + } + + return QVariant(); +} + +int ServiceModel::rowCount(const QModelIndex& parent) const +{ + Q_UNUSED(parent); + return m_items.count(); +} + +#include "moc_servicemodel.cpp" diff --git a/dolphin/src/settings/servicemodel.h b/dolphin/src/settings/servicemodel.h new file mode 100644 index 00000000..2b1e1fe0 --- /dev/null +++ b/dolphin/src/settings/servicemodel.h @@ -0,0 +1,68 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef SERVICEMODEL_H +#define SERVICEMODEL_H + +#include +#include + +/** + * @brief Provides a simple model for enabling/disabling services + * + * The following roles are supported: + * - Qt::DisplayRole: Name of the service + * - Qt::DecorationRole: Icon name of the service + * - Qt::CheckStateRole: Specifies whether the service is enabled + * - ServiceModel::DesktopEntryNameRole: Name of the desktop-entry of the service + * - ServiceModel::Configurable: Specifies whether the service is configurable by the user + */ +class ServiceModel : public QAbstractListModel +{ + Q_OBJECT + +public: + enum Role + { + DesktopEntryNameRole = Qt::UserRole, + ConfigurableRole + }; + + explicit ServiceModel(QObject* parent = 0); + virtual ~ServiceModel(); + + virtual bool insertRows(int row, int count, const QModelIndex & parent = QModelIndex()); + virtual bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole); + virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const; + virtual int rowCount(const QModelIndex& parent = QModelIndex()) const; + + private: + struct ServiceItem + { + bool checked; + bool configurable; + QString icon; + QString text; + QString desktopEntryName; + }; + + QList m_items; +}; + +#endif diff --git a/dolphin/src/settings/services/servicessettingspage.cpp b/dolphin/src/settings/services/servicessettingspage.cpp new file mode 100644 index 00000000..a26f6b0f --- /dev/null +++ b/dolphin/src/settings/services/servicessettingspage.cpp @@ -0,0 +1,269 @@ +/*************************************************************************** + * Copyright (C) 2009-2010 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "servicessettingspage.h" + +#include "dolphin_generalsettings.h" +#include "dolphin_versioncontrolsettings.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace +{ + const bool ShowDeleteDefault = false; + const char* VersionControlServicePrefix = "_version_control_"; + const char* DeleteService = "_delete"; + const char* CopyToMoveToService ="_copy_to_move_to"; +} + +ServicesSettingsPage::ServicesSettingsPage(QWidget* parent) : + SettingsPageBase(parent), + m_initialized(false), + m_serviceModel(0), + m_sortModel(0), + m_listView(0), + m_enabledVcsPlugins() +{ + QVBoxLayout* topLayout = new QVBoxLayout(this); + + QLabel* label = new QLabel(i18nc("@label:textbox", + "Select which services should " + "be shown in the context menu:"), this); + label->setWordWrap(true); + + m_listView = new QListView(this); + ServiceItemDelegate* delegate = new ServiceItemDelegate(m_listView, m_listView); + m_serviceModel = new ServiceModel(this); + m_sortModel = new QSortFilterProxyModel(this); + m_sortModel->setSourceModel(m_serviceModel); + m_sortModel->setSortRole(Qt::DisplayRole); + m_listView->setModel(m_sortModel); + m_listView->setItemDelegate(delegate); + m_listView->setVerticalScrollMode(QListView::ScrollPerPixel); + connect(m_listView, SIGNAL(clicked(QModelIndex)), this, SIGNAL(changed())); + + topLayout->addWidget(label); + topLayout->addWidget(m_listView); + + m_enabledVcsPlugins = VersionControlSettings::enabledPlugins(); + qSort(m_enabledVcsPlugins); +} + +ServicesSettingsPage::~ServicesSettingsPage() +{ +} + +void ServicesSettingsPage::applySettings() +{ + if (!m_initialized) { + return; + } + + KConfig config("kservicemenurc", KConfig::NoGlobals); + KConfigGroup showGroup = config.group("Show"); + + QStringList enabledPlugins; + + const QAbstractItemModel* model = m_listView->model(); + for (int i = 0; i < model->rowCount(); ++i) { + const QModelIndex index = model->index(i, 0); + const QString service = model->data(index, ServiceModel::DesktopEntryNameRole).toString(); + const bool checked = model->data(index, Qt::CheckStateRole).toBool(); + + if (service.startsWith(VersionControlServicePrefix)) { + if (checked) { + enabledPlugins.append(model->data(index, Qt::DisplayRole).toString()); + } + } else if (service == QLatin1String(DeleteService)) { + KSharedConfig::Ptr globalConfig = KSharedConfig::openConfig("kdeglobals", KConfig::NoGlobals); + KConfigGroup configGroup(globalConfig, "KDE"); + configGroup.writeEntry("ShowDeleteCommand", checked); + configGroup.sync(); + } else if (service == QLatin1String(CopyToMoveToService)) { + GeneralSettings::setShowCopyMoveMenu(checked); + GeneralSettings::self()->writeConfig(); + } else { + showGroup.writeEntry(service, checked); + } + } + + showGroup.sync(); + + if (m_enabledVcsPlugins != enabledPlugins) { + VersionControlSettings::setEnabledPlugins(enabledPlugins); + VersionControlSettings::self()->writeConfig(); + + KMessageBox::information(window(), + i18nc("@info", "Dolphin must be restarted to apply the " + "updated version control systems settings."), + QString(), // default title + QLatin1String("ShowVcsRestartInformation")); + } +} + +void ServicesSettingsPage::restoreDefaults() +{ + QAbstractItemModel* model = m_listView->model(); + for (int i = 0; i < model->rowCount(); ++i) { + const QModelIndex index = model->index(i, 0); + const QString service = model->data(index, ServiceModel::DesktopEntryNameRole).toString(); + + const bool checked = !service.startsWith(VersionControlServicePrefix) + && service != QLatin1String(DeleteService) + && service != QLatin1String(CopyToMoveToService); + model->setData(index, checked, Qt::CheckStateRole); + } +} + +void ServicesSettingsPage::showEvent(QShowEvent* event) +{ + if (!event->spontaneous() && !m_initialized) { + loadServices(); + + loadVersionControlSystems(); + + // Add "Show 'Delete' command" as service + KSharedConfig::Ptr globalConfig = KSharedConfig::openConfig("kdeglobals", KConfig::IncludeGlobals); + KConfigGroup configGroup(globalConfig, "KDE"); + addRow("edit-delete", + i18nc("@option:check", "Delete"), + DeleteService, + configGroup.readEntry("ShowDeleteCommand", ShowDeleteDefault)); + + // Add "Show 'Copy To' and 'Move To' commands" as service + addRow("edit-copy", + i18nc("@option:check", "'Copy To' and 'Move To' commands"), + CopyToMoveToService, + GeneralSettings::showCopyMoveMenu()); + + m_sortModel->sort(Qt::DisplayRole); + + m_initialized = true; + } + SettingsPageBase::showEvent(event); +} + +void ServicesSettingsPage::loadServices() +{ + const KConfig config("kservicemenurc", KConfig::NoGlobals); + const KConfigGroup showGroup = config.group("Show"); + + // Load generic services + const KService::List entries = KServiceTypeTrader::self()->query("KonqPopupMenu/Plugin"); + foreach (const KSharedPtr& service, entries) { + const QString file = KStandardDirs::locate("services", service->entryPath()); + const QList serviceActions = + KDesktopFileActions::userDefinedServices(file, true); + + KDesktopFile desktopFile(file); + const QString subMenuName = desktopFile.desktopGroup().readEntry("X-KDE-Submenu"); + + foreach (const KServiceAction& action, serviceActions) { + const QString serviceName = action.name(); + const bool addService = !action.noDisplay() + && !action.isSeparator() + && !isInServicesList(serviceName); + + if (addService) { + const QString itemName = subMenuName.isEmpty() + ? action.text() + : i18nc("@item:inmenu", "%1: %2", subMenuName, action.text()); + const bool checked = showGroup.readEntry(serviceName, true); + addRow(action.icon(), itemName, serviceName, checked); + } + } + } + + // Load service plugins that implement the KFileItemActionPlugin interface + const KService::List pluginServices = KServiceTypeTrader::self()->query("KFileItemAction/Plugin"); + foreach (const KSharedPtr& service, pluginServices) { + const QString desktopEntryName = service->desktopEntryName(); + if (!isInServicesList(desktopEntryName)) { + const bool checked = showGroup.readEntry(desktopEntryName, true); + addRow(service->icon(), service->name(), desktopEntryName, checked); + } + } + + m_sortModel->sort(Qt::DisplayRole); +} + +void ServicesSettingsPage::loadVersionControlSystems() +{ + const QStringList enabledPlugins = VersionControlSettings::enabledPlugins(); + + // Create a checkbox for each available version control plugin + const KService::List pluginServices = KServiceTypeTrader::self()->query("FileViewVersionControlPlugin"); + for (KService::List::ConstIterator it = pluginServices.constBegin(); it != pluginServices.constEnd(); ++it) { + const QString pluginName = (*it)->name(); + addRow("code-class", + pluginName, + VersionControlServicePrefix + pluginName, + enabledPlugins.contains(pluginName)); + } + + m_sortModel->sort(Qt::DisplayRole); +} + +bool ServicesSettingsPage::isInServicesList(const QString& service) const +{ + for (int i = 0; i < m_serviceModel->rowCount(); ++i) { + const QModelIndex index = m_serviceModel->index(i, 0); + if (m_serviceModel->data(index, ServiceModel::DesktopEntryNameRole).toString() == service) { + return true; + } + } + return false; +} + +void ServicesSettingsPage::addRow(const QString& icon, + const QString& text, + const QString& value, + bool checked) +{ + m_serviceModel->insertRow(0); + + const QModelIndex index = m_serviceModel->index(0, 0); + m_serviceModel->setData(index, icon, Qt::DecorationRole); + m_serviceModel->setData(index, text, Qt::DisplayRole); + m_serviceModel->setData(index, value, ServiceModel::DesktopEntryNameRole); + m_serviceModel->setData(index, checked, Qt::CheckStateRole); +} + +#include "moc_servicessettingspage.cpp" diff --git a/dolphin/src/settings/services/servicessettingspage.h b/dolphin/src/settings/services/servicessettingspage.h new file mode 100644 index 00000000..b65e6d9b --- /dev/null +++ b/dolphin/src/settings/services/servicessettingspage.h @@ -0,0 +1,83 @@ +/*************************************************************************** + * Copyright (C) 2009-2010 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ +#ifndef SERVICESSETTINGSPAGE_H +#define SERVICESSETTINGSPAGE_H + +#include + +#include +#include + +#include +#include +#include +#include +class ServiceModel; + +/** + * @brief Page for the 'Services' settings of the Dolphin settings dialog. + */ +class ServicesSettingsPage : public SettingsPageBase +{ + Q_OBJECT + +public: + ServicesSettingsPage(QWidget* parent); + virtual ~ServicesSettingsPage(); + + /** @see SettingsPageBase::applySettings() */ + virtual void applySettings(); + + /** @see SettingsPageBase::restoreDefaults() */ + virtual void restoreDefaults(); + +protected: + virtual void showEvent(QShowEvent* event); + +private slots: + /** + * Loads locally installed services. + */ + void loadServices(); + +private: + /** + * Loads installed version control systems. + */ + void loadVersionControlSystems(); + + bool isInServicesList(const QString& service) const; + + /** + * Adds a row to the model of m_listView. + */ + void addRow(const QString& icon, + const QString& text, + const QString& value, + bool checked); + +private: + bool m_initialized; + ServiceModel* m_serviceModel; + QSortFilterProxyModel* m_sortModel; + QListView* m_listView; + QStringList m_enabledVcsPlugins; +}; + +#endif diff --git a/dolphin/src/settings/settingspagebase.cpp b/dolphin/src/settings/settingspagebase.cpp new file mode 100644 index 00000000..9b703ce6 --- /dev/null +++ b/dolphin/src/settings/settingspagebase.cpp @@ -0,0 +1,31 @@ +/*************************************************************************** + * Copyright (C) 2006 by Peter Penz * + * peter.penz@gmx.at * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "settingspagebase.h" + +SettingsPageBase::SettingsPageBase(QWidget* parent) : + QWidget(parent) +{} + +SettingsPageBase::~SettingsPageBase() +{} + + +#include "moc_settingspagebase.cpp" diff --git a/dolphin/src/settings/settingspagebase.h b/dolphin/src/settings/settingspagebase.h new file mode 100644 index 00000000..05973d45 --- /dev/null +++ b/dolphin/src/settings/settingspagebase.h @@ -0,0 +1,54 @@ +/*************************************************************************** + * Copyright (C) 2006 by Peter Penz * + * peter.penz@gmx.at * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef SETTINGSPAGEBASE_H +#define SETTINGSPAGEBASE_H + +#include + +/** + * @brief Base class for the settings pages of the Dolphin settings dialog. + */ +class SettingsPageBase : public QWidget +{ + Q_OBJECT + +public: + explicit SettingsPageBase(QWidget* parent = 0); + virtual ~SettingsPageBase(); + + /** + * Must be implemented by a derived class to + * persistently store the settings. + */ + virtual void applySettings() = 0; + + /** + * Must be implemented by a derived class to + * restored the settings to default values. + */ + virtual void restoreDefaults() = 0; + +signals: + /** Is emitted if a setting has been changed. */ + void changed(); +}; + +#endif diff --git a/dolphin/src/settings/startup/startupsettingspage.cpp b/dolphin/src/settings/startup/startupsettingspage.cpp new file mode 100644 index 00000000..e7e4bc3b --- /dev/null +++ b/dolphin/src/settings/startup/startupsettingspage.cpp @@ -0,0 +1,179 @@ +/*************************************************************************** + * Copyright (C) 2008 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "startupsettingspage.h" + +#include "dolphinmainwindow.h" +#include "dolphinviewcontainer.h" + +#include "dolphin_generalsettings.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "views/dolphinview.h" + +StartupSettingsPage::StartupSettingsPage(const KUrl& url, QWidget* parent) : + SettingsPageBase(parent), + m_url(url), + m_homeUrl(0), + m_splitView(0), + m_editableUrl(0), + m_showFullPath(0), + m_filterBar(0) +{ + const int spacing = KDialog::spacingHint(); + + QVBoxLayout* topLayout = new QVBoxLayout(this); + KVBox* vBox = new KVBox(this); + vBox->setSpacing(spacing); + + // create 'Home URL' editor + QGroupBox* homeBox = new QGroupBox(i18nc("@title:group", "Home Folder"), vBox); + + KHBox* homeUrlBox = new KHBox(homeBox); + homeUrlBox->setSpacing(spacing); + + new QLabel(i18nc("@label:textbox", "Location:"), homeUrlBox); + m_homeUrl = new KLineEdit(homeUrlBox); + m_homeUrl->setClearButtonShown(true); + + QPushButton* selectHomeUrlButton = new QPushButton(KIcon("folder-open"), QString(), homeUrlBox); + connect(selectHomeUrlButton, SIGNAL(clicked()), + this, SLOT(selectHomeUrl())); + + KHBox* buttonBox = new KHBox(homeBox); + buttonBox->setSpacing(spacing); + + QPushButton* useCurrentButton = new QPushButton(i18nc("@action:button", "Use Current Location"), buttonBox); + connect(useCurrentButton, SIGNAL(clicked()), + this, SLOT(useCurrentLocation())); + QPushButton* useDefaultButton = new QPushButton(i18nc("@action:button", "Use Default Location"), buttonBox); + connect(useDefaultButton, SIGNAL(clicked()), + this, SLOT(useDefaultLocation())); + + QVBoxLayout* homeBoxLayout = new QVBoxLayout(homeBox); + homeBoxLayout->addWidget(homeUrlBox); + homeBoxLayout->addWidget(buttonBox); + + // create 'Split view', 'Show full path', 'Editable location' and 'Filter bar' checkboxes + m_splitView = new QCheckBox(i18nc("@option:check Startup Settings", "Split view mode"), vBox); + m_editableUrl = new QCheckBox(i18nc("@option:check Startup Settings", "Editable location bar"), vBox); + m_showFullPath = new QCheckBox(i18nc("@option:check Startup Settings", "Show full path inside location bar"), vBox); + m_filterBar = new QCheckBox(i18nc("@option:check Startup Settings", "Show filter bar"), vBox); + + // Add a dummy widget with no restriction regarding + // a vertical resizing. This assures that the dialog layout + // is not stretched vertically. + new QWidget(vBox); + + topLayout->addWidget(vBox); + + loadSettings(); + + connect(m_homeUrl, SIGNAL(textChanged(QString)), this, SLOT(slotSettingsChanged())); + connect(m_splitView, SIGNAL(toggled(bool)), this, SLOT(slotSettingsChanged())); + connect(m_editableUrl, SIGNAL(toggled(bool)), this, SLOT(slotSettingsChanged())); + connect(m_showFullPath, SIGNAL(toggled(bool)), this, SLOT(slotSettingsChanged())); + connect(m_filterBar, SIGNAL(toggled(bool)), this, SLOT(slotSettingsChanged())); +} + +StartupSettingsPage::~StartupSettingsPage() +{ +} + +void StartupSettingsPage::applySettings() +{ + GeneralSettings* settings = GeneralSettings::self(); + + const KUrl url(m_homeUrl->text()); + KFileItem fileItem(KFileItem::Unknown, KFileItem::Unknown, url); + if (url.isValid() && fileItem.isDir()) { + settings->setHomeUrl(url.prettyUrl()); + } else { + KMessageBox::error(this, i18nc("@info", "The location for the home folder is invalid or does not exist, it will not be applied.")); + } + + settings->setSplitView(m_splitView->isChecked()); + settings->setEditableUrl(m_editableUrl->isChecked()); + settings->setShowFullPath(m_showFullPath->isChecked()); + settings->setFilterBar(m_filterBar->isChecked()); + + settings->writeConfig(); +} + +void StartupSettingsPage::restoreDefaults() +{ + GeneralSettings* settings = GeneralSettings::self(); + settings->useDefaults(true); + loadSettings(); + settings->useDefaults(false); +} + +void StartupSettingsPage::slotSettingsChanged() +{ + // Provide a hint that the startup settings have been changed. This allows the views + // to apply the startup settings only if they have been explicitly changed by the user + // (see bug #254947). + GeneralSettings::setModifiedStartupSettings(true); + emit changed(); +} + +void StartupSettingsPage::selectHomeUrl() +{ + const QString homeUrl = m_homeUrl->text(); + KUrl url = KFileDialog::getExistingDirectoryUrl(homeUrl, this); + if (!url.isEmpty()) { + m_homeUrl->setText(url.prettyUrl()); + slotSettingsChanged(); + } +} + +void StartupSettingsPage::useCurrentLocation() +{ + m_homeUrl->setText(m_url.prettyUrl()); +} + +void StartupSettingsPage::useDefaultLocation() +{ + KUrl url(QDir::homePath()); + m_homeUrl->setText(url.prettyUrl()); +} + +void StartupSettingsPage::loadSettings() +{ + const KUrl url(GeneralSettings::homeUrl()); + m_homeUrl->setText(url.prettyUrl()); + m_splitView->setChecked(GeneralSettings::splitView()); + m_editableUrl->setChecked(GeneralSettings::editableUrl()); + m_showFullPath->setChecked(GeneralSettings::showFullPath()); + m_filterBar->setChecked(GeneralSettings::filterBar()); +} + +#include "moc_startupsettingspage.cpp" diff --git a/dolphin/src/settings/startup/startupsettingspage.h b/dolphin/src/settings/startup/startupsettingspage.h new file mode 100644 index 00000000..113e3f59 --- /dev/null +++ b/dolphin/src/settings/startup/startupsettingspage.h @@ -0,0 +1,67 @@ +/*************************************************************************** + * Copyright (C) 2008 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ +#ifndef STARTUPSETTINGSPAGE_H +#define STARTUPSETTINGSPAGE_H + +#include +#include + +class KLineEdit; +#include + +/** + * @brief Page for the 'Startup' settings of the Dolphin settings dialog. + * + * The startup settings allow to set the home URL and to configure the + * state of the view mode, split mode and the filter bar when starting Dolphin. + */ +class StartupSettingsPage : public SettingsPageBase +{ + Q_OBJECT + +public: + StartupSettingsPage(const KUrl& url, QWidget* parent); + virtual ~StartupSettingsPage(); + + /** @see SettingsPageBase::applySettings() */ + virtual void applySettings(); + + /** @see SettingsPageBase::restoreDefaults() */ + virtual void restoreDefaults(); + +private slots: + void slotSettingsChanged(); + void selectHomeUrl(); + void useCurrentLocation(); + void useDefaultLocation(); + +private: + void loadSettings(); + +private: + KUrl m_url; + KLineEdit* m_homeUrl; + + QCheckBox* m_splitView; + QCheckBox* m_editableUrl; + QCheckBox* m_showFullPath; + QCheckBox* m_filterBar; +}; + +#endif diff --git a/dolphin/src/settings/trash/trashsettingspage.cpp b/dolphin/src/settings/trash/trashsettingspage.cpp new file mode 100644 index 00000000..3bf9c7b4 --- /dev/null +++ b/dolphin/src/settings/trash/trashsettingspage.cpp @@ -0,0 +1,70 @@ +/*************************************************************************** + * Copyright (C) 2009 by Shaun Reich shaun.reich@kdemail.net * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "trashsettingspage.h" + +#include +#include +#include + +#include + +TrashSettingsPage::TrashSettingsPage(QWidget* parent) : + SettingsPageBase(parent) +{ + const int spacing = KDialog::spacingHint(); + + QVBoxLayout* topLayout = new QVBoxLayout(this); + KVBox* vBox = new KVBox(this); + vBox->setSpacing(spacing); + + m_proxy = new KCModuleProxy("kcmtrash"); + topLayout->addWidget(m_proxy); + + // Add a dummy widget with no restriction regarding + // a vertical resizing. This assures that the dialog layout + // is not stretched vertically. + new QWidget(vBox); + topLayout->addWidget(vBox); + + loadSettings(); + + connect(m_proxy, SIGNAL(changed(bool)), this, SIGNAL(changed())); +} + +TrashSettingsPage::~TrashSettingsPage() +{ +} + +void TrashSettingsPage::applySettings() +{ + m_proxy->save(); +} + +void TrashSettingsPage::restoreDefaults() +{ + m_proxy->defaults(); +} + +void TrashSettingsPage::loadSettings() +{ + m_proxy->load(); +} + +#include "moc_trashsettingspage.cpp" diff --git a/dolphin/src/settings/trash/trashsettingspage.h b/dolphin/src/settings/trash/trashsettingspage.h new file mode 100644 index 00000000..bf4a71ea --- /dev/null +++ b/dolphin/src/settings/trash/trashsettingspage.h @@ -0,0 +1,47 @@ +/*************************************************************************** + * Copyright (C) 2009 by Shaun Reich shaun.reich@kdemail.net * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ +#ifndef TRASHSETTINGSPAGE_H +#define TRASHSETTINGSPAGE_H + +#include "settings/settingspagebase.h" +class KCModuleProxy; + +/** + * @brief Tab page for the 'Trash' settings of the Dolphin settings dialog, it uses the KCM. + */ +class TrashSettingsPage : public SettingsPageBase +{ + Q_OBJECT + +public: + TrashSettingsPage(QWidget* parent); + virtual ~TrashSettingsPage(); + + /** @see SettingsPageBase::applySettings() */ + virtual void applySettings(); + + /** @see SettingsPageBase::restoreDefaults() */ + virtual void restoreDefaults(); + +private: + void loadSettings(); + KCModuleProxy *m_proxy; +}; + +#endif diff --git a/dolphin/src/settings/viewmodes/dolphinfontrequester.cpp b/dolphin/src/settings/viewmodes/dolphinfontrequester.cpp new file mode 100644 index 00000000..d6b465ba --- /dev/null +++ b/dolphin/src/settings/viewmodes/dolphinfontrequester.cpp @@ -0,0 +1,107 @@ +/*************************************************************************** + * Copyright (C) 2008 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "dolphinfontrequester.h" + +#include +#include +#include +#include + +#include +#include +#include + +DolphinFontRequester::DolphinFontRequester(QWidget* parent) : + QWidget(parent), + m_modeCombo(0), + m_chooseFontButton(0), + m_mode(SystemFont), + m_customFont() +{ + QHBoxLayout* topLayout = new QHBoxLayout(this); + topLayout->setMargin(0); + + m_modeCombo = new KComboBox(this); + m_modeCombo->addItem(i18nc("@item:inlistbox Font", "System Font")); + m_modeCombo->addItem(i18nc("@item:inlistbox Font", "Custom Font")); + connect(m_modeCombo, SIGNAL(activated(int)), + this, SLOT(changeMode(int))); + + m_chooseFontButton = new QPushButton(i18nc("@action:button Choose font", "Choose..."), this); + connect(m_chooseFontButton, SIGNAL(clicked()), + this, SLOT(openFontDialog())); + + changeMode(m_modeCombo->currentIndex()); + + topLayout->addWidget(m_modeCombo); + topLayout->addWidget(m_chooseFontButton); +} + +DolphinFontRequester::~DolphinFontRequester() +{ +} + +void DolphinFontRequester::setMode(Mode mode) +{ + m_mode = mode; + m_modeCombo->setCurrentIndex(m_mode); + m_chooseFontButton->setEnabled(m_mode == CustomFont); +} + +DolphinFontRequester::Mode DolphinFontRequester::mode() const +{ + return m_mode; +} + +QFont DolphinFontRequester::currentFont() const +{ + return (m_mode == CustomFont) ? m_customFont : KGlobalSettings::generalFont(); +} + +void DolphinFontRequester::setCustomFont(const QFont& font) +{ + m_customFont = font; +} + +QFont DolphinFontRequester::customFont() const +{ + return m_customFont; +} + +void DolphinFontRequester::openFontDialog() +{ + QFont font = m_customFont; + const int result = KFontDialog::getFont(font, + KFontChooser::NoDisplayFlags, + this); + if (result == KFontDialog::Accepted) { + m_customFont = font; + m_modeCombo->setFont(m_customFont); + emit changed(); + } +} + +void DolphinFontRequester::changeMode(int index) +{ + setMode((index == CustomFont) ? CustomFont : SystemFont); + emit changed(); +} + +#include "moc_dolphinfontrequester.cpp" diff --git a/dolphin/src/settings/viewmodes/dolphinfontrequester.h b/dolphin/src/settings/viewmodes/dolphinfontrequester.h new file mode 100644 index 00000000..ce10b319 --- /dev/null +++ b/dolphin/src/settings/viewmodes/dolphinfontrequester.h @@ -0,0 +1,75 @@ +/*************************************************************************** + * Copyright (C) 2008 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef DOLPHINFONTREQUESTER_H +#define DOLPHINFONTREQUESTER_H + +#include +#include + +class KComboBox; +#include + +/** + * @brief Allows to select between using the system font or a custom font. + */ +class DolphinFontRequester : public QWidget +{ + Q_OBJECT + +public: + enum Mode + { + SystemFont = 0, + CustomFont = 1 + }; + + DolphinFontRequester(QWidget* parent); + virtual ~DolphinFontRequester(); + + void setMode(Mode mode); + Mode mode() const; + + /** + * Returns the custom font (see DolphinFontRequester::customFont()), + * if the mode is \a CustomFont, otherwise the system font is + * returned. + */ + QFont currentFont() const; + + void setCustomFont(const QFont& font); + QFont customFont() const; + +signals: + /** Is emitted, if the font has been changed. */ + void changed(); + +private slots: + void openFontDialog(); + void changeMode(int index); + +private: + KComboBox* m_modeCombo; + QPushButton* m_chooseFontButton; + + Mode m_mode; + QFont m_customFont; +}; + +#endif diff --git a/dolphin/src/settings/viewmodes/viewmodesettings.cpp b/dolphin/src/settings/viewmodes/viewmodesettings.cpp new file mode 100644 index 00000000..5b9334c2 --- /dev/null +++ b/dolphin/src/settings/viewmodes/viewmodesettings.cpp @@ -0,0 +1,143 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "viewmodesettings.h" + +#include "dolphin_iconsmodesettings.h" +#include "dolphin_detailsmodesettings.h" +#include "dolphin_compactmodesettings.h" + +#define VIEWMODESETTINGS_SET_VALUE(mode, setValue, value) \ + switch (mode) { \ + case ViewModeSettings::IconsMode: IconsModeSettings::setValue(value); break; \ + case ViewModeSettings::CompactMode: CompactModeSettings::setValue(value); break; \ + case ViewModeSettings::DetailsMode: DetailsModeSettings::setValue(value); break; \ + default: Q_ASSERT(false); break; \ + } + +#define VIEWMODESETTINGS_RETURN_VALUE(mode, getValue, type) \ + type value; \ + switch (m_mode) { \ + case IconsMode: value = IconsModeSettings::getValue(); break; \ + case CompactMode: value = CompactModeSettings::getValue(); break; \ + case DetailsMode: value = DetailsModeSettings::getValue(); break; \ + default: value = IconsModeSettings::getValue(); \ + Q_ASSERT(false); \ + break; \ + } \ + return value + +ViewModeSettings::ViewModeSettings(ViewMode mode) : + m_mode(mode) +{ +} + +ViewModeSettings::~ViewModeSettings() +{ +} + +void ViewModeSettings::setIconSize(int size) const +{ + VIEWMODESETTINGS_SET_VALUE(m_mode, setIconSize, size); +} + +int ViewModeSettings::iconSize() const +{ + VIEWMODESETTINGS_RETURN_VALUE(m_mode, iconSize, int); +} + +void ViewModeSettings::setPreviewSize(int size) const +{ + VIEWMODESETTINGS_SET_VALUE(m_mode, setPreviewSize, size); +} + +int ViewModeSettings::previewSize() const +{ + VIEWMODESETTINGS_RETURN_VALUE(m_mode, previewSize, int); +} + +void ViewModeSettings::setUseSystemFont(bool flag) +{ + VIEWMODESETTINGS_SET_VALUE(m_mode, setUseSystemFont, flag); +} + +bool ViewModeSettings::useSystemFont() const +{ + VIEWMODESETTINGS_RETURN_VALUE(m_mode, useSystemFont, bool); +} + +void ViewModeSettings::setFontFamily(const QString& fontFamily) +{ + VIEWMODESETTINGS_SET_VALUE(m_mode, setFontFamily, fontFamily); +} + +QString ViewModeSettings::fontFamily() const +{ + VIEWMODESETTINGS_RETURN_VALUE(m_mode, fontFamily, QString); +} + +void ViewModeSettings::setFontSize(qreal fontSize) +{ + VIEWMODESETTINGS_SET_VALUE(m_mode, setFontSize, fontSize); +} + +qreal ViewModeSettings::fontSize() const +{ + VIEWMODESETTINGS_RETURN_VALUE(m_mode, fontSize, qreal); +} + +void ViewModeSettings::setItalicFont(bool italic) +{ + VIEWMODESETTINGS_SET_VALUE(m_mode, setItalicFont, italic); +} + +bool ViewModeSettings::italicFont() const +{ + VIEWMODESETTINGS_RETURN_VALUE(m_mode, italicFont, bool); +} + +void ViewModeSettings::setFontWeight(int fontWeight) +{ + VIEWMODESETTINGS_SET_VALUE(m_mode, setFontWeight, fontWeight); +} + +int ViewModeSettings::fontWeight() const +{ + VIEWMODESETTINGS_RETURN_VALUE(m_mode, fontWeight, int); +} + +void ViewModeSettings::readConfig() +{ + switch (m_mode) { + case ViewModeSettings::IconsMode: IconsModeSettings::self()->readConfig(); break; + case ViewModeSettings::CompactMode: CompactModeSettings::self()->readConfig(); break; + case ViewModeSettings::DetailsMode: DetailsModeSettings::self()->readConfig(); break; + default: Q_ASSERT(false); break; + } +} + +void ViewModeSettings::writeConfig() +{ + switch (m_mode) { + case ViewModeSettings::IconsMode: IconsModeSettings::self()->writeConfig(); break; + case ViewModeSettings::CompactMode: CompactModeSettings::self()->writeConfig(); break; + case ViewModeSettings::DetailsMode: DetailsModeSettings::self()->writeConfig(); break; + default: Q_ASSERT(false); break; + } +} diff --git a/dolphin/src/settings/viewmodes/viewmodesettings.h b/dolphin/src/settings/viewmodes/viewmodesettings.h new file mode 100644 index 00000000..be41ae28 --- /dev/null +++ b/dolphin/src/settings/viewmodes/viewmodesettings.h @@ -0,0 +1,70 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef VIEWMODESETTINGS_H +#define VIEWMODESETTINGS_H + +#include + +/** + * @short Helper class for accessing similar properties of IconsModeSettings, + * CompactModeSettings and DetailsModeSettings. + */ +class ViewModeSettings +{ +public: + enum ViewMode + { + IconsMode, + CompactMode, + DetailsMode + }; + + ViewModeSettings(ViewMode mode); + virtual ~ViewModeSettings(); + + void setIconSize(int size) const; + int iconSize() const; + + void setPreviewSize(int size) const; + int previewSize() const; + + void setUseSystemFont(bool flag); + bool useSystemFont() const; + + void setFontFamily(const QString& fontFamily); + QString fontFamily() const; + + void setFontSize(qreal fontSize); + qreal fontSize() const; + + void setItalicFont(bool italic); + bool italicFont() const; + + void setFontWeight(int fontWeight); + int fontWeight() const; + + void readConfig(); + void writeConfig(); + +private: + ViewMode m_mode; +}; + +#endif diff --git a/dolphin/src/settings/viewmodes/viewsettingspage.cpp b/dolphin/src/settings/viewmodes/viewsettingspage.cpp new file mode 100644 index 00000000..be42b70a --- /dev/null +++ b/dolphin/src/settings/viewmodes/viewsettingspage.cpp @@ -0,0 +1,83 @@ +/*************************************************************************** + * Copyright (C) 2006 by Peter Penz * + * peter.penz@gmx.at * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "viewsettingspage.h" + +#include +#include "viewsettingstab.h" + +#include + +#include +#include +#include +#include + +ViewSettingsPage::ViewSettingsPage(QWidget* parent) : + SettingsPageBase(parent), + m_tabs() +{ + QVBoxLayout* topLayout = new QVBoxLayout(this); + topLayout->setMargin(0); + topLayout->setSpacing(KDialog::spacingHint()); + + KTabWidget* tabWidget = new KTabWidget(this); + + // Initialize 'Icons' tab + ViewSettingsTab* iconsTab = new ViewSettingsTab(ViewSettingsTab::IconsMode, tabWidget); + tabWidget->addTab(iconsTab, KIcon("view-list-icons"), i18nc("@title:tab", "Icons")); + connect(iconsTab, SIGNAL(changed()), this, SIGNAL(changed())); + + // Initialize 'Compact' tab + ViewSettingsTab* compactTab = new ViewSettingsTab(ViewSettingsTab::CompactMode, tabWidget); + tabWidget->addTab(compactTab, KIcon("view-list-details"), i18nc("@title:tab", "Compact")); + connect(compactTab, SIGNAL(changed()), this, SIGNAL(changed())); + + // Initialize 'Details' tab + ViewSettingsTab* detailsTab = new ViewSettingsTab(ViewSettingsTab::DetailsMode, tabWidget); + tabWidget->addTab(detailsTab, KIcon("view-list-tree"), i18nc("@title:tab", "Details")); + connect(detailsTab, SIGNAL(changed()), this, SIGNAL(changed())); + + m_tabs.append(iconsTab); + m_tabs.append(compactTab); + m_tabs.append(detailsTab); + + topLayout->addWidget(tabWidget, 0, 0); +} + +ViewSettingsPage::~ViewSettingsPage() +{ +} + +void ViewSettingsPage::applySettings() +{ + foreach (ViewSettingsTab* tab, m_tabs) { + tab->applySettings(); + } +} + +void ViewSettingsPage::restoreDefaults() +{ + foreach (ViewSettingsTab* tab, m_tabs) { + tab->restoreDefaultSettings(); + } +} + +#include "moc_viewsettingspage.cpp" diff --git a/dolphin/src/settings/viewmodes/viewsettingspage.h b/dolphin/src/settings/viewmodes/viewsettingspage.h new file mode 100644 index 00000000..3411a260 --- /dev/null +++ b/dolphin/src/settings/viewmodes/viewsettingspage.h @@ -0,0 +1,52 @@ +/*************************************************************************** + * Copyright (C) 2006 by Peter Penz * + * peter.penz@gmx.at * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ +#ifndef VIEWSETTINGSPAGE_H +#define VIEWSETTINGSPAGE_H + +#include + +class ViewSettingsTab; +#include + +/** + * @brief Page for the 'View' settings of the Dolphin settings dialog. + * + * The views settings allow to set the properties for the icons mode, + * the details mode and the column mode. + */ +class ViewSettingsPage : public SettingsPageBase +{ + Q_OBJECT + +public: + ViewSettingsPage(QWidget* parent); + virtual ~ViewSettingsPage(); + + /** @see SettingsPageBase::applySettings() */ + virtual void applySettings(); + + /** @see SettingsPageBase::restoreDefaults() */ + virtual void restoreDefaults(); + +private: + QList m_tabs; +}; + +#endif diff --git a/dolphin/src/settings/viewmodes/viewsettingstab.cpp b/dolphin/src/settings/viewmodes/viewsettingstab.cpp new file mode 100644 index 00000000..aad3cb2e --- /dev/null +++ b/dolphin/src/settings/viewmodes/viewsettingstab.cpp @@ -0,0 +1,292 @@ +/*************************************************************************** + * Copyright (C) 2008-2011 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "viewsettingstab.h" + +#include "dolphinfontrequester.h" +#include "dolphin_compactmodesettings.h" +#include "dolphin_detailsmodesettings.h" +#include "dolphin_iconsmodesettings.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +ViewSettingsTab::ViewSettingsTab(Mode mode, QWidget* parent) : + QWidget(parent), + m_mode(mode), + m_defaultSizeSlider(0), + m_previewSizeSlider(0), + m_fontRequester(0), + m_widthBox(0), + m_maxLinesBox(0), + m_expandableFolders(0) +{ + QVBoxLayout* topLayout = new QVBoxLayout(this); + + // Create "Icon Size" group + QGroupBox* iconSizeGroup = new QGroupBox(this); + iconSizeGroup->setTitle(i18nc("@title:group", "Icon Size")); + + const int minRange = ZoomLevelInfo::minimumLevel(); + const int maxRange = ZoomLevelInfo::maximumLevel(); + + QLabel* defaultLabel = new QLabel(i18nc("@label:listbox", "Default:"), this); + m_defaultSizeSlider = new QSlider(Qt::Horizontal, this); + m_defaultSizeSlider->setPageStep(1); + m_defaultSizeSlider->setTickPosition(QSlider::TicksBelow); + m_defaultSizeSlider->setRange(minRange, maxRange); + connect(m_defaultSizeSlider, SIGNAL(valueChanged(int)), + this, SLOT(slotDefaultSliderMoved(int))); + + QLabel* previewLabel = new QLabel(i18nc("@label:listbox", "Preview:"), this); + m_previewSizeSlider = new QSlider(Qt::Horizontal, this); + m_previewSizeSlider->setPageStep(1); + m_previewSizeSlider->setTickPosition(QSlider::TicksBelow); + m_previewSizeSlider->setRange(minRange, maxRange); + connect(m_previewSizeSlider, SIGNAL(valueChanged(int)), + this, SLOT(slotPreviewSliderMoved(int))); + + QGridLayout* layout = new QGridLayout(iconSizeGroup); + layout->addWidget(defaultLabel, 0, 0, Qt::AlignRight); + layout->addWidget(m_defaultSizeSlider, 0, 1); + layout->addWidget(previewLabel, 1, 0, Qt::AlignRight); + layout->addWidget(m_previewSizeSlider, 1, 1); + + // Create "Text" group + QGroupBox* textGroup = new QGroupBox(i18nc("@title:group", "Text"), this); + + QLabel* fontLabel = new QLabel(i18nc("@label:listbox", "Font:"), textGroup); + m_fontRequester = new DolphinFontRequester(textGroup); + + QGridLayout* textGroupLayout = new QGridLayout(textGroup); + textGroupLayout->addWidget(fontLabel, 0, 0, Qt::AlignRight); + textGroupLayout->addWidget(m_fontRequester, 0, 1); + + switch (m_mode) { + case IconsMode: { + QLabel* widthLabel = new QLabel(i18nc("@label:listbox", "Width:"), textGroup); + m_widthBox = new KComboBox(textGroup); + m_widthBox->addItem(i18nc("@item:inlistbox Text width", "Small")); + m_widthBox->addItem(i18nc("@item:inlistbox Text width", "Medium")); + m_widthBox->addItem(i18nc("@item:inlistbox Text width", "Large")); + m_widthBox->addItem(i18nc("@item:inlistbox Text width", "Huge")); + + QLabel* maxLinesLabel = new QLabel(i18nc("@label:listbox", "Maximum lines:"), textGroup); + m_maxLinesBox = new KComboBox(textGroup); + m_maxLinesBox->addItem(i18nc("@item:inlistbox Maximum lines", "Unlimited")); + m_maxLinesBox->addItem(i18nc("@item:inlistbox Maximum lines", "1")); + m_maxLinesBox->addItem(i18nc("@item:inlistbox Maximum lines", "2")); + m_maxLinesBox->addItem(i18nc("@item:inlistbox Maximum lines", "3")); + m_maxLinesBox->addItem(i18nc("@item:inlistbox Maximum lines", "4")); + m_maxLinesBox->addItem(i18nc("@item:inlistbox Maximum lines", "5")); + + textGroupLayout->addWidget(widthLabel, 2, 0, Qt::AlignRight); + textGroupLayout->addWidget(m_widthBox, 2, 1); + textGroupLayout->addWidget(maxLinesLabel, 3, 0, Qt::AlignRight); + textGroupLayout->addWidget(m_maxLinesBox, 3, 1); + break; + } + case CompactMode: { + QLabel* maxWidthLabel = new QLabel(i18nc("@label:listbox", "Maximum width:"), textGroup); + m_widthBox = new KComboBox(textGroup); + m_widthBox->addItem(i18nc("@item:inlistbox Maximum width", "Unlimited")); + m_widthBox->addItem(i18nc("@item:inlistbox Maximum width", "Small")); + m_widthBox->addItem(i18nc("@item:inlistbox Maximum width", "Medium")); + m_widthBox->addItem(i18nc("@item:inlistbox Maximum width", "Large")); + + textGroupLayout->addWidget(maxWidthLabel, 2, 0, Qt::AlignRight); + textGroupLayout->addWidget(m_widthBox, 2, 1); + break; + } + case DetailsMode: + m_expandableFolders = new QCheckBox(i18nc("@option:check", "Expandable folders"), this); + break; + default: + break; + } + + topLayout->addWidget(iconSizeGroup); + topLayout->addWidget(textGroup); + topLayout->addWidget(m_expandableFolders); + topLayout->addStretch(1); + + loadSettings(); + + connect(m_defaultSizeSlider, SIGNAL(valueChanged(int)), this, SIGNAL(changed())); + connect(m_previewSizeSlider, SIGNAL(valueChanged(int)), this, SIGNAL(changed())); + connect(m_fontRequester, SIGNAL(changed()), this, SIGNAL(changed())); + + switch (m_mode) { + case IconsMode: + connect(m_widthBox, SIGNAL(currentIndexChanged(int)), this, SIGNAL(changed())); + connect(m_maxLinesBox, SIGNAL(currentIndexChanged(int)), this, SIGNAL(changed())); + break; + case CompactMode: + connect(m_widthBox, SIGNAL(currentIndexChanged(int)), this, SIGNAL(changed())); + break; + case DetailsMode: + connect(m_expandableFolders, SIGNAL(toggled(bool)), this, SIGNAL(changed())); + break; + default: + break; + } +} + +ViewSettingsTab::~ViewSettingsTab() +{ +} + +void ViewSettingsTab::applySettings() +{ + const QFont font = m_fontRequester->currentFont(); + const bool useSystemFont = (m_fontRequester->mode() == DolphinFontRequester::SystemFont); + + switch (m_mode) { + case IconsMode: + IconsModeSettings::setTextWidthIndex(m_widthBox->currentIndex()); + IconsModeSettings::setMaximumTextLines(m_maxLinesBox->currentIndex()); + break; + case CompactMode: + CompactModeSettings::setMaximumTextWidthIndex(m_widthBox->currentIndex()); + break; + case DetailsMode: + DetailsModeSettings::setExpandableFolders(m_expandableFolders->isChecked()); + break; + default: + break; + } + + ViewModeSettings settings(viewMode()); + + const int iconSize = ZoomLevelInfo::iconSizeForZoomLevel(m_defaultSizeSlider->value()); + const int previewSize = ZoomLevelInfo::iconSizeForZoomLevel(m_previewSizeSlider->value()); + settings.setIconSize(iconSize); + settings.setPreviewSize(previewSize); + + settings.setUseSystemFont(useSystemFont); + settings.setFontFamily(font.family()); + settings.setFontSize(font.pointSizeF()); + settings.setItalicFont(font.italic()); + settings.setFontWeight(font.weight()); + + settings.writeConfig(); +} + +void ViewSettingsTab::restoreDefaultSettings() +{ + KConfigSkeleton* settings = 0; + switch (m_mode) { + case IconsMode: settings = IconsModeSettings::self(); break; + case CompactMode: settings = CompactModeSettings::self(); break; + case DetailsMode: settings = DetailsModeSettings::self(); break; + default: Q_ASSERT(false); break; + } + + settings->useDefaults(true); + loadSettings(); + settings->useDefaults(false); +} + +void ViewSettingsTab::loadSettings() +{ + switch (m_mode) { + case IconsMode: + m_widthBox->setCurrentIndex(IconsModeSettings::textWidthIndex()); + m_maxLinesBox->setCurrentIndex(IconsModeSettings::maximumTextLines()); + break; + case CompactMode: + m_widthBox->setCurrentIndex(CompactModeSettings::maximumTextWidthIndex()); + break; + case DetailsMode: + m_expandableFolders->setChecked(DetailsModeSettings::expandableFolders()); + break; + default: + break; + } + + ViewModeSettings settings(viewMode()); + settings.readConfig(); + + const QSize iconSize(settings.iconSize(), settings.iconSize()); + m_defaultSizeSlider->setValue(ZoomLevelInfo::zoomLevelForIconSize(iconSize)); + + const QSize previewSize(settings.previewSize(), settings.previewSize()); + m_previewSizeSlider->setValue(ZoomLevelInfo::zoomLevelForIconSize(previewSize)); + + m_fontRequester->setMode(settings.useSystemFont() + ? DolphinFontRequester::SystemFont + : DolphinFontRequester::CustomFont); + + QFont font(settings.fontFamily(), qRound(settings.fontSize())); + font.setItalic(settings.italicFont()); + font.setWeight(settings.fontWeight()); + font.setPointSizeF(settings.fontSize()); + m_fontRequester->setCustomFont(font); +} + +ViewModeSettings::ViewMode ViewSettingsTab::viewMode() const +{ + ViewModeSettings::ViewMode mode; + + switch (m_mode) { + case ViewSettingsTab::IconsMode: mode = ViewModeSettings::IconsMode; break; + case ViewSettingsTab::CompactMode: mode = ViewModeSettings::CompactMode; break; + case ViewSettingsTab::DetailsMode: mode = ViewModeSettings::DetailsMode; break; + default: mode = ViewModeSettings::IconsMode; + Q_ASSERT(false); + break; + } + + return mode; +} + + +void ViewSettingsTab::slotDefaultSliderMoved(int value) +{ + showToolTip(m_defaultSizeSlider, value); +} + +void ViewSettingsTab::slotPreviewSliderMoved(int value) +{ + showToolTip(m_previewSizeSlider, value); +} + +void ViewSettingsTab::showToolTip(QSlider* slider, int value) +{ + const int size = ZoomLevelInfo::iconSizeForZoomLevel(value); + slider->setToolTip(i18ncp("@info:tooltip", "Size: 1 pixel", "Size: %1 pixels", size)); + if (!slider->isVisible()) { + return; + } + QPoint global = slider->rect().topLeft(); + global.ry() += slider->height() / 2; + QHelpEvent toolTipEvent(QEvent::ToolTip, QPoint(0, 0), slider->mapToGlobal(global)); + QApplication::sendEvent(slider, &toolTipEvent); +} +#include "moc_viewsettingstab.cpp" diff --git a/dolphin/src/settings/viewmodes/viewsettingstab.h b/dolphin/src/settings/viewmodes/viewsettingstab.h new file mode 100644 index 00000000..e86e5722 --- /dev/null +++ b/dolphin/src/settings/viewmodes/viewsettingstab.h @@ -0,0 +1,76 @@ +/*************************************************************************** + * Copyright (C) 2008-2011 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef VIEWSETTINGSTAB_H +#define VIEWSETTINGSTAB_H + +#include +#include + +class DolphinFontRequester; +class KComboBox; +#include +#include + +/** + * @brief Represents one tab of the view-settings page. + */ +class ViewSettingsTab : public QWidget +{ + Q_OBJECT + +public: + enum Mode + { + IconsMode, + CompactMode, + DetailsMode + }; + + explicit ViewSettingsTab(Mode mode, QWidget* parent = 0); + virtual ~ViewSettingsTab(); + + void applySettings(); + void restoreDefaultSettings(); + +signals: + void changed(); + +private slots: + + void slotDefaultSliderMoved(int value); + void slotPreviewSliderMoved(int value); +private: + void loadSettings(); + void showToolTip(QSlider* slider, int value); + + ViewModeSettings::ViewMode viewMode() const; + +private: + Mode m_mode; + QSlider* m_defaultSizeSlider; + QSlider* m_previewSizeSlider; + + DolphinFontRequester* m_fontRequester; + KComboBox* m_widthBox; + KComboBox* m_maxLinesBox; + QCheckBox* m_expandableFolders; +}; + +#endif diff --git a/dolphin/src/settings/viewpropertiesdialog.cpp b/dolphin/src/settings/viewpropertiesdialog.cpp new file mode 100644 index 00000000..9fedc4b3 --- /dev/null +++ b/dolphin/src/settings/viewpropertiesdialog.cpp @@ -0,0 +1,409 @@ +/*************************************************************************** + * Copyright (C) 2006 by Peter Penz * + * peter.penz@gmx.at * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "viewpropertiesdialog.h" + +#include "additionalinfodialog.h" +#include "kitemviews/kfileitemmodel.h" +#include "views/dolphinview.h" +#include "dolphin_generalsettings.h" +#include "dolphin_iconsmodesettings.h" +#include "viewpropsprogressinfo.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +ViewPropertiesDialog::ViewPropertiesDialog(DolphinView* dolphinView) : + KDialog(dolphinView), + m_isDirty(false), + m_dolphinView(dolphinView), + m_viewProps(0), + m_viewMode(0), + m_sortOrder(0), + m_sorting(0), + m_sortFoldersFirst(0), + m_previewsShown(0), + m_showInGroups(0), + m_showHiddenFiles(0), + m_additionalInfo(0), + m_applyToCurrentFolder(0), + m_applyToSubFolders(0), + m_applyToAllFolders(0), + m_useAsDefault(0) +{ + Q_ASSERT(dolphinView); + const bool useGlobalViewProps = GeneralSettings::globalViewProps(); + + setCaption(i18nc("@title:window", "View Properties")); + setButtons(KDialog::Ok | KDialog::Cancel | KDialog::Apply); + + const KUrl& url = dolphinView->url(); + m_viewProps = new ViewProperties(url); + m_viewProps->setAutoSaveEnabled(false); + + QWidget* main = new QWidget(); + QVBoxLayout* topLayout = new QVBoxLayout(); + + // create 'Properties' group containing view mode, sorting, sort order and show hidden files + QWidget* propsBox = main; + if (!useGlobalViewProps) { + propsBox = new QGroupBox(i18nc("@title:group", "Properties"), main); + } + + QWidget* propsGrid = new QWidget(); + + QLabel* viewModeLabel = new QLabel(i18nc("@label:listbox", "View mode:"), propsGrid); + m_viewMode = new KComboBox(propsGrid); + m_viewMode->addItem(KIcon("view-list-icons"), i18nc("@item:inlistbox", "Icons"), DolphinView::IconsView); + m_viewMode->addItem(KIcon("view-list-details"), i18nc("@item:inlistbox", "Compact"), DolphinView::CompactView); + m_viewMode->addItem(KIcon("view-list-tree"), i18nc("@item:inlistbox", "Details"), DolphinView::DetailsView); + + QLabel* sortingLabel = new QLabel(i18nc("@label:listbox", "Sorting:"), propsGrid); + QWidget* sortingBox = new QWidget(propsGrid); + + m_sortOrder = new KComboBox(sortingBox); + m_sortOrder->addItem(i18nc("@item:inlistbox Sort", "Ascending")); + m_sortOrder->addItem(i18nc("@item:inlistbox Sort", "Descending")); + + m_sorting = new KComboBox(sortingBox); + const QList rolesInfo = KFileItemModel::rolesInformation(); + foreach (const KFileItemModel::RoleInfo& info, rolesInfo) { + m_sorting->addItem(info.translation, info.role); + } + + m_sortFoldersFirst = new QCheckBox(i18nc("@option:check", "Show folders first")); + m_previewsShown = new QCheckBox(i18nc("@option:check", "Show preview")); + m_showInGroups = new QCheckBox(i18nc("@option:check", "Show in groups")); + m_showHiddenFiles = new QCheckBox(i18nc("@option:check", "Show hidden files")); + + m_additionalInfo = new QPushButton(i18nc("@action:button", "Additional Information")); + + QHBoxLayout* sortingLayout = new QHBoxLayout(); + sortingLayout->setMargin(0); + sortingLayout->addWidget(m_sortOrder); + sortingLayout->addWidget(m_sorting); + sortingBox->setLayout(sortingLayout); + + QGridLayout* propsGridLayout = new QGridLayout(propsGrid); + propsGridLayout->addWidget(viewModeLabel, 0, 0, Qt::AlignRight); + propsGridLayout->addWidget(m_viewMode, 0, 1); + propsGridLayout->addWidget(sortingLabel, 1, 0, Qt::AlignRight); + propsGridLayout->addWidget(sortingBox, 1, 1); + + QVBoxLayout* propsBoxLayout = new QVBoxLayout(propsBox); + propsBoxLayout->addWidget(propsGrid); + propsBoxLayout->addWidget(m_sortFoldersFirst); + propsBoxLayout->addWidget(m_previewsShown); + propsBoxLayout->addWidget(m_showInGroups); + propsBoxLayout->addWidget(m_showHiddenFiles); + propsBoxLayout->addWidget(m_additionalInfo); + + topLayout->addWidget(propsBox); + + connect(m_viewMode, SIGNAL(currentIndexChanged(int)), + this, SLOT(slotViewModeChanged(int))); + connect(m_sorting, SIGNAL(currentIndexChanged(int)), + this, SLOT(slotSortingChanged(int))); + connect(m_sortOrder, SIGNAL(currentIndexChanged(int)), + this, SLOT(slotSortOrderChanged(int))); + connect(m_additionalInfo, SIGNAL(clicked()), + this, SLOT(configureAdditionalInfo())); + connect(m_sortFoldersFirst, SIGNAL(clicked()), + this, SLOT(slotSortFoldersFirstChanged())); + connect(m_previewsShown, SIGNAL(clicked()), + this, SLOT(slotShowPreviewChanged())); + connect(m_showInGroups, SIGNAL(clicked()), + this, SLOT(slotGroupedSortingChanged())); + connect(m_showHiddenFiles, SIGNAL(clicked()), + this, SLOT(slotShowHiddenFilesChanged())); + + connect(this, SIGNAL(okClicked()), this, SLOT(slotOk())); + connect(this, SIGNAL(applyClicked()), this, SLOT(slotApply())); + + // Only show the following settings if the view properties are remembered + // for each directory: + if (!useGlobalViewProps) { + // create 'Apply View Properties To' group + QGroupBox* applyBox = new QGroupBox(i18nc("@title:group", "Apply View Properties To"), main); + + m_applyToCurrentFolder = new QRadioButton(i18nc("@option:radio Apply View Properties To", + "Current folder"), applyBox); + m_applyToCurrentFolder->setChecked(true); + m_applyToSubFolders = new QRadioButton(i18nc("@option:radio Apply View Properties To", + "Current folder including all sub-folders"), applyBox); + m_applyToAllFolders = new QRadioButton(i18nc("@option:radio Apply View Properties To", + "All folders"), applyBox); + + QButtonGroup* applyGroup = new QButtonGroup(this); + applyGroup->addButton(m_applyToCurrentFolder); + applyGroup->addButton(m_applyToSubFolders); + applyGroup->addButton(m_applyToAllFolders); + + QVBoxLayout* applyBoxLayout = new QVBoxLayout(applyBox); + applyBoxLayout->addWidget(m_applyToCurrentFolder); + applyBoxLayout->addWidget(m_applyToSubFolders); + applyBoxLayout->addWidget(m_applyToAllFolders); + + m_useAsDefault = new QCheckBox(i18nc("@option:check", "Use these view properties as default"), main); + + topLayout->addWidget(applyBox); + topLayout->addWidget(m_useAsDefault); + + connect(m_applyToCurrentFolder, SIGNAL(clicked(bool)), + this, SLOT(markAsDirty(bool))); + connect(m_applyToSubFolders, SIGNAL(clicked(bool)), + this, SLOT(markAsDirty(bool))); + connect(m_applyToAllFolders, SIGNAL(clicked(bool)), + this, SLOT(markAsDirty(bool))); + connect(m_useAsDefault, SIGNAL(clicked(bool)), + this, SLOT(markAsDirty(bool))); + } + + main->setLayout(topLayout); + setMainWidget(main); + + const KConfigGroup dialogConfig(KSharedConfig::openConfig("dolphinrc"), + "ViewPropertiesDialog"); + restoreDialogSize(dialogConfig); + + loadSettings(); +} + +ViewPropertiesDialog::~ViewPropertiesDialog() +{ + m_isDirty = false; + delete m_viewProps; + m_viewProps = 0; + + KConfigGroup dialogConfig(KSharedConfig::openConfig("dolphinrc"), + "ViewPropertiesDialog"); + saveDialogSize(dialogConfig, KConfigBase::Persistent); +} + +void ViewPropertiesDialog::slotOk() +{ + applyViewProperties(); + accept(); +} + +void ViewPropertiesDialog::slotApply() +{ + applyViewProperties(); + markAsDirty(false); +} + +void ViewPropertiesDialog::slotViewModeChanged(int index) +{ + const QVariant itemData = m_viewMode->itemData(index); + const DolphinView::Mode viewMode = static_cast(itemData.toInt()); + m_viewProps->setViewMode(viewMode); + markAsDirty(true); +} + +void ViewPropertiesDialog::slotSortingChanged(int index) +{ + const QByteArray role = m_sorting->itemData(index).toByteArray(); + m_viewProps->setSortRole(role); + markAsDirty(true); +} + +void ViewPropertiesDialog::slotSortOrderChanged(int index) +{ + const Qt::SortOrder sortOrder = (index == 0) ? Qt::AscendingOrder : Qt::DescendingOrder; + m_viewProps->setSortOrder(sortOrder); + markAsDirty(true); +} + +void ViewPropertiesDialog::slotGroupedSortingChanged() +{ + m_viewProps->setGroupedSorting(m_showInGroups->isChecked()); + markAsDirty(true); +} + +void ViewPropertiesDialog::slotSortFoldersFirstChanged() +{ + const bool foldersFirst = m_sortFoldersFirst->isChecked(); + m_viewProps->setSortFoldersFirst(foldersFirst); + markAsDirty(true); +} + +void ViewPropertiesDialog::slotShowPreviewChanged() +{ + const bool show = m_previewsShown->isChecked(); + m_viewProps->setPreviewsShown(show); + markAsDirty(true); +} + +void ViewPropertiesDialog::slotShowHiddenFilesChanged() +{ + const bool show = m_showHiddenFiles->isChecked(); + m_viewProps->setHiddenFilesShown(show); + markAsDirty(true); +} + +void ViewPropertiesDialog::markAsDirty(bool isDirty) +{ + m_isDirty = isDirty; + enableButtonApply(isDirty); +} + +void ViewPropertiesDialog::configureAdditionalInfo() +{ + QList visibleRoles = m_viewProps->visibleRoles(); + const bool useDefaultRoles = (m_viewProps->viewMode() == DolphinView::DetailsView) && visibleRoles.isEmpty(); + if (useDefaultRoles) { + // Using the details view without any additional information (-> additional column) + // makes no sense and leads to a usability problem as no viewport area is available + // anymore. Hence as fallback provide at least a size and date column. + visibleRoles.clear(); + visibleRoles.append("text"); + visibleRoles.append("size"); + visibleRoles.append("date"); + m_viewProps->setVisibleRoles(visibleRoles); + } + + QPointer dialog = new AdditionalInfoDialog(this, visibleRoles); + if (dialog->exec() == QDialog::Accepted) { + m_viewProps->setVisibleRoles(dialog->visibleRoles()); + markAsDirty(true); + } + delete dialog; +} + +void ViewPropertiesDialog::applyViewProperties() +{ + // if nothing changed in the dialog, we have nothing to apply + if (!m_isDirty) { + return; + } + + const bool applyToSubFolders = m_applyToSubFolders && m_applyToSubFolders->isChecked(); + if (applyToSubFolders) { + const QString text(i18nc("@info", "The view properties of all sub-folders will be changed. Do you want to continue?")); + if (KMessageBox::questionYesNo(this, text) == KMessageBox::No) { + return; + } + + ViewPropsProgressInfo* info = new ViewPropsProgressInfo(m_dolphinView, + m_dolphinView->url(), + *m_viewProps); + info->setAttribute(Qt::WA_DeleteOnClose); + info->setWindowModality(Qt::NonModal); + info->show(); + } + + const bool applyToAllFolders = m_applyToAllFolders && m_applyToAllFolders->isChecked(); + + // If the user selected 'Apply To All Folders' the view properties implicitely + // are also used as default for new folders. + const bool useAsDefault = applyToAllFolders || (m_useAsDefault && m_useAsDefault->isChecked()); + if (useAsDefault) { + // For directories where no .directory file is available, the .directory + // file stored for the global view properties is used as fallback. To update + // this file we temporary turn on the global view properties mode. + Q_ASSERT(!GeneralSettings::globalViewProps()); + + GeneralSettings::setGlobalViewProps(true); + ViewProperties defaultProps(m_dolphinView->url()); + defaultProps.setDirProperties(*m_viewProps); + defaultProps.save(); + GeneralSettings::setGlobalViewProps(false); + } + + if (applyToAllFolders) { + const QString text(i18nc("@info", "The view properties of all folders will be changed. Do you want to continue?")); + if (KMessageBox::questionYesNo(this, text) == KMessageBox::No) { + return; + } + + // Updating the global view properties time stamp in the general settings makes + // all existing viewproperties invalid, as they have a smaller time stamp. + GeneralSettings* settings = GeneralSettings::self(); + settings->setViewPropsTimestamp(QDateTime::currentDateTime()); + settings->writeConfig(); + } + + m_dolphinView->setMode(m_viewProps->viewMode()); + m_dolphinView->setSortRole(m_viewProps->sortRole()); + m_dolphinView->setSortOrder(m_viewProps->sortOrder()); + m_dolphinView->setSortFoldersFirst(m_viewProps->sortFoldersFirst()); + m_dolphinView->setGroupedSorting(m_viewProps->groupedSorting()); + m_dolphinView->setVisibleRoles(m_viewProps->visibleRoles()); + m_dolphinView->setPreviewsShown(m_viewProps->previewsShown()); + m_dolphinView->setHiddenFilesShown(m_viewProps->hiddenFilesShown()); + + m_viewProps->save(); + + markAsDirty(false); +} + +void ViewPropertiesDialog::loadSettings() +{ + // Load view mode + switch (m_viewProps->viewMode()) { + case DolphinView::IconsView: m_viewMode->setCurrentIndex(0); break; + case DolphinView::CompactView: m_viewMode->setCurrentIndex(1); break; + case DolphinView::DetailsView: m_viewMode->setCurrentIndex(2); break; + default: break; + } + + // Load sort order and sorting + const int sortOrderIndex = (m_viewProps->sortOrder() == Qt::AscendingOrder) ? 0 : 1; + m_sortOrder->setCurrentIndex(sortOrderIndex); + + const QList rolesInfo = KFileItemModel::rolesInformation(); + int sortRoleIndex = 0; + for (int i = 0; i < rolesInfo.count(); ++i) { + if (rolesInfo[i].role == m_viewProps->sortRole()) { + sortRoleIndex = i; + break; + } + } + m_sorting->setCurrentIndex(sortRoleIndex); + + m_sortFoldersFirst->setChecked(m_viewProps->sortFoldersFirst()); + + // Load show preview, show in groups and show hidden files settings + m_previewsShown->setChecked(m_viewProps->previewsShown()); + m_showInGroups->setChecked(m_viewProps->groupedSorting()); + m_showHiddenFiles->setChecked(m_viewProps->hiddenFilesShown()); + markAsDirty(false); +} + +#include "moc_viewpropertiesdialog.cpp" diff --git a/dolphin/src/settings/viewpropertiesdialog.h b/dolphin/src/settings/viewpropertiesdialog.h new file mode 100644 index 00000000..8ff3b37c --- /dev/null +++ b/dolphin/src/settings/viewpropertiesdialog.h @@ -0,0 +1,86 @@ +/*************************************************************************** + * Copyright (C) 2006 by Peter Penz * + * peter.penz@gmx.at * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef VIEWPROPERTIESDIALOG_H +#define VIEWPROPERTIESDIALOG_H + +#include "dolphinprivate_export.h" + +#include + +#include +class KComboBox; +#include +#include +class ViewProperties; +class DolphinView; + +/** + * @brief Dialog for changing the current view properties of a directory. + * + * It is possible to specify the view mode, the sorting order, whether hidden files + * and previews should be shown. The properties can be assigned to the current folder, + * or recursively to all sub folders. + */ +class DOLPHINPRIVATE_EXPORT ViewPropertiesDialog : public KDialog +{ + Q_OBJECT + +public: + explicit ViewPropertiesDialog(DolphinView* dolphinView); + virtual ~ViewPropertiesDialog(); + +private slots: + void slotOk(); + void slotApply(); + void slotViewModeChanged(int index); + void slotSortingChanged(int index); + void slotSortOrderChanged(int index); + void slotGroupedSortingChanged(); + void slotSortFoldersFirstChanged(); + void slotShowPreviewChanged(); + void slotShowHiddenFilesChanged(); + void markAsDirty(bool isDirty); + void configureAdditionalInfo(); + +private: + void applyViewProperties(); + void loadSettings(); + +private: + bool m_isDirty; + DolphinView* m_dolphinView; + ViewProperties* m_viewProps; + + KComboBox* m_viewMode; + KComboBox* m_sortOrder; + KComboBox* m_sorting; + QCheckBox* m_sortFoldersFirst; + QCheckBox* m_previewsShown; + QCheckBox* m_showInGroups; + QCheckBox* m_showHiddenFiles; + QPushButton* m_additionalInfo; + QRadioButton* m_applyToCurrentFolder; + QRadioButton* m_applyToSubFolders; + QRadioButton* m_applyToAllFolders; + QCheckBox* m_useAsDefault; +}; + +#endif diff --git a/dolphin/src/settings/viewpropsprogressinfo.cpp b/dolphin/src/settings/viewpropsprogressinfo.cpp new file mode 100644 index 00000000..5a464a43 --- /dev/null +++ b/dolphin/src/settings/viewpropsprogressinfo.cpp @@ -0,0 +1,148 @@ +/*************************************************************************** + * Copyright (C) 2006 by Peter Penz * + * peter.penz@gmx.at * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "viewpropsprogressinfo.h" +#include "applyviewpropsjob.h" + +#include +#include +#include +#include + +#include +#include + +#include + +ViewPropsProgressInfo::ViewPropsProgressInfo(QWidget* parent, + const KUrl& dir, + const ViewProperties& viewProps) : + KDialog(parent), + m_dir(dir), + m_viewProps(0), + m_label(0), + m_progressBar(0), + m_dirSizeJob(0), + m_applyViewPropsJob(0), + m_timer(0) +{ + const QSize minSize = minimumSize(); + setMinimumSize(QSize(320, minSize.height())); + + setCaption(i18nc("@title:window", "Applying View Properties")); + setButtons(KDialog::Cancel); + + m_viewProps = new ViewProperties(dir); + m_viewProps->setDirProperties(viewProps); + + // the view properties are stored by the ViewPropsApplierJob, so prevent + // that the view properties are saved twice: + m_viewProps->setAutoSaveEnabled(false); + + QWidget* main = new QWidget(); + QVBoxLayout* topLayout = new QVBoxLayout(); + + m_label = new QLabel(i18nc("@info:progress", "Counting folders: %1", 0), main); + m_progressBar = new QProgressBar(main); + m_progressBar->setMinimum(0); + m_progressBar->setMaximum(0); + m_progressBar->setValue(0); + + topLayout->addWidget(m_label); + topLayout->addWidget(m_progressBar); + + main->setLayout(topLayout); + setMainWidget(main); + + // Use the directory size job to count the number of directories first. This + // allows to give a progress indication for the user when applying the view + // properties later. + m_dirSizeJob = KIO::directorySize(dir); + connect(m_dirSizeJob, SIGNAL(result(KJob*)), + this, SLOT(applyViewProperties())); + + // The directory size job cannot emit any progress signal, as it is not aware + // about the total number of directories. Therefor a timer is triggered, which + // periodically updates the current directory count. + m_timer = new QTimer(this); + connect(m_timer, SIGNAL(timeout()), + this, SLOT(updateProgress())); + m_timer->start(300); + + connect(this, SIGNAL(cancelClicked()), this, SLOT(cancelApplying())); +} + +ViewPropsProgressInfo::~ViewPropsProgressInfo() +{ + delete m_viewProps; + m_viewProps = 0; +} + +void ViewPropsProgressInfo::closeEvent(QCloseEvent* event) +{ + m_timer->stop(); + m_applyViewPropsJob = 0; + KDialog::closeEvent(event); +} + +void ViewPropsProgressInfo::updateProgress() +{ + if (m_dirSizeJob) { + const int subdirs = m_dirSizeJob->totalSubdirs(); + m_label->setText(i18nc("@info:progress", "Counting folders: %1", subdirs)); + } + + if (m_applyViewPropsJob) { + const int progress = m_applyViewPropsJob->progress(); + m_progressBar->setValue(progress); + } +} + +void ViewPropsProgressInfo::applyViewProperties() +{ + if (m_dirSizeJob->error()) { + return; + } + + const int subdirs = m_dirSizeJob->totalSubdirs(); + m_label->setText(i18nc("@info:progress", "Folders: %1", subdirs)); + m_progressBar->setMaximum(subdirs); + + m_dirSizeJob = 0; + + m_applyViewPropsJob = new ApplyViewPropsJob(m_dir, *m_viewProps); + connect(m_applyViewPropsJob, SIGNAL(result(KJob*)), + this, SLOT(close())); +} + +void ViewPropsProgressInfo::cancelApplying() +{ + if (m_dirSizeJob) { + m_dirSizeJob->kill(); + m_dirSizeJob = 0; + } + + if (m_applyViewPropsJob) { + m_applyViewPropsJob->kill(); + m_applyViewPropsJob = 0; + } +} + +#include "moc_viewpropsprogressinfo.cpp" diff --git a/dolphin/src/settings/viewpropsprogressinfo.h b/dolphin/src/settings/viewpropsprogressinfo.h new file mode 100644 index 00000000..25f3f00b --- /dev/null +++ b/dolphin/src/settings/viewpropsprogressinfo.h @@ -0,0 +1,78 @@ +/*************************************************************************** + * Copyright (C) 2006 by Peter Penz * + * peter.penz@gmx.at * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ +#ifndef VIEWPROPSPROGRESSINFO_H +#define VIEWPROPSPROGRESSINFO_H + +#include +#include +#include + +class ApplyViewPropsJob; +#include +#include +#include +class ViewProperties; + +/** + * @brief Shows the progress information when applying view properties + * recursively to a given directory. + * + * It is possible to cancel the applying. In this case the already applied + * view properties won't get reverted. + */ +class ViewPropsProgressInfo : public KDialog +{ + Q_OBJECT + +public: + /** + * @param parent Parent widget of the dialog. + * @param dir Directory where the view properties should be applied to + * (including sub directories). + * @param viewProps View properties for the directory \a dir including its + * sub directories. + */ + ViewPropsProgressInfo(QWidget* parent, + const KUrl& dir, + const ViewProperties& viewProps); + + virtual ~ViewPropsProgressInfo(); + +protected: + virtual void closeEvent(QCloseEvent* event); + +private slots: + void updateProgress(); + void applyViewProperties(); + void cancelApplying(); + +private: + KUrl m_dir; + ViewProperties* m_viewProps; + + QLabel* m_label; + QProgressBar* m_progressBar; + + KIO::DirectorySizeJob* m_dirSizeJob; + ApplyViewPropsJob* m_applyViewPropsJob; + QTimer* m_timer; +}; + +#endif diff --git a/dolphin/src/statusbar/dolphinstatusbar.cpp b/dolphin/src/statusbar/dolphinstatusbar.cpp new file mode 100644 index 00000000..0fc52dd3 --- /dev/null +++ b/dolphin/src/statusbar/dolphinstatusbar.cpp @@ -0,0 +1,357 @@ +/*************************************************************************** + * Copyright (C) 2006-2012 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "dolphinstatusbar.h" + +#include "dolphin_generalsettings.h" + +#include +#include +#include +#include +#include + +#include "statusbarspaceinfo.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace { + const int ResetToDefaultTimeout = 1000; +} + +DolphinStatusBar::DolphinStatusBar(QWidget* parent) : + QWidget(parent), + m_text(), + m_defaultText(), + m_label(0), + m_spaceInfo(0), + m_zoomSlider(0), + m_progressBar(0), + m_stopButton(0), + m_progress(100), + m_showProgressBarTimer(0), + m_resetToDefaultTextTimer(0), + m_textTimestamp() +{ + // Initialize text label + m_label = new QLabel(this); + m_label->setWordWrap(true); + m_label->setTextFormat(Qt::PlainText); + m_label->installEventFilter(this); + + // Initialize zoom widget + m_zoomSlider = new QSlider(Qt::Horizontal, this); + m_zoomSlider->setPageStep(1); + m_zoomSlider->setRange(ZoomLevelInfo::minimumLevel(), ZoomLevelInfo::maximumLevel()); + + connect(m_zoomSlider, SIGNAL(valueChanged(int)), this, SIGNAL(zoomLevelChanged(int))); + connect(m_zoomSlider, SIGNAL(valueChanged(int)), this, SLOT(updateZoomSliderToolTip(int))); + connect(m_zoomSlider, SIGNAL(sliderMoved(int)), this, SLOT(showZoomSliderToolTip(int))); + + // Initialize space information + m_spaceInfo = new StatusBarSpaceInfo(this); + + // Initialize progress information + m_stopButton = new QToolButton(this); + m_stopButton->setIcon(KIcon("process-stop")); + m_stopButton->setAutoRaise(true); + m_stopButton->setToolTip(i18nc("@tooltip", "Stop loading")); + m_stopButton->hide(); + connect(m_stopButton, SIGNAL(clicked()), this, SIGNAL(stopPressed())); + + m_progressTextLabel = new QLabel(this); + m_progressTextLabel->hide(); + + m_progressBar = new QProgressBar(this); + m_progressBar->hide(); + + m_showProgressBarTimer = new QTimer(this); + m_showProgressBarTimer->setInterval(500); + m_showProgressBarTimer->setSingleShot(true); + connect(m_showProgressBarTimer, SIGNAL(timeout()), this, SLOT(updateProgressInfo())); + + m_resetToDefaultTextTimer = new QTimer(this); + m_resetToDefaultTextTimer->setInterval(ResetToDefaultTimeout); + m_resetToDefaultTextTimer->setSingleShot(true); + connect(m_resetToDefaultTextTimer, SIGNAL(timeout()), this, SLOT(slotResetToDefaultText())); + + // Initialize top layout and size policies + const int fontHeight = QFontMetrics(m_label->font()).height(); + const int zoomSliderHeight = m_zoomSlider->minimumSizeHint().height(); + const int contentHeight = qMax(fontHeight, zoomSliderHeight); + + QFontMetrics fontMetrics(m_label->font()); + + m_label->setFixedHeight(contentHeight); + m_label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + + m_zoomSlider->setMaximumWidth(fontMetrics.averageCharWidth() * 25); + + m_spaceInfo->setFixedHeight(contentHeight); + m_spaceInfo->setMaximumWidth(fontMetrics.averageCharWidth() * 25); + m_spaceInfo->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + + m_progressBar->setFixedHeight(contentHeight); + m_progressBar->setMaximumWidth(fontMetrics.averageCharWidth() * 25); + + QHBoxLayout* topLayout = new QHBoxLayout(this); + topLayout->setMargin(0); + topLayout->setSpacing(4); + topLayout->addWidget(m_label); + topLayout->addWidget(m_zoomSlider); + topLayout->addWidget(m_spaceInfo); + topLayout->addWidget(m_stopButton); + topLayout->addWidget(m_progressTextLabel); + topLayout->addWidget(m_progressBar); + + setExtensionsVisible(true); +} + +DolphinStatusBar::~DolphinStatusBar() +{ +} + +void DolphinStatusBar::setText(const QString& text) +{ + if (m_text == text) { + return; + } + + m_textTimestamp = QTime::currentTime(); + + if (text.isEmpty()) { + // Assure that the previous set text won't get + // cleared immediatelly. + m_resetToDefaultTextTimer->start(); + } else { + m_text = text; + + if (m_resetToDefaultTextTimer->isActive()) { + m_resetToDefaultTextTimer->start(); + } + + updateLabelText(); + } +} + +QString DolphinStatusBar::text() const +{ + return m_text; +} + +void DolphinStatusBar::setProgressText(const QString& text) +{ + m_progressTextLabel->setText(text); +} + +QString DolphinStatusBar::progressText() const +{ + return m_progressTextLabel->text(); +} + +void DolphinStatusBar::setProgress(int percent) +{ + // Show a busy indicator if a value < 0 is provided: + m_progressBar->setMaximum((percent < 0) ? 0 : 100); + + percent = qBound(0, percent, 100); + const bool progressRestarted = (percent < 100) && (percent < m_progress); + m_progress = percent; + if (progressRestarted && !m_progressBar->isVisible()) { + // Show the progress bar delayed: In the case if 100 % are reached within + // a short time, no progress bar will be shown at all. + m_showProgressBarTimer->start(); + } + + m_progressBar->setValue(m_progress); + if (percent == 100) { + // The end of the progress has been reached. Assure that the progress bar + // gets hidden and the extensions widgets get visible again. + m_showProgressBarTimer->stop(); + updateProgressInfo(); + } +} + +int DolphinStatusBar::progress() const +{ + return m_progress; +} + +void DolphinStatusBar::resetToDefaultText() +{ + QTime currentTime; + if (currentTime.msecsTo(m_textTimestamp) < ResetToDefaultTimeout) { + m_resetToDefaultTextTimer->start(); + } else { + m_resetToDefaultTextTimer->stop(); + slotResetToDefaultText(); + } +} + +void DolphinStatusBar::setDefaultText(const QString& text) +{ + m_defaultText = text; + updateLabelText(); +} + +QString DolphinStatusBar::defaultText() const +{ + return m_defaultText; +} + +void DolphinStatusBar::setUrl(const KUrl& url) +{ + m_spaceInfo->setUrl(url); +} + +KUrl DolphinStatusBar::url() const +{ + return m_spaceInfo->url(); +} + +void DolphinStatusBar::setZoomLevel(int zoomLevel) +{ + if (zoomLevel != m_zoomSlider->value()) { + m_zoomSlider->setValue(zoomLevel); + } +} + +int DolphinStatusBar::zoomLevel() const +{ + return m_zoomSlider->value(); +} + +void DolphinStatusBar::readSettings() +{ + setExtensionsVisible(true); +} + +void DolphinStatusBar::contextMenuEvent(QContextMenuEvent* event) +{ + Q_UNUSED(event); + + KMenu menu(this); + + QAction* showZoomSliderAction = menu.addAction(i18nc("@action:inmenu", "Show Zoom Slider")); + showZoomSliderAction->setCheckable(true); + showZoomSliderAction->setChecked(GeneralSettings::showZoomSlider()); + + QAction* showSpaceInfoAction = menu.addAction(i18nc("@action:inmenu", "Show Space Information")); + showSpaceInfoAction->setCheckable(true); + showSpaceInfoAction->setChecked(GeneralSettings::showSpaceInfo()); + + const QAction* action = menu.exec(QCursor::pos()); + if (action == showZoomSliderAction) { + const bool visible = showZoomSliderAction->isChecked(); + GeneralSettings::setShowZoomSlider(visible); + m_zoomSlider->setVisible(visible); + } else if (action == showSpaceInfoAction) { + const bool visible = showSpaceInfoAction->isChecked(); + GeneralSettings::setShowSpaceInfo(visible); + m_spaceInfo->setVisible(visible); + } +} + +bool DolphinStatusBar::eventFilter(QObject* obj, QEvent* event) +{ + if (obj == m_label && event->type() == QEvent::Resize) { + updateLabelText(); + } + return QWidget::eventFilter(obj, event); +} + +void DolphinStatusBar::showZoomSliderToolTip(int zoomLevel) +{ + updateZoomSliderToolTip(zoomLevel); + + QPoint global = m_zoomSlider->rect().topLeft(); + global.ry() += m_zoomSlider->height() / 2; + QHelpEvent toolTipEvent(QEvent::ToolTip, QPoint(0, 0), m_zoomSlider->mapToGlobal(global)); + QApplication::sendEvent(m_zoomSlider, &toolTipEvent); +} + +void DolphinStatusBar::updateProgressInfo() +{ + if (m_progress < 100) { + // Show the progress information and hide the extensions + m_stopButton->show(); + m_progressTextLabel->show(); + m_progressBar->show(); + setExtensionsVisible(false); + } else { + // Hide the progress information and show the extensions + m_stopButton->hide(); + m_progressTextLabel->hide(); + m_progressBar->hide(); + setExtensionsVisible(true); + } +} + +void DolphinStatusBar::updateLabelText() +{ + const QString text = m_text.isEmpty() ? m_defaultText : m_text; + + // Set status bar text and elide it if too long + QFontMetrics fontMetrics(m_label->font()); + const QString elidedText = fontMetrics.elidedText(text, Qt::ElideRight, m_label->width()); + m_label->setText(elidedText); + + // If the text has been elided, set the original text as tooltip + if (text != elidedText) { + m_label->setToolTip(Qt::convertFromPlainText(text)); + } else { + m_label->setToolTip(QString()); + } +} + +void DolphinStatusBar::slotResetToDefaultText() +{ + m_text.clear(); + updateLabelText(); +} + +void DolphinStatusBar::updateZoomSliderToolTip(int zoomLevel) +{ + const int size = ZoomLevelInfo::iconSizeForZoomLevel(zoomLevel); + m_zoomSlider->setToolTip(i18ncp("@info:tooltip", "Size: 1 pixel", "Size: %1 pixels", size)); +} + +void DolphinStatusBar::setExtensionsVisible(bool visible) +{ + bool showSpaceInfo = visible; + bool showZoomSlider = visible; + if (visible) { + showSpaceInfo = GeneralSettings::showSpaceInfo(); + showZoomSlider = GeneralSettings::showZoomSlider(); + } + m_spaceInfo->setVisible(showSpaceInfo); + m_zoomSlider->setVisible(showZoomSlider); +} + +#include "moc_dolphinstatusbar.cpp" diff --git a/dolphin/src/statusbar/dolphinstatusbar.h b/dolphin/src/statusbar/dolphinstatusbar.h new file mode 100644 index 00000000..699d3def --- /dev/null +++ b/dolphin/src/statusbar/dolphinstatusbar.h @@ -0,0 +1,158 @@ +/*************************************************************************** + * Copyright (C) 2006-2012 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef DOLPHINSTATUSBAR_H +#define DOLPHINSTATUSBAR_H + +#include +#include + +class KUrl; +class StatusBarSpaceInfo; +#include +#include +#include +#include +#include + +/** + * @brief Represents the statusbar of a Dolphin view. + * + * The statusbar allows to show messages, progress + * information and space-information of a disk. + */ +class DolphinStatusBar : public QWidget +{ + Q_OBJECT + +public: + DolphinStatusBar(QWidget* parent); + virtual ~DolphinStatusBar(); + + QString text() const; + + /** + * Sets the text for the progress information. + * DolphinStatusBar::setProgress() should be invoked + * afterwards each time the progress changes. + */ + void setProgressText(const QString& text); + QString progressText() const; + + /** + * Sets the progress in percent (0 - 100). The + * progress is shown delayed by 500 milliseconds: + * If the progress does reach 100 % within 500 milliseconds, + * the progress is not shown at all. + */ + void setProgress(int percent); + int progress() const; + + /** + * Replaces the text set by setText() by the text that + * has been set by setDefaultText(). It is assured that the previous + * text will be shown for at least 1 second. DolphinStatusBar::text() + * will return an empty string after the reset has been done. + */ + void resetToDefaultText(); + + /** + * Sets the default text, which is shown if the status bar + * is rest by DolphinStatusBar::resetToDefaultText(). + */ + void setDefaultText(const QString& text); + QString defaultText() const; + + KUrl url() const; + int zoomLevel() const; + + /** + * Refreshes the status bar to get synchronized with the (updated) Dolphin settings. + */ + void readSettings(); + +public slots: + void setText(const QString& text); + void setUrl(const KUrl& url); + void setZoomLevel(int zoomLevel); + +signals: + /** + * Is emitted if the stop-button has been pressed during showing a progress. + */ + void stopPressed(); + + void zoomLevelChanged(int zoomLevel); + +protected: + virtual void contextMenuEvent(QContextMenuEvent* event); + virtual bool eventFilter(QObject* obj, QEvent* event); + +private slots: + void showZoomSliderToolTip(int zoomLevel); + void updateProgressInfo(); + + /** + * Updates the text for m_label and does an eliding in + * case if the text does not fit into the available width. + */ + void updateLabelText(); + + /** + * Is invoked by m_resetToDefaultTextTimer and clears + * m_text so that the default text will be shown. This + * prevents that information-messages will be cleared + * too fast. + */ + void slotResetToDefaultText(); + + /** + * Updates the text of the zoom slider tooltip to show + * the currently used size. + */ + void updateZoomSliderToolTip(int zoomLevel); + +private: + /** + * Makes the space information widget and zoom slider widget + * visible, if \a visible is true and the settings allow to show + * the widgets. showUnknownProgressIf \a visible is false, it is assured that both + * widgets are hidden. + */ + void setExtensionsVisible(bool visible); + +private: + QString m_text; + QString m_defaultText; + QLabel* m_label; + StatusBarSpaceInfo* m_spaceInfo; + + QSlider* m_zoomSlider; + + QLabel* m_progressTextLabel; + QProgressBar* m_progressBar; + QToolButton* m_stopButton; + int m_progress; + QTimer* m_showProgressBarTimer; + + QTimer* m_resetToDefaultTextTimer; + QTime m_textTimestamp; +}; + +#endif diff --git a/dolphin/src/statusbar/mountpointobserver.cpp b/dolphin/src/statusbar/mountpointobserver.cpp new file mode 100644 index 00000000..4a23be6b --- /dev/null +++ b/dolphin/src/statusbar/mountpointobserver.cpp @@ -0,0 +1,48 @@ +/*************************************************************************** + * Copyright (C) 2014 by Frank Reininghaus * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "mountpointobserver.h" +#include "mountpointobservercache.h" + +MountPointObserver::MountPointObserver(const QString& mountPoint, QObject* parent) : + QObject(parent), + m_mountPoint(mountPoint), + m_referenceCount(0), + m_spaceInfo(KDiskFreeSpaceInfo::freeSpaceInfo(mountPoint)) +{ +} + +MountPointObserver* MountPointObserver::observerForPath(const QString& path) +{ + MountPointObserver* observer = MountPointObserverCache::instance()->observerForPath(path); + return observer; +} + +void MountPointObserver::update() +{ + if (m_referenceCount == 0) { + delete this; + } else { + const KDiskFreeSpaceInfo spaceInfo = KDiskFreeSpaceInfo::freeSpaceInfo(m_mountPoint); + if (spaceInfo.size() != m_spaceInfo.size() || spaceInfo.available() != m_spaceInfo.available()) { + m_spaceInfo = spaceInfo; + emit spaceInfoChanged(); + } + } +} diff --git a/dolphin/src/statusbar/mountpointobserver.h b/dolphin/src/statusbar/mountpointobserver.h new file mode 100644 index 00000000..ac5f8ecc --- /dev/null +++ b/dolphin/src/statusbar/mountpointobserver.h @@ -0,0 +1,109 @@ +/*************************************************************************** + * Copyright (C) 2014 by Frank Reininghaus * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef MOUNTPOINTOBSERVER_H +#define MOUNTPOINTOBSERVER_H + +#include + +#include + +/** + * A MountPointObserver can be used to determine the free space on a mount + * point. It will then check the free space periodically, and emit the signal + * spaceInfoChanged() if the return value of spaceInfo() has changed. + * + * Since multiple users which watch paths on the same mount point can share + * a MountPointObserver, it is not possible to create a MountPointObserver + * manually. Instead, the function observerForPath(QString&) should be called, + * which returns either an existing or a newly created MountPointObserver for + * the mount point where the path is mounted. observerForPath(QString&) looks + * for a suitable MountPointObserver in an internal cache and creates new + * MountPointObservers and adds them to the cache if necessary. + * + * Reference counting is used to keep track of the number of users, and to + * decide when the object can be deleted. A user of this class should call + * the method ref() when it starts using it, and deref() when it does not need + * the MountPointObserver any more. + * + * The object will not be deleted immediately if the reference count reaches + * zero. The object will only be destroyed when the next periodic update of + * the free space information happens, and the reference count is still zero. + * This approach makes it possible to re-use the object if a new user requests + * the free space for the same mount point before the next update. + */ +class MountPointObserver : public QObject +{ + Q_OBJECT + + explicit MountPointObserver(const QString& mountPoint, QObject* parent = 0); + virtual ~MountPointObserver() {} + +public: + /** + * Obtains information about the available space on the observed mount point. + */ + KDiskFreeSpaceInfo spaceInfo() const { return m_spaceInfo; } + + /** + * Call this function to indicate that the caller intends to continue using this object. An + * internal reference count is increased then. When the observer is not needed any more, + * deref() should be called, which decreases the reference count again. + */ + void ref() { ++m_referenceCount; } + + /** + * This function can be used to indicate that the caller does not need this MountPointObserver + * any more. Internally, a reference count is decreased. If the reference count is zero while + * update() is called, the object deletes itself. + */ + void deref() + { + --m_referenceCount; + Q_ASSERT(m_referenceCount >= 0); + } + + /** + * Returns a MountPointObserver for the given \a path. If the caller intends to continue using + * the returned object, it must call its ref() method. + */ + static MountPointObserver* observerForPath(const QString& path); + +signals: + /** + * This signal is emitted if the information that spaceInfo() will return has changed. + */ + void spaceInfoChanged(); + +public slots: + /** + * If this slot is invoked, MountPointObserver checks if the available space on the observed + * mount point has changed, and emits spaceInfoChanged() if that is the case. + */ + void update(); + +private: + const QString m_mountPoint; + int m_referenceCount; + KDiskFreeSpaceInfo m_spaceInfo; + + friend class MountPointObserverCache; +}; + +#endif diff --git a/dolphin/src/statusbar/mountpointobservercache.cpp b/dolphin/src/statusbar/mountpointobservercache.cpp new file mode 100644 index 00000000..aff0c8e2 --- /dev/null +++ b/dolphin/src/statusbar/mountpointobservercache.cpp @@ -0,0 +1,99 @@ +/*************************************************************************** + * Copyright (C) 2014 by Frank Reininghaus * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "mountpointobservercache.h" + +#include "mountpointobserver.h" + +#include +#include + +#include + +class MountPointObserverCacheSingleton +{ +public: + MountPointObserverCache instance; +}; +K_GLOBAL_STATIC(MountPointObserverCacheSingleton, s_MountPointObserverCache) + + +MountPointObserverCache::MountPointObserverCache() : + m_observerForMountPoint(), + m_mountPointForObserver(), + m_updateTimer(0) +{ + m_updateTimer = new QTimer(this); +} + +MountPointObserverCache::~MountPointObserverCache() +{ +} + +MountPointObserverCache* MountPointObserverCache::instance() +{ + return &s_MountPointObserverCache->instance; +} + +MountPointObserver* MountPointObserverCache::observerForPath(const QString& path) +{ + // Try to share the observer with other paths that have the same mount point. + QString mountPointPath; + KMountPoint::Ptr mountPoint = KMountPoint::currentMountPoints().findByPath(path); + if (mountPoint) { + mountPointPath = mountPoint->mountPoint(); + } else { + // Even if determining the mount point failed, KDiskFreeSpaceInfo might still + // be able to retrieve information about the path. + mountPointPath = path; + } + + MountPointObserver* observer = m_observerForMountPoint.value(mountPointPath); + if (!observer) { + observer = new MountPointObserver(mountPointPath, this); + m_observerForMountPoint.insert(mountPointPath, observer); + m_mountPointForObserver.insert(observer, mountPointPath); + Q_ASSERT(m_observerForMountPoint.count() == m_mountPointForObserver.count()); + + connect(observer, SIGNAL(destroyed(QObject*)), this, SLOT(slotObserverDestroyed(QObject*))); + + if (!m_updateTimer->isActive()) { + m_updateTimer->start(10000); + } + + connect(m_updateTimer, SIGNAL(timeout()), observer, SLOT(update())); + } + + return observer; +} + +void MountPointObserverCache::slotObserverDestroyed(QObject* observer) +{ + Q_ASSERT(m_mountPointForObserver.contains(observer)); + const QString& path = m_mountPointForObserver.value(observer); + Q_ASSERT(m_observerForMountPoint.contains(path)); + m_observerForMountPoint.remove(path); + m_mountPointForObserver.remove(observer); + + Q_ASSERT(m_observerForMountPoint.count() == m_mountPointForObserver.count()); + + if (m_mountPointForObserver.isEmpty()) { + m_updateTimer->stop(); + } +} diff --git a/dolphin/src/statusbar/mountpointobservercache.h b/dolphin/src/statusbar/mountpointobservercache.h new file mode 100644 index 00000000..34f565d8 --- /dev/null +++ b/dolphin/src/statusbar/mountpointobservercache.h @@ -0,0 +1,58 @@ +/*************************************************************************** + * Copyright (C) 2014 by Frank Reininghaus * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef MOUNTPOINTOBSERVERCACHE_H +#define MOUNTPOINTOBSERVERCACHE_H + +#include +#include + +class MountPointObserver; +#include + +class MountPointObserverCache : public QObject +{ + Q_OBJECT + + MountPointObserverCache(); + virtual ~MountPointObserverCache(); + +public: + static MountPointObserverCache* instance(); + + /** + * Returns a MountPointObserver for the given \a path. A new observer is created if necessary. + */ + MountPointObserver* observerForPath(const QString& path); + +private slots: + /** + * Removes the given \a observer from the cache. + */ + void slotObserverDestroyed(QObject* observer); + +private: + QHash m_observerForMountPoint; + QHash m_mountPointForObserver; + QTimer* m_updateTimer; + + friend class MountPointObserverCacheSingleton; +}; + +#endif diff --git a/dolphin/src/statusbar/spaceinfoobserver.cpp b/dolphin/src/statusbar/spaceinfoobserver.cpp new file mode 100644 index 00000000..9125a930 --- /dev/null +++ b/dolphin/src/statusbar/spaceinfoobserver.cpp @@ -0,0 +1,89 @@ +/*************************************************************************** + * Copyright (C) 2014 by Frank Reininghaus * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "spaceinfoobserver.h" + +#include "mountpointobserver.h" + +#include + +SpaceInfoObserver::SpaceInfoObserver(const KUrl& url, QObject* parent) : + QObject(parent), + m_mountPointObserver(0) +{ + if (url.isLocalFile()) { + m_mountPointObserver = MountPointObserver::observerForPath(url.toLocalFile()); + m_mountPointObserver->ref(); + connect(m_mountPointObserver, SIGNAL(spaceInfoChanged()), this, SIGNAL(valuesChanged())); + } +} + +SpaceInfoObserver::~SpaceInfoObserver() +{ + if (m_mountPointObserver) { + m_mountPointObserver->deref(); + m_mountPointObserver = 0; + } +} + +quint64 SpaceInfoObserver::size() const +{ + if (m_mountPointObserver && m_mountPointObserver->spaceInfo().isValid()) { + return m_mountPointObserver->spaceInfo().size(); + } else { + return 0; + } +} + +quint64 SpaceInfoObserver::available() const +{ + if (m_mountPointObserver && m_mountPointObserver->spaceInfo().isValid()) { + return m_mountPointObserver->spaceInfo().available(); + } else { + return 0; + } +} + +void SpaceInfoObserver::setUrl(const KUrl& url) +{ + if (url.isLocalFile()) { + MountPointObserver* newObserver = MountPointObserver::observerForPath(url.toLocalFile()); + if (newObserver != m_mountPointObserver) { + if (m_mountPointObserver) { + disconnect(m_mountPointObserver, SIGNAL(spaceInfoChanged()), this, SIGNAL(valuesChanged())); + m_mountPointObserver->deref(); + m_mountPointObserver = 0; + } + + m_mountPointObserver = newObserver; + m_mountPointObserver->ref(); + connect(m_mountPointObserver, SIGNAL(spaceInfoChanged()), this, SIGNAL(valuesChanged())); + + emit valuesChanged(); + } + } else { + if (m_mountPointObserver) { + disconnect(m_mountPointObserver, SIGNAL(spaceInfoChanged()), this, SIGNAL(valuesChanged())); + m_mountPointObserver->deref(); + m_mountPointObserver = 0; + + emit valuesChanged(); + } + } +} diff --git a/dolphin/src/statusbar/spaceinfoobserver.h b/dolphin/src/statusbar/spaceinfoobserver.h new file mode 100644 index 00000000..d2fb6ebf --- /dev/null +++ b/dolphin/src/statusbar/spaceinfoobserver.h @@ -0,0 +1,51 @@ +/*************************************************************************** + * Copyright (C) 2014 by Frank Reininghaus * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef SPACEINFOOBSERVER_H +#define SPACEINFOOBSERVER_H + +#include + +class KUrl; +class MountPointObserver; + +class SpaceInfoObserver : public QObject +{ + Q_OBJECT + +public: + explicit SpaceInfoObserver(const KUrl& url, QObject* parent = 0); + virtual ~SpaceInfoObserver(); + + quint64 size() const; + quint64 available() const; + + void setUrl(const KUrl& url); + +signals: + /** + * This signal is emitted if the information that size() and/or available() will return has changed. + */ + void valuesChanged(); + +private: + MountPointObserver* m_mountPointObserver; +}; + +#endif diff --git a/dolphin/src/statusbar/statusbarspaceinfo.cpp b/dolphin/src/statusbar/statusbarspaceinfo.cpp new file mode 100644 index 00000000..cd68fa5d --- /dev/null +++ b/dolphin/src/statusbar/statusbarspaceinfo.cpp @@ -0,0 +1,90 @@ +/*************************************************************************** + * Copyright (C) 2006 by Peter Penz (peter.penz@gmx.at) and * + * and Patrice Tremblay * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "statusbarspaceinfo.h" + +#include "spaceinfoobserver.h" + +#include +#include + +#include + +StatusBarSpaceInfo::StatusBarSpaceInfo(QWidget* parent) : + KCapacityBar(KCapacityBar::DrawTextInline, parent), + m_observer(0) +{ +} + +StatusBarSpaceInfo::~StatusBarSpaceInfo() +{ +} + +void StatusBarSpaceInfo::setUrl(const KUrl& url) +{ + if (m_url != url) { + m_url = url; + if (m_observer) { + m_observer->setUrl(url); + } + } +} + +KUrl StatusBarSpaceInfo::url() const +{ + return m_url; +} + +void StatusBarSpaceInfo::showEvent(QShowEvent* event) +{ + KCapacityBar::showEvent(event); + m_observer.reset(new SpaceInfoObserver(m_url, this)); + slotValuesChanged(); + connect(m_observer.data(), SIGNAL(valuesChanged()), this, SLOT(slotValuesChanged())); +} + +void StatusBarSpaceInfo::hideEvent(QHideEvent* event) +{ + m_observer.reset(); + KCapacityBar::hideEvent(event); +} + +void StatusBarSpaceInfo::slotValuesChanged() +{ + Q_ASSERT(m_observer); + const quint64 size = m_observer->size(); + if (size == 0) { + setText(i18nc("@info:status", "Unknown size")); + setValue(0); + update(); + } else { + const quint64 available = m_observer->available(); + const quint64 used = size - available; + const int percentUsed = qRound(100.0 * qreal(used) / qreal(size)); + + setText(i18nc("@info:status Free disk space", "%1 free", KIO::convertSize(available))); + setUpdatesEnabled(false); + setValue(percentUsed); + setUpdatesEnabled(true); + update(); + } +} + +#include "moc_statusbarspaceinfo.cpp" diff --git a/dolphin/src/statusbar/statusbarspaceinfo.h b/dolphin/src/statusbar/statusbarspaceinfo.h new file mode 100644 index 00000000..3ec44573 --- /dev/null +++ b/dolphin/src/statusbar/statusbarspaceinfo.h @@ -0,0 +1,64 @@ +/*************************************************************************** + * Copyright (C) 2006 by Peter Penz (peter.penz@gmx.at) and * + * and Patrice Tremblay * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ +#ifndef STATUSBARSPACEINFO_H +#define STATUSBARSPACEINFO_H + +#include + +#include +#include +#include +#include + +#include + +#include +#include + +class SpaceInfoObserver; + +/** + * @short Shows the available space for the volume represented + * by the given URL as part of the status bar. + */ +class StatusBarSpaceInfo : public KCapacityBar +{ + Q_OBJECT + +public: + explicit StatusBarSpaceInfo(QWidget* parent = 0); + virtual ~StatusBarSpaceInfo(); + + void setUrl(const KUrl& url); + KUrl url() const; + +protected: + void showEvent(QShowEvent* event); + void hideEvent(QHideEvent* event); + +private slots: + void slotValuesChanged(); + +private: + QScopedPointer m_observer; + KUrl m_url; +}; + +#endif diff --git a/dolphin/src/tests/CMakeLists.txt b/dolphin/src/tests/CMakeLists.txt new file mode 100644 index 00000000..7c55f5d2 --- /dev/null +++ b/dolphin/src/tests/CMakeLists.txt @@ -0,0 +1,153 @@ +include_directories( + ${CMAKE_CURRENT_SOURCE_DIR}/.. + ${CMAKE_CURRENT_BUILD_DIR}/.. + ${KDE4_INCLUDES} +) + +# KItemSetTest +set(kitemsettest_SRCS + kitemsettest.cpp + ../kitemviews/kitemset.cpp +) +kde4_add_test(dolphin-kitemsettest ${kitemsettest_SRCS}) +target_link_libraries(dolphin-kitemsettest + dolphinprivate + ${KDE4_KIO_LIBS} + ${QT_QTTEST_LIBRARY} +) + +# KItemRangeTest +kde4_add_test(dolphin-kitemrangetest kitemrangetest.cpp) +target_link_libraries(dolphin-kitemrangetest + dolphinprivate + ${KDE4_KIO_LIBS} + ${QT_QTTEST_LIBRARY} +) + +# KItemListSelectionManagerTest +set(kitemlistselectionmanagertest_SRCS + kitemlistselectionmanagertest.cpp + ../kitemviews/kitemlistselectionmanager.cpp + ../kitemviews/kitemmodelbase.cpp + ../kitemviews/kitemset.cpp +) +kde4_add_test(dolphin-kitemlistselectionmanagertest ${kitemlistselectionmanagertest_SRCS}) +target_link_libraries(dolphin-kitemlistselectionmanagertest + dolphinprivate + ${KDE4_KIO_LIBS} + ${QT_QTTEST_LIBRARY} +) + +# KItemListControllerTest +set(kitemlistcontrollertest_SRCS + kitemlistcontrollertest.cpp + testdir.cpp + ../kitemviews/kfileitemmodel.cpp + ../kitemviews/kfileitemlistview.cpp + ../kitemviews/kfileitemlistwidget.cpp + ../kitemviews/kitemmodelbase.cpp + ../kitemviews/kitemlistview.cpp + ../kitemviews/kitemlistcontainer.cpp + ../kitemviews/kitemlistwidget.cpp + ../kitemviews/kitemset.cpp + ../kitemviews/kstandarditemlistview.cpp + ../kitemviews/kstandarditemlistwidget.cpp +) +kde4_add_test(dolphin-kitemlistcontrollertest ${kitemlistcontrollertest_SRCS}) +target_link_libraries(dolphin-kitemlistcontrollertest + dolphinprivate + ${KDE4_KIO_LIBS} + ${QT_QTTEST_LIBRARY} +) + +# KFileItemListViewTest +set(kfileitemlistviewtest_SRCS + kfileitemlistviewtest.cpp + testdir.cpp + ../kitemviews/kfileitemmodel.cpp + ../kitemviews/kfileitemlistview.cpp + ../kitemviews/kitemmodelbase.cpp + ../kitemviews/kitemlistview.cpp + ../kitemviews/kitemlistcontainer.cpp + ../kitemviews/kitemlistwidget.cpp + ../kitemviews/kitemset.cpp + ../kitemviews/kstandarditemlistview.cpp + ../kitemviews/kstandarditemlistwidget.cpp +) +kde4_add_test(dolphin-kfileitemlistviewtest ${kfileitemlistviewtest_SRCS}) +target_link_libraries(dolphin-kfileitemlistviewtest + dolphinprivate + ${KDE4_KIO_LIBS} + ${QT_QTTEST_LIBRARY} +) + +# KFileItemModelTest +set(kfileitemmodeltest_SRCS + kfileitemmodeltest.cpp + testdir.cpp + ../kitemviews/kfileitemmodel.cpp + ../kitemviews/kitemmodelbase.cpp + ../kitemviews/kitemset.cpp +) +kde4_add_test(dolphin-kfileitemmodeltest ${kfileitemmodeltest_SRCS}) +target_link_libraries(dolphin-kfileitemmodeltest + dolphinprivate + ${KDE4_KIO_LIBS} + ${QT_QTTEST_LIBRARY} +) + +# KFileItemModelBenchmark +set(kfileitemmodelbenchmark_SRCS + kfileitemmodelbenchmark.cpp + testdir.cpp + ../kitemviews/kfileitemmodel.cpp + ../kitemviews/kitemmodelbase.cpp +) +kde4_add_manual_test(dolphin-kfileitemmodelbenchmark ${kfileitemmodelbenchmark_SRCS}) +target_link_libraries(dolphin-kfileitemmodelbenchmark + dolphinprivate + ${KDE4_KIO_LIBS} + ${QT_QTTEST_LIBRARY} +) + +# KItemListKeyboardSearchManagerTest +set(kitemlistkeyboardsearchmanagertest_SRCS + kitemlistkeyboardsearchmanagertest.cpp + ../kitemviews/private/kitemlistkeyboardsearchmanager.cpp +) +kde4_add_test(dolphin-kitemlistkeyboardsearchmanagertest ${kitemlistkeyboardsearchmanagertest_SRCS}) +target_link_libraries(dolphin-kitemlistkeyboardsearchmanagertest + ${KDE4_KIO_LIBS} + ${QT_QTTEST_LIBRARY} +) + +# KStandardItemModelTest +set(kstandarditemmodeltest_SRCS + kstandarditemmodeltest.cpp + ../kitemviews/kstandarditem.cpp + ../kitemviews/kstandarditemmodel.cpp + ../kitemviews/kitemmodelbase.cpp +) +kde4_add_test(dolphin-kstandarditemmodeltest ${kstandarditemmodeltest_SRCS}) +target_link_libraries(dolphin-kstandarditemmodeltest + dolphinprivate + ${KDE4_KIO_LIBS} + ${QT_QTTEST_LIBRARY} +) + +# ViewPropertiesTest +set(viewpropertiestest_SRCS + viewpropertiestest.cpp + testdir.cpp + ../views/viewproperties.cpp +) +kde4_add_kcfg_files(viewpropertiestest_SRCS + ../settings/dolphin_generalsettings.kcfgc + ../settings/dolphin_directoryviewpropertysettings.kcfgc +) +kde4_add_test(dolphin-viewpropertiestest ${viewpropertiestest_SRCS}) +target_link_libraries(dolphin-viewpropertiestest + dolphinprivate + ${KDE4_KIO_LIBS} + ${QT_QTTEST_LIBRARY} +) diff --git a/dolphin/src/tests/kfileitemlistviewtest.cpp b/dolphin/src/tests/kfileitemlistviewtest.cpp new file mode 100644 index 00000000..9f174584 --- /dev/null +++ b/dolphin/src/tests/kfileitemlistviewtest.cpp @@ -0,0 +1,117 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include + +#include "kitemviews/kfileitemlistview.h" +#include "kitemviews/kfileitemmodel.h" +#include "kitemviews/private/kfileitemmodeldirlister.h" +#include "testdir.h" + +#include + +namespace { + const int DefaultTimeout = 2000; +}; + +class KFileItemListViewTest : public QObject +{ + Q_OBJECT + +private slots: + void init(); + void cleanup(); + void testGroupedItemChanges(); + +private: + KFileItemListView* m_listView; + KFileItemModel* m_model; + TestDir* m_testDir; + QGraphicsView* m_graphicsView; +}; + +void KFileItemListViewTest::init() +{ + qRegisterMetaType("KItemRangeList"); + qRegisterMetaType("KFileItemList"); + + m_testDir = new TestDir(); + m_model = new KFileItemModel(); + m_model->m_dirLister->setAutoUpdate(false); + + m_listView = new KFileItemListView(); + m_listView->onModelChanged(m_model, 0); + + m_graphicsView = new QGraphicsView(); + m_graphicsView->show(); + QTest::qWaitForWindowShown(m_graphicsView); +} + +void KFileItemListViewTest::cleanup() +{ + delete m_graphicsView; + m_graphicsView = 0; + + delete m_listView; + m_listView = 0; + + delete m_model; + m_model = 0; + + delete m_testDir; + m_testDir = 0; +} + +/** + * If grouping is enabled, the group headers must be updated + * when items have been inserted or removed. This updating + * may only be done after all multiple ranges have been inserted + * or removed and not after each individual range (see description + * in #ifndef QT_NO_DEBUG-block of KItemListView::slotItemsInserted() + * and KItemListView::slotItemsRemoved()). This test inserts and + * removes multiple ranges and will trigger the Q_ASSERT in the + * ifndef QT_NO_DEBUG-block in case if a group-header will be updated + * too early. + */ +void KFileItemListViewTest::testGroupedItemChanges() +{ + m_model->setGroupedSorting(true); + + m_testDir->createFiles(QStringList() << "1" << "3" << "5"); + + m_model->loadDirectory(m_testDir->url()); + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout)); + QCOMPARE(m_model->count(), 3); + + m_testDir->createFiles(QStringList() << "2" << "4"); + m_model->m_dirLister->updateDirectory(m_testDir->url()); + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout)); + QCOMPARE(m_model->count(), 5); + + m_testDir->removeFile("1"); + m_testDir->removeFile("3"); + m_testDir->removeFile("5"); + m_model->m_dirLister->updateDirectory(m_testDir->url()); + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsRemoved(KItemRangeList)), DefaultTimeout)); + QCOMPARE(m_model->count(), 2); +} + +QTEST_KDEMAIN(KFileItemListViewTest, GUI) + +#include "kfileitemlistviewtest.moc" diff --git a/dolphin/src/tests/kfileitemmodelbenchmark.cpp b/dolphin/src/tests/kfileitemmodelbenchmark.cpp new file mode 100644 index 00000000..66918b6e --- /dev/null +++ b/dolphin/src/tests/kfileitemmodelbenchmark.cpp @@ -0,0 +1,334 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz * + * Copyright (C) 2013 by Frank Reininghaus * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include + +#include "kitemviews/kfileitemmodel.h" +#include "kitemviews/private/kfileitemmodelsortalgorithm.h" + +#include "testdir.h" + +#include + +void myMessageOutput(QtMsgType type, const char* msg) +{ + switch (type) { + case QtDebugMsg: + break; + case QtWarningMsg: + break; + case QtCriticalMsg: + fprintf(stderr, "Critical: %s\n", msg); + break; + case QtFatalMsg: + fprintf(stderr, "Fatal: %s\n", msg); + abort(); + default: + break; + } +} + +namespace { + const int DefaultTimeout = 5000; +}; + +Q_DECLARE_METATYPE(KFileItemList) +Q_DECLARE_METATYPE(KItemRangeList) + +class KFileItemModelBenchmark : public QObject +{ + Q_OBJECT + +public: + KFileItemModelBenchmark(); + +private slots: + void insertAndRemoveManyItems_data(); + void insertAndRemoveManyItems(); + void insertManyChildItems(); + +private: + static KFileItemList createFileItemList(const QStringList& fileNames, const QString& urlPrefix = QLatin1String("file:///")); +}; + +KFileItemModelBenchmark::KFileItemModelBenchmark() +{ +} + +void KFileItemModelBenchmark::insertAndRemoveManyItems_data() +{ + QTest::addColumn("initialItems"); + QTest::addColumn("newItems"); + QTest::addColumn("removedItems"); + QTest::addColumn("expectedFinalItems"); + QTest::addColumn("expectedItemsInserted"); + QTest::addColumn("expectedItemsRemoved"); + + QList sizes; + sizes << 1000 << 4000 << 16000 << 64000 << 256000; + //sizes << 50000 << 100000 << 150000 << 200000 << 250000; + + foreach (int n, sizes) { + QStringList allStrings; + for (int i = 0; i < n; ++i) { + allStrings << QString::number(i); + } + + // We want to keep the sorting overhead in the benchmark low. + // Therefore, we do not use natural sorting. However, this + // means that our list is currently not sorted. + allStrings.sort(); + + KFileItemList all = createFileItemList(allStrings); + + KFileItemList firstHalf, secondHalf, even, odd; + for (int i = 0; i < n; ++i) { + if (i < n / 2) { + firstHalf << all.at(i); + } else { + secondHalf << all.at(i); + } + + if (i % 2 == 0) { + even << all.at(i); + } else { + odd << all.at(i); + } + } + + KItemRangeList itemRangeListFirstHalf; + itemRangeListFirstHalf << KItemRange(0, firstHalf.count()); + + KItemRangeList itemRangeListSecondHalf; + itemRangeListSecondHalf << KItemRange(firstHalf.count(), secondHalf.count()); + + KItemRangeList itemRangeListOddInserted, itemRangeListOddRemoved; + for (int i = 0; i < odd.count(); ++i) { + // Note that the index in the KItemRange is the index of + // the model *before* the items have been inserted. + itemRangeListOddInserted << KItemRange(i + 1, 1); + itemRangeListOddRemoved << KItemRange(2 * i + 1, 1); + } + + const int bufferSize = 128; + char buffer[bufferSize]; + + snprintf(buffer, bufferSize, "all--n=%i", n); + QTest::newRow(buffer) << all << KFileItemList() << KFileItemList() << all << KItemRangeList() << KItemRangeList(); + + snprintf(buffer, bufferSize, "1st half + 2nd half--n=%i", n); + QTest::newRow(buffer) << firstHalf << secondHalf << KFileItemList() << all << itemRangeListSecondHalf << KItemRangeList(); + + snprintf(buffer, bufferSize, "2nd half + 1st half--n=%i", n); + QTest::newRow(buffer) << secondHalf << firstHalf << KFileItemList() << all << itemRangeListFirstHalf << KItemRangeList(); + + snprintf(buffer, bufferSize, "even + odd--n=%i", n); + QTest::newRow(buffer) << even << odd << KFileItemList() << all << itemRangeListOddInserted << KItemRangeList(); + + snprintf(buffer, bufferSize, "all - 2nd half--n=%i", n); + QTest::newRow(buffer) << all << KFileItemList() << secondHalf << firstHalf << KItemRangeList() << itemRangeListSecondHalf; + + snprintf(buffer, bufferSize, "all - 1st half--n=%i", n); + QTest::newRow(buffer) << all << KFileItemList() << firstHalf << secondHalf << KItemRangeList() << itemRangeListFirstHalf; + + snprintf(buffer, bufferSize, "all - odd--n=%i", n); + QTest::newRow(buffer) << all << KFileItemList() << odd << even << KItemRangeList() << itemRangeListOddRemoved; + } +} + +void KFileItemModelBenchmark::insertAndRemoveManyItems() +{ + QFETCH(KFileItemList, initialItems); + QFETCH(KFileItemList, newItems); + QFETCH(KFileItemList, removedItems); + QFETCH(KFileItemList, expectedFinalItems); + QFETCH(KItemRangeList, expectedItemsInserted); + QFETCH(KItemRangeList, expectedItemsRemoved); + + KFileItemModel model; + + // Avoid overhead caused by natural sorting + // and determining the isDir/isLink roles. + model.m_naturalSorting = false; + model.setRoles(QSet() << "text"); + + QSignalSpy spyItemsInserted(&model, SIGNAL(itemsInserted(KItemRangeList))); + QSignalSpy spyItemsRemoved(&model, SIGNAL(itemsRemoved(KItemRangeList))); + + QBENCHMARK { + model.slotClear(); + model.slotItemsAdded(model.directory(), initialItems); + model.slotCompleted(); + QCOMPARE(model.count(), initialItems.count()); + + if (!newItems.isEmpty()) { + model.slotItemsAdded(model.directory(), newItems); + model.slotCompleted(); + } + QCOMPARE(model.count(), initialItems.count() + newItems.count()); + + if (!removedItems.isEmpty()) { + model.slotItemsDeleted(removedItems); + } + QCOMPARE(model.count(), initialItems.count() + newItems.count() - removedItems.count()); + } + + QVERIFY(model.isConsistent()); + + for (int i = 0; i < model.count(); ++i) { + QCOMPARE(model.fileItem(i), expectedFinalItems.at(i)); + } + + if (!expectedItemsInserted.empty()) { + QVERIFY(!spyItemsInserted.empty()); + const KItemRangeList actualItemsInserted = spyItemsInserted.last().first().value(); + QCOMPARE(actualItemsInserted, expectedItemsInserted); + } + + if (!expectedItemsRemoved.empty()) { + QVERIFY(!spyItemsRemoved.empty()); + const KItemRangeList actualItemsRemoved = spyItemsRemoved.last().first().value(); + QCOMPARE(actualItemsRemoved, expectedItemsRemoved); + } +} + +void KFileItemModelBenchmark::insertManyChildItems() +{ + // TODO: this function needs to be adjusted to the changes in KFileItemModel + // (replacement of slotNewItems(KFileItemList) by slotItemsAdded(KUrl,KFileItemList)) + // Currently, this function tries to insert child items of multiple + // directories by invoking the slot only once. +#if 0 + qInstallMsgHandler(myMessageOutput); + + KFileItemModel model; + + // Avoid overhead caused by natural sorting. + model.m_naturalSorting = false; + + QSet modelRoles = model.roles(); + modelRoles << "isExpanded" << "isExpandable" << "expandedParentsCount"; + model.setRoles(modelRoles); + model.setSortDirectoriesFirst(false); + + // Create a test folder with a 3-level tree structure of folders. + TestDir testFolder; + int numberOfFolders = 0; + + QStringList subFolderNames; + subFolderNames << "a/" << "b/" << "c/" << "d/"; + + foreach (const QString& s1, subFolderNames) { + ++numberOfFolders; + foreach (const QString& s2, subFolderNames) { + ++numberOfFolders; + foreach (const QString& s3, subFolderNames) { + testFolder.createDir("level-1-" + s1 + "level-2-" + s2 + "level-3-" + s3); + ++numberOfFolders; + } + } + } + + // Open the folder in the model and expand all subfolders. + model.loadDirectory(testFolder.url()); + QVERIFY(QTest::kWaitForSignal(&model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout)); + + int index = 0; + while (index < model.count()) { + if (model.isExpandable(index)) { + model.setExpanded(index, true); + + if (!model.data(index).value("text").toString().startsWith("level-3")) { + // New subfolders will appear unless we are on the final level already. + QVERIFY(QTest::kWaitForSignal(&model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout)); + } + + QVERIFY(model.isExpanded(index)); + } + ++index; + } + + QCOMPARE(model.count(), numberOfFolders); + + // Create a list of many file items, which will be added to each of the + // "level 1", "level 2", and "level 3" folders. + const int filesPerDirectory = 500; + QStringList allStrings; + for (int i = 0; i < filesPerDirectory; ++i) { + allStrings << QString::number(i); + } + allStrings.sort(); + + KFileItemList newItems; + + // Also keep track of all expected items, including the existing + // folders, to verify the final state of the model. + KFileItemList allExpectedItems; + + for (int i = 0; i < model.count(); ++i) { + const KFileItem folderItem = model.fileItem(i); + allExpectedItems << folderItem; + + const KUrl folderUrl = folderItem.url(); + KFileItemList itemsInFolder = createFileItemList(allStrings, folderUrl.url(KUrl::AddTrailingSlash)); + + newItems.append(itemsInFolder); + allExpectedItems.append(itemsInFolder); + } + + // Bring the items into random order. + KRandomSequence randomSequence(0); + randomSequence.randomize(newItems); + + // Measure how long it takes to insert and then remove all files. + QBENCHMARK { + model.slotNewItems(newItems); + model.slotCompleted(); + + QCOMPARE(model.count(), allExpectedItems.count()); + QVERIFY(model.isConsistent()); + for (int i = 0; i < model.count(); ++i) { + QCOMPARE(model.fileItem(i), allExpectedItems.at(i)); + } + + model.slotItemsDeleted(newItems); + QCOMPARE(model.count(), numberOfFolders); + QVERIFY(model.isConsistent()); + } +#endif +} + +KFileItemList KFileItemModelBenchmark::createFileItemList(const QStringList& fileNames, const QString& prefix) +{ + // Suppress 'file does not exist anymore' messages from KFileItemPrivate::init(). + qInstallMsgHandler(myMessageOutput); + + KFileItemList result; + foreach (const QString& name, fileNames) { + const KUrl url(prefix + name); + const KFileItem item(url, QString(), KFileItem::Unknown); + result << item; + } + return result; +} + +QTEST_KDEMAIN(KFileItemModelBenchmark, NoGUI) + +#include "kfileitemmodelbenchmark.moc" diff --git a/dolphin/src/tests/kfileitemmodeltest.cpp b/dolphin/src/tests/kfileitemmodeltest.cpp new file mode 100644 index 00000000..bda9f7b4 --- /dev/null +++ b/dolphin/src/tests/kfileitemmodeltest.cpp @@ -0,0 +1,1651 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz * + * Copyright (C) 2011 by Frank Reininghaus * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include + +#include + +#include +#include + +#include "kitemviews/kfileitemmodel.h" +#include "kitemviews/private/kfileitemmodeldirlister.h" +#include "testdir.h" + +void myMessageOutput(QtMsgType type, const char* msg) +{ + switch (type) { + case QtDebugMsg: + break; + case QtWarningMsg: + break; + case QtCriticalMsg: + fprintf(stderr, "Critical: %s\n", msg); + break; + case QtFatalMsg: + fprintf(stderr, "Fatal: %s\n", msg); + abort(); + default: + break; + } +} + +namespace { + const int DefaultTimeout = 5000; +}; + +Q_DECLARE_METATYPE(KItemRange) +Q_DECLARE_METATYPE(KItemRangeList) +Q_DECLARE_METATYPE(QList) + +class KFileItemModelTest : public QObject +{ + Q_OBJECT + +private slots: + void init(); + void cleanup(); + + void testDefaultRoles(); + void testDefaultSortRole(); + void testDefaultGroupedSorting(); + void testNewItems(); + void testRemoveItems(); + void testDirLoadingCompleted(); + void testSetData(); + void testChangeSortRole(); + void testResortAfterChangingName(); + void testModelConsistencyWhenInsertingItems(); + void testItemRangeConsistencyWhenInsertingItems(); + void testExpandItems(); + void testExpandParentItems(); + void testMakeExpandedItemHidden(); + void testRemoveFilteredExpandedItems(); + void testSorting(); + void testIndexForKeyboardSearch(); + void testNameFilter(); + void testEmptyPath(); + void testRefreshExpandedItem(); + void testRemoveHiddenItems(); + void collapseParentOfHiddenItems(); + void removeParentOfHiddenItems(); + void testGeneralParentChildRelationships(); + void testNameRoleGroups(); + void testNameRoleGroupsWithExpandedItems(); + void testInconsistentModel(); + void testChangeRolesForFilteredItems(); + void testChangeSortRoleWhileFiltering(); + void testRefreshFilteredItems(); + void testCollapseFolderWhileLoading(); + void testCreateMimeData(); + void testDeleteFileMoreThanOnce(); + +private: + QStringList itemsInModel() const; + +private: + KFileItemModel* m_model; + TestDir* m_testDir; +}; + +void KFileItemModelTest::init() +{ + // The item-model tests result in a huge number of debugging + // output from kdelibs. Only show critical and fatal messages. + qInstallMsgHandler(myMessageOutput); + + qRegisterMetaType("KItemRange"); + qRegisterMetaType("KItemRangeList"); + qRegisterMetaType("KFileItemList"); + + m_testDir = new TestDir(); + m_model = new KFileItemModel(); + m_model->m_dirLister->setAutoUpdate(false); + + // Reduce the timer interval to make the test run faster. + m_model->m_resortAllItemsTimer->setInterval(0); +} + +void KFileItemModelTest::cleanup() +{ + delete m_model; + m_model = 0; + + delete m_testDir; + m_testDir = 0; +} + +void KFileItemModelTest::testDefaultRoles() +{ + const QSet roles = m_model->roles(); + QCOMPARE(roles.count(), 3); + QVERIFY(roles.contains("text")); + QVERIFY(roles.contains("isDir")); + QVERIFY(roles.contains("isLink")); +} + +void KFileItemModelTest::testDefaultSortRole() +{ + QCOMPARE(m_model->sortRole(), QByteArray("text")); + + QStringList files; + files << "c.txt" << "a.txt" << "b.txt"; + + m_testDir->createFiles(files); + + m_model->loadDirectory(m_testDir->url()); + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout)); + + QCOMPARE(m_model->count(), 3); + QCOMPARE(m_model->data(0)["text"].toString(), QString("a.txt")); + QCOMPARE(m_model->data(1)["text"].toString(), QString("b.txt")); + QCOMPARE(m_model->data(2)["text"].toString(), QString("c.txt")); +} + +void KFileItemModelTest::testDefaultGroupedSorting() +{ + QCOMPARE(m_model->groupedSorting(), false); +} + +void KFileItemModelTest::testNewItems() +{ + QStringList files; + files << "a.txt" << "b.txt" << "c.txt"; + m_testDir->createFiles(files); + + m_model->loadDirectory(m_testDir->url()); + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout)); + + QCOMPARE(m_model->count(), 3); + + QVERIFY(m_model->isConsistent()); +} + +void KFileItemModelTest::testRemoveItems() +{ + m_testDir->createFile("a.txt"); + m_testDir->createFile("b.txt"); + m_model->loadDirectory(m_testDir->url()); + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout)); + QCOMPARE(m_model->count(), 2); + QVERIFY(m_model->isConsistent()); + + m_testDir->removeFile("a.txt"); + m_model->m_dirLister->updateDirectory(m_testDir->url()); + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsRemoved(KItemRangeList)), DefaultTimeout)); + QCOMPARE(m_model->count(), 1); + QVERIFY(m_model->isConsistent()); +} + +void KFileItemModelTest::testDirLoadingCompleted() +{ + QSignalSpy loadingCompletedSpy(m_model, SIGNAL(directoryLoadingCompleted())); + QSignalSpy itemsInsertedSpy(m_model, SIGNAL(itemsInserted(KItemRangeList))); + QSignalSpy itemsRemovedSpy(m_model, SIGNAL(itemsRemoved(KItemRangeList))); + + m_testDir->createFiles(QStringList() << "a.txt" << "b.txt" << "c.txt"); + + m_model->loadDirectory(m_testDir->url()); + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(directoryLoadingCompleted()), DefaultTimeout)); + QCOMPARE(loadingCompletedSpy.count(), 1); + QCOMPARE(itemsInsertedSpy.count(), 1); + QCOMPARE(itemsRemovedSpy.count(), 0); + QCOMPARE(m_model->count(), 3); + + m_testDir->createFiles(QStringList() << "d.txt" << "e.txt"); + m_model->m_dirLister->updateDirectory(m_testDir->url()); + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(directoryLoadingCompleted()), DefaultTimeout)); + QCOMPARE(loadingCompletedSpy.count(), 2); + QCOMPARE(itemsInsertedSpy.count(), 2); + QCOMPARE(itemsRemovedSpy.count(), 0); + QCOMPARE(m_model->count(), 5); + + m_testDir->removeFile("a.txt"); + m_testDir->createFile("f.txt"); + m_model->m_dirLister->updateDirectory(m_testDir->url()); + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(directoryLoadingCompleted()), DefaultTimeout)); + QCOMPARE(loadingCompletedSpy.count(), 3); + QCOMPARE(itemsInsertedSpy.count(), 3); + QCOMPARE(itemsRemovedSpy.count(), 1); + QCOMPARE(m_model->count(), 5); + + m_testDir->removeFile("b.txt"); + m_model->m_dirLister->updateDirectory(m_testDir->url()); + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsRemoved(KItemRangeList)), DefaultTimeout)); + QCOMPARE(loadingCompletedSpy.count(), 4); + QCOMPARE(itemsInsertedSpy.count(), 3); + QCOMPARE(itemsRemovedSpy.count(), 2); + QCOMPARE(m_model->count(), 4); + + QVERIFY(m_model->isConsistent()); +} + +void KFileItemModelTest::testSetData() +{ + m_testDir->createFile("a.txt"); + + m_model->loadDirectory(m_testDir->url()); + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout)); + + QHash values; + values.insert("customRole1", "Test1"); + values.insert("customRole2", "Test2"); + + QSignalSpy itemsChangedSpy(m_model, SIGNAL(itemsChanged(KItemRangeList,QSet))); + m_model->setData(0, values); + QCOMPARE(itemsChangedSpy.count(), 1); + + values = m_model->data(0); + QCOMPARE(values.value("customRole1").toString(), QString("Test1")); + QCOMPARE(values.value("customRole2").toString(), QString("Test2")); + QVERIFY(m_model->isConsistent()); +} + +void KFileItemModelTest::testChangeSortRole() +{ + QCOMPARE(m_model->sortRole(), QByteArray("text")); + + QStringList files; + files << "a.txt" << "b.jpg" << "c.txt"; + m_testDir->createFiles(files); + + m_model->loadDirectory(m_testDir->url()); + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout)); + QCOMPARE(itemsInModel(), QStringList() << "a.txt" << "b.jpg" << "c.txt"); + + // Simulate that KFileItemModelRolesUpdater determines the mime type. + // Resorting the files by 'type' will only work immediately if their + // mime types are known. + for (int index = 0; index < m_model->count(); ++index) { + m_model->fileItem(index).determineMimeType(); + } + + // Now: sort by type. + QSignalSpy spyItemsMoved(m_model, SIGNAL(itemsMoved(KItemRange,QList))); + m_model->setSortRole("type"); + QCOMPARE(m_model->sortRole(), QByteArray("type")); + QVERIFY(!spyItemsMoved.isEmpty()); + + // The actual order of the files might depend on the translation of the + // result of KFileItem::mimeComment() in the user's language. + QStringList version1; + version1 << "b.jpg" << "a.txt" << "c.txt"; + + QStringList version2; + version2 << "a.txt" << "c.txt" << "b.jpg"; + + const bool ok1 = (itemsInModel() == version1); + const bool ok2 = (itemsInModel() == version2); + + QVERIFY(ok1 || ok2); +} + +void KFileItemModelTest::testResortAfterChangingName() +{ + // We sort by size in a directory where all files have the same size. + // Therefore, the files are sorted by their names. + m_model->setSortRole("size"); + + QStringList files; + files << "a.txt" << "b.txt" << "c.txt"; + m_testDir->createFiles(files); + + m_model->loadDirectory(m_testDir->url()); + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout)); + QCOMPARE(itemsInModel(), QStringList() << "a.txt" << "b.txt" << "c.txt"); + + // We rename a.txt to d.txt. Even though the size has not changed at all, + // the model must re-sort the items. + QHash data; + data.insert("text", "d.txt"); + m_model->setData(0, data); + + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsMoved(KItemRange,QList)), DefaultTimeout)); + QCOMPARE(itemsInModel(), QStringList() << "b.txt" << "c.txt" << "d.txt"); + + // We rename d.txt back to a.txt using the dir lister's refreshItems() signal. + const KFileItem fileItemD = m_model->fileItem(2); + KFileItem fileItemA = fileItemD; + KUrl urlA = fileItemA.url(); + urlA.setFileName("a.txt"); + fileItemA.setUrl(urlA); + + m_model->slotRefreshItems(QList >() << qMakePair(fileItemD, fileItemA)); + + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsMoved(KItemRange,QList)), DefaultTimeout)); + QCOMPARE(itemsInModel(), QStringList() << "a.txt" << "b.txt" << "c.txt"); +} + +void KFileItemModelTest::testModelConsistencyWhenInsertingItems() +{ + //QSKIP("Temporary disabled", SkipSingle); + + // KFileItemModel prevents that inserting a punch of items sequentially + // results in an itemsInserted()-signal for each item. Instead internally + // a timeout is given that collects such operations and results in only + // one itemsInserted()-signal. However in this test we want to stress + // KFileItemModel to do a lot of insert operation and hence decrease + // the timeout to 1 millisecond. + m_testDir->createFile("1"); + m_model->loadDirectory(m_testDir->url()); + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout)); + QCOMPARE(m_model->count(), 1); + + // Insert 10 items for 20 times. After each insert operation the model consistency + // is checked. + QSet insertedItems; + for (int i = 0; i < 20; ++i) { + QSignalSpy spy(m_model, SIGNAL(itemsInserted(KItemRangeList))); + + for (int j = 0; j < 10; ++j) { + int itemName = qrand(); + while (insertedItems.contains(itemName)) { + itemName = qrand(); + } + insertedItems.insert(itemName); + + m_testDir->createFile(QString::number(itemName)); + } + + m_model->m_dirLister->updateDirectory(m_testDir->url()); + if (spy.count() == 0) { + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout)); + } + + QVERIFY(m_model->isConsistent()); + } + + QCOMPARE(m_model->count(), 201); +} + +void KFileItemModelTest::testItemRangeConsistencyWhenInsertingItems() +{ + QStringList files; + files << "B" << "E" << "G"; + m_testDir->createFiles(files); + + // Due to inserting the 3 items one item-range with index == 0 and + // count == 3 must be given + QSignalSpy spy1(m_model, SIGNAL(itemsInserted(KItemRangeList))); + m_model->loadDirectory(m_testDir->url()); + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout)); + + QCOMPARE(spy1.count(), 1); + QList arguments = spy1.takeFirst(); + KItemRangeList itemRangeList = arguments.at(0).value(); + QCOMPARE(itemRangeList, KItemRangeList() << KItemRange(0, 3)); + + // The indexes of the item-ranges must always be related to the model before + // the items have been inserted. Having: + // 0 1 2 + // B E G + // and inserting A, C, D, F the resulting model will be: + // 0 1 2 3 4 5 6 + // A B C D E F G + // and the item-ranges must be: + // index: 0, count: 1 for A + // index: 1, count: 2 for B, C + // index: 2, count: 1 for G + + files.clear(); + files << "A" << "C" << "D" << "F"; + m_testDir->createFiles(files); + + QSignalSpy spy2(m_model, SIGNAL(itemsInserted(KItemRangeList))); + m_model->m_dirLister->updateDirectory(m_testDir->url()); + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout)); + + QCOMPARE(spy2.count(), 1); + arguments = spy2.takeFirst(); + itemRangeList = arguments.at(0).value(); + QCOMPARE(itemRangeList, KItemRangeList() << KItemRange(0, 1) << KItemRange(1, 2) << KItemRange(2, 1)); +} + +void KFileItemModelTest::testExpandItems() +{ + // Test expanding subfolders in a folder with the items "a/", "a/a/", "a/a/1", "a/a-1/", "a/a-1/1". + // Besides testing the basic item expansion functionality, the test makes sure that + // KFileItemModel::expansionLevelsCompare(const KFileItem& a, const KFileItem& b) + // yields the correct result for "a/a/1" and "a/a-1/", whis is non-trivial because they share the + // first three characters. + QSet originalModelRoles = m_model->roles(); + QSet modelRoles = originalModelRoles; + modelRoles << "isExpanded" << "isExpandable" << "expandedParentsCount"; + m_model->setRoles(modelRoles); + + QStringList files; + files << "a/a/1" << "a/a-1/1"; // missing folders are created automatically + m_testDir->createFiles(files); + + // Store the URLs of all folders in a set. + QSet allFolders; + allFolders << KUrl(m_testDir->name() + 'a') << KUrl(m_testDir->name() + "a/a") << KUrl(m_testDir->name() + "a/a-1"); + + m_model->loadDirectory(m_testDir->url()); + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout)); + + // So far, the model contains only "a/" + QCOMPARE(m_model->count(), 1); + QVERIFY(m_model->isExpandable(0)); + QVERIFY(!m_model->isExpanded(0)); + QVERIFY(m_model->expandedDirectories().empty()); + + QSignalSpy spyInserted(m_model, SIGNAL(itemsInserted(KItemRangeList))); + + // Expand the folder "a/" -> "a/a/" and "a/a-1/" become visible + m_model->setExpanded(0, true); + QVERIFY(m_model->isExpanded(0)); + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout)); + QCOMPARE(m_model->count(), 3); // 3 items: "a/", "a/a/", "a/a-1/" + QCOMPARE(m_model->expandedDirectories(), QSet() << KUrl(m_testDir->name() + 'a')); + + QCOMPARE(spyInserted.count(), 1); + KItemRangeList itemRangeList = spyInserted.takeFirst().at(0).value(); + QCOMPARE(itemRangeList, KItemRangeList() << KItemRange(1, 2)); // 2 new items "a/a/" and "a/a-1/" with indices 1 and 2 + + QVERIFY(m_model->isExpandable(1)); + QVERIFY(!m_model->isExpanded(1)); + QVERIFY(m_model->isExpandable(2)); + QVERIFY(!m_model->isExpanded(2)); + + // Expand the folder "a/a/" -> "a/a/1" becomes visible + m_model->setExpanded(1, true); + QVERIFY(m_model->isExpanded(1)); + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout)); + QCOMPARE(m_model->count(), 4); // 4 items: "a/", "a/a/", "a/a/1", "a/a-1/" + QCOMPARE(m_model->expandedDirectories(), QSet() << KUrl(m_testDir->name() + 'a') << KUrl(m_testDir->name() + "a/a")); + + QCOMPARE(spyInserted.count(), 1); + itemRangeList = spyInserted.takeFirst().at(0).value(); + QCOMPARE(itemRangeList, KItemRangeList() << KItemRange(2, 1)); // 1 new item "a/a/1" with index 2 + + QVERIFY(!m_model->isExpandable(2)); + QVERIFY(!m_model->isExpanded(2)); + + // Expand the folder "a/a-1/" -> "a/a-1/1" becomes visible + m_model->setExpanded(3, true); + QVERIFY(m_model->isExpanded(3)); + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout)); + QCOMPARE(m_model->count(), 5); // 5 items: "a/", "a/a/", "a/a/1", "a/a-1/", "a/a-1/1" + QCOMPARE(m_model->expandedDirectories(), allFolders); + + QCOMPARE(spyInserted.count(), 1); + itemRangeList = spyInserted.takeFirst().at(0).value(); + QCOMPARE(itemRangeList, KItemRangeList() << KItemRange(4, 1)); // 1 new item "a/a-1/1" with index 4 + + QVERIFY(!m_model->isExpandable(4)); + QVERIFY(!m_model->isExpanded(4)); + + QSignalSpy spyRemoved(m_model, SIGNAL(itemsRemoved(KItemRangeList))); + + // Collapse the top-level folder -> all other items should disappear + m_model->setExpanded(0, false); + QVERIFY(!m_model->isExpanded(0)); + QCOMPARE(m_model->count(), 1); + QVERIFY(!m_model->expandedDirectories().contains(KUrl(m_testDir->name() + 'a'))); // TODO: Make sure that child URLs are also removed + + QCOMPARE(spyRemoved.count(), 1); + itemRangeList = spyRemoved.takeFirst().at(0).value(); + QCOMPARE(itemRangeList, KItemRangeList() << KItemRange(1, 4)); // 4 items removed + QVERIFY(m_model->isConsistent()); + + // Clear the model, reload the folder and try to restore the expanded folders. + m_model->clear(); + QCOMPARE(m_model->count(), 0); + QVERIFY(m_model->expandedDirectories().empty()); + + m_model->loadDirectory(m_testDir->url()); + m_model->restoreExpandedDirectories(allFolders); + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(directoryLoadingCompleted()), DefaultTimeout)); + QCOMPARE(m_model->count(), 5); // 5 items: "a/", "a/a/", "a/a/1", "a/a-1/", "a/a-1/1" + QVERIFY(m_model->isExpanded(0)); + QVERIFY(m_model->isExpanded(1)); + QVERIFY(!m_model->isExpanded(2)); + QVERIFY(m_model->isExpanded(3)); + QVERIFY(!m_model->isExpanded(4)); + QCOMPARE(m_model->expandedDirectories(), allFolders); + QVERIFY(m_model->isConsistent()); + + // Move to a sub folder, then call restoreExpandedFolders() *before* going back. + // This is how DolphinView restores the expanded folders when navigating in history. + m_model->loadDirectory(KUrl(m_testDir->name() + "a/a/")); + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(directoryLoadingCompleted()), DefaultTimeout)); + QCOMPARE(m_model->count(), 1); // 1 item: "1" + m_model->restoreExpandedDirectories(allFolders); + m_model->loadDirectory(m_testDir->url()); + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(directoryLoadingCompleted()), DefaultTimeout)); + QCOMPARE(m_model->count(), 5); // 5 items: "a/", "a/a/", "a/a/1", "a/a-1/", "a/a-1/1" + QCOMPARE(m_model->expandedDirectories(), allFolders); + + // Remove all expanded items by changing the roles + spyRemoved.clear(); + m_model->setRoles(originalModelRoles); + QVERIFY(!m_model->isExpanded(0)); + QCOMPARE(m_model->count(), 1); + QVERIFY(!m_model->expandedDirectories().contains(KUrl(m_testDir->name() + 'a'))); + + QCOMPARE(spyRemoved.count(), 1); + itemRangeList = spyRemoved.takeFirst().at(0).value(); + QCOMPARE(itemRangeList, KItemRangeList() << KItemRange(1, 4)); // 4 items removed + QVERIFY(m_model->isConsistent()); +} + +void KFileItemModelTest::testExpandParentItems() +{ + // Create a tree structure of folders: + // a 1/ + // a 1/b1/ + // a 1/b1/c1/ + // a2/ + // a2/b2/ + // a2/b2/c2/ + // a2/b2/c2/d2/ + QSet modelRoles = m_model->roles(); + modelRoles << "isExpanded" << "isExpandable" << "expandedParentsCount"; + m_model->setRoles(modelRoles); + + QStringList files; + files << "a 1/b1/c1/file.txt" << "a2/b2/c2/d2/file.txt"; // missing folders are created automatically + m_testDir->createFiles(files); + + m_model->loadDirectory(m_testDir->url()); + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout)); + + // So far, the model contains only "a 1/" and "a2/". + QCOMPARE(m_model->count(), 2); + QVERIFY(m_model->expandedDirectories().empty()); + + // Expand the parents of "a2/b2/c2". + m_model->expandParentDirectories(KUrl(m_testDir->name() + "a2/b2/c2")); + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(directoryLoadingCompleted()), DefaultTimeout)); + + // The model should now contain "a 1/", "a2/", "a2/b2/", and "a2/b2/c2/". + // It's important that only the parents of "a1/b1/c1" are expanded. + QCOMPARE(m_model->count(), 4); + QVERIFY(!m_model->isExpanded(0)); + QVERIFY(m_model->isExpanded(1)); + QVERIFY(m_model->isExpanded(2)); + QVERIFY(!m_model->isExpanded(3)); + + // Expand the parents of "a 1/b1". + m_model->expandParentDirectories(KUrl(m_testDir->name() + "a 1/b1")); + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(directoryLoadingCompleted()), DefaultTimeout)); + + // The model should now contain "a 1/", "a 1/b1/", "a2/", "a2/b2", and "a2/b2/c2/". + // It's important that only the parents of "a 1/b1/" and "a2/b2/c2/" are expanded. + QCOMPARE(m_model->count(), 5); + QVERIFY(m_model->isExpanded(0)); + QVERIFY(!m_model->isExpanded(1)); + QVERIFY(m_model->isExpanded(2)); + QVERIFY(m_model->isExpanded(3)); + QVERIFY(!m_model->isExpanded(4)); + QVERIFY(m_model->isConsistent()); + + // Expand "a 1/b1/". + m_model->setExpanded(1, true); + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(directoryLoadingCompleted()), DefaultTimeout)); + QCOMPARE(m_model->count(), 6); + QVERIFY(m_model->isExpanded(0)); + QVERIFY(m_model->isExpanded(1)); + QVERIFY(!m_model->isExpanded(2)); + QVERIFY(m_model->isExpanded(3)); + QVERIFY(m_model->isExpanded(4)); + QVERIFY(!m_model->isExpanded(5)); + QVERIFY(m_model->isConsistent()); + + // Collapse "a 1/b1/" again, and verify that the previous state is restored. + m_model->setExpanded(1, false); + QCOMPARE(m_model->count(), 5); + QVERIFY(m_model->isExpanded(0)); + QVERIFY(!m_model->isExpanded(1)); + QVERIFY(m_model->isExpanded(2)); + QVERIFY(m_model->isExpanded(3)); + QVERIFY(!m_model->isExpanded(4)); + QVERIFY(m_model->isConsistent()); +} + +/** + * Renaming an expanded folder by prepending its name with a dot makes it + * hidden. Verify that this does not cause an inconsistent model state and + * a crash later on, see https://bugs.kde.org/show_bug.cgi?id=311947 + */ +void KFileItemModelTest::testMakeExpandedItemHidden() +{ + QSet modelRoles = m_model->roles(); + modelRoles << "isExpanded" << "isExpandable" << "expandedParentsCount"; + m_model->setRoles(modelRoles); + + QStringList files; + m_testDir->createFile("1a/2a/3a"); + m_testDir->createFile("1a/2a/3b"); + m_testDir->createFile("1a/2b"); + m_testDir->createFile("1b"); + + m_model->loadDirectory(m_testDir->url()); + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout)); + + // So far, the model contains only "1a/" and "1b". + QCOMPARE(m_model->count(), 2); + m_model->setExpanded(0, true); + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout)); + + // Now "1a/2a" and "1a/2b" have appeared. + QCOMPARE(m_model->count(), 4); + m_model->setExpanded(1, true); + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout)); + QCOMPARE(m_model->count(), 6); + + // Rename "1a/2" and make it hidden. + const QString oldPath = m_model->fileItem(0).url().path() + "/2a"; + const QString newPath = m_model->fileItem(0).url().path() + "/.2a"; + + KIO::SimpleJob* job = KIO::rename(oldPath, newPath, KIO::HideProgressInfo); + bool ok = job->exec(); + QVERIFY(ok); + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsRemoved(KItemRangeList)), DefaultTimeout)); + + // "1a/2" and its subfolders have disappeared now. + QVERIFY(m_model->isConsistent()); + QCOMPARE(m_model->count(), 3); + + m_model->setExpanded(0, false); + QCOMPARE(m_model->count(), 2); + +} + +void KFileItemModelTest::testRemoveFilteredExpandedItems() +{ + QSet originalModelRoles = m_model->roles(); + QSet modelRoles = originalModelRoles; + modelRoles << "isExpanded" << "isExpandable" << "expandedParentsCount"; + m_model->setRoles(modelRoles); + + QStringList files; + files << "folder/child" << "file"; // missing folders are created automatically + m_testDir->createFiles(files); + + m_model->loadDirectory(m_testDir->url()); + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout)); + + // So far, the model contains only "folder/" and "file". + QCOMPARE(m_model->count(), 2); + QVERIFY(m_model->isExpandable(0)); + QVERIFY(!m_model->isExpandable(1)); + QVERIFY(!m_model->isExpanded(0)); + QVERIFY(!m_model->isExpanded(1)); + QCOMPARE(itemsInModel(), QStringList() << "folder" << "file"); + + // Expand "folder" -> "folder/child" becomes visible. + m_model->setExpanded(0, true); + QVERIFY(m_model->isExpanded(0)); + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout)); + QCOMPARE(itemsInModel(), QStringList() << "folder" << "child" << "file"); + + // Add a name filter. + m_model->setNameFilter("f"); + QCOMPARE(itemsInModel(), QStringList() << "folder" << "file"); + + m_model->setNameFilter("fo"); + QCOMPARE(itemsInModel(), QStringList() << "folder"); + + // Remove all expanded items by changing the roles + m_model->setRoles(originalModelRoles); + QVERIFY(!m_model->isExpanded(0)); + QCOMPARE(itemsInModel(), QStringList() << "folder"); + + // Remove the name filter and verify that "folder/child" does not reappear. + m_model->setNameFilter(QString()); + QCOMPARE(itemsInModel(), QStringList() << "folder" << "file"); +} + +void KFileItemModelTest::testSorting() +{ + // Create some files with different sizes and modification times to check the different sorting options + QDateTime now = QDateTime::currentDateTime(); + + QSet roles; + roles.insert("text"); + roles.insert("isExpanded"); + roles.insert("isExpandable"); + roles.insert("expandedParentsCount"); + m_model->setRoles(roles); + + m_testDir->createDir("c/c-2"); + m_testDir->createFile("c/c-2/c-3"); + m_testDir->createFile("c/c-1"); + + m_testDir->createFile("a", "A file", now.addDays(-3)); + m_testDir->createFile("b", "A larger file", now.addDays(0)); + m_testDir->createDir("c", now.addDays(-2)); + m_testDir->createFile("d", "The largest file in this directory", now.addDays(-1)); + m_testDir->createFile("e", "An even larger file", now.addDays(-4)); + m_testDir->createFile(".f"); + + m_model->loadDirectory(m_testDir->url()); + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout)); + + int index = m_model->index(KUrl(m_testDir->url().url() + 'c')); + m_model->setExpanded(index, true); + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout)); + + index = m_model->index(KUrl(m_testDir->url().url() + "c/c-2")); + m_model->setExpanded(index, true); + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout)); + + // Default: Sort by Name, ascending + QCOMPARE(m_model->sortRole(), QByteArray("text")); + QCOMPARE(m_model->sortOrder(), Qt::AscendingOrder); + QVERIFY(m_model->sortDirectoriesFirst()); + QVERIFY(!m_model->showHiddenFiles()); + QCOMPARE(itemsInModel(), QStringList() << "c" << "c-2" << "c-3" << "c-1" << "a" << "b" << "d" << "e"); + + QSignalSpy spyItemsMoved(m_model, SIGNAL(itemsMoved(KItemRange,QList))); + + // Sort by Name, ascending, 'Sort Folders First' disabled + m_model->setSortDirectoriesFirst(false); + QCOMPARE(m_model->sortRole(), QByteArray("text")); + QCOMPARE(m_model->sortOrder(), Qt::AscendingOrder); + QCOMPARE(itemsInModel(), QStringList() << "a" << "b" << "c" << "c-1" << "c-2" << "c-3" << "d" << "e"); + QCOMPARE(spyItemsMoved.count(), 1); + QCOMPARE(spyItemsMoved.first().at(0).value(), KItemRange(0, 6)); + QCOMPARE(spyItemsMoved.takeFirst().at(1).value >(), QList() << 2 << 4 << 5 << 3 << 0 << 1); + + // Sort by Name, descending + m_model->setSortDirectoriesFirst(true); + m_model->setSortOrder(Qt::DescendingOrder); + QCOMPARE(m_model->sortRole(), QByteArray("text")); + QCOMPARE(m_model->sortOrder(), Qt::DescendingOrder); + QCOMPARE(itemsInModel(), QStringList() << "c" << "c-2" << "c-3" << "c-1" << "e" << "d" << "b" << "a"); + QCOMPARE(spyItemsMoved.count(), 2); + QCOMPARE(spyItemsMoved.first().at(0).value(), KItemRange(0, 6)); + QCOMPARE(spyItemsMoved.takeFirst().at(1).value >(), QList() << 4 << 5 << 0 << 3 << 1 << 2); + QCOMPARE(spyItemsMoved.first().at(0).value(), KItemRange(4, 4)); + QCOMPARE(spyItemsMoved.takeFirst().at(1).value >(), QList() << 7 << 6 << 5 << 4); + + // Sort by Date, descending + m_model->setSortDirectoriesFirst(true); + m_model->setSortRole("date"); + QCOMPARE(m_model->sortRole(), QByteArray("date")); + QCOMPARE(m_model->sortOrder(), Qt::DescendingOrder); + QCOMPARE(itemsInModel(), QStringList() << "c" << "c-2" << "c-3" << "c-1" << "b" << "d" << "a" << "e"); + QCOMPARE(spyItemsMoved.count(), 1); + QCOMPARE(spyItemsMoved.first().at(0).value(), KItemRange(4, 4)); + QCOMPARE(spyItemsMoved.takeFirst().at(1).value >(), QList() << 7 << 5 << 4 << 6); + + // Sort by Date, ascending + m_model->setSortOrder(Qt::AscendingOrder); + QCOMPARE(m_model->sortRole(), QByteArray("date")); + QCOMPARE(m_model->sortOrder(), Qt::AscendingOrder); + QCOMPARE(itemsInModel(), QStringList() << "c" << "c-2" << "c-3" << "c-1" << "e" << "a" << "d" << "b"); + QCOMPARE(spyItemsMoved.count(), 1); + QCOMPARE(spyItemsMoved.first().at(0).value(), KItemRange(4, 4)); + QCOMPARE(spyItemsMoved.takeFirst().at(1).value >(), QList() << 7 << 6 << 5 << 4); + + // Sort by Date, ascending, 'Sort Folders First' disabled + m_model->setSortDirectoriesFirst(false); + QCOMPARE(m_model->sortRole(), QByteArray("date")); + QCOMPARE(m_model->sortOrder(), Qt::AscendingOrder); + QVERIFY(!m_model->sortDirectoriesFirst()); + QCOMPARE(itemsInModel(), QStringList() << "e" << "a" << "c" << "c-1" << "c-2" << "c-3" << "d" << "b"); + QCOMPARE(spyItemsMoved.count(), 1); + QCOMPARE(spyItemsMoved.first().at(0).value(), KItemRange(0, 6)); + QCOMPARE(spyItemsMoved.takeFirst().at(1).value >(), QList() << 2 << 4 << 5 << 3 << 0 << 1); + + // Sort by Name, ascending, 'Sort Folders First' disabled + m_model->setSortRole("text"); + QCOMPARE(m_model->sortOrder(), Qt::AscendingOrder); + QVERIFY(!m_model->sortDirectoriesFirst()); + QCOMPARE(itemsInModel(), QStringList() << "a" << "b" << "c" << "c-1" << "c-2" << "c-3" << "d" << "e"); + QCOMPARE(spyItemsMoved.count(), 1); + QCOMPARE(spyItemsMoved.first().at(0).value(), KItemRange(0, 8)); + QCOMPARE(spyItemsMoved.takeFirst().at(1).value >(), QList() << 7 << 0 << 2 << 3 << 4 << 5 << 6 << 1); + + // Sort by Size, ascending, 'Sort Folders First' disabled + m_model->setSortRole("size"); + QCOMPARE(m_model->sortRole(), QByteArray("size")); + QCOMPARE(m_model->sortOrder(), Qt::AscendingOrder); + QVERIFY(!m_model->sortDirectoriesFirst()); + QCOMPARE(itemsInModel(), QStringList() << "c" << "c-2" << "c-3" << "c-1" << "a" << "b" << "e" << "d"); + QCOMPARE(spyItemsMoved.count(), 1); + QCOMPARE(spyItemsMoved.first().at(0).value(), KItemRange(0, 8)); + QCOMPARE(spyItemsMoved.takeFirst().at(1).value >(), QList() << 4 << 5 << 0 << 3 << 1 << 2 << 7 << 6); + + // In 'Sort by Size' mode, folders are always first -> changing 'Sort Folders First' does not resort the model + m_model->setSortDirectoriesFirst(true); + QCOMPARE(m_model->sortRole(), QByteArray("size")); + QCOMPARE(m_model->sortOrder(), Qt::AscendingOrder); + QVERIFY(m_model->sortDirectoriesFirst()); + QCOMPARE(itemsInModel(), QStringList() << "c" << "c-2" << "c-3" << "c-1" << "a" << "b" << "e" << "d"); + QCOMPARE(spyItemsMoved.count(), 0); + + // Sort by Size, descending, 'Sort Folders First' enabled + m_model->setSortOrder(Qt::DescendingOrder); + QCOMPARE(m_model->sortRole(), QByteArray("size")); + QCOMPARE(m_model->sortOrder(), Qt::DescendingOrder); + QVERIFY(m_model->sortDirectoriesFirst()); + QCOMPARE(itemsInModel(), QStringList() << "c" << "c-2" << "c-3" << "c-1" << "d" << "e" << "b" << "a"); + QCOMPARE(spyItemsMoved.count(), 1); + QCOMPARE(spyItemsMoved.first().at(0).value(), KItemRange(4, 4)); + QCOMPARE(spyItemsMoved.takeFirst().at(1).value >(), QList() << 7 << 6 << 5 << 4); + + // TODO: Sort by other roles; show/hide hidden files +} + +void KFileItemModelTest::testIndexForKeyboardSearch() +{ + QStringList files; + files << "a" << "aa" << "Image.jpg" << "Image.png" << "Text" << "Text1" << "Text2" << "Text11"; + m_testDir->createFiles(files); + + m_model->loadDirectory(m_testDir->url()); + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout)); + + // Search from index 0 + QCOMPARE(m_model->indexForKeyboardSearch("a", 0), 0); + QCOMPARE(m_model->indexForKeyboardSearch("aa", 0), 1); + QCOMPARE(m_model->indexForKeyboardSearch("i", 0), 2); + QCOMPARE(m_model->indexForKeyboardSearch("image", 0), 2); + QCOMPARE(m_model->indexForKeyboardSearch("image.jpg", 0), 2); + QCOMPARE(m_model->indexForKeyboardSearch("image.png", 0), 3); + QCOMPARE(m_model->indexForKeyboardSearch("t", 0), 4); + QCOMPARE(m_model->indexForKeyboardSearch("text", 0), 4); + QCOMPARE(m_model->indexForKeyboardSearch("text1", 0), 5); + QCOMPARE(m_model->indexForKeyboardSearch("text2", 0), 6); + QCOMPARE(m_model->indexForKeyboardSearch("text11", 0), 7); + + // Start a search somewhere in the middle + QCOMPARE(m_model->indexForKeyboardSearch("a", 1), 1); + QCOMPARE(m_model->indexForKeyboardSearch("i", 3), 3); + QCOMPARE(m_model->indexForKeyboardSearch("t", 5), 5); + QCOMPARE(m_model->indexForKeyboardSearch("text1", 6), 7); + + // Test searches that go past the last item back to index 0 + QCOMPARE(m_model->indexForKeyboardSearch("a", 2), 0); + QCOMPARE(m_model->indexForKeyboardSearch("i", 7), 2); + QCOMPARE(m_model->indexForKeyboardSearch("image.jpg", 3), 2); + QCOMPARE(m_model->indexForKeyboardSearch("text2", 7), 6); + + // Test searches that yield no result + QCOMPARE(m_model->indexForKeyboardSearch("aaa", 0), -1); + QCOMPARE(m_model->indexForKeyboardSearch("b", 0), -1); + QCOMPARE(m_model->indexForKeyboardSearch("image.svg", 0), -1); + QCOMPARE(m_model->indexForKeyboardSearch("text3", 0), -1); + QCOMPARE(m_model->indexForKeyboardSearch("text3", 5), -1); + + // Test upper case searches (note that search is case insensitive) + QCOMPARE(m_model->indexForKeyboardSearch("A", 0), 0); + QCOMPARE(m_model->indexForKeyboardSearch("aA", 0), 1); + QCOMPARE(m_model->indexForKeyboardSearch("TexT", 5), 5); + QCOMPARE(m_model->indexForKeyboardSearch("IMAGE", 4), 2); + + // TODO: Maybe we should also test keyboard searches in directories which are not sorted by Name? +} + +void KFileItemModelTest::testNameFilter() +{ + QStringList files; + files << "A1" << "A2" << "Abc" << "Bcd" << "Cde"; + m_testDir->createFiles(files); + + m_model->loadDirectory(m_testDir->url()); + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout)); + + m_model->setNameFilter("A"); // Shows A1, A2 and Abc + QCOMPARE(m_model->count(), 3); + + m_model->setNameFilter("A2"); // Shows only A2 + QCOMPARE(m_model->count(), 1); + + m_model->setNameFilter("A2"); // Shows only A1 + QCOMPARE(m_model->count(), 1); + + m_model->setNameFilter("Bc"); // Shows "Abc" and "Bcd" + QCOMPARE(m_model->count(), 2); + + m_model->setNameFilter("bC"); // Shows "Abc" and "Bcd" + QCOMPARE(m_model->count(), 2); + + m_model->setNameFilter(QString()); // Shows again all items + QCOMPARE(m_model->count(), 5); +} + +/** + * Verifies that we do not crash when adding a KFileItem with an empty path. + * Before this issue was fixed, KFileItemModel::expandedParentsCountCompare() + * tried to always read the first character of the path, even if the path is empty. + */ +void KFileItemModelTest::testEmptyPath() +{ + QSet roles; + roles.insert("text"); + roles.insert("isExpanded"); + roles.insert("isExpandable"); + roles.insert("expandedParentsCount"); + m_model->setRoles(roles); + + const KUrl emptyUrl; + QVERIFY(emptyUrl.path().isEmpty()); + + const KUrl url("file:///test/"); + + KFileItemList items; + items << KFileItem(emptyUrl, QString(), KFileItem::Unknown) << KFileItem(url, QString(), KFileItem::Unknown); + m_model->slotItemsAdded(emptyUrl, items); + m_model->slotCompleted(); +} + +/** + * Verifies that the 'isExpanded' state of folders does not change when the + * 'refreshItems' signal is received, see https://bugs.kde.org/show_bug.cgi?id=299675. + */ +void KFileItemModelTest::testRefreshExpandedItem() +{ + QSet modelRoles = m_model->roles(); + modelRoles << "isExpanded" << "isExpandable" << "expandedParentsCount"; + m_model->setRoles(modelRoles); + + QStringList files; + files << "a/1" << "a/2" << "3" << "4"; + m_testDir->createFiles(files); + + m_model->loadDirectory(m_testDir->url()); + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout)); + QCOMPARE(m_model->count(), 3); // "a/", "3", "4" + + m_model->setExpanded(0, true); + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout)); + QCOMPARE(m_model->count(), 5); // "a/", "a/1", "a/2", "3", "4" + QVERIFY(m_model->isExpanded(0)); + + QSignalSpy spyItemsChanged(m_model, SIGNAL(itemsChanged(KItemRangeList,QSet))); + + const KFileItem item = m_model->fileItem(0); + m_model->slotRefreshItems(QList >() << qMakePair(item, item)); + QVERIFY(!spyItemsChanged.isEmpty()); + + QCOMPARE(m_model->count(), 5); // "a/", "a/1", "a/2", "3", "4" + QVERIFY(m_model->isExpanded(0)); +} + +/** + * Verify that removing hidden files and folders from the model does not + * result in a crash, see https://bugs.kde.org/show_bug.cgi?id=314046 + */ +void KFileItemModelTest::testRemoveHiddenItems() +{ + m_testDir->createDir(".a"); + m_testDir->createDir(".b"); + m_testDir->createDir("c"); + m_testDir->createDir("d"); + m_testDir->createFiles(QStringList() << ".f" << ".g" << "h" << "i"); + + QSignalSpy spyItemsInserted(m_model, SIGNAL(itemsInserted(KItemRangeList))); + QSignalSpy spyItemsRemoved(m_model, SIGNAL(itemsRemoved(KItemRangeList))); + + m_model->setShowHiddenFiles(true); + m_model->loadDirectory(m_testDir->url()); + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout)); + QCOMPARE(itemsInModel(), QStringList() << ".a" << ".b" << "c" << "d" <<".f" << ".g" << "h" << "i"); + QCOMPARE(spyItemsInserted.count(), 1); + QCOMPARE(spyItemsRemoved.count(), 0); + KItemRangeList itemRangeList = spyItemsInserted.takeFirst().at(0).value(); + QCOMPARE(itemRangeList, KItemRangeList() << KItemRange(0, 8)); + + m_model->setShowHiddenFiles(false); + QCOMPARE(itemsInModel(), QStringList() << "c" << "d" << "h" << "i"); + QCOMPARE(spyItemsInserted.count(), 0); + QCOMPARE(spyItemsRemoved.count(), 1); + itemRangeList = spyItemsRemoved.takeFirst().at(0).value(); + QCOMPARE(itemRangeList, KItemRangeList() << KItemRange(0, 2) << KItemRange(4, 2)); + + m_model->setShowHiddenFiles(true); + QCOMPARE(itemsInModel(), QStringList() << ".a" << ".b" << "c" << "d" <<".f" << ".g" << "h" << "i"); + QCOMPARE(spyItemsInserted.count(), 1); + QCOMPARE(spyItemsRemoved.count(), 0); + itemRangeList = spyItemsInserted.takeFirst().at(0).value(); + QCOMPARE(itemRangeList, KItemRangeList() << KItemRange(0, 2) << KItemRange(2, 2)); + + m_model->clear(); + QCOMPARE(itemsInModel(), QStringList()); + QCOMPARE(spyItemsInserted.count(), 0); + QCOMPARE(spyItemsRemoved.count(), 1); + itemRangeList = spyItemsRemoved.takeFirst().at(0).value(); + QCOMPARE(itemRangeList, KItemRangeList() << KItemRange(0, 8)); + + // Hiding hidden files makes the dir lister emit its itemsDeleted signal. + // Verify that this does not make the model crash. + m_model->setShowHiddenFiles(false); +} + +/** + * Verify that filtered items are removed when their parent is collapsed. + */ +void KFileItemModelTest::collapseParentOfHiddenItems() +{ + QSet modelRoles = m_model->roles(); + modelRoles << "isExpanded" << "isExpandable" << "expandedParentsCount"; + m_model->setRoles(modelRoles); + + QStringList files; + files << "a/1" << "a/b/1" << "a/b/c/1" << "a/b/c/d/1"; + m_testDir->createFiles(files); + + m_model->loadDirectory(m_testDir->url()); + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout)); + QCOMPARE(m_model->count(), 1); // Only "a/" + + // Expand "a/". + m_model->setExpanded(0, true); + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout)); + QCOMPARE(m_model->count(), 3); // 3 items: "a/", "a/b/", "a/1" + + // Expand "a/b/". + m_model->setExpanded(1, true); + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout)); + QCOMPARE(m_model->count(), 5); // 5 items: "a/", "a/b/", "a/b/c", "a/b/1", "a/1" + + // Expand "a/b/c/". + m_model->setExpanded(2, true); + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout)); + QCOMPARE(m_model->count(), 7); // 7 items: "a/", "a/b/", "a/b/c", "a/b/c/d/", "a/b/c/1", "a/b/1", "a/1" + + // Set a name filter that matches nothing -> only the expanded folders remain. + m_model->setNameFilter("xyz"); + QCOMPARE(m_model->count(), 3); + QCOMPARE(itemsInModel(), QStringList() << "a" << "b" << "c"); + + // Collapse the folder "a/". + QSignalSpy spyItemsRemoved(m_model, SIGNAL(itemsRemoved(KItemRangeList))); + m_model->setExpanded(0, false); + QCOMPARE(spyItemsRemoved.count(), 1); + QCOMPARE(m_model->count(), 1); + QCOMPARE(itemsInModel(), QStringList() << "a"); + + // Remove the filter -> no files should appear (and we should not get a crash). + m_model->setNameFilter(QString()); + QCOMPARE(m_model->count(), 1); +} + +/** + * Verify that filtered items are removed when their parent is deleted. + */ +void KFileItemModelTest::removeParentOfHiddenItems() +{ + QSet modelRoles = m_model->roles(); + modelRoles << "isExpanded" << "isExpandable" << "expandedParentsCount"; + m_model->setRoles(modelRoles); + + QStringList files; + files << "a/1" << "a/b/1" << "a/b/c/1" << "a/b/c/d/1"; + m_testDir->createFiles(files); + + m_model->loadDirectory(m_testDir->url()); + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout)); + QCOMPARE(m_model->count(), 1); // Only "a/" + + // Expand "a/". + m_model->setExpanded(0, true); + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout)); + QCOMPARE(m_model->count(), 3); // 3 items: "a/", "a/b/", "a/1" + + // Expand "a/b/". + m_model->setExpanded(1, true); + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout)); + QCOMPARE(m_model->count(), 5); // 5 items: "a/", "a/b/", "a/b/c", "a/b/1", "a/1" + + // Expand "a/b/c/". + m_model->setExpanded(2, true); + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout)); + QCOMPARE(m_model->count(), 7); // 7 items: "a/", "a/b/", "a/b/c", "a/b/c/d/", "a/b/c/1", "a/b/1", "a/1" + + // Set a name filter that matches nothing -> only the expanded folders remain. + m_model->setNameFilter("xyz"); + QCOMPARE(m_model->count(), 3); + QCOMPARE(itemsInModel(), QStringList() << "a" << "b" << "c"); + + // Simulate the deletion of the directory "a/b/". + QSignalSpy spyItemsRemoved(m_model, SIGNAL(itemsRemoved(KItemRangeList))); + m_model->slotItemsDeleted(KFileItemList() << m_model->fileItem(1)); + QCOMPARE(spyItemsRemoved.count(), 1); + QCOMPARE(m_model->count(), 1); + QCOMPARE(itemsInModel(), QStringList() << "a"); + + // Remove the filter -> only the file "a/1" should appear. + m_model->setNameFilter(QString()); + QCOMPARE(m_model->count(), 2); + QCOMPARE(itemsInModel(), QStringList() << "a" << "1"); +} + +/** + * Create a tree structure where parent-child relationships can not be + * determined by parsing the URLs, and verify that KFileItemModel + * handles them correctly. + */ +void KFileItemModelTest::testGeneralParentChildRelationships() +{ + QSet modelRoles = m_model->roles(); + modelRoles << "isExpanded" << "isExpandable" << "expandedParentsCount"; + m_model->setRoles(modelRoles); + + QStringList files; + files << "parent1/realChild1/realGrandChild1" << "parent2/realChild2/realGrandChild2"; + m_testDir->createFiles(files); + + m_model->loadDirectory(m_testDir->url()); + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout)); + QCOMPARE(itemsInModel(), QStringList() << "parent1" << "parent2"); + + // Expand all folders. + m_model->setExpanded(0, true); + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout)); + QCOMPARE(itemsInModel(), QStringList() << "parent1" << "realChild1" << "parent2"); + + m_model->setExpanded(1, true); + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout)); + QCOMPARE(itemsInModel(), QStringList() << "parent1" << "realChild1" << "realGrandChild1" << "parent2"); + + m_model->setExpanded(3, true); + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout)); + QCOMPARE(itemsInModel(), QStringList() << "parent1" << "realChild1" << "realGrandChild1" << "parent2" << "realChild2"); + + m_model->setExpanded(4, true); + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout)); + QCOMPARE(itemsInModel(), QStringList() << "parent1" << "realChild1" << "realGrandChild1" << "parent2" << "realChild2" << "realGrandChild2"); + + // Add some more children and grand-children. + const KUrl parent1 = m_model->fileItem(0).url(); + const KUrl parent2 = m_model->fileItem(3).url(); + const KUrl realChild1 = m_model->fileItem(1).url(); + const KUrl realChild2 = m_model->fileItem(4).url(); + + m_model->slotItemsAdded(parent1, KFileItemList() << KFileItem(KUrl("child1"), QString(), KFileItem::Unknown)); + m_model->slotCompleted(); + QCOMPARE(itemsInModel(), QStringList() << "parent1" << "realChild1" << "realGrandChild1" << "child1" << "parent2" << "realChild2" << "realGrandChild2"); + + m_model->slotItemsAdded(parent2, KFileItemList() << KFileItem(KUrl("child2"), QString(), KFileItem::Unknown)); + m_model->slotCompleted(); + QCOMPARE(itemsInModel(), QStringList() << "parent1" << "realChild1" << "realGrandChild1" << "child1" << "parent2" << "realChild2" << "realGrandChild2" << "child2"); + + m_model->slotItemsAdded(realChild1, KFileItemList() << KFileItem(KUrl("grandChild1"), QString(), KFileItem::Unknown)); + m_model->slotCompleted(); + QCOMPARE(itemsInModel(), QStringList() << "parent1" << "realChild1" << "grandChild1" << "realGrandChild1" << "child1" << "parent2" << "realChild2" << "realGrandChild2" << "child2"); + + m_model->slotItemsAdded(realChild1, KFileItemList() << KFileItem(KUrl("grandChild1"), QString(), KFileItem::Unknown)); + m_model->slotCompleted(); + QCOMPARE(itemsInModel(), QStringList() << "parent1" << "realChild1" << "grandChild1" << "realGrandChild1" << "child1" << "parent2" << "realChild2" << "realGrandChild2" << "child2"); + + m_model->slotItemsAdded(realChild2, KFileItemList() << KFileItem(KUrl("grandChild2"), QString(), KFileItem::Unknown)); + m_model->slotCompleted(); + QCOMPARE(itemsInModel(), QStringList() << "parent1" << "realChild1" << "grandChild1" << "realGrandChild1" << "child1" << "parent2" << "realChild2" << "grandChild2" << "realGrandChild2" << "child2"); + + // Set a name filter that matches nothing -> only expanded folders remain. + QSignalSpy itemsRemovedSpy(m_model, SIGNAL(itemsRemoved(KItemRangeList))); + m_model->setNameFilter("xyz"); + QCOMPARE(itemsInModel(), QStringList() << "parent1" << "realChild1" << "parent2" << "realChild2"); + QCOMPARE(itemsRemovedSpy.count(), 1); + QList arguments = itemsRemovedSpy.takeFirst(); + KItemRangeList itemRangeList = arguments.at(0).value(); + QCOMPARE(itemRangeList, KItemRangeList() << KItemRange(2, 3) << KItemRange(7, 3)); + + // Collapse "parent1". + m_model->setExpanded(0, false); + QCOMPARE(itemsInModel(), QStringList() << "parent1" << "parent2" << "realChild2"); + QCOMPARE(itemsRemovedSpy.count(), 1); + arguments = itemsRemovedSpy.takeFirst(); + itemRangeList = arguments.at(0).value(); + QCOMPARE(itemRangeList, KItemRangeList() << KItemRange(1, 1)); + + // Remove "parent2". + m_model->slotItemsDeleted(KFileItemList() << m_model->fileItem(1)); + QCOMPARE(itemsInModel(), QStringList() << "parent1"); + QCOMPARE(itemsRemovedSpy.count(), 1); + arguments = itemsRemovedSpy.takeFirst(); + itemRangeList = arguments.at(0).value(); + QCOMPARE(itemRangeList, KItemRangeList() << KItemRange(1, 2)); + + // Clear filter, verify that no items reappear. + m_model->setNameFilter(QString()); + QCOMPARE(itemsInModel(), QStringList() << "parent1"); +} + +void KFileItemModelTest::testNameRoleGroups() +{ + QStringList files; + files << "b.txt" << "c.txt" << "d.txt" << "e.txt"; + + m_testDir->createFiles(files); + + m_model->setGroupedSorting(true); + m_model->loadDirectory(m_testDir->url()); + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout)); + QCOMPARE(itemsInModel(), QStringList() << "b.txt" << "c.txt" << "d.txt" << "e.txt"); + + QList > expectedGroups; + expectedGroups << QPair(0, QLatin1String("B")); + expectedGroups << QPair(1, QLatin1String("C")); + expectedGroups << QPair(2, QLatin1String("D")); + expectedGroups << QPair(3, QLatin1String("E")); + QCOMPARE(m_model->groups(), expectedGroups); + + // Rename d.txt to a.txt. + QHash data; + data.insert("text", "a.txt"); + m_model->setData(2, data); + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsMoved(KItemRange,QList)), DefaultTimeout)); + QCOMPARE(itemsInModel(), QStringList() << "a.txt" << "b.txt" << "c.txt" << "e.txt"); + + expectedGroups.clear(); + expectedGroups << QPair(0, QLatin1String("A")); + expectedGroups << QPair(1, QLatin1String("B")); + expectedGroups << QPair(2, QLatin1String("C")); + expectedGroups << QPair(3, QLatin1String("E")); + QCOMPARE(m_model->groups(), expectedGroups); + + // Rename c.txt to d.txt. + data.insert("text", "d.txt"); + m_model->setData(2, data); + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(groupsChanged()), DefaultTimeout)); + QCOMPARE(itemsInModel(), QStringList() << "a.txt" << "b.txt" << "d.txt" << "e.txt"); + + expectedGroups.clear(); + expectedGroups << QPair(0, QLatin1String("A")); + expectedGroups << QPair(1, QLatin1String("B")); + expectedGroups << QPair(2, QLatin1String("D")); + expectedGroups << QPair(3, QLatin1String("E")); + QCOMPARE(m_model->groups(), expectedGroups); + + // Change d.txt back to c.txt, but this time using the dir lister's refreshItems() signal. + const KFileItem fileItemD = m_model->fileItem(2); + KFileItem fileItemC = fileItemD; + KUrl urlC = fileItemC.url(); + urlC.setFileName("c.txt"); + fileItemC.setUrl(urlC); + + m_model->slotRefreshItems(QList >() << qMakePair(fileItemD, fileItemC)); + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(groupsChanged()), DefaultTimeout)); + QCOMPARE(itemsInModel(), QStringList() << "a.txt" << "b.txt" << "c.txt" << "e.txt"); + + expectedGroups.clear(); + expectedGroups << QPair(0, QLatin1String("A")); + expectedGroups << QPair(1, QLatin1String("B")); + expectedGroups << QPair(2, QLatin1String("C")); + expectedGroups << QPair(3, QLatin1String("E")); + QCOMPARE(m_model->groups(), expectedGroups); +} + +void KFileItemModelTest::testNameRoleGroupsWithExpandedItems() +{ + QSet modelRoles = m_model->roles(); + modelRoles << "isExpanded" << "isExpandable" << "expandedParentsCount"; + m_model->setRoles(modelRoles); + + QStringList files; + files << "a/b.txt" << "a/c.txt" << "d/e.txt" << "d/f.txt"; + + m_testDir->createFiles(files); + + m_model->setGroupedSorting(true); + m_model->loadDirectory(m_testDir->url()); + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout)); + QCOMPARE(itemsInModel(), QStringList() << "a" << "d"); + + QList > expectedGroups; + expectedGroups << QPair(0, QLatin1String("A")); + expectedGroups << QPair(1, QLatin1String("D")); + QCOMPARE(m_model->groups(), expectedGroups); + + // Verify that expanding "a" and "d" will not change the groups (except for the index of "D"). + expectedGroups.clear(); + expectedGroups << QPair(0, QLatin1String("A")); + expectedGroups << QPair(3, QLatin1String("D")); + + m_model->setExpanded(0, true); + QVERIFY(m_model->isExpanded(0)); + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout)); + QCOMPARE(itemsInModel(), QStringList() << "a" << "b.txt" << "c.txt" << "d"); + QCOMPARE(m_model->groups(), expectedGroups); + + m_model->setExpanded(3, true); + QVERIFY(m_model->isExpanded(3)); + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout)); + QCOMPARE(itemsInModel(), QStringList() << "a" << "b.txt" << "c.txt" << "d" << "e.txt" << "f.txt"); + QCOMPARE(m_model->groups(), expectedGroups); +} + +void KFileItemModelTest::testInconsistentModel() +{ + QSet modelRoles = m_model->roles(); + modelRoles << "isExpanded" << "isExpandable" << "expandedParentsCount"; + m_model->setRoles(modelRoles); + + QStringList files; + files << "a/b/c1.txt" << "a/b/c2.txt"; + + m_testDir->createFiles(files); + + m_model->loadDirectory(m_testDir->url()); + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout)); + QCOMPARE(itemsInModel(), QStringList() << "a"); + + // Expand "a/" and "a/b/". + m_model->setExpanded(0, true); + QVERIFY(m_model->isExpanded(0)); + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout)); + QCOMPARE(itemsInModel(), QStringList() << "a" << "b"); + + m_model->setExpanded(1, true); + QVERIFY(m_model->isExpanded(1)); + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout)); + QCOMPARE(itemsInModel(), QStringList() << "a" << "b" << "c1.txt" << "c2.txt"); + + // Add the files "c1.txt" and "c2.txt" to the model also as top-level items. + // Such a thing can in principle happen when performing a search, and there + // are files which + // (a) match the search string, and + // (b) are children of a folder that matches the search string and is expanded. + // + // Note that the first item in the list of added items must be new (i.e., not + // in the model yet). Otherwise, KFileItemModel::slotItemsAdded() will see that + // it receives items that are in the model already and ignore them. + KUrl url(m_model->directory().url() + "/a2"); + KFileItem newItem(KFileItem::Unknown, KFileItem::Unknown, url); + + KFileItemList items; + items << newItem << m_model->fileItem(2) << m_model->fileItem(3); + m_model->slotItemsAdded(m_model->directory(), items); + m_model->slotCompleted(); + QCOMPARE(itemsInModel(), QStringList() << "a" << "b" << "c1.txt" << "c2.txt" << "a2" << "c1.txt" << "c2.txt"); + + m_model->setExpanded(0, false); + + // Test that the right items have been removed, see + // https://bugs.kde.org/show_bug.cgi?id=324371 + QCOMPARE(itemsInModel(), QStringList() << "a" << "a2" << "c1.txt" << "c2.txt"); + + // Test that resorting does not cause a crash, see + // https://bugs.kde.org/show_bug.cgi?id=325359 + // The crash is not 100% reproducible, but Valgrind will report an invalid memory access. + m_model->resortAllItems(); + +} + +void KFileItemModelTest::testChangeRolesForFilteredItems() +{ + QSet modelRoles = m_model->roles(); + modelRoles << "owner"; + m_model->setRoles(modelRoles); + + QStringList files; + files << "a.txt" << "aa.txt" << "aaa.txt"; + m_testDir->createFiles(files); + + m_model->loadDirectory(m_testDir->url()); + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout)); + QCOMPARE(itemsInModel(), QStringList() << "a.txt" << "aa.txt" << "aaa.txt"); + + for (int index = 0; index < m_model->count(); ++index) { + // All items should have the "text" and "owner" roles, but not "group". + QVERIFY(m_model->data(index).contains("text")); + QVERIFY(m_model->data(index).contains("owner")); + QVERIFY(!m_model->data(index).contains("group")); + } + + // Add a filter, such that only "aaa.txt" remains in the model. + m_model->setNameFilter("aaa"); + QCOMPARE(itemsInModel(), QStringList() << "aaa.txt"); + + // Add the "group" role. + modelRoles << "group"; + m_model->setRoles(modelRoles); + + // Modify the filter, such that "aa.txt" reappears, and verify that all items have the expected roles. + m_model->setNameFilter("aa"); + QCOMPARE(itemsInModel(), QStringList() << "aa.txt" << "aaa.txt"); + + for (int index = 0; index < m_model->count(); ++index) { + // All items should have the "text", "owner", and "group" roles. + QVERIFY(m_model->data(index).contains("text")); + QVERIFY(m_model->data(index).contains("owner")); + QVERIFY(m_model->data(index).contains("group")); + } + + // Remove the "owner" role. + modelRoles.remove("owner"); + m_model->setRoles(modelRoles); + + // Clear the filter, and verify that all items have the expected roles + m_model->setNameFilter(QString()); + QCOMPARE(itemsInModel(), QStringList() << "a.txt" << "aa.txt" << "aaa.txt"); + + for (int index = 0; index < m_model->count(); ++index) { + // All items should have the "text" and "group" roles, but now "owner". + QVERIFY(m_model->data(index).contains("text")); + QVERIFY(!m_model->data(index).contains("owner")); + QVERIFY(m_model->data(index).contains("group")); + } +} + +void KFileItemModelTest::testChangeSortRoleWhileFiltering() +{ + KFileItemList items; + + KIO::UDSEntry entry; + entry.insert(KIO::UDSEntry::UDS_FILE_TYPE, 0100000); // S_IFREG might not be defined on non-Unix platforms. + entry.insert(KIO::UDSEntry::UDS_ACCESS, 07777); + entry.insert(KIO::UDSEntry::UDS_SIZE, 0); + entry.insert(KIO::UDSEntry::UDS_MODIFICATION_TIME, 0); + entry.insert(KIO::UDSEntry::UDS_GROUP, "group"); + entry.insert(KIO::UDSEntry::UDS_ACCESS_TIME, 0); + + entry.insert(KIO::UDSEntry::UDS_NAME, "a.txt"); + entry.insert(KIO::UDSEntry::UDS_USER, "user-b"); + items.append(KFileItem(entry, m_testDir->url(), false, true)); + + entry.insert(KIO::UDSEntry::UDS_NAME, "b.txt"); + entry.insert(KIO::UDSEntry::UDS_USER, "user-c"); + items.append(KFileItem(entry, m_testDir->url(), false, true)); + + entry.insert(KIO::UDSEntry::UDS_NAME, "c.txt"); + entry.insert(KIO::UDSEntry::UDS_USER, "user-a"); + items.append(KFileItem(entry, m_testDir->url(), false, true)); + + m_model->slotItemsAdded(m_testDir->url(), items); + m_model->slotCompleted(); + + QCOMPARE(itemsInModel(), QStringList() << "a.txt" << "b.txt" << "c.txt"); + + // Add a filter. + m_model->setNameFilter("a"); + QCOMPARE(itemsInModel(), QStringList() << "a.txt"); + + // Sort by "owner". + m_model->setSortRole("owner"); + + // Clear the filter, and verify that the items are sorted correctly. + m_model->setNameFilter(QString()); + QCOMPARE(itemsInModel(), QStringList() << "c.txt" << "a.txt" << "b.txt"); +} + +void KFileItemModelTest::testRefreshFilteredItems() +{ + QStringList files; + files << "a.txt" << "b.txt" << "c.jpg" << "d.jpg"; + m_testDir->createFiles(files); + + m_model->loadDirectory(m_testDir->url()); + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout)); + QCOMPARE(itemsInModel(), QStringList() << "a.txt" << "b.txt" << "c.jpg" << "d.jpg"); + + const KFileItem fileItemC = m_model->fileItem(2); + + // Show only the .txt files. + m_model->setNameFilter(".txt"); + QCOMPARE(itemsInModel(), QStringList() << "a.txt" << "b.txt"); + + // Rename one of the .jpg files. + KFileItem fileItemE = fileItemC; + KUrl urlE = fileItemE.url(); + urlE.setFileName("e.jpg"); + fileItemE.setUrl(urlE); + + m_model->slotRefreshItems(QList >() << qMakePair(fileItemC, fileItemE)); + + // Show all files again, and verify that the model has updated the file name. + m_model->setNameFilter(QString()); + QCOMPARE(itemsInModel(), QStringList() << "a.txt" << "b.txt" << "d.jpg" << "e.jpg"); +} + +void KFileItemModelTest::testCreateMimeData() +{ + QSet modelRoles = m_model->roles(); + modelRoles << "isExpanded" << "isExpandable" << "expandedParentsCount"; + m_model->setRoles(modelRoles); + + QStringList files; + files << "a/1"; + m_testDir->createFiles(files); + + m_model->loadDirectory(m_testDir->url()); + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout)); + QCOMPARE(itemsInModel(), QStringList() << "a"); + + // Expand "a/". + m_model->setExpanded(0, true); + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout)); + QCOMPARE(itemsInModel(), QStringList() << "a" << "1"); + + // Verify that creating the MIME data for a child of an expanded folder does + // not cause a crash, see https://bugs.kde.org/show_bug.cgi?id=329119 + KItemSet selection; + selection.insert(1); + QMimeData* mimeData = m_model->createMimeData(selection); + delete mimeData; +} + +void KFileItemModelTest::testCollapseFolderWhileLoading() +{ + QSet modelRoles = m_model->roles(); + modelRoles << "isExpanded" << "isExpandable" << "expandedParentsCount"; + m_model->setRoles(modelRoles); + + QStringList files; + files << "a2/b/c1.txt"; + m_testDir->createFiles(files); + + m_model->loadDirectory(m_testDir->url()); + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout)); + QCOMPARE(itemsInModel(), QStringList() << "a2"); + + // Expand "a2/". + m_model->setExpanded(0, true); + QVERIFY(m_model->isExpanded(0)); + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout)); + QCOMPARE(itemsInModel(), QStringList() << "a2" << "b"); + + // Expand "a2/b/". + m_model->setExpanded(1, true); + QVERIFY(m_model->isExpanded(1)); + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout)); + QCOMPARE(itemsInModel(), QStringList() << "a2" << "b" << "c1.txt"); + + // Simulate that a new item "c2.txt" appears, but that the dir lister's completed() + // signal is not emitted yet. + const KFileItem fileItemC1 = m_model->fileItem(2); + KFileItem fileItemC2 = fileItemC1; + KUrl urlC2 = fileItemC2.url(); + urlC2.setFileName("c2.txt"); + fileItemC2.setUrl(urlC2); + + const KUrl urlB = m_model->fileItem(1).url(); + m_model->slotItemsAdded(urlB, KFileItemList() << fileItemC2); + QCOMPARE(itemsInModel(), QStringList() << "a2" << "b" << "c1.txt"); + + // Collapse "a2/". This should also remove all its (indirect) children from + // the model and from the model's m_pendingItemsToInsert member. + m_model->setExpanded(0, false); + QCOMPARE(itemsInModel(), QStringList() << "a2"); + + // Simulate that the dir lister's completed() signal is emitted. If "c2.txt" + // is still in m_pendingItemsToInsert, then we might get a crash, see + // https://bugs.kde.org/show_bug.cgi?id=332102. Even if the crash is not + // reproducible here, Valgrind will complain, and the item "c2.txt" will appear + // without parent in the model. + m_model->slotCompleted(); + QCOMPARE(itemsInModel(), QStringList() << "a2"); + + // Expand "a2/" again. + m_model->setExpanded(0, true); + QVERIFY(m_model->isExpanded(0)); + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout)); + QCOMPARE(itemsInModel(), QStringList() << "a2" << "b"); + + // Now simulate that a new folder "a1/" is appears, but that the dir lister's + // completed() signal is not emitted yet. + const KFileItem fileItemA2 = m_model->fileItem(0); + KFileItem fileItemA1 = fileItemA2; + KUrl urlA1 = fileItemA1.url(); + urlA1.setFileName("a1"); + fileItemA1.setUrl(urlA1); + + m_model->slotItemsAdded(m_model->directory(), KFileItemList() << fileItemA1); + QCOMPARE(itemsInModel(), QStringList() << "a2" << "b"); + + // Collapse "a2/". Note that this will cause "a1/" to be added to the model, + // i.e., the index of "a2/" will change from 0 to 1. Check that this does not + // confuse the code which collapses the folder. + m_model->setExpanded(0, false); + QCOMPARE(itemsInModel(), QStringList() << "a1" << "a2"); + QVERIFY(!m_model->isExpanded(0)); + QVERIFY(!m_model->isExpanded(1)); +} + +void KFileItemModelTest::testDeleteFileMoreThanOnce() +{ + QStringList files; + files << "a.txt" << "b.txt" << "c.txt" << "d.txt"; + m_testDir->createFiles(files); + + m_model->loadDirectory(m_testDir->url()); + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout)); + QCOMPARE(itemsInModel(), QStringList() << "a.txt" << "b.txt" << "c.txt" << "d.txt"); + + const KFileItem fileItemB = m_model->fileItem(1); + + // Tell the model that a list of items has been deleted, where "b.txt" appears twice in the list. + KFileItemList list; + list << fileItemB << fileItemB; + m_model->slotItemsDeleted(list); + + QVERIFY(m_model->isConsistent()); + QCOMPARE(itemsInModel(), QStringList() << "a.txt" << "c.txt" << "d.txt"); +} + +QStringList KFileItemModelTest::itemsInModel() const +{ + QStringList items; + for (int i = 0; i < m_model->count(); i++) { + items << m_model->fileItem(i).text(); + } + return items; +} + +QTEST_KDEMAIN(KFileItemModelTest, NoGUI) + +#include "kfileitemmodeltest.moc" diff --git a/dolphin/src/tests/kitemlistcontrollertest.cpp b/dolphin/src/tests/kitemlistcontrollertest.cpp new file mode 100644 index 00000000..f55aa116 --- /dev/null +++ b/dolphin/src/tests/kitemlistcontrollertest.cpp @@ -0,0 +1,675 @@ +/*************************************************************************** + * Copyright (C) 2012 by Frank Reininghaus * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include +#include +#include + +#include "kitemviews/kitemlistcontainer.h" +#include "kitemviews/kfileitemlistview.h" +#include "kitemviews/kfileitemmodel.h" +#include "kitemviews/kitemlistcontroller.h" +#include "kitemviews/kitemlistselectionmanager.h" +#include "kitemviews/private/kitemlistviewlayouter.h" +#include "testdir.h" + +#include +#include + +#include + +namespace { + const int DefaultTimeout = 2000; +}; + +Q_DECLARE_METATYPE(KFileItemListView::ItemLayout); +Q_DECLARE_METATYPE(Qt::Orientation); +Q_DECLARE_METATYPE(KItemListController::SelectionBehavior); +Q_DECLARE_METATYPE(KItemSet); + +class KItemListControllerTest : public QObject +{ + Q_OBJECT + +private slots: + void initTestCase(); + void cleanupTestCase(); + + void init(); + void cleanup(); + + void testKeyboardNavigation_data(); + void testKeyboardNavigation(); + void testMouseClickActivation(); + +private: + /** + * Make sure that the number of columns in the view is equal to \a count + * by changing the geometry of the container. + */ + void adjustGeometryForColumnCount(int count); + +private: + KFileItemListView* m_view; + KItemListController* m_controller; + KItemListSelectionManager* m_selectionManager; + KFileItemModel* m_model; + TestDir* m_testDir; + KItemListContainer* m_container; +}; + +/** + * This function initializes the member objects, creates the temporary files, and + * shows the view on the screen on startup. This could also be done before every + * single test, but this would make the time needed to run the test much larger. + */ +void KItemListControllerTest::initTestCase() +{ + qRegisterMetaType("KItemSet"); + + m_testDir = new TestDir(); + m_model = new KFileItemModel(); + m_view = new KFileItemListView(); + m_controller = new KItemListController(m_model, m_view, this); + m_container = new KItemListContainer(m_controller); + m_controller = m_container->controller(); + m_controller->setSelectionBehavior(KItemListController::MultiSelection); + m_selectionManager = m_controller->selectionManager(); + + QStringList files; + files + << "a1" << "a2" << "a3" + << "b1" + << "c1" << "c2" << "c3" << "c4" << "c5" + << "d1" << "d2" << "d3" << "d4" + << "e" << "e 2" << "e 3" << "e 4" << "e 5" << "e 6" << "e 7"; + + m_testDir->createFiles(files); + m_model->loadDirectory(m_testDir->url()); + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(directoryLoadingCompleted()), DefaultTimeout)); + + m_container->show(); + QTest::qWaitForWindowShown(m_container); +} + +void KItemListControllerTest::cleanupTestCase() +{ + delete m_container; + m_container = 0; + + delete m_testDir; + m_testDir = 0; +} + +/** Before each test, the current item, selection, and item size are reset to the defaults. */ +void KItemListControllerTest::init() +{ + m_selectionManager->setCurrentItem(0); + QCOMPARE(m_selectionManager->currentItem(), 0); + + m_selectionManager->clearSelection(); + QVERIFY(!m_selectionManager->hasSelection()); + + const QSizeF itemSize(50, 50); + m_view->setItemSize(itemSize); + QCOMPARE(m_view->itemSize(), itemSize); +} + +void KItemListControllerTest::cleanup() +{ +} + +/** + * \class KeyPress is a small helper struct that represents a key press event, + * including the key and the keyboard modifiers. + */ +struct KeyPress { + + KeyPress(Qt::Key key, Qt::KeyboardModifiers modifier = Qt::NoModifier) : + m_key(key), + m_modifier(modifier) + {} + + Qt::Key m_key; + Qt::KeyboardModifiers m_modifier; +}; + +/** + * \class ViewState is a small helper struct that represents a certain state + * of the view, including the current item, the selected items in MultiSelection + * mode (in the other modes, the selection is either empty or equal to the + * current item), and the information whether items were activated by the last + * key press. + */ +struct ViewState { + + ViewState(int current, const KItemSet selection, bool activated = false) : + m_current(current), + m_selection(selection), + m_activated(activated) + {} + + int m_current; + KItemSet m_selection; + bool m_activated; +}; + +// We have to define a typedef for the pair in order to make the test compile. +typedef QPair keyPressViewStatePair; +Q_DECLARE_METATYPE(QList); + +/** + * This function provides the data for the actual test function + * KItemListControllerTest::testKeyboardNavigation(). + * It tests all possible combinations of view layouts, selection behaviors, + * and enabled/disabled groupings for different column counts, and + * provides a list of key presses and the states that the view should be in + * after the key press event. + */ +void KItemListControllerTest::testKeyboardNavigation_data() +{ + QTest::addColumn("layout"); + QTest::addColumn("scrollOrientation"); + QTest::addColumn("columnCount"); + QTest::addColumn("selectionBehavior"); + QTest::addColumn("groupingEnabled"); + QTest::addColumn > >("testList"); + + QList layoutList; + QHash layoutNames; + layoutList.append(KFileItemListView::IconsLayout); + layoutNames[KFileItemListView::IconsLayout] = "Icons"; + layoutList.append(KFileItemListView::CompactLayout); + layoutNames[KFileItemListView::CompactLayout] = "Compact"; + layoutList.append(KFileItemListView::DetailsLayout); + layoutNames[KFileItemListView::DetailsLayout] = "Details"; + + QList selectionBehaviorList; + QHash selectionBehaviorNames; + selectionBehaviorList.append(KItemListController::NoSelection); + selectionBehaviorNames[KItemListController::NoSelection] = "NoSelection"; + selectionBehaviorList.append(KItemListController::SingleSelection); + selectionBehaviorNames[KItemListController::SingleSelection] = "SingleSelection"; + selectionBehaviorList.append(KItemListController::MultiSelection); + selectionBehaviorNames[KItemListController::MultiSelection] = "MultiSelection"; + + QList groupingEnabledList; + QHash groupingEnabledNames; + groupingEnabledList.append(false); + groupingEnabledNames[false] = "ungrouped"; + groupingEnabledList.append(true); + groupingEnabledNames[true] = "grouping enabled"; + + foreach (const KFileItemListView::ItemLayout& layout, layoutList) { + // The following settings depend on the layout. + // Note that 'columns' are actually 'rows' in + // Compact layout. + Qt::Orientation scrollOrientation; + QList columnCountList; + Qt::Key nextItemKey; + Qt::Key previousItemKey; + Qt::Key nextRowKey; + Qt::Key previousRowKey; + + switch (layout) { + case KFileItemListView::IconsLayout: + scrollOrientation = Qt::Vertical; + columnCountList << 1 << 3 << 5; + nextItemKey = Qt::Key_Right; + previousItemKey = Qt::Key_Left; + nextRowKey = Qt::Key_Down; + previousRowKey = Qt::Key_Up; + break; + case KFileItemListView::CompactLayout: + scrollOrientation = Qt::Horizontal; + columnCountList << 1 << 3 << 5; + nextItemKey = Qt::Key_Down; + previousItemKey = Qt::Key_Up; + nextRowKey = Qt::Key_Right; + previousRowKey = Qt::Key_Left; + break; + case KFileItemListView::DetailsLayout: + scrollOrientation = Qt::Vertical; + columnCountList << 1; + nextItemKey = Qt::Key_Down; + previousItemKey = Qt::Key_Up; + nextRowKey = Qt::Key_Down; + previousRowKey = Qt::Key_Up; + break; + } + + foreach (int columnCount, columnCountList) { + foreach (const KItemListController::SelectionBehavior& selectionBehavior, selectionBehaviorList) { + foreach (bool groupingEnabled, groupingEnabledList) { // krazy:exclude=foreach + QList > testList; + + // First, key presses which should have the same effect + // for any layout and any number of columns. + testList + << qMakePair(KeyPress(nextItemKey), ViewState(1, KItemSet() << 1)) + << qMakePair(KeyPress(Qt::Key_Return), ViewState(1, KItemSet() << 1, true)) + << qMakePair(KeyPress(Qt::Key_Enter), ViewState(1, KItemSet() << 1, true)) + << qMakePair(KeyPress(nextItemKey), ViewState(2, KItemSet() << 2)) + << qMakePair(KeyPress(nextItemKey, Qt::ShiftModifier), ViewState(3, KItemSet() << 2 << 3)) + << qMakePair(KeyPress(Qt::Key_Return), ViewState(3, KItemSet() << 2 << 3, true)) + << qMakePair(KeyPress(previousItemKey, Qt::ShiftModifier), ViewState(2, KItemSet() << 2)) + << qMakePair(KeyPress(nextItemKey, Qt::ShiftModifier), ViewState(3, KItemSet() << 2 << 3)) + << qMakePair(KeyPress(nextItemKey, Qt::ControlModifier), ViewState(4, KItemSet() << 2 << 3)) + << qMakePair(KeyPress(Qt::Key_Return), ViewState(4, KItemSet() << 2 << 3, true)) + << qMakePair(KeyPress(previousItemKey), ViewState(3, KItemSet() << 3)) + << qMakePair(KeyPress(Qt::Key_Home, Qt::ShiftModifier), ViewState(0, KItemSet() << 0 << 1 << 2 << 3)) + << qMakePair(KeyPress(nextItemKey, Qt::ControlModifier), ViewState(1, KItemSet() << 0 << 1 << 2 << 3)) + << qMakePair(KeyPress(Qt::Key_Space, Qt::ControlModifier), ViewState(1, KItemSet() << 0 << 2 << 3)) + << qMakePair(KeyPress(Qt::Key_Space, Qt::ControlModifier), ViewState(1, KItemSet() << 0 << 1 << 2 << 3)) + << qMakePair(KeyPress(Qt::Key_End), ViewState(19, KItemSet() << 19)) + << qMakePair(KeyPress(previousItemKey, Qt::ShiftModifier), ViewState(18, KItemSet() << 18 << 19)) + << qMakePair(KeyPress(Qt::Key_Home), ViewState(0, KItemSet() << 0)) + << qMakePair(KeyPress(Qt::Key_Space, Qt::ControlModifier), ViewState(0, KItemSet())) + << qMakePair(KeyPress(Qt::Key_Enter), ViewState(0, KItemSet(), true)) + << qMakePair(KeyPress(Qt::Key_Space, Qt::ControlModifier), ViewState(0, KItemSet() << 0)) + << qMakePair(KeyPress(Qt::Key_Space, Qt::ControlModifier), ViewState(0, KItemSet())) + << qMakePair(KeyPress(Qt::Key_Space), ViewState(0, KItemSet() << 0)) + << qMakePair(KeyPress(Qt::Key_E), ViewState(13, KItemSet() << 13)) + << qMakePair(KeyPress(Qt::Key_Space), ViewState(14, KItemSet() << 14)) + << qMakePair(KeyPress(Qt::Key_3), ViewState(15, KItemSet() << 15)) + << qMakePair(KeyPress(Qt::Key_Home), ViewState(0, KItemSet() << 0)) + << qMakePair(KeyPress(Qt::Key_Escape), ViewState(0, KItemSet())); + + // Next, we test combinations of key presses which only work for a + // particular number of columns and either enabled or disabled grouping. + + // One column. + if (columnCount == 1) { + testList + << qMakePair(KeyPress(nextRowKey), ViewState(1, KItemSet() << 1)) + << qMakePair(KeyPress(nextRowKey, Qt::ShiftModifier), ViewState(2, KItemSet() << 1 << 2)) + << qMakePair(KeyPress(nextRowKey, Qt::ControlModifier), ViewState(3, KItemSet() << 1 << 2)) + << qMakePair(KeyPress(previousRowKey), ViewState(2, KItemSet() << 2)) + << qMakePair(KeyPress(previousItemKey), ViewState(1, KItemSet() << 1)) + << qMakePair(KeyPress(Qt::Key_Home), ViewState(0, KItemSet() << 0)); + } + + // Multiple columns: we test both 3 and 5 columns with grouping + // enabled or disabled. For each case, the layout of the items + // in the view is shown (both using file names and indices) to + // make it easier to understand what the tests do. + + if (columnCount == 3 && !groupingEnabled) { + // 3 columns, no grouping: + // + // a1 a2 a3 | 0 1 2 + // b1 c1 c2 | 3 4 5 + // c3 c4 c5 | 6 7 8 + // d1 d2 d3 | 9 10 11 + // d4 e1 e2 | 12 13 14 + // e3 e4 e5 | 15 16 17 + // e6 e7 | 18 19 + testList + << qMakePair(KeyPress(nextRowKey), ViewState(3, KItemSet() << 3)) + << qMakePair(KeyPress(nextItemKey, Qt::ControlModifier), ViewState(4, KItemSet() << 3)) + << qMakePair(KeyPress(nextRowKey), ViewState(7, KItemSet() << 7)) + << qMakePair(KeyPress(nextItemKey, Qt::ShiftModifier), ViewState(8, KItemSet() << 7 << 8)) + << qMakePair(KeyPress(nextItemKey, Qt::ShiftModifier), ViewState(9, KItemSet() << 7 << 8 << 9)) + << qMakePair(KeyPress(previousItemKey, Qt::ShiftModifier), ViewState(8, KItemSet() << 7 << 8)) + << qMakePair(KeyPress(previousItemKey, Qt::ShiftModifier), ViewState(7, KItemSet() << 7)) + << qMakePair(KeyPress(previousItemKey, Qt::ShiftModifier), ViewState(6, KItemSet() << 6 << 7)) + << qMakePair(KeyPress(previousItemKey, Qt::ShiftModifier), ViewState(5, KItemSet() << 5 << 6 << 7)) + << qMakePair(KeyPress(nextItemKey, Qt::ShiftModifier), ViewState(6, KItemSet() << 6 << 7)) + << qMakePair(KeyPress(nextItemKey, Qt::ShiftModifier), ViewState(7, KItemSet() << 7)) + << qMakePair(KeyPress(nextRowKey), ViewState(10, KItemSet() << 10)) + << qMakePair(KeyPress(nextItemKey), ViewState(11, KItemSet() << 11)) + << qMakePair(KeyPress(nextRowKey), ViewState(14, KItemSet() << 14)) + << qMakePair(KeyPress(nextRowKey), ViewState(17, KItemSet() << 17)) + << qMakePair(KeyPress(nextRowKey), ViewState(19, KItemSet() << 19)) + << qMakePair(KeyPress(previousRowKey), ViewState(17, KItemSet() << 17)) + << qMakePair(KeyPress(Qt::Key_End), ViewState(19, KItemSet() << 19)) + << qMakePair(KeyPress(previousRowKey), ViewState(16, KItemSet() << 16)) + << qMakePair(KeyPress(Qt::Key_Home), ViewState(0, KItemSet() << 0)); + } + + if (columnCount == 5 && !groupingEnabled) { + // 5 columns, no grouping: + // + // a1 a2 a3 b1 c1 | 0 1 2 3 4 + // c2 c3 c4 c5 d1 | 5 6 7 8 9 + // d2 d3 d4 e1 e2 | 10 11 12 13 14 + // e3 e4 e5 e6 e7 | 15 16 17 18 19 + testList + << qMakePair(KeyPress(nextRowKey), ViewState(5, KItemSet() << 5)) + << qMakePair(KeyPress(nextItemKey, Qt::ControlModifier), ViewState(6, KItemSet() << 5)) + << qMakePair(KeyPress(nextRowKey), ViewState(11, KItemSet() << 11)) + << qMakePair(KeyPress(nextItemKey), ViewState(12, KItemSet() << 12)) + << qMakePair(KeyPress(nextRowKey, Qt::ShiftModifier), ViewState(17, KItemSet() << 12 << 13 << 14 << 15 << 16 << 17)) + << qMakePair(KeyPress(previousRowKey, Qt::ShiftModifier), ViewState(12, KItemSet() << 12)) + << qMakePair(KeyPress(previousRowKey, Qt::ShiftModifier), ViewState(7, KItemSet() << 7 << 8 << 9 << 10 << 11 << 12)) + << qMakePair(KeyPress(nextRowKey, Qt::ShiftModifier), ViewState(12, KItemSet() << 12)) + << qMakePair(KeyPress(Qt::Key_End, Qt::ControlModifier), ViewState(19, KItemSet() << 12)) + << qMakePair(KeyPress(previousRowKey), ViewState(14, KItemSet() << 14)) + << qMakePair(KeyPress(Qt::Key_Home), ViewState(0, KItemSet() << 0)); + } + + if (columnCount == 3 && groupingEnabled) { + // 3 columns, with grouping: + // + // a1 a2 a3 | 0 1 2 + // b1 | 3 + // c1 c2 c3 | 4 5 6 + // c4 c5 | 7 8 + // d1 d2 d3 | 9 10 11 + // d4 | 12 + // e1 e2 e3 | 13 14 15 + // e4 e5 e6 | 16 17 18 + // e7 | 19 + testList + << qMakePair(KeyPress(nextItemKey), ViewState(1, KItemSet() << 1)) + << qMakePair(KeyPress(nextItemKey), ViewState(2, KItemSet() << 2)) + << qMakePair(KeyPress(nextRowKey, Qt::ShiftModifier), ViewState(3, KItemSet() << 2 << 3)) + << qMakePair(KeyPress(nextRowKey, Qt::ShiftModifier), ViewState(6, KItemSet() << 2 << 3 << 4 << 5 << 6)) + << qMakePair(KeyPress(nextRowKey), ViewState(8, KItemSet() << 8)) + << qMakePair(KeyPress(nextRowKey), ViewState(11, KItemSet() << 11)) + << qMakePair(KeyPress(nextItemKey, Qt::ControlModifier), ViewState(12, KItemSet() << 11)) + << qMakePair(KeyPress(nextRowKey), ViewState(13, KItemSet() << 13)) + << qMakePair(KeyPress(nextRowKey), ViewState(16, KItemSet() << 16)) + << qMakePair(KeyPress(nextItemKey), ViewState(17, KItemSet() << 17)) + << qMakePair(KeyPress(nextRowKey), ViewState(19, KItemSet() << 19)) + << qMakePair(KeyPress(previousRowKey), ViewState(17, KItemSet() << 17)) + << qMakePair(KeyPress(Qt::Key_Home), ViewState(0, KItemSet() << 0)); + } + + if (columnCount == 5 && groupingEnabled) { + // 5 columns, with grouping: + // + // a1 a2 a3 | 0 1 2 + // b1 | 3 + // c1 c2 c3 c4 c5 | 4 5 6 7 8 + // d1 d2 d3 d4 | 9 10 11 12 + // e1 e2 e3 e4 e5 | 13 14 15 16 17 + // e6 e7 | 18 19 + testList + << qMakePair(KeyPress(nextItemKey), ViewState(1, KItemSet() << 1)) + << qMakePair(KeyPress(nextRowKey, Qt::ShiftModifier), ViewState(3, KItemSet() << 1 << 2 << 3)) + << qMakePair(KeyPress(nextRowKey, Qt::ShiftModifier), ViewState(5, KItemSet() << 1 << 2 << 3 << 4 << 5)) + << qMakePair(KeyPress(nextItemKey), ViewState(6, KItemSet() << 6)) + << qMakePair(KeyPress(nextItemKey, Qt::ControlModifier), ViewState(7, KItemSet() << 6)) + << qMakePair(KeyPress(nextItemKey, Qt::ControlModifier), ViewState(8, KItemSet() << 6)) + << qMakePair(KeyPress(nextRowKey), ViewState(12, KItemSet() << 12)) + << qMakePair(KeyPress(nextRowKey), ViewState(17, KItemSet() << 17)) + << qMakePair(KeyPress(nextRowKey), ViewState(19, KItemSet() << 19)) + << qMakePair(KeyPress(previousRowKey), ViewState(17, KItemSet() << 17)) + << qMakePair(KeyPress(Qt::Key_End, Qt::ShiftModifier), ViewState(19, KItemSet() << 17 << 18 << 19)) + << qMakePair(KeyPress(previousRowKey, Qt::ShiftModifier), ViewState(14, KItemSet() << 14 << 15 << 16 << 17)) + << qMakePair(KeyPress(Qt::Key_Home), ViewState(0, KItemSet() << 0)); + } + + const QString testName = + layoutNames[layout] + ", " + + QString("%1 columns, ").arg(columnCount) + + selectionBehaviorNames[selectionBehavior] + ", " + + groupingEnabledNames[groupingEnabled]; + + const QByteArray testNameAscii = testName.toAscii(); + + QTest::newRow(testNameAscii.data()) + << layout + << scrollOrientation + << columnCount + << selectionBehavior + << groupingEnabled + << testList; + } + } + } + } +} + +/** + * This function sets the view's properties according to the data provided by + * KItemListControllerTest::testKeyboardNavigation_data(). + * + * The list \a testList contains pairs of key presses, which are sent to the + * container, and expected view states, which are verified then. + */ +void KItemListControllerTest::testKeyboardNavigation() +{ + QFETCH(KFileItemListView::ItemLayout, layout); + QFETCH(Qt::Orientation, scrollOrientation); + QFETCH(int, columnCount); + QFETCH(KItemListController::SelectionBehavior, selectionBehavior); + QFETCH(bool, groupingEnabled); + QFETCH(QList, testList); + + m_view->setItemLayout(layout); + QCOMPARE(m_view->itemLayout(), layout); + + m_view->setScrollOrientation(scrollOrientation); + QCOMPARE(m_view->scrollOrientation(), scrollOrientation); + + m_controller->setSelectionBehavior(selectionBehavior); + QCOMPARE(m_controller->selectionBehavior(), selectionBehavior); + + m_model->setGroupedSorting(groupingEnabled); + QCOMPARE(m_model->groupedSorting(), groupingEnabled); + + adjustGeometryForColumnCount(columnCount); + QCOMPARE(m_view->m_layouter->m_columnCount, columnCount); + + QSignalSpy spySingleItemActivated(m_controller, SIGNAL(itemActivated(int))); + QSignalSpy spyMultipleItemsActivated(m_controller, SIGNAL(itemsActivated(KItemSet))); + + while (!testList.isEmpty()) { + const QPair test = testList.takeFirst(); + const Qt::Key key = test.first.m_key; + const Qt::KeyboardModifiers modifier = test.first.m_modifier; + const int current = test.second.m_current; + const KItemSet selection = test.second.m_selection; + const bool activated = test.second.m_activated; + + QTest::keyClick(m_container, key, modifier); + + QCOMPARE(m_selectionManager->currentItem(), current); + switch (selectionBehavior) { + case KItemListController::NoSelection: QVERIFY(m_selectionManager->selectedItems().isEmpty()); break; + case KItemListController::SingleSelection: QCOMPARE(m_selectionManager->selectedItems(), KItemSet() << current); break; + case KItemListController::MultiSelection: QCOMPARE(m_selectionManager->selectedItems(), selection); break; + } + + if (activated) { + switch (selectionBehavior) { + case KItemListController::MultiSelection: + if (!selection.isEmpty()) { + // The selected items should be activated. + if (selection.count() == 1) { + QVERIFY(!spySingleItemActivated.isEmpty()); + QCOMPARE(qvariant_cast(spySingleItemActivated.takeFirst().at(0)), selection.first()); + QVERIFY(spyMultipleItemsActivated.isEmpty()); + } else { + QVERIFY(spySingleItemActivated.isEmpty()); + QVERIFY(!spyMultipleItemsActivated.isEmpty()); + QCOMPARE(qvariant_cast(spyMultipleItemsActivated.takeFirst().at(0)), selection); + } + break; + } + // No items are selected. Therefore, the current item should be activated. + // This is handled by falling through to the NoSelection/SingleSelection case. + case KItemListController::NoSelection: + case KItemListController::SingleSelection: + // In NoSelection and SingleSelection mode, the current item should be activated. + QVERIFY(!spySingleItemActivated.isEmpty()); + QCOMPARE(qvariant_cast(spySingleItemActivated.takeFirst().at(0)), current); + QVERIFY(spyMultipleItemsActivated.isEmpty()); + break; + } + } + } +} + +void KItemListControllerTest::testMouseClickActivation() +{ + m_view->setItemLayout(KFileItemListView::IconsLayout); + + // Make sure that we have a large window, such that + // the items are visible and clickable. + adjustGeometryForColumnCount(5); + + // Make sure that the first item is visible in the view. + m_view->setScrollOffset(0); + QCOMPARE(m_view->firstVisibleIndex(), 0); + + const QPointF pos = m_view->itemContextRect(0).center(); + + // Save the "single click" setting. + const bool restoreKGlobalSettingsSingleClick = KGlobalSettings::singleClick(); + + KConfig config("kcminputrc"); + KConfigGroup group = config.group("KDE"); + + QGraphicsSceneMouseEvent mousePressEvent(QEvent::GraphicsSceneMousePress); + mousePressEvent.setPos(pos); + mousePressEvent.setButton(Qt::LeftButton); + mousePressEvent.setButtons(Qt::LeftButton); + + QGraphicsSceneMouseEvent mouseReleaseEvent(QEvent::GraphicsSceneMouseRelease); + mouseReleaseEvent.setPos(pos); + mouseReleaseEvent.setButton(Qt::LeftButton); + mouseReleaseEvent.setButtons(Qt::NoButton); + + QSignalSpy spyItemActivated(m_controller, SIGNAL(itemActivated(int))); + + // Default setting: single click activation. + group.writeEntry("SingleClick", true, KConfig::Persistent|KConfig::Global); + config.sync(); + KGlobalSettings::self()->emitChange(KGlobalSettings::SettingsChanged, KGlobalSettings::SETTINGS_MOUSE); + + int iterations = 0; + const int maxIterations = 20; + while (!KGlobalSettings::singleClick() && iterations < maxIterations) { + QTest::qWait(50); + ++iterations; + } + + if (!KGlobalSettings::singleClick()) { + // TODO: Try to find a way to make sure that changing the global setting works. + QSKIP("Failed to change the KGlobalSettings::singleClick() setting!", SkipSingle); + } + + m_view->event(&mousePressEvent); + m_view->event(&mouseReleaseEvent); + QCOMPARE(spyItemActivated.count(), 1); + spyItemActivated.clear(); + + // Set the global setting to "double click activation". + group.writeEntry("SingleClick", false, KConfig::Persistent|KConfig::Global); + config.sync(); + KGlobalSettings::self()->emitChange(KGlobalSettings::SettingsChanged, KGlobalSettings::SETTINGS_MOUSE); + + iterations = 0; + while (KGlobalSettings::singleClick() && iterations < maxIterations) { + QTest::qWait(50); + ++iterations; + } + + if (KGlobalSettings::singleClick()) { + // TODO: Try to find a way to make sure that changing the global setting works. + QSKIP("Failed to change the KGlobalSettings::singleClick() setting!", SkipSingle); + } + + m_view->event(&mousePressEvent); + m_view->event(&mouseReleaseEvent); + QCOMPARE(spyItemActivated.count(), 0); + spyItemActivated.clear(); + + // Enforce single click activation in the controller. + m_controller->setSingleClickActivationEnforced(true); + m_view->event(&mousePressEvent); + m_view->event(&mouseReleaseEvent); + QCOMPARE(spyItemActivated.count(), 1); + spyItemActivated.clear(); + + // Do not enforce single click activation in the controller. + m_controller->setSingleClickActivationEnforced(false); + m_view->event(&mousePressEvent); + m_view->event(&mouseReleaseEvent); + QCOMPARE(spyItemActivated.count(), 0); + spyItemActivated.clear(); + + // Set the global setting back to "single click activation". + group.writeEntry("SingleClick", true, KConfig::Persistent|KConfig::Global); + config.sync(); + KGlobalSettings::self()->emitChange(KGlobalSettings::SettingsChanged, KGlobalSettings::SETTINGS_MOUSE); + + iterations = 0; + while (!KGlobalSettings::singleClick() && iterations < maxIterations) { + QTest::qWait(50); + ++iterations; + } + + if (!KGlobalSettings::singleClick()) { + // TODO: Try to find a way to make sure that changing the global setting works. + QSKIP("Failed to change the KGlobalSettings::singleClick() setting!", SkipSingle); + } + + m_view->event(&mousePressEvent); + m_view->event(&mouseReleaseEvent); + QCOMPARE(spyItemActivated.count(), 1); + spyItemActivated.clear(); + + // Enforce single click activation in the controller. + m_controller->setSingleClickActivationEnforced(true); + m_view->event(&mousePressEvent); + m_view->event(&mouseReleaseEvent); + QCOMPARE(spyItemActivated.count(), 1); + spyItemActivated.clear(); + + // Restore previous settings. + m_controller->setSingleClickActivationEnforced(true); + group.writeEntry("SingleClick", restoreKGlobalSettingsSingleClick, KConfig::Persistent|KConfig::Global); + config.sync(); + KGlobalSettings::self()->emitChange(KGlobalSettings::SettingsChanged, KGlobalSettings::SETTINGS_MOUSE); + + iterations = 0; + while (KGlobalSettings::singleClick() != restoreKGlobalSettingsSingleClick && iterations < maxIterations) { + QTest::qWait(50); + ++iterations; + } + + if (KGlobalSettings::singleClick() != restoreKGlobalSettingsSingleClick) { + // TODO: Try to find a way to make sure that changing the global setting works. + QSKIP("Failed to change the KGlobalSettings::singleClick() setting!", SkipSingle); + } +} + +void KItemListControllerTest::adjustGeometryForColumnCount(int count) +{ + const QSize size = m_view->itemSize().toSize(); + + QRect rect = m_container->geometry(); + rect.setSize(size * count); + m_container->setGeometry(rect); + + // Increase the size of the container until the correct column count is reached. + while (m_view->m_layouter->m_columnCount < count) { + rect = m_container->geometry(); + rect.setSize(rect.size() + size); + m_container->setGeometry(rect); + } +} + +QTEST_KDEMAIN(KItemListControllerTest, GUI) + +#include "kitemlistcontrollertest.moc" diff --git a/dolphin/src/tests/kitemlistkeyboardsearchmanagertest.cpp b/dolphin/src/tests/kitemlistkeyboardsearchmanagertest.cpp new file mode 100644 index 00000000..7d5fc3b9 --- /dev/null +++ b/dolphin/src/tests/kitemlistkeyboardsearchmanagertest.cpp @@ -0,0 +1,152 @@ +/*************************************************************************** + * Copyright (C) 2011 by Frank Reininghaus * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include + +#include "kitemviews/private/kitemlistkeyboardsearchmanager.h" + +class KItemListKeyboardSearchManagerTest : public QObject +{ + Q_OBJECT + +private slots: + void init(); + + void testBasicKeyboardSearch(); + void testAbortedKeyboardSearch(); + void testRepeatedKeyPress(); + void testPressShift(); + +private: + KItemListKeyboardSearchManager m_keyboardSearchManager; +}; + +void KItemListKeyboardSearchManagerTest::init() +{ + // Make sure that the previous search string is cleared + m_keyboardSearchManager.cancelSearch(); +} + +void KItemListKeyboardSearchManagerTest::testBasicKeyboardSearch() +{ + QSignalSpy spy(&m_keyboardSearchManager, SIGNAL(changeCurrentItem(QString,bool))); + + m_keyboardSearchManager.addKeys("f"); + QCOMPARE(spy.count(), 1); + QCOMPARE(spy.takeFirst(), QList() << "f" << true); + + m_keyboardSearchManager.addKeys("i"); + QCOMPARE(spy.count(), 1); + QCOMPARE(spy.takeFirst(), QList() << "fi" << false); + + m_keyboardSearchManager.addKeys("l"); + QCOMPARE(spy.count(), 1); + QCOMPARE(spy.takeFirst(), QList() << "fil" << false); + + m_keyboardSearchManager.addKeys("e"); + QCOMPARE(spy.count(), 1); + QCOMPARE(spy.takeFirst(), QList() << "file" << false); +} + +void KItemListKeyboardSearchManagerTest::testAbortedKeyboardSearch() +{ + // Set the timeout to a small value (the default is 5000 milliseconds) + // to save time when running this test. + m_keyboardSearchManager.setTimeout(100); + + QSignalSpy spy(&m_keyboardSearchManager, SIGNAL(changeCurrentItem(QString,bool))); + + m_keyboardSearchManager.addKeys("f"); + QCOMPARE(spy.count(), 1); + QCOMPARE(spy.takeFirst(), QList() << "f" << true); + + m_keyboardSearchManager.addKeys("i"); + QCOMPARE(spy.count(), 1); + QCOMPARE(spy.takeFirst(), QList() << "fi" << false); + + // If the delay between two key presses is larger than the chosen timeout, + // a new search is started. We add a small safety margin to avoid race conditions. + QTest::qWait(m_keyboardSearchManager.timeout() + 10); + + m_keyboardSearchManager.addKeys("l"); + QCOMPARE(spy.count(), 1); + QCOMPARE(spy.takeFirst(), QList() << "l" << true); + + m_keyboardSearchManager.addKeys("e"); + QCOMPARE(spy.count(), 1); + QCOMPARE(spy.takeFirst(), QList() << "le" << false); +} + +void KItemListKeyboardSearchManagerTest::testRepeatedKeyPress() +{ + // If the same key is pressed repeatedly, the next matching item should be highlighted after + // each key press. To achieve, that, the manager emits the changeCurrentItem(QString,bool) + // signal, where + // 1. the string contains the repeated key only once, and + // 2. the bool searchFromNextItem is true. + + QSignalSpy spy(&m_keyboardSearchManager, SIGNAL(changeCurrentItem(QString,bool))); + + m_keyboardSearchManager.addKeys("p"); + QCOMPARE(spy.count(), 1); + QCOMPARE(spy.takeFirst(), QList() << "p" << true); + + m_keyboardSearchManager.addKeys("p"); + QCOMPARE(spy.count(), 1); + QCOMPARE(spy.takeFirst(), QList() << "p" << true); + + m_keyboardSearchManager.addKeys("p"); + QCOMPARE(spy.count(), 1); + QCOMPARE(spy.takeFirst(), QList() << "p" << true); + + // Now press another key -> the search string contains all pressed keys + m_keyboardSearchManager.addKeys("q"); + QCOMPARE(spy.count(), 1); + QCOMPARE(spy.takeFirst(), QList() << "pppq" << false); +} + +void KItemListKeyboardSearchManagerTest::testPressShift() +{ + // If the user presses Shift, i.e., to get a character like '_', + // KItemListController calls the addKeys(QString) method with an empty + // string. Make sure that this does not reset the current search. See + // https://bugs.kde.org/show_bug.cgi?id=321286 + + QSignalSpy spy(&m_keyboardSearchManager, SIGNAL(changeCurrentItem(QString,bool))); + + // Simulate that the user enters "a_b". + m_keyboardSearchManager.addKeys("a"); + QCOMPARE(spy.count(), 1); + QCOMPARE(spy.takeFirst(), QList() << "a" << true); + + m_keyboardSearchManager.addKeys(""); + QCOMPARE(spy.count(), 0); + + m_keyboardSearchManager.addKeys("_"); + QCOMPARE(spy.count(), 1); + QCOMPARE(spy.takeFirst(), QList() << "a_" << false); + + m_keyboardSearchManager.addKeys("b"); + QCOMPARE(spy.count(), 1); + QCOMPARE(spy.takeFirst(), QList() << "a_b" << false); +} + +QTEST_KDEMAIN(KItemListKeyboardSearchManagerTest, NoGUI) + +#include "kitemlistkeyboardsearchmanagertest.moc" diff --git a/dolphin/src/tests/kitemlistselectionmanagertest.cpp b/dolphin/src/tests/kitemlistselectionmanagertest.cpp new file mode 100644 index 00000000..492d0234 --- /dev/null +++ b/dolphin/src/tests/kitemlistselectionmanagertest.cpp @@ -0,0 +1,576 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz * + * Copyright (C) 2011 by Frank Reininghaus * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include + +#include "kitemviews/kitemmodelbase.h" +#include "kitemviews/kitemlistselectionmanager.h" + +class DummyModel : public KItemModelBase +{ +public: + DummyModel(); + void setCount(int count); + virtual int count() const; + virtual QHash data(int index) const; + +private: + int m_count; +}; + +DummyModel::DummyModel() : + KItemModelBase(), + m_count(100) +{ +} + +void DummyModel::setCount(int count) +{ + m_count = count; +} + +int DummyModel::count() const +{ + return m_count; +} + +QHash DummyModel::data(int index) const +{ + Q_UNUSED(index); + return QHash(); +} + + +class KItemListSelectionManagerTest : public QObject +{ + Q_OBJECT + +private slots: + void init(); + void cleanup(); + + void testConstructor(); + + void testCurrentItemAnchorItem(); + void testSetSelected_data(); + void testSetSelected(); + void testItemsInserted(); + void testItemsRemoved(); + void testAnchoredSelection(); + void testChangeSelection_data(); + void testChangeSelection(); + void testDeleteCurrentItem_data(); + void testDeleteCurrentItem(); + void testAnchoredSelectionAfterMovingItems(); + +private: + void verifySelectionChange(QSignalSpy& spy, const KItemSet& currentSelection, const KItemSet& previousSelection) const; + + KItemListSelectionManager* m_selectionManager; + DummyModel* m_model; +}; + +void KItemListSelectionManagerTest::init() +{ + m_model = new DummyModel(); + m_selectionManager = new KItemListSelectionManager(); + m_selectionManager->setModel(m_model); +} + +void KItemListSelectionManagerTest::cleanup() +{ + delete m_selectionManager; + m_selectionManager = 0; + + delete m_model; + m_model = 0; +} + +void KItemListSelectionManagerTest::testConstructor() +{ + QVERIFY(!m_selectionManager->hasSelection()); + QCOMPARE(m_selectionManager->selectedItems().count(), 0); + QCOMPARE(m_selectionManager->currentItem(), 0); + QCOMPARE(m_selectionManager->m_anchorItem, -1); +} + +void KItemListSelectionManagerTest::testCurrentItemAnchorItem() +{ + QSignalSpy spyCurrent(m_selectionManager, SIGNAL(currentChanged(int,int))); + + // Set current item and check that the selection manager emits the currentChanged(int,int) signal correctly. + m_selectionManager->setCurrentItem(4); + QCOMPARE(m_selectionManager->currentItem(), 4); + QCOMPARE(spyCurrent.count(), 1); + QCOMPARE(qvariant_cast(spyCurrent.at(0).at(0)), 4); + spyCurrent.takeFirst(); + + // Begin an anchored selection. + m_selectionManager->beginAnchoredSelection(5); + QVERIFY(m_selectionManager->isAnchoredSelectionActive()); + QCOMPARE(m_selectionManager->m_anchorItem, 5); + + // Items between current and anchor should be selected now + QCOMPARE(m_selectionManager->selectedItems(), KItemSet() << 4 << 5); + QVERIFY(m_selectionManager->hasSelection()); + + // Change current item again and check the selection + m_selectionManager->setCurrentItem(2); + QCOMPARE(m_selectionManager->currentItem(), 2); + QCOMPARE(spyCurrent.count(), 1); + QCOMPARE(qvariant_cast(spyCurrent.at(0).at(0)), 2); + QCOMPARE(qvariant_cast(spyCurrent.at(0).at(1)), 4); + spyCurrent.takeFirst(); + + QCOMPARE(m_selectionManager->selectedItems(), KItemSet() << 2 << 3 << 4 << 5); + QVERIFY(m_selectionManager->hasSelection()); + + // Inserting items should update current item and anchor item. + m_selectionManager->itemsInserted(KItemRangeList() << + KItemRange(0, 1) << + KItemRange(2, 2) << + KItemRange(6, 3)); + + QCOMPARE(m_selectionManager->currentItem(), 5); + QCOMPARE(spyCurrent.count(), 1); + QCOMPARE(qvariant_cast(spyCurrent.at(0).at(0)), 5); + QCOMPARE(qvariant_cast(spyCurrent.at(0).at(1)), 2); + spyCurrent.takeFirst(); + + QCOMPARE(m_selectionManager->m_anchorItem, 8); + + QCOMPARE(m_selectionManager->selectedItems(), KItemSet() << 5 << 6 << 7 << 8); + QVERIFY(m_selectionManager->hasSelection()); + + // Removing items should update current item and anchor item. + m_selectionManager->itemsRemoved(KItemRangeList() << + KItemRange(0, 2) << + KItemRange(2, 1) << + KItemRange(9, 2)); + + QCOMPARE(m_selectionManager->currentItem(), 2); + QCOMPARE(spyCurrent.count(), 1); + QCOMPARE(qvariant_cast(spyCurrent.at(0).at(0)), 2); + QCOMPARE(qvariant_cast(spyCurrent.at(0).at(1)), 5); + spyCurrent.takeFirst(); + + QCOMPARE(m_selectionManager->m_anchorItem, 5); + + QCOMPARE(m_selectionManager->selectedItems(), KItemSet() << 2 << 3 << 4 << 5); + QVERIFY(m_selectionManager->hasSelection()); + + // Verify that clearSelection() also clears the anchored selection. + m_selectionManager->clearSelection(); + QCOMPARE(m_selectionManager->selectedItems(), KItemSet()); + QVERIFY(!m_selectionManager->hasSelection()); + + m_selectionManager->endAnchoredSelection(); + QVERIFY(!m_selectionManager->isAnchoredSelectionActive()); +} + +void KItemListSelectionManagerTest::testSetSelected_data() +{ + QTest::addColumn("index"); + QTest::addColumn("count"); + QTest::addColumn("expectedSelectionCount"); + + QTest::newRow("Select all") << 0 << 100 << 100; + QTest::newRow("Sub selection 15 items") << 20 << 15 << 15; + QTest::newRow("Sub selection 1 item") << 20 << 1 << 1; + QTest::newRow("Too small index") << -1 << 100 << 0; + QTest::newRow("Too large index") << 100 << 100 << 0; + QTest::newRow("Too large count") << 0 << 100000 << 100; + QTest::newRow("Too small count") << 0 << 0 << 0; +} + +void KItemListSelectionManagerTest::testSetSelected() +{ + QFETCH(int, index); + QFETCH(int, count); + QFETCH(int, expectedSelectionCount); + m_selectionManager->setSelected(index, count); + QCOMPARE(m_selectionManager->selectedItems().count(), expectedSelectionCount); +} + +void KItemListSelectionManagerTest::testItemsInserted() +{ + // Select items 10 to 12 + m_selectionManager->setSelected(10, 3); + KItemSet selectedItems = m_selectionManager->selectedItems(); + QCOMPARE(selectedItems.count(), 3); + QVERIFY(selectedItems.contains(10)); + QVERIFY(selectedItems.contains(11)); + QVERIFY(selectedItems.contains(12)); + + // Insert items 0 to 4 -> selection must be 15 to 17 + m_selectionManager->itemsInserted(KItemRangeList() << KItemRange(0, 5)); + selectedItems = m_selectionManager->selectedItems(); + QCOMPARE(selectedItems.count(), 3); + QVERIFY(selectedItems.contains(15)); + QVERIFY(selectedItems.contains(16)); + QVERIFY(selectedItems.contains(17)); + + // Insert 3 items between the selections + m_selectionManager->itemsInserted(KItemRangeList() << + KItemRange(15, 1) << + KItemRange(16, 1) << + KItemRange(17, 1)); + selectedItems = m_selectionManager->selectedItems(); + QCOMPARE(selectedItems.count(), 3); + QVERIFY(selectedItems.contains(16)); + QVERIFY(selectedItems.contains(18)); + QVERIFY(selectedItems.contains(20)); +} + +void KItemListSelectionManagerTest::testItemsRemoved() +{ + // Select items 10 to 15 + m_selectionManager->setSelected(10, 6); + KItemSet selectedItems = m_selectionManager->selectedItems(); + QCOMPARE(selectedItems.count(), 6); + for (int i = 10; i <= 15; ++i) { + QVERIFY(selectedItems.contains(i)); + } + + // Remove items 0 to 4 -> selection must be 5 to 10 + m_selectionManager->itemsRemoved(KItemRangeList() << KItemRange(0, 5)); + selectedItems = m_selectionManager->selectedItems(); + QCOMPARE(selectedItems.count(), 6); + for (int i = 5; i <= 10; ++i) { + QVERIFY(selectedItems.contains(i)); + } + + // Remove the items 6 , 8 and 10 + m_selectionManager->itemsRemoved(KItemRangeList() << + KItemRange(6, 1) << + KItemRange(8, 1) << + KItemRange(10, 1)); + selectedItems = m_selectionManager->selectedItems(); + QCOMPARE(selectedItems.count(), 3); + QVERIFY(selectedItems.contains(5)); + QVERIFY(selectedItems.contains(6)); + QVERIFY(selectedItems.contains(7)); +} + +void KItemListSelectionManagerTest::testAnchoredSelection() +{ + m_selectionManager->beginAnchoredSelection(5); + QVERIFY(m_selectionManager->isAnchoredSelectionActive()); + QCOMPARE(m_selectionManager->m_anchorItem, 5); + + m_selectionManager->setCurrentItem(6); + QCOMPARE(m_selectionManager->currentItem(), 6); + QCOMPARE(m_selectionManager->selectedItems(), KItemSet() << 5 << 6); + + m_selectionManager->setCurrentItem(4); + QCOMPARE(m_selectionManager->currentItem(), 4); + QCOMPARE(m_selectionManager->selectedItems(), KItemSet() << 4 << 5); + + m_selectionManager->setCurrentItem(7); + QCOMPARE(m_selectionManager->currentItem(), 7); + QCOMPARE(m_selectionManager->selectedItems(), KItemSet() << 5 << 6 << 7); + + // Ending the anchored selection should not change the selected items. + m_selectionManager->endAnchoredSelection(); + QVERIFY(!m_selectionManager->isAnchoredSelectionActive()); + QCOMPARE(m_selectionManager->selectedItems(), KItemSet() << 5 << 6 << 7); + + // Start a new anchored selection that overlaps the previous one + m_selectionManager->beginAnchoredSelection(9); + QVERIFY(m_selectionManager->isAnchoredSelectionActive()); + QCOMPARE(m_selectionManager->m_anchorItem, 9); + + m_selectionManager->setCurrentItem(6); + QCOMPARE(m_selectionManager->currentItem(), 6); + QCOMPARE(m_selectionManager->selectedItems(), KItemSet() << 5 << 6 << 7 << 8 << 9); + + m_selectionManager->setCurrentItem(10); + QCOMPARE(m_selectionManager->currentItem(), 10); + QCOMPARE(m_selectionManager->selectedItems(), KItemSet() << 5 << 6 << 7 << 9 << 10); + + m_selectionManager->endAnchoredSelection(); + QVERIFY(!m_selectionManager->isAnchoredSelectionActive()); + QCOMPARE(m_selectionManager->selectedItems(), KItemSet() << 5 << 6 << 7 << 9 << 10); +} + +namespace { + enum ChangeType { + NoChange, + InsertItems, + RemoveItems, + MoveItems, + EndAnchoredSelection, + SetSelected + }; +} + +Q_DECLARE_METATYPE(KItemSet); +Q_DECLARE_METATYPE(ChangeType); +Q_DECLARE_METATYPE(KItemRange); +Q_DECLARE_METATYPE(KItemRangeList); +Q_DECLARE_METATYPE(KItemListSelectionManager::SelectionMode); +Q_DECLARE_METATYPE(QList); + +/** + * The following function provides a generic way to test the selection functionality. + * + * The test is data-driven and takes the following arguments: + * + * \param initialSelection The selection at the beginning. + * \param anchor This item will be the anchor item. + * \param current This item will be the current item. + * \param expectedSelection Expected selection after anchor and current are set. + * \param changeType Type of the change that is done then: + * - NoChange + * - InsertItems -> data.at(0) provides the KItemRangeList. \sa KItemListSelectionManager::itemsInserted() + * - RemoveItems -> data.at(0) provides the KItemRangeList. \sa KItemListSelectionManager::itemsRemoved() + * - MoveItems -> data.at(0) provides the KItemRange containing the original indices, + * data.at(1) provides the list containing the new indices + * \sa KItemListSelectionManager::itemsMoved(), KItemModelBase::itemsMoved() + * - EndAnchoredSelection + * - SetSelected -> data.at(0) provides the index where the selection process starts, + * data.at(1) provides the number of indices to be selected, + * data.at(2) provides the selection mode. + * \sa KItemListSelectionManager::setSelected() + * \param data A list of QVariants which will be cast to the arguments needed for the chosen ChangeType (see above). + * \param finalSelection The expected final selection. + * + */ + +void KItemListSelectionManagerTest::testChangeSelection_data() +{ + QTest::addColumn("initialSelection"); + QTest::addColumn("anchor"); + QTest::addColumn("current"); + QTest::addColumn("expectedSelection"); + QTest::addColumn("changeType"); + QTest::addColumn >("data"); + QTest::addColumn("finalSelection"); + + QTest::newRow("No change") + << (KItemSet() << 5 << 6) + << 2 << 3 + << (KItemSet() << 2 << 3 << 5 << 6) + << NoChange + << QList() + << (KItemSet() << 2 << 3 << 5 << 6); + + QTest::newRow("Insert Items") + << (KItemSet() << 5 << 6) + << 2 << 3 + << (KItemSet() << 2 << 3 << 5 << 6) + << InsertItems + << (QList() << QVariant::fromValue(KItemRangeList() << KItemRange(1, 1) << KItemRange(5, 2) << KItemRange(10, 5))) + << (KItemSet() << 3 << 4 << 8 << 9); + + QTest::newRow("Remove Items") + << (KItemSet() << 5 << 6) + << 2 << 3 + << (KItemSet() << 2 << 3 << 5 << 6) + << RemoveItems + << (QList() << QVariant::fromValue(KItemRangeList() << KItemRange(1, 1) << KItemRange(3, 1) << KItemRange(10, 5))) + << (KItemSet() << 1 << 2 << 3 << 4); + + QTest::newRow("Empty Anchored Selection") + << KItemSet() + << 2 << 2 + << KItemSet() + << EndAnchoredSelection + << QList() + << KItemSet(); + + QTest::newRow("Toggle selection") + << (KItemSet() << 1 << 3 << 4) + << 6 << 8 + << (KItemSet() << 1 << 3 << 4 << 6 << 7 << 8) + << SetSelected + << (QList() << 0 << 10 << QVariant::fromValue(KItemListSelectionManager::Toggle)) + << (KItemSet() << 0 << 2 << 5 << 9); + + // Swap items 2, 3 and 4, 5 + QTest::newRow("Move items") + << (KItemSet() << 0 << 1 << 2 << 3) + << -1 << -1 + << (KItemSet() << 0 << 1 << 2 << 3) + << MoveItems + << (QList() << QVariant::fromValue(KItemRange(2, 4)) + << QVariant::fromValue(QList() << 4 << 5 << 2 << 3)) + << (KItemSet() << 0 << 1 << 4 << 5); + + QTest::newRow("Move items with active anchored selection") + << KItemSet() + << 0 << 3 + << (KItemSet() << 0 << 1 << 2 << 3) + << MoveItems + << (QList() << QVariant::fromValue(KItemRange(2, 4)) + << QVariant::fromValue(QList() << 4 << 5 << 2 << 3)) + << (KItemSet() << 0 << 1 << 4 << 5); + + // Revert sort order + QTest::newRow("Revert sort order") + << (KItemSet() << 0 << 1) + << 3 << 4 + << (KItemSet() << 0 << 1 << 3 << 4) + << MoveItems + << (QList() << QVariant::fromValue(KItemRange(0, 10)) + << QVariant::fromValue(QList() << 9 << 8 << 7 << 6 << 5 << 4 << 3 << 2 << 1 << 0)) + << (KItemSet() << 5 << 6 << 8 << 9); +} + +void KItemListSelectionManagerTest::testChangeSelection() +{ + QFETCH(KItemSet, initialSelection); + QFETCH(int, anchor); + QFETCH(int, current); + QFETCH(KItemSet, expectedSelection); + QFETCH(ChangeType, changeType); + QFETCH(QList, data); + QFETCH(KItemSet, finalSelection); + + QSignalSpy spySelectionChanged(m_selectionManager, SIGNAL(selectionChanged(KItemSet,KItemSet))); + + // Initial selection should be empty + QVERIFY(!m_selectionManager->hasSelection()); + QVERIFY(m_selectionManager->selectedItems().isEmpty()); + + // Perform the initial selectiion + m_selectionManager->setSelectedItems(initialSelection); + + verifySelectionChange(spySelectionChanged, initialSelection, KItemSet()); + + // Perform an anchored selection. + // Note that current and anchor index are equal first because this is the case in typical uses of the + // selection manager, and because this makes it easier to test the correctness of the signal's arguments. + m_selectionManager->setCurrentItem(anchor); + m_selectionManager->beginAnchoredSelection(anchor); + m_selectionManager->setCurrentItem(current); + QCOMPARE(m_selectionManager->m_anchorItem, anchor); + QCOMPARE(m_selectionManager->currentItem(), current); + + verifySelectionChange(spySelectionChanged, expectedSelection, initialSelection); + + // Change the model by inserting or removing items. + switch (changeType) { + case InsertItems: + m_selectionManager->itemsInserted(data.at(0).value()); + break; + case RemoveItems: + m_selectionManager->itemsRemoved(data.at(0).value()); + break; + case MoveItems: + m_selectionManager->itemsMoved(data.at(0).value(), + data.at(1).value >()); + break; + case EndAnchoredSelection: + m_selectionManager->endAnchoredSelection(); + QVERIFY(!m_selectionManager->isAnchoredSelectionActive()); + break; + case SetSelected: + m_selectionManager->setSelected(data.at(0).value(), // index + data.at(1).value(), // count + data.at(2).value()); + break; + case NoChange: + break; + } + + verifySelectionChange(spySelectionChanged, finalSelection, expectedSelection); + + // Finally, clear the selection + m_selectionManager->clearSelection(); + + verifySelectionChange(spySelectionChanged, KItemSet(), finalSelection); +} + +void KItemListSelectionManagerTest::testDeleteCurrentItem_data() +{ + QTest::addColumn("oldCurrentItemIndex"); + QTest::addColumn("removeIndex"); + QTest::addColumn("removeCount"); + QTest::addColumn("newCurrentItemIndex"); + + QTest::newRow("Remove before") << 50 << 0 << 10 << 40; + QTest::newRow("Remove after") << 50 << 51 << 10 << 50; + QTest::newRow("Remove exactly current item") << 50 << 50 << 1 << 50; + QTest::newRow("Remove around current item") << 50 << 45 << 10 << 45; + QTest::newRow("Remove all except one item") << 50 << 1 << 99 << 0; +} + +void KItemListSelectionManagerTest::testDeleteCurrentItem() +{ + QFETCH(int, oldCurrentItemIndex); + QFETCH(int, removeIndex); + QFETCH(int, removeCount); + QFETCH(int, newCurrentItemIndex); + + m_selectionManager->setCurrentItem(oldCurrentItemIndex); + + const int newCount = m_model->count() - removeCount; + m_model->setCount(newCount); + m_selectionManager->itemsRemoved(KItemRangeList() << KItemRange(removeIndex, removeCount)); + + QCOMPARE(m_selectionManager->currentItem(), newCurrentItemIndex); +} + +void KItemListSelectionManagerTest::testAnchoredSelectionAfterMovingItems() +{ + m_selectionManager->setCurrentItem(4); + m_selectionManager->beginAnchoredSelection(4); + + // Reverse the items between 0 and 5. + m_selectionManager->itemsMoved(KItemRange(0, 6), QList() << 5 << 4 << 3 << 2 << 1 << 0); + + QCOMPARE(m_selectionManager->currentItem(), 1); + QCOMPARE(m_selectionManager->m_anchorItem, 1); + + // Make 2 the current item -> 1 and 2 should be selected. + m_selectionManager->setCurrentItem(2); + QCOMPARE(m_selectionManager->selectedItems(), KItemSet() << 1 << 2); +} + +void KItemListSelectionManagerTest::verifySelectionChange(QSignalSpy& spy, + const KItemSet& currentSelection, + const KItemSet& previousSelection) const +{ + QCOMPARE(m_selectionManager->selectedItems(), currentSelection); + QCOMPARE(m_selectionManager->hasSelection(), !currentSelection.isEmpty()); + for (int index = 0; index < m_selectionManager->model()->count(); ++index) { + if (currentSelection.contains(index)) { + QVERIFY(m_selectionManager->isSelected(index)); + } + else { + QVERIFY(!m_selectionManager->isSelected(index)); + } + } + + if (currentSelection == previousSelection) { + QCOMPARE(spy.count(), 0); + } + else { + QCOMPARE(spy.count(), 1); + QList arguments = spy.takeFirst(); + QCOMPARE(qvariant_cast(arguments.at(0)), currentSelection); + QCOMPARE(qvariant_cast(arguments.at(1)), previousSelection); + } +} + +QTEST_KDEMAIN(KItemListSelectionManagerTest, NoGUI) + +#include "kitemlistselectionmanagertest.moc" diff --git a/dolphin/src/tests/kitemrangetest.cpp b/dolphin/src/tests/kitemrangetest.cpp new file mode 100644 index 00000000..9f3f7998 --- /dev/null +++ b/dolphin/src/tests/kitemrangetest.cpp @@ -0,0 +1,75 @@ +/*************************************************************************** + * Copyright (C) 2014 by Frank Reininghaus * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include + +#include "kitemviews/kitemrange.h" + +#include + +Q_DECLARE_METATYPE(QVector); +Q_DECLARE_METATYPE(KItemRangeList); + +class KItemRangeTest : public QObject +{ + Q_OBJECT + +private slots: + void testFromSortedContainer_data(); + void testFromSortedContainer(); +}; + +void KItemRangeTest::testFromSortedContainer_data() +{ + QTest::addColumn >("sortedNumbers"); + QTest::addColumn("expected"); + + QTest::newRow("empty") << QVector() << KItemRangeList(); + QTest::newRow("[1]") << (QVector() << 1) << (KItemRangeList() << KItemRange(1, 1)); + QTest::newRow("[9]") << (QVector() << 9) << (KItemRangeList() << KItemRange(9, 1)); + QTest::newRow("[1-2]") << (QVector() << 1 << 2) << (KItemRangeList() << KItemRange(1, 2)); + QTest::newRow("[1-3]") << (QVector() << 1 << 2 << 3) << (KItemRangeList() << KItemRange(1, 3)); + QTest::newRow("[1] [4]") << (QVector() << 1 << 4) << (KItemRangeList() << KItemRange(1, 1) << KItemRange(4, 1)); + QTest::newRow("[1-3] [5]") << (QVector() << 1 << 2 << 3 << 5) << (KItemRangeList() << KItemRange(1, 3) << KItemRange(5, 1)); + QTest::newRow("[1] [5-6]") << (QVector() << 1 << 5 << 6) << (KItemRangeList() << KItemRange(1, 1) << KItemRange(5, 2)); + QTest::newRow("duplicates: 1 1") << (QVector() << 1 << 1) << (KItemRangeList() << KItemRange(1, 1)); + QTest::newRow("duplicates: 1 1 1") << (QVector() << 1 << 1 << 1) << (KItemRangeList() << KItemRange(1, 1)); + QTest::newRow("duplicates: 1 1 5") << (QVector() << 1 << 1 << 5) << (KItemRangeList() << KItemRange(1, 1) << KItemRange(5, 1)); + QTest::newRow("duplicates: 1 5 5") << (QVector() << 1 << 5 << 5) << (KItemRangeList() << KItemRange(1, 1) << KItemRange(5, 1)); + QTest::newRow("duplicates: 1 1 1 5") << (QVector() << 1 << 1 << 1 << 5) << (KItemRangeList() << KItemRange(1, 1) << KItemRange(5, 1)); + QTest::newRow("duplicates: 1 5 5 5") << (QVector() << 1 << 5 << 5 << 5) << (KItemRangeList() << KItemRange(1, 1) << KItemRange(5, 1)); + QTest::newRow("duplicates: 1 1 2") << (QVector() << 1 << 1 << 2) << (KItemRangeList() << KItemRange(1, 2)); + QTest::newRow("duplicates: 1 2 2") << (QVector() << 1 << 2 << 2) << (KItemRangeList() << KItemRange(1, 2)); + QTest::newRow("duplicates: 1 1 2 3") << (QVector() << 1 << 1 << 2 << 3) << (KItemRangeList() << KItemRange(1, 3)); + QTest::newRow("duplicates: 1 2 2 3") << (QVector() << 1 << 2 << 2 << 3) << (KItemRangeList() << KItemRange(1, 3)); + QTest::newRow("duplicates: 1 2 3 3") << (QVector() << 1 << 2 << 3 << 3) << (KItemRangeList() << KItemRange(1, 3)); +} + +void KItemRangeTest::testFromSortedContainer() +{ + QFETCH(QVector, sortedNumbers); + QFETCH(KItemRangeList, expected); + + const KItemRangeList result = KItemRangeList::fromSortedContainer(sortedNumbers); + QCOMPARE(expected, result); +} + +QTEST_KDEMAIN(KItemRangeTest, NoGUI) + +#include "kitemrangetest.moc" diff --git a/dolphin/src/tests/kitemsettest.cpp b/dolphin/src/tests/kitemsettest.cpp new file mode 100644 index 00000000..35c7e3b7 --- /dev/null +++ b/dolphin/src/tests/kitemsettest.cpp @@ -0,0 +1,615 @@ +/*************************************************************************** + * Copyright (C) 2013 by Frank Reininghaus * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include + +#include "kitemviews/kitemset.h" + +#include +#include + +Q_DECLARE_METATYPE(KItemRangeList); + +/** + * Converts a KItemRangeList to a KItemSet. + */ +KItemSet KItemRangeList2KItemSet(const KItemRangeList& itemRanges) +{ + KItemSet result; + foreach (const KItemRange& range, itemRanges) { + for (int i = range.index; i < range.index + range.count; ++i) { + result.insert(i); + } + } + return result; +} + +/** + * Converts a KItemRangeList to a QSet. + */ +QSet KItemRangeList2QSet(const KItemRangeList& itemRanges) +{ + QSet result; + foreach (const KItemRange& range, itemRanges) { + for (int i = range.index; i < range.index + range.count; ++i) { + result.insert(i); + } + } + return result; +} + +/** + * Converts a KItemRangeList to a QVector. + */ +QVector KItemRangeList2QVector(const KItemRangeList& itemRanges) +{ + QVector result; + foreach (const KItemRange& range, itemRanges) { + for (int i = range.index; i < range.index + range.count; ++i) { + result.append(i); + } + } + return result; +} + +/** + * Converts a KItemSet to a QSet. + */ +static QSet KItemSet2QSet(const KItemSet& itemSet) +{ + QSet result; + foreach (int i, itemSet) { + result.insert(i); + } + + // Check that the conversion was successful. + Q_ASSERT(itemSet.count() == result.count()); + + foreach (int i, itemSet) { + Q_ASSERT(result.contains(i)); + Q_UNUSED(i); + } + + foreach (int i, result) { + Q_ASSERT(itemSet.contains(i)); + Q_UNUSED(i); + } + + return result; +} + + +/** + * The main test class. + */ +class KItemSetTest : public QObject +{ + Q_OBJECT + +private slots: + void initTestCase(); + + void testConstruction_data(); + void testConstruction(); + void testIterators_data(); + void testIterators(); + void testFind_data(); + void testFind(); + void testChangingOneItem_data(); + void testChangingOneItem(); + void testAddSets_data(); + void testAddSets(); + /* + void testSubtractSets_data(); + void testSubtractSets(); + */ + void testSymmetricDifference_data(); + void testSymmetricDifference(); + +private: + QHash m_testCases; +}; + +void KItemSetTest::initTestCase() +{ + m_testCases.insert("empty", KItemRangeList()); + m_testCases.insert("[0]", KItemRangeList() << KItemRange(0, 1)); + m_testCases.insert("[1]", KItemRangeList() << KItemRange(1, 1)); + m_testCases.insert("[1, 2]", KItemRangeList() << KItemRange(1, 2)); + m_testCases.insert("[1, 2] [5]", KItemRangeList() << KItemRange(1, 2) << KItemRange(5, 1)); + m_testCases.insert("[1] [4, 5]", KItemRangeList() << KItemRange(1, 1) << KItemRange(4, 2)); + m_testCases.insert("[1, 2] [4, 5]", KItemRangeList() << KItemRange(1, 2) << KItemRange(4, 2)); + m_testCases.insert("[1, 5]", KItemRangeList() << KItemRange(1, 5)); + m_testCases.insert("[1, 2] [4, 5] [7] [9, 10] [13] [20, 25] [30]", + KItemRangeList() << KItemRange(1, 2) << KItemRange(4, 2) << KItemRange(7, 1) << KItemRange(9, 2) << KItemRange(20, 6) << KItemRange(30, 1)); + m_testCases.insert("[-10, -1]", KItemRangeList() << KItemRange(-10, 10)); + m_testCases.insert("[-10, 0]", KItemRangeList() << KItemRange(-10, 11)); + m_testCases.insert("[-10, 1]", KItemRangeList() << KItemRange(-10, 12)); + m_testCases.insert("[0, 9]", KItemRangeList() << KItemRange(0, 10)); + m_testCases.insert("[0, 19]", KItemRangeList() << KItemRange(0, 10)); +} + +void KItemSetTest::testConstruction_data() +{ + QTest::addColumn("itemRanges"); + + QHash::const_iterator it = m_testCases.constBegin(); + const QHash::const_iterator end = m_testCases.constEnd(); + + while (it != end) { + QTest::newRow(it.key()) << it.value(); + ++it; + } +} + +void KItemSetTest::testConstruction() +{ + QFETCH(KItemRangeList, itemRanges); + + KItemSet itemSet = KItemRangeList2KItemSet(itemRanges); + QSet itemsQSet = KItemRangeList2QSet(itemRanges); + + QVERIFY(itemSet.isValid()); + QVERIFY(itemSet.count() == itemsQSet.count()); + QCOMPARE(KItemSet2QSet(itemSet), itemsQSet); + + // Test copy constructor. + KItemSet copy(itemSet); + QCOMPARE(itemSet, copy); + copy.clear(); + QVERIFY(itemSet != copy || itemSet.isEmpty()); + + // Clear the set. + itemSet.clear(); + QVERIFY(itemSet.isEmpty()); + QCOMPARE(itemSet.count(), 0); +} + +void KItemSetTest::testIterators_data() +{ + QTest::addColumn("itemRanges"); + + QHash::const_iterator it = m_testCases.constBegin(); + const QHash::const_iterator end = m_testCases.constEnd(); + + while (it != end) { + QTest::newRow(it.key()) << it.value(); + ++it; + } +} + +/** + * Verify that the iterators work exactly like their counterparts for the + * equivalent QVector. + */ +void KItemSetTest::testIterators() +{ + QFETCH(KItemRangeList, itemRanges); + + KItemSet itemSet = KItemRangeList2KItemSet(itemRanges); + QVector itemsQVector = KItemRangeList2QVector(itemRanges); + + QVERIFY(itemSet.isValid()); + QVERIFY(itemSet.count() == itemsQVector.count()); + + if (itemSet.count() == 0) { + QVERIFY(itemSet.isEmpty()); + QVERIFY(itemSet.begin() == itemSet.end()); + QVERIFY(itemSet.constBegin() == itemSet.constEnd()); + } else { + QVERIFY(!itemSet.isEmpty()); + QVERIFY(itemSet.begin() != itemSet.end()); + QVERIFY(itemSet.constBegin() != itemSet.constEnd()); + + const int min = itemsQVector.first(); + const int max = itemsQVector.last(); + + QCOMPARE(*itemSet.begin(), min); + QCOMPARE(*itemSet.constBegin(), min); + QCOMPARE(itemSet.first(), min); + + QCOMPARE(*(--itemSet.end()), max); + QCOMPARE(*(--itemSet.constEnd()), max); + QCOMPARE(itemSet.last(), max); + } + + // Test iterating using the different iterators. + QVector testQVector; + for (KItemSet::iterator it = itemSet.begin(), end = itemSet.end(); it != end; ++it) { + testQVector.append(*it); + } + QCOMPARE(testQVector, itemsQVector); + + testQVector.clear(); + for (KItemSet::const_iterator it = itemSet.constBegin(), end = itemSet.constEnd(); it != end; ++it) { + testQVector.append(*it); + } + QCOMPARE(testQVector, itemsQVector); + + testQVector.clear(); + foreach (int i, itemSet) { + testQVector.append(i); + } + QCOMPARE(testQVector, itemsQVector); + + // Verify that both variants of the (const)iterator's operator++ and + // operator-- functions behave exactly like their QVector equivalents. + KItemSet::iterator it1 = itemSet.begin(); + KItemSet::iterator it2 = itemSet.begin(); + KItemSet::const_iterator constIt1 = itemSet.constBegin(); + KItemSet::const_iterator constIt2 = itemSet.constBegin(); + QVector::iterator vectorIt1 = itemsQVector.begin(); + QVector::iterator vectorIt2 = itemsQVector.begin(); + QVector::const_iterator vectorConstIt1 = itemsQVector.constBegin(); + QVector::const_iterator vectorConstIt2 = itemsQVector.constBegin(); + + while (it1 != itemSet.end()) { + if (it1 != --itemSet.end()) { + QCOMPARE(*(++it1), *(++vectorIt1)); + QCOMPARE(*(++constIt1), *(++vectorConstIt1)); + } else { + QCOMPARE(++it1, itemSet.end()); + QCOMPARE(++vectorIt1, itemsQVector.end()); + QCOMPARE(++constIt1, itemSet.constEnd()); + QCOMPARE(++vectorConstIt1, itemsQVector.constEnd()); + } + + QCOMPARE(*(it2++), *(vectorIt2++)); + QCOMPARE(*(constIt2++), *(vectorConstIt2++)); + + QCOMPARE(it1, it2); + QCOMPARE(constIt1, constIt2); + QCOMPARE(KItemSet::const_iterator(it1), constIt1); + } + + QCOMPARE(it1, itemSet.end()); + QCOMPARE(it2, itemSet.end()); + QCOMPARE(constIt1, itemSet.constEnd()); + QCOMPARE(constIt2, itemSet.constEnd()); + QCOMPARE(vectorIt1, itemsQVector.end()); + QCOMPARE(vectorIt2, itemsQVector.end()); + QCOMPARE(vectorConstIt1, itemsQVector.constEnd()); + QCOMPARE(vectorConstIt2, itemsQVector.constEnd()); + + while (it1 != itemSet.begin()) { + QCOMPARE(*(--it1), *(--vectorIt1)); + QCOMPARE(*(--constIt1), *(--vectorConstIt1)); + + if (it2 != itemSet.end()) { + QCOMPARE(*(it2--), *(vectorIt2--)); + QCOMPARE(*(constIt2--), *(vectorConstIt2--)); + } else { + QCOMPARE(it2--, itemSet.end()); + QCOMPARE(vectorIt2--, itemsQVector.end()); + QCOMPARE(constIt2--, itemSet.constEnd()); + QCOMPARE(vectorConstIt2--, itemsQVector.constEnd()); + } + + QCOMPARE(it1, it2); + QCOMPARE(constIt1, constIt2); + QCOMPARE(KItemSet::const_iterator(it1), constIt1); + } + + QCOMPARE(it1, itemSet.begin()); + QCOMPARE(it2, itemSet.begin()); + QCOMPARE(constIt1, itemSet.constBegin()); + QCOMPARE(constIt2, itemSet.constBegin()); + QCOMPARE(vectorIt1, itemsQVector.begin()); + QCOMPARE(vectorIt2, itemsQVector.begin()); + QCOMPARE(vectorConstIt1, itemsQVector.constBegin()); + QCOMPARE(vectorConstIt2, itemsQVector.constBegin()); +} + +void KItemSetTest::testFind_data() +{ + QTest::addColumn("itemRanges"); + + QHash::const_iterator it = m_testCases.constBegin(); + const QHash::const_iterator end = m_testCases.constEnd(); + + while (it != end) { + QTest::newRow(it.key()) << it.value(); + ++it; + } +} + +/** + * Test all functions that find items: + * contais(int), find(int), constFind(int) + */ +void KItemSetTest::testFind() +{ + QFETCH(KItemRangeList, itemRanges); + + KItemSet itemSet = KItemRangeList2KItemSet(itemRanges); + QSet itemsQSet = KItemRangeList2QSet(itemRanges); + + QVERIFY(itemSet.isValid()); + QVERIFY(itemSet.count() == itemsQSet.count()); + + // Find the minimum and maximum items. + int min; + int max; + + if (itemSet.count() == 0) { + // Use some arbitrary values for the upcoming tests. + min = 0; + max = 5; + } else { + min = *itemSet.begin(); + max = *(--itemSet.end()); + } + + // Test contains(int), find(int), and constFind(int) + // for items between min - 2 and max + 2. + for (int i = min - 2; i <= max + 2; ++i) { + const KItemSet::iterator it = itemSet.find(i); + const KItemSet::const_iterator constIt = itemSet.constFind(i); + QCOMPARE(KItemSet::const_iterator(it), constIt); + + if (itemsQSet.contains(i)) { + QVERIFY(itemSet.contains(i)); + QCOMPARE(*it, i); + QCOMPARE(*constIt, i); + } else { + QVERIFY(!itemSet.contains(i)); + QCOMPARE(it, itemSet.end()); + QCOMPARE(constIt, itemSet.constEnd()); + } + } +} + +void KItemSetTest::testChangingOneItem_data() +{ + QTest::addColumn("itemRanges"); + + QHash::const_iterator it = m_testCases.constBegin(); + const QHash::const_iterator end = m_testCases.constEnd(); + + while (it != end) { + QTest::newRow(it.key()) << it.value(); + ++it; + } +} + +/** + * Test all functions that change a single item: + * insert(int), remove(int), erase(KItemSet::iterator) + */ +void KItemSetTest::testChangingOneItem() +{ + QFETCH(KItemRangeList, itemRanges); + + KItemSet itemSet = KItemRangeList2KItemSet(itemRanges); + QSet itemsQSet = KItemRangeList2QSet(itemRanges); + + QVERIFY(itemSet.isValid()); + QVERIFY(itemSet.count() == itemsQSet.count()); + + // Find the minimum and maximum items. + int min; + int max; + + if (itemSet.count() == 0) { + // Use some arbitrary values for the upcoming tests. + min = 0; + max = 5; + } else { + min = *itemSet.begin(); + max = *(--itemSet.end()); + } + + // Test insert(int), remove(int), and erase(KItemSet::iterator) + // for items between min - 2 and max + 2. + for (int i = min - 2; i <= max + 2; ++i) { + + // Test insert(int). + { + KItemSet tmp(itemSet); + const KItemSet::iterator insertedIt = tmp.insert(i); + QCOMPARE(*insertedIt, i); + + QVERIFY(tmp.isValid()); + QVERIFY(tmp.contains(i)); + + QSet expectedQSet = itemsQSet; + expectedQSet.insert(i); + QCOMPARE(KItemSet2QSet(tmp), expectedQSet); + + if (!itemSet.contains(i)) { + QVERIFY(itemSet != tmp); + QCOMPARE(tmp.count(), itemSet.count() + 1); + } else { + QCOMPARE(itemSet, tmp); + } + + QCOMPARE(i, *tmp.find(i)); + QCOMPARE(i, *tmp.constFind(i)); + + // Erase the new item and check that we get the old KItemSet back. + tmp.erase(tmp.find(i)); + QVERIFY(tmp.isValid()); + QVERIFY(!tmp.contains(i)); + + if (!itemSet.contains(i)) { + QCOMPARE(itemSet, tmp); + } + + expectedQSet.remove(i); + QCOMPARE(KItemSet2QSet(tmp), expectedQSet); + } + + // Test remove(int). + { + KItemSet tmp(itemSet); + const bool removed = tmp.remove(i); + + QCOMPARE(removed, itemSet.contains(i)); + + QVERIFY(tmp.isValid()); + QVERIFY(!tmp.contains(i)); + + QSet expectedQSet = itemsQSet; + expectedQSet.remove(i); + QCOMPARE(KItemSet2QSet(tmp), expectedQSet); + + if (itemSet.contains(i)) { + QVERIFY(itemSet != tmp); + QCOMPARE(tmp.count(), itemSet.count() - 1); + } else { + QCOMPARE(itemSet, tmp); + } + + QCOMPARE(tmp.end(), tmp.find(i)); + QCOMPARE(tmp.constEnd(), tmp.constFind(i)); + } + + // Test erase(KItemSet::iterator). + if (itemSet.contains(i)) { + KItemSet tmp(itemSet); + KItemSet::iterator it = tmp.find(i); + it = tmp.erase(it); + + QVERIFY(tmp.isValid()); + QVERIFY(!tmp.contains(i)); + + QSet expectedQSet = itemsQSet; + expectedQSet.remove(i); + QCOMPARE(KItemSet2QSet(tmp), expectedQSet); + + if (itemSet.contains(i)) { + QVERIFY(itemSet != tmp); + QCOMPARE(tmp.count(), itemSet.count() - 1); + } else { + QCOMPARE(itemSet, tmp); + } + + QCOMPARE(tmp.end(), tmp.find(i)); + QCOMPARE(tmp.constEnd(), tmp.constFind(i)); + + // Check the returen value, now contained in 'it'. + if (i == max) { + QCOMPARE(it, tmp.end()); + } else { + // it now points to the next item. + QVERIFY(tmp.contains(*it)); + for (int j = i; j < *it; ++j) { + QVERIFY(!tmp.contains(j)); + } + } + } + } + + // Clear the set. + itemSet.clear(); + QVERIFY(itemSet.isEmpty()); + QCOMPARE(itemSet.count(), 0); +} + +void KItemSetTest::testAddSets_data() +{ + QTest::addColumn("itemRanges1"); + QTest::addColumn("itemRanges2"); + + QHash::const_iterator it1 = m_testCases.constBegin(); + const QHash::const_iterator end = m_testCases.constEnd(); + + while (it1 != end) { + QHash::const_iterator it2 = m_testCases.constBegin(); + + while (it2 != end) { + QByteArray name = it1.key() + QByteArray(" + ") + it2.key(); + QTest::newRow(name) << it1.value() << it2.value(); + ++it2; + } + + ++it1; + } +} + +void KItemSetTest::testAddSets() +{ + QFETCH(KItemRangeList, itemRanges1); + QFETCH(KItemRangeList, itemRanges2); + + KItemSet itemSet1 = KItemRangeList2KItemSet(itemRanges1); + QSet itemsQSet1 = KItemRangeList2QSet(itemRanges1); + + KItemSet itemSet2 = KItemRangeList2KItemSet(itemRanges2); + QSet itemsQSet2 = KItemRangeList2QSet(itemRanges2); + + KItemSet sum = itemSet1 + itemSet2; + QSet sumQSet = itemsQSet1 + itemsQSet2; + + QCOMPARE(sum.count(), sumQSet.count()); + QCOMPARE(KItemSet2QSet(sum), sumQSet); +} + +void KItemSetTest::testSymmetricDifference_data() +{ + QTest::addColumn("itemRanges1"); + QTest::addColumn("itemRanges2"); + + QHash::const_iterator it1 = m_testCases.constBegin(); + const QHash::const_iterator end = m_testCases.constEnd(); + + while (it1 != end) { + QHash::const_iterator it2 = m_testCases.constBegin(); + + while (it2 != end) { + QByteArray name = it1.key() + QByteArray(" ^ ") + it2.key(); + QTest::newRow(name) << it1.value() << it2.value(); + ++it2; + } + + ++it1; + } +} + +void KItemSetTest::testSymmetricDifference() +{ + QFETCH(KItemRangeList, itemRanges1); + QFETCH(KItemRangeList, itemRanges2); + + KItemSet itemSet1 = KItemRangeList2KItemSet(itemRanges1); + QSet itemsQSet1 = KItemRangeList2QSet(itemRanges1); + + KItemSet itemSet2 = KItemRangeList2KItemSet(itemRanges2); + QSet itemsQSet2 = KItemRangeList2QSet(itemRanges2); + + KItemSet symmetricDifference = itemSet1 ^ itemSet2; + QSet symmetricDifferenceQSet = (itemsQSet1 - itemsQSet2) + (itemsQSet2 - itemsQSet1); + + QCOMPARE(symmetricDifference.count(), symmetricDifferenceQSet.count()); + QCOMPARE(KItemSet2QSet(symmetricDifference), symmetricDifferenceQSet); + + // Check commutativity. + QCOMPARE(itemSet2 ^ itemSet1, symmetricDifference); + + // Some more checks: + // itemSet1 ^ symmetricDifference == itemSet2, + // itemSet2 ^ symmetricDifference == itemSet1. + QCOMPARE(itemSet1 ^ symmetricDifference, itemSet2); + QCOMPARE(itemSet2 ^ symmetricDifference, itemSet1); +} + + +QTEST_KDEMAIN(KItemSetTest, NoGUI) + +#include "kitemsettest.moc" diff --git a/dolphin/src/tests/kstandarditemmodeltest.cpp b/dolphin/src/tests/kstandarditemmodeltest.cpp new file mode 100644 index 00000000..f102e0ed --- /dev/null +++ b/dolphin/src/tests/kstandarditemmodeltest.cpp @@ -0,0 +1,119 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz * + * Copyright (C) 2011 by Frank Reininghaus * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include + +#include "kitemviews/kstandarditem.h" +#include "kitemviews/kstandarditemmodel.h" + +#include + +class KStandardItemModelTest : public QObject +{ + Q_OBJECT + +private slots: + void init(); + void cleanup(); + + void testNewItems(); + void testRemoveItems(); + +private: + bool isModelConsistent() const; + +private: + KStandardItemModel* m_model; +}; + +void KStandardItemModelTest::init() +{ + m_model = new KStandardItemModel(); +} + +void KStandardItemModelTest::cleanup() +{ + delete m_model; + m_model = 0; +} + +void KStandardItemModelTest::testNewItems() +{ + m_model->insertItem(0, new KStandardItem("item 1")); + m_model->insertItem(0, new KStandardItem("item 2")); + m_model->insertItem(2, new KStandardItem("item 3")); + m_model->appendItem(new KStandardItem("item 4")); + m_model->insertItem(-1, new KStandardItem("invalid 1")); + m_model->insertItem(5, new KStandardItem("invalid 2")); + QCOMPARE(m_model->count(), 4); + QCOMPARE(m_model->item(0)->text(), QString("item 2")); + QCOMPARE(m_model->item(1)->text(), QString("item 1")); + QCOMPARE(m_model->item(2)->text(), QString("item 3")); + QCOMPARE(m_model->item(3)->text(), QString("item 4")); + + QVERIFY(isModelConsistent()); +} + +void KStandardItemModelTest::testRemoveItems() +{ + for (int i = 1; i <= 10; ++i) { + m_model->appendItem(new KStandardItem("item " + QString::number(i))); + } + + m_model->removeItem(0); + m_model->removeItem(3); + m_model->removeItem(5); + m_model->removeItem(-1); + QCOMPARE(m_model->count(), 7); + QCOMPARE(m_model->item(0)->text(), QString("item 2")); + QCOMPARE(m_model->item(1)->text(), QString("item 3")); + QCOMPARE(m_model->item(2)->text(), QString("item 4")); + QCOMPARE(m_model->item(3)->text(), QString("item 6")); + QCOMPARE(m_model->item(4)->text(), QString("item 7")); + QCOMPARE(m_model->item(5)->text(), QString("item 9")); + QCOMPARE(m_model->item(6)->text(), QString("item 10")); +} + +bool KStandardItemModelTest::isModelConsistent() const +{ + if (m_model->m_items.count() != m_model->m_indexesForItems.count()) { + return false; + } + + for (int i = 0; i < m_model->count(); ++i) { + const KStandardItem* item = m_model->item(i); + if (!item) { + qWarning() << "Item" << i << "is null"; + return false; + } + + const int itemIndex = m_model->index(item); + if (itemIndex != i) { + qWarning() << "Item" << i << "has a wrong index:" << itemIndex; + return false; + } + } + + return true; +} + +QTEST_KDEMAIN(KStandardItemModelTest, NoGUI) + +#include "kstandarditemmodeltest.moc" diff --git a/dolphin/src/tests/testdir.cpp b/dolphin/src/tests/testdir.cpp new file mode 100644 index 00000000..a598179c --- /dev/null +++ b/dolphin/src/tests/testdir.cpp @@ -0,0 +1,116 @@ +/***************************************************************************** + * Copyright (C) 2010-2011 by Frank Reininghaus (frank78ac@googlemail.com) * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + *****************************************************************************/ + +#include "testdir.h" + +#include + +#ifdef Q_OS_UNIX +#include +#else +#include +#endif + +TestDir::TestDir(const QString& directoryPrefix) : + KTempDir(directoryPrefix) +{ +} + +TestDir::~TestDir() +{ +} + +KUrl TestDir::url() const +{ + return KUrl(name()); +} + +/** The following function is taken from kdelibs/kio/tests/kiotesthelper.h, copyright (C) 2006 by David Faure */ +static void setTimeStamp(const QString& path, const QDateTime& mtime) +{ +#ifdef Q_OS_UNIX + struct utimbuf utbuf; + utbuf.actime = mtime.toTime_t(); + utbuf.modtime = utbuf.actime; + utime(QFile::encodeName(path), &utbuf); +#endif +} + +void TestDir::createFile(const QString& path, const QByteArray& data, const QDateTime& time) +{ + QString absolutePath = path; + makePathAbsoluteAndCreateParents(absolutePath); + + QFile f(absolutePath); + f.open(QIODevice::WriteOnly); + f.write(data); + f.close(); + + if (time.isValid()) { + setTimeStamp(absolutePath, time); + } + + Q_ASSERT(QFile::exists(absolutePath)); +} + +void TestDir::createFiles(const QStringList& files) +{ + foreach (const QString& path, files) { + createFile(path); + } +} + +void TestDir::createDir(const QString& path, const QDateTime& time) +{ + QString absolutePath = path; + makePathAbsoluteAndCreateParents(absolutePath); + QDir(name()).mkdir(absolutePath); + + if (time.isValid()) { + setTimeStamp(absolutePath, time); + } + + Q_ASSERT(QFile::exists(absolutePath)); +} + +void TestDir::removeFile(const QString& path) +{ + QString absolutePath = path; + QFileInfo fileInfo(absolutePath); + if (!fileInfo.isAbsolute()) { + absolutePath = name() + path; + } + QFile::remove(absolutePath); +} + +void TestDir::makePathAbsoluteAndCreateParents(QString& path) +{ + QFileInfo fileInfo(path); + if (!fileInfo.isAbsolute()) { + path = name() + path; + fileInfo.setFile(path); + } + + const QDir dir = fileInfo.dir(); + if (!dir.exists()) { + createDir(dir.absolutePath()); + } + + Q_ASSERT(dir.exists()); +} diff --git a/dolphin/src/tests/testdir.h b/dolphin/src/tests/testdir.h new file mode 100644 index 00000000..0d3c5dd8 --- /dev/null +++ b/dolphin/src/tests/testdir.h @@ -0,0 +1,59 @@ +/***************************************************************************** + * Copyright (C) 2010-2011 by Frank Reininghaus (frank78ac@googlemail.com) * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + *****************************************************************************/ + +#ifndef TESTDIR_H +#define TESTDIR_H + +#include +#include + +#include + +/** + * TestDir provides a temporary directory. In addition to KTempDir, it has + * methods that create files and subdirectories inside the directory. + */ +class TestDir : public KTempDir +{ + +public: + TestDir(const QString& directoryPrefix = QString()); + virtual ~TestDir(); + + KUrl url() const; + + /** + * The following functions create either a file, a list of files, or a directory. + * The paths may be absolute or relative to the test directory. Any missing parent + * directories will be created automatically. + */ + void createFile(const QString& path, + const QByteArray& data = QByteArray("test"), + const QDateTime& time = QDateTime()); + void createFiles(const QStringList& files); + void createDir(const QString& path, const QDateTime& time = QDateTime()); + + void removeFile(const QString& path); + +private: + void makePathAbsoluteAndCreateParents(QString& path); + +}; + +#endif diff --git a/dolphin/src/tests/viewpropertiestest.cpp b/dolphin/src/tests/viewpropertiestest.cpp new file mode 100644 index 00000000..c459f687 --- /dev/null +++ b/dolphin/src/tests/viewpropertiestest.cpp @@ -0,0 +1,101 @@ +/*************************************************************************** + * Copyright (C) 2012 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include + +#include "dolphin_generalsettings.h" +#include "views/viewproperties.h" +#include "testdir.h" + +#include +#include + +class ViewPropertiesTest : public QObject +{ + Q_OBJECT + +private slots: + void init(); + void cleanup(); + + void testReadOnlyBehavior(); + void testAutoSave(); + +private: + bool m_globalViewProps; + TestDir* m_testDir; +}; + +void ViewPropertiesTest::init() +{ + m_globalViewProps = GeneralSettings::self()->globalViewProps(); + GeneralSettings::self()->setGlobalViewProps(false); + + // It is mandatory to create the test-directory inside the home-directory + // of the user: ViewProperties does not write inside directories + // outside the home-directory to prevent overwriting other user-settings + // in case if write-permissions are given. + m_testDir = new TestDir(QDir::homePath() + "/.viewPropertiesTest-"); +} + +void ViewPropertiesTest::cleanup() +{ + delete m_testDir; + m_testDir = 0; + + GeneralSettings::self()->setGlobalViewProps(m_globalViewProps); +} + +/** + * Test whether only reading properties won't result in creating + * a .directory file when destructing the ViewProperties instance + * and autosaving is enabled. + */ +void ViewPropertiesTest::testReadOnlyBehavior() +{ + QString dotDirectoryFile = m_testDir->url().toLocalFile() + ".directory"; + QVERIFY(!QFile::exists(dotDirectoryFile)); + + ViewProperties* props = new ViewProperties(m_testDir->url()); + QVERIFY(props->isAutoSaveEnabled()); + const QByteArray sortRole = props->sortRole(); + Q_UNUSED(sortRole); + delete props; + props = 0; + + QVERIFY(!QFile::exists(dotDirectoryFile)); +} + +void ViewPropertiesTest::testAutoSave() +{ + QString dotDirectoryFile = m_testDir->url().toLocalFile() + ".directory"; + QVERIFY(!QFile::exists(dotDirectoryFile)); + + ViewProperties* props = new ViewProperties(m_testDir->url()); + QVERIFY(props->isAutoSaveEnabled()); + props->setSortRole("someNewSortRole"); + delete props; + props = 0; + + QVERIFY(QFile::exists(dotDirectoryFile)); +} + +QTEST_KDEMAIN(ViewPropertiesTest, NoGUI) + +#include "viewpropertiestest.moc" diff --git a/dolphin/src/views/dolphinfileitemlistwidget.cpp b/dolphin/src/views/dolphinfileitemlistwidget.cpp new file mode 100644 index 00000000..6edde0bb --- /dev/null +++ b/dolphin/src/views/dolphinfileitemlistwidget.cpp @@ -0,0 +1,129 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "dolphinfileitemlistwidget.h" + +#include +#include +#include +#include + +#include + +DolphinFileItemListWidget::DolphinFileItemListWidget(KItemListWidgetInformant* informant, + QGraphicsItem* parent) : + KFileItemListWidget(informant, parent) +{ +} + +DolphinFileItemListWidget::~DolphinFileItemListWidget() +{ +} + +void DolphinFileItemListWidget::refreshCache() +{ + QColor color; + const QHash values = data(); + if (values.contains("version")) { + // The item is under version control. Apply the text color corresponding + // to its version state. + const KVersionControlPlugin::ItemVersion version = static_cast(values.value("version").toInt()); + const QColor textColor = styleOption().palette.text().color(); + QColor tintColor = textColor; + + // Using hardcoded colors is generally a bad idea. In this case the colors just act + // as tint colors and are mixed with the current set text color. The tint colors + // have been optimized for the base colors of the corresponding Oxygen emblems. + switch (version) { + case KVersionControlPlugin::UpdateRequiredVersion: tintColor = Qt::yellow; break; + case KVersionControlPlugin::LocallyModifiedUnstagedVersion: tintColor = Qt::green; break; + case KVersionControlPlugin::LocallyModifiedVersion: tintColor = Qt::green; break; + case KVersionControlPlugin::AddedVersion: tintColor = Qt::green; break; + case KVersionControlPlugin::RemovedVersion: tintColor = Qt::darkRed; break; + case KVersionControlPlugin::ConflictingVersion: tintColor = Qt::red; break; + case KVersionControlPlugin::IgnoredVersion: tintColor = Qt::white; break; + case KVersionControlPlugin::MissingVersion: tintColor = Qt::red; break; + case KVersionControlPlugin::NormalVersion: + case KVersionControlPlugin::UnversionedVersion: + default: + break; + } + + color = QColor((tintColor.red() + textColor.red()) / 2, + (tintColor.green() + textColor.green()) / 2, + (tintColor.blue() + textColor.blue()) / 2, + (tintColor.alpha() + textColor.alpha()) / 2); + + setOverlay(overlayForState(version, styleOption().iconSize)); + } else if (!overlay().isNull()) { + setOverlay(QPixmap()); + } + + setTextColor(color); +} + +QPixmap DolphinFileItemListWidget::overlayForState(KVersionControlPlugin::ItemVersion version, int size) +{ + int overlayHeight = KIconLoader::SizeSmall; + if (size >= KIconLoader::SizeEnormous) { + overlayHeight = KIconLoader::SizeMedium; + } else if (size >= KIconLoader::SizeLarge) { + overlayHeight = KIconLoader::SizeSmallMedium; + } else if (size >= KIconLoader::SizeMedium) { + overlayHeight = KIconLoader::SizeSmall; + } else { + overlayHeight = KIconLoader::SizeSmall / 2; + } + + QString iconName; + switch (version) { + case KVersionControlPlugin::NormalVersion: + iconName = "vcs-normal"; + break; + case KVersionControlPlugin::UpdateRequiredVersion: + iconName = "vcs-update-required"; + break; + case KVersionControlPlugin::LocallyModifiedVersion: + iconName = "vcs-locally-modified"; + break; + case KVersionControlPlugin::LocallyModifiedUnstagedVersion: + iconName = "vcs-locally-modified-unstaged"; + break; + case KVersionControlPlugin::AddedVersion: + iconName = "vcs-added"; + break; + case KVersionControlPlugin::RemovedVersion: + iconName = "vcs-removed"; + break; + case KVersionControlPlugin::ConflictingVersion: + iconName = "vcs-conflicting"; + break; + case KVersionControlPlugin::UnversionedVersion: + case KVersionControlPlugin::IgnoredVersion: + case KVersionControlPlugin::MissingVersion: + break; + default: + Q_ASSERT(false); + break; + } + + return KIcon(iconName).pixmap(QSize(overlayHeight, overlayHeight)); +} + +#include "moc_dolphinfileitemlistwidget.cpp" diff --git a/dolphin/src/views/dolphinfileitemlistwidget.h b/dolphin/src/views/dolphinfileitemlistwidget.h new file mode 100644 index 00000000..59792624 --- /dev/null +++ b/dolphin/src/views/dolphinfileitemlistwidget.h @@ -0,0 +1,52 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef DOLPHINFILEITEMLISTWIDGET_H +#define DOLPHINFILEITEMLISTWIDGET_H + +#include + +#include +#include + +/** + * @brief Extends KFileItemListWidget to handle the "version" role. + * + * The "version" role is set if version-control-plugins have been enabled. + * @see KVersionControlPlugin + */ +class DOLPHINPRIVATE_EXPORT DolphinFileItemListWidget : public KFileItemListWidget +{ + Q_OBJECT + +public: + DolphinFileItemListWidget(KItemListWidgetInformant* informant, QGraphicsItem* parent); + virtual ~DolphinFileItemListWidget(); + +protected: + virtual void refreshCache(); + +private: + static QPixmap overlayForState(KVersionControlPlugin::ItemVersion version, int size); + +}; + +#endif + + diff --git a/dolphin/src/views/dolphinitemlistview.cpp b/dolphin/src/views/dolphinitemlistview.cpp new file mode 100644 index 00000000..36c431dc --- /dev/null +++ b/dolphin/src/views/dolphinitemlistview.cpp @@ -0,0 +1,272 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "dolphinitemlistview.h" + +#include "dolphin_generalsettings.h" +#include "dolphin_iconsmodesettings.h" +#include "dolphin_detailsmodesettings.h" +#include "dolphin_compactmodesettings.h" +#include "dolphinfileitemlistwidget.h" + +#include +#include +#include +#include + +#include +#include + +#include + +#include "zoomlevelinfo.h" + + +DolphinItemListView::DolphinItemListView(QGraphicsWidget* parent) : + KFileItemListView(parent), + m_zoomLevel(0) +{ + updateFont(); + updateGridSize(); +} + +DolphinItemListView::~DolphinItemListView() +{ + writeSettings(); +} + +void DolphinItemListView::setZoomLevel(int level) +{ + if (level < ZoomLevelInfo::minimumLevel()) { + level = ZoomLevelInfo::minimumLevel(); + } else if (level > ZoomLevelInfo::maximumLevel()) { + level = ZoomLevelInfo::maximumLevel(); + } + + if (level == m_zoomLevel) { + return; + } + + m_zoomLevel = level; + + ViewModeSettings settings(viewMode()); + if (previewsShown()) { + const int previewSize = ZoomLevelInfo::iconSizeForZoomLevel(level); + settings.setPreviewSize(previewSize); + } else { + const int iconSize = ZoomLevelInfo::iconSizeForZoomLevel(level); + settings.setIconSize(iconSize); + } + + updateGridSize(); +} + +int DolphinItemListView::zoomLevel() const +{ + return m_zoomLevel; +} + +void DolphinItemListView::readSettings() +{ + ViewModeSettings settings(viewMode()); + settings.readConfig(); + + beginTransaction(); + + setEnabledSelectionToggles(GeneralSettings::showSelectionToggle()); + setSupportsItemExpanding(itemLayoutSupportsItemExpanding(itemLayout())); + + updateFont(); + updateGridSize(); + + QStringList enabledByDefault; + const KService::List plugins = KServiceTypeTrader::self()->query(QLatin1String("ThumbCreator")); + foreach (const KSharedPtr& service, plugins) { + const bool enabled = service->property("X-KDE-PluginInfo-EnabledByDefault", QVariant::Bool).toBool(); + if (enabled) { + enabledByDefault << service->desktopEntryName(); + } + } + + const KConfigGroup globalConfig(KGlobal::config(), "PreviewSettings"); + const QStringList enabledPlugins = globalConfig.readEntry("Plugins", enabledByDefault); + setEnabledPlugins(enabledPlugins); + + endTransaction(); +} + +void DolphinItemListView::writeSettings() +{ + IconsModeSettings::self()->writeConfig(); + CompactModeSettings::self()->writeConfig(); + DetailsModeSettings::self()->writeConfig(); +} + +KItemListWidgetCreatorBase* DolphinItemListView::defaultWidgetCreator() const +{ + return new KItemListWidgetCreator(); +} + +bool DolphinItemListView::itemLayoutSupportsItemExpanding(ItemLayout layout) const +{ + return layout == DetailsLayout && DetailsModeSettings::expandableFolders(); +} + +void DolphinItemListView::onItemLayoutChanged(ItemLayout current, ItemLayout previous) +{ + setHeaderVisible(current == DetailsLayout); + + updateFont(); + updateGridSize(); + + KFileItemListView::onItemLayoutChanged(current, previous); +} + +void DolphinItemListView::onPreviewsShownChanged(bool shown) +{ + Q_UNUSED(shown); + updateGridSize(); +} + +void DolphinItemListView::onVisibleRolesChanged(const QList& current, + const QList& previous) +{ + KFileItemListView::onVisibleRolesChanged(current, previous); + updateGridSize(); +} + +void DolphinItemListView::updateFont() +{ + const ViewModeSettings settings(viewMode()); + + if (settings.useSystemFont()) { + KItemListView::updateFont(); + } else { + QFont font(settings.fontFamily(), qRound(settings.fontSize())); + font.setItalic(settings.italicFont()); + font.setWeight(settings.fontWeight()); + font.setPointSizeF(settings.fontSize()); + + KItemListStyleOption option = styleOption(); + option.font = font; + option.fontMetrics = QFontMetrics(font); + + setStyleOption(option); + } +} + +void DolphinItemListView::updateGridSize() +{ + const ViewModeSettings settings(viewMode()); + + // Calculate the size of the icon + const int iconSize = previewsShown() ? settings.previewSize() : settings.iconSize(); + m_zoomLevel = ZoomLevelInfo::zoomLevelForIconSize(QSize(iconSize, iconSize)); + KItemListStyleOption option = styleOption(); + + const int padding = 2; + int horizontalMargin = 0; + int verticalMargin = 0; + + // Calculate the item-width and item-height + int itemWidth; + int itemHeight; + int maxTextLines = 0; + int maxTextWidth = 0; + + switch (itemLayout()) { + case KFileItemListView::IconsLayout: { + const int minItemWidth = 48; + itemWidth = minItemWidth + IconsModeSettings::textWidthIndex() * 64; + + if (previewsShown()) { + // Optimize the width for previews with a 3:2 aspect ratio instead + // of a 1:1 ratio to avoid wasting too much vertical space when + // showing photos. + const int minWidth = iconSize * 3 / 2; + itemWidth = qMax(itemWidth, minWidth); + } + + if (itemWidth < iconSize + padding * 2) { + itemWidth = iconSize + padding * 2; + } + + itemHeight = padding * 3 + iconSize + option.fontMetrics.lineSpacing(); + + horizontalMargin = 4; + verticalMargin = 8; + maxTextLines = IconsModeSettings::maximumTextLines(); + break; + } + case KFileItemListView::CompactLayout: { + itemWidth = padding * 4 + iconSize + option.fontMetrics.height() * 5; + const int textLinesCount = visibleRoles().count(); + itemHeight = padding * 2 + qMax(iconSize, textLinesCount * option.fontMetrics.lineSpacing()); + + if (CompactModeSettings::maximumTextWidthIndex() > 0) { + // A restriction is given for the maximum width of the text (0 means + // having no restriction) + maxTextWidth = option.fontMetrics.height() * 10 * CompactModeSettings::maximumTextWidthIndex(); + } + + horizontalMargin = 8; + break; + } + case KFileItemListView::DetailsLayout: { + itemWidth = -1; + itemHeight = padding * 2 + qMax(iconSize, option.fontMetrics.lineSpacing()); + break; + } + default: + itemWidth = -1; + itemHeight = -1; + Q_ASSERT(false); + break; + } + + // Apply the calculated values + option.padding = padding; + option.horizontalMargin = horizontalMargin; + option.verticalMargin = verticalMargin; + option.iconSize = iconSize; + option.maxTextLines = maxTextLines; + option.maxTextWidth = maxTextWidth; + beginTransaction(); + setStyleOption(option); + setItemSize(QSizeF(itemWidth, itemHeight)); + endTransaction(); +} + +ViewModeSettings::ViewMode DolphinItemListView::viewMode() const +{ + ViewModeSettings::ViewMode mode; + + switch (itemLayout()) { + case KFileItemListView::IconsLayout: mode = ViewModeSettings::IconsMode; break; + case KFileItemListView::CompactLayout: mode = ViewModeSettings::CompactMode; break; + case KFileItemListView::DetailsLayout: mode = ViewModeSettings::DetailsMode; break; + default: mode = ViewModeSettings::IconsMode; + Q_ASSERT(false); + break; + } + + return mode; +} + +#include "moc_dolphinitemlistview.cpp" diff --git a/dolphin/src/views/dolphinitemlistview.h b/dolphin/src/views/dolphinitemlistview.h new file mode 100644 index 00000000..40a1dc3d --- /dev/null +++ b/dolphin/src/views/dolphinitemlistview.h @@ -0,0 +1,70 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef DOLPHINITEMLISTVIEW_H +#define DOLPHINITEMLISTVIEW_H + +#include +#include + +#include + +class KFileItemListView; + +/** + * @brief Dolphin specific view-implementation. + * + * Offers zoom-level support and takes care for translating + * the view-properties into the corresponding KItemListView + * properties. + */ +class DOLPHINPRIVATE_EXPORT DolphinItemListView : public KFileItemListView +{ + Q_OBJECT + +public: + explicit DolphinItemListView(QGraphicsWidget* parent = 0); + virtual ~DolphinItemListView(); + + void setZoomLevel(int level); + int zoomLevel() const; + + void readSettings(); + void writeSettings(); + +protected: + virtual KItemListWidgetCreatorBase* defaultWidgetCreator() const; + virtual bool itemLayoutSupportsItemExpanding(ItemLayout layout) const; + virtual void onItemLayoutChanged(ItemLayout current, ItemLayout previous); + virtual void onPreviewsShownChanged(bool shown); + virtual void onVisibleRolesChanged(const QList& current, + const QList& previous); + + virtual void updateFont(); + +private: + void updateGridSize(); + + ViewModeSettings::ViewMode viewMode() const; + +private: + int m_zoomLevel; +}; + +#endif diff --git a/dolphin/src/views/dolphinnewfilemenuobserver.cpp b/dolphin/src/views/dolphinnewfilemenuobserver.cpp new file mode 100644 index 00000000..af3671d5 --- /dev/null +++ b/dolphin/src/views/dolphinnewfilemenuobserver.cpp @@ -0,0 +1,66 @@ +/*************************************************************************** + * Copyright (C) 2009 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "dolphinnewfilemenuobserver.h" + +#include +#include "dolphinnewfilemenu.h" + +class DolphinNewFileMenuObserverSingleton +{ +public: + DolphinNewFileMenuObserver instance; +}; +K_GLOBAL_STATIC(DolphinNewFileMenuObserverSingleton, s_DolphinNewFileMenuObserver) + +DolphinNewFileMenuObserver& DolphinNewFileMenuObserver::instance() +{ + return s_DolphinNewFileMenuObserver->instance; +} + +void DolphinNewFileMenuObserver::attach(const DolphinNewFileMenu* menu) +{ + connect(menu, SIGNAL(fileCreated(KUrl)), + this, SIGNAL(itemCreated(KUrl))); + connect(menu, SIGNAL(directoryCreated(KUrl)), + this, SIGNAL(itemCreated(KUrl))); + connect(menu, SIGNAL(errorMessage(QString)), + this, SIGNAL(errorMessage(QString))); +} + +void DolphinNewFileMenuObserver::detach(const DolphinNewFileMenu* menu) +{ + disconnect(menu, SIGNAL(fileCreated(KUrl)), + this, SIGNAL(itemCreated(KUrl))); + disconnect(menu, SIGNAL(directoryCreated(KUrl)), + this, SIGNAL(itemCreated(KUrl))); + disconnect(menu, SIGNAL(errorMessage(QString)), + this, SIGNAL(errorMessage(QString))); +} + +DolphinNewFileMenuObserver::DolphinNewFileMenuObserver() : + QObject(0) +{ +} + +DolphinNewFileMenuObserver::~DolphinNewFileMenuObserver() +{ +} + +#include "moc_dolphinnewfilemenuobserver.cpp" diff --git a/dolphin/src/views/dolphinnewfilemenuobserver.h b/dolphin/src/views/dolphinnewfilemenuobserver.h new file mode 100644 index 00000000..fd81a50b --- /dev/null +++ b/dolphin/src/views/dolphinnewfilemenuobserver.h @@ -0,0 +1,57 @@ +/*************************************************************************** + * Copyright (C) 2009 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef DOLPHINNEWFILEMENUOBSERVER_H +#define DOLPHINNEWFILEMENUOBSERVER_H + +#include + +#include "dolphinprivate_export.h" + +class DolphinNewFileMenu; +class KUrl; + +/** + * @brief Allows to observe new file items that have been created + * by a DolphinNewFileMenu instance. + * + * As soon as a DolphinNewFileMenu instance created a new item, + * the observer will emit the signal itemCreated(). + */ +class DOLPHINPRIVATE_EXPORT DolphinNewFileMenuObserver : public QObject +{ + Q_OBJECT + +public: + static DolphinNewFileMenuObserver& instance(); + void attach(const DolphinNewFileMenu* menu); + void detach(const DolphinNewFileMenu* menu); + +signals: + void itemCreated(const KUrl& url); + void errorMessage(const QString& error); + +private: + DolphinNewFileMenuObserver(); + virtual ~DolphinNewFileMenuObserver(); + + friend class DolphinNewFileMenuObserverSingleton; +}; + +#endif diff --git a/dolphin/src/views/dolphinremoteencoding.cpp b/dolphin/src/views/dolphinremoteencoding.cpp new file mode 100644 index 00000000..699b9309 --- /dev/null +++ b/dolphin/src/views/dolphinremoteencoding.cpp @@ -0,0 +1,231 @@ +/*************************************************************************** + * Copyright (C) 2009 by Rahman Duran * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + + /* + * This code is largely based on the kremoteencodingplugin + * Copyright (c) 2003 Thiago Macieira + * Distributed under the same terms. + */ + +#include "dolphinremoteencoding.h" +#include "dolphinviewactionhandler.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DATA_KEY QLatin1String("Charset") + +DolphinRemoteEncoding::DolphinRemoteEncoding(QObject* parent, DolphinViewActionHandler* actionHandler) + :QObject(parent), + m_actionHandler(actionHandler), + m_loaded(false) +{ + m_menu = new KActionMenu(KIcon("character-set"), i18n("Select Remote Charset"), this); + m_actionHandler->actionCollection()->addAction("change_remote_encoding", m_menu); + connect(m_menu->menu(), SIGNAL(aboutToShow()), + this, SLOT(slotAboutToShow())); + + m_menu->setEnabled(false); + m_menu->setDelayed(false); +} + +DolphinRemoteEncoding::~DolphinRemoteEncoding() +{ +} + +void DolphinRemoteEncoding::slotReload() +{ + loadSettings(); +} + +void DolphinRemoteEncoding::loadSettings() +{ + m_loaded = true; + + fillMenu(); +} + +void DolphinRemoteEncoding::slotAboutToOpenUrl() +{ + KUrl oldURL = m_currentURL; + m_currentURL = m_actionHandler->currentView()->url(); + + if (m_currentURL.protocol() != oldURL.protocol()) { + // This plugin works on ftp, fish, etc. + // everything whose type is T_FILESYSTEM except for local files + if (!m_currentURL.isLocalFile() && + KProtocolManager::outputType(m_currentURL) == KProtocolInfo::T_FILESYSTEM) { + + m_menu->setEnabled(true); + loadSettings(); + } else { + m_menu->setEnabled(false); + } + return; + } + + if (m_currentURL.host() != oldURL.host()) { + updateMenu(); + } +} + +void DolphinRemoteEncoding::fillMenu() +{ + KMenu* menu = m_menu->menu(); + menu->clear(); + +#warning TODO: split into sub-menus based on script + foreach (const QString &description, KGlobal::charsets()->descriptiveEncodingNames()) { + QAction* action = new QAction(description, this); + action->setCheckable(true); + action->setData(KGlobal::charsets()->encodingForName(description)); + menu->addAction(action); + } + menu->addSeparator(); + + menu->addAction(i18n("Reload"), this, SLOT(slotReload()), 0); + menu->addAction(i18n("Default"), this, SLOT(slotDefault()), 0)->setCheckable(true); + + connect(menu, SIGNAL(triggered(QAction*)), this, SLOT(slotItemSelected(QAction*))); +} + +void DolphinRemoteEncoding::updateMenu() +{ + if (!m_loaded) { + loadSettings(); + } + + const QList menuActions = m_menu->menu()->actions(); + // uncheck everything + foreach (QAction *action, menuActions) { + action->setChecked(false); + } + + const QString charset = KProtocolManager::charsetFor(m_currentURL);; + if (!charset.isEmpty()) { + bool isFound = false; + foreach (QAction* action, menuActions) { + if (action->data().toString() == charset) { + isFound = true; + action->setChecked(true); + break; + } + } + + kDebug() << "URL=" << m_currentURL << " charset=" << charset; + + if (!isFound) { + kWarning() << "could not find entry for charset=" << charset ; + } + } else { + m_menu->menu()->actions().last()->setChecked(true); + } + +} + +void DolphinRemoteEncoding::slotAboutToShow() +{ + if (!m_loaded) { + loadSettings(); + } + updateMenu(); +} + +void DolphinRemoteEncoding::slotItemSelected(QAction* action) +{ + if (action) { + if (action->isChecked()) { + KConfig config(("kio_" + m_currentURL.protocol() + "rc").toLatin1()); + QString host = m_currentURL.host(); + QString charset = action->data().toString(); + KConfigGroup cg(&config, host); + cg.writeEntry(DATA_KEY, charset); + config.sync(); + + // Update the io-slaves... + updateView(); + } + } +} + +void DolphinRemoteEncoding::slotDefault() +{ + // We have no choice but delete all higher domain level + // settings here since it affects what will be matched. + KConfig config(("kio_" + m_currentURL.protocol() + "rc").toLatin1()); + + QStringList partList = m_currentURL.host().split('.', QString::SkipEmptyParts); + if (!partList.isEmpty()) { + partList.erase(partList.begin()); + + QStringList domains; + // Remove the exact name match... + domains << m_currentURL.host(); + + while (!partList.isEmpty()) { + if (partList.count() == 2) { + if (partList[0].length() <= 2 && partList[1].length() == 2) { + break; + } + } + + if (partList.count() == 1) { + break; + } + + domains << partList.join("."); + partList.erase(partList.begin()); + } + + for (QStringList::const_iterator it = domains.constBegin(); it != domains.constEnd();++it) { + kDebug() << "Domain to remove: " << *it; + if (config.hasGroup(*it)) { + config.deleteGroup(*it); + } else if (config.group("").hasKey(*it)) { + config.group("").deleteEntry(*it); //don't know what group name is supposed to be XXX + } + } + } + config.sync(); + + // Update the io-slaves. + updateView(); +} + +void DolphinRemoteEncoding::updateView() +{ + KIO::Scheduler::emitReparseSlaveConfiguration(); + // Reload the page with the new charset + m_actionHandler->currentView()->setUrl(m_currentURL); + m_actionHandler->currentView()->reload(); +} + +#include "moc_dolphinremoteencoding.cpp" diff --git a/dolphin/src/views/dolphinremoteencoding.h b/dolphin/src/views/dolphinremoteencoding.h new file mode 100644 index 00000000..d432fbca --- /dev/null +++ b/dolphin/src/views/dolphinremoteencoding.h @@ -0,0 +1,67 @@ +/*************************************************************************** + * Copyright (C) 2009 by Rahman Duran * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef DOLPHINREMOTEENCODING_H +#define DOLPHINREMOTEENCODING_H + +#include +#include +#include +#include "dolphinprivate_export.h" + + +class KActionMenu; +class DolphinViewActionHandler; + +/** + * @brief Allows to change character encoding for remote urls like ftp. + * + * When browsing remote url, its possible to change encoding from Tools Menu. + */ + +class DOLPHINPRIVATE_EXPORT DolphinRemoteEncoding: public QObject +{ + Q_OBJECT +public: + DolphinRemoteEncoding(QObject* parent, DolphinViewActionHandler* actionHandler); + ~DolphinRemoteEncoding(); + +public Q_SLOTS: + void slotAboutToOpenUrl(); + void slotItemSelected(QAction* action); + void slotReload(); + void slotDefault(); + +private Q_SLOTS: + void slotAboutToShow(); + +private: + void updateView(); + void loadSettings(); + void fillMenu(); + void updateMenu(); + + KActionMenu* m_menu; + KUrl m_currentURL; + DolphinViewActionHandler* m_actionHandler; + + bool m_loaded; +}; + +#endif diff --git a/dolphin/src/views/dolphinview.cpp b/dolphin/src/views/dolphinview.cpp new file mode 100644 index 00000000..9c19fb61 --- /dev/null +++ b/dolphin/src/views/dolphinview.cpp @@ -0,0 +1,1650 @@ +/*************************************************************************** + * Copyright (C) 2006-2009 by Peter Penz * + * Copyright (C) 2006 by Gregor Kališnik * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "dolphinview.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dolphinnewfilemenuobserver.h" +#include "dolphin_detailsmodesettings.h" +#include "dolphin_generalsettings.h" +#include "dolphinitemlistview.h" +#include "draganddrophelper.h" +#include "renamedialog.h" +#include "versioncontrol/versioncontrolobserver.h" +#include "viewmodecontroller.h" +#include "viewproperties.h" +#include "views/tooltips/tooltipmanager.h" +#include "zoomlevelinfo.h" + +DolphinView::DolphinView(const KUrl& url, QWidget* parent) : + QWidget(parent), + m_active(true), + m_tabsForFiles(false), + m_assureVisibleCurrentIndex(false), + m_isFolderWritable(true), + m_dragging(false), + m_url(url), + m_viewPropertiesContext(), + m_mode(DolphinView::IconsView), + m_visibleRoles(), + m_topLayout(0), + m_model(0), + m_view(0), + m_container(0), + m_toolTipManager(0), + m_selectionChangedTimer(0), + m_currentItemUrl(), + m_scrollToCurrentItem(false), + m_restoredContentsPosition(), + m_selectedUrls(), + m_clearSelectionBeforeSelectingNewItems(false), + m_markFirstNewlySelectedItemAsCurrent(false), + m_versionControlObserver(0) +{ + m_topLayout = new QVBoxLayout(this); + m_topLayout->setSpacing(0); + m_topLayout->setMargin(0); + + // When a new item has been created by the "Create New..." menu, the item should + // get selected and it must be assured that the item will get visible. As the + // creation is done asynchronously, several signals must be checked: + connect(&DolphinNewFileMenuObserver::instance(), SIGNAL(itemCreated(KUrl)), + this, SLOT(observeCreatedItem(KUrl))); + + m_selectionChangedTimer = new QTimer(this); + m_selectionChangedTimer->setSingleShot(true); + m_selectionChangedTimer->setInterval(300); + connect(m_selectionChangedTimer, SIGNAL(timeout()), + this, SLOT(emitSelectionChangedSignal())); + + m_model = new KFileItemModel(this); + m_view = new DolphinItemListView(); + m_view->setEnabledSelectionToggles(GeneralSettings::showSelectionToggle()); + m_view->setVisibleRoles(QList() << "text"); + applyModeToView(); + + KItemListController* controller = new KItemListController(m_model, m_view, this); + const int delay = GeneralSettings::autoExpandFolders() ? 750 : -1; + controller->setAutoActivationDelay(delay); + + m_container = new KItemListContainer(controller, this); + m_container->installEventFilter(this); + setFocusProxy(m_container); + connect(m_container->horizontalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(hideToolTip())); + connect(m_container->verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(hideToolTip())); + + controller->setSelectionBehavior(KItemListController::MultiSelection); + connect(controller, SIGNAL(itemActivated(int)), this, SLOT(slotItemActivated(int))); + connect(controller, SIGNAL(itemsActivated(KItemSet)), this, SLOT(slotItemsActivated(KItemSet))); + connect(controller, SIGNAL(itemMiddleClicked(int)), this, SLOT(slotItemMiddleClicked(int))); + connect(controller, SIGNAL(itemContextMenuRequested(int,QPointF)), this, SLOT(slotItemContextMenuRequested(int,QPointF))); + connect(controller, SIGNAL(viewContextMenuRequested(QPointF)), this, SLOT(slotViewContextMenuRequested(QPointF))); + connect(controller, SIGNAL(headerContextMenuRequested(QPointF)), this, SLOT(slotHeaderContextMenuRequested(QPointF))); + connect(controller, SIGNAL(mouseButtonPressed(int,Qt::MouseButtons)), this, SLOT(slotMouseButtonPressed(int,Qt::MouseButtons))); + connect(controller, SIGNAL(itemHovered(int)), this, SLOT(slotItemHovered(int))); + connect(controller, SIGNAL(itemUnhovered(int)), this, SLOT(slotItemUnhovered(int))); + connect(controller, SIGNAL(itemDropEvent(int,QGraphicsSceneDragDropEvent*)), this, SLOT(slotItemDropEvent(int,QGraphicsSceneDragDropEvent*))); + connect(controller, SIGNAL(escapePressed()), this, SLOT(stopLoading())); + connect(controller, SIGNAL(modelChanged(KItemModelBase*,KItemModelBase*)), this, SLOT(slotModelChanged(KItemModelBase*,KItemModelBase*))); + + connect(m_model, SIGNAL(directoryLoadingStarted()), this, SLOT(slotDirectoryLoadingStarted())); + connect(m_model, SIGNAL(directoryLoadingCompleted()), this, SLOT(slotDirectoryLoadingCompleted())); + connect(m_model, SIGNAL(directoryLoadingCanceled()), this, SIGNAL(directoryLoadingCanceled())); + connect(m_model, SIGNAL(directoryLoadingProgress(int)), this, SIGNAL(directoryLoadingProgress(int))); + connect(m_model, SIGNAL(directorySortingProgress(int)), this, SIGNAL(directorySortingProgress(int))); + connect(m_model, SIGNAL(itemsChanged(KItemRangeList,QSet)), + this, SLOT(slotItemsChanged())); + connect(m_model, SIGNAL(itemsRemoved(KItemRangeList)), this, SIGNAL(itemCountChanged())); + connect(m_model, SIGNAL(itemsInserted(KItemRangeList)), this, SIGNAL(itemCountChanged())); + connect(m_model, SIGNAL(infoMessage(QString)), this, SIGNAL(infoMessage(QString))); + connect(m_model, SIGNAL(errorMessage(QString)), this, SIGNAL(errorMessage(QString))); + connect(m_model, SIGNAL(directoryRedirection(KUrl,KUrl)), this, SLOT(slotDirectoryRedirection(KUrl,KUrl))); + connect(m_model, SIGNAL(urlIsFileError(KUrl)), this, SIGNAL(urlIsFileError(KUrl))); + + m_view->installEventFilter(this); + connect(m_view, SIGNAL(sortOrderChanged(Qt::SortOrder,Qt::SortOrder)), + this, SLOT(slotSortOrderChangedByHeader(Qt::SortOrder,Qt::SortOrder))); + connect(m_view, SIGNAL(sortRoleChanged(QByteArray,QByteArray)), + this, SLOT(slotSortRoleChangedByHeader(QByteArray,QByteArray))); + connect(m_view, SIGNAL(visibleRolesChanged(QList,QList)), + this, SLOT(slotVisibleRolesChangedByHeader(QList,QList))); + connect(m_view, SIGNAL(roleEditingCanceled(int,QByteArray,QVariant)), + this, SLOT(slotRoleEditingCanceled())); + connect(m_view->header(), SIGNAL(columnWidthChanged(QByteArray,qreal,qreal)), + this, SLOT(slotHeaderColumnWidthChanged(QByteArray,qreal,qreal))); + + KItemListSelectionManager* selectionManager = controller->selectionManager(); + connect(selectionManager, SIGNAL(selectionChanged(KItemSet,KItemSet)), + this, SLOT(slotSelectionChanged(KItemSet,KItemSet))); + + m_toolTipManager = new ToolTipManager(this); + + m_versionControlObserver = new VersionControlObserver(this); + m_versionControlObserver->setModel(m_model); + connect(m_versionControlObserver, SIGNAL(infoMessage(QString)), this, SIGNAL(infoMessage(QString))); + connect(m_versionControlObserver, SIGNAL(errorMessage(QString)), this, SIGNAL(errorMessage(QString))); + connect(m_versionControlObserver, SIGNAL(operationCompletedMessage(QString)), this, SIGNAL(operationCompletedMessage(QString))); + + applyViewProperties(); + m_topLayout->addWidget(m_container); + + loadDirectory(url); +} + +DolphinView::~DolphinView() +{ +} + +KUrl DolphinView::url() const +{ + return m_url; +} + +void DolphinView::setActive(bool active) +{ + if (active == m_active) { + return; + } + + m_active = active; + + QColor color = KColorScheme(QPalette::Active, KColorScheme::View).background().color(); + if (!active) { + color.setAlpha(150); + } + + QWidget* viewport = m_container->viewport(); + if (viewport) { + QPalette palette; + palette.setColor(viewport->backgroundRole(), color); + viewport->setPalette(palette); + } + + update(); + + if (active) { + m_container->setFocus(); + emit activated(); + emit writeStateChanged(m_isFolderWritable); + } +} + +bool DolphinView::isActive() const +{ + return m_active; +} + +void DolphinView::setMode(Mode mode) +{ + if (mode != m_mode) { + ViewProperties props(viewPropertiesUrl()); + props.setViewMode(mode); + + // We pass the new ViewProperties to applyViewProperties, rather than + // storing them on disk and letting applyViewProperties() read them + // from there, to prevent that changing the view mode fails if the + // .directory file is not writable (see bug 318534). + applyViewProperties(props); + } +} + +DolphinView::Mode DolphinView::mode() const +{ + return m_mode; +} + +void DolphinView::setPreviewsShown(bool show) +{ + if (previewsShown() == show) { + return; + } + + ViewProperties props(viewPropertiesUrl()); + props.setPreviewsShown(show); + + const int oldZoomLevel = m_view->zoomLevel(); + m_view->setPreviewsShown(show); + emit previewsShownChanged(show); + + const int newZoomLevel = m_view->zoomLevel(); + if (newZoomLevel != oldZoomLevel) { + emit zoomLevelChanged(newZoomLevel, oldZoomLevel); + } +} + +bool DolphinView::previewsShown() const +{ + return m_view->previewsShown(); +} + +void DolphinView::setHiddenFilesShown(bool show) +{ + if (m_model->showHiddenFiles() == show) { + return; + } + + const KFileItemList itemList = selectedItems(); + m_selectedUrls.clear(); + m_selectedUrls = itemList.urlList(); + + ViewProperties props(viewPropertiesUrl()); + props.setHiddenFilesShown(show); + + m_model->setShowHiddenFiles(show); + emit hiddenFilesShownChanged(show); +} + +bool DolphinView::hiddenFilesShown() const +{ + return m_model->showHiddenFiles(); +} + +void DolphinView::setGroupedSorting(bool grouped) +{ + if (grouped == groupedSorting()) { + return; + } + + ViewProperties props(viewPropertiesUrl()); + props.setGroupedSorting(grouped); + props.save(); + + m_container->controller()->model()->setGroupedSorting(grouped); + + emit groupedSortingChanged(grouped); +} + +bool DolphinView::groupedSorting() const +{ + return m_model->groupedSorting(); +} + +KFileItemList DolphinView::items() const +{ + KFileItemList list; + const int itemCount = m_model->count(); + list.reserve(itemCount); + + for (int i = 0; i < itemCount; ++i) { + list.append(m_model->fileItem(i)); + } + + return list; +} + +int DolphinView::itemsCount() const +{ + return m_model->count(); +} + +KFileItemList DolphinView::selectedItems() const +{ + const KItemListSelectionManager* selectionManager = m_container->controller()->selectionManager(); + + KFileItemList selectedItems; + foreach (int index, selectionManager->selectedItems()) { + selectedItems.append(m_model->fileItem(index)); + } + return selectedItems; +} + +int DolphinView::selectedItemsCount() const +{ + const KItemListSelectionManager* selectionManager = m_container->controller()->selectionManager(); + return selectionManager->selectedItems().count(); +} + +void DolphinView::markUrlsAsSelected(const QList& urls) +{ + m_selectedUrls = urls; +} + +void DolphinView::markUrlAsCurrent(const KUrl& url) +{ + m_currentItemUrl = url; + m_scrollToCurrentItem = true; +} + +void DolphinView::selectItems(const QRegExp& pattern, bool enabled) +{ + const KItemListSelectionManager::SelectionMode mode = enabled + ? KItemListSelectionManager::Select + : KItemListSelectionManager::Deselect; + KItemListSelectionManager* selectionManager = m_container->controller()->selectionManager(); + + for (int index = 0; index < m_model->count(); index++) { + const KFileItem item = m_model->fileItem(index); + if (pattern.exactMatch(item.text())) { + // An alternative approach would be to store the matching items in a KItemSet and + // select them in one go after the loop, but we'd need a new function + // KItemListSelectionManager::setSelected(KItemSet, SelectionMode mode) + // for that. + selectionManager->setSelected(index, 1, mode); + } + } +} + +void DolphinView::setZoomLevel(int level) +{ + const int oldZoomLevel = zoomLevel(); + m_view->setZoomLevel(level); + if (zoomLevel() != oldZoomLevel) { + hideToolTip(); + emit zoomLevelChanged(zoomLevel(), oldZoomLevel); + } +} + +int DolphinView::zoomLevel() const +{ + return m_view->zoomLevel(); +} + +void DolphinView::setSortRole(const QByteArray& role) +{ + if (role != sortRole()) { + updateSortRole(role); + } +} + +QByteArray DolphinView::sortRole() const +{ + const KItemModelBase* model = m_container->controller()->model(); + return model->sortRole(); +} + +void DolphinView::setSortOrder(Qt::SortOrder order) +{ + if (sortOrder() != order) { + updateSortOrder(order); + } +} + +Qt::SortOrder DolphinView::sortOrder() const +{ + return m_model->sortOrder(); +} + +void DolphinView::setSortFoldersFirst(bool foldersFirst) +{ + if (sortFoldersFirst() != foldersFirst) { + updateSortFoldersFirst(foldersFirst); + } +} + +bool DolphinView::sortFoldersFirst() const +{ + return m_model->sortDirectoriesFirst(); +} + +void DolphinView::setVisibleRoles(const QList& roles) +{ + const QList previousRoles = roles; + + ViewProperties props(viewPropertiesUrl()); + props.setVisibleRoles(roles); + + m_visibleRoles = roles; + m_view->setVisibleRoles(roles); + + emit visibleRolesChanged(m_visibleRoles, previousRoles); +} + +QList DolphinView::visibleRoles() const +{ + return m_visibleRoles; +} + +void DolphinView::reload() +{ + QByteArray viewState; + QDataStream saveStream(&viewState, QIODevice::WriteOnly); + saveState(saveStream); + + const KFileItemList itemList = selectedItems(); + m_selectedUrls.clear(); + m_selectedUrls = itemList.urlList(); + + setUrl(url()); + loadDirectory(url(), true); + + QDataStream restoreStream(viewState); + restoreState(restoreStream); +} + +void DolphinView::readSettings() +{ + const int oldZoomLevel = m_view->zoomLevel(); + + GeneralSettings::self()->readConfig(); + m_view->readSettings(); + applyViewProperties(); + + const int delay = GeneralSettings::autoExpandFolders() ? 750 : -1; + m_container->controller()->setAutoActivationDelay(delay); + + const int newZoomLevel = m_view->zoomLevel(); + if (newZoomLevel != oldZoomLevel) { + emit zoomLevelChanged(newZoomLevel, oldZoomLevel); + } +} + +void DolphinView::writeSettings() +{ + GeneralSettings::self()->writeConfig(); + m_view->writeSettings(); +} + +void DolphinView::setNameFilter(const QString& nameFilter) +{ + m_model->setNameFilter(nameFilter); +} + +QString DolphinView::nameFilter() const +{ + return m_model->nameFilter(); +} + +void DolphinView::setMimeTypeFilters(const QStringList& filters) +{ + return m_model->setMimeTypeFilters(filters); +} + +QStringList DolphinView::mimeTypeFilters() const +{ + return m_model->mimeTypeFilters(); +} + +QString DolphinView::statusBarText() const +{ + QString summary; + QString foldersText; + QString filesText; + + int folderCount = 0; + int fileCount = 0; + KIO::filesize_t totalFileSize = 0; + + if (m_container->controller()->selectionManager()->hasSelection()) { + // Give a summary of the status of the selected files + const KFileItemList list = selectedItems(); + foreach (const KFileItem& item, list) { + if (item.isDir()) { + ++folderCount; + } else { + ++fileCount; + totalFileSize += item.size(); + } + } + + if (folderCount + fileCount == 1) { + // If only one item is selected, show info about it + return list.first().getStatusBarInfo(); + } else { + // At least 2 items are selected + foldersText = i18ncp("@info:status", "1 Folder selected", "%1 Folders selected", folderCount); + filesText = i18ncp("@info:status", "1 File selected", "%1 Files selected", fileCount); + } + } else { + calculateItemCount(fileCount, folderCount, totalFileSize); + foldersText = i18ncp("@info:status", "1 Folder", "%1 Folders", folderCount); + filesText = i18ncp("@info:status", "1 File", "%1 Files", fileCount); + } + + if (fileCount > 0 && folderCount > 0) { + summary = i18nc("@info:status folders, files (size)", "%1, %2 (%3)", + foldersText, filesText, + KGlobal::locale()->formatByteSize(totalFileSize)); + } else if (fileCount > 0) { + summary = i18nc("@info:status files (size)", "%1 (%2)", + filesText, + KGlobal::locale()->formatByteSize(totalFileSize)); + } else if (folderCount > 0) { + summary = foldersText; + } else { + summary = i18nc("@info:status", "0 Folders, 0 Files"); + } + + return summary; +} + +QList DolphinView::versionControlActions(const KFileItemList& items) const +{ + QList actions; + + if (items.isEmpty()) { + const KFileItem item = m_model->rootItem(); + if (!item.isNull()) { + actions = m_versionControlObserver->actions(KFileItemList() << item); + } + } else { + actions = m_versionControlObserver->actions(items); + } + + return actions; +} + +void DolphinView::setUrl(const KUrl& url) +{ + if (url == m_url) { + return; + } + + clearSelection(); + + emit urlAboutToBeChanged(url); + m_url = url; + + hideToolTip(); + + disconnect(m_view, SIGNAL(roleEditingFinished(int,QByteArray,QVariant)), + this, SLOT(slotRoleEditingFinished(int,QByteArray,QVariant))); + + // It is important to clear the items from the model before + // applying the view properties, otherwise expensive operations + // might be done on the existing items although they get cleared + // anyhow afterwards by loadDirectory(). + m_model->clear(); + applyViewProperties(); + loadDirectory(url); + + emit urlChanged(url); +} + +void DolphinView::selectAll() +{ + KItemListSelectionManager* selectionManager = m_container->controller()->selectionManager(); + selectionManager->setSelected(0, m_model->count()); +} + +void DolphinView::invertSelection() +{ + KItemListSelectionManager* selectionManager = m_container->controller()->selectionManager(); + selectionManager->setSelected(0, m_model->count(), KItemListSelectionManager::Toggle); +} + +void DolphinView::clearSelection() +{ + m_selectedUrls.clear(); + m_container->controller()->selectionManager()->clearSelection(); +} + +void DolphinView::renameSelectedItems() +{ + const KFileItemList items = selectedItems(); + if (items.isEmpty()) { + return; + } + + if (items.count() == 1 && GeneralSettings::renameInline()) { + const int index = m_model->index(items.first()); + m_view->editRole(index, "text"); + + hideToolTip(); + + connect(m_view, SIGNAL(roleEditingFinished(int,QByteArray,QVariant)), + this, SLOT(slotRoleEditingFinished(int,QByteArray,QVariant))); + } else { + RenameDialog* dialog = new RenameDialog(this, items); + dialog->setAttribute(Qt::WA_DeleteOnClose); + dialog->show(); + dialog->raise(); + dialog->activateWindow(); + } + + // Assure that the current index remains visible when KFileItemModel + // will notify the view about changed items (which might result in + // a changed sorting). + m_assureVisibleCurrentIndex = true; +} + +void DolphinView::trashSelectedItems() +{ + const KUrl::List list = simplifiedSelectedUrls(); + KonqOperations::del(this, KonqOperations::TRASH, list); +} + +void DolphinView::deleteSelectedItems() +{ + const KUrl::List list = simplifiedSelectedUrls(); + const bool del = KonqOperations::askDeleteConfirmation(list, + KonqOperations::DEL, + KonqOperations::DEFAULT_CONFIRMATION, + this); + + if (del) { + KIO::Job* job = KIO::del(list); + if (job->ui()) { + job->ui()->setWindow(this); + } + connect(job, SIGNAL(result(KJob*)), + this, SLOT(slotDeleteFileFinished(KJob*))); + } +} + +void DolphinView::cutSelectedItems() +{ + QMimeData* mimeData = selectionMimeData(); + KonqMimeData::addIsCutSelection(mimeData, true); + QApplication::clipboard()->setMimeData(mimeData); +} + +void DolphinView::copySelectedItems() +{ + QMimeData* mimeData = selectionMimeData(); + QApplication::clipboard()->setMimeData(mimeData); +} + +void DolphinView::paste() +{ + pasteToUrl(url()); +} + +void DolphinView::pasteIntoFolder() +{ + const KFileItemList items = selectedItems(); + if ((items.count() == 1) && items.first().isDir()) { + pasteToUrl(items.first().url()); + } +} + +void DolphinView::stopLoading() +{ + m_model->cancelDirectoryLoading(); +} + +bool DolphinView::eventFilter(QObject* watched, QEvent* event) +{ + switch (event->type()) { + case QEvent::FocusIn: + if (watched == m_container) { + setActive(true); + } + break; + + case QEvent::GraphicsSceneDragEnter: + if (watched == m_view) { + m_dragging = true; + } + break; + + case QEvent::GraphicsSceneDragLeave: + if (watched == m_view) { + m_dragging = false; + } + break; + + case QEvent::GraphicsSceneDrop: + if (watched == m_view) { + m_dragging = false; + } + default: + break; + } + + return QWidget::eventFilter(watched, event); +} + +void DolphinView::wheelEvent(QWheelEvent* event) +{ + if (event->modifiers().testFlag(Qt::ControlModifier)) { + const int numDegrees = event->delta() / 8; + const int numSteps = numDegrees / 15; + + setZoomLevel(zoomLevel() + numSteps); + event->accept(); + } else { + event->ignore(); + } +} + +void DolphinView::hideEvent(QHideEvent* event) +{ + hideToolTip(); + QWidget::hideEvent(event); +} + +bool DolphinView::event(QEvent* event) +{ + /* See Bug 297355 + * Dolphin leaves file preview tooltips open even when is not visible. + * + * Hide tool-tip when Dolphin loses focus. + */ + if (event->type() == QEvent::WindowDeactivate) { + hideToolTip(); + } + + return QWidget::event(event); +} + +void DolphinView::activate() +{ + setActive(true); +} + +void DolphinView::slotItemActivated(int index) +{ + const KFileItem item = m_model->fileItem(index); + if (!item.isNull()) { + emit itemActivated(item); + } +} + +void DolphinView::slotItemsActivated(const KItemSet& indexes) +{ + Q_ASSERT(indexes.count() >= 2); + + if (indexes.count() > 5) { + QString question = i18np("Are you sure you want to open 1 item?", "Are you sure you want to open %1 items?", indexes.count()); + const int answer = KMessageBox::warningYesNo(this, question); + if (answer != KMessageBox::Yes) { + return; + } + } + + KFileItemList items; + items.reserve(indexes.count()); + + foreach (int index, indexes) { + KFileItem item = m_model->fileItem(index); + const KUrl& url = openItemAsFolderUrl(item); + + if (!url.isEmpty()) { // Open folders in new tabs + emit tabRequested(url); + } else { + items.append(item); + } + } + + if (items.count() == 1) { + emit itemActivated(items.first()); + } else if (items.count() > 1) { + emit itemsActivated(items); + } +} + +void DolphinView::slotItemMiddleClicked(int index) +{ + const KFileItem& item = m_model->fileItem(index); + const KUrl& url = openItemAsFolderUrl(item); + if (!url.isEmpty()) { + emit tabRequested(url); + } else if (isTabsForFilesEnabled()) { + emit tabRequested(item.url()); + } +} + +void DolphinView::slotItemContextMenuRequested(int index, const QPointF& pos) +{ + // Force emit of a selection changed signal before we request the + // context menu, to update the edit-actions first. (See Bug 294013) + if (m_selectionChangedTimer->isActive()) { + emitSelectionChangedSignal(); + } + + const KFileItem item = m_model->fileItem(index); + emit requestContextMenu(pos.toPoint(), item, url(), QList()); +} + +void DolphinView::slotViewContextMenuRequested(const QPointF& pos) +{ + emit requestContextMenu(pos.toPoint(), KFileItem(), url(), QList()); +} + +void DolphinView::slotHeaderContextMenuRequested(const QPointF& pos) +{ + ViewProperties props(viewPropertiesUrl()); + + QPointer menu = new KMenu(QApplication::activeWindow()); + + KItemListView* view = m_container->controller()->view(); + const QSet visibleRolesSet = view->visibleRoles().toSet(); + + QString groupName; + QMenu* groupMenu = 0; + + // Add all roles to the menu that can be shown or hidden by the user + const QList rolesInfo = KFileItemModel::rolesInformation(); + foreach (const KFileItemModel::RoleInfo& info, rolesInfo) { + if (info.role == "text") { + // It should not be possible to hide the "text" role + continue; + } + + const QString text = m_model->roleDescription(info.role); + QAction* action = 0; + if (info.group.isEmpty()) { + action = menu->addAction(text); + } else { + if (!groupMenu || info.group != groupName) { + groupName = info.group; + groupMenu = menu->addMenu(groupName); + } + + action = groupMenu->addAction(text); + } + + action->setCheckable(true); + action->setChecked(visibleRolesSet.contains(info.role)); + action->setData(info.role); + action->setEnabled(true); + } + + menu->addSeparator(); + + QActionGroup* widthsGroup = new QActionGroup(menu); + const bool autoColumnWidths = props.headerColumnWidths().isEmpty(); + + QAction* autoAdjustWidthsAction = menu->addAction(i18nc("@action:inmenu", "Automatic Column Widths")); + autoAdjustWidthsAction->setCheckable(true); + autoAdjustWidthsAction->setChecked(autoColumnWidths); + autoAdjustWidthsAction->setActionGroup(widthsGroup); + + QAction* customWidthsAction = menu->addAction(i18nc("@action:inmenu", "Custom Column Widths")); + customWidthsAction->setCheckable(true); + customWidthsAction->setChecked(!autoColumnWidths); + customWidthsAction->setActionGroup(widthsGroup); + + QAction* action = menu->exec(pos.toPoint()); + if (menu && action) { + KItemListHeader* header = view->header(); + + if (action == autoAdjustWidthsAction) { + // Clear the column-widths from the viewproperties and turn on + // the automatic resizing of the columns + props.setHeaderColumnWidths(QList()); + header->setAutomaticColumnResizing(true); + } else if (action == customWidthsAction) { + // Apply the current column-widths as custom column-widths and turn + // off the automatic resizing of the columns + QList columnWidths; + foreach (const QByteArray& role, view->visibleRoles()) { + columnWidths.append(header->columnWidth(role)); + } + props.setHeaderColumnWidths(columnWidths); + header->setAutomaticColumnResizing(false); + } else { + // Show or hide the selected role + const QByteArray selectedRole = action->data().toByteArray(); + + QList visibleRoles = view->visibleRoles(); + if (action->isChecked()) { + visibleRoles.append(selectedRole); + } else { + visibleRoles.removeOne(selectedRole); + } + + view->setVisibleRoles(visibleRoles); + props.setVisibleRoles(visibleRoles); + + QList columnWidths; + if (!header->automaticColumnResizing()) { + foreach (const QByteArray& role, view->visibleRoles()) { + columnWidths.append(header->columnWidth(role)); + } + } + props.setHeaderColumnWidths(columnWidths); + } + } + + delete menu; +} + +void DolphinView::slotHeaderColumnWidthChanged(const QByteArray& role, qreal current, qreal previous) +{ + Q_UNUSED(previous); + + const QList visibleRoles = m_view->visibleRoles(); + + ViewProperties props(viewPropertiesUrl()); + QList columnWidths = props.headerColumnWidths(); + if (columnWidths.count() != visibleRoles.count()) { + columnWidths.clear(); + columnWidths.reserve(visibleRoles.count()); + const KItemListHeader* header = m_view->header(); + foreach (const QByteArray& role, visibleRoles) { + const int width = header->columnWidth(role); + columnWidths.append(width); + } + } + + const int roleIndex = visibleRoles.indexOf(role); + Q_ASSERT(roleIndex >= 0 && roleIndex < columnWidths.count()); + columnWidths[roleIndex] = current; + + props.setHeaderColumnWidths(columnWidths); +} + +void DolphinView::slotItemHovered(int index) +{ + const KFileItem item = m_model->fileItem(index); + + if (GeneralSettings::showToolTips() && !m_dragging) { + QRectF itemRect = m_container->controller()->view()->itemContextRect(index); + const QPoint pos = m_container->mapToGlobal(itemRect.topLeft().toPoint()); + itemRect.moveTo(pos); + + m_toolTipManager->showToolTip(item, itemRect); + } + + emit requestItemInfo(item); +} + +void DolphinView::slotItemUnhovered(int index) +{ + Q_UNUSED(index); + hideToolTip(); + emit requestItemInfo(KFileItem()); +} + +void DolphinView::slotItemDropEvent(int index, QGraphicsSceneDragDropEvent* event) +{ + KUrl destUrl; + KFileItem destItem = m_model->fileItem(index); + if (destItem.isNull() || (!destItem.isDir() && !destItem.isDesktopFile())) { + // Use the URL of the view as drop target if the item is no directory + // or desktop-file + destItem = m_model->rootItem(); + destUrl = url(); + } else { + // The item represents a directory or desktop-file + destUrl = destItem.url(); + } + + QDropEvent dropEvent(event->pos().toPoint(), + event->possibleActions(), + event->mimeData(), + event->buttons(), + event->modifiers()); + + QString error; + KonqOperations* op = DragAndDropHelper::dropUrls(destItem, destUrl, &dropEvent, error); + if (!error.isEmpty()) { + emit infoMessage(error); + } + + if (op && destUrl == url()) { + // Mark the dropped urls as selected. + m_clearSelectionBeforeSelectingNewItems = true; + m_markFirstNewlySelectedItemAsCurrent = true; + connect(op, SIGNAL(aboutToCreate(KUrl::List)), this, SLOT(slotAboutToCreate(KUrl::List))); + } + + setActive(true); +} + +void DolphinView::slotModelChanged(KItemModelBase* current, KItemModelBase* previous) +{ + if (previous != 0) { + disconnect(previous, SIGNAL(directoryLoadingCompleted()), this, SLOT(slotDirectoryLoadingCompleted())); + m_versionControlObserver->setModel(0); + } + + if (current) { + Q_ASSERT(qobject_cast(current)); + connect(current, SIGNAL(loadingCompleted()), this, SLOT(slotDirectoryLoadingCompleted())); + + KFileItemModel* fileItemModel = static_cast(current); + m_versionControlObserver->setModel(fileItemModel); + } +} + +void DolphinView::slotMouseButtonPressed(int itemIndex, Qt::MouseButtons buttons) +{ + Q_UNUSED(itemIndex); + Q_UNUSED(buttons); + + hideToolTip(); +} + +void DolphinView::slotAboutToCreate(const KUrl::List& urls) +{ + if (!urls.isEmpty()) { + if (m_markFirstNewlySelectedItemAsCurrent) { + markUrlAsCurrent(urls.first()); + m_markFirstNewlySelectedItemAsCurrent = false; + } + m_selectedUrls << KDirModel::simplifiedUrlList(urls); + } +} + +void DolphinView::slotSelectionChanged(const KItemSet& current, const KItemSet& previous) +{ + const int currentCount = current.count(); + const int previousCount = previous.count(); + const bool selectionStateChanged = (currentCount == 0 && previousCount > 0) || + (currentCount > 0 && previousCount == 0); + + // If nothing has been selected before and something got selected (or if something + // was selected before and now nothing is selected) the selectionChangedSignal must + // be emitted asynchronously as fast as possible to update the edit-actions. + m_selectionChangedTimer->setInterval(selectionStateChanged ? 0 : 300); + m_selectionChangedTimer->start(); +} + +void DolphinView::emitSelectionChangedSignal() +{ + m_selectionChangedTimer->stop(); + emit selectionChanged(selectedItems()); +} + +void DolphinView::updateSortRole(const QByteArray& role) +{ + ViewProperties props(viewPropertiesUrl()); + props.setSortRole(role); + + KItemModelBase* model = m_container->controller()->model(); + model->setSortRole(role); + + emit sortRoleChanged(role); +} + +void DolphinView::updateSortOrder(Qt::SortOrder order) +{ + ViewProperties props(viewPropertiesUrl()); + props.setSortOrder(order); + + m_model->setSortOrder(order); + + emit sortOrderChanged(order); +} + +void DolphinView::updateSortFoldersFirst(bool foldersFirst) +{ + ViewProperties props(viewPropertiesUrl()); + props.setSortFoldersFirst(foldersFirst); + + m_model->setSortDirectoriesFirst(foldersFirst); + + emit sortFoldersFirstChanged(foldersFirst); +} + +QPair DolphinView::pasteInfo() const +{ + return KonqOperations::pasteInfo(url()); +} + +void DolphinView::setTabsForFilesEnabled(bool tabsForFiles) +{ + m_tabsForFiles = tabsForFiles; +} + +bool DolphinView::isTabsForFilesEnabled() const +{ + return m_tabsForFiles; +} + +bool DolphinView::itemsExpandable() const +{ + return m_mode == DetailsView; +} + +void DolphinView::restoreState(QDataStream& stream) +{ + // Restore the current item that had the keyboard focus + stream >> m_currentItemUrl; + + // Restore the view position + stream >> m_restoredContentsPosition; + + // Restore expanded folders (only relevant for the details view - will be ignored by the view in other view modes) + QSet urls; + stream >> urls; + m_model->restoreExpandedDirectories(urls); +} + +void DolphinView::saveState(QDataStream& stream) +{ + // Save the current item that has the keyboard focus + const int currentIndex = m_container->controller()->selectionManager()->currentItem(); + if (currentIndex != -1) { + KFileItem item = m_model->fileItem(currentIndex); + Q_ASSERT(!item.isNull()); // If the current index is valid a item must exist + KUrl currentItemUrl = item.url(); + stream << currentItemUrl; + } else { + stream << KUrl(); + } + + // Save view position + const qreal x = m_container->horizontalScrollBar()->value(); + const qreal y = m_container->verticalScrollBar()->value(); + stream << QPoint(x, y); + + // Save expanded folders (only relevant for the details view - the set will be empty in other view modes) + stream << m_model->expandedDirectories(); +} + +KFileItem DolphinView::rootItem() const +{ + return m_model->rootItem(); +} + +void DolphinView::setViewPropertiesContext(const QString& context) +{ + m_viewPropertiesContext = context; +} + +QString DolphinView::viewPropertiesContext() const +{ + return m_viewPropertiesContext; +} + +KUrl DolphinView::openItemAsFolderUrl(const KFileItem& item, const bool browseThroughArchives) +{ + if (item.isNull()) { + return KUrl(); + } + + KUrl url = item.targetUrl(); + + if (item.isDir()) { + return url; + } + + if (item.isMimeTypeKnown()) { + const QString& mimetype = item.mimetype(); + + if (browseThroughArchives && item.isFile() && url.isLocalFile()) { + // Generic mechanism for redirecting to tar:// when clicking on a tar file, + // zip:// when clicking on a zip file, etc. + // The .protocol file specifies the mimetype that the kioslave handles. + // Note that we don't use mimetype inheritance since we don't want to + // open OpenDocument files as zip folders... + const QString& protocol = KProtocolManager::protocolForArchiveMimetype(mimetype); + if (!protocol.isEmpty()) { + url.setProtocol(protocol); + return url; + } + } + + if (mimetype == QLatin1String("application/x-desktop")) { + // Redirect to the URL in Type=Link desktop files, unless it is a http(s) URL. + KDesktopFile desktopFile(url.toLocalFile()); + if (desktopFile.hasLinkType()) { + const QString linkUrl = desktopFile.readUrl(); + if (!linkUrl.startsWith(QLatin1String("http"))) { + return linkUrl; + } + } + } + } + + return KUrl(); +} + +void DolphinView::observeCreatedItem(const KUrl& url) +{ + if (m_active) { + clearSelection(); + markUrlAsCurrent(url); + markUrlsAsSelected(QList() << url); + } +} + +void DolphinView::slotDirectoryRedirection(const KUrl& oldUrl, const KUrl& newUrl) +{ + if (oldUrl.equals(url(), KUrl::CompareWithoutTrailingSlash)) { + emit redirection(oldUrl, newUrl); + m_url = newUrl; // #186947 + } +} + +void DolphinView::updateViewState() +{ + if (m_currentItemUrl != KUrl()) { + KItemListSelectionManager* selectionManager = m_container->controller()->selectionManager(); + const int currentIndex = m_model->index(m_currentItemUrl); + if (currentIndex != -1) { + selectionManager->setCurrentItem(currentIndex); + + // scroll to current item and reset the state + if (m_scrollToCurrentItem) { + m_view->scrollToItem(currentIndex); + m_scrollToCurrentItem = false; + } + } else { + selectionManager->setCurrentItem(0); + } + + m_currentItemUrl = KUrl(); + } + + if (!m_restoredContentsPosition.isNull()) { + const int x = m_restoredContentsPosition.x(); + const int y = m_restoredContentsPosition.y(); + m_restoredContentsPosition = QPoint(); + + m_container->horizontalScrollBar()->setValue(x); + m_container->verticalScrollBar()->setValue(y); + } + + if (!m_selectedUrls.isEmpty()) { + KItemListSelectionManager* selectionManager = m_container->controller()->selectionManager(); + + if (m_clearSelectionBeforeSelectingNewItems) { + selectionManager->clearSelection(); + m_clearSelectionBeforeSelectingNewItems = false; + } + + KItemSet selectedItems = selectionManager->selectedItems(); + + QList::iterator it = m_selectedUrls.begin(); + while (it != m_selectedUrls.end()) { + const int index = m_model->index(*it); + if (index >= 0) { + selectedItems.insert(index); + it = m_selectedUrls.erase(it); + } else { + ++it; + } + } + + selectionManager->setSelectedItems(selectedItems); + } +} + +void DolphinView::hideToolTip() +{ + if (GeneralSettings::showToolTips()) { + m_toolTipManager->hideToolTip(); + } +} + +void DolphinView::calculateItemCount(int& fileCount, + int& folderCount, + KIO::filesize_t& totalFileSize) const +{ + const int itemCount = m_model->count(); + for (int i = 0; i < itemCount; ++i) { + const KFileItem item = m_model->fileItem(i); + if (item.isDir()) { + ++folderCount; + } else { + ++fileCount; + totalFileSize += item.size(); + } + } +} + +void DolphinView::slotDeleteFileFinished(KJob* job) +{ + if (job->error() == 0) { + emit operationCompletedMessage(i18nc("@info:status", "Delete operation completed.")); + } else if (job->error() != KIO::ERR_USER_CANCELED) { + emit errorMessage(job->errorString()); + } +} + +void DolphinView::slotRenamingFailed(const KUrl& oldUrl, const KUrl& newUrl) +{ + const int index = m_model->index(newUrl); + if (index >= 0) { + QHash data; + data.insert("text", oldUrl.fileName()); + m_model->setData(index, data); + } +} + +void DolphinView::slotDirectoryLoadingStarted() +{ + // Disable the writestate temporary until it can be determined in a fast way + // in DolphinView::slotLoadingCompleted() + if (m_isFolderWritable) { + m_isFolderWritable = false; + emit writeStateChanged(m_isFolderWritable); + } + + emit directoryLoadingStarted(); +} + +void DolphinView::slotDirectoryLoadingCompleted() +{ + // Update the view-state. This has to be done asynchronously + // because the view might not be in its final state yet. + QTimer::singleShot(0, this, SLOT(updateViewState())); + + emit directoryLoadingCompleted(); + + updateWritableState(); +} + +void DolphinView::slotItemsChanged() +{ + m_assureVisibleCurrentIndex = false; +} + +void DolphinView::slotSortOrderChangedByHeader(Qt::SortOrder current, Qt::SortOrder previous) +{ + Q_UNUSED(previous); + Q_ASSERT(m_model->sortOrder() == current); + + ViewProperties props(viewPropertiesUrl()); + props.setSortOrder(current); + + emit sortOrderChanged(current); +} + +void DolphinView::slotSortRoleChangedByHeader(const QByteArray& current, const QByteArray& previous) +{ + Q_UNUSED(previous); + Q_ASSERT(m_model->sortRole() == current); + + ViewProperties props(viewPropertiesUrl()); + props.setSortRole(current); + + emit sortRoleChanged(current); +} + +void DolphinView::slotVisibleRolesChangedByHeader(const QList& current, + const QList& previous) +{ + Q_UNUSED(previous); + Q_ASSERT(m_container->controller()->view()->visibleRoles() == current); + + const QList previousVisibleRoles = m_visibleRoles; + + m_visibleRoles = current; + + ViewProperties props(viewPropertiesUrl()); + props.setVisibleRoles(m_visibleRoles); + + emit visibleRolesChanged(m_visibleRoles, previousVisibleRoles); +} + +void DolphinView::slotRoleEditingCanceled() +{ + disconnect(m_view, SIGNAL(roleEditingFinished(int,QByteArray,QVariant)), + this, SLOT(slotRoleEditingFinished(int,QByteArray,QVariant))); +} + +void DolphinView::slotRoleEditingFinished(int index, const QByteArray& role, const QVariant& value) +{ + disconnect(m_view, SIGNAL(roleEditingFinished(int,QByteArray,QVariant)), + this, SLOT(slotRoleEditingFinished(int,QByteArray,QVariant))); + + if (index < 0 || index >= m_model->count()) { + return; + } + + if (role == "text") { + const KFileItem oldItem = m_model->fileItem(index); + const QString newName = value.toString(); + if (!newName.isEmpty() && newName != oldItem.text() && newName != QLatin1String(".") && newName != QLatin1String("..")) { + const KUrl oldUrl = oldItem.url(); + + const KUrl newUrl(url().path(KUrl::AddTrailingSlash) + newName); + const bool newNameExistsAlready = (m_model->index(newUrl) >= 0); + if (!newNameExistsAlready) { + // Only change the data in the model if no item with the new name + // is in the model yet. If there is an item with the new name + // already, calling KonqOperations::rename() will open a dialog + // asking for a new name, and KFileItemModel will update the + // data when the dir lister signals that the file name has changed. + QHash data; + data.insert(role, value); + m_model->setData(index, data); + } + + KonqOperations* op = KonqOperations::renameV2(this, oldUrl, newName); + if (op && !newNameExistsAlready) { + // Only connect the renamingFailed signal if there is no item with the new name + // in the model yet, see bug 328262. + connect(op, SIGNAL(renamingFailed(KUrl,KUrl)), SLOT(slotRenamingFailed(KUrl,KUrl))); + } + } + } +} + +void DolphinView::loadDirectory(const KUrl& url, bool reload) +{ + if (!url.isValid()) { + const QString location(url.pathOrUrl()); + if (location.isEmpty()) { + emit errorMessage(i18nc("@info:status", "The location is empty.")); + } else { + emit errorMessage(i18nc("@info:status", "The location '%1' is invalid.", location)); + } + return; + } + + if (reload) { + m_model->refreshDirectory(url); + } else { + m_model->loadDirectory(url); + } +} + +void DolphinView::applyViewProperties() +{ + const ViewProperties props(viewPropertiesUrl()); + applyViewProperties(props); +} + +void DolphinView::applyViewProperties(const ViewProperties& props) +{ + m_view->beginTransaction(); + + const Mode mode = props.viewMode(); + if (m_mode != mode) { + const Mode previousMode = m_mode; + m_mode = mode; + + // Changing the mode might result in changing + // the zoom level. Remember the old zoom level so + // that zoomLevelChanged() can get emitted. + const int oldZoomLevel = m_view->zoomLevel(); + applyModeToView(); + + emit modeChanged(m_mode, previousMode); + + if (m_view->zoomLevel() != oldZoomLevel) { + emit zoomLevelChanged(m_view->zoomLevel(), oldZoomLevel); + } + } + + const bool hiddenFilesShown = props.hiddenFilesShown(); + if (hiddenFilesShown != m_model->showHiddenFiles()) { + m_model->setShowHiddenFiles(hiddenFilesShown); + emit hiddenFilesShownChanged(hiddenFilesShown); + } + + const bool groupedSorting = props.groupedSorting(); + if (groupedSorting != m_model->groupedSorting()) { + m_model->setGroupedSorting(groupedSorting); + emit groupedSortingChanged(groupedSorting); + } + + const QByteArray sortRole = props.sortRole(); + if (sortRole != m_model->sortRole()) { + m_model->setSortRole(sortRole); + emit sortRoleChanged(sortRole); + } + + const Qt::SortOrder sortOrder = props.sortOrder(); + if (sortOrder != m_model->sortOrder()) { + m_model->setSortOrder(sortOrder); + emit sortOrderChanged(sortOrder); + } + + const bool sortFoldersFirst = props.sortFoldersFirst(); + if (sortFoldersFirst != m_model->sortDirectoriesFirst()) { + m_model->setSortDirectoriesFirst(sortFoldersFirst); + emit sortFoldersFirstChanged(sortFoldersFirst); + } + + const QList visibleRoles = props.visibleRoles(); + if (visibleRoles != m_visibleRoles) { + const QList previousVisibleRoles = m_visibleRoles; + m_visibleRoles = visibleRoles; + m_view->setVisibleRoles(visibleRoles); + emit visibleRolesChanged(m_visibleRoles, previousVisibleRoles); + } + + const bool previewsShown = props.previewsShown(); + if (previewsShown != m_view->previewsShown()) { + const int oldZoomLevel = zoomLevel(); + + m_view->setPreviewsShown(previewsShown); + emit previewsShownChanged(previewsShown); + + // Changing the preview-state might result in a changed zoom-level + if (oldZoomLevel != zoomLevel()) { + emit zoomLevelChanged(zoomLevel(), oldZoomLevel); + } + } + + KItemListView* itemListView = m_container->controller()->view(); + if (itemListView->isHeaderVisible()) { + KItemListHeader* header = itemListView->header(); + const QList headerColumnWidths = props.headerColumnWidths(); + const int rolesCount = m_visibleRoles.count(); + if (headerColumnWidths.count() == rolesCount) { + header->setAutomaticColumnResizing(false); + + QHash columnWidths; + for (int i = 0; i < rolesCount; ++i) { + columnWidths.insert(m_visibleRoles[i], headerColumnWidths[i]); + } + header->setColumnWidths(columnWidths); + } else { + header->setAutomaticColumnResizing(true); + } + } + + m_view->endTransaction(); +} + +void DolphinView::applyModeToView() +{ + switch (m_mode) { + case IconsView: m_view->setItemLayout(KFileItemListView::IconsLayout); break; + case CompactView: m_view->setItemLayout(KFileItemListView::CompactLayout); break; + case DetailsView: m_view->setItemLayout(KFileItemListView::DetailsLayout); break; + default: Q_ASSERT(false); break; + } +} + +void DolphinView::pasteToUrl(const KUrl& url) +{ + KonqOperations* op = KonqOperations::doPasteV2(this, url); + if (op) { + m_clearSelectionBeforeSelectingNewItems = true; + m_markFirstNewlySelectedItemAsCurrent = true; + connect(op, SIGNAL(aboutToCreate(KUrl::List)), this, SLOT(slotAboutToCreate(KUrl::List))); + } +} + +KUrl::List DolphinView::simplifiedSelectedUrls() const +{ + KUrl::List urls; + + const KFileItemList items = selectedItems(); + foreach (const KFileItem& item, items) { + urls.append(item.url()); + } + + if (itemsExpandable()) { + // TODO: Check if we still need KDirModel for this in KDE 5.0 + urls = KDirModel::simplifiedUrlList(urls); + } + + return urls; +} + +QMimeData* DolphinView::selectionMimeData() const +{ + const KItemListSelectionManager* selectionManager = m_container->controller()->selectionManager(); + const KItemSet selectedIndexes = selectionManager->selectedItems(); + + return m_model->createMimeData(selectedIndexes); +} + +void DolphinView::updateWritableState() +{ + const bool wasFolderWritable = m_isFolderWritable; + m_isFolderWritable = false; + + KFileItem item = m_model->rootItem(); + if (item.isNull()) { + // Try to find out if the URL is writable even if the "root item" is + // null, see https://bugs.kde.org/show_bug.cgi?id=330001 + item = KFileItem(KFileItem::Unknown, KFileItem::Unknown, url(), true); + } + + KFileItemListProperties capabilities(KFileItemList() << item); + m_isFolderWritable = capabilities.supportsWriting(); + + if (m_isFolderWritable != wasFolderWritable) { + emit writeStateChanged(m_isFolderWritable); + } +} + +KUrl DolphinView::viewPropertiesUrl() const +{ + if (m_viewPropertiesContext.isEmpty()) { + return m_url; + } + + KUrl url; + url.setProtocol(m_url.protocol()); + url.setPath(m_viewPropertiesContext); + return url; +} + +#include "moc_dolphinview.cpp" diff --git a/dolphin/src/views/dolphinview.h b/dolphin/src/views/dolphinview.h new file mode 100644 index 00000000..50636a26 --- /dev/null +++ b/dolphin/src/views/dolphinview.h @@ -0,0 +1,777 @@ +/*************************************************************************** + * Copyright (C) 2006-2009 by Peter Penz * + * Copyright (C) 2006 by Gregor Kališnik * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef DOLPHINVIEW_H +#define DOLPHINVIEW_H + +#include "dolphinprivate_export.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +typedef KIO::FileUndoManager::CommandType CommandType; + +class DolphinItemListView; +class KAction; +class KActionCollection; +class KFileItemModel; +class KItemListContainer; +class KItemModelBase; +class KItemSet; +class KUrl; +class ToolTipManager; +class VersionControlObserver; +class ViewProperties; + +/** + * @short Represents a view for the directory content. + * + * View modes for icons, compact and details are supported. It's + * possible to adjust: + * - sort order + * - sort type + * - show hidden files + * - show previews + * - enable grouping + */ +class DOLPHINPRIVATE_EXPORT DolphinView : public QWidget +{ + Q_OBJECT + +public: + /** + * Defines the view mode for a directory. The + * view mode is automatically updated if the directory itself + * defines a view mode (see class ViewProperties for details). + */ + enum Mode + { + /** + * The items are shown as icons with a name-label below. + */ + IconsView = 0, + + /** + * The icon, the name and the size of the items are + * shown per default as a table. + */ + DetailsView, + + /** + * The items are shown as icons with the name-label aligned + * to the right side. + */ + CompactView + }; + + /** + * @param url Specifies the content which should be shown. + * @param parent Parent widget of the view. + */ + DolphinView(const KUrl& url, QWidget* parent); + + virtual ~DolphinView(); + + /** + * Returns the current active URL, where all actions are applied. + * The URL navigator is synchronized with this URL. + */ + KUrl url() const; + + /** + * If \a active is true, the view will marked as active. The active + * view is defined as view where all actions are applied to. + */ + void setActive(bool active); + bool isActive() const; + + /** + * Changes the view mode for the current directory to \a mode. + * If the view properties should be remembered for each directory + * (GeneralSettings::globalViewProps() returns false), then the + * changed view mode will be stored automatically. + */ + void setMode(Mode mode); + Mode mode() const; + + /** + * Turns on the file preview for the all files of the current directory, + * if \a show is true. + * If the view properties should be remembered for each directory + * (GeneralSettings::globalViewProps() returns false), then the + * preview setting will be stored automatically. + */ + void setPreviewsShown(bool show); + bool previewsShown() const; + + /** + * Shows all hidden files of the current directory, + * if \a show is true. + * If the view properties should be remembered for each directory + * (GeneralSettings::globalViewProps() returns false), then the + * show hidden file setting will be stored automatically. + */ + void setHiddenFilesShown(bool show); + bool hiddenFilesShown() const; + + /** + * Turns on sorting by groups if \a enable is true. + */ + void setGroupedSorting(bool grouped); + bool groupedSorting() const; + + /** + * Returns the items of the view. + */ + KFileItemList items() const; + + /** + * @return The number of items. itemsCount() is faster in comparison + * to items().count(). + */ + int itemsCount() const; + + /** + * Returns the selected items. The list is empty if no item has been + * selected. + */ + KFileItemList selectedItems() const; + + /** + * Returns the number of selected items (this is faster than + * invoking selectedItems().count()). + */ + int selectedItemsCount() const; + + /** + * Marks the items indicated by \p urls to get selected after the + * directory DolphinView::url() has been loaded. Note that nothing + * gets selected if no loading of a directory has been triggered + * by DolphinView::setUrl() or DolphinView::reload(). + */ + void markUrlsAsSelected(const QList& urls); + + /** + * Marks the item indicated by \p url to be scrolled to and as the + * current item after directory DolphinView::url() has been loaded. + */ + void markUrlAsCurrent(const KUrl& url); + + /** + * All items that match to the pattern \a pattern will get selected + * if \a enabled is true and deselected if \a enabled is false. + */ + void selectItems(const QRegExp& pattern, bool enabled); + + /** + * Sets the zoom level to \a level. It is assured that the used + * level is adjusted to be inside the range ZoomLevelInfo::minimumLevel() and + * ZoomLevelInfo::maximumLevel(). + */ + void setZoomLevel(int level); + int zoomLevel() const; + + void setSortRole(const QByteArray& role); + QByteArray sortRole() const; + + void setSortOrder(Qt::SortOrder order); + Qt::SortOrder sortOrder() const; + + /** Sets a separate sorting with folders first (true) or a mixed sorting of files and folders (false). */ + void setSortFoldersFirst(bool foldersFirst); + bool sortFoldersFirst() const; + + /** Sets the additional information which should be shown for the items. */ + void setVisibleRoles(const QList& roles); + + /** Returns the additional information which should be shown for the items. */ + QList visibleRoles() const; + + void reload(); + + /** + * Refreshes the view to get synchronized with the settings (e.g. icons size, + * font, ...). + */ + void readSettings(); + + /** + * Saves the current settings (e.g. icons size, font, ..). + */ + void writeSettings(); + + /** + * Filters the currently shown items by \a nameFilter. All items + * which contain the given filter string will be shown. + */ + void setNameFilter(const QString& nameFilter); + QString nameFilter() const; + + /** + * Filters the currently shown items by \a filters. All items + * whose content-type matches those given by the list of filters + * will be shown. + */ + void setMimeTypeFilters(const QStringList& filters); + QStringList mimeTypeFilters() const; + + /** + * Returns a textual representation of the state of the current + * folder or selected items, suitable for use in the status bar. + */ + QString statusBarText() const; + + /** + * Returns the version control actions that are provided for the items \p items. + * Usually the actions are presented in the context menu. + */ + QList versionControlActions(const KFileItemList& items) const; + + /** + * Returns the state of the paste action: + * first is whether the action should be enabled + * second is the text for the action + */ + QPair pasteInfo() const; + + /** + * If \a tabsForFiles is true, the signal tabRequested() will also + * emitted also for files. Per default tabs for files is disabled + * and hence the signal tabRequested() will only be emitted for + * directories. + */ + void setTabsForFilesEnabled(bool tabsForFiles); + bool isTabsForFilesEnabled() const; + + /** + * Returns true if the current view allows folders to be expanded, + * i.e. presents a hierarchical view to the user. + */ + bool itemsExpandable() const; + + /** + * Restores the view state (current item, contents position, details view expansion state) + */ + void restoreState(QDataStream& stream); + + /** + * Saves the view state (current item, contents position, details view expansion state) + */ + void saveState(QDataStream& stream); + + /** + * Returns the root item which represents the current URL. + */ + KFileItem rootItem() const; + + /** + * Sets a context that is used for remembering the view-properties. + * Per default the context is empty and the path of the currently set URL + * is used for remembering the view-properties. Setting a custom context + * makes sense if specific types of URLs (e.g. search-URLs) should + * share common view-properties. + */ + void setViewPropertiesContext(const QString& context); + QString viewPropertiesContext() const; + + /** + * Checks if the given \a item can be opened as folder (e.g. archives). + * This function will also adjust the \a url (e.g. change the protocol). + * @return a valid and adjusted url if the item can be opened as folder, + * otherwise return an empty url. + */ + static KUrl openItemAsFolderUrl(const KFileItem& item, const bool browseThroughArchives = true); + +public slots: + /** + * Changes the directory to \a url. If the current directory is equal to + * \a url, nothing will be done (use DolphinView::reload() instead). + */ + void setUrl(const KUrl& url); + + /** + * Selects all items. + * @see DolphinView::selectedItems() + */ + void selectAll(); + + /** + * Inverts the current selection: selected items get unselected, + * unselected items get selected. + * @see DolphinView::selectedItems() + */ + void invertSelection(); + + void clearSelection(); + + /** + * Triggers the renaming of the currently selected items, where + * the user must input a new name for the items. + */ + void renameSelectedItems(); + + /** + * Moves all selected items to the trash. + */ + void trashSelectedItems(); + + /** + * Deletes all selected items. + */ + void deleteSelectedItems(); + + /** + * Copies all selected items to the clipboard and marks + * the items as cut. + */ + void cutSelectedItems(); + + /** Copies all selected items to the clipboard. */ + void copySelectedItems(); + + /** Pastes the clipboard data to this view. */ + void paste(); + + /** + * Pastes the clipboard data into the currently selected + * folder. If the current selection is not exactly one folder, no + * paste operation is done. + */ + void pasteIntoFolder(); + + void stopLoading(); + + /** Activates the view if the item list container gets focus. */ + virtual bool eventFilter(QObject* watched, QEvent* event); + +signals: + /** + * Is emitted if the view has been activated by e. g. a mouse click. + */ + void activated(); + + /** + * Is emitted if the URL of the view will be changed to \a url. + * After the URL has been changed the signal urlChanged() will + * be emitted. + */ + void urlAboutToBeChanged(const KUrl& url); + + /** Is emitted if the URL of the view has been changed to \a url. */ + void urlChanged(const KUrl& url); + + /** + * Is emitted when clicking on an item with the left mouse button. + */ + void itemActivated(const KFileItem& item); + + /** + * Is emitted when multiple items have been activated by e. g. + * context menu open with. + */ + void itemsActivated(const KFileItemList& items); + + /** + * Is emitted if items have been added or deleted. + */ + void itemCountChanged(); + + /** + * Is emitted if a new tab should be opened for the URL \a url. + */ + void tabRequested(const KUrl& url); + + /** + * Is emitted if the view mode (IconsView, DetailsView, + * PreviewsView) has been changed. + */ + void modeChanged(DolphinView::Mode current, DolphinView::Mode previous); + + /** Is emitted if the 'show preview' property has been changed. */ + void previewsShownChanged(bool shown); + + /** Is emitted if the 'show hidden files' property has been changed. */ + void hiddenFilesShownChanged(bool shown); + + /** Is emitted if the 'grouped sorting' property has been changed. */ + void groupedSortingChanged(bool groupedSorting); + + /** Is emitted if the sorting by name, size or date has been changed. */ + void sortRoleChanged(const QByteArray& role); + + /** Is emitted if the sort order (ascending or descending) has been changed. */ + void sortOrderChanged(Qt::SortOrder order); + + /** + * Is emitted if the sorting of files and folders (separate with folders + * first or mixed) has been changed. + */ + void sortFoldersFirstChanged(bool foldersFirst); + + /** Is emitted if the additional information shown for this view has been changed. */ + void visibleRolesChanged(const QList& current, + const QList& previous); + + /** Is emitted if the zoom level has been changed by zooming in or out. */ + void zoomLevelChanged(int current, int previous); + + /** + * Is emitted if information of an item is requested to be shown e. g. in the panel. + * If item is null, no item information request is pending. + */ + void requestItemInfo(const KFileItem& item); + + /** + * Is emitted whenever the selection has been changed. + */ + void selectionChanged(const KFileItemList& selection); + + /** + * Is emitted if a context menu is requested for the item \a item, + * which is part of \a url. If the item is null, the context menu + * for the URL should be shown and the custom actions \a customActions + * will be added. + */ + void requestContextMenu(const QPoint& pos, + const KFileItem& item, + const KUrl& url, + const QList& customActions); + + /** + * Is emitted if an information message with the content \a msg + * should be shown. + */ + void infoMessage(const QString& msg); + + /** + * Is emitted if an error message with the content \a msg + * should be shown. + */ + void errorMessage(const QString& msg); + + /** + * Is emitted if an "operation completed" message with the content \a msg + * should be shown. + */ + void operationCompletedMessage(const QString& msg); + + /** + * Is emitted after DolphinView::setUrl() has been invoked and + * the current directory is loaded. If this signal is emitted, + * it is assured that the view contains already the correct root + * URL and property settings. + */ + void directoryLoadingStarted(); + + /** + * Is emitted after the directory triggered by DolphinView::setUrl() + * has been loaded. + */ + void directoryLoadingCompleted(); + + /** + * Is emitted after the directory loading triggered by DolphinView::setUrl() + * has been canceled. + */ + void directoryLoadingCanceled(); + + /** + * Is emitted after DolphinView::setUrl() has been invoked and provides + * the information how much percent of the current directory have been loaded. + */ + void directoryLoadingProgress(int percent); + + /** + * Is emitted if the sorting is done asynchronously and provides the + * progress information of the sorting. + */ + void directorySortingProgress(int percent); + + /** + * Emitted when the file-item-model emits redirection. + * Testcase: fish://localhost + */ + void redirection(const KUrl& oldUrl, const KUrl& newUrl); + + /** + * Is emitted when the URL set by DolphinView::setUrl() represents a file. + * In this case no signal errorMessage() will be emitted. + */ + void urlIsFileError(const KUrl& url); + + /** + * Is emitted when the write state of the folder has been changed. The application + * should disable all actions like "Create New..." that depend on the write + * state. + */ + void writeStateChanged(bool isFolderWritable); + +protected: + /** Changes the zoom level if Control is pressed during a wheel event. */ + virtual void wheelEvent(QWheelEvent* event); + + /** @reimp */ + virtual void hideEvent(QHideEvent* event); + virtual bool event(QEvent* event); + +private slots: + /** + * Marks the view as active (DolphinView:isActive() will return true) + * and emits the 'activated' signal if it is not already active. + */ + void activate(); + + void slotItemActivated(int index); + void slotItemsActivated(const KItemSet& indexes); + void slotItemMiddleClicked(int index); + void slotItemContextMenuRequested(int index, const QPointF& pos); + void slotViewContextMenuRequested(const QPointF& pos); + void slotHeaderContextMenuRequested(const QPointF& pos); + void slotHeaderColumnWidthChanged(const QByteArray& role, qreal current, qreal previous); + void slotItemHovered(int index); + void slotItemUnhovered(int index); + void slotItemDropEvent(int index, QGraphicsSceneDragDropEvent* event); + void slotModelChanged(KItemModelBase* current, KItemModelBase* previous); + void slotMouseButtonPressed(int itemIndex, Qt::MouseButtons buttons); + + /* + * Is called when new items get pasted or dropped. + */ + void slotAboutToCreate(const KUrl::List& urls); + + /** + * Emits the signal \a selectionChanged() with a small delay. This is + * because getting all file items for the selection can be an expensive + * operation. Fast selection changes are collected in this case and + * the signal is emitted only after no selection change has been done + * within a small delay. + */ + void slotSelectionChanged(const KItemSet& current, const KItemSet& previous); + + /** + * Is called by emitDelayedSelectionChangedSignal() and emits the + * signal \a selectionChanged() with all selected file items as parameter. + */ + void emitSelectionChangedSignal(); + + /** + * Updates the view properties of the current URL to the + * sorting given by \a role. + */ + void updateSortRole(const QByteArray& role); + + /** + * Updates the view properties of the current URL to the + * sort order given by \a order. + */ + void updateSortOrder(Qt::SortOrder order); + + /** + * Updates the view properties of the current URL to the + * sorting of files and folders (separate with folders first or mixed) given by \a foldersFirst. + */ + void updateSortFoldersFirst(bool foldersFirst); + + /** + * Indicates in the status bar that the delete operation + * of the job \a job has been finished. + */ + void slotDeleteFileFinished(KJob* job); + + void slotRenamingFailed(const KUrl& oldUrl, const KUrl& newUrl); + + /** + * Invoked when the file item model has started the loading + * of the directory specified by DolphinView::url(). + */ + void slotDirectoryLoadingStarted(); + + /** + * Invoked when the file item model indicates that the loading of a directory has + * been completed. Assures that pasted items and renamed items get seleced. + */ + void slotDirectoryLoadingCompleted(); + + /** + * Is invoked when items of KFileItemModel have been changed. + */ + void slotItemsChanged(); + + /** + * Is invoked when the sort order has been changed by the user by clicking + * on a header item. The view properties of the directory will get updated. + */ + void slotSortOrderChangedByHeader(Qt::SortOrder current, Qt::SortOrder previous); + + /** + * Is invoked when the sort role has been changed by the user by clicking + * on a header item. The view properties of the directory will get updated. + */ + void slotSortRoleChangedByHeader(const QByteArray& current, const QByteArray& previous); + + /** + * Is invoked when the visible roles have been changed by the user by dragging + * a header item. The view properties of the directory will get updated. + */ + void slotVisibleRolesChangedByHeader(const QList& current, + const QList& previous); + + void slotRoleEditingCanceled(); + void slotRoleEditingFinished(int index, const QByteArray& role, const QVariant& value); + + /** + * Observes the item with the URL \a url. As soon as the directory + * model indicates that the item is available, the item will + * get selected and it is assured that the item stays visible. + */ + void observeCreatedItem(const KUrl& url); + + /** + * Called when a redirection happens. + * Testcase: fish://localhost + */ + void slotDirectoryRedirection(const KUrl& oldUrl, const KUrl& newUrl); + + /** + * Applies the state that has been restored by restoreViewState() + * to the view. + */ + void updateViewState(); + + void hideToolTip(); + + /** + * Calculates the number of currently shown files into + * \a fileCount and the number of folders into \a folderCount. + * The size of all files is written into \a totalFileSize. + * It is recommend using this method instead of asking the + * directory lister or the model directly, as it takes + * filtering and hierarchical previews into account. + */ + void calculateItemCount(int& fileCount, int& folderCount, KIO::filesize_t& totalFileSize) const; + +private: + void loadDirectory(const KUrl& url, bool reload = false); + + /** + * Applies the view properties which are defined by the current URL + * to the DolphinView properties. The view properties are read from a + * .directory file either in the current directory, or in the + * share/apps/dolphin/view_properties/ subfolder of the user's .kde folder. + */ + void applyViewProperties(); + + /** + * Applies the given view properties to the DolphinView. + */ + void applyViewProperties(const ViewProperties& props); + + /** + * Applies the m_mode property to the corresponding + * itemlayout-property of the KItemListView. + */ + void applyModeToView(); + + /** + * Helper method for DolphinView::paste() and DolphinView::pasteIntoFolder(). + * Pastes the clipboard data into the URL \a url. + */ + void pasteToUrl(const KUrl& url); + + /** + * Returns a list of URLs for all selected items. The list is + * simplified, so that when the URLs are part of different tree + * levels, only the parent is returned. + */ + KUrl::List simplifiedSelectedUrls() const; + + /** + * Returns the MIME data for all selected items. + */ + QMimeData* selectionMimeData() const; + + /** + * Updates m_isFolderWritable dependent on whether the folder represented by + * the current URL is writable. If the state has changed, the signal + * writeableStateChanged() will be emitted. + */ + void updateWritableState(); + + /** + * @return The current URL if no viewproperties-context is given (see + * DolphinView::viewPropertiesContext(), otherwise the context + * is returned. + */ + KUrl viewPropertiesUrl() const; + +private: + bool m_active; + bool m_tabsForFiles; + bool m_assureVisibleCurrentIndex; + bool m_isFolderWritable; + bool m_dragging; // True if a dragging is done. Required to be able to decide whether a + // tooltip may be shown when hovering an item. + + KUrl m_url; + QString m_viewPropertiesContext; + Mode m_mode; + QList m_visibleRoles; + + QVBoxLayout* m_topLayout; + + KFileItemModel* m_model; + DolphinItemListView* m_view; + KItemListContainer* m_container; + + ToolTipManager* m_toolTipManager; + + QTimer* m_selectionChangedTimer; + + KUrl m_currentItemUrl; // Used for making the view to remember the current URL after F5 + bool m_scrollToCurrentItem; // Used for marking we need to scroll to current item or not + QPoint m_restoredContentsPosition; + + QList m_selectedUrls; // Used for making the view to remember selections after F5 + bool m_clearSelectionBeforeSelectingNewItems; + bool m_markFirstNewlySelectedItemAsCurrent; + + VersionControlObserver* m_versionControlObserver; + + // For unit tests + friend class TestBase; + friend class DolphinDetailsViewTest; + friend class DolphinPart; // Accesses m_model +}; + +/// Allow using DolphinView::Mode in QVariant +Q_DECLARE_METATYPE(DolphinView::Mode) + +#endif // DOLPHINVIEW_H diff --git a/dolphin/src/views/dolphinviewactionhandler.cpp b/dolphin/src/views/dolphinviewactionhandler.cpp new file mode 100644 index 00000000..bc2727fe --- /dev/null +++ b/dolphin/src/views/dolphinviewactionhandler.cpp @@ -0,0 +1,583 @@ +/*************************************************************************** + * Copyright (C) 2008 by David Faure * + * Copyright (C) 2012 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "dolphinviewactionhandler.h" + +#include "settings/viewpropertiesdialog.h" +#include "views/dolphinview.h" +#include "views/zoomlevelinfo.h" +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +DolphinViewActionHandler::DolphinViewActionHandler(KActionCollection* collection, QObject* parent) : + QObject(parent), + m_actionCollection(collection), + m_currentView(0), + m_sortByActions(), + m_visibleRoles() +{ + Q_ASSERT(m_actionCollection); + createActions(); +} + +void DolphinViewActionHandler::setCurrentView(DolphinView* view) +{ + Q_ASSERT(view); + + if (m_currentView) { + disconnect(m_currentView, 0, this, 0); + } + + m_currentView = view; + + connect(view, SIGNAL(modeChanged(DolphinView::Mode,DolphinView::Mode)), + this, SLOT(updateViewActions())); + connect(view, SIGNAL(previewsShownChanged(bool)), + this, SLOT(slotPreviewsShownChanged(bool))); + connect(view, SIGNAL(sortOrderChanged(Qt::SortOrder)), + this, SLOT(slotSortOrderChanged(Qt::SortOrder))); + connect(view, SIGNAL(sortFoldersFirstChanged(bool)), + this, SLOT(slotSortFoldersFirstChanged(bool))); + connect(view, SIGNAL(visibleRolesChanged(QList,QList)), + this, SLOT(slotVisibleRolesChanged(QList,QList))); + connect(view, SIGNAL(groupedSortingChanged(bool)), + this, SLOT(slotGroupedSortingChanged(bool))); + connect(view, SIGNAL(hiddenFilesShownChanged(bool)), + this, SLOT(slotHiddenFilesShownChanged(bool))); + connect(view, SIGNAL(sortRoleChanged(QByteArray)), + this, SLOT(slotSortRoleChanged(QByteArray))); + connect(view, SIGNAL(zoomLevelChanged(int,int)), + this, SLOT(slotZoomLevelChanged(int,int))); + connect(view, SIGNAL(writeStateChanged(bool)), + this, SLOT(slotWriteStateChanged(bool))); +} + +DolphinView* DolphinViewActionHandler::currentView() +{ + return m_currentView; +} + +void DolphinViewActionHandler::createActions() +{ + // This action doesn't appear in the GUI, it's for the shortcut only. + // KNewFileMenu takes care of the GUI stuff. + KAction* newDirAction = m_actionCollection->addAction("create_dir"); + newDirAction->setText(i18nc("@action", "Create Folder...")); + newDirAction->setShortcut(Qt::Key_F10); + newDirAction->setIcon(KIcon("folder-new")); + newDirAction->setEnabled(false); // Will be enabled in slotWriteStateChanged(bool) if the current URL is writable + connect(newDirAction, SIGNAL(triggered()), this, SIGNAL(createDirectory())); + + // File menu + + KAction* rename = m_actionCollection->addAction("rename"); + rename->setText(i18nc("@action:inmenu File", "Rename...")); + rename->setShortcut(Qt::Key_F2); + rename->setIcon(KIcon("edit-rename")); + connect(rename, SIGNAL(triggered()), this, SLOT(slotRename())); + + KAction* moveToTrash = m_actionCollection->addAction("move_to_trash"); + moveToTrash->setText(i18nc("@action:inmenu File", "Move to Trash")); + moveToTrash->setIcon(KIcon("user-trash")); + moveToTrash->setShortcut(QKeySequence::Delete); + connect(moveToTrash, SIGNAL(triggered(Qt::MouseButtons,Qt::KeyboardModifiers)), + this, SLOT(slotTrashActivated(Qt::MouseButtons,Qt::KeyboardModifiers))); + + KAction* deleteAction = m_actionCollection->addAction("delete"); + deleteAction->setIcon(KIcon("edit-delete")); + deleteAction->setText(i18nc("@action:inmenu File", "Delete")); + deleteAction->setShortcut(Qt::SHIFT | Qt::Key_Delete); + connect(deleteAction, SIGNAL(triggered()), this, SLOT(slotDeleteItems())); + + // This action is useful for being enabled when "move_to_trash" should be + // disabled and "delete" is enabled (e.g. non-local files), so that Key_Del + // can be used for deleting the file (#76016). It needs to be a separate action + // so that the Edit menu isn't affected. + KAction* deleteWithTrashShortcut = m_actionCollection->addAction("delete_shortcut"); + // The descriptive text is just for the shortcuts editor. + deleteWithTrashShortcut->setText(i18nc("@action \"Move to Trash\" for non-local files, etc.", "Delete (using shortcut for Trash)")); + deleteWithTrashShortcut->setShortcut(QKeySequence::Delete); + deleteWithTrashShortcut->setEnabled(false); + connect(deleteWithTrashShortcut, SIGNAL(triggered()), this, SLOT(slotDeleteItems())); + + KAction *propertiesAction = m_actionCollection->addAction( "properties" ); + // Well, it's the File menu in dolphinmainwindow and the Edit menu in dolphinpart... :) + propertiesAction->setText( i18nc("@action:inmenu File", "Properties") ); + propertiesAction->setIcon(KIcon("document-properties")); + propertiesAction->setShortcuts(QList() << Qt::ALT + Qt::Key_Return << Qt::ALT + Qt::Key_Enter); + connect(propertiesAction, SIGNAL(triggered()), SLOT(slotProperties())); + + // View menu + KToggleAction* iconsAction = iconsModeAction(); + KToggleAction* compactAction = compactModeAction(); + KToggleAction* detailsAction = detailsModeAction(); + + KSelectAction* viewModeActions = m_actionCollection->add("view_mode"); + viewModeActions->setText(i18nc("@action:intoolbar", "View Mode")); + viewModeActions->addAction(iconsAction); + viewModeActions->addAction(compactAction); + viewModeActions->addAction(detailsAction); + viewModeActions->setToolBarMode(KSelectAction::MenuMode); + connect(viewModeActions, SIGNAL(triggered(QAction*)), this, SLOT(slotViewModeActionTriggered(QAction*))); + + KStandardAction::zoomIn(this, + SLOT(zoomIn()), + m_actionCollection); + + KStandardAction::zoomOut(this, + SLOT(zoomOut()), + m_actionCollection); + + KToggleAction* showPreview = m_actionCollection->add("show_preview"); + showPreview->setText(i18nc("@action:intoolbar", "Preview")); + showPreview->setToolTip(i18nc("@info", "Show preview of files and folders")); + showPreview->setIcon(KIcon("view-preview")); + connect(showPreview, SIGNAL(triggered(bool)), this, SLOT(togglePreview(bool))); + + KToggleAction* sortDescending = m_actionCollection->add("descending"); + sortDescending->setText(i18nc("@action:inmenu Sort", "Descending")); + connect(sortDescending, SIGNAL(triggered()), this, SLOT(toggleSortOrder())); + + KToggleAction* sortFoldersFirst = m_actionCollection->add("folders_first"); + sortFoldersFirst->setText(i18nc("@action:inmenu Sort", "Folders First")); + connect(sortFoldersFirst, SIGNAL(triggered()), this, SLOT(toggleSortFoldersFirst())); + + // View -> Sort By + QActionGroup* sortByActionGroup = createFileItemRolesActionGroup("sort_by_"); + + KActionMenu* sortByActionMenu = m_actionCollection->add("sort"); + sortByActionMenu->setText(i18nc("@action:inmenu View", "Sort By")); + sortByActionMenu->setDelayed(false); + + foreach (QAction* action, sortByActionGroup->actions()) { + sortByActionMenu->addAction(action); + } + sortByActionMenu->addSeparator(); + sortByActionMenu->addAction(sortDescending); + sortByActionMenu->addAction(sortFoldersFirst); + + // View -> Additional Information + QActionGroup* visibleRolesGroup = createFileItemRolesActionGroup("show_"); + + KActionMenu* visibleRolesMenu = m_actionCollection->add("additional_info"); + visibleRolesMenu->setText(i18nc("@action:inmenu View", "Additional Information")); + visibleRolesMenu->setDelayed(false); + + foreach (QAction* action, visibleRolesGroup->actions()) { + visibleRolesMenu->addAction(action); + } + + KToggleAction* showInGroups = m_actionCollection->add("show_in_groups"); + showInGroups->setIcon(KIcon("view-group")); + showInGroups->setText(i18nc("@action:inmenu View", "Show in Groups")); + connect(showInGroups, SIGNAL(triggered(bool)), this, SLOT(toggleGroupedSorting(bool))); + + KToggleAction* showHiddenFiles = m_actionCollection->add("show_hidden_files"); + showHiddenFiles->setText(i18nc("@action:inmenu View", "Show Hidden Files")); + showHiddenFiles->setShortcuts(QList() << Qt::ALT + Qt::Key_Period << Qt::Key_F8); + connect(showHiddenFiles, SIGNAL(triggered(bool)), this, SLOT(toggleShowHiddenFiles(bool))); + + KAction* adjustViewProps = m_actionCollection->addAction("view_properties"); + adjustViewProps->setText(i18nc("@action:inmenu View", "Adjust View Properties...")); + connect(adjustViewProps, SIGNAL(triggered()), this, SLOT(slotAdjustViewProperties())); +} + +QActionGroup* DolphinViewActionHandler::createFileItemRolesActionGroup(const QString& groupPrefix) +{ + const bool isSortGroup = (groupPrefix == QLatin1String("sort_by_")); + Q_ASSERT(isSortGroup || (!isSortGroup && groupPrefix == QLatin1String("show_"))); + + QActionGroup* rolesActionGroup = new QActionGroup(m_actionCollection); + rolesActionGroup->setExclusive(isSortGroup); + if (isSortGroup) { + connect(rolesActionGroup, SIGNAL(triggered(QAction*)), + this, SLOT(slotSortTriggered(QAction*))); + } else { + connect(rolesActionGroup, SIGNAL(triggered(QAction*)), + this, SLOT(toggleVisibleRole(QAction*))); + } + + QString groupName; + KActionMenu* groupMenu = 0; + QActionGroup* groupMenuGroup = 0; + + const QList rolesInfo = KFileItemModel::rolesInformation(); + foreach (const KFileItemModel::RoleInfo& info, rolesInfo) { + if (!isSortGroup && info.role == "text") { + // It should not be possible to hide the "text" role + continue; + } + + KToggleAction* action = 0; + const QString name = groupPrefix + info.role; + if (info.group.isEmpty()) { + action = m_actionCollection->add(name); + action->setActionGroup(rolesActionGroup); + } else { + if (!groupMenu || info.group != groupName) { + groupName = info.group; + groupMenu = m_actionCollection->add(groupName); + groupMenu->setText(groupName); + groupMenu->setActionGroup(rolesActionGroup); + + groupMenuGroup = new QActionGroup(groupMenu); + groupMenuGroup->setExclusive(isSortGroup); + if (isSortGroup) { + connect(groupMenuGroup, SIGNAL(triggered(QAction*)), + this, SLOT(slotSortTriggered(QAction*))); + } else { + connect(groupMenuGroup, SIGNAL(triggered(QAction*)), + this, SLOT(toggleVisibleRole(QAction*))); + } + } + + action = new KToggleAction(groupMenu); + action->setActionGroup(groupMenuGroup); + groupMenu->addAction(action); + } + action->setText(info.translation); + action->setData(info.role); + action->setEnabled(true); + + if (isSortGroup) { + m_sortByActions.insert(info.role, action); + } else { + m_visibleRoles.insert(info.role, action); + } + } + + return rolesActionGroup; +} + +void DolphinViewActionHandler::slotViewModeActionTriggered(QAction* action) +{ + const DolphinView::Mode mode = action->data().value(); + m_currentView->setMode(mode); + + QAction* viewModeMenu = m_actionCollection->action("view_mode"); + viewModeMenu->setIcon(KIcon(action->icon())); +} + +void DolphinViewActionHandler::slotRename() +{ + emit actionBeingHandled(); + m_currentView->renameSelectedItems(); +} + +void DolphinViewActionHandler::slotTrashActivated(Qt::MouseButtons, Qt::KeyboardModifiers modifiers) +{ + emit actionBeingHandled(); + m_currentView->trashSelectedItems(); +} + +void DolphinViewActionHandler::slotDeleteItems() +{ + emit actionBeingHandled(); + m_currentView->deleteSelectedItems(); +} + +void DolphinViewActionHandler::togglePreview(bool show) +{ + emit actionBeingHandled(); + m_currentView->setPreviewsShown(show); +} + +void DolphinViewActionHandler::slotPreviewsShownChanged(bool shown) +{ + Q_UNUSED(shown); + // It is not enough to update the 'Show Preview' action, also + // the 'Zoom In' and 'Zoom Out' actions must be adapted. + updateViewActions(); +} + +QString DolphinViewActionHandler::currentViewModeActionName() const +{ + switch (m_currentView->mode()) { + case DolphinView::IconsView: + return "icons"; + case DolphinView::DetailsView: + return "details"; + case DolphinView::CompactView: + return "compact"; + default: + Q_ASSERT(false); + break; + } + return QString(); // can't happen +} + +KActionCollection* DolphinViewActionHandler::actionCollection() +{ + return m_actionCollection; +} + +void DolphinViewActionHandler::updateViewActions() +{ + QAction* viewModeAction = m_actionCollection->action(currentViewModeActionName()); + if (viewModeAction) { + viewModeAction->setChecked(true); + + QAction* viewModeMenu = m_actionCollection->action("view_mode"); + viewModeMenu->setIcon(KIcon(viewModeAction->icon())); + } + + QAction* showPreviewAction = m_actionCollection->action("show_preview"); + showPreviewAction->setChecked(m_currentView->previewsShown()); + + slotSortOrderChanged(m_currentView->sortOrder()); + slotSortFoldersFirstChanged(m_currentView->sortFoldersFirst()); + slotVisibleRolesChanged(m_currentView->visibleRoles(), QList()); + slotGroupedSortingChanged(m_currentView->groupedSorting()); + slotSortRoleChanged(m_currentView->sortRole()); + slotZoomLevelChanged(m_currentView->zoomLevel(), -1); + + QAction* showHiddenFilesAction = m_actionCollection->action("show_hidden_files"); + showHiddenFilesAction->setChecked(m_currentView->hiddenFilesShown()); +} + +void DolphinViewActionHandler::zoomIn() +{ + const int level = m_currentView->zoomLevel(); + m_currentView->setZoomLevel(level + 1); + updateViewActions(); +} + +void DolphinViewActionHandler::zoomOut() +{ + const int level = m_currentView->zoomLevel(); + m_currentView->setZoomLevel(level - 1); + updateViewActions(); +} + +void DolphinViewActionHandler::toggleSortOrder() +{ + const Qt::SortOrder order = (m_currentView->sortOrder() == Qt::AscendingOrder) ? + Qt::DescendingOrder : + Qt::AscendingOrder; + m_currentView->setSortOrder(order); +} + +void DolphinViewActionHandler::toggleSortFoldersFirst() +{ + const bool sortFirst = m_currentView->sortFoldersFirst(); + m_currentView->setSortFoldersFirst(!sortFirst); +} + +void DolphinViewActionHandler::slotSortOrderChanged(Qt::SortOrder order) +{ + QAction* descending = m_actionCollection->action("descending"); + const bool sortDescending = (order == Qt::DescendingOrder); + descending->setChecked(sortDescending); +} + +void DolphinViewActionHandler::slotSortFoldersFirstChanged(bool foldersFirst) +{ + m_actionCollection->action("folders_first")->setChecked(foldersFirst); +} + +void DolphinViewActionHandler::toggleVisibleRole(QAction* action) +{ + emit actionBeingHandled(); + + const QByteArray toggledRole = action->data().toByteArray(); + + QList roles = m_currentView->visibleRoles(); + + const bool show = action->isChecked(); + + const int index = roles.indexOf(toggledRole); + const bool containsInfo = (index >= 0); + if (show && !containsInfo) { + roles.append(toggledRole); + m_currentView->setVisibleRoles(roles); + } else if (!show && containsInfo) { + roles.removeAt(index); + m_currentView->setVisibleRoles(roles); + Q_ASSERT(roles.indexOf(toggledRole) < 0); + } +} + +void DolphinViewActionHandler::slotVisibleRolesChanged(const QList& current, + const QList& previous) +{ + Q_UNUSED(previous); + + const QSet checkedRoles = current.toSet(); + QHashIterator it(m_visibleRoles); + while (it.hasNext()) { + it.next(); + const QByteArray& role = it.key(); + KToggleAction* action = it.value(); + action->setChecked(checkedRoles.contains(role)); + } +} + +void DolphinViewActionHandler::toggleGroupedSorting(bool grouped) +{ + m_currentView->setGroupedSorting(grouped); +} + +void DolphinViewActionHandler::slotGroupedSortingChanged(bool groupedSorting) +{ + QAction* showInGroupsAction = m_actionCollection->action("show_in_groups"); + showInGroupsAction->setChecked(groupedSorting); +} + +void DolphinViewActionHandler::toggleShowHiddenFiles(bool show) +{ + emit actionBeingHandled(); + m_currentView->setHiddenFilesShown(show); +} + +void DolphinViewActionHandler::slotHiddenFilesShownChanged(bool shown) +{ + QAction* showHiddenFilesAction = m_actionCollection->action("show_hidden_files"); + showHiddenFilesAction->setChecked(shown); +} + +void DolphinViewActionHandler::slotWriteStateChanged(bool isFolderWritable) +{ + m_actionCollection->action("create_dir")->setEnabled(isFolderWritable); +} + +KToggleAction* DolphinViewActionHandler::iconsModeAction() +{ + KToggleAction* iconsView = m_actionCollection->add("icons"); + iconsView->setText(i18nc("@action:inmenu View Mode", "Icons")); + iconsView->setToolTip(i18nc("@info", "Icons view mode")); + iconsView->setShortcut(Qt::CTRL | Qt::Key_1); + iconsView->setIcon(KIcon("view-list-icons")); + iconsView->setData(QVariant::fromValue(DolphinView::IconsView)); + return iconsView; +} + +KToggleAction* DolphinViewActionHandler::compactModeAction() +{ + KToggleAction* iconsView = m_actionCollection->add("compact"); + iconsView->setText(i18nc("@action:inmenu View Mode", "Compact")); + iconsView->setToolTip(i18nc("@info", "Compact view mode")); + iconsView->setShortcut(Qt::CTRL | Qt::Key_2); + iconsView->setIcon(KIcon("view-list-details")); // TODO: discuss with Oxygen-team the wrong (?) name + iconsView->setData(QVariant::fromValue(DolphinView::CompactView)); + return iconsView; +} + +KToggleAction* DolphinViewActionHandler::detailsModeAction() +{ + KToggleAction* detailsView = m_actionCollection->add("details"); + detailsView->setText(i18nc("@action:inmenu View Mode", "Details")); + detailsView->setToolTip(i18nc("@info", "Details view mode")); + detailsView->setShortcut(Qt::CTRL | Qt::Key_3); + detailsView->setIcon(KIcon("view-list-tree")); + detailsView->setData(QVariant::fromValue(DolphinView::DetailsView)); + return detailsView; +} + +void DolphinViewActionHandler::slotSortRoleChanged(const QByteArray& role) +{ + KToggleAction* action = m_sortByActions.value(role); + if (action) { + action->setChecked(true); + + if (!action->icon().isNull()) { + QAction* sortByMenu = m_actionCollection->action("sort"); + sortByMenu->setIcon(KIcon(action->icon())); + } + } +} + +void DolphinViewActionHandler::slotZoomLevelChanged(int current, int previous) +{ + Q_UNUSED(previous); + + QAction* zoomInAction = m_actionCollection->action(KStandardAction::name(KStandardAction::ZoomIn)); + if (zoomInAction) { + zoomInAction->setEnabled(current < ZoomLevelInfo::maximumLevel()); + } + + QAction* zoomOutAction = m_actionCollection->action(KStandardAction::name(KStandardAction::ZoomOut)); + if (zoomOutAction) { + zoomOutAction->setEnabled(current > ZoomLevelInfo::minimumLevel()); + } +} + +void DolphinViewActionHandler::slotSortTriggered(QAction* action) +{ + // The radiobuttons of the "Sort By"-menu are split between the main-menu + // and several sub-menus. Because of this they don't have a common + // action-group that assures an exclusive toggle-state between the main-menu + // actions and the sub-menu-actions. If an action gets checked, it must + // be assured that all other actions get unchecked. + QAction* sortByMenu = m_actionCollection->action("sort"); + foreach (QAction* groupAction, sortByMenu->menu()->actions()) { + KActionMenu* actionMenu = qobject_cast(groupAction); + if (actionMenu) { + foreach (QAction* subAction, actionMenu->menu()->actions()) { + subAction->setChecked(false); + } + } else if (groupAction->actionGroup()) { + groupAction->setChecked(false); + } + } + action->setChecked(true); + + // Apply the activated sort-role to the view + const QByteArray role = action->data().toByteArray(); + m_currentView->setSortRole(role); +} + +void DolphinViewActionHandler::slotAdjustViewProperties() +{ + emit actionBeingHandled(); + QPointer dialog = new ViewPropertiesDialog(m_currentView); + dialog->exec(); + delete dialog; +} + +void DolphinViewActionHandler::slotProperties() +{ + KPropertiesDialog* dialog = 0; + const KFileItemList list = m_currentView->selectedItems(); + if (list.isEmpty()) { + const KUrl url = m_currentView->url(); + dialog = new KPropertiesDialog(url, m_currentView); + } else { + dialog = new KPropertiesDialog(list, m_currentView); + } + + dialog->setAttribute(Qt::WA_DeleteOnClose); + dialog->show(); + dialog->raise(); + dialog->activateWindow(); +} diff --git a/dolphin/src/views/dolphinviewactionhandler.h b/dolphin/src/views/dolphinviewactionhandler.h new file mode 100644 index 00000000..786b7be4 --- /dev/null +++ b/dolphin/src/views/dolphinviewactionhandler.h @@ -0,0 +1,260 @@ +/*************************************************************************** + * Copyright (C) 2008 by David Faure * + * Copyright (C) 2012 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + + +#ifndef DOLPHINVIEWACTIONHANDLER_H +#define DOLPHINVIEWACTIONHANDLER_H + +#include "dolphinprivate_export.h" +#include +#include +#include "views/dolphinview.h" +#include + +class KToggleAction; +#include +#include +class DolphinView; +class KActionCollection; + +/** + * @short Handles all actions for DolphinView + * + * The action handler owns all the actions and slots related to DolphinView, + * but can the view that is acts upon can be switched to another one + * (this is used in the case of split views). + * + * The purpose of this class is also to share this code between DolphinMainWindow + * and DolphinPart. + * + * @see DolphinView + * @see DolphinMainWindow + * @see DolphinPart + */ +class DOLPHINPRIVATE_EXPORT DolphinViewActionHandler : public QObject +{ + Q_OBJECT + +public: + explicit DolphinViewActionHandler(KActionCollection* collection, QObject* parent); + + /** + * Sets the view that this action handler should work on. + */ + void setCurrentView(DolphinView* view); + + /** + * Returns the view that this action handler should work on. + */ + DolphinView* currentView(); + + /** + * Returns the name of the action for the current viewmode + */ + QString currentViewModeActionName() const; + + /** + * Returns m_actionCollection + */ + KActionCollection* actionCollection(); + +public Q_SLOTS: + /** + * Update all actions in the 'View' menu, i.e. those that depend on the + * settings in the current view. + */ + void updateViewActions(); + +Q_SIGNALS: + /** + * Emitted by DolphinViewActionHandler when the user triggered an action. + * This is only used for clearining the statusbar in DolphinMainWindow. + */ + void actionBeingHandled(); + + /** + * Emitted if the user requested creating a new directory by the F10 key. + * The receiver of the signal (DolphinMainWindow or DolphinPart) invokes + * the method createDirectory of their KNewFileMenu instance. + */ + void createDirectory(); + +private Q_SLOTS: + /** + * Emitted when the user requested a change of view mode + */ + void slotViewModeActionTriggered(QAction*); + + /** + * Let the user input a name for the selected item(s) and trigger + * a renaming afterwards. + */ + void slotRename(); + + /** + * Moves the selected items of the active view to the trash. + * This methods adds "shift means del" handling. + */ + void slotTrashActivated(Qt::MouseButtons, Qt::KeyboardModifiers); + + /** + * Deletes the selected items of the active view. + */ + void slotDeleteItems(); + + /** + * Switches between showing a preview of the file content and showing the icon. + */ + void togglePreview(bool); + + /** Updates the state of the 'Show preview' menu action. */ + void slotPreviewsShownChanged(bool shown); + + /** Increases the size of the current set view mode. */ + void zoomIn(); + + /** Decreases the size of the current set view mode. */ + void zoomOut(); + + /** Switches between an ascending and descending sorting order. */ + void toggleSortOrder(); + + /** Switches between a separate sorting and a mixed sorting of files and folders. */ + void toggleSortFoldersFirst(); + + /** + * Updates the state of the 'Sort Ascending/Descending' action. + */ + void slotSortOrderChanged(Qt::SortOrder order); + + /** + * Updates the state of the 'Sort Folders First' action. + */ + void slotSortFoldersFirstChanged(bool foldersFirst); + + /** + * Updates the state of the 'Sort by' actions. + */ + void slotSortRoleChanged(const QByteArray& role); + + /** + * Updates the state of the 'Zoom In' and 'Zoom Out' actions. + */ + void slotZoomLevelChanged(int current, int previous); + + /** + * Switches on or off the displaying of additional information + * as specified by \a action. + */ + void toggleVisibleRole(QAction* action); + + /** + * Changes the sorting of the current view. + */ + void slotSortTriggered(QAction*); + + /** + * Updates the state of the 'Additional Information' actions. + */ + void slotVisibleRolesChanged(const QList& current, + const QList& previous); + + /** + * Switches between sorting by groups or not. + */ + void toggleGroupedSorting(bool); + + /** + * Updates the state of the 'Categorized sorting' menu action. + */ + void slotGroupedSortingChanged(bool sortCategorized); + + /** + * Switches between showing and hiding of hidden marked files + */ + void toggleShowHiddenFiles(bool); + + /** + * Updates the state of the 'Show hidden files' menu action. + */ + void slotHiddenFilesShownChanged(bool shown); + + /** + * Updates the state of the 'Create Folder...' action. + */ + void slotWriteStateChanged(bool isFolderWritable); + + /** + * Opens the view properties dialog, which allows to modify the properties + * of the currently active view. + */ + void slotAdjustViewProperties(); + + /** + * Connected to the "properties" action. + * Opens the properties dialog for the selected items of the + * active view. The properties dialog shows information + * like name, size and permissions. + */ + void slotProperties(); + +private: + /** + * Create all the actions. + * This is called only once (by the constructor) + */ + void createActions(); + + /** + * Creates an action-group out of all roles from KFileItemModel. + * Dependent on the group-prefix either a radiobutton-group is + * created for sorting (prefix is "sort_by_") or a checkbox-group + * is created for additional information (prefix is "show_"). + * The changes of actions are reported to slotSortTriggered() or + * toggleAdditionalInfo(). + */ + QActionGroup* createFileItemRolesActionGroup(const QString& groupPrefix); + + /** + * Returns the "switch to icons mode" action. + * Helper method for createActions(); + */ + KToggleAction* iconsModeAction(); + + /** + * Returns the "switch to compact mode" action. + * Helper method for createActions(); + */ + KToggleAction* compactModeAction(); + + /** + * Returns the "switch to details mode" action. + * Helper method for createActions(); + */ + KToggleAction* detailsModeAction(); + + KActionCollection* m_actionCollection; + DolphinView* m_currentView; + + QHash m_sortByActions; + QHash m_visibleRoles; +}; + +#endif /* DOLPHINVIEWACTIONHANDLER_H */ diff --git a/dolphin/src/views/draganddrophelper.cpp b/dolphin/src/views/draganddrophelper.cpp new file mode 100644 index 00000000..ff86b957 --- /dev/null +++ b/dolphin/src/views/draganddrophelper.cpp @@ -0,0 +1,68 @@ +/*************************************************************************** + * Copyright (C) 2007-2011 by Peter Penz * + * Copyright (C) 2007 by David Faure * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "draganddrophelper.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +KonqOperations* DragAndDropHelper::dropUrls(const KFileItem& destItem, const KUrl& destUrl, QDropEvent* event, QString& error) +{ + error.clear(); + + if (!destItem.isNull() && !destItem.isWritable()) { + error = i18nc("@info:status", "Access denied. Could not write to %1", destUrl.pathOrUrl()); + return 0; + } + + const QMimeData* mimeData = event->mimeData(); + if (mimeData->hasFormat("application/x-kde-ark-dndextract-service") && + mimeData->hasFormat("application/x-kde-ark-dndextract-path")) { + const QString remoteDBusClient = mimeData->data("application/x-kde-ark-dndextract-service"); + const QString remoteDBusPath = mimeData->data("application/x-kde-ark-dndextract-path"); + + QDBusMessage message = QDBusMessage::createMethodCall(remoteDBusClient, remoteDBusPath, + "org.kde.ark.DndExtract", "extractSelectedFilesTo"); + message.setArguments(QVariantList() << destUrl.pathOrUrl()); + QDBusConnection::sessionBus().call(message); + } else if (!destItem.isNull() && (destItem.isDir() || destItem.isDesktopFile())) { + // Drop into a directory or a desktop-file + const KUrl::List urls = KUrl::List::fromMimeData(event->mimeData()); + foreach (const KUrl& url, urls) { + if (url == destUrl) { + error = i18nc("@info:status", "A folder cannot be dropped into itself"); + return 0; + } + } + + return KonqOperations::doDrop(destItem, destUrl, event, QApplication::activeWindow(), QList()); + } else { + return KonqOperations::doDrop(KFileItem(), destUrl, event, QApplication::activeWindow(), QList()); + } + + return 0; +} + diff --git a/dolphin/src/views/draganddrophelper.h b/dolphin/src/views/draganddrophelper.h new file mode 100644 index 00000000..e10f505b --- /dev/null +++ b/dolphin/src/views/draganddrophelper.h @@ -0,0 +1,61 @@ +/*************************************************************************** + * Copyright (C) 2007-2011 by Peter Penz * + * Copyright (C) 2007 by David Faure * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef DRAGANDDROPHELPER_H +#define DRAGANDDROPHELPER_H + +#include "dolphinprivate_export.h" + +#include + +class KFileItem; +class KUrl; +#include +#include +class KonqOperations; + +class DOLPHINPRIVATE_EXPORT DragAndDropHelper +{ +public: + /** + * Handles the dropping of URLs to the given destination. A context menu + * with the options 'Move Here', 'Copy Here', 'Link Here' and 'Cancel' is + * offered to the user. The drag destination must represent a directory or + * a desktop-file, otherwise the dropping gets ignored. + * + * @param destItem Item of the destination. Can be 0 (KFileItem::isNull()) if + * no file-item is available for the destination. In this case + * destUrl is used as fallback. For performance reasons it is + * recommended to pass a file-item if available. + * @param destUrl URL of the item destination. Is used only if destItem::isNull() + * is true. + * @param event Drop event. + * @param error Error message intended to be shown for users if dropping is not + * possible. If an empty string is returned, the dropping has been + * successful. + * @return KonqOperations pointer + */ + static KonqOperations* dropUrls(const KFileItem& destItem, + const KUrl& destUrl, + QDropEvent* event, + QString& error); +}; + +#endif diff --git a/dolphin/src/views/renamedialog.cpp b/dolphin/src/views/renamedialog.cpp new file mode 100644 index 00000000..ba3a7233 --- /dev/null +++ b/dolphin/src/views/renamedialog.cpp @@ -0,0 +1,221 @@ +/*************************************************************************** + * Copyright (C) 2006-2010 by Peter Penz (peter.penz@gmx.at) * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "renamedialog.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +RenameDialog::RenameDialog(QWidget *parent, const KFileItemList& items) : + KDialog(parent), + m_renameOneItem(false), + m_newName(), + m_lineEdit(0), + m_items(items), + m_allExtensionsDifferent(true), + m_spinBox(0) +{ + const QSize minSize = minimumSize(); + setMinimumSize(QSize(320, minSize.height())); + + const int itemCount = items.count(); + Q_ASSERT(itemCount >= 1); + m_renameOneItem = (itemCount == 1); + + setCaption(m_renameOneItem ? + i18nc("@title:window", "Rename Item") : + i18nc("@title:window", "Rename Items")); + setButtons(Ok | Cancel); + setDefaultButton(Ok); + + setButtonGuiItem(Ok, KGuiItem(i18nc("@action:button", "&Rename"), "dialog-ok-apply")); + + QWidget* page = new QWidget(this); + setMainWidget(page); + + QVBoxLayout* topLayout = new QVBoxLayout(page); + + QLabel* editLabel = 0; + if (m_renameOneItem) { + m_newName = items.first().name(); + editLabel = new QLabel(i18nc("@label:textbox", "Rename the item %1 to:", m_newName), + page); + editLabel->setTextFormat(Qt::PlainText); + } else { + m_newName = i18nc("@info:status", "New name #"); + editLabel = new QLabel(i18ncp("@label:textbox", + "Rename the %1 selected item to:", + "Rename the %1 selected items to:", itemCount), + page); + } + + m_lineEdit = new KLineEdit(page); + connect(m_lineEdit, SIGNAL(textChanged(QString)), this, SLOT(slotTextChanged(QString))); + + int selectionLength = m_newName.length(); + if (m_renameOneItem) { + const QString fileName = items.first().url().prettyUrl(); + const QString extension = KMimeType::extractKnownExtension(fileName.toLower()); + + // If the current item is a directory, select the whole file name. + if ((extension.length() > 0) && !items.first().isDir()) { + // Don't select the extension + selectionLength -= extension.length() + 1; + } + } else { + // Don't select the # character + --selectionLength; + } + + m_lineEdit->setText(m_newName); + m_lineEdit->setSelection(0, selectionLength); + m_lineEdit->setFocus(); + + topLayout->addWidget(editLabel); + topLayout->addWidget(m_lineEdit); + + if (!m_renameOneItem) { + QSet extensions; + foreach (const KFileItem& item, m_items) { + const QString extension = KMimeType::extractKnownExtension(item.url().prettyUrl().toLower()); + + if (extensions.contains(extension)) { + m_allExtensionsDifferent = false; + break; + } + + extensions.insert(extension); + } + + QLabel* infoLabel = new QLabel(i18nc("@info", "# will be replaced by ascending numbers starting with:"), page); + m_spinBox = new KIntSpinBox(0, 10000, 1, 1, page, 10); + + QHBoxLayout* horizontalLayout = new QHBoxLayout(page); + horizontalLayout->setMargin(0); + horizontalLayout->addWidget(infoLabel); + horizontalLayout->addWidget(m_spinBox); + + topLayout->addLayout(horizontalLayout); + } +} + +RenameDialog::~RenameDialog() +{ +} + +void RenameDialog::slotButtonClicked(int button) +{ + if (button == KDialog::Ok) { + m_newName = m_lineEdit->text(); + + if (m_renameOneItem) { + Q_ASSERT(m_items.count() == 1); + const KUrl oldUrl = m_items.first().url(); + KUrl newUrl = oldUrl; + newUrl.setFileName(KIO::encodeFileName(m_newName)); + + QWidget* widget = parentWidget(); + if (!widget) { + widget = this; + } + + KonqOperations::rename(widget, oldUrl, newUrl); + } else { + renameItems(); + } + } + + KDialog::slotButtonClicked(button); +} + +void RenameDialog::slotTextChanged(const QString& newName) +{ + bool enable = !newName.isEmpty() && (newName != QLatin1String("..")) && (newName != QLatin1String(".")); + if (enable && !m_renameOneItem) { + const int count = newName.count(QLatin1Char('#')); + if (count == 0) { + // Renaming multiple files without '#' will only work if all extensions are different. + enable = m_allExtensionsDifferent; + } else { + // Assure that the new name contains exactly one # (or a connected sequence of #'s) + const int first = newName.indexOf(QLatin1Char('#')); + const int last = newName.lastIndexOf(QLatin1Char('#')); + enable = (last - first + 1 == count); + } + } + enableButtonOk(enable); +} + +void RenameDialog::renameItems() +{ + // Iterate through all items and rename them... + int index = m_spinBox->value(); + foreach (const KFileItem& item, m_items) { + QString newName = indexedName(m_newName, index, QLatin1Char('#')); + ++index; + + const KUrl oldUrl = item.url(); + const QString extension = KMimeType::extractKnownExtension(oldUrl.prettyUrl().toLower()); + if (!extension.isEmpty()) { + newName.append(QLatin1Char('.')); + newName.append(extension); + } + + if (oldUrl.fileName() != newName) { + KUrl newUrl = oldUrl; + newUrl.setFileName(KIO::encodeFileName(newName)); + + QWidget* widget = parentWidget(); + if (!widget) { + widget = this; + } + + KonqOperations::rename(widget, oldUrl, newUrl); + } + } +} + +QString RenameDialog::indexedName(const QString& name, int index, const QChar& indexPlaceHolder) +{ + QString newName = name; + + QString indexString = QString::number(index); + + // Insert leading zeros if necessary + const int minIndexLength = name.count(indexPlaceHolder); + while (indexString.length() < minIndexLength) { + indexString.prepend(QLatin1Char('0')); + } + + // Replace the index placeholders by the indexString + const int placeHolderStart = newName.indexOf(indexPlaceHolder); + newName.replace(placeHolderStart, minIndexLength, indexString); + + return newName; +} + +#include "moc_renamedialog.cpp" diff --git a/dolphin/src/views/renamedialog.h b/dolphin/src/views/renamedialog.h new file mode 100644 index 00000000..d91e7eed --- /dev/null +++ b/dolphin/src/views/renamedialog.h @@ -0,0 +1,71 @@ +/*************************************************************************** + * Copyright (C) 2006-2010 by Peter Penz (peter.penz@gmx.at) * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef RENAMEDIALOG_H +#define RENAMEDIALOG_H + +#include "dolphinprivate_export.h" + +#include +#include + +class KLineEdit; +class KIntSpinBox; + +#include + +/** + * @brief Dialog for renaming a variable number of files. + */ +class DOLPHINPRIVATE_EXPORT RenameDialog : public KDialog +{ + Q_OBJECT + +public: + explicit RenameDialog(QWidget* parent, const KFileItemList& items); + virtual ~RenameDialog(); + +protected slots: + virtual void slotButtonClicked(int button); + +private slots: + void slotTextChanged(const QString& newName); + +private: + void renameItems(); + + /** + * @return Returns the string \p name, where the characters represented by + * \p indexPlaceHolder get replaced by the index \p index. + * E. g. Calling indexedName("Test #.jpg", 12, '#') returns "Test 12.jpg". + * A connected sequence of placeholders results in leading zeros: + * indexedName("Test ####.jpg", 12, '#') returns "Test 0012.jpg". + */ + static QString indexedName(const QString& name, int index, const QChar& indexPlaceHolder); + +private: + bool m_renameOneItem; + QString m_newName; + KLineEdit* m_lineEdit; + KFileItemList m_items; + bool m_allExtensionsDifferent; + KIntSpinBox* m_spinBox; +}; + +#endif diff --git a/dolphin/src/views/tooltips/filemetadatatooltip.cpp b/dolphin/src/views/tooltips/filemetadatatooltip.cpp new file mode 100644 index 00000000..d9283314 --- /dev/null +++ b/dolphin/src/views/tooltips/filemetadatatooltip.cpp @@ -0,0 +1,163 @@ +/*************************************************************************** + * Copyright (C) 2010 by Peter Penz * + * Copyright (C) 2008 by Fredrik Höglund * + * Copyright (C) 2012 by Mark Gaiser * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "filemetadatatooltip.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +FileMetaDataToolTip::FileMetaDataToolTip(QWidget* parent) : + QWidget(parent), + m_preview(0), + m_name(0), + m_fileMetaDataWidget(0) +{ + setAttribute(Qt::WA_TranslucentBackground); + setWindowFlags(Qt::ToolTip | Qt::FramelessWindowHint); + + // Create widget for file preview + m_preview = new QLabel(this); + m_preview->setAlignment(Qt::AlignTop); + + // Create widget for file name + m_name = new QLabel(this); + m_name->setForegroundRole(QPalette::ToolTipText); + m_name->setTextFormat(Qt::PlainText); + m_name->setAlignment(Qt::AlignHCenter); + + QFont font = m_name->font(); + font.setBold(true); + m_name->setFont(font); + + QFontMetrics fontMetrics(font); + m_name->setMaximumWidth(fontMetrics.averageCharWidth() * 40); + + // Create widget for the meta data + m_fileMetaDataWidget = new KFileMetaDataWidget(this); + m_fileMetaDataWidget->setForegroundRole(QPalette::ToolTipText); + connect(m_fileMetaDataWidget, SIGNAL(metaDataRequestFinished(KFileItemList)), + this, SIGNAL(metaDataRequestFinished(KFileItemList))); + + QVBoxLayout* textLayout = new QVBoxLayout(); + textLayout->addWidget(m_name); + textLayout->addWidget(new KSeparator()); + textLayout->addWidget(m_fileMetaDataWidget); + textLayout->setAlignment(m_name, Qt::AlignCenter); + textLayout->setAlignment(m_fileMetaDataWidget, Qt::AlignLeft); + // Assure that the text-layout gets top-aligned by adding a stretch. + // Don't use textLayout->setAlignment(Qt::AlignTop) instead, as this does + // not work with the heightForWidth()-size-hint of m_fileMetaDataWidget + // (see bug #241608) + textLayout->addStretch(); + + QHBoxLayout* tipLayout = new QHBoxLayout(this); + tipLayout->addWidget(m_preview); + tipLayout->addSpacing(tipLayout->margin()); + tipLayout->addLayout(textLayout); +} + +FileMetaDataToolTip::~FileMetaDataToolTip() +{ +} + +void FileMetaDataToolTip::setPreview(const QPixmap& pixmap) +{ + m_preview->setPixmap(pixmap); +} + +QPixmap FileMetaDataToolTip::preview() const +{ + if (m_preview->pixmap()) { + return *m_preview->pixmap(); + } + return QPixmap(); +} + +void FileMetaDataToolTip::setName(const QString& name) +{ + QTextOption textOption; + textOption.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); + + const QString processedName = Qt::mightBeRichText(name) ? name : KStringHandler::preProcessWrap(name); + + QTextLayout textLayout(processedName); + textLayout.setFont(m_name->font()); + textLayout.setTextOption(textOption); + + QString wrappedText; + wrappedText.reserve(processedName.length()); + + // wrap the text to fit into the maximum width of m_name + textLayout.beginLayout(); + QTextLine line = textLayout.createLine(); + while (line.isValid()) { + line.setLineWidth(m_name->maximumWidth()); + wrappedText += processedName.mid(line.textStart(), line.textLength()); + + line = textLayout.createLine(); + if (line.isValid()) { + wrappedText += QChar::LineSeparator; + } + } + textLayout.endLayout(); + + m_name->setText(wrappedText); +} + +QString FileMetaDataToolTip::name() const +{ + return m_name->text(); +} + +void FileMetaDataToolTip::setItems(const KFileItemList& items) +{ + m_fileMetaDataWidget->setItems(items); +} + +KFileItemList FileMetaDataToolTip::items() const +{ + return m_fileMetaDataWidget->items(); +} + +void FileMetaDataToolTip::paintEvent(QPaintEvent* event) +{ + QStylePainter painter(this); + QStyleOptionFrame option; + option.init(this); + painter.drawPrimitive(QStyle::PE_PanelTipLabel, option); + painter.end(); + + QWidget::paintEvent(event); +} + +#include "moc_filemetadatatooltip.cpp" diff --git a/dolphin/src/views/tooltips/filemetadatatooltip.h b/dolphin/src/views/tooltips/filemetadatatooltip.h new file mode 100644 index 00000000..f99124be --- /dev/null +++ b/dolphin/src/views/tooltips/filemetadatatooltip.h @@ -0,0 +1,74 @@ +/*************************************************************************** + * Copyright (C) 2010 by Peter Penz * + * Copyright (C) 2008 by Fredrik Höglund * + * Copyright (C) 2012 by Mark Gaiser * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef FILEMETADATATOOLTIP_H +#define FILEMETADATATOOLTIP_H + +#include + +class KFileItemList; +#include + +class KFileMetaDataWidget; + +/** + * @brief Tooltip, that shows the meta information and a preview of one + * or more files. + */ +class FileMetaDataToolTip : public QWidget +{ + Q_OBJECT + +public: + FileMetaDataToolTip(QWidget* parent = 0); + virtual ~FileMetaDataToolTip(); + + void setPreview(const QPixmap& pixmap); + QPixmap preview() const; + + void setName(const QString& name); + QString name() const; + + /** + * Sets the items for which the meta data should be shown. + * The signal metaDataRequestFinished() will be emitted, + * as soon as the meta data for the items has been received. + */ + void setItems(const KFileItemList& items); + KFileItemList items() const; + +signals: + /** + * Is emitted after the meta data has been received for the items + * set by FileMetaDataToolTip::setItems(). + */ + void metaDataRequestFinished(const KFileItemList& items); + +protected: + virtual void paintEvent(QPaintEvent* event); + +private: + QLabel* m_preview; + QLabel* m_name; + KFileMetaDataWidget* m_fileMetaDataWidget; +}; + +#endif diff --git a/dolphin/src/views/tooltips/tooltipmanager.cpp b/dolphin/src/views/tooltips/tooltipmanager.cpp new file mode 100644 index 00000000..173a3e08 --- /dev/null +++ b/dolphin/src/views/tooltips/tooltipmanager.cpp @@ -0,0 +1,269 @@ +/******************************************************************************* + * Copyright (C) 2008 by Konstantin Heil * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + *******************************************************************************/ + +#include "tooltipmanager.h" + +#include "filemetadatatooltip.h" +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +ToolTipManager::ToolTipManager(QWidget* parent) : + QObject(parent), + m_showToolTipTimer(0), + m_contentRetrievalTimer(0), + m_fileMetaDataToolTip(0), + m_toolTipRequested(false), + m_metaDataRequested(false), + m_appliedWaitCursor(false), + m_margin(4), + m_item(), + m_itemRect() +{ + if (parent) { + m_margin = qMax(m_margin, parent->style()->pixelMetric(QStyle::PM_ToolTipLabelFrameWidth)); + } + + m_showToolTipTimer = new QTimer(this); + m_showToolTipTimer->setSingleShot(true); + m_showToolTipTimer->setInterval(500); + connect(m_showToolTipTimer, SIGNAL(timeout()), this, SLOT(showToolTip())); + + m_contentRetrievalTimer = new QTimer(this); + m_contentRetrievalTimer->setSingleShot(true); + m_contentRetrievalTimer->setInterval(200); + connect(m_contentRetrievalTimer, SIGNAL(timeout()), this, SLOT(startContentRetrieval())); + + Q_ASSERT(m_contentRetrievalTimer->interval() < m_showToolTipTimer->interval()); +} + +ToolTipManager::~ToolTipManager() +{ + delete m_fileMetaDataToolTip; + m_fileMetaDataToolTip = 0; +} + +void ToolTipManager::showToolTip(const KFileItem& item, const QRectF& itemRect) +{ + hideToolTip(); + + m_itemRect = itemRect.toRect(); + + m_itemRect.adjust(-m_margin, -m_margin, m_margin, m_margin); + m_item = item; + + // Only start the retrieving of the content, when the mouse has been over this + // item for 200 milliseconds. This prevents a lot of useless preview jobs and + // meta data retrieval, when passing rapidly over a lot of items. + Q_ASSERT(!m_fileMetaDataToolTip); + m_fileMetaDataToolTip = new FileMetaDataToolTip(); + connect(m_fileMetaDataToolTip, SIGNAL(metaDataRequestFinished(KFileItemList)), + this, SLOT(slotMetaDataRequestFinished())); + + m_contentRetrievalTimer->start(); + m_showToolTipTimer->start(); + m_toolTipRequested = true; + Q_ASSERT(!m_metaDataRequested); +} + +void ToolTipManager::hideToolTip() +{ + if (m_appliedWaitCursor) { + QApplication::restoreOverrideCursor(); + m_appliedWaitCursor = false; + } + + m_toolTipRequested = false; + m_metaDataRequested = false; + m_showToolTipTimer->stop(); + m_contentRetrievalTimer->stop(); + + if (m_fileMetaDataToolTip) { + m_fileMetaDataToolTip->hide(); + // Do not delete the tool tip immediately to prevent crashes when + // QCoreApplication tries to deliver an 'Enter' event to it, see bug 310579. + m_fileMetaDataToolTip->deleteLater(); + m_fileMetaDataToolTip = 0; + } +} + +void ToolTipManager::startContentRetrieval() +{ + if (!m_toolTipRequested) { + return; + } + + m_fileMetaDataToolTip->setName(m_item.text()); + + // Request the retrieval of meta-data. The slot + // slotMetaDataRequestFinished() is invoked after the + // meta-data have been received. + m_metaDataRequested = true; + m_fileMetaDataToolTip->setItems(KFileItemList() << m_item); + m_fileMetaDataToolTip->adjustSize(); + + // Request a preview of the item + m_fileMetaDataToolTip->setPreview(QPixmap()); + + KIO::PreviewJob* job = new KIO::PreviewJob(KFileItemList() << m_item, QSize(256, 256)); + job->setIgnoreMaximumSize(m_item.isLocalFile()); + if (job->ui()) { + job->ui()->setWindow(qApp->activeWindow()); + } + + connect(job, SIGNAL(gotPreview(KFileItem,QPixmap)), + this, SLOT(setPreviewPix(KFileItem,QPixmap))); + connect(job, SIGNAL(failed(KFileItem)), + this, SLOT(previewFailed())); +} + + +void ToolTipManager::setPreviewPix(const KFileItem& item, + const QPixmap& pixmap) +{ + if (!m_toolTipRequested || (m_item.url() != item.url())) { + // No tooltip is requested anymore or an old preview has been received + return; + } + + if (pixmap.isNull()) { + previewFailed(); + } else { + m_fileMetaDataToolTip->setPreview(pixmap); + if (!m_showToolTipTimer->isActive()) { + showToolTip(); + } + } +} + +void ToolTipManager::previewFailed() +{ + if (!m_toolTipRequested) { + return; + } + + const QPixmap pixmap = KIcon(m_item.iconName()).pixmap(128, 128); + m_fileMetaDataToolTip->setPreview(pixmap); + if (!m_showToolTipTimer->isActive()) { + showToolTip(); + } +} + +void ToolTipManager::slotMetaDataRequestFinished() +{ + if (!m_toolTipRequested) { + return; + } + + m_metaDataRequested = false; + + if (!m_showToolTipTimer->isActive()) { + showToolTip(); + } +} + +void ToolTipManager::showToolTip() +{ + Q_ASSERT(m_toolTipRequested); + if (m_appliedWaitCursor) { + QApplication::restoreOverrideCursor(); + m_appliedWaitCursor = false; + } + + if (m_fileMetaDataToolTip->preview().isNull() || m_metaDataRequested) { + Q_ASSERT(!m_appliedWaitCursor); + QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); + m_appliedWaitCursor = true; + return; + } + + const QRect screen = QApplication::desktop()->screenGeometry(QCursor::pos()); + + // Restrict tooltip size to current screen size when needed. + // Because layout controlling widget doesn't respect widget's maximumSize property + // (correct me if I'm wrong), we need to let layout do its work, then manually change + // geometry if resulting widget doesn't fit the screen. + + // Step #1 - make sizeHint return calculated tooltip size + m_fileMetaDataToolTip->layout()->setSizeConstraint(QLayout::SetFixedSize); + m_fileMetaDataToolTip->adjustSize(); + QSize size = m_fileMetaDataToolTip->sizeHint(); + + // Step #2 - correct tooltip size when needed + if (size.width() > screen.width()) { + size.setWidth(screen.width()); + } + if (size.height() > screen.height()) { + size.setHeight(screen.height()); + } + + // m_itemRect defines the area of the item, where the tooltip should be + // shown. Per default the tooltip is shown centered at the bottom. + // It must be assured that: + // - the content is fully visible + // - the content is not drawn inside m_itemRect + const bool hasRoomToLeft = (m_itemRect.left() - size.width() - m_margin >= screen.left()); + const bool hasRoomToRight = (m_itemRect.right() + size.width() + m_margin <= screen.right()); + const bool hasRoomAbove = (m_itemRect.top() - size.height() - m_margin >= screen.top()); + const bool hasRoomBelow = (m_itemRect.bottom() + size.height() + m_margin <= screen.bottom()); + if (!hasRoomAbove && !hasRoomBelow && !hasRoomToLeft && !hasRoomToRight) { + return; + } + + int x, y; + if (hasRoomBelow || hasRoomAbove) { + x = qMax(screen.left(), m_itemRect.center().x() - size.width() / 2); + if (x + size.width() >= screen.right()) { + x = screen.right() - size.width() + 1; + } + if (hasRoomBelow) { + y = m_itemRect.bottom() + m_margin; + } else { + y = m_itemRect.top() - size.height() - m_margin; + } + } else { + Q_ASSERT(hasRoomToLeft || hasRoomToRight); + if (hasRoomToRight) { + x = m_itemRect.right() + m_margin; + } else { + x = m_itemRect.left() - size.width() - m_margin; + } + // Put the tooltip at the bottom of the screen. The x-coordinate has already + // been adjusted, so that no overlapping with m_itemRect occurs. + y = screen.bottom() - size.height() + 1; + } + + // Step #3 - Alter tooltip geometry + m_fileMetaDataToolTip->setFixedSize(size); + m_fileMetaDataToolTip->layout()->setSizeConstraint(QLayout::SetNoConstraint); + m_fileMetaDataToolTip->move(QPoint(x, y)); + m_fileMetaDataToolTip->show(); + + m_toolTipRequested = false; +} + +#include "moc_tooltipmanager.cpp" diff --git a/dolphin/src/views/tooltips/tooltipmanager.h b/dolphin/src/views/tooltips/tooltipmanager.h new file mode 100644 index 00000000..3fdaa752 --- /dev/null +++ b/dolphin/src/views/tooltips/tooltipmanager.h @@ -0,0 +1,89 @@ +/******************************************************************************* + * Copyright (C) 2008 by Konstantin Heil * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + *******************************************************************************/ + +#ifndef TOOLTIPMANAGER_H +#define TOOLTIPMANAGER_H + +#include +#include + +#include + +class DolphinModel; +class DolphinSortFilterProxyModel; +class FileMetaDataToolTip; +#include +#include +#include + +/** + * @brief Manages the tooltips for an item view. + * + * When hovering an item, a tooltip is shown after + * a short timeout. The tooltip is hidden again when the + * viewport is hovered or the item view has been left. + */ +class ToolTipManager : public QObject +{ + Q_OBJECT + +public: + explicit ToolTipManager(QWidget* parent); + virtual ~ToolTipManager(); + + /** + * Triggers the showing of the tooltip for the item \p item + * where the item has the maximum boundaries of \p itemRect. + * The tooltip manager takes care that the tooltip is shown + * slightly delayed. + */ + void showToolTip(const KFileItem& item, const QRectF& itemRect); + + /** + * Hides the currently shown tooltip. + */ + void hideToolTip(); + +private slots: + void startContentRetrieval(); + void setPreviewPix(const KFileItem& item, const QPixmap& pix); + void previewFailed(); + void slotMetaDataRequestFinished(); + void showToolTip(); + +private: + /// Timeout from requesting a tooltip until the tooltip + /// should be shown + QTimer* m_showToolTipTimer; + + /// Timeout from requesting a tooltip until the retrieving of + /// the tooltip content like preview and meta data gets started. + QTimer* m_contentRetrievalTimer; + + FileMetaDataToolTip* m_fileMetaDataToolTip; + + bool m_toolTipRequested; + bool m_metaDataRequested; + bool m_appliedWaitCursor; + int m_margin; + KFileItem m_item; + QRect m_itemRect; +}; + +#endif diff --git a/dolphin/src/views/versioncontrol/fileviewversioncontrolplugin.desktop b/dolphin/src/views/versioncontrol/fileviewversioncontrolplugin.desktop new file mode 100644 index 00000000..80292720 --- /dev/null +++ b/dolphin/src/views/versioncontrol/fileviewversioncontrolplugin.desktop @@ -0,0 +1,69 @@ +[Desktop Entry] +Type=ServiceType +X-KDE-ServiceType=FileViewVersionControlPlugin +Comment=Version Control Plugin for File Views +Comment[ar]=ملحق التحكم بالإصدارات لعروض الملفات +Comment[ast]=Complementu de control de versiones pa vistes de ficheros +Comment[bg]=Приставка за контрол на версиите при преглед на файлове +Comment[bs]=Priključak upravljanja verzijama za datotečne prikaze +Comment[ca]=Connector de control de versions per a les vistes de fitxers +Comment[ca@valencia]=Connector de control de versions per a les vistes de fitxers +Comment[cs]=Modul pro správu verzí pro pohledy na soubory +Comment[csb]=Wtëkôcz kòntrolë wersëji dlô pòdzerków lopków +Comment[da]=Versionsstyringsplugin til filvisninger +Comment[de]=Versionskontroll-Modul für Dateiansichten +Comment[el]=Έλεγχος εκδόσεων πρόσθετων για την προβολή των αρχείων +Comment[en_GB]=Version Control Plugin for File Views +Comment[eo]=Kromprogramo de versia kontrolo por dosiervidilo +Comment[es]=Complemento de control de versiones para vistas de archivos +Comment[et]=Failivaadete versioonikontrolli plugin +Comment[eu]=Bertsio kontrolaren plugina fitxategien ikuspegietarako +Comment[fi]=Versionhallintaliitännäinen tiedostonäkymille +Comment[fr]=Module externe pour le contrôle de version applicable aux vues de fichiers +Comment[fy]=Ferzje kontrôle plugin foar triem werjeften +Comment[ga]=Breiseán rialaithe leaganacha le haghaidh Amhairc Chomhad +Comment[gl]=Complemento de control de versións para as vistas dos ficheiros +Comment[he]=תוסף ניהול גרסאות עבור תצוגת קבצים +Comment[hr]=Priključak sustava kontrole inačica za prikaz datoteka +Comment[hu]=Verziókövető modul fájlnézetekhez +Comment[ia]=Plugin de controlo de version pro vistas de file +Comment[id]=Plugin Kontrol Versi untuk Tampilan Berkas +Comment[is]=Útgáfustýringar-íforrit fyrir skráasýnir +Comment[it]=Estensione di controllo delle versioni per le viste dei file +Comment[ja]=ファイルビューのためのバージョン管理プラグイン +Comment[kk]=Файл көрнісіне арналған Нұсқаларды басқару плагині +Comment[km]=កំណែ​កម្មវិធី​ជំនួយ​វត្ថុ​បញ្ជា​​សម្រាប់​មើល​ឯកសារ +Comment[kn]=ಕಡತ ನೋಟಗಳಿಗಾಗಿ ಆವೃತ್ತಿ ನಿಯಂತ್ರಣಾ ವ್ಯವಸ್ಥೆ ಪ್ಲಗ್ಗಿನ್ (ಮಿಳಿತಾನ್ವಯ) +Comment[ko]=파일 보기를 위한 버전 관리 플러그인 +Comment[lt]=Versijų kontrolės priedas failų tvarkyklėms +Comment[lv]=Versiju kontroles spraudnis Failu skatiem +Comment[mk]=Приклучок за „Преглед на датотеки“ за контрола на верзии +Comment[ml]=ഫയല്‍ അവതരണത്തിനുള്ള വേര്‍ഷന്‍ കണ്ട്രോള്‍ സംയോജകം +Comment[mr]=फाईल दृश्या करिता आवृत्ती नियंत्रण प्लगइन +Comment[nb]=Versjonskontrollmodul for filvisninger +Comment[nds]=Verschoonkuntrull-Moduul för Dateiansichten +Comment[nl]=Plugin voor versiecontrole op bestandoverzichten +Comment[nn]=Versjonskontroll-tillegg for filvisingar +Comment[pa]=ਫਾਇਲ ਝਲਕ ਲਈ ਵਰਜਨ ਕੰਟਰੋਲ ਪਲੱਗਇਨ +Comment[pl]=Wtyczka kontroli wersji dla widoku plików +Comment[pt]='Plugin' de Controlo de Versões para as Áreas de Ficheiros +Comment[pt_BR]=Extensão de controle de versões para as visualizações de arquivos +Comment[ro]=Modul de control al versiunii pentru Vizualizări fișiere +Comment[ru]=Расширение для управления версиями для компонента просмотра папки +Comment[si]=ගොනු දැකුම් සඳහා අනුවාද පාලන ප්ලගින +Comment[sk]=Modul pre správu verzií v súborovom zobrazení +Comment[sl]=Vstavek za delo s sistemi za nadzor različic +Comment[sr]=Прикључак управљања верзијама за фајл приказе +Comment[sr@ijekavian]=Прикључак управљања верзијама за фајл приказе +Comment[sr@ijekavianlatin]=Priključak upravljanja verzijama za fajl prikaze +Comment[sr@latin]=Priključak upravljanja verzijama za fajl prikaze +Comment[sv]=Insticksprogram för versionskontroll i filvyer +Comment[tg]=Плагини идоракунии версиҳои Намоиши файлҳо +Comment[th]=ส่วนเสริมการควบคุมเวอร์ชันสำหรับมุมมองแฟ้มต่าง ๆ +Comment[tr]=Dosya Görünümleri için Sürüm Kontrol Eklentisi +Comment[ug]=ھۆججەت كۆرۈنۈشىنىڭ نەشر تىزگىن قىستۇرمىسى +Comment[uk]=Додаток керування версіями для панелей перегляду файлів +Comment[wa]=Tchôke-divins di controle del modêye po les Vuwes des fitchîs +Comment[x-test]=xxVersion Control Plugin for File Viewsxx +Comment[zh_CN]=文件视图的版本控制插件 +Comment[zh_TW]=檔案檢視的版本控制外掛程式 diff --git a/dolphin/src/views/versioncontrol/updateitemstatesthread.cpp b/dolphin/src/views/versioncontrol/updateitemstatesthread.cpp new file mode 100644 index 00000000..61093c08 --- /dev/null +++ b/dolphin/src/views/versioncontrol/updateitemstatesthread.cpp @@ -0,0 +1,70 @@ +/*************************************************************************** + * Copyright (C) 2009 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "updateitemstatesthread.h" + +#include + +#include + +UpdateItemStatesThread::UpdateItemStatesThread(KVersionControlPlugin* plugin, + const QMap >& itemStates) : + QThread(), + m_globalPluginMutex(0), + m_plugin(plugin), + m_itemStates(itemStates) +{ + // Several threads may share one instance of a plugin. A global + // mutex is required to serialize the retrieval of version control + // states inside run(). + static QMutex globalMutex; + m_globalPluginMutex = &globalMutex; +} + +UpdateItemStatesThread::~UpdateItemStatesThread() +{ +} + +void UpdateItemStatesThread::run() +{ + Q_ASSERT(!m_itemStates.isEmpty()); + Q_ASSERT(m_plugin); + + QMutexLocker pluginLocker(m_globalPluginMutex); + QMap >::iterator it = m_itemStates.begin(); + for (; it != m_itemStates.end(); ++it) { + if (m_plugin->beginRetrieval(it.key())) { + QVector& items = it.value(); + const int count = items.count(); + + for (int i = 0; i < count; ++i) { + items[i].version = m_plugin->itemVersion(items[i].item); + } + } + + m_plugin->endRetrieval(); + } +} + +QMap > UpdateItemStatesThread::itemStates() const +{ + return m_itemStates; +} + +#include "moc_updateitemstatesthread.cpp" diff --git a/dolphin/src/views/versioncontrol/updateitemstatesthread.h b/dolphin/src/views/versioncontrol/updateitemstatesthread.h new file mode 100644 index 00000000..04c1077a --- /dev/null +++ b/dolphin/src/views/versioncontrol/updateitemstatesthread.h @@ -0,0 +1,65 @@ +/*************************************************************************** + * Copyright (C) 2009 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef UPDATEITEMSTATESTHREAD_H +#define UPDATEITEMSTATESTHREAD_H + +#include +#include + +#include +#include + +class KVersionControlPlugin; + +/** + * The performance of updating the version state of items depends + * on the used plugin. To prevent that Dolphin gets blocked by a + * slow plugin, the updating is delegated to a thread. + */ +class DOLPHINPRIVATE_EXPORT UpdateItemStatesThread : public QThread +{ + Q_OBJECT + +public: + /** + * @param plugin Version control plugin that is used to update the + * state of the items. Whenever the plugin is accessed + * from the thread creator after starting the thread, + * UpdateItemStatesThread::lockPlugin() and + * UpdateItemStatesThread::unlockPlugin() must be used. + * @param itemStates List of items, where the states get updated. + */ + UpdateItemStatesThread(KVersionControlPlugin* plugin, + const QMap >& itemStates); + virtual ~UpdateItemStatesThread(); + + QMap > itemStates() const; + +protected: + virtual void run(); + +private: + QMutex* m_globalPluginMutex; // Protects the m_plugin globally + KVersionControlPlugin* m_plugin; + + QMap > m_itemStates; +}; + +#endif // UPDATEITEMSTATESTHREAD_H diff --git a/dolphin/src/views/versioncontrol/versioncontrolobserver.cpp b/dolphin/src/views/versioncontrol/versioncontrolobserver.cpp new file mode 100644 index 00000000..ba119b76 --- /dev/null +++ b/dolphin/src/views/versioncontrol/versioncontrolobserver.cpp @@ -0,0 +1,340 @@ +/*************************************************************************** + * Copyright (C) 2009 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "versioncontrolobserver.h" + +#include "dolphin_versioncontrolsettings.h" + +#include +#include +#include +#include +#include + +#include "updateitemstatesthread.h" + +#include +#include +#include + +VersionControlObserver::VersionControlObserver(QObject* parent) : + QObject(parent), + m_pendingItemStatesUpdate(false), + m_versionedDirectory(false), + m_silentUpdate(false), + m_model(0), + m_dirVerificationTimer(0), + m_plugin(0), + m_updateItemStatesThread(0) +{ + // The verification timer specifies the timeout until the shown directory + // is checked whether it is versioned. Per default it is assumed that users + // don't iterate through versioned directories and a high timeout is used + // The timeout will be decreased as soon as a versioned directory has been + // found (see verifyDirectory()). + m_dirVerificationTimer = new QTimer(this); + m_dirVerificationTimer->setSingleShot(true); + m_dirVerificationTimer->setInterval(500); + connect(m_dirVerificationTimer, SIGNAL(timeout()), + this, SLOT(verifyDirectory())); +} + +VersionControlObserver::~VersionControlObserver() +{ + if (m_plugin) { + m_plugin->disconnect(this); + m_plugin = 0; + } +} + +void VersionControlObserver::setModel(KFileItemModel* model) +{ + if (m_model) { + disconnect(m_model, SIGNAL(itemsInserted(KItemRangeList)), + this, SLOT(delayedDirectoryVerification())); + disconnect(m_model, SIGNAL(itemsChanged(KItemRangeList,QSet)), + this, SLOT(delayedDirectoryVerification())); + } + + m_model = model; + + if (model) { + connect(m_model, SIGNAL(itemsInserted(KItemRangeList)), + this, SLOT(delayedDirectoryVerification())); + connect(m_model, SIGNAL(itemsChanged(KItemRangeList,QSet)), + this, SLOT(delayedDirectoryVerification())); + } +} + +KFileItemModel* VersionControlObserver::model() const +{ + return m_model; +} + +QList VersionControlObserver::actions(const KFileItemList& items) const +{ + bool hasNullItems = false; + foreach (const KFileItem& item, items) { + if (item.isNull()) { + kWarning() << "Requesting version-control-actions for empty items"; + hasNullItems = true; + break; + } + } + + if (!m_model || hasNullItems || !isVersioned()) { + return QList(); + } + + return m_plugin->actions(items); +} + +void VersionControlObserver::delayedDirectoryVerification() +{ + m_silentUpdate = false; + m_dirVerificationTimer->start(); +} + +void VersionControlObserver::silentDirectoryVerification() +{ + m_silentUpdate = true; + m_dirVerificationTimer->start(); +} + +void VersionControlObserver::verifyDirectory() +{ + if (!m_model) { + return; + } + + const KFileItem rootItem = m_model->rootItem(); + if (rootItem.isNull() || !rootItem.url().isLocalFile()) { + return; + } + + if (m_plugin) { + m_plugin->disconnect(this); + } + + m_plugin = searchPlugin(rootItem.url()); + if (m_plugin) { + connect(m_plugin, SIGNAL(itemVersionsChanged()), + this, SLOT(silentDirectoryVerification())); + connect(m_plugin, SIGNAL(infoMessage(QString)), + this, SIGNAL(infoMessage(QString))); + connect(m_plugin, SIGNAL(errorMessage(QString)), + this, SIGNAL(errorMessage(QString))); + connect(m_plugin, SIGNAL(operationCompletedMessage(QString)), + this, SIGNAL(operationCompletedMessage(QString))); + + if (!m_versionedDirectory) { + m_versionedDirectory = true; + + // The directory is versioned. Assume that the user will further browse through + // versioned directories and decrease the verification timer. + m_dirVerificationTimer->setInterval(100); + } + updateItemStates(); + } else if (m_versionedDirectory) { + m_versionedDirectory = false; + + // The directory is not versioned. Reset the verification timer to a higher + // value, so that browsing through non-versioned directories is not slown down + // by an immediate verification. + m_dirVerificationTimer->setInterval(500); + } +} + +void VersionControlObserver::slotThreadFinished() +{ + UpdateItemStatesThread* thread = m_updateItemStatesThread; + m_updateItemStatesThread = 0; // The thread deletes itself automatically (see updateItemStates()) + + if (!m_plugin || !thread) { + return; + } + + const QMap >& itemStates = thread->itemStates(); + QMap >::const_iterator it = itemStates.constBegin(); + for (; it != itemStates.constEnd(); ++it) { + const QVector& items = it.value(); + + foreach (const ItemState& item, items) { + QHash values; + values.insert("version", QVariant(item.version)); + m_model->setData(m_model->index(item.item), values); + } + } + + if (!m_silentUpdate) { + // Using an empty message results in clearing the previously shown information message and showing + // the default status bar information. This is useful as the user already gets feedback that the + // operation has been completed because of the icon emblems. + emit operationCompletedMessage(QString()); + } + + if (m_pendingItemStatesUpdate) { + m_pendingItemStatesUpdate = false; + updateItemStates(); + } +} + +void VersionControlObserver::updateItemStates() +{ + Q_ASSERT(m_plugin); + if (m_updateItemStatesThread) { + // An update is currently ongoing. Wait until the thread has finished + // the update (see slotThreadFinished()). + m_pendingItemStatesUpdate = true; + return; + } + + QMap > itemStates; + createItemStatesList(itemStates); + + if (!itemStates.isEmpty()) { + if (!m_silentUpdate) { + emit infoMessage(i18nc("@info:status", "Updating version information...")); + } + m_updateItemStatesThread = new UpdateItemStatesThread(m_plugin, itemStates); + connect(m_updateItemStatesThread, SIGNAL(finished()), + this, SLOT(slotThreadFinished())); + connect(m_updateItemStatesThread, SIGNAL(finished()), + m_updateItemStatesThread, SLOT(deleteLater())); + + m_updateItemStatesThread->start(); // slotThreadFinished() is called when finished + } +} + +int VersionControlObserver::createItemStatesList(QMap >& itemStates, + const int firstIndex) +{ + const int itemCount = m_model->count(); + const int currentExpansionLevel = m_model->expandedParentsCount(firstIndex); + + QVector items; + items.reserve(itemCount - firstIndex); + + int index; + for (index = firstIndex; index < itemCount; ++index) { + const int expansionLevel = m_model->expandedParentsCount(index); + + if (expansionLevel == currentExpansionLevel) { + ItemState itemState; + itemState.item = m_model->fileItem(index); + itemState.version = KVersionControlPlugin::UnversionedVersion; + + items.append(itemState); + } else if (expansionLevel > currentExpansionLevel) { + // Sub folder + index += createItemStatesList(itemStates, index) - 1; + } else { + break; + } + } + + if (items.count() > 0) { + const KUrl& url = items.first().item.url(); + itemStates.insert(url.directory(KUrl::AppendTrailingSlash), items); + } + + return index - firstIndex; // number of processed items +} + +KVersionControlPlugin* VersionControlObserver::searchPlugin(const KUrl& directory) const +{ + static bool pluginsAvailable = true; + static QList plugins; + + if (!pluginsAvailable) { + // A searching for plugins has already been done, but no + // plugins are installed + return 0; + } + + if (plugins.isEmpty()) { + // No searching for plugins has been done yet. Query the KServiceTypeTrader for + // all fileview version control plugins and remember them in 'plugins'. + const QStringList enabledPlugins = VersionControlSettings::enabledPlugins(); + + const KService::List pluginServices = KServiceTypeTrader::self()->query("FileViewVersionControlPlugin"); + for (KService::List::ConstIterator it = pluginServices.constBegin(); it != pluginServices.constEnd(); ++it) { + if (enabledPlugins.contains((*it)->name())) { + KVersionControlPlugin* plugin = (*it)->createInstance(); + if (plugin) { + plugins.append(plugin); + } + } + } + if (plugins.isEmpty()) { + pluginsAvailable = false; + return 0; + } + } + + // We use the number of upUrl() calls to find the best matching plugin + // for the given directory. The smaller value, the better it is (0 is best). + KVersionControlPlugin* bestPlugin = 0; + int bestScore = INT_MAX; + + // Verify whether the current directory contains revision information + // like .svn, .git, ... + foreach (KVersionControlPlugin* plugin, plugins) { + const QString fileName = directory.path(KUrl::AddTrailingSlash) + plugin->fileName(); + if (QFileInfo(fileName).exists()) { + // The score of this plugin is 0 (best), so we can just return this plugin, + // instead of going through the plugin scoring procedure, we can't find a better one ;) + return plugin; + } + + // Version control systems like Git provide the version information + // file only in the root directory. Check whether the version information file can + // be found in one of the parent directories. For performance reasons this + // step is only done, if the previous directory was marked as versioned by + // m_versionedDirectory. Drawback: Until e. g. Git is recognized, the root directory + // must be shown at least once. + if (m_versionedDirectory) { + KUrl dirUrl(directory); + KUrl upUrl = dirUrl.upUrl(); + int upUrlCounter = 1; + while ((upUrlCounter < bestScore) && (upUrl != dirUrl)) { + const QString fileName = dirUrl.path(KUrl::AddTrailingSlash) + plugin->fileName(); + if (QDir(fileName).exists()) { + if (upUrlCounter < bestScore) { + bestPlugin = plugin; + bestScore = upUrlCounter; + } + break; + } + dirUrl = upUrl; + upUrl = dirUrl.upUrl(); + ++upUrlCounter; + } + } + } + + return bestPlugin; +} + +bool VersionControlObserver::isVersioned() const +{ + return m_versionedDirectory && m_plugin; +} + +#include "moc_versioncontrolobserver.cpp" diff --git a/dolphin/src/views/versioncontrol/versioncontrolobserver.h b/dolphin/src/views/versioncontrol/versioncontrolobserver.h new file mode 100644 index 00000000..9f6d06d4 --- /dev/null +++ b/dolphin/src/views/versioncontrol/versioncontrolobserver.h @@ -0,0 +1,155 @@ +/*************************************************************************** + * Copyright (C) 2009 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef VERSIONCONTROLOBSERVER_H +#define VERSIONCONTROLOBSERVER_H + +#include + +#include +#include +#include +#include +#include +#include + +class KFileItemList; +class KFileItemModel; +#include +#include +class UpdateItemStatesThread; + +/** + * @brief Observes all version control plugins. + * + * The items of the directory-model get updated automatically if the currently + * shown directory is under version control. + * + * @see VersionControlPlugin + */ +class DOLPHINPRIVATE_EXPORT VersionControlObserver : public QObject +{ + Q_OBJECT + +public: + explicit VersionControlObserver(QObject* parent = 0); + virtual ~VersionControlObserver(); + + void setModel(KFileItemModel* model); + KFileItemModel* model() const; + + QList actions(const KFileItemList& items) const; + +signals: + /** + * Is emitted if an information message with the content \a msg + * should be shown. + */ + void infoMessage(const QString& msg); + + /** + * Is emitted if an error message with the content \a msg + * should be shown. + */ + void errorMessage(const QString& msg); + + /** + * Is emitted if an "operation completed" message with the content \a msg + * should be shown. + */ + void operationCompletedMessage(const QString& msg); + +private slots: + /** + * Invokes verifyDirectory() with a small delay. If delayedDirectoryVerification() + * is invoked before the delay has been exceeded, the delay will be reset. This + * assures that a lot of short requests for directory verification only result + * in one (expensive) call. + */ + void delayedDirectoryVerification(); + + /** + * Invokes verifyDirectory() with a small delay. In opposite to + * delayedDirectoryVerification() it and assures that the verification of + * the directory is done silently without information messages. + */ + void silentDirectoryVerification(); + + void verifyDirectory(); + + /** + * Is invoked if the thread m_updateItemStatesThread has been finished + * and applys the item states. + */ + void slotThreadFinished(); + +private: + struct ItemState + { + KFileItem item; + KVersionControlPlugin::ItemVersion version; + }; + + void updateItemStates(); + + /** + * It creates a item state list for every expanded directory and stores + * this list together with the directory url in the \a itemStates map. + * + * @itemStates A map of item state lists for every expanded directory + * and its items, where the "key" is the directory url and + * the "value" is a list of ItemStates for every item + * within this directory. + * @firstIndex The index to start the processing from, this is needed + * because this function is recursively called. + * + * @return The number of (recursive) processed items. + */ + int createItemStatesList(QMap >& itemStates, + const int firstIndex = 0); + + /** + * Returns a matching plugin for the given directory. + * 0 is returned, if no matching plugin has been found. + */ + KVersionControlPlugin* searchPlugin(const KUrl& directory) const; + + /** + * Returns true, if the directory contains a version control information. + */ + bool isVersioned() const; + +private: + bool m_pendingItemStatesUpdate; + bool m_versionedDirectory; + bool m_silentUpdate; // if true, no messages will be send during the update + // of version states + + KFileItemModel* m_model; + + QTimer* m_dirVerificationTimer; + + KVersionControlPlugin* m_plugin; + UpdateItemStatesThread* m_updateItemStatesThread; + + friend class UpdateItemStatesThread; +}; + +#endif // REVISIONCONTROLOBSERVER_H + diff --git a/dolphin/src/views/viewmodecontroller.cpp b/dolphin/src/views/viewmodecontroller.cpp new file mode 100644 index 00000000..ff7e6558 --- /dev/null +++ b/dolphin/src/views/viewmodecontroller.cpp @@ -0,0 +1,88 @@ +/*************************************************************************** + * Copyright (C) 2010 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "viewmodecontroller.h" + +#include "zoomlevelinfo.h" + +ViewModeController::ViewModeController(QObject* parent) : + QObject(parent), + m_zoomLevel(0), + m_nameFilter(), + m_url() +{ +} + +ViewModeController::~ViewModeController() +{ +} + +KUrl ViewModeController::url() const +{ + return m_url; +} + +void ViewModeController::redirectToUrl(const KUrl& url) +{ + m_url = url; +} + +void ViewModeController::indicateActivationChange(bool active) +{ + emit activationChanged(active); +} + +void ViewModeController::setNameFilter(const QString& nameFilter) +{ + if (nameFilter != m_nameFilter) { + m_nameFilter = nameFilter; + emit nameFilterChanged(nameFilter); + } +} + +QString ViewModeController::nameFilter() const +{ + return m_nameFilter; +} + +void ViewModeController::setZoomLevel(int level) +{ + Q_ASSERT(level >= ZoomLevelInfo::minimumLevel()); + Q_ASSERT(level <= ZoomLevelInfo::maximumLevel()); + if (level != m_zoomLevel) { + m_zoomLevel = level; + emit zoomLevelChanged(m_zoomLevel); + } +} + +int ViewModeController::zoomLevel() const +{ + return m_zoomLevel; +} + +void ViewModeController::setUrl(const KUrl& url) +{ + if (m_url != url) { + m_url = url; + emit cancelPreviews(); + emit urlChanged(url); + } +} + +#include "moc_viewmodecontroller.cpp" diff --git a/dolphin/src/views/viewmodecontroller.h b/dolphin/src/views/viewmodecontroller.h new file mode 100644 index 00000000..b3f80441 --- /dev/null +++ b/dolphin/src/views/viewmodecontroller.h @@ -0,0 +1,124 @@ +/*************************************************************************** + * Copyright (C) 2010 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef VIEWMODECONTROLLER_H +#define VIEWMODECONTROLLER_H + +#include +#include +#include +#include + +/** + * @brief Allows the DolphinView to control the view implementations for the + * different view modes. + * + * The view implementations (DolphinIconsView, DolphinDetailsView, DolphinColumnView) + * connect to signals of the ViewModeController to react on changes. The view + * implementations get only read-access to the ViewModeController. + */ +class DOLPHINPRIVATE_EXPORT ViewModeController : public QObject +{ + Q_OBJECT + +public: + explicit ViewModeController(QObject* parent = 0); + virtual ~ViewModeController(); + + /** + * @return URL that is shown by the view mode implementation. + */ + KUrl url() const; + + /** + * Sets the URL to \a url and does nothing else. Called when + * a redirection happens. See ViewModeController::setUrl() + */ + void redirectToUrl(const KUrl& url); + + /** + * Informs the view mode implementation about a change of the activation + * state by emitting the signal activationChanged(). + */ + void indicateActivationChange(bool active); + + /** + * Sets the zoom level to \a level and emits the signal zoomLevelChanged(). + * It must be assured that the used level is inside the range + * ViewModeController::zoomLevelMinimum() and + * ViewModeController::zoomLevelMaximum(). + */ + void setZoomLevel(int level); + int zoomLevel() const; + + /** + * Sets the name filter to \a and emits the signal nameFilterChanged(). + */ + void setNameFilter(const QString& nameFilter); + QString nameFilter() const; + + /** + * Requests the view mode implementation to hide tooltips. + */ + void requestToolTipHiding(); + +public slots: + /** + * Sets the URL to \a url and emits the signals cancelPreviews() and + * urlChanged() if \a url is different for the current URL. + */ + void setUrl(const KUrl& url); + +signals: + /** + * Is emitted if the URL has been changed by ViewModeController::setUrl(). + */ + void urlChanged(const KUrl& url); + + /** + * Is emitted, if ViewModeController::indicateActivationChange() has been + * invoked. The view mode implementation may update its visual state + * to represent the activation state. + */ + void activationChanged(bool active); + + /** + * Is emitted if the name filter has been changed by + * ViewModeController::setNameFilter(). + */ + void nameFilterChanged(const QString& nameFilter); + + /** + * Is emitted if the zoom level has been changed by + * ViewModeController::setZoomLevel(). + */ + void zoomLevelChanged(int level); + + /** + * Is emitted if pending previews should be canceled (e. g. because of an URL change). + */ + void cancelPreviews(); + +private: + int m_zoomLevel; + QString m_nameFilter; + KUrl m_url; +}; + +#endif diff --git a/dolphin/src/views/viewproperties.cpp b/dolphin/src/views/viewproperties.cpp new file mode 100644 index 00000000..866182ce --- /dev/null +++ b/dolphin/src/views/viewproperties.cpp @@ -0,0 +1,472 @@ +/*************************************************************************** + * Copyright (C) 2006-2010 by Peter Penz * + * Copyright (C) 2006 by Aaron J. Seigo * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "viewproperties.h" + +#include "dolphin_directoryviewpropertysettings.h" +#include "dolphin_generalsettings.h" + +#include +#include +#include +#include + +#include +#include +#include + +namespace { + const int AdditionalInfoViewPropertiesVersion = 1; + const int NameRolePropertiesVersion = 2; + const int CurrentViewPropertiesVersion = 3; + + // String representation to mark the additional properties of + // the details view as customized by the user. See + // ViewProperties::visibleRoles() for more information. + const char* CustomizedDetailsString = "CustomizedDetails"; + + // Filename that is used for storing the properties + const char* ViewPropertiesFileName = ".directory"; +} + +ViewProperties::ViewProperties(const KUrl& url) : + m_changedProps(false), + m_autoSave(true), + m_node(0) +{ + GeneralSettings* settings = GeneralSettings::self(); + const bool useGlobalViewProps = settings->globalViewProps() || url.isEmpty(); + bool useDetailsViewWithPath = false; + + // We try and save it to the file .directory in the directory being viewed. + // If the directory is not writable by the user or the directory is not local, + // we store the properties information in a local file. + if (useGlobalViewProps) { + m_filePath = destinationDir("global"); + } else if (url.protocol() == QLatin1String("filenamesearch")) { + m_filePath = destinationDir("search/") + directoryHashForUrl(url); + useDetailsViewWithPath = true; + } else if (url.protocol() == QLatin1String("trash")) { + m_filePath = destinationDir("trash"); + useDetailsViewWithPath = true; + } else if (url.isLocalFile()) { + m_filePath = url.toLocalFile(); + const QFileInfo dirInfo(m_filePath); + const QFileInfo fileInfo(m_filePath + QDir::separator() + ViewPropertiesFileName); + // Check if the directory is writable and check if the ".directory" file exists and + // is read- and writable. + if (!dirInfo.isWritable() + || (fileInfo.exists() && !(fileInfo.isReadable() && fileInfo.isWritable())) + || !isPartOfHome(m_filePath)) { + m_filePath = destinationDir("local") + m_filePath; + } + } else { + m_filePath = destinationDir("remote") + m_filePath; + } + + const QString file = m_filePath + QDir::separator() + ViewPropertiesFileName; + m_node = new ViewPropertySettings(KSharedConfig::openConfig(file)); + + // If the .directory file does not exist or the timestamp is too old, + // use default values instead. + const bool useDefaultProps = (!useGlobalViewProps || useDetailsViewWithPath) && + (!QFile::exists(file) || + (m_node->timestamp() < settings->viewPropsTimestamp())); + if (useDefaultProps) { + if (useDetailsViewWithPath) { + setViewMode(DolphinView::DetailsView); + setVisibleRoles(QList() << "path"); + } else { + // The global view-properties act as default for directories without + // any view-property configuration. Constructing a ViewProperties + // instance for an empty KUrl ensures that the global view-properties + // are loaded. + KUrl emptyUrl; + ViewProperties defaultProps(emptyUrl); + setDirProperties(defaultProps); + + m_changedProps = false; + } + } + + if (m_node->version() < CurrentViewPropertiesVersion) { + // The view-properties have an outdated version. Convert the properties + // to the changes of the current version. + if (m_node->version() < AdditionalInfoViewPropertiesVersion) { + convertAdditionalInfo(); + Q_ASSERT(m_node->version() == AdditionalInfoViewPropertiesVersion); + } + + if (m_node->version() < NameRolePropertiesVersion) { + convertNameRoleToTextRole(); + Q_ASSERT(m_node->version() == NameRolePropertiesVersion); + } + + m_node->setVersion(CurrentViewPropertiesVersion); + } +} + +ViewProperties::~ViewProperties() +{ + if (m_changedProps && m_autoSave) { + save(); + } + + delete m_node; + m_node = 0; +} + +void ViewProperties::setViewMode(DolphinView::Mode mode) +{ + if (m_node->viewMode() != mode) { + m_node->setViewMode(mode); + update(); + } +} + +DolphinView::Mode ViewProperties::viewMode() const +{ + const int mode = qBound(0, m_node->viewMode(), 2); + return static_cast(mode); +} + +void ViewProperties::setPreviewsShown(bool show) +{ + if (m_node->previewsShown() != show) { + m_node->setPreviewsShown(show); + update(); + } +} + +bool ViewProperties::previewsShown() const +{ + return m_node->previewsShown(); +} + +void ViewProperties::setHiddenFilesShown(bool show) +{ + if (m_node->hiddenFilesShown() != show) { + m_node->setHiddenFilesShown(show); + update(); + } +} + +void ViewProperties::setGroupedSorting(bool grouped) +{ + if (m_node->groupedSorting() != grouped) { + m_node->setGroupedSorting(grouped); + update(); + } +} + +bool ViewProperties::groupedSorting() const +{ + return m_node->groupedSorting(); +} + +bool ViewProperties::hiddenFilesShown() const +{ + return m_node->hiddenFilesShown(); +} + +void ViewProperties::setSortRole(const QByteArray& role) +{ + if (m_node->sortRole() != role) { + m_node->setSortRole(role); + update(); + } +} + +QByteArray ViewProperties::sortRole() const +{ + return m_node->sortRole().toLatin1(); +} + +void ViewProperties::setSortOrder(Qt::SortOrder sortOrder) +{ + if (m_node->sortOrder() != sortOrder) { + m_node->setSortOrder(sortOrder); + update(); + } +} + +Qt::SortOrder ViewProperties::sortOrder() const +{ + return static_cast(m_node->sortOrder()); +} + +void ViewProperties::setSortFoldersFirst(bool foldersFirst) +{ + if (m_node->sortFoldersFirst() != foldersFirst) { + m_node->setSortFoldersFirst(foldersFirst); + update(); + } +} + +bool ViewProperties::sortFoldersFirst() const +{ + return m_node->sortFoldersFirst(); +} + +void ViewProperties::setVisibleRoles(const QList& roles) +{ + if (roles == visibleRoles()) { + return; + } + + // See ViewProperties::visibleRoles() for the storage format + // of the additional information. + + // Remove the old values stored for the current view-mode + const QStringList oldVisibleRoles = m_node->visibleRoles(); + const QString prefix = viewModePrefix(); + QStringList newVisibleRoles = oldVisibleRoles; + for (int i = newVisibleRoles.count() - 1; i >= 0; --i) { + if (newVisibleRoles[i].startsWith(prefix)) { + newVisibleRoles.removeAt(i); + } + } + + // Add the updated values for the current view-mode + foreach (const QByteArray& role, roles) { + newVisibleRoles.append(prefix + role); + } + + if (oldVisibleRoles != newVisibleRoles) { + const bool markCustomizedDetails = (m_node->viewMode() == DolphinView::DetailsView) + && !newVisibleRoles.contains(CustomizedDetailsString); + if (markCustomizedDetails) { + // The additional information of the details-view has been modified. Set a marker, + // so that it is allowed to also show no additional information without doing the + // fallback to show the size and date per default. + newVisibleRoles.append(CustomizedDetailsString); + } + + m_node->setVisibleRoles(newVisibleRoles); + update(); + } +} + +QList ViewProperties::visibleRoles() const +{ + // The shown additional information is stored for each view-mode separately as + // string with the view-mode as prefix. Example: + // + // AdditionalInfo=Details_size,Details_date,Details_owner,Icons_size + // + // To get the representation as QList, the current + // view-mode must be checked and the values of this mode added to the list. + // + // For the details-view a special case must be respected: Per default the size + // and date should be shown without creating a .directory file. Only if + // the user explictly has modified the properties of the details view (marked + // by "CustomizedDetails"), also a details-view with no additional information + // is accepted. + + QList roles; + roles.append("text"); + + // Iterate through all stored keys and append all roles that match to + // the current view mode. + const QString prefix = viewModePrefix(); + const int prefixLength = prefix.length(); + + const QStringList visibleRoles = m_node->visibleRoles(); + foreach (const QString& visibleRole, visibleRoles) { + if (visibleRole.startsWith(prefix)) { + const QByteArray role = visibleRole.right(visibleRole.length() - prefixLength).toLatin1(); + if (role != "text") { + roles.append(role); + } + } + } + + // For the details view the size and date should be shown per default + // until the additional information has been explicitly changed by the user + const bool useDefaultValues = roles.count() == 1 // "text" + && (m_node->viewMode() == DolphinView::DetailsView) + && !visibleRoles.contains(CustomizedDetailsString); + if (useDefaultValues) { + roles.append("size"); + roles.append("date"); + } + + return roles; +} + +void ViewProperties::setHeaderColumnWidths(const QList& widths) +{ + if (m_node->headerColumnWidths() != widths) { + m_node->setHeaderColumnWidths(widths); + update(); + } +} + +QList ViewProperties::headerColumnWidths() const +{ + return m_node->headerColumnWidths(); +} + +void ViewProperties::setDirProperties(const ViewProperties& props) +{ + setViewMode(props.viewMode()); + setPreviewsShown(props.previewsShown()); + setHiddenFilesShown(props.hiddenFilesShown()); + setGroupedSorting(props.groupedSorting()); + setSortRole(props.sortRole()); + setSortOrder(props.sortOrder()); + setSortFoldersFirst(props.sortFoldersFirst()); + setVisibleRoles(props.visibleRoles()); + setHeaderColumnWidths(props.headerColumnWidths()); + m_node->setVersion(props.m_node->version()); +} + +void ViewProperties::setAutoSaveEnabled(bool autoSave) +{ + m_autoSave = autoSave; +} + +bool ViewProperties::isAutoSaveEnabled() const +{ + return m_autoSave; +} + +void ViewProperties::update() +{ + m_changedProps = true; + m_node->setTimestamp(QDateTime::currentDateTime()); +} + +void ViewProperties::save() +{ + kDebug() << "Saving view-properties to" << m_filePath; + KStandardDirs::makeDir(m_filePath); + m_node->setVersion(CurrentViewPropertiesVersion); + m_node->writeConfig(); + m_changedProps = false; +} + +bool ViewProperties::exist() const +{ + const QString file = m_filePath + QDir::separator() + ViewPropertiesFileName; + return QFile::exists(file); +} + +QString ViewProperties::destinationDir(const QString& subDir) const +{ + QString basePath = KGlobal::mainComponent().componentName(); + basePath.append("/view_properties/").append(subDir); + return KStandardDirs::locateLocal("data", basePath); +} + +QString ViewProperties::viewModePrefix() const +{ + QString prefix; + + switch (m_node->viewMode()) { + case DolphinView::IconsView: prefix = "Icons_"; break; + case DolphinView::CompactView: prefix = "Compact_"; break; + case DolphinView::DetailsView: prefix = "Details_"; break; + default: kWarning() << "Unknown view-mode of the view properties"; + } + + return prefix; +} + +void ViewProperties::convertAdditionalInfo() +{ + QStringList visibleRoles; + + const QStringList additionalInfo = m_node->additionalInfo(); + if (!additionalInfo.isEmpty()) { + // Convert the obsolete values like Icons_Size, Details_Date, ... + // to Icons_size, Details_date, ... where the suffix just represents + // the internal role. One special-case must be handled: "LinkDestination" + // has been used for "destination". + visibleRoles.reserve(additionalInfo.count()); + foreach (const QString& info, additionalInfo) { + QString visibleRole = info; + int index = visibleRole.indexOf('_'); + if (index >= 0 && index + 1 < visibleRole.length()) { + ++index; + if (visibleRole[index] == QLatin1Char('L')) { + visibleRole.replace("LinkDestination", "destination"); + } else { + visibleRole[index] = visibleRole[index].toLower(); + } + } + visibleRoles.append(visibleRole); + } + } + + m_node->setAdditionalInfo(QStringList()); + m_node->setVisibleRoles(visibleRoles); + m_node->setVersion(AdditionalInfoViewPropertiesVersion); + update(); +} + +void ViewProperties::convertNameRoleToTextRole() +{ + QStringList visibleRoles = m_node->visibleRoles(); + for (int i = 0; i < visibleRoles.count(); ++i) { + if (visibleRoles[i].endsWith(QLatin1String("_name"))) { + const int leftLength = visibleRoles[i].length() - 5; + visibleRoles[i] = visibleRoles[i].left(leftLength) + "_text"; + } + } + + QString sortRole = m_node->sortRole(); + if (sortRole == QLatin1String("name")) { + sortRole = QLatin1String("text"); + } + + m_node->setVisibleRoles(visibleRoles); + m_node->setSortRole(sortRole); + m_node->setVersion(NameRolePropertiesVersion); + update(); +} + +bool ViewProperties::isPartOfHome(const QString& filePath) +{ + // For performance reasons cache the path in a static QString + // (see QDir::homePath() for more details) + static QString homePath; + if (homePath.isEmpty()) { + homePath = QDir::homePath(); + Q_ASSERT(!homePath.isEmpty()); + } + + return filePath.startsWith(homePath); +} + +QString ViewProperties::directoryHashForUrl(const KUrl& url) +{ + const QByteArray hashValue = url.prettyUrl().toLatin1().toBase64(); + QString hashString = hashValue.toBase64(); + hashString.replace('/', '-'); + return hashString; +} + +KUrl ViewProperties::mirroredDirectory() +{ + QString basePath = KGlobal::mainComponent().componentName(); + basePath.append("/view_properties/"); + return KUrl(KStandardDirs::locateLocal("data", basePath)); +} diff --git a/dolphin/src/views/viewproperties.h b/dolphin/src/views/viewproperties.h new file mode 100644 index 00000000..48f302e2 --- /dev/null +++ b/dolphin/src/views/viewproperties.h @@ -0,0 +1,185 @@ +/*************************************************************************** + * Copyright (C) 2006-2010 by Peter Penz * + * Copyright (C) 2006 by Aaron J. Seigo * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef VIEWPROPERTIES_H +#define VIEWPROPERTIES_H + +#include +#include +#include + +class ViewPropertySettings; +/** + * @brief Maintains the view properties like 'view mode' or + * 'show hidden files' for a directory. + * + * The view properties are automatically stored as part of the file + * .directory inside the corresponding path. To read out the view properties + * just construct an instance by passing the path of the directory: + * + * \code + * ViewProperties props(KUrl("/home/peter/Documents")); + * const DolphinView::Mode mode = props.viewMode(); + * const bool hiddenFilesShown = props.hiddenFilesShown(); + * \endcode + * + * When modifying a view property, the '.directory' file is automatically updated + * inside the destructor. + * + * If no .directory file is available or the global view mode is turned on + * (see GeneralSettings::globalViewMode()), the values from the global .directory file + * are used for initialization. + */ +class DOLPHINPRIVATE_EXPORT ViewProperties +{ +public: + explicit ViewProperties(const KUrl& url); + virtual ~ViewProperties(); + + void setViewMode(DolphinView::Mode mode); + DolphinView::Mode viewMode() const; + + void setPreviewsShown(bool show); + bool previewsShown() const; + + void setHiddenFilesShown(bool show); + bool hiddenFilesShown() const; + + void setGroupedSorting(bool grouped); + bool groupedSorting() const; + + void setSortRole(const QByteArray& role); + QByteArray sortRole() const; + + void setSortOrder(Qt::SortOrder sortOrder); + Qt::SortOrder sortOrder() const; + + void setSortFoldersFirst(bool foldersFirst); + bool sortFoldersFirst() const; + + /** + * Sets the additional information for the current set view-mode. + * Note that the additional-info property is the only property where + * the value is dependent from another property (in this case the view-mode). + */ + void setVisibleRoles(const QList& info); + + /** + * Returns the additional information for the current set view-mode. + * Note that the additional-info property is the only property where + * the value is dependent from another property (in this case the view-mode). + */ + QList visibleRoles() const; + + void setHeaderColumnWidths(const QList& widths); + QList headerColumnWidths() const; + + /** + * Sets the directory properties view mode, show preview, + * show hidden files, sorting and sort order like + * set in \a props. + */ + void setDirProperties(const ViewProperties& props); + + /** + * If \a autoSave is true, the properties are automatically + * saved when the destructor is called. Per default autosaving + * is enabled. + */ + void setAutoSaveEnabled(bool autoSave); + bool isAutoSaveEnabled() const; + + void update(); + + /** + * Saves the view properties for the directory specified + * in the constructor. The method is automatically + * invoked in the destructor, if + * ViewProperties::isAutoSaveEnabled() returns true and + * at least one property has been changed. + */ + void save(); + + /** + * @return True if properties for the given URL exist: + * As soon as the properties for an URL have been saved with + * ViewProperties::save(), true will be returned. If false is + * returned, the default view-properties are used. + */ + bool exist() const; + +private: + /** + * Returns the destination directory path where the view + * properties are stored. \a subDir specifies the used sub + * directory. + */ + QString destinationDir(const QString& subDir) const; + + /** + * Returns the view-mode prefix when storing additional properties for + * a view-mode. + */ + QString viewModePrefix() const; + + /** + * Provides backward compatibility with .directory files created with + * Dolphin < 2.0: Converts the old additionalInfo-property into + * the visibleRoles-property and clears the additionalInfo-property. + */ + void convertAdditionalInfo(); + + /** + * Provides backward compatibility with .directory files created with + * Dolphin < 2.1: Converts the old name-role "name" to the generic + * role "text". + */ + void convertNameRoleToTextRole(); + + /** + * Returns true, if \a filePath is part of the home-path (see QDir::homePath()). + */ + static bool isPartOfHome(const QString& filePath); + + /** + * @return A hash-value for an URL that can be used as directory name. + * Is used to be able to remember view-properties for long baloo-URLs. + */ + static QString directoryHashForUrl(const KUrl& url); + + /** + * Returns the URL of the directory, where the mirrored view properties + * are stored into. Mirrored view properties are used if: + * - there is no write access for storing the view properties into + * the original directory + * - for non local directories + */ + static KUrl mirroredDirectory(); + + Q_DISABLE_COPY(ViewProperties) + +private: + bool m_changedProps; + bool m_autoSave; + QString m_filePath; + ViewPropertySettings* m_node; +}; + +#endif diff --git a/dolphin/src/views/zoomlevelinfo.cpp b/dolphin/src/views/zoomlevelinfo.cpp new file mode 100644 index 00000000..faca8fd5 --- /dev/null +++ b/dolphin/src/views/zoomlevelinfo.cpp @@ -0,0 +1,60 @@ +/*************************************************************************** + * Copyright (C) 2008 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "zoomlevelinfo.h" +#include +#include + +int ZoomLevelInfo::minimumLevel() +{ + return 0; +} + +int ZoomLevelInfo::maximumLevel() +{ + return 16; +} + +int ZoomLevelInfo::iconSizeForZoomLevel(int level) +{ + int size = KIconLoader::SizeMedium; + switch (level) { + case 0: size = KIconLoader::SizeSmall; break; + case 1: size = KIconLoader::SizeSmallMedium; break; + case 2: size = KIconLoader::SizeMedium; break; + case 3: size = KIconLoader::SizeLarge; break; + case 4: size = KIconLoader::SizeHuge; break; + default: size = KIconLoader::SizeHuge + ((level - 4) << 4); + } + return size; +} + +int ZoomLevelInfo::zoomLevelForIconSize(const QSize& size) +{ + int level = 0; + switch (size.height()) { + case KIconLoader::SizeSmall: level = 0; break; + case KIconLoader::SizeSmallMedium: level = 1; break; + case KIconLoader::SizeMedium: level = 2; break; + case KIconLoader::SizeLarge: level = 3; break; + case KIconLoader::SizeHuge: level = 4; break; + default: level = 4 + ((size.height() - KIconLoader::SizeHuge) >> 4); + } + return level; +} diff --git a/dolphin/src/views/zoomlevelinfo.h b/dolphin/src/views/zoomlevelinfo.h new file mode 100644 index 00000000..6b436860 --- /dev/null +++ b/dolphin/src/views/zoomlevelinfo.h @@ -0,0 +1,51 @@ +/*************************************************************************** + * Copyright (C) 2008 by Peter Penz * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef ZOOMLEVELINFO_H +#define ZOOMLEVELINFO_H + +#include + +/** + * @short Helper class for getting information about the zooming + * capabilities. + */ +class ZoomLevelInfo { +public: + static int minimumLevel(); + static int maximumLevel(); + + /** + * Helper method for the view implementation to get + * the icon size for the zoom level \a level that + * is between the range ZoomLevelInfo::minimumLevel() and + * ZoomLevelInfo::maximumLevel(). + */ + static int iconSizeForZoomLevel(int level); + + /** + * Helper method for the view implementation to get + * the zoom level for the icon size \a size that + * is between the range ZoomLevelInfo::minimumLevel() and + * ZoomLevelInfo::maximumLevel(). + */ + static int zoomLevelForIconSize(const QSize& size); +}; + +#endif diff --git a/kate/.kateconfig b/kate/.kateconfig new file mode 100644 index 00000000..5b0885ab --- /dev/null +++ b/kate/.kateconfig @@ -0,0 +1 @@ +kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/AUTHORS b/kate/AUTHORS new file mode 100644 index 00000000..b3b1c238 --- /dev/null +++ b/kate/AUTHORS @@ -0,0 +1,63 @@ +# +# The KatePart/Kate/KWrite Team +# + +This list is not exhaustive, if you think you are missing and you contributed some +content of kate.git, please feel free to add yourself here and in the corresponding +about page. + +- Christoph Cullmann + (Maintainer) + +- Joseph Wenninger + (highlight guy + big bug hunter) + +- Anders Lund + (bookmarks, iconborder, very cool split view, ...) + +- Hamish Rodda + (dynamic wrap, view, optimise, etc) + +- Waldo Bastian + (improved buffer handling) + +- Charles Samuels + (regular expressions) + +- Michael Bartl + (first one who really helped out :) + +- Matt Newell + +- Michael McCallum + +- John Firebaugh + +- Nadeem Hasan + +- Erlend Hamberg + (vi input mode) + +- Jochen Wilhelmy + (original author of kwrite in kde 1.x until kde 2.1) + +- Glen Parker + (undo history, kspell-integration) + +- Michael Koch + (port to KParts) + +- Dominik Haumann + (developer, highlight wizard) + +- Mirko Stocker + (various bugfixes) + +- Matthew Woehlke + (selection, KColorScheme integration) + +- Sebastian Pipping + (search bar back- and front-end) + +- Phlip + (project stuff) diff --git a/kate/CMakeLists.txt b/kate/CMakeLists.txt new file mode 100644 index 00000000..d329a15b --- /dev/null +++ b/kate/CMakeLists.txt @@ -0,0 +1,32 @@ +# Kate project +project (kate) + +# config.h +check_function_exists(fdatasync HAVE_FDATASYNC) +check_function_exists(ctermid HAVE_CTERMID) + +configure_file( + config.h.cmake + ${CMAKE_CURRENT_BINARY_DIR}/config.h +) + +# global include directories +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_BINARY_DIR}/src/app + ${KDE4_KIO_INCLUDES} +) + +# kate part +add_subdirectory(part) + +# kate application +add_subdirectory(src) + +# addons, e.g. ktexteditor plugins, kate plugins, plasma applets, ... +add_subdirectory(addons) + +# tests +if(ENABLE_TESTING) + add_subdirectory(tests) +endif() diff --git a/kate/COPYING-GPL3 b/kate/COPYING-GPL3 new file mode 100644 index 00000000..94a9ed02 --- /dev/null +++ b/kate/COPYING-GPL3 @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/kate/COPYING-LGPL3 b/kate/COPYING-LGPL3 new file mode 100644 index 00000000..65c5ca88 --- /dev/null +++ b/kate/COPYING-LGPL3 @@ -0,0 +1,165 @@ + GNU LESSER 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. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser 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 +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/kate/COPYING.LIB b/kate/COPYING.LIB new file mode 100644 index 00000000..a96b5730 --- /dev/null +++ b/kate/COPYING.LIB @@ -0,0 +1,482 @@ + GNU LIBRARY GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1991 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor + Boston, MA 02110-1301, USA. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the library GPL. It is + numbered 2 because it goes with version 2 of the ordinary GPL.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Library General Public License, applies to some +specially designated Free Software Foundation software, and to any +other libraries whose authors decide to use it. You can use it for +your libraries, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if +you distribute copies of the library, or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link a program with the library, you must provide +complete object files to the recipients so that they can relink them +with the library, after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + Our method of protecting your rights has two steps: (1) copyright +the library, and (2) offer you this license which gives you legal +permission to copy, distribute and/or modify the library. + + Also, for each distributor's protection, we want to make certain +that everyone understands that there is no warranty for this free +library. If the library is modified by someone else and passed on, we +want its recipients to know that what they have is not the original +version, so that any problems introduced by others will not reflect on +the original authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that companies distributing free +software will individually obtain patent licenses, thus in effect +transforming the program into proprietary software. To prevent this, +we have made it clear that any patent must be licensed for everyone's +free use or not licensed at all. + + Most GNU software, including some libraries, is covered by the ordinary +GNU General Public License, which was designed for utility programs. This +license, the GNU Library General Public License, applies to certain +designated libraries. This license is quite different from the ordinary +one; be sure to read it in full, and don't assume that anything in it is +the same as in the ordinary license. + + The reason we have a separate public license for some libraries is that +they blur the distinction we usually make between modifying or adding to a +program and simply using it. Linking a program with a library, without +changing the library, is in some sense simply using the library, and is +analogous to running a utility program or application program. However, in +a textual and legal sense, the linked executable is a combined work, a +derivative of the original library, and the ordinary General Public License +treats it as such. + + Because of this blurred distinction, using the ordinary General +Public License for libraries did not effectively promote software +sharing, because most developers did not use the libraries. We +concluded that weaker conditions might promote sharing better. + + However, unrestricted linking of non-free programs would deprive the +users of those programs of all benefit from the free status of the +libraries themselves. This Library General Public License is intended to +permit developers of non-free programs to use free libraries, while +preserving your freedom as a user of such programs to change the free +libraries that are incorporated in them. (We have not seen how to achieve +this as regards changes in header files, but we have achieved it as regards +changes in the actual functions of the Library.) The hope is that this +will lead to faster development of free libraries. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, while the latter only +works together with the library. + + Note that it is possible for a library to be covered by the ordinary +General Public License rather than by this special one. + + GNU LIBRARY GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library which +contains a notice placed by the copyright holder or other authorized +party saying it may be distributed under the terms of this Library +General Public License (also called "this License"). Each licensee is +addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also compile or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + c) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + d) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the source code distributed need not include anything that is normally +distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Library 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 Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "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 +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY 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 +LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/kate/CommitPolicy.txt b/kate/CommitPolicy.txt new file mode 100644 index 00000000..ecb1214c --- /dev/null +++ b/kate/CommitPolicy.txt @@ -0,0 +1,89 @@ +== THIS IS A DRAFT == + +This document is a work in progress, and may be incomplete and / or inaccurate. + +Please help improve it! + +== About this document == + +So you want to participate in developing kate? Great! The project is always in need of helping hands. + +However, trying to help, and being helpful is not always the same thing. Therefore, we would like to ask +you to read through this document, '''before''' you start comitting to this repository. We try to keep +this text short, and limit it to some of the most important pitfalls, only, and we will focus mostly on +issues which are specific to kate, as opposed to generic guidelines. '''Common sense applies.''' + +In writing this document, we will assume that you have developer access to the repository. However, if +you don't, the same basic rules apply for sending patches, too. + +== General rules == + +# If you have the slightest doubt, '''ask'''! +#* This is probably the most important rule. Communicate, discuss, ask questions. +#* For small technical questions, the fatest way to get an answer is typically IRC (irc://irc.kde.org/kate for the kate specific channel; often irc://irc.kde.org/kde-devel is also a good place to ask). +#* For questions that may need a broader discussion, or some time to think about, the mailing list is generally the best place to ask: kwrite-devel@kde.org . In fact, if you plan to contribute more than just once, it is highly recommended that you subscribe. +#* For feedback on a concrete patch, reviewboard.kde.org is suited, best. +# Understand what you are doing. +#* Do not commit code that you don't understand, even if it appears to fix problems. '''Ask''' instead (see above). +# Test you code. +#* Test your code to make sure it really does what you think it does. Then test that it doesn't break anything. Then test again. +# Respect schedules and agreements. +#* Make sure you are aware of the current [http://techbase.kde.org/Schedules Release Schedules], and the associated freezes in the different branches. +#* Generally it is a good idea to have a look at the last few weeks of the [http://lists.kde.org/?l=kwrite-devel&r=1&w=2 mailing list archive], to see if there may be any reasons against committing certain changes at that time. +#* If in any doubt: Ask. +# Respect coding conventions and style +#* Don't just write in your personal style. Tastes differ, and if we mix all sorts of styles, the code just gets harder to read. +#* In general, please try to follow the coding style of the surrounding code. For more in-depth information, refer to http://techbase.kde.org/Policies/Kdelibs_Coding_Style . +# Respect copyright and licences. +#* In the open source world, a lot of copying and re-use of code is permitted. But please make sure you have checked and understood the applicable terms, in each case. +#* If in any doubt: Ask. +# Use bugs.kde.org +#* If the bug you are about to fix has been annoying you for ages, probably others have noted it before, too. If you think the feature you're about to implement is a must have, then others might have had the same idea. Please be sure to check bugs.kde.org for existing reports / requests. This might contain important considerations that you did not think about, yet. And of course it is important to close the corresponding reports after the bug has been fixed / feature committed, in order to keep bug database managable. Taking some time for bookkeeping is an important part of developing in a community project. + + +== Kate specific rules == + +The main sub-directories of this repository are "kate", "kwrite", "part", and "ktexteditor". Very +differnt guidelines apply to these, so make sure you understand what is or is not allowed in each sub-project. + +=== kate / kwrite === + +These directories hold code that is specific to the stand-alone applications kate and kwrite. As such it is +application code, and this means that, generally, there is no need to care about source or binary compatibility. +Changes in this directory do not affect applications other than kate or kwrite, and thus they are ''relatively'' +safe. However, please be aware that both kate and kwrite are productivity tools, used extensively by a large userbase. +Often, these users will use kate/kwrite in ways that you have never even thought about, or use features, which even +core contributors may not be aware of. Please be careful that you don't break existing features and workflows, unless +you have very good reason to do so. If in any doubt: Ask. + +When implementing new features, please take a moment to think about whether they be limited to kate, or whether they +should be implemented in the kate part, in order to make them accessible to all applications using katepart. As a +generic rule of thumb: If the feature (or a similar feature) could be useful in kwrite, then it should probably +go into katepart. If the feature might be useful in applications such as kdevelop, kile, quanta, rkward, etc. then it +should probably be implemented in katepart. If in any doubt: Ask. + +=== part === + +This directory holds the implementation of the kate "part". We assume that you know what a KPart is (and if you don't, +then please read up on it), but as a short summary: Any KDE application can embed this part, in order to gain advanced +text-editor features in very few lines of code. And many, many applications do, inside and outside of kde.org. +These external applications are not compiled or linked against the katepart. Thus many concerns about source and binary +compatibility do not apply. However, several types of changes in katepart code can potentially have non-obvious +negative side-effects on applications that embed katepart. Thus, please be sure you understand the following issues, +and think twice before doing such changes: + +# Shortcuts +#* Embedding applications will typically inherit the complete GUI from katepart, including all shortcuts. More often than not, these applications will also define a considerable number of application-specific shortcuts in addition to that. This has a lot of potential to create clashes between shortcuts in the katepart, and the +embedding application. Thus, please think twice before adding new shortcuts, or changing the default keys for existing shortcuts. Ask yourself: Does this action really need a shortcut ''by default''? +# Action names +#* The names of actions and menus may seem like a fully internal implementation detail, on first glance. But they are not. In fact embedding applications sometimes have the need to hide specific actions, to re-arrange them, or to modify them in other ways (e.g. changing the text to avoid ambiguities, etc.). To do so, they will reference the actions by their id / name (not their text label). So please do not change existing action names without very strong reasons to do so. +#* The same point applies for names of merge points and groups in the ui.rc. +# Syntax highlighting names +#* Similarly, embedding applications may want to set a specific highlighting mode under some circumstances. To do so they will probably use KTextEditor::Document::setHighlightingMode(), which takes the name of the highlighting mode as parameter. Thus, please be very careful when changing names. +# Removing the implementation of KTextEditor Extension interfaces +#* Theoretically, embedding applications may not assume that a certain extension interface (such as KTextEditor::SmartInterface) is implemented by the embedded part. In practice they do. Thus, if you ever have the need to remove a certain interface implementation, be sure you announce this '''very''' visibly, and to allow ample time for embedding applications to adjust to the change. + +=== ktexteditor === + +'''This directory is actually part of kdelibs'''. It is synced with kdelibs, regularly, but technically it belongs to kdelibs (at least for KDE 4.x.y). Thus, any changes you do, here, ''must'' follow the kdelibs library code policy: http://techbase.kde.org/Policies/Library_Code_Policy . Before you touch anything, in this directory, please be sure to read and understand that in full. In particular you should have a firm undersanding of API and ABI compatibility issues. '''If in any doubt: Ask'''. +In any case, notify kwrite-devel about changes in that directory. diff --git a/kate/README b/kate/README new file mode 100644 index 00000000..337dd371 --- /dev/null +++ b/kate/README @@ -0,0 +1,13 @@ +Whats in kate.git? + +part/ + => KatePart + +kate/ + => Kate Application + +tests/ + => Automated unit tests + +examples/ + => Examples for syntax highlighting and co diff --git a/kate/addons/CMakeLists.txt b/kate/addons/CMakeLists.txt new file mode 100644 index 00000000..75d9379c --- /dev/null +++ b/kate/addons/CMakeLists.txt @@ -0,0 +1,8 @@ +# KTextEditor plugins, for the kate part +add_subdirectory(ktexteditor) + +# Kate plugins +add_subdirectory(kate) + +# Plasma applets +add_subdirectory(plasma) diff --git a/kate/addons/kate/CMakeLists.txt b/kate/addons/kate/CMakeLists.txt new file mode 100644 index 00000000..f06e1309 --- /dev/null +++ b/kate/addons/kate/CMakeLists.txt @@ -0,0 +1,29 @@ +# Kate interfaces used for the plugins +include_directories(${CMAKE_SOURCE_DIR}/kate/src/interfaces) + +# default debug area +add_definitions(-DKDE_DEFAULT_DEBUG_AREA=13040) + +add_subdirectory(textfilter) +add_subdirectory(konsole) + +add_subdirectory(filebrowser) +add_subdirectory(mailfiles) + +add_subdirectory(tabbarextension) + +add_subdirectory(filetemplates) + +add_subdirectory(kate-ctags) +add_subdirectory(katebuild-plugin) +add_subdirectory(search) + +add_subdirectory(kttsd) + +add_subdirectory(openheader) + +add_subdirectory(tabify) + +add_subdirectory(close-except-like) + +add_subdirectory(project) diff --git a/kate/addons/kate/close-except-like/.kateconfig b/kate/addons/kate/close-except-like/.kateconfig new file mode 100644 index 00000000..6049b6ed --- /dev/null +++ b/kate/addons/kate/close-except-like/.kateconfig @@ -0,0 +1 @@ +kate: space-indent on; tab-width 4; indent-width 4; replace-tabs on; hl C++/Qt4; diff --git a/kate/addons/kate/close-except-like/CMakeLists.txt b/kate/addons/kate/close-except-like/CMakeLists.txt new file mode 100644 index 00000000..16727b69 --- /dev/null +++ b/kate/addons/kate/close-except-like/CMakeLists.txt @@ -0,0 +1,33 @@ +set(VERSION_MAJOR 0) +set(VERSION_MINOR 3) +set(VERSION_PATCH 5) +set(VERSION_STRING ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}) + +set(KATE_CLOSE_EXCEPT_PLUGIN_SOURCES + close_confirm_dialog.cpp + close_except_plugin.cpp +) + +kde4_add_plugin(katecloseexceptplugin ${KATE_CLOSE_EXCEPT_PLUGIN_SOURCES}) + +target_link_libraries(katecloseexceptplugin + ${KDE4_KDEUI_LIBS} + ${KDE4_KFILE_LIBS} + ${KDE4_KTEXTEDITOR_LIBS} + kateinterfaces +) + +configure_file(config.h.in config.h) + +install( + TARGETS katecloseexceptplugin + DESTINATION ${KDE4_PLUGIN_INSTALL_DIR} +) +install( + FILES ui.rc + DESTINATION ${KDE4_DATA_INSTALL_DIR}/kate/plugins/katecloseexceptplugin +) +install( + FILES katecloseexceptplugin.desktop + DESTINATION ${KDE4_SERVICES_INSTALL_DIR} +) diff --git a/kate/addons/kate/close-except-like/Messages.sh b/kate/addons/kate/close-except-like/Messages.sh new file mode 100644 index 00000000..df117f92 --- /dev/null +++ b/kate/addons/kate/close-except-like/Messages.sh @@ -0,0 +1,2 @@ +#! /bin/sh +$XGETTEXT *.cpp -o $podir/katecloseexceptplugin.pot diff --git a/kate/addons/kate/close-except-like/close_confirm_dialog.cpp b/kate/addons/kate/close-except-like/close_confirm_dialog.cpp new file mode 100644 index 00000000..cf27d3f4 --- /dev/null +++ b/kate/addons/kate/close-except-like/close_confirm_dialog.cpp @@ -0,0 +1,138 @@ +/** + * \file + * + * \brief Class \c kate::CloseConfirmDialog (implementation) + * + * Copyright (C) 2012 Alex Turbov + * + * \date Sun Jun 24 16:29:13 MSK 2012 -- Initial design + */ +/* + * KateCloseExceptPlugin 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. + * + * KateCloseExceptPlugin 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 . + */ + +// Project specific includes +#include "close_confirm_dialog.h" + +// Standard includes +#include +#include /// \todo Where is \c i18n() defiend? +#include +#include +#include +#include + +namespace kate { namespace { + +class KateDocItem : public QTreeWidgetItem +{ + public: + KateDocItem(KTextEditor::Document* doc, QTreeWidget* tw) + : QTreeWidgetItem(tw) + , document(doc) + { + setText(0, doc->documentName()); + setText(1, doc->url().prettyUrl()); + setCheckState(0, Qt::Checked); + } + KTextEditor::Document* document; +}; +} // anonymous namespace + +CloseConfirmDialog::CloseConfirmDialog( + QList& docs + , KToggleAction* show_confirmation_action + , QWidget* const parent + ) + : KDialog(parent) + , m_docs(docs) +{ + assert("Documents container expected to be non empty" && !docs.isEmpty()); + + setCaption(i18nc("@title:window", "Close files confirmation")); + setButtons(Ok | Cancel); + setModal(true); + setDefaultButton(KDialog::Ok); + + KVBox* w = new KVBox(this); + setMainWidget(w); + w->setSpacing(KDialog::spacingHint()); + + KHBox* lo1 = new KHBox(w); + + // dialog text + QLabel* icon = new QLabel(lo1); + icon->setPixmap(DesktopIcon("dialog-warning")); + + QLabel* t = new QLabel( + i18nc("@label:listbox", "You are about to close the following documents:") + , lo1 + ); + lo1->setStretchFactor(t, 1000); + + // document list + m_docs_tree = new QTreeWidget(w); + QStringList headers; + headers << i18nc("@title:column", "Document") << i18nc("@title:column", "Location"); + m_docs_tree->setHeaderLabels(headers); + m_docs_tree->setSelectionMode(QAbstractItemView::SingleSelection); + m_docs_tree->setRootIsDecorated(false); + + for (int i = 0; i < m_docs.size(); i++) + { + new KateDocItem(m_docs[i], m_docs_tree); + } + m_docs_tree->header()->setStretchLastSection(false); + m_docs_tree->header()->setResizeMode(0, QHeaderView::ResizeToContents); + m_docs_tree->header()->setResizeMode(1, QHeaderView::ResizeToContents); + + m_dont_ask_again = new QCheckBox(i18nc("option:check", "Do not ask again"), w); + // NOTE If we are here, it means that 'Show Confirmation' action is enabled, + // so not needed to read config... + assert("Sanity check" && show_confirmation_action->isChecked()); + m_dont_ask_again->setCheckState(Qt::Unchecked); + connect(m_dont_ask_again, SIGNAL(toggled(bool)), show_confirmation_action, SLOT(toggle())); + + // Update documents list according checkboxes + connect(this, SIGNAL(accepted()), this, SLOT(updateDocsList())); + + KConfigGroup gcg(KGlobal::config(), "CloseConfirmationDialog"); + restoreDialogSize(gcg); // restore dialog geometry from config +} + +CloseConfirmDialog::~CloseConfirmDialog() +{ + KConfigGroup gcg(KGlobal::config(), "CloseConfirmationDialog"); + saveDialogSize(gcg); // write dialog geometry to config + gcg.sync(); +} + +/** + * Going to remove unchecked files from the given documents list + */ +void CloseConfirmDialog::updateDocsList() +{ + for ( + QTreeWidgetItemIterator it(m_docs_tree, QTreeWidgetItemIterator::NotChecked) + ; *it + ; ++it + ) + { + KateDocItem* item = static_cast(*it); + m_docs.removeAll(item->document); + kDebug() << "do not close the file " << item->document->url().prettyUrl(); + } +} + +} // namespace kate diff --git a/kate/addons/kate/close-except-like/close_confirm_dialog.h b/kate/addons/kate/close-except-like/close_confirm_dialog.h new file mode 100644 index 00000000..9f40836b --- /dev/null +++ b/kate/addons/kate/close-except-like/close_confirm_dialog.h @@ -0,0 +1,64 @@ +/** + * \file + * + * \brief Class \c kate::CloseConfirmDialog (interface) + * + * Copyright (C) 2012 Alex Turbov + * + * \date Sun Jun 24 16:29:13 MSK 2012 -- Initial design + */ +/* + * KateCloseExceptPlugin 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. + * + * KateCloseExceptPlugin is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#ifndef __SRC__CLOSE_CONFIRM_DIALOG_H__ +# define __SRC__CLOSE_CONFIRM_DIALOG_H__ + +// Project specific includes + +// Standard includes +# include +# include +# include +# include +# include +# include + +namespace kate { + +/** + * \brief [Type brief class description here] + * + * [More detailed description here] + * + */ +class CloseConfirmDialog : public KDialog +{ + Q_OBJECT +public: + /// Default constructor + explicit CloseConfirmDialog(QList&, KToggleAction*, QWidget* const = 0); + ~CloseConfirmDialog(); + +private Q_SLOTS: + void updateDocsList(); + +private: + QList& m_docs; + QTreeWidget* m_docs_tree; + QCheckBox* m_dont_ask_again; +}; + +} // namespace kate +#endif // __SRC__CLOSE_CONFIRM_DIALOG_H__ diff --git a/kate/addons/kate/close-except-like/close_except_plugin.cpp b/kate/addons/kate/close-except-like/close_except_plugin.cpp new file mode 100644 index 00000000..788b90b5 --- /dev/null +++ b/kate/addons/kate/close-except-like/close_except_plugin.cpp @@ -0,0 +1,350 @@ +/** + * \file + * + * \brief Kate Close Except/Like plugin implementation + * + * Copyright (C) 2012 Alex Turbov + * + * \date Thu Mar 8 08:13:43 MSK 2012 -- Initial design + */ +/* + * KateCloseExceptPlugin 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. + * + * KateCloseExceptPlugin 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 . + */ + +// Project specific includes +#include "config.h" +#include "close_except_plugin.h" +#include "close_confirm_dialog.h" + +// Standard includes +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +K_PLUGIN_FACTORY(CloseExceptPluginFactory, registerPlugin();) +K_EXPORT_PLUGIN( + CloseExceptPluginFactory( + KAboutData( + "katecloseexceptplugin" + , "katecloseexceptplugin" + , ki18n("Close Except/Like Plugin") + , PLUGIN_VERSION + , ki18n("Close all documents started from specified path") + , KAboutData::License_LGPL_V3 + ) + ) + ) + +namespace kate { +//BEGIN CloseExceptPlugin +CloseExceptPlugin::CloseExceptPlugin( + QObject* application + , const QList& + ) + : Kate::Plugin(static_cast(application), "katecloseexceptplugin") +{ +} + +Kate::PluginView* CloseExceptPlugin::createView(Kate::MainWindow* parent) +{ + return new CloseExceptPluginView(parent, CloseExceptPluginFactory::componentData(), this); +} + +void CloseExceptPlugin::readSessionConfig(KConfigBase* config, const QString& groupPrefix) +{ + KConfigGroup scg(config, groupPrefix + "menu"); + m_show_confirmation_needed = scg.readEntry("ShowConfirmation", true); +} + +void CloseExceptPlugin::writeSessionConfig(KConfigBase* config, const QString& groupPrefix) +{ + KConfigGroup scg(config, groupPrefix + "menu"); + scg.writeEntry("ShowConfirmation", m_show_confirmation_needed); + scg.sync(); +} +//END CloseExceptPlugin + +//BEGIN CloseExceptPluginView +CloseExceptPluginView::CloseExceptPluginView( + Kate::MainWindow* mw + , const KComponentData& data + , CloseExceptPlugin* plugin + ) + : Kate::PluginView(mw) + , Kate::XMLGUIClient(data) + , m_plugin(plugin) + , m_show_confirmation_action(new KToggleAction(i18nc("@action:inmenu", "Show Confirmation"), this)) + , m_except_menu(new KActionMenu( + i18nc("@action:inmenu close docs except the following...", "Close Except") + , this + )) + , m_like_menu(new KActionMenu( + i18nc("@action:inmenu close docs like the following...", "Close Like") + , this + )) +{ + actionCollection()->addAction("file_close_except", m_except_menu); + actionCollection()->addAction("file_close_like", m_like_menu); + + // Subscribe self to document creation + connect( + m_plugin->application()->editor() + , SIGNAL(documentCreated(KTextEditor::Editor*, KTextEditor::Document*)) + , this + , SLOT(documentCreated(KTextEditor::Editor*, KTextEditor::Document*)) + ); + // Configure toggle action and connect it to update state + m_show_confirmation_action->setChecked(m_plugin->showConfirmationNeeded()); + connect( + m_show_confirmation_action + , SIGNAL(toggled(bool)) + , m_plugin + , SLOT(toggleShowConfirmation(bool)) + ); + // + connect( + mainWindow() + , SIGNAL(viewCreated(KTextEditor::View*)) + , this + , SLOT(viewCreated(KTextEditor::View*)) + ); + // Fill menu w/ currently opened document masks/groups + updateMenu(); + + mainWindow()->guiFactory()->addClient(this); +} + +CloseExceptPluginView::~CloseExceptPluginView() +{ + mainWindow()->guiFactory()->removeClient(this); +} + +void CloseExceptPluginView::viewCreated(KTextEditor::View* view) +{ + connectToDocument(view->document()); + updateMenu(); +} + +void CloseExceptPluginView::documentCreated(KTextEditor::Editor*, KTextEditor::Document* document) +{ + connectToDocument(document); + updateMenu(); +} + +void CloseExceptPluginView::connectToDocument(KTextEditor::Document* document) +{ + // Subscribe self to document close and name changes + connect( + document + , SIGNAL(aboutToClose(KTextEditor::Document*)) + , this + , SLOT(updateMenuSlotStub(KTextEditor::Document*)) + ); + connect( + document + , SIGNAL(documentNameChanged(KTextEditor::Document*)) + , this + , SLOT(updateMenuSlotStub(KTextEditor::Document*)) + ); + connect( + document + , SIGNAL(documentUrlChanged(KTextEditor::Document*)) + , this + , SLOT(updateMenuSlotStub(KTextEditor::Document*)) + ); +} + +void CloseExceptPluginView::updateMenuSlotStub(KTextEditor::Document*) +{ + updateMenu(); +} + +void CloseExceptPluginView::appendActionsFrom( + const QSet& paths + , actions_map_type& actions + , KActionMenu* menu + , QSignalMapper* mapper + ) +{ + Q_FOREACH(const QString& path, paths) + { + QString action = path.startsWith('*') ? path : path + '*'; + QPointer kaction(new KAction(action, menu)); + actions[action] = kaction; + menu->addAction(kaction); + connect(kaction, SIGNAL(triggered()), mapper, SLOT(map())); + mapper->setMapping(kaction, action); + } +} + +QPointer CloseExceptPluginView::updateMenu( + const QSet& paths + , const QSet& masks + , actions_map_type& actions + , KActionMenu* menu + ) +{ + // turn menu ON or OFF depending on collected results + menu->setEnabled(!paths.empty()); + + // Clear previous menus + foreach (const QPointer it, actions) + { + menu->removeAction(it); + } + actions.clear(); + + // Form a new one + QPointer mapper = QPointer(new QSignalMapper(this)); + appendActionsFrom(paths, actions, menu, mapper); + if (!masks.empty()) + { + if (!paths.empty()) + menu->addSeparator(); // Add separator between paths and file's ext filters + appendActionsFrom(masks, actions, menu, mapper); + } + // Append 'Show Confirmation' toggle menu item + menu->addSeparator(); // Add separator between paths and show confirmation + menu->addAction(m_show_confirmation_action); + return mapper; +} + +void CloseExceptPluginView::updateMenu() +{ + const QList& docs = m_plugin->application()->documentManager()->documents(); + if (docs.size() < 2) + { + kDebug() << "No docs r (or the only) opened right now --> disable menu"; + m_except_menu->setEnabled(false); + m_except_menu->addSeparator(); + m_like_menu->setEnabled(false); + m_like_menu->addSeparator(); + /// \note It seems there is always a document present... it named \em 'Untitled' + } + else + { + // Iterate over documents and form a set of candidates + typedef QSet paths_set_type; + paths_set_type doc_paths; + paths_set_type masks; + Q_FOREACH(KTextEditor::Document* document, docs) + { + const QString& ext = QFileInfo(document->url().path()).completeSuffix(); + if (!ext.isEmpty()) + masks.insert("*." + ext); + doc_paths.insert(document->url().upUrl().path()); + } + paths_set_type paths = doc_paths; + kDebug() << "stage #1: Collected" << paths.size() << "paths and" << masks.size() << "masks"; + // Add common paths to the collection + for (paths_set_type::iterator it = doc_paths.begin(), last = doc_paths.end(); it != last; ++it) + { + for ( + KUrl url = *it + ; url.hasPath() && url.path() != "/" + ; url = url.upUrl() + ) + { + paths_set_type::iterator not_it = it; + for (++not_it; not_it != last; ++not_it) + if (!not_it->startsWith(url.path())) + break; + if (not_it == last) + { + paths.insert(url.path()); + break; + } + } + } + kDebug() << "stage #2: Collected" << paths.size() << "paths and" << masks.size() << "masks"; + // + m_except_mapper = updateMenu(paths, masks, m_except_actions, m_except_menu); + m_like_mapper = updateMenu(paths, masks, m_like_actions, m_like_menu); + connect(m_except_mapper, SIGNAL(mapped(const QString&)), this, SLOT(closeExcept(const QString&))); + connect(m_like_mapper, SIGNAL(mapped(const QString&)), this, SLOT(closeLike(const QString&))); + } +} + +void CloseExceptPluginView::close(const QString& item, const bool close_if_match) +{ + assert( + "Parameter seems invalid! Is smth has changed in the code?" + && !item.isEmpty() && (item[0] == '*' || item[item.size() - 1] == '*') + ); + + const bool is_path = item[0] != '*'; + const QString mask = is_path ? item.left(item.size() - 1) : item; + kDebug() << "Going to close items [" << close_if_match << "/" << is_path << "]: " << mask; + + QList docs2close; + const QList& docs = m_plugin->application()->documentManager()->documents(); + Q_FOREACH(KTextEditor::Document* document, docs) + { + const QString& path = document->url().upUrl().path(); + /// \note Take a dot in account, so \c *.c would not match for \c blah.kcfgc + const QString& ext = '.' + QFileInfo(document->url().fileName()).completeSuffix(); + const bool match = (!is_path && mask.endsWith(ext)) + || (is_path && path.startsWith(mask)) + ; + if (match == close_if_match) + { + kDebug() << "*** Will close: " << document->url(); + docs2close.push_back(document); + } + } + if (docs2close.isEmpty()) + { + KPassivePopup::message( + i18nc("@title:window", "Error") + , i18nc("@info:tooltip", "No files to close ...") + , qobject_cast(this) + ); + return; + } + // Show confirmation dialog if needed + const bool removeNeeded = !m_plugin->showConfirmationNeeded() + || CloseConfirmDialog(docs2close, m_show_confirmation_action, qobject_cast(this)).exec(); + if (removeNeeded) + { + if (docs2close.isEmpty()) + { + KPassivePopup::message( + i18nc("@title:window", "Error") + , i18nc("@info:tooltip", "No files to close ...") + , qobject_cast(this) + ); + } + else + { + // Close 'em all! + m_plugin->application()->documentManager()->closeDocumentList(docs2close); + updateMenu(); + KPassivePopup::message( + i18nc("@title:window", "Done") + , i18np("%1 file closed", "%1 files closed", docs2close.size()) + , qobject_cast(this) + ); + } + } +} +//END CloseExceptPluginView +} // namespace kate diff --git a/kate/addons/kate/close-except-like/close_except_plugin.h b/kate/addons/kate/close-except-like/close_except_plugin.h new file mode 100644 index 00000000..ef762f56 --- /dev/null +++ b/kate/addons/kate/close-except-like/close_except_plugin.h @@ -0,0 +1,135 @@ +/** + * \file + * + * \brief Declate Kate's Close Except/Like plugin classes + * + * Copyright (C) 2012 Alex Turbov + * + * \date Thu Mar 8 08:13:43 MSK 2012 -- Initial design + */ +/* + * KateCloseExceptPlugin 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. + * + * KateCloseExceptPlugin is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#ifndef __SRC__CLOSE_EXCEPT_PLUGIN_H__ +# define __SRC__CLOSE_EXCEPT_PLUGIN_H__ + +// Project specific includes + +// Standard includes +# include +# include +# include +# include +# include +# include +# include +# include +# include + +namespace kate { +class CloseExceptPlugin; // forward declaration + +/** + * \brief Plugin to close docs grouped by extension or location + */ +class CloseExceptPluginView + : public Kate::PluginView + , public Kate::XMLGUIClient +{ + Q_OBJECT + typedef QMap > actions_map_type; + +public: + /// Default constructor + CloseExceptPluginView(Kate::MainWindow*, const KComponentData&, CloseExceptPlugin*); + /// Destructor + ~CloseExceptPluginView(); + +private Q_SLOTS: + void viewCreated(KTextEditor::View*); + void documentCreated(KTextEditor::Editor*, KTextEditor::Document*); + void updateMenuSlotStub(KTextEditor::Document*); + void close(const QString&, const bool); + void closeExcept(const QString& item) + { + close(item, false); + } + void closeLike(const QString& item) + { + close(item, true); + } + +private: + void connectToDocument(KTextEditor::Document*); + void updateMenu(); + QPointer updateMenu( + const QSet& + , const QSet& + , actions_map_type& + , KActionMenu* + ); + void appendActionsFrom( + const QSet& + , actions_map_type& + , KActionMenu* + , QSignalMapper* + ); + + CloseExceptPlugin* m_plugin; + QPointer m_show_confirmation_action; + QPointer m_except_menu; + QPointer m_like_menu; + QPointer m_except_mapper; + QPointer m_like_mapper; + actions_map_type m_except_actions; + actions_map_type m_like_actions; +}; + +/** + * \brief Plugin view class + */ +class CloseExceptPlugin : public Kate::Plugin +{ + Q_OBJECT + +public: + /// Default constructor + CloseExceptPlugin(QObject* = 0, const QList& = QList()); + /// Destructor + virtual ~CloseExceptPlugin() {} + /// Create a new view of this plugin for the given main window + Kate::PluginView* createView(Kate::MainWindow*); + /// \name Plugin interface implementation + //@{ + void readSessionConfig(KConfigBase*, const QString&); + void writeSessionConfig(KConfigBase*, const QString&); + //@} + bool showConfirmationNeeded() const + { + return m_show_confirmation_needed; + } + +public Q_SLOTS: + void toggleShowConfirmation(bool flag) + { + m_show_confirmation_needed = flag; + } + +private: + bool m_show_confirmation_needed; +}; + +} // namespace kate +#endif // __SRC__CLOSE_EXCEPT_PLUGIN_H__ diff --git a/kate/addons/kate/close-except-like/config.h.in b/kate/addons/kate/close-except-like/config.h.in new file mode 100644 index 00000000..37da1548 --- /dev/null +++ b/kate/addons/kate/close-except-like/config.h.in @@ -0,0 +1,31 @@ +/** + * \file + * + * \brief Class \c kate::config (interface) + * + * Copyright (C) 2012 Alex Turbov + * + * \date Thu Mar 8 08:19:57 MSK 2012 -- Initial design + * + * \attention DO NOT EDIT THIS FILE! IT WAS GENERATED BY CMAKE. + */ +/* + * KateCloseExceptPlugin 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. + * + * KateCloseExceptPlugin is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#ifndef __SRC__CONFIG_H__ +# define __SRC__CONFIG_H__ +# define PLUGIN_VERSION "@VERSION_STRING@" +#endif // __SRC__CONFIG_H__ +// kate: hl c++; diff --git a/kate/addons/kate/close-except-like/katecloseexceptplugin.desktop b/kate/addons/kate/close-except-like/katecloseexceptplugin.desktop new file mode 100644 index 00000000..71c56aa6 --- /dev/null +++ b/kate/addons/kate/close-except-like/katecloseexceptplugin.desktop @@ -0,0 +1,92 @@ +[Desktop Entry] +Encoding=UTF-8 +Type=Service +ServiceTypes=Kate/Plugin +X-KDE-Library=katecloseexceptplugin +X-KDE-Version=4.0 +X-Kate-Version=2.9 +X-Kate-Load=True +Name=Close Except/Like +Name[ar]=أغلق ما عدى/المشابه لِـ +Name[bs]=Zatvori Osim/Kao +Name[ca]=Tancament excepte/com +Name[ca@valencia]=Tancament excepte/com +Name[cs]=Zavřít kromě/jako +Name[da]=Luk undtagen/lignende +Name[de]=Bedingtes Schließen +Name[el]=Κλείσιμο Except/Like +Name[en_GB]=Close Except/Like +Name[es]=Cerrar excepto/como +Name[et]=Selliste/teistsuguste sulgemine +Name[fi]=Sulje samanlaiset kuin / muut kuin +Name[fr]=Fermer les exclusions / inclusions +Name[gl]=Pechar agás/como +Name[he]=סגור דומה/למעט +Name[hu]=Kivétel/Like bezárása +Name[ia]=Claude Excepte/Simile +Name[it]=Chiudi escludendo/includendo +Name[kk]=Шарт бойынша жабу +Name[ko]=다음을 제외한/다음과 같은 것 닫기 +Name[lt]=Uždaryti Išskyrus/Kaip +Name[nb]=Lukk unntatt/lik +Name[nds]=Liek/Anner Dateien tomaken +Name[nl]=Behalve/Like sluiten +Name[pl]=Zamknij oprócz/podobne +Name[pt]=Fechar Excluindo/Incluindo +Name[pt_BR]=Fechamento com exclusão/inclusão +Name[ro]=Închide excepția/precum +Name[ru]=Закрыть кроме/только... +Name[sk]=Zatvoriť okrem/ako +Name[sl]=Zapri razen/kot +Name[sr]=Затварање осим/према +Name[sr@ijekavian]=Затварање осим/према +Name[sr@ijekavianlatin]=Zatvaranje osim/prema +Name[sr@latin]=Zatvaranje osim/prema +Name[sv]=Stäng förutom/som +Name[tr]=Hariç/Benzerlerini Kapat +Name[uk]=Додаток закриття за критерієм +Name[x-test]=xxClose Except/Likexx +Name[zh_CN]=关闭除/类似 +Name[zh_TW]=關閉「例外/喜歡」 +Comment=Close group of documents based on a common path or file extension +Comment[ar]=أغلق مجموعة من المستندات بناءً على مسار شائع أو امتداد الملف +Comment[bs]=Zatvori grupu dokumenara na bazi zajedniočke staye ili ekstenzije datoteke +Comment[ca]=Tanca un grup de documents basats en un camí comú o en l'extensió del fitxer +Comment[ca@valencia]=Tanca un grup de documents basats en un camí comú o en l'extensió del fitxer +Comment[cs]=Zavřít skupinu dokumentů podle společné cesty nebo přípony +Comment[da]=Luk gruppe af dokumenter baseret på en fælles sti eller filendelse +Comment[de]=Eine Gruppe von Dokumenten auf der Basis eines gemeinsamen Pfades oder einer Dateierweiterung schließen +Comment[el]=Κλείσιμο ομάδας εγγράφων με βάση κοινή διαδρομή ή κατάληξη ονόματος αρχείου +Comment[en_GB]=Close group of documents based on a common path or file extension +Comment[es]=Cerrar un grupo de documentos según una ruta común o una extensión de archivo +Comment[et]=Rühma ühise asukoha või faililaiendiga dokumentide sulgemine +Comment[fi]=Sulje joukko tiedostoja niille yhteisen polun tai tiedostopäätteen perusteella +Comment[fr]=Fermer un groupe de documents reposant sur un emplacement ou une extension de fichier communs +Comment[gl]=Pecha un grupo de documentos baseado nunha ruta ou extensión de ficheiro común +Comment[he]=סגור קבוצה של מסמכים בהתאם לנתיב משותף או סיומת +Comment[hu]=Közös útvonalon vagy fájlkiterjesztésen alapuló dokumentumok csoportjának bezárása +Comment[ia]=Claude gruppo de documentos basate su un commun percurso o extension de file +Comment[it]=Chiudi gruppo di documenti con percorso o estensione comune +Comment[kk]=Ортақ жолы не жұрнағы негізінде құжаттар тобын жабу +Comment[ko]=공통 경로나 확장자를 기준으로 문서 그룹 닫기 +Comment[lt]=Uždaryti dokumentų grupę remianti bendruoju keliu ar failo plėtiniu +Comment[nb]=Lukk en gruppe dokumenter basert på en felles sti eller etternavn +Comment[nds]=Mehr Dateien op eenmaal tomaken, wenn se en gemeen Padd oder de sülve Ennen hebbt +Comment[nl]=Groep documenten sluiten gebaseerd op een gemeenschappelijk pad of bestandsextensie +Comment[pl]=Zamknij grupę dokumentów w oparciu o wspólną ścieżkę lub rozszerzenie pliku +Comment[pt]=Fecha um grupo de documentos com base numa localização ou extensão de ficheiros comum +Comment[pt_BR]=Fecha um grupo de documentos com base em localização comum ou extensão do arquivo +Comment[ro]=Închide grupul de documente avînd la bază o cale sau extensie de fișier comune +Comment[ru]=Закрывает группу документов, в зависимости от пути или расширения +Comment[sk]=Zatvoriť skupinu dokumentov založenú na spoločnej ceste alebo koncovke súboru +Comment[sl]=Zapre skupino dokumentov, ki temeljijo na skupni poti ali priponi datoteke +Comment[sr]=Затворите групу докумената на основу заједничке путање или проширења фајла +Comment[sr@ijekavian]=Затворите групу докумената на основу заједничке путање или проширења фајла +Comment[sr@ijekavianlatin]=Zatvorite grupu dokumenata na osnovu zajedničke putanje ili proširenja fajla +Comment[sr@latin]=Zatvorite grupu dokumenata na osnovu zajedničke putanje ili proširenja fajla +Comment[sv]=Stäng grupp av dokument baserat på en gemensam sökväg eller filändelse +Comment[tr]=Ortak yol veya dosya uzantısına bağlı belge gruplarını kapat +Comment[uk]=Закриває групу документів, які зберігаються у одному каталозі або мають однаковий суфікс назви. +Comment[x-test]=xxClose group of documents based on a common path or file extensionxx +Comment[zh_CN]=根据路径或文件扩展名关闭一组文档 +Comment[zh_TW]=關閉相同路徑或相同副檔名的文件 diff --git a/kate/addons/kate/close-except-like/ui.rc b/kate/addons/kate/close-except-like/ui.rc new file mode 100644 index 00000000..024dc940 --- /dev/null +++ b/kate/addons/kate/close-except-like/ui.rc @@ -0,0 +1,9 @@ + + + + &File + + + + + diff --git a/kate/addons/kate/filebrowser/CMakeLists.txt b/kate/addons/kate/filebrowser/CMakeLists.txt new file mode 100644 index 00000000..a40d4eac --- /dev/null +++ b/kate/addons/kate/filebrowser/CMakeLists.txt @@ -0,0 +1,26 @@ +set(katefilebrowserplugin_PART_SRCS + katefilebrowserplugin.cpp + katefilebrowserconfig.cpp + katefilebrowser.cpp + katebookmarkhandler.cpp +) + +kde4_add_plugin(katefilebrowserplugin ${katefilebrowserplugin_PART_SRCS}) + +target_link_libraries(katefilebrowserplugin + ${KDE4_KFILE_LIBS} + ${KDE4_KPARTS_LIBS} + kateinterfaces +) + +install( + TARGETS katefilebrowserplugin + DESTINATION ${KDE4_PLUGIN_INSTALL_DIR} +) + +########### install files ############### + +install( + FILES katefilebrowserplugin.desktop + DESTINATION ${KDE4_SERVICES_INSTALL_DIR} +) diff --git a/kate/addons/kate/filebrowser/Messages.sh b/kate/addons/kate/filebrowser/Messages.sh new file mode 100644 index 00000000..8abbef07 --- /dev/null +++ b/kate/addons/kate/filebrowser/Messages.sh @@ -0,0 +1,2 @@ +#! /bin/sh +$XGETTEXT *.cpp -o $podir/katefilebrowserplugin.pot diff --git a/kate/addons/kate/filebrowser/katebookmarkhandler.cpp b/kate/addons/kate/filebrowser/katebookmarkhandler.cpp new file mode 100644 index 00000000..3db53e53 --- /dev/null +++ b/kate/addons/kate/filebrowser/katebookmarkhandler.cpp @@ -0,0 +1,70 @@ +/* This file is part of the KDE project + Copyright (C) xxxx KFile Authors + Copyright (C) 2002 Anders Lund + Copyright (C) 2009 Dominik Haumann + Copyright (C) 2007 Mirko Stocker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "katebookmarkhandler.h" +#include "moc_katebookmarkhandler.cpp" +#include "katefilebrowser.h" + +#include +#include + + +KateBookmarkHandler::KateBookmarkHandler( KateFileBrowser *parent, KMenu* kpopupmenu ) + : QObject( parent ), + KBookmarkOwner(), + mParent( parent ), + m_menu( kpopupmenu ) +{ + setObjectName( "KateBookmarkHandler" ); + if (!m_menu) + m_menu = new KMenu( parent); + + QString file = KStandardDirs::locate( "data", "kate/fsbookmarks.xml" ); + if ( file.isEmpty() ) + file = KStandardDirs::locateLocal( "data", "kate/fsbookmarks.xml" ); + + KBookmarkManager *manager = KBookmarkManager::managerForFile( file, "kate" ); + manager->setUpdate( true ); + + m_bookmarkMenu = new KBookmarkMenu( manager, this, m_menu, parent->actionCollection() ); +} + +KateBookmarkHandler::~KateBookmarkHandler() +{ + delete m_bookmarkMenu; +} + +QString KateBookmarkHandler::currentUrl() const +{ + return mParent->dirOperator()->url().url(); +} + +QString KateBookmarkHandler::currentTitle() const +{ + return currentUrl(); +} + +void KateBookmarkHandler::openBookmark( const KBookmark & bm, Qt::MouseButtons, Qt::KeyboardModifiers ) +{ + emit openUrl(bm.url().url()); +} + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/addons/kate/filebrowser/katebookmarkhandler.h b/kate/addons/kate/filebrowser/katebookmarkhandler.h new file mode 100644 index 00000000..6909d3a9 --- /dev/null +++ b/kate/addons/kate/filebrowser/katebookmarkhandler.h @@ -0,0 +1,59 @@ +/* This file is part of the KDE project + Copyright (C) xxxx KFile Authors + Copyright (C) 2002 Anders Lund + Copyright (C) 2007 Mirko Stocker + Copyright (C) 2009 Dominik Haumann + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KATE_BOOKMARK_HANDLER_H +#define KATE_BOOKMARK_HANDLER_H + +#include +#include + +class KateFileBrowser; + +class KateBookmarkHandler : public QObject, public KBookmarkOwner +{ + Q_OBJECT + + public: + explicit KateBookmarkHandler( KateFileBrowser *parent, KMenu *kpopupmenu = 0 ); + ~KateBookmarkHandler(); + + // KBookmarkOwner interface: + virtual QString currentUrl() const; + virtual QString currentTitle() const; + + KMenu *menu() const + { + return m_menu; + } + virtual void openBookmark( const KBookmark &, Qt::MouseButtons, Qt::KeyboardModifiers ); + + Q_SIGNALS: + void openUrl( const QString& url ); + + private: + KateFileBrowser *mParent; + KMenu *m_menu; + KBookmarkMenu *m_bookmarkMenu; +}; + +#endif // KATE_BOOKMARK_HANDLER_H +// kate: space-indent on; indent-width 2; replace-tabs on; + diff --git a/kate/addons/kate/filebrowser/katefilebrowser.cpp b/kate/addons/kate/filebrowser/katefilebrowser.cpp new file mode 100644 index 00000000..4cca543e --- /dev/null +++ b/kate/addons/kate/filebrowser/katefilebrowser.cpp @@ -0,0 +1,349 @@ +/* This file is part of the KDE project + Copyright (C) 2001 Christoph Cullmann + Copyright (C) 2001 Joseph Wenninger + Copyright (C) 2001 Anders Lund + Copyright (C) 2007 Mirko Stocker + Copyright (C) 2009 Dominik Haumann + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +//BEGIN Includes +#include "katefilebrowser.h" +#include "moc_katefilebrowser.cpp" + +#include "katebookmarkhandler.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +//END Includes + + +KateFileBrowser::KateFileBrowser(Kate::MainWindow *mainWindow, + QWidget * parent, const char * name) + : KVBox (parent) + , m_mainWindow(mainWindow) +{ + setObjectName(name); + + m_toolbar = new KToolBar(this); + m_toolbar->setMovable(false); + m_toolbar->setToolButtonStyle(Qt::ToolButtonIconOnly); + m_toolbar->setContextMenuPolicy(Qt::NoContextMenu); + + // includes some actions, but not hooked into the shortcut dialog atm + m_actionCollection = new KActionCollection(this); + m_actionCollection->addAssociatedWidget(this); + + KFilePlacesModel* model = new KFilePlacesModel(this); + m_urlNavigator = new KUrlNavigator(model, KUrl(QDir::homePath()), this); + connect(m_urlNavigator, SIGNAL(urlChanged(KUrl)), SLOT(updateDirOperator(KUrl))); + + m_dirOperator = new KDirOperator(KUrl(), this); + m_dirOperator->setView(KFile::/* Simple */Detail); + m_dirOperator->view()->setSelectionMode(QAbstractItemView::ExtendedSelection); + m_dirOperator->setSizePolicy(QSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding)); + + // Mime filter for the KDirOperator + QStringList filter; + filter << "text/plain" << "text/html" << "inode/directory"; + m_dirOperator->setNewFileMenuSupportedMimeTypes(filter); + + setFocusProxy(m_dirOperator); + connect(m_dirOperator, SIGNAL(viewChanged(QAbstractItemView*)), + this, SLOT(selectorViewChanged(QAbstractItemView*))); + connect(m_urlNavigator, SIGNAL(returnPressed()), + m_dirOperator, SLOT(setFocus())); + // now all actions exist in dir operator and we can use them in the toolbar + setupActions(); + setupToolbar(); + + KHBox* filterBox = new KHBox(this); + + QLabel* filterLabel = new QLabel(i18n("Filter:"), filterBox); + m_filter = new KHistoryComboBox(true, filterBox); + filterLabel->setBuddy(m_filter); + m_filter->setMaxCount(10); + m_filter->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed)); + + connect(m_filter, SIGNAL(editTextChanged(QString)), + SLOT(slotFilterChange(QString))); + connect(m_filter, SIGNAL(returnPressed(QString)), + m_filter, SLOT(addToHistory(QString))); + connect(m_filter, SIGNAL(returnPressed(QString)), + m_dirOperator, SLOT(setFocus())); + + connect(m_dirOperator, SIGNAL(urlEntered(KUrl)), + this, SLOT(updateUrlNavigator(KUrl))); + + // Connect the bookmark handler + connect(m_bookmarkHandler, SIGNAL(openUrl(QString)), + this, SLOT(setDir(QString))); + + m_filter->setWhatsThis(i18n("Enter a name filter to limit which files are displayed.")); + + connect(m_dirOperator, SIGNAL(fileSelected(KFileItem)), this, SLOT(fileSelected(KFileItem))); + connect(m_mainWindow, SIGNAL(viewChanged()), this, SLOT(autoSyncFolder())); +} + +KateFileBrowser::~KateFileBrowser() +{ +} +//END Constroctor/Destrctor + +//BEGIN Public Methods +void KateFileBrowser::setupToolbar() +{ + KConfigGroup config(KGlobal::config(), "filebrowser"); + QStringList actions = config.readEntry( "toolbar actions", QStringList() ); + if ( actions.isEmpty() ) // default toolbar + actions << "back" << "forward" << "bookmarks" << "sync_dir" << "configure"; + + // remove all actions from the toolbar (there should be none) + m_toolbar->clear(); + + // now add all actions to the toolbar + foreach (const QString& it, actions) + { + QAction *ac = 0; + if (it.isEmpty()) continue; + if (it == "bookmarks" || it == "sync_dir" || it == "configure") + ac = actionCollection()->action(it); + else + ac = m_dirOperator->actionCollection()->action(it); + + if (ac) + m_toolbar->addAction(ac); + } +} + +void KateFileBrowser::readSessionConfig(KConfigBase *config, const QString & name) +{ + KConfigGroup cgDir(config, name + ":dir"); + m_dirOperator->readConfig(cgDir); + m_dirOperator->setView(KFile::Default); + + KConfigGroup cg(config, name); + m_urlNavigator->setLocationUrl(cg.readPathEntry("location", QDir::homePath())); + setDir(cg.readPathEntry("location", QDir::homePath())); + m_autoSyncFolder->setChecked(cg.readEntry("auto sync folder", false)); + m_filter->setHistoryItems(cg.readEntry("filter history", QStringList()), true); +} + +void KateFileBrowser::writeSessionConfig(KConfigBase *config, const QString & name) +{ + KConfigGroup cgDir(config, name + ":dir"); + m_dirOperator->writeConfig(cgDir); + + KConfigGroup cg = KConfigGroup(config, name); + cg.writePathEntry("location", m_urlNavigator->locationUrl().url()); + cg.writeEntry("auto sync folder", m_autoSyncFolder->isChecked()); + cg.writeEntry("filter history", m_filter->historyItems()); +} + +//END Public Methods + +//BEGIN Public Slots + +void KateFileBrowser::slotFilterChange(const QString & nf) +{ + QString f = nf.trimmed(); + const bool empty = f.isEmpty() || f == "*"; + + if (empty) { + m_dirOperator->clearFilter(); + } else { + m_dirOperator->setNameFilter(f); + } + + m_dirOperator->updateDir(); +} + +bool kateFileSelectorIsReadable (const KUrl& url) +{ + if (!url.isLocalFile()) + return true; // what else can we say? + + QDir dir(url.toLocalFile()); + return dir.exists (); +} + +void KateFileBrowser::setDir(KUrl u) +{ + KUrl newurl; + + if (!u.isValid()) + newurl.setPath(QDir::homePath()); + else + newurl = u; + + QString pathstr = newurl.path(KUrl::AddTrailingSlash); + newurl.setPath(pathstr); + + if (!kateFileSelectorIsReadable (newurl)) + newurl.cd(QString::fromLatin1("..")); + + if (!kateFileSelectorIsReadable (newurl)) + newurl.setPath(QDir::homePath()); + + m_dirOperator->setUrl(newurl, true); +} + +//END Public Slots + +//BEGIN Private Slots + +void KateFileBrowser::fileSelected(const KFileItem & /*file*/) +{ + openSelectedFiles(); +} + +void KateFileBrowser::openSelectedFiles() +{ + const KFileItemList list = m_dirOperator->selectedItems(); + + if (list.count()>20) { + if (KMessageBox::questionYesNo(this,i18np("You are trying to open 1 file, are you sure?", "You are trying to open %1 files, are you sure?", list.count())) + == KMessageBox::No) return; + } + + foreach (const KFileItem& item, list) + { + m_mainWindow->openUrl(item.url()); + } + + m_dirOperator->view()->selectionModel()->clear(); +} + + + + +void KateFileBrowser::updateDirOperator(const KUrl& u) +{ + m_dirOperator->setUrl(u, true); +} + +void KateFileBrowser::updateUrlNavigator(const KUrl& u) +{ + m_urlNavigator->setLocationUrl(u); +} + +void KateFileBrowser::setActiveDocumentDir() +{ +// kDebug(13001)<<"KateFileBrowser::setActiveDocumentDir()"; + KUrl u = activeDocumentUrl(); +// kDebug(13001)<<"URL: "<isChecked()) { + setActiveDocumentDir(); + } +} + + +void KateFileBrowser::selectorViewChanged(QAbstractItemView * newView) +{ + newView->setSelectionMode(QAbstractItemView::ExtendedSelection); +} + +//END Private Slots + +//BEGIN Protected + +KUrl KateFileBrowser::activeDocumentUrl() +{ + KTextEditor::View *v = m_mainWindow->activeView(); + if (v) + return v->document()->url(); + return KUrl(); +} + +void KateFileBrowser::setupActions() +{ + // bookmarks action! + KActionMenu *acmBookmarks = new KActionMenu(KIcon("bookmarks"), i18n("Bookmarks"), this); + acmBookmarks->setDelayed(false); + m_bookmarkHandler = new KateBookmarkHandler(this, acmBookmarks->menu()); + acmBookmarks->setShortcutContext(Qt::WidgetWithChildrenShortcut); + + // action for synchronizing the dir operator with the current document path + KAction* syncFolder = new KAction(this); + syncFolder->setShortcutContext(Qt::WidgetWithChildrenShortcut); + syncFolder->setText(i18n("Current Document Folder")); + syncFolder->setIcon(KIcon("system-switch-user")); + connect(syncFolder, SIGNAL(triggered()), this, SLOT(setActiveDocumentDir())); + + m_actionCollection->addAction("sync_dir", syncFolder); + m_actionCollection->addAction("bookmarks", acmBookmarks); + + // section for settings menu + KActionMenu *optionsMenu = new KActionMenu(KIcon("configure"), i18n("Options"), this); + optionsMenu->setDelayed(false); + optionsMenu->addAction(m_dirOperator->actionCollection()->action("short view")); + optionsMenu->addAction(m_dirOperator->actionCollection()->action("detailed view")); + optionsMenu->addAction(m_dirOperator->actionCollection()->action("tree view")); + optionsMenu->addAction(m_dirOperator->actionCollection()->action("detailed tree view")); + optionsMenu->addSeparator(); + optionsMenu->addAction(m_dirOperator->actionCollection()->action("show hidden")); + + // action for synchronising the dir operator with the current document path + m_autoSyncFolder = new KAction(this); + m_autoSyncFolder->setCheckable(true); + m_autoSyncFolder->setText(i18n("Automatically synchronize with current document")); + m_autoSyncFolder->setIcon(KIcon("system-switch-user")); + connect(m_autoSyncFolder, SIGNAL(triggered()), this, SLOT(autoSyncFolder())); + optionsMenu->addAction(m_autoSyncFolder); + + m_actionCollection->addAction("configure", optionsMenu); + + // + // Remove all shortcuts due to shortcut clashes (e.g. F5: reload, Ctrl+B: bookmark) + // BUGS: #188954, #236368 + // + foreach (QAction* a, m_actionCollection->actions()) { + a->setShortcut(QKeySequence()); + } + foreach (QAction* a, m_dirOperator->actionCollection()->actions()) { + a->setShortcut(QKeySequence()); + } +} +//END Protected + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/addons/kate/filebrowser/katefilebrowser.h b/kate/addons/kate/filebrowser/katefilebrowser.h new file mode 100644 index 00000000..f0c569a9 --- /dev/null +++ b/kate/addons/kate/filebrowser/katefilebrowser.h @@ -0,0 +1,106 @@ +/* This file is part of the KDE project + Copyright (C) 2001 Christoph Cullmann + Copyright (C) 2001 Joseph Wenninger + Copyright (C) 2001 Anders Lund + Copyright (C) 2007 Mirko Stocker + Copyright (C) 2009 Dominik Haumann + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KATE_FILEBROWSER_H +#define KATE_FILEBROWSER_H + +#include + +#include +#include +#include + +#include +class KateBookmarkHandler; +class KAction; +class KActionCollection; +class KDirOperator; +class KFileItem; +class KHistoryComboBox; +class KToolBar; + +class KUrlNavigator; +/* + The kate file selector presents a directory view, in which the default action is + to open the activated file. + Additionally, a toolbar for managing the kdiroperator widget + sync that to + the directory of the current file is available, as well as a filter widget + allowing to filter the displayed files using a name filter. +*/ + +class KateFileBrowser : public KVBox +{ + Q_OBJECT + + public: + explicit KateFileBrowser( Kate::MainWindow *mainWindow = 0, + QWidget * parent = 0, const char * name = 0 ); + ~KateFileBrowser(); + + virtual void readSessionConfig( KConfigBase *, const QString & ); + virtual void writeSessionConfig( KConfigBase *, const QString & ); + + void setupToolbar(); + void setView( KFile::FileView ); + KDirOperator *dirOperator() { return m_dirOperator; } + + KActionCollection* actionCollection() + { return m_actionCollection; } + + public Q_SLOTS: + void slotFilterChange(const QString&); + void setDir(KUrl); + void setDir( const QString& url ) { setDir( KUrl( url ) ); } + void selectorViewChanged( QAbstractItemView * ); + + private Q_SLOTS: + void fileSelected(const KFileItem & /*file*/); + void updateDirOperator( const KUrl& u ); + void updateUrlNavigator( const KUrl& u ); + void setActiveDocumentDir(); + void autoSyncFolder(); + + protected: + KUrl activeDocumentUrl(); + void openSelectedFiles(); + void setupActions(); + + public: + Kate::MainWindow* mainWindow() + { + return m_mainWindow; + } + private: + KToolBar *m_toolbar; + KActionCollection *m_actionCollection; + KateBookmarkHandler *m_bookmarkHandler; + KUrlNavigator *m_urlNavigator; + KDirOperator * m_dirOperator; + KHistoryComboBox * m_filter; + KAction *m_autoSyncFolder; + + Kate::MainWindow *m_mainWindow; +}; + +#endif //KATE_FILEBROWSER_H + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/addons/kate/filebrowser/katefilebrowserconfig.cpp b/kate/addons/kate/filebrowser/katefilebrowserconfig.cpp new file mode 100644 index 00000000..f84af32f --- /dev/null +++ b/kate/addons/kate/filebrowser/katefilebrowserconfig.cpp @@ -0,0 +1,171 @@ +/* This file is part of the KDE project + Copyright (C) 2001 Christoph Cullmann + Copyright (C) 2001 Joseph Wenninger + Copyright (C) 2001 Anders Lund + Copyright (C) 2007 Mirko Stocker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "katefilebrowserconfig.h" +#include "moc_katefilebrowserconfig.cpp" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +//BEGIN ACtionLBItem +/* + QListboxItem that can store and return a string, + used for the toolbar action selector. +*/ +class ActionLBItem : public QListWidgetItem +{ + public: + ActionLBItem( QListWidget *lb = 0, + const QIcon &pm = QIcon(), + const QString &text = QString(), + const QString &str = QString() ) : + QListWidgetItem(pm, text, lb, 0 ), + _str(str) + {} + QString idstring() + { + return _str; + } + private: + QString _str; +}; +//END ActionLBItem + + + +//BEGIN KateFileBrowserConfigPage +KateFileBrowserConfigPage::KateFileBrowserConfigPage( QWidget *parent, const char *, KateFileBrowser *kfb ) + : Kate::PluginConfigPage( parent ), + fileBrowser( kfb ), + m_changed( false ) +{ + QVBoxLayout *lo = new QVBoxLayout( this ); + int spacing = KDialog::spacingHint(); + lo->setSpacing( spacing ); + + // Toolbar - a lot for a little... + QGroupBox *gbToolbar = new QGroupBox(i18n("Toolbar"), this ); + acSel = new KActionSelector( gbToolbar ); + acSel->setAvailableLabel( i18n("A&vailable actions:") ); + acSel->setSelectedLabel( i18n("S&elected actions:") ); + + QVBoxLayout *vbox = new QVBoxLayout; + vbox->addWidget(acSel); + gbToolbar->setLayout(vbox); + + lo->addWidget( gbToolbar ); + connect( acSel, SIGNAL(added(QListWidgetItem*)), this, SLOT(slotMyChanged()) ); + connect( acSel, SIGNAL(removed(QListWidgetItem*)), this, SLOT(slotMyChanged()) ); + connect( acSel, SIGNAL(movedUp(QListWidgetItem*)), this, SLOT(slotMyChanged()) ); + connect( acSel, SIGNAL(movedDown(QListWidgetItem*)), this, SLOT(slotMyChanged()) ); + + // make it look nice + lo->addStretch( 1 ); + + init(); +} + +void KateFileBrowserConfigPage::apply() +{ + if ( ! m_changed ) + return; + + m_changed = false; + + KConfigGroup config(KGlobal::config(), "filebrowser"); + QStringList l; + ActionLBItem *aItem; + QList list = acSel->selectedListWidget()->findItems(QString("*"), Qt::MatchWildcard); + foreach(QListWidgetItem *item, list) + { + aItem = static_cast(item); + l << aItem->idstring(); + } + config.writeEntry( "toolbar actions", l ); + + fileBrowser->setupToolbar(); +} + +void KateFileBrowserConfigPage::reset() +{ + // hmm, what is this supposed to do, actually?? + init(); + m_changed = false; +} + +void KateFileBrowserConfigPage::init() +{ + KConfigGroup config(KGlobal::config(), "filebrowser"); + // toolbar + QStringList l = config.readEntry( "toolbar actions", QStringList() ); + if ( l.isEmpty() ) // default toolbar + l << "back" << "forward" << "bookmarks" << "sync_dir" << "configure"; + + // actions from diroperator + two of our own + QStringList allActions; + allActions << "up" << "back" << "forward" << "home" + << "reload" << "mkdir" << "delete" + << "short view" << "detailed view" + << "tree view" << "detailed tree view" + << "show hidden" /*<< "view menu" << "properties"*/ + << "bookmarks" << "sync_dir" << "configure"; + QRegExp re("&(?=[^&])"); + QAction *ac = 0; + QListWidget *lb; + for ( QStringList::Iterator it = allActions.begin(); it != allActions.end(); ++it ) + { + lb = l.contains( *it ) ? acSel->selectedListWidget() : acSel->availableListWidget(); + + if ( *it == "bookmarks" || *it == "sync_dir" || *it == "configure" ) + ac = fileBrowser->actionCollection()->action( (*it).toLatin1().constData() ); + else + ac = fileBrowser->dirOperator()->actionCollection()->action( (*it).toLatin1().constData() ); + + if ( ac ) + { + QString text = ac->text().remove( re ); + // CJK languages need a filtering message for action texts in lists, + // to remove special accelerators that they use. + // The exact same filtering message exists in kdelibs; hence, + // avoid extraction here and let it be sourced from kdelibs. + #define i18ncX i18nc + text = i18ncX( "@item:intable Action name in toolbar editor", "%1", text ); + new ActionLBItem( lb, ac->icon(), text, *it ); + } + } +} + +void KateFileBrowserConfigPage::slotMyChanged() +{ + m_changed = true; + emit changed(); +} +//END KateFileBrowserConfigPage + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/addons/kate/filebrowser/katefilebrowserconfig.h b/kate/addons/kate/filebrowser/katefilebrowserconfig.h new file mode 100644 index 00000000..45ff3118 --- /dev/null +++ b/kate/addons/kate/filebrowser/katefilebrowserconfig.h @@ -0,0 +1,61 @@ +/* This file is part of the KDE project + Copyright (C) 2001 Christoph Cullmann + Copyright (C) 2001 Joseph Wenninger + Copyright (C) 2001 Anders Lund + Copyright (C) 2007 Mirko Stocker + Copyright (C) 2009 Dominik Haumann + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KATE_FILEBROWSER_CONFIG_H +#define KATE_FILEBROWSER_CONFIG_H + +#include + +#include +#include + +#include "katefilebrowser.h" + +class KateFileBrowserConfigPage : public Kate::PluginConfigPage +{ + Q_OBJECT + + public: + explicit KateFileBrowserConfigPage( QWidget* parent = 0, const char *name = 0, KateFileBrowser *kfb = 0); + virtual ~KateFileBrowserConfigPage() + {} + + virtual void apply(); + virtual void reset(); + virtual void defaults() + {} + + private Q_SLOTS: + void slotMyChanged(); + + private: + void init(); + + KateFileBrowser *fileBrowser; + KActionSelector *acSel; + + bool m_changed; +}; + +#endif //KATE_FILEBROWSER_CONFIG_H + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/addons/kate/filebrowser/katefilebrowserplugin.cpp b/kate/addons/kate/filebrowser/katefilebrowserplugin.cpp new file mode 100644 index 00000000..ffdb9e40 --- /dev/null +++ b/kate/addons/kate/filebrowser/katefilebrowserplugin.cpp @@ -0,0 +1,145 @@ +/* This file is part of the KDE project + Copyright (C) 2001 Christoph Cullmann + Copyright (C) 2001 Joseph Wenninger + Copyright (C) 2001 Anders Lund + Copyright (C) 2007 Mirko Stocker + Copyright (C) 2009 Dominik Haumann + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +//BEGIN Includes +#include "katefilebrowserplugin.h" +#include "moc_katefilebrowserplugin.cpp" +#include "katefilebrowserconfig.h" +#include "katefilebrowser.h" + +#include +#include + +#include +#include + +#include +//END Includes + +K_PLUGIN_FACTORY(KateFileBrowserFactory, registerPlugin();) +K_EXPORT_PLUGIN(KateFileBrowserFactory(KAboutData("katefilebrowserplugin","katefilebrowserplugin",ki18n("Filesystem Browser"), "0.1", ki18n("Browse through the filesystem"), KAboutData::License_LGPL_V2)) ) + +//BEGIN KateFileBrowserPlugin +KateFileBrowserPlugin::KateFileBrowserPlugin(QObject* parent, const QList&) + : Kate::Plugin ((Kate::Application*)parent) +{ +} + +Kate::PluginView *KateFileBrowserPlugin::createView (Kate::MainWindow *mainWindow) +{ + KateFileBrowserPluginView* view = new KateFileBrowserPluginView (mainWindow); + connect(view, SIGNAL(destroyed(QObject*)), this, SLOT(viewDestroyed(QObject*))); + m_views.append(view); + + return view; +} + +void KateFileBrowserPlugin::viewDestroyed(QObject* view) +{ + // do not access the view pointer, since it is partially destroyed already + m_views.removeAll(static_cast(view)); +} + +uint KateFileBrowserPlugin::configPages() const +{ + return 1; +} + +Kate::PluginConfigPage *KateFileBrowserPlugin::configPage (uint number, QWidget *parent, const char *name) +{ + if (number != 0) + return 0; + return new KateFileBrowserConfigPage(parent, name, m_views[0]->m_fileBrowser); +} + +QString KateFileBrowserPlugin::configPageName (uint number) const +{ + if (number != 0) return QString(); + kDebug() << "Returning a config page name"; + return i18n("Filesystem Browser"); +} + +QString KateFileBrowserPlugin::configPageFullName (uint number) const +{ + if (number != 0) return QString(); + return i18n("Filesystem Browser Settings"); +} + +KIcon KateFileBrowserPlugin::configPageIcon (uint number) const +{ + if (number != 0) return KIcon(); + return KIcon("document-open"); +} +//END KateFileBrowserPlugin + + + +//BEGIN KateFileBrowserPluginView +KateFileBrowserPluginView::KateFileBrowserPluginView (Kate::MainWindow *mainWindow) + : Kate::PluginView (mainWindow) + , m_toolView( + mainWindow->createToolView( + "kate_private_plugin_katefileselectorplugin" + , Kate::MainWindow::Left + , SmallIcon("document-open") + , i18n("Filesystem Browser") + ) + ) + , m_fileBrowser(new KateFileBrowser(mainWindow, m_toolView)) +{ + m_toolView->installEventFilter(this); +} + +KateFileBrowserPluginView::~KateFileBrowserPluginView () +{ + // cleanup, kill toolview + console + delete m_fileBrowser->parentWidget(); +} + +void KateFileBrowserPluginView::readSessionConfig(KConfigBase* config, const QString& group) +{ + m_fileBrowser->readSessionConfig(config, group); +} + +void KateFileBrowserPluginView::writeSessionConfig(KConfigBase* config, const QString& group) +{ + m_fileBrowser->writeSessionConfig(config, group); +} + +bool KateFileBrowserPluginView::eventFilter(QObject* obj, QEvent* event) +{ + if (event->type() == QEvent::KeyPress) + { + QKeyEvent* ke = static_cast(event); + if ((obj == m_toolView) && (ke->key() == Qt::Key_Escape)) + { + mainWindow()->hideToolView(m_toolView); + event->accept(); + return true; + } + } + return QObject::eventFilter(obj, event); +} +//ENDKateFileBrowserPluginView + + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/addons/kate/filebrowser/katefilebrowserplugin.desktop b/kate/addons/kate/filebrowser/katefilebrowserplugin.desktop new file mode 100644 index 00000000..195e8b69 --- /dev/null +++ b/kate/addons/kate/filebrowser/katefilebrowserplugin.desktop @@ -0,0 +1,119 @@ +[Desktop Entry] +Type=Service +ServiceTypes=Kate/Plugin +X-KDE-Library=katefilebrowserplugin +X-Kate-Version=2.8 +Name=File system browser +Name[ar]=متصّفح نظام الملفات +Name[ast]=Restolador del sistema de ficheros +Name[bg]=Файлова система +Name[bs]=Pregledač datotečnog sistema +Name[ca]=Navegador del sistema de fitxers +Name[ca@valencia]=Navegador del sistema de fitxers +Name[cs]=Prohlížeč souborového systému +Name[da]=Filsystemsbrowser +Name[de]=Dateisystem-Browser +Name[el]=Περιηγητής συστήματος αρχείων +Name[en_GB]=File system browser +Name[es]=Navegador del sistema de archivos +Name[et]=Failisüsteemi sirvija +Name[eu]=Fitxategi-sistemaren arakatzailea +Name[fi]=Tiedostojärjestelmäselain +Name[fr]=Explorateur de systèmes de fichiers +Name[ga]=Brabhsálaí an chórais comhad +Name[gl]=Navegador do sistema de ficheiros +Name[he]=דפדפן מערכת קבצים +Name[hu]=Fájlböngésző +Name[ia]=Navigator de systema de file +Name[is]=Skráarkerfisvafri +Name[it]=Navigatore del filesystem +Name[ja]=ファイルシステムブラウザ +Name[kk]=Файл жүйе шолғышы +Name[km]=កម្មវិធី​រុករក​ប្រព័ន្ធ​ឯកសារ +Name[ko]=파일 시스템 탐색기 +Name[lt]=Failų sistemos naršyklė +Name[lv]=Datņu sistēmas pārlūks +Name[mr]=फाईल प्रणाली ब्राऊजर +Name[nb]=Filsystemviser +Name[nds]=Dateisysteem-Kieker +Name[ne]=फाइल प्रणाली ब्राउजर +Name[nl]=Bestandssysteembrowser +Name[nn]=Filsystemvisar +Name[pa]=ਫਾਇਲ ਸਿਸਟਮ ਬਰਾਊਜ਼ਰ +Name[pl]=Przeglądarka systemu plików +Name[pt]=Navegador do sistema de ficheiros +Name[pt_BR]=Navegador do sistema de arquivos +Name[ro]=Navigator sistem de fișiere +Name[ru]=Обозреватель файловой системы +Name[si]=ගොනු පද්ධති ගවේශකය +Name[sk]=Prehliadač súborového systému +Name[sl]=Brskalnik po datotečnem sistemu +Name[sr]=Прегледач фајл система +Name[sr@ijekavian]=Прегледач фајл система +Name[sr@ijekavianlatin]=Pregledač fajl sistema +Name[sr@latin]=Pregledač fajl sistema +Name[sv]=Filsystembläddrare +Name[tg]=Браузери системаи файлҳо +Name[tr]=Dosya sistemi tarayıcı +Name[ug]=ھۆججەت سىستېما كۆرگۈ +Name[uk]=Навігатор файловою системою +Name[wa]=Naivieu d' sistinme di fitchî +Name[x-test]=xxFile system browserxx +Name[zh_CN]=文件系统浏览器 +Name[zh_TW]=檔案系統瀏覽器 +Comment=File system browser tool view +Comment[ast]=Ferramienta de visualización del restolador del sistema de ficheros +Comment[bg]=Инструмент за разглеждане на файловата система +Comment[bs]=Prikaz pregledača datotečnog sistema +Comment[ca]=Eina de visita del navegador del sistema de fitxers +Comment[ca@valencia]=Eina de visita del navegador del sistema de fitxers +Comment[cs]=Nástroj pro procházení souborového systému +Comment[da]=Visning af værktøj til filsystembrowser +Comment[de]=Werkzeugansicht für Dateisystem-Browser +Comment[el]=Εργαλείο περιήγησης συστήματος αρχείων +Comment[en_GB]=File system browser tool view +Comment[es]=Herramienta de visualización del navegador del sistema de archivos +Comment[et]=Failisüsteemi sirvija tööriistavaade +Comment[eu]=Fitxategi-sistemaren arakatzailearen ikuspegia +Comment[fi]=Tiedostojärjestelmäselain-työkalunäkymä +Comment[fr]=Vue des outils de l'explorateur de systèmes de fichiers +Comment[ga]=Amharc uirlisí brabhsálaí an chórais comhad +Comment[gl]=Vista da utilidade de navegador do sistema de ficheiros +Comment[he]=כלי לתצוגת מערכת קבצים +Comment[hu]=Fájlböngésző - Eszköznézet +Comment[ia]=Vista de instrumento de navigator de systema de file +Comment[it]=Strumento di navigazione del filesystem +Comment[ja]=ファイルシステムブラウザ・ツールビュー +Comment[kk]=Файл жүйесін шолу құралы +Comment[km]=ទិដ្ឋភាព​ឧបករណ៍​កម្មវិធី​រុករក​ប្រព័ន្ធ​ឯកសារ +Comment[ko]=파일 시스템 탐색기 도구 보기 +Comment[lt]=Failų sistemos naršyklės įrankio rodinys +Comment[lv]=Datņu sistēmas pārlūkošanas rīka skats +Comment[mr]=फाईल प्रणाली ब्राऊजर साधन दृश्य +Comment[nb]=Vising for filsystemviser +Comment[nds]=De Warktüüchansicht Dateisysteem-Kieker +Comment[ne]=फाइल प्रणाली ब्राउजर उपकरण दृश्य +Comment[nl]=Blader door het bestandssysteem +Comment[nn]=Vising for filsystemvisar +Comment[pa]=ਫਾਇਲ ਸਿਸਟਮ ਬਰਾਊਜ਼ਰ ਟੂਲ ਝਲਕ +Comment[pl]=Widok narzędzia przeglądarki systemu plików +Comment[pt]=Área da ferramenta de navegação no sistema de ficheiros +Comment[pt_BR]=Janela de navegação no sistema de arquivos +Comment[ro]=Mod de vizualizare navigator sistem de fișiere +Comment[ru]=Представление средства обзора файловой системы +Comment[si]=ගොනු පද්ධති ගවේශක මෙවලම් දසුන +Comment[sk]=Zobrazenie nástroja prehliadača súborového systému +Comment[sl]=Orodni pogled za brskanje po datotečnem sistemu +Comment[sr]=Приказ прегледача фајл система +Comment[sr@ijekavian]=Приказ прегледача фајл система +Comment[sr@ijekavianlatin]=Prikaz pregledača fajl sistema +Comment[sr@latin]=Prikaz pregledača fajl sistema +Comment[sv]=Verktygsvy för filsystembläddring +Comment[tg]=Асбоби намоиши системаи файлҳои браузер +Comment[tr]=Dosya sistemi tarayıcısı görünümü +Comment[ug]=ھۆججەت سىستېما كۆرگۈ قورال كۆرۈنۈشى +Comment[uk]=Панель навігатора файловою системою +Comment[wa]=Voeyaedje d' usteye di naivieu di sistinme di fitchî +Comment[x-test]=xxFile system browser tool viewxx +Comment[zh_CN]=文件系统浏览器工具视图 +Comment[zh_TW]=「檔案系統瀏覽器」工具檢視 diff --git a/kate/addons/kate/filebrowser/katefilebrowserplugin.h b/kate/addons/kate/filebrowser/katefilebrowserplugin.h new file mode 100644 index 00000000..e095938d --- /dev/null +++ b/kate/addons/kate/filebrowser/katefilebrowserplugin.h @@ -0,0 +1,104 @@ +/* This file is part of the KDE project + Copyright (C) 2001 Christoph Cullmann + Copyright (C) 2001 Joseph Wenninger + Copyright (C) 2001 Anders Lund + Copyright (C) 2007 Mirko Stocker + Copyright (C) 2009 Dominik Haumann + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KATE_FILEBROWSER_PLUGIN_H +#define KATE_FILEBROWSER_PLUGIN_H + + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +class KActionCollection; +class KActionSelector; +class KDirOperator; +class KFileItem; +class KHistoryComboBox; +class KToolBar; +#include +#include +#include + +class KateFileBrowser; +class KateFileBrowserPluginView; + +class KateFileBrowserPlugin: public Kate::Plugin, public Kate::PluginConfigPageInterface +{ + Q_OBJECT + Q_INTERFACES(Kate::PluginConfigPageInterface) + + public: + explicit KateFileBrowserPlugin( QObject* parent = 0, const QList& = QList() ); + virtual ~KateFileBrowserPlugin() + {} + + Kate::PluginView *createView (Kate::MainWindow *mainWindow); + + virtual uint configPages() const; + virtual Kate::PluginConfigPage *configPage (uint number = 0, QWidget *parent = 0, const char *name = 0); + virtual QString configPageName (uint number = 0) const; + virtual QString configPageFullName (uint number = 0) const; + virtual KIcon configPageIcon (uint number = 0) const; + + public Q_SLOTS: + void viewDestroyed(QObject* view); + + private: + QList m_views; +}; + +class KateFileBrowserPluginView : public Kate::PluginView +{ + Q_OBJECT + + public: + /** + * Constructor. + */ + KateFileBrowserPluginView (Kate::MainWindow *mainWindow); + + /** + * Virtual destructor. + */ + ~KateFileBrowserPluginView (); + + virtual void readSessionConfig (KConfigBase* config, const QString& groupPrefix); + virtual void writeSessionConfig (KConfigBase* config, const QString& groupPrefix); + + private: + bool eventFilter(QObject*, QEvent*); + + QWidget *m_toolView; + KateFileBrowser *m_fileBrowser; + friend class KateFileBrowserPlugin; +}; + +#endif //KATE_FILEBROWSER_PLUGIN_H + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/addons/kate/filetemplates/CMakeLists.txt b/kate/addons/kate/filetemplates/CMakeLists.txt new file mode 100644 index 00000000..8db16ffb --- /dev/null +++ b/kate/addons/kate/filetemplates/CMakeLists.txt @@ -0,0 +1,2 @@ +add_subdirectory(plugin) +add_subdirectory(templates) diff --git a/kate/addons/kate/filetemplates/katefiletemplates.kateproject b/kate/addons/kate/filetemplates/katefiletemplates.kateproject new file mode 100644 index 00000000..c7506d86 --- /dev/null +++ b/kate/addons/kate/filetemplates/katefiletemplates.kateproject @@ -0,0 +1,15 @@ +[Dir plugin] +Dirs= +Files=filetemplates.cpp/filetemplates.h/katefiletemplates.desktop/katetemplate.xml/Makefile.am/ui.rc + +[Dir templates] +Dirs= +Files=cppgpl.cpp.katetemplate/cppgpl.hh.katetemplate/cpplgpl.cpp.katetemplate/cpplgpl.hh.katetemplate/docbookchapter.xml.katetemplate/html.katetemplate/language.xml.katetemplate/Makefile.am + +[Project Dir] +Dirs=plugin/templates +Files=Makefile.am + +[Project File] +Name=katefiletemplates +Type=Default diff --git a/kate/addons/kate/filetemplates/plugin/CMakeLists.txt b/kate/addons/kate/filetemplates/plugin/CMakeLists.txt new file mode 100644 index 00000000..be92553c --- /dev/null +++ b/kate/addons/kate/filetemplates/plugin/CMakeLists.txt @@ -0,0 +1,28 @@ +########### next target ############### + +kde4_add_plugin(katefiletemplates filetemplates.cpp) + +target_link_libraries(katefiletemplates + kateinterfaces + ${KDE4_KTEXTEDITOR_LIBS} +) + +install( + TARGETS katefiletemplates + DESTINATION ${KDE4_PLUGIN_INSTALL_DIR} +) + +########### install files ############### + +install( + FILES ui.rc + DESTINATION ${KDE4_DATA_INSTALL_DIR}/kate/plugins/katefiletemplates +) +install( + FILES katetemplate.xml + DESTINATION ${KDE4_DATA_INSTALL_DIR}/katepart/syntax +) +install( + FILES katefiletemplates.desktop + DESTINATION ${KDE4_SERVICES_INSTALL_DIR} +) diff --git a/kate/addons/kate/filetemplates/plugin/Messages.sh b/kate/addons/kate/filetemplates/plugin/Messages.sh new file mode 100644 index 00000000..e27f06aa --- /dev/null +++ b/kate/addons/kate/filetemplates/plugin/Messages.sh @@ -0,0 +1,21 @@ +#! /bin/sh +$EXTRACTRC *.rc >> rc.cpp + +for fname in `find ../templates -iname '*.katetemplate'`; do + for compound in template@item:inmenu \ + group@item:inmenu \ + description@info:whatsthis \ + author@info:credit \ + ; do + field=`echo $compound | sed 's/\(.*\)@.*/\1/'` + ctxmark=`echo $compound | sed 's/.*\(@.*\)/\1/'` + grep -inH "^katetemplate:.*$field=" $fname \ + | sed "s/\\([^:]*\\):\\([^:]*\\):.*$field= *\\([^=]*\\)\\( .*\\)\\?\$/\ + \/\/i18n: file \1 line \2\n\ + i18nc(\"$ctxmark\", \"\3\");\ + /i" \ + >> rc.cpp + done +done + +$XGETTEXT *.cpp -o $podir/katefiletemplates.pot diff --git a/kate/addons/kate/filetemplates/plugin/filetemplates.cpp b/kate/addons/kate/filetemplates/plugin/filetemplates.cpp new file mode 100644 index 00000000..784be29c --- /dev/null +++ b/kate/addons/kate/filetemplates/plugin/filetemplates.cpp @@ -0,0 +1,1192 @@ +/* + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + + --- + Copyright (C) 2004, Anders Lund + */ + +//BEGIN Includes +#include "filetemplates.h" + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//Added by qt3to4: +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include +#include +//END Includes + +//BEGIN plugin + factory stuff + +K_PLUGIN_FACTORY(KateFileTemplatesFactory, registerPlugin();) +K_EXPORT_PLUGIN(KateFileTemplatesFactory(KAboutData("katefiletemplates","katefiletemplates",ki18n("File Templates"), "0.1", ki18n("Create files from templates"), KAboutData::License_LGPL_V2)) ) + +//END + +//BEGIN PluginViewKateFileTemplates +PluginViewKateFileTemplates::PluginViewKateFileTemplates(KateFileTemplates *plugin, Kate::MainWindow *mainwindow): + Kate::PluginView(mainwindow),Kate::XMLGUIClient(KateFileTemplatesFactory::componentData()),m_plugin(plugin) +{ + QAction *a = actionCollection()->addAction("settings_manage_templates"); + a->setText(i18n("&Manage Templates...")); + connect( a, SIGNAL(triggered(bool)), plugin, SLOT(slotEditTemplate()) ); + + a=new KActionMenu( i18n("New From &Template"), actionCollection()); + actionCollection()->addAction("file_new_fromtemplate",a); + refreshMenu(); + + mainwindow->guiFactory()->addClient (this); + +} + +void PluginViewKateFileTemplates::refreshMenu() +{ + m_plugin->refreshMenu( (static_cast(actionCollection()->action("file_new_fromtemplate")))->menu()); +} + +PluginViewKateFileTemplates::~PluginViewKateFileTemplates() +{ + mainWindow()->guiFactory()->removeClient (this); +} +//END PluginViewKateFileTemplates + +//BEGIN TemplateInfo +class TemplateInfo +{ + public: + TemplateInfo( const QString& fn, const QString &t, const QString &g ) + : filename( fn ), tmplate ( t ), group( g ) { ; } + ~TemplateInfo() { ; } + + QString filename; + QString tmplate; + QString group; + QString description; + QString author; + QString highlight; + QString icon; +}; +Q_DECLARE_METATYPE(TemplateInfo*) +//END TemplateInfo + +//BEGIN KateFileTemplates +KateFileTemplates::KateFileTemplates( QObject* parent, const QList &dummy) + : Kate::Plugin ( (Kate::Application*)parent) +{ + Q_UNUSED(dummy) +// // create actions, so that they are shared. +// // We plug them into each view's menus, and update them centrally, so that +// // new plugins can automatically become visible in all windows. + mActionAny = new KAction ( i18n("Any File..."), this ); + connect( mActionAny, SIGNAL(triggered(bool)), this, SLOT(slotAny()) ); + + + // template menu + m_dw = new KDirWatch( this); + m_dw->setObjectName( "template_dirwatch" ); + const QStringList dirs = KGlobal::dirs()->findDirs("data", "kate/plugins/katefiletemplates/templates"); + for ( QStringList::const_iterator it = dirs.begin(); it != dirs.end(); ++it ) + { + m_dw->addDir( *it, KDirWatch::WatchFiles ); + } + + connect( m_dw, SIGNAL(dirty(QString)), + this, SLOT(updateTemplateDirs(QString)) ); + connect( m_dw, SIGNAL(created(QString)), + this, SLOT(updateTemplateDirs(QString)) ); + connect( m_dw, SIGNAL(deleted(QString)), + this, SLOT(updateTemplateDirs(QString)) ); + +// m_templates.setAutoDelete( true ); + updateTemplateDirs(); + + m_user = 0; + m_emailstuff = 0; +} + +/** + * Called whenever the template dir is changed. Recreates the templates list. + */ +void KateFileTemplates::updateTemplateDirs(const QString &d) +{ + kDebug()<<"updateTemplateDirs called with arg "<findAllResources( + "data","kate/plugins/katefiletemplates/templates/*.katetemplate", + KStandardDirs::NoDuplicates); + + m_templates.clear(); + + QRegExp re( "\\b(\\w+)\\s*=\\s*(.+)(?:\\s+\\w+=|$)" ); + re.setMinimal( true ); + + KConfigGroup cg( KGlobal::config(), "KateFileTemplates" ); + QStringList hidden; + cg.readXdgListEntry( "Hidden", hidden ); // XXX this is bogus + + for ( QStringList::const_iterator it=templates.begin(); it != templates.end(); ++it ) + { + QFile _f( *it ); + if ( _f.open( QIODevice::ReadOnly ) ) + { + QString fname = (*it).section( '/', -1 ); + + // skip if hidden + if ( hidden.contains( fname ) ) + continue; + + // Read the first line of the file, to get the group/name + TemplateInfo *tmp = new TemplateInfo( *it, fname, i18nc( "@item:inmenu", "Other" ) ); + bool trymore ( true ); + QTextStream stream(&_f); + while ( trymore ) + { + QString _line = stream.readLine(); + trymore = _line.startsWith( "katetemplate:" ); + if ( ! trymore ) break; + + int pos ( 0 ); + while ( ( ( pos = re.indexIn( _line, pos ) ) >= 0 ) ) + { + pos += re.cap( 1 ).length(); + if ( re.cap( 1 ).toLower() == "template" ) + tmp->tmplate = i18nc( "@item:inmenu", re.cap( 2 ).toUtf8() ); + if ( re.cap( 1 ).toLower() == "group" ) + tmp->group = i18nc( "@item:inmenu", re.cap( 2 ).toUtf8() ); + if ( re.cap( 1 ).toLower() == "description" ) + tmp->description = i18nc( "@info:whatsthis", re.cap( 2 ).toUtf8() ); + if ( re.cap( 1 ).toLower() == "author" ) + tmp->author = i18nc( "@info:credit", re.cap( 2 ).toUtf8() ); + if ( re.cap( 1 ).toLower() == "highlight" ) + tmp->highlight = re.cap( 2 ); + if ( re.cap( 1 ) == "icon" ) + tmp->icon = re.cap( 2 ); + } + } + + m_templates.append( tmp ); + _f.close(); + } + } + + emit triggerMenuRefresh(); + +} + +KateFileTemplates::~KateFileTemplates() +{ +// m_acRecentTemplates->saveEntries( KGlobal::config(), "Recent Templates" ); + delete m_emailstuff; + delete m_user; +} + +Kate::PluginView *KateFileTemplates::createView (Kate::MainWindow *mainWindow) +{ + PluginViewKateFileTemplates *view=new PluginViewKateFileTemplates(this,mainWindow); + connect(this,SIGNAL(triggerMenuRefresh()),view,SLOT(refreshMenu())); + return view; +} + +QStringList KateFileTemplates::groups() +{ + QStringList l; + QString s; + + for ( int i = 0; i < m_templates.count(); i++ ) + { + s = m_templates[ i ]->group; + if ( ! l.contains( s ) ) + l.append( s ); + } + + return l; +} + +void KateFileTemplates::refreshMenu( KMenu *menu ) +{ + + // clear the menu for templates + menu->clear(); + + // restore it + menu->addAction( mActionAny ); + menu->addSeparator(); + + QMap submenus; // ### QMAP + for ( int i = 0; i < m_templates.count(); i++ ) + { + if ( ! submenus[ m_templates[ i ]->group ] ) + { + QMenu *sm=menu->addMenu(m_templates[ i ]->group); + submenus.insert( m_templates[ i ]->group, sm ); + } +// kDebug()<<"=== ICON: '"<group]; + QAction *a; + if ( ! m_templates[ i ]->icon.isEmpty() ) + a=sm->addAction( + KIcon( m_templates[ i ]->icon ), + m_templates[ i ]->tmplate); + else + a=sm->addAction( + m_templates[ i ]->tmplate); + a->setData(i); + connect(a,SIGNAL(triggered(bool)),this,SLOT(slotOpenTemplate())); + + // add whatsthis containing the description and author + QString w ( m_templates[ i ]->description ); + if( ! m_templates[ i ]->author.isEmpty() ) + { + w.append( "

" ); + w.append( i18n("Author: ") ); + w.append( m_templates[ i ]->author ); + } + if ( ! w.isEmpty() ) + w.prepend( "

" ); + + if ( ! w.isEmpty() ) + a->setWhatsThis( w ); + } +} + +/** + * Action slot: use any file as a template. + * Get a URL and pass it on. + */ +void KateFileTemplates::slotAny() +{ + if (!application()->activeMainWindow()) + return; + + // get a URL and pass that to slotOpenTemplate + QString fn = KFileDialog::getOpenFileName( + KUrl(), + QString(), + application()->activeMainWindow()->activeView(), + i18n("Open as Template") ); + if ( ! fn.isEmpty() ) + slotOpenTemplate( KUrl( fn ) ); +} + +/** + * converts template [index] to a URL and passes that + */ +void KateFileTemplates::slotOpenTemplate() +{ + int index=static_cast(sender())->data().toInt(); + kDebug()<<"slotOpenTemplate( "< m_templates.count() ) return; + slotOpenTemplate( KUrl( m_templates.at( index )->filename ) ); +} + +void KateFileTemplates::slotOpenTemplate( const KUrl &url ) +{ + // check if the file can be opened + QString tmpfile; + QString filename = url.fileName(); + kDebug()<<"file: "<activeMainWindow()->activeView(), + i18n("Error opening the file
%1
for reading. The document will not be created.
", filename), + i18n("Template Plugin"), 0 ); + KIO::NetAccess::removeTempFile( tmpfile ); + return; + } + + // this may take a moment.. + QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); + + // create a new document + application()->activeMainWindow()->openUrl( KUrl() ); + KTextEditor::View *view = application()->activeMainWindow()->activeView(); + KTextEditor::Document *doc = view->document(); + + + QTextStream stream(&file); + QString str, tmp; + uint numlines = 0; + uint doneheader = 0; + while ( !stream.atEnd() ) { + tmp = stream.readLine(); + if ( ! numlines && isTemplate && tmp.startsWith( "katetemplate:" ) ) + { + // look for document name, highlight + if ( ! (doneheader & 1) ) + { + QRegExp reName( "\\bdocumentname\\s*=\\s*(.+)(?:\\s+\\w+\\s*=|$)", Qt::CaseInsensitive ); + reName.setMinimal( true ); + if ( reName.indexIn( tmp ) > -1 ) + { + docname = reName.cap( 1 ); + docname = docname.replace( "%N", "%1" ); + doneheader |= 1; + } + } + + if ( ! (doneheader & 2) ) + { + QRegExp reHl( "\\bhighlight\\s*=\\s*(.+)(?:\\s+\\w+\\s*=|$)", Qt::CaseInsensitive ); + reHl.setMinimal( true ); + kDebug()<<"looking for a hl mode"; + if ( reHl.indexIn( tmp ) > -1 ) + { + kDebug()<<"looking for a hl mode -- "<setMode (hlmode); + + doneheader |= 2; + } + } + + continue; // skip this line + } + if ( numlines ) + str += '\n'; + str += tmp; + numlines++; + } + file.close(); + KIO::NetAccess::removeTempFile( tmpfile ); + + uint line, col; + line = col = 0; + + if ( ! isTemplate ) + { + int d = filename.lastIndexOf('.'); +// ### warning i18n: Hack to have localized number later... + docname = i18n("Untitled %1", QString("%1")); + if ( d > 0 ) docname += filename.mid( d ); + } else if ( docname.isEmpty() ) + docname = filename.left( filename.length() - 13 ); + + // check for other documents matching this naming scheme, + // and do a count before choosing a name for this one + QString p = docname; + p.replace( "%1", "\\d+" ); + p.replace( ".", "\\." ); + p.prepend( "^" ); + p.append( "$" ); + QRegExp reName( p ); + + int count = 1; + const QList docs=application()->documentManager()->documents(); + foreach(const KTextEditor::Document *doc,docs) { + if ( ( reName.indexIn( doc->documentName() ) > -1 ) ) + count++; + } + if ( docname.contains( "%1" ) ) +//### warning i18n: ...localized number here + + docname = docname.arg( i18n("%1", count) ); +//### warning FIXME, setDocName is gone +#if 0 + doc->setDocName( docname ); +#endif + doc->setModified( false ); + + QApplication::restoreOverrideCursor(); +// m_acRecentTemplates->addUrl( url ); + + // clean up + delete m_user; + m_user = 0; + delete m_emailstuff; + m_emailstuff = 0; + if (isTemplate) { + KTextEditor::TemplateInterface *ti=qobject_cast(doc->activeView()); + ti->insertTemplateText(KTextEditor::Cursor(0,0),str,QMap()); + } else { + doc->insertText( KTextEditor::Cursor(0, 0), str ); + view->setCursorPosition(KTextEditor::Cursor(line, col)); + } + } +} + + +QWidget *KateFileTemplates::parentWindow() +{ + return dynamic_cast(application()->activeMainWindow()); +} + +// The next part are tools to aid the creation and editing of templates +// ///////////////////////////////////////////////////////////////////// +// Steps to produce a template +// * Choose a file to start from (optional) +// * Ask for a location to store the file -- suggesting either the file +// directory, or the local template directory. +// Set the URL +// * Get the template properties -- provide a dialog, which has filled in what +// we already know -- the author name, list of known groups +// +// Combine those data into the editor, and tell the user to position the cursor +// and edit the file as she wants to... +void KateFileTemplates::slotCreateTemplate() +{ + KateTemplateWizard w( parentWindow(), this ); + w.exec(); + + updateTemplateDirs(); +} + +// Tools for editing the existing templates +// Editing a template: +// * Select the template to edit +// * Open the template +// * Set the URL to a writable one if required +void KateFileTemplates::slotEditTemplate() +{ + KDialog dlg( parentWindow()); + dlg.setModal(true); + dlg.setCaption(i18n("Manage File Templates")); + dlg.setButtons(KDialog::Close); + dlg.setMainWidget( new KateTemplateManager( this, &dlg ) ); + dlg.exec(); +} +//END KateFileTemplates + +//BEGIN KateTemplateInfoWidget +// This widget can be used to change the data of a TemplateInfo object +KateTemplateInfoWidget::KateTemplateInfoWidget( QWidget *parent, TemplateInfo *info, KateFileTemplates *kft ) + : QWidget( parent ), + info( info ), + kft( kft ) +{ + QGridLayout *lo = new QGridLayout( this ); + lo->setSpacing( KDialog::spacingHint() ); + + QLabel *l = new QLabel( i18n("&Template:"), this ); + KHBox *hb = new KHBox( this ); + hb->setSpacing( KDialog::spacingHint() ); + leTemplate = new KLineEdit( hb ); + l->setBuddy( leTemplate ); + leTemplate->setToolTip( i18n("

This string is used as the template's name " + "and is displayed, for example, in the Template menu. It should describe the " + "meaning of the template, for example 'HTML Document'.

") ); + ibIcon = new KIconButton( hb ); + ibIcon->setToolTip(i18n( + "Press to select or change the icon for this template") ); + + l = new QLabel( i18n("&Group:"), this ); + cmbGroup = new KComboBox( true, this ); + cmbGroup->insertItems( 0, kft->groups() ); + l->setBuddy( cmbGroup ); + cmbGroup->setToolTip(i18n("

The group is used for choosing a " + "submenu for the plugin. If it is empty, 'Other' is used.

" + "

You can type any string to add a new group to your menu.

") ); + + l = new QLabel( i18n("Document &name:"), this ); + leDocumentName = new KLineEdit( this ); + l->setBuddy( leDocumentName ); + leDocumentName->setToolTip( i18n("

This string will be used to set a name " + "for the new document, to display in the title bar and file list.

" + "

If the string contains '%N', that will be replaced with a number " + "increasing with each similarly named file.

For example, if the " + "Document Name is 'New shellscript (%N).sh', the first document will be " + "named 'New shellscript (1).sh', the second 'New shellscipt (2).sh', and " + "so on.

") ); + + l = new QLabel( i18n( "&Highlight:"), this ); + btnHighlight = new QPushButton( i18n("None"), this ); + l->setBuddy( btnHighlight ); + btnHighlight->setToolTip( i18n("

Select the highlight to use for the " + "template. If 'None' is chosen, the property will not be set.

") ); + + l = new QLabel( i18n("&Description:"), this ); + leDescription = new KLineEdit( this ); + l->setBuddy( leDescription ); + leDescription->setToolTip(i18n("

This string is used, for example, as " + "context help for this template (such as the 'whatsthis' help for the " + "menu item.)

") ); + + l = new QLabel( i18n("&Author:"), this ); + leAuthor = new KLineEdit( this ); + l->setBuddy( leAuthor ); + leAuthor->setToolTip(i18n("

You can set this if you want to share your " + "template with other users.

" + "

the recommended form is like an Email " + "address: 'Anders Lund <anders@alweb.dk>'

") ); + + // if we have a object ! null + if ( info ) + { + if ( ! info->icon.isEmpty() ) + ibIcon->setIcon( info->icon ); + leTemplate->setText( info->tmplate ); + int i = cmbGroup->findText( info->group ); + if ( i != -1 ) { + cmbGroup->setCurrentIndex( i ); + } else { + cmbGroup->setEditText( info->group ); + } + leDescription->setText( info->description ); + leAuthor->setText( info->author ); + if ( ! info->highlight.isEmpty() ) + btnHighlight->setText( info->highlight ); + } + + // fill in the Hl menu + KTextEditor::Document *doc = kft->application()->activeMainWindow()->activeView()->document(); + if ( doc ) + { + QStringList highlightModes = doc->highlightingModes(); + QMenu *m = new QMenu( btnHighlight ); + connect( m, SIGNAL(triggered(QAction*)), this, SLOT(slotHlSet(QAction*)) ); + QMap submenus; + for ( int n = 0; n < highlightModes.count(); n++ ) + { + // create the sub menu if it does not exist + QString text( doc->highlightingModeSection( n ) ); + if ( ! text.isEmpty() ) + { + if ( ! submenus[ text ] ) + { + QMenu *sm = m->addMenu( text ); + connect( sm, SIGNAL(triggered(QAction*)), this, SLOT(slotHlSet(QAction*)) ); + submenus.insert( text, sm ); + } + // create the item + QAction *a = submenus[ text ]->addAction( highlightModes[ n ] ); + a->setData( n ); + } + else { + QAction *a = m->addAction( highlightModes[ n ] ); + a->setData( n ); + } + } + btnHighlight->setMenu( m ); + } +} + +void KateTemplateInfoWidget::slotHlSet( QAction *action ) +{ + KTextEditor::Document *doc=kft->application()->activeMainWindow()->activeView()->document(); + if (doc) + highlightName = doc->highlightingModes()[action->data().toInt()]; + btnHighlight->setText( action->text() ); // fixme +} +//END KateTemplateInfoWidget + +//BEGIN KateTemplateWizard +// A simple wizard to help create a new template :-) +KateTemplateWizard::KateTemplateWizard( QWidget *parent, KateFileTemplates *kft ) + : QWizard( parent ), + kft( kft ) +{ + // 1) Optionally chose a file or existing template to start from + QWizardPage *page = new QWizardPage; + page->setTitle( i18n("Template Origin") ); + page->setSubTitle( i18n("If you want to base this " + "template on an existing file or template, select the appropriate option " + "below.") ); + + addPage( page ); + + QGridLayout *glo = new QGridLayout( page ); + glo->setSpacing( KDialog::spacingHint() ); + + bgOrigin = new QButtonGroup( page ); +// bgOrigin->hide(); + bgOrigin->setExclusive( true ); + + QRadioButton *rb = new QRadioButton( i18n("Start with an &empty document" ), page ); + bgOrigin->addButton( rb, 1 ); + glo->addWidget( rb, 1, 1, 1, 2 ); + rb->setChecked( true ); + + rb = new QRadioButton( i18n("Use an existing file:"), page ); + bgOrigin->addButton( rb, 2 ); + glo->addWidget( rb, 2, 1, 1, 2 ); +// ### warning 0 could be wrong here: it crashes with Plastik + // int marg = rb->style()->subElementRect( QStyle::SE_RadioButtonIndicator, 0,rb ).width(); + int marg = KDialog::marginHint(); + glo->addItem( new QSpacerItem( marg, 1, QSizePolicy::Fixed ), 3, 1 ); + urOrigin = new KUrlRequester( page ); + glo->addWidget( urOrigin, 3, 2 ); + + rb = new QRadioButton( i18n("Use an existing template:"), page ); + bgOrigin->addButton( rb, 3 ); + glo->addWidget( rb, 4, 1, 1, 2 ); + glo->addItem( new QSpacerItem( marg, 1, QSizePolicy::Fixed ), 5, 1 ); + btnTmpl = new QPushButton( page ); + glo->addWidget( btnTmpl, 5, 2 ); + QMenu *m = new QMenu( btnTmpl ); + connect( m, SIGNAL(triggered(QAction*)), this, SLOT(slotTmplateSet(QAction*)) ); + + QMap submenus; + for ( int i = 0; i < kft->templates().count(); i++ ) + { + if ( ! submenus[ kft->templates()[ i ]->group ] ) + { + QMenu *sm = m->addMenu( kft->templates()[ i ]->group ); + connect( sm, SIGNAL(triggered(QAction*)), this, SLOT(slotTmplateSet(QAction*)) ); + submenus.insert( kft->templates()[ i ]->group, sm ); + } + + QAction *a = submenus[kft->templates()[ i ]->group]->addAction( + kft->templates()[ i ]->tmplate ); + a->setData( i ); + } + btnTmpl->setMenu( m ); + + connect( bgOrigin, SIGNAL(buttonClicked(int)), this, SLOT(slotStateChanged()) ); + connect( urOrigin, SIGNAL(textChanged(QString)), this, SLOT(slotStateChanged()) ); + + glo->addItem( new QSpacerItem( 1, 1, QSizePolicy::Expanding, QSizePolicy::Expanding ), 7, 7, 1, 2 ); + + // 2) edit the template properties + page = new QWizardPage; + page->setTitle( i18n("Edit Template Properties") ); + page->setSubTitle( i18n("Specify the main properties of your plugin. You can leave fields empty for which you have no meaningful value.") ); + glo = new QGridLayout( page ); + kti = new KateTemplateInfoWidget( page, 0, kft ); + glo->addWidget( kti, 1, 1 ); + addPage( page ); + + // get liekly values from KTE + QMap map; + map[ "fullname" ] = ""; + map[ "email" ] = ""; + + KTextEditor::TemplateInterface::expandMacros( map, parent ); + QString sFullname = map["fullname"]; + QString sEmail = map["email"]; + QString _s = sFullname; + if ( ! sEmail.isEmpty() ) + _s += " <" + sEmail + '>'; + kti->leAuthor->setText( _s ); + + // 3) chose a location - either the template directory (default) or + // a custom location + page = new QWizardPage; + page->setTitle( i18n("Choose Location") ); + page->setSubTitle( i18n("

Choose a location for the " + "template. If you store it in the template directory, it will " + "automatically be added to the template menu.

") ); + glo = new QGridLayout( page ); + glo->setSpacing( KDialog::spacingHint() ); + + bgLocation = new QButtonGroup( page ); +// bgLocation->hide(); + bgLocation->setExclusive( true ); + + rb = new QRadioButton( i18n("Template directory"), page ); + bgLocation->addButton( rb, 1 ); + glo->addWidget( rb, 2, 1, 1, 2 ); + rb->setChecked( true ); + + glo->addItem( new QSpacerItem( marg, 1, QSizePolicy::Fixed ), 3, 1, 2, 1 ); + leTemplateFileName = new KLineEdit( page ); + QLabel *l = new QLabel( i18n("Template &file name:"), page ); + l->setBuddy( leTemplateFileName ); + + glo->addWidget( l, 3, 2 ); + glo->addWidget( leTemplateFileName, 4, 2 ); + + rb = new QRadioButton( i18n("Custom location:"), page ); + bgLocation->addButton( rb, 2 ); + glo->addWidget( rb, 5, 1, 1, 2 ); + + glo->addItem( new QSpacerItem( marg, 1, QSizePolicy::Fixed ), 6, 1 ); + urLocation = new KUrlRequester( page ); + glo->addWidget( urLocation, 6, 2 ); + + connect( bgLocation, SIGNAL(buttonClicked(int)), this, SLOT(slotStateChanged()) ); + connect( urLocation, SIGNAL(textChanged(QString)), this, SLOT(slotStateChanged()) ); + connect( leTemplateFileName, SIGNAL(textChanged(QString)), this, SLOT(slotStateChanged()) ); + + glo->addItem( new QSpacerItem( 1, 1, QSizePolicy::Expanding, QSizePolicy::Expanding ), 7, 1, 1, 2 ); + + addPage( page ); + // 4) Should we edit the text to add some macros, replacing username etc? + // This is *only* relevant if the origin is a non-template file. + page = new QWizardPage; + page->setTitle( i18n("Autoreplace Macros") ); + page->setSubTitle( i18n( "You can replace certain strings in the text with " + "template macros. If any of the data below is incorrect or missing, " + "edit the data in the personal kaddressbook entry.") ); + QVBoxLayout *lo = new QVBoxLayout( page ); + lo->setSpacing( KDialog::spacingHint() ); + + cbRRealname = new QCheckBox( i18n("Replace full name '%1' with the " + "'%{fullname}' macro", sFullname ), page ); + cbRRealname->setEnabled( ! sFullname.isEmpty() ); + lo->addWidget( cbRRealname ); + + cbREmail = new QCheckBox( i18n("Replace email address '%1' with the " + "'%email' macro", sEmail ), page); + cbREmail->setEnabled( ! sEmail.isEmpty() ); + lo->addWidget( cbREmail ); + + lo->addStretch(); + + addPage( page ); + + // 5) Display a summary + page = new QWizardPage; + page->setTitle( i18n("Create Template") ); + page->setSubTitle( i18n("The template will now be created and saved to the chosen " + "location. To position the cursor put the string ${|} where you " + "want it in files created from the template.") ); + lo = new QVBoxLayout( page ); + lo->setSpacing( KDialog::spacingHint() ); + + cbOpenTemplate = new QCheckBox( i18n("Open the template for editing in Kate"), page ); + + lo->addWidget( cbOpenTemplate ); + + lo->addStretch(); + + addPage( page ); + connect( this, SIGNAL(currentIdChanged(int)), this, SLOT(slotStateChanged()) ); +} + +void KateTemplateWizard::slotTmplateSet( QAction *action ) +{ + int idx = action->data().toInt(); + btnTmpl->setText( kft->templates().at( idx )->tmplate ); + selectedTemplateIdx = idx; + slotStateChanged(); +} + +/** + * When the state of any button in any setup page is changed, set the + * enabled state of the next button accordingly. + * + * Origin: + * if file is chosen, the URLRequester must have a valid URL in it + * if template is chosen, one must be selected in the menu button. + * + * Props: + * anything goes, but if the user wants to store the template in the template + * directory, she should be encouraged to fill in information. +*/ +void KateTemplateWizard::slotStateChanged() +{ + bool sane( true ); + switch ( currentId() ) + { + case 0: // origin + { + int _t = bgOrigin->checkedId(); + kDebug()<<"selected button:"<<_t; + sane = ( _t == 1 || + ( _t == 2 && ! urOrigin->url().isEmpty() ) || + ( _t == 3 && ! btnTmpl->text().isEmpty() ) ); +// setAppropriate( page(3), _t == 2 ); + } + break; + case 1: // template properties + // if origin is a existing template, let us try setting some of the properties + if ( bgOrigin->checkedId() == 3 ) + { + TemplateInfo *info = kft->templateInfo( selectedTemplateIdx ); + int i = kti->cmbGroup->findText( info->group ); + if ( i != -1 ) { + kti->cmbGroup->setCurrentIndex( i ); + } else { + kti->cmbGroup->setEditText( info->group ); + } + } + break; + case 2: // location + { + // If there is a template name, and the user did not enter text into + // the template file name entry, we will construct the name from the + // template name. + int _t = bgLocation->checkedId(); + sane = ( ( _t == 1 && (! leTemplateFileName->text().isEmpty() || ! kti->leTemplate->text().isEmpty() ) ) || + ( _t == 2 && ! urLocation->url().isEmpty() ) ); + } + break; + default: + break; + } + kDebug()<<"enabling 'next' button:"<setEnabled( sane ); +} + +int KateTemplateWizard::nextId() const +{ + switch ( currentId() ) + { +// case 0: +// if ( bgOrigin->checkedId() == 2 ) return 2; +// else return 1; + default: + return QWizard::nextId(); + } +} +/** + * This will create the new template based on the collected information. + */ +void KateTemplateWizard::accept() +{ + // TODO check that everything is kosher, so that we can get a save location + // etc. + + // check that we can combine a valid URL + KUrl templateUrl; + if ( bgLocation->checkedId() == 1 ) + { + QString suggestion; + if ( ! leTemplateFileName->text().isEmpty() ) + suggestion = leTemplateFileName->text(); + else + suggestion = kti->leTemplate->text(); + + suggestion.remove(' '); + + if ( ! suggestion.endsWith(".katetemplate") ) + suggestion.append(".katetemplate"); + + QString dir = KGlobal::dirs()->saveLocation( "data", "kate/plugins/katefiletemplates/templates/", true ); + + templateUrl = dir + suggestion; + + if ( QFile::exists( templateUrl.toLocalFile() ) ) + { + if ( KMessageBox::warningContinueCancel( this, i18n( + "

The file
'%1'
already exists; if you " + "do not want to overwrite it, change the template file name to " + "something else.

", templateUrl.pathOrUrl() ), + i18n("File Exists"), KGuiItem(i18n("Overwrite") )) + == KMessageBox::Cancel ) + return; + } + } + else + { + templateUrl = urLocation->url(); + } + + QWizard::accept(); + // The following must be done: + // 1) add the collected template information to the top + uint ln = 0; + QString s, str; + if ( ! kti->leTemplate->text().isEmpty() ) + s += " Template=" + kti->leTemplate->text(); + if ( ! kti->cmbGroup->currentText().isEmpty() ) + s += " Group=" + kti->cmbGroup->currentText(); + if ( ! kti->leDocumentName->text().isEmpty() ) + s += " Documentname=" + kti->leDocumentName->text(); + if ( ! kti->ibIcon->icon().isEmpty() ) + s += " Icon=" + kti->ibIcon->icon(); + if ( ! kti->highlightName.isEmpty() && kti->highlightName != "None" ) + s += " Highlight=" + kti->highlightName; + + str = "katetemplate:" + s; + + if ( ! (s = kti->leAuthor->text()).isEmpty() ) + str += "\nkatetemplate: Author=" + s; + + if ( ! (s = kti->leDescription->text()).isEmpty() ) + str += "\nkatetemplate: Description=" + s; + + // 2) If a file or template is chosen, open that. and fill the data into a string + int toid = bgOrigin->checkedId(); // 1 = blank, 2 = file, 3 = template + kDebug()<<"=== create template: origin type "< 1 ) + { + KUrl u; + if ( toid == 2 ) // file + u = KUrl( urOrigin->url() ); + else // template + u = KUrl( kft->templates().at( selectedTemplateIdx )->filename ); + + QString tmpfile, tmp; + if ( KIO::NetAccess::download( u, tmpfile, 0L ) ) + { + QFile file(tmpfile); + if ( ! file.open( QIODevice::ReadOnly ) ) + { + KMessageBox::sorry( this, i18n( + "Error opening the file
%1
for reading. " + "The document will not be created
", u.pathOrUrl()), + i18n("Template Plugin"), 0 ); + + KIO::NetAccess::removeTempFile( tmpfile ); + return; + } + + QTextStream stream(&file); + QString ln; + bool trymore = true; + while ( !stream.atEnd() ) + { + // skip template headers + ln = stream.readLine(); + if ( trymore && ln.startsWith("katetemplate:") ) + continue; + + trymore = false; + tmp += '\n' + ln; + } + + file.close(); + KIO::NetAccess::removeTempFile( tmpfile ); + } + + if ( toid == 2 ) // file + { + // 3) if the file is not already a template, escape any "%" and "^" in it, + // and try do do some replacement of the authors username, name and email. + tmp.replace( QRegExp("%(?=\\{[^}]+\\})"), "\\%" ); + tmp.replace( QRegExp("\\$(?=\\{[^}]+\\})"), "\\$" ); + //tmp.replace( "^", "\\^" ); + + if ( cbRRealname->isChecked() && ! sFullname.isEmpty() ) + tmp.replace( sFullname, "%{realname}" ); + + + if ( cbREmail->isChecked() && ! sEmail.isEmpty() ) + tmp.replace( sEmail, "%{email}" ); + } + + str += tmp; + } + + // 4) Save the document to the suggested URL if possible + + bool succes = false; + + if ( templateUrl.isValid() ) + { + if ( templateUrl.isLocalFile() ) + { + QFile file( templateUrl.toLocalFile() ); + if ( file.open(QIODevice::WriteOnly) ) + { + kDebug()<<"file opened with succes"; + QTextStream stream( &file ); + stream << str; + file.close(); + succes = true; + } + } + else + { + KTemporaryFile tmp; + tmp.open(); + QString fn = tmp.fileName(); + QTextStream stream( &tmp ); + stream << str; + stream.flush(); + + succes = KIO::NetAccess::upload( fn, templateUrl, 0 ); + } + } + + if ( ! succes ) + { + KMessageBox::sorry( this, i18n( + "Unable to save the template to '%1'.\n\nThe template will be opened, " + "so you can save it from the editor.", templateUrl.pathOrUrl() ), + i18n("Save Failed") ); + + kft->application()->activeMainWindow()->openUrl( KUrl() ); + KTextEditor::View *view = kft->application()->activeMainWindow()->activeView(); + KTextEditor::Document *doc = view->document(); + doc->insertText( KTextEditor::Cursor(ln++, 0), str ); + } + else if ( cbOpenTemplate->isChecked() ) + kft->application()->activeMainWindow()->openUrl( templateUrl ); +} +//END KateTemplateWizard + +//BEGIN KateTemplateManager +KateTemplateManager::KateTemplateManager( KateFileTemplates *kft, QWidget *parent ) + : QWidget( parent ) + , kft( kft ) +{ + QGridLayout *lo = new QGridLayout( this ); + lo->setSpacing( KDialog::spacingHint() ); + lvTemplates = new QTreeWidget( this ); + lvTemplates->setHeaderLabel( i18n("Template") ); + lvTemplates->setSelectionMode( QAbstractItemView::SingleSelection ); + lo->addWidget( lvTemplates, 1, 1, 1, 4 ); + connect( lvTemplates, SIGNAL(itemSelectionChanged()), this, SLOT(slotUpdateState()) ); + + btnNew = new QPushButton( i18nc("@action:button Template", "New..."), this ); + connect( btnNew, SIGNAL(clicked()), kft, SLOT(slotCreateTemplate()) ); + lo->addWidget( btnNew, 2, 2 ); + + btnEdit = new QPushButton( i18nc("@action:button Template", "Edit..."), this ); + connect( btnEdit, SIGNAL(clicked()), this, SLOT(slotEditTemplate()) ); + lo->addWidget( btnEdit, 2, 3 ); + + btnRemove = new QPushButton( i18nc("@action:button Template", "Remove"), this ); + connect( btnRemove, SIGNAL(clicked()), this, SLOT(slotRemoveTemplate()) ); + lo->addWidget( btnRemove, 2, 4 ); + + lo->setColumnStretch( 1, 1 ); + + reload(); + slotUpdateState(); +} + +void KateTemplateManager::apply() +{ + // if any files were removed, delete them unless they are not writeable, in + // which case a link .filename should be put in the writable directory. +} + +#define KATETEMPLATEITEM 1001 +void KateTemplateManager::reload() +{ + lvTemplates->clear(); + + QMap groupitems; + for ( int i = 0; i < kft->templates().count(); i++ ) + { + if ( ! groupitems[ kft->templates()[ i ]->group ] ) + { + groupitems.insert( kft->templates()[ i ]->group , new QTreeWidgetItem( lvTemplates ) ); + groupitems[ kft->templates()[ i ]->group ]->setText( 0, kft->templates()[ i ]->group ); + groupitems[ kft->templates()[ i ]->group ]->setExpanded( true ); + } + QTreeWidgetItem *item = new QTreeWidgetItem( groupitems[ kft->templates()[ i ]->group ], KATETEMPLATEITEM ); + item->setText( 0, kft->templates()[ i ]->tmplate ); + item->setData( 0, Qt::UserRole, QVariant::fromValue( kft->templates()[ i ] ) ); + } +} + +void KateTemplateManager::slotUpdateState() +{ + // enable/disable buttons wrt the current item in the list view. + // we are in single selection mode, so currentItem() is selected. + QTreeWidgetItem *item = lvTemplates->currentItem(); + bool cool = item && item->type() == KATETEMPLATEITEM; + + btnEdit->setEnabled( cool ); + btnRemove->setEnabled( cool ); +} + +void KateTemplateManager::slotEditTemplate() +{ + // open the template file in kate + // TODO show the properties dialog, and modify the file if the data was changed. + QList selection = lvTemplates->selectedItems(); + + if ( selection.count() ) { + QTreeWidgetItem *item = selection[ 0 ]; + if ( item->type() != KATETEMPLATEITEM ) + return; + TemplateInfo *info = item->data(0, Qt::UserRole ).value(); + kft->application()->activeMainWindow()->openUrl( info->filename ); + } +} + +void KateTemplateManager::slotRemoveTemplate() +{ + QTreeWidgetItem *item = lvTemplates->selectedItems().first(); + if ( item && item->type() == KATETEMPLATEITEM ) + { + // Find all instances of filename, and try to delete them. + // If it fails (there was a global, unwritable instance), add to a + // list of removed templates + KSharedConfig::Ptr config = KGlobal::config(); + TemplateInfo *info = item->data(0, Qt::UserRole).value(); + QString fname = info->filename.section( '/', -1 ); + const QStringList templates = KGlobal::dirs()->findAllResources( + "data", fname.prepend( "kate/plugins/katefiletemplates/templates/" ),KStandardDirs::NoDuplicates); + int failed = 0; + int removed = 0; + for ( QStringList::const_iterator it=templates.begin(); it!=templates.end(); ++it ) + { + if ( ! QFile::remove(*it) ) + failed++; + else + removed++; + } + + if ( failed ) + { + KConfigGroup cg( config, "KateFileTemplates" ); + QStringList l; + cg.readXdgListEntry( "Hidden", l ); // XXX this is bogus + l << fname; + cg.writeXdgListEntry( "Hidden", l ); // XXX this is bogus + } + + kft->updateTemplateDirs(); + reload(); + } +} +//END KateTemplateManager + +// kate: space-indent on; indent-width 2; replace-tabs on; + +#include "moc_filetemplates.cpp" diff --git a/kate/addons/kate/filetemplates/plugin/filetemplates.h b/kate/addons/kate/filetemplates/plugin/filetemplates.h new file mode 100644 index 00000000..5a1a7835 --- /dev/null +++ b/kate/addons/kate/filetemplates/plugin/filetemplates.h @@ -0,0 +1,259 @@ +/* + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + + --- + Copyright (C) 2004, Anders Lund + */ + +#ifndef _PLUGIN_KATE_FILETEMPLATES_H_ +#define _PLUGIN_KATE_FILETEMPLATES_H_ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +/** + * This template system has the following features: + * It allows to create new documents that already has some contents and a meaningfull + * document name. + * + * Any file can b e used as a template. + * + * Special template files can contain macros that are expanded when the document + * is created, and the cursor can be positioned in the new document. + * + * A menu is provided, allowing access to templates located in the KDE file system + * in the plugins data directory. The menu is dynamically updated. + * + * Simple tools are provided for creating/eidting templates. + * + * The main class has methods to do all of the work related to use file templates: + * @li Maintain the Template menu (File, New from Template) + * @li Load templates + * @li Provide simple tools for creating/editing templates +*/ +class KateFileTemplates : public Kate::Plugin +{ + Q_OBJECT + + public: + KateFileTemplates( QObject* parent = 0, const QList& dummy=QList()); + virtual ~KateFileTemplates(); + + virtual Kate::PluginView *createView (Kate::MainWindow *mainWindow); + + /** + * @return a list of unique group names in the template list. + */ + QStringList groups(); + + /** + * @return a pointer to the templateinfo collection + */ + QList templates() { return m_templates; } + + /** + * @return a pointer to the templateInfo for the template at @p index + * in the template collection. + */ + class TemplateInfo *templateInfo( int index ) { return m_templates.at( index ); } + + /** + * @return a a pointer to the active main window + */ + QWidget * parentWindow(); + + public Q_SLOTS: + /** + * Update the template collection by rereading the template + * directories. Also updates the menu. + */ + void updateTemplateDirs(const QString &s=QString()); + protected Q_SLOTS: + /** + * Show a file dialog, so that any file can be opened as a template. + * If the chosen file has the .katetemplate extension, it is parsed, + * otherwise it is just copied to the new document. + */ + void slotAny(); + + /** + * Open the template found at @p index in the colletion + */ + void slotOpenTemplate(); + + /** + * Open the file at @p url as a template. If it has the .katetemplate + * extension it is parsed, otherwise its content is just copied to the new + * document. + */ + void slotOpenTemplate( const KUrl &url ); + + void slotEditTemplate(); + + /** + * Show a KateTemplateWizard wizard. + */ + void slotCreateTemplate(); + + Q_SIGNALS: + void triggerMenuRefresh(); + public: + void refreshMenu( class KMenu */*class QPopupMenu **/ ); + + private: + class KAction *mActionAny; + QList m_templates; + class KDirWatch *m_dw; + class KUser *m_user; + class KConfig *m_emailstuff; + class KActionMenu *m_menu; +}; + +class TemplateInfo; + +/** + * This widget provide a GUI for editing template properties. + */ +class KateTemplateInfoWidget : public QWidget +{ + Q_OBJECT + public: + explicit KateTemplateInfoWidget( QWidget *parent=0, TemplateInfo *info=0, KateFileTemplates *kft=0 ); + ~KateTemplateInfoWidget() {} + + TemplateInfo *info; + + class KLineEdit *leTemplate, *leDocumentName, *leDescription, *leAuthor; + class KComboBox *cmbGroup; + QPushButton *btnHighlight; + class KIconButton *ibIcon; + QString highlightName; + + private slots: + void slotHlSet( QAction *action ); + + private: + KateFileTemplates *kft; +}; + +/** + * This wizard helps creating a new template, which is then opened for the user + * to edit. + * Basically, the user is offered to select an existing file or template to start + * from, set template properties, and if a file is loaded, some replacements is + * done in the text: + * @li % characters are protected (% -> %%) + * @li ^ characters are protected (with a backsplash) + * @li The users name, username and email is replaced by the corresponding macros + * If so chosen, the file is saved to either the template directory, or a location + * set by the user. +*/ +class KateTemplateWizard : public QWizard +{ + friend class KateFileTemplates; + Q_OBJECT + public: + KateTemplateWizard( QWidget* parent, KateFileTemplates *ktf ); + ~KateTemplateWizard() {} + + virtual int nextId() const; + + public slots: + void accept(); + + private slots: + void slotTmplateSet( QAction* ); + void slotStateChanged(); + void slotStateChanged( int ) { slotStateChanged(); } + void slotStateChanged( const QString& ) { slotStateChanged(); } + + private: + KateFileTemplates *kft; + KateTemplateInfoWidget *kti; + + // origin page + QButtonGroup *bgOrigin; + class KUrlRequester *urOrigin; + QPushButton *btnTmpl; + uint selectedTemplateIdx; + + // location page + QButtonGroup *bgLocation; + class KUrlRequester *urLocation; + class KLineEdit *leTemplateFileName; + + // macro replacement page + QCheckBox *cbRRealname, *cbRUsername, *cbREmail; + QString sFullname, sEmail/*, sUsername*/; + + // final + QCheckBox *cbOpenTemplate; +}; + +class KateTemplateManager : public QWidget +{ + Q_OBJECT + public: + explicit KateTemplateManager( KateFileTemplates *kft=0, QWidget *parent=0 ); + ~KateTemplateManager() {} + + public slots: + void apply(); + void reload(); + void reset() { reload(); } + + private slots: + void slotUpdateState(); + void slotEditTemplate(); + void slotRemoveTemplate(); + + private: + QTreeWidget *lvTemplates; + QPushButton *btnNew, *btnEdit, *btnRemove; + KateFileTemplates *kft; +// QList *remove; + +}; + +class PluginViewKateFileTemplates : public Kate::PluginView, public Kate::XMLGUIClient +{ + Q_OBJECT + public: + PluginViewKateFileTemplates(KateFileTemplates *plugin,Kate::MainWindow* mainwindow); + virtual ~PluginViewKateFileTemplates(); + public Q_SLOTS: + void refreshMenu(); + private: + KateFileTemplates *m_plugin; +}; + +#endif // _PLUGIN_KATE_FILETEMPLATES_H_ +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/addons/kate/filetemplates/plugin/katefiletemplates.desktop b/kate/addons/kate/filetemplates/plugin/katefiletemplates.desktop new file mode 100644 index 00000000..aac11494 --- /dev/null +++ b/kate/addons/kate/filetemplates/plugin/katefiletemplates.desktop @@ -0,0 +1,119 @@ +[Desktop Entry] +Type=Service +ServiceTypes=Kate/Plugin +X-Kate-Version=2.8 +X-KDE-Library=katefiletemplates +Name=File Templates +Name[ar]=قوالب الملفات +Name[ast]=Plantíes de ficheros +Name[bg]=Файлови шаблони +Name[bs]=Šabloni datoteka +Name[ca]=Plantilles de fitxers +Name[ca@valencia]=Plantilles de fitxers +Name[cs]=Šablony souborů +Name[da]=Filskabeloner +Name[de]=Dateivorlagen +Name[el]=Πρότυπα αρχείων +Name[en_GB]=File Templates +Name[es]=Plantillas de archivos +Name[et]=Failimallid +Name[eu]=Fitxategien txantiloiak +Name[fi]=Mallipohjat +Name[fr]=Modèles de fichiers +Name[ga]=Teimpléid Chomhaid +Name[gl]=Modelos de ficheiro +Name[he]=תבניות קבצים +Name[hu]=Fájlsablonok +Name[ia]=Patronos de file +Name[is]=Sniðskrár +Name[it]=Modelli di file +Name[ja]=ファイルテンプレート +Name[kk]=Файл үлгілері +Name[km]=ពុម្ព​ឯកសារ​ +Name[ko]=파일 템플릿 +Name[lt]=Failų šablonai +Name[lv]=Datņu veidnes +Name[mr]=फाईल नमूने +Name[nb]=Filmaler +Name[nds]=Dateivörlagen +Name[nl]=Bestandssjablonen +Name[nn]=Filmalar +Name[pa]=ਫਾਇਲ ਟੈਪਲੇਟ +Name[pl]=Szablony plików +Name[pt]=Modelos de Ficheiros +Name[pt_BR]=Modelos de arquivos +Name[ro]=Șabloane de fișier +Name[ru]=Шаблоны файлов +Name[si]=ගොනු ආකෘති +Name[sk]=Šablóny súborov +Name[sl]=Predloge datotek +Name[sr]=Шаблони фајлова +Name[sr@ijekavian]=Шаблони фајлова +Name[sr@ijekavianlatin]=Šabloni fajlova +Name[sr@latin]=Šabloni fajlova +Name[sv]=Filmallar +Name[tg]=Қолабҳои файл +Name[tr]=Dosya Şablonları +Name[ug]=ھۆججەت قېلىپلىرى +Name[uk]=Шаблони файлів +Name[wa]=Modeles di fitchîs +Name[x-test]=xxFile Templatesxx +Name[zh_CN]=文件模板 +Name[zh_TW]=檔案樣本 +Comment=Create new files from templates +Comment[ar]=أنشئ ملفات جديدة من القوالب +Comment[ast]=Facer ficheros nuevos a partir de plantíes +Comment[bg]=Създаване на нови файлове от шаблони +Comment[bs]=Pravite nove datoteke prema šablonima +Comment[ca]=Crea fitxers nous des de plantilles +Comment[ca@valencia]=Crea fitxers nous des de plantilles +Comment[cs]=Vytvořit ze šablon nové soubory +Comment[da]=Opret nye filer fra skabeloner +Comment[de]=Erstellt neue Dateien aus Vorlagen +Comment[el]=Δημιουργία νέων αρχείων από πρότυπα +Comment[en_GB]=Create new files from templates +Comment[es]=Crear archivos nuevos a partir de plantillas +Comment[et]=Uute failide loomine malli põhjal +Comment[eu]=Sortu fitxategi berriak txantiloietatik +Comment[fi]=Luo uusia tiedostoja mallipohjista +Comment[fr]=Crée de nouveaux fichiers à partir de modèles +Comment[ga]=Cruthaigh comhaid nua ó theimpléid +Comment[gl]=Crea novos ficheiros a partir de modelos +Comment[he]=צור קבצים חדשים מתבניות +Comment[hu]=Új fájlok létrehozása sablonból +Comment[ia]=Crea nive files ex patronos +Comment[it]=Crea nuovi file partendo da modelli +Comment[ja]=テンプレートから新しいファイルを作成します +Comment[kk]=Үлгілерден жаңа файлдарды құру +Comment[km]=បង្កើត​ឯកសារ​ថ្មី​ពី​ពុម្ព​ +Comment[ko]=템플릿을 기반으로 새 파일을 만듭니다 +Comment[lt]=Kurkite naujus failus iš šablonų +Comment[lv]=Izveido jaunas datnes, izmantojot veidnes +Comment[mr]=नमून्यांवरून नवीन फाईल्स निर्माण करा +Comment[nb]=Opprett nye filer fra maler +Comment[nds]=Nieg Dateien ut Vörlagen opstellen +Comment[nl]=Maak aan de hand van sjablonen nieuwe bestanden aan +Comment[nn]=Lag nye filer frå malar +Comment[pa]=ਟੈਪਲੇਟ ਤੋਂ ਨਵੀਆਂ ਫਾਇਲਾਂ ਬਣਾਓ +Comment[pl]=Utwórz nowe pliki z szablonów +Comment[pt]=Criar ficheiros novos a partir de modelos +Comment[pt_BR]=Cria novos arquivos a partir de modelos +Comment[ro]=Creează fișiere noi din șabloane +Comment[ru]=Создание новых файлов из шаблонов +Comment[si]=ආකෘති මගින් නව ගොනු නිර්මාණය කරන්න +Comment[sk]=Vytvoriť nové súbory zo šablón +Comment[sl]=Ustvarite nove datoteke iz predlog +Comment[sr]=Правите нове фајлове према шаблонима +Comment[sr@ijekavian]=Правите нове фајлове према шаблонима +Comment[sr@ijekavianlatin]=Pravite nove fajlove prema šablonima +Comment[sr@latin]=Pravite nove fajlove prema šablonima +Comment[sv]=Skapa nya filer från mallar +Comment[tg]=Файлҳои навро аз қолабҳо эҷод кунед +Comment[tr]=Şablondan yeni dosyalar oluştur +Comment[ug]=قېلىپتىن يېڭى ھۆججەت قۇر +Comment[uk]=Створення нових файлів з шаблонів +Comment[wa]=Ahiver des noveas fitchîs a pårti d' modeles +Comment[x-test]=xxCreate new files from templatesxx +Comment[zh_CN]=根据模板新建文件 +Comment[zh_TW]=以樣本來建立新檔案 +author=Anders Lund, anders@alweb.dk diff --git a/kate/addons/kate/filetemplates/plugin/katetemplate.xml b/kate/addons/kate/filetemplates/plugin/katetemplate.xml new file mode 100644 index 00000000..0748481c --- /dev/null +++ b/kate/addons/kate/filetemplates/plugin/katetemplate.xml @@ -0,0 +1,87 @@ + + + + + + + + + template + group + documentname + author + description + highlight + icon + + + realname + username + email + organisation + date + time + datetime + month + year + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/addons/kate/filetemplates/plugin/ui.rc b/kate/addons/kate/filetemplates/plugin/ui.rc new file mode 100644 index 00000000..f9fbe687 --- /dev/null +++ b/kate/addons/kate/filetemplates/plugin/ui.rc @@ -0,0 +1,15 @@ + + + + &File + + + &Settings + + + + + + + + diff --git a/kate/addons/kate/filetemplates/templates/CMakeLists.txt b/kate/addons/kate/filetemplates/templates/CMakeLists.txt new file mode 100644 index 00000000..0c93bda8 --- /dev/null +++ b/kate/addons/kate/filetemplates/templates/CMakeLists.txt @@ -0,0 +1,14 @@ +########### install template files ############### + +install( + FILES + html.katetemplate + cpplgpl.cpp.katetemplate + cpplgpl.hh.katetemplate + cppgpl.cpp.katetemplate + cppgpl.hh.katetemplate + docbookchapter.xml.katetemplate + language.xml.katetemplate + FindXXX.cmake.katetemplate + DESTINATION ${KDE4_DATA_INSTALL_DIR}/kate/plugins/katefiletemplates/templates +) diff --git a/kate/addons/kate/filetemplates/templates/FindXXX.cmake.katetemplate b/kate/addons/kate/filetemplates/templates/FindXXX.cmake.katetemplate new file mode 100644 index 00000000..446ac913 --- /dev/null +++ b/kate/addons/kate/filetemplates/templates/FindXXX.cmake.katetemplate @@ -0,0 +1,56 @@ +katetemplate: Template=FindXXX.cmake Group=Other Highlight=CMake +katetemplate: Author=Alex Turbov +katetemplate: Description=CMake finder skeleton wrapper based on pkg-config +# - Find ${package_description} using `pkg-config` +# Search for %{package_description} and set the following variables: +# %{package}_FOUND - is package found +# %{package}_VERSION - found package version +# %{package}_INCLUDE_DIRS - dir w/ header files +# %{package}_DEFINITIONS - other than `-I' compiler flags +# %{package}_LIBRARIES - libs for dynamic linkage +#%{cursor} + +# +# Copyright (C) %{year}, %{fullname} <%{email}> +# +# Distributed under the OSI-approved BSD License (the "License"); +# see accompanying file LICENSE for details. +# + +# Check if already in cache +# NOTE Feel free to check/change/add any other vars +if(NOT %{package}_LIBRARIES) + + if(%{package}_FIND_QUIETLY) + set(_pkg_find_quietly QUIET) + endif() + + set(_pkg_module_name "${pkg_config_module_name}") + if(${package}_FIND_VERSION) + if(%{package}_FIND_VERSION_EXACT) + set(_pkg_module_name "\${_pkg_module_name}=\${%{package}_FIND_VERSION}") + else() + set(_pkg_module_name "\${_pkg_module_name}>=\${%{package}_FIND_VERSION}") + endif() + endif() + + find_package(PkgConfig \${_pkg_find_quietly}) + pkg_check_modules(%{package} \${_pkg_module_name} \${_pkg_find_quietly}) + + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args( + %{package_finder_name} + FOUND_VAR %{package}_FOUND + REQUIRED_VARS %{package}_LIBRARIES + VERSION_VAR %{package}_VERSION + ) + + if(%{package}_FOUND) + # Copy other than `-I' flags to `XXX_DEFINITIONS' variable, + # according CMake guide (/usr/share/cmake/Modules/readme.txt) + set(%{package}_DEFINITIONS \${%{package}_CFLAGS_OTHER}) + # Unset non-standard variable + unset(%{package}_CFLAGS_OTHER) + endif() +endif() + diff --git a/kate/addons/kate/filetemplates/templates/cppgpl.cpp.katetemplate b/kate/addons/kate/filetemplates/templates/cppgpl.cpp.katetemplate new file mode 100644 index 00000000..a4796154 --- /dev/null +++ b/kate/addons/kate/filetemplates/templates/cppgpl.cpp.katetemplate @@ -0,0 +1,24 @@ +katetemplate: template=C++ Source File (GPL) group=Source Code documentname=New%N.cpp highlight=C++ +katetemplate: description=A very simple GPL C++ source file +katetemplate: author= Anders Lund +/* + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + + --- + Copyright (C) %{year}, %{fullname} <%{email}> + */ + +${cursor} \ No newline at end of file diff --git a/kate/addons/kate/filetemplates/templates/cppgpl.hh.katetemplate b/kate/addons/kate/filetemplates/templates/cppgpl.hh.katetemplate new file mode 100644 index 00000000..7e0bd028 --- /dev/null +++ b/kate/addons/kate/filetemplates/templates/cppgpl.hh.katetemplate @@ -0,0 +1,34 @@ +katetemplate: template=C++ Header (GPL) group=Source Code documentname=New%N.hh highlight=C++ +katetemplate: description=A very simple GPL C++ header file +katetemplate: author= Anders Lund +/* + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + + --- + Copyright (C) %{year}, %{fullname} <%{email}> + */ + +#ifndef _${ClassName}_h_ +#define _${ClassName}_h_ + +class ${ClassName} : public ${Super} { + public: + ${ClassName}( ${cursor} ); + ~${ClassName}(); + +} + +#endif // _${ClassName}_h_ diff --git a/kate/addons/kate/filetemplates/templates/cpplgpl.cpp.katetemplate b/kate/addons/kate/filetemplates/templates/cpplgpl.cpp.katetemplate new file mode 100644 index 00000000..abbb58f4 --- /dev/null +++ b/kate/addons/kate/filetemplates/templates/cpplgpl.cpp.katetemplate @@ -0,0 +1,23 @@ +katetemplate: template=C++ Source File (LGPL) group=Source Code documentname=New%N.cpp highlight=C++ +katetemplate: description=A very simple LGPL C++ source file +katetemplate: author= Anders Lund +/* + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + + --- + Copyright (C) %{year}, %{fullname} <%{email}> +*/ + +${cursor} diff --git a/kate/addons/kate/filetemplates/templates/cpplgpl.hh.katetemplate b/kate/addons/kate/filetemplates/templates/cpplgpl.hh.katetemplate new file mode 100644 index 00000000..301f34b3 --- /dev/null +++ b/kate/addons/kate/filetemplates/templates/cpplgpl.hh.katetemplate @@ -0,0 +1,32 @@ +katetemplate: template=C++ Header (LGPL) group=Source Code documentname=New%N.hh highlight=C++ +katetemplate: description=A very simple LGPL C++ header file +katetemplate: author= Anders Lund +/* + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + + --- + Copyright (C) %{year}, %{fullname} <%{email}> +*/ +#ifndef _${ClassName}_h_ +#define _${ClassName}_h_ + +class ${ClassName} : public ${Super} { + public: + ${ClassName}( ${cursor} ); + ~${ClassName}(); + +} + +#endif // _${ClassName}_h_ diff --git a/kate/addons/kate/filetemplates/templates/docbookchapter.xml.katetemplate b/kate/addons/kate/filetemplates/templates/docbookchapter.xml.katetemplate new file mode 100644 index 00000000..581682c3 --- /dev/null +++ b/kate/addons/kate/filetemplates/templates/docbookchapter.xml.katetemplate @@ -0,0 +1,75 @@ +katetemplate: Template=Kate Plugin Docbook Chapter DocumentName=chapter%N.docbook +katetemplate: Group=Documentation highlight=XML +katetemplate: Description=This creates a suitable beginning of a docbook chapter for a Kate plugin. +katetemplate: Author= Anders Lund + + + +${title} + + + +%{firstname} +%{lastname} + +
%{email}
+
+
+
+%{date} + + + + + + + +KDE +kate + + + +
+ +Introduction + + + + +Menu Structure + + + + + +Tools +Validate XML + + + + + + + + + +Thanks and Acknowledgments + + +&kate; Plugin PROJECT copyright %{year} %{fullname} +%{email}. + + + +Documentation copyright %{year} %{fullname} + + + + + +&underGPL; + + +
+ + diff --git a/kate/addons/kate/filetemplates/templates/html.katetemplate b/kate/addons/kate/filetemplates/templates/html.katetemplate new file mode 100644 index 00000000..40718e3d --- /dev/null +++ b/kate/addons/kate/filetemplates/templates/html.katetemplate @@ -0,0 +1,18 @@ +katetemplate: template=HTML 4.01 Strict Document documentname=New%N.html +katetemplate: highlight=HTML group=Internet icon=www +katetemplate: description=This will create a very basic HTML file with the HTML 4.01 strict DTD. +katetemplate: author=Anders Lund +katetemplate: this text is supposedly thrown away. + + + + + ${title} + + + +

${title}

+ ${cursor} + + diff --git a/kate/addons/kate/filetemplates/templates/language.xml.katetemplate b/kate/addons/kate/filetemplates/templates/language.xml.katetemplate new file mode 100644 index 00000000..420ba84a --- /dev/null +++ b/kate/addons/kate/filetemplates/templates/language.xml.katetemplate @@ -0,0 +1,60 @@ +katetemplate: Documentname=New language.xml (%N) Template=Kate Highlight Definition highlight=XML +katetemplate: Author=Dominik Haumann +katetemplate: Description=This template will create the basics of a kate highlight definition file. + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/addons/kate/helloworld/CMakeLists.txt b/kate/addons/kate/helloworld/CMakeLists.txt new file mode 100644 index 00000000..7b02bfcc --- /dev/null +++ b/kate/addons/kate/helloworld/CMakeLists.txt @@ -0,0 +1,20 @@ +kde4_add_plugin(katehelloworldplugin plugin_katehelloworld.cpp) + +target_link_libraries(katehelloworldplugin + ${KDE4_KDEUI_LIBS} + kateinterfaces +) + +########### install files ############### +install( + TARGETS katehelloworldplugin + DESTINATION ${KDE4_PLUGIN_INSTALL_DIR} +) +install( + FILES ui.rc + DESTINATION ${KDE4_DATA_INSTALL_DIR}/kate/plugins/katehelloworld +) +install( + FILES katehelloworld.desktop + DESTINATION ${KDE4_SERVICES_INSTALL_DIR} +) diff --git a/kate/addons/kate/helloworld/Messages.sh b/kate/addons/kate/helloworld/Messages.sh new file mode 100644 index 00000000..24dc5829 --- /dev/null +++ b/kate/addons/kate/helloworld/Messages.sh @@ -0,0 +1,3 @@ +#! /bin/sh +$EXTRACTRC *.rc >> rc.cpp +$XGETTEXT *.cpp -o $podir/katehelloworld.pot diff --git a/kate/addons/kate/helloworld/katehelloworld.desktop b/kate/addons/kate/helloworld/katehelloworld.desktop new file mode 100644 index 00000000..fe1a9d82 --- /dev/null +++ b/kate/addons/kate/helloworld/katehelloworld.desktop @@ -0,0 +1,116 @@ +[Desktop Entry] +Type=Service +ServiceTypes=Kate/Plugin +X-KDE-Library=katehelloworldplugin +X-Kate-Version=2.9 +Name=Hello World Plugin +Name[ar]=ملحقة أهلًا يا عالم +Name[ast]=Complementu Hola mundu +Name[bg]=Приставка "Здравей свят!" +Name[bs]=Zdravo‑svijete priključak +Name[ca]=Connector Hola món +Name[ca@valencia]=Connector Hola món +Name[cs]=Modul Hello World +Name[da]="Hello World"-plugin +Name[de]=Hello-World-Modul +Name[el]=Πρόσθετο Hello World +Name[en_GB]=Hello World Plugin +Name[es]=Complemento Hola mundo +Name[et]='Tere, maailm' plugin +Name[eu]=Kaixo mundua plugina +Name[fi]=Hei maailma -liitännäinen +Name[fr]=Module externe « Bonjour tout le monde » +Name[ga]=Breiseán 'Dia Dhuit a Dhomhain' +Name[gl]=Complemento de «Olá, mundo» +Name[he]=תוסך שלום עולם +Name[hu]=„Helló Világ” bővítmény +Name[ia]=Plug-in de Salute Mondo +Name[it]=Estensione Ciao Mondo +Name[ja]=Hello World プラグイン +Name[kk]=Сынақ плагині +Name[km]=សួស្ដី​កម្មវិធី​ជំនួយ​ពាក្យ​​​​​​​​​​​​​​​ +Name[ko]=Hello World 플러그인 +Name[lt]=Sveikas pasauli! priedas +Name[lv]=“Sveika, pasaule!” spraudnis +Name[mr]=हेल्लो वर्ल्ड प्लगइन +Name[nb]=Programtillegg-eksempel +Name[nds]="Moin-Welt"-Moduul +Name[nl]=Plug-in voor Hello World +Name[nn]=Eksempel-tillegg +Name[pa]=ਹੈਲੋ ਵਰਲਡ ਪਲੱਗਇਨ +Name[pl]=Wtyczka "Witaj świecie" +Name[pt]='Plugin' de Olá Mundo +Name[pt_BR]=Plugin de teste +Name[ro]=Modul Hello World +Name[ru]=Тестовый модуль +Name[si]=ලෝකයට ආයුබෝවන් ප්ලගිනය +Name[sk]=Hello World Plugin +Name[sl]=Vstavek »Pozdravljen svet« +Name[sr]=Здраво‑свете прикључак +Name[sr@ijekavian]=Здраво‑свијете прикључак +Name[sr@ijekavianlatin]=Zdravo‑svijete priključak +Name[sr@latin]=Zdravo‑svete priključak +Name[sv]=Hello world-insticksprogram +Name[tg]=Плагини Салом ҷаҳон +Name[tr]=Merhaba Dünya Eklentisi +Name[ug]=سالام دۇنيا قىستۇرمىسى +Name[uk]=Тестовий додаток +Name[x-test]=xxHello World Pluginxx +Name[zh_CN]=Hello World 插件 +Name[zh_TW]=Hello World 外掛程式 +Comment=Your short description about the plugin goes here +Comment[ar]=الوصف القصير لهذه الملحقة يكون هنا +Comment[ast]=Equí va una descripción curtia sobro'l so complementu +Comment[bg]=Кратко описание на приставката +Comment[bs]=Ovdje ide kratak opis priključka +Comment[ca]=Aquí va una breu descripció sobre el connector +Comment[ca@valencia]=Ací va una breu descripció sobre el connector +Comment[cs]=Sem patří krátký popis vašeho modulu +Comment[da]=Din korte beskrivelse af pluginet skal være her +Comment[de]=Ihre Kurzbeschreibung des Moduls gehört hier hinein +Comment[el]=Η σύντομη περιγραφή σας σχετικά με το πρόσθετο +Comment[en_GB]=Your short description about the plugin goes here +Comment[es]=Aquí va una breve descripción sobre su complemento +Comment[et]=Siia võib kirjutada oma plugina kirjelduse +Comment[eu]=Pluginari buruzko zure deskribapena hemen doa +Comment[fi]=Lyhyt kuvaus liitännäisestäsi tähän +Comment[fr]=Une courte description de votre module externe s'insère ici +Comment[ga]=Cuir do chur síos gearr ar an mbreiseán anseo +Comment[gl]=A descrición curta acerca do complemento vai aquí +Comment[he]=כאן אתה צריך להוסיף את התיאור של התוסף +Comment[hu]=A bővítőmodul rövid leírása +Comment[ia]=Tu breve description re le plug-in va hic +Comment[it]=La tua breve descrizione dell'estensione va qui +Comment[ja]=プラグインの簡単な説明をここに書きます +Comment[kk]=Мұнда плагинінің қысқа сипаттамасы болады +Comment[km]=ការ​ពិពណ៌នា​សង្ខេប​របស់​អ្នក​អំពី​កម្មវិធី​ជំនួយ​នៅ​ទីនេះ +Comment[ko]=플러그인에 대한 짧은 설명을 입력하십시오 +Comment[lt]=Čia turėtų būti trumpas priedo aprašymas +Comment[lv]=Šeit jābūt nelielam spraudņa aprakstam +Comment[mr]=प्लगइन करिता थोडक्यात वर्णन +Comment[nb]=Din kortbeskrivelse av programtillegget skal legges inn her +Comment[nds]=Hier kummt Dien korte Beschrieven vun't Moduul hen +Comment[ne]=यहाँ प्लगइन जाने बारे तपाईँको छोटो वर्णन +Comment[nl]=Voer hier een korte beschrijving van uw plug-in in. +Comment[nn]=Skriv inn ei kort skildring av tillegget her +Comment[pa]=ਪਲੱਗਇਨ ਬਾਰੇ ਤੁਹਾਡੀ ਸੰਖੇਪ ਜਾਣਕਾਰੀ ਇੱਥੇ +Comment[pl]=Tutaj powinien znaleźć się krótki opis wtyczki +Comment[pt]=Aqui aparece a sua descrição breve sobre o 'plugin' +Comment[pt_BR]=Sua descrição resumida sobre o plugin aparece aqui +Comment[ro]=Aici merge o scurtă descriere a modulului +Comment[ru]=А здесь должно быть описание модуля +Comment[si]=ප්ලගිනය පිලිබඳ ඔබේ කෙටි විස්තරය මෙහි දැක්වේ +Comment[sk]=Sem ide krátky popis vášho pluginu +Comment[sl]=Sem spada kratek opis vašega vstavka +Comment[sr]=Овде иде кратак опис прикључка +Comment[sr@ijekavian]=Овдје иде кратак опис прикључка +Comment[sr@ijekavianlatin]=Ovdje ide kratak opis priključka +Comment[sr@latin]=Ovde ide kratak opis priključka +Comment[sv]=Kort beskrivning av vad insticksprogrammet gör +Comment[tg]=Шарҳи кӯтоҳи шумо дар бораи ин плагин дар ин ҷо сар мешавад +Comment[tr]=Eklenti hakkındaki kısa yorumunuzu buraya ekleyin +Comment[ug]=بۇ قىستۇرمىنىڭ قىسقىچە چۈشەندۈرۈشى +Comment[uk]=Тут слід вказати ваш короткий опис додатка +Comment[x-test]=xxYour short description about the plugin goes herexx +Comment[zh_CN]=关于此插件的简短描述 +Comment[zh_TW]=在這裡描述您的外掛程式 diff --git a/kate/addons/kate/helloworld/plugin_katehelloworld.cpp b/kate/addons/kate/helloworld/plugin_katehelloworld.cpp new file mode 100644 index 00000000..a7d52e85 --- /dev/null +++ b/kate/addons/kate/helloworld/plugin_katehelloworld.cpp @@ -0,0 +1,100 @@ +/* This file is part of the Kate project. + * + * Copyright (C) 2010 Christoph Cullmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "plugin_katehelloworld.h" +#include "moc_plugin_katehelloworld.cpp" + +#include +#include + +#include +#include +#include +#include +#include +#include + +K_PLUGIN_FACTORY(KatePluginHelloWorldFactory, registerPlugin();) +K_EXPORT_PLUGIN(KatePluginHelloWorldFactory(KAboutData("katehelloworld","katehelloworld",ki18n("Hello World"), "0.1", ki18n("Example kate plugin"))) ) + +KatePluginHelloWorld::KatePluginHelloWorld( QObject* parent, const QList& ) + : Kate::Plugin( (Kate::Application*)parent, "kate-hello-world-plugin" ) +{ +} + +KatePluginHelloWorld::~KatePluginHelloWorld() +{ +} + +Kate::PluginView *KatePluginHelloWorld::createView( Kate::MainWindow *mainWindow ) +{ + return new KatePluginHelloWorldView( mainWindow ); +} + + +KatePluginHelloWorldView::KatePluginHelloWorldView( Kate::MainWindow *mainWin ) + : Kate::PluginView( mainWin ), + Kate::XMLGUIClient(KatePluginHelloWorldFactory::componentData()) +{ + KAction *a = actionCollection()->addAction( "edit_insert_helloworld" ); + a->setText( i18n("Insert Hello World") ); + connect( a, SIGNAL(triggered(bool)), this, SLOT(slotInsertHello()) ); + + mainWindow()->guiFactory()->addClient( this ); +} + +KatePluginHelloWorldView::~KatePluginHelloWorldView() +{ + mainWindow()->guiFactory()->removeClient( this ); +} + +void KatePluginHelloWorldView::slotInsertHello() +{ + if (!mainWindow()) { + return; + } + + KTextEditor::View *kv = mainWindow()->activeView(); + + if (kv) { + kv->insertText( "Hello World" ); + } +} + +void KatePluginHelloWorldView::readSessionConfig( KConfigBase* config, const QString& groupPrefix ) +{ + // If you have session-dependant settings, load them here. + // If you have application wide settings, you have to read your own KConfig, + // see the Kate::Plugin docs for more information. + Q_UNUSED( config ); + Q_UNUSED( groupPrefix ); +} + +void KatePluginHelloWorldView::writeSessionConfig( KConfigBase* config, const QString& groupPrefix ) +{ + // If you have session-dependant settings, save them here. + // If you have application wide settings, you have to create your own KConfig, + // see the Kate::Plugin docs for more information. + Q_UNUSED( config ); + Q_UNUSED( groupPrefix ); +} + +// kate: space-indent on; indent-width 2; replace-tabs on; + diff --git a/kate/addons/kate/helloworld/plugin_katehelloworld.h b/kate/addons/kate/helloworld/plugin_katehelloworld.h new file mode 100644 index 00000000..895fac56 --- /dev/null +++ b/kate/addons/kate/helloworld/plugin_katehelloworld.h @@ -0,0 +1,57 @@ +/* This file is part of the Kate project. + * + * Copyright (C) 2010 Christoph Cullmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _PLUGIN_KATE_HELLOWORLD_H_ +#define _PLUGIN_KATE_HELLOWORLD_H_ + +#include +#include +#include + +class KatePluginHelloWorld : public Kate::Plugin +{ + Q_OBJECT + + public: + explicit KatePluginHelloWorld( QObject* parent = 0, const QList& = QList() ); + virtual ~KatePluginHelloWorld(); + + Kate::PluginView *createView( Kate::MainWindow *mainWindow ); +}; + +class KatePluginHelloWorldView : public Kate::PluginView, public Kate::XMLGUIClient +{ + Q_OBJECT + + public: + KatePluginHelloWorldView( Kate::MainWindow *mainWindow ); + ~KatePluginHelloWorldView(); + + virtual void readSessionConfig( KConfigBase* config, const QString& groupPrefix ); + virtual void writeSessionConfig( KConfigBase* config, const QString& groupPrefix ); + + public slots: + void slotInsertHello(); +}; + +#endif + +// kate: space-indent on; indent-width 2; replace-tabs on; + diff --git a/kate/addons/kate/helloworld/ui.rc b/kate/addons/kate/helloworld/ui.rc new file mode 100644 index 00000000..ccc2dac0 --- /dev/null +++ b/kate/addons/kate/helloworld/ui.rc @@ -0,0 +1,8 @@ + + + + &Tools + + + + diff --git a/kate/addons/kate/kate-ctags/CMakeLists.txt b/kate/addons/kate/kate-ctags/CMakeLists.txt new file mode 100644 index 00000000..c117f567 --- /dev/null +++ b/kate/addons/kate/kate-ctags/CMakeLists.txt @@ -0,0 +1,33 @@ +include_directories(${CMAKE_CURRENT_BINARY_DIR}) + +set(ctagsplugin_SRC + readtags.c + tags.cpp + ctagskinds.cpp + kate_ctags_view.cpp + kate_ctags_plugin.cpp +) + +kde4_add_plugin(katectagsplugin ${ctagsplugin_SRC}) + +target_link_libraries(katectagsplugin + ${KDE4_KDECORE_LIBS} + kateinterfaces + ${KDE4_KTEXTEDITOR_LIBS} +) + +install( + TARGETS katectagsplugin + DESTINATION ${KDE4_PLUGIN_INSTALL_DIR} +) + +########### install files ############### + +install( + FILES ui.rc + DESTINATION ${KDE4_DATA_INSTALL_DIR}/kate/plugins/katectags +) +install( + FILES katectagsplugin.desktop + DESTINATION ${KDE4_SERVICES_INSTALL_DIR} +) diff --git a/kate/addons/kate/kate-ctags/CTagsGlobalConfig.ui b/kate/addons/kate/kate-ctags/CTagsGlobalConfig.ui new file mode 100644 index 00000000..e52fd0cf --- /dev/null +++ b/kate/addons/kate/kate-ctags/CTagsGlobalConfig.ui @@ -0,0 +1,100 @@ + + + CTagsGlobalConfig + + + + + + Session-global index targets + + + + + + + + + + + Qt::Vertical + + + + 1 + 1 + + + + + + + + Add + + + + + + + Remove + + + + + + + Qt::Horizontal + + + + + + + Update + + + + + + + + + + + + CTags command + + + + + + true + + + + + + + + + + + KListWidget + QListWidget +
klistwidget.h
+
+ + KPushButton + QPushButton +
kpushbutton.h
+
+ + KLineEdit + QLineEdit +
klineedit.h
+
+
+ + +
diff --git a/kate/addons/kate/kate-ctags/Messages.sh b/kate/addons/kate/kate-ctags/Messages.sh new file mode 100755 index 00000000..c0312fcf --- /dev/null +++ b/kate/addons/kate/kate-ctags/Messages.sh @@ -0,0 +1,5 @@ +#! /bin/sh +$EXTRACTRC `find . -name \*.ui` >> rc.cpp +$XGETTEXT *.cpp -o $podir/kate-ctags-plugin.pot +rm -f rc.cpp + diff --git a/kate/addons/kate/kate-ctags/ctagskinds.cpp b/kate/addons/kate/kate-ctags/ctagskinds.cpp new file mode 100644 index 00000000..fbf31dea --- /dev/null +++ b/kate/addons/kate/kate-ctags/ctagskinds.cpp @@ -0,0 +1,305 @@ +/*************************************************************************** + * Copyright (C) 2001-2002 by Bernd Gehrmann * + * bernd@kdevelop.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "ctagskinds.h" + +#include + +struct CTagsKindMapping { + char abbrev; + const char *verbose; +}; + + +struct CTagsExtensionMapping { + const char *extension; + CTagsKindMapping *kinds; +}; + + +static CTagsKindMapping kindMappingAsm[] = { + { 'd', I18N_NOOP2("Tag Type", "define") }, + { 'l', I18N_NOOP2("Tag Type", "label") }, + { 'm', I18N_NOOP2("Tag Type", "macro") }, + { 0 , 0 } +}; + + +static CTagsKindMapping kindMappingAsp[] = { + { 'f', I18N_NOOP2("Tag Type", "function") }, + { 's', I18N_NOOP2("Tag Type", "subroutine") }, + { 0 , 0 } +}; + + +static CTagsKindMapping kindMappingAwk[] = { + { 'f', I18N_NOOP2("Tag Type", "function") }, + { 0 , 0 } +}; + + +static CTagsKindMapping kindMappingBeta[] = { + { 'f', I18N_NOOP2("Tag Type", "fragment definition") }, + { 'p', I18N_NOOP2("Tag Type", "any pattern") }, + { 's', I18N_NOOP2("Tag Type", "slot") }, + { 'v', I18N_NOOP2("Tag Type", "pattern") }, + { 0 , 0 } +}; + + +static CTagsKindMapping kindMappingC[] = { + { 'c', I18N_NOOP2("Tag Type", "class") }, + { 'd', I18N_NOOP2("Tag Type", "macro") }, + { 'e', I18N_NOOP2("Tag Type", "enumerator") }, + { 'f', I18N_NOOP2("Tag Type", "function") }, + { 'g', I18N_NOOP2("Tag Type", "enumeration") }, + { 'm', I18N_NOOP2("Tag Type", "member") }, + { 'n', I18N_NOOP2("Tag Type", "namespace") }, + { 'p', I18N_NOOP2("Tag Type", "prototype") }, + { 's', I18N_NOOP2("Tag Type", "struct") }, + { 't', I18N_NOOP2("Tag Type", "typedef") }, + { 'u', I18N_NOOP2("Tag Type", "union") }, + { 'v', I18N_NOOP2("Tag Type", "variable") }, + { 'x', I18N_NOOP2("Tag Type", "external variable") }, + { 0 , 0 } +}; + + +static CTagsKindMapping kindMappingCobol[] = { + { 'p', I18N_NOOP2("Tag Type", "paragraph") }, + { 0 , 0 } +}; + + +static CTagsKindMapping kindMappingEiffel[] = { + { 'c', I18N_NOOP2("Tag Type", "class") }, + { 'f', I18N_NOOP2("Tag Type", "feature") }, + { 'l', I18N_NOOP2("Tag Type", "local entity") }, + { 0 , 0 } +}; + + +static CTagsKindMapping kindMappingFortran[] = { + { 'b', I18N_NOOP2("Tag Type", "block") }, + { 'c', I18N_NOOP2("Tag Type", "common") }, + { 'e', I18N_NOOP2("Tag Type", "entry") }, + { 'f', I18N_NOOP2("Tag Type", "function") }, + { 'i', I18N_NOOP2("Tag Type", "interface") }, + { 'k', I18N_NOOP2("Tag Type", "type component") }, + { 'l', I18N_NOOP2("Tag Type", "label") }, + { 'L', I18N_NOOP2("Tag Type", "local") }, + { 'm', I18N_NOOP2("Tag Type", "module") }, + { 'n', I18N_NOOP2("Tag Type", "namelist") }, + { 'p', I18N_NOOP2("Tag Type", "program") }, + { 's', I18N_NOOP2("Tag Type", "subroutine") }, + { 't', I18N_NOOP2("Tag Type", "type") }, + { 'v', I18N_NOOP2("Tag Type", "variable") }, + { 0 , 0 } +}; + + +static CTagsKindMapping kindMappingJava[] = { + { 'c', I18N_NOOP2("Tag Type", "class") }, + { 'f', I18N_NOOP2("Tag Type", "field") }, + { 'i', I18N_NOOP2("Tag Type", "interface") }, + { 'm', I18N_NOOP2("Tag Type", "method") }, + { 'p', I18N_NOOP2("Tag Type", "package") }, + { 0 , 0 } +}; + + +static CTagsKindMapping kindMappingLisp[] = { + { 'f', I18N_NOOP2("Tag Type", "function") }, + { 0 , 0 } +}; + + +static CTagsKindMapping kindMappingMake[] = { + { 'm', I18N_NOOP2("Tag Type", "macro") }, + { 0 , 0 } +}; + + +static CTagsKindMapping kindMappingPascal[] = { + { 'f', I18N_NOOP2("Tag Type", "function") }, + { 'p', I18N_NOOP2("Tag Type", "procedure") }, + { 0 , 0 } +}; + + +static CTagsKindMapping kindMappingPerl[] = { + { 's', I18N_NOOP2("Tag Type", "subroutine") }, + { 0 , 0 } +}; + + +static CTagsKindMapping kindMappingPHP[] = { + { 'c', I18N_NOOP2("Tag Type", "class") }, + { 'f', I18N_NOOP2("Tag Type", "function") }, + { 0 , 0 } +}; + + +static CTagsKindMapping kindMappingPython[] = { + { 'c', I18N_NOOP2("Tag Type", "class") }, + { 'f', I18N_NOOP2("Tag Type", "function") }, + { 0 , 0 } +}; + + +static CTagsKindMapping kindMappingRexx[] = { + { 's', I18N_NOOP2("Tag Type", "subroutine") }, + { 0 , 0 } +}; + + +static CTagsKindMapping kindMappingRuby[] = { + { 'c', I18N_NOOP2("Tag Type", "class") }, + { 'f', I18N_NOOP2("Tag Type", "function") }, + { 'm', I18N_NOOP2("Tag Type", "mixin") }, + { 0 , 0 } +}; + + +static CTagsKindMapping kindMappingScheme[] = { + { 'f', I18N_NOOP2("Tag Type", "function") }, + { 's', I18N_NOOP2("Tag Type", "set") }, + { 0 , 0 } +}; + + +static CTagsKindMapping kindMappingSh[] = { + { 'f', I18N_NOOP2("Tag Type", "function") }, + { 0 , 0 } +}; + + +static CTagsKindMapping kindMappingSlang[] = { + { 'f', I18N_NOOP2("Tag Type", "function") }, + { 'n', I18N_NOOP2("Tag Type", "namespace") }, + { 0 , 0 } +}; + + +static CTagsKindMapping kindMappingTcl[] = { + { 'p', I18N_NOOP2("Tag Type", "procedure") }, + { 0 , 0 } +}; + + +static CTagsKindMapping kindMappingVim[] = { + { 'f', I18N_NOOP2("Tag Type", "function") }, + { 0 , 0 } +}; + + +static CTagsExtensionMapping extensionMapping[] = { + { "asm", kindMappingAsm }, + { "s", kindMappingAsm }, + { "S", kindMappingAsm }, + { "asp", kindMappingAsp }, + { "asa", kindMappingAsp }, + { "awk", kindMappingAwk }, + { "c++", kindMappingC }, + { "cc", kindMappingC }, + { "cp" , kindMappingC }, + { "cpp", kindMappingC }, + { "cxx", kindMappingC }, + { "h" , kindMappingC }, + { "h++", kindMappingC }, + { "hh" , kindMappingC }, + { "hp" , kindMappingC }, + { "hpp", kindMappingC }, + { "hxx", kindMappingC }, + { "beta", kindMappingBeta }, + { "cob", kindMappingCobol }, + { "COB", kindMappingCobol }, + { "e", kindMappingEiffel }, + { "f" , kindMappingFortran }, + { "for" , kindMappingFortran }, + { "ftn" , kindMappingFortran }, + { "f77" , kindMappingFortran }, + { "f90" , kindMappingFortran }, + { "f95" , kindMappingFortran }, + { "java", kindMappingJava }, + { "cl", kindMappingLisp }, + { "clisp", kindMappingLisp }, + { "el", kindMappingLisp }, + { "l", kindMappingLisp }, + { "lisp", kindMappingLisp }, + { "lsp", kindMappingLisp }, + { "ml", kindMappingLisp }, + { "mak", kindMappingMake }, + { "p", kindMappingPascal }, + { "pas", kindMappingPascal }, + { "pl", kindMappingPerl }, + { "pm", kindMappingPerl }, + { "perl", kindMappingPerl }, + { "php", kindMappingPHP }, + { "php3", kindMappingPHP }, + { "phtml", kindMappingPHP }, + { "py", kindMappingPython }, + { "python", kindMappingPython }, + { "cmd", kindMappingRexx }, + { "rexx", kindMappingRexx }, + { "rx", kindMappingRexx }, + { "rb", kindMappingRuby }, + { "sch", kindMappingScheme }, + { "scheme", kindMappingScheme }, + { "scm", kindMappingScheme }, + { "sm", kindMappingScheme }, + { "SCM", kindMappingScheme }, + { "SM", kindMappingScheme }, + { "sh", kindMappingSh }, + { "SH", kindMappingSh }, + { "bsh", kindMappingSh }, + { "bash", kindMappingSh }, + { "ksh", kindMappingSh }, + { "zsh", kindMappingSh }, + { "sl", kindMappingSlang }, + { "tcl", kindMappingTcl }, + { "wish", kindMappingTcl }, + { "vim", kindMappingVim }, + { 0 , 0 } +}; + + +static CTagsKindMapping *findKindMapping(const QString &extension) +{ + const char *pextension = extension.toLocal8Bit(); + + CTagsExtensionMapping *pem = extensionMapping; + while (pem->extension != 0) { + if (strcmp(pem->extension, pextension) == 0) + return pem->kinds; + ++pem; + } + + return 0; +} + + +QString CTagsKinds::findKind( const char * kindChar, const QString &extension ) +{ + if ( kindChar == 0 ) return QString(); + + CTagsKindMapping *kindMapping = findKindMapping(extension); + if (kindMapping) { + CTagsKindMapping *pkm = kindMapping; + while (pkm->verbose != 0) { + if (pkm->abbrev == *kindChar) + return i18nc("Tag Type", pkm->verbose); + ++pkm; + } + } + + return QString(); +} diff --git a/kate/addons/kate/kate-ctags/ctagskinds.h b/kate/addons/kate/kate-ctags/ctagskinds.h new file mode 100644 index 00000000..82c816db --- /dev/null +++ b/kate/addons/kate/kate-ctags/ctagskinds.h @@ -0,0 +1,24 @@ +/*************************************************************************** + * Copyright (C) 2001-2002 by Bernd Gehrmann * + * bernd@kdevelop.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef CTAGSKINDS_H +#define CTAGSKINDS_H + +#include + + +class CTagsKinds +{ +public: + static QString findKind( const char * kindChar, const QString &extension); +}; + +#endif diff --git a/kate/addons/kate/kate-ctags/kate_ctags.ui b/kate/addons/kate/kate-ctags/kate_ctags.ui new file mode 100644 index 00000000..57178808 --- /dev/null +++ b/kate/addons/kate/kate-ctags/kate_ctags.ui @@ -0,0 +1,195 @@ + + + kateCtags + + + + + + 0 + + + + Lookup + + + + + + true + + + + + + + Update Index + + + + + + + false + + + + Tag + + + + + Type + + + + + File + + + + + + + + + Index Targets + + + + + + + + + Add + + + + + + + Remove + + + + + + + Qt::Vertical + + + + 13 + 16 + + + + + + + + Update Index + + + + + + + + Database + + + + + + Qt::RightToLeft + + + CTags database file + + + + + + + Qt::RightToLeft + + + CTags command + + + + + + + true + + + true + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + ... + + + true + + + + + + + + + + + + KUrlRequester + QFrame +
kurlrequester.h
+
+ + KListWidget + QListWidget +
klistwidget.h
+
+ + KPushButton + QPushButton +
kpushbutton.h
+
+ + KLineEdit + QLineEdit +
klineedit.h
+
+ + KTabWidget + QTabWidget +
ktabwidget.h
+ 1 +
+
+ + +
diff --git a/kate/addons/kate/kate-ctags/kate_ctags_plugin.cpp b/kate/addons/kate/kate-ctags/kate_ctags_plugin.cpp new file mode 100644 index 00000000..376abbcf --- /dev/null +++ b/kate/addons/kate/kate-ctags/kate_ctags_plugin.cpp @@ -0,0 +1,249 @@ +/* Description : Kate CTags plugin + * + * Copyright (C) 2008-2011 by Kare Sars + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) version 3, or any + * later version accepted by the membership of KDE e.V. (or its + * successor approved by the membership of KDE e.V.), which shall + * act as a proxy defined in Section 6 of version 3 of the license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see . + */ + +#include "kate_ctags_plugin.h" + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +K_PLUGIN_FACTORY(KateCTagsPluginFactory, registerPlugin();) +K_EXPORT_PLUGIN(KateCTagsPluginFactory(KAboutData("katectags", "kate-ctags-plugin", + ki18n("CTags Plugin"), "0.2", + ki18n( "CTags Plugin")))) + +/******************************************************************/ +KateCTagsPlugin::KateCTagsPlugin(QObject* parent, const QList&): +Kate::Plugin ((Kate::Application*)parent), m_view(0) +{ + KGlobal::locale()->insertCatalog("kate-ctags-plugin"); +} + +/******************************************************************/ +Kate::PluginView *KateCTagsPlugin::createView(Kate::MainWindow *mainWindow) +{ + m_view = new KateCTagsView(mainWindow, KateCTagsPluginFactory::componentData()); + return m_view; +} + + +/******************************************************************/ +Kate::PluginConfigPage *KateCTagsPlugin::configPage (uint number, QWidget *parent, const char *) +{ + if (number != 0) return 0; + return new KateCTagsConfigPage(parent, this); +} + +/******************************************************************/ +QString KateCTagsPlugin::configPageName (uint number) const +{ + if (number != 0) return QString(); + return i18n("CTags"); +} + +/******************************************************************/ +QString KateCTagsPlugin::configPageFullName (uint number) const +{ + if (number != 0) return QString(); + return i18n("CTags Settings"); +} + +/******************************************************************/ +KIcon KateCTagsPlugin::configPageIcon (uint number) const +{ + if (number != 0) return KIcon(); + return KIcon("text-x-csrc"); +} + +/******************************************************************/ +void KateCTagsPlugin::readConfig() +{ +} + + + + +/******************************************************************/ +KateCTagsConfigPage::KateCTagsConfigPage( QWidget* parent, KateCTagsPlugin *plugin ) +: Kate::PluginConfigPage( parent ) +, m_plugin( plugin ) +{ + m_confUi.setupUi(this); + m_confUi.cmdEdit->setText(DEFAULT_CTAGS_CMD); + + m_confUi.addButton->setToolTip(i18n("Add a directory to index.")); + m_confUi.addButton->setIcon(KIcon("list-add")); + + m_confUi.delButton->setToolTip(i18n("Remove a directory.")); + m_confUi.delButton->setIcon(KIcon("list-remove")); + + m_confUi.updateDB->setToolTip(i18n("(Re-)generate the common CTags database.")); + m_confUi.updateDB->setIcon(KIcon("view-refresh")); + + connect(m_confUi.updateDB, SIGNAL(clicked()), this, SLOT(updateGlobalDB())); + connect(m_confUi.addButton, SIGNAL(clicked()), this, SLOT(addGlobalTagTarget())); + connect(m_confUi.delButton, SIGNAL(clicked()), this, SLOT(delGlobalTagTarget())); + + connect(&m_proc, SIGNAL(finished(int,QProcess::ExitStatus)), + this, SLOT(updateDone(int,QProcess::ExitStatus))); + + reset(); +} + +/******************************************************************/ +void KateCTagsConfigPage::apply() +{ + KConfigGroup config(KGlobal::config(), "CTags"); + config.writeEntry("GlobalCommand", m_confUi.cmdEdit->text()); + + config.writeEntry("GlobalNumTargets", m_confUi.targetList->count()); + + QString nr; + for (int i=0; icount(); i++) { + nr = QString("%1").arg(i,3); + config.writeEntry("GlobalTarget_"+nr, m_confUi.targetList->item(i)->text()); + } + config.sync(); +} + +/******************************************************************/ +void KateCTagsConfigPage::reset() +{ + KConfigGroup config(KGlobal::config(), "CTags"); + m_confUi.cmdEdit->setText(config.readEntry("GlobalCommand", DEFAULT_CTAGS_CMD)); + + int numEntries = config.readEntry("GlobalNumTargets", 0); + QString nr; + QString target; + for (int i=0; icurrentItem (); +} + + +/******************************************************************/ +bool KateCTagsConfigPage::listContains(const QString &target) +{ + for (int i=0; icount(); i++) { + if (m_confUi.targetList->item(i)->text() == target) { + return true; + } + } + return false; +} + +/******************************************************************/ +void KateCTagsConfigPage::updateGlobalDB() +{ + if (m_proc.state() != QProcess::NotRunning) { + return; + } + + QString targets; + QString target; + for (int i=0; icount(); i++) { + target = m_confUi.targetList->item(i)->text(); + if (target.endsWith('/') || target.endsWith('\\')) { + target = target.left(target.size() - 1); + } + targets += target + ' '; + } + + QString file = KStandardDirs::locateLocal("appdata", "plugins/katectags/common_db", true); + + if (targets.isEmpty()) { + QFile::remove(file); + return; + } + + QString command = QString("%1 -f %2 %3").arg(m_confUi.cmdEdit->text()).arg(file).arg(targets) ; + + m_proc.setShellCommand(command); + m_proc.setOutputChannelMode(KProcess::SeparateChannels); + m_proc.start(); + + if(!m_proc.waitForStarted(500)) { + KMessageBox::error(0, i18n("Failed to run \"%1\". exitStatus = %2", command, m_proc.exitStatus())); + return; + } + m_confUi.updateDB->setDisabled(true); + QApplication::setOverrideCursor(QCursor(Qt::BusyCursor)); +} + +/******************************************************************/ +void KateCTagsConfigPage::updateDone(int exitCode, QProcess::ExitStatus status) +{ + if (status == QProcess::CrashExit) { + KMessageBox::error(this, i18n("The CTags executable crashed.")); + } + else if (exitCode != 0) { + KMessageBox::error(this, i18n("The CTags command exited with code %1", exitCode)); + } + + m_confUi.updateDB->setDisabled(false); + QApplication::restoreOverrideCursor(); +} + + diff --git a/kate/addons/kate/kate-ctags/kate_ctags_plugin.h b/kate/addons/kate/kate-ctags/kate_ctags_plugin.h new file mode 100644 index 00000000..40d9b33e --- /dev/null +++ b/kate/addons/kate/kate-ctags/kate_ctags_plugin.h @@ -0,0 +1,87 @@ +#ifndef KATE_CTAGS_PLUGIN_H +#define KATE_CTAGS_PLUGIN_H +/* Description : Kate CTags plugin + * + * Copyright (C) 2008-2011 by Kare Sars + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) version 3, or any + * later version accepted by the membership of KDE e.V. (or its + * successor approved by the membership of KDE e.V.), which shall + * act as a proxy defined in Section 6 of version 3 of the license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see . + */ + + +#include +#include + +#include +#include +#include +#include +#include + +#include "kate_ctags_view.h" +#include "ui_CTagsGlobalConfig.h" + +//******************************************************************/ +class KateCTagsPlugin : public Kate::Plugin, public Kate::PluginConfigPageInterface +{ + Q_OBJECT + Q_INTERFACES(Kate::PluginConfigPageInterface) + + public: + explicit KateCTagsPlugin(QObject* parent = 0, const QList & = QList()); + virtual ~KateCTagsPlugin() {} + + Kate::PluginView *createView(Kate::MainWindow *mainWindow); + + // PluginConfigPageInterface + uint configPages() const { return 1; }; + Kate::PluginConfigPage *configPage (uint number = 0, QWidget *parent = 0, const char *name = 0); + QString configPageName (uint number = 0) const; + QString configPageFullName (uint number = 0) const; + KIcon configPageIcon (uint number = 0) const; + void readConfig(); + + KateCTagsView *m_view; +}; + +//******************************************************************/ +class KateCTagsConfigPage : public Kate::PluginConfigPage { + Q_OBJECT +public: + explicit KateCTagsConfigPage( QWidget* parent = 0, KateCTagsPlugin *plugin = 0 ); + ~KateCTagsConfigPage() {} + + void apply(); + void reset(); + void defaults() {} + +private Q_SLOTS: + void addGlobalTagTarget(); + void delGlobalTagTarget(); + void updateGlobalDB(); + void updateDone(int exitCode, QProcess::ExitStatus status); + +private: + + bool listContains(const QString &target); + + KProcess m_proc; + KateCTagsPlugin *m_plugin; + Ui_CTagsGlobalConfig m_confUi; +}; + +#endif + diff --git a/kate/addons/kate/kate-ctags/kate_ctags_view.cpp b/kate/addons/kate/kate-ctags/kate_ctags_view.cpp new file mode 100644 index 00000000..98af78e0 --- /dev/null +++ b/kate/addons/kate/kate-ctags/kate_ctags_view.cpp @@ -0,0 +1,648 @@ +/* Description : Kate CTags plugin + * + * Copyright (C) 2008-2011 by Kare Sars + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) version 3, or any + * later version accepted by the membership of KDE e.V. (or its + * successor approved by the membership of KDE e.V.), which shall + * act as a proxy defined in Section 6 of version 3 of the license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see . + */ + +#include "kate_ctags_view.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +/******************************************************************/ +KateCTagsView::KateCTagsView(Kate::MainWindow *mw, const KComponentData& componentData) + : Kate::PluginView (mw) + , Kate::XMLGUIClient(componentData) + , m_toolView (mw->createToolView ("kate_private_plugin_katectagsplugin", + Kate::MainWindow::Bottom, + SmallIcon("application-x-ms-dos-executable"), + i18n("CTags")) + ) + , m_proc(0) +{ + m_mWin = mw; + + KAction *back = actionCollection()->addAction("ctags_return_step"); + back->setText(i18n("Jump back one step")); + back->setShortcut(QKeySequence(Qt::ALT+Qt::Key_1) ); + connect(back, SIGNAL(triggered(bool)), this, SLOT(stepBack())); + + KAction *decl = actionCollection()->addAction("ctags_lookup_current_as_declaration"); + decl->setText(i18n("Go to Declaration")); + decl->setShortcut(QKeySequence(Qt::ALT+Qt::Key_2) ); + connect(decl, SIGNAL(triggered(bool)), this, SLOT(gotoDeclaration())); + + KAction *defin = actionCollection()->addAction("ctags_lookup_current_as_definition"); + defin->setText(i18n("Go to Definition")); + defin->setShortcut(QKeySequence(Qt::ALT+Qt::Key_3) ); + connect(defin, SIGNAL(triggered(bool)), this, SLOT(gotoDefinition())); + + KAction *lookup = actionCollection()->addAction("ctags_lookup_current"); + lookup->setText(i18n("Lookup Current Text")); + lookup->setShortcut(QKeySequence(Qt::ALT+Qt::Key_4) ); + connect(lookup, SIGNAL(triggered(bool)), this, SLOT(lookupTag())); + + // popup menu + m_menu = new KActionMenu(i18n("CTags"), this); + actionCollection()->addAction("popup_ctags", m_menu); + + m_gotoDec=m_menu->menu()->addAction(i18n("Go to Declaration: %1",QString()), this, SLOT(gotoDeclaration())); + m_gotoDef=m_menu->menu()->addAction(i18n("Go to Definition: %1",QString()), this, SLOT(gotoDefinition())); + m_lookup=m_menu->menu()->addAction(i18n("Lookup: %1",QString()), this, SLOT(lookupTag())); + + connect(m_menu->menu(), SIGNAL(aboutToShow()), this, SLOT(aboutToShow())); + + QWidget *ctagsWidget = new QWidget(m_toolView); + m_ctagsUi.setupUi(ctagsWidget); + m_ctagsUi.cmdEdit->setText(DEFAULT_CTAGS_CMD); + + m_ctagsUi.addButton->setToolTip(i18n("Add a directory to index.")); + m_ctagsUi.addButton->setIcon(KIcon("list-add")); + + m_ctagsUi.delButton->setToolTip(i18n("Remove a directory.")); + m_ctagsUi.delButton->setIcon(KIcon("list-remove")); + + m_ctagsUi.updateButton->setToolTip(i18n("(Re-)generate the session specific CTags database.")); + m_ctagsUi.updateButton->setIcon(KIcon("view-refresh")); + + m_ctagsUi.updateButton2->setToolTip(i18n("(Re-)generate the session specific CTags database.")); + m_ctagsUi.updateButton2->setIcon(KIcon("view-refresh")); + + m_ctagsUi.resetCMD->setIcon(KIcon("view-refresh")); + + m_ctagsUi.tagsFile->setToolTip(i18n("Select new or existing database file.")); + + connect(m_ctagsUi.resetCMD, SIGNAL(clicked()), this, SLOT(resetCMD())); + connect(m_ctagsUi.addButton, SIGNAL(clicked()), this, SLOT(addTagTarget())); + connect(m_ctagsUi.delButton, SIGNAL(clicked()), this, SLOT(delTagTarget())); + connect(m_ctagsUi.updateButton, SIGNAL(clicked()), this, SLOT(updateSessionDB())); + connect(m_ctagsUi.updateButton2, SIGNAL(clicked()), this, SLOT(updateSessionDB())); + connect(&m_proc, SIGNAL(finished(int,QProcess::ExitStatus)), + this, SLOT(updateDone(int,QProcess::ExitStatus))); + + connect(m_ctagsUi.inputEdit, SIGNAL(textChanged(QString)), this, SLOT(startEditTmr())); + + m_editTimer.setSingleShot(true); + connect(&m_editTimer, SIGNAL(timeout()), this, SLOT(editLookUp())); + + connect(m_ctagsUi.tagTreeWidget, SIGNAL(itemActivated(QTreeWidgetItem*,int)), + SLOT(tagHitClicked(QTreeWidgetItem*))); + + connect(mainWindow(), SIGNAL(unhandledShortcutOverride(QEvent*)), + this, SLOT(handleEsc(QEvent*))); + + m_toolView->installEventFilter(this); + + mainWindow()->guiFactory()->addClient(this); + + m_commonDB = KStandardDirs::locateLocal("appdata", "plugins/katectags/common_db", true); +} + + +/******************************************************************/ +KateCTagsView::~KateCTagsView() +{ + mainWindow()->guiFactory()->removeClient( this ); + + delete m_toolView; +} + +/******************************************************************/ +void KateCTagsView::aboutToShow() +{ + QString currWord = currentWord(); + if (currWord.isEmpty()) { + return; + } + + if (Tags::hasTag(currWord)) { + QString squeezed = KStringHandler::csqueeze(currWord, 30); + + m_gotoDec->setText(i18n("Go to Declaration: %1",squeezed)); + m_gotoDef->setText(i18n("Go to Definition: %1",squeezed)); + m_lookup->setText(i18n("Lookup: %1",squeezed)); + } +} + + +/******************************************************************/ +void KateCTagsView::readSessionConfig (KConfigBase* config, const QString& groupPrefix) +{ + KConfigGroup cg(config, groupPrefix + ":ctags-plugin"); + + m_ctagsUi.cmdEdit->setText(cg.readEntry("TagsGenCMD", DEFAULT_CTAGS_CMD)); + + int numEntries = cg.readEntry("SessionNumTargets", 0); + QString nr; + QString target; + for (int i=0; isetText(sessionDB); + +} + +/******************************************************************/ +void KateCTagsView::writeSessionConfig (KConfigBase* config, const QString& groupPrefix) +{ + KConfigGroup cg(config, groupPrefix + ":ctags-plugin"); + + cg.writeEntry("TagsGenCMD", m_ctagsUi.cmdEdit->text()); + cg.writeEntry("SessionNumTargets", m_ctagsUi.targetList->count()); + + QString nr; + for (int i=0; icount(); i++) { + nr = QString("%1").arg(i,3); + cg.writeEntry("SessionTarget_"+nr, m_ctagsUi.targetList->item(i)->text()); + } + + cg.writeEntry("SessionDatabase", m_ctagsUi.tagsFile->text()); + + cg.sync(); +} + + +/******************************************************************/ +void KateCTagsView::stepBack() +{ + if (m_jumpStack.isEmpty()) { + return; + } + + TagJump back; + back = m_jumpStack.pop(); + + m_mWin->openUrl(back.url); + m_mWin->activeView()->setCursorPosition(back.cursor); + m_mWin->activeView()->setFocus(); + +} + + +/******************************************************************/ +void KateCTagsView::lookupTag( ) +{ + QString currWord = currentWord(); + if (currWord.isEmpty()) { + return; + } + + setNewLookupText(currWord); + Tags::TagList list = Tags::getExactMatches(m_ctagsUi.tagsFile->text(), currWord); + if (list.size() == 0) list = Tags::getExactMatches(m_commonDB, currWord); + displayHits(list); + + // activate the hits tab + m_ctagsUi.tabWidget->setCurrentIndex(0); + m_mWin->showToolView(m_toolView); +} + +/******************************************************************/ +void KateCTagsView::editLookUp() +{ + Tags::TagList list = Tags::getPartialMatches(m_ctagsUi.tagsFile->text(), m_ctagsUi.inputEdit->text()); + if (list.size() == 0) list = Tags::getPartialMatches(m_commonDB, m_ctagsUi.inputEdit->text()); + displayHits(list); +} + +/******************************************************************/ +void KateCTagsView::gotoDefinition( ) +{ + QString currWord = currentWord(); + if (currWord.isEmpty()) { + return; + } + + QStringList types; + types << "S" << "d" << "f" << "t" << "v"; + gotoTagForTypes(currWord, types); +} + +/******************************************************************/ +void KateCTagsView::gotoDeclaration( ) +{ + QString currWord = currentWord(); + if (currWord.isEmpty()) { + return; + } + + QStringList types; + types << "L" << "c" << "e" << "g" << "m" << "n" << "p" << "s" << "u" << "x"; + gotoTagForTypes(currWord, types); +} + +/******************************************************************/ +void KateCTagsView::gotoTagForTypes(const QString &word, const QStringList &types) +{ + Tags::TagList list = Tags::getMatches(m_ctagsUi.tagsFile->text(), word, false, types); + if (list.size() == 0) list = Tags::getMatches(m_commonDB, word, false, types); + + //kDebug() << "found" << list.count() << word << types; + setNewLookupText(word); + + if ( list.count() < 1) { + m_ctagsUi.tagTreeWidget->clear(); + new QTreeWidgetItem(m_ctagsUi.tagTreeWidget, QStringList(i18n("No hits found"))); + m_ctagsUi.tabWidget->setCurrentIndex(0); + m_mWin->showToolView(m_toolView); + return; + } + + displayHits(list); + + if (list.count() == 1) { + Tags::TagEntry tag = list.first(); + jumpToTag(tag.file, tag.pattern, word); + } + else { + Tags::TagEntry tag = list.first(); + jumpToTag(tag.file, tag.pattern, word); + m_ctagsUi.tabWidget->setCurrentIndex(0); + m_mWin->showToolView(m_toolView); + } +} + +/******************************************************************/ +void KateCTagsView::setNewLookupText(const QString &newString) +{ + m_ctagsUi.inputEdit->blockSignals( true ); + m_ctagsUi.inputEdit->setText(newString); + m_ctagsUi.inputEdit->blockSignals( false ); +} + +/******************************************************************/ +void KateCTagsView::displayHits(const Tags::TagList &list) +{ + KUrl url; + + m_ctagsUi.tagTreeWidget->clear(); + if (list.isEmpty()) { + new QTreeWidgetItem(m_ctagsUi.tagTreeWidget, QStringList(i18n("No hits found"))); + return; + } + m_ctagsUi.tagTreeWidget->setSortingEnabled(false); + + Tags::TagList::ConstIterator it = list.begin(); + while(it != list.end()) { + // search for the file + QFileInfo file((*it).file); + if(file.isAbsolute()) { + // we have absolute path + url.setPath((*it).file); + } + else { + // not absolute + QString name = (*it).file; + name = name.remove(".\\"); + name = name.replace("\\", "/"); + QFileInfo abs(QFileInfo(Tags::getTagsFile()).path()+ '/' + name); + url.setPath(abs.absoluteFilePath()); + } + + QTreeWidgetItem* item = new QTreeWidgetItem(m_ctagsUi.tagTreeWidget); + item->setText(0, (*it).tag); + item->setText(1, (*it).type); + item->setText(2, url.toLocalFile()); + + item->setData(0, Qt::UserRole, (*it).pattern); + + QString pattern = (*it).pattern; + pattern.replace( "\\/", "/" ); + pattern = pattern.mid(2, pattern.length() - 4); + pattern = pattern.trimmed(); + + item->setData(0, Qt::ToolTipRole, pattern); + item->setData(1, Qt::ToolTipRole, pattern); + item->setData(2, Qt::ToolTipRole, pattern); + + ++it; + } + m_ctagsUi.tagTreeWidget->setSortingEnabled(true); +} + +/******************************************************************/ +void KateCTagsView::tagHitClicked(QTreeWidgetItem *item) +{ + // get stuff + const QString file = item->data(2, Qt::DisplayRole).toString(); + const QString pattern = item->data(0, Qt::UserRole).toString(); + const QString word = item->data(0, Qt::DisplayRole).toString(); + + jumpToTag(file, pattern, word); +} + +/******************************************************************/ +QString KateCTagsView::currentWord( ) +{ + KTextEditor::View *kv = mainWindow()->activeView(); + if (!kv) { + kDebug() << "no KTextEditor::View" << endl; + return QString(); + } + + if (kv->selection()) { + return kv->selectionText(); + } + + if (!kv->cursorPosition().isValid()) { + kDebug() << "cursor not valid!" << endl; + return QString(); + } + + int line = kv->cursorPosition().line(); + int col = kv->cursorPosition().column(); + bool includeColon = m_ctagsUi.cmdEdit->text().contains("--extra=+q"); + + QString linestr = kv->document()->line(line); + + int startPos = qMax(qMin(col, linestr.length()-1), 0); + int endPos = startPos; + while (startPos >= 0 && (linestr[startPos].isLetterOrNumber() || + (linestr[startPos] == ':' && includeColon) || + linestr[startPos] == '_' || + linestr[startPos] == '~')) + { + startPos--; + } + while (endPos < (int)linestr.length() && (linestr[endPos].isLetterOrNumber() || + (linestr[endPos] == ':' && includeColon) || + linestr[endPos] == '_')) { + endPos++; + } + if (startPos == endPos) { + kDebug() << "no word found!" << endl; + return QString(); + } + + linestr = linestr.mid(startPos+1, endPos-startPos-1); + + while (linestr.endsWith(':')) { + linestr.remove(linestr.size()-1, 1); + } + + while (linestr.startsWith(':')) { + linestr.remove(0, 1); + } + + //kDebug() << linestr; + return linestr; +} + +/******************************************************************/ +void KateCTagsView::jumpToTag(const QString &file, const QString &pattern, const QString &word) +{ + KUrl url; + + if (pattern.isEmpty()) return; + + // generate a regexp from the pattern + // ctags interestingly escapes "/", but apparently nothing else. lets revert that + QString unescaped = pattern; + unescaped.replace( "\\/", "/" ); + + // most of the time, the ctags pattern has the form /^foo$/ + // but this isn't true for some macro definitions + // where the form is only /^foo/ + // I have no idea if this is a ctags bug or not, but we have to deal with it + + QString reduced; + QString escaped; + QString re_string; + + if (unescaped.endsWith("$/")) { + reduced = unescaped.mid(2, unescaped.length() - 4); + escaped = QRegExp::escape(reduced); + re_string = QString('^' + escaped + '$'); + } + else { + reduced = unescaped.mid( 2, unescaped.length() -3 ); + escaped = QRegExp::escape(reduced); + re_string = QString('^' + escaped); + } + + QRegExp re(re_string); + + // search for the file + QFileInfo find(file); + if(find.isAbsolute()) { + // we have absolute path + url.setPath(file); + } + else { + // not absolute + QString name = file; + name = name.remove(".\\"); + name = name.replace("\\", "/"); + QFileInfo abs(QFileInfo(Tags::getTagsFile()).path()+ '/' + name); + url.setPath(abs.absoluteFilePath()); + } + + //kDebug() << url << pattern; + + // save current location + TagJump from; + from.url = m_mWin->activeView()->document()->url(); + from.cursor = m_mWin->activeView()->cursorPosition(); + m_jumpStack.push(from); + + // open/activate the file + m_mWin->openUrl(url); + + // any view active? + if (!m_mWin->activeView()) { + return; + } + + // look for the line + QString linestr; + int line; + for (line =0; line < m_mWin->activeView()->document()->lines(); line++) { + linestr = m_mWin->activeView()->document()->line(line); + if (linestr.indexOf(re) > -1) break; + } + + // activate the line + if (line != m_mWin->activeView()->document()->lines()) { + // line found now look for the column + int column = linestr.indexOf(word) + (word.length()/2); + m_mWin->activeView()->setCursorPosition(KTextEditor::Cursor(line, column)); + } + m_mWin->activeView()->setFocus(); + +} + + +/******************************************************************/ +void KateCTagsView::startEditTmr() +{ + if (m_ctagsUi.inputEdit->text().size() > 3) { + m_editTimer.start(500); + } +} + + +/******************************************************************/ +void KateCTagsView::updateSessionDB() +{ + if (m_proc.state() != QProcess::NotRunning) { + return; + } + + QString targets; + QString target; + for (int i=0; icount(); i++) { + target = m_ctagsUi.targetList->item(i)->text(); + if (target.endsWith('/') || target.endsWith('\\')) { + target = target.left(target.size() - 1); + } + targets += target + ' '; + } + + if (m_ctagsUi.tagsFile->text().isEmpty()) { + // FIXME we need a way to get the session name + QString sessionDB = KStandardDirs::locateLocal("appdata", "plugins/katectags/session_db_", true); + sessionDB += QDateTime::currentDateTime().toString("yyyyMMdd_hhmmss"); + m_ctagsUi.tagsFile->setText(sessionDB); + } + + if (targets.isEmpty()) { + QFile::remove(m_ctagsUi.tagsFile->text()); + return; + } + + QString command = QString("%1 -f %2 %3").arg(m_ctagsUi.cmdEdit->text()).arg(m_ctagsUi.tagsFile->text()).arg(targets); + + m_proc.setShellCommand(command); + m_proc.setOutputChannelMode(KProcess::SeparateChannels); + m_proc.start(); + + if(!m_proc.waitForStarted(500)) { + KMessageBox::error(0, i18n("Failed to run \"%1\". exitStatus = %2", command, m_proc.exitStatus())); + return; + } + QApplication::setOverrideCursor(QCursor(Qt::BusyCursor)); + m_ctagsUi.updateButton->setDisabled(true); + m_ctagsUi.updateButton2->setDisabled(true); +} + + +/******************************************************************/ +void KateCTagsView::updateDone(int exitCode, QProcess::ExitStatus status) +{ + if (status == QProcess::CrashExit) { + KMessageBox::error(m_toolView, i18n("The CTags executable crashed.")); + } else if (exitCode != 0) { + KMessageBox::error(m_toolView, i18n("The CTags program exited with code %1", exitCode)); + } + + m_ctagsUi.updateButton->setDisabled(false); + m_ctagsUi.updateButton2->setDisabled(false); + QApplication::restoreOverrideCursor(); +} + +/******************************************************************/ +void KateCTagsView::addTagTarget() +{ + KUrl defDir = m_mWin->activeView()->document()->url().directory(); + + KFileDialog dialog(defDir, QString(), 0, 0); + dialog.setMode(KFile::Directory | KFile::Files | KFile::ExistingOnly | KFile::LocalOnly); + + // i18n("CTags Database Location")); + if (dialog.exec() != QDialog::Accepted) { + return; + } + + QStringList urls = dialog.selectedFiles(); + + for (int i=0; icurrentItem (); +} + +/******************************************************************/ +bool KateCTagsView::listContains(const QString &target) +{ + for (int i=0; icount(); i++) { + if (m_ctagsUi.targetList->item(i)->text() == target) { + return true; + } + } + return false; +} + +/******************************************************************/ +bool KateCTagsView::eventFilter(QObject *obj, QEvent *event) +{ + if (event->type() == QEvent::KeyPress) { + QKeyEvent *ke = static_cast(event); + if ((obj == m_toolView) && (ke->key() == Qt::Key_Escape)) { + mainWindow()->hideToolView(m_toolView); + event->accept(); + return true; + } + } + return QObject::eventFilter(obj, event); +} + +/******************************************************************/ +void KateCTagsView::resetCMD() +{ + m_ctagsUi.cmdEdit->setText(DEFAULT_CTAGS_CMD); +} + +/******************************************************************/ +void KateCTagsView::handleEsc(QEvent *e) +{ + if (!mainWindow()) return; + + QKeyEvent *k = static_cast(e); + if (k->key() == Qt::Key_Escape && k->modifiers() == Qt::NoModifier) { + if (m_toolView->isVisible()) { + mainWindow()->hideToolView(m_toolView); + } + } +} + diff --git a/kate/addons/kate/kate-ctags/kate_ctags_view.h b/kate/addons/kate/kate-ctags/kate_ctags_view.h new file mode 100644 index 00000000..7530ef54 --- /dev/null +++ b/kate/addons/kate/kate-ctags/kate_ctags_view.h @@ -0,0 +1,118 @@ +#ifndef KATE_CTAGS_VIEW_H +#define KATE_CTAGS_VIEW_H +/* Description : Kate CTags plugin + * + * Copyright (C) 2008-2011 by Kare Sars + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) version 3, or any + * later version accepted by the membership of KDE e.V. (or its + * successor approved by the membership of KDE e.V.), which shall + * act as a proxy defined in Section 6 of version 3 of the license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see . + */ + + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include "tags.h" + +#include "ui_kate_ctags.h" + +#define DEFAULT_CTAGS_CMD "ctags -R --c++-types=+px --extra=+q --excmd=pattern --exclude=Makefile --exclude=." + +typedef struct +{ + KUrl url; + KTextEditor::Cursor cursor; +} TagJump; + +/******************************************************************/ +class KateCTagsView : public Kate::PluginView, public Kate::XMLGUIClient +{ + Q_OBJECT + +public: + KateCTagsView(Kate::MainWindow *mw, const KComponentData& componentData); + ~KateCTagsView(); + + // overwritten: read and write session config + void readSessionConfig (KConfigBase* config, const QString& groupPrefix); + void writeSessionConfig (KConfigBase* config, const QString& groupPrefix); + +public Q_SLOTS: + void gotoDefinition(); + void gotoDeclaration(); + void lookupTag(); + void stepBack(); + void editLookUp(); + void aboutToShow(); + void tagHitClicked(QTreeWidgetItem *); + void startEditTmr(); + + void addTagTarget(); + void delTagTarget(); + + void updateSessionDB(); + void updateDone(int exitCode, QProcess::ExitStatus status); + +protected: + bool eventFilter(QObject *obj, QEvent *ev); + +private Q_SLOTS: + void resetCMD(); + void handleEsc(QEvent *e); + +private: + bool listContains(const QString &target); + + QString currentWord(); + + void setNewLookupText(const QString &newText); + void displayHits(const Tags::TagList &list); + + void gotoTagForTypes(const QString &tag, QStringList const &types); + void jumpToTag(const QString &file, const QString &pattern, const QString &word); + + + Kate::MainWindow *m_mWin; + QWidget *m_toolView; + Ui::kateCtags m_ctagsUi; + + QPointer m_menu; + QAction *m_gotoDef; + QAction *m_gotoDec; + QAction *m_lookup; + + KProcess m_proc; + QString m_commonDB; + + QTimer m_editTimer; + QStack m_jumpStack; +}; + + +#endif + diff --git a/kate/addons/kate/kate-ctags/katectagsplugin.desktop b/kate/addons/kate/kate-ctags/katectagsplugin.desktop new file mode 100644 index 00000000..43ee99a5 --- /dev/null +++ b/kate/addons/kate/kate-ctags/katectagsplugin.desktop @@ -0,0 +1,113 @@ +[Desktop Entry] +Type=Service +ServiceTypes=Kate/Plugin +X-KDE-Library=katectagsplugin +X-Kate-Version=2.9 +Name=CTags +Name[bg]=CTags +Name[bs]=C‑tags +Name[ca]=CTags +Name[ca@valencia]=CTags +Name[cs]=CTags +Name[da]=CTags +Name[de]=CTags +Name[el]=CTags +Name[en_GB]=CTags +Name[es]=CTags +Name[et]=CTags +Name[eu]=CTags +Name[fi]=CTags +Name[fr]=CTags +Name[ga]=CTags +Name[gl]=CTags +Name[he]=CTags +Name[hu]=CTags +Name[ia]=CTags +Name[is]=CTags +Name[it]=CTags +Name[kk]=CTags +Name[km]=CTags +Name[ko]=CTags +Name[lt]=CTags +Name[lv]=CTags +Name[mr]=CTags +Name[nb]=CTags +Name[nds]=CTags +Name[nl]=CTags +Name[nn]=CTags +Name[pa]=CTags +Name[pl]=CTags +Name[pt]=CTags +Name[pt_BR]=CTags +Name[ro]=CTags +Name[ru]=CTags +Name[si]=CTags +Name[sk]=CTags +Name[sl]=CTags +Name[sr]=Ц‑тагс +Name[sr@ijekavian]=Ц‑тагс +Name[sr@ijekavianlatin]=CTags +Name[sr@latin]=CTags +Name[sv]=CTags +Name[tg]=CTags +Name[tr]=CTags +Name[ug]=CTags +Name[uk]=CTags +Name[wa]=CTags +Name[x-test]=xxCTagsxx +Name[zh_CN]=CTags +Name[zh_TW]=CTags +Comment=Look up definitions/declarations with CTags +Comment[ast]=Guetar definiciones/declaraciones con CTags +Comment[bg]=Проверка на дефиниции и декларации с CTag +Comment[bs]=Tražite definicije i deklaracije C‑tagsom +Comment[ca]=Cerca definicions/declaracions amb CTags +Comment[ca@valencia]=Cerca definicions/declaracions amb CTags +Comment[cs]=Vyhledávání definic/deklarací pomocí CTags +Comment[da]=Slå definitioner/erklæringer op med CTags +Comment[de]=Definition/Deklaration mit CTags nachschauen +Comment[el]=Αναζήτηση ορισμών/δηλώσεων με το CTags +Comment[en_GB]=Look up definitions/declarations with CTags +Comment[es]=Buscar definiciones/declaraciones con CTags +Comment[et]=Definitsiooni/deklaratsiooni otsimine CTagsiga +Comment[eu]=Bilatu CTags duten definizioak/deklarazioak +Comment[fi]=Hyppää määrittelyihin/esittelyihin käyttäen CTagsia +Comment[fr]=Cherche des définitions / déclarations avec CTags +Comment[ga]=Cuardaigh sainmhínithe/fógraí le CTags +Comment[gl]=Busca de definicións/declaracións con CTags +Comment[he]=חפש הגדרות של משתנים בעזרת CTags +Comment[hu]=Definíciók/deklarációk kikeresése CTags segítségével +Comment[ia]=Cerca definitiones/declarationes con CTags +Comment[it]=Cerca definizioni e dichiarazioni con CTags +Comment[ja]=CTags を使って定義/宣言を検索します +Comment[kk]=CTags көмегімен анықтамалар/жарияламаларды қарастыру +Comment[km]=រក​មើល​និយមន័យ/កា​រ​ប្រកាស​ជា​មួយ CTags +Comment[ko]=CTags를 사용하여 선언/정의 찾기 +Comment[lt]=Peržiūrėti apibrėžimus naudojant CTags +Comment[lv]=Uzmeklē definīcijas/deklarācijas ar CTags +Comment[mr]=CTags ने व्याख्या/डिक्लेरेशन्स बघतो +Comment[nb]=Slå opp definisjoner og deklarasjoner med CTags +Comment[nds]=Definitschonen/Deklaratschonen mit CTags nakieken +Comment[nl]=Defenities/declaraties opzoeken met CTags +Comment[nn]=Slå opp definisjonar og deklarasjonar med CTags +Comment[pl]=Wyszukiwanie definicji/deklaracji w CTags +Comment[pt]=Procurar por definições/declarações com o CTags +Comment[pt_BR]=Procura definições/declarações com o CTags +Comment[ro]=Caută definiții/declarații cu CTags +Comment[ru]=Поиск определений и объявлений с помощью индекса CTags +Comment[si]=CTag සමඟ යෙදුම්/හැඳින්වීම බලන්න +Comment[sk]=Vyhľadať definície/deklarácie s CTags +Comment[sl]=Poiščite definicije in deklaracije +Comment[sr]=Тражите дефиниције и декларације Ц‑тагсом +Comment[sr@ijekavian]=Тражите дефиниције и декларације Ц‑тагсом +Comment[sr@ijekavianlatin]=Tražite definicije i deklaracije CTagsom +Comment[sr@latin]=Tražite definicije i deklaracije CTagsom +Comment[sv]=Slå upp definitioner och deklarationer med Ctags +Comment[tg]=Намоиши маъно/эъломияҳо тавассути CTags +Comment[tr]=CTags ile bildirimlerde ve tanımlamalarda ara +Comment[ug]=CTags ئىشلىتىپ كودتىكى ئېنىقلىما/بايانات ئىزدەيدۇ +Comment[uk]=Пошук визначень і оголошень за допомогою CTags +Comment[x-test]=xxLook up definitions/declarations with CTagsxx +Comment[zh_CN]=使用 CTags 查阅代码中的定义/声明 +Comment[zh_TW]=使用 CTags 尋找定義與宣告 + diff --git a/kate/addons/kate/kate-ctags/readtags.c b/kate/addons/kate/kate-ctags/readtags.c new file mode 100644 index 00000000..c0123afb --- /dev/null +++ b/kate/addons/kate/kate-ctags/readtags.c @@ -0,0 +1,961 @@ +/* +* +* Copyright (c) 1996-2003, Darren Hiebert +* +* This source code is released into the public domain. +* +* This module contains functions for reading tag files. +* +*/ + +/* +* INCLUDE FILES +*/ +#include +#include +#include +#include +#include +#include /* to declare off_t */ + +#include "readtags.h" + +/* +* MACROS +*/ +#define TAB '\t' + + +/* +* DATA DECLARATIONS +*/ +typedef struct { + size_t size; + char *buffer; +} vstring; + +/* Information about current tag file */ +struct sTagFile { + /* has the file been opened and this structure initialized? */ + short initialized; + /* format of tag file */ + short format; + /* how is the tag file sorted? */ + sortType sortMethod; + /* pointer to file structure */ + FILE* fp; + /* file position of first character of `line' */ + off_t pos; + /* size of tag file in seekable positions */ + off_t size; + /* last line read */ + vstring line; + /* name of tag in last line read */ + vstring name; + /* defines tag search state */ + struct { + /* file position of last match for tag */ + off_t pos; + /* name of tag last searched for */ + const char *name; + /* length of name for partial matches */ + size_t nameLength; + /* peforming partial match */ + short partial; + /* ignoring case */ + short ignorecase; + } search; + /* miscellaneous extension fields */ + struct { + /* number of entries in `list' */ + unsigned short max; + /* list of key value pairs */ + tagExtensionField *list; + } fields; + /* buffers to be freed at close */ + struct { + /* name of program author */ + char *author; + /* name of program */ + char *name; + /* URL of distribution */ + char *url; + /* program version */ + char *version; + } program; +}; + +/* +* DATA DEFINITIONS +*/ +const char *const EmptyString = ""; +const char *const PseudoTagPrefix = "!_"; + +/* +* FUNCTION DEFINITIONS +*/ + +/* + * Compare two strings, ignoring case. + * Return 0 for match, < 0 for smaller, > 0 for bigger + * Make sure case is folded to uppercase in comparison (like for 'sort -f') + * This makes a difference when one of the chars lies between upper and lower + * ie. one of the chars [ \ ] ^ _ ` for ascii. (The '_' in particular !) + */ +static int struppercmp (const char *s1, const char *s2) +{ + int result; + do + { + result = toupper ((int) *s1) - toupper ((int) *s2); + } while (result == 0 && *s1++ != '\0' && *s2++ != '\0'); + return result; +} + +static int strnuppercmp (const char *s1, const char *s2, size_t n) +{ + int result; + do + { + result = toupper ((int) *s1) - toupper ((int) *s2); + } while (result == 0 && --n > 0 && *s1++ != '\0' && *s2++ != '\0'); + return result; +} + +static int growString (vstring *s) +{ + int result = 0; + size_t newLength; + char *newLine; + if (s->size == 0) + { + newLength = 128; + newLine = (char*) malloc (newLength); + *newLine = '\0'; + } + else + { + newLength = 2 * s->size; + newLine = (char*) realloc (s->buffer, newLength); + } + if (newLine == NULL) + perror ("string too large"); + else + { + s->buffer = newLine; + s->size = newLength; + result = 1; + } + return result; +} + +/* Copy name of tag out of tag line */ +static void copyName (tagFile *const file) +{ + size_t length; + const char *end = strchr (file->line.buffer, '\t'); + if (end == NULL) + { + end = strchr (file->line.buffer, '\n'); + if (end == NULL) + end = strchr (file->line.buffer, '\r'); + } + if (end != NULL) + length = end - file->line.buffer; + else + length = strlen (file->line.buffer); + while (length >= file->name.size) + growString (&file->name); + strncpy (file->name.buffer, file->line.buffer, length); + file->name.buffer [length] = '\0'; +} + +static int readTagLineRaw (tagFile *const file) +{ + int result = 1; + int reReadLine; + + /* If reading the line places any character other than a null or a + * newline at the last character position in the buffer (one less than + * the buffer size), then we must resize the buffer and reattempt to read + * the line. + */ + do + { + char *const pLastChar = file->line.buffer + file->line.size - 2; + char *line; + + file->pos = ftell (file->fp); + reReadLine = 0; + *pLastChar = '\0'; + line = fgets (file->line.buffer, (int) file->line.size, file->fp); + if (line == NULL) + { + /* read error */ + if (! feof (file->fp)) + perror ("readTagLine"); + result = 0; + } + else if (*pLastChar != '\0' && + *pLastChar != '\n' && *pLastChar != '\r') + { + /* buffer overflow */ + growString (&file->line); + fseek (file->fp, file->pos, SEEK_SET); + reReadLine = 1; + } + else + { + size_t i = strlen (file->line.buffer); + while (i > 0 && + (file->line.buffer [i - 1] == '\n' || file->line.buffer [i - 1] == '\r')) + { + file->line.buffer [i - 1] = '\0'; + --i; + } + } + } while (reReadLine && result); + if (result) + copyName (file); + return result; +} + +static int readTagLine (tagFile *const file) +{ + int result; + do + { + result = readTagLineRaw (file); + } while (result && *file->name.buffer == '\0'); + return result; +} + +static tagResult growFields (tagFile *const file) +{ + tagResult result = TagFailure; + unsigned short newCount = 2 * file->fields.max; + tagExtensionField *newFields = (tagExtensionField*) + realloc (file->fields.list, newCount * sizeof (tagExtensionField)); + if (newFields == NULL) + perror ("too many extension fields"); + else + { + file->fields.list = newFields; + file->fields.max = newCount; + result = TagSuccess; + } + return result; +} + +static void parseExtensionFields (tagFile *const file, tagEntry *const entry, + char *const string) +{ + char *p = string; + while (p != NULL && *p != '\0') + { + while (*p == TAB) + *p++ = '\0'; + if (*p != '\0') + { + char *colon; + char *field = p; + p = strchr (p, TAB); + if (p != NULL) + *p++ = '\0'; + colon = strchr (field, ':'); + if (colon == NULL) + entry->kind = field; + else + { + const char *key = field; + const char *value = colon + 1; + *colon = '\0'; + if (strcmp (key, "kind") == 0) + entry->kind = value; + else if (strcmp (key, "file") == 0) + entry->fileScope = 1; + else if (strcmp (key, "line") == 0) + entry->address.lineNumber = atol (value); + else + { + if (entry->fields.count == file->fields.max) + growFields (file); + file->fields.list [entry->fields.count].key = key; + file->fields.list [entry->fields.count].value = value; + ++entry->fields.count; + } + } + } + } +} + +static void parseTagLine (tagFile *file, tagEntry *const entry) +{ + int i; + char *p = file->line.buffer; + char *tab = strchr (p, TAB); + int fieldsPresent = 0; + + entry->fields.list = NULL; + entry->fields.count = 0; + entry->kind = NULL; + entry->fileScope = 0; + + entry->name = p; + if (tab != NULL) + { + *tab = '\0'; + p = tab + 1; + entry->file = p; + tab = strchr (p, TAB); + if (tab != NULL) + { + *tab = '\0'; + p = tab + 1; + if (*p == '/' || *p == '?') + { + /* parse pattern */ + int delimiter = *(unsigned char*) p; + entry->address.lineNumber = 0; + entry->address.pattern = p; + do + { + p = strchr (p + 1, delimiter); + } while (p != NULL && *(p - 1) == '\\'); + if (p == NULL) + { + /* invalid pattern */ + } + else + ++p; + } + else if (isdigit ((int) *(unsigned char*) p)) + { + /* parse line number */ + entry->address.pattern = p; + entry->address.lineNumber = atol (p); + while (isdigit ((int) *(unsigned char*) p)) + ++p; + } + else + { + /* invalid pattern */ + } + if ( p != NULL ) + { + fieldsPresent = (strncmp (p, ";\"", 2) == 0); + *p = '\0'; + if (fieldsPresent) + parseExtensionFields (file, entry, p + 2); + } + } + } + if (entry->fields.count > 0) + entry->fields.list = file->fields.list; + for (i = entry->fields.count ; i < file->fields.max ; ++i) + { + file->fields.list [i].key = NULL; + file->fields.list [i].value = NULL; + } +} + +static char *duplicate (const char *str) +{ + char *result = NULL; + if (str != NULL) + { + result = (char*) malloc (strlen (str) + 1); + if (result == NULL) + perror ("malloc"); + else + strcpy (result, str); + } + return result; +} + +static void readPseudoTags (tagFile *const file, tagFileInfo *const info) +{ + fpos_t startOfLine; + const size_t prefixLength = strlen (PseudoTagPrefix); + if (info != NULL) + { + info->file.format = 1; + info->file.sort = TAG_UNSORTED; + info->program.author = NULL; + info->program.name = NULL; + info->program.url = NULL; + info->program.version = NULL; + } + while (1) + { + fgetpos (file->fp, &startOfLine); + if (! readTagLine (file)) + break; + if (strncmp (file->line.buffer, PseudoTagPrefix, prefixLength) != 0) + break; + else + { + tagEntry entry; + const char *key, *value; + parseTagLine (file, &entry); + key = entry.name + prefixLength; + value = entry.file; + if (strcmp (key, "TAG_FILE_SORTED") == 0) + file->sortMethod = (sortType) atoi (value); + else if (strcmp (key, "TAG_FILE_FORMAT") == 0) + file->format = atoi (value); + else if (strcmp (key, "TAG_PROGRAM_AUTHOR") == 0) + file->program.author = duplicate (value); + else if (strcmp (key, "TAG_PROGRAM_NAME") == 0) + file->program.name = duplicate (value); + else if (strcmp (key, "TAG_PROGRAM_URL") == 0) + file->program.url = duplicate (value); + else if (strcmp (key, "TAG_PROGRAM_VERSION") == 0) + file->program.version = duplicate (value); + if (info != NULL) + { + info->file.format = file->format; + info->file.sort = file->sortMethod; + info->program.author = file->program.author; + info->program.name = file->program.name; + info->program.url = file->program.url; + info->program.version = file->program.version; + } + } + } + fsetpos (file->fp, &startOfLine); +} + +static void gotoFirstLogicalTag (tagFile *const file) +{ + fpos_t startOfLine; + const size_t prefixLength = strlen (PseudoTagPrefix); + rewind (file->fp); + while (1) + { + fgetpos (file->fp, &startOfLine); + if (! readTagLine (file)) + break; + if (strncmp (file->line.buffer, PseudoTagPrefix, prefixLength) != 0) + break; + } + fsetpos (file->fp, &startOfLine); +} + +static tagFile *initialize (const char *const filePath, tagFileInfo *const info) +{ + tagFile *result = (tagFile*) malloc (sizeof (tagFile)); + if (result != NULL) + { + memset (result, 0, sizeof (tagFile)); + growString (&result->line); + growString (&result->name); + result->fields.max = 20; + result->fields.list = (tagExtensionField*) malloc ( + result->fields.max * sizeof (tagExtensionField)); + result->fp = fopen (filePath, "r"); + if (result->fp == NULL) + { + free (result); + result = NULL; + info->status.error_number = errno; + } + else + { + fseek (result->fp, 0, SEEK_END); + result->size = ftell (result->fp); + rewind (result->fp); + readPseudoTags (result, info); + info->status.opened = 1; + result->initialized = 1; + } + } + return result; +} + +static void terminate (tagFile *const file) +{ + fclose (file->fp); + + free (file->line.buffer); + free (file->name.buffer); + free (file->fields.list); + + if (file->program.author != NULL) + free (file->program.author); + if (file->program.name != NULL) + free (file->program.name); + if (file->program.url != NULL) + free (file->program.url); + if (file->program.version != NULL) + free (file->program.version); + + memset (file, 0, sizeof (tagFile)); + + free (file); +} + +static tagResult readNext (tagFile *const file, tagEntry *const entry) +{ + tagResult result = TagFailure; + if (file == NULL || ! file->initialized) + result = TagFailure; + else if (! readTagLine (file)) + result = TagFailure; + else + { + if (entry != NULL) + parseTagLine (file, entry); + result = TagSuccess; + } + return result; +} + +static const char *readFieldValue ( + const tagEntry *const entry, const char *const key) +{ + const char *result = NULL; + int i; + if (strcmp (key, "kind") == 0) + result = entry->kind; + else if (strcmp (key, "file") == 0) + result = EmptyString; + else for (i = 0 ; i < entry->fields.count && result == NULL ; ++i) + if (strcmp (entry->fields.list [i].key, key) == 0) + result = entry->fields.list [i].value; + return result; +} + +static int readTagLineSeek (tagFile *const file, const off_t pos) +{ + int result = 0; + if (fseek (file->fp, pos, SEEK_SET) == 0) + { + result = readTagLine (file); /* read probable partial line */ + if (pos > 0 && result) + result = readTagLine (file); /* read complete line */ + } + return result; +} + +static int nameComparison (tagFile *const file) +{ + int result; + if (file->search.ignorecase) + { + if (file->search.partial) + result = strnuppercmp (file->search.name, file->name.buffer, + file->search.nameLength); + else + result = struppercmp (file->search.name, file->name.buffer); + } + else + { + if (file->search.partial) + result = strncmp (file->search.name, file->name.buffer, + file->search.nameLength); + else + result = strcmp (file->search.name, file->name.buffer); + } + return result; +} + +static void findFirstNonMatchBefore (tagFile *const file) +{ +#define JUMP_BACK 512 + int more_lines; + int comp; + off_t start = file->pos; + off_t pos = start; + do + { + if (pos < (off_t) JUMP_BACK) + pos = 0; + else + pos = pos - JUMP_BACK; + more_lines = readTagLineSeek (file, pos); + comp = nameComparison (file); + } while (more_lines && comp == 0 && pos > 0 && pos < start); +} + +static tagResult findFirstMatchBefore (tagFile *const file) +{ + tagResult result = TagFailure; + int more_lines; + off_t start = file->pos; + findFirstNonMatchBefore (file); + do + { + more_lines = readTagLine (file); + if (nameComparison (file) == 0) + result = TagSuccess; + } while (more_lines && result != TagSuccess && file->pos < start); + return result; +} + +static tagResult findBinary (tagFile *const file) +{ + tagResult result = TagFailure; + off_t lower_limit = 0; + off_t upper_limit = file->size; + off_t last_pos = 0; + off_t pos = upper_limit / 2; + while (result != TagSuccess) + { + if (! readTagLineSeek (file, pos)) + { + /* in case we fell off end of file */ + result = findFirstMatchBefore (file); + break; + } + else if (pos == last_pos) + { + /* prevent infinite loop if we backed up to beginning of file */ + break; + } + else + { + const int comp = nameComparison (file); + last_pos = pos; + if (comp < 0) + { + upper_limit = pos; + pos = lower_limit + ((upper_limit - lower_limit) / 2); + } + else if (comp > 0) + { + lower_limit = pos; + pos = lower_limit + ((upper_limit - lower_limit) / 2); + } + else if (pos == 0) + result = TagSuccess; + else + result = findFirstMatchBefore (file); + } + } + return result; +} + +static tagResult findSequential (tagFile *const file) +{ + tagResult result = TagFailure; + if (file->initialized) + { + while (result == TagFailure && readTagLine (file)) + { + if (nameComparison (file) == 0) + result = TagSuccess; + } + } + return result; +} + +static tagResult find (tagFile *const file, tagEntry *const entry, + const char *const name, const int options) +{ + tagResult result = TagFailure; + file->search.name = name; + file->search.nameLength = strlen (name); + file->search.partial = (options & TAG_PARTIALMATCH) != 0; + file->search.ignorecase = (options & TAG_IGNORECASE) != 0; + fseek (file->fp, 0, SEEK_END); + file->size = ftell (file->fp); + rewind (file->fp); + if ((file->sortMethod == TAG_SORTED && !file->search.ignorecase) || + (file->sortMethod == TAG_FOLDSORTED && file->search.ignorecase)) + { +#ifdef DEBUG + printf ("\n"); +#endif + result = findBinary (file); + } + else + { +#ifdef DEBUG + printf ("\n"); +#endif + result = findSequential (file); + } + + if (result != TagSuccess) + file->search.pos = file->size; + else + { + file->search.pos = file->pos; + if (entry != NULL) + parseTagLine (file, entry); + } + return result; +} + +static tagResult findNext (tagFile *const file, tagEntry *const entry) +{ + tagResult result = TagFailure; + if ((file->sortMethod == TAG_SORTED && !file->search.ignorecase) || + (file->sortMethod == TAG_FOLDSORTED && file->search.ignorecase)) + { + result = tagsNext (file, entry); + if (result == TagSuccess && nameComparison (file) != 0) + result = TagFailure; + } + else + { + result = findSequential (file); + if (result == TagSuccess && entry != NULL) + parseTagLine (file, entry); + } + return result; +} + +/* +* EXTERNAL INTERFACE +*/ + +extern tagFile *tagsOpen (const char *const filePath, tagFileInfo *const info) +{ + return initialize (filePath, info); +} + +extern tagResult tagsSetSortType (tagFile *const file, const sortType type) +{ + tagResult result = TagFailure; + if (file != NULL && file->initialized) + { + file->sortMethod = type; + result = TagSuccess; + } + return result; +} + +extern tagResult tagsFirst (tagFile *const file, tagEntry *const entry) +{ + tagResult result = TagFailure; + if (file != NULL && file->initialized) + { + gotoFirstLogicalTag (file); + result = readNext (file, entry); + } + return result; +} + +extern tagResult tagsNext (tagFile *const file, tagEntry *const entry) +{ + tagResult result = TagFailure; + if (file != NULL && file->initialized) + result = readNext (file, entry); + return result; +} + +extern const char *tagsField (const tagEntry *const entry, const char *const key) +{ + const char *result = NULL; + if (entry != NULL) + result = readFieldValue (entry, key); + return result; +} + +extern tagResult tagsFind (tagFile *const file, tagEntry *const entry, + const char *const name, const int options) +{ + tagResult result = TagFailure; + if (file != NULL && file->initialized) + result = find (file, entry, name, options); + return result; +} + +extern tagResult tagsFindNext (tagFile *const file, tagEntry *const entry) +{ + tagResult result = TagFailure; + if (file != NULL && file->initialized) + result = findNext (file, entry); + return result; +} + +extern tagResult tagsClose (tagFile *const file) +{ + tagResult result = TagFailure; + if (file != NULL && file->initialized) + { + terminate (file); + result = TagSuccess; + } + return result; +} + +/* +* TEST FRAMEWORK +*/ + +#ifdef READTAGS_MAIN + +static const char *TagFileName = "tags"; +static const char *ProgramName; +static int extensionFields; +static int SortOverride; +static sortType SortMethod; + +static void printTag (const tagEntry *entry) +{ + int i; + int first = 1; + const char* separator = ";\""; + const char* const empty = ""; +/* "sep" returns a value only the first time it is evaluated */ +#define sep (first ? (first = 0, separator) : empty) + printf ("%s\t%s\t%s", + entry->name, entry->file, entry->address.pattern); + if (extensionFields) + { + if (entry->kind != NULL && entry->kind [0] != '\0') + printf ("%s\tkind:%s", sep, entry->kind); + if (entry->fileScope) + printf ("%s\tfile:", sep); +#if 0 + if (entry->address.lineNumber > 0) + printf ("%s\tline:%lu", sep, entry->address.lineNumber); +#endif + for (i = 0 ; i < entry->fields.count ; ++i) + printf ("%s\t%s:%s", sep, entry->fields.list [i].key, + entry->fields.list [i].value); + } + putchar ('\n'); +#undef sep +} + +static void findTag (const char *const name, const int options) +{ + tagFileInfo info; + tagEntry entry; + tagFile *const file = tagsOpen (TagFileName, &info); + if (file == NULL) + { + fprintf (stderr, "%s: cannot open tag file: %s: %s\n", + ProgramName, strerror (info.status.error_number), name); + exit (1); + } + else + { + if (SortOverride) + tagsSetSortType (file, SortMethod); + if (tagsFind (file, &entry, name, options) == TagSuccess) + { + do + { + printTag (&entry); + } while (tagsFindNext (file, &entry) == TagSuccess); + } + tagsClose (file); + } +} + +static void listTags (void) +{ + tagFileInfo info; + tagEntry entry; + tagFile *const file = tagsOpen (TagFileName, &info); + if (file == NULL) + { + fprintf (stderr, "%s: cannot open tag file: %s: %s\n", + ProgramName, strerror (info.status.error_number), TagFileName); + exit (1); + } + else + { + while (tagsNext (file, &entry) == TagSuccess) + printTag (&entry); + tagsClose (file); + } +} + +const char *const Usage = + "Find tag file entries matching specified names.\n\n" + "Usage: %s [-ilp] [-s[0|1]] [-t file] [name(s)]\n\n" + "Options:\n" + " -e Include extension fields in output.\n" + " -i Perform case-insensitive matching.\n" + " -l List all tags.\n" + " -p Perform partial matching.\n" + " -s[0|1|2] Override sort detection of tag file.\n" + " -t file Use specified tag file (default: \"tags\").\n" + "Note that options are acted upon as encountered, so order is significant.\n"; + +extern int main (int argc, char **argv) +{ + int options = 0; + int actionSupplied = 0; + int i; + ProgramName = argv [0]; + if (argc == 1) + { + fprintf (stderr, Usage, ProgramName); + exit (1); + } + for (i = 1 ; i < argc ; ++i) + { + const char *const arg = argv [i]; + if (arg [0] != '-') + { + findTag (arg, options); + actionSupplied = 1; + } + else + { + size_t j; + for (j = 1 ; arg [j] != '\0' ; ++j) + { + switch (arg [j]) + { + case 'e': extensionFields = 1; break; + case 'i': options |= TAG_IGNORECASE; break; + case 'p': options |= TAG_PARTIALMATCH; break; + case 'l': listTags (); actionSupplied = 1; break; + + case 't': + if (arg [j+1] != '\0') + { + TagFileName = arg + j + 1; + j += strlen (TagFileName); + } + else if (i + 1 < argc) + TagFileName = argv [++i]; + else + { + fprintf (stderr, Usage, ProgramName); + exit (1); + } + break; + case 's': + SortOverride = 1; + ++j; + if (arg [j] == '\0') + SortMethod = TAG_SORTED; + else if (strchr ("012", arg[j]) != NULL) + SortMethod = (sortType) (arg[j] - '0'); + else + { + fprintf (stderr, Usage, ProgramName); + exit (1); + } + break; + default: + fprintf (stderr, "%s: unknown option: %c\n", + ProgramName, arg[j]); + exit (1); + break; + } + } + } + } + if (! actionSupplied) + { + fprintf (stderr, + "%s: no action specified: specify tag name(s) or -l option\n", + ProgramName); + exit (1); + } + return 0; +} + +#endif + +/* vi:set tabstop=8 shiftwidth=4: */ diff --git a/kate/addons/kate/kate-ctags/readtags.h b/kate/addons/kate/kate-ctags/readtags.h new file mode 100644 index 00000000..59428a8d --- /dev/null +++ b/kate/addons/kate/kate-ctags/readtags.h @@ -0,0 +1,251 @@ +/* +* +* Copyright (c) 1996-2003, Darren Hiebert +* +* This source code is released for the public domain. +* +* This file defines the public interface for looking up tag entries in tag +* files. +* +* The functions defined in this interface are intended to provide tag file +* support to a software tool. The tag lookups provided are sufficiently fast +* enough to permit opening a sorted tag file, searching for a matching tag, +* then closing the tag file each time a tag is looked up (search times are +* on the order of hundreths of a second, even for huge tag files). This is +* the recommended use of this library for most tool applications. Adhering +* to this approach permits a user to regenerate a tag file at will without +* the tool needing to detect and resynchronize with changes to the tag file. +* Even for an unsorted 24MB tag file, tag searches take about one second. +*/ +#ifndef READTAGS_H +#define READTAGS_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* +* MACROS +*/ + +/* Options for tagsSetSortType() */ +typedef enum { + TAG_UNSORTED, TAG_SORTED, TAG_FOLDSORTED +} sortType ; + +/* Options for tagsFind() */ +#define TAG_FULLMATCH 0x0 +#define TAG_PARTIALMATCH 0x1 + +#define TAG_OBSERVECASE 0x0 +#define TAG_IGNORECASE 0x2 + +/* +* DATA DECLARATIONS +*/ + +typedef enum { TagFailure = 0, TagSuccess = 1 } tagResult; + +struct sTagFile; + +typedef struct sTagFile tagFile; + +/* This structure contains information about the tag file. */ +typedef struct { + + struct { + /* was the tag file successfully opened? */ + int opened; + + /* errno value when 'opened' is false */ + int error_number; + } status; + + /* information about the structure of the tag file */ + struct { + /* format of tag file (1 = original, 2 = extended) */ + short format; + + /* how is the tag file sorted? */ + sortType sort; + } file; + + + /* information about the program which created this tag file */ + struct { + /* name of author of generating program (may be null) */ + const char *author; + + /* name of program (may be null) */ + const char *name; + + /* URL of distribution (may be null) */ + const char *url; + + /* program version (may be null) */ + const char *version; + } program; + +} tagFileInfo; + +/* This structure contains information about an extension field for a tag. + * These exist at the end of the tag in the form "key:value"). + */ +typedef struct { + + /* the key of the extension field */ + const char *key; + + /* the value of the extension field (may be an empty string) */ + const char *value; + +} tagExtensionField; + +/* This structure contains information about a specific tag. */ +typedef struct { + + /* name of tag */ + const char *name; + + /* path of source file containing definition of tag */ + const char *file; + + /* address for locating tag in source file */ + struct { + /* pattern for locating source line + * (may be NULL if not present) */ + const char *pattern; + + /* line number in source file of tag definition + * (may be zero if not known) */ + unsigned long lineNumber; + } address; + + /* kind of tag (may by name, character, or NULL if not known) */ + const char *kind; + + /* is tag of file-limited scope? */ + short fileScope; + + /* miscellaneous extension fields */ + struct { + /* number of entries in `list' */ + unsigned short count; + + /* list of key value pairs */ + tagExtensionField *list; + } fields; + +} tagEntry; + + +/* +* FUNCTION PROTOTYPES +*/ + +/* +* This function must be called before calling other functions in this +* library. It is passed the path to the tag file to read and a (possibly +* null) pointer to a structure which, if not null, will be populated with +* information about the tag file. If successful, the function will return a +* handle which must be supplied to other calls to read information from the +* tag file, and info.status.opened will be set to true. If unsuccessful, +* info.status.opened will be set to false and info.status.error_number will +* be set to the errno value representing the system error preventing the tag +* file from being successfully opened. +*/ +extern tagFile *tagsOpen (const char *const filePath, tagFileInfo *const info); + +/* +* This function allows the client to override the normal automatic detection +* of how a tag file is sorted. Permissible values for `type' are +* TAG_UNSORTED, TAG_SORTED, TAG_FOLDSORTED. Tag files in the new extended +* format contain a key indicating whether or not they are sorted. However, +* tag files in the original format do not contain such a key even when +* sorted, preventing this library from taking advantage of fast binary +* lookups. If the client knows that such an unmarked tag file is indeed +* sorted (or not), it can override the automatic detection. Note that +* incorrect lookup results will result if a tag file is marked as sorted when +* it actually is not. The function will return TagSuccess if called on an +* open tag file or TagFailure if not. +*/ +extern tagResult tagsSetSortType (tagFile *const file, const sortType type); + +/* +* Reads the first tag in the file, if any. It is passed the handle to an +* opened tag file and a (possibly null) pointer to a structure which, if not +* null, will be populated with information about the first tag file entry. +* The function will return TagSuccess another tag entry is found, or +* TagFailure if not (i.e. it reached end of file). +*/ +extern tagResult tagsFirst (tagFile *const file, tagEntry *const entry); + +/* +* Step to the next tag in the file, if any. It is passed the handle to an +* opened tag file and a (possibly null) pointer to a structure which, if not +* null, will be populated with information about the next tag file entry. The +* function will return TagSuccess another tag entry is found, or TagFailure +* if not (i.e. it reached end of file). It will always read the first tag in +* the file immediately after calling tagsOpen(). +*/ +extern tagResult tagsNext (tagFile *const file, tagEntry *const entry); + +/* +* Retrieve the value associated with the extension field for a specified key. +* It is passed a pointer to a structure already populated with values by a +* previous call to tagsNext(), tagsFind(), or tagsFindNext(), and a string +* containing the key of the desired extension field. If no such field of the +* specified key exists, the function will return null. +*/ +extern const char *tagsField (const tagEntry *const entry, const char *const key); + +/* +* Find the first tag matching `name'. The structure pointed to by `entry' +* will be populated with information about the tag file entry. If a tag file +* is sorted using the C locale, a binary search algorithm is used to search +* the tag file, resulting in very fast tag lookups, even in huge tag files. +* Various options controlling the matches can be combined by bit-wise or-ing +* certain values together. The available values are: +* +* TAG_PARTIALMATCH +* Tags whose leading characters match `name' will qualify. +* +* TAG_FULLMATCH +* Only tags whose full lengths match `name' will qualify. +* +* TAG_IGNORECASE +* Matching will be performed in a case-insenstive manner. Note that +* this disables binary searches of the tag file. +* +* TAG_OBSERVECASE +* Matching will be performed in a case-senstive manner. Note that +* this enables binary searches of the tag file. +* +* The function will return TagSuccess if a tag matching the name is found, or +* TagFailure if not. +*/ +extern tagResult tagsFind (tagFile *const file, tagEntry *const entry, const char *const name, const int options); + +/* +* Find the next tag matching the name and options supplied to the most recent +* call to tagsFind() for the same tag file. The structure pointed to by +* `entry' will be populated with information about the tag file entry. The +* function will return TagSuccess if another tag matching the name is found, +* or TagFailure if not. +*/ +extern tagResult tagsFindNext (tagFile *const file, tagEntry *const entry); + +/* +* Call tagsTerminate() at completion of reading the tag file, which will +* close the file and free any internal memory allocated. The function will +* return TagFailure is no file is currently open, TagSuccess otherwise. +*/ +extern tagResult tagsClose (tagFile *const file); + +#ifdef __cplusplus +} +#endif + +#endif + +/* vi:set tabstop=8 shiftwidth=4: */ diff --git a/kate/addons/kate/kate-ctags/tags.cpp b/kate/addons/kate/kate-ctags/tags.cpp new file mode 100644 index 00000000..c0b03d3d --- /dev/null +++ b/kate/addons/kate/kate-ctags/tags.cpp @@ -0,0 +1,153 @@ +/*************************************************************************** + * Copyright (C) 2004 by Jens Dagerbo * + * jens.dagerbo@swipnet.se * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +#include "tags.h" +#include + +namespace ctags +{ +#include "readtags.h" +} + +#include "ctagskinds.h" + +QString Tags::_tagsfile; + +Tags::TagEntry::TagEntry() {} + +Tags::TagEntry::TagEntry( const QString & tag, const QString & type, const QString & file, const QString & pattern ) + : tag(tag), type(type), file(file), pattern(pattern) +{} + + +bool Tags::hasTag( const QString & tag ) +{ + ctags::tagFileInfo info; + ctags::tagFile * file = ctags::tagsOpen( _tagsfile.toLocal8Bit(), &info ); + ctags::tagEntry entry; + + bool found = ( ctags::tagsFind( file, &entry, tag.toLocal8Bit(), TAG_FULLMATCH | TAG_OBSERVECASE ) == ctags::TagSuccess ); + + ctags::tagsClose( file ); + + return found; +} + +unsigned int Tags::numberOfMatches( const QString & tagpart, bool partial ) +{ + unsigned int n = 0; + + if ( tagpart.isEmpty() ) return 0; + + ctags::tagFileInfo info; + ctags::tagFile * file = ctags::tagsOpen( _tagsfile.toLocal8Bit(), &info ); + ctags::tagEntry entry; + + QByteArray tagpartBArray = tagpart.toLocal8Bit(); // for holding the char * + if ( ctags::tagsFind( file, &entry, tagpartBArray.data(), TAG_OBSERVECASE | (partial ? TAG_PARTIALMATCH : TAG_FULLMATCH) ) == ctags::TagSuccess ) + { + do + { + n++; + } + while ( ctags::tagsFindNext( file, &entry ) == ctags::TagSuccess ); + } + + ctags::tagsClose( file ); + + return n; +} + +Tags::TagList Tags::getMatches( const QString & tagpart, bool partial, const QStringList & types ) +{ + Tags::TagList list; + + if ( tagpart.isEmpty() ) return list; + + ctags::tagFileInfo info; + ctags::tagFile * file = ctags::tagsOpen( _tagsfile.toLocal8Bit(), &info ); + ctags::tagEntry entry; + + QByteArray tagpartBArray = tagpart.toLocal8Bit(); // for holding the char * + if ( ctags::tagsFind( file, &entry, tagpartBArray.data(), TAG_OBSERVECASE | (partial ? TAG_PARTIALMATCH : TAG_FULLMATCH) ) == ctags::TagSuccess ) + { + do + { + QString type( CTagsKinds::findKind( entry.kind, QString( entry.file ).section( '.', -1 ) ) ); + QString file( entry.file ); + + if ( type.isEmpty() && file.endsWith( "Makefile" ) ) + { + type = "macro"; + } + if ( types.isEmpty() || types.contains( entry.kind ) ) + { + list << TagEntry( QString( entry.name ), type, file, QString( entry.address.pattern ) ); + } + } + while ( ctags::tagsFindNext( file, &entry ) == ctags::TagSuccess ); + } + + ctags::tagsClose( file ); + + return list; +} + +void Tags::setTagsFile( const QString & file ) +{ + _tagsfile = file.toLocal8Bit(); +} + +QString Tags::getTagsFile( ) +{ + return _tagsfile; +} + +unsigned int Tags::numberOfPartialMatches( const QString & tagpart ) +{ + return numberOfMatches( tagpart, true ); +} + +unsigned int Tags::numberOfExactMatches( const QString & tagpart ) +{ + return numberOfMatches( tagpart, false ); +} + +Tags::TagList Tags::getPartialMatches( const QString & tagpart ) +{ + return getMatches( tagpart, true ); +} + +Tags::TagList Tags::getExactMatches( const QString & tag ) +{ + return getMatches( tag, false ); +} + +Tags::TagList Tags::getPartialMatches( const QString & file, const QString & tagpart ) +{ + setTagsFile( file ); + return getMatches( tagpart, true ); +} + +Tags::TagList Tags::getExactMatches( const QString & file, const QString & tag ) +{ + setTagsFile( file ); + return getMatches( tag, false ); +} + +Tags::TagList Tags::getMatches( const QString & file, const QString & tagpart, bool partial, const QStringList & types ) +{ + setTagsFile( file ); + return getMatches( tagpart, partial, types); +} + + // kate: space-indent off; indent-width 4; tab-width 4; show-tabs off; + + diff --git a/kate/addons/kate/kate-ctags/tags.h b/kate/addons/kate/kate-ctags/tags.h new file mode 100644 index 00000000..412969eb --- /dev/null +++ b/kate/addons/kate/kate-ctags/tags.h @@ -0,0 +1,69 @@ +/*************************************************************************** + * Copyright (C) 2004 by Jens Dagerbo * + * jens.dagerbo@swipnet.se * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef TAGS_H +#define TAGS_H + +#include +#include +#include + +class Tags +{ +public: + + struct TagEntry + { + TagEntry(); + TagEntry( const QString & tag, const QString & type, const QString & file, const QString & pattern ); + + QString tag; + QString type; + QString file; + QString pattern; + }; + + typedef QList TagList; + + /** + * Method to set the tag database filename + * @param file the tag database filename + */ + static void setTagsFile( const QString & file ); + + static QString getTagsFile(); + + /** + * Method to check if the tag database contains a specific tag + * @param tag Tag to look up + * @return returns true if tag database contains 'tag' + */ + static bool hasTag( const QString & tag ); + + static unsigned int numberOfPartialMatches( const QString & tagpart ); + static unsigned int numberOfExactMatches( const QString & tag ); + static unsigned int numberOfMatches( const QString & tagpart, bool partial ); + + static TagList getPartialMatches( const QString & tagpart ); + static TagList getExactMatches( const QString & tag ); + static TagList getMatches( const QString & tagpart, bool partial, const QStringList & types = QStringList() ); + + static TagList getPartialMatches( const QString & file, const QString & tagpart ); + static TagList getExactMatches( const QString & file, const QString & tag ); + static TagList getMatches( const QString & file, const QString & tagpart, bool partial, const QStringList & types = QStringList() ); + +private: + static QString _tagsfile; +}; + +#endif + +// kate: space-indent off; indent-width 4; tab-width 4; show-tabs off; diff --git a/kate/addons/kate/kate-ctags/ui.rc b/kate/addons/kate/kate-ctags/ui.rc new file mode 100644 index 00000000..5e8cfe51 --- /dev/null +++ b/kate/addons/kate/kate-ctags/ui.rc @@ -0,0 +1,17 @@ + + + + CTags + + + + + + + + + + + + + diff --git a/kate/addons/kate/katebuild-plugin/CMakeLists.txt b/kate/addons/kate/katebuild-plugin/CMakeLists.txt new file mode 100644 index 00000000..99dee635 --- /dev/null +++ b/kate/addons/kate/katebuild-plugin/CMakeLists.txt @@ -0,0 +1,29 @@ +set(katebuild_SRCS + plugin_katebuild.cpp + targets.cpp + selecttargetdialog.cpp +) + +kde4_add_plugin(katebuildplugin ${katebuild_SRCS}) + +target_link_libraries(katebuildplugin + ${KDE4_KDECORE_LIBS} + kateinterfaces + ${KDE4_KTEXTEDITOR_LIBS} +) + +install( + TARGETS katebuildplugin + DESTINATION ${KDE4_PLUGIN_INSTALL_DIR} +) + +########### install files ############### + +install( + FILES ui.rc + DESTINATION ${KDE4_DATA_INSTALL_DIR}/kate/plugins/katebuild +) +install( + FILES katebuildplugin.desktop + DESTINATION ${KDE4_SERVICES_INSTALL_DIR} +) diff --git a/kate/addons/kate/katebuild-plugin/Messages.sh b/kate/addons/kate/katebuild-plugin/Messages.sh new file mode 100755 index 00000000..817fd439 --- /dev/null +++ b/kate/addons/kate/katebuild-plugin/Messages.sh @@ -0,0 +1,5 @@ +#! /bin/sh +$EXTRACTRC `find . -name \*.ui -o -name \*.rc` >> rc.cpp +$XGETTEXT *.cpp -o $podir/katebuild-plugin.pot +rm -f rc.cpp + diff --git a/kate/addons/kate/katebuild-plugin/build.ui b/kate/addons/kate/katebuild-plugin/build.ui new file mode 100644 index 00000000..282188a3 --- /dev/null +++ b/kate/addons/kate/katebuild-plugin/build.ui @@ -0,0 +1,201 @@ + + + build + + + + 0 + 0 + 407 + 178 + + + + + 0 + + + 0 + + + + + 0 + + + + Output + + + + + + + + Show: + + + + + + + + 0 + 0 + + + + + 100 + 0 + + + + 3 + + + 1 + + + Qt::Horizontal + + + QSlider::TicksAbove + + + 1 + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + + Build again + + + + + + + Cancel + + + + + + + + + + + + + + + + + + Build again + + + + + + + Cancel + + + + + + + + + + 1 + 1 + + + + false + + + true + + + false + + + true + + + + File + + + + + Line + + + + + Message + + + + + + + + true + + + + + + + + + + + + KTabWidget + QTabWidget +
ktabwidget.h
+ 1 +
+
+ + errTreeWidget + + + +
diff --git a/kate/addons/kate/katebuild-plugin/katebuildplugin.desktop b/kate/addons/kate/katebuild-plugin/katebuildplugin.desktop new file mode 100644 index 00000000..6d6ba38c --- /dev/null +++ b/kate/addons/kate/katebuild-plugin/katebuildplugin.desktop @@ -0,0 +1,114 @@ +[Desktop Entry] +Type=Service +ServiceTypes=Kate/Plugin +X-KDE-Library=katebuildplugin +X-Kate-Version=2.9 +Name=Build Plugin +Name[ar]=ملحقة بناء +Name[ast]=Complementu de construcción +Name[bg]=Приставка за построяване +Name[bs]=Priključak za gradnju +Name[ca]=Connector de construcció +Name[ca@valencia]=Connector de construcció +Name[cs]=Modul pro překlad +Name[da]=Bygge-plugin +Name[de]=Erstellen-Modul +Name[el]=Πρόσθετο κατασκευής +Name[en_GB]=Build Plugin +Name[es]=Complemento de construcción +Name[et]=Ehitamisplugin +Name[eu]=Eraikitzeko plugina +Name[fi]=Koostamisliitännäinen +Name[fr]=Module externe de compilation +Name[ga]=Breiseán Tógála +Name[gl]=Complemento de construción +Name[he]=תוסף בנייה +Name[hu]=Lefordító bővítmény +Name[ia]= Plug-in Build (Plug-in de construer) +Name[it]=Estensione di generazione +Name[ja]=ビルドプラグイン +Name[kk]=Плагинді құру +Name[km]=ស្ថាបនា​កម្មវិធី​ជំនួយ​​ +Name[ko]=빌드 플러그인 +Name[lt]=Sukūrimo priedas +Name[lv]=Būvēšanas spraudnis +Name[mr]=बिल्ड प्लगइन +Name[nb]=Bygg programtillegg +Name[nds]=Buumoduul +Name[nl]=Bouwplug-in +Name[nn]=Bygg-tillegg +Name[pa]=ਬਿਲਡ ਪਲੱਗਇਨ +Name[pl]=Wtyczka budowania +Name[pt]='Plugin' de Compilação +Name[pt_BR]=Plugin de compilação +Name[ro]=Modul de construire +Name[ru]=Модуль для сборки ПО +Name[si]=ප්ලගිනය ගොඩනගන්න +Name[sk]=Build Plugin +Name[sl]=Vstavek za izgradnjo +Name[sr]=Прикључак за градњу +Name[sr@ijekavian]=Прикључак за градњу +Name[sr@ijekavianlatin]=Priključak za gradnju +Name[sr@latin]=Priključak za gradnju +Name[sv]=Bygginsticksprogram +Name[tg]=Эҷоди плагин +Name[tr]=Derleme Eklentisi +Name[ug]=قىستۇرما قۇر +Name[uk]=Додаток для збирання +Name[wa]=Tchôke-divins d' bastixhaedje +Name[x-test]=xxBuild Pluginxx +Name[zh_CN]=构建插件 +Name[zh_TW]=編譯外掛程式 +Comment=Compile or Make and parse error messages +Comment[ast]=Compilar o usar make y analizar mensaxes d'error +Comment[bs]=Kompilovanje ili spravljanje i raščlanjivanje grešaka +Comment[ca]=Compila o construeix amb Make i analitza els missatges d'error +Comment[ca@valencia]=Compila o construeix amb Make i analitza els missatges d'error +Comment[cs]=Zkompilovat či sestavit a analyzovat chybová hlášení +Comment[da]=Oversættelse eller Make og fortolkning af fejlmeddelelser +Comment[de]=Kompilieren oder Make aufrufen und Fehlermeldungen ausgeben +Comment[el]=Σφάλματα μηνυμάτων μεταγλώττισης ή εκτέλεσης Make και ανάλυσης +Comment[en_GB]=Compile or Make and parse error messages +Comment[es]=Compilar o usar make y analizar mensajes de error +Comment[et]=Kompileerimine või ehitamine (make) ja veateadete parsimine +Comment[eu]=Konpilatu edo Make eta analizatu erroreen mezuak +Comment[fi]=Kääntää tai ajaa maken ja jäsentää virheilmoitukset +Comment[fr]=Compile ou lance « make » et analyse syntaxiquement les messages d'erreur +Comment[ga]=Tiomsaigh nó Tóg agus parsáil teachtaireachtaí earráide +Comment[gl]=Compila ou constrúe e procesa as mensaxes de erro +Comment[he]=בנייה ופיענוח של שגיאות בנייה +Comment[hu]=Fordítás vagy Make művelet a hibaüzenetek feldolgozásával +Comment[ia]=Compila o face e analysa messages de error +Comment[it]=Compila o esegui make ed elabora i messaggi di errore +Comment[ja]=コンパイルまたは make してエラーメッセージを解析します +Comment[kk]=Компиляция не жинақтау және талдау қателіктер хабарламалары +Comment[km]=Compile ឬ Make និង​ញែកសារ​កំហុស +Comment[ko]=컴파일, 메이크 실행 및 오류 메시지 처리 +Comment[lt]=Kompiliuoti arba kurti ir išvesti klaidų pranešimus +Comment[lv]=Kompilē un analizē kļūdu ziņojumus +Comment[mr]=कंपाईल किंवा मेक व त्रुटी संदेश वाचतो +Comment[nb]=Kompiler eller bygg, og tolk feilmeldinger +Comment[nds]=Kompileren oder "Make" utföhren un Fehlermellen inlesen +Comment[nl]=Compileer of voer Make uit en analyseer foutmeldingen +Comment[nn]=Kompiler eller bygg, og tolk feilmeldingar +Comment[pl]=Kompiluj lub uruchom make i przeanalizuj komunikaty o błędach +Comment[pt]=Compilar ou construir e processar as mensagens de erro +Comment[pt_BR]=Compila ou executa o Make e processa as mensagens de erro +Comment[ro]=Compilează sau rulează Make și parcurge mesajele de eroare +Comment[ru]=Сборка и обработка сообщений об ошибках +Comment[si]=දෝශ පණිවුඩ සම්පාදනය හෝ සාදා පළ කරන්න +Comment[sk]=Preložiť alebo zlinkovať a skontrolovať chybové správy +Comment[sl]=Prevedite izvorno kodo in razčlenite sporočila o napakah +Comment[sr]=Компиловање или справљање и рашчлањивање грешака +Comment[sr@ijekavian]=Компиловање или справљање и рашчлањивање грешака +Comment[sr@ijekavianlatin]=Kompilovanje ili spravljanje i raščlanjivanje grešaka +Comment[sr@latin]=Kompilovanje ili spravljanje i raščlanjivanje grešaka +Comment[sv]=Kompilera eller bygg och tolka felmeddelanden +Comment[tg]=Ҳамгардон ё эҷод кунед ва паёмҳо дар бораи хато гиред +Comment[tr]=Derleme ya da Make ve ayrıştırma hata iletileri +Comment[ug]=تەرجىمە-تەھرىر ياكى بۇيرۇق قۇرىدۇ ھەمدە خاتالىق ئۇچۇرىنى تەھلىل قىلىدۇ +Comment[uk]=Компілює або збирає і обробляє повідомлення про помилки +Comment[wa]=Compiler ou fé make eyet messaedjes d' aroke d' analijhe del sintake +Comment[x-test]=xxCompile or Make and parse error messagesxx +Comment[zh_CN]=执行编译或构建命令并分析错误讯息 +Comment[zh_TW]=編譯或執行 make 並分析錯誤訊息 diff --git a/kate/addons/kate/katebuild-plugin/plugin_katebuild.cpp b/kate/addons/kate/katebuild-plugin/plugin_katebuild.cpp new file mode 100644 index 00000000..801ba8e7 --- /dev/null +++ b/kate/addons/kate/katebuild-plugin/plugin_katebuild.cpp @@ -0,0 +1,1542 @@ +/* plugin_katebuild.c Kate Plugin +** +** Copyright (C) 2013 by Alexander Neundorf +** Copyright (C) 2006-2011 by Kåre Särs +** Copyright (C) 2011 by Ian Wakeling +** +** This code is mostly a modification of the GPL'ed Make plugin +** by Adriaan de Groot. +*/ + +/* +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program in a file called COPYING; if not, write to +** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +** MA 02110-1301, USA. +*/ + +#include "moc_plugin_katebuild.cpp" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + + +#include "selecttargetdialog.h" + +K_PLUGIN_FACTORY(KateBuildPluginFactory, registerPlugin();) +K_EXPORT_PLUGIN(KateBuildPluginFactory(KAboutData("katebuild", + "katebuild-plugin", + ki18n("Build Plugin"), + "0.1", + ki18n( "Build Plugin")))) + +static const QString DefConfigCmd = "cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX=/usr/local ../"; +static const QString DefConfClean = ""; +static const QString DefTargetName = "all"; +static const QString DefBuildCmd = "make"; +static const QString DefCleanCmd = "make clean"; + + +/******************************************************************/ +KateBuildPlugin::KateBuildPlugin(QObject *parent, const VariantList&): +Kate::Plugin ((Kate::Application*)parent, "kate-build-plugin") +{ + KGlobal::locale()->insertCatalog("katebuild-plugin"); +} + +/******************************************************************/ +Kate::PluginView *KateBuildPlugin::createView (Kate::MainWindow *mainWindow) +{ + return new KateBuildView(mainWindow); +} + +/******************************************************************/ +KateBuildView::KateBuildView(Kate::MainWindow *mw) + : Kate::PluginView (mw) + , Kate::XMLGUIClient(KateBuildPluginFactory::componentData()) + , m_toolView (mw->createToolView ("kate_private_plugin_katebuildplugin", + Kate::MainWindow::Bottom, + SmallIcon("application-x-ms-dos-executable"), + i18n("Build Output")) + ) + , m_buildWidget(0) + , m_outputWidgetWidth(0) + , m_proc(0) + , m_buildCancelled(false) + , m_displayModeBeforeBuild(1) + // NOTE this will not allow spaces in file names. + // e.g. from gcc: "main.cpp:14: error: cannot convert ‘std::string’ to ‘int’ in return" + , m_filenameDetector("(([a-np-zA-Z]:[\\\\/])?[a-zA-Z0-9_\\.\\-/\\\\]+\\.[a-zA-Z0-9]+):([0-9]+)(.*)") + // e.g. from icpc: "main.cpp(14): error: no suitable conversion function from "std::string" to "int" exists" + , m_filenameDetectorIcpc("(([a-np-zA-Z]:[\\\\/])?[a-zA-Z0-9_\\.\\-/\\\\]+\\.[a-zA-Z0-9]+)\\(([0-9]+)\\)(:.*)") + , m_filenameDetectorGccWorked(false) + , m_newDirDetector("make\\[.+\\]: .+ `.*'") +{ + m_win=mw; + + KAction *a = actionCollection()->addAction("run_make"); + a->setText(i18n("Build Default Target")); + connect(a, SIGNAL(triggered(bool)), this, SLOT(slotMake())); + + a = actionCollection()->addAction("make_clean"); + a->setText(i18n("Clean")); + connect(a, SIGNAL(triggered(bool)), this, SLOT(slotMakeClean())); + + a = actionCollection()->addAction("stop"); + a->setText(i18n("Stop")); + connect(a, SIGNAL(triggered(bool)), this, SLOT(slotStop())); + + a = actionCollection()->addAction("select_target"); + a->setText(i18n("Build Target...")); + connect(a, SIGNAL(triggered(bool)), this, SLOT(slotSelectTarget())); + + a = actionCollection()->addAction("build_previous_target"); + a->setText(i18n("Build Previous Target Again")); + connect(a, SIGNAL(triggered(bool)), this, SLOT(slotBuildPreviousTarget())); + + a = actionCollection()->addAction("goto_next"); + a->setText(i18n("Next Error")); + a->setShortcut(QKeySequence(Qt::CTRL+Qt::ALT+Qt::Key_Right)); + connect(a, SIGNAL(triggered(bool)), this, SLOT(slotNext())); + + a = actionCollection()->addAction("goto_prev"); + a->setText(i18n("Previous Error")); + a->setShortcut(QKeySequence(Qt::CTRL+Qt::ALT+Qt::Key_Left)); + connect(a, SIGNAL(triggered(bool)), this, SLOT(slotPrev())); + + m_targetSelectAction = actionCollection()->add( "targets" ); + m_targetSelectAction->setText( i18n( "Sets of Targets" ) ); + connect(m_targetSelectAction, SIGNAL(triggered(int)), this, SLOT(targetSelected(int))); + + a = actionCollection()->addAction("target_next"); + a->setText(i18n("Next Set of Targets")); + connect(a, SIGNAL(triggered(bool)), this, SLOT(targetNext())); + + m_buildWidget = new QWidget(m_toolView); + m_buildUi.setupUi(m_buildWidget); + m_targetsUi = new TargetsUi(m_buildUi.ktabwidget); + m_buildUi.ktabwidget->insertTab(0, m_targetsUi, i18nc("Tab label", "Target Settings")); + m_buildUi.ktabwidget->setCurrentWidget(m_targetsUi); + + m_buildWidget->installEventFilter(this); + + m_buildUi.buildAgainButton->setVisible(true); + m_buildUi.cancelBuildButton->setVisible(true); + m_buildUi.buildStatusLabel->setVisible(true); + m_buildUi.buildAgainButton2->setVisible(false); + m_buildUi.cancelBuildButton2->setVisible(false); + m_buildUi.buildStatusLabel2->setVisible(false); + m_buildUi.extraLineLayout->setAlignment(Qt::AlignRight); + m_buildUi.cancelBuildButton->setEnabled(false); + m_buildUi.cancelBuildButton2->setEnabled(false); + + connect(m_buildUi.errTreeWidget, SIGNAL(itemClicked(QTreeWidgetItem*,int)), + SLOT(slotItemSelected(QTreeWidgetItem*))); + + m_buildUi.plainTextEdit->setReadOnly(true); + slotDisplayMode(0); + + connect(m_buildUi.displayModeSlider, SIGNAL(valueChanged(int)), this, SLOT(slotDisplayMode(int))); + + connect(m_buildUi.buildAgainButton, SIGNAL(clicked()), this, SLOT(slotBuildPreviousTarget())); + connect(m_buildUi.cancelBuildButton, SIGNAL(clicked()), this, SLOT(slotStop())); + connect(m_buildUi.buildAgainButton2, SIGNAL(clicked()), this, SLOT(slotBuildPreviousTarget())); + connect(m_buildUi.cancelBuildButton2, SIGNAL(clicked()), this, SLOT(slotStop())); + + connect(m_targetsUi->browse, SIGNAL(clicked()), this, SLOT(slotBrowseClicked())); + + connect(m_targetsUi->addButton, SIGNAL(clicked()), this, SLOT(slotAddTargetClicked())); + connect(m_targetsUi->buildButton, SIGNAL(clicked()), this, SLOT(slotBuildTargetClicked())); + connect(m_targetsUi->deleteButton, SIGNAL(clicked()), this, SLOT(slotDeleteTargetClicked())); + connect(m_targetsUi->targetsList, SIGNAL(cellChanged(int, int)), this, SLOT(slotCellChanged(int, int))); + connect(m_targetsUi->targetsList, SIGNAL(itemSelectionChanged()), this, SLOT(slotSelectionChanged())); + + connect(m_targetsUi->targetsList->horizontalHeader(), SIGNAL(sectionDoubleClicked(int)), this, SLOT(slotResizeColumn(int))); + + // set the default values of the build settings. (I think loading a plugin should also trigger + // a read of the session config data, but it does not) + //m_targetsUi->buildCmds->setText("make"); + //m_targetsUi->cleanCmds->setText("make clean"); + + QCompleter* dirCompleter = new QCompleter(this); + QStringList filter; + dirCompleter->setModel(new QDirModel(filter, QDir::AllDirs|QDir::NoDotAndDotDot, QDir::Name, this)); + m_targetsUi->buildDir->setCompleter(dirCompleter); + + m_targetsUi->deleteButton->setEnabled(false); + m_targetsUi->buildButton->setEnabled(false); + + m_proc = new KProcess(); + + connect(m_proc, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(slotProcExited(int,QProcess::ExitStatus))); + connect(m_proc, SIGNAL(readyReadStandardError()),this, SLOT(slotReadReadyStdErr())); + connect(m_proc, SIGNAL(readyReadStandardOutput()),this, SLOT(slotReadReadyStdOut())); + + connect(m_targetsUi->targetCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(targetSelected(int))); + connect(m_targetsUi->targetCombo->lineEdit(), SIGNAL(textEdited(QString)), this, SLOT(slotTargetSetNameChanged(const QString&))); + connect(m_targetsUi->newTarget, SIGNAL(clicked()), this, SLOT(targetNew())); + connect(m_targetsUi->copyTarget, SIGNAL(clicked()), this, SLOT(targetCopy())); + connect(m_targetsUi->deleteTarget, SIGNAL(clicked()), this, SLOT(targetDelete())); + connect(m_targetsUi->buildDir, SIGNAL(textChanged(const QString&)), this, SLOT(slotBuildDirChanged(const QString&))); + + connect(mainWindow(), SIGNAL(unhandledShortcutOverride(QEvent*)), + this, SLOT(handleEsc(QEvent*))); + + m_toolView->installEventFilter(this); + + mainWindow()->guiFactory()->addClient(this); + + // watch for project plugin view creation/deletion + connect(mainWindow(), SIGNAL(pluginViewCreated (const QString &, Kate::PluginView *)) + , this, SLOT(slotPluginViewCreated (const QString &, Kate::PluginView *))); + + connect(mainWindow(), SIGNAL(pluginViewDeleted (const QString &, Kate::PluginView *)) + , this, SLOT(slotPluginViewDeleted (const QString &, Kate::PluginView *))); + + // update once project plugin state manually + m_projectPluginView = mainWindow()->pluginView ("kateprojectplugin"); + slotProjectMapChanged (); + slotSelectionChanged(); +} + + +/******************************************************************/ +KateBuildView::~KateBuildView() +{ + mainWindow()->guiFactory()->removeClient( this ); + delete m_proc; + delete m_toolView; +} + +/******************************************************************/ +QWidget *KateBuildView::toolView() const +{ + return m_toolView; +} + +/******************************************************************/ +void KateBuildView::readSessionConfig (KConfigBase* config, const QString& groupPrefix) +{ + m_targetsUi->targetCombo->blockSignals(true); + + KConfigGroup cg(config, groupPrefix + ":build-plugin"); + int numTargets = cg.readEntry("NumTargets", 0); + m_targetsUi->targetCombo->clear(); + m_targetList.clear(); + m_targetIndex = 0; + int tmpIndex; + if (numTargets == 0 ) { + // either the config is empty or uses the older format + m_targetList.append(TargetSet()); + + m_targetList[0].name = "Default"; + m_targetList[0].defaultTarget = QString("build"); + m_targetList[0].cleanTarget = QString("clean"); + m_targetList[0].defaultDir = cg.readEntry(QString("Make Path"), QString()); + + m_targetList[0].prevTarget.clear(); + + m_targetList[0].targets["config"] = DefConfigCmd; + m_targetList[0].targets["build"] = cg.readEntry(QString("Make Command"), DefBuildCmd); + m_targetList[0].targets["clean"] = cg.readEntry(QString("Make Command"), DefCleanCmd); + + QString quickCmd = cg.readEntry(QString("Quick Compile Command")); + if (!quickCmd.isEmpty()) { + m_targetList[0].targets["quick"] = quickCmd; + } + + tmpIndex = 0; + } + else { + + for (int i=0; itargetCombo->addItem(m_targetList[i].name); + } + + + // update the targets menu + targetsChanged(); + + // select the last active target if possible + m_targetsUi->targetCombo->setCurrentIndex(tmpIndex); + targetSelected(tmpIndex); + + m_targetsUi->targetCombo->blockSignals(false); +} + +/******************************************************************/ +void KateBuildView::writeSessionConfig (KConfigBase* config, const QString& groupPrefix) +{ + KConfigGroup cg(config, groupPrefix + ":build-plugin"); + cg.writeEntry("NumTargets", m_targetList.size()); + for (int i=0; i::const_iterator tgtIt = m_targetList[i].targets.begin(); + tgtIt != m_targetList[i].targets.end(); ++tgtIt) { + const QString& tgtName = tgtIt->first; + const QString& buildCmd = tgtIt->second; + + targetNames << tgtName; + + cg.writeEntry(QString("%1 BuildCmd %2").arg(i).arg(tgtName), buildCmd); + } + + cg.writeEntry(QString("%1 Target Names").arg(i), targetNames); + } + cg.writeEntry(QString("Active Target Index"), m_targetIndex); +} + + +/******************************************************************/ +void KateBuildView::slotNext() +{ + const int itemCount = m_buildUi.errTreeWidget->topLevelItemCount(); + if (itemCount == 0) { + return; + } + + QTreeWidgetItem *item = m_buildUi.errTreeWidget->currentItem(); + if (item && item->isHidden()) item = 0; + + int i = (item == 0) ? -1 : m_buildUi.errTreeWidget->indexOfTopLevelItem(item); + + while (++i < itemCount) { + item = m_buildUi.errTreeWidget->topLevelItem(i); + if (!item->text(1).isEmpty() && !item->isHidden()) { + m_buildUi.errTreeWidget->setCurrentItem(item); + m_buildUi.errTreeWidget->scrollToItem(item); + slotItemSelected(item); + return; + } + } +} + +/******************************************************************/ +void KateBuildView::slotPrev() +{ + const int itemCount = m_buildUi.errTreeWidget->topLevelItemCount(); + if (itemCount == 0) { + return; + } + + QTreeWidgetItem *item = m_buildUi.errTreeWidget->currentItem(); + if (item && item->isHidden()) item = 0; + + int i = (item == 0) ? itemCount : m_buildUi.errTreeWidget->indexOfTopLevelItem(item); + + while (--i >= 0) { + item = m_buildUi.errTreeWidget->topLevelItem(i); + if (!item->text(1).isEmpty() && !item->isHidden()) { + m_buildUi.errTreeWidget->setCurrentItem(item); + m_buildUi.errTreeWidget->scrollToItem(item); + slotItemSelected(item); + return; + } + } +} + +/******************************************************************/ +void KateBuildView::slotItemSelected(QTreeWidgetItem *item) +{ + // get stuff + const QString filename = item->data(0, Qt::UserRole).toString(); + if (filename.isEmpty()) return; + const int line = item->data(1, Qt::UserRole).toInt(); + const int column = item->data(2, Qt::UserRole).toInt(); + + // open file (if needed, otherwise, this will activate only the right view...) + m_win->openUrl(KUrl(filename)); + + // any view active? + if (!m_win->activeView()) { + return; + } + + // do it ;) + m_win->activeView()->setCursorPosition(KTextEditor::Cursor(line-1, column)); + m_win->activeView()->setFocus(); +} + + +/******************************************************************/ +void KateBuildView::addError(const QString &filename, const QString &line, + const QString &column, const QString &message) +{ + bool isError=false; + bool isWarning=false; + QTreeWidgetItem* item = new QTreeWidgetItem(m_buildUi.errTreeWidget); + item->setBackground(1, Qt::gray); + // The strings are twice in case kate is translated but not make. + if (message.contains("error") || + message.contains(i18nc("The same word as 'make' uses to mark an error.","error")) || + message.contains("undefined reference") || + message.contains(i18nc("The same word as 'ld' uses to mark an ...","undefined reference")) + ) + { + isError=true; + item->setForeground(1, Qt::red); + m_numErrors++; + item->setHidden(false); + } + if (message.contains("warning") || + message.contains(i18nc("The same word as 'make' uses to mark a warning.","warning")) + ) + { + isWarning=true; + item->setForeground(1, Qt::yellow); + m_numWarnings++; + item->setHidden(m_buildUi.displayModeSlider->value() > 2); + } + item->setTextAlignment(1, Qt::AlignRight); + + // visible text + //remove path from visible file name + KUrl file(filename); + item->setText(0, file.fileName()); + item->setText(1, line); + item->setText(2, message.trimmed()); + + // used to read from when activating an item + item->setData(0, Qt::UserRole, filename); + item->setData(1, Qt::UserRole, line); + item->setData(2, Qt::UserRole, column); + + if ((isError==false) && (isWarning==false)) { + item->setHidden(m_buildUi.displayModeSlider->value() > 1); + } + + item->setData(0,Qt::UserRole+1,isError); + item->setData(0,Qt::UserRole+2,isWarning); + + // add tooltips in all columns + // The enclosing ... enables word-wrap for long error messages + item->setData(0, Qt::ToolTipRole, filename); + item->setData(1, Qt::ToolTipRole, QString("" + message + "")); + item->setData(2, Qt::ToolTipRole, QString("" + message + "")); +} + +/******************************************************************/ +KUrl KateBuildView::docUrl() +{ + KTextEditor::View *kv = mainWindow()->activeView(); + if (!kv) { + kDebug() << "no KTextEditor::View" << endl; + return KUrl(); + } + + if (kv->document()->isModified()) kv->document()->save(); + return kv->document()->url(); +} + +/******************************************************************/ +bool KateBuildView::checkLocal(const KUrl &dir) +{ + if (dir.path().isEmpty()) { + KMessageBox::sorry(0, i18n("There is no file or directory specified for building.")); + return false; + } + else if (!dir.isLocalFile()) { + KMessageBox::sorry(0, i18n("The file \"%1\" is not a local file. " + "Non-local files cannot be compiled.", dir.path())); + return false; + } + return true; +} + + + +/******************************************************************/ +void KateBuildView::slotBuildPreviousTarget() { + TargetSet* tgtSet = currentTargetSet(); + if (tgtSet == 0) { + return; + } + + if (tgtSet->prevTarget.isEmpty()) { + KMessageBox::sorry(0, i18n("No previous target to build.")); + return; + } + + buildTarget(tgtSet->prevTarget); +} + + +/******************************************************************/ +bool KateBuildView::slotMake(void) +{ + TargetSet* tgtSet = currentTargetSet(); + if (tgtSet == 0) { + return false; + } + + if (tgtSet->defaultTarget.isEmpty()) { + KMessageBox::sorry(0, i18n("No target set as default target.")); + return false; + } + + return buildTarget(tgtSet->defaultTarget); +} + +/******************************************************************/ +bool KateBuildView::slotMakeClean(void) +{ + TargetSet* tgtSet = currentTargetSet(); + if (tgtSet == 0) { + return false; + } + + if (tgtSet->cleanTarget.isEmpty()) { + KMessageBox::sorry(0, i18n("No target set as clean target.")); + return false; + } + + return buildTarget(tgtSet->cleanTarget); +} + +/******************************************************************/ +void KateBuildView::clearBuildResults() +{ + m_buildUi.plainTextEdit->clear(); + m_buildUi.errTreeWidget->clear(); + m_output_lines.clear(); + m_numErrors = 0; + m_numWarnings = 0; + m_make_dir_stack.clear(); +} + +/******************************************************************/ +bool KateBuildView::startProcess(const KUrl &dir, const QString &command) +{ + if (m_proc->state() != QProcess::NotRunning) { + return false; + } + + // clear previous runs + clearBuildResults(); + + // activate the output tab + m_buildUi.ktabwidget->setCurrentIndex(1); + m_displayModeBeforeBuild = m_buildUi.displayModeSlider->value(); + m_buildUi.displayModeSlider->setValue(0); + + mainWindow()->showToolView(m_toolView); + + // set working directory + m_make_dir = dir; + m_make_dir_stack.push(m_make_dir); + m_proc->setWorkingDirectory(m_make_dir.toLocalFile(KUrl::AddTrailingSlash)); + m_proc->setShellCommand(command); + m_proc->setOutputChannelMode(KProcess::SeparateChannels); + m_proc->start(); + + if(!m_proc->waitForStarted(500)) { + KMessageBox::error(0, i18n("Failed to run \"%1\". exitStatus = %2", command, m_proc->exitStatus())); + return false; + } + + m_buildUi.cancelBuildButton->setEnabled(true); + m_buildUi.cancelBuildButton2->setEnabled(true); + m_buildUi.buildAgainButton->setEnabled(false); + m_buildUi.buildAgainButton2->setEnabled(false); + + QApplication::setOverrideCursor(QCursor(Qt::BusyCursor)); + return true; +} + +/******************************************************************/ +bool KateBuildView::slotStop() +{ + if (m_proc->state() != QProcess::NotRunning) { + m_buildCancelled = true; + QString msg = i18n("Building %1 cancelled", m_currentlyBuildingTarget); + m_buildUi.buildStatusLabel->setText(msg); + m_buildUi.buildStatusLabel2->setText(msg); + m_proc->terminate(); + return true; + } + return false; +} + +/******************************************************************/ +void KateBuildView::slotSelectTarget() { + TargetSet* targetSet = currentTargetSet(); + if (targetSet == 0) { + return; + } + + SelectTargetDialog* dlg = new SelectTargetDialog(m_targetList, 0); + dlg->setTargetSet(targetSet->name); + + int result = dlg->exec(); + if (result == QDialog::Accepted) { + QString target = dlg->selectedTarget(); + buildTarget(target); + } + delete dlg; + dlg = 0; +} + + +/******************************************************************/ +KateBuildView::TargetSet* KateBuildView::currentTargetSet() +{ + if (m_targetIndex >= m_targetList.size()) { + return 0; + } + + return &m_targetList[m_targetIndex]; +} + + +/******************************************************************/ +bool KateBuildView::buildTarget(const QString& targetName) +{ + KUrl dir(docUrl()); // docUrl() saves the current document + + TargetSet* targetSet = currentTargetSet(); + + if (targetSet == 0) { + return false; + } + + std::map::const_iterator tgtIt = targetSet->targets.find(targetName); + if (tgtIt == targetSet->targets.end()) { + KMessageBox::sorry(0, i18n("Target \"%1\" not found for building.", targetName)); + return false; + } + + QString buildCmd = tgtIt->second; + + if (targetSet->defaultDir.isEmpty()) { + if (!checkLocal(dir)) { + return false; + } + // dir is a file -> remove the file with upUrl(). + dir = dir.upUrl(); + } + else { + dir = KUrl(targetSet->defaultDir); + } + + targetSet->prevTarget = targetName; + + // Check if the command contains the file name or directory + if (buildCmd.contains("%f") || buildCmd.contains("%d") || buildCmd.contains("%n")) { + KUrl docURL(docUrl()); + KUrl docDir = docURL.upUrl();// url is a file -> remove the file with upUrl() + + if (!checkLocal(docURL)) { + return false; + } + + buildCmd.replace("%n", QFileInfo(docURL.toLocalFile()).baseName()); + buildCmd.replace("%f", docURL.toLocalFile()); + buildCmd.replace("%d", docDir.toLocalFile()); + } + m_filenameDetectorGccWorked = false; + m_currentlyBuildingTarget = targetName; + m_buildCancelled = false; + QString msg = i18n("Building target %1 ...", m_currentlyBuildingTarget); + m_buildUi.buildStatusLabel->setText(msg); + m_buildUi.buildStatusLabel2->setText(msg); + return startProcess(dir, buildCmd); +} + + + +/******************************************************************/ +void KateBuildView::slotProcExited(int exitCode, QProcess::ExitStatus) +{ + QApplication::restoreOverrideCursor(); + m_buildUi.cancelBuildButton->setEnabled(false); + m_buildUi.cancelBuildButton2->setEnabled(false); + m_buildUi.buildAgainButton->setEnabled(true); + m_buildUi.buildAgainButton2->setEnabled(true); + + QString buildStatus = i18n("Building %1 completed.", m_currentlyBuildingTarget); + + // did we get any errors? + if (m_numErrors || m_numWarnings || (exitCode != 0)) { + m_buildUi.ktabwidget->setCurrentIndex(1); + if (m_buildUi.displayModeSlider->value() == 0) { + m_buildUi.displayModeSlider->setValue(m_displayModeBeforeBuild > 0 ? m_displayModeBeforeBuild: 1); + } + m_buildUi.errTreeWidget->resizeColumnToContents(0); + m_buildUi.errTreeWidget->resizeColumnToContents(1); + m_buildUi.errTreeWidget->resizeColumnToContents(2); + m_buildUi.errTreeWidget->horizontalScrollBar()->setValue(0); + //m_buildUi.errTreeWidget->setSortingEnabled(true); + m_win->showToolView(m_toolView); + } + + if (m_numErrors || m_numWarnings) { + QStringList msgs; + if (m_numErrors) { + msgs << i18np("Found one error.", "Found %1 errors.", m_numErrors); + buildStatus = i18n("Building %1 had errors.", m_currentlyBuildingTarget); + } + else if (m_numWarnings) { + msgs << i18np("Found one warning.", "Found %1 warnings.", m_numWarnings); + buildStatus = i18n("Building %1 had warnings.", m_currentlyBuildingTarget); + } + KPassivePopup::message(i18n("Make Results"), msgs.join("\n"), m_toolView); + } + else if (exitCode != 0) { + KPassivePopup::message(i18n("Make Results"), i18n("Build failed."), m_toolView); + } + else { + KPassivePopup::message(i18n("Make Results"), i18n("Build completed without problems."), m_toolView); + } + + if (!m_buildCancelled) { + m_buildUi.buildStatusLabel->setText(buildStatus); + m_buildUi.buildStatusLabel2->setText(buildStatus); + m_buildCancelled = false; + } + +} + + +/******************************************************************/ +void KateBuildView::slotReadReadyStdOut() +{ + // read data from procs stdout and add + // the text to the end of the output + // FIXME This works for utf8 but not for all charsets + QString l= QString::fromUtf8(m_proc->readAllStandardOutput()); + l.remove('\r'); + m_output_lines += l; + + QString tmp; + + int end=0; + + + // handle one line at a time + do { + end = m_output_lines.indexOf('\n'); + if (end < 0) break; + end++; + tmp = m_output_lines.mid(0, end); + tmp.remove('\n'); + m_buildUi.plainTextEdit->appendPlainText(tmp); + //kDebug() << tmp; + if (tmp.indexOf(m_newDirDetector) >=0) { + //kDebug() << "Enter/Exit dir found"; + int open = tmp.indexOf("`"); + int close = tmp.indexOf("'"); + KUrl newDir = KUrl(tmp.mid(open+1, close-open-1)); + kDebug () << "New dir = " << newDir; + + if ((m_make_dir_stack.size() > 1) && (m_make_dir_stack.top() == newDir)) { + m_make_dir_stack.pop(); + newDir = m_make_dir_stack.top(); + } + else { + m_make_dir_stack.push(newDir); + } + + m_make_dir = newDir; + } + + + m_output_lines.remove(0,end); + + } while (1); + +} + +/******************************************************************/ +void KateBuildView::slotReadReadyStdErr() +{ + // FIXME This works for utf8 but not for all charsets + QString l= QString::fromUtf8(m_proc->readAllStandardError()); + l.remove('\r'); + m_output_lines += l; + + QString tmp; + + int end=0; + + do { + end = m_output_lines.indexOf('\n'); + if (end < 0) break; + end++; + tmp = m_output_lines.mid(0, end); + tmp.remove('\n'); + m_buildUi.plainTextEdit->appendPlainText(tmp); + + processLine(tmp); + + m_output_lines.remove(0,end); + + } while (1); + +} + +/******************************************************************/ +void KateBuildView::processLine(const QString &line) +{ + //kDebug() << line ; + + //look for a filename + int index = m_filenameDetector.indexIn(line); + + QRegExp* rx = 0; + if (index >= 0) + { + m_filenameDetectorGccWorked = true; + rx = &m_filenameDetector; + } + else + { + if (!m_filenameDetectorGccWorked) + { + // let's see whether the icpc regexp works: + // so for icpc users error detection will be a bit slower, + // since always both regexps are checked. + // But this should be the minority, for gcc and clang users + // both regexes will only be checked until the first regex + // matched the first time. + index = m_filenameDetectorIcpc.indexIn(line); + if (index >= 0) + { + rx = &m_filenameDetectorIcpc; + } + } + } + + if (!rx) + { + addError(QString(), QString(), QString(), line); + //kDebug() << "A filename was not found in the line "; + return; + } + + QString filename = rx->cap(1); + QString line_n = rx->cap(3); + QString msg = rx->cap(4); + + //kDebug() << "File Name:"<buildDir->text()); + + if (m_targetsUi->buildDir->text().isEmpty()) { + // try current document dir + KTextEditor::View *kv = mainWindow()->activeView(); + if (kv != 0) { + defDir = kv->document()->url(); + } + } + + QString newDir = KFileDialog::getExistingDirectory(defDir, 0, QString()); + if (!newDir.isEmpty()) { + m_targetsUi->buildDir->setText(newDir); + } +} + +/******************************************************************/ +void KateBuildView::slotBuildDirChanged(const QString& dir) +{ + TargetSet* tgtSet = currentTargetSet(); + if (tgtSet == 0) { + return; + } + + tgtSet->defaultDir = dir; +} + +/******************************************************************/ +void KateBuildView::slotCellChanged(int row, int column) +{ + TargetSet* tgtSet = currentTargetSet(); + if (tgtSet == 0) { + return; + } + + bool wasBlocked = m_targetsUi->targetsList->blockSignals(true); + QTableWidgetItem* item = m_targetsUi->targetsList->item(row, column); + + QString prevTargetName = m_targetsUi->targetsList->item(row, COL_NAME)->text(); + if (column == COL_NAME) { + prevTargetName = m_prevItemContent; + } + + QString prevCommand = m_targetsUi->targetsList->item(row, COL_COMMAND)->text(); + + switch (column) + { + case COL_DEFAULT_TARGET: + case COL_CLEAN_TARGET: + for(int i=0; itargetsList->rowCount(); i++) { + m_targetsUi->targetsList->item(i, column)->setCheckState(Qt::Unchecked); + } + item->setCheckState(Qt::Checked); + if (column == COL_DEFAULT_TARGET) { + tgtSet->defaultTarget = prevTargetName; + } + else { + tgtSet->cleanTarget = prevTargetName; + } + break; + case COL_NAME: + { + QString newName = item->text(); + if (newName.isEmpty()) { + item->setText(prevTargetName); + } + else { + m_targetList[m_targetIndex].targets.erase(prevTargetName); + newName = makeTargetNameUnique(newName); + m_targetList[m_targetIndex].targets[newName] = prevCommand; + } + break; + } + case COL_COMMAND: + m_targetList[m_targetIndex].targets[prevTargetName] = item->text(); + break; + } + + m_targetsUi->targetsList->blockSignals(wasBlocked); +} + + +/******************************************************************/ +void KateBuildView::slotSelectionChanged() +{ + QList selectedItems = m_targetsUi->targetsList->selectedItems(); + bool enableButtons = (selectedItems.size() > 0); + if (enableButtons) { + m_prevItemContent = selectedItems.at(0)->text(); + } + m_targetsUi->deleteButton->setEnabled(enableButtons); + m_targetsUi->buildButton->setEnabled(enableButtons); +} + + +/******************************************************************/ +void KateBuildView::slotResizeColumn(int column) +{ + m_targetsUi->targetsList->resizeColumnToContents(column); +} + +/******************************************************************/ +QString KateBuildView::makeTargetNameUnique(const QString& name) +{ + TargetSet* targetSet = currentTargetSet(); + if (targetSet == 0) { + return name; + } + + QString uniqueName = name; + int count = 2; + + while (m_targetList[m_targetIndex].targets.find(uniqueName) != m_targetList[m_targetIndex].targets.end()) { + uniqueName = QString("%1_%2").arg(name).arg(count); + count++; + } + + return uniqueName; +} + + +/******************************************************************/ +void KateBuildView::slotAddTargetClicked() +{ + TargetSet* targetSet = currentTargetSet(); + if (targetSet == 0) { + return; + } + + bool wasBlocked = m_targetsUi->targetsList->blockSignals(true); + QString newName = makeTargetNameUnique(DefTargetName); + + int rowCount = m_targetList[m_targetIndex].targets.size(); + m_targetsUi->targetsList->setRowCount(rowCount + 1); + setTargetRowContents(rowCount, m_targetList[m_targetIndex], newName, DefBuildCmd); + + m_targetList[m_targetIndex].targets[newName] = DefBuildCmd; + + m_targetsUi->deleteButton->setEnabled(true); + m_targetsUi->buildButton->setEnabled(true); + m_targetsUi->targetsList->blockSignals(wasBlocked); +} + + +/******************************************************************/ +void KateBuildView::slotBuildTargetClicked() +{ + TargetSet* tgtSet = currentTargetSet(); + if (tgtSet == 0) { + return; + } + + QList selectedItems = m_targetsUi->targetsList->selectedItems(); + if (selectedItems.size() == 0) { + return; + } + + int row = selectedItems.at(0)->row(); + + QString target = m_targetsUi->targetsList->item(row, COL_NAME)->text(); + + buildTarget(target); +} + +/******************************************************************/ +void KateBuildView::slotDeleteTargetClicked() +{ + TargetSet* tgtSet = currentTargetSet(); + if (tgtSet == 0) { + return; + } + + QList selectedItems = m_targetsUi->targetsList->selectedItems(); + if (selectedItems.size() == 0) { + return; + } + + int row = selectedItems.at(0)->row(); + + QString target = m_targetsUi->targetsList->item(row, COL_NAME)->text(); + + int result = KMessageBox::questionYesNo(0, i18n("Really delete target %1?", target)); + if (result == KMessageBox::No) { + return; + } + + m_targetsUi->targetsList->removeRow(row); + + if (tgtSet->cleanTarget == target) { + tgtSet->cleanTarget = QString(""); + } + + if (tgtSet->defaultTarget == target) { + tgtSet->defaultTarget = QString(""); + } + + tgtSet->targets.erase(target); + + bool enableButtons = (m_targetsUi->targetsList->rowCount() > 0); + m_targetsUi->deleteButton->setEnabled(enableButtons); + m_targetsUi->buildButton->setEnabled(enableButtons); +} + + +/******************************************************************/ +void KateBuildView::targetSelected(int index) +{ + if (index >= m_targetList.size() || (index < 0)) { + kDebug() << "Invalid target"; + return; + } + + // Set the new values + bool wasBlocked = m_targetsUi->targetsList->blockSignals(true); + bool dirWasBlocked = m_targetsUi->buildDir->blockSignals(true); + + m_targetsUi->buildDir->setText(m_targetList[index].defaultDir); + + m_targetsUi->targetsList->setRowCount(m_targetList[index].targets.size()); + + int row=0; + for(std::map::const_iterator it = m_targetList[index].targets.begin(); it != m_targetList[index].targets.end(); ++it) { + setTargetRowContents(row, m_targetList[index], it->first, it->second); + row++; + } + + m_targetsUi->targetsList->blockSignals(wasBlocked); + m_targetsUi->buildDir->blockSignals(dirWasBlocked); + + m_targetsUi->targetsList->resizeColumnsToContents(); + + m_targetIndex = index; + + // make sure that both the combo box and the menu are updated + m_targetsUi->targetCombo->setCurrentIndex(index); + m_targetSelectAction->setCurrentItem(index); + + const bool enableButtons = (m_targetsUi->targetsList->currentItem() != 0); + + m_targetsUi->deleteButton->setEnabled(enableButtons); + m_targetsUi->buildButton->setEnabled(enableButtons); + + clearBuildResults(); + m_currentlyBuildingTarget.clear(); + m_buildUi.buildStatusLabel->setText(i18n("Nothing built yet.")); + m_buildUi.buildStatusLabel2->setText(i18n("Nothing built yet.")); +} + +/******************************************************************/ +void KateBuildView::setTargetRowContents(int row, const TargetSet& tgtSet, const QString& name, const QString& buildCmd) +{ + QTableWidgetItem* nameItem = new QTableWidgetItem(name); + QTableWidgetItem* cmdItem = new QTableWidgetItem(buildCmd); + QTableWidgetItem* def = new QTableWidgetItem(); + QTableWidgetItem* clean = new QTableWidgetItem(); + + def->setFlags(Qt::ItemIsEnabled | Qt::ItemIsUserCheckable | Qt::ItemIsSelectable); + clean->setFlags(Qt::ItemIsEnabled | Qt::ItemIsUserCheckable | Qt::ItemIsSelectable); + + def->setCheckState(name == tgtSet.defaultTarget ? Qt::Checked : Qt::Unchecked); + clean->setCheckState(name == tgtSet.cleanTarget ? Qt::Checked : Qt::Unchecked); + + m_targetsUi->targetsList->setItem(row, COL_DEFAULT_TARGET, def); + m_targetsUi->targetsList->setItem(row, COL_CLEAN_TARGET, clean); + m_targetsUi->targetsList->setItem(row, COL_NAME, nameItem); + m_targetsUi->targetsList->setItem(row, COL_COMMAND, cmdItem); +} + + +/******************************************************************/ +void KateBuildView::slotTargetSetNameChanged(const QString& name) +{ + TargetSet* tgtSet = currentTargetSet(); + if (tgtSet == 0) { + return; + } + + tgtSet->name = name; + targetsChanged(); +} + +/******************************************************************/ +void KateBuildView::targetsChanged() +{ + QStringList items; + + for( int i = 0; i < m_targetList.size(); ++i ) { + items.append( m_targetList[i].name ); + } + m_targetSelectAction->setItems( items ); + m_targetSelectAction->setCurrentItem( m_targetIndex ); +} + +/******************************************************************/ +QString KateBuildView::makeUniqueTargetSetName() const +{ + QString uniqueName; + + int count = 0; + bool nameAlreadyUsed = false; + do { + count++; + uniqueName = i18n("Target Set %1", count); + + nameAlreadyUsed = false; + for (int i=0; itargetCombo->addItem(m_targetList[m_targetIndex].name); + m_targetsUi->targetCombo->setCurrentIndex(m_targetIndex); + + // update the targets menu + targetsChanged(); +} + +/******************************************************************/ +void KateBuildView::targetCopy() +{ + TargetSet tgt = *currentTargetSet(); + m_targetList.append(tgt); + m_targetIndex = m_targetList.size()-1; + m_targetList[m_targetIndex].name = makeUniqueTargetSetName(); + + m_targetsUi->targetCombo->addItem(m_targetList[m_targetIndex].name); + m_targetsUi->targetCombo->setCurrentIndex(m_targetIndex); + + // update the targets menu + targetsChanged(); +} + +/******************************************************************/ +void KateBuildView::targetDelete() +{ + m_targetsUi->targetCombo->blockSignals(true); + + int newTargetIndex = 0; + + if (m_targetList.size() > 1) { + m_targetList.removeAt(m_targetIndex); + m_targetsUi->targetCombo->removeItem(m_targetIndex); + if (m_targetIndex > 0) { + newTargetIndex = m_targetIndex - 1; + } + + } + else { + m_targetsUi->targetCombo->clear(); + m_targetList.clear(); + + m_targetList.append(TargetSet()); + m_targetList[0].name = i18n("Target"); + m_targetList[0].defaultTarget = "Build"; + m_targetList[0].cleanTarget = "Clean"; + m_targetList[0].prevTarget.clear(); + m_targetList[0].defaultDir = QString(); + + m_targetList[0].targets["Build"] = DefBuildCmd; + m_targetList[0].targets["Clean"] = DefCleanCmd; + m_targetList[0].targets["Config"] = DefConfigCmd; + m_targetList[0].targets["ConfigClean"] = DefConfClean; + + m_targetsUi->targetCombo->addItem(m_targetList[0].name); + + } + + m_targetsUi->targetCombo->blockSignals(false); + + targetSelected(newTargetIndex); + + // update the targets menu + targetsChanged(); +} + +/******************************************************************/ +void KateBuildView::targetNext() +{ + if (m_toolView->isVisible() && m_buildUi.ktabwidget->currentIndex() == 0) { + int index = m_targetsUi->targetCombo->currentIndex(); + index++; + if (index == m_targetsUi->targetCombo->count()) index = 0; + + m_targetsUi->targetCombo->setCurrentIndex(index); + } + else { + m_win->showToolView(m_toolView); + m_buildUi.ktabwidget->setCurrentIndex(0); + } +} + +/******************************************************************/ +bool KateBuildView::eventFilter(QObject *obj, QEvent *event) +{ + if (event->type() == QEvent::KeyPress) { + QKeyEvent *ke = static_cast(event); + if ((obj == m_toolView) && (ke->key() == Qt::Key_Escape)) { + mainWindow()->hideToolView(m_toolView); + event->accept(); + return true; + } + } + if ((event->type() == QEvent::Resize) && (obj == m_buildWidget)) { + if (m_buildUi.ktabwidget->currentIndex() == 1) { + if ((m_outputWidgetWidth == 0) && m_buildUi.buildAgainButton->isVisible()) { + QSize msh = m_buildWidget->minimumSizeHint(); + m_outputWidgetWidth = msh.width(); + } + } + bool useVertLayout = (m_buildWidget->width() < m_outputWidgetWidth); + m_buildUi.buildAgainButton->setVisible(!useVertLayout); + m_buildUi.cancelBuildButton->setVisible(!useVertLayout); + m_buildUi.buildStatusLabel->setVisible(!useVertLayout); + m_buildUi.buildAgainButton2->setVisible(useVertLayout); + m_buildUi.cancelBuildButton2->setVisible(useVertLayout); + m_buildUi.buildStatusLabel2->setVisible(useVertLayout); + } + + return QObject::eventFilter(obj, event); +} + +/******************************************************************/ +void KateBuildView::handleEsc(QEvent *e) +{ + if (!mainWindow()) return; + + QKeyEvent *k = static_cast(e); + if (k->key() == Qt::Key_Escape && k->modifiers() == Qt::NoModifier) { + if (m_toolView->isVisible()) { + mainWindow()->hideToolView(m_toolView); + } + } +} + + +/******************************************************************/ +void KateBuildView::slotDisplayMode(int mode) { + QTreeWidget *tree=m_buildUi.errTreeWidget; + tree->setVisible(mode != 0); + m_buildUi.plainTextEdit->setVisible(mode == 0); + + QString modeText; + switch(mode) + { + case 3: + modeText = i18n("Only Errors"); + break; + case 2: + modeText = i18n("Errors and Warnings"); + break; + case 1: + modeText = i18n("Parsed Output"); + break; + case 0: + modeText = i18n("Full Output"); + break; + } + m_buildUi.displayModeLabel->setText(modeText); + + if (mode < 1) { + return; + } + + const int itemCount = tree->topLevelItemCount(); + + for (int i=0;itopLevelItem(i); + + if ( (item->data(0,Qt::UserRole+1).toBool()==false) && (item->data(0,Qt::UserRole+2).toBool()==false) ) { + item->setHidden(mode > 1); + } + + if (item->data(0,Qt::UserRole+2).toBool()==true) { + item->setHidden(mode > 2); + } + + if (item->data(0,Qt::UserRole+1).toBool()==true) { + item->setHidden(false); + } + } +} + +/******************************************************************/ +void KateBuildView::slotPluginViewCreated (const QString &name, Kate::PluginView *pluginView) +{ + // add view + if (name == "kateprojectplugin") { + m_projectPluginView = pluginView; + slotProjectMapChanged (); + connect (pluginView, SIGNAL(projectMapChanged()), this, SLOT(slotProjectMapChanged())); + } +} + +/******************************************************************/ +void KateBuildView::slotPluginViewDeleted (const QString &name, Kate::PluginView *) +{ + // remove view + if (name == "kateprojectplugin") { + m_projectPluginView = 0; + slotRemoveProjectTarget(); + } +} + +/******************************************************************/ +void KateBuildView::slotProjectMapChanged () +{ + // only do stuff with valid project + if (!m_projectPluginView) { + return; + } + slotRemoveProjectTarget(); + slotAddProjectTarget(); + +} + +/******************************************************************/ +void KateBuildView::slotAddProjectTarget() +{ + // query new project map + QVariantMap projectMap = m_projectPluginView->property("projectMap").toMap(); + + // do we have a valid map for build settings? + QVariantMap buildMap = projectMap.value("build").toMap(); + if (buildMap.isEmpty()) { + return; + } + + int index = m_targetList.size(); + m_targetList.append(TargetSet()); + m_targetList[index].name = i18n("Project Plugin Targets"); + m_targetList[index].cleanTarget = buildMap.value("clean_target").toString(); + m_targetList[index].defaultTarget = buildMap.value("default_target").toString(); + m_targetList[index].prevTarget.clear(); + m_targetList[index].defaultDir = buildMap.value("directory").toString(); + + // get build dir + QString defaultBuildDir = buildMap.value("directory").toString(); + + QVariantList targets = buildMap.value("targets").toList(); + foreach (const QVariant &targetVariant, targets) { + QVariantMap targetMap = targetVariant.toMap(); + QString tgtName = targetMap["name"].toString(); + QString buildCmd = targetMap["build_cmd"].toString(); + + if (tgtName.isEmpty() || buildCmd.isEmpty()) { + continue; + } + + m_targetList[index].targets[tgtName] = buildCmd; + } + + if (targets.empty()) { + // support "old" project files + QString buildCmd = buildMap.value("build").toString(); + QString cleanCmd = buildMap.value("clean").toString(); + QString quickCmd = buildMap.value("quick").toString(); + if (!buildCmd.isEmpty()) { + // we have loaded an "old" project file (<= 4.12) + m_targetList[index].cleanTarget = "clean"; + m_targetList[index].defaultTarget = "build"; + m_targetList[index].prevTarget.clear(); + m_targetList[index].targets["build"] = buildCmd; + m_targetList[index].targets["clean"] = cleanCmd; + m_targetList[index].targets["quick"] = quickCmd; + } + } + + m_targetsUi->targetCombo->addItem(m_targetList[index].name); + + targetSelected(index); + + + // set build dir + + // update the targets menu + targetsChanged(); +} + +/******************************************************************/ +void KateBuildView::slotRemoveProjectTarget() +{ + int i; + for (i=0; i= m_targetList.size()) { + // not found + return; + } + + targetSelected(i); + targetDelete(); +} + + +// kate: space-indent on; indent-width 4; replace-tabs on; diff --git a/kate/addons/kate/katebuild-plugin/plugin_katebuild.h b/kate/addons/kate/katebuild-plugin/plugin_katebuild.h new file mode 100644 index 00000000..1b0782c4 --- /dev/null +++ b/kate/addons/kate/katebuild-plugin/plugin_katebuild.h @@ -0,0 +1,189 @@ +#ifndef PLUGIN_KATEBUILD_H +#define PLUGIN_KATEBUILD_H +/* plugin_katebuild.h Kate Plugin +** +** Copyright (C) 2008 by Kåre Särs +** +** This code is almost a total rewrite of the GPL'ed Make plugin +** by Adriaan de Groot. +*/ + +/* +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program in a file called COPYING; if not, write to +** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +** MA 02110-1301, USA. +*/ + +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include + +#include "ui_build.h" +#include "targets.h" + +/******************************************************************/ +class KateBuildView : public Kate::PluginView, public Kate::XMLGUIClient +{ + Q_OBJECT + + public: + + struct TargetSet { + QString name; + QString defaultDir; + QString defaultTarget; + QString cleanTarget; + QString prevTarget; + std::map targets; + }; + + KateBuildView(Kate::MainWindow *mw); + ~KateBuildView(); + + // overwritten: read and write session config + void readSessionConfig(KConfigBase* config, const QString& groupPrefix); + void writeSessionConfig(KConfigBase* config, const QString& groupPrefix); + + QWidget *toolView() const; + + TargetSet* currentTargetSet(); + + bool buildTarget(const QString& targetName); + + private Q_SLOTS: + // selecting warnings + void slotItemSelected(QTreeWidgetItem *item); + void slotNext(); + void slotPrev(); + + bool slotMake(); + bool slotMakeClean(); + bool slotStop(); + + void slotProcExited(int exitCode, QProcess::ExitStatus exitStatus); + void slotReadReadyStdErr(); + void slotReadReadyStdOut(); + + void slotSelectTarget(); + void slotBuildPreviousTarget(); + + // settings + void slotBrowseClicked(); + void targetSelected(int index); + void targetsChanged(); + void targetNew(); + void targetCopy(); + void targetDelete(); + void targetNext(); + void slotBuildDirChanged(const QString& dir); + void slotTargetSetNameChanged(const QString& name); + + void slotDisplayMode(int mode); + + void handleEsc(QEvent *e); + + /** + * keep track if the project plugin is alive and if the project map did change + */ + void slotPluginViewCreated(const QString &name, Kate::PluginView *pluginView); + void slotPluginViewDeleted(const QString &name, Kate::PluginView *pluginView); + void slotProjectMapChanged(); + void slotAddProjectTarget(); + void slotRemoveProjectTarget(); + + void slotAddTargetClicked(); + void slotBuildTargetClicked(); + void slotDeleteTargetClicked(); + void slotCellChanged(int row, int column); + void slotSelectionChanged(); + void slotResizeColumn(int column); + + protected: + bool eventFilter(QObject *obj, QEvent *ev); + + private: + void processLine(const QString &); + void addError(const QString &filename, const QString &line, + const QString &column, const QString &message); + bool startProcess(const KUrl &dir, const QString &command); + KUrl docUrl(); + bool checkLocal(const KUrl &dir); + void setTargetRowContents(int row, const TargetSet& tgtSet, const QString& name, const QString& buildCmd); + QString makeTargetNameUnique(const QString& name); + QString makeUniqueTargetSetName() const; + void clearBuildResults(); + + Kate::MainWindow *m_win; + QWidget *m_toolView; + Ui::build m_buildUi; + QWidget *m_buildWidget; + int m_outputWidgetWidth; + TargetsUi *m_targetsUi; + KProcess *m_proc; + QString m_output_lines; + QString m_currentlyBuildingTarget; + bool m_buildCancelled; + int m_displayModeBeforeBuild; + KUrl m_make_dir; + QStack m_make_dir_stack; + QRegExp m_filenameDetector; + QRegExp m_filenameDetectorIcpc; + bool m_filenameDetectorGccWorked; + QRegExp m_newDirDetector; + unsigned int m_numErrors; + unsigned int m_numWarnings; + QList m_targetList; + int m_targetIndex; + KSelectAction* m_targetSelectAction; + QString m_prevItemContent; + + /** + * current project plugin view, if any + */ + Kate::PluginView *m_projectPluginView; +}; + + +typedef QList VariantList; + +/******************************************************************/ +class KateBuildPlugin : public Kate::Plugin +{ + Q_OBJECT + + public: + explicit KateBuildPlugin(QObject* parent = 0, const VariantList& = VariantList()); + virtual ~KateBuildPlugin() {} + + Kate::PluginView *createView(Kate::MainWindow *mainWindow); + + }; + +#endif + diff --git a/kate/addons/kate/katebuild-plugin/selecttargetdialog.cpp b/kate/addons/kate/katebuild-plugin/selecttargetdialog.cpp new file mode 100644 index 00000000..60282ca5 --- /dev/null +++ b/kate/addons/kate/katebuild-plugin/selecttargetdialog.cpp @@ -0,0 +1,199 @@ +// Description: dialog for selecting a target to build +// +// Copyright (c) 2013 Alexander Neundorf +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Library General Public +// License version 2 as published by the Free Software Foundation. +// +// This library 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 +// Library General Public License for more details. +// +// You should have received a copy of the GNU Library General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#include "selecttargetdialog.h" + +#include + +#include +#include +#include +#include +#include +#include + + +SelectTargetDialog::SelectTargetDialog(QList& targetSets, QWidget* parent) +:KDialog(parent) +,m_currentTargetSet(0) +,m_targetName(0) +,m_targetsList(0) +,m_command(0) +,m_targetSets(targetSets) +,m_targets(0) +{ + setButtons( KDialog::Ok | KDialog::Cancel); + + QWidget* container = new QWidget(); + + QLabel* filterLabel = new QLabel(i18n("Target:")); + m_targetName = new QLineEdit(); + m_targetsList = new QListWidget(); + + QLabel* setLabel = new QLabel(i18n("from")); + m_currentTargetSet = new QComboBox(); + for (int i=0; iaddItem(m_targetSets.at(i).name); + } + + QLabel* commandLabel = new QLabel(i18n("Command:")); + m_command = new QLabel(); + + QHBoxLayout* filterLayout = new QHBoxLayout(); + filterLayout->addWidget(filterLabel); + filterLayout->addWidget(m_targetName); + filterLayout->addWidget(setLabel); + filterLayout->addWidget(m_currentTargetSet); + + QHBoxLayout* commandLayout = new QHBoxLayout(); + commandLayout->addWidget(commandLabel); + commandLayout->addWidget(m_command); + commandLayout->setAlignment(Qt::AlignLeft); + + QVBoxLayout* mainLayout = new QVBoxLayout(this); + + mainLayout->addLayout(filterLayout); + mainLayout->addWidget(m_targetsList); + mainLayout->addLayout(commandLayout); + + container->setLayout(mainLayout); + + this->setMainWidget(container); + + connect(m_currentTargetSet, SIGNAL(currentIndexChanged(int)), this, SLOT(slotTargetSetSelected(int))); + connect(m_targetName, SIGNAL(textEdited(const QString&)), this, SLOT(slotFilterTargets(const QString&))); + connect(m_targetsList, SIGNAL(itemActivated(QListWidgetItem*)), this, SLOT(accept())); + connect(m_targetsList, SIGNAL(currentItemChanged(QListWidgetItem*, QListWidgetItem*)), this, SLOT(slotCurrentItemChanged(QListWidgetItem*))); + m_targetName->installEventFilter(this); + m_targetsList->installEventFilter(this); + + this->setFocusProxy(m_targetName); + m_targetName->setFocus(); +} + + +void SelectTargetDialog::slotTargetSetSelected(int index) +{ + setTargetSet(m_targetSets.at(index).name); +} + + +void SelectTargetDialog::setTargetSet(const QString& name) +{ + m_targets = NULL; + m_allTargets.clear(); + m_targetsList->clear(); + m_command->setText(""); + m_targetName->clear(); + + for (int i=0; isetCurrentIndex(i); + setTargets(m_targetSets.at(i).targets); + return; + } + } +} + + +void SelectTargetDialog::slotFilterTargets(const QString& filter) +{ + QStringList filteredTargets; + if (filter.isEmpty()) { + filteredTargets = m_allTargets; + } + else { + filteredTargets = m_allTargets.filter(filter, Qt::CaseInsensitive); + } + m_targetsList->clear(); + m_targetsList->addItems(filteredTargets); + if (filteredTargets.size() > 0) { + m_targetsList->item(0)->setSelected(true); + m_targetsList->setCurrentItem(m_targetsList->item(0)); + } +} + + +void SelectTargetDialog::setTargets(const std::map& targets) +{ + m_targets = &targets; + m_allTargets.clear(); + + for(std::map::const_iterator tgtIt = targets.begin(); tgtIt != targets.end(); ++tgtIt) { + m_allTargets << tgtIt->first; + } + + slotFilterTargets(QString()); +} + + +void SelectTargetDialog::slotCurrentItemChanged(QListWidgetItem* currentItem) +{ + QString command; + + if (currentItem && m_targets) { + std::map::const_iterator tgtIt = m_targets->find(currentItem->text()); + if (tgtIt != m_targets->end()) { + command = tgtIt->second; + } + } + + m_command->setText(command); +} + + +QString SelectTargetDialog::selectedTarget() const +{ + if (m_targetsList->currentItem() == 0) { + return m_targetName->text(); + } + return m_targetsList->currentItem()->text(); +} + + +bool SelectTargetDialog::eventFilter(QObject *obj, QEvent *event) +{ + if (event->type()==QEvent::KeyPress) { + QKeyEvent *keyEvent=static_cast(event); + if (obj==m_targetName) { + const bool forward2list = (keyEvent->key()==Qt::Key_Up) + || (keyEvent->key()==Qt::Key_Down) + || (keyEvent->key()==Qt::Key_PageUp) + || (keyEvent->key()==Qt::Key_PageDown); + if (forward2list) { + QCoreApplication::sendEvent(m_targetsList,event); + return true; + } + } + else { + const bool forward2input = (keyEvent->key()!=Qt::Key_Up) + && (keyEvent->key()!=Qt::Key_Down) + && (keyEvent->key()!=Qt::Key_PageUp) + && (keyEvent->key()!=Qt::Key_PageDown) + && (keyEvent->key()!=Qt::Key_Tab) + && (keyEvent->key()!=Qt::Key_Backtab); + if (forward2input) { + QCoreApplication::sendEvent(m_targetName,event); + return true; + } + } + } + return KDialog::eventFilter(obj, event); +} + +// kate: space-indent on; indent-width 4; replace-tabs on; diff --git a/kate/addons/kate/katebuild-plugin/selecttargetdialog.h b/kate/addons/kate/katebuild-plugin/selecttargetdialog.h new file mode 100644 index 00000000..29aa3fd6 --- /dev/null +++ b/kate/addons/kate/katebuild-plugin/selecttargetdialog.h @@ -0,0 +1,71 @@ +#ifndef SELECT_TARGET_DIALOG_H +#define SELECT_TARGET_DIALOG_H + +// Description: dialog for selecting a target to build +// +// Copyright (c) 2013 Alexander Neundorf +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Library General Public +// License version 2 as published by the Free Software Foundation. +// +// This library 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 +// Library General Public License for more details. +// +// You should have received a copy of the GNU Library General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + + +#include + +#include "plugin_katebuild.h" + +#include + +#include + +#include +#include +#include +#include +#include + + +class SelectTargetDialog : public KDialog +{ + Q_OBJECT + public: + SelectTargetDialog(QList& targetSets, QWidget* parent); + void setTargetSet(const QString& name); + + QString selectedTarget() const; + + protected: + virtual bool eventFilter(QObject *obj, QEvent *event); + + private slots: + void slotFilterTargets(const QString& filter); + void slotCurrentItemChanged(QListWidgetItem* currentItem); + void slotTargetSetSelected(int index); + + private: + void setTargets(const std::map& _targets); + + QStringList m_allTargets; + + QComboBox* m_currentTargetSet; + QLineEdit* m_targetName; + QListWidget* m_targetsList; + QLabel* m_command; + + QList& m_targetSets; + const std::map* m_targets; +}; + +#endif + +// kate: space-indent on; indent-width 4; replace-tabs on; diff --git a/kate/addons/kate/katebuild-plugin/targets.cpp b/kate/addons/kate/katebuild-plugin/targets.cpp new file mode 100644 index 00000000..8656c39a --- /dev/null +++ b/kate/addons/kate/katebuild-plugin/targets.cpp @@ -0,0 +1,174 @@ +// +// Description: Widget for configuring build targets +// +// Copyright (c) 2011 Kåre Särs +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Library General Public +// License version 2 as published by the Free Software Foundation. +// +// This library 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 +// Library General Public License for more details. +// +// You should have received a copy of the GNU Library General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#include "targets.h" +#include "moc_targets.cpp" +#include +#include +#include +#include + +TargetsUi::TargetsUi(QWidget *parent): +QWidget(parent) +{ + + targetLabel = new QLabel(i18n("Target set"), this); + + targetCombo = new KComboBox(this); + targetCombo->setEditable(true); + targetCombo->setInsertPolicy(QComboBox::InsertAtCurrent); + connect(targetCombo, SIGNAL(editTextChanged(QString)), this, SLOT(editTarget(QString))); + targetLabel->setBuddy(targetCombo); + + newTarget = new QToolButton(this); + newTarget->setToolTip(i18n("Create new set of targets")); + newTarget->setIcon(KIcon("document-new")); + + copyTarget = new QToolButton(this); + copyTarget->setToolTip(i18n("Copy set of targets")); + copyTarget->setIcon(KIcon("edit-copy")); + + deleteTarget = new QToolButton(this); + deleteTarget->setToolTip(i18n("Delete current set of targets")); + deleteTarget->setIcon(KIcon("edit-delete")); + + dirLabel = new QLabel(i18n("Working directory"), this); + buildDir = new KLineEdit(this); + buildDir->setToolTip(i18n("Leave empty to use the directory of the current document. ")); + buildDir->setClearButtonShown(true); + browse = new QToolButton(this); + browse->setIcon(KIcon("inode-directory")); + +// quickCmd->setToolTip(i18n("Use:\n\"%f\" for current file\n\"%d\" for directory of current file\n\"%n\" for current file name without suffix")); + + dirLabel->setBuddy(buildDir); + + targetsList = new QTableWidget(0, 4, this); + targetsList->setAlternatingRowColors(true); + targetsList->setWordWrap(false); + targetsList->setShowGrid(false); + targetsList->setSelectionMode(QAbstractItemView::SingleSelection); + targetsList->setSelectionBehavior(QAbstractItemView::SelectItems); //SelectRows); + QStringList headerLabels; + headerLabels << QString("Def") << QString("Clean") << QString("Name") << QString("Command"); + targetsList->setHorizontalHeaderLabels(headerLabels); + targetsList->verticalHeader()->setVisible(false); + + addButton = new QToolButton(this); + addButton->setIcon(KIcon("list-add")); + addButton->setToolTip(i18n("Add new target")); + + deleteButton = new QToolButton(this); + deleteButton->setIcon(KIcon("list-remove")); + deleteButton->setToolTip(i18n("Delete selected target")); + + buildButton = new QToolButton(this); + buildButton->setIcon(KIcon("dialog-ok")); + buildButton->setToolTip(i18n("Build selected target")); + + // calculate the approximate height to exceed before going to "Side Layout" + setSideLayout(); + m_widgetsHeight = sizeHint().height(); + delete layout(); + + setBottomLayout(); + m_useBottomLayout = true; +} + +void TargetsUi::resizeEvent(QResizeEvent *) +{ + // check if the widgets fit in a VBox layout + if (m_useBottomLayout && (size().height() > m_widgetsHeight) && (size().width() < m_widgetsHeight * 2.5)) + { + delete layout(); + setSideLayout(); + m_useBottomLayout = false; + } + else if (!m_useBottomLayout && ((size().height() < m_widgetsHeight) || (size().width() > m_widgetsHeight * 2.5))) + { + delete layout(); + setBottomLayout(); + m_useBottomLayout = true; + } +} + +void TargetsUi::setSideLayout() +{ + QHBoxLayout* tLayout = new QHBoxLayout(); + tLayout->addWidget(targetCombo, 1); + tLayout->addWidget(newTarget, 0); + tLayout->addWidget(copyTarget, 0); + tLayout->addWidget(deleteTarget, 0); + tLayout->setContentsMargins(0,0,0,0); + + QHBoxLayout* buttonsLayout = new QHBoxLayout(); + buttonsLayout->addStretch(); + buttonsLayout->addWidget(addButton); + buttonsLayout->addWidget(deleteButton); + buttonsLayout->addWidget(buildButton); + + QGridLayout* layout = new QGridLayout(this); + layout->addWidget(targetLabel, 0, 0, 1, 4); + layout->addLayout(tLayout, 1, 0, 1, 4); + + layout->addWidget(dirLabel, 2, 0, Qt::AlignLeft); + layout->addWidget(buildDir, 3, 0, 1, 3); + layout->addWidget(browse, 3, 3); + + layout->addWidget(targetsList, 4, 0, 1, 4); + layout->addLayout(buttonsLayout, 5, 0, 1, 4); + + layout->addItem(new QSpacerItem(1, 1), 8, 0); + layout->setColumnStretch(0, 1); + layout->setRowStretch(4, 1); +} + +void TargetsUi::setBottomLayout() +{ + QHBoxLayout* tLayout = new QHBoxLayout(); + tLayout->addWidget(targetLabel); + tLayout->addWidget(targetCombo, 1); + tLayout->addWidget(newTarget, 0); + tLayout->addWidget(copyTarget, 0); + tLayout->addWidget(deleteTarget, 0); + tLayout->setContentsMargins(0,0,0,0); + + QHBoxLayout* buttonsLayout = new QHBoxLayout(); + buttonsLayout->addWidget(dirLabel); + buttonsLayout->addWidget(buildDir); + buttonsLayout->addWidget(browse); + + buttonsLayout->addStretch(); + buttonsLayout->addWidget(addButton); + buttonsLayout->addWidget(deleteButton); + buttonsLayout->addWidget(buildButton); + + QVBoxLayout* layout = new QVBoxLayout(this); + layout->addLayout(tLayout); + layout->addWidget(targetsList); + layout->addLayout(buttonsLayout); +} + +void TargetsUi::editTarget(const QString &text) +{ + int curPos = targetCombo->lineEdit()->cursorPosition(); + targetCombo->setItemText(targetCombo->currentIndex(), text); + targetCombo->lineEdit()->setCursorPosition(curPos); +} + diff --git a/kate/addons/kate/katebuild-plugin/targets.h b/kate/addons/kate/katebuild-plugin/targets.h new file mode 100644 index 00000000..27b3db28 --- /dev/null +++ b/kate/addons/kate/katebuild-plugin/targets.h @@ -0,0 +1,60 @@ + +#ifndef TARGETS_H +#define TARGETS_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include "klineedit.h" +#include "kpushbutton.h" +#include + + +#define COL_DEFAULT_TARGET 0 +#define COL_CLEAN_TARGET 1 +#define COL_NAME 2 +#define COL_COMMAND 3 + + +class TargetsUi: public QWidget +{ + Q_OBJECT + +public: + TargetsUi(QWidget *parent = 0); + + QLabel *targetLabel; + QComboBox *targetCombo; + QToolButton *newTarget; + QToolButton *copyTarget; + QToolButton *deleteTarget; + + QLabel *dirLabel; + KLineEdit *buildDir; + QToolButton *browse; + QTableWidget *targetsList; + + QToolButton *addButton; + QToolButton *deleteButton; + QToolButton *buildButton; + +protected: + void resizeEvent(QResizeEvent *event); + +private Q_SLOTS: + void editTarget(const QString &text); + +private: + void setBottomLayout(); + void setSideLayout(); + + int m_widgetsHeight; + bool m_useBottomLayout; +}; + +#endif diff --git a/kate/addons/kate/katebuild-plugin/test/Makefile b/kate/addons/kate/katebuild-plugin/test/Makefile new file mode 100644 index 00000000..5af02bb6 --- /dev/null +++ b/kate/addons/kate/katebuild-plugin/test/Makefile @@ -0,0 +1,18 @@ +all: test + +main.o: main.c + gcc -Wall -g -Isubdir -c -o main.o main.c + +enter_exit_subdir.o: subdir/enter_exit_subdir.c subdir/enter_exit_subdir.h + cd ./subdir/ && make + mv ./subdir/*.o ./ + +incl_from.o: incl_from.c incl_from.h incl_from_from.h + gcc -Wall -g -c -o incl_from.o incl_from.c + +test: clean enter_exit_subdir.o incl_from.o main.o + gcc -Wall -g main.o incl_from.o -o test + + +clean: + rm -rf *.o test \ No newline at end of file diff --git a/kate/addons/kate/katebuild-plugin/test/incl_from.c b/kate/addons/kate/katebuild-plugin/test/incl_from.c new file mode 100644 index 00000000..706bda38 --- /dev/null +++ b/kate/addons/kate/katebuild-plugin/test/incl_from.c @@ -0,0 +1,9 @@ +#include +#include "incl_from.h" + +int incl_from(void) +{ + printf("incl_from\n"); + return 0; +} + diff --git a/kate/addons/kate/katebuild-plugin/test/incl_from.h b/kate/addons/kate/katebuild-plugin/test/incl_from.h new file mode 100644 index 00000000..628ea2a4 --- /dev/null +++ b/kate/addons/kate/katebuild-plugin/test/incl_from.h @@ -0,0 +1,6 @@ +#ifndef INCL_FROM_H +#define INCL_FROM_H +//krazy:skip +#include "incl_from_from.h" + +#endif diff --git a/kate/addons/kate/katebuild-plugin/test/incl_from_from.h b/kate/addons/kate/katebuild-plugin/test/incl_from_from.h new file mode 100644 index 00000000..13ebe148 --- /dev/null +++ b/kate/addons/kate/katebuild-plugin/test/incl_from_from.h @@ -0,0 +1,6 @@ +#ifndef INCL_FROM_FROM_H +#define INCL_FROM_FROM_H +//krazy:skip +int incl_from_from(void); +#warning this is a warning +#endif diff --git a/kate/addons/kate/katebuild-plugin/test/main.c b/kate/addons/kate/katebuild-plugin/test/main.c new file mode 100644 index 00000000..3d840777 --- /dev/null +++ b/kate/addons/kate/katebuild-plugin/test/main.c @@ -0,0 +1,15 @@ +#include "incl_from_from.h" + +int main(void) +{ + test(); + + printf("test\n"); + return 0; +} + +int incl_from(void) +{ + printf("incl_from\n"); + return 0; +} diff --git a/kate/addons/kate/katebuild-plugin/test/subdir/Makefile b/kate/addons/kate/katebuild-plugin/test/subdir/Makefile new file mode 100644 index 00000000..84d901b3 --- /dev/null +++ b/kate/addons/kate/katebuild-plugin/test/subdir/Makefile @@ -0,0 +1,3 @@ + +enter_exit_subdir.o: enter_exit_subdir.c enter_exit_subdir.h + gcc -Wall -g -c -o enter_exit_subdir.o enter_exit_subdir.c diff --git a/kate/addons/kate/katebuild-plugin/test/subdir/enter_exit_subdir.c b/kate/addons/kate/katebuild-plugin/test/subdir/enter_exit_subdir.c new file mode 100644 index 00000000..a452da95 --- /dev/null +++ b/kate/addons/kate/katebuild-plugin/test/subdir/enter_exit_subdir.c @@ -0,0 +1,6 @@ +int enter_exit_subdir(void) +{ + printf("enter_exit_subdir\n"); + return 0; +} + diff --git a/kate/addons/kate/katebuild-plugin/test/subdir/enter_exit_subdir.h b/kate/addons/kate/katebuild-plugin/test/subdir/enter_exit_subdir.h new file mode 100644 index 00000000..705a1936 --- /dev/null +++ b/kate/addons/kate/katebuild-plugin/test/subdir/enter_exit_subdir.h @@ -0,0 +1,6 @@ +#ifndef ENTER_EXIT_SUBDIR_H +#define ENTER_EXIT_SUBDIR_H + +int enter_exit_subdir(void); + +#endif diff --git a/kate/addons/kate/katebuild-plugin/ui.rc b/kate/addons/kate/katebuild-plugin/ui.rc new file mode 100644 index 00000000..0d953755 --- /dev/null +++ b/kate/addons/kate/katebuild-plugin/ui.rc @@ -0,0 +1,18 @@ + + + + &Build + + + + + + + + + + + + + + diff --git a/kate/addons/kate/konsole/CMakeLists.txt b/kate/addons/kate/konsole/CMakeLists.txt new file mode 100644 index 00000000..020b7cdd --- /dev/null +++ b/kate/addons/kate/konsole/CMakeLists.txt @@ -0,0 +1,26 @@ +########### next target ############### + +kde4_add_plugin(katekonsoleplugin kateconsole.cpp) + +target_link_libraries(katekonsoleplugin + ${KDE4_KDEUI_LIBS} + ${KDE4_KPARTS_LIBS} + kateinterfaces +) + +install( + TARGETS katekonsoleplugin + DESTINATION ${KDE4_PLUGIN_INSTALL_DIR} +) + + +########### install files ############### + +install( + FILES ui.rc + DESTINATION ${KDE4_DATA_INSTALL_DIR}/kate/plugins/katekonsole +) +install( + FILES katekonsoleplugin.desktop + DESTINATION ${KDE4_SERVICES_INSTALL_DIR} +) diff --git a/kate/addons/kate/konsole/Messages.sh b/kate/addons/kate/konsole/Messages.sh new file mode 100644 index 00000000..6fb1ba1e --- /dev/null +++ b/kate/addons/kate/konsole/Messages.sh @@ -0,0 +1,3 @@ +#! /bin/sh +$EXTRACTRC *.rc >> rc.cpp +$XGETTEXT *.cpp -o $podir/katekonsoleplugin.pot diff --git a/kate/addons/kate/konsole/kateconsole.cpp b/kate/addons/kate/konsole/kateconsole.cpp new file mode 100644 index 00000000..6fc49261 --- /dev/null +++ b/kate/addons/kate/konsole/kateconsole.cpp @@ -0,0 +1,360 @@ +/* This file is part of the KDE project + Copyright (C) 2001 Christoph Cullmann + Copyright (C) 2002 Joseph Wenninger + Copyright (C) 2002 Anders Lund + Copyright (C) 2007 Anders Lund + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "kateconsole.h" +#include "moc_kateconsole.cpp" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +K_PLUGIN_FACTORY(KateKonsoleFactory, registerPlugin();) +K_EXPORT_PLUGIN(KateKonsoleFactory(KAboutData("katekonsole","katekonsoleplugin",ki18n("Konsole"), "0.1", ki18n("Embedded Konsole"), KAboutData::License_LGPL_V2)) ) + +KateKonsolePlugin::KateKonsolePlugin( QObject* parent, const QList& ): + Kate::Plugin ( (Kate::Application*)parent ) +{ + m_previousEditorEnv=qgetenv("EDITOR"); +} + +KateKonsolePlugin::~KateKonsolePlugin() +{ + ::setenv( "EDITOR", m_previousEditorEnv.data(), 1 ); +} + +Kate::PluginView *KateKonsolePlugin::createView (Kate::MainWindow *mainWindow) +{ + KateKonsolePluginView *view = new KateKonsolePluginView (this, mainWindow); + return view; +} + +Kate::PluginConfigPage *KateKonsolePlugin::configPage (uint number, QWidget *parent, const char *name) +{ + Q_UNUSED(name) + if (number != 0) + return 0; + return new KateKonsoleConfigPage(parent, this); +} + +QString KateKonsolePlugin::configPageName (uint number) const +{ + if (number != 0) return QString(); + return i18n("Terminal"); +} + +QString KateKonsolePlugin::configPageFullName (uint number) const +{ + if (number != 0) return QString(); + return i18n("Terminal Settings"); +} + +KIcon KateKonsolePlugin::configPageIcon (uint number) const +{ + if (number != 0) return KIcon(); + return KIcon("utilities-terminal"); +} + +void KateKonsolePlugin::readConfig() +{ + foreach ( KateKonsolePluginView *view, mViews ) + view->readConfig(); +} + +KateKonsolePluginView::KateKonsolePluginView (KateKonsolePlugin* plugin, Kate::MainWindow *mainWindow) + : Kate::PluginView (mainWindow),m_plugin(plugin) +{ + // init console + QWidget *toolview = mainWindow->createToolView ("kate_private_plugin_katekonsoleplugin", Kate::MainWindow::Bottom, SmallIcon("utilities-terminal"), i18n("Terminal")); + m_console = new KateConsole(m_plugin, mainWindow, toolview); + + // register this view + m_plugin->mViews.append ( this ); +} + +KateKonsolePluginView::~KateKonsolePluginView () +{ + // unregister this view + m_plugin->mViews.removeAll (this); + + // cleanup, kill toolview + console + QWidget *toolview = m_console->parentWidget(); + delete m_console; + delete toolview; +} + +void KateKonsolePluginView::readConfig() +{ + m_console->readConfig(); +} + +KateConsole::KateConsole (KateKonsolePlugin* plugin, Kate::MainWindow *mw, QWidget *parent) + : KVBox (parent), Kate::XMLGUIClient(KateKonsoleFactory::componentData()) + , m_part (0) + , m_mw (mw) + , m_toolView (parent) + , m_plugin(plugin) +{ + QAction* a = actionCollection()->addAction("katekonsole_tools_pipe_to_terminal"); + a->setIcon(KIcon("utilities-terminal")); + a->setText(i18nc("@action", "&Pipe to Terminal")); + connect(a, SIGNAL(triggered()), this, SLOT(slotPipeToConsole())); + + a = actionCollection()->addAction("katekonsole_tools_sync"); + a->setText(i18nc("@action", "S&ynchronize Terminal with Current Document")); + connect(a, SIGNAL(triggered()), this, SLOT(slotManualSync())); + + a = actionCollection()->addAction("katekonsole_tools_toggle_focus"); + a->setIcon(KIcon("utilities-terminal")); + a->setText(i18nc("@action", "&Focus Terminal")); + connect(a, SIGNAL(triggered()), this, SLOT(slotToggleFocus())); + + m_mw->guiFactory()->addClient (this); + + readConfig(); +} + +KateConsole::~KateConsole () +{ + m_mw->guiFactory()->removeClient (this); + if (m_part) + disconnect ( m_part, SIGNAL(destroyed()), this, SLOT(slotDestroyed()) ); +} + +void KateConsole::loadConsoleIfNeeded() +{ + if (m_part) return; + + if (!window() || !parentWidget()) return; + if (!window() || !isVisibleTo(window())) return; + + KPluginFactory* factory = KPluginLoader("konsolepart").factory(); + if (!factory) return; + + m_part = static_cast(factory->create(this, this)); + + if (!m_part) return; + + // start the terminal + qobject_cast(m_part)->showShellInDir( QString() ); + + KGlobal::locale()->insertCatalog("konsole"); + + setFocusProxy(m_part->widget()); + m_part->widget()->show(); + + connect ( m_part, SIGNAL(destroyed()), this, SLOT(slotDestroyed()) ); + connect ( m_part, SIGNAL(overrideShortcut(QKeyEvent*,bool&)), + this, SLOT(overrideShortcut(QKeyEvent*,bool&))); + slotSync(); +} + +void KateConsole::slotDestroyed () +{ + m_part = 0; + m_currentPath.clear (); + + // hide the dockwidget + if (parentWidget()) + { + m_mw->hideToolView (m_toolView); + m_mw->centralWidget()->setFocus (); + } +} + +void KateConsole::overrideShortcut (QKeyEvent *, bool &override) +{ + /** + * let konsole handle all shortcuts + */ + override = true; +} + +void KateConsole::showEvent(QShowEvent *) +{ + if (m_part) return; + + loadConsoleIfNeeded(); +} + +void KateConsole::cd (const KUrl &url) +{ + if (m_currentPath == url.path()) + return; + + if (!m_part) + return; + + m_currentPath = url.path(); + sendInput("cd " + KShell::quoteArg(m_currentPath) + '\n'); +} + +void KateConsole::sendInput( const QString& text ) +{ + loadConsoleIfNeeded(); + + if (!m_part) return; + + TerminalInterface *t = qobject_cast(m_part); + + if (!t) return; + + t->sendInput (text); +} + +void KateConsole::slotPipeToConsole () +{ + if (KMessageBox::warningContinueCancel + (m_mw->window() + , i18n ("Do you really want to pipe the text to the console? This will execute any contained commands with your user rights.") + , i18n ("Pipe to Terminal?") + , KGuiItem(i18n("Pipe to Terminal")), KStandardGuiItem::cancel(), "Pipe To Terminal Warning") != KMessageBox::Continue) + return; + + KTextEditor::View *v = m_mw->activeView(); + + if (!v) + return; + + if (v->selection()) + sendInput (v->selectionText()); + else + sendInput (v->document()->text()); +} + +void KateConsole::slotSync() +{ + if (m_mw->activeView() ) { + if ( m_mw->activeView()->document()->url().isValid() + && m_mw->activeView()->document()->url().isLocalFile() ) { + cd(KUrl( m_mw->activeView()->document()->url().directory() )); + } else if ( !m_mw->activeView()->document()->url().isEmpty() ) { + sendInput( "### " + i18n("Sorry, cannot cd into '%1'", m_mw->activeView()->document()->url().directory() ) + '\n' ); + } + } +} + +void KateConsole::slotManualSync() +{ + m_currentPath.clear (); + slotSync(); + if ( ! m_part || ! m_part->widget()->isVisible() ) + m_mw->showToolView( parentWidget() ); +} +void KateConsole::slotToggleFocus() +{ + QAction *action = actionCollection()->action("katekonsole_tools_toggle_focus"); + if ( ! m_part ) { + m_mw->showToolView( parentWidget() ); + action->setText( i18n("Defocus Terminal") ); + return; // this shows and focuses the konsole + } + + if ( ! m_part ) return; + + if (m_part->widget()->hasFocus()) { + if (m_mw->activeView()) + m_mw->activeView()->setFocus(); + action->setText( i18n("Focus Terminal") ); + } else { + // show the view if it is hidden + if (parentWidget()->isHidden()) + m_mw->showToolView( parentWidget() ); + else // should focus the widget too! + m_part->widget()->setFocus( Qt::OtherFocusReason ); + action->setText( i18n("Defocus Terminal") ); + } +} + +void KateConsole::readConfig() +{ + disconnect( m_mw, SIGNAL(viewChanged()), this, SLOT(slotSync()) ); + if ( KConfigGroup(KGlobal::config(), "Konsole").readEntry("AutoSyncronize", false) ) + connect( m_mw, SIGNAL(viewChanged()), SLOT(slotSync()) ); + + + if ( KConfigGroup(KGlobal::config(), "Konsole").readEntry("SetEditor", false) ) + ::setenv( "EDITOR", "kate -b",1); + else + ::setenv( "EDITOR", m_plugin->previousEditorEnv().data(), 1 ); +} + +KateKonsoleConfigPage::KateKonsoleConfigPage( QWidget* parent, KateKonsolePlugin *plugin ) + : Kate::PluginConfigPage( parent ) + , mPlugin( plugin ) +{ + QVBoxLayout *lo = new QVBoxLayout( this ); + lo->setSpacing( KDialog::spacingHint() ); + + cbAutoSyncronize = new QCheckBox( i18n("&Automatically synchronize the terminal with the current document when possible"), this ); + lo->addWidget( cbAutoSyncronize ); + cbSetEditor = new QCheckBox( i18n("Set &EDITOR environment variable to 'kate -b'"), this ); + lo->addWidget( cbSetEditor ); + QLabel *tmp = new QLabel(this); + tmp->setText(i18n("Important: The document has to be closed to make the console application continue")); + lo->addWidget(tmp); + reset(); + lo->addStretch(); + connect( cbAutoSyncronize, SIGNAL(stateChanged(int)), SIGNAL(changed()) ); + connect( cbSetEditor, SIGNAL(stateChanged(int)), SIGNAL(changed()) ); +} + +void KateKonsoleConfigPage::apply() +{ + KConfigGroup config(KGlobal::config(), "Konsole"); + config.writeEntry("AutoSyncronize", cbAutoSyncronize->isChecked()); + config.writeEntry("SetEditor", cbSetEditor->isChecked()); + config.sync(); + mPlugin->readConfig(); +} + +void KateKonsoleConfigPage::reset() +{ + KConfigGroup config(KGlobal::config(), "Konsole"); + cbAutoSyncronize->setChecked(config.readEntry("AutoSyncronize", false)); + cbSetEditor->setChecked(config.readEntry("SetEditor", false)); +} + +// kate: space-indent on; indent-width 2; replace-tabs on; + diff --git a/kate/addons/kate/konsole/kateconsole.h b/kate/addons/kate/konsole/kateconsole.h new file mode 100644 index 00000000..c00ef4df --- /dev/null +++ b/kate/addons/kate/konsole/kateconsole.h @@ -0,0 +1,221 @@ +/* This file is part of the KDE project + Copyright (C) 2001 Christoph Cullmann + Copyright (C) 2002 Joseph Wenninger + Copyright (C) 2002 Anders Lund + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef __KATE_CONSOLE_H__ +#define __KATE_CONSOLE_H__ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace KParts +{ + class ReadOnlyPart; +} + +namespace KateMDI +{ + } + +class KateConsole; +class KateKonsolePluginView; + +class KateKonsolePlugin: public Kate::Plugin, public Kate::PluginConfigPageInterface +{ + Q_OBJECT + Q_INTERFACES(Kate::PluginConfigPageInterface) + + friend class KateKonsolePluginView; + + public: + explicit KateKonsolePlugin( QObject* parent = 0, const QList& = QList() ); + virtual ~KateKonsolePlugin(); + + Kate::PluginView *createView (Kate::MainWindow *mainWindow); + + // PluginConfigPageInterface + uint configPages() const { return 1; }; + Kate::PluginConfigPage *configPage (uint number = 0, QWidget *parent = 0, const char *name = 0); + QString configPageName (uint number = 0) const; + QString configPageFullName (uint number = 0) const; + KIcon configPageIcon (uint number = 0) const; + + void readConfig(); + + QByteArray previousEditorEnv() {return m_previousEditorEnv;} + + private: + QList mViews; + QByteArray m_previousEditorEnv; +}; + +class KateKonsolePluginView : public Kate::PluginView +{ + Q_OBJECT + + public: + /** + * Constructor. + */ + KateKonsolePluginView (KateKonsolePlugin* plugin, Kate::MainWindow *mainWindow); + + /** + * Virtual destructor. + */ + ~KateKonsolePluginView (); + + void readConfig(); + + private: + KateKonsolePlugin *m_plugin; + KateConsole *m_console; +}; + +/** + * KateConsole + * This class is used for the internal terminal emulator + * It uses internally the konsole part, thx to konsole devs :) + */ +class KateConsole : public KVBox, public Kate::XMLGUIClient +{ + Q_OBJECT + + public: + /** + * construct us + * @param mw main window + * @param parent toolview + */ + KateConsole (KateKonsolePlugin* plugin, Kate::MainWindow *mw, QWidget* parent); + + /** + * destruct us + */ + ~KateConsole (); + + void readConfig(); + + /** + * cd to dir + * @param url given dir + */ + void cd (const KUrl &url); + + /** + * send given text to console + * @param text commands for console + */ + void sendInput( const QString& text ); + + Kate::MainWindow *mainWindow() + { + return m_mw; + } + + public Q_SLOTS: + /** + * pipe current document to console + */ + void slotPipeToConsole (); + + /** + * synchronize the konsole with the current document (cd to the directory) + */ + void slotSync(); + /** + * When syncing is done by the user, also show the terminal if it is hidden + */ + void slotManualSync(); + + private Q_SLOTS: + /** + * the konsole exited ;) + * handle that, hide the dock + */ + void slotDestroyed (); + + /** + * construct console if needed + */ + void loadConsoleIfNeeded(); + + /** + * set or clear focus as appropriate. + */ + void slotToggleFocus(); + + /** + * Handle that shortcuts are not eaten by console + */ + void overrideShortcut (QKeyEvent *event, bool &override); + + protected: + /** + * the konsole get shown + * @param ev show event + */ + void showEvent(QShowEvent *ev); + + private: + /** + * console part + */ + KParts::ReadOnlyPart *m_part; + + /** + * main window of this console + */ + Kate::MainWindow *m_mw; + + /** + * toolview for this console + */ + QWidget *m_toolView; + + KateKonsolePlugin *m_plugin; + QString m_currentPath; +}; + +class KateKonsoleConfigPage : public Kate::PluginConfigPage { + Q_OBJECT + public: + explicit KateKonsoleConfigPage( QWidget* parent = 0, KateKonsolePlugin *plugin = 0 ); + virtual ~KateKonsoleConfigPage() + {} + + virtual void apply(); + virtual void reset(); + virtual void defaults() + {} + private: + QCheckBox *cbAutoSyncronize; + QCheckBox *cbSetEditor; + KateKonsolePlugin *mPlugin; +}; +#endif +// kate: space-indent on; indent-width 2; replace-tabs on; + diff --git a/kate/addons/kate/konsole/katekonsoleplugin.desktop b/kate/addons/kate/konsole/katekonsoleplugin.desktop new file mode 100644 index 00000000..c280936f --- /dev/null +++ b/kate/addons/kate/konsole/katekonsoleplugin.desktop @@ -0,0 +1,111 @@ +[Desktop Entry] +Type=Service +ServiceTypes=Kate/Plugin +X-KDE-Library=katekonsoleplugin +X-Kate-Version=2.8 +Name=Terminal tool view +Name[ast]=Vista de la ferramienta de terminal +Name[bs]=Prikaz terminala +Name[ca]=Eina de vista del terminal +Name[ca@valencia]=Eina de vista del terminal +Name[cs]=Nástroj zobrazující terminál +Name[da]=Terminalværktøjsvisning +Name[de]=Werkzeugansicht für Terminal +Name[el]=Προβολή εργαλείου τερματικού +Name[en_GB]=Terminal tool view +Name[es]=Vista de la herramienta de terminal +Name[et]=Terminali tööriistavaade +Name[eu]=Terminalaren ikuspegi-tresna +Name[fi]=Päätenäkymä +Name[fr]=Vue des outils pour le terminal +Name[ga]=Amharc uirlisí teirminéil +Name[gl]=Vista da utilidade de terminal +Name[he]=כלי לתצוגה של מסוף +Name[hu]=Terminál - Eszköznézet +Name[ia]=Vista de instrumento Terminal +Name[it]=Strumento visivo per terminale +Name[ja]=ターミナルツールビュー +Name[kk]=Терминал құралы +Name[km]=ទិដ្ឋភាព​ឧបករណ៍​ស្ថានីយ +Name[ko]=터미널 도구 보기 +Name[lt]=Terminalo įrankio rodinys +Name[lv]=Termināļa rīka skats +Name[mr]=टर्मिनल साधन दृश्य +Name[nb]=Terminal-verktøyvisning +Name[nds]=Terminal +Name[nl]=Terminalweergave +Name[nn]=Terminal-verktøyvising +Name[pa]=ਟਰਮੀਨਲ ਟੂਲ ਝਲਕ +Name[pl]=Widok narzędzia terminala +Name[pt]=Área do emulador de terminal +Name[pt_BR]=Área de ferramentas do Terminal +Name[ro]=Vizualizare de unelte Terminal +Name[ru]=Встроенный терминал +Name[si]=අග්‍ර මෙවලම් දසුන +Name[sk]=Pohľad nástroja terminálu +Name[sl]=Orodni pogled s terminalom +Name[sr]=Приказ терминала +Name[sr@ijekavian]=Приказ терминала +Name[sr@ijekavianlatin]=Prikaz terminala +Name[sr@latin]=Prikaz terminala +Name[sv]=Terminalverktygsvy +Name[tg]=Терминали намоиши асбобҳо +Name[tr]=Uçbirim görünümü +Name[ug]=تېرمىنال قورال كۆرۈنۈشى +Name[uk]=Перегляд інструмента термінала +Name[wa]=Vuwe des usteyes terminå +Name[x-test]=xxTerminal tool viewxx +Name[zh_CN]=终端工具视图 +Name[zh_TW]=終端機工具檢視 +Comment=Toolview embedding a terminal widget +Comment[ast]=Vista de ferramienta qu'integra un widget de terminal +Comment[bs]=Grafička kontrola ugrađenog terminala +Comment[ca]=Eina de vista que incrusta un estri de terminal +Comment[ca@valencia]=Eina de vista que incrusta un estri de terminal +Comment[cs]=Widget zobrazující terminál +Comment[da]=Værktøjsvisning til indlejring af en terminalkontrol +Comment[de]=Werkzeugansicht, die ein Terminalelement einbettet +Comment[el]=Προβολή εργαλείου που ενσωματώνει ένα συστατικό τερματικού +Comment[en_GB]=Toolview embedding a terminal widget +Comment[es]=Vista de herramienta que integra una ventana de terminal +Comment[et]=Terminalividinat põimiv tööriistavaade +Comment[eu]=Terminalaren trepeta kapsulatuta duen ikuspegi-tresna +Comment[fi]=Päätesovelman sisältävä työkalunäkymä +Comment[fr]=Vue des outils intégrant un composant graphique pour le terminal +Comment[ga]=Amharc uirlisí a leabaíonn giuirléid teimpléid +Comment[gl]=Vista que incorpora un widget de terminal +Comment[he]=כלי המטמיע תצוגה של מסוף +Comment[hu]=Eszköznézet, beágyazott terminál objektummal +Comment[ia]=Vista de instrumento includente un widget de terminal +Comment[it]=Strumento visivo per incorporare un terminale +Comment[ja]=ターミナルウィジェットを埋め込んだツールビュー +Comment[kk]=Құрамына енетін терминал виджеті +Comment[km]=ទិដ្ឋភាព​ឧបករណ៍​បង្កប់​នៅ​ក្នុងធាតុក្រាហ្វិក​ស្ថានីយ +Comment[ko]=터미널 위젯을 포함하는 도구 보기 +Comment[lt]=Įrankis su įstatytu terminalo valdikliu +Comment[lv]=Termināļa logdaļas iegulšanas rīku skats +Comment[mr]=टर्मिनल विजेट समाविष्ट करणारे साधन दृश्य +Comment[nb]=Verktøyvisning med en innebygd terminal +Comment[nds]=En inbett Konsool +Comment[nl]=Weergave met ingebedde terminal +Comment[nn]=Verktøyvising med ein innebygd terminal +Comment[pl]=Widok osadzający element interfejsu terminala +Comment[pt]=Área de ferramentas que incorpora uma janela de terminal +Comment[pt_BR]=Área de ferramentas que incorpora um console +Comment[ro]=Vizualizare de unelte ce înglobează un terminal +Comment[ru]=Служебная панель с консолью +Comment[si]=මෙවලම්දසුන අග්‍ර විජෙට්ටුවට තිළැලිව +Comment[sk]=Zobrazovací nástroj vkladajúci komponent terminálu +Comment[sl]=Orodni pogled z vgrajenim gradnikom terminala +Comment[sr]=Виџет угнежђеног терминала +Comment[sr@ijekavian]=Виџет угнијежђеног терминала +Comment[sr@ijekavianlatin]=Vidžet ugniježđenog terminala +Comment[sr@latin]=Vidžet ugnežđenog terminala +Comment[sv]=Verktygsvy med en inbäddad grafisk terminalkomponent +Comment[tg]=Асбоби намоиш бо терминали дарунсохт +Comment[tr]=Gömülü bir uçbirim parçacığı aracı +Comment[ug]=بىر تېرمىنالغا سىڭدۈرۈلگەن كىچىك ئەپنىڭ قورال كۆرۈنۈشى +Comment[uk]=Вбудування віджета термінала у вікно програми +Comment[x-test]=xxToolview embedding a terminal widgetxx +Comment[zh_CN]=嵌入一个终端构件的工具视图 +Comment[zh_TW]=嵌入終端機元件的工具檢視 diff --git a/kate/addons/kate/konsole/ui.rc b/kate/addons/kate/konsole/ui.rc new file mode 100644 index 00000000..bdc359aa --- /dev/null +++ b/kate/addons/kate/konsole/ui.rc @@ -0,0 +1,10 @@ + + + + &Tools + + + + + + diff --git a/kate/addons/kate/kttsd/CMakeLists.txt b/kate/addons/kate/kttsd/CMakeLists.txt new file mode 100644 index 00000000..7945505e --- /dev/null +++ b/kate/addons/kate/kttsd/CMakeLists.txt @@ -0,0 +1,24 @@ +kde4_add_plugin(kate_kttsd katekttsd.cpp) + +target_link_libraries(kate_kttsd + ${KDE4_KDECORE_LIBS} + ${KDE4_KTEXTEDITOR_LIBS} + ${KDE4_KPARTS_LIBS} + kateinterfaces +) + +install( + TARGETS kate_kttsd + DESTINATION ${KDE4_PLUGIN_INSTALL_DIR} +) + +########### install files ############### + +install( + FILES kate_kttsd.desktop + DESTINATION ${KDE4_SERVICES_INSTALL_DIR} +) +install( + FILES ui.rc + DESTINATION ${KDE4_DATA_INSTALL_DIR}/kate/plugins/kate_kttsd +) diff --git a/kate/addons/kate/kttsd/Messages.sh b/kate/addons/kate/kttsd/Messages.sh new file mode 100644 index 00000000..28688d07 --- /dev/null +++ b/kate/addons/kate/kttsd/Messages.sh @@ -0,0 +1,3 @@ +#! /bin/sh +$EXTRACTRC *.rc >> rc.cpp +$XGETTEXT *.cpp -o $podir/kate_kttsd.pot diff --git a/kate/addons/kate/kttsd/kate_kttsd.desktop b/kate/addons/kate/kttsd/kate_kttsd.desktop new file mode 100644 index 00000000..90a5c07b --- /dev/null +++ b/kate/addons/kate/kttsd/kate_kttsd.desktop @@ -0,0 +1,116 @@ +[Desktop Entry] +Type=Service +ServiceTypes=Kate/Plugin +X-KDE-Library=kate_kttsd +X-Kate-Version=3.0 +Icon=kttsd +Name=Jovie Text-to-Speech +Name[bg]=Jovie - четене на текст +Name[bs]=Jovie (tekst‑u‑govor) +Name[ca]=Text a veu Jovie +Name[ca@valencia]=Text a veu Jovie +Name[cs]=Hlasová syntéza Jovie +Name[da]=Jovie tekst-til-tale +Name[de]=Jovie-Sprachausgabe +Name[el]=Jovie Text-to-Speech +Name[en_GB]=Jovie Text-to-Speech +Name[es]=Complemento de texto hablado Jovie para KTextEditor +Name[et]=Jovie teksti ettelugemine +Name[eu]=Jovie testutik-hizketara +Name[fi]=Jovie-puhesyntetisaattori +Name[fr]=Synthèse vocale « Jovie » +Name[ga]=Téacs-go-Caint Jovie +Name[gl]=Texto-para-fala mediante Jovie +Name[he]=מנוע טקסט לדיבור Jovie +Name[hu]=Jovie szövegfelolvasó +Name[ia]=Texto-a-Discurso (TextToSpeech) Jovie +Name[is]=Jovie texti-í-tal +Name[it]=Pronuncia di Jovie +Name[ja]=Jovie 読み上げプラグイン +Name[kk]=Jovie - мәтінді дауыстап оқу +Name[km]=អត្ថបទ​ត្រូវ​និយាយ​របស់​ Jovie +Name[ko]=Jovie 텍스트 음성 변환 +Name[lt]=Jovie teksto vertimas į kalbą +Name[lv]=Jovie no teksta uz runu +Name[mr]=जोव्ही मजकुर बोलून दाखवणारा +Name[nb]=Jovie tekst til tale +Name[nds]=Vörlesermoduul "Jovie" +Name[nl]=Jovie tekst-naar-spraak +Name[nn]=Jovie tekst-til-tale +Name[pa]=ਜੋਵੀ ਟੈਕਸਟ ਤੋਂ ਬੋਲੀ +Name[pl]=Tekst-na-mowę Jovie +Name[pt]=Texto-para-Fala do Jovie +Name[pt_BR]=Texto-para-fala Jovie +Name[ro]=Text-în-vorbire Jovi +Name[ru]=Служба синтеза речи Jovie +Name[si]=Jovie පෙල-කියවීමට +Name[sk]=Jovie Text-na-Reč +Name[sl]=Besedilo-v-govor Jovie +Name[sr]=Џови (текст‑у‑говор) +Name[sr@ijekavian]=Џови (текст‑у‑говор) +Name[sr@ijekavianlatin]=Džovi (tekst‑u‑govor) +Name[sr@latin]=Džovi (tekst‑u‑govor) +Name[sv]=Jovie text-till-tal +Name[tg]=Барномаи талаффузи матни Jovie +Name[tr]=Jovie Metin Okuma +Name[ug]=Jovie تېكىستتىن ئاۋازغا +Name[uk]=Синтез мовлення з тексту Jovie +Name[x-test]=xxJovie Text-to-Speechxx +Name[zh_CN]=Jovie 语音合成 +Name[zh_TW]=Jovie 文字轉語音 +Comment=Adds a menu entry for speaking the text +Comment[ar]=يضيف مُدخلة قائمة لنطق النصّ +Comment[ast]=Amestar una entrada al menú pa lleer el testu +Comment[bg]=Добавяне на запис в менюто за изговаряне на текст +Comment[bs]=Dodaje stavku menija za izgovaranje teksta +Comment[ca]=Afegeix una entrada al menú per parlar el text +Comment[ca@valencia]=Afig una entrada al menú per parlar el text +Comment[cs]=Přidá položku v nabídce pro čtení textu +Comment[da]=Tilføjer en menu til oplæsning af tekst +Comment[de]=Fügt einen Menüeintrag zum Vorlesen des Textes hinzu +Comment[el]=Προσθέτει μία καταχώρηση στο μενού για τη φωνητική απόδοση κειμένου +Comment[en_GB]=Adds a menu entry for speaking the text +Comment[es]=Añade una entrada de menú para leer el texto +Comment[et]=Lisab menüükirje teksti kõnelemiseks +Comment[eu]=Menuko sarrera gehitzen du testua hitzegiteko +Comment[fi]=Lisää tekstin puhumismahdollisuuden valikkoon +Comment[fr]=Ajoute une ligne de menu pour énoncer le texte +Comment[ga]=Cuir iontráil sa roghchlár chun an téacs a léamh +Comment[gl]=Engade unha entrada no menú para falar o texto +Comment[he]=מוסיף תפריט להקראת הטקסט +Comment[hu]=Menübejegyzés szövegfelolvasáshoz +Comment[ia]=Adde un entrata de menu pro pronunciar le texto +Comment[it]=Aggiunge una voce del menu per pronunciare il testo +Comment[ja]=メニューにテキストを読み上げるための項目を追加します +Comment[kk]=Мәзірге мәтінді дауыстау жолын қосу +Comment[km]=បន្ថែម​ធាតុ​​ម៉ឺនុយ​សម្រាប់​ការ​អាន​អត្ថបទ +Comment[ko]=텍스트를 말하는 메뉴 항목 추가 +Comment[lt]=Prideda meniu elementą teksto skaitymui +Comment[lv]=Pievieno izvēlnes komandu, kas izrunā tekstu +Comment[mr]=पाठ्य बोलण्याकरिता मेन्यू नोंद जोडतो +Comment[nb]=Legger til en menyoppføring for å si teksten høyt +Comment[nds]=Föögt en Menüindrag för't Vörlesen vun Text to +Comment[nl]=Voegt een menuoptie toe voor het uitspreken van de tekst +Comment[nn]=Legg til ei menyoppføring for opplesing av tekst +Comment[pl]=Dodaje element menu, który powoduje wypowiedzenie tekstu +Comment[pt]=Adiciona um item de menu para sintetizar o texto +Comment[pt_BR]=Adiciona um item no menu para ler o texto +Comment[ro]=Adaugă un element de meniu pentru vorbirea textului +Comment[ru]=Добавляет меню синтеза речи +Comment[si]=පෙළ කතා කරවීමට මෙනු ඇතුළත්කිරීමක් එක් කරයි +Comment[sk]=Pridá položku ponuky pre hovorenie textu +Comment[sl]=V meni doda vnos za izgovorjavo besedila +Comment[sr]=Додаје ставку менија за изговарање текста +Comment[sr@ijekavian]=Додаје ставку менија за изговарање текста +Comment[sr@ijekavianlatin]=Dodaje stavku menija za izgovaranje teksta +Comment[sr@latin]=Dodaje stavku menija za izgovaranje teksta +Comment[sv]=Lägger till ett menyalternativ för att läsa upp texten +Comment[tg]=Вориди менюро барои талаффузи матн илова мекунад +Comment[tr]=Metni okumak için bir menü girdisi oluşturur +Comment[ug]=تېكىست ئوقۇش ئۈچۈن تىزىملىككە بىر تۈر قوشىدۇ +Comment[uk]=Додає елемент меню для декламування тексту +Comment[wa]=Radjoute ene intrêye el dressêye po lére li tecse +Comment[x-test]=xxAdds a menu entry for speaking the textxx +Comment[zh_CN]=添加一个菜单条目用来朗读文本 +Comment[zh_TW]=新增選單項目以唸出文字 +author=Olaf Jan Schmidt, ojschmidt@kde.org diff --git a/kate/addons/kate/kttsd/katekttsd.cpp b/kate/addons/kate/kttsd/katekttsd.cpp new file mode 100644 index 00000000..92f95a09 --- /dev/null +++ b/kate/addons/kate/kttsd/katekttsd.cpp @@ -0,0 +1,105 @@ +/*************************************************************************** + A KTextEditor (Kate Part) plugin for speaking text. + + Copyright: + (C) 2003-2004 by Olaf Schmidt + (C) 2005 by Gary Cramblitt + (C) 2009 by Laurent Montel + + Original Author: Olaf Schmidt + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +// KateKttsdPlugin includes. +#include "katekttsd.h" +#include "moc_katekttsd.cpp" +#include +// Qt includes. +#include +#include + +// KDE includes. +#include +#include +#include +#include +#include +#include +#include +#include + +K_PLUGIN_FACTORY(KateKttsdFactory, registerPlugin();) +K_EXPORT_PLUGIN(KateKttsdFactory(KAboutData("kate_kttsd","kate_kttsd",ki18n("Jovie Text-to-Speech Plugin"), "0.1", ki18n("Jovie Text-to-Speech Plugin"), KAboutData::License_LGPL_V2)) ) + +KateKttsdPlugin::KateKttsdPlugin(QObject* parent, const QList&) + : Kate::Plugin ((Kate::Application*)parent) +{ +} + +Kate::PluginView *KateKttsdPlugin::createView (Kate::MainWindow *mainWindow) +{ + return new KateKttsdPluginView(mainWindow); +} + +KateKttsdPluginView::KateKttsdPluginView( Kate::MainWindow *mw ) + : Kate::PluginView (mw), + Kate::XMLGUIClient(KateKttsdFactory::componentData()) +{ + KGlobal::locale()->insertCatalog("kttsd"); + KAction *a = actionCollection()->addAction("tools_kttsd"); + a->setText(i18n("Speak Text")); + a->setIcon(KIcon("preferences-desktop-text-to-speech")); + connect( a, SIGNAL(triggered(bool)), this, SLOT(slotReadOut()) ); + + mainWindow()->guiFactory()->addClient(this); +} + +KateKttsdPluginView::~KateKttsdPluginView() +{ + mainWindow()->guiFactory()->removeClient( this ); +} + + +void KateKttsdPluginView::slotReadOut() +{ + KTextEditor::View *v = mainWindow()->activeView(); + if ( !v ) + return; + KTextEditor::Document *doc = v->document(); + QString text; + if ( v->selection() ) + { + text = v->selectionText(); + } + else + text = doc->text(); + if ( text.isEmpty() ) + return; + + // If KTTSD not running, start it. + if (!QDBusConnection::sessionBus().interface()->isServiceRegistered("org.kde.kttsd")) + { + QString error; + if (KToolInvocation::startServiceByDesktopName("kttsd", QStringList(), &error)) + { + KMessageBox::error(0, i18n( "Starting Jovie Text-to-Speech Service Failed"), error ); + return; + } + } + + QDBusInterface kttsd( "org.kde.kttsd", "/KSpeech", "org.kde.KSpeech" ); + + QDBusReply reply = kttsd.call("say", text,0); + if ( !reply.isValid()) + KMessageBox::error( 0, i18n( "D-Bus Call Failed" ), + i18n( "The D-Bus call say failed." )); +} + diff --git a/kate/addons/kate/kttsd/katekttsd.h b/kate/addons/kate/kttsd/katekttsd.h new file mode 100644 index 00000000..5337a168 --- /dev/null +++ b/kate/addons/kate/kttsd/katekttsd.h @@ -0,0 +1,51 @@ +/*************************************************************************** + A KTextEditor (Kate Part) plugin for speaking text. + + Copyright: + (C) 2003-2004 by Olaf Schmidt + (C) 2005 by Gary Cramblitt + + Original Author: Olaf Schmidt + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef _KATEKTTSD_H_ +#define _KATEKTTSD_H_ + +#include +#include +#include + +#include + +class KateKttsdPlugin : public Kate::Plugin +{ + Q_OBJECT + + public: + explicit KateKttsdPlugin( QObject* parent = 0, const QList& = QList() ); + virtual ~KateKttsdPlugin() {}; + Kate::PluginView *createView(Kate::MainWindow *mainWindow); +}; + +class KateKttsdPluginView : public Kate::PluginView, public Kate::XMLGUIClient +{ + Q_OBJECT + + public: + explicit KateKttsdPluginView(Kate::MainWindow *mw ); + ~KateKttsdPluginView(); + + public slots: + void slotReadOut(); +}; + +#endif // _KATEKTTSD_H_ diff --git a/kate/addons/kate/kttsd/ui.rc b/kate/addons/kate/kttsd/ui.rc new file mode 100644 index 00000000..ba4638d4 --- /dev/null +++ b/kate/addons/kate/kttsd/ui.rc @@ -0,0 +1,11 @@ + + + + &Tools + + + + Main Toolbar + + + diff --git a/kate/addons/kate/mailfiles/CMakeLists.txt b/kate/addons/kate/mailfiles/CMakeLists.txt new file mode 100644 index 00000000..3afa9205 --- /dev/null +++ b/kate/addons/kate/mailfiles/CMakeLists.txt @@ -0,0 +1,29 @@ +set(katemailfilesplugin_PART_SRCS + katemailfiles.cpp + katemailfilesdialog.cpp +) + +kde4_add_plugin(katemailfilesplugin ${katemailfilesplugin_PART_SRCS}) + + +target_link_libraries(katemailfilesplugin + ${KDE4_KDECORE_LIBS} + ${KDE4_KPARTS_LIBS} + kateinterfaces +) + +install( + TARGETS katemailfilesplugin + DESTINATION ${KDE4_PLUGIN_INSTALL_DIR} +) + +########### install files ############### + +install( + FILES ui.rc + DESTINATION ${KDE4_DATA_INSTALL_DIR}/kate/plugins/katemailfiles +) +install( + FILES katemailfilesplugin.desktop + DESTINATION ${KDE4_SERVICES_INSTALL_DIR} +) diff --git a/kate/addons/kate/mailfiles/Messages.sh b/kate/addons/kate/mailfiles/Messages.sh new file mode 100644 index 00000000..a6c18b40 --- /dev/null +++ b/kate/addons/kate/mailfiles/Messages.sh @@ -0,0 +1,3 @@ +#! /bin/sh +$EXTRACTRC *.rc >> rc.cpp +$XGETTEXT *.cpp -o $podir/katemailfilesplugin.pot diff --git a/kate/addons/kate/mailfiles/katemailfiles.cpp b/kate/addons/kate/mailfiles/katemailfiles.cpp new file mode 100644 index 00000000..4b9bcfc9 --- /dev/null +++ b/kate/addons/kate/mailfiles/katemailfiles.cpp @@ -0,0 +1,152 @@ +/* This file is part of the KDE project + Copyright (C) 2001 Christoph Cullmann + Copyright (C) 2002 Joseph Wenninger + Copyright (C) 2002 Anders Lund + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "katemailfiles.h" +#include "moc_katemailfiles.cpp" + +#include "katemailfilesdialog.h" + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +K_PLUGIN_FACTORY(KateMailFilesFactory, registerPlugin();) +K_EXPORT_PLUGIN(KateMailFilesFactory(KAboutData("katemailfilesplugin","katemailfilesplugin",ki18n("Mail Files"), "0.1", ki18n("Support mailing files"), KAboutData::License_LGPL_V2)) ) + +KateMailFilesPlugin::KateMailFilesPlugin( QObject* parent, const QList& ): + Kate::Plugin ( (Kate::Application*)parent ) +{} + +Kate::PluginView *KateMailFilesPlugin::createView (Kate::MainWindow *mainWindow) +{ + return new KateMailFilesPluginView (mainWindow); +} + +KateMailFilesPluginView::KateMailFilesPluginView (Kate::MainWindow *mainWindow) + : Kate::PluginView (mainWindow), Kate::XMLGUIClient(KateMailFilesFactory::componentData()) +{ + actionCollection()->addAction( KStandardAction::Mail, this, SLOT(slotMail()) ) + ->setWhatsThis(i18n("Send one or more of the open documents as email attachments.")); + mainWindow->guiFactory()->addClient (this); +} + +KateMailFilesPluginView::~KateMailFilesPluginView () +{ + mainWindow()->guiFactory()->removeClient (this); +} + +void KateMailFilesPluginView::slotMail() +{ + KateMailDialog *d = new KateMailDialog(mainWindow()->window(), mainWindow()); + if ( ! d->exec() ) + { + delete d; + return; + } + QList attDocs = d->selectedDocs(); + delete d; + // Check that all selected files are saved (or shouldn't be) + QStringList urls; // to atthatch + KTextEditor::Document *doc; + for ( QList::iterator it = attDocs.begin(); + it != attDocs.end(); ++it ) + { + doc = *it; + if (!doc) continue; + if ( doc->url().isEmpty() ) + { + // unsaved document. back out unless it gets saved + int r = KMessageBox::questionYesNo( mainWindow()->window(), + i18n("

The current document has not been saved, and " + "cannot be attached to an email message.

" + "

Do you want to save it and proceed?

"), + i18n("Cannot Send Unsaved File"), KStandardGuiItem::saveAs(), KStandardGuiItem::cancel() ); + if ( r == KMessageBox::Yes ) + { + bool sr = doc->documentSaveAs(); + /* if ( sr == KTextEditor::View::SAVE_OK ) { ; + } + else {*/ + if ( !sr ) // ERROR or RETRY(?) + { KMessageBox::sorry( mainWindow()->window(), i18n("The file could not be saved. Please check " + "if you have write permission.") ); + continue; + } + } + else + continue; + } + if ( doc->isModified() ) + { + // warn that document is modified and offer to save it before proceeding. + int r = KMessageBox::warningYesNoCancel( mainWindow()->window(), + i18n("

The current file:
%1
has been " + "modified. Modifications will not be available in the attachment.

" + "

Do you want to save it before sending it?

", doc->url().pathOrUrl()), + i18n("Save Before Sending?"), KStandardGuiItem::save(), KGuiItem(i18n("Do Not Save")) ); + switch ( r ) + { + case KMessageBox::Cancel: + continue; + case KMessageBox::Yes: + doc->save(); + if ( doc->isModified() ) + { // read-only docs ends here, if modified. Hmm. + KMessageBox::sorry( mainWindow()->window(), i18n("The file could not be saved. Please check " + "if you have write permission.") ); + continue; + } + break; + default: + break; + } + } + // finally call the mailer + urls << doc->url().url(); + } // check selected docs done + if ( ! urls.count() ) + return; + KToolInvocation::invokeMailer( QString(), // to + QString(), // cc + QString(), // bcc + QString(), // subject + QString(), // body + QString(), // msgfile + urls // urls to atthatch + ); +} +// kate: space-indent on; indent-width 2; replace-tabs on; + diff --git a/kate/addons/kate/mailfiles/katemailfiles.h b/kate/addons/kate/mailfiles/katemailfiles.h new file mode 100644 index 00000000..9d3c110c --- /dev/null +++ b/kate/addons/kate/mailfiles/katemailfiles.h @@ -0,0 +1,60 @@ +/* This file is part of the KDE project + Copyright (C) 2001 Christoph Cullmann + Copyright (C) 2002 Joseph Wenninger + Copyright (C) 2002 Anders Lund + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef __KATE_MAILFILES_H__ +#define __KATE_MAILFILES_H__ + +#include +#include + +class KateMailFilesPlugin: public Kate::Plugin +{ + Q_OBJECT + + public: + explicit KateMailFilesPlugin( QObject* parent = 0, const QList& = QList() ); + virtual ~KateMailFilesPlugin() + {} + + Kate::PluginView *createView (Kate::MainWindow *mainWindow); +}; + +class KateMailFilesPluginView : public Kate::PluginView, public Kate::XMLGUIClient +{ + Q_OBJECT + + public: + /** + * Constructor. + */ + KateMailFilesPluginView (Kate::MainWindow *mainWindow); + + /** + * Virtual destructor. + */ + ~KateMailFilesPluginView (); + + private Q_SLOTS: + void slotMail(); +}; + +#endif +// kate: space-indent on; indent-width 2; replace-tabs on; + diff --git a/kate/addons/kate/mailfiles/katemailfilesdialog.cpp b/kate/addons/kate/mailfiles/katemailfilesdialog.cpp new file mode 100644 index 00000000..0f96ada9 --- /dev/null +++ b/kate/addons/kate/mailfiles/katemailfilesdialog.cpp @@ -0,0 +1,134 @@ +/* This file is part of the KDE project + Copyright (C) 2002 Anders Lund + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "katemailfilesdialog.h" + +#include +#include +#include +#include +#include +#include + +/* a private check list item, that can store a KTextEditor::Document*. */ +class KateMailDocItem : public QTreeWidgetItem +{ + public: + KateMailDocItem( QTreeWidget *parent, KTextEditor::Document *doc ) + : QTreeWidgetItem( parent ), mdoc(doc) + { + setText(0, doc->documentName()); + setText(1, doc->url().pathOrUrl()); + setCheckState(0, Qt::Unchecked); + } + KTextEditor::Document *doc() + { + return mdoc; + } + private: + KTextEditor::Document *mdoc; +}; + +/////////////////////////////////////////////////////////////////////////// +// KateMailDialog implementation +/////////////////////////////////////////////////////////////////////////// +KateMailDialog::KateMailDialog( QWidget *parent, Kate::MainWindow *mainwin ) + : KDialog( parent ), + mainWindow( mainwin ) +{ + setCaption( i18n("Email Files") ); + setButtons( Ok | Cancel | User1 ); + setButtonGuiItem( User1, KGuiItem( i18n("&Show All Documents >>") ) ); + setObjectName( "kate mail dialog" ); + setModal( true ); + + setButtonGuiItem( KDialog::Ok, KGuiItem( i18n("&Mail..."), "mail-send") ); + + mw = new KVBox(this); + setMainWidget(mw); + mw->installEventFilter( this ); + + lInfo = new QLabel( i18n( + "

Press Mail... to email the current document.

" + "

To select more documents to send, press Show All Documents >>.

"), mw ); + // TODO avoid untill needed - later + list = new QTreeWidget( mw ); + QStringList header; + header << i18n("Name"); + header << i18n("URL"); + list->setHeaderLabels(header); + + KTextEditor::Document *currentDoc = mainWindow->activeView()->document(); + uint n = Kate::documentManager()->documents().size(); + uint i = 0; + QTreeWidgetItem *item; + while ( i < n ) + { + KTextEditor::Document *doc = Kate::documentManager()->documents().at( i ); + if ( doc ) + { + item = new KateMailDocItem( list, doc ); + if ( doc == currentDoc ) + { + list->setCurrentItem(item); + item->setCheckState(0, Qt::Checked); + } + } + i++; + } + list->hide(); + connect( this, SIGNAL(user1Clicked()), this, SLOT(slotShowButton()) ); + mw->setMinimumSize( lInfo->sizeHint() ); +} + +QList KateMailDialog::selectedDocs() +{ + QList l; + KateMailDocItem *item = NULL; + for(int i = 0; i < list->topLevelItemCount(); i++) + { + item = static_cast( list->topLevelItem(i) ); + if ( item->checkState(0) == Qt::Checked ) + l.append( item->doc() ); + } + return l; +} + +void KateMailDialog::slotShowButton() +{ + if ( list->isVisible() ) + { + setButtonText( User1, i18n("&Show All Documents >>") ); + list->hide(); + mw->setMinimumSize( QSize( lInfo->sizeHint().width(), lInfo->sizeHint().height()) ); + setMinimumSize( mw->width(), sizeHint().height() - list->sizeHint().height()); + } + else + { + list->show(); + setButtonText( User1, i18n("&Hide Document List <<") ); + lInfo->setText( i18n("Press Mail... to send selected documents") ); + mw->setMinimumSize( QSize( lInfo->sizeHint().width(), list->sizeHint().height() + lInfo->sizeHint().height()) ); + setMinimumSize( mw->width(), sizeHint().height()); + } + + resize( width(), minimumHeight() ); +} +#include "moc_katemailfilesdialog.cpp" + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/addons/kate/mailfiles/katemailfilesdialog.h b/kate/addons/kate/mailfiles/katemailfilesdialog.h new file mode 100644 index 00000000..01d80f78 --- /dev/null +++ b/kate/addons/kate/mailfiles/katemailfilesdialog.h @@ -0,0 +1,64 @@ +/* This file is part of the KDE project + Copyright (C) 2002 Anders Lund + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef _KATE_MAILFILES_DIALOG_H_ +#define _KATE_MAILFILES_DIALOG_H_ + + +#include +#include + +#include + +#include +#include +#include + + +/** + This is a dialog for choosing which of the open files to mail. + The current file is selected by default, the dialog can be expanded + to display all the files if required. + +*/ +class KateMailDialog : public KDialog +{ + Q_OBJECT + public: + explicit KateMailDialog( QWidget *parent = 0, + Kate::MainWindow *mainwin = 0 ); + ~KateMailDialog() + {} + + /** + @return a list of the selected docs. + */ + QList selectedDocs(); + private Q_SLOTS: + void slotShowButton(); + private: + class QTreeWidget *list; + class QLabel *lInfo; + Kate::MainWindow *mainWindow; + class KVBox *mw; + +}; + +#endif // _KATE_MAILFILES_DIALOG_H_ +// kate: space-indent on; indent-width 2; replace-tabs on; + diff --git a/kate/addons/kate/mailfiles/katemailfilesplugin.desktop b/kate/addons/kate/mailfiles/katemailfilesplugin.desktop new file mode 100644 index 00000000..baff1462 --- /dev/null +++ b/kate/addons/kate/mailfiles/katemailfilesplugin.desktop @@ -0,0 +1,117 @@ +[Desktop Entry] +Type=Service +ServiceTypes=Kate/Plugin +X-KDE-Library=katemailfilesplugin +X-Kate-Version=2.8 +Name=Mail files +Name[ar]=إرسال الملفات بالبريد +Name[ast]=Ficheros de corréu +Name[bg]=Файлове по пощата +Name[bs]=Slanje e‑poštom +Name[ca]=Envia fitxers per correu +Name[ca@valencia]=Envia fitxers per correu +Name[cs]=Soubory e-mailu +Name[da]=E-mail filer +Name[de]=Mail-Dateien +Name[el]=Αρχεία αλληλογραφίας +Name[en_GB]=Mail files +Name[es]=Enviar archivos por correo +Name[et]=Failide saatmine e-postiga +Name[eu]=Postaren fitxategiak +Name[fa]=پرونده‌های نامه +Name[fi]=Sähköpostita tiedostoja +Name[fr]=Envoyer des fichiers par courrier électronique +Name[ga]=Seol comhaid +Name[gl]=Enviar por correo os ficheiros +Name[he]=שלח קבצים בדוא"ל +Name[hu]=Levélküldő +Name[ia]=Invia files +Name[it]=Invia file +Name[ja]=ファイルをメールで送信 +Name[kk]=Файлдарды жіберу +Name[km]=ឯកសារ​សំបុត្រ +Name[ko]=이메일 파일 +Name[lt]=Siųsti failus paštu +Name[lv]=Sūtīt datnes pa e-pastu +Name[mr]=मेल फाईल्स +Name[nb]=Epost-filer +Name[nds]=Dateien mit Nettpost loosstüern +Name[ne]=मेल फाइल +Name[nl]=Bestanden e-mailen +Name[nn]=E-postfiler +Name[pa]=ਫਾਇਲ ਮੇਲ +Name[pl]=Wyślij pliki +Name[pt]=Enviar os ficheiros por e-mail +Name[pt_BR]=Enviar arquivos por e-mail +Name[ro]=Fișiere poștale +Name[ru]=Отправка файлов +Name[si]=පණිවුඩ ගොනු +Name[sk]=Poštové súbory +Name[sl]=Pošiljanje datotek +Name[sr]=Слање е‑поштом +Name[sr@ijekavian]=Слање е‑поштом +Name[sr@ijekavianlatin]=Slanje e‑poštom +Name[sr@latin]=Slanje e‑poštom +Name[sv]=E-postfiler +Name[tg]=Файлҳои почта +Name[tr]=Dosyaları e-posta ile gönder +Name[ug]=خەت ھۆججەتلىرى +Name[uk]=Надсилання файлів поштою +Name[x-test]=xxMail filesxx +Name[zh_CN]=邮寄文件 +Name[zh_TW]=郵件檔案 +Comment=Send files via email +Comment[ar]=أرسل الملفات عبر البريد الإلكتروني +Comment[ast]=Unviar ficheros por e-mail +Comment[bg]=Изпращане на файлове по пощата +Comment[bs]=Šaljite datoteke e‑poštom +Comment[ca]=Envia fitxers per correu electrònic +Comment[ca@valencia]=Envia fitxers per correu electrònic +Comment[cs]=Poslat soubory přes e-mail +Comment[da]=Send filer via e-mail +Comment[de]=Dateien per E-Mail versenden +Comment[el]=Αποστολή αρχείων μέσω email +Comment[en_GB]=Send files via email +Comment[es]=Enviar archivos por correo +Comment[et]=Failide saatmine e-postiga +Comment[eu]=Bidali fitxategiak posta elektoniko bidez +Comment[fi]=Lähetä tiedostoja sähköpostitse +Comment[fr]=Envoie des fichiers par courrier électronique +Comment[ga]=Seol comhaid trí ríomhphost +Comment[gl]=Envía os ficheiros por correo electrónico +Comment[he]=שולח קבצים בדוא"ל +Comment[hu]=Fájlok küldése e-mailen keresztül +Comment[ia]=Invia files via e-posta +Comment[it]=Invia file per posta elettronica +Comment[ja]=ファイルをメールで送信します +Comment[kk]=Файлдарды эл.поштамен жіберу +Comment[km]=ផ្ញើ​ឯកសារ​តាម​​អ៊ីមែល​ +Comment[ko]=파일을 이메일로 보내기 +Comment[lt]=Siųsti failus naudojant e-paštą +Comment[lv]=Nosūtīt datnes pa e-pastu +Comment[mr]=फाईल्स इमेल द्वारे पाठवा +Comment[nb]=Send filer via e-post +Comment[nds]=Dateien mit Nettpost loosstüern +Comment[nl]=Bestanden via e-mail verzenden +Comment[nn]=Send filer via e-post +Comment[pa]=ਫਾਇਲਾਂ ਈਮੇਲ ਰਾਹੀਂ ਭੇਜੋ +Comment[pl]=Wyślij pliki pocztą elektroniczną +Comment[pt]=Enviar os ficheiros por e-mail +Comment[pt_BR]=Envia os arquivos por e-mail +Comment[ro]=Trimite fișiere prin email +Comment[ru]=Отправить файлы по эл. почте +Comment[si]=විද්‍යුත් ලිපි හරහා ගොනු යවන්න +Comment[sk]=Poslať súbory cez e-mail +Comment[sl]=Pošljite datoteke preko e-pošte +Comment[sr]=Шаљите фајлове е‑поштом +Comment[sr@ijekavian]=Шаљите фајлове е‑поштом +Comment[sr@ijekavianlatin]=Šaljite fajlove e‑poštom +Comment[sr@latin]=Šaljite fajlove e‑poštom +Comment[sv]=Skicka filer via e-post +Comment[tg]=Ирсоли файлҳо тавассути почтаи электронӣ +Comment[tr]=Dosyaları e-posta ile gönder +Comment[ug]=ھۆججەتلەرنى ئېلخەت ئىلە يوللا' +Comment[uk]=Надіслати файли електронною поштою +Comment[x-test]=xxSend files via emailxx +Comment[zh_CN]=通过电子邮件发送文件 +Comment[zh_TW]=透過電子郵件傳送檔案 diff --git a/kate/addons/kate/mailfiles/ui.rc b/kate/addons/kate/mailfiles/ui.rc new file mode 100644 index 00000000..d3a6fd3c --- /dev/null +++ b/kate/addons/kate/mailfiles/ui.rc @@ -0,0 +1,8 @@ + + + + &File + + + + diff --git a/kate/addons/kate/openheader/CMakeLists.txt b/kate/addons/kate/openheader/CMakeLists.txt new file mode 100644 index 00000000..ca373046 --- /dev/null +++ b/kate/addons/kate/openheader/CMakeLists.txt @@ -0,0 +1,26 @@ +########### next target ############### + +kde4_add_plugin(kateopenheaderplugin plugin_kateopenheader.cpp) + +target_link_libraries(kateopenheaderplugin + kateinterfaces + ${KDE4_KIO_LIBS} + ${KDE4_KPARTS_LIBS} + ${KDE4_KTEXTEDITOR_LIBS} +) + +install( + TARGETS kateopenheaderplugin + DESTINATION ${KDE4_PLUGIN_INSTALL_DIR} +) + +########### install files ############### + +install( + FILES ui.rc + DESTINATION ${KDE4_DATA_INSTALL_DIR}/kate/plugins/kateopenheader +) +install( + FILES kateopenheader.desktop + DESTINATION ${KDE4_SERVICES_INSTALL_DIR} +) diff --git a/kate/addons/kate/openheader/Messages.sh b/kate/addons/kate/openheader/Messages.sh new file mode 100644 index 00000000..3d2878ac --- /dev/null +++ b/kate/addons/kate/openheader/Messages.sh @@ -0,0 +1,3 @@ +#! /bin/sh +$EXTRACTRC *.rc >> rc.cpp +$XGETTEXT *.cpp -o $podir/kateopenheader.pot diff --git a/kate/addons/kate/openheader/kateopenheader.desktop b/kate/addons/kate/openheader/kateopenheader.desktop new file mode 100644 index 00000000..068b45cf --- /dev/null +++ b/kate/addons/kate/openheader/kateopenheader.desktop @@ -0,0 +1,116 @@ +[Desktop Entry] +Type=Service +ServiceTypes=Kate/Plugin +X-KDE-Library=kateopenheaderplugin +X-Kate-Version=2.8 +Name=Open Header +Name[ar]=افتح الترويسة +Name[ast]=Abrir cabecera +Name[bg]=Отваряне на header +Name[bs]=Otvaranje zaglavlja +Name[ca]=Obre capçaleres +Name[ca@valencia]=Obri capçaleres +Name[cs]=Otevřít hlavičku +Name[da]=Åbn headerfil +Name[de]=Header öffnen +Name[el]=Άνοιγμα κεφαλίδας +Name[en_GB]=Open Header +Name[es]=Abrir cabecera +Name[et]=Päiseavaja +Name[eu]=Ireki goiburua +Name[fi]=Avaa otsake +Name[fr]=Ouvrir un fichier d'en-tête +Name[ga]=Oscail Comhad Ceanntáisc +Name[gl]=Abrir a cabeceira +Name[he]=פתח קובץ כותרת +Name[hu]=Fejlécböngésző +Name[ia]=Aperi capite (header) +Name[it]=Apri intestazioni +Name[ja]=オープンヘッダ +Name[kk]=Айдарын ашу +Name[km]=បើក​បឋមកថា​ +Name[ko]=헤더 열기 +Name[lt]=Atverti antraštę +Name[lv]=Atvērt galveni +Name[mr]=हेडर उघडा +Name[nb]=Åpne deklarasjonsfil +Name[nds]=Koppdatei opmaken +Name[nl]=OpenHeader +Name[nn]=Opna deklarasjonsfil +Name[pa]=ਹੈੱਡਰ ਖੋਲ੍ਹੋ +Name[pl]=Otwórz plik nagłówkowy +Name[pt]=Abrir um Ficheiro de Inclusão +Name[pt_BR]=Abrir arquivo de inclusão +Name[ro]=Deschide antet +Name[ru]=Открытие заголовочного файла +Name[si]=ශීර්ෂය විවෘතකරන්න +Name[sk]=Otvoriť hlavičku +Name[sl]=Odpiranje glav +Name[sr]=Отварање заглавља +Name[sr@ijekavian]=Отварање заглавља +Name[sr@ijekavianlatin]=Otvaranje zaglavlja +Name[sr@latin]=Otvaranje zaglavlja +Name[sv]=Öppna deklarationsfil +Name[tg]=Кушодани сарлавҳа +Name[tr]=Başlığı Aç +Name[ug]=باشنى ئاچ +Name[uk]=Відкрити заголовок +Name[x-test]=xxOpen Headerxx +Name[zh_CN]=打开头文件 +Name[zh_TW]=開啟標頭 +Comment=Opens the corresponding .h/[.cpp|.c] file +Comment[ar]=يفتح ملف ‎.h/[.cpp|.c] ‎ المناسب +Comment[ast]=Abre'l correspondiente ficheru .h/[.cpp|.c] +Comment[bg]=Отваряне на съответните файлове .h/[.cpp|.c] +Comment[bs]=Otvara pridruženu .h/[.cpp|.c] datoteku +Comment[ca]=Obre el fitxer .h/[.cpp|.c] corresponent +Comment[ca@valencia]=Obri el fitxer .h/[.cpp|.c] corresponent +Comment[cs]=Otevře odpovídající soubor .h/[.cpp|.c] +Comment[da]=Åbner den tilhørende .h/[.cpp|.c]-fil +Comment[de]=Öffnet die zusammengehörigen .h/[.cpp|.c]-Dateien +Comment[el]=Άνοιγμα του αντίστοιχου αρχείου .h/[.cpp|.c] +Comment[en_GB]=Opens the corresponding .h/[.cpp|.c] file +Comment[es]=Abre el correspondiente archivo .h/[.cpp|.c] +Comment[et]=Vastava .h/[.cpp|.c] faili avamine +Comment[eu]=Dagokion .h/[.cpp|.c] fitxategia irekitzen du +Comment[fi]=Avaa vastaavan .h/[.cpp|.c]-tiedoston +Comment[fr]=Ouvre le fichier .h / [.cpp |.c] correspondant +Comment[ga]=Oscail an comhad .h/[.cpp|.c] a fhreagraíonn leis an gceann seo +Comment[gl]=Abre o ficheiro .h/[.cpp|.c] correspondente +Comment[he]=פותח את הקובץ המתאים (file.h, file.cpp) +Comment[hu]=Megnyitja a megfelelő .h/[.cpp|.c] fájlt +Comment[ia]=Aperi le correspondente file .h/[.cpp].c] +Comment[it]=Apre il file .h/[.cpp|.c] corrispondente +Comment[ja]=対応する .h/[.cpp|.c] ファイルを開きます +Comment[kk]=Сәйкесті .h/[.cpp|.c] файлын ашу +Comment[km]=បើក​ឯកសារ​ដែល​ត្រូវ​នឹង .h/[.cpp|.c] +Comment[ko]=해당하는 .h/[.cpp|.c] 파일 열기 +Comment[lt]=Atveria atitinkamą .h/[.cpp|.c] failą +Comment[lv]=Atver atbilstošo .h/[.cpp|.c] datni +Comment[mr]=संबंधित .h/[.cpp|.c] फाईल उघडतो +Comment[nb]=Åpner tilhørende .h/[.cpp|.c] fil +Comment[nds]=Maakt de tohören .h/.cpp/.c-Datei op +Comment[ne]=संगत .h/[.cpp|.c] फाइल खोल्दछ +Comment[nl]=Open het bijhorende .h/[.cpp|.c]-bestand +Comment[nn]=Opna tilhøyrande .h/[.cpp|.c]-fil +Comment[pa]=ਸਬੰਧਿਤ .h/[.cpp|.c] ਫਾਇਲ ਖੋਲ੍ਹੋ +Comment[pl]=Otwiera odpowiedni plik .h/[.cpp|.c] +Comment[pt]=Abre o ficheiro .h/[.cpp|.c] correspondente +Comment[pt_BR]=Abre o arquivo .h/[.cpp|.c] correspondente +Comment[ro]=Deschide fișierul .h/[.cpp|.c] corespunzător +Comment[ru]=Открывает соответствующий файл .h/[.cpp|.c] +Comment[si]=අදාල .h/[.cpp|.c] ගොනුව විවෘත කරයි +Comment[sk]=Otvorí korešpondujúci .h/[.cpp|.c] súbor +Comment[sl]=Odpre ustrezno datoteko .h/[.cpp|.c] +Comment[sr]=Отвара придружени .h/[.cpp|.c] фајл +Comment[sr@ijekavian]=Отвара придружени .h/[.cpp|.c] фајл +Comment[sr@ijekavianlatin]=Otvara pridruženi .h/[.cpp|.c] fajl +Comment[sr@latin]=Otvara pridruženi .h/[.cpp|.c] fajl +Comment[sv]=Öppnar den motsvarande .h/[.cpp|.c]-filen +Comment[tg]=Кушодани фаилҳои мувофиқи .h/[.cpp|.c] +Comment[tr]=Uyumlu .h/[.cpp|.c] dosyalarını açar +Comment[ug]=نۆۋەتتىكى ھۆججەت .h/[.cpp.c] غا ماس كەلگەن ھۆججەتنى ئاچىدۇ +Comment[uk]=Відкриває відповідний файл .h/[.cpp|.c] +Comment[x-test]=xxOpens the corresponding .h/[.cpp|.c] filexx +Comment[zh_CN]=打开对应于当前文件的 .h/[.cpp|.c] 文件 +Comment[zh_TW]=開啟相關的 .h/.cpp/.c 檔 diff --git a/kate/addons/kate/openheader/plugin_kateopenheader.cpp b/kate/addons/kate/openheader/plugin_kateopenheader.cpp new file mode 100644 index 00000000..1617cb9c --- /dev/null +++ b/kate/addons/kate/openheader/plugin_kateopenheader.cpp @@ -0,0 +1,170 @@ +/* This file is part of the KDE project + Copyright (C) 2001 Joseph Wenninger + Copyright (C) 2009 Erlend Hamberg + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "plugin_kateopenheader.h" +#include "moc_plugin_kateopenheader.cpp" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +K_PLUGIN_FACTORY(KateOpenHeaderFactory, registerPlugin();) +K_EXPORT_PLUGIN(KateOpenHeaderFactory(KAboutData("kateopenheader","kateopenheader",ki18n("Open Header"), "0.1", ki18n("Open header for a source file"), KAboutData::License_LGPL_V2)) ) + + +PluginViewKateOpenHeader::PluginViewKateOpenHeader(PluginKateOpenHeader *plugin, Kate::MainWindow *mainwindow) + : Kate::PluginView(mainwindow) + , Kate::XMLGUIClient(KateOpenHeaderFactory::componentData()) + , KTextEditor::Command() + , m_plugin(plugin) +{ + KAction *a = actionCollection()->addAction("file_openheader"); + a->setText(i18n("Open .h/.cpp/.c")); + a->setShortcut( Qt::Key_F12 ); + connect( a, SIGNAL(triggered(bool)), plugin, SLOT(slotOpenHeader()) ); + + mainwindow->guiFactory()->addClient (this); + + KTextEditor::CommandInterface* cmdIface = + qobject_cast( Kate::application()->editor() ); + + if( cmdIface ) { + cmdIface->registerCommand( this ); + } +} + +PluginViewKateOpenHeader::~PluginViewKateOpenHeader() +{ + mainWindow()->guiFactory()->removeClient (this); + + KTextEditor::CommandInterface* cmdIface = + qobject_cast( Kate::application()->editor() ); + + if( cmdIface ) { + cmdIface->unregisterCommand( this ); + } +} + +PluginKateOpenHeader::PluginKateOpenHeader( QObject* parent, const QList& ) + : Kate::Plugin ( (Kate::Application *)parent, "open-header-plugin" ) +{ +} + +PluginKateOpenHeader::~PluginKateOpenHeader() +{ +} + +Kate::PluginView *PluginKateOpenHeader::createView (Kate::MainWindow *mainWindow) +{ + return new PluginViewKateOpenHeader(this,mainWindow); +} + +void PluginKateOpenHeader::slotOpenHeader () +{ + if (!application()->activeMainWindow()) + return; + + KTextEditor::View * kv (application()->activeMainWindow()->activeView()); + if (!kv) return; + + KUrl url=kv->document()->url(); + if ((!url.isValid()) || (url.isEmpty())) return; + + QFileInfo info( url.toLocalFile() ); + QString extension = info.suffix().toLower(); + + QStringList headers( QStringList() << "h" << "H" << "hh" << "hpp" ); + QStringList sources( QStringList() << "c" << "cpp" << "cc" << "cp" << "cxx" ); + + if( sources.contains( extension ) ) { + tryOpen( url, headers ); + } else if ( headers.contains( extension ) ) { + tryOpen( url, sources ); + } +} + +void PluginKateOpenHeader::tryOpen( const KUrl& url, const QStringList& extensions ) +{ + if (!application()->activeMainWindow()) + return; + + kDebug() << "Trying to open " << url.pathOrUrl() << " with extensions " << extensions.join(" "); + QString basename = QFileInfo( url.path() ).baseName(); + KUrl newURL( url ); + for( QStringList::ConstIterator it = extensions.begin(); it != extensions.end(); ++it ) { + newURL.setFileName( basename + '.' + *it ); + if( KIO::NetAccess::exists( newURL , KIO::NetAccess::SourceSide, application()->activeMainWindow()->window()) ) + application()->activeMainWindow()->openUrl( newURL ); + newURL.setFileName( basename + '.' + (*it).toUpper() ); + if( KIO::NetAccess::exists( newURL , KIO::NetAccess::SourceSide, application()->activeMainWindow()->window()) ) + application()->activeMainWindow()->openUrl( newURL ); + } +} + +const QStringList& PluginViewKateOpenHeader::cmds() +{ + static QStringList l; + + if (l.empty()) { + l << "toggle-header"; + } + + return l; +} + +bool PluginViewKateOpenHeader::exec(KTextEditor::View *view, const QString &cmd, QString &msg) +{ + Q_UNUSED(view) + Q_UNUSED(cmd) + Q_UNUSED(msg) + + m_plugin->slotOpenHeader(); + return true; +} + +bool PluginViewKateOpenHeader::help(KTextEditor::View *view, const QString &cmd, QString &msg) +{ + Q_UNUSED(view) + Q_UNUSED(cmd) + + msg = "

toggle-header — switch between header and corresponding c/cpp file

" + "

usage: toggle-header

" + "

When editing C or C++ code, this command will switch between a header file and " + "its corresponding C/C++ file or vice verca.

" + "

For example, if you are editing myclass.cpp, toggle-header will change " + "to myclass.h if this file is available.

" + "

Pairs of the following filename suffixes will work:
" + " Header files: h, H, hh, hpp
" + " Source files: c, cpp, cc, cp, cxx

"; + + return true; +} diff --git a/kate/addons/kate/openheader/plugin_kateopenheader.h b/kate/addons/kate/openheader/plugin_kateopenheader.h new file mode 100644 index 00000000..43957a26 --- /dev/null +++ b/kate/addons/kate/openheader/plugin_kateopenheader.h @@ -0,0 +1,61 @@ +/* This file is part of the KDE project + Copyright (C) 2001 Joseph Wenninger + Copyright (C) 2009 Erlend Hamberg + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + + +#ifndef PLUGIN_KATEOPENHEADER_H +#define PLUGIN_KATEOPENHEADER_H + +#include +#include +#include + +class PluginKateOpenHeader : public Kate::Plugin +{ + Q_OBJECT + + public: + explicit PluginKateOpenHeader( QObject* parent = 0, const QList& = QList() ); + virtual ~PluginKateOpenHeader(); + + Kate::PluginView *createView (Kate::MainWindow *mainWindow); + + public slots: + void slotOpenHeader (); + void tryOpen( const KUrl& url, const QStringList& extensions ); +}; + +class PluginViewKateOpenHeader + : public Kate::PluginView + , public Kate::XMLGUIClient + , public KTextEditor::Command +{ + Q_OBJECT + public: + PluginViewKateOpenHeader(PluginKateOpenHeader* plugin, Kate::MainWindow *mainwindow); + virtual ~PluginViewKateOpenHeader(); + + virtual const QStringList &cmds (); + virtual bool exec (KTextEditor::View *view, const QString &cmd, QString &msg); + virtual bool help (KTextEditor::View *view, const QString &cmd, QString &msg); + + private: + PluginKateOpenHeader* m_plugin; +}; + +#endif // PLUGIN_KATEOPENHEADER_H diff --git a/kate/addons/kate/openheader/ui.rc b/kate/addons/kate/openheader/ui.rc new file mode 100644 index 00000000..fe1fdf08 --- /dev/null +++ b/kate/addons/kate/openheader/ui.rc @@ -0,0 +1,8 @@ + + + + &File + + + + diff --git a/kate/addons/kate/project/CMakeLists.txt b/kate/addons/kate/project/CMakeLists.txt new file mode 100644 index 00000000..c309e064 --- /dev/null +++ b/kate/addons/kate/project/CMakeLists.txt @@ -0,0 +1,41 @@ +set(kateprojectplugin_PART_SRCS + kateprojectplugin.cpp + kateprojectpluginview.cpp + kateproject.cpp + kateprojectworker.cpp + kateprojectitem.cpp + kateprojectview.cpp + kateprojectviewtree.cpp + kateprojecttreeviewcontextmenu.cpp + kateprojectinfoview.cpp + kateprojectcompletion.cpp + kateprojectindex.cpp + kateprojectinfoviewindex.cpp + kateprojectinfoviewcodeanalysis.cpp + kateprojectinfoviewnotes.cpp + kateprojectnew.cpp +) + +kde4_add_plugin(kateprojectplugin ${kateprojectplugin_PART_SRCS}) + +target_link_libraries(kateprojectplugin + ${KDE4_KDEUI_LIBS} + ${KDE4_KTEXTEDITOR_LIBS} + kateinterfaces +) + +########### install files ############### +install( + TARGETS kateprojectplugin + DESTINATION ${KDE4_PLUGIN_INSTALL_DIR} +) +install( + FILES + ui.rc + kateproject.example + DESTINATION ${KDE4_DATA_INSTALL_DIR}/kate/plugins/project +) +install( + FILES kateprojectplugin.desktop + DESTINATION ${KDE4_SERVICES_INSTALL_DIR} +) diff --git a/kate/addons/kate/project/Messages.sh b/kate/addons/kate/project/Messages.sh new file mode 100644 index 00000000..6acec51e --- /dev/null +++ b/kate/addons/kate/project/Messages.sh @@ -0,0 +1,3 @@ +#! /bin/sh +$EXTRACTRC *.rc >> rc.cpp +$XGETTEXT *.cpp -o $podir/kateproject.pot diff --git a/kate/addons/kate/project/ctags/readtags.c b/kate/addons/kate/project/ctags/readtags.c new file mode 100644 index 00000000..c0123afb --- /dev/null +++ b/kate/addons/kate/project/ctags/readtags.c @@ -0,0 +1,961 @@ +/* +* +* Copyright (c) 1996-2003, Darren Hiebert +* +* This source code is released into the public domain. +* +* This module contains functions for reading tag files. +* +*/ + +/* +* INCLUDE FILES +*/ +#include +#include +#include +#include +#include +#include /* to declare off_t */ + +#include "readtags.h" + +/* +* MACROS +*/ +#define TAB '\t' + + +/* +* DATA DECLARATIONS +*/ +typedef struct { + size_t size; + char *buffer; +} vstring; + +/* Information about current tag file */ +struct sTagFile { + /* has the file been opened and this structure initialized? */ + short initialized; + /* format of tag file */ + short format; + /* how is the tag file sorted? */ + sortType sortMethod; + /* pointer to file structure */ + FILE* fp; + /* file position of first character of `line' */ + off_t pos; + /* size of tag file in seekable positions */ + off_t size; + /* last line read */ + vstring line; + /* name of tag in last line read */ + vstring name; + /* defines tag search state */ + struct { + /* file position of last match for tag */ + off_t pos; + /* name of tag last searched for */ + const char *name; + /* length of name for partial matches */ + size_t nameLength; + /* peforming partial match */ + short partial; + /* ignoring case */ + short ignorecase; + } search; + /* miscellaneous extension fields */ + struct { + /* number of entries in `list' */ + unsigned short max; + /* list of key value pairs */ + tagExtensionField *list; + } fields; + /* buffers to be freed at close */ + struct { + /* name of program author */ + char *author; + /* name of program */ + char *name; + /* URL of distribution */ + char *url; + /* program version */ + char *version; + } program; +}; + +/* +* DATA DEFINITIONS +*/ +const char *const EmptyString = ""; +const char *const PseudoTagPrefix = "!_"; + +/* +* FUNCTION DEFINITIONS +*/ + +/* + * Compare two strings, ignoring case. + * Return 0 for match, < 0 for smaller, > 0 for bigger + * Make sure case is folded to uppercase in comparison (like for 'sort -f') + * This makes a difference when one of the chars lies between upper and lower + * ie. one of the chars [ \ ] ^ _ ` for ascii. (The '_' in particular !) + */ +static int struppercmp (const char *s1, const char *s2) +{ + int result; + do + { + result = toupper ((int) *s1) - toupper ((int) *s2); + } while (result == 0 && *s1++ != '\0' && *s2++ != '\0'); + return result; +} + +static int strnuppercmp (const char *s1, const char *s2, size_t n) +{ + int result; + do + { + result = toupper ((int) *s1) - toupper ((int) *s2); + } while (result == 0 && --n > 0 && *s1++ != '\0' && *s2++ != '\0'); + return result; +} + +static int growString (vstring *s) +{ + int result = 0; + size_t newLength; + char *newLine; + if (s->size == 0) + { + newLength = 128; + newLine = (char*) malloc (newLength); + *newLine = '\0'; + } + else + { + newLength = 2 * s->size; + newLine = (char*) realloc (s->buffer, newLength); + } + if (newLine == NULL) + perror ("string too large"); + else + { + s->buffer = newLine; + s->size = newLength; + result = 1; + } + return result; +} + +/* Copy name of tag out of tag line */ +static void copyName (tagFile *const file) +{ + size_t length; + const char *end = strchr (file->line.buffer, '\t'); + if (end == NULL) + { + end = strchr (file->line.buffer, '\n'); + if (end == NULL) + end = strchr (file->line.buffer, '\r'); + } + if (end != NULL) + length = end - file->line.buffer; + else + length = strlen (file->line.buffer); + while (length >= file->name.size) + growString (&file->name); + strncpy (file->name.buffer, file->line.buffer, length); + file->name.buffer [length] = '\0'; +} + +static int readTagLineRaw (tagFile *const file) +{ + int result = 1; + int reReadLine; + + /* If reading the line places any character other than a null or a + * newline at the last character position in the buffer (one less than + * the buffer size), then we must resize the buffer and reattempt to read + * the line. + */ + do + { + char *const pLastChar = file->line.buffer + file->line.size - 2; + char *line; + + file->pos = ftell (file->fp); + reReadLine = 0; + *pLastChar = '\0'; + line = fgets (file->line.buffer, (int) file->line.size, file->fp); + if (line == NULL) + { + /* read error */ + if (! feof (file->fp)) + perror ("readTagLine"); + result = 0; + } + else if (*pLastChar != '\0' && + *pLastChar != '\n' && *pLastChar != '\r') + { + /* buffer overflow */ + growString (&file->line); + fseek (file->fp, file->pos, SEEK_SET); + reReadLine = 1; + } + else + { + size_t i = strlen (file->line.buffer); + while (i > 0 && + (file->line.buffer [i - 1] == '\n' || file->line.buffer [i - 1] == '\r')) + { + file->line.buffer [i - 1] = '\0'; + --i; + } + } + } while (reReadLine && result); + if (result) + copyName (file); + return result; +} + +static int readTagLine (tagFile *const file) +{ + int result; + do + { + result = readTagLineRaw (file); + } while (result && *file->name.buffer == '\0'); + return result; +} + +static tagResult growFields (tagFile *const file) +{ + tagResult result = TagFailure; + unsigned short newCount = 2 * file->fields.max; + tagExtensionField *newFields = (tagExtensionField*) + realloc (file->fields.list, newCount * sizeof (tagExtensionField)); + if (newFields == NULL) + perror ("too many extension fields"); + else + { + file->fields.list = newFields; + file->fields.max = newCount; + result = TagSuccess; + } + return result; +} + +static void parseExtensionFields (tagFile *const file, tagEntry *const entry, + char *const string) +{ + char *p = string; + while (p != NULL && *p != '\0') + { + while (*p == TAB) + *p++ = '\0'; + if (*p != '\0') + { + char *colon; + char *field = p; + p = strchr (p, TAB); + if (p != NULL) + *p++ = '\0'; + colon = strchr (field, ':'); + if (colon == NULL) + entry->kind = field; + else + { + const char *key = field; + const char *value = colon + 1; + *colon = '\0'; + if (strcmp (key, "kind") == 0) + entry->kind = value; + else if (strcmp (key, "file") == 0) + entry->fileScope = 1; + else if (strcmp (key, "line") == 0) + entry->address.lineNumber = atol (value); + else + { + if (entry->fields.count == file->fields.max) + growFields (file); + file->fields.list [entry->fields.count].key = key; + file->fields.list [entry->fields.count].value = value; + ++entry->fields.count; + } + } + } + } +} + +static void parseTagLine (tagFile *file, tagEntry *const entry) +{ + int i; + char *p = file->line.buffer; + char *tab = strchr (p, TAB); + int fieldsPresent = 0; + + entry->fields.list = NULL; + entry->fields.count = 0; + entry->kind = NULL; + entry->fileScope = 0; + + entry->name = p; + if (tab != NULL) + { + *tab = '\0'; + p = tab + 1; + entry->file = p; + tab = strchr (p, TAB); + if (tab != NULL) + { + *tab = '\0'; + p = tab + 1; + if (*p == '/' || *p == '?') + { + /* parse pattern */ + int delimiter = *(unsigned char*) p; + entry->address.lineNumber = 0; + entry->address.pattern = p; + do + { + p = strchr (p + 1, delimiter); + } while (p != NULL && *(p - 1) == '\\'); + if (p == NULL) + { + /* invalid pattern */ + } + else + ++p; + } + else if (isdigit ((int) *(unsigned char*) p)) + { + /* parse line number */ + entry->address.pattern = p; + entry->address.lineNumber = atol (p); + while (isdigit ((int) *(unsigned char*) p)) + ++p; + } + else + { + /* invalid pattern */ + } + if ( p != NULL ) + { + fieldsPresent = (strncmp (p, ";\"", 2) == 0); + *p = '\0'; + if (fieldsPresent) + parseExtensionFields (file, entry, p + 2); + } + } + } + if (entry->fields.count > 0) + entry->fields.list = file->fields.list; + for (i = entry->fields.count ; i < file->fields.max ; ++i) + { + file->fields.list [i].key = NULL; + file->fields.list [i].value = NULL; + } +} + +static char *duplicate (const char *str) +{ + char *result = NULL; + if (str != NULL) + { + result = (char*) malloc (strlen (str) + 1); + if (result == NULL) + perror ("malloc"); + else + strcpy (result, str); + } + return result; +} + +static void readPseudoTags (tagFile *const file, tagFileInfo *const info) +{ + fpos_t startOfLine; + const size_t prefixLength = strlen (PseudoTagPrefix); + if (info != NULL) + { + info->file.format = 1; + info->file.sort = TAG_UNSORTED; + info->program.author = NULL; + info->program.name = NULL; + info->program.url = NULL; + info->program.version = NULL; + } + while (1) + { + fgetpos (file->fp, &startOfLine); + if (! readTagLine (file)) + break; + if (strncmp (file->line.buffer, PseudoTagPrefix, prefixLength) != 0) + break; + else + { + tagEntry entry; + const char *key, *value; + parseTagLine (file, &entry); + key = entry.name + prefixLength; + value = entry.file; + if (strcmp (key, "TAG_FILE_SORTED") == 0) + file->sortMethod = (sortType) atoi (value); + else if (strcmp (key, "TAG_FILE_FORMAT") == 0) + file->format = atoi (value); + else if (strcmp (key, "TAG_PROGRAM_AUTHOR") == 0) + file->program.author = duplicate (value); + else if (strcmp (key, "TAG_PROGRAM_NAME") == 0) + file->program.name = duplicate (value); + else if (strcmp (key, "TAG_PROGRAM_URL") == 0) + file->program.url = duplicate (value); + else if (strcmp (key, "TAG_PROGRAM_VERSION") == 0) + file->program.version = duplicate (value); + if (info != NULL) + { + info->file.format = file->format; + info->file.sort = file->sortMethod; + info->program.author = file->program.author; + info->program.name = file->program.name; + info->program.url = file->program.url; + info->program.version = file->program.version; + } + } + } + fsetpos (file->fp, &startOfLine); +} + +static void gotoFirstLogicalTag (tagFile *const file) +{ + fpos_t startOfLine; + const size_t prefixLength = strlen (PseudoTagPrefix); + rewind (file->fp); + while (1) + { + fgetpos (file->fp, &startOfLine); + if (! readTagLine (file)) + break; + if (strncmp (file->line.buffer, PseudoTagPrefix, prefixLength) != 0) + break; + } + fsetpos (file->fp, &startOfLine); +} + +static tagFile *initialize (const char *const filePath, tagFileInfo *const info) +{ + tagFile *result = (tagFile*) malloc (sizeof (tagFile)); + if (result != NULL) + { + memset (result, 0, sizeof (tagFile)); + growString (&result->line); + growString (&result->name); + result->fields.max = 20; + result->fields.list = (tagExtensionField*) malloc ( + result->fields.max * sizeof (tagExtensionField)); + result->fp = fopen (filePath, "r"); + if (result->fp == NULL) + { + free (result); + result = NULL; + info->status.error_number = errno; + } + else + { + fseek (result->fp, 0, SEEK_END); + result->size = ftell (result->fp); + rewind (result->fp); + readPseudoTags (result, info); + info->status.opened = 1; + result->initialized = 1; + } + } + return result; +} + +static void terminate (tagFile *const file) +{ + fclose (file->fp); + + free (file->line.buffer); + free (file->name.buffer); + free (file->fields.list); + + if (file->program.author != NULL) + free (file->program.author); + if (file->program.name != NULL) + free (file->program.name); + if (file->program.url != NULL) + free (file->program.url); + if (file->program.version != NULL) + free (file->program.version); + + memset (file, 0, sizeof (tagFile)); + + free (file); +} + +static tagResult readNext (tagFile *const file, tagEntry *const entry) +{ + tagResult result = TagFailure; + if (file == NULL || ! file->initialized) + result = TagFailure; + else if (! readTagLine (file)) + result = TagFailure; + else + { + if (entry != NULL) + parseTagLine (file, entry); + result = TagSuccess; + } + return result; +} + +static const char *readFieldValue ( + const tagEntry *const entry, const char *const key) +{ + const char *result = NULL; + int i; + if (strcmp (key, "kind") == 0) + result = entry->kind; + else if (strcmp (key, "file") == 0) + result = EmptyString; + else for (i = 0 ; i < entry->fields.count && result == NULL ; ++i) + if (strcmp (entry->fields.list [i].key, key) == 0) + result = entry->fields.list [i].value; + return result; +} + +static int readTagLineSeek (tagFile *const file, const off_t pos) +{ + int result = 0; + if (fseek (file->fp, pos, SEEK_SET) == 0) + { + result = readTagLine (file); /* read probable partial line */ + if (pos > 0 && result) + result = readTagLine (file); /* read complete line */ + } + return result; +} + +static int nameComparison (tagFile *const file) +{ + int result; + if (file->search.ignorecase) + { + if (file->search.partial) + result = strnuppercmp (file->search.name, file->name.buffer, + file->search.nameLength); + else + result = struppercmp (file->search.name, file->name.buffer); + } + else + { + if (file->search.partial) + result = strncmp (file->search.name, file->name.buffer, + file->search.nameLength); + else + result = strcmp (file->search.name, file->name.buffer); + } + return result; +} + +static void findFirstNonMatchBefore (tagFile *const file) +{ +#define JUMP_BACK 512 + int more_lines; + int comp; + off_t start = file->pos; + off_t pos = start; + do + { + if (pos < (off_t) JUMP_BACK) + pos = 0; + else + pos = pos - JUMP_BACK; + more_lines = readTagLineSeek (file, pos); + comp = nameComparison (file); + } while (more_lines && comp == 0 && pos > 0 && pos < start); +} + +static tagResult findFirstMatchBefore (tagFile *const file) +{ + tagResult result = TagFailure; + int more_lines; + off_t start = file->pos; + findFirstNonMatchBefore (file); + do + { + more_lines = readTagLine (file); + if (nameComparison (file) == 0) + result = TagSuccess; + } while (more_lines && result != TagSuccess && file->pos < start); + return result; +} + +static tagResult findBinary (tagFile *const file) +{ + tagResult result = TagFailure; + off_t lower_limit = 0; + off_t upper_limit = file->size; + off_t last_pos = 0; + off_t pos = upper_limit / 2; + while (result != TagSuccess) + { + if (! readTagLineSeek (file, pos)) + { + /* in case we fell off end of file */ + result = findFirstMatchBefore (file); + break; + } + else if (pos == last_pos) + { + /* prevent infinite loop if we backed up to beginning of file */ + break; + } + else + { + const int comp = nameComparison (file); + last_pos = pos; + if (comp < 0) + { + upper_limit = pos; + pos = lower_limit + ((upper_limit - lower_limit) / 2); + } + else if (comp > 0) + { + lower_limit = pos; + pos = lower_limit + ((upper_limit - lower_limit) / 2); + } + else if (pos == 0) + result = TagSuccess; + else + result = findFirstMatchBefore (file); + } + } + return result; +} + +static tagResult findSequential (tagFile *const file) +{ + tagResult result = TagFailure; + if (file->initialized) + { + while (result == TagFailure && readTagLine (file)) + { + if (nameComparison (file) == 0) + result = TagSuccess; + } + } + return result; +} + +static tagResult find (tagFile *const file, tagEntry *const entry, + const char *const name, const int options) +{ + tagResult result = TagFailure; + file->search.name = name; + file->search.nameLength = strlen (name); + file->search.partial = (options & TAG_PARTIALMATCH) != 0; + file->search.ignorecase = (options & TAG_IGNORECASE) != 0; + fseek (file->fp, 0, SEEK_END); + file->size = ftell (file->fp); + rewind (file->fp); + if ((file->sortMethod == TAG_SORTED && !file->search.ignorecase) || + (file->sortMethod == TAG_FOLDSORTED && file->search.ignorecase)) + { +#ifdef DEBUG + printf ("\n"); +#endif + result = findBinary (file); + } + else + { +#ifdef DEBUG + printf ("\n"); +#endif + result = findSequential (file); + } + + if (result != TagSuccess) + file->search.pos = file->size; + else + { + file->search.pos = file->pos; + if (entry != NULL) + parseTagLine (file, entry); + } + return result; +} + +static tagResult findNext (tagFile *const file, tagEntry *const entry) +{ + tagResult result = TagFailure; + if ((file->sortMethod == TAG_SORTED && !file->search.ignorecase) || + (file->sortMethod == TAG_FOLDSORTED && file->search.ignorecase)) + { + result = tagsNext (file, entry); + if (result == TagSuccess && nameComparison (file) != 0) + result = TagFailure; + } + else + { + result = findSequential (file); + if (result == TagSuccess && entry != NULL) + parseTagLine (file, entry); + } + return result; +} + +/* +* EXTERNAL INTERFACE +*/ + +extern tagFile *tagsOpen (const char *const filePath, tagFileInfo *const info) +{ + return initialize (filePath, info); +} + +extern tagResult tagsSetSortType (tagFile *const file, const sortType type) +{ + tagResult result = TagFailure; + if (file != NULL && file->initialized) + { + file->sortMethod = type; + result = TagSuccess; + } + return result; +} + +extern tagResult tagsFirst (tagFile *const file, tagEntry *const entry) +{ + tagResult result = TagFailure; + if (file != NULL && file->initialized) + { + gotoFirstLogicalTag (file); + result = readNext (file, entry); + } + return result; +} + +extern tagResult tagsNext (tagFile *const file, tagEntry *const entry) +{ + tagResult result = TagFailure; + if (file != NULL && file->initialized) + result = readNext (file, entry); + return result; +} + +extern const char *tagsField (const tagEntry *const entry, const char *const key) +{ + const char *result = NULL; + if (entry != NULL) + result = readFieldValue (entry, key); + return result; +} + +extern tagResult tagsFind (tagFile *const file, tagEntry *const entry, + const char *const name, const int options) +{ + tagResult result = TagFailure; + if (file != NULL && file->initialized) + result = find (file, entry, name, options); + return result; +} + +extern tagResult tagsFindNext (tagFile *const file, tagEntry *const entry) +{ + tagResult result = TagFailure; + if (file != NULL && file->initialized) + result = findNext (file, entry); + return result; +} + +extern tagResult tagsClose (tagFile *const file) +{ + tagResult result = TagFailure; + if (file != NULL && file->initialized) + { + terminate (file); + result = TagSuccess; + } + return result; +} + +/* +* TEST FRAMEWORK +*/ + +#ifdef READTAGS_MAIN + +static const char *TagFileName = "tags"; +static const char *ProgramName; +static int extensionFields; +static int SortOverride; +static sortType SortMethod; + +static void printTag (const tagEntry *entry) +{ + int i; + int first = 1; + const char* separator = ";\""; + const char* const empty = ""; +/* "sep" returns a value only the first time it is evaluated */ +#define sep (first ? (first = 0, separator) : empty) + printf ("%s\t%s\t%s", + entry->name, entry->file, entry->address.pattern); + if (extensionFields) + { + if (entry->kind != NULL && entry->kind [0] != '\0') + printf ("%s\tkind:%s", sep, entry->kind); + if (entry->fileScope) + printf ("%s\tfile:", sep); +#if 0 + if (entry->address.lineNumber > 0) + printf ("%s\tline:%lu", sep, entry->address.lineNumber); +#endif + for (i = 0 ; i < entry->fields.count ; ++i) + printf ("%s\t%s:%s", sep, entry->fields.list [i].key, + entry->fields.list [i].value); + } + putchar ('\n'); +#undef sep +} + +static void findTag (const char *const name, const int options) +{ + tagFileInfo info; + tagEntry entry; + tagFile *const file = tagsOpen (TagFileName, &info); + if (file == NULL) + { + fprintf (stderr, "%s: cannot open tag file: %s: %s\n", + ProgramName, strerror (info.status.error_number), name); + exit (1); + } + else + { + if (SortOverride) + tagsSetSortType (file, SortMethod); + if (tagsFind (file, &entry, name, options) == TagSuccess) + { + do + { + printTag (&entry); + } while (tagsFindNext (file, &entry) == TagSuccess); + } + tagsClose (file); + } +} + +static void listTags (void) +{ + tagFileInfo info; + tagEntry entry; + tagFile *const file = tagsOpen (TagFileName, &info); + if (file == NULL) + { + fprintf (stderr, "%s: cannot open tag file: %s: %s\n", + ProgramName, strerror (info.status.error_number), TagFileName); + exit (1); + } + else + { + while (tagsNext (file, &entry) == TagSuccess) + printTag (&entry); + tagsClose (file); + } +} + +const char *const Usage = + "Find tag file entries matching specified names.\n\n" + "Usage: %s [-ilp] [-s[0|1]] [-t file] [name(s)]\n\n" + "Options:\n" + " -e Include extension fields in output.\n" + " -i Perform case-insensitive matching.\n" + " -l List all tags.\n" + " -p Perform partial matching.\n" + " -s[0|1|2] Override sort detection of tag file.\n" + " -t file Use specified tag file (default: \"tags\").\n" + "Note that options are acted upon as encountered, so order is significant.\n"; + +extern int main (int argc, char **argv) +{ + int options = 0; + int actionSupplied = 0; + int i; + ProgramName = argv [0]; + if (argc == 1) + { + fprintf (stderr, Usage, ProgramName); + exit (1); + } + for (i = 1 ; i < argc ; ++i) + { + const char *const arg = argv [i]; + if (arg [0] != '-') + { + findTag (arg, options); + actionSupplied = 1; + } + else + { + size_t j; + for (j = 1 ; arg [j] != '\0' ; ++j) + { + switch (arg [j]) + { + case 'e': extensionFields = 1; break; + case 'i': options |= TAG_IGNORECASE; break; + case 'p': options |= TAG_PARTIALMATCH; break; + case 'l': listTags (); actionSupplied = 1; break; + + case 't': + if (arg [j+1] != '\0') + { + TagFileName = arg + j + 1; + j += strlen (TagFileName); + } + else if (i + 1 < argc) + TagFileName = argv [++i]; + else + { + fprintf (stderr, Usage, ProgramName); + exit (1); + } + break; + case 's': + SortOverride = 1; + ++j; + if (arg [j] == '\0') + SortMethod = TAG_SORTED; + else if (strchr ("012", arg[j]) != NULL) + SortMethod = (sortType) (arg[j] - '0'); + else + { + fprintf (stderr, Usage, ProgramName); + exit (1); + } + break; + default: + fprintf (stderr, "%s: unknown option: %c\n", + ProgramName, arg[j]); + exit (1); + break; + } + } + } + } + if (! actionSupplied) + { + fprintf (stderr, + "%s: no action specified: specify tag name(s) or -l option\n", + ProgramName); + exit (1); + } + return 0; +} + +#endif + +/* vi:set tabstop=8 shiftwidth=4: */ diff --git a/kate/addons/kate/project/ctags/readtags.h b/kate/addons/kate/project/ctags/readtags.h new file mode 100644 index 00000000..59428a8d --- /dev/null +++ b/kate/addons/kate/project/ctags/readtags.h @@ -0,0 +1,251 @@ +/* +* +* Copyright (c) 1996-2003, Darren Hiebert +* +* This source code is released for the public domain. +* +* This file defines the public interface for looking up tag entries in tag +* files. +* +* The functions defined in this interface are intended to provide tag file +* support to a software tool. The tag lookups provided are sufficiently fast +* enough to permit opening a sorted tag file, searching for a matching tag, +* then closing the tag file each time a tag is looked up (search times are +* on the order of hundreths of a second, even for huge tag files). This is +* the recommended use of this library for most tool applications. Adhering +* to this approach permits a user to regenerate a tag file at will without +* the tool needing to detect and resynchronize with changes to the tag file. +* Even for an unsorted 24MB tag file, tag searches take about one second. +*/ +#ifndef READTAGS_H +#define READTAGS_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* +* MACROS +*/ + +/* Options for tagsSetSortType() */ +typedef enum { + TAG_UNSORTED, TAG_SORTED, TAG_FOLDSORTED +} sortType ; + +/* Options for tagsFind() */ +#define TAG_FULLMATCH 0x0 +#define TAG_PARTIALMATCH 0x1 + +#define TAG_OBSERVECASE 0x0 +#define TAG_IGNORECASE 0x2 + +/* +* DATA DECLARATIONS +*/ + +typedef enum { TagFailure = 0, TagSuccess = 1 } tagResult; + +struct sTagFile; + +typedef struct sTagFile tagFile; + +/* This structure contains information about the tag file. */ +typedef struct { + + struct { + /* was the tag file successfully opened? */ + int opened; + + /* errno value when 'opened' is false */ + int error_number; + } status; + + /* information about the structure of the tag file */ + struct { + /* format of tag file (1 = original, 2 = extended) */ + short format; + + /* how is the tag file sorted? */ + sortType sort; + } file; + + + /* information about the program which created this tag file */ + struct { + /* name of author of generating program (may be null) */ + const char *author; + + /* name of program (may be null) */ + const char *name; + + /* URL of distribution (may be null) */ + const char *url; + + /* program version (may be null) */ + const char *version; + } program; + +} tagFileInfo; + +/* This structure contains information about an extension field for a tag. + * These exist at the end of the tag in the form "key:value"). + */ +typedef struct { + + /* the key of the extension field */ + const char *key; + + /* the value of the extension field (may be an empty string) */ + const char *value; + +} tagExtensionField; + +/* This structure contains information about a specific tag. */ +typedef struct { + + /* name of tag */ + const char *name; + + /* path of source file containing definition of tag */ + const char *file; + + /* address for locating tag in source file */ + struct { + /* pattern for locating source line + * (may be NULL if not present) */ + const char *pattern; + + /* line number in source file of tag definition + * (may be zero if not known) */ + unsigned long lineNumber; + } address; + + /* kind of tag (may by name, character, or NULL if not known) */ + const char *kind; + + /* is tag of file-limited scope? */ + short fileScope; + + /* miscellaneous extension fields */ + struct { + /* number of entries in `list' */ + unsigned short count; + + /* list of key value pairs */ + tagExtensionField *list; + } fields; + +} tagEntry; + + +/* +* FUNCTION PROTOTYPES +*/ + +/* +* This function must be called before calling other functions in this +* library. It is passed the path to the tag file to read and a (possibly +* null) pointer to a structure which, if not null, will be populated with +* information about the tag file. If successful, the function will return a +* handle which must be supplied to other calls to read information from the +* tag file, and info.status.opened will be set to true. If unsuccessful, +* info.status.opened will be set to false and info.status.error_number will +* be set to the errno value representing the system error preventing the tag +* file from being successfully opened. +*/ +extern tagFile *tagsOpen (const char *const filePath, tagFileInfo *const info); + +/* +* This function allows the client to override the normal automatic detection +* of how a tag file is sorted. Permissible values for `type' are +* TAG_UNSORTED, TAG_SORTED, TAG_FOLDSORTED. Tag files in the new extended +* format contain a key indicating whether or not they are sorted. However, +* tag files in the original format do not contain such a key even when +* sorted, preventing this library from taking advantage of fast binary +* lookups. If the client knows that such an unmarked tag file is indeed +* sorted (or not), it can override the automatic detection. Note that +* incorrect lookup results will result if a tag file is marked as sorted when +* it actually is not. The function will return TagSuccess if called on an +* open tag file or TagFailure if not. +*/ +extern tagResult tagsSetSortType (tagFile *const file, const sortType type); + +/* +* Reads the first tag in the file, if any. It is passed the handle to an +* opened tag file and a (possibly null) pointer to a structure which, if not +* null, will be populated with information about the first tag file entry. +* The function will return TagSuccess another tag entry is found, or +* TagFailure if not (i.e. it reached end of file). +*/ +extern tagResult tagsFirst (tagFile *const file, tagEntry *const entry); + +/* +* Step to the next tag in the file, if any. It is passed the handle to an +* opened tag file and a (possibly null) pointer to a structure which, if not +* null, will be populated with information about the next tag file entry. The +* function will return TagSuccess another tag entry is found, or TagFailure +* if not (i.e. it reached end of file). It will always read the first tag in +* the file immediately after calling tagsOpen(). +*/ +extern tagResult tagsNext (tagFile *const file, tagEntry *const entry); + +/* +* Retrieve the value associated with the extension field for a specified key. +* It is passed a pointer to a structure already populated with values by a +* previous call to tagsNext(), tagsFind(), or tagsFindNext(), and a string +* containing the key of the desired extension field. If no such field of the +* specified key exists, the function will return null. +*/ +extern const char *tagsField (const tagEntry *const entry, const char *const key); + +/* +* Find the first tag matching `name'. The structure pointed to by `entry' +* will be populated with information about the tag file entry. If a tag file +* is sorted using the C locale, a binary search algorithm is used to search +* the tag file, resulting in very fast tag lookups, even in huge tag files. +* Various options controlling the matches can be combined by bit-wise or-ing +* certain values together. The available values are: +* +* TAG_PARTIALMATCH +* Tags whose leading characters match `name' will qualify. +* +* TAG_FULLMATCH +* Only tags whose full lengths match `name' will qualify. +* +* TAG_IGNORECASE +* Matching will be performed in a case-insenstive manner. Note that +* this disables binary searches of the tag file. +* +* TAG_OBSERVECASE +* Matching will be performed in a case-senstive manner. Note that +* this enables binary searches of the tag file. +* +* The function will return TagSuccess if a tag matching the name is found, or +* TagFailure if not. +*/ +extern tagResult tagsFind (tagFile *const file, tagEntry *const entry, const char *const name, const int options); + +/* +* Find the next tag matching the name and options supplied to the most recent +* call to tagsFind() for the same tag file. The structure pointed to by +* `entry' will be populated with information about the tag file entry. The +* function will return TagSuccess if another tag matching the name is found, +* or TagFailure if not. +*/ +extern tagResult tagsFindNext (tagFile *const file, tagEntry *const entry); + +/* +* Call tagsTerminate() at completion of reading the tag file, which will +* close the file and free any internal memory allocated. The function will +* return TagFailure is no file is currently open, TagSuccess otherwise. +*/ +extern tagResult tagsClose (tagFile *const file); + +#ifdef __cplusplus +} +#endif + +#endif + +/* vi:set tabstop=8 shiftwidth=4: */ diff --git a/kate/addons/kate/project/kateproject.cpp b/kate/addons/kate/project/kateproject.cpp new file mode 100644 index 00000000..7b72564b --- /dev/null +++ b/kate/addons/kate/project/kateproject.cpp @@ -0,0 +1,393 @@ +/* This file is part of the Kate project. + * + * Copyright (C) 2012 Christoph Cullmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "kateproject.h" +#include "kateprojectworker.h" + +#include + +#include + +#include +#include +#include +#include +#include + +KateProject::KateProject () + : QObject () + , m_worker (new KateProjectWorker (this)) + , m_thread (m_worker) + , m_notesDocument (0) + , m_documentsParent (0) +{ + /** + * move worker object over and start our worker thread + * thread will delete worker on run() exit + */ + m_worker->moveToThread (&m_thread); + m_thread.start (); +} + +KateProject::~KateProject () +{ + /** + * only do this once + */ + Q_ASSERT (m_worker); + + /** + * quit the thread event loop and wait for completion + * will delete worker on thread run() exit + */ + m_thread.quit (); + m_thread.wait (); + + /** + * marks as deleted + */ + m_worker = 0; + + /** + * save notes document, if any + */ + saveNotesDocument (); +} + +bool KateProject::load (const QString &fileName) +{ + /** + * bail out if already fileName set! + */ + if (!m_fileName.isEmpty()) + return false; + + /** + * set new filename and base directory + */ + m_fileName = fileName; + m_baseDir = QFileInfo(m_fileName).canonicalPath(); + + /** + * trigger reload + */ + return reload (); +} + +bool KateProject::reload (bool force) +{ + /** + * open the file for reading, bail out on error! + */ + QFile file (m_fileName); + if (!file.open (QIODevice::ReadOnly | QIODevice::Text)) + return false; + + /** + * parse the whole file, bail out again on error! + */ + QJsonDocument jsondoc = QJsonDocument::fromJson(file.readAll()); + if (jsondoc.isNull()) { + kWarning() << jsondoc.errorString(); + return false; + } + QVariant project = jsondoc.toVariant(); + + /** + * now: get global group + */ + QVariantMap globalProject = project.toMap (); + + /** + * no name, bad => bail out + */ + if (globalProject["name"].toString().isEmpty()) + return false; + + /** + * support out-of-source project files + */ + if (!globalProject["directory"].toString().isEmpty()) + m_baseDir = QFileInfo (globalProject["directory"].toString()).canonicalFilePath (); + + /** + * anything changed? + * else be done without forced reload! + */ + if (!force && (m_projectMap == globalProject)) + return true; + + /** + * setup global attributes in this object + */ + m_projectMap = globalProject; + + /** + * emit that we changed stuff + */ + emit projectMapChanged (); + + /** + * trigger worker to REALLY load the project model and stuff + */ + QMetaObject::invokeMethod (m_worker, "loadProject", Qt::QueuedConnection, Q_ARG(QString, m_baseDir), Q_ARG(QVariantMap, m_projectMap)); + + /** + * done ok ;) + */ + return true; +} + +void KateProject::loadProjectDone (KateProjectSharedQStandardItem topLevel, KateProjectSharedQMapStringItem file2Item) +{ + /** + * setup model data + */ + m_model.clear (); + m_model.invisibleRootItem()->appendColumn (topLevel->takeColumn (0)); + + /** + * setup file => item map + */ + m_file2Item = file2Item; + + /** + * readd the documents that are open atm + */ + m_documentsParent = 0; + foreach (KTextEditor::Document *document, m_documents.keys ()) + registerDocument (document); + + /** + * model changed + */ + emit modelChanged (); +} + +void KateProject::loadIndexDone (KateProjectSharedProjectIndex projectIndex) +{ + /** + * move to our project + */ + m_projectIndex = projectIndex; + + /** + * notify external world that data is available + */ + emit indexChanged (); +} + +QFile *KateProject::projectLocalFile (const QString &file) const +{ + /** + * nothing on empty file names for project + * should not happen + */ + if (m_fileName.isEmpty()) + return 0; + + /** + * create dir to store local files, else fail + */ + if (!QDir().mkpath (m_fileName+".d")) + return 0; + + /** + * try to open file read-write + */ + QFile *readWriteFile = new QFile (m_fileName + ".d" + QDir::separator() + file); + if (!readWriteFile->open (QIODevice::ReadWrite)) { + delete readWriteFile; + return 0; + } + + /** + * all fine, return file + */ + return readWriteFile; +} + +QTextDocument* KateProject::notesDocument () +{ + /** + * already there? + */ + if (m_notesDocument) + return m_notesDocument; + + /** + * else create it + */ + m_notesDocument = new QTextDocument (this); + m_notesDocument->setDocumentLayout (new QPlainTextDocumentLayout (m_notesDocument)); + + /** + * and load text if possible + */ + if (QFile *inFile = projectLocalFile ("notes.txt")) { + { + QTextStream inStream (inFile); + inStream.setCodec ("UTF-8"); + m_notesDocument->setPlainText (inStream.readAll ()); + } + delete inFile; + } + + /** + * and be done + */ + return m_notesDocument; +} + +void KateProject::saveNotesDocument () +{ + /** + * no notes document, nothing to do + */ + if (!m_notesDocument) + return; + + /** + * try to get file to save to + */ + if (QFile *outFile = projectLocalFile ("notes.txt")) { + outFile->resize (0); + { + QTextStream outStream (outFile); + outStream.setCodec ("UTF-8"); + outStream << m_notesDocument->toPlainText (); + } + delete outFile; + } +} + + +void KateProject::slotModifiedChanged(KTextEditor::Document* document) { + KateProjectItem *item = itemForFile (m_documents.value (document)); + + if (!item) return; + + item->slotModifiedChanged(document); +} + +void KateProject::slotModifiedOnDisk (KTextEditor::Document *document, + bool isModified, KTextEditor::ModificationInterface::ModifiedOnDiskReason reason) { + + KateProjectItem *item = itemForFile (m_documents.value (document)); + + if (!item) return; + + item->slotModifiedOnDisk(document,isModified, reason); + +} + + +void KateProject::registerDocument (KTextEditor::Document *document) +{ + // remember the document, if not already there + if (!m_documents.contains(document)) + m_documents[document] = document->url().toLocalFile (); + + // try to get item for the document + KateProjectItem *item = itemForFile (document->url().toLocalFile ()); + + // if we got one, we are done, else create a dummy! + if (item) { + disconnect(document,SIGNAL(modifiedChanged(KTextEditor::Document *)),this,SLOT(slotModifiedChanged(KTextEditor::Document *))); + disconnect(document,SIGNAL(modifiedOnDisk(KTextEditor::Document*,bool,KTextEditor::ModificationInterface::ModifiedOnDiskReason)),this,SLOT(slotModifiedOnDisk(KTextEditor::Document*,bool,KTextEditor::ModificationInterface::ModifiedOnDiskReason))); + item->slotModifiedChanged(document); + +/*FIXME item->slotModifiedOnDisk(document,document->isModified(),qobject_cast(document)->modifiedOnDisk()); FIXME*/ + + connect(document,SIGNAL(modifiedChanged(KTextEditor::Document *)),this,SLOT(slotModifiedChanged(KTextEditor::Document *))); + connect(document,SIGNAL(modifiedOnDisk(KTextEditor::Document*,bool,KTextEditor::ModificationInterface::ModifiedOnDiskReason)),this,SLOT(slotModifiedOnDisk(KTextEditor::Document*,bool,KTextEditor::ModificationInterface::ModifiedOnDiskReason))); + + return; + } + + // perhaps create the parent item + if (!m_documentsParent) { + m_documentsParent = new KateProjectItem (KateProjectItem::Directory, i18n ("")); + m_model.insertRow (0, m_documentsParent); + } + + // create document item + QFileInfo fileInfo (document->url().toLocalFile ()); + KateProjectItem *fileItem = new KateProjectItem (KateProjectItem::File, fileInfo.fileName()); + fileItem->setData(document->url().toLocalFile (), Qt::ToolTipRole); + fileItem->slotModifiedChanged(document); + connect(document,SIGNAL(modifiedChanged(KTextEditor::Document *)),this,SLOT(slotModifiedChanged(KTextEditor::Document *))); + connect(document,SIGNAL(modifiedOnDisk(KTextEditor::Document*,bool,KTextEditor::ModificationInterface::ModifiedOnDiskReason)),this,SLOT(slotModifiedOnDisk(KTextEditor::Document*,bool,KTextEditor::ModificationInterface::ModifiedOnDiskReason))); + + + bool inserted = false; + for (int i = 0; i < m_documentsParent->rowCount(); ++i) { + if (m_documentsParent->child (i)->data(Qt::UserRole).toString() > document->url().toLocalFile ()) { + m_documentsParent->insertRow (i, fileItem); + inserted = true; + break; + } + } + if (!inserted) + m_documentsParent->appendRow (fileItem); + + fileItem->setData (document->url().toLocalFile (), Qt::UserRole); + fileItem->setData (QVariant (true), Qt::UserRole + 3); + + if (!m_file2Item) + m_file2Item = KateProjectSharedQMapStringItem (new QMap ()); + (*m_file2Item)[document->url().toLocalFile ()] = fileItem; +} + +void KateProject::unregisterDocument (KTextEditor::Document *document) +{ + // skip if no works + if (!m_documents.contains (document)) + return; + + // perhaps kill the item we have generated + bool empty = false; + if (KateProjectItem *item = (KateProjectItem*)itemForFile (m_documents.value (document))) { + disconnect(document,SIGNAL(modifiedChanged(KTextEditor::Document *)),this,SLOT(slotModifiedChanged(KTextEditor::Document *))); + if (m_documentsParent && item->data (Qt::UserRole + 3).toBool ()) { + for (int i = 0; i < m_documentsParent->rowCount(); ++i) { + if (m_documentsParent->child (i) == item) { + m_documentsParent->removeRow (i); + break; + } + } + + empty = !m_documentsParent->rowCount(); + + m_file2Item->remove (m_documents.value (document)); + } + } + + // forget the document + m_documents.remove (document); + + // perhaps remove parent item + if (m_documentsParent && empty) { + m_model.removeRow (0); + m_documentsParent = 0; + } +} + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/addons/kate/project/kateproject.example b/kate/addons/kate/project/kateproject.example new file mode 100644 index 00000000..90ceaf82 --- /dev/null +++ b/kate/addons/kate/project/kateproject.example @@ -0,0 +1,133 @@ +/// This struct describes in almost-C++ the json data structure as expected by the +/// project plugin. +/// The json file has to be named ".kateproject". +struct +{ + /// name of the project + string name; + + /// The "directory" is optional. + /// It is probably only useful for the kate-project-generator in cmake (>= 3.0.0). + /// If set, the directory given here is used as the base directory for the project. + /// Otherwise, the directory in which the project file is located as the base directory. + string directory; + + /// The "files" struct describes which files belong to the project. + /// There are five miutually exclusive methods to do this. + struct files + { + /// "directory" is the files directory. If it is empty, the project base directory + /// will be used. If it is a relative path, it is appended to the project + /// base directory. Absolute paths work too. + string directory; + + /// If "git" is set to "1", the list of files is retrieved by running git in the files directory. + bool git; + + /// If "hg" is set to "1", the list of files is retrieved by running hg (mercurial) in the files directory. + bool hg; + + /// If "svn" is set to "1", the list of files is retrieved by running svn (subversion) in the files directory. + bool svn; + + /// "list" can be set to a list of files. + vector< string > list; + + /// If nothing of the above has been set, "filters" can be set to a list of globbing expressions, which + /// will be executed in the files directory. + vector< string > filters; + + /// If "recursive" is set to 1, the globbing expressions in filters are executed recursively in the directory tree. + bool recursive; + }; + + /// The "build" structure is optional. + /// If set, its contents are used by the build plugin in kate. + /// "targets", "default_target" and "clean_target" are supported starting with kate 4.13. + /// The "build", "clean" and "quick" fields are only used if "targets" is empty. + /// They servce for backward compatibility with the build plugin < 4.13, or they can + /// be used as a quicker way to set up projects with up to 3 targets. + struct build + { + /// The build directory + string directory; + + /// "targets" contains a vector of targets (as in a makefile). Each target has a name + /// and a command. The commands are exeucted in the build directory. + vector< {string name, string build_cmd} > targets; + + /// "default_target" must be set to one of the target names in "targets". This is the target + /// which will be built by the "Build default target" action of the build plugin. + string default_target; + + /// "clean_target" must be set to one of the target names in "targets". This is the target + /// which will be built by the "Build clean target" action of the build plugin. + string clean_target; + + /// Creates a target names "build" with the given command. + string build; + + /// Creates a target names "clean" with the given command. + string clean; + + /// Creates a target names "quick" with the given command. + string quick; + + }; + +}; + + + +Simple example, get files via globbing recursively in doc/, no build plugin: + +{ + "name": "MyProject", + "files": [ { + "directory": "doc", + "filters": ["*.tex", "Makefile"], + "recursive": 1 + } ] +} + + + +A more advanced project file, get the files from svn, set up three commands for the build plugin: + +{ + "name": "Foo", + "files": [ { "svn" : 1 } ], + + "build": { + "directory": "build", + "build": "make all -j4", + "clean": "make clean", + "quick": "make install" + } +} + + +An out-of-source project file as generated by cmake: it points to the actual project directory, +it retrieves the file list from git, and a long list of targets for the build plugin: + + +{ + "name": "CMake@build", + "directory": "/home/neundorf/src/CMake/cmake", + "files": [ { "git": 1 } ], + "build": { + "directory": "/home/neundorf/src/CMake/build", + "default_target": "all", + "clean_target": "clean", + "targets":[ + { "name":"all", "build_cmd":"/usr/bin/gmake -C /home/neundorf/src/CMake/build -j8 all" }, + { "name":"clean", "build_cmd":"/usr/bin/gmake -C /home/neundorf/src/CMake/build -j8 clean" }, + { "name":"cmLocalGenerator.cxx.o", "build_cmd":"/usr/bin/gmake -C /home/neundorf/src/CMake/build/Source -j8 cmLocalGenerator.cxx.o" }, + { "name":"cmLocalGenerator.cxx.i", "build_cmd":"/usr/bin/gmake -C /home/neundorf/src/CMake/build/Source -j8 cmLocalGenerator.cxx.i" }, +... + { "name":"cmLocalGenerator.cxx.s", "build_cmd":"/usr/bin/gmake -C /home/neundorf/src/CMake/build/Source -j8 cmLocalGenerator.cxx.s" }, + { "name":"cmMakefile.cxx.o", "build_cmd":"/usr/bin/gmake -C /home/neundorf/src/CMake/build/Source -j8 cmMakefile.cxx.o" }, + { "name":"cmMakefile.cxx.i", "build_cmd":"/usr/bin/gmake -C /home/neundorf/src/CMake/build/Source -j8 cmMakefile.cxx.i" }, + { "name":"cmMakefile.cxx.s", "build_cmd":"/usr/bin/gmake -C /home/neundorf/src/CMake/build/Source -j8 cmMakefile.cxx.s" } + ] } +} diff --git a/kate/addons/kate/project/kateproject.h b/kate/addons/kate/project/kateproject.h new file mode 100644 index 00000000..3aaed678 --- /dev/null +++ b/kate/addons/kate/project/kateproject.h @@ -0,0 +1,335 @@ +/* This file is part of the Kate project. + * + * Copyright (C) 2010 Christoph Cullmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef KATE_PROJECT_H +#define KATE_PROJECT_H + +#include +#include +#include +#include +#include +#include "kateprojectindex.h" +#include "kateprojectitem.h" + +/** + * Shared pointer data types. + * Used to pass pointers over queued connected slots + */ +typedef QSharedPointer KateProjectSharedQStandardItem; +Q_DECLARE_METATYPE(KateProjectSharedQStandardItem) + +typedef QSharedPointer > KateProjectSharedQMapStringItem; +Q_DECLARE_METATYPE(KateProjectSharedQMapStringItem) + +typedef QSharedPointer KateProjectSharedProjectIndex; +Q_DECLARE_METATYPE(KateProjectSharedProjectIndex) + +/** + * Private worker thread. + * Will take care of worker object deletion. + */ +class KateProjectWorkerThread : public QThread +{ + public: + /** + * Construct thread for given worker + */ + KateProjectWorkerThread (QObject *worker) + : QThread() + , m_worker (worker) + { + } + + protected: + /** + * start the thread event loop + */ + virtual void run() + { + /** + * run event loop + */ + exec (); + + /** + * kill worker in THIS thread + */ + delete m_worker; + } + + private: + /** + * Worker object + */ + QObject *m_worker; +}; + +/** + * Class representing a project. + * Holds project properties like name, groups, contained files, ... + */ +class KateProject : public QObject +{ + Q_OBJECT + + public: + /** + * construct empty project + */ + KateProject (); + + /** + * deconstruct project + */ + ~KateProject (); + + /** + * Load a project. + * Only works once, afterwards use reload(). + * @param fileName name of project file + * @return success + */ + bool load (const QString &fileName); + + /** + * Try to reload a project. + * If the reload fails, e.g. because the file is not readable or corrupt, nothing will happen! + * @param force will enforce the worker to update files list and co even if the content of the file was not changed! + * @return success + */ + bool reload (bool force = false); + + /** + * Accessor to file name. + * @return file name + */ + const QString &fileName () const + { + return m_fileName; + } + + /** + * Return the base directory of this project. + * @return base directory of project, might not be the directory of the fileName! + */ + const QString &baseDir () const + { + return m_baseDir; + } + + /** + * Accessor to project map containing the whole project info. + * @return project info + */ + const QVariantMap &projectMap () const + { + return m_projectMap; + } + + /** + * Accessor to project name. + * @return project name + */ + QString name () const + { + return m_projectMap["name"].toString (); + } + + /** + * Accessor for the model. + * @return model of this project + */ + QStandardItemModel *model () + { + return &m_model; + } + + /** + * Flat list of all files in the project + * @return list of files in project + */ + QStringList files () + { + return m_file2Item ? m_file2Item->keys () : QStringList (); + } + + /** + * get item for file + * @param file file to get item for + * @return item for given file or 0 + */ + KateProjectItem *itemForFile (const QString &file) + { + return m_file2Item ? m_file2Item->value (file) : 0; + } + + /** + * Access to project index. + * May be null. + * Don't store this pointer, might change. + * @return project index + */ + KateProjectIndex *projectIndex () + { + return m_projectIndex.data(); + } + + /** + * Will try to open a project local file. + * Such files will be stored as .kateproject.d/file in the project directory. + * @param file wanted file name, relative to .kateproject.d folder in project directory + * @return either a pointer to a read-write opened file or null on error + */ + QFile *projectLocalFile (const QString &file) const; + + /** + * Document with project local notes. + * Will be stored in a projectLocalFile "notes.txt". + * @return notes document + */ + QTextDocument *notesDocument (); + + /** + * Save the notes document to "notes.txt" if any document around. + */ + void saveNotesDocument (); + + /** + * Register a document for this project. + * @param document document to register + */ + void registerDocument (KTextEditor::Document *document); + + /** + * Unregister a document for this project. + * @param document document to unregister + */ + void unregisterDocument (KTextEditor::Document *document); + + private Q_SLOTS: + /** + * Used for worker to send back the results of project loading + * @param topLevel new toplevel element for model + * @param file2Item new file => item mapping + */ + void loadProjectDone (KateProjectSharedQStandardItem topLevel, KateProjectSharedQMapStringItem file2Item); + + /** + * Used for worker to send back the results of index loading + * @param projectIndex new project index + */ + void loadIndexDone (KateProjectSharedProjectIndex projectIndex); + + void slotModifiedChanged(KTextEditor::Document*); + + + void slotModifiedOnDisk (KTextEditor::Document *document, + bool isModified, KTextEditor::ModificationInterface::ModifiedOnDiskReason reason); + + + signals: + /** + * Emitted on project map changes. + * This includes the name! + */ + void projectMapChanged (); + + /** + * Emitted on model changes. + * This includes the files list, itemForFile mapping! + */ + void modelChanged (); + + /** + * Emitted when the index creation is finished. + * This includes the ctags index. + */ + void indexChanged (); + + private: + /** + * the worker inside the background thread + * if this is NULL, we are in our deconstruction state and should + * ignore the feedback of our already stopped thread that + * may still come in because of queued connects + * only DELETE all stuff we need to cleanup in the slots + */ + QObject *m_worker; + + /** + * our internal thread to load stuff and do things in background + */ + KateProjectWorkerThread m_thread; + + /** + * project file name + */ + QString m_fileName; + + /** + * base directory of the project + */ + QString m_baseDir; + + /** + * project name + */ + QString m_name; + + /** + * variant map representing the project + */ + QVariantMap m_projectMap; + + /** + * standard item model with content of this project + */ + QStandardItemModel m_model; + + /** + * mapping files => items + */ + KateProjectSharedQMapStringItem m_file2Item; + + /** + * project index, if any + */ + KateProjectSharedProjectIndex m_projectIndex; + + /** + * notes buffer for project local notes + */ + QTextDocument *m_notesDocument; + + /** + * Set of existing documents for this project. + */ + QMap m_documents; + + /** + * Parent item for existing documents that are not in the project tree + */ + QStandardItem *m_documentsParent; +}; + +#endif + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/addons/kate/project/kateprojectcompletion.cpp b/kate/addons/kate/project/kateprojectcompletion.cpp new file mode 100644 index 00000000..bc89040d --- /dev/null +++ b/kate/addons/kate/project/kateprojectcompletion.cpp @@ -0,0 +1,207 @@ +/* This file is part of the Kate project. + * + * Copyright (C) 2012 Christoph Cullmann + * Copyright (C) 2003 Anders Lund + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "kateprojectcompletion.h" +#include "kateprojectplugin.h" + +#include +#include + +KateProjectCompletion::KateProjectCompletion (KateProjectPlugin *plugin) + : KTextEditor::CodeCompletionModel (0) + , m_plugin (plugin) +{ +} + +KateProjectCompletion::~KateProjectCompletion () +{ +} + +void KateProjectCompletion::saveMatches( KTextEditor::View* view, const KTextEditor::Range& range) +{ + m_matches.clear (); + allMatches( m_matches, view, range ); +} + +QVariant KateProjectCompletion::data(const QModelIndex& index, int role) const +{ + if( role == InheritanceDepth ) + return 10010; //Very high value, so the word-completion group and items are shown behind any other groups/items if there is multiple + + if( !index.parent().isValid() ) { + //It is the group header + switch ( role ) + { + case Qt::DisplayRole: + return i18n("Project Completion"); + case GroupRole: + return Qt::DisplayRole; + } + } + + if( index.column() == KTextEditor::CodeCompletionModel::Name && role == Qt::DisplayRole ) + return m_matches.item ( index.row() )->data (Qt::DisplayRole); + + if( index.column() == KTextEditor::CodeCompletionModel::Icon && role == Qt::DecorationRole ) { + static QIcon icon(KIcon("insert-text").pixmap(QSize(16, 16))); + return icon; + } + + return QVariant(); +} + +QModelIndex KateProjectCompletion::parent(const QModelIndex& index) const +{ + if(index.internalId()) + return createIndex(0, 0, 0); + else + return QModelIndex(); +} + +QModelIndex KateProjectCompletion::index(int row, int column, const QModelIndex& parent) const +{ + if( !parent.isValid()) { + if(row == 0) + return createIndex(row, column, 0); + else + return QModelIndex(); + + }else if(parent.parent().isValid()) + return QModelIndex(); + + + if (row < 0 || row >= m_matches.rowCount() || column < 0 || column >= ColumnCount ) + return QModelIndex(); + + return createIndex(row, column, 1); +} + +int KateProjectCompletion::rowCount ( const QModelIndex & parent ) const +{ + if( !parent.isValid() && !(m_matches.rowCount() == 0) ) + return 1; //One root node to define the custom group + else if(parent.parent().isValid()) + return 0; //Completion-items have no children + else + return m_matches.rowCount(); +} + + +bool KateProjectCompletion::shouldStartCompletion(KTextEditor::View* view, const QString &insertedText, bool userInsertion, const KTextEditor::Cursor &position) +{ + if (!userInsertion) return false; + if(insertedText.isEmpty()) + return false; + + QString text = view->document()->line(position.line()).left(position.column()); + + uint check=3;//v->config()->wordCompletionMinimalWordLength(); + + if (check<=0) return true; + int start=text.length(); + int end=text.length()-check; + if (end<0) return false; + for (int i=start-1;i>=end;i--) { + QChar c=text.at(i); + if (! (c.isLetter() || (c.isNumber()) || c=='_') ) return false; + } + + return true; +} + +bool KateProjectCompletion::shouldAbortCompletion(KTextEditor::View* view, const KTextEditor::Range &range, const QString ¤tCompletion) { + + if (m_automatic) { + if (currentCompletion.length() < 3 /*v->config()->wordCompletionMinimalWordLength()*/) return true; + } + + return CodeCompletionModelControllerInterface::shouldAbortCompletion(view,range,currentCompletion); +} + +void KateProjectCompletion::completionInvoked(KTextEditor::View* view, const KTextEditor::Range& range, InvocationType it) +{ + /** + * auto invoke... + */ + m_automatic=false; + if (it==AutomaticInvocation) { + m_automatic=true; + + if (range.columnWidth() >= 3 /*v->config()->wordCompletionMinimalWordLength()*/) + saveMatches( view, range ); + else + m_matches.clear(); + + // done here... + return; + } + + // normal case ;) + saveMatches( view, range ); +} + + +// Scan throughout the entire document for possible completions, +// ignoring any dublets +void KateProjectCompletion::allMatches (QStandardItemModel &model, KTextEditor::View *view, const KTextEditor::Range &range) const +{ + /** + * get project for this document, else fail + */ + KateProject *project = m_plugin->projectForDocument (view->document()); + if (!project) + return; + + /** + * let project index fill the completion for this document + */ + if (project->projectIndex()) + project->projectIndex()->findMatches (model, view->document()->text(range), KateProjectIndex::CompletionMatches); +} + +KTextEditor::CodeCompletionModelControllerInterface::MatchReaction KateProjectCompletion::matchingItem(const QModelIndex& /*matched*/) +{ + return HideListIfAutomaticInvocation; +} + +// Return the range containing the word left of the cursor +KTextEditor::Range KateProjectCompletion::completionRange(KTextEditor::View* view, const KTextEditor::Cursor &position) +{ + int line = position.line(); + int col = position.column(); + + KTextEditor::Document *doc = view->document(); + while ( col > 0 ) + { + QChar c = ( doc->character( KTextEditor::Cursor( line, col-1 ) ) ); + if ( c.isLetterOrNumber() || c.isMark() || c == '_' ) + { + col--; + continue; + } + + break; + } + + return KTextEditor::Range( KTextEditor::Cursor( line, col ), position ); +} + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/addons/kate/project/kateprojectcompletion.h b/kate/addons/kate/project/kateprojectcompletion.h new file mode 100644 index 00000000..ce7b28f2 --- /dev/null +++ b/kate/addons/kate/project/kateprojectcompletion.h @@ -0,0 +1,103 @@ +/* This file is part of the Kate project. + * + * Copyright (C) 2010 Christoph Cullmann + * Copyright (C) 2003 Anders Lund + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef KATE_PROJECT_COMPLETION_H +#define KATE_PROJECT_COMPLETION_H + +#include +#include +#include + +#include + +/** + * Project wide completion support. + */ +class KateProjectCompletion : public KTextEditor::CodeCompletionModel, public KTextEditor::CodeCompletionModelControllerInterface +{ + Q_OBJECT + + Q_INTERFACES(KTextEditor::CodeCompletionModelControllerInterface) + + public: + /** + * Construct project completion. + * @param plugin our plugin + */ + KateProjectCompletion (class KateProjectPlugin *plugin); + + /** + * Deconstruct project completion. + */ + ~KateProjectCompletion (); + + /** + * This function is responsible to generating / updating the list of current + * completions. The default implementation does nothing. + * + * When implementing this function, remember to call setRowCount() (or implement + * rowCount()), and to generate the appropriate change notifications (for instance + * by calling QAbstractItemModel::reset()). + * @param view The view to generate completions for + * @param range The range of text to generate completions for + * */ + void completionInvoked(KTextEditor::View* view, const KTextEditor::Range& range, InvocationType invocationType); + + + bool shouldStartCompletion(KTextEditor::View* view, const QString &insertedText, bool userInsertion, const KTextEditor::Cursor &position); + bool shouldAbortCompletion(KTextEditor::View* view, const KTextEditor::Range &range, const QString ¤tCompletion); + + + + void saveMatches( KTextEditor::View* view, + const KTextEditor::Range& range); + + int rowCount ( const QModelIndex & parent ) const; + + QVariant data(const QModelIndex& index, int role) const; + virtual QModelIndex index(int row, int column, const QModelIndex& parent=QModelIndex()) const; + virtual QModelIndex parent(const QModelIndex& index) const; + virtual MatchReaction matchingItem(const QModelIndex& matched); + + virtual KTextEditor::Range completionRange(KTextEditor::View* view, const KTextEditor::Cursor &position); + + void allMatches( QStandardItemModel &model, KTextEditor::View *view, const KTextEditor::Range &range ) const; + + private: + /** + * our plugin view + */ + KateProjectPlugin *m_plugin; + + /** + * model with matching data + */ + QStandardItemModel m_matches; + + /** + * automatic invocation? + */ + bool m_automatic; +}; + +#endif + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/addons/kate/project/kateprojectindex.cpp b/kate/addons/kate/project/kateprojectindex.cpp new file mode 100644 index 00000000..ed8aeae5 --- /dev/null +++ b/kate/addons/kate/project/kateprojectindex.cpp @@ -0,0 +1,213 @@ +/* This file is part of the Kate project. + * + * Copyright (C) 2012 Christoph Cullmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "kateprojectindex.h" + +#include +#include +#include +#include + +/** + * include ctags reading + */ +#include "ctags/readtags.c" + +KateProjectIndex::KateProjectIndex (const QStringList &files) + : m_ctagsIndexFile (QDir::tempPath () + "/kate.project.ctags") + , m_ctagsIndexHandle (0) +{ + /** + * load ctags + */ + loadCtags (files); +} + +KateProjectIndex::~KateProjectIndex () +{ + /** + * delete ctags handle if any + */ + if (m_ctagsIndexHandle) { + tagsClose (m_ctagsIndexHandle); + m_ctagsIndexHandle = 0; + } +} + +void KateProjectIndex::loadCtags (const QStringList &files) +{ + /** + * create temporary file + * if not possible, fail + */ + if (!m_ctagsIndexFile.open ()) + return; + + /** + * close file again, other process will use it + */ + m_ctagsIndexFile.close (); + + static const QStringList ctagexes = QStringList() + << QLatin1String("ctags") + << QLatin1String("ctags-universal") + << QLatin1String("ctags-exuberant") + << QLatin1String("exctags") + << QLatin1String("uctags"); + QString ctagsexe; + foreach (const QString &it, ctagexes) { + ctagsexe = KStandardDirs::findExe(it); + if (!ctagsexe.isEmpty()) { + break; + } + } + if (ctagsexe.isEmpty()) { + kWarning() << "ctags not found"; + return; + } + + /** + * try to run ctags for all files in this project + * output to our ctags index file + */ + QProcess ctags; + QStringList args; + args << "-L" << "-" << "-f" << m_ctagsIndexFile.fileName() << "--fields=+K+n"; + ctags.start(ctagsexe, args); + if (!ctags.waitForStarted()) + return; + + /** + * write files list and close write channel + */ + ctags.write(files.join("\n").toLocal8Bit()); + ctags.closeWriteChannel(); + + /** + * wait for done + */ + if (!ctags.waitForFinished()) + return; + + /** + * file not openable, bad + */ + if (!m_ctagsIndexFile.open ()) + return; + + /** + * get size + */ + qint64 size = m_ctagsIndexFile.size (); + + /** + * close again + */ + m_ctagsIndexFile.close (); + + /** + * empty file, bad + */ + if (!size) + return; + + /** + * try to open ctags file + */ + tagFileInfo info; + memset (&info, 0, sizeof (tagFileInfo)); + m_ctagsIndexHandle = tagsOpen (m_ctagsIndexFile.fileName().toLocal8Bit(), &info); +} + +void KateProjectIndex::findMatches (QStandardItemModel &model, const QString &searchWord, MatchType type) +{ + /** + * abort if no ctags index + */ + if (!m_ctagsIndexHandle) + return; + + /** + * word to complete + * abort if empty + */ + QByteArray word = searchWord.toLocal8Bit(); + if (word.isEmpty()) + return; + + /** + * try to search entry + * fail if none found + */ + tagEntry entry; + if (tagsFind (m_ctagsIndexHandle, &entry, word.constData(), TAG_PARTIALMATCH | TAG_OBSERVECASE) != TagSuccess) + return; + + /** + * set to show words only once for completion matches + */ + QSet guard; + + /** + * loop over all found tags + * first one is filled by above find, others by find next + */ + do { + /** + * skip if no name + */ + if (!entry.name) + continue; + + /** + * get name + */ + QString name (QString::fromLocal8Bit(entry.name)); + + /** + * construct right items + */ + switch (type) { + case CompletionMatches: + /** + * add new completion item, if new name + */ + if (!guard.contains (name)) { + model.appendRow (new QStandardItem (name)); + guard.insert (name); + } + break; + + case FindMatches: + /** + * add new find item, contains of multiple columns + */ + QList items; + items << new QStandardItem (name); + items << new QStandardItem (entry.kind ? QString::fromLocal8Bit(entry.kind) : QString()); + items << new QStandardItem (entry.file ? QString::fromLocal8Bit(entry.file) : QString()); + items << new QStandardItem (QString("%1").arg(entry.address.lineNumber)); + model.appendRow (items); + break; + } + } while (tagsFindNext (m_ctagsIndexHandle, &entry) == TagSuccess); +} + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/addons/kate/project/kateprojectindex.h b/kate/addons/kate/project/kateprojectindex.h new file mode 100644 index 00000000..36d1fe0c --- /dev/null +++ b/kate/addons/kate/project/kateprojectindex.h @@ -0,0 +1,113 @@ +/* This file is part of the Kate project. + * + * Copyright (C) 2010 Christoph Cullmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef KATE_PROJECT_INDEX_H +#define KATE_PROJECT_INDEX_H + +#include +#include + +#include +#include +#include + +/** + * ctags reading + */ +#include "ctags/readtags.h" + +/** + * Class representing the index of a project. + * This includes knowledge from ctags and Co. + * Allows you to search for stuff and to get some useful auto-completion. + * Is created in Worker thread in the background, then passed to project in + * the main thread for usage. + */ +class KateProjectIndex +{ + public: + /** + * construct new index for given files + * @param files files to index + */ + KateProjectIndex (const QStringList &files); + + /** + * deconstruct project + */ + ~KateProjectIndex (); + + /** + * Which kind of match items should be created in the passed model + * of the findMatches function? + */ + enum MatchType { + /** + * Completion matches, containing only name and same name only once + */ + CompletionMatches, + + /** + * Find matches, containing name, kind, file, line, ... + */ + FindMatches + }; + + /** + * Fill in completion matches for given view/range. + * Uses e.g. ctags index. + * @param model model to fill with matches + * @param searchWord word to search for + * @param type type of matches + */ + void findMatches (QStandardItemModel &model, const QString &searchWord, MatchType type); + + /** + * Check if running ctags was successful. This can be used + * as indicator whether ctags is installed or not. + * @return true if a valid index exists, otherwise false + */ + bool isValid () const + { + return m_ctagsIndexHandle; + } + + private: + /** + * Load ctags tags. + * @param files files to index + */ + void loadCtags (const QStringList &files); + + private: + /** + * ctags index file + */ + QTemporaryFile m_ctagsIndexFile; + + /** + * handle to ctags file for querying, if possible + */ + tagFile *m_ctagsIndexHandle; +}; + +#endif + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/addons/kate/project/kateprojectinfoview.cpp b/kate/addons/kate/project/kateprojectinfoview.cpp new file mode 100644 index 00000000..f7b034ac --- /dev/null +++ b/kate/addons/kate/project/kateprojectinfoview.cpp @@ -0,0 +1,54 @@ +/* This file is part of the Kate project. + * + * Copyright (C) 2012 Christoph Cullmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "kateprojectinfoview.h" +#include "kateprojectpluginview.h" +#include "kateprojectinfoviewindex.h" +#include "kateprojectinfoviewcodeanalysis.h" +#include "kateprojectinfoviewnotes.h" + +#include "klocale.h" + +KateProjectInfoView::KateProjectInfoView (KateProjectPluginView *pluginView, KateProject *project) + : QTabWidget () + , m_pluginView (pluginView) + , m_project (project) +{ + /** + * index + */ + addTab (new KateProjectInfoViewIndex (pluginView, project), i18n("Code Index")); + + /** + * code analysis + */ + addTab (new KateProjectInfoViewCodeAnalysis (pluginView, project), i18n("Code Analysis")); + + /** + * notes + */ + addTab (new KateProjectInfoViewNotes (pluginView, project), i18n("Notes")); +} + +KateProjectInfoView::~KateProjectInfoView () +{ +} + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/addons/kate/project/kateprojectinfoview.h b/kate/addons/kate/project/kateprojectinfoview.h new file mode 100644 index 00000000..135f78d7 --- /dev/null +++ b/kate/addons/kate/project/kateprojectinfoview.h @@ -0,0 +1,74 @@ +/* This file is part of the Kate project. + * + * Copyright (C) 2010 Christoph Cullmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef KATE_PROJECT_INFO_VIEW_H +#define KATE_PROJECT_INFO_VIEW_H + +#include "kateproject.h" + +#include + +class KateProjectPluginView; + +/** + * Class representing a view of a project. + * A tree like view of project content. + */ +class KateProjectInfoView : public QTabWidget +{ + Q_OBJECT + + public: + /** + * construct project info view for given project + * @param pluginView our plugin view + * @param project project this view is for + */ + KateProjectInfoView (KateProjectPluginView *pluginView, KateProject *project); + + /** + * deconstruct info view + */ + ~KateProjectInfoView (); + + /** + * our project. + * @return project + */ + KateProject *project () const + { + return m_project; + } + + private: + /** + * our plugin view + */ + KateProjectPluginView *m_pluginView; + + /** + * our project + */ + KateProject *m_project; +}; + +#endif + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/addons/kate/project/kateprojectinfoviewcodeanalysis.cpp b/kate/addons/kate/project/kateprojectinfoviewcodeanalysis.cpp new file mode 100644 index 00000000..639bd6b0 --- /dev/null +++ b/kate/addons/kate/project/kateprojectinfoviewcodeanalysis.cpp @@ -0,0 +1,217 @@ +/* This file is part of the Kate project. + * + * Copyright (C) 2012 Christoph Cullmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "kateprojectinfoviewcodeanalysis.h" +#include "kateprojectpluginview.h" + +#include +#include +#include +#include + +#include +#include +#include + +KateProjectInfoViewCodeAnalysis::KateProjectInfoViewCodeAnalysis (KateProjectPluginView *pluginView, KateProject *project) + : QWidget () + , m_pluginView (pluginView) + , m_project (project) + , m_messageWidget (0) + , m_startStopAnalysis (new QPushButton(i18n("Start Analysis..."))) + , m_treeView (new QTreeView()) + , m_model (new QStandardItemModel (m_treeView)) + , m_analyzer (0) +{ + /** + * default style + */ + m_treeView->setEditTriggers (QAbstractItemView::NoEditTriggers); + m_treeView->setUniformRowHeights (true); + m_treeView->setRootIsDecorated (false); + m_model->setHorizontalHeaderLabels (QStringList () << "File" << "Line" << "Severity" << "Message"); + + /** + * attach model + * kill selection model + */ + QItemSelectionModel *m = m_treeView->selectionModel(); + m_treeView->setModel (m_model); + delete m; + + /** + * layout widget + */ + QVBoxLayout *layout = new QVBoxLayout; + layout->setSpacing (0); + layout->addWidget (m_treeView); + QHBoxLayout *hlayout = new QHBoxLayout; + layout->addLayout (hlayout); + hlayout->setSpacing (0); + hlayout->addStretch(); + hlayout->addWidget (m_startStopAnalysis); + setLayout (layout); + + /** + * connect needed signals + */ + connect (m_startStopAnalysis, SIGNAL(clicked (bool)), this, SLOT(slotStartStopClicked ())); + connect (m_treeView, SIGNAL(clicked (const QModelIndex &)), this, SLOT(slotClicked (const QModelIndex &))); +} + +KateProjectInfoViewCodeAnalysis::~KateProjectInfoViewCodeAnalysis () +{ +} + +void KateProjectInfoViewCodeAnalysis::slotStartStopClicked () +{ + /** + * stop analyzer + */ + if (m_analyzer) { + m_startStopAnalysis->setText(i18n("Start Analysis...")); + m_analyzer->terminate(); + delete m_analyzer; + m_analyzer=0; + return; + } + + /** + * display a message to install cppcheck, but after stop has been performed + * since cppcheck may have been uninstalled after the analyzer has been run + */ + if (m_messageWidget) { + delete m_messageWidget; + m_messageWidget=0; + } + + if (KStandardDirs::findExe("cppcheck").isEmpty()) { + m_messageWidget = new KMessageWidget(); + m_messageWidget->setCloseButtonVisible(true); + m_messageWidget->setMessageType(KMessageWidget::Warning); + m_messageWidget->setWordWrap(false); + m_messageWidget->setText(i18n("Please install 'cppcheck'.")); + static_cast(layout ())->insertWidget(0, m_messageWidget); + m_messageWidget->animatedShow(); + return; + } + + + m_treeView->setSortingEnabled (false); + + /** + * get files for cppcheck + */ + QStringList files = m_project->files ().filter (QRegExp ("\\.(cpp|cxx|cc|c\\+\\+|c|tpp|txx)$")); + + /** + * clear existing entries + */ + m_model->removeRows(0,m_model->rowCount(),QModelIndex()); + + /** + * launch cppcheck + */ + m_analyzer = new QProcess (this); + m_analyzer->setProcessChannelMode(QProcess::MergedChannels); + + connect (m_analyzer, SIGNAL(readyRead()), this, SLOT(slotReadyRead())); + + QStringList args; + args << "-q" << "--inline-suppr" << "--enable=all" << "--template={file}////{line}////{severity}////{message}" << "--file-list=-"; + m_analyzer->start("cppcheck", args); + + /** + * TODO: if failure occured stop now and display the error + */ + if (m_analyzer->waitForStarted()) { + m_startStopAnalysis->setText(i18n("Stop Analysis")); + } + + /** + * write files list and close write channel + */ + m_analyzer->write(files.join("\n").toLocal8Bit()); + m_analyzer->closeWriteChannel(); +} + +void KateProjectInfoViewCodeAnalysis::slotReadyRead () +{ + /** + * get results of analysis + */ + while (m_analyzer->canReadLine()) { + /** + * get one line, split it, skip it, if too few elements + */ + QString line = QString::fromLocal8Bit (m_analyzer->readLine()); + QStringList elements = line.split (QRegExp("////"), QString::SkipEmptyParts); + if (elements.size() < 4) + continue; + + /** + * feed into model + */ + QList items; + QStandardItem *fileNameItem = new QStandardItem (QFileInfo (elements[0]).fileName()); + fileNameItem->setToolTip (elements[0]); + items << fileNameItem; + items << new QStandardItem (elements[1]); + items << new QStandardItem (elements[2]); + items << new QStandardItem (elements[3].simplified()); + m_model->appendRow (items); + } + + /** + * tree view polish ;) + */ + m_treeView->resizeColumnToContents (2); + m_treeView->resizeColumnToContents (1); + m_treeView->resizeColumnToContents (0); + // TODO: resort view + m_treeView->setSortingEnabled (true); +} + +void KateProjectInfoViewCodeAnalysis::slotClicked (const QModelIndex &index) +{ + /** + * get path + */ + QString filePath = m_model->item (index.row(), 0)->toolTip(); + if (filePath.isEmpty()) + return; + + /** + * create view + */ + KTextEditor::View *view = m_pluginView->mainWindow()->openUrl (KUrl::fromPath (filePath)); + if (!view) + return; + + /** + * set cursor, if possible + */ + int line = m_model->item (index.row(), 1)->text().toInt(); + if (line >= 1) + view->setCursorPosition (KTextEditor::Cursor (line - 1, 0)); +} + + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/addons/kate/project/kateprojectinfoviewcodeanalysis.h b/kate/addons/kate/project/kateprojectinfoviewcodeanalysis.h new file mode 100644 index 00000000..447c3ed3 --- /dev/null +++ b/kate/addons/kate/project/kateprojectinfoviewcodeanalysis.h @@ -0,0 +1,119 @@ +/* This file is part of the Kate project. + * + * Copyright (C) 2010 Christoph Cullmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef KATE_PROJECT_INFO_VIEW_CODE_ANALYSIS_H +#define KATE_PROJECT_INFO_VIEW_CODE_ANALYSIS_H + +#include "kateproject.h" + +#include +#include + +class KateProjectPluginView; +class KMessageWidget; +#include + +/** + * View for Code Analysis. + * cppcheck and perhaps later more... + */ +class KateProjectInfoViewCodeAnalysis : public QWidget +{ + Q_OBJECT + + public: + /** + * construct project info view for given project + * @param pluginView our plugin view + * @param project project this view is for + */ + KateProjectInfoViewCodeAnalysis (KateProjectPluginView *pluginView, KateProject *project); + + /** + * deconstruct info view + */ + ~KateProjectInfoViewCodeAnalysis (); + + /** + * our project. + * @return project + */ + KateProject *project () const + { + return m_project; + } + + private Q_SLOTS: + /** + * Called if start/stop button is clicked. + */ + void slotStartStopClicked (); + + /** + * More checker output is available + */ + void slotReadyRead (); + + /** + * item got clicked, do stuff, like open document + * @param index model index of clicked item + */ + void slotClicked (const QModelIndex &index); + + private: + /** + * our plugin view + */ + KateProjectPluginView *m_pluginView; + + /** + * our project + */ + KateProject *m_project; + + /** + * information widget showing a warning about missing ctags. + */ + KMessageWidget *m_messageWidget; + + /** + * start/stop analysis button + */ + QPushButton *m_startStopAnalysis; + + /** + * tree view for results + */ + QTreeView *m_treeView; + + /** + * standard item model for results + */ + QStandardItemModel *m_model; + + /** + * running analyzer process + */ + QProcess *m_analyzer; +}; + +#endif + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/addons/kate/project/kateprojectinfoviewindex.cpp b/kate/addons/kate/project/kateprojectinfoviewindex.cpp new file mode 100644 index 00000000..3fe13563 --- /dev/null +++ b/kate/addons/kate/project/kateprojectinfoviewindex.cpp @@ -0,0 +1,154 @@ +/* This file is part of the Kate project. + * + * Copyright (C) 2012 Christoph Cullmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "kateprojectinfoviewindex.h" +#include "kateprojectpluginview.h" + +#include +#include +#include + +KateProjectInfoViewIndex::KateProjectInfoViewIndex (KateProjectPluginView *pluginView, KateProject *project) + : QWidget () + , m_pluginView (pluginView) + , m_project (project) + , m_messageWidget (0) + , m_lineEdit (new QLineEdit()) + , m_treeView (new QTreeView()) + , m_model (new QStandardItemModel (m_treeView)) +{ + /** + * default style + */ + m_treeView->setEditTriggers (QAbstractItemView::NoEditTriggers); + m_treeView->setUniformRowHeights (true); + m_treeView->setRootIsDecorated (false); + m_model->setHorizontalHeaderLabels (QStringList () << i18n ("Name") << i18n ("Kind") << i18n ("File") << i18n ("Line")); + + /** + * attach model + * kill selection model + */ + QItemSelectionModel *m = m_treeView->selectionModel(); + m_treeView->setModel (m_model); + delete m; + + /** + * layout widget + */ + QVBoxLayout *layout = new QVBoxLayout; + layout->setSpacing (0); + layout->addWidget (m_lineEdit); + layout->addWidget (m_treeView); + setLayout (layout); + + /** + * connect needed signals + */ + connect (m_lineEdit, SIGNAL(textChanged (const QString &)), this, SLOT(slotTextChanged (const QString &))); + connect (m_treeView, SIGNAL(clicked (const QModelIndex &)), this, SLOT(slotClicked (const QModelIndex &))); + connect (m_project, SIGNAL(indexChanged ()), this, SLOT(indexAvailable ())); + + /** + * trigger once search with nothing + */ + slotTextChanged (QString()); +} + +KateProjectInfoViewIndex::~KateProjectInfoViewIndex () +{ +} + +void KateProjectInfoViewIndex::slotTextChanged (const QString &text) +{ + /** + * init + */ + m_treeView->setSortingEnabled (false); + m_model->setRowCount(0); + + /** + * get results + */ + if (m_project->projectIndex() && !text.isEmpty()) + m_project->projectIndex()->findMatches (*m_model, text, KateProjectIndex::FindMatches); + + /** + * tree view polish ;) + */ + m_treeView->setSortingEnabled (true); + m_treeView->resizeColumnToContents (2); + m_treeView->resizeColumnToContents (1); + m_treeView->resizeColumnToContents (0); +} + +void KateProjectInfoViewIndex::slotClicked (const QModelIndex &index) +{ + /** + * get path + */ + QString filePath = m_model->item (index.row(), 2)->text(); + if (filePath.isEmpty()) + return; + + /** + * create view + */ + KTextEditor::View *view = m_pluginView->mainWindow()->openUrl (KUrl::fromPath (filePath)); + if (!view) + return; + + /** + * set cursor, if possible + */ + int line = m_model->item (index.row(), 3)->text().toInt(); + if (line >= 1) + view->setCursorPosition (KTextEditor::Cursor (line - 1, 0)); +} + +void KateProjectInfoViewIndex::indexAvailable () +{ + /** + * update enabled state of widgets + */ + const bool valid = m_project->projectIndex ()->isValid (); + m_lineEdit->setEnabled(valid); + m_treeView->setEnabled(valid); + + /** + * if index exists, hide possible message widget, else create it + */ + if (valid) { + if (m_messageWidget && m_messageWidget->isVisible ()) { + m_messageWidget->animatedHide (); + } + } else if (!m_messageWidget) { + m_messageWidget = new KMessageWidget(); + m_messageWidget->setCloseButtonVisible(true); + m_messageWidget->setMessageType(KMessageWidget::Warning); + m_messageWidget->setWordWrap(false); + m_messageWidget->setText(i18n("The index could not be created. Please install 'ctags'.")); + static_cast(layout ())->insertWidget(0, m_messageWidget); + } else { + m_messageWidget->animatedShow (); + } +} + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/addons/kate/project/kateprojectinfoviewindex.h b/kate/addons/kate/project/kateprojectinfoviewindex.h new file mode 100644 index 00000000..42b956f1 --- /dev/null +++ b/kate/addons/kate/project/kateprojectinfoviewindex.h @@ -0,0 +1,115 @@ +/* This file is part of the Kate project. + * + * Copyright (C) 2010 Christoph Cullmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef KATE_PROJECT_INFO_VIEW_INDEX_H +#define KATE_PROJECT_INFO_VIEW_INDEX_H + +#include "kateproject.h" + +#include +#include + +class KateProjectPluginView; +class KMessageWidget; + +/** + * Class representing a view of a project. + * A tree like view of project content. + */ +class KateProjectInfoViewIndex : public QWidget +{ + Q_OBJECT + + public: + /** + * construct project info view for given project + * @param pluginView our plugin view + * @param project project this view is for + */ + KateProjectInfoViewIndex (KateProjectPluginView *pluginView, KateProject *project); + + /** + * deconstruct info view + */ + ~KateProjectInfoViewIndex (); + + /** + * our project. + * @return project + */ + KateProject *project () const + { + return m_project; + } + + private Q_SLOTS: + /** + * Called if text in lineedit changes, then we need to search + * @param text new text + */ + void slotTextChanged (const QString &text); + + /** + * item got clicked, do stuff, like open document + * @param index model index of clicked item + */ + void slotClicked (const QModelIndex &index); + + /** + * called whenever the index of the project was updated. Here, + * it's used to show a warning, if ctags is not installed. + */ + void indexAvailable (); + + private: + /** + * our plugin view + */ + KateProjectPluginView *m_pluginView; + + /** + * our project + */ + KateProject *m_project; + + /** + * information widget showing a warning about missing ctags. + */ + KMessageWidget *m_messageWidget; + + /** + * line edit which allows to search index + */ + QLineEdit *m_lineEdit; + + /** + * tree view for results + */ + QTreeView *m_treeView; + + /** + * standard item model for results + */ + QStandardItemModel *m_model; +}; + +#endif + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/addons/kate/project/kateprojectinfoviewnotes.cpp b/kate/addons/kate/project/kateprojectinfoviewnotes.cpp new file mode 100644 index 00000000..5bf86d2e --- /dev/null +++ b/kate/addons/kate/project/kateprojectinfoviewnotes.cpp @@ -0,0 +1,46 @@ +/* This file is part of the Kate project. + * + * Copyright (C) 2012 Joseph Wenninger + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "kateprojectinfoviewnotes.h" +#include "kateprojectpluginview.h" + +#include + +KateProjectInfoViewNotes::KateProjectInfoViewNotes (KateProjectPluginView *pluginView, KateProject *project) + : QWidget () + , m_pluginView (pluginView) + , m_project (project) + , m_edit (new QPlainTextEdit ()) +{ + /* + * layout widget + */ + QVBoxLayout *layout = new QVBoxLayout; + layout->setSpacing (0); + layout->addWidget (m_edit); + setLayout (layout); + m_edit->setDocument(project->notesDocument()); +} + +KateProjectInfoViewNotes::~KateProjectInfoViewNotes () +{ +} + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/addons/kate/project/kateprojectinfoviewnotes.h b/kate/addons/kate/project/kateprojectinfoviewnotes.h new file mode 100644 index 00000000..13f72739 --- /dev/null +++ b/kate/addons/kate/project/kateprojectinfoviewnotes.h @@ -0,0 +1,79 @@ +/* This file is part of the Kate project. + * + * Copyright (C) 2012 Joseph Wenninger + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef KATE_PROJECT_INFO_VIEW_NOTES_H +#define KATE_PROJECT_INFO_VIEW_NOTES_H + +#include "kateproject.h" + +#include + +class KateProjectPluginView; + +/** + * Class representing a view of a project. + * A tree like view of project content. + */ +class KateProjectInfoViewNotes : public QWidget +{ + Q_OBJECT + + public: + /** + * construct project info view for given project + * @param pluginView our plugin view + * @param project project this view is for + */ + KateProjectInfoViewNotes (KateProjectPluginView *pluginView, KateProject *project); + + /** + * deconstruct info view + */ + ~KateProjectInfoViewNotes (); + + /** + * our project. + * @return project + */ + KateProject *project () const + { + return m_project; + } + + private: + /** + * our plugin view + */ + KateProjectPluginView *m_pluginView; + + /** + * our project + */ + KateProject *m_project; + + /** + * edit widget bound to notes document of project + */ + QPlainTextEdit *m_edit; +}; + +#endif + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/addons/kate/project/kateprojectitem.cpp b/kate/addons/kate/project/kateprojectitem.cpp new file mode 100644 index 00000000..cc32d63e --- /dev/null +++ b/kate/addons/kate/project/kateprojectitem.cpp @@ -0,0 +1,142 @@ +/* This file is part of the Kate project. + * + * Copyright (C) 2012 Christoph Cullmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "kateprojectitem.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +KateProjectItem::KateProjectItem (Type type, const QString &text) + : QStandardItem (text) + , m_type (type) + , m_icon (0) + , m_emblem (0) +{ +} + +KateProjectItem::~KateProjectItem () +{ + /** + * cleanup + */ + delete m_icon; + delete m_emblem; +} + +void KateProjectItem::slotModifiedChanged(KTextEditor::Document *doc) { + if (m_icon) { + delete m_icon; + m_icon=0; + } + if (doc->isModified()) { + if (m_emblem) { + QStringList emblems; + emblems<<*m_emblem; + m_icon=new KIcon("document-save",0,emblems); + } else + m_icon=new KIcon("document-save",0); + } + emitDataChanged(); +} + +void KateProjectItem::slotModifiedOnDisk (KTextEditor::Document *document, + bool isModified, KTextEditor::ModificationInterface::ModifiedOnDiskReason reason) { + Q_UNUSED(document) + Q_UNUSED(isModified) + + if (m_icon) { + delete m_icon; + m_icon=0; + } + + if (m_emblem) { + delete m_emblem; + m_emblem=0; + } + + if (reason!=KTextEditor::ModificationInterface::OnDiskUnmodified) + m_emblem=new QString("emblem-important"); + emitDataChanged(); + +} + +QVariant KateProjectItem::data (int role) const +{ + /** + * create icons on demand + */ + if (role == Qt::DecorationRole) { + /** + * this should only happen in main thread + * the background thread should only construct this elements and fill data + * but never query gui stuff! + */ + Q_ASSERT (QThread::currentThread () == QCoreApplication::instance()->thread ()); + + /** + * create icon, on demand + */ + if (!m_icon) { + /** + * use right type + */ + switch (m_type) { + case Project: + m_icon = new QIcon (KIconLoader::global ()->loadIcon ("folder-documents", KIconLoader::Small)); + break; + + case Directory: + m_icon = new QIcon (KIconLoader::global ()->loadIcon ("folder", KIconLoader::Small)); + break; + + case File: { + QString iconName = KMimeType::iconNameForUrl(KUrl::fromPath(data(Qt::UserRole).toString())); + QStringList emblems; + if (m_emblem) { + emblems<<*m_emblem; + } + kDebug( 13035 ) << emblems; + m_icon = new QIcon (KIconLoader::global ()->loadMimeTypeIcon (iconName, KIconLoader::Small,0,KIconLoader::DefaultState,emblems)); + break; + } + } + } + + /** + * return the cached icon + */ + return QVariant (*m_icon); + } + + /** + * use normal data method + */ + return QStandardItem::data (role); +} + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/addons/kate/project/kateprojectitem.h b/kate/addons/kate/project/kateprojectitem.h new file mode 100644 index 00000000..a8d35512 --- /dev/null +++ b/kate/addons/kate/project/kateprojectitem.h @@ -0,0 +1,94 @@ +/* This file is part of the Kate project. + * + * Copyright (C) 2010 Christoph Cullmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef KATE_PROJECT_ITEM_H +#define KATE_PROJECT_ITEM_H + +#include +#include + +namespace KTextEditor { + class Document; + +} + +/** + * Class representing a item inside a project. + * Items can be: projects, directories, files + */ +class KateProjectItem : public QStandardItem +{ + + public: + /** + * Possible Types + */ + enum Type { + Project + , Directory + , File + }; + + /** + * construct new item with given text + * @param type type for this item + * @param text text for this item + */ + KateProjectItem (Type type, const QString &text); + + /** + * deconstruct project + */ + ~KateProjectItem (); + + /** + * Overwritten data methode for on-demand icon creation and co. + * @param role role to get data for + * @return data for role + */ + QVariant data (int role = Qt::UserRole + 1) const; + + private: + /** + * type + */ + const Type m_type; + + /** + * cached icon + */ + mutable QIcon *m_icon; + + /** + * for document icons + */ + QString *m_emblem; + + public: + void slotModifiedChanged(KTextEditor::Document*); + void slotModifiedOnDisk (KTextEditor::Document *document, + bool isModified, KTextEditor::ModificationInterface::ModifiedOnDiskReason reason); + + +}; + +#endif + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/addons/kate/project/kateprojectnew.cpp b/kate/addons/kate/project/kateprojectnew.cpp new file mode 100644 index 00000000..371943e5 --- /dev/null +++ b/kate/addons/kate/project/kateprojectnew.cpp @@ -0,0 +1,109 @@ +/* This file is part of the Kate project. + * + * Copyright (C) 2016 Ivailo Monev + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "ui_kateprojectnew.h" +#include "kateprojectnew.h" + +#include +#include +#include + +KateProjectNew::KateProjectNew(QWidget *parent) + : QDialog(parent) +{ + m_ui = new Ui_KateProjectNew(); + m_ui->setupUi(this); + + connect(m_ui->projectPath, SIGNAL(textChanged(QString)), this, SLOT(slotPath(QString))); + connect(m_ui->okButton, SIGNAL(clicked()), this, SLOT(slotOk())); + connect(m_ui->cancelButton, SIGNAL(clicked()), this, SLOT(slotCancel())); +} + +KateProjectNew::~KateProjectNew() +{ + delete m_ui; +} + +QByteArray KateProjectNew::getPathType(QString path) +{ + QDir gitdir(path + QLatin1String("/.git")); + QDir hgdir(path + QLatin1String("/.hg")); + QDir svndir(path + QLatin1String("/.svn")); + if (gitdir.exists()) { + return "git"; + } else if (hgdir.exists()) { + return "hg"; + } else if (svndir.exists()) { + return "svn"; + } + return "unknown"; +} + +void KateProjectNew::slotPath(QString path) +{ + // path can be empty on close + if (!path.isEmpty()) { + m_ui->okButton->setEnabled(true); + if (getPathType(path) == "unknown") { + m_ui->okButton->setEnabled(false); + KMessageBox::error(this, i18n("Path is not managed with Version Control System"), i18n("Invalid project path")); + } else { + QDir projectdir(path); + m_ui->projectName->setText(projectdir.dirName()); + } + } +} + +void KateProjectNew::slotOk() +{ + QFile kateproject(m_ui->projectPath->text() + QLatin1String("/.kateproject")); + + if (kateproject.exists()) { + int result = KMessageBox::questionYesNo(this, + i18n("Project already exists, overwrite?"), i18n("Project exists")); + if (result != KMessageBox::Yes) { + return; + } + } + + // not very elegant but it will do + QByteArray projectdata("{\n\t\"name\": \"#PROJECTNAME#\" ,\n\t\"files\": [ { \"#PROJECTTYPE#\": 1 } ]\n}"); + projectdata.replace("#PROJECTNAME#", m_ui->projectName->text().toUtf8()); + projectdata.replace("#PROJECTTYPE#", getPathType(m_ui->projectPath->text())); + + kateproject.open(QIODevice::WriteOnly); + int byteswritten = kateproject.write(projectdata); + if (byteswritten <= 0) { + KMessageBox::error(this, i18n("Could not write project file"), i18n("Project creation failed")); + } + kateproject.close(); + emit projectCreated(kateproject.fileName()); + + slotCancel(); +} + +void KateProjectNew::slotCancel() +{ + m_ui->projectName->clear(); + m_ui->projectPath->clear(); + hide(); +} + +#include "moc_kateprojectnew.cpp" diff --git a/kate/addons/kate/project/kateprojectnew.h b/kate/addons/kate/project/kateprojectnew.h new file mode 100644 index 00000000..70d1256c --- /dev/null +++ b/kate/addons/kate/project/kateprojectnew.h @@ -0,0 +1,52 @@ +/* This file is part of the Kate project. + * + * Copyright (C) 2016 Ivailo Monev + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef KATEPROJECTNEW_H +#define KATEPROJECTNEW_H + +#include +#include + +QT_BEGIN_NAMESPACE +class Ui_KateProjectNew; +QT_END_NAMESPACE + +class KateProjectNew: public QDialog +{ + Q_OBJECT +public: + KateProjectNew(QWidget *parent); + ~KateProjectNew(); + + static QByteArray getPathType(QString path); + +Q_SIGNALS: + void projectCreated(QString path); + +private Q_SLOTS: + void slotPath(QString path); + void slotOk(); + void slotCancel(); + +private: + Ui_KateProjectNew *m_ui; +}; + +#endif // KATEPROJECTNEW_H diff --git a/kate/addons/kate/project/kateprojectnew.ui b/kate/addons/kate/project/kateprojectnew.ui new file mode 100644 index 00000000..34002bec --- /dev/null +++ b/kate/addons/kate/project/kateprojectnew.ui @@ -0,0 +1,95 @@ + + + KateProjectNew + + + + 0 + 0 + 269 + 94 + + + + New project + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + My Project + + + + + + + KFile::Directory|KFile::ExistingOnly|KFile::LocalOnly + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + OK + + + + + + + + + + + + Cancel + + + + + + + + + + + + + + + KUrlRequester + QFrame +
kurlrequester.h
+
+
+ +
diff --git a/kate/addons/kate/project/kateprojectplugin.cpp b/kate/addons/kate/project/kateprojectplugin.cpp new file mode 100644 index 00000000..bb93e41a --- /dev/null +++ b/kate/addons/kate/project/kateprojectplugin.cpp @@ -0,0 +1,262 @@ +/* This file is part of the Kate project. + * + * Copyright (C) 2010 Christoph Cullmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "kateprojectplugin.h" +#include "moc_kateprojectplugin.cpp" + +#include "kateproject.h" +#include "kateprojectpluginview.h" + +#include +#include +#include + +#include +#include + +#include "config.h" + +#ifdef HAVE_CTERMID +#include +#include +#include +#include +#include +#endif + +KateProjectPlugin::KateProjectPlugin (QObject* parent, const QList&) + : Kate::Plugin ((Kate::Application*)parent) + , m_completion (this) +{ + /** + * register some data types + */ + qRegisterMetaType("KateProjectSharedQStandardItem"); + qRegisterMetaType("KateProjectSharedQMapStringItem"); + qRegisterMetaType("KateProjectSharedProjectIndex"); + + /** + * connect to important signals, e.g. for auto project loading + */ + connect (application()->documentManager(), SIGNAL(documentCreated (KTextEditor::Document *)), this, SLOT(slotDocumentCreated (KTextEditor::Document *))); + connect (&m_fileWatcher, SIGNAL(directoryChanged (const QString &)), this, SLOT(slotDirectoryChanged (const QString &))); + +#ifdef HAVE_CTERMID + /** + * open project for our current working directory, if this kate has a terminal + * http://stackoverflow.com/questions/1312922/detect-if-stdin-is-a-terminal-or-pipe-in-c-c-qt + */ + char tty[L_ctermid+1] = {0}; + ctermid (tty); + int fd = ::open(tty, O_RDONLY); + if (fd >= 0) { + /** + * open project for working dir! + */ + projectForDir (QDir::current ()); + + /** + * close again + */ + ::close (fd); + } +#endif + + /** + * connect for all already existing documents + */ + foreach (KTextEditor::Document *document, application()->documentManager()->documents()) + slotDocumentCreated (document); +} + +KateProjectPlugin::~KateProjectPlugin() +{ + /** + * cleanup open projects + */ + foreach (KateProject *project, m_projects) { + /** + * remove path + */ + m_fileWatcher.removePath (QFileInfo (project->fileName()).canonicalPath()); + + /** + * let events still be handled! + */ + delete project; + } + + /** + * cleanup list + */ + m_projects.clear (); +} + +Kate::PluginView *KateProjectPlugin::createView( Kate::MainWindow *mainWindow ) +{ + return new KateProjectPluginView ( this, mainWindow ); +} + +KateProject *KateProjectPlugin::createProjectForFileName (const QString &fileName) +{ + /** + * try to load or fail + */ + KateProject *project = new KateProject (); + if (!project->load (fileName)) { + delete project; + return 0; + } + + /** + * remember project and emit & return it + */ + m_projects.append(project); + m_fileWatcher.addPath (QFileInfo(fileName).canonicalPath()); + emit projectCreated (project); + return project; +} + +KateProject *KateProjectPlugin::projectForDir (QDir dir) +{ + /** + * search projects upwards + * with recursion guard + */ + QSet seenDirectories; + while (!seenDirectories.contains (dir.absolutePath ())) { + /** + * fill recursion guard + */ + seenDirectories.insert (dir.absolutePath ()); + + /** + * check for project and load it if found + */ + QString canonicalPath = dir.canonicalPath(); + QString canonicalFileName = canonicalPath + QString("/.kateproject"); + + foreach (KateProject *project, m_projects) { + if (project->baseDir() == canonicalPath || project->fileName() == canonicalFileName) + return project; + } + + if (dir.exists (".kateproject")) + return createProjectForFileName (canonicalFileName); + + /** + * else: cd up, if possible or abort + */ + if (!dir.cdUp()) + break; + } + + /** + * nothing there + */ + return 0; +} + +KateProject *KateProjectPlugin::projectForUrl (const KUrl &url) +{ + /** + * abort if empty url or no local path + */ + if (url.isEmpty() || !url.isLocalFile()) + return 0; + + /** + * else get local filename and then the dir for it + * pass this to right search function + */ + return projectForDir (QFileInfo(url.toLocalFile ()).absoluteDir ()); +} + +void KateProjectPlugin::slotDocumentCreated (KTextEditor::Document *document) +{ + /** + * connect to url changed, for auto load and destroyed + */ + connect (document, SIGNAL(documentUrlChanged (KTextEditor::Document *)), this, SLOT(slotDocumentUrlChanged (KTextEditor::Document *))); + connect (document, SIGNAL(destroyed (QObject *)), this, SLOT(slotDocumentDestroyed (QObject *))); + + /** + * trigger slot once, for existing docs + */ + slotDocumentUrlChanged (document); +} + +void KateProjectPlugin::slotDocumentDestroyed (QObject *document) +{ + /** + * remove mapping to project + */ + if (KateProject *project = m_document2Project.value (document)) + project->unregisterDocument (static_cast (document)); + + /** + * remove mapping + */ + m_document2Project.remove (document); +} + +void KateProjectPlugin::slotDocumentUrlChanged (KTextEditor::Document *document) +{ + /** + * search matching project + */ + KateProject *project = projectForUrl (document->url()); + + /** + * remove mapping to project + */ + if (KateProject *project = m_document2Project.value (document)) + project->unregisterDocument (document); + + /** + * update mapping document => project + */ + if (!project) + m_document2Project.remove (document); + else + m_document2Project[document] = project; + + /** + * add mapping to project + */ + if (KateProject *project = m_document2Project.value (document)) + project->registerDocument (document); +} + +void KateProjectPlugin::slotDirectoryChanged (const QString &path) +{ + /** + * auto-reload, if there + */ + QString fileName = path + QString("/.kateproject"); + foreach (KateProject *project, m_projects) { + if (project->fileName() == fileName) { + project->reload(); + break; + } + } +} + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/addons/kate/project/kateprojectplugin.desktop b/kate/addons/kate/project/kateprojectplugin.desktop new file mode 100644 index 00000000..0da4f93a --- /dev/null +++ b/kate/addons/kate/project/kateprojectplugin.desktop @@ -0,0 +1,102 @@ +[Desktop Entry] +Type=Service +ServiceTypes=Kate/Plugin +X-KDE-Library=kateprojectplugin +X-Kate-Version=2.9 +X-Kate-Load=True +Name=Project Plugin +Name[ar]=ملحقة المشاريع +Name[bg]=Приставка за проекти +Name[bs]=Priključak projekta +Name[ca]=Connector de projecte +Name[ca@valencia]=Connector de projecte +Name[cs]=Modul projektu +Name[da]=Projekt-plugin +Name[de]=Projektmodul +Name[el]=Project πρόσθετο +Name[en_GB]=Project Plugin +Name[es]=Complemento de proyecto +Name[et]=Projektiplugin +Name[fi]=Projektiliitännäinen +Name[fr]=Module externe de projet +Name[ga]=Breiseán Tionscadail +Name[gl]=Complemento de proxecto +Name[he]=תוסף פרוייקט +Name[hu]=Projekt bővítmény +Name[ia]=Plug-in de Projecto +Name[it]=Estensione di progetto +Name[kk]=Жоба плагині +Name[km]=កម្មវិធី​ជំនួយ​គម្រោង +Name[ko]=프로젝트 플러그인 +Name[lt]=Projekto įskiepiai +Name[mr]=परियोजना प्लगइन +Name[nb]=Tillegg for prosjekt +Name[nds]=Projektmoduul +Name[nl]=Projectplug-in +Name[pa]=ਪਰੋਜੈਕਟ ਪਲੱਗਇਨ +Name[pl]=Wtyczka projektu +Name[pt]='Plugin' de Projecto +Name[pt_BR]=Plugin de projeto +Name[ro]=Modul de proiecte +Name[ru]=Модуль проектов +Name[sk]=Modul projektu +Name[sl]=Projektni vstavek +Name[sr]=Пројектни прикључак +Name[sr@ijekavian]=Пројектни прикључак +Name[sr@ijekavianlatin]=Projektni priključak +Name[sr@latin]=Projektni priključak +Name[sv]=Projektinsticksprogram +Name[tg]=Плагини лоиҳа +Name[tr]=Proje Eklentisi +Name[uk]=Додаток проектів +Name[x-test]=xxProject Pluginxx +Name[zh_CN]=工程插件 +Name[zh_TW]=專案外掛程式 +Comment=Project plugin for Kate +Comment[ar]=ملحقة مشاريع لِكيت +Comment[bg]=Приставка за проекти в Kate +Comment[bs]=Priključak projekta za Kate +Comment[ca]=Connector de projecte pel Kate +Comment[ca@valencia]=Connector de projecte pel Kate +Comment[cs]=Modul projektů pro Kate +Comment[da]=Projekt-plugin til Kate +Comment[de]=Projektmodul für Kate +Comment[el]=Project πρόσθετο για το Kate +Comment[en_GB]=Project plugin for Kate +Comment[es]=Complemento de proyectos para Kate +Comment[et]=Kate projektiplugin +Comment[fi]=Projektiliitännäinen Kateen +Comment[fr]=Module externe de projet pour Kate +Comment[ga]=Breiseán tionscadail le haghaidh Kate +Comment[gl]=Complemento de proxecto para Kate +Comment[he]=תוסף פורייקט עבור Kate +Comment[hu]=Projekt bővítmény a Kate-hez +Comment[ia]=Plugin de projecto pro Kate +Comment[it]=Estensione di progetto per Kate +Comment[kk]=Kate жоба плагині +Comment[km]=កម្មវិធី​ជំនួយ​គម្រោង​សម្រាប់​ Kate +Comment[ko]=Kate 프로젝트 플러그인 +Comment[lt]=Kate Projekto įskiepis +Comment[mr]=केट करिता परियोजना प्लगइन +Comment[nb]=Prosjekt-programtillegg for Kate +Comment[nds]=Projektmoduul för Kate +Comment[nl]=Projectplug-in voor Kate +Comment[pa]=ਕੇਟ ਲਈ ਪਰੋਜੈਕਟ ਪਲੱਗਇਨ +Comment[pl]=Wtyczka projektu dla Kate +Comment[pt]='Plugin' de projectos para o Kate +Comment[pt_BR]=Plugin de projeto para o Kate +Comment[ro]=Modul de proiecte pentru Kate +Comment[ru]=Модуль проектов для Kate +Comment[sk]=Projektový plugin pre Kate +Comment[sl]=Projektni vstavek za Kate +Comment[sr]=Пројектни прикључак за Кејт +Comment[sr@ijekavian]=Пројектни прикључак за Кејт +Comment[sr@ijekavianlatin]=Projektni priključak za Kate +Comment[sr@latin]=Projektni priključak za Kate +Comment[sv]=Projektinsticksprogram för Kate +Comment[tg]=Плагини лоиҳа барои Kate +Comment[tr]=Kate için proje eklentisi +Comment[uk]=Додаток проектів до Kate +Comment[x-test]=xxProject plugin for Katexx +Comment[zh_CN]=Kate 工程插件 +Comment[zh_TW]=Kate 的專案外掛程式 diff --git a/kate/addons/kate/project/kateprojectplugin.h b/kate/addons/kate/project/kateprojectplugin.h new file mode 100644 index 00000000..74ef10b6 --- /dev/null +++ b/kate/addons/kate/project/kateprojectplugin.h @@ -0,0 +1,158 @@ +/* This file is part of the Kate project. + * + * Copyright (C) 2010 Christoph Cullmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _PLUGIN_KATE_PROJECT_H_ +#define _PLUGIN_KATE_PROJECT_H_ + +#include +#include + +#include + +#include +#include +#include + +#include "kateproject.h" +#include "kateprojectcompletion.h" + +class KateProjectPlugin : public Kate::Plugin +{ + Q_OBJECT + + public: + explicit KateProjectPlugin( QObject* parent = 0, const QList& = QList() ); + virtual ~KateProjectPlugin(); + + Kate::PluginView *createView( Kate::MainWindow *mainWindow ); + + /** + * Create new project for given project filename. + * Null pointer if no project can be opened. + * File name will be canonicalized! + * @param fileName canonicalized file name for the project + * @return project or null if not openable + */ + KateProject *createProjectForFileName (const QString &fileName); + + /** + * Search and open project for given dir, if possible. + * Will search upwards for .kateproject file. + * Will use internally projectForFileName if project file is found. + * @param dir dir to search matching project for + * @return project or null if not openable + */ + KateProject *projectForDir (QDir dir); + + /** + * Search and open project that contains given url, if possible. + * Will search upwards for .kateproject file, if the url is a local file. + * Will use internally projectForDir. + * @param url url to search matching project for + * @return project or null if not openable + */ + KateProject *projectForUrl (const KUrl &url); + + /** + * get list of all current open projects + * @return list of all open projects + */ + QList projects () const + { + return m_projects; + } + + /** + * Get global code completion. + * @return global completion object for KTextEditor::View + */ + KateProjectCompletion *completion () + { + return &m_completion; + } + + /** + * Map current open documents to projects. + * @param document document we want to know which project it belongs to + * @return project or 0 if none found for this document + */ + KateProject *projectForDocument (KTextEditor::Document *document) + { + return m_document2Project.value (document); + } + + signals: + /** + * Signal that a new project got created. + * @param project new created project + */ + void projectCreated (KateProject *project); + + public slots: + /** + * New document got created, we need to update our connections + * @param document new created document + */ + void slotDocumentCreated (KTextEditor::Document *document); + + /** + * Document got destroyed. + * @param document deleted document + */ + void slotDocumentDestroyed (QObject *document); + + /** + * Url changed, to auto-load projects + */ + void slotDocumentUrlChanged (KTextEditor::Document *document); + + /** + * did some project file change? + * @param path name of directory that did change + */ + void slotDirectoryChanged (const QString &path); + + private: + /** + * open plugins, maps project base directory => project + */ + QList m_projects; + + /** + * filesystem watcher to keep track of all project files + * and auto-reload + */ + QFileSystemWatcher m_fileWatcher; + + /** + * Mapping document => project + */ + QHash m_document2Project; + + /** + * Project completion + */ + KateProjectCompletion m_completion; +}; + +#endif + +// kate: space-indent on; indent-width 2; replace-tabs on; + diff --git a/kate/addons/kate/project/kateprojectpluginview.cpp b/kate/addons/kate/project/kateprojectpluginview.cpp new file mode 100644 index 00000000..04a3e50a --- /dev/null +++ b/kate/addons/kate/project/kateprojectpluginview.cpp @@ -0,0 +1,413 @@ +/* This file is part of the Kate project. + * + * Copyright (C) 2010 Christoph Cullmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "kateprojectpluginview.h" +#include "moc_kateprojectpluginview.cpp" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +K_PLUGIN_FACTORY(KateProjectPluginFactory, registerPlugin();) +K_EXPORT_PLUGIN(KateProjectPluginFactory(KAboutData("project", "kateproject", + ki18n("Kate Project"), "0.1", ki18n("Manage your projects with ease"))) ) + +KateProjectPluginView::KateProjectPluginView( KateProjectPlugin *plugin, Kate::MainWindow *mainWin ) + : Kate::PluginView( mainWin ) + , Kate::XMLGUIClient(KateProjectPluginFactory::componentData()) + , m_plugin (plugin) +{ + /** + * create toolviews + */ + m_toolView = mainWindow()->createToolView ("kateproject", + Kate::MainWindow::Left, SmallIcon("project-open"), i18n("Projects")); + m_toolInfoView = mainWindow()->createToolView ("kateprojectinfo", + Kate::MainWindow::Bottom, SmallIcon("view-choose"), i18n("Current Project")); + + /** + * create the combo + buttons for the toolViews + stacked widgets + */ + m_projectsCombo = new QComboBox (m_toolView); + m_reloadButton = new QToolButton (m_toolView); + m_reloadButton->setIcon (SmallIcon("view-refresh")); + QHBoxLayout *layout = new QHBoxLayout (); + layout->setSpacing (0); + layout->addWidget (m_projectsCombo); + layout->addWidget (m_reloadButton); + m_toolView->layout()->addItem (layout); + + m_stackedProjectViews = new QStackedWidget (m_toolView); + m_stackedProjectInfoViews = new QStackedWidget (m_toolInfoView); + + /** + * create views for all already existing projects + */ + foreach (KateProject *project, m_plugin->projects()) + viewForProject (project); + + /** + * create new project widget + */ + m_newProject = new KateProjectNew(m_toolView); + connect (m_newProject, SIGNAL(projectCreated(QString)), this, SLOT(slotProjectCreated(QString))); + + /** + * connect to important signals, e.g. for auto project view creation + */ + connect (m_plugin, SIGNAL(projectCreated (KateProject *)), this, SLOT(viewForProject (KateProject *))); + connect (mainWindow(), SIGNAL(viewChanged ()), this, SLOT(slotViewChanged ())); + connect (m_projectsCombo, SIGNAL(currentIndexChanged (int)), this, SLOT(slotCurrentChanged (int))); + connect (mainWindow(), SIGNAL(viewCreated (KTextEditor::View *)), this, SLOT(slotViewCreated (KTextEditor::View *))); + connect (m_reloadButton, SIGNAL(clicked (bool)), this, SLOT(slotProjectReload ())); + + /** + * connect for all already existing views + */ + foreach (KTextEditor::View *view, mainWindow()->views()) + slotViewCreated (view); + + /** + * trigger once view change, to highlight right document + */ + slotViewChanged (); + + /** + * new + project + */ + actionCollection()->addAction (KStandardAction::New, "projects_new_project", this, + SLOT(slotProjectNew())); + actionCollection()->addAction (KStandardAction::Open, "projects_open_project", this, + SLOT(slotProjectOpen())); + /** + * back + forward + */ + actionCollection()->addAction (KStandardAction::Back, "projects_prev_project", this, + SLOT(slotProjectPrev()))->setShortcut (Qt::CTRL | Qt::ALT | Qt::Key_Left); + actionCollection()->addAction (KStandardAction::Forward, "projects_next_project", this, + SLOT(slotProjectNext()))->setShortcut (Qt::CTRL | Qt::ALT | Qt::Key_Right); + + /** + * add us to gui + */ + mainWindow()->guiFactory()->addClient( this ); +} + +KateProjectPluginView::~KateProjectPluginView() +{ + /** + * cleanup for all views + */ + foreach (QObject *view, m_textViews) { + KTextEditor::CodeCompletionInterface *cci = qobject_cast(view); + if (cci) + cci->unregisterCompletionModel (m_plugin->completion()); + } + + /** + * cu toolviews + */ + delete m_newProject; + delete m_toolView; + delete m_toolInfoView; + + /** + * cu gui client + */ + mainWindow()->guiFactory()->removeClient( this ); +} + +QPair KateProjectPluginView::viewForProject (KateProject *project) +{ + /** + * needs valid project + */ + Q_ASSERT (project); + + /** + * existing view? + */ + if (m_project2View.contains (project)) + return m_project2View.value (project); + + /** + * create new views + */ + KateProjectView *view = new KateProjectView (this, project); + KateProjectInfoView *infoView = new KateProjectInfoView (this, project); + + /** + * attach to toolboxes + * first the views, then the combo, that triggers signals + */ + m_stackedProjectViews->addWidget (view); + m_stackedProjectInfoViews->addWidget (infoView); + m_projectsCombo->addItem (SmallIcon("project-open"), project->name(), project->fileName()); + + /** + * remember and return it + */ + return (m_project2View[project] = QPair (view, infoView)); +} + +void KateProjectPluginView::readSessionConfig( KConfigBase* config, const QString& groupPrefix ) +{ + // If you have session-dependant settings, load them here. + // If you have application wide settings, you have to read your own KConfig, + // see the Kate::Plugin docs for more information. + Q_UNUSED( config ); + Q_UNUSED( groupPrefix ); +} + +void KateProjectPluginView::writeSessionConfig( KConfigBase* config, const QString& groupPrefix ) +{ + // If you have session-dependant settings, save them here. + // If you have application wide settings, you have to create your own KConfig, + // see the Kate::Plugin docs for more information. + Q_UNUSED( config ); + Q_UNUSED( groupPrefix ); +} + +QString KateProjectPluginView::projectFileName () const +{ + QWidget *active = m_stackedProjectViews->currentWidget (); + if (!active) + return QString (); + + return static_cast (active)->project()->fileName (); +} + +QString KateProjectPluginView::projectName () const +{ + QWidget *active = m_stackedProjectViews->currentWidget (); + if (!active) + return QString (); + + return static_cast (active)->project()->name (); +} + +QString KateProjectPluginView::projectBaseDir () const +{ + QWidget *active = m_stackedProjectViews->currentWidget (); + if (!active) + return QString (); + + return static_cast (active)->project()->baseDir (); +} + +QVariantMap KateProjectPluginView::projectMap () const +{ + QWidget *active = m_stackedProjectViews->currentWidget (); + if (!active) + return QVariantMap (); + + return static_cast (active)->project()->projectMap (); +} + +QStringList KateProjectPluginView::projectFiles () const +{ + KateProjectView *active = static_cast (m_stackedProjectViews->currentWidget ()); + if (!active) + return QStringList (); + + return active->project()->files (); +} + +void KateProjectPluginView::slotViewChanged () +{ + /** + * get active view + */ + KTextEditor::View *activeView = mainWindow()->activeView (); + + /** + * update pointer, maybe disconnect before + */ + if (m_activeTextEditorView) + m_activeTextEditorView->document()->disconnect (this); + m_activeTextEditorView = activeView; + + /** + * no current active view, return + */ + if (!m_activeTextEditorView) + return; + + /** + * connect to url changed, for auto load + */ + connect (m_activeTextEditorView->document(), SIGNAL(documentUrlChanged (KTextEditor::Document *)), this, SLOT(slotDocumentUrlChanged (KTextEditor::Document *))); + + /** + * trigger slot once + */ + slotDocumentUrlChanged (m_activeTextEditorView->document()); +} + +void KateProjectPluginView::slotCurrentChanged (int index) +{ + /** + * trigger change of stacked widgets + */ + m_stackedProjectViews->setCurrentIndex (index); + m_stackedProjectInfoViews->setCurrentIndex (index); + + /** + * open currently selected document + */ + if (QWidget *current = m_stackedProjectViews->currentWidget ()) + static_cast (current)->openSelectedDocument (); + + /** + * project file name might have changed + */ + emit projectFileNameChanged (); + emit projectMapChanged (); +} + +void KateProjectPluginView::slotDocumentUrlChanged (KTextEditor::Document *document) +{ + /** + * abort if empty url or no local path + */ + if (document->url().isEmpty() || !document->url().isLocalFile()) + return; + + /** + * search matching project + */ + KateProject *project = m_plugin->projectForUrl (document->url()); + if (!project) + return; + + /** + * select the file FIRST + */ + m_project2View.value (project).first->selectFile (document->url().toLocalFile ()); + + /** + * get active project view and switch it, if it is for a different project + * do this AFTER file selection + */ + KateProjectView *active = static_cast (m_stackedProjectViews->currentWidget ()); + if (active != m_project2View.value (project).first) { + int index = m_projectsCombo->findData (project->fileName()); + if (index >= 0) + m_projectsCombo->setCurrentIndex (index); + } +} + +void KateProjectPluginView::slotProjectCreated(QString path) +{ + m_plugin->createProjectForFileName(path); +} + +void KateProjectPluginView::slotViewCreated (KTextEditor::View *view) +{ + /** + * connect to destroyed + */ + connect (view, SIGNAL(destroyed (QObject *)), this, SLOT(slotViewDestroyed (QObject *))); + + /** + * add completion model if possible + */ + KTextEditor::CodeCompletionInterface *cci = qobject_cast(view); + if (cci) + cci->registerCompletionModel (m_plugin->completion()); + + /** + * remember for this view we need to cleanup! + */ + m_textViews.insert (view); +} + +void KateProjectPluginView::slotViewDestroyed (QObject *view) +{ + /** + * remove remembered views for which we need to cleanup on exit! + */ + m_textViews.remove (view); +} + +void KateProjectPluginView::slotProjectNew () +{ + m_newProject->show(); +} + +void KateProjectPluginView::slotProjectOpen () +{ + QString path = KFileDialog::getExistingDirectory(KUrl(), m_toolView, i18n("Select project path")); + if (!path.isEmpty()) { + QFile kateproject(path + QLatin1String("/.kateproject")); + if (!kateproject.exists()) { + KMessageBox::error(m_toolView, i18n("Path is not valid Kate project"), i18n("Invalid project path")); + } else if (!projectFiles().contains(kateproject.fileName())) { + m_plugin->createProjectForFileName(kateproject.fileName()); + } + } +} + +void KateProjectPluginView::slotProjectPrev () +{ + if (!m_projectsCombo->count()) + return; + + if (m_projectsCombo->currentIndex () == 0) + m_projectsCombo->setCurrentIndex (m_projectsCombo->count()-1); + else + m_projectsCombo->setCurrentIndex (m_projectsCombo->currentIndex () - 1); +} + +void KateProjectPluginView::slotProjectNext () +{ + if (!m_projectsCombo->count()) + return; + + if (m_projectsCombo->currentIndex () + 1 == m_projectsCombo->count()) + m_projectsCombo->setCurrentIndex (0); + else + m_projectsCombo->setCurrentIndex (m_projectsCombo->currentIndex () + 1); +} + +void KateProjectPluginView::slotProjectReload () +{ + /** + * force reload if any active project + */ + if (QWidget *current = m_stackedProjectViews->currentWidget ()) + static_cast (current)->project()->reload (true); +} + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/addons/kate/project/kateprojectpluginview.h b/kate/addons/kate/project/kateprojectpluginview.h new file mode 100644 index 00000000..2257f7ff --- /dev/null +++ b/kate/addons/kate/project/kateprojectpluginview.h @@ -0,0 +1,222 @@ +/* This file is part of the Kate project. + * + * Copyright (C) 2010 Christoph Cullmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _PLUGIN_KATE_PROJECTVIEW_H_ +#define _PLUGIN_KATE_PROJECTVIEW_H_ + +#include "kateprojectplugin.h" +#include "kateproject.h" +#include "kateprojectview.h" +#include "kateprojectinfoview.h" +#include "kateprojectnew.h" + +#include +#include +#include +#include + +class KateProjectPluginView : public Kate::PluginView, public Kate::XMLGUIClient +{ + Q_OBJECT + + Q_PROPERTY(QString projectFileName READ projectFileName NOTIFY projectFileNameChanged) + Q_PROPERTY(QString projectName READ projectName) + Q_PROPERTY(QString projectBaseDir READ projectBaseDir) + Q_PROPERTY(QVariantMap projectMap READ projectMap NOTIFY projectMapChanged) + Q_PROPERTY(QStringList projectFiles READ projectFiles) + + public: + KateProjectPluginView( KateProjectPlugin *plugin, Kate::MainWindow *mainWindow ); + ~KateProjectPluginView(); + + virtual void readSessionConfig( KConfigBase* config, const QString& groupPrefix ); + virtual void writeSessionConfig( KConfigBase* config, const QString& groupPrefix ); + + /** + * content of current active project, as variant map + * @return empty map if no project active, else content of project JSON + */ + QVariantMap projectMap () const; + + /** + * which project file is currently active? + * @return empty string if none, else project file name + */ + QString projectFileName () const; + + /** + * Returns the name of the project + */ + QString projectName () const; + + /** + * Returns the base directory of the project + */ + QString projectBaseDir () const; + + /** + * files for the current active project? + * @return empty list if none, else project files as stringlist + */ + QStringList projectFiles () const; + + public slots: + /** + * Create views for given project. + * Either gives existing ones or creates new one + * @param project project we want view for + * @return views (normal + info view) + */ + QPair viewForProject (KateProject *project); + + private slots: + /** + * New view got created, we need to update our connections + * @param view new created view + */ + void slotViewCreated (KTextEditor::View *view); + + /** + * View got destroyed. + * @param view deleted view + */ + void slotViewDestroyed (QObject *view); + + /** + * Create new project. + */ + void slotProjectNew (); + + /** + * Open existing project. + */ + void slotProjectOpen (); + + /** + * Activate the previous project. + */ + void slotProjectPrev (); + + /** + * Activate the next project. + */ + void slotProjectNext (); + + /** + * Reload current project, if any. + * This will trigger a reload with force. + */ + void slotProjectReload (); + + Q_SIGNALS: + /** + * Emitted if projectFileName changed. + */ + void projectFileNameChanged (); + + /** + * Emitted if projectMap changed. + */ + void projectMapChanged (); + + private slots: + /** + * This slot is called whenever the active view changes in our main window. + */ + void slotViewChanged (); + + /** + * Current project changed. + * @param index index in toolbox + */ + void slotCurrentChanged (int index); + + /** + * Url changed, to auto-load projects + */ + void slotDocumentUrlChanged (KTextEditor::Document *document); + + /** + * A new project was created, load it + */ + void slotProjectCreated(QString path); + + private: + /** + * our plugin + */ + KateProjectPlugin *m_plugin; + + /** + * our projects toolview + */ + QWidget *m_toolView; + + /** + * our projects info toolview + */ + QWidget *m_toolInfoView; + + /** + * combo box with all loaded projects inside + */ + QComboBox *m_projectsCombo; + + /** + * Reload button + */ + QToolButton *m_reloadButton; + + /** + * stacked widget will all currently created project views + */ + QStackedWidget *m_stackedProjectViews; + + /** + * stacked widget will all currently created project info views + */ + QStackedWidget *m_stackedProjectInfoViews; + + /** + * project => view + */ + QMap > m_project2View; + + /** + * remember current active view text editor view + * might be 0 + */ + QPointer m_activeTextEditorView; + + /** + * remember for which text views we might need to cleanup stuff + */ + QSet m_textViews; + + /* + * new project widget + */ + KateProjectNew *m_newProject; +}; + +#endif + +// kate: space-indent on; indent-width 2; replace-tabs on; + diff --git a/kate/addons/kate/project/kateprojecttreeviewcontextmenu.cpp b/kate/addons/kate/project/kateprojecttreeviewcontextmenu.cpp new file mode 100644 index 00000000..6b26d202 --- /dev/null +++ b/kate/addons/kate/project/kateprojecttreeviewcontextmenu.cpp @@ -0,0 +1,173 @@ +/* This file is part of the Kate project. + * + * Copyright (C) 2013 Dominik Haumann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "kateprojecttreeviewcontextmenu.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +KateProjectTreeViewContextMenu::KateProjectTreeViewContextMenu () +{ +} + +KateProjectTreeViewContextMenu::~KateProjectTreeViewContextMenu () +{ +} + +static inline bool isGit(const QString& filename) +{ + QFileInfo fi(filename); + QDir dir (fi.absoluteDir()); + QProcess git; + git.setWorkingDirectory (dir.absolutePath()); + QStringList args; + args << "ls-files" << fi.fileName(); + git.start("git", args); + bool isGit = false; + if (git.waitForStarted() && git.waitForFinished()) { + QStringList files = QString::fromLocal8Bit (git.readAllStandardOutput ()).split (QRegExp("[\n\r]"), QString::SkipEmptyParts); + isGit = files.contains(fi.fileName()); + } + return isGit; +} + +void KateProjectTreeViewContextMenu::exec(const QString& filename, const QPoint& pos, QWidget* parent) +{ + /** + * create context menu + */ + QMenu menu; + + QAction *copyAction=menu.addAction(KIcon("edit-copy"),i18n("Copy Filename")); + + /** + * handle "open with" + * find correct mimetype to query for possible applications + */ + QMenu *openWithMenu = menu.addMenu(i18n("Open With")); + KMimeType::Ptr mimeType = KMimeType::findByPath(filename); + KService::List offers = KMimeTypeTrader::self()->query(mimeType->name(), "Application"); + + /** + * for each one, insert a menu item... + */ + for(KService::List::Iterator it = offers.begin(); it != offers.end(); ++it) + { + KService::Ptr service = *it; + if (service->name() == "Kate") continue; // omit Kate + QAction *action = openWithMenu->addAction(KIcon(service->icon()), service->name()); + action->setData(service->entryPath()); + } + + /** + * handle "open directory with" + * the mimetype to query for possible applications is known + */ + // QFileInfo dirname(filename); + QMenu *openDirectoryWithMenu = menu.addMenu(i18n("Open Directory With")); + KService::List dirOffers = KMimeTypeTrader::self()->query("inode/directory", "Application"); + + /** + * for each one, insert a menu item... + */ + for(KService::List::Iterator it = dirOffers.begin(); it != dirOffers.end(); ++it) + { + KService::Ptr service = *it; + QAction *action = openDirectoryWithMenu->addAction(KIcon(service->icon()), service->name()); + action->setData(service->entryPath()); + } + + /** + * perhaps disable menu, if no entries! + */ + openWithMenu->setEnabled(!openWithMenu->isEmpty()); + openDirectoryWithMenu->setEnabled(!openDirectoryWithMenu->isEmpty()); + + QList appActions; + if (isGit(filename)) { + QMenu* git = menu.addMenu(i18n("Git Tools")); + if (!KStandardDirs::findExe("gitk").isEmpty()) { + QAction* action = git->addAction(i18n("Launch gitk")); + action->setData("gitk"); + appActions.append(action); + } + if (!KStandardDirs::findExe("qgit").isEmpty()) { + QAction* action = git->addAction(i18n("Launch qgit")); + action->setData("qgit"); + appActions.append(action); + } + if (!KStandardDirs::findExe("git-cola").isEmpty()) { + QAction* action = git->addAction(i18n("Launch git-cola")); + action->setData("git-cola"); + appActions.append(action); + } + + if (appActions.size() == 0) { + delete git; + } + } + + /** + * run menu and handle the triggered action + */ + if (QAction *action = menu.exec (pos)) { + if (copyAction == action) { + // handle copy + QApplication::clipboard()->setText(filename); + } else if (appActions.contains(action)) { + // handle app action + QFileInfo fi(filename); + QDir dir (fi.absoluteDir()); + + QStringList args; + args << filename; + + QProcess::startDetached(action->data().toString(), QStringList(), dir.absolutePath()); + } else if(openDirectoryWithMenu == action->parentWidget()) { + // handle open directory with + const QString openDirWith = action->data().toString(); + if (KService::Ptr app = KService::serviceByDesktopPath(openDirWith)) { + QFileInfo fi(filename); + QList list; + list << QUrl::fromLocalFile (fi.dir().absolutePath()); + KRun::run(*app, list, parent); + } + } else { + // open with + const QString openWith = action->data().toString(); + if (KService::Ptr app = KService::serviceByDesktopPath(openWith)) { + QList list; + list << QUrl::fromLocalFile (filename); + KRun::run(*app, list, parent); + } + } + } +} + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/addons/kate/project/kateprojecttreeviewcontextmenu.h b/kate/addons/kate/project/kateprojecttreeviewcontextmenu.h new file mode 100644 index 00000000..aa9aac53 --- /dev/null +++ b/kate/addons/kate/project/kateprojecttreeviewcontextmenu.h @@ -0,0 +1,55 @@ +/* This file is part of the Kate project. + * + * Copyright (C) 2013 Dominik Haumann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef KATE_PROJECT_VIEW_TREE_CONTEXT_MENU_H +#define KATE_PROJECT_VIEW_TREE_CONTEXT_MENU_H + +#include +#include + +#include + +class KateProjectTreeViewContextMenu +{ + public: + /** + * construct project view for given project + * @param pluginView our plugin view + * @param project project this view is for + */ + KateProjectTreeViewContextMenu (); + + /** + * deconstruct project + */ + ~KateProjectTreeViewContextMenu (); + + /** + * our project. + * @return project + */ + void exec(const QString& filename, const QPoint& pos, QWidget* parent); + + protected: +}; + +#endif + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/addons/kate/project/kateprojectview.cpp b/kate/addons/kate/project/kateprojectview.cpp new file mode 100644 index 00000000..77449201 --- /dev/null +++ b/kate/addons/kate/project/kateprojectview.cpp @@ -0,0 +1,88 @@ +/* This file is part of the Kate project. + * + * Copyright (C) 2012 Christoph Cullmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "kateprojectview.h" +#include "kateprojectpluginview.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +KateProjectView::KateProjectView (KateProjectPluginView *pluginView, KateProject *project) + : QWidget () + , m_pluginView (pluginView) + , m_project (project) + , m_treeView (new KateProjectViewTree(pluginView, project)) + , m_filter (new KLineEdit ()) +{ + /** + * layout tree view and co. + */ + QVBoxLayout *layout = new QVBoxLayout (); + layout->setSpacing (0); + layout->setContentsMargins (0, 0, 0, 0); + layout->addWidget (m_treeView); + layout->addWidget (m_filter); + setLayout (layout); + + /** + * do some stuff if line edit is changed + */ + connect (m_filter, SIGNAL(textChanged(QString)), this, SLOT(filterTextChanged(QString))); +} + +KateProjectView::~KateProjectView () +{ +} + +void KateProjectView::selectFile (const QString &file) +{ + m_treeView->selectFile (file); +} + +void KateProjectView::openSelectedDocument () +{ + m_treeView->openSelectedDocument (); +} + +void KateProjectView::filterTextChanged (QString filterText) +{ + /** + * filter + */ + static_cast(m_treeView->model ())->setFilterFixedString (filterText); + + /** + * expand + */ + if (!filterText.isEmpty()) + QTimer::singleShot (100, m_treeView, SLOT(expandAll ())); +} + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/addons/kate/project/kateprojectview.h b/kate/addons/kate/project/kateprojectview.h new file mode 100644 index 00000000..baa2d765 --- /dev/null +++ b/kate/addons/kate/project/kateprojectview.h @@ -0,0 +1,102 @@ +/* This file is part of the Kate project. + * + * Copyright (C) 2010 Christoph Cullmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef KATE_PROJECT_VIEW_H +#define KATE_PROJECT_VIEW_H + +#include "kateproject.h" +#include "kateprojectviewtree.h" + +class KLineEdit; +class KateProjectPluginView; + +/** + * Class representing a view of a project. + * A tree like view of project content. + */ +class KateProjectView : public QWidget +{ + Q_OBJECT + + public: + /** + * construct project view for given project + * @param pluginView our plugin view + * @param project project this view is for + */ + KateProjectView (KateProjectPluginView *pluginView, KateProject *project); + + /** + * deconstruct project + */ + ~KateProjectView (); + + /** + * our project. + * @return project + */ + KateProject *project () const + { + return m_project; + } + + /** + * Select given file in the view. + * @param file select this file in the view, will be shown if invisible + */ + void selectFile (const QString &file); + + /** + * Open the selected document, if any. + */ + void openSelectedDocument (); + + private Q_SLOTS: + /** + * React on filter change + * @param filterText new filter text + */ + void filterTextChanged (QString filterText); + + private: + /** + * our plugin view + */ + KateProjectPluginView *m_pluginView; + + /** + * our project + */ + KateProject *m_project; + + /** + * our tree view + */ + KateProjectViewTree *m_treeView; + + /** + * filter + */ + KLineEdit *m_filter; +}; + +#endif + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/addons/kate/project/kateprojectviewtree.cpp b/kate/addons/kate/project/kateprojectviewtree.cpp new file mode 100644 index 00000000..23852a75 --- /dev/null +++ b/kate/addons/kate/project/kateprojectviewtree.cpp @@ -0,0 +1,149 @@ +/* This file is part of the Kate project. + * + * Copyright (C) 2012 Christoph Cullmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "kateprojectviewtree.h" +#include "kateprojectpluginview.h" +#include "kateprojecttreeviewcontextmenu.h" + +#include +#include + +#include +#include + +KateProjectViewTree::KateProjectViewTree (KateProjectPluginView *pluginView, KateProject *project) + : QTreeView () + , m_pluginView (pluginView) + , m_project (project) +{ + /** + * default style + */ + setHeaderHidden (true); + setEditTriggers (QAbstractItemView::NoEditTriggers); + + /** + * attach view => project + * do this once, model is stable for whole project life time + * kill selection model + * create sort proxy model + */ + QItemSelectionModel *m = selectionModel(); + QSortFilterProxyModel *sortModel = new KRecursiveFilterProxyModel (this); + //sortModel->setFilterRole(SortFilterRole); + //sortModel->setSortRole(SortFilterRole); + sortModel->setFilterCaseSensitivity(Qt::CaseInsensitive); + sortModel->setSortCaseSensitivity(Qt::CaseInsensitive); + sortModel->setSourceModel (m_project->model ()); + setModel (sortModel); + delete m; + + /** + * connect needed signals + */ + connect (this, SIGNAL(clicked (const QModelIndex &)), this, SLOT(slotClicked (const QModelIndex &))); + connect (m_project, SIGNAL(modelChanged ()), this, SLOT(slotModelChanged ())); + + /** + * trigger once some slots + */ + slotModelChanged (); +} + +KateProjectViewTree::~KateProjectViewTree () +{ +} + +void KateProjectViewTree::selectFile (const QString &file) +{ + /** + * get item if any + */ + QStandardItem *item = m_project->itemForFile (file); + if (!item) + return; + + /** + * select it + */ + QModelIndex index = static_cast(model())->mapFromSource (m_project->model()->indexFromItem (item)); + scrollTo (index, QAbstractItemView::EnsureVisible); + selectionModel()->setCurrentIndex (index, QItemSelectionModel::Clear | QItemSelectionModel::Select); +} + +void KateProjectViewTree::openSelectedDocument () +{ + /** + * anything selected? + */ + QModelIndexList selecteStuff = selectedIndexes (); + if (selecteStuff.isEmpty()) + return; + + /** + * open document for first element, if possible + */ + QString filePath = selecteStuff[0].data (Qt::UserRole).toString(); + if (!filePath.isEmpty()) + m_pluginView->mainWindow()->openUrl (KUrl::fromPath (filePath)); +} + +void KateProjectViewTree::slotClicked (const QModelIndex &index) +{ + /** + * open document, if any usable user data + */ + QString filePath = index.data (Qt::UserRole).toString(); + if (!filePath.isEmpty()) { + m_pluginView->mainWindow()->openUrl (KUrl::fromPath (filePath)); + selectionModel()->setCurrentIndex (index, QItemSelectionModel::Clear | QItemSelectionModel::Select); + } +} + +void KateProjectViewTree::slotModelChanged () +{ + /** + * model was updated + * perhaps we need to highlight again new file + */ + KTextEditor::View *activeView = m_pluginView->mainWindow()->activeView (); + if (activeView && activeView->document()->url().isLocalFile()) + selectFile (activeView->document()->url().toLocalFile ()); +} + +void KateProjectViewTree::contextMenuEvent (QContextMenuEvent *event) +{ + /** + * get path file path or don't do anything + */ + QModelIndex index = selectionModel()->currentIndex(); + QString filePath = index.data (Qt::UserRole).toString(); + if (filePath.isEmpty()) { + QTreeView::contextMenuEvent (event); + return; + } + + KateProjectTreeViewContextMenu menu; + menu.exec(filePath, viewport()->mapToGlobal(event->pos()), this); + + event->accept(); +} + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/addons/kate/project/kateprojectviewtree.h b/kate/addons/kate/project/kateprojectviewtree.h new file mode 100644 index 00000000..8f457bb0 --- /dev/null +++ b/kate/addons/kate/project/kateprojectviewtree.h @@ -0,0 +1,104 @@ +/* This file is part of the Kate project. + * + * Copyright (C) 2010 Christoph Cullmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef KATE_PROJECT_VIEW_TREE_H +#define KATE_PROJECT_VIEW_TREE_H + +#include "kateproject.h" + +#include + +class KateProjectPluginView; + +/** + * A tree like view of project content. + */ +class KateProjectViewTree : public QTreeView +{ + Q_OBJECT + + public: + /** + * construct project view for given project + * @param pluginView our plugin view + * @param project project this view is for + */ + KateProjectViewTree (KateProjectPluginView *pluginView, KateProject *project); + + /** + * deconstruct project + */ + ~KateProjectViewTree (); + + /** + * our project. + * @return project + */ + KateProject *project () const + { + return m_project; + } + + /** + * Select given file in the view. + * @param file select this file in the view, will be shown if invisible + */ + void selectFile (const QString &file); + + /** + * Open the selected document, if any. + */ + void openSelectedDocument (); + + private Q_SLOTS: + /** + * item got clicked, do stuff, like open document + * @param index model index of clicked item + */ + void slotClicked (const QModelIndex &index); + + /** + * Triggered on model changes. + * This includes the files list, itemForFile mapping! + */ + void slotModelChanged (); + + protected: + /** + * Create matching context menu. + * @param event context menu event + */ + void contextMenuEvent (QContextMenuEvent *event); + + private: + /** + * our plugin view + */ + KateProjectPluginView *m_pluginView; + + /** + * our project + */ + KateProject *m_project; +}; + +#endif + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/addons/kate/project/kateprojectworker.cpp b/kate/addons/kate/project/kateprojectworker.cpp new file mode 100644 index 00000000..ae040963 --- /dev/null +++ b/kate/addons/kate/project/kateprojectworker.cpp @@ -0,0 +1,401 @@ +/* This file is part of the Kate project. + * + * Copyright (C) 2012 Christoph Cullmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "kateprojectworker.h" +#include "kateproject.h" + +#include +#include +#include +#include +#include +#include +#include + +KateProjectWorker::KateProjectWorker (QObject *project) + : QObject () + , m_project (project) +{ +} + +KateProjectWorker::~KateProjectWorker () +{ +} + +void KateProjectWorker::loadProject (QString baseDir, QVariantMap projectMap) +{ + /** + * setup project base directory + * this should be FIX after initial setting + */ + Q_ASSERT (m_baseDir.isEmpty() || (m_baseDir == baseDir)); + m_baseDir = baseDir; + + /** + * Create dummy top level parent item and empty map inside shared pointers + * then load the project recursively + */ + KateProjectSharedQStandardItem topLevel (new QStandardItem ()); + KateProjectSharedQMapStringItem file2Item (new QMap ()); + loadProject (topLevel.data(), projectMap, file2Item.data()); + + /** + * create some local backup of some data we need for further processing! + */ + QStringList files = file2Item->keys (); + + /** + * feed back our results + */ + QMetaObject::invokeMethod (m_project, "loadProjectDone", Qt::QueuedConnection, Q_ARG(KateProjectSharedQStandardItem, topLevel), Q_ARG(KateProjectSharedQMapStringItem, file2Item)); + + /** + * load index + */ + loadIndex (files); +} + +void KateProjectWorker::loadProject (QStandardItem *parent, const QVariantMap &project, QMap *file2Item) +{ + /** + * recurse to sub-projects FIRST + */ + QVariantList subGroups = project["projects"].toList (); + foreach (const QVariant &subGroupVariant, subGroups) { + /** + * convert to map and get name, else skip + */ + QVariantMap subProject = subGroupVariant.toMap (); + if (subProject["name"].toString().isEmpty()) + continue; + + /** + * recurse + */ + QStandardItem *subProjectItem = new KateProjectItem (KateProjectItem::Project, subProject["name"].toString()); + loadProject (subProjectItem, subProject, file2Item); + parent->appendRow (subProjectItem); + } + + /** + * load all specified files + */ + QVariantList files = project["files"].toList (); + foreach (const QVariant &fileVariant, files) + loadFilesEntry (parent, fileVariant.toMap (), file2Item); +} + +/** + * small helper to construct directory parent items + * @param dir2Item map for path => item + * @param path current path we need item for + * @return correct parent item for given path, will reuse existing ones + */ +static QStandardItem *directoryParent (QMap &dir2Item, QString path) +{ + /** + * throw away simple / + */ + if (path == "/") + path = ""; + + /** + * quick check: dir already seen? + */ + if (dir2Item.contains (path)) + return dir2Item[path]; + + /** + * else: construct recursively + */ + int slashIndex = path.lastIndexOf ('/'); + + /** + * no slash? + * simple, no recursion, append new item toplevel + */ + if (slashIndex < 0) { + dir2Item[path] = new KateProjectItem (KateProjectItem::Directory, path); + dir2Item[""]->appendRow (dir2Item[path]); + return dir2Item[path]; + } + + /** + * else, split and recurse + */ + QString leftPart = path.left (slashIndex); + QString rightPart = path.right (path.size() - (slashIndex + 1)); + + /** + * special handling if / with nothing on one side are found + */ + if (leftPart.isEmpty() || rightPart.isEmpty ()) + return directoryParent (dir2Item, leftPart.isEmpty() ? rightPart : leftPart); + + /** + * else: recurse on left side + */ + dir2Item[path] = new KateProjectItem (KateProjectItem::Directory, rightPart); + directoryParent (dir2Item, leftPart)->appendRow (dir2Item[path]); + return dir2Item[path]; +} + +void KateProjectWorker::loadFilesEntry (QStandardItem *parent, const QVariantMap &filesEntry, QMap *file2Item) +{ + /** + * get directory to open or skip + */ + QDir dir (m_baseDir); + if (!dir.cd (filesEntry["directory"].toString())) + return; + + /** + * get recursive attribute, default is TRUE + */ + const bool recursive = !filesEntry.contains ("recursive") || filesEntry["recursive"].toBool(); + + /** + * now: choose between different methodes to get files in the directory + */ + QStringList files; + + /** + * use GIT + */ + if (filesEntry["git"].toBool()) { + /** + * try to run git with ls-files for this directory + */ + QProcess git; + git.setWorkingDirectory (dir.absolutePath()); + QStringList args; + args << "ls-files" << "."; + git.start("git", args); + if (!git.waitForStarted() || !git.waitForFinished()) + return; + + /** + * get output and split up into files + */ + QStringList relFiles = QString::fromLocal8Bit (git.readAllStandardOutput ()).split (QRegExp("[\n\r]"), QString::SkipEmptyParts); + + /** + * prepend the directory path + */ + foreach (QString relFile, relFiles) { + /** + * skip non-direct files if not recursive + */ + if (!recursive && (relFile.indexOf ("/") != -1)) + continue; + + files.append (dir.absolutePath() + '/' + relFile); + } + } + + /** + * use MERCURIAL + */ + else if (filesEntry["hg"].toBool()) { + /** + * try to run "hg manifest" for this directory + */ + QProcess hg; + hg.setWorkingDirectory (dir.absolutePath()); + QStringList args; + args << "manifest" << "."; + hg.start("hg", args); + if (!hg.waitForStarted() || !hg.waitForFinished()) + return; + + /** + * get output and split up into files + */ + QStringList relFiles = QString::fromLocal8Bit (hg.readAllStandardOutput ()).split (QRegExp("[\n\r]"), QString::SkipEmptyParts); + + /** + * prepend the directory path + */ + foreach (QString relFile, relFiles) { + /** + * skip non-direct files if not recursive + */ + if (!recursive && (relFile.indexOf ("/") != -1)) + continue; + + files.append (dir.absolutePath() + '/' + relFile); + } + } + + /** + * use SVN + */ + else if (filesEntry["svn"].toBool()) { + /** + * try to run git with ls-files for this directory + */ + QProcess svn; + svn.setWorkingDirectory (dir.absolutePath()); + QStringList args; + args << "status" << "--verbose" << "."; + if (recursive) + args << "--depth=infinity"; + else + args << "--depth=files"; + svn.start("svn", args); + if (!svn.waitForStarted() || !svn.waitForFinished()) + return; + + /** + * get output and split up into lines + */ + QStringList lines = QString::fromLocal8Bit (svn.readAllStandardOutput ()).split (QRegExp("[\n\r]"), QString::SkipEmptyParts); + + /** + * remove start of line that is no filename, sort out unknown and ignore + */ + bool first = true; + int prefixLength = -1; + foreach (QString line, lines) { + /** + * get length of stuff to cut + */ + if (first) { + /** + * try to find ., else fail + */ + prefixLength = line.lastIndexOf ("."); + if (prefixLength < 0) + break; + + /** + * skip first + */ + first = false; + continue; + } + + /** + * get file, if not unknown or ignored + * prepend directory path + */ + if ((line.size() > prefixLength) && line[0] != '?' && line[0] != 'I') + files.append (dir.absolutePath() + '/' + line.right (line.size() - prefixLength)); + } + } + + else { + files = filesEntry["list"].toStringList(); + + /** + * fallback to use QDirIterator and search files ourself! + */ + if (files.empty()) { + /** + * default filter: only files! + */ + dir.setFilter (QDir::Files); + + /** + * set name filters, if any + */ + QStringList filters = filesEntry["filters"].toStringList(); + if (!filters.isEmpty()) + dir.setNameFilters (filters); + + /** + * construct flags for iterator + */ + QDirIterator::IteratorFlags flags = QDirIterator::NoIteratorFlags; + if (recursive) + flags = flags | QDirIterator::Subdirectories; + + /** + * create iterator and collect all files + */ + QDirIterator dirIterator (dir, flags); + while (dirIterator.hasNext()) { + dirIterator.next(); + files.append (dirIterator.filePath()); + } + } + } + + /** + * sort them + */ + files.sort (); + + /** + * construct paths first in tree and items in a map + */ + QMap dir2Item; + dir2Item[""] = parent; + QList > item2ParentPath; + foreach (QString filePath, files) { + /** + * get file info and skip NON-files + */ + QFileInfo fileInfo (filePath); + if (!fileInfo.isFile()) + continue; + + /** + * skip dupes + */ + if (file2Item->contains(filePath)) + continue; + + /** + * construct the item with right directory prefix + * already hang in directories in tree + */ + KateProjectItem *fileItem = new KateProjectItem (KateProjectItem::File, fileInfo.fileName()); + fileItem->setData(filePath,Qt::ToolTipRole); + item2ParentPath.append (QPair(fileItem, directoryParent(dir2Item, dir.relativeFilePath (fileInfo.absolutePath())))); + fileItem->setData (filePath, Qt::UserRole); + (*file2Item)[filePath] = fileItem; + } + + /** + * plug in the file items to the tree + */ + QList >::const_iterator i = item2ParentPath.constBegin(); + while (i != item2ParentPath.constEnd()) { + i->second->appendRow (i->first); + ++i; + } +} + +void KateProjectWorker::loadIndex (const QStringList &files) +{ + /** + * create new index, this will do the loading in the constructor + * wrap it into shared pointer for transfer to main thread + */ + KateProjectSharedProjectIndex index (new KateProjectIndex(files)); + + /** + * send new index object back to project + */ + QMetaObject::invokeMethod (m_project, "loadIndexDone", Qt::QueuedConnection, Q_ARG(KateProjectSharedProjectIndex, index)); +} + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/addons/kate/project/kateprojectworker.h b/kate/addons/kate/project/kateprojectworker.h new file mode 100644 index 00000000..ea57cd39 --- /dev/null +++ b/kate/addons/kate/project/kateprojectworker.h @@ -0,0 +1,102 @@ +/* This file is part of the Kate project. + * + * Copyright (C) 2010 Christoph Cullmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef KATE_PROJECT_WORKER_H +#define KATE_PROJECT_WORKER_H + +#include +#include + +#include "kateprojectitem.h" + +/** + * Class representing a project background worker. + * This worker will build up the model for the project on load and do other stuff in the background. + */ +class KateProjectWorker : public QObject +{ + Q_OBJECT + + public: + /** + * Type for QueuedConnection + */ + typedef QMap MapString2Item; + + /** + * construct project worker for given project + * @param project our project + */ + KateProjectWorker (QObject *project); + + /** + * deconstruct worker + */ + ~KateProjectWorker (); + + private Q_SLOTS: + /** + * Load the project. + * Will be used to load project in background. + * Will inform the project after loading was done and pass over all needed data! + * @param baseDir project file name, should stay the same after initial setup + * @param projectMap full map containing the whole project as copy to work on + */ + void loadProject (QString baseDir, QVariantMap projectMap); + + private: + /** + * Load one project inside the project tree. + * Fill data from JSON storage to model and recurse to sub-projects. + * @param parent parent standard item in the model + * @param project variant map for this group + * @param file2Item mapping file => item, will be filled + */ + void loadProject (QStandardItem *parent, const QVariantMap &project, QMap *file2Item); + + /** + * Load one files entry in the current parent item. + * @param parent parent standard item in the model + * @param filesEntry one files entry specification to load + * @param file2Item mapping file => item, will be filled + */ + void loadFilesEntry (QStandardItem *parent, const QVariantMap &filesEntry, QMap *file2Item); + + /** + * Load index for whole project. + * @param files list of all project files to index + */ + void loadIndex (const QStringList &files); + + private: + /** + * our project, only as QObject, we only send messages back and forth! + */ + QObject *m_project; + + /** + * project base directory name + */ + QString m_baseDir; +}; + +#endif + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/addons/kate/project/ui.rc b/kate/addons/kate/project/ui.rc new file mode 100644 index 00000000..3ed9adeb --- /dev/null +++ b/kate/addons/kate/project/ui.rc @@ -0,0 +1,13 @@ + + + + &Projects + + + + + + + + + diff --git a/kate/addons/kate/search/CMakeLists.txt b/kate/addons/kate/search/CMakeLists.txt new file mode 100644 index 00000000..07f46a44 --- /dev/null +++ b/kate/addons/kate/search/CMakeLists.txt @@ -0,0 +1,33 @@ +set(katesearchplugin_PART_SRCS + plugin_search.cpp + search_open_files.cpp + SearchDiskFiles.cpp + FolderFilesList.cpp + replace_matches.cpp + htmldelegate.cpp +) + +kde4_add_plugin(katesearchplugin ${katesearchplugin_PART_SRCS}) + +target_link_libraries(katesearchplugin + ${KDE4_KDEUI_LIBS} + ${KDE4_KDECORE_LIBS} + ${KDE4_KTEXTEDITOR_LIBS} + kateinterfaces +) + +########### install files ############### + +install( + TARGETS katesearchplugin + DESTINATION ${KDE4_PLUGIN_INSTALL_DIR} +) +install( + FILES ui.rc + DESTINATION ${KDE4_DATA_INSTALL_DIR}/kate/plugins/katesearch +) +install( + FILES katesearch.desktop + DESTINATION ${KDE4_SERVICES_INSTALL_DIR} +) + diff --git a/kate/addons/kate/search/FolderFilesList.cpp b/kate/addons/kate/search/FolderFilesList.cpp new file mode 100644 index 00000000..104ec47a --- /dev/null +++ b/kate/addons/kate/search/FolderFilesList.cpp @@ -0,0 +1,127 @@ +/* Kate search plugin + * + * Copyright (C) 2013 by Kåre Särs + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program in a file called COPYING; if not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#include "FolderFilesList.h" +#include "moc_FolderFilesList.cpp" +#include +#include + +#include +#include +#include + +FolderFilesList::FolderFilesList(QObject *parent) : QThread(parent) {} + +FolderFilesList::~FolderFilesList() +{ + m_cancelSearch = true; + wait(); +} + +void FolderFilesList::run() +{ + m_files.clear(); + + QFileInfo folderInfo(m_folder); + checkNextItem(folderInfo); + + if (m_cancelSearch) m_files.clear(); +} + +void FolderFilesList::generateList(const QString &folder, + bool recursive, + bool hidden, + bool symlinks, + bool binary, + const QString &types, + const QString &excludes) +{ + m_cancelSearch = false; + m_folder = folder; + m_recursive = recursive; + m_hidden = hidden; + m_symlinks = symlinks; + m_binary = binary; + m_types = types.split(',', QString::SkipEmptyParts); + + if (m_types.isEmpty()) { + m_types << "*"; + } + + QStringList tmpExcludes = excludes.split(','); + m_excludeList.clear(); + for (int i=0; i + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program in a file called COPYING; if not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#ifndef FOLDERFILESLIST_H +#define FOLDERFILESLIST_H + +#include +#include +#include +#include +#include + +class FolderFilesList: public QThread +{ + Q_OBJECT + +public: + FolderFilesList(QObject *parent = 0); + ~FolderFilesList(); + + void run(); + + void generateList(const QString &folder, + bool recursive, + bool hidden, + bool symlinks, + bool binary, + const QString &types, + const QString &excludes); + + QStringList fileList(); + +public Q_SLOTS: + void cancelSearch(); + +private: + void checkNextItem(const QFileInfo &item); + +private: + QString m_folder; + QStringList m_files; + bool m_cancelSearch; + + bool m_recursive; + bool m_hidden; + bool m_symlinks; + bool m_binary; + QStringList m_types; + QVector m_excludeList; +}; + + +#endif diff --git a/kate/addons/kate/search/Messages.sh b/kate/addons/kate/search/Messages.sh new file mode 100644 index 00000000..fc19cf2e --- /dev/null +++ b/kate/addons/kate/search/Messages.sh @@ -0,0 +1,3 @@ +#! /bin/sh +$EXTRACTRC *.rc *.ui >> rc.cpp +$XGETTEXT *.cpp -o $podir/katesearch.pot diff --git a/kate/addons/kate/search/SearchDiskFiles.cpp b/kate/addons/kate/search/SearchDiskFiles.cpp new file mode 100644 index 00000000..01ddca72 --- /dev/null +++ b/kate/addons/kate/search/SearchDiskFiles.cpp @@ -0,0 +1,176 @@ +/* Kate search plugin + * + * Copyright (C) 2011-2013 by Kåre Särs + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program in a file called COPYING; if not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#include "SearchDiskFiles.h" +#include "moc_SearchDiskFiles.cpp" +#include + +#include +#include + +SearchDiskFiles::SearchDiskFiles(QObject *parent) : QThread(parent) +,m_cancelSearch(true) +,m_matchCount(0) +{} + +SearchDiskFiles::~SearchDiskFiles() +{ + m_cancelSearch = true; + wait(); +} + +void SearchDiskFiles::startSearch(const QStringList &files, + const QRegExp ®exp) +{ + if (files.size() == 0) { + emit searchDone(); + return; + } + m_cancelSearch = false; + m_files = files; + m_regExp = regexp; + m_matchCount = 0; + m_statusTime.restart(); + start(); +} + +void SearchDiskFiles::run() +{ + foreach (QString fileName, m_files) { + if (m_cancelSearch) { + break; + } + + if (m_statusTime.elapsed() > 100) { + m_statusTime.restart(); + emit searching(fileName); + } + + if (m_regExp.pattern().contains("\\n")) { + searchMultiLineRegExp(fileName); + } + else { + searchSingleLineRegExp(fileName); + } + } + emit searchDone(); + m_cancelSearch = true; +} + +void SearchDiskFiles::cancelSearch() +{ + m_cancelSearch = true; +} + +bool SearchDiskFiles::searching() +{ + return !m_cancelSearch; +} + +void SearchDiskFiles::searchSingleLineRegExp(const QString &fileName) +{ + QFile file (fileName); + + if (!file.open(QFile::ReadOnly)) { + return; + } + + QTextStream stream (&file); + QString line; + int i = 0; + int column; + while (!(line=stream.readLine()).isNull()) { + if (m_cancelSearch) break; + column = m_regExp.indexIn(line); + while (column != -1) { + if (m_regExp.cap().isEmpty()) break; + // limit line length + if (line.length() > 512) line = line.left(512); + emit matchFound(fileName, fileName, i, column, line, m_regExp.matchedLength()); + column = m_regExp.indexIn(line, column + m_regExp.cap().size()); + m_matchCount++; + // NOTE: This sleep is here so that the main thread will get a chance to + // handle any stop button clicks if there are a lot of matches + if (m_matchCount%50) msleep(1); + } + i++; + } +} + +void SearchDiskFiles::searchMultiLineRegExp(const QString &fileName) +{ + QFile file (fileName); + int column = 0; + int line = 0; + static QString fullDoc; + static QVector lineStart; + QRegExp tmpRegExp = m_regExp; + + if (!file.open(QFile::ReadOnly)) { + return; + } + + QTextStream stream (&file); + fullDoc = stream.readAll(); + fullDoc.remove('\r'); + + lineStart.clear(); + lineStart << 0; + for (int i=0; i column) { + line = i-1; + break; + } + } + if (line == -1) { + break; + } + emit matchFound(fileName,fileName, + line, + (column - lineStart[line]), + fullDoc.mid(lineStart[line], column - lineStart[line])+tmpRegExp.cap(), + tmpRegExp.matchedLength()); + column = tmpRegExp.indexIn(fullDoc, column + tmpRegExp.matchedLength()); + m_matchCount++; + // NOTE: This sleep is here so that the main thread will get a chance to + // handle any stop button clicks if there are a lot of matches + if (m_matchCount%50) msleep(1); + } +} + diff --git a/kate/addons/kate/search/SearchDiskFiles.h b/kate/addons/kate/search/SearchDiskFiles.h new file mode 100644 index 00000000..ca6218a8 --- /dev/null +++ b/kate/addons/kate/search/SearchDiskFiles.h @@ -0,0 +1,68 @@ +/* Kate search plugin + * + * Copyright (C) 2011-2013 by Kåre Särs + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program in a file called COPYING; if not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#ifndef SEARCHDISKFILES_H +#define SEARCHDISKFILES_H + +#include +#include +#include +#include +#include +#include +#include + +class SearchDiskFiles: public QThread +{ + Q_OBJECT + +public: + SearchDiskFiles(QObject *parent = 0); + ~SearchDiskFiles(); + + void startSearch(const QStringList &iles, + const QRegExp ®exp); + void run(); + + bool searching(); + +private: + void searchSingleLineRegExp(const QString &fileName); + void searchMultiLineRegExp(const QString &fileName); + +public Q_SLOTS: + void cancelSearch(); + +Q_SIGNALS: + void matchFound(const QString &url, const QString &docName, int line, int column, + const QString &lineContent, int matchLen); + void searchDone(); + void searching(const QString &file); + +private: + QRegExp m_regExp; + QStringList m_files; + bool m_cancelSearch; + int m_matchCount; + QTime m_statusTime; +}; + + +#endif diff --git a/kate/addons/kate/search/htmldelegate.cpp b/kate/addons/kate/search/htmldelegate.cpp new file mode 100644 index 00000000..5ad6c467 --- /dev/null +++ b/kate/addons/kate/search/htmldelegate.cpp @@ -0,0 +1,76 @@ +/*************************************************************************** + * This file is part of Kate search plugin * + * Copyright 2011 Kåre Särs * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Library General Public License as * + * published by the Free Software Foundation; either version 2 of the * + * License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#include "htmldelegate.h" + +#include +#include +#include +#include +#include +#include + +#include + +SPHtmlDelegate::SPHtmlDelegate( QObject* parent ) +: QStyledItemDelegate(parent) +{} + +SPHtmlDelegate::~SPHtmlDelegate() {} + +void SPHtmlDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const +{ + QStyleOptionViewItemV4 options = option; + initStyleOption(&options, index); + + QTextDocument doc; + //doc.setDocumentMargin(0); + doc.setHtml(index.data().toString()); + + painter->save(); + options.text = QString(); // clear old text + options.widget->style()->drawControl(QStyle::CE_ItemViewItem, &options, painter, options.widget); + + // draw area + QRect clip = options.widget->style()->subElementRect(QStyle::SE_ItemViewItemText, &options); + QFontMetrics metrics(options.font); + if (index.flags() == Qt::NoItemFlags) { + painter->setBrush(QBrush(QWidget().palette().color(QPalette::Base))); + painter->setPen(QWidget().palette().color(QPalette::Base)); + painter->drawRect(QRect(clip.topLeft() - QPoint(20, metrics.descent()), clip.bottomRight())); + painter->translate(clip.topLeft() - QPoint(20, metrics.descent())); + } + else { + painter->translate(clip.topLeft() - QPoint(0, metrics.descent())); + } + QAbstractTextDocumentLayout::PaintContext pcontext; + doc.documentLayout()->draw(painter, pcontext); + + painter->restore(); +} + +QSize SPHtmlDelegate::sizeHint(const QStyleOptionViewItem& /*option*/, const QModelIndex& index) const +{ + QTextDocument doc; + //doc.setDocumentMargin(0); + doc.setHtml(index.data().toString()); + //kDebug() << doc.toPlainText() << doc.size().toSize(); + return doc.size().toSize() + QSize(30, 0); // add margin for the check-box +} diff --git a/kate/addons/kate/search/htmldelegate.h b/kate/addons/kate/search/htmldelegate.h new file mode 100644 index 00000000..9f376fc4 --- /dev/null +++ b/kate/addons/kate/search/htmldelegate.h @@ -0,0 +1,37 @@ +/*************************************************************************** + * This file is part of Kate search plugin * + * Copyright 2011 Kåre Särs * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Library General Public License as * + * published by the Free Software Foundation; either version 2 of the * + * License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#ifndef HTML_DELEGATE_H +#define HTML_DELEGATE_H + +#include + +class SPHtmlDelegate : public QStyledItemDelegate +{ +public: + explicit SPHtmlDelegate(QObject* parent); + virtual ~SPHtmlDelegate(); + + void paint(QPainter*, const QStyleOptionViewItem&, const QModelIndex&) const; + virtual QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const; +}; + +#endif + diff --git a/kate/addons/kate/search/katesearch.desktop b/kate/addons/kate/search/katesearch.desktop new file mode 100644 index 00000000..2dd0862f --- /dev/null +++ b/kate/addons/kate/search/katesearch.desktop @@ -0,0 +1,103 @@ +[Desktop Entry] +Type=Service +ServiceTypes=Kate/Plugin +X-KDE-Library=katesearchplugin +X-Kate-Version=2.9 +X-Kate-Load=True +Name=Search & Replace +Name[ar]=ابحث واستبدل +Name[bg]=Търсене и &замяна +Name[bs]=Pretraži i zamjeni +Name[ca]=Cerca i substitueix +Name[ca@valencia]=Cerca i substitueix +Name[cs]=Najít a nahradit +Name[da]=Søg og erstat +Name[de]=Suchen & Ersetzen +Name[el]=Αναζήτηση & αντικατάσταση +Name[en_GB]=Search & Replace +Name[es]=Buscar y remplazar +Name[et]=Otsimine ja asendamine +Name[fi]=Etsi ja korvaa +Name[fr]=Chercher et remplacer +Name[ga]=Cuardach agus Ionadú +Name[gl]=Buscar e substituír +Name[he]=חיפוש והחלפה +Name[hu]=Keresés és csere +Name[ia]=Cerca & Reimplacia +Name[it]=Cerca e sostituisci +Name[kk]=Іздеу & Ауыстыру +Name[km]=ស្វែងរក និង​ជំនួស +Name[ko]=찾아 바꾸기 +Name[lt]=Rasti ir keisti +Name[lv]=Meklēt un aizvietot +Name[mr]=शोधा व बदला +Name[nb]=Søk og erstatt +Name[nds]=Söken un utwesseln +Name[nl]=Zoeken en vervangen +Name[pa]=ਲੱਭੋ ਅਤੇ ਬਦਲੋ +Name[pl]=Znajdź i zamień +Name[pt]=Procurar & Substituir +Name[pt_BR]=Pesquisar e substituir +Name[ro]=Căutare și înlocuire +Name[ru]=Поиск и замена +Name[sk]=Hľadať & Nahradiť +Name[sl]=Poišči & zamenjaj +Name[sr]=Претрага и замена +Name[sr@ijekavian]=Претрага и замена +Name[sr@ijekavianlatin]=Pretraga i zamena +Name[sr@latin]=Pretraga i zamena +Name[sv]=Sök och ersätt +Name[tg]=Ҷустуҷӯ ва Ҷойгузин +Name[tr]=Bul ve Değiştir +Name[ug]=ئىزدەش ۋە ئالماشتۇرۇش +Name[uk]=Пошук з заміною +Name[x-test]=xxSearch & Replacexx +Name[zh_CN]=搜索和替换 +Name[zh_TW]=搜尋並取代 +Comment=Search & replace in opened documents or in files on disk +Comment[ar]=ابحث واستبدل في المستندات المفتوحة أو في الملفات في القرص +Comment[bg]=Търсене и замяна в отворени документи или файлове на диска +Comment[bs]=Traži i zamijeni u otvorenim dokumentima ili datotekama na disku +Comment[ca]=Cerca i substitució en documents oberts o en fitxers del disc +Comment[ca@valencia]=Cerca i substitució en documents oberts o en fitxers del disc +Comment[cs]=Najít a nahradit v otevřených dokumentech nebo souborech na disku +Comment[da]=Søg og erstat i åbne dokumenter eller i filer på disken +Comment[de]=Suchen & Ersetzen in geöffneten Dateien oder in Dateien auf der Festplatte +Comment[el]=Αναζήτηση & αντικατάσταση σε ανοιγμένα έγγραφα ή σε αρχεία στο δίσκο +Comment[en_GB]=Search & replace in opened documents or in files on disk +Comment[es]=Buscar y remplazar en los documentos abiertos o en los archivos del disco +Comment[et]=Otsimine ja asendamine avatud dokumentides või kettal asuvates failides +Comment[fi]=Etsi ja korvaa avatuista tai levyllä sijaitsevista tiedostoista +Comment[fr]=Trouver et remplacer dans des documents ouverts ou dans des fichiers sur disque +Comment[gl]=Busca e substitúe nos documentos abertos ou nos ficheiros do disco +Comment[he]=חפש והחלף בקבצים הפתוחים או בדיסק +Comment[hu]=Keresés és csere a megnyitott dokumentumokban vagy a lemezen lévő fájlokban +Comment[ia]=Cerca & reimplacia in documentos aperite o i files sur disco +Comment[it]=Cerca e sostituisci in documenti aperti o in file sul disco +Comment[kk]=Ашық құжаттарда не дискідегі файлдарда іздеу және ауыстыру +Comment[km]=ស្វែងរក និង​ជំនួស នៅ​ក្នុង​ឯកសារ​ដែល​បាន​បើក ឬ​ក្នុង​ឯកសារ​ដែល​មាន​នៅ​លើ​ថាស +Comment[ko]=열린 문서나 디스크에 있는 파일에서 찾아 바꾸기 +Comment[lt]=Rasti ir keisti atidarytuose dokumentuose arba disko failuose +Comment[lv]=Meklēt un aizvietot atvērtos dokumentos vai datnēs uz diska +Comment[mr]=उघड्या दस्तऐवजात कींवा डिस्कवरील फाईल्समध्ये शोधा व बदला +Comment[nb]=Søk og erstatt i åpne dokumenter eller i filer på disk +Comment[nds]=Söken un Utwesseln in opmaakt Dokmenten oder Dateien op de Fastplaat +Comment[nl]=Zoeken & vervangen in geopende documenten of in bestanden op de schijf +Comment[pl]=Znajdź i zamień w otwartych dokumentach lub w plikach na dysku +Comment[pt]=Procurar & substituir nos documentos abertos ou nos ficheiros do disco +Comment[pt_BR]=Pesquisa e substitui nos documentos abertos ou nos arquivos do disco +Comment[ro]=Căutare și înlocuire în documentele deschise sau în fișiere de pe disc +Comment[ru]=Поиск и замена в открытых документах или в файлах на диске +Comment[sk]=Hľadať & nahradiť v otvorených dokumentoch alebo v súboroch na disku +Comment[sl]=Poišči in zamenjaj v odprtih dokumentih ali datotekah na disku +Comment[sr]=Претрага и замена кроз отворене документе или фајлове на диску +Comment[sr@ijekavian]=Претрага и замена кроз отворене документе или фајлове на диску +Comment[sr@ijekavianlatin]=Pretraga i zamena kroz otvorene dokumente ili fajlove na disku +Comment[sr@latin]=Pretraga i zamena kroz otvorene dokumente ili fajlove na disku +Comment[sv]=Sök och ersätt i öppnade dokument eller i filer på disk +Comment[tg]=Ҷустуҷӯ ва ҷойгузин кардан дар ҳуҷҷатҳои кушода ё дар файлҳо +Comment[tr]=Açık belgelerde veya diskteki dosyalarda bul ve değiştir +Comment[uk]=Пошук з заміною у відкритих документах або у файлах на диску +Comment[x-test]=xxSearch & replace in opened documents or in files on diskxx +Comment[zh_CN]=在打开的文件或磁盘上的文件中搜索和替换 +Comment[zh_TW]=在開啟的文件或磁碟中的檔案搜尋並取代 diff --git a/kate/addons/kate/search/plugin_search.cpp b/kate/addons/kate/search/plugin_search.cpp new file mode 100644 index 00000000..261e387c --- /dev/null +++ b/kate/addons/kate/search/plugin_search.cpp @@ -0,0 +1,1791 @@ +/* Kate search plugin + * + * Copyright (C) 2011-2013 by Kåre Särs + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program in a file called COPYING; if not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#include "plugin_search.h" +#include "moc_plugin_search.cpp" + +#include "htmldelegate.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "kacceleratormanager.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +static QAction *menuEntry(QMenu *menu, + const QString &before, const QString &after, const QString &desc, + QString menuBefore = QString(), QString menuAfter = QString()); + +static QAction *menuEntry(QMenu *menu, + const QString &before, const QString &after, const QString &desc, + QString menuBefore, QString menuAfter) +{ + if (menuBefore.isEmpty()) menuBefore = before; + if (menuAfter.isEmpty()) menuAfter = after; + + QAction *const action = menu->addAction(menuBefore + menuAfter + '\t' + desc); + if (!action) return 0; + + action->setData(QString(before + ' ' + after)); + return action; +} + +class TreeWidgetItem : public QTreeWidgetItem { +public: + TreeWidgetItem(QTreeWidget* parent):QTreeWidgetItem(parent){} + TreeWidgetItem(QTreeWidget* parent, const QStringList &list):QTreeWidgetItem(parent, list){} + TreeWidgetItem(QTreeWidgetItem* parent, const QStringList &list):QTreeWidgetItem(parent, list){} +private: + bool operator<(const QTreeWidgetItem &other)const { + if (childCount() == 0) { + int line = data(0, ReplaceMatches::LineRole).toInt(); + int column = data(0, ReplaceMatches::ColumnRole).toInt(); + int oLine = other.data(0, ReplaceMatches::LineRole).toInt(); + int oColumn = other.data(0, ReplaceMatches::ColumnRole).toInt(); + if (line < oLine) { + return true; + } + if ((line == oLine) && (column < oColumn)) { + return true; + } + return false; + } + int sepCount = data(0, ReplaceMatches::FileUrlRole).toString().count(QDir::separator()); + int oSepCount = other.data(0, ReplaceMatches::FileUrlRole).toString().count(QDir::separator()); + if (sepCount < oSepCount) return true; + if (sepCount > oSepCount) return false; + return data(0, ReplaceMatches::FileUrlRole).toString().toLower() < other.data(0, ReplaceMatches::FileUrlRole).toString().toLower(); + } +}; + +Results::Results(QWidget *parent): QWidget(parent), matches(0) +{ + setupUi(this); + + tree->setItemDelegate(new SPHtmlDelegate(tree)); +} + + +K_PLUGIN_FACTORY(KatePluginSearchFactory, registerPlugin();) +K_EXPORT_PLUGIN(KatePluginSearchFactory(KAboutData("katesearch","katesearch",ki18n("Search & Replace"), "0.1", ki18n("Search & replace in files")))) + +KatePluginSearch::KatePluginSearch(QObject* parent, const QList&) + : Kate::Plugin((Kate::Application*)parent, "kate-search-plugin"), + m_searchCommand(0) +{ + KGlobal::locale()->insertCatalog("katesearch"); + + KTextEditor::CommandInterface* iface = + qobject_cast(Kate::application()->editor()); + if (iface) { + m_searchCommand = new KateSearchCommand(this); + iface->registerCommand(m_searchCommand); + } +} + +KatePluginSearch::~KatePluginSearch() +{ + KTextEditor::CommandInterface* iface = + qobject_cast(Kate::application()->editor()); + if (iface && m_searchCommand) { + iface->unregisterCommand(m_searchCommand); + } +} + +Kate::PluginView *KatePluginSearch::createView(Kate::MainWindow *mainWindow) +{ + KatePluginSearchView *view = new KatePluginSearchView(mainWindow, application()); + connect(m_searchCommand, SIGNAL(setSearchPlace(int)), view, SLOT(setSearchPlace(int))); + connect(m_searchCommand, SIGNAL(setCurrentFolder()), view, SLOT(setCurrentFolder())); + connect(m_searchCommand, SIGNAL(setSearchString(QString)), view, SLOT(setSearchString(QString))); + connect(m_searchCommand, SIGNAL(startSearch()), view, SLOT(startSearch())); + connect(m_searchCommand, SIGNAL(newTab()), view, SLOT(addTab())); + + return view; +} + + +bool ContainerWidget::focusNextPrevChild (bool next) +{ + QWidget* fw = focusWidget(); + bool found = false; + emit nextFocus(fw, &found, next); + + if (found) { + return true; + } + return QWidget::focusNextPrevChild(next); +} + +void KatePluginSearchView::nextFocus(QWidget *currentWidget, bool *found, bool next) +{ + *found = false; + + if (!currentWidget) { + return; + } + + // we use the object names here because there can be multiple replaceButtons (on multiple result tabs) + if (next) { + if (currentWidget->objectName() == "tree") { + m_ui.newTabButton->setFocus(); + *found = true; + return; + } + if (currentWidget == m_ui.displayOptions) { + if (m_ui.displayOptions->isChecked()) { + m_ui.newTabButton->setFocus(); + *found = true; + return; + } + else { + Results *res = qobject_cast(m_ui.resultTabWidget->currentWidget()); + if (!res) { + return; + } + res->tree->setFocus(); + *found = true; + return; + } + } + } + else { + if (currentWidget == m_ui.newTabButton) { + if(m_ui.displayOptions->isChecked()) { + m_ui.displayOptions->setFocus(); + } + else { + Results *res = qobject_cast(m_ui.resultTabWidget->currentWidget()); + if (!res) { + return; + } + res->tree->setFocus(); + } + *found = true; + return; + } + else { + if (currentWidget->objectName() == "tree") { + m_ui.displayOptions->setFocus(); + *found = true; + return; + } + } + } +} + +KatePluginSearchView::KatePluginSearchView(Kate::MainWindow *mainWin, Kate::Application* application) +: Kate::PluginView(mainWin), +Kate::XMLGUIClient(KatePluginSearchFactory::componentData()), +m_kateApp(application), +m_curResults(0), +m_searchJustOpened(false), +m_switchToProjectModeWhenAvailable(false), +m_searchDiskFilesDone(true), +m_searchOpenFilesDone(true), +m_projectPluginView(0) +{ + m_toolView = mainWin->createToolView ("kate_plugin_katesearch", + Kate::MainWindow::Bottom, + SmallIcon("edit-find"), + i18n("Search and Replace")); + + ContainerWidget *container = new ContainerWidget(m_toolView); + m_ui.setupUi(container); + container->setFocusProxy(m_ui.searchCombo); + connect(container, SIGNAL(nextFocus(QWidget*,bool*,bool)), this, SLOT(nextFocus(QWidget*,bool*,bool))); + + KAction *a = actionCollection()->addAction("search_in_files"); + a->setText(i18n("Search in Files")); + connect(a, SIGNAL(triggered(bool)), this, SLOT(openSearchView())); + + a = actionCollection()->addAction("search_in_files_new_tab"); + a->setText(i18n("Search in Files (in new tab)")); + // first add tab, then open search view, since open search view switches to show the search options + connect(a, SIGNAL(triggered(bool)), this, SLOT(addTab())); + connect(a, SIGNAL(triggered(bool)), this, SLOT(openSearchView())); + + a = actionCollection()->addAction("go_to_next_match"); + a->setText(i18n("Go to Next Match")); + connect(a, SIGNAL(triggered(bool)), this, SLOT(goToNextMatch())); + + a = actionCollection()->addAction("go_to_prev_match"); + a->setText(i18n("Go to Previous Match")); + connect(a, SIGNAL(triggered(bool)), this, SLOT(goToPreviousMatch())); + + m_ui.resultTabWidget->tabBar()->setSelectionBehaviorOnRemove(QTabBar::SelectLeftTab); + KAcceleratorManager::setNoAccel(m_ui.resultTabWidget); + + m_ui.displayOptions->setIcon(KIcon("arrow-down-double")); + m_ui.searchButton->setIcon(KIcon("edit-find")); + m_ui.stopButton->setIcon(KIcon("process-stop")); + m_ui.searchPlaceCombo->setItemIcon(0, KIcon("text-plain")); + m_ui.searchPlaceCombo->setItemIcon(1, KIcon("folder")); + m_ui.folderUpButton->setIcon(KIcon("go-up")); + m_ui.currentFolderButton->setIcon(KIcon("view-refresh")); + m_ui.newTabButton->setIcon(KIcon("tab-new")); + + m_ui.filterCombo->setToolTip(i18n("Comma separated list of file types to search in. Example: \"*.cpp,*.h\"\n")); + m_ui.excludeCombo->setToolTip(i18n("Comma separated list of files and directories to exclude from the search. Example: \"build*\"")); + + // the order here is important to get the tabBar hidden for only one tab + addTab(); + m_ui.resultTabWidget->tabBar()->hide(); + + // get url-requester's combo box and sanely initialize + KComboBox* cmbUrl = m_ui.folderRequester->comboBox(); + cmbUrl->setDuplicatesEnabled(false); + cmbUrl->setEditable(true); + m_ui.folderRequester->setMode(KFile::Directory | KFile::LocalOnly); + KUrlCompletion* cmpl = new KUrlCompletion(KUrlCompletion::DirCompletion); + cmbUrl->setCompletionObject(cmpl); + cmbUrl->setAutoDeleteCompletionObject(true); + + connect(m_ui.newTabButton, SIGNAL(clicked()), this, SLOT(addTab())); + connect(m_ui.resultTabWidget, SIGNAL(closeRequest(QWidget*)), this, SLOT(closeTab(QWidget*))); + connect(m_ui.resultTabWidget, SIGNAL(currentChanged(int)), this, SLOT(resultTabChanged(int))); + + connect(m_ui.folderUpButton, SIGNAL(clicked()), this, SLOT(navigateFolderUp())); + connect(m_ui.currentFolderButton, SIGNAL(clicked()), this, SLOT(setCurrentFolder())); + + connect(m_ui.searchCombo, SIGNAL(editTextChanged(QString)), &m_changeTimer, SLOT(start())); + connect(m_ui.matchCase, SIGNAL(stateChanged(int)), &m_changeTimer, SLOT(start())); + connect(m_ui.useRegExp, SIGNAL(stateChanged(int)), &m_changeTimer, SLOT(start())); + m_changeTimer.setInterval(300); + m_changeTimer.setSingleShot(true); + connect(&m_changeTimer, SIGNAL(timeout()), this, SLOT(startSearchWhileTyping())); + + connect(m_ui.searchCombo, SIGNAL(returnPressed()), this, SLOT(startSearch())); + connect(m_ui.folderRequester, SIGNAL(returnPressed()), this, SLOT(startSearch())); + connect(m_ui.filterCombo, SIGNAL(returnPressed()), this, SLOT(startSearch())); + connect(m_ui.excludeCombo, SIGNAL(returnPressed()), this, SLOT(startSearch())); + connect(m_ui.searchButton, SIGNAL(clicked()), this, SLOT(startSearch())); + + connect(m_ui.displayOptions, SIGNAL(toggled(bool)), this, SLOT(toggleOptions(bool))); + connect(m_ui.searchPlaceCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(searchPlaceChanged())); + + connect(m_ui.stopButton, SIGNAL(clicked()), &m_searchOpenFiles, SLOT(cancelSearch())); + connect(m_ui.stopButton, SIGNAL(clicked()), &m_searchDiskFiles, SLOT(cancelSearch())); + connect(m_ui.stopButton, SIGNAL(clicked()), &m_folderFilesList, SLOT(cancelSearch())); + connect(m_ui.stopButton, SIGNAL(clicked()), &m_replacer, SLOT(cancelReplace())); + + connect(m_ui.nextButton, SIGNAL(clicked()), this, SLOT(goToNextMatch())); + + connect(m_ui.replaceButton, SIGNAL(clicked(bool)), this, SLOT(replaceSingleMatch())); + connect(m_ui.replaceCheckedBtn, SIGNAL(clicked(bool)), this, SLOT(replaceChecked())); + connect(m_ui.replaceCombo, SIGNAL(returnPressed()), this, SLOT(replaceChecked())); + + + + m_ui.displayOptions->setChecked(true); + + connect(&m_searchOpenFiles, SIGNAL(matchFound(QString,QString,int,int,QString,int)), + this, SLOT(matchFound(QString,QString,int,int,QString,int))); + connect(&m_searchOpenFiles, SIGNAL(searchDone()), this, SLOT(searchDone())); + connect(&m_searchOpenFiles, SIGNAL(searching(QString)), this, SLOT(searching(QString))); + + connect(&m_folderFilesList, SIGNAL(finished()), this, SLOT(folderFileListChanged())); + + connect(&m_searchDiskFiles, SIGNAL(matchFound(QString,QString,int,int,QString,int)), + this, SLOT(matchFound(QString,QString,int,int,QString,int))); + connect(&m_searchDiskFiles, SIGNAL(searchDone()), this, SLOT(searchDone())); + connect(&m_searchDiskFiles, SIGNAL(searching(QString)), this, SLOT(searching(QString))); + + connect(m_kateApp->documentManager(), SIGNAL(documentWillBeDeleted(KTextEditor::Document*)), + &m_searchOpenFiles, SLOT(cancelSearch())); + + connect(m_kateApp->documentManager(), SIGNAL(documentWillBeDeleted(KTextEditor::Document*)), + &m_replacer, SLOT(cancelReplace())); + + connect(m_kateApp->documentManager(), SIGNAL(documentWillBeDeleted(KTextEditor::Document*)), + this, SLOT(clearDocMarks(KTextEditor::Document*))); + + connect(&m_replacer, SIGNAL(matchReplaced(KTextEditor::Document*,int,int,int)), + this, SLOT(addMatchMark(KTextEditor::Document*,int,int,int))); + + // Hook into line edit context menus + m_ui.searchCombo->setContextMenuPolicy(Qt::CustomContextMenu); + connect(m_ui.searchCombo, SIGNAL(customContextMenuRequested(QPoint)), this, + SLOT(searchContextMenu(QPoint))); + + connect(mainWindow(), SIGNAL(unhandledShortcutOverride(QEvent*)), + this, SLOT(handleEsc(QEvent*))); + + // watch for project plugin view creation/deletion + connect(mainWindow(), SIGNAL(pluginViewCreated (const QString &, Kate::PluginView *)) + , this, SLOT(slotPluginViewCreated (const QString &, Kate::PluginView *))); + + connect(mainWindow(), SIGNAL(pluginViewDeleted (const QString &, Kate::PluginView *)) + , this, SLOT(slotPluginViewDeleted (const QString &, Kate::PluginView *))); + + connect(mainWindow(), SIGNAL(viewChanged()), this, SLOT(docViewChanged())); + + + // update once project plugin state manually + m_projectPluginView = mainWindow()->pluginView ("kateprojectplugin"); + slotProjectFileNameChanged (); + + m_replacer.setDocumentManager(m_kateApp->documentManager()); + connect(&m_replacer, SIGNAL(replaceDone()), this, SLOT(replaceDone())); + + searchPlaceChanged(); + + m_toolView->installEventFilter(this); + + mainWindow()->guiFactory()->addClient(this); +} + +KatePluginSearchView::~KatePluginSearchView() +{ + clearMarks(); + + mainWindow()->guiFactory()->removeClient(this); + delete m_toolView; +} + +void KatePluginSearchView::navigateFolderUp() +{ + // navigate one folder up + m_ui.folderRequester->setUrl(m_ui.folderRequester->url().upUrl()); +} + +void KatePluginSearchView::setCurrentFolder() +{ + if (!mainWindow()) { + return; + } + KTextEditor::View* editView = mainWindow()->activeView(); + if (editView && editView->document()) { + // upUrl as we want the folder not the file + m_ui.folderRequester->setUrl(editView->document()->url().upUrl()); + } +} + + +QString KatePluginSearchView::currentWord(const KTextEditor::Document& document, const KTextEditor::Cursor& cursor ) const +{ + QString textLine = document.line(cursor.line()); + + int len = textLine.length(); + + if (cursor.column() > len) { // Probably because of non-wrapping cursor mode. + return QString(); + } + + int start = cursor.column(); + for(int currPos = cursor.column()-1; currPos >= 0; currPos--) { + if (textLine.at(currPos).isLetterOrNumber() || (textLine[currPos]=='_') || (textLine[currPos]=='~')) { + start = currPos; + } + else { + break; + } + } + + int end = cursor.column(); + while (end < len && (textLine.at(end).isLetterOrNumber() + || (textLine[end]=='_') || (textLine[end]=='~'))) { + end++; + } + + return textLine.mid(start, (end - start)); +} + +void KatePluginSearchView::openSearchView() +{ + if (!mainWindow()) { + return; + } + if (!m_toolView->isVisible()) { + mainWindow()->showToolView(m_toolView); + } + m_ui.searchCombo->setFocus(Qt::OtherFocusReason); + m_ui.displayOptions->setChecked(true); + + KTextEditor::View* editView = mainWindow()->activeView(); + if (editView && editView->document()) { + if (m_ui.folderRequester->text().isEmpty()) { + // upUrl as we want the folder not the file + m_ui.folderRequester->setUrl(editView->document()->url().upUrl()); + } + QString selection; + if (editView->selection()) { + selection = editView->selectionText(); + // remove possible trailing '\n' + if (selection.endsWith('\n')) { + selection = selection.left(selection.size() -1); + } + } + if (selection.isEmpty()) { + selection = currentWord(*editView->document(), editView->cursorPosition()); + } + + if (!selection.isEmpty() && !selection.contains('\n')) { + m_ui.searchCombo->blockSignals(true); + m_ui.searchCombo->lineEdit()->setText(selection); + m_ui.searchCombo->blockSignals(false); + } + + m_ui.searchCombo->lineEdit()->selectAll(); + m_searchJustOpened = true; + startSearchWhileTyping(); + } +} + +void KatePluginSearchView::handleEsc(QEvent *e) +{ + if (!mainWindow()) return; + + QKeyEvent *k = static_cast(e); + if (k->key() == Qt::Key_Escape && k->modifiers() == Qt::NoModifier) { + + if (m_toolView->isVisible()) { + mainWindow()->hideToolView(m_toolView); + } + else { + clearMarks(); + } + } +} + +void KatePluginSearchView::setSearchString(const QString &pattern) +{ + m_ui.searchCombo->lineEdit()->setText(pattern); +} + +void KatePluginSearchView::toggleOptions(bool show) +{ + m_ui.stackedWidget->setCurrentIndex((show) ? 1:0); +} + +void KatePluginSearchView::setSearchPlace(int place) +{ + m_ui.searchPlaceCombo->setCurrentIndex(place); +} + +QStringList KatePluginSearchView::filterFiles(const QStringList& files) const +{ + QString types = m_ui.filterCombo->currentText(); + QString excludes = m_ui.excludeCombo->currentText(); + if (((types.isEmpty() || types == "*")) && (excludes.isEmpty())) { + // shortcut for use all files + return files; + } + + QStringList tmpTypes = types.split(','); + QVector typeList; + for (int i=0; i excludeList; + for (int i=0; i openList; + for (int i=0; idocumentManager()->documents().size(); i++) { + int index = fileList.indexOf(m_kateApp->documentManager()->documents()[i]->url().pathOrUrl()); + if (index != -1) { + openList << m_kateApp->documentManager()->documents()[i]; + fileList.removeAt(index); + } + } + + // search order is important: Open files starts immediately and should finish + // earliest after first event loop. + // The DiskFile might finish immediately + if (openList.size() > 0) { + m_searchOpenFiles.startSearch(openList, m_curResults->regExp); + } + else { + m_searchOpenFilesDone = true; + } + + m_searchDiskFiles.startSearch(fileList, m_curResults->regExp); +} + + +void KatePluginSearchView::searchPlaceChanged() +{ + m_ui.displayOptions->setChecked(true); + + const bool inFolder = (m_ui.searchPlaceCombo->currentIndex() == 1); + const bool inProject = (m_ui.searchPlaceCombo->currentIndex() == 2); + + m_ui.filterCombo->setEnabled(inFolder || inProject); + + m_ui.excludeCombo->setEnabled(inFolder || inProject); + m_ui.folderRequester->setEnabled(inFolder); + m_ui.folderUpButton->setEnabled(inFolder); + m_ui.currentFolderButton->setEnabled(inFolder); + m_ui.recursiveCheckBox->setEnabled(inFolder); + m_ui.hiddenCheckBox->setEnabled(inFolder); + m_ui.symLinkCheckBox->setEnabled(inFolder); + m_ui.binaryCheckBox->setEnabled(inFolder); + + // ... and the labels: + m_ui.folderLabel->setEnabled(m_ui.folderRequester->isEnabled()); + m_ui.filterLabel->setEnabled(m_ui.filterCombo->isEnabled()); + m_ui.excludeLabel->setEnabled(m_ui.excludeCombo->isEnabled()); +} + +void KatePluginSearchView::addHeaderItem() +{ + QTreeWidgetItem *item = new QTreeWidgetItem(m_curResults->tree, QStringList()); + item->setCheckState(0, Qt::Checked); + item->setFlags(item->flags() | Qt::ItemIsTristate); + m_curResults->tree->expandItem(item); +} + +QTreeWidgetItem * KatePluginSearchView::rootFileItem(const QString &url, const QString &fName) +{ + if (!m_curResults) { + return 0; + } + + KUrl kurl(url); + QString path = kurl.isLocalFile() ? kurl.upUrl().path() : kurl.upUrl().url(); + path.replace(m_resultBaseDir, ""); + QString name = kurl.fileName(); + if (url.isEmpty()) { + name = fName; + } + + // make sure we have a root item + if (m_curResults->tree->topLevelItemCount() == 0) { + addHeaderItem(); + } + QTreeWidgetItem *root = m_curResults->tree->topLevelItem(0); + + if (root->data(0, ReplaceMatches::FileNameRole).toString() == fName) { + // The root item contains the document name -> + // this is search as you type, return the root item + return root; + } + + for (int i=0; ichildCount(); i++) { + kDebug() << root->child(i)->data(0, ReplaceMatches::FileNameRole).toString() << fName; + if ((root->child(i)->data(0, ReplaceMatches::FileUrlRole).toString() == url)&& + (root->child(i)->data(0, ReplaceMatches::FileNameRole).toString() == fName)) { + int matches = root->child(i)->data(0, ReplaceMatches::LineRole).toInt() + 1; + QString tmpUrl = QString("%1%2: %3").arg(path).arg(name).arg(matches); + root->child(i)->setData(0, Qt::DisplayRole, tmpUrl); + root->child(i)->setData(0, ReplaceMatches::LineRole, matches); + return root->child(i); + } + } + + // file item not found create a new one + QString tmpUrl = QString("%1%2: %3").arg(path).arg(name).arg(1); + + TreeWidgetItem *item = new TreeWidgetItem(root, QStringList(tmpUrl)); + item->setData(0, ReplaceMatches::FileUrlRole, url); + item->setData(0, ReplaceMatches::FileNameRole, fName); + item->setData(0, ReplaceMatches::LineRole, 1); + item->setCheckState(0, Qt::Checked); + item->setFlags(item->flags() | Qt::ItemIsTristate); + return item; +} + +void KatePluginSearchView::addMatchMark(KTextEditor::Document* doc, int line, int column, int matchLen) +{ + if (!doc) return; + + KTextEditor::MovingInterface* miface = qobject_cast(doc); + KTextEditor::ConfigInterface* ciface = qobject_cast(mainWindow()->activeView()); + KTextEditor::Attribute::Ptr attr(new KTextEditor::Attribute()); + + bool replace = ((sender() == &m_replacer) || (sender() == 0) || (sender() == m_ui.replaceButton)); + if (replace) { + QColor replaceColor(Qt::green); + if (ciface) replaceColor = ciface->configValue("replace-highlight-color").value(); + attr->setBackground(replaceColor); + } + else { + QColor searchColor(Qt::yellow); + if (ciface) searchColor = ciface->configValue("search-highlight-color").value(); + attr->setBackground(searchColor); + } + // calculate end line in case of multi-line match + int endLine = line; + int endColumn = column+matchLen; + while ((endLine < doc->lines()) && (endColumn > doc->line(endLine).size())) { + endColumn -= doc->line(endLine).size(); + endColumn--; // remove one for '\n' + endLine++; + } + + KTextEditor::Range range(line, column, endLine, endColumn); + + if (m_curResults && !replace) { + // special handling for "(?=\\n)" in multi-line search + QRegExp tmpReg = m_curResults->regExp; + if (m_curResults->regExp.pattern().endsWith("(?=\\n)")) { + QString newPatern = tmpReg.pattern(); + newPatern.replace("(?=\\n)", "$"); + tmpReg.setPattern(newPatern); + } + + if (tmpReg.indexIn(doc->text(range)) != 0) { + kDebug() << doc->text(range) << "Does not match" << m_curResults->regExp.pattern(); + return; + } + } + + KTextEditor::MovingRange* mr = miface->newMovingRange(range); + mr->setAttribute(attr); + mr->setZDepth(-90000.0); // Set the z-depth to slightly worse than the selection + mr->setAttributeOnlyForViews(true); + m_matchRanges.append(mr); + + KTextEditor::MarkInterface* iface = qobject_cast(doc); + if (!iface) return; + iface->setMarkDescription(KTextEditor::MarkInterface::markType32, i18n("SearchHighLight")); + iface->setMarkPixmap(KTextEditor::MarkInterface::markType32, + KIcon().pixmap(0,0)); + iface->addMark(line, KTextEditor::MarkInterface::markType32); + + connect(doc, SIGNAL(aboutToInvalidateMovingInterfaceContent(KTextEditor::Document*)), + this, SLOT(clearMarks()), Qt::UniqueConnection); +} + +void KatePluginSearchView::matchFound(const QString &url, const QString &fName, int line, int column, + const QString &lineContent, int matchLen) +{ + if (!m_curResults) { + return; + } + + QString pre = Qt::escape(lineContent.left(column)); + QString match = Qt::escape(lineContent.mid(column, matchLen)); + match.replace('\n', "\\n"); + QString post = Qt::escape(lineContent.mid(column + matchLen)); + QStringList row; + row << i18n("Line: %1: %2", line+1, pre+""+match+""+post); + + TreeWidgetItem *item = new TreeWidgetItem(rootFileItem(url, fName), row); + item->setData(0, ReplaceMatches::FileUrlRole, url); + item->setData(0, Qt::ToolTipRole, url); + item->setData(0, ReplaceMatches::FileNameRole, fName); + item->setData(0, ReplaceMatches::LineRole, line); + item->setData(0, ReplaceMatches::ColumnRole, column); + item->setData(0, ReplaceMatches::MatchLenRole, matchLen); + item->setData(0, ReplaceMatches::PreMatchRole, pre); + item->setData(0, ReplaceMatches::MatchRole, match); + item->setData(0, ReplaceMatches::PostMatchRole, post); + item->setCheckState (0, Qt::Checked); + + m_curResults->matches++; + + // Add mark if the document is open + KTextEditor::Document* doc; + if (url.isEmpty()) { + doc = m_replacer.findNamed(fName); + } + else { + doc = m_kateApp->documentManager()->findUrl(url); + } + addMatchMark(doc, line, column, matchLen); +} + +void KatePluginSearchView::clearMarks() +{ + // FIXME: check for ongoing search... + KTextEditor::MarkInterface* iface; + foreach (KTextEditor::Document* doc, m_kateApp->documentManager()->documents()) { + iface = qobject_cast(doc); + if (iface) { + const QHash marks = iface->marks(); + QHashIterator i(marks); + while (i.hasNext()) { + i.next(); + if (i.value()->type & KTextEditor::MarkInterface::markType32) { + iface->removeMark(i.value()->line, KTextEditor::MarkInterface::markType32); + } + } + } + } + qDeleteAll(m_matchRanges); + m_matchRanges.clear(); +} + +void KatePluginSearchView::clearDocMarks(KTextEditor::Document* doc) +{ + //kDebug() << sender(); + // FIXME: check for ongoing search... + KTextEditor::MarkInterface* iface; + iface = qobject_cast(doc); + if (iface) { + const QHash marks = iface->marks(); + QHashIterator i(marks); + while (i.hasNext()) { + i.next(); + if (i.value()->type & KTextEditor::MarkInterface::markType32) { + iface->removeMark(i.value()->line, KTextEditor::MarkInterface::markType32); + } + } + } + + int i = 0; + while (idocument() == doc) { + //kDebug() << "removing mark in" << doc->url(); + delete m_matchRanges.at(i); + m_matchRanges.removeAt(i); + } + else { + i++; + } + } +} + +void KatePluginSearchView::startSearch() +{ + m_changeTimer.stop(); // make sure not to start a "while you type" search now + mainWindow()->showToolView(m_toolView); // in case we are invoked from the command interface + m_switchToProjectModeWhenAvailable = false; // now that we started, don't switch back automatically + + if (m_ui.searchCombo->currentText().isEmpty()) { + // return pressed in the folder combo or filter combo + return; + } + m_ui.searchCombo->addToHistory(m_ui.searchCombo->currentText()); + if(m_ui.filterCombo->findText(m_ui.filterCombo->currentText()) == -1) { + m_ui.filterCombo->insertItem(0, m_ui.filterCombo->currentText()); + m_ui.filterCombo->setCurrentIndex(0); + } + if(m_ui.excludeCombo->findText(m_ui.excludeCombo->currentText()) == -1) { + m_ui.excludeCombo->insertItem(0, m_ui.excludeCombo->currentText()); + m_ui.excludeCombo->setCurrentIndex(0); + } + m_curResults = qobject_cast(m_ui.resultTabWidget->currentWidget()); + if (!m_curResults) { + kWarning() << "This is a bug"; + return; + } + + m_ui.newTabButton->setDisabled(true); + m_ui.searchCombo->setDisabled(true); + m_ui.searchButton->setDisabled(true); + m_ui.displayOptions->setChecked (false); + m_ui.displayOptions->setDisabled(true); + m_ui.replaceCheckedBtn->setDisabled(true); + m_ui.replaceButton->setDisabled(true); + m_ui.nextAndStop->setCurrentIndex(1); + m_ui.replaceCombo->setDisabled(true); + + + QRegExp reg(m_ui.searchCombo->currentText(), + m_ui.matchCase->isChecked() ? Qt::CaseSensitive : Qt::CaseInsensitive, + m_ui.useRegExp->isChecked() ? QRegExp::RegExp : QRegExp::FixedString); + m_curResults->regExp = reg; + + clearMarks(); + m_curResults->tree->clear(); + m_curResults->matches = 0; + + m_ui.resultTabWidget->setTabText(m_ui.resultTabWidget->currentIndex(), + m_ui.searchCombo->currentText()); + + m_toolView->setCursor(Qt::WaitCursor); + m_searchDiskFilesDone = false; + m_searchOpenFilesDone = false; + + if (m_ui.searchPlaceCombo->currentIndex() == 0) { + m_searchDiskFilesDone = true; + m_resultBaseDir.clear(); + const QList & documents = m_kateApp->documentManager()->documents(); + addHeaderItem(); + m_searchOpenFiles.startSearch(documents, reg); + } + else if (m_ui.searchPlaceCombo->currentIndex() == 1) { + m_resultBaseDir = m_ui.folderRequester->text(); + addHeaderItem(); + m_folderFilesList.generateList(m_ui.folderRequester->text(), + m_ui.recursiveCheckBox->isChecked(), + m_ui.hiddenCheckBox->isChecked(), + m_ui.symLinkCheckBox->isChecked(), + m_ui.binaryCheckBox->isChecked(), + m_ui.filterCombo->currentText(), + m_ui.excludeCombo->currentText()); + // the file list will be ready when the thread returns (connected to folderFileListChanged) + } + else { + /** + * init search with file list from current project, if any + */ + m_resultBaseDir.clear(); + QStringList files; + QString projectName; + if (m_projectPluginView) { + projectName = m_projectPluginView->property ("projectName").toString(); + m_resultBaseDir = m_projectPluginView->property ("projectBaseDir").toString(); + if (!m_resultBaseDir.endsWith('/')) + m_resultBaseDir += '/'; + QStringList projectFiles = m_projectPluginView->property ("projectFiles").toStringList(); + files = filterFiles(projectFiles); + } + addHeaderItem(); + + QList openList; + for (int i=0; idocumentManager()->documents().size(); i++) { + int index = files.indexOf(m_kateApp->documentManager()->documents()[i]->url().pathOrUrl()); + if (index != -1) { + openList << m_kateApp->documentManager()->documents()[i]; + files.removeAt(index); + } + } + // search order is important: Open files starts immediately and should finish + // earliest after first event loop. + // The DiskFile might finish immediately + if (openList.size() > 0) { + m_searchOpenFiles.startSearch(openList, m_curResults->regExp); + } + else { + m_searchOpenFilesDone = true; + } + m_searchDiskFiles.startSearch(files, reg); + } +} + +void KatePluginSearchView::startSearchWhileTyping() +{ + if (!m_searchDiskFilesDone || !m_searchOpenFilesDone) { + return; + } + + m_ui.searchButton->setDisabled(m_ui.searchCombo->currentText().isEmpty()); + + if (!mainWindow()->activeView()) return; + + KTextEditor::Document *doc = mainWindow()->activeView()->document(); + if (!doc) return; + + m_curResults =qobject_cast(m_ui.resultTabWidget->currentWidget()); + if (!m_curResults) { + kWarning() << "This is a bug"; + return; + } + + m_ui.replaceCheckedBtn->setDisabled(true); + m_ui.replaceButton->setDisabled(true); + m_ui.nextButton->setDisabled(true); + + QRegExp reg(m_ui.searchCombo->currentText(), + m_ui.matchCase->isChecked() ? Qt::CaseSensitive : Qt::CaseInsensitive, + m_ui.useRegExp->isChecked() ? QRegExp::RegExp : QRegExp::FixedString); + + m_curResults->regExp = reg; + + clearMarks(); + m_curResults->tree->clear(); + m_curResults->matches = 0; + + m_resultBaseDir.clear(); + + // add header item + TreeWidgetItem *item = new TreeWidgetItem(m_curResults->tree, QStringList()); + item->setData(0, ReplaceMatches::FileUrlRole, doc->url().pathOrUrl()); + item->setData(0, ReplaceMatches::FileNameRole, doc->documentName()); + item->setData(0, ReplaceMatches::LineRole, 0); + item->setCheckState(0, Qt::Checked); + item->setFlags(item->flags() | Qt::ItemIsTristate); + + if (m_ui.searchCombo->currentText().length() >= 2) { + m_searchOpenFiles.searchOpenFile(doc, reg, 0); + } + searchWhileTypingDone(); +} + + +void KatePluginSearchView::searchDone() +{ + m_changeTimer.stop(); // avoid "while you type" search directly after + + if (sender() == &m_searchDiskFiles) { + m_searchDiskFilesDone = true; + } + if (sender() == &m_searchOpenFiles) { + m_searchOpenFilesDone = true; + } + + if (!m_searchDiskFilesDone || !m_searchOpenFilesDone) { + return; + } + + m_ui.newTabButton->setDisabled(false); + m_ui.searchCombo->setDisabled(false); + m_ui.searchButton->setDisabled(false); + m_ui.nextAndStop->setCurrentIndex(0); + m_ui.displayOptions->setDisabled(false); + m_ui.replaceCombo->setDisabled(false); + + if (!m_curResults) { + return; + } + + m_ui.replaceCheckedBtn->setDisabled(m_curResults->matches < 1); + m_ui.replaceButton->setDisabled(m_curResults->matches < 1); + m_ui.nextButton->setDisabled(m_curResults->matches < 1); + + m_curResults->tree->sortItems(0, Qt::AscendingOrder); + + m_curResults->tree->expandAll(); + m_curResults->tree->resizeColumnToContents(0); + if (m_curResults->tree->columnWidth(0) < m_curResults->tree->width()-30) { + m_curResults->tree->setColumnWidth(0, m_curResults->tree->width()-30); + } + + // expand the "header item " to display all files and all results if configured + QTreeWidgetItem *root = m_curResults->tree->topLevelItem(0); + m_curResults->tree->expandItem(root); + if (root && (root->childCount() > 1) && (!m_ui.expandResults->isChecked())) { + for (int i=0; ichildCount(); i++) { + m_curResults->tree->collapseItem(root->child(i)); + } + } + + m_curResults->tree->setCurrentItem(root); + m_curResults->tree->setFocus(Qt::OtherFocusReason); + + if (root) { + switch (m_ui.searchPlaceCombo->currentIndex()) + { + case 0: + root->setData(0, Qt::DisplayRole, i18np("One match found in open files", + "%1 matches found in open files", + m_curResults->matches)); + break; + case 1: + root->setData(0, Qt::DisplayRole, i18np("One match found in folder %2", + "%1 matches found in folder %2", + m_curResults->matches, + m_resultBaseDir)); + break; + case 2: + QString projectName; + if (m_projectPluginView) { + projectName = m_projectPluginView->property("projectName").toString(); + } + root->setData(0, Qt::DisplayRole, i18np("One match found in project %2 (%3)", + "%1 matches found in project %2 (%3)", + m_curResults->matches, + projectName, + m_resultBaseDir)); + break; + } + } + + indicateMatch(m_curResults->matches > 0); + m_curResults = 0; + m_toolView->unsetCursor(); + m_searchJustOpened = false; +} + +void KatePluginSearchView::searchWhileTypingDone() +{ + if (!m_curResults) { + return; + } + + m_ui.replaceCheckedBtn->setDisabled(m_curResults->matches < 1); + m_ui.replaceButton->setDisabled(m_curResults->matches < 1); + m_ui.nextButton->setDisabled(m_curResults->matches < 1); + + m_curResults->tree->expandAll(); + m_curResults->tree->resizeColumnToContents(0); + if (m_curResults->tree->columnWidth(0) < m_curResults->tree->width()-30) { + m_curResults->tree->setColumnWidth(0, m_curResults->tree->width()-30); + } + + QTreeWidgetItem *root = m_curResults->tree->topLevelItem(0); + if (root) { + QTreeWidgetItem *child = root->child(0); + if (!m_searchJustOpened) { + itemSelected(child); + } + indicateMatch(child); + + root->setData(0, Qt::DisplayRole, i18np("One match found", + "%1 matches found", + m_curResults->matches)); + } + m_curResults = 0; + m_ui.searchCombo->lineEdit()->setFocus(); + m_searchJustOpened = false; +} + + +void KatePluginSearchView::searching(const QString &file) +{ + if (!m_curResults) { + return; + } + + QTreeWidgetItem *root = m_curResults->tree->topLevelItem(0); + if (root) { + if (file.size() > 70) { + root->setData(0, Qt::DisplayRole, i18n("Searching: ...%1", file.right(70))); + } + else { + root->setData(0, Qt::DisplayRole, i18n("Searching: %1", file)); + } + } +} + +void KatePluginSearchView::indicateMatch(bool hasMatch) { + QLineEdit * const lineEdit = m_ui.searchCombo->lineEdit(); + QPalette background(lineEdit->palette()); + + if (hasMatch) { + // Green background for line edit + KColorScheme::adjustBackground(background, KColorScheme::PositiveBackground); + } + else { + // Reset background of line edit + background = QPalette(); + } + // Red background for line edit + //KColorScheme::adjustBackground(background, KColorScheme::NegativeBackground); + // Neutral background + //KColorScheme::adjustBackground(background, KColorScheme::NeutralBackground); + + lineEdit->setPalette(background); +} + +void KatePluginSearchView::replaceSingleMatch() +{ + // check if the cursor is at the current item if not jump there + Results *res = qobject_cast(m_ui.resultTabWidget->currentWidget()); + if (!res) { + return; + } + QTreeWidgetItem *item = res->tree->currentItem(); + if (!item || !item->parent()) { + // nothing was selected + goToNextMatch(); + return; + } + + if (!mainWindow()->activeView() || !mainWindow()->activeView()->cursorPosition().isValid()) { + itemSelected(item); + return; + } + + int dLine = mainWindow()->activeView()->cursorPosition().line(); + int dColumn = mainWindow()->activeView()->cursorPosition().column(); + + int iLine = item->data(0, ReplaceMatches::LineRole).toInt(); + int iColumn = item->data(0, ReplaceMatches::ColumnRole).toInt(); + + if ((dLine != iLine) || (dColumn != iColumn)) { + itemSelected(item); + return; + } + + KTextEditor::Document *doc = mainWindow()->activeView()->document(); + // Find the corresponding range + int i; + for (i=0; idocument() != doc) continue; + if (m_matchRanges[i]->start().line() != iLine) continue; + if (m_matchRanges[i]->start().column() != iColumn) continue; + break; + } + + if (i >=m_matchRanges.size()) { + goToNextMatch(); + return; + } + + if (!res->regExp.exactMatch(doc->text(m_matchRanges[i]->toRange()))) { + kDebug() << doc->text(m_matchRanges[i]->toRange()) << "Does not match" << res->regExp.pattern(); + goToNextMatch(); + return; + } + + if (m_ui.replaceCombo->findText(m_ui.replaceCombo->currentText()) == -1) { + m_ui.replaceCombo->insertItem(0, m_ui.replaceCombo->currentText()); + m_ui.replaceCombo->setCurrentIndex(0); + } + + + QString replaceText = m_ui.replaceCombo->currentText(); + replaceText.replace("\\\\", "¤Search&Replace¤"); + for (int j=1; j<=res->regExp.captureCount(); j++) { + replaceText.replace(QString("\\%1").arg(j), res->regExp.cap(j)); + } + replaceText.replace("\\n", "\n"); + replaceText.replace("¤Search&Replace¤", "\\\\"); + + doc->replaceText(m_matchRanges[i]->toRange(), replaceText); + addMatchMark(doc, dLine, dColumn, replaceText.size()); + + replaceText.replace('\n', "\\n"); + QString html = item->data(0, ReplaceMatches::PreMatchRole).toString(); + html += "" + item->data(0, ReplaceMatches::MatchRole).toString() + " "; + html += "" + replaceText + ""; + html += item->data(0, ReplaceMatches::PostMatchRole).toString(); + item->setData(0, Qt::DisplayRole, i18n("Line: %1: %2",m_matchRanges[i]->start().line()+1, html)); + + // now update the rest of the tree items for this file (they are sorted in ascending order + i++; + for (; idocument() != doc) continue; + item = res->tree->itemBelow(item); + if (!item) break; + if (item->data(0, ReplaceMatches::FileUrlRole).toString() != doc->url().pathOrUrl()) break; + iLine = item->data(0, ReplaceMatches::LineRole).toInt(); + iColumn = item->data(0, ReplaceMatches::ColumnRole).toInt(); + if ((m_matchRanges[i]->start().line() == iLine) && (m_matchRanges[i]->start().column() == iColumn)) { + break; + } + item->setData(0, ReplaceMatches::LineRole, m_matchRanges[i]->start().line()); + item->setData(0, ReplaceMatches::ColumnRole, m_matchRanges[i]->start().column()); + } + goToNextMatch(); +} + +void KatePluginSearchView::replaceChecked() +{ + m_curResults =qobject_cast(m_ui.resultTabWidget->currentWidget()); + if (!m_curResults) { + kWarning() << "Results not found"; + return; + } + + if (m_ui.replaceCombo->findText(m_ui.replaceCombo->currentText()) == -1) { + m_ui.replaceCombo->insertItem(0, m_ui.replaceCombo->currentText()); + m_ui.replaceCombo->setCurrentIndex(0); + } + + m_ui.nextAndStop->setCurrentIndex(1); + m_ui.displayOptions->setChecked(false); + + m_curResults->replace = m_ui.replaceCombo->currentText(); + + m_replacer.replaceChecked(m_curResults->tree, + m_curResults->regExp, + m_curResults->replace); +} + +void KatePluginSearchView::replaceDone() +{ + m_ui.nextAndStop->setCurrentIndex(0); + m_ui.replaceCombo->setDisabled(false); +} + +void KatePluginSearchView::docViewChanged() +{ + Results *res = qobject_cast(m_ui.resultTabWidget->currentWidget()); + if (!res) { + return; + } + + m_curResults = res; + + if (!mainWindow()->activeView()) { + return; + } + + // add the marks if it is not already open + KTextEditor::Document *doc = mainWindow()->activeView()->document(); + if (doc) { + QTreeWidgetItem *rootItem = 0; + for (int i=0; itree->topLevelItemCount(); i++) { + QString url = res->tree->topLevelItem(i)->data(0, ReplaceMatches::FileUrlRole).toString(); + QString fName = res->tree->topLevelItem(i)->data(0, ReplaceMatches::FileNameRole).toString(); + if (url == doc->url().pathOrUrl() && fName == doc->documentName()) { + rootItem = res->tree->topLevelItem(i); + break; + } + } + if (rootItem) { + + int line; + int column; + int len; + QTreeWidgetItem *item; + for (int i=0; ichildCount(); i++) { + item = rootItem->child(i); + line = item->data(0, ReplaceMatches::LineRole).toInt(); + column = item->data(0, ReplaceMatches::ColumnRole).toInt(); + len = item->data(0, ReplaceMatches::MatchLenRole).toInt(); + addMatchMark(doc, line, column, len); + } + } + } +} + +void KatePluginSearchView::itemSelected(QTreeWidgetItem *item) +{ + if (!item) return; + + m_curResults = qobject_cast(m_ui.resultTabWidget->currentWidget()); + if (!m_curResults) { + return; + } + + while (item->data(0, ReplaceMatches::ColumnRole).toString().isEmpty()) { + item->treeWidget()->expandItem(item); + item = item->child(0); + if (!item) return; + } + item->treeWidget()->setCurrentItem(item); + + // get stuff + int toLine = item->data(0, ReplaceMatches::LineRole).toInt(); + int toColumn = item->data(0, ReplaceMatches::ColumnRole).toInt(); + + KTextEditor::Document* doc; + QString url = item->data(0, ReplaceMatches::FileUrlRole).toString(); + if (!url.isEmpty()) { + doc = m_kateApp->documentManager()->findUrl(url); + } + else { + doc = m_replacer.findNamed(item->data(0, ReplaceMatches::FileNameRole).toString()); + } + + // add the marks to the document if it is not already open + if (!doc) { + doc = m_kateApp->documentManager()->openUrl(url); + if (doc) { + int line; + int column; + int len; + QTreeWidgetItem *rootItem = (item->parent()==0) ? item : item->parent(); + for (int i=0; ichildCount(); i++) { + item = rootItem->child(i); + line = item->data(0, ReplaceMatches::LineRole).toInt(); + column = item->data(0, ReplaceMatches::ColumnRole).toInt(); + len = item->data(0, ReplaceMatches::MatchLenRole).toInt(); + addMatchMark(doc, line, column, len); + } + } + } + if (!doc) return; + + // open the right view... + mainWindow()->activateView(doc); + + // any view active? + if (!mainWindow()->activeView()) { + return; + } + + + // set the cursor to the correct position + mainWindow()->activeView()->setCursorPosition(KTextEditor::Cursor(toLine, toColumn)); + mainWindow()->activeView()->setFocus(); +} + +void KatePluginSearchView::goToNextMatch() +{ + Results *res = qobject_cast(m_ui.resultTabWidget->currentWidget()); + if (!res) { + return; + } + QTreeWidgetItem *curr = res->tree->currentItem(); + if (!curr) { + curr = res->tree->topLevelItem(0); + } + if (!curr) return; + + if (!curr->data(0, ReplaceMatches::ColumnRole).toString().isEmpty()) { + curr = res->tree->itemBelow(curr); + if (!curr) { + curr = res->tree->topLevelItem(0); + } + } + + itemSelected(curr); +} + +void KatePluginSearchView::goToPreviousMatch() +{ + Results *res = qobject_cast(m_ui.resultTabWidget->currentWidget()); + if (!res) { + return; + } + if (res->tree->topLevelItemCount() == 0) { + return; + } + QTreeWidgetItem *curr = res->tree->currentItem(); + + // go to the item above. (curr == null is not a problem) + curr = res->tree->itemAbove(curr); + + // skip file name items and the root item + while (curr && curr->data(0, ReplaceMatches::ColumnRole).toString().isEmpty()) { + curr = res->tree->itemAbove(curr); + } + + if (!curr) { + // select the last child of the last next-to-top-level item + QTreeWidgetItem *root = res->tree->topLevelItem(0); + + // select the last "root item" + if (!root || (root->childCount() < 1)) return; + root = root->child(root->childCount()-1); + + // select the last match of the "root item" + if (!root || (root->childCount() < 1)) return; + curr = root->child(root->childCount()-1); + } + + itemSelected(curr); +} + +void KatePluginSearchView::readSessionConfig(KConfigBase* config, const QString& groupPrefix) +{ + KConfigGroup cg(config, groupPrefix + ":search-plugin"); + m_ui.searchCombo->clearHistory(); + m_ui.searchCombo->setHistoryItems(cg.readEntry("Search", QStringList()), true); + m_ui.matchCase->setChecked(cg.readEntry("MatchCase", false)); + m_ui.useRegExp->setChecked(cg.readEntry("UseRegExp", false)); + m_ui.expandResults->setChecked(cg.readEntry("ExpandSearchResults", false)); + + int searchPlaceIndex = cg.readEntry("Place", 1); + if (searchPlaceIndex < 0) { + searchPlaceIndex = 1; // for the case we happen to read -1 as Place + } + if ((searchPlaceIndex == 2) && (searchPlaceIndex >= m_ui.searchPlaceCombo->count())) { + // handle the case that project mode was selected, butnot yet available + m_switchToProjectModeWhenAvailable = true; + searchPlaceIndex = 1; + } + m_ui.searchPlaceCombo->setCurrentIndex(searchPlaceIndex); + + m_ui.recursiveCheckBox->setChecked(cg.readEntry("Recursive", true)); + m_ui.hiddenCheckBox->setChecked(cg.readEntry("HiddenFiles", false)); + m_ui.symLinkCheckBox->setChecked(cg.readEntry("FollowSymLink", false)); + m_ui.binaryCheckBox->setChecked(cg.readEntry("BinaryFiles", false)); + m_ui.folderRequester->comboBox()->clear(); + m_ui.folderRequester->comboBox()->addItems(cg.readEntry("SearchDiskFiless", QStringList())); + m_ui.folderRequester->setText(cg.readEntry("SearchDiskFiles", QString())); + m_ui.filterCombo->clear(); + m_ui.filterCombo->addItems(cg.readEntry("Filters", QStringList())); + m_ui.filterCombo->setCurrentIndex(cg.readEntry("CurrentFilter", 0)); + m_ui.excludeCombo->clear(); + m_ui.excludeCombo->addItems(cg.readEntry("ExcludeFilters", QStringList())); + m_ui.excludeCombo->setCurrentIndex(cg.readEntry("CurrentExcludeFilter", 0)); +} + +void KatePluginSearchView::writeSessionConfig(KConfigBase* config, const QString& groupPrefix) +{ + KConfigGroup cg(config, groupPrefix + ":search-plugin"); + cg.writeEntry("Search", m_ui.searchCombo->historyItems()); + cg.writeEntry("MatchCase", m_ui.matchCase->isChecked()); + cg.writeEntry("UseRegExp", m_ui.useRegExp->isChecked()); + cg.writeEntry("ExpandSearchResults", m_ui.expandResults->isChecked()); + + cg.writeEntry("Place", m_ui.searchPlaceCombo->currentIndex()); + cg.writeEntry("Recursive", m_ui.recursiveCheckBox->isChecked()); + cg.writeEntry("HiddenFiles", m_ui.hiddenCheckBox->isChecked()); + cg.writeEntry("FollowSymLink", m_ui.symLinkCheckBox->isChecked()); + cg.writeEntry("BinaryFiles", m_ui.binaryCheckBox->isChecked()); + QStringList folders; + for (int i=0; icomboBox()->count(), 10); i++) { + folders << m_ui.folderRequester->comboBox()->itemText(i); + } + cg.writeEntry("SearchDiskFiless", folders); + cg.writeEntry("SearchDiskFiles", m_ui.folderRequester->text()); + QStringList filterItems; + for (int i=0; icount(), 10); i++) { + filterItems << m_ui.filterCombo->itemText(i); + } + cg.writeEntry("Filters", filterItems); + cg.writeEntry("CurrentFilter", m_ui.filterCombo->currentIndex()); + + QStringList excludeFilterItems; + for (int i=0; icount(), 10); i++) { + excludeFilterItems << m_ui.excludeCombo->itemText(i); + } + cg.writeEntry("ExcludeFilters", excludeFilterItems); + cg.writeEntry("CurrentExcludeFilter", m_ui.excludeCombo->currentIndex()); +} + +void KatePluginSearchView::addTab() +{ + if ((sender() != m_ui.newTabButton) && + (m_ui.resultTabWidget->count() > 0) && + m_ui.resultTabWidget->tabText(m_ui.resultTabWidget->currentIndex()).isEmpty()) + { + return; + } + + Results *res = new Results(); + + res->tree->setRootIsDecorated(false); + + connect(res->tree, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)), + this, SLOT (itemSelected(QTreeWidgetItem*)), Qt::QueuedConnection); + + m_ui.resultTabWidget->addTab(res, ""); + m_ui.resultTabWidget->setCurrentIndex(m_ui.resultTabWidget->count()-1); + m_ui.stackedWidget->setCurrentIndex(0); + m_ui.resultTabWidget->tabBar()->show(); + m_ui.displayOptions->setChecked(false); + + res->tree->installEventFilter(this); +} + +void KatePluginSearchView::closeTab(QWidget *widget) +{ + Results *tmp = qobject_cast(widget); + if (m_curResults == tmp) { + m_searchOpenFiles.cancelSearch(); + m_searchDiskFiles.cancelSearch(); + } + if (m_ui.resultTabWidget->count() > 1) { + delete tmp; // remove the tab + m_curResults = 0; + } + if (m_ui.resultTabWidget->count() == 1) { + m_ui.resultTabWidget->tabBar()->hide(); + } +} + +void KatePluginSearchView::resultTabChanged(int index) +{ + if (index < 0) { + return; + } + // empty tab -> nothing to set. + if (m_ui.resultTabWidget->tabText(index).isEmpty()) { + return; + } + + Results *res = qobject_cast(m_ui.resultTabWidget->widget(index)); + if (!res) { + return; + } + // check if the text has been modified + int i; + for (i=0; icount(); i++) { + if ((m_ui.resultTabWidget->tabText(i) == m_ui.searchCombo->currentText()) && + !m_ui.resultTabWidget->tabText(i).isEmpty()) + { + break; + } + } + if (i == m_ui.resultTabWidget->count()) { + // the text does not match a tab -> do not update the search + return; + } + + m_ui.searchCombo->blockSignals(true); + m_ui.matchCase->blockSignals(true); + m_ui.useRegExp->blockSignals(true); + m_ui.searchCombo->lineEdit()->setText(m_ui.resultTabWidget->tabText(index)); + m_ui.matchCase->setChecked(res->regExp.caseSensitivity() == Qt::CaseSensitive); + m_ui.useRegExp->setChecked(res->regExp.patternSyntax() != QRegExp::FixedString); + m_ui.searchCombo->blockSignals(false); + m_ui.matchCase->blockSignals(false); + m_ui.useRegExp->blockSignals(false); +} + + +bool KatePluginSearchView::eventFilter(QObject *obj, QEvent *event) +{ + if (event->type() == QEvent::KeyPress) { + QKeyEvent *ke = static_cast(event); + QTreeWidget *tree = qobject_cast(obj); + if (tree) { + if (ke->matches(QKeySequence::Copy)) { + // user pressed ctrl+c -> copy full URL to the clipboard + QVariant variant = tree->currentItem()->data(0, ReplaceMatches::FileUrlRole); + QApplication::clipboard()->setText(variant.toString()); + event->accept(); + return true; + } + if (ke->key() == Qt::Key_Enter || ke->key() == Qt::Key_Return) { + if (tree->currentItem()) { + itemSelected(tree->currentItem()); + event->accept(); + return true; + } + } + } + if ((obj == m_toolView) && (ke->key() == Qt::Key_Escape)) { + mainWindow()->hideToolView(m_toolView); + event->accept(); + return true; + } + } + return QObject::eventFilter(obj, event); +} + +void KatePluginSearchView::searchContextMenu(const QPoint& pos) +{ + QSet actionPointers; + + QMenu* const contextMenu = m_ui.searchCombo->lineEdit()->createStandardContextMenu(); + if (!contextMenu) return; + + if (m_ui.useRegExp->isChecked()) { + QMenu* menu = contextMenu->addMenu(i18n("Add...")); + if (!menu) return; + + menu->setIcon(KIcon("list-add")); + + actionPointers << menuEntry(menu, "^", "", i18n("Beginning of line")); + actionPointers << menuEntry(menu, "$", "", i18n("End of line")); + menu->addSeparator(); + actionPointers << menuEntry(menu, ".", "", i18n("Any single character (excluding line breaks)")); + menu->addSeparator(); + actionPointers << menuEntry(menu, "+", "", i18n("One or more occurrences")); + actionPointers << menuEntry(menu, "*", "", i18n("Zero or more occurrences")); + actionPointers << menuEntry(menu, "?", "", i18n("Zero or one occurrences")); + actionPointers << menuEntry(menu, "{", ",}", i18n(" through occurrences"), "{a", ",b}"); + menu->addSeparator(); + actionPointers << menuEntry(menu, "(", ")", i18n("Group, capturing")); + actionPointers << menuEntry(menu, "|", "", i18n("Or")); + actionPointers << menuEntry(menu, "[", "]", i18n("Set of characters")); + actionPointers << menuEntry(menu, "[^", "]", i18n("Negative set of characters")); + actionPointers << menuEntry(menu, "(?:", ")", i18n("Group, non-capturing"), "(?:E"); + actionPointers << menuEntry(menu, "(?=", ")", i18n("Lookahead"), "(?=E"); + actionPointers << menuEntry(menu, "(?!", ")", i18n("Negative lookahead"), "(?!E"); + + menu->addSeparator(); + actionPointers << menuEntry(menu, "\\n", "", i18n("Line break")); + actionPointers << menuEntry(menu, "\\t", "", i18n("Tab")); + actionPointers << menuEntry(menu, "\\b", "", i18n("Word boundary")); + actionPointers << menuEntry(menu, "\\B", "", i18n("Not word boundary")); + actionPointers << menuEntry(menu, "\\d", "", i18n("Digit")); + actionPointers << menuEntry(menu, "\\D", "", i18n("Non-digit")); + actionPointers << menuEntry(menu, "\\s", "", i18n("Whitespace (excluding line breaks)")); + actionPointers << menuEntry(menu, "\\S", "", i18n("Non-whitespace (excluding line breaks)")); + actionPointers << menuEntry(menu, "\\w", "", i18n("Word character (alphanumerics plus '_')")); + actionPointers << menuEntry(menu, "\\W", "", i18n("Non-word character")); + } + // Show menu + QAction * const result = contextMenu->exec(m_ui.searchCombo->mapToGlobal(pos)); + + // Act on action + if (result && actionPointers.contains(result)) { + QLineEdit * lineEdit = m_ui.searchCombo->lineEdit(); + const int cursorPos = lineEdit->cursorPosition(); + QStringList beforeAfter = result->data().toString().split(' '); + if (beforeAfter.size() != 2) return; + lineEdit->insert(beforeAfter[0] + beforeAfter[1]); + lineEdit->setCursorPosition(cursorPos + beforeAfter[0].count()); + lineEdit->setFocus(); + } +} + +void KatePluginSearchView::slotPluginViewCreated (const QString &name, Kate::PluginView *pluginView) +{ + // add view + if (name == "kateprojectplugin") { + m_projectPluginView = pluginView; + slotProjectFileNameChanged (); + connect (pluginView, SIGNAL(projectFileNameChanged()), this, SLOT(slotProjectFileNameChanged())); + } +} + +void KatePluginSearchView::slotPluginViewDeleted (const QString &name, Kate::PluginView *) +{ + // remove view + if (name == "kateprojectplugin") { + m_projectPluginView = 0; + slotProjectFileNameChanged (); + } +} + +void KatePluginSearchView::slotProjectFileNameChanged () +{ + // query new project file name + QString projectFileName; + if (m_projectPluginView) + projectFileName = m_projectPluginView->property("projectFileName").toString(); + + // have project, enable gui for it + if (!projectFileName.isEmpty()) { + if (m_ui.searchPlaceCombo->count() < 3) { + // add "in Project" + m_ui.searchPlaceCombo->addItem (SmallIcon("project-open"), i18n("in Project")); + if (m_switchToProjectModeWhenAvailable) { + // switch to search "in Project" + m_switchToProjectModeWhenAvailable = false; + setSearchPlace (2); + } + } + } + + // else: disable gui for it + else { + if (m_ui.searchPlaceCombo->count() > 2) { + // switch to search "in Open files", if "in Project" is active + if (m_ui.searchPlaceCombo->currentIndex () == 2) + setSearchPlace (0); + + // remove "in Project" + m_ui.searchPlaceCombo->removeItem (2); + } + } +} + +KateSearchCommand::KateSearchCommand(QObject *parent) +: QObject(parent), KTextEditor::Command() +{ +} + +const QStringList& KateSearchCommand::cmds() +{ + static QStringList sl = QStringList() << "grep" << "newGrep" + << "search" << "newSearch" + << "pgrep" << "newPGrep"; + return sl; +} + +bool KateSearchCommand::exec (KTextEditor::View* /*view*/, const QString& cmd, QString& /*msg*/) +{ + //create a list of args + QStringList args(cmd.split(' ', QString::KeepEmptyParts)); + QString command = args.takeFirst(); + QString searchText = args.join(QString(' ')); + + if (command == "grep" || command == "newGrep") { + emit setSearchPlace(1); + emit setCurrentFolder(); + if (command == "newGrep") + emit newTab(); + } + + else if (command == "search" || command == "newSearch") { + emit setSearchPlace(0); + if (command == "newSearch") + emit newTab(); + } + + else if (command == "pgrep" || command == "newPGrep") { + emit setSearchPlace(2); + if (command == "newPGrep") + emit newTab(); + } + + emit setSearchString(searchText); + emit startSearch(); + + return true; +} + +bool KateSearchCommand::help (KTextEditor::View */*view*/, const QString &cmd, QString & msg) +{ + if (cmd.startsWith("grep")) { + msg = i18n("Usage: grep [pattern to search for in folder]"); + } + else if (cmd.startsWith("newGrep")) { + msg = i18n("Usage: newGrep [pattern to search for in folder]"); + } + + else if (cmd.startsWith("search")) { + msg = i18n("Usage: search [pattern to search for in open files]"); + } + else if (cmd.startsWith("newSearch")) { + msg = i18n("Usage: search [pattern to search for in open files]"); + } + + else if (cmd.startsWith("pgrep")) { + msg = i18n("Usage: pgrep [pattern to search for in current project]"); + } + else if (cmd.startsWith("newPGrep")) { + msg = i18n("Usage: newPGrep [pattern to search for in current project]"); + } + + return true; +} + +// kate: space-indent on; indent-width 4; replace-tabs on; diff --git a/kate/addons/kate/search/plugin_search.h b/kate/addons/kate/search/plugin_search.h new file mode 100644 index 00000000..bb73b8dc --- /dev/null +++ b/kate/addons/kate/search/plugin_search.h @@ -0,0 +1,211 @@ +/* Kate search plugin + * + * Copyright (C) 2011-2013 by Kåre Särs + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program in a file called COPYING; if not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#ifndef _PLUGIN_SEARCH_H_ +#define _PLUGIN_SEARCH_H_ + +#include +#include +#include +#include + +#include +#include + +#include "ui_search.h" +#include "ui_results.h" + +#include "search_open_files.h" +#include "SearchDiskFiles.h" +#include "FolderFilesList.h" +#include "replace_matches.h" + +class KateSearchCommand; +namespace KTextEditor{ + class MovingRange; +} + +class Results: public QWidget, public Ui::Results +{ + Q_OBJECT +public: + Results(QWidget *parent = 0); + int matches; + QRegExp regExp; + QString replace; +}; + +// This class keeps the focus inside the S&R plugin when pressing tab/shift+tab by overriding focusNextPrevChild() +class ContainerWidget:public QWidget +{ + Q_OBJECT +public: + ContainerWidget(QWidget *parent): QWidget(parent) {} + +Q_SIGNALS: + void nextFocus(QWidget *currentWidget, bool *found, bool next); + +protected: + virtual bool focusNextPrevChild (bool next); +}; + + +class KatePluginSearch : public Kate::Plugin +{ + Q_OBJECT + +public: + explicit KatePluginSearch(QObject* parent = 0, const QList& = QList()); + virtual ~KatePluginSearch(); + + Kate::PluginView *createView(Kate::MainWindow *mainWindow); + +private: + KateSearchCommand* m_searchCommand; +}; + + + +class KatePluginSearchView : public Kate::PluginView, public Kate::XMLGUIClient +{ + Q_OBJECT + +public: + KatePluginSearchView(Kate::MainWindow *mainWindow, Kate::Application* application); + ~KatePluginSearchView(); + + virtual void readSessionConfig(KConfigBase* config, const QString& groupPrefix); + virtual void writeSessionConfig(KConfigBase* config, const QString& groupPrefix); + +public Q_SLOTS: + void startSearch(); + void setSearchString(const QString &pattern); + void navigateFolderUp(); + void setCurrentFolder(); + void setSearchPlace(int place); + void goToNextMatch(); + void goToPreviousMatch(); + +private Q_SLOTS: + void openSearchView(); + void handleEsc(QEvent *e); + void nextFocus(QWidget *currentWidget, bool *found, bool next); + + void addTab(); + void closeTab(QWidget *widget); + void toggleOptions(bool show); + + void searchContextMenu(const QPoint& pos); + + void searchPlaceChanged(); + void startSearchWhileTyping(); + + void folderFileListChanged(); + + void matchFound(const QString &url, const QString &fileName, int line, int column, + const QString &lineContent, int matchLen); + + void addMatchMark(KTextEditor::Document* doc, int line, int column, int len); + + void searchDone(); + void searchWhileTypingDone(); + void indicateMatch(bool hasMatch); + + void searching(const QString &file); + + void itemSelected(QTreeWidgetItem *item); + + void clearMarks(); + void clearDocMarks(KTextEditor::Document* doc); + + void replaceSingleMatch(); + void replaceChecked(); + + void replaceDone(); + + void docViewChanged(); + + + void resultTabChanged(int index); + + /** + * keep track if the project plugin is alive and if the project file did change + */ + void slotPluginViewCreated (const QString &name, Kate::PluginView *pluginView); + void slotPluginViewDeleted (const QString &name, Kate::PluginView *pluginView); + void slotProjectFileNameChanged (); + +protected: + bool eventFilter(QObject *obj, QEvent *ev); + void addHeaderItem(); + +private: + QTreeWidgetItem *rootFileItem(const QString &url, const QString &fName); + QStringList filterFiles(const QStringList& files) const; + QString currentWord(const KTextEditor::Document& document, const KTextEditor::Cursor& cursor) const; + + Ui::SearchDialog m_ui; + QWidget *m_toolView; + Kate::Application *m_kateApp; + SearchOpenFiles m_searchOpenFiles; + FolderFilesList m_folderFilesList; + SearchDiskFiles m_searchDiskFiles; + ReplaceMatches m_replacer; + Results *m_curResults; + bool m_searchJustOpened; + bool m_switchToProjectModeWhenAvailable; + bool m_searchDiskFilesDone; + bool m_searchOpenFilesDone; + QString m_resultBaseDir; + QList m_matchRanges; + QTimer m_changeTimer; + + /** + * current project plugin view, if any + */ + Kate::PluginView *m_projectPluginView; +}; + +class KateSearchCommand : public QObject, public KTextEditor::Command +{ + Q_OBJECT +public: + KateSearchCommand(QObject *parent); + +Q_SIGNALS: + void setSearchPlace(int place); + void setCurrentFolder(); + void setSearchString(const QString &pattern); + void startSearch(); + void newTab(); + + // + // KTextEditor::Command + // +public: + const QStringList &cmds (); + bool exec (KTextEditor::View *view, const QString &cmd, QString &msg); + bool help (KTextEditor::View *view, const QString &cmd, QString &msg); +}; + +#endif + +// kate: space-indent on; indent-width 4; replace-tabs on; + diff --git a/kate/addons/kate/search/replace_matches.cpp b/kate/addons/kate/search/replace_matches.cpp new file mode 100644 index 00000000..a1662937 --- /dev/null +++ b/kate/addons/kate/search/replace_matches.cpp @@ -0,0 +1,191 @@ +/* Kate search plugin + * + * Copyright (C) 2011-2013 by Kåre Särs + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program in a file called COPYING; if not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#include "replace_matches.h" +#include "moc_replace_matches.cpp" +#include +#include +#include +#include + +ReplaceMatches::ReplaceMatches(QObject *parent) : QObject(parent), +m_manager(0), +m_tree(0), +m_rootIndex(-1) +{ + connect(this, SIGNAL(replaceNextMatch()), this, SLOT(doReplaceNextMatch()), Qt::QueuedConnection); +} + +void ReplaceMatches::replaceChecked(QTreeWidget *tree, const QRegExp ®exp, const QString &replace) +{ + if (m_manager == 0) return; + if (m_rootIndex != -1) return; + + m_tree = tree; + m_rootIndex = 0; + m_regExp = regexp; + m_replaceText = replace; + m_cancelReplace = false; + emit replaceNextMatch(); +} +void ReplaceMatches::setDocumentManager(Kate::DocumentManager *manager) +{ + m_manager = manager; +} + +void ReplaceMatches::cancelReplace() +{ + m_cancelReplace = true; +} + +KTextEditor::Document *ReplaceMatches::findNamed(const QString &name) +{ + QList docs = m_manager->documents(); + + foreach (KTextEditor::Document* it, docs) { + if ( it->documentName() == name) { + return it; + } + } + return 0; +} + + +void ReplaceMatches::doReplaceNextMatch() +{ + if ((!m_manager) || (m_cancelReplace) || (m_tree->topLevelItemCount() != 1)) { + m_rootIndex = -1; + emit replaceDone(); + return; + } + + // NOTE The document managers signal documentWillBeDeleted() must be connected to + // cancelReplace(). A closed file could lead to a crash if it is not handled. + + // Open the file + QTreeWidgetItem *rootItem = m_tree->topLevelItem(0)->child(m_rootIndex); + if (!rootItem) { + m_rootIndex = -1; + emit replaceDone(); + return; + } + + if (!rootItem->data(0, ColumnRole).toString().isEmpty()) { + // this is a search as you type replace + rootItem = m_tree->topLevelItem(0); + m_cancelReplace = true; // only one document... + } + + if (rootItem->checkState(0) == Qt::Unchecked) { + m_rootIndex++; + emit replaceNextMatch(); + return; + } + + KTextEditor::Document *doc; + QString docUrl = rootItem->data(0, FileUrlRole).toString(); + QString docName = rootItem->data(0, FileNameRole).toString(); + if (docUrl.isEmpty()) { + doc = findNamed(rootItem->data(0, FileNameRole).toString()); + } + else { + doc = m_manager->findUrl(docUrl); + if (!doc) { + doc = m_manager->openUrl(rootItem->data(0, FileUrlRole).toString()); + } + } + + if (!doc) { + m_rootIndex++; + emit replaceNextMatch(); + return; + } + + QVector rVector; + QStringList rTexts; + KTextEditor::MovingInterface* miface = qobject_cast(doc); + int line; + int column; + int matchLen; + int endLine; + int endColumn; + QTreeWidgetItem *item; + QString matchLines; + + // lines might be modified so search the document again + for (int i=0; ichildCount(); i++) { + item = rootItem->child(i); + if (item->checkState(0) == Qt::Unchecked) continue; + + line = endLine= item->data(0, LineRole).toInt(); + column = item->data(0, ColumnRole).toInt(); + matchLen = item->data(0, MatchLenRole).toInt(); + matchLines = doc->line(line).mid(column); + while (matchLines.size() < matchLen) { + if (endLine+1 >= doc->lines()) break; + endLine++; + matchLines+= '\n' + doc->line(endLine); + } + + if (m_regExp.indexIn(matchLines) != 0) { + kDebug() << "expression does not match"; + continue; + } + + QString replaceText = m_replaceText; + replaceText.replace("\\\\", "¤Search&Replace¤"); + for (int j=1; j<=m_regExp.captureCount(); j++) { + replaceText.replace(QString("\\%1").arg(j), m_regExp.cap(j)); + } + replaceText.replace("\\n", "\n"); + replaceText.replace("¤Search&Replace¤", "\\\\"); + rTexts << replaceText; + + replaceText.replace('\n', "\\n"); + QString html = item->data(0, PreMatchRole).toString(); + html += "" + item->data(0, MatchRole).toString() + " "; + html += "" + replaceText + ""; + html += item->data(0, PostMatchRole).toString(); + item->setData(0, Qt::DisplayRole, i18n("Line: %1: %2",line+1, html)); + + endLine = line; + endColumn = column+matchLen; + while ((endLine < doc->lines()) && (endColumn > doc->line(endLine).size())) { + endColumn -= doc->line(endLine).size(); + endColumn--; // remove one for '\n' + endLine++; + } + KTextEditor::Range range(line, column, endLine, endColumn); + KTextEditor::MovingRange* mr = miface->newMovingRange(range); + rVector.append(mr); + } + + for (int i=0; istart().line(); + column = rVector[i]->start().column(); + doc->replaceText(*rVector[i], rTexts[i]); + emit matchReplaced(doc, line, column, rTexts[i].length()); + } + + qDeleteAll(rVector); + + m_rootIndex++; + emit replaceNextMatch(); +} diff --git a/kate/addons/kate/search/replace_matches.h b/kate/addons/kate/search/replace_matches.h new file mode 100644 index 00000000..c015e538 --- /dev/null +++ b/kate/addons/kate/search/replace_matches.h @@ -0,0 +1,74 @@ +/* Kate search plugin + * + * Copyright (C) 2011 by Kåre Särs + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program in a file called COPYING; if not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#ifndef _REPLACE_MATCHES_H_ +#define _REPLACE_MATCHES_H_ + +#include +#include +#include +#include +#include + +class ReplaceMatches: public QObject +{ + Q_OBJECT + +public: + enum MatchData { + FileUrlRole = Qt::UserRole, + FileNameRole, + LineRole, + ColumnRole, + MatchLenRole, + PreMatchRole, + MatchRole, + PostMatchRole + }; + + ReplaceMatches(QObject *parent = 0); + void setDocumentManager(Kate::DocumentManager *manager); + + void replaceChecked(QTreeWidget *tree, const QRegExp ®exp, const QString &replace); + + KTextEditor::Document *findNamed(const QString &name); + +public Q_SLOTS: + void cancelReplace(); + +private Q_SLOTS: + void doReplaceNextMatch(); + +Q_SIGNALS: + void replaceNextMatch(); + void matchReplaced(KTextEditor::Document* doc, int line, int column, int matchLen); + void replaceDone(); + +private: + Kate::DocumentManager *m_manager; + QTreeWidget *m_tree; + int m_rootIndex; + QRegExp m_regExp; + QString m_replaceText; + bool m_cancelReplace; +}; + + +#endif diff --git a/kate/addons/kate/search/results.ui b/kate/addons/kate/search/results.ui new file mode 100644 index 00000000..a97d7f2e --- /dev/null +++ b/kate/addons/kate/search/results.ui @@ -0,0 +1,42 @@ + + + Results + + + + 0 + 0 + 381 + 110 + + + + + 0 + + + + + true + + + true + + + true + + + false + + + + 1 + + + + + + + + + diff --git a/kate/addons/kate/search/search.ui b/kate/addons/kate/search/search.ui new file mode 100644 index 00000000..b5e6e2f9 --- /dev/null +++ b/kate/addons/kate/search/search.ui @@ -0,0 +1,479 @@ + + + SearchDialog + + + + 0 + 0 + 594 + 206 + + + + + 0 + + + + + ... + + + true + + + + + + + false + + + Replace + + + + + + + false + + + Search + + + + + + + ... + + + + + + + Find + + + searchCombo + + + + + + + Replace + + + replaceCombo + + + + + + + + 0 + 0 + + + + 1 + + + + + 0 + + + + + true + + + true + + + true + + + + + + + + + 0 + + + + + Qt::Vertical + + + + 1 + 1 + + + + + + + + + 0 + 0 + + + + + 0 + + + + + Regular e&xpressions + + + + + + + Qt::Horizontal + + + + + + + + + KFile::Directory|KFile::ExistingOnly|KFile::LocalOnly + + + + + + + Go one folder up. + + + + + + + Use the current document's path. + + + + + + + + + true + + + QComboBox::InsertAtTop + + + + * + + + + + + + + Filter + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + filterCombo + + + + + + + Expand results + + + + + + + Recursive + + + true + + + + + + + true + + + + + + + F&older + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + folderRequester + + + + + + + Exclude + + + excludeCombo + + + + + + + &Match case + + + + + + + Include binary files + + + + + + + Include hidden + + + + + + + Follow symbolic links + + + + + + + + + Qt::Horizontal + + + + 1 + 1 + + + + + + + + Se&arch: + + + searchPlaceCombo + + + + + + + + in Open files + + + + + in Folder + + + + + + + + + + + + + + + + + + 0 + 0 + + + + 0 + + + + + 0 + 0 + + + + + 0 + + + + + false + + + Next + + + + + + + + + 0 + 0 + + + + + 0 + + + + + + 0 + 0 + + + + Stop + + + + + + + + + + + true + + + true + + + + + + + true + + + true + + + + + + + false + + + Replace checked + + + + + + + + KComboBox + QComboBox +
kcombobox.h
+
+ + KUrlComboRequester + KUrlRequester +
kurlrequester.h
+
+ + KPushButton + QPushButton +
kpushbutton.h
+
+ + KUrlRequester + QFrame +
kurlrequester.h
+
+ + KTabWidget + QTabWidget +
ktabwidget.h
+ 1 +
+ + KHistoryComboBox + KComboBox +
khistorycombobox.h
+
+
+ + newTabButton + searchCombo + replaceCombo + searchButton + nextButton + stopButton + replaceButton + replaceCheckedBtn + matchCase + useRegExp + expandResults + searchPlaceCombo + folderRequester + folderUpButton + currentFolderButton + filterCombo + excludeCombo + recursiveCheckBox + hiddenCheckBox + symLinkCheckBox + binaryCheckBox + displayOptions + resultTabWidget + + + +
diff --git a/kate/addons/kate/search/search_open_files.cpp b/kate/addons/kate/search/search_open_files.cpp new file mode 100644 index 00000000..3f0a6e32 --- /dev/null +++ b/kate/addons/kate/search/search_open_files.cpp @@ -0,0 +1,182 @@ +/* Kate search plugin + * + * Copyright (C) 2011-2013 by Kåre Särs + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program in a file called COPYING; if not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#include "search_open_files.h" +#include "moc_search_open_files.cpp" + +#include + +SearchOpenFiles::SearchOpenFiles(QObject *parent) : QObject(parent), m_nextIndex(-1), m_cancelSearch(true) +{ + connect(this, SIGNAL(searchNextFile(int)), this, SLOT(doSearchNextFile(int)), Qt::QueuedConnection); +} + +bool SearchOpenFiles::searching() { return !m_cancelSearch; } + +void SearchOpenFiles::startSearch(const QList &list, const QRegExp ®exp) +{ + if (m_nextIndex != -1) return; + + m_docList = list; + m_nextIndex = 0; + m_regExp = regexp; + m_cancelSearch = false; + m_statusTime.restart(); + emit searchNextFile(0); +} + +void SearchOpenFiles::cancelSearch() +{ + m_cancelSearch = true; +} + +void SearchOpenFiles::doSearchNextFile(int startLine) +{ + if (m_cancelSearch) { + m_nextIndex = -1; + m_cancelSearch = true; + emit searchDone(); + return; + } + + // NOTE The document managers signal documentWillBeDeleted() must be connected to + // cancelSearch(). A closed file could lead to a crash if it is not handled. + int line = searchOpenFile(m_docList[m_nextIndex], m_regExp, startLine); + if (line == 0) { + // file searched go to next + m_nextIndex++; + if (m_nextIndex == m_docList.size()) { + m_nextIndex = -1; + m_cancelSearch = true; + emit searchDone(); + } + else { + emit searchNextFile(0); + } + } + else { + emit searchNextFile(line); + } +} + +int SearchOpenFiles::searchOpenFile(KTextEditor::Document *doc, const QRegExp ®Exp, int startLine) +{ + if (m_statusTime.elapsed() > 100) { + m_statusTime.restart(); + emit searching(doc->url().pathOrUrl()); + } + + if (regExp.pattern().contains("\\n")) { + return searchMultiLineRegExp(doc, regExp, startLine); + } + + return searchSingleLineRegExp(doc, regExp, startLine); +} + +int SearchOpenFiles::searchSingleLineRegExp(KTextEditor::Document *doc, const QRegExp ®Exp, int startLine) +{ + int column; + QTime time; + + time.start(); + for (int line = startLine; line < doc->lines(); line++) { + if (time.elapsed() > 100) { + kDebug() << "Search time exceeded" << time.elapsed() << line; + return line; + } + column = regExp.indexIn(doc->line(line)); + while (column != -1) { + if (regExp.cap().isEmpty()) break; + emit matchFound(doc->url().pathOrUrl(), doc->documentName(), line, column, + doc->line(line), regExp.matchedLength()); + column = regExp.indexIn(doc->line(line), column + regExp.cap().size()); + } + } + return 0; +} + +int SearchOpenFiles::searchMultiLineRegExp(KTextEditor::Document *doc, const QRegExp ®Exp, int startLine) +{ + int column = 0; + int line = 0; + QTime time; + time.start(); + QRegExp tmpRegExp = regExp; + + if (startLine == 0) { + // Copy the whole file to a temporary buffer to be able to search newlines + m_fullDoc.clear(); + m_lineStart.clear(); + m_lineStart << 0; + for (int i=0; ilines(); i++) { + m_fullDoc += doc->line(i) + '\n'; + m_lineStart << m_fullDoc.size(); + } + if (!regExp.pattern().endsWith("$")) { + // if regExp ends with '$' leave the extra newline at the end as + // '$' will be replaced with (?=\\n), which needs the extra newline + m_fullDoc.remove(m_fullDoc.size()-1, 1); + } + } + else { + if (startLine>0 && startLine column) { + line = i-1; + break; + } + } + if (line == -1) { + break; + } + emit matchFound(doc->url().pathOrUrl(), doc->documentName(), + line, + (column - m_lineStart[line]), + doc->line(line).left(column - m_lineStart[line])+tmpRegExp.cap(), + tmpRegExp.matchedLength()); + column = tmpRegExp.indexIn(m_fullDoc, column + tmpRegExp.matchedLength()); + + if (time.elapsed() > 100) { + //kDebug() << "Search time exceeded" << time.elapsed() << line; + return line; + } + } + return 0; +} diff --git a/kate/addons/kate/search/search_open_files.h b/kate/addons/kate/search/search_open_files.h new file mode 100644 index 00000000..5b574220 --- /dev/null +++ b/kate/addons/kate/search/search_open_files.h @@ -0,0 +1,69 @@ +/* Kate search plugin + * + * Copyright (C) 2011-2013 by Kåre Särs + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program in a file called COPYING; if not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#ifndef _SEARCH_OPEN_FILES_H_ +#define _SEARCH_OPEN_FILES_H_ + +#include +#include +#include +#include + +class SearchOpenFiles: public QObject +{ + Q_OBJECT + +public: + SearchOpenFiles(QObject *parent = 0); + + void startSearch(const QList &list,const QRegExp ®exp); + bool searching(); + +public Q_SLOTS: + void cancelSearch(); + + /// return 0 on success or a line number where we stopped. + int searchOpenFile(KTextEditor::Document *doc, const QRegExp ®Exp, int startLine); + +private Q_SLOTS: + void doSearchNextFile(int startLine); + +private: + int searchSingleLineRegExp(KTextEditor::Document *doc, const QRegExp ®Exp, int startLine); + int searchMultiLineRegExp(KTextEditor::Document *doc, const QRegExp ®Exp, int startLine); + +Q_SIGNALS: + void searchNextFile(int startLine); + void matchFound(const QString &url, const QString &fileName, int line, int column, const QString &lineContent, int matchLen); + void searchDone(); + void searching(const QString &file); + +private: + QList m_docList; + int m_nextIndex; + QRegExp m_regExp; + bool m_cancelSearch; + QString m_fullDoc; + QVector m_lineStart; + QTime m_statusTime; +}; + + +#endif diff --git a/kate/addons/kate/search/ui.rc b/kate/addons/kate/search/ui.rc new file mode 100644 index 00000000..9da19994 --- /dev/null +++ b/kate/addons/kate/search/ui.rc @@ -0,0 +1,11 @@ + + + + + &Edit + + + + + + diff --git a/kate/addons/kate/tabbarextension/CMakeLists.txt b/kate/addons/kate/tabbarextension/CMakeLists.txt new file mode 100644 index 00000000..1313d9ac --- /dev/null +++ b/kate/addons/kate/tabbarextension/CMakeLists.txt @@ -0,0 +1,33 @@ +########### next target ############### + +set(katetabbarextensionplugin_PART_SRCS + plugin_katetabbarextension.cpp + ktinytabbutton.cpp + ktinytabbar.cpp + ktinytabbarconfigdialog.cpp + ktinytabbarconfigpage.cpp +) + +kde4_add_plugin(katetabbarextensionplugin ${katetabbarextensionplugin_PART_SRCS}) + +target_link_libraries(katetabbarextensionplugin + ${KDE4_KDEUI_LIBS} + ${KDE4_KPARTS_LIBS} + kateinterfaces +) + +install( + TARGETS katetabbarextensionplugin + DESTINATION ${KDE4_PLUGIN_INSTALL_DIR} +) + +########### install files ############### + +install( + FILES ui.rc + DESTINATION ${KDE4_DATA_INSTALL_DIR}/kate/plugins/katetabbarextension +) +install( + FILES katetabbarextension.desktop + DESTINATION ${KDE4_SERVICES_INSTALL_DIR} +) diff --git a/kate/addons/kate/tabbarextension/Messages.sh b/kate/addons/kate/tabbarextension/Messages.sh new file mode 100644 index 00000000..2dde84fd --- /dev/null +++ b/kate/addons/kate/tabbarextension/Messages.sh @@ -0,0 +1,3 @@ +#! /bin/sh +$EXTRACTRC *.rc *.ui >> rc.cpp +$XGETTEXT *.cpp *.h -o $podir/katetabbarextension.pot diff --git a/kate/addons/kate/tabbarextension/katetabbarextension.desktop b/kate/addons/kate/tabbarextension/katetabbarextension.desktop new file mode 100644 index 00000000..893a9633 --- /dev/null +++ b/kate/addons/kate/tabbarextension/katetabbarextension.desktop @@ -0,0 +1,111 @@ +[Desktop Entry] +Type=Service +ServiceTypes=Kate/Plugin +X-KDE-Library=katetabbarextensionplugin +X-Kate-Version=2.8 +Name=Multiline Tab Bar +Name[ar]=شريط ألسنة متعدّد الأسطر +Name[bg]=Лента с подпрозорци на няколко реда +Name[bs]=Višelinijska traka kartica +Name[ca]=Barra de pestanyes multilínia +Name[ca@valencia]=Barra de pestanyes multilínia +Name[cs]=Víceřádkový pruh s kartami +Name[da]=Fanebladslinje med flere linjer +Name[de]=Mehrzeilige Unterfensterleiste +Name[el]=Καρτέλες πολλαπλών γραμμών +Name[en_GB]=Multiline Tab Bar +Name[es]=Barra de pestañas multilínea +Name[et]=Mitmerealine kaardiriba +Name[eu]=Lerro anitzeko fitxa-barra +Name[fi]=Monirivinen välilehtipalkki +Name[fr]=Barre d'onglets multi-ligne +Name[ga]=Barra Cluaisíní Il-Líne +Name[gl]=Barra de lapelas multiliña +Name[he]=כרטיסיות בעלות כמה שורות +Name[hu]=Többsoros lapozósáv +Name[ia]=Barra de scheda multi-rango +Name[it]=Barra di schede multilinea +Name[kk]=Көпжолды қойынды +Name[km]=របារ​ផ្ទាំង​​​ពហុ​បន្ទាត់ +Name[ko]=여러 줄 탭 표시줄 +Name[lt]=Kelių eilučių kortelių juosta +Name[lv]=Vairāku rindu ciļņu josla +Name[mr]=बहुलरेषा टॅब पट्टी +Name[nb]=Fanelinje med flere rader +Name[nds]=Mehrregen-Paneelbalken +Name[nl]=Meerregelige tabbladbalk +Name[pa]=ਬਹੁ-ਲਾਈਨ ਟੈਬ ਪੱਟੀ +Name[pl]=Wieloliniowy pasek kart +Name[pt]=Barra de Páginas Multi-Linha +Name[pt_BR]=Barra de abas multilinha +Name[ro]=Bară de file multilinie +Name[ru]=Многострочная панель вкладок +Name[si]=බහුපේලි තීරුව +Name[sk]=Viacriadkový panel kariet +Name[sl]=Vrstica z zavihki v več vrsticah +Name[sr]=Вишередна трака језичака +Name[sr@ijekavian]=Вишередна трака језичака +Name[sr@ijekavianlatin]=Višeredna traka jezičaka +Name[sr@latin]=Višeredna traka jezičaka +Name[sv]=Flerraders flikrad +Name[tg]=Панели бисёрсатра +Name[tr]=Çok Satırlı Sekme Çubuğu +Name[ug]=كۆپ قۇرلۇق بەتكۈچ بالداق +Name[uk]=Багаторядкова панель вкладок +Name[x-test]=xxMultiline Tab Barxx +Name[zh_CN]=多行标签栏 +Name[zh_TW]=多行分頁列 +Comment=Adds a tab bar with multiple rows to Kate's main window +Comment[ar]=يضيف شريط ألسنة بصفوف متعدّدة إلى نافذة كيت الرئيسية +Comment[bg]=Добавяне на лента с подпрозорци на няколко реда към основния прозорец на Kate +Comment[bs]=Dodaje traku sa karticama u glavni prozor Kate +Comment[ca]=Afegeix una barra de pestanyes amb diverses files a la finestra principal del Kate +Comment[ca@valencia]=Afig una barra de pestanyes amb diverses files a la finestra principal del Kate +Comment[cs]=Přidá do hlavního okna Kate pruh karet s více řádky +Comment[da]=Føjer en fanebladslinje med flere rækker til Kates hovedvindue +Comment[de]=Fügt dem Hauptfenster von Kate eine mehrzeilige Unterfensterleiste hinzu +Comment[el]=Προσθέτει καρτέλες πολλαπλών γραμμών στο κύριο παράθυρο του Kate +Comment[en_GB]=Adds a tab bar with multiple rows to Kate's main window +Comment[es]=Añade una barra de pestañas con varias filas a la ventana principal de Kate +Comment[et]=Lisab Kate peaaknasse mitmerealise kaardiriba +Comment[eu]=Hainbat errenkada dituen fitxa-barra gehitzen dio Kate-ren leiho nagusiari +Comment[fi]=Lisää monirivisen välilehtipalkin Katen pääikkunaan +Comment[fr]=Ajoute une barre d'onglets comportant plusieurs lignes à la fenêtre principale de Kate +Comment[ga]=Cuir barra cluaisíní ina bhfuil níos mó ná ró amháin le príomhfhuinneog Kate +Comment[gl]=Engade unha barra de lapelas en varias liñas á xanela principal de Kate +Comment[he]=מוסיף שורת לשוניות עם כמה שורות של לשוניות אל החלון הראשי של Kate +Comment[hu]=Többsoros lapozósáv hozzáadása a Kate főablakához +Comment[ia]=Adde un barra con multiple rangos a fenestra principal de Kate +Comment[it]=Aggiunge una barra di schede su più righe alla finestra principale di Kate +Comment[kk]=Kate-тің негізгі терезесіне көп бағанды қойындысын қосу +Comment[km]=បន្ថែម​របារ​​ផ្ទាំង​ដែល​មាន​​ពហុ​បន្ទាត់​​​ទៅ​បង្អួច​មេ​របស់ Kate​ +Comment[ko]=Kate의 주 창에 여러 줄로 된 탭 표시줄 추가 +Comment[lt]=Įdeda kelių juostų kortelių juostą į Kate langą +Comment[lv]=Pievieno ciļņu joslu ar vairākām rindām Kate galvenajam logam +Comment[mr]=केटच्या मुख्य चौकटीत बहुलरेषेची टॅब पट्टी जोडतो +Comment[nb]=Legger til en fanelinje med flere rader i Kates hovedvindu +Comment[nds]=Föögt Kate sien Hööftfinster en Paneelbalken mit mehr Regen to +Comment[nl]=Voegt een tabbladbalk met meerder regels toe aan Kate's hoofdvenster +Comment[nn]=Legg ei fanelinje med fleire rader til hovudvindauget i Kate +Comment[pa]=ਕੇਟ ਦੀ ਮੇਨ ਵਿੰਡੋ ਵਿੱਚ ਕਈ ਕਤਾਰਾਂ ਵਾਲੀ ਇੱਕ ਟੈਬ ਬਾਰ ਸ਼ਾਮਲ ਕਰੋ +Comment[pl]=Dodaje pasek kart o wielu wierszach do głównego okna Kate +Comment[pt]=Adiciona uma barra de páginas com várias linhas à janela principal do Kate +Comment[pt_BR]=Adiciona uma barra de abas com várias linhas à janela principal do Kate +Comment[ro]=Adaugă o bară de file cu mai multe rînduri la fereastra principală Kate +Comment[ru]=Добавляет панель вкладок с несколькими строками в окно Kate +Comment[si]=Kate හි ප්‍රධාන කවුළුවට පේළි කිහිපයක් සහිත ටැබ් තීරුවක් එක් කරයි +Comment[sk]=Pridá panel kariet s viacerými riadkami na hlavné okno Kate +Comment[sl]=Doda vrstico z zavihki v več vrsticah v glavno okno urejevalnika Kate +Comment[sr]=Додаје траку језичака са више редова у главни прозор Кејт +Comment[sr@ijekavian]=Додаје траку језичака са више редова у главни прозор Кејт +Comment[sr@ijekavianlatin]=Dodaje traku jezičaka sa više redova u glavni prozor Kate +Comment[sr@latin]=Dodaje traku jezičaka sa više redova u glavni prozor Kate +Comment[sv]=Lägger till en flikrad med flera rader i Kates huvudfönster +Comment[tg]=Панели бисёрқаторро ба тирезаи асосии барномаи Kate илова мекунад +Comment[tr]=Kate uygulamasının ana penceresine çok satırlı bir sekme çubuğu ekler +Comment[ug]=كۆپ قۇرلۇق بەتكۈچ بالداقتىن بىرنى Kate نىڭ ئاساسىي كۆزنىكىگە قوشىدۇ +Comment[uk]=Додає панель вкладок з декількома рядками до головного вікна Kate +Comment[x-test]=xxAdds a tab bar with multiple rows to Kate's main windowxx +Comment[zh_CN]=在 Kate 主窗口上添加一个多行标签栏 +Comment[zh_TW]=在 Kate 主視窗中加入多行分頁列 +author=Dominik Haumann, dhdev@gmx.de diff --git a/kate/addons/kate/tabbarextension/ktinytabbar.cpp b/kate/addons/kate/tabbarextension/ktinytabbar.cpp new file mode 100644 index 00000000..3f40249f --- /dev/null +++ b/kate/addons/kate/tabbarextension/ktinytabbar.cpp @@ -0,0 +1,1233 @@ +/*************************************************************************** + ktinytabbar.cpp + ------------------- + begin : 2005-06-15 + copyright : (C) 2005 by Dominik Haumann + email : dhdev@gmx.de + + Copyright (C) 2007 Flavio Castelli + ***************************************************************************/ + +/*************************************************************************** + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#include "ktinytabbar.h" +#include "moc_ktinytabbar.cpp" +#include "ktinytabbutton.h" +#include "ktinytabbarconfigpage.h" +#include "ktinytabbarconfigdialog.h" + +#include +#include +#include +#include +#include + +#include // QApplication::sendEvent +#include // qSort + +KTinyTabBar::SortType global_sortType; + +// public operator < for two tab buttons +bool tabLessThan( const KTinyTabButton* a, const KTinyTabButton* b ) +{ + switch( global_sortType ) + { + case KTinyTabBar::OpeningOrder: { + return a->buttonID() < b->buttonID(); + } + + case KTinyTabBar::Name: { + // fall back to ID + if( a->text().toLower() == b->text().toLower() ) + return a->buttonID() < b->buttonID(); + + return KStringHandler::naturalCompare(a->text(), b->text(), Qt::CaseInsensitive) < 0; + } + + case KTinyTabBar::URL: { + // fall back, if infos not available + if( a->url().isEmpty() && b->url().isEmpty() ) + { + if( a->text().toLower() == b->text().toLower() ) + return a->buttonID() < b->buttonID(); + + return KStringHandler::naturalCompare(a->text(), b->text(), Qt::CaseInsensitive) < 0; + } + + return KStringHandler::naturalCompare(a->url(), b->url(), Qt::CaseInsensitive) < 0; + } + + case KTinyTabBar::Extension: + { + // sort by extension, but check whether the files have an + // extension first + const int apos = a->text().lastIndexOf( '.' ); + const int bpos = b->text().lastIndexOf( '.' ); + + if( apos == -1 && bpos == -1 ) + return a->text().toLower() < b->text().toLower(); + else if( apos == -1 ) + return true; + else if( bpos == -1 ) + return false; + else + { + const int aright = a->text().size() - apos; + const int bright = b->text().size() - bpos; + QString aExt = a->text().right( aright ).toLower(); + QString bExt = b->text().right( bright ).toLower(); + QString aFile = a->text().left( apos ).toLower(); + QString bFile = b->text().left( bpos ).toLower(); + + if( aExt == bExt ) + return (aFile == bFile) ? a->buttonID() < b->buttonID() + : aFile < bFile; + else + return aExt < bExt; + } + } + } + + return true; +} + +//BEGIN public member functions +/** + * Creates a new tab bar with the given \a parent and \a name. + * + * The default values are in detail: + * - minimum tab width: 150 pixel + * - maximum tab width: 200 pixel + * - fixed tab height : 22 pixel. Note that the icon's size is 16 pixel. + * - number of rows: 1 row. + * . + */ +KTinyTabBar::KTinyTabBar( QWidget *parent ) + : QWidget( parent ) +{ + m_minimumTabWidth = 150; + m_maximumTabWidth = 200; + + m_tabHeight = 22; + + m_locationTop = true; + m_numRows = 1; + m_currentRow = 0; + m_followCurrentTab = true; + m_highlightModifiedTabs = false; + m_highlightPreviousTab = false; + m_highlightActiveTab = false; + m_highlightOpacity = 20; + + m_tabButtonStyle = Push; + m_sortType = OpeningOrder; + m_nextID = 0; + + m_activeButton = 0L; + m_previousButton = 0L; + + // default colors + m_colorModifiedTab = QColor( Qt::red ); + m_colorActiveTab = QColor( 150, 150, 255 ); + m_colorPreviousTab = QColor( 150, 150, 255 ); + + // functions called in ::load() will set settings for the nav buttons + m_upButton = new KTinyTabButton( QString(), QString(), -1, true, this ); + m_downButton = new KTinyTabButton( QString(), QString(), -2, true, this ); + m_configureButton = new KTinyTabButton( QString(), QString(), -3, true, this ); + m_navigateSize = 20; + + m_upButton->setIcon( KIconLoader::global()->loadIcon( "arrow-up", KIconLoader::Small, 16 ) ); + m_downButton->setIcon( KIconLoader::global()->loadIcon( "arrow-down", KIconLoader::Small, 16 ) ); + m_configureButton->setIcon( KIconLoader::global()->loadIcon( "configure", KIconLoader::Small, 16 ) ); + + connect( m_upButton, SIGNAL(activated(KTinyTabButton*)), this, SLOT(upClicked()) ); + connect( m_downButton, SIGNAL(activated(KTinyTabButton*)), this, SLOT(downClicked()) ); + connect( m_configureButton, SIGNAL(activated(KTinyTabButton*)), this, SLOT(configureClicked()) ); + + setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed ); + updateFixedHeight(); +} + +/** + * Destroys the tab bar. + */ +KTinyTabBar::~KTinyTabBar() +{ +} + +/** + * Loads the settings from \a config from section \a group. + * Remembered properties are: + * - number of rows + * - minimum and maximum tab width + * - fixed tab height + * - button colors + * - much more! + * . + * The original group is saved and restored at the end of this function. + * + * \note Call @p load() immediately after you created the tabbar, otherwise + * some properties might not be restored correctly (like highlighted + * buttons). + */ +void KTinyTabBar::load( KConfigBase* config, const QString& group ) +{ + KConfigGroup cg( config, group ); + + // tabbar properties + setLocationTop ( cg.readEntry( "location top", false ) ); + setNumRows ( cg.readEntry( "count of rows", 1 ) ); + setMinimumTabWidth( cg.readEntry( "minimum width", 150 ) ); + setMaximumTabWidth( cg.readEntry( "maximum width", 300 ) ); + setTabHeight ( cg.readEntry( "fixed height", 20 ) ); + setTabSortType ( (SortType) cg.readEntry( "sort type", (int)OpeningOrder ) ); + setTabButtonStyle ( (ButtonStyle) cg.readEntry( "button style", (int)Push ) ); + setFollowCurrentTab(cg.readEntry("follow current tab", true ) ); + setHighlightModifiedTabs( cg.readEntry( "highlight modified", false ) ); + setHighlightPreviousTab( cg.readEntry( "highlight previous", false ) ); + setHighlightActiveTab( cg.readEntry( "highlight active", false ) ); + setHighlightOpacity(cg.readEntry( "highlight opacity", 20 ) ); + + // color settings + setModifiedTabsColor( cg.readEntry( "color modified", m_colorModifiedTab ) ); + setActiveTabColor( cg.readEntry( "color active", m_colorActiveTab ) ); + setPreviousTabColor( cg.readEntry( "color previous", m_colorPreviousTab ) ); + + // highlighted entries + QStringList documents = cg.readEntry( "highlighted documents", QStringList() ); + QStringList colors = cg.readEntry( "highlighted colors", QStringList() ); + + // restore highlight map + m_highlightedTabs.clear(); + for( int i = 0; i < documents.size() && i < colors.size(); ++i ) + m_highlightedTabs[documents[i]] = colors[i]; + + setHighlightMarks( highlightMarks() ); +} + +/** + * Saves the settings to \a config into section \a group. + * The original group is saved and restored at the end of this function. + * See @p load() for more information. + */ +void KTinyTabBar::save( KConfigBase* config, const QString& group ) const +{ + KConfigGroup cg( config, group ); + + // tabbar properties + cg.writeEntry( "location top", locationTop() ); + cg.writeEntry( "count of rows", numRows() ); + cg.writeEntry( "minimum width", minimumTabWidth() ); + cg.writeEntry( "maximum width", maximumTabWidth() ); + cg.writeEntry( "fixed height", tabHeight() ); + cg.writeEntry( "sort type", (int)tabSortType() ); + cg.writeEntry( "button style", (int) tabButtonStyle() ); + cg.writeEntry( "follow current tab", followCurrentTab() ); + cg.writeEntry( "highlight modified", highlightModifiedTabs() ); + cg.writeEntry( "highlight previous", highlightPreviousTab() ); + cg.writeEntry( "highlight active", highlightActiveTab() ); + cg.writeEntry( "highlight opacity", highlightOpacity() ); + + + // color settings + cg.writeEntry( "color modified", modifiedTabsColor() ); + cg.writeEntry( "color active", activeTabColor() ); + cg.writeEntry( "color previous", previousTabColor() ); + + // highlighted entries + cg.writeEntry( "highlighted documents", m_highlightedTabs.keys() ); + cg.writeEntry( "highlighted colors", m_highlightedTabs.values() ); +} + +/** + * Set the location to @p top. + */ +void KTinyTabBar::setLocationTop( bool top ) +{ + m_locationTop = top; +} + +/** + * Get whether the location is on top or bottom. + */ +bool KTinyTabBar::locationTop() const +{ + return m_locationTop; +} + + +/** + * Set the number of \a rows. + */ +void KTinyTabBar::setNumRows( int rows ) +{ + if( rows < 1 || rows == numRows() ) + return; + + m_numRows = rows; + updateFixedHeight(); +} + +/** + * Get the number of rows. + */ +int KTinyTabBar::numRows() const +{ + return m_numRows; +} + +/** + * Set the minimum width in pixels a tab must have. + */ +void KTinyTabBar::setMinimumTabWidth( int min_pixel ) +{ + if( m_minimumTabWidth == min_pixel ) + return; + + m_minimumTabWidth = min_pixel; + triggerResizeEvent(); +} + +/** + * Set the maximum width in pixels a tab may have. + */ +void KTinyTabBar::setMaximumTabWidth( int max_pixel ) +{ + if( m_maximumTabWidth == max_pixel ) + return; + + m_maximumTabWidth = max_pixel; + triggerResizeEvent(); +} + +/** + * Get the minimum width in pixels a tab can have. + */ +int KTinyTabBar::minimumTabWidth() const +{ + return m_minimumTabWidth; +} + +/** + * Get the maximum width in pixels a tab can have. + */ +int KTinyTabBar::maximumTabWidth() const +{ + return m_maximumTabWidth; +} + +/** + * Set the fixed height in pixels all tabs have. + * \note If you also show icons use a height of iconheight + 2. + * E.g. for 16x16 pixel icons, a tab height of 18 pixel looks best. + * For 22x22 pixel icons a height of 24 pixel is best etc. + */ +void KTinyTabBar::setTabHeight( int height_pixel ) +{ + if( m_tabHeight == height_pixel ) + return; + + m_tabHeight = height_pixel; + updateFixedHeight(); +} + +/** + * Get the fixed tab height in pixels. + */ +int KTinyTabBar::tabHeight() const +{ + return m_tabHeight; +} + +/** + * Adds a new tab with text \a text. Returns the new tab's ID. The document's + * url @p docurl is used to sort the documents by URL. + */ +int KTinyTabBar::addTab( const QString& docurl, const QString& text ) +{ + return addTab( docurl, QIcon(), text ); +} + +/** + * This is an overloaded member function, provided for convenience. + * It behaves essentially like the above function. + * + * Adds a new tab with \a icon and \a text. Returns the new tab's index. + */ +int KTinyTabBar::addTab( const QString& docurl, const QIcon& icon, const QString& text ) +{ + KTinyTabButton* tabButton = new KTinyTabButton( docurl, text, m_nextID, false, this ); + tabButton->setIcon( icon ); + if( m_highlightedTabs.contains( text ) ) + tabButton->setHighlightColor( QColor( m_highlightedTabs[text] ) ); + + // set all properties! be sure nothing is missing :) + tabButton->setHighlightOpacity( highlightOpacity() ); + tabButton->setTabButtonStyle( tabButtonStyle() ); + tabButton->setHighlightModifiedTabs( highlightModifiedTabs() ); + tabButton->setHighlightActiveTab( highlightActiveTab() ); + tabButton->setHighlightPreviousTab( highlightPreviousTab() ); + tabButton->setModifiedTabsColor( modifiedTabsColor() ); + tabButton->setActiveTabColor( activeTabColor() ); + tabButton->setPreviousTabColor( previousTabColor() ); + + m_tabButtons.append( tabButton ); + m_IDToTabButton[m_nextID] = tabButton; + connect( tabButton, SIGNAL(activated(KTinyTabButton*)), + this, SLOT(tabButtonActivated(KTinyTabButton*)) ); + connect( tabButton, SIGNAL(highlightChanged(KTinyTabButton*)), + this, SLOT(tabButtonHighlightChanged(KTinyTabButton*)) ); + connect( tabButton, SIGNAL(closeRequest(KTinyTabButton*)), + this, SLOT(tabButtonCloseRequest(KTinyTabButton*)) ); + connect( tabButton, SIGNAL(closeOtherTabsRequest(KTinyTabButton*)), + this, SLOT(tabButtonCloseOtherRequest(KTinyTabButton*)) ); + connect( tabButton, SIGNAL(closeAllTabsRequest()), + this, SLOT(tabButtonCloseAllRequest()) ); + + if( !isVisible() ) + show(); + + updateSort(); + + return m_nextID++; +} + +/** + * Get the ID of the tab bar's activated tab. Returns -1 if no tab is activated. + */ +int KTinyTabBar::currentTab() const +{ + if( m_activeButton != 0L ) + return m_activeButton->buttonID(); + + return -1; +} + +/** + * Activate the tab with ID \a button_id. No signal is emitted. + */ +void KTinyTabBar::setCurrentTab( int button_id ) +{ + if( !m_IDToTabButton.contains( button_id ) ) + return; + + KTinyTabButton* tabButton = m_IDToTabButton[button_id]; + if( m_activeButton == tabButton ) + return; + + if( m_previousButton ) + m_previousButton->setPreviousTab( false ); + + if( m_activeButton ) + { + m_activeButton->setActivated( false ); + m_previousButton = m_activeButton; + m_previousButton->setPreviousTab( true ); + } + + m_activeButton = tabButton; + m_activeButton->setActivated( true ); + m_activeButton->setPreviousTab( false ); + + // make current tab visible + if( followCurrentTab() && !m_activeButton->isVisible() ) + makeCurrentTabVisible(); +} + +/** + * Removes the tab with ID \a button_id. + */ +void KTinyTabBar::removeTab( int button_id ) +{ + if( !m_IDToTabButton.contains( button_id ) ) + return; + + KTinyTabButton* tabButton = m_IDToTabButton[button_id]; + + if( tabButton == m_previousButton ) + m_previousButton = 0L; + + if( tabButton == m_activeButton ) + m_activeButton = 0L; + + m_IDToTabButton.remove( button_id ); + m_tabButtons.removeAll( tabButton ); + // delete the button with deleteLater() because the button itself might + // have send a close-request. So the app-execution is still in the + // button, a delete tabButton; would lead to a crash. + tabButton->hide(); + tabButton->deleteLater(); + + if( m_tabButtons.count() == 0 ) + hide(); + + triggerResizeEvent(); +} + +/** + * Returns whether a tab with ID \a button_id exists. + */ +bool KTinyTabBar::containsTab( int button_id ) const +{ + return m_IDToTabButton.contains( button_id ); +} + +/** + * Sets the text of the tab with ID \a button_id to \a text. + * \see tabText() + */ +void KTinyTabBar::setTabText( int button_id, const QString& text ) +{ + if( !m_IDToTabButton.contains( button_id ) ) + return; + + // change highlight key, if entry exists + if( m_highlightedTabs.contains( m_IDToTabButton[button_id]->text() ) ) + { + QString value = m_highlightedTabs[m_IDToTabButton[button_id]->text()]; + m_highlightedTabs.remove( m_IDToTabButton[button_id]->text() ); + m_highlightedTabs[text] = value; + + // do not emit highlightMarksChanged(), because every tabbar gets this + // change (usually) + // emit highlightMarksChanged( this ); + } + + m_IDToTabButton[button_id]->setText( text ); + + if( tabSortType() == Name || tabSortType() == URL || tabSortType() == Extension ) + updateSort(); +} + +/** + * Returns the text of the tab with ID \a button_id. If the button id does not + * exist \a QString() is returned. + * \see setTabText() + */ +QString KTinyTabBar::tabText( int button_id ) const +{ + if( m_IDToTabButton.contains( button_id ) ) + return m_IDToTabButton[button_id]->text(); + + return QString(); +} + +/** + * Set the button @p button_id's url to @p docurl. + */ +void KTinyTabBar::setTabURL( int button_id, const QString& docurl ) +{ + if( !m_IDToTabButton.contains( button_id ) ) + return; + + m_IDToTabButton[button_id]->setURL( docurl); + + if( tabSortType() == URL ) + updateSort(); +} + +/** + * Get the button @p button_id's url. Result is QStrint() if not available. + */ +QString KTinyTabBar::tabURL( int button_id ) const +{ + if( m_IDToTabButton.contains( button_id ) ) + return m_IDToTabButton[button_id]->url(); + + return QString(); +} + + +/** + * Sets the icon of the tab with ID \a button_id to \a icon. + * \see tabIcon() + */ +void KTinyTabBar::setTabIcon( int button_id, const QIcon& icon ) +{ + if( m_IDToTabButton.contains( button_id ) ) + m_IDToTabButton[button_id]->setIcon( icon ); +} + +/** + * Returns the icon of the tab with ID \a button_id. If the button id does not + * exist \a QIcon() is returned. + * \see setTabIcon() + */ +QIcon KTinyTabBar::tabIcon( int button_id ) const +{ + if( m_IDToTabButton.contains( button_id ) ) + return m_IDToTabButton[button_id]->icon(); + + return QIcon(); +} + +/** + * Returns the number of tabs in the tab bar. + */ +int KTinyTabBar::count() const +{ + return m_tabButtons.count(); +} + +/** + * Set the tabbutton style. The signal @p tabButtonStyleChanged() is not + * emitted. + * @param tabStyle button style + */ +void KTinyTabBar::setTabButtonStyle( ButtonStyle tabStyle ) +{ + m_tabButtonStyle = tabStyle; + + foreach (KTinyTabButton* tabButton, m_tabButtons ) + tabButton->setTabButtonStyle( tabStyle ); + + m_upButton->setTabButtonStyle( tabStyle ); + m_downButton->setTabButtonStyle( tabStyle ); + m_configureButton->setTabButtonStyle( tabStyle ); +} + +/** + * Get the tabbutton style. + * @return button style + */ +KTinyTabBar::ButtonStyle KTinyTabBar::tabButtonStyle() const +{ + return m_tabButtonStyle; +} + +/** + * Set the sort tye to @p sort. + */ +void KTinyTabBar::setTabSortType( SortType sort ) +{ + if( m_sortType == sort ) + return; + + m_sortType = sort; + updateSort(); +} + +/** + * Get the sort type. + */ +KTinyTabBar::SortType KTinyTabBar::tabSortType() const +{ + return m_sortType; +} + + +/** + * Set follow current tab to @p follow. + */ +void KTinyTabBar::setFollowCurrentTab( bool follow ) +{ + m_followCurrentTab = follow; + if( m_followCurrentTab ) + makeCurrentTabVisible(); +} + +/** + * Check, whether to follow the current tab. + */ +bool KTinyTabBar::followCurrentTab() const +{ + return m_followCurrentTab; +} + +/** + * Set whether to highlight the previous button to @e highlight. + */ +void KTinyTabBar::setHighlightPreviousTab( bool highlight ) +{ + m_highlightPreviousTab = highlight; + + foreach (KTinyTabButton* tabButton, m_tabButtons ) + tabButton->setHighlightPreviousTab( highlight ); +} + +/** + * Check, whether to highlight the previous button. + */ +bool KTinyTabBar::highlightPreviousTab() const +{ + return m_highlightPreviousTab; +} + +/** + * Set whether to highlight the previous button to @e highlight. + */ +void KTinyTabBar::setHighlightActiveTab( bool highlight ) +{ + m_highlightActiveTab = highlight; + + foreach( KTinyTabButton* tabButton, m_tabButtons ) + tabButton->setHighlightActiveTab( highlight ); +} + +/** + * Check, whether to highlight the previous button. + */ +bool KTinyTabBar::highlightActiveTab() const +{ + return m_highlightActiveTab; +} + +/** + * Set the highlight opacity to @p value. + */ +void KTinyTabBar::setHighlightOpacity( int value ) +{ + m_highlightOpacity = value; + + foreach( KTinyTabButton* tabButton, m_tabButtons ) + tabButton->setHighlightOpacity( value ); +} + +/** + * Get the highlight opacity. + */ +int KTinyTabBar::highlightOpacity() const +{ + return m_highlightOpacity; +} + +/** + * Set the color for the previous tab to @p color. + */ +void KTinyTabBar::setPreviousTabColor( const QColor& color ) +{ + m_colorPreviousTab = color; + foreach( KTinyTabButton* tabButton, m_tabButtons ) + tabButton->setPreviousTabColor( color ); +} + +/** + * Get the color for the previous tab. + */ +QColor KTinyTabBar::previousTabColor() const +{ + return m_colorPreviousTab; +} + +/** + * Set the color for the active tab to @p color. + */ +void KTinyTabBar::setActiveTabColor( const QColor& color ) +{ + m_colorActiveTab = color; + foreach( KTinyTabButton* tabButton, m_tabButtons ) + tabButton->setActiveTabColor( color ); +} + +/** + * Get the color for the active tab. + */ +QColor KTinyTabBar::activeTabColor() const +{ + return m_colorActiveTab; +} + +void KTinyTabBar::setTabModified( int button_id, bool modified ) +{ + if( m_IDToTabButton.contains( button_id ) ) + m_IDToTabButton[button_id]->setModified( modified); +} + +bool KTinyTabBar::isTabModified( int button_id ) const +{ + if( m_IDToTabButton.contains( button_id ) ) + return m_IDToTabButton[button_id]->isModified(); + + return false; +} + +void KTinyTabBar::setHighlightModifiedTabs( bool modified ) +{ + m_highlightModifiedTabs = modified; + foreach( KTinyTabButton* tabButton, m_tabButtons ) + tabButton->setHighlightModifiedTabs( modified ); +} + +bool KTinyTabBar::highlightModifiedTabs() const +{ + return m_highlightModifiedTabs; +} + +void KTinyTabBar::setModifiedTabsColor( const QColor& color ) +{ + m_colorModifiedTab = color; + foreach( KTinyTabButton* tabButton, m_tabButtons ) + tabButton->setModifiedTabsColor( color ); +} + +QColor KTinyTabBar::modifiedTabsColor() const +{ + return m_colorModifiedTab; +} + +void KTinyTabBar::removeHighlightMarks() +{ + foreach( KTinyTabButton* tabButton, m_tabButtons ) + { + if( tabButton->highlightColor().isValid() ) + tabButton->setHighlightColor( QColor() ); + } + + m_highlightedTabs.clear(); + emit highlightMarksChanged( this ); +} + +void KTinyTabBar::setHighlightMarks( const QMap& marks ) +{ + m_highlightedTabs = marks; + + foreach( KTinyTabButton* tabButton, m_tabButtons ) + { + if( marks.contains( tabButton->text() ) ) + { + if( tabButton->highlightColor().name() != marks[tabButton->text()] ) + tabButton->setHighlightColor( QColor( marks[tabButton->text()] ) ); + } + else if( tabButton->highlightColor().isValid() ) + tabButton->setHighlightColor( QColor() ); + } +} + +QMap KTinyTabBar::highlightMarks() const +{ + return m_highlightedTabs; +} +//END public member functions + + + + + + +//BEGIN protected / private member functions + +/** + * Active button changed. Emit signal \p currentChanged() with the button's ID. + */ +void KTinyTabBar::tabButtonActivated( KTinyTabButton* tabButton ) +{ + if( tabButton == m_activeButton ) + return; + + if( m_previousButton ) + m_previousButton->setPreviousTab( false ); + + if( m_activeButton ) + { + m_activeButton->setActivated( false ); + m_previousButton = m_activeButton; + m_previousButton->setPreviousTab( true ); + } + + m_activeButton = tabButton; + m_activeButton->setActivated( true ); + m_activeButton->setPreviousTab( false ); + + emit currentChanged( tabButton->buttonID() ); +} + +/** + * The \e tabButton's highlight color changed, so update the list of documents + * and colors. + */ +void KTinyTabBar::tabButtonHighlightChanged( KTinyTabButton* tabButton ) +{ + if( tabButton->highlightColor().isValid() ) + { + m_highlightedTabs[tabButton->text()] = tabButton->highlightColor().name(); + emit highlightMarksChanged( this ); + } + else if( m_highlightedTabs.contains( tabButton->text() ) ) + { + // invalid color, so remove the item + m_highlightedTabs.remove( tabButton->text() ); + emit highlightMarksChanged( this ); + } +} + +/** + * If the user wants to close a tab with the context menu, it sends a close + * request. Throw the close request by emitting the signal @p closeRequest(). + */ +void KTinyTabBar::tabButtonCloseRequest( KTinyTabButton* tabButton ) +{ + emit closeRequest( tabButton->buttonID() ); +} + +/** + * If the user wants to close all tabs except the current one using the context + * menu, it sends multiple close requests. + * Throw the close requests by emitting the signal @p closeRequest(). + */ +void KTinyTabBar::tabButtonCloseOtherRequest( KTinyTabButton* tabButton ) +{ + QList tabToCloseID; + for (int i = 0; i < m_tabButtons.size(); ++i) { + if ((m_tabButtons.at(i))->buttonID() != tabButton->buttonID()) + tabToCloseID << (m_tabButtons.at(i))->buttonID(); + } + + for (int i = 0; i < tabToCloseID.size(); i++) { + emit closeRequest(tabToCloseID.at(i)); + } +} + +/** + * If the user wants to close all the tabs using the context menu, it sends + * multiple close requests. + * Throw the close requests by emitting the signal @p closeRequest(). + */ +void KTinyTabBar::tabButtonCloseAllRequest( ) +{ + QList tabToCloseID; + for (int i = 0; i < m_tabButtons.size(); ++i) { + tabToCloseID << (m_tabButtons.at(i))->buttonID(); + } + + for (int i = 0; i < tabToCloseID.size(); i++) { + emit closeRequest(tabToCloseID.at(i)); + } +} + +/** + * Recalculate geometry for all children. + */ +void KTinyTabBar::resizeEvent( QResizeEvent* event ) +{ +// kDebug() << "resizeEvent"; + // if there are no tabs there is nothing to do. Do not delete otherwise + // division by zero is possible. + if( m_tabButtons.count() == 0 ) + { + updateHelperButtons( event->size(), 0 ); + return; + } + + int tabbar_width = event->size().width() - ( 4 - ( numRows()>3?3:numRows() ) ) * m_navigateSize; + int tabs_per_row = tabbar_width / minimumTabWidth(); + if( tabs_per_row == 0 ) + tabs_per_row = 1; + + int tab_width = minimumTabWidth(); + + int needed_rows = m_tabButtons.count() / tabs_per_row; + if( needed_rows * tabs_per_row < (int)m_tabButtons.count() ) + ++needed_rows; + + // if we do not need more rows than available we can increase the tab + // buttons' width up to maximumTabWidth. + if( needed_rows <= numRows() ) + { + // use available size optimal, but honor maximumTabWidth() + tab_width = tabbar_width * numRows() / m_tabButtons.count(); + + if( tab_width > maximumTabWidth() ) + tab_width = maximumTabWidth(); + + tabs_per_row = tabbar_width / tab_width; + + // due to rounding fuzzys we have to increase the tabs_per_row if + // the number of tabs does not fit. + if( tabs_per_row * numRows() < (int)m_tabButtons.count() ) + ++tabs_per_row; + } + + // On this point, we really know the value of tabs_per_row. So a final + // calculation gives us the tab_width. With this the width can even get + // greater than maximumTabWidth(), but that does not matter as it looks + // more ugly if there is a lot wasted space on the right. + tab_width = tabbar_width / tabs_per_row; + + updateHelperButtons( event->size(), needed_rows ); + + foreach( KTinyTabButton* tabButton, m_tabButtons ) + tabButton->hide(); + + for( int row = 0; row < numRows(); ++row ) + { + int current_row = row + currentRow(); + for( int i = 0; i < tabs_per_row; ++i ) + { + // value returns 0L, if index is out of bounds + KTinyTabButton *tabButton = m_tabButtons.value( current_row * tabs_per_row + i ); + + if( tabButton ) + { + tabButton->setGeometry( i * tab_width, row * tabHeight(), + tab_width, tabHeight() ); + tabButton->show(); + } + } + } +} + +void KTinyTabBar::wheelEvent( QWheelEvent* event ) +{ + event->accept(); + + if (event->delta() < 0) { + scrollDown(); + } else { + scrollUp(); + } +} + +/** + * Make sure the current tab visible. If it is not visible the tabbar scrolls + * so that it is visible. + */ +void KTinyTabBar::makeCurrentTabVisible() +{ + if( !m_activeButton || m_activeButton->isVisible() ) + return; + + //BEGIN copy of resizeEvent + int tabbar_width = width() - ( 4 - ( numRows()>3?3:numRows() ) ) * m_navigateSize; + int tabs_per_row = tabbar_width / minimumTabWidth(); + if( tabs_per_row == 0 ) + tabs_per_row = 1; + + int tab_width = minimumTabWidth(); + + int needed_rows = m_tabButtons.count() / tabs_per_row; + if( needed_rows * tabs_per_row < (int)m_tabButtons.count() ) + ++needed_rows; + + // if we do not need more rows than available we can increase the tab + // buttons' width up to maximumTabWidth. + if( needed_rows <= numRows() ) + { + // use available size optimal, but honor maximumTabWidth() + tab_width = tabbar_width * numRows() / m_tabButtons.count(); + + if( tab_width > maximumTabWidth() ) + tab_width = maximumTabWidth(); + + tabs_per_row = tabbar_width / tab_width; + + // due to rounding fuzzys we have to increase the tabs_per_row if + // the number of tabs does not fit. + if( tabs_per_row * numRows() < (int)m_tabButtons.count() ) + ++tabs_per_row; + } + //END copy of resizeEvent + + int index = m_tabButtons.indexOf( m_activeButton ); + int firstVisible = currentRow() * tabs_per_row; + int lastVisible = ( currentRow() + numRows() ) * tabs_per_row - 1; + + if( firstVisible >= m_tabButtons.count() ) + firstVisible = m_tabButtons.count() - 1; + + if( lastVisible >= m_tabButtons.count() ) + lastVisible = m_tabButtons.count() - 1; + + if( index < firstVisible ) + { + setCurrentRow( index / tabs_per_row ); + } + else if( index > lastVisible ) + { + const int diff = index / tabs_per_row - ( numRows() - 1 ); + setCurrentRow( diff ); + } +} + +/** + * Updates the fixed height. Called when the tab height or the number of rows + * changed. + */ +void KTinyTabBar::updateFixedHeight() +{ + setFixedHeight( numRows() * tabHeight() ); + triggerResizeEvent(); +} + +/** + * May modifies current row if more tabs fit into a row. + * Sets geometry for the buttons 'up', 'down' and 'configure'. + */ +void KTinyTabBar::updateHelperButtons( QSize new_size, int needed_rows ) +{ + // if the size increased so that more tabs fit into one row it can happen + // that suddenly a row on the bottom is empty - or that even all rows are + // empty. That is not desired, so make sure that currentRow has a + // reasonable value. + if( currentRow() + numRows() > needed_rows ) + m_currentRow = ( needed_rows - numRows() < 0 ? + 0 : needed_rows - numRows() ); + + m_upButton->setEnabled( currentRow() != 0 ); + m_downButton->setEnabled( needed_rows - currentRow() > numRows() ); + + // set geometry for up, down, configure + switch( numRows() ) + { + case 1: + m_upButton->setGeometry( new_size.width() - 3 * m_navigateSize, + 0, m_navigateSize, tabHeight() ); + m_downButton->setGeometry( new_size.width() - 2 * m_navigateSize, + 0, m_navigateSize, tabHeight() ); + m_configureButton->setGeometry( new_size.width() - m_navigateSize, + 0, m_navigateSize, tabHeight() ); + break; + case 2: + m_upButton->setGeometry( new_size.width() - 2 * m_navigateSize, + 0, m_navigateSize, tabHeight() ); + m_downButton->setGeometry( new_size.width() - 2 * m_navigateSize, + tabHeight(), m_navigateSize, tabHeight() ); + m_configureButton->setGeometry( new_size.width() - m_navigateSize, + 0, m_navigateSize, 2 * tabHeight() ); + break; + default: + m_upButton->setGeometry( new_size.width() - m_navigateSize, + 0, m_navigateSize, tabHeight() ); + m_downButton->setGeometry( new_size.width() - m_navigateSize, + tabHeight(), m_navigateSize, tabHeight() ); + m_configureButton->setGeometry( new_size.width() - m_navigateSize, + 2 * tabHeight(), m_navigateSize, tabHeight() ); + break; + } +} + +void KTinyTabBar::updateSort() +{ + global_sortType = tabSortType(); + qSort( m_tabButtons.begin(), m_tabButtons.end(), tabLessThan ); + triggerResizeEvent(); +} + +/** + * Decrease the current row. Called when the button 'up' was clicked. + */ +void KTinyTabBar::upClicked() +{ + scrollUp(); + m_upButton->setActivated( false ); +} + +/** + * Increase the current row. Called when the button 'down' was clicked. + */ +void KTinyTabBar::downClicked() +{ + scrollDown(); + m_downButton->setActivated( false ); +} + +/** + * Show configure dialog. If the button style changes the signal + * @p tabButtonStyleChanged() will be emitted. + */ +void KTinyTabBar::configureClicked() +{ + m_configureButton->setActivated( false ); + + KTinyTabBarConfigDialog dlg( this, (QWidget*)parent() ); + dlg.setObjectName( "tabbar_config_dialog" ); + if( dlg.exec() == KDialog::Accepted ) + { + KTinyTabBarConfigPage* page = dlg.configPage(); + + setLocationTop( page->locationTop() ); + setNumRows( page->numberOfRows() ); + setMinimumTabWidth( page->minimumTabWidth() ); + setMaximumTabWidth( page->maximumTabWidth() ); + setTabHeight( page->fixedTabHeight() ); + setTabSortType( page->tabSortType() ); + setTabButtonStyle( page->tabButtonStyle() ); + setFollowCurrentTab( page->followCurrentTab() ); + setHighlightModifiedTabs( page->highlightModifiedTabs() ); + setHighlightActiveTab( page->highlightActiveTab() ); + setHighlightPreviousTab( page->highlightPreviousTab() ); + setModifiedTabsColor( page->modifiedTabsColor() ); + setActiveTabColor( page->activeTabColor() ); + setPreviousTabColor( page->previousTabColor() ); + setHighlightOpacity( page->highlightOpacity() ); + + emit settingsChanged( this ); + } +} + +/** + * Set the current row. + * @param row new current row + */ +void KTinyTabBar::setCurrentRow( int row ) +{ + if( row == currentRow() ) + return; + + m_currentRow = row; + if( m_currentRow < 0 ) + m_currentRow = 0; + + triggerResizeEvent(); +} + +/** + * Returns the current row. + */ +int KTinyTabBar::currentRow() const +{ + return m_currentRow; +} + +/** + * Increase the current row. + */ +void KTinyTabBar::scrollDown() +{ + ++m_currentRow; + triggerResizeEvent(); +} + +/** + * Decrease the current row. + */ +void KTinyTabBar::scrollUp() +{ + if( m_currentRow == 0 ) + return; + + --m_currentRow; + triggerResizeEvent(); +} + +/** + * Triggers a resizeEvent. This is used whenever the tab buttons need + * a rearrange. By using \p QApplication::sendEvent() multiple calls are + * combined into only one call. + * + * \see addTab(), removeTab(), setMinimumWidth(), setMaximumWidth(), + * setFixedHeight(), scrollDown(), scrollUp() + */ +void KTinyTabBar::triggerResizeEvent() +{ + QResizeEvent ev( size(), size() ); + QApplication::sendEvent( this, &ev ); +} + +//END protected / private member functions + +// kate: space-indent on; indent-width 4; tab-width 4; replace-tabs on; eol unix; diff --git a/kate/addons/kate/tabbarextension/ktinytabbar.h b/kate/addons/kate/tabbarextension/ktinytabbar.h new file mode 100644 index 00000000..8a570c65 --- /dev/null +++ b/kate/addons/kate/tabbarextension/ktinytabbar.h @@ -0,0 +1,250 @@ +/*************************************************************************** + ktinytabbar.h + ------------------- + begin : 2005-06-15 + copyright : (C) 2005 by Dominik Haumann + email : dhdev@gmx.de + + Copyright (C) 2007 Flavio Castelli + ***************************************************************************/ + +/*************************************************************************** + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#ifndef KTINYTABBAR_H +#define KTINYTABBAR_H + +#include + +#include +#include +#include +#include + +class KTinyTabButton; +class KConfigBase; + +/** + * The \p KTinyTabBar class provides a tab bar, e.g. for tabbed documents and + * supports multiple rows. The tab bar hides itself if there are no tabs. + * + * It implements the API from TrollTech's \p QTabBar with some minor changes + * and additions. + * + * @author Dominik Haumann + */ +class KTinyTabBar : public QWidget +{ + Q_OBJECT + +public: + /** + * Defines the tab button's style. + */ + enum ButtonStyle + { + Push=0, ///< default push button + Flat ///< flat push button + }; + + /** + * Sort types. + */ + enum SortType + { + OpeningOrder=0, ///< opening order + Name, ///< alphabetically + URL, ///< alphabetically URL based + Extension ///< by file extension (suffix) + }; + Q_DECLARE_FLAGS( SortTypes, SortType ) + +public: + // NOTE: as the API here is very self-explaining the docs are in the cpp + // file, more clean imho. + + KTinyTabBar( QWidget *parent = 0 ); + virtual ~KTinyTabBar(); + + void load( KConfigBase* config, const QString& group ); + void save( KConfigBase* config, const QString& group ) const; + + void setLocationTop( bool top ); + bool locationTop() const; + + void setNumRows( int rows ); + int numRows() const; + + void setMinimumTabWidth( int min_pixel ); + void setMaximumTabWidth( int max_pixel ); + + int minimumTabWidth() const; + int maximumTabWidth() const; + + void setTabHeight( int height_pixel ); + int tabHeight() const; + + int addTab( const QString& docurl, const QString& text ); + int addTab( const QString& docurl, const QIcon& pixmap, const QString& text ); + void removeTab( int button_id ); + + int currentTab() const; + // corresponding SLOT: void setCurrentTab( int button_id ); + + bool containsTab( int button_id ) const; + + void setTabURL( int button_id, const QString& docurl ); + QString tabURL( int button_id ) const; + + void setTabText( int button_id, const QString& text ); + QString tabText( int button_id ) const; + + void setTabIcon( int button_id, const QIcon& pixmap ); + QIcon tabIcon( int button_id ) const; + + void setTabModified( int button_id, bool modified ); + bool isTabModified( int button_id ) const; + + void setHighlightModifiedTabs( bool modified ); + bool highlightModifiedTabs() const; + + void setModifiedTabsColor( const QColor& color ); + QColor modifiedTabsColor() const; + + int count() const; + + void setTabButtonStyle( ButtonStyle tabStyle ); + ButtonStyle tabButtonStyle() const; + + void setTabSortType( SortType sort ); + SortType tabSortType() const; + + void setFollowCurrentTab( bool follow ); + bool followCurrentTab() const; + + void setHighlightPreviousTab( bool highlight ); + bool highlightPreviousTab() const; + + void setHighlightActiveTab( bool highlight ); + bool highlightActiveTab() const; + + void setHighlightOpacity( int value ); + int highlightOpacity() const; + + void setPreviousTabColor( const QColor& color ); + QColor previousTabColor() const; + + void setActiveTabColor( const QColor& color ); + QColor activeTabColor() const; + + void setHighlightMarks( const QMap& marks ); + QMap highlightMarks() const; + +public slots: + void setCurrentTab( int button_id ); // does not emit signal + void removeHighlightMarks(); + +signals: + /** + * This signal is emitted whenever the current activated tab changes. + */ + void currentChanged( int button_id ); + /** + * This signal is emitted whenever a tab should be closed. + */ + void closeRequest( int button_id ); + /** + * This signal is emitted whenever a setting entry changes. + * A special property is the location. As the tabbar is embedded it cannot + * change its location itself. So if you find a changed location, then go + * and change it yourself! + */ + void settingsChanged( KTinyTabBar* tabbar ); + + /** + * This signal is emitted whenever a highlight mark changes. + * Usually this is used to synchronice several tabbars. + */ + void highlightMarksChanged( KTinyTabBar* tabbar ); + +protected slots: + void tabButtonActivated( KTinyTabButton* tabButton ); + void tabButtonHighlightChanged( KTinyTabButton* tabButton ); + void tabButtonCloseAllRequest(); + void tabButtonCloseRequest( KTinyTabButton* tabButton ); + void tabButtonCloseOtherRequest( KTinyTabButton* tabButton ); + void upClicked(); + void downClicked(); + void configureClicked(); + void makeCurrentTabVisible(); + +protected: + virtual void resizeEvent( QResizeEvent* event ); + virtual void wheelEvent( QWheelEvent* event ); + + +protected: + void updateFixedHeight(); + void triggerResizeEvent(); + void updateSort(); + int currentRow() const; + void setCurrentRow( int row ); + void updateHelperButtons( QSize new_size, int needed_rows ); + void scrollDown(); + void scrollUp(); + +private: + bool m_locationTop; + int m_numRows; + int m_currentRow; + int m_minimumTabWidth; + int m_maximumTabWidth; + int m_tabHeight; + + QList< KTinyTabButton* > m_tabButtons; + QMap< int, KTinyTabButton* > m_IDToTabButton; + + KTinyTabButton* m_activeButton; + KTinyTabButton* m_previousButton; + + // buttons on the right to navigate and configure + KTinyTabButton* m_upButton; + KTinyTabButton* m_downButton; + KTinyTabButton* m_configureButton; + int m_navigateSize; + + int m_nextID; + + // map of highlighted tabs and colors + QMap< QString, QString > m_highlightedTabs; + // tab button style + ButtonStyle m_tabButtonStyle; + SortType m_sortType; + bool m_highlightModifiedTabs; + bool m_followCurrentTab; + bool m_highlightPreviousTab; + bool m_highlightActiveTab; + int m_highlightOpacity; + + // configurable and saved by KTinyTabBar + QColor m_colorModifiedTab; + QColor m_colorActiveTab; + QColor m_colorPreviousTab; +}; + +#endif // KTINYTABBAR_H + +// kate: space-indent on; indent-width 4; tab-width 4; replace-tabs on; eol unix; diff --git a/kate/addons/kate/tabbarextension/ktinytabbarconfigdialog.cpp b/kate/addons/kate/tabbarextension/ktinytabbarconfigdialog.cpp new file mode 100644 index 00000000..d0b8ff5c --- /dev/null +++ b/kate/addons/kate/tabbarextension/ktinytabbarconfigdialog.cpp @@ -0,0 +1,84 @@ +/*************************************************************************** + ktinytabbarconfigdialog.cpp + ------------------- + begin : 2005-06-19 + copyright : (C) 2005 by Dominik Haumann + email : dhdev@gmx.de + ***************************************************************************/ + +/*************************************************************************** + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#include "ktinytabbarconfigdialog.h" +#include "ktinytabbarconfigpage.h" +#include "ktinytabbar.h" +#include "ktinytabbutton.h" + +#include + + +KTinyTabBarConfigDialog::KTinyTabBarConfigDialog( const KTinyTabBar* tabbar, + QWidget *parent ) + : KDialog( parent ) +{ + setCaption( i18n( "Configure Tab Bar" ) ); + setButtons( KDialog::Cancel | KDialog::Ok ); + + m_configPage = new KTinyTabBarConfigPage( this ); + + m_configPage->setLocationTop( tabbar->locationTop() ); + m_configPage->setNumberOfRows( tabbar->numRows() ); + m_configPage->setMinimumTabWidth( tabbar->minimumTabWidth() ); + m_configPage->setMaximumTabWidth( tabbar->maximumTabWidth() ); + m_configPage->setFixedTabHeight( tabbar->tabHeight() ); + m_configPage->setFollowCurrentTab( tabbar->followCurrentTab() ); + m_configPage->setTabSortType( tabbar->tabSortType() ); + m_configPage->setTabButtonStyle( tabbar->tabButtonStyle() ); + m_configPage->setHighlightModifiedTabs( tabbar->highlightModifiedTabs() ); + m_configPage->setHighlightActiveTab( tabbar->highlightActiveTab() ); + m_configPage->setHighlightPreviousTab( tabbar->highlightPreviousTab() ); + m_configPage->setModifiedTabsColor( tabbar->modifiedTabsColor() ); + m_configPage->setActiveTabColor( tabbar->activeTabColor() ); + m_configPage->setPreviousTabColor( tabbar->previousTabColor() ); + m_configPage->setHighlightOpacity( tabbar->highlightOpacity() ); + + setMainWidget( m_configPage ); + resize( 400, 300 ); + + enableButton( KDialog::Ok, false ); + connect( m_configPage, SIGNAL(changed()), this, SLOT(configChanged()) ); + connect( m_configPage, SIGNAL(removeHighlightMarks()), + tabbar, SLOT(removeHighlightMarks()) ); +} + +KTinyTabBarConfigDialog::~KTinyTabBarConfigDialog() +{ +} + +void KTinyTabBarConfigDialog::configChanged() +{ + enableButton( KDialog::Ok, true ); +} + +KTinyTabBarConfigPage* KTinyTabBarConfigDialog::configPage() +{ + return m_configPage; +} + +#include "moc_ktinytabbarconfigdialog.cpp" + +// kate: space-indent on; tab-width 4; replace-tabs off; eol unix; + diff --git a/kate/addons/kate/tabbarextension/ktinytabbarconfigdialog.h b/kate/addons/kate/tabbarextension/ktinytabbarconfigdialog.h new file mode 100644 index 00000000..c4473a69 --- /dev/null +++ b/kate/addons/kate/tabbarextension/ktinytabbarconfigdialog.h @@ -0,0 +1,61 @@ +/*************************************************************************** + ktinytabbarconfigdialog.h + ------------------- + begin : 2005-06-19 + copyright : (C) 2005 by Dominik Haumann + email : dhdev@gmx.de + ***************************************************************************/ + +/*************************************************************************** + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#ifndef KTINYTABBARCONFIGDIALOG_H +#define KTINYTABBARCONFIGDIALOG_H + +#include + + +class KTinyTabBar; +class KTinyTabBarConfigPage; + +/** + * The class @p KTinyTabBarConfigDialog provides a configuration dialog + * for the @p KTinyTabBar widget. It is only a wrapper dialog for + * @p KTinyTabBarConfigPage. It's mainly for private usage. If the return code + * is @p KDialog::Accepted an option changed for sure. + * + * @author Dominik Haumann + */ +class KTinyTabBarConfigDialog + : public KDialog +{ + Q_OBJECT +public: + explicit KTinyTabBarConfigDialog( const KTinyTabBar* tabbar, QWidget *parent = 0 ); + ~KTinyTabBarConfigDialog(); + + KTinyTabBarConfigPage* configPage(); + +protected slots: + void configChanged(); + +private: + KTinyTabBarConfigPage* m_configPage; +}; + +#endif + +// kate: space-indent on; tab-width 4; replace-tabs off; eol unix; diff --git a/kate/addons/kate/tabbarextension/ktinytabbarconfigpage.cpp b/kate/addons/kate/tabbarextension/ktinytabbarconfigpage.cpp new file mode 100644 index 00000000..8f811c07 --- /dev/null +++ b/kate/addons/kate/tabbarextension/ktinytabbarconfigpage.cpp @@ -0,0 +1,426 @@ +/*************************************************************************** + ktinytabbarconfigpage.cpp + ------------------- + begin : 2005-06-19 + copyright : (C) 2005 by Dominik Haumann + email : dhdev@gmx.de + ***************************************************************************/ + +/*************************************************************************** + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#include "ktinytabbarconfigpage.h" +#include "ktinytabbutton.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +KTinyTabBarConfigPage::KTinyTabBarConfigPage( QWidget *parent ) + : QWidget( parent ) + , Ui::TabBarConfigWidget() +{ + setupUi(this); + + // preview group box + QHBoxLayout* hlPreview = new QHBoxLayout( gbPreview ); + m_previewMinimum = new KTinyTabButton( QString(), i18n( "minimum size" ), 0, true, gbPreview ); + m_previewMaximum = new KTinyTabButton( QString(), i18n( "maximum size" ), 1, true, gbPreview ); + hlPreview->addWidget( m_previewMinimum ); + hlPreview->addWidget( m_previewMaximum ); + + connect(btnClearCache, SIGNAL(clicked()), + this, SIGNAL(removeHighlightMarks())); + + setupConnections(); +} + +void KTinyTabBarConfigPage::setupDefaults() +{ + // location + cmbLocation->setCurrentIndex(0); + + // follow current tab + chkFollowActive->setChecked(true); + + // sort by + cmbSorting->setCurrentIndex(0); + + // tab sizes group box + sbMinWidth->setValue(150); + sbMaxWidth->setValue(200); + sbHeight->setValue(22); + + // button style group + cmbStyle->setCurrentIndex(0); + + // tab highlighting + chkModified->setChecked(false); + colModified->setEnabled(false); + colModified->setColor(Qt::red); + chkActive->setChecked(false); + colActive->setEnabled(false); + colActive->setColor(Qt::blue); + chkPrevious->setChecked(false); + colPrevious->setEnabled(false); + colPrevious->setColor(Qt::yellow); + + slOpacity->setValue( 20 ); + + + // preview + m_previewMinimum->setActivated( true ); + m_previewMinimum->setFixedSize( minimumTabWidth(), fixedTabHeight() ); + m_previewMaximum->setFixedSize( maximumTabWidth(), fixedTabHeight() ); + m_previewMinimum->setHighlightOpacity( highlightOpacity() ); + m_previewMaximum->setHighlightOpacity( highlightOpacity() ); + m_previewMinimum->setTabButtonStyle( tabButtonStyle() ); + m_previewMaximum->setTabButtonStyle( tabButtonStyle() ); + m_previewMinimum->setHighlightActiveTab( highlightActiveTab() ); + m_previewMinimum->setHighlightPreviousTab( highlightPreviousTab() ); + m_previewMaximum->setHighlightActiveTab( highlightActiveTab() ); + m_previewMaximum->setHighlightPreviousTab( highlightPreviousTab() ); + + m_previewMinimum->setHighlightModifiedTabs( false ); + m_previewMaximum->setHighlightModifiedTabs( true ); + m_previewMaximum->setModifiedTabsColor( modifiedTabsColor() ); + m_previewMaximum->setModified( true ); +} + +void KTinyTabBarConfigPage::setupConnections() +{ + // location: nothing + connect(cmbLocation, SIGNAL(currentIndexChanged(int)), this, SIGNAL(changed())); + + // rows group box + connect(sbRows, SIGNAL(valueChanged(int)), this, SIGNAL(changed())); + connect(chkFollowActive, SIGNAL(toggled(bool)), this, SIGNAL(changed())); + + // sort by group box + connect(cmbSorting, SIGNAL(currentIndexChanged(int)), this, SIGNAL(changed())); + + // tab sizes group box + connect(sbMinWidth, SIGNAL(valueChanged(int)), this, SLOT(minimumTabWidthChanged(int))); + connect(sbMaxWidth, SIGNAL(valueChanged(int)), this, SLOT(maximumTabWidthChanged(int))); + connect(sbHeight, SIGNAL(valueChanged(int)), this, SLOT(fixedTabHeightChanged(int))); + + // button style group + connect(cmbStyle, SIGNAL(currentIndexChanged(int)), this, SLOT(buttonStyleChanged(int))); + + // tab highlighting + connect(chkModified, SIGNAL(toggled(bool)), this, SLOT(highlightModifiedTabsChanged(bool))); + connect(colModified, SIGNAL(changed(QColor)), this, SLOT(modifiedTabsColorChanged(QColor))); + connect(chkActive, SIGNAL(toggled(bool)), this, SLOT(highlightActiveTabChanged(bool))); + connect(chkPrevious, SIGNAL(toggled(bool)), this, SLOT(highlightPreviousTabChanged(bool))); + connect(colActive, SIGNAL(changed(QColor)), this, SLOT(activeTabColorChanged(QColor))); + connect(colPrevious, SIGNAL(changed(QColor)), this, SLOT(previousTabColorChanged(QColor))); + connect(slOpacity, SIGNAL(valueChanged(int)), this, SLOT(highlightOpacityChanged(int))); + + // preview + connect(m_previewMinimum, SIGNAL(activated(KTinyTabButton*)), this, SLOT(buttonActivated(KTinyTabButton*))); + connect(m_previewMaximum, SIGNAL(activated(KTinyTabButton*)), this, SLOT(buttonActivated(KTinyTabButton*))); +} + +KTinyTabBarConfigPage::~KTinyTabBarConfigPage() +{ +} + +//BEGIN protected slots + +void KTinyTabBarConfigPage::minimumTabWidthChanged( int value ) +{ + m_previewMinimum->setFixedWidth( value ); + emit changed(); +} + +void KTinyTabBarConfigPage::maximumTabWidthChanged( int value ) +{ + m_previewMaximum->setFixedWidth( value ); + emit changed(); +} + +void KTinyTabBarConfigPage::fixedTabHeightChanged( int value ) +{ + m_previewMinimum->setFixedHeight( value ); + m_previewMaximum->setFixedHeight( value ); + emit changed(); +} + +void KTinyTabBarConfigPage::buttonStyleChanged(int index) +{ + KTinyTabBar::ButtonStyle style = static_cast(index); + m_previewMinimum->setTabButtonStyle(style); + m_previewMaximum->setTabButtonStyle(style); + emit changed(); +} + +void KTinyTabBarConfigPage::highlightActiveTabChanged( bool highlight ) +{ + m_previewMinimum->setHighlightActiveTab( highlight ); + m_previewMaximum->setHighlightActiveTab( highlight ); + emit changed(); +} + +void KTinyTabBarConfigPage::highlightPreviousTabChanged( bool highlight ) +{ + m_previewMinimum->setHighlightPreviousTab( highlight ); + m_previewMaximum->setHighlightPreviousTab( highlight ); + emit changed(); +} + +void KTinyTabBarConfigPage::activeTabColorChanged( const QColor& newColor ) +{ + m_previewMinimum->setActiveTabColor( newColor ); + m_previewMaximum->setActiveTabColor( newColor ); + emit changed(); +} + +void KTinyTabBarConfigPage::previousTabColorChanged( const QColor& newColor ) +{ + m_previewMinimum->setPreviousTabColor( newColor ); + m_previewMaximum->setPreviousTabColor( newColor ); + emit changed(); +} + +void KTinyTabBarConfigPage::highlightOpacityChanged( int value ) +{ + m_previewMinimum->setHighlightOpacity( value ); + m_previewMaximum->setHighlightOpacity( value ); + emit changed(); +} + +void KTinyTabBarConfigPage::highlightModifiedTabsChanged( bool highlight ) +{ + m_previewMinimum->setHighlightModifiedTabs( highlight ); + m_previewMaximum->setHighlightModifiedTabs( highlight ); + emit changed(); +} + +void KTinyTabBarConfigPage::modifiedTabsColorChanged( const QColor& newColor ) +{ + m_previewMinimum->setModifiedTabsColor( newColor ); + m_previewMaximum->setModifiedTabsColor( newColor ); + emit changed(); +} + + + + +void KTinyTabBarConfigPage::buttonActivated( KTinyTabButton* button ) +{ + if( button == m_previewMinimum ) { + m_previewMinimum->setPreviousTab(false); + m_previewMaximum->setActivated(false); + m_previewMaximum->setPreviousTab(true); + } else { + m_previewMaximum->setPreviousTab(false); + m_previewMinimum->setActivated(false); + m_previewMinimum->setPreviousTab(true); + } +} +//END protected slots + +bool KTinyTabBarConfigPage::locationTop() const +{ + return cmbLocation->currentIndex() == 0; +} + +void KTinyTabBarConfigPage::setLocationTop( bool value ) +{ + cmbLocation->setCurrentIndex( value ? 0 : 1); +} + +int KTinyTabBarConfigPage::minimumTabWidth() const +{ + return sbMinWidth->value(); +} + +void KTinyTabBarConfigPage::setMinimumTabWidth( int value ) +{ + sbMinWidth->setValue( value ); +} + +int KTinyTabBarConfigPage::maximumTabWidth() const +{ + return sbMaxWidth->value(); +} + +void KTinyTabBarConfigPage::setMaximumTabWidth( int value ) +{ + sbMaxWidth->setValue( value ); +} + +int KTinyTabBarConfigPage::fixedTabHeight() const +{ + return sbHeight->value(); +} + +void KTinyTabBarConfigPage::setFixedTabHeight( int value ) +{ + sbHeight->setValue( value ); +} + + +int KTinyTabBarConfigPage::numberOfRows() const +{ + return sbRows->value(); +} + +void KTinyTabBarConfigPage::setNumberOfRows( int value ) +{ + sbRows->setValue( value ); +} + +bool KTinyTabBarConfigPage::followCurrentTab() const +{ + return chkFollowActive->isChecked(); +} + +void KTinyTabBarConfigPage::setFollowCurrentTab( bool value ) +{ + chkFollowActive->setChecked( value ); +} + +void KTinyTabBarConfigPage::setTabSortType( KTinyTabBar::SortType type ) +{ + int index = static_cast(type); + cmbSorting->setCurrentIndex(index); +} + +KTinyTabBar::SortType KTinyTabBarConfigPage::tabSortType() const +{ + return static_cast(cmbSorting->currentIndex()); +} + + +void KTinyTabBarConfigPage::setTabButtonStyle( KTinyTabBar::ButtonStyle style ) +{ + int index = static_cast(style); + cmbStyle->setCurrentIndex(index); +} + +KTinyTabBar::ButtonStyle KTinyTabBarConfigPage::tabButtonStyle() const +{ + return static_cast(cmbStyle->currentIndex()); +} + + +void KTinyTabBarConfigPage::setHighlightActiveTab( bool value ) +{ + chkActive->setChecked( value ); + m_previewMinimum->setHighlightActiveTab( value ); + m_previewMaximum->setHighlightActiveTab( value ); +} + +bool KTinyTabBarConfigPage::highlightActiveTab() const +{ + return chkActive->isChecked(); +} + +void KTinyTabBarConfigPage::setActiveTabColor( const QColor& color ) +{ + colActive->setColor( color ); + m_previewMinimum->setActiveTabColor( color ); + m_previewMaximum->setActiveTabColor( color ); +} + +QColor KTinyTabBarConfigPage::activeTabColor() const +{ + return colActive->color(); +} + + +void KTinyTabBarConfigPage::setHighlightPreviousTab( bool value ) +{ + chkPrevious->setChecked( value ); + m_previewMinimum->setHighlightPreviousTab( value ); + m_previewMaximum->setHighlightPreviousTab( value ); +} + +bool KTinyTabBarConfigPage::highlightPreviousTab() const +{ + return chkPrevious->isChecked(); +} + +void KTinyTabBarConfigPage::setPreviousTabColor( const QColor& color ) +{ + colPrevious->setColor( color ); + m_previewMinimum->setPreviousTabColor( color ); + m_previewMaximum->setPreviousTabColor( color ); +} + +QColor KTinyTabBarConfigPage::previousTabColor() const +{ + return colPrevious->color(); +} + +void KTinyTabBarConfigPage::setHighlightOpacity( int value ) +{ + slOpacity->setValue( value ); + m_previewMinimum->setHighlightOpacity( value ); + m_previewMaximum->setHighlightOpacity( value ); +} + +int KTinyTabBarConfigPage::highlightOpacity() const +{ + return slOpacity->value(); +} + +void KTinyTabBarConfigPage::setHighlightModifiedTabs( bool modified ) +{ + chkModified->setChecked( modified ); + m_previewMinimum->setHighlightModifiedTabs( modified ); + m_previewMaximum->setHighlightModifiedTabs( modified ); +} + +bool KTinyTabBarConfigPage::highlightModifiedTabs() const +{ + return chkModified->isChecked(); +} + +void KTinyTabBarConfigPage::setModifiedTabsColor( const QColor& color ) +{ + colModified->setColor( color ); + m_previewMinimum->setModifiedTabsColor( color ); + m_previewMaximum->setModifiedTabsColor( color ); +} + +QColor KTinyTabBarConfigPage::modifiedTabsColor() const +{ + return colModified->color(); +} + + + +#include "moc_ktinytabbarconfigpage.cpp" + +// kate: space-indent on; indent-width 4; tab-width 4; replace-tabs off; eol unix; diff --git a/kate/addons/kate/tabbarextension/ktinytabbarconfigpage.h b/kate/addons/kate/tabbarextension/ktinytabbarconfigpage.h new file mode 100644 index 00000000..0e2efd72 --- /dev/null +++ b/kate/addons/kate/tabbarextension/ktinytabbarconfigpage.h @@ -0,0 +1,139 @@ +/*************************************************************************** + ktinytabbarconfigpage.h + ------------------- + begin : 2005-06-19 + copyright : (C) 2005 by Dominik Haumann + email : dhdev@gmx.de + ***************************************************************************/ + +/*************************************************************************** + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#ifndef KTINYTABBARCONFIGPAGE_H +#define KTINYTABBARCONFIGPAGE_H + +#include +#include + +#include "ktinytabbar.h" +#include "ui_tabbarconfigwidget.h" + +class KColorButton; +class KTinyTabButton; +#include +#include +#include +#include +#include + +/** + * The class @p KTinyTabBarConfigPage provides a config page for + * @p KTinyTabBar. It provides an interface to set the number of rows, + * minimum and maximum tab width, and the tab height. + * + * @author Dominik Haumann + */ +class KTinyTabBarConfigPage + : public QWidget + , private Ui::TabBarConfigWidget +{ + Q_OBJECT +public: + KTinyTabBarConfigPage( QWidget *parent = 0 ); + + ~KTinyTabBarConfigPage(); + + + bool locationTop() const; + void setLocationTop( bool value ); + + + int minimumTabWidth() const; + void setMinimumTabWidth( int value ); + + int maximumTabWidth() const; + void setMaximumTabWidth( int value ); + + int fixedTabHeight() const; + void setFixedTabHeight( int value ); + + + int numberOfRows() const; + void setNumberOfRows( int value ); + + bool followCurrentTab() const; + void setFollowCurrentTab( bool value ); + + void setTabSortType( KTinyTabBar::SortType type ); + KTinyTabBar::SortType tabSortType() const; + + void setTabButtonStyle( KTinyTabBar::ButtonStyle style ); + KTinyTabBar::ButtonStyle tabButtonStyle() const; + + + void setHighlightActiveTab( bool value ); + bool highlightActiveTab() const; + + void setActiveTabColor( const QColor& color ); + QColor activeTabColor() const; + + void setHighlightPreviousTab( bool value ); + bool highlightPreviousTab() const; + + void setPreviousTabColor( const QColor& color ); + QColor previousTabColor() const; + + void setHighlightOpacity( int value ); + int highlightOpacity() const; + + void setHighlightModifiedTabs( bool modified ); + bool highlightModifiedTabs() const; + + void setModifiedTabsColor( const QColor& color ); + QColor modifiedTabsColor() const; + + +signals: + void changed(); + void removeHighlightMarks(); + +protected slots: + void minimumTabWidthChanged(int value); + void maximumTabWidthChanged(int value); + void fixedTabHeightChanged(int value); + void buttonStyleChanged(int index); + void highlightActiveTabChanged( bool highlight ); + void highlightPreviousTabChanged( bool highlight ); + void activeTabColorChanged( const QColor& newColor ); + void previousTabColorChanged( const QColor& newColor ); + void highlightOpacityChanged( int value ); + void highlightModifiedTabsChanged( bool highlight ); + void modifiedTabsColorChanged( const QColor& newColor ); + + void buttonActivated( KTinyTabButton* ); + +protected: + void setupDefaults(); + void setupConnections(); + +private: + KTinyTabButton* m_previewMinimum; + KTinyTabButton* m_previewMaximum; +}; + +#endif + +// kate: space-indent on; indent-width 2; tab-width 4; replace-tabs off; eol unix; diff --git a/kate/addons/kate/tabbarextension/ktinytabbutton.cpp b/kate/addons/kate/tabbarextension/ktinytabbutton.cpp new file mode 100644 index 00000000..32d083ad --- /dev/null +++ b/kate/addons/kate/tabbarextension/ktinytabbutton.cpp @@ -0,0 +1,398 @@ +/*************************************************************************** + ktinytabbutton.cpp + ------------------- + begin : 2005-06-15 + copyright : (C) 2005 by Dominik Haumann + email : dhdev@gmx.de + + Copyright (C) 2007 Flavio Castelli + ***************************************************************************/ + +/*************************************************************************** + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#include "ktinytabbutton.h" +#include "moc_ktinytabbutton.cpp" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +QColor KTinyTabButton::s_predefinedColors[] = { Qt::red, Qt::yellow, Qt::green, Qt::cyan, Qt::blue, Qt::magenta }; +const int KTinyTabButton::s_colorCount = 6; +int KTinyTabButton::s_currentColor = 0; + + +KTinyTabButton::KTinyTabButton( const QString& docurl, const QString& caption, + int button_id, bool blockContextMenu, QWidget *parent ) + : QPushButton( parent ) +{ + setFont(KGlobalSettings::toolBarFont()); + setCheckable( true ); + setFocusPolicy( Qt::NoFocus ); + setMinimumWidth( 1 ); + + if( blockContextMenu ) + setContextMenuPolicy( Qt::NoContextMenu ); + + m_buttonId = button_id; + m_tabButtonStyle = KTinyTabBar::Push; + m_highlightModifiedTab = false; + m_isPreviousTab = false; + m_highlightColor = QColor(); // set to invalid color + m_highlightActiveTab = false; + m_highlightPreviousTab = false; + m_highlightOpacity = 20; + m_modified = false; + + setIcon( QIcon() ); + setText( caption ); + setURL( docurl ); + + connect( this, SIGNAL(clicked()), this, SLOT(buttonClicked()) ); +} + +KTinyTabButton::~KTinyTabButton() +{ +} + +void KTinyTabButton::setURL( const QString& docurl ) +{ + m_url = docurl; + if( !m_url.isEmpty() ) + setToolTip( m_url ); + else + setToolTip( text() ); +} + +QString KTinyTabButton::url() const +{ + return m_url; +} + + +void KTinyTabButton::buttonClicked() +{ + // once down, stay down until another tab is activated + if( isChecked() ) + emit activated( this ); + else + setChecked( true ); +} + +void KTinyTabButton::setActivated( bool active ) +{ + if( isChecked() == active ) return; + setChecked( active ); + update(); +} + +bool KTinyTabButton::isActivated() const +{ + return isChecked(); +} + +void KTinyTabButton::paintEvent( QPaintEvent* ev ) +{ + const int opac = m_highlightOpacity; + const int comp = 100 - opac; + + QColor mix = ( highlightActiveTab() && isActivated() ) ? m_colorActiveTab : + ( ( highlightPreviousTab() && previousTab() ) ? m_colorPreviousTab : + m_highlightColor ); + QPalette pal = QApplication::palette(); + if( isModified() && highlightModifiedTabs() ) + pal.setColor( QPalette::ButtonText, modifiedTabsColor() ); + + + switch( tabButtonStyle() ) + { + case KTinyTabBar::Push: + case KTinyTabBar::Flat: + { + if( m_highlightColor.isValid() + || ( isActivated() && highlightActiveTab() ) + || ( previousTab() && highlightPreviousTab() ) ) + { + QColor col( pal.button().color() ); + col.setRed( ( col.red()*comp + mix.red()*opac ) / 100 ); + col.setGreen( ( col.green()*comp + mix.green()*opac ) / 100 ); + col.setBlue( ( col.blue()*comp + mix.blue()*opac ) / 100 ); + pal.setColor( QPalette::Button, col ); + if( tabButtonStyle() == KTinyTabBar::Flat ) + pal.setColor( QPalette::Background, col ); + } + setPalette( pal ); + QPushButton::paintEvent( ev ); + + break; + } + } +} + +void KTinyTabButton::contextMenuEvent( QContextMenuEvent* ev ) +{ + QPixmap colorIcon( 22, 22 ); + QMenu menu( /*text(),*/ this ); + QMenu* colorMenu = menu.addMenu( i18n( "&Highlight Tab" ) ); + QAction* aNone = colorMenu->addAction( i18n( "&None" ) ); + colorMenu->addSeparator(); + colorIcon.fill( Qt::red ); + QAction* aRed = colorMenu->addAction( colorIcon, i18n( "&Red" ) ); + colorIcon.fill( Qt::yellow ); + QAction* aYellow = colorMenu->addAction( colorIcon, i18n( "&Yellow" ) ); + colorIcon.fill( Qt::green ); + QAction* aGreen = colorMenu->addAction( colorIcon, i18n( "&Green" ) ); + colorIcon.fill( Qt::cyan ); + QAction* aCyan = colorMenu->addAction( colorIcon, i18n( "&Cyan" ) ); + colorIcon.fill( Qt::blue ); + QAction* aBlue = colorMenu->addAction( colorIcon, i18n( "&Blue" ) ); + colorIcon.fill( Qt::magenta ); + QAction* aMagenta = colorMenu->addAction( colorIcon, i18n( "&Magenta" ) ); + colorMenu->addSeparator(); + QAction* aCustomColor = colorMenu->addAction( + QIcon( SmallIcon( "colors" ) ), i18n( "C&ustom Color..." ) ); + menu.addSeparator(); + + QAction* aCloseTab = menu.addAction( i18n( "&Close Tab" ) ); + QAction* aCloseOtherTabs = menu.addAction( i18n( "Close &Other Tabs" ) ); + QAction* aCloseAllTabs = menu.addAction( i18n( "Close &All Tabs" ) ); + + QAction* choice = menu.exec( ev->globalPos() ); + + // process the result + if( choice == aNone ) { + if( m_highlightColor.isValid() ) + { + setHighlightColor( QColor() ); + emit highlightChanged( this ); + } + } else if( choice == aRed ) { + setHighlightColor( Qt::red ); + emit highlightChanged( this ); + } else if( choice == aYellow ) { + setHighlightColor( Qt::yellow ); + emit highlightChanged( this ); + } else if( choice == aGreen ) { + setHighlightColor( Qt::green ); + emit highlightChanged( this ); + } else if( choice == aCyan ) { + setHighlightColor( Qt::cyan ); + emit highlightChanged( this ); + } else if( choice == aBlue ) { + setHighlightColor( Qt::blue ); + emit highlightChanged( this ); + } else if( choice == aMagenta ) { + setHighlightColor( Qt::magenta ); + emit highlightChanged( this ); + } else if( choice == aCustomColor ) { + QColor newColor; + int result = KColorDialog::getColor( newColor, m_highlightColor, this ); + if ( result == KColorDialog::Accepted ) + { + setHighlightColor( newColor ); + emit highlightChanged( this ); + } + } else if( choice == aCloseTab ) { + emit closeRequest( this ); + } else if (choice == aCloseOtherTabs) { + emit closeOtherTabsRequest (this); + } else if (choice == aCloseAllTabs) { + emit closeAllTabsRequest (); + } + +} + +void KTinyTabButton::mousePressEvent( QMouseEvent* ev ) +{ + if (ev->button() == Qt::MiddleButton) { + if (ev->modifiers() & Qt::ControlModifier) { + // clear tab highlight + setHighlightColor(QColor()); + } else { + setHighlightColor(s_predefinedColors[s_currentColor]); + if (++s_currentColor >= s_colorCount) + s_currentColor = 0; + } + ev->accept(); + } else { + QPushButton::mousePressEvent(ev); + } +} + +void KTinyTabButton::setButtonID( int button_id ) +{ + m_buttonId = button_id; +} + +int KTinyTabButton::buttonID() const +{ + return m_buttonId; +} + +void KTinyTabButton::setHighlightColor( const QColor& color ) +{ + if( color.isValid() ) + { + m_highlightColor = color; + update(); + } + else if( m_highlightColor.isValid() ) + { + m_highlightColor = QColor(); + update(); + } +} + +QColor KTinyTabButton::highlightColor() const +{ + return m_highlightColor; +} + +void KTinyTabButton::setTabButtonStyle( KTinyTabBar::ButtonStyle tabStyle ) +{ + if( m_tabButtonStyle == tabStyle ) + return; + + const bool flat = tabStyle == KTinyTabBar::Flat; + setFlat( flat ); + setAutoFillBackground( flat ); + m_tabButtonStyle = tabStyle; + update(); +} + +KTinyTabBar::ButtonStyle KTinyTabButton::tabButtonStyle() const +{ + return m_tabButtonStyle; +} + +void KTinyTabButton::setPreviousTab( bool previous ) +{ + m_isPreviousTab = previous; + update(); +} + +bool KTinyTabButton::previousTab() const +{ + return m_isPreviousTab; +} + +void KTinyTabButton::setHighlightOpacity( int value ) +{ + m_highlightOpacity = value; + update(); +} + +int KTinyTabButton::highlightOpacity() const +{ + return m_highlightOpacity; +} + +void KTinyTabButton::setHighlightActiveTab( bool value ) +{ + m_highlightActiveTab = value; + update(); +} + +bool KTinyTabButton::highlightActiveTab() +{ + return m_highlightActiveTab; +} + +void KTinyTabButton::setHighlightPreviousTab( bool value ) +{ + m_highlightPreviousTab = value; + update(); +} + +bool KTinyTabButton::highlightPreviousTab() +{ + return m_highlightPreviousTab; +} + + +void KTinyTabButton::setPreviousTabColor( const QColor& color ) +{ + m_colorPreviousTab = color; + if( highlightPreviousTab() ) + update(); +} + +QColor KTinyTabButton::previousTabColor() const +{ + return m_colorPreviousTab; +} + +void KTinyTabButton::setActiveTabColor( const QColor& color ) +{ + m_colorActiveTab = color; + if( isActivated() ) + update(); +} + +QColor KTinyTabButton::activeTabColor() const +{ + return m_colorActiveTab; +} + +void KTinyTabButton::setHighlightModifiedTabs( bool highlight ) +{ + m_highlightModifiedTab = highlight; + if( isModified() ) + update(); +} + +bool KTinyTabButton::highlightModifiedTabs() const +{ + return m_highlightModifiedTab; +} + +void KTinyTabButton::setModifiedTabsColor( const QColor& color ) +{ + m_colorModifiedTab = color; + if( isModified() ) + update(); +} + +QColor KTinyTabButton::modifiedTabsColor() const +{ + return m_colorModifiedTab; +} + + +void KTinyTabButton::setModified( bool modified ) +{ + m_modified = modified; + update(); +} + +bool KTinyTabButton::isModified() const +{ + return m_modified; +} + +// kate: space-indent on; indent-width 4; tab-width 4; replace-tabs on; eol unix; diff --git a/kate/addons/kate/tabbarextension/ktinytabbutton.h b/kate/addons/kate/tabbarextension/ktinytabbutton.h new file mode 100644 index 00000000..64730f13 --- /dev/null +++ b/kate/addons/kate/tabbarextension/ktinytabbutton.h @@ -0,0 +1,242 @@ +/*************************************************************************** + ktinytabbutton.h + ------------------- + begin : 2005-06-15 + copyright : (C) 2005 by Dominik Haumann + email : dhdev@gmx.de + + Copyright (C) 2007 Flavio Castelli + ***************************************************************************/ + +/*************************************************************************** + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#ifndef TINYTABBUTTON_H +#define TINYTABBUTTON_H + +#include +#include "ktinytabbar.h" + + +/** + * A \p KTinyTabButton represents a button on the tab bar. It can either be + * \e activated or \e deactivated. If the state is \e deactivated it will + * be @e activated when the mouse is pressed. It then emits the signal + * @p activated(). The \p KTinyTabButton's caption can be set with \p setText() + * and an additional pixmap can be shown with \p setPixmap(). + * + * @author Dominik Haumann + */ +class KTinyTabButton : public QPushButton +{ +Q_OBJECT + +public: + /** + * Constructs a new tab bar button with \a caption and \a parent. If + * \e blockContextMenu is \e false the context menu will be blocked, i.e. + * hidden. If the @p docurl is unknown, pass QString(). + */ + KTinyTabButton( const QString& docurl, const QString& caption, int button_id, + bool blockContextMenu = true, QWidget *parent = 0 ); + + virtual ~KTinyTabButton(); + + /** + * Activate or deactivate the button. If the button is already down + * and \a active is \e true nothing happens, otherwise the state toggles. + * \note The signal \p activated is \e not emitted. + */ + void setActivated( bool active ); + + /** + * Check the button status. The return value is \e true, if the button is + * down. + */ + bool isActivated() const; + + /** + * Set a unique button id number. + */ + void setButtonID( int button_id ); + + /** + * Get the unique id number. + */ + int buttonID() const; + + /** + * Set the document's url to @p docurl. If unknown, pass QString(). + */ + void setURL( const QString& docurl ); + + /** + * Get the document's url. + */ + QString url() const; + + /** + * Set the highlighted state. If @p color.isValid() is \e false the + * button is not highlighted. This does \e not emit the signal + * @p highlightChanged(). + * @param color the color + */ + void setHighlightColor( const QColor& color ); + + /** + * Get the highlight color. If the button is not highlighted then the color + * is invalid, i.e. \p QColor::isValid() returns \e flase. + */ + QColor highlightColor() const; + + /** + * Set the highlight opacity to @p value. + * @param value opacity between 0..100 + */ + void setHighlightOpacity( int value ); + + /** + * Get the highlight opacity + * @return opacity + */ + int highlightOpacity() const; + + /** + * Set whether the tab was previously selected. + * @param previous truth vlaue + */ + void setPreviousTab( bool previous ); + + /** + * Check whether the tab is the previously selected tab. + * @return true, if tab was active before the current active tab + */ + bool previousTab() const; + + /** + * Set the tabbutton style. + * @param tabStyle button style + */ + void setTabButtonStyle( KTinyTabBar::ButtonStyle tabStyle ); + + /** + * Get the tabbutton style + * @return button style + */ + KTinyTabBar::ButtonStyle tabButtonStyle() const; + + /** + * Set whether an active tab should be highlighted. + */ + void setHighlightActiveTab( bool value ); + /** + * Get whether an active tab should be highlighted. + */ + bool highlightActiveTab(); + + /** + * Set whether a previous tab should be highlighted. + */ + void setHighlightPreviousTab( bool value ); + /** + * Get whether a previous tab is highlighted. + */ + bool highlightPreviousTab(); + + void setPreviousTabColor( const QColor& color ); + QColor previousTabColor() const; + + void setActiveTabColor( const QColor& color ); + QColor activeTabColor() const; + + void setHighlightModifiedTabs( bool highlight ); + bool highlightModifiedTabs() const; + + void setModifiedTabsColor( const QColor& color ); + QColor modifiedTabsColor() const; + + void setModified( bool modified ); + bool isModified() const; + +signals: + /** + * Emitted whenever the button changes state from deactivated to activated. + * @param tabbutton the pressed button (this) + */ + void activated( KTinyTabButton* tabbutton ); + + /** + * Emitted whenever the user changes the highlighted state. This can be + * done only via the context menu. + * @param tabbutton the changed button (this) + */ + void highlightChanged( KTinyTabButton* tabbutton ); + + /** + * Emitted whenever the user wants to close the tab button. + * @param tabbutton the button that emitted this signal + */ + void closeRequest( KTinyTabButton* tabbutton ); + + /** + * Emitted whenever the user wants to close all the tab button except the + * selected one. + * @param tabbutton the button that emitted this signal + */ + void closeOtherTabsRequest( KTinyTabButton* tabbutton ); + + /** + * Emitted whenever the user wants to close all the tabs. + */ + void closeAllTabsRequest(); + +protected slots: + void buttonClicked(); + +protected: + /** paint eyecandy rectangles around the button */ + virtual void paintEvent( QPaintEvent* ev ); + /** support for context menu */ + virtual void contextMenuEvent( QContextMenuEvent* ev ); + /** middle mouse button changes color */ + virtual void mousePressEvent( QMouseEvent* ev ); + + +private: + QString m_url; + int m_buttonId; + bool m_modified; + bool m_highlightModifiedTab; + bool m_highlightActiveTab; + bool m_highlightPreviousTab; + bool m_isPreviousTab; + + QColor m_colorModifiedTab; + QColor m_colorActiveTab; + QColor m_colorPreviousTab; + + QColor m_highlightColor; + KTinyTabBar::ButtonStyle m_tabButtonStyle; + int m_highlightOpacity; + + static QColor s_predefinedColors[6]; + static const int s_colorCount; + static int s_currentColor; +}; + +#endif + +// kate: space-indent on; indent-width 4; tab-width 4; replace-tabs on; eol unix; diff --git a/kate/addons/kate/tabbarextension/plugin_katetabbarextension.cpp b/kate/addons/kate/tabbarextension/plugin_katetabbarextension.cpp new file mode 100644 index 00000000..316b1e76 --- /dev/null +++ b/kate/addons/kate/tabbarextension/plugin_katetabbarextension.cpp @@ -0,0 +1,293 @@ +/*************************************************************************** + plugin_katetabbarextension.cpp + ------------------- + begin : 2004-04-20 + copyright : (C) 2004-2005 by Dominik Haumann + email : dhdev@gmx.de + ***************************************************************************/ + +/*************************************************************************** + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + ***************************************************************************/ + + +//BEGIN INCLUDES +#include "plugin_katetabbarextension.h" +#include "ktinytabbar.h" + +#include +#include + +#include +#include +#include +#include + +#include + +#include +//END + + +K_PLUGIN_FACTORY(KateTabBarExtensionFactory, registerPlugin();) +K_EXPORT_PLUGIN(KateTabBarExtensionFactory(KAboutData("katetabbarextension","katetabbarextension",ki18n("TabBarExtension"), "0.1", ki18n("TabBar extension"), KAboutData::License_LGPL_V2)) ) + + +//BEGIN PluginView +PluginView::PluginView( Kate::MainWindow* mainwindow ) + : Kate::PluginView( mainwindow ) +{ + tabbar = new KTinyTabBar( mainWindow()->centralWidget() ); + + QBoxLayout* layout = qobject_cast(mainWindow()->centralWidget()->layout()); + layout->insertWidget( 0, tabbar ); + + connect( Kate::application()->documentManager(), SIGNAL(documentCreated(KTextEditor::Document*)), + this, SLOT(slotDocumentCreated(KTextEditor::Document*)) ); + connect( Kate::application()->documentManager(), SIGNAL(documentDeleted(KTextEditor::Document*)), + this, SLOT(slotDocumentDeleted(KTextEditor::Document*)) ); + connect( mainWindow(), SIGNAL(viewChanged()), + this, SLOT(slotViewChanged()) ); + + connect( tabbar, SIGNAL(currentChanged(int)), + this, SLOT(currentTabChanged(int)) ); + connect( tabbar, SIGNAL(closeRequest(int)), + this, SLOT(closeTabRequest(int)) ); + + // add already existing documents + foreach( KTextEditor::Document* doc, Kate::application()->documentManager()->documents() ) + slotDocumentCreated( doc ); +} + +PluginView::~PluginView() +{ + delete tabbar; +} + +void PluginView::readSessionConfig (KConfigBase* config, const QString& groupPrefix) +{ + tabbar->load( config, groupPrefix + ":view" ); + updateLocation(); +} + +void PluginView::writeSessionConfig (KConfigBase* config, const QString& groupPrefix) +{ + tabbar->save( config, groupPrefix + ":view" ); +} + +void PluginView::updateLocation() +{ + QBoxLayout* layout = qobject_cast(mainWindow()->centralWidget()->layout()); + if( !layout ) return; + + layout->removeWidget( tabbar ); + layout->insertWidget( tabbar->locationTop()?0:-1, tabbar ); +} + + +void PluginView::currentTabChanged( int button_id ) +{ + mainWindow()->activateView( id2doc[button_id] ); +} + +void PluginView::closeTabRequest( int button_id ) +{ + Kate::application()->documentManager()->closeDocument( id2doc[button_id] ); +} + +void PluginView::slotDocumentCreated( KTextEditor::Document* document ) +{ + if( !document ) + return; + + connect( document, SIGNAL(modifiedChanged(KTextEditor::Document*)), + this, SLOT(slotDocumentChanged(KTextEditor::Document*)) ); + connect( document, SIGNAL( modifiedOnDisk( KTextEditor::Document*, bool, + KTextEditor::ModificationInterface::ModifiedOnDiskReason ) ), + this, SLOT( slotModifiedOnDisc( KTextEditor::Document*, bool, + KTextEditor::ModificationInterface::ModifiedOnDiskReason ) ) ); + connect( document, SIGNAL(documentNameChanged(KTextEditor::Document*)), + this, SLOT(slotNameChanged(KTextEditor::Document*)) ); + + + int tabID = tabbar->addTab( document->url().prettyUrl(), document->documentName() ); + id2doc[tabID] = document; + doc2id[document] = tabID; +} + +void PluginView::slotDocumentDeleted( KTextEditor::Document* document ) +{ + // kDebug() << "slotDocumentDeleted "; + int tabID = doc2id[document]; + + tabbar->removeTab( tabID ); + doc2id.remove( document ); + id2doc.remove( tabID ); +} + +void PluginView::slotViewChanged() +{ + KTextEditor::View *view = mainWindow()->activeView(); + if( !view ) + return; + + int tabID = doc2id[view->document()]; + tabbar->setCurrentTab( tabID ); +} + +void PluginView::slotDocumentChanged( KTextEditor::Document* document ) +{ + if( !document ) + return; + + int tabID = doc2id[document]; + if( document->isModified() ) + tabbar->setTabIcon( tabID, KIconLoader::global() + ->loadIcon( "document-save", KIconLoader::Small, 16 ) ); + else + tabbar->setTabIcon( tabID, QIcon() ); + + tabbar->setTabModified( tabID, document->isModified() ); +} + +void PluginView::slotNameChanged( KTextEditor::Document* document ) +{ + if( !document ) + return; + + int tabID = doc2id[document]; + tabbar->setTabText( tabID, document->documentName() ); + if( document->url().prettyUrl() != tabbar->tabURL( tabID ) ) + tabbar->setTabURL( tabID, document->url().prettyUrl() ); +} + +void PluginView::slotModifiedOnDisc( KTextEditor::Document* document, bool modified, + KTextEditor::ModificationInterface::ModifiedOnDiskReason reason ) +{ + kDebug() << "modified: " << modified << ", id: " << reason; + int tabID = doc2id[document]; + if( !modified ) + { + tabbar->setTabIcon( tabID, QIcon() ); + tabbar->setTabModified( tabID, false ); + } + else + { + // NOTE: right now the size 16 pixel is hard coded. If I omit the size + // parameter it would use KDE defaults. So if a user requests standard + // sized icons (because he changed his KDE defaults) the solution is to + // omit the parameter. + switch( reason ) + { + case KTextEditor::ModificationInterface::OnDiskModified: + tabbar->setTabIcon( tabID, KIconLoader::global() + ->loadIcon( "dialog-warning", KIconLoader::Small, 16 ) ); + break; + case KTextEditor::ModificationInterface::OnDiskCreated: + tabbar->setTabIcon( tabID, KIconLoader::global() + ->loadIcon( "document-save", KIconLoader::Small, 16 ) ); + break; + case KTextEditor::ModificationInterface::OnDiskDeleted: + tabbar->setTabIcon( tabID, KIconLoader::global() + ->loadIcon( "dialog-warning", KIconLoader::Small, 16 ) ); + break; + default: + tabbar->setTabIcon( tabID, KIconLoader::global() + ->loadIcon( "dialog-warning", KIconLoader::Small, 16 ) ); + } + + tabbar->setTabModified( tabID, true ); + } +} +//END PluginView + + + +//BEGIN KatePluginTabBarExtension +KatePluginTabBarExtension::KatePluginTabBarExtension( + QObject* parent, const QList& ) + : Kate::Plugin ( (Kate::Application*)parent) +{ +} + +KatePluginTabBarExtension::~KatePluginTabBarExtension() +{ +} + +Kate::PluginView *KatePluginTabBarExtension::createView (Kate::MainWindow *mainWindow) +{ + PluginView *view = new PluginView( mainWindow ); + connect( view->tabbar, SIGNAL(settingsChanged(KTinyTabBar*)), + this, SLOT(tabbarSettingsChanged(KTinyTabBar*)) ); + connect( view->tabbar, SIGNAL(highlightMarksChanged(KTinyTabBar*)), + this, SLOT(tabbarHighlightMarksChanged(KTinyTabBar*)) ); + m_views.append( view ); + return view; +} + + +void KatePluginTabBarExtension::readSessionConfig (KConfigBase* /*config*/, const QString& /*groupPrefix*/) +{ +} + +void KatePluginTabBarExtension::writeSessionConfig (KConfigBase* /*config*/, const QString& /*groupPrefix*/) +{ +} + +void KatePluginTabBarExtension::tabbarHighlightMarksChanged( KTinyTabBar* tabbar ) +{ + // synchronize all tabbars + foreach( PluginView* view, m_views ) + { + view->updateLocation(); + if( view->tabbar != tabbar ) + { + view->tabbar->setHighlightMarks( tabbar->highlightMarks() ); + } + } +} + +void KatePluginTabBarExtension::tabbarSettingsChanged( KTinyTabBar* tabbar ) +{ + // synchronize all tabbars + foreach( PluginView* view, m_views ) + { + view->updateLocation(); + if( view->tabbar != tabbar ) + { + view->tabbar->setLocationTop( tabbar->locationTop() ); + view->updateLocation(); + view->tabbar->setNumRows( tabbar->numRows() ); + view->tabbar->setMinimumTabWidth( tabbar->minimumTabWidth() ); + view->tabbar->setMaximumTabWidth( tabbar->maximumTabWidth() ); + view->tabbar->setTabHeight( tabbar->tabHeight() ); + view->tabbar->setTabButtonStyle( tabbar->tabButtonStyle() ); + view->tabbar->setFollowCurrentTab( tabbar->followCurrentTab() ); + view->tabbar->setTabSortType( tabbar->tabSortType() ); + view->tabbar->setHighlightModifiedTabs( tabbar->highlightModifiedTabs() ); + view->tabbar->setHighlightActiveTab( tabbar->highlightActiveTab() ); + view->tabbar->setHighlightPreviousTab( tabbar->highlightPreviousTab() ); + view->tabbar->setHighlightOpacity( tabbar->highlightOpacity() ); + view->tabbar->setModifiedTabsColor( tabbar->modifiedTabsColor() ); + view->tabbar->setActiveTabColor( tabbar->activeTabColor() ); + view->tabbar->setPreviousTabColor( tabbar->previousTabColor() ); + } + } +} +//END KatePluginTabBarExtension + +#include "moc_plugin_katetabbarextension.cpp" + +// kate: space-indent on; indent-width 2; tab-width 4; replace-tabs off; eol unix; diff --git a/kate/addons/kate/tabbarextension/plugin_katetabbarextension.h b/kate/addons/kate/tabbarextension/plugin_katetabbarextension.h new file mode 100644 index 00000000..7c0fc700 --- /dev/null +++ b/kate/addons/kate/tabbarextension/plugin_katetabbarextension.h @@ -0,0 +1,99 @@ +/*************************************************************************** + plugin_katetabbarextension.h + ------------------- + begin : 2004-04-20 + copyright : (C) 2004-2005 by Dominik Haumann + email : dhdev@gmx.de + ***************************************************************************/ + +/*************************************************************************** + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#ifndef PLUGIN_KATETABBAREXTENSION_H +#define PLUGIN_KATETABBAREXTENSION_H + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include + +class KateTabBarExtension; +class KTinyTabBar; + +class PluginView : public Kate::PluginView +{ + Q_OBJECT + friend class KatePluginTabBarExtension; + +public: + PluginView( Kate::MainWindow* mainwindow ); + virtual ~PluginView(); + + void updateLocation(); + + void readSessionConfig (KConfigBase* config, const QString& groupPrefix); + void writeSessionConfig (KConfigBase* config, const QString& groupPrefix); + +public slots: + void currentTabChanged( int button_id ); + void closeTabRequest( int button_id ); + void slotDocumentCreated( KTextEditor::Document* document ); + void slotDocumentDeleted( KTextEditor::Document* document ); + void slotViewChanged(); + void slotDocumentChanged( KTextEditor::Document* ); + void slotModifiedOnDisc( KTextEditor::Document* document, bool modified, + KTextEditor::ModificationInterface::ModifiedOnDiskReason reason ); + void slotNameChanged( KTextEditor::Document* document ); + +private: + KTinyTabBar* tabbar; + QMap id2doc; + QMap doc2id; +}; + +class KatePluginTabBarExtension : public Kate::Plugin +{ + Q_OBJECT + + public: + explicit KatePluginTabBarExtension( QObject* parent = 0, const QList& = QList() ); + virtual ~KatePluginTabBarExtension(); + + Kate::PluginView *createView (Kate::MainWindow *mainWindow); + + void readSessionConfig (KConfigBase* config, const QString& groupPrefix); + void writeSessionConfig (KConfigBase* config, const QString& groupPrefix); + + protected slots: + void tabbarSettingsChanged( KTinyTabBar* tabbar ); + void tabbarHighlightMarksChanged( KTinyTabBar* tabbar ); + + private: + QList m_views; +}; + +#endif // PLUGIN_KATETABBAREXTENSION_H diff --git a/kate/addons/kate/tabbarextension/tabbarconfigwidget.ui b/kate/addons/kate/tabbarextension/tabbarconfigwidget.ui new file mode 100644 index 00000000..42791e2d --- /dev/null +++ b/kate/addons/kate/tabbarextension/tabbarconfigwidget.ui @@ -0,0 +1,496 @@ + + TabBarConfigWidget + + + + + + Behavior + + + + + + Location: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + Top + + + + + Bottom + + + + + + + + Qt::Horizontal + + + QSizePolicy::MinimumExpanding + + + + 98 + 53 + + + + + + + + Rows: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + rows + + + 1 + + + 10 + + + + + + + Sorting: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + Keep activated tab visible + + + + + + + Qt::Horizontal + + + QSizePolicy::MinimumExpanding + + + + 0 + 28 + + + + + + + + + + + + + Opening Order + + + + + Document Name + + + + + Document URL + + + + + File Extension + + + + + + + + Qt::Horizontal + + + QSizePolicy::MinimumExpanding + + + + 0 + 17 + + + + + + + + + + + + + Tabs + + + + + + Minimum width: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + pixels + + + 20 + + + 500 + + + + + + + Qt::Horizontal + + + + 11 + 96 + + + + + + + + Maximum width: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + 20 pixels + + + pixels + + + 20 + + + 500 + + + + + + + Height: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + 16 pixels + + + pixels + + + 16 + + + 60 + + + + + + + Style: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + Buttons + + + + + Flat + + + + + + + + + + + Highlighting + + + + + + + + + + false + + + + + + + Highlight modified tabs + + + + + + + false + + + + + + + Highlight active tab + + + + + + + false + + + + + + + Highlight previous tab + + + + + + + Opacity: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + 100 + + + Qt::Horizontal + + + + + + + + + Qt::Horizontal + + + + 241 + 108 + + + + + + + + + + + + Note: Use the context menu to highlight a tab + + + + + + + Qt::Horizontal + + + + 0 + 13 + + + + + + + + Remove all highlight marks in the current session. + + + Clear Highlight Cache + + + + + + + + + + + + Preview + + + + + + + + KColorButton + QPushButton +
kcolorbutton.h
+
+
+ + cmbLocation + sbRows + chkFollowActive + cmbSorting + cmbStyle + sbMinWidth + sbMaxWidth + sbHeight + chkModified + colModified + chkActive + colActive + chkPrevious + colPrevious + slOpacity + btnClearCache + + + + + chkModified + toggled(bool) + colModified + setEnabled(bool) + + + 111 + 237 + + + 50 + 238 + + + + + chkActive + toggled(bool) + colActive + setEnabled(bool) + + + 96 + 273 + + + 55 + 268 + + + + + chkPrevious + toggled(bool) + colPrevious + setEnabled(bool) + + + 84 + 297 + + + 60 + 303 + + + + +
diff --git a/kate/addons/kate/tabbarextension/ui.rc b/kate/addons/kate/tabbarextension/ui.rc new file mode 100644 index 00000000..953a8121 --- /dev/null +++ b/kate/addons/kate/tabbarextension/ui.rc @@ -0,0 +1,7 @@ + + + + Tab Bar Extension + + + diff --git a/kate/addons/kate/tabify/CMakeLists.txt b/kate/addons/kate/tabify/CMakeLists.txt new file mode 100644 index 00000000..edb45c00 --- /dev/null +++ b/kate/addons/kate/tabify/CMakeLists.txt @@ -0,0 +1,22 @@ +kde4_add_plugin(katetabifyplugin tabify.cpp) + +target_link_libraries(katetabifyplugin + ${KDE4_KDEUI_LIBS} + ${KDE4_KPARTS_LIBS} + kateinterfaces +) + +install( + TARGETS katetabifyplugin + DESTINATION ${KDE4_PLUGIN_INSTALL_DIR} +) + +####################### install files ######################################### +install( + FILES ui.rc + DESTINATION ${KDE4_DATA_INSTALL_DIR}/kate/plugins/katetabify +) +install( + FILES katetabifyplugin.desktop + DESTINATION ${KDE4_SERVICES_INSTALL_DIR} +) diff --git a/kate/addons/kate/tabify/Messages.sh b/kate/addons/kate/tabify/Messages.sh new file mode 100644 index 00000000..de3a8885 --- /dev/null +++ b/kate/addons/kate/tabify/Messages.sh @@ -0,0 +1,3 @@ +#! /bin/sh +$EXTRACTRC *.rc >> rc.cpp +$XGETTEXT *.cpp -o $podir/katetabifyplugin.pot diff --git a/kate/addons/kate/tabify/katetabifyplugin.desktop b/kate/addons/kate/tabify/katetabifyplugin.desktop new file mode 100644 index 00000000..38211961 --- /dev/null +++ b/kate/addons/kate/tabify/katetabifyplugin.desktop @@ -0,0 +1,117 @@ +[Desktop Entry] +X-Kate-Version=2.8 +X-KDE-Library=katetabifyplugin +ServiceTypes=Kate/Plugin +Type=Service +Icon=korganizer +Name=Tab Bar +Name[ar]=شريط الألسنة +Name[ast]=Barra de llingüetes +Name[bg]=Лента с подпрозорци +Name[bs]=Traka kartica +Name[ca]=Barra de pestanyes +Name[ca@valencia]=Barra de pestanyes +Name[cs]=Lišta karet +Name[da]=Fanebladslinje +Name[de]=Unterfensterleiste +Name[el]=Γραμμή καρτελών +Name[en_GB]=Tab Bar +Name[es]=Barra de pestañas +Name[et]=Kaardiriba +Name[eu]=Fitxa-barra +Name[fi]=Välilehtipalkki +Name[fr]=Barre d'onglets +Name[ga]=Barra Cluaisíní +Name[gl]=Barra de lapelas +Name[he]=תצוגת כרטיסיות +Name[hu]=Lapozósáv +Name[ia]=Barra de Scheda +Name[it]=Barra di schede +Name[ja]=タブバー +Name[kk]=Қойынды жиегі +Name[km]=របារ​ផ្ទាំង​ +Name[ko]=탭 표시줄 +Name[lt]=Kortelių juosta +Name[lv]=Ciļņu josla +Name[mai]=टैब पट्टी +Name[mr]=टॅब पट्टी +Name[nb]=Fanelinje +Name[nds]=Paneelbalken +Name[nl]=Tabbladbalk +Name[nn]=Fanelinje +Name[pa]=ਟੈਬ ਬਾਰ +Name[pl]=Pasek kart +Name[pt]=Barra de Páginas +Name[pt_BR]=Barra de abas +Name[ro]=Bară de file +Name[ru]=Панель вкладок +Name[si]=ටැබ් තීරුව +Name[sk]=Panel kariet +Name[sl]=Vrstica z zavihki +Name[sr]=Трака језичака +Name[sr@ijekavian]=Трака језичака +Name[sr@ijekavianlatin]=Traka jezičaka +Name[sr@latin]=Traka jezičaka +Name[sv]=Flikrad +Name[tg]=Панели замима +Name[tr]=Sekme Çubuğu +Name[ug]=بەتكۈچ بالداق +Name[uk]=Панель вкладок +Name[x-test]=xxTab Barxx +Name[zh_CN]=标签栏 +Name[zh_TW]=分頁列 +Comment=Adds a standard tab bar to Kate's main window +Comment[ar]=يضيف شريط ألسنة قياسي إلى نافذة كيت الرئيسية +Comment[bg]=Добавяне на стандартна лента с подпрозорци към основния прозорец на Kate +Comment[bs]=Dodaje traku sa karticama u glavni prozor Kate +Comment[ca]=Afegeix una barra de pestanyes estàndard a la finestra principal del Kate +Comment[ca@valencia]=Afig una barra de pestanyes estàndard a la finestra principal del Kate +Comment[cs]=Přidá do hlavního okna Kate standardní pruh karet +Comment[da]=Føjer en standard fanebladslinje til Kates hovedvindue +Comment[de]=Fügt dem Hauptfenster von Kate eine normale Unterfensterleiste hinzu +Comment[el]=Προσθέτει μια τυπική γραμμή καρτελών στο κύριο παράθυρο του Kate +Comment[en_GB]=Adds a standard tab bar to Kate's main window +Comment[es]=Añade una barra de pestañas estándar a la ventana principal de Kate +Comment[et]=Lisab Kate peaaknasse standardse kaardiriba +Comment[eu]=Fitxa-barra estandarra gehitzen dio Kate-ren leiho nagusiari +Comment[fi]=Lisää standardin välilehtipalkin Katen pääikkunaan +Comment[fr]=Ajoute une barre standard d'onglets à la fenêtre principale de Kate +Comment[ga]=Cuir barra cluaisíní caighdeánach le príomhfhuinneog Kate +Comment[gl]=Engade unha barra con lapelas á xanela principal de Kate +Comment[he]=מוסיף תצוגת כרטיסיות רגילה אל החלון הראשי של Kate +Comment[hu]=Lapozósáv hozzáadása a Kate főablakához +Comment[ia]=Adde un barra de scheda standard a fenestra principal de Kate +Comment[it]=Aggiunge una barra di schede standard alla finestra principale di Kate +Comment[ja]=Kate のメインウィンドウに標準のタブバーを追加します +Comment[kk]=Kate-тің негізгі терезесіне стандартты қойындысын қосу +Comment[km]=បន្ថែម​របារ​​ផ្ទាំង​ទៅ​​ស្តង់ដារ​ទៅ​បង្អួច​មេ​របស់ Kate +Comment[ko]=Kate의 주 창에 탭 표시줄 추가 +Comment[lt]=Įdeda standartinę kortelių juostą į Kate langą +Comment[lv]=Pievieno standarta ciļņu joslu Kate galvenajam logam +Comment[mr]=केटच्या मुख्य चौकटीत प्रमाणित टॅब पट्टी जोडतो +Comment[nb]=Legger til en standard fanelinje i Kates hovedvindu +Comment[nds]=Föögt Kate sien Hööftfinster en Standard-Paneelbalken to +Comment[nl]=Voegt een standaard tabbladbalk toe aan Kate's hoofdvenster +Comment[nn]=Legg ei fanelinje til hovudvindauget i Kate +Comment[pa]=ਕੇਟ ਦੀ ਮੇਨ ਵਿੰਡੋ ਵਿੱਚ ਸਟੈਂਡਰਡ ਟੈਬ ਬਾਰ ਸ਼ਾਮਲ ਕਰੋ +Comment[pl]=Dodaje standardowy pasek kart do głównego okna Kate +Comment[pt]=Adiciona uma barra de páginas normal à janela principal do Kate +Comment[pt_BR]=Adiciona uma barra de abas padrão à janela principal do Kate +Comment[ro]=Adaugă o bară de file standard la fereastra principală Kate +Comment[ru]=Добавляет стандартную панель вкладок в окно Kate +Comment[si]=Kate හි ප්‍රධාන කවුළුවට සම්මත ටැබ් තීරුව එක්කරයි +Comment[sk]=Pridá štandardný panel kariet na hlavné okno Kate +Comment[sl]=Doda vrstico z zavihki v glavno okno urejevalnika Kate +Comment[sr]=Додаје уобичајену траку језичака у главни прозор Кејт +Comment[sr@ijekavian]=Додаје уобичајену траку језичака у главни прозор Кејт +Comment[sr@ijekavianlatin]=Dodaje uobičajenu traku jezičaka u glavni prozor Kate +Comment[sr@latin]=Dodaje uobičajenu traku jezičaka u glavni prozor Kate +Comment[sv]=Lägger till en vanlig flikrad i Kates huvudfönster +Comment[tg]=Панели стандартиро ба тирезаи асосии барномаи Kate илова мекунад +Comment[tr]=Kate uygulamasının ana penceresine standart bir sekme çubuğu ekler +Comment[ug]=ئۆلچەملىك بەتكۈچ بالداقتىن بىرنى Kate نىڭ ئاساسىي كۆزنىكىگە قوشىدۇ +Comment[uk]=Додає стандартну смужку вкладок до головного вікна Kate +Comment[x-test]=xxAdds a standard tab bar to Kate's main windowxx +Comment[zh_CN]=在 Kate 主窗口上添加一个标准标签栏 +Comment[zh_TW]=在 Kate 主視窗中加入標準分頁列 +author=Abhishek Patil, abhishekworld@gmail.com diff --git a/kate/addons/kate/tabify/tabify.cpp b/kate/addons/kate/tabify/tabify.cpp new file mode 100644 index 00000000..81eed3c1 --- /dev/null +++ b/kate/addons/kate/tabify/tabify.cpp @@ -0,0 +1,267 @@ +/*************************************************************************** +* Copyright (C) 2010 by Abhishek Patil * +* * +* This program is free software; you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation; either version 2 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program; if not, write to the * +* Free Software Foundation, Inc., * +* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * +***************************************************************************/ +#include "tabify.h" +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +K_PLUGIN_FACTORY(TabBarFactory, registerPlugin();) +K_EXPORT_PLUGIN(TabBarFactory(KAboutData("tabifyplugin", "katetabifyplugin", + ki18n("TabifyPlugin"), "0.1", ki18n("Tabify Plugin"), KAboutData::License_LGPL_V2))) + +/////////////////////////////////////////////////////////////////////////////// +// TabBarPluginView +/////////////////////////////////////////////////////////////////////////////// +TabBarPluginView::TabBarPluginView(Kate::MainWindow* mainwindow) + : Kate::PluginView(mainwindow) +{ + m_tabBar = new KTabBar(mainWindow()->centralWidget()); + KAcceleratorManager::setNoAccel(m_tabBar); + + m_tabIsDeleting = false; + + m_tabBar->setTabsClosable(true); + m_tabBar->setDocumentMode(true); + m_tabBar->setMovable(true); + + QBoxLayout* layout = qobject_cast(mainWindow()->centralWidget()->layout()); + layout->insertWidget(0, m_tabBar); + + connect(Kate::application()->documentManager(), SIGNAL(documentCreated(KTextEditor::Document*)), + this, SLOT(slotDocumentCreated(KTextEditor::Document*))); + connect(Kate::application()->documentManager(), SIGNAL(documentDeleted(KTextEditor::Document*)), + this, SLOT(slotDocumentDeleted(KTextEditor::Document*))); + connect(mainWindow(), SIGNAL(viewChanged()), + this, SLOT(slotViewChanged())); + + connect(m_tabBar, SIGNAL(currentChanged(int)), this, SLOT(slotTabChanged(int))); + connect(m_tabBar, SIGNAL(tabCloseRequested(int)), this, SLOT(slotTabCloseRequest(int))); + connect(m_tabBar, SIGNAL(mouseMiddleClick(int)), this, SLOT(slotMiddleMouseButtonPressed(int))); + connect(m_tabBar, SIGNAL(wheelDelta(int)), this, SLOT(slotWheelDelta(int))); + connect(m_tabBar, SIGNAL(tabMoved(int,int)), this, SLOT(slotTabMoved(int,int))); + + foreach(KTextEditor::Document* document, Kate::application()->documentManager()->documents()) { + slotDocumentCreated(document); + } +} + +TabBarPluginView::~TabBarPluginView() +{ + delete m_tabBar; +} + +void TabBarPluginView::slotDocumentCreated(KTextEditor::Document* document) +{ + if (!document) + return; + + connect(document, SIGNAL(modifiedChanged(KTextEditor::Document*)), + this, SLOT(slotDocumentChanged(KTextEditor::Document*))); + connect(document, SIGNAL(modifiedOnDisk(KTextEditor::Document*, bool, + KTextEditor::ModificationInterface::ModifiedOnDiskReason)), + this, SLOT(slotModifiedOnDisc(KTextEditor::Document*, bool, + KTextEditor::ModificationInterface::ModifiedOnDiskReason))); + connect(document, SIGNAL(documentNameChanged(KTextEditor::Document*)), + this, SLOT(slotNameChanged(KTextEditor::Document*))); + + int index = m_tabBar->addTab(document->documentName()); + m_tabBar->setTabToolTip(index, document->url().pathOrUrl()); + m_tabDocMap[index] = document; + m_docTabMap[document] = index; + m_docList.append(document); + m_modifiedMap[document] = false; +} + +void TabBarPluginView::slotTabChanged(int index) +{ + if (m_tabIsDeleting) { + return; + } + + mainWindow()->activateView(m_tabDocMap[index]); +} + +void TabBarPluginView::slotDocumentDeleted(KTextEditor::Document* document) +{ + const int index = m_docTabMap[document]; + m_docTabMap.remove(document); + m_tabDocMap.remove(index); + m_modifiedMap.remove(document); + m_docList.removeAll(document); + + m_tabIsDeleting = true; + m_tabBar->removeTab(index); + m_tabIsDeleting = false; + + // Rebuild the maps using the new state of the list. + rebuildMaps(); +} + +void TabBarPluginView::slotViewChanged() +{ + if (m_tabIsDeleting) { + return; + } + + KTextEditor::View* view = mainWindow()->activeView(); + if (!view) { + return; + } + + int tabID = m_docTabMap[view->document()]; + m_tabBar->setCurrentIndex(tabID); +} + +void TabBarPluginView::slotMiddleMouseButtonPressed(int tabId) +{ + // only close by middle mouse button, if the document is not externally + // modified. Avoids a non-trivial crash: bug #299744 + if (!m_modifiedMap[m_tabDocMap[tabId]]) { + slotTabCloseRequest(tabId); + } +} + +void TabBarPluginView::slotTabCloseRequest(int tabId) +{ + Kate::application()->documentManager()->closeDocument(m_tabDocMap[tabId]); +} + +void TabBarPluginView::slotDocumentChanged(KTextEditor::Document* document) +{ + + int tabID = m_docTabMap[document]; + if (document->isModified()) { + m_tabBar->setTabIcon(tabID, KIconLoader::global() + ->loadIcon("document-save", KIconLoader::Small, 16)); + } else { + m_tabBar->setTabIcon(tabID, QIcon()); + } +} + +void TabBarPluginView::slotModifiedOnDisc(KTextEditor::Document* document, bool modified, + KTextEditor::ModificationInterface::ModifiedOnDiskReason reason) +{ + int tabID = m_docTabMap[document]; + m_modifiedMap[document] = modified; + + if (!modified) { + m_tabBar->setTabIcon(tabID, QIcon()); + } else { + switch (reason) { + case KTextEditor::ModificationInterface::OnDiskModified: + m_tabBar->setTabIcon(tabID, KIconLoader::global() + ->loadIcon("dialog-warning", KIconLoader::Small)); + break; + case KTextEditor::ModificationInterface::OnDiskCreated: + m_tabBar->setTabIcon(tabID, KIconLoader::global() + ->loadIcon("document-save", KIconLoader::Small)); + break; + case KTextEditor::ModificationInterface::OnDiskDeleted: + m_tabBar->setTabIcon(tabID, KIconLoader::global() + ->loadIcon("dialog-warning", KIconLoader::Small)); + default: + m_tabBar->setTabIcon(tabID, KIconLoader::global() + ->loadIcon("dialog-warning", KIconLoader::Small)); + } + } +} + +void TabBarPluginView::slotNameChanged(KTextEditor::Document* document) +{ + + if (!document) { + return; + } + + int tabID = m_docTabMap[document]; + m_tabBar->setTabText(tabID, document->documentName()); + m_tabBar->setTabToolTip(tabID, document->url().pathOrUrl()); +} + +void TabBarPluginView::slotWheelDelta(int delta) +{ + if (m_tabBar->count() < 2) { + return; + } + + int page = m_tabBar->currentIndex(); + if (delta < 0) { + page = (page + 1) % m_tabBar->count(); + } else { + page --; + } + + if (page < 0) { + page = m_tabBar->count() - 1; + } + + m_tabBar->setCurrentIndex(page); +} + +void TabBarPluginView::slotTabMoved(int from, int to) +{ + KTextEditor::Document* document = m_docList.takeAt(from); + m_docList.insert(to, document); + rebuildMaps(); +} + +void TabBarPluginView::rebuildMaps() { + m_tabDocMap.clear(); + m_docTabMap.clear(); + + for (int i = 0; i < m_docList.count(); i++) { + KTextEditor::Document* document = m_docList.at(i); + //m_tabBar->setTabToolTip(i, document->url().pathOrUrl()); + m_tabDocMap[i] = document; + m_docTabMap[document] = i; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// TabBarPlugin +/////////////////////////////////////////////////////////////////////////////// +TabBarPlugin::TabBarPlugin(QObject* parent , const QList&) + : Kate::Plugin((Kate::Application*)parent) +{ +} + +TabBarPlugin::~TabBarPlugin() +{ +} + +Kate::PluginView *TabBarPlugin::createView(Kate::MainWindow *mainWindow) +{ + TabBarPluginView *view = new TabBarPluginView(mainWindow); + return view; +} + diff --git a/kate/addons/kate/tabify/tabify.h b/kate/addons/kate/tabify/tabify.h new file mode 100644 index 00000000..a10b9a55 --- /dev/null +++ b/kate/addons/kate/tabify/tabify.h @@ -0,0 +1,87 @@ +/*************************************************************************** + * Copyright (C) 2010 by Abhishek Patil * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ +#ifndef TABIFY_H +#define TABIFY_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +class TabBarPluginView + : public Kate::PluginView +{ + Q_OBJECT + +public: + explicit TabBarPluginView(Kate::MainWindow* mainwindow); + virtual ~TabBarPluginView(); + +public slots: + void slotDocumentCreated(KTextEditor::Document* doc); + void slotTabChanged(int); + void slotDocumentDeleted(KTextEditor::Document*); + void slotViewChanged(); + void slotMiddleMouseButtonPressed(int); + void slotTabCloseRequest(int); + void slotDocumentChanged(KTextEditor::Document*); + void slotModifiedOnDisc(KTextEditor::Document*, bool, + KTextEditor::ModificationInterface::ModifiedOnDiskReason); + void slotNameChanged(KTextEditor::Document*); + void slotWheelDelta(int); + void slotTabMoved(int, int); + +private: + KTabBar* m_tabBar; + QMap m_tabDocMap; + QMap m_docTabMap; + QList m_docList; + QMap m_modifiedMap; + bool m_tabIsDeleting; + void rebuildMaps(); +}; + +class TabBarPlugin + : public Kate::Plugin +{ + Q_OBJECT + + +public: + explicit TabBarPlugin(QObject* parent = 0, const QList& = QList()); + virtual ~TabBarPlugin(); + + Kate::PluginView *createView(Kate::MainWindow *mainWindow); + +private: + QList m_views; +}; + +#endif // TABIFY_H diff --git a/kate/addons/kate/tabify/ui.rc b/kate/addons/kate/tabify/ui.rc new file mode 100644 index 00000000..183150e2 --- /dev/null +++ b/kate/addons/kate/tabify/ui.rc @@ -0,0 +1,7 @@ + + + + Tabify Plugin + + + diff --git a/kate/addons/kate/textfilter/CMakeLists.txt b/kate/addons/kate/textfilter/CMakeLists.txt new file mode 100644 index 00000000..791df4b1 --- /dev/null +++ b/kate/addons/kate/textfilter/CMakeLists.txt @@ -0,0 +1,25 @@ +########### next target ############### + +kde4_add_plugin(katetextfilterplugin plugin_katetextfilter.cpp) + +target_link_libraries(katetextfilterplugin + ${KDE4_KDEUI_LIBS} + ${KDE4_KTEXTEDITOR_LIBS} + kateinterfaces +) + +install( + TARGETS katetextfilterplugin + DESTINATION ${KDE4_PLUGIN_INSTALL_DIR} +) + +########### install files ############### + +install( + FILES ui.rc + DESTINATION ${KDE4_DATA_INSTALL_DIR}/kate/plugins/katetextfilter +) +install( + FILES katetextfilter.desktop + DESTINATION ${KDE4_SERVICES_INSTALL_DIR} +) diff --git a/kate/addons/kate/textfilter/Messages.sh b/kate/addons/kate/textfilter/Messages.sh new file mode 100644 index 00000000..7747b0da --- /dev/null +++ b/kate/addons/kate/textfilter/Messages.sh @@ -0,0 +1,3 @@ +#! /bin/sh +$EXTRACTRC *.rc *.ui >> rc.cpp +$XGETTEXT *.cpp -o $podir/katetextfilter.pot diff --git a/kate/addons/kate/textfilter/katetextfilter.desktop b/kate/addons/kate/textfilter/katetextfilter.desktop new file mode 100644 index 00000000..c3374906 --- /dev/null +++ b/kate/addons/kate/textfilter/katetextfilter.desktop @@ -0,0 +1,117 @@ +[Desktop Entry] +Type=Service +ServiceTypes=Kate/Plugin +X-KDE-Library=katetextfilterplugin +X-Kate-Version=2.8 +Name=Text Filter +Name[ar]=مرشّح النصوص +Name[ast]=Filtru de testu +Name[bg]=Текстов филтър +Name[bs]=Filter teksta +Name[ca]=Filtre de text +Name[ca@valencia]=Filtre de text +Name[cs]=Textový filtr +Name[da]=Tekstfilter +Name[de]=Textfilter +Name[el]=Φίλτρο κειμένου +Name[en_GB]=Text Filter +Name[es]=Filtro de texto +Name[et]=Tekstifilter +Name[eu]=Testuen iragazkia +Name[fi]=Tekstisuodatin +Name[fr]=Filtre de texte +Name[ga]=Scagaire Téacs +Name[gl]=Filtro de texto +Name[he]=מסנן טקסט +Name[hu]=Szövegszűrő +Name[ia]=Filtro de texto +Name[it]=Filtro di testo +Name[ja]=テキストフィルタ +Name[kk]=Мәтін сүзгісі +Name[km]=តម្រង​អត្ថបទ +Name[ko]=텍스트 필터 +Name[lt]=Teksto filtras +Name[lv]=Teksta filtrs +Name[mr]=पाठ्य गाळणी +Name[nb]=Tekstfilter +Name[nds]=Textfilter +Name[nl]=Tekstfilter +Name[nn]=Tekstfilter +Name[pa]=ਟੈਕਸਟ ਫਿਲਟਰ +Name[pl]=Filtr tekstu +Name[pt]=Filtro de Texto +Name[pt_BR]=Filtro de texto +Name[ro]=Filtru text +Name[ru]=Текстовый фильтр +Name[si]=පෙළ පෙරහන +Name[sk]=Textový filter +Name[sl]=Filter besedila +Name[sr]=Филтер текста +Name[sr@ijekavian]=Филтер текста +Name[sr@ijekavianlatin]=Filter teksta +Name[sr@latin]=Filter teksta +Name[sv]=Textfilter +Name[tg]=Филтри матн +Name[tr]=Metin Filtresi +Name[ug]=تېكىست سۈزگۈچ +Name[uk]=Фільтр тексту +Name[x-test]=xxText Filterxx +Name[zh_CN]=文本过滤 +Name[zh_TW]=文字過濾器 +Comment=Easy text filtering +Comment[ar]=ترشيح سهل للنصوص +Comment[ast]=Filtráu cenciellu de testu +Comment[bg]=Лесно филтриране на текст +Comment[bs]=Lako filtriranje teksta +Comment[ca]=Filtrat fàcil de text +Comment[ca@valencia]=Filtrat fàcil de text +Comment[cs]=Jednoduché filtrování textu +Comment[da]=Let tekst filtrering +Comment[de]=Einfacher Textfilter +Comment[el]=Εύκολο φιλτράρισμα κειμένου +Comment[en_GB]=Easy text filtering +Comment[es]=Filtrado fácil de texto +Comment[et]=Lihtne teksti filtreerimine +Comment[eu]=Testuen iragazpen erraza +Comment[fi]=Helppo tekstisuodatin +Comment[fr]=Filtrage simple de texte +Comment[ga]=Scagadh téacs go héasca +Comment[gl]=Filtrado fácil de textos +Comment[he]=מסנן טקסט מהיר +Comment[hu]=Könnyen kezelhető szövegszűrő +Comment[ia]=Facile filtrar de texto +Comment[it]=Filtraggio facile dei testi +Comment[ja]=簡単にテキストにフィルタをかけます +Comment[kk]=Оңай мәтінді сүзгілеу +Comment[km]=តម្រង​អត្ថបទ​ងាយ​ស្រួល +Comment[ko]=쉬운 텍스트 필터링 +Comment[lt]=Paprastas teksto filtravimas +Comment[lv]=Vienkārša teksta filtrēšana +Comment[mr]=सोपी पाठ्य गाळणी +Comment[nb]=Enkel tekstfiltrering +Comment[nds]=Eenfach Texten filtern +Comment[ne]=सजिलो पाठ फिल्टरिङ +Comment[nl]=Eenvoudig tekst filteren +Comment[nn]=Enkel tekstfiltrering +Comment[pa]=ਸੌਖੇ ਟੈਕਸਟ ਫਿਲਟਰ +Comment[pl]=Łatwe filtrowanie tekstu +Comment[pt]=Filtragem de texto simples +Comment[pt_BR]=Filtragem simples de texto +Comment[ro]=Filtrare ușoară a textului +Comment[ru]=Простая фильтрация текста +Comment[si]=පහසු පෙළ පෙරහන්කරණය +Comment[sk]=Ľahké filtrovanie textu +Comment[sl]=Preprosto filtriranje besedila +Comment[sr]=Лако филтрирање текста +Comment[sr@ijekavian]=Лако филтрирање текста +Comment[sr@ijekavianlatin]=Lako filtriranje teksta +Comment[sr@latin]=Lako filtriranje teksta +Comment[sv]=Enkel textfiltrering +Comment[tg]=Филтркунии матни осон +Comment[tr]=Kolay metin filtreleme +Comment[ug]=ئاددىي تېكىست سۈزگۈچ +Comment[uk]=Просте фільтрування тексту +Comment[wa]=Åjhey passaedje al passete di scrijhaedjes +Comment[x-test]=xxEasy text filteringxx +Comment[zh_CN]=简单的文本过滤器 +Comment[zh_TW]=簡易文字過濾器 diff --git a/kate/addons/kate/textfilter/plugin_katetextfilter.cpp b/kate/addons/kate/textfilter/plugin_katetextfilter.cpp new file mode 100644 index 00000000..ae957bb6 --- /dev/null +++ b/kate/addons/kate/textfilter/plugin_katetextfilter.cpp @@ -0,0 +1,281 @@ +/*************************************************************************** + plugin_katetextfilter.cpp - description + ------------------- + begin : FRE Feb 23 2001 + copyright : (C) 2001 by Joseph Wenninger + copyright : (C) 2009 Dominik Haumann + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "plugin_katetextfilter.h" +#include "moc_plugin_katetextfilter.cpp" + +#include "ui_textfilterwidget.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +K_PLUGIN_FACTORY(PluginKateTextFilterFactory, registerPlugin();) +K_EXPORT_PLUGIN(PluginKateTextFilterFactory("katetextfilter")) + +PluginViewKateTextFilter::PluginViewKateTextFilter(PluginKateTextFilter *plugin, + Kate::MainWindow *mainwindow) + : Kate::PluginView(mainwindow) + , Kate::XMLGUIClient(PluginKateTextFilterFactory::componentData()) +{ + KAction* a = actionCollection()->addAction("edit_filter"); + a->setText(i18n("Filter Te&xt...")); + a->setShortcut(Qt::CTRL + Qt::Key_Backslash); + connect(a, SIGNAL(triggered(bool)), plugin, SLOT(slotEditFilter())); + + mainwindow->guiFactory()->addClient(this); +} + +PluginViewKateTextFilter::~PluginViewKateTextFilter() +{ + mainWindow()->guiFactory()->removeClient (this); +} + +PluginKateTextFilter::PluginKateTextFilter(QObject* parent, const QVariantList&) + : Kate::Plugin((Kate::Application *)parent, "kate-text-filter-plugin") + , KTextEditor::Command() + , m_pFilterProcess(NULL) + , copyResult(false) + , mergeOutput(true) +{ + KTextEditor::CommandInterface* cmdIface = + qobject_cast(application()->editor()); + + if (cmdIface) { + cmdIface->registerCommand(this); + } +} + +PluginKateTextFilter::~PluginKateTextFilter() +{ + delete m_pFilterProcess; + KTextEditor::CommandInterface* cmdIface = + qobject_cast(application()->editor()); + + if (cmdIface) { + cmdIface->unregisterCommand(this); + } +} + + +Kate::PluginView *PluginKateTextFilter::createView (Kate::MainWindow *mainWindow) +{ + return new PluginViewKateTextFilter(this, mainWindow); +} + +void PluginKateTextFilter::slotFilterReceivedStdout() +{ + m_strFilterOutput += QString::fromLocal8Bit(m_pFilterProcess->readAllStandardOutput()); +} + + +void PluginKateTextFilter::slotFilterReceivedStderr () +{ + const QString block = QString::fromLocal8Bit(m_pFilterProcess->readAllStandardError()); + if (mergeOutput) + m_strFilterOutput += block; + else + m_stderrOutput += block; +} + +void PluginKateTextFilter::slotFilterProcessExited(int, QProcess::ExitStatus) +{ + KTextEditor::View* kv(application()->activeMainWindow()->activeView()); + if (!kv) return; + + // Is there any error output to display? + if (!mergeOutput && !m_stderrOutput.isEmpty()) + { + KTextEditor::MessageInterface* iface = + qobject_cast(kv->document()); + if (iface) + { + QPointer message = new KTextEditor::Message( + i18nc( + "@info" + , "Result of:
$ %1\n%2
" + , m_last_command + , m_stderrOutput + ) + , KTextEditor::Message::Error + ); + message->setWordWrap(true); + message->setAutoHide(1000); + iface->postMessage(message); + } + } + + if (copyResult) { + QApplication::clipboard()->setText(m_strFilterOutput); + return; + } + + // Do not even try to change the document if no result collected... + if (m_strFilterOutput.isEmpty()) + return; + + kv->document()->startEditing(); + + KTextEditor::Cursor start = kv->cursorPosition(); + if (kv->selection()) { + start = kv->selectionRange().start(); + kv->removeSelectionText(); + } + + kv->setCursorPosition(start); // for block selection + + kv->insertText(m_strFilterOutput); + kv->document()->endEditing(); +} + + +static void slipInFilter(KProcess & proc, KTextEditor::View & view, QString command) +{ + QString inputText; + + if (view.selection()) { + inputText = view.selectionText(); + } + + proc.clearProgram (); + proc.setShellCommand(command); + + proc.start(); + QByteArray encoded = inputText.toLocal8Bit(); + proc.write(encoded); + proc.closeWriteChannel(); + // TODO: Put up a modal dialog to defend the text from further + // keystrokes while the command is out. With a cancel button... +} + +void PluginKateTextFilter::slotEditFilter() +{ + if (!application()->activeMainWindow()) + return; + + KTextEditor::View* kv(application()->activeMainWindow()->activeView()); + if (!kv) return; + + KDialog dialog(application()->activeMainWindow()->window()); + dialog.setCaption("Text Filter"); + dialog.setButtons(KDialog::Cancel | KDialog::Ok); + dialog.setDefaultButton(KDialog::Ok); + + QWidget* widget = new QWidget(&dialog); + Ui::TextFilterWidget ui; + ui.setupUi(widget); + ui.filterBox->setFocus(); + dialog.setMainWidget(widget); + + KConfigGroup config(KGlobal::config(), "PluginTextFilter"); + QStringList items = config.readEntry("Completion list", QStringList()); + copyResult = config.readEntry("Copy result", false); + mergeOutput = config.readEntry("Merge output", true); + ui.filterBox->setMaxCount(10); + ui.filterBox->setHistoryItems(items, true); + ui.copyResult->setChecked(copyResult); + ui.mergeOutput->setChecked(mergeOutput); + + connect(ui.filterBox, SIGNAL(activated(QString)), &dialog, SIGNAL(okClicked())); + + if (dialog.exec() == QDialog::Accepted) { + copyResult = ui.copyResult->isChecked(); + mergeOutput = ui.mergeOutput->isChecked(); + const QString filter = ui.filterBox->currentText(); + if (!filter.isEmpty()) { + ui.filterBox->addToHistory(filter); + config.writeEntry("Completion list", ui.filterBox->historyItems()); + config.writeEntry("Copy result", copyResult); + config.writeEntry("Merge output", mergeOutput); + m_last_command = filter; + runFilter(kv, filter); + } + } +} + +void PluginKateTextFilter::runFilter(KTextEditor::View *kv, const QString &filter) +{ + m_strFilterOutput.clear(); + m_stderrOutput.clear(); + + if (!m_pFilterProcess) + { + m_pFilterProcess = new KProcess; + + connect (m_pFilterProcess, SIGNAL(readyReadStandardOutput()), + this, SLOT(slotFilterReceivedStdout())); + + connect (m_pFilterProcess, SIGNAL(readyReadStandardError()), + this, SLOT(slotFilterReceivedStderr())); + + connect (m_pFilterProcess, SIGNAL(finished(int,QProcess::ExitStatus)), + this, SLOT(slotFilterProcessExited(int,QProcess::ExitStatus))); + } + m_pFilterProcess->setOutputChannelMode( + mergeOutput ? KProcess::MergedChannels : KProcess::SeparateChannels + ); + + slipInFilter(*m_pFilterProcess, *kv, filter); +} + +//BEGIN Kate::Command methods +const QStringList &PluginKateTextFilter::cmds() +{ + static QStringList dummy("textfilter"); + return dummy; +} + +bool PluginKateTextFilter::help(KTextEditor::View *, const QString&, QString &msg) +{ + msg = i18n("

Usage: textfilter COMMAND

" + "

Replace the selection with the output of the specified shell command.

"); + return true; +} + +bool PluginKateTextFilter::exec(KTextEditor::View *v, const QString &cmd, QString &msg) +{ + QString filter = cmd.section(' ', 1).trimmed(); + + if (filter.isEmpty()) { + msg = i18n("Usage: textfilter COMMAND"); + return false; + } + + runFilter(v, filter); + return true; +} +//END + +// kate: space-indent on; indent-width 2; replace-tabs on; mixed-indent off; diff --git a/kate/addons/kate/textfilter/plugin_katetextfilter.h b/kate/addons/kate/textfilter/plugin_katetextfilter.h new file mode 100644 index 00000000..00ff8c30 --- /dev/null +++ b/kate/addons/kate/textfilter/plugin_katetextfilter.h @@ -0,0 +1,78 @@ + /*************************************************************************** + plugin_katetextfilter.h - description + ------------------- + begin : FRE Feb 23 2001 + copyright : (C) 2001 by Joseph Wenninger + email : jowenn@bigfoot.com + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef PLUGIN_KATETEXTFILTER_H +#define PLUGIN_KATETEXTFILTER_H + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +class KProcess; + +class PluginKateTextFilter : public Kate::Plugin, public KTextEditor::Command +{ + Q_OBJECT + + public: + explicit PluginKateTextFilter(QObject* parent = 0, const QVariantList& = QVariantList() ); + virtual ~PluginKateTextFilter(); + + Kate::PluginView *createView (Kate::MainWindow *mainWindow); + + // Kate::Command + const QStringList& cmds (); + bool exec(KTextEditor::View *view, const QString &cmd, QString &msg); + bool help(KTextEditor::View *view, const QString &cmd, QString &msg); + private: + void runFilter(KTextEditor::View *kv, const QString & filter); + + private: + QString m_strFilterOutput; + QString m_stderrOutput; + QString m_last_command; + KProcess * m_pFilterProcess; + QStringList completionList; + bool copyResult; + bool mergeOutput; + public slots: + void slotEditFilter (); + void slotFilterReceivedStdout(); + void slotFilterReceivedStderr(); + void slotFilterProcessExited(int exitCode, QProcess::ExitStatus exitStatus); +}; + +class PluginViewKateTextFilter: public Kate::PluginView, public Kate::XMLGUIClient +{ + Q_OBJECT + + public: + PluginViewKateTextFilter(PluginKateTextFilter *plugin, Kate::MainWindow *mainwindow); + virtual ~PluginViewKateTextFilter(); +}; + +#endif // PLUGIN_KATETEXTFILTER_H + +// kate: space-indent on; indent-width 2; replace-tabs on; mixed-indent off; diff --git a/kate/addons/kate/textfilter/textfilterwidget.ui b/kate/addons/kate/textfilter/textfilterwidget.ui new file mode 100644 index 00000000..9f313dbd --- /dev/null +++ b/kate/addons/kate/textfilter/textfilterwidget.ui @@ -0,0 +1,75 @@ + + + TextFilterWidget + + + Filter + + + + 0 + + + + + + 0 + 0 + + + + Enter command to pipe selected text through: + + + filterBox + + + + + + + + 0 + 0 + + + + + + + + Copy the result to clipboard leaving a document unchanged. + + + Copy the result instead of pasting it + + + + + + + If checked, an output from STDOUT and STDERR will be merged and no errors will be reported. +Otherwise, STDERR will be displayed as a passive message. + + + Merge STDOUT and STDERR + + + + + + + + KComboBox + QComboBox +
kcombobox.h
+
+ + KHistoryComboBox + KComboBox +
khistorycombobox.h
+
+
+ + +
diff --git a/kate/addons/kate/textfilter/ui.rc b/kate/addons/kate/textfilter/ui.rc new file mode 100644 index 00000000..194cd44d --- /dev/null +++ b/kate/addons/kate/textfilter/ui.rc @@ -0,0 +1,8 @@ + + + + &Tools + + + + diff --git a/kate/addons/ktexteditor/CMakeLists.txt b/kate/addons/ktexteditor/CMakeLists.txt new file mode 100644 index 00000000..6e559808 --- /dev/null +++ b/kate/addons/ktexteditor/CMakeLists.txt @@ -0,0 +1,6 @@ +add_subdirectory(hlselection) +add_subdirectory(insertfile) +add_subdirectory(exporter) +add_subdirectory(autobrace) +add_subdirectory(kte_iconinserter) +add_subdirectory(lumen) diff --git a/kate/addons/ktexteditor/Messages.sh b/kate/addons/ktexteditor/Messages.sh new file mode 100644 index 00000000..7e0311d7 --- /dev/null +++ b/kate/addons/ktexteditor/Messages.sh @@ -0,0 +1,9 @@ +#! /usr/bin/env bash +$EXTRACTRC `find -name "*.rc"` >> rc.cpp +find -name "*.cpp" -print > files +find -name "*.cc" -print >> files +find -name "*.h" -print >> files +$XGETTEXT --files-from=files -o $podir/ktexteditor_plugins.pot +rm -f files +rm -f rc.cpp + diff --git a/kate/addons/ktexteditor/autobrace/.kateconfig b/kate/addons/ktexteditor/autobrace/.kateconfig new file mode 100644 index 00000000..5f3fd0d8 --- /dev/null +++ b/kate/addons/ktexteditor/autobrace/.kateconfig @@ -0,0 +1 @@ +kate: indent-width 4; space-indent on; \ No newline at end of file diff --git a/kate/addons/ktexteditor/autobrace/CMakeLists.txt b/kate/addons/ktexteditor/autobrace/CMakeLists.txt new file mode 100644 index 00000000..a31e7a68 --- /dev/null +++ b/kate/addons/ktexteditor/autobrace/CMakeLists.txt @@ -0,0 +1,31 @@ +project(ktexteditor_autobrace) + +add_subdirectory(icons) + +########### next target ############### + +set(ktexteditor_autobrace_PART_SRCS + autobrace.cpp + autobrace_config.cpp +) + +kde4_add_plugin(ktexteditor_autobrace ${ktexteditor_autobrace_PART_SRCS}) + +target_link_libraries(ktexteditor_autobrace + ${KDE4_KDECORE_LIBS} + ${KDE4_KTEXTEDITOR_LIBS} +) + +install( + TARGETS ktexteditor_autobrace + DESTINATION ${KDE4_PLUGIN_INSTALL_DIR} +) + +########### install files ############### + +install( + FILES + ktexteditor_autobrace.desktop + ktexteditor_autobrace_config.desktop + DESTINATION ${KDE4_SERVICES_INSTALL_DIR} +) diff --git a/kate/addons/ktexteditor/autobrace/autobrace.cpp b/kate/addons/ktexteditor/autobrace/autobrace.cpp new file mode 100644 index 00000000..244f59b9 --- /dev/null +++ b/kate/addons/ktexteditor/autobrace/autobrace.cpp @@ -0,0 +1,452 @@ +/** + * This file is part of the KDE libraries + * Copyright (C) 2008 Jakob Petsovits + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License version 2 as published by the Free Software Foundation. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "autobrace.h" +#include "autobrace_config.h" + +#include +#include + +#include +#include +#include +#include +#include + +AutoBracePlugin *AutoBracePlugin::plugin = 0; + +K_PLUGIN_FACTORY_DEFINITION(AutoBracePluginFactory, + registerPlugin("ktexteditor_autobrace"); + registerPlugin("ktexteditor_autobrace_config"); + ) +K_EXPORT_PLUGIN(AutoBracePluginFactory("ktexteditor_autobrace", "ktexteditor_plugins")) + +AutoBracePlugin::AutoBracePlugin(QObject *parent, const QVariantList &args) + : KTextEditor::Plugin(parent), m_autoBrackets(true), m_autoQuotations(true) +{ + Q_UNUSED(args); + plugin = this; + + readConfig(); +} + +AutoBracePlugin::~AutoBracePlugin() +{ + plugin = 0; +} + +void AutoBracePlugin::addView(KTextEditor::View *view) +{ + AutoBracePluginDocument *docplugin; + + // We're not storing the brace inserter by view but by document, + // which makes signal connection and destruction a bit easier. + if (m_docplugins.contains(view->document())) { + docplugin = m_docplugins.value(view->document()); + } + else { + // Create Editor plugin and assign options through reference + docplugin = new AutoBracePluginDocument(view->document(), + m_autoBrackets, + m_autoQuotations); + m_docplugins.insert(view->document(), docplugin); + } + // Shouldn't be necessary in theory, but for removeView() the document + // might already be destroyed and removed. Also used as refcounter. + m_documents.insert(view, view->document()); +} + +void AutoBracePlugin::removeView(KTextEditor::View *view) +{ + if (m_documents.contains(view)) + { + KTextEditor::Document *document = m_documents.value(view); + m_documents.remove(view); + + // Only detach from the document if it was the last view pointing to that. + if (m_documents.keys(document).empty()) { + AutoBracePluginDocument *docplugin = m_docplugins.value(document); + m_docplugins.remove(document); + delete docplugin; + } + } +} + +void AutoBracePlugin::readConfig() +{ + KConfigGroup cg(KGlobal::config(), "AutoBrace Plugin"); + m_autoBrackets = cg.readEntry("autobrackets", true); + m_autoQuotations = cg.readEntry("autoquotations", false); +} + +void AutoBracePlugin::writeConfig() +{ + KConfigGroup cg(KGlobal::config(), "AutoBrace Plugin"); + cg.writeEntry("autobrackets", m_autoBrackets); + cg.writeEntry("autoquotations", m_autoQuotations); +} + +/// AutoBracePluginDocument + +AutoBracePluginDocument::AutoBracePluginDocument(KTextEditor::Document* document, const bool& autoBrackets, const bool& autoQuotations) + : QObject(document), m_insertionLine(0), m_withSemicolon(false), + m_lastRange(KTextEditor::Range::invalid()), m_autoBrackets(autoBrackets), m_autoQuotations(autoQuotations) +{ + connect(document, SIGNAL(exclusiveEditStart(KTextEditor::Document*)), + this, SLOT(disconnectSlots(KTextEditor::Document*))); + connect(document, SIGNAL(exclusiveEditEnd(KTextEditor::Document*)), + this, SLOT(connectSlots(KTextEditor::Document*))); + + connectSlots(document); +} + +AutoBracePluginDocument::~AutoBracePluginDocument() +{ + disconnect(parent() /* == document */, 0, this, 0); +} + +/** + * (Re-)setups slots for AutoBracePluginDocument. + * @param document Current document. + */ +void AutoBracePluginDocument::connectSlots(KTextEditor::Document *document) +{ + connect(document, SIGNAL(textInserted(KTextEditor::Document*,KTextEditor::Range)), + this, SLOT(slotTextInserted(KTextEditor::Document*,KTextEditor::Range))); + connect(document, SIGNAL(textRemoved(KTextEditor::Document*,KTextEditor::Range)), + this, SLOT(slotTextRemoved(KTextEditor::Document*,KTextEditor::Range))); +} + +void AutoBracePluginDocument::disconnectSlots(KTextEditor::Document* document) +{ + disconnect(document, SIGNAL(textInserted(KTextEditor::Document*,KTextEditor::Range)), + this, SLOT(slotTextInserted(KTextEditor::Document*,KTextEditor::Range))); + disconnect(document, SIGNAL(textRemoved(KTextEditor::Document*,KTextEditor::Range)), + this, SLOT(slotTextRemoved(KTextEditor::Document*,KTextEditor::Range))); + disconnect(document, SIGNAL(textChanged(KTextEditor::Document*)), + this, SLOT(slotTextChanged(KTextEditor::Document*))); +} + +/** + * Connected to KTextEditor::Document::textChanged() once slotTextInserted() + * found a line with an opening brace. This takes care of inserting the new + * line with its closing counterpart. + */ +void AutoBracePluginDocument::slotTextChanged(KTextEditor::Document *document) { + // Disconnect from all signals as we insert stuff by ourselves. + // Prevent infinite recursion. + disconnectSlots(document); + + // Make really sure that we want to insert the brace, paste guard and all. + if (m_insertionLine != 0 + && m_insertionLine == document->activeView()->cursorPosition().line() + && document->line(m_insertionLine).trimmed().isEmpty()) + { + KTextEditor::View *view = document->activeView(); + document->startEditing(); + + // If the document's View is a KateView then it's able to indent. + // We hereby ignore the indenter and always indent correctly. (Sorry!) + if (view->inherits("KateView")) { + // Correctly indent the empty line. Magic! + KTextEditor::Range lineRange( + m_insertionLine, 0, + m_insertionLine, document->lineLength(m_insertionLine) + ); + document->replaceText(lineRange, m_indentation); + + connect(this, SIGNAL(indent()), view, SLOT(indent())); + emit indent(); + disconnect(this, SIGNAL(indent()), view, SLOT(indent())); + } + // The line with the closing brace. (Inserted via insertLine() in order + // to avoid space removal by potential indenters.) + document->insertLine(m_insertionLine + 1, m_indentation + '}' + (m_withSemicolon ? ";" : "")); + + document->endEditing(); + view->setCursorPosition(document->endOfLine(m_insertionLine)); + } + m_insertionLine = 0; + + // Re-enable the textInserted() slot again. + connectSlots(document); +} + +/** + * Connected to KTextEditor::Documet::textRemoved. Allows to delete + * an automatically inserted closing bracket if the opening counterpart + * has been removed. + */ +void AutoBracePluginDocument::slotTextRemoved(KTextEditor::Document* document, const KTextEditor::Range& range) +{ + // If last range equals the deleted text range (last range + // is last inserted bracket), we also delete the associated closing bracket. + if (m_lastRange == range) { + // avoid endless recursion + disconnectSlots(document); + + // Delete the character at the same range because the opening + // bracket has already been removed so the closing bracket + // should now have been shifted to that same position + if (range.isValid()) { + document->removeText(range); + } + + connectSlots(document); + } +} + +/** + * Connected to KTextEditor::Document::textInserted(), which is emitted on all + * insertion changes. Line text and line breaks are emitted separately by + * KatePart, and pasted text gets the same treatment as manually entered text. + * Because of that, we discard paste operations by only remembering the + * insertion status for the last line that was entered. + */ +void AutoBracePluginDocument::slotTextInserted(KTextEditor::Document *document, + const KTextEditor::Range& range) +{ + // Fill brackets map matching opening and closing brackets. + QMap brackets; + brackets["("] = ")"; + brackets["["] = "]"; + + // latex wants {, too + if (document->mode() == "LaTeX") + brackets["{"] = "}"; + + // List of Tokens after which an automatic bracket expanion + // is allowed. + const static QStringList allowedNextToken = QStringList() << "]" << ")" << "," + << "." << ";" << "\n" << "\t" << " " << ""; + const QString text = document->text(range); + + // An insertion operation cancels any last range removal + // operation + m_lastRange = KTextEditor::Range::invalid(); + + // Make sure to handle only: + // 1.) New lines after { (brace openers) + // 2.) Opening braces like '(' and '[' + // 3.) Quotation marks like " and ' + + // Handle brace openers + if (text == "\n") { + // Remember this position as insertion candidate. + // We don't directly insert this here because of KatePart specifics: + // a) Setting the cursor position crashes at this point, and + // b) textChanged() only gets called once per edit operation, so we can + // ignore the same braces when they're being inserted via paste. + if (isInsertionCandidate(document, range.start().line())) { + m_insertionLine = range.end().line(); + connect(document, SIGNAL(textChanged(KTextEditor::Document*)), + this, SLOT(slotTextChanged(KTextEditor::Document*))); + } + else { + m_insertionLine = 0; + } + } + // Opening brackets (defined in ctor) + else if (m_autoBrackets && brackets.contains(text)) { + // Only insert auto closing brackets if current text range + // is followed by one of the allowed next tokens. + if (allowedNextToken.contains(nextToken(document,range))) { + insertAutoBracket(document, range, brackets[text]); + } + + } + // Check whether closing brackets are allowed. + // If a brace is not allowed remove it + // and set the cursor to the position after that text range. + // Bracket tests bases on this simple idea: A bracket can only be inserted + // if it is NOT followed by the same bracket. This results in overwriting closing brackets. + else if (m_autoBrackets && brackets.values().contains(text)) { + if (nextToken(document,range) == text) { + KTextEditor::Cursor saved = range.end(); + document->removeText(range); + document->activeView()->setCursorPosition(saved); + } + } + // Insert auto-quotation marks (if enabled). Same idea as with brackets + // applies here: double quotation marks are eaten up and only inserted if not + // followed by the same quoation mark. Additionally automatic quotation marks + // are inserted only if NOT followed by a back slash (escaping character). + else if (m_autoQuotations && (text == "\"" || text == "\'") && previousToken(document, range) != "\\") { + const QString next = nextToken(document, range); + // Eat it if already there + if (next == text) { + KTextEditor::Cursor saved = range.end(); + document->removeText(range); + document->activeView()->setCursorPosition(saved); + } + // Quotation marks only inserted if followed by one of the allowed + // next tokens and the number of marks in the insertion line is even + // (excluding the already inserted mark) + else if (allowedNextToken.contains(next) + && (document->line(range.start().line()).count(text) % 2) ) { + insertAutoBracket(document, range, text); + } + } +} + +/** + * Automatically inserts closing bracket. Cursor + * is placed in between the brackets. + * @param document Current document. + * @param range Inserted text range (by text-inserted slot) + * @param brace Brace to insert + */ +void AutoBracePluginDocument::insertAutoBracket(KTextEditor::Document *document, + const KTextEditor::Range& range, + const QString& brace) { + // Disconnect Slots to avoid check for redundant closing brackets + disconnectSlots(document); + + // Save range to allow following remove operation to + // detect the corresponding closing bracket + m_lastRange = range; + + KTextEditor::Cursor saved = range.end(); + // Depending on brace, insert corresponding closing brace. + document->insertText(range.end(), brace); + document->activeView()->setCursorPosition(saved); + + // Re-Enable insertion slot. + connectSlots(document); +} + +/** + * Returns next character after specified text range in document. + * @param document Current document. + * @param range Inserted text range (by text-inserted slot) + * @return Next character after text range + */ +const QString AutoBracePluginDocument::nextToken(KTextEditor::Document* document, const KTextEditor::Range& range) +{ + // Calculate range after insertion (exactly one character) + KTextEditor::Range afterRange(range.end(), range.end().line(), range.end().column()+1); + + return (afterRange.isValid() ? document->text(afterRange) : ""); +} + +/** + * Returns previous character before specified text range in document. + * @param document Current document. + * @param range Inserted text range (by text-inserted slot) + * @return Next character after text range + */ +const QString AutoBracePluginDocument::previousToken(KTextEditor::Document* document, const KTextEditor::Range& range) +{ + // Calculate range before insertion (exactly one character) + KTextEditor::Range beforeRange(range.start().line(), range.start().column()-1, range.start().line(), + range.start().column()); + + return (beforeRange.isValid() ? document->text(beforeRange) : ""); +} + +bool AutoBracePluginDocument::isInsertionCandidate(KTextEditor::Document *document, int openingBraceLine) { + QString line = document->line(openingBraceLine); + if (line.isEmpty() || !line.endsWith('{')) { + return false; + } + + // Get the indentation prefix. + QRegExp rx("^(\\s+)"); + QString indentation = (rx.indexIn(line) == -1) ? "" : rx.cap(1); + + // Determine whether to insert a brace or not, depending on the indentation + // of the upcoming (non-empty) line. + bool isCandidate = true; + QString indentationLength = QString::number(indentation.length()); + QString indentationLengthMinusOne = QString::number(indentation.length() - 1); + + ///TODO: make configurable + // these tokens must not start a line that is used to get the correct indendation width + QStringList forbiddenTokenList; + if ( line.contains("class") || line.contains("interface") || line.contains("struct") ) { + forbiddenTokenList << "private" << "public" << "protected"; + if ( document->mode() == "C++" ) { + forbiddenTokenList << "signals" << "Q_SIGNALS"; + } else { + // PHP and potentially others + forbiddenTokenList << "function"; + } + } + if ( (document->mode() == "C++" || document->mode() == "C") && line.contains("namespace", Qt::CaseInsensitive) ) { + // C++ specific + forbiddenTokenList << "class" << "struct"; + } + const QString forbiddenTokens = forbiddenTokenList.isEmpty() ? QLatin1String("") : QString(QLatin1String("(?!") + forbiddenTokenList.join(QLatin1String("|")) + QLatin1Char(')')); + + for (int i = openingBraceLine + 1; i < document->lines(); ++i) + { + line = document->line(i); + if (line.trimmed().isEmpty()) { + continue; // Empty lines are not a reliable source of information. + } + + if (indentation.length() == 0) { + // Inserting a brace is ok if there is a line (not starting with a + // brace) without indentation. + rx.setPattern("^(?=[^\\}\\s])" + // But it's not OK if the line starts with one of our forbidden tokens. + + forbiddenTokens + ); + } + else { + rx.setPattern("^(?:" + // Inserting a brace is ok if there is a closing brace with + // less indentation than the opener line. + "[\\s]{0," + indentationLengthMinusOne + "}\\}" + "|" + // Inserting a brace is ok if there is a line (not starting with a + // brace) with less or similar indentation as the original line. + "[\\s]{0," + indentationLength + "}(?=[^\\}\\s])" + // But it's not OK if the line starts with one of our forbidden tokens. + + forbiddenTokens + + ")" + ); + } + + if (rx.indexIn(line) == -1) { + // There is already a brace, or the line is indented more than the + // opener line (which means we expect a brace somewhere further down), + // or we found a forbidden token. + // So don't insert the brace, and just indent the line. + isCandidate = false; + } + // Quit the loop - a non-empty line always leads to a definitive decision. + break; + } + + if (isCandidate) { + m_indentation = indentation; + // in C++ automatically add a semicolon after the closing brace when we create a new class/struct + if ( (document->mode() == "C++" || document->mode() == "C") + && document->line(openingBraceLine).indexOf(QRegExp("(?:class|struct|enum)\\s+[^\\s]+(\\s*[:,](\\s*((public|protected|private)\\s+)?[^\\s]+))*\\s*\\{\\s*$")) != -1 ) + { + m_withSemicolon = true; + } else { + m_withSemicolon = false; + } + } + return isCandidate; +} + +#include "moc_autobrace.cpp" diff --git a/kate/addons/ktexteditor/autobrace/autobrace.h b/kate/addons/ktexteditor/autobrace/autobrace.h new file mode 100644 index 00000000..91fe68ff --- /dev/null +++ b/kate/addons/ktexteditor/autobrace/autobrace.h @@ -0,0 +1,105 @@ +/** + * This file is part of the KDE libraries + * Copyright (C) 2008 Jakob Petsovits + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License version 2 as published by the Free Software Foundation. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef AUTOBRACE_H +#define AUTOBRACE_H + +#include +#include +#include +#include + +#include +#include +#include +#include + +class AutoBracePlugin + : public KTextEditor::Plugin +{ + Q_OBJECT + + public: + explicit AutoBracePlugin(QObject *parent = 0, const QVariantList &args = QVariantList()); + virtual ~AutoBracePlugin(); + + static AutoBracePlugin *self() { return plugin; } + + void addView (KTextEditor::View *view); + void removeView (KTextEditor::View *view); + + void readConfig(); + void writeConfig(); + + virtual void readConfig (KConfig *) {} + virtual void writeConfig (KConfig *) {} + + /// Inline Option Get/Setters + bool autoBrackets() const { return m_autoBrackets; } + void setAutoBrackets(bool y) { m_autoBrackets = y; } + bool autoQuotations() const { return m_autoQuotations; } + void setAutoQuotations(bool y) { m_autoQuotations = y; } + private: + static AutoBracePlugin *plugin; + QHash m_documents; + QHash m_docplugins; + bool m_autoBrackets; + bool m_autoQuotations; +}; + +class AutoBracePluginDocument + : public QObject, public KXMLGUIClient +{ + Q_OBJECT + + public: + explicit AutoBracePluginDocument(KTextEditor::Document *document, const bool& autoBrackets, const bool& autoQuotations); + ~AutoBracePluginDocument(); + + private Q_SLOTS: + void slotTextChanged(KTextEditor::Document *document); + void slotTextInserted(KTextEditor::Document *document, const KTextEditor::Range& range); + void slotTextRemoved(KTextEditor::Document *document, const KTextEditor::Range& range); + + void connectSlots(KTextEditor::Document* document); + void disconnectSlots(KTextEditor::Document* document); + + private: + bool isInsertionCandidate(KTextEditor::Document *document, int openingBraceLine); + + Q_SIGNALS: + void indent(); + + private: + void insertAutoBracket(KTextEditor::Document *document,const KTextEditor::Range& range, + const QString& brace); + const QString previousToken(KTextEditor::Document *document,const KTextEditor::Range& range); + const QString nextToken(KTextEditor::Document *document,const KTextEditor::Range& range); + + int m_insertionLine; + QString m_indentation; + bool m_withSemicolon; + KTextEditor::Range m_lastRange; + const bool& m_autoBrackets; + const bool& m_autoQuotations; +}; + +K_PLUGIN_FACTORY_DECLARATION(AutoBracePluginFactory) + +#endif // AUTOBRACE_H diff --git a/kate/addons/ktexteditor/autobrace/autobrace_config.cpp b/kate/addons/ktexteditor/autobrace/autobrace_config.cpp new file mode 100644 index 00000000..da99816d --- /dev/null +++ b/kate/addons/ktexteditor/autobrace/autobrace_config.cpp @@ -0,0 +1,106 @@ +/** + * This file is part of the KDE libraries + * Copyright (C) 2010 André Stein + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License version 2 as published by the Free Software Foundation. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "autobrace_config.h" +#include "autobrace.h" + +#include +#include + +#include +#include +#include +#include +#include + +AutoBraceConfig::AutoBraceConfig(QWidget *parent, const QVariantList &args) + : KCModule(AutoBracePluginFactory::componentData(), parent, args) +{ + QVBoxLayout *layout = new QVBoxLayout(this); + + m_autoBrackets = new QCheckBox(i18n("Automatically add closing brackets ) and ] (and } for e.g. LaTeX)"), this); + m_autoQuotations = new QCheckBox(i18n("Automatically add closing quotation marks"), this); + + layout->addWidget(m_autoBrackets); + layout->addWidget(m_autoQuotations); + + setLayout(layout); + + load(); + + // Changed slots + connect(m_autoBrackets, SIGNAL(toggled(bool)), this, SLOT(slotChanged(bool))); + connect(m_autoQuotations, SIGNAL(toggled(bool)), this, SLOT(slotChanged(bool))); +} + +AutoBraceConfig::~AutoBraceConfig() +{ +} + +void AutoBraceConfig::save() +{ + if (AutoBracePlugin::self()) + { + AutoBracePlugin::self()->setAutoBrackets(m_autoBrackets->isChecked()); + AutoBracePlugin::self()->setAutoQuotations(m_autoQuotations->isChecked()); + AutoBracePlugin::self()->writeConfig(); + } + else + { + KConfigGroup cg(KGlobal::config(), "AutoBrace Plugin"); + cg.writeEntry("autobrackets", m_autoBrackets->isChecked()); + cg.writeEntry("autoquotations", m_autoQuotations->isChecked()); + } + + emit changed(false); +} + +void AutoBraceConfig::load() +{ + if (AutoBracePlugin::self()) + { + AutoBracePlugin::self()->readConfig(); + m_autoBrackets->setChecked(AutoBracePlugin::self()->autoBrackets()); + m_autoQuotations->setChecked(AutoBracePlugin::self()->autoQuotations()); + } + else + { + KConfigGroup cg(KGlobal::config(), "AutoBrace Plugin" ); + m_autoBrackets->setChecked(cg.readEntry("autobrackets", QVariant(true)).toBool()); + m_autoQuotations->setChecked(cg.readEntry("autoquotations", QVariant(true)).toBool()); + } + + emit changed(false); +} + +void AutoBraceConfig::defaults() +{ + m_autoBrackets->setChecked(true); + m_autoQuotations->setChecked(true); + + emit changed(true); +} + +void AutoBraceConfig::slotChanged(bool) +{ + emit changed(true); +} + +#include "moc_autobrace_config.cpp" + diff --git a/kate/addons/ktexteditor/autobrace/autobrace_config.h b/kate/addons/ktexteditor/autobrace/autobrace_config.h new file mode 100644 index 00000000..58f60233 --- /dev/null +++ b/kate/addons/ktexteditor/autobrace/autobrace_config.h @@ -0,0 +1,49 @@ +/** + * This file is part of the KDE libraries + * Copyright (C) 2010 André Stein + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License version 2 as published by the Free Software Foundation. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef TIMEDATE_CONFIG_H +#define TIMEDATE_CONFIG_H + +#include + +#include + +class AutoBraceConfig + : public KCModule +{ + Q_OBJECT + +public: + explicit AutoBraceConfig(QWidget *parent = 0, const QVariantList &args = QVariantList()); + virtual ~AutoBraceConfig(); + + virtual void save(); + virtual void load(); + virtual void defaults(); + +private Q_SLOTS: + void slotChanged(bool); + +private: + QCheckBox* m_autoBrackets; + QCheckBox* m_autoQuotations; +}; + +#endif // TIMEDATE_CONFIG_H + diff --git a/kate/addons/ktexteditor/autobrace/icons/CMakeLists.txt b/kate/addons/ktexteditor/autobrace/icons/CMakeLists.txt new file mode 100644 index 00000000..64266d49 --- /dev/null +++ b/kate/addons/ktexteditor/autobrace/icons/CMakeLists.txt @@ -0,0 +1 @@ +kde4_install_icons(${KDE4_ICON_INSTALL_DIR}) diff --git a/kate/addons/ktexteditor/autobrace/icons/hisc-app-ktexteditorautobrace.svgz b/kate/addons/ktexteditor/autobrace/icons/hisc-app-ktexteditorautobrace.svgz new file mode 100644 index 00000000..c4a76acd Binary files /dev/null and b/kate/addons/ktexteditor/autobrace/icons/hisc-app-ktexteditorautobrace.svgz differ diff --git a/kate/addons/ktexteditor/autobrace/ktexteditor_autobrace.desktop b/kate/addons/ktexteditor/autobrace/ktexteditor_autobrace.desktop new file mode 100644 index 00000000..2b61cb00 --- /dev/null +++ b/kate/addons/ktexteditor/autobrace/ktexteditor_autobrace.desktop @@ -0,0 +1,121 @@ +[Desktop Entry] +X-KDE-Library=ktexteditor_autobrace +X-KDE-PluginKeyword=ktexteditor_autobrace +X-KDE-PluginInfo-Author=Jakob Petsovits +X-KDE-PluginInfo-Email=jpetso@gmx.at +X-KDE-PluginInfo-Name=ktexteditorautobrace +X-KDE-PluginInfo-Version=0.1 +X-KDE-PluginInfo-Website=http://kate.kde.org +X-KDE-PluginInfo-Category=Editor +X-KDE-PluginInfo-Depends= +X-KDE-PluginInfo-License=GPL +X-KDE-PluginInfo-EnabledByDefault=false +X-KDE-ParentApp=kate +X-KDE-Version=4.0 +X-KDE-ServiceTypes=KTextEditor/Plugin +Type=Service +Icon=ktexteditorautobrace +Name=AutoBrace +Name[bg]=Автоматични скоби +Name[bs]=Autozagrade +Name[ca]=Parèntesis automàtics +Name[ca@valencia]=Parèntesis automàtics +Name[cs]=Automatické závorky +Name[da]=AutoBrace +Name[de]=AutoKlammern +Name[el]=AutoBrace +Name[en_GB]=AutoBrace +Name[es]=AutoLlaves +Name[et]=Automaatsulud +Name[eu]=Parentesi automatikoak +Name[fi]=Automaattisulutus +Name[fr]=Accolades automatiques +Name[ga]=AutoBrace +Name[gl]=Parénteses automáticos +Name[hu]=Automatikus zárójelbezáró +Name[ia]=AutoBrace (Parentheses Automatic) +Name[it]=Parentesi automatiche +Name[ja]=AutoBrace +Name[kk]=АвтоЖақшаЖабу +Name[km]=AutoBrace +Name[ko]=자동 괄호 +Name[lt]=Automatiniai skliaustai +Name[lv]=Autom. figūriekavas +Name[mr]=ऑटो-ब्रेस +Name[nb]=AutoBrace +Name[nds]=Auto-Klemmen +Name[nl]=AutoHaakjes +Name[pa]=ਆਟੋ-ਬਰੈਕਟ +Name[pl]=Auto-klamrowanie +Name[pt]=Parêntesis Automáticos +Name[pt_BR]=Parênteses automático +Name[ro]=Închidere Automată a Acoladei +Name[ru]=Автоматическое закрытие скобок +Name[si]=AutoBrace +Name[sk]=AutoBrace +Name[sl]=Samodejni oklepaji +Name[sr]=Аутоматске заграде +Name[sr@ijekavian]=Аутоматске заграде +Name[sr@ijekavianlatin]=Automatske zagrade +Name[sr@latin]=Automatske zagrade +Name[sv]=Automatisk parentes +Name[tg]=Қавсҳои худкор +Name[tr]=AutoBrace +Name[ug]=AutoBrace +Name[uk]=Автоматичні дужки +Name[wa]=AutoBrace +Name[x-test]=xxAutoBracexx +Name[zh_CN]=自动括号 +Name[zh_TW]=自動產生括號 +Comment=Insert closing braces on pressing Enter +Comment[ar]=أدرج الأقواس الغالِقَة عند ضغط Enter +Comment[bg]=Затваряне на скобите при натискане на "Enter" +Comment[bs]=Ubacuje zatvarajuće zagrade pritiskom na Enter +Comment[ca]=Insereix els parèntesis de tancament en prémer Retorn +Comment[ca@valencia]=Insereix els parèntesis de tancament en prémer Retorn +Comment[cs]=Vložit uzavírací závorky při stisku Enter +Comment[da]=Indsæt afsluttende klammer ved tryk på Retur +Comment[de]=Beim Drücken der Eingabetaste schließende Klammer einfügen +Comment[el]=Κλείσιμο παρενθέσεων πιέζοντας το Enter +Comment[en_GB]=Insert closing braces on pressing Enter +Comment[es]=Insertar llaves de cierre al presionar Intro +Comment[et]=Lõpetavate sulgude lisamine Enteri vajutamisel +Comment[eu]=Txertatu itxierako parentesia Sartu sakatzean +Comment[fi]=Lisää lopettavat sulut painettaessa Enteriä +Comment[fr]=Insère des accolades fermantes lors d'un appui sur « Entrée » +Comment[ga]=Ionsáigh lúibín deiridh nuair a bhrúitear Enter +Comment[gl]=Insire os parénteses de peche ao premer Intro +Comment[hu]=Az Enter megnyomásakor beszúr egy zárójelet +Comment[ia]=Inserta un parentheses claudente quando on preme Enter +Comment[it]=Inserisci parentesi di chiusura alla pressione del tasto Invio +Comment[kk]=Enter пернені басқанда жақшаны жабу +Comment[km]=បញ្ចូល​ដោយ​បិទ braces ដោយ​ចុច​បញ្ចូល​ +Comment[ko]=Enter 키를 누를 때 닫는 괄호 넣기 +Comment[lt]=Įterpti uždarančius skliaustus paspaudus Enter +Comment[lv]=Nospiežot Enter, ievietot aizverošās figūriekavas +Comment[mr]=एंटर दाबल्यावर बंद समासचिन्ह अंतर्भूत करतो +Comment[nb]=Sett inn avsluttende krøllparentes når Enter trykkes +Comment[nds]=Afsluten Klemmen mit de Ingaavtast infögen +Comment[nl]=Voert afsluithaakjes in bij indrukken van Enter +Comment[pa]=ਐਂਟਰ ਦੱਬਣ ਉੱਤੇ ਬਰੈਕਟ ਬੰਦ ਕਰੋ +Comment[pl]=Wstaw klamrę zamykającą przy naciśnięciu Enteru +Comment[pt]=Inserir os parêntesis de fecho ao carregar em Enter +Comment[pt_BR]=Insere parênteses de fechamento ao pressionar Enter +Comment[ro]=Inserează acoladele de închidere la apăsarea Enter +Comment[ru]=Вставка парной скобки при нажатии клавиши Enter +Comment[si]=Enter යතුර එබීමේදී රැඳවුම් වසාදැමීම එක් කරන්න +Comment[sk]=Vložiť koniec zátvorky stlačením Enter +Comment[sl]=Vstavi zaklepaje ob pritisku na tipko Enter +Comment[sr]=Умеће затварајуће заграде кад се притисне Enter +Comment[sr@ijekavian]=Умеће затварајуће заграде кад се притисне Enter +Comment[sr@ijekavianlatin]=Umeće zatvarajuće zagrade kad se pritisne Enter +Comment[sr@latin]=Umeće zatvarajuće zagrade kad se pritisne Enter +Comment[sv]=Infoga avslutande parenteser när returtangenten trycks +Comment[tg]=Қавсҳои хотимавиро бо зеркунии тугмаи Enter ворид кунед +Comment[tr]=Enter'a basıldığında parantezi kapat +Comment[ug]=Enter بېسىلغاندا يېپىلغان braces قىستۇر +Comment[uk]=Вставляє завершальні дужки після натискання Enter +Comment[wa]=Stitchî des acolådes cloyantes å tchôcaedje d' Intrêye +Comment[x-test]=xxInsert closing braces on pressing Enterxx +Comment[zh_CN]=按下回车后插入反括号 +Comment[zh_TW]=按下 Enter 時自動插入關閉(右側)括號 diff --git a/kate/addons/ktexteditor/autobrace/ktexteditor_autobrace_config.desktop b/kate/addons/ktexteditor/autobrace/ktexteditor_autobrace_config.desktop new file mode 100644 index 00000000..5b221b2a --- /dev/null +++ b/kate/addons/ktexteditor/autobrace/ktexteditor_autobrace_config.desktop @@ -0,0 +1,58 @@ +[Desktop Entry] +Type=Service +X-KDE-ServiceTypes=KCModule +X-KDE-Library=ktexteditor_autobrace +X-KDE-PluginKeyword=ktexteditor_autobrace_config +X-KDE-ParentComponents=ktexteditorautobrace +Name=AutoBrace Configuration +Name[bg]=Настройка на автоматичните скоби +Name[bs]=Konfiguracija automatskih zagrada +Name[ca]=Configuració dels parèntesis automàtics +Name[ca@valencia]=Configuració dels parèntesis automàtics +Name[cs]=Nastavení automatických závorek +Name[da]=Indstilling af AutoBrace +Name[de]=Einstellungen zu automatischer Klammersetzung +Name[el]=Διαμόρφωση AutoBrace +Name[en_GB]=AutoBrace Configuration +Name[es]=Configuración de AutoLlaves +Name[et]=Automaatsete sulgude seadistamine +Name[eu]=Parentesi automatikoen konfigurazioa +Name[fi]=Automaattisulutuksen asetukset +Name[fr]=Configuration des accolades automatiques +Name[ga]=Cumraíocht AutoBrace +Name[gl]=Configuración dos parénteses automáticos +Name[hu]=Beállítások - Automatikus zárójelbezáró +Name[ia]=Configuration de AutoBrace (Parentheses Automatic) +Name[it]=Configurazione parentesi automatiche +Name[ja]=AutoBrace の設定 +Name[kk]=АвтоЖақшаЖабу баптауы +Name[km]=ការ​កំណត់​រចនាសម្ព័ន្ធ​ AutoBrace +Name[ko]=자동 괄호 설정 +Name[lt]=Automatinių skliaustų konfigūracija +Name[lv]=Automātisko figūriekavu ievietošanas konfigurācija +Name[mr]=ऑटो-ब्रेस संयोजना +Name[nb]=Oppsett for automatisk parenteslukking +Name[nds]=Auto-Klemmen instellen +Name[nl]=Configuratie voor automatische haakjes +Name[pa]=AutoBrace ਸੰਰਚਨਾ +Name[pl]=Konfiguracja auto-klamrowania +Name[pt]=Configuração dos Parêntesis Automáticos +Name[pt_BR]=Configuração automática dos parênteses +Name[ro]=Configurare Închidere Automată a Acoladei +Name[ru]=Настройка автоматического закрытия скобок +Name[si]=AutoBrace සැකසුම +Name[sk]=Konfugurácia AutoBrace +Name[sl]=Nastavitve samodejnih oklepajev +Name[sr]=Подешавање аутоматских заграда +Name[sr@ijekavian]=Подешавање аутоматских заграда +Name[sr@ijekavianlatin]=Podešavanje automatskih zagrada +Name[sr@latin]=Podešavanje automatskih zagrada +Name[sv]=Automatisk parentesinställning +Name[tg]=Танзимоти қавсҳои худкор +Name[tr]=AutoBrace Yapılandırması +Name[ug]=AutoBrace سەپلىمە +Name[uk]=Налаштування автоматичних дужок +Name[wa]=Apontiaedje d' AutoBrace +Name[x-test]=xxAutoBrace Configurationxx +Name[zh_CN]=自动括号配置 +Name[zh_TW]=AutoBrace 組態 diff --git a/kate/addons/ktexteditor/exporter/CMakeLists.txt b/kate/addons/ktexteditor/exporter/CMakeLists.txt new file mode 100644 index 00000000..3a7bb7e7 --- /dev/null +++ b/kate/addons/ktexteditor/exporter/CMakeLists.txt @@ -0,0 +1,32 @@ +project(ktexteditor_exporter) + +########### next target ############### + +set(ktexteditor_exporter_PART_SRCS + exporterplugin.cpp + exporterpluginview.cpp + htmlexporter.cpp +) + +kde4_add_plugin(ktexteditor_exporter ${ktexteditor_exporter_PART_SRCS}) + +target_link_libraries(ktexteditor_exporter + ${KDE4_KDECORE_LIBS} + ${KDE4_KTEXTEDITOR_LIBS} +) + +install( + TARGETS ktexteditor_exporter + DESTINATION ${KDE4_PLUGIN_INSTALL_DIR} +) + +########### install files ############### + +install( + FILES ktexteditor_exporter.desktop + DESTINATION ${KDE4_SERVICES_INSTALL_DIR} +) +install( + FILES ktexteditor_exporterui.rc + DESTINATION ${KDE4_DATA_INSTALL_DIR}/ktexteditor_exporter +) diff --git a/kate/addons/ktexteditor/exporter/TODO b/kate/addons/ktexteditor/exporter/TODO new file mode 100644 index 00000000..d8ca0154 --- /dev/null +++ b/kate/addons/ktexteditor/exporter/TODO @@ -0,0 +1,13 @@ +1) add more exporters +- LaTeX +- Bash color codes +- ??? + +2) add dialog for export action +- select exporter +- copy to clipboard +- save to file + +3) make it possible to run this from the outside + +4) optionally add linenumbers \ No newline at end of file diff --git a/kate/addons/ktexteditor/exporter/abstractexporter.h b/kate/addons/ktexteditor/exporter/abstractexporter.h new file mode 100644 index 00000000..0c8f72e3 --- /dev/null +++ b/kate/addons/ktexteditor/exporter/abstractexporter.h @@ -0,0 +1,80 @@ +/** + * This file is part of the KDE libraries + * Copyright (C) 2009 Milian Wolff + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License version 2 as published by the Free Software Foundation. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef ABSTRACTEXPORTER_H +#define ABSTRACTEXPORTER_H + +#include + +#include +#include +#include +#include +#include +#include + +class AbstractExporter +{ + public: + /// If \p m_encapsulate is set, you should add some kind of header in the ctor + /// to \p m_output. + AbstractExporter(KTextEditor::View* view, + QTextStream &output, const bool encapsulate = false) + : m_view(view), m_output(output), m_encapsulate(encapsulate), + m_defaultAttribute(0) + { + QColor defaultBackground; + if ( KTextEditor::ConfigInterface* ciface = qobject_cast< KTextEditor::ConfigInterface* >(m_view) ) { + QVariant variant = ciface->configValue("background-color"); + if ( variant.canConvert() ) { + defaultBackground = variant.value(); + } + } + if ( KTextEditor::HighlightInterface* hiface = qobject_cast< KTextEditor::HighlightInterface* >(m_view->document()) ) { + m_defaultAttribute = hiface->defaultStyle(KTextEditor::HighlightInterface::dsNormal); + m_defaultAttribute->setBackground(QBrush(defaultBackground)); + } + } + + /// Gets called after everything got exported. + /// Hence, if \p m_encapsulate is set, you should probably add some kind of footer here. + virtual ~AbstractExporter() + {} + + /// Begin a new line. + virtual void openLine() = 0; + + /// Finish the current line. + virtual void closeLine(const bool lastLine) = 0; + + /// Export \p text with given text attribute \p attrib. + /// NOTE: Check \p attrib, it might be null for KTextEditors that do not implement the + /// HighlightInterface. + virtual void exportText(const QString& text, const KTextEditor::Attribute::Ptr& attrib) = 0; + + protected: + KTextEditor::View* m_view; + QTextStream &m_output; + bool m_encapsulate; + KTextEditor::Attribute::Ptr m_defaultAttribute; +}; + +#endif // ABSTRACTEXPORTER_H + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/addons/ktexteditor/exporter/exporterplugin.cpp b/kate/addons/ktexteditor/exporter/exporterplugin.cpp new file mode 100644 index 00000000..abf1ce68 --- /dev/null +++ b/kate/addons/ktexteditor/exporter/exporterplugin.cpp @@ -0,0 +1,56 @@ +/** + * This file is part of the KDE libraries + * Copyright (C) 2009 Milian Wolff + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License version 2 as published by the Free Software Foundation. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "exporterplugin.h" + +#include + +#include + +#include "exporterpluginview.h" + +K_PLUGIN_FACTORY_DEFINITION(ExporterPluginFactory, + registerPlugin("ktexteditor_exporter"); + ) +K_EXPORT_PLUGIN(ExporterPluginFactory("ktexteditor_exporter", "ktexteditor_plugins")) + +ExporterPlugin::ExporterPlugin(QObject *parent, const QVariantList &args) + : KTextEditor::Plugin(parent) +{ + Q_UNUSED(args); +} + +ExporterPlugin::~ExporterPlugin() +{ +} + +void ExporterPlugin::addView(KTextEditor::View *view) +{ + // need to keep track of, since the plugin might get disabled and we have to delete the views + m_views[view] = new ExporterPluginView(view); +} + +void ExporterPlugin::removeView(KTextEditor::View* view) +{ + delete m_views.take(view); +} + +#include "moc_exporterplugin.cpp" + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/addons/ktexteditor/exporter/exporterplugin.h b/kate/addons/ktexteditor/exporter/exporterplugin.h new file mode 100644 index 00000000..f719d0fc --- /dev/null +++ b/kate/addons/ktexteditor/exporter/exporterplugin.h @@ -0,0 +1,55 @@ +/** + * This file is part of the KDE libraries + * Copyright (C) 2009 Milian Wolff + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License version 2 as published by the Free Software Foundation. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef EXPORTERPLUGIN_H +#define EXPORTERPLUGIN_H + +#include + +#include + +#include + +namespace KTextEditor { +class View; +} + +class ExporterPluginView; + +class ExporterPlugin + : public KTextEditor::Plugin +{ + Q_OBJECT + + public: + explicit ExporterPlugin(QObject *parent = 0, const QVariantList &args = QVariantList()); + virtual ~ExporterPlugin(); + + virtual void addView (KTextEditor::View *view); + virtual void removeView(KTextEditor::View* view); + + private: + QMap m_views; +}; + +K_PLUGIN_FACTORY_DECLARATION(ExporterPluginFactory) + +#endif // EXPORTERPLUGIN_H + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/addons/ktexteditor/exporter/exporterpluginview.cpp b/kate/addons/ktexteditor/exporter/exporterpluginview.cpp new file mode 100644 index 00000000..89598134 --- /dev/null +++ b/kate/addons/ktexteditor/exporter/exporterpluginview.cpp @@ -0,0 +1,206 @@ +/** + * This file is part of the KDE libraries + * Copyright (C) 2009 Milian Wolff + * Copyright (C) 2002 John Firebaugh + * Copyright (C) 2001 Christoph Cullmann + * Copyright (C) 2001 Joseph Wenninger + * Copyright (C) 1999 Jochen Wilhelmy + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License version 2 as published by the Free Software Foundation. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "exporterpluginview.h" + +#include "exporterplugin.h" +#include "abstractexporter.h" +#include "htmlexporter.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +ExporterPluginView::ExporterPluginView(KTextEditor::View* view) + : QObject(view), KXMLGUIClient(view), m_view(view) +{ + setComponentData( ExporterPluginFactory::componentData() ); + setXMLFile("ktexteditor_exporterui.rc"); + + m_copyAction = actionCollection()->addAction("edit_copy_html", this, SLOT(exportToClipboard())); + m_copyAction->setIcon(KIcon("edit-copy")); + m_copyAction->setText(i18n("Copy as &HTML")); + m_copyAction->setWhatsThis(i18n("Use this command to copy the currently selected text as HTML to the system clipboard.")); + m_copyAction->setEnabled(m_view->selection()); + + m_fileExportAction = actionCollection()->addAction("file_export_html", this, SLOT(exportToFile())); + m_fileExportAction->setText(i18n("E&xport as HTML...")); + m_fileExportAction->setWhatsThis(i18n("This command allows you to export the current document" + " with all highlighting information into a HTML document.")); + + connect(m_view, SIGNAL(selectionChanged(KTextEditor::View*)), + this, SLOT(updateSelectionAction(KTextEditor::View*))); +} + +ExporterPluginView::~ExporterPluginView() +{ +} + +void ExporterPluginView::updateSelectionAction(KTextEditor::View* view) +{ + Q_ASSERT(view == m_view); Q_UNUSED(view) + m_copyAction->setEnabled(m_view->selection()); +} + +void ExporterPluginView::exportToClipboard() +{ + if (!m_view->selection()) { + return; + } + + QMimeData *data = new QMimeData(); + + QString s; + QTextStream output( &s, QIODevice::WriteOnly ); + exportData(true, output); + + data->setHtml(s); + data->setText(s); + + QApplication::clipboard()->setMimeData(data); +} + +void ExporterPluginView::exportToFile() +{ + KUrl url = KFileDialog::getSaveUrl(m_view->document()->documentName(), "text/html", + m_view, i18n("Export File as HTML")); + + if ( url.isEmpty() ) { + return; + } + + QString filename; + + if ( url.isLocalFile() ) { + filename = url.toLocalFile(); + } else { + ///TODO: cleanup! don't let the temp files lay around + KTemporaryFile tmp; // ### only used for network export + tmp.setAutoRemove(false); + tmp.open(); + filename = tmp.fileName(); + } + + KSaveFile savefile(filename); + if (savefile.open()) { + QTextStream outputStream ( &savefile ); + + exportData(false, outputStream); + + savefile.finalize(); //check error? + } +// else +// {/*ERROR*/} + + if ( !url.isLocalFile() ) { + KIO::NetAccess::upload( filename, url, 0 ); + } +} + +void ExporterPluginView::exportData(const bool useSelection, QTextStream &output) +{ + const KTextEditor::Range range = useSelection ? m_view->selectionRange() : m_view->document()->documentRange(); + const bool blockwise = useSelection ? m_view->blockSelection() : false; + + if ( (blockwise || range.onSingleLine()) && (range.start().column() > range.end().column() ) ) { + return; + } + + //outputStream.setEncoding(QTextStream::UnicodeUTF8); + output.setCodec(QTextCodec::codecForName("UTF-8")); + + ///TODO: add more exporters + QScopedPointer exporter; + + exporter.reset(new HTMLExporter(m_view, output, !useSelection)); + + KTextEditor::HighlightInterface* hiface = qobject_cast(m_view->document()); + + const KTextEditor::Attribute::Ptr noAttrib(0); + + for (int i = range.start().line(); (i <= range.end().line()) && (i < m_view->document()->lines()); ++i) + { + const QString &line = m_view->document()->line(i); + + QList attribs; + if ( hiface ) { + attribs = hiface->lineAttributes(i); + } + + int lineStart = 0; + int remainingChars = line.length(); + if ( blockwise || range.onSingleLine() ) { + lineStart = range.start().column(); + remainingChars = range.columnWidth(); + } else if ( i == range.start().line() ) { + lineStart = range.start().column(); + } else if ( i == range.end().line() ) { + remainingChars = range.end().column(); + } + + int handledUntil = lineStart; + + foreach ( const KTextEditor::HighlightInterface::AttributeBlock& block, attribs ) { + // honor (block-) selections + if ( block.start + block.length <= lineStart ) { + continue; + } else if ( block.start >= lineStart + remainingChars ) { + break; + } + int start = qMax(block.start, lineStart); + if ( start > handledUntil ) { + exporter->exportText( line.mid( handledUntil, start - handledUntil ), noAttrib ); + } + int length = qMin(block.length, remainingChars); + exporter->exportText( line.mid( start, length ), block.attribute); + handledUntil = start + length; + } + + if ( handledUntil < lineStart + remainingChars ) { + exporter->exportText( line.mid( handledUntil, remainingChars ), noAttrib ); + } + + exporter->closeLine(i == range.end().line()); + } + + output.flush(); +} + +#include "moc_exporterpluginview.cpp" + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/addons/ktexteditor/exporter/exporterpluginview.h b/kate/addons/ktexteditor/exporter/exporterpluginview.h new file mode 100644 index 00000000..9e3e52a1 --- /dev/null +++ b/kate/addons/ktexteditor/exporter/exporterpluginview.h @@ -0,0 +1,58 @@ +/** + * This file is part of the KDE libraries + * Copyright (C) 2009 Milian Wolff + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License version 2 as published by the Free Software Foundation. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef EXPORTERPLUGINVIEW_H +#define EXPORTERPLUGINVIEW_H + +#include +#include + +#include + +namespace KTextEditor { +class View; +} + +class ExporterPluginView : public QObject, public KXMLGUIClient +{ + Q_OBJECT + + public: + ExporterPluginView(KTextEditor::View* view = 0); + ~ExporterPluginView(); + + private: + ///TODO: maybe make this scriptable for additional exporters? + void exportData(const bool useSelction, QTextStream& output); + + private slots: + void exportToClipboard(); + void exportToFile(); + + void updateSelectionAction(KTextEditor::View *view); + + private: + KTextEditor::View* m_view; + QAction* m_copyAction; + QAction* m_fileExportAction; +}; + +#endif // EXPORTERPLUGINVIEW_H + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/addons/ktexteditor/exporter/htmlexporter.cpp b/kate/addons/ktexteditor/exporter/htmlexporter.cpp new file mode 100644 index 00000000..4c6d7c7f --- /dev/null +++ b/kate/addons/ktexteditor/exporter/htmlexporter.cpp @@ -0,0 +1,119 @@ +/** + * This file is part of the KDE libraries + * Copyright (C) 2009 Milian Wolff + * Copyright (C) 2002 John Firebaugh + * Copyright (C) 2001 Christoph Cullmann + * Copyright (C) 2001 Joseph Wenninger + * Copyright (C) 1999 Jochen Wilhelmy + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License version 2 as published by the Free Software Foundation. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "htmlexporter.h" + +#include + +#include + +HTMLExporter::HTMLExporter(KTextEditor::View* view, QTextStream& output, const bool encapsulate) + : AbstractExporter(view, output, encapsulate) +{ + if ( m_encapsulate ) { + // let's write the HTML header : + m_output << "" << endl; + m_output << "" << endl; + m_output << "" << endl; + m_output << "" << endl; + m_output << "" << endl; + m_output << "" << endl; + // for the title, we write the name of the file (/usr/local/emmanuel/myfile.cpp -> myfile.cpp) + m_output << "" << view->document()->documentName() << "" << endl; + m_output << "" << endl; + m_output << "" << endl; + } + + if ( !m_defaultAttribute ) { + m_output << "
" << endl;
+  } else {
+    m_output << QString("
")
+                  .arg(m_defaultAttribute->fontBold() ? "font-weight:bold;" : "")
+                  .arg(m_defaultAttribute->fontItalic() ? "font-style:italic;" : "")
+                  .arg("color:" + m_defaultAttribute->foreground().color().name() + ';')
+                  .arg("background-color:" + m_defaultAttribute->background().color().name() + ';')
+             << endl;
+  }
+}
+
+HTMLExporter::~HTMLExporter()
+{
+  m_output << "
" << endl; + + if ( m_encapsulate ) { + m_output << "" << endl; + m_output << "" << endl; + } +} + +void HTMLExporter::openLine() +{ +} + +void HTMLExporter::closeLine(const bool lastLine) +{ + if ( !lastLine ) { + //we are inside a
, so a \n is a new line
+    m_output << "\n";
+  }
+}
+
+void HTMLExporter::exportText(const QString& text, const KTextEditor::Attribute::Ptr& attrib)
+{
+  if ( !attrib || !attrib->hasAnyProperty() || attrib == m_defaultAttribute ) {
+    m_output << Qt::escape(text);
+    return;
+  }
+
+  if ( attrib->fontBold() ) {
+    m_output << "";
+  }
+  if ( attrib->fontItalic() ) {
+    m_output << "";
+  }
+
+  bool writeForeground = attrib->hasProperty(QTextCharFormat::ForegroundBrush)
+    && (!m_defaultAttribute || attrib->foreground().color() != m_defaultAttribute->foreground().color());
+  bool writeBackground = attrib->hasProperty(QTextCharFormat::BackgroundBrush)
+    && (!m_defaultAttribute || attrib->background().color() != m_defaultAttribute->background().color());
+
+  if ( writeForeground || writeBackground ) {
+    m_output << QString("")
+                  .arg(writeForeground ? QString(QLatin1String("color:") + attrib->foreground().color().name() + QLatin1Char(';')) : QString())
+                  .arg(writeBackground ? QString(QLatin1String("background:") + attrib->background().color().name() + QLatin1Char(';')) : QString());
+  }
+
+  m_output << Qt::escape(text);
+
+  if ( writeBackground || writeForeground ) {
+    m_output << "";
+  }
+  if ( attrib->fontItalic() ) {
+    m_output << "";
+  }
+  if ( attrib->fontBold() ) {
+    m_output << "";
+  }
+}
+
+// kate: space-indent on; indent-width 2; replace-tabs on;
diff --git a/kate/addons/ktexteditor/exporter/htmlexporter.h b/kate/addons/ktexteditor/exporter/htmlexporter.h
new file mode 100644
index 00000000..1d47acf0
--- /dev/null
+++ b/kate/addons/ktexteditor/exporter/htmlexporter.h
@@ -0,0 +1,39 @@
+/**
+ * This file is part of the KDE libraries
+ * Copyright (C) 2009 Milian Wolff 
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef HTMLEXPORTER_H
+#define HTMLEXPORTER_H
+
+#include "abstractexporter.h"
+
+/// TODO: add abstract interface for future exporters
+class HTMLExporter : public AbstractExporter
+{
+  public:
+    HTMLExporter(KTextEditor::View* view, QTextStream& output, const bool withHeaderFooter = false);
+    virtual ~HTMLExporter();
+
+    virtual void openLine();
+    virtual void closeLine(const bool lastLine);
+    virtual void exportText(const QString& text, const KTextEditor::Attribute::Ptr& attrib);
+};
+
+#endif // HTMLEXPORTER_H
+
+// kate: space-indent on; indent-width 2; replace-tabs on;
diff --git a/kate/addons/ktexteditor/exporter/ktexteditor_exporter.desktop b/kate/addons/ktexteditor/exporter/ktexteditor_exporter.desktop
new file mode 100644
index 00000000..dc009312
--- /dev/null
+++ b/kate/addons/ktexteditor/exporter/ktexteditor_exporter.desktop
@@ -0,0 +1,124 @@
+[Desktop Entry]
+X-KDE-Library=ktexteditor_exporter
+X-KDE-PluginKeyword=ktexteditor_exporter
+X-KDE-PluginInfo-Author=Milian Wolff
+X-KDE-PluginInfo-Email=mail@milianw.de
+X-KDE-PluginInfo-Name=ktexteditorexporter
+X-KDE-PluginInfo-Version=0.1
+X-KDE-PluginInfo-Website=http://kate.kde.org
+X-KDE-PluginInfo-Category=Editor
+X-KDE-PluginInfo-Depends=
+X-KDE-PluginInfo-License=GPL
+X-KDE-PluginInfo-EnabledByDefault=true
+X-KDE-ParentApp=kate
+X-KDE-Version=4.0
+X-KDE-ServiceTypes=KTextEditor/Plugin
+Type=Service
+Icon=ktexteditorexporter
+Name=Exporter
+Name[ar]=مُصدِّر
+Name[bg]=Изнасяне
+Name[bs]=Izvozni program
+Name[ca]=Exportador
+Name[ca@valencia]=Exportador
+Name[cs]=Exportér
+Name[da]=Eksport
+Name[de]=Exporter
+Name[el]=Εξαγωγέας
+Name[en_GB]=Exporter
+Name[es]=Exportador
+Name[et]=Eksport
+Name[eu]=Esportatzailea
+Name[fi]=Vienti
+Name[fr]=Exportation
+Name[ga]=Easpórtálaí
+Name[gl]=Exportador
+Name[he]=מייצא
+Name[hu]=Exportálás
+Name[ia]=Exportator
+Name[it]=Esportazione
+Name[ja]=エクスポーター
+Name[kk]=Экспорттау
+Name[km]=កម្មវិធី​នាំចេញ​
+Name[ko]=내보내기
+Name[lt]=Eksportuotojas
+Name[lv]=Eksportētājs
+Name[mr]=निर्यात करणारा
+Name[nb]=Eksporterer
+Name[nds]=Exporteren
+Name[nl]=Exportprogramma
+Name[pa]=ਐਕਸਪੋਰਟਰ
+Name[pl]=Eksporter
+Name[pt]=Exportação
+Name[pt_BR]=Exportação
+Name[ro]=Exportator
+Name[ru]=Экспорт
+Name[si]=අපනයකය
+Name[sk]=Exportér
+Name[sl]=Izvoznik
+Name[sr]=Извозник
+Name[sr@ijekavian]=Извозник
+Name[sr@ijekavianlatin]=Izvoznik
+Name[sr@latin]=Izvoznik
+Name[sv]=Export
+Name[tg]=Содиркунанда
+Name[tr]=Dışa Aktarıcı
+Name[ug]=چىقارغۇچ
+Name[uk]=Інструмент експортування
+Name[wa]=Ebagueu
+Name[x-test]=xxExporterxx
+Name[zh_CN]=导出器
+Name[zh_TW]=匯出器
+Comment=Export highlighted document to HTML
+Comment[ar]=صدّر المستند المُبرَز إلى HTML
+Comment[bg]=Изнасяне на избраните документи към HTML
+Comment[bs]=Izvezi označeni dokument u HTML
+Comment[ca]=Exporta el document ressaltat a HTML
+Comment[ca@valencia]=Exporta el document ressaltat a HTML
+Comment[cs]=Exportovat zvýrazněný dokument do HTML
+Comment[da]=Eksportér fremhævet dokument til HTML
+Comment[de]=Hervorgehobenes Dokument nach HTML exportieren
+Comment[el]=Εξάγει το τονισμένο έγγραφο σε HTML
+Comment[en_GB]=Export highlighted document to HTML
+Comment[es]=Exportar como HTML el documento resaltado
+Comment[et]=Esiletõstetud dokumenti eksport HTML-i
+Comment[eu]=Esportatu nabarmendutako dokumentua HTMLra
+Comment[fi]=Vie korostetun tiedoston HTML-muodossa
+Comment[fr]=Exporte le document surligné en HTML
+Comment[ga]=Easpórtáil an cháipéis aibhsithe mar HTML
+Comment[gl]=Exportar o documento realzado a HTML
+Comment[he]=מייצא מסמך עם הדגשת תחביר אל HTML
+Comment[hu]=A kiemelt dokumentum exportálása HTML formátumba
+Comment[ia]=Exporta le documento evidentiate a HTML
+Comment[it]=Esporta i documenti evidenziati in HTML
+Comment[kk]=Белгіленген құжатты HTML-ге экспорттау
+Comment[km]=នាំចេញ​ឯកសារ​ដែល​​រំលេច​​ទៅកាន់​​ HTML
+Comment[ko]=구문 강조된 문서를 HTML로 내보내기
+Comment[lt]=Eksportuoti pažymėtą dokumentą į HTML
+Comment[lv]=Eksportēt izcelto dokumentu HTML formātā
+Comment[mr]=ठळक केलेल दस्तऐवज HTML मध्ये निर्यात करा
+Comment[nb]=Eksporter fremhevede dokumenter til HTML
+Comment[nds]=Syntaxmarkeert Dokment as HTML exporteren
+Comment[nl]=Geaccentueerd document exporteren naar HTML
+Comment[pa]=ਹਾਈਲਾਈਟ ਕੀਤਾ ਡੌਕੂਮੈਂਟ HTML ਐਕਸਪੋਰਟ ਕਰੋ
+Comment[pl]=Eksportuj podświetlony dokument do HTML
+Comment[pt]=Exportar o documento com realce para HTML
+Comment[pt_BR]=Exporta o documento selecionado para HTML
+Comment[ro]=Exportă documentul evidențiat în HTML
+Comment[ru]=Экспорт документов в HTML
+Comment[si]=උපුටාගත් ලේඛනය HTML වෙත අපනයනය කරයි
+Comment[sk]=Exportovať zvýraznený dokument do HTML
+Comment[sl]=Izvozite poudarjen dokument v HTML
+Comment[sr]=Извози документе са истицањем у ХТМЛ
+Comment[sr@ijekavian]=Извози документе са истицањем у ХТМЛ
+Comment[sr@ijekavianlatin]=Izvozi dokumente sa isticanjem u HTML
+Comment[sr@latin]=Izvozi dokumente sa isticanjem u HTML
+Comment[sv]=Exportera markerat dokument till HTML
+Comment[tg]=Ҳуҷҷати интихобшуда ба HTML содир кунед
+Comment[tr]=Vurgulanan belgeyi HTML olarak dışa aktar
+Comment[ug]=يورۇتۇلغان پۈتۈكنى HTML گە چىقار
+Comment[uk]=Експорт позначеного документа до HTML
+Comment[wa]=Ebaguer l' documint metou e sorbriyance èn HTML
+Comment[x-test]=xxExport highlighted document to HTMLxx
+Comment[zh_CN]=将高亮的文档导出成 HTML
+Comment[zh_TW]=將突顯的文件匯出為 HTML
diff --git a/kate/addons/ktexteditor/exporter/ktexteditor_exporterui.rc b/kate/addons/ktexteditor/exporter/ktexteditor_exporterui.rc
new file mode 100644
index 00000000..80c309d8
--- /dev/null
+++ b/kate/addons/ktexteditor/exporter/ktexteditor_exporterui.rc
@@ -0,0 +1,14 @@
+
+
+
+
+  &File
+    
+  
+
+  &Edit
+    
+  
+
+
+
diff --git a/kate/addons/ktexteditor/hlselection/CMakeLists.txt b/kate/addons/ktexteditor/hlselection/CMakeLists.txt
new file mode 100644
index 00000000..a3da2ce0
--- /dev/null
+++ b/kate/addons/ktexteditor/hlselection/CMakeLists.txt
@@ -0,0 +1,26 @@
+
+########### next target ###############
+
+kde4_add_plugin(ktexteditor_hlselection hlselectionplugin.cpp)
+
+target_link_libraries(ktexteditor_hlselection
+    ${KDE4_KIO_LIBS}
+    ${KDE4_KTEXTEDITOR_LIBS}
+    ${KDE4_KDEUI_LIBS}
+    ${KDE4_KFILE_LIBS}
+)
+
+install(
+    TARGETS ktexteditor_hlselection
+    DESTINATION ${KDE4_PLUGIN_INSTALL_DIR}
+)
+
+########### install files ###############
+
+install(
+    FILES ktexteditor_hlselection.desktop
+    DESTINATION ${KDE4_SERVICES_INSTALL_DIR}
+)
+
+# Dominik: right now, we do not have any actions, so do not insall ui.rc file
+# install( FILES ktexteditor_hlselectionui.rc  DESTINATION  ${KDE4_DATA_INSTALL_DIR}/ktexteditor_hlselection )
diff --git a/kate/addons/ktexteditor/hlselection/hlselectionplugin.cpp b/kate/addons/ktexteditor/hlselection/hlselectionplugin.cpp
new file mode 100644
index 00000000..d014578e
--- /dev/null
+++ b/kate/addons/ktexteditor/hlselection/hlselectionplugin.cpp
@@ -0,0 +1,190 @@
+/* This file is part of the KDE libraries
+   Copyright (C) 2010 Dominik Haumann 
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public
+   License version 2 as published by the Free Software Foundation.
+
+   This library 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
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public License
+   along with this library; see the file COPYING.LIB.  If not, write to
+   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+   Boston, MA 02110-1301, USA.
+*/
+
+#include "hlselectionplugin.h"
+#include "moc_hlselectionplugin.cpp"
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+
+K_PLUGIN_FACTORY( HighlightSelectionPluginFactory, registerPlugin(); )
+K_EXPORT_PLUGIN( HighlightSelectionPluginFactory( KAboutData( "ktexteditor_insertfile", "ktexteditor_plugins", ki18n("Highlight Selection"), "1.0", ki18n("Highlight Selection"), KAboutData::License_LGPL_V2 ) ) )
+
+//BEGIN HighlightSelectionPlugin
+HighlightSelectionPlugin::HighlightSelectionPlugin( QObject *parent, const QVariantList& )
+  : KTextEditor::Plugin ( parent )
+{
+}
+
+HighlightSelectionPlugin::~HighlightSelectionPlugin()
+{
+}
+
+void HighlightSelectionPlugin::addView(KTextEditor::View *view)
+{
+  HighlightSelectionPluginView *nview = new HighlightSelectionPluginView (view);
+  m_views.append (nview);
+}
+
+void HighlightSelectionPlugin::removeView(KTextEditor::View *view)
+{
+  foreach (HighlightSelectionPluginView *pluginView, m_views) {
+    if (pluginView->view() == view) {
+      m_views.removeAll(pluginView);
+      delete pluginView;
+      break;
+    }
+  }
+}
+//END HighlightSelectionPlugin
+
+//BEGIN HighlightSelectionPluginView
+HighlightSelectionPluginView::HighlightSelectionPluginView( KTextEditor::View *view)
+  : QObject( view )
+//   , KXMLGUIClient( view ) // XMLGUI stuff not needed right now
+{
+  setObjectName("highlight-selection-plugin");
+
+  m_view = view;
+
+  // we don't need any XMLGUI stuff, so comment out
+//  setComponentData( HighlightSelectionPluginFactory::componentData() );
+//  setXMLFile( "ktexteditor_hlselectionui.rc" );
+
+  connect(view, SIGNAL(selectionChanged(KTextEditor::View*)), this, SLOT(selectionChanged()));
+  connect(view->document(), SIGNAL(aboutToReload(KTextEditor::Document*)), this, SLOT(clearHighlights()));
+}
+
+HighlightSelectionPluginView::~HighlightSelectionPluginView()
+{
+  clearHighlights();
+}
+
+KTextEditor::View* HighlightSelectionPluginView::view() const
+{
+  return m_view;
+}
+
+void HighlightSelectionPluginView::clearHighlights()
+{
+  qDeleteAll(m_ranges);
+  m_ranges.clear();
+  m_currentText.clear();
+}
+
+void HighlightSelectionPluginView::selectionChanged()
+{
+  QString text;
+  // if text of selection is still the same, abort
+  if (m_view->selection() && m_view->selectionRange().onSingleLine()) {
+    text = m_view->selectionText();
+    if (text == m_currentText) {
+      return;
+    }
+  }
+
+  // text changed: remove all highlights + create new ones
+  // (do not call clearHighlights(), since this also resets the m_currentText
+  qDeleteAll(m_ranges);
+  m_ranges.clear();
+  
+  // do not highlight strings with leading and trailing spaces
+  if (!text.isEmpty() && (text.at(0).isSpace() || text.at(text.length()-1).isSpace())) {
+    return; 
+  }
+
+  m_currentText = text;
+  if (!m_currentText.isEmpty()) {
+    createHighlights();
+  }
+}
+
+void HighlightSelectionPluginView::createHighlights()
+{
+  m_currentText = m_view->selectionText();
+
+  KTextEditor::SearchInterface* siface =
+    qobject_cast(m_view->document());
+
+  if (!siface) {
+    return;
+  }
+
+  KTextEditor::MovingInterface* miface =
+    qobject_cast(m_view->document());
+
+  KTextEditor::Attribute::Ptr attr(new KTextEditor::Attribute());
+// disable bold for now: If you use non-fixed font, making it bold leads to wobbly text
+//  attr->setFontBold(true);
+  attr->setBackground(Qt::yellow);
+
+  // set correct highlight color from Kate's color schema
+  KTextEditor::ConfigInterface* ciface =
+    qobject_cast(m_view);
+  if (ciface) {
+    QColor color = ciface->configValue("search-highlight-color").value();
+    attr->setBackground(color);
+  }
+
+  KTextEditor::Cursor start(0, 0);
+  KTextEditor::Range searchRange;
+
+  /**
+   * only add word boundary if we can find the text then
+   * fixes $lala hl
+   */
+  QString regex = QRegExp::escape (m_currentText);
+  if (QRegExp (QString ("\\b%1").arg(regex)).indexIn (QString (" %1 ").arg(m_currentText)) != -1)
+    regex = QString ("\\b%1").arg(regex);
+  if (QRegExp (QString ("%1\\b").arg(regex)).indexIn (QString (" %1 ").arg(m_currentText)) != -1)
+    regex = QString ("%1\\b").arg(regex);
+
+  QVector matches;
+  do {
+    searchRange.setRange(start, m_view->document()->documentEnd());
+
+    matches = siface->searchText(searchRange, regex, KTextEditor::Search::Regex);
+
+    if (matches.first().isValid()) {
+      KTextEditor::MovingRange* mr = miface->newMovingRange(matches.first());
+      mr->setAttribute(attr);
+      mr->setView(m_view);
+      mr->setZDepth(-90000.0); // Set the z-depth to slightly worse than the selection
+      mr->setAttributeOnlyForViews(true);
+      m_ranges.append(mr);
+      start = matches.first().end();
+    }
+  } while (matches.first().isValid());
+}
+//END HighlightSelectionPluginView
+
+// kate: space-indent on; indent-width 2; replace-tabs on;
diff --git a/kate/addons/ktexteditor/hlselection/hlselectionplugin.h b/kate/addons/ktexteditor/hlselection/hlselectionplugin.h
new file mode 100644
index 00000000..c5756558
--- /dev/null
+++ b/kate/addons/ktexteditor/hlselection/hlselectionplugin.h
@@ -0,0 +1,77 @@
+/* This file is part of the KDE libraries
+   Copyright (C) 2010 Dominik Haumann 
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public
+   License version 2 as published by the Free Software Foundation.
+
+   This library 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
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public License
+   along with this library; see the file COPYING.LIB.  If not, write to
+   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+   Boston, MA 02110-1301, USA.
+ */
+
+#ifndef _HIGHLIGHT_SELECTION_PLUGIN_H_
+#define _HIGHLIGHT_SELECTION_PLUGIN_H_
+
+#include 
+#include 
+
+// #include 
+#include 
+#include 
+#include 
+#include 
+
+namespace KTextEditor {
+  class MovingRange;
+}
+
+class HighlightSelectionPlugin : public KTextEditor::Plugin
+{
+  Q_OBJECT
+
+  public:
+    explicit HighlightSelectionPlugin( QObject *parent = 0,
+                      const QVariantList &args = QVariantList() );
+    virtual ~HighlightSelectionPlugin();
+
+    void addView (KTextEditor::View *view);
+    void removeView (KTextEditor::View *view);
+
+
+  private:
+    QList m_views;
+};
+
+class HighlightSelectionPluginView
+  : public QObject
+//   , public KXMLGUIClient
+{
+  Q_OBJECT
+  public:
+    explicit HighlightSelectionPluginView(KTextEditor::View *view);
+    ~HighlightSelectionPluginView();
+
+    void createHighlights();
+    KTextEditor::View* view() const;
+
+  public Q_SLOTS:
+    void selectionChanged();
+    void clearHighlights();
+
+  private:
+    KTextEditor::View* m_view;
+    QString m_currentText;
+
+    QList m_ranges;
+};
+
+#endif // _HIGHLIGHT_SELECTION_PLUGIN_H_
+
+// kate: space-indent on; indent-width 2; replace-tabs on;
diff --git a/kate/addons/ktexteditor/hlselection/ktexteditor_hlselection.desktop b/kate/addons/ktexteditor/hlselection/ktexteditor_hlselection.desktop
new file mode 100644
index 00000000..5d9b463f
--- /dev/null
+++ b/kate/addons/ktexteditor/hlselection/ktexteditor_hlselection.desktop
@@ -0,0 +1,121 @@
+[Desktop Entry]
+X-KDE-Library=ktexteditor_hlselection
+X-KDE-PluginInfo-Author=
+X-KDE-PluginInfo-Email=
+X-KDE-PluginInfo-Name=ktexteditorhlselection
+X-KDE-PluginInfo-Version=0.1
+X-KDE-PluginInfo-Website=http://kate.kde.org
+X-KDE-PluginInfo-Category=Editor
+X-KDE-PluginInfo-Depends=
+X-KDE-PluginInfo-License=GPL
+X-KDE-PluginInfo-EnabledByDefault=true
+X-KDE-ParentApp=kate
+X-KDE-Version=4.3
+X-KDE-ServiceTypes=KTextEditor/Plugin
+Type=Service
+Icon=document-import
+Name=Highlight Selection
+Name[ar]=أبرِز التحديد
+Name[bg]=Осветяване на избраното
+Name[bs]=Označi izbor
+Name[ca]=Selecció de ressaltat
+Name[ca@valencia]=Selecció de ressaltat
+Name[cs]=Zvýraznění výběru
+Name[da]=Fremhæv markering
+Name[de]=Auswahl hervorheben
+Name[el]=Επιλογή έμφασης
+Name[en_GB]=Highlight Selection
+Name[es]=Resaltar selección
+Name[et]=Valiku esiletõstmine
+Name[eu]=Nabarmendu hautapena
+Name[fi]=Valinnan korostus
+Name[fr]=Mettre la sélection en surbrillance
+Name[ga]=Aibhsigh Roghnúchán
+Name[gl]=Realzar selección
+Name[he]=הדגש בחירה
+Name[hu]=Kiemeléskijelölés
+Name[ia]=Evidentia selection
+Name[it]=Evidenzia selezione
+Name[kk]=Бояп белгілеу
+Name[km]=ជម្រើស​​រំលេច
+Name[ko]=선택 강조
+Name[lt]=Paryškinti pažymėjimą
+Name[lv]=Izcelt izvēli
+Name[mr]=निवड ठळक करा
+Name[nb]=Fremhev utvalg
+Name[nds]=Köör rutheven
+Name[nl]=Accentueren van selectie
+Name[pa]=ਚੋਣ ਹਾਈਲਾਈਟ
+Name[pl]=Podświetlanie wyboru
+Name[pt]=Realçar a Selecção
+Name[pt_BR]=Destacar a seleção
+Name[ro]=Evidențiază selecția
+Name[ru]=Подсветка найденного
+Name[si]=තෝරාගැනීම උපුටන්න
+Name[sk]=Zvýrazniť výber
+Name[sl]=Poudari izbiro
+Name[sr]=Истицање избора
+Name[sr@ijekavian]=Истицање избора
+Name[sr@ijekavianlatin]=Isticanje izbora
+Name[sr@latin]=Isticanje izbora
+Name[sv]=Färglägg markering
+Name[tg]=Равшанкунии интихоб
+Name[tr]=Seçimi Vurgula
+Name[ug]=تاللىغاننى يورۇت
+Name[uk]=Підсвічування позначеного
+Name[wa]=Mete en sorbriyance li tchoes
+Name[x-test]=xxHighlight Selectionxx
+Name[zh_CN]=加亮选择区
+Name[zh_TW]=突顯選取區
+Comment=Highlight all words based on the text selection
+Comment[ar]=أبرِز كلّ الكلمات بناءً على تحديد النصّ
+Comment[bs]=Označi sve riječi na bazi izbora teksta
+Comment[ca]=Ressalta totes les paraules a partir de la selecció de text
+Comment[ca@valencia]=Ressalta totes les paraules a partir de la selecció de text
+Comment[cs]=Zvýraznit všechna slova podle vybraného textu
+Comment[da]=Fremhæv alle ord baseret på tekstmarkeringen
+Comment[de]=Alle Wörter anhand der aktuellen Textauswahl hervorheben
+Comment[el]=Τονίζει όλες τις λέξεις με βάση την επιλογή κειμένου
+Comment[en_GB]=Highlight all words based on the text selection
+Comment[es]=Resaltar todas las palabras basadas en el texto seleccionado
+Comment[et]=Kõigi sõnade esiletõstmine vastavalt valitud tekstile
+Comment[eu]=Nabarmendu hitz guztiak testuaren hautapenean oinarrituta
+Comment[fi]=Korosta kaikki sanat tekstivalinnan perusteella
+Comment[fr]=Met en surbrillance tous les mots reposant sur la sélection de texte
+Comment[ga]=Aibhsigh gach focal bunaithe ar an roghnúchán téacs
+Comment[gl]=Realzar todas as palabras baseadas no texto seleccionado
+Comment[he]=מדגיש את כל המילים המבוססות על בחירת טקסט
+Comment[hu]=Az összes szó kiemelése a szövegkijelölés alapján
+Comment[ia]=Evidentia omne parolas basate sur le selection de texto
+Comment[it]=Evidenzia tutte le parole nella selezione
+Comment[kk]=Таңдаған мәтіннің бүкіл сөздрін бояп белгілеу
+Comment[km]=រំលេច​គ្រប់​ពាក្យ​ទាំងអស់​ដែល​មាន​មូលដ្ឋាន​លើ​ជម្រើស​អត្ថបទ​​​
+Comment[ko]=선택한 텍스트 강조
+Comment[lt]=Paryškinti visus žodžius priklausomai nuo teksto pažymėjimo
+Comment[lv]=Izcelt visus vārdus, balstoties uz izvēlēto tekstu
+Comment[mr]=पाठ्य निवड आधारित सर्व शब्द ठळक करा
+Comment[nb]=Fremhev alle ord basert på tekstutvalget
+Comment[nds]=All Wöör rutheven, de op disse Textköör baseert
+Comment[nl]=Alle woorden accentueren gebaseerd op de selectie van de tekst
+Comment[pa]=ਟੈਕਸਟ ਚੋਣ ਉੱਤੇ ਅਧਾਰਿਤ ਸਭ ਸ਼ਬਦ ਹਾਈਲਾਈਟ ਕਰੋ
+Comment[pl]=Podświetl wszystkie słowa bazując na zaznaczeniu tekstu
+Comment[pt]=Realçar todas as palavras com base na selecção de texto
+Comment[pt_BR]=Destaca todas as palavras baseadas na seleção de texto
+Comment[ro]=Evidențiază toate cuvintele folosind textul selectat.
+Comment[ru]=Подсветить все вхождения выделенного текста
+Comment[si]=පෙළ තෝරාගැනීම මත පදනම්වූ සියළු වදන් උපුටාගනී
+Comment[sk]=Zvýrazniť všetky slová založené na výbere textu
+Comment[sl]=Poudari vse besede glede na izbrano besedilo
+Comment[sr]=Истицање свих речи на основу текстуалног избора
+Comment[sr@ijekavian]=Истицање свих речи на основу текстуалног избора
+Comment[sr@ijekavianlatin]=Isticanje svih reči na osnovu tekstualnog izbora
+Comment[sr@latin]=Isticanje svih reči na osnovu tekstualnog izbora
+Comment[sv]=Färglägg alla ord baserat på textmarkeringen
+Comment[tg]=Ҳамаи калимаҳои матни интихобшударо равшан кунед
+Comment[tr]=Metin seçimine bağlı tüm kelimeleri vurgula
+Comment[ug]=تاللانغان تېكىستتە ھەممە سۆزنى يورۇت
+Comment[uk]=Підсвічування всіх слів у позначеному фрагменті тексту
+Comment[wa]=Mete en sorbriyance tos les mots båzé sol tchoes e tecse
+Comment[x-test]=xxHighlight all words based on the text selectionxx
+Comment[zh_CN]=根据选择的文本高亮全部单词
+Comment[zh_TW]=突顯選取區內所有單字
diff --git a/kate/addons/ktexteditor/hlselection/ktexteditor_hlselectionui.rc b/kate/addons/ktexteditor/hlselection/ktexteditor_hlselectionui.rc
new file mode 100644
index 00000000..ce41a04c
--- /dev/null
+++ b/kate/addons/ktexteditor/hlselection/ktexteditor_hlselectionui.rc
@@ -0,0 +1,9 @@
+
+
+
+ &Tools
+    
+    
+ 
+
+
diff --git a/kate/addons/ktexteditor/insertfile/CMakeLists.txt b/kate/addons/ktexteditor/insertfile/CMakeLists.txt
new file mode 100644
index 00000000..b9a8305b
--- /dev/null
+++ b/kate/addons/ktexteditor/insertfile/CMakeLists.txt
@@ -0,0 +1,27 @@
+
+########### next target ###############
+
+kde4_add_plugin(ktexteditor_insertfile insertfileplugin.cpp)
+
+target_link_libraries(ktexteditor_insertfile
+    ${KDE4_KIO_LIBS}
+    ${KDE4_KTEXTEDITOR_LIBS}
+    ${KDE4_KDEUI_LIBS}
+    ${KDE4_KFILE_LIBS}
+)
+
+install(
+    TARGETS ktexteditor_insertfile
+    DESTINATION ${KDE4_PLUGIN_INSTALL_DIR}
+)
+
+########### install files ###############
+
+install(
+    FILES ktexteditor_insertfile.desktop
+    DESTINATION ${KDE4_SERVICES_INSTALL_DIR}
+)
+install(
+    FILES ktexteditor_insertfileui.rc
+    DESTINATION ${KDE4_DATA_INSTALL_DIR}/ktexteditor_insertfile
+)
diff --git a/kate/addons/ktexteditor/insertfile/insertfileplugin.cpp b/kate/addons/ktexteditor/insertfile/insertfileplugin.cpp
new file mode 100644
index 00000000..760ba836
--- /dev/null
+++ b/kate/addons/ktexteditor/insertfile/insertfileplugin.cpp
@@ -0,0 +1,193 @@
+/* This file is part of the KDE libraries
+   Copyright (C) 2002 Anders Lund 
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public
+   License version 2 as published by the Free Software Foundation.
+
+   This library 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
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public License
+   along with this library; see the file COPYING.LIB.  If not, write to
+   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+   Boston, MA 02110-1301, USA.
+*/
+
+#include "insertfileplugin.h"
+#include "moc_insertfileplugin.cpp"
+
+#include 
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include 
+#include 
+
+K_PLUGIN_FACTORY( InsertFilePluginFactory, registerPlugin(); )
+K_EXPORT_PLUGIN( InsertFilePluginFactory( KAboutData( "ktexteditor_insertfile", "ktexteditor_plugins", ki18n("Insert File"), "0.1", ki18n("Insert File"), KAboutData::License_LGPL_V2 ) ) )
+
+//BEGIN InsertFilePlugin
+InsertFilePlugin::InsertFilePlugin( QObject *parent, const QVariantList& )
+	: KTextEditor::Plugin ( parent )
+{
+}
+
+InsertFilePlugin::~InsertFilePlugin()
+{
+}
+
+void InsertFilePlugin::addView(KTextEditor::View *view)
+{
+  InsertFilePluginView *nview = new InsertFilePluginView (view, "Insert File Plugin");
+  m_views.append (nview);
+}
+
+void InsertFilePlugin::removeView(KTextEditor::View *view)
+{
+    int z=0;
+    // Loop written for the unlikely case of a view being added more than once
+    while (z < m_views.count())
+    {
+      InsertFilePluginView *nview = m_views.at(z);
+      if (nview->parentClient() == view)
+      {
+         m_views.removeAll (nview);
+         delete nview;
+      }
+      else
+         ++z;
+    }
+}
+//END InsertFilePlugin
+
+//BEGIN InsertFilePluginView
+InsertFilePluginView::InsertFilePluginView( KTextEditor::View *view, const char *name )
+  : QObject( view ),
+    KXMLGUIClient( view )
+{
+  setObjectName( name );
+
+  setComponentData( InsertFilePluginFactory::componentData() );
+  _job = 0;
+
+  KAction *action = new KAction( i18n("Insert File..."), this );
+  actionCollection()->addAction( "tools_insert_file", action );
+  connect( action, SIGNAL(triggered(bool)), this, SLOT(slotInsertFile()) );
+
+  setXMLFile( "ktexteditor_insertfileui.rc" );
+}
+
+void InsertFilePluginView::slotInsertFile()
+{
+  KFileDialog dlg( KUrl( "kfiledialog:///insertfile?global" ), "", (QWidget*)parent());
+  dlg.setOperationMode( KFileDialog::Opening );
+
+  dlg.setCaption(i18n("Choose File to Insert"));
+  dlg.okButton()->setText(i18n("&Insert"));
+  dlg.setMode( KFile::File );
+  dlg.exec();
+
+  _file = dlg.selectedUrl().url();
+  if ( _file.isEmpty() ) return;
+
+  if ( _file.isLocalFile() ) {
+    _tmpfile = _file.toLocalFile();
+    insertFile();
+  }
+  else {
+    KTemporaryFile tempFile;
+    tempFile.setAutoRemove(false);
+    tempFile.open();
+    _tmpfile = tempFile.fileName();
+
+    KUrl destURL;
+    destURL.setPath( _tmpfile );
+    _job = KIO::file_copy( _file, destURL, 0600, KIO::Overwrite );
+    connect( _job, SIGNAL(result(KJob*)), this, SLOT(slotFinished(KJob*)) );
+  }
+}
+
+void InsertFilePluginView::slotFinished( KJob *job )
+{
+  assert( job == _job );
+  _job = 0;
+  if ( job->error() )
+    KMessageBox::error( (QWidget*)parent(), i18n("Failed to load file:\n\n") + job->errorString(), i18n("Insert File Error") );
+  else
+    insertFile();
+}
+
+void InsertFilePluginView::insertFile()
+{
+  QString error;
+  if ( _tmpfile.isEmpty() )
+    return;
+
+  QFileInfo fi;
+  fi.setFile( _tmpfile );
+  if (!fi.exists() || !fi.isReadable())
+    error = i18n("

The file %1 does not exist or is not readable, aborting.

", _file.fileName()); + + QFile f( _tmpfile ); + if ( !f.open(QIODevice::ReadOnly) ) + error = i18n("

Unable to open file %1, aborting.

", _file.fileName()); + + if ( ! error.isEmpty() ) { + KMessageBox::sorry( (QWidget*)parent(), error, i18n("Insert File Error") ); + return; + } + + // now grab file contents + QTextStream stream(&f); + QString str, tmp; + uint numlines = 0; + uint len = 0; + while (!stream.atEnd()) { + if ( numlines ) + str += '\n'; + tmp = stream.readLine(); + str += tmp; + len = tmp.length(); + numlines++; + } + f.close(); + + if ( str.isEmpty() ) + error = i18n("

File %1 had no contents.

", _file.fileName()); + if ( ! error.isEmpty() ) { + KMessageBox::sorry( (QWidget*)parent(), error, i18n("Insert File Error") ); + return; + } + + // insert !! + KTextEditor::View *v = (KTextEditor::View*)parent(); + int line, col; + line = v->cursorPosition().line(); + col = v->cursorPosition().column(); + v->document()->insertText( v->cursorPosition(), str ); + + // move the cursor + v->setCursorPosition ( KTextEditor::Cursor (line + numlines - 1, numlines > 1 ? len : col + len) ); + + // clean up + _file = KUrl (); + _tmpfile.truncate( 0 ); +} + +//END InsertFilePluginView + diff --git a/kate/addons/ktexteditor/insertfile/insertfileplugin.h b/kate/addons/ktexteditor/insertfile/insertfileplugin.h new file mode 100644 index 00000000..1d9f4506 --- /dev/null +++ b/kate/addons/ktexteditor/insertfile/insertfileplugin.h @@ -0,0 +1,67 @@ +/* This file is part of the KDE libraries + Copyright (C) 2002 Anders Lund + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + */ + +#ifndef _INSERT_FILE_PLUGIN_H_ +#define _INSERT_FILE_PLUGIN_H_ + +#include +#include + +#include +#include +#include +#include +#include + +class InsertFilePlugin : public KTextEditor::Plugin +{ + Q_OBJECT + + public: + explicit InsertFilePlugin( QObject *parent = 0, + const QVariantList &args = QVariantList() ); + virtual ~InsertFilePlugin(); + + void addView (KTextEditor::View *view); + void removeView (KTextEditor::View *view); + + + private: + QList m_views; +}; + +class InsertFilePluginView : public QObject, public KXMLGUIClient +{ + Q_OBJECT + public: + explicit InsertFilePluginView( KTextEditor::View *view, const char *name=0 ); + ~InsertFilePluginView() {} + public Q_SLOTS: + /* display a file dialog, and insert the chosen file */ + void slotInsertFile(); + private Q_SLOTS: + void slotFinished( KJob *job ); + //slotAborted( KIO::Job *job ); + private: + void insertFile(); + KUrl _file; + QString _tmpfile; + KIO::FileCopyJob *_job; +}; + +#endif // _INSERT_FILE_PLUGIN_H_ diff --git a/kate/addons/ktexteditor/insertfile/ktexteditor_insertfile.desktop b/kate/addons/ktexteditor/insertfile/ktexteditor_insertfile.desktop new file mode 100644 index 00000000..f8d9f072 --- /dev/null +++ b/kate/addons/ktexteditor/insertfile/ktexteditor_insertfile.desktop @@ -0,0 +1,124 @@ +[Desktop Entry] +X-KDE-Library=ktexteditor_insertfile +X-KDE-PluginInfo-Author= +X-KDE-PluginInfo-Email= +X-KDE-PluginInfo-Name=ktexteditorinsertfile +X-KDE-PluginInfo-Version=0.1 +X-KDE-PluginInfo-Website=http://kate.kde.org +X-KDE-PluginInfo-Category=Editor +X-KDE-PluginInfo-Depends= +X-KDE-PluginInfo-License=GPL +X-KDE-PluginInfo-EnabledByDefault=false +X-KDE-ParentApp=kate +X-KDE-Version=4.0 +X-KDE-ServiceTypes=KTextEditor/Plugin +Type=Service +Icon=document-import +Name=Insert File +Name[ar]=أدرج ملفًا +Name[bg]=Вмъкване на файл +Name[bs]=Ubaci datoteku +Name[ca]=Inserció de fitxer +Name[ca@valencia]=Inserció de fitxer +Name[cs]=Vložit soubor +Name[da]=Indsæt fil +Name[de]=Datei einfügen +Name[el]=Εισαγωγή αρχείου +Name[en_GB]=Insert File +Name[es]=Insertar archivo +Name[et]=Faililisaja +Name[eu]=Txertatu fitxategia +Name[fi]=Tiedoston lisäys +Name[fr]=Insérer un fichier +Name[ga]=Ionsáigh Comhad +Name[gl]=Inserir un ficheiro +Name[he]=הוסף קובץ +Name[hu]=Fájlbeszúrás +Name[ia]=Inserta file +Name[it]=Inserisci file +Name[ja]=フィルタを挿入 +Name[kk]=Файлды ендіру +Name[km]=បញ្ចូល​ឯកសារ​ +Name[ko]=파일 삽입 +Name[lt]=Įterpti failą +Name[lv]=Ievietot datni +Name[mr]=फाईल अंतर्भूत करा +Name[ms]=Selit Fail +Name[nb]=Sett inn fil +Name[nds]=Datei infögen +Name[nl]=Bestand invoegen +Name[pa]=ਫਾਇਲ ਸ਼ਾਮਲ +Name[pl]=Wstaw plik +Name[pt]=Inserir um Ficheiro +Name[pt_BR]=Inserir arquivo +Name[ro]=Inserează fișier +Name[ru]=Вставка файла +Name[si]=ගොනුව ඇතුළත් කරන්න +Name[sk]=Vložiť súbor +Name[sl]=Vstavi datoteko +Name[sr]=Уметање фајла +Name[sr@ijekavian]=Уметање фајла +Name[sr@ijekavianlatin]=Umetanje fajla +Name[sr@latin]=Umetanje fajla +Name[sv]=Infoga fil +Name[tg]=Дарҷ кардани файл +Name[tr]=Dosya Ekle +Name[ug]=ھۆججەت قىستۇر +Name[uk]=Вставити файл +Name[wa]=Sititchî fitchî +Name[x-test]=xxInsert Filexx +Name[zh_CN]=插入文件 +Name[zh_TW]=插入檔案 +Comment=Insert any readable file at cursor position +Comment[ar]=أدرج أيّ ملف قابل للقراءة عند موقع المؤشّر +Comment[bg]=Вмъкване на четим файл на мястото на курсора +Comment[bs]=Ubaci neku čitljivu datoteku na poziciji kursora +Comment[ca]=Inserir qualsevol fitxer llegible en la posició del cursor +Comment[ca@valencia]=Inserir qualsevol fitxer llegible en la posició del cursor +Comment[cs]=Vloží jakýkoliv čitelný soubor na místo kurzoru +Comment[da]=Indsæt en vilkårlig læsbar fil ved markørens position +Comment[de]=Beliebige lesbare Datei an Cursor-Position einfügen +Comment[el]=Εισαγωγή οποιουδήποτε αναγνώσιμου αρχείου στη θέση του δρομέα +Comment[en_GB]=Insert any readable file at cursor position +Comment[es]=Insertar un archivo legible en la posición del cursor +Comment[et]=Suvalise loetava faili lisamine kursori asukohta +Comment[eu]=Txertatu edozein fitxategi irakurgarri kurtsorearen posizioan +Comment[fi]=Lisää mikä tahansa luettavissa oleva tiedosto kohdistimen kohtaan +Comment[fr]=Insère n'importe quel fichier lisible à la position du curseur +Comment[ga]=Ionsáigh aon chomhad inléite ag an gcúrsóir +Comment[gl]=Inserir calquera ficheiro lexíbel na posición do cursor +Comment[he]=הכנס כל קובץ אל מיקום הסמן +Comment[hu]=Tetszőleges olvasható fájl beszúrása a kurzorpozíciónál +Comment[ia]=Inserta omne le legibile file al position del cursor +Comment[it]=Inserisci un qualsiasi file leggibile a partire dal cursore +Comment[kk]=Меңзер көрсететін орынға кез-келген оқылатын файлды ендіру +Comment[km]=បញ្ចូល​ឯកសារ​ដែល​អាច​អាន​បាន​មួយ​ចំនួន​នៅ​ទីតាំង​ទស្សន៍ទ្រនិច​ +Comment[ko]=커서 위치에 읽을 수 있는 파일 삽입 +Comment[lt]=Įterpti bet kokį perskaitomą failą žymeklio vietoje +Comment[lv]=Ievietot jebkuru nolasāmu datni kursora pozīcijā +Comment[mr]=कर्सर स्थानावर वाचण्याजोगी कोणतीही फाईल अंतर्भूत करा +Comment[nb]=Sett inn en lesbar fil ved skrivemerket +Comment[nds]=Jichtenseen leesbore Datei bi'n Blinker infögen +Comment[nl]=Voeg een willekeurig leesbaar bestand in op de cursorpositie +Comment[pa]=ਕੋਈ ਪੜ੍ਹਨਯੋਗ ਫਾਇਲ ਕਰਸਰ ਸਥਿਤੀ ਤੇ ਸ਼ਾਮਿਲ ਕਰੋ +Comment[pl]=Wstaw jakikolwiek odczytywalny plik na pozycji kursora +Comment[pt]=Inserir qualquer ficheiro legível na posição do cursor +Comment[pt_BR]=Insere qualquer arquivo com permissões de leitura na posição do cursor +Comment[ro]=Inserează orice fișier citibil la poziția cursorului +Comment[ru]=Вставка любого читаемого файла в позицию курсора +Comment[si]=සැරිත්ත ඇති ස්ථානයේ ඇති ඕනෑම කියවිය හැකි ගොනුවක් ඇතුළු කරන්න +Comment[sk]=Vložiť akýkoľvek čitateľný súbor na pozíciu kurzora +Comment[sl]=Vstavi poljubno datoteko datoteko na mesto kazalke +Comment[sr]=Умеће било који читљиви фајл на положају курсора +Comment[sr@ijekavian]=Умеће било који читљиви фајл на положају курсора +Comment[sr@ijekavianlatin]=Umeće bilo koji čitljivi fajl na položaju kursora +Comment[sr@latin]=Umeće bilo koji čitljivi fajl na položaju kursora +Comment[sv]=Infoga vilken läsbar fil som helst vid markörens plats +Comment[tg]=Ягон файли хондашавандаро дар ҷои курсор дарҷ кунед +Comment[tr]=Okunabilir bir dosya içeriğini imleç konumuna ekle +Comment[ug]=نۇربەلگە ئورنىغا خالىغان ئوقۇشچان ھۆججەتنى قىستۇر +Comment[uk]=Вставляє вміст будь-якого файла, який можна прочитати, у позицію курсора +Comment[wa]=Sititchî tot l' minme li ké fitchî lijhåve al pôzucion do cursoe +Comment[x-test]=xxInsert any readable file at cursor positionxx +Comment[zh_CN]=在光标位置插入可读文件 +Comment[zh_TW]=在游標處插入任意的可讀檔案 diff --git a/kate/addons/ktexteditor/insertfile/ktexteditor_insertfileui.rc b/kate/addons/ktexteditor/insertfile/ktexteditor_insertfileui.rc new file mode 100644 index 00000000..c3ad9b17 --- /dev/null +++ b/kate/addons/ktexteditor/insertfile/ktexteditor_insertfileui.rc @@ -0,0 +1,9 @@ + + + + &Tools + + + + + diff --git a/kate/addons/ktexteditor/kte_iconinserter/CMakeLists.txt b/kate/addons/ktexteditor/kte_iconinserter/CMakeLists.txt new file mode 100644 index 00000000..93096594 --- /dev/null +++ b/kate/addons/ktexteditor/kte_iconinserter/CMakeLists.txt @@ -0,0 +1,20 @@ +kde4_add_plugin(ktexteditor_iconinserter iconinserterplugin.cpp) + +target_link_libraries(ktexteditor_iconinserter + ${KDE4_KDEUI_LIBS} + ${KDE4_KTEXTEDITOR_LIBS} +) + +install( + TARGETS ktexteditor_iconinserter + DESTINATION ${KDE4_PLUGIN_INSTALL_DIR} +) + +install( + FILES ktexteditor_iconinserter.desktop + DESTINATION ${KDE4_SERVICES_INSTALL_DIR} +) +install( + FILES ktexteditor_iconinserterui.rc + DESTINATION ${KDE4_DATA_INSTALL_DIR}/ktexteditor_iconinserter +) diff --git a/kate/addons/ktexteditor/kte_iconinserter/iconinserterplugin.cpp b/kate/addons/ktexteditor/kte_iconinserter/iconinserterplugin.cpp new file mode 100644 index 00000000..ba9e9592 --- /dev/null +++ b/kate/addons/ktexteditor/kte_iconinserter/iconinserterplugin.cpp @@ -0,0 +1,129 @@ +/**************************************************************************** + * This file is part of the KTextEditor-Icon-Inserter-Plugin * + * Copyright 2009-2010 Jonathan Schmidt-Dominé * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Library General Public License version * + * 3, or (at your option) any later version, as published by the Free * + * Software Foundation. * + * * + * This library 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 * + * Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with the kdelibs library; see the file COPYING.LIB. If * + * not, write to the Free Software Foundation, Inc., 51 Franklin Street, * + * Fifth Floor, Boston, MA 02110-1301, USA. or see * + * . * + ****************************************************************************/ + +#include "iconinserterplugin.h" + +#include + +#include +#include +#include +#include +#include +#include + + + +K_PLUGIN_FACTORY_DEFINITION(IconInserterPluginFactory, + registerPlugin("ktexteditor_iconinserter"); + ) +K_EXPORT_PLUGIN(IconInserterPluginFactory(KAboutData ("ktexteditor_iconinserter","ktexteditor_iconinserter", ki18n ("Select an Icon to use it inside the Code"), "0.1", ki18n ("Insert Code for KIcon-Creation"), KAboutData::License_LGPL_V3))) + + +IconInserterPluginView::IconInserterPluginView(IconInserterPlugin *plugin, KTextEditor::View *view): QObject(plugin),KXMLGUIClient(view),m_view(view) +{ + setComponentData( IconInserterPluginFactory::componentData() ); + setXMLFile("ktexteditor_iconinserterui.rc"); + QAction *a=actionCollection()->addAction("iconinserter_inserticon",this,SLOT(insertIcon())); + a->setIcon(KIcon("insert-image")); + a->setText(i18n ("Insert KIcon-Code")); + a->setToolTip (i18n ("Insert Code for KIcon-Creation")); + a->setWhatsThis (i18n ("IconInserter

Select an icon and use it as a KIcon in your source code.")); +} + +IconInserterPluginView::~IconInserterPluginView() +{ +} + + +void IconInserterPluginView::insertIcon() +{ + if (m_view.isNull()) return; + QString iconName = KIconDialog::getIcon ( KIconLoader::Desktop, + KIconLoader::Application, + false, + 0, + false, + 0, + i18n( "Select the Icon you want to use in your code as KIcon." ) + ); + if(iconName.isEmpty()) + return; + + View *view=m_view.data(); + Document *doc=view->document(); + + + QString suffix = doc->url().url(); + suffix = suffix.right(suffix.size() - suffix.lastIndexOf('.') - 1); + QString code; + if(suffix == "cpp" || suffix == "h" || suffix == "py") + code = "KIcon (\"" + iconName + "\")"; + else if(suffix == "rb") + code = "KDE::Icon.new (:\"" + iconName + "\")"; + else if(suffix == "js" || suffix == "qts" || suffix == "cs") + code = "new KIcon (\"" + iconName + "\")"; + else if(suffix == "java") + code = "new org.kde.kdeui.KIcon (\"" + iconName + "\")"; + else if(suffix == "fal" || suffix == "ftd") + code = "KIcon ('" + iconName + "')"; + else if(suffix == "php") + code = "new KIcon ('" + iconName + "')"; + else if(suffix == "pl") + code = "KDE::Icon (\"" + iconName + "\")"; + else if(suffix == "pas") + code = "KIcon_create ('" + iconName + "')"; + else if(suffix == "scm") + code = "(make KIcon '" + iconName + ")"; + else if(suffix == "hs") + code = "kIcon \"" + iconName + "\""; + else if(suffix == "ads" || suffix == "adb") + code = "KDEui.Icons.Constructos.Create (\"" + iconName + "\")"; + else + code = iconName; + doc->insertText(view->cursorPosition(), code); +} + + + + +IconInserterPlugin::IconInserterPlugin (QObject *parent, const QVariantList &) + : Plugin (parent) +{ +} + +IconInserterPlugin::~IconInserterPlugin() +{ +} + + +void IconInserterPlugin::addView (KTextEditor::View *view) +{ + m_views.insert(view,new IconInserterPluginView(this,view)); +} + +void IconInserterPlugin::removeView(KTextEditor::View *view) +{ + kDebug(); + delete m_views.take(view); +} + +#include "moc_iconinserterplugin.cpp" diff --git a/kate/addons/ktexteditor/kte_iconinserter/iconinserterplugin.h b/kate/addons/ktexteditor/kte_iconinserter/iconinserterplugin.h new file mode 100644 index 00000000..a663df5d --- /dev/null +++ b/kate/addons/ktexteditor/kte_iconinserter/iconinserterplugin.h @@ -0,0 +1,66 @@ +/**************************************************************************** + * This file is part of the KTextEditor-Icon-Inserter-Plugin * + * Copyright 2009-2010 Jonathan Schmidt-Dominé * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Library General Public License version * + * 3, or (at your option) any later version, as published by the Free * + * Software Foundation. * + * * + * This library 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 * + * Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with the kdelibs library; see the file COPYING.LIB. If * + * not, write to the Free Software Foundation, Inc., 51 Franklin Street, * + * Fifth Floor, Boston, MA 02110-1301, USA. or see * + * . * + ****************************************************************************/ + +#ifndef KDEVICONINSERTERPLUGIN_H +#define KDEVICONINSERTERPLUGIN_H + +#include + +#include + +#include + +#include +#include + +using namespace KTextEditor; + +class IconInserterPlugin; + +class IconInserterPluginView: public QObject, public KXMLGUIClient +{ + Q_OBJECT + public: + IconInserterPluginView(IconInserterPlugin *plugin, KTextEditor::View *view); + virtual ~IconInserterPluginView(); + private slots: + void insertIcon(); + private: + QPointer m_view; +}; + +class IconInserterPlugin: public Plugin +{ + Q_OBJECT + public: + IconInserterPlugin (QObject *parent, const QVariantList & = QVariantList()); + ~IconInserterPlugin(); + void addView (View *view); + void removeView(View *view); + virtual void readConfig (KConfig*) {} + virtual void writeConfig (KConfig*) {} + private: + QMap m_views; +}; + +K_PLUGIN_FACTORY_DECLARATION(IconInserterPluginFactory) + +#endif diff --git a/kate/addons/ktexteditor/kte_iconinserter/ktexteditor_iconinserter.desktop b/kate/addons/ktexteditor/kte_iconinserter/ktexteditor_iconinserter.desktop new file mode 100644 index 00000000..9e297f36 --- /dev/null +++ b/kate/addons/ktexteditor/kte_iconinserter/ktexteditor_iconinserter.desktop @@ -0,0 +1,173 @@ +[Desktop Entry] +Type=Service +Comment=Select an icon and use it as a KIcon in your source-code +Comment[ar]=اختر أيقونة واستخدمها كأيقونتك في الشِفرة المصدرية +Comment[bg]=Избор на икона и използване като KIcon в изходния код +Comment[bs]=Odabira ikonu i koristi je kao KIcon u izvornom kodu +Comment[ca]=Seleccioneu una icona i utilitzeu-la com a «KIcon» en el codi font +Comment[ca@valencia]=Seleccioneu una icona i utilitzeu-la com a «KIcon» en el codi font +Comment[cs]=Vyberte ikonu a použijte ji ve vašem kódu jako KIcon +Comment[da]=Vælg et ikon og brug det som et KIcon i din kildekode +Comment[de]=Wählen Sie ein Symbol und verwenden Sie dieses als KIcon in Ihrem Quelltext +Comment[el]=Επιλέξτε ένα εικονίδιο και χρησιμοποιήστε το ως KIcon στον πηγαίο σας κώδικα +Comment[en_GB]=Select an icon and use it as a KIcon in your source-code +Comment[es]=Selecciona un icono y lo utiliza como un KIcon en su código fuente +Comment[et]=Ikooni valimine ja selle kasutamine KIconina oma lähtekoodis +Comment[eu]=Hautatu ikono bat eta erabili KIcon bezala zure iturburu kodean +Comment[fi]=Valitse kuvake ja käytä sitä KIconina lähdekoodissasi +Comment[fr]=Sélectionne une icône et l'utilise en tant qu'icône K dans votre code source +Comment[ga]=Roghnaigh deilbhín agus bain úsáid as mar KIcon i do chód foinseach +Comment[gl]=Escoller unha icona e empregala como KIcon no código fonte +Comment[he]=בחר סמן והשתמש בו בתור KIcon בתור קוד המקור +Comment[hu]=A kiválasztott ikon használata KIconként a forráskódban +Comment[ia]=Selige un icone e usa lo como un KIcon in tu codice fonte +Comment[it]=Seleziona un'icona ed usala come KIcon nel tuo codice sorgente +Comment[kk]=Бастапқы кодыңызда KIcon ретінде қолданатын таңбашаны таңдау +Comment[km]=ជ្រើស​រូបតំណាង​ ហើយ​ប្រើ​វា​ជា​ KIcon ​នៅ​ក្នុង​កូដ​ប្រភព​របស់​អ្នក​​ +Comment[ko]=아이콘을 선택하여 원본 코드에 KIcon으로 삽입 +Comment[lt]=Parinkti ženkliuką ir naudoti jį kaip KIcon jūsų programos faile +Comment[lv]=Izvēlieties ikonu un izmantojiet kā KIcon savā pirmkodā +Comment[mr]=चिन्ह निवडा व तुमच्या सोर्स-कोड मध्ये के-आयकॉन प्रमाणे वापरा +Comment[nb]=Velg et ikon og bruk det som et KIcon i din kildekode +Comment[nds]=Söök en Lüttbild ut, un bruuk dat binnen Dien Bornkode as KIcon. +Comment[nl]=Selecteer een pictogram en gebruik het als een KIcon in uw broncode +Comment[pa]=ਆਈਕਾਨ ਚੁਣੋ ਅਤੇ ਇਸ ਨੂੰ KIcon ਵਜੋਂ ਆਪਣੇ ਸਰੋਤ-ਕੋਡ 'ਚ ਵਰਤੋਂ +Comment[pl]=Wybierz ikonę i użyj jej jako KIcon w twoim kodzie źródłowym +Comment[pt]=Seleccionar um ícone e usá-lo como KIcon no seu código-fonte +Comment[pt_BR]=Seleciona um ícone e usa-o como um KIcon no seu código-fonte +Comment[ro]=Selectați o pictogramă și folosiți-o ca un KIcon în codul-sursă al dumneavoastră +Comment[ru]=Вставка значка для использования как KIcon в коде программы +Comment[si]=අයිකනයක් තෝරාගෙන එය ඔබේ මූල කේතයේ KIcon ලෙස භාවිත කරන්න +Comment[sk]=Vybrať ikonu a použiť ju ako KIcon vo vašom zdrojovom kóde +Comment[sl]=Izberite ikono in jo v izvorni kodi uporabite kot KIcon +Comment[sr]=Изаберите икону која ће бити KIcon у вашем изворном коду +Comment[sr@ijekavian]=Изаберите икону која ће бити KIcon у вашем изворном коду +Comment[sr@ijekavianlatin]=Izaberite ikonu koja će biti KIcon u vašem izvornom kodu +Comment[sr@latin]=Izaberite ikonu koja će biti KIcon u vašem izvornom kodu +Comment[sv]=Välj en ikon och använd den som en KIcon i källkoden +Comment[tg]=Нишонаро интихоб кунед ва онро ҳамчун KIcon дар барномарезӣ истифода баред +Comment[tr]=Bir simge seçin ve kaynak kodunuzda KIcon olarak kullanın +Comment[ug]=مەنبە كودىڭىزدا KIcon غا ئوخشاش بىر سىنبەلگە تاللاپ ئىشلىتىڭ +Comment[uk]=Виберіть піктограму і використайте її як KIcon у коді вашої програми +Comment[wa]=Tchoezixhoz ene imådjete eyet l' eployî dins vosse côde sourdant +Comment[x-test]=xxSelect an icon and use it as a KIcon in your source-codexx +Comment[zh_CN]=选择一个图标并在代码中作为 KIcon 使用 +Comment[zh_TW]=選擇一個圖示,並在您的程式碼中做為 KIcon +Name=IconInserter +Name[ar]=مُدرج الأيقونات +Name[bs]=Ubacivač ikona +Name[ca]=Inserció d'icona +Name[ca@valencia]=Inserció d'icona +Name[cs]=Vložit ikonu +Name[da]=IconInserter +Name[de]=Symboleinfüger +Name[el]=IconInserter +Name[en_GB]=IconInserter +Name[es]=IconInserter +Name[et]=Ikoonilisaja +Name[eu]=Ikonoen txertatzailea +Name[fi]=Kuvakkeen lisäys +Name[fr]=Insertion d'icône +Name[ga]=IconInserter +Name[gl]=IconInserter +Name[he]=IconInserter +Name[hu]=Ikonbeszúró +Name[ia]=Iconinserter (Insertator de Icones) +Name[it]=Inserimento icona +Name[kk]=Таңбашаны ендіру +Name[km]=IconInserter +Name[ko]=아이콘 삽입기 +Name[lt]=PiktogramosĮterpimas +Name[lv]=Ikonu ievietotājs +Name[mr]=चिन्ह-अंतर्भूत-करणारा +Name[nb]=Ikoninnsetting +Name[nds]=Lüttbild infögen +Name[nl]=PictogramInvoegen +Name[pa]=IconInserter +Name[pl]=Wstawiacz ikon +Name[pt]=Inserção de Ícone +Name[pt_BR]=Inserção de ícones +Name[ro]=InseratorPictogramă +Name[ru]=Вставка значка +Name[si]=අයිකනඇතුළත්කරනය +Name[sk]=Vkladač ikon +Name[sl]=Vstavljalnik ikon +Name[sr]=Уметач икона +Name[sr@ijekavian]=Уметач икона +Name[sr@ijekavianlatin]=Umetač ikona +Name[sr@latin]=Umetač ikona +Name[sv]=Infoga ikon +Name[tg]=Дарҷкунандаи нишонаҳо +Name[tr]=SimgeEkleyici +Name[ug]=سىنبەلگە قىستۇرغۇچ +Name[uk]=Вставка піктограм +Name[wa]=IconInserter +Name[x-test]=xxIconInserterxx +Name[zh_CN]=图标插入 +Name[zh_TW]=IconInserter +GenericName=Insert Code for KIcon-Creation +GenericName[bs]=Ubaci kod za KIcon kreiranje +GenericName[ca]=Inseriu el codi per a la creació de KIcon +GenericName[ca@valencia]=Inseriu el codi per a la creació de KIcon +GenericName[cs]=Vložit kód pro vytvoření KIcon +GenericName[da]=Indsæt kode til KIcon-Creation +GenericName[de]=Quelltext zur KIcon-Erstellung einfügen +GenericName[el]=Εισαγωγή κώδικα για KIcon-Creation +GenericName[en_GB]=Insert Code for KIcon-Creation +GenericName[es]=Insertar código de creación de KIcon +GenericName[et]=Koodi lisamine KIconi loomiseks +GenericName[eu]=Txertatu KIcon sortzeko kodea +GenericName[fi]=Lisää koodi KIconin luontiin +GenericName[fr]=Insertion de code pour la création d'une icône K +GenericName[ga]=Ionsáigh cód le haghaidh cruthaithe KIcon +GenericName[gl]=Inserir código para a creación de KIcon +GenericName[he]=הכנס קוד עבור KIcon-Creation +GenericName[hu]=Kód beszúrása KIcon létrehozásához +GenericName[ia]=Inserta Codice pro Creation de KIcon +GenericName[it]=Inserisci il codice per creazione di KIcon +GenericName[kk]=KIcon-құру үшін кодын енгізу +GenericName[km]=ប្រើ​កូដ​សម្រាប់​ការ​បង្កើត​ KIcon +GenericName[ko]=KIcon 생성 코드 삽입 +GenericName[lt]=Įterpti kodą KIcon sukūrimui +GenericName[lv]=Ievietot kodu KIcon-Creation +GenericName[mr]=के-आयकॉन-क्रियेशन करिता कोड अंतर्भूत करा +GenericName[nb]=Sett inn kode for å lage KIcon +GenericName[nds]=Kode för't KIcon-Opstellen infögen +GenericName[nl]=Voeg code in voor het aanmaken met KIcon +GenericName[pa]=KIcon-Creation ਲਈ ਕੋਡ ਸ਼ਾਮਲ ਕਰੋ +GenericName[pl]=Wstawia kod do utworzenia KIcon +GenericName[pt]=Inserir o Código de Criação do KIcon +GenericName[pt_BR]=Inserir o código de criação de KIcons +GenericName[ro]=Inserează Cod pentru Creare-KIcon +GenericName[ru]=Вставка кода для создания KIcon +GenericName[si]=KIcon-නිර්මාණයට කේතය ඇතුළත් කරන්න +GenericName[sk]=Vložiť kód pre KIcon-Creation +GenericName[sl]=Vstavi kodo za ustvaritev KIcon +GenericName[sr]=Уметање кода за стварање KIcon‑а +GenericName[sr@ijekavian]=Уметање кода за стварање KIcon‑а +GenericName[sr@ijekavianlatin]=Umetanje koda za stvaranje KIcon‑a +GenericName[sr@latin]=Umetanje koda za stvaranje KIcon‑a +GenericName[sv]=Infoga kod för att skapa en KIcon +GenericName[tg]=Рамзро барои KIcon-Creation дарҷ кунед +GenericName[tr]=KIcon-Oluşturma için Kod Ekle +GenericName[ug]=KIcon-Creation ئۈچۈن كود قىستۇر +GenericName[uk]=Додавання коду для створення KIcon +GenericName[wa]=Stitchî do côde po KIcon-Creation +GenericName[x-test]=xxInsert Code for KIcon-Creationxx +GenericName[zh_CN]=KDE 图标插入代码 +GenericName[zh_TW]=插入程式碼產生 KIcon +Icon=insert-image +ServiceTypes=KTextEditor/Plugin +X-KDE-ServiceTypes=KTextEditor/Plugin +X-KDE-Library=ktexteditor_iconinserter +X-KDE-PluginKeyword=ktexteditor_iconinserter +X-KDE-PluginInfo-Name=kdeviconinserter +X-KDE-PluginInfo-Author=Jonathan Schmidt-Dominé (The User) +X-KDE-PluginInfo-Email=devel@the-user.org +X-KDE-PluginInfo-Website=the-user.org +X-KDE-PluginInfo-Version=0.1 +X-KDE-PluginInfo-License=LGPL_V3 +X-KDE-PluginInfo-Depends= +X-KDE-PluginInfo-Category=Editor +X-KDE-ParentApp=kate +X-KDE-Version=4.0 diff --git a/kate/addons/ktexteditor/kte_iconinserter/ktexteditor_iconinserterui.rc b/kate/addons/ktexteditor/kte_iconinserter/ktexteditor_iconinserterui.rc new file mode 100644 index 00000000..ce6c14a1 --- /dev/null +++ b/kate/addons/ktexteditor/kte_iconinserter/ktexteditor_iconinserterui.rc @@ -0,0 +1,8 @@ + + + +

+ + + + diff --git a/kate/addons/ktexteditor/lumen/CMakeLists.txt b/kate/addons/ktexteditor/lumen/CMakeLists.txt new file mode 100644 index 00000000..803407ae --- /dev/null +++ b/kate/addons/ktexteditor/lumen/CMakeLists.txt @@ -0,0 +1,28 @@ +project(ktexteditor_lumen) + +########### next target ############### + +set(ktexteditor_lumen_SRCS + lumen.cpp + dcd.cpp + completion.cpp +) + +kde4_add_plugin(ktexteditor_lumen ${ktexteditor_lumen_SRCS}) + +target_link_libraries(ktexteditor_lumen + ${KDE4_KDECORE_LIBS} + ${KDE4_KTEXTEDITOR_LIBS} +) + +install( + TARGETS ktexteditor_lumen + DESTINATION ${KDE4_PLUGIN_INSTALL_DIR} +) + +########### install files ############### + +install( + FILES ktexteditor_lumen.desktop + DESTINATION ${KDE4_SERVICES_INSTALL_DIR} +) diff --git a/kate/addons/ktexteditor/lumen/completion.cpp b/kate/addons/ktexteditor/lumen/completion.cpp new file mode 100644 index 00000000..86448831 --- /dev/null +++ b/kate/addons/ktexteditor/lumen/completion.cpp @@ -0,0 +1,158 @@ +/* + * Copyright 2014 David Herberth kde@dav1d.de + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) version 3, or any + * later version accepted by the membership of KDE e.V. (or its + * successor approved by the membership of KDE e.V.), which shall + * act as a proxy defined in Section 6 of version 3 of the license. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . +**/ + +#include "completion.h" +#include +#include +#include +#include +#include + + +LumenCompletionModel::LumenCompletionModel(QObject* parent, DCD* dcd): CodeCompletionModel2(parent) +{ + m_dcd = dcd; +} + +LumenCompletionModel::~LumenCompletionModel() +{ + +} + +bool LumenCompletionModel::shouldStartCompletion(View* view, const QString& insertedText, bool userInsertion, const Cursor& position) +{ + bool complete = KTextEditor::CodeCompletionModelControllerInterface::shouldStartCompletion( + view, insertedText, userInsertion, position + ); + + complete = complete || insertedText.endsWith("("); // calltip + complete = complete || insertedText.endsWith("import "); // import + + return complete; +} + +void LumenCompletionModel::completionInvoked(View* view, const Range& range, CodeCompletionModel::InvocationType invocationType) +{ + Q_UNUSED(invocationType); + KTextEditor::Document* document = view->document(); + + KTextEditor::Cursor cursor = range.end(); + KTextEditor::Cursor cursorEnd = document->documentEnd(); + KTextEditor::Range range0c = KTextEditor::Range(0, 0, cursor.line(), cursor.column()); + KTextEditor::Range rangece = KTextEditor::Range(cursor.line(), cursor.column(), + cursorEnd.line(), cursorEnd.column()); + QString text0c = document->text(range0c, false); + QByteArray utf8 = text0c.toUtf8(); + int offset = utf8.length(); + utf8.append(document->text(rangece, false).toUtf8()); + + m_data = m_dcd->complete(utf8, offset); + setRowCount(m_data.completions.length()); + + setHasGroups(false); +} + +void LumenCompletionModel::executeCompletionItem2(Document* document, const Range& word, const QModelIndex& index) const +{ + QModelIndex sibling = index.sibling(index.row(), Name); + KTextEditor::View* view = document->activeView(); + + document->replaceText(word, data(sibling).toString()); + + int crole = data(sibling, CompletionRole).toInt(); + if (crole & Function) { + KTextEditor::Cursor cursor = document->activeView()->cursorPosition(); + document->insertText(cursor, QString("()")); + view->setCursorPosition(Cursor(cursor.line(), cursor.column()+1)); + } +} + +QVariant LumenCompletionModel::data(const QModelIndex& index, int role) const +{ + DCDCompletionItem item = m_data.completions[index.row()]; + + switch (role) + { + case Qt::DecorationRole: + { + if(index.column() == Icon) { + return item.icon(); + } + break; + } + case Qt::DisplayRole: + { + if(item.type == DCDCompletionItemType::Calltip) { + QRegExp funcRE("^\\s*(\\w+)\\s+(\\w+\\s*\\(.*\\))\\s*$"); + funcRE.indexIn(item.name); + QStringList matches = funcRE.capturedTexts(); + + switch(index.column()) { + case Prefix: return matches[1]; + case Name: return matches[2]; + } + } else { + if(index.column() == Name) { + return item.name; + } + } + break; + } + case CompletionRole: + { + int p = NoProperty; + switch (item.type) { + case DCDCompletionItemType::FunctionName: p |= Function; break; + case DCDCompletionItemType::VariableName: p |= Variable; break; + default: break; + } + return p; + } + case BestMatchesCount: + { + return 5; + } + case ArgumentHintDepth: + { + if(item.type == DCDCompletionItemType::Calltip) { + return 1; + } + break; + } + case GroupRole: + { + break; + } + case IsExpandable: + { + // I like the green arrow + return true; + } + case ExpandingWidget: + { + // TODO well implementation in DCD is missing + return QVariant(); + } + } + + return QVariant(); +} + +#include "moc_completion.cpp" \ No newline at end of file diff --git a/kate/addons/ktexteditor/lumen/completion.h b/kate/addons/ktexteditor/lumen/completion.h new file mode 100644 index 00000000..db07ea76 --- /dev/null +++ b/kate/addons/ktexteditor/lumen/completion.h @@ -0,0 +1,49 @@ +/* + * Copyright 2014 David Herberth kde@dav1d.de + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) version 3, or any + * later version accepted by the membership of KDE e.V. (or its + * successor approved by the membership of KDE e.V.), which shall + * act as a proxy defined in Section 6 of version 3 of the license. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . +**/ + +#ifndef LUMEN_COMPLETION_H +#define LUMEN_COMPLETION_H +#include +#include +#include "dcd.h" + +using namespace KTextEditor; + +class LumenCompletionModel: public CodeCompletionModel2, + public KTextEditor::CodeCompletionModelControllerInterface +{ + Q_OBJECT + Q_INTERFACES(KTextEditor::CodeCompletionModelControllerInterface) +public: + LumenCompletionModel(QObject* parent, DCD* dcd); + virtual ~LumenCompletionModel(); + + virtual bool shouldStartCompletion(View* view, const QString& insertedText, bool userInsertion, const Cursor& position); + virtual void completionInvoked(View* view, const Range& range, InvocationType invocationType); + virtual void executeCompletionItem2(Document* document, const Range& word, const QModelIndex& index) const; + virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const; +private: + DCD* m_dcd; + DCDCompletion m_data; +}; + + + +#endif diff --git a/kate/addons/ktexteditor/lumen/dcd.cpp b/kate/addons/ktexteditor/lumen/dcd.cpp new file mode 100644 index 00000000..82b9fb33 --- /dev/null +++ b/kate/addons/ktexteditor/lumen/dcd.cpp @@ -0,0 +1,357 @@ +/* + * Copyright 2014 David Herberth kde@dav1d.de + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) version 3, or any + * later version accepted by the membership of KDE e.V. (or its + * successor approved by the membership of KDE e.V.), which shall + * act as a proxy defined in Section 6 of version 3 of the license. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . +**/ + +#include "dcd.h" +#include +#include + + +char DCDCompletionItemType::toChar(DCDCompletionItemType e) +{ + switch (e) { + case Invalid: return 0; + case Calltip: return 1; + case ClassName: return 'c'; + case InterfaceName: return 'i'; + case StructName: return 's'; + case UnionName: return 'u'; + case VariableName: return 'v'; + case MemberVariableName: return 'm'; + case Keyword: return 'k'; + case FunctionName: return 'f'; + case EnumName: return 'g'; + case EnumMember: return 'e'; + case PackageName: return 'p'; + case ModuleName: return 'M'; + } + + return 0; +} + +DCDCompletionItemType::DCDCompletionItemType DCDCompletionItemType::fromChar(char c) +{ + switch (c) { + case 0: return Invalid; + case 1: return Calltip; + case 'c': return ClassName; + case 'i': return InterfaceName; + case 's': return StructName; + case 'u': return UnionName; + case 'v': return VariableName; + case 'm': return MemberVariableName; + case 'k': return Keyword; + case 'f': return FunctionName; + case 'g': return EnumName; + case 'e': return EnumMember; + case 'p': return PackageName; + case 'M': return ModuleName; + } + + return Invalid; +} + + + +DCDCompletionItem::DCDCompletionItem(DCDCompletionItemType::DCDCompletionItemType t, QString s): type(t), name(s) +{ + +} + +#define RETURN_CACHED_ICON(name) {static QIcon icon(KIcon(name).pixmap(QSize(16, 16))); return icon;} +QIcon DCDCompletionItem::icon() const +{ + using namespace DCDCompletionItemType; + switch (type) + { + case Invalid: break; + case Calltip: RETURN_CACHED_ICON("code-function") + case ClassName: RETURN_CACHED_ICON("code-class") + case InterfaceName: RETURN_CACHED_ICON("code-class") + case StructName: RETURN_CACHED_ICON("struct") + case UnionName: RETURN_CACHED_ICON("union") + case VariableName: RETURN_CACHED_ICON("code-variable") + case MemberVariableName: RETURN_CACHED_ICON("field") + case Keyword: RETURN_CACHED_ICON("field") + case FunctionName: RETURN_CACHED_ICON("code-function") + case EnumName: RETURN_CACHED_ICON("enum") + case EnumMember: RETURN_CACHED_ICON("enum") + case PackageName: RETURN_CACHED_ICON("field") + case ModuleName: RETURN_CACHED_ICON("field") + } + + return KIcon(); +} + +QString DCDCompletionItem::typeLong() const +{ + using namespace DCDCompletionItemType; + switch (type) + { + case Invalid: return "invalid"; + case Calltip: return "calltip"; + case ClassName: return "class"; + case InterfaceName: return "interface"; + case StructName: return "struct"; + case UnionName: return "union"; + case VariableName: return "variable"; + case MemberVariableName: return "member"; + case Keyword: return "keyword"; + case FunctionName: return "function"; + case EnumName: return "enum"; + case EnumMember: return "enum member"; + case PackageName: return "package"; + case ModuleName: return "module"; + } + + return "completion"; +} + + +static const int TIMEOUT_START_SERVER = 200; +static const int TIMEOUT_COMPLETE = 200; +static const int TIMEOUT_IMPORTPATH = 200; +static const int TIMEOUT_SHUTDOWN = 350; +static const int TIMEOUT_SHUTDOWN_SERVER = 200; + + +DCD::DCD(int port, const QString& server, const QString& client) +{ + m_port = port; + m_server = server; + m_client = client; +} + +int DCD::port() +{ + return m_port; +} + +bool DCD::running() +{ + return m_sproc.state() == QProcess::Running; +} + + +bool DCD::startServer() +{ + m_sproc.setProcessChannelMode(QProcess::MergedChannels); + m_sproc.start(m_server, QStringList(QString("-p%1").arg(m_port))); + bool started = m_sproc.waitForStarted(TIMEOUT_START_SERVER); + bool finished = m_sproc.waitForFinished(TIMEOUT_START_SERVER); + + if (!started || finished || m_sproc.state() == QProcess::NotRunning) { + kWarning() << "unable to start completion-server:" << m_sproc.exitCode(); + kWarning() << m_sproc.readAll(); + return false; + } + kDebug() << "started completion-server"; + return true; +} + + +DCDCompletion DCD::complete(QString file, int offset) +{ + QProcess proc; + proc.setProcessChannelMode(QProcess::MergedChannels); + proc.start(m_client, + QStringList() + << QString("-p%1").arg(m_port) + << QString("-c%1").arg(offset) + << file + ); + + if (!proc.waitForFinished(TIMEOUT_COMPLETE)) { + kWarning() << "unable to complete:" << proc.exitCode(); + kWarning() << proc.readAll(); + return DCDCompletion(); + } + + return processCompletion(proc.readAllStandardOutput()); +} + +DCDCompletion DCD::complete(QByteArray data, int offset) +{ + QProcess proc; + proc.setProcessChannelMode(QProcess::MergedChannels); + proc.start(m_client, + QStringList() + << QString("-p%1").arg(m_port) + << QString("-c%1").arg(offset) + ); + + proc.write(data); + proc.closeWriteChannel(); + if (!proc.waitForFinished(TIMEOUT_COMPLETE)) { + kWarning() << "unable to complete: client didn't finish in time"; + proc.close(); + } else if (proc.exitCode() != 0) { + kWarning() << "unable to complete:" << proc.exitCode(); + kWarning() << proc.readAll(); + } else { + // everything Ok + return processCompletion(proc.readAllStandardOutput()); + } + + return DCDCompletion(); +} + +QString DCD::doc(QByteArray data, int offset) +{ + QProcess proc; + proc.setProcessChannelMode(QProcess::MergedChannels); + proc.start(m_client, + QStringList() + << QString("-p%1").arg(m_port) + << QString("-c%1").arg(offset) + << QString("--doc") + ); + + proc.write(data); + proc.closeWriteChannel(); + if (!proc.waitForFinished(TIMEOUT_COMPLETE)) { + kWarning() << "unable to lookup documentation: client didn't finish in time"; + proc.close(); + } else if (proc.exitCode() != 0) { + kWarning() << "unable to lookup documentation:" << proc.exitCode(); + kWarning() << proc.readAll(); + } else { + return proc.readAllStandardOutput(); + } + + return QString(""); +} + + +DCDCompletion DCD::processCompletion(QString data) +{ + DCDCompletion completion; + + QStringList lines = data.split(QRegExp("[\r\n]"), QString::SkipEmptyParts); + if (lines.length() == 0) { + return completion; + } + + QString type = lines.front(); + if (type == "identifiers") { completion.type = DCDCompletionType::Identifiers; } + else if (type == "calltips") { completion.type = DCDCompletionType::Calltips; } + else { + kWarning() << "Invalid type:" << type; + return completion; + } + lines.pop_front(); + + foreach(QString line, lines) { + if (line.trimmed().length() == 0) { + continue; + } + + QStringList kv = line.split(QRegExp("\\s+"), QString::SkipEmptyParts); + if (kv.length() != 2 && completion.type != DCDCompletionType::Calltips) { + kWarning() << "invalid completion data:" << kv.length() << completion.type; + continue; + } + + if (completion.type == DCDCompletionType::Identifiers) { + completion.completions.append(DCDCompletionItem( + DCDCompletionItemType::fromChar(kv[1].at(0).toAscii()), kv[0] + )); + } else { + completion.completions.append(DCDCompletionItem( + DCDCompletionItemType::Calltip, line + )); + } + } + + return completion; +} + + +void DCD::addImportPath(QString path) +{ + addImportPath(QStringList(path)); +} + +void DCD::addImportPath(QStringList paths) +{ + if (paths.isEmpty()) { + return; + } + + QStringList arguments = QStringList(QString("-p%1").arg(m_port)); + foreach(QString path, paths) { + if (QFile::exists(path)) + arguments << QString("-I%1").arg(path); + } + + kDebug() << "ARGUMENTS:" << arguments; + + QProcess proc; + proc.setProcessChannelMode(QProcess::MergedChannels); + proc.start(m_client, arguments); + + if (!proc.waitForFinished(TIMEOUT_IMPORTPATH)) { + kWarning() << "unable to add importpath(s)" << paths << ":" << proc.exitCode(); + kWarning() << proc.readAll(); + } +} + +void DCD::shutdown() +{ + QProcess proc; + proc.setProcessChannelMode(QProcess::MergedChannels); + proc.start(m_client, + QStringList() + << QString("-p%1").arg(m_port) + << QString("--shutdown") + ); + + if (!proc.waitForFinished(TIMEOUT_SHUTDOWN)) { + kWarning() << "unable to shutdown dcd:" << proc.exitCode(); + kWarning() << proc.readAll(); + } +} + + +bool DCD::stopServer() +{ + if (m_sproc.state() == QProcess::Running) { + kDebug() << "shutting down dcd"; + shutdown(); + if(!m_sproc.waitForFinished(TIMEOUT_SHUTDOWN_SERVER)) + m_sproc.terminate(); + if(!m_sproc.waitForFinished(TIMEOUT_SHUTDOWN_SERVER)) + m_sproc.kill(); + + return true; + } + return false; +} + + + +DCD::~DCD() +{ + if (running()) { + stopServer(); + } +} + + +#include "moc_dcd.cpp" diff --git a/kate/addons/ktexteditor/lumen/dcd.h b/kate/addons/ktexteditor/lumen/dcd.h new file mode 100644 index 00000000..4288a718 --- /dev/null +++ b/kate/addons/ktexteditor/lumen/dcd.h @@ -0,0 +1,92 @@ +/* + * Copyright 2014 David Herberth kde@dav1d.de + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) version 3, or any + * later version accepted by the membership of KDE e.V. (or its + * successor approved by the membership of KDE e.V.), which shall + * act as a proxy defined in Section 6 of version 3 of the license. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . +**/ + +#ifndef LUMEN_DCD_H +#define LUMEN_DCD_H + +#include +#include +#include +#include + +namespace DCDCompletionType { enum DCDCompletionType { Identifiers, Calltips }; }; +namespace DCDCompletionItemType { + enum DCDCompletionItemType { + Invalid, + + Calltip, + + ClassName, + InterfaceName, + StructName, + UnionName, + VariableName, + MemberVariableName, + Keyword, + FunctionName, + EnumName, + EnumMember, + PackageName, + ModuleName, + }; + + char toChar(DCDCompletionItemType e); + DCDCompletionItemType fromChar(char c); +}; + +struct DCDCompletionItem { + DCDCompletionItem(DCDCompletionItemType::DCDCompletionItemType, QString); + + DCDCompletionItemType::DCDCompletionItemType type; + QString name; + + QIcon icon() const; + QString typeLong() const; +}; + +struct DCDCompletion { + DCDCompletionType::DCDCompletionType type; + QList completions; +}; + +class DCD +{ + public: + DCD(int, const QString&, const QString&); + virtual ~DCD(); + int port(); + bool running(); + bool startServer(); + bool stopServer(); + DCDCompletion complete(QString, int); + DCDCompletion complete(QByteArray, int); + QString doc(QByteArray, int); + void shutdown(); + void addImportPath(QString); + void addImportPath(QStringList); + private: + DCDCompletion processCompletion(QString); + int m_port; + QString m_server; + QString m_client; + QProcess m_sproc; +}; + +#endif diff --git a/kate/addons/ktexteditor/lumen/ktexteditor_lumen.desktop b/kate/addons/ktexteditor/lumen/ktexteditor_lumen.desktop new file mode 100644 index 00000000..9c97c3de --- /dev/null +++ b/kate/addons/ktexteditor/lumen/ktexteditor_lumen.desktop @@ -0,0 +1,90 @@ +[Desktop Entry] +X-KDE-Library=ktexteditor_lumen +X-KDE-PluginKeyword=ktexteditor_lumen +X-KDE-PluginInfo-Author=David Herberth +X-KDE-PluginInfo-Email=kde@dav1d.de +X-KDE-PluginInfo-Name=ktexteditorlumen +X-KDE-PluginInfo-Version=0.1 +X-KDE-PluginInfo-Website=http://kate.kde.org +X-KDE-PluginInfo-Category=Editor +X-KDE-PluginInfo-Depends= +X-KDE-PluginInfo-License=LGPLv2 +X-KDE-PluginInfo-EnabledByDefault=false +X-KDE-ParentApp=kate +X-KDE-Version=4.0 +X-KDE-ServiceTypes=KTextEditor/Plugin +Type=Service +Icon=task-delegate +Name=Lumen +Name[ca]=Lumen +Name[ca@valencia]=Lumen +Name[cs]=Lumen +Name[da]=Lumen +Name[de]=Lumen +Name[el]=Lumen +Name[en_GB]=Lumen +Name[es]=Lumen +Name[et]=Lumen +Name[fi]=Lumen +Name[fr]=Lumen +Name[gl]=Lumen +Name[he]=Lumen +Name[hu]=Lumen +Name[ia]=Lumen +Name[ko]=Lumen +Name[nb]=Lumen +Name[nds]=Lumen +Name[nl]=Lumen +Name[pl]=Lumen +Name[pt]=Lumen +Name[pt_BR]=Lumen +Name[ro]=Lumen +Name[ru]=Lumen +Name[sk]=Lumen +Name[sl]=Lumen +Name[sr]=Лумен +Name[sr@ijekavian]=Лумен +Name[sr@ijekavianlatin]=Lumen +Name[sr@latin]=Lumen +Name[sv]=Lumen +Name[tr]=Lumen +Name[uk]=Lumen +Name[x-test]=xxLumenxx +Name[zh_CN]=Lumen +Name[zh_TW]=Lumen +Comment=Lumen is a Autocompletion Plugin for D, using the DCD autocompletion server +Comment[ca]=El Lumen és un connector de compleció automàtica pel D, que utilitza el servidor de compleció automàtica DCD +Comment[ca@valencia]=El Lumen és un connector de compleció automàtica pel D, que utilitza el servidor de compleció automàtica DCD +Comment[cs]=Lumen modul pro automatické doplňování D využívající server pro automatické doplňování DCD +Comment[da]=Lumen er et autofuldførelse-plugin til D, som bruger autofuldførelse-serveren DCD +Comment[de]=Lumen ist ein Modul zur automatischen Vervollständigung für D und benutzt den DCD-Auto-Vervollständigungs-Server +Comment[el]=Το Lumen είναι ένα πρόσθετο αυτόματης συμπλήρωσης για την D, με τη χρήση του εξυπηρετητή αυτόματης συμπλήρωσης DCD +Comment[en_GB]=Lumen is a Autocompletion Plugin for D, using the DCD autocompletion server +Comment[es]=Lumen es un complemento de completado automático para D que utiliza el servidor de completado automático DCD. +Comment[et]=Lumen on D keele automaatse koodilõpetamise plugin, mis kasutab välist koodilõpetamisserverit +Comment[fi]=Lumen on D-kielen automaattitäydennysliitännäinen, joka käyttää DCD-automaattitäydennyspalvelinta +Comment[fr]=Lumen est un composant externe d'auto-complètement pour le langage D qui utilise le serveur d'auto-complètement DCD +Comment[gl]=Lumen é un complemento de completado automático para D que emprega o servidor de completado automático DCD +Comment[he]=התוסף Lumen הוא תוסף השלמה אוטומית עבור שפת D, המשתמש בשרת השלמה DCD. +Comment[hu]=A Lumen egy automatikus kiegészítő bővítmény a D-hez a DCD automatikus kiegészítő kiszolgáló használatával +Comment[ia]=Lumen es un plugin de autocompletion pro D, usante le servitor de autocompletion DCD +Comment[ko]=Lumen은 DCD를 자동 완성 서버로 사용하는 D 자동 완성 플러그인 +Comment[nb]=Lumen er et tillegg for autofullføring for D, som bruker DCD som tjener for autofullføring +Comment[nds]=Lumen is en D-Kompletteermoduul, bruukt den DCD as Kompletteerserver +Comment[nl]=Lumen is een plug-in voor automatisch aanvullen voor D, met gebruikmaking van de DCD-server voor automatisch aanvullen +Comment[pl]=Lumen jest wtyczką samoczynnego uzupełniania dla D. Wykorzystuje serwer samoczynnego uzupełniania DCD. +Comment[pt]=O Lumen é um 'plugin' de completação automática para o D, usando o servidor de completação automática DCD +Comment[pt_BR]=Lumen é um plugin de autocompletar palavras para o D, usando o servidor de completação automática DCD +Comment[ru]=Lumen — модуль автодополнения для языка D, использующий сервер автодополнения DCD +Comment[sk]=Lumen je plugin pre automatické dokončovanie pre D, pomocou servera automatického dokončovania DCD +Comment[sl]=Lumen je vstavek za samodejno dopolnjevanje za D, ki uporablja strežnik DCD +Comment[sr]=Лумен је прикључак за самодопуну за Д, преко ДЦД‑овог сервера +Comment[sr@ijekavian]=Лумен је прикључак за самодопуну за Д, преко ДЦД‑овог сервера +Comment[sr@ijekavianlatin]=Lumen je priključak za samodopunu za D, preko DCD‑ovog servera +Comment[sr@latin]=Lumen je priključak za samodopunu za D, preko DCD‑ovog servera +Comment[sv]=Lumen insticksprogram för automatisk komplettering av D, som använder DCD-servern för automatisk komplettering +Comment[tr]=Lumen D için DCD otomatik tamamlama sunucusu kullanan Otomatik Tamamlama Eklentisi'dir. +Comment[uk]=Lumen — додаток автоматичного доповнення коду мовою D, якому використано сервер доповнення DCD +Comment[x-test]=xxLumen is a Autocompletion Plugin for D, using the DCD autocompletion serverxx +Comment[zh_CN]=Lumen 是 D 语言自动补全插件,使用的是 DCD 自动补全服务器 +Comment[zh_TW]=Lumen 是 D 的自動補完外掛程式,使用 DCD 自動補完伺服器 diff --git a/kate/addons/ktexteditor/lumen/lumen.cpp b/kate/addons/ktexteditor/lumen/lumen.cpp new file mode 100644 index 00000000..a89bd67d --- /dev/null +++ b/kate/addons/ktexteditor/lumen/lumen.cpp @@ -0,0 +1,176 @@ +/* + * Copyright 2014 David Herberth kde@dav1d.de + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) version 3, or any + * later version accepted by the membership of KDE e.V. (or its + * successor approved by the membership of KDE e.V.), which shall + * act as a proxy defined in Section 6 of version 3 of the license. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . +**/ + +#include "lumen.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +K_PLUGIN_FACTORY_DEFINITION( + LumenPluginFactory, registerPlugin("ktexteditor_lumen"); +) + +K_EXPORT_PLUGIN( + LumenPluginFactory( + KAboutData( + "ktexteditor_lumen", + "ktexteditor_plugins", + ki18n("Lumen"), + "0.1", + ki18n("Lumen"), + KAboutData::License_LGPL_V2, + ki18n("© David Herberth"), + ki18n("D Autocompletion plugin using DCD as completion server.") + ) + ) +) + + +LumenPluginView::LumenPluginView(LumenPlugin *plugin, KTextEditor::View *view): QObject(plugin),KXMLGUIClient(view),m_view(view),m_registered(false) +{ + m_plugin = plugin; + m_model = new LumenCompletionModel((QObject*)m_view, m_plugin->dcd()); + + KTextEditor::Document* document = view->document(); + + connect(document, SIGNAL(documentUrlChanged(KTextEditor::Document*)), + this, SLOT(urlChanged(KTextEditor::Document*))); + + registerCompletion(); + registerTextHints(); +} + +void LumenPluginView::registerCompletion() +{ + KTextEditor::CodeCompletionInterface *completion = + qobject_cast(m_view); + + bool isD = m_view->document()->url().path().endsWith(".d") || + m_view->document()->highlightingMode() == "D"; + + if (isD && !m_registered) { + completion->registerCompletionModel(m_model); + m_registered = true; + } else if(!isD && m_registered) { + completion->unregisterCompletionModel(m_model); + m_registered = false; + } +} + +void LumenPluginView::registerTextHints() +{ + KTextEditor::TextHintInterface *th = + qobject_cast(m_view); + th->enableTextHints(500); + + connect(m_view, SIGNAL(needTextHint(const KTextEditor::Cursor&, QString &)), + this, SLOT(getTextHint(const KTextEditor::Cursor&, QString &))); +} + +void LumenPluginView::getTextHint(const Cursor& cursor, QString& text) +{ + KTextEditor::Document* document = m_view->document(); + + KTextEditor::Cursor cursorEnd = document->documentEnd(); + KTextEditor::Range range0c = KTextEditor::Range(0, 0, cursor.line(), cursor.column()); + KTextEditor::Range rangece = KTextEditor::Range(cursor.line(), cursor.column(), + cursorEnd.line(), cursorEnd.column()); + QString text0c = document->text(range0c, false); + QByteArray utf8 = text0c.toUtf8(); + int offset = utf8.length(); + utf8.append(document->text(rangece, false).toUtf8()); + + text = m_plugin->dcd()->doc(utf8, offset).trimmed().replace("\\n", "\n"); +} + +LumenPluginView::~LumenPluginView() +{ +} + +void LumenPluginView::urlChanged(Document* document) +{ + registerCompletion(); + + QStringList paths; + for (KUrl url = document->url(); !url.equals(KUrl("/")); url = url.upUrl()) { + url = url.directory(); + url.addPath(".lumenconfig"); + + QFile file(url.path()); + if (file.open(QIODevice::ReadOnly | QIODevice::Text)) { + while (!file.atEnd()) { + QString path = file.readLine().trimmed(); + // KUrl doesn really provide this functionallity + if (QDir::isRelativePath(path)){ + path = QDir::cleanPath( + url.directory() + QDir::separator() + path + ); + } + + paths.append(path); + } + } + } + + if (!paths.isEmpty()) { + m_plugin->dcd()->addImportPath(paths); + } +} + + +LumenPlugin::LumenPlugin(QObject *parent, const QVariantList &): Plugin(parent) +{ + m_dcd = new DCD(9166, "dcd-server", "dcd-client"); + m_dcd->startServer(); +} + +LumenPlugin::~LumenPlugin() +{ + m_dcd->stopServer(); + delete m_dcd; +} + +DCD* LumenPlugin::dcd() +{ + return m_dcd; +} + + +void LumenPlugin::addView(KTextEditor::View *view) +{ + m_views.insert(view, new LumenPluginView(this, view)); +} + +void LumenPlugin::removeView(KTextEditor::View *view) +{ + delete m_views.take(view); +} + +#include "moc_lumen.cpp" diff --git a/kate/addons/ktexteditor/lumen/lumen.h b/kate/addons/ktexteditor/lumen/lumen.h new file mode 100644 index 00000000..3fdb5255 --- /dev/null +++ b/kate/addons/ktexteditor/lumen/lumen.h @@ -0,0 +1,76 @@ +/* + * Copyright 2014 David Herberth kde@dav1d.de + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) version 3, or any + * later version accepted by the membership of KDE e.V. (or its + * successor approved by the membership of KDE e.V.), which shall + * act as a proxy defined in Section 6 of version 3 of the license. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . +**/ + +#ifndef LUMEN_H +#define LUMEN_H + +#include +#include + +#include + +#include + +#include +#include +#include "dcd.h" +#include "completion.h" + +using namespace KTextEditor; + +class LumenPlugin; + +class LumenPluginView: public QObject, public KXMLGUIClient +{ + Q_OBJECT + public: + LumenPluginView(LumenPlugin *plugin, KTextEditor::View *view); + virtual ~LumenPluginView(); + void registerCompletion(); + void registerTextHints(); + private slots: + void urlChanged(KTextEditor::Document*); + void getTextHint(const KTextEditor::Cursor&, QString&); + private: + LumenPlugin *m_plugin; + QPointer m_view; + LumenCompletionModel *m_model; + bool m_registered; +}; + +class LumenPlugin: public Plugin +{ + Q_OBJECT + public: + LumenPlugin(QObject *parent, const QVariantList & = QVariantList()); + ~LumenPlugin(); + DCD* dcd(); + void addView(View *view); + void removeView(View *view); + virtual void readConfig(KConfig*) {} + virtual void writeConfig(KConfig*) {} + private: + QMap m_views; + DCD* m_dcd; +}; + +K_PLUGIN_FACTORY_DECLARATION(LumenPluginFactory) + +#endif diff --git a/kate/addons/plasma/CMakeLists.txt b/kate/addons/plasma/CMakeLists.txt new file mode 100644 index 00000000..5ce11936 --- /dev/null +++ b/kate/addons/plasma/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(session) diff --git a/kate/addons/plasma/session/CMakeLists.txt b/kate/addons/plasma/session/CMakeLists.txt new file mode 100644 index 00000000..c44b3507 --- /dev/null +++ b/kate/addons/plasma/session/CMakeLists.txt @@ -0,0 +1,17 @@ +project(katesessionapplet) + +kde4_add_plugin(plasma_applet_katesession katesessionapplet.cpp) + +target_link_libraries(plasma_applet_katesession + ${KDE4_PLASMA_LIBS} + ${KDE4_KIO_LIBS} +) + +install( + TARGETS plasma_applet_katesession + DESTINATION ${KDE4_PLUGIN_INSTALL_DIR} +) +install( + FILES plasma-applet-katesession.desktop + DESTINATION ${KDE4_SERVICES_INSTALL_DIR} +) diff --git a/kate/addons/plasma/session/Messages.sh b/kate/addons/plasma/session/Messages.sh new file mode 100755 index 00000000..b77868ac --- /dev/null +++ b/kate/addons/plasma/session/Messages.sh @@ -0,0 +1,2 @@ +#! /usr/bin/env bash +$XGETTEXT *.cpp -o $podir/plasma_applet_katesession.pot diff --git a/kate/addons/plasma/session/katesessionConfig.ui b/kate/addons/plasma/session/katesessionConfig.ui new file mode 100644 index 00000000..452a1ad2 --- /dev/null +++ b/kate/addons/plasma/session/katesessionConfig.ui @@ -0,0 +1,39 @@ + + + KateSessionConfig + + + + 0 + 0 + 553 + 333 + + + + + 0 + 0 + + + + + + + + + + + QAbstractItemView::ExtendedSelection + + + + + + + + + + + + diff --git a/kate/addons/plasma/session/katesessionapplet.cpp b/kate/addons/plasma/session/katesessionapplet.cpp new file mode 100644 index 00000000..17dcab82 --- /dev/null +++ b/kate/addons/plasma/session/katesessionapplet.cpp @@ -0,0 +1,260 @@ +/*************************************************************************** + * Copyright (C) 2008 by Montel Laurent * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * + ***************************************************************************/ + +#include "katesessionapplet.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +bool katesessions_compare_sessions(const QString &s1, const QString &s2) { + return KStringHandler::naturalCompare(s1,s2)==-1; +} + + +KateSessionApplet::KateSessionApplet(QObject *parent, const QVariantList &args) + : Plasma::PopupApplet(parent, args), m_listView( 0 ), m_config(0) +{ + KDirWatch *dirwatch = new KDirWatch( this ); + QStringList lst = KGlobal::dirs()->findDirs( "data", "kate/sessions/" ); + for ( int i = 0; i < lst.count(); i++ ) + { + dirwatch->addDir( lst[i] ); + } + connect( dirwatch, SIGNAL(dirty(QString)), this, SLOT(slotUpdateSessionMenu()) ); + setPopupIcon( "kate" ); + setHasConfigurationInterface(true); + setAspectRatioMode(Plasma::IgnoreAspectRatio); +} + +KateSessionApplet::~KateSessionApplet() +{ + delete m_listView; +} + +QWidget *KateSessionApplet::widget() +{ + if ( !m_listView ) + { + m_listView= new QTreeView(); + m_listView->setAttribute(Qt::WA_NoSystemBackground); + m_listView->setEditTriggers( QAbstractItemView::NoEditTriggers ); + m_listView->setRootIsDecorated(false); + m_listView->setHeaderHidden(true); + m_listView->setMouseTracking(true); + + m_kateModel = new QStandardItemModel(this); + m_listView->setModel(m_kateModel); + m_listView->setMouseTracking(true); + + initSessionFiles(); + + connect(m_listView, SIGNAL(activated(QModelIndex)), + this, SLOT(slotOnItemClicked(QModelIndex))); + } + return m_listView; +} + + +void KateSessionApplet::slotUpdateSessionMenu() +{ + m_kateModel->clear(); + m_sessions.clear(); + m_fullList.clear(); + initSessionFiles(); +} + +void KateSessionApplet::initSessionFiles() +{ + // Obtain list of items previously configured as hidden + const QStringList hideList = config().readEntry("hideList", QStringList()); + + // Construct a full list of items (m_fullList) so we can display them + // in the config dialog, but leave out the hidden stuff for m_kateModel + // that is actually displayed + int index=0; + QStandardItem *item = new QStandardItem(); + item->setData(i18n("Start Kate (no arguments)"), Qt::DisplayRole); + item->setData( KIcon( "kate" ), Qt::DecorationRole ); + item->setData( index++, Index ); + m_fullList << item->data(Qt::DisplayRole).toString(); + if (!hideList.contains(item->data(Qt::DisplayRole).toString())) { + m_kateModel->appendRow(item); + } + + item = new QStandardItem(); + item->setData( i18n("New Kate Session"), Qt::DisplayRole); + item->setData( KIcon( "document-new" ), Qt::DecorationRole ); + item->setData( index++, Index ); + m_fullList << item->data(Qt::DisplayRole).toString(); + if (!hideList.contains(item->data(Qt::DisplayRole).toString())) { + m_kateModel->appendRow(item); + } + + item = new QStandardItem(); + item->setData( i18n("New Anonymous Session"), Qt::DisplayRole); + item->setData( index++, Index ); + item->setData( KIcon( "document-new" ), Qt::DecorationRole ); + m_fullList << item->data(Qt::DisplayRole).toString(); + if (!hideList.contains(item->data(Qt::DisplayRole).toString())) { + m_kateModel->appendRow(item); + } + + const QStringList list = KGlobal::dirs()->findAllResources( "data", "kate/sessions/*.katesession", KStandardDirs::NoDuplicates ); + KUrl url; + for (QStringList::ConstIterator it = list.constBegin(); it != list.constEnd(); ++it) + { + url.setPath(*it); + QString name=url.fileName(); + name = QUrl::fromPercentEncoding(QFile::encodeName(url.fileName())); + name.chop(12);///.katesession==12 +/* KConfig _config( *it, KConfig::SimpleConfig ); + KConfigGroup config(&_config, "General" ); + QString name = config.readEntry( "Name" );*/ + m_sessions.append( name ); + } + qSort(m_sessions.begin(),m_sessions.end(),katesessions_compare_sessions); + for(QStringList::ConstIterator it=m_sessions.constBegin();it!=m_sessions.constEnd();++it) + { + m_fullList << *it; + if (!hideList.contains(*it)) { + item = new QStandardItem(); + item->setData(*it, Qt::DisplayRole); + item->setData( index++, Index ); + m_kateModel->appendRow( item); + } + } +} + +void KateSessionApplet::slotOnItemClicked(const QModelIndex &index) +{ + hidePopup(); + int id = index.data(Index).toInt(); + QStringList args; + + // If a new session is requested we try to ask for a name. + if ( id == 1 ) + { + bool ok = false; + QString name = KInputDialog::getText( i18n("Session Name"), + i18n("Please enter a name for the new session"), + QString(), + &ok ); + if ( ! ok ) + return; + + if ( name.isEmpty() && KMessageBox::questionYesNo( 0, + i18n("An unnamed session will not be saved automatically. " + "Do you want to create such a session?"), + i18n("Create anonymous session?"), + KStandardGuiItem::yes(), KStandardGuiItem::cancel(), + "kate_session_button_create_anonymous" ) == KMessageBox::No ) + return; + + if ( m_sessions.contains( name ) && + KMessageBox::warningYesNo( 0, + i18n("You already have a session named %1. Do you want to open that session?", name ), + i18n("Session exists") ) == KMessageBox::No ) + return; + if (name.isEmpty()) + args <<"-startanon"; + else + args <<"-n"<<"--start"<< name; + } + + else if ( id == 2 ) + args << "--startanon"; + + else if ( id > 2 ) + args <<"-n"<< "--start"<addPage(m_config, i18n("Sessions"), + "preferences-desktop-notification", + i18n("Sessions to show")); + connect(parent, SIGNAL(applyClicked()), this, SLOT(slotSaveConfig())); + connect(parent, SIGNAL(okClicked()), this, SLOT(slotSaveConfig())); +} + +void KateSessionApplet::slotSaveConfig() +{ + config().writeEntry("hideList", m_config->hideList()); +} + +void KateSessionApplet::configChanged() +{ + // refresh menu from config + slotUpdateSessionMenu(); +} + +KateSessionConfigInterface::KateSessionConfigInterface(const QStringList& all, const QStringList& hidden) +{ + m_all = all; + m_config.setupUi(this); + for (int i=0; i < m_all.size(); i++) { + QListWidgetItem *item = new QListWidgetItem(m_all[i]); + item->setFlags(item->flags() | Qt::ItemIsUserCheckable); + if (hidden.contains(item->text())) { + item->setCheckState(Qt::Unchecked); + m_config.itemList->addItem(item); + } else { + item->setCheckState(Qt::Checked); + m_config.itemList->addItem(item); + } + } +} + +QStringList KateSessionConfigInterface::hideList() const +{ + QStringList hideList; + const int numberOfItem = m_config.itemList->count(); + for (int i=0; i< numberOfItem; ++i) { + QListWidgetItem *item = m_config.itemList->item(i); + if (item->checkState() == Qt::Unchecked) + hideList << m_config.itemList->item(i)->text(); + } + return hideList; +} + +#include "moc_katesessionapplet.cpp" diff --git a/kate/addons/plasma/session/katesessionapplet.h b/kate/addons/plasma/session/katesessionapplet.h new file mode 100644 index 00000000..ce503094 --- /dev/null +++ b/kate/addons/plasma/session/katesessionapplet.h @@ -0,0 +1,77 @@ +/*************************************************************************** + * Copyright (C) 2008 by Montel Laurent * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * + ***************************************************************************/ + +#ifndef _KATESESSIONAPPLET_H_ +#define _KATESESSIONAPPLET_H_ + +#include + +#include "ui_katesessionConfig.h" + +#include +#include +#include +#include +class KConfigDialog; +#include +class KateSessionConfigInterface : public QWidget { + // Wrapper widget class for the configuration interface. + Q_OBJECT +public: + KateSessionConfigInterface(const QStringList& all, const QStringList& hidden); + QStringList hideList() const; +private: + QStringList m_all; + Ui::KateSessionConfig m_config; +}; + + +class KateSessionApplet : public Plasma::PopupApplet +{ + Q_OBJECT +public: + KateSessionApplet(QObject *parent, const QVariantList &args); + ~KateSessionApplet(); + + QWidget *widget(); + + enum SpecificRoles { + Index = Qt::UserRole+1 + }; + +protected slots: + void slotOnItemClicked(const QModelIndex &index); + void slotUpdateSessionMenu(); + void slotSaveConfig(); + +protected: + void initSessionFiles(); + void createConfigurationInterface(KConfigDialog *parent); + void configChanged(); +private: + QTreeView *m_listView; + QStandardItemModel *m_kateModel; + QStringList m_sessions; + QStringList m_fullList; + KateSessionConfigInterface *m_config; +}; + +K_EXPORT_PLASMA_APPLET(katesession, KateSessionApplet ) + +#endif diff --git a/kate/addons/plasma/session/plasma-applet-katesession.desktop b/kate/addons/plasma/session/plasma-applet-katesession.desktop new file mode 100644 index 00000000..f99771fa --- /dev/null +++ b/kate/addons/plasma/session/plasma-applet-katesession.desktop @@ -0,0 +1,126 @@ +[Desktop Entry] +Name=Kate Session Applet +Name[ar]=بريمج جلسة كيت +Name[ast]=Miniaplicación de sesiones de Kate +Name[bg]=Сесии на Kate +Name[bs]=Programčić Kate sesije +Name[ca]=Miniaplicació de sessió del Kate +Name[ca@valencia]=Miniaplicació de sessió del Kate +Name[cs]=Applet sezení Kate +Name[da]=Panelprogram til Kate-session +Name[de]=Kate-Sitzungsverwaltung +Name[el]=Μικροεφαρμογή συνεδρίας Kate +Name[en_GB]=Kate Session Applet +Name[es]=Miniaplicación de sesiones de Kate +Name[et]=Kate seansi aplett +Name[eu]=Kate saioaren miniaplikazioa +Name[fi]=Kate-istuntosovelma +Name[fr]=Applet de sessions pour Kate +Name[ga]=Feidhmchláirín Seisiúin Kate +Name[gl]=Applet de sesións de Kate +Name[he]=יישומון הפעלות של Kate +Name[hu]=Kate munkamenet-menü +Name[ia]=Applet de session de Kate +Name[it]=Applet di sessioni di Kate +Name[ja]=Kate セッションアプレット +Name[kk]=Kate сеанс апплеті +Name[km]=អាប់ភ្លេត​សម័យ Kate +Name[ko]=Kate 세션 애플릿 +Name[lt]=Kate sesijų programėlė +Name[lv]=Kate sesiju sīklietotne +Name[mr]=केट सत्र एप्लेट +Name[nb]=Kate øktvelger +Name[nds]=Kate-Törnlüttprogramm +Name[nl]=Kate sessieapplet +Name[nn]=Kate-øktveljar +Name[pa]=ਕੇਟ ਸ਼ੈਸ਼ਨ ਐਪਲਿਟ +Name[pl]=Aplet sesji Kate +Name[pt]='Applet' de Sessões do Kate +Name[pt_BR]=Miniaplicativo de sessões do Kate +Name[ro]=Miniaplicație de sesiune Kate +Name[ru]=Kate: сеансы +Name[si]=Kate වාර ඇප්ලටය +Name[sk]=Applet Kate sedenia +Name[sl]=Aplet sej za Kate +Name[sr]=Кејтине сесије +Name[sr@ijekavian]=Кејтине сесије +Name[sr@ijekavianlatin]=Kateine sesije +Name[sr@latin]=Kateine sesije +Name[sv]=Kate sessionsminiprogram +Name[tg]=Барномаи мониторинги Кейт +Name[tr]=Kate Oturum Programcığı +Name[ug]=Kate ئەڭگىمە قوللانچاق +Name[uk]=Аплет сеансів Kate +Name[x-test]=xxKate Session Appletxx +Name[zh_CN]=Kate 会话小程序 +Name[zh_TW]=Kate 工作階段小程式 +Comment=Kate Session Launcher +Comment[ar]=مُطلِق جلسة كيت +Comment[ast]=Llanzador de sesiones de Kate +Comment[bg]=Зареждане на сесии на Kate +Comment[bs]=Pokretač Kate sesija +Comment[ca]=Llançador de sessió del Kate +Comment[ca@valencia]=Llançador de sessió del Kate +Comment[cs]=Spouštěč sezení Kate +Comment[da]=Kate sessionstarter +Comment[de]=Kate-Sitzung starten +Comment[el]=Εκτελεστής συνεδρίας Kate +Comment[en_GB]=Kate Session Launcher +Comment[es]=Lanzador de sesiones de Kate +Comment[et]=Kate seansi käivitaja +Comment[eu]=Kate saioaren abiarazlea +Comment[fi]=Kate-istunnonkäynnistin +Comment[fr]=Lanceur de sessions pour Kate +Comment[ga]=Tosaitheoir Seisiúin Kate +Comment[gl]=Iniciador de sesións de Kate +Comment[he]=מפעיל הפעלות של Kate +Comment[hu]=Kate munkamenet-indító +Comment[ia]=Lanceator de session de Kate +Comment[is]=Kate seturæsir +Comment[it]=Avviatore di sessioni di Kate +Comment[ja]=Kate のセッションを開始します +Comment[kk]=Kate сеанс жеккіші +Comment[km]=កម្មវិធី​ចាប់ផ្ដើម​សម័យ Kate +Comment[ko]=Kate 세션 실행기 +Comment[lt]=Kate sesijų paleidiklis +Comment[lv]=Kate sesiju palaidējs +Comment[mr]=केट सत्र प्रक्षेपक +Comment[nb]=Kate øktstarter +Comment[nds]=Kate-Törnstarter +Comment[nl]=Kate sessiestarter +Comment[nn]=Start Kate-økter +Comment[pa]=ਕੇਟ ਸ਼ੈਸ਼ਨ ਲਾਂਚਰ +Comment[pl]=Program uruchamiający sesję Kate +Comment[pt]=Lançador de Sessões do Kate +Comment[pt_BR]=Lançamento de sessões do Kate +Comment[ro]=Lansator de sesiuni Kate +Comment[ru]=Программа запуска сеанса Kate +Comment[si]=Kate වාර ආරම්භකය +Comment[sk]=Spúšťač Kate sedenia +Comment[sl]=Zaganjalnik sej za Kate +Comment[sr]=Покретач Кејтиних сесија +Comment[sr@ijekavian]=Покретач Кејтиних сесија +Comment[sr@ijekavianlatin]=Pokretač Kateinih sesija +Comment[sr@latin]=Pokretač Kateinih sesija +Comment[sv]=Kate sessionsstart +Comment[tg]=Оғозкунандаи мониторинги Кейт +Comment[tr]=Kate Oturum Başlatıcı +Comment[ug]=Kate ئەڭگىمە قوزغاتقۇچ +Comment[uk]=Запуск сеансів Kate +Comment[x-test]=xxKate Session Launcherxx +Comment[zh_CN]=Kate 会话启动器 +Comment[zh_TW]=Kate 工作階段啟動器 +Type=Service +Icon=kate +X-KDE-ServiceTypes=Plasma/Applet + +X-KDE-Library=plasma_applet_katesession +X-KDE-PluginInfo-Author=Montel Laurent +X-KDE-PluginInfo-Email=montel@kde.org +X-KDE-PluginInfo-Name=katesession +X-KDE-PluginInfo-Version=pre0.1 +X-KDE-PluginInfo-Category=Utilities +X-KDE-PluginInfo-Depends= +X-KDE-PluginInfo-License=GPL +X-KDE-PluginInfo-EnabledByDefault=true + diff --git a/kate/config.h.cmake b/kate/config.h.cmake new file mode 100644 index 00000000..f268a5a1 --- /dev/null +++ b/kate/config.h.cmake @@ -0,0 +1,4 @@ +/* config.h. Generated by cmake from config.h.cmake */ + +#cmakedefine HAVE_FDATASYNC 1 +#cmakedefine HAVE_CTERMID 1 diff --git a/kate/part/CMakeLists.txt b/kate/part/CMakeLists.txt new file mode 100644 index 00000000..c8e5c4c1 --- /dev/null +++ b/kate/part/CMakeLists.txt @@ -0,0 +1,196 @@ +# define project +project(katepart) + +# these subdirs have their own CMakeLists +add_subdirectory(data) + +# syntax highlighting data files +add_subdirectory(syntax/data) + +# includes +include_directories( + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/buffer + ${CMAKE_CURRENT_SOURCE_DIR}/completion + ${CMAKE_CURRENT_SOURCE_DIR}/dialogs + ${CMAKE_CURRENT_SOURCE_DIR}/document + ${CMAKE_CURRENT_SOURCE_DIR}/mode + ${CMAKE_CURRENT_SOURCE_DIR}/render + ${CMAKE_CURRENT_SOURCE_DIR}/search + ${CMAKE_CURRENT_SOURCE_DIR}/syntax + ${CMAKE_CURRENT_SOURCE_DIR}/schema + ${CMAKE_CURRENT_SOURCE_DIR}/undo + ${CMAKE_CURRENT_SOURCE_DIR}/utils + ${CMAKE_CURRENT_SOURCE_DIR}/view + ${CMAKE_CURRENT_SOURCE_DIR}/swapfile + ${CMAKE_CURRENT_SOURCE_DIR}/variableeditor + ${CMAKE_CURRENT_SOURCE_DIR}/kte5 + ${KDE4_KIO_INCLUDES} +) + +# our sources +set(katepart_PART_SRCS + # text buffer & buffer helpers + buffer/katetextbuffer.cpp + buffer/katetextblock.cpp + buffer/katetextline.cpp + buffer/katetextcursor.cpp + buffer/katetextrange.cpp + buffer/katetexthistory.cpp + buffer/katetextfolding.cpp + + # completion (widget, model, delegate, ...) + completion/codecompletionmodelcontrollerinterfacev4.cpp + completion/katecompletionwidget.cpp + completion/katecompletionmodel.cpp + completion/katecompletiontree.cpp + completion/katecompletionconfig.cpp + completion/kateargumenthinttree.cpp + completion/kateargumenthintmodel.cpp + completion/katecompletiondelegate.cpp + completion/expandingtree/expandingwidgetmodel.cpp + completion/expandingtree/expandingdelegate.cpp + completion/expandingtree/expandingtree.cpp + + # simple internal word completion + completion/katewordcompletion.cpp + # internal syntax-file based keyword completion + completion/katekeywordcompletion.cpp + + # dialogs + dialogs/katedialogs.cpp + + # document (THE document, buffer, lines/cursors/..., CORE STUFF) + document/katedocument.cpp + document/katedocumenthelpers.cpp + document/katebuffer.cpp + + # undo + undo/kateundo.cpp + undo/katemodifiedundo.cpp + undo/kateundomanager.cpp + + # mode (modemanager and co) + mode/katemodemanager.cpp + mode/katemodeconfigpage.cpp + mode/katemodemenu.cpp + mode/katewildcardmatcher.cpp + + # modeline variable editor + variableeditor/variablelineedit.cpp + variableeditor/variablelistview.cpp + variableeditor/variableeditor.cpp + variableeditor/variableitem.cpp + variableeditor/katehelpbutton.cpp + + # rendering stuff (katerenderer and helpers) + render/katerenderer.cpp + render/katerenderrange.cpp + render/katelayoutcache.cpp + render/katetextlayout.cpp + render/katelinelayout.cpp + + # search stuff + search/kateregexp.cpp + search/kateplaintextsearch.cpp + search/kateregexpsearch.cpp + search/katematch.cpp + search/katesearchbar.cpp + + # syntax related stuff (highlighting, xml file parsing, ...) + syntax/katesyntaxmanager.cpp + syntax/katehighlight.cpp + syntax/katehighlighthelpers.cpp + syntax/katehighlightmenu.cpp + syntax/katesyntaxdocument.cpp + syntax/kateextendedattribute.cpp + + # view stuff (THE view and its helpers) + view/kateview.cpp + view/kateviewinternal.cpp + view/kateviewhelpers.cpp + view/katemessagewidget.cpp + view/katefadeeffect.cpp + view/kateanimation.cpp + view/katetextanimation.cpp + + # spell checking + spellcheck/prefixstore.h + spellcheck/prefixstore.cpp + spellcheck/ontheflycheck.h + spellcheck/ontheflycheck.cpp + spellcheck/spellcheck.h + spellcheck/spellcheck.cpp + spellcheck/spellcheckdialog.h + spellcheck/spellcheckdialog.cpp + spellcheck/spellingmenu.h + spellcheck/spellingmenu.cpp + + # generic stuff, unsorted... + utils/katecmds.cpp + utils/kateconfig.cpp + utils/katebookmarks.cpp + utils/kateautoindent.cpp + utils/kateprinter.cpp + utils/kateglobal.cpp + utils/katecmd.cpp + utils/katepartpluginmanager.cpp + utils/katedefaultcolors.cpp + + # schema + schema/kateschema.cpp + schema/kateschemaconfig.cpp + schema/katestyletreewidget.cpp + schema/katecolortreewidget.cpp + schema/katecategorydrawer.cpp + + # swapfile + swapfile/kateswapdiffcreator.cpp + swapfile/kateswapfile.cpp + + # KDE5: move to KTextEditor + kte5/documentcursor.cpp +) + +add_definitions(-DKDE_DEFAULT_DEBUG_AREA=13000) + +add_library(katepartinterfaces ${LIBRARY_TYPE} ${katepart_PART_SRCS}) + +target_link_libraries(katepartinterfaces + ${KDE4_KDECORE_LIBS} + ${KDE4_KPARTS_LIBS} + ${KDE4_KCMUTILS_LIBS} + ${KDE4_KTEXTEDITOR_LIBS} +) + +set_target_properties( + katepartinterfaces PROPERTIES + VERSION ${GENERIC_LIB_VERSION} + SOVERSION ${GENERIC_LIB_SOVERSION} +) + +generate_export_header(katepartinterfaces) + +# install kate part interfaces +install( + TARGETS katepartinterfaces + EXPORT kdelibsLibraryTargets ${INSTALL_TARGETS_DEFAULT_ARGS} +) + +# kate part itself just is interfaces + the factory +kde4_add_plugin(katepart utils/katefactory.cpp) + +# link the part, use kate part interfaces + kde stuff +target_link_libraries(katepart + ${KDE4_KDECORE_LIBS} + ${KDE4_KPARTS_LIBS} + ${KDE4_KCMUTILS_LIBS} + ${KDE4_KTEXTEDITOR_LIBS} + katepartinterfaces +) + +# install the part +install( + TARGETS katepart + DESTINATION ${KDE4_PLUGIN_INSTALL_DIR} +) diff --git a/kate/part/INDENTATION b/kate/part/INDENTATION new file mode 100644 index 00000000..f2aa3acb --- /dev/null +++ b/kate/part/INDENTATION @@ -0,0 +1,105 @@ +# +# Indentation for Dummies +# + +This file should is aimed to describe how the KatePart in KDE 4 is designed to allow +flexible indentation based on JavaScripts. + +As references, a good read is the way VIM handles this all, Emacs is out of scope I think, +as it's just to mangled together there with the mighty lisp engine. + +VIM seems to have a fairly simple way to integrate indentation scripts, I guess a similar +approach is no bad idea... + +What tell the VIM docs about indentation? +http://www.vim.org/htmldoc/indent.html + +Some little HOWTO showing a simple Pascal indenter: +http://psy.swan.ac.uk/staff/carter/unix/vim_indent.htm + +In short, how does scripted indentation work in VIM? + + - you must write a functions which returns for a given line the wanted indentation level. + - you specify some keywords/char which trigger indentation, inserting a linebreak always triggers it + - if indentation is triggered, VIM evaluates the function for the line and reindents the line to the + returned level or keeps the current indentation level if -1 is returned + +This sounds fairly simple, but seems to be enough for normal world usage, given that VIM is +very popular.... + +KatePart's current indentation scripts work similar, but are triggered on each keystroke. +I guess only calling them for linebreaks + special chars would increase performance already +a lot. Next part is that the scripts atm indent theirself, I guess letting them just return the +needed level and let the C++ code really indent the line would be faster and easier for the script +writers, too. + +One bad thing with the VIM way is that mixed indentation won't work correct, you can't return in one +int that the first 4 chars can be replaced with tabs, if tab indentation is enabled but the remaining 4 +chars should stay single spaces, as it is useful in this example (given tabwidth is 4): + +int test () +{ + if (hello || + muhh) // this line should be indented with tab + 4 spaces + return 1; // this line should be indented with 2 tabs + + return 0; +} + +I think we should have a interface in which the script indicates which normal indentation is wanted +and which further amount of spaces should be inserted to align with other words/keywords whatever... + +As it shows up, if we want to support both mixed-indentation and alignment, indent/unindent functions can't +be generic, as there is no way to tell, if for example 8 spaces are actually one tab, as they are 2 indent levels +or just one indent level + 4 alignment spaces. Therefor will take the vim way of things, where alignment is not +possible... + +# +# Built-in indenters +# +Right now we removed all built-in indenters due to refactoring and new indentation system. If it turns +out that js indentation for complex stuff like c/c++ is too slow, we maybe have to reintroduce the +built-in indenters. Interesting reference might be in vim, see: vim/src/misc1.c, function get_c_indent. + +# +# Idea: Extending itemData (=attribute information) +# +Scripts would benefit if they could access highlighting information in a -reliable- way, i.e. -not- +what we have now in KateNormalIndent::updateConfig(). + +Possible solution: extend itemData to have another attribute 'type'. +Example: +What types do indentation scripts need? C/C++ like languages need: + * default = everything that is not of the following types + * comment = single and multiline comments, and #if 0ed code + * string = "..." and '...' strings + * pp = all preprocessor macros (#ifdef etc.) + * symbol = characters like: {}[](),; (and more) + +This is actually everything we need for C like languages. Though, some further thoughts: + * number = all kind of numbers: int, double, float + * keyword = keywords like: void, int, while, class +Those two maybe would be a nice addon, but they are not necessarily needed for indentation. + +Shortcomings: +We need a good set of 'types', as they will be hard coded in the Kate Part. +-> Todo: Think of other languages, what types do we need? + +In what way are those types useful? +Indenters (and scripts) can for example query doc.findOfType("{", "symbol"); +This will skip all { in comments and strings. +Indenters can check 'where are we?': if (doc.typeAt(cursor) == "comment") ... +-> Todo: Find good API. + +How to make it work with our .xml files? +To be backward compatible, the default type would be "default", if not defined otherwise. +To make it work with our .xml files, we would need to adapt C++/Java/Php/... files to set the types +correctly. +Implementation: Extend KateExtendedAttribute to contain the type (besides the defaultStyle). To make +it fast, use an integer (enum) just like the defaultStyles. + +Another approach: Instead of putting a type into a KateExtendedAttribute, we also can put the type +into the KTextEditor::Attribute. Then overlay highlighting like it probably will be done by KDevelop +can also provide the needed type information (otherwise indentation might not work). + +Thoughts welcome. diff --git a/kate/part/Messages.sh b/kate/part/Messages.sh new file mode 100644 index 00000000..97d8b49d --- /dev/null +++ b/kate/part/Messages.sh @@ -0,0 +1,4 @@ +#! /usr/bin/env bash +$EXTRACTRC `find . -name \*.rc -o -name \*.ui` >> rc.cpp || exit 11 +$EXTRACTATTR --attr=language,name,Language --attr="language,section,Language Section" syntax/data/*.xml >> rc.cpp || exit 12 +$XGETTEXT `find . -name "*.cpp" -o -name "*.h"` -o $podir/katepart4.pot diff --git a/kate/part/README b/kate/part/README new file mode 100644 index 00000000..7068ba0b --- /dev/null +++ b/kate/part/README @@ -0,0 +1,11 @@ + *** Kate Part *** +The Kate Part implements the KTextEditor interfaces and it is +highly recommended to use them. Detailed information can be found +in the API documentation of the KTextEditor interfaces: +http://api.kde.org/4.x-api/kdelibs-apidocs/interfaces/ktexteditor/html/index.html + +Homepage: http://kate-editor.org + +Licensing: +The Kate Part and its interfaces are licensed under the LGPL version 2, +not any later version. diff --git a/kate/part/README.vimode b/kate/part/README.vimode new file mode 100644 index 00000000..904c04f9 --- /dev/null +++ b/kate/part/README.vimode @@ -0,0 +1,24 @@ +To enable the VI input mode, go to Settings → Configure Kate... → Editing → +VI Input Mode. It can also be toggled with the “VI Input Mode” setting in the +“Edit” menu (default shortcut key is Meta+Ctrl+V – where Meta usually is the +Windows key). + +Vim incompatibilities: + +1) + Kate: 'O' and 'o' opens [count] new lines and puts you in insert mode + Vim: 'O' and 'o' opens a new line and inserts text [count] times when + exiting insert mode + +2) + Kate: U and is redo + Vim: is normal redo, U is used to "undo all latest changes on one line" + +3) + Kate: :print shows the 'print' dialogue + Vim: :print prints the lines of the given range like its grandfather ed + +4) + Kate: 'Y' yanks to end of line. This is described as 'more + logical' in the Vim documentation (:help Y). + Vim: 'Y' yanks whole line, just like 'yy'. diff --git a/kate/part/TODO b/kate/part/TODO new file mode 100644 index 00000000..49581913 --- /dev/null +++ b/kate/part/TODO @@ -0,0 +1,31 @@ +KDE Framework 5.0 + +- Remove Smart* +- Cleanup Cursor and Range classes (non virtual!) +-- mark as movable +-- inline as much as possible +-- declare Q_MOVABLE_TYPE: http://doc.qt.nokia.com/qq/qq19-containers.html +-- remove Range* from Cursor +- Dropping KTextEditor::Plugin/View interface +- KTE: drop unneeded code completion interfaces 2, 3, 4 +- KTE: Merge some interfaces +- KTE: add Document::readWriteChanged, remove from KateDocument +- KTE: search for places marked with "KDE5" +- KTE: export Kate Part Scripting +- KTE: drop MarkInterface + - replace it with an interface based on KTE::MovingCursors + - this way, we can drop a lot of code dedicated just for mark handling + - example + - a mark could be set to MovingCursor(line, 0) with StayOnInsert + - drawing the bookmark works just like now + - if lines are joined the bookmark changes to MoveOnInsert (*) + - if lines are wrapped at the mark position, change back to StayOnInsert (*) + - (*) right now, we don't have a MovingCursorFeedback class, so if we + want to adapt the insert behavior manually (again special code)... + ...or add as additional flag to InsertBehavior: WrapOnLineWrap + this way the mark would automatically always stay on the correct line, + even when joining text and wrapping it again + - still provide some default mark types + - https://bugs.kde.org/show_bug.cgi?id=243375 + maybe move incRef(), decRef() into KTE::Factory or KTE::Editor? +- scrolling API: http://bugs.kde.org/show_bug.cgi?id=138956 diff --git a/kate/part/TODO.vimode b/kate/part/TODO.vimode new file mode 100644 index 00000000..ab053601 --- /dev/null +++ b/kate/part/TODO.vimode @@ -0,0 +1,13 @@ +* Make it possible to save marks, command history etc. between sessions. +* integrate the vi input mode marks with kate's bookmarks system +* make mouse dragging start visual mode +* in insert mode should first delete back to the point where insert mode + was started even if that was in the middle of a word +* make it possible to use registers in ranges and add support for the < and + > registers (start and end of selection) +* synchronize modes for views of the same document. this is necessary because + when in insert mode all undo items are merged. if one of the views of a + document is in insert mode, all undo items will be merged. +* commands like 'y', 'gU', etc, in visual mode should result in the cursor + being at the start of the visual mode selection after having been returned to + normal mode diff --git a/kate/part/buffer/katetextblock.cpp b/kate/part/buffer/katetextblock.cpp new file mode 100644 index 00000000..9c6a4ebc --- /dev/null +++ b/kate/part/buffer/katetextblock.cpp @@ -0,0 +1,688 @@ +/* This file is part of the Kate project. + * + * Copyright (C) 2010 Christoph Cullmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "katetextblock.h" +#include "katetextbuffer.h" + +namespace Kate { + +TextBlock::TextBlock (TextBuffer *buffer, int startLine) + : m_buffer (buffer) + , m_startLine (startLine) +{ + // reserve the block size + m_lines.reserve (m_buffer->m_blockSize); +} + +TextBlock::~TextBlock () +{ + // blocks should be empty before they are deleted! + Q_ASSERT (m_lines.empty()); + Q_ASSERT (m_cursors.empty()); + + // it only is a hint for ranges for this block, not the storage of them +} + +void TextBlock::setStartLine (int startLine) +{ + // allow only valid lines + Q_ASSERT (startLine >= 0); + Q_ASSERT (startLine < m_buffer->lines ()); + + m_startLine = startLine; +} + +TextLine TextBlock::line (int line) const +{ + // right input + Q_ASSERT (line >= startLine ()); + + // calc internal line + line = line - startLine (); + + // in range + Q_ASSERT (line < m_lines.size ()); + + // get text line + return m_lines.at(line); +} + +void TextBlock::appendLine (const QString &textOfLine) +{ + m_lines.append (TextLine (new TextLineData(textOfLine))); +} + +void TextBlock::clearLines () +{ + m_lines.clear (); +} + +void TextBlock::text (QString &text) const +{ + // combine all lines + for (int i = 0; i < m_lines.size(); ++i) { + // not first line, insert \n + if (i > 0 || startLine() > 0) + text.append ('\n'); + + text.append (m_lines.at(i)->text ()); + } +} + +void TextBlock::wrapLine (const KTextEditor::Cursor &position, int fixStartLinesStartIndex) +{ + // calc internal line + int line = position.line () - startLine (); + + // get text + QString &text = m_lines.at(line)->textReadWrite (); + + // check if valid column + Q_ASSERT (position.column() >= 0); + Q_ASSERT (position.column() <= text.size()); + + // create new line and insert it + m_lines.insert (m_lines.begin() + line + 1, TextLine (new TextLineData())); + + // cases for modification: + // 1. line is wrapped in the middle + // 2. if empty line is wrapped, mark new line as modified + // 3. line-to-be-wrapped is already modified + if (position.column() > 0 || text.size() == 0 || m_lines.at(line)->markedAsModified()) { + m_lines.at(line + 1)->markAsModified(true); + } else if (m_lines.at(line)->markedAsSavedOnDisk()) { + m_lines.at(line + 1)->markAsSavedOnDisk(true); + } + + // perhaps remove some text from previous line and append it + if (position.column() < text.size ()) { + // text from old line moved first to new one + m_lines.at(line+1)->textReadWrite() = text.right (text.size() - position.column()); + + // now remove wrapped text from old line + text.chop (text.size() - position.column()); + + // mark line as modified + m_lines.at(line)->markAsModified(true); + } + + /** + * fix all start lines + * we need to do this NOW, else the range update will FAIL! + * bug 313759 + */ + m_buffer->fixStartLines (fixStartLinesStartIndex); + + /** + * notify the text history + */ + m_buffer->history().wrapLine (position); + + /** + * cursor and range handling below + */ + + // no cursors will leave or join this block + + // no cursors in this block, no work to do.. + if (m_cursors.empty()) + return; + + // move all cursors on the line which has the text inserted + // remember all ranges modified + QSet changedRanges; + foreach (TextCursor *cursor, m_cursors) { + // skip cursors on lines in front of the wrapped one! + if (cursor->lineInBlock() < line) + continue; + + // either this is simple, line behind the wrapped one + if (cursor->lineInBlock() > line) { + // patch line of cursor + cursor->m_line++; + } + + // this is the wrapped line + else { + // skip cursors with too small column + if (cursor->column() <= position.column()) { + if (cursor->column() < position.column() || !cursor->m_moveOnInsert) + continue; + } + + // move cursor + + // patch line of cursor + cursor->m_line++; + + // patch column + cursor->m_column -= position.column(); + } + + // remember range, if any + if (cursor->kateRange()) + changedRanges.insert (cursor->kateRange()); + } + + // check validity of all ranges, might invalidate them... + foreach (TextRange *range, changedRanges) + range->checkValidity (); +} + +void TextBlock::unwrapLine (int line, TextBlock *previousBlock, int fixStartLinesStartIndex) +{ + // calc internal line + line = line - startLine (); + + // two possiblities: either first line of this block or later line + if (line == 0) { + // we need previous block with at least one line + Q_ASSERT (previousBlock); + Q_ASSERT (previousBlock->lines () > 0); + + // move last line of previous block to this one, might result in empty block + TextLine oldFirst = m_lines.at(0); + int lastLineOfPreviousBlock = previousBlock->lines ()-1; + TextLine newFirst = previousBlock->m_lines.last(); + m_lines[0] = newFirst; + previousBlock->m_lines.erase (previousBlock->m_lines.begin() + (previousBlock->lines () - 1)); + + const int oldSizeOfPreviousLine = newFirst->text().size(); + if (oldFirst->length() > 0) { + // append text + newFirst->textReadWrite().append (oldFirst->text()); + + // mark line as modified, since text was appended + newFirst->markAsModified(true); + } + + // patch startLine of this block + --m_startLine; + + /** + * fix all start lines + * we need to do this NOW, else the range update will FAIL! + * bug 313759 + */ + m_buffer->fixStartLines (fixStartLinesStartIndex); + + /** + * notify the text history in advance + */ + m_buffer->history().unwrapLine (startLine () + line, oldSizeOfPreviousLine); + + /** + * cursor and range handling below + */ + + // no cursors in this block and the previous one, no work to do.. + if (m_cursors.empty() && previousBlock->m_cursors.empty()) + return; + + // move all cursors because of the unwrapped line + // remember all ranges modified + QSet changedRanges; + foreach (TextCursor *cursor, m_cursors) { + // this is the unwrapped line + if (cursor->lineInBlock() == 0) { + // patch column + cursor->m_column += oldSizeOfPreviousLine; + + // remember range, if any + if (cursor->kateRange()) + changedRanges.insert (cursor->kateRange()); + } + } + + // move cursors of the moved line from previous block to this block now + QSet newPreviousCursors; + foreach (TextCursor *cursor, previousBlock->m_cursors) { + if (cursor->lineInBlock() == lastLineOfPreviousBlock) { + cursor->m_line = 0; + cursor->m_block = this; + m_cursors.insert (cursor); + + // remember range, if any + if (cursor->kateRange()) + changedRanges.insert (cursor->kateRange()); + } + else + newPreviousCursors.insert (cursor); + } + previousBlock->m_cursors = newPreviousCursors; + + // fixup the ranges that might be effected, because they moved from last line to this block + foreach (TextRange *range, changedRanges) { + // update both blocks + updateRange (range); + previousBlock->updateRange (range); + } + + // check validity of all ranges, might invalidate them... + foreach (TextRange *range, changedRanges) + range->checkValidity (); + + // be done + return; + } + + // easy: just move text to previous line and remove current one + const int oldSizeOfPreviousLine = m_lines.at(line-1)->length(); + const int sizeOfCurrentLine = m_lines.at(line)->length(); + if (sizeOfCurrentLine > 0) + m_lines.at(line-1)->textReadWrite().append (m_lines.at(line)->text()); + + const bool lineChanged = (oldSizeOfPreviousLine > 0 && m_lines.at(line - 1)->markedAsModified()) + || (sizeOfCurrentLine > 0 && (oldSizeOfPreviousLine > 0 || m_lines.at(line)->markedAsModified())); + m_lines.at(line-1)->markAsModified(lineChanged); + if (oldSizeOfPreviousLine == 0 && m_lines.at(line)->markedAsSavedOnDisk()) + m_lines.at(line-1)->markAsSavedOnDisk(true); + + m_lines.erase (m_lines.begin () + line); + + /** + * fix all start lines + * we need to do this NOW, else the range update will FAIL! + * bug 313759 + */ + m_buffer->fixStartLines (fixStartLinesStartIndex); + + /** + * notify the text history in advance + */ + m_buffer->history().unwrapLine (startLine () + line, oldSizeOfPreviousLine); + + /** + * cursor and range handling below + */ + + // no cursors in this block, no work to do.. + if (m_cursors.empty()) + return; + + // move all cursors because of the unwrapped line + // remember all ranges modified + QSet changedRanges; + foreach (TextCursor *cursor, m_cursors) { + // skip cursors in lines in front of removed one + if (cursor->lineInBlock() < line) + continue; + + // this is the unwrapped line + if (cursor->lineInBlock() == line) { + // patch column + cursor->m_column += oldSizeOfPreviousLine; + } + + // patch line of cursor + cursor->m_line--; + + // remember range, if any + if (cursor->kateRange()) + changedRanges.insert (cursor->kateRange()); + } + + // check validity of all ranges, might invalidate them... + foreach (TextRange *range, changedRanges) + range->checkValidity (); +} + +void TextBlock::insertText (const KTextEditor::Cursor &position, const QString &text) +{ + // calc internal line + int line = position.line () - startLine (); + + // get text + QString &textOfLine = m_lines.at(line)->textReadWrite (); + int oldLength = textOfLine.size (); + m_lines.at(line)->markAsModified(true); + + // check if valid column + Q_ASSERT (position.column() >= 0); + Q_ASSERT (position.column() <= textOfLine.size()); + + // insert text + textOfLine.insert (position.column(), text); + + /** + * notify the text history + */ + m_buffer->history().insertText (position, text.size(), oldLength); + + /** + * cursor and range handling below + */ + + // no cursors in this block, no work to do.. + if (m_cursors.empty()) + return; + + // move all cursors on the line which has the text inserted + // remember all ranges modified + QSet changedRanges; + foreach (TextCursor *cursor, m_cursors) { + // skip cursors not on this line! + if (cursor->lineInBlock() != line) + continue; + + // skip cursors with too small column + if (cursor->column() <= position.column()) { + if (cursor->column() < position.column() || !cursor->m_moveOnInsert) + continue; + } + + // patch column of cursor + if (cursor->m_column <= oldLength) + cursor->m_column += text.size (); + + // special handling if cursor behind the real line, e.g. non-wrapping cursor in block selection mode + else if (cursor->m_column < textOfLine.size()) + cursor->m_column = textOfLine.size(); + + // remember range, if any + if (cursor->kateRange()) + changedRanges.insert (cursor->kateRange()); + } + + // check validity of all ranges, might invalidate them... + foreach (TextRange *range, changedRanges) + range->checkValidity (); +} + +void TextBlock::removeText (const KTextEditor::Range &range, QString &removedText) +{ + // calc internal line + int line = range.start().line () - startLine (); + + // get text + QString &textOfLine = m_lines.at(line)->textReadWrite (); + int oldLength = textOfLine.size (); + + // check if valid column + Q_ASSERT (range.start().column() >= 0); + Q_ASSERT (range.start().column() <= textOfLine.size()); + Q_ASSERT (range.end().column() >= 0); + Q_ASSERT (range.end().column() <= textOfLine.size()); + + // get text which will be removed + removedText = textOfLine.mid (range.start().column(), range.end().column() - range.start().column()); + + // remove text + textOfLine.remove (range.start().column(), range.end().column() - range.start().column()); + m_lines.at(line)->markAsModified(true); + + /** + * notify the text history + */ + m_buffer->history().removeText (range, oldLength); + + /** + * cursor and range handling below + */ + + // no cursors in this block, no work to do.. + if (m_cursors.empty()) + return; + + // move all cursors on the line which has the text removed + // remember all ranges modified + QSet changedRanges; + foreach (TextCursor *cursor, m_cursors) { + // skip cursors not on this line! + if (cursor->lineInBlock() != line) + continue; + + // skip cursors with too small column + if (cursor->column() <= range.start().column()) + continue; + + // patch column of cursor + if (cursor->column() <= range.end().column()) + cursor->m_column = range.start().column (); + else + cursor->m_column -= (range.end().column() - range.start().column()); + + // remember range, if any + if (cursor->kateRange()) + changedRanges.insert (cursor->kateRange()); + } + + // check validity of all ranges, might invalidate them... + foreach (TextRange *range, changedRanges) + range->checkValidity (); +} + +void TextBlock::debugPrint (int blockIndex) const +{ + // print all blocks + for (int i = 0; i < m_lines.size(); ++i) + printf ("%4d - %4d : %4d : '%s'\n", blockIndex, startLine() + i + , m_lines.at(i)->text().size(), qPrintable (m_lines.at(i)->text())); +} + +TextBlock *TextBlock::splitBlock (int fromLine) +{ + // half the block + int linesOfNewBlock = lines () - fromLine; + + // create and insert new block + TextBlock *newBlock = new TextBlock (m_buffer, startLine() + fromLine); + + // move lines + newBlock->m_lines.reserve (linesOfNewBlock); + for (int i = fromLine; i < m_lines.size(); ++i) + newBlock->m_lines.append (m_lines.at(i)); + m_lines.resize (fromLine); + + // move cursors + QSet oldBlockSet; + foreach (TextCursor *cursor, m_cursors) { + if (cursor->lineInBlock() >= fromLine) { + cursor->m_line = cursor->lineInBlock() - fromLine; + cursor->m_block = newBlock; + newBlock->m_cursors.insert (cursor); + } + else + oldBlockSet.insert (cursor); + } + m_cursors = oldBlockSet; + + // fix ALL ranges! + QList allRanges = m_uncachedRanges.toList() + m_cachedLineForRanges.keys(); + foreach (TextRange *range, allRanges) { + // update both blocks + updateRange (range); + newBlock->updateRange (range); + } + + // return the new generated block + return newBlock; +} + +void TextBlock::mergeBlock (TextBlock *targetBlock) +{ + // move cursors, do this first, now still lines() count is correct for target + foreach (TextCursor *cursor, m_cursors) { + cursor->m_line = cursor->lineInBlock() + targetBlock->lines (); + cursor->m_block = targetBlock; + targetBlock->m_cursors.insert (cursor); + } + m_cursors.clear (); + + // move lines + targetBlock->m_lines.reserve (targetBlock->lines() + lines ()); + for (int i = 0; i < m_lines.size(); ++i) + targetBlock->m_lines.append (m_lines.at(i)); + m_lines.clear (); + + // fix ALL ranges! + QList allRanges = m_uncachedRanges.toList() + m_cachedLineForRanges.keys(); + foreach(TextRange* range, allRanges) { + // update both blocks + updateRange (range); + targetBlock->updateRange (range); + } +} + +void TextBlock::deleteBlockContent () +{ + // kill cursors, if not belonging to a range + QSet copy = m_cursors; + foreach (TextCursor *cursor, copy) + if (!cursor->kateRange()) + delete cursor; + + // kill lines + m_lines.clear (); +} + +void TextBlock::clearBlockContent (TextBlock *targetBlock) +{ + // move cursors, if not belonging to a range + QSet copy = m_cursors; + foreach (TextCursor *cursor, copy) { + if (!cursor->kateRange()) { + cursor->m_column = 0; + cursor->m_line = 0; + cursor->m_block = targetBlock; + targetBlock->m_cursors.insert (cursor); + m_cursors.remove (cursor); + } + } + + // kill lines + m_lines.clear (); +} + +void TextBlock::markModifiedLinesAsSaved () +{ + // mark all modified lines as saved + for (int i = 0; i < m_lines.size(); ++i) { + TextLine textLine = m_lines[i]; + if (textLine->markedAsModified()) + textLine->markAsSavedOnDisk(true); + } +} + +void TextBlock::updateRange (TextRange* range) +{ + /** + * get some simple facts about our nice range + */ + const int startLine = range->startInternal().lineInternal(); + const int endLine = range->endInternal().lineInternal(); + const bool isSingleLine = startLine == endLine; + + /** + * perhaps remove range and be done + */ + if ((endLine < m_startLine) || (startLine >= (m_startLine + lines()))) { + removeRange (range); + return; + } + + /** + * The range is still a single-line range, and is still cached to the correct line. + */ + if(isSingleLine && m_cachedLineForRanges.contains (range) && (m_cachedLineForRanges.value(range) == startLine - m_startLine)) + return; + + /** + * The range is still a multi-line range, and is already in the correct set. + */ + if(!isSingleLine && m_uncachedRanges.contains (range)) + return; + + /** + * remove, if already there! + */ + removeRange(range); + + /** + * simple case: multi-line range + */ + if (!isSingleLine) { + /** + * The range cannot be cached per line, as it spans multiple lines + */ + m_uncachedRanges.insert(range); + return; + } + + /** + * The range is contained by a single line, put it into the line-cache + */ + const int lineOffset = startLine - m_startLine; + + /** + * enlarge cache if needed + */ + if (m_cachedRangesForLine.size() <= lineOffset) + m_cachedRangesForLine.resize(lineOffset+1); + + /** + * insert into mapping + */ + m_cachedRangesForLine[lineOffset].insert(range); + m_cachedLineForRanges[range] = lineOffset; +} + +void TextBlock::removeRange (TextRange* range) +{ + /** + * uncached range? remove it and be done + */ + if(m_uncachedRanges.remove (range)) { + /** + * must be only uncached! + */ + Q_ASSERT (!m_cachedLineForRanges.contains(range)); + return; + } + + /** + * cached range? + */ + QHash::iterator it = m_cachedLineForRanges.find(range); + if (it != m_cachedLineForRanges.end()) { + /** + * must be only cached! + */ + Q_ASSERT (!m_uncachedRanges.contains(range)); + + /** + * query the range from cache, must be there + */ + Q_ASSERT (m_cachedRangesForLine.at(*it).contains(range)); + + /** + * remove it and be done + */ + m_cachedRangesForLine[*it].remove(range); + m_cachedLineForRanges.erase(it); + return; + } + + /** + * else: range was not for this block, just do nothing, removeRange should be "safe" to use + */ +} + +} diff --git a/kate/part/buffer/katetextblock.h b/kate/part/buffer/katetextblock.h new file mode 100644 index 00000000..87893c89 --- /dev/null +++ b/kate/part/buffer/katetextblock.h @@ -0,0 +1,263 @@ +/* This file is part of the Kate project. + * + * Copyright (C) 2010 Christoph Cullmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef KATE_TEXTBLOCK_H +#define KATE_TEXTBLOCK_H + +#include +#include + +#include "katepartinterfaces_export.h" +#include +#include "katetextline.h" + +namespace Kate { + +class TextBuffer; +class TextCursor; +class TextRange; + +/** + * Class representing a text block. + * This is used to build up a Kate::TextBuffer. + * This class should only be used by TextBuffer/Cursor/Range. + */ +class KATEPARTINTERFACES_EXPORT TextBlock { + public: + /** + * Construct an empty text block. + * @param buffer parent text buffer + * @param startLine start line of this block + */ + TextBlock (TextBuffer *buffer, int startLine); + + /** + * Destruct the text block + */ + ~TextBlock (); + + /** + * Start line of this block. + * @return start line of this block + */ + int startLine () const { return m_startLine; } + + /** + * Set start line of this block. + * @param startLine new start line of this block + */ + void setStartLine (int startLine); + + /** + * Retrieve a text line. + * @param line wanted line number + * @return text line + */ + TextLine line (int line) const; + + /** + * Append a new line with given text. + * @param textOfLine text of the line to append + */ + void appendLine (const QString &textOfLine); + + /** + * Clear the lines. + */ + void clearLines (); + + /** + * Number of lines in this block. + * @return number of lines + */ + int lines () const { return m_lines.size(); } + + /** + * Retrieve text of block. + * @param text for this block, lines separated by '\n' + */ + void text (QString &text) const; + + /** + * Wrap line at given cursor position. + * @param position line/column as cursor where to wrap + * @param fixStartLinesStartIndex start index to fix start lines, normally this is this block + */ + void wrapLine (const KTextEditor::Cursor &position, int fixStartLinesStartIndex); + + /** + * Unwrap given line. + * @param line line to unwrap + * @param previousBlock previous block, if any, if we unwrap first line in block, we need to have this + * @param fixStartLinesStartIndex start index to fix start lines, normally this is this block or the previous one + */ + void unwrapLine (int line, TextBlock *previousBlock, int fixStartLinesStartIndex); + + /** + * Insert text at given cursor position. + * @param position position where to insert text + * @param text text to insert + */ + void insertText (const KTextEditor::Cursor &position, const QString &text); + + /** + * Remove text at given range. + * @param range range of text to remove, must be on one line only. + * @param removedText will be filled with removed text + */ + void removeText (const KTextEditor::Range &range, QString &removedText); + + /** + * Debug output, print whole block content with line numbers and line length + * @param blockIndex index of this block in buffer + */ + void debugPrint (int blockIndex) const; + + /** + * Split given block. A new block will be created and all lines starting from the given index will + * be moved to it, together with the cursors belonging to it. + * @param fromLine line from which to split + * @return new block containing the lines + cursors removed from this one + */ + TextBlock *splitBlock (int fromLine); + + /** + * Merge this block with given one, the given one must be a direct predecessor. + * @param targetBlock block to merge with + */ + void mergeBlock (TextBlock *targetBlock); + + /** + * Delete the block content, delete all lines and delete all cursors not bound to ranges. + * This is used in destructor of TextBuffer, for fast cleanup. Only stuff remaining afterwards are cursors which are + * part of a range, TextBuffer will delete them itself... + */ + void deleteBlockContent (); + + /** + * Clear the block content, delete all lines, move all cursors not bound to range to given block at 0,0. + * This is used by clear() of TextBuffer. + * @param targetBlock empty target block for cursors + */ + void clearBlockContent (TextBlock *targetBlock); + + /** + * Return all ranges in this block which might intersect the given line. + * @param line line to check intersection + * @return list of sets of possible candidate ranges + */ + QList > rangesForLine (int line) const { + return QList >() << m_uncachedRanges << cachedRangesForLine(line); + } + + /** + * Is the given range contained in this block? + * @param range range to check for + * @return contained in this blocks mapping? + */ + bool containsRange (TextRange* range) const { + return m_cachedLineForRanges.contains(range) || m_uncachedRanges.contains(range); + } + + /** + * Flag all modified text lines as saved on disk. + */ + void markModifiedLinesAsSaved (); + + /** + * Insert cursor into this block. + * @param cursor cursor to insert + */ + void insertCursor (Kate::TextCursor *cursor) { m_cursors.insert (cursor); } + + /** + * Remove cursor from this block. + * @param cursor cursor to remove + */ + void removeCursor (Kate::TextCursor *cursor) { m_cursors.remove (cursor); } + + /** + * Update a range from this block. + * Will move the range to right set, either cached for one-line ranges or not. + * @param range range to update + */ + void updateRange(TextRange* range); + + /** + * Remove a range from this block. + * @param range range to remove + */ + void removeRange (TextRange* range); + + /** + * Return all ranges in this block which might intersect the given line and only span one line. + * For them an internal fast lookup cache is hold. + * @param line line to check intersection + * @return set of ranges + */ + QSet cachedRangesForLine (int line) const { + line -= m_startLine; + if(line >= 0 && line < m_cachedRangesForLine.size()) + return m_cachedRangesForLine[line]; + else + return QSet(); + } + + private: + /** + * parent text buffer + */ + TextBuffer *m_buffer; + + /** + * Lines contained in this buffer. These are shared pointers. + */ + QVector m_lines; + + /** + * Startline of this block + */ + int m_startLine; + + /** + * Set of cursors for this block. + */ + QSet m_cursors; + + /** + * Contains for each line-offset the ranges that were cached into it. + * These ranges are fully contained by the line. + */ + QVector > m_cachedRangesForLine; + + /** + * Maps for each cached range the line into which the range was cached. + */ + QHash m_cachedLineForRanges; + + /** + * This contains all the ranges that are not cached. + */ + QSet m_uncachedRanges; +}; + +} + +#endif diff --git a/kate/part/buffer/katetextbuffer.cpp b/kate/part/buffer/katetextbuffer.cpp new file mode 100644 index 00000000..b5152ab8 --- /dev/null +++ b/kate/part/buffer/katetextbuffer.cpp @@ -0,0 +1,873 @@ +/* This file is part of the Kate project. + * + * Copyright (C) 2010 Christoph Cullmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" + +#include "katetextbuffer.h" +#include "katetextloader.h" + +// this is unfortunate, but needed for performance +#include "katedocument.h" +#include "kateview.h" + +// fdatasync +#include + +#include +#include + +#if 0 +#define EDIT_DEBUG kDebug() +#else +#define EDIT_DEBUG if (0) kDebug() +#endif + +namespace Kate { + +TextBuffer::TextBuffer (KateDocument *parent, int blockSize) + : QObject (parent) + , m_document (parent) + , m_history (*this) + , m_blockSize (blockSize) + , m_lines (0) + , m_lastUsedBlock (0) + , m_revision (0) + , m_editingTransactions (0) + , m_editingLastRevision (0) + , m_editingLastLines (0) + , m_editingMinimalLineChanged (-1) + , m_editingMaximalLineChanged (-1) + , m_fallbackTextCodec (0) + , m_textCodec (0) + , m_generateByteOrderMark (false) + , m_endOfLineMode (eolUnix) + , m_newLineAtEof (false) + , m_lineLengthLimit (4096) +{ + // minimal block size must be > 0 + Q_ASSERT (m_blockSize > 0); + + // create initial state + clear (); +} + +TextBuffer::~TextBuffer () +{ + // remove document pointer, this will avoid any notifyAboutRangeChange to have a effect + m_document = 0; + + // not allowed during editing + Q_ASSERT (m_editingTransactions == 0); + + // kill all ranges, work on copy, they will remove themself from the hash + QSet copyRanges = m_ranges; + qDeleteAll (copyRanges); + Q_ASSERT (m_ranges.empty()); + + // clean out all cursors and lines, only cursors belonging to range will survive + foreach(TextBlock* block, m_blocks) + block->deleteBlockContent (); + + // delete all blocks, now that all cursors are really deleted + // else asserts in destructor of blocks will fail! + qDeleteAll (m_blocks); + m_blocks.clear (); + + // kill all invalid cursors, do this after block deletion, to uncover if they might be still linked in blocks + QSet copyCursors = m_invalidCursors; + qDeleteAll (copyCursors); + Q_ASSERT (m_invalidCursors.empty()); +} + +void TextBuffer::invalidateRanges() +{ + // invalidate all ranges, work on copy, they might delete themself... + QSet copyRanges = m_ranges; + foreach (TextRange *range, copyRanges) + range->setRange (KTextEditor::Cursor::invalid(), KTextEditor::Cursor::invalid()); +} + +void TextBuffer::clear () +{ + // not allowed during editing + Q_ASSERT (m_editingTransactions == 0); + + invalidateRanges(); + + // new block for empty buffer + TextBlock *newBlock = new TextBlock (this, 0); + newBlock->appendLine (QString()); + + // clean out all cursors and lines, either move them to newBlock or invalidate them, if belonging to a range + foreach(TextBlock* block, m_blocks) + block->clearBlockContent (newBlock); + + // kill all buffer blocks + qDeleteAll (m_blocks); + m_blocks.clear (); + + // insert one block with one empty line + m_blocks.append (newBlock); + + // reset lines and last used block + m_lines = 1; + m_lastUsedBlock = 0; + + // reset revision + m_revision = 0; + + // reset bom detection + m_generateByteOrderMark = false; + + // clear edit history + m_history.clear (); + + // we got cleared + emit cleared (); +} + +TextLine TextBuffer::line (int line) const +{ + // get block, this will assert on invalid line + int blockIndex = blockForLine (line); + + // get line + return m_blocks.at(blockIndex)->line (line); +} + +QString TextBuffer::text () const +{ + QString text; + + // combine all blocks + foreach(TextBlock* block, m_blocks) + block->text (text); + + // return generated string + return text; +} + +bool TextBuffer::startEditing () +{ + // increment transaction counter + ++m_editingTransactions; + + // if not first running transaction, do nothing + if (m_editingTransactions > 1) + return false; + + // reset information about edit... + m_editingLastRevision = m_revision; + m_editingLastLines = m_lines; + m_editingMinimalLineChanged = -1; + m_editingMaximalLineChanged = -1; + + // transaction has started + emit editingStarted (); + + // first transaction started + return true; +} + +bool TextBuffer::finishEditing () +{ + // only allowed if still transactions running + Q_ASSERT (m_editingTransactions > 0); + + // decrement counter + --m_editingTransactions; + + // if not last running transaction, do nothing + if (m_editingTransactions > 0) + return false; + + // assert that if buffer changed, the line ranges are set and valid! + Q_ASSERT (!editingChangedBuffer() || (m_editingMinimalLineChanged != -1 && m_editingMaximalLineChanged != -1)); + Q_ASSERT (!editingChangedBuffer() || (m_editingMinimalLineChanged <= m_editingMaximalLineChanged)); + Q_ASSERT (!editingChangedBuffer() || (m_editingMinimalLineChanged >= 0 && m_editingMinimalLineChanged < m_lines)); + Q_ASSERT (!editingChangedBuffer() || (m_editingMaximalLineChanged >= 0 && m_editingMaximalLineChanged < m_lines)); + + // transaction has finished + emit editingFinished (); + + // last transaction finished + return true; +} + +void TextBuffer::wrapLine (const KTextEditor::Cursor &position) +{ + // debug output for REAL low-level debugging + EDIT_DEBUG << "wrapLine" << position; + + // only allowed if editing transaction running + Q_ASSERT (m_editingTransactions > 0); + + // get block, this will assert on invalid line + int blockIndex = blockForLine (position.line()); + + /** + * let the block handle the wrapLine + * this can only lead to one more line in this block + * no other blocks will change + * this call will trigger fixStartLines + */ + ++m_lines; // first alter the line counter, as functions called will need the valid one + m_blocks.at(blockIndex)->wrapLine (position, blockIndex); + + // remember changes + ++m_revision; + + // update changed line interval + if (position.line() < m_editingMinimalLineChanged || m_editingMinimalLineChanged == -1) + m_editingMinimalLineChanged = position.line(); + + if (position.line() <= m_editingMaximalLineChanged) + ++m_editingMaximalLineChanged; + else + m_editingMaximalLineChanged = position.line() + 1; + + // balance the changed block if needed + balanceBlock (blockIndex); + + // emit signal about done change + emit lineWrapped (position); +} + +void TextBuffer::unwrapLine (int line) +{ + // debug output for REAL low-level debugging + EDIT_DEBUG << "unwrapLine" << line; + + // only allowed if editing transaction running + Q_ASSERT (m_editingTransactions > 0); + + // line 0 can't be unwrapped + Q_ASSERT (line > 0); + + // get block, this will assert on invalid line + int blockIndex = blockForLine (line); + + // is this the first line in the block? + bool firstLineInBlock = (line == m_blocks.at(blockIndex)->startLine()); + + /** + * let the block handle the unwrapLine + * this can either lead to one line less in this block or the previous one + * the previous one could even end up with zero lines + * this call will trigger fixStartLines + */ + m_blocks.at(blockIndex)->unwrapLine (line, (blockIndex > 0) ? m_blocks.at(blockIndex-1) : 0, firstLineInBlock ? (blockIndex - 1) : blockIndex); + --m_lines; + + // decrement index for later fixup, if we modified the block in front of the found one + if (firstLineInBlock) + --blockIndex; + + // remember changes + ++m_revision; + + // update changed line interval + if ((line - 1) < m_editingMinimalLineChanged || m_editingMinimalLineChanged == -1) + m_editingMinimalLineChanged = line - 1; + + if (line <= m_editingMaximalLineChanged) + --m_editingMaximalLineChanged; + else + m_editingMaximalLineChanged = line -1; + + // balance the changed block if needed + balanceBlock (blockIndex); + + // emit signal about done change + emit lineUnwrapped (line); +} + +void TextBuffer::insertText (const KTextEditor::Cursor &position, const QString &text) +{ + // debug output for REAL low-level debugging + EDIT_DEBUG << "insertText" << position << text; + + // only allowed if editing transaction running + Q_ASSERT (m_editingTransactions > 0); + + // skip work, if no text to insert + if (text.isEmpty()) + return; + + // get block, this will assert on invalid line + int blockIndex = blockForLine (position.line()); + + // let the block handle the insertText + m_blocks.at(blockIndex)->insertText (position, text); + + // remember changes + ++m_revision; + + // update changed line interval + if (position.line () < m_editingMinimalLineChanged || m_editingMinimalLineChanged == -1) + m_editingMinimalLineChanged = position.line (); + + if (position.line () > m_editingMaximalLineChanged) + m_editingMaximalLineChanged = position.line (); + + // emit signal about done change + emit textInserted (position, text); +} + +void TextBuffer::removeText (const KTextEditor::Range &range) +{ + // debug output for REAL low-level debugging + EDIT_DEBUG << "removeText" << range; + + // only allowed if editing transaction running + Q_ASSERT (m_editingTransactions > 0); + + // only ranges on one line are supported + Q_ASSERT (range.start().line() == range.end().line()); + + // start column <= end column and >= 0 + Q_ASSERT (range.start().column() <= range.end().column()); + Q_ASSERT (range.start().column() >= 0); + + // skip work, if no text to remove + if (range.isEmpty()) + return; + + // get block, this will assert on invalid line + int blockIndex = blockForLine (range.start().line()); + + // let the block handle the removeText, retrieve removed text + QString text; + m_blocks.at(blockIndex)->removeText (range, text); + + // remember changes + ++m_revision; + + // update changed line interval + if (range.start().line() < m_editingMinimalLineChanged || m_editingMinimalLineChanged == -1) + m_editingMinimalLineChanged = range.start().line(); + + if (range.start().line() > m_editingMaximalLineChanged) + m_editingMaximalLineChanged = range.start().line(); + + // emit signal about done change + emit textRemoved (range, text); +} + +int TextBuffer::blockForLine (int line) const +{ + // only allow valid lines + if ((line < 0) || (line >= lines())) + qFatal ("out of range line requested in text buffer (%d out of [0, %d[)", line, lines()); + + // we need blocks and last used block should not be negative + Q_ASSERT (!m_blocks.isEmpty()); + Q_ASSERT (m_lastUsedBlock >= 0); + + /** + * shortcut: try last block first + */ + if (m_lastUsedBlock < m_blocks.size()) { + /** + * check if block matches + * if yes, just return again this block + */ + TextBlock* block = m_blocks[m_lastUsedBlock]; + const int start = block->startLine(); + const int lines = block->lines (); + if (start <= line && line < (start + lines)) + return m_lastUsedBlock; + } + + /** + * search for right block + * use binary search + * if we leave this loop not by returning the found element we have an error + */ + int blockStart = 0; + int blockEnd = m_blocks.size() - 1; + while (blockEnd >= blockStart) { + // get middle and ensure it is OK + int middle = blockStart + ((blockEnd - blockStart) / 2); + Q_ASSERT (middle >= 0); + Q_ASSERT (middle < m_blocks.size()); + + // facts bout this block + TextBlock* block = m_blocks[middle]; + const int start = block->startLine(); + const int lines = block->lines (); + + // right block found, remember it and return it + if (start <= line && line < (start + lines)) { + m_lastUsedBlock = middle; + return middle; + } + + // half our stuff ;) + if (line < start) + blockEnd = middle - 1; + else + blockStart = middle + 1; + } + + // we should always find a block + qFatal ("line requested in text buffer (%d out of [0, %d[), no block found", line, lines()); + return -1; +} + +void TextBuffer::fixStartLines (int startBlock) +{ + // only allow valid start block + Q_ASSERT (startBlock >= 0); + Q_ASSERT (startBlock < m_blocks.size()); + + // new start line for next block + TextBlock* block = m_blocks.at(startBlock); + int newStartLine = block->startLine () + block->lines (); + + // fixup block + for (int index = startBlock + 1; index < m_blocks.size(); ++index) { + // set new start line + block = m_blocks.at(index); + block->setStartLine (newStartLine); + + // calculate next start line + newStartLine += block->lines (); + } +} + +void TextBuffer::balanceBlock (int index) +{ + /** + * two cases, too big or too small block + */ + TextBlock *blockToBalance = m_blocks.at(index); + + // first case, too big one, split it + if (blockToBalance->lines () >= 2 * m_blockSize) { + // half the block + int halfSize = blockToBalance->lines () / 2; + + // create and insert new block behind current one, already set right start line + TextBlock *newBlock = blockToBalance->splitBlock (halfSize); + Q_ASSERT (newBlock); + m_blocks.insert (m_blocks.begin() + index + 1, newBlock); + + // split is done + return; + } + + // second case: possibly too small block + + // if only one block, no chance to unite + // same if this is first block, we always append to previous one + if (index == 0) + return; + + // block still large enough, do nothing + if (2 * blockToBalance->lines () > m_blockSize) + return; + + // unite small block with predecessor + TextBlock *targetBlock = m_blocks.at(index-1); + + // merge block + blockToBalance->mergeBlock (targetBlock); + + // delete old block + delete blockToBalance; + m_blocks.erase (m_blocks.begin() + index); +} + +void TextBuffer::debugPrint (const QString &title) const +{ + // print header with title + printf ("%s (lines: %d bs: %d)\n", qPrintable (title), m_lines, m_blockSize); + + // print all blocks + for (int i = 0; i < m_blocks.size(); ++i) + m_blocks.at(i)->debugPrint (i); +} + +bool TextBuffer::load (const QString &filename, bool &encodingErrors, bool &tooLongLinesWrapped, bool enforceTextCodec) +{ + // fallback codec must exist + Q_ASSERT (m_fallbackTextCodec); + + // codec must be set! + Q_ASSERT (m_textCodec); + + /** + * first: clear buffer in any case! + */ + clear (); + + /** + * construct the file loader for the given file, with correct prober type + */ + Kate::TextLoader file (filename); + + /** + * triple play, maximal three loading rounds + * 0) use the given encoding, be done, if no encoding errors happen + * 1) use BOM to decided if unicode or if that fails, use encoding prober, if no encoding errors happen, be done + * 2) use fallback encoding, be done, if no encoding errors happen + * 3) use again given encoding, be done in any case + */ + for (int i = 0; i < (enforceTextCodec ? 1 : 4); ++i) { + /** + * kill all blocks beside first one + */ + for (int b = 1; b < m_blocks.size(); ++b) { + TextBlock* block = m_blocks.at(b); + block->clearLines (); + delete block; + } + m_blocks.resize (1); + + /** + * remove lines in first block + */ + m_blocks.last()->clearLines (); + m_lines = 0; + + /** + * try to open file, with given encoding + * in round 0 + 3 use the given encoding from user + * in round 1 use 0, to trigger detection + * in round 2 use fallback + */ + QTextCodec *codec = m_textCodec; + if (i == 1) + codec = 0; + else if (i == 2) + codec = m_fallbackTextCodec; + + if (!file.open (codec)) { + // create one dummy textline, in any case + m_blocks.last()->appendLine (QString()); + m_lines++; + return false; + } + + // read in all lines... + encodingErrors = false; + while ( !file.eof() ) + { + // read line + int offset = 0, length = 0; + bool currentError = !file.readLine (offset, length); + encodingErrors = encodingErrors || currentError; + + // bail out on encoding error, if not last round! + if (encodingErrors && i < (enforceTextCodec ? 0 : 3)) { + kDebug (13020) << "Failed try to load file" << filename << "with codec" << + (file.textCodec() ? file.textCodec()->name() : "(null)"); + break; + } + + // get unicode data for this line + const QChar *unicodeData = file.unicode () + offset; + + /** + * split lines, if too large + */ + do { + /** + * calculate line length + */ + int lineLength = length; + if ((m_lineLengthLimit > 0) && (lineLength > m_lineLengthLimit)) { + /** + * search for place to wrap + */ + int spacePosition = m_lineLengthLimit-1; + for (int testPosition = m_lineLengthLimit-1; (testPosition >= 0) && (testPosition >= (m_lineLengthLimit - (m_lineLengthLimit/10))); --testPosition) { + /** + * wrap place found? + */ + if (unicodeData[testPosition].isSpace() || unicodeData[testPosition].isPunct()) { + spacePosition = testPosition; + break; + } + } + + /** + * wrap the line + */ + lineLength = spacePosition+1; + length -= lineLength; + tooLongLinesWrapped = true; + } else { + /** + * be done after this round + */ + length = 0; + } + + /** + * construct new text line with content from file + * move data pointer + */ + QString textLine (unicodeData, lineLength); + unicodeData += lineLength; + + /** + * ensure blocks aren't too large + */ + if (m_blocks.last()->lines() >= m_blockSize) + m_blocks.append (new TextBlock (this, m_blocks.last()->startLine() + m_blocks.last()->lines())); + + /** + * append line to last block + */ + m_blocks.last()->appendLine (textLine); + ++m_lines; + } while (length > 0); + } + + // if no encoding error, break out of reading loop + if (!encodingErrors) { + // remember used codec, might change bom setting + setTextCodec (file.textCodec ()); + break; + } + } + + // save sha1sum of file on disk + setDigest (file.digest ()); + + // remember if BOM was found + if (file.byteOrderMarkFound ()) + setGenerateByteOrderMark (true); + + // remember eol mode, if any found in file + if (file.eol() != eolUnknown) + setEndOfLineMode (file.eol()); + + // assert that one line is there! + Q_ASSERT (m_lines > 0); + + // report CODEC + ERRORS + kDebug (13020) << "Loaded file " << filename << "with codec" << m_textCodec->name() + << (encodingErrors ? "with" : "without") << "encoding errors"; + + // report BOM + kDebug (13020) << (file.byteOrderMarkFound () ? "Found" : "Didn't find") << "byte order mark"; + + // emit success + emit loaded (filename, encodingErrors); + + // file loading worked, modulo encoding problems + return true; +} + +const QByteArray &TextBuffer::digest () const +{ + return m_digest; +} + +void TextBuffer::setDigest (const QByteArray & sha1sum) +{ + m_digest = sha1sum; +} + +void TextBuffer::setTextCodec (QTextCodec *codec) +{ + m_textCodec = codec; + + // enforce bom for some encodings + int mib = m_textCodec->mibEnum (); + if (mib == 1013 || mib == 1014 || mib == 1015) // utf16 + setGenerateByteOrderMark (true); + if (mib == 1017 || mib == 1018 || mib == 1019) // utf32 + setGenerateByteOrderMark (true); +} + +bool TextBuffer::save (const QString &filename) +{ + // codec must be set! + Q_ASSERT (m_textCodec); + + /** + * use KSaveFile for save write + rename + */ + KSaveFile saveFile (filename); + + /** + * allow fallback if directory not writable + * fixes bug 312415 + */ + saveFile.setDirectWriteFallback (true); + + /** + * try to open or fail + */ + if (!saveFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) { + return false; + } + + /** + * construct stream + disable Unicode headers + */ + QTextStream stream (&saveFile); + stream.setCodec (QTextCodec::codecForName("UTF-16")); + + // set the correct codec + stream.setCodec (m_textCodec); + + // generate byte order mark? + stream.setGenerateByteOrderMark (generateByteOrderMark()); + + // our loved eol string ;) + QString eol = "\n"; //m_doc->config()->eolString (); + if (endOfLineMode() == eolDos) + eol = QString ("\r\n"); + else if (endOfLineMode() == eolMac) + eol = QString ("\r"); + + // just dump the lines out ;) + for (int i = 0; i < m_lines; ++i) + { + // get line to save + Kate::TextLine textline = line (i); + + stream << textline->text(); + + // append correct end of line string + if ((i+1) < m_lines) + stream << eol; + } + + if (m_newLineAtEof) { + Q_ASSERT(m_lines > 0); // see .h file + const Kate::TextLine lastLine = line (m_lines - 1); + const int firstChar = lastLine->firstChar(); + if (firstChar > -1 || lastLine->length() > 0) { + stream << eol; + } + } + + // flush stream + stream.flush (); + + // flush file + if (!saveFile.flush()) + return false; + + // ensure that the file is written to disk +#ifdef HAVE_FDATASYNC + fdatasync (saveFile.handle()); +#else + fsync (saveFile.handle()); +#endif + + // did save work? + // only finalize if stream status == OK + bool ok = (stream.status() == QTextStream::Ok) && saveFile.finalize(); + + // remember this revision as last saved if we had success! + if (ok) + m_history.setLastSavedRevision (); + + // report CODEC + ERRORS + kDebug (13020) << "Saved file " << filename << "with codec" << m_textCodec->name() + << (ok ? "without" : "with") << "errors"; + + if (ok) + markModifiedLinesAsSaved(); + + // emit signal on success + if (ok) + emit saved (filename); + + // return success or not + return ok; +} + +void TextBuffer::notifyAboutRangeChange (KTextEditor::View *view, int startLine, int endLine, bool rangeWithAttribute) +{ + /** + * ignore calls if no document is around + */ + if (!m_document) + return; + + /** + * update all views, this IS ugly and could be a signal, but I profiled and a signal is TOO slow, really + * just create 20k ranges in a go and you wait seconds on a decent machine + */ + const QList &views = m_document->views (); + foreach(KTextEditor::View* curView, views) { + // filter wrong views + if (view && view != curView) + continue; + + // notify view, it is really a kate view + static_cast (curView)->notifyAboutRangeChange (startLine, endLine, rangeWithAttribute); + } +} + +void TextBuffer::markModifiedLinesAsSaved() +{ + foreach(TextBlock* block, m_blocks) + block->markModifiedLinesAsSaved (); +} + +QList TextBuffer::rangesForLine (int line, KTextEditor::View *view, bool rangesWithAttributeOnly) const +{ + // get block, this will assert on invalid line + const int blockIndex = blockForLine (line); + + // get the ranges of the right block + QList rightRanges; + foreach (const QSet &ranges, m_blocks.at(blockIndex)->rangesForLine (line)) { + foreach (TextRange * const range, ranges) { + /** + * we want only ranges with attributes, but this one has none + */ + if (rangesWithAttributeOnly && !range->hasAttribute()) + continue; + + /** + * we want ranges for no view, but this one's attribute is only valid for views + */ + if (!view && range->attributeOnlyForViews()) + continue; + + /** + * the range's attribute is not valid for this view + */ + if (range->view() && range->view() != view) + continue; + + /** + * if line is in the range, ok + */ + if (range->startInternal().lineInternal() <= line && line <= range->endInternal().lineInternal()) + rightRanges.append (range); + } + } + + // return right ranges + return rightRanges; +} + +} diff --git a/kate/part/buffer/katetextbuffer.h b/kate/part/buffer/katetextbuffer.h new file mode 100644 index 00000000..3d3937a7 --- /dev/null +++ b/kate/part/buffer/katetextbuffer.h @@ -0,0 +1,548 @@ +/* This file is part of the Kate project. + * + * Copyright (C) 2010 Christoph Cullmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef KATE_TEXTBUFFER_H +#define KATE_TEXTBUFFER_H + +#include +#include +#include +#include +#include + +#include + +#include "katedocument.h" +#include "katepartinterfaces_export.h" +#include "katetextblock.h" +#include "katetextcursor.h" +#include "katetextrange.h" +#include "katetexthistory.h" + +namespace Kate { + +/** + * Class representing a text buffer. + * The interface is line based, internally the text will be stored in blocks of text lines. + */ +class KATEPARTINTERFACES_EXPORT TextBuffer : public QObject { + friend class TextCursor; + friend class TextRange; + friend class TextBlock; + + Q_OBJECT + + public: + /** + * End of line mode + */ + enum EndOfLineMode { + eolUnknown = -1 + , eolUnix = 0 + , eolDos = 1 + , eolMac = 2 + }; + + /** + * Construct an empty text buffer. + * Empty means one empty line in one block. + * @param parent parent qobject + * @param blockSize block size in lines the buffer should try to hold, default 64 lines + */ + TextBuffer (KateDocument *parent, int blockSize = 64); + + /** + * Destruct the text buffer + * Virtual, we allow inheritance + */ + virtual ~TextBuffer (); + + /** + * Clears the buffer, reverts to initial empty state. + * Empty means one empty line in one block. + * Virtual, can be overwritten. + */ + virtual void clear (); + + /** + * Set fallback codec for this buffer to use for load. + * @param codec fallback QTextCodec to use for encoding + */ + void setFallbackTextCodec (QTextCodec *codec) { m_fallbackTextCodec = codec; } + + /** + * Get fallback codec for this buffer + * @return currently in use fallback codec of this buffer + */ + QTextCodec *fallbackTextCodec () const { return m_fallbackTextCodec; } + + /** + * Set codec for this buffer to use for load/save. + * Loading might overwrite this, if it encounters problems and finds a better codec. + * Might change BOM setting. + * @param codec QTextCodec to use for encoding + */ + void setTextCodec (QTextCodec *codec); + + /** + * Get codec for this buffer + * @return currently in use codec of this buffer + */ + QTextCodec *textCodec () const { return m_textCodec; } + + /** + * Generate byte order mark on save. + * Loading might overwrite this setting, if there is a BOM found inside the file. + * @param generateByteOrderMark should BOM be generated? + */ + void setGenerateByteOrderMark (bool generateByteOrderMark) { m_generateByteOrderMark = generateByteOrderMark; } + + /** + * Generate byte order mark on save? + * @return should BOM be generated? + */ + bool generateByteOrderMark () const { return m_generateByteOrderMark; } + + /** + * Set end of line mode for this buffer, not allowed to be set to unknown. + * Loading might overwrite this setting, if there is a eol found inside the file. + * @param endOfLineMode new eol mode + */ + void setEndOfLineMode (EndOfLineMode endOfLineMode) { Q_ASSERT (endOfLineMode != eolUnknown); m_endOfLineMode = endOfLineMode; } + + /** + * Get end of line mode + * @return end of line mode + */ + EndOfLineMode endOfLineMode () const { return m_endOfLineMode; } + + /** + * Set whether to insert a newline character on save at the end of the file + * @param newlineAtEof should newline be added if non-existing + */ + void setNewLineAtEof(bool newlineAtEof) { m_newLineAtEof = newlineAtEof; } + + /** + * Set line length limit + * @param lineLengthLimit new line length limit + */ + void setLineLengthLimit (int lineLengthLimit) { m_lineLengthLimit = lineLengthLimit; } + + /** + * Load the given file. This will first clear the buffer and then load the file. + * Even on error during loading the buffer will still be cleared. + * Before calling this, setTextCodec must have been used to set codec! + * @param filename file to open + * @param encodingErrors were there problems occurred while decoding the file? + * @param tooLongLinesWrapped were too long lines found and wrapped? + * @param enforceTextCodec enforce to use only the set text codec + * @return success, the file got loaded, perhaps with encoding errors + * Virtual, can be overwritten. + */ + virtual bool load (const QString &filename, bool &encodingErrors, bool &tooLongLinesWrapped, bool enforceTextCodec); + + /** + * Save the current buffer content to the given file. + * Before calling this, setTextCodec and setFallbackTextCodec must have been used to set codec! + * @param filename file to save + * @return success + * Virtual, can be overwritten. + */ + virtual bool save (const QString &filename); + + /** + * Lines currently stored in this buffer. + * This is never 0, even clear will let one empty line remain. + */ + int lines () const { Q_ASSERT (m_lines > 0); return m_lines; } + + /** + * Revision of this buffer. Is set to 0 on construction, clear() (load will trigger clear()). + * Is incremented on each change to the buffer. + * @return current revision + */ + qint64 revision () const { return m_revision; } + + /** + * Retrieve a text line. + * @param line wanted line number + * @return text line + */ + TextLine line (int line) const; + + /** + * Retrieve text of complete buffer. + * @return text for this buffer, lines separated by '\n' + */ + QString text () const; + + /** + * Start an editing transaction, the wrapLine/unwrapLine/insertText and removeText functions + * are only to be allowed to be called inside a editing transaction. + * Editing transactions can stack. The number startEdit and endEdit calls must match. + * @return returns true, if no transaction was already running + * Virtual, can be overwritten. + */ + virtual bool startEditing (); + + /** + * Finish an editing transaction. Only allowed to be called if editing transaction is started. + * @return returns true, if this finished last running transaction + * Virtual, can be overwritten. + */ + virtual bool finishEditing (); + + /** + * Query the number of editing transactions running atm. + * @return number of running transactions + */ + int editingTransactions () const { return m_editingTransactions; } + + /** + * Query the revsion of this buffer before the ongoing editing transactions. + * @return revision of buffer before current editing transaction altered it + */ + qint64 editingLastRevision () const { return m_editingLastRevision; } + + /** + * Query the number of lines of this buffer before the ongoing editing transactions. + * @return number of lines of buffer before current editing transaction altered it + */ + int editingLastLines () const { return m_editingLastLines; } + + /** + * Query information from the last editing transaction: was the content of the buffer changed? + * This is checked by comparing the editingLastRevision() with the current revision(). + * @return content of buffer was changed in last transaction? + */ + bool editingChangedBuffer () const { return editingLastRevision() != revision(); } + + /** + * Query information from the last editing transaction: was the number of lines of the buffer changed? + * This is checked by comparing the editingLastLines() with the current lines(). + * @return content of buffer was changed in last transaction? + */ + bool editingChangedNumberOfLines () const { return editingLastLines() != lines(); } + + /** + * Get minimal line number changed by last editing transaction + * @return maximal line number changed by last editing transaction, or -1, if none changed + */ + int editingMinimalLineChanged () const { return m_editingMinimalLineChanged; } + + /** + * Get maximal line number changed by last editing transaction + * @return maximal line number changed by last editing transaction, or -1, if none changed + */ + int editingMaximalLineChanged () const { return m_editingMaximalLineChanged; } + + /** + * Wrap line at given cursor position. + * @param position line/column as cursor where to wrap + * Virtual, can be overwritten. + */ + virtual void wrapLine (const KTextEditor::Cursor &position); + + /** + * Unwrap given line. + * @param line line to unwrap + * Virtual, can be overwritten. + */ + virtual void unwrapLine (int line); + + /** + * Insert text at given cursor position. Does nothing if text is empty, beside some consistency checks. + * @param position position where to insert text + * @param text text to insert + * Virtual, can be overwritten. + */ + virtual void insertText (const KTextEditor::Cursor &position, const QString &text); + + /** + * Remove text at given range. Does nothing if range is empty, beside some consistency checks. + * @param range range of text to remove, must be on one line only. + * Virtual, can be overwritten. + */ + virtual void removeText (const KTextEditor::Range &range); + + /** + * TextHistory of this buffer + * @return text history for this buffer + */ + TextHistory &history () { return m_history; } + + Q_SIGNALS: + /** + * Buffer got cleared. This is emitted when constructor or load have called clear() internally, + * or when the user of the buffer has called clear() itself. + */ + void cleared (); + + /** + * Buffer loaded successfully a file + * @param filename file which was loaded + * @param encodingErrors were there problems occurred while decoding the file? + */ + void loaded (const QString &filename, bool encodingErrors); + + /** + * Buffer saved successfully a file + * @param filename file which was saved + */ + void saved (const QString &filename); + + /** + * Editing transaction has started. + */ + void editingStarted (); + + /** + * Editing transaction has finished. + */ + void editingFinished (); + + /** + * A line got wrapped. + * @param position position where the wrap occurred + */ + void lineWrapped (const KTextEditor::Cursor &position); + + /** + * A line got unwrapped. + * @param line line where the unwrap occurred + */ + void lineUnwrapped (int line); + + /** + * Text got inserted. + * @param position position where the insertion occurred + * @param text inserted text + */ + void textInserted (const KTextEditor::Cursor &position, const QString &text); + + /** + * Text got removed. + * @param range range where the removal occurred + * @param text removed text + */ + void textRemoved (const KTextEditor::Range &range, const QString &text); + + private: + /** + * Find block containing given line. + * @param line we want to find block for this line + * @return index of found block + */ + int blockForLine (int line) const; + + /** + * Fix start lines of all blocks after the given one + * @param startBlock index of block from which we start to fix + */ + void fixStartLines (int startBlock); + + /** + * Balance the given block. Look if it is too small or too large. + * @param index block to balance + */ + void balanceBlock (int index); + + /** + * Block for given index in block list. + * @param index block index + * @return block matching this index + */ + TextBlock *blockForIndex (int index) { return m_blocks[index]; } + + /** + * A range changed, notify the views, in case of attributes or feedback. + * @param view which view is affected? 0 for all views + * @param startLine start line of change + * @param endLine end line of change + * @param rangeWithAttribute attribute changed or is active, this will perhaps lead to repaints + */ + void notifyAboutRangeChange (KTextEditor::View *view, int startLine, int endLine, bool rangeWithAttribute); + + /** + * Mark all modified lines as lines saved on disk (modified line system). + */ + void markModifiedLinesAsSaved(); + + public: + /** + * Gets the document to which this buffer is bound. + * \return a pointer to the document + */ + KateDocument *document () const { return m_document; } + + /** + * Debug output, print whole buffer content with line numbers and line length + * @param title title for this output + */ + void debugPrint (const QString &title) const; + + /** + * Return the ranges which affect the given line. + * @param line line to look at + * @param view only return ranges associated with given view + * @param rangesWithAttributeOnly only return ranges which have a attribute set + * @return list of ranges affecting this line + */ + QList rangesForLine (int line, KTextEditor::View *view, bool rangesWithAttributeOnly) const; + + /** + * Check if the given range pointer is still valid. + * @return range pointer still belongs to range for this buffer + */ + bool rangePointerValid (TextRange *range) const { return m_ranges.contains (range); } + + /** + * Invalidate all ranges in this buffer. + */ + void invalidateRanges(); + + // + // sha1 checksum handling + // + public: + /** + * sha1 digest of the document on disk, set either through file loading + * in openFile() or in KateDocument::saveFile() + * @return sha1 digest for this document + */ + const QByteArray &digest () const; + + /** + * Set the sha1sum of this buffer. Make sure this checksum is up-to-date + * when reading digest(). + * @param sha1sum sha1 digest for the document on disk + */ + void setDigest (const QByteArray & sha1sum); + + private: + QByteArray m_digest; + + private: + /** + * parent document + */ + KateDocument *m_document; + + /** + * text history + */ + TextHistory m_history; + + /** + * block size in lines the buffer will try to hold + */ + const int m_blockSize; + + /** + * List of blocks which contain the lines of this buffer + */ + QVector m_blocks; + + /** + * Number of lines in buffer + */ + int m_lines; + + /** + * Last used block in the buffer. Is used for speeding up blockForLine. + * May contain invalid index, must be checked before using. + */ + mutable int m_lastUsedBlock; + + /** + * Revision of the buffer. + */ + qint64 m_revision; + + /** + * Current number of running edit transactions + */ + int m_editingTransactions; + + /** + * Revision remembered at start of current editing transaction + */ + qint64 m_editingLastRevision; + + /** + * Number of lines remembered at start of current editing transaction + */ + int m_editingLastLines; + + /** + * minimal line number changed by last editing transaction + */ + int m_editingMinimalLineChanged; + + /** + * maximal line number changed by last editing transaction + */ + int m_editingMaximalLineChanged; + + /** + * Set of invalid cursors for this whole buffer. + * Valid cursors are inside the block the belong to. + */ + QSet m_invalidCursors; + + /** + * Set of ranges of this whole buffer. + */ + QSet m_ranges; + + /** + * Fallback text codec to use + */ + QTextCodec *m_fallbackTextCodec; + + /** + * Text codec to use + */ + QTextCodec *m_textCodec; + + /** + * Should byte order mark be created? + */ + bool m_generateByteOrderMark; + + /** + * End of line mode, default is Unix + */ + EndOfLineMode m_endOfLineMode; + + /** + * Insert newline character at the end of the file? + */ + bool m_newLineAtEof; + + /** + * Limit for line length, longer lines will be wrapped on load + */ + int m_lineLengthLimit; +}; + +} + +#endif diff --git a/kate/part/buffer/katetextcursor.cpp b/kate/part/buffer/katetextcursor.cpp new file mode 100644 index 00000000..78f33be5 --- /dev/null +++ b/kate/part/buffer/katetextcursor.cpp @@ -0,0 +1,152 @@ +/* This file is part of the Kate project. + * + * Copyright (C) 2010 Christoph Cullmann + * + * Based on code of the SmartCursor/Range by: + * Copyright (C) 2003-2005 Hamish Rodda + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "katetextcursor.h" +#include "katetextbuffer.h" + +namespace Kate { + +TextCursor::TextCursor (TextBuffer &buffer, const KTextEditor::Cursor &position, InsertBehavior insertBehavior) + : m_buffer (buffer) + , m_range (0) + , m_block (0) + , m_line (-1) + , m_column (-1) + , m_moveOnInsert (insertBehavior == MoveOnInsert) +{ + // init position + setPosition (position, true); +} + +TextCursor::TextCursor (TextBuffer &buffer, TextRange *range, const KTextEditor::Cursor &position, InsertBehavior insertBehavior) + : m_buffer (buffer) + , m_range (range) + , m_block (0) + , m_line (-1) + , m_column (-1) + , m_moveOnInsert (insertBehavior == MoveOnInsert) +{ + // init position + setPosition (position, true); +} + +TextCursor::~TextCursor () +{ + // remove cursor from block or buffer + if (m_block) + m_block->removeCursor (this); + + // only cursors without range are here! + else if (!m_range) + m_buffer.m_invalidCursors.remove (this); +} + +void TextCursor::setPosition( const TextCursor& position ) +{ + if (m_block && m_block != position.m_block) + m_block->removeCursor (this); + + m_line = position.m_line; + m_column = position.m_column; + + m_block = position.m_block; + if (m_block) + m_block->insertCursor (this); +} + +void TextCursor::setPosition(const KTextEditor::Cursor& position, bool init) +{ + // any change or init? else do nothing + if (!init && position.line() == line() && position.column() == m_column) + return; + + // remove cursor from old block in any case + if (m_block) + m_block->removeCursor (this); + + // first: validate the line and column, else invalid + if (position.column() < 0 || position.line () < 0 || position.line () >= m_buffer.lines ()) { + if (!m_range) + m_buffer.m_invalidCursors.insert (this); + m_block = 0; + m_line = m_column = -1; + return; + } + + // else, find block + TextBlock *block = m_buffer.blockForIndex (m_buffer.blockForLine (position.line())); + Q_ASSERT(block); + + // get line + TextLine textLine = block->line (position.line()); + +#if 0 // this is no good idea, smart cursors don't do that, too, for non-wrapping cursors + // now, validate column, else stay invalid + if (position.column() > textLine->text().size()) { + if (!m_range) + m_buffer.m_invalidCursors.insert (this); + m_block = 0; + m_line = m_column = -1; + return; + } +#endif + + // if cursor was invalid before, remove it from invalid cursor list + if (!m_range && !m_block && !init) { + Q_ASSERT(m_buffer.m_invalidCursors.contains (this)); + m_buffer.m_invalidCursors.remove (this); + } + + // else: valid cursor + m_block = block; + m_line = position.line () - m_block->startLine (); + m_column = position.column (); + m_block->insertCursor (this); +} + +void TextCursor::setPosition(const KTextEditor::Cursor& position) +{ + setPosition(position, false); +} + +int TextCursor::line() const +{ + // invalid cursor have no block + if (!m_block) + return -1; + + // else, calculate real line + return m_block->startLine () + m_line; +} + +KTextEditor::Document *Kate::TextCursor::document () const +{ + return m_buffer.document(); +} + +KTextEditor::MovingRange *Kate::TextCursor::range () const +{ + return m_range; +} + +} diff --git a/kate/part/buffer/katetextcursor.h b/kate/part/buffer/katetextcursor.h new file mode 100644 index 00000000..47fdb7e2 --- /dev/null +++ b/kate/part/buffer/katetextcursor.h @@ -0,0 +1,231 @@ +/* This file is part of the Kate project. + * + * Copyright (C) 2010 Christoph Cullmann + * + * Based on code of the SmartCursor/Range by: + * Copyright (C) 2003-2005 Hamish Rodda + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef KATE_TEXTCURSOR_H +#define KATE_TEXTCURSOR_H + +#include + +#include "katepartinterfaces_export.h" +#include "katetextblock.h" + +namespace Kate { + +class TextBuffer; +class TextBlock; +class TextRange; + +/** + * Class representing a 'clever' text cursor. + * It will automagically move if the text inside the buffer it belongs to is modified. + * By intention no subclass of KTextEditor::Cursor, must be converted manually. + */ +class KATEPARTINTERFACES_EXPORT TextCursor : public KTextEditor::MovingCursor { + // range wants direct access to some internals + friend class TextRange; + + // this is a friend, because this is needed to efficiently transfer cursors from on to an other block + friend class TextBlock; + + private: + /** + * Construct a text cursor with given range as parent, private, used by TextRange constructor only. + * @param buffer text buffer this cursor belongs to + * @param range text range this cursor is part of + * @param position wanted cursor position, if not valid for given buffer, will lead to invalid cursor + * @param insertBehavior behavior of this cursor on insert of text at its position + */ + TextCursor (TextBuffer &buffer, TextRange *range, const KTextEditor::Cursor &position, InsertBehavior insertBehavior); + + public: + /** + * Construct a text cursor. + * @param buffer text buffer this cursor belongs to + * @param position wanted cursor position, if not valid for given buffer, will lead to invalid cursor + * @param insertBehavior behavior of this cursor on insert of text at its position + */ + TextCursor (TextBuffer &buffer, const KTextEditor::Cursor &position, InsertBehavior insertBehavior); + + /** + * Destruct the text cursor + */ + ~TextCursor (); + + /** + * Set insert behavior. + * @param insertBehavior new insert behavior + */ + void setInsertBehavior (InsertBehavior insertBehavior) { m_moveOnInsert = insertBehavior == MoveOnInsert; } + + /** + * Get current insert behavior. + * @return current insert behavior + */ + InsertBehavior insertBehavior () const { return m_moveOnInsert ? MoveOnInsert : StayOnInsert; } + + /** + * Gets the document to which this cursor is bound. + * \return a pointer to the document + */ + KTextEditor::Document *document () const; + + /** + * Fast way to set the current cursor position to \e position. + * + * \param position new cursor position + */ + void setPosition (const TextCursor& position); + + /** + * Set the current cursor position to \e position. + * + * \param position new cursor position + */ + void setPosition (const KTextEditor::Cursor& position); + + /** + * \overload + * + * Set the cursor position to \e line and \e column. + * + * \param line new cursor line + * \param column new cursor column + */ + void setPosition (int line, int column) { KTextEditor::MovingCursor::setPosition (line, column); } + + /** + * Retrieve the line on which this cursor is situated. + * \return line number, where 0 is the first line. + */ + int line() const; + + /** + * Non-virtual version of line(), which is faster. + * Inlined for fast access (especially in KateTextBuffer::rangesForLine + * \return line number, where 0 is the first line. + */ + int lineInternal() const + { + // invalid cursor have no block + if (!m_block) + return -1; + + // else, calculate real line + return m_block->startLine () + m_line; + } + + /** + * Retrieve the column on which this cursor is situated. + * \return column number, where 0 is the first column. + */ + int column() const { return m_column; } + + /** + * Non-virtual version of column(), which is faster. + * \return column number, where 0 is the first column. + * */ + int columnInternal() const { return m_column; } + + /** + * Get range this cursor belongs to, if any + * @return range this pointer is part of, else 0 + */ + KTextEditor::MovingRange *range () const; + + /** + * Get range this cursor belongs to, if any + * @return range this pointer is part of, else 0 + */ + Kate::TextRange *kateRange () const { return m_range; } + + /** + * Get block this cursor belongs to, if any + * @return block this pointer is part of, else 0 + */ + TextBlock *block () const { return m_block; } + + /** + * Get offset into block this cursor belongs to, if any + * @return offset into block this pointer is part of, else -1 + */ + int lineInBlock () const { if (m_block) return m_line; return -1; } + + private: + /** + * no copy constructor, don't allow this to be copied. + */ + TextCursor (const TextCursor &); + + /** + * no assignment operator, no copying around. + */ + TextCursor &operator= (const TextCursor &); + + /** + * Set the current cursor position to \e position. + * Internal helper to allow the same code be used for constructor and + * setPosition. + * + * @param position new cursor position + * @param init is this the initial setup of the position in the constructor? + */ + void setPosition (const KTextEditor::Cursor& position, bool init); + + private: + /** + * parent text buffer + * is a reference, and no pointer, as this must always exist and can't change + */ + TextBuffer &m_buffer; + + /** + * range this cursor belongs to + * may be null, then no range owns this cursor + * can not change after initial assignment + */ + TextRange * const m_range; + + /** + * parent text block, valid cursors always belong to a block, else they are invalid. + */ + TextBlock *m_block; + + /** + * line, offset in block, or -1 + */ + int m_line; + + /** + * column + */ + int m_column; + + /** + * should this cursor move on insert + */ + bool m_moveOnInsert; +}; + +} + +#endif diff --git a/kate/part/buffer/katetextfolding.cpp b/kate/part/buffer/katetextfolding.cpp new file mode 100644 index 00000000..a47c3823 --- /dev/null +++ b/kate/part/buffer/katetextfolding.cpp @@ -0,0 +1,939 @@ +/* This file is part of the Kate project. + * + * Copyright (C) 2013 Christoph Cullmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "katetextfolding.h" +#include "katetextbuffer.h" +#include "katetextrange.h" + +namespace Kate { + +TextFolding::FoldingRange::FoldingRange (TextBuffer &buffer, const KTextEditor::Range &range, FoldingRangeFlags _flags) + : start (new TextCursor (buffer, range.start(), KTextEditor::MovingCursor::MoveOnInsert)) + , end (new TextCursor (buffer, range.end(), KTextEditor::MovingCursor::MoveOnInsert)) + , parent (0) + , flags (_flags) + , id (-1) +{ +} + +TextFolding::FoldingRange::~FoldingRange () +{ + /** + * kill all our data! + * this will recurse all sub-structures! + */ + delete start; + delete end; + qDeleteAll (nestedRanges); +} + +TextFolding::TextFolding (TextBuffer &buffer) + : QObject () + , m_buffer (buffer) + , m_idCounter (-1) +{ + /** + * connect needed signals from buffer + */ + connect (&m_buffer, SIGNAL(cleared()), SLOT(clear())); +} + +TextFolding::~TextFolding () +{ + /** + * only delete the folding ranges, the folded ranges and mapped ranges are the same objects + */ + qDeleteAll (m_foldingRanges); +} + +void TextFolding::clear () +{ + /** + * reset counter + */ + m_idCounter = -1; + + /** + * no ranges, no work + */ + if (m_foldingRanges.isEmpty()) { + /** + * assert all stuff is consistent and return! + */ + Q_ASSERT (m_idToFoldingRange.isEmpty()); + Q_ASSERT (m_foldedFoldingRanges.isEmpty()); + return; + } + + /** + * cleanup + */ + m_idToFoldingRange.clear(); + m_foldedFoldingRanges.clear(); + qDeleteAll (m_foldingRanges); + m_foldingRanges.clear (); + + /** + * folding changed! + */ + emit foldingRangesChanged (); +} + +qint64 TextFolding::newFoldingRange (const KTextEditor::Range &range, FoldingRangeFlags flags) +{ + /** + * sort out invalid and empty ranges + * that makes no sense, they will never grow again! + */ + if (!range.isValid() || range.isEmpty()) + return -1; + + /** + * create new folding region that we want to insert + * this will internally create moving cursors! + */ + FoldingRange *newRange = new FoldingRange (m_buffer, range, flags); + + /** + * the construction of the text cursors might have invalidated this + * check and bail out if that happens + * bail out, too, if it can't be inserted! + */ + if ( !newRange->start->isValid() + || !newRange->end->isValid() + || !insertNewFoldingRange (0 /* no parent here */, m_foldingRanges, newRange)) { + /** + * cleanup and be done + */ + delete newRange; + return -1; + } + + /** + * set id, catch overflows, even if they shall not happen + */ + newRange->id = ++m_idCounter; + if (newRange->id < 0) + newRange->id = m_idCounter = 0; + + /** + * remember the range + */ + m_idToFoldingRange.insert (newRange->id, newRange); + + /** + * update our folded ranges vector! + */ + bool updated = updateFoldedRangesForNewRange (newRange); + + /** + * emit that something may have changed + * do that only, if updateFoldedRangesForNewRange did not already do the job! + */ + if (!updated) + emit foldingRangesChanged (); + + /** + * all went fine, newRange is now registered internally! + */ + return newRange->id; +} + +KTextEditor::Range TextFolding::foldingRange(qint64 id) const +{ + FoldingRange* range = m_idToFoldingRange.value (id); + if (!range) + return KTextEditor::Range::invalid(); + + return KTextEditor::Range(range->start->toCursor(), range->end->toCursor()); +} + +bool TextFolding::foldRange (qint64 id) +{ + /** + * try to find the range, else bail out + */ + FoldingRange *range = m_idToFoldingRange.value (id); + if (!range) + return false; + + /** + * already folded? nothing to do + */ + if (range->flags & Folded) + return true; + + /** + * fold and be done + */ + range->flags |= Folded; + updateFoldedRangesForNewRange (range); + return true; +} + +bool TextFolding::unfoldRange (qint64 id, bool remove) +{ + /** + * try to find the range, else bail out + */ + FoldingRange *range = m_idToFoldingRange.value (id); + if (!range) + return false; + + /** + * nothing to do? + * range is already unfolded and we need not to remove it! + */ + if (!remove && !(range->flags & Folded)) + return true; + + /** + * do we need to delete the range? + */ + const bool deleteRange = remove || !(range->flags & Persistent); + + /** + * first: remove the range, if forced or non-persistent! + */ + if (deleteRange) { + /** + * remove from outside visible mapping! + */ + m_idToFoldingRange.remove (id); + + /** + * remove from folding vectors! + * FIXME: OPTIMIZE + */ + FoldingRange::Vector &parentVector = range->parent ? range->parent->nestedRanges : m_foldingRanges; + FoldingRange::Vector newParentVector; + Q_FOREACH (FoldingRange *curRange, parentVector) { + /** + * insert our nested ranges and reparent them + */ + if (curRange == range) { + Q_FOREACH (FoldingRange *newRange, range->nestedRanges) { + newRange->parent = range->parent; + newParentVector.push_back (newRange); + } + + continue; + } + + /** + * else just transfer elements + */ + newParentVector.push_back (curRange); + } + parentVector = newParentVector; + } + + /** + * second: unfold the range, if needed! + */ + bool updated = false; + if (range->flags & Folded) { + range->flags &= ~Folded; + updated = updateFoldedRangesForRemovedRange (range); + } + + /** + * emit that something may have changed + * do that only, if updateFoldedRangesForRemoveRange did not already do the job! + */ + if (!updated) + emit foldingRangesChanged (); + + /** + * really delete the range, if needed! + */ + if (deleteRange) { + /** + * clear ranges first, they got moved! + */ + range->nestedRanges.clear (); + delete range; + } + + /** + * be done ;) + */ + return true; +} + +bool TextFolding::isLineVisible (int line, qint64 *foldedRangeId) const +{ + /** + * skip if nothing folded + */ + if (m_foldedFoldingRanges.isEmpty()) + return true; + + /** + * search upper bound, index to item with start line higher than our one + */ + FoldingRange::Vector::const_iterator upperBound = qUpperBound (m_foldedFoldingRanges.begin(), m_foldedFoldingRanges.end(), line, compareRangeByStartWithLine); + if (upperBound != m_foldedFoldingRanges.begin()) + --upperBound; + + /** + * check if we overlap with the range in front of us + */ + const bool hidden = (((*upperBound)->end->line() >= line) && (line > (*upperBound)->start->line())); + + /** + * fill in folded range id, if needed + */ + if (foldedRangeId) + (*foldedRangeId) = hidden ? (*upperBound)->id : -1; + + /** + * visible == !hidden + */ + return !hidden; +} + +void TextFolding::ensureLineIsVisible (int line) +{ + /** + * skip if nothing folded + */ + if (m_foldedFoldingRanges.isEmpty()) + return; + + /** + * while not visible, unfold + */ + qint64 foldedRangeId = -1; + while (!isLineVisible (line, &foldedRangeId)) { + /** + * id should be valid! + */ + Q_ASSERT (foldedRangeId >= 0); + + /** + * unfold shall work! + */ + const bool unfolded = unfoldRange (foldedRangeId); + (void) unfolded; + Q_ASSERT (unfolded); + } +} + +int TextFolding::visibleLines () const +{ + /** + * start with all lines we have + */ + int visibleLines = m_buffer.lines(); + + /** + * skip if nothing folded + */ + if (m_foldedFoldingRanges.isEmpty()) + return visibleLines; + + /** + * count all folded lines and subtract them from visible lines + */ + Q_FOREACH (FoldingRange *range, m_foldedFoldingRanges) + visibleLines -= (range->end->line() - range->start->line()); + + /** + * be done, assert we did no trash + */ + Q_ASSERT (visibleLines > 0); + return visibleLines; +} + +int TextFolding::lineToVisibleLine (int line) const +{ + /** + * valid input needed! + */ + Q_ASSERT (line >= 0); + + /** + * start with identity + */ + int visibleLine = line; + + /** + * skip if nothing folded or first line + */ + if (m_foldedFoldingRanges.isEmpty() || (line == 0)) + return visibleLine; + + /** + * walk over all folded ranges until we reach the line + * keep track of seen visible lines, for the case we want to convert a hidden line! + */ + int seenVisibleLines = 0; + int lastLine = 0; + Q_FOREACH (FoldingRange *range, m_foldedFoldingRanges) { + /** + * abort if we reach our line! + */ + if (range->start->line() >= line) + break; + + /** + * count visible lines + */ + seenVisibleLines += (range->start->line() - lastLine); + lastLine = range->end->line(); + + /** + * we might be contained in the region, then we return last visible line + */ + if (line <= range->end->line()) + return seenVisibleLines; + + /** + * subtrace folded lines + */ + visibleLine -= (range->end->line() - range->start->line()); + } + + /** + * be done, assert we did no trash + */ + Q_ASSERT (visibleLine >= 0); + return visibleLine; +} + +int TextFolding::visibleLineToLine (int visibleLine) const +{ + /** + * valid input needed! + */ + Q_ASSERT (visibleLine >= 0); + + /** + * start with identity + */ + int line = visibleLine; + + /** + * skip if nothing folded or first line + */ + if (m_foldedFoldingRanges.isEmpty() || (visibleLine == 0)) + return line; + + /** + * last visible line seen, as line in buffer + */ + int seenVisibleLines = 0; + int lastLine = 0; + int lastLineVisibleLines = 0; + Q_FOREACH (FoldingRange *range, m_foldedFoldingRanges) { + /** + * else compute visible lines and move last seen + */ + lastLineVisibleLines = seenVisibleLines; + seenVisibleLines += (range->start->line() - lastLine); + + /** + * bail out if enough seen + */ + if (seenVisibleLines >= visibleLine) + break; + + lastLine = range->end->line(); + } + + /** + * check if still no enough visible! + */ + if (seenVisibleLines < visibleLine) + lastLineVisibleLines = seenVisibleLines; + + /** + * compute visible line + */ + line = (lastLine + (visibleLine - lastLineVisibleLines)); + Q_ASSERT (line >= 0); + return line; +} + +QVector > TextFolding::foldingRangesStartingOnLine (int line) const +{ + /** + * results vector + */ + QVector > results; + + /** + * recursively do binary search + */ + foldingRangesStartingOnLine (results, m_foldingRanges, line); + + /** + * return found results + */ + return results; +} + +void TextFolding::foldingRangesStartingOnLine (QVector > &results, const TextFolding::FoldingRange::Vector &ranges, int line) const +{ + /** + * early out for no folds + */ + if (ranges.isEmpty()) + return; + + /** + * first: lower bound of start + */ + FoldingRange::Vector::const_iterator lowerBound = qLowerBound (ranges.begin(), ranges.end(), line, compareRangeByLineWithStart); + + /** + * second: upper bound of end + */ + FoldingRange::Vector::const_iterator upperBound = qUpperBound (ranges.begin(), ranges.end(), line, compareRangeByStartWithLine); + + /** + * we may need to go one to the left, if not already at the begin, as we might overlap with the one in front of us! + */ + if ((lowerBound != ranges.begin()) && ((*(lowerBound-1))->end->line() >= line)) + --lowerBound; + + /** + * for all of them, check if we start at right line and recurse + */ + for (FoldingRange::Vector::const_iterator it = lowerBound; it != upperBound; ++it) { + /** + * this range already ok? add it to results + */ + if ((*it)->start->line() == line) + results.push_back (qMakePair((*it)->id, (*it)->flags)); + + /** + * recurse anyway + */ + foldingRangesStartingOnLine (results, (*it)->nestedRanges, line); + } +} + +QString TextFolding::debugDump () const +{ + /** + * dump toplevel ranges recursively + */ + return QString ("tree %1 - folded %2").arg (debugDump (m_foldingRanges, true)).arg(debugDump (m_foldedFoldingRanges, false)); +} + +void TextFolding::debugPrint (const QString &title) const +{ + // print title + content + printf ("%s\n %s\n", qPrintable (title), qPrintable(debugDump())); +} + +QString TextFolding::debugDump (const TextFolding::FoldingRange::Vector &ranges, bool recurse) +{ + /** + * dump all ranges recursively + */ + QString dump; + Q_FOREACH (FoldingRange *range, ranges) { + if (!dump.isEmpty()) + dump += " "; + + dump += QString ("[%1:%2 %3%4 ").arg (range->start->line()).arg(range->start->column()).arg((range->flags & Persistent) ? "p" : "").arg((range->flags & Folded) ? "f" : ""); + + /** + * recurse + */ + if (recurse) { + QString inner = debugDump (range->nestedRanges, recurse); + if (!inner.isEmpty()) + dump += inner + " "; + } + + dump += QString ("%1:%2]").arg (range->end->line()).arg(range->end->column()); + } + return dump; +} + +bool TextFolding::insertNewFoldingRange (FoldingRange *parent, FoldingRange::Vector &existingRanges, FoldingRange *newRange) +{ + /** + * existing ranges are non-overlapping and sorted + * that means, we can search for lower bound of start of range and upper bound of end of range to find all "overlapping" ranges. + */ + + /** + * first: lower bound of start + */ + FoldingRange::Vector::iterator lowerBound = qLowerBound (existingRanges.begin(), existingRanges.end(), newRange, compareRangeByStart); + + /** + * second: upper bound of end + */ + FoldingRange::Vector::iterator upperBound = qUpperBound (existingRanges.begin(), existingRanges.end(), newRange, compareRangeByEnd); + + /** + * we may need to go one to the left, if not already at the begin, as we might overlap with the one in front of us! + */ + if ((lowerBound != existingRanges.begin()) && ((*(lowerBound-1))->end->toCursor() > newRange->start->toCursor())) + --lowerBound; + + /** + * now: first case, we overlap with nothing or hit exactly one range! + */ + if (lowerBound == upperBound) { + /** + * nothing we overlap with? + * then just insert and be done! + */ + if ((lowerBound == existingRanges.end()) || (newRange->start->toCursor() >= (*lowerBound)->end->toCursor()) || (newRange->end->toCursor() <= (*lowerBound)->start->toCursor())) { + /** + * insert + fix parent + */ + existingRanges.insert (lowerBound, newRange); + newRange->parent = parent; + + /** + * all done + */ + return true; + } + + /** + * we are contained in this one range? + * then recurse! + */ + if ((newRange->start->toCursor() >= (*lowerBound)->start->toCursor()) && (newRange->end->toCursor() <= (*lowerBound)->end->toCursor())) + return insertNewFoldingRange ((*lowerBound), (*lowerBound)->nestedRanges, newRange); + + /** + * else: we might contain at least this fold, or many more, if this if block is not taken at all + * use the general code that checks for "we contain stuff" below! + */ + } + + /** + * check if we contain other folds! + */ + FoldingRange::Vector::iterator it = lowerBound; + bool includeUpperBound = false; + FoldingRange::Vector nestedRanges; + while (it != existingRanges.end()) { + /** + * do we need to take look at upper bound, too? + * if not break + */ + if (it == upperBound) { + if (newRange->end->toCursor() <= (*upperBound)->start->toCursor()) + break; + else + includeUpperBound = true; + } + + /** + * if one region is not contained in the new one, abort! + * then this is not well nested! + */ + if (!((newRange->start->toCursor() <= (*it)->start->toCursor()) && (newRange->end->toCursor() >= (*it)->end->toCursor()))) + return false; + + /** + * include into new nested ranges + */ + nestedRanges.push_back ((*it)); + + /** + * end reached + */ + if (it == upperBound) + break; + + /** + * else increment + */ + ++it; + } + + /** + * if we arrive here, all is nicely nested into our new range + * remove the contained ones here, insert new range with new nested ranges we already constructed + */ + it = existingRanges.erase (lowerBound, includeUpperBound ? (upperBound+1) : upperBound); + existingRanges.insert (it, newRange); + newRange->nestedRanges = nestedRanges; + + /** + * correct parent mapping! + */ + newRange->parent = parent; + Q_FOREACH (FoldingRange *range, newRange->nestedRanges) + range->parent = newRange; + + /** + * all nice + */ + return true; +} + +bool TextFolding::compareRangeByStart (FoldingRange *a, FoldingRange *b) +{ + return a->start->toCursor() < b->start->toCursor(); +} + +bool TextFolding::compareRangeByEnd (FoldingRange *a, FoldingRange *b) +{ + return a->end->toCursor() < b->end->toCursor(); +} + +bool TextFolding::compareRangeByStartWithLine (int line, FoldingRange *range) +{ + return (line < range->start->line()); +} + +bool TextFolding::compareRangeByLineWithStart (FoldingRange *range, int line) +{ + return (range->start->line() < line); +} + +bool TextFolding::updateFoldedRangesForNewRange (TextFolding::FoldingRange *newRange) +{ + /** + * not folded? not interesting! we don't need to touch our m_foldedFoldingRanges vector + */ + if (!(newRange->flags & Folded)) + return false; + + /** + * any of the parents folded? not interesting, too! + */ + TextFolding::FoldingRange *parent = newRange->parent; + while (parent) { + /** + * parent folded => be done + */ + if (parent->flags & Folded) + return false; + + /** + * walk up + */ + parent = parent->parent; + } + + /** + * ok, if we arrive here, we are a folded range and we have no folded parent + * we now want to add this range to the m_foldedFoldingRanges vector, just removing any ranges that is included in it! + * TODO: OPTIMIZE + */ + FoldingRange::Vector newFoldedFoldingRanges; + bool newRangeInserted = false; + Q_FOREACH (FoldingRange *range, m_foldedFoldingRanges) { + /** + * contained? kill + */ + if ((newRange->start->toCursor() <= range->start->toCursor()) && (newRange->end->toCursor() >= range->end->toCursor())) + continue; + + /** + * range is behind newRange? + * insert newRange if not already done + */ + if (!newRangeInserted && (range->start->toCursor() >= newRange->end->toCursor())) { + newFoldedFoldingRanges.push_back (newRange); + newRangeInserted = true; + } + + /** + * just transfer range + */ + newFoldedFoldingRanges.push_back (range); + } + + /** + * last: insert new range, if not done + */ + if (!newRangeInserted) + newFoldedFoldingRanges.push_back (newRange); + + /** + * fixup folded ranges + */ + m_foldedFoldingRanges = newFoldedFoldingRanges; + + /** + * folding changed! + */ + emit foldingRangesChanged (); + + /** + * all fine, stuff done, signal emitted + */ + return true; +} + +bool TextFolding::updateFoldedRangesForRemovedRange (TextFolding::FoldingRange *oldRange) +{ + /** + * folded? not interesting! we don't need to touch our m_foldedFoldingRanges vector + */ + if (oldRange->flags & Folded) + return false; + + /** + * any of the parents folded? not interesting, too! + */ + TextFolding::FoldingRange *parent = oldRange->parent; + while (parent) { + /** + * parent folded => be done + */ + if (parent->flags & Folded) + return false; + + /** + * walk up + */ + parent = parent->parent; + } + + /** + * ok, if we arrive here, we are a unfolded range and we have no folded parent + * we now want to remove this range from the m_foldedFoldingRanges vector and include our nested folded ranges! + * TODO: OPTIMIZE + */ + FoldingRange::Vector newFoldedFoldingRanges; + Q_FOREACH (FoldingRange *range, m_foldedFoldingRanges) { + /** + * right range? insert folded nested ranges + */ + if (range == oldRange) { + appendFoldedRanges (newFoldedFoldingRanges, oldRange->nestedRanges); + continue; + } + + /** + * just transfer range + */ + newFoldedFoldingRanges.push_back (range); + } + + /** + * fixup folded ranges + */ + m_foldedFoldingRanges = newFoldedFoldingRanges; + + /** + * folding changed! + */ + emit foldingRangesChanged (); + + /** + * all fine, stuff done, signal emitted + */ + return true; +} + +void TextFolding::appendFoldedRanges (TextFolding::FoldingRange::Vector &newFoldedFoldingRanges, const TextFolding::FoldingRange::Vector &ranges) const +{ + /** + * search for folded ranges and append them + */ + Q_FOREACH (FoldingRange *range, ranges) { + /** + * itself folded? append + */ + if (range->flags & Folded) { + newFoldedFoldingRanges.push_back (range); + continue; + } + + /** + * else: recurse! + */ + appendFoldedRanges (newFoldedFoldingRanges, range->nestedRanges); + } +} + +QVariantList TextFolding::exportFoldingRanges () const +{ + QVariantList folds; + exportFoldingRanges (m_foldingRanges, folds); + return folds; +} + +void TextFolding::exportFoldingRanges (const TextFolding::FoldingRange::Vector &ranges, QVariantList &folds) +{ + /** + * dump all ranges recursively + */ + Q_FOREACH (FoldingRange *range, ranges) { + /** + * construct one range and dump to folds + */ + QVariantMap rangeMap; + rangeMap["startLine"] = range->start->line(); + rangeMap["startColumn"] = range->start->column(); + rangeMap["endLine"] = range->end->line(); + rangeMap["endColumn"] = range->end->column(); + rangeMap["flags"] = (int)range->flags; + folds.append (rangeMap); + + /** + * recurse + */ + exportFoldingRanges (range->nestedRanges, folds); + } +} + +void TextFolding::importFoldingRanges (const QVariantList &folds) +{ + /** + * try to create all folding ranges + */ + Q_FOREACH (QVariant rangeVariant, folds) { + /** + * get map + */ + QVariantMap rangeMap = rangeVariant.toMap (); + + /** + * construct range start/end + */ + KTextEditor::Cursor start (rangeMap["startLine"].toInt(), rangeMap["startColumn"].toInt()); + KTextEditor::Cursor end (rangeMap["endLine"].toInt(), rangeMap["endColumn"].toInt()); + + /** + * get flags + */ + int rawFlags = rangeMap["flags"].toInt(); + FoldingRangeFlags flags; + if (rawFlags & Persistent) + flags = Persistent; + if (rawFlags & Folded) + flags = Folded; + + /** + * create folding range + */ + newFoldingRange (KTextEditor::Range (start, end), flags); + } +} + +} diff --git a/kate/part/buffer/katetextfolding.h b/kate/part/buffer/katetextfolding.h new file mode 100644 index 00000000..9bd37222 --- /dev/null +++ b/kate/part/buffer/katetextfolding.h @@ -0,0 +1,377 @@ +/* This file is part of the Kate project. + * + * Copyright (C) 2013 Christoph Cullmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef KATE_TEXTFOLDING_H +#define KATE_TEXTFOLDING_H + +#include "katepartinterfaces_export.h" + +#include "ktexteditor/range.h" + +#include +#include + +namespace Kate { + +class TextBuffer; +class TextCursor; + +/** + * Class representing the folding information for a TextBuffer. + * The interface allows to arbitrary fold given regions of a buffer as long + * as they are well nested. + * Multiple instances of this class can exist for the same buffer. + */ +class KATEPARTINTERFACES_EXPORT TextFolding : public QObject { + Q_OBJECT + + public: + /** + * Create folding object for given buffer. + * @param buffer text buffer we want to provide folding info for + */ + TextFolding (TextBuffer &buffer); + + /** + * Cleanup + */ + ~TextFolding (); + + /** + * Folding state of a range + */ + enum FoldingRangeFlag { + /** + * Range is persistent, e.g. it should not auto-delete after unfolding! + */ + Persistent = 0x1, + + /** + * Range is folded away + */ + Folded = 0x2 + }; + Q_DECLARE_FLAGS(FoldingRangeFlags, FoldingRangeFlag) + + /** + * Create a new folding range. + * @param range folding range + * @param flags initial flags for the new folding range + * @return on success, id of new range >= 0, else -1, we return no pointer as folding ranges might be auto-deleted internally! + * the ids are stable for one Kate::TextFolding, e.g. you can rely in unit tests that you get 0,1,.... for successfully created ranges! + */ + qint64 newFoldingRange (const KTextEditor::Range &range, FoldingRangeFlags flags = FoldingRangeFlags()); + + /** + * Returns the folding range associated with @p id. + * If @p id is not a valid id, the returned range matches KTextEditor::Range::invalid(). + * @note This works for either persistend ranges or folded ranges. + * Note, that the highlighting does not add folds unless text is folded. + * + * @return the folding range for @p id + */ + KTextEditor::Range foldingRange(qint64 id) const; + + /** + * Fold the given range. + * @param id id of the range to fold + * @return success + */ + bool foldRange (qint64 id); + + /** + * Unfold the given range. + * In addition it can be forced to remove the region, even if it is persistent. + * @param id id of the range to unfold + * @param remove should the range be removed from the folding after unfolding? ranges that are not persistent auto-remove themself on unfolding + * @return success + */ + bool unfoldRange (qint64 id, bool remove = false); + + /** + * Query if a given line is visible. + * Very fast, if nothing is folded, else does binary search + * log(n) for n == number of folded ranges + * @param line line to query + * @param foldedRangeId if the line is not visible and that pointer is not 0, will be filled with id of range hiding the line or -1 + * @return is that line visible? + */ + bool isLineVisible (int line, qint64 *foldedRangeId = 0) const; + + /** + * Ensure that a given line will be visible. + * Potentially unfold recursively all folds hiding this line, else just returns. + * @param line line to make visible + */ + void ensureLineIsVisible (int line); + + /** + * Query number of visible lines. + * Very fast, if nothing is folded, else walks over all folded regions + * O(n) for n == number of folded ranges + */ + int visibleLines () const; + + /** + * Convert a text buffer line to a visible line number. + * Very fast, if nothing is folded, else walks over all folded regions + * O(n) for n == number of folded ranges + * @param line line index in the text buffer + * @return index in visible lines + */ + int lineToVisibleLine (int line) const; + + /** + * Convert a visible line number to a line number in the text buffer. + * Very fast, if nothing is folded, else walks over all folded regions + * O(n) for n == number of folded ranges + * @param visibleLine visible line index + * @return index in text buffer lines + */ + int visibleLineToLine (int visibleLine) const; + + /** + * Queries which folding ranges start at the given line and returns the id + flags for all + * of them. Very fast if nothing is folded, else binary search. + * @param line line to query starting folding ranges + * @return vector of id's + flags + */ + QVector > foldingRangesStartingOnLine (int line) const; + + /** + * Return the current known folding ranges a QVariantList to store in configs. + * @return current folds as variant list + */ + QVariantList exportFoldingRanges () const; + + /** + * Import the folding ranges given as a QVariantList like read from configs. + * @param folds list of folds to import + */ + void importFoldingRanges (const QVariantList &folds); + + /** + * Dump folding state as string, for unit testing and debugging + * @return current state as text + */ + QString debugDump () const; + + /** + * Print state to stdout for testing + */ + void debugPrint (const QString &title) const; + + public Q_SLOTS: + /** + * Clear the complete folding. + * This is automatically triggered if the buffer is cleared. + */ + void clear (); + + Q_SIGNALS: + /** + * If the folding state of existing ranges changes or + * ranges are added/removed, this signal is emitted. + */ + void foldingRangesChanged (); + + private: + /** + * Data holder for text folding range and its nested children + */ + class FoldingRange { + public: + /** + * Construct new one + * @param buffer text buffer to use + * @param range folding range + * @param flags flags for the new folding range + */ + FoldingRange (TextBuffer &buffer, const KTextEditor::Range &range, FoldingRangeFlags flags); + + /** + * Cleanup + */ + ~FoldingRange (); + + /** + * Vector of range pointers + */ + typedef QVector Vector; + + /** + * start moving cursor + * NO range to be more efficient + */ + Kate::TextCursor *start; + + /** + * end moving cursor + * NO range to be more efficient + */ + Kate::TextCursor *end; + + /** + * parent range, if any + */ + FoldingRange *parent; + + /** + * nested ranges, if any + * this will always be sorted and non-overlapping + * nested ranges are inside these ranges + */ + FoldingRange::Vector nestedRanges; + + /** + * Folding range flags + */ + FoldingRangeFlags flags; + + /** + * id of this range + */ + qint64 id; + }; + + /** + * Fill known folding ranges in a QVariantList to store in configs. + * @param ranges ranges vector to dump + * @param folds current folds as variant list, will be filled + */ + static void exportFoldingRanges (const TextFolding::FoldingRange::Vector &ranges, QVariantList &folds); + + /** + * Dump folding state of given vector as string, for unit testing and debugging. + * Will recurse if wanted. + * @param ranges ranges vector to dump + * @param recurse recurse to nestedRanges? + * @return current state as text + */ + static QString debugDump (const TextFolding::FoldingRange::Vector &ranges, bool recurse); + + /** + * Helper to insert folding range into existing ones. + * Might fail, if not correctly nested. + * Then the outside must take care of the passed pointer, e.g. delete it. + * Will sanitize the ranges vectors, purge invalid/empty ranges. + * @param parent parent folding range if any + * @param existingRanges ranges into which we want to insert the new one + * @param newRange new folding range + * @return success, if false, newRange should be deleted afterwards, else it is registered internally + */ + bool insertNewFoldingRange (FoldingRange *parent, TextFolding::FoldingRange::Vector &existingRanges, TextFolding::FoldingRange *newRange); + + /** + * Helper to update the folded ranges if we insert a new range into the tree. + * @param newRange new folding range that was inserted, will already contain its new nested ranges, if any! + * @return any updated done? if yes, the foldingRangesChanged() signal got emitted! + */ + bool updateFoldedRangesForNewRange (TextFolding::FoldingRange *newRange); + + /** + * Helper to update the folded ranges if we remove a new range from the tree. + * @param oldRange new folding range that is removed, will still contain its new nested ranges, if any! + * @return any updated done? if yes, the foldingRangesChanged() signal got emitted! + */ + bool updateFoldedRangesForRemovedRange (TextFolding::FoldingRange *oldRange); + + /** + * Helper to append recursively topmost folded ranges from input to output vector. + * @param newFoldedFoldingRanges output vector for folded ranges + * @param ranges input vector to search recursively folded ranges inside + */ + void appendFoldedRanges (TextFolding::FoldingRange::Vector &newFoldedFoldingRanges, const TextFolding::FoldingRange::Vector &ranges) const; + + /** + * Compare two ranges by their start cursor. + * @param a first range + * @param b second range + */ + static bool compareRangeByStart (FoldingRange *a, FoldingRange *b); + + /** + * Compare two ranges by their end cursor. + * @param a first range + * @param b second range + */ + static bool compareRangeByEnd (FoldingRange *a, FoldingRange *b); + + /** + * Compare range start with line + * @param line line + * @param range range + */ + static bool compareRangeByStartWithLine (int line, FoldingRange *range); + + /** + * Compare range start with line + * @param range range + * @param line line + */ + static bool compareRangeByLineWithStart (FoldingRange *range, int line); + + /** + * Internal helper that queries which folding ranges start at the given line and returns the id + flags for all + * of them. Will recursively dive down starting with given vector + * @param results vector that is filled with id's + flags + * @param ranges ranges vector to search in + * @param line line to query starting folding ranges + */ + void foldingRangesStartingOnLine (QVector > &results, const TextFolding::FoldingRange::Vector &ranges, int line) const; + + private: + /** + * parent text buffer + * is a reference, and no pointer, as this must always exist and can't change + * can't be const, as we create text cursors! + */ + TextBuffer &m_buffer; + + /** + * toplevel folding ranges + * this will always be sorted and non-overlapping + * nested ranges are inside these ranges + */ + FoldingRange::Vector m_foldingRanges; + + /** + * folded folding ranges + * this is a sorted vector of ranges + * all non-overlapping + */ + FoldingRange::Vector m_foldedFoldingRanges; + + /** + * global id counter for the created ranges + */ + qint64 m_idCounter; + + /** + * mapping: id => range + */ + QHash m_idToFoldingRange; +}; + +} + +Q_DECLARE_OPERATORS_FOR_FLAGS(Kate::TextFolding::FoldingRangeFlags) + +#endif diff --git a/kate/part/buffer/katetexthistory.cpp b/kate/part/buffer/katetexthistory.cpp new file mode 100644 index 00000000..5ebcbdc3 --- /dev/null +++ b/kate/part/buffer/katetexthistory.cpp @@ -0,0 +1,587 @@ +/* This file is part of the Kate project. + * + * Copyright (C) 2010 Christoph Cullmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "katetexthistory.h" +#include "katetextbuffer.h" + +namespace Kate { + +TextHistory::TextHistory (TextBuffer &buffer) + : m_buffer (buffer) + , m_lastSavedRevision (-1) + , m_firstHistoryEntryRevision (0) +{ + // just call clear to init + clear (); +} + +TextHistory::~TextHistory () +{ +} + +qint64 TextHistory::revision () const +{ + // just output last revisions of buffer + return m_buffer.revision (); +} + +void TextHistory::clear () +{ + // reset last saved revision + m_lastSavedRevision = -1; + + // remove all history entries and add no-change dummy for first revision + m_historyEntries.clear (); + m_historyEntries.push_back (Entry ()); + + // first entry will again belong to first revision + m_firstHistoryEntryRevision = 0; +} + +void TextHistory::setLastSavedRevision () +{ + // current revision was successful saved + m_lastSavedRevision = revision (); +} + +void TextHistory::wrapLine (const KTextEditor::Cursor &position) +{ + // create and add new entry + Entry entry; + entry.type = Entry::WrapLine; + entry.line = position.line (); + entry.column = position.column (); + addEntry (entry); +} + +void TextHistory::unwrapLine (int line, int oldLineLength) +{ + // create and add new entry + Entry entry; + entry.type = Entry::UnwrapLine; + entry.line = line; + entry.column = 0; + entry.oldLineLength = oldLineLength; + addEntry (entry); +} + +void TextHistory::insertText (const KTextEditor::Cursor &position, int length, int oldLineLength) +{ + // create and add new entry + Entry entry; + entry.type = Entry::InsertText; + entry.line = position.line (); + entry.column = position.column (); + entry.length = length; + entry.oldLineLength = oldLineLength; + addEntry (entry); +} + +void TextHistory::removeText (const KTextEditor::Range &range, int oldLineLength) +{ + // create and add new entry + Entry entry; + entry.type = Entry::RemoveText; + entry.line = range.start().line (); + entry.column = range.start().column (); + entry.length = range.end().column() - range.start().column(); + entry.oldLineLength = oldLineLength; + addEntry (entry); +} + +void TextHistory::addEntry (const Entry &entry) +{ + /** + * history should never be empty + */ + Q_ASSERT (!m_historyEntries.empty ()); + + /** + * simple efficient check: if we only have one entry, and the entry is not referenced + * just replace it with the new one and adjust the revision + */ + if ((m_historyEntries.size () == 1) && !m_historyEntries.first().referenceCounter) { + /** + * remember new revision for first element, it is the revision we get after this change + */ + m_firstHistoryEntryRevision = revision () + 1; + + /** + * remember edit + */ + m_historyEntries.first() = entry; + + /** + * be done... + */ + return; + } + + /** + * ok, we have more than one entry or the entry is referenced, just add up new entries + */ + m_historyEntries.push_back (entry); +} + +void TextHistory::lockRevision (qint64 revision) +{ + /** + * some invariants must hold + */ + Q_ASSERT (!m_historyEntries.empty ()); + Q_ASSERT (revision >= m_firstHistoryEntryRevision); + Q_ASSERT (revision < (m_firstHistoryEntryRevision + m_historyEntries.size())); + + /** + * increment revision reference counter + */ + Entry &entry = m_historyEntries[revision - m_firstHistoryEntryRevision]; + ++entry.referenceCounter; +} + +void TextHistory::unlockRevision (qint64 revision) +{ + /** + * some invariants must hold + */ + Q_ASSERT (!m_historyEntries.empty ()); + Q_ASSERT (revision >= m_firstHistoryEntryRevision); + Q_ASSERT (revision < (m_firstHistoryEntryRevision + m_historyEntries.size())); + + /** + * decrement revision reference counter + */ + Entry &entry = m_historyEntries[revision - m_firstHistoryEntryRevision]; + Q_ASSERT (entry.referenceCounter); + --entry.referenceCounter; + + /** + * clean up no longer used revisions... + */ + if (!entry.referenceCounter) { + /** + * search for now unused stuff + */ + int unreferencedEdits = 0; + for (int i = 0; i + 1 < m_historyEntries.size(); ++i) { + if (m_historyEntries.at(i).referenceCounter) + break; + + // remember deleted count + ++unreferencedEdits; + } + + /** + * remove unrefed from the list now + */ + if (unreferencedEdits > 0) { + // remove stuff from history + m_historyEntries.erase (m_historyEntries.begin(), m_historyEntries.begin() + unreferencedEdits); + + // patch first entry revision + m_firstHistoryEntryRevision += unreferencedEdits; + } + } +} + +void TextHistory::Entry::transformCursor (int &cursorLine, int &cursorColumn, bool moveOnInsert) const +{ + /** + * simple stuff, sort out generic things + */ + + /** + * no change, if this change is in line behind cursor + */ + if (line > cursorLine) + return; + + /** + * handle all history types + */ + switch (type) { + /** + * Wrap a line + */ + case WrapLine: + /** + * we wrap this line + */ + if (cursorLine == line) { + /** + * skip cursors with too small column + */ + if (cursorColumn <= column) { + if (cursorColumn < column || !moveOnInsert) + return; + } + + /** + * adjust column + */ + cursorColumn = cursorColumn - column; + } + + /** + * always increment cursor line + */ + cursorLine += 1; + return; + + /** + * Unwrap a line + */ + case UnwrapLine: + /** + * we unwrap this line, adjust column + */ + if (cursorLine == line) + cursorColumn += oldLineLength; + + /** + * decrease cursor line + */ + cursorLine -= 1; + return; + + /** + * Insert text + */ + case InsertText: + /** + * only interesting, if same line + */ + if (cursorLine != line) + return; + + // skip cursors with too small column + if (cursorColumn <= column) + if (cursorColumn < column || !moveOnInsert) + return; + + // patch column of cursor + if (cursorColumn <= oldLineLength) + cursorColumn += length; + + // special handling if cursor behind the real line, e.g. non-wrapping cursor in block selection mode + else if (cursorColumn < oldLineLength + length) + cursorColumn = oldLineLength + length; + + return; + + /** + * Remove text + */ + case RemoveText: + /** + * only interesting, if same line + */ + if (cursorLine != line) + return; + + // skip cursors with too small column + if (cursorColumn <= column) + return; + + // patch column of cursor + if (cursorColumn <= column + length) + cursorColumn = column; + else + cursorColumn -= length; + + return; + + /** + * nothing + */ + default: + return; + } +} + +void TextHistory::Entry::reverseTransformCursor (int &cursorLine, int &cursorColumn, bool moveOnInsert) const +{ + /** + * handle all history types + */ + switch (type) { + /** + * Wrap a line + */ + case WrapLine: + /** + * ignore this line + */ + if (cursorLine <= line) + return; + + /** + * next line is unwrapped + */ + if (cursorLine == line + 1) { + /** + * adjust column + */ + cursorColumn = cursorColumn + column; + } + + /** + * always decrement cursor line + */ + cursorLine -= 1; + return; + + /** + * Unwrap a line + */ + case UnwrapLine: + /** + * ignore lines before unwrapped one + */ + if (cursorLine < line - 1) + return; + + /** + * we unwrap this line, try to adjust cursor column if needed + */ + if (cursorLine == line - 1) { + /** + * skip cursors with to small columns + */ + if (cursorColumn <= oldLineLength) { + if (cursorColumn < oldLineLength || !moveOnInsert) + return; + } + + cursorColumn -= oldLineLength; + } + + /** + * increase cursor line + */ + cursorLine += 1; + return; + + /** + * Insert text + */ + case InsertText: + /** + * only interesting, if same line + */ + if (cursorLine != line) + return; + + // skip cursors with too small column + if (cursorColumn <= column) + return; + + // patch column of cursor + if (cursorColumn - length < column) + cursorColumn = column; + else + cursorColumn -= length; + + return; + + /** + * Remove text + */ + case RemoveText: + /** + * only interesting, if same line + */ + if (cursorLine != line) + return; + + // skip cursors with too small column + if (cursorColumn <= column) + if (cursorColumn < column || !moveOnInsert) + return; + + // patch column of cursor + if (cursorColumn <= oldLineLength) + cursorColumn += length; + + // special handling if cursor behind the real line, e.g. non-wrapping cursor in block selection mode + else if (cursorColumn < oldLineLength + length) + cursorColumn = oldLineLength + length; + return; + + /** + * nothing + */ + default: + return; + } +} + +void TextHistory::transformCursor (int& line, int& column, KTextEditor::MovingCursor::InsertBehavior insertBehavior, qint64 fromRevision, qint64 toRevision) +{ + /** + * -1 special meaning for from/toRevision + */ + if (fromRevision == -1) + fromRevision = revision (); + + if (toRevision == -1) + toRevision = revision (); + + /** + * shortcut, same revision + */ + if (fromRevision == toRevision) + return; + + /** + * some invariants must hold + */ + Q_ASSERT (!m_historyEntries.empty ()); + Q_ASSERT (fromRevision != toRevision); + Q_ASSERT (fromRevision >= m_firstHistoryEntryRevision); + Q_ASSERT (fromRevision < (m_firstHistoryEntryRevision + m_historyEntries.size())); + Q_ASSERT (toRevision >= m_firstHistoryEntryRevision); + Q_ASSERT (toRevision < (m_firstHistoryEntryRevision + m_historyEntries.size())); + + /** + * transform cursor + */ + bool moveOnInsert = insertBehavior == KTextEditor::MovingCursor::MoveOnInsert; + + /** + * forward or reverse transform? + */ + if (toRevision > fromRevision) { + for (int rev = fromRevision - m_firstHistoryEntryRevision + 1; rev <= (toRevision - m_firstHistoryEntryRevision); ++rev) { + const Entry &entry = m_historyEntries.at(rev); + entry.transformCursor (line, column, moveOnInsert); + } + } else { + for (int rev = fromRevision - m_firstHistoryEntryRevision; rev >= (toRevision - m_firstHistoryEntryRevision + 1); --rev) { + const Entry &entry = m_historyEntries.at(rev); + entry.reverseTransformCursor (line, column, moveOnInsert); + } + } +} + +void TextHistory::transformRange (KTextEditor::Range &range, KTextEditor::MovingRange::InsertBehaviors insertBehaviors, KTextEditor::MovingRange::EmptyBehavior emptyBehavior, qint64 fromRevision, qint64 toRevision) +{ + /** + * invalidate on empty? + */ + bool invalidateIfEmpty = emptyBehavior == KTextEditor::MovingRange::InvalidateIfEmpty; + if (invalidateIfEmpty && range.end() <= range.start()) { + range = KTextEditor::Range::invalid(); + return; + } + + /** + * -1 special meaning for from/toRevision + */ + if (fromRevision == -1) + fromRevision = revision (); + + if (toRevision == -1) + toRevision = revision (); + + /** + * shortcut, same revision + */ + if (fromRevision == toRevision) + return; + + /** + * some invariants must hold + */ + Q_ASSERT (!m_historyEntries.empty ()); + Q_ASSERT (fromRevision != toRevision); + Q_ASSERT (fromRevision >= m_firstHistoryEntryRevision); + Q_ASSERT (fromRevision < (m_firstHistoryEntryRevision + m_historyEntries.size())); + Q_ASSERT (toRevision >= m_firstHistoryEntryRevision); + Q_ASSERT (toRevision < (m_firstHistoryEntryRevision + m_historyEntries.size())); + + /** + * transform cursors + */ + + // first: copy cursors, without range association + int startLine = range.start().line(), startColumn = range.start().column(), endLine = range.end().line(), endColumn = range.end().column(); + + bool moveOnInsertStart = !(insertBehaviors & KTextEditor::MovingRange::ExpandLeft); + bool moveOnInsertEnd = (insertBehaviors & KTextEditor::MovingRange::ExpandRight); + + /** + * forward or reverse transform? + */ + if (toRevision > fromRevision) { + for (int rev = fromRevision - m_firstHistoryEntryRevision + 1; rev <= (toRevision - m_firstHistoryEntryRevision); ++rev) { + const Entry &entry = m_historyEntries.at(rev); + + entry.transformCursor (startLine, startColumn, moveOnInsertStart); + + entry.transformCursor (endLine, endColumn, moveOnInsertEnd); + + // got empty? + if(endLine < startLine || (endLine == startLine && endColumn <= startColumn)) + { + if (invalidateIfEmpty) { + range = KTextEditor::Range::invalid(); + return; + } + else{ + // else normalize them + endLine = startLine; + endColumn = startColumn; + } + } + } + } else { + for (int rev = fromRevision - m_firstHistoryEntryRevision ; rev >= (toRevision - m_firstHistoryEntryRevision + 1); --rev) { + const Entry &entry = m_historyEntries.at(rev); + + entry.reverseTransformCursor (startLine, startColumn, moveOnInsertStart); + + entry.reverseTransformCursor (endLine, endColumn, moveOnInsertEnd); + + // got empty? + if(endLine < startLine || (endLine == startLine && endColumn <= startColumn)) + { + if (invalidateIfEmpty) { + range = KTextEditor::Range::invalid(); + return; + } + else{ + // else normalize them + endLine = startLine; + endColumn = startColumn; + } + } + } + } + + // now, copy cursors back + range.start().setLine(startLine); + range.start().setColumn(startColumn); + range.end().setLine(endLine); + range.end().setColumn(endColumn); + +} + +} diff --git a/kate/part/buffer/katetexthistory.h b/kate/part/buffer/katetexthistory.h new file mode 100644 index 00000000..88a21520 --- /dev/null +++ b/kate/part/buffer/katetexthistory.h @@ -0,0 +1,240 @@ +/* This file is part of the Kate project. + * + * Copyright (C) 2010 Christoph Cullmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef KATE_TEXTHISTORY_H +#define KATE_TEXTHISTORY_H + +#include + +#include + +#include "katepartinterfaces_export.h" +#include "katetextcursor.h" +#include "katetextrange.h" + +namespace Kate { + +class TextBuffer; + +/** + * Class representing the editing history of a TextBuffer + */ +class KATEPARTINTERFACES_EXPORT TextHistory { + friend class TextBuffer; + friend class TextBlock; + + public: + /** + * Current revision, just relay the revision of the buffer + * @return current revision + */ + qint64 revision () const; + + /** + * Last revision the buffer got successful saved + * @return last revision buffer got saved, -1 if none + */ + qint64 lastSavedRevision () const { return m_lastSavedRevision; } + + /** + * Lock a revision, this will keep it around until released again. + * But all revisions will always be cleared on buffer clear() (and therefor load()) + * @param revision revision to lock + */ + void lockRevision (qint64 revision); + + /** + * Release a revision. + * @param revision revision to release + */ + void unlockRevision (qint64 revision); + + /** + * Transform a cursor from one revision to an other. + * @param line line number of the cursor to transform + * @param column column number of the cursor to transform + * @param insertBehavior behavior of this cursor on insert of text at its position + * @param fromRevision from this revision we want to transform + * @param toRevision to this revision we want to transform, default of -1 is current revision + */ + void transformCursor (int& line, int& column, KTextEditor::MovingCursor::InsertBehavior insertBehavior, qint64 fromRevision, qint64 toRevision = -1); + + /** + * Transform a range from one revision to an other. + * @param range range to transform + * @param insertBehaviors behavior of this range on insert of text at its position + * @param emptyBehavior behavior on becoming empty + * @param fromRevision from this revision we want to transform + * @param toRevision to this revision we want to transform, default of -1 is current revision + */ + void transformRange (KTextEditor::Range &range, KTextEditor::MovingRange::InsertBehaviors insertBehaviors, KTextEditor::MovingRange::EmptyBehavior emptyBehavior, qint64 fromRevision, qint64 toRevision = -1); + + private: + /** + * Class representing one entry in the editing history. + */ + class Entry { + public: + /** + * transform cursor for this history entry + * @param line line number of the cursor to transform + * @param column column number of the cursor to transform + * @param moveOnInsert behavior of this cursor on insert of text at its position + */ + void transformCursor (int &line, int &column, bool moveOnInsert) const; + + /** + * reverse transform cursor for this history entry + * @param line line number of the cursor to transform + * @param column column number of the cursor to transform + * @param moveOnInsert behavior of this cursor on insert of text at its position + */ + void reverseTransformCursor (int &line, int &column, bool moveOnInsert) const; + + /** + * Types of entries, matching editing primitives of buffer and placeholder + */ + enum Type { + NoChange + , WrapLine + , UnwrapLine + , InsertText + , RemoveText + }; + + /** + * Default Constructor, invalidates all fields + */ + Entry () + : referenceCounter (0), type (NoChange), line (-1), column (-1), length (-1), oldLineLength (-1) + { + } + + /** + * Reference counter, how often ist this entry referenced from the outside? + */ + unsigned int referenceCounter; + + /** + * Type of change + */ + Type type; + + /** + * line the change occurred + */ + int line; + + /** + * column the change occurred + */ + int column; + + /** + * length of change (length of insert or removed text) + */ + int length; + + /** + * old line length (needed for unwrap and insert) + */ + int oldLineLength; + }; + + /** + * Construct an empty text history. + * @param buffer buffer this text history belongs to + */ + TextHistory (TextBuffer &buffer); + + /** + * Destruct the text history + */ + ~TextHistory (); + + /** + * Clear the edit history, this is done on clear() in buffer. + */ + void clear (); + + /** + * Set current revision as last saved revision + */ + void setLastSavedRevision (); + + /** + * Notify about wrap line at given cursor position. + * @param position line/column as cursor where to wrap + */ + void wrapLine (const KTextEditor::Cursor &position); + + /** + * Notify about unwrap given line. + * @param line line to unwrap + * @param oldLineLength text length of the line in front of this one before this unwrap + */ + void unwrapLine (int line, int oldLineLength); + + /** + * Notify about insert text at given cursor position. + * @param position position where to insert text + * @param length text length to be inserted + * @param oldLineLength text length of the line before this insert + */ + void insertText (const KTextEditor::Cursor &position, int length, int oldLineLength); + + /** + * Notify about remove text at given range. + * @param range range of text to remove, must be on one line only. + * @param oldLineLength text length of the line before this remove + */ + void removeText (const KTextEditor::Range &range, int oldLineLength); + + /** + * Generic function to add a entry to the history. Is used by the above functions for the different editing primitives. + * @param entry new entry to add + */ + void addEntry (const Entry &entry); + + private: + /** + * TextBuffer this history belongs to + */ + TextBuffer &m_buffer; + + /** + * Last revision the buffer got saved + */ + qint64 m_lastSavedRevision; + + /** + * history of edits + */ + QList m_historyEntries; + + /** + * offset for the first entry in m_history, to which revision it really belongs? + */ + qint64 m_firstHistoryEntryRevision; +}; + +} + +#endif diff --git a/kate/part/buffer/katetextline.cpp b/kate/part/buffer/katetextline.cpp new file mode 100644 index 00000000..55260625 --- /dev/null +++ b/kate/part/buffer/katetextline.cpp @@ -0,0 +1,199 @@ +/* This file is part of the Kate project. + * + * Copyright (C) 2010 Christoph Cullmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "katetextline.h" + +namespace Kate { + +TextLineData::TextLineData () + : m_flags (0) +{ +} + +TextLineData::TextLineData (const QString &text) + : m_text (text) + , m_flags (0) +{ +} + +TextLineData::~TextLineData () +{ +} + +int TextLineData::firstChar() const +{ + return nextNonSpaceChar(0); +} + +int TextLineData::lastChar() const +{ + return previousNonSpaceChar(m_text.length() - 1); +} + +int TextLineData::nextNonSpaceChar (int pos) const +{ + Q_ASSERT (pos >= 0); + + for(int i = pos; i < m_text.length(); i++) + if (!m_text[i].isSpace()) + return i; + + return -1; +} + +int TextLineData::previousNonSpaceChar (int pos) const +{ + if (pos >= m_text.length()) + pos = m_text.length() - 1; + + for(int i = pos; i >= 0; i--) + if (!m_text[i].isSpace()) + return i; + + return -1; +} + +QString TextLineData::leadingWhitespace() const +{ + if (firstChar() < 0) + return string(0, length()); + + return string(0, firstChar()); +} + +int TextLineData::indentDepth (int tabWidth) const +{ + int d = 0; + const int len = m_text.length(); + const QChar *unicode = m_text.unicode(); + + for(int i = 0; i < len; ++i) + { + if(unicode[i].isSpace()) + { + if (unicode[i] == QLatin1Char('\t')) + d += tabWidth - (d % tabWidth); + else + d++; + } + else + return d; + } + + return d; +} + +bool TextLineData::matchesAt(int column, const QString& match) const +{ + if (column < 0) + return false; + + const int len = m_text.length(); + const int matchlen = match.length(); + + if ((column + matchlen) > len) + return false; + + const QChar *unicode = m_text.unicode(); + const QChar *matchUnicode = match.unicode(); + + for (int i=0; i < matchlen; ++i) + if (unicode[i+column] != matchUnicode[i]) + return false; + + return true; +} + +int TextLineData::toVirtualColumn (int column, int tabWidth) const +{ + if (column < 0) + return 0; + + int x = 0; + const int zmax = qMin(column, m_text.length()); + const QChar *unicode = m_text.unicode(); + + for ( int z = 0; z < zmax; ++z) + { + if (unicode[z] == QLatin1Char('\t')) + x += tabWidth - (x % tabWidth); + else + x++; + } + + return x + column - zmax; +} + +int TextLineData::fromVirtualColumn (int column, int tabWidth) const +{ + if (column < 0) + return 0; + + const int zmax = qMin(m_text.length(), column); + const QChar *unicode = m_text.unicode(); + + int x = 0; + int z = 0; + for (; z < zmax; ++z) + { + int diff = 1; + if (unicode[z] == QLatin1Char('\t')) + diff = tabWidth - (x % tabWidth); + + if (x + diff > column) + break; + x += diff; + } + + return z + qMax(column - x, 0); +} + +int TextLineData::virtualLength (int tabWidth) const +{ + int x = 0; + const int len = m_text.length(); + const QChar *unicode = m_text.unicode(); + + for ( int z = 0; z < len; ++z) + { + if (unicode[z] == QLatin1Char('\t')) + x += tabWidth - (x % tabWidth); + else + x++; + } + + return x; +} + +void TextLineData::addAttribute (const Attribute &attribute) +{ + // try to append to previous range, if no folding info + same attribute value + if ((attribute.foldingValue == 0) && !m_attributesList.isEmpty() && (m_attributesList.back().foldingValue == 0) + && (m_attributesList.back().attributeValue == attribute.attributeValue) + && ((m_attributesList.back().offset + m_attributesList.back().length) == attribute.offset)) + { + m_attributesList.back().length += attribute.length; + return; + } + + m_attributesList.append (attribute); +} + +} diff --git a/kate/part/buffer/katetextline.h b/kate/part/buffer/katetextline.h new file mode 100644 index 00000000..a6ef5f8c --- /dev/null +++ b/kate/part/buffer/katetextline.h @@ -0,0 +1,447 @@ +/* This file is part of the Kate project. + * + * Copyright (C) 2010 Christoph Cullmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef KATE_TEXTLINE_H +#define KATE_TEXTLINE_H + +#include +#include +#include + +#include "katepartinterfaces_export.h" + +namespace Kate { + +/** + * Class representing a single text line. + * For efficience reasons, not only pure text is stored here, but also additional data. + * Will be only accessed over shared pointers. + */ +class KATEPARTINTERFACES_EXPORT TextLineData { + /** + * TexBlock is a friend class, only one allowed to touch the text content. + */ + friend class TextBlock; + + public: + /** + * Context stack + */ + typedef QVector ContextStack; + + /** + * Attribute storage + */ + class Attribute { + public: + /** + * Attribute constructor + * @param _offset offset + * @param _length length + * @param _attributeValue attribute value + * @param _foldingValue folding value + */ + Attribute (int _offset = 0, int _length = 0, short _attributeValue = 0, short _foldingValue = 0) + : offset (_offset) + , length (_length) + , attributeValue (_attributeValue) + , foldingValue (_foldingValue) + { + } + + /** + * offset + */ + int offset; + + /** + * length + */ + int length; + + /** + * attribute value (to encode type of this range) + */ + short attributeValue; + + /** + * folding value (begin/end type) + */ + short foldingValue; + }; + + /** + * Flags of TextLineData + */ + enum Flags + { + flagHlContinue = 1, + flagAutoWrapped = 2, + flagFoldingStartAttribute = 4, + flagFoldingStartIndentation = 8, + flagLineModified = 16, + flagLineSavedOnDisk = 32 + }; + + /** + * Construct an empty text line. + */ + TextLineData (); + + /** + * Construct an text line with given text. + * @param text text to use for this line + */ + TextLineData (const QString &text); + + /** + * Destruct the text line + */ + ~TextLineData (); + + /** + * Accessor to the text contained in this line. + * @return text of this line as constant reference + */ + const QString &text () const { return m_text; } + + /** + * Returns the position of the first non-whitespace character + * @return position of first non-whitespace char or -1 if there is none + */ + int firstChar() const; + + /** + * Returns the position of the last non-whitespace character + * @return position of last non-whitespace char or -1 if there is none + */ + int lastChar() const; + + /** + * 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 nextNonSpaceChar(int pos) const; + + /** + * 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 previousNonSpaceChar(int pos) const; + + /** + * Returns the character at the given \e column. If \e column is out of + * range, the return value is QChar(). + * @param column column you want char for + * @return char at given column or QChar() + */ + inline QChar at (int column) const + { + if (column >= 0 && column < m_text.length()) + return m_text[column]; + + return QChar(); + } + + /** + * Same as at(). + * @param column column you want char for + * @return char at given column or QChar() + */ + inline QChar operator[](int column) const + { + if (column >= 0 && column < m_text.length()) + return m_text[column]; + + return QChar(); + } + + inline void markAsModified(bool modified) + { + if (modified) { + m_flags |= flagLineModified; + m_flags &= (~flagLineSavedOnDisk); + } else { + m_flags &= (~flagLineModified); + } + } + + inline bool markedAsModified() const + { + return m_flags & flagLineModified; + } + + inline void markAsSavedOnDisk(bool savedOnDisk) + { + if (savedOnDisk) { + m_flags |= flagLineSavedOnDisk; + m_flags &= (~flagLineModified); + } else { + m_flags &= (~flagLineSavedOnDisk); + } + } + + inline bool markedAsSavedOnDisk() const + { + return m_flags & flagLineSavedOnDisk; + } + + /** + * Is on this line a folding start? + * @return folding start line or not? + */ + bool markedAsFoldingStart() const + { + return m_flags & (flagFoldingStartAttribute | flagFoldingStartIndentation); + } + + /** + * Clear folding start status. + */ + void clearMarkedAsFoldingStart () + { + m_flags &= ~(flagFoldingStartAttribute | flagFoldingStartIndentation); + } + + /** + * Is on this line a folding start per attribute? + * @return folding start line per attribute? or not? + */ + bool markedAsFoldingStartAttribute() const + { + return m_flags & flagFoldingStartAttribute; + } + + /** + * Is on this line a folding start per indentation? + * @return folding start line per indentation? or not? + */ + bool markedAsFoldingStartIndentation() const + { + return m_flags & flagFoldingStartIndentation; + } + + /** + * Mark as folding start line of an attribute based folding. + */ + void markAsFoldingStartAttribute () + { + clearMarkedAsFoldingStart (); + m_flags |= flagFoldingStartAttribute; + } + + /** + * Mark as folding start line of an indentation based folding. + */ + void markAsFoldingStartIndentation () + { + clearMarkedAsFoldingStart (); + m_flags |= flagFoldingStartIndentation; + } + + /** + * Returns the line's length. + */ + int length() const { return m_text.length(); } + + /** + * Returns \e true, if the line's hl-continue flag is set, otherwise returns + * \e false. The hl-continue flag is set in the hl-definition files. + * @return hl-continue flag is set + */ + bool hlLineContinue () const { return m_flags & flagHlContinue; } + + /** + * Returns \e true, if the line was automagically wrapped, otherwise returns + * \e false. + * @return was this line auto-wrapped? + */ + bool isAutoWrapped () const { return m_flags & flagAutoWrapped; } + + /** + * Returns the complete text line (as a QString reference). + * @return text of this line, read-only + */ + const QString& string() const { return m_text; } + + /** + * Returns the substring with \e length beginning at the given \e column. + * @param column start column of text to return + * @param length length of text to return + * @return wanted part of text + */ + QString string (int column, int length) const + { return m_text.mid(column, length); } + + /** + * Leading whitespace of this line + * @return leading whitespace of this line + */ + QString leadingWhitespace() const; + + /** + * Returns the indentation depth with each tab expanded into \e tabWidth characters. + */ + int indentDepth (int tabWidth) const; + + /** + * Returns the \e column with each tab expanded into \e tabWidth characters. + */ + int toVirtualColumn (int column, int tabWidth) const; + + /** + * Returns the "real" column where each tab only counts one character. + * The conversion calculates with \e tabWidth characters for each tab. + */ + int fromVirtualColumn (int column, int tabWidth) const; + + /** + * Returns the text length with each tab expanded into \e tabWidth characters. + */ + int virtualLength (int tabWidth) const; + + /** + * Returns \e true, if \e match equals to the text at position \e column, + * otherwise returns \e false. + */ + bool matchesAt(int column, const QString& match) const; + + /** + * Returns \e true, if the line starts with \e match, otherwise returns \e false. + */ + bool startsWith(const QString& match) const { return m_text.startsWith (match); } + + /** + * Returns \e true, if the line ends with \e match, otherwise returns \e false. + */ + bool endsWith(const QString& match) const { return m_text.endsWith (match); } + + /** + * context stack + * @return context stack + */ + const ContextStack &contextStack () const { return m_contextStack; } + + /** + * Sets the syntax highlight context number + * @param val new context array + */ + void setContextStack (const ContextStack &val) { m_contextStack = val; } + + /** + * Add attribute to this line. + * @param attribute new attribute to append + */ + void addAttribute (const Attribute &attribute); + + /** + * Clear attributes of this line + */ + void clearAttributes () { m_attributesList.clear (); } + + /** + * Accessor to attributes + * @return attributes of this line + */ + const QVector &attributesList () const { return m_attributesList; } + + /** + * Gets the attribute at the given position + * use KRenderer::attributes to get the KTextAttribute for this. + * + * @param pos position of attribute requested + * @return value of attribute + */ + short attribute (int pos) const + { + for (int i=0; i < m_attributesList.size(); ++i) + { + if (pos >= m_attributesList[i].offset && pos < (m_attributesList[i].offset + m_attributesList[i].length)) + return m_attributesList[i].attributeValue; + + if (pos < m_attributesList[i].offset) + break; + } + + return 0; + } + + /** + * set hl continue flag + * @param cont continue flag? + */ + void setHlLineContinue (bool cont) + { + if (cont) m_flags = m_flags | flagHlContinue; + else m_flags = m_flags & ~ flagHlContinue; + } + + /** + * set auto-wrapped property + * @param wrapped line was wrapped? + */ + void setAutoWrapped (bool wrapped) + { + if (wrapped) m_flags = m_flags | flagAutoWrapped; + else m_flags = m_flags & ~ flagAutoWrapped; + } + + private: + /** + * Accessor to the text contained in this line. + * This accessor is private, only the friend class text buffer/block is allowed to access the text read/write. + * @return text of this line + */ + QString &textReadWrite () { return m_text; } + + private: + /** + * text of this line + */ + QString m_text; + + /** + * attributes of this line + */ + QVector m_attributesList; + + /** + * context stack of this line + */ + ContextStack m_contextStack; + + /** + * flags of this line + */ + unsigned int m_flags; +}; + +/** + * The normal world only accesses the text lines with shared pointers. + */ +typedef QSharedPointer TextLine; + +} + +#endif diff --git a/kate/part/buffer/katetextloader.h b/kate/part/buffer/katetextloader.h new file mode 100644 index 00000000..c52e0ff0 --- /dev/null +++ b/kate/part/buffer/katetextloader.h @@ -0,0 +1,346 @@ +/* This file is part of the Kate project. + * + * Copyright (C) 2010 Christoph Cullmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef KATE_TEXTLOADER_H +#define KATE_TEXTLOADER_H + +#include + +#include +#include +#include + +namespace Kate { + +/** + * File Loader, will handle reading of files + detecting encoding + */ +class TextLoader +{ + public: + /** + * Construct file loader for given file. + * @param filename file to open + * @param proberType prober type + */ + TextLoader (const QString &filename) + : m_codec (0) + , m_eof (false) // default to not eof + , m_lastWasEndOfLine (true) // at start of file, we had a virtual newline + , m_lastWasR (false) // we have not found a \r as last char + , m_position (0) + , m_lastLineStart (0) + , m_eol (TextBuffer::eolUnknown) // no eol type detected atm + , m_buffer (KATE_FILE_LOADER_BS, 0) + , m_digest (KATE_HASH_ALGORITHM) + , m_converterState (0) + , m_bomFound (false) + , m_firstRead (true) + { + // construct file device + m_file = new QFile (filename); + } + + /** + * Destructor + */ + ~TextLoader () + { + delete m_file; + delete m_converterState; + } + + /** + * open file with given codec + * @param codec codec to use, if 0, will do some auto-dectect or fallback + * @return success + */ + bool open (QTextCodec *codec) + { + m_codec = codec; + m_eof = false; + m_lastWasEndOfLine = true; + m_lastWasR = false; + m_position = 0; + m_lastLineStart = 0; + m_eol = TextBuffer::eolUnknown; + m_text.clear (); + delete m_converterState; + m_converterState = new QTextCodec::ConverterState (QTextCodec::ConvertInvalidToNull); + m_bomFound = false; + m_firstRead = true; + + // if already opened, close the file... + if (m_file->isOpen()) + m_file->close (); + + return m_file->open (QIODevice::ReadOnly); + } + + /** + * end of file reached? + * @return end of file reached + */ + bool eof () const { return m_eof && !m_lastWasEndOfLine && (m_lastLineStart == m_text.length()); } + + /** + * Detected end of line mode for this file. + * Detected during reading, is valid after complete file is read. + * @return eol mode of this file + */ + TextBuffer::EndOfLineMode eol () const { return m_eol; } + + /** + * BOM found? + * @return byte order mark found? + */ + bool byteOrderMarkFound () const { return m_bomFound; } + + /** + * internal unicode data array + * @return internal unicode data + */ + const QChar *unicode () const { return m_text.unicode(); } + + /** + * Get codec for this loader + * @return currently in use codec of this loader + */ + QTextCodec *textCodec () const { return m_codec; } + + /** + * read a line, return length + offset in unicode data + * @param offset offset into internal unicode data for read line + * @param length length of read line + * @return true if no encoding errors occurred + */ + bool readLine (int &offset, int &length) + { + length = 0; + offset = 0; + bool encodingError = false; + + static const QLatin1Char cr(QLatin1Char('\r')); + static const QLatin1Char lf(QLatin1Char('\n')); + + /** + * did we read two time but got no stuff? encoding error + * fixes problem with one character latin-1 files, which lead to crash otherwise! + * bug 272579 + */ + bool failedToConvertOnce = false; + + /** + * reading loop + */ + while (m_position <= m_text.length()) + { + if (m_position == m_text.length()) + { + // try to load more text if something is around + if (!m_eof) + { + // kill the old lines... + m_text.remove (0, m_lastLineStart); + + // try to read new data + const int c = m_file->read(m_buffer.data(), m_buffer.size()); + + // if any text is there, append it.... + if (c > 0) + { + // update hash sum + m_digest.addData (m_buffer.data(), c); + + // detect byte order marks & codec for byte order markers on first read + int bomBytes = 0; + if (m_firstRead) { + // use first 16 bytes max to allow BOM detection of codec + QByteArray bom (m_buffer.data(), qMin (16, c)); + QTextCodec *codecForByteOrderMark = QTextCodec::codecForUtfText (bom, 0); + + // if codec != null, we found a BOM! + if (codecForByteOrderMark) { + m_bomFound = true; + + // eat away the different boms! + int mib = codecForByteOrderMark->mibEnum (); + if (mib == 106) // utf8 + bomBytes = 3; + if (mib == 1013 || mib == 1014 || mib == 1015) // utf16 + bomBytes = 2; + if (mib == 1017 || mib == 1018 || mib == 1019) // utf32 + bomBytes = 4; + } + + /** + * if no codec given, do autodetection + */ + if (!m_codec) { + /** + * byte order said something about encoding? + */ + if (codecForByteOrderMark) + m_codec = codecForByteOrderMark; + else { + return false; + } + } + + m_firstRead = false; + } + + Q_ASSERT (m_codec); + QString unicode = m_codec->toUnicode (m_buffer.constData() + bomBytes, c - bomBytes, m_converterState); + + // detect broken encoding + for (int i = 0; i < unicode.size(); ++i) { + if (unicode[i] == 0) { + encodingError = true; + break; + } + } + + m_text.append (unicode); + } + + // is file completely read ? + m_eof = (c == -1) || (c == 0); + + // recalc current pos and last pos + m_position -= m_lastLineStart; + m_lastLineStart = 0; + } + + // oh oh, end of file, escape ! + if (m_eof && (m_position == m_text.length())) + { + m_lastWasEndOfLine = false; + + // line data + offset = m_lastLineStart; + length = m_position-m_lastLineStart; + + m_lastLineStart = m_position; + + return !encodingError && !failedToConvertOnce; + } + + // empty? try again + if (m_position == m_text.length()) { + failedToConvertOnce = true; + continue; + } + } + + if (m_text.at(m_position) == lf) + { + m_lastWasEndOfLine = true; + + if (m_lastWasR) + { + m_lastLineStart++; + m_lastWasR = false; + m_eol = TextBuffer::eolDos; + } + else + { + // line data + offset = m_lastLineStart; + length = m_position-m_lastLineStart; + + m_lastLineStart = m_position+1; + m_position++; + + // only win, if not dos! + if (m_eol != TextBuffer::eolDos) + m_eol = TextBuffer::eolUnix; + + return !encodingError; + } + } + else if (m_text.at(m_position) == cr) + { + m_lastWasEndOfLine = true; + m_lastWasR = true; + + // line data + offset = m_lastLineStart; + length = m_position-m_lastLineStart; + + m_lastLineStart = m_position+1; + m_position++; + + // should only win of first time! + if (m_eol == TextBuffer::eolUnknown) + m_eol = TextBuffer::eolMac; + + return !encodingError; + } + else if (m_text.at(m_position) == QChar::LineSeparator) + { + m_lastWasEndOfLine = true; + + // line data + offset = m_lastLineStart; + length = m_position-m_lastLineStart; + + m_lastLineStart = m_position+1; + m_position++; + + return !encodingError; + } + else + { + m_lastWasEndOfLine = false; + m_lastWasR = false; + } + + m_position++; + } + + return !encodingError; + } + + QByteArray digest () + { + return m_digest.result (); + } + + private: + QTextCodec *m_codec; + bool m_eof; + bool m_lastWasEndOfLine; + bool m_lastWasR; + int m_position; + int m_lastLineStart; + TextBuffer::EndOfLineMode m_eol; + QIODevice *m_file; + QByteArray m_buffer; + QCryptographicHash m_digest; + QString m_text; + QTextCodec::ConverterState *m_converterState; + bool m_bomFound; + bool m_firstRead; +}; + +} + +#endif diff --git a/kate/part/buffer/katetextrange.cpp b/kate/part/buffer/katetextrange.cpp new file mode 100644 index 00000000..f24af4e1 --- /dev/null +++ b/kate/part/buffer/katetextrange.cpp @@ -0,0 +1,331 @@ +/* This file is part of the Kate project. + * + * Copyright (C) 2010 Christoph Cullmann + * + * Based on code of the SmartCursor/Range by: + * Copyright (C) 2003-2005 Hamish Rodda + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "katetextrange.h" +#include "katetextbuffer.h" + +namespace Kate { + +TextRange::TextRange (TextBuffer &buffer, const KTextEditor::Range &range, InsertBehaviors insertBehavior, EmptyBehavior emptyBehavior) + : m_buffer (buffer) + , m_start (buffer, this, range.start(), (insertBehavior & ExpandLeft) ? Kate::TextCursor::StayOnInsert : Kate::TextCursor::MoveOnInsert) + , m_end (buffer, this, range.end(), (insertBehavior & ExpandRight) ? Kate::TextCursor::MoveOnInsert : Kate::TextCursor::StayOnInsert) + , m_view (0) + , m_feedback (0) + , m_zDepth (0.0) + , m_attributeOnlyForViews (false) + , m_invalidateIfEmpty (emptyBehavior == InvalidateIfEmpty) +{ + // remember this range in buffer + m_buffer.m_ranges.insert (this); + + // check if range now invalid, there can happen no feedback, as m_feedback == 0 + checkValidity (); +} + +TextRange::~TextRange () +{ + /** + * reset feedback, don't want feedback during destruction + */ + m_feedback = 0; + + // remove range from m_ranges + fixLookup (m_start.line(), m_end.line(), -1, -1); + + // remove this range from the buffer + m_buffer.m_ranges.remove (this); + + /** + * trigger update, if we have attribute + * notify right view + * here we can ignore feedback, even with feedback, we want none if the range is deleted! + */ + if (m_attribute) + m_buffer.notifyAboutRangeChange (m_view, m_start.line(), m_end.line(), true /* we have a attribute */); +} + +void TextRange::setInsertBehaviors (InsertBehaviors _insertBehaviors) +{ + /** + * nothing to do? + */ + if (_insertBehaviors == insertBehaviors ()) + return; + + /** + * modify cursors + */ + m_start.setInsertBehavior ((_insertBehaviors & ExpandLeft) ? KTextEditor::MovingCursor::StayOnInsert : KTextEditor::MovingCursor::MoveOnInsert); + m_end.setInsertBehavior ((_insertBehaviors & ExpandRight) ? KTextEditor::MovingCursor::MoveOnInsert : KTextEditor::MovingCursor::StayOnInsert); + + /** + * notify world + */ + if (m_attribute || m_feedback) + m_buffer.notifyAboutRangeChange (m_view, m_start.line(), m_end.line(), true /* we have a attribute */); +} + +KTextEditor::MovingRange::InsertBehaviors TextRange::insertBehaviors () const +{ + InsertBehaviors behaviors = DoNotExpand; + + if (m_start.insertBehavior() == KTextEditor::MovingCursor::StayOnInsert) + behaviors |= ExpandLeft; + + if (m_end.insertBehavior() == KTextEditor::MovingCursor::MoveOnInsert) + behaviors |= ExpandRight; + + return behaviors; +} + +void TextRange::setEmptyBehavior (EmptyBehavior emptyBehavior) +{ + /** + * nothing to do? + */ + if (m_invalidateIfEmpty == (emptyBehavior == InvalidateIfEmpty)) + return; + + /** + * remember value + */ + m_invalidateIfEmpty = (emptyBehavior == InvalidateIfEmpty); + + /** + * invalidate range? + */ + if (end() <= start()) + setRange (KTextEditor::Range::invalid()); +} + +void TextRange::setRange (const KTextEditor::Range &range) +{ + // avoid work if nothing changed! + if (range == toRange()) + return; + + // remember old line range + int oldStartLine = m_start.line(); + int oldEndLine = m_end.line(); + + // change start and end cursor + m_start.setPosition (range.start ()); + m_end.setPosition (range.end ()); + + // check if range now invalid, don't emit feedback here, will be handled below + // otherwise you can't delete ranges in feedback! + checkValidity (oldStartLine, oldEndLine, false); + + // no attribute or feedback set, be done + if (!m_attribute && !m_feedback) + return; + + // get full range + int startLineMin = oldStartLine; + if (oldStartLine == -1 || (m_start.line() != -1 && m_start.line() < oldStartLine)) + startLineMin = m_start.line(); + + int endLineMax = oldEndLine; + if (oldEndLine == -1 || m_end.line() > oldEndLine) + endLineMax = m_end.line(); + + /** + * notify buffer about attribute change, it will propagate the changes + * notify right view + */ + m_buffer.notifyAboutRangeChange (m_view, startLineMin, endLineMax, m_attribute); + + // perhaps need to notify stuff! + if (m_feedback) { + // do this last: may delete this range + if (!toRange().isValid()) + m_feedback->rangeInvalid (this); + else if (toRange().isEmpty()) + m_feedback->rangeEmpty (this); + } +} + +void TextRange::checkValidity (int oldStartLine, int oldEndLine, bool notifyAboutChange) +{ + /** + * check if any cursor is invalid or the range is zero size and it should be invalidated then + */ + if (!m_start.isValid() || !m_end.isValid() || (m_invalidateIfEmpty && m_end <= m_start)) { + m_start.setPosition (-1, -1); + m_end.setPosition (-1, -1); + } + + /** + * for ranges which are allowed to become empty, normalize them, if the end has moved to the front of the start + */ + if (!m_invalidateIfEmpty && m_end < m_start) + m_end.setPosition (m_start); + + // fix lookup + fixLookup (oldStartLine, oldEndLine, m_start.line(), m_end.line()); + + // perhaps need to notify stuff! + if (notifyAboutChange && m_feedback) { + m_buffer.notifyAboutRangeChange (m_view, m_start.line(), m_end.line(), false /* attribute not interesting here */); + + // do this last: may delete this range + if (!toRange().isValid()) + m_feedback->rangeInvalid (this); + else if (toRange().isEmpty()) + m_feedback->rangeEmpty (this); + } +} + +void TextRange::fixLookup (int oldStartLine, int oldEndLine, int startLine, int endLine) +{ + // nothing changed? + if (oldStartLine == startLine && oldEndLine == endLine) + return; + + // now, not both can be invalid + Q_ASSERT (oldStartLine >= 0 || startLine >= 0); + Q_ASSERT (oldEndLine >= 0 || endLine >= 0); + + // get full range + int startLineMin = oldStartLine; + if (oldStartLine == -1 || (startLine != -1 && startLine < oldStartLine)) + startLineMin = startLine; + + int endLineMax = oldEndLine; + if (oldEndLine == -1 || endLine > oldEndLine) + endLineMax = endLine; + + // get start block + int blockIndex = m_buffer.blockForLine (startLineMin); + Q_ASSERT (blockIndex >= 0); + + // remove this range from m_ranges + for (; blockIndex < m_buffer.m_blocks.size(); ++blockIndex) { + // get block + TextBlock *block = m_buffer.m_blocks[blockIndex]; + + // either insert or remove range + if ((endLine < block->startLine()) || (startLine >= (block->startLine() + block->lines()))) + block->removeRange (this); + else + block->updateRange (this); + + // ok, reached end block + if (endLineMax < (block->startLine() + block->lines())) + return; + } + + // we should not be here, really, then endLine is wrong + Q_ASSERT (false); +} + +void TextRange::setView (KTextEditor::View *view) +{ + /** + * nothing changes, nop + */ + if (view == m_view) + return; + + /** + * remember the new attribute + */ + m_view = view; + + /** + * notify buffer about attribute change, it will propagate the changes + * notify all views (can be optimized later) + */ + if (m_attribute || m_feedback) + m_buffer.notifyAboutRangeChange (0, m_start.line(), m_end.line(), m_attribute); +} + +void TextRange::setAttribute ( KTextEditor::Attribute::Ptr attribute ) +{ + /** + * remember the new attribute + */ + m_attribute = attribute; + + /** + * notify buffer about attribute change, it will propagate the changes + * notify right view + */ + m_buffer.notifyAboutRangeChange (m_view, m_start.line(), m_end.line(), m_attribute); +} + +void TextRange::setFeedback (KTextEditor::MovingRangeFeedback *feedback) +{ + /** + * nothing changes, nop + */ + if (feedback == m_feedback) + return; + + /** + * remember the new feedback object + */ + m_feedback = feedback; + + /** + * notify buffer about feedback change, it will propagate the changes + * notify right view + */ + m_buffer.notifyAboutRangeChange (m_view, m_start.line(), m_end.line(), m_attribute); +} + +void TextRange::setAttributeOnlyForViews (bool onlyForViews) +{ + /** + * just set the value, no need to trigger updates, printing is not interruptable + */ + m_attributeOnlyForViews = onlyForViews; +} + +void TextRange::setZDepth (qreal zDepth) +{ + /** + * nothing changes, nop + */ + if (zDepth == m_zDepth) + return; + + /** + * remember the new attribute + */ + m_zDepth = zDepth; + + /** + * notify buffer about attribute change, it will propagate the changes + */ + if (m_attribute) + m_buffer.notifyAboutRangeChange (m_view, m_start.line(), m_end.line(), m_attribute); +} + +KTextEditor::Document *Kate::TextRange::document () const +{ + return m_buffer.document(); +} + +} diff --git a/kate/part/buffer/katetextrange.h b/kate/part/buffer/katetextrange.h new file mode 100644 index 00000000..4fbb4dab --- /dev/null +++ b/kate/part/buffer/katetextrange.h @@ -0,0 +1,319 @@ +/* This file is part of the Kate project. + * + * Copyright (C) 2010 Christoph Cullmann + * + * Based on code of the SmartCursor/Range by: + * Copyright (C) 2003-2005 Hamish Rodda + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef KATE_TEXTRANGE_H +#define KATE_TEXTRANGE_H + +#include +#include +#include + +#include "katepartinterfaces_export.h" +#include "katetextcursor.h" + +namespace Kate { + +class TextBuffer; + +/** + * Class representing a 'clever' text range. + * It will automagically move if the text inside the buffer it belongs to is modified. + * By intention no subclass of KTextEditor::Range, must be converted manually. + * A TextRange is allowed to be empty. If you call setInvalidateIfEmpty(true), + * a TextRange will become automatically invalid as soon as start() == end() + * position holds. + */ +class KATEPARTINTERFACES_EXPORT TextRange : public KTextEditor::MovingRange { + // this is a friend, block changes might invalidate ranges... + friend class TextBlock; + + public: + /** + * Construct a text range. + * A TextRange is not allowed to be empty, as soon as start == end position, it will become + * automatically invalid! + * @param buffer parent text buffer + * @param range The initial text range assumed by the new range. + * @param insertBehavior Define whether the range should expand when text is inserted adjacent to the range. + * @param emptyBehavior Define whether the range should invalidate itself on becoming empty. + */ + TextRange (TextBuffer &buffer, const KTextEditor::Range &range, InsertBehaviors insertBehavior, EmptyBehavior emptyBehavior = AllowEmpty); + + /** + * Destruct the text block + */ + ~TextRange (); + + /** + * Set insert behaviors. + * @param insertBehaviors new insert behaviors + */ + void setInsertBehaviors (InsertBehaviors insertBehaviors); + + /** + * Get current insert behaviors. + * @return current insert behaviors + */ + InsertBehaviors insertBehaviors () const; + + /** + * Set if this range will invalidate itself if it becomes empty. + * @param emptyBehavior behavior on becoming empty + */ + void setEmptyBehavior (EmptyBehavior emptyBehavior); + + /** + * Will this range invalidate itself if it becomes empty? + * @return behavior on becoming empty + */ + EmptyBehavior emptyBehavior () const { return m_invalidateIfEmpty ? InvalidateIfEmpty : AllowEmpty; } + + /** + * Gets the document to which this range is bound. + * \return a pointer to the document + */ + KTextEditor::Document *document () const; + + /** + * Set the range of this range. + * A TextRange is not allowed to be empty, as soon as start == end position, it will become + * automatically invalid! + * @param range new range for this clever range + */ + void setRange (const KTextEditor::Range &range); + + /** + * \overload + * Set the range of this range + * A TextRange is not allowed to be empty, as soon as start == end position, it will become + * automatically invalid! + * @param start new start for this clever range + * @param end new end for this clever range + */ + void setRange (const KTextEditor::Cursor &start, const KTextEditor::Cursor &end) { KTextEditor::MovingRange::setRange (start, end); } + + /** + * Retrieve start cursor of this range, read-only. + * @return start cursor + */ + const KTextEditor::MovingCursor &start () const { return m_start; } + + /** + * Non-virtual version of start(), which is faster. + * @return start cursor + */ + const TextCursor &startInternal() const { return m_start; } + + /** + * Retrieve end cursor of this range, read-only. + * @return end cursor + */ + const KTextEditor::MovingCursor &end () const { return m_end; } + + /** + * Nonvirtual version of end(), which is faster. + * @return end cursor + */ + const TextCursor &endInternal () const { return m_end; } + + /** + * Convert this clever range into a dumb one. + * @return normal range + */ + const KTextEditor::Range toRange () const { return KTextEditor::Range (start().toCursor(), end().toCursor()); } + + /** + * Convert this clever range into a dumb one. Equal to toRange, allowing to use implicit conversion. + * @return normal range + */ + operator const KTextEditor::Range () const { return KTextEditor::Range (start().toCursor(), end().toCursor()); } + + /** + * Gets the active view for this range. Might be already invalid, internally only used for pointer comparisons. + * + * \return a pointer to the active view + */ + KTextEditor::View *view () const { return m_view; } + + /** + * Sets the currently active view for this range. + * This will trigger update of the relevant view parts, if the view changed. + * Set view before the attribute, that will avoid not needed redraws. + * + * \param attribute View to assign to this range. If null, simply + * removes the previous view. + */ + void setView (KTextEditor::View *view); + + /** + * Gets the active Attribute for this range. + * + * \return a pointer to the active attribute + */ + KTextEditor::Attribute::Ptr attribute () const { return m_attribute; } + + /** + * \return whether a nonzero attribute is set. This is faster than checking attribute(), + * because the reference-counting is omitted. + */ + bool hasAttribute() const { return !m_attribute.isNull(); } + + /** + * Sets the currently active attribute for this range. + * This will trigger update of the relevant view parts. + * + * \param attribute Attribute to assign to this range. If null, simply + * removes the previous Attribute. + */ + void setAttribute (KTextEditor::Attribute::Ptr attribute); + + /** + * Gets the active MovingRangeFeedback for this range. + * + * \return a pointer to the active MovingRangeFeedback + */ + KTextEditor::MovingRangeFeedback *feedback () const { return m_feedback; } + + /** + * Sets the currently active MovingRangeFeedback for this range. + * This will trigger evaluation if feedback must be send again (for example if mouse is already inside range). + * + * \param attribute MovingRangeFeedback to assign to this range. If null, simply + * removes the previous MovingRangeFeedback. + */ + void setFeedback (KTextEditor::MovingRangeFeedback *feedback); + + /** + * Is this range's attribute only visible in views, not for example prints? + * Default is false. + * @return range visible only for views + */ + bool attributeOnlyForViews () const { return m_attributeOnlyForViews; } + + /** + * Set if this range's attribute is only visible in views, not for example prints. + * @param onlyForViews attribute only valid for views + */ + void setAttributeOnlyForViews (bool onlyForViews); + + /** + * Gets the current Z-depth of this range. + * Ranges with smaller Z-depth than others will win during rendering. + * Default is 0.0. + * + * \return current Z-depth of this range + */ + qreal zDepth () const { return m_zDepth; } + + /** + * Set the current Z-depth of this range. + * Ranges with smaller Z-depth than others will win during rendering. + * This will trigger update of the relevant view parts, if the depth changed. + * Set depth before the attribute, that will avoid not needed redraws. + * Default is 0.0. + * + * \param zDepth new Z-depth of this range + */ + void setZDepth (qreal zDepth); + + private: + /** + * no copy constructor, don't allow this to be copied. + */ + TextRange (const TextRange &); + + /** + * no assignment operator, no copying around. + */ + TextRange &operator= (const TextRange &); + + /** + * Check if range is valid, used by constructor and setRange. + * If at least one cursor is invalid, both will set to invalid. + * Same if range itself is invalid (start >= end). + * @param oldStartLine old start line of this range before changing of cursors, needed to add/remove range from m_ranges in blocks + * @param oldEndLine old end line of this range + * @param notifyAboutChange should feedback be emitted or not? + */ + void checkValidity (int oldStartLine = -1, int oldEndLine = -1, bool notifyAboutChange = true); + + /** + * Add/Remove range from the lookup m_ranges hash of each block + * @param oldStartLine old start line of this range before changing of cursors, needed to add/remove range from m_ranges in blocks + * @param oldEndLine old end line of this range + * @param startLine start line to start looking for the range to remove + * @param endLine end line of this range + */ + void fixLookup (int oldStartLine, int oldEndLine, int startLine, int endLine); + + private: + /** + * parent text buffer + * is a reference, and no pointer, as this must always exist and can't change + */ + TextBuffer &m_buffer; + + /** + * Start cursor for this range, is a clever cursor + */ + TextCursor m_start; + + /** + * End cursor for this range, is a clever cursor + */ + TextCursor m_end; + + /** + * The view for which the attribute is valid, 0 means any view + */ + KTextEditor::View *m_view; + + /** + * This range's current attribute. + */ + KTextEditor::Attribute::Ptr m_attribute; + + /** + * pointer to the active MovingRangeFeedback + */ + KTextEditor::MovingRangeFeedback *m_feedback; + + /** + * Z-depth of this range for rendering + */ + qreal m_zDepth; + + /** + * Is this range's attribute only visible in views, not for example prints? + */ + bool m_attributeOnlyForViews; + + /** + * Will this range invalidate itself if it becomes empty? + */ + bool m_invalidateIfEmpty; +}; + +} + +#endif diff --git a/kate/part/completion/codecompletionmodelcontrollerinterfacev4.cpp b/kate/part/completion/codecompletionmodelcontrollerinterfacev4.cpp new file mode 100644 index 00000000..0ae259e8 --- /dev/null +++ b/kate/part/completion/codecompletionmodelcontrollerinterfacev4.cpp @@ -0,0 +1,31 @@ +/* This file is part of the KDE libraries + Copyright (C) 20010 David Nolden + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "codecompletionmodelcontrollerinterfacev4.h" + +namespace KTextEditor { + +//BEGIN V4 +bool CodeCompletionModelControllerInterface4::shouldHideItemsWithEqualNames() const +{ + return false; +} +//END V4 + +} diff --git a/kate/part/completion/codecompletionmodelcontrollerinterfacev4.h b/kate/part/completion/codecompletionmodelcontrollerinterfacev4.h new file mode 100644 index 00000000..9a5c1d8f --- /dev/null +++ b/kate/part/completion/codecompletionmodelcontrollerinterfacev4.h @@ -0,0 +1,47 @@ +/* This file is part of the KDE libraries + Copyright (C) 20010 David Nolden + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KDELIBS_KTEXTEDITOR_CODECOMPLETIONMODELCONTROLLERINTERFACE_V4_H +#define KDELIBS_KTEXTEDITOR_CODECOMPLETIONMODELCONTROLLERINTERFACE_V4_H + +#include + +#include "katepartinterfaces_export.h" + +/// @todo For KDE 4.7 this interface (or a version of it) should go into interfaces/ktexteditor + +namespace KTextEditor { +//BEGIN V4 +///Extension of CodeCompletionModelControllerInterface +class CodeCompletionModelControllerInterface4 : public CodeCompletionModelControllerInterface { + public: + + /** + * When multiple completion models are used at the same time, it may happen that multiple models add items with the same + * name to the list. This option allows to hide items from this completion model when another model with higher priority + * contains items with the same name. + * \return Whether items of this completion model should be hidden if another completion model has items with the same name + */ + virtual bool shouldHideItemsWithEqualNames() const; +}; +//END V4 +} + +Q_DECLARE_INTERFACE(KTextEditor::CodeCompletionModelControllerInterface4, "org.kde.KTextEditor.CodeCompletionModelControllerInterface4") +#endif // KDELIBS_KTEXTEDITOR_CODECOMPLETIONMODELCONTROLLERINTERFACE_V4_H diff --git a/kate/part/completion/completionconfigwidget.ui b/kate/part/completion/completionconfigwidget.ui new file mode 100644 index 00000000..186cec39 --- /dev/null +++ b/kate/part/completion/completionconfigwidget.ui @@ -0,0 +1,455 @@ + + + CompletionConfigWidget + + + + + + Sorting + + + true + + + true + + + false + + + + + + 0 + + + + + Alphabetical + + + + + + + Reverse + + + + + + + Case sensitive + + + + + + + Inheritance depth + + + + + + + Qt::Vertical + + + + 0 + 0 + + + + + + + + + + 0 + + + + + Order of Groupings (select a grouping method to configure): + + + + + + + 0 + + + + + + + + 0 + + + + + ^ + + + + + + + \/ + + + + + + + + + + + + + + + + Filtering + + + true + + + true + + + false + + + + + + Suitable context matches only + + + + + + + Hide completions with the following attributes: + + + + + + + + + + Maximum inheritance depth: + + + 0 + + + 20 + + + 0 + + + Infinity + + + + + + + + + + Grouping + + + true + + + true + + + false + + + + + + false + + + + Grouping Method + + + + + Scope type (local, namespace, global) + + + + + Scope (eg. per class) + + + + + Access type (public etc.) + + + + + Item type (function etc.) + + + + + + + + 0 + + + + + ^ + + + + + + + \/ + + + + + + + + + 0 + + + + + 0 + + + + + 0 + + + + + + 75 + true + + + + Access Grouping Properties + + + + + + + Include const in grouping + + + + + + + Include static in grouping + + + + + + + Include signals and slots in grouping + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + 75 + true + + + + Item Grouping properties + + + + + + + Include templates in grouping + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + + + + Column Merging + + + true + + + true + + + false + + + + + + + 0 + 0 + + + + false + + + + Columns + + + + + Merged + + + + + Shown + + + + + + + + 0 + + + + + ^ + + + + + + + \/ + + + + + + + + + Qt::Horizontal + + + + 20 + 105 + + + + + + + + + + + + KIntNumInput + QWidget +
knuminput.h
+
+
+ + + + filteringHideAttributes + toggled(bool) + filteringAttributesList + setEnabled(bool) + + + 290 + 298 + + + 307 + 375 + + + + +
diff --git a/kate/part/completion/expandingtree/expandingdelegate.cpp b/kate/part/completion/expandingtree/expandingdelegate.cpp new file mode 100644 index 00000000..d3b0f0b7 --- /dev/null +++ b/kate/part/completion/expandingtree/expandingdelegate.cpp @@ -0,0 +1,338 @@ +/* This file is part of the KDE libraries and the Kate part. + * + * Copyright (C) 2006 Hamish Rodda + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "expandingdelegate.h" + +#include +#include +#include +#include +#include +#include + +#include + +#include "expandingwidgetmodel.h" + +ExpandingDelegate::ExpandingDelegate(ExpandingWidgetModel* model, QObject* parent) + : QItemDelegate(parent) + , m_model(model) +{ +} + +//Gets the background-color in the way QItemDelegate does it +static QColor getUsedBackgroundColor(const QStyleOptionViewItem & option, const QModelIndex& index) { +if (option.showDecorationSelected && (option.state & QStyle::State_Selected)) { + QPalette::ColorGroup cg = option.state & QStyle::State_Enabled + ? QPalette::Normal : QPalette::Disabled; + if (cg == QPalette::Normal && !(option.state & QStyle::State_Active)) + cg = QPalette::Inactive; + + return option.palette.brush(cg, QPalette::Highlight).color(); + } else { + QVariant value = index.data(Qt::BackgroundRole); + if (value.canConvert()) + return qvariant_cast(value).color(); + } + + return QApplication::palette().base().color(); +} + +static void dampColors(QColor& col) { + //Reduce the colors that are less visible to the eye, because they are closer to black when it comes to contrast + //The most significant color to the eye is green. Then comes red, and then blue, with blue _much_ less significant. + + col.setBlue(0); + col.setRed(col.red() / 2); +} + +//A hack to compute more eye-focused contrast values +static double readabilityContrast(QColor foreground, QColor background) { + dampColors(foreground); + dampColors(background); + return abs(foreground.green()-background.green()) + abs(foreground.red()-background.red()) + abs(foreground.blue() - background.blue()); +} + +void ExpandingDelegate::paint( QPainter * painter, const QStyleOptionViewItem & optionOld, const QModelIndex & index ) const +{ + QStyleOptionViewItem option(optionOld); + + m_currentIndex = index; + + adjustStyle(index, option); + + if( index.column() == 0 ) + model()->placeExpandingWidget(index); + + //Make sure the decorations are painted at the top, because the center of expanded items will be filled with the embedded widget. + if( model()->isPartiallyExpanded(index) == ExpandingWidgetModel::ExpandUpwards ) + m_cachedAlignment = Qt::AlignBottom; + else + m_cachedAlignment = Qt::AlignTop; + + option.decorationAlignment = m_cachedAlignment; + option.displayAlignment = m_cachedAlignment; + + //kDebug( 13035 ) << "Painting row " << index.row() << ", column " << index.column() << ", internal " << index.internalPointer() << ", drawselected " << option.showDecorationSelected << ", selected " << (option.state & QStyle::State_Selected); + + m_cachedHighlights.clear(); + m_backgroundColor = getUsedBackgroundColor(option, index); + + if (model()->indexIsItem(index) ) { + m_currentColumnStart = 0; + m_cachedHighlights = createHighlighting(index, option); + } + + /*kDebug( 13035 ) << "Highlights for line:"; + foreach (const QTextLayout::FormatRange& fr, m_cachedHighlights) + kDebug( 13035 ) << fr.start << " len " << fr.length << " format ";*/ + + QItemDelegate::paint(painter, option, index); + + ///This is a bug workaround for the Qt raster paint engine: It paints over widgets embedded into the viewport when updating due to mouse events + ///@todo report to Qt Software + if( model()->isExpanded(index) && model()->expandingWidget( index ) ) + model()->expandingWidget( index )->update(); +} + +QList ExpandingDelegate::createHighlighting(const QModelIndex& index, QStyleOptionViewItem& option) const { + Q_UNUSED( index ); + Q_UNUSED( option ); + return QList(); +} + +QSize ExpandingDelegate::basicSizeHint( const QModelIndex& index ) const { + return QItemDelegate::sizeHint( QStyleOptionViewItem(), index ); +} + +QSize ExpandingDelegate::sizeHint ( const QStyleOptionViewItem & option, const QModelIndex & index ) const +{ + QSize s = QItemDelegate::sizeHint( option, index ); + if( model()->isExpanded(index) && model()->expandingWidget( index ) ) + { + QWidget* widget = model()->expandingWidget( index ); + QSize widgetSize = widget->size(); + + s.setHeight( widgetSize.height() + s.height() + 10 ); //10 is the sum that must match exactly the offsets used in ExpandingWidgetModel::placeExpandingWidgets + } else if( model()->isPartiallyExpanded( index ) ) { + s.setHeight( s.height() + 30 + 10 ); + } + return s; +} + +void ExpandingDelegate::adjustStyle( const QModelIndex& index, QStyleOptionViewItem & option ) const +{ + Q_UNUSED(index) + Q_UNUSED(option) +} + +void ExpandingDelegate::adjustRect(QRect& rect) const { + if (!model()->indexIsItem(m_currentIndex) /*&& m_currentIndex.column() == 0*/) { + + rect.setLeft(model()->treeView()->columnViewportPosition(0)); + int columnCount = model()->columnCount(m_currentIndex.parent()); + + if(!columnCount) + return; + rect.setRight(model()->treeView()->columnViewportPosition(columnCount-1) + model()->treeView()->columnWidth(columnCount-1)); + } +} + +void ExpandingDelegate::drawDisplay( QPainter * painter, const QStyleOptionViewItem & option, const QRect & _rect, const QString & text ) const +{ + QRect rect(_rect); + + adjustRect(rect); + + QTextLayout layout(text, option.font, painter->device()); + + QList additionalFormats; + + int missingFormats = text.length(); + + for (int i = 0; i < m_cachedHighlights.count(); ++i) { + if (m_cachedHighlights[i].start + m_cachedHighlights[i].length <= m_currentColumnStart) + continue; + + if (!additionalFormats.count()) + if (i != 0 && m_cachedHighlights[i - 1].start + m_cachedHighlights[i - 1].length > m_currentColumnStart) { + QTextLayout::FormatRange before; + before.start = 0; + before.length = m_cachedHighlights[i - 1].start + m_cachedHighlights[i - 1].length - m_currentColumnStart; + before.format = m_cachedHighlights[i - 1].format; + additionalFormats.append(before); + } + + + QTextLayout::FormatRange format; + format.start = m_cachedHighlights[i].start - m_currentColumnStart; + format.length = m_cachedHighlights[i].length; + format.format = m_cachedHighlights[i].format; + + additionalFormats.append(format); + } + if(!additionalFormats.isEmpty()) + missingFormats = text.length() - (additionalFormats.back().length + additionalFormats.back().start); + + if (missingFormats > 0) { + QTextLayout::FormatRange format; + format.start = text.length() - missingFormats; + format.length = missingFormats; + QTextCharFormat fm; + fm.setForeground(option.palette.text()); + format.format = fm; + additionalFormats.append(format); + } + + if(m_backgroundColor.isValid()) { + QColor background = m_backgroundColor; +// kDebug() << text << "background:" << background.name(); + //Now go through the formats, and make sure the contrast background/foreground is readable + for(int a = 0; a < additionalFormats.size(); ++a) { + QColor currentBackground = background; + if(additionalFormats[a].format.hasProperty(QTextFormat::BackgroundBrush)) + currentBackground = additionalFormats[a].format.background().color(); + + QColor currentColor = additionalFormats[a].format.foreground().color(); + + double currentContrast = readabilityContrast(currentColor, currentBackground); + QColor invertedColor(0xffffffff-additionalFormats[a].format.foreground().color().rgb()); + double invertedContrast = readabilityContrast(invertedColor, currentBackground); + +// kDebug() << "values:" << invertedContrast << currentContrast << invertedColor.name() << currentColor.name(); + + if(invertedContrast > currentContrast) { +// kDebug() << text << additionalFormats[a].length << "switching from" << currentColor.name() << "to" << invertedColor.name(); + QBrush b(additionalFormats[a].format.foreground()); + b.setColor(invertedColor); + additionalFormats[a].format.setForeground(b); + } + } + } + + for(int a = additionalFormats.size()-1; a >= 0; --a) { + if(additionalFormats[a].length == 0){ + additionalFormats.removeAt(a); + }else{ + ///For some reason the text-formats seem to be invalid in some way, sometimes + ///@todo Fix this properly, it sucks not copying everything over + QTextCharFormat fm; + fm.setForeground(QBrush(additionalFormats[a].format.foreground().color())); + fm.setBackground(additionalFormats[a].format.background()); + fm.setUnderlineStyle( additionalFormats[a].format.underlineStyle() ); + fm.setUnderlineColor( additionalFormats[a].format.underlineColor() ); + fm.setFontWeight( additionalFormats[a].format.fontWeight() ); + additionalFormats[a].format = fm; + } + } + +// kDebug( 13035 ) << "Highlights for text [" << text << "] col start " << m_currentColumnStart << ":"; +// foreach (const QTextLayout::FormatRange& fr, additionalFormats) +// kDebug( 13035 ) << fr.start << " len " << fr.length << "foreground" << fr.format.foreground() << "background" << fr.format.background(); + + layout.setAdditionalFormats(additionalFormats); + + QTextOption to; + + to.setAlignment( static_cast(m_cachedAlignment | option.displayAlignment) ); + + to.setWrapMode(QTextOption::WrapAnywhere); + layout.setTextOption(to); + + layout.beginLayout(); + QTextLine line = layout.createLine(); + // Leave some extra space when the text is right-aligned + line.setLineWidth(rect.width() - (option.displayAlignment == Qt::AlignRight ? 8 : 0)); + layout.endLayout(); + + //We need to do some hand layouting here + if( to.alignment() & Qt::AlignBottom) + layout.draw(painter, QPoint(rect.left(), rect.bottom() - (int)line.height()) ); + else + layout.draw(painter, rect.topLeft() ); + + return; + + //if (painter->fontMetrics().width(text) > textRect.width() && !text.contains(QLatin1Char('\n'))) + //str = elidedText(option.fontMetrics, textRect.width(), option.textElideMode, text); + //qt_format_text(option.font, textRect, option.displayAlignment, str, 0, 0, 0, 0, painter); +} + +void ExpandingDelegate::drawDecoration(QPainter* painter, const QStyleOptionViewItem& option, const QRect& rect, const QPixmap& pixmap) const { + if (model()->indexIsItem(m_currentIndex) ) + QItemDelegate::drawDecoration(painter, option, rect, pixmap); +} + +void ExpandingDelegate::drawBackground ( QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index ) const { + Q_UNUSED(index) + QStyleOptionViewItemV4 opt = option; + //initStyleOption(&opt, index); + //Problem: This isn't called at all, because drawBrackground is not virtual :-/ + QStyle *style = model()->treeView()->style() ? model()->treeView()->style() : QApplication::style(); + style->drawControl(QStyle::CE_ItemViewItem, &opt, painter); +} + +ExpandingWidgetModel* ExpandingDelegate::model() const { + return m_model; +} + +void ExpandingDelegate::heightChanged() const { +} + +bool ExpandingDelegate::editorEvent ( QEvent * event, QAbstractItemModel * /*model*/, const QStyleOptionViewItem & /*option*/, const QModelIndex & index ) +{ + if( event->type() == QEvent::MouseButtonRelease ) + { + event->accept(); + model()->setExpanded(index, !model()->isExpanded( index )); + heightChanged(); + + return true; + } else { + event->ignore(); + } + + return false; +} + +QList ExpandingDelegate::highlightingFromVariantList(const QList& customHighlights) const +{ + QList ret; + + for (int i = 0; i + 2 < customHighlights.count(); i += 3) { + if (!customHighlights[i].canConvert(QVariant::Int) || !customHighlights[i+1].canConvert(QVariant::Int) || !customHighlights[i+2].canConvert()) { + kWarning() << "Unable to convert triple to custom formatting."; + continue; + } + + QTextLayout::FormatRange format; + format.start = customHighlights[i].toInt(); + format.length = customHighlights[i+1].toInt(); + format.format = customHighlights[i+2].value().toCharFormat(); + + if(!format.format.isValid()) + kWarning() << "Format is not valid"; + + ret << format; + } + return ret; +} + +#include "moc_expandingdelegate.cpp" diff --git a/kate/part/completion/expandingtree/expandingdelegate.h b/kate/part/completion/expandingtree/expandingdelegate.h new file mode 100644 index 00000000..31d03535 --- /dev/null +++ b/kate/part/completion/expandingtree/expandingdelegate.h @@ -0,0 +1,93 @@ +/* This file is part of the KDE libraries and the Kate part. + * + * Copyright (C) 2006 Hamish Rodda + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef EXPANDINGDELEGATE_H +#define EXPANDINGDELEGATE_H + +#include +#include +#include +#include +#include + +class KateRenderer; +class KateCompletionWidget; +class KateDocument; +class KateTextLine; +class ExpandingWidgetModel; +#include +#include + +/** + * This is a delegate that cares, together with ExpandingWidgetModel, about embedded widgets in tree-view. + * */ + +class ExpandingDelegate : public QItemDelegate +{ + Q_OBJECT + + public: + explicit ExpandingDelegate(ExpandingWidgetModel* model, QObject* parent = 0L); + + + // Overridden to create highlighting for current index + virtual void paint ( QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index ) const; + + // Returns the basic size-hint as reported by QItemDelegate + QSize basicSizeHint( const QModelIndex& index ) const; + + ExpandingWidgetModel* model() const; + protected: + //Called right before paint to allow last-minute changes to the style + virtual void adjustStyle( const QModelIndex& index, QStyleOptionViewItem & option ) const; + virtual void drawDisplay ( QPainter * painter, const QStyleOptionViewItem & option, const QRect & rect, const QString & text ) const; + virtual QSize sizeHint ( const QStyleOptionViewItem & option, const QModelIndex & index ) const; + virtual bool editorEvent ( QEvent * event, QAbstractItemModel * model, const QStyleOptionViewItem & option, const QModelIndex & index ); + virtual void drawBackground ( QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index ) const; + virtual void drawDecoration(QPainter* painter, const QStyleOptionViewItem& option, const QRect& rect, const QPixmap& pixmap) const; + //option can be changed + virtual QList createHighlighting(const QModelIndex& index, QStyleOptionViewItem& option) const; + + void adjustRect(QRect& rect) const; + + /** + * Creates a list of FormatRanges as should be returned by createHighlighting from a list of QVariants as described in the kde header ktexteditor/codecompletionmodel.h + * */ + QList highlightingFromVariantList(const QList& customHighlights) const; + + //Called when an item was expanded/unexpanded and the height changed + virtual void heightChanged() const; + + //Initializes the style options from the index + void initStyleOption(QStyleOptionViewItem *option, const QModelIndex &index) const; + + mutable int m_currentColumnStart; //Text-offset for custom highlighting, will be applied to m_cachedHighlights(Only highlights starting after this will be used). Shoult be zero of the highlighting is not taken from kate. + mutable QList m_currentColumnStarts; + mutable QList m_cachedHighlights; + + mutable Qt::Alignment m_cachedAlignment; + mutable QColor m_backgroundColor; + mutable QModelIndex m_currentIndex; + private: + + ExpandingWidgetModel* m_model; +}; + +#endif diff --git a/kate/part/completion/expandingtree/expandingtree.cpp b/kate/part/completion/expandingtree/expandingtree.cpp new file mode 100644 index 00000000..83c14366 --- /dev/null +++ b/kate/part/completion/expandingtree/expandingtree.cpp @@ -0,0 +1,64 @@ +/* This file is part of the KDE libraries and the Kate part. + * + * Copyright (C) 2007 David Nolden + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "expandingtree.h" + +#include +#include +#include +#include +#include +#include "expandingwidgetmodel.h" + +ExpandingTree::ExpandingTree(QWidget* parent) : QTreeView(parent) { + m_drawText.documentLayout()->setPaintDevice(this); + setUniformRowHeights(false); +} + +void ExpandingTree::drawRow ( QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index ) const { + QTreeView::drawRow( painter, option, index ); + + const ExpandingWidgetModel* eModel = qobject_cast(model()); + if( eModel && eModel->isPartiallyExpanded( index ) ) + { + QRect rect = eModel->partialExpandRect( index ); + if( rect.isValid() ) + { + painter->fillRect(rect,QBrush(0xffffffff)); + + QAbstractTextDocumentLayout::PaintContext ctx; + // since arbitrary HTML can be shown use a black on white color scheme here + ctx.palette = QPalette( Qt::black, Qt::white ); + ctx.clip = QRectF(0,0,rect.width(),rect.height());; + painter->setViewTransformEnabled(true); + painter->translate(rect.left(), rect.top()); + + m_drawText.setHtml( eModel->partialExpandText( index ) ); + m_drawText.setPageSize(QSizeF(rect.width(), rect.height())); + m_drawText.documentLayout()->draw( painter, ctx ); + + painter->translate(-rect.left(), -rect.top()); + } + } +} + +int ExpandingTree::sizeHintForColumn ( int column ) const { + return columnWidth( column ); +} diff --git a/kate/part/completion/expandingtree/expandingtree.h b/kate/part/completion/expandingtree/expandingtree.h new file mode 100644 index 00000000..47c38dce --- /dev/null +++ b/kate/part/completion/expandingtree/expandingtree.h @@ -0,0 +1,38 @@ +/* This file is part of the KDE libraries and the Kate part. + * + * Copyright (C) 2007 David Nolden + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef EXPANDINGTREE_H +#define EXPANDINGTREE_H + +#include +#include + +//A tree that allows drawing additional information +class ExpandingTree : public QTreeView { + public: + ExpandingTree(QWidget* parent); + protected: + virtual void drawRow ( QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index ) const; + virtual int sizeHintForColumn ( int column ) const; + private: + mutable QTextDocument m_drawText; +}; + +#endif diff --git a/kate/part/completion/expandingtree/expandingwidgetmodel.cpp b/kate/part/completion/expandingtree/expandingwidgetmodel.cpp new file mode 100644 index 00000000..e5671f43 --- /dev/null +++ b/kate/part/completion/expandingtree/expandingwidgetmodel.cpp @@ -0,0 +1,529 @@ +/* This file is part of the KDE libraries and the Kate part. + * + * Copyright (C) 2007 David Nolden + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "expandingwidgetmodel.h" + +#include +#include +#include + +#include +#include +#include +#include "kcolorutils.h" + +#include "expandingdelegate.h" +#include + +QIcon ExpandingWidgetModel::m_expandedIcon; +QIcon ExpandingWidgetModel::m_collapsedIcon; + +using namespace KTextEditor; + +inline QModelIndex firstColumn( const QModelIndex& index ) { + return index.sibling(index.row(), 0); +} + +ExpandingWidgetModel::ExpandingWidgetModel( QWidget* parent ) : + QAbstractTableModel(parent) +{ +} + +ExpandingWidgetModel::~ExpandingWidgetModel() { + clearExpanding(); +} + +static QColor doAlternate(QColor color) { + QColor background = QApplication::palette().background().color(); + return KColorUtils::mix(color, background, 0.15); +} + +uint ExpandingWidgetModel::matchColor(const QModelIndex& index) const { + + int matchQuality = contextMatchQuality( index.sibling(index.row(), 0) ); + + if( matchQuality > 0 ) + { + bool alternate = index.row() & 1; + + QColor badMatchColor(0xff00aa44); //Blue-ish green + QColor goodMatchColor(0xff00ff00); //Green + + QColor background = treeView()->palette().light().color(); + + QColor totalColor = KColorUtils::mix(badMatchColor, goodMatchColor, ((float)matchQuality)/10.0); + + if(alternate) + totalColor = doAlternate(totalColor); + + const float dynamicTint = 0.2; + const float minimumTint = 0.2; + double tintStrength = (dynamicTint*matchQuality)/10; + if(tintStrength) + tintStrength += minimumTint; //Some minimum tinting strength, else it's not visible any more + + return KColorUtils::tint(background, totalColor, tintStrength ).rgb(); + }else{ + return 0; + } +} + +QVariant ExpandingWidgetModel::data( const QModelIndex & index, int role ) const +{ + switch( role ) { + case Qt::BackgroundRole: + { + if( index.column() == 0 ) { + //Highlight by match-quality + uint color = matchColor(index); + if( color ) + return QBrush( color ); + } + //Use a special background-color for expanded items + if( isExpanded(index) ) { + if( index.row() & 1 ) { + return doAlternate(treeView()->palette().toolTipBase().color()); + } else { + return treeView()->palette().toolTipBase(); + } + } + } + } + return QVariant(); +} + +void ExpandingWidgetModel::clearMatchQualities() { + m_contextMatchQualities.clear(); +} + +QModelIndex ExpandingWidgetModel::partiallyExpandedRow() const { + if( m_partiallyExpanded.isEmpty() ) + return QModelIndex(); + else + return m_partiallyExpanded.constBegin().key(); +} + +void ExpandingWidgetModel::clearExpanding() { + + clearMatchQualities(); + QMap oldExpandState = m_expandState; + foreach( const QPointer &widget, m_expandingWidgets ) + if(widget) + widget->deleteLater(); // By using deleteLater, we prevent crashes when an action within a widget makes the completion cancel + m_expandingWidgets.clear(); + m_expandState.clear(); + m_partiallyExpanded.clear(); + + for( QMap::const_iterator it = oldExpandState.constBegin(); it != oldExpandState.constEnd(); ++it ) + if(it.value() == Expanded) + emit dataChanged(it.key(), it.key()); +} + +ExpandingWidgetModel::ExpansionType ExpandingWidgetModel::isPartiallyExpanded(const QModelIndex& index) const { + if( m_partiallyExpanded.contains(firstColumn(index)) ) + return m_partiallyExpanded[firstColumn(index)]; + else + return NotExpanded; +} + +void ExpandingWidgetModel::partiallyUnExpand(const QModelIndex& idx_) +{ + QModelIndex index( firstColumn(idx_) ); + m_partiallyExpanded.remove(index); + m_partiallyExpanded.remove(idx_); +} + +int ExpandingWidgetModel::partiallyExpandWidgetHeight() const { + return 60; ///@todo use font-metrics text-height*2 for 2 lines +} + +void ExpandingWidgetModel::rowSelected(const QModelIndex& idx_) +{ + QModelIndex idx( firstColumn(idx_) ); + if( !m_partiallyExpanded.contains( idx ) ) + { + QModelIndex oldIndex = partiallyExpandedRow(); + //Unexpand the previous partially expanded row + if( !m_partiallyExpanded.isEmpty() ) + { ///@todo allow multiple partially expanded rows + while( !m_partiallyExpanded.isEmpty() ) + m_partiallyExpanded.erase(m_partiallyExpanded.begin()); + //partiallyUnExpand( m_partiallyExpanded.begin().key() ); + } + //Notify the underlying models that the item was selected, and eventually get back the text for the expanding widget. + if( !idx.isValid() ) { + //All items have been unselected + if( oldIndex.isValid() ) + emit dataChanged(oldIndex, oldIndex); + } else { + QVariant variant = data(idx, CodeCompletionModel::ItemSelected); + + if( !isExpanded(idx) && variant.type() == QVariant::String) { + + //Either expand upwards or downwards, choose in a way that + //the visible fields of the new selected entry are not moved. + if( oldIndex.isValid() && (oldIndex < idx || (!(oldIndex < idx) && oldIndex.parent() < idx.parent()) ) ) + m_partiallyExpanded.insert(idx, ExpandUpwards); + else + m_partiallyExpanded.insert(idx, ExpandDownwards); + + //Say that one row above until one row below has changed, so no items will need to be moved(the space that is taken from one item is given to the other) + if( oldIndex.isValid() && oldIndex < idx ) { + emit dataChanged(oldIndex, idx); + + if( treeView()->verticalScrollMode() == QAbstractItemView::ScrollPerItem ) + { + //Qt fails to correctly scroll in ScrollPerItem mode, so the selected index is completely visible, + //so we do the scrolling by hand. + QRect selectedRect = treeView()->visualRect(idx); + QRect frameRect = treeView()->frameRect(); + + if( selectedRect.bottom() > frameRect.bottom() ) { + int diff = selectedRect.bottom() - frameRect.bottom(); + //We need to scroll down + QModelIndex newTopIndex = idx; + + QModelIndex nextTopIndex = idx; + QRect nextRect = treeView()->visualRect(nextTopIndex); + while( nextTopIndex.isValid() && nextRect.isValid() && nextRect.top() >= diff ) { + newTopIndex = nextTopIndex; + nextTopIndex = treeView()->indexAbove(nextTopIndex); + if( nextTopIndex.isValid() ) + nextRect = treeView()->visualRect(nextTopIndex); + } + treeView()->scrollTo( newTopIndex, QAbstractItemView::PositionAtTop ); + } + } + + //This is needed to keep the item we are expanding completely visible. Qt does not scroll the view to keep the item visible. + //But we must make sure that it isn't too expensive. + //We need to make sure that scrolling is efficient, and the whole content is not repainted. + //Since we are scrolling anyway, we can keep the next line visible, which might be a cool feature. + + //Since this also doesn't work smoothly, leave it for now + //treeView()->scrollTo( nextLine, QAbstractItemView::EnsureVisible ); + } else if( oldIndex.isValid() && idx < oldIndex ) { + emit dataChanged(idx, oldIndex); + + //For consistency with the down-scrolling, we keep one additional line visible above the current visible. + + //Since this also doesn't work smoothly, leave it for now +/* QModelIndex prevLine = idx.sibling(idx.row()-1, idx.column()); + if( prevLine.isValid() ) + treeView()->scrollTo( prevLine );*/ + } else + emit dataChanged(idx, idx); + } else if( oldIndex.isValid() ) { + //We are not partially expanding a new row, but we previously had a partially expanded row. So signalize that it has been unexpanded. + + emit dataChanged(oldIndex, oldIndex); + } + } + }else{ + kDebug( 13035 ) << "ExpandingWidgetModel::rowSelected: Row is already partially expanded"; + } +} + +QString ExpandingWidgetModel::partialExpandText(const QModelIndex& idx) const { + if( !idx.isValid() ) + return QString(); + + return data(firstColumn(idx), CodeCompletionModel::ItemSelected).toString(); +} + +QRect ExpandingWidgetModel::partialExpandRect(const QModelIndex& idx_) const +{ + QModelIndex idx(firstColumn(idx_)); + + if( !idx.isValid() ) + return QRect(); + + ExpansionType expansion = ExpandDownwards; + + if( m_partiallyExpanded.find(idx) != m_partiallyExpanded.constEnd() ) + expansion = m_partiallyExpanded[idx]; + + //Get the whole rectangle of the row: + QModelIndex rightMostIndex = idx; + QModelIndex tempIndex = idx; + while( (tempIndex = rightMostIndex.sibling(rightMostIndex.row(), rightMostIndex.column()+1)).isValid() ) + rightMostIndex = tempIndex; + + QRect rect = treeView()->visualRect(idx); + QRect rightMostRect = treeView()->visualRect(rightMostIndex); + + rect.setLeft( rect.left() + 20 ); + rect.setRight( rightMostRect.right() - 5 ); + + //These offsets must match exactly those used in ExpandingDelegate::sizeHint() + int top = rect.top() + 5; + int bottom = rightMostRect.bottom() - 5 ; + + if( expansion == ExpandDownwards ) + top += basicRowHeight(idx); + else + bottom -= basicRowHeight(idx); + + rect.setTop( top ); + rect.setBottom( bottom ); + + return rect; +} + +bool ExpandingWidgetModel::isExpandable(const QModelIndex& idx_) const +{ + QModelIndex idx(firstColumn(idx_)); + + if( !m_expandState.contains(idx) ) + { + m_expandState.insert(idx, NotExpandable); + QVariant v = data(idx, CodeCompletionModel::IsExpandable); + if( v.canConvert() && v.value() ) + m_expandState[idx] = Expandable; + } + + return m_expandState[idx] != NotExpandable; +} + +bool ExpandingWidgetModel::isExpanded(const QModelIndex& idx_) const +{ + QModelIndex idx(firstColumn(idx_)); + return m_expandState.contains(idx) && m_expandState[idx] == Expanded; +} + +void ExpandingWidgetModel::setExpanded(QModelIndex idx_, bool expanded) +{ + QModelIndex idx(firstColumn(idx_)); + + //kDebug( 13035 ) << "Setting expand-state of row " << idx.row() << " to " << expanded; + if( !idx.isValid() ) + return; + + if( isExpandable(idx) ) { + if( !expanded && m_expandingWidgets.contains(idx) && m_expandingWidgets[idx] ) { + m_expandingWidgets[idx]->hide(); + } + + m_expandState[idx] = expanded ? Expanded : Expandable; + + if( expanded ) + partiallyUnExpand(idx); + + if( expanded && !m_expandingWidgets.contains(idx) ) + { + QVariant v = data(idx, CodeCompletionModel::ExpandingWidget); + + if( v.canConvert() ) { + m_expandingWidgets[idx] = v.value(); + } else if( v.canConvert() ) { + //Create a html widget that shows the given string + KTextEdit* edit = new KTextEdit( v.value() ); + edit->setReadOnly(true); + edit->resize(200, 50); //Make the widget small so it embeds nicely. + m_expandingWidgets[idx] = edit; + } else { + m_expandingWidgets[idx] = 0; + } + } + + //Eventually partially expand the row + if( !expanded && firstColumn(treeView()->currentIndex()) == idx && !isPartiallyExpanded(idx) ) + rowSelected(idx); //Partially expand the row. + + emit dataChanged(idx, idx); + + if(treeView()) + treeView()->scrollTo(idx); + } +} + +int ExpandingWidgetModel::basicRowHeight( const QModelIndex& idx_ ) const +{ + QModelIndex idx(firstColumn(idx_)); + + ExpandingDelegate* delegate = dynamic_cast( treeView()->itemDelegate(idx) ); + if( !delegate || !idx.isValid() ) { + kDebug( 13035 ) << "ExpandingWidgetModel::basicRowHeight: Could not get delegate"; + return 15; + } + return delegate->basicSizeHint( idx ).height(); +} + + +void ExpandingWidgetModel::placeExpandingWidget(const QModelIndex& idx_) +{ + QModelIndex idx(firstColumn(idx_)); + + QWidget* w = 0; + if( m_expandingWidgets.contains(idx) ) + w = m_expandingWidgets[idx]; + + if( w && isExpanded(idx) ) { + if( !idx.isValid() ) + return; + + QRect rect = treeView()->visualRect(idx); + + if( !rect.isValid() || rect.bottom() < 0 || rect.top() >= treeView()->height() ) { + //The item is currently not visible + w->hide(); + return; + } + + QModelIndex rightMostIndex = idx; + QModelIndex tempIndex = idx; + while( (tempIndex = rightMostIndex.sibling(rightMostIndex.row(), rightMostIndex.column()+1)).isValid() ) + rightMostIndex = tempIndex; + + QRect rightMostRect = treeView()->visualRect(rightMostIndex); + + //Find out the basic height of the row + rect.setLeft( rect.left() + 20 ); + rect.setRight( rightMostRect.right() - 5 ); + + //These offsets must match exactly those used in KateCompletionDeleage::sizeHint() + rect.setTop( rect.top() + basicRowHeight(idx) + 5 ); + rect.setHeight( w->height() ); + + if( w->parent() != treeView()->viewport() || w->geometry() != rect || !w->isVisible() ) { + w->setParent( treeView()->viewport() ); + + w->setGeometry(rect); + w->show(); + } + } +} + +void ExpandingWidgetModel::placeExpandingWidgets() { + for( QMap >::const_iterator it = m_expandingWidgets.constBegin(); it != m_expandingWidgets.constEnd(); ++it ) { + placeExpandingWidget(it.key()); + } +} + +int ExpandingWidgetModel::expandingWidgetsHeight() const +{ + int sum = 0; + for( QMap >::const_iterator it = m_expandingWidgets.constBegin(); it != m_expandingWidgets.constEnd(); ++it ) { + if( isExpanded(it.key() ) && (*it) ) + sum += (*it)->height(); + } + return sum; +} + + +QWidget* ExpandingWidgetModel::expandingWidget(const QModelIndex& idx_) const +{ + QModelIndex idx(firstColumn(idx_)); + + if( m_expandingWidgets.contains(idx) ) + return m_expandingWidgets[idx]; + else + return 0; +} + +void ExpandingWidgetModel::cacheIcons() const { + if( m_expandedIcon.isNull() ) + m_expandedIcon = KIconLoader::global()->loadIcon("arrow-down", KIconLoader::Small, 10); + + if( m_collapsedIcon.isNull() ) + m_collapsedIcon = KIconLoader::global()->loadIcon("arrow-right", KIconLoader::Small, 10); +} + +QList mergeCustomHighlighting( int leftSize, const QList& left, int rightSize, const QList& right ) +{ + QList ret = left; + if( left.isEmpty() ) { + ret << QVariant(0); + ret << QVariant(leftSize); + ret << QTextFormat(QTextFormat::CharFormat); + } + + if( right.isEmpty() ) { + ret << QVariant(leftSize); + ret << QVariant(rightSize); + ret << QTextFormat(QTextFormat::CharFormat); + } else { + QList::const_iterator it = right.constBegin(); + while( it != right.constEnd() ) { + { + QList::const_iterator testIt = it; + for(int a = 0; a < 2; a++) { + ++testIt; + if(testIt == right.constEnd()) { + kWarning() << "Length of input is not multiple of 3"; + break; + } + } + } + + ret << QVariant( (*it).toInt() + leftSize ); + ++it; + ret << QVariant( (*it).toInt() ); + ++it; + ret << *it; + if(!(*it).value().isValid()) + kDebug( 13035 ) << "Text-format is invalid"; + ++it; + } + } + return ret; +} + +//It is assumed that between each two strings, one space is inserted +QList mergeCustomHighlighting( QStringList strings, QList highlights, int grapBetweenStrings ) +{ + if(strings.isEmpty()) { + kWarning() << "List of strings is empty"; + return QList(); + } + + if(highlights.isEmpty()) { + kWarning() << "List of highlightings is empty"; + return QList(); + } + + if(strings.count() != highlights.count()) { + kWarning() << "Length of string-list is " << strings.count() << " while count of highlightings is " << highlights.count() << ", should be same"; + return QList(); + } + + //Merge them together + QString totalString = strings[0]; + QVariantList totalHighlighting = highlights[0]; + + strings.pop_front(); + highlights.pop_front(); + + while( !strings.isEmpty() ) { + totalHighlighting = mergeCustomHighlighting( totalString.length(), totalHighlighting, strings[0].length(), highlights[0] ); + totalString += strings[0]; + + for(int a = 0; a < grapBetweenStrings; a++) + totalString += ' '; + + strings.pop_front(); + highlights.pop_front(); + + } + //Combine the custom-highlightings + return totalHighlighting; +} +#include "moc_expandingwidgetmodel.cpp" + diff --git a/kate/part/completion/expandingtree/expandingwidgetmodel.h b/kate/part/completion/expandingtree/expandingwidgetmodel.h new file mode 100644 index 00000000..4db2f15d --- /dev/null +++ b/kate/part/completion/expandingtree/expandingwidgetmodel.h @@ -0,0 +1,160 @@ +/* This file is part of the KDE libraries and the Kate part. + * + * Copyright (C) 2007 David Nolden + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef EXPANDING_WIDGET_MODEL_H +#define EXPANDING_WIDGET_MODEL_H + +#include +#include +#include +#include + +class KWidget; +#include +#include + +/** + * Cares about expanding/un-expanding items in a tree-view together with ExpandingDelegate + */ +class ExpandingWidgetModel : public QAbstractTableModel { + Q_OBJECT + public: + + ExpandingWidgetModel( QWidget* parent ); + virtual ~ExpandingWidgetModel(); + + enum ExpandingType { + NotExpandable=0, + Expandable, + Expanded + }; + + ///The following three are convenience-functions for the current item that could be replaced by the later ones + ///@return whether the current item can be expanded + bool canExpandCurrentItem() const; + ///@return whether the current item can be collapsed + bool canCollapseCurrentItem() const; + ///Expand/collapse the current item + void setCurrentItemExpanded( bool ); + + void clearMatchQualities(); + + ///Unexpand all rows and clear all cached information about them(this includes deleting the expanding-widgets) + void clearExpanding(); + + ///@return whether the row given through index is expandable + bool isExpandable(const QModelIndex& index) const; + + enum ExpansionType { + NotExpanded = 0, + ExpandDownwards, //The additional(expanded) information is shown UNDER the original information + ExpandUpwards //The additional(expanded) information is shown ABOVE the original information + }; + + ///Returns whether the given index is currently partially expanded. Does not do any other checks like calling models for data. + ExpansionType isPartiallyExpanded(const QModelIndex& index) const; + + ///@return whether row is currently expanded + bool isExpanded(const QModelIndex & row) const; + ///Change the expand-state of the row given through index. The display will be updated. + void setExpanded(QModelIndex index, bool expanded); + + ///Returns the total height added through all open expanding-widgets + int expandingWidgetsHeight() const; + + ///@return the expanding-widget for the given row, if available. Expanding-widgets are in best case available for all expanded rows. + ///This does not return the partially-expand widget. + QWidget* expandingWidget(const QModelIndex & row) const; + + ///Amount by which the height of a row increases when it is partially expanded + int partiallyExpandWidgetHeight() const; + /** + * Notifies underlying models that the item was selected, collapses any previous partially expanded line, + * checks whether this line should be partially expanded, and eventually does it. + * Does nothing when nothing needs to be done. + * Does NOT show the expanding-widget. That is done immediately when painting by ExpandingDelegate, + * to reduce flickering. @see showPartialExpandWidget() + * @param row The row + * */ + /// + virtual void rowSelected(const QModelIndex & row); + + ///Returns the rectangle for the partially expanded part of the given row + QRect partialExpandRect(const QModelIndex & row) const; + + QString partialExpandText(const QModelIndex & row) const; + + ///Places and shows the expanding-widget for the given row, if it should be visible and is valid. + ///Also shows the partial-expanding-widget when it should be visible. + void placeExpandingWidget(const QModelIndex & row); + + virtual QTreeView* treeView() const = 0; + + ///Should return true if the given row should be painted like a contained item(as opposed to label-rows etc.) + virtual bool indexIsItem(const QModelIndex& index) const = 0; + + ///Does not request data from index, this only returns local data like highlighting for expanded rows and similar + virtual QVariant data ( const QModelIndex & index, int role = Qt::DisplayRole ) const; + + ///Returns the first row that is currently partially expanded. + QModelIndex partiallyExpandedRow() const; + + ///Returns the match-color for the given index, or zero if match-quality could not be computed. + uint matchColor(const QModelIndex& index) const; + + public slots: + ///Place or hides all expanding-widgets to the correct positions. Should be called after the view was scrolled. + void placeExpandingWidgets(); + + protected: + /** + * @return the context-match quality from 0 to 10 if it could be determined, else -1 + * */ + virtual int contextMatchQuality(const QModelIndex & index) const = 0; + + //Makes sure m_expandedIcon and m_collapsedIcon are loaded + void cacheIcons() const; + + static QIcon m_expandedIcon; + static QIcon m_collapsedIcon; + + //Does not update the view + void partiallyUnExpand(const QModelIndex& index); + //Finds out the basic height of the row represented by the given index. Basic means without respecting any expansion. + int basicRowHeight( const QModelIndex& index ) const; + + private: + QMap m_partiallyExpanded; + // Store expanding-widgets and cache whether items can be expanded + mutable QMap m_expandState; + QMap< QModelIndex, QPointer > m_expandingWidgets; //Map rows to their expanding-widgets + QMap< QModelIndex, int > m_contextMatchQualities; //Map rows to their context-match qualities(undefined if unknown, else 0 to 10). Not used yet, eventually remove. +}; + + +/** + * Helper-function to merge custom-highlighting variant-lists. + * + * @param strings A list of strings that should be merged + * @param highlights One variant-list for highlighting, as described in the kde header ktextedtor/codecompletionmodel.h + * @param gapBetweenStrings How many signs are inserted between 2 strings? + * */ +QList mergeCustomHighlighting( QStringList strings, QList highlights, int gapBetweenStrings = 0 ); +#endif diff --git a/kate/part/completion/kateargumenthintmodel.cpp b/kate/part/completion/kateargumenthintmodel.cpp new file mode 100644 index 00000000..22e3dcf8 --- /dev/null +++ b/kate/part/completion/kateargumenthintmodel.cpp @@ -0,0 +1,306 @@ +/* This file is part of the KDE libraries and the Kate part. + * + * Copyright (C) 2007 David Nolden + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "kateargumenthintmodel.h" + +#include +#include +#include + +#include +#include "katecompletionwidget.h" +#include "kateargumenthinttree.h" +#include "katecompletiontree.h" + +using namespace KTextEditor; + +void KateArgumentHintModel::clear() { + m_rows.clear(); + clearExpanding(); +} + +QModelIndex KateArgumentHintModel::mapToSource( const QModelIndex & index ) const { + if( index.row() < 0 || index.row() >= m_rows.count() ) + return QModelIndex(); + + if( m_rows[index.row()] < 0 || m_rows[index.row()] >= group()->filtered.count() ) + return QModelIndex(); + + KateCompletionModel::ModelRow source = group()->filtered[m_rows[index.row()]].sourceRow(); + if( !source.first ) { + kDebug( 13035 ) << "KateArgumentHintModel::data: Row does not exist in source"; + return QModelIndex(); + } + + QModelIndex sourceIndex = source.second.sibling(source.second.row(), index.column()); + + return sourceIndex; +} + +void KateArgumentHintModel::parentModelReset() { + clear(); + buildRows(); +} + +void KateArgumentHintModel::buildRows() { + m_rows.clear(); + QMap > m_depths; //Map each hint-depth to a list of functions of that depth + for( int a = 0; a < group()->filtered.count(); a++ ) { + KateCompletionModel::ModelRow source = group()->filtered[a].sourceRow(); + QModelIndex sourceIndex = source.second.sibling(source.second.row(), 0); + QVariant v = sourceIndex.data(CodeCompletionModel::ArgumentHintDepth); + if( v.type() == QVariant::Int ) { + QList& lst( m_depths[v.toInt()] ); + lst << a; + } + } + + for( QMap >::const_iterator it = m_depths.constBegin(); it != m_depths.constEnd(); ++it ) { + foreach( int row, *it ) + m_rows.push_front(row);//Insert filtered in reversed order + m_rows.push_front( -it.key() ); + } + + reset(); + emit contentStateChanged(!m_rows.isEmpty()); +} + +KateArgumentHintModel::KateArgumentHintModel( KateCompletionWidget* parent ) : ExpandingWidgetModel(parent), m_parent(parent) { + connect(parent->model(), SIGNAL(modelReset()), this, SLOT(parentModelReset())); + connect(parent->model(), SIGNAL(argumentHintsChanged()), this, SLOT(parentModelReset())); +} + +QVariant KateArgumentHintModel::data ( const QModelIndex & index, int role ) const { + if( index.row() < 0 || index.row() >= m_rows.count() ) { + //kDebug( 13035 ) << "KateArgumentHintModel::data: index out of bound: " << index.row() << " total filtered: " << m_rows.count(); + return QVariant(); + } + + if( m_rows[index.row()] < 0 ) { + //Show labels + if( role == Qt::DisplayRole && index.column() == 0 ) { + return QString(); //QString("Depth %1").arg(-m_rows[index.row()]); + } else if( role == Qt::BackgroundRole ) { + return KApplication::kApplication()->palette().toolTipBase().color(); + }else if( role == Qt::ForegroundRole ) { + return KApplication::kApplication()->palette().toolTipText().color(); + }else{ + return QVariant(); + } + } + + if( m_rows[index.row()] < 0 || m_rows[index.row()] >= group()->filtered.count() ) { + kDebug( 13035 ) << "KateArgumentHintModel::data: index out of bound: " << m_rows[index.row()] << " total filtered: " << group()->filtered.count(); + return QVariant(); + } + + KateCompletionModel::ModelRow source = group()->filtered[m_rows[index.row()]].sourceRow(); + if( !source.first ) { + kDebug( 13035 ) << "KateArgumentHintModel::data: Row does not exist in source"; + return QVariant(); + } + + if( index.column() == 0 ) { + switch( role ) { + case Qt::DecorationRole: + { + //Show the expand-handle + model()->cacheIcons(); + + if( !isExpanded(index ) ) + return QVariant( model()->m_collapsedIcon ); + else + return QVariant( model()->m_expandedIcon ); + } + case Qt::DisplayRole: + //Ignore text in the first column(we create our own compound text in the second) + return QVariant(); + } + } + + QModelIndex sourceIndex = source.second.sibling(source.second.row(), index.column()); + + if( !sourceIndex.isValid() ) { + kDebug( 13035 ) << "KateArgumentHintModel::data: Source-index is not valid"; + return QVariant(); + } + + switch( role ) { + case Qt::DisplayRole: + { + //Construct the text + QString totalText; + for( int a = CodeCompletionModel::Prefix; a <= CodeCompletionModel::Postfix; a++ ) + if( a != CodeCompletionModel::Scope ) //Skip the scope + totalText += source.second.sibling(source.second.row(), a).data(Qt::DisplayRole).toString() + ' '; + + + return QVariant(totalText); + } + case CodeCompletionModel::HighlightingMethod: + { + //Return that we are doing custom-highlighting of one of the sub-strings does it + for( int a = CodeCompletionModel::Prefix; a <= CodeCompletionModel::Postfix; a++ ) { + QVariant method = source.second.sibling(source.second.row(), a).data(CodeCompletionModel::HighlightingMethod); + if( method.type() == QVariant::Int && method.toInt() == CodeCompletionModel::CustomHighlighting) + return QVariant(CodeCompletionModel::CustomHighlighting); + } + + return QVariant(); + } + case CodeCompletionModel::CustomHighlight: + { + QStringList strings; + + //Collect strings + for( int a = CodeCompletionModel::Prefix; a <= CodeCompletionModel::Postfix; a++ ) + strings << source.second.sibling(source.second.row(), a).data(Qt::DisplayRole).toString(); + + QList highlights; + + //Collect custom-highlightings + for( int a = CodeCompletionModel::Prefix; a <= CodeCompletionModel::Postfix; a++ ) + highlights << source.second.sibling(source.second.row(), a).data(CodeCompletionModel::CustomHighlight).toList(); + + //Replace invalid QTextFormats with match-quality color or yellow. + for( QList::iterator it = highlights.begin(); it != highlights.end(); ++it ) + { + QVariantList& list( *it ); + + for( int a = 2; a < list.count(); a += 3 ) + { + if( list[a].canConvert() ) + { + QTextFormat f = list[a].value(); + + if(!f.isValid()) + { + f = QTextFormat( QTextFormat::CharFormat ); + uint color = matchColor( index ); + + if( color ) + f.setBackground( QBrush(color) ); + else + f.setBackground( Qt::yellow ); + + list[a] = QVariant( f ); + } + } + } + } + + + return mergeCustomHighlighting( strings, highlights, 1 ); + } + case Qt::DecorationRole: + { + //Redirect the decoration to the decoration of the item-column + return source.second.sibling(source.second.row(), CodeCompletionModel::Icon).data(role); + } + } + + QVariant v = ExpandingWidgetModel::data( index, role ); + if( v.isValid() ) + return v; + else + return sourceIndex.data( role ); +} + +int KateArgumentHintModel::rowCount ( const QModelIndex & parent ) const { + if( !parent.isValid() ) + return m_rows.count(); + else + return 0; +} + +int KateArgumentHintModel::columnCount ( const QModelIndex & /*parent*/ ) const { + return 2; //2 Columns, one for the expand-handle, one for the signature +} + +KateCompletionModel::Group* KateArgumentHintModel::group() const { + return model()->m_argumentHints; +} + +KateCompletionModel* KateArgumentHintModel::model() const { + return m_parent->model(); +} + +QTreeView* KateArgumentHintModel::treeView() const { + return m_parent->argumentHintTree(); +} + +void KateArgumentHintModel::emitDataChanged( const QModelIndex& start, const QModelIndex& end ) { + emit dataChanged(start, end); +} + +bool KateArgumentHintModel::indexIsItem(const QModelIndex& index) const { + return index.row() >= 0 && index.row() < m_rows.count() && m_rows[index.row()] >= 0; +} + +int KateArgumentHintModel::contextMatchQuality(const QModelIndex& index) const { + int row=index.row(); + if( row < 0 || row >= m_rows.count() ) + return -1; + + if( m_rows[row] < 0 || m_rows[row] >= group()->filtered.count() ) + return -1; //Probably a label + + KateCompletionModel::ModelRow source = group()->filtered[m_rows[row]].sourceRow(); + if( !source.first ) + return -1; + + QModelIndex sourceIndex = source.second.sibling(source.second.row(), 0); + + if( !sourceIndex.isValid() ) + return -1; + + int depth = sourceIndex.data(CodeCompletionModel::ArgumentHintDepth).toInt(); + + switch(depth) { + case 1: + { + //This argument-hint is on the lowest level, match it with the currently selected item in the completion-widget + QModelIndex row = m_parent->treeView()->currentIndex(); + if( !row.isValid() ) + return -1; + + QModelIndex selectedIndex = m_parent->model()->mapToSource( row ); + if( !selectedIndex.isValid() ) + return -1; + + if( selectedIndex.model() != sourceIndex.model() ) + return -1; //We can only match items from the same source-model + + sourceIndex.data( CodeCompletionModel::SetMatchContext ); + + QVariant v = selectedIndex.data( CodeCompletionModel::MatchQuality ); + if( v.type() == QVariant::Int ) + return v.toInt(); + } + break; + default: + //Do some other nice matching here in future + break; + } + + return -1; +} + +#include "moc_kateargumenthintmodel.cpp" diff --git a/kate/part/completion/kateargumenthintmodel.h b/kate/part/completion/kateargumenthintmodel.h new file mode 100644 index 00000000..ebf60578 --- /dev/null +++ b/kate/part/completion/kateargumenthintmodel.h @@ -0,0 +1,67 @@ +/* This file is part of the KDE libraries and the Kate part. + * + * Copyright (C) 2007 David Nolden + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef KATEARGUMENTHINTMODEL_H +#define KATEARGUMENTHINTMODEL_H + +#include +#include "katecompletionmodel.h" +#include "expandingtree/expandingwidgetmodel.h" + +class KateCompletionWidget; + +class KateArgumentHintModel : public ExpandingWidgetModel { + Q_OBJECT + public: + KateArgumentHintModel( KateCompletionWidget* parent ); + + virtual QVariant data ( const QModelIndex & index, int role = Qt::DisplayRole ) const; + + virtual int rowCount ( const QModelIndex & parent ) const; + + virtual int columnCount ( const QModelIndex & /*parent*/ ) const; + + virtual QTreeView* treeView() const; + + virtual bool indexIsItem(const QModelIndex& index) const; + + void emitDataChanged( const QModelIndex& start, const QModelIndex& end ); + + //Returns the index in the source-model for an index within this model + QModelIndex mapToSource( const QModelIndex & proxyIndex ) const; + + void buildRows(); + void clear(); + protected: + virtual int contextMatchQuality(const QModelIndex& row) const; + public slots: + void parentModelReset(); + signals: + void contentStateChanged(bool hasContent); + private: + KateCompletionModel::Group* group() const; + KateCompletionModel* model() const; + + QList m_rows; //Maps rows to either a positive row-number in the source group, or to a negative number which indicates a label + + KateCompletionWidget* m_parent; +}; + +#endif diff --git a/kate/part/completion/kateargumenthinttree.cpp b/kate/part/completion/kateargumenthinttree.cpp new file mode 100644 index 00000000..0fa788f8 --- /dev/null +++ b/kate/part/completion/kateargumenthinttree.cpp @@ -0,0 +1,295 @@ +/* This file is part of the KDE libraries and the Kate part. + * + * Copyright (C) 2007 David Nolden + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "kateargumenthinttree.h" + +#include +#include +#include +#include + +#include "kateargumenthintmodel.h" +#include "katecompletionwidget.h" +#include "expandingtree/expandingwidgetmodel.h" +#include "katecompletiondelegate.h" +#include "kateview.h" +#include + + +KateArgumentHintTree::KateArgumentHintTree( KateCompletionWidget* parent ) : ExpandingTree(0), m_parent(parent) { //Do not use the completion-widget as widget-parent, because the argument-hint-tree will be rendered separately + + setFrameStyle( QFrame::Box | QFrame::Plain ); + setLineWidth( 1 ); + + connect( parent, SIGNAL(destroyed(QObject*)), this, SLOT(deleteLater()) ); + setFrameStyle(QFrame::NoFrame); + setFrameStyle( QFrame::Box | QFrame::Plain ); + setFocusPolicy(Qt::NoFocus); + setWindowFlags(Qt::Tool | Qt::FramelessWindowHint); + setUniformRowHeights(false); + setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); + header()->hide(); + setRootIsDecorated(false); + setIndentation(0); + setAllColumnsShowFocus(true); + setAlternatingRowColors(true); + setItemDelegate(new KateCompletionDelegate(parent->argumentHintModel(), parent)); +} + +void KateArgumentHintTree::clearCompletion() { + setCurrentIndex(QModelIndex()); +} + +KateArgumentHintModel* KateArgumentHintTree::model() const { + return m_parent->argumentHintModel(); +} + +void KateArgumentHintTree::paintEvent ( QPaintEvent * event ) { + QTreeView::paintEvent(event); + updateGeometry(); ///@todo delay this. It is needed here, because visualRect(...) returns an invalid rect in updateGeometry before the content is painted +} + +void KateArgumentHintTree::dataChanged ( const QModelIndex & topLeft, const QModelIndex & bottomRight ) { + QTreeView::dataChanged(topLeft,bottomRight); + //updateGeometry(); +} + +void KateArgumentHintTree::currentChanged ( const QModelIndex & current, const QModelIndex & previous ) { +/* kDebug( 13035 ) << "currentChanged()";*/ + static_cast(model())->rowSelected(current); + QTreeView::currentChanged(current, previous); +} + +void KateArgumentHintTree::rowsInserted ( const QModelIndex & parent, int start, int end ) { + QTreeView::rowsInserted(parent, start, end); + updateGeometry(); +} + +int KateArgumentHintTree::sizeHintForColumn(int column) const { + return QTreeView::sizeHintForColumn(column); +} + +unsigned int KateArgumentHintTree::rowHeight(const QModelIndex& index) const { + uint max = sizeHintForIndex(index).height(); + + for(int a = 0; a < index.model()->columnCount(index.parent()); ++a) { + QModelIndex i = index.sibling(index.row(), a); + uint cSize = sizeHintForIndex(i).height(); + if(cSize > max) + max = cSize; + } + return max; +} + +void KateArgumentHintTree::updateGeometry(QRect geom) { + //Avoid recursive calls of updateGeometry + static bool updatingGeometry = false; + if( updatingGeometry ) return; + updatingGeometry = true; + + if( model()->rowCount(QModelIndex()) == 0 ) { +/* kDebug( 13035 ) << "KateArgumentHintTree:: empty model";*/ + hide(); + setGeometry(geom); + updatingGeometry = false; + return; + } + + int bottom = geom.bottom(); + int totalWidth = resizeColumns(); + int totalHeight = 0; + for(int a = 0; a < model()->rowCount( QModelIndex() ); ++a) { + QModelIndex index(model()->index(a, 0)); + totalHeight += rowHeight(index); + for(int b = 0; b < model()->rowCount(index); ++b) { + QModelIndex childIndex = index.child(b, 0); + totalHeight += rowHeight(childIndex); + } + } + + totalHeight += frameWidth()*2; + + geom.setHeight(totalHeight); + + geom.moveBottom(bottom); +// if( totalWidth > geom.width() ) + geom.setWidth(totalWidth); + + bool enableScrollBars = false; + + //Resize and move so it fits the screen horizontally + int maxWidth = (QApplication::desktop()->screenGeometry(m_parent->view()).width()*3)/4; + if( geom.width() > maxWidth ) { + geom.setWidth(maxWidth); + geom.setHeight(geom.height() + horizontalScrollBar()->height() +2); + geom.moveBottom(bottom); + enableScrollBars = true; + } + + if (geom.right() > QApplication::desktop()->screenGeometry(m_parent->view()).right()) + geom.moveRight( QApplication::desktop()->screenGeometry(m_parent->view()).right() ); + + if( geom.left() < QApplication::desktop()->screenGeometry(m_parent->view()).left() ) + geom.moveLeft(QApplication::desktop()->screenGeometry(m_parent->view()).left()); + + //Resize and move so it fits the screen vertically + bool resized = false; + if( geom.top() < QApplication::desktop()->screenGeometry(this).top() ) { + int offset = QApplication::desktop()->screenGeometry(this).top() - geom.top(); + geom.setBottom( geom.bottom() - offset ); + geom.moveTo(geom.left(), QApplication::desktop()->screenGeometry(this).top()); + resized = true; + } + + if(geom != geometry()) + { + setUpdatesEnabled(false); + setAnimated(false); + + setHorizontalScrollBarPolicy( enableScrollBars ? Qt::ScrollBarAlwaysOn : Qt::ScrollBarAlwaysOff ); + + /* kDebug( 13035 ) << "KateArgumentHintTree::updateGeometry: updating geometry to " << geom;*/ + setGeometry(geom); + + if( resized && currentIndex().isValid() ) + scrollTo(currentIndex()); + + setUpdatesEnabled(true); + } + + updatingGeometry = false; +} + +int KateArgumentHintTree::resizeColumns() { + int totalSize = 0; + for( int a = 0; a < header()->count(); a++ ) { + int columnSize = sizeHintForColumn(a); + setColumnWidth(a, columnSize); + totalSize += columnSize; + } + return totalSize; +} + +void KateArgumentHintTree::updateGeometry() { + updateGeometry( geometry() ); +} + +bool KateArgumentHintTree::nextCompletion() +{ + QModelIndex current; + QModelIndex firstCurrent = currentIndex(); + + do { + QModelIndex oldCurrent = currentIndex(); + + current = moveCursor(MoveDown, Qt::NoModifier); + + if (current != oldCurrent && current.isValid()) { + setCurrentIndex(current); + + } else { + if (firstCurrent.isValid()) + setCurrentIndex(firstCurrent); + return false; + } + + } while (!model()->indexIsItem(current)); + + return true; +} + +bool KateArgumentHintTree::previousCompletion() +{ + QModelIndex current; + QModelIndex firstCurrent = currentIndex(); + + do { + QModelIndex oldCurrent = currentIndex(); + + current = moveCursor(MoveUp, Qt::NoModifier); + + if (current != oldCurrent && current.isValid()) { + setCurrentIndex(current); + + } else { + if (firstCurrent.isValid()) + setCurrentIndex(firstCurrent); + return false; + } + + } while (!model()->indexIsItem(current)); + + return true; +} + +bool KateArgumentHintTree::pageDown( ) +{ + QModelIndex old = currentIndex(); + QModelIndex current = moveCursor(MovePageDown, Qt::NoModifier); + + if (current.isValid()) { + setCurrentIndex(current); + if (!model()->indexIsItem(current)) + if (!nextCompletion()) + previousCompletion(); + } + + return current != old; +} + +bool KateArgumentHintTree::pageUp( ) +{ + QModelIndex old = currentIndex(); + QModelIndex current = moveCursor(MovePageUp, Qt::NoModifier); + + if (current.isValid()) { + setCurrentIndex(current); + if (!model()->indexIsItem(current)) + if (!previousCompletion()) + nextCompletion(); + } + return current != old; +} + +void KateArgumentHintTree::top( ) +{ + QModelIndex current = moveCursor(MoveHome, Qt::NoModifier); + setCurrentIndex(current); + + if (current.isValid()) { + setCurrentIndex(current); + if (!model()->indexIsItem(current)) + nextCompletion(); + } +} + +void KateArgumentHintTree::bottom( ) +{ + QModelIndex current = moveCursor(MoveEnd, Qt::NoModifier); + setCurrentIndex(current); + + if (current.isValid()) { + setCurrentIndex(current); + if (!model()->indexIsItem(current)) + previousCompletion(); + } +} + +#include "moc_kateargumenthinttree.cpp" diff --git a/kate/part/completion/kateargumenthinttree.h b/kate/part/completion/kateargumenthinttree.h new file mode 100644 index 00000000..139ebcc8 --- /dev/null +++ b/kate/part/completion/kateargumenthinttree.h @@ -0,0 +1,63 @@ +/* This file is part of the KDE libraries and the Kate part. + * + * Copyright (C) 2007 David Nolden + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef KATEARGUMENTHINTTREE_H +#define KATEARGUMENTHINTTREE_H + +#include "expandingtree/expandingtree.h" + +class KateCompletionWidget; +class KateArgumentHintModel; +#include + +class KateArgumentHintTree : public ExpandingTree { + Q_OBJECT + public: + KateArgumentHintTree( KateCompletionWidget* parent ); + + // Navigation + bool nextCompletion(); + bool previousCompletion(); + bool pageDown(); + bool pageUp(); + void top(); + void bottom(); + + //Returns the total size of all columns + int resizeColumns(); + + void clearCompletion(); + public slots: + void updateGeometry(); + void updateGeometry(QRect rect); + protected: + virtual void paintEvent ( QPaintEvent * event ); + virtual void rowsInserted ( const QModelIndex & parent, int start, int end ); + virtual void dataChanged ( const QModelIndex & topLeft, const QModelIndex & bottomRight ); + virtual void currentChanged ( const QModelIndex & current, const QModelIndex & previous ); + private: + uint rowHeight(const QModelIndex& index) const; + KateArgumentHintModel* model() const; + virtual int sizeHintForColumn ( int column ) const; + + KateCompletionWidget* m_parent; +}; + +#endif diff --git a/kate/part/completion/katecompletionconfig.cpp b/kate/part/completion/katecompletionconfig.cpp new file mode 100644 index 00000000..9ffcfc57 --- /dev/null +++ b/kate/part/completion/katecompletionconfig.cpp @@ -0,0 +1,411 @@ +/* This file is part of the KDE libraries and the Kate part. + * + * Copyright (C) 2006 Hamish Rodda + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "katecompletionconfig.h" + +#include + +#include +#include +#include + +#include "katecompletionmodel.h" + +#include "ui_completionconfigwidget.h" + +using namespace KTextEditor; + +KateCompletionConfig::KateCompletionConfig(KateCompletionModel* model, QWidget* parent) + : KDialog(parent) + , ui(new Ui::CompletionConfigWidget()) + , m_model(model) +{ + //setAttribute(Qt::WA_DestructiveClose); + setCaption(i18n("Code Completion Configuration")); + setButtons(KDialog::Ok | KDialog::Cancel); + setDefaultButton(KDialog::Ok); + connect(this, SIGNAL(okClicked()), SLOT(apply())); + + QWidget* mw = new QWidget(this); + ui->setupUi(mw); + setMainWidget(mw); + + // Sorting + ui->sorting->setChecked(m_model->isSortingEnabled()); + ui->sortingAlphabetical->setChecked(m_model->isSortingAlphabetical()); + ui->sortingCaseSensitive->setChecked(m_model->sortingCaseSensitivity() == Qt::CaseSensitive); + ui->groupingOrderUp->setIcon(KIcon("go-up")); + ui->groupingOrderDown->setIcon(KIcon("go-down")); + connect(ui->groupingOrderUp, SIGNAL(pressed()), SLOT(moveGroupingOrderUp())); + connect(ui->groupingOrderDown, SIGNAL(pressed()), SLOT(moveGroupingOrderDown())); + + // Filtering + ui->filtering->setChecked(m_model->isFilteringEnabled()); + ui->filteringContextMatchOnly->setChecked(m_model->filterContextMatchesOnly()); + ui->filteringHideAttributes->setChecked(m_model->filterByAttribute()); + + for (CodeCompletionModel::CompletionProperty i = CodeCompletionModel::FirstProperty; i <= CodeCompletionModel::LastProperty; i = static_cast(i << 1)) { + QListWidgetItem* item = new QListWidgetItem(m_model->propertyName(i), ui->filteringAttributesList, i); + item->setCheckState((m_model->filterAttributes() & i) ? Qt::Checked : Qt::Unchecked); + } + + ui->filteringMaximumInheritanceDepth->setValue(m_model->maximumInheritanceDepth()); + + // Grouping + ui->grouping->setChecked(m_model->isGroupingEnabled()); + ui->groupingUp->setIcon(KIcon("go-up")); + ui->groupingDown->setIcon(KIcon("go-down")); + + m_groupingScopeType = ui->groupingMethods->topLevelItem(0); + m_groupingScopeType->setCheckState(0, (m_model->groupingMethod() & KateCompletionModel::ScopeType) ? Qt::Checked : Qt::Unchecked); + + m_groupingScope = ui->groupingMethods->topLevelItem(1); + m_groupingScope->setCheckState(0, (m_model->groupingMethod() & KateCompletionModel::Scope) ? Qt::Checked : Qt::Unchecked); + + m_groupingAccessType = ui->groupingMethods->topLevelItem(2); + m_groupingAccessType->setCheckState(0, (m_model->groupingMethod() & KateCompletionModel::AccessType) ? Qt::Checked : Qt::Unchecked); + + m_groupingItemType = ui->groupingMethods->topLevelItem(3); + m_groupingItemType->setCheckState(0, (m_model->groupingMethod() & KateCompletionModel::ItemType) ? Qt::Checked : Qt::Unchecked); + + ui->accessConst->setChecked(m_model->accessIncludeConst()); + ui->accessStatic->setChecked(m_model->accessIncludeStatic()); + ui->accessSignalSlot->setChecked(m_model->accessIncludeSignalSlot()); + + for (int i = 0; i < 4; ++i) + ui->groupingMethods->topLevelItem(i)->setCheckState(0, Qt::Unchecked); + connect(ui->groupingUp, SIGNAL(pressed()), SLOT(moveGroupingUp())); + connect(ui->groupingDown, SIGNAL(pressed()), SLOT(moveGroupingDown())); + + // Column merging + ui->columnMerging->setChecked(m_model->isColumnMergingEnabled()); + ui->columnUp->setIcon(KIcon("go-up")); + ui->columnDown->setIcon(KIcon("go-down")); + connect(ui->columnUp, SIGNAL(pressed()), SLOT(moveColumnUp())); + connect(ui->columnDown, SIGNAL(pressed()), SLOT(moveColumnDown())); + + + QList mergedColumns; + if (!m_model->columnMerges().isEmpty()) { + foreach (const QList& list, m_model->columnMerges()) { + bool first = true; + foreach (int column, list) { + QTreeWidgetItem* item = new QTreeWidgetItem(ui->columnMergeTree, column); + item->setText(0, KateCompletionModel::columnName(column) + QString(" %1").arg(column)); + item->setCheckState(1, first ? Qt::Unchecked : Qt::Checked); + + if (column == KTextEditor::CodeCompletionModel::Name) + item->setText(2, i18n("Always")); + else + item->setCheckState(2, Qt::Checked); + + first = false; + mergedColumns << column; + } + } + + for (int column = 0; column < KTextEditor::CodeCompletionModel::ColumnCount; ++column) { + if (!mergedColumns.contains(column)) { + QTreeWidgetItem* item = new QTreeWidgetItem(ui->columnMergeTree, column); + item->setText(0, KateCompletionModel::columnName(column) + QString(" %1").arg(column)); + item->setCheckState(1, Qt::Unchecked); + + Q_ASSERT(column != KTextEditor::CodeCompletionModel::Name); + + item->setCheckState(2, Qt::Unchecked); + } + } + + } else { + for (int column = 0; column < KTextEditor::CodeCompletionModel::ColumnCount; ++column) { + QTreeWidgetItem* item = new QTreeWidgetItem(ui->columnMergeTree, column); + item->setText(0, KateCompletionModel::columnName(column) + QString(" %1").arg(column)); + item->setCheckState(1, Qt::Unchecked); + + if (column == KTextEditor::CodeCompletionModel::Name) + item->setText(2, i18n("Always")); + else + item->setCheckState(2, Qt::Checked); + } + } + + // init with defaults from config or really hardcoded ones + KConfigGroup config( KGlobal::config(), "Kate Code Completion Defaults"); + readConfig (config); +} + +KateCompletionConfig::~ KateCompletionConfig( ) +{ + delete ui; +} + +void KateCompletionConfig::readConfig(const KConfigGroup &config) +{ + configStart (); + + // Sorting + ui->sorting->setChecked(config.readEntry("Sorting Enabled", true)); + ui->sortingAlphabetical->setChecked(config.readEntry("Sort Alphabetically", true)); + ui->sortingCaseSensitive->setChecked(config.readEntry("Case Sensitive Sort", false)); + ui->sortingInheritanceDepth->setChecked(config.readEntry("Sort by Inheritance Depth", true)); + + // Filtering + ui->filtering->setChecked(config.readEntry("Filtering Enabled", false)); + ui->filteringContextMatchOnly->setChecked(config.readEntry("Filter by Context Match Only", false)); + ui->filteringHideAttributes->setChecked(config.readEntry("Hide Completions by Attribute", false)); + + int attributes = config.readEntry("Filter Attribute Mask", 0); + for (int i = 0; i < ui->filteringAttributesList->count(); ++i) { + QListWidgetItem* item = ui->filteringAttributesList->item(i); + item->setCheckState(((1 << (i - 1)) & attributes) ? Qt::Checked : Qt::Unchecked); + } + + ui->filteringMaximumInheritanceDepth->setValue(config.readEntry("Filter by Maximum Inheritance Depth", 0)); + + // Grouping + ui->grouping->setChecked(config.readEntry("Grouping Enabled", true)); + + m_groupingScopeType->setCheckState(0, config.readEntry("Group by Scope Type", true) ? Qt::Checked : Qt::Unchecked); + m_groupingScope->setCheckState(0, config.readEntry("Group by Scope", false) ? Qt::Checked : Qt::Unchecked); + m_groupingAccessType->setCheckState(0, config.readEntry("Group by Access Type", true) ? Qt::Checked : Qt::Unchecked); + m_groupingItemType->setCheckState(0, config.readEntry("Group by Item Type", false) ? Qt::Checked : Qt::Unchecked); + + ui->accessConst->setChecked(config.readEntry("Group by Const", false)); + ui->accessStatic->setChecked(config.readEntry("Group by Static", false)); + ui->accessSignalSlot->setChecked(config.readEntry("Group by Signals and Slots", false)); + + // Column merging + ui->columnMerging->setChecked(config.readEntry("Column Merging Enabled", true)); + + for (int i = 0; i < ui->columnMergeTree->topLevelItemCount(); ++i) { + QTreeWidgetItem* item = ui->columnMergeTree->topLevelItem(i); + ///Initialize a standard column-merging: Merge Scope, Name, Arguments and Postfix + item->setCheckState(1, config.readEntry(QString("Column %1 Merge").arg(i), (i == CodeCompletionModel::Scope || i == CodeCompletionModel::Name || i == CodeCompletionModel::Arguments)) ? Qt::Checked : Qt::Unchecked); + item->setCheckState(2, config.readEntry(QString("Column %1 Show").arg(i), true) ? Qt::Checked : Qt::Unchecked); + } + + applyInternal(); + + configEnd(); +} + +void KateCompletionConfig::writeConfig(KConfigGroup &config) +{ + // Sorting + config.writeEntry("Sorting Enabled", ui->sorting->isChecked()); + config.writeEntry("Sort Alphabetically", ui->sortingAlphabetical->isChecked()); + config.writeEntry("Case Sensitive Sort", ui->sortingCaseSensitive->isChecked()); + config.writeEntry("Sort by Inheritance Depth", ui->sortingInheritanceDepth->isChecked()); + + // Filtering + config.writeEntry("Filtering Enabled", ui->filtering->isChecked()); + config.writeEntry("Filter by Context Match Only", ui->filteringContextMatchOnly->isChecked()); + config.writeEntry("Hide Completions by Attribute", ui->filteringHideAttributes->isChecked()); + + int attributes = 0; + for (int i = 0; i < ui->filteringAttributesList->count(); ++i) { + QListWidgetItem* item = ui->filteringAttributesList->item(i); + if (item->checkState() == Qt::Checked) + attributes |= 1 << (i - 1); + } + config.writeEntry("Filter Attribute Mask", attributes); + + config.writeEntry("Filter by Maximum Inheritance Depth", ui->filteringMaximumInheritanceDepth->value()); + + // Grouping + config.writeEntry("Grouping Enabled", ui->grouping->isChecked()); + + config.writeEntry("Group by Scope Type", m_groupingScopeType->checkState(0) == Qt::Checked ? true : false); + config.writeEntry("Group by Scope", m_groupingScope->checkState(0) == Qt::Checked ? true : false); + config.writeEntry("Group by Access Type", m_groupingAccessType->checkState(0) == Qt::Checked ? true : false); + config.writeEntry("Group by Item Type", m_groupingItemType->checkState(0) == Qt::Checked ? true : false); + + config.writeEntry("Group by Const", ui->accessConst->isChecked()); + config.writeEntry("Group by Static", ui->accessStatic->isChecked()); + config.writeEntry("Group by Signals and Slots", ui->accessSignalSlot->isChecked()); + + // Column merging + config.writeEntry("Column Merging Enabled", ui->columnMerging->isChecked()); + + for (int i = 0; i < ui->columnMergeTree->topLevelItemCount(); ++i) { + QTreeWidgetItem* item = ui->columnMergeTree->topLevelItem(i); + config.writeEntry(QString("Column %1 Merge").arg(i), item->checkState(1) == Qt::Checked ? true : false); + config.writeEntry(QString("Column %1 Show").arg(i), item->checkState(2) == Qt::Checked ? true : false); + } + + config.sync(); +} + +void KateCompletionConfig::updateConfig() +{ + // Ah, nothing to do, I think...? +} + +void KateCompletionConfig::moveColumnUp( ) +{ + QTreeWidgetItem* item = ui->columnMergeTree->currentItem(); + if (item) { + int index = ui->columnMergeTree->indexOfTopLevelItem(item); + if (index > 0) { + ui->columnMergeTree->takeTopLevelItem(index); + ui->columnMergeTree->insertTopLevelItem(index - 1, item); + ui->columnMergeTree->setCurrentItem(item); + } + } +} + +void KateCompletionConfig::moveColumnDown( ) +{ + QTreeWidgetItem* item = ui->columnMergeTree->currentItem(); + if (item) { + int index = ui->columnMergeTree->indexOfTopLevelItem(item); + if (index < ui->columnMergeTree->topLevelItemCount() - 1) { + ui->columnMergeTree->takeTopLevelItem(index); + ui->columnMergeTree->insertTopLevelItem(index + 1, item); + ui->columnMergeTree->setCurrentItem(item); + } + } +} + +void KateCompletionConfig::apply( ) +{ + applyInternal(); + + KConfigGroup config( KGlobal::config(), "Kate Code Completion Defaults"); + writeConfig (config); +} + +void KateCompletionConfig::applyInternal() +{ + // Sorting + m_model->setSortingEnabled(ui->sorting->isChecked()); + m_model->setSortingAlphabetical(ui->sortingAlphabetical->isChecked()); + m_model->setSortingCaseSensitivity(ui->sortingCaseSensitive->isChecked() ? Qt::CaseSensitive : Qt::CaseInsensitive); + m_model->setSortingByInheritanceDepth(ui->sortingInheritanceDepth->isChecked()); + + // Filtering + m_model->setFilteringEnabled(ui->filtering->isChecked()); + + m_model->setFilterContextMatchesOnly(ui->filteringContextMatchOnly->isChecked()); + m_model->setFilterByAttribute(ui->filteringHideAttributes->isChecked()); + + CodeCompletionModel::CompletionProperties attributes = 0; + for (int i = 0; i < ui->filteringAttributesList->count(); ++i) { + QListWidgetItem* item = ui->filteringAttributesList->item(i); + if (item->checkState() == Qt::Checked) + attributes |= static_cast(item->type()); + } + m_model->setFilterAttributes(attributes); + + m_model->setMaximumInheritanceDepth(ui->filteringMaximumInheritanceDepth->value()); + + // Grouping + m_model->setGroupingEnabled(ui->grouping->isChecked()); + + KateCompletionModel::GroupingMethods groupingMethod = 0; + if (m_groupingScopeType->checkState(0) == Qt::Checked) + groupingMethod = KateCompletionModel::ScopeType; + if (m_groupingScope->checkState(0) == Qt::Checked) + groupingMethod |= KateCompletionModel::Scope; + if (m_groupingAccessType->checkState(0) == Qt::Checked) + groupingMethod |= KateCompletionModel::AccessType; + if (m_groupingItemType->checkState(0) == Qt::Checked) + groupingMethod |= KateCompletionModel::ItemType; + m_model->setGroupingMethod(groupingMethod); + + m_model->setAccessIncludeConst(ui->accessConst->isChecked()); + m_model->setAccessIncludeStatic(ui->accessStatic->isChecked()); + m_model->setAccessIncludeSignalSlot(ui->accessSignalSlot->isChecked()); + + // Column merging + m_model->setColumnMergingEnabled(ui->columnMerging->isChecked()); + + QList< QList > mergedColumns; + QList oneMerge; + for (int i = 0; i < ui->columnMergeTree->topLevelItemCount(); ++i) { + QTreeWidgetItem* item = ui->columnMergeTree->topLevelItem(i); + + if (item->type() != KTextEditor::CodeCompletionModel::Name && item->checkState(2) == Qt::Unchecked) + continue; + + if (item->checkState(1) == Qt::Unchecked) { + if (oneMerge.count()) + mergedColumns.append(oneMerge); + oneMerge.clear(); + } + + oneMerge.append(item->type()); + } + + if (oneMerge.count()) + mergedColumns.append(oneMerge); + + m_model->setColumnMerges(mergedColumns); +} + +void KateCompletionConfig::moveGroupingUp( ) +{ + QTreeWidgetItem* item = ui->groupingMethods->currentItem(); + if (item) { + int index = ui->groupingMethods->indexOfTopLevelItem(item); + if (index > 0) { + ui->groupingMethods->takeTopLevelItem(index); + ui->groupingMethods->insertTopLevelItem(index - 1, item); + ui->groupingMethods->setCurrentItem(item); + } + } +} + +void KateCompletionConfig::moveGroupingDown( ) +{ + QTreeWidgetItem* item = ui->groupingMethods->currentItem(); + if (item) { + int index = ui->groupingMethods->indexOfTopLevelItem(item); + if (index < ui->groupingMethods->topLevelItemCount() - 1) { + ui->groupingMethods->takeTopLevelItem(index); + ui->groupingMethods->insertTopLevelItem(index + 1, item); + ui->groupingMethods->setCurrentItem(item); + } + } +} + +void KateCompletionConfig::moveGroupingOrderUp( ) +{ + QListWidgetItem* item = ui->sortGroupingOrder->currentItem(); + int index = ui->sortGroupingOrder->currentRow(); + if (index > 0) { + ui->sortGroupingOrder->takeItem(index); + ui->sortGroupingOrder->insertItem(index - 1, item); + ui->sortGroupingOrder->setCurrentItem(item); + } +} + +void KateCompletionConfig::moveGroupingOrderDown( ) +{ + QListWidgetItem* item = ui->sortGroupingOrder->currentItem(); + int index = ui->sortGroupingOrder->currentRow(); + if (index < ui->sortGroupingOrder->count() - 1) { + ui->sortGroupingOrder->takeItem(index); + ui->sortGroupingOrder->insertItem(index + 1, item); + ui->sortGroupingOrder->setCurrentItem(item); + } +} + +#include "moc_katecompletionconfig.cpp" diff --git a/kate/part/completion/katecompletionconfig.h b/kate/part/completion/katecompletionconfig.h new file mode 100644 index 00000000..4031af26 --- /dev/null +++ b/kate/part/completion/katecompletionconfig.h @@ -0,0 +1,82 @@ +/* This file is part of the KDE libraries and the Kate part. + * + * Copyright (C) 2006 Hamish Rodda + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef KATECOMPLETIONCONFIG_H +#define KATECOMPLETIONCONFIG_H + +#include + +#include "kateconfig.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class CompletionConfigWidget; } +QT_END_NAMESPACE + +#include +class KateCompletionModel; + +/** + * @author Hamish Rodda + */ +class KateCompletionConfig : public KDialog, public KateConfig +{ + Q_OBJECT + + public: + explicit KateCompletionConfig(KateCompletionModel* model, QWidget* parent = 0L); + virtual ~KateCompletionConfig(); + + /** + * Read config from object + */ + void readConfig (const KConfigGroup &config); + + /** + * Write config to object + */ + void writeConfig (KConfigGroup &config); + + public Q_SLOTS: + void apply(); + + protected: + virtual void updateConfig(); + + private Q_SLOTS: + void moveColumnUp(); + void moveColumnDown(); + void moveGroupingUp(); + void moveGroupingDown(); + void moveGroupingOrderUp(); + void moveGroupingOrderDown(); + + private: + void applyInternal(); + + Ui::CompletionConfigWidget* ui; + KateCompletionModel* m_model; + + QTreeWidgetItem* m_groupingScopeType; + QTreeWidgetItem* m_groupingScope; + QTreeWidgetItem* m_groupingAccessType; + QTreeWidgetItem* m_groupingItemType; +}; + +#endif diff --git a/kate/part/completion/katecompletiondelegate.cpp b/kate/part/completion/katecompletiondelegate.cpp new file mode 100644 index 00000000..f7a29e4d --- /dev/null +++ b/kate/part/completion/katecompletiondelegate.cpp @@ -0,0 +1,160 @@ +/* This file is part of the KDE libraries and the Kate part. + * + * Copyright (C) 2007 David Nolden + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "katecompletiondelegate.h" + +#include + +#include "katerenderer.h" +#include "katetextline.h" +#include "katedocument.h" +#include "kateview.h" +#include "katehighlight.h" +#include "katerenderrange.h" + +#include "katecompletionwidget.h" +#include "katecompletionmodel.h" +#include "katecompletiontree.h" + +//Currently disable because it doesn't work +#define DISABLE_INTERNAL_HIGHLIGHTING + +KateCompletionDelegate::KateCompletionDelegate(ExpandingWidgetModel* model, KateCompletionWidget* parent) : + ExpandingDelegate(model, parent), m_cachedRow(-1) +{ +} + +void KateCompletionDelegate::adjustStyle( const QModelIndex& index, QStyleOptionViewItem & option ) const { + if(index.column() == 0) { + //We always want to use the match-color if available, also for highlighted items. + ///@todo Only do this for the "current" item, for others the model is asked for the match color. + uint color = model()->matchColor(index); + if(color != 0) { + QColor match(color); + + for(int a = 0; a <=2; a++ ) + option.palette.setColor( (QPalette::ColorGroup)a, QPalette::Highlight, match ); + } + } +} + + +KateRenderer * KateCompletionDelegate::renderer( ) const +{ + return widget()->view()->renderer(); +} + +KateCompletionWidget * KateCompletionDelegate::widget( ) const +{ + return static_cast(const_cast(parent())); +} + +KateDocument * KateCompletionDelegate::document( ) const +{ + return widget()->view()->doc(); +} + +void KateCompletionDelegate::heightChanged() const { + if(parent()) + widget()->updateHeight(); +} + +QList KateCompletionDelegate::createHighlighting(const QModelIndex& index, QStyleOptionViewItem& option) const { + + QVariant highlight = model()->data(index, KTextEditor::CodeCompletionModel::HighlightingMethod); + + // TODO: config enable specifying no highlight as default + int highlightMethod = KTextEditor::CodeCompletionModel::InternalHighlighting; + if (highlight.canConvert(QVariant::Int)) + highlightMethod = highlight.toInt(); + + if (highlightMethod & KTextEditor::CodeCompletionModel::CustomHighlighting) { + m_currentColumnStart = 0; + return highlightingFromVariantList(model()->data(index, KTextEditor::CodeCompletionModel::CustomHighlight).toList()); + } + +#ifdef DISABLE_INTERNAL_HIGHLIGHTING + return QList(); +#endif + + if( index.row() == m_cachedRow && highlightMethod & KTextEditor::CodeCompletionModel::InternalHighlighting ) { + + if( index.column() < m_cachedColumnStarts.size() ) { + m_currentColumnStart = m_cachedColumnStarts[index.column()]; + } else { + kWarning() << "Column-count does not match"; + } + + return m_cachedHighlights; + } + + ///@todo reset the cache when the model changed + m_cachedRow = index.row(); + + KTextEditor::Cursor completionStart = widget()->completionRange()->start(); + + QString startText = document()->text(KTextEditor::Range(completionStart.line(), 0, completionStart.line(), completionStart.column())); + + QString lineContent = startText; + + int len = completionStart.column(); + m_cachedColumnStarts.clear(); + + for (int i = 0; i < KTextEditor::CodeCompletionModel::ColumnCount; ++i) { + m_cachedColumnStarts.append(len); + QString text = model()->data(model()->index(index.row(), i, index.parent()), Qt::DisplayRole).toString(); + lineContent += text; + len += text.length(); + } + + Kate::TextLine thisLine = Kate::TextLine (new Kate::TextLineData(lineContent)); + + //kDebug( 13035 ) << "About to highlight with mode " << highlightMethod << " text [" << thisLine->string() << "]"; + + if (highlightMethod & KTextEditor::CodeCompletionModel::InternalHighlighting) { + Kate::TextLine previousLine; + if (completionStart.line()) + previousLine = document()->kateTextLine(completionStart.line() - 1); + else + previousLine = Kate::TextLine (new Kate::TextLineData()); + + Kate::TextLine nextLine; + if ((completionStart.line() + 1) < document()->lines()) + nextLine = document()->kateTextLine(completionStart.line() + 1); + else + nextLine = Kate::TextLine (new Kate::TextLineData()); + + bool ctxChanged = false; + document()->highlight()->doHighlight(previousLine.data(), thisLine.data(), nextLine.data(), ctxChanged); + } + + m_currentColumnStart = m_cachedColumnStarts[index.column()]; + + NormalRenderRange rr; + QList ret = renderer()->decorationsForLine(thisLine, 0, false, &rr, option.state & QStyle::State_Selected); + + //Remove background-colors + for( QList::iterator it = ret.begin(); it != ret.end(); ++it ) + (*it).format.clearBackground(); + + return ret; +} + + diff --git a/kate/part/completion/katecompletiondelegate.h b/kate/part/completion/katecompletiondelegate.h new file mode 100644 index 00000000..c5264323 --- /dev/null +++ b/kate/part/completion/katecompletiondelegate.h @@ -0,0 +1,42 @@ +/* This file is part of the KDE libraries and the Kate part. + * + * Copyright (C) 2007 David Nolden + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef KATECOMPLETIONDELEGATE_H +#define KATECOMPLETIONDELEGATE_H + +#include "expandingtree/expandingdelegate.h" + +class KateCompletionDelegate : public ExpandingDelegate { + public: + explicit KateCompletionDelegate(ExpandingWidgetModel* model, KateCompletionWidget* parent); + + KateRenderer* renderer() const; + KateCompletionWidget* widget() const; + KateDocument* document() const; + protected: + virtual void adjustStyle( const QModelIndex& index, QStyleOptionViewItem & option ) const; + mutable int m_cachedRow; + mutable QList m_cachedColumnStarts; + virtual void heightChanged() const; + QList createHighlighting(const QModelIndex& index, QStyleOptionViewItem& option) const; + +}; + +#endif diff --git a/kate/part/completion/katecompletionmodel.cpp b/kate/part/completion/katecompletionmodel.cpp new file mode 100644 index 00000000..014eb026 --- /dev/null +++ b/kate/part/completion/katecompletionmodel.cpp @@ -0,0 +1,2277 @@ +/* This file is part of the KDE libraries and the Kate part. + * + * Copyright (C) 2005-2006 Hamish Rodda + * Copyright (C) 2007-2008 David Nolden + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "katecompletionmodel.h" + +#include +#include +#include +#include + +#include +#include +#include + +#include "katecompletionwidget.h" +#include "katecompletiontree.h" +#include "katecompletiondelegate.h" +#include "kateargumenthintmodel.h" +#include "kateview.h" +#include "katerenderer.h" +#include "kateconfig.h" +#include "codecompletionmodelcontrollerinterfacev4.h" + +using namespace KTextEditor; + +///A helper-class for handling completion-models with hierarchical grouping/optimization +class HierarchicalModelHandler { +public: + HierarchicalModelHandler(CodeCompletionModel* model); + void addValue(CodeCompletionModel::ExtraItemDataRoles role, const QVariant& value); + //Walks the index upwards and collects all defined completion-roles on the way + void collectRoles(const QModelIndex& index); + void takeRole(const QModelIndex& index); + + CodeCompletionModel* model() const; + + //Assumes that index is a sub-index of the indices where role-values were taken + QVariant getData(CodeCompletionModel::ExtraItemDataRoles role, const QModelIndex& index) const; + + bool hasHierarchicalRoles() const; + + int inheritanceDepth(const QModelIndex& i) const; + + QString customGroup() const { + return m_customGroup; + } + + int customGroupingKey() const { + return m_groupSortingKey; + } +private: + typedef QMap RoleMap; + RoleMap m_roleValues; + QString m_customGroup; + int m_groupSortingKey; + CodeCompletionModel* m_model; +}; + +CodeCompletionModel* HierarchicalModelHandler::model() const { + return m_model; +} + +bool HierarchicalModelHandler::hasHierarchicalRoles() const { + return !m_roleValues.isEmpty(); +} + +void HierarchicalModelHandler::collectRoles(const QModelIndex& index) { + if( index.parent().isValid() ) + collectRoles(index.parent()); + if(m_model->rowCount(index) != 0) + takeRole(index); +} + +int HierarchicalModelHandler::inheritanceDepth(const QModelIndex& i) const { + return getData(CodeCompletionModel::InheritanceDepth, i).toInt(); +} + +void HierarchicalModelHandler::takeRole(const QModelIndex& index) { + QVariant v = index.data(CodeCompletionModel::GroupRole); + if( v.isValid() && v.canConvert(QVariant::Int) ) { + QVariant value = index.data(v.toInt()); + if(v.toInt() == Qt::DisplayRole) { + m_customGroup = index.data(Qt::DisplayRole).toString(); + QVariant sortingKey = index.data(CodeCompletionModel::InheritanceDepth); + if(sortingKey.canConvert(QVariant::Int)) + m_groupSortingKey = sortingKey.toInt(); + }else{ + m_roleValues[(CodeCompletionModel::ExtraItemDataRoles)v.toInt()] = value; + } + }else{ + kDebug( 13035 ) << "Did not return valid GroupRole in hierarchical completion-model"; + } +} + +QVariant HierarchicalModelHandler::getData(CodeCompletionModel::ExtraItemDataRoles role, const QModelIndex& index) const { + RoleMap::const_iterator it = m_roleValues.find(role); + if( it != m_roleValues.end() ) + return *it; + else + return index.data(role); +} + +HierarchicalModelHandler::HierarchicalModelHandler(CodeCompletionModel* model) : m_groupSortingKey(-1), m_model(model) { +} + +void HierarchicalModelHandler::addValue(CodeCompletionModel::ExtraItemDataRoles role, const QVariant& value) { + m_roleValues[role] = value; +} + +KateCompletionModel::KateCompletionModel(KateCompletionWidget* parent) + : ExpandingWidgetModel(parent) + , m_hasGroups(false) + , m_matchCaseSensitivity(Qt::CaseInsensitive) + , m_ungrouped(new Group(this)) + , m_argumentHints(new Group(this)) + , m_bestMatches(new Group(this)) + , m_sortingEnabled(false) + , m_sortingAlphabetical(false) + , m_isSortingByInheritance(false) + , m_sortingCaseSensitivity(Qt::CaseInsensitive) + , m_filteringEnabled(false) + , m_filterContextMatchesOnly(false) + , m_filterByAttribute(false) + , m_filterAttributes(KTextEditor::CodeCompletionModel::NoProperty) + , m_maximumInheritanceDepth(0) + , m_groupingEnabled(false) + , m_accessConst(false) + , m_accessStatic(false) + , m_accesSignalSlot(false) + , m_columnMergingEnabled(false) +// , m_haveExactMatch(false) +{ + + m_ungrouped->attribute = 0; + m_argumentHints->attribute = -1; + m_bestMatches->attribute = BestMatchesProperty; + + m_argumentHints->title = i18n("Argument-hints"); + m_bestMatches->title = i18n("Best matches"); + + m_emptyGroups.append(m_ungrouped); + m_emptyGroups.append(m_argumentHints); + m_emptyGroups.append(m_bestMatches); + + m_updateBestMatchesTimer = new QTimer(this); + m_updateBestMatchesTimer->setSingleShot(true); + connect(m_updateBestMatchesTimer, SIGNAL(timeout()), this, SLOT(updateBestMatches())); + + m_groupHash.insert(0, m_ungrouped); + m_groupHash.insert(-1, m_argumentHints); + m_groupHash.insert(BestMatchesProperty, m_argumentHints); +} + +KateCompletionModel::~KateCompletionModel() { + clearCompletionModels(); + delete m_argumentHints; + delete m_ungrouped; + delete m_bestMatches; +} + +QTreeView* KateCompletionModel::treeView() const { + return view()->completionWidget()->treeView(); +} + +QVariant KateCompletionModel::data( const QModelIndex & index, int role ) const +{ + if (!hasCompletionModel() || !index.isValid()) + return QVariant(); + + if( role == Qt::DecorationRole && index.column() == KTextEditor::CodeCompletionModel::Prefix && isExpandable(index) ) + { + cacheIcons(); + + if( !isExpanded(index ) ) + return QVariant( m_collapsedIcon ); + else + return QVariant( m_expandedIcon ); + } + + //groupOfParent returns a group when the index is a member of that group, but not the group head/label. + if (!hasGroups() || groupOfParent(index)) { + switch (role) { + case Qt::TextAlignmentRole: + if (isColumnMergingEnabled() && m_columnMerges.count()) { + int c = 0; + foreach (const QList& list, m_columnMerges) { + foreach (int column, list) { + if (c++ == index.column()) { + if (column == CodeCompletionModel::Scope) + if (list.count() == 1) + return Qt::AlignRight; + + goto dontalign; + } + } + } + + } else if ((!isColumnMergingEnabled() || m_columnMerges.isEmpty()) && index.column() == CodeCompletionModel::Scope) { + return Qt::AlignRight; + } + + dontalign: + break; + } + + // Merge text for column merging + if (role == Qt::DisplayRole && m_columnMerges.count() && isColumnMergingEnabled()) { + QString text; + foreach (int column, m_columnMerges[index.column()]) { + QModelIndex sourceIndex = mapToSource(createIndex(index.row(), column, index.internalPointer())); + text.append(sourceIndex.data(role).toString()); + } + + return text; + } + + if(role == CodeCompletionModel::HighlightingMethod) + { + //Return that we are doing custom-highlighting of one of the sub-strings does it. Unfortunately internal highlighting does not work for the other substrings. + foreach (int column, m_columnMerges[index.column()]) { + QModelIndex sourceIndex = mapToSource(createIndex(index.row(), column, index.internalPointer())); + QVariant method = sourceIndex.data(CodeCompletionModel::HighlightingMethod); + if( method.type() == QVariant::Int && method.toInt() == CodeCompletionModel::CustomHighlighting) + return QVariant(CodeCompletionModel::CustomHighlighting); + } + return QVariant(); + } + if(role == CodeCompletionModel::CustomHighlight) + { + //Merge custom highlighting if multiple columns were merged + QStringList strings; + + //Collect strings + foreach (int column, m_columnMerges[index.column()]) + strings << mapToSource(createIndex(index.row(), column, index.internalPointer())).data(Qt::DisplayRole).toString(); + + QList highlights; + + //Collect custom-highlightings + foreach (int column, m_columnMerges[index.column()]) + highlights << mapToSource(createIndex(index.row(), column, index.internalPointer())).data(CodeCompletionModel::CustomHighlight).toList(); + + return mergeCustomHighlighting( strings, highlights, 0 ); + } + + QVariant v = mapToSource(index).data(role); + if( v.isValid() ) + return v; + else + return ExpandingWidgetModel::data(index, role); + } + + //Returns a nonzero group if this index is the head of a group(A Label in the list) + Group* g = groupForIndex(index); + + if (g && (!g->isEmpty)) { + switch (role) { + case Qt::DisplayRole: + //We return the group-header for all columns, ExpandingDelegate will paint them properly over the whole space + return QString(' ' + g->title); + break; + + case Qt::FontRole: + if (!index.column()) { + QFont f = view()->renderer()->config()->font(); + f.setBold(true); + return f; + } + break; + + case Qt::ForegroundRole: + return KApplication::kApplication()->palette().toolTipText().color(); + case Qt::BackgroundRole: + return KApplication::kApplication()->palette().toolTipBase().color(); + } + } + + return QVariant(); +} + +int KateCompletionModel::contextMatchQuality(const QModelIndex& index) const { + if(!index.isValid()) + return 0; + Group* g = groupOfParent(index); + if(!g || g->filtered.size() < index.row()) + return 0; + + return contextMatchQuality(g->filtered[index.row()].sourceRow()); +} + +int KateCompletionModel::contextMatchQuality(const ModelRow& source) const { + QModelIndex realIndex = source.second; + + int bestMatch = -1; + //Iterate through all argument-hints and find the best match-quality + foreach( const Item& item, m_argumentHints->filtered ) + { + const ModelRow& row(item.sourceRow()); + if( realIndex.model() != row.first ) + continue; //We can only match within the same source-model + + QModelIndex hintIndex = row.second; + + QVariant depth = hintIndex.data(CodeCompletionModel::ArgumentHintDepth); + if( !depth.isValid() || depth.type() != QVariant::Int || depth.toInt() != 1 ) + continue; //Only match completion-items to argument-hints of depth 1(the ones the item will be given to as argument) + + hintIndex.data(CodeCompletionModel::SetMatchContext); + + QVariant matchQuality = realIndex.data(CodeCompletionModel::MatchQuality); + if( matchQuality.isValid() && matchQuality.type() == QVariant::Int ) { + int m = matchQuality.toInt(); + if( m > bestMatch ) + bestMatch = m; + } + } + + if(m_argumentHints->filtered.isEmpty()) { + QVariant matchQuality = realIndex.data(CodeCompletionModel::MatchQuality); + if( matchQuality.isValid() && matchQuality.type() == QVariant::Int ) { + int m = matchQuality.toInt(); + if( m > bestMatch ) + bestMatch = m; + } + } + + return bestMatch; +} + +Qt::ItemFlags KateCompletionModel::flags( const QModelIndex & index ) const +{ + if (!hasCompletionModel() || !index.isValid()) + return 0; + + if (!hasGroups() || groupOfParent(index)) + return Qt::ItemIsSelectable | Qt::ItemIsEnabled; + + return Qt::ItemIsEnabled; +} + +KateCompletionWidget* KateCompletionModel::widget() const { + return static_cast(QObject::parent()); +} + +KateView * KateCompletionModel::view( ) const +{ + return widget()->view(); +} + +void KateCompletionModel::setMatchCaseSensitivity( Qt::CaseSensitivity cs ) +{ + m_matchCaseSensitivity = cs; +} + +int KateCompletionModel::columnCount( const QModelIndex& ) const +{ + return isColumnMergingEnabled() && !m_columnMerges.isEmpty() ? m_columnMerges.count() : KTextEditor::CodeCompletionModel::ColumnCount; +} + +KateCompletionModel::ModelRow KateCompletionModel::modelRowPair(const QModelIndex& index) const +{ + return qMakePair(static_cast(const_cast(index.model())), index); +} + +bool KateCompletionModel::hasChildren( const QModelIndex & parent ) const +{ + if (!hasCompletionModel()) + return false; + + if (!parent.isValid()) { + if (hasGroups()) + return true; + + return !m_ungrouped->filtered.isEmpty(); + } + + if (parent.column() != 0) + return false; + + if (!hasGroups()) + return false; + + if (Group* g = groupForIndex(parent)) + return !g->filtered.isEmpty(); + + return false; +} + +QModelIndex KateCompletionModel::index( int row, int column, const QModelIndex & parent ) const +{ + if (row < 0 || column < 0 || column >= columnCount(QModelIndex())) + return QModelIndex(); + + if (parent.isValid() || !hasGroups()) { + if (parent.isValid() && parent.column() != 0) + return QModelIndex(); + + Group* g = groupForIndex(parent); + + if (!g) + return QModelIndex(); + + if (row >= g->filtered.count()) { + //kWarning() << "Invalid index requested: row " << row << " beyond indivdual range in group " << g; + return QModelIndex(); + } + + //kDebug( 13035 ) << "Returning index for child " << row << " of group " << g; + return createIndex(row, column, g); + } + + if (row >= m_rowTable.count()) { + //kWarning() << "Invalid index requested: row " << row << " beyond group range."; + return QModelIndex(); + } + + //kDebug( 13035 ) << "Returning index for group " << m_rowTable[row]; + return createIndex(row, column, 0); +} + +/*QModelIndex KateCompletionModel::sibling( int row, int column, const QModelIndex & index ) const +{ + if (row < 0 || column < 0 || column >= columnCount(QModelIndex())) + return QModelIndex(); + + if (!index.isValid()) { + } + + if (Group* g = groupOfParent(index)) { + if (row >= g->filtered.count()) + return QModelIndex(); + + return createIndex(row, column, g); + } + + if (hasGroups()) + return QModelIndex(); + + if (row >= m_ungrouped->filtered.count()) + return QModelIndex(); + + return createIndex(row, column, m_ungrouped); +}*/ + +bool KateCompletionModel::hasIndex( int row, int column, const QModelIndex & parent ) const +{ + if (row < 0 || column < 0 || column >= columnCount(QModelIndex())) + return false; + + if (parent.isValid() || !hasGroups()) { + if (parent.isValid() && parent.column() != 0) + return false; + + Group* g = groupForIndex(parent); + + if (row >= g->filtered.count()) + return false; + + return true; + } + + if (row >= m_rowTable.count()) + return false; + + return true; +} + +QModelIndex KateCompletionModel::indexForRow( Group * g, int row ) const +{ + if (row < 0 || row >= g->filtered.count()) + return QModelIndex(); + + return createIndex(row, 0, g); +} + +QModelIndex KateCompletionModel::indexForGroup( Group * g ) const +{ + if (!hasGroups()) + return QModelIndex(); + + int row = m_rowTable.indexOf(g); + + if (row == -1) + return QModelIndex(); + + return createIndex(row, 0, 0); +} + +void KateCompletionModel::clearGroups() +{ + clearExpanding(); + m_ungrouped->clear(); + m_argumentHints->clear(); + m_bestMatches->clear(); + + // Don't bother trying to work out where it is + m_rowTable.removeAll(m_ungrouped); + m_emptyGroups.removeAll(m_ungrouped); + + m_rowTable.removeAll(m_argumentHints); + m_emptyGroups.removeAll(m_argumentHints); + + m_rowTable.removeAll(m_bestMatches); + m_emptyGroups.removeAll(m_bestMatches); + + qDeleteAll(m_rowTable); + qDeleteAll(m_emptyGroups); + m_rowTable.clear(); + m_emptyGroups.clear(); + m_groupHash.clear(); + m_customGroupHash.clear(); + + m_emptyGroups.append(m_ungrouped); + m_groupHash.insert(0, m_ungrouped); + + m_emptyGroups.append(m_argumentHints); + m_groupHash.insert(-1, m_argumentHints); + + m_emptyGroups.append(m_bestMatches); + m_groupHash.insert(BestMatchesProperty, m_bestMatches); +} + +QSet KateCompletionModel::createItems(const HierarchicalModelHandler& _handler, const QModelIndex& i, bool notifyModel) { + HierarchicalModelHandler handler(_handler); + QSet ret; + + if( handler.model()->rowCount(i) == 0 ) { + //Leaf node, create an item + ret.insert( createItem(handler, i, notifyModel) ); + } else { + //Non-leaf node, take the role from the node, and recurse to the sub-nodes + handler.takeRole(i); + for(int a = 0; a < handler.model()->rowCount(i); a++) + ret += createItems(handler, i.child(a, 0), notifyModel); + } + + return ret; +} + +QSet KateCompletionModel::deleteItems(const QModelIndex& i) { + QSet ret; + + if( i.model()->rowCount(i) == 0 ) { + //Leaf node, delete the item + Group* g = groupForIndex(mapFromSource(i)); + ret.insert(g); + g->removeItem(ModelRow(const_cast(static_cast(i.model())), i)); + } else { + //Non-leaf node + for(int a = 0; a < i.model()->rowCount(i); a++) + ret += deleteItems(i.child(a, 0)); + } + + return ret; +} + +void KateCompletionModel::createGroups() +{ + beginResetModel(); + //After clearing the model, it has to be reset, else we will be in an invalid state while inserting + //new groups. + clearGroups(); + + bool has_groups=false; + foreach (CodeCompletionModel* sourceModel, m_completionModels) { + has_groups|=sourceModel->hasGroups(); + for (int i = 0; i < sourceModel->rowCount(); ++i) + createItems(HierarchicalModelHandler(sourceModel), sourceModel->index(i, 0)); + } + m_hasGroups=has_groups; + + //debugStats(); + + foreach (Group* g, m_rowTable) + hideOrShowGroup(g); + + foreach (Group* g, m_emptyGroups) + hideOrShowGroup(g); + + makeGroupItemsUnique(); + + updateBestMatches(); + endResetModel(); +} + +KateCompletionModel::Group* KateCompletionModel::createItem(const HierarchicalModelHandler& handler, const QModelIndex& sourceIndex, bool notifyModel) +{ + //QModelIndex sourceIndex = sourceModel->index(row, CodeCompletionModel::Name, QModelIndex()); + + int completionFlags = handler.getData(CodeCompletionModel::CompletionRole, sourceIndex).toInt(); + + //Scope is expensive, should not be used with big models + QString scopeIfNeeded = (groupingMethod() & Scope) ? sourceIndex.sibling(sourceIndex.row(), CodeCompletionModel::Scope).data(Qt::DisplayRole).toString() : QString(); + + int argumentHintDepth = handler.getData(CodeCompletionModel::ArgumentHintDepth, sourceIndex).toInt(); + + Group* g; + if( argumentHintDepth ) { + g = m_argumentHints; + } else{ + QString customGroup = handler.customGroup(); + if(!customGroup.isNull() && m_hasGroups) { + if(m_customGroupHash.contains(customGroup)) { + g = m_customGroupHash[customGroup]; + }else{ + g = new Group(this); + g->title = customGroup; + g->customSortingKey = handler.customGroupingKey(); + m_emptyGroups.append(g); + m_customGroupHash.insert(customGroup, g); + } + }else{ + g = fetchGroup(completionFlags, scopeIfNeeded, handler.hasHierarchicalRoles()); + } + } + + Item item = Item(g != m_argumentHints, this, handler, ModelRow(handler.model(), sourceIndex)); + + if(g != m_argumentHints) + item.match(); + + g->addItem(item, notifyModel); + + return g; +} + +void KateCompletionModel::slotRowsInserted( const QModelIndex & parent, int start, int end ) +{ + QSet affectedGroups; + + HierarchicalModelHandler handler(static_cast(sender())); + if(parent.isValid()) + handler.collectRoles(parent); + + + for (int i = start; i <= end; ++i) + affectedGroups += createItems(handler, parent.isValid() ? parent.child(i, 0) : handler.model()->index(i, 0), true); + + foreach (Group* g, affectedGroups) + hideOrShowGroup(g, true); +} + +void KateCompletionModel::slotRowsRemoved( const QModelIndex & parent, int start, int end ) +{ + CodeCompletionModel* source = static_cast(sender()); + + QSet affectedGroups; + + for (int i = start; i <= end; ++i) { + QModelIndex index = parent.isValid() ? parent.child(i, 0) : source->index(i, 0); + + affectedGroups += deleteItems(index); + } + + foreach (Group* g, affectedGroups) + hideOrShowGroup(g, true); +} + +KateCompletionModel::Group* KateCompletionModel::fetchGroup( int attribute, const QString& scope, bool forceGrouping ) +{ + Q_UNUSED(forceGrouping); + + ///@todo use forceGrouping + if (!hasGroups()) + return m_ungrouped; + + int groupingAttribute = groupingAttributes(attribute); + //kDebug( 13035 ) << attribute << " " << groupingAttribute; + + if (m_groupHash.contains(groupingAttribute)) { + if (groupingMethod() & Scope) { + for (QHash::ConstIterator it = m_groupHash.constFind(groupingAttribute); it != m_groupHash.constEnd() && it.key() == groupingAttribute; ++it) + if (it.value()->scope == scope) + return it.value(); + } else { + return m_groupHash.value(groupingAttribute); + } + } + Group* ret = new Group(this); + + ret->attribute = attribute; + ret->scope = scope; + + QString st, at, it; + + if (groupingMethod() & ScopeType) { + if (attribute & KTextEditor::CodeCompletionModel::GlobalScope) + st = "Global"; + else if (attribute & KTextEditor::CodeCompletionModel::NamespaceScope) + st = "Namespace"; + else if (attribute & KTextEditor::CodeCompletionModel::LocalScope) + st = "Local"; + + ret->title = st; + } + + if (groupingMethod() & Scope) { + if (!ret->title.isEmpty()) + ret->title.append(" "); + + ret->title.append(scope); + } + + if (groupingMethod() & AccessType) { + if (attribute & KTextEditor::CodeCompletionModel::Public) + at = "Public"; + else if (attribute & KTextEditor::CodeCompletionModel::Protected) + at = "Protected"; + else if (attribute & KTextEditor::CodeCompletionModel::Private) + at = "Private"; + + if (accessIncludeStatic() && attribute & KTextEditor::CodeCompletionModel::Static) + at.append(" Static"); + + if (accessIncludeConst() && attribute & KTextEditor::CodeCompletionModel::Const) + at.append(" Const"); + + if( !at.isEmpty() ) { + if (!ret->title.isEmpty()) + ret->title.append(", "); + + ret->title.append(at); + } + } + + if (groupingMethod() & ItemType) { + if (attribute & CodeCompletionModel::Namespace) + it = i18n("Namespaces"); + else if (attribute & CodeCompletionModel::Class) + it = i18n("Classes"); + else if (attribute & CodeCompletionModel::Struct) + it = i18n("Structs"); + else if (attribute & CodeCompletionModel::Union) + it = i18n("Unions"); + else if (attribute & CodeCompletionModel::Function) + it = i18n("Functions"); + else if (attribute & CodeCompletionModel::Variable) + it = i18n("Variables"); + else if (attribute & CodeCompletionModel::Enum) + it = i18n("Enumerations"); + + if( !it.isEmpty() ) { + if (!ret->title.isEmpty()) + ret->title.append(" "); + + ret->title.append(it); + } + } + + m_emptyGroups.append(ret); + m_groupHash.insert(groupingAttribute, ret); + + return ret; +} + +bool KateCompletionModel::hasGroups( ) const +{ + //kDebug( 13035 ) << "m_groupHash.size()"<= m_rowTable.count()) + return m_ungrouped; + + return m_rowTable[index.row()]; +} + +/*QMap< int, QVariant > KateCompletionModel::itemData( const QModelIndex & index ) const +{ + if (!hasGroups() || groupOfParent(index)) { + QModelIndex index = mapToSource(index); + if (index.isValid()) + return index.model()->itemData(index); + } + + return QAbstractItemModel::itemData(index); +}*/ + +QModelIndex KateCompletionModel::parent( const QModelIndex & index ) const +{ + if (!index.isValid()) + return QModelIndex(); + + if (Group* g = groupOfParent(index)) { + if (!hasGroups()) { + Q_ASSERT(g == m_ungrouped); + return QModelIndex(); + } + + int row = m_rowTable.indexOf(g); + + if (row == -1) { + kWarning() << "Couldn't find parent for index" << index; + return QModelIndex(); + } + + return createIndex(row, 0, 0); + } + + return QModelIndex(); +} + +int KateCompletionModel::rowCount( const QModelIndex & parent ) const +{ + if (!parent.isValid()) { + if (hasGroups()) { + //kDebug( 13035 ) << "Returning row count for toplevel " << m_rowTable.count(); + return m_rowTable.count(); + } else { + //kDebug( 13035 ) << "Returning ungrouped row count for toplevel " << m_ungrouped->filtered.count(); + return m_ungrouped->filtered.count(); + } + } + + Group* g = groupForIndex(parent); + + // This is not an error, seems you don't have to check hasChildren() + if (!g) + return 0; + + //kDebug( 13035 ) << "Returning row count for group " << g << " as " << g->filtered.count(); + return g->filtered.count(); +} + +void KateCompletionModel::sort( int column, Qt::SortOrder order ) +{ + Q_UNUSED(column) + Q_UNUSED(order) +} + +QModelIndex KateCompletionModel::mapToSource( const QModelIndex & proxyIndex ) const +{ + if (!proxyIndex.isValid()) + return QModelIndex(); + + if (Group* g = groupOfParent(proxyIndex)) { + if( proxyIndex.row() >= 0 && proxyIndex.row() < g->filtered.count() ) { + ModelRow source = g->filtered[proxyIndex.row()].sourceRow(); + return source.second.sibling(source.second.row(), proxyIndex.column()); + }else{ + kDebug( 13035 ) << "Invalid proxy-index"; + } + } + + return QModelIndex(); +} + +QModelIndex KateCompletionModel::mapFromSource( const QModelIndex & sourceIndex ) const +{ + if (!sourceIndex.isValid()) + return QModelIndex(); + + if (!hasGroups()) + return index(m_ungrouped->rowOf(modelRowPair(sourceIndex)), sourceIndex.column(), QModelIndex()); + + foreach (Group* g, m_rowTable) { + int row = g->rowOf(modelRowPair(sourceIndex)); + if (row != -1) + return index(row, sourceIndex.column(), indexForGroup(g)); + } + + // Copied from above + foreach (Group* g, m_emptyGroups) { + int row = g->rowOf(modelRowPair(sourceIndex)); + if (row != -1) + return index(row, sourceIndex.column(), indexForGroup(g)); + } + + return QModelIndex(); +} + +void KateCompletionModel::setCurrentCompletion( KTextEditor::CodeCompletionModel* model, const QString & completion ) +{ + if (m_currentMatch[model] == completion) + return; + + if (!hasCompletionModel()) { + m_currentMatch[model] = completion; + return; + } + + changeTypes changeType = Change; + + if (m_currentMatch[model].length() > completion.length() && m_currentMatch[model].startsWith(completion, m_matchCaseSensitivity)) { + // Filter has been broadened + changeType = Broaden; + + } else if (m_currentMatch[model].length() < completion.length() && completion.startsWith(m_currentMatch[model], m_matchCaseSensitivity)) { + // Filter has been narrowed + changeType = Narrow; + } + + //kDebug( 13035 ) << model << "Old match: " << m_currentMatch[model] << ", new: " << completion << ", type: " << changeType; + + m_currentMatch[model] = completion; + + const bool resetModel = (changeType != Narrow); + if (resetModel) { + beginResetModel(); + } + + if (!hasGroups()) { + changeCompletions(m_ungrouped, changeType, !resetModel); + } else { + foreach (Group* g, m_rowTable) { + if(g != m_argumentHints) + changeCompletions(g, changeType, !resetModel); + } + foreach (Group* g, m_emptyGroups) { + if(g != m_argumentHints) + changeCompletions(g, changeType, !resetModel); + } + } + + // NOTE: best matches are also updated in resort + resort(); + + if (resetModel) { + endResetModel(); + } + + clearExpanding(); //We need to do this, or be aware of expanding-widgets while filtering. + + emit layoutChanged(); +} + +QString KateCompletionModel::commonPrefixInternal(const QString &forcePrefix) const +{ + QString commonPrefix; // isNull() = true + + QList< Group* > groups = m_rowTable; + groups += m_ungrouped; + + foreach (Group* g, groups) { + foreach(const Item& item, g->filtered) + { + uint startPos = m_currentMatch[item.sourceRow().first].length(); + const QString candidate = item.name().mid(startPos); + + if(!candidate.startsWith(forcePrefix)) + continue; + + if(commonPrefix.isNull()) { + commonPrefix = candidate; + + //Replace QString::null prefix with QString(""), so we won't initialize it again + if(commonPrefix.isNull()) + commonPrefix = QString(""); // isEmpty() = true, isNull() = false + }else{ + commonPrefix = commonPrefix.left(candidate.length()); + + for(int a = 0; a < commonPrefix.length(); ++a) { + if(commonPrefix[a] != candidate[a]) { + commonPrefix = commonPrefix.left(a); + break; + } + } + } + } + } + + return commonPrefix; +} + +QString KateCompletionModel::commonPrefix(QModelIndex selectedIndex) const +{ + QString commonPrefix = commonPrefixInternal(QString()); + + if(commonPrefix.isEmpty() && selectedIndex.isValid()) { + Group* g = m_ungrouped; + if(hasGroups()) + g = groupOfParent(selectedIndex); + + if(g && selectedIndex.row() < g->filtered.size()) + { + //Follow the path of the selected item, finding the next non-empty common prefix + Item item = g->filtered[selectedIndex.row()]; + int matchLength = m_currentMatch[item.sourceRow().first].length(); + commonPrefix = commonPrefixInternal(item.name().mid(matchLength).left(1)); + } + } + + return commonPrefix; +} + +void KateCompletionModel::changeCompletions( Group * g, changeTypes changeType, bool notifyModel ) +{ + if(changeType != Narrow) { + g->filtered = g->prefilter; + //In the "Broaden" or "Change" case, just re-filter everything, + //and don't notify the model. The model is notified afterwards through a reset(). + } + + //This code determines what of the filtered items still fit, and computes the ranges that were removed, giving + //them to beginRemoveRows(..) in batches + + QList newFiltered; + int deleteUntil = -1; //In each state, the range [currentRow+1, deleteUntil] needs to be deleted + for(int currentRow = g->filtered.count()-1; currentRow >= 0; --currentRow) { + if(g->filtered[currentRow].match()) { + //This row does not need to be deleted, which means that currentRow+1 to deleteUntil need to be deleted now + if(deleteUntil != -1 && notifyModel) { + beginRemoveRows(indexForGroup(g), currentRow+1, deleteUntil); + endRemoveRows(); + } + deleteUntil = -1; + + newFiltered.prepend(g->filtered[currentRow]); + }else{ + if(deleteUntil == -1) + deleteUntil = currentRow; //Mark that this row needs to be deleted + } + } + + if(deleteUntil != -1 && notifyModel) { + beginRemoveRows(indexForGroup(g), 0, deleteUntil); + endRemoveRows(); + } + + g->filtered = newFiltered; + hideOrShowGroup(g, notifyModel); +} + +int KateCompletionModel::Group::orderNumber() const { + if( this == model->m_ungrouped ) + return 700; + + if(customSortingKey != -1) + return customSortingKey; + + if( attribute & BestMatchesProperty ) + return 1; + + if (attribute & KTextEditor::CodeCompletionModel::LocalScope) + return 100; + else if (attribute & KTextEditor::CodeCompletionModel::Public) + return 200; + else if (attribute & KTextEditor::CodeCompletionModel::Protected) + return 300; + else if (attribute & KTextEditor::CodeCompletionModel::Private) + return 400; + else if (attribute & KTextEditor::CodeCompletionModel::NamespaceScope) + return 500; + else if (attribute & KTextEditor::CodeCompletionModel::GlobalScope) + return 600; + + + return 700; +} + +bool KateCompletionModel::Group::orderBefore(Group* other) const { + return orderNumber() < other->orderNumber(); +} + +void KateCompletionModel::hideOrShowGroup(Group* g, bool notifyModel) +{ + if( g == m_argumentHints ) { + emit argumentHintsChanged(); + m_updateBestMatchesTimer->start(200); //We have new argument-hints, so we have new best matches + return; //Never show argument-hints in the normal completion-list + } + + if (!g->isEmpty) { + if (g->filtered.isEmpty()) { + // Move to empty group list + g->isEmpty = true; + int row = m_rowTable.indexOf(g); + if (row != -1) { + if (hasGroups() && notifyModel) + beginRemoveRows(QModelIndex(), row, row); + m_rowTable.removeAt(row); + if (hasGroups() && notifyModel) + endRemoveRows(); + m_emptyGroups.append(g); + } else { + kWarning() << "Group " << g << " not found in row table!!"; + } + } + + } else { + + if (!g->filtered.isEmpty()) { + // Move off empty group list + g->isEmpty = false; + + int row = 0; //Find row where to insert + for( int a = 0; a < m_rowTable.count(); a++ ) { + if( g->orderBefore(m_rowTable[a]) ) { + row = a; + break; + } + row = a+1; + } + + if(notifyModel) { + if (hasGroups()) + beginInsertRows(QModelIndex(), row, row); + else + beginInsertRows(QModelIndex(), 0, g->filtered.count()); + } + m_rowTable.insert(row, g); + if(notifyModel) + endInsertRows(); + m_emptyGroups.removeAll(g); + } + } +} + +bool KateCompletionModel::indexIsItem( const QModelIndex & index ) const +{ + if (!hasGroups()) + return true; + + if (groupOfParent(index)) + return true; + + return false; +} + +void KateCompletionModel::slotModelReset() +{ + createGroups(); + + //debugStats(); +} + +void KateCompletionModel::debugStats() +{ + if (!hasGroups()) + kDebug( 13035 ) << "Model groupless, " << m_ungrouped->filtered.count() << " items."; + else { + kDebug( 13035 ) << "Model grouped (" << m_rowTable.count() << " groups):"; + foreach (Group* g, m_rowTable) + kDebug( 13035 ) << "Group" << g << "count" << g->filtered.count(); + } +} + +bool KateCompletionModel::hasCompletionModel( ) const +{ + return !m_completionModels.isEmpty(); +} + +void KateCompletionModel::setFilteringEnabled( bool enable ) +{ + if (m_filteringEnabled != enable) + m_filteringEnabled = enable; +} + +void KateCompletionModel::setSortingEnabled( bool enable ) +{ + if (m_sortingEnabled != enable) { + m_sortingEnabled = enable; + beginResetModel(); + resort(); + endResetModel(); + } +} + +void KateCompletionModel::setGroupingEnabled(bool enable) +{ + if (m_groupingEnabled != enable) + m_groupingEnabled = enable; +} + +void KateCompletionModel::setColumnMergingEnabled(bool enable) +{ + if (m_columnMergingEnabled != enable) + m_columnMergingEnabled = enable; +} + +bool KateCompletionModel::isColumnMergingEnabled( ) const +{ + return m_columnMergingEnabled; +} + +bool KateCompletionModel::isGroupingEnabled( ) const +{ + return m_groupingEnabled; +} + +bool KateCompletionModel::isFilteringEnabled( ) const +{ + return m_filteringEnabled; +} + +bool KateCompletionModel::isSortingEnabled( ) const +{ + return m_sortingEnabled; +} + +QString KateCompletionModel::columnName( int column ) +{ + switch (column) { + case KTextEditor::CodeCompletionModel::Prefix: + return i18n("Prefix"); + case KTextEditor::CodeCompletionModel::Icon: + return i18n("Icon"); + case KTextEditor::CodeCompletionModel::Scope: + return i18n("Scope"); + case KTextEditor::CodeCompletionModel::Name: + return i18n("Name"); + case KTextEditor::CodeCompletionModel::Arguments: + return i18n("Arguments"); + case KTextEditor::CodeCompletionModel::Postfix: + return i18n("Postfix"); + } + + return QString(); +} + +const QList< QList < int > > & KateCompletionModel::columnMerges( ) const +{ + return m_columnMerges; +} + +void KateCompletionModel::setColumnMerges( const QList< QList < int > > & columnMerges ) +{ + beginResetModel(); + m_columnMerges = columnMerges; + endResetModel(); +} + +int KateCompletionModel::translateColumn( int sourceColumn ) const +{ + if (m_columnMerges.isEmpty()) + return sourceColumn; + + /* Debugging - dump column merge list + + QString columnMerge; + foreach (const QList& list, m_columnMerges) { + columnMerge += '['; + foreach (int column, list) { + columnMerge += QString::number(column) + " "; + } + columnMerge += "] "; + } + + kDebug( 13035 ) << columnMerge;*/ + + int c = 0; + foreach (const QList& list, m_columnMerges) { + foreach (int column, list) { + if (column == sourceColumn) + return c; + } + c++; + } + return -1; +} + +int KateCompletionModel::groupingAttributes( int attribute ) const +{ + int ret = 0; + + if (m_groupingMethod & ScopeType) { + if (countBits(attribute & ScopeTypeMask) > 1) + kWarning() << "Invalid completion model metadata: more than one scope type modifier provided."; + + if (attribute & KTextEditor::CodeCompletionModel::GlobalScope) + ret |= KTextEditor::CodeCompletionModel::GlobalScope; + else if (attribute & KTextEditor::CodeCompletionModel::NamespaceScope) + ret |= KTextEditor::CodeCompletionModel::NamespaceScope; + else if (attribute & KTextEditor::CodeCompletionModel::LocalScope) + ret |= KTextEditor::CodeCompletionModel::LocalScope; + } + + if (m_groupingMethod & AccessType) { + if (countBits(attribute & AccessTypeMask) > 1) + kWarning() << "Invalid completion model metadata: more than one access type modifier provided."; + + if (attribute & KTextEditor::CodeCompletionModel::Public) + ret |= KTextEditor::CodeCompletionModel::Public; + else if (attribute & KTextEditor::CodeCompletionModel::Protected) + ret |= KTextEditor::CodeCompletionModel::Protected; + else if (attribute & KTextEditor::CodeCompletionModel::Private) + ret |= KTextEditor::CodeCompletionModel::Private; + + if (accessIncludeStatic() && attribute & KTextEditor::CodeCompletionModel::Static) + ret |= KTextEditor::CodeCompletionModel::Static; + + if (accessIncludeConst() && attribute & KTextEditor::CodeCompletionModel::Const) + ret |= KTextEditor::CodeCompletionModel::Const; + } + + if (m_groupingMethod & ItemType) { + if (countBits(attribute & ItemTypeMask) > 1) + kWarning() << "Invalid completion model metadata: more than one item type modifier provided."; + + if (attribute & KTextEditor::CodeCompletionModel::Namespace) + ret |= KTextEditor::CodeCompletionModel::Namespace; + else if (attribute & KTextEditor::CodeCompletionModel::Class) + ret |= KTextEditor::CodeCompletionModel::Class; + else if (attribute & KTextEditor::CodeCompletionModel::Struct) + ret |= KTextEditor::CodeCompletionModel::Struct; + else if (attribute & KTextEditor::CodeCompletionModel::Union) + ret |= KTextEditor::CodeCompletionModel::Union; + else if (attribute & KTextEditor::CodeCompletionModel::Function) + ret |= KTextEditor::CodeCompletionModel::Function; + else if (attribute & KTextEditor::CodeCompletionModel::Variable) + ret |= KTextEditor::CodeCompletionModel::Variable; + else if (attribute & KTextEditor::CodeCompletionModel::Enum) + ret |= KTextEditor::CodeCompletionModel::Enum; + + /* + if (itemIncludeTemplate() && attribute & KTextEditor::CodeCompletionModel::Template) + ret |= KTextEditor::CodeCompletionModel::Template;*/ + } + + return ret; +} + +void KateCompletionModel::setGroupingMethod( GroupingMethods m ) +{ + m_groupingMethod = m; + + createGroups(); +} + +bool KateCompletionModel::accessIncludeConst( ) const +{ + return m_accessConst; +} + +void KateCompletionModel::setAccessIncludeConst( bool include ) +{ + if (m_accessConst != include) { + m_accessConst = include; + + if (groupingMethod() & AccessType) + createGroups(); + } +} + +bool KateCompletionModel::accessIncludeStatic( ) const +{ + return m_accessStatic; +} + +void KateCompletionModel::setAccessIncludeStatic( bool include ) +{ + if (m_accessStatic != include) { + m_accessStatic = include; + + if (groupingMethod() & AccessType) + createGroups(); + } +} + +bool KateCompletionModel::accessIncludeSignalSlot( ) const +{ + return m_accesSignalSlot; +} + +void KateCompletionModel::setAccessIncludeSignalSlot( bool include ) +{ + if (m_accesSignalSlot != include) { + m_accesSignalSlot = include; + + if (groupingMethod() & AccessType) + createGroups(); + } +} + +int KateCompletionModel::countBits( int value ) const +{ + int count = 0; + for (int i = 1; i; i <<= 1) + if (i & value) + count++; + + return count; +} + +KateCompletionModel::GroupingMethods KateCompletionModel::groupingMethod( ) const +{ + return m_groupingMethod; +} + +bool KateCompletionModel::isSortingByInheritanceDepth() const { + return m_isSortingByInheritance; +} +void KateCompletionModel::setSortingByInheritanceDepth(bool byInheritance) { + m_isSortingByInheritance = byInheritance; +} + +bool KateCompletionModel::isSortingAlphabetical( ) const +{ + return m_sortingAlphabetical; +} + +Qt::CaseSensitivity KateCompletionModel::sortingCaseSensitivity( ) const +{ + return m_sortingCaseSensitivity; +} + +KateCompletionModel::Item::Item( bool doInitialMatch, KateCompletionModel* m, const HierarchicalModelHandler& handler, ModelRow sr ) + : model(m) + , m_sourceRow(sr) + , matchCompletion(StartsWithMatch) + , matchFilters(true) + , m_haveExactMatch(false) +{ + + inheritanceDepth = handler.getData(CodeCompletionModel::InheritanceDepth, m_sourceRow.second).toInt(); + + QModelIndex nameSibling = sr.second.sibling(sr.second.row(), CodeCompletionModel::Name); + m_nameColumn = nameSibling.data(Qt::DisplayRole).toString(); + + if(doInitialMatch) { + filter(); + match(); + } +} + +bool KateCompletionModel::Item::operator <( const Item & rhs ) const +{ + int ret = 0; + + //kDebug( 13035 ) << c1 << " c/w " << c2 << " -> " << (model->isSortingReverse() ? ret > 0 : ret < 0) << " (" << ret << ")"; + + const bool isBad = m_sourceRow.second.data(CodeCompletionModel::UnimportantItemRole).toBool(); + const bool otherIsBad = rhs.m_sourceRow.second.data(CodeCompletionModel::UnimportantItemRole).toBool(); + if( isBad && !otherIsBad ) { + return false; + } + if( otherIsBad && !isBad ) { + return true; + } + + if( matchCompletion < rhs.matchCompletion ) { + // enums are ordered in the order items should be displayed + return true; + } + if( matchCompletion > rhs.matchCompletion ) { + return false; + } + + if( model->isSortingByInheritanceDepth() ) + ret = inheritanceDepth - rhs.inheritanceDepth; + + if (ret == 0 && model->isSortingAlphabetical()) { + // Do not use localeAwareCompare, because it is simply too slow for a list of about 1000 items + ret = QString::compare(m_nameColumn, rhs.m_nameColumn, model->sortingCaseSensitivity()); + } + + if( ret == 0 ) { + const QString& filter = rhs.model->currentCompletion(rhs.m_sourceRow.first); + if( m_nameColumn.startsWith(filter, Qt::CaseSensitive) ) { + return true; + } + if( rhs.m_nameColumn.startsWith(filter, Qt::CaseSensitive) ) { + return false; + } + // FIXME need to define a better default ordering for multiple model display + ret = m_sourceRow.second.row() - rhs.m_sourceRow.second.row(); + } + + return ret < 0; +} + +void KateCompletionModel::Group::addItem( Item i, bool notifyModel ) +{ + if (isEmpty) + notifyModel = false; + + QModelIndex groupIndex; + if (notifyModel) + groupIndex = model->indexForGroup(this); + + if (model->isSortingEnabled()) { + + prefilter.insert(qUpperBound(prefilter.begin(), prefilter.end(), i), i); + if(i.isVisible()) { + QList::iterator it = qUpperBound(filtered.begin(), filtered.end(), i); + uint rowNumber = it - filtered.begin(); + + if(notifyModel) + model->beginInsertRows(groupIndex, rowNumber, rowNumber); + + filtered.insert(it, i); + } + } else { + if(notifyModel) + model->beginInsertRows(groupIndex, prefilter.size(), prefilter.size()); + if (i.isVisible()) + prefilter.append(i); + } + + if(notifyModel) + model->endInsertRows(); +} + +bool KateCompletionModel::Group::removeItem(const ModelRow& row) +{ + for (int pi = 0; pi < prefilter.count(); ++pi) + if (prefilter[pi].sourceRow() == row) { + int index = rowOf(row); + if (index != -1) + model->beginRemoveRows(model->indexForGroup(this), index, index); + + filtered.removeAt(index); + prefilter.removeAt(pi); + + if (index != -1) + model->endRemoveRows(); + + return index != -1; + } + + Q_ASSERT(false); + return false; +} + +KateCompletionModel::Group::Group( KateCompletionModel * m ) + : model(m) + , isEmpty(true) + , customSortingKey(-1) +{ + Q_ASSERT(model); +} + +void KateCompletionModel::setSortingAlphabetical( bool alphabetical ) +{ + if (m_sortingAlphabetical != alphabetical) { + m_sortingAlphabetical = alphabetical; + beginResetModel(); + resort(); + endResetModel(); + } +} + +void KateCompletionModel::Group::resort( ) +{ + qStableSort(filtered.begin(), filtered.end()); + model->hideOrShowGroup(this); +} + +void KateCompletionModel::setSortingCaseSensitivity( Qt::CaseSensitivity cs ) +{ + if (m_sortingCaseSensitivity != cs) { + m_sortingCaseSensitivity = cs; + beginResetModel(); + resort(); + endResetModel(); + } +} + +void KateCompletionModel::resort() +{ + foreach (Group* g, m_rowTable) + g->resort(); + + foreach (Group* g, m_emptyGroups) + g->resort(); + + // call updateBestMatches here, so they are moved to the top again. + updateBestMatches(); +} + +bool KateCompletionModel::Item::isValid( ) const +{ + return model && m_sourceRow.first && m_sourceRow.second.row() >= 0; +} + +void KateCompletionModel::Group::clear( ) +{ + prefilter.clear(); + filtered.clear(); + isEmpty = true; +} + +bool KateCompletionModel::filterContextMatchesOnly( ) const +{ + return m_filterContextMatchesOnly; +} + +void KateCompletionModel::setFilterContextMatchesOnly( bool filter ) +{ + if (m_filterContextMatchesOnly != filter) { + m_filterContextMatchesOnly = filter; + refilter(); + } +} + +bool KateCompletionModel::filterByAttribute( ) const +{ + return m_filterByAttribute; +} + +void KateCompletionModel::setFilterByAttribute( bool filter ) +{ + if (m_filterByAttribute == filter) { + m_filterByAttribute = filter; + refilter(); + } +} + +KTextEditor::CodeCompletionModel::CompletionProperties KateCompletionModel::filterAttributes( ) const +{ + return m_filterAttributes; +} + +void KateCompletionModel::setFilterAttributes( KTextEditor::CodeCompletionModel::CompletionProperties attributes ) +{ + if (m_filterAttributes == attributes) { + m_filterAttributes = attributes; + refilter(); + } +} + +int KateCompletionModel::maximumInheritanceDepth( ) const +{ + return m_maximumInheritanceDepth; +} + +void KateCompletionModel::setMaximumInheritanceDepth( int maxDepth ) +{ + if (m_maximumInheritanceDepth != maxDepth) { + m_maximumInheritanceDepth = maxDepth; + refilter(); + } +} + +void KateCompletionModel::refilter( ) +{ + beginResetModel(); + m_ungrouped->refilter(); + + foreach (Group* g, m_rowTable) + if(g != m_argumentHints) + g->refilter(); + + foreach (Group* g, m_emptyGroups) + if(g != m_argumentHints) + g->refilter(); + + updateBestMatches(); + + clearExpanding(); //We need to do this, or be aware of expanding-widgets while filtering. + endResetModel(); +} + +void KateCompletionModel::Group::refilter( ) +{ + filtered.clear(); + foreach (const Item& i, prefilter) + if (!i.isFiltered()) + filtered.append(i); +} + +bool KateCompletionModel::Item::filter( ) +{ + matchFilters = false; + + if (model->isFilteringEnabled()) { + QModelIndex sourceIndex = m_sourceRow.second.sibling(m_sourceRow.second.row(), CodeCompletionModel::Name); + + if (model->filterContextMatchesOnly()) { + QVariant contextMatch = sourceIndex.data(CodeCompletionModel::MatchQuality); + if (contextMatch.canConvert(QVariant::Int) && !contextMatch.toInt()) + goto filter; + } + + if (model->filterByAttribute()) { + int completionFlags = sourceIndex.data(CodeCompletionModel::CompletionRole).toInt(); + if (model->filterAttributes() & completionFlags) + goto filter; + } + + if (model->maximumInheritanceDepth() > 0) { + int inheritanceDepth = sourceIndex.data(CodeCompletionModel::InheritanceDepth).toInt(); + if (inheritanceDepth > model->maximumInheritanceDepth()) + goto filter; + } + } + + matchFilters = true; + + filter: + return matchFilters; +} + +uint KateCompletionModel::filteredItemCount() const +{ + uint ret = 0; + foreach(Group* group, m_rowTable) + ret += group->filtered.size(); + + return ret; +} + +bool KateCompletionModel::shouldMatchHideCompletionList() const { + + // @todo Make this faster + + bool doHide = false; + CodeCompletionModel* hideModel = 0; + + foreach(Group* group, m_rowTable) + foreach(const Item& item, group->filtered) + if(item.haveExactMatch()) { + KTextEditor::CodeCompletionModelControllerInterface* iface3 = dynamic_cast(item.sourceRow().first); + bool hide = false; + if ( !iface3 ) hide = true; + if(iface3 && iface3->matchingItem(item.sourceRow().second) == KTextEditor::CodeCompletionModelControllerInterface::HideListIfAutomaticInvocation) + hide = true; + if(hide) + { + doHide = true; + hideModel = item.sourceRow().first; + } + } + + if(doHide) + { + // Check if all other visible items are from the same model + foreach(Group* group, m_rowTable) + foreach(const Item& item, group->filtered) + if(item.sourceRow().first != hideModel) + return false; + } + + return doHide; +} + +static inline bool matchesAbbreviationHelper(const QString& word, const QString& typed, const QVarLengthArray& offsets, + int& depth, int atWord = -1, int i = 0) { + int atLetter = 1; + for ( ; i < typed.size(); i++ ) { + const QChar c = typed.at(i).toLower(); + bool haveNextWord = offsets.size() > atWord + 1; + bool canCompare = atWord != -1 && word.size() > offsets.at(atWord) + atLetter; + if ( canCompare && c == word.at(offsets.at(atWord) + atLetter).toLower() ) { + // the typed letter matches a letter after the current word beginning + if ( ! haveNextWord || c != word.at(offsets.at(atWord + 1)).toLower() ) { + // good, simple case, no conflict + atLetter += 1; + continue; + } + // For maliciously crafted data, the code used here theoretically can have very high + // complexity. Thus ensure we don't run into this case, by limiting the amount of branches + // we walk through to 128. + depth++; + if ( depth > 128 ) { + return false; + } + // the letter matches both the next word beginning and the next character in the word + if ( haveNextWord && matchesAbbreviationHelper(word, typed, offsets, depth, atWord + 1, i + 1) ) { + // resolving the conflict by taking the next word's first character worked, fine + return true; + } + // otherwise, continue by taking the next letter in the current word. + atLetter += 1; + continue; + } + else if ( haveNextWord && c == word.at(offsets.at(atWord + 1)).toLower() ) { + // the typed letter matches the next word beginning + atWord++; + atLetter = 1; + continue; + } + // no match + return false; + } + // all characters of the typed word were matched + return true; +} + +bool KateCompletionModel::matchesAbbreviation(const QString& word, const QString& typed) +{ + // A mismatch is very likely for random even for the first letter, + // thus this optimization makes sense. + if ( word.at(0).toLower() != typed.at(0).toLower() ) { + return false; + } + + // First, check if all letters are contained in the word in the right order. + int atLetter = 0; + foreach ( const QChar c, typed ) { + while ( c.toLower() != word.at(atLetter).toLower() ) { + atLetter += 1; + if ( atLetter >= word.size() ) { + return false; + } + } + } + + bool haveUnderscore = true; + QVarLengthArray offsets; + // We want to make "KComplM" match "KateCompletionModel"; this means we need + // to allow parts of the typed text to be not part of the actual abbreviation, + // which consists only of the uppercased / underscored letters (so "KCM" in this case). + // However it might be ambigous whether a letter is part of such a word or part of + // the following abbreviation, so we need to find all possible word offsets first, + // then compare. + for ( int i = 0; i < word.size(); i++ ) { + const QChar c = word.at(i); + if ( c == QLatin1Char('_') ) { + haveUnderscore = true; + } else if ( haveUnderscore || c.isUpper() ) { + offsets.append(i); + haveUnderscore = false; + } + } + int depth = 0; + return matchesAbbreviationHelper(word, typed, offsets, depth); +} + +static inline bool containsAtWordBeginning(const QString& word, const QString& typed, Qt::CaseSensitivity caseSensitive) { + for(int i = 1; i < word.size(); i++) { + // The current position is a word beginning if the previous character was an underscore + // or if the current character is uppercase. Subsequent uppercase characters do not count, + // to handle the special case of UPPER_CASE_VARS properly. + const QChar c = word.at(i); + const QChar prev = word.at(i-1); + if(!(prev == QLatin1Char('_') || (c.isUpper() && !prev.isUpper()))) { + continue; + } + if(word.midRef(i).startsWith(typed, caseSensitive)) { + return true; + } + } + return false; +}; + +KateCompletionModel::Item::MatchType KateCompletionModel::Item::match() +{ + // Check to see if the item is matched by the current completion string + QModelIndex sourceIndex = m_sourceRow.second.sibling(m_sourceRow.second.row(), CodeCompletionModel::Name); + + QString match = model->currentCompletion(m_sourceRow.first); + + m_haveExactMatch = false; + + // Hehe, everything matches nothing! (ie. everything matches a blank string) + if (match.isEmpty()) + return PerfectMatch; + if (m_nameColumn.isEmpty()) + return NoMatch; + + matchCompletion = (m_nameColumn.startsWith(match, model->matchCaseSensitivity()) ? StartsWithMatch : NoMatch); + if(matchCompletion == NoMatch) { + // if no match, try for "contains" + // Only match when the occurence is at a "word" beginning, marked by + // an underscore or a capital. So Foo matches BarFoo and Bar_Foo, but not barfoo. + // Starting at 1 saves looking at the beginning of the word, that was already checked above. + if(containsAtWordBeginning(m_nameColumn, match, model->matchCaseSensitivity())) { + matchCompletion = ContainsMatch; + } + } + + if(matchCompletion == NoMatch && !m_nameColumn.isEmpty() && !match.isEmpty()) + { + // if still no match, try abbreviation matching + if(matchesAbbreviation(m_nameColumn, match)) { + matchCompletion = AbbreviationMatch; + } + } + + if(matchCompletion && match.length() == m_nameColumn.length()) { + matchCompletion = PerfectMatch; + m_haveExactMatch = true; + } + + return matchCompletion; +} + +QString KateCompletionModel::propertyName( KTextEditor::CodeCompletionModel::CompletionProperty property ) +{ + switch (property) { + case CodeCompletionModel::Public: + return i18n("Public"); + + case CodeCompletionModel::Protected: + return i18n("Protected"); + + case CodeCompletionModel::Private: + return i18n("Private"); + + case CodeCompletionModel::Static: + return i18n("Static"); + + case CodeCompletionModel::Const: + return i18n("Constant"); + + case CodeCompletionModel::Namespace: + return i18n("Namespace"); + + case CodeCompletionModel::Class: + return i18n("Class"); + + case CodeCompletionModel::Struct: + return i18n("Struct"); + + case CodeCompletionModel::Union: + return i18n("Union"); + + case CodeCompletionModel::Function: + return i18n("Function"); + + case CodeCompletionModel::Variable: + return i18n("Variable"); + + case CodeCompletionModel::Enum: + return i18n("Enumeration"); + + case CodeCompletionModel::Template: + return i18n("Template"); + + case CodeCompletionModel::Virtual: + return i18n("Virtual"); + + case CodeCompletionModel::Override: + return i18n("Override"); + + case CodeCompletionModel::Inline: + return i18n("Inline"); + + case CodeCompletionModel::Friend: + return i18n("Friend"); + + case CodeCompletionModel::Signal: + return i18n("Signal"); + + case CodeCompletionModel::Slot: + return i18n("Slot"); + + case CodeCompletionModel::LocalScope: + return i18n("Local Scope"); + + case CodeCompletionModel::NamespaceScope: + return i18n("Namespace Scope"); + + case CodeCompletionModel::GlobalScope: + return i18n("Global Scope"); + + default: + return i18n("Unknown Property"); + } +} + +bool KateCompletionModel::Item::isVisible( ) const +{ + return matchCompletion && matchFilters; +} + +bool KateCompletionModel::Item::isFiltered( ) const +{ + return !matchFilters; +} + +bool KateCompletionModel::Item::isMatching( ) const +{ + return matchFilters; +} + +const KateCompletionModel::ModelRow& KateCompletionModel::Item::sourceRow( ) const +{ + return m_sourceRow; +} + +QString KateCompletionModel::currentCompletion( KTextEditor::CodeCompletionModel* model ) const +{ + return m_currentMatch.value(model); +} + +Qt::CaseSensitivity KateCompletionModel::matchCaseSensitivity( ) const +{ + return m_matchCaseSensitivity; +} + +void KateCompletionModel::addCompletionModel(KTextEditor::CodeCompletionModel * model) +{ + if (m_completionModels.contains(model)) + return; + + m_completionModels.append(model); + + connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)), SLOT(slotRowsInserted(QModelIndex,int,int))); + connect(model, SIGNAL(rowsRemoved(QModelIndex,int,int)), SLOT(slotRowsRemoved(QModelIndex,int,int))); + connect(model, SIGNAL(modelReset()), SLOT(slotModelReset())); + + // This performs the reset + createGroups(); +} + +void KateCompletionModel::setCompletionModel(KTextEditor::CodeCompletionModel* model) +{ + clearCompletionModels(); + addCompletionModel(model); +} + +void KateCompletionModel::setCompletionModels(const QList& models) +{ + //if (m_completionModels == models) + //return; + + clearCompletionModels(); + + m_completionModels = models; + + foreach (KTextEditor::CodeCompletionModel* model, models) { + connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)), SLOT(slotRowsInserted(QModelIndex,int,int))); + connect(model, SIGNAL(rowsRemoved(QModelIndex,int,int)), SLOT(slotRowsRemoved(QModelIndex,int,int))); + connect(model, SIGNAL(modelReset()), SLOT(slotModelReset())); + } + + // This performs the reset + createGroups(); +} + +QList< KTextEditor::CodeCompletionModel * > KateCompletionModel::completionModels() const +{ + return m_completionModels; +} + +void KateCompletionModel::removeCompletionModel(CodeCompletionModel * model) +{ + if (!model || !m_completionModels.contains(model)) + return; + + beginResetModel(); + m_currentMatch.remove(model); + + clearGroups(); + + model->disconnect(this); + + m_completionModels.removeAll(model); + endResetModel(); + + if (!m_completionModels.isEmpty()) { + // This performs the reset + createGroups(); + } +} + +void KateCompletionModel::makeGroupItemsUnique(bool onlyFiltered) +{ + struct FilterItems { + FilterItems(KateCompletionModel& model, const QVector& needShadowing) : m_model(model), m_needShadowing(needShadowing) { + } + + QHash had; + KateCompletionModel& m_model; + const QVector< KTextEditor::CodeCompletionModel* > m_needShadowing; + + void filter(QList& items) + { + QList temp; + foreach(const Item& item, items) + { + QHash::const_iterator it = had.constFind(item.name()); + if(it != had.constEnd() && *it != item.sourceRow().first && m_needShadowing.contains(item.sourceRow().first)) + continue; + had.insert(item.name(), item.sourceRow().first); + temp.push_back(item); + } + items = temp; + } + + void filter(Group* group, bool onlyFiltered) + { + if(group->prefilter.size() == group->filtered.size()) + { + // Filter only once + filter(group->filtered); + if(!onlyFiltered) + group->prefilter = group->filtered; + }else{ + // Must filter twice + filter(group->filtered); + if(!onlyFiltered) + filter(group->prefilter); + } + + if(group->filtered.isEmpty()) + m_model.hideOrShowGroup(group); + + } + }; + + QVector needShadowing; + + foreach(KTextEditor::CodeCompletionModel* model, m_completionModels) + { + KTextEditor::CodeCompletionModelControllerInterface4* v4 = dynamic_cast(model); + if(v4 && v4->shouldHideItemsWithEqualNames()) + needShadowing.push_back(model); + } + + if(needShadowing.isEmpty()) + return; + + FilterItems filter(*this, needShadowing); + + filter.filter(m_ungrouped, onlyFiltered); + + foreach(Group* group, m_rowTable) + filter.filter(group, onlyFiltered); +} + + +//Updates the best-matches group +void KateCompletionModel::updateBestMatches() { + int maxMatches = 300; //We cannot do too many operations here, because they are all executed whenever a character is added. Would be nice if we could split the operations up somewhat using a timer. + + m_updateBestMatchesTimer->stop(); + //Maps match-qualities to ModelRows paired together with the BestMatchesCount returned by the items. + typedef QMultiMap > BestMatchMap; + BestMatchMap matches; + + if(!hasGroups()) { + //If there is no grouping, just change the order of the items, moving the best matching ones to the front + QMultiMap rowsForQuality; + + int row = 0; + foreach(const Item& item, m_ungrouped->filtered) { + ModelRow source = item.sourceRow(); + + QVariant v = source.second.data(CodeCompletionModel::BestMatchesCount); + + if( v.type() == QVariant::Int && v.toInt() > 0 ) { + int quality = contextMatchQuality(source); + if(quality > 0) + rowsForQuality.insert(quality, row); + } + + ++row; + --maxMatches; + if(maxMatches < 0) + break; + } + + if(!rowsForQuality.isEmpty()) { + //Rewrite m_ungrouped->filtered in a new order + QSet movedToFront; + QList newFiltered; + for(QMultiMap::const_iterator it = rowsForQuality.constBegin(); it != rowsForQuality.constEnd(); ++it) { + newFiltered.prepend(m_ungrouped->filtered[it.value()]); + movedToFront.insert(it.value()); + } + + { + uint size = m_ungrouped->filtered.size(); + for(uint a = 0; a < size; ++a) + if(!movedToFront.contains(a)) + newFiltered.append(m_ungrouped->filtered[a]); + } + m_ungrouped->filtered = newFiltered; + } + return; + } + + ///@todo Cache the CodeCompletionModel::BestMatchesCount + foreach (Group* g, m_rowTable) { + if( g == m_bestMatches ) + continue; + for( int a = 0; a < g->filtered.size(); a++ ) + { + ModelRow source = g->filtered[a].sourceRow(); + + QVariant v = source.second.data(CodeCompletionModel::BestMatchesCount); + + if( v.type() == QVariant::Int && v.toInt() > 0 ) { + //Return the best match with any of the argument-hints + + int quality = contextMatchQuality(source); + if( quality > 0 ) + matches.insert(quality, qMakePair(v.toInt(), g->filtered[a].sourceRow())); + --maxMatches; + } + + if( maxMatches < 0 ) + break; + } + if( maxMatches < 0 ) + break; + } + + //Now choose how many of the matches will be taken. This is done with the rule: + //The count of shown best-matches should equal the average count of their BestMatchesCounts + int cnt = 0; + int matchesSum = 0; + BestMatchMap::const_iterator it = matches.constEnd(); + while( it != matches.constBegin() ) + { + --it; + ++cnt; + matchesSum += (*it).first; + if( cnt > matchesSum / cnt ) + break; + } + + m_bestMatches->filtered.clear(); + + it = matches.constEnd(); + + while( it != matches.constBegin() && cnt > 0 ) + { + --it; + --cnt; + + m_bestMatches->filtered.append( Item( true, this, HierarchicalModelHandler((*it).second.first), (*it).second) ); + } + + hideOrShowGroup(m_bestMatches); +} + +void KateCompletionModel::rowSelected(const QModelIndex& row) { + ExpandingWidgetModel::rowSelected(row); + ///@todo delay this + int rc = widget()->argumentHintModel()->rowCount(QModelIndex()); + if( rc == 0 ) return; + + //For now, simply update the whole column 0 + QModelIndex start = widget()->argumentHintModel()->index(0,0); + QModelIndex end = widget()->argumentHintModel()->index(rc-1,0); + + widget()->argumentHintModel()->emitDataChanged(start, end); +} + +void KateCompletionModel::clearCompletionModels() +{ + if (m_completionModels.isEmpty()) + return; + + beginResetModel(); + foreach (CodeCompletionModel * model, m_completionModels) + model->disconnect(this); + + m_completionModels.clear(); + + m_currentMatch.clear(); + + clearGroups(); + endResetModel(); +} + +#include "moc_katecompletionmodel.cpp" +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/completion/katecompletionmodel.h b/kate/part/completion/katecompletionmodel.h new file mode 100644 index 00000000..7bd44fdc --- /dev/null +++ b/kate/part/completion/katecompletionmodel.h @@ -0,0 +1,406 @@ +/* This file is part of the KDE libraries and the Kate part. + * + * Copyright (C) 2005-2006 Hamish Rodda + * Copyright (C) 2007-2008 David Nolden + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef KATECOMPLETIONMODEL_H +#define KATECOMPLETIONMODEL_H + +#include +#include +#include + +#include + +#include "katepartinterfaces_export.h" +#include "expandingtree/expandingwidgetmodel.h" + +class KateCompletionWidget; +class KateArgumentHintModel; +class KateView; +#include +#include +#include +class HierarchicalModelHandler; + +/** + * This class has the responsibility for filtering, sorting, and manipulating + * code completion data provided by a CodeCompletionModel. + * + * @author Hamish Rodda + */ +class KATEPARTINTERFACES_EXPORT KateCompletionModel : public ExpandingWidgetModel +{ + Q_OBJECT + + public: + KateCompletionModel(KateCompletionWidget* parent = 0L); + ~KateCompletionModel(); + + QList completionModels() const; + void clearCompletionModels(); + void addCompletionModel(KTextEditor::CodeCompletionModel* model); + void setCompletionModel(KTextEditor::CodeCompletionModel* model); + void setCompletionModels(const QList& models); + void removeCompletionModel(KTextEditor::CodeCompletionModel* model); + + KateView* view() const; + KateCompletionWidget* widget() const; + + QString currentCompletion(KTextEditor::CodeCompletionModel* model) const; + void setCurrentCompletion(KTextEditor::CodeCompletionModel* model, const QString& completion); + + Qt::CaseSensitivity matchCaseSensitivity() const; + void setMatchCaseSensitivity( Qt::CaseSensitivity cs ); + + static QString columnName(int column); + int translateColumn(int sourceColumn) const; + + static QString propertyName(KTextEditor::CodeCompletionModel::CompletionProperty property); + + ///Returns a common prefix for all current visible completion entries + ///If there is no common prefix, extracts the next useful prefix for the selected index + QString commonPrefix(QModelIndex selectedIndex) const; + + virtual void rowSelected(const QModelIndex& row); + + virtual bool indexIsItem(const QModelIndex& index) const; + + virtual int columnCount ( const QModelIndex & parent = QModelIndex() ) const; + virtual QVariant data ( const QModelIndex & index, int role = Qt::DisplayRole ) const; + virtual Qt::ItemFlags flags ( const QModelIndex & index ) const; + virtual bool hasChildren ( const QModelIndex & parent = QModelIndex() ) const; + virtual bool hasIndex ( int row, int column, const QModelIndex & parent = QModelIndex() ) const; + virtual QModelIndex index ( int row, int column, const QModelIndex & parent = QModelIndex() ) const; + + // Disabled in case of bugs, reenable once fully debugged. + //virtual QMap itemData ( const QModelIndex & index ) const; + virtual QModelIndex parent ( const QModelIndex & index ) const; + virtual int rowCount ( const QModelIndex & parent = QModelIndex() ) const; + + // Disabled in case of bugs, reenable once fully debugged. + //virtual QModelIndex sibling ( int row, int column, const QModelIndex & index ) const; + virtual void sort ( int column, Qt::SortOrder order = Qt::AscendingOrder ); + + ///Maps from this display-model into the appropriate source code-completion model + virtual QModelIndex mapToSource(const QModelIndex &proxyIndex) const; + + ///Maps from an index in a source-model to the index of the item in this display-model + virtual QModelIndex mapFromSource(const QModelIndex &sourceIndex) const; + + // Sorting + bool isSortingEnabled() const; + bool isSortingAlphabetical() const; + bool isSortingByInheritanceDepth() const; + void setSortingByInheritanceDepth(bool byIneritance); + void setSortingAlphabetical(bool alphabetical); + + Qt::CaseSensitivity sortingCaseSensitivity() const; + void setSortingCaseSensitivity(Qt::CaseSensitivity cs); + + bool isSortingReverse() const; + void setSortingReverse(bool reverse); + + // Filtering + bool isFilteringEnabled() const; + + bool filterContextMatchesOnly() const; + void setFilterContextMatchesOnly(bool filter); + + bool filterByAttribute() const; + void setFilterByAttribute(bool filter); + + KTextEditor::CodeCompletionModel::CompletionProperties filterAttributes() const; + void setFilterAttributes(KTextEditor::CodeCompletionModel::CompletionProperties attributes); + + // A maximum depth of <= 0 equals don't filter by inheritance depth (i.e. infinity) and is default + int maximumInheritanceDepth() const; + void setMaximumInheritanceDepth(int maxDepth); + + // Grouping + bool isGroupingEnabled() const; + + enum gm { + ScopeType = 0x1, + Scope = 0x2, + AccessType = 0x4, + ItemType = 0x8 + }; + + enum { //An own property that will be used to mark the best-matches group internally + BestMatchesProperty = 2*KTextEditor::CodeCompletionModel::LastProperty + }; + + Q_DECLARE_FLAGS(GroupingMethods, gm) + + static const int ScopeTypeMask = 0x380000; + static const int AccessTypeMask = 0x7; + static const int ItemTypeMask = 0xfe0; + + GroupingMethods groupingMethod() const; + void setGroupingMethod(GroupingMethods m); + + bool accessIncludeConst() const; + void setAccessIncludeConst(bool include); + bool accessIncludeStatic() const; + void setAccessIncludeStatic(bool include); + bool accessIncludeSignalSlot() const; + void setAccessIncludeSignalSlot(bool include); + + // Column merging + bool isColumnMergingEnabled() const; + + const QList< QList >& columnMerges() const; + void setColumnMerges(const QList< QList >& columnMerges); + + void debugStats(); + + ///Returns whether one of the filtered items exactly matches its completion string + bool shouldMatchHideCompletionList() const; + + uint filteredItemCount() const; + + protected: + virtual int contextMatchQuality(const QModelIndex & index) const; + + Q_SIGNALS: + void expandIndex(const QModelIndex& index); + //Emitted whenever something has changed about the group of argument-hints + void argumentHintsChanged(); + + public Q_SLOTS: + void setSortingEnabled(bool enable); + void setFilteringEnabled(bool enable); + void setGroupingEnabled(bool enable); + void setColumnMergingEnabled(bool enable); + + private Q_SLOTS: + void slotRowsInserted( const QModelIndex & parent, int start, int end ); + void slotRowsRemoved( const QModelIndex & parent, int start, int end ); + void slotModelReset(); + + //Updates the best-matches group + void updateBestMatches(); + //Makes sure that the ungrouped group contains each item only once + //Must only be called right after the group was created + void makeGroupItemsUnique(bool onlyFiltered = false); + + private: + + typedef QPair ModelRow; + virtual int contextMatchQuality(const ModelRow& sourceRow) const; + + QTreeView* treeView() const; + + friend class KateArgumentHintModel; + ModelRow modelRowPair(const QModelIndex& index) const; + + // Represents a source row; provides sorting method + class Item { + public: + Item(bool doInitialMatch, KateCompletionModel* model, const HierarchicalModelHandler& handler, ModelRow sourceRow); + + bool isValid() const; + // Returns true if the item is not filtered and matches the current completion string + bool isVisible() const; + // Returns whether the item is filtered or not + bool isFiltered() const; + // Returns whether the item matches the current completion string + bool isMatching() const; + + bool filter(); + + enum MatchType { + NoMatch = 0, + PerfectMatch, + StartsWithMatch, + AbbreviationMatch, + ContainsMatch + }; + MatchType match(); + + const ModelRow& sourceRow() const; + + // Sorting operator + bool operator<(const Item& rhs) const; + + bool haveExactMatch() const { + return m_haveExactMatch; + } + + void clearExactMatch() { + m_haveExactMatch = false; + } + + QString name() const { + return m_nameColumn; + } + + private: + KateCompletionModel* model; + ModelRow m_sourceRow; + + mutable QString m_nameColumn; + + int inheritanceDepth; + + // True when currently matching completion string + MatchType matchCompletion : 6; + // True when passes all active filters + bool matchFilters : 1; + bool m_haveExactMatch : 1; + + QString completionSortingName() const; + }; + + public: + // Grouping and sorting of rows + class Group { + public: + explicit Group(KateCompletionModel* model); + + void addItem(Item i, bool notifyModel = false); + /// Removes the item specified by \a row. Returns true if a change was made to rows. + bool removeItem(const ModelRow& row); + void resort(); + void refilter(); + void clear(); + //Returns whether this group should be ordered before other + bool orderBefore(Group* other) const; + //Returns a number that can be used for ordering + int orderNumber() const; + + ///Returns the row in the this group's filtered list of the given model-row in a source-model + ///-1 if the item is not in the filtered list + ///@todo Implement an efficient way of doing this map, that does _not_ iterate over all items! + int rowOf(ModelRow item) { + for(int a = 0; a < filtered.size(); ++a) + if(filtered[a].sourceRow() == item) + return a; + return -1; + } + + KateCompletionModel* model; + int attribute; + QString title, scope; + QList filtered; + QList prefilter; + bool isEmpty; + //-1 if none was set + int customSortingKey; + }; + + bool hasGroups() const; + + private: + QString commonPrefixInternal(const QString &forcePrefix) const; + /// @note performs model reset + void createGroups(); + ///Creates all sub-items of index i, or the item corresponding to index i. Returns the affected groups. + ///i must be an index in the source model + QSet createItems(const HierarchicalModelHandler&, const QModelIndex& i, bool notifyModel = false); + ///Deletes all sub-items of index i, or the item corresponding to index i. Returns the affected groups. + ///i must be an index in the source model + QSet deleteItems(const QModelIndex& i); + Group* createItem(const HierarchicalModelHandler&, const QModelIndex& i, bool notifyModel = false); + /// @note Make sure you're in a {begin,end}ResetModel block when calling this! + void clearGroups(); + void hideOrShowGroup(Group* g, bool notifyModel = false); + /// When forceGrouping is enabled, all given attributes will be used for grouping, regardless of the completion settings. + Group* fetchGroup(int attribute, const QString& scope = QString(), bool forceGrouping = false); + //If this returns nonzero on an index, the index is the header of the returned group + Group* groupForIndex(const QModelIndex& index) const; + inline Group* groupOfParent(const QModelIndex& child) const { return static_cast(child.internalPointer()); } + QModelIndex indexForRow(Group* g, int row) const; + QModelIndex indexForGroup(Group* g) const; + + enum changeTypes { + Broaden, + Narrow, + Change + }; + + //Returns whether the model needs to be reset + void changeCompletions(Group* g, changeTypes changeType, bool notifyModel); + + bool hasCompletionModel() const; + + /// Removes attributes not used in grouping from the input \a attribute + int groupingAttributes(int attribute) const; + int countBits(int value) const; + + void resort(); + void refilter(); + + static bool matchesAbbreviation(const QString& word, const QString& typed); + + bool m_hasGroups; + + // ### Runtime state + // General + QList m_completionModels; + QMap m_currentMatch; + Qt::CaseSensitivity m_matchCaseSensitivity; + + // Column merging + QList< QList > m_columnMerges; + + QTimer* m_updateBestMatchesTimer; + + Group* m_ungrouped; + Group* m_argumentHints; //The argument-hints will be passed on to another model, to be shown in another widget + Group* m_bestMatches; //A temporary group used for holding the best matches of all visible items + + // Storing the sorted order + QList m_rowTable; + QList m_emptyGroups; + // Quick access to each specific group (if it exists) + QMultiHash m_groupHash; + // Maps custom group-names to their specific groups + QHash m_customGroupHash; + + // ### Configurable state + // Sorting + bool m_sortingEnabled; + bool m_sortingAlphabetical; + bool m_isSortingByInheritance; + Qt::CaseSensitivity m_sortingCaseSensitivity; + QHash< int, QList > m_sortingGroupingOrder; + + // Filtering + bool m_filteringEnabled; + bool m_filterContextMatchesOnly; + bool m_filterByAttribute; + KTextEditor::CodeCompletionModel::CompletionProperties m_filterAttributes; + int m_maximumInheritanceDepth; + + // Grouping + bool m_groupingEnabled; + GroupingMethods m_groupingMethod; + bool m_accessConst, m_accessStatic, m_accesSignalSlot; + + // Column merging + bool m_columnMergingEnabled/*, m_haveExactMatch*/; + + friend class CompletionTest; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(KateCompletionModel::GroupingMethods) + +#endif diff --git a/kate/part/completion/katecompletiontree.cpp b/kate/part/completion/katecompletiontree.cpp new file mode 100644 index 00000000..04a55151 --- /dev/null +++ b/kate/part/completion/katecompletiontree.cpp @@ -0,0 +1,403 @@ +/* This file is part of the KDE libraries and the Kate part. + * + * Copyright (C) 2006 Hamish Rodda + * Copyright (C) 2007-2008 David Nolden + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "katecompletiontree.h" + +#include +#include +#include +#include +#include +#include + +#include "kateview.h" +#include "katerenderer.h" +#include "kateconfig.h" + +#include "katecompletionwidget.h" +#include "katecompletiondelegate.h" +#include "katecompletionmodel.h" + +KateCompletionTree::KateCompletionTree(KateCompletionWidget* parent) + : ExpandingTree(parent) +{ + m_scrollingEnabled = true; + header()->hide(); + setRootIsDecorated(false); + setIndentation(0); + setFrameStyle(QFrame::NoFrame); + setAllColumnsShowFocus(true); + setAlternatingRowColors(true); + //We need ScrollPerItem, because ScrollPerPixel is too slow with a very large competion-list(see KDevelop). + setVerticalScrollMode(QAbstractItemView::ScrollPerItem); + + m_resizeTimer = new QTimer(this); + m_resizeTimer->setSingleShot(true); + + connect(m_resizeTimer, SIGNAL(timeout()), this, SLOT(resizeColumnsSlot())); + + // Provide custom highlighting to completion entries + setItemDelegate(new KateCompletionDelegate(widget()->model(), widget())); + + ///@todo uncomment this once we're sure it isn't called too often, or maybe use a timer. + //connect(widget()->model(), SIGNAL(contentGeometryChanged()), this, SLOT(resizeColumnsSlot())); + + // Prevent user from expanding / collapsing with the mouse + setItemsExpandable(false); + setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); +} + +void KateCompletionTree::currentChanged ( const QModelIndex & current, const QModelIndex & previous ) { + widget()->model()->rowSelected(current); + ExpandingTree::currentChanged(current, previous); +} + +void KateCompletionTree::setScrollingEnabled(bool enabled) { + m_scrollingEnabled = enabled; +} + +void KateCompletionTree::scrollContentsBy( int dx, int dy ) +{ + if(m_scrollingEnabled) + QTreeView::scrollContentsBy(dx, dy); + + if (isVisible()) + m_resizeTimer->start(300); +} + +int KateCompletionTree::columnTextViewportPosition ( int column ) const { + int ret = columnViewportPosition(column); + QModelIndex i = model()->index(0, column, QModelIndex()); + QModelIndex base = model()->index(0, 0, QModelIndex()); + + //If it's just a group header, use the first child + if(base.isValid() && model()->rowCount(base)) + i = base.child(0, column); + + if(i.isValid()) { + QIcon icon = i.data(Qt::DecorationRole).value(); + if(!icon.isNull()) + ret += icon.actualSize(sizeHintForIndex(i)).width(); + } + return ret; +} + +KateCompletionWidget * KateCompletionTree::widget( ) const +{ + return static_cast(const_cast(parent())); +} + +void KateCompletionTree::resizeColumnsSlot() +{ + if(model()) + resizeColumns(); +} + +void KateCompletionTree::resizeColumns(bool firstShow, bool forceResize) +{ + static bool preventRecursion = false; + if (preventRecursion) + return; + + if(firstShow) + forceResize = true; + + preventRecursion = true; + + widget()->setUpdatesEnabled(false); + + int modelIndexOfName = kateModel()->translateColumn(KTextEditor::CodeCompletionModel::Name); + int oldIndentWidth = columnViewportPosition(modelIndexOfName); + + ///Step 1: Compute the needed column-sizes for the visible content + + int numColumns = model()->columnCount(); + + QVector columnSize(numColumns, 5); + + int currentYPos = 0; + + QModelIndex current = indexAt(QPoint(1,1)); + if( current.child(0,0).isValid() ) { //If the index has children, it is a group-label. Then we should start with it's first child. + currentYPos += sizeHintForIndex(current).height(); + current = current.child(0,0); + } + + int num = 0; + bool changed = false; + + while( current.isValid() && currentYPos < height() ) + { +// kDebug() << current.row() << "out of" << model()->rowCount(current.parent()) << "in" << current.parent().data(Qt::DisplayRole); + currentYPos += sizeHintForIndex(current).height(); +// itemDelegate()->sizeHint(QStyleOptionViewItem(), current).isValid() && itemDelegate()->sizeHint(QStyleOptionViewItem(), current).intersects(visibleViewportRect) + changed = true; + num++; + for( int a = 0; a < numColumns; a++ ) + { + QSize s = sizeHintForIndex (current.sibling(current.row(), a)); +// kDebug() << "size-hint for" << current.row() << a << ":" << s << current.sibling(current.row(), a).data(Qt::DisplayRole); + if( s.width() > columnSize[a] && s.width() < 2000 ) + columnSize[a] = s.width(); + else if( s.width() > 2000 ) + kDebug( 13035 ) << "got invalid size-hint of width " << s.width(); + } + + QModelIndex oldCurrent = current; + current = current.sibling(current.row()+1, 0); + + //Are we at the end of a group? If yes, move on into the next group + if( !current.isValid() && oldCurrent.parent().isValid() ) { + current = oldCurrent.parent().sibling( oldCurrent.parent().row()+1, 0 ); + if( current.isValid() && current.child(0,0).isValid() ) { + currentYPos += sizeHintForIndex(current).height(); + current = current.child(0,0); + } + } + } + + int totalColumnsWidth = 0, originalViewportWidth = viewport()->width(); + + int maxWidth = (QApplication::desktop()->screenGeometry(widget()->view()).width()*3) / 4; + + ///Step 2: Update column-sizes + //This contains several hacks to reduce the amount of resizing that happens. Generally, + //resizes only happen if a) More than a specific amount of space is saved by the resize, or + //b) the resizing is required so the list can show all of its contents. + int minimumResize = 0; + int maximumResize = 0; + + if( changed ) { + + for( int n = 0; n < numColumns; n++ ) { + totalColumnsWidth += columnSize[n]; + + int diff = columnSize[n] - columnWidth(n); + if( diff < minimumResize ) + minimumResize = diff; + if( diff > maximumResize ) + maximumResize = diff; + } + + int noReduceTotalWidth = 0; //The total width of the widget of no columns are reduced + for( int n = 0; n < numColumns; n++ ) { + if(columnSize[n] < columnWidth(n)) + noReduceTotalWidth += columnWidth(n); + else + noReduceTotalWidth += columnSize[n]; + } + + //Check whether we can afford to reduce none of the columns + //Only reduce size if we widget would else be too wide. + bool noReduce = noReduceTotalWidth < maxWidth && !forceResize; + + if(noReduce) { + totalColumnsWidth = 0; + for( int n = 0; n < numColumns; n++ ) { + if(columnSize[n] < columnWidth(n)) + columnSize[n] = columnWidth(n); + + totalColumnsWidth += columnSize[n]; + } + } + + if( minimumResize > -40 && maximumResize == 0 && !forceResize ) { + //No column needs to be exanded, and no column needs to be reduced by more than 40 pixels. + //To prevent flashing, do not resize at all. + totalColumnsWidth = 0; + for( int n = 0; n < numColumns; n++ ) { + columnSize[n] = columnWidth(n); + totalColumnsWidth += columnSize[n]; + } + } else { +// viewport()->resize( 5000, viewport()->height() ); + for( int n = 0; n < numColumns; n++ ) { + setColumnWidth(n, columnSize[n]); + } +// kDebug() << "resizing viewport to" << totalColumnsWidth; + viewport()->resize( totalColumnsWidth, viewport()->height() ); + } + } + + ///Step 3: Update widget-size and -position + + int scrollBarWidth = verticalScrollBar()->width(); + + int newIndentWidth = columnViewportPosition(modelIndexOfName); + + int newWidth = qMin(maxWidth, qMax(75, totalColumnsWidth)); + + if(newWidth == maxWidth) + setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); + else + setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + + if(maximumResize > 0 || forceResize || oldIndentWidth != newIndentWidth) { + + // kDebug() << geometry() << "newWidth" << newWidth << "current width" << width() << "target width" << newWidth + scrollBarWidth; + + if((newWidth + scrollBarWidth) != width() && originalViewportWidth != totalColumnsWidth) + { + widget()->resize(newWidth + scrollBarWidth + 2, widget()->height()); + resize(newWidth + scrollBarWidth, widget()->height()- (2*widget()->frameWidth())); + } + + // kDebug() << "created geometry:" << widget()->geometry() << geometry() << "newWidth" << newWidth << "viewport" << viewport()->width(); + + if( viewport()->width() > totalColumnsWidth ) //Set the size of the last column to fill the whole rest of the widget + setColumnWidth(numColumns-1, viewport()->width() - columnViewportPosition(numColumns-1)); + + /* for(int a = 0; a < numColumns; ++a) + kDebug() << "column" << a << columnWidth(a) << "target:" << columnSize[a];*/ + + if (oldIndentWidth != newIndentWidth) + if(widget()->updatePosition() && !forceResize) { + preventRecursion = false; + resizeColumns(true, true); + } + } + + widget()->setUpdatesEnabled(true); + + preventRecursion = false; +} + +QStyleOptionViewItem KateCompletionTree::viewOptions( ) const +{ + QStyleOptionViewItem opt = QTreeView::viewOptions(); + + opt.font = widget()->view()->renderer()->config()->font(); + + return opt; +} + +KateCompletionModel * KateCompletionTree::kateModel( ) const +{ + return static_cast(model()); +} + +bool KateCompletionTree::nextCompletion() +{ + QModelIndex current; + QModelIndex firstCurrent = currentIndex(); + + do { + QModelIndex oldCurrent = currentIndex(); + + current = moveCursor(MoveDown, Qt::NoModifier); + + if (current != oldCurrent && current.isValid()) { + setCurrentIndex(current); + } else { + if (firstCurrent.isValid()) + setCurrentIndex(firstCurrent); + return false; + } + + } while (!kateModel()->indexIsItem(current)); + + return true; +} + +bool KateCompletionTree::previousCompletion() +{ + QModelIndex current; + QModelIndex firstCurrent = currentIndex(); + + do { + QModelIndex oldCurrent = currentIndex(); + + current = moveCursor(MoveUp, Qt::NoModifier); + + if (current != oldCurrent && current.isValid()) { + setCurrentIndex(current); + + } else { + if (firstCurrent.isValid()) + setCurrentIndex(firstCurrent); + return false; + } + + } while (!kateModel()->indexIsItem(current)); + + return true; +} + +bool KateCompletionTree::pageDown( ) +{ + QModelIndex old = currentIndex(); + + QModelIndex current = moveCursor(MovePageDown, Qt::NoModifier); + + if (current.isValid()) { + setCurrentIndex(current); + if (!kateModel()->indexIsItem(current)) + if (!nextCompletion()) + previousCompletion(); + } + + return current != old; +} + +bool KateCompletionTree::pageUp( ) +{ + QModelIndex old = currentIndex(); + QModelIndex current = moveCursor(MovePageUp, Qt::NoModifier); + + if (current.isValid()) { + setCurrentIndex(current); + if (!kateModel()->indexIsItem(current)) + if (!previousCompletion()) + nextCompletion(); + } + return current != old; +} + +void KateCompletionTree::top( ) +{ + QModelIndex current = moveCursor(MoveHome, Qt::NoModifier); + setCurrentIndex(current); + + if (current.isValid()) { + setCurrentIndex(current); + if (!kateModel()->indexIsItem(current)) + nextCompletion(); + } +} + +void KateCompletionTree::scheduleUpdate() +{ + m_resizeTimer->start(300); +} + +void KateCompletionTree::bottom( ) +{ + QModelIndex current = moveCursor(MoveEnd, Qt::NoModifier); + setCurrentIndex(current); + + if (current.isValid()) { + setCurrentIndex(current); + if (!kateModel()->indexIsItem(current)) + previousCompletion(); + } +} + +#include "moc_katecompletiontree.cpp" diff --git a/kate/part/completion/katecompletiontree.h b/kate/part/completion/katecompletiontree.h new file mode 100644 index 00000000..0ece8350 --- /dev/null +++ b/kate/part/completion/katecompletiontree.h @@ -0,0 +1,71 @@ +/* This file is part of the KDE libraries and the Kate part. + * + * Copyright (C) 2006 Hamish Rodda + * Copyright (C) 2007-2008 David Nolden + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef KATECOMPLETIONTREE_H +#define KATECOMPLETIONTREE_H + +#include "expandingtree/expandingtree.h" + +class KateCompletionWidget; +class KateCompletionModel; + +#include + +class KateCompletionTree : public ExpandingTree +{ + Q_OBJECT + + public: + explicit KateCompletionTree(KateCompletionWidget* parent); + + KateCompletionWidget* widget() const; + KateCompletionModel* kateModel() const; + + void resizeColumns(bool firstShow = false, bool forceResize = false); + + // Navigation + bool nextCompletion(); + bool previousCompletion(); + bool pageDown(); + bool pageUp(); + void top(); + void bottom(); + + void scheduleUpdate(); + + void setScrollingEnabled(bool); + + /// Returns the approximated viewport position of the text in the given column, skipping an eventual icon + int columnTextViewportPosition ( int column ) const; + + private slots: + void resizeColumnsSlot(); + + protected: + virtual void currentChanged ( const QModelIndex & current, const QModelIndex & previous ); ///Not available as a signal in this way + virtual void scrollContentsBy(int dx, int dy); + virtual QStyleOptionViewItem viewOptions() const; + private: + bool m_scrollingEnabled; + QTimer* m_resizeTimer; +}; + +#endif diff --git a/kate/part/completion/katecompletionwidget.cpp b/kate/part/completion/katecompletionwidget.cpp new file mode 100644 index 00000000..68f1af6c --- /dev/null +++ b/kate/part/completion/katecompletionwidget.cpp @@ -0,0 +1,1368 @@ +/* This file is part of the KDE libraries and the Kate part. + * + * Copyright (C) 2005-2006 Hamish Rodda + * Copyright (C) 2007-2008 David Nolden + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "katecompletionwidget.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "kateview.h" +#include "katerenderer.h" +#include "kateconfig.h" +#include "katedocument.h" +#include "katebuffer.h" + +#include "katecompletionmodel.h" +#include "katecompletiontree.h" +#include "katecompletionconfig.h" +#include "kateargumenthinttree.h" +#include "kateargumenthintmodel.h" + +//#include "modeltest.h" + +const bool hideAutomaticCompletionOnExactMatch = true; + +//If this is true, the completion-list is navigated up/down when 'tab' is pressed, instead of doing partial completion +const bool shellLikeTabCompletion = false; + +#define CALLCI(WHAT,WHATELSE,WHAT2,model,FUNC) \ +{\ + static KTextEditor::CodeCompletionModelControllerInterface defaultIf;\ + KTextEditor::CodeCompletionModelControllerInterface* ret =\ + dynamic_cast(model);\ + if (!ret) {\ + WHAT2 defaultIf.FUNC;\ + }else \ + WHAT2 ret->FUNC;\ +} + + +static KTextEditor::Range _completionRange(KTextEditor::CodeCompletionModel *model, KTextEditor::View *view, const KTextEditor::Cursor& cursor){ + CALLCI(return,,return, model,completionRange(view, cursor)); +} + +static KTextEditor::Range _updateRange(KTextEditor::CodeCompletionModel *model,KTextEditor::View *view, KTextEditor::Range& range) { + CALLCI(, return range,return, model,updateCompletionRange(view, range)); +} + +static QString _filterString(KTextEditor::CodeCompletionModel *model,KTextEditor::View *view, const KTextEditor::Range& range, const KTextEditor::Cursor& cursor) { + CALLCI(return,,return, model,filterString(view, range, cursor)); +} + +static bool _shouldAbortCompletion(KTextEditor::CodeCompletionModel *model,KTextEditor::View *view, const KTextEditor::Range& range, const QString& currentCompletion) { + CALLCI(return,,return, model,shouldAbortCompletion(view, range, currentCompletion)); +} + +static void _aborted(KTextEditor::CodeCompletionModel *model,KTextEditor::View *view) { + CALLCI(return,,return, model,aborted(view)); +} + +static bool _shouldStartCompletion(KTextEditor::CodeCompletionModel *model,KTextEditor::View *view, QString m_automaticInvocationLine,bool m_lastInsertionByUser, const KTextEditor::Cursor& cursor) { + CALLCI(return,,return,model,shouldStartCompletion(view, m_automaticInvocationLine, m_lastInsertionByUser, cursor)); +} + +KateCompletionWidget::KateCompletionWidget(KateView* parent) + : QFrame(parent, Qt::ToolTip) + , m_presentationModel(new KateCompletionModel(this)) + , m_entryList(new KateCompletionTree(this)) + , m_argumentHintModel(new KateArgumentHintModel(this)) + , m_argumentHintTree(new KateArgumentHintTree(this)) + , m_automaticInvocationDelay(100) + , m_filterInstalled(false) + , m_configWidget(new KateCompletionConfig(m_presentationModel, view())) + , m_lastInsertionByUser(false) + , m_inCompletionList(false) + , m_isSuspended(false) + , m_dontShowArgumentHints(false) + , m_needShow(false) + , m_hadCompletionNavigation(false) + , m_noAutoHide(false) + , m_completionEditRunning (false) + , m_expandedAddedHeightBase(0) + , m_lastInvocationType(KTextEditor::CodeCompletionModel::AutomaticInvocation) +{ + connect(parent, SIGNAL(navigateAccept()), SLOT(navigateAccept())); + connect(parent, SIGNAL(navigateBack()), SLOT(navigateBack())); + connect(parent, SIGNAL(navigateDown()), SLOT(navigateDown())); + connect(parent, SIGNAL(navigateLeft()), SLOT(navigateLeft())); + connect(parent, SIGNAL(navigateRight()), SLOT(navigateRight())); + connect(parent, SIGNAL(navigateUp()), SLOT(navigateUp())); + + qRegisterMetaType("KTextEditor::Cursor"); + + setFrameStyle( QFrame::Box | QFrame::Plain ); + setLineWidth( 1 ); + //setWindowOpacity(0.8); + + m_entryList->setModel(m_presentationModel); + m_entryList->setColumnWidth(0, 0); //These will be determined automatically in KateCompletionTree::resizeColumns + m_entryList->setColumnWidth(1, 0); + m_entryList->setColumnWidth(2, 0); + + m_entryList->setVerticalScrollMode(QAbstractItemView::ScrollPerItem); + + m_argumentHintTree->setParent(0, Qt::ToolTip); + m_argumentHintTree->setModel(m_argumentHintModel); + + // trigger completion on double click on completion list + connect(m_entryList, SIGNAL(doubleClicked(const QModelIndex &)), this, SLOT(execute())); + + connect(m_entryList->verticalScrollBar(), SIGNAL(valueChanged(int)), m_presentationModel, SLOT(placeExpandingWidgets())); + connect(m_argumentHintTree->verticalScrollBar(), SIGNAL(valueChanged(int)), m_argumentHintModel, SLOT(placeExpandingWidgets())); + connect(view(), SIGNAL(focusOut(KTextEditor::View*)), this, SLOT(viewFocusOut())); + + m_automaticInvocationTimer = new QTimer(this); + m_automaticInvocationTimer->setSingleShot(true); + connect(m_automaticInvocationTimer, SIGNAL(timeout()), this, SLOT(automaticInvocation())); + + // Keep branches expanded + connect(m_presentationModel, SIGNAL(modelReset()), this, SLOT(modelReset())); + connect(m_presentationModel, SIGNAL(rowsInserted(QModelIndex,int,int)), SLOT(rowsInserted(QModelIndex,int,int))); + connect(m_argumentHintModel, SIGNAL(contentStateChanged(bool)), this, SLOT(argumentHintsChanged(bool))); + + // No smart lock, no queued connects + connect(view(), SIGNAL(cursorPositionChanged(KTextEditor::View*,KTextEditor::Cursor)), this, SLOT(cursorPositionChanged())); + connect(view(), SIGNAL(verticalScrollPositionChanged(KTextEditor::View*,KTextEditor::Cursor)), this, SLOT(updatePositionSlot())); + + /** + * connect to all possible editing primitives + */ + connect(&view()->doc()->buffer(), SIGNAL(lineWrapped(KTextEditor::Cursor)), this, SLOT(wrapLine(KTextEditor::Cursor))); + connect(&view()->doc()->buffer(), SIGNAL(lineUnwrapped(int)), this, SLOT(unwrapLine(int))); + connect(&view()->doc()->buffer(), SIGNAL(textInserted(KTextEditor::Cursor,QString)), this, SLOT(insertText(KTextEditor::Cursor,QString))); + connect(&view()->doc()->buffer(), SIGNAL(textRemoved(KTextEditor::Range,QString)), this, SLOT(removeText(KTextEditor::Range))); + + // This is a non-focus widget, it is passed keyboard input from the view + + //We need to do this, because else the focus goes to nirvana without any control when the completion-widget is clicked. + setFocusPolicy(Qt::ClickFocus); + m_argumentHintTree->setFocusPolicy(Qt::ClickFocus); + + foreach (QWidget* childWidget, findChildren()) + childWidget->setFocusPolicy(Qt::NoFocus); + + //Position the entry-list so a frame can be drawn around it + m_entryList->move(frameWidth(), frameWidth()); +} + +KateCompletionWidget::~KateCompletionWidget() { +} + +void KateCompletionWidget::viewFocusOut() { + abortCompletion(); +} + +void KateCompletionWidget::modelContentChanged() { + ////kDebug()<<">>>>>>>>>>>>>>>>"; + if(m_completionRanges.isEmpty()) { + //kDebug( 13035 ) << "content changed, but no completion active"; + abortCompletion(); + return; + } + + if(!view()->hasFocus()) { + //kDebug( 13035 ) << "view does not have focus"; + return; + } + + if(!m_waitingForReset.isEmpty()) { + //kDebug( 13035 ) << "waiting for" << m_waitingForReset.size() << "completion-models to reset"; + return; + } + + int realItemCount = 0; + foreach (KTextEditor::CodeCompletionModel* model, m_presentationModel->completionModels()) + realItemCount += model->rowCount(); + if( !m_isSuspended && ((isHidden() && m_argumentHintTree->isHidden()) || m_needShow) && realItemCount != 0 ) { + m_needShow = false; + updateAndShow(); + } + + if(m_argumentHintModel->rowCount(QModelIndex()) == 0) + m_argumentHintTree->hide(); + + if(m_presentationModel->rowCount(QModelIndex()) == 0) + hide(); + + + //With each filtering items can be added or removed, so we have to reset the current index here so we always have a selected item + m_entryList->setCurrentIndex(model()->index(0,0)); + if(!model()->indexIsItem(m_entryList->currentIndex())) { + QModelIndex firstIndex = model()->index(0,0, m_entryList->currentIndex()); + m_entryList->setCurrentIndex(firstIndex); + //m_entryList->scrollTo(firstIndex, QAbstractItemView::PositionAtTop); + } + + updateHeight(); + + //New items for the argument-hint tree may have arrived, so check whether it needs to be shown + if( m_argumentHintTree->isHidden() && !m_dontShowArgumentHints && m_argumentHintModel->rowCount(QModelIndex()) != 0 ) + m_argumentHintTree->show(); + + if(!m_noAutoHide && hideAutomaticCompletionOnExactMatch && !isHidden() && + m_lastInvocationType == KTextEditor::CodeCompletionModel::AutomaticInvocation && + m_presentationModel->shouldMatchHideCompletionList()) + hide(); + else if(isHidden() && !m_presentationModel->shouldMatchHideCompletionList() && + m_presentationModel->rowCount(QModelIndex())) + show(); +} + +KateArgumentHintTree* KateCompletionWidget::argumentHintTree() const { + return m_argumentHintTree; +} + +KateArgumentHintModel* KateCompletionWidget::argumentHintModel() const { + return m_argumentHintModel; +} + +const KateCompletionModel* KateCompletionWidget::model() const { + return m_presentationModel; +} + +KateCompletionModel* KateCompletionWidget::model() { + return m_presentationModel; +} + +void KateCompletionWidget::rowsInserted(const QModelIndex& parent, int rowFrom, int rowEnd) +{ + m_entryList->setAnimated(false); + if(!model()->isGroupingEnabled()) + return; + + if (!parent.isValid()) + for (int i = rowFrom; i <= rowEnd; ++i) + m_entryList->expand(m_presentationModel->index(i, 0, parent)); +} + +KateView * KateCompletionWidget::view( ) const +{ + return static_cast(const_cast(parent())); +} + +void KateCompletionWidget::argumentHintsChanged(bool hasContent) +{ + m_dontShowArgumentHints = !hasContent; + + if( m_dontShowArgumentHints ) + m_argumentHintTree->hide(); + else + updateArgumentHintGeometry(); +} + +void KateCompletionWidget::startCompletion(KTextEditor::CodeCompletionModel::InvocationType invocationType, const QList& models) +{ + if(invocationType == KTextEditor::CodeCompletionModel::UserInvocation) + { + abortCompletion(); + } + startCompletion(KTextEditor::Range(KTextEditor::Cursor(-1, -1), KTextEditor::Cursor(-1, -1)), models, invocationType); +} + +void KateCompletionWidget::deleteCompletionRanges() +{ + ////kDebug(); + foreach(const CompletionRange &r, m_completionRanges) + delete r.range; + m_completionRanges.clear(); +} + +void KateCompletionWidget::startCompletion(const KTextEditor::Range& word, KTextEditor::CodeCompletionModel* model, KTextEditor::CodeCompletionModel::InvocationType invocationType) +{ + QList models; + if (model) { + models << model; + } else { + models = m_sourceModels; + } + startCompletion(word, models, invocationType); +} + +void KateCompletionWidget::startCompletion(const KTextEditor::Range& word, const QList& modelsToStart, KTextEditor::CodeCompletionModel::InvocationType invocationType) +{ + + ////kDebug()<<"============"; + + m_isSuspended = false; + m_inCompletionList = true; //Always start at the top of the completion-list + m_needShow = true; + + if(m_completionRanges.isEmpty()) + m_noAutoHide = false; //Re-enable auto-hide on every clean restart of the completion + + m_lastInvocationType = invocationType; + + disconnect(this->model(), SIGNAL(layoutChanged()), this, SLOT(modelContentChanged())); + disconnect(this->model(), SIGNAL(modelReset()), this, SLOT(modelContentChanged())); + + m_dontShowArgumentHints = true; + + QList models = (modelsToStart.isEmpty() ? m_sourceModels : modelsToStart); + + foreach(KTextEditor::CodeCompletionModel* model, m_completionRanges.keys()) + if(!models.contains(model)) + models << model; + + if (!m_filterInstalled) { + if (!QApplication::activeWindow()) { + kWarning(13035) << "No active window to install event filter on!!"; + return; + } + // Enable the cc box to move when the editor window is moved + QApplication::activeWindow()->installEventFilter(this); + m_filterInstalled = true; + } + + m_presentationModel->clearCompletionModels(); + + if(invocationType == KTextEditor::CodeCompletionModel::UserInvocation) { + deleteCompletionRanges(); + } + + foreach (KTextEditor::CodeCompletionModel* model, models) { + KTextEditor::Range range; + if (word.isValid()) { + range = word; + //kDebug()<<"word is used"; + } else { + range=_completionRange(model,view(), view()->cursorPosition()); + //kDebug()<<"completionRange has been called, cursor pos is"<cursorPosition(); + } + //kDebug()<<"range is"<completionInvoked(view(), range, invocationType); + + disconnect(model, SIGNAL(waitForReset()), this, SLOT(waitForModelReset())); + + m_completionRanges[model] = view()->doc()->newMovingRange(range, KTextEditor::MovingRange::ExpandRight | KTextEditor::MovingRange::ExpandLeft); + + //In automatic invocation mode, hide the completion widget as soon as the position where the completion was started is passed to the left + m_completionRanges[model].leftBoundary = view()->cursorPosition(); + + //In manual invocation mode, bound the activity either the point from where completion was invoked, or to the start of the range + if(invocationType != KTextEditor::CodeCompletionModel::AutomaticInvocation) + if(range.start() < m_completionRanges[model].leftBoundary) + m_completionRanges[model].leftBoundary = range.start(); + + if(!m_completionRanges[model].range->toRange().isValid()) { + kWarning(13035) << "Could not construct valid smart-range from" << range << "instead got" << *m_completionRanges[model].range; + abortCompletion(); + return; + } + } + + m_presentationModel->setCompletionModels(models); + + cursorPositionChanged(); + + if (!m_completionRanges.isEmpty()) { + connect(this->model(), SIGNAL(layoutChanged()), this, SLOT(modelContentChanged())); + connect(this->model(), SIGNAL(modelReset()), this, SLOT(modelContentChanged())); + //Now that all models have been notified, check whether the widget should be displayed instantly + modelContentChanged(); + } + else { + abortCompletion(); + } +} + +void KateCompletionWidget::waitForModelReset() +{ + KTextEditor::CodeCompletionModel* senderModel = qobject_cast(sender()); + if(!senderModel) { + kWarning() << "waitForReset signal from bad model"; + return; + } + m_waitingForReset.insert(senderModel); +} + +void KateCompletionWidget::updateAndShow() +{ + //kDebug()<<"*******************************************"; + if(!view()->hasFocus()) { + kDebug( 13035 ) << "view does not have focus"; + return; + } + + setUpdatesEnabled(false); + + modelReset(); + + m_argumentHintModel->buildRows(); + if( m_argumentHintModel->rowCount(QModelIndex()) != 0 ) + argumentHintsChanged(true); +// } + + //We do both actions twice here so they are stable, because they influence each other: + //updatePosition updates the height, resizeColumns needs the correct height to decide over + //how many rows it computs the column-width + updatePosition(true); + m_entryList->resizeColumns(true, true); + updatePosition(true); + m_entryList->resizeColumns(true, true); + + setUpdatesEnabled(true); + + if(m_argumentHintModel->rowCount(QModelIndex())) { + updateArgumentHintGeometry(); + m_argumentHintTree->show(); + } else + m_argumentHintTree->hide(); + + if (m_presentationModel->rowCount() && (!m_presentationModel->shouldMatchHideCompletionList() || + !hideAutomaticCompletionOnExactMatch || + m_lastInvocationType != KTextEditor::CodeCompletionModel::AutomaticInvocation) ) + show(); + else + hide(); +} + +void KateCompletionWidget::updatePositionSlot() +{ + updatePosition(); +} + +bool KateCompletionWidget::updatePosition(bool force) +{ + if (!force && !isCompletionActive()) + return false; + + if (!completionRange()) { + return false; + } + QPoint cursorPosition = view()->cursorToCoordinate(completionRange()->start()); + if (cursorPosition == QPoint(-1,-1)) { + // Start of completion range is now off-screen -> abort + abortCompletion(); + return false; + } + + QPoint p = view()->mapToGlobal( cursorPosition ); + int x = p.x() - m_entryList->columnTextViewportPosition(m_presentationModel->translateColumn(KTextEditor::CodeCompletionModel::Name)) - 4 - (m_entryList->viewport()->pos().x()); + int y = p.y(); + + y += view()->renderer()->config()->fontMetrics().height() + 4; + + bool borderHit = false; + + if (x + width() > QApplication::desktop()->screenGeometry(view()).right()) { + x = QApplication::desktop()->screenGeometry(view()).right() - width(); + borderHit = true; + } + + if( x < QApplication::desktop()->screenGeometry(view()).left() ) { + x = QApplication::desktop()->screenGeometry(view()).left(); + borderHit = true; + } + + move( QPoint(x,y) ); + + updateHeight(); + + updateArgumentHintGeometry(); + +// //kDebug() << "updated to" << geometry() << m_entryList->geometry() << borderHit; + + return borderHit; +} + +void KateCompletionWidget::updateArgumentHintGeometry() +{ + if( !m_dontShowArgumentHints ) { + //Now place the argument-hint widget + QRect geom = m_argumentHintTree->geometry(); + geom.moveTo(pos()); + geom.setWidth(width()); + geom.moveBottom(pos().y() - view()->renderer()->config()->fontMetrics().height()*2); + m_argumentHintTree->updateGeometry(geom); + } +} + +//Checks whether the given model has at least "rows" rows, also searching the second level of the tree. +bool hasAtLeastNRows(int rows, QAbstractItemModel* model) { + int count = 0; + for(int row = 0; row < model->rowCount(); ++row) { + ++count; + + QModelIndex index(model->index(row, 0)); + if(index.isValid()) + count += model->rowCount(index); + + if(count > rows) + return true; + } + return false; +} + +void KateCompletionWidget::updateHeight() +{ + QRect geom = geometry(); + + int minBaseHeight = 10; + int maxBaseHeight = 300; + + int baseHeight = 0; + int calculatedCustomHeight = 0; + + if(hasAtLeastNRows(15, m_presentationModel)) { + //If we know there is enough rows, always use max-height, we don't need to calculate size-hints + baseHeight = maxBaseHeight; + }else{ + //Calculate size-hints to determine the best height + for(int row = 0; row < m_presentationModel->rowCount(); ++row) { + baseHeight += treeView()->sizeHintForRow(row); + + QModelIndex index(m_presentationModel->index(row, 0)); + if(index.isValid()) { + for(int row2 = 0; row2 < m_presentationModel->rowCount(index); ++row2) { + int h = 0; + for(int a = 0; a < m_presentationModel->columnCount(index); ++a) { + int localHeight = treeView()->sizeHintForIndex(index.child(row2, a)).height(); + if(localHeight > h) + h = localHeight; + } + baseHeight += h; + if(baseHeight > maxBaseHeight) + break; + } + + if(baseHeight > maxBaseHeight) + break; + } + } + + calculatedCustomHeight = baseHeight; + } + + baseHeight += 2*frameWidth(); + + if(m_entryList->horizontalScrollBar()->isVisible()) + baseHeight += m_entryList->horizontalScrollBar()->height(); + + + if(baseHeight < minBaseHeight) + baseHeight = minBaseHeight; + if(baseHeight > maxBaseHeight) { + baseHeight = maxBaseHeight; + m_entryList->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); + }else{ + //Somewhere there seems to be a bug that makes QTreeView add a scroll-bar + //even if the content exactly fits in. So forcefully disable the scroll-bar in that case + m_entryList->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); +} + + int newExpandingAddedHeight = 0; + + if(baseHeight == maxBaseHeight && model()->expandingWidgetsHeight()) { + //Eventually add some more height + if(calculatedCustomHeight && calculatedCustomHeight > baseHeight && calculatedCustomHeight < (maxBaseHeight + model()->expandingWidgetsHeight())) + newExpandingAddedHeight = calculatedCustomHeight - baseHeight; + else + newExpandingAddedHeight = model()->expandingWidgetsHeight(); + } + + if( m_expandedAddedHeightBase != baseHeight && m_expandedAddedHeightBase - baseHeight > -2 && m_expandedAddedHeightBase - baseHeight < 2 ) + { + //Re-use the stored base-height if it only slightly differs from the current one. + //Reason: Qt seems to apply slightly wrong sizes when the completion-widget is moved out of the screen at the bottom, + // which completely breaks this algorithm. Solution: re-use the old base-size if it only slightly differs from the computed one. + baseHeight = m_expandedAddedHeightBase; + } + + int screenBottom = QApplication::desktop()->screenGeometry(view()).bottom(); + + //Limit the height to the bottom of the screen + int bottomPosition = baseHeight + newExpandingAddedHeight + geometry().top(); + + if( bottomPosition > screenBottom ) { + newExpandingAddedHeight -= bottomPosition - (screenBottom); + } + + int finalHeight = baseHeight+newExpandingAddedHeight; + + if( finalHeight < 10 ) { + m_entryList->resize(m_entryList->width(), height() - 2*frameWidth()); + return; + } + + m_expandedAddedHeightBase = geometry().height(); + + geom.setHeight(finalHeight); + + //Work around a crash deep within the Qt 4.5 raster engine + m_entryList->setScrollingEnabled(false); + + if(geometry() != geom) + setGeometry(geom); + + QSize entryListSize = QSize(m_entryList->width(), finalHeight - 2*frameWidth()); + if(m_entryList->size() != entryListSize) + m_entryList->resize(entryListSize); + + + m_entryList->setScrollingEnabled(true); +} + +void KateCompletionWidget::cursorPositionChanged( ) +{ + ////kDebug(); + if (m_completionRanges.isEmpty()) + return; + + QModelIndex oldCurrentSourceIndex; + if(m_inCompletionList && m_entryList->currentIndex().isValid()) + oldCurrentSourceIndex = m_presentationModel->mapToSource(m_entryList->currentIndex()); + + KTextEditor::Cursor cursor = view()->cursorPosition(); + + QList checkCompletionRanges = m_completionRanges.keys(); + + //Check the models and eventuall abort some + for(QList::iterator it = checkCompletionRanges.begin(); it != checkCompletionRanges.end(); ++it) { + KTextEditor::CodeCompletionModel *model = *it; + if(!m_completionRanges.contains(model)) + continue; + + //kDebug()<<"range before _updateRange:"<< *range; + + // this might invalidate the range, therefore re-check afterwards + KTextEditor::Range rangeTE = m_completionRanges[model].range->toRange(); + KTextEditor::Range newRange = _updateRange(model, view(), rangeTE); + if(!m_completionRanges.contains(model)) + continue; + + // update value + m_completionRanges[model].range->setRange (newRange); + + //kDebug()<<"range after _updateRange:"<< *range; + QString currentCompletion = _filterString(model,view(), *m_completionRanges[model].range, view()->cursorPosition()); + if(!m_completionRanges.contains(model)) + continue; + + //kDebug()<<"after _filterString, currentCompletion="<< currentCompletion; + bool abort = _shouldAbortCompletion(model,view(), *m_completionRanges[model].range, currentCompletion); + if(!m_completionRanges.contains(model)) + continue; + + //kDebug()<<"after _shouldAbortCompletion:abort="<cursorPosition() < m_completionRanges[model].leftBoundary) { + //kDebug() << "aborting because of boundary: cursor:"<cursorPosition()<<"completion_Range_left_boundary:"<removeCompletionModel(model); + } + } else { + m_presentationModel->setCurrentCompletion(model, currentCompletion); + } + } + + if(oldCurrentSourceIndex.isValid()) { + QModelIndex idx = m_presentationModel->mapFromSource(oldCurrentSourceIndex); + if(idx.isValid()) { + //kDebug() << "setting" << idx; + m_entryList->setCurrentIndex(idx.sibling(idx.row(), 0)); +// m_entryList->nextCompletion(); +// m_entryList->previousCompletion(); + }else{ + //kDebug() << "failed to map from source"; + } + } + + m_entryList->scheduleUpdate(); +} + +bool KateCompletionWidget::isCompletionActive( ) const +{ + return !m_completionRanges.isEmpty() && ((!isHidden() && isVisible()) || (!m_argumentHintTree->isHidden() && m_argumentHintTree->isVisible())); +} + +void KateCompletionWidget::abortCompletion( ) +{ + //kDebug(13035) ; + + m_isSuspended = false; + + bool wasActive = isCompletionActive(); + + clear(); + + if(!isHidden()) + hide(); + if(!m_argumentHintTree->isHidden()) + m_argumentHintTree->hide(); + + if (wasActive) + view()->sendCompletionAborted(); +} + +void KateCompletionWidget::clear() { + m_presentationModel->clearCompletionModels(); + m_argumentHintTree->clearCompletion(); + m_argumentHintModel->clear(); + + foreach(KTextEditor::CodeCompletionModel* model, m_completionRanges.keys()) + _aborted(model,view()); + + deleteCompletionRanges(); +} + +bool KateCompletionWidget::navigateAccept() { + m_hadCompletionNavigation = true; + + if(currentEmbeddedWidget()) + QMetaObject::invokeMethod(currentEmbeddedWidget(), "embeddedWidgetAccept"); + + QModelIndex index = selectedIndex(); + if( index.isValid() ) { + index.data(KTextEditor::CodeCompletionModel::AccessibilityAccept); + return true; + } + return false; +} + +void KateCompletionWidget::execute() +{ + //kDebug(13035) ; + + if (!isCompletionActive()) + return; + + QModelIndex index = selectedIndex(); + + if (!index.isValid()) + return abortCompletion(); + + QModelIndex toExecute; + + if(index.model() == m_presentationModel) + toExecute = m_presentationModel->mapToSource(index); + else + toExecute = m_argumentHintModel->mapToSource(index); + + if (!toExecute.isValid()) { + kWarning() << "Could not map index" << m_entryList->selectionModel()->currentIndex() << "to source index."; + return abortCompletion(); + } + + // encapsulate all editing as being from the code completion, and undo-able in one step. + view()->doc()->editStart(); + m_completionEditRunning = true; + + // create scoped pointer, to ensure deletion of cursor + QScopedPointer oldPos (view()->doc()->newMovingCursor(view()->cursorPosition(), KTextEditor::MovingCursor::StayOnInsert)); + + KTextEditor::CodeCompletionModel* model = static_cast(const_cast(toExecute.model())); + Q_ASSERT(model); + + KTextEditor::CodeCompletionModel2* model2 = qobject_cast(model); + + Q_ASSERT(m_completionRanges.contains(model)); + KTextEditor::Cursor start = m_completionRanges[model].range->start(); + + if(model2) + model2->executeCompletionItem2(view()->document(), *m_completionRanges[model].range, toExecute); + else if(toExecute.parent().isValid()) + //The normale CodeCompletionInterface cannot handle feedback for hierarchical models, so just do the replacement + view()->document()->replaceText(*m_completionRanges[model].range, model->data(toExecute.sibling(toExecute.row(), KTextEditor::CodeCompletionModel::Name)).toString()); + else + model->executeCompletionItem(view()->document(), *m_completionRanges[model].range, toExecute.row()); + + view()->doc()->editEnd(); + m_completionEditRunning = false; + + abortCompletion(); + + view()->sendCompletionExecuted(start, model, toExecute); + + KTextEditor::Cursor newPos = view()->cursorPosition(); + + if(newPos > *oldPos) { + m_automaticInvocationAt = newPos; + m_automaticInvocationLine = view()->doc()->text(KTextEditor::Range(*oldPos, newPos)); + //kDebug() << "executed, starting automatic invocation with line" << m_automaticInvocationLine; + m_lastInsertionByUser = false; + m_automaticInvocationTimer->start(); + } +} + +void KateCompletionWidget::resizeEvent( QResizeEvent * event ) +{ + QFrame::resizeEvent(event); +} + +void KateCompletionWidget::showEvent ( QShowEvent * event ) +{ + m_isSuspended = false; + + QFrame::showEvent(event); + + if( !m_dontShowArgumentHints && m_argumentHintModel->rowCount(QModelIndex()) != 0 ) + m_argumentHintTree->show(); +} + +KTextEditor::MovingRange * KateCompletionWidget::completionRange(KTextEditor::CodeCompletionModel* model) const +{ + if (!model) { + if (m_completionRanges.isEmpty()) return 0; + + KTextEditor::MovingRange* ret = m_completionRanges.begin()->range; + + foreach(const CompletionRange &range, m_completionRanges) + if(range.range->start() > ret->start()) + ret = range.range; + return ret; + } + if(m_completionRanges.contains(model)) + return m_completionRanges[model].range; + else + return 0; +} + +QMap KateCompletionWidget::completionRanges( ) const +{ + return m_completionRanges; +} + +void KateCompletionWidget::modelReset( ) +{ + setUpdatesEnabled(false); + m_entryList->setAnimated(false); + m_argumentHintTree->setAnimated(false); + ///We need to do this by hand, because QTreeView::expandAll is very inefficient. + ///It creates a QPersistentModelIndex for every single item in the whole tree.. + for(int row = 0; row < m_argumentHintModel->rowCount(QModelIndex()); ++row) { + QModelIndex index(m_argumentHintModel->index(row, 0, QModelIndex())); + if(!m_argumentHintTree->isExpanded(index)) { + m_argumentHintTree->expand(index); + } + } + + for(int row = 0; row < m_entryList->model()->rowCount(QModelIndex()); ++row) { + QModelIndex index(m_entryList->model()->index(row, 0, QModelIndex())); + if(!m_entryList->isExpanded(index)) { + m_entryList->expand(index); + } + } + setUpdatesEnabled(true); +} + +KateCompletionTree* KateCompletionWidget::treeView() const { + return m_entryList; +} + +QModelIndex KateCompletionWidget::selectedIndex() const { + if(!isCompletionActive()) + return QModelIndex(); + + if( m_inCompletionList ) + return m_entryList->currentIndex(); + else + return m_argumentHintTree->currentIndex(); +} + +bool KateCompletionWidget::navigateLeft() { + m_hadCompletionNavigation = true; + if(currentEmbeddedWidget()) + QMetaObject::invokeMethod(currentEmbeddedWidget(), "embeddedWidgetLeft"); + + QModelIndex index = selectedIndex(); + + if( index.isValid() ) { + index.data(KTextEditor::CodeCompletionModel::AccessibilityPrevious); + + return true; + } + return false; +} + +bool KateCompletionWidget::navigateRight() { + m_hadCompletionNavigation = true; + if(currentEmbeddedWidget()) ///@todo post 4.2: Make these slots public interface, or create an interface using virtual functions + QMetaObject::invokeMethod(currentEmbeddedWidget(), "embeddedWidgetRight"); + + QModelIndex index = selectedIndex(); + + if( index.isValid() ) { + index.data(KTextEditor::CodeCompletionModel::AccessibilityNext); + return true; + } + + return false; +} + +bool KateCompletionWidget::hadNavigation() const { + return m_hadCompletionNavigation; +} + +void KateCompletionWidget::resetHadNavigation() { + m_hadCompletionNavigation = false; +} + + +bool KateCompletionWidget::navigateBack() { + m_hadCompletionNavigation = true; + if(currentEmbeddedWidget()) + QMetaObject::invokeMethod(currentEmbeddedWidget(), "embeddedWidgetBack"); + return false; +} + +bool KateCompletionWidget::toggleExpanded(bool forceExpand, bool forceUnExpand) { + if ( (canExpandCurrentItem() || forceExpand ) && !forceUnExpand) { + bool ret = canExpandCurrentItem(); + setCurrentItemExpanded(true); + return ret; + } else if (canCollapseCurrentItem() || forceUnExpand) { + bool ret = canCollapseCurrentItem(); + setCurrentItemExpanded(false); + return ret; + } + return false; +} + +bool KateCompletionWidget::canExpandCurrentItem() const { + if( m_inCompletionList ) { + if( !m_entryList->currentIndex().isValid() ) return false; + return model()->isExpandable( m_entryList->currentIndex() ) && !model()->isExpanded( m_entryList->currentIndex() ); + } else { + if( !m_argumentHintTree->currentIndex().isValid() ) return false; + return argumentHintModel()->isExpandable( m_argumentHintTree->currentIndex() ) && !argumentHintModel()->isExpanded( m_argumentHintTree->currentIndex() ); + } +} + +bool KateCompletionWidget::canCollapseCurrentItem() const { + if( m_inCompletionList ) { + if( !m_entryList->currentIndex().isValid() ) return false; + return model()->isExpandable( m_entryList->currentIndex() ) && model()->isExpanded( m_entryList->currentIndex() ); + }else{ + if( !m_argumentHintTree->currentIndex().isValid() ) return false; + return m_argumentHintModel->isExpandable( m_argumentHintTree->currentIndex() ) && m_argumentHintModel->isExpanded( m_argumentHintTree->currentIndex() ); + } +} + +void KateCompletionWidget::setCurrentItemExpanded( bool expanded ) { + if( m_inCompletionList ) { + if( !m_entryList->currentIndex().isValid() ) return; + model()->setExpanded(m_entryList->currentIndex(), expanded); + updateHeight(); + }else{ + if( !m_argumentHintTree->currentIndex().isValid() ) return; + m_argumentHintModel->setExpanded(m_argumentHintTree->currentIndex(), expanded); + } +} + +bool KateCompletionWidget::eventFilter( QObject * watched, QEvent * event ) +{ + bool ret = QFrame::eventFilter(watched, event); + + if (watched != this) + if (event->type() == QEvent::Move) + updatePosition(); + + return ret; +} + +bool KateCompletionWidget::navigateDown() { + m_hadCompletionNavigation = true; + if(currentEmbeddedWidget()) { + QMetaObject::invokeMethod(currentEmbeddedWidget(), "embeddedWidgetDown"); + } + return false; +} + +bool KateCompletionWidget::navigateUp() { + m_hadCompletionNavigation = true; + if(currentEmbeddedWidget()) + QMetaObject::invokeMethod(currentEmbeddedWidget(), "embeddedWidgetUp"); + return false; +} + +QWidget* KateCompletionWidget::currentEmbeddedWidget() { + QModelIndex index = selectedIndex(); + if(!index.isValid()) + return 0; + if( qobject_cast(index.model()) ) { + const ExpandingWidgetModel* model = static_cast(index.model()); + if( model->isExpanded(index) ) + return model->expandingWidget(index); + } + return 0; +} + +void KateCompletionWidget::cursorDown() +{ + bool wasPartiallyExpanded = model()->partiallyExpandedRow().isValid(); + + if( m_inCompletionList ) + m_entryList->nextCompletion(); + else { + if( !m_argumentHintTree->nextCompletion() ) + switchList(); + } + + if(wasPartiallyExpanded != model()->partiallyExpandedRow().isValid()) + updateHeight(); +} + +void KateCompletionWidget::cursorUp() +{ + bool wasPartiallyExpanded = model()->partiallyExpandedRow().isValid(); + + if( m_inCompletionList ) { + if( !m_entryList->previousCompletion() ) + switchList(); + }else{ + m_argumentHintTree->previousCompletion(); + } + + if(wasPartiallyExpanded != model()->partiallyExpandedRow().isValid()) + updateHeight(); +} + +void KateCompletionWidget::pageDown( ) +{ + bool wasPartiallyExpanded = model()->partiallyExpandedRow().isValid(); + + if( m_inCompletionList ) + m_entryList->pageDown(); + else { + if( !m_argumentHintTree->pageDown() ) + switchList(); + } + + if(wasPartiallyExpanded != model()->partiallyExpandedRow().isValid()) + updateHeight(); +} + +void KateCompletionWidget::pageUp( ) +{ + bool wasPartiallyExpanded = model()->partiallyExpandedRow().isValid(); + + if( m_inCompletionList ) { + if( !m_entryList->pageUp() ) + switchList(); + }else{ + m_argumentHintTree->pageUp(); + } + + if(wasPartiallyExpanded != model()->partiallyExpandedRow().isValid()) + updateHeight(); +} + +void KateCompletionWidget::top( ) +{ + bool wasPartiallyExpanded = model()->partiallyExpandedRow().isValid(); + + if( m_inCompletionList ) + m_entryList->top(); + else + m_argumentHintTree->top(); + + if(wasPartiallyExpanded != model()->partiallyExpandedRow().isValid()) + updateHeight(); +} + +void KateCompletionWidget::bottom( ) +{ + bool wasPartiallyExpanded = model()->partiallyExpandedRow().isValid(); + + if( m_inCompletionList ) + m_entryList->bottom(); + else + m_argumentHintTree->bottom(); + + if(wasPartiallyExpanded != model()->partiallyExpandedRow().isValid()) + updateHeight(); +} + +void KateCompletionWidget::switchList() { + if( m_inCompletionList ) { + if( m_argumentHintModel->rowCount(QModelIndex()) != 0 ) { + m_entryList->setCurrentIndex(QModelIndex()); + m_argumentHintTree->setCurrentIndex(m_argumentHintModel->index(m_argumentHintModel->rowCount(QModelIndex())-1, 0)); + m_inCompletionList = false; + } + } else { + if( m_presentationModel->rowCount(QModelIndex()) != 0 ) { + m_argumentHintTree->setCurrentIndex(QModelIndex()); + m_entryList->setCurrentIndex(m_presentationModel->index(0, 0)); + if(model()->hasGroups()) //If we have groups we have to move on, because the first item is a label + m_entryList->nextCompletion(); + m_inCompletionList = true; + } + } +} + +void KateCompletionWidget::showConfig( ) +{ + abortCompletion(); + + m_configWidget->exec(); +} + +void KateCompletionWidget::completionModelReset() +{ + KTextEditor::CodeCompletionModel* model = qobject_cast(sender()); + if(!model) { + kWarning() << "bad sender"; + return; + } + + if(!m_waitingForReset.contains(model)) + return; + + m_waitingForReset.remove(model); + + if(m_waitingForReset.isEmpty()) { + if(!isCompletionActive()) { + //kDebug() << "all completion-models we waited for are ready. Last one: " << model->objectName(); + //Eventually show the completion-list if this was the last model we were waiting for + //Use a queued connection once again to make sure that KateCompletionModel is notified before we are + QMetaObject::invokeMethod(this, "modelContentChanged", Qt::QueuedConnection); + } + } +} + +void KateCompletionWidget::modelDestroyed(QObject* model) { + unregisterCompletionModel(static_cast(model)); +} + +void KateCompletionWidget::registerCompletionModel(KTextEditor::CodeCompletionModel* model) +{ + if (m_sourceModels.contains(model)) { + return; + } + + connect(model, SIGNAL(destroyed(QObject*)), SLOT(modelDestroyed(QObject*))); + //This connection must not be queued + connect(model, SIGNAL(modelReset()), SLOT(completionModelReset())); + + m_sourceModels.append(model); + + if (isCompletionActive()) { + m_presentationModel->addCompletionModel(model); + } +} + +void KateCompletionWidget::unregisterCompletionModel(KTextEditor::CodeCompletionModel* model) +{ + disconnect(model, SIGNAL(destroyed(QObject*)), this, SLOT(modelDestroyed(QObject*))); + disconnect(model, SIGNAL(modelReset()), this, SLOT(completionModelReset())); + + m_sourceModels.removeAll(model); + abortCompletion(); +} + +int KateCompletionWidget::automaticInvocationDelay() const { + return m_automaticInvocationDelay; +} + +void KateCompletionWidget::setAutomaticInvocationDelay(int delay) { + m_automaticInvocationDelay = delay; +} + +void KateCompletionWidget::wrapLine (const KTextEditor::Cursor &) +{ + m_lastInsertionByUser = !m_completionEditRunning; + + // wrap line, be done + m_automaticInvocationLine.clear(); + m_automaticInvocationTimer->stop(); +} + +void KateCompletionWidget::unwrapLine (int) +{ + m_lastInsertionByUser = !m_completionEditRunning; + + // just removal + m_automaticInvocationLine.clear(); + m_automaticInvocationTimer->stop(); +} + +void KateCompletionWidget::insertText (const KTextEditor::Cursor &position, const QString &text) +{ + m_lastInsertionByUser = !m_completionEditRunning; + + // no invoke? + if (!view()->config()->automaticCompletionInvocation()) { + m_automaticInvocationLine.clear(); + m_automaticInvocationTimer->stop(); + return; + } + + if(m_automaticInvocationAt != position) { + m_automaticInvocationLine.clear(); + m_lastInsertionByUser = !m_completionEditRunning; + } + + m_automaticInvocationLine += text; + m_automaticInvocationAt = position; + m_automaticInvocationAt.setColumn (position.column() + text.length()); + + if (m_automaticInvocationLine.isEmpty()) { + m_automaticInvocationTimer->stop(); + return; + } + + m_automaticInvocationTimer->start(m_automaticInvocationDelay); +} + +void KateCompletionWidget::removeText (const KTextEditor::Range &) +{ + m_lastInsertionByUser = !m_completionEditRunning; + + // just removal + m_automaticInvocationLine.clear(); + m_automaticInvocationTimer->stop(); +} + +void KateCompletionWidget::automaticInvocation() +{ + //kDebug()<<"m_automaticInvocationAt:"<cursorPosition(); + if(m_automaticInvocationAt != view()->cursorPosition()) + return; + + bool start = false; + QList models; + + //kDebug()<<"checking models"; + foreach (KTextEditor::CodeCompletionModel *model, m_sourceModels) { + //kDebug()<<"m_completionRanges contains model?:"<cursorPosition()); + //kDebug()<<"start="<commonPrefix((m_inCompletionList && !shellLikeTabCompletion) ? m_entryList->currentIndex() : QModelIndex()); + if(!prefix.isEmpty()) { + view()->insertText(prefix); + }else if(shellLikeTabCompletion) { + cursorDown(); + return; + } + }else{ + if(shellLikeTabCompletion) { + cursorUp(); + return; + } + + //Reset left boundaries, so completion isn't stopped + typedef QMap CompletionRangeMap; + for(CompletionRangeMap::iterator it = m_completionRanges.begin(); it != m_completionRanges.end(); ++it) + (*it).leftBoundary = (*it).range->start(); + + //Remove suffix until the completion-list filter is widened again + uint itemCount = m_presentationModel->filteredItemCount(); + + while(view()->cursorPosition().column() > 0 && m_presentationModel->filteredItemCount() == itemCount) { + KTextEditor::Range lastcharRange = KTextEditor::Range(view()->cursorPosition()-KTextEditor::Cursor(0,1), view()->cursorPosition()); + QString cursorText = view()->document()->text(lastcharRange); + if(!cursorText[0].isSpace()) + { + view()->document()->removeText(lastcharRange); + QApplication::sendPostedEvents(); + }else{ + break; + } + } + } +} + +#include "moc_katecompletionwidget.cpp" + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/completion/katecompletionwidget.h b/kate/part/completion/katecompletionwidget.h new file mode 100644 index 00000000..106cfd16 --- /dev/null +++ b/kate/part/completion/katecompletionwidget.h @@ -0,0 +1,240 @@ +/* This file is part of the KDE libraries and the Kate part. + * + * Copyright (C) 2005-2006 Hamish Rodda + * Copyright (C) 2007-2008 David Nolden + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef KATECOMPLETIONWIDGET_H +#define KATECOMPLETIONWIDGET_H + +#include +#include +#include +#include +#include +#include + +#include "katepartinterfaces_export.h" + +#include +#include +#include + +class KateView; +class KateCompletionModel; +class KateCompletionTree; +class KateArgumentHintTree; +class KateArgumentHintModel; + +namespace KTextEditor { + class EmbeddedWidgetInterface; +} + +/** + * This is the code completion's main widget, and also contains the + * core interface logic. + * + * @author Hamish Rodda + */ +class KATEPARTINTERFACES_EXPORT KateCompletionWidget : public QFrame +{ + Q_OBJECT + + public: + explicit KateCompletionWidget(KateView* parent); + ~KateCompletionWidget(); + + KateView* view() const; + KateCompletionTree* treeView() const; + + bool isCompletionActive() const; + void startCompletion(KTextEditor::CodeCompletionModel::InvocationType invocationType, const QList& models = QList()); + void startCompletion(const KTextEditor::Range& word, KTextEditor::CodeCompletionModel* model, KTextEditor::CodeCompletionModel::InvocationType invocationType = KTextEditor::CodeCompletionModel::ManualInvocation); + void startCompletion(const KTextEditor::Range& word, const QList& models = QList(), KTextEditor::CodeCompletionModel::InvocationType invocationType = KTextEditor::CodeCompletionModel::ManualInvocation); + void userInvokedCompletion(); + + public Q_SLOTS: + //Executed when return is pressed while completion is active. + void execute(); + void cursorDown(); + void cursorUp(); + + public: + void tab(bool shift); + + ///Returns whether the current item was expanded/unexpanded + bool toggleExpanded(bool forceExpand = false, bool forceUnExpand = false); + + const KateCompletionModel* model() const; + KateCompletionModel* model(); + + void registerCompletionModel(KTextEditor::CodeCompletionModel* model); + void unregisterCompletionModel(KTextEditor::CodeCompletionModel* model); + + int automaticInvocationDelay() const; + void setAutomaticInvocationDelay(int delay); + + struct CompletionRange{ + CompletionRange() : range(0) { + } + CompletionRange(KTextEditor::MovingRange* r) : range(r) { + } + + bool operator==(const CompletionRange& rhs) const { + return range->toRange() == rhs.range->toRange(); + } + + KTextEditor::MovingRange* range; + //Whenever the cursor goes before this position, the completion is stopped, unless it is invalid. + KTextEditor::Cursor leftBoundary; + }; + + KTextEditor::MovingRange* completionRange(KTextEditor::CodeCompletionModel* model = 0) const; + QMap completionRanges( ) const; + + // Navigation + void pageDown(); + void pageUp(); + void top(); + void bottom(); + + QWidget* currentEmbeddedWidget(); + + bool canExpandCurrentItem() const; + + bool canCollapseCurrentItem() const; + + void setCurrentItemExpanded( bool ); + + //Returns true if a screen border has been hit + bool updatePosition(bool force = false); + + virtual bool eventFilter( QObject * watched, QEvent * event ); + + KateArgumentHintTree* argumentHintTree() const; + + KateArgumentHintModel* argumentHintModel() const; + + ///Called by KateViewInternal, because we need the specific information from the event. + + void updateHeight(); + + public Q_SLOTS: + void waitForModelReset(); + + void abortCompletion(); + void showConfig(); +/* void viewFocusIn(); + void viewFocusOut();*/ + void updatePositionSlot(); + void automaticInvocation(); + +/* void updateFocus();*/ + void argumentHintsChanged(bool hasContent); + + bool navigateUp(); + bool navigateDown(); + bool navigateLeft(); + bool navigateRight(); + bool navigateAccept(); + bool navigateBack(); + + bool hadNavigation() const; + void resetHadNavigation(); + + protected: + virtual void showEvent ( QShowEvent * event ); + virtual void resizeEvent ( QResizeEvent * event ); +// virtual void focusInEvent ( QFocusEvent * event ); + + private Q_SLOTS: + void completionModelReset(); + void modelDestroyed(QObject* model); + void modelContentChanged(); + void cursorPositionChanged(); + void modelReset(); + void rowsInserted(const QModelIndex& parent, int row, int rowEnd); + void viewFocusOut(); + + void wrapLine (const KTextEditor::Cursor &position); + void unwrapLine (int line); + void insertText (const KTextEditor::Cursor &position, const QString &text); + void removeText (const KTextEditor::Range &range); + + private: + void updateAndShow(); + void updateArgumentHintGeometry(); + QModelIndex selectedIndex() const; + + void clear(); + //Switch cursor between argument-hint list / completion-list + void switchList(); + KTextEditor::Range determineRange() const; + void completionRangeChanged(KTextEditor::CodeCompletionModel*, const KTextEditor::Range& word); + + void deleteCompletionRanges(); + + QList m_sourceModels; + KateCompletionModel* m_presentationModel; + + QMap m_completionRanges; + QSet m_waitingForReset; + + KTextEditor::Cursor m_lastCursorPosition; + + KateCompletionTree* m_entryList; + KateArgumentHintModel* m_argumentHintModel; + KateArgumentHintTree* m_argumentHintTree; + + QTimer* m_automaticInvocationTimer; + //QTimer* m_updateFocusTimer; + QWidget* m_statusBar; + QToolButton* m_sortButton; + QLabel* m_sortText; + QToolButton* m_filterButton; + QLabel* m_filterText; + QPushButton* m_configButton; + + KTextEditor::Cursor m_automaticInvocationAt; + QString m_automaticInvocationLine; + int m_automaticInvocationDelay; + bool m_filterInstalled; + + class KateCompletionConfig* m_configWidget; + bool m_lastInsertionByUser; + bool m_inCompletionList; //Are we in the completion-list? If not, we're in the argument-hint list + bool m_isSuspended; + bool m_dontShowArgumentHints; //Used temporarily to prevent flashing + bool m_needShow; + + bool m_hadCompletionNavigation; + + bool m_haveExactMatch; + + bool m_noAutoHide; + + /** + * is a completion edit ongoing? + */ + bool m_completionEditRunning; + + int m_expandedAddedHeightBase; + KTextEditor::CodeCompletionModel::InvocationType m_lastInvocationType; +}; + +#endif diff --git a/kate/part/completion/katekeywordcompletion.cpp b/kate/part/completion/katekeywordcompletion.cpp new file mode 100644 index 00000000..4111903b --- /dev/null +++ b/kate/part/completion/katekeywordcompletion.cpp @@ -0,0 +1,210 @@ +/* This file is part of the KDE libraries + Copyright (C) 2014 Sven Brauch + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2, or any later version, + as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "katekeywordcompletion.h" + +#include "katehighlight.h" +#include "katehighlighthelpers.h" +#include "katedocument.h" +#include "katetextline.h" + +#include +#include + +KateKeywordCompletionModel::KateKeywordCompletionModel(QObject* parent) + : CodeCompletionModel2(parent) +{ + setHasGroups(false); +} + +void KateKeywordCompletionModel::completionInvoked(KTextEditor::View* view, const KTextEditor::Range& range, + KTextEditor::CodeCompletionModel::InvocationType /*invocationType*/) +{ + KateDocument* doc = static_cast(view->document()); + if ( ! doc->highlight() || doc->highlight()->noHighlighting() ) { + // no highlighting -- nothing to do + return; + } + + Kate::TextLine line = doc->kateTextLine(range.end().line()); + Kate::TextLine previousLine = doc->kateTextLine(range.end().line() - 1); + Kate::TextLine nextLine = doc->kateTextLine(range.end().line() + 1); + bool contextChanged; + QVector contextChanges; + // Ask the highlighting engine to re-calcualte the highlighting for the line + // where completion was invoked, and store all the context changes in the process. + doc->highlight()->doHighlight(previousLine.data(), line.data(), nextLine.data(), + contextChanged, 0, &contextChanges); + + // From the list of context changes, find the highlighting context which is + // active at the position where completion was invoked. + KateHlContext* context = 0; + foreach ( const KateHighlighting::ContextChange& change, contextChanges ) { + if ( change.pos == 0 || change.pos <= range.end().column() ) { + context = change.toContext; + } + if ( change.pos > range.end().column() ) { + break; + } + } + + // Find all keyword items which exist for that context, + // and suggest them as completion items. + QSet items; + if ( ! context ) { + // default context + context = doc->highlight()->contextNum(0); + } + foreach ( const KateHlItem* item, context->items ) { + if ( const KateHlKeyword* kw = dynamic_cast(item) ) { + items.unite(kw->allKeywords()); + } + } + m_items = items.toList(); + qSort(m_items); +} + +QModelIndex KateKeywordCompletionModel::parent(const QModelIndex& index) const +{ + if ( index.internalId() ) + return createIndex(0, 0, 0); + else + return QModelIndex(); +} + +QModelIndex KateKeywordCompletionModel::index(int row, int column, const QModelIndex& parent) const +{ + if ( !parent.isValid() ) { + if ( row == 0 ) + return createIndex(row, column, 0); + else + return QModelIndex(); + } else if ( parent.parent().isValid() ) { + return QModelIndex(); + } + + if ( row < 0 || row >= m_items.count() || column < 0 || column >= ColumnCount ) { + return QModelIndex(); + } + + return createIndex(row, column, 1); +} + +int KateKeywordCompletionModel::rowCount(const QModelIndex& parent) const +{ + if( !parent.isValid() && !m_items.isEmpty() ) + return 1; //One root node to define the custom group + else if(parent.parent().isValid()) + return 0; //Completion-items have no children + else + return m_items.count(); +} + +static bool isInWord(const KTextEditor::View* view, const KTextEditor::Cursor& position, QChar c) +{ + KateDocument* document = static_cast(view->document()); + KateHighlighting* highlight = document->highlight(); + Kate::TextLine line = document->kateTextLine(position.line()); + return highlight->isInWord(c, line->attribute(position.column()-1)); +}; + +KTextEditor::Range KateKeywordCompletionModel::completionRange(KTextEditor::View* view, + const KTextEditor::Cursor& position) +{ + const QString& text = view->document()->text(KTextEditor::Range(position, KTextEditor::Cursor(position.line(), 0))); + int pos; + for ( pos = text.size() - 1; pos >= 0; pos-- ) { + if ( isInWord(view, position, text.at(pos)) ) { + // This needs to be aware of what characters are word-characters in the + // active language, so that languages which prefix commands with e.g. @ + // or \ have properly working completion. + continue; + } + break; + } + return KTextEditor::Range(KTextEditor::Cursor(position.line(), pos + 1), position); +} + +bool KateKeywordCompletionModel::shouldAbortCompletion(KTextEditor::View* view, const KTextEditor::Range& range, + const QString& currentCompletion) +{ + if ( view->cursorPosition() < range.start() || view->cursorPosition() > range.end() ) + return true; // Always abort when the completion-range has been left + // Do not abort completions when the text has been empty already before and a newline has been entered + + foreach ( QChar c, currentCompletion ) { + if ( ! isInWord(view, range.start(), c) ) { + return true; + } + } + return false; +} + +bool KateKeywordCompletionModel::shouldStartCompletion(KTextEditor::View* /*view*/, const QString& insertedText, + bool userInsertion, const KTextEditor::Cursor& /*position*/) +{ + if ( userInsertion && insertedText.size() > 3 && ! insertedText.contains(' ') + && insertedText.at(insertedText.size()-1).isLetter() ) { + return true; + } + return false; +} + +bool KateKeywordCompletionModel::shouldHideItemsWithEqualNames() const +{ + return true; +} + +QVariant KateKeywordCompletionModel::data(const QModelIndex& index, int role) const +{ + if ( role == UnimportantItemRole ) + return QVariant(true); + if ( role == InheritanceDepth ) + return 9000; + + if ( !index.parent().isValid() ) { + // group header + switch ( role ) { + case Qt::DisplayRole: + return i18n("Language keywords"); + case GroupRole: + return Qt::DisplayRole; + } + } + + if ( index.column() == KTextEditor::CodeCompletionModel::Name && role == Qt::DisplayRole ) + return m_items.at(index.row()); + + if ( index.column() == KTextEditor::CodeCompletionModel::Icon && role == Qt::DecorationRole ) { + static const QIcon icon(KIcon("code-variable").pixmap(QSize(16, 16))); + return icon; + } + + return QVariant(); +} + +KTextEditor::CodeCompletionModelControllerInterface::MatchReaction KateKeywordCompletionModel::matchingItem( + const QModelIndex& /*matched*/) +{ + return KTextEditor::CodeCompletionModelControllerInterface::None; +} + +#include "moc_katekeywordcompletion.cpp" + +// kate: indent-width 4; replace-tabs on diff --git a/kate/part/completion/katekeywordcompletion.h b/kate/part/completion/katekeywordcompletion.h new file mode 100644 index 00000000..1fe254a3 --- /dev/null +++ b/kate/part/completion/katekeywordcompletion.h @@ -0,0 +1,62 @@ +/* This file is part of the KDE libraries + Copyright (C) 2014 Sven Brauch + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2, or any later version, + as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KATEKEYWORDCOMPLETIONMODEL_H +#define KATEKEYWORDCOMPLETIONMODEL_H + +#include "ktexteditor/codecompletionmodel.h" +#include "codecompletionmodelcontrollerinterfacev4.h" + +/** + * @brief Highlighting-file based keyword completion for the editor. + * + * This model offers completion of language-specific keywords based on information + * taken from the kate syntax files. It queries the highlighting engine to get the + * correct context for a given cursor position, then suggests all keyword items + * from the XML file for the active language. + */ +class KateKeywordCompletionModel : public KTextEditor::CodeCompletionModel2 + , public KTextEditor::CodeCompletionModelControllerInterface4 +{ + Q_OBJECT + Q_INTERFACES(KTextEditor::CodeCompletionModelControllerInterface4) + +public: + KateKeywordCompletionModel(QObject* parent); + virtual QVariant data(const QModelIndex& index, int role) const; + virtual int rowCount(const QModelIndex& parent = QModelIndex()) const; + virtual QModelIndex parent(const QModelIndex& index) const; + virtual QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const; + virtual void completionInvoked(KTextEditor::View* view, const KTextEditor::Range& range, + InvocationType invocationType); + virtual KTextEditor::Range completionRange(KTextEditor::View* view, const KTextEditor::Cursor& position); + virtual bool shouldAbortCompletion(KTextEditor::View* view, const KTextEditor::Range& range, + const QString& currentCompletion); + virtual bool shouldStartCompletion(KTextEditor::View* view, const QString& insertedText, bool userInsertion, + const KTextEditor::Cursor& position); + virtual MatchReaction matchingItem(const QModelIndex& matched); + virtual bool shouldHideItemsWithEqualNames() const; + +private: + QList m_items; +}; + +#endif // KATEKEYWORDCOMPLETIONMODEL_H + +// kate: indent-width 4; replace-tabs on diff --git a/kate/part/completion/katewordcompletion.cpp b/kate/part/completion/katewordcompletion.cpp new file mode 100644 index 00000000..029827f7 --- /dev/null +++ b/kate/part/completion/katewordcompletion.cpp @@ -0,0 +1,606 @@ +/* This file is part of the KDE libraries and the Kate part. + * + * Copyright (C) 2003 Anders Lund + * Copyright (C) 2010 Christoph Cullmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +//BEGIN includes +#include "katewordcompletion.h" +#include "kateview.h" +#include "kateconfig.h" +#include "katedocument.h" +#include "kateglobal.h" +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +//END + +/// Amount of characters the document may have to enable automatic invocation (1MB) +static const int autoInvocationMaxFilesize = 1000000; + +//BEGIN KateWordCompletionModel +KateWordCompletionModel::KateWordCompletionModel( QObject *parent ) + : CodeCompletionModel2( parent ), m_automatic(false) +{ + setHasGroups(false); +} + +KateWordCompletionModel::~KateWordCompletionModel() +{ +} + +void KateWordCompletionModel::saveMatches( KTextEditor::View* view, + const KTextEditor::Range& range) +{ + m_matches = allMatches( view, range ); + m_matches.sort(); +} + +QVariant KateWordCompletionModel::data(const QModelIndex& index, int role) const +{ + if( role == UnimportantItemRole ) + return QVariant(true); + if( role == InheritanceDepth ) + return 10000; + + if( !index.parent().isValid() ) { + //It is the group header + switch ( role ) + { + case Qt::DisplayRole: + return i18n("Auto Word Completion"); + case GroupRole: + return Qt::DisplayRole; + } + } + + if( index.column() == KTextEditor::CodeCompletionModel::Name && role == Qt::DisplayRole ) + return m_matches.at( index.row() ); + + if( index.column() == KTextEditor::CodeCompletionModel::Icon && role == Qt::DecorationRole ) { + static QIcon icon(KIcon("insert-text").pixmap(QSize(16, 16))); + return icon; + } + + return QVariant(); +} + +QModelIndex KateWordCompletionModel::parent(const QModelIndex& index) const +{ + if(index.internalId()) + return createIndex(0, 0, 0); + else + return QModelIndex(); +} + +QModelIndex KateWordCompletionModel::index(int row, int column, const QModelIndex& parent) const +{ + if( !parent.isValid()) { + if(row == 0) + return createIndex(row, column, 0); + else + return QModelIndex(); + + }else if(parent.parent().isValid()) + return QModelIndex(); + + + if (row < 0 || row >= m_matches.count() || column < 0 || column >= ColumnCount ) + return QModelIndex(); + + return createIndex(row, column, 1); +} + +int KateWordCompletionModel::rowCount ( const QModelIndex & parent ) const +{ + if( !parent.isValid() && !m_matches.isEmpty() ) + return 1; //One root node to define the custom group + else if(parent.parent().isValid()) + return 0; //Completion-items have no children + else + return m_matches.count(); +} + + +bool KateWordCompletionModel::shouldStartCompletion(KTextEditor::View* view, const QString &insertedText, bool userInsertion, const KTextEditor::Cursor &position) +{ + if (!userInsertion) return false; + if (insertedText.isEmpty()) + return false; + + + KateView *v = qobject_cast (view); + + if (view->document()->totalCharacters() > autoInvocationMaxFilesize) { + // Disable automatic invocation for files larger than 1MB (see benchmarks) + return false; + } + + const QString& text = view->document()->line(position.line()).left(position.column()); + const uint check = v->config()->wordCompletionMinimalWordLength(); + // Start completion immediately if min. word size is zero + if (!check) return true; + // Otherwise, check if user has typed long enough text... + const int start = text.length(); + const int end = start - check; + if (end < 0) return false; + for (int i = start - 1; i >= end; i--) { + const QChar c = text.at(i); + if (!(c.isLetter() || (c.isNumber()) || c=='_')) return false; + } + + return true; +} + +bool KateWordCompletionModel::shouldAbortCompletion(KTextEditor::View* view, const KTextEditor::Range &range, const QString ¤tCompletion) { + + if (m_automatic) { + KateView *v = qobject_cast (view); + if (currentCompletion.length()config()->wordCompletionMinimalWordLength()) return true; + } + + return CodeCompletionModelControllerInterface4::shouldAbortCompletion(view,range,currentCompletion); +} + + + +void KateWordCompletionModel::completionInvoked(KTextEditor::View* view, const KTextEditor::Range& range, InvocationType it) +{ + m_automatic = it == AutomaticInvocation; + saveMatches( view, range ); +} + + +/** + * Scan throughout the entire document for possible completions, + * ignoring any dublets and words shorter than configured and/or + * reasonable minimum length. + */ +QStringList KateWordCompletionModel::allMatches( KTextEditor::View *view, const KTextEditor::Range &range ) const +{ + QSet result; + const int minWordSize = qMax(2, qobject_cast(view)->config()->wordCompletionMinimalWordLength()); + const int lines = view->document()->lines(); + for ( int line = 0; line < lines; line++ ) { + const QString& text = view->document()->line(line); + int wordBegin = 0; + int offset = 0; + const int end = text.size(); + while ( offset < end ) { + const QChar c = text.at(offset); + // increment offset when at line end, so we take the last character too + if ( ( ! c.isLetterOrNumber() && c != '_' ) || (offset == end - 1 && offset++) ) { + if ( offset - wordBegin > minWordSize && ( line != range.end().line() || offset != range.end().column() ) ) { + result.insert(text.mid(wordBegin, offset - wordBegin)); + } + wordBegin = offset + 1; + } + if ( c.isSpace() ) { + wordBegin = offset + 1; + } + offset += 1; + } + } + return result.values(); +} + +void KateWordCompletionModel::executeCompletionItem2( + KTextEditor::Document* document + , const KTextEditor::Range& word + , const QModelIndex& index + ) const +{ + KateView *v = qobject_cast (document->activeView()); + if (v->config()->wordCompletionRemoveTail()) + { + int tailStart = word.end().column(); + const QString& line = document->line(word.end().line()); + int tailEnd = line.length(); + for (int i = word.end().column(); i < tailEnd; ++i) + { + // Letters, numbers and underscore are part of a word! + /// \todo Introduce configurable \e word-separators?? + if (!line[i].isLetterOrNumber() && line[i] != '_') + { + tailEnd = i; + } + } + + int sizeDiff = m_matches.at(index.row()).size() - (word.end().column() - word.start().column()); + + tailStart += sizeDiff; + tailEnd += sizeDiff; + + KTextEditor::Range tail = word; + tail.start().setColumn(tailStart); + tail.end().setColumn(tailEnd); + + document->replaceText(word, m_matches.at(index.row())); + v->doc()->editEnd(); + v->doc()->editStart(); + document->replaceText(tail, ""); + } + else + { + document->replaceText(word, m_matches.at(index.row())); + } +} + +KTextEditor::CodeCompletionModelControllerInterface::MatchReaction KateWordCompletionModel::matchingItem(const QModelIndex& /*matched*/) +{ + return HideListIfAutomaticInvocation; +} + +bool KateWordCompletionModel::shouldHideItemsWithEqualNames() const +{ + // We don't want word-completion items if the same items + // are available through more sophisticated completion models + return true; +} + +// Return the range containing the word left of the cursor +KTextEditor::Range KateWordCompletionModel::completionRange(KTextEditor::View* view, const KTextEditor::Cursor &position) +{ + int line = position.line(); + int col = position.column(); + + KTextEditor::Document *doc = view->document(); + while ( col > 0 ) + { + const QChar c = ( doc->character( KTextEditor::Cursor( line, col-1 ) ) ); + if ( c.isLetterOrNumber() || c.isMark() || c == '_' ) + { + col--; + continue; + } + + break; + } + + return KTextEditor::Range( KTextEditor::Cursor( line, col ), position ); +} +//END KateWordCompletionModel + + +//BEGIN KateWordCompletionView +struct KateWordCompletionViewPrivate +{ + KTextEditor::MovingRange* liRange; // range containing last inserted text + KTextEditor::Range dcRange; // current range to be completed by directional completion + KTextEditor::Cursor dcCursor; // directional completion search cursor + QRegExp re; // hrm + int directionalPos; // be able to insert "" at the correct time + bool isCompleting; // true when the directional completion is doing a completion +}; + +KateWordCompletionView::KateWordCompletionView( KTextEditor::View *view, KActionCollection* ac ) + : QObject( view ), + m_view( view ), + m_dWCompletionModel( KateGlobal::self()->wordCompletionModel() ), + d( new KateWordCompletionViewPrivate ) +{ + d->isCompleting = false; + d->dcRange = KTextEditor::Range::invalid(); + + d->liRange = static_cast(m_view->document())->newMovingRange(KTextEditor::Range::invalid(), KTextEditor::MovingRange::DoNotExpand); + + KColorScheme colors(QPalette::Active); + KTextEditor::Attribute::Ptr a = KTextEditor::Attribute::Ptr( new KTextEditor::Attribute() ); + a->setBackground( colors.background(KColorScheme::ActiveBackground) ); + a->setForeground( colors.foreground(KColorScheme::ActiveText) ); // ### this does 0 + d->liRange->setAttribute( a ); + + KTextEditor::CodeCompletionInterface *cci = qobject_cast(view); + + KAction *action; + + if (cci) + { + cci->registerCompletionModel( m_dWCompletionModel ); + + action = new KAction( i18n("Shell Completion"), this ); + ac->addAction( "doccomplete_sh", action ); + connect( action, SIGNAL(triggered()), this, SLOT(shellComplete()) ); + } + + + action = new KAction( i18n("Reuse Word Above"), this ); + ac->addAction( "doccomplete_bw", action ); + action->setShortcut( Qt::CTRL+Qt::Key_8 ); + connect( action, SIGNAL(triggered()), this, SLOT(completeBackwards()) ); + + action = new KAction( i18n("Reuse Word Below"), this ); + ac->addAction( "doccomplete_fw", action ); + action->setShortcut( Qt::CTRL+Qt::Key_9 ); + connect( action, SIGNAL(triggered()), this, SLOT(completeForwards()) ); +} + +KateWordCompletionView::~KateWordCompletionView() +{ + KTextEditor::CodeCompletionInterface *cci = qobject_cast(m_view); + + if (cci) cci->unregisterCompletionModel(m_dWCompletionModel); + + delete d; +} + +void KateWordCompletionView::completeBackwards() +{ + complete( false ); +} + +void KateWordCompletionView::completeForwards() +{ + complete(); +} + +// Pop up the editors completion list if applicable +void KateWordCompletionView::popupCompletionList() +{ + kDebug( 13040 ) << "entered ..."; + KTextEditor::Range r = range(); + + KTextEditor::CodeCompletionInterface *cci = qobject_cast( m_view ); + if(!cci || cci->isCompletionActive()) + return; + + m_dWCompletionModel->saveMatches( m_view, r ); + + kDebug( 13040 ) << "after save matches ..."; + + if ( ! m_dWCompletionModel->rowCount(QModelIndex()) ) return; + + cci->startCompletion( r, m_dWCompletionModel ); +} + +// Contributed by +void KateWordCompletionView::shellComplete() +{ + KTextEditor::Range r = range(); + + QStringList matches = m_dWCompletionModel->allMatches( m_view, r ); + + if (matches.size() == 0) + return; + + QString partial = findLongestUnique( matches, r.columnWidth() ); + + if ( ! partial.length() ) + popupCompletionList(); + + else + { + m_view->document()->insertText( r.end(), partial.mid( r.columnWidth() ) ); + d->liRange->setView(m_view); + d->liRange->setRange( KTextEditor::Range( r.end(), partial.length() - r.columnWidth() ) ); + connect( m_view, SIGNAL(cursorPositionChanged(KTextEditor::View*,KTextEditor::Cursor)), this, SLOT(slotCursorMoved()) ); + } +} + +// Do one completion, searching in the desired direction, +// if possible +void KateWordCompletionView::complete( bool fw ) +{ + KTextEditor::Range r = range(); + + int inc = fw ? 1 : -1; + KTextEditor::Document *doc = m_view->document(); + + if ( d->dcRange.isValid() ) + { + //kDebug( 13040 )<<"CONTINUE "<dcRange; + // this is a repeted activation + + // if we are back to where we started, reset. + if ( ( fw && d->directionalPos == -1 ) || + ( !fw && d->directionalPos == 1 ) ) + { + const int spansColumns = d->liRange->end().column() - d->liRange->start().column(); + if ( spansColumns > 0 ) + doc->removeText( *d->liRange ); + + d->liRange->setRange( KTextEditor::Range::invalid() ); + d->dcCursor = r.end(); + d->directionalPos = 0; + + return; + } + + if ( fw ) { + const int spansColumns = d->liRange->end().column() - d->liRange->start().column(); + d->dcCursor.setColumn( d->dcCursor.column() + spansColumns ); + } + + d->directionalPos += inc; + } + else // new completion, reset all + { + //kDebug( 13040 )<<"RESET FOR NEW"; + d->dcRange = r; + d->liRange->setRange( KTextEditor::Range::invalid() ); + d->dcCursor = r.start(); + d->directionalPos = inc; + + d->liRange->setView( m_view ); + + connect( m_view, SIGNAL(cursorPositionChanged(KTextEditor::View*,KTextEditor::Cursor)), this, SLOT(slotCursorMoved()) ); + + } + + d->re.setPattern( "\\b" + doc->text( d->dcRange ) + "(\\w+)" ); + int pos ( 0 ); + QString ln = doc->line( d->dcCursor.line() ); + + while ( true ) + { + //kDebug( 13040 )<<"SEARCHING FOR "<re.pattern()<<" "<dcCursor; + pos = fw ? + d->re.indexIn( ln, d->dcCursor.column() ) : + d->re.lastIndexIn( ln, d->dcCursor.column() ); + + if ( pos > -1 ) // we matched a word + { + //kDebug( 13040 )<<"USABLE MATCH"; + QString m = d->re.cap( 1 ); + if ( m != doc->text( *d->liRange ) && (d->dcCursor.line() != d->dcRange.start().line() || pos != d->dcRange.start().column() ) ) + { + // we got good a match! replace text and return. + d->isCompleting = true; + KTextEditor::Range replaceRange(d->liRange->toRange()); + if (!replaceRange.isValid()) { + replaceRange.setRange(r.end(), r.end()); + } + doc->replaceText( replaceRange, m ); + d->liRange->setRange( KTextEditor::Range( d->dcRange.end(), m.length() ) ); + + d->dcCursor.setColumn( pos ); // for next try + + d->isCompleting = false; + return; + } + + // equal to last one, continue + else + { + //kDebug( 13040 )<<"SKIPPING, EQUAL MATCH"; + d->dcCursor.setColumn( pos ); // for next try + + if ( fw ) + d->dcCursor.setColumn( pos + m.length() ); + + else + { + if ( pos == 0 ) + { + if ( d->dcCursor.line() > 0 ) + { + int l = d->dcCursor.line() + inc; + ln = doc->line( l ); + d->dcCursor.setPosition( l, ln.length() ); + } + else + { + KNotification::beep(); + return; + } + } + + else + d->dcCursor.setColumn( d->dcCursor.column()-1 ); + } + } + } + + else // no match + { + //kDebug( 13040 )<<"NO MATCH"; + if ( (! fw && d->dcCursor.line() == 0 ) || ( fw && d->dcCursor.line() >= doc->lines() ) ) + { + KNotification::beep(); + return; + } + + int l = d->dcCursor.line() + inc; + ln = doc->line( l ); + d->dcCursor.setPosition( l, fw ? 0 : ln.length() ); + } + } // while true +} + +void KateWordCompletionView::slotCursorMoved() +{ + if ( d->isCompleting) return; + + d->dcRange = KTextEditor::Range::invalid(); + + disconnect( m_view, SIGNAL(cursorPositionChanged(KTextEditor::View*,KTextEditor::Cursor)), this, SLOT(slotCursorMoved()) ); + + d->liRange->setView(0); + d->liRange->setRange(KTextEditor::Range::invalid()); +} + +// Contributed by FIXME +QString KateWordCompletionView::findLongestUnique( const QStringList &matches, int lead ) const +{ + QString partial = matches.first(); + + foreach ( const QString& current, matches ) + { + if ( !current.startsWith( partial ) ) + { + while( partial.length() > lead ) + { + partial.remove( partial.length() - 1, 1 ); + if ( current.startsWith( partial ) ) + break; + } + + if ( partial.length() == lead ) + return QString(); + } + } + + return partial; +} + +// Return the string to complete (the letters behind the cursor) +QString KateWordCompletionView::word() const +{ + return m_view->document()->text( range() ); +} + +// Return the range containing the word behind the cursor +KTextEditor::Range KateWordCompletionView::range() const +{ + return m_dWCompletionModel->completionRange(m_view, m_view->cursorPosition()); +} +//END + +#include "moc_katewordcompletion.cpp" +// kate: space-indent on; indent-width 2; replace-tabs on; mixed-indent off; diff --git a/kate/part/completion/katewordcompletion.h b/kate/part/completion/katewordcompletion.h new file mode 100644 index 00000000..5a94d61c --- /dev/null +++ b/kate/part/completion/katewordcompletion.h @@ -0,0 +1,117 @@ +/* This file is part of the KDE libraries and the Kate part. + * + * Copyright (C) 2003 Anders Lund + * Copyright (C) 2010 Christoph Cullmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _KateWordCompletion_h_ +#define _KateWordCompletion_h_ + +#include +#include +#include +#include +#include +#include "codecompletionmodelcontrollerinterfacev4.h" +#include + +#include +#include +#include + +#include + +class KATEPARTINTERFACES_EXPORT KateWordCompletionModel : public KTextEditor::CodeCompletionModel2, public KTextEditor::CodeCompletionModelControllerInterface4 +{ + Q_OBJECT + Q_INTERFACES(KTextEditor::CodeCompletionModelControllerInterface4) + public: + KateWordCompletionModel( QObject *parent ); + ~KateWordCompletionModel(); + + /** + * This function is responsible to generating / updating the list of current + * completions. The default implementation does nothing. + * + * When implementing this function, remember to call setRowCount() (or implement + * rowCount()), and to generate the appropriate change notifications (for instance + * by calling QAbstractItemModel::reset()). + * @param view The view to generate completions for + * @param range The range of text to generate completions for + * */ + void completionInvoked(KTextEditor::View* view, const KTextEditor::Range& range, InvocationType invocationType); + + bool shouldStartCompletion(KTextEditor::View* view, const QString &insertedText, bool userInsertion, const KTextEditor::Cursor &position); + bool shouldAbortCompletion(KTextEditor::View* view, const KTextEditor::Range &range, const QString ¤tCompletion); + + void saveMatches( KTextEditor::View* view, + const KTextEditor::Range& range); + + int rowCount ( const QModelIndex & parent ) const; + + QVariant data(const QModelIndex& index, int role) const; + virtual QModelIndex index(int row, int column, const QModelIndex& parent=QModelIndex()) const; + virtual QModelIndex parent(const QModelIndex& index) const; + virtual MatchReaction matchingItem(const QModelIndex& matched); + + virtual KTextEditor::Range completionRange(KTextEditor::View* view, const KTextEditor::Cursor &position); + + virtual bool shouldHideItemsWithEqualNames() const; + + QStringList allMatches( KTextEditor::View *view, const KTextEditor::Range &range ) const; + + virtual void executeCompletionItem2(KTextEditor::Document* document, const KTextEditor::Range& word, const QModelIndex& index) const; + + private: + QStringList m_matches; + bool m_automatic; +}; + +class KateWordCompletionView : public QObject +{ + Q_OBJECT + + public: + KateWordCompletionView(KTextEditor::View *view, KActionCollection* ac ); + ~KateWordCompletionView(); + + private Q_SLOTS: + void completeBackwards(); + void completeForwards(); + void slotCursorMoved(); + + void shellComplete(); + + void popupCompletionList(); + + private: + void complete( bool fw=true ); + + QString word() const; + KTextEditor::Range range() const; + + QString findLongestUnique( const QStringList &matches, int lead ) const; + + KTextEditor::View *m_view; + KateWordCompletionModel *m_dWCompletionModel; + struct KateWordCompletionViewPrivate *d; +}; + +#endif // _DocWordCompletionPlugin_h_ + +// kate: space-indent on; indent-width 2; replace-tabs on; mixed-indent off; diff --git a/kate/part/data/CMakeLists.txt b/kate/part/data/CMakeLists.txt new file mode 100644 index 00000000..321d30ef --- /dev/null +++ b/kate/part/data/CMakeLists.txt @@ -0,0 +1,3 @@ +install( FILES katepartui.rc katepartsimpleui.rc DESTINATION ${KDE4_DATA_INSTALL_DIR}/katepart ) +install( FILES katepart.desktop DESTINATION ${KDE4_SERVICES_INSTALL_DIR} ) +install( FILES katemoderc kateschemarc katesyntaxhighlightingrc DESTINATION ${KDE4_CONFIG_INSTALL_DIR} ) diff --git a/kate/part/data/katemoderc b/kate/part/data/katemoderc new file mode 100644 index 00000000..9a5872dc --- /dev/null +++ b/kate/part/data/katemoderc @@ -0,0 +1,8 @@ +[Makefile] +Variables=kate: tab-width 8; indent-width 8; replace-tabs off; replace-tabs-save off; + +[Python] +Variables=kate: indent-pasted-text false; + +[YAML] +Variables=kate: space-indent true; diff --git a/kate/part/data/katepart.desktop b/kate/part/data/katepart.desktop new file mode 100644 index 00000000..bf38b32e --- /dev/null +++ b/kate/part/data/katepart.desktop @@ -0,0 +1,61 @@ +[Desktop Entry] +Name=Embedded Advanced Text Editor +Name[ar]=محرّر نصوص مضمّن متقدّم +Name[bg]=Вграден усъвършенстван текстов редактор +Name[bs]=Ugrađeni napredni uređivač teksta +Name[ca]=Editor de text avançat incrustat +Name[ca@valencia]=Editor de text avançat incrustat +Name[cs]=Zabudovaný rozšířený editor +Name[da]=Indlejret avanceret teksteditor +Name[de]=Einbettungsfähige erweiterte Editorkomponente +Name[el]=Ενσωματωμένος προηγμένος επεξεργαστής κειμένου +Name[en_GB]=Embedded Advanced Text Editor +Name[es]=Editor de texto avanzado empotrado +Name[et]=Põimitud võimas tekstiredaktor +Name[eu]=Testu-editore aurreratu kapsulagarria +Name[fi]=Kehittynyt upotettava tekstimuokkain +Name[fr]=Éditeur de texte intégré avancé +Name[ga]=Ardeagarthóir Leabaithe Téacs +Name[gl]=Editor avanzado de textos integrado +Name[he]=עורך טקסט בר הטמעה +Name[hu]=Beágyazott szövegszerkesztő +Name[ia]=Avantiate Editor Interne de Texto +Name[it]=Editor di testi avanzato integrato +Name[ja]=埋め込みテキストエディタ +Name[kk]=Ендірілетін үздік мәтін редакторы +Name[km]=កម្មវិធី​និពន្ធ​អត្ថបទ​កម្រិត​ខ្ពស់​ដែល​បង្កប់​ +Name[ko]=끼워넣은 고급 텍스트 편집기 +Name[lt]=Vidinis sudėtingesnis teksto redaktorius +Name[lv]=Iegultais paplašinātais teksta redaktors +Name[mr]=अंतर्भूतीत प्रगत पाठ्य संपादक +Name[nb]=Innebygget, avansert skriveprogram +Name[nds]=Inbett verwiedert Texteditor +Name[nl]=Ingebed geavanceerd tekstinvoercomponent +Name[pa]=ਇੰਬੈੱਡ ਮਾਹਰ ਟੈਕਸਟ ਐਡੀਟਰ +Name[pl]=Zaawansowany osadzony edytor tekstu +Name[pt]=Editor de Texto Avançado Incorporado +Name[pt_BR]=Editor de texto avançado integrado +Name[ro]=Redactor de text avansat înglobat +Name[ru]=Встроенный расширенный текстовый редактор +Name[si]=තිළැලි උසස් පෙළ සකසනය +Name[sk]=Zabudovaný pokročilý textový editor +Name[sl]=Vgrajen napreden urejevalnik besedil +Name[sr]=Угнежђени напредни уређивач текста +Name[sr@ijekavian]=Угнијежђени напредни уређивач текста +Name[sr@ijekavianlatin]=Ugniježđeni napredni uređivač teksta +Name[sr@latin]=Ugnežđeni napredni uređivač teksta +Name[sv]=Inbäddningsbar avancerad texteditor +Name[tg]=Таҳриргари матнии беҳтаршудаи дарунсохт +Name[tr]=Gelişmiş Gömülü Metin Düzenleyici +Name[ug]=سىڭدۈرمە KDE ئالىي تېكىست تەھرىرلىگۈچ +Name[uk]=Вмонтований потужний текстовий редактор +Name[wa]=Ravalé aspougneu di tecse avancî +Name[x-test]=xxEmbedded Advanced Text Editorxx +Name[zh_CN]=嵌入式高级文本编辑器 +Name[zh_TW]=嵌入式進階文字編輯器 +X-KDE-Library=katepart +Icon=accessories-text-editor +X-KDE-ServiceTypes=KParts/ReadOnlyPart,KParts/ReadWritePart,KTextEditor/Document +Type=Service +InitialPreference=8 +MimeType=text/plain;text/x-makefile;text/x-c++hdr;text/x-c++src;text/x-chdr;text/x-csrc;text/x-java;text/x-moc;text/x-pascal;text/x-tcl;text/x-tex;application/x-shellscript;text/x-patch;text/x-adasrc;text/x-chdr;text/x-csrc;text/css;application/x-desktop;text/x-patch;text/x-fortran;text/html;text/x-java;text/x-tex;text/x-makefile;text/x-objcsrc;text/x-pascal;application/x-perl;application/x-perl;application/x-php;text/vnd.wap.wml;text/x-python;application/x-ruby;text/sgml;application/xml;model/vrml; diff --git a/kate/part/data/katepartsimpleui.rc b/kate/part/data/katepartsimpleui.rc new file mode 100644 index 00000000..bdd7b685 --- /dev/null +++ b/kate/part/data/katepartsimpleui.rc @@ -0,0 +1,90 @@ + + + + &File + + + + + + + + &Edit + + + + + + + + + + + + + + + + + + + + + + + &View + + + + + + + + &Tools + + + + + + + + + + + + + + + + + + + + + + + &Settings + + + + + + + + + + + + + + + + +Main Toolbar + + + + + + + diff --git a/kate/part/data/katepartui.rc b/kate/part/data/katepartui.rc new file mode 100644 index 00000000..af1e23fd --- /dev/null +++ b/kate/part/data/katepartui.rc @@ -0,0 +1,156 @@ + + + + &File + + + + + + + + &Edit + + + + + + + + + + + + + + + + + Find Variants + + + + + + + + + + + + + &View + + + + + + + + + + + + + + + + + &Code Folding + + + + + + + + + + + + + + + + + + + + + + + &Tools + + + + + + + + + + + + + Word Completion + + + + + + Spelling + + + + + + + + + + + + + + + + + + + + + + + + + + &Settings + + + + + + + + + + + + + + + + + + + + + +Main Toolbar + + + + + + + diff --git a/kate/part/data/kateschemarc b/kate/part/data/kateschemarc new file mode 100644 index 00000000..ce7a19ae --- /dev/null +++ b/kate/part/data/kateschemarc @@ -0,0 +1,156 @@ +# Normal Schema, NEEDED +[Normal] +ShippedDefaultSchema=9999 +Color Background=255,255,255 +Color Code Folding=148,202,239 +Color Highlighted Bracket=237,249,255 +Color Highlighted Line=248,247,246 +Color Icon Bar=214,210,208 +Color Indentation Line=210,210,210 +Color Line Number=34,31,30 +Color MarkType 1=0,0,255 +Color MarkType 2=255,0,0 +Color MarkType 3=255,255,0 +Color MarkType 4=255,0,255 +Color MarkType 5=160,160,164 +Color MarkType 6=0,255,0 +Color MarkType 7=255,0,0 +Color Modified Lines=246,230,230 +Color Replace Highlight=0,255,0 +Color Saved Lines=186,248,206 +Color Search Highlight=255,255,0 +Color Selection=148,202,239 +Color Separator=137,136,135 +Color Spelling Mistake Line=191,3,3 +Color Tab Marker=210,210,210 +Color Template Background=214,210,208 +Color Template Editable Placeholder=186,248,206 +Color Template Focused Editable Placeholder=118,218,152 +Color Template Not Editable Placeholder=246,230,230 +Color Word Wrap Marker=237,237,237 + +# Printing Schema, NEEDED +[Printing] +ShippedDefaultSchema=9990 +Color Background=255,255,255 +Color Code Folding=148,202,239 +Color Highlighted Bracket=237,249,255 +Color Highlighted Line=248,247,246 +Color Icon Bar=214,210,208 +Color Indentation Line=210,210,210 +Color Line Number=34,31,30 +Color MarkType 1=0,0,255 +Color MarkType 2=255,0,0 +Color MarkType 3=255,255,0 +Color MarkType 4=255,0,255 +Color MarkType 5=160,160,164 +Color MarkType 6=0,255,0 +Color MarkType 7=255,0,0 +Color Modified Lines=246,230,230 +Color Replace Highlight=0,255,0 +Color Saved Lines=186,248,206 +Color Search Highlight=255,255,0 +Color Selection=148,202,239 +Color Separator=137,136,135 +Color Spelling Mistake Line=191,3,3 +Color Tab Marker=210,210,210 +Color Template Background=214,210,208 +Color Template Editable Placeholder=186,248,206 +Color Template Focused Editable Placeholder=118,218,152 +Color Template Not Editable Placeholder=246,230,230 +Color Word Wrap Marker=237,237,237 + +# KDE Schema, no settings here, fallback to KDE defaults +[KDE] +ShippedDefaultSchema=9000 + +# Solarized (light), NEEDED +[Solarized (light)] +ShippedDefaultSchema=8000 +Color Background=253,246,227 +Color Code Folding=108,113,196 +Color Highlighted Bracket=7,54,66 +Color Highlighted Line=238,232,213 +Color Icon Bar=238,232,213 +Color Indentation Line=238,232,213 +Color Line Number=147,161,161 +Color MarkType 1=38,139,210 +Color MarkType 2=220,50,47 +Color MarkType 3=181,137,0 +Color MarkType 4=211,54,130 +Color MarkType 5=147,161,161 +Color MarkType 6=133,153,0 +Color MarkType 7=220,50,47 +Color Modified Lines=203,75,22 +Color Replace Highlight=133,153,0 +Color Saved Lines=42,161,152 +Color Search Highlight=181,137,0 +Color Selection=7,54,66 +Color Separator=147,161,161 +Color Spelling Mistake Line=220,50,47 +Color Tab Marker=147,161,161 +Color Template Background=238,232,213 +Color Template Editable Placeholder=238,232,213 +Color Template Focused Editable Placeholder=238,232,213 +Color Template Not Editable Placeholder=238,232,213 +Color Word Wrap Marker=147,161,161 + +# Solarized (dark), NEEDED +[Solarized (dark)] +ShippedDefaultSchema=7000 +Color Background=0,43,54 +Color Code Folding=108,113,196 +Color Highlighted Bracket=7,54,66 +Color Highlighted Line=7,54,66 +Color Icon Bar=7,54,66 +Color Indentation Line=7,54,66 +Color Line Number=147,161,161 +Color MarkType 1=38,139,210 +Color MarkType 2=220,50,47 +Color MarkType 3=181,137,0 +Color MarkType 4=211,54,130 +Color MarkType 5=147,161,161 +Color MarkType 6=133,153,0 +Color MarkType 7=220,50,47 +Color Modified Lines=203,75,22 +Color Replace Highlight=133,153,0 +Color Saved Lines=42,161,152 +Color Search Highlight=181,137,0 +Color Selection=238,232,213 +Color Separator=147,161,161 +Color Spelling Mistake Line=220,50,47 +Color Tab Marker=147,161,161 +Color Template Background=7,54,66 +Color Template Editable Placeholder=7,54,66 +Color Template Focused Editable Placeholder=7,54,66 +Color Template Not Editable Placeholder=7,54,66 +Color Word Wrap Marker=147,161,161 + +[Vim (dark)] +ShippedDefaultSchema=6000 +Color Background=0,0,0 +Color Code Folding=0,43,38 +Color Highlighted Bracket=68,0,170 +Color Highlighted Line=23,0,59 +Color Icon Bar=0,0,0 +Color Indentation Line=42,0,210 +Color Line Number=0,93,122 +Color MarkType1=0,0,255 +Color MarkType2=255,0,0 +Color MarkType3=255,255,0 +Color MarkType4=255,0,255 +Color MarkType5=160,160,164 +Color MarkType6=0,255,0 +Color MarkType7=255,0,0 +Color Modified Lines=84,255,84 +Color Replace Highlight=84,255,84 +Color Saved Lines=84,84,255 +Color Search Highlight=255,255,0 +Color Selection=35,35,35 +Color Spelling Mistake Line=255,0,0 +Color Tab Marker=65,65,65 +Color Template Background=204,204,204 +Color Template Editable Placeholder=204,255,204 +Color Template Focused Editable Placeholder=102,255,102 +Color Template Not Editable Placeholder=255,204,204 +Color Word Wrap Marker=38,38,38 diff --git a/kate/part/data/katesyntaxhighlightingrc b/kate/part/data/katesyntaxhighlightingrc new file mode 100644 index 00000000..aa57aad9 --- /dev/null +++ b/kate/part/data/katesyntaxhighlightingrc @@ -0,0 +1,84 @@ +# Normal Schema, Default Item Styles +[Default Item Styles - Schema Normal] +Alert=ffbf0303,ff9c0e0e,1,,,,fff7e6e6,-,,--- +Base-N Integer=ffb08000,ffffdd00,,,,,-,-,,--- +Character=ff924c9d,ff6c2477,,,,,-,-,,--- +Comment=ff898887,ffc7e2f8,,,,,-,-,,--- +Data Type=ff0057ae,ff00316e,,,,,-,-,,--- +Decimal/Value=ffb08000,ffffdd00,,,,,-,-,,--- +Error=ffbf0303,ff9c0e0e,,,,,-,-,,--- +Floating Point=ffb08000,ffffdd00,,,,,-,-,,--- +Function=ff644a9b,ff452886,,,,,-,-,,--- +Keyword=ff1f1c1b,ffffffff,1,,,,-,-,,--- +Normal=ff1f1c1b,ffffffff,,,,,-,-,,--- +Others=ff006e28,ff80ff80,,,,,-,-,,--- +Region Marker=ff0057ae,ff00316e,,,,,ffe0e9f8,-,,--- +String=ffbf0303,ff9c0e0e,,,,,-,-,,--- + +# Printing Schema, Default Item Styles +[Default Item Styles - Schema Printing] +Alert=ffbf0303,ff9c0e0e,1,,,,fff7e6e6,-,,--- +Base-N Integer=ffb08000,ffffdd00,,,,,-,-,,--- +Character=ff924c9d,ff6c2477,,,,,-,-,,--- +Comment=ff898887,ffc7e2f8,,,,,-,-,,--- +Data Type=ff0057ae,ff00316e,,,,,-,-,,--- +Decimal/Value=ffb08000,ffffdd00,,,,,-,-,,--- +Error=ffbf0303,ff9c0e0e,,,,,-,-,,--- +Floating Point=ffb08000,ffffdd00,,,,,-,-,,--- +Function=ff644a9b,ff452886,,,,,-,-,,--- +Keyword=ff1f1c1b,ffffffff,1,,,,-,-,,--- +Normal=ff000000,ffffffff,,,,,-,-,,--- +Others=ff006e28,ff80ff80,,,,,-,-,,--- +Region Marker=ff0057ae,ff00316e,,,,,ffe0e9f8,-,,--- +String=ffbf0303,ff9c0e0e,,,,,-,-,,--- + +# Solarized (light), Default Item Styles +[Default Item Styles - Schema Solarized (light)] +Alert=ffd33682,ffd33682,1,,,,-,-,,--- +Base-N Integer=ffb58900,ffb58900,,,,,-,-,,--- +Character=ffd33682,ffd33682,,,,,-,-,,--- +Comment=ff93a1a1,ff93a1a1,,,,,-,-,,--- +Data Type=ff268bd2,ff268bd2,,,,,-,-,,--- +Decimal/Value=ffb58900,ffb58900,,,,,-,-,,--- +Error=ffdc322f,ffdc322f,,,,,-,-,,--- +Floating Point=ffb58900,ffb58900,,,,,-,-,,--- +Function=ff6c71c4,ff6c71c4,,,,,-,-,,--- +Keyword=ff839496,ff839496,1,,,,-,-,,--- +Normal=ff839496,ff839496,,,,,-,-,,--- +Others=ff859900,ff859900,,,,,-,-,,--- +Region Marker=ff268bd2,ff268bd2,,,,,ffeee8d5,-,,--- +String=ffdc322f,ffdc322f,,,,,-,-,,--- + +# Solarized (dark), Default Item Styles +[Default Item Styles - Schema Solarized (dark)] +Alert=ffd33682,ffd33682,1,,,,-,-,,--- +Base-N Integer=ffb58900,ffb58900,,,,,-,-,,--- +Character=ffd33682,ffd33682,,,,,-,-,,--- +Comment=ff93a1a1,ff93a1a1,,,,,-,-,,--- +Data Type=ff268bd2,ff268bd2,,,,,-,-,,--- +Decimal/Value=ffb58900,ffb58900,,,,,-,-,,--- +Error=ffdc322f,ffdc322f,,,,,-,-,,--- +Floating Point=ffb58900,ffb58900,,,,,-,-,,--- +Function=ff6c71c4,ff6c71c4,,,,,-,-,,--- +Keyword=ff839496,ff839496,1,,,,-,-,,--- +Normal=ff839496,ff839496,,,,,-,-,,--- +Others=ff859900,ff859900,,,,,-,-,,--- +Region Marker=ff268bd2,ff268bd2,,,,,ffeee8d5,-,,--- +String=ffdc322f,ffdc322f,,,,,-,-,,--- + +# Vim (dark), Default Item Styles +[Default Item Styles - Schema Vim (dark)] +Alert=ffbf0303,ffbf0303,1,,,,fff7e6e6,-,,--- +Base-N Integer=ffff54ff,ffff54ff,1,,,,-,-,,--- +Character=ffff54ff,ffff54ff,1,,,,-,-,,--- +Comment=ff5454ff,ff5454ff,1,,,,-,-,,--- +Data Type=ff54ff54,ff54ff54,1,,,,-,-,,--- +Decimal/Value=ffff54ff,ffff54ff,1,,,,-,-,,--- +Error=ffbf0303,ffbf0303,0,0,0,,-,-,,--- +Floating Point=ffff54ff,ffff54ff,1,,,,-,-,,--- +Function=ff644a9b,ff644a9b,,,,,-,-,,--- +Keyword=ffdede49,ffdede49,1,,,,-,-,,--- +Normal=ffb2b2b2,ffb2b2b2,1,0,,,-,-,,--- +Others=ff006e28,ff006e28,,,,,-,-,,--- +Region Marker=ff0095ff,ff0095ff,1,,,,ff22226d,-,,--- +String=ffaf7f00,ffaf7f00,1,,,,-,-,,--- diff --git a/kate/part/dialogs/bordersappearanceconfigwidget.ui b/kate/part/dialogs/bordersappearanceconfigwidget.ui new file mode 100644 index 00000000..deb5dad9 --- /dev/null +++ b/kate/part/dialogs/bordersappearanceconfigwidget.ui @@ -0,0 +1,274 @@ + + + BordersAppearanceConfigWidget + + + + + + Borders + + + + + + If this option is checked, every new view will display marks for folding. + + + Show &folding markers + + + + + + + <p>If this option is checked, every new view will display an icon border on the left hand side.</p><p>The icon border shows bookmark signs, for instance.</p> + + + Show &icon border + + + + + + + If this option is checked, every new view will display line numbers on the left hand side. + + + Show &line numbers + + + + + + + If this option is checked, a small indicator for modified and saved lines is shown on the left hand side. + + + Show line modification markers + + + + + + + <p>If this option is checked, every new view will show marks on the vertical scrollbar.</p><p>These marks will show bookmarks, for instance.</p> + + + Show &scrollbar marks + + + + + + + If this option is checked, every new view will show a mini map on the vertical scrollbar. + + + Show scrollbar mini-map + + + + + + + false + + + + + + false + + + If this option is checked, every new view will show a mini map of the whole document on the vertical scrollbar. + + + Map the whole document + + + true + + + + + + + 40 + + + 100 + + + 5 + + + 60 + + + + + + + Minimap Width + + + spBoxMiniMapWidth + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 16 + 16 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + Scrollbars visibility: + + + cmbShowScrollbars + + + + + + + + Always On + + + + + Show When Needed + + + + + Always Off + + + + + + + + + + + + + Choose how the bookmarks should be ordered in the <b>Bookmarks</b> menu. + + + Sort Bookmarks Menu + + + + + + Each new bookmark will be added to the bottom, independently from where it is placed in the document. + + + By c&reation + + + + + + + The bookmarks will be ordered by the line numbers they are placed at. + + + By &position + + + + + + + + + + Qt::Vertical + + + + 20 + 31 + + + + + + + + + KIntSpinBox + QSpinBox +
knuminput.h
+
+
+ + chkShowFoldingMarkers + chkIconBorder + chkLineNumbers + chkShowLineModification + chkScrollbarMarks + chkScrollbarMiniMap + chkScrollbarMiniMapAll + spBoxMiniMapWidth + cmbShowScrollbars + rbSortBookmarksByCreation + rbSortBookmarksByPosition + + + + + chkScrollbarMiniMap + toggled(bool) + miniMapConfigs + setEnabled(bool) + + + 62 + 162 + + + 23 + 191 + + + + +
diff --git a/kate/part/dialogs/commandmenuconfigwidget.ui b/kate/part/dialogs/commandmenuconfigwidget.ui new file mode 100644 index 00000000..6448edf6 --- /dev/null +++ b/kate/part/dialogs/commandmenuconfigwidget.ui @@ -0,0 +1,175 @@ + + + CmdBindingConfigWidget + + + + 0 + + + + + + 0 + 1 + + + + false + + + + Name + + + + + Command + + + + + Description + + + + + + + + 0 + + + + + false + + + Edit Entry... + + + + + + + false + + + Remove Entry + + + + + + + Qt::Horizontal + + + + 1 + 0 + + + + + + + + Add Entry... + + + + + + + + + + 0 + 0 + + + + Further Notes + + + true + + + + + + + 0 + 0 + + + + <p>The entries are accessible through the submenu <b>Commands</b> in the <b>Tools</b> menu. For faster access it is possible to assign <b>shortcuts</b> in the shortcut configuration page after applying the changes.</p> + + + Qt::RichText + + + true + + + + + + + 0 + + + + + + 0 + 0 + + + + + + + + Qt::Vertical + + + + 0 + 1 + + + + + + + + + + + + + Qt::Vertical + + + + + + + + KSeparator + QFrame +
kseparator.h
+
+
+ + treeWidget + btnAddEntry + btnRemoveEntry + btnEditEntry + + + +
diff --git a/kate/part/dialogs/commandmenueditwidget.ui b/kate/part/dialogs/commandmenueditwidget.ui new file mode 100644 index 00000000..f89b3c63 --- /dev/null +++ b/kate/part/dialogs/commandmenueditwidget.ui @@ -0,0 +1,180 @@ + + + CmdBindingEditWidget + + + + 0 + + + + + Edit Command + + + true + + + + + + 0 + + + + + &Associated command: + + + cmbCommand + + + + + + + true + + + Qt::CaseSensitive + + + false + + + + + + + Qt::Horizontal + + + + 1 + 0 + + + + + + + + + + 0 + + + + + &Name: + + + edtName + + + + + + + 0 + + + + + + + + Choose an icon. + + + <p>This icon will be displayed in the menu and toolbar.</p> + + + + + + + + + + + + &Description: + + + edtDescription + + + + + + + + + + &Category: + + + cmbCategory + + + + + + + true + + + Qt::CaseSensitive + + + false + + + + + + + + + + + + Qt::Vertical + + + + 0 + 1 + + + + + + + + + KIconButton + QPushButton +
kicondialog.h
+
+ + KLineEdit + QLineEdit +
klineedit.h
+
+ + KComboBox + QComboBox +
kcombobox.h
+
+
+ + edtName + edtDescription + cmbCategory + cmbCommand + + + +
diff --git a/kate/part/dialogs/completionconfigtab.ui b/kate/part/dialogs/completionconfigtab.ui new file mode 100644 index 00000000..1cf248e0 --- /dev/null +++ b/kate/part/dialogs/completionconfigtab.ui @@ -0,0 +1,131 @@ + + + CompletionConfigTab + + + + 0 + 0 + 465 + 228 + + + + + 0 + + + + + General + + + + + + Enable &auto completion + + + + + + + + + + Auto Word Completion + + + true + + + false + + + + + + + + Minimal word length to complete: + + + + + + + + + + Qt::Horizontal + + + + 1 + 0 + + + + + + + + + + Remove tail of a previous word when completion item chosen from a list + + + Remove tail on complete + + + + + + + + + + Keyword completion + + + true + + + + + + Keyword completion provides suggestions based on the keywords which exist in the document's language. + + + true + + + + + + + + + + Qt::Vertical + + + + 0 + 1 + + + + + + + + + KIntSpinBox + QSpinBox +
knuminput.h
+
+
+ + +
diff --git a/kate/part/dialogs/editconfigwidget.ui b/kate/part/dialogs/editconfigwidget.ui new file mode 100644 index 00000000..1e2aea4d --- /dev/null +++ b/kate/part/dialogs/editconfigwidget.ui @@ -0,0 +1,124 @@ + + + EditConfigWidget + + + + 0 + + + + + Static Word Wrap + + + + + + <p>Automatically start a new line of text when the current line exceeds the length specified by the <b>Wrap words at:</b> option.</p><p>This option does not wrap existing lines of text - use the <b>Apply Static Word Wrap</b> option in the <b>Tools</b> menu for that purpose.</p><p>If you want lines to be <i>visually wrapped</i> instead, according to the width of the view, enable <b>Dynamic Word Wrap</b> in the <b>Appearance</b> config page.</p> + + + Enable static &word wrap + + + + + + + <p>If this option is checked, a vertical line will be drawn at the word wrap column as defined in the <strong>Editing</strong> properties.</p><p>Note that the word wrap marker is only drawn if you use a fixed pitch font.</p> + + + Show static word wra&p marker (if applicable) + + + + + + + 0 + + + + + W&rap words at: + + + sbWordWrap + + + + + + + If the Word Wrap option is selected this entry determines the length (in characters) at which the editor will automatically start a new line. + + + 20 + + + 200 + + + 76 + + + + + + + Qt::Horizontal + + + + 1 + 0 + + + + + + + + + + + + + Misc + + + + + + Copy/Cut the current line if no selection + + + + + + + + + + Qt::Vertical + + + + 0 + 1 + + + + + + + + + KIntSpinBox + QSpinBox +
knuminput.h
+
+
+ + +
diff --git a/kate/part/dialogs/indentationconfigwidget.ui b/kate/part/dialogs/indentationconfigwidget.ui new file mode 100644 index 00000000..d96614c2 --- /dev/null +++ b/kate/part/dialogs/indentationconfigwidget.ui @@ -0,0 +1,288 @@ + + + IndentationConfigWidget + + + + 0 + + + + + 0 + + + + + Default indentation mode: + + + cmbMode + + + + + + + This is a list of available indentation modes. The specified indentation mode will be used for all new documents. Be aware that it is also possible to set the indentation mode with document variables, modes or a .kateconfig file. + + + + + + + Qt::Horizontal + + + + 1 + 0 + + + + + + + + + + Indent using + + + + + + &Tabulators + + + + + + + &Spaces + + + + + + + &Indentation width: + + + sbIndentWidth + + + + + + + The indentation width is the number of spaces which is used to indent a line. If the option <b>Insert spaces instead of tabulators</b> in the section <b>Editing</b> is disabled, a <b>Tab</b> character is inserted if the indentation is divisible by the tab width. + + + 1 + + + 16 + + + 4 + + + + + + + Tabulators &and Spaces + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Tab wi&dth: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + sbTabWidth + + + + + + + 1 + + + 16 + + + 8 + + + + + + + + + + Indentation Properties + + + + + + If this option is disabled, changing the indentation level aligns a line to a multiple of the width specified in <b>Indentation width</b>. + + + &Keep extra spaces + + + + + + + If this option is selected, pasted code from the clipboard is indented. Triggering the <b>undo</b>-action removes the indentation. + + + Adjust indentation of code &pasted from the clipboard + + + + + + + + + + Indentation Actions + + + + + + If this option is selected, the <b>Backspace</b> key decreases the indentation level if the cursor is located in the leading blank space of a line. + + + &Backspace key in leading blank space unindents + + + + + + + <html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body> +<p>Tab key action (if no selection exists) <a href="If you want <b>Tab</b> to align the current line in the current code block like in emacs, make <b>Tab</b> a shortcut to the action <b>Align</b>."><span>More ...</span></a></p></body></html> + + + + + + + 0 + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 20 + 0 + + + + + + + + 0 + + + + + If this option is selected, the <b>Tab</b> key always inserts white space so that the next tab position is reached. If the option <b>Insert spaces instead of tabulators</b> in the section <b>Editing</b> is enabled, spaces are inserted; otherwise, a single tabulator is inserted. + + + Always advance to the &next tab position + + + + + + + If this option is selected, the <b>Tab</b> key always indents the current line by the number of character positions specified in <b>Indentation width</b>. + + + Always increase indentation &level + + + + + + + If this option is selected, the <b>Tab</b> key either indents the current line or advances to the next tab position.<p> If the insertion point is at or before the first non-space character in the line, or if there is a selection, the current line is indented by the number of character positions specified in <b>Indentation width</b>.<p> If the insertion point is located after the first non-space character in the line and there is no selection, white space is inserted so that the next tab position is reached: if the option <b>Insert spaces instead of tabulators</b> in the section <b>Editing</b> is enabled, spaces are inserted; otherwise, a single tabulator is inserted. + + + Increase indentation level if in l&eading blank space + + + + + + + + + + + + + + Qt::Vertical + + + + 0 + 1 + + + + + + + + + KIntSpinBox + QSpinBox +
knuminput.h
+
+ + KComboBox + QComboBox +
kcombobox.h
+
+
+ + +
diff --git a/kate/part/dialogs/katedialogs.cpp b/kate/part/dialogs/katedialogs.cpp new file mode 100644 index 00000000..b86769fe --- /dev/null +++ b/kate/part/dialogs/katedialogs.cpp @@ -0,0 +1,1460 @@ +/* This file is part of the KDE libraries + Copyright (C) 2002, 2003 Anders Lund + Copyright (C) 2003 Christoph Cullmann + Copyright (C) 2001 Joseph Wenninger + Copyright (C) 2006 Dominik Haumann + Copyright (C) 2007 Mirko Stocker + Copyright (C) 2009 Michel Ludwig + Copyright (C) 2009 Erlend Hamberg + + Based on work of: + Copyright (C) 1999 Jochen Wilhelmy + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +//BEGIN Includes +#include "katedialogs.h" +#include "moc_katedialogs.cpp" + +#include "kateautoindent.h" +#include "katebuffer.h" +#include "kateconfig.h" +#include "katedocument.h" +#include "kateglobal.h" +#include "kateschema.h" +#include "katesyntaxdocument.h" +#include "katemodeconfigpage.h" +#include "kateview.h" +#include "katepartpluginmanager.h" +#include "kpluginselector.h" +#include "spellcheck/spellcheck.h" + +// auto generated ui files +#include "ui_modonhdwidget.h" +#include "ui_textareaappearanceconfigwidget.h" +#include "ui_bordersappearanceconfigwidget.h" +#include "ui_navigationconfigwidget.h" +#include "ui_editconfigwidget.h" +#include "ui_indentationconfigwidget.h" +#include "ui_completionconfigtab.h" +#include "ui_opensaveconfigwidget.h" +#include "ui_opensaveconfigadvwidget.h" +#include "ui_spellcheckconfigwidget.h" + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// trailing slash is important +#define HLDOWNLOADPATH "http://kate.kde.org/syntax/" + +//END + +//BEGIN KateConfigPage +KateConfigPage::KateConfigPage ( QWidget *parent, const char * ) + : KTextEditor::ConfigPage (parent) + , m_changed (false) +{ + connect (this, SIGNAL(changed()), this, SLOT(somethingHasChanged())); +} + +KateConfigPage::~KateConfigPage () +{ +} + +void KateConfigPage::slotChanged() +{ + emit changed(); +} + +void KateConfigPage::somethingHasChanged () +{ + m_changed = true; + kDebug (13000) << "TEST: something changed on the config page: " << this; +} +//END KateConfigPage + +//BEGIN KateIndentConfigTab +KateIndentConfigTab::KateIndentConfigTab(QWidget *parent) + : KateConfigPage(parent) +{ + // This will let us have more separation between this page and + // the KTabWidget edge (ereslibre) + QVBoxLayout *layout = new QVBoxLayout; + QWidget *newWidget = new QWidget(this); + + ui = new Ui::IndentationConfigWidget(); + ui->setupUi( newWidget ); + + ui->cmbMode->addItems (KateAutoIndent::listModes()); + + ui->label->setTextInteractionFlags(Qt::LinksAccessibleByMouse | Qt::LinksAccessibleByKeyboard); + connect(ui->label, SIGNAL(linkActivated(QString)), this, SLOT(showWhatsThis(QString))); + + // What's This? help can be found in the ui file + + reload (); + + // + // after initial reload, connect the stuff for the changed () signal + // + + connect(ui->cmbMode, SIGNAL(activated(int)), this, SLOT(slotChanged())); + connect(ui->rbIndentWithTabs, SIGNAL(toggled(bool)), this, SLOT(slotChanged())); + connect(ui->rbIndentWithSpaces, SIGNAL(toggled(bool)), this, SLOT(slotChanged())); + connect(ui->rbIndentMixed, SIGNAL(toggled(bool)), this, SLOT(slotChanged())); + connect(ui->rbIndentWithTabs, SIGNAL(toggled(bool)), ui->sbIndentWidth, SLOT(setDisabled(bool))); + + connect(ui->chkKeepExtraSpaces, SIGNAL(toggled(bool)), this, SLOT(slotChanged())); + connect(ui->chkIndentPaste, SIGNAL(toggled(bool)), this, SLOT(slotChanged())); + connect(ui->chkBackspaceUnindents, SIGNAL(toggled(bool)), this, SLOT(slotChanged())); + + connect(ui->sbTabWidth, SIGNAL(valueChanged(int)), this, SLOT(slotChanged())); + connect(ui->sbIndentWidth, SIGNAL(valueChanged(int)), this, SLOT(slotChanged())); + + connect(ui->rbTabAdvances, SIGNAL(toggled(bool)), this, SLOT(slotChanged())); + connect(ui->rbTabIndents, SIGNAL(toggled(bool)), this, SLOT(slotChanged())); + connect(ui->rbTabSmart, SIGNAL(toggled(bool)), this, SLOT(slotChanged())); + + layout->addWidget(newWidget); + setLayout(layout); +} + +KateIndentConfigTab::~KateIndentConfigTab() +{ + delete ui; +} + +void KateIndentConfigTab::slotChanged() +{ + if (ui->rbIndentWithTabs->isChecked()) + ui->sbIndentWidth->setValue(ui->sbTabWidth->value()); + + KateConfigPage::slotChanged(); +} + +void KateIndentConfigTab::showWhatsThis(const QString& text) +{ + QWhatsThis::showText(QCursor::pos(), text); +} + +void KateIndentConfigTab::apply () +{ + // nothing changed, no need to apply stuff + if (!hasChanged()) + return; + m_changed = false; + + KateDocumentConfig::global()->configStart (); + + KateDocumentConfig::global()->setKeepExtraSpaces(ui->chkKeepExtraSpaces->isChecked()); + KateDocumentConfig::global()->setBackspaceIndents(ui->chkBackspaceUnindents->isChecked()); + KateDocumentConfig::global()->setIndentPastedText(ui->chkIndentPaste->isChecked()); + KateDocumentConfig::global()->setIndentationWidth(ui->sbIndentWidth->value()); + KateDocumentConfig::global()->setIndentationMode(KateAutoIndent::modeName(ui->cmbMode->currentIndex())); + KateDocumentConfig::global()->setTabWidth(ui->sbTabWidth->value()); + KateDocumentConfig::global()->setReplaceTabsDyn(ui->rbIndentWithSpaces->isChecked()); + + if (ui->rbTabAdvances->isChecked()) + KateDocumentConfig::global()->setTabHandling( KateDocumentConfig::tabInsertsTab ); + else if (ui->rbTabIndents->isChecked()) + KateDocumentConfig::global()->setTabHandling( KateDocumentConfig::tabIndents ); + else + KateDocumentConfig::global()->setTabHandling( KateDocumentConfig::tabSmart ); + + KateDocumentConfig::global()->configEnd (); +} + +void KateIndentConfigTab::reload () +{ + ui->sbTabWidth->setSuffix(ki18np(" character", " characters")); + ui->sbTabWidth->setValue(KateDocumentConfig::global()->tabWidth()); + ui->sbIndentWidth->setSuffix(ki18np(" character", " characters")); + ui->sbIndentWidth->setValue(KateDocumentConfig::global()->indentationWidth()); + ui->chkKeepExtraSpaces->setChecked(KateDocumentConfig::global()->keepExtraSpaces()); + ui->chkIndentPaste->setChecked(KateDocumentConfig::global()->indentPastedText()); + ui->chkBackspaceUnindents->setChecked(KateDocumentConfig::global()->backspaceIndents()); + + ui->rbTabAdvances->setChecked( KateDocumentConfig::global()->tabHandling() == KateDocumentConfig::tabInsertsTab ); + ui->rbTabIndents->setChecked( KateDocumentConfig::global()->tabHandling() == KateDocumentConfig::tabIndents ); + ui->rbTabSmart->setChecked( KateDocumentConfig::global()->tabHandling() == KateDocumentConfig::tabSmart ); + + ui->cmbMode->setCurrentIndex (KateAutoIndent::modeNumber (KateDocumentConfig::global()->indentationMode())); + + if (KateDocumentConfig::global()->replaceTabsDyn()) + ui->rbIndentWithSpaces->setChecked (true); + else + { + if (KateDocumentConfig::global()->indentationWidth() == KateDocumentConfig::global()->tabWidth()) + ui->rbIndentWithTabs->setChecked (true); + else + ui->rbIndentMixed->setChecked (true); + } + + ui->sbIndentWidth->setEnabled(!ui->rbIndentWithTabs->isChecked()); +} +//END KateIndentConfigTab + +//BEGIN KateCompletionConfigTab +KateCompletionConfigTab::KateCompletionConfigTab(QWidget *parent) + : KateConfigPage(parent) +{ + // This will let us have more separation between this page and + // the KTabWidget edge (ereslibre) + QVBoxLayout *layout = new QVBoxLayout; + QWidget *newWidget = new QWidget(this); + + ui = new Ui::CompletionConfigTab (); + ui->setupUi( newWidget ); + + // What's This? help can be found in the ui file + + reload (); + + // + // after initial reload, connect the stuff for the changed () signal + // + + connect(ui->chkAutoCompletionEnabled, SIGNAL(toggled(bool)), this, SLOT(slotChanged())); + connect(ui->gbWordCompletion, SIGNAL(toggled(bool)), this, SLOT(slotChanged())); + connect(ui->gbKeywordCompletion, SIGNAL(toggled(bool)), this, SLOT(slotChanged())); + connect(ui->minimalWordLength, SIGNAL(valueChanged(int)), this, SLOT(slotChanged())); + connect(ui->removeTail, SIGNAL(toggled(bool)), this, SLOT(slotChanged())); + + layout->addWidget(newWidget); + setLayout(layout); +} + +KateCompletionConfigTab::~KateCompletionConfigTab() +{ + delete ui; +} + +void KateCompletionConfigTab::showWhatsThis(const QString& text) +{ + QWhatsThis::showText(QCursor::pos(), text); +} + +void KateCompletionConfigTab::apply () +{ + // nothing changed, no need to apply stuff + if (!hasChanged()) + return; + m_changed = false; + + KateViewConfig::global()->configStart (); + KateViewConfig::global()->setAutomaticCompletionInvocation (ui->chkAutoCompletionEnabled->isChecked()); + KateViewConfig::global()->setWordCompletion (ui->gbWordCompletion->isChecked()); + KateViewConfig::global()->setWordCompletionMinimalWordLength (ui->minimalWordLength->value()); + KateViewConfig::global()->setWordCompletionRemoveTail (ui->removeTail->isChecked()); + KateViewConfig::global()->setKeywordCompletion (ui->gbKeywordCompletion->isChecked()); + KateViewConfig::global()->configEnd (); +} + +void KateCompletionConfigTab::reload () +{ + ui->chkAutoCompletionEnabled->setChecked( KateViewConfig::global()->automaticCompletionInvocation () ); + ui->gbWordCompletion->setChecked( KateViewConfig::global()->wordCompletion () ); + ui->gbKeywordCompletion->setChecked( KateViewConfig::global()->keywordCompletion () ); + ui->minimalWordLength->setValue (KateViewConfig::global()->wordCompletionMinimalWordLength ()); + ui->removeTail->setChecked (KateViewConfig::global()->wordCompletionRemoveTail ()); +} +//END KateCompletionConfigTab + +//BEGIN KateSpellCheckConfigTab +KateSpellCheckConfigTab::KateSpellCheckConfigTab(QWidget *parent) + : KateConfigPage(parent) +{ + // This will let us have more separation between this page and + // the KTabWidget edge (ereslibre) + QVBoxLayout *layout = new QVBoxLayout; + QWidget *newWidget = new QWidget(this); + + ui = new Ui::SpellCheckConfigWidget(); + ui->setupUi(newWidget); + + // What's This? help can be found in the ui file + reload(); + + // + // after initial reload, connect the stuff for the changed () signal + + m_sonnetConfigWidget = new Sonnet::ConfigWidget(KGlobal::config().data(), this); + connect(m_sonnetConfigWidget, SIGNAL(configChanged()), this, SLOT(slotChanged())); + layout->addWidget(m_sonnetConfigWidget); + + layout->addWidget(newWidget); + setLayout(layout); +} + +KateSpellCheckConfigTab::~KateSpellCheckConfigTab() +{ + delete ui; +} + +void KateSpellCheckConfigTab::showWhatsThis(const QString& text) +{ + QWhatsThis::showText(QCursor::pos(), text); +} + +void KateSpellCheckConfigTab::apply() +{ + if (!hasChanged()) { + // nothing changed, no need to apply stuff + return; + } + m_changed = false; + + KateDocumentConfig::global()->configStart(); + m_sonnetConfigWidget->save(); + KateDocumentConfig::global()->configEnd(); + foreach (KateDocument *doc, KateGlobal::self()->kateDocuments()) { + doc->refreshOnTheFlyCheck(); + } +} + +void KateSpellCheckConfigTab::reload() +{ + // does nothing +} +//END KateSpellCheckConfigTab + +//BEGIN KateNavigationConfigTab +KateNavigationConfigTab::KateNavigationConfigTab(QWidget *parent) + : KateConfigPage(parent) +{ + // This will let us having more separation between this page and + // the KTabWidget edge (ereslibre) + QVBoxLayout *layout = new QVBoxLayout; + QWidget *newWidget = new QWidget(this); + + ui = new Ui::NavigationConfigWidget(); + ui->setupUi( newWidget ); + + // What's This? Help is in the ui-files + + reload (); + + // + // after initial reload, connect the stuff for the changed () signal + // + + connect(ui->cbTextSelectionMode, SIGNAL(currentIndexChanged(int)), this, SLOT(slotChanged())); + connect(ui->chkSmartHome, SIGNAL(toggled(bool)), this, SLOT(slotChanged())); + connect(ui->chkPagingMovesCursor, SIGNAL(toggled(bool)), this, SLOT(slotChanged())); + connect(ui->sbAutoCenterCursor, SIGNAL(valueChanged(int)), this, SLOT(slotChanged())); + connect(ui->chkScrollPastEnd, SIGNAL(toggled(bool)), this, SLOT(slotChanged())); + + layout->addWidget(newWidget); + setLayout(layout); +} + +KateNavigationConfigTab::~KateNavigationConfigTab() +{ + delete ui; +} + +void KateNavigationConfigTab::apply () +{ + // nothing changed, no need to apply stuff + if (!hasChanged()) + return; + m_changed = false; + + KateViewConfig::global()->configStart (); + KateDocumentConfig::global()->configStart (); + + KateDocumentConfig::global()->setSmartHome(ui->chkSmartHome->isChecked()); + + KateViewConfig::global()->setAutoCenterLines(qMax(0, ui->sbAutoCenterCursor->value())); + KateDocumentConfig::global()->setPageUpDownMovesCursor(ui->chkPagingMovesCursor->isChecked()); + + KateViewConfig::global()->setPersistentSelection (ui->cbTextSelectionMode->currentIndex() == 1); + + KateViewConfig::global()->setScrollPastEnd(ui->chkScrollPastEnd->isChecked()); + + KateDocumentConfig::global()->configEnd (); + KateViewConfig::global()->configEnd (); +} + +void KateNavigationConfigTab::reload () +{ + ui->cbTextSelectionMode->setCurrentIndex( KateViewConfig::global()->persistentSelection() ? 1 : 0 ); + + ui->chkSmartHome->setChecked(KateDocumentConfig::global()->smartHome()); + ui->chkPagingMovesCursor->setChecked(KateDocumentConfig::global()->pageUpDownMovesCursor()); + ui->sbAutoCenterCursor->setValue(KateViewConfig::global()->autoCenterLines()); + ui->chkScrollPastEnd->setChecked(KateViewConfig::global()->scrollPastEnd()); +} +//END KateNavigationConfigTab + +//BEGIN KateEditGeneralConfigTab +KateEditGeneralConfigTab::KateEditGeneralConfigTab(QWidget *parent) + : KateConfigPage(parent) +{ + QVBoxLayout *layout = new QVBoxLayout; + QWidget *newWidget = new QWidget(this); + ui = new Ui::EditConfigWidget(); + ui->setupUi( newWidget ); + + reload(); + + connect(ui->chkStaticWordWrap, SIGNAL(toggled(bool)), this, SLOT(slotChanged())); + connect(ui->chkShowStaticWordWrapMarker, SIGNAL(toggled(bool)), this, SLOT(slotChanged())); + connect(ui->sbWordWrap, SIGNAL(valueChanged(int)), this, SLOT(slotChanged())); + connect(ui->chkSmartCopyCut, SIGNAL(toggled(bool)), this, SLOT(slotChanged())); + + // "What's this?" help is in the ui-file + + layout->addWidget(newWidget); + setLayout(layout); +} + +KateEditGeneralConfigTab::~KateEditGeneralConfigTab() +{ + delete ui; +} + +void KateEditGeneralConfigTab::apply () +{ + // nothing changed, no need to apply stuff + if (!hasChanged()) + return; + m_changed = false; + + KateViewConfig::global()->configStart (); + KateDocumentConfig::global()->configStart (); + + KateDocumentConfig::global()->setWordWrapAt(ui->sbWordWrap->value()); + KateDocumentConfig::global()->setWordWrap(ui->chkStaticWordWrap->isChecked()); + + KateRendererConfig::global()->setWordWrapMarker (ui->chkShowStaticWordWrapMarker->isChecked()); + + KateDocumentConfig::global()->configEnd (); + KateViewConfig::global()->setSmartCopyCut(ui->chkSmartCopyCut->isChecked()); + KateViewConfig::global()->configEnd (); +} + +void KateEditGeneralConfigTab::reload () +{ + ui->chkStaticWordWrap->setChecked(KateDocumentConfig::global()->wordWrap()); + ui->chkShowStaticWordWrapMarker->setChecked( KateRendererConfig::global()->wordWrapMarker() ); + ui->sbWordWrap->setSuffix(ki18ncp("Wrap words at", " character", " characters")); + ui->sbWordWrap->setValue( KateDocumentConfig::global()->wordWrapAt() ); + ui->chkSmartCopyCut->setChecked( KateViewConfig::global()->smartCopyCut() ); +} +//END KateEditGeneralConfigTab + + +//BEGIN KateEditConfigTab +KateEditConfigTab::KateEditConfigTab(QWidget *parent) + : KateConfigPage(parent) + , editConfigTab(new KateEditGeneralConfigTab(this)) + , navigationConfigTab(new KateNavigationConfigTab(this)) + , indentConfigTab(new KateIndentConfigTab(this)) + , completionConfigTab (new KateCompletionConfigTab(this)) + , spellCheckConfigTab(new KateSpellCheckConfigTab(this)) +{ + QVBoxLayout *layout = new QVBoxLayout; + layout->setMargin(0); + KTabWidget *tabWidget = new KTabWidget(this); + + // add all tabs + tabWidget->insertTab(0, editConfigTab, i18n("General")); + tabWidget->insertTab(1, navigationConfigTab, i18n("Text Navigation")); + tabWidget->insertTab(2, indentConfigTab, i18n("Indentation")); + tabWidget->insertTab(3, completionConfigTab, i18n("Auto Completion")); + tabWidget->insertTab(4, spellCheckConfigTab, i18n("Spellcheck")); + + connect(editConfigTab, SIGNAL(changed()), this, SLOT(slotChanged())); + connect(navigationConfigTab, SIGNAL(changed()), this, SLOT(slotChanged())); + connect(indentConfigTab, SIGNAL(changed()), this, SLOT(slotChanged())); + connect(completionConfigTab, SIGNAL(changed()), this, SLOT(slotChanged())); + connect(spellCheckConfigTab, SIGNAL(changed()), this, SLOT(slotChanged())); + + layout->addWidget(tabWidget); + setLayout(layout); +} + +KateEditConfigTab::~KateEditConfigTab() +{ +} + +void KateEditConfigTab::apply () +{ + // try to update the rest of tabs + editConfigTab->apply(); + navigationConfigTab->apply(); + indentConfigTab->apply(); + completionConfigTab->apply(); + spellCheckConfigTab->apply(); +} + +void KateEditConfigTab::reload () +{ + editConfigTab->reload(); + navigationConfigTab->reload(); + indentConfigTab->reload(); + completionConfigTab->reload(); + spellCheckConfigTab->reload(); +} + +void KateEditConfigTab::reset () +{ + editConfigTab->reset(); + navigationConfigTab->reset(); + indentConfigTab->reset(); + completionConfigTab->reset(); + spellCheckConfigTab->reset(); +} + +void KateEditConfigTab::defaults () +{ + editConfigTab->defaults(); + navigationConfigTab->defaults(); + indentConfigTab->defaults(); + completionConfigTab->defaults(); + spellCheckConfigTab->defaults(); +} +//END KateEditConfigTab + +//BEGIN KateViewDefaultsConfig +KateViewDefaultsConfig::KateViewDefaultsConfig(QWidget *parent) + : KateConfigPage(parent) + , textareaUi(new Ui::TextareaAppearanceConfigWidget()) + , bordersUi(new Ui::BordersAppearanceConfigWidget()) +{ + QLayout *layout = new QVBoxLayout( this ); + QTabWidget *tabWidget = new QTabWidget( this ); + layout->addWidget( tabWidget ); + layout->setMargin( 0 ); + + QWidget *textareaTab = new QWidget( tabWidget ); + textareaUi->setupUi( textareaTab ); + tabWidget->addTab( textareaTab, i18n("General") ); + + QWidget *bordersTab = new QWidget( tabWidget ); + bordersUi->setupUi( bordersTab ); + tabWidget->addTab( bordersTab, i18n("Borders") ); + + if (KateDocument::simpleMode ()) + bordersUi->gbSortBookmarks->hide (); + + textareaUi->cmbDynamicWordWrapIndicator->addItem( i18n("Off") ); + textareaUi->cmbDynamicWordWrapIndicator->addItem( i18n("Follow Line Numbers") ); + textareaUi->cmbDynamicWordWrapIndicator->addItem( i18n("Always On") ); + + // hide power user mode if activated anyway + if (!KateGlobal::self()->simpleMode ()) + textareaUi->chkDeveloperMode->hide (); + + // What's This? help is in the ui-file + + reload(); + + // + // after initial reload, connect the stuff for the changed () signal + // + + connect(textareaUi->gbWordWrap, SIGNAL(toggled(bool)), this, SLOT(slotChanged())); + connect(textareaUi->cmbDynamicWordWrapIndicator, SIGNAL(activated(int)), this, SLOT(slotChanged())); + connect(textareaUi->sbDynamicWordWrapDepth, SIGNAL(valueChanged(int)), this, SLOT(slotChanged())); + connect(textareaUi->chkShowTabs, SIGNAL(toggled(bool)), this, SLOT(slotChanged())); + connect(textareaUi->chkShowSpaces, SIGNAL(toggled(bool)), this, SLOT(slotChanged())); + connect(textareaUi->chkShowIndentationLines, SIGNAL(toggled(bool)), this, SLOT(slotChanged())); + connect(textareaUi->chkShowWholeBracketExpression, SIGNAL(toggled(bool)), this, SLOT(slotChanged())); + connect(textareaUi->chkAnimateBracketMatching, SIGNAL(toggled(bool)), this, SLOT(slotChanged())); + connect(textareaUi->chkDeveloperMode, SIGNAL(toggled(bool)), this, SLOT(slotChanged())); + connect(textareaUi->chkFoldFirstLine, SIGNAL(toggled(bool)), this, SLOT(slotChanged())); + + connect(bordersUi->chkIconBorder, SIGNAL(toggled(bool)), this, SLOT(slotChanged())); + connect(bordersUi->chkScrollbarMarks, SIGNAL(toggled(bool)), this, SLOT(slotChanged())); + connect(bordersUi->chkScrollbarMiniMap, SIGNAL(toggled(bool)), this, SLOT(slotChanged())); + connect(bordersUi->chkScrollbarMiniMapAll, SIGNAL(toggled(bool)), this, SLOT(slotChanged())); + bordersUi->chkScrollbarMiniMapAll->hide(); // this is temporary until the feature is done + connect(bordersUi->spBoxMiniMapWidth, SIGNAL(valueChanged(int)), this, SLOT(slotChanged())); + connect(bordersUi->chkLineNumbers, SIGNAL(toggled(bool)), this, SLOT(slotChanged())); + connect(bordersUi->chkShowLineModification, SIGNAL(toggled(bool)), this, SLOT(slotChanged())); + connect(bordersUi->chkShowFoldingMarkers, SIGNAL(toggled(bool)), this, SLOT(slotChanged())); + connect(bordersUi->rbSortBookmarksByPosition, SIGNAL(toggled(bool)), this, SLOT(slotChanged())); + connect(bordersUi->rbSortBookmarksByCreation, SIGNAL(toggled(bool)), this, SLOT(slotChanged())); + connect(bordersUi->cmbShowScrollbars, SIGNAL(activated(int)), this, SLOT(slotChanged())); +} + +KateViewDefaultsConfig::~KateViewDefaultsConfig() +{ + delete bordersUi; + delete textareaUi; +} + +void KateViewDefaultsConfig::apply () +{ + // nothing changed, no need to apply stuff + if (!hasChanged()) + return; + m_changed = false; + + KateViewConfig::global()->configStart (); + KateRendererConfig::global()->configStart (); + + KateViewConfig::global()->setDynWordWrap (textareaUi->gbWordWrap->isChecked()); + KateViewConfig::global()->setDynWordWrapIndicators (textareaUi->cmbDynamicWordWrapIndicator->currentIndex ()); + KateViewConfig::global()->setDynWordWrapAlignIndent(textareaUi->sbDynamicWordWrapDepth->value()); + KateDocumentConfig::global()->setShowTabs (textareaUi->chkShowTabs->isChecked()); + KateDocumentConfig::global()->setShowSpaces (textareaUi->chkShowSpaces->isChecked()); + KateViewConfig::global()->setLineNumbers (bordersUi->chkLineNumbers->isChecked()); + KateViewConfig::global()->setIconBar (bordersUi->chkIconBorder->isChecked()); + KateViewConfig::global()->setScrollBarMarks (bordersUi->chkScrollbarMarks->isChecked()); + KateViewConfig::global()->setScrollBarMiniMap (bordersUi->chkScrollbarMiniMap->isChecked()); + KateViewConfig::global()->setScrollBarMiniMapAll (bordersUi->chkScrollbarMiniMapAll->isChecked()); + KateViewConfig::global()->setScrollBarMiniMapWidth (bordersUi->spBoxMiniMapWidth->value()); + KateViewConfig::global()->setFoldingBar (bordersUi->chkShowFoldingMarkers->isChecked()); + KateViewConfig::global()->setLineModification(bordersUi->chkShowLineModification->isChecked()); + KateViewConfig::global()->setShowScrollbars( bordersUi->cmbShowScrollbars->currentIndex() ); + + KateViewConfig::global()->setBookmarkSort (bordersUi->rbSortBookmarksByPosition->isChecked()?0:1); + KateRendererConfig::global()->setShowIndentationLines(textareaUi->chkShowIndentationLines->isChecked()); + KateRendererConfig::global()->setShowWholeBracketExpression(textareaUi->chkShowWholeBracketExpression->isChecked()); + KateRendererConfig::global()->setAnimateBracketMatching(textareaUi->chkAnimateBracketMatching->isChecked()); + KateViewConfig::global()->setFoldFirstLine(textareaUi->chkFoldFirstLine->isChecked()); + + // warn user that he needs restart the application + if (!textareaUi->chkDeveloperMode->isChecked() != KateDocumentConfig::global()->allowSimpleMode()) + { + // inform... + KMessageBox::information( + this, + i18n("Changing the power user mode affects only newly opened / created documents. In KWrite a restart is recommended."), + i18n("Power user mode changed")); + + KateDocumentConfig::global()->setAllowSimpleMode (!textareaUi->chkDeveloperMode->isChecked()); + } + + KateRendererConfig::global()->configEnd (); + KateViewConfig::global()->configEnd (); +} + +void KateViewDefaultsConfig::reload () +{ + textareaUi->gbWordWrap->setChecked(KateViewConfig::global()->dynWordWrap()); + textareaUi->cmbDynamicWordWrapIndicator->setCurrentIndex( KateViewConfig::global()->dynWordWrapIndicators() ); + textareaUi->sbDynamicWordWrapDepth->setValue(KateViewConfig::global()->dynWordWrapAlignIndent()); + textareaUi->chkShowTabs->setChecked(KateDocumentConfig::global()->showTabs()); + textareaUi->chkShowSpaces->setChecked(KateDocumentConfig::global()->showSpaces()); + bordersUi->chkLineNumbers->setChecked(KateViewConfig::global()->lineNumbers()); + bordersUi->chkIconBorder->setChecked(KateViewConfig::global()->iconBar()); + bordersUi->chkScrollbarMarks->setChecked(KateViewConfig::global()->scrollBarMarks()); + bordersUi->chkScrollbarMiniMap->setChecked(KateViewConfig::global()->scrollBarMiniMap()); + bordersUi->chkScrollbarMiniMapAll->setChecked(KateViewConfig::global()->scrollBarMiniMapAll()); + bordersUi->spBoxMiniMapWidth->setValue(KateViewConfig::global()->scrollBarMiniMapWidth()); + bordersUi->chkShowFoldingMarkers->setChecked(KateViewConfig::global()->foldingBar()); + bordersUi->chkShowLineModification->setChecked(KateViewConfig::global()->lineModification()); + bordersUi->rbSortBookmarksByPosition->setChecked(KateViewConfig::global()->bookmarkSort()==0); + bordersUi->rbSortBookmarksByCreation->setChecked(KateViewConfig::global()->bookmarkSort()==1); + bordersUi->cmbShowScrollbars->setCurrentIndex( KateViewConfig::global()->showScrollbars() ); + textareaUi->chkShowIndentationLines->setChecked(KateRendererConfig::global()->showIndentationLines()); + textareaUi->chkShowWholeBracketExpression->setChecked(KateRendererConfig::global()->showWholeBracketExpression()); + textareaUi->chkAnimateBracketMatching->setChecked(KateRendererConfig::global()->animateBracketMatching()); + textareaUi->chkDeveloperMode->setChecked(!KateDocumentConfig::global()->allowSimpleMode()); + textareaUi->chkFoldFirstLine->setChecked(KateViewConfig::global()->foldFirstLine()); +} + +void KateViewDefaultsConfig::reset () {;} + +void KateViewDefaultsConfig::defaults (){;} +//END KateViewDefaultsConfig + +//BEGIN KateSaveConfigTab +KateSaveConfigTab::KateSaveConfigTab( QWidget *parent ) + : KateConfigPage( parent ) + , modeConfigPage( new ModeConfigPage( this ) ) +{ + // FIXME: Is really needed to move all this code below to another class, + // since it is another tab itself on the config dialog. This means we should + // initialize, add and work with as we do with modeConfigPage (ereslibre) + QVBoxLayout *layout = new QVBoxLayout; + layout->setMargin(0); + KTabWidget *tabWidget = new KTabWidget(this); + + QWidget *tmpWidget = new QWidget(tabWidget); + QVBoxLayout *internalLayout = new QVBoxLayout; + QWidget *newWidget = new QWidget(tabWidget); + ui = new Ui::OpenSaveConfigWidget(); + ui->setupUi( newWidget ); + + QWidget *tmpWidget2 = new QWidget(tabWidget); + QVBoxLayout *internalLayout2 = new QVBoxLayout; + QWidget *newWidget2 = new QWidget(tabWidget); + uiadv = new Ui::OpenSaveConfigAdvWidget(); + uiadv->setupUi( newWidget2 ); + + // What's this help is added in ui/opensaveconfigwidget.ui + reload(); + + // + // after initial reload, connect the stuff for the changed () signal + // + + connect( ui->cmbEncoding, SIGNAL(activated(int)), this, SLOT(slotChanged())); + connect( ui->cmbEncodingFallback, SIGNAL(activated(int)), this, SLOT(slotChanged())); + connect( ui->cmbEOL, SIGNAL(activated(int)), this, SLOT(slotChanged())); + connect( ui->chkDetectEOL, SIGNAL(toggled(bool)), this, SLOT(slotChanged()) ); + connect( ui->chkEnableBOM, SIGNAL(toggled(bool)), this, SLOT(slotChanged()) ); + connect( ui->lineLengthLimit, SIGNAL(valueChanged(int)), this, SLOT(slotChanged())); + connect( ui->cbRemoveTrailingSpaces, SIGNAL(currentIndexChanged(int)), this, SLOT(slotChanged())); + connect( ui->chkNewLineAtEof, SIGNAL(toggled(bool)), this, SLOT(slotChanged())); + connect( uiadv->chkBackupLocalFiles, SIGNAL(toggled(bool)), this, SLOT(slotChanged()) ); + connect( uiadv->chkBackupRemoteFiles, SIGNAL(toggled(bool)), this, SLOT(slotChanged()) ); + connect( uiadv->sbConfigFileSearchDepth, SIGNAL(valueChanged(int)), this, SLOT(slotChanged())); + connect( uiadv->edtBackupPrefix, SIGNAL(textChanged(QString)), this, SLOT(slotChanged()) ); + connect( uiadv->edtBackupSuffix, SIGNAL(textChanged(QString)), this, SLOT(slotChanged()) ); + connect( uiadv->chkNoSync, SIGNAL(toggled(bool)), this, SLOT(slotChanged()) ); + + internalLayout->addWidget(newWidget); + tmpWidget->setLayout(internalLayout); + internalLayout2->addWidget(newWidget2); + tmpWidget2->setLayout(internalLayout2); + + // add all tabs + tabWidget->insertTab(0, tmpWidget, i18n("General")); + tabWidget->insertTab(1, tmpWidget2, i18n("Advanced")); + tabWidget->insertTab(2, modeConfigPage, i18n("Modes && Filetypes")); + + connect(modeConfigPage, SIGNAL(changed()), this, SLOT(slotChanged())); + + layout->addWidget(tabWidget); + setLayout(layout); +} + +KateSaveConfigTab::~KateSaveConfigTab() +{ + delete ui; +} + +void KateSaveConfigTab::apply() +{ + modeConfigPage->apply(); + + // nothing changed, no need to apply stuff + if (!hasChanged()) + return; + m_changed = false; + + KateGlobalConfig::global()->configStart (); + KateDocumentConfig::global()->configStart (); + + if ( uiadv->edtBackupSuffix->text().isEmpty() && uiadv->edtBackupPrefix->text().isEmpty() ) { + KMessageBox::information( + this, + i18n("You did not provide a backup suffix or prefix. Using default suffix: '~'"), + i18n("No Backup Suffix or Prefix") + ); + uiadv->edtBackupSuffix->setText( "~" ); + } + + uint f( 0 ); + if ( uiadv->chkBackupLocalFiles->isChecked() ) + f |= KateDocumentConfig::LocalFiles; + if ( uiadv->chkBackupRemoteFiles->isChecked() ) + f |= KateDocumentConfig::RemoteFiles; + + KateDocumentConfig::global()->setBackupFlags(f); + KateDocumentConfig::global()->setBackupPrefix(uiadv->edtBackupPrefix->text()); + KateDocumentConfig::global()->setBackupSuffix(uiadv->edtBackupSuffix->text()); + + KateDocumentConfig::global()->setSwapFileNoSync(uiadv->chkNoSync->isChecked()); + + KateDocumentConfig::global()->setSearchDirConfigDepth(uiadv->sbConfigFileSearchDepth->value()); + + KateDocumentConfig::global()->setRemoveSpaces(ui->cbRemoveTrailingSpaces->currentIndex()); + + KateDocumentConfig::global()->setNewLineAtEof(ui->chkNewLineAtEof->isChecked()); + + // set both standard and fallback encoding + KateDocumentConfig::global()->setEncoding(KGlobal::charsets()->encodingForName(ui->cmbEncoding->currentText())); + + KateGlobalConfig::global()->setFallbackEncoding(KGlobal::charsets()->encodingForName(ui->cmbEncodingFallback->currentText())); + + KateDocumentConfig::global()->setEol(ui->cmbEOL->currentIndex()); + KateDocumentConfig::global()->setAllowEolDetection(ui->chkDetectEOL->isChecked()); + KateDocumentConfig::global()->setBom(ui->chkEnableBOM->isChecked()); + + KateDocumentConfig::global()->setLineLengthLimit(ui->lineLengthLimit->value()); + + KateDocumentConfig::global()->configEnd (); + KateGlobalConfig::global()->configEnd (); +} + +void KateSaveConfigTab::reload() +{ + modeConfigPage->reload(); + + // encodings + ui->cmbEncoding->clear (); + ui->cmbEncodingFallback->clear (); + QStringList encodings (KGlobal::charsets()->descriptiveEncodingNames()); + for (int i=0; i < encodings.count(); i++) + { + bool found = false; + QTextCodec *codecForEnc = KGlobal::charsets()->codecForName(KGlobal::charsets()->encodingForName(encodings[i]), found); + + if (found) + { + ui->cmbEncoding->addItem (encodings[i]); + ui->cmbEncodingFallback->addItem (encodings[i]); + + if ( codecForEnc->name() == KateDocumentConfig::global()->encoding() ) + { + ui->cmbEncoding->setCurrentIndex(i); + } + + if ( codecForEnc == KateGlobalConfig::global()->fallbackCodec() ) + { + // adjust index for fallback config, has no default! + ui->cmbEncodingFallback->setCurrentIndex(i); + } + } + } + + // eol + ui->cmbEOL->setCurrentIndex(KateDocumentConfig::global()->eol()); + ui->chkDetectEOL->setChecked(KateDocumentConfig::global()->allowEolDetection()); + ui->chkEnableBOM->setChecked(KateDocumentConfig::global()->bom()); + ui->lineLengthLimit->setValue(KateDocumentConfig::global()->lineLengthLimit()); + + ui->cbRemoveTrailingSpaces->setCurrentIndex(KateDocumentConfig::global()->removeSpaces()); + ui->chkNewLineAtEof->setChecked(KateDocumentConfig::global()->newLineAtEof()); + uiadv->sbConfigFileSearchDepth->setValue(KateDocumentConfig::global()->searchDirConfigDepth()); + + // other stuff + uint f ( KateDocumentConfig::global()->backupFlags() ); + uiadv->chkBackupLocalFiles->setChecked( f & KateDocumentConfig::LocalFiles ); + uiadv->chkBackupRemoteFiles->setChecked( f & KateDocumentConfig::RemoteFiles ); + uiadv->edtBackupPrefix->setText( KateDocumentConfig::global()->backupPrefix() ); + uiadv->edtBackupSuffix->setText( KateDocumentConfig::global()->backupSuffix() ); + uiadv->chkNoSync->setChecked( KateDocumentConfig::global()->swapFileNoSync() ); +} + +void KateSaveConfigTab::reset() +{ + modeConfigPage->reset(); +} + +void KateSaveConfigTab::defaults() +{ + modeConfigPage->defaults(); + + ui->cbRemoveTrailingSpaces->setCurrentIndex(0); + + uiadv->chkBackupLocalFiles->setChecked( true ); + uiadv->chkBackupRemoteFiles->setChecked( false ); + uiadv->edtBackupPrefix->setText( "" ); + uiadv->edtBackupSuffix->setText( "~" ); + uiadv->chkNoSync->setChecked( false ); +} + +//END KateSaveConfigTab + +//BEGIN KatePartPluginConfigPage +KatePartPluginConfigPage::KatePartPluginConfigPage (QWidget *parent) + : KateConfigPage (parent, "") +{ + QVBoxLayout *layout = new QVBoxLayout; + layout->setMargin(0); + + plugins.clear(); + + int i = 0; + foreach (const KatePartPluginInfo &info, KatePartPluginManager::self()->pluginList()) + { + KPluginInfo it(info.service()); + it.setPluginEnabled(info.load); + plugins.append(it); + i++; + } + + selector = new KPluginSelector(0); + + connect(selector, SIGNAL(changed(bool)), this, SLOT(slotChanged())); + connect(selector, SIGNAL(configCommitted(QByteArray)), this, SLOT(slotChanged())); + + selector->addPlugins(plugins, KPluginSelector::IgnoreConfigFile, i18n("Editor Plugins"), "Editor"); + + layout->addWidget(selector); + setLayout(layout); +} + +KatePartPluginConfigPage::~KatePartPluginConfigPage () +{ +} + +void KatePartPluginConfigPage::apply () +{ + selector->updatePluginsState(); + + KatePartPluginList &katePluginList = KatePartPluginManager::self()->pluginList(); + for (int i=0; i < plugins.count(); i++) { + if (plugins[i].isPluginEnabled()) { + if (!katePluginList[i].load) { + KatePartPluginManager::self()->loadPlugin(katePluginList[i]); + KatePartPluginManager::self()->enablePlugin(katePluginList[i]); + } + } else { + if (katePluginList[i].load) { + KatePartPluginManager::self()->disablePlugin(katePluginList[i]); + KatePartPluginManager::self()->unloadPlugin(katePluginList[i]); + } + } + } +} + +void KatePartPluginConfigPage::reload () +{ + selector->load(); +} + +void KatePartPluginConfigPage::reset () +{ + selector->load(); +} + +void KatePartPluginConfigPage::defaults () +{ + selector->defaults(); +} +//END KatePartPluginConfigPage + + +//BEGIN KateHlDownloadDialog +KateHlDownloadDialog::KateHlDownloadDialog(QWidget *parent, const char *name, bool modal) + : KDialog( parent ) +{ + setCaption( i18n("Highlight Download") ); + setButtons( User1 | Close ); + setButtonGuiItem( User1, KGuiItem(i18n("&Install")) ); + setDefaultButton( User1 ); + setObjectName( name ); + setModal( modal ); + + KVBox* vbox = new KVBox(this); + setMainWidget(vbox); + vbox->setSpacing(-1); + new QLabel(i18n("Select the syntax highlighting files you want to update:"), vbox); + list = new QTreeWidget(vbox); + list->setColumnCount(4); + list->setHeaderLabels(QStringList() << "" << i18n("Name") << i18n("Installed") << i18n("Latest")); + list->setSelectionMode(QAbstractItemView::MultiSelection); + list->setAllColumnsShowFocus(true); + list->setRootIsDecorated(false); + list->setColumnWidth(0, 22); + + new QLabel(i18n("Note: New versions are selected automatically."), vbox); + setButtonIcon(User1, KIcon("dialog-ok")); + + transferJob = KIO::get( + KUrl(QString(HLDOWNLOADPATH) + + QString("update-") + + KateGlobal::katePartVersion() + + QString(".xml")), KIO::Reload ); + connect(transferJob, SIGNAL(data(KIO::Job*,QByteArray)), + this, SLOT(listDataReceived(KIO::Job*,QByteArray))); +// void data( KIO::Job *, const QByteArray &data); + resize(450, 400); + connect(this,SIGNAL(user1Clicked()),this,SLOT(slotUser1())); +} + +KateHlDownloadDialog::~KateHlDownloadDialog(){} + +/// Split typical version string (\c major.minor.patch) into +/// numeric components, convert 'em to \c unsigned and form a +/// single value that can be compared w/ other versions +/// using relation operators. +/// \note It takes into account only first 3 numbers +unsigned KateHlDownloadDialog::parseVersion(const QString& version_string) +{ + unsigned vn[3] = {0, 0, 0}; + unsigned idx = 0; + foreach (const QString& n, version_string.split(".")) + { + vn[idx++] = n.toUInt(); + if (idx == sizeof(vn)) + break; + } + return KDE_MAKE_VERSION(vn[0], vn[1], vn[2]); +} + +void KateHlDownloadDialog::listDataReceived(KIO::Job *, const QByteArray &data) +{ + if (!transferJob || transferJob->error() != 0) + { + enableButton( User1, false ); + if (data.size()==0) + KMessageBox::error(this,i18n("The list of highlightings could not be found on / retrieved from the server")); + return; + } + + listData+=QString(data); + kDebug(13000)<0) + { + QString installedVersion; + KateHlManager *hlm=KateHlManager::self(); + QDomDocument doc; + doc.setContent(listData); + QDomElement DocElem=doc.documentElement(); + QDomNode n=DocElem.firstChild(); + KateHighlighting *hl = 0; + + if (n.isNull()) kDebug(13000)<<"There is no usable childnode"; + while (!n.isNull()) + { + installedVersion=" --"; + + QDomElement e=n.toElement(); + if (!e.isNull()) + kDebug(13000)<highlights();i++) + { + hl=hlm->getHl(i); + if (hl && hl->name()==Name) + { + installedVersion=" "+hl->version(); + break; + } + else hl = 0; + } + + // autoselect entry if new or updated. + QTreeWidgetItem* entry = new QTreeWidgetItem(list); + entry->setText(0, ""); + entry->setText(1, e.attribute("name")); + entry->setText(2, installedVersion); + entry->setText(3, e.attribute("version")); + entry->setText(4, e.attribute("url")); + + bool is_fresh = false; + if (hl) + { + unsigned prev_version = parseVersion(hl->version()); + unsigned next_version = parseVersion(e.attribute("version")); + is_fresh = prev_version < next_version; + } + else is_fresh = true; + if (is_fresh) + { + entry->treeWidget()->setItemSelected(entry, true); + entry->setIcon(0, SmallIcon(("get-hot-new-stuff"))); + } + } + list->resizeColumnToContents(1); + list->sortItems(1, Qt::AscendingOrder); + } + } +} + +void KateHlDownloadDialog::slotUser1() +{ + QString destdir=KGlobal::dirs()->saveLocation("data","katepart/syntax/"); + foreach (QTreeWidgetItem *it, list->selectedItems()) + { + KUrl src(it->text(4)); + QString filename=src.fileName(KUrl::ObeyTrailingSlash); + QString dest = destdir+filename; + + KIO::NetAccess::download(src,dest, this); + } + + // update Config !! + // this rewrites the cache.... + KateSyntaxDocument doc (KateHlManager::self()->getKConfig(), true); +} +//END KateHlDownloadDialog + +//BEGIN KateGotoBar +KateGotoBar::KateGotoBar(KTextEditor::View *view, QWidget *parent) + : KateViewBarWidget( true, parent ) + , m_view( view ) +{ + Q_ASSERT( m_view != 0 ); // this bar widget is pointless w/o a view + + QHBoxLayout *topLayout = new QHBoxLayout( centralWidget() ); + topLayout->setMargin(0); + gotoRange = new KIntSpinBox(centralWidget()); + + QLabel *label = new QLabel(i18n("&Go to line:"), centralWidget() ); + label->setBuddy(gotoRange); + + QToolButton *btnOK = new QToolButton(centralWidget()); + btnOK->setAutoRaise(true); + btnOK->setIcon(QIcon(SmallIcon("go-jump"))); + btnOK->setText(i18n("Go")); + btnOK->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + connect(btnOK, SIGNAL(clicked()), this, SLOT(gotoLine())); + + topLayout->addWidget(label); + topLayout->addWidget(gotoRange, 1); + topLayout->setStretchFactor( gotoRange, 0 ); + topLayout->addWidget(btnOK); + topLayout->addStretch(); + + setFocusProxy(gotoRange); +} + +void KateGotoBar::updateData() +{ + gotoRange->setMaximum(m_view->document()->lines()); + if (!isVisible()) + { + gotoRange->setValue(m_view->cursorPosition().line() + 1); + gotoRange->adjustSize(); // ### does not respect the range :-( + } + gotoRange->setFocus(Qt::OtherFocusReason); + gotoRange->selectAll(); +} + +void KateGotoBar::keyPressEvent(QKeyEvent* event) +{ + int key = event->key(); + if (key == Qt::Key_Return || key == Qt::Key_Enter) { + gotoLine(); + return; + } + KateViewBarWidget::keyPressEvent(event); +} + +void KateGotoBar::gotoLine() +{ + KateView *kv = qobject_cast(m_view); + if (kv && kv->selection() && !kv->config()->persistentSelection()) { + kv->clearSelection(); + } + + m_view->setCursorPosition( KTextEditor::Cursor(gotoRange->value() - 1, 0) ); + m_view->setFocus(); + emit hideMe(); +} +//END KateGotoBar + +//BEGIN KateDictionaryBar +KateDictionaryBar::KateDictionaryBar(KateView* view, QWidget *parent) + : KateViewBarWidget( true, parent ) + , m_view( view ) +{ + Q_ASSERT(m_view != 0); // this bar widget is pointless w/o a view + + QHBoxLayout *topLayout = new QHBoxLayout(centralWidget()); + topLayout->setMargin(0); + //topLayout->setSpacing(spacingHint()); + m_dictionaryComboBox = new Sonnet::DictionaryComboBox(centralWidget()); + connect(m_dictionaryComboBox, SIGNAL(dictionaryChanged(QString)), + this, SLOT(dictionaryChanged(QString))); + connect(view->doc(), SIGNAL(defaultDictionaryChanged(KateDocument*)), + this, SLOT(updateData())); + QLabel *label = new QLabel(i18n("Dictionary:"), centralWidget()); + label->setBuddy(m_dictionaryComboBox); + + topLayout->addWidget(label); + topLayout->addWidget(m_dictionaryComboBox, 1); + topLayout->setStretchFactor(m_dictionaryComboBox, 0); + topLayout->addStretch(); +} + +KateDictionaryBar::~KateDictionaryBar() +{ +} + +void KateDictionaryBar::updateData() +{ + KateDocument *document = m_view->doc(); + QString dictionary = document->defaultDictionary(); + if(dictionary.isEmpty()) { + dictionary = Sonnet::Speller().defaultLanguage(); + } + m_dictionaryComboBox->setCurrentByDictionary(dictionary); +} + +void KateDictionaryBar::dictionaryChanged(const QString& dictionary) +{ + KTextEditor::Range selection = m_view->selectionRange(); + if(selection.isValid() && !selection.isEmpty()) { + m_view->doc()->setDictionary(dictionary, selection); + } + else { + m_view->doc()->setDefaultDictionary(dictionary); + } +} + +//END KateGotoBar + + +//BEGIN KateModOnHdPrompt +KateModOnHdPrompt::KateModOnHdPrompt( KateDocument *doc, + KTextEditor::ModificationInterface::ModifiedOnDiskReason modtype, + const QString &reason, + QWidget *parent ) + : KDialog( parent ), + m_doc( doc ), + m_modtype ( modtype ), + m_proc( 0 ), + m_diffFile( 0 ) +{ + setButtons( Ok | Apply | Cancel | User1 ); + + QString title, okText, okIcon, okToolTip; + if ( modtype == KTextEditor::ModificationInterface::OnDiskDeleted ) + { + title = i18n("File Was Deleted on Disk"); + okText = i18n("&Save File As..."); + okIcon = "document-save-as"; + okToolTip = i18n("Lets you select a location and save the file again."); + } else { + title = i18n("File Changed on Disk"); + okText = i18n("&Reload File"); + okIcon = "view-refresh"; + okToolTip = i18n("Reload the file from disk. If you have unsaved changes, " + "they will be lost."); + } + + setButtonText( Ok, okText ); + setButtonIcon( Ok, KIcon( okIcon ) ); + setButtonText( Apply, i18n("&Ignore Changes") ); + setButtonIcon( Apply, KIcon( "dialog-warning" ) ); + + setButtonToolTip( Ok, okToolTip ); + setButtonToolTip( Apply, i18n("Ignore the changes. You will not be prompted again.") ); + setButtonToolTip( Cancel, i18n("Do nothing. Next time you focus the file, " + "or try to save it or close it, you will be prompted again.") ); + + setCaption( title ); + + QWidget *w = new QWidget(this); + ui = new Ui::ModOnHdWidget(); + ui->setupUi( w ); + setMainWidget( w ); + + ui->lblIcon->setPixmap( DesktopIcon("dialog-warning" ) ); + ui->lblText->setText( reason + "\n\n" + i18n("What do you want to do?") ); + + // If the file isn't deleted, present a diff button, and a overwrite action. + if ( modtype != KTextEditor::ModificationInterface::OnDiskDeleted ) + { + setButtonGuiItem( User1, KStandardGuiItem::overwrite() ); + setButtonToolTip( User1, i18n("Overwrite the disk file with the editor content.") ); + connect( ui->btnDiff, SIGNAL(clicked()), this, SLOT(slotDiff()) ); + } + else + { + ui->chkIgnoreWhiteSpaces->setVisible( false ); + ui->btnDiff->setVisible( false ); + showButton( User1, false ); + } +} + +KateModOnHdPrompt::~KateModOnHdPrompt() +{ + delete m_proc; + m_proc = 0; + if (m_diffFile) { + m_diffFile->setAutoRemove(true); + delete m_diffFile; + m_diffFile = 0; + } + delete ui; +} + +void KateModOnHdPrompt::slotDiff() +{ + if (m_diffFile) + return; + + m_diffFile = new KTemporaryFile(); + m_diffFile->open(); + + // Start a QProcess that creates a diff + m_proc = new QProcess( this ); + m_proc->setProcessChannelMode( QProcess::MergedChannels ); + connect( m_proc, SIGNAL(readyRead()), this, SLOT(slotDataAvailable()) ); + connect( m_proc, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(slotPDone()) ); + + setCursor( Qt::WaitCursor ); + // disable the button and checkbox, to hinder the user to run it twice. + ui->chkIgnoreWhiteSpaces->setEnabled( false ); + ui->btnDiff->setEnabled( false ); + + QStringList procargs; + procargs << QString(ui->chkIgnoreWhiteSpaces->isChecked() ? "-ub" : "-u") + << "-" << m_doc->url().toLocalFile(); + qDebug() << "diff" << procargs; + m_proc->start("diff", procargs); + + QTextStream ts(m_proc); + int lastln = m_doc->lines() - 1; + for ( int l = 0; l < lastln; ++l ) { + ts << m_doc->line( l ) << '\n'; + } + ts << m_doc->line(lastln); + ts.flush(); + m_proc->closeWriteChannel(); +} + +void KateModOnHdPrompt::slotDataAvailable() +{ + m_diffFile->write(m_proc->readAll()); +} + +void KateModOnHdPrompt::slotPDone() +{ + setCursor( Qt::ArrowCursor ); + ui->chkIgnoreWhiteSpaces->setEnabled( true ); + ui->btnDiff->setEnabled( true ); + + const QProcess::ExitStatus es = m_proc->exitStatus(); + delete m_proc; + m_proc = 0; + + if ( es != QProcess::NormalExit ) + { + KMessageBox::sorry( this, + i18n("The diff command failed. Please make sure that " + "diff(1) is installed and in your PATH."), + i18n("Error Creating Diff") ); + delete m_diffFile; + m_diffFile = 0; + return; + } + + if ( m_diffFile->size() == 0 ) + { + if (ui->chkIgnoreWhiteSpaces->isChecked()) { + KMessageBox::information( this, + i18n("The files are identical."), + i18n("Diff Output") ); + } else { + KMessageBox::information( this, + i18n("Ignoring amount of white space changed, the files are identical."), + i18n("Diff Output") ); + } + delete m_diffFile; + m_diffFile = 0; + return; + } + + m_diffFile->setAutoRemove(false); + KUrl url = KUrl::fromPath(m_diffFile->fileName()); + delete m_diffFile; + m_diffFile = 0; + + // KRun::runUrl should delete the file, once the client exits + KRun::runUrl( url, "text/x-patch", this, true ); +} + +void KateModOnHdPrompt::slotButtonClicked(int button) +{ + switch(button) + { + case Default: + case Ok: + done( (m_modtype == KTextEditor::ModificationInterface::OnDiskDeleted) ? + Save : Reload ); + break; + case Apply: + { + if ( KMessageBox::warningContinueCancel( + this, + i18n("Ignoring means that you will not be warned again (unless " + "the disk file changes once more): if you save the document, you " + "will overwrite the file on disk; if you do not save then the disk " + "file (if present) is what you have."), + i18n("You Are on Your Own"), + KStandardGuiItem::cont(), + KStandardGuiItem::cancel(), + "kate_ignore_modonhd" ) != KMessageBox::Continue ) + return; + done( Ignore ); + break; + } + case User1: + done( Overwrite ); + break; + default: + KDialog::slotButtonClicked(button); + } +} + +//END KateModOnHdPrompt + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/dialogs/katedialogs.h b/kate/part/dialogs/katedialogs.h new file mode 100644 index 00000000..07dc4cf6 --- /dev/null +++ b/kate/part/dialogs/katedialogs.h @@ -0,0 +1,411 @@ +/* This file is part of the KDE libraries + Copyright (C) 2002, 2003 Anders Lund + Copyright (C) 2003 Christoph Cullmann + Copyright (C) 2001 Joseph Wenninger + Copyright (C) 2006 Dominik Haumann + Copyright (C) 2007 Mirko Stocker + Copyright (C) 2009 Michel Ludwig + + Based on work of: + Copyright (C) 1999 Jochen Wilhelmy + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef __KATE_DIALOGS_H__ +#define __KATE_DIALOGS_H__ + +#include "katehighlight.h" +#include "kateviewhelpers.h" + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct syntaxContextData; + +class ModeConfigPage; +class KateDocument; +class KateView; +class KatePartPluginInfo; + +namespace KIO +{ + class Job; + class TransferJob; +} + +class KComboBox; +class KShortcutsEditor; +class KTemporaryFile; +class KIntNumInput; +class KIntSpinBox; +class KPluginSelector; +class KPluginInfo; + +QT_BEGIN_NAMESPACE +namespace Ui +{ + class ModOnHdWidget; + class TextareaAppearanceConfigWidget; + class BordersAppearanceConfigWidget; + class NavigationConfigWidget; + class EditConfigWidget; + class IndentationConfigWidget; + class OpenSaveConfigWidget; + class OpenSaveConfigAdvWidget; + class CompletionConfigTab; + class ViInputModeConfigWidget; + class SpellCheckConfigWidget; +} +QT_END_NAMESPACE + +class KateConfigPage : public KTextEditor::ConfigPage +{ + Q_OBJECT + + public: + explicit KateConfigPage ( QWidget *parent=0, const char *name=0 ); + virtual ~KateConfigPage (); + + public: + bool hasChanged () { return m_changed; } + + protected Q_SLOTS: + void slotChanged(); + + private Q_SLOTS: + void somethingHasChanged (); + + protected: + bool m_changed; +}; + +class KateGotoBar : public KateViewBarWidget +{ + Q_OBJECT + + public: + explicit KateGotoBar(KTextEditor::View *view, QWidget *parent = 0); + + void updateData(); + + protected Q_SLOTS: + void gotoLine(); + + protected: + virtual void keyPressEvent(QKeyEvent* event); + + private: + KTextEditor::View *const m_view; + KIntSpinBox *gotoRange; +}; + +class KateDictionaryBar : public KateViewBarWidget +{ + Q_OBJECT + + public: + explicit KateDictionaryBar(KateView *view, QWidget *parent = NULL); + virtual ~KateDictionaryBar(); + + public Q_SLOTS: + void updateData(); + + protected Q_SLOTS: + void dictionaryChanged(const QString& dictionary); + + private: + KateView* m_view; + Sonnet::DictionaryComboBox *m_dictionaryComboBox; +}; + +class KateIndentConfigTab : public KateConfigPage +{ + Q_OBJECT + + public: + KateIndentConfigTab(QWidget *parent); + ~KateIndentConfigTab(); + + protected: + Ui::IndentationConfigWidget *ui; + + public Q_SLOTS: + void apply (); + void reload (); + void reset () {} + void defaults () {} + + private Q_SLOTS: + void slotChanged (); + void showWhatsThis(const QString& text); +}; + +class KateCompletionConfigTab : public KateConfigPage +{ + Q_OBJECT + + public: + KateCompletionConfigTab(QWidget *parent); + ~KateCompletionConfigTab(); + + protected: + Ui::CompletionConfigTab *ui; + + public Q_SLOTS: + void apply (); + void reload (); + void reset () {} + void defaults () {} + + private Q_SLOTS: + void showWhatsThis(const QString& text); +}; + +class KateEditGeneralConfigTab : public KateConfigPage +{ + Q_OBJECT + + public: + KateEditGeneralConfigTab(QWidget *parent); + ~KateEditGeneralConfigTab(); + + private: + Ui::EditConfigWidget *ui; + + public Q_SLOTS: + void apply (); + void reload (); + void reset () {} + void defaults () {} +}; + +class KateNavigationConfigTab : public KateConfigPage +{ + Q_OBJECT + +public: + KateNavigationConfigTab(QWidget *parent); + ~KateNavigationConfigTab(); + +private: + Ui::NavigationConfigWidget *ui; + +public Q_SLOTS: + void apply (); + void reload (); + void reset () {} + void defaults () {} +}; + +class KateSpellCheckConfigTab : public KateConfigPage +{ + Q_OBJECT + + public: + KateSpellCheckConfigTab(QWidget *parent); + ~KateSpellCheckConfigTab(); + + protected: + Ui::SpellCheckConfigWidget *ui; + Sonnet::ConfigWidget *m_sonnetConfigWidget; + + public Q_SLOTS: + void apply (); + void reload (); + void reset () {} + void defaults () {} + + private Q_SLOTS: + void showWhatsThis(const QString& text); +}; + +class KateEditConfigTab : public KateConfigPage +{ + Q_OBJECT + +public: + KateEditConfigTab(QWidget *parent); + ~KateEditConfigTab(); + +public Q_SLOTS: + void apply (); + void reload (); + void reset (); + void defaults (); + +private: + KateEditGeneralConfigTab *editConfigTab; + KateNavigationConfigTab *navigationConfigTab; + KateIndentConfigTab *indentConfigTab; + KateCompletionConfigTab *completionConfigTab; + KateSpellCheckConfigTab *spellCheckConfigTab; +}; + +class KateViewDefaultsConfig : public KateConfigPage +{ + Q_OBJECT + +public: + KateViewDefaultsConfig( QWidget *parent ); + ~KateViewDefaultsConfig(); + +public Q_SLOTS: + void apply (); + void reload (); + void reset (); + void defaults (); + +private: + Ui::TextareaAppearanceConfigWidget *const textareaUi; + Ui::BordersAppearanceConfigWidget *const bordersUi; +}; + +class KateSaveConfigTab : public KateConfigPage +{ + Q_OBJECT + + public: + KateSaveConfigTab( QWidget *parent ); + ~KateSaveConfigTab(); + + public Q_SLOTS: + void apply(); + void reload(); + void reset(); + void defaults(); + + protected: + //why? + //KComboBox *m_encoding, *m_encodingDetection, *m_eol; + QCheckBox *cbLocalFiles, *cbRemoteFiles; + QCheckBox *replaceTabs, *removeSpaces, *allowEolDetection; + KIntNumInput *dirSearchDepth; + class KIntSpinBox *blockCount; + class QLabel *blockCountLabel; + + private: + Ui::OpenSaveConfigWidget* ui; + Ui::OpenSaveConfigAdvWidget* uiadv; + ModeConfigPage *modeConfigPage; +}; + +class KatePartPluginConfigPage : public KateConfigPage +{ + Q_OBJECT + + public: + KatePartPluginConfigPage (QWidget *parent); + ~KatePartPluginConfigPage (); + + public Q_SLOTS: + void apply (); + void reload (); + void reset (); + void defaults (); + + private: + KPluginSelector *selector; + QList plugins; +}; + + +class KateHlDownloadDialog: public KDialog +{ + Q_OBJECT + + public: + KateHlDownloadDialog(QWidget *parent, const char *name, bool modal); + ~KateHlDownloadDialog(); + + private: + static unsigned parseVersion(const QString&); + class QTreeWidget *list; + class QString listData; + KIO::TransferJob *transferJob; + + private Q_SLOTS: + void listDataReceived(KIO::Job *, const QByteArray &data); + + public Q_SLOTS: + void slotUser1(); +}; + +/** + * This dialog will prompt the user for what do with a file that is + * modified on disk. + * If the file wasn't deleted, it has a 'diff' button, which will create + * a diff file (uing diff(1)) and launch that using KRun. + */ +class KateModOnHdPrompt : public KDialog +{ + Q_OBJECT + public: + enum Status { + Reload = 1, // 0 is QDialog::Rejected + Save, + Overwrite, + Ignore + }; + KateModOnHdPrompt( KateDocument *doc, + KTextEditor::ModificationInterface::ModifiedOnDiskReason modtype, + const QString &reason, QWidget *parent ); + ~KateModOnHdPrompt(); + + public Q_SLOTS: + /** + * Show a diff between the document text and the disk file. + * This will not close the dialog, since we still need a + * decision from the user. + */ + void slotDiff(); + + protected Q_SLOTS: + virtual void slotButtonClicked(int button); + + private Q_SLOTS: + void slotDataAvailable(); ///< read data from the process + void slotPDone(); ///< Runs the diff file when done + + private: + Ui::ModOnHdWidget* ui; + KateDocument *m_doc; + KTextEditor::ModificationInterface::ModifiedOnDiskReason m_modtype; + QProcess *m_proc; + KTemporaryFile *m_diffFile; +}; + +#endif +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/dialogs/modonhdwidget.ui b/kate/part/dialogs/modonhdwidget.ui new file mode 100644 index 00000000..635730c2 --- /dev/null +++ b/kate/part/dialogs/modonhdwidget.ui @@ -0,0 +1,44 @@ + + + ModOnHdWidget + + + + 0 + + + + + + + + + 7 + 0 + 0 + 0 + + + + Ignore white space changes + + + + + + + Calculates the difference between the editor contents and the disk file using diff(1). + + + &View Difference + + + + + + + + + + + diff --git a/kate/part/dialogs/navigationconfigwidget.ui b/kate/part/dialogs/navigationconfigwidget.ui new file mode 100644 index 00000000..1f9e6ca8 --- /dev/null +++ b/kate/part/dialogs/navigationconfigwidget.ui @@ -0,0 +1,162 @@ + + + NavigationConfigWidget + + + + 0 + + + + + Text Cursor Movement + + + + + + When selected, pressing the home key will cause the cursor to skip whitespace and go to the start of a line's text. The same applies for the end key. + + + Smart ho&me and smart end + + + + + + + Selects whether the PageUp and PageDown keys should alter the vertical position of the cursor relative to the top of the view. + + + &PageUp/PageDown moves cursor + + + + + + + 0 + + + + + &Autocenter cursor: + + + sbAutoCenterCursor + + + + + + + Sets the number of lines to maintain visible above and below the cursor when possible. + + + Disabled + + + lines + + + + + + + Qt::Horizontal + + + + 1 + 0 + + + + + + + + + + + + + Misc + + + + + + + + Text selection mode: + + + cbTextSelectionMode + + + + + + + + Normal + + + + + Persistent + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Allow scrolling past the end of the document + + + + + + + + + + Qt::Vertical + + + + 0 + 1 + + + + + + + + + KIntSpinBox + QSpinBox +
knuminput.h
+
+
+ + +
diff --git a/kate/part/dialogs/opensaveconfigadvwidget.ui b/kate/part/dialogs/opensaveconfigadvwidget.ui new file mode 100644 index 00000000..7578fdf7 --- /dev/null +++ b/kate/part/dialogs/opensaveconfigadvwidget.ui @@ -0,0 +1,185 @@ + + + OpenSaveConfigAdvWidget + + + + 0 + + + + + Folder Config File + + + + + + 0 + + + + + Search &depth for config file: + + + sbConfigFileSearchDepth + + + + + + + The editor will search the given number of folder levels upwards for a .kateconfig file and load the settings line from it. + + + Do not use config file + + + -1 + + + 64 + + + + + + + Qt::Horizontal + + + + 1 + 0 + + + + + + + + + + + + + <p>Backing up on save will cause Kate to copy the disk file to '&lt;prefix&gt;&lt;filename&gt;&lt;suffix&gt;' before saving changes.<p>The suffix defaults to <strong>~</strong> and prefix is empty by default. + + + Backup on Save + + + + QFormLayout::ExpandingFieldsGrow + + + + + If this option is enabled, backups for local files will be created when saving. + + + &Local files + + + + + + + If this option is enabled, backups for remote files will be created when saving. + + + &Remote files + + + + + + + &Prefix: + + + edtBackupPrefix + + + + + + + Enter the prefix to prepend to the backup file names. + + + + + + + &Suffix: + + + edtBackupSuffix + + + + + + + Enter the suffix to append to the backup file names. + + + + + + + Qt::Vertical + + + + 0 + 1 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Disable swap files syncing + + + If checked, the swap file is not forcibly written to disk each 15 seconds. Be aware, that disabling the swap file synchronization may lead to data loss in case of a system crash. + + + + + + + + + + + KIntSpinBox + QSpinBox +
knuminput.h
+
+ + KLineEdit + QLineEdit +
klineedit.h
+
+
+ + +
diff --git a/kate/part/dialogs/opensaveconfigwidget.ui b/kate/part/dialogs/opensaveconfigwidget.ui new file mode 100644 index 00000000..bfbeca9a --- /dev/null +++ b/kate/part/dialogs/opensaveconfigwidget.ui @@ -0,0 +1,234 @@ + + + OpenSaveConfigWidget + + + + 0 + 0 + 345 + 285 + + + + + 0 + + + + + File Format + + + + + + QFormLayout::ExpandingFieldsGrow + + + + + &Encoding: + + + cmbEncoding + + + + + + + This defines the standard encoding to use to open/save files, if not changed in the open/save dialog or by using a command line option. + + + + + + + &Fallback Encoding: + + + cmbEncodingFallback + + + + + + + This defines the fallback encoding to try for opening files if neither the encoding chosen as standard above, nor the encoding specified in the open/save dialog, nor the encoding specified on command line match the content of the file. Before this is used, an attempt will be made to determine the encoding to use by looking for a byte order marker at start of file: if one is found, the right unicode encoding will be chosen; otherwise encoding detection will run, if both fail fallback encoding will be tried. + + + + + + + E&nd of line: + + + cmbEOL + + + + + + + + UNIX + + + + + DOS/Windows + + + + + Macintosh + + + + + + + + + + If this option is enabled the editor will autodetect the end of line type. The first found end of line type will be used for the whole file. + + + A&utomatic end of line detection + + + + + + + The byte order mark is a special sequence at the beginning of unicode encoded documents. It helps editors to open text documents with the correct unicode encoding. The byte order mark is not visible in the displayed document. + + + Enable byte order marker + + + + + + + Line Length Limit: + + + lineLengthLimit + + + + + + + Unlimited + + + -1 + + + 1000000 + + + + + + + + + + Automatic Cleanups on Save + + + + + + + + Depending on the choice, trailing spaces are removed when saving a document, either in the entire document or only of modified lines. + + + Re&move trailing spaces: + + + cbRemoveTrailingSpaces + + + + + + + Depending on the choice, trailing spaces are removed when saving a document, either in the entire document or only of modified lines. + + + + Never + + + + + On Modified Lines + + + + + In Entire Document + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + On save, a line break is appended to the document if not already present. The line break is visible after reloading the file. + + + Append newline at end of file on save + + + + + + + + + + Qt::Vertical + + + + 0 + 1 + + + + + + + + + KComboBox + QComboBox +
kcombobox.h
+
+
+ + +
diff --git a/kate/part/dialogs/schemaconfigcolortab.ui b/kate/part/dialogs/schemaconfigcolortab.ui new file mode 100644 index 00000000..5186396e --- /dev/null +++ b/kate/part/dialogs/schemaconfigcolortab.ui @@ -0,0 +1,195 @@ + + + SchemaConfigColorTab + + + + + + Text Area Background + + + true + + + + + + Normal text: + + + + + + + <p>Sets the background color of the editing area.</p> + + + + + + + Selected text: + + + + + + + <p>Sets the background color of the selection.</p><p>To set the text color for selected text, use the "<b>Configure Highlighting</b>" dialog.</p> + + + + + + + Current line: + + + + + + + <p>Sets the background color of the currently active line, which means the line where your cursor is positioned.</p> + + + + + + + <p>Select the marker type you want to change.</p> + + + + + + + <p>Sets the background color of the selected marker type.</p><p><b>Note</b>: The marker color is displayed lightly because of transparency.</p> + + + + + + + + + + Additional Elements + + + true + + + + + + Left border background: + + + + + + + + + + Line numbers: + + + + + + + <p>This color will be used to draw the line numbers (if enabled) and the lines in the code-folding pane.</p> + + + + + + + Bracket highlight: + + + + + + + <p>Sets the bracket matching color. This means, if you place the cursor e.g. at a <b>(</b>, the matching <b>)</b> will be highlighted with this color.</p> + + + + + + + Word wrap markers: + + + + + + + <p>Sets the color of Word Wrap-related markers:</p><dl><dt>Static Word Wrap</dt><dd>A vertical line which shows the column where text is going to be wrapped</dd><dt>Dynamic Word Wrap</dt><dd>An arrow shown to the left of visually-wrapped lines</dd></dl> + + + + + + + Tab and space markers: + + + + + + + <p>Sets the color of the tabulator marks.</p> + + + + + + + Spelling mistake line: + + + + + + + <p>Sets the color of the line that is used to indicate spelling mistakes.</p> + + + + + + + + + + Qt::Vertical + + + + 540 + 1 + + + + + + + + + KColorButton + QPushButton +
kcolorbutton.h
+
+ + KComboBox + QComboBox +
kcombobox.h
+
+
+ + +
diff --git a/kate/part/dialogs/spellcheckconfigwidget.ui b/kate/part/dialogs/spellcheckconfigwidget.ui new file mode 100644 index 00000000..023e2017 --- /dev/null +++ b/kate/part/dialogs/spellcheckconfigwidget.ui @@ -0,0 +1,29 @@ + + + SpellCheckConfigWidget + + + + 0 + + + + + Qt::Vertical + + + QSizePolicy::Minimum + + + + 0 + 1 + + + + + + + + + diff --git a/kate/part/dialogs/textareaappearanceconfigwidget.ui b/kate/part/dialogs/textareaappearanceconfigwidget.ui new file mode 100644 index 00000000..78638dc1 --- /dev/null +++ b/kate/part/dialogs/textareaappearanceconfigwidget.ui @@ -0,0 +1,234 @@ + + + TextareaAppearanceConfigWidget + + + + 0 + 0 + 528 + 380 + + + + + + + If this option is checked, the text lines will be wrapped at the view border on the screen. + + + &Dynamic Word Wrap + + + true + + + + + + Dynamic &word wrap indicators (if applicable): + + + cmbDynamicWordWrapIndicator + + + + + + + Choose when the Dynamic Word Wrap Indicators should be displayed. + + + + + + + Align dynamically wrapped lines to indentation depth: + + + sbDynamicWordWrapDepth + + + + + + + + 0 + 0 + + + + <p>Enables the start of dynamically wrapped lines to be aligned vertically to the indentation level of the first line. This can help to make code and markup more readable.</p><p>Additionally, this allows you to set a maximum width of the screen, as a percentage, after which dynamically wrapped lines will no longer be vertically aligned. For example, at 50%, lines whose indentation levels are deeper than 50% of the width of the screen will not have vertical alignment applied to subsequent wrapped lines.</p> + + + Disabled + + + % of View Width + + + 80 + + + 10 + + + + + + + + + + Whitespace Highlighting + + + + + + The editor will display a symbol to indicate the presence of a tab in the text. + + + &Highlight tabulators + + + + + + + Highlight trailing &spaces + + + + + + + + + + Advanced + + + + + + Changing this mode affects only newly opened / created documents. In KWrite a restart is recommended. + + + Enable power user mode (KDE 3 mode) + + + + + + + If this is enabled, the editor will display vertical lines to help identify indent lines. + + + Show i&ndentation lines + + + + + + + If this is enabled, the range between the selected matching brackets will be highlighted. + + + Highlight range between selected brackets + + + + + + + Flash matching brackets + + + If this is enabled, matching brackets are animated for better visibility. + + + Animate bracket matching + + + + + + + When this setting is enabled, the editor view automatically folds +comment blocks that start on the first line of the document. This is +helpful to hide license headers which are commonly placed at the +beginning of a file. + + + Fold First Line + + + + + + + + + + Qt::Vertical + + + + 0 + 1 + + + + + + + + + KIntSpinBox + QSpinBox +
knuminput.h
+
+ + KComboBox + QComboBox +
kcombobox.h
+
+
+ + + + gbWordWrap + toggled(bool) + cmbDynamicWordWrapIndicator + setEnabled(bool) + + + 115 + 7 + + + 340 + 44 + + + + + gbWordWrap + toggled(bool) + sbDynamicWordWrapDepth + setEnabled(bool) + + + 59 + 6 + + + 385 + 72 + + + + +
diff --git a/kate/part/document/katebuffer.cpp b/kate/part/document/katebuffer.cpp new file mode 100644 index 00000000..d03b52b6 --- /dev/null +++ b/kate/part/document/katebuffer.cpp @@ -0,0 +1,731 @@ +/* This file is part of the KDE libraries + Copyright (c) 2000 Waldo Bastian + Copyright (C) 2002-2004 Christoph Cullmann + Copyright (C) 2007 Mirko Stocker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "katebuffer.h" +#include "moc_katebuffer.cpp" + +#include +#include +#include +#include + +#include "katedocument.h" +#include "katehighlight.h" +#include "kateconfig.h" +#include "kateglobal.h" +#include "kateautoindent.h" + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +/** + * Initial value for m_maxDynamicContexts + */ +static const int KATE_MAX_DYNAMIC_CONTEXTS = 512; + +/** + * Create an empty buffer. (with one block with one empty line) + */ +KateBuffer::KateBuffer(KateDocument *doc) + : Kate::TextBuffer (doc), + m_doc (doc), + m_brokenEncoding (false), + m_tooLongLinesWrapped (false), + m_highlight (0), + m_tabWidth (8), + m_lineHighlighted (0), + m_maxDynamicContexts (KATE_MAX_DYNAMIC_CONTEXTS) +{ + // we need kate global to stay alive + KateGlobal::incRef (); +} + +/** + * Cleanup on destruction + */ +KateBuffer::~KateBuffer() +{ + // release HL + if (m_highlight) + m_highlight->release(); + + // release kate global + KateGlobal::decRef (); +} + +void KateBuffer::editStart () +{ + if (!startEditing ()) + return; +} + +void KateBuffer::editEnd () +{ + /** + * not finished, do nothing + */ + if (!finishEditing()) + return; + + /** + * nothing change, OK + */ + if (!editingChangedBuffer ()) + return; + + /** + * if we arrive here, line changed should be OK + */ + Q_ASSERT (editingMinimalLineChanged () != -1); + Q_ASSERT (editingMaximalLineChanged () != -1); + Q_ASSERT (editingMinimalLineChanged () <= editingMaximalLineChanged ()); + + /** + * no highlighting, nothing to do + */ + if (!m_highlight) + return; + + /** + * if we don't touch the highlighted area => fine + */ + if (editingMinimalLineChanged() > m_lineHighlighted) + return; + + /** + * look one line too far, needed for linecontinue stuff + */ + int editTagLineEnd = editingMaximalLineChanged () + 1; + int editTagLineStart = editingMinimalLineChanged (); + + /** + * look one line before, needed nearly 100% only for indentation based folding ! + */ + if (editTagLineStart > 0) + --editTagLineStart; + + /** + * really update highlighting + */ + doHighlight ( + editTagLineStart, + editTagLineEnd, + true); +} + +void KateBuffer::clear() +{ + // call original clear function + Kate::TextBuffer::clear (); + + // reset the state + m_brokenEncoding = false; + m_tooLongLinesWrapped = false; + + // back to line 0 with hl + m_lineHighlighted = 0; +} + +bool KateBuffer::openFile (const QString &m_file, bool enforceTextCodec) +{ + // first: setup encoding + setFallbackTextCodec (KateGlobalConfig::global()->fallbackCodec ()); + setTextCodec (m_doc->config()->codec ()); + + // setup eol + setEndOfLineMode ((EndOfLineMode) m_doc->config()->eol()); + + // NOTE: we do not remove trailing spaces on load. This was discussed + // over the years again and again. bugs: 306926, 239077, ... + + // line length limit + setLineLengthLimit (m_doc->config()->lineLengthLimit()); + + // then, try to load the file + m_brokenEncoding = false; + m_tooLongLinesWrapped = false; + + /** + * allow non-existent files without error, if local file! + * will allow to do "kate newfile.txt" without error messages but still fail if e.g. you mistype a url + * and it can't be fetched via fish:// or other strange things in kio happen... + * just clear() + exit with success! + */ + if (m_doc->url().isLocalFile() && !QFile::exists (m_file)) { + clear (); + KTextEditor::Message* message = new KTextEditor::Message(i18nc("short translation, user created new file", "New file"), + KTextEditor::Message::Warning); + message->setPosition(KTextEditor::Message::TopInView); + message->setAutoHide(1000); + m_doc->postMessage(message); + + // remember error + m_doc->setOpeningError(true); + m_doc->setOpeningErrorMessage(i18n ("The file %1 does not exist.", m_doc->url().pathOrUrl())); + return true; + } + + /** + * check if this is a normal file or not, avoids to open char devices or directories! + * else clear buffer and break out with error + */ + KDE_struct_stat sbuf; + if (KDE::stat(m_file, &sbuf) != 0 || !S_ISREG(sbuf.st_mode)) { + clear (); + return false; + } + + /** + * try to load + */ + if (!load (m_file, m_brokenEncoding, m_tooLongLinesWrapped, enforceTextCodec)) + return false; + + // save back encoding + m_doc->config()->setEncoding (textCodec()->name()); + + // set eol mode, if a eol char was found + if (m_doc->config()->allowEolDetection()) + m_doc->config()->setEol (endOfLineMode ()); + + // generate a bom? + if (generateByteOrderMark()) + m_doc->config()->setBom (true); + + // okay, loading did work + return true; +} + +bool KateBuffer::canEncode () +{ + QTextCodec *codec = m_doc->config()->codec(); + + kDebug(13020) << "ENC NAME: " << codec->name(); + + // hardcode some unicode encodings which can encode all chars + if ((QString(codec->name()) == "UTF-8") || (QString(codec->name()) == "ISO-10646-UCS-2")) + return true; + + for (int i=0; i < lines(); i++) + { + if (!codec->canEncode (line(i)->string())) + { + kDebug(13020) << "STRING LINE: " << line(i)->string(); + kDebug(13020) << "ENC WORKING: FALSE"; + + return false; + } + } + + return true; +} + +bool KateBuffer::saveFile (const QString &m_file) +{ + // first: setup encoding + setFallbackTextCodec (KateGlobalConfig::global()->fallbackCodec ()); + setTextCodec (m_doc->config()->codec ()); + + // setup eol + setEndOfLineMode ((EndOfLineMode) m_doc->config()->eol()); + + // generate bom? + setGenerateByteOrderMark (m_doc->config()->bom()); + + // append a newline character at the end of the file (eof) ? + setNewLineAtEof (m_doc->config()->newLineAtEof()); + + // try to save + if (!save (m_file)) + return false; + + // no longer broken encoding, or we don't care + m_brokenEncoding = false; + m_tooLongLinesWrapped = false; + + // okay + return true; +} + +void KateBuffer::ensureHighlighted (int line, int lookAhead) +{ + // valid line at all? + if (line < 0 || line >= lines ()) + return; + + // already hl up-to-date for this line? + if (line < m_lineHighlighted) + return; + + // update hl until this line + max lookAhead + int end = qMin(line + lookAhead, lines ()-1); + + // ensure we have enough highlighted + doHighlight ( m_lineHighlighted, end, false ); +} + +void KateBuffer::wrapLine (const KTextEditor::Cursor &position) +{ + // call original + Kate::TextBuffer::wrapLine (position); + + if (m_lineHighlighted > position.line()+1) + m_lineHighlighted++; +} + +void KateBuffer::unwrapLines (int from, int to) +{ + // catch out of range access, should never happen + Q_ASSERT(from >= 0); + Q_ASSERT(to + 1 <= lines()); + + for (int line = to; line >= from; --line) { + if (line + 1 < lines()) { + Kate::TextBuffer::unwrapLine (line + 1); + + if (m_lineHighlighted > (line + 1)) + --m_lineHighlighted; + } + + // Line "0" can't be unwraped + // This call is used to unwrap the last line (if last line != 0) + // This call was used in the previous version too and it looks like the last + // line can't be unwraped without it + else if (line) { + Kate::TextBuffer::unwrapLine (line); + + if (m_lineHighlighted > line) + --m_lineHighlighted; + } + } +} + +void KateBuffer::unwrapLine (int line) +{ + // reimplemented, so first call original + Kate::TextBuffer::unwrapLine (line); + + if (m_lineHighlighted > line) + --m_lineHighlighted; +} + +void KateBuffer::setTabWidth (int w) +{ + if ((m_tabWidth != w) && (m_tabWidth > 0)) + { + m_tabWidth = w; + + if (m_highlight && m_highlight->foldingIndentationSensitive()) + invalidateHighlighting(); + } +} + +void KateBuffer::setHighlight(int hlMode) +{ + KateHighlighting *h = KateHlManager::self()->getHl(hlMode); + + // aha, hl will change + if (h != m_highlight) + { + bool invalidate = !h->noHighlighting(); + + if (m_highlight) + { + m_highlight->release(); + invalidate = true; + } + + h->use(); + + m_highlight = h; + + if (invalidate) + invalidateHighlighting(); + + // inform the document that the hl was really changed + // needed to update attributes and more ;) + m_doc->bufferHlChanged (); + + // try to set indentation + if (!h->indentation().isEmpty()) + m_doc->config()->setIndentationMode (h->indentation()); + } +} + +void KateBuffer::invalidateHighlighting() +{ + m_lineHighlighted = 0; +} + +void KateBuffer::doHighlight (int startLine, int endLine, bool invalidate) +{ + // no hl around, no stuff to do + if (!m_highlight || m_highlight->noHighlighting()) + return; + +#ifdef BUFFER_DEBUGGING + QTime t; + t.start(); + kDebug (13020) << "HIGHLIGHTED START --- NEED HL, LINESTART: " << startLine << " LINEEND: " << endLine; + kDebug (13020) << "HL UNTIL LINE: " << m_lineHighlighted; + kDebug (13020) << "HL DYN COUNT: " << KateHlManager::self()->countDynamicCtxs() << " MAX: " << m_maxDynamicContexts; +#endif + + // see if there are too many dynamic contexts; if yes, invalidate HL of all documents + if (KateHlManager::self()->countDynamicCtxs() >= m_maxDynamicContexts) + { + { + if (KateHlManager::self()->resetDynamicCtxs()) + { +#ifdef BUFFER_DEBUGGING + kDebug (13020) << "HL invalidated - too many dynamic contexts ( >= " << m_maxDynamicContexts << ")"; +#endif + + // avoid recursive invalidation + KateHlManager::self()->setForceNoDCReset(true); + + foreach(KateDocument* doc, KateGlobal::self()->kateDocuments()) + doc->makeAttribs(); + + // doHighlight *shall* do his work. After invalidation, some highlight has + // been recalculated, but *maybe not* until endLine ! So we shall force it manually... + doHighlight ( m_lineHighlighted, endLine, false ); + m_lineHighlighted = endLine; + + KateHlManager::self()->setForceNoDCReset(false); + return; + } + else + { + m_maxDynamicContexts *= 2; + +#ifdef BUFFER_DEBUGGING + kDebug (13020) << "New dynamic contexts limit: " << m_maxDynamicContexts; +#endif + } + } + } + + // if possible get previous line, otherwise create 0 line. + Kate::TextLine prevLine = (startLine >= 1) ? plainLine (startLine - 1) : Kate::TextLine (); + + // here we are atm, start at start line in the block + int current_line = startLine; + int start_spellchecking = -1; + int last_line_spellchecking = -1; + bool ctxChanged = false; + Kate::TextLine textLine = plainLine (current_line); + Kate::TextLine nextLine; + // loop over the lines of the block, from startline to endline or end of block + // if stillcontinue forces us to do so + for (; current_line < qMin (endLine+1, lines()); ++current_line) + { + // get next line, if any + if ((current_line + 1) < lines()) + nextLine = plainLine (current_line+1); + else + nextLine = Kate::TextLine (new Kate::TextLineData ()); + + ctxChanged = false; + m_highlight->doHighlight (prevLine.data(), textLine.data(), nextLine.data(), ctxChanged, tabWidth()); + +#ifdef BUFFER_DEBUGGING + // debug stuff + kDebug( 13020 ) << "current line to hl: " << current_line; + kDebug( 13020 ) << "text length: " << textLine->length() << " attribute list size: " << textLine->attributesList().size(); + + const QVector &ml (textLine->attributesList()); + for (int i=2; i < ml.size(); i+=3) + { + kDebug( 13020 ) << "start: " << ml.at(i-2) << " len: " << ml.at(i-1) << " at: " << ml.at(i) << " "; + } + kDebug( 13020 ); +#endif + + // need we to continue ? + bool stillcontinue = ctxChanged; + if (stillcontinue && start_spellchecking < 0) { + start_spellchecking=current_line; + } + else if (!stillcontinue && start_spellchecking >= 0) { + last_line_spellchecking=current_line; + } + + // move around the lines + prevLine = textLine; + textLine = nextLine; + } + + /** + * perhaps we need to adjust the maximal highlighed line + */ + int oldHighlighted = m_lineHighlighted; + if (ctxChanged || current_line > m_lineHighlighted) + m_lineHighlighted = current_line; + + // tag the changed lines ! + if (invalidate) { +#ifdef BUFFER_DEBUGGING + kDebug (13020) << "HIGHLIGHTED TAG LINES: " << startLine << current_line; +#endif + + emit tagLines (startLine, qMax (current_line, oldHighlighted)); + + if(start_spellchecking >= 0 && lines() > 0) { + emit respellCheckBlock(start_spellchecking, + qMin(lines()-1, (last_line_spellchecking==-1)?qMax (current_line, oldHighlighted):last_line_spellchecking)); + } + } + +#ifdef BUFFER_DEBUGGING + kDebug (13020) << "HIGHLIGHTED END --- NEED HL, LINESTART: " << startLine << " LINEEND: " << endLine; + kDebug (13020) << "HL UNTIL LINE: " << m_lineHighlighted; + kDebug (13020) << "HL DYN COUNT: " << KateHlManager::self()->countDynamicCtxs() << " MAX: " << m_maxDynamicContexts; + kDebug (13020) << "TIME TAKEN: " << t.elapsed(); +#endif +} + +KTextEditor::Range KateBuffer::computeFoldingRangeForStartLine (int startLine) +{ + /** + * ensure valid input + */ + Q_ASSERT (startLine >= 0); + Q_ASSERT (startLine < lines()); + + /** + * no highlighting, no folding, ATM + */ + if (!m_highlight || m_highlight->noHighlighting()) + return KTextEditor::Range::invalid(); + + /** + * first: get the wanted start line highlighted + */ + ensureHighlighted (startLine); + Kate::TextLine startTextLine = plainLine (startLine); + + /** + * return if no folding start! + */ + if (!startTextLine->markedAsFoldingStart ()) + return KTextEditor::Range::invalid(); + + /** + * now: decided if indentation based folding or not! + */ + if (startTextLine->markedAsFoldingStartIndentation ()) { + /** + * get our start indentation level + */ + const int startIndentation = startTextLine->indentDepth (tabWidth()); + + /** + * search next line with indentation level <= our one + */ + int lastLine = startLine + 1; + for (; lastLine < lines(); ++lastLine) { + /** + * get line + */ + Kate::TextLine textLine = plainLine (lastLine); + + /** + * indentation higher than our start line? continue + */ + if (startIndentation < textLine->indentDepth (tabWidth())) + continue; + + /** + * empty line? continue + */ + if (m_highlight->isEmptyLine (textLine.data())) + continue; + + /** + * else, break + */ + break; + } + + /** + * lastLine is always one too much + */ + --lastLine; + + /** + * backtrack all empty lines, we don't want to add them to the fold! + */ + while (lastLine > startLine) { + if (m_highlight->isEmptyLine (plainLine (lastLine).data())) + --lastLine; + else + break; + } + + /** + * we shall not fold one-liners + */ + if (lastLine == startLine) + return KTextEditor::Range::invalid(); + + /** + * be done now + */ + return KTextEditor::Range (KTextEditor::Cursor (startLine, 0), KTextEditor::Cursor (lastLine, plainLine (lastLine)->length())); + } + + /** + * 'normal' attribute based folding, aka token based like '{' BLUB '}' + */ + + /** + * first step: search the first region type, that stays open for the start line + */ + short openedRegionType = 0; + int openedRegionOffset = -1; + { + /** + * mapping of type to "first" offset of it and current number of not matched openings + */ + QHash > foldingStartToOffsetAndCount; + + /** + * walk over all attributes of the line and compute the matchings + */ + const QVector &startLineAttributes = startTextLine->attributesList(); + for ( int i = 0; i < startLineAttributes.size(); ++i ) { + /** + * folding close? + */ + if (startLineAttributes[i].foldingValue < 0) { + /** + * search for this type, try to decrement counter, perhaps erase element! + */ + QHash >::iterator end = foldingStartToOffsetAndCount.find (-startLineAttributes[i].foldingValue); + if (end != foldingStartToOffsetAndCount.end()) { + if (end.value().second > 1) + --(end.value().second); + else + foldingStartToOffsetAndCount.erase (end); + } + } + + /** + * folding open? + */ + if (startLineAttributes[i].foldingValue > 0) { + /** + * search for this type, either insert it, with current offset or increment counter! + */ + QHash >::iterator start = foldingStartToOffsetAndCount.find (startLineAttributes[i].foldingValue); + if (start != foldingStartToOffsetAndCount.end()) + ++(start.value().second); + else + foldingStartToOffsetAndCount.insert (startLineAttributes[i].foldingValue, qMakePair (startLineAttributes[i].offset, 1)); + } + } + + /** + * compute first type with offset + */ + QHashIterator > hashIt (foldingStartToOffsetAndCount); + while (hashIt.hasNext()) { + hashIt.next(); + if (openedRegionOffset == -1 || hashIt.value().first < openedRegionOffset) { + openedRegionType = hashIt.key(); + openedRegionOffset = hashIt.value().first; + } + } + } + + /** + * no opening region found, bad, nothing to do + */ + if (openedRegionType == 0) + return KTextEditor::Range::invalid(); + + /** + * second step: search for matching end region marker! + */ + int countOfOpenRegions = 1; + for (int line = startLine + 1; line < lines(); ++line) { + /** + * ensure line is highlighted + */ + ensureHighlighted (line); + Kate::TextLine textLine = plainLine (line); + + /** + * search for matching end marker + */ + const QVector &lineAttributes = textLine->attributesList(); + for (int i = 0; i < lineAttributes.size(); ++i) { + /** + * matching folding close? + */ + if (lineAttributes[i].foldingValue == -openedRegionType) { + --countOfOpenRegions; + + /** + * end reached? + * compute resulting range! + */ + if (countOfOpenRegions == 0) { + /** + * special handling of end: if end is at column 0 of a line, move it to end of previous line! + * fixes folding for stuff like + * #pragma mark END_OLD_AND_START_NEW_REGION + */ + KTextEditor::Cursor endCursor (line, lineAttributes[i].offset); + if (endCursor.column() == 0 && endCursor.line() > 0) + endCursor = KTextEditor::Cursor (endCursor.line()-1, plainLine (lines()-1)->length()); + + /** + * return computed range + */ + return KTextEditor::Range (KTextEditor::Cursor (startLine, openedRegionOffset), endCursor); + } + } + + /** + * matching folding open? + */ + if (lineAttributes[i].foldingValue == openedRegionType) + ++countOfOpenRegions; + } + } + + /** + * if we arrive here, the opened range spans to the end of the document! + */ + return KTextEditor::Range (KTextEditor::Cursor (startLine, openedRegionOffset), KTextEditor::Cursor (lines()-1, plainLine (lines()-1)->length())); +} + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/document/katebuffer.h b/kate/part/document/katebuffer.h new file mode 100644 index 00000000..5c996c3a --- /dev/null +++ b/kate/part/document/katebuffer.h @@ -0,0 +1,264 @@ +/* This file is part of the KDE libraries + Copyright (c) 2000 Waldo Bastian + Copyright (C) 2002-2004 Christoph Cullmann + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef __KATE_BUFFER_H__ +#define __KATE_BUFFER_H__ + +#include "katetextbuffer.h" + +#include "katepartinterfaces_export.h" + +#include + +class KateLineInfo; +class KateDocument; +class KateHighlighting; + +/** + * The KateBuffer class maintains a collections of lines. + * + * @author Waldo Bastian + * @author Christoph Cullmann + */ +class KATEPARTINTERFACES_EXPORT KateBuffer : public Kate::TextBuffer +{ + Q_OBJECT + + public: + /** + * Create an empty buffer. + * @param doc parent document + */ + explicit KateBuffer (KateDocument *doc); + + /** + * Goodbye buffer + */ + ~KateBuffer (); + + public: + /** + * start some editing action + */ + void editStart (); + + /** + * finish some editing action + */ + void editEnd (); + + /** + * were there changes in the current running + * editing session? + * @return changes done? + */ + inline bool editChanged () const { return editingChangedBuffer (); } + + /** + * dirty lines start + * @return start line + */ + inline int editTagStart () const { return editingMinimalLineChanged (); } + + /** + * dirty lines end + * @return end line + */ + inline int editTagEnd () const { return editingMaximalLineChanged (); } + + /** + * line inserted/removed? + * @return line inserted/removed? + */ + inline bool editTagFrom () const { return editingChangedNumberOfLines() != 0; } + + public: + /** + * Clear the buffer. + */ + void clear(); + + /** + * Open a file, use the given filename + * @param m_file filename to open + * @param enforceTextCodec enforce to use only the set text codec + * @return success + */ + bool openFile (const QString &m_file, bool enforceTextCodec); + + /** + * Did encoding errors occur on load? + * @return encoding errors occurred on load? + */ + bool brokenEncoding () const { return m_brokenEncoding; } + + /** + * Too long lines wrapped on load? + * @return too long lines wrapped on load? + */ + bool tooLongLinesWrapped () const { return m_tooLongLinesWrapped; } + + /** + * Can the current codec handle all chars + * @return chars can be encoded + */ + bool canEncode (); + + /** + * Save the buffer to a file, use the given filename + codec + end of line chars (internal use of qtextstream) + * @param m_file filename to save to + * @return success + */ + bool saveFile (const QString &m_file); + + public: + /** + * Return line @p lineno. + * Highlighting of returned line might be out-dated, which may be sufficient + * for pure text manipulation functions, like search/replace. + * If you require highlighting to be up to date, call @ref ensureHighlighted + * prior to this method. + */ + inline Kate::TextLine plainLine (int lineno) + { + if (lineno < 0 || lineno >= lines()) + return Kate::TextLine (); + + return line (lineno); + } + + /** + * Update highlighting of given line @p line, if needed. + * If @p line is already highlighted, this function does nothing. + * If @p line is not highlighted, all lines up to line + lookAhead + * are highlighted. + * @param lookAhead also highlight these following lines + */ + void ensureHighlighted(int line, int lookAhead = 64); + + /** + * Return the total number of lines in the buffer. + */ + inline int count() const { return lines(); } + + /** + * Unwrap given lines. + * @param from - first line of the block + * @param to - last line of the block + * @param lastLine - last line of the document + */ + void unwrapLines (int from, int to); + + /** + * Unwrap given line. + * @param line line to unwrap + */ + void unwrapLine (int line); + + /** + * Wrap line at given cursor position. + * @param position line/column as cursor where to wrap + */ + void wrapLine (const KTextEditor::Cursor &position); + + public: + inline int tabWidth () const { return m_tabWidth; } + + public: + void setTabWidth (int w); + + /** + * Use @p highlight for highlighting + * + * @p highlight may be 0 in which case highlighting + * will be disabled. + */ + void setHighlight (int hlMode); + + KateHighlighting *highlight () { return m_highlight; } + + /** + * Invalidate highlighting of whole buffer. + */ + void invalidateHighlighting(); + + /** + * For a given line, compute the folding range that starts there + * to be used to fold e.g. from the icon border + * @param startLine start line + * @return folding range starting at the given line or invalid range + */ + KTextEditor::Range computeFoldingRangeForStartLine (int startLine); + + private: + /** + * Highlight information needs to be updated. + * + * @param from first line in range + * @param to last line in range + * @param invalidat should the rehighlighted lines be tagged ? + */ + void doHighlight (int from, int to, bool invalidate); + + Q_SIGNALS: + /** + * Emitted when the highlighting of a certain range has + * changed. + */ + void tagLines(int start, int end); + void respellCheckBlock(int start, int end); + + private: + /** + * document we belong to + */ + KateDocument *const m_doc; + + /** + * file loaded with encoding problems? + */ + bool m_brokenEncoding; + + /** + * too long lines wrapped on load? + */ + bool m_tooLongLinesWrapped; + + /** + * current highlighting mode or 0 + */ + KateHighlighting *m_highlight; + + // for the scrapty indent sensitive langs + int m_tabWidth; + + /** + * last line with valid highlighting + */ + int m_lineHighlighted; + + /** + * number of dynamic contexts causing a full invalidation + */ + int m_maxDynamicContexts; +}; + +#endif + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/document/katedocument.cpp b/kate/part/document/katedocument.cpp new file mode 100644 index 00000000..a4eb5275 --- /dev/null +++ b/kate/part/document/katedocument.cpp @@ -0,0 +1,5565 @@ +/* This file is part of the KDE libraries + Copyright (C) 2001-2004 Christoph Cullmann + Copyright (C) 2001 Joseph Wenninger + Copyright (C) 1999 Jochen Wilhelmy + Copyright (C) 2006 Hamish Rodda + Copyright (C) 2007 Mirko Stocker + Copyright (C) 2009-2010 Michel Ludwig + Copyright (C) 2013 Gerald Senarclens de Grancy + Copyright (C) 2013 Andrey Matveyakin + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02111-13020, USA. +*/ + +//BEGIN includes +#include "katedocument.h" +#include "moc_katedocument.cpp" +#include "kateglobal.h" +#include "katedialogs.h" +#include "katehighlight.h" +#include "kateview.h" +#include "kateautoindent.h" +#include "katetextline.h" +#include "katedocumenthelpers.h" +#include "katehighlighthelpers.h" +#include "kateprinter.h" +#include "katerenderer.h" +#include "kateregexp.h" +#include "kateplaintextsearch.h" +#include "kateregexpsearch.h" +#include "kateconfig.h" +#include "katemodemanager.h" +#include "kateschema.h" +#include "katebuffer.h" +#include "kateundomanager.h" +#include "katepartpluginmanager.h" +#include "spellcheck/prefixstore.h" +#include "spellcheck/ontheflycheck.h" +#include "spellcheck/spellcheck.h" +#include "kateswapfile.h" + +#include "documentcursor.h" + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +//END includes + +#if 0 +#define EDIT_DEBUG kDebug() +#else +#define EDIT_DEBUG if (0) kDebug() +#endif + +static int dummy = 0; + +inline bool isStartBracket( const QChar& c ) { return c == '{' || c == '[' || c == '('; } +inline bool isEndBracket ( const QChar& c ) { return c == '}' || c == ']' || c == ')'; } +inline bool isBracket ( const QChar& c ) { return isStartBracket( c ) || isEndBracket( c ); } + +//BEGIN d'tor, c'tor +// +// KateDocument Constructor +// +KateDocument::KateDocument ( bool bSingleViewMode, bool bBrowserView, + bool bReadOnly, QWidget *parentWidget, + QObject *parent) +: KTextEditor::Document (parent), + m_bSingleViewMode(bSingleViewMode), + m_bBrowserView(bBrowserView), + m_bReadOnly(bReadOnly), + m_activeView(0), + editSessionNumber(0), + editIsRunning(false), + m_undoMergeAllEdits(false), + m_undoManager(new KateUndoManager(this)), + m_editableMarks(markType01), + m_annotationModel(0), + m_isasking(0), + m_buffer(new KateBuffer(this)), + m_indenter(new KateAutoIndent(this)), + m_hlSetByUser(false), + m_bomSetByUser(false), + m_indenterSetByUser(false), + m_userSetEncodingForNextReload(false), + m_modOnHd(false), + m_modOnHdReason(OnDiskUnmodified), + m_docName("need init"), + m_docNameNumber(0), + m_fileTypeSetByUser(false), + m_reloading(false), + m_config(new KateDocumentConfig(this)), + m_fileChangedDialogsActivated(false), + m_onTheFlyChecker(0), + m_documentState (DocumentIdle), + m_readWriteStateBeforeLoading (false), + m_isUntitled(true) +{ + setComponentData ( KateGlobal::self()->componentData () ); + + /** + * avoid spamming plasma and other window managers with progress dialogs + * we show such stuff inline in the views! + */ + setProgressInfoEnabled (false); + + QString pathName ("/Kate/Document/%1"); + pathName = pathName.arg (++dummy); + + // my dbus object + QDBusConnection::sessionBus().registerObject (pathName, this, QDBusConnection::ExportAdaptors | QDBusConnection::ExportScriptableSlots); + + // register doc at factory + KateGlobal::self()->registerDocument(this); + + // normal hl + m_buffer->setHighlight (0); + + // swap file + m_swapfile = new Kate::SwapFile(this); + + new KateBrowserExtension( this ); // deleted by QObject memory management + + // important, fill in the config into the indenter we use... + m_indenter->updateConfig (); + + // some nice signals from the buffer + connect(m_buffer, SIGNAL(tagLines(int,int)), this, SLOT(tagLines(int,int))); + + // if the user changes the highlight with the dialog, notify the doc + connect(KateHlManager::self(),SIGNAL(changed()),SLOT(internalHlChanged())); + + // signals for mod on hd + connect( KateGlobal::self()->dirWatch(), SIGNAL(dirty(QString)), + this, SLOT(slotModOnHdDirty(QString)) ); + + connect( KateGlobal::self()->dirWatch(), SIGNAL(created(QString)), + this, SLOT(slotModOnHdCreated(QString)) ); + + connect( KateGlobal::self()->dirWatch(), SIGNAL(deleted(QString)), + this, SLOT(slotModOnHdDeleted(QString)) ); + + /** + * load handling + * this is needed to ensure we signal the user if a file ist still loading + * and to disallow him to edit in that time + */ + connect (this, SIGNAL(started(KIO::Job*)), this, SLOT(slotStarted(KIO::Job*))); + connect (this, SIGNAL(completed()), this, SLOT(slotCompleted())); + connect (this, SIGNAL(canceled(QString)), this, SLOT(slotCanceled())); + + // update doc name + updateDocName (); + + // if single view mode, like in the konqui embedding, create a default view ;) + // be lazy, only create it now, if any parentWidget is given, otherwise widget() + // will create it on demand... + if ( m_bSingleViewMode && parentWidget ) + { + KTextEditor::View *view = (KTextEditor::View*)createView( parentWidget ); + insertChildClient( view ); + view->show(); + setWidget( view ); + } + + connect(m_undoManager, SIGNAL(undoChanged()), this, SIGNAL(undoChanged())); + connect(m_undoManager, SIGNAL(undoStart(KTextEditor::Document*)), this, SIGNAL(exclusiveEditStart(KTextEditor::Document*))); + connect(m_undoManager, SIGNAL(undoEnd(KTextEditor::Document*)), this, SIGNAL(exclusiveEditEnd(KTextEditor::Document*))); + connect(m_undoManager, SIGNAL(redoStart(KTextEditor::Document*)), this, SIGNAL(exclusiveEditStart(KTextEditor::Document*))); + connect(m_undoManager, SIGNAL(redoEnd(KTextEditor::Document*)), this, SIGNAL(exclusiveEditEnd(KTextEditor::Document*))); + + connect(this,SIGNAL(sigQueryClose(bool*,bool*)),this,SLOT(slotQueryClose_save(bool*,bool*))); + + onTheFlySpellCheckingEnabled(config()->onTheFlySpellCheck()); + + // register document in plugins + KatePartPluginManager::self()->addDocument(this); +} + +// +// KateDocument Destructor +// +KateDocument::~KateDocument() +{ + /** + * we are about to delete cursors/ranges/... + */ + emit aboutToDeleteMovingInterfaceContent (this); + + // kill it early, it has ranges! + delete m_onTheFlyChecker; + m_onTheFlyChecker = NULL; + + clearDictionaryRanges(); + + // Tell the world that we're about to close (== destruct) + // Apps must receive this in a direct signal-slot connection, and prevent + // any further use of interfaces once they return. + emit aboutToClose(this); + + // remove file from dirwatch + deactivateDirWatch (); + + // thanks for offering, KPart, but we're already self-destructing + setAutoDeleteWidget(false); + setAutoDeletePart(false); + + // clean up remaining views + while (!m_views.isEmpty()) { + delete m_views.takeFirst(); + } + + // de-register from plugin + KatePartPluginManager::self()->removeDocument(this); + + // cu marks + for (QHash::const_iterator i = m_marks.constBegin(); i != m_marks.constEnd(); ++i) + delete i.value(); + m_marks.clear(); + + delete m_config; + KateGlobal::self()->deregisterDocument (this); +} +//END + +// on-demand view creation +QWidget *KateDocument::widget() +{ + // no singleViewMode -> no widget()... + if (!singleViewMode()) + return 0; + + // does a widget exist already? use it! + if (KTextEditor::Document::widget()) + return KTextEditor::Document::widget(); + + // create and return one... + KTextEditor::View *view = (KTextEditor::View*)createView(0); + insertChildClient( view ); + setWidget( view ); + return view; +} + +//BEGIN KTextEditor::Document stuff + +KTextEditor::View *KateDocument::createView( QWidget *parent ) +{ + KateView* newView = new KateView( this, parent); + if ( m_fileChangedDialogsActivated ) + connect( newView, SIGNAL(focusIn(KTextEditor::View*)), this, SLOT(slotModifiedOnDisk()) ); + + emit viewCreated (this, newView); + + // post existing messages to the new view, if no specific view is given + foreach (KTextEditor::Message *message, m_messageHash.keys()) { + if (!message->view()) + newView->postMessage(message, m_messageHash[message]); + } + + return newView; +} + +const QList &KateDocument::views () const +{ + return m_textEditViews; +} + +KTextEditor::Editor *KateDocument::editor () +{ + return KateGlobal::self(); +} + +KTextEditor::Range KateDocument::rangeOnLine(KTextEditor::Range range, int line) const +{ + int col1 = toVirtualColumn(range.start()); + int col2 = toVirtualColumn(range.end()); + + col1 = fromVirtualColumn(line, col1); + col2 = fromVirtualColumn(line, col2); + + return KTextEditor::Range(line, col1, line, col2); +} + +//BEGIN KTextEditor::EditInterface stuff + +QString KateDocument::text() const +{ + return m_buffer->text (); +} + +QString KateDocument::text( const KTextEditor::Range& range, bool blockwise ) const +{ + if (!range.isValid()) { + kWarning() << "Text requested for invalid range" << range; + return QString(); + } + + QString s; + + if (range.start().line() == range.end().line()) + { + if (range.start().column() > range.end().column()) + return QString (); + + Kate::TextLine textLine = m_buffer->plainLine(range.start().line()); + + if ( !textLine ) + return QString (); + + return textLine->string(range.start().column(), range.end().column()-range.start().column()); + } + else + { + + for (int i = range.start().line(); (i <= range.end().line()) && (i < m_buffer->count()); ++i) + { + Kate::TextLine textLine = m_buffer->plainLine(i); + + if ( !blockwise ) + { + if (i == range.start().line()) + s.append (textLine->string(range.start().column(), textLine->length()-range.start().column())); + else if (i == range.end().line()) + s.append (textLine->string(0, range.end().column())); + else + s.append (textLine->string()); + } + else + { + KTextEditor::Range subRange = rangeOnLine(range, i); + s.append(textLine->string(subRange.start().column(), subRange.columnWidth())); + } + + if ( i < range.end().line() ) + s.append(QChar::fromAscii('\n')); + } + } + + return s; +} + +QChar KateDocument::character( const KTextEditor::Cursor & position ) const +{ + Kate::TextLine textLine = m_buffer->plainLine(position.line()); + + if ( !textLine ) + return QChar(); + + return textLine->at(position.column()); +} + +QStringList KateDocument::textLines( const KTextEditor::Range & range, bool blockwise ) const +{ + QStringList ret; + + if (!range.isValid()) { + kWarning() << "Text requested for invalid range" << range; + return ret; + } + + if ( blockwise && (range.start().column() > range.end().column()) ) + return ret; + + if (range.start().line() == range.end().line()) + { + Q_ASSERT(range.start() <= range.end()); + + Kate::TextLine textLine = m_buffer->plainLine(range.start().line()); + + if ( !textLine ) + return ret; + + ret << textLine->string(range.start().column(), range.end().column() - range.start().column()); + } + else + { + for (int i = range.start().line(); (i <= range.end().line()) && (i < m_buffer->count()); ++i) + { + Kate::TextLine textLine = m_buffer->plainLine(i); + + if ( !blockwise ) + { + if (i == range.start().line()) + ret << textLine->string(range.start().column(), textLine->length() - range.start().column()); + else if (i == range.end().line()) + ret << textLine->string(0, range.end().column()); + else + ret << textLine->string(); + } + else + { + KTextEditor::Range subRange = rangeOnLine(range, i); + ret << textLine->string(subRange.start().column(), subRange.columnWidth()); + } + } + } + + return ret; +} + +QString KateDocument::line( int line ) const +{ + Kate::TextLine l = m_buffer->plainLine(line); + + if (!l) + return QString(); + + return l->string(); +} + +bool KateDocument::setText(const QString &s) +{ + if (!isReadWrite()) + return false; + + QList msave; + + foreach (KTextEditor::Mark* mark, m_marks) + msave.append(*mark); + + editStart (); + + // delete the text + clear(); + + // insert the new text + insertText (KTextEditor::Cursor(), s); + + editEnd (); + + foreach (const KTextEditor::Mark& mark, msave) + setMark (mark.line, mark.type); + + return true; +} + +bool KateDocument::setText( const QStringList & text ) +{ + if (!isReadWrite()) + return false; + + QList msave; + + foreach (KTextEditor::Mark* mark, m_marks) + msave.append(*mark); + + editStart (); + + // delete the text + clear(); + + // insert the new text + insertText (KTextEditor::Cursor::start(), text); + + editEnd (); + + foreach (const KTextEditor::Mark& mark, msave) + setMark (mark.line, mark.type); + + return true; +} + +bool KateDocument::clear() +{ + if (!isReadWrite()) + return false; + + foreach (KateView *view, m_views) { + view->clear(); + view->tagAll(); + view->update(); + } + + clearMarks (); + + emit aboutToInvalidateMovingInterfaceContent(this); + m_buffer->invalidateRanges(); + + emit aboutToRemoveText(documentRange()); + + return editRemoveLines(0, lastLine()); +} + +bool KateDocument::insertText( const KTextEditor::Cursor& position, const QString& text, bool block ) +{ + if (!isReadWrite()) + return false; + + if (text.isEmpty()) + return true; + + editStart(); + + int currentLine = position.line(); + int currentLineStart = 0; + int totalLength = text.length(); + int insertColumn = position.column(); + + if (position.line() > lines()) + { + int line = lines(); + while (line != position.line() + totalLength + 1) + { + editInsertLine(line,QString()); + line++; + } + } + + bool replacetabs = ( config()->replaceTabsDyn() ); + int tabWidth = config()->tabWidth(); + + static const QChar newLineChar('\n'); + static const QChar tabChar('\t'); + static const QChar spaceChar(' '); + + int insertColumnExpanded = insertColumn; + Kate::TextLine l = plainKateTextLine( currentLine ); + if (l) + insertColumnExpanded = l->toVirtualColumn( insertColumn, tabWidth ); + int positionColumnExpanded = insertColumnExpanded; + + int pos = 0; + for (; pos < totalLength; pos++) + { + const QChar& ch = text.at(pos); + + if (ch == newLineChar) + { + // Only perform the text insert if there is text to insert + if (currentLineStart < pos) + editInsertText(currentLine, insertColumn, text.mid(currentLineStart, pos - currentLineStart)); + + if ( !block ) + { + editWrapLine(currentLine, insertColumn + pos - currentLineStart); + insertColumn = 0; + } + + currentLine++; + l = plainKateTextLine( currentLine ); + + if ( block ) + { + if ( currentLine == lastLine() + 1 ) + editInsertLine(currentLine, QString()); + insertColumn = positionColumnExpanded; + if (l) + insertColumn = l->fromVirtualColumn( insertColumn, tabWidth ); + } + + currentLineStart = pos + 1; + if (l) + insertColumnExpanded = l->toVirtualColumn( insertColumn, tabWidth ); + } + else + { + if ( replacetabs && ch == tabChar ) + { + int spacesRequired = tabWidth - ( (insertColumnExpanded + pos - currentLineStart) % tabWidth ); + editInsertText(currentLine, insertColumn, text.mid(currentLineStart, pos - currentLineStart) + QString(spacesRequired, spaceChar)); + + insertColumn += pos - currentLineStart + spacesRequired; + currentLineStart = pos + 1; + if (l) + insertColumnExpanded = l->toVirtualColumn( insertColumn, tabWidth ); + } + } + } + + // Only perform the text insert if there is text to insert + if (currentLineStart < pos) + editInsertText(currentLine, insertColumn, text.mid(currentLineStart, pos - currentLineStart)); + + editEnd(); + return true; +} + +bool KateDocument::insertText( const KTextEditor::Cursor & position, const QStringList & textLines, bool block ) +{ + if (!isReadWrite()) + return false; + + // just reuse normal function + return insertText (position, textLines.join ("\n"), block); +} + +bool KateDocument::removeText ( const KTextEditor::Range &_range, bool block ) +{ + KTextEditor::Range range = _range; + + if (!isReadWrite()) + return false; + + // Should now be impossible to trigger with the new Range class + Q_ASSERT( range.start().line() <= range.end().line() ); + + if ( range.start().line() > lastLine() ) + return false; + + if (!block) + emit aboutToRemoveText(range); + + editStart(); + + if ( !block ) + { + if ( range.end().line() > lastLine() ) + { + range.end().setPosition(lastLine()+1, 0); + } + + if (range.onSingleLine()) + { + editRemoveText(range.start().line(), range.start().column(), range.columnWidth()); + } + else + { + int from = range.start().line(); + int to = range.end().line(); + + // remove last line + if (to <= lastLine()) + editRemoveText(to, 0, range.end().column()); + + // editRemoveLines() will be called on first line (to remove bookmark) + if (range.start().column() == 0 && from > 0) + --from; + + // remove middle lines + editRemoveLines(from+1, to-1); + + // remove first line if not already removed by editRemoveLines() + if (range.start().column() > 0 || range.start().line() == 0) { + editRemoveText(from, range.start().column(), m_buffer->plainLine(from)->length() - range.start().column()); + editUnWrapLine(from); + } + } + } // if ( ! block ) + else + { + int startLine = qMax(0, range.start().line()); + int vc1 = toVirtualColumn(range.start()); + int vc2 = toVirtualColumn(range.end()); + for (int line = qMin(range.end().line(), lastLine()); line >= startLine; --line) { + int col1 = fromVirtualColumn(line, vc1); + int col2 = fromVirtualColumn(line, vc2); + editRemoveText(line, qMin(col1, col2), qAbs(col2 - col1)); + } + } + + editEnd (); + return true; +} + +bool KateDocument::insertLine( int l, const QString &str ) +{ + if (!isReadWrite()) + return false; + + if (l < 0 || l > lines()) + return false; + + return editInsertLine (l, str); +} + +bool KateDocument::insertLines( int line, const QStringList & text ) +{ + if (!isReadWrite()) + return false; + + if (line < 0 || line > lines()) + return false; + + bool success = true; + foreach (const QString &string, text) + success &= editInsertLine (line++, string); + + return success; +} + +bool KateDocument::removeLine( int line ) +{ + if (!isReadWrite()) + return false; + + if (line < 0 || line > lastLine()) + return false; + + return editRemoveLine (line); +} + +int KateDocument::totalCharacters() const +{ + int l = 0; + + for (int i = 0; i < m_buffer->count(); ++i) + { + Kate::TextLine line = m_buffer->plainLine(i); + + if (line) + l += line->length(); + } + + return l; +} + +int KateDocument::lines() const +{ + return m_buffer->count(); +} + +int KateDocument::lineLength ( int line ) const +{ + if (line < 0 || line > lastLine()) + return -1; + + Kate::TextLine l = m_buffer->plainLine(line); + + if (!l) + return -1; + + return l->length(); +} +//END + +//BEGIN KTextEditor::EditInterface internal stuff +// +// Starts an edit session with (or without) undo, update of view disabled during session +// +void KateDocument::editStart () +{ + editSessionNumber++; + + if (editSessionNumber > 1) + return; + + editIsRunning = true; + + m_undoManager->editStart(); + + foreach(KateView *view,m_views) + view->editStart (); + + m_buffer->editStart (); +} + +// +// End edit session and update Views +// +void KateDocument::editEnd () +{ + if (editSessionNumber == 0) { + Q_ASSERT(0); + return; + } + + // wrap the new/changed text, if something really changed! + if (m_buffer->editChanged() && (editSessionNumber == 1)) + if (m_undoManager->isActive() && config()->wordWrap()) + wrapText (m_buffer->editTagStart(), m_buffer->editTagEnd()); + + editSessionNumber--; + + if (editSessionNumber > 0) + return; + + // end buffer edit, will trigger hl update + // this will cause some possible adjustment of tagline start/end + m_buffer->editEnd (); + + m_undoManager->editEnd(); + + // edit end for all views !!!!!!!!! + foreach(KateView *view, m_views) + view->editEnd (m_buffer->editTagStart(), m_buffer->editTagEnd(), m_buffer->editTagFrom()); + + if (m_buffer->editChanged()) { + setModified(true); + emit textChanged (this); + } + + editIsRunning = false; +} + +void KateDocument::pushEditState () +{ + editStateStack.push(editSessionNumber); +} + +void KateDocument::popEditState () +{ + if (editStateStack.isEmpty()) return; + + int count = editStateStack.pop() - editSessionNumber; + while (count < 0) { ++count; editEnd(); } + while (count > 0) { --count; editStart(); } +} + +void KateDocument::inputMethodStart() +{ + m_undoManager->inputMethodStart(); +} + +void KateDocument::inputMethodEnd() +{ + m_undoManager->inputMethodEnd(); +} + +bool KateDocument::wrapText(int startLine, int endLine) +{ + if (startLine < 0 || endLine < 0) + return false; + + if (!isReadWrite()) + return false; + + int col = config()->wordWrapAt(); + + if (col == 0) + return false; + + editStart (); + + for (int line = startLine; (line <= endLine) && (line < lines()); line++) + { + Kate::TextLine l = kateTextLine(line); + + if (!l) + break; + + //kDebug (13020) << "try wrap line: " << line; + + if (l->virtualLength(m_buffer->tabWidth()) > col) + { + Kate::TextLine nextl = kateTextLine(line+1); + + //kDebug (13020) << "do wrap line: " << line; + + int eolPosition = l->length()-1; + + // take tabs into account here, too + int x = 0; + const QString & t = l->string(); + int z2 = 0; + for ( ; z2 < l->length(); z2++) + { + static const QChar tabChar('\t'); + if (t.at(z2) == tabChar) + x += m_buffer->tabWidth() - (x % m_buffer->tabWidth()); + else + x++; + + if (x > col) + break; + } + + const int colInChars = qMin (z2, l->length()-1); + int searchStart = colInChars; + + // If where we are wrapping is an end of line and is a space we don't + // want to wrap there + if (searchStart == eolPosition && t.at(searchStart).isSpace()) + searchStart--; + + // Scan backwards looking for a place to break the line + // We are not interested in breaking at the first char + // of the line (if it is a space), but we are at the second + // anders: if we can't find a space, try breaking on a word + // boundary, using KateHighlight::canBreakAt(). + // This could be a priority (setting) in the hl/filetype/document + int z = -1; + int nw = -1; // alternative position, a non word character + for (z=searchStart; z >= 0; z--) + { + if (t.at(z).isSpace()) break; + if ( (nw < 0) && highlight()->canBreakAt( t.at(z) , l->attribute(z) ) ) + nw = z; + } + + if (z >= 0) + { + // So why don't we just remove the trailing space right away? + // Well, the (view's) cursor may be directly in front of that space + // (user typing text before the last word on the line), and if that + // happens, the cursor would be moved to the next line, which is not + // what we want (bug #106261) + z++; + } + else + { + // There was no space to break at so break at a nonword character if + // found, or at the wrapcolumn ( that needs be configurable ) + // Don't try and add any white space for the break + if ( (nw >= 0) && nw < colInChars ) nw++; // break on the right side of the character + z = (nw >= 0) ? nw : colInChars; + } + + if (nextl && !nextl->isAutoWrapped()) + { + editWrapLine (line, z, true); + editMarkLineAutoWrapped (line+1, true); + + endLine++; + } + else + { + if (nextl && (nextl->length() > 0) && !nextl->at(0).isSpace() && ((l->length() < 1) || !l->at(l->length()-1).isSpace())) + editInsertText (line+1, 0, QString (" ")); + + bool newLineAdded = false; + editWrapLine (line, z, false, &newLineAdded); + + editMarkLineAutoWrapped (line+1, true); + + endLine++; + } + } + } + + editEnd (); + + return true; +} + +bool KateDocument::editInsertText ( int line, int col, const QString &s ) +{ + // verbose debug + EDIT_DEBUG << "editInsertText" << line << col << s; + + if (line < 0 || col < 0) + return false; + + if (!isReadWrite()) + return false; + + Kate::TextLine l = kateTextLine(line); + + if (!l) + return false; + + // nothing to do, do nothing! + if (s.isEmpty()) + return true; + + editStart (); + + QString s2 = s; + int col2 = col; + if (col2 > l->length()) { + s2 = QString(col2 - l->length(), QLatin1Char(' ')) + s; + col2 = l->length(); + } + + m_undoManager->slotTextInserted(line, col2, s2); + + // insert text into line + m_buffer->insertText (KTextEditor::Cursor (line, col2), s2); + + emit KTextEditor::Document::textInserted(this, KTextEditor::Range(line, col2, line, col2 + s2.length())); + + editEnd(); + + return true; +} + +bool KateDocument::editRemoveText ( int line, int col, int len ) +{ + // verbose debug + EDIT_DEBUG << "editRemoveText" << line << col << len; + + if (line < 0 || col < 0 || len < 0) + return false; + + if (!isReadWrite()) + return false; + + Kate::TextLine l = kateTextLine(line); + + if (!l) + return false; + + // nothing to do, do nothing! + if (len == 0) + return true; + + // wrong column + if (col >= l->text().size()) + return false; + + // don't try to remove what's not there + len = qMin(len, l->text().size() - col); + + editStart (); + + QString oldText = l->string().mid(col, len); + + m_undoManager->slotTextRemoved(line, col, oldText); + + // remove text from line + m_buffer->removeText (KTextEditor::Range (KTextEditor::Cursor (line, col), KTextEditor::Cursor (line, col+len))); + + emit KTextEditor::Document::textRemoved(this, KTextEditor::Range(line, col, line, col + len)); + emit KTextEditor::Document::textRemoved(this, KTextEditor::Range(line, col, line, col + len), oldText); + + editEnd (); + + return true; +} + +bool KateDocument::editMarkLineAutoWrapped ( int line, bool autowrapped ) +{ + // verbose debug + EDIT_DEBUG << "editMarkLineAutoWrapped" << line << autowrapped; + + if (line < 0) + return false; + + if (!isReadWrite()) + return false; + + Kate::TextLine l = kateTextLine(line); + + if (!l) + return false; + + editStart (); + + m_undoManager->slotMarkLineAutoWrapped(line, autowrapped); + + l->setAutoWrapped (autowrapped); + + editEnd (); + + return true; +} + +bool KateDocument::editWrapLine ( int line, int col, bool newLine, bool *newLineAdded) +{ + // verbose debug + EDIT_DEBUG << "editWrapLine" << line << col << newLine; + + if (line < 0 || col < 0) + return false; + + if (!isReadWrite()) + return false; + + Kate::TextLine l = kateTextLine(line); + + if (!l) + return false; + + editStart (); + + Kate::TextLine nextLine = kateTextLine(line+1); + + const int length = l->length(); + m_undoManager->slotLineWrapped(line, col, length - col, (!nextLine || newLine)); + + if (!nextLine || newLine) + { + m_buffer->wrapLine (KTextEditor::Cursor (line, col)); + + QList list; + for (QHash::const_iterator i = m_marks.constBegin(); i != m_marks.constEnd(); ++i) + { + if( i.value()->line >= line ) + { + if ((col == 0) || (i.value()->line > line)) + list.append( i.value() ); + } + } + + for( int i=0; i < list.size(); ++i ) + m_marks.take( list.at(i)->line ); + + for( int i=0; i < list.size(); ++i ) + { + list.at(i)->line++; + m_marks.insert( list.at(i)->line, list.at(i) ); + } + + if( !list.isEmpty() ) + emit marksChanged( this ); + + // yes, we added a new line ! + if (newLineAdded) + (*newLineAdded) = true; + } + else + { + m_buffer->wrapLine (KTextEditor::Cursor (line, col)); + m_buffer->unwrapLine (line + 2); + + // no, no new line added ! + if (newLineAdded) + (*newLineAdded) = false; + } + + emit KTextEditor::Document::textInserted(this, KTextEditor::Range(line, col, line+1, 0)); + + editEnd (); + + return true; +} + +bool KateDocument::editUnWrapLine ( int line, bool removeLine, int length ) +{ + // verbose debug + EDIT_DEBUG << "editUnWrapLine" << line << removeLine << length; + + if (line < 0 || length < 0) + return false; + + if (!isReadWrite()) + return false; + + Kate::TextLine l = kateTextLine(line); + Kate::TextLine nextLine = kateTextLine(line+1); + + if (!l || !nextLine) + return false; + + editStart (); + + int col = l->length (); + + m_undoManager->slotLineUnWrapped(line, col, length, removeLine); + + if (removeLine) + { + m_buffer->unwrapLine (line+1); + } + else + { + m_buffer->wrapLine (KTextEditor::Cursor (line + 1, length)); + m_buffer->unwrapLine (line + 1); + } + + QList list; + for (QHash::const_iterator i = m_marks.constBegin(); i != m_marks.constEnd(); ++i) + { + if( i.value()->line >= line+1 ) + list.append( i.value() ); + + if ( i.value()->line == line+1 ) + { + KTextEditor::Mark* mark = m_marks.take( line ); + + if (mark) + { + i.value()->type |= mark->type; + } + } + } + + for( int i=0; i < list.size(); ++i ) + m_marks.take( list.at(i)->line ); + + for( int i=0; i < list.size(); ++i ) + { + list.at(i)->line--; + m_marks.insert( list.at(i)->line, list.at(i) ); + } + + if( !list.isEmpty() ) + emit marksChanged( this ); + + emit KTextEditor::Document::textRemoved(this, KTextEditor::Range(line, col, line+1, 0)); + emit KTextEditor::Document::textRemoved(this, KTextEditor::Range(line, col, line+1, 0), "\n"); + + editEnd (); + + return true; +} + +bool KateDocument::editInsertLine ( int line, const QString &s ) +{ + // verbose debug + EDIT_DEBUG << "editInsertLine" << line << s; + + if (line < 0) + return false; + + if (!isReadWrite()) + return false; + + if ( line > lines() ) + return false; + + editStart (); + + m_undoManager->slotLineInserted(line, s); + + // wrap line + if (line > 0) { + Kate::TextLine previousLine = m_buffer->line (line-1); + m_buffer->wrapLine (KTextEditor::Cursor (line-1, previousLine->text().size())); + } else { + m_buffer->wrapLine (KTextEditor::Cursor (0, 0)); + } + + // insert text + m_buffer->insertText (KTextEditor::Cursor (line, 0), s); + + Kate::TextLine tl = m_buffer->line (line); + + QList list; + for (QHash::const_iterator i = m_marks.constBegin(); i != m_marks.constEnd(); ++i) + { + if( i.value()->line >= line ) + list.append( i.value() ); + } + + for( int i=0; i < list.size(); ++i ) + m_marks.take( list.at(i)->line ); + + for( int i=0; i < list.size(); ++i ) + { + list.at(i)->line++; + m_marks.insert( list.at(i)->line, list.at(i) ); + } + + if( !list.isEmpty() ) + emit marksChanged( this ); + + KTextEditor::Range rangeInserted(line, 0, line, tl->length()); + + if (line) { + Kate::TextLine prevLine = plainKateTextLine(line - 1); + rangeInserted.start().setPosition(line - 1, prevLine->length()); + } else { + rangeInserted.end().setPosition(line + 1, 0); + } + + emit KTextEditor::Document::textInserted(this, rangeInserted); + + editEnd (); + + return true; +} + +bool KateDocument::editRemoveLine ( int line ) +{ + return editRemoveLines(line, line); +} + +bool KateDocument::editRemoveLines ( int from, int to ) +{ + // verbose debug + EDIT_DEBUG << "editRemoveLines" << from << to; + + if (to < from || from < 0 || to > lastLine()) + return false; + + if (!isReadWrite()) + return false; + + if (lines() == 1) + return editRemoveText(0, 0, kateTextLine(0)->length()); + + editStart(); + QStringList oldText; + + for (int line = to; line >= from; line--) { + Kate::TextLine tl = m_buffer->line (line); + oldText.prepend(this->line(line)); + m_undoManager->slotLineRemoved(line, this->line(line)); + + m_buffer->removeText (KTextEditor::Range (KTextEditor::Cursor (line, 0), KTextEditor::Cursor (line, tl->text().size()))); + } + + m_buffer->unwrapLines(from, to); + + QList rmark; + QList list; + + foreach (KTextEditor::Mark* mark, m_marks) { + int line = mark->line; + if (line > to) + list << line; + else if (line >= from) + rmark << line; + } + + foreach (int line, rmark) + delete m_marks.take(line); + + foreach (int line, list) + { + KTextEditor::Mark* mark = m_marks.take(line); + mark->line -= to - from + 1; + m_marks.insert(mark->line, mark); + } + + if (!list.isEmpty()) + emit marksChanged(this); + + KTextEditor::Range rangeRemoved(from, 0, to + 1, 0); + + if (to == lastLine() + to - from + 1) { + rangeRemoved.end().setPosition(to, oldText.last().length()); + if (from > 0) { + Kate::TextLine prevLine = plainKateTextLine(from - 1); + rangeRemoved.start().setPosition(from - 1, prevLine->length()); + } + } + + emit KTextEditor::Document::textRemoved(this, rangeRemoved); + emit KTextEditor::Document::textRemoved(this, rangeRemoved, oldText.join("\n") + '\n'); + + editEnd(); + + return true; +} +//END + +//BEGIN KTextEditor::UndoInterface stuff +uint KateDocument::undoCount () const +{ + return m_undoManager->undoCount (); +} + +uint KateDocument::redoCount () const +{ + return m_undoManager->redoCount (); +} + +void KateDocument::undo() +{ + m_undoManager->undo(); +} + +void KateDocument::redo() +{ + m_undoManager->redo(); +} +//END + +//BEGIN KTextEditor::SearchInterface stuff +QVector KateDocument::searchText( + const KTextEditor::Range & range, + const QString & pattern, + const KTextEditor::Search::SearchOptions options) +{ + // TODO + // * support BlockInputRange + // * support DotMatchesNewline + + const bool escapeSequences = options.testFlag(KTextEditor::Search::EscapeSequences); + const bool regexMode = options.testFlag(KTextEditor::Search::Regex); + const bool backwards = options.testFlag(KTextEditor::Search::Backwards); + const bool wholeWords = options.testFlag(KTextEditor::Search::WholeWords); + const Qt::CaseSensitivity caseSensitivity = options.testFlag(KTextEditor::Search::CaseInsensitive) ? Qt::CaseInsensitive : Qt::CaseSensitive; + + if (regexMode) + { + // regexp search + // escape sequences are supported by definition + KateRegExpSearch searcher(this, caseSensitivity); + return searcher.search(pattern, range, backwards); + } + + if (escapeSequences) + { + // escaped search + KatePlainTextSearch searcher(this, caseSensitivity, wholeWords); + KTextEditor::Range match = searcher.search(KateRegExpSearch::escapePlaintext(pattern), range, backwards); + + QVector result; + result.append(match); + return result; + } + + // plaintext search + KatePlainTextSearch searcher(this, caseSensitivity, wholeWords); + KTextEditor::Range match = searcher.search(pattern, range, backwards); + + QVector result; + result.append(match); + return result; +} + + + +KTextEditor::Search::SearchOptions KateDocument::supportedSearchOptions() const +{ + KTextEditor::Search::SearchOptions supported(KTextEditor::Search::Default); + supported |= KTextEditor::Search::Regex; + supported |= KTextEditor::Search::CaseInsensitive; + supported |= KTextEditor::Search::Backwards; +// supported |= KTextEditor::Search::BlockInputRange; + supported |= KTextEditor::Search::EscapeSequences; + supported |= KTextEditor::Search::WholeWords; +// supported |= KTextEditor::Search::DotMatchesNewline; + return supported; +} +//END + +QWidget * KateDocument::dialogParent() +{ + QWidget *w=widget(); + + if(!w) + { + w=activeView(); + + if(!w) + w=QApplication::activeWindow(); + } + + return w; +} + +//BEGIN KTextEditor::HighlightingInterface stuff +bool KateDocument::setMode (const QString &name) +{ + updateFileType (name); + return true; +} + +QString KateDocument::mode () const +{ + return m_fileType; +} + +QStringList KateDocument::modes () const +{ + QStringList m; + + const QList &modeList = KateGlobal::self()->modeManager()->list(); + foreach(KateFileType* type, modeList) + m << type->name; + + return m; +} + +bool KateDocument::setHighlightingMode (const QString &name) +{ + int mode = KateHlManager::self()->nameFind(name); + if (mode == -1) { + return false; + } + m_buffer->setHighlight(mode); + return true; +} + +QString KateDocument::highlightingMode () const +{ + return highlight()->name (); +} + +QStringList KateDocument::highlightingModes () const +{ + QStringList hls; + + for (int i = 0; i < KateHlManager::self()->highlights(); ++i) + hls << KateHlManager::self()->hlName (i); + + return hls; +} + +QString KateDocument::highlightingModeSection( int index ) const +{ + return KateHlManager::self()->hlSection( index ); +} + +QString KateDocument::modeSection( int index ) const +{ + return KateGlobal::self()->modeManager()->list().at( index )->section; +} + +void KateDocument::bufferHlChanged () +{ + // update all views + makeAttribs(false); + + // deactivate indenter if necessary + m_indenter->checkRequiredStyle(); + + emit highlightingModeChanged(this); +} + + +void KateDocument::setDontChangeHlOnSave() +{ + m_hlSetByUser = true; +} + +void KateDocument::bomSetByUser() +{ + m_bomSetByUser=true; +} +//END + +//BEGIN KTextEditor::SessionConfigInterface and KTextEditor::ParameterizedSessionConfigInterface stuff +void KateDocument::readSessionConfig(const KConfigGroup &kconfig) +{ + readParameterizedSessionConfig(kconfig, SkipNone); +} + +void KateDocument::readParameterizedSessionConfig(const KConfigGroup &kconfig, + unsigned long configParameters) +{ + if(!(configParameters & KTextEditor::ParameterizedSessionConfigInterface::SkipEncoding)) { + // get the encoding + QString tmpenc=kconfig.readEntry("Encoding"); + if (!tmpenc.isEmpty() && (tmpenc != encoding())) + setEncoding(tmpenc); + } + + if(!(configParameters & KTextEditor::ParameterizedSessionConfigInterface::SkipUrl)) { + // restore the url + KUrl url (kconfig.readEntry("URL")); + + // open the file if url valid + if (!url.isEmpty() && url.isValid()) + openUrl (url); + else completed(); //perhaps this should be emitted at the end of this function + } + else { + completed(); //perhaps this should be emitted at the end of this function + } + + if(!(configParameters & KTextEditor::ParameterizedSessionConfigInterface::SkipMode)) { + // restore the filetype + if (kconfig.hasKey("Mode")) { + updateFileType (kconfig.readEntry("Mode", fileType())); + } + } + + if(!(configParameters & KTextEditor::ParameterizedSessionConfigInterface::SkipHighlighting)) { + // restore the hl stuff + if (kconfig.hasKey("Highlighting")) { + int mode = KateHlManager::self()->nameFind(kconfig.readEntry("Highlighting")); + if (mode >= 0) { + m_buffer->setHighlight(mode); + } + } + } + + // indent mode + config()->setIndentationMode( kconfig.readEntry("Indentation Mode", config()->indentationMode() ) ); + + // Restore Bookmarks + const QList marks = kconfig.readEntry("Bookmarks", QList()); + for( int i = 0; i < marks.count(); i++ ) + addMark( marks.at(i), KateDocument::markType01 ); +} + +void KateDocument::writeSessionConfig(KConfigGroup &kconfig) +{ + writeParameterizedSessionConfig(kconfig, SkipNone); +} + +void KateDocument::writeParameterizedSessionConfig(KConfigGroup &kconfig, + unsigned long configParameters) +{ + if ( this->url().isLocalFile() ) { + const QString path = this->url().toLocalFile(); + if ( KGlobal::dirs()->relativeLocation( "tmp", path ) != path ) { + return; // inside tmp resource, do not save + } + } + + if(!(configParameters & KTextEditor::ParameterizedSessionConfigInterface::SkipUrl)) { + // save url + kconfig.writeEntry("URL", this->url().prettyUrl() ); + } + + if(!(configParameters & KTextEditor::ParameterizedSessionConfigInterface::SkipEncoding)) { + // save encoding + kconfig.writeEntry("Encoding",encoding()); + } + + if(!(configParameters & KTextEditor::ParameterizedSessionConfigInterface::SkipMode)) { + // save file type + kconfig.writeEntry("Mode", m_fileType); + } + + if(!(configParameters & KTextEditor::ParameterizedSessionConfigInterface::SkipHighlighting)) { + // save hl + kconfig.writeEntry("Highlighting", highlight()->name()); + } + + // indent mode + kconfig.writeEntry("Indentation Mode", config()->indentationMode() ); + + // Save Bookmarks + QList marks; + for (QHash::const_iterator i = m_marks.constBegin(); i != m_marks.constEnd(); ++i) + if (i.value()->type & KTextEditor::MarkInterface::markType01) + marks << i.value()->line; + + kconfig.writeEntry( "Bookmarks", marks ); +} + +//END KTextEditor::SessionConfigInterface and KTextEditor::ParameterizedSessionConfigInterface stuff + +uint KateDocument::mark( int line ) +{ + KTextEditor::Mark* m = m_marks.value(line); + if( !m ) + return 0; + + return m->type; +} + +void KateDocument::setMark( int line, uint markType ) +{ + clearMark( line ); + addMark( line, markType ); +} + +void KateDocument::clearMark( int line ) +{ + if( line < 0 || line > lastLine() ) + return; + + if( !m_marks.value(line) ) + return; + + KTextEditor::Mark* mark = m_marks.take( line ); + emit markChanged( this, *mark, MarkRemoved ); + emit marksChanged( this ); + delete mark; + tagLines( line, line ); + repaintViews(true); +} + +void KateDocument::addMark( int line, uint markType ) +{ + KTextEditor::Mark *mark; + + if( line < 0 || line > lastLine()) + return; + + if( markType == 0 ) + return; + + if( (mark = m_marks.value(line)) ) { + // Remove bits already set + markType &= ~mark->type; + + if( markType == 0 ) + return; + + // Add bits + mark->type |= markType; + } else { + mark = new KTextEditor::Mark; + mark->line = line; + mark->type = markType; + m_marks.insert( line, mark ); + } + + // Emit with a mark having only the types added. + KTextEditor::Mark temp; + temp.line = line; + temp.type = markType; + emit markChanged( this, temp, MarkAdded ); + + emit marksChanged( this ); + tagLines( line, line ); + repaintViews(true); +} + +void KateDocument::removeMark( int line, uint markType ) +{ + if( line < 0 || line > lastLine() ) + return; + + KTextEditor::Mark* mark = m_marks.value(line); + + if( !mark ) + return; + + // Remove bits not set + markType &= mark->type; + + if( markType == 0 ) + return; + + // Subtract bits + mark->type &= ~markType; + + // Emit with a mark having only the types removed. + KTextEditor::Mark temp; + temp.line = line; + temp.type = markType; + emit markChanged( this, temp, MarkRemoved ); + + if( mark->type == 0 ) + m_marks.remove( line ); + + emit marksChanged( this ); + tagLines( line, line ); + repaintViews(true); +} + +const QHash &KateDocument::marks() +{ + return m_marks; +} + +void KateDocument::requestMarkTooltip( int line, QPoint position ) +{ + KTextEditor::Mark* mark = m_marks.value(line); + if(!mark) + return; + + bool handled = false; + emit markToolTipRequested( this, *mark, position, handled ); +} + +bool KateDocument::handleMarkClick( int line ) +{ + KTextEditor::Mark* mark = m_marks.value(line); + if(!mark) + return false; + + bool handled = false; + emit markClicked( this, *mark, handled ); + + return handled; +} + +bool KateDocument::handleMarkContextMenu( int line, QPoint position ) +{ + KTextEditor::Mark* mark = m_marks.value(line); + if(!mark) + return false; + + bool handled = false; + + emit markContextMenuRequested( this, *mark, position, handled ); + + return handled; +} + +void KateDocument::clearMarks() +{ + while (!m_marks.isEmpty()) + { + QHash::iterator it = m_marks.begin(); + KTextEditor::Mark mark = *it.value(); + delete it.value(); + m_marks.erase (it); + + emit markChanged( this, mark, MarkRemoved ); + tagLines( mark.line, mark.line ); + } + + m_marks.clear(); + + emit marksChanged( this ); + repaintViews(true); +} + +void KateDocument::setMarkPixmap( MarkInterface::MarkTypes type, const QPixmap& pixmap ) +{ + m_markPixmaps.insert( type, pixmap ); +} + +void KateDocument::setMarkDescription( MarkInterface::MarkTypes type, const QString& description ) +{ + m_markDescriptions.insert( type, description ); +} + +QPixmap KateDocument::markPixmap( MarkInterface::MarkTypes type ) const +{ + return m_markPixmaps.value(type, QPixmap()); +} + +QColor KateDocument::markColor( MarkInterface::MarkTypes type ) const +{ + uint reserved = (0x1 << KTextEditor::MarkInterface::reservedMarkersCount()) - 1; + if ((uint)type >= (uint)markType01 && (uint)type <= reserved) { + return KateRendererConfig::global()->lineMarkerColor(type); + } else { + return QColor(); + } +} + +QString KateDocument::markDescription( MarkInterface::MarkTypes type ) const +{ + return m_markDescriptions.value(type, QString()); +} + +void KateDocument::setEditableMarks( uint markMask ) +{ + m_editableMarks = markMask; +} + +uint KateDocument::editableMarks() const +{ + return m_editableMarks; +} +//END + +//BEGIN KTextEditor::PrintInterface stuff +bool KateDocument::printDialog () +{ + return KatePrinter::print (this); +} + +bool KateDocument::print () +{ + return KatePrinter::print (this); +} +//END + +//BEGIN KTextEditor::DocumentInfoInterface (### unfinished) +QString KateDocument::mimeType() +{ + KMimeType::Ptr result = KMimeType::defaultMimeTypePtr(); + + // if the document has a URL, try KMimeType::findByURL + if ( ! this->url().isEmpty() ) + result = KMimeType::findByUrl( this->url() ); + + else if ( this->url().isEmpty() || ! this->url().isLocalFile() ) + result = mimeTypeForContent(); + + return result->name(); +} + +KMimeType::Ptr KateDocument::mimeTypeForContent() +{ + QByteArray buf (1024,'\0'); + uint bufpos = 0; + + for (int i=0; i < lines(); ++i) + { + QString line = this->line( i ); + uint len = line.length() + 1; + + if (bufpos + len > 1024) + len = 1024 - bufpos; + + QString ld (line + QChar::fromAscii('\n')); + buf.replace(bufpos,len,ld.toLatin1()); //memcpy(buf.data() + bufpos, ld.toLatin1().constData(), len); + + bufpos += len; + + if (bufpos >= 1024) + break; + } + buf.resize( bufpos ); + + int accuracy = 0; + KMimeType::Ptr mt = KMimeType::findByContent(buf, &accuracy); + return mt ? mt : KMimeType::defaultMimeTypePtr(); +} +//END KTextEditor::DocumentInfoInterface + +//BEGIN: error +void KateDocument::showAndSetOpeningErrorAccess() { + QPointer message + = new KTextEditor::Message(i18n ("The file %1 could not be loaded, as it was not possible to read from it.
Check if you have read access to this file.", this->url().pathOrUrl()), + KTextEditor::Message::Error); + message->setWordWrap(true); + QAction* tryAgainAction = new QAction(KIcon("view-refresh"), i18nc("translators: you can also translate 'Try Again' with 'Reload'", "Try Again"), 0); + connect(tryAgainAction, SIGNAL(triggered()), SLOT(documentReload()), Qt::QueuedConnection); + + QAction* closeAction = new QAction(KIcon("window-close"), i18n("&Close"), 0); + closeAction->setToolTip(i18n("Close message")); + + // add try again and close actions + message->addAction(tryAgainAction); + message->addAction(closeAction); + + // finally post message + postMessage(message); + + // remember error + setOpeningError(true); + setOpeningErrorMessage(i18n ("The file %1 could not be loaded, as it was not possible to read from it.\n\nCheck if you have read access to this file.",this->url().pathOrUrl())); + +} +//END: error + + +//BEGIN KParts::ReadWrite stuff +bool KateDocument::openFile() +{ + /** + * we are about to invalidate all cursors/ranges/.. => m_buffer->openFile will do so + */ + emit aboutToInvalidateMovingInterfaceContent (this); + + // no open errors until now... + setOpeningError(false); + + // add new m_file to dirwatch + activateDirWatch (); + + // remember current encoding + QString currentEncoding = encoding(); + + // + // mime type magic to get encoding right + // + QString mimeType = arguments().mimeType(); + int pos = mimeType.indexOf(';'); + if (pos != -1 && !(m_reloading && m_userSetEncodingForNextReload)) + setEncoding (mimeType.mid(pos+1)); + + // do we have success ? + emit KTextEditor::Document::textRemoved(this, documentRange()); + emit KTextEditor::Document::textRemoved(this, documentRange(), m_buffer->text()); + + // update file type, we do this here PRE-LOAD, therefore pass file name for reading from + updateFileType (KateGlobal::self()->modeManager()->fileType (this, localFilePath())); + + // read dir config (if possible and wanted) + // do this PRE-LOAD to get encoding info! + readDirConfig (); + + // perhaps we need to re-set again the user encoding + if (m_reloading && m_userSetEncodingForNextReload && (currentEncoding != encoding())) + setEncoding (currentEncoding); + + bool success = m_buffer->openFile (localFilePath(), (m_reloading && m_userSetEncodingForNextReload)); + + // disable view updates + foreach (KateView * view, m_views) + view->setUpdatesEnabled (false); + + // + // yeah, success + // read variables + // + if (success) + readVariables (); + + // + // update views + // + foreach (KateView * view, m_views) + { + // This is needed here because inserting the text moves the view's start position (it is a MovingCursor) + view->setCursorPosition (KTextEditor::Cursor()); + view->setUpdatesEnabled (true); + view->updateView (true); + } + + // emit all signals about new text after view updates + emit KTextEditor::Document::textInserted(this, documentRange()); + + // Inform that the text has changed (required as we're not inside the usual editStart/End stuff) + emit textChanged (this); + + if (!m_reloading) + { + // + // emit the signal we need for example for kate app + // + emit documentUrlChanged (this); + + // + // set doc name, dummy value as arg, don't need it + // + updateDocName (); + } + + // + // to houston, we are not modified + // + if (m_modOnHd) + { + m_modOnHd = false; + m_modOnHdReason = OnDiskUnmodified; + emit modifiedOnDisk (this, m_modOnHd, m_modOnHdReason); + } + + // + // display errors + // + if (!success) { + showAndSetOpeningErrorAccess(); + } + + // warn: broken encoding + if (m_buffer->brokenEncoding()) { + // this file can't be saved again without killing it + setReadWrite( false ); + + QPointer message + = new KTextEditor::Message(i18n ("The file %1 was opened with %2 encoding but contained invalid characters.
" + "It is set to read-only mode, as saving might destroy its content.
" + "Either reopen the file with the correct encoding chosen or enable the read-write mode again in the menu to be able to edit it.", this->url().pathOrUrl(), + QString (m_buffer->textCodec()->name ())), + KTextEditor::Message::Warning); + message->setWordWrap(true); + postMessage(message); + + // remember error + setOpeningError(true); + setOpeningErrorMessage(i18n ("The file %1 was opened with %2 encoding but contained invalid characters." + " It is set to read-only mode, as saving might destroy its content." + " Either reopen the file with the correct encoding chosen or enable the read-write mode again in the menu to be able to edit it.", this->url().pathOrUrl(), QString (m_buffer->textCodec()->name ()))); + } + + // warn: too long lines + if (m_buffer->tooLongLinesWrapped()) { + // this file can't be saved again without modifications + setReadWrite( false ); + + m_readWriteStateBeforeLoading = false; + QPointer message + = new KTextEditor::Message(i18n ("The file %1 was opened and contained lines longer than the configured Line Length Limit (%2 characters).
" + "Those lines were wrapped and the document is set to read-only mode, as saving will modify its content.", + this->url().pathOrUrl(),config()->lineLengthLimit()), + KTextEditor::Message::Warning); + message->setWordWrap(true); + postMessage(message); + + // remember error + setOpeningError(true); + setOpeningErrorMessage(i18n ("The file %1 was opened and contained lines longer than the configured Line Length Limit (%2 characters)." + " Those lines were wrapped and the document is set to read-only mode, as saving will modify its content.", this->url().pathOrUrl(),config()->lineLengthLimit())); + } + + // + // return the success + // + return success; +} + +bool KateDocument::saveFile() +{ + QWidget *parentWidget(dialogParent()); + + // some warnings, if file was changed by the outside! + if ( !url().isEmpty() ) + { + if (m_fileChangedDialogsActivated && m_modOnHd) + { + QString str = reasonedMOHString() + "\n\n"; + + if (!isModified()) + { + if (KMessageBox::warningContinueCancel(parentWidget, + str + i18n("Do you really want to save this unmodified file? You could overwrite changed data in the file on disk."),i18n("Trying to Save Unmodified File"),KGuiItem(i18n("Save Nevertheless"))) != KMessageBox::Continue) + return false; + } + else + { + if (KMessageBox::warningContinueCancel(parentWidget, + str + i18n("Do you really want to save this file? Both your open file and the file on disk were changed. There could be some data lost."),i18n("Possible Data Loss"),KGuiItem(i18n("Save Nevertheless"))) != KMessageBox::Continue) + return false; + } + } + } + + // + // can we encode it if we want to save it ? + // + if (!m_buffer->canEncode () + && (KMessageBox::warningContinueCancel(parentWidget, + i18n("The selected encoding cannot encode every unicode character in this document. Do you really want to save it? There could be some data lost."),i18n("Possible Data Loss"),KGuiItem(i18n("Save Nevertheless"))) != KMessageBox::Continue)) + { + return false; + } + + // + // try to create backup file.. + // + + // local file or not is here the question + bool l ( url().isLocalFile() ); + + // does the user want any backup, if not, not our problem? + if ( ( l && config()->backupFlags() & KateDocumentConfig::LocalFiles ) + || ( ! l && config()->backupFlags() & KateDocumentConfig::RemoteFiles ) ) + { + KUrl u( url() ); + if (config()->backupPrefix().contains(QDir::separator())) { + u.setPath( config()->backupPrefix() + url().fileName() + config()->backupSuffix() ); + } else { + u.setFileName( config()->backupPrefix() + url().fileName() + config()->backupSuffix() ); + } + + kDebug( 13020 ) << "backup src file name: " << url(); + kDebug( 13020 ) << "backup dst file name: " << u; + + // handle the backup... + bool backupSuccess = false; + + // local file mode, no kio + if (u.isLocalFile ()) + { + if (QFile::exists (url().toLocalFile ())) + { + // first: check if backupFile is already there, if true, unlink it + QFile backupFile (u.toLocalFile ()); + if (backupFile.exists()) backupFile.remove (); + + backupSuccess = QFile::copy (url().toLocalFile (), u.toLocalFile ()); + } + else + backupSuccess = true; + } + else // remote file mode, kio + { + // get the right permissions, start with safe default + KIO::UDSEntry fentry; + if (KIO::NetAccess::stat (url(), fentry, QApplication::activeWindow())) { + // do a evil copy which will overwrite target if possible + KFileItem item (fentry, url()); + KIO::FileCopyJob *job = KIO::file_copy ( url(), u, item.permissions(), KIO::Overwrite ); + backupSuccess = KIO::NetAccess::synchronousRun(job, QApplication::activeWindow()); + } + else + backupSuccess = true; + } + + // backup has failed, ask user how to proceed + if (!backupSuccess && (KMessageBox::warningContinueCancel (parentWidget + , i18n ("For file %1 no backup copy could be created before saving." + " If an error occurs while saving, you might lose the data of this file." + " A reason could be that the media you write to is full or the directory of the file is read-only for you.", url().pathOrUrl()) + , i18n ("Failed to create backup copy.") + , KGuiItem(i18n("Try to Save Nevertheless")) + , KStandardGuiItem::cancel(), "Backup Failed Warning") != KMessageBox::Continue)) + { + return false; + } + } + + // update file type, pass no file path, read file type content from this document + updateFileType (KateGlobal::self()->modeManager()->fileType (this, QString ())); + + // remember the oldpath... + QString oldPath = m_dirWatchFile; + + // read dir config (if possible and wanted) + if ( url().isLocalFile()) + { + QFileInfo fo (oldPath), fn (localFilePath()); + + if (fo.path() != fn.path()) + readDirConfig(); + } + + // read our vars + readVariables(); + + // remove file from dirwatch + deactivateDirWatch (); + + // remove all trailing spaces in the document (as edit actions) + // NOTE: we need this as edit actions, since otherwise the edit actions + // in the swap file recovery may happen at invalid cursor positions + removeTrailingSpaces (); + + // + // try to save + // + if (!m_buffer->saveFile (localFilePath())) + { + // add m_file again to dirwatch + activateDirWatch (oldPath); + + KMessageBox::error (parentWidget, i18n ("The document could not be saved, as it was not possible to write to %1.\n\nCheck that you have write access to this file or that enough disk space is available.", this->url().pathOrUrl())); + + return false; + } + + // update the digest + createDigest (); + + // add m_file again to dirwatch + activateDirWatch (); + + // + // we are not modified + // + if (m_modOnHd) + { + m_modOnHd = false; + m_modOnHdReason = OnDiskUnmodified; + emit modifiedOnDisk (this, m_modOnHd, m_modOnHdReason); + } + + // update document name... + updateDocName (); + + // url may have changed... + emit documentUrlChanged (this); + + // (dominik) mark last undo group as not mergeable, otherwise the next + // edit action might be merged and undo will never stop at the saved state + m_undoManager->undoSafePoint(); + m_undoManager->updateLineModifications(); + + // + // return success + // + return true; +} + +void KateDocument::readDirConfig () +{ + int depth = config()->searchDirConfigDepth (); + + if (this->url().isLocalFile() && (depth > -1)) + { + QString currentDir = QFileInfo (localFilePath()).absolutePath(); + + // only search as deep as specified or not at all ;) + while (depth > -1) + { + //kDebug (13020) << "search for config file in path: " << currentDir; + + // try to open config file in this dir + QFile f (currentDir + "/.kateconfig"); + + if (f.open (QIODevice::ReadOnly)) + { + QTextStream stream (&f); + + uint linesRead = 0; + QString line = stream.readLine(); + while ((linesRead < 32) && !line.isNull()) + { + readVariableLine( line ); + + line = stream.readLine(); + + linesRead++; + } + + break; + } + + QString newDir = QFileInfo (currentDir).absolutePath(); + + // bail out on looping (for example reached /) + if (currentDir == newDir) + break; + + currentDir = newDir; + --depth; + } + } +} + +void KateDocument::activateDirWatch (const QString &useFileName) +{ + QString fileToUse = useFileName; + if (fileToUse.isEmpty()) + fileToUse = localFilePath(); + + QFileInfo fileInfo = QFileInfo(fileToUse); + if (fileInfo.isSymLink()) { + // Monitor the actual data and not the symlink + fileToUse = fileInfo.canonicalFilePath(); + } + + // same file as we are monitoring, return + if (fileToUse == m_dirWatchFile) + return; + + // remove the old watched file + deactivateDirWatch (); + + // add new file if needed + if (url().isLocalFile() && !fileToUse.isEmpty()) + { + KateGlobal::self()->dirWatch ()->addFile (fileToUse); + m_dirWatchFile = fileToUse; + } +} + +void KateDocument::deactivateDirWatch () +{ + if (!m_dirWatchFile.isEmpty()) + KateGlobal::self()->dirWatch ()->removeFile (m_dirWatchFile); + + m_dirWatchFile.clear(); +} + +bool KateDocument::openUrl( const KUrl &url ) { + bool res=KTextEditor::Document::openUrl(url); + updateDocName(); + return res; +} + +bool KateDocument::closeUrl() +{ + // + // file mod on hd + // + if ( !m_reloading && !url().isEmpty() ) + { + if (m_fileChangedDialogsActivated && m_modOnHd) + { + QWidget *parentWidget(dialogParent()); + + if (!(KMessageBox::warningContinueCancel( + parentWidget, + reasonedMOHString() + "\n\n" + i18n("Do you really want to continue to close this file? Data loss may occur."), + i18n("Possible Data Loss"), KGuiItem(i18n("Close Nevertheless")), KStandardGuiItem::cancel(), + QString("kate_close_modonhd_%1").arg( m_modOnHdReason ) ) == KMessageBox::Continue)) { + /** + * reset reloading + */ + m_reloading = false; + return false; + } + } + } + + // + // first call the normal kparts implementation + // + if (!KParts::ReadWritePart::closeUrl ()) { + /** + * reset reloading + */ + m_reloading = false; + return false; + } + + // Tell the world that we're about to go ahead with the close + if (!m_reloading) + emit aboutToClose(this); + + /** + * delete all KTE::Messages + */ + if (m_messageHash.count()) { + QList keys = m_messageHash.keys (); + foreach (KTextEditor::Message* message, keys) + delete message; + } + + /** + * we are about to invalidate all cursors/ranges/.. => m_buffer->clear will do so + */ + emit aboutToInvalidateMovingInterfaceContent (this); + + // remove file from dirwatch + deactivateDirWatch (); + + // + // empty url + fileName + // + setUrl(KUrl()); + setLocalFilePath(QString()); + + // we are not modified + if (m_modOnHd) + { + m_modOnHd = false; + m_modOnHdReason = OnDiskUnmodified; + emit modifiedOnDisk (this, m_modOnHd, m_modOnHdReason); + } + + emit KTextEditor::Document::textRemoved(this, documentRange()); + emit KTextEditor::Document::textRemoved(this, documentRange(), m_buffer->text()); + + { + // remove all marks + clearMarks (); + + // clear the buffer + m_buffer->clear(); + + // clear undo/redo history + m_undoManager->clearUndo(); + m_undoManager->clearRedo(); + } + + // no, we are no longer modified + setModified(false); + + // we have no longer any hl + m_buffer->setHighlight(0); + + // update all our views + foreach (KateView * view, m_views ) + { + view->clearSelection(); // fix bug #118588 + view->clear(); + } + + if (!m_reloading) + { + // uh, fileName changed + emit documentUrlChanged (this); + + // update doc name + updateDocName (); + } + + // purge swap file + m_swapfile->fileClosed (); + + // success + return true; +} + +bool KateDocument::isDataRecoveryAvailable() const +{ + return m_swapfile->shouldRecover(); +} + +void KateDocument::recoverData() +{ + if (isDataRecoveryAvailable()) + m_swapfile->recover(); +} + +void KateDocument::discardDataRecovery() +{ + if (isDataRecoveryAvailable()) + m_swapfile->discard(); +} + +void KateDocument::setReadWrite( bool rw ) +{ + if (isReadWrite() != rw) + { + KParts::ReadWritePart::setReadWrite (rw); + + foreach( KateView* view, m_views) + { + view->slotUpdateUndo(); + view->slotReadWriteChanged (); + } + emit readWriteChanged(this); + } +} + +void KateDocument::setModified(bool m) { + + if (isModified() != m) { + KParts::ReadWritePart::setModified (m); + + foreach( KateView* view,m_views) + { + view->slotUpdateUndo(); + } + + emit modifiedChanged (this); + } + + m_undoManager->setModified (m); +} +//END + +//BEGIN Kate specific stuff ;) + +void KateDocument::makeAttribs(bool needInvalidate) +{ + foreach(KateView *view,m_views) + view->renderer()->updateAttributes (); + + if (needInvalidate) + m_buffer->invalidateHighlighting(); + + foreach(KateView *view,m_views) + { + view->tagAll(); + view->updateView (true); + } +} + +// the attributes of a hl have changed, update +void KateDocument::internalHlChanged() +{ + makeAttribs(); +} + +void KateDocument::addView(KTextEditor::View *view) { + if (!view) + return; + + m_views.append( static_cast(view) ); + m_textEditViews.append( view ); + + // apply the view & renderer vars from the file type + if (!m_fileType.isEmpty()) + readVariableLine(KateGlobal::self()->modeManager()->fileType(m_fileType).varLine, true); + + // apply the view & renderer vars from the file + readVariables (true); + + setActiveView(view); +} + +void KateDocument::removeView(KTextEditor::View *view) { + if (!view) + return; + + if (activeView() == view) + setActiveView(0L); + + m_views.removeAll( static_cast(view) ); + m_textEditViews.removeAll( view ); +} + +void KateDocument::setActiveView(KTextEditor::View* view) +{ + if ( m_activeView == view ) + return; + + m_activeView = static_cast(view); +} + +bool KateDocument::ownedView(KateView *view) { + // do we own the given view? + return (m_views.contains(view)); +} + +int KateDocument::toVirtualColumn( int line, int column ) const +{ + Kate::TextLine textLine = m_buffer->plainLine(line); + + if (textLine) + return textLine->toVirtualColumn(column, config()->tabWidth()); + else + return 0; +} + +int KateDocument::toVirtualColumn( const KTextEditor::Cursor& cursor ) const +{ + return toVirtualColumn(cursor.line(), cursor.column()); +} + +int KateDocument::fromVirtualColumn( int line, int column ) const +{ + Kate::TextLine textLine = m_buffer->plainLine(line); + + if (textLine) + return textLine->fromVirtualColumn(column, config()->tabWidth()); + else + return 0; +} + +int KateDocument::fromVirtualColumn( const KTextEditor::Cursor& cursor ) const +{ + return fromVirtualColumn(cursor.line(), cursor.column()); +} + +bool KateDocument::typeChars ( KateView *view, const QString &realChars ) +{ + Kate::TextLine textLine = m_buffer->plainLine(view->cursorPosition().line ()); + if (!textLine) + return false; + + /** + * filter out non-printable + */ + QString chars; + Q_FOREACH (QChar c, realChars) + if (c.isPrint() || c == QChar::fromLatin1('\t')) + chars.append (c); + + if (chars.isEmpty()) + return false; + + editStart (); + + if (!view->config()->persistentSelection() && view->selection() ) + view->removeSelectedText(); + + KTextEditor::Cursor oldCur (view->cursorPosition()); + + if (config()->ovr()) { + + KTextEditor::Range r = KTextEditor::Range(view->cursorPosition(), qMin(chars.length(), + textLine->length() - view->cursorPosition().column())); + + removeText(r); + } + + if (view->blockSelection() && view->selection()) { + KTextEditor::Range selectionRange = view->selectionRange(); + int startLine = qMax(0, selectionRange.start().line()); + int endLine = qMin(selectionRange.end().line(), lastLine()); + int column = toVirtualColumn(selectionRange.end()); + for (int line = endLine; line >= startLine; --line) + editInsertText(line, fromVirtualColumn(line, column), chars); + int newSelectionColumn = toVirtualColumn(view->cursorPosition()); + selectionRange.start().setColumn(fromVirtualColumn(selectionRange.start().line(), newSelectionColumn)); + selectionRange.end().setColumn(fromVirtualColumn(selectionRange.end().line(), newSelectionColumn)); + view->setSelection(selectionRange); + } + else + insertText(view->cursorPosition(), chars); + + // end edit session here, to have updated HL in userTypedChar! + editEnd(); + + KTextEditor::Cursor b(view->cursorPosition()); + m_indenter->userTypedChar (view, b, chars.isEmpty() ? QChar() : chars.at(chars.length() - 1)); + + view->slotTextInserted (view, oldCur, chars); + return true; +} + +void KateDocument::newLine( KateView *v ) +{ + editStart(); + + if( !v->config()->persistentSelection() && v->selection() ) { + v->removeSelectedText(); + v->clearSelection(); + } + + // query cursor position + KTextEditor::Cursor c = v->cursorPosition(); + + if (c.line() > (int)lastLine()) + c.setLine(lastLine()); + + if (c.line() < 0) + c.setLine(0); + + uint ln = c.line(); + + Kate::TextLine textLine = plainKateTextLine(ln); + + if (c.column() > (int)textLine->length()) + c.setColumn(textLine->length()); + + // first: wrap line + editWrapLine (c.line(), c.column()); + + // end edit session here, to have updated HL in userTypedChar! + editEnd(); + + // second: indent the new line, if needed... + m_indenter->userTypedChar(v, v->cursorPosition(), '\n'); +} + +void KateDocument::transpose( const KTextEditor::Cursor& cursor) +{ + Kate::TextLine textLine = m_buffer->plainLine(cursor.line()); + + if (!textLine || (textLine->length() < 2)) + return; + + uint col = cursor.column(); + + if (col > 0) + col--; + + if ((textLine->length() - col) < 2) + return; + + uint line = cursor.line(); + QString s; + + //clever swap code if first character on the line swap right&left + //otherwise left & right + s.append (textLine->at(col+1)); + s.append (textLine->at(col)); + //do the swap + + // do it right, never ever manipulate a textline + editStart (); + editRemoveText (line, col, 2); + editInsertText (line, col, s); + editEnd (); +} + +void KateDocument::backspace( KateView *view, const KTextEditor::Cursor& c ) +{ + if ( !view->config()->persistentSelection() && view->selection() ) { + if (view->blockSelection() && view->selection() && toVirtualColumn(view->selectionRange().start()) == toVirtualColumn(view->selectionRange().end())) { + // Remove one character after selection line + KTextEditor::Range range = view->selectionRange(); + range.start().setColumn(range.start().column() - 1); + view->setSelection(range); + } + view->removeSelectedText(); + return; + } + + uint col = qMax( c.column(), 0 ); + uint line = qMax( c.line(), 0 ); + + if ((col == 0) && (line == 0)) + return; + + if (col > 0) + { + if (!(config()->backspaceIndents())) + { + // ordinary backspace + //c.cursor.col--; + removeText(KTextEditor::Range(line, col-1, line, col)); + // in most cases cursor is moved by removeText, but we should do it manually + // for past-end-of-line cursors in block mode + view->setCursorPosition(KTextEditor::Cursor(line, col-1)); + } + else + { + // backspace indents: erase to next indent position + Kate::TextLine textLine = m_buffer->plainLine(line); + + // don't forget this check!!!! really!!!! + if (!textLine) + return; + + int colX = textLine->toVirtualColumn(col, config()->tabWidth()); + int pos = textLine->firstChar(); + if (pos > 0) + pos = textLine->toVirtualColumn(pos, config()->tabWidth()); + + if (pos < 0 || pos >= (int)colX) + { + // only spaces on left side of cursor + indent( KTextEditor::Range( line, 0, line, 0), -1); + } + else + { + removeText(KTextEditor::Range(line, col-1, line, col)); + // in most cases cursor is moved by removeText, but we should do it manually + // for past-end-of-line cursors in block mode + view->setCursorPosition(KTextEditor::Cursor(line, col-1)); + } + } + } + else + { + // col == 0: wrap to previous line + if (line >= 1) + { + Kate::TextLine textLine = m_buffer->plainLine(line-1); + + // don't forget this check!!!! really!!!! + if (!textLine) + return; + + if (config()->wordWrap() && textLine->endsWith(QLatin1String(" "))) + { + // gg: in hard wordwrap mode, backspace must also eat the trailing space + removeText (KTextEditor::Range(line-1, textLine->length()-1, line, 0)); + } + else + removeText (KTextEditor::Range(line-1, textLine->length(), line, 0)); + } + } +} + +void KateDocument::del( KateView *view, const KTextEditor::Cursor& c ) +{ + if ( !view->config()->persistentSelection() && view->selection() ) { + if (view->blockSelection() && view->selection() && toVirtualColumn(view->selectionRange().start()) == toVirtualColumn(view->selectionRange().end())) { + // Remove one character after selection line + KTextEditor::Range range = view->selectionRange(); + range.end().setColumn(range.end().column() + 1); + view->setSelection(range); + } + view->removeSelectedText(); + return; + } + + if( c.column() < (int) m_buffer->plainLine(c.line())->length()) + { + removeText(KTextEditor::Range(c, 1)); + } + else if ( c.line() < lastLine() ) + { + removeText(KTextEditor::Range(c.line(), c.column(), c.line()+1, 0)); + } +} + +void KateDocument::paste ( KateView* view, const QString &text ) +{ + static const QChar newLineChar('\n'); + QString s = text; + + if (s.isEmpty()) + return; + + int lines = s.count (newLineChar); + + m_undoManager->undoSafePoint(); + + editStart (); + + KTextEditor::Cursor pos = view->cursorPosition(); + if (!view->config()->persistentSelection() && view->selection()) { + pos = view->selectionRange().start(); + if (view->blockSelection()) { + pos = rangeOnLine(view->selectionRange(), pos.line()).start(); + if (lines == 0) { + s += newLineChar; + s = s.repeated(view->selectionRange().numberOfLines() + 1); + s.chop(1); + } + } + view->removeSelectedText(); + } + + if (config()->ovr()) { + QStringList pasteLines = s.split(newLineChar); + + if (!view->blockSelection()) { + int endColumn = (pasteLines.count() == 1 ? pos.column() : 0) + pasteLines.last().length(); + removeText(KTextEditor::Range(pos, + pos.line()+pasteLines.count()-1, endColumn)); + } else { + int maxi = qMin(pos.line() + pasteLines.count(), this->lines()); + + for (int i = pos.line(); i < maxi; ++i) { + int pasteLength = pasteLines.at(i-pos.line()).length(); + removeText(KTextEditor::Range(i, pos.column(), + i, qMin(pasteLength + pos.column(), lineLength(i)))); + } + } + } + + + insertText(pos, s, view->blockSelection()); + editEnd(); + + // move cursor right for block select, as the user is moved right internal + // even in that case, but user expects other behavior in block selection + // mode ! + // just let cursor stay, that was it before I changed to moving ranges! + if (view->blockSelection()) + view->setCursorPositionInternal(pos); + + if (config()->indentPastedText()) + { + KTextEditor::Range range = KTextEditor::Range(KTextEditor::Cursor(pos.line(), 0), + KTextEditor::Cursor(pos.line() + lines, 0)); + + m_indenter->indent(view, range); + } + + if (!view->blockSelection()) + emit charactersSemiInteractivelyInserted (pos, s); + m_undoManager->undoSafePoint(); +} + +void KateDocument::indent (KTextEditor::Range range, int change) +{ + if (!isReadWrite()) + return; + + editStart(); + m_indenter->changeIndent(range, change); + editEnd(); +} + +void KateDocument::align(KateView *view, const KTextEditor::Range &range) +{ + m_indenter->indent(view, range); +} + +void KateDocument::insertTab( KateView *view, const KTextEditor::Cursor&) +{ + if (!isReadWrite()) + return; + + int lineLen = line(view->cursorPosition().line()).length(); + KTextEditor::Cursor c = view->cursorPosition(); + + editStart(); + + + if (!view->config()->persistentSelection() && view->selection() ) { + view->removeSelectedText(); + } + else if (config()->ovr() && c.column() < lineLen) + { + KTextEditor::Range r = KTextEditor::Range(view->cursorPosition(), 1); + + removeText(r); + } + + c = view->cursorPosition(); + editInsertText(c.line(), c.column(), QChar('\t')); + + editEnd(); +} + +/* + Remove a given string at the beginning + of the current line. +*/ +bool KateDocument::removeStringFromBeginning(int line, const QString &str) +{ + Kate::TextLine textline = m_buffer->plainLine(line); + + KTextEditor::Cursor cursor (line, 0); + bool there = textline->startsWith(str); + + if (!there) + { + cursor.setColumn(textline->firstChar()); + there = textline->matchesAt(cursor.column(), str); + } + + if (there) + { + // Remove some chars + removeText (KTextEditor::Range(cursor, str.length())); + } + + return there; +} + +/* + Remove a given string at the end + of the current line. +*/ +bool KateDocument::removeStringFromEnd(int line, const QString &str) +{ + Kate::TextLine textline = m_buffer->plainLine(line); + + KTextEditor::Cursor cursor (line, 0); + bool there = textline->endsWith(str); + + if (there) + { + cursor.setColumn(textline->length() - str.length()); + } + else + { + cursor.setColumn(textline->lastChar() - str.length() + 1); + there = textline->matchesAt(cursor.column(), str); + } + + if (there) + { + // Remove some chars + removeText (KTextEditor::Range(cursor, str.length())); + } + + return there; +} + +/* + Add to the current line a comment line mark at the beginning. +*/ +void KateDocument::addStartLineCommentToSingleLine( int line, int attrib ) +{ + QString commentLineMark = highlight()->getCommentSingleLineStart(attrib); + int pos = -1; + + if (highlight()->getCommentSingleLinePosition(attrib) == KateHighlighting::CSLPosColumn0) + { + pos = 0; + commentLineMark += ' '; + } else { + const Kate::TextLine l = kateTextLine(line); + pos = l->firstChar(); + } + + if (pos >= 0) + insertText (KTextEditor::Cursor(line, pos), commentLineMark); +} + +/* + Remove from the current line a comment line mark at + the beginning if there is one. +*/ +bool KateDocument::removeStartLineCommentFromSingleLine( int line, int attrib ) +{ + const QString shortCommentMark = highlight()->getCommentSingleLineStart( attrib ); + const QString longCommentMark = shortCommentMark + ' '; + + editStart(); + + // Try to remove the long comment mark first + bool removed = (removeStringFromBeginning(line, longCommentMark) + || removeStringFromBeginning(line, shortCommentMark)); + + editEnd(); + + return removed; +} + +/* + Add to the current line a start comment mark at the + beginning and a stop comment mark at the end. +*/ +void KateDocument::addStartStopCommentToSingleLine( int line, int attrib ) +{ + const QString startCommentMark = highlight()->getCommentStart( attrib ) + ' '; + const QString stopCommentMark = ' ' + highlight()->getCommentEnd( attrib ); + + editStart(); + + // Add the start comment mark + insertText (KTextEditor::Cursor(line, 0), startCommentMark); + + // Go to the end of the line + const int col = m_buffer->plainLine(line)->length(); + + // Add the stop comment mark + insertText (KTextEditor::Cursor(line, col), stopCommentMark); + + editEnd(); +} + +/* + Remove from the current line a start comment mark at + the beginning and a stop comment mark at the end. +*/ +bool KateDocument::removeStartStopCommentFromSingleLine( int line, int attrib ) +{ + QString shortStartCommentMark = highlight()->getCommentStart( attrib ); + QString longStartCommentMark = shortStartCommentMark + ' '; + QString shortStopCommentMark = highlight()->getCommentEnd( attrib ); + QString longStopCommentMark = ' ' + shortStopCommentMark; + + editStart(); + + // TODO "that's a bad idea, can lead to stray endings, FIXME" + + // Try to remove the long start comment mark first + bool removedStart = (removeStringFromBeginning(line, longStartCommentMark) + || removeStringFromBeginning(line, shortStartCommentMark)); + + bool removedStop = false; + if (removedStart) + { + // Try to remove the long stop comment mark first + removedStop = (removeStringFromEnd(line, longStopCommentMark) + || removeStringFromEnd(line, shortStopCommentMark)); + } + + editEnd(); + + return (removedStart || removedStop); +} + +/* + Add to the current selection a start comment mark at the beginning + and a stop comment mark at the end. +*/ +void KateDocument::addStartStopCommentToSelection( KateView *view, int attrib ) +{ + const QString startComment = highlight()->getCommentStart( attrib ); + const QString endComment = highlight()->getCommentEnd( attrib ); + + KTextEditor::Range range = view->selectionRange(); + + if ((range.end().column() == 0) && (range.end().line() > 0)) + range.end().setPosition(range.end().line() - 1, lineLength(range.end().line() - 1)); + + editStart(); + + if (!view->blockSelection()) { + insertText(range.end(), endComment); + insertText(range.start(), startComment); + } else { + for (int line = range.start().line(); line <= range.end().line(); line++ ) { + KTextEditor::Range subRange = rangeOnLine(range, line); + insertText(subRange.end(), endComment); + insertText(subRange.start(), startComment); + } + } + + editEnd (); + // selection automatically updated (MovingRange) +} + +/* + Add to the current selection a comment line mark at the beginning of each line. +*/ +void KateDocument::addStartLineCommentToSelection( KateView *view, int attrib ) +{ + const QString commentLineMark = highlight()->getCommentSingleLineStart( attrib ) + ' '; + + int sl = view->selectionRange().start().line(); + int el = view->selectionRange().end().line(); + + // if end of selection is in column 0 in last line, omit the last line + if ((view->selectionRange().end().column() == 0) && (el > 0)) + { + el--; + } + + editStart(); + + // For each line of the selection + for (int z = el; z >= sl; z--) { + //insertText (z, 0, commentLineMark); + addStartLineCommentToSingleLine(z, attrib ); + } + + editEnd (); + // selection automatically updated (MovingRange) +} + +bool KateDocument::nextNonSpaceCharPos(int &line, int &col) +{ + for(; line < (int)m_buffer->count(); line++) { + Kate::TextLine textLine = m_buffer->plainLine(line); + + if (!textLine) + break; + + col = textLine->nextNonSpaceChar(col); + if(col != -1) + return true; // Next non-space char found + col = 0; + } + // No non-space char found + line = -1; + col = -1; + return false; +} + +bool KateDocument::previousNonSpaceCharPos(int &line, int &col) +{ + while(true) + { + Kate::TextLine textLine = m_buffer->plainLine(line); + + if (!textLine) + break; + + col = textLine->previousNonSpaceChar(col); + if(col != -1) return true; + if(line == 0) return false; + --line; + col = textLine->length(); + } + // No non-space char found + line = -1; + col = -1; + return false; +} + +/* + Remove from the selection a start comment mark at + the beginning and a stop comment mark at the end. +*/ +bool KateDocument::removeStartStopCommentFromSelection( KateView *view, int attrib ) +{ + const QString startComment = highlight()->getCommentStart( attrib ); + const QString endComment = highlight()->getCommentEnd( attrib ); + + int sl = qMax (0, view->selectionRange().start().line()); + int el = qMin (view->selectionRange().end().line(), lastLine()); + int sc = view->selectionRange().start().column(); + int ec = view->selectionRange().end().column(); + + // The selection ends on the char before selectEnd + if (ec != 0) { + --ec; + } else if (el > 0) { + --el; + ec = m_buffer->plainLine(el)->length() - 1; + } + + const int startCommentLen = startComment.length(); + const int endCommentLen = endComment.length(); + + // had this been perl or sed: s/^\s*$startComment(.+?)$endComment\s*/$2/ + + bool remove = nextNonSpaceCharPos(sl, sc) + && m_buffer->plainLine(sl)->matchesAt(sc, startComment) + && previousNonSpaceCharPos(el, ec) + && ( (ec - endCommentLen + 1) >= 0 ) + && m_buffer->plainLine(el)->matchesAt(ec - endCommentLen + 1, endComment); + + if (remove) { + editStart(); + + removeText (KTextEditor::Range(el, ec - endCommentLen + 1, el, ec + 1)); + removeText (KTextEditor::Range(sl, sc, sl, sc + startCommentLen)); + + editEnd (); + // selection automatically updated (MovingRange) + } + + return remove; +} + +bool KateDocument::removeStartStopCommentFromRegion(const KTextEditor::Cursor &start,const KTextEditor::Cursor &end,int attrib) +{ + const QString startComment = highlight()->getCommentStart( attrib ); + const QString endComment = highlight()->getCommentEnd( attrib ); + const int startCommentLen = startComment.length(); + const int endCommentLen = endComment.length(); + + const bool remove = m_buffer->plainLine(start.line())->matchesAt(start.column(), startComment) + && m_buffer->plainLine(end.line())->matchesAt(end.column() - endCommentLen , endComment); + if (remove) { + editStart(); + removeText(KTextEditor::Range(end.line(), end.column() - endCommentLen, end.line(), end.column())); + removeText(KTextEditor::Range(start, startCommentLen)); + editEnd(); + } + return remove; +} + +/* + Remove from the beginning of each line of the + selection a start comment line mark. +*/ +bool KateDocument::removeStartLineCommentFromSelection( KateView *view, int attrib ) +{ + const QString shortCommentMark = highlight()->getCommentSingleLineStart( attrib ); + const QString longCommentMark = shortCommentMark + ' '; + + int sl = view->selectionRange().start().line(); + int el = view->selectionRange().end().line(); + + if ((view->selectionRange().end().column() == 0) && (el > 0)) + { + el--; + } + + bool removed = false; + + editStart(); + + // For each line of the selection + for (int z = el; z >= sl; z--) + { + // Try to remove the long comment mark first + removed = (removeStringFromBeginning(z, longCommentMark) + || removeStringFromBeginning(z, shortCommentMark) + || removed); + } + + editEnd(); + // selection automatically updated (MovingRange) + + return removed; +} + +/* + Comment or uncomment the selection or the current + line if there is no selection. +*/ +void KateDocument::comment( KateView *v, uint line,uint column, int change) +{ + // skip word wrap bug #105373 + const bool skipWordWrap = wordWrap(); + if (skipWordWrap) setWordWrap(false); + + bool hassel = v->selection(); + int c = 0; + + if ( hassel ) + c = v->selectionRange().start().column(); + + int startAttrib = 0; + Kate::TextLine ln = kateTextLine( line ); + + if ( c < ln->length() ) + startAttrib = ln->attribute( c ); + else if ( !ln->contextStack().isEmpty() ) + startAttrib = highlight()->attribute( ln->contextStack().last() ); + + bool hasStartLineCommentMark = !(highlight()->getCommentSingleLineStart( startAttrib ).isEmpty()); + bool hasStartStopCommentMark = ( !(highlight()->getCommentStart( startAttrib ).isEmpty()) + && !(highlight()->getCommentEnd( startAttrib ).isEmpty()) ); + + bool removed = false; + + if (change > 0) // comment + { + if ( !hassel ) + { + if ( hasStartLineCommentMark ) + addStartLineCommentToSingleLine( line, startAttrib ); + else if ( hasStartStopCommentMark ) + addStartStopCommentToSingleLine( line, startAttrib ); + } + else + { + // anders: prefer single line comment to avoid nesting probs + // If the selection starts after first char in the first line + // or ends before the last char of the last line, we may use + // multiline comment markers. + // TODO We should try to detect nesting. + // - if selection ends at col 0, most likely she wanted that + // line ignored + const KTextEditor::Range sel = v->selectionRange(); + if ( hasStartStopCommentMark && + ( !hasStartLineCommentMark || ( + ( sel.start().column() > m_buffer->plainLine( sel.start().line() )->firstChar() ) || + ( sel.end().column() > 0 && + sel.end().column() < (m_buffer->plainLine( sel.end().line() )->length()) ) + ) ) ) + addStartStopCommentToSelection( v, startAttrib ); + else if ( hasStartLineCommentMark ) + addStartLineCommentToSelection( v, startAttrib ); + } + } + else // uncomment + { + if ( !hassel ) + { + removed = ( hasStartLineCommentMark + && removeStartLineCommentFromSingleLine( line, startAttrib ) ) + || ( hasStartStopCommentMark + && removeStartStopCommentFromSingleLine( line, startAttrib ) ); + } + else + { + // anders: this seems like it will work with above changes :) + removed = ( hasStartStopCommentMark + && removeStartStopCommentFromSelection( v, startAttrib ) ) + || ( hasStartLineCommentMark + && removeStartLineCommentFromSelection( v, startAttrib ) ); + } + + // recursive call for toggle comment + if (!removed && change == 0) + comment(v, line, column, 1); + } + + if (skipWordWrap) setWordWrap(true); // see begin of function ::comment (bug #105373) +} + +void KateDocument::transform( KateView *v, const KTextEditor::Cursor &c, + KateDocument::TextTransform t ) +{ + if ( v->selection() ) + { + editStart(); + + // remember cursor + KTextEditor::Cursor cursor = c; + + // cache the selection and cursor, so we can be sure to restore. + KTextEditor::Range selection = v->selectionRange(); + + KTextEditor::Range range(selection.start(), 0); + while ( range.start().line() <= selection.end().line() ) + { + int start = 0; + int end = lineLength( range.start().line() ); + + if (range.start().line() == selection.start().line() || v->blockSelection()) + start = selection.start().column(); + + if (range.start().line() == selection.end().line() || v->blockSelection()) + end = selection.end().column(); + + if ( start > end ) + { + int swapCol = start; + start = end; + end = swapCol; + } + range.start().setColumn( start ); + range.end().setColumn( end ); + + QString s = text( range ); + QString old = s; + + if ( t == Uppercase ) + s = s.toUpper(); + else if ( t == Lowercase ) + s = s.toLower(); + else // Capitalize + { + Kate::TextLine l = m_buffer->plainLine( range.start().line() ); + int p ( 0 ); + while( p < s.length() ) + { + // If bol or the character before is not in a word, up this one: + // 1. if both start and p is 0, upper char. + // 2. if blockselect or first line, and p == 0 and start-1 is not in a word, upper + // 3. if p-1 is not in a word, upper. + if ( ( ! range.start().column() && ! p ) || + ( ( range.start().line() == selection.start().line() || v->blockSelection() ) && + ! p && ! highlight()->isInWord( l->at( range.start().column() - 1 )) ) || + ( p && ! highlight()->isInWord( s.at( p-1 ) ) ) + ) { + s[p] = s.at(p).toUpper(); + } + p++; + } + } + + if ( s != old ) + { + removeText( range ); + insertText( range.start(), s ); + } + + range.setBothLines(range.start().line() + 1); + } + + editEnd(); + + // restore selection & cursor + v->setSelection( selection ); + v->setCursorPosition( c ); + + } else { // no selection + editStart(); + + // get cursor + KTextEditor::Cursor cursor = c; + + QString old = text( KTextEditor::Range(cursor, 1) ); + QString s; + switch ( t ) { + case Uppercase: + s = old.toUpper(); + break; + case Lowercase: + s = old.toLower(); + break; + case Capitalize: + { + Kate::TextLine l = m_buffer->plainLine( cursor.line() ); + while ( cursor.column() > 0 && highlight()->isInWord( l->at( cursor.column() - 1 ), l->attribute( cursor.column() - 1 ) ) ) + cursor.setColumn(cursor.column() - 1); + old = text( KTextEditor::Range(cursor, 1) ); + s = old.toUpper(); + } + break; + default: + break; + } + + removeText( KTextEditor::Range(cursor, 1) ); + insertText( cursor, s ); + + editEnd(); + } + +} + +void KateDocument::joinLines( uint first, uint last ) +{ +// if ( first == last ) last += 1; + editStart(); + int line( first ); + while ( first < last ) + { + // Normalize the whitespace in the joined lines by making sure there's + // always exactly one space between the joined lines + // This cannot be done in editUnwrapLine, because we do NOT want this + // behavior when deleting from the start of a line, just when explicitly + // calling the join command + Kate::TextLine l = kateTextLine( line ); + Kate::TextLine tl = kateTextLine( line + 1 ); + + if ( !l || !tl ) + { + editEnd(); + return; + } + + int pos = tl->firstChar(); + if ( pos >= 0 ) + { + if (pos != 0) + editRemoveText( line + 1, 0, pos ); + if ( !( l->length() == 0 || l->at( l->length() - 1 ).isSpace() ) ) + editInsertText( line + 1, 0, " " ); + } + else + { + // Just remove the whitespace and let Kate handle the rest + editRemoveText( line + 1, 0, tl->length() ); + } + + editUnWrapLine( line ); + first++; + } + editEnd(); +} + +QString KateDocument::getWord( const KTextEditor::Cursor& cursor ) +{ + int start, end, len; + + Kate::TextLine textLine = m_buffer->plainLine(cursor.line()); + len = textLine->length(); + start = end = cursor.column(); + if (start > len) // Probably because of non-wrapping cursor mode. + return QString(); + + while (start > 0 && highlight()->isInWord(textLine->at(start - 1), textLine->attribute(start - 1))) start--; + while (end < len && highlight()->isInWord(textLine->at(end), textLine->attribute(end))) end++; + len = end - start; + return textLine->string().mid(start, len); +} + +void KateDocument::tagLines(int start, int end) +{ + foreach(KateView *view,m_views) + view->tagLines (start, end, true); +} + +void KateDocument::repaintViews(bool paintOnlyDirty) +{ + foreach(KateView *view,m_views) + view->repaintText(paintOnlyDirty); +} + +/* + Bracket matching uses the following algorithm: + If in overwrite mode, match the bracket currently underneath the cursor. + Otherwise, if the character to the left is a bracket, + match it. Otherwise if the character to the right of the cursor is a + bracket, match it. Otherwise, don't match anything. +*/ +void KateDocument::newBracketMark( const KTextEditor::Cursor& cursor, KTextEditor::Range& bm, int maxLines ) +{ + // search from cursor for brackets + KTextEditor::Range range (cursor, cursor); + + // if match found, remember the range + if( findMatchingBracket( range, maxLines ) ) { + bm = range; + return; + } + + // else, invalidate, if still valid + if (bm.isValid()) + bm = KTextEditor::Range::invalid(); +} + +bool KateDocument::findMatchingBracket( KTextEditor::Range& range, int maxLines ) +{ + Kate::TextLine textLine = m_buffer->plainLine( range.start().line() ); + if( !textLine ) + return false; + + QChar right = textLine->at( range.start().column() ); + QChar left = textLine->at( range.start().column() - 1 ); + QChar bracket; + + if ( config()->ovr() ) { + if( isBracket( right ) ) { + bracket = right; + } else { + return false; + } + } else if ( isBracket( left ) ) { + range.start().setColumn(range.start().column() - 1); + bracket = left; + } else if ( isBracket( right ) ) { + bracket = right; + } else { + return false; + } + + QChar opposite; + + switch( bracket.toAscii() ) { + case '{': opposite = '}'; break; + case '}': opposite = '{'; break; + case '[': opposite = ']'; break; + case ']': opposite = '['; break; + case '(': opposite = ')'; break; + case ')': opposite = '('; break; + default: return false; + } + + const int searchDir = isStartBracket( bracket ) ? 1 : -1; + uint nesting = 0; + + int minLine = qMax( range.start().line() - maxLines, 0 ); + int maxLine = qMin( range.start().line() + maxLines, documentEnd().line() ); + + range.end() = range.start(); + KTextEditor::DocumentCursor cursor(this); + cursor.setPosition(range.start()); + int validAttr = kateTextLine(cursor.line())->attribute(cursor.column()); + + while( cursor.line() >= minLine && cursor.line() <= maxLine ) { + + if (!cursor.move(searchDir)) + return false; + + Kate::TextLine textLine = kateTextLine(cursor.line()); + if (textLine->attribute(cursor.column()) == validAttr ) + { + // Check for match + QChar c = textLine->at(cursor.column()); + if( c == opposite ) { + if( nesting == 0 ) { + if (searchDir > 0) // forward + range.end() = cursor.toCursor(); + else + range.start() = cursor.toCursor(); + return true; + } + nesting--; + } else if( c == bracket ) { + nesting++; + } + } + } + + return false; +} + +// helper: remove \r and \n from visible document name (bug #170876) +inline static QString removeNewLines(const QString& str) +{ + QString tmp(str); + return tmp.replace(QLatin1String("\r\n"), QLatin1String(" ")) + .replace(QChar('\r'), QLatin1Char(' ')) + .replace(QChar('\n'), QLatin1Char(' ')); +} + +void KateDocument::updateDocName () +{ + // if the name is set, and starts with FILENAME, it should not be changed! + if ( ! url().isEmpty() + && (m_docName == removeNewLines(url().fileName()) || + m_docName.startsWith (removeNewLines(url().fileName()) + " (") ) ) { + return; + } + + int count = -1; + + + foreach(KateDocument* doc, KateGlobal::self()->kateDocuments()) + { + if ( (doc != this) && (doc->url().fileName() == url().fileName()) ) + if ( doc->m_docNameNumber > count ) + count = doc->m_docNameNumber; + } + + m_docNameNumber = count + 1; + + QString oldName = m_docName; + m_docName = removeNewLines(url().fileName()); + + m_isUntitled = m_docName.isEmpty(); + if (m_isUntitled) { + m_docName = i18n ("Untitled"); + } + + if (m_docNameNumber > 0) + m_docName = QString(m_docName + " (%1)").arg(m_docNameNumber+1); + + /** + * avoid to emit this, if name doesn't change! + */ + if (oldName != m_docName) + emit documentNameChanged (this); +} + +void KateDocument::slotModifiedOnDisk( KTextEditor::View * /*v*/ ) +{ + if ( m_isasking < 0 ) + { + m_isasking = 0; + return; + } + + if ( !m_fileChangedDialogsActivated || m_isasking ) + return; + + if (m_modOnHd && !url().isEmpty()) + { + m_isasking = 1; + + QWidget *parentWidget(dialogParent()); + + KateModOnHdPrompt p( this, m_modOnHdReason, reasonedMOHString(), parentWidget ); + switch ( p.exec() ) + { + case KateModOnHdPrompt::Save: + { + m_modOnHd = false; + KEncodingFileDialog::Result res=KEncodingFileDialog::getSaveUrlAndEncoding(config()->encoding(), + url().url(),QString(),parentWidget,i18n("Save File")); + + kDebug(13020)<<"got "< tmp; + + for (QHash::const_iterator i = m_marks.constBegin(); i != m_marks.constEnd(); ++i) + { + KateDocumentTmpMark m; + + m.line = line (i.value()->line); + m.mark = *i.value(); + + tmp.append (m); + } + + const QString oldMode = mode (); + const bool byUser = m_fileTypeSetByUser; + const QString hl_mode = highlightingMode (); + KTextEditor::View* oldActiveView = activeView(); + + m_storedVariables.clear(); + + // save cursor positions for all views + QVector cursorPositions; + cursorPositions.reserve(m_views.size()); + foreach (KateView *v, m_views) + cursorPositions.append( v->cursorPosition() ); + + m_reloading = true; + KateDocument::openUrl( url() ); + + // reset some flags only valid for one reload! + m_userSetEncodingForNextReload = false; + + // restore cursor positions for all views + QList::iterator it = m_views.begin(); + for(int i = 0; i < m_views.size(); ++i, ++it) { + setActiveView(*it); + (*it)->setCursorPositionInternal( cursorPositions.at(i), m_config->tabWidth(), false ); + if ((*it)->isVisible()) { + (*it)->repaintText(false); + } + } + setActiveView(oldActiveView); + + for (int z=0; z < tmp.size(); z++) + { + if (z < (int)lines()) + { + if (line(tmp.at(z).mark.line) == tmp.at(z).line) + setMark (tmp.at(z).mark.line, tmp.at(z).mark.type); + } + } + + if (byUser) + setMode (oldMode); + setHighlightingMode (hl_mode); + + emit reloaded(this); + + return true; + } + + return false; +} + +bool KateDocument::documentSave() +{ + if( !url().isValid() || !isReadWrite() ) + return documentSaveAs(); + + return save(); +} + +bool KateDocument::documentSaveAs() +{ + QWidget *parentWidget(dialogParent()); + + KEncodingFileDialog::Result res=KEncodingFileDialog::getSaveUrlAndEncoding(config()->encoding(), + url().url(),QString(),parentWidget,i18n("Save File")); + + if( res.URLs.isEmpty() || !checkOverwrite( res.URLs.first(), parentWidget ) ) + return false; + + setEncoding( res.encoding ); + + return saveAs( res.URLs.first() ); +} + +void KateDocument::setWordWrap (bool on) +{ + config()->setWordWrap (on); +} + +bool KateDocument::wordWrap () const +{ + return config()->wordWrap (); +} + +void KateDocument::setWordWrapAt (uint col) +{ + config()->setWordWrapAt (col); +} + +unsigned int KateDocument::wordWrapAt () const +{ + return config()->wordWrapAt (); +} + +void KateDocument::setPageUpDownMovesCursor (bool on) +{ + config()->setPageUpDownMovesCursor (on); +} + +bool KateDocument::pageUpDownMovesCursor () const +{ + return config()->pageUpDownMovesCursor (); +} +//END + +bool KateDocument::setEncoding (const QString &e) +{ + return m_config->setEncoding(e); +} + +const QString &KateDocument::encoding() const +{ + return m_config->encoding(); +} + +void KateDocument::updateConfig () +{ + m_undoManager->updateConfig (); + + // switch indenter if needed and update config.... + m_indenter->setMode (m_config->indentationMode()); + m_indenter->updateConfig(); + + // set tab width there, too + m_buffer->setTabWidth (config()->tabWidth()); + + // update all views, does tagAll and updateView... + foreach (KateView * view, m_views) + view->updateDocumentConfig (); + + // update on-the-fly spell checking as spell checking defaults might have changes + if(m_onTheFlyChecker) { + m_onTheFlyChecker->updateConfig(); + } + + emit configChanged(); +} + +//BEGIN Variable reader +// "local variable" feature by anders, 2003 +/* TODO + add config options (how many lines to read, on/off) + add interface for plugins/apps to set/get variables + add view stuff +*/ +QRegExp KateDocument::kvLine = QRegExp("kate:(.*)"); +QRegExp KateDocument::kvLineWildcard = QRegExp("kate-wildcard\\((.*)\\):(.*)"); +QRegExp KateDocument::kvLineMime = QRegExp("kate-mimetype\\((.*)\\):(.*)"); +QRegExp KateDocument::kvVar = QRegExp("([\\w\\-]+)\\s+([^;]+)"); + +void KateDocument::readVariables(bool onlyViewAndRenderer) +{ + if (!onlyViewAndRenderer) + m_config->configStart(); + + // views! + foreach (KateView *v,m_views) + { + v->config()->configStart(); + v->renderer()->config()->configStart(); + } + // read a number of lines in the top/bottom of the document + for (int i=0; i < qMin( 9, lines() ); ++i ) + { + readVariableLine( line( i ), onlyViewAndRenderer ); + } + if ( lines() > 10 ) + { + for ( int i = qMax( 10, lines() - 10); i < lines(); i++ ) + { + readVariableLine( line( i ), onlyViewAndRenderer ); + } + } + + if (!onlyViewAndRenderer) + m_config->configEnd(); + + foreach (KateView *v,m_views) + { + v->config()->configEnd(); + v->renderer()->config()->configEnd(); + } +} + +void KateDocument::readVariableLine( QString t, bool onlyViewAndRenderer ) +{ + // simple check first, no regex + // no kate inside, no vars, simple... + if (!t.contains("kate")) + return; + + // found vars, if any + QString s; + + // now, try first the normal ones + if ( kvLine.indexIn( t ) > -1 ) + { + s = kvLine.cap(1); + + kDebug (13020) << "normal variable line kate: matched: " << s; + } + else if (kvLineWildcard.indexIn( t ) > -1) // regex given + { + const QStringList wildcards (kvLineWildcard.cap(1).split (';', QString::SkipEmptyParts)); + const QString nameOfFile = url().fileName(); + + bool found = false; + foreach(const QString& pattern, wildcards) + { + QRegExp wildcard (pattern, Qt::CaseSensitive, QRegExp::Wildcard); + + found = wildcard.exactMatch (nameOfFile); + } + + // nothing usable found... + if (!found) + return; + + s = kvLineWildcard.cap(2); + + kDebug (13020) << "guarded variable line kate-wildcard: matched: " << s; + } + else if (kvLineMime.indexIn( t ) > -1) // mime-type given + { + const QStringList types (kvLineMime.cap(1).split (';', QString::SkipEmptyParts)); + + // no matching type found + if (!types.contains (mimeType ())) + return; + + s = kvLineMime.cap(2); + + kDebug (13020) << "guarded variable line kate-mimetype: matched: " << s; + } + else // nothing found + { + return; + } + + QStringList vvl; // view variable names + vvl << "dynamic-word-wrap" << "dynamic-word-wrap-indicators" + << "line-numbers" << "icon-border" << "folding-markers" + << "bookmark-sorting" << "auto-center-lines" + << "icon-bar-color" + // renderer + << "background-color" << "selection-color" + << "current-line-color" << "bracket-highlight-color" + << "word-wrap-marker-color" + << "font" << "font-size" << "scheme"; + int spaceIndent = -1; // for backward compatibility; see below + bool replaceTabsSet = false; + int p( 0 ); + + QString var, val; + while ( (p = kvVar.indexIn( s, p )) > -1 ) + { + p += kvVar.matchedLength(); + var = kvVar.cap( 1 ); + val = kvVar.cap( 2 ).trimmed(); + bool state; // store booleans here + int n; // store ints here + + // only apply view & renderer config stuff + if (onlyViewAndRenderer) + { + if ( vvl.contains( var ) ) // FIXME define above + setViewVariable( var, val ); + } + else + { + // BOOL SETTINGS + if ( var == "word-wrap" && checkBoolValue( val, &state ) ) + setWordWrap( state ); // ??? FIXME CHECK + // KateConfig::configFlags + // FIXME should this be optimized to only a few calls? how? + else if ( var == "backspace-indents" && checkBoolValue( val, &state ) ) + m_config->setBackspaceIndents( state ); + else if ( var == "indent-pasted-text" && checkBoolValue( val, &state ) ) + m_config->setIndentPastedText( state ); + else if ( var == "replace-tabs" && checkBoolValue( val, &state ) ) + { + m_config->setReplaceTabsDyn( state ); + replaceTabsSet = true; // for backward compatibility; see below + } + else if ( var == "remove-trailing-space" && checkBoolValue( val, &state ) ) { + kWarning() << i18n("Using deprecated modeline 'remove-trailing-space'. " + "Please replace with 'remove-trailing-spaces modified;', see " + "http://docs.kde.org/stable/en/applications/kate/config-variables.html#variable-remove-trailing-spaces"); + m_config->setRemoveSpaces( state ? 1 : 0 ); + } + else if ( var == "replace-trailing-space-save" && checkBoolValue( val, &state ) ) { + kWarning() << i18n("Using deprecated modeline 'replace-trailing-space-save'. " + "Please replace with 'remove-trailing-spaces all;', see " + "http://docs.kde.org/stable/en/applications/kate/config-variables.html#variable-remove-trailing-spaces"); + m_config->setRemoveSpaces( state ? 2 : 0 ); + } + else if ( var == "overwrite-mode" && checkBoolValue( val, &state ) ) + m_config->setOvr( state ); + else if ( var == "keep-extra-spaces" && checkBoolValue( val, &state ) ) + m_config->setKeepExtraSpaces( state ); + else if ( var == "tab-indents" && checkBoolValue( val, &state ) ) + m_config->setTabIndents( state ); + else if ( var == "show-tabs" && checkBoolValue( val, &state ) ) + m_config->setShowTabs( state ); + else if ( var == "show-trailing-spaces" && checkBoolValue( val, &state ) ) + m_config->setShowSpaces( state ); + else if ( var == "space-indent" && checkBoolValue( val, &state ) ) + { + // this is for backward compatibility; see below + spaceIndent = state; + } + else if ( var == "smart-home" && checkBoolValue( val, &state ) ) + m_config->setSmartHome( state ); + else if ( var == "newline-at-eof" && checkBoolValue( val, &state ) ) + m_config->setNewLineAtEof( state ); + + // INTEGER SETTINGS + else if ( var == "tab-width" && checkIntValue( val, &n ) ) + m_config->setTabWidth( n ); + else if ( var == "indent-width" && checkIntValue( val, &n ) ) + m_config->setIndentationWidth( n ); + else if ( var == "indent-mode" ) + { + m_config->setIndentationMode( val ); + } + else if ( var == "word-wrap-column" && checkIntValue( val, &n ) && n > 0 ) // uint, but hard word wrap at 0 will be no fun ;) + m_config->setWordWrapAt( n ); + + // STRING SETTINGS + else if ( var == "eol" || var == "end-of-line" ) + { + QStringList l; + l << "unix" << "dos" << "mac"; + if ( (n = l.indexOf( val.toLower() )) != -1 ) + m_config->setEol( n ); + } + else if (var == "bom" || var =="byte-order-marker") + { + if (checkBoolValue(val,&state)) { + m_config->setBom(state); + } + } + else if ( var == "remove-trailing-spaces" ) { + val = val.toLower(); + if (val == "1" || val == "modified" || val == "mod" || val == "+") { + m_config->setRemoveSpaces(1); + } else if (val == "2" || val == "all" || val == "*") { + m_config->setRemoveSpaces(2); + } else { + m_config->setRemoveSpaces(0); + } + } + else if ( var == "syntax" || var == "hl" ) + { + setHighlightingMode( val ); + } + else if ( var == "mode" ) + { + setMode( val ); + } + else if ( var == "encoding" ) + { + setEncoding( val ); + } + else if ( var == "default-dictionary" ) + { + setDefaultDictionary( val ); + } + else if ( var == "automatic-spell-checking" && checkBoolValue( val, &state ) ) + { + onTheFlySpellCheckingEnabled( state ); + } + + // VIEW SETTINGS + else if ( vvl.contains( var ) ) + setViewVariable( var, val ); + else + { + m_storedVariables.insert( var, val ); + emit variableChanged( this, var, val ); + } + } + } + + // Backward compatibility + // If space-indent was set, but replace-tabs was not set, we assume + // that the user wants to replace tabulators and set that flag. + // If both were set, replace-tabs has precedence. + // At this point spaceIndent is -1 if it was never set, + // 0 if it was set to off, and 1 if it was set to on. + // Note that if onlyViewAndRenderer was requested, spaceIndent is -1. + if ( !replaceTabsSet && spaceIndent >= 0 ) + { + m_config->setReplaceTabsDyn( spaceIndent > 0 ); + } +} + +void KateDocument::setViewVariable( QString var, QString val ) +{ + bool state; + int n; + QColor c; + foreach (KateView *v,m_views) + { + if ( var == "dynamic-word-wrap" && checkBoolValue( val, &state ) ) + v->config()->setDynWordWrap( state ); + else if ( var == "persistent-selection" && checkBoolValue( val, &state ) ) + v->config()->setPersistentSelection( state ); + else if ( var == "block-selection" && checkBoolValue( val, &state ) ) + v->setBlockSelection( state ); + //else if ( var = "dynamic-word-wrap-indicators" ) + else if ( var == "line-numbers" && checkBoolValue( val, &state ) ) + v->config()->setLineNumbers( state ); + else if (var == "icon-border" && checkBoolValue( val, &state ) ) + v->config()->setIconBar( state ); + else if (var == "folding-markers" && checkBoolValue( val, &state ) ) + v->config()->setFoldingBar( state ); + else if ( var == "auto-center-lines" && checkIntValue( val, &n ) ) + v->config()->setAutoCenterLines( n ); + else if ( var == "icon-bar-color" && checkColorValue( val, c ) ) + v->renderer()->config()->setIconBarColor( c ); + // RENDERER + else if ( var == "background-color" && checkColorValue( val, c ) ) + v->renderer()->config()->setBackgroundColor( c ); + else if ( var == "selection-color" && checkColorValue( val, c ) ) + v->renderer()->config()->setSelectionColor( c ); + else if ( var == "current-line-color" && checkColorValue( val, c ) ) + v->renderer()->config()->setHighlightedLineColor( c ); + else if ( var == "bracket-highlight-color" && checkColorValue( val, c ) ) + v->renderer()->config()->setHighlightedBracketColor( c ); + else if ( var == "word-wrap-marker-color" && checkColorValue( val, c ) ) + v->renderer()->config()->setWordWrapMarkerColor( c ); + else if ( var == "font" || ( var == "font-size" && checkIntValue( val, &n ) ) ) + { + QFont _f( v->renderer()->config()->font() ); + + if ( var == "font" ) + { + _f.setFamily( val ); + _f.setFixedPitch( QFont( val ).fixedPitch() ); + } + else + _f.setPointSize( n ); + + v->renderer()->config()->setFont( _f ); + } + else if ( var == "scheme" ) + { + v->renderer()->config()->setSchema( val ); + } + } +} + +bool KateDocument::checkBoolValue( QString val, bool *result ) +{ + val = val.trimmed().toLower(); + QStringList l; + l << "1" << "on" << "true"; + if ( l.contains( val ) ) + { + *result = true; + return true; + } + l.clear(); + l << "0" << "off" << "false"; + if ( l.contains( val ) ) + { + *result = false; + return true; + } + return false; +} + +bool KateDocument::checkIntValue( QString val, int *result ) +{ + bool ret( false ); + *result = val.toInt( &ret ); + return ret; +} + +bool KateDocument::checkColorValue( QString val, QColor &c ) +{ + c.setNamedColor( val ); + return c.isValid(); +} + +// KTextEditor::variable +QString KateDocument::variable( const QString &name ) const +{ + return m_storedVariables.value(name, QString()); +} + +QString KateDocument::setVariable( const QString &name, const QString &value) +{ + QString s = "kate: "; + s.append(name); + s.append(" "); + s.append(value); + readVariableLine(s); + return m_storedVariables.value(name, QString()); +} + +//END + +void KateDocument::slotModOnHdDirty (const QString &path) +{ + if ((path == m_dirWatchFile) && (!m_modOnHd || m_modOnHdReason != OnDiskModified)) + { + // compare digest with the one we have (if we have one) + if ( !digest().isEmpty() ) + { + QByteArray oldDigest = digest(); + if ( createDigest () && oldDigest == digest() ) { + return; + } + } + + m_modOnHd = true; + m_modOnHdReason = OnDiskModified; + + // reenable dialog if not running atm + if (m_isasking == -1) + m_isasking = false; + + emit modifiedOnDisk (this, m_modOnHd, m_modOnHdReason); + } +} + +void KateDocument::slotModOnHdCreated (const QString &path) +{ + if ((path == m_dirWatchFile) && (!m_modOnHd || m_modOnHdReason != OnDiskCreated)) + { + m_modOnHd = true; + m_modOnHdReason = OnDiskCreated; + + // reenable dialog if not running atm + if (m_isasking == -1) + m_isasking = false; + + emit modifiedOnDisk (this, m_modOnHd, m_modOnHdReason); + } +} + +void KateDocument::slotModOnHdDeleted (const QString &path) +{ + if ((path == m_dirWatchFile) && (!m_modOnHd || m_modOnHdReason != OnDiskDeleted)) + { + m_modOnHd = true; + m_modOnHdReason = OnDiskDeleted; + + // reenable dialog if not running atm + if (m_isasking == -1) + m_isasking = false; + + emit modifiedOnDisk (this, m_modOnHd, m_modOnHdReason); + } +} + +const QByteArray &KateDocument::digest () const +{ + return m_buffer->digest(); +} + +bool KateDocument::createDigest () +{ + QByteArray sha1sum; + + if ( url().isLocalFile() ) + { + QFile f ( url().toLocalFile() ); + if ( f.open( QIODevice::ReadOnly) ) + { + QCryptographicHash crypto(KATE_HASH_ALGORITHM); + crypto.addData(&f); + sha1sum = crypto.result(); + } + } + + m_buffer->setDigest( sha1sum ); + + return !sha1sum.isEmpty(); +} + +QString KateDocument::reasonedMOHString() const +{ + // squeeze path + QString str = KStringHandler::csqueeze(url().pathOrUrl()); + + switch( m_modOnHdReason ) + { + case OnDiskModified: + return i18n("The file '%1' was modified by another program.", str ); + break; + case OnDiskCreated: + return i18n("The file '%1' was created by another program.", str ); + break; + case OnDiskDeleted: + return i18n("The file '%1' was deleted by another program.", str ); + break; + default: + return QString(); + } + return QString(); +} + +void KateDocument::removeTrailingSpaces() +{ + const int remove = config()->removeSpaces(); + if (remove == 0) + return; + + // temporarily disable static word wrap (see bug #328900) + const bool wordWrapEnabled = config()->wordWrap (); + if (wordWrapEnabled) setWordWrap(false); + + // get cursor position of active view + KTextEditor::Cursor curPos = KTextEditor::Cursor::invalid(); + if (activeView()) { + curPos = activeView()->cursorPosition(); + } + + editStart(); + + for (int line = 0; line < lines(); ++line) + { + Kate::TextLine textline = plainKateTextLine(line); + + // remove trailing spaces in entire document, remove = 2 + // remove trailing spaces of touched lines, remove = 1 + // remove trailing spaces of lines saved on disk, remove = 1 + if (remove == 2 || textline->markedAsModified() || textline->markedAsSavedOnDisk()) { + const int p = textline->lastChar() + 1; + const int l = textline->length() - p; + if (l > 0 ) { + // if the cursor is in the trailing space, only delete behind cursor + if (curPos.line() != line || curPos.column() <= p || curPos.column() > p + l) { + editRemoveText(line, p, l); + } else { + editRemoveText(line, curPos.column(), l - (curPos.column() - p)); + } + } + } + } + + editEnd(); + + // enable word wrap again, if it was enabled (see bug #328900) + if (wordWrapEnabled) setWordWrap(true); // see begin of this function +} + + +void KateDocument::updateFileType (const QString &newType, bool user) +{ + if (user || !m_fileTypeSetByUser) + { + if (!newType.isEmpty()) + { + // remember that we got set by user + m_fileTypeSetByUser = user; + + m_fileType = newType; + + m_config->configStart(); + + if (!m_hlSetByUser && !KateGlobal::self()->modeManager()->fileType(newType).hl.isEmpty()) + { + int hl (KateHlManager::self()->nameFind (KateGlobal::self()->modeManager()->fileType(newType).hl)); + + if (hl >= 0) + m_buffer->setHighlight(hl); + } + + /** + * set the indentation mode, if any in the mode... + * and user did not set it before! + */ + if (!m_indenterSetByUser && !KateGlobal::self()->modeManager()->fileType(newType).indenter.isEmpty()) + config()->setIndentationMode (KateGlobal::self()->modeManager()->fileType(newType).indenter); + + // views! + foreach (KateView *v,m_views) + { + v->config()->configStart(); + v->renderer()->config()->configStart(); + } + + bool bom_settings = false; + if (m_bomSetByUser) + bom_settings=m_config->bom(); + readVariableLine( KateGlobal::self()->modeManager()->fileType(newType).varLine ); + if (m_bomSetByUser) + m_config->setBom(bom_settings); + m_config->configEnd(); + foreach (KateView *v,m_views) + { + v->config()->configEnd(); + v->renderer()->config()->configEnd(); + } + } + } + + // fixme, make this better... + emit modeChanged (this); +} + +void KateDocument::slotQueryClose_save(bool *handled, bool* abortClosing) { + *handled=true; + *abortClosing=true; + if (this->url().isEmpty()) + { + QWidget *parentWidget(dialogParent()); + + KEncodingFileDialog::Result res=KEncodingFileDialog::getSaveUrlAndEncoding(config()->encoding(), + QString(),QString(),parentWidget,i18n("Save File")); + + if( res.URLs.isEmpty() || !checkOverwrite( res.URLs.first(), parentWidget ) ) { + *abortClosing=true; + return; + } + setEncoding( res.encoding ); + saveAs( res.URLs.first() ); + *abortClosing=false; + } + else + { + save(); + *abortClosing=false; + } + +} + +bool KateDocument::checkOverwrite( KUrl u, QWidget *parent ) +{ + if( !u.isLocalFile() ) + return true; + + QFileInfo info( u.path() ); + if( !info.exists() ) + return true; + + return KMessageBox::Cancel != KMessageBox::warningContinueCancel( parent, + i18n( "A file named \"%1\" already exists. " + "Are you sure you want to overwrite it?" , info.fileName() ), + i18n( "Overwrite File?" ), KStandardGuiItem::overwrite(), + KStandardGuiItem::cancel(), QString(), KMessageBox::Notify | KMessageBox::Dangerous ); +} + +//BEGIN KTextEditor::ConfigInterface + +// BEGIN ConfigInterface stff +QStringList KateDocument::configKeys() const +{ + return QStringList() << "tab-width" << "indent-width"; +} + +QVariant KateDocument::configValue(const QString &key) +{ + if (key == "backup-on-save-local") { + return m_config->backupFlags() & KateDocumentConfig::LocalFiles; + } else if (key == "backup-on-save-remote") { + return m_config->backupFlags() & KateDocumentConfig::RemoteFiles; + } else if (key == "backup-on-save-suffix") { + return m_config->backupSuffix(); + } else if (key == "backup-on-save-prefix") { + return m_config->backupPrefix(); + } else if (key == "replace-tabs") { + return m_config->replaceTabsDyn(); + } else if (key == "indent-pasted-text") { + return m_config->indentPastedText(); + } else if (key == "tab-width") { + return m_config->tabWidth(); + } else if (key == "indent-width") { + return m_config->indentationWidth(); + } + + // return invalid variant + return QVariant(); +} + +void KateDocument::setConfigValue(const QString &key, const QVariant &value) +{ + if (value.type() == QVariant::String) { + if (key == "backup-on-save-suffix") { + m_config->setBackupSuffix(value.toString()); + } else if (key == "backup-on-save-prefix") { + m_config->setBackupPrefix(value.toString()); + } + } else if (value.canConvert(QVariant::Bool)) { + const bool bValue = value.toBool(); + if (key == "backup-on-save-local" && value.type() == QVariant::String) { + uint f = m_config->backupFlags(); + if (bValue) { + f |= KateDocumentConfig::LocalFiles; + } else { + f ^= KateDocumentConfig::LocalFiles; + } + + m_config->setBackupFlags(f); + } else if (key == "backup-on-save-remote") { + uint f = m_config->backupFlags(); + if (bValue) { + f |= KateDocumentConfig::RemoteFiles; + } else { + f ^= KateDocumentConfig::RemoteFiles; + } + + m_config->setBackupFlags(f); + } else if (key == "replace-tabs") { + m_config->setReplaceTabsDyn(bValue); + } else if (key == "indent-pasted-text") { + m_config->setIndentPastedText(bValue); + } + } else if (value.canConvert(QVariant::Int)) { + if (key == "tab-width") { + config()->setTabWidth(value.toInt()); + } else if (key == "indent-width") { + config()->setIndentationWidth(value.toInt()); + } + } +} + +//END KTextEditor::ConfigInterface + +KateView * KateDocument::activeKateView( ) const +{ + return static_cast(m_activeView); +} + +KTextEditor::Cursor KateDocument::documentEnd( ) const +{ + return KTextEditor::Cursor(lastLine(), lineLength(lastLine())); +} + +bool KateDocument::replaceText( const KTextEditor::Range & range, const QString & s, bool block ) +{ + // TODO more efficient? + editStart(); + bool changed = removeText(range, block); + changed |= insertText(range.start(), s, block); + editEnd(); + return changed; +} + +void KateDocument::ignoreModifiedOnDiskOnce( ) +{ + m_isasking = -1; +} + +KateHighlighting * KateDocument::highlight( ) const +{ + return m_buffer->highlight(); +} + +Kate::TextLine KateDocument::kateTextLine( uint i ) +{ + m_buffer->ensureHighlighted (i); + return m_buffer->plainLine (i); +} + +Kate::TextLine KateDocument::plainKateTextLine( uint i ) +{ + return m_buffer->plainLine (i); +} + +bool KateDocument::isEditRunning() const +{ + return editIsRunning; +} + +void KateDocument::setUndoMergeAllEdits(bool merge) +{ + if (merge && m_undoMergeAllEdits) + { + // Don't add another undo safe point: it will override our current one, + // meaning we'll need two undo's to get back there - which defeats the object! + return; + } + m_undoManager->undoSafePoint(); + m_undoManager->setAllowComplexMerge(merge); + m_undoMergeAllEdits = merge; +} + +//BEGIN KTextEditor::MovingInterface +KTextEditor::MovingCursor *KateDocument::newMovingCursor (const KTextEditor::Cursor &position, KTextEditor::MovingCursor::InsertBehavior insertBehavior) +{ + return new Kate::TextCursor(buffer(), position, insertBehavior); +} + +KTextEditor::MovingRange *KateDocument::newMovingRange (const KTextEditor::Range &range, KTextEditor::MovingRange::InsertBehaviors insertBehaviors, KTextEditor::MovingRange::EmptyBehavior emptyBehavior) +{ + return new Kate::TextRange(buffer(), range, insertBehaviors, emptyBehavior); +} + +qint64 KateDocument::revision () const +{ + return m_buffer->history().revision (); +} + +qint64 KateDocument::lastSavedRevision () const +{ + return m_buffer->history().lastSavedRevision (); +} + +void KateDocument::lockRevision (qint64 revision) +{ + m_buffer->history().lockRevision (revision); +} + +void KateDocument::unlockRevision (qint64 revision) +{ + m_buffer->history().unlockRevision (revision); +} + +void KateDocument::transformCursor(int& line, int& column, KTextEditor::MovingCursor::InsertBehavior insertBehavior, qint64 fromRevision, qint64 toRevision) +{ + m_buffer->history().transformCursor (line, column, insertBehavior, fromRevision, toRevision); +} + +void KateDocument::transformCursor (KTextEditor::Cursor &cursor, KTextEditor::MovingCursor::InsertBehavior insertBehavior, qint64 fromRevision, qint64 toRevision) +{ + int line = cursor.line(), column = cursor.column(); + m_buffer->history().transformCursor (line, column, insertBehavior, fromRevision, toRevision); + cursor.setLine(line); + cursor.setColumn(column); +} + +void KateDocument::transformRange (KTextEditor::Range &range, KTextEditor::MovingRange::InsertBehaviors insertBehaviors, KTextEditor::MovingRange::EmptyBehavior emptyBehavior, qint64 fromRevision, qint64 toRevision) +{ + m_buffer->history().transformRange (range, insertBehaviors, emptyBehavior, fromRevision, toRevision); +} + +//END + +bool KateDocument::simpleMode () +{ + return KateGlobal::self()->simpleMode () && KateGlobal::self()->documentConfig()->allowSimpleMode (); +} + +//BEGIN KTextEditor::AnnotationInterface +void KateDocument::setAnnotationModel( KTextEditor::AnnotationModel* model ) +{ + KTextEditor::AnnotationModel* oldmodel = m_annotationModel; + m_annotationModel = model; + emit annotationModelChanged(oldmodel, m_annotationModel); +} + +KTextEditor::AnnotationModel* KateDocument::annotationModel() const +{ + return m_annotationModel; +} +//END KTextEditor::AnnotationInterface + +//TAKEN FROM kparts.h +bool KateDocument::queryClose() +{ + if ( !isReadWrite() || !isModified() ) + return true; + + QString docName = documentName(); + + int res = KMessageBox::warningYesNoCancel( dialogParent(), + i18n( "The document \"%1\" has been modified.\n" + "Do you want to save your changes or discard them?" , docName ), + i18n( "Close Document" ), KStandardGuiItem::save(), KStandardGuiItem::discard() ); + + bool abortClose=false; + bool handled=false; + + switch(res) { + case KMessageBox::Yes : + sigQueryClose(&handled,&abortClose); + if (!handled) + { + if (url().isEmpty()) + { + KUrl url = KFileDialog::getSaveUrl(KUrl(), QString(), dialogParent()); + if (url.isEmpty()) + return false; + + saveAs( url ); + } + else + { + save(); + } + } else if (abortClose) return false; + return waitSaveComplete(); + case KMessageBox::No : + return true; + default : // case KMessageBox::Cancel : + return false; + } +} + +void KateDocument::slotStarted (KIO::Job *job) +{ + /** + * if we are idle before, we are now loading! + */ + if (m_documentState == DocumentIdle) + m_documentState = DocumentLoading; + + /** + * if loading: + * - remember pre loading read-write mode + * if remote load: + * - set to read-only + * - trigger possible message + */ + if (m_documentState == DocumentLoading) { + /** + * remember state + */ + m_readWriteStateBeforeLoading = isReadWrite (); + + /** + * perhaps show loading message, but wait one second + */ + if (job) { + /** + * only read only if really remote file! + */ + setReadWrite (false); + + /** + * perhaps some message about loading in one second! + * remember job pointer, we want to be able to kill it! + */ + m_loadingJob = job; + QTimer::singleShot (1000, this, SLOT(slotTriggerLoadingMessage())); + } + } +} + +void KateDocument::slotCompleted() { + /** + * if were loading, reset back to old read-write mode before loading + * and kill the possible loading message + */ + if (m_documentState == DocumentLoading) { + setReadWrite (m_readWriteStateBeforeLoading); + delete m_loadingMessage; + } + + /** + * Emit signal that we saved the document, if needed + */ + if (m_documentState == DocumentSaving || m_documentState == DocumentSavingAs) + emit documentSavedOrUploaded (this, m_documentState == DocumentSavingAs); + + /** + * back to idle mode + */ + m_documentState = DocumentIdle; + m_reloading = false; +} + +void KateDocument::slotCanceled() { + /** + * if were loading, reset back to old read-write mode before loading + * and kill the possible loading message + */ + if (m_documentState == DocumentLoading) { + setReadWrite (m_readWriteStateBeforeLoading); + delete m_loadingMessage; + + showAndSetOpeningErrorAccess(); + + updateDocName(); + } + + /** + * back to idle mode + */ + m_documentState = DocumentIdle; + m_reloading = false; + + +} + +void KateDocument::slotTriggerLoadingMessage () +{ + /** + * no longer loading? + * no message needed! + */ + if (m_documentState != DocumentLoading) + return; + + /** + * create message about file loading in progress + */ + delete m_loadingMessage; + m_loadingMessage = new KTextEditor::Message(i18n ("The file
%2 is still loading.", url().pathOrUrl(), url().fileName())); + m_loadingMessage->setPosition(KTextEditor::Message::TopInView); + + /** + * if around job: add cancel action + */ + if (m_loadingJob) { + QAction *cancel = new QAction ( i18n ("&Abort Loading"), 0); + connect (cancel, SIGNAL(triggered()), this, SLOT(slotAbortLoading())); + m_loadingMessage->addAction (cancel); + } + + /** + * really post message + */ + postMessage(m_loadingMessage); +} + +void KateDocument::slotAbortLoading () +{ + /** + * no job, no work + */ + if (!m_loadingJob) + return; + + /** + * abort loading if any job + * signal results! + */ + m_loadingJob->kill (KJob::EmitResult); + m_loadingJob = 0; +} + +bool KateDocument::save() +{ + /** + * no double save/load + * we need to allow DocumentPreSavingAs here as state, as save is called in saveAs! + */ + if ((m_documentState != DocumentIdle) && (m_documentState != DocumentPreSavingAs)) + return false; + + /** + * if we are idle, we are now saving + */ + if (m_documentState == DocumentIdle) + m_documentState = DocumentSaving; + else + m_documentState = DocumentSavingAs; + + /** + * call back implementation for real work + */ + return KTextEditor::Document::save(); +} + +bool KateDocument::saveAs( const KUrl &url ) +{ + /** + * abort on bad URL + * that is done in saveAs below, too + * but we must check it here already to avoid messing up + * as no signals will be send, then + */ + if (!url.isValid()) + return false; + + /** + * no double save/load + */ + if (m_documentState != DocumentIdle) + return false; + + /** + * we enter the pre save as phase + */ + m_documentState = DocumentPreSavingAs; + + /** + * call base implementation for real work + */ + return KTextEditor::Document::saveAs(url); +} + +QString KateDocument::defaultDictionary() const +{ + return m_defaultDictionary; +} + +QList > KateDocument::dictionaryRanges() const +{ + return m_dictionaryRanges; +} + +void KateDocument::clearDictionaryRanges() +{ + for (QList >::iterator i = m_dictionaryRanges.begin(); + i != m_dictionaryRanges.end(); ++i) + { + delete (*i).first; + } + m_dictionaryRanges.clear(); + if (m_onTheFlyChecker) { + m_onTheFlyChecker->refreshSpellCheck(); + } + emit dictionaryRangesPresent(false); +} + +void KateDocument::setDictionary(const QString& newDictionary, const KTextEditor::Range &range) +{ + KTextEditor::Range newDictionaryRange = range; + if (!newDictionaryRange.isValid() || newDictionaryRange.isEmpty()) { + return; + } + QList > newRanges; + // all ranges is 'm_dictionaryRanges' are assumed to be mutually disjoint + for(QList >::iterator i = m_dictionaryRanges.begin(); + i != m_dictionaryRanges.end();) + { + kDebug(13000) << "new iteration" << newDictionaryRange; + if (newDictionaryRange.isEmpty()) { + break; + } + QPair pair = *i; + QString dictionarySet = pair.second; + KTextEditor::MovingRange *dictionaryRange = pair.first; + kDebug(13000) << *dictionaryRange << dictionarySet; + if(dictionaryRange->contains(newDictionaryRange) && newDictionary == dictionarySet) + { + kDebug(13000) << "dictionaryRange contains newDictionaryRange"; + return; + } + if(newDictionaryRange.contains(*dictionaryRange)) + { + delete dictionaryRange; + i = m_dictionaryRanges.erase(i); + kDebug(13000) << "newDictionaryRange contains dictionaryRange"; + continue; + } + + KTextEditor::Range intersection = dictionaryRange->toRange().intersect(newDictionaryRange); + if(!intersection.isEmpty() && intersection.isValid()) + { + if(dictionarySet == newDictionary) // we don't have to do anything for 'intersection' + { // except cut off the intersection + QList remainingRanges = KateSpellCheckManager::rangeDifference(newDictionaryRange, intersection); + Q_ASSERT(remainingRanges.size() == 1); + newDictionaryRange = remainingRanges.first(); + ++i; + kDebug(13000) << "dictionarySet == newDictionary"; + continue; + } + QList remainingRanges = KateSpellCheckManager::rangeDifference(*dictionaryRange, intersection); + for(QList::iterator j = remainingRanges.begin(); j != remainingRanges.end(); ++j) + { + KTextEditor::MovingRange *remainingRange = newMovingRange(*j, + KTextEditor::MovingRange::ExpandLeft | KTextEditor::MovingRange::ExpandRight); + remainingRange->setFeedback(this); + newRanges.push_back(QPair(remainingRange, dictionarySet)); + } + i = m_dictionaryRanges.erase(i); + delete dictionaryRange; + } else { + ++i; + } + } + m_dictionaryRanges += newRanges; + if(!newDictionaryRange.isEmpty() && !newDictionary.isEmpty()) // we don't add anything for the default dictionary + { + KTextEditor::MovingRange *newDictionaryMovingRange = newMovingRange(newDictionaryRange, + KTextEditor::MovingRange::ExpandLeft | KTextEditor::MovingRange::ExpandRight); + newDictionaryMovingRange->setFeedback(this); + m_dictionaryRanges.push_back(QPair(newDictionaryMovingRange, newDictionary)); + } + if(m_onTheFlyChecker && !newDictionaryRange.isEmpty()) + { + m_onTheFlyChecker->refreshSpellCheck(newDictionaryRange); + } + emit dictionaryRangesPresent(!m_dictionaryRanges.isEmpty()); +} + +void KateDocument::revertToDefaultDictionary(const KTextEditor::Range &range) +{ + setDictionary(QString(), range); +} + +void KateDocument::setDefaultDictionary(const QString& dict) +{ + if (m_defaultDictionary == dict) { + return; + } + + m_defaultDictionary = dict; + + if (m_onTheFlyChecker) { + m_onTheFlyChecker->updateConfig(); + refreshOnTheFlyCheck(); + } + emit defaultDictionaryChanged(this); +} + +void KateDocument::onTheFlySpellCheckingEnabled(bool enable) +{ + if (isOnTheFlySpellCheckingEnabled() == enable) { + return; + } + + if (enable) { + Q_ASSERT(m_onTheFlyChecker == 0); + m_onTheFlyChecker = new KateOnTheFlyChecker(this); + } else { + delete m_onTheFlyChecker; + m_onTheFlyChecker = 0; + } + + foreach (KateView *view, m_views) { + view->reflectOnTheFlySpellCheckStatus(enable); + } +} + +bool KateDocument::isOnTheFlySpellCheckingEnabled() const +{ + return m_onTheFlyChecker != 0; +} + +QString KateDocument::dictionaryForMisspelledRange(const KTextEditor::Range& range) const +{ + if (!m_onTheFlyChecker) { + return QString(); + } else { + return m_onTheFlyChecker->dictionaryForMisspelledRange(range); + } +} + +void KateDocument::clearMisspellingForWord(const QString& word) +{ + if (m_onTheFlyChecker) { + m_onTheFlyChecker->clearMisspellingForWord(word); + } +} + +void KateDocument::refreshOnTheFlyCheck(const KTextEditor::Range &range) +{ + if (m_onTheFlyChecker) { + m_onTheFlyChecker->refreshSpellCheck(range); + } +} + +void KateDocument::rangeInvalid(KTextEditor::MovingRange *movingRange) +{ + deleteDictionaryRange(movingRange); +} + +void KateDocument::rangeEmpty(KTextEditor::MovingRange *movingRange) +{ + deleteDictionaryRange(movingRange); +} + +void KateDocument::deleteDictionaryRange(KTextEditor::MovingRange *movingRange) +{ + kDebug(13020) << "deleting" << movingRange; + for(QList >::iterator i = m_dictionaryRanges.begin(); + i != m_dictionaryRanges.end();) + { + KTextEditor::MovingRange *dictionaryRange = (*i).first; + if(dictionaryRange == movingRange) + { + delete movingRange; + i = m_dictionaryRanges.erase(i); + } else { + ++i; + } + } +} + +bool KateDocument::containsCharacterEncoding(const KTextEditor::Range& range) +{ + KateHighlighting *highlighting = highlight(); + Kate::TextLine textLine; + + const int rangeStartLine = range.start().line(); + const int rangeStartColumn = range.start().column(); + const int rangeEndLine = range.end().line(); + const int rangeEndColumn = range.end().column(); + + for(int line = range.start().line(); line <= rangeEndLine; ++line) { + textLine = kateTextLine(line); + int startColumn = (line == rangeStartLine) ? rangeStartColumn : 0; + int endColumn = (line == rangeEndLine) ? rangeEndColumn : textLine->length(); + for(int col = startColumn; col < endColumn; ++col) { + int attr = textLine->attribute(col); + const KatePrefixStore& prefixStore = highlighting->getCharacterEncodingsPrefixStore(attr); + if(!prefixStore.findPrefix(textLine, col).isEmpty()) { + return true; + } + } + } + + return false; +} + +int KateDocument::computePositionWrtOffsets(const OffsetList& offsetList, int pos) +{ + int previousOffset = 0; + for(OffsetList::const_iterator i = offsetList.begin(); i != offsetList.end(); ++i) { + if((*i).first > pos) { + break; + } + previousOffset = (*i).second; + } + return pos + previousOffset; +} + +QString KateDocument::decodeCharacters(const KTextEditor::Range& range, KateDocument::OffsetList& decToEncOffsetList, + KateDocument::OffsetList& encToDecOffsetList) +{ + QString toReturn; + KTextEditor::Cursor previous = range.start(); + int decToEncCurrentOffset = 0, encToDecCurrentOffset = 0; + int i = 0; + int newI = 0; + + KateHighlighting *highlighting = highlight(); + Kate::TextLine textLine; + + const int rangeStartLine = range.start().line(); + const int rangeStartColumn = range.start().column(); + const int rangeEndLine = range.end().line(); + const int rangeEndColumn = range.end().column(); + + for(int line = range.start().line(); line <= rangeEndLine; ++line) { + textLine = kateTextLine(line); + int startColumn = (line == rangeStartLine) ? rangeStartColumn : 0; + int endColumn = (line == rangeEndLine) ? rangeEndColumn : textLine->length(); + for(int col = startColumn; col < endColumn;) { + int attr = textLine->attribute(col); + const KatePrefixStore& prefixStore = highlighting->getCharacterEncodingsPrefixStore(attr); + const QHash& characterEncodingsHash = highlighting->getCharacterEncodings(attr); + QString matchingPrefix = prefixStore.findPrefix(textLine, col); + if(!matchingPrefix.isEmpty()) { + toReturn += text(KTextEditor::Range(previous, KTextEditor::Cursor(line, col))); + const QChar& c = characterEncodingsHash.value(matchingPrefix); + const bool isNullChar = c.isNull(); + if(!c.isNull()) { + toReturn += c; + } + i += matchingPrefix.length(); + col += matchingPrefix.length(); + previous = KTextEditor::Cursor(line, col); + decToEncCurrentOffset = decToEncCurrentOffset - (isNullChar ? 0 : 1) + matchingPrefix.length(); + encToDecCurrentOffset = encToDecCurrentOffset - matchingPrefix.length() + (isNullChar ? 0 : 1); + newI += (isNullChar ? 0 : 1); + decToEncOffsetList.push_back(QPair(newI, decToEncCurrentOffset)); + encToDecOffsetList.push_back(QPair(i, encToDecCurrentOffset)); + continue; + } + ++col; + ++i; + ++newI; + } + ++i; + ++newI; + } + if(previous < range.end()) { + toReturn += text(KTextEditor::Range(previous, range.end())); + } + return toReturn; +} + +void KateDocument::replaceCharactersByEncoding(const KTextEditor::Range& range) +{ + KateHighlighting *highlighting = highlight(); + Kate::TextLine textLine; + + const int rangeStartLine = range.start().line(); + const int rangeStartColumn = range.start().column(); + const int rangeEndLine = range.end().line(); + const int rangeEndColumn = range.end().column(); + + for(int line = range.start().line(); line <= rangeEndLine; ++line) { + textLine = kateTextLine(line); + int startColumn = (line == rangeStartLine) ? rangeStartColumn : 0; + int endColumn = (line == rangeEndLine) ? rangeEndColumn : textLine->length(); + for(int col = startColumn; col < endColumn;) { + int attr = textLine->attribute(col); + const QHash& reverseCharacterEncodingsHash = highlighting->getReverseCharacterEncodings(attr); + QHash::const_iterator it = reverseCharacterEncodingsHash.find(textLine->at(col)); + if(it != reverseCharacterEncodingsHash.end()) { + replaceText(KTextEditor::Range(line, col, line, col + 1), *it); + col += (*it).length(); + continue; + } + ++col; + } + } +} + +// +// KTextEditor::HighlightInterface +// + +KTextEditor::Attribute::Ptr KateDocument::defaultStyle(const KTextEditor::HighlightInterface::DefaultStyle ds) const +{ + ///TODO: move attributes to document, they are not view-dependant + KateView* view = activeKateView(); + if ( !view ) { + kWarning() << "ATTENTION: cannot access defaultStyle() without any View (will be fixed eventually)"; + return KTextEditor::Attribute::Ptr(0); + } + + KTextEditor::Attribute::Ptr style = highlight()->attributes(view->renderer()->config()->schema()).at(ds); + if (!style->hasProperty(QTextFormat::BackgroundBrush)) { + // make sure the returned style has the default background color set + style.attach(new KTextEditor::Attribute(*style)); + style->setBackground( QBrush(view->renderer()->config()->backgroundColor()) ); + } + return style; +} + +QList< KTextEditor::HighlightInterface::AttributeBlock > KateDocument::lineAttributes(const unsigned int line) +{ + ///TODO: move attributes to document, they are not view-dependant + + QList< KTextEditor::HighlightInterface::AttributeBlock > attribs; + + KateView* view = activeKateView(); + if ( !view ) { + kWarning() << "ATTENTION: cannot access lineAttributes() without any View (will be fixed eventually)"; + return attribs; + } + + Kate::TextLine kateLine = kateTextLine(line); + if ( !kateLine ) + return attribs; + + const QVector &intAttrs = kateLine->attributesList(); + for ( int i = 0; i < intAttrs.size(); ++i ) { + if (intAttrs[i].length > 0 && intAttrs[i].attributeValue > 0) + attribs << KTextEditor::HighlightInterface::AttributeBlock( + intAttrs.at(i).offset, + intAttrs.at(i).length, + view->renderer()->attribute(intAttrs.at(i).attributeValue) + ); + } + + return attribs; +} + +KTextEditor::Attribute::Ptr KateDocument::attributeAt(const KTextEditor::Cursor & position) +{ + KTextEditor::Attribute::Ptr attrib(new KTextEditor::Attribute()); + + KateView* view = activeKateView(); + if ( !view ) { + kWarning() << "ATTENTION: cannot access lineAttributes() without any View (will be fixed eventually)"; + return attrib; + } + + Kate::TextLine kateLine = kateTextLine(position.line()); + if ( !kateLine ) + return attrib; + + *attrib = *view->renderer()->attribute(kateLine->attribute(position.column())); + + return attrib; +} + +QStringList KateDocument::embeddedHighlightingModes() const +{ + return highlight()->getEmbeddedHighlightingModes(); +} + +QString KateDocument::highlightingModeAt(const KTextEditor::Cursor& position) +{ + Kate::TextLine kateLine = kateTextLine(position.line()); + +// const QVector< short >& attrs = kateLine->ctxArray(); +// kDebug() << "----------------------------------------------------------------------"; +// foreach( short a, attrs ) { +// kDebug() << a; +// } +// kDebug() << "----------------------------------------------------------------------"; +// kDebug() << "col: " << position.column() << " lastchar:" << kateLine->lastChar() << " length:" << kateLine->length() << "global mode:" << highlightingMode(); + + int len = kateLine->length(); + int pos = position.column(); + if ( pos >= len ) { + const Kate::TextLineData::ContextStack &ctxs = kateLine->contextStack(); + int ctxcnt = ctxs.count(); + if ( ctxcnt == 0 ) { + return highlightingMode(); + } + int ctx = ctxs.at(ctxcnt-1); + if ( ctx == 0 ) { + return highlightingMode(); + } + return KateHlManager::self()->nameForIdentifier(highlight()->hlKeyForContext(ctx)); + } + + int attr = kateLine->attribute(pos); + if ( attr == 0 ) { + return mode(); + } + + return KateHlManager::self()->nameForIdentifier(highlight()->hlKeyForAttrib(attr)); + +} + +Kate::SwapFile* KateDocument::swapFile() +{ + return m_swapfile; +} + +/** + * \return \c -1 if \c line or \c column invalid, otherwise one of + * standard style attribute number + */ +int KateDocument::defStyleNum(int line, int column) +{ + // Validate parameters to prevent out of range access + if (line < 0 || line >= lines() || column < 0) + return -1; + + // get highlighted line + Kate::TextLine tl = kateTextLine(line); + + // make sure the textline is a valid pointer + if (!tl) + return -1; + + /** + * either get char attribute or attribute of context still active at end of line + */ + int attribute = 0; + if (column < tl->length()) + attribute = tl->attribute (column); + else if (column == tl->length()) { + KateHlContext *context = tl->contextStack().isEmpty() ? highlight()->contextNum(0) : highlight()->contextNum (tl->contextStack().back()); + attribute = context->attr; + } else + return -1; + + KateView* view = static_cast(activeView()); + if (!view) { + if (!m_views.isEmpty()) { + view = m_views.first(); + } else { + //FIXME: find a way to use this function without any view, or move it to the KateView. + return -1; + } + } + + QList attributes = highlight()->attributes( + view->renderer()->config()->schema() + ); + + // sanity check for the attribute + if (attribute < 0 || attribute >= attributes.size()) + return -1; + + KTextEditor::Attribute::Ptr a = attributes[attribute]; + return a->property(KateExtendedAttribute::AttributeDefaultStyleIndex).toInt(); +} + +bool KateDocument::isComment(int line, int column) +{ + const int defaultStyle = defStyleNum(line, column); + return defaultStyle == KTextEditor::HighlightInterface::dsComment; +} + +int KateDocument::findModifiedLine(int startLine, bool down) +{ + const int offset = down ? 1 : -1; + const int lineCount = lines(); + while (startLine >= 0 && startLine < lineCount) { + Kate::TextLine tl = m_buffer->plainLine(startLine); + if (tl && (tl->markedAsModified() || tl->markedAsSavedOnDisk())) { + return startLine; + } + startLine += offset; + } + + return -1; +} + +//BEGIN KTextEditor::MessageInterface +bool KateDocument::postMessage(KTextEditor::Message* message) +{ + // no message -> cancel + if (!message) + return false; + + // make sure the desired view belongs to this document + if (message->view() && message->view()->document() != this) { + kWarning(13020) << "trying to post a message to a view of another document:" << message->text(); + return false; + } + + message->setParent(this); + message->setDocument(this); + + // if there are no actions, add a close action by default if widget does not auto-hide + if (message->actions().count() == 0 && message->autoHide() < 0) { + QAction* closeAction = new QAction(KIcon("window-close"), i18n("&Close"), 0); + closeAction->setToolTip(i18n("Close message")); + message->addAction(closeAction); + } + + // make sure the message is registered even if no actions and no views exist + m_messageHash[message] = QList >(); + + // reparent actions, as we want full control over when they are deleted + foreach (QAction *action, message->actions()) { + action->setParent(0); + m_messageHash[message].append(QSharedPointer(action)); + } + + // post message to requested view, or to all views + if (KateView *view = qobject_cast(message->view())) { + view->postMessage(message, m_messageHash[message]); + } else { + foreach (KateView *view, m_views) + view->postMessage(message, m_messageHash[message]); + } + + // also catch if the user manually calls delete message + connect(message, SIGNAL(closed(KTextEditor::Message*)), SLOT(messageDestroyed(KTextEditor::Message*))); + + return true; +} + +void KateDocument::messageDestroyed(KTextEditor::Message* message) +{ + // KTE:Message is already in destructor + Q_ASSERT(m_messageHash.contains(message)); + m_messageHash.remove(message); +} +//END KTextEditor::MessageInterface + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/document/katedocument.h b/kate/part/document/katedocument.h new file mode 100644 index 00000000..65729734 --- /dev/null +++ b/kate/part/document/katedocument.h @@ -0,0 +1,1249 @@ +/* This file is part of the KDE libraries + Copyright (C) 2001-2004 Christoph Cullmann + Copyright (C) 2001 Joseph Wenninger + Copyright (C) 1999 Jochen Wilhelmy + Copyright (C) 2006 Hamish Rodda + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef _KATE_DOCUMENT_H_ +#define _KATE_DOCUMENT_H_ + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "katepartinterfaces_export.h" +#include "katetextline.h" +#include "katetextcursor.h" +#include "katetextrange.h" + +namespace KTextEditor { + class Plugin; + class Attribute; + class TemplateScript; +} + +namespace KIO { class TransferJob; } + +namespace Kate { class SwapFile; } + +class KateBuffer; +class KateView; +class KateDocumentConfig; +class KateHighlighting; +class KateUndoManager; +class KateOnTheFlyChecker; + +class KateAutoIndent; + + +// +// Kate KTextEditor::Document class (and even KTextEditor::Editor ;) +// +class KATEPARTINTERFACES_EXPORT KateDocument : public KTextEditor::Document, + public KTextEditor::SessionConfigInterface, + public KTextEditor::ParameterizedSessionConfigInterface, + public KTextEditor::SearchInterface, + public KTextEditor::MarkInterface, + public KTextEditor::VariableInterface, + public KTextEditor::ModificationInterface, + public KTextEditor::ConfigInterface, + public KTextEditor::AnnotationInterface, + public KTextEditor::HighlightInterface, + public KTextEditor::MovingInterface, + public KTextEditor::RecoveryInterface, + public KTextEditor::MessageInterface, + private KTextEditor::MovingRangeFeedback +{ + Q_OBJECT + Q_INTERFACES(KTextEditor::SessionConfigInterface) + Q_INTERFACES(KTextEditor::ParameterizedSessionConfigInterface) + Q_INTERFACES(KTextEditor::SearchInterface) + Q_INTERFACES(KTextEditor::MarkInterface) + Q_INTERFACES(KTextEditor::VariableInterface) + Q_INTERFACES(KTextEditor::ModificationInterface) + Q_INTERFACES(KTextEditor::AnnotationInterface) + Q_INTERFACES(KTextEditor::ConfigInterface) + Q_INTERFACES(KTextEditor::HighlightInterface) + Q_INTERFACES(KTextEditor::MovingInterface) + Q_INTERFACES(KTextEditor::RecoveryInterface) + Q_INTERFACES(KTextEditor::MessageInterface) + + friend class KateDocumentTest; + friend class KateBuffer; + + public: + explicit KateDocument (bool bSingleViewMode=false, bool bBrowserView=false, bool bReadOnly=false, + QWidget *parentWidget = 0, QObject * = 0); + ~KateDocument (); + + using ReadWritePart::closeUrl; + virtual bool closeUrl(); + + virtual bool openUrl( const KUrl &url ); + + virtual KTextEditor::Editor *editor (); + + KTextEditor::Range rangeOnLine(KTextEditor::Range range, int line) const; + + private: + void showAndSetOpeningErrorAccess(); + /* + * Overload this to have on-demand view creation + */ + public: + /** + * @return The widget defined by this part, set by setWidget(). + */ + virtual QWidget *widget(); + +Q_SIGNALS: +// TODO for KDE5: move to KTE::Document + void readWriteChanged (KTextEditor::Document *document); + + + + public: + bool readOnly () const { return m_bReadOnly; } + bool browserView () const { return m_bBrowserView; } + bool singleViewMode () const { return m_bSingleViewMode; } + static bool simpleMode (); + + private: + // only to make part work, don't change it ! + const bool m_bSingleViewMode; + const bool m_bBrowserView; + const bool m_bReadOnly; + + // + // KTextEditor::Document stuff + // + public: + virtual KTextEditor::View *createView( QWidget *parent ); + virtual const QList &views () const; + + virtual KTextEditor::View* activeView() const { return m_activeView; } + // Invalid covariant returns my a$$... for some reason gcc won't let me return a KateView above! + KateView* activeKateView() const; + + private: + QList m_views; + QList m_textEditViews; + KTextEditor::View *m_activeView; + + // + // KTextEditor::EditInterface stuff + // + public Q_SLOTS: + virtual bool setText(const QString &); + virtual bool setText(const QStringList& text); + virtual bool clear (); + + virtual bool insertText ( const KTextEditor::Cursor &position, const QString &s, bool block = false ); + virtual bool insertText ( const KTextEditor::Cursor &position, const QStringList &text, bool block = false ); + + virtual bool insertLine ( int line, const QString &s ); + virtual bool insertLines ( int line, const QStringList &s ); + + virtual bool removeText ( const KTextEditor::Range &range, bool block = false ); + virtual bool removeLine ( int line ); + + virtual bool replaceText ( const KTextEditor::Range &range, const QString &s, bool block = false ); + + // unhide method... + virtual bool replaceText (const KTextEditor::Range &r, const QStringList &l, bool b) + { return KTextEditor::Document::replaceText (r, l, b); } + + public: + virtual QString text ( const KTextEditor::Range &range, bool blockwise = false ) const; + virtual QStringList textLines ( const KTextEditor::Range& range, bool block = false ) const; + virtual QString text() const; + virtual QString line(int line) const; + virtual QChar character(const KTextEditor::Cursor& position) const; + virtual int lines() const; + virtual KTextEditor::Cursor documentEnd() const; + virtual int totalCharacters() const; + virtual int lineLength(int line) const; + + Q_SIGNALS: + void charactersSemiInteractivelyInserted(const KTextEditor::Cursor& position, const QString& text); + + public: +//BEGIN editStart/editEnd (start, end, undo, cursor update, view update) + /** + * Enclose editor actions with @p editStart() and @p editEnd() to group + * them. + */ + void editStart (); + + /** + * Alias for @p editStart() + */ + void editBegin () { editStart(); } + + /** + * End a editor operation. + * @see editStart() + */ + void editEnd (); + + void pushEditState(); + void popEditState(); + + virtual bool startEditing () { editStart (); return true; } + virtual bool endEditing () { editEnd (); return true; } + +//END editStart/editEnd + + void inputMethodStart(); + void inputMethodEnd(); + +//BEGIN LINE BASED INSERT/REMOVE STUFF (editStart() and editEnd() included) + /** + * Add a string in the given line/column + * @param line line number + * @param col column + * @param s string to be inserted + * @return true on success + */ + bool editInsertText ( int line, int col, const QString &s ); + /** + * Remove a string in the given line/column + * @param line line number + * @param col column + * @param len length of text to be removed + * @return true on success + */ + bool editRemoveText ( int line, int col, int len ); + + /** + * Mark @p line as @p autowrapped. This is necessary if static word warp is + * enabled, because we have to know whether to insert a new line or add the + * wrapped words to the followin line. + * @param line line number + * @param autowrapped autowrapped? + * @return true on success + */ + bool editMarkLineAutoWrapped ( int line, bool autowrapped ); + + /** + * Wrap @p line. If @p newLine is true, ignore the textline's flag + * KateTextLine::flagAutoWrapped and force a new line. Whether a new line + * was needed/added you can grab with @p newLineAdded. + * @param line line number + * @param col column + * @param newLine if true, force a new line + * @param newLineAdded return value is true, if new line was added (may be 0) + * @return true on success + */ + bool editWrapLine ( int line, int col, bool newLine = true, bool *newLineAdded = 0 ); + /** + * Unwrap @p line. If @p removeLine is true, we force to join the lines. If + * @p removeLine is true, @p length is ignored (eg not needed). + * @param line line number + * @param removeLine if true, force to remove the next line + * @return true on success + */ + bool editUnWrapLine ( int line, bool removeLine = true, int length = 0 ); + + /** + * Insert a string at the given line. + * @param line line number + * @param s string to insert + * @return true on success + */ + bool editInsertLine ( int line, const QString &s ); + /** + * Remove a line + * @param line line number + * @return true on success + */ + bool editRemoveLine ( int line ); + + bool editRemoveLines ( int from, int to ); + + /** + * Remove a line + * @param startLine line to begin wrapping + * @param endLine line to stop wrapping + * @return true on success + */ + bool wrapText (int startLine, int endLine); +//END LINE BASED INSERT/REMOVE STUFF + + Q_SIGNALS: + /** + * Emmitted when text from @p line was wrapped at position pos onto line @p nextLine. + */ + void editLineWrapped ( int line, int col, int len ); + + /** + * Emitted each time text from @p nextLine was upwrapped onto @p line. + */ + void editLineUnWrapped ( int line, int col ); + + public: + bool isEditRunning() const; + + void setUndoMergeAllEdits(bool merge); + + private: + int editSessionNumber; + QStack editStateStack; + bool editIsRunning; + bool m_undoMergeAllEdits; + + // + // KTextEditor::UndoInterface stuff + // + public Q_SLOTS: + void undo (); + void redo (); + + public: + uint undoCount () const; + uint redoCount () const; + + KateUndoManager* undoManager() + { + return m_undoManager; + } + + protected: + KateUndoManager* const m_undoManager; + + Q_SIGNALS: + void undoChanged (); + + // + // KTextEditor::SearchInterface stuff + // + public: + virtual QVector searchText( + const KTextEditor::Range & range, + const QString & pattern, + const KTextEditor::Search::SearchOptions options); + + virtual KTextEditor::Search::SearchOptions supportedSearchOptions() const; + + private: + /** + * Return a widget suitable to be used as a dialog parent. + */ + QWidget * dialogParent(); + + /* + * Access to the mode/highlighting subsystem + */ + public: + /** + * Return the name of the currently used mode + * \return name of the used mode + * + */ + virtual QString mode() const; + + /** + * Return the name of the currently used mode + * \return name of the used mode + * + */ + virtual QString highlightingMode() const; + + /** + * Return a list of the names of all possible modes + * \return list of mode names + */ + virtual QStringList modes() const; + + /** + * Return a list of the names of all possible modes + * \return list of mode names + */ + virtual QStringList highlightingModes() const; + + /** + * Set the current mode of the document by giving its name + * \param name name of the mode to use for this document + * \return \e true on success, otherwise \e false + */ + virtual bool setMode(const QString &name); + + /** + * Set the current mode of the document by giving its name + * \param name name of the mode to use for this document + * \return \e true on success, otherwise \e false + */ + virtual bool setHighlightingMode(const QString &name); + /** + * Returns the name of the section for a highlight given its index in the highlight + * list (as returned by highlightModes()). + * You can use this function to build a tree of the highlight names, organized in sections. + * \param name the name of the highlight for which to find the section name. + */ + virtual QString highlightingModeSection( int index ) const; + + /** + * Returns the name of the section for a mode given its index in the highlight + * list (as returned by modes()). + * You can use this function to build a tree of the mode names, organized in sections. + * \param name the name of the highlight for which to find the section name. + */ + virtual QString modeSection( int index ) const; + + /* + * Helpers.... + */ + public: + void bufferHlChanged(); + + /** + * allow to mark, that we changed hl on user wish and should not reset it + * atm used for the user visible menu to select highlightings + */ + void setDontChangeHlOnSave(); + + /** + * Set that the BOM marker is forced via the tool menu + */ + void bomSetByUser(); + + // + // KTextEditor::SessionConfigInterface and KTextEditor::ParameterizedSessionConfigInterface stuff + // + public: + virtual void readSessionConfig (const KConfigGroup&); + virtual void writeSessionConfig (KConfigGroup&); + virtual void readParameterizedSessionConfig (const KConfigGroup&, unsigned long configParameters); + virtual void writeParameterizedSessionConfig (KConfigGroup&, unsigned long configParameters); + + Q_SIGNALS: + void configChanged(); + + // + // KTextEditor::MarkInterface + // + public Q_SLOTS: + virtual void setMark( int line, uint markType ); + virtual void clearMark( int line ); + + virtual void addMark( int line, uint markType ); + virtual void removeMark( int line, uint markType ); + + virtual void clearMarks(); + + void requestMarkTooltip( int line, QPoint position ); + + ///Returns true if the click on the mark should not be further processed + bool handleMarkClick( int line ); + + ///Returns true if the context-menu event should not further be processed + bool handleMarkContextMenu( int line, QPoint position ); + + virtual void setMarkPixmap( MarkInterface::MarkTypes, const QPixmap& ); + + virtual void setMarkDescription( MarkInterface::MarkTypes, const QString& ); + + virtual void setEditableMarks( uint markMask ); + + public: + virtual uint mark( int line ); + virtual const QHash &marks (); + virtual QPixmap markPixmap( MarkInterface::MarkTypes ) const; + virtual QString markDescription( MarkInterface::MarkTypes ) const; + virtual QColor markColor( MarkInterface::MarkTypes ) const; + virtual uint editableMarks() const; + + Q_SIGNALS: + void markToolTipRequested( KTextEditor::Document* document, KTextEditor::Mark mark, QPoint position, bool& handled ); + + void markContextMenuRequested( KTextEditor::Document* document, KTextEditor::Mark mark, QPoint pos, bool& handled ); + + void markClicked( KTextEditor::Document* document, KTextEditor::Mark mark, bool& handled ); + + void marksChanged( KTextEditor::Document* ); + void markChanged( KTextEditor::Document*, KTextEditor::Mark, KTextEditor::MarkInterface::MarkChangeAction ); + + private: + QHash m_marks; + QHash m_markPixmaps; + QHash m_markDescriptions; + uint m_editableMarks; + + // + // KTextEditor::PrintInterface + // + public Q_SLOTS: + bool printDialog (); + Q_SCRIPTABLE bool print (); + + // + // KTextEditor::DocumentInfoInterface ( ### unfinished ) + // + public: + /** + * @return the name of the mimetype for the document. + * + * This method is using KMimeType::findByUrl, and if the pointer + * is then still the default MimeType for a nonlocal or unsaved file, + * uses mimeTypeForContent(). + */ + virtual QString mimeType(); + + /** + * @return a pointer to the KMimeType for this document, found by analyzing the + * actual content. + * + * Note that this method is *not* part of the DocumentInfoInterface. + */ + KMimeType::Ptr mimeTypeForContent(); + + // + // KTextEditor::VariableInterface + // + public: + virtual QString variable( const QString &name ) const; + // ### TODO KDE5: add to KTextEditor::VaribaleInterface + virtual QString setVariable( const QString &name, const QString &value); + + Q_SIGNALS: + void variableChanged( KTextEditor::Document*, const QString &, const QString & ); + + private: + QMap m_storedVariables; + + // + // MovingInterface API + // + public: + /** + * Create a new moving cursor for this document. + * @param position position of the moving cursor to create + * @param insertBehavior insertion behavior + * @return new moving cursor for the document + */ + virtual KTextEditor::MovingCursor *newMovingCursor (const KTextEditor::Cursor &position, KTextEditor::MovingCursor::InsertBehavior insertBehavior = KTextEditor::MovingCursor::MoveOnInsert); + + /** + * Create a new moving range for this document. + * @param range range of the moving range to create + * @param insertBehaviors insertion behaviors + * @param emptyBehavior behavior on becoming empty + * @return new moving range for the document + */ + virtual KTextEditor::MovingRange *newMovingRange (const KTextEditor::Range &range, KTextEditor::MovingRange::InsertBehaviors insertBehaviors = KTextEditor::MovingRange::DoNotExpand + , KTextEditor::MovingRange::EmptyBehavior emptyBehavior = KTextEditor::MovingRange::AllowEmpty); + + /** + * Current revision + * @return current revision + */ + virtual qint64 revision () const; + + /** + * Last revision the buffer got successful saved + * @return last revision buffer got saved, -1 if none + */ + virtual qint64 lastSavedRevision () const; + + /** + * Lock a revision, this will keep it around until released again. + * But all revisions will always be cleared on buffer clear() (and therefor load()) + * @param revision revision to lock + */ + virtual void lockRevision (qint64 revision); + + /** + * Release a revision. + * @param revision revision to release + */ + virtual void unlockRevision (qint64 revision); + + /** + * Transform a cursor from one revision to an other. + * @param cursor cursor to transform + * @param insertBehavior behavior of this cursor on insert of text at its position + * @param fromRevision from this revision we want to transform + * @param toRevision to this revision we want to transform, default of -1 is current revision + */ + virtual void transformCursor (KTextEditor::Cursor &cursor, KTextEditor::MovingCursor::InsertBehavior insertBehavior, qint64 fromRevision, qint64 toRevision = -1); + + /** + * Transform a cursor from one revision to an other. + * @param line line number of the cursor to transform + * @param column column number of the cursor to transform + * @param insertBehavior behavior of this cursor on insert of text at its position + * @param fromRevision from this revision we want to transform + * @param toRevision to this revision we want to transform, default of -1 is current revision + */ + virtual void transformCursor (int& line, int& column, KTextEditor::MovingCursor::InsertBehavior insertBehavior, qint64 fromRevision, qint64 toRevision = -1); + + /** + * Transform a range from one revision to an other. + * @param range range to transform + * @param insertBehaviors behavior of this range on insert of text at its position + * @param emptyBehavior behavior on becoming empty + * @param fromRevision from this revision we want to transform + * @param toRevision to this revision we want to transform, default of -1 is current revision + */ + virtual void transformRange (KTextEditor::Range &range, KTextEditor::MovingRange::InsertBehaviors insertBehaviors, KTextEditor::MovingRange::EmptyBehavior emptyBehavior, qint64 fromRevision, qint64 toRevision = -1); + + // + // MovingInterface Signals + // + Q_SIGNALS: + /** + * This signal is emitted before the cursors/ranges/revisions of a document are destroyed as the document is deleted. + * @param document the document which the interface belongs too which is in the process of being deleted + */ + void aboutToDeleteMovingInterfaceContent (KTextEditor::Document *document); + + /** + * This signal is emitted before the ranges of a document are invalidated and the revisions are deleted as the document is cleared (for example on load/reload). + * While this signal is emitted, still the old document content is around before the clear. + * @param document the document which the interface belongs too which will invalidate its data + */ + void aboutToInvalidateMovingInterfaceContent (KTextEditor::Document *document); + + // + // Annotation Interface + // + public: + + virtual void setAnnotationModel( KTextEditor::AnnotationModel* model ); + virtual KTextEditor::AnnotationModel* annotationModel() const; + + Q_SIGNALS: + void annotationModelChanged( KTextEditor::AnnotationModel*, KTextEditor::AnnotationModel* ); + + private: + KTextEditor::AnnotationModel* m_annotationModel; + + // + // KParts::ReadWrite stuff + // + public: + /** + * open the file obtained by the kparts framework + * the framework abstracts the loading of remote files + * @return success + */ + virtual bool openFile (); + + /** + * save the file obtained by the kparts framework + * the framework abstracts the uploading of remote files + * @return success + */ + virtual bool saveFile (); + + virtual void setReadWrite ( bool rw = true ); + + virtual void setModified( bool m ); + + private: + void activateDirWatch (const QString &useFileName = QString()); + void deactivateDirWatch (); + + QString m_dirWatchFile; + + public: + /** + * Type chars in a view. + * Will filter out non-printable chars from the realChars array before inserting. + */ + bool typeChars ( KateView *type, const QString &realChars ); + + /** + * gets the last line number (lines() - 1) + */ + inline int lastLine() const { return lines()-1; } + + // Repaint all of all of the views + void repaintViews(bool paintOnlyDirty = true); + + KateHighlighting *highlight () const; + + public Q_SLOTS: + void tagLines(int start, int end); + + private Q_SLOTS: + void internalHlChanged(); + + public: + void addView(KTextEditor::View *); + /** removes the view from the list of views. The view is *not* deleted. + * That's your job. Or, easier, just delete the view in the first place. + * It will remove itself. TODO: this could be converted to a private slot + * connected to the view's destroyed() signal. It is not currently called + * anywhere except from the KateView destructor. + */ + void removeView(KTextEditor::View *); + void setActiveView(KTextEditor::View*); + + bool ownedView(KateView *); + + int toVirtualColumn( int line, int column ) const; + int toVirtualColumn( const KTextEditor::Cursor& ) const; + int fromVirtualColumn( int line, int column ) const; + int fromVirtualColumn( const KTextEditor::Cursor& ) const; + + void newLine( KateView*view ); // Changes input + void backspace( KateView *view, const KTextEditor::Cursor& ); + void del( KateView *view, const KTextEditor::Cursor& ); + void transpose( const KTextEditor::Cursor& ); + void paste ( KateView* view, const QString &text ); + + public: + void indent ( KTextEditor::Range range, int change ); + void comment ( KateView *view, uint line, uint column, int change ); + void align ( KateView *view, const KTextEditor::Range &range ); + void insertTab( KateView *view, const KTextEditor::Cursor& ); + + enum TextTransform { Uppercase, Lowercase, Capitalize }; + + /** + Handling uppercase, lowercase and capitalize for the view. + + If there is a selection, that is transformed, otherwise for uppercase or + lowercase the character right of the cursor is transformed, for capitalize + the word under the cursor is transformed. + */ + void transform ( KateView *view, const KTextEditor::Cursor &, TextTransform ); + /** + Unwrap a range of lines. + */ + void joinLines( uint first, uint last ); + + private: + bool removeStringFromBeginning(int line, const QString &str); + bool removeStringFromEnd(int line, const QString &str); + + /** + Find the position (line and col) of the next char + that is not a space. If found line and col point to the found character. + Otherwise they have both the value -1. + @param line Line of the character which is examined first. + @param col Column of the character which is examined first. + @return True if the specified or a following character is not a space + Otherwise false. + */ + bool nextNonSpaceCharPos(int &line, int &col); + + /** + Find the position (line and col) of the previous char + that is not a space. If found line and col point to the found character. + Otherwise they have both the value -1. + @return True if the specified or a preceding character is not a space. + Otherwise false. + */ + bool previousNonSpaceCharPos(int &line, int &col); + + /** + * Sets a comment marker as defined by the language providing the attribute + * @p attrib on the line @p line + */ + void addStartLineCommentToSingleLine(int line, int attrib=0); + /** + * Removes a comment marker as defined by the language providing the attribute + * @p attrib on the line @p line + */ + bool removeStartLineCommentFromSingleLine(int line, int attrib=0); + + /** + * @see addStartLineCommentToSingleLine. + */ + void addStartStopCommentToSingleLine(int line, int attrib=0); + /** + *@see removeStartLineCommentFromSingleLine. + */ + bool removeStartStopCommentFromSingleLine(int line, int attrib=0); + /** + *@see removeStartLineCommentFromSingleLine. + */ + bool removeStartStopCommentFromRegion(const KTextEditor::Cursor &start, const KTextEditor::Cursor &end, int attrib=0); + + /** + * Add a comment marker as defined by the language providing the attribute + * @p attrib to each line in the selection. + */ + void addStartStopCommentToSelection( KateView *view, int attrib=0 ); + /** + * @see addStartStopCommentToSelection. + */ + void addStartLineCommentToSelection( KateView *view, int attrib=0 ); + + /** + * Removes comment markers relevant to the language providing + * the attribuge @p attrib from each line in the selection. + * + * @return whether the operation succeeded. + */ + bool removeStartStopCommentFromSelection( KateView *view, int attrib=0 ); + /** + * @see removeStartStopCommentFromSelection. + */ + bool removeStartLineCommentFromSelection( KateView *view, int attrib=0 ); + + public: + // KDE5: rename to wordAt(), add wordRangeAt(), see ktexteditor/document.h + QString getWord( const KTextEditor::Cursor& cursor ); + + public: + void newBracketMark( const KTextEditor::Cursor& start, KTextEditor::Range& bm, int maxLines = -1 ); + bool findMatchingBracket( KTextEditor::Range& range, int maxLines = -1 ); + + public: + virtual const QString &documentName () const { return m_docName; } + + private: + void updateDocName (); + + public: + /** + * @return whether the document is modified on disk since last saved + */ + bool isModifiedOnDisc() { return m_modOnHd; } + + virtual void setModifiedOnDisk( ModifiedOnDiskReason reason ); + + virtual void setModifiedOnDiskWarning ( bool on ); + + public Q_SLOTS: + /** + * Ask the user what to do, if the file has been modified on disk. + * Reimplemented from KTextEditor::Document. + */ + virtual void slotModifiedOnDisk( KTextEditor::View *v = 0 ); + + /** + * Reloads the current document from disk if possible + */ + virtual bool documentReload (); + + virtual bool documentSave (); + virtual bool documentSaveAs (); + + virtual bool save(); + public: + virtual bool saveAs( const KUrl &url ); + + Q_SIGNALS: + /** + * Indicate this file is modified on disk + * @param doc the KTextEditor::Document object that represents the file on disk + * @param isModified indicates the file was modified rather than created or deleted + * @param reason the reason we are emitting the signal. + */ + void modifiedOnDisk (KTextEditor::Document *doc, bool isModified, KTextEditor::ModificationInterface::ModifiedOnDiskReason reason); + + public: + void ignoreModifiedOnDiskOnce(); + + private: + int m_isasking; // don't reenter slotModifiedOnDisk when this is true + // -1: ignore once, 0: false, 1: true + + public: + virtual bool setEncoding (const QString &e); + virtual const QString &encoding() const; + + + public Q_SLOTS: + void setWordWrap (bool on); + void setWordWrapAt (uint col); + + public: + bool wordWrap() const; + uint wordWrapAt() const; + + public Q_SLOTS: + void setPageUpDownMovesCursor(bool on); + + public: + bool pageUpDownMovesCursor() const; + + // code folding + public: + Kate::TextLine kateTextLine(uint i); + Kate::TextLine plainKateTextLine(uint i); + + Q_SIGNALS: + void aboutToRemoveText(const KTextEditor::Range&); + + private Q_SLOTS: + void slotModOnHdDirty (const QString &path); + void slotModOnHdCreated (const QString &path); + void slotModOnHdDeleted (const QString &path); + + private: + /** + * create a Sha1 digest of the file, if it is a local file. + * The result can be accessed through KateBuffer::digest(). + * + * @return wheather the operation was attempted and succeeded. + */ + bool createDigest (); + + /** + * create a string for the modonhd warnings, giving the reason. + */ + QString reasonedMOHString() const; + + /** + * Removes all trailing whitespace in the document. + */ + void removeTrailingSpaces(); + + public: + /** + * sha1 digest of this document + * @return sha1 digest for this document + */ + const QByteArray &digest () const; + + void updateFileType (const QString &newType, bool user = false); + + QString fileType () const { return m_fileType; } + + /** + * Get access to buffer of this document. + * Is needed to create cursors and ranges for example. + * @return document buffer + */ + KateBuffer &buffer () { return *m_buffer; } + + /** + * set indentation mode by user + * this will remember that a user did set it and will avoid reset on save + */ + void rememberUserDidSetIndentationMode () + { + m_indenterSetByUser = true; + } + + /** + * User did set encoding for next reload => enforce it! + */ + void userSetEncodingForNextReload () + { + m_userSetEncodingForNextReload = true; + } + + // + // REALLY internal data ;) + // + private: + // text buffer + KateBuffer *const m_buffer; + + // indenter + KateAutoIndent *const m_indenter; + + bool m_hlSetByUser; + bool m_bomSetByUser; + bool m_indenterSetByUser; + bool m_userSetEncodingForNextReload; + + bool m_modOnHd; + ModifiedOnDiskReason m_modOnHdReason; + + QString m_docName; + int m_docNameNumber; + + // file type !!! + QString m_fileType; + bool m_fileTypeSetByUser; + + /** + * document is still reloading a file + */ + bool m_reloading; + + public Q_SLOTS: + void slotQueryClose_save(bool *handled, bool* abortClosing); + + public: + virtual bool queryClose(); + + void makeAttribs (bool needInvalidate = true); + + static bool checkOverwrite( KUrl u, QWidget *parent ); + + /** + * Configuration + */ + public: + KateDocumentConfig *config() { return m_config; } + KateDocumentConfig *config() const { return m_config; } + + void updateConfig (); + + private: + KateDocumentConfig *const m_config; + + /** + * Variable Reader + * TODO add register functionality/ktexteditor interface + */ + private: + /** + * read dir config file + */ + void readDirConfig (); + + /** + Reads all the variables in the document. + Called when opening/saving a document + */ + void readVariables(bool onlyViewAndRenderer = false); + + /** + Reads and applies the variables in a single line + TODO registered variables gets saved in a [map] + */ + void readVariableLine( QString t, bool onlyViewAndRenderer = false ); + /** + Sets a view variable in all the views. + */ + void setViewVariable( QString var, QString val ); + /** + @return weather a string value could be converted + to a bool value as supported. + The value is put in *result. + */ + static bool checkBoolValue( QString value, bool *result ); + /** + @return weather a string value could be converted + to a integer value. + The value is put in *result. + */ + static bool checkIntValue( QString value, int *result ); + /** + Feeds value into @p col using QColor::setNamedColor() and returns + wheather the color is valid + */ + static bool checkColorValue( QString value, QColor &col ); + + /** + * helper regex to capture the document variables + */ + static QRegExp kvLine; + static QRegExp kvLineWildcard; + static QRegExp kvLineMime; + static QRegExp kvVar; + + bool m_fileChangedDialogsActivated; + + // + // KTextEditor::ConfigInterface + // + public: + virtual QStringList configKeys() const; + virtual QVariant configValue(const QString &key); + virtual void setConfigValue(const QString &key, const QVariant &value); + + // + // KTextEditor::RecoveryInterface + // + public: + virtual bool isDataRecoveryAvailable() const; + virtual void recoverData(); + virtual void discardDataRecovery(); + + // + // KTextEditor::HighlightInterface + // + public: + virtual KTextEditor::Attribute::Ptr defaultStyle(const KTextEditor::HighlightInterface::DefaultStyle ds) const; + virtual QList< KTextEditor::HighlightInterface::AttributeBlock > lineAttributes(const unsigned int line); + virtual QStringList embeddedHighlightingModes() const; + virtual QString highlightingModeAt(const KTextEditor::Cursor& position); + // TODO KDE5: maybe make available in HighlightInterface for convenience + virtual KTextEditor::Attribute::Ptr attributeAt(const KTextEditor::Cursor & position); + + // + //BEGIN: KTextEditor::MessageInterface + // + public: + virtual bool postMessage(KTextEditor::Message* message); + + public Q_SLOTS: + void messageDestroyed(KTextEditor::Message* message); + + private: + QHash > > m_messageHash; + //END KTextEditor::MessageInterface + + public: + QString defaultDictionary() const; + QList > dictionaryRanges() const; + bool isOnTheFlySpellCheckingEnabled() const; + + QString dictionaryForMisspelledRange(const KTextEditor::Range& range) const; + void clearMisspellingForWord(const QString& word); + + public Q_SLOTS: + void clearDictionaryRanges(); + void setDictionary(const QString& dict, const KTextEditor::Range &range); + void revertToDefaultDictionary(const KTextEditor::Range &range); + void setDefaultDictionary(const QString& dict); + void onTheFlySpellCheckingEnabled(bool enable); + void refreshOnTheFlyCheck(const KTextEditor::Range &range = KTextEditor::Range::invalid()); + + Q_SIGNALS: + void dictionaryRangesPresent(bool yesNo); + void defaultDictionaryChanged(KateDocument *document); + + public: + bool containsCharacterEncoding(const KTextEditor::Range& range); + + typedef QList > OffsetList; + + int computePositionWrtOffsets(const OffsetList& offsetList, int pos); + + /** + * The first OffsetList is from decoded to encoded, and the second OffsetList from + * encoded to decoded. + **/ + QString decodeCharacters(const KTextEditor::Range& range, + KateDocument::OffsetList& decToEncOffsetList, + KateDocument::OffsetList& encToDecOffsetList); + void replaceCharactersByEncoding(const KTextEditor::Range& range); + + enum EncodedCharaterInsertionPolicy {EncodeAlways, EncodeWhenPresent, EncodeNever}; + + protected: + KateOnTheFlyChecker *m_onTheFlyChecker; + QString m_defaultDictionary; + QList > m_dictionaryRanges; + + // from KTextEditor::MovingRangeFeedback + void rangeInvalid(KTextEditor::MovingRange *movingRange); + void rangeEmpty(KTextEditor::MovingRange *movingRange); + + void deleteDictionaryRange(KTextEditor::MovingRange *movingRange); + + private: + Kate::SwapFile *m_swapfile; + + public: + Kate::SwapFile* swapFile(); + + //helpers for scripting and codefolding + int defStyleNum(int line, int column); + bool isComment(int line, int column); + +public: + int findModifiedLine(int startLine, bool down); + + private Q_SLOTS: + /** + * watch for all started io jobs to remember if file is perhaps loading atm + * @param job started job + */ + void slotStarted(KIO::Job *job); + void slotCompleted(); + void slotCanceled(); + + /** + * trigger display of loading message, after 1000 ms + */ + void slotTriggerLoadingMessage (); + + /** + * Abort loading + */ + void slotAbortLoading (); + + private: + /** + * different possible states + */ + enum DocumentStates { + /** + * Idle + */ + DocumentIdle, + + /** + * Loading + */ + DocumentLoading, + + /** + * Saving + */ + DocumentSaving, + + /** + * Pre Saving As, this is between ::saveAs is called and ::save + */ + DocumentPreSavingAs, + + /** + * Saving As + */ + DocumentSavingAs + }; + + /** + * current state + */ + DocumentStates m_documentState; + + /** + * read-write state before loading started + */ + bool m_readWriteStateBeforeLoading; + + /** + * if the document is untitled + */ + bool m_isUntitled; + /** + * loading job, we want to cancel with cancel in the loading message + */ + QPointer m_loadingJob; + + /** + * message to show during loading + */ + QPointer m_loadingMessage; +}; + +#endif + +// kate: space-indent on; indent-width 2; replace-tabs on; + diff --git a/kate/part/document/katedocumenthelpers.cpp b/kate/part/document/katedocumenthelpers.cpp new file mode 100644 index 00000000..f13b5385 --- /dev/null +++ b/kate/part/document/katedocumenthelpers.cpp @@ -0,0 +1,47 @@ +/* This file is part of the KDE libraries and the Kate part. + * + * Copyright (C) 2001-2010 Christoph Cullmann + * Copyright (C) 2001 Joseph Wenninger + * Copyright (C) 1999 Jochen Wilhelmy + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "katedocumenthelpers.h" +#include "moc_katedocumenthelpers.cpp" + +#include "katedocument.h" +#include "kateview.h" + +#include +#include + +namespace KTextEditor { class View; } + +KateBrowserExtension::KateBrowserExtension( KateDocument* doc ) +: KParts::BrowserExtension( doc ), + m_doc (doc) +{ + setObjectName( "katepartbrowserextension" ); + emit enableAction( "print", true ); +} + +void KateBrowserExtension::print() +{ + m_doc->printDialog(); +} + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/document/katedocumenthelpers.h b/kate/part/document/katedocumenthelpers.h new file mode 100644 index 00000000..b0311319 --- /dev/null +++ b/kate/part/document/katedocumenthelpers.h @@ -0,0 +1,61 @@ +/* This file is part of the KDE libraries and the Kate part. + * + * Copyright (C) 2001-2010 Christoph Cullmann + * Copyright (C) 2001 Joseph Wenninger + * Copyright (C) 1999 Jochen Wilhelmy + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __KATE_DOCUMENT_HELPERS__ +#define __KATE_DOCUMENT_HELPERS__ + +#include + +#include +#include + +class KateDocument; + +/** + * Interface for embedding KateDocument into a browser + */ +class KateBrowserExtension : public KParts::BrowserExtension +{ + Q_OBJECT + + public: + /** + * Constructor + * @param doc parent document + */ + explicit KateBrowserExtension( KateDocument* doc ); + + public Q_SLOTS: + /** + * print the current file + */ + void print(); + + private: + /** + * parent document + */ + KateDocument* m_doc; +}; + +#endif + diff --git a/kate/part/kte5/documentcursor.cpp b/kate/part/kte5/documentcursor.cpp new file mode 100644 index 00000000..bc05a6ef --- /dev/null +++ b/kate/part/kte5/documentcursor.cpp @@ -0,0 +1,250 @@ +/* This file is part of the KDE project + * + * Copyright (C) 2010 Christoph Cullmann + * Copyright (C) 2012 Dominik Haumann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "documentcursor.h" + +namespace KTextEditor { + +DocumentCursor::DocumentCursor(KTextEditor::Document* document) + : m_document(document) + , m_cursor(KTextEditor::Cursor::invalid()) +{ + // we require a valid document + Q_ASSERT(m_document); +} + +DocumentCursor::DocumentCursor(KTextEditor::Document* document, const KTextEditor::Cursor& position) + : m_document(document) + , m_cursor(position) +{ + // we require a valid document + Q_ASSERT(m_document); +} + +DocumentCursor::DocumentCursor(KTextEditor::Document* document, int line, int column) + : m_document(document) + , m_cursor(line, column) +{ + // we require a valid document + Q_ASSERT(m_document); +} + +DocumentCursor::DocumentCursor (const DocumentCursor &other) +: m_document(other.m_document) +, m_cursor(other.m_cursor) +{ +} + +DocumentCursor& DocumentCursor::operator= (const DocumentCursor &other) +{ + m_document = other.m_document; + m_cursor = other.m_cursor; + return *this; +} + +KTextEditor::Document *DocumentCursor::document () const +{ + return m_document; +} + +void DocumentCursor::setPosition (const KTextEditor::Cursor& position) +{ + if (position.isValid()) { + m_cursor = position; + } else { + m_cursor = KTextEditor::Cursor::invalid(); + } +} + +int DocumentCursor::line() const +{ + return m_cursor.line(); +} + +int DocumentCursor::column() const +{ + return m_cursor.column(); +} + +DocumentCursor::~DocumentCursor () +{ +} + +void DocumentCursor::makeValid() +{ + const int line = m_cursor.line(); + const int col = m_cursor.line(); + + if (line < 0) { + m_cursor.setPosition(0, 0); + } else if (line >= m_document->lines()) { + m_cursor = m_document->documentEnd(); + } else if (col > m_document->lineLength(line)) { + m_cursor.setColumn(m_document->lineLength(line)); + } +// TODO KDE5 if QChar::isLowSurrogate() -> move one to the left. +// } else if (m_document->character(m_cursor).isLowSurrogate()) { +// Q_ASSERT(col > 0); +// m_cursor.setColumn(col - 1); +// } + + Q_ASSERT(isValidTextPosition()); +} + +void DocumentCursor::setPosition (int line, int column) +{ + m_cursor.setPosition(line, column); +} + +void DocumentCursor::setLine(int line) +{ + setPosition(line, column()); +} + +void DocumentCursor::setColumn(int column) +{ + setPosition(line(), column); +} + +bool DocumentCursor::atStartOfLine() const +{ + return isValidTextPosition() && column() == 0; +} + +bool DocumentCursor::atEndOfLine() const +{ + return isValidTextPosition() && column() == document()->lineLength(line()); +} + +bool DocumentCursor::atStartOfDocument() const +{ + return line() == 0 && column() == 0; +} + +bool DocumentCursor::atEndOfDocument() const +{ + return m_cursor == document()->documentEnd(); +} + +bool DocumentCursor::gotoNextLine() +{ + // only allow valid cursors + const bool ok = isValid() && (line() + 1 < document()->lines()); + + if (ok) { + setPosition(Cursor(line() + 1, 0)); + } + + return ok; +} + +bool DocumentCursor::gotoPreviousLine() +{ + // only allow valid cursors + bool ok = (line() > 0) && (column() >= 0); + + if (ok) { + setPosition(Cursor(line() - 1, 0)); + } + + return ok; +} + +bool DocumentCursor::move(int chars, WrapBehavior wrapBehavior) +{ + if (!isValid()) { + return false; + } + + Cursor c(m_cursor); + + // cache lineLength to minimize calls of KateDocument::lineLength(), as + // results in locating the correct block in the text buffer every time, + // which is relatively slow + int lineLength = document()->lineLength(c.line()); + + // special case: cursor position is not in valid text, then the algo does + // not work for Wrap mode. Hence, catch this special case by setting + // c.column() to the lineLength() + if (chars > 0 && wrapBehavior == Wrap && c.column() > lineLength) { + c.setColumn(lineLength); + } + + while (chars != 0) { + if (chars > 0) { + if (wrapBehavior == Wrap) { + int advance = qMin(lineLength - c.column(), chars); + + if (chars > advance) { + if (c.line() + 1 >= document()->lines()) { + return false; + } + + c.setPosition(c.line() + 1, 0); + chars -= advance + 1; // +1 because of end-of-line wrap + + // advanced one line, so cache correct line length again + lineLength = document()->lineLength(c.line()); + } else { + c.setColumn(c.column() + chars); + chars = 0; + } + } else { // NoWrap + c.setColumn(c.column() + chars); + chars = 0; + } + } else { + int back = qMin(c.column(), -chars); + if (-chars > back) { + if (c.line() == 0) + return false; + + c.setPosition(c.line() - 1, document()->lineLength(c.line() - 1)); + chars += back + 1; // +1 because of wrap-around at start-of-line + + // advanced one line, so cache correct line length again + lineLength = document()->lineLength(c.line()); + } else { + c.setColumn(c.column() + chars); + chars = 0; + } + } + } + + if (c != m_cursor) { + setPosition(c); + } + return true; +} + +const Cursor& DocumentCursor::toCursor () const +{ + return m_cursor; +} + +DocumentCursor::operator const Cursor& () const +{ + return m_cursor; +} + +} + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/kte5/documentcursor.h b/kate/part/kte5/documentcursor.h new file mode 100644 index 00000000..09ad7be1 --- /dev/null +++ b/kate/part/kte5/documentcursor.h @@ -0,0 +1,396 @@ +/* This file is part of the KDE project + * + * Copyright (C) 2010 Christoph Cullmann + * Copyright (C) 2012 Dominik Haumann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef KTEXTEDITOR_DOCUMENT_CURSOR_H +#define KTEXTEDITOR_DOCUMENT_CURSOR_H + +#include +#include + +#include "katepartinterfaces_export.h" + +namespace KTextEditor { + +/** + * \short A Cursor which is bound to a specific Document. + * + * \section documentcursor_intro Introduction + * A DocumentCursor is an extension of the basic Cursor class. + * The DocumentCursor is bound to a specific Document instance. + * This way, the cursor povides additional functions like gotoNextLine(), + * gotoPreviousLine() and move() according to the WrapBehavior. + * + * The only difference to a MovingCursor is that the DocumentCursor's + * position does not automatically move on text manipulation. + * + * \section documentcursor_position Validity + * + * When constructing a DocumentCursor, a valid document pointer is required + * in the constructor. A null pointer will assert in debug builds. + * Further, a DocumentCursor should only be used as long as the Document + * exists, otherwise the DocumentCursor contains a dangling pointer to the + * previously assigned Document. + * + * \section documentcursor_example Example + * + * A DocumentCursor is created and used like this: + * \code + * KTextEditor::DocumentCursor docCursor(document); + * docCursor.setPosition(0, 0); + * docCursor.gotoNextLine(); + * docCursor.move(5); // move 5 characters to the right + * \endcode + * + * \see KTextEditor::Cursor, KTextEditor::MovingCursor + * + * \author Dominik Haumann \ + * + * \todo KDE5: move to ktexteditor interface, use it in + * MovingCursor::move() to avoid code duplication + */ +class KATEPARTINTERFACES_EXPORT DocumentCursor +{ + // + // sub types + // + public: + /** + * Wrap behavior for end of line treatement used in move(). + */ + enum WrapBehavior { + Wrap = 0x0, ///< wrap at end of line + NoWrap = 0x1 ///< do not wrap at end of line + }; + + // + // Constructor + // + public: + /** + * Constructor that creates a DocumentCursor at the \e invalid position + * (-1, -1). + * \see isValid() + */ + DocumentCursor(KTextEditor::Document* document); + + /** + * Constructor that creates a DocumentCursor located at \p position. + */ + DocumentCursor(KTextEditor::Document* document, const KTextEditor::Cursor& position); + + /** + * Constructor that creates a DocumentCursor located at \p line and \p column. + */ + DocumentCursor(KTextEditor::Document* document, int line, int column); + + /** + * Copy constructor. Make sure the Document of the DocumentCursor is + * valid. + */ + DocumentCursor (const DocumentCursor &other); + + // + // stuff that needs to be implemented by editor part cusors + // + public: + + /** + * Gets the document to which this cursor is bound. + * \return a pointer to the document + */ + Document *document () const; + + /** + * Set the current cursor position to \e position. + * If \e position is not valid, meaning that either its line < 0 or its + * column < 0, then the document cursor is set to invalid(-1, -1). + * + * \param position new cursor position + */ + void setPosition (const KTextEditor::Cursor& position); + + /** + * Retrieve the line on which this cursor is situated. + * \return line number, where 0 is the first line. + */ + int line() const; + + /** + * Retrieve the column on which this cursor is situated. + * \return column number, where 0 is the first column. + */ + int column() const; + + /** + * Destruct the moving cursor. + */ + ~DocumentCursor (); + + // + // forbidden stuff + // + private: + /** + * no default constructor, as we need a document. + */ + DocumentCursor (); + + // + // convenience API + // + public: + + /** + * Returns whether the current position of this cursor is a valid position, + * i.e. whether line() >= 0 and column() >= 0. + * \return \e true , if the cursor position is valid, otherwise \e false + */ + inline bool isValid() const { + return line() >= 0 && column() >= 0; + } + + /** + * Check whether the current position of this cursor is a valid text + * position. + * \return \e true , if the cursor is a valid text position, otherwise \e false + */ + // TODO KDE5: use KTE::Document::isValidTextPosition() + inline bool isValidTextPosition() const { + return isValid() && line() < document()->lines() && column() <= document()->lineLength(line()); + } + + /** + * Make sure the cursor position is at a valid text position according to + * the following rules. + * - If the cursor is invalid(), i.e. either line < 0 or column < 0, it is + * set to (0, 0). + * - If the cursor's line is past the number of lines in the document, the + * cursor is set to Document::documentEnd(). + * - If the cursor's column is past the line length, the cursor column is + * set to the line length. + * - If the cursor is inside a Unicode surrogate, the cursor is moved to the + * beginning of the Unicode surrogate. + * + * After calling makeValid(), the cursor is guaranteed to be located at + * a valid text position. + * \see isValidTextPosition(), isValid() + */ + void makeValid(); + + /** + * \overload + * + * Set the cursor position to \e line and \e column. + * + * \param line new cursor line + * \param column new cursor column + */ + void setPosition (int line, int column); + + /** + * Set the cursor line to \e line. + * \param line new cursor line + */ + void setLine(int line); + + /** + * Set the cursor column to \e column. + * \param column new cursor column + */ + void setColumn(int column); + + /** + * Determine if this cursor is located at column 0 of a valid text line. + * + * \return \e true if cursor is a valid text position and column()=0, otherwise \e false. + */ + bool atStartOfLine() const; + + /** + * Determine if this cursor is located at the end of the current line. + * + * \return \e true if the cursor is situated at the end of the line, otherwise \e false. + */ + bool atEndOfLine() const; + + /** + * Determine if this cursor is located at line 0 and column 0. + * + * \return \e true if the cursor is at start of the document, otherwise \e false. + */ + bool atStartOfDocument() const; + + /** + * Determine if this cursor is located at the end of the last line in the + * document. + * + * \return \e true if the cursor is at the end of the document, otherwise \e false. + */ + bool atEndOfDocument() const; + + /** + * Moves the cursor to the next line and sets the column to 0. If the cursor + * position is already in the last line of the document, the cursor position + * remains unchanged and the return value is \e false. + * + * \return \e true on success, otherwise \e false + */ + bool gotoNextLine(); + + /** + * Moves the cursor to the previous line and sets the column to 0. If the + * cursor position is already in line 0, the cursor position remains + * unchanged and the return value is \e false. + * + * \return \e true on success, otherwise \e false + */ + bool gotoPreviousLine(); + + /** + * Moves the cursor \p chars character forward or backwards. If \e wrapBehavior + * equals WrapBehavior::Wrap, the cursor is automatically wrapped to the + * next line at the end of a line. + * + * When moving backwards, the WrapBehavior does not have any effect. + * \note If the cursor could not be moved the amount of chars requested, + * the cursor is not moved at all! + * + * \return \e true on success, otherwise \e false + */ + bool move(int chars, WrapBehavior wrapBehavior = Wrap); + + /** + * Convert this clever cursor into a dumb one. + * @return normal cursor + */ + const Cursor& toCursor () const; + + /** + * Convert this clever cursor into a dumb one. Equal to toCursor, allowing to use implicit conversion. + * @return normal cursor + */ + operator const Cursor& () const; + + // + // operators for: DocumentCursor <-> DocumentCursor + // + /** + * Assignment operator. Same as the copy constructor. Make sure that + * the assigned Document is a valid document pointer. + */ + DocumentCursor &operator= (const DocumentCursor &other); + + /** + * Equality operator. + * + * \note comparison between two invalid cursors is undefined. + * comparison between an invalid and a valid cursor will always be \e false. + * + * \param c1 first cursor to compare + * \param c2 second cursor to compare + * \return \e true, if c1's and c2's assigned document, line and column are \e equal. + */ + inline friend bool operator==(const DocumentCursor& c1, const DocumentCursor& c2) + { return c1.document() == c2.document() && c1.line() == c2.line() && c1.column() == c2.column(); } + + /** + * Inequality operator. + * \param c1 first cursor to compare + * \param c2 second cursor to compare + * \return \e true, if c1's and c2's assigned document, line and column are \e not equal. + */ + inline friend bool operator!=(const DocumentCursor& c1, const DocumentCursor& c2) + { return !(c1 == c2); } + + /** + * Greater than operator. + * \param c1 first cursor to compare + * \param c2 second cursor to compare + * \return \e true, if c1's position is greater than c2's position, + * otherwise \e false. + */ + inline friend bool operator>(const DocumentCursor& c1, const DocumentCursor& c2) + { return c1.line() > c2.line() || (c1.line() == c2.line() && c1.column() > c2.column()); } + + /** + * Greater than or equal to operator. + * \param c1 first cursor to compare + * \param c2 second cursor to compare + * \return \e true, if c1's position is greater than or equal to c2's + * position, otherwise \e false. + */ + inline friend bool operator>=(const DocumentCursor& c1, const DocumentCursor& c2) + { return c1.line() > c2.line() || (c1.line() == c2.line() && c1.column() >= c2.column()); } + + /** + * Less than operator. + * \param c1 first cursor to compare + * \param c2 second cursor to compare + * \return \e true, if c1's position is greater than or equal to c2's + * position, otherwise \e false. + */ + inline friend bool operator<(const DocumentCursor& c1, const DocumentCursor& c2) + { return !(c1 >= c2); } + + /** + * Less than or equal to operator. + * \param c1 first cursor to compare + * \param c2 second cursor to compare + * \return \e true, if c1's position is lesser than or equal to c2's + * position, otherwise \e false. + */ + inline friend bool operator<=(const DocumentCursor& c1, const DocumentCursor& c2) + { return !(c1 > c2); } + + /** + * kDebug() stream operator. Writes this cursor to the debug output in a nicely formatted way. + * @param s debug stream + * @param cursor cursor to print + * @return debug stream + */ + inline friend QDebug operator<< (QDebug s, const DocumentCursor *cursor) { + if (cursor) + s.nospace() << "(" << cursor->document() << ": " << cursor->line() << ", " << cursor->column() << ")"; + else + s.nospace() << "(null document cursor)"; + return s.space(); + } + + /** + * kDebug() stream operator. Writes this cursor to the debug output in a nicely formatted way. + * @param s debug stream + * @param cursor cursor to print + * @return debug stream + */ + inline friend QDebug operator<< (QDebug s, const DocumentCursor &cursor) { + return s << &cursor; + } + + private: + KTextEditor::Document* m_document; + KTextEditor::Cursor m_cursor; +}; + +} + +#endif + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/kte5/foldinginterface.h b/kate/part/kte5/foldinginterface.h new file mode 100644 index 00000000..8402a534 --- /dev/null +++ b/kate/part/kte5/foldinginterface.h @@ -0,0 +1,203 @@ +/* This file is part of the Kate project. + * + * Copyright (C) 2013 Christoph Cullmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef KTEXTEDITOR_FOLDINGINTERFACE_H +#define KTEXTEDITOR_FOLDINGINTERFACE_H + +// #include "ktexteditor_export.h" + +#include + +namespace KTextEditor { + +class Range; + +/** + * KTextEditor interface for code folding of a KTextEditor::View. + * The interface allows to arbitrary fold given regions of a buffer as long + * as they are well nested. + * Multiple instances of this class can exist for the same buffer. + */ +class KTEXTEDITOR_EXPORT FoldingInterface +{ + public: + /** + * Create folding object for given buffer. + * @param buffer text buffer we want to provide folding info for + */ + FoldingInterface (); + + /** + * Cleanup + */ + virtual ~FoldingInterface (); + + /** + * Folding state of a range + */ + enum FoldingRangeFlag { + /** + * Range is persistent, e.g. it should not auto-delete after unfolding! + */ + Persistent = 0x1, + + /** + * Range is folded away + */ + Folded = 0x2 + }; + Q_DECLARE_FLAGS(FoldingRangeFlags, FoldingRangeFlag); + + /** + * \name Creation, Folding and Unfolding + * + * The following functions provide access and manipulation of the range's position. + * \{ + */ + + /** + * Create a new folding range. + * @param range folding range + * @param flags initial flags for the new folding range + * @return on success, id of new range >= 0, else -1, we return no pointer as folding ranges might be auto-deleted internally! + * the ids are stable for one KTextEditor::FoldingInterface, e.g. you can rely in unit tests that you get 0,1,.... for successfully created ranges! + */ + qint64 newFoldingRange (const KTextEditor::Range &range, FoldingRangeFlags flags = FoldingRangeFlags()); + + /** + * Fold the given range. + * @param id id of the range to fold + * @return success + */ + virtual bool foldRange (qint64 id) = 0; + + /** + * Unfold the given range. + * In addition it can be forced to remove the region, even if it is persistent. + * @param id id of the range to unfold + * @param remove should the range be removed from the folding after unfolding? ranges that are not persistent auto-remove themself on unfolding + * @return success + */ + virtual bool unfoldRange (qint64 id, bool remove = false) = 0; + + /** + * Queries which folding ranges start at the given line and returns the id + flags for all + * of them. Very fast if nothing is folded, else binary search. + * @param line line to query starting folding ranges + * @return vector of id's + flags + */ + virtual QVector > foldingRangesStartingOnLine (int line) const = 0; + + /** + * Check whether on this line starts a folding range + * @param line line to query starting folding ranges + * @return true, if a folding range starts, otherwise false + */ + virtual bool lineContainsStartFoldingRanges (int line) const = 0; + + /** + * Fold the first folding range starting on this line, if applicable. + * @param line line to fold + * @return id of folded range (>= 0) or -1, if no folding range starts at line + */ + virtual qint64 foldLine (int line) const = 0; + + /** + * Unfolds all folding range starting on this line, if applicable. + * @param line line to unfold + * @return id of folded range (>= 0) or -1, if no folding range starts at line + */ + virtual bool unfoldLine (int line) const = 0; + + /** + * \} + * + * \name Line Visibility + * + * The following functions provide access and manipulation of the range's position. + * \{ + */ + + /** + * Query if a given line is visible. + * Very fast, if nothing is folded, else does binary search + * log(n) for n == number of folded ranges + * @param line line to query + * @param foldedRangeId if the line is not visible and that pointer is not 0, will be filled with id of range hiding the line or -1 + * @return is that line visible? + */ + virtual bool isLineVisible (int line, qint64 *foldedRangeId = 0) const = 0; + + /** + * Ensure that a given line will be visible. + * Potentially unfold recursively all folds hiding this line, else just returns. + * @param line line to make visible + */ + virtual void ensureLineIsVisible (int line) = 0; + + /** + * Query number of visible lines. + * Very fast, if nothing is folded, else walks over all folded regions + * O(n) for n == number of folded ranges + */ + virtual int visibleLines () const = 0; + + /** + * Convert a text buffer line to a visible line number. + * Very fast, if nothing is folded, else walks over all folded regions + * O(n) for n == number of folded ranges + * @param line line index in the text buffer + * @return index in visible lines + */ + virtual int lineToVisibleLine (int line) const = 0; + + /** + * Convert a visible line number to a line number in the text buffer. + * Very fast, if nothing is folded, else walks over all folded regions + * O(n) for n == number of folded ranges + * @param visibleLine visible line index + * @return index in text buffer lines + */ + virtual int visibleLineToLine (int visibleLine) const = 0; + + /** + * \} + */ + + public Q_SLOTS: + /** + * Clear the complete folding. + * This is automatically triggered if the buffer is cleared. + */ + void clear (); + + Q_SIGNALS: + /** + * If the folding state of existing ranges changes or + * ranges are added/removed, this signal is emitted. + */ + void foldingRangesChanged (); +}; + +} + +Q_DECLARE_OPERATORS_FOR_FLAGS(KTextEditor::FoldingInterface::FoldingRangeFlags) + +#endif diff --git a/kate/part/mode/filetypeconfigwidget.ui b/kate/part/mode/filetypeconfigwidget.ui new file mode 100644 index 00000000..43f2e5aa --- /dev/null +++ b/kate/part/mode/filetypeconfigwidget.ui @@ -0,0 +1,277 @@ + + + FileTypeConfigWidget + + + + 0 + + + + + + + &Filetype: + + + cmbFiletypes + + + + + + + Select the filetype you want to change. + + + + + + + Create a new file type. + + + &New + + + + + + + Delete the current file type. + + + &Delete + + + + + + + Qt::Horizontal + + + + 1 + 0 + + + + + + + + + + Properties + + + + + + &Name: + + + edtName + + + + + + + The name of the filetype will be the text of the corresponding menu item. + + + + + + + &Section: + + + edtSection + + + + + + + The section name is used to organize the file types in menus. + + + + + + + &Variables: + + + edtVariables + + + + + + + <p>This string allows to configure Kate's settings for the files selected by this mimetype using Kate variables. Almost any configuration option can be set, such as highlight, indent-mode, encoding, etc.</p><p>For a full list of known variables, see the manual.</p> + + + + + + + &Highlighting: + + + cmbHl + + + + + + + + + + &Indentation Mode: + + + cmbIndenter + + + + + + + + + + File e&xtensions: + + + edtFileExtensions + + + + + + + The wildcards mask allows to select files by filename. A typical mask uses an asterisk and the file extension, for example <code>*.txt; *.text</code>. The string is a semicolon-separated list of masks. + + + + + + + MIME &types: + + + edtMimeTypes + + + + + + + 0 + + + + + The mime type mask allows to select files by mimetype. The string is a semicolon-separated list of mimetypes, for example <code>text/plain; text/english</code>. + + + + + + + Displays a wizard that helps you easily select mimetypes. + + + + + + + + + P&riority: + + + sbPriority + + + + + + + Sets priority for this file type. If more than one file type selects the same file, the one with the highest priority will be used. + + + + + + + + + + + + Qt::Horizontal + + + + 1 + 0 + + + + + + + + Download Highlighting Files... + + + + + + + + + Qt::Vertical + + + + 0 + 1 + + + + + + + + + KIntSpinBox + QSpinBox +
knuminput.h
+
+ + KLineEdit + QLineEdit +
klineedit.h
+
+ + KComboBox + QComboBox +
kcombobox.h
+
+ + VariableLineEdit + +
variablelineedit.h
+
+
+ + +
diff --git a/kate/part/mode/katemodeconfigpage.cpp b/kate/part/mode/katemodeconfigpage.cpp new file mode 100644 index 00000000..0e5e44cf --- /dev/null +++ b/kate/part/mode/katemodeconfigpage.cpp @@ -0,0 +1,320 @@ +/* This file is part of the KDE libraries and the Kate part. + * + * Copyright (C) 2001-2010 Christoph Cullmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +//BEGIN Includes +#include "katemodeconfigpage.h" +#include "moc_katemodeconfigpage.cpp" + +#include "katedocument.h" +#include "kateconfig.h" +#include "kateview.h" +#include "kateglobal.h" +#include "kateautoindent.h" +#include "katesyntaxmanager.h" +#include "katesyntaxdocument.h" + +#include "ui_filetypeconfigwidget.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define KATE_FT_HOWMANY 1024 +//END Includes + +ModeConfigPage::ModeConfigPage( QWidget *parent ) + : KateConfigPage( parent ) +{ + m_lastType = -1; + + // This will let us have more separation between this page and + // the KTabWidget edge (ereslibre) + QVBoxLayout *layout = new QVBoxLayout; + QWidget *newWidget = new QWidget(this); + + ui = new Ui::FileTypeConfigWidget(); + ui->setupUi( newWidget ); + + ui->cmbHl->addItem(i18n(""), QVariant("")); + for( int i = 0; i < KateHlManager::self()->highlights(); i++) { + if (KateHlManager::self()->hlSection(i).length() > 0) + ui->cmbHl->addItem(KateHlManager::self()->hlSection(i) + QString ("/") + + KateHlManager::self()->hlNameTranslated(i), QVariant(KateHlManager::self()->hlName(i))); + else + ui->cmbHl->addItem(KateHlManager::self()->hlNameTranslated(i), QVariant(KateHlManager::self()->hlName(i))); + } + + QStringList indentationModes; + indentationModes << i18n ("Use Default"); + indentationModes << KateAutoIndent::listModes(); + ui->cmbIndenter->addItems (indentationModes); + + connect( ui->cmbFiletypes, SIGNAL(activated(int)), this, SLOT(typeChanged(int)) ); + connect( ui->btnNew, SIGNAL(clicked()), this, SLOT(newType()) ); + connect( ui->btnDelete, SIGNAL(clicked()), this, SLOT(deleteType()) ); + ui->btnMimeTypes->setIcon(KIcon("tools-wizard")); + connect(ui->btnMimeTypes, SIGNAL(clicked()), this, SLOT(showMTDlg())); + connect( ui->btnDownload, SIGNAL(clicked()), this, SLOT(hlDownload()) ); + + reload(); + + connect( ui->edtName, SIGNAL(textChanged(QString)), this, SLOT(slotChanged()) ); + connect( ui->edtSection, SIGNAL(textChanged(QString)), this, SLOT(slotChanged()) ); + connect( ui->edtVariables, SIGNAL(textChanged(QString)), this, SLOT(slotChanged()) ); + connect( ui->edtFileExtensions, SIGNAL(textChanged(QString)), this, SLOT(slotChanged()) ); + connect( ui->edtMimeTypes, SIGNAL(textChanged(QString)), this, SLOT(slotChanged()) ); + connect( ui->sbPriority, SIGNAL(valueChanged(int)), this, SLOT(slotChanged()) ); + connect( ui->cmbHl, SIGNAL(activated(int)), this, SLOT(slotChanged()) ); + connect( ui->cmbIndenter, SIGNAL(activated(int)), this, SLOT(slotChanged()) ); + + layout->addWidget(newWidget); + setLayout(layout); +} + +ModeConfigPage::~ModeConfigPage () +{ + qDeleteAll (m_types); + delete ui; +} + +void ModeConfigPage::apply() +{ + if (!hasChanged()) + return; + + save (); + + KateGlobal::self()->modeManager()->save(m_types); +} + +void ModeConfigPage::reload() +{ + qDeleteAll (m_types); + m_types.clear(); + + // deep copy... + foreach (KateFileType *type, KateGlobal::self()->modeManager()->list()) + { + KateFileType *t = new KateFileType (); + *t = *type; + m_types.append (t); + } + + update (); +} + +void ModeConfigPage::reset() +{ + reload (); +} + +void ModeConfigPage::defaults() +{ + reload (); +} + +void ModeConfigPage::update () +{ + m_lastType = -1; + + ui->cmbFiletypes->clear (); + + foreach (KateFileType *type, m_types) { + if (!type->sectionTranslated().isEmpty()) + ui->cmbFiletypes->addItem(type->sectionTranslated() + QString ("/") + type->nameTranslated()); + else + ui->cmbFiletypes->addItem(type->nameTranslated()); + } + + // get current filetype from active view via the host application + int currentIndex = 0; + KTextEditor::MdiContainer *iface = qobject_cast(KateGlobal::self()->container()); + if (iface) { + KateView *kv = qobject_cast(iface->activeView()); + if (kv) { + const QString filetypeName = kv->doc()->fileType(); + for (int i = 0; i < m_types.size(); ++i) { + if (filetypeName == m_types[i]->name) { + currentIndex = i; + break; + } + } + } + } + ui->cmbFiletypes->setCurrentIndex (currentIndex); + typeChanged (currentIndex); + + ui->cmbFiletypes->setEnabled (ui->cmbFiletypes->count() > 0); +} + +void ModeConfigPage::deleteType () +{ + int type = ui->cmbFiletypes->currentIndex (); + + if (type > -1 && type < m_types.count()) + { + delete m_types[type]; + m_types.removeAt(type); + update (); + } +} + +void ModeConfigPage::newType () +{ + QString newN = i18n("New Filetype"); + + for (int i = 0; i < m_types.count(); ++i) { + KateFileType *type = m_types.at(i); + if (type->name == newN) + { + ui->cmbFiletypes->setCurrentIndex (i); + typeChanged (i); + return; + } + } + + KateFileType *newT = new KateFileType (); + newT->priority = 0; + newT->name = newN; + newT->hlGenerated = false; + + m_types.prepend (newT); + + update (); +} + +void ModeConfigPage::save () +{ + if (m_lastType != -1) + { + if (!m_types[m_lastType]->hlGenerated) { + m_types[m_lastType]->name = ui->edtName->text (); + m_types[m_lastType]->section = ui->edtSection->text (); + } + m_types[m_lastType]->varLine = ui->edtVariables->text (); + m_types[m_lastType]->wildcards = ui->edtFileExtensions->text().split (';', QString::SkipEmptyParts); + m_types[m_lastType]->mimetypes = ui->edtMimeTypes->text().split (';', QString::SkipEmptyParts); + m_types[m_lastType]->priority = ui->sbPriority->value(); + m_types[m_lastType]->hl = ui->cmbHl->itemData(ui->cmbHl->currentIndex()).toString(); + + if (ui->cmbIndenter->currentIndex() > 0) + m_types[m_lastType]->indenter = KateAutoIndent::modeName (ui->cmbIndenter->currentIndex() - 1); + else + m_types[m_lastType]->indenter = ""; + } +} + +void ModeConfigPage::typeChanged (int type) +{ + save (); + + ui->cmbHl->setEnabled (true); + ui->btnDelete->setEnabled (true); + ui->edtName->setEnabled (true); + ui->edtSection->setEnabled (true); + + if (type > -1 && type < m_types.count()) + { + KateFileType *t = m_types.at(type); + + ui->gbProperties->setTitle (i18n("Properties of %1", ui->cmbFiletypes->currentText())); + + ui->gbProperties->setEnabled (true); + ui->btnDelete->setEnabled (true); + + ui->edtName->setText(t->nameTranslated()); + ui->edtSection->setText(t->sectionTranslated()); + ui->edtVariables->setText(t->varLine); + ui->edtFileExtensions->setText(t->wildcards.join (";")); + ui->edtMimeTypes->setText(t->mimetypes.join (";")); + ui->sbPriority->setValue(t->priority); + + ui->cmbHl->setEnabled (!t->hlGenerated); + ui->btnDelete->setEnabled (!t->hlGenerated); + ui->edtName->setEnabled (!t->hlGenerated); + ui->edtSection->setEnabled (!t->hlGenerated); + + // activate current hl... + for (int i = 0; i < ui->cmbHl->count(); ++i) + if (ui->cmbHl->itemData (i).toString() == t->hl) + ui->cmbHl->setCurrentIndex (i); + + // activate the right indenter + int indenterIndex = 0; + if (!t->indenter.isEmpty()) + indenterIndex = KateAutoIndent::modeNumber (t->indenter) + 1; + ui->cmbIndenter->setCurrentIndex (indenterIndex); + } + else + { + ui->gbProperties->setTitle (i18n("Properties")); + + ui->gbProperties->setEnabled (false); + ui->btnDelete->setEnabled (false); + + ui->edtName->clear(); + ui->edtSection->clear(); + ui->edtVariables->clear(); + ui->edtFileExtensions->clear(); + ui->edtMimeTypes->clear(); + ui->sbPriority->setValue(0); + ui->cmbHl->setCurrentIndex (0); + ui->cmbIndenter->setCurrentIndex (0); + } + + m_lastType = type; +} + +void ModeConfigPage::showMTDlg() +{ + QString text = i18n("Select the MimeTypes you want for this file type.\nPlease note that this will automatically edit the associated file extensions as well."); + QStringList list = ui->edtMimeTypes->text().split( QRegExp("\\s*;\\s*"), QString::SkipEmptyParts ); + KMimeTypeChooserDialog d( i18n("Select Mime Types"), text, list, "text", this ); + if ( d.exec() == KDialog::Accepted ) { + // do some checking, warn user if mime types or patterns are removed. + // if the lists are empty, and the fields not, warn. + ui->edtFileExtensions->setText( d.chooser()->patterns().join(";") ); + ui->edtMimeTypes->setText( d.chooser()->mimeTypes().join(";") ); + } +} + +void ModeConfigPage::hlDownload() +{ + KateHlDownloadDialog diag(this,"hlDownload",true); + diag.exec(); +} + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/mode/katemodeconfigpage.h b/kate/part/mode/katemodeconfigpage.h new file mode 100644 index 00000000..11a4d089 --- /dev/null +++ b/kate/part/mode/katemodeconfigpage.h @@ -0,0 +1,69 @@ +/* This file is part of the KDE libraries and the Kate part. + * + * Copyright (C) 2001-2010 Christoph Cullmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef KATE_MODECONFIGPAGE_H__ +#define KATE_MODECONFIGPAGE_H__ + +#include +#include +#include + +#include "katedialogs.h" +#include "katemodemanager.h" + +QT_BEGIN_NAMESPACE +namespace Ui +{ + class FileTypeConfigWidget; +} +QT_END_NAMESPACE + +class ModeConfigPage : public KateConfigPage +{ + Q_OBJECT + + public: + explicit ModeConfigPage( QWidget *parent ); + ~ModeConfigPage (); + + public Q_SLOTS: + void apply(); + void reload(); + void reset(); + void defaults(); + + private Q_SLOTS: + void update (); + void deleteType (); + void newType (); + void typeChanged (int type); + void showMTDlg(); + void save (); + void hlDownload (); + + private: + Ui::FileTypeConfigWidget *ui; + + QList m_types; + int m_lastType; +}; + +#endif +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/mode/katemodemanager.cpp b/kate/part/mode/katemodemanager.cpp new file mode 100644 index 00000000..e7f7c0cc --- /dev/null +++ b/kate/part/mode/katemodemanager.cpp @@ -0,0 +1,314 @@ +/* This file is part of the KDE libraries and the Kate part. + * + * Copyright (C) 2001-2010 Christoph Cullmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +//BEGIN Includes +#include "katemodemanager.h" +#include "katewildcardmatcher.h" + +#include "katedocument.h" +#include "kateconfig.h" +#include "kateview.h" +#include "kateglobal.h" +#include "katesyntaxmanager.h" +#include "katesyntaxdocument.h" + +#include "ui_filetypeconfigwidget.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define KATE_FT_HOWMANY 1024 +//END Includes + +KateModeManager::KateModeManager () +{ + update (); +} + +KateModeManager::~KateModeManager () +{ + qDeleteAll (m_types); +} + +bool compareKateFileType(const KateFileType* const left, const KateFileType* const right) +{ + int comparison = left->section.compare(right->section, Qt::CaseInsensitive); + if (comparison == 0) { + comparison = left->name.compare(right->name, Qt::CaseInsensitive); + } + return comparison < 0; +} + +// +// read the types from config file and update the internal list +// +void KateModeManager::update () +{ + KConfig config ("katemoderc", KConfig::NoGlobals); + + QStringList g (config.groupList()); + + qDeleteAll (m_types); + m_types.clear (); + m_name2Type.clear (); + for (int z=0; z < g.count(); z++) + { + KConfigGroup cg(&config, g[z]); + + KateFileType *type = new KateFileType (); + type->number = z; + type->name = g[z]; + type->section = cg.readEntry ("Section"); + type->wildcards = cg.readXdgListEntry ("Wildcards"); + type->mimetypes = cg.readXdgListEntry ("Mimetypes"); + type->priority = cg.readEntry ("Priority", 0); + type->varLine = cg.readEntry ("Variables"); + type->indenter = cg.readEntry ("Indenter"); + + type->hl = cg.readEntry ("Highlighting"); + + // only for generated types... + type->hlGenerated = cg.readEntry ("Highlighting Generated", false); + type->version = cg.readEntry ("Highlighting Version"); + + // insert into the list + hash... + m_types.append(type); + m_name2Type.insert (type->name, type); + } + + // try if the hl stuff is up to date... + const KateSyntaxModeList &modes = KateHlManager::self()->syntaxDocument()->modeList(); + for (int i = 0; i < modes.size(); ++i) + { + KateFileType *type = 0; + bool newType = false; + if (m_name2Type.contains (modes[i]->name)) + type = m_name2Type[modes[i]->name]; + else + { + newType = true; + type = new KateFileType (); + type->name = modes[i]->name; + type->priority = 0; + m_types.append (type); + m_name2Type.insert (type->name, type); + } + + if (newType || type->version != modes[i]->version) + { + type->name = modes[i]->name; + type->section = modes[i]->section; + type->wildcards = modes[i]->extension.split (';', QString::SkipEmptyParts); + type->mimetypes = modes[i]->mimetype.split (';', QString::SkipEmptyParts); + type->priority = modes[i]->priority.toInt(); + type->version = modes[i]->version; + type->indenter = modes[i]->indenter; + type->hl = modes[i]->name; + type->hlGenerated = true; + } + } + + // sort the list... + qSort(m_types.begin(), m_types.end(), compareKateFileType); + + // add the none type... + KateFileType *t = new KateFileType (); + + // DO NOT TRANSLATE THIS, DONE LATER, marked by hlGenerated + t->name = "Normal"; + t->hl = "None"; + t->hlGenerated = true; + + m_types.prepend (t); +} + +// +// save the given list to config file + update +// +void KateModeManager::save (const QList& v) +{ + KConfig katerc("katemoderc", KConfig::NoGlobals); + + QStringList newg; + foreach (const KateFileType *type, v) + { + KConfigGroup config(&katerc, type->name); + + config.writeEntry ("Section", type->section); + config.writeXdgListEntry ("Wildcards", type->wildcards); + config.writeXdgListEntry ("Mimetypes", type->mimetypes); + config.writeEntry ("Priority", type->priority); + config.writeEntry ("Indenter", type->indenter); + + QString varLine = type->varLine; + if (QRegExp("kate:(.*)").indexIn(varLine) < 0) + varLine.prepend ("kate: "); + + config.writeEntry ("Variables", varLine); + + config.writeEntry ("Highlighting", type->hl); + + // only for generated types... + config.writeEntry ("Highlighting Generated", type->hlGenerated); + config.writeEntry ("Highlighting Version", type->version); + + newg << type->name; + } + + foreach (const QString &groupName, katerc.groupList()) + { + if (newg.indexOf (groupName) == -1) + { + katerc.deleteGroup (groupName); + } + } + + katerc.sync (); + + update (); +} + +QString KateModeManager::fileType (KateDocument *doc, const QString &fileToReadFrom) +{ + kDebug(13020); + if (!doc) + return ""; + + if (m_types.isEmpty()) + return ""; + + QString fileName = doc->url().prettyUrl(); + int length = doc->url().prettyUrl().length(); + + QString result; + + // Try wildcards + if ( ! fileName.isEmpty() ) + { + static const QStringList commonSuffixes = QString(".orig;.new;~;.bak;.BAK").split (';'); + + if (!(result = wildcardsFind(fileName)).isEmpty()) + return result; + + QString backupSuffix = KateDocumentConfig::global()->backupSuffix(); + if (fileName.endsWith(backupSuffix)) { + if (!(result = wildcardsFind(fileName.left(length - backupSuffix.length()))).isEmpty()) + return result; + } + + for (QStringList::ConstIterator it = commonSuffixes.begin(); it != commonSuffixes.end(); ++it) { + if (*it != backupSuffix && fileName.endsWith(*it)) { + if (!(result = wildcardsFind(fileName.left(length - (*it).length()))).isEmpty()) + return result; + } + } + } + + // Try content-based mimetype + KMimeType::Ptr mt; + if (!fileToReadFrom.isEmpty()) { + int accuracy = 0; + mt = KMimeType::findByFileContent(fileToReadFrom, &accuracy); + if (!mt) + mt = KMimeType::defaultMimeTypePtr(); + } else { + mt = doc->mimeTypeForContent(); + } + + QList types; + + foreach (KateFileType *type, m_types) + { + if (type->mimetypes.indexOf (mt->name()) > -1) + types.append (type); + } + + if ( !types.isEmpty() ) + { + int pri = -1; + QString name; + + foreach (KateFileType *type, types) + { + if (type->priority > pri) + { + pri = type->priority; + name = type->name; + } + } + + return name; + } + + + return ""; +} + +QString KateModeManager::wildcardsFind (const QString &fileName) +{ + KateFileType * match = NULL; + int minPrio = -1; + foreach (KateFileType *type, m_types) + { + if (type->priority <= minPrio) { + continue; + } + + foreach (const QString &wildcard, type->wildcards) + { + if (KateWildcardMatcher::exactMatch(fileName, wildcard)) { + match = type; + minPrio = type->priority; + break; + } + } + } + + return (match == NULL) ? "" : match->name; +} + +const KateFileType& KateModeManager::fileType(const QString &name) const +{ + for (int i = 0; i < m_types.size(); ++i) + if (m_types[i]->name == name) + return *m_types[i]; + + static KateFileType notype; + return notype; +} + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/mode/katemodemanager.h b/kate/part/mode/katemodemanager.h new file mode 100644 index 00000000..0ad4ecdf --- /dev/null +++ b/kate/part/mode/katemodemanager.h @@ -0,0 +1,106 @@ +/* This file is part of the KDE libraries and the Kate part. + * + * Copyright (C) 2001-2010 Christoph Cullmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef KATE_MODEMANAGER_H__ +#define KATE_MODEMANAGER_H__ + +#include +#include +#include + +#include + +#include "katedialogs.h" + +class KateDocument; + +class KateFileType +{ + public: + int number; + QString name; + QString section; + QStringList wildcards; + QStringList mimetypes; + int priority; + QString varLine; + QString hl; + bool hlGenerated; + QString version; + QString indenter; + + QString nameTranslated () const + { + // use "Language" as for highlightings, to avoid double work! + return hlGenerated ? i18nc("Language", name.toUtf8()) : name; + } + + QString sectionTranslated () const + { + // use "Language Section" as for highlightings, to avoid double work! + return hlGenerated ? i18nc("Language Section", section.toUtf8()) : section; + } + + KateFileType() + : number(-1), priority(0), hlGenerated(false) + {} +}; + +class KateModeManager +{ + public: + KateModeManager (); + ~KateModeManager (); + + /** + * File Type Config changed, update all docs (which will take care of views/renderers) + */ + void update (); + + void save (const QList& v); + + /** + * get the right fileType for the given document + * -1 if none found ! + */ + QString fileType (KateDocument *doc, const QString &fileToReadFrom); + + /** + * Don't store the pointer somewhere longer times, won't be valid after the next update() + */ + const KateFileType& fileType (const QString &name) const; + + /** + * Don't modify + */ + const QList& list() const { return m_types; } + + private: + QString wildcardsFind (const QString &fileName); + + private: + QList m_types; + QHash m_name2Type; + +}; + +#endif + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/mode/katemodemenu.cpp b/kate/part/mode/katemodemenu.cpp new file mode 100644 index 00000000..baa51558 --- /dev/null +++ b/kate/part/mode/katemodemenu.cpp @@ -0,0 +1,170 @@ +/* This file is part of the KDE libraries and the Kate part. + * + * Copyright (C) 2001-2010 Christoph Cullmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +//BEGIN Includes +#include "katemodemenu.h" +#include "moc_katemodemenu.cpp" + +#include "katedocument.h" +#include "kateconfig.h" +#include "kateview.h" +#include "kateglobal.h" +#include "katesyntaxmanager.h" +#include "katesyntaxdocument.h" + +#include "ui_filetypeconfigwidget.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define KATE_FT_HOWMANY 1024 +//END Includes + +void KateModeMenu::init() +{ + m_doc = 0; + + connect( menu(), SIGNAL(triggered(QAction*)), this, SLOT(setType(QAction*)) ); + + connect(menu(),SIGNAL(aboutToShow()),this,SLOT(slotAboutToShow())); + + m_actionGroup = new QActionGroup(menu()); +} + +KateModeMenu::~KateModeMenu( ) +{ + qDeleteAll(subMenus); +} + +void KateModeMenu::updateMenu (KTextEditor::Document *doc) +{ + m_doc = static_cast(doc); +} + +void KateModeMenu::slotAboutToShow() +{ + KateDocument *doc=m_doc; + int count = KateGlobal::self()->modeManager()->list().count(); + + for (int z=0; zmodeManager()->list().at(z)->name; + QString hlName = KateGlobal::self()->modeManager()->list().at(z)->nameTranslated(); + QString hlSection = KateGlobal::self()->modeManager()->list().at(z)->sectionTranslated(); + + if ( !hlSection.isEmpty() && !names.contains(hlName) ) + { + if (!subMenusName.contains(hlSection)) + { + subMenusName << hlSection; + QMenu *qmenu = new QMenu (hlSection); + connect( qmenu, SIGNAL(triggered(QAction*)), this, SLOT(setType(QAction*)) ); + subMenus.append(qmenu); + menu()->addMenu (qmenu); + } + + int m = subMenusName.indexOf (hlSection); + names << hlName; + QAction *action = subMenus.at(m)->addAction ( hlName ); + m_actionGroup->addAction(action); + action->setCheckable( true ); + action->setData( nameRaw ); + } + else if (!names.contains(hlName)) + { + names << hlName; + + disconnect( menu(), SIGNAL(triggered(QAction*)), this, SLOT(setType(QAction*)) ); + connect( menu(), SIGNAL(triggered(QAction*)), this, SLOT(setType(QAction*)) ); + + QAction *action = menu()->addAction ( hlName ); + m_actionGroup->addAction(action); + action->setCheckable( true ); + action->setData( nameRaw ); + } + } + + if (!doc) return; + + for (int i=0;i actions = subMenus.at( i )->actions(); + for ( int j = 0; j < actions.count(); ++j ) + actions[ j ]->setChecked( false ); + } + + QList actions = menu()->actions(); + for ( int i = 0; i < actions.count(); ++i ) + actions[ i ]->setChecked( false ); + + if (doc->fileType().isEmpty() || doc->fileType() == "Normal") { + for ( int i = 0; i < actions.count(); ++i ) { + if ( actions[ i ]->data().toString() == "Normal" ) + actions[ i ]->setChecked( true ); + } + } else { + if (!doc->fileType().isEmpty()) + { + const KateFileType& t = KateGlobal::self()->modeManager()->fileType(doc->fileType()); + int i = subMenusName.indexOf (t.section); + if (i >= 0 && subMenus.at(i)) { + QList actions = subMenus.at( i )->actions(); + for ( int j = 0; j < actions.count(); ++j ) { + if ( actions[ j ]->data().toString() == doc->fileType() ) + actions[ j ]->setChecked( true ); + } + } else { + QList actions = menu()->actions(); + for ( int j = 0; j < actions.count(); ++j ) { + if ( actions[ j ]->data().toString().isEmpty() ) + actions[ j ]->setChecked( true ); + } + } + } + } +} + +void KateModeMenu::setType (QAction *action) +{ + KateDocument *doc=m_doc; + + if (doc) { + doc->updateFileType(action->data().toString(), true); + } +} + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/mode/katemodemenu.h b/kate/part/mode/katemodemenu.h new file mode 100644 index 00000000..4cf63731 --- /dev/null +++ b/kate/part/mode/katemodemenu.h @@ -0,0 +1,63 @@ +/* This file is part of the KDE libraries and the Kate part. + * + * Copyright (C) 2001-2010 Christoph Cullmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef KATE_MODEMENU_H__ +#define KATE_MODEMENU_H__ + +#include +#include +#include + +#include "katedialogs.h" +#include "katemodemanager.h" + +class KateDocument; + +class KateModeMenu : public KActionMenu +{ + Q_OBJECT + + public: + KateModeMenu(const QString& text, QObject *parent) + : KActionMenu(text, parent) { init(); } + + ~KateModeMenu(); + + void updateMenu (KTextEditor::Document *doc); + + private: + void init(); + + QPointer m_doc; + QStringList subMenusName; + QStringList names; + QList subMenus; + QActionGroup *m_actionGroup; + + public Q_SLOTS: + void slotAboutToShow(); + + private Q_SLOTS: + void setType (QAction*); +}; + +#endif + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/mode/katewildcardmatcher.cpp b/kate/part/mode/katewildcardmatcher.cpp new file mode 100644 index 00000000..270fbe30 --- /dev/null +++ b/kate/part/mode/katewildcardmatcher.cpp @@ -0,0 +1,89 @@ +/* This file is part of the KDE libraries + Copyright (C) 2007 Sebastian Pipping + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "katewildcardmatcher.h" +#include +#include + + + +namespace KateWildcardMatcher { + + + +bool exactMatch(const QString & candidate, const QString & wildcard, int candidatePosFromRight, + int wildcardPosFromRight, bool caseSensitive = true) { + for (; wildcardPosFromRight >= 0; wildcardPosFromRight--) { + const ushort ch = wildcard[wildcardPosFromRight].unicode(); + switch (ch) { + case L'*': + if (candidatePosFromRight == -1) { + break; + } + + if (wildcardPosFromRight == 0) { + return true; + } + + // Eat all we can and go back as far as we have to + for (int j = -1; j <= candidatePosFromRight; j++) { + if (exactMatch(candidate, wildcard, j, wildcardPosFromRight - 1)) { + return true; + } + } + return false; + + case L'?': + if (candidatePosFromRight == -1) { + return false; + } + + candidatePosFromRight--; + break; + + default: + if (candidatePosFromRight == -1) { + return false; + } + + const ushort candidateCh = candidate[candidatePosFromRight].unicode(); + const bool match = caseSensitive + ? (candidateCh == ch) + : (QChar::toLower(candidateCh) == QChar::toLower(ch)); + if (match) { + candidatePosFromRight--; + } else { + return false; + } + } + } + return true; +} + + + +bool exactMatch(const QString & candidate, const QString & wildcard, + bool caseSensitive) { + return exactMatch(candidate, wildcard, candidate.length() - 1, + wildcard.length() - 1, caseSensitive); +} + + + +} + diff --git a/kate/part/mode/katewildcardmatcher.h b/kate/part/mode/katewildcardmatcher.h new file mode 100644 index 00000000..6205a139 --- /dev/null +++ b/kate/part/mode/katewildcardmatcher.h @@ -0,0 +1,47 @@ +/* This file is part of the KDE libraries + Copyright (C) 2007 Sebastian Pipping + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KATE_WILDCARD_MATCHER_H +#define KATE_WILDCARD_MATCHER_H + + + +#include + + + +namespace KateWildcardMatcher { + + /** + * Matches a string against a given wildcard. + * The wildcard supports '*' (".*" in regex) and '?' ("." in regex), not more. + * + * @param candidate Text to match + * @param wildcard Wildcard to use + * @param caseSensitive Case-sensitivity flag + * @return True for an exact match, false otherwise + */ + bool exactMatch(const QString & candidate, const QString & wildcard, + bool caseSensitive = true); + +} + + + +#endif // KATE_WILDCARD_MATCHER_H + diff --git a/kate/part/mode/testing/katewildcardmatcher_test.cpp b/kate/part/mode/testing/katewildcardmatcher_test.cpp new file mode 100644 index 00000000..99b54b80 --- /dev/null +++ b/kate/part/mode/testing/katewildcardmatcher_test.cpp @@ -0,0 +1,63 @@ +/* This file is part of the KDE libraries + Copyright (C) 2007 Sebastian Pipping + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "../katewildcardmatcher.h" +#include +#include + + + +bool testCase(const char * candidate, const char * wildcard) { + qDebug("\"%s\" / \"%s\"", candidate, wildcard); + return KateWildcardMatcher::exactMatch(QString(candidate), QString(wildcard)); +} + + + +int main() { + Q_ASSERT(testCase("abc.txt", "*.txt")); + Q_ASSERT(!testCase("abc.txt", "*.cpp")); + + Q_ASSERT(testCase("Makefile.am", "*Makefile*")); + + Q_ASSERT(testCase("control", "control")); + + Q_ASSERT(testCase("abcd", "a??d")); + + Q_ASSERT(testCase("a", "?")); + Q_ASSERT(testCase("a", "*?*")); + Q_ASSERT(testCase("a", "*")); + Q_ASSERT(testCase("a", "**")); + Q_ASSERT(testCase("a", "***")); + + Q_ASSERT(testCase("", "*")); + Q_ASSERT(testCase("", "**")); + Q_ASSERT(!testCase("", "?")); + + Q_ASSERT(testCase("ab", "a*")); + Q_ASSERT(testCase("ab", "*b")); + Q_ASSERT(testCase("ab", "a?")); + Q_ASSERT(testCase("ab", "?b")); + + Q_ASSERT(testCase("aXXbXXbYYaYc", "a*b*c")); + + + qDebug() << endl << "DONE"; + return 0; +} + diff --git a/kate/part/mode/testing/testing.pro b/kate/part/mode/testing/testing.pro new file mode 100644 index 00000000..1583780a --- /dev/null +++ b/kate/part/mode/testing/testing.pro @@ -0,0 +1,7 @@ +SOURCES += \ + ../katewildcardmatcher.cpp \ + katewildcardmatcher_test.cpp \ + +HEADERS += \ + ../katewildcardmatcher.h \ + diff --git a/kate/part/render/katelayoutcache.cpp b/kate/part/render/katelayoutcache.cpp new file mode 100644 index 00000000..f9d47c79 --- /dev/null +++ b/kate/part/render/katelayoutcache.cpp @@ -0,0 +1,570 @@ +/* This file is part of the KDE libraries and the Kate part. + * + * Copyright (C) 2005 Hamish Rodda + * Copyright (C) 2008 Dominik Haumann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "katelayoutcache.h" +#include "moc_katelayoutcache.cpp" + +#include + +#include "katerenderer.h" +#include "kateview.h" +#include "katedocument.h" +#include "katebuffer.h" + +static bool enableLayoutCache = false; + +//BEGIN KateLineLayoutMap +KateLineLayoutMap::KateLineLayoutMap() +{ +} + +KateLineLayoutMap::~KateLineLayoutMap() +{ +} + +bool lessThan(const KateLineLayoutMap::LineLayoutPair& lhs, + const KateLineLayoutMap::LineLayoutPair& rhs) +{ + return lhs.first < rhs.first; +} + +void KateLineLayoutMap::clear() +{ + m_lineLayouts.clear(); +} + +bool KateLineLayoutMap::contains(int i) const +{ + LineLayoutMap::const_iterator it = + qBinaryFind(m_lineLayouts.constBegin(), m_lineLayouts.constEnd(), LineLayoutPair(i,KateLineLayoutPtr()), lessThan); + return (it != m_lineLayouts.constEnd()); +} + +void KateLineLayoutMap::insert(int realLine, const KateLineLayoutPtr& lineLayoutPtr) +{ + LineLayoutMap::iterator it = + qBinaryFind(m_lineLayouts.begin(), m_lineLayouts.end(), LineLayoutPair(realLine,KateLineLayoutPtr()), lessThan); + if (it != m_lineLayouts.end()) { + (*it).second = lineLayoutPtr; + } else { + it = qUpperBound(m_lineLayouts.begin(), m_lineLayouts.end(), LineLayoutPair(realLine,KateLineLayoutPtr()), lessThan); + m_lineLayouts.insert(it, LineLayoutPair(realLine, lineLayoutPtr)); + } +} + +void KateLineLayoutMap::viewWidthIncreased() +{ + LineLayoutMap::iterator it = m_lineLayouts.begin(); + for ( ; it != m_lineLayouts.end(); ++it) { + if ((*it).second->isValid() && (*it).second->viewLineCount() > 1) + (*it).second->invalidateLayout(); + } +} + +void KateLineLayoutMap::viewWidthDecreased(int newWidth) +{ + LineLayoutMap::iterator it = m_lineLayouts.begin(); + for ( ; it != m_lineLayouts.end(); ++it) { + if ((*it).second->isValid() + && ((*it).second->viewLineCount() > 1 || (*it).second->width() > newWidth)) + (*it).second->invalidateLayout(); + } +} + +void KateLineLayoutMap::relayoutLines(int startRealLine, int endRealLine) +{ + LineLayoutMap::iterator start = + qLowerBound(m_lineLayouts.begin(), m_lineLayouts.end(), LineLayoutPair(startRealLine, KateLineLayoutPtr()), lessThan); + LineLayoutMap::iterator end = + qUpperBound(start, m_lineLayouts.end(), LineLayoutPair(endRealLine, KateLineLayoutPtr()), lessThan); + + while (start != end) { + (*start).second->setLayoutDirty(); + ++start; + } +} + +void KateLineLayoutMap::slotEditDone(int fromLine, int toLine, int shiftAmount) +{ + LineLayoutMap::iterator start = + qLowerBound(m_lineLayouts.begin(), m_lineLayouts.end(), LineLayoutPair(fromLine, KateLineLayoutPtr()), lessThan); + LineLayoutMap::iterator end = + qUpperBound(start, m_lineLayouts.end(), LineLayoutPair(toLine, KateLineLayoutPtr()), lessThan); + LineLayoutMap::iterator it; + + if (shiftAmount != 0) { + for (it = end; it != m_lineLayouts.end(); ++it) { + (*it).first += shiftAmount; + (*it).second->setLine((*it).second->line() + shiftAmount); + } + + for (it = start; it != end; ++it) { + (*it).second->clear(); + } + + m_lineLayouts.erase(start, end); + } else { + for (it = start; it != end; ++it) { + (*it).second->setLayoutDirty(); + } + } +} + + +KateLineLayoutPtr& KateLineLayoutMap::operator[](int i) +{ + LineLayoutMap::iterator it = + qBinaryFind(m_lineLayouts.begin(), m_lineLayouts.end(), LineLayoutPair(i, KateLineLayoutPtr()), lessThan); + return (*it).second; +} +//END KateLineLayoutMap + +KateLayoutCache::KateLayoutCache(KateRenderer* renderer, QObject* parent) + : QObject(parent) + , m_renderer(renderer) + , m_startPos(-1,-1) + , m_viewWidth(0) + , m_wrap(false) + , m_acceptDirtyLayouts (false) +{ + Q_ASSERT(m_renderer); + + /** + * connect to all possible editing primitives + */ + connect(&m_renderer->doc()->buffer(), SIGNAL(lineWrapped(KTextEditor::Cursor)), this, SLOT(wrapLine(KTextEditor::Cursor))); + connect(&m_renderer->doc()->buffer(), SIGNAL(lineUnwrapped(int)), this, SLOT(unwrapLine(int))); + connect(&m_renderer->doc()->buffer(), SIGNAL(textInserted(KTextEditor::Cursor,QString)), this, SLOT(insertText(KTextEditor::Cursor,QString))); + connect(&m_renderer->doc()->buffer(), SIGNAL(textRemoved(KTextEditor::Range,QString)), this, SLOT(removeText(KTextEditor::Range))); +} + +void KateLayoutCache::updateViewCache(const KTextEditor::Cursor& startPos, int newViewLineCount, int viewLinesScrolled) +{ + //kDebug( 13033 ) << startPos << " nvlc " << newViewLineCount << " vls " << viewLinesScrolled; + + int oldViewLineCount = m_textLayouts.count(); + if (newViewLineCount == -1) + newViewLineCount = oldViewLineCount; + + enableLayoutCache = true; + + int realLine; + if (newViewLineCount == -1) + realLine = m_renderer->folding().visibleLineToLine(m_renderer->folding().lineToVisibleLine(startPos.line())); + else + realLine = m_renderer->folding().visibleLineToLine(startPos.line()); + int _viewLine = 0; + + if (wrap()) { + // TODO check these assumptions are ok... probably they don't give much speedup anyway? + if (startPos == m_startPos && m_textLayouts.count()) { + _viewLine = m_textLayouts.first().viewLine(); + + } else if (viewLinesScrolled > 0 && viewLinesScrolled < m_textLayouts.count()) { + _viewLine = m_textLayouts[viewLinesScrolled].viewLine(); + + } else { + KateLineLayoutPtr l = line(realLine); + if (l) { + Q_ASSERT(l->isValid()); + Q_ASSERT(l->length() >= startPos.column() || m_renderer->view()->wrapCursor()); + + for (; _viewLine < l->viewLineCount(); ++_viewLine) { + const KateTextLayout& t = l->viewLine(_viewLine); + if (t.startCol() >= startPos.column() || _viewLine == l->viewLineCount() - 1) + goto foundViewLine; + } + + // FIXME FIXME need to calculate past-end-of-line position here... + Q_ASSERT(false); + + foundViewLine: + Q_ASSERT(true); + } + } + } + + m_startPos = startPos; + + // Move the text layouts if we've just scrolled... + if (viewLinesScrolled != 0) { + // loop backwards if we've just scrolled up... + bool forwards = viewLinesScrolled >= 0 ? true : false; + for (int z = forwards ? 0 : m_textLayouts.count() - 1; forwards ? (z < m_textLayouts.count()) : (z >= 0); forwards ? z++ : z--) { + int oldZ = z + viewLinesScrolled; + if (oldZ >= 0 && oldZ < m_textLayouts.count()) + m_textLayouts[z] = m_textLayouts[oldZ]; + } + } + + // Resize functionality + if (newViewLineCount > oldViewLineCount) { + m_textLayouts.reserve(newViewLineCount); + + } else if (newViewLineCount < oldViewLineCount) { + /* FIXME reintroduce... check we're not missing any + int lastLine = m_textLayouts[newSize - 1].line(); + for (int i = oldSize; i < newSize; i++) { + const KateTextLayout& layout = m_textLayouts[i]; + if (layout.line() > lastLine && !layout.viewLine()) + layout.kateLineLayout()->layout()->setCacheEnabled(false); + }*/ + m_textLayouts.resize(newViewLineCount); + } + + KateLineLayoutPtr l = line(realLine); + for (int i = 0; i < newViewLineCount; ++i) { + if (!l) { + if (i < m_textLayouts.count()) { + if (m_textLayouts[i].isValid()) + m_textLayouts[i] = KateTextLayout::invalid(); + } else { + m_textLayouts.append(KateTextLayout::invalid()); + } + continue; + } + + Q_ASSERT(l->isValid()); + Q_ASSERT(_viewLine < l->viewLineCount()); + + if (i < m_textLayouts.count()) { + bool dirty = false; + if (m_textLayouts[i].line() != realLine || m_textLayouts[i].viewLine() != _viewLine || (!m_textLayouts[i].isValid() && l->viewLine(_viewLine).isValid())) + dirty = true; + m_textLayouts[i] = l->viewLine(_viewLine); + if (dirty) + m_textLayouts[i].setDirty(true); + + } else { + m_textLayouts.append(l->viewLine(_viewLine)); + } + + //kDebug( 13033 ) << "Laid out line " << realLine << " (" << l << "), viewLine " << _viewLine << " (" << m_textLayouts[i].kateLineLayout().data() << ")"; + //m_textLayouts[i].debugOutput(); + + _viewLine++; + + if (_viewLine > l->viewLineCount() - 1) { + int virtualLine = l->virtualLine() + 1; + realLine = m_renderer->folding().visibleLineToLine(virtualLine); + _viewLine = 0; + if (realLine < m_renderer->doc()->lines()) { + l = line(realLine, virtualLine); + } else { + l = 0; + } + } + } + + enableLayoutCache = false; +} + +KateLineLayoutPtr KateLayoutCache::line( int realLine, int virtualLine ) +{ + if (m_lineLayouts.contains(realLine)) { + KateLineLayoutPtr l = m_lineLayouts[realLine]; + + // ensure line is OK + Q_ASSERT (l->line() == realLine); + Q_ASSERT (realLine < m_renderer->doc()->buffer().lines()); + + if (virtualLine != -1) + l->setVirtualLine(virtualLine); + + if (!l->isValid()) + { + l->setUsePlainTextLine (acceptDirtyLayouts()); + l->textLine (!acceptDirtyLayouts()); + m_renderer->layoutLine(l, wrap() ? m_viewWidth : -1, enableLayoutCache); + } + else if (l->isLayoutDirty() && !acceptDirtyLayouts()) + { + // reset textline + l->setUsePlainTextLine (false); + l->textLine (true); + m_renderer->layoutLine(l, wrap() ? m_viewWidth : -1, enableLayoutCache); + } + + Q_ASSERT(l->isValid() && (!l->isLayoutDirty() || acceptDirtyLayouts())); + + return l; + } + + if (realLine < 0 || realLine >= m_renderer->doc()->lines()) + return KateLineLayoutPtr(); + + KateLineLayoutPtr l(new KateLineLayout(*m_renderer)); + l->setLine(realLine, virtualLine); + + // Mark it dirty, because it may not have the syntax highlighting applied + // mark this here, to allow layoutLine to use plainLines... + if (acceptDirtyLayouts()) + l->setUsePlainTextLine (true); + + m_renderer->layoutLine(l, wrap() ? m_viewWidth : -1, enableLayoutCache); + Q_ASSERT(l->isValid()); + + if (acceptDirtyLayouts()) + l->setLayoutDirty (true); + + m_lineLayouts.insert(realLine, l); + return l; +} + +KateLineLayoutPtr KateLayoutCache::line( const KTextEditor::Cursor & realCursor ) +{ + return line(realCursor.line()); +} + +KateTextLayout KateLayoutCache::textLayout( const KTextEditor::Cursor & realCursor ) +{ + /*if (realCursor >= viewCacheStart() && (realCursor < viewCacheEnd() || realCursor == viewCacheEnd() && !m_textLayouts.last().wrap())) + foreach (const KateTextLayout& l, m_textLayouts) + if (l.line() == realCursor.line() && (l.endCol() < realCursor.column() || !l.wrap())) + return l;*/ + + return line(realCursor.line())->viewLine(viewLine(realCursor)); +} + +KateTextLayout KateLayoutCache::textLayout( uint realLine, int _viewLine ) +{ + /*if (m_textLayouts.count() && (realLine >= m_textLayouts.first().line() && _viewLine >= m_textLayouts.first().viewLine()) && + (realLine <= m_textLayouts.last().line() && _viewLine <= m_textLayouts.first().viewLine())) + foreach (const KateTextLayout& l, m_textLayouts) + if (l.line() == realLine && l.viewLine() == _viewLine) + return const_cast(l);*/ + + return line(realLine)->viewLine(_viewLine); +} + +KateTextLayout & KateLayoutCache::viewLine( int _viewLine ) +{ + Q_ASSERT(_viewLine >= 0 && _viewLine < m_textLayouts.count()); + return m_textLayouts[_viewLine]; +} + +int KateLayoutCache::viewCacheLineCount( ) const +{ + return m_textLayouts.count(); +} + +KTextEditor::Cursor KateLayoutCache::viewCacheStart( ) const +{ + return m_textLayouts.count() ? m_textLayouts.first().start() : KTextEditor::Cursor(); +} + +KTextEditor::Cursor KateLayoutCache::viewCacheEnd( ) const +{ + return m_textLayouts.count() ? m_textLayouts.last().end() : KTextEditor::Cursor(); +} + +int KateLayoutCache::viewWidth( ) const +{ + return m_viewWidth; +} + +/** + * This returns the view line upon which realCursor is situated. + * The view line is the number of lines in the view from the first line + * The supplied cursor should be in real lines. + */ +int KateLayoutCache::viewLine(const KTextEditor::Cursor& realCursor) +{ + if (realCursor.column() <= 0 || realCursor.line() < 0) return 0; + + Q_ASSERT(realCursor.line() < m_renderer->doc()->lines()); + KateLineLayoutPtr thisLine = line(realCursor.line()); + + for (int i = 0; i < thisLine->viewLineCount(); ++i) { + const KateTextLayout& l = thisLine->viewLine(i); + if (realCursor.column() >= l.startCol() && realCursor.column() < l.endCol()) + return i; + } + + return thisLine->viewLineCount() - 1; +} + +int KateLayoutCache::displayViewLine(const KTextEditor::Cursor& virtualCursor, bool limitToVisible) +{ + if (!virtualCursor.isValid()) + return -1; + + KTextEditor::Cursor work = viewCacheStart(); + + // only try this with valid lines! + if (work.isValid()) + work.setLine(m_renderer->folding().lineToVisibleLine(work.line())); + + if (!work.isValid()) + return virtualCursor.line(); + + int limit = m_textLayouts.count(); + + // Efficient non-word-wrapped path + if (!m_renderer->view()->dynWordWrap()) { + int ret = virtualCursor.line() - work.line(); + if (limitToVisible && (ret < 0 || ret > limit)) + return -1; + else + return ret; + } + + if (work == virtualCursor) { + return 0; + } + + int ret = -(int)viewLine(viewCacheStart()); + bool forwards = (work < virtualCursor); + + // FIXME switch to using ranges? faster? + if (forwards) { + while (work.line() != virtualCursor.line()) { + ret += viewLineCount(m_renderer->folding().visibleLineToLine(work.line())); + work.setLine(work.line() + 1); + if (limitToVisible && ret > limit) + return -1; + } + } else { + while (work.line() != virtualCursor.line()) { + work.setLine(work.line() - 1); + ret -= viewLineCount(m_renderer->folding().visibleLineToLine(work.line())); + if (limitToVisible && ret < 0) + return -1; + } + } + + // final difference + KTextEditor::Cursor realCursor = virtualCursor; + realCursor.setLine(m_renderer->folding().visibleLineToLine(realCursor.line())); + if (realCursor.column() == -1) realCursor.setColumn(m_renderer->doc()->lineLength(realCursor.line())); + ret += viewLine(realCursor); + + if (limitToVisible && (ret < 0 || ret > limit)) + return -1; + + return ret; +} + +int KateLayoutCache::lastViewLine(int realLine) +{ + if (!m_renderer->view()->dynWordWrap()) return 0; + + KateLineLayoutPtr l = line(realLine); + Q_ASSERT(l); + return l->viewLineCount() - 1; +} + +int KateLayoutCache::viewLineCount(int realLine) +{ + return lastViewLine(realLine) + 1; +} + +void KateLayoutCache::viewCacheDebugOutput( ) const +{ + kDebug( 13033 ) << "Printing values for " << m_textLayouts.count() << " lines:"; + if (m_textLayouts.count()) + { + foreach (const KateTextLayout& t, m_textLayouts) + if (t.isValid()) + { + t.debugOutput(); + } + else + { + kDebug( 13033 ) << "Line Invalid."; + } + } +} + +void KateLayoutCache::wrapLine (const KTextEditor::Cursor &position) +{ + m_lineLayouts.slotEditDone (position.line(), position.line() + 1, 1); +} + +void KateLayoutCache::unwrapLine (int line) +{ + m_lineLayouts.slotEditDone (line - 1, line, -1); +} + +void KateLayoutCache::insertText (const KTextEditor::Cursor &position, const QString &) +{ + m_lineLayouts.slotEditDone(position.line(), position.line(), 0); +} + +void KateLayoutCache::removeText (const KTextEditor::Range &range) +{ + m_lineLayouts.slotEditDone(range.start().line(), range.start().line(), 0); +} + +void KateLayoutCache::clear( ) +{ + m_textLayouts.clear(); + m_lineLayouts.clear(); + m_startPos = KTextEditor::Cursor(-1,-1); +} + +void KateLayoutCache::setViewWidth( int width ) +{ + bool wider = width > m_viewWidth; + + m_viewWidth = width; + + m_lineLayouts.clear(); + m_startPos = KTextEditor::Cursor(-1,-1); + + // Only get rid of layouts that we have to + if (wider) { + m_lineLayouts.viewWidthIncreased(); + } else { + m_lineLayouts.viewWidthDecreased(width); + } +} + +bool KateLayoutCache::wrap( ) const +{ + return m_wrap; +} + +void KateLayoutCache::setWrap( bool wrap ) +{ + m_wrap = wrap; + clear(); +} + +void KateLayoutCache::relayoutLines( int startRealLine, int endRealLine ) +{ + if (startRealLine > endRealLine) + kWarning() << "start" << startRealLine << "before end" << endRealLine; + + m_lineLayouts.relayoutLines(startRealLine, endRealLine); +} + +bool KateLayoutCache::acceptDirtyLayouts() +{ + return m_acceptDirtyLayouts; +} + +void KateLayoutCache::setAcceptDirtyLayouts(bool accept) +{ + m_acceptDirtyLayouts = accept; +} + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/render/katelayoutcache.h b/kate/part/render/katelayoutcache.h new file mode 100644 index 00000000..50130fa8 --- /dev/null +++ b/kate/part/render/katelayoutcache.h @@ -0,0 +1,172 @@ +/* This file is part of the KDE libraries and the Kate part. + * + * Copyright (C) 2005 Hamish Rodda + * Copyright (C) 2008 Dominik Haumann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef KATELAYOUTCACHE_H +#define KATELAYOUTCACHE_H + +#include + +#include + +#include "katetextlayout.h" + +class KateRenderer; + +namespace KTextEditor { class Document; } + +class KateLineLayoutMap +{ + public: + KateLineLayoutMap(); + ~KateLineLayoutMap(); + + inline void clear(); + + inline bool contains(int i) const; + + inline void insert(int realLine, const KateLineLayoutPtr& lineLayoutPtr); + + inline void viewWidthIncreased(); + inline void viewWidthDecreased(int newWidth); + + inline void relayoutLines(int startRealLine, int endRealLine); + + inline void slotEditDone(int fromLine, int toLine, int shiftAmount); + + KateLineLayoutPtr& operator[](int i); + + typedef QPair LineLayoutPair; + private: + typedef QVector LineLayoutMap; + LineLayoutMap m_lineLayouts; +}; + +/** + * This class handles Kate's caching of layouting information (in KateLineLayout + * and KateTextLayout). This information is used primarily by both the view and + * the renderer. + * + * We outsource the hardcore layouting logic to the renderer, but other than + * that, this class handles all manipulation of the layout objects. + * + * This is separate from the renderer 1) for clarity 2) so you can have separate + * caches for separate views of the same document, even for view and printer + * (if the renderer is made to support rendering onto different targets). + * + * @author Hamish Rodda \ + */ + +class KateLayoutCache : public QObject +{ + Q_OBJECT + + public: + explicit KateLayoutCache(KateRenderer* renderer, QObject* parent); + + void clear(); + + int viewWidth() const; + void setViewWidth(int width); + + bool wrap() const; + void setWrap(bool wrap); + + bool acceptDirtyLayouts(); + void setAcceptDirtyLayouts(bool accept); + + // BEGIN generic methods to get/set layouts + /** + * Returns the KateLineLayout for the specified line. + * + * If one does not exist, it will be created and laid out. + * Layouts which are not directly part of the view will be kept until the + * cache is full or until they are invalidated by other means (eg. the text + * changes). + * + * \param realLine real line number of the layout to retrieve. + * \param virtualLine virtual line number. only needed if you think it may have changed + * (ie. basically internal to KateLayoutCache) + */ + KateLineLayoutPtr line(int realLine, int virtualLine = -1); + /// \overload + KateLineLayoutPtr line(const KTextEditor::Cursor& realCursor); + + /// Returns the layout describing the text line which is occupied by \p realCursor. + KateTextLayout textLayout(const KTextEditor::Cursor& realCursor); + + /// Returns the layout of the specified realLine + viewLine. + /// if viewLine is -1, return the last. + KateTextLayout textLayout(uint realLine, int viewLine); + // END + + // BEGIN methods to do with the caching of lines visible within a view + /// Returns the layout of the corresponding line in the view + KateTextLayout& viewLine(int viewLine); + + // find the view line of the cursor, relative to the display (0 = top line of view, 1 = second line, etc.) + // if limitToVisible is true, only lines which are currently visible will be searched for, and -1 returned if the line is not visible. + int displayViewLine(const KTextEditor::Cursor& virtualCursor, bool limitToVisible = false); + + int viewCacheLineCount() const; + KTextEditor::Cursor viewCacheStart() const; + KTextEditor::Cursor viewCacheEnd() const; + void updateViewCache(const KTextEditor::Cursor& startPos, int newViewLineCount = -1, int viewLinesScrolled = 0); + + void relayoutLines(int startRealLine, int endRealLine); + + // find the index of the last view line for a specific line + int lastViewLine(int realLine); + // find the view line of cursor c (0 = same line, 1 = down one, etc.) + int viewLine(const KTextEditor::Cursor& realCursor); + int viewLineCount(int realLine); + + void viewCacheDebugOutput() const; + // END + +private Q_SLOTS: + void wrapLine (const KTextEditor::Cursor &position); + void unwrapLine (int line); + void insertText (const KTextEditor::Cursor &position, const QString &text); + void removeText (const KTextEditor::Range &range); + +private: + KateRenderer* m_renderer; + + /** + * The master cache of all line layouts. + * + * Layouts which are not within the current view cache and whose + * refcount == 1 are only known to the cache and can be safely deleted. + */ + mutable KateLineLayoutMap m_lineLayouts; + + // Convenience vector for quick direct access to the specific text layout + KTextEditor::Cursor m_startPos; + mutable QVector m_textLayouts; + + int m_viewWidth; + bool m_wrap; + bool m_acceptDirtyLayouts; +}; + +#endif + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/render/katelinelayout.cpp b/kate/part/render/katelinelayout.cpp new file mode 100644 index 00000000..eec1e15b --- /dev/null +++ b/kate/part/render/katelinelayout.cpp @@ -0,0 +1,252 @@ +/* This file is part of the KDE libraries + Copyright (C) 2002-2005 Hamish Rodda + Copyright (C) 2003 Anakim Border + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "katelinelayout.h" +#include "katetextlayout.h" +#include "katetextfolding.h" + +#include + +#include + +#include "katedocument.h" +#include "katerenderer.h" + +KateLineLayout::KateLineLayout(KateRenderer &renderer) + : m_renderer(renderer) + , m_textLine(0L) + , m_line(-1) + , m_virtualLine(-1) + , m_shiftX(0) + , m_layout(0L) + , m_layoutDirty(true) + , m_usePlainTextLine(false) +{ +} + +KateLineLayout::~KateLineLayout() +{ + delete m_layout; +} + +void KateLineLayout::clear() +{ + m_textLine = Kate::TextLine (); + m_line = -1; + m_virtualLine = -1; + m_shiftX = 0; + // not touching dirty + delete m_layout; + m_layout = 0L; + // not touching layout dirty +} + +bool KateLineLayout::includesCursor(const KTextEditor::Cursor& realCursor) const +{ + return realCursor.line() == line(); +} + +const Kate::TextLine& KateLineLayout::textLine(bool reloadForce) const +{ + if (reloadForce || !m_textLine) + m_textLine = usePlainTextLine() ? m_renderer.doc()->plainKateTextLine (line()) : m_renderer.doc()->kateTextLine(line()); + + Q_ASSERT(m_textLine); + + return m_textLine; +} + +int KateLineLayout::line( ) const +{ + return m_line; +} + +void KateLineLayout::setLine( int line, int virtualLine ) +{ + m_line = line; + m_virtualLine = (virtualLine == -1) ? m_renderer.folding().lineToVisibleLine (line) : virtualLine; + m_textLine = Kate::TextLine (); +} + +int KateLineLayout::virtualLine( ) const +{ + return m_virtualLine; +} + +void KateLineLayout::setVirtualLine( int virtualLine ) +{ + m_virtualLine = virtualLine; +} + +bool KateLineLayout::startsInvisibleBlock() const +{ + if (!isValid()) + return false; + + return (virtualLine() + 1) != m_renderer.folding().lineToVisibleLine (line() + 1); +} + +int KateLineLayout::shiftX() const +{ + return m_shiftX; +} + +void KateLineLayout::setShiftX(int shiftX) +{ + m_shiftX = shiftX; +} + +KateDocument* KateLineLayout::doc() const +{ + return m_renderer.doc(); +} + +bool KateLineLayout::isValid( ) const +{ + return line() != -1 && layout() && textLine(); +} + +QTextLayout* KateLineLayout::layout() const +{ + return m_layout; +} + +void KateLineLayout::setLayout(QTextLayout* layout) +{ + if (m_layout != layout) { + delete m_layout; + m_layout = layout; + } + + m_layoutDirty = !m_layout; + m_dirtyList.clear(); + if (m_layout) + for (int i = 0; i < qMax(1, m_layout->lineCount()); ++i) + m_dirtyList.append(true); +} + +void KateLineLayout::invalidateLayout( ) +{ + setLayout(0L); +} + +bool KateLineLayout::isDirty( int viewLine ) const +{ + Q_ASSERT(isValid() && viewLine >= 0 && viewLine < viewLineCount()); + return m_dirtyList[viewLine]; +} + +bool KateLineLayout::setDirty( int viewLine, bool dirty ) +{ + Q_ASSERT(isValid() && viewLine >= 0 && viewLine < viewLineCount()); + m_dirtyList[viewLine] = dirty; + return dirty; +} + +KTextEditor::Cursor KateLineLayout::start( ) const +{ + return KTextEditor::Cursor(line(), 0); +} + +int KateLineLayout::length( ) const +{ + return textLine()->length(); +} + +int KateLineLayout::viewLineCount( ) const +{ + return m_layout->lineCount(); +} + +KateTextLayout KateLineLayout::viewLine( int viewLine ) const +{ + if (viewLine < 0) + viewLine += viewLineCount(); + Q_ASSERT(isValid()); + Q_ASSERT(viewLine >= 0 && viewLine < viewLineCount()); + return KateTextLayout(KateLineLayoutPtr(const_cast(this)), viewLine); +} + +int KateLineLayout::width( ) const +{ + int width = 0; + + for (int i = 0; i < m_layout->lineCount(); ++i) + width = qMax((int)m_layout->lineAt(i).naturalTextWidth(), width); + + return width; +} + +int KateLineLayout::widthOfLastLine( ) const +{ + const KateTextLayout& lastLine = viewLine(viewLineCount() - 1); + return lastLine.width() + lastLine.xOffset(); +} + +bool KateLineLayout::isOutsideDocument( ) const +{ + return line() < 0 || line() >= m_renderer.doc()->lines(); +} + +void KateLineLayout::debugOutput() const +{ + kDebug( 13033 ) << "KateLineLayout: " << this << " valid " << isValid() << " line " << line() << " length " << length() << " width " << width() << " viewLineCount " << viewLineCount(); +} + +int KateLineLayout::viewLineForColumn( int column ) const +{ + int len = 0; + int i = 0; + for (; i < m_layout->lineCount() - 1; ++i) { + len += m_layout->lineAt(i).textLength(); + if (column < len) + return i; + } + return i; +} + +bool KateLineLayout::isLayoutDirty( ) const +{ + return m_layoutDirty; +} + +void KateLineLayout::setLayoutDirty( bool dirty ) +{ + m_layoutDirty = dirty; +} + +bool KateLineLayout::usePlainTextLine () const +{ + return m_usePlainTextLine; +} + +void KateLineLayout::setUsePlainTextLine (bool plain) +{ + m_usePlainTextLine = plain; +} + +bool KateLineLayout::isRightToLeft() const +{ + if (!m_layout) + return false; + + return m_layout->textOption().textDirection() == Qt::RightToLeft; +} + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/render/katelinelayout.h b/kate/part/render/katelinelayout.h new file mode 100644 index 00000000..86f29047 --- /dev/null +++ b/kate/part/render/katelinelayout.h @@ -0,0 +1,122 @@ +/* This file is part of the KDE libraries + Copyright (C) 2002-2005 Hamish Rodda + Copyright (C) 2003 Anakim Border + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef _KATE_LINELAYOUT_H_ +#define _KATE_LINELAYOUT_H_ + +#include + +#include "katetextline.h" + +#include + +#include +class KateDocument; +class KateTextLayout; +class KateRenderer; + +class KateLineLayout : public QSharedData +{ + public: + KateLineLayout(KateRenderer &renderer); + ~KateLineLayout(); + + KateDocument* doc() const; + void debugOutput() const; + + void clear(); + bool isValid() const; + bool isOutsideDocument() const; + + bool isRightToLeft() const; + + bool includesCursor(const KTextEditor::Cursor& realCursor) const; + + friend bool operator> (const KateLineLayout& r, const KTextEditor::Cursor& c); + friend bool operator>= (const KateLineLayout& r, const KTextEditor::Cursor& c); + friend bool operator< (const KateLineLayout& r, const KTextEditor::Cursor& c); + friend bool operator<= (const KateLineLayout& r, const KTextEditor::Cursor& c); + + const Kate::TextLine& textLine(bool forceReload = false) const; + int length() const; + + int line() const; + /** + * Only pass virtualLine if you know it (and thus we shouldn't try to look it up) + */ + void setLine(int line, int virtualLine = -1); + KTextEditor::Cursor start() const; + + int virtualLine() const; + void setVirtualLine(int virtualLine); + + bool isDirty(int viewLine) const; + bool setDirty(int viewLine, bool dirty = true); + + int width() const; + int widthOfLastLine() const; + + int viewLineCount() const; + KateTextLayout viewLine(int viewLine) const; + int viewLineForColumn(int column) const; + + bool startsInvisibleBlock() const; + + // This variable is used as follows: + // non-dynamic-wrapping mode: unused + // dynamic wrapping mode: + // first viewLine of a line: the X position of the first non-whitespace char + // subsequent viewLines: the X offset from the left of the display. + // + // this is used to provide a dynamic-wrapping-retains-indent feature. + int shiftX() const; + void setShiftX(int shiftX); + + QTextLayout* layout() const; + void setLayout(QTextLayout* layout); + void invalidateLayout(); + + bool isLayoutDirty() const; + void setLayoutDirty(bool dirty = true); + + bool usePlainTextLine () const; + void setUsePlainTextLine (bool plain = true); + +private: + // Disable copy + KateLineLayout(const KateLineLayout& copy); + + QTextLayout* takeLayout() const; + + KateRenderer &m_renderer; + mutable Kate::TextLine m_textLine; + int m_line; + int m_virtualLine; + int m_shiftX; + + QTextLayout* m_layout; + QList m_dirtyList; + + bool m_layoutDirty; + bool m_usePlainTextLine; +}; + +typedef KSharedPtr KateLineLayoutPtr; + +#endif diff --git a/kate/part/render/katerenderer.cpp b/kate/part/render/katerenderer.cpp new file mode 100644 index 00000000..0a897e01 --- /dev/null +++ b/kate/part/render/katerenderer.cpp @@ -0,0 +1,1088 @@ +/* This file is part of the KDE libraries + Copyright (C) 2007 Mirko Stocker + Copyright (C) 2003-2005 Hamish Rodda + Copyright (C) 2001 Christoph Cullmann + Copyright (C) 2001 Joseph Wenninger + Copyright (C) 1999 Jochen Wilhelmy + Copyright (C) 2013 Andrey Matveyakin + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "katerenderer.h" + +#include "katedocument.h" +#include "kateconfig.h" +#include "katehighlight.h" +#include "kateview.h" +#include "katerenderrange.h" +#include "katetextlayout.h" +#include "katebuffer.h" + +#include + +#include + +#include +#include +#include +#include + +#include + +static const QChar tabChar('\t'); +static const QChar spaceChar(' '); +static const QChar nbSpaceChar(0xa0); // non-breaking space + +KateRenderer::KateRenderer(KateDocument* doc, Kate::TextFolding &folding, KateView *view) + : m_doc(doc) + , m_folding (folding) + , m_view (view) + , m_tabWidth(m_doc->config()->tabWidth()) + , m_indentWidth(m_doc->config()->indentationWidth()) + , m_caretStyle(KateRenderer::Line) + , m_drawCaret(true) + , m_showSelections(true) + , m_showTabs(true) + , m_showSpaces(true) + , m_printerFriendly(false) + , m_config(new KateRendererConfig(this)) +{ + updateAttributes (); + + // initialize with a sane font height + updateFontHeight (); +} + +KateRenderer::~KateRenderer() +{ + delete m_config; +} + +void KateRenderer::updateAttributes () +{ + m_attributes = m_doc->highlight()->attributes (config()->schema ()); +} + +KTextEditor::Attribute::Ptr KateRenderer::attribute(uint pos) const +{ + if (pos < (uint)m_attributes.count()) + return m_attributes[pos]; + + return m_attributes[0]; +} + +KTextEditor::Attribute::Ptr KateRenderer::specificAttribute( int context ) const +{ + if (context >= 0 && context < m_attributes.count()) + return m_attributes[context]; + + return m_attributes[0]; +} + +void KateRenderer::setDrawCaret(bool drawCaret) +{ + m_drawCaret = drawCaret; +} + +void KateRenderer::setCaretStyle(KateRenderer::caretStyles style) +{ + m_caretStyle = style; +} + +void KateRenderer::setShowTabs(bool showTabs) +{ + m_showTabs = showTabs; +} + +void KateRenderer::setShowTrailingSpaces(bool showSpaces) +{ + m_showSpaces = showSpaces; +} + +void KateRenderer::setTabWidth(int tabWidth) +{ + m_tabWidth = tabWidth; +} + +bool KateRenderer::showIndentLines() const +{ + return m_config->showIndentationLines(); +} + +void KateRenderer::setShowIndentLines(bool showIndentLines) +{ + m_config->setShowIndentationLines(showIndentLines); +} + +void KateRenderer::setIndentWidth(int indentWidth) +{ + m_indentWidth = indentWidth; +} + +void KateRenderer::setShowSelections(bool showSelections) +{ + m_showSelections = showSelections; +} + +void KateRenderer::increaseFontSizes() +{ + QFont f ( config()->font () ); + f.setPointSize (f.pointSize ()+1); + + config()->setFont (f); +} + +void KateRenderer::decreaseFontSizes() +{ + QFont f ( config()->font () ); + + if ((f.pointSize ()-1) > 0) + f.setPointSize (f.pointSize ()-1); + + config()->setFont (f); +} + +bool KateRenderer::isPrinterFriendly() const +{ + return m_printerFriendly; +} + +void KateRenderer::setPrinterFriendly(bool printerFriendly) +{ + m_printerFriendly = printerFriendly; + setShowTabs(false); + setShowTrailingSpaces(false); + setShowSelections(false); + setDrawCaret(false); +} + +void KateRenderer::paintTextLineBackground(QPainter& paint, KateLineLayoutPtr layout, int currentViewLine, int xStart, int xEnd) +{ + if (isPrinterFriendly()) + return; + + // Normal background color + QColor backgroundColor( config()->backgroundColor() ); + + // paint the current line background if we're on the current line + QColor currentLineColor = config()->highlightedLineColor(); + + // Check for mark background + int markRed = 0, markGreen = 0, markBlue = 0, markCount = 0; + + // Retrieve marks for this line + uint mrk = m_doc->mark( layout->line() ); + if (mrk) + { + for (uint bit = 0; bit < 32; bit++) + { + KTextEditor::MarkInterface::MarkTypes markType = (KTextEditor::MarkInterface::MarkTypes)(1<lineMarkerColor(markType); + + if (markColor.isValid()) { + markCount++; + markRed += markColor.red(); + markGreen += markColor.green(); + markBlue += markColor.blue(); + } + } + } // for + } // Marks + + if (markCount) { + markRed /= markCount; + markGreen /= markCount; + markBlue /= markCount; + backgroundColor.setRgb( + int((backgroundColor.red() * 0.9) + (markRed * 0.1)), + int((backgroundColor.green() * 0.9) + (markGreen * 0.1)), + int((backgroundColor.blue() * 0.9) + (markBlue * 0.1)) + ); + } + + // Draw line background + paint.fillRect(0, 0, xEnd - xStart, lineHeight() * layout->viewLineCount(), backgroundColor); + + // paint the current line background if we're on the current line + if (currentViewLine != -1) { + if (markCount) { + markRed /= markCount; + markGreen /= markCount; + markBlue /= markCount; + currentLineColor.setRgb( + int((currentLineColor.red() * 0.9) + (markRed * 0.1)), + int((currentLineColor.green() * 0.9) + (markGreen * 0.1)), + int((currentLineColor.blue() * 0.9) + (markBlue * 0.1)) + ); + } + + paint.fillRect(0, lineHeight() * currentViewLine, xEnd - xStart, lineHeight(), currentLineColor); + } +} + +void KateRenderer::paintTabstop(QPainter &paint, qreal x, qreal y) +{ + QPen penBackup( paint.pen() ); + QPen pen( config()->tabMarkerColor() ); + pen.setWidthF(qMax(1.0, spaceWidth() / 10.0)); + paint.setPen( pen ); + paint.setRenderHint(QPainter::Antialiasing, false); + + int dist = spaceWidth() * 0.3; + QPoint points[8]; + points[0] = QPoint(x - dist, y - dist); + points[1] = QPoint(x, y); + points[2] = QPoint(x, y); + points[3] = QPoint(x - dist, y + dist); + x += spaceWidth() / 3.0; + points[4] = QPoint(x - dist, y - dist); + points[5] = QPoint(x, y); + points[6] = QPoint(x, y); + points[7] = QPoint(x - dist, y + dist); + paint.drawLines(points, 4); + paint.setPen( penBackup ); +} + +void KateRenderer::paintTrailingSpace(QPainter &paint, qreal x, qreal y) +{ + QPen penBackup( paint.pen() ); + QPen pen( config()->tabMarkerColor() ); + pen.setWidthF(spaceWidth() / 3.5); + pen.setCapStyle(Qt::RoundCap); + paint.setPen( pen ); + paint.setRenderHint(QPainter::Antialiasing, true); + + paint.drawPoint( QPointF(x, y) ); + paint.setPen( penBackup ); +} + +void KateRenderer::paintNonBreakSpace(QPainter &paint, qreal x, qreal y) +{ + QPen penBackup( paint.pen() ); + QPen pen( config()->tabMarkerColor() ); + pen.setWidthF(qMax(1.0, spaceWidth() / 10.0)); + paint.setPen( pen ); + paint.setRenderHint(QPainter::Antialiasing, false); + + const int height = fontHeight(); + const int width = spaceWidth(); + + QPoint points[6]; + points[0] = QPoint(x+width/10, y+height/4); + points[1] = QPoint(x+width/10, y+height/3); + points[2] = QPoint(x+width/10, y+height/3); + points[3] = QPoint(x+width-width/10, y+height/3); + points[4] = QPoint(x+width-width/10, y+height/3); + points[5] = QPoint(x+width-width/10, y+height/4); + paint.drawLines(points, 3); + paint.setPen( penBackup ); +} + +void KateRenderer::paintIndentMarker(QPainter &paint, uint x, uint y /*row*/) +{ + QPen penBackup( paint.pen() ); + QPen myPen(config()->indentationLineColor()); + static const QVector dashPattern = QVector() << 1 << 1; + myPen.setDashPattern(dashPattern); + if (y % 2) + myPen.setDashOffset(1); + paint.setPen(myPen); + + const int height = fontHeight(); + const int top = 0; + const int bottom = height-1; + + QPainter::RenderHints renderHints = paint.renderHints(); + paint.setRenderHints(renderHints, false); + + paint.drawLine(x + 2, top, x + 2, bottom); + + paint.setRenderHints(renderHints, true); + + paint.setPen( penBackup ); +} + +static bool rangeLessThanForRenderer (const Kate::TextRange *a, const Kate::TextRange *b) +{ + // compare Z-Depth first + // smaller Z-Depths should win! + if (a->zDepth() > b->zDepth()) + return true; + else if (a->zDepth() < b->zDepth()) + return false; + + // end of a > end of b? + if (a->end().toCursor() > b->end().toCursor()) + return true; + + // if ends are equal, start of a < start of b? + if (a->end().toCursor() == b->end().toCursor()) + return a->start().toCursor() < b->start().toCursor(); + + return false; +} + +QList KateRenderer::decorationsForLine( const Kate::TextLine& textLine, int line, bool selectionsOnly, KateRenderRange* completionHighlight, bool completionSelected ) const +{ + QList newHighlight; + + // Don't compute the highlighting if there isn't going to be any highlighting + QList rangesWithAttributes = m_doc->buffer().rangesForLine (line, m_printerFriendly ? 0 : m_view, true); + if (selectionsOnly || textLine->attributesList().count() || rangesWithAttributes.count()) { + RenderRangeList renderRanges; + + // Add the inbuilt highlighting to the list + NormalRenderRange* inbuiltHighlight = new NormalRenderRange(); + const QVector &al = textLine->attributesList(); + for (int i = 0; i < al.count(); ++i) + if (al[i].length > 0 && al[i].attributeValue > 0) + inbuiltHighlight->addRange(new KTextEditor::Range(KTextEditor::Cursor(line, al[i].offset), al[i].length), specificAttribute(al[i].attributeValue)); + renderRanges.append(inbuiltHighlight); + + if (!completionHighlight) { + // check for dynamic hl stuff + const QSet *rangesMouseIn = m_view ? m_view->rangesMouseIn () : 0; + const QSet *rangesCaretIn = m_view ? m_view->rangesCaretIn () : 0; + bool anyDynamicHlsActive = m_view && (!rangesMouseIn->empty() || !rangesCaretIn->empty()); + + // sort all ranges, we want that the most specific ranges win during rendering, multiple equal ranges are kind of random, still better than old smart rangs behavior ;) + qSort (rangesWithAttributes.begin(), rangesWithAttributes.end(), rangeLessThanForRenderer); + + // loop over all ranges + for (int i = 0; i < rangesWithAttributes.size(); ++i) { + // real range + Kate::TextRange *kateRange = rangesWithAttributes[i]; + + // calculate attribute, default: normal attribute + KTextEditor::Attribute::Ptr attribute = kateRange->attribute(); + if (anyDynamicHlsActive) { + // check mouse in + if (KTextEditor::Attribute::Ptr attributeMouseIn = attribute->dynamicAttribute (KTextEditor::Attribute::ActivateMouseIn)) { + if (rangesMouseIn->contains (kateRange)) + attribute = attributeMouseIn; + } + + // check caret in + if (KTextEditor::Attribute::Ptr attributeCaretIn = attribute->dynamicAttribute (KTextEditor::Attribute::ActivateCaretIn)) { + if (rangesCaretIn->contains (kateRange)) + attribute = attributeCaretIn; + } + } + + // span range + NormalRenderRange *additionaHl = new NormalRenderRange(); + additionaHl->addRange(new KTextEditor::Range (*kateRange), attribute); + renderRanges.append(additionaHl); + } + } else { + // Add the code completion arbitrary highlight to the list + renderRanges.append(completionHighlight); + } + + // Add selection highlighting if we're creating the selection decorations + if ((selectionsOnly && showSelections() && m_view->selection()) || (completionHighlight && completionSelected) || m_view->blockSelection()) { + NormalRenderRange* selectionHighlight = new NormalRenderRange(); + + // Set up the selection background attribute TODO: move this elsewhere, eg. into the config? + static KTextEditor::Attribute::Ptr backgroundAttribute; + if (!backgroundAttribute) + backgroundAttribute = KTextEditor::Attribute::Ptr(new KTextEditor::Attribute()); + + backgroundAttribute->setBackground(config()->selectionColor()); + backgroundAttribute->setForeground(attribute(KTextEditor::HighlightInterface::dsNormal)->selectedForeground().color()); + + // Create a range for the current selection + if (completionHighlight && completionSelected) + selectionHighlight->addRange(new KTextEditor::Range(line, 0, line + 1, 0), backgroundAttribute); + else + if(m_view->blockSelection() && m_view->selectionRange().overlapsLine(line)) + selectionHighlight->addRange(new KTextEditor::Range(m_doc->rangeOnLine(m_view->selectionRange(), line)), backgroundAttribute); + else { + selectionHighlight->addRange(new KTextEditor::Range(m_view->selectionRange()), backgroundAttribute); + } + + renderRanges.append(selectionHighlight); + // highlighting for the vi visual modes + } + + KTextEditor::Cursor currentPosition, endPosition; + + // Calculate the range which we need to iterate in order to get the highlighting for just this line + if (selectionsOnly) { + if(m_view->blockSelection()) { + KTextEditor::Range subRange = m_doc->rangeOnLine(m_view->selectionRange(), line); + currentPosition = subRange.start(); + endPosition = subRange.end(); + } else { + KTextEditor::Range rangeNeeded = m_view->selectionRange() & KTextEditor::Range(line, 0, line + 1, 0); + + currentPosition = qMax(KTextEditor::Cursor(line, 0), rangeNeeded.start()); + endPosition = qMin(KTextEditor::Cursor(line + 1, 0), rangeNeeded.end()); + } + } else { + currentPosition = KTextEditor::Cursor(line, 0); + endPosition = KTextEditor::Cursor(line + 1, 0); + } + + // Main iterative loop. This walks through each set of highlighting ranges, and stops each + // time the highlighting changes. It then creates the corresponding QTextLayout::FormatRanges. + while (currentPosition < endPosition) { + renderRanges.advanceTo(currentPosition); + + if (!renderRanges.hasAttribute()) { + // No attribute, don't need to create a FormatRange for this text range + currentPosition = renderRanges.nextBoundary(); + continue; + } + + KTextEditor::Cursor nextPosition = renderRanges.nextBoundary(); + + // Create the format range and populate with the correct start, length and format info + QTextLayout::FormatRange fr; + fr.start = currentPosition.column(); + + if (nextPosition < endPosition || endPosition.line() <= line) { + fr.length = nextPosition.column() - currentPosition.column(); + + } else { + // +1 to force background drawing at the end of the line when it's warranted + fr.length = textLine->length() - currentPosition.column() + 1; + } + + KTextEditor::Attribute::Ptr a = renderRanges.generateAttribute(); + if (a) { + fr.format = *a; + + if(selectionsOnly) { + assignSelectionBrushesFromAttribute(fr, *a); + } + } + + newHighlight.append(fr); + + currentPosition = nextPosition; + } + + if (completionHighlight) + // Don't delete external completion render range + renderRanges.removeAll(completionHighlight); + + qDeleteAll(renderRanges); + } + + return newHighlight; +} + +void KateRenderer::assignSelectionBrushesFromAttribute(QTextLayout::FormatRange& target, const KTextEditor::Attribute& attribute) const +{ + if(attribute.hasProperty(KTextEditor::Attribute::SelectedForeground)) { + target.format.setForeground(attribute.selectedForeground()); + } + if(attribute.hasProperty(KTextEditor::Attribute::SelectedBackground)) { + target.format.setBackground(attribute.selectedBackground()); + } +} + +/* +The ultimate line painting function. +Currently missing features: +- draw indent lines +*/ +void KateRenderer::paintTextLine(QPainter& paint, KateLineLayoutPtr range, int xStart, int xEnd, const KTextEditor::Cursor* cursor) +{ + Q_ASSERT(range->isValid()); + +// kDebug( 13033 )<<"KateRenderer::paintTextLine"; + + // font data + const QFontMetricsF &fm = config()->fontMetrics(); + + int currentViewLine = -1; + if (cursor && cursor->line() == range->line()) + currentViewLine = range->viewLineForColumn(cursor->column()); + + paintTextLineBackground(paint, range, currentViewLine, xStart, xEnd); + + if (range->layout()) { + bool drawSelection = m_view->selection() && showSelections() && m_view->selectionRange().overlapsLine(range->line()); + // Draw selection in block selecton mode. We need 2 kinds of selections that QTextLayout::draw can't render: + // - past-end-of-line selection and + // - 0-column-wide selection (used to indicate where text will be typed) + if (drawSelection && m_view->blockSelection()) { + int selectionStartColumn = m_doc->fromVirtualColumn(range->line(), m_doc->toVirtualColumn(m_view->selectionRange().start())); + int selectionEndColumn = m_doc->fromVirtualColumn(range->line(), m_doc->toVirtualColumn(m_view->selectionRange().end())); + QBrush selectionBrush = config()->selectionColor(); + if (selectionStartColumn != selectionEndColumn) { + KateTextLayout lastLine = range->viewLine(range->viewLineCount() - 1); + if (selectionEndColumn > lastLine.startCol()) { + int selectionStartX = (selectionStartColumn > lastLine.startCol()) ? cursorToX(lastLine, selectionStartColumn, true) : 0; + int selectionEndX = cursorToX(lastLine, selectionEndColumn, true); + paint.fillRect(QRect(selectionStartX - xStart, (int)lastLine.lineLayout().y(), selectionEndX - selectionStartX, lineHeight()), selectionBrush); + } + } else { + const int selectStickWidth = 2; + KateTextLayout selectionLine = range->viewLine(range->viewLineForColumn(selectionStartColumn)); + int selectionX = cursorToX(selectionLine, selectionStartColumn, true); + paint.fillRect(QRect(selectionX - xStart, (int)selectionLine.lineLayout().y(), selectStickWidth, lineHeight()), selectionBrush); + } + } + + QVector additionalFormats; + if (range->length() > 0) { + // We may have changed the pen, be absolutely sure it gets set back to + // normal foreground color before drawing text for text that does not + // set the pen color + paint.setPen(attribute(KTextEditor::HighlightInterface::dsNormal)->foreground().color()); + // Draw the text :) + if (drawSelection) { + // FIXME toVector() may be a performance issue + additionalFormats = decorationsForLine(range->textLine(), range->line(), true).toVector(); + range->layout()->draw(&paint, QPoint(-xStart,0), additionalFormats); + + } else { + range->layout()->draw(&paint, QPoint(-xStart,0)); + } + } + + QBrush backgroundBrush; + bool backgroundBrushSet = false; + + // Loop each individual line for additional text decoration etc. + QListIterator it = range->layout()->additionalFormats(); + QVectorIterator it2 = additionalFormats; + for (int i = 0; i < range->viewLineCount(); ++i) { + KateTextLayout line = range->viewLine(i); + + // Determine the background to use, if any, for the end of this view line + backgroundBrushSet = false; + while (it2.hasNext()) { + const QTextLayout::FormatRange& fr = it2.peekNext(); + if (fr.start > line.endCol()) + break; + + if (fr.start + fr.length > line.endCol()) { + if (fr.format.hasProperty(QTextFormat::BackgroundBrush)) { + backgroundBrushSet = true; + backgroundBrush = fr.format.background(); + } + + goto backgroundDetermined; + } + + it2.next(); + } + + while (it.hasNext()) { + const QTextLayout::FormatRange& fr = it.peekNext(); + if (fr.start > line.endCol()) + break; + + if (fr.start + fr.length > line.endCol()) { + if (fr.format.hasProperty(QTextFormat::BackgroundBrush)) { + backgroundBrushSet = true; + backgroundBrush = fr.format.background(); + } + + break; + } + + it.next(); + } + + backgroundDetermined: + + // Draw selection or background color outside of areas where text is rendered + if (!m_printerFriendly ) { + bool draw = false; + QBrush drawBrush; + if (m_view->selection() && !m_view->blockSelection() && m_view->lineEndSelected(line.end(true))) { + draw = true; + drawBrush = config()->selectionColor(); + } else if (backgroundBrushSet && !m_view->blockSelection()) { + draw = true; + drawBrush = backgroundBrush; + } + + if (draw) { + int fillStartX = line.endX() - line.startX() + line.xOffset() - xStart; + int fillStartY = lineHeight() * i; + int width= xEnd - xStart - fillStartX; + int height= lineHeight(); + + // reverse X for right-aligned lines + if (range->layout()->textOption().alignment() == Qt::AlignRight) + fillStartX = 0; + + if (width > 0) { + QRect area(fillStartX, fillStartY, width, height); + paint.fillRect(area, drawBrush); + } + } + } + // Draw indent lines + if (showIndentLines() && i == 0) + { + const qreal w = spaceWidth(); + const int lastIndentColumn = range->textLine()->indentDepth(m_tabWidth); + + for (int x = m_indentWidth; x < lastIndentColumn; x += m_indentWidth) + { + paintIndentMarker(paint, x * w + 1 - xStart, range->line()); + } + } + + // draw an open box to mark non-breaking spaces + const QString& text = range->textLine()->string(); + int y = lineHeight() * i + fm.ascent() - fm.strikeOutPos(); + int nbSpaceIndex = text.indexOf(nbSpaceChar, line.lineLayout().xToCursor(xStart)); + + while (nbSpaceIndex != -1 && nbSpaceIndex < line.endCol()) { + int x = line.lineLayout().cursorToX(nbSpaceIndex); + if (x > xEnd) + break; + paintNonBreakSpace(paint, x - xStart, y); + nbSpaceIndex = text.indexOf(nbSpaceChar, nbSpaceIndex + 1); + } + + // draw tab stop indicators + if (showTabs()) { + int tabIndex = text.indexOf(tabChar, line.lineLayout().xToCursor(xStart)); + while (tabIndex != -1 && tabIndex < line.endCol()) { + int x = line.lineLayout().cursorToX(tabIndex); + if (x > xEnd) + break; + paintTabstop(paint, x - xStart + spaceWidth()/2.0, y); + tabIndex = text.indexOf(tabChar, tabIndex + 1); + } + } + + // draw trailing spaces + if (showTrailingSpaces()) { + int spaceIndex = line.endCol() - 1; + int trailingPos = range->textLine()->lastChar(); + if (trailingPos < 0) + trailingPos = 0; + if (spaceIndex >= trailingPos) { + while (spaceIndex >= line.startCol() && text.at(spaceIndex).isSpace()) { + if (text.at(spaceIndex) != '\t' || !showTabs()) + paintTrailingSpace(paint, line.lineLayout().cursorToX(spaceIndex) - xStart + spaceWidth()/2.0, y); + --spaceIndex; + } + } + } + } + + // draw word-wrap-honor-indent filling + if ( (range->viewLineCount() > 1) && range->shiftX() && (range->shiftX() > xStart) ) + { + if (backgroundBrushSet) + paint.fillRect(0, lineHeight(), range->shiftX() - xStart, lineHeight() * (range->viewLineCount() - 1), + backgroundBrush); + paint.fillRect(0, lineHeight(), range->shiftX() - xStart, lineHeight() * (range->viewLineCount() - 1), + QBrush(config()->wordWrapMarkerColor(), Qt::Dense4Pattern)); + } + + // Draw caret + if (drawCaret() && cursor && range->includesCursor(*cursor)) { + int caretWidth, lineWidth = 2; + QColor color; + QTextLine line = range->layout()->lineForTextPosition(qMin(cursor->column(), range->length())); + + // Determine the caret's style + caretStyles style = caretStyle(); + + // Make the caret the desired width + if (style == Line) { + caretWidth = lineWidth; + } else if (line.isValid() && cursor->column() < range->length()) { + caretWidth = int(line.cursorToX(cursor->column() + 1) - line.cursorToX(cursor->column())); + if (caretWidth < 0) { + caretWidth = -caretWidth; + } + } else { + caretWidth = spaceWidth(); + } + + // Determine the color + if (m_caretOverrideColor.isValid()) { + // Could actually use the real highlighting system for this... + // would be slower, but more accurate for corner cases + color = m_caretOverrideColor; + } else { + // search for the FormatRange that includes the cursor + foreach (const QTextLayout::FormatRange &r, range->layout()->additionalFormats()) { + if ((r.start <= cursor->column() ) && ( (r.start + r.length) > cursor->column())) { + // check for Qt::NoBrush, as the returned color is black() and no invalid QColor + QBrush foregroundBrush = r.format.foreground(); + if (foregroundBrush != Qt::NoBrush) { + color = r.format.foreground().color(); + } + break; + } + } + // still no color found, fall back to default style + if (!color.isValid()) + color = attribute(KTextEditor::HighlightInterface::dsNormal)->foreground().color(); + } + + // Clip the caret - Qt's caret has a habit of intruding onto other lines. + paint.save(); + paint.setClipRect(0, line.lineNumber() * lineHeight(), xEnd - xStart, lineHeight()); + switch(style) { + case Line : + paint.setPen(QPen(color, caretWidth)); + break; + case Block : + // use a gray caret so it's possible to see the character + color.setAlpha(128); + paint.setPen(QPen(color, caretWidth)); + break; + case Underline : + paint.setClipRect(0, lineHeight() - lineWidth, xEnd - xStart, lineWidth); + break; + case Half : + color.setAlpha(128); + paint.setPen(QPen(color, caretWidth)); + paint.setClipRect(0, lineHeight() / 2, xEnd - xStart, lineHeight() / 2); + break; + } + + if (cursor->column() <= range->length()) { + range->layout()->drawCursor(&paint, QPoint(-xStart,0), cursor->column(), caretWidth); + } else { + // Off the end of the line... must be block mode. Draw the caret ourselves. + const KateTextLayout& lastLine = range->viewLine(range->viewLineCount() - 1); + int x = cursorToX(lastLine, KTextEditor::Cursor(range->line(), cursor->column()), true); + if ((x >= xStart) && (x <= xEnd)) { + paint.fillRect(x - xStart, (int)lastLine.lineLayout().y(), caretWidth, lineHeight(), color); + } + } + + paint.restore(); + } + } + + // Draws the dashed underline at the start of a folded block of text. + if (range->startsInvisibleBlock()) { + const QPainter::RenderHints backupRenderHints = paint.renderHints(); + paint.setRenderHint(QPainter::Antialiasing, false); + QPen pen(config()->wordWrapMarkerColor()); + pen.setCosmetic(true); + pen.setStyle(Qt::DashLine); + pen.setDashOffset(xStart); + paint.setPen(pen); + paint.drawLine(0, (lineHeight() * range->viewLineCount()) - 1, xEnd - xStart, (lineHeight() * range->viewLineCount()) - 1); + paint.setRenderHints(backupRenderHints); + } + + // show word wrap marker if desirable + if ((!isPrinterFriendly()) && config()->wordWrapMarker() && QFontInfo(config()->font()).fixedPitch()) + { + const QPainter::RenderHints backupRenderHints = paint.renderHints(); + paint.setRenderHint(QPainter::Antialiasing, false); + paint.setPen( config()->wordWrapMarkerColor() ); + int _x = qreal(m_doc->config()->wordWrapAt()) * fm.width('x') - xStart; + paint.drawLine( _x,0,_x,lineHeight() ); + paint.setRenderHints(backupRenderHints); + } +} + +const QFont& KateRenderer::currentFont() const +{ + return config()->font(); +} + +const QFontMetricsF& KateRenderer::currentFontMetrics() const +{ + return config()->fontMetrics(); +} + +uint KateRenderer::fontHeight() const +{ + return m_fontHeight; +} + +uint KateRenderer::documentHeight() const +{ + return m_doc->lines() * lineHeight(); +} + +int KateRenderer::lineHeight() const +{ + return fontHeight(); +} + +bool KateRenderer::getSelectionBounds(int line, int lineLength, int &start, int &end) const +{ + bool hasSel = false; + + if (m_view->selection() && !m_view->blockSelection()) + { + if (m_view->lineIsSelection(line)) + { + start = m_view->selectionRange().start().column(); + end = m_view->selectionRange().end().column(); + hasSel = true; + } + else if (line == m_view->selectionRange().start().line()) + { + start = m_view->selectionRange().start().column(); + end = lineLength; + hasSel = true; + } + else if (m_view->selectionRange().containsLine(line)) + { + start = 0; + end = lineLength; + hasSel = true; + } + else if (line == m_view->selectionRange().end().line()) + { + start = 0; + end = m_view->selectionRange().end().column(); + hasSel = true; + } + } + else if (m_view->lineHasSelected(line)) + { + start = m_view->selectionRange().start().column(); + end = m_view->selectionRange().end().column(); + hasSel = true; + } + + if (start > end) { + int temp = end; + end = start; + start = temp; + } + + return hasSel; +} + +void KateRenderer::updateConfig () +{ + // update the attibute list pointer + updateAttributes (); + + if (m_view) + m_view->updateRendererConfig(); + + // update font height + updateFontHeight (); +} + +void KateRenderer::updateFontHeight () +{ + // first: get normal line spacing + m_fontHeight = config()->fontMetrics().height(); + + // Sometimes the height of italic fonts is larger than for the non-italic + // font. Since all our lines are of same/fixed height, use the maximum of + // both heights (bug #302748) + QFont italicFont = config()->font(); + italicFont.setItalic(true); + m_fontHeight = qMax (m_fontHeight, QFontMetrics(italicFont).height()); + + // same for bold font + QFont boldFont = config()->font(); + boldFont.setBold (true); + m_fontHeight = qMax (m_fontHeight, QFontMetrics(boldFont).height()); +} + +qreal KateRenderer::spaceWidth() const +{ + return config()->fontMetrics().width(spaceChar); +} + +void KateRenderer::layoutLine(KateLineLayoutPtr lineLayout, int maxwidth, bool cacheLayout) const +{ + // if maxwidth == -1 we have no wrap + + Kate::TextLine textLine = lineLayout->textLine(); + Q_ASSERT(textLine); + + QTextLayout* l = lineLayout->layout(); + if (!l) { + l = new QTextLayout(textLine->string(), config()->font()); + } else { + l->setText(textLine->string()); + l->setFont(config()->font()); + } + + l->setCacheEnabled(cacheLayout); + + // Initial setup of the QTextLayout. + + // Tab width + QTextOption opt; + opt.setFlags(QTextOption::IncludeTrailingSpaces); + opt.setTabStop(m_tabWidth * config()->fontMetrics().width(spaceChar)); + 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 (isLineRightToLeft(lineLayout)) { + opt.setAlignment( Qt::AlignRight ); + opt.setTextDirection( Qt::RightToLeft ); + } + else { + opt.setAlignment( Qt::AlignLeft ); + opt.setTextDirection( Qt::LeftToRight ); + } + + l->setTextOption(opt); + + // Syntax highlighting, inbuilt and arbitrary + l->setAdditionalFormats(decorationsForLine(textLine, lineLayout->line())); + + // Begin layouting + l->beginLayout(); + + int height = 0; + int shiftX = 0; + + bool needShiftX = (maxwidth != -1) + && (m_view->config()->dynWordWrapAlignIndent() > 0); + + forever { + QTextLine line = l->createLine(); + if (!line.isValid()) + break; + + if (maxwidth > 0) + line.setLineWidth(maxwidth); + + line.setPosition(QPoint(line.lineNumber() ? shiftX : 0, height)); + + if (needShiftX && line.width() > 0) { + needShiftX = false; + // Determine x offset for subsequent-lines-of-paragraph indenting + int pos = textLine->nextNonSpaceChar(0); + + if (pos > 0) { + shiftX = (int)line.cursorToX(pos); + } + + // check for too deep shift value and limit if necessary + if (shiftX > ((double)maxwidth / 100 * m_view->config()->dynWordWrapAlignIndent())) + shiftX = 0; + + // if shiftX > 0, the maxwidth has to adapted + maxwidth -= shiftX; + + lineLayout->setShiftX(shiftX); + } + + height += lineHeight(); + } + + l->endLayout(); + + lineLayout->setLayout(l); +} + + +// 1) QString::isRightToLeft() sux +// 2) QString::isRightToLeft() is marked as internal (WTF?) +// 3) QString::isRightToLeft() does not seem to work on my setup +// 4) isStringRightToLeft() should behave much better than QString::isRightToLeft() therefore: +// 5) isStringRightToLeft() kicks ass +bool KateRenderer::isLineRightToLeft( KateLineLayoutPtr lineLayout ) const +{ + QString s = lineLayout->textLine()->string(); + int i = 0; + + // borrowed from QString::updateProperties() + while( i != s.length() ) + { + QChar c = s.at(i); + + switch(c.direction()) { + case QChar::DirL: + case QChar::DirLRO: + case QChar::DirLRE: + return false; + + case QChar::DirR: + case QChar::DirAL: + case QChar::DirRLO: + case QChar::DirRLE: + return true; + + default: + break; + } + i ++; + } + + return false; +#if 0 + // or should we use the direction of the widget? + QWidget* display = qobject_cast(view()->parent()); + if (!display) + return false; + return display->layoutDirection() == Qt::RightToLeft; +#endif +} + +int KateRenderer::cursorToX(const KateTextLayout& range, int col, bool returnPastLine) const +{ + return cursorToX(range, KTextEditor::Cursor(range.line(), col), returnPastLine); +} + +int KateRenderer::cursorToX(const KateTextLayout& range, const KTextEditor::Cursor & pos, bool returnPastLine) const +{ + Q_ASSERT(range.isValid()); + + int x; + if (range.lineLayout().width() > 0) { + x = (int)range.lineLayout().cursorToX(pos.column()); + } else { + x = 0; + } + + int over = pos.column() - range.endCol(); + if (returnPastLine && over > 0) + x += over * spaceWidth(); + + return x; +} + +KTextEditor::Cursor KateRenderer::xToCursor(const KateTextLayout & range, int x, bool returnPastLine ) const +{ + Q_ASSERT(range.isValid()); + KTextEditor::Cursor ret(range.line(), range.lineLayout().xToCursor(x)); + + // TODO wrong for RTL lines? + if (returnPastLine && range.endCol(true) == -1 && x > range.width() + range.xOffset()) + ret.setColumn(ret.column() + ((x - (range.width() + range.xOffset())) / spaceWidth())); + + return ret; +} + +void KateRenderer::setCaretOverrideColor(const QColor& color) +{ + m_caretOverrideColor = color; +} + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/render/katerenderer.h b/kate/part/render/katerenderer.h new file mode 100644 index 00000000..5a3bb963 --- /dev/null +++ b/kate/part/render/katerenderer.h @@ -0,0 +1,370 @@ +/* This file is part of the KDE libraries + Copyright (C) 2007 Mirko Stocker + Copyright (C) 2003-2005 Hamish Rodda + Copyright (C) 2001 Christoph Cullmann + Copyright (C) 2001 Joseph Wenninger + Copyright (C) 1999 Jochen Wilhelmy + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef __KATE_RENDERER_H__ +#define __KATE_RENDERER_H__ + +#include +#include "katetextline.h" +#include "katelinelayout.h" + +#include +#include +#include +#include + +class KateDocument; +class KateView; +class KateRendererConfig; +class KateRenderRange; +namespace KTextEditor { class Range; } +namespace Kate { class TextFolding; } + +class KateLineLayout; +typedef KSharedPtr KateLineLayoutPtr; + +/** + * Handles all of the work of rendering the text + * (used for the views and printing) + * + **/ +class KATEPARTINTERFACES_EXPORT KateRenderer +{ +public: + /** + * Style of Caret + * + * The caret is displayed as a vertical bar (Line), a filled box + * (Block), a horizontal bar (Underline), or a half-height filled + * box (Half). The default is Line. + * + * Line Block Underline Half + * + * ## _ ######### _ _ + * ## __| | #####| |# __| | __| | + * ## / _' | ##/ _' |# / _' | / _' | + * ##| (_| | #| (#| |# | (_| | #| (#| |# + * ## \__,_| ##\__,_|# \__,_| ##\__,_|# + * ## ######### ######### ######### + */ + enum caretStyles { + Line, + Block, + Underline, + Half + }; + + /** + * Constructor + * @param doc document to render + * @param folding folding information + * @param view view which is output (0 for example for rendering to print) + */ + explicit KateRenderer(KateDocument* doc, Kate::TextFolding &folding, KateView *view = 0); + + /** + * Destructor + */ + ~KateRenderer(); + + /** + * Returns the document to which this renderer is bound + */ + KateDocument* doc() const { return m_doc; } + + /** + * Returns the folding info to which this renderer is bound + * @return folding info + */ + Kate::TextFolding &folding() const { return m_folding; } + + /** + * Returns the view to which this renderer is bound + */ + KateView* view() const { return m_view; } + + /** + * update the highlighting attributes + * (for example after an hl change or after hl config changed) + */ + void updateAttributes (); + + /** + * Determine whether the caret (text cursor) will be drawn. + * @return should it be drawn? + */ + inline bool drawCaret() const { return m_drawCaret; } + + /** + * Set whether the caret (text cursor) will be drawn. + * @param drawCaret should caret be drawn? + */ + void setDrawCaret(bool drawCaret); + + /** + * The style of the caret (text cursor) to be painted. + * @return caretStyle + */ + inline KateRenderer::caretStyles caretStyle() const { return m_caretStyle; } + + /** + * Set the style of caret to be painted. + * @param style style to set + */ + void setCaretStyle(KateRenderer::caretStyles style); + + /** + * Set a \a brush with which to override drawing of the caret. Set to QColor() to clear. + */ + void setCaretOverrideColor(const QColor& color); + + /** + * @returns whether tabs should be shown (ie. a small mark + * drawn to identify a tab) + * @return tabs should be shown + */ + inline bool showTabs() const { return m_showTabs; } + + /** + * Set whether a mark should be painted to help identifying tabs. + * @param showTabs show the tabs? + */ + void setShowTabs(bool showTabs); + + /** + * @returns whether trailing spaces should be shown. + */ + inline bool showTrailingSpaces() const { return m_showSpaces; } + + /** + * Set whether a mark should be painted for trailing spaces. + */ + void setShowTrailingSpaces(bool showSpaces); + + /** + * Sets the width of the tab. Helps performance. + * @param tabWidth new tab width + */ + void setTabWidth(int tabWidth); + + /** + * @returns whether indent lines should be shown + * @return indent lines should be shown + */ + bool showIndentLines() const; + + /** + * Set whether a guide should be painted to help identifying indent lines. + * @param showLines show the indent lines? + */ + void setShowIndentLines(bool showLines); + + /** + * Sets the width of the tab. Helps performance. + * @param indentWidth new indent width + */ + void setIndentWidth(int indentWidth); + + /** + * Show the view's selection? + * @return show sels? + */ + inline bool showSelections() const { return m_showSelections; } + + /** + * Set whether the view's selections should be shown. + * The default is true. + * @param showSelections show the selections? + */ + void setShowSelections(bool showSelections); + + /** + * Change to a different font (soon to be font set?) + */ + void increaseFontSizes(); + void decreaseFontSizes(); + const QFont& currentFont() const; + const QFontMetricsF& currentFontMetrics() const; + + /** + * @return whether the renderer is configured to paint in a + * printer-friendly fashion. + */ + bool isPrinterFriendly() const; + + /** + * Configure this renderer to paint in a printer-friendly fashion. + * + * Sets the other options appropriately if true. + */ + void setPrinterFriendly(bool printerFriendly); + + /** + * Text width & height calculation functions... + */ + void layoutLine(KateLineLayoutPtr line, int maxwidth = -1, bool cacheLayout = false) const; + + /** + * This is a smaller QString::isRightToLeft(). It's also marked as internal to kate + * instead of internal to Qt, so we can modify. This method searches for the first + * strong character in the paragraph and then returns its direction. In case of a + * line without any strong characters, the direction is forced to be LTR. + * + * Back in KDE 4.1 this method counted chars, which lead to unwanted side effects. + * (see https://bugs.kde.org/show_bug.cgi?id=178594). As this function is internal + * the way it work will probably change between releases. Be warned! + */ + bool isLineRightToLeft( KateLineLayoutPtr lineLayout ) const; + + /** + * The ultimate decoration creation function. + * + * \param range line to return decoration for + * \param selectionsOnly return decorations for selections and/or dynamic highlighting. + */ + QList decorationsForLine(const Kate::TextLine& textLine, int line, bool selectionsOnly = false, KateRenderRange* completionHighlight = 0L, bool completionSelected = false) const; + + // Width calculators + qreal spaceWidth() const; + + /** + * Returns the x position of cursor \p col on the line \p range. + */ + int cursorToX(const KateTextLayout& range, int col, bool returnPastLine = false) const; + /// \overload + int cursorToX(const KateTextLayout& range, const KTextEditor::Cursor& pos, bool returnPastLine = false) const; + + /** + * Returns the real cursor which is occupied by the specified x value, or that closest to it. + * If \p returnPastLine is true, the column will be extrapolated out with the assumption + * that the extra characters are spaces. + */ + KTextEditor::Cursor xToCursor(const KateTextLayout& range, int x, bool returnPastLine = false) const; + + // Font height + uint fontHeight() const; + + // Line height + int lineHeight() const; + + // Document height + uint documentHeight() const; + + // Selection boundaries + bool getSelectionBounds(int line, int lineLength, int &start, int &end) const; + + /** + * This is the ultimate function to perform painting of a text line. + * + * The text line is painted from the upper limit of (0,0). To move that, + * apply a transform to your painter. + * + * @param paint painter to use + * @param range layout to use in painting this line + * @param xStart starting width in pixels. + * @param xEnd ending width in pixels. + * @param cursor position of the caret, if placed on the current line. + */ + void paintTextLine(QPainter& paint, KateLineLayoutPtr range, int xStart, int xEnd, const KTextEditor::Cursor* cursor = 0L); + + /** + * Paint the background of a line + * + * Split off from the main @ref paintTextLine method to make it smaller. As it's being + * called only once per line it shouldn't noticably affect performance and it + * helps readability a LOT. + * + * @param paint painter to use + * @param layout layout to use in painting this line + * @param currentViewLine if one of the view lines is the current line, set + * this to the index; otherwise -1. + * @param xStart starting width in pixels. + * @param xEnd ending width in pixels. + */ + void paintTextLineBackground(QPainter& paint, KateLineLayoutPtr layout, int currentViewLine, int xStart, int xEnd); + + /** + * This takes an in index, and returns all the attributes for it. + * For example, if you have a ktextline, and want the KTextEditor::Attribute + * for a given position, do: + * + * attribute(myktextline->attribute(position)); + */ + KTextEditor::Attribute::Ptr attribute(uint pos) const; + KTextEditor::Attribute::Ptr specificAttribute(int context) const; + + private: + /** + * Paint a trailing space on position (x, y). + */ + void paintTrailingSpace(QPainter &paint, qreal x, qreal y); + /** + * Paint a tab stop marker on position (x, y). + */ + void paintTabstop(QPainter &paint, qreal x, qreal y); + + /** + * Paint a non-breaking space marker on position (x, y). + */ + void paintNonBreakSpace(QPainter &paint, qreal x, qreal y); + + /** Paint a SciTE-like indent marker. */ + void paintIndentMarker(QPainter &paint, uint x, uint y); + + void assignSelectionBrushesFromAttribute(QTextLayout::FormatRange& target, const KTextEditor::Attribute& attribute) const; + + // update font height + void updateFontHeight (); + + KateDocument *const m_doc; + Kate::TextFolding &m_folding; + KateView *const m_view; + + // cache of config values + int m_tabWidth; + int m_indentWidth; + int m_fontHeight; + + // some internal flags + KateRenderer::caretStyles m_caretStyle; + bool m_drawCaret; + bool m_showSelections; + bool m_showTabs; + bool m_showSpaces; + bool m_printerFriendly; + QColor m_caretOverrideColor; + + QList m_attributes; + + /** + * Configuration + */ + public: + inline KateRendererConfig *config () const { return m_config; } + + void updateConfig (); + + private: + KateRendererConfig *const m_config; +}; + +#endif diff --git a/kate/part/render/katerenderrange.cpp b/kate/part/render/katerenderrange.cpp new file mode 100644 index 00000000..f6793dfb --- /dev/null +++ b/kate/part/render/katerenderrange.cpp @@ -0,0 +1,211 @@ +/* This file is part of the KDE libraries and the Kate part. + * + * Copyright (C) 2003-2005 Hamish Rodda + * Copyright (C) 2007 Mirko Stocker + * Copyright (C) 2008 David Nolden + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "katerenderrange.h" + +#include + +#include + +void mergeAttributes(KTextEditor::Attribute::Ptr base, KTextEditor::Attribute::Ptr add) +{ + if(!add) + return; + + bool hadBg = base->hasProperty(KTextEditor::Attribute::BackgroundBrush); + bool hasBg = add->hasProperty(KTextEditor::Attribute::BackgroundBrush); + + bool hadFg = base->hasProperty(KTextEditor::Attribute::ForegroundBrush); + bool hasFg = add->hasProperty(KTextEditor::Attribute::ForegroundBrush); + + if(((!hadBg || !hasBg) && (!hadFg || !hasFg))) { + //Nothing to blend + *base += *add; + return; + } + + //We eventually have to blend + + QBrush baseBgBrush, baseFgBrush; + + if(hadBg) + baseBgBrush = base->background(); + + if(hadFg) + baseFgBrush = base->foreground(); + + *base += *add; + + if(hadBg && hasBg) + { + QBrush bg = add->background(); + if(!bg.isOpaque()) { + QColor mixWithColor = bg.color(); + mixWithColor.setAlpha(255); + bg.setColor(KColorUtils::mix(baseBgBrush.color(), mixWithColor, bg.color().alphaF())); + base->setBackground(bg); + } + } + if(hadFg && hasFg) + { + QBrush fg = add->foreground(); + if(!fg.isOpaque()) { + QColor mixWithColor = fg.color(); + mixWithColor.setAlpha(255); + fg.setColor(KColorUtils::mix(baseFgBrush.color(), mixWithColor, fg.color().alphaF())); + base->setForeground(fg); + } + } +} + +bool KateRenderRange::isReady() const { + return false; +} + +NormalRenderRange::NormalRenderRange() + : m_currentRange(0) +{ +} + +NormalRenderRange::~NormalRenderRange() +{ + QListIterator it = m_ranges; + while (it.hasNext()) + delete it.next().first; +} + +void NormalRenderRange::addRange(KTextEditor::Range* range, KTextEditor::Attribute::Ptr attribute) +{ + m_ranges.append(pairRA(range, attribute)); +} + +KTextEditor::Cursor NormalRenderRange::nextBoundary() const +{ + return m_nextBoundary; +} + +bool NormalRenderRange::advanceTo(const KTextEditor::Cursor& pos) +{ + int index = m_currentRange; + while (index < m_ranges.count()) { + const pairRA& p = m_ranges.at(index); + KTextEditor::Range* r = p.first; + if (r->end() <= pos) { + ++index; + } else { + bool ret = index != m_currentRange; + m_currentRange = index; + + if (r->start() > pos) { + m_nextBoundary = r->start(); + } else { + m_nextBoundary = r->end(); + } + if (r->contains(pos)) { + m_currentAttribute = p.second; + } else { + m_currentAttribute.clear(); + } + + return ret; + } + } + + m_nextBoundary = KTextEditor::Cursor(INT_MAX, INT_MAX); + m_currentAttribute.clear(); + return false; +} + +KTextEditor::Attribute::Ptr NormalRenderRange::currentAttribute() const +{ + return m_currentAttribute; +} + +KTextEditor::Cursor RenderRangeList::nextBoundary() const +{ + KTextEditor::Cursor ret = m_currentPos; + bool first = true; + foreach (KateRenderRange* r, *this) { + if (first) { + ret = r->nextBoundary(); + first = false; + + } else { + KTextEditor::Cursor nb = r->nextBoundary(); + if (ret > nb) + ret = nb; + } + } + return ret; +} + +RenderRangeList::~RenderRangeList() +{ +} + +void RenderRangeList::advanceTo(const KTextEditor::Cursor& pos) +{ + foreach (KateRenderRange* r, *this) + r->advanceTo(pos); + + //Delete lists that are ready, else the list may get too large due to temporaries + for(int a = size()-1; a >= 0; --a) { + KateRenderRange* r = at(a); + if(r->isReady()) { + delete r; + removeAt(a); + } + } +} + +bool RenderRangeList::hasAttribute() const +{ + foreach (KateRenderRange* r, *this) + if (r->currentAttribute()) + return true; + + return false; +} + +KTextEditor::Attribute::Ptr RenderRangeList::generateAttribute() const +{ + KTextEditor::Attribute::Ptr a; + bool ownsAttribute = false; + + foreach (KateRenderRange* r, *this) { + if (KTextEditor::Attribute::Ptr a2 = r->currentAttribute()) { + if(!a) { + a = a2; + }else { + if(!ownsAttribute) { + //Make an own copy of the attribute.. + ownsAttribute = true; + a = new KTextEditor::Attribute(*a); + } + mergeAttributes(a, a2); + } + } + } + + return a; +} + diff --git a/kate/part/render/katerenderrange.h b/kate/part/render/katerenderrange.h new file mode 100644 index 00000000..69cda872 --- /dev/null +++ b/kate/part/render/katerenderrange.h @@ -0,0 +1,79 @@ +/* This file is part of the KDE libraries and the Kate part. + * + * Copyright (C) 2003-2005 Hamish Rodda + * Copyright (C) 2008 David Nolden + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef KATERENDERRANGE_H +#define KATERENDERRANGE_H + +#include +#include + +#include +#include +#include + +class KateView; +class RenderRangeList; + +class KateRenderRange +{ + public: + virtual ~KateRenderRange() {} + virtual KTextEditor::Cursor nextBoundary() const = 0; + virtual bool advanceTo(const KTextEditor::Cursor& pos) = 0; + virtual KTextEditor::Attribute::Ptr currentAttribute() const = 0; + virtual bool isReady() const; +}; + +typedef QPair pairRA; + +class NormalRenderRange : public KateRenderRange +{ + public: + NormalRenderRange(); + virtual ~NormalRenderRange(); + + void addRange(KTextEditor::Range* range, KTextEditor::Attribute::Ptr attribute); + + virtual KTextEditor::Cursor nextBoundary() const; + virtual bool advanceTo(const KTextEditor::Cursor& pos); + virtual KTextEditor::Attribute::Ptr currentAttribute() const; + + private: + QList m_ranges; + KTextEditor::Cursor m_nextBoundary; + KTextEditor::Attribute::Ptr m_currentAttribute; + int m_currentRange; +}; + +class RenderRangeList : public QList +{ + public: + ~RenderRangeList(); + KTextEditor::Cursor nextBoundary() const; + void advanceTo(const KTextEditor::Cursor& pos); + bool hasAttribute() const; + KTextEditor::Attribute::Ptr generateAttribute() const; + + private: + KTextEditor::Cursor m_currentPos; +}; + +#endif diff --git a/kate/part/render/katetextlayout.cpp b/kate/part/render/katetextlayout.cpp new file mode 100644 index 00000000..86c02ca1 --- /dev/null +++ b/kate/part/render/katetextlayout.cpp @@ -0,0 +1,221 @@ +/* This file is part of the KDE libraries + Copyright (C) 2002-2005 Hamish Rodda + Copyright (C) 2003 Anakim Border + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "katetextlayout.h" + +#include + +KateTextLayout::KateTextLayout(KateLineLayoutPtr line, int viewLine) + : m_lineLayout(line) + , m_viewLine(viewLine) + , m_startX(m_viewLine ? -1 : 0) + , m_invalidDirty(true) +{ + if (isValid()) + m_textLayout = m_lineLayout->layout()->lineAt(m_viewLine); +} + +bool KateTextLayout::isDirty( ) const +{ + if (!isValid()) + return m_invalidDirty; + + return m_lineLayout->isDirty(viewLine()); +} + +bool KateTextLayout::setDirty( bool dirty ) +{ + if (!isValid()) + return (m_invalidDirty = dirty); + + return m_lineLayout->setDirty(viewLine(), dirty); +} + +bool KateTextLayout::includesCursor(const KTextEditor::Cursor& realCursor) const +{ + return realCursor.line() == line() && realCursor.column() >= startCol() && (!wrap() || realCursor.column() < endCol()); +} + +int KateTextLayout::xOffset() const +{ + if (!isValid()) + return 0; + + return startX() ? m_lineLayout->shiftX() : 0; +} + +void KateTextLayout::debugOutput() const +{ + kDebug( 13033 ) << "KateTextLayout: " << m_lineLayout << " valid " << isValid() << " line " << m_lineLayout->line() << " (" << line() << ") cols [" << startCol() << " -> " << endCol() << "] x [" << startX() << " -> " << endX() << " off " << m_lineLayout->shiftX() << "] wrap " << wrap(); +} + +bool operator> (const KateTextLayout& r, const KTextEditor::Cursor& c) +{ + return r.line() > c.line() || r.endCol() > c.column(); +} + +bool operator>= (const KateTextLayout& r, const KTextEditor::Cursor& c) +{ + return r.line() > c.line() || r.endCol() >= c.column(); +} + +bool operator< (const KateTextLayout& r, const KTextEditor::Cursor& c) +{ + return r.line() < c.line() || r.startCol() < c.column(); +} + +bool operator<= (const KateTextLayout& r, const KTextEditor::Cursor& c) +{ + return r.line() < c.line() || r.startCol() <= c.column(); +} + +bool KateTextLayout::isValid( ) const +{ + return m_lineLayout && m_lineLayout->isValid() && m_viewLine >= 0 && m_viewLine < m_lineLayout->viewLineCount(); +} + +int KateTextLayout::line( ) const +{ + if (!isValid()) + return -1; + + return m_lineLayout->line(); +} + +int KateTextLayout::virtualLine( ) const +{ + if (!isValid()) + return -1; + + return m_lineLayout->virtualLine(); +} + +int KateTextLayout::viewLine( ) const +{ + if (!isValid()) + return 0; + + return m_viewLine; +} + +const QTextLine & KateTextLayout::lineLayout( ) const +{ + return m_textLayout; +} + +KateLineLayoutPtr KateTextLayout::kateLineLayout( ) const +{ + return m_lineLayout; +} + +int KateTextLayout::startCol( ) const +{ + if (!isValid()) + return 0; + + return lineLayout().textStart(); +} + +KTextEditor::Cursor KateTextLayout::start( ) const +{ + return KTextEditor::Cursor(line(), startCol()); +} + +int KateTextLayout::endCol(bool indicateEOL) const +{ + if (!isValid()) + return 0; + + if (indicateEOL) + if (viewLine() == kateLineLayout()->viewLineCount() - 1) + return -1; + + return startCol() + m_textLayout.textLength(); +} + +KTextEditor::Cursor KateTextLayout::end(bool indicateEOL) const +{ + return KTextEditor::Cursor(line(), endCol(indicateEOL)); +} + +int KateTextLayout::length( ) const +{ + if (!isValid()) + return 0; + + return m_textLayout.textLength(); +} + +bool KateTextLayout::isEmpty( ) const +{ + if (!isValid()) + return true; + + return startCol() == 0 && endCol() == 0; +} + +bool KateTextLayout::wrap( ) const +{ + if (!isValid()) + return false; + + return viewLine() < m_lineLayout->viewLineCount() - 1; +} + +int KateTextLayout::startX( ) const +{ + if (!isValid()) + return 0; + + if (m_startX == -1) + // viewLine is already > 0, from the constructor + for (int i = 0; i < viewLine(); ++i) + m_startX += (int)m_lineLayout->layout()->lineAt(i).naturalTextWidth(); + + return m_startX; +} + +int KateTextLayout::endX( ) const +{ + if (!isValid()) + return 0; + + return startX() + (int)m_textLayout.naturalTextWidth(); +} + +int KateTextLayout::width( ) const +{ + if (!isValid()) + return 0; + + return (int)m_textLayout.naturalTextWidth(); +} + +KateTextLayout KateTextLayout::invalid( ) +{ + return KateTextLayout(); +} + +bool KateTextLayout::isRightToLeft() const +{ + if (m_lineLayout) + return m_lineLayout->isRightToLeft(); + + return false; +} diff --git a/kate/part/render/katetextlayout.h b/kate/part/render/katetextlayout.h new file mode 100644 index 00000000..f15a1d58 --- /dev/null +++ b/kate/part/render/katetextlayout.h @@ -0,0 +1,112 @@ +/* This file is part of the KDE libraries + Copyright (C) 2002-2005 Hamish Rodda + Copyright (C) 2003 Anakim Border + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef _KATE_TEXTLAYOUT_H_ +#define _KATE_TEXTLAYOUT_H_ + +#include + +#include + +#include "katelinelayout.h" + + +class KateLineLayout; +typedef KSharedPtr KateLineLayoutPtr; + +/** + * This class represents one visible line of text; with dynamic wrapping, + * many KateTextLayouts can be needed to represent one actual line of text + * (ie. one KateLineLayout) + */ +class KateTextLayout +{ + friend class KateLineLayout; + friend class KateLayoutCache; + template friend class QT_PREPEND_NAMESPACE(QVector); + + public: + bool isValid() const; + static KateTextLayout invalid(); + + int line() const; + int virtualLine() const; + /** Return the index of this visual line inside the document line + (KateLineLayout). */ + int viewLine() const; + + const QTextLine& lineLayout() const; + KateLineLayoutPtr kateLineLayout() const; + + int startCol() const; + KTextEditor::Cursor start() const; + + /** + * Return the end column of this text line. + * + * \param indicateEOL set to true to return -1 if this layout is the + * end of the line, otherwise false to return the end column number + */ + int endCol(bool indicateEOL = false) const; + + /** + * Return the end position of this text line. + * + * \param indicateEOL set to true to return -1 if this layout is the + * end of the line, otherwise false to return the end column number + */ + KTextEditor::Cursor end(bool indicateEOL = false) const; + + int length() const; + bool isEmpty() const; + + bool wrap() const; + + bool isDirty() const; + bool setDirty(bool dirty = true); + + int startX() const; + int endX() const; + int width() const; + + int xOffset() const; + + bool isRightToLeft() const; + + bool includesCursor(const KTextEditor::Cursor& realCursor) const; + + friend bool operator> (const KateLineLayout& r, const KTextEditor::Cursor& c); + friend bool operator>= (const KateLineLayout& r, const KTextEditor::Cursor& c); + friend bool operator< (const KateLineLayout& r, const KTextEditor::Cursor& c); + friend bool operator<= (const KateLineLayout& r, const KTextEditor::Cursor& c); + + void debugOutput() const; + + private: + explicit KateTextLayout(KateLineLayoutPtr line = KateLineLayoutPtr(), int viewLine = 0); + + KateLineLayoutPtr m_lineLayout; + QTextLine m_textLayout; + + int m_viewLine; + mutable int m_startX; + bool m_invalidDirty; +}; + +#endif diff --git a/kate/part/schema/howtoimportschema.ui b/kate/part/schema/howtoimportschema.ui new file mode 100644 index 00000000..52611939 --- /dev/null +++ b/kate/part/schema/howtoimportschema.ui @@ -0,0 +1,97 @@ + + + KateHowToImportSchema + + + + 0 + 0 + 400 + 300 + + + + + + + How do you want to import the schema? + + + + + + + Replace current schema? + + + true + + + + + + + Replace existing schema %1 + + + + + + + + + Import as new schema: + + + + + + + false + + + + + + + + + Qt::Vertical + + + + 20 + 179 + + + + + + + + + KLineEdit + QLineEdit +
klineedit.h
+
+
+ + + + radioAsNew + toggled(bool) + newName + setEnabled(bool) + + + 110 + 110 + + + 263 + 110 + + + + +
diff --git a/kate/part/schema/katecategorydrawer.cpp b/kate/part/schema/katecategorydrawer.cpp new file mode 100644 index 00000000..63b6a234 --- /dev/null +++ b/kate/part/schema/katecategorydrawer.cpp @@ -0,0 +1,273 @@ +/*************************************************************************** + * Copyright (C) 2009 by Rafael Fernández López * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License version 2 as published by the Free Software Foundation. * + * * + * This library 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 * + * Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to * + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * + * Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +// this code is taken from SystemSettings/icons/CategoryDrawer.{h,cpp} +// Rafael agreet to relicense it under LGPLv2 or LGPLv3, just as we need it, +// see: http://lists.kde.org/?l=kwrite-devel&m=133061943317199&w=2 + +#include "katecategorydrawer.h" + +#include +#include +#include + +KateCategoryDrawer::KateCategoryDrawer() : KCategoryDrawer (0) +{ + setLeftMargin( 7 ); + setRightMargin( 7 ); +} + +void KateCategoryDrawer::drawCategory(const QModelIndex &index, + int sortRole, + const QStyleOption &option, + QPainter *painter) const +{ + Q_UNUSED( sortRole ) + + painter->setRenderHint(QPainter::Antialiasing); + + const QRect optRect = option.rect; + QFont font(QApplication::font()); + font.setBold(true); + const QFontMetrics fontMetrics = QFontMetrics(font); + const int height = categoryHeight(index, option); + const bool leftToRight = painter->layoutDirection() == Qt::LeftToRight; + + //BEGIN: decoration gradient + { + QPainterPath path(optRect.bottomLeft()); + + path.lineTo(QPoint(optRect.topLeft().x(), optRect.topLeft().y() - 3)); + const QPointF topLeft(optRect.topLeft()); + QRectF arc(topLeft, QSizeF(4, 4)); + path.arcTo(arc, 180, -90); + path.lineTo(optRect.topRight()); + path.lineTo(optRect.bottomRight()); + path.lineTo(optRect.bottomLeft()); + + QColor window(option.palette.window().color()); + const QColor base(option.palette.base().color()); + + window.setAlphaF(0.4); + + QLinearGradient decoGradient1; + if (leftToRight) { + decoGradient1.setStart(optRect.topLeft()); + decoGradient1.setFinalStop(optRect.bottomLeft()); + } else { + decoGradient1.setStart(optRect.topRight()); + decoGradient1.setFinalStop(optRect.bottomRight()); + } + decoGradient1.setColorAt(0, window); + decoGradient1.setColorAt(1, Qt::transparent); + + QLinearGradient decoGradient2; + if (leftToRight) { + decoGradient2.setStart(optRect.topLeft()); + decoGradient2.setFinalStop(optRect.topRight()); + } else { + decoGradient2.setStart(optRect.topRight()); + decoGradient2.setFinalStop(optRect.topLeft()); + } + decoGradient2.setColorAt(0, Qt::transparent); + decoGradient2.setColorAt(1, base); + + painter->fillPath(path, decoGradient1); + painter->fillPath(path, decoGradient2); + } + //END: decoration gradient + + { + QRect newOptRect(optRect); + + if (leftToRight) { + newOptRect.translate(1, 1); + } else { + newOptRect.translate(-1, 1); + } + + //BEGIN: inner top left corner + { + painter->save(); + painter->setPen(option.palette.base().color()); + QRectF arc; + if (leftToRight) { + const QPointF topLeft(newOptRect.topLeft()); + arc = QRectF(topLeft, QSizeF(4, 4)); + arc.translate(0.5, 0.5); + painter->drawArc(arc, 1440, 1440); + } else { + QPointF topRight(newOptRect.topRight()); + topRight.rx() -= 4; + arc = QRectF(topRight, QSizeF(4, 4)); + arc.translate(-0.5, 0.5); + painter->drawArc(arc, 0, 1440); + } + painter->restore(); + } + //END: inner top left corner + + //BEGIN: inner left vertical line + { + QPoint start; + QPoint verticalGradBottom; + if (leftToRight) { + start = newOptRect.topLeft(); + verticalGradBottom = newOptRect.topLeft(); + } else { + start = newOptRect.topRight(); + verticalGradBottom = newOptRect.topRight(); + } + start.ry() += 3; + verticalGradBottom.ry() += newOptRect.height() - 3; + QLinearGradient gradient(start, verticalGradBottom); + gradient.setColorAt(0, option.palette.base().color()); + gradient.setColorAt(1, Qt::transparent); + painter->fillRect(QRect(start, QSize(1, newOptRect.height() - 3)), gradient); + } + //END: inner left vertical line + + //BEGIN: inner horizontal line + { + QPoint start; + QPoint horizontalGradTop; + if (leftToRight) { + start = newOptRect.topLeft(); + horizontalGradTop = newOptRect.topLeft(); + start.rx() += 3; + horizontalGradTop.rx() += newOptRect.width() - 3; + } else { + start = newOptRect.topRight(); + horizontalGradTop = newOptRect.topRight(); + start.rx() -= 3; + horizontalGradTop.rx() -= newOptRect.width() - 3; + } + QLinearGradient gradient(start, horizontalGradTop); + gradient.setColorAt(0, option.palette.base().color()); + gradient.setColorAt(1, Qt::transparent); + QSize rectSize; + if (leftToRight) { + rectSize = QSize(newOptRect.width() - 3, 1); + } else { + rectSize = QSize(-newOptRect.width() + 3, 1); + } + painter->fillRect(QRect(start, rectSize), gradient); + } + //END: inner horizontal line + } + + QColor outlineColor = option.palette.text().color(); + outlineColor.setAlphaF(0.35); + + //BEGIN: top left corner + { + painter->save(); + painter->setPen(outlineColor); + QRectF arc; + if (leftToRight) { + const QPointF topLeft(optRect.topLeft()); + arc = QRectF(topLeft, QSizeF(4, 4)); + arc.translate(0.5, 0.5); + painter->drawArc(arc, 1440, 1440); + } else { + QPointF topRight(optRect.topRight()); + topRight.rx() -= 4; + arc = QRectF(topRight, QSizeF(4, 4)); + arc.translate(-0.5, 0.5); + painter->drawArc(arc, 0, 1440); + } + painter->restore(); + } + //END: top left corner + + //BEGIN: left vertical line + { + QPoint start; + QPoint verticalGradBottom; + if (leftToRight) { + start = optRect.topLeft(); + verticalGradBottom = optRect.topLeft(); + } else { + start = optRect.topRight(); + verticalGradBottom = optRect.topRight(); + } + start.ry() += 3; + verticalGradBottom.ry() += optRect.height() - 3; + QLinearGradient gradient(start, verticalGradBottom); + gradient.setColorAt(0, outlineColor); + gradient.setColorAt(1, option.palette.base().color()); + painter->fillRect(QRect(start, QSize(1, optRect.height() - 3)), gradient); + } + //END: left vertical line + + //BEGIN: horizontal line + { + QPoint start; + QPoint horizontalGradTop; + if (leftToRight) { + start = optRect.topLeft(); + horizontalGradTop = optRect.topLeft(); + start.rx() += 3; + horizontalGradTop.rx() += optRect.width() - 3; + } else { + start = optRect.topRight(); + horizontalGradTop = optRect.topRight(); + start.rx() -= 3; + horizontalGradTop.rx() -= optRect.width() - 3; + } + QLinearGradient gradient(start, horizontalGradTop); + gradient.setColorAt(0, outlineColor); + gradient.setColorAt(1, option.palette.base().color()); + QSize rectSize; + if (leftToRight) { + rectSize = QSize(optRect.width() - 3, 1); + } else { + rectSize = QSize(-optRect.width() + 3, 1); + } + painter->fillRect(QRect(start, rectSize), gradient); + } + //END: horizontal line + + //BEGIN: draw text + { + const QString category = index.model()->data(index, Qt::DisplayRole).toString(); // KCategorizedSortFilterProxyModel::CategoryDisplayRole).toString(); + QRect textRect = QRect(option.rect.topLeft(), QSize(option.rect.width() - 2 - 3 - 3, height)); + textRect.setTop(textRect.top() + 2 + 3 /* corner */); + textRect.setLeft(textRect.left() + 2 + 3 /* corner */ + 3 /* a bit of margin */); + painter->save(); + painter->setFont(font); + QColor penColor(option.palette.text().color()); + penColor.setAlphaF(0.6); + painter->setPen(penColor); + painter->drawText(textRect, Qt::AlignLeft | Qt::AlignTop, category); + painter->restore(); + } + //END: draw text +} + +int KateCategoryDrawer::categoryHeight(const QModelIndex &index, const QStyleOption &option) const +{ + Q_UNUSED( index ); + Q_UNUSED( option ); + + QFont font(QApplication::font()); + font.setBold(true); + const QFontMetrics fontMetrics = QFontMetrics(font); + + return fontMetrics.height() + 2 + 12 /* vertical spacing */; +} diff --git a/kate/part/schema/katecategorydrawer.h b/kate/part/schema/katecategorydrawer.h new file mode 100644 index 00000000..9fc17173 --- /dev/null +++ b/kate/part/schema/katecategorydrawer.h @@ -0,0 +1,45 @@ +/*************************************************************************** + * Copyright (C) 2009 by Rafael Fernández López * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License version 2 as published by the Free Software Foundation. * + * * + * This library 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 * + * Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to * + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * + * Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +// this code is taken from SystemSettings/icons/CategoryDrawer.{h,cpp} +// Rafael agreet to relicense it under LGPLv2 or LGPLv3, just as we need it, +// see: http://lists.kde.org/?l=kwrite-devel&m=133061943317199&w=2 + +#ifndef KATE_CATEGORYDRAWER_H +#define KATE_CATEGORYDRAWER_H + +#include + +#include +#include +#include + +class KateCategoryDrawer : public KCategoryDrawer +{ +public: + KateCategoryDrawer(); + + virtual void drawCategory(const QModelIndex &index, + int sortRole, + const QStyleOption &option, + QPainter *painter) const; + + virtual int categoryHeight(const QModelIndex &index, const QStyleOption &option) const; +}; + +#endif diff --git a/kate/part/schema/katecolortreewidget.cpp b/kate/part/schema/katecolortreewidget.cpp new file mode 100644 index 00000000..92c600d2 --- /dev/null +++ b/kate/part/schema/katecolortreewidget.cpp @@ -0,0 +1,377 @@ +/* This file is part of the KDE libraries + Copyright (C) 2012 Dominik Haumann + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "katecolortreewidget.h" + +#include "katecategorydrawer.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +//BEGIN KateColorTreeItem +class KateColorTreeItem : public QTreeWidgetItem +{ + public: + KateColorTreeItem(const KateColorItem& colorItem, QTreeWidgetItem* parent = 0) + : QTreeWidgetItem(parent) + , m_colorItem(colorItem) + { + setText(0, m_colorItem.name); + if (!colorItem.whatsThis.isEmpty()) { + setData(1, Qt::WhatsThisRole, colorItem.whatsThis); + } + if (!colorItem.useDefault) { + setData(2, Qt::ToolTipRole, i18n("Use default color from the KDE color scheme")); + } + } + + QColor color() const { + return m_colorItem.color; + } + + void setColor(const QColor& c) { + m_colorItem.color = c; + } + + QColor defaultColor() const { + return m_colorItem.defaultColor; + } + + bool useDefaultColor() const { + return m_colorItem.useDefault; + } + + void setUseDefaultColor(bool useDefault) { + m_colorItem.useDefault = useDefault; + QString tooltip = useDefault ? QString() : i18n("Use default color from the KDE color scheme"); + setData(2, Qt::ToolTipRole, tooltip); + } + + QString key() { + return m_colorItem.key; + } + + KateColorItem colorItem() const { + return m_colorItem; + } + + private: + KateColorItem m_colorItem; +}; +//END KateColorTreeItem + + +//BEGIN KateColorTreeDelegate +class KateColorTreeDelegate : public QStyledItemDelegate +{ + public: + KateColorTreeDelegate(KateColorTreeWidget* widget) + : QStyledItemDelegate(widget) + , m_tree(widget) + { + } + + QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const { + QSize sh = QStyledItemDelegate::sizeHint(option, index); + if (!index.parent().isValid()) { + sh.rheight() += 2 * m_categoryDrawer.leftMargin(); + } else { + sh.rheight() += m_categoryDrawer.leftMargin(); + } + if (index.column() == 0) { + sh.rwidth() += m_categoryDrawer.leftMargin(); + } else if (index.column() == 1) { + sh.rwidth() = 150; + } else { + sh.rwidth() += m_categoryDrawer.leftMargin(); + } + + return sh; + } + + QRect fullCategoryRect(const QStyleOptionViewItem& option, const QModelIndex& index) const { + QModelIndex i = index; + if (i.parent().isValid()) { + i = i.parent(); + } + + QTreeWidgetItem* item = m_tree->itemFromIndex(i); + QRect r = m_tree->visualItemRect(item); + + // adapt width + r.setLeft(m_categoryDrawer.leftMargin()); + r.setWidth(m_tree->viewport()->width() - m_categoryDrawer.leftMargin() - m_categoryDrawer.rightMargin()); + + // adapt height + if (item->isExpanded() && item->childCount() > 0) { + const int childCount = item->childCount(); + const int h = sizeHint(option, index.child(0, 0)).height(); + r.setHeight(r.height() + childCount * h); + } + + r.setTop(r.top() + m_categoryDrawer.leftMargin()); + + return r; + } + + virtual void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const + { + Q_ASSERT(index.isValid()); + Q_ASSERT(index.column() >= 0 && index.column() <= 2); + + //BEGIN: draw toplevel items + if (!index.parent().isValid()) { + QStyleOptionViewItem opt(option); + const QRegion cl = painter->clipRegion(); + painter->setClipRect(opt.rect); + opt.rect = fullCategoryRect(option, index); + m_categoryDrawer.drawCategory(index, 0, opt, painter); + painter->setClipRegion(cl); + return; + } + //END: draw toplevel items + + //BEGIN: draw background of category for all other items + { + QStyleOptionViewItem opt(option); + opt.rect = fullCategoryRect(option, index); + const QRegion cl = painter->clipRegion(); + QRect cr = option.rect; + if (index.column() == 0) { + if (m_tree->layoutDirection() == Qt::LeftToRight) { + cr.setLeft(5); + } else { + cr.setRight(opt.rect.right()); + } + } + painter->setClipRect(cr); + m_categoryDrawer.drawCategory(index, 0, opt, painter); + painter->setClipRegion(cl); + painter->setRenderHint(QPainter::Antialiasing, false); + } + //END: draw background of category for all other items + + // paint the text + QStyledItemDelegate::paint(painter, option, index); + if (index.column() == 0) { + return; + } + + painter->setClipRect(option.rect); + KateColorTreeItem* item = dynamic_cast(m_tree->itemFromIndex(index)); + + //BEGIN: draw color button + if (index.column() == 1) { + + QColor color = item->useDefaultColor() ? item->defaultColor() : item->color(); + + QStyleOptionButton opt; + opt.rect = option.rect; + opt.palette = m_tree->palette(); + + m_tree->style()->drawControl(QStyle::CE_PushButton, &opt, painter, m_tree); + opt.rect = m_tree->style()->subElementRect(QStyle::SE_PushButtonContents, &opt, m_tree); + opt.rect.adjust(1, 1, -1, -1); + painter->fillRect(opt.rect, color); + qDrawShadePanel(painter, opt.rect, opt.palette, true, 1, NULL); + } + //END: draw color button + + //BEGIN: draw reset icon + if (index.column() == 2 && !item->useDefaultColor()) { + + QPixmap p = SmallIcon("edit-undo"); + QRect rect(option.rect.left() + 10, option.rect.top() + (option.rect.height() - p.height() + 1) / 2, p.width(), p.height()); + + if (option.state & QStyle::State_MouseOver || option.state & QStyle::State_HasFocus) { + painter->drawPixmap(rect, p); + } else { + painter->drawPixmap(rect, SmallIcon("edit-undo", 0, KIconLoader::DisabledState)); + } + } + //END: draw reset icon + } + + private: + KateColorTreeWidget* m_tree; + KateCategoryDrawer m_categoryDrawer; +}; +//END KateColorTreeDelegate + +KateColorTreeWidget::KateColorTreeWidget(QWidget *parent) + : QTreeWidget(parent) +{ + setItemDelegate(new KateColorTreeDelegate(this)); + + QStringList headers; + headers << QString() // i18nc("@title:column the color name", "Color Role") + << QString() // i18nc("@title:column a color button", "Color") + << QString();// i18nc("@title:column use default color", "Reset") + setHeaderLabels(headers); + setHeaderHidden(true); + setRootIsDecorated(false); + setIndentation(25); +} + +bool KateColorTreeWidget::edit(const QModelIndex& index, EditTrigger trigger, QEvent* event) +{ + // accept edit only for color buttons in column 1 and reset in column 2 + if (!index.parent().isValid() || index.column() < 1) { + return QTreeWidget::edit(index, trigger, event); + } + + bool accept = false; + if (event && event->type() == QEvent::KeyPress) { + QKeyEvent* ke = static_cast(event); + accept = (ke->key() == Qt::Key_Space); // allow Space to edit + } + + switch (trigger) { + case QAbstractItemView::DoubleClicked: + case QAbstractItemView::SelectedClicked: + case QAbstractItemView::EditKeyPressed: // = F2 + accept = true; + break; + default: break; + } + + if (accept) { + KateColorTreeItem* item = dynamic_cast(itemFromIndex(index)); + QColor color = item->useDefaultColor() ? item->defaultColor() : item->color(); + + if (index.column() == 1) { + if (KColorDialog::getColor(color, item->defaultColor(), this) == QDialog::Accepted) { + item->setUseDefaultColor(false); + item->setColor(color); + viewport()->update(); + emit changed(); + } + } else if (index.column() == 2 && !item->useDefaultColor()) { + item->setUseDefaultColor(true); + viewport()->update(); + emit changed(); + } + + return false; + } + return QTreeWidget::edit(index, trigger, event); +} + +void KateColorTreeWidget::drawBranches(QPainter* painter, const QRect& rect, const QModelIndex& index) const +{ + Q_UNUSED(painter) + Q_UNUSED(rect) + Q_UNUSED(index) +} + +void KateColorTreeWidget::selectDefaults() +{ + bool somethingChanged = false; + + // use default colors for all selected items + for (int a = 0; a < topLevelItemCount(); ++a) { + QTreeWidgetItem* top = topLevelItem(a); + for (int b = 0; b < top->childCount(); ++b) { + KateColorTreeItem* it = dynamic_cast(top->child(b)); + Q_ASSERT(it); + if (!it->useDefaultColor()) { + it->setUseDefaultColor(true); + somethingChanged = true; + } + } + } + + if (somethingChanged) { + viewport()->update(); + emit changed(); + } +} + +void KateColorTreeWidget::addColorItem(const KateColorItem& colorItem) +{ + QTreeWidgetItem* categoryItem = 0; + for (int i = 0; i < topLevelItemCount(); ++i) { + if (topLevelItem(i)->text(0) == colorItem.category) { + categoryItem = topLevelItem(i); + break; + } + } + + if (!categoryItem) { + categoryItem = new QTreeWidgetItem(); + categoryItem->setText(0, colorItem.category); + addTopLevelItem(categoryItem); + expandItem(categoryItem); + } + + new KateColorTreeItem(colorItem, categoryItem); + + resizeColumnToContents(0); +} + +void KateColorTreeWidget::addColorItems(const QVector& colorItems) +{ + foreach(const KateColorItem& item, colorItems) + addColorItem(item); +} + +QVector KateColorTreeWidget::colorItems() const +{ + QVector items; + for (int a = 0; a < topLevelItemCount(); ++a) { + QTreeWidgetItem* top = topLevelItem(a); + for (int b = 0; b < top->childCount(); ++b) { + KateColorTreeItem* item = dynamic_cast(top->child(b)); + Q_ASSERT(item); + items.append(item->colorItem()); + } + } + return items; +} + +QColor KateColorTreeWidget::findColor(const QString& key) const +{ + for (int a = 0; a < topLevelItemCount(); ++a) { + QTreeWidgetItem* top = topLevelItem(a); + for (int b = 0; b < top->childCount(); ++b) { + KateColorTreeItem* item = dynamic_cast(top->child(b)); + if (item->key() == key) { + if (item->useDefaultColor()) { + return item->defaultColor(); + } else { + return item->color(); + } + } + } + } + return QColor(); +} + +// kate: indent-width 2; replace-tabs on; diff --git a/kate/part/schema/katecolortreewidget.h b/kate/part/schema/katecolortreewidget.h new file mode 100644 index 00000000..fe97f163 --- /dev/null +++ b/kate/part/schema/katecolortreewidget.h @@ -0,0 +1,74 @@ +/* This file is part of the KDE libraries + Copyright (C) 2012 Dominik Haumann + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KATE_COLOR_TREE_WIDGET_H +#define KATE_COLOR_TREE_WIDGET_H + +#include + +class KConfigGroup; +class KateColorTreeItem; + +class KateColorItem +{ + public: + KateColorItem() + : useDefault(true) + { + } + + QString name; // translated name + QString category; // translated category for tree view hierarchy + QString whatsThis; // what's this info + QString key; // untranslated id, used as key to save/load from KConfig + QColor color; // user visible color + QColor defaultColor; // used when "Default" is clicked + bool useDefault; // flag whether to use the default color +}; + +class KateColorTreeWidget : public QTreeWidget +{ + Q_OBJECT + friend class KateColorTreeItem; + friend class KateColorTreeDelegate; + + public: + explicit KateColorTreeWidget(QWidget *parent = 0); + + public: + void addColorItem(const KateColorItem& colorItem); + void addColorItems(const QVector& colorItems); + + QVector colorItems() const; + + QColor findColor(const QString& key) const; + + public Q_SLOTS: + void selectDefaults(); + + Q_SIGNALS: + void changed(); + + protected: + virtual bool edit(const QModelIndex& index, EditTrigger trigger, QEvent* event); + void drawBranches(QPainter* painter, const QRect& rect, const QModelIndex& index) const; +}; + +#endif + +// kate: indent-width 2; replace-tabs on; diff --git a/kate/part/schema/kateschema.cpp b/kate/part/schema/kateschema.cpp new file mode 100644 index 00000000..d2219894 --- /dev/null +++ b/kate/part/schema/kateschema.cpp @@ -0,0 +1,141 @@ +/* This file is part of the KDE libraries + Copyright (C) 2007, 2008 Matthew Woehlke + Copyright (C) 2001-2003 Christoph Cullmann + Copyright (C) 2002, 2003 Anders Lund + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +//BEGIN Includes +#include "kateschema.h" +#include "moc_kateschema.cpp" + +#include "kateconfig.h" +#include "kateglobal.h" +#include "kateview.h" +#include "katerenderer.h" + +#include +#include +#include +#include +#include +//END + +//BEGIN KateSchemaManager +KateSchemaManager::KateSchemaManager () + : m_config ("kateschemarc", KConfig::NoGlobals) +{ +} + +KConfigGroup KateSchemaManager::schema (const QString &name) +{ + return m_config.group (name); +} + +KateSchema KateSchemaManager::schemaData (const QString &name) +{ + KConfigGroup cg (schema (name)); + KateSchema schema; + schema.rawName = name; + schema.shippedDefaultSchema = cg.readEntry ("ShippedDefaultSchema", 0); + return schema; +} + +static bool schemasCompare (const KateSchema &s1, const KateSchema &s2) +{ + if (s1.shippedDefaultSchema > s2.shippedDefaultSchema) + return true; + + return s1.translatedName().localeAwareCompare(s1.translatedName()) < 0; +} + +QList KateSchemaManager::list () +{ + QList schemas; + Q_FOREACH (QString s, m_config.groupList()) + schemas.append (schemaData (s)); + + // sort: prio given by default schema and name + qSort(schemas.begin(), schemas.end(), schemasCompare); + + return schemas; +} +//END + +//BEGIN SCHEMA ACTION -- the 'View->Schema' menu action +void KateViewSchemaAction::init() +{ + m_group=0; + m_view = 0; + last = 0; + + connect(menu(),SIGNAL(aboutToShow()),this,SLOT(slotAboutToShow())); +} + +void KateViewSchemaAction::updateMenu (KateView *view) +{ + m_view = view; +} + +void KateViewSchemaAction::slotAboutToShow() +{ + KateView *view=m_view; + + QList schemas = KateGlobal::self()->schemaManager()->list (); + + if (!m_group) { + m_group=new QActionGroup(menu()); + m_group->setExclusive(true); + + } + + for (int z=0; z< schemas.count(); z++) + { + QString hlName = schemas[z].translatedName(); + + if (!names.contains(hlName)) + { + names << hlName; + QAction *a=menu()->addAction ( hlName, this, SLOT(setSchema())); + a->setData(schemas[z].rawName); + a->setCheckable(true); + a->setActionGroup(m_group); + } + } + + if (!view) return; + + QString id=view->renderer()->config()->schema(); + foreach(QAction *a,menu()->actions()) { + a->setChecked(a->data().toString()==id); + + } +} + +void KateViewSchemaAction::setSchema () { + QAction *action = qobject_cast(sender()); + + if (!action) return; + QString mode=action->data().toString(); + + KateView *view=m_view; + + if (view) + view->renderer()->config()->setSchema (mode); +} +//END SCHEMA ACTION + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/schema/kateschema.h b/kate/part/schema/kateschema.h new file mode 100644 index 00000000..a760ba49 --- /dev/null +++ b/kate/part/schema/kateschema.h @@ -0,0 +1,107 @@ +/* This file is part of the KDE libraries + Copyright (C) 2001-2003 Christoph Cullmann + Copyright (C) 2002, 2003 Anders Lund + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef __KATE_SCHEMA_H__ +#define __KATE_SCHEMA_H__ + +#include +#include +#include + +#include +#include + +class KateView; +#include + +class KateSchema +{ + public: + QString rawName; + int shippedDefaultSchema; + + /** + * construct translated name for shipped schemas + */ + QString translatedName () const { + return shippedDefaultSchema ? i18nc("Color Schema", rawName.toUtf8()) : rawName; + } +}; + +class KateSchemaManager +{ + public: + KateSchemaManager (); + + /** + * Config + */ + KConfig &config () + { + return m_config; + } + + /** + * return kconfiggroup for the given schema + */ + KConfigGroup schema (const QString &name); + + /** + * return schema data for on schema + */ + KateSchema schemaData (const QString &name); + + /** + * Constructs list of schemas atm known in config object + */ + QList list (); + + private: + KConfig m_config; +}; + + +class KateViewSchemaAction : public KActionMenu +{ + Q_OBJECT + + public: + KateViewSchemaAction(const QString& text, QObject *parent) + : KActionMenu(text, parent) { init(); } + + void updateMenu (KateView *view); + + private: + void init(); + + QPointer m_view; + QStringList names; + QActionGroup *m_group; + int last; + + public Q_SLOTS: + void slotAboutToShow(); + + private Q_SLOTS: + void setSchema(); +}; + +#endif + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/schema/kateschemaconfig.cpp b/kate/part/schema/kateschemaconfig.cpp new file mode 100644 index 00000000..80b61d9e --- /dev/null +++ b/kate/part/schema/kateschemaconfig.cpp @@ -0,0 +1,1262 @@ +/* This file is part of the KDE libraries + Copyright (C) 2007, 2008 Matthew Woehlke + Copyright (C) 2001-2003 Christoph Cullmann + Copyright (C) 2002, 2003 Anders Lund + Copyright (C) 2012 Dominik Haumann + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +//BEGIN Includes +#include "kateschemaconfig.h" +#include "moc_kateschemaconfig.cpp" + +#include "kateschema.h" +#include "kateconfig.h" +#include "kateglobal.h" +#include "kateview.h" +#include "katerenderer.h" +#include "katestyletreewidget.h" +#include "katecolortreewidget.h" +#include "katedefaultcolors.h" + +#include "ui_howtoimportschema.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +//END + + +//BEGIN KateSchemaConfigColorTab -- 'Colors' tab +KateSchemaConfigColorTab::KateSchemaConfigColorTab() +{ + QGridLayout* l = new QGridLayout(this); + setLayout(l); + + ui = new KateColorTreeWidget(this); + QPushButton* btnUseColorScheme = new QPushButton(i18n("Use KDE Color Scheme"), this); + + l->addWidget(ui, 0, 0, 1, 2); + l->addWidget(btnUseColorScheme, 1, 1); + + l->setColumnStretch(0, 1); + l->setColumnStretch(1, 0); + + connect(btnUseColorScheme, SIGNAL(clicked()), ui, SLOT(selectDefaults())); + connect(ui, SIGNAL(changed()), SIGNAL(changed())); +} + +KateSchemaConfigColorTab::~KateSchemaConfigColorTab() +{ +} + +QVector KateSchemaConfigColorTab::colorItemList() const +{ + QVector items; + + KateDefaultColors colors; + + // + // editor background colors + // + KateColorItem ci; + ci.category = i18n("Editor Background Colors"); + + ci.name = i18n("Text Area"); + ci.key = "Color Background"; + ci.whatsThis = i18n("

Sets the background color of the editing area.

"); + ci.defaultColor = colors.color(Kate::Background); + items.append(ci); + + ci.name = i18n("Selected Text"); + ci.key = "Color Selection"; + ci.whatsThis = i18n("

Sets the background color of the selection.

To set the text color for selected text, use the "Configure Highlighting" dialog.

"); + ci.defaultColor = colors.color(Kate::SelectionBackground); + items.append(ci); + + ci.name = i18n("Current Line"); + ci.key = "Color Highlighted Line"; + ci.whatsThis = i18n("

Sets the background color of the currently active line, which means the line where your cursor is positioned.

"); + ci.defaultColor = colors.color(Kate::HighlightedLineBackground); + items.append(ci); + + ci.name = i18n("Search Highlight"); + ci.key = "Color Search Highlight"; + ci.whatsThis = i18n("

Sets the background color of search results.

"); + ci.defaultColor = colors.color(Kate::SearchHighlight); + items.append(ci); + + ci.name = i18n("Replace Highlight"); + ci.key = "Color Replace Highlight"; + ci.whatsThis = i18n("

Sets the background color of replaced text.

"); + ci.defaultColor = colors.color(Kate::ReplaceHighlight); + items.append(ci); + + + // + // icon border + // + ci.category = i18n("Icon Border"); + + ci.name = i18n("Background Area"); + ci.key = "Color Icon Bar"; + ci.whatsThis = i18n("

Sets the background color of the icon border.

"); + ci.defaultColor = colors.color(Kate::IconBar); + items.append(ci); + + ci.name = i18n("Line Numbers"); + ci.key = "Color Line Number"; + ci.whatsThis = i18n("

This color will be used to draw the line numbers (if enabled).

"); + ci.defaultColor = colors.color(Kate::LineNumber); + items.append(ci); + + ci.name = i18n("Separator"); + ci.key = "Color Separator"; + ci.whatsThis = i18n("

This color will be used to draw the line between line numbers and the icon borders, if both are enabled.

"); + ci.defaultColor = colors.color(Kate::Separator); + items.append(ci); + + ci.name = i18n("Word Wrap Marker"); + ci.key = "Color Word Wrap Marker"; + ci.whatsThis = i18n("

Sets the color of Word Wrap-related markers:

Static Word Wrap
A vertical line which shows the column where text is going to be wrapped
Dynamic Word Wrap
An arrow shown to the left of visually-wrapped lines
"); + ci.defaultColor = colors.color(Kate::WordWrapMarker); + items.append(ci); + + ci.name = i18n("Code Folding"); + ci.key = "Color Code Folding"; + ci.whatsThis = i18n("

Sets the color of the code folding bar.

"); + ci.defaultColor = colors.color(Kate::CodeFolding); + items.append(ci); + + + ci.name = i18n("Modified Lines"); + ci.key = "Color Modified Lines"; + ci.whatsThis = i18n("

Sets the color of the line modification marker for modified lines.

"); + ci.defaultColor = colors.color(Kate::ModifiedLine); + items.append(ci); + + ci.name = i18n("Saved Lines"); + ci.key = "Color Saved Lines"; + ci.whatsThis = i18n("

Sets the color of the line modification marker for saved lines.

"); + ci.defaultColor = colors.color(Kate::SavedLine); + items.append(ci); + + + // + // text decorations + // + ci.category = i18n("Text Decorations"); + + ci.name = i18n("Spelling Mistake Line"); + ci.key = "Color Spelling Mistake Line"; + ci.whatsThis = i18n("

Sets the color of the line that is used to indicate spelling mistakes.

"); + ci.defaultColor = colors.color(Kate::SpellingMistakeLine); + items.append(ci); + + ci.name = i18n("Tab and Space Markers"); + ci.key = "Color Tab Marker"; + ci.whatsThis = i18n("

Sets the color of the tabulator marks.

"); + ci.defaultColor = colors.color(Kate::TabMarker); + items.append(ci); + + ci.name = i18n("Indentation Line"); + ci.key = "Color Indentation Line"; + ci.whatsThis = i18n("

Sets the color of the vertical indentation lines.

"); + ci.defaultColor = colors.color(Kate::IndentationLine); + items.append(ci); + + ci.name = i18n("Bracket Highlight"); + ci.key = "Color Highlighted Bracket"; + ci.whatsThis = i18n("

Sets the bracket matching color. This means, if you place the cursor e.g. at a (, the matching ) will be highlighted with this color.

"); + ci.defaultColor = colors.color(Kate::HighlightedBracket); + items.append(ci); + + + // + // marker colors + // + ci.category = i18n("Marker Colors"); + + const QString markerNames[Kate::LAST_MARK + 1] = { + i18n("Bookmark"), + i18n("Active Breakpoint"), + i18n("Reached Breakpoint"), + i18n("Disabled Breakpoint"), + i18n("Execution"), + i18n("Warning"), + i18n("Error") + }; + + ci.whatsThis = i18n("

Sets the background color of mark type.

Note: The marker color is displayed lightly because of transparency.

"); + for (int i = Kate::FIRST_MARK; i <= Kate::LAST_MARK; ++i) { + ci.defaultColor = colors.mark(i); + ci.name = markerNames[i]; + ci.key = "Color MarkType " + QString::number(i + 1); + items.append(ci); + } + + // + // finally, add all elements + // + return items; +} + +void KateSchemaConfigColorTab::schemaChanged ( const QString &newSchema ) +{ + // save curent schema + if ( !m_currentSchema.isEmpty() ) { + if (m_schemas.contains(m_currentSchema)) { + m_schemas.remove(m_currentSchema); // clear this color schema + } + + // now add it again + m_schemas[m_currentSchema] = ui->colorItems(); + } + + if ( newSchema == m_currentSchema ) return; + + // switch + m_currentSchema = newSchema; + + // If we havent this schema, read in from config file + if ( ! m_schemas.contains( newSchema ) ) + { + KConfigGroup config = KateGlobal::self()->schemaManager()->schema(newSchema); + QVector items = readConfig(config); + + m_schemas[ newSchema ] = items; + } + + // first block signals otherwise setColor emits changed + const bool blocked = blockSignals(true); + + ui->clear(); + ui->addColorItems(m_schemas[m_currentSchema]); + + blockSignals(blocked); +} + +QVector KateSchemaConfigColorTab::readConfig(KConfigGroup& config) +{ + QVector items = colorItemList(); + for (int i = 0; i < items.count(); ++i ) { + KateColorItem& item(items[i]); + item.useDefault = !config.hasKey(item.key); + if (item.useDefault) { + item.color = item.defaultColor; + } else { + item.color = config.readEntry(item.key, item.defaultColor); + if (!item.color.isValid()) { + config.deleteEntry(item.key); + item.useDefault = true; + item.color = item.defaultColor; + } + } + } + return items; +} + +void KateSchemaConfigColorTab::importSchema(KConfigGroup& config) +{ + m_schemas[m_currentSchema] = readConfig(config); + + // first block signals otherwise setColor emits changed + const bool blocked = blockSignals(true); + + ui->clear(); + ui->addColorItems(m_schemas[m_currentSchema]); + + blockSignals(blocked); +} + +void KateSchemaConfigColorTab::exportSchema(KConfigGroup& config) +{ + QVector items = ui->colorItems(); + foreach (const KateColorItem& item, items) { + QColor c = item.useDefault ? item.defaultColor : item.color; + config.writeEntry(item.key, c); + } +} + +void KateSchemaConfigColorTab::apply () +{ + schemaChanged( m_currentSchema ); + QMap >::Iterator it; + for ( it = m_schemas.begin(); it != m_schemas.end(); ++it ) + { + KConfigGroup config = KateGlobal::self()->schemaManager()->schema( it.key() ); + kDebug(13030) << "writing 'Color' tab: scheme =" << it.key() + << "and config group =" << config.name(); + + foreach (const KateColorItem& item, m_schemas[it.key()]) { + if (item.useDefault) { + config.deleteEntry(item.key); + } else { + config.writeEntry(item.key, item.color); + } + } + + // add dummy entry to prevent the config group from being empty. + // As if the group is empty, KateSchemaManager will not find it anymore. + config.writeEntry("dummy", "prevent-empty-group"); + } + + // all colors are written, so throw away all cached schemas + m_schemas.clear(); +} + +void KateSchemaConfigColorTab::reload() +{ + // drop all cached data + m_schemas.clear(); + + // load from config + KConfigGroup config = KateGlobal::self()->schemaManager()->schema(m_currentSchema); + QVector items = readConfig(config); + + // first block signals otherwise setColor emits changed + const bool blocked = blockSignals(true); + + ui->clear(); + ui->addColorItems(items); + + blockSignals(blocked); +} + +QColor KateSchemaConfigColorTab::backgroundColor() const +{ + return ui->findColor("Color Background"); +} + +QColor KateSchemaConfigColorTab::selectionColor() const +{ + return ui->findColor("Color Selection"); +} +//END KateSchemaConfigColorTab + +//BEGIN FontConfig -- 'Fonts' tab +KateSchemaConfigFontTab::KateSchemaConfigFontTab() +{ + QGridLayout *grid = new QGridLayout( this ); + + m_fontchooser = new KFontChooser ( this, KFontChooser::NoDisplayFlags ); + grid->addWidget( m_fontchooser, 0, 0); +} + +KateSchemaConfigFontTab::~KateSchemaConfigFontTab() +{ +} + +void KateSchemaConfigFontTab::slotFontSelected( const QFont &font ) +{ + if ( !m_currentSchema.isEmpty() ) { + m_fonts[m_currentSchema] = font; + emit changed(); + } +} + +void KateSchemaConfigFontTab::apply() +{ + QMap::Iterator it; + for ( it = m_fonts.begin(); it != m_fonts.end(); ++it ) + { + KateGlobal::self()->schemaManager()->schema( it.key() ).writeEntry( "Font", it.value() ); + } + + // all fonts are written, so throw away all cached schemas + m_fonts.clear(); +} + +void KateSchemaConfigFontTab::reload() +{ + // drop all cached data + m_fonts.clear(); + + // now set current schema font in the font chooser + schemaChanged(m_currentSchema); +} + +void KateSchemaConfigFontTab::schemaChanged( const QString &newSchema ) +{ + m_currentSchema = newSchema; + + // reuse font, if cached + QFont newFont (KGlobalSettings::fixedFont()); + if (m_fonts.contains(m_currentSchema)) { + newFont = m_fonts[m_currentSchema]; + } else { + newFont = KateGlobal::self()->schemaManager()->schema(m_currentSchema).readEntry("Font", newFont); + } + + m_fontchooser->disconnect ( this ); + m_fontchooser->setFont ( newFont ); + connect (m_fontchooser, SIGNAL (fontSelected(QFont)), this, SLOT (slotFontSelected(QFont))); +} + +void KateSchemaConfigFontTab::importSchema(KConfigGroup& config) +{ + QFont f (KGlobalSettings::fixedFont()); + m_fontchooser->setFont(config.readEntry("Font", f)); + m_fonts[m_currentSchema] = m_fontchooser->font(); +} + +void KateSchemaConfigFontTab::exportSchema(KConfigGroup& config) +{ + config.writeEntry("Font", m_fontchooser->font()); +} +//END FontConfig + +//BEGIN FontColorConfig -- 'Normal Text Styles' tab +KateSchemaConfigDefaultStylesTab::KateSchemaConfigDefaultStylesTab(KateSchemaConfigColorTab* colorTab) +{ + m_colorTab = colorTab; + + // size management + QGridLayout *grid = new QGridLayout( this ); + + m_defaultStyles = new KateStyleTreeWidget( this ); + m_defaultStyles->setRootIsDecorated(false); + connect(m_defaultStyles, SIGNAL(changed()), this, SIGNAL(changed())); + grid->addWidget( m_defaultStyles, 0, 0); + + m_defaultStyles->setWhatsThis(i18n( + "

This list displays the default styles for the current schema and " + "offers the means to edit them. The style name reflects the current " + "style settings.

" + "

To edit the colors, click the colored squares, or select the color " + "to edit from the popup menu.

You can unset the Background and Selected " + "Background colors from the popup menu when appropriate.

") ); +} + +KateSchemaConfigDefaultStylesTab::~KateSchemaConfigDefaultStylesTab() +{ + qDeleteAll(m_defaultStyleLists); +} + +KateAttributeList *KateSchemaConfigDefaultStylesTab::attributeList (const QString &schema) +{ + if (!m_defaultStyleLists.contains(schema)) + { + KateAttributeList *list = new KateAttributeList (); + KateHlManager::self()->getDefaults(schema, *list); + + m_defaultStyleLists.insert (schema, list); + } + + return m_defaultStyleLists[schema]; +} + +void KateSchemaConfigDefaultStylesTab::schemaChanged (const QString &schema) +{ + m_currentSchema = schema; + + m_defaultStyles->clear (); + + KateAttributeList *l = attributeList (schema); + updateColorPalette(l->at(0)->foreground().color()); + + for ( uint i = 0; i < KateHlManager::self()->defaultStyles(); i++ ) + { + m_defaultStyles->addItem( KateHlManager::self()->defaultStyleName(i, true), l->at( i ) ); + } +} + +void KateSchemaConfigDefaultStylesTab::updateColorPalette(const QColor& textColor) +{ + QPalette p ( m_defaultStyles->palette() ); + p.setColor( QPalette::Base, m_colorTab->backgroundColor() ); + p.setColor( QPalette::Highlight, m_colorTab->selectionColor() ); + p.setColor( QPalette::Text, textColor ); + m_defaultStyles->setPalette( p ); +} + +void KateSchemaConfigDefaultStylesTab::reload () +{ + m_defaultStyles->clear (); + qDeleteAll(m_defaultStyleLists); + m_defaultStyleLists.clear (); + + schemaChanged(m_currentSchema); +} + +void KateSchemaConfigDefaultStylesTab::apply () +{ + QHashIterator it = m_defaultStyleLists; + while (it.hasNext()) { + it.next(); + KateHlManager::self()->setDefaults(it.key(), *it.value()); + } +} + +void KateSchemaConfigDefaultStylesTab::exportSchema(const QString &schema, KConfig *cfg) +{ + KateHlManager::self()->setDefaults(schema, *(m_defaultStyleLists[schema]),cfg); +} + +void KateSchemaConfigDefaultStylesTab::importSchema(const QString& schemaName, const QString &schema, KConfig *cfg) +{ + KateHlManager::self()->getDefaults(schemaName, *(m_defaultStyleLists[schema]),cfg); +} + +void KateSchemaConfigDefaultStylesTab::showEvent(QShowEvent* event) +{ + if (!event->spontaneous() && !m_currentSchema.isEmpty()) { + KateAttributeList *l = attributeList (m_currentSchema); + Q_ASSERT(l != 0); + updateColorPalette(l->at(0)->foreground().color()); + } + + QWidget::showEvent(event); +} +//END FontColorConfig + +//BEGIN KateSchemaConfigHighlightTab -- 'Highlighting Text Styles' tab +KateSchemaConfigHighlightTab::KateSchemaConfigHighlightTab(KateSchemaConfigDefaultStylesTab *page, KateSchemaConfigColorTab* colorTab) +{ + m_defaults = page; + m_colorTab = colorTab; + + m_hl = 0; + + QVBoxLayout *layout = new QVBoxLayout(this); + + // hl chooser + KHBox *hbHl = new KHBox( this ); + layout->addWidget (hbHl); + + hbHl->setSpacing( -1 ); + QLabel *lHl = new QLabel( i18n("H&ighlight:"), hbHl ); + hlCombo = new KComboBox( hbHl ); + hlCombo->setEditable( false ); + lHl->setBuddy( hlCombo ); + connect( hlCombo, SIGNAL(activated(int)), + this, SLOT(hlChanged(int)) ); + + QPushButton *btnexport = new QPushButton( i18n("Export..."), hbHl ); + QPushButton *btnimport = new QPushButton( i18n("Import..."), hbHl ); + + qobject_cast(hbHl->layout())->addStretch(); + + connect( btnexport,SIGNAL(clicked()),this,SLOT(exportHl())); + connect( btnimport,SIGNAL(clicked()),this,SLOT(importHl())); + + for( int i = 0; i < KateHlManager::self()->highlights(); i++) { + if (KateHlManager::self()->hlSection(i).length() > 0) + hlCombo->addItem(KateHlManager::self()->hlSection(i) + QString ("/") + KateHlManager::self()->hlNameTranslated(i)); + else + hlCombo->addItem(KateHlManager::self()->hlNameTranslated(i)); + } + hlCombo->setCurrentIndex(0); + + // styles listview + m_styles = new KateStyleTreeWidget( this, true ); + connect(m_styles, SIGNAL(changed()), this, SIGNAL(changed())); + layout->addWidget (m_styles, 999); + + // get current highlighting from the host application + int hl = 0; + KTextEditor::MdiContainer *iface = qobject_cast(KateGlobal::self()->container()); + if (iface) { + KateView *kv = qobject_cast(iface->activeView()); + if (kv) { + const QString hlName = kv->doc()->highlight()->name(); + hl = KateHlManager::self()->nameFind(hlName); + Q_ASSERT(hl >= 0); + } + } + hlCombo->setCurrentIndex ( hl ); + hlChanged ( hl ); + + m_styles->setWhatsThis(i18n( + "

This list displays the contexts of the current syntax highlight mode and " + "offers the means to edit them. The context name reflects the current " + "style settings.

To edit using the keyboard, press " + "<SPACE> and choose a property from the popup menu.

" + "

To edit the colors, click the colored squares, or select the color " + "to edit from the popup menu.

You can unset the Background and Selected " + "Background colors from the context menu when appropriate.

") ); +} + +KateSchemaConfigHighlightTab::~KateSchemaConfigHighlightTab() +{ +} + +void KateSchemaConfigHighlightTab::hlChanged(int z) +{ + m_hl = z; + schemaChanged (m_schema); +} + +bool KateSchemaConfigHighlightTab::loadAllHlsForSchema(const QString &schema) +{ + QProgressDialog progress(i18n("Loading all highlightings for schema"), QString(), 0, KateHlManager::self()->highlights(), this); + progress.setWindowModality(Qt::WindowModal); + for (int i = 0; i < KateHlManager::self()->highlights(); ++i) { + if (!m_hlDict[schema].contains(i)) + { + kDebug(13030) << "NEW HL, create list"; + + QList list; + KateHlManager::self()->getHl(i)->getKateExtendedAttributeListCopy(schema, list); + m_hlDict[schema].insert (i, list); + } + progress.setValue(progress.value() + 1); + } + progress.setValue(KateHlManager::self()->highlights()); + return true; +} + +void KateSchemaConfigHighlightTab::schemaChanged (const QString &schema) +{ + m_schema = schema; + + kDebug(13030) << "NEW SCHEMA: " << m_schema << " NEW HL: " << m_hl; + + m_styles->clear (); + + if (!m_hlDict.contains(m_schema)) + { + kDebug(13030) << "NEW SCHEMA, create dict"; + + m_hlDict.insert (schema, QHash >()); + } + + if (!m_hlDict[m_schema].contains(m_hl)) + { + kDebug(13030) << "NEW HL, create list"; + + QList list; + KateHlManager::self()->getHl( m_hl )->getKateExtendedAttributeListCopy(m_schema, list); + m_hlDict[m_schema].insert (m_hl, list); + } + + KateAttributeList *l = m_defaults->attributeList (schema); + + // Set listview colors + updateColorPalette(l->at(0)->foreground().color()); + + QHash prefixes; + QList::ConstIterator it = m_hlDict[m_schema][m_hl].constBegin(); + while (it != m_hlDict[m_schema][m_hl].constEnd()) + { + const KateExtendedAttribute::Ptr itemData = *it; + Q_ASSERT(itemData); + + kDebug(13030) << "insert items " << itemData->name(); + + // All stylenames have their language mode prefixed, e.g. HTML:Comment + // split them and put them into nice substructures. + int c = itemData->name().indexOf(':'); + if ( c > 0 ) { + QString prefix = itemData->name().left(c); + QString name = itemData->name().mid(c+1); + + QTreeWidgetItem *parent = prefixes[prefix]; + if ( ! parent ) + { + parent = new QTreeWidgetItem( m_styles, QStringList() << prefix ); + m_styles->expandItem(parent); + prefixes.insert( prefix, parent ); + } + m_styles->addItem( parent, name, l->at(itemData->defaultStyleIndex()), itemData ); + } else { + m_styles->addItem( itemData->name(), l->at(itemData->defaultStyleIndex()), itemData ); + } + ++it; + } + + m_styles->resizeColumns(); +} + +void KateSchemaConfigHighlightTab::updateColorPalette(const QColor& textColor) +{ + QPalette p ( m_styles->palette() ); + p.setColor( QPalette::Base, m_colorTab->backgroundColor() ); + p.setColor( QPalette::Highlight, m_colorTab->selectionColor() ); + p.setColor( QPalette::Text, textColor ); + m_styles->setPalette( p ); +} + +void KateSchemaConfigHighlightTab::reload () +{ + m_styles->clear (); + + m_hlDict.clear (); + + hlChanged( hlCombo->currentIndex() ); +} + +void KateSchemaConfigHighlightTab::apply () +{ + QMutableHashIterator > > it = m_hlDict; + while (it.hasNext()) { + it.next(); + QMutableHashIterator > it2 = it.value(); + while (it2.hasNext()) { + it2.next(); + KateHlManager::self()->getHl( it2.key() )->setKateExtendedAttributeList (it.key(), it2.value()); + } + } +} + + +QList KateSchemaConfigHighlightTab::hlsForSchema(const QString &schema) { + return m_hlDict[schema].keys(); +} + + +void KateSchemaConfigHighlightTab::importHl(const QString& fromSchemaName, QString schema, int hl, KConfig *cfg) { + QString schemaNameForLoading(fromSchemaName); + QString hlName; + bool doManage=(cfg==0); + if (schema.isEmpty()) schema=m_schema; + + if (doManage) { + QString srcName=KFileDialog::getOpenFileName( QString(KateHlManager::self()->getHl(hl)->name()+QString(".katehlcolor")), + QString::fromLatin1("*.katehlcolor|%1").arg(i18n("Kate color schema")), + this, + i18n("Importing colors for single highlighting")); + kDebug(13030)<<"hl file to open "<nameFind(hlName); + kDebug(13030)<"< list; + KateHlManager::self()->getHl( hl )->getKateExtendedAttributeListCopy(schemaNameForLoading, list, cfg); + KateHlManager::self()->getHl( hl )->setKateExtendedAttributeList(schema, list); + m_hlDict[schema].insert (hl, list); + } + + if (cfg && doManage) { + apply(); + delete cfg; + cfg=0; + if ( (hl!=-1) && (!schemaNameForLoading.isEmpty())) { + hlChanged(m_hl); + KMessageBox::information( + this, + i18n("Colors have been imported for highlighting: %1",hlName), + i18n("Import has finished")); + } + } + + +} + + +void KateSchemaConfigHighlightTab::exportHl(QString schema, int hl, KConfig *cfg) { + bool doManage=(cfg==0); + if (schema.isEmpty()) schema=m_schema; + if (hl==-1) hl=m_hl; + + QList items=m_hlDict[schema][hl]; + if (doManage) { + QString destName=KFileDialog::getSaveFileName( QString(KateHlManager::self()->getHl(hl)->name()+".katehlcolor"), + QString::fromLatin1("*.katehlcolor|%1").arg(i18n("Kate color schema")), + this, + i18n("Exporting colors for single highlighting: %1", KateHlManager::self()->getHl(hl)->name()), + KFileDialog::ConfirmOverwrite ); + + if (destName.isEmpty()) return; + + cfg=new KConfig(destName,KConfig::SimpleConfig); + KConfigGroup grp(cfg,"KateHLColors"); + grp.writeEntry("highlight",KateHlManager::self()->getHl(hl)->name()); + grp.writeEntry("schema",schema); + grp.writeEntry("full schema","false"); + } + KateHlManager::self()->getHl(hl)->setKateExtendedAttributeList(schema,items,cfg,doManage); + + if (doManage) { + cfg->sync(); + delete cfg; + } + +} + +void KateSchemaConfigHighlightTab::showEvent(QShowEvent* event) +{ + if (!event->spontaneous()) { + KateAttributeList *l = m_defaults->attributeList (m_schema); + Q_ASSERT(l != 0); + updateColorPalette(l->at(0)->foreground().color()); + } + + QWidget::showEvent(event); +} +//END KateSchemaConfigHighlightTab + +//BEGIN KateSchemaConfigPage -- Main dialog page +KateSchemaConfigPage::KateSchemaConfigPage( QWidget *parent) + : KateConfigPage( parent ), + m_currentSchema (-1) +{ + QVBoxLayout *layout = new QVBoxLayout(this); + layout->setMargin(0); + + KHBox *hbHl = new KHBox( this ); + layout->addWidget(hbHl); + hbHl->setSpacing( -1 ); + QLabel *lHl = new QLabel( i18n("&Schema:"), hbHl ); + schemaCombo = new KComboBox( hbHl ); + schemaCombo->setEditable( false ); + lHl->setBuddy( schemaCombo ); + connect( schemaCombo, SIGNAL(currentIndexChanged(int)), + this, SLOT(comboBoxIndexChanged(int)) ); + + QPushButton *btnnew = new QPushButton( i18n("&New..."), hbHl ); + connect( btnnew, SIGNAL(clicked()), this, SLOT(newSchema()) ); + + btndel = new QPushButton( i18n("&Delete"), hbHl ); + connect( btndel, SIGNAL(clicked()), this, SLOT(deleteSchema()) ); + + QPushButton *btnexport = new QPushButton( i18n("Export..."), hbHl ); + connect(btnexport,SIGNAL(clicked()),this,SLOT(exportFullSchema())); + QPushButton *btnimport = new QPushButton( i18n("Import..."), hbHl ); + connect(btnimport,SIGNAL(clicked()),this,SLOT(importFullSchema())); + + qobject_cast(hbHl->layout())->addStretch(); + + m_tabWidget = new KTabWidget ( this ); + layout->addWidget (m_tabWidget); + + m_colorTab = new KateSchemaConfigColorTab(); + m_tabWidget->addTab (m_colorTab, i18n("Colors")); + connect(m_colorTab, SIGNAL(changed()), SLOT(slotChanged())); + + m_fontTab = new KateSchemaConfigFontTab(); + m_tabWidget->addTab (m_fontTab, i18n("Font")); + connect(m_fontTab, SIGNAL(changed()), SLOT(slotChanged())); + + m_defaultStylesTab = new KateSchemaConfigDefaultStylesTab(m_colorTab); + m_tabWidget->addTab (m_defaultStylesTab, i18n("Default Text Styles")); + connect(m_defaultStylesTab, SIGNAL(changed()), SLOT(slotChanged())); + + m_highlightTab = new KateSchemaConfigHighlightTab(m_defaultStylesTab, m_colorTab); + m_tabWidget->addTab(m_highlightTab, i18n("Highlighting Text Styles")); + connect(m_highlightTab, SIGNAL(changed()), SLOT(slotChanged())); + + hbHl = new KHBox( this ); + layout->addWidget (hbHl); + hbHl->setSpacing( -1 ); + lHl = new QLabel( i18n("&Default schema for %1:", KGlobal::mainComponent().aboutData()->programName ()), hbHl ); + defaultSchemaCombo = new KComboBox( hbHl ); + defaultSchemaCombo->setEditable( false ); + lHl->setBuddy( defaultSchemaCombo ); + + reload(); + + connect( defaultSchemaCombo, SIGNAL(currentIndexChanged(int)), + this, SLOT(slotChanged()) ); +} + +KateSchemaConfigPage::~KateSchemaConfigPage () +{ +} + +void KateSchemaConfigPage::exportFullSchema() +{ + // get save destination + const QString currentSchemaName = m_currentSchema; + QString destName = KFileDialog::getSaveFileName( + QString(currentSchemaName + ".kateschema"), + QString::fromLatin1("*.kateschema|%1").arg(i18n("Kate color schema")), + this, + i18n("Exporting color schema: %1", currentSchemaName), + KFileDialog::ConfirmOverwrite ); + + if (destName.isEmpty()) return; + + // open config file + KConfig cfg(destName, KConfig::SimpleConfig); + + // + // export editor Colors (background, ...) + // + KConfigGroup colorConfigGroup(&cfg, "Editor Colors"); + m_colorTab->exportSchema(colorConfigGroup); + + // + // export Default Styles + // + m_defaultStylesTab->exportSchema(m_currentSchema, &cfg); + + // + // export Highlighting Text Styles + // + // force a load of all Highlighting Text Styles + QStringList hlList; + m_highlightTab->loadAllHlsForSchema(m_currentSchema); + + QList hls = m_highlightTab->hlsForSchema(m_currentSchema); + + int cnt = 0; + QProgressDialog progress(i18n("Exporting schema"), QString(), 0, hls.count(), this); + progress.setWindowModality(Qt::WindowModal); + foreach (int hl, hls) { + hlList << KateHlManager::self()->getHl (hl)->name(); + m_highlightTab->exportHl(m_currentSchema, hl, &cfg); + progress.setValue(++cnt); + if (progress.wasCanceled()) break; + } + progress.setValue(hls.count()); + + KConfigGroup grp(&cfg, "KateSchema"); + grp.writeEntry("full schema", "true"); + grp.writeEntry("highlightings", hlList); + grp.writeEntry("schema", currentSchemaName); + m_fontTab->exportSchema(grp); + cfg.sync(); +} + +QString KateSchemaConfigPage::requestSchemaName(const QString& suggestedName) +{ + QString schemaName = suggestedName; + + bool reask = true; + do { + KDialog howToImportDialog(this); + Ui_KateHowToImportSchema howToImport; + howToImport.setupUi(howToImportDialog.mainWidget()); + + // if schema exists, prepare option to replace + if (KateGlobal::self()->schemaManager()->schema(schemaName).exists()) { + howToImport.radioReplaceExisting->show(); + howToImport.radioReplaceExisting->setText(i18n("Replace existing schema %1", schemaName)); + howToImport.radioReplaceExisting->setChecked(true); + } else { + howToImport.radioReplaceExisting->hide(); + howToImport.newName->setText(schemaName); + } + + // cancel pressed? + if (howToImportDialog.exec() == KDialog::Cancel) { + schemaName.clear(); + reask = false; + } + // check what the user wants + else { + // replace existing + if (howToImport.radioReplaceExisting->isChecked()) { + reask = false; + } + // replace current + else if (howToImport.radioReplaceCurrent->isChecked()) { + schemaName = m_currentSchema; + reask = false; + } + // new one, check again, whether the schema already exists + else if (howToImport.radioAsNew->isChecked()) { + schemaName = howToImport.newName->text(); + if (KateGlobal::self()->schemaManager()->schema(schemaName).exists()) { + reask = true; + } else reask = false; + } + // should never happen + else reask = true; + } + } while (reask); + + return schemaName; +} + +void KateSchemaConfigPage::importFullSchema() +{ + const QString srcName = KFileDialog::getOpenFileName(KUrl(), + QString::fromLatin1("*.kateschema|%1").arg(i18n("Kate color schema")), + this, i18n("Importing Color Schema")); + + if (srcName.isEmpty()) return; + + // carete config + sanity check for full color schema + KConfig cfg(srcName, KConfig::SimpleConfig); + KConfigGroup schemaGroup(&cfg, "KateSchema"); + if (schemaGroup.readEntry("full schema", "false").toUpper() != "TRUE") { + KMessageBox::sorry(this, i18n("The file does not contain a full color schema."), + i18n("Fileformat error")); + return; + } + + // read color schema name + const QStringList highlightings = schemaGroup.readEntry("highlightings",QStringList()); + const QString fromSchemaName = schemaGroup.readEntry("schema", i18n("Name unspecified")); + + // request valid schema name + const QString schemaName = requestSchemaName(fromSchemaName); + if (schemaName.isEmpty()) { + return; + } + + // if the schema already exists, select it in the combo box + if (schemaCombo->findData(schemaName) != -1) { + schemaCombo->setCurrentIndex(schemaCombo->findData(schemaName)); + } + else { // it is really a new schema, easy meat :-) + newSchema(schemaName); + } + + // make sure the correct schema is activated + schemaChanged(schemaName); + + + // Finally, the correct schema is activated. + // Next, start importing. + kDebug(13030) << "Importing schema: " << schemaName; + + // + // import editor Colors (background, ...) + // + KConfigGroup colorConfigGroup(&cfg, "Editor Colors"); + m_colorTab->importSchema(colorConfigGroup); + + // + // import font + // + m_fontTab->importSchema(schemaGroup); + + // + // import Default Styles + // + m_defaultStylesTab->importSchema(fromSchemaName, schemaName, &cfg); + + // + // import all Highlighting Text Styles + // + // create mapping from highlighting name to internal id + const int hlCount = KateHlManager::self()->highlights(); + QHash nameToId; + for(int i = 0; i < hlCount; ++i) { + nameToId.insert(KateHlManager::self()->hlName(i),i); + } + + // may take some time, as we have > 200 highlightings + int cnt = 0; + QProgressDialog progress(i18n("Importing schema"), QString(), 0, highlightings.count(), this); + progress.setWindowModality(Qt::WindowModal); + foreach(const QString& hl,highlightings) { + if (nameToId.contains(hl)) { + const int i = nameToId[hl]; + m_highlightTab->importHl(fromSchemaName, schemaName, i, &cfg); + kDebug(13030) << "hl imported:" << hl; + } else { + kDebug(13030) << "could not import hl, hl unknown:" << hl; + } + progress.setValue(++cnt); + } + progress.setValue(highlightings.count()); +} + +void KateSchemaConfigPage::apply() +{ + // remember name + index + const QString schemaName = schemaCombo->itemData (schemaCombo->currentIndex()).toString(); + + // first apply all tabs + m_colorTab->apply(); + m_fontTab->apply(); + m_defaultStylesTab->apply (); + m_highlightTab->apply (); + + // just sync the config and reload + KateGlobal::self()->schemaManager()->config ().sync(); + KateGlobal::self()->schemaManager()->config ().reparseConfiguration(); + + // clear all attributes + for (int i = 0; i < KateHlManager::self()->highlights(); ++i) + KateHlManager::self()->getHl (i)->clearAttributeArrays(); + + // than reload the whole stuff + KateRendererConfig::global()->setSchema (defaultSchemaCombo->itemData (defaultSchemaCombo->currentIndex()).toString()); + KateRendererConfig::global()->reloadSchema(); + + // sync the hl config for real + KateHlManager::self()->getKConfig()->sync (); + + // KateSchemaManager::update() sorts the schema alphabetically, hence the + // schema indexes change. Thus, repopulate the schema list... + refillCombos(schemaCombo->itemData (schemaCombo->currentIndex()).toString(), defaultSchemaCombo->itemData (defaultSchemaCombo->currentIndex()).toString()); + schemaChanged(schemaName); +} + +void KateSchemaConfigPage::reload() +{ + // now reload the config from disc + KateGlobal::self()->schemaManager()->config ().reparseConfiguration(); + + // reinitialize combo boxes + refillCombos(KateRendererConfig::global()->schema(), KateRendererConfig::global()->schema()); + + // finally, activate the current schema again + schemaChanged(schemaCombo->itemData(schemaCombo->currentIndex()).toString()); + + // all tabs need to reload to discard all the cached data, as the index + // mapping may have changed + m_colorTab->reload (); + m_fontTab->reload (); + m_defaultStylesTab->reload (); + m_highlightTab->reload (); +} + +void KateSchemaConfigPage::refillCombos(const QString& schemaName, const QString& defaultSchemaName) +{ + schemaCombo->blockSignals(true); + defaultSchemaCombo->blockSignals(true); + + // reinitialize combo boxes + schemaCombo->clear(); + defaultSchemaCombo->clear(); + QList schemaList = KateGlobal::self()->schemaManager()->list(); + foreach (const KateSchema& s, schemaList) { + schemaCombo->addItem(s.translatedName(), s.rawName); + defaultSchemaCombo->addItem(s.translatedName(), s.rawName); + } + + // set the correct indexes again, fallback to always existing "Normal" + int schemaIndex = schemaCombo->findData (schemaName); + if (schemaIndex == -1) + schemaIndex = schemaCombo->findData ("Normal"); + + int defaultSchemaIndex = defaultSchemaCombo->findData (defaultSchemaName); + if (defaultSchemaIndex == -1) + defaultSchemaIndex = defaultSchemaCombo->findData ("Normal"); + + Q_ASSERT(schemaIndex != -1); + Q_ASSERT(defaultSchemaIndex != -1); + + defaultSchemaCombo->setCurrentIndex (defaultSchemaIndex); + schemaCombo->setCurrentIndex (schemaIndex); + + schemaCombo->blockSignals(false); + defaultSchemaCombo->blockSignals(false); +} + +void KateSchemaConfigPage::reset() +{ + reload (); +} + +void KateSchemaConfigPage::defaults() +{ + reload (); +} + +void KateSchemaConfigPage::deleteSchema () +{ + const int comboIndex = schemaCombo->currentIndex (); + const QString schemaNameToDelete = schemaCombo->itemData (comboIndex).toString(); + + if (KateGlobal::self()->schemaManager()->schemaData(schemaNameToDelete).shippedDefaultSchema) { + // Default and Printing schema cannot be deleted. + kDebug(13030) << "default and printing schema cannot be deleted"; + return; + } + + // kill group + KateGlobal::self()->schemaManager()->config().deleteGroup (schemaNameToDelete); + + // fallback to Default schema + schemaCombo->setCurrentIndex(schemaCombo->findData (QVariant ("Normal"))); + if (defaultSchemaCombo->currentIndex() == defaultSchemaCombo->findData (schemaNameToDelete)) + defaultSchemaCombo->setCurrentIndex(defaultSchemaCombo->findData (QVariant ("Normal"))); + + // remove schema from combo box + schemaCombo->removeItem(comboIndex); + defaultSchemaCombo->removeItem(comboIndex); + + // Reload the color tab, since it uses cached schemas + m_colorTab->reload(); +} + +bool KateSchemaConfigPage::newSchema (const QString& newName) +{ + // get sane name + QString schemaName(newName); + if (newName.isEmpty()) { + bool ok = false; + schemaName = KInputDialog::getText (i18n("Name for New Schema"), i18n ("Name:"), i18n("New Schema"), &ok, this); + if (!ok) return false; + } + + // try if schema already around + if (KateGlobal::self()->schemaManager()->schema (schemaName).exists()) { + KMessageBox::information(this, i18n("

The schema %1 already exists.

Please choose a different schema name.

", schemaName), i18n("New Schema")); + return false; + } + + // append items to combo boxes + schemaCombo->addItem(schemaName, QVariant (schemaName)); + defaultSchemaCombo->addItem(schemaName, QVariant (schemaName)); + + // finally, activate new schema (last item in the list) + schemaCombo->setCurrentIndex(schemaCombo->count() - 1); + + return true; +} + +void KateSchemaConfigPage::schemaChanged (const QString &schema) +{ + btndel->setEnabled( !KateGlobal::self()->schemaManager()->schemaData(schema).shippedDefaultSchema ); + + // propagate changed schema to all tabs + m_colorTab->schemaChanged(schema); + m_fontTab->schemaChanged(schema); + m_defaultStylesTab->schemaChanged(schema); + m_highlightTab->schemaChanged(schema); + + // save current schema index + m_currentSchema = schema; +} + +void KateSchemaConfigPage::comboBoxIndexChanged (int currentIndex) +{ + schemaChanged (schemaCombo->itemData (currentIndex).toString()); +} +//END KateSchemaConfigPage + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/schema/kateschemaconfig.h b/kate/part/schema/kateschemaconfig.h new file mode 100644 index 00000000..be1139e6 --- /dev/null +++ b/kate/part/schema/kateschemaconfig.h @@ -0,0 +1,219 @@ +/* This file is part of the KDE libraries + Copyright (C) 2001-2003 Christoph Cullmann + Copyright (C) 2002, 2003 Anders Lund + Copyright (C) 2012 Dominik Haumann + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef __KATE_SCHEMA_CONFIG_H__ +#define __KATE_SCHEMA_CONFIG_H__ + +#include "katedialogs.h" +#include "katecolortreewidget.h" +#include "kateextendedattribute.h" + +#include +#include + +#include + +class KateStyleTreeWidget; + +class KComboBox; +class KTabWidget; + + +class KateSchemaConfigColorTab : public QWidget +{ + Q_OBJECT + + public: + KateSchemaConfigColorTab(); + ~KateSchemaConfigColorTab(); + + QColor backgroundColor() const; + QColor selectionColor() const; + + public Q_SLOTS: + void apply(); + void reload(); + void schemaChanged( const QString &newSchema ); + + void importSchema(KConfigGroup& config); + void exportSchema(KConfigGroup& config); + + Q_SIGNALS: + void changed(); + + private: + QVector colorItemList() const; + QVector readConfig(KConfigGroup& config); + + private: + // multiple shemas may be edited. Hence, we need one ColorList for each schema + QMap > m_schemas; + QString m_currentSchema; + + KateColorTreeWidget* ui; +}; + +class KateSchemaConfigFontTab : public QWidget +{ + Q_OBJECT + + public: + KateSchemaConfigFontTab(); + ~KateSchemaConfigFontTab(); + + public: + void readConfig (KConfig *config); + + void importSchema(KConfigGroup& config); + void exportSchema(KConfigGroup& config); + + public Q_SLOTS: + void apply(); + void reload(); + void schemaChanged( const QString &newSchema ); + + Q_SIGNALS: + void changed(); + + private: + class KFontChooser *m_fontchooser; + QMap m_fonts; + QString m_currentSchema; + + private Q_SLOTS: + void slotFontSelected( const QFont &font ); +}; + +class KateSchemaConfigDefaultStylesTab : public QWidget +{ + Q_OBJECT + + public: + KateSchemaConfigDefaultStylesTab(KateSchemaConfigColorTab* colorTab); + ~KateSchemaConfigDefaultStylesTab(); + + Q_SIGNALS: + void changed(); + + public: + void schemaChanged (const QString &schema); + void reload (); + void apply (); + + KateAttributeList *attributeList (const QString &schema); + void exportSchema(const QString &schema, KConfig *cfg); + void importSchema(const QString& schemaName, const QString &schema, KConfig *cfg); + + protected: + virtual void showEvent(QShowEvent* event); + void updateColorPalette(const QColor& textColor); + + private: + KateStyleTreeWidget* m_defaultStyles; + QHash m_defaultStyleLists; + KateSchemaConfigColorTab* m_colorTab; + QString m_currentSchema; +}; + +class KateSchemaConfigHighlightTab : public QWidget +{ + Q_OBJECT + + public: + explicit KateSchemaConfigHighlightTab(KateSchemaConfigDefaultStylesTab *page, KateSchemaConfigColorTab* colorTab); + ~KateSchemaConfigHighlightTab(); + + void schemaChanged (const QString &schema); + void reload (); + void apply (); + + Q_SIGNALS: + void changed(); + + protected Q_SLOTS: + void hlChanged(int z); + + public Q_SLOTS: + void exportHl(QString schema=QString(),int hl=-1,KConfig* cfg=0); + void importHl(const QString& fromSchemaName=QString(), QString schema=QString(), int hl=-1, KConfig *cfg=0); + + protected: + virtual void showEvent(QShowEvent* event); + void updateColorPalette(const QColor& textColor); + + private: + KateSchemaConfigDefaultStylesTab *m_defaults; + KateSchemaConfigColorTab* m_colorTab; + + KComboBox *hlCombo; + KateStyleTreeWidget *m_styles; + + QString m_schema; + int m_hl; + + QHash > > m_hlDict; + +public: + QList hlsForSchema(const QString &schema); + bool loadAllHlsForSchema(const QString &schema); +}; + +class KateSchemaConfigPage : public KateConfigPage +{ + Q_OBJECT + + public: + explicit KateSchemaConfigPage ( QWidget *parent); + virtual ~KateSchemaConfigPage (); + + public Q_SLOTS: + void apply(); + void reload(); + void reset(); + void defaults(); + void exportFullSchema(); + void importFullSchema(); + + private Q_SLOTS: + void deleteSchema (); + bool newSchema (const QString& newName = QString()); + void schemaChanged (const QString &schema); + void comboBoxIndexChanged (int currentIndex); + + private: + void refillCombos(const QString& schemaName, const QString& defaultSchemaName); + QString requestSchemaName(const QString& suggestedName); + + private: + QString m_currentSchema; + + class KTabWidget *m_tabWidget; + class QPushButton *btndel; + class KComboBox *defaultSchemaCombo; + class KComboBox *schemaCombo; + KateSchemaConfigColorTab *m_colorTab; + KateSchemaConfigFontTab *m_fontTab; + KateSchemaConfigDefaultStylesTab *m_defaultStylesTab; + KateSchemaConfigHighlightTab *m_highlightTab; +}; + +#endif + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/schema/katestyletreewidget.cpp b/kate/part/schema/katestyletreewidget.cpp new file mode 100644 index 00000000..e7dd5465 --- /dev/null +++ b/kate/part/schema/katestyletreewidget.cpp @@ -0,0 +1,717 @@ +/* This file is part of the KDE libraries + Copyright (C) 2001-2003 Christoph Cullmann + Copyright (C) 2002, 2003 Anders Lund + Copyright (C) 2005-2006 Hamish Rodda + Copyright (C) 2007 Mirko Stocker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "katestyletreewidget.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "kateconfig.h" +#include "kateextendedattribute.h" + +//BEGIN KateStyleTreeDelegate +class KateStyleTreeDelegate : public QStyledItemDelegate +{ + public: + KateStyleTreeDelegate(KateStyleTreeWidget* widget); + + virtual void paint( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const; + + private: + QBrush getBrushForColorColumn(const QModelIndex& index, int column) const; + KateStyleTreeWidget* m_widget; +}; +//END + +//BEGIN KateStyleTreeWidgetItem decl +/* + QListViewItem subclass to display/edit a style, bold/italic is check boxes, + normal and selected colors are boxes, which will display a color chooser when + activated. + The context name for the style will be drawn using the editor default font and + the chosen colors. + This widget id designed to handle the default as well as the individual hl style + lists. + This widget is designed to work with the KateStyleTreeWidget class exclusively. + Added by anders, jan 23 2002. +*/ +class KateStyleTreeWidgetItem : public QTreeWidgetItem +{ + public: + KateStyleTreeWidgetItem( QTreeWidgetItem *parent, const QString& styleName, KTextEditor::Attribute::Ptr defaultstyle, KateExtendedAttribute::Ptr data = KateExtendedAttribute::Ptr() ); + KateStyleTreeWidgetItem( QTreeWidget *parent, const QString& styleName, KTextEditor::Attribute::Ptr defaultstyle, KateExtendedAttribute::Ptr data = KateExtendedAttribute::Ptr() ); + ~KateStyleTreeWidgetItem() {} + + enum columns { + Context = 0, + Bold, + Italic, + Underline, + StrikeOut, + Foreground, + SelectedForeground, + Background, + SelectedBackground, + UseDefaultStyle, + NumColumns + }; + + /* initializes the style from the default and the hldata */ + void initStyle(); + /* updates the hldata's style */ + void updateStyle(); + /* For bool fields, toggles them, for color fields, display a color chooser */ + void changeProperty( int p ); + /** unset a color. + * c is 100 (BGColor) or 101 (SelectedBGColor) for now. + */ + void unsetColor( int c ); + /* style context name */ + QString contextName() const { return text(0); } + /* only true for a hl mode item using its default style */ + bool defStyle() const; + /* true for default styles */ + bool isDefault() const; + /* whichever style is active (currentStyle for hl mode styles not using + the default style, defaultStyle otherwise) */ + KTextEditor::Attribute::Ptr style() const { return currentStyle; } + + virtual QVariant data( int column, int role ) const; + + KateStyleTreeWidget* treeWidget() const; + + private: + /* private methods to change properties */ + void toggleDefStyle(); + void setColor( int ); + /* helper function to copy the default style into the KateExtendedAttribute, + when a property is changed and we are using default style. */ + + KTextEditor::Attribute::Ptr currentStyle, // the style currently in use (was "is") + defaultStyle; // default style for hl mode contexts and default styles (was "ds") + KateExtendedAttribute::Ptr actualStyle; // itemdata for hl mode contexts (was "st") +}; +//END + + +//BEGIN KateStyleTreeWidget +KateStyleTreeWidget::KateStyleTreeWidget( QWidget *parent, bool showUseDefaults ) + : QTreeWidget( parent ) +{ + setItemDelegate(new KateStyleTreeDelegate(this)); + setRootIsDecorated(false); + + QStringList headers; + headers << i18nc("@title:column Meaning of text in editor", "Context") << QString() << QString() << QString() << QString() << i18nc("@title:column Text style", "Normal") << i18nc("@title:column Text style", "Selected") << i18nc("@title:column Text style", "Background") << i18nc("@title:column Text style", "Background Selected"); + if(showUseDefaults) { + headers << i18n("Use Default Style"); + } + + setHeaderLabels(headers); + + headerItem()->setIcon(1, KIcon("format-text-bold")); + headerItem()->setIcon(2, KIcon("format-text-italic")); + headerItem()->setIcon(3, KIcon("format-text-underline")); + headerItem()->setIcon(4, KIcon("format-text-strikethrough")); + + // grap the bg color, selected color and default font + normalcol = KColorScheme(QPalette::Active, KColorScheme::View).foreground().color(); + bgcol = KateRendererConfig::global()->backgroundColor(); + selcol = KateRendererConfig::global()->selectionColor(); + docfont = KateRendererConfig::global()->font(); + + QPalette pal = viewport()->palette(); + pal.setColor(QPalette::Background, bgcol); + viewport()->setPalette( pal ); +} + +QIcon brushIcon(const QColor& color) +{ + QPixmap pm(16,16); + QRect all(0,0,15,15); + { + QPainter p(&pm); + p.fillRect(all, color); + p.setPen(Qt::black); + p.drawRect(all); + } + return QIcon(pm); +} + +bool KateStyleTreeWidget::edit( const QModelIndex & index, EditTrigger trigger, QEvent * event ) +{ + if(index.column() == KateStyleTreeWidgetItem::Context) + return false; + + KateStyleTreeWidgetItem *i = dynamic_cast(itemFromIndex(index)); + if (!i) + return QTreeWidget::edit(index, trigger, event); + + switch (trigger) { + case QAbstractItemView::DoubleClicked: + case QAbstractItemView::SelectedClicked: + case QAbstractItemView::EditKeyPressed: + i->changeProperty(index.column()); + update(index); + update(index.sibling(index.row(), KateStyleTreeWidgetItem::Context)); + return false; + default: + return QTreeWidget::edit(index, trigger, event); + } +} + +void KateStyleTreeWidget::resizeColumns() +{ + for (int i = 0; i < columnCount(); ++i) + resizeColumnToContents(i); +} + +void KateStyleTreeWidget::showEvent( QShowEvent * event ) +{ + QTreeWidget::showEvent(event); + + resizeColumns(); +} + +void KateStyleTreeWidget::contextMenuEvent( QContextMenuEvent * event ) +{ + KateStyleTreeWidgetItem *i = dynamic_cast(itemAt(event->pos())); + if (!i) return; + + KMenu m( this ); + KTextEditor::Attribute::Ptr currentStyle = i->style(); + // the title is used, because the menu obscures the context name when + // displayed on behalf of spacePressed(). + QPainter p; + p.setPen(Qt::black); + + QIcon cl = brushIcon( i->style()->foreground().color() ); + QIcon scl = brushIcon( i->style()->selectedForeground().color() ); + QIcon bgcl = brushIcon( i->style()->hasProperty(QTextFormat::BackgroundBrush) ? i->style()->background().color() : viewport()->palette().base().color() ); + QIcon sbgcl = brushIcon( i->style()->hasProperty(KTextEditor::Attribute::SelectedBackground) ? i->style()->selectedBackground().color() : viewport()->palette().base().color() ); + + m.addTitle( i->contextName() ); + + QAction* a = m.addAction( i18n("&Bold"), this, SLOT(changeProperty()) ); + a->setCheckable(true); + a->setChecked( currentStyle->fontBold() ); + a->setData(KateStyleTreeWidgetItem::Bold); + + a = m.addAction( i18n("&Italic"), this, SLOT(changeProperty()) ); + a->setCheckable(true); + a->setChecked( currentStyle->fontItalic() ); + a->setData(KateStyleTreeWidgetItem::Italic); + + a = m.addAction( i18n("&Underline"), this, SLOT(changeProperty()) ); + a->setCheckable(true); + a->setChecked( currentStyle->fontUnderline() ); + a->setData(KateStyleTreeWidgetItem::Underline); + + a = m.addAction( i18n("S&trikeout"), this, SLOT(changeProperty()) ); + a->setCheckable(true); + a->setChecked( currentStyle->fontStrikeOut() ); + a->setData(KateStyleTreeWidgetItem::StrikeOut); + + m.addSeparator(); + + a = m.addAction( cl, i18n("Normal &Color..."), this, SLOT(changeProperty()) ); + a->setData(KateStyleTreeWidgetItem::Foreground); + + a = m.addAction( scl, i18n("&Selected Color..."), this, SLOT(changeProperty()) ); + a->setData(KateStyleTreeWidgetItem::SelectedForeground); + + a = m.addAction( bgcl, i18n("&Background Color..."), this, SLOT(changeProperty()) ); + a->setData(KateStyleTreeWidgetItem::Background); + + a = m.addAction( sbgcl, i18n("S&elected Background Color..."), this, SLOT(changeProperty()) ); + a->setData(KateStyleTreeWidgetItem::SelectedBackground); + + // Unset [some] colors. I could show one only if that button was clicked, but that + // would disable setting this with the keyboard (how many aren't doing just + // that every day? ;) + // ANY ideas for doing this in a nicer way will be warmly wellcomed. + KTextEditor::Attribute::Ptr style = i->style(); + if ( style->hasProperty( QTextFormat::BackgroundBrush) || style->hasProperty( KTextEditor::Attribute::SelectedBackground ) ) + { + m.addSeparator(); + if ( style->hasProperty( QTextFormat::BackgroundBrush) ) { + a = m.addAction( i18n("Unset Background Color"), this, SLOT(unsetColor()) ); + a->setData(100); + } + if ( style->hasProperty( KTextEditor::Attribute::SelectedBackground ) ) { + a = m.addAction( i18n("Unset Selected Background Color"), this, SLOT(unsetColor()) ); + a->setData(101); + } + } + + if ( ! i->isDefault() && ! i->defStyle() ) { + m.addSeparator(); + a = m.addAction( i18n("Use &Default Style"), this, SLOT(changeProperty()) ); + a->setCheckable(true); + a->setChecked( i->defStyle() ); + a->setData(KateStyleTreeWidgetItem::UseDefaultStyle); + } + m.exec( event->globalPos() ); +} + +void KateStyleTreeWidget::changeProperty() +{ + static_cast(currentItem())->changeProperty( static_cast(sender())->data().toInt() ); +} + +void KateStyleTreeWidget::unsetColor() +{ + static_cast(currentItem())->unsetColor( static_cast(sender())->data().toInt() ); +} + +void KateStyleTreeWidget::updateGroupHeadings() +{ + for(int i = 0; i < topLevelItemCount(); i++) { + QTreeWidgetItem* currentTopLevelItem = topLevelItem(i); + QTreeWidgetItem* firstChild = currentTopLevelItem->child(0); + + if(firstChild) { + QColor foregroundColor = firstChild->data(KateStyleTreeWidgetItem::Foreground, Qt::DisplayRole).value(); + QColor backgroundColor = firstChild->data(KateStyleTreeWidgetItem::Background, Qt::DisplayRole).value(); + + currentTopLevelItem->setForeground(KateStyleTreeWidgetItem::Context, foregroundColor); + + if(backgroundColor.isValid()) { + currentTopLevelItem->setBackground(KateStyleTreeWidgetItem::Context, backgroundColor); + } + } + } +} + +void KateStyleTreeWidget::emitChanged( ) +{ + updateGroupHeadings(); + emit changed(); +} + +void KateStyleTreeWidget::addItem( const QString & styleName, KTextEditor::Attribute::Ptr defaultstyle, KateExtendedAttribute::Ptr data ) +{ + new KateStyleTreeWidgetItem(this, styleName, defaultstyle, data); +} + +void KateStyleTreeWidget::addItem( QTreeWidgetItem * parent, const QString & styleName, KTextEditor::Attribute::Ptr defaultstyle, KateExtendedAttribute::Ptr data ) +{ + new KateStyleTreeWidgetItem(parent, styleName, defaultstyle, data); + updateGroupHeadings(); +} +//END + +//BEGIN KateStyleTreeWidgetItem +KateStyleTreeDelegate::KateStyleTreeDelegate(KateStyleTreeWidget* widget) + : m_widget(widget) +{ +} + +QBrush KateStyleTreeDelegate::getBrushForColorColumn(const QModelIndex& index, int column) const +{ + QModelIndex colorIndex = index.sibling(index.row(), column); + QVariant displayData = colorIndex.model()->data(colorIndex); + return qvariant_cast(displayData); +} + +void KateStyleTreeDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const +{ + static QSet columns; + if (!columns.count()) + columns << KateStyleTreeWidgetItem::Foreground << KateStyleTreeWidgetItem::SelectedForeground << KateStyleTreeWidgetItem::Background << KateStyleTreeWidgetItem::SelectedBackground; + + if(index.column() == KateStyleTreeWidgetItem::Context) { + QStyleOptionViewItem styleContextItem(option); + + QBrush brush = getBrushForColorColumn(index, KateStyleTreeWidgetItem::SelectedBackground); + if (brush != QBrush()) { + styleContextItem.palette.setBrush(QPalette::Highlight, brush); + } + + brush = getBrushForColorColumn(index, KateStyleTreeWidgetItem::SelectedForeground); + if (brush != QBrush()) { + styleContextItem.palette.setBrush(QPalette::HighlightedText, brush); + } + + return QStyledItemDelegate::paint(painter, styleContextItem, index); + } + + QStyledItemDelegate::paint(painter, option, index); + + if (!columns.contains(index.column())) { + return; + } + + QVariant displayData = index.model()->data(index); + if (displayData.type() != QVariant::Brush) + return; + + QBrush brush = qvariant_cast(displayData); + + QStyleOptionButton opt; + opt.rect = option.rect; + opt.palette = m_widget->palette(); + + bool set = brush != QBrush(); + + if (!set) { + opt.text = i18nc("No text or background color set", "None set"); + brush = Qt::white; + } + + m_widget->style()->drawControl(QStyle::CE_PushButton, &opt, painter, m_widget); + + if (set) + painter->fillRect(m_widget->style()->subElementRect(QStyle::SE_PushButtonContents, &opt,m_widget), brush); +} + +KateStyleTreeWidgetItem::KateStyleTreeWidgetItem( QTreeWidgetItem *parent, const QString & stylename, + KTextEditor::Attribute::Ptr defaultAttribute, KateExtendedAttribute::Ptr actualAttribute ) + : QTreeWidgetItem( parent ), + currentStyle( 0L ), + defaultStyle( defaultAttribute ), + actualStyle( actualAttribute ) +{ + initStyle(); + setText(0, stylename); +} + +KateStyleTreeWidgetItem::KateStyleTreeWidgetItem( QTreeWidget *parent, const QString & stylename, + KTextEditor::Attribute::Ptr defaultAttribute, KateExtendedAttribute::Ptr actualAttribute ) + : QTreeWidgetItem( parent ), + currentStyle( 0L ), + defaultStyle( defaultAttribute ), + actualStyle( actualAttribute ) +{ + initStyle(); + setText(0, stylename); +} + +void KateStyleTreeWidgetItem::initStyle() +{ + if (!actualStyle) + { + currentStyle = defaultStyle; + } + else + { + currentStyle = new KTextEditor::Attribute (*defaultStyle); + + if (actualStyle->hasAnyProperty()) + *currentStyle += *actualStyle; + } + + setFlags(Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsUserCheckable | Qt::ItemIsEnabled); +} + +static Qt::CheckState toCheckState(bool b) { + return b ? Qt::Checked : Qt::Unchecked; +} + +QVariant KateStyleTreeWidgetItem::data( int column, int role ) const +{ + if (column == Context) { + switch (role) { + case Qt::ForegroundRole: + if (style()->hasProperty(QTextFormat::ForegroundBrush)) + return style()->foreground().color(); + break; + + case Qt::BackgroundRole: + if (style()->hasProperty(QTextFormat::BackgroundBrush)) + return style()->background().color(); + break; + + case Qt::FontRole: + return style()->font(); + break; + } + } + + if (role == Qt::CheckStateRole) { + switch (column) { + case Bold: + return toCheckState(style()->fontBold()); + case Italic: + return toCheckState(style()->fontItalic()); + case Underline: + return toCheckState(style()->fontUnderline()); + case StrikeOut: + return toCheckState(style()->fontStrikeOut()); + case UseDefaultStyle: + /* can't compare all attributes, currentStyle has always more than defaultStyle (e.g. the item's name), + * so we just compare the important ones:*/ + return toCheckState( + currentStyle->foreground() == defaultStyle->foreground() + && currentStyle->background() == defaultStyle->background() + && currentStyle->selectedForeground() == defaultStyle->selectedForeground() + && currentStyle->selectedBackground() == defaultStyle->selectedBackground() + && currentStyle->fontBold() == defaultStyle->fontBold() + && currentStyle->fontItalic() == defaultStyle->fontItalic() + && currentStyle->fontUnderline() == defaultStyle->fontUnderline() + && currentStyle->fontStrikeOut() == defaultStyle->fontStrikeOut()); + } + } + + if (role == Qt::DisplayRole) { + switch (column) { + case Foreground: + return style()->foreground(); + case SelectedForeground: + return style()->selectedForeground(); + case Background: + return style()->background(); + case SelectedBackground: + return style()->selectedBackground(); + } + } + + return QTreeWidgetItem::data(column, role); +} + +void KateStyleTreeWidgetItem::updateStyle() +{ + // nothing there, not update it, will crash + if (!actualStyle) + return; + + if ( currentStyle->hasProperty(QTextFormat::FontWeight) ) + { + if ( currentStyle->fontWeight() != actualStyle->fontWeight()) + actualStyle->setFontWeight( currentStyle->fontWeight() ); + } + else actualStyle->clearProperty( QTextFormat::FontWeight ); + + if ( currentStyle->hasProperty(QTextFormat::FontItalic) ) + { + if ( currentStyle->fontItalic() != actualStyle->fontItalic()) + actualStyle->setFontItalic( currentStyle->fontItalic() ); + } + else actualStyle->clearProperty( QTextFormat::FontItalic ); + + if ( currentStyle->hasProperty(QTextFormat::FontStrikeOut) ) + { + if ( currentStyle->fontStrikeOut() != actualStyle->fontStrikeOut()) + actualStyle->setFontStrikeOut( currentStyle->fontStrikeOut() ); + } + else actualStyle->clearProperty( QTextFormat::FontStrikeOut ); + + if ( currentStyle->hasProperty(QTextFormat::FontUnderline) ) + { + if ( currentStyle->fontUnderline() != actualStyle->fontUnderline()) + actualStyle->setFontUnderline( currentStyle->fontUnderline() ); + } + else actualStyle->clearProperty( QTextFormat::FontUnderline ); + + if ( currentStyle->hasProperty(KTextEditor::Attribute::Outline) ) + { + if ( currentStyle->outline() != actualStyle->outline()) + actualStyle->setOutline( currentStyle->outline() ); + } + else actualStyle->clearProperty( KTextEditor::Attribute::Outline ); + + if ( currentStyle->hasProperty(QTextFormat::ForegroundBrush) ) + { + if ( currentStyle->foreground() != actualStyle->foreground()) + actualStyle->setForeground( currentStyle->foreground() ); + } + else actualStyle->clearProperty( QTextFormat::ForegroundBrush ); + + if ( currentStyle->hasProperty(KTextEditor::Attribute::SelectedForeground) ) + { + if ( currentStyle->selectedForeground() != actualStyle->selectedForeground()) + actualStyle->setSelectedForeground( currentStyle->selectedForeground() ); + } + else actualStyle->clearProperty( KTextEditor::Attribute::SelectedForeground ); + + if ( currentStyle->hasProperty(QTextFormat::BackgroundBrush) ) + { + if ( currentStyle->background() != actualStyle->background()) + actualStyle->setBackground( currentStyle->background() ); + } + else actualStyle->clearProperty( QTextFormat::BackgroundBrush ); + + if ( currentStyle->hasProperty(KTextEditor::Attribute::SelectedBackground) ) + { + if ( currentStyle->selectedBackground() != actualStyle->selectedBackground()) + actualStyle->setSelectedBackground( currentStyle->selectedBackground() ); + } + else actualStyle->clearProperty( KTextEditor::Attribute::SelectedBackground ); +} + +/* only true for a hl mode item using its default style */ +bool KateStyleTreeWidgetItem::defStyle() const { return actualStyle && actualStyle->properties() != defaultStyle->properties(); } + +/* true for default styles */ +bool KateStyleTreeWidgetItem::isDefault() const { return actualStyle ? false : true; } + +void KateStyleTreeWidgetItem::changeProperty( int p ) +{ + if ( p == Bold ) + currentStyle->setFontBold( ! currentStyle->fontBold() ); + else if ( p == Italic ) + currentStyle->setFontItalic( ! currentStyle->fontItalic() ); + else if ( p == Underline ) + currentStyle->setFontUnderline( ! currentStyle->fontUnderline() ); + else if ( p == StrikeOut ) + currentStyle->setFontStrikeOut( ! currentStyle->fontStrikeOut() ); + else if ( p == UseDefaultStyle ) + toggleDefStyle(); + else + setColor( p ); + + updateStyle (); + + treeWidget()->emitChanged(); +} + +void KateStyleTreeWidgetItem::toggleDefStyle() +{ + if ( *currentStyle == *defaultStyle ) { + KMessageBox::information( treeWidget(), + i18n("\"Use Default Style\" will be automatically unset when you change any style properties."), + i18n("Kate Styles"), + "Kate hl config use defaults" ); + } + else { + currentStyle = KTextEditor::Attribute::Ptr(new KTextEditor::Attribute( *defaultStyle )); + updateStyle(); + + QModelIndex currentIndex = treeWidget()->currentIndex(); + while(currentIndex.isValid()) { + treeWidget()->update(currentIndex); + currentIndex = currentIndex.sibling(currentIndex.row(), currentIndex.column() - 1); + } + } +} + +void KateStyleTreeWidgetItem::setColor( int column ) +{ + QColor c; // use this + QColor d; // default color + if ( column == Foreground) + { + c = currentStyle->foreground().color(); + d = defaultStyle->foreground().color(); + } + else if ( column == SelectedForeground ) + { + c = currentStyle->selectedForeground().color(); + d = defaultStyle->selectedForeground().color(); + } + else if ( column == Background ) + { + c = currentStyle->background().color(); + d = defaultStyle->background().color(); + } + else if ( column == SelectedBackground ) + { + c = currentStyle->selectedBackground().color(); + d = defaultStyle->selectedBackground().color(); + } + + if ( KColorDialog::getColor( c, d, treeWidget() ) != QDialog::Accepted) return; + + bool def = ! c.isValid(); + + // if set default, and the attrib is set in the default style use it + // else if set default, unset it + // else set the selected color + switch (column) + { + case Foreground: + if ( def ) + { + if ( defaultStyle->hasProperty(QTextFormat::ForegroundBrush) ) + currentStyle->setForeground( defaultStyle->foreground()); + else + currentStyle->clearProperty(QTextFormat::ForegroundBrush); + } + else + currentStyle->setForeground( c ); + break; + case SelectedForeground: + if ( def ) + { + if ( defaultStyle->hasProperty(KTextEditor::Attribute::SelectedForeground) ) + currentStyle->setSelectedForeground( defaultStyle->selectedForeground()); + else + currentStyle->clearProperty(KTextEditor::Attribute::SelectedForeground); + } + else + currentStyle->setSelectedForeground( c ); + break; + case Background: + if ( def ) + { + if ( defaultStyle->hasProperty(QTextFormat::BackgroundBrush) ) + currentStyle->setBackground( defaultStyle->background()); + else + currentStyle->clearProperty(QTextFormat::BackgroundBrush); + } + else + currentStyle->setBackground( c ); + break; + case SelectedBackground: + if ( def ) + { + if ( defaultStyle->hasProperty(KTextEditor::Attribute::SelectedBackground) ) + currentStyle->setSelectedBackground( defaultStyle->selectedBackground()); + else + currentStyle->clearProperty(KTextEditor::Attribute::SelectedBackground); + } + else + currentStyle->setSelectedBackground( c ); + break; + } + + //FIXME + //repaint(); +} + +void KateStyleTreeWidgetItem::unsetColor( int c ) +{ + if ( c == 100 && currentStyle->hasProperty(QTextFormat::BackgroundBrush) ) + currentStyle->clearProperty(QTextFormat::BackgroundBrush); + else if ( c == 101 && currentStyle->hasProperty(KTextEditor::Attribute::SelectedBackground) ) + currentStyle->clearProperty(KTextEditor::Attribute::SelectedBackground); + updateStyle(); + + treeWidget()->emitChanged(); +} + +KateStyleTreeWidget* KateStyleTreeWidgetItem::treeWidget() const +{ + return static_cast(QTreeWidgetItem::treeWidget()); +} +//END + +#include "moc_katestyletreewidget.cpp" diff --git a/kate/part/schema/katestyletreewidget.h b/kate/part/schema/katestyletreewidget.h new file mode 100644 index 00000000..ceea0ccf --- /dev/null +++ b/kate/part/schema/katestyletreewidget.h @@ -0,0 +1,74 @@ +/* This file is part of the KDE libraries + Copyright (C) 2001-2003 Christoph Cullmann + Copyright (C) 2002, 2003 Anders Lund + Copyright (C) 2005-2006 Hamish Rodda + Copyright (C) 2007 Mirko Stocker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KATESTYLETREEWIDGET_H +#define KATESTYLETREEWIDGET_H + +#include + +#include "kateextendedattribute.h" + +class KateExtendedAttribute; + +/** + * QTreeWidget that automatically adds columns for KateStyleListItems and provides a + * popup menu and a slot to edit a style using the keyboard. + * Added by anders, jan 23 2002. + */ +class KateStyleTreeWidget : public QTreeWidget +{ + Q_OBJECT + + friend class KateStyleListItem; + + public: + explicit KateStyleTreeWidget(QWidget *parent = 0, bool showUseDefaults = false); + + void emitChanged(); + + void setBgCol( const QColor &c ) { bgcol = c; } + void setSelCol( const QColor &c ) { selcol = c; } + void setNormalCol( const QColor &c ) { normalcol = c; } + + void addItem( QTreeWidgetItem *parent, const QString& styleName, KTextEditor::Attribute::Ptr defaultstyle, KateExtendedAttribute::Ptr data = KateExtendedAttribute::Ptr() ); + void addItem( const QString& styleName, KTextEditor::Attribute::Ptr defaultstyle, KateExtendedAttribute::Ptr data = KateExtendedAttribute::Ptr() ); + + void resizeColumns(); + + Q_SIGNALS: + void changed(); + + protected: + virtual void contextMenuEvent(QContextMenuEvent* event); + virtual void showEvent(QShowEvent* event); + virtual bool edit(const QModelIndex& index, EditTrigger trigger, QEvent* event); + + private Q_SLOTS: + void changeProperty( ); + void unsetColor( ); + void updateGroupHeadings(); + + private: + QColor bgcol, selcol, normalcol; + QFont docfont; +}; + +#endif diff --git a/kate/part/search/katematch.cpp b/kate/part/search/katematch.cpp new file mode 100644 index 00000000..744b2066 --- /dev/null +++ b/kate/part/search/katematch.cpp @@ -0,0 +1,96 @@ +/* This file is part of the KDE libraries + Copyright (C) 2010 Bernhard Beschow + Copyright (C) 2007 Sebastian Pipping + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "katematch.h" + +#include "kateregexpsearch.h" +#include "katedocument.h" +#include + +KateMatch::KateMatch(KateDocument *document, KTextEditor::Search::SearchOptions options) + : m_document(document) + , m_options(options) +{ + m_resultRanges.append(KTextEditor::Range::invalid()); +} + + +KTextEditor::Range KateMatch::searchText(const KTextEditor::Range &range, const QString &pattern) +{ + m_resultRanges = m_document->searchText(range, pattern, m_options); + + return m_resultRanges[0]; +} + + +KTextEditor::Range KateMatch::replace(const QString &replacement, bool blockMode, int replacementCounter) +{ + // Placeholders depending on search mode + const bool usePlaceholders = m_options.testFlag(KTextEditor::Search::Regex) || + m_options.testFlag(KTextEditor::Search::EscapeSequences); + + const QString finalReplacement = usePlaceholders ? buildReplacement(replacement, blockMode, replacementCounter) + : replacement; + + // Track replacement operation + KTextEditor::MovingRange *const afterReplace = m_document->newMovingRange(range(), KTextEditor::MovingRange::ExpandLeft | KTextEditor::MovingRange::ExpandRight); + + blockMode = blockMode && !range().onSingleLine(); + m_document->replaceText(range(), finalReplacement, blockMode); + + const KTextEditor::Range result = *afterReplace; + delete afterReplace; + + return result; +} + + +KTextEditor::Range KateMatch::range() const +{ + if (m_resultRanges.size() > 0) + return m_resultRanges[0]; + + return KTextEditor::Range::invalid(); +} + + +bool KateMatch::isEmpty() const +{ + return range().isEmpty(); +} + + +bool KateMatch::isValid() const +{ + return range().isValid(); +} + + +QString KateMatch::buildReplacement(const QString &replacement, bool blockMode, int replacementCounter) const { + QStringList capturedTexts; + foreach (const KTextEditor::Range &captureRange, m_resultRanges) { + // Copy capture content + capturedTexts << m_document->text(captureRange, blockMode); + } + + return KateRegExpSearch::buildReplacement(replacement, capturedTexts, replacementCounter); +} + + +// kate: space-indent on; indent-width 4; replace-tabs on; diff --git a/kate/part/search/katematch.h b/kate/part/search/katematch.h new file mode 100644 index 00000000..28bad08f --- /dev/null +++ b/kate/part/search/katematch.h @@ -0,0 +1,54 @@ +/* This file is part of the KDE libraries and the Kate part. + * + * Copyright (C) 2010 Bernhard Beschow + * Copyright (C) 2007 Sebastian Pipping + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef KATE_MATCH_H +#define KATE_MATCH_H + +#include + +class KateDocument; + + +class KateMatch +{ +public: + KateMatch(KateDocument *document, KTextEditor::Search::SearchOptions options); + KTextEditor::Range searchText(const KTextEditor::Range &range, const QString &pattern); + KTextEditor::Range replace(const QString &replacement, bool blockMode, int replacementCounter = 1); + bool isValid() const; + bool isEmpty() const; + KTextEditor::Range range() const; + +private: + /** + * Resolve references and escape sequences. + */ + QString buildReplacement(const QString &replacement, bool blockMode, int replacementCounter) const; + +private: + KateDocument *m_document; + KTextEditor::Search::SearchOptions m_options; + QVector m_resultRanges; +}; + +#endif // KATE_MATCH_H + +// kate: space-indent on; indent-width 4; replace-tabs on; diff --git a/kate/part/search/kateplaintextsearch.cpp b/kate/part/search/kateplaintextsearch.cpp new file mode 100644 index 00000000..e1d24eda --- /dev/null +++ b/kate/part/search/kateplaintextsearch.cpp @@ -0,0 +1,145 @@ +/* This file is part of the KDE libraries and the Kate part. + * + * Copyright (C) 2009-2010 Bernhard Beschow + * Copyright (C) 2007 Sebastian Pipping + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +//BEGIN includes +#include "kateplaintextsearch.h" + +#include "kateregexpsearch.h" + +#include + +#include +//END includes + + +//BEGIN d'tor, c'tor +// +// KateSearch Constructor +// +KatePlainTextSearch::KatePlainTextSearch ( KTextEditor::Document *document, Qt::CaseSensitivity caseSensitivity, bool wholeWords ) +: m_document (document) +, m_caseSensitivity (caseSensitivity) +, m_wholeWords (wholeWords) +{ +} + +// +// KateSearch Destructor +// +KatePlainTextSearch::~KatePlainTextSearch() +{ +} +//END + +KTextEditor::Range KatePlainTextSearch::search (const QString & text, const KTextEditor::Range & inputRange, bool backwards) +{ + // abuse regex for whole word plaintext search + if (m_wholeWords) + { + // escape dot and friends + const QString workPattern = "\\b" + QRegExp::escape(text) + "\\b"; + + return KateRegExpSearch(m_document, m_caseSensitivity).search(workPattern, inputRange, backwards)[0]; + } + + if (text.isEmpty() || !inputRange.isValid() || (inputRange.start() == inputRange.end())) + { + return KTextEditor::Range::invalid(); + } + + // split multi-line needle into single lines + const QStringList needleLines = text.split("\n"); + + if (needleLines.count() > 1) + { + // multi-line plaintext search (both forwards or backwards) + const int forMin = inputRange.start().line(); // first line in range + const int forMax = inputRange.end().line() + 1 - needleLines.count(); // last line in range + const int forInit = backwards ? forMax : forMin; + const int forInc = backwards ? -1 : +1; + + for (int j = forInit; (forMin <= j) && (j <= forMax); j += forInc) + { + // try to match all lines + const int startCol = m_document->lineLength(j) - needleLines[0].length(); + for (int k = 0; k < needleLines.count(); k++) + { + // which lines to compare + const QString & needleLine = needleLines[k]; + const QString & hayLine = m_document->line(j + k); + + // position specific comparison (first, middle, last) + if (k == 0) { + // first line + if (forMin == j && startCol < inputRange.start().column()) + break; + if (!hayLine.endsWith(needleLine, m_caseSensitivity)) + break; + } else if (k == needleLines.count() - 1) { + // last line + const int maxRight = (j + k == inputRange.end().line()) ? inputRange.end().column() : hayLine.length(); + + if (hayLine.startsWith(needleLine, m_caseSensitivity) && needleLine.length() <= maxRight) + return KTextEditor::Range(j, startCol, j + k, needleLine.length()); + } else { + // mid lines + if (hayLine.compare(needleLine, m_caseSensitivity) != 0) { + break; + } + } + } + } + + // not found + return KTextEditor::Range::invalid(); + } + else + { + // single-line plaintext search (both forward of backward mode) + const int startCol = inputRange.start().column(); + const int endCol = inputRange.end().column(); // first not included + const int startLine = inputRange.start().line(); + const int endLine = inputRange.end().line(); + const int forInc = backwards ? -1 : +1; + + for (int line = backwards ? endLine : startLine; (startLine <= line) && (line <= endLine); line += forInc) + { + if ((line < 0) || (m_document->lines() <= line)) + { + kWarning() << "line " << line << " is not within interval [0.." << m_document->lines() << ") ... returning invalid range"; + return KTextEditor::Range::invalid(); + } + + const QString textLine = m_document->line(line); + + const int offset = (line == startLine) ? startCol : 0; + const int line_end = (line == endLine) ? endCol : textLine.length(); + const int foundAt = backwards ? textLine.lastIndexOf(text, line_end-text.length(), m_caseSensitivity) : + textLine.indexOf(text, offset, m_caseSensitivity); + + if ((offset <= foundAt) && (foundAt + text.length() <= line_end)) + return KTextEditor::Range(line, foundAt, line, foundAt + text.length()); + } + } + return KTextEditor::Range::invalid(); +} + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/search/kateplaintextsearch.h b/kate/part/search/kateplaintextsearch.h new file mode 100644 index 00000000..49f9ab5a --- /dev/null +++ b/kate/part/search/kateplaintextsearch.h @@ -0,0 +1,73 @@ +/* This file is part of the KDE libraries and the Kate part. + * + * Copyright (C) 2009-2010 Bernhard Beschow + * Copyright (C) 2007 Sebastian Pipping + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _KATE_PLAINTEXTSEARCH_H_ +#define _KATE_PLAINTEXTSEARCH_H_ + +#include + +#include + +#include "katepartinterfaces_export.h" + +namespace KTextEditor { + class Document; +} + +/** + * Object to help to search for plain text. + * This should be NO QObject, it is created too often! + * I measured that, if you create it 20k times to replace for example " " in a document, that takes seconds on a modern machine! + */ +class KATEPARTINTERFACES_EXPORT KatePlainTextSearch +{ + public: + explicit KatePlainTextSearch (KTextEditor::Document *document, Qt::CaseSensitivity caseSensitivity, bool wholeWords); + ~KatePlainTextSearch (); + + public: + /** + * Search for the given \p text inside the range \p inputRange taking + * into account whether to search \p casesensitive and \p backwards. + * + * \param text text to search for + * \param inputRange Range to search in + * \param casesensitive if \e true, the search is performed case + * sensitive, otherwise case insensitive + * \param backwards if \e true, the search will be backwards + * \return The valid range of the matched text if \p text was found. If + * the \p text was not found, the returned range is not valid + * (see Range::isValid()). + * \see KTextEditor::Range + */ + KTextEditor::Range search (const QString & text, + const KTextEditor::Range & inputRange, bool backwards = false); + + private: + KTextEditor::Document *m_document; + Qt::CaseSensitivity m_caseSensitivity; + bool m_wholeWords; +}; + +#endif + +// kate: space-indent on; indent-width 2; replace-tabs on; + diff --git a/kate/part/search/kateregexp.cpp b/kate/part/search/kateregexp.cpp new file mode 100644 index 00000000..46c90bfc --- /dev/null +++ b/kate/part/search/kateregexp.cpp @@ -0,0 +1,327 @@ +/* This file is part of the KDE libraries and the Kate part. + * + * Copyright (C) 2009 Bernhard Beschow + * Copyright (C) 2007 Sebastian Pipping + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "kateregexp.h" + +KateRegExp::KateRegExp(const QString &pattern, Qt::CaseSensitivity cs, + QRegExp::PatternSyntax syntax) + : m_regExp(pattern, cs, syntax) +{ +} + +// these things can besides '.' and '\s' make apptern multi-line: +// \n, \x000A, \x????-\x????, \0012, \0???-\0??? +// a multi-line pattern must not pass as single-line, the other +// way around will just result in slower searches and is therefore +// not as critical +int KateRegExp::repairPattern(bool & stillMultiLine) +{ + const QString & text = pattern(); // read-only input for parsing + + // get input + const int inputLen = text.length(); + int input = 0; // walker index + + // prepare output + QString output; + output.reserve(2 * inputLen + 1); // twice should be enough for the average case + + // parser state + stillMultiLine = false; + int replaceCount = 0; + bool insideClass = false; + + while (input < inputLen) + { + if (insideClass) + { + // wait for closing, unescaped ']' + switch (text[input].unicode()) + { + case L'\\': + switch (text[input + 1].unicode()) + { + case L'x': + if (input + 5 < inputLen) + { + // copy "\x????" unmodified + output.append(text.mid(input, 6)); + input += 6; + } else { + // copy "\x" unmodified + output.append(text.mid(input, 2)); + input += 2; + } + stillMultiLine = true; + break; + + case L'0': + if (input + 4 < inputLen) + { + // copy "\0???" unmodified + output.append(text.mid(input, 5)); + input += 5; + } else { + // copy "\0" unmodified + output.append(text.mid(input, 2)); + input += 2; + } + stillMultiLine = true; + break; + + case L's': + // replace "\s" with "[ \t]" + output.append(" \\t"); + input += 2; + replaceCount++; + break; + + case L'n': + stillMultiLine = true; + // FALLTROUGH + + default: + // copy "\?" unmodified + output.append(text.mid(input, 2)); + input += 2; + } + break; + + case L']': + // copy "]" unmodified + insideClass = false; + output.append(text[input]); + input++; + break; + + default: + // copy "?" unmodified + output.append(text[input]); + input++; + + } + } + else + { + // search for real dots and \S + switch (text[input].unicode()) + { + case L'\\': + switch (text[input + 1].unicode()) + { + case L'x': + if (input + 5 < inputLen) + { + // copy "\x????" unmodified + output.append(text.mid(input, 6)); + input += 6; + } else { + // copy "\x" unmodified + output.append(text.mid(input, 2)); + input += 2; + } + stillMultiLine = true; + break; + + case L'0': + if (input + 4 < inputLen) + { + // copy "\0???" unmodified + output.append(text.mid(input, 5)); + input += 5; + } else { + // copy "\0" unmodified + output.append(text.mid(input, 2)); + input += 2; + } + stillMultiLine = true; + break; + + case L's': + // replace "\s" with "[ \t]" + output.append("[ \\t]"); + input += 2; + replaceCount++; + break; + + case L'n': + stillMultiLine = true; + // FALLTROUGH + + default: + // copy "\?" unmodified + output.append(text.mid(input, 2)); + input += 2; + } + break; + + case L'.': + // replace " with "[^\n]" + output.append("[^\\n]"); + input++; + replaceCount++; + break; + + case L'[': + // copy "]" unmodified + insideClass = true; + output.append(text[input]); + input++; + break; + + default: + // copy "?" unmodified + output.append(text[input]); + input++; + + } + } + } + + // Overwrite with repaired pattern + m_regExp.setPattern(output); + return replaceCount; +} + + + +bool KateRegExp::isMultiLine() const +{ + const QString &text = pattern(); + + // parser state + bool insideClass = false; + + for (int input = 0; input < text.length(); /*empty*/ ) + { + if (insideClass) + { + // wait for closing, unescaped ']' + switch (text[input].unicode()) + { + case L'\\': + switch (text[input + 1].unicode()) + { + case L'x': + return true; + + case L'0': + return true; + + case L's': + // replace "\s" with "[ \t]" + input += 2; + break; + + case L'n': + return true; + // FALLTROUGH + + default: + // copy "\?" unmodified + input += 2; + } + break; + + case L']': + // copy "]" unmodified + insideClass = false; + input++; + break; + + default: + // copy "?" unmodified + input++; + + } + } + else + { + // search for real dots and \S + switch (text[input].unicode()) + { + case L'\\': + switch (text[input + 1].unicode()) + { + case L'x': + return true; + + case L'0': + return true; + + case L's': + // replace "\s" with "[ \t]" + input += 2; + break; + + case L'n': + return true; + + default: + // copy "\?" unmodified + input += 2; + } + break; + + case L'.': + // replace " with "[^\n]" + input++; + break; + + case L'[': + // copy "]" unmodified + insideClass = true; + input++; + break; + + default: + // copy "?" unmodified + input++; + + } + } + } + + return false; +} + + + +int KateRegExp::indexIn(const QString &str, int start, int end) const +{ + return m_regExp.indexIn(str.left(end), start, QRegExp::CaretAtZero); +} + + + +int KateRegExp::lastIndexIn(const QString &str, int start, int end) const +{ + const int index = m_regExp.lastIndexIn(str.mid(start, end-start), -1, QRegExp::CaretAtZero); + + if (index == -1) + return -1; + + const int index2 = m_regExp.indexIn(str.left(end), start+index, QRegExp::CaretAtZero); + + return index2; +} + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/search/kateregexp.h b/kate/part/search/kateregexp.h new file mode 100644 index 00000000..bf188ee1 --- /dev/null +++ b/kate/part/search/kateregexp.h @@ -0,0 +1,81 @@ +/* This file is part of the KDE libraries and the Kate part. + * + * Copyright (C) 2009 Bernhard Beschow + * Copyright (C) 2007 Sebastian Pipping + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _KATE_REGEXP_H_ +#define _KATE_REGEXP_H_ + +#include + +class KateRegExp +{ + public: + explicit KateRegExp(const QString &pattern, Qt::CaseSensitivity cs = Qt::CaseSensitive, + QRegExp::PatternSyntax syntax = QRegExp::RegExp2); + + bool isEmpty() const { return m_regExp.isEmpty(); } + bool isValid() const { return m_regExp.isValid(); } + QString pattern() const { return m_regExp.pattern(); } + int captureCount() const { return m_regExp.captureCount(); } + int pos(int nth = 0) const { return m_regExp.pos(nth); } + QString cap(int nth = 0) const { return m_regExp.cap(nth); } + int matchedLength() const { return m_regExp.matchedLength(); } + + int indexIn(const QString &str, int offset, int end) const; + + /** + * This function is a replacement for QRegExp.lastIndexIn that + * returns the last match that would have been found when + * searching forwards, which QRegExp.lastIndexIn does not. + * We need this behavior to allow the user to jump back to + * the last match. + * + * \param str Text to search in + * \param offset Offset (-1 starts from end, -2 from one before the end) + * \return Index of match or -1 if no match is found + */ + int lastIndexIn(const QString &str, int offset, int end) const; + + /** + * Repairs a regular Expression pattern. + * This is a workaround to make "." and "\s" not match + * newlines, which currently is the unconfigurable + * default in QRegExp. + * + * \param stillMultiLine Multi-line after reparation flag + * \return Number of replacements done + */ + int repairPattern(bool & stillMultiLine); + + /** + * States, whether the pattern matches multiple lines, + * even if it was repaired using @p repairPattern(). + * + * \return Whether the pattern matches multiple lines + */ + bool isMultiLine() const; + + private: + QRegExp m_regExp; +}; + +#endif // KATEREGEXP_H + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/search/kateregexpsearch.cpp b/kate/part/search/kateregexpsearch.cpp new file mode 100644 index 00000000..f43fad4b --- /dev/null +++ b/kate/part/search/kateregexpsearch.cpp @@ -0,0 +1,820 @@ +/* This file is part of the KDE libraries and the Kate part. + * + * Copyright (C) 2010 Bernhard Beschow + * Copyright (C) 2007 Sebastian Pipping + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +//BEGIN includes +#include "kateregexpsearch.h" +#include "kateregexp.h" + +#include +//END includes + + + +// Turn debug messages on/off here +// #define FAST_DEBUG_ENABLE + +#ifdef FAST_DEBUG_ENABLE +# define FAST_DEBUG(x) kDebug( 13020 ) << x +#else +# define FAST_DEBUG(x) +#endif + + +class KateRegExpSearch::ReplacementStream +{ +public: + struct counter { + counter(int value, int minWidth) + : value(value) + , minWidth(minWidth) + {} + + const int value; + const int minWidth; + }; + + struct cap { + cap(int n) + : n(n) + {} + + const int n; + }; + + enum CaseConversion { + upperCase, ///< \U ... uppercase from now on + upperCaseFirst, ///< \u ... uppercase the first letter + lowerCase, ///< \L ... lowercase from now on + lowerCaseFirst, ///< \l ... lowercase the first letter + keepCase ///< \E ... back to original case + }; + +public: + ReplacementStream(const QStringList &capturedTexts); + + QString str() const { return m_str; } + + ReplacementStream &operator<<(const QString &); + ReplacementStream &operator<<(const counter &); + ReplacementStream &operator<<(const cap &); + ReplacementStream &operator<<(CaseConversion); + +private: + const QStringList m_capturedTexts; + CaseConversion m_caseConversion; + QString m_str; +}; + + +KateRegExpSearch::ReplacementStream::ReplacementStream(const QStringList &capturedTexts) + : m_capturedTexts(capturedTexts) + , m_caseConversion(keepCase) +{ +} + +KateRegExpSearch::ReplacementStream &KateRegExpSearch::ReplacementStream::operator<<(const QString &str) +{ + switch (m_caseConversion) { + case upperCase: + // Copy as uppercase + m_str.append(str.toUpper()); + break; + + case upperCaseFirst: + if (str.length() > 0) { + m_str.append(str.at(0).toUpper()); + m_str.append(str.mid(1)); + m_caseConversion = keepCase; + } + break; + + case lowerCase: + // Copy as lowercase + m_str.append(str.toLower()); + break; + + case lowerCaseFirst: + if (str.length() > 0) { + m_str.append(str.at(0).toLower()); + m_str.append(str.mid(1)); + m_caseConversion = keepCase; + } + break; + + case keepCase: // FALLTHROUGH + default: + // Copy unmodified + m_str.append(str); + break; + + } + + return *this; +} + + +KateRegExpSearch::ReplacementStream &KateRegExpSearch::ReplacementStream::operator<<(const counter &c) +{ + // Zero padded counter value + m_str.append(QString("%1").arg(c.value, c.minWidth, 10, QLatin1Char('0'))); + + return *this; +} + + +KateRegExpSearch::ReplacementStream &KateRegExpSearch::ReplacementStream::operator<<(const cap &cap) +{ + if (0 <= cap.n && cap.n < m_capturedTexts.size()) { + (*this) << m_capturedTexts[cap.n]; + } else { + // Insert just the number to be consistent with QRegExp ("\c" becomes "c") + m_str.append(QString::number(cap.n)); + } + + return *this; +} + + +KateRegExpSearch::ReplacementStream &KateRegExpSearch::ReplacementStream::operator<<(CaseConversion caseConversion) +{ + m_caseConversion = caseConversion; + + return *this; +} + + +//BEGIN d'tor, c'tor +// +// KateSearch Constructor +// +KateRegExpSearch::KateRegExpSearch ( KTextEditor::Document *document, Qt::CaseSensitivity caseSensitivity ) +: m_document (document) +, m_caseSensitivity (caseSensitivity) +{ +} + +// +// KateSearch Destructor +// +KateRegExpSearch::~KateRegExpSearch() +{ +} + + +// helper structs for captures re-construction +struct TwoViewCursor { + int index; + int openLine; + int openCol; + int closeLine; + int closeCol; + // note: open/close distinction does not seem needed + // anymore. i keep it to make a potential way back + // easier. overhead is minimal. +}; + +struct IndexPair { + int openIndex; + int closeIndex; +}; + + + +QVector KateRegExpSearch::search( + const QString &pattern, + const KTextEditor::Range & inputRange, + bool backwards) +{ + // regex search + KateRegExp regexp(pattern, m_caseSensitivity); + + if (regexp.isEmpty() || !regexp.isValid() || !inputRange.isValid() || (inputRange.start() == inputRange.end())) + { + QVector result; + result.append(KTextEditor::Range::invalid()); + return result; + } + + + // detect pattern type (single- or mutli-line) + bool isMultiLine; + + // detect '.' and '\s' and fix them + const bool dotMatchesNewline = false; // TODO + const int replacements = regexp.repairPattern(isMultiLine); + if (dotMatchesNewline && (replacements > 0)) + { + isMultiLine = true; + } + + const int firstLineIndex = inputRange.start().line(); + const int minColStart = inputRange.start().column(); +// const int maxColEnd = inputRange.end().column(); + if (isMultiLine) + { + // multi-line regex search (both forward and backward mode) + QString wholeDocument; + const int inputLineCount = inputRange.end().line() - inputRange.start().line() + 1; + FAST_DEBUG("multi line search (lines " << firstLineIndex << ".." << firstLineIndex + inputLineCount - 1 << ")"); + + // nothing to do... + if (firstLineIndex >= m_document->lines()) + { + QVector result; + result.append(KTextEditor::Range::invalid()); + return result; + } + + QVector lineLens (inputLineCount); + + // first line + if (firstLineIndex < 0 || m_document->lines() <= firstLineIndex) + { + QVector result; + result.append(KTextEditor::Range::invalid()); + return result; + } + + const QString firstLine = m_document->line(firstLineIndex); + + const int firstLineLen = firstLine.length() - minColStart; + wholeDocument.append(firstLine.right(firstLineLen)); + lineLens[0] = firstLineLen; + FAST_DEBUG(" line" << 0 << "has length" << lineLens[0]); + + // second line and after + const QString sep("\n"); + for (int i = 1; i < inputLineCount; i++) + { + const int lineNum = firstLineIndex + i; + if (lineNum < 0 || m_document->lines() <= lineNum) + { + QVector result; + result.append(KTextEditor::Range::invalid()); + return result; + } + const QString text = m_document->line(lineNum); + + lineLens[i] = text.length(); + wholeDocument.append(sep); + wholeDocument.append(text); + FAST_DEBUG(" line" << i << "has length" << lineLens[i]); + } + + const int pos = backwards + ? regexp.lastIndexIn(wholeDocument, 0, wholeDocument.length()) + : regexp.indexIn(wholeDocument, 0, wholeDocument.length()); + if (pos == -1) + { + // no match + FAST_DEBUG("not found"); + { + QVector result; + result.append(KTextEditor::Range::invalid()); + return result; + } + } + +#ifdef FAST_DEBUG_ENABLE + const int matchLen = regexp.matchedLength(); + FAST_DEBUG("found at relative pos " << pos << ", length " << matchLen); +#endif + + // save opening and closing indices and build a map. + // the correct values will be written into it later. + QMap indicesToCursors; + const int numCaptures = regexp.captureCount(); + QVector indexPairs(1 + numCaptures); + for (int z = 0; z <= numCaptures; z++) + { + const int openIndex = regexp.pos(z); + IndexPair & pair = indexPairs[z]; + if (openIndex == -1) + { + // empty capture gives invalid + pair.openIndex = -1; + pair.closeIndex = -1; + FAST_DEBUG("capture []"); + } + else + { + const int closeIndex = openIndex + regexp.cap(z).length(); + pair.openIndex = openIndex; + pair.closeIndex = closeIndex; + FAST_DEBUG("capture [" << pair.openIndex << ".." << pair.closeIndex << "]"); + + // each key no more than once + if (!indicesToCursors.contains(openIndex)) + { + TwoViewCursor * twoViewCursor = new TwoViewCursor; + twoViewCursor->index = openIndex; + indicesToCursors.insert(openIndex, twoViewCursor); + FAST_DEBUG(" border index added: " << openIndex); + } + if (!indicesToCursors.contains(closeIndex)) + { + TwoViewCursor * twoViewCursor = new TwoViewCursor; + twoViewCursor->index = closeIndex; + indicesToCursors.insert(closeIndex, twoViewCursor); + FAST_DEBUG(" border index added: " << closeIndex); + } + } + } + + // find out where they belong + int curRelLine = 0; + int curRelCol = 0; + int curRelIndex = 0; + QMap::const_iterator iter = indicesToCursors.constBegin(); + while (iter != indicesToCursors.constEnd()) + { + // forward to index, save line/col + const int index = (*iter)->index; + FAST_DEBUG("resolving position" << index); + TwoViewCursor & twoViewCursor = *(*iter); + while (curRelIndex <= index) + { + FAST_DEBUG("walk pos (" << curRelLine << "," << curRelCol << ") = " + << curRelIndex << "relative, steps more to go" << index - curRelIndex); + const int curRelLineLen = lineLens[curRelLine]; + const int curLineRemainder = curRelLineLen - curRelCol; + const int lineFeedIndex = curRelIndex + curLineRemainder; + if (index <= lineFeedIndex) { + if (index == lineFeedIndex) { + // on this line _on_ line feed + FAST_DEBUG(" on line feed"); + const int absLine = curRelLine + firstLineIndex; + twoViewCursor.openLine + = twoViewCursor.closeLine + = absLine; + twoViewCursor.openCol + = twoViewCursor.closeCol + = ((curRelLine == 0) ? minColStart : 0) + curRelLineLen; + + // advance to next line + const int advance = (index - curRelIndex) + 1; + curRelLine++; + curRelCol = 0; + curRelIndex += advance; + } else { // index < lineFeedIndex + // on this line _before_ line feed + FAST_DEBUG(" before line feed"); + const int diff = (index - curRelIndex); + const int absLine = curRelLine + firstLineIndex; + const int absCol = ((curRelLine == 0) ? minColStart : 0) + curRelCol + diff; + twoViewCursor.openLine + = twoViewCursor.closeLine + = absLine; + twoViewCursor.openCol + = twoViewCursor.closeCol + = absCol; + + // advance on same line + const int advance = diff + 1; + curRelCol += advance; + curRelIndex += advance; + } + FAST_DEBUG("open(" << twoViewCursor.openLine << "," << twoViewCursor.openCol + << ") close(" << twoViewCursor.closeLine << "," << twoViewCursor.closeCol << ")"); + } + else // if (index > lineFeedIndex) + { + // not on this line + // advance to next line + FAST_DEBUG(" not on this line"); + const int advance = curLineRemainder + 1; + curRelLine++; + curRelCol = 0; + curRelIndex += advance; + } + } + + ++iter; + } + + // build result array + QVector result(1 + numCaptures); + for (int y = 0; y <= numCaptures; y++) + { + IndexPair & pair = indexPairs[y]; + if ((pair.openIndex == -1) || (pair.closeIndex == -1)) + { + result[y] = KTextEditor::Range::invalid(); + } + else + { + const TwoViewCursor * const openCursors = indicesToCursors[pair.openIndex]; + const TwoViewCursor * const closeCursors = indicesToCursors[pair.closeIndex]; + const int startLine = openCursors->openLine; + const int startCol = openCursors->openCol; + const int endLine = closeCursors->closeLine; + const int endCol = closeCursors->closeCol; + FAST_DEBUG("range " << y << ": (" << startLine << ", " << startCol << ")..(" << endLine << ", " << endCol << ")"); + result[y] = KTextEditor::Range(startLine, startCol, endLine, endCol); + } + } + + // free structs allocated for indicesToCursors + iter = indicesToCursors.constBegin(); + while (iter != indicesToCursors.constEnd()) + { + TwoViewCursor * const twoViewCursor = *iter; + delete twoViewCursor; + ++iter; + } + return result; + } + else + { + // single-line regex search (both forward of backward mode) + const int minLeft = inputRange.start().column(); + const uint maxRight = inputRange.end().column(); // first not included + const int forMin = inputRange.start().line(); + const int forMax = inputRange.end().line(); + const int forInit = backwards ? forMax : forMin; + const int forInc = backwards ? -1 : +1; + FAST_DEBUG("single line " << (backwards ? forMax : forMin) << ".." + << (backwards ? forMin : forMax)); + for (int j = forInit; (forMin <= j) && (j <= forMax); j += forInc) + { + if (j < 0 || m_document->lines() <= j) + { + FAST_DEBUG("searchText | line " << j << ": no"); + QVector result; + result.append(KTextEditor::Range::invalid()); + return result; + } + const QString textLine = m_document->line(j); + + // Find (and don't match ^ in between...) + const int first = (j == forMin) ? minLeft : 0; + const int last = (j == forMax) ? maxRight : textLine.length(); + const int foundAt = (backwards ? regexp.lastIndexIn(textLine, first, last) + : regexp.indexIn(textLine, first, last)); + const bool found = (foundAt != -1); + + /* + TODO do we still need this? + + // A special case which can only occur when searching with a regular expression consisting + // only of a lookahead (e.g. ^(?=\{) for a function beginning without selecting '{'). + if (myMatchLen == 0 && line == startPosition.line() && foundAt == (uint) col) + { + if (col < lineLength(line)) + col++; + else { + line++; + col = 0; + } + continue; + } + */ + + if (found) + { + FAST_DEBUG("line " << j << ": yes"); + + // build result array + const int numCaptures = regexp.captureCount(); + QVector result(1 + numCaptures); + result[0] = KTextEditor::Range(j, foundAt, j, foundAt + regexp.matchedLength()); + FAST_DEBUG("result range " << 0 << ": (" << j << ", " << foundAt << ")..(" << j << ", " << + foundAt + regexp.matchedLength() << ")"); + for (int y = 1; y <= numCaptures; y++) + { + const int openIndex = regexp.pos(y); + if (openIndex == -1) + { + result[y] = KTextEditor::Range::invalid(); + FAST_DEBUG("capture []"); + } + else + { + const int closeIndex = openIndex + regexp.cap(y).length(); + FAST_DEBUG("result range " << y << ": (" << j << ", " << openIndex << ")..(" << j << ", " << closeIndex << ")"); + result[y] = KTextEditor::Range(j, openIndex, j, closeIndex); + } + } + return result; + } + else + { + FAST_DEBUG("searchText | line " << j << ": no"); + } + } + } + + QVector result; + result.append(KTextEditor::Range::invalid()); + return result; +} + + +/*static*/ QString KateRegExpSearch::escapePlaintext(const QString & text) +{ + return buildReplacement(text, QStringList(), 0, false); +} + + +/*static*/ QString KateRegExpSearch::buildReplacement(const QString & text, const QStringList &capturedTexts, int replacementCounter) +{ + return buildReplacement(text, capturedTexts, replacementCounter, true); +} + + +/*static*/ QString KateRegExpSearch::buildReplacement(const QString & text, const QStringList &capturedTexts, int replacementCounter, bool replacementGoodies) { + // get input + const int inputLen = text.length(); + int input = 0; // walker index + + // prepare output + ReplacementStream out(capturedTexts); + + while (input < inputLen) + { + switch (text[input].unicode()) + { + case L'\n': + out << text[input]; + input++; + break; + + case L'\\': + if (input + 1 >= inputLen) + { + // copy backslash + out << text[input]; + input++; + break; + } + + switch (text[input + 1].unicode()) + { + case L'0': // "\0000".."\0377" + if (input + 4 >= inputLen) + { + out << ReplacementStream::cap(0); + input += 2; + } + else + { + bool stripAndSkip = false; + const ushort text_2 = text[input + 2].unicode(); + if ((text_2 >= L'0') && (text_2 <= L'3')) + { + const ushort text_3 = text[input + 3].unicode(); + if ((text_3 >= L'0') && (text_3 <= L'7')) + { + const ushort text_4 = text[input + 4].unicode(); + if ((text_4 >= L'0') && (text_4 <= L'7')) + { + int digits[3]; + for (int i = 0; i < 3; i++) + { + digits[i] = 7 - (L'7' - text[input + 2 + i].unicode()); + } + const int ch = 64 * digits[0] + 8 * digits[1] + digits[2]; + out << QChar(ch); + input += 5; + } + else + { + stripAndSkip = true; + } + } + else + { + stripAndSkip = true; + } + } + else + { + stripAndSkip = true; + } + + if (stripAndSkip) + { + out << ReplacementStream::cap(0); + input += 2; + } + } + break; + + case L'1': + case L'2': + case L'3': + case L'4': + case L'5': + case L'6': + case L'7': + case L'8': + case L'9': + out << ReplacementStream::cap(9 - (L'9' - text[input + 1].unicode())); + input += 2; + break; + + case L'E': // FALLTHROUGH + case L'L': // FALLTHROUGH + case L'l': // FALLTHROUGH + case L'U': // FALLTHROUGH + case L'u': + if (!replacementGoodies) { + // strip backslash ("\?" -> "?") + out << text[input + 1]; + } else { + // handle case switcher + switch (text[input + 1].unicode()) { + case L'L': + out << ReplacementStream::lowerCase; + break; + + case L'l': + out << ReplacementStream::lowerCaseFirst; + break; + + case L'U': + out << ReplacementStream::upperCase; + break; + + case L'u': + out << ReplacementStream::upperCaseFirst; + break; + + case L'E': // FALLTHROUGH + default: + out << ReplacementStream::keepCase; + + } + } + input += 2; + break; + + case L'#': + if (!replacementGoodies) { + // strip backslash ("\?" -> "?") + out << text[input + 1]; + input += 2; + } else { + // handle replacement counter + // eat and count all following hash marks + // each hash stands for a leading zero: \### will produces 001, 002, ... + int minWidth = 1; + while ((input + minWidth + 1 < inputLen) && (text[input + minWidth + 1].unicode() == L'#')) { + minWidth++; + } + out << ReplacementStream::counter(replacementCounter, minWidth); + input += 1 + minWidth; + } + break; + + case L'a': + out << QChar(0x07); + input += 2; + break; + + case L'f': + out << QChar(0x0c); + input += 2; + break; + + case L'n': + out << QChar(0x0a); + input += 2; + break; + + case L'r': + out << QChar(0x0d); + input += 2; + break; + + case L't': + out << QChar(0x09); + input += 2; + break; + + case L'v': + out << QChar(0x0b); + input += 2; + break; + + case L'x': // "\x0000".."\xffff" + if (input + 5 >= inputLen) + { + // strip backslash ("\x" -> "x") + out << text[input + 1]; + input += 2; + } + else + { + bool stripAndSkip = false; + const ushort text_2 = text[input + 2].unicode(); + if (((text_2 >= L'0') && (text_2 <= L'9')) + || ((text_2 >= L'a') && (text_2 <= L'f')) + || ((text_2 >= L'A') && (text_2 <= L'F'))) + { + const ushort text_3 = text[input + 3].unicode(); + if (((text_3 >= L'0') && (text_3 <= L'9')) + || ((text_3 >= L'a') && (text_3 <= L'f')) + || ((text_3 >= L'A') && (text_3 <= L'F'))) + { + const ushort text_4 = text[input + 4].unicode(); + if (((text_4 >= L'0') && (text_4 <= L'9')) + || ((text_4 >= L'a') && (text_4 <= L'f')) + || ((text_4 >= L'A') && (text_4 <= L'F'))) + { + const ushort text_5 = text[input + 5].unicode(); + if (((text_5 >= L'0') && (text_5 <= L'9')) + || ((text_5 >= L'a') && (text_5 <= L'f')) + || ((text_5 >= L'A') && (text_5 <= L'F'))) + { + int digits[4]; + for (int i = 0; i < 4; i++) + { + const ushort cur = text[input + 2 + i].unicode(); + if ((cur >= L'0') && (cur <= L'9')) + { + digits[i] = 9 - (L'9' - cur); + } + else if ((cur >= L'a') && (cur <= L'f')) + { + digits[i] = 15 - (L'f' - cur); + } + else // if ((cur >= L'A') && (cur <= L'F'))) + { + digits[i] = 15 - (L'F' - cur); + } + } + + const int ch = 4096 * digits[0] + 256 * digits[1] + 16 * digits[2] + digits[3]; + out << QChar(ch); + input += 6; + } + else + { + stripAndSkip = true; + } + } + else + { + stripAndSkip = true; + } + } + else + { + stripAndSkip = true; + } + } + + if (stripAndSkip) + { + // strip backslash ("\x" -> "x") + out << text[input + 1]; + input += 2; + } + } + break; + + default: + // strip backslash ("\?" -> "?") + out << text[input + 1]; + input += 2; + + } + break; + + default: + out << text[input]; + input++; + + } + } + + return out.str(); +} + + +// Kill our helpers again +#ifdef FAST_DEBUG_ENABLE +# undef FAST_DEBUG_ENABLE +#endif +#undef FAST_DEBUG + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/search/kateregexpsearch.h b/kate/part/search/kateregexpsearch.h new file mode 100644 index 00000000..1df8f366 --- /dev/null +++ b/kate/part/search/kateregexpsearch.h @@ -0,0 +1,108 @@ +/* This file is part of the KDE libraries and the Kate part. + * + * Copyright (C) 2010 Bernhard Beschow + * Copyright (C) 2007 Sebastian Pipping + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _KATE_REGEXPSEARCH_H_ +#define _KATE_REGEXPSEARCH_H_ + +#include + +#include + +#include "katepartinterfaces_export.h" + +namespace KTextEditor { + class Document; +} + +/** + * Object to help to search for regexp. + * This should be NO QObject, it is created to often! + * I measured that, if you create it 20k times to replace for example " " in a document, that takes seconds on a modern machine! + */ +class KATEPARTINTERFACES_EXPORT KateRegExpSearch +{ + public: + explicit KateRegExpSearch (KTextEditor::Document *document, Qt::CaseSensitivity caseSensitivity); + ~KateRegExpSearch (); + + // + // KTextEditor::SearchInterface stuff + // + public: + /** + * Search for the regular expression \p regexp inside the range + * \p inputRange. If \p backwards is \e true, the search direction will + * be reversed. + * + * \param regexp text to search for + * \param inputRange Range to search in + * \param backwards if \e true, the search will be backwards + * \return Vector of ranges, one for each capture. The first range (index zero) + * spans the full match. If the pattern does not match the vector + * has length 1 and holds the invalid range (see Range::isValid()). + * \see KTextEditor::Range, QRegExp + */ + QVector search (const QString &pattern, + const KTextEditor::Range & inputRange, bool backwards = false); + + /** + * Returns a modified version of text where escape sequences are resolved, e.g. "\\n" to "\n". + * + * \param text text containing escape sequences + * \return text with resolved escape sequences + */ + static QString escapePlaintext(const QString &text); + + /** + * Returns a modified version of text where + * \li escape sequences are resolved, e.g. "\\n" to "\n", + * \li references are resolved, e.g. "\\1" to 1st entry in capturedTexts, and + * \li counter sequences are resolved, e.g. "\\#...#" to replacementCounter. + * + * \param text text containing escape sequences, references, and counter sequences + * \param capturedTexts list of substitutes for references + * \param replacementCounter value for replacement counter + * \return resolved text + */ + static QString buildReplacement(const QString &text, const QStringList &capturedTexts, int replacementCounter); + + private: + /** + * Implementation of escapePlainText() and public buildReplacement(). + * + * \param text text containing escape sequences and possibly references and counters + * \param capturedTexts list of substitutes for references + * \param replacementCounter value for replacement counter (only used when replacementGoodies == true) + * \param replacementGoodies true for buildReplacement(), false for escapePlainText() + * \return resolved text + */ + static QString buildReplacement(const QString &text, const QStringList &capturedTexts, int replacementCounter, bool replacementGoodies); + + private: + KTextEditor::Document *const m_document; + Qt::CaseSensitivity m_caseSensitivity; + class ReplacementStream; +}; + +#endif + +// kate: space-indent on; indent-width 2; replace-tabs on; + diff --git a/kate/part/search/katesearchbar.cpp b/kate/part/search/katesearchbar.cpp new file mode 100644 index 00000000..bb9a1f86 --- /dev/null +++ b/kate/part/search/katesearchbar.cpp @@ -0,0 +1,1627 @@ +/* This file is part of the KDE libraries + Copyright (C) 2009-2010 Bernhard Beschow + Copyright (C) 2007 Sebastian Pipping + Copyright (C) 2007 Matthew Woehlke + Copyright (C) 2007 Thomas Friedrichsmeier + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "katesearchbar.h" +#include "moc_katesearchbar.cpp" + +#include "kateregexp.h" +#include "katematch.h" +#include "kateview.h" +#include "katedocument.h" +#include "kateundomanager.h" +#include "kateconfig.h" +#include "katerenderer.h" + +#include +#include + +#include "ui_searchbarincremental.h" +#include "ui_searchbarpower.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + +// Turn debug messages on/off here +// #define FAST_DEBUG_ENABLE + +#ifdef FAST_DEBUG_ENABLE +# define FAST_DEBUG(x) kDebug() << x +#else +# define FAST_DEBUG(x) +#endif + +using namespace KTextEditor; + +namespace { + +class AddMenuManager { + +private: + QVector m_insertBefore; + QVector m_insertAfter; + QSet m_actionPointers; + uint m_indexWalker; + QMenu * m_menu; + +public: + AddMenuManager(QMenu * parent, int expectedItemCount) + : m_insertBefore(QVector(expectedItemCount)), + m_insertAfter(QVector(expectedItemCount)), + m_indexWalker(0), + m_menu(NULL) { + Q_ASSERT(parent != NULL); + m_menu = parent->addMenu(i18n("Add...")); + if (m_menu == NULL) { + return; + } + m_menu->setIcon(KIcon("list-add")); + } + + void enableMenu(bool enabled) { + if (m_menu == NULL) { + return; + } + m_menu->setEnabled(enabled); + } + + void addEntry(const QString & before, const QString after, + const QString description, const QString & realBefore = QString(), + const QString & realAfter = QString()) { + if (m_menu == NULL) { + return; + } + QAction * const action = m_menu->addAction(before + after + '\t' + description); + m_insertBefore[m_indexWalker] = QString(realBefore.isEmpty() ? before : realBefore); + m_insertAfter[m_indexWalker] = QString(realAfter.isEmpty() ? after : realAfter); + action->setData(QVariant(m_indexWalker++)); + m_actionPointers.insert(action); + } + + void addSeparator() { + if (m_menu == NULL) { + return; + } + m_menu->addSeparator(); + } + + void handle(QAction * action, QLineEdit * lineEdit) { + if (!m_actionPointers.contains(action)) { + return; + } + + const int cursorPos = lineEdit->cursorPosition(); + const int index = action->data().toUInt(); + const QString & before = m_insertBefore[index]; + const QString & after = m_insertAfter[index]; + lineEdit->insert(before + after); + lineEdit->setCursorPosition(cursorPos + before.count()); + lineEdit->setFocus(); + } +}; + +} // anon namespace + + + +KateSearchBar::KateSearchBar(bool initAsPower, KateView* view, KateViewConfig *config) + : KateViewBarWidget(true, view), + m_view(view), + m_config(config), + m_layout(new QVBoxLayout()), + m_widget(NULL), + m_incUi(NULL), + m_incInitCursor(view->cursorPosition()), + m_powerUi(NULL), + highlightMatchAttribute (new Attribute()), + highlightReplacementAttribute (new Attribute()), + m_incHighlightAll(false), + m_incFromCursor(true), + m_incMatchCase(false), + m_powerMatchCase(true), + m_powerFromCursor(false), + m_powerHighlightAll(false), + m_powerMode(0), + m_unitTestMode(false) +{ + + connect(view, SIGNAL(cursorPositionChanged(KTextEditor::View*,KTextEditor::Cursor)), + this, SLOT(updateIncInitCursor())); + + // init match attribute + Attribute::Ptr mouseInAttribute(new Attribute()); + mouseInAttribute->setFontBold(true); + highlightMatchAttribute->setDynamicAttribute (Attribute::ActivateMouseIn, mouseInAttribute); + + Attribute::Ptr caretInAttribute(new Attribute()); + caretInAttribute->setFontItalic(true); + highlightMatchAttribute->setDynamicAttribute (Attribute::ActivateCaretIn, caretInAttribute); + + updateHighlightColors(); + + // Modify parent + QWidget * const widget = centralWidget(); + widget->setLayout(m_layout); + m_layout->setMargin(0); + + // allow to have small size, for e.g. Kile + setMinimumWidth (100); + + // Copy global to local config backup + const long searchFlags = m_config->searchFlags(); + m_incHighlightAll = (searchFlags & KateViewConfig::IncHighlightAll) != 0; + m_incFromCursor = (searchFlags & KateViewConfig::IncFromCursor) != 0; + m_incMatchCase = (searchFlags & KateViewConfig::IncMatchCase) != 0; + m_powerMatchCase = (searchFlags & KateViewConfig::PowerMatchCase) != 0; + m_powerFromCursor = (searchFlags & KateViewConfig::PowerFromCursor) != 0; + m_powerHighlightAll = (searchFlags & KateViewConfig::PowerHighlightAll) != 0; + m_powerMode = ((searchFlags & KateViewConfig::PowerModeRegularExpression) != 0) + ? MODE_REGEX + : (((searchFlags & KateViewConfig::PowerModeEscapeSequences) != 0) + ? MODE_ESCAPE_SEQUENCES + : (((searchFlags & KateViewConfig::PowerModeWholeWords) != 0) + ? MODE_WHOLE_WORDS + : MODE_PLAIN_TEXT)); + + + // Load one of either dialogs + if (initAsPower) { + enterPowerMode(); + } else { + enterIncrementalMode(); + } + + updateSelectionOnly(); + connect(view, SIGNAL(selectionChanged(KTextEditor::View*)), + this, SLOT(updateSelectionOnly())); +} + + + +KateSearchBar::~KateSearchBar() { + clearHighlights(); + delete m_layout; + delete m_widget; + + delete m_incUi; + delete m_powerUi; +} + +void KateSearchBar::closed() +{ + // remove search from the view bar, because it vertically bloats up the + // stacked layout in KateViewBar. + if (viewBar()) { + viewBar()->removeBarWidget(this); + } + + clearHighlights(); +} + + +void KateSearchBar::setReplacementPattern(const QString &replacementPattern) { + Q_ASSERT(isPower()); + + if (this->replacementPattern() == replacementPattern) + return; + + m_powerUi->replacement->setEditText(replacementPattern); +} + + + +QString KateSearchBar::replacementPattern() const +{ + Q_ASSERT(isPower()); + + return m_powerUi->replacement->currentText(); +} + + + +void KateSearchBar::setSearchMode(KateSearchBar::SearchMode mode) +{ + Q_ASSERT(isPower()); + + m_powerUi->searchMode->setCurrentIndex(mode); +} + + + +void KateSearchBar::findNext() +{ + const bool found = find(); + + if (found) { + QComboBox *combo = m_powerUi != 0 ? m_powerUi->pattern : m_incUi->pattern; + + // Add to search history + addCurrentTextToHistory(combo); + } +} + + + +void KateSearchBar::findPrevious() { + const bool found = find(SearchBackward); + + if (found) { + QComboBox *combo = m_powerUi != 0 ? m_powerUi->pattern : m_incUi->pattern; + + // Add to search history + addCurrentTextToHistory(combo); + } +} + +void KateSearchBar::updateMessage(QPointer& message, bool visible, const QString& text, + KTextEditor::Message::MessageType type, KTextEditor::Message::MessagePosition position, + KTextEditor::Message::AutoHideMode autoHideMode, int durationMs, bool blink) +{ + // It the message is visible now and we want it to be visible and we don't want the message to blink, + // then just leave it. + if (message && visible && !blink) + return; + + delete message; + + if (!visible) + return; + + message = new KTextEditor::Message(text, type); + message->setPosition(position); + message->setAutoHide(durationMs); + message->setAutoHideMode(autoHideMode); + + m_view->doc()->postMessage(message); +} + +void KateSearchBar::showInfoMessage(const QString& text) +{ + typedef KTextEditor::Message KTEM; + updateMessage(m_infoMessage, true, text, KTEM::Positive, KTEM::BottomInView, KTEM::AfterUserInteraction, 3000, false); +} + +void KateSearchBar::highlightMatch(const Range & range) { + KTextEditor::MovingRange* const highlight = m_view->doc()->newMovingRange(range, Kate::TextRange::DoNotExpand); + highlight->setView(m_view); // show only in this view + highlight->setAttributeOnlyForViews(true); + // use z depth defined in moving ranges interface + highlight->setZDepth (-10000.0); + highlight->setAttribute(highlightMatchAttribute); + m_hlRanges.append(highlight); +} + +void KateSearchBar::highlightReplacement(const Range & range) { + KTextEditor::MovingRange* const highlight = m_view->doc()->newMovingRange(range, Kate::TextRange::DoNotExpand); + highlight->setView(m_view); // show only in this view + highlight->setAttributeOnlyForViews(true); + // use z depth defined in moving ranges interface + highlight->setZDepth (-10000.0); + highlight->setAttribute(highlightReplacementAttribute); + m_hlRanges.append(highlight); +} + +void KateSearchBar::indicateMatch(MatchResult matchResult) { + QLineEdit * const lineEdit = isPower() ? m_powerUi->pattern->lineEdit() + : m_incUi->pattern->lineEdit(); + QPalette background(lineEdit->palette()); + + switch (matchResult) { + case MatchFound: // FALLTHROUGH + case MatchWrappedForward: + case MatchWrappedBackward: + // Green background for line edit + KColorScheme::adjustBackground(background, KColorScheme::PositiveBackground); + break; + case MatchMismatch: + // Red background for line edit + KColorScheme::adjustBackground(background, KColorScheme::NegativeBackground); + break; + case MatchNothing: + // Reset background of line edit + background = QPalette(); + break; + case MatchNeutral: + KColorScheme::adjustBackground(background, KColorScheme::NeutralBackground); + break; + } + + typedef KTextEditor::Message KTEM; + const int messageDuration = 2000; // ms + + updateMessage(m_wrappedTopMessage, matchResult == MatchWrappedForward, i18n("Continuing search from top"), + KTEM::Information, KTEM::TopInView, KTEM::Immediate, messageDuration, true); + + updateMessage(m_wrappedBottomMessage, matchResult == MatchWrappedBackward, i18n("Continuing search from bottom"), + KTEM::Information, KTEM::BottomInView, KTEM::Immediate, messageDuration, true); + + updateMessage(m_notFoundMessage, matchResult == MatchMismatch, i18n("Not found"), + KTEM::Warning, KTEM::BottomInView, KTEM::Immediate, messageDuration, false); + + lineEdit->setPalette(background); +} + + + +/*static*/ void KateSearchBar::selectRange(KateView * view, const KTextEditor::Range & range) { + view->setCursorPositionInternal(range.end()); + view->setSelection(range); +} + + + +void KateSearchBar::selectRange2(const KTextEditor::Range & range) { + disconnect(m_view, SIGNAL(selectionChanged(KTextEditor::View*)), this, SLOT(updateSelectionOnly())); + selectRange(m_view, range); + connect(m_view, SIGNAL(selectionChanged(KTextEditor::View*)), this, SLOT(updateSelectionOnly())); +} + + + +void KateSearchBar::onIncPatternChanged(const QString & pattern) +{ + if (!m_incUi) + return; + + // clear prior highlightings (deletes info message if present) + clearHighlights(); + + m_incUi->next->setDisabled(pattern.isEmpty()); + m_incUi->prev->setDisabled(pattern.isEmpty()); + + KateMatch match(m_view->doc(), searchOptions()); + + if (!pattern.isEmpty()) { + // Find, first try + const Range inputRange = KTextEditor::Range(m_incInitCursor, m_view->document()->documentEnd()); + match.searchText(inputRange, pattern); + } + + const bool wrap = !match.isValid() && !pattern.isEmpty(); + + if (wrap) { + // Find, second try + const KTextEditor::Range inputRange = m_view->document()->documentRange(); + match.searchText(inputRange, pattern); + } + + const MatchResult matchResult = match.isValid() ? (wrap ? MatchWrappedForward : MatchFound) : + pattern.isEmpty() ? MatchNothing : + MatchMismatch; + + const Range selectionRange = pattern.isEmpty() ? Range(m_incInitCursor, m_incInitCursor) : + match.isValid() ? match.range() : + Range::invalid(); + + // don't update m_incInitCursor when we move the cursor + disconnect(m_view, SIGNAL(cursorPositionChanged(KTextEditor::View*,KTextEditor::Cursor)), + this, SLOT(updateIncInitCursor())); + selectRange2(selectionRange); + connect(m_view, SIGNAL(cursorPositionChanged(KTextEditor::View*,KTextEditor::Cursor)), + this, SLOT(updateIncInitCursor())); + + indicateMatch(matchResult); +} + + + +void KateSearchBar::setMatchCase(bool matchCase) { + if (this->matchCase() == matchCase) + return; + + if (isPower()) + m_powerUi->matchCase->setChecked(matchCase); + else + m_incUi->matchCase->setChecked(matchCase); +} + + + +void KateSearchBar::onMatchCaseToggled(bool /*matchCase*/) { + sendConfig(); + + if (m_incUi != 0) { + // Re-search with new settings + const QString pattern = m_incUi->pattern->currentText(); + onIncPatternChanged(pattern); + } else { + indicateMatch(MatchNothing); + } +} + + + +bool KateSearchBar::matchCase() const +{ + return isPower() ? m_powerUi->matchCase->isChecked() + : m_incUi->matchCase->isChecked(); +} + + + +void KateSearchBar::fixForSingleLine(Range & range, SearchDirection searchDirection) { + FAST_DEBUG("Single-line workaround checking BEFORE" << range); + if (searchDirection == SearchForward) { + const int line = range.start().line(); + const int col = range.start().column(); + const int maxColWithNewline = m_view->document()->lineLength(line) + 1; + if (col == maxColWithNewline) { + FAST_DEBUG("Starting on a newline" << range); + const int maxLine = m_view->document()->lines() - 1; + if (line < maxLine) { + range.setRange(Cursor(line + 1, 0), range.end()); + FAST_DEBUG("Search range fixed to " << range); + } else { + FAST_DEBUG("Already at last line"); + range = Range::invalid(); + } + } + } else { + const int col = range.end().column(); + if (col == 0) { + FAST_DEBUG("Ending after a newline" << range); + const int line = range.end().line(); + if (line > 0) { + const int maxColWithNewline = m_view->document()->lineLength(line - 1); + range.setRange(range.start(), Cursor(line - 1, maxColWithNewline)); + FAST_DEBUG("Search range fixed to " << range); + } else { + FAST_DEBUG("Already at first line"); + range = Range::invalid(); + } + } + } + FAST_DEBUG("Single-line workaround checking AFTER" << range); +} + + + +void KateSearchBar::onReturnPressed() { + const Qt::KeyboardModifiers modifiers = QApplication::keyboardModifiers(); + const bool shiftDown = (modifiers & Qt::ShiftModifier) != 0; + const bool controlDown = (modifiers & Qt::ControlModifier) != 0; + + if (shiftDown) { + // Shift down, search backwards + findPrevious(); + } else { + // Shift up, search forwards + findNext(); + } + + if (controlDown) { + emit hideMe(); + } +} + + + +bool KateSearchBar::find(SearchDirection searchDirection, const QString * replacement) { + // What to find? + if (searchPattern().isEmpty()) { + return false; // == Pattern error + } + + // don't let selectionChanged signal mess around in this routine + disconnect(m_view, SIGNAL(selectionChanged(KTextEditor::View*)), this, SLOT(updateSelectionOnly())); + + // clear previous highlights if there are any + clearHighlights(); + + const Search::SearchOptions enabledOptions = searchOptions(searchDirection); + + // Where to find? + Range inputRange; + const Range selection = m_view->selection() ? m_view->selectionRange() : Range::invalid(); + if (selection.isValid()) { + if (selectionOnly()) { + // First match in selection + inputRange = selection; + } else { + // Next match after/before selection if a match was selected before + if (searchDirection == SearchForward) { + inputRange.setRange(selection.start(), m_view->document()->documentEnd()); + } else { + inputRange.setRange(Cursor(0, 0), selection.end()); + } + } + } else { + // No selection + const Cursor cursorPos = m_view->cursorPosition(); + if (searchDirection == SearchForward) { + inputRange.setRange(cursorPos, m_view->document()->documentEnd()); + } else { + inputRange.setRange(Cursor(0, 0), cursorPos); + } + } + FAST_DEBUG("Search range is" << inputRange); + + { + const bool regexMode = enabledOptions.testFlag(Search::Regex); + const bool multiLinePattern = regexMode ? KateRegExp(searchPattern()).isMultiLine() : false; + + // Single-line pattern workaround + if (regexMode && !multiLinePattern) { + fixForSingleLine(inputRange, searchDirection); + } + } + + KateMatch match(m_view->doc(), enabledOptions); + Range afterReplace = Range::invalid(); + + // Find, first try + match.searchText(inputRange, searchPattern()); + if (match.isValid() && match.range() == selection) { + // Same match again + if (replacement != 0) { + // Selection is match -> replace + KTextEditor::MovingRange *smartInputRange = m_view->doc()->newMovingRange (inputRange, KTextEditor::MovingRange::ExpandLeft | KTextEditor::MovingRange::ExpandRight); + afterReplace = match.replace(*replacement, m_view->blockSelection()); + inputRange = *smartInputRange; + delete smartInputRange; + } + + if (!selectionOnly()) { + // Find, second try after old selection + if (searchDirection == SearchForward) { + const Cursor start = (replacement != 0) ? afterReplace.end() : selection.end(); + inputRange.setRange(start, inputRange.end()); + } else { + const Cursor end = (replacement != 0) ? afterReplace.start() : selection.start(); + inputRange.setRange(inputRange.start(), end); + } + } + + // Single-line pattern workaround + fixForSingleLine(inputRange, searchDirection); + + match.searchText(inputRange, searchPattern()); + } + + bool askWrap = !match.isValid() && (!selection.isValid() || !selectionOnly()); + bool wrap = false; + KateMatch matchAfterWarp(m_view->doc(), enabledOptions); + + if (askWrap) { + matchAfterWarp.searchText(m_view->document()->documentRange(), searchPattern()); + if (!matchAfterWarp.isValid()) + askWrap = false; + } + + if (askWrap) { + if (m_unitTestMode) { + wrap = true; + } + else { + QString question = searchDirection == SearchForward ? i18n("Bottom of file reached. Continue from top?") + : i18n("Top of file reached. Continue from bottom?"); + wrap = (KMessageBox::questionYesNo( 0, question, i18n("Continue search?"), KStandardGuiItem::yes(), KStandardGuiItem::no(), + QString("DoNotShowAgainContinueSearchDialog")) == KMessageBox::Yes ); + } + } + + if (wrap) { + match = matchAfterWarp; + } + + if (match.isValid()) { + selectRange2(match.range()); + } + + const MatchResult matchResult = !match.isValid() ? MatchMismatch : + !wrap ? MatchFound : + searchDirection == SearchForward ? MatchWrappedForward : + MatchWrappedBackward; + indicateMatch(matchResult); + + // highlight replacements if applicable + if (afterReplace.isValid()) { + highlightReplacement(afterReplace); + } + + // restore connection + connect(m_view, SIGNAL(selectionChanged(KTextEditor::View*)), this, SLOT(updateSelectionOnly())); + + return true; // == No pattern error +} + + + + +void KateSearchBar::findAll() +{ + // clear highlightings of prior search&replace action + clearHighlights(); + + Range inputRange = (m_view->selection() && selectionOnly()) + ? m_view->selectionRange() + : m_view->document()->documentRange(); + const int occurrences = findAll(inputRange, NULL); + + // send passive notification to view + showInfoMessage(i18ncp("short translation", "1 match found", "%1 matches found", occurrences)); + + indicateMatch(occurrences > 0 ? MatchFound : MatchMismatch); +} + + + +void KateSearchBar::onPowerPatternChanged(const QString & /*pattern*/) { + givePatternFeedback(); + indicateMatch(MatchNothing); +} + + + +bool KateSearchBar::isPatternValid() const { + if (searchPattern().isEmpty()) + return false; + + return searchOptions().testFlag(Search::WholeWords) ? searchPattern().trimmed() == searchPattern() : + searchOptions().testFlag(Search::Regex) ? QRegExp(searchPattern()).isValid() : + true; +} + + + +void KateSearchBar::givePatternFeedback() { + // Enable/disable next/prev and replace next/all + m_powerUi->findNext->setEnabled(isPatternValid()); + m_powerUi->findPrev->setEnabled(isPatternValid()); + m_powerUi->replaceNext->setEnabled(isPatternValid()); + m_powerUi->replaceAll->setEnabled(isPatternValid()); +} + + + +void KateSearchBar::addCurrentTextToHistory(QComboBox * combo) { + const QString text = combo->currentText(); + const int index = combo->findText(text); + + if (index > 0) + combo->removeItem(index); + if (index != 0) { + combo->insertItem(0, text); + combo->setCurrentIndex(0); + } +} + + + +void KateSearchBar::backupConfig(bool ofPower) { + if (ofPower) { + m_powerMatchCase = m_powerUi->matchCase->isChecked(); + m_powerMode = m_powerUi->searchMode->currentIndex(); + } else { + m_incMatchCase = m_incUi->matchCase->isChecked(); + } +} + + + +void KateSearchBar::sendConfig() { + const long pastFlags = m_config->searchFlags(); + long futureFlags = pastFlags; + + if (m_powerUi != NULL) { + const bool OF_POWER = true; + backupConfig(OF_POWER); + + // Update power search flags only + const long incFlagsOnly = pastFlags + & (KateViewConfig::IncHighlightAll + | KateViewConfig::IncFromCursor + | KateViewConfig::IncMatchCase); + + futureFlags = incFlagsOnly + | (m_powerMatchCase ? KateViewConfig::PowerMatchCase : 0) + | (m_powerFromCursor ? KateViewConfig::PowerFromCursor : 0) + | (m_powerHighlightAll ? KateViewConfig::PowerHighlightAll : 0) + | ((m_powerMode == MODE_REGEX) + ? KateViewConfig::PowerModeRegularExpression + : ((m_powerMode == MODE_ESCAPE_SEQUENCES) + ? KateViewConfig::PowerModeEscapeSequences + : ((m_powerMode == MODE_WHOLE_WORDS) + ? KateViewConfig::PowerModeWholeWords + : KateViewConfig::PowerModePlainText))); + + } else if (m_incUi != NULL) { + const bool OF_INCREMENTAL = false; + backupConfig(OF_INCREMENTAL); + + // Update incremental search flags only + const long powerFlagsOnly = pastFlags + & (KateViewConfig::PowerMatchCase + | KateViewConfig::PowerFromCursor + | KateViewConfig::PowerHighlightAll + | KateViewConfig::PowerModeRegularExpression + | KateViewConfig::PowerModeEscapeSequences + | KateViewConfig::PowerModeWholeWords + | KateViewConfig::PowerModePlainText); + + futureFlags = powerFlagsOnly + | (m_incHighlightAll ? KateViewConfig::IncHighlightAll : 0) + | (m_incFromCursor ? KateViewConfig::IncFromCursor : 0) + | (m_incMatchCase ? KateViewConfig::IncMatchCase : 0); + } + + // Adjust global config + m_config->setSearchFlags(futureFlags); +} + + + +void KateSearchBar::replaceNext() { + const QString replacement = m_powerUi->replacement->currentText(); + + if (find(SearchForward, &replacement)) { + // Never merge replace actions with other replace actions/user actions + m_view->doc()->undoManager()->undoSafePoint(); + + // Add to search history + addCurrentTextToHistory(m_powerUi->pattern); + + // Add to replace history + addCurrentTextToHistory(m_powerUi->replacement); + } +} + + + +// replacement == NULL --> Highlight all matches +// replacement != NULL --> Replace and highlight all matches +int KateSearchBar::findAll(Range inputRange, const QString * replacement) +{ + // don't let selectionChanged signal mess around in this routine + disconnect(m_view, SIGNAL(selectionChanged(KTextEditor::View*)), this, SLOT(updateSelectionOnly())); + + const Search::SearchOptions enabledOptions = searchOptions(SearchForward); + + const bool regexMode = enabledOptions.testFlag(Search::Regex); + const bool multiLinePattern = regexMode ? KateRegExp(searchPattern()).isMultiLine() : false; + + KTextEditor::MovingRange * workingRange = m_view->doc()->newMovingRange(inputRange); + QList highlightRanges; + int matchCounter = 0; + + bool block = m_view->selection() && m_view->blockSelection(); + int line = inputRange.start().line(); + do { + if (block) + workingRange = m_view->doc()->newMovingRange(m_view->doc()->rangeOnLine(inputRange, line)); + + for (;;) { + KateMatch match(m_view->doc(), enabledOptions); + match.searchText(*workingRange, searchPattern()); + if (!match.isValid()) { + break; + } + bool const originalMatchEmpty = match.isEmpty(); + + // Work with the match + if (replacement != NULL) { + if (matchCounter == 0) { + m_view->document()->startEditing(); + } + + // Replace + const Range afterReplace = match.replace(*replacement, false, ++matchCounter); + + // Highlight and continue after adjusted match + //highlightReplacement(*afterReplace); + highlightRanges << afterReplace; + } else { + // Highlight and continue after original match + //highlightMatch(match); + highlightRanges << match.range(); + matchCounter++; + } + + // Continue after match + if (highlightRanges.last().end() >= workingRange->end()) + break; + KTextEditor::MovingCursor* workingStart = + static_cast(m_view->document())->newMovingCursor(highlightRanges.last().end()); + if (originalMatchEmpty) { + // Can happen for regex patterns like "^". + // If we don't advance here we will loop forever... + workingStart->move(1); + } else if (regexMode && !multiLinePattern && workingStart->atEndOfLine()) { + // single-line regexps might match the naked line end + // therefore we better advance to the next line + workingStart->move(1); + } + workingRange->setRange(*workingStart, workingRange->end()); + + const bool atEndOfDocument = workingStart->atEndOfDocument(); + delete workingStart; + // Are we done? + if (!workingRange->toRange().isValid() || atEndOfDocument) { + break; + } + } + + } while (block && ++line <= inputRange.end().line()); + + // After last match + if (matchCounter > 0) { + if (replacement != NULL) { + m_view->document()->endEditing(); + } + } + + if (replacement == NULL) + foreach (const Range &r, highlightRanges) { + highlightMatch(r); + } + else + foreach (const Range &r, highlightRanges) { + highlightReplacement(r); + } + + delete workingRange; + + // restore connection + connect(m_view, SIGNAL(selectionChanged(KTextEditor::View*)), this, SLOT(updateSelectionOnly())); + + return matchCounter; +} + + + +void KateSearchBar::replaceAll() +{ + // clear prior highlightings (deletes info message if present) + clearHighlights(); + + // What to find/replace? + const QString replacement = m_powerUi->replacement->currentText(); + + // Where to replace? + Range selection; + const bool selected = m_view->selection(); + Range inputRange = (selected && selectionOnly()) + ? m_view->selectionRange() + : m_view->document()->documentRange(); + + + // Pass on the hard work + int replacementsDone=findAll(inputRange, &replacement); + + // send passive notification to view + showInfoMessage(i18ncp("short translation", "1 replacement made", "%1 replacements made", replacementsDone)); + + // Never merge replace actions with other replace actions/user actions + m_view->doc()->undoManager()->undoSafePoint(); + + // Add to search history + addCurrentTextToHistory(m_powerUi->pattern); + + // Add to replace history + addCurrentTextToHistory(m_powerUi->replacement); +} + + + +void KateSearchBar::setSearchPattern(const QString &searchPattern) +{ + if (searchPattern == this->searchPattern()) + return; + + if (isPower()) + m_powerUi->pattern->setEditText(searchPattern); + else + m_incUi->pattern->setEditText(searchPattern); +} + + + +QString KateSearchBar::searchPattern() const { + return (m_powerUi != 0) ? m_powerUi->pattern->currentText() + : m_incUi->pattern->currentText(); +} + + + +void KateSearchBar::setSelectionOnly(bool selectionOnly) +{ + if (this->selectionOnly() == selectionOnly) + return; + + if (isPower()) + m_powerUi->selectionOnly->setChecked(selectionOnly); +} + + + + +bool KateSearchBar::selectionOnly() const { + return isPower() ? m_powerUi->selectionOnly->isChecked() + : false; +} + + + +KTextEditor::Search::SearchOptions KateSearchBar::searchOptions(SearchDirection searchDirection) const { + Search::SearchOptions enabledOptions = KTextEditor::Search::Default; + + if (!matchCase()) { + enabledOptions |= Search::CaseInsensitive; + } + + if (searchDirection == SearchBackward) { + enabledOptions |= Search::Backwards; + } + + if (m_powerUi != NULL) { + switch (m_powerUi->searchMode->currentIndex()) { + case MODE_WHOLE_WORDS: + enabledOptions |= Search::WholeWords; + break; + + case MODE_ESCAPE_SEQUENCES: + enabledOptions |= Search::EscapeSequences; + break; + + case MODE_REGEX: + enabledOptions |= Search::Regex; + break; + + case MODE_PLAIN_TEXT: // FALLTHROUGH + default: + break; + + } + } + + return enabledOptions; +} + + + + +struct ParInfo { + int openIndex; + bool capturing; + int captureNumber; // 1..9 +}; + + + +QVector KateSearchBar::getCapturePatterns(const QString & pattern) const { + QVector capturePatterns; + capturePatterns.reserve(9); + QStack parInfos; + + const int inputLen = pattern.length(); + int input = 0; // walker index + bool insideClass = false; + int captureCount = 0; + + while (input < inputLen) { + if (insideClass) { + // Wait for closing, unescaped ']' + if (pattern[input].unicode() == L']') { + insideClass = false; + } + input++; + } + else + { + switch (pattern[input].unicode()) + { + case L'\\': + // Skip this and any next character + input += 2; + break; + + case L'(': + ParInfo curInfo; + curInfo.openIndex = input; + curInfo.capturing = (input + 1 >= inputLen) || (pattern[input + 1].unicode() != '?'); + if (curInfo.capturing) { + captureCount++; + } + curInfo.captureNumber = captureCount; + parInfos.push(curInfo); + + input++; + break; + + case L')': + if (!parInfos.empty()) { + ParInfo & top = parInfos.top(); + if (top.capturing && (top.captureNumber <= 9)) { + const int start = top.openIndex + 1; + const int len = input - start; + if (capturePatterns.size() < top.captureNumber) { + capturePatterns.resize(top.captureNumber); + } + capturePatterns[top.captureNumber - 1] = pattern.mid(start, len); + } + parInfos.pop(); + } + + input++; + break; + + case L'[': + input++; + insideClass = true; + break; + + default: + input++; + break; + + } + } + } + + return capturePatterns; +} + + + +void KateSearchBar::showExtendedContextMenu(bool forPattern, const QPoint& pos) { + // Make original menu + QComboBox* comboBox = forPattern ? m_powerUi->pattern : m_powerUi->replacement; + QMenu* const contextMenu = comboBox->lineEdit()->createStandardContextMenu(); + + if (contextMenu == NULL) { + return; + } + + bool extendMenu = false; + bool regexMode = false; + switch (m_powerUi->searchMode->currentIndex()) { + case MODE_REGEX: + regexMode = true; + // FALLTHROUGH + + case MODE_ESCAPE_SEQUENCES: + extendMenu = true; + break; + + default: + break; + } + + AddMenuManager addMenuManager(contextMenu, 37); + if (!extendMenu) { + addMenuManager.enableMenu(extendMenu); + } else { + // Build menu + if (forPattern) { + if (regexMode) { + addMenuManager.addEntry("^", "", i18n("Beginning of line")); + addMenuManager.addEntry("$", "", i18n("End of line")); + addMenuManager.addSeparator(); + addMenuManager.addEntry(".", "", i18n("Any single character (excluding line breaks)")); + addMenuManager.addSeparator(); + addMenuManager.addEntry("+", "", i18n("One or more occurrences")); + addMenuManager.addEntry("*", "", i18n("Zero or more occurrences")); + addMenuManager.addEntry("?", "", i18n("Zero or one occurrences")); + addMenuManager.addEntry("{a", ",b}", i18n(" through occurrences"), "{", ",}"); + addMenuManager.addSeparator(); + addMenuManager.addEntry("(", ")", i18n("Group, capturing")); + addMenuManager.addEntry("|", "", i18n("Or")); + addMenuManager.addEntry("[", "]", i18n("Set of characters")); + addMenuManager.addEntry("[^", "]", i18n("Negative set of characters")); + addMenuManager.addSeparator(); + } + } else { + addMenuManager.addEntry("\\0", "", i18n("Whole match reference")); + addMenuManager.addSeparator(); + if (regexMode) { + const QString pattern = m_powerUi->pattern->currentText(); + const QVector capturePatterns = getCapturePatterns(pattern); + + const int captureCount = capturePatterns.count(); + for (int i = 1; i <= 9; i++) { + const QString number = QString::number(i); + const QString & captureDetails = (i <= captureCount) + ? (QString(" = (") + capturePatterns[i - 1].left(30)) + QString(")") + : QString(); + addMenuManager.addEntry("\\" + number, "", + i18n("Reference") + ' ' + number + captureDetails); + } + + addMenuManager.addSeparator(); + } + } + + addMenuManager.addEntry("\\n", "", i18n("Line break")); + addMenuManager.addEntry("\\t", "", i18n("Tab")); + + if (forPattern && regexMode) { + addMenuManager.addEntry("\\b", "", i18n("Word boundary")); + addMenuManager.addEntry("\\B", "", i18n("Not word boundary")); + addMenuManager.addEntry("\\d", "", i18n("Digit")); + addMenuManager.addEntry("\\D", "", i18n("Non-digit")); + addMenuManager.addEntry("\\s", "", i18n("Whitespace (excluding line breaks)")); + addMenuManager.addEntry("\\S", "", i18n("Non-whitespace (excluding line breaks)")); + addMenuManager.addEntry("\\w", "", i18n("Word character (alphanumerics plus '_')")); + addMenuManager.addEntry("\\W", "", i18n("Non-word character")); + } + + addMenuManager.addEntry("\\0???", "", i18n("Octal character 000 to 377 (2^8-1)"), "\\0"); + addMenuManager.addEntry("\\x????", "", i18n("Hex character 0000 to FFFF (2^16-1)"), "\\x"); + addMenuManager.addEntry("\\\\", "", i18n("Backslash")); + + if (forPattern && regexMode) { + addMenuManager.addSeparator(); + addMenuManager.addEntry("(?:E", ")", i18n("Group, non-capturing"), "(?:"); + addMenuManager.addEntry("(?=E", ")", i18n("Lookahead"), "(?="); + addMenuManager.addEntry("(?!E", ")", i18n("Negative lookahead"), "(?!"); + } + + if (!forPattern) { + addMenuManager.addSeparator(); + addMenuManager.addEntry("\\L", "", i18n("Begin lowercase conversion")); + addMenuManager.addEntry("\\U", "", i18n("Begin uppercase conversion")); + addMenuManager.addEntry("\\E", "", i18n("End case conversion")); + addMenuManager.addEntry("\\l", "", i18n("Lowercase first character conversion")); + addMenuManager.addEntry("\\u", "", i18n("Uppercase first character conversion")); + addMenuManager.addEntry("\\#[#..]", "", i18n("Replacement counter (for Replace All)"), "\\#"); + } + } + + // Show menu + QAction * const result = contextMenu->exec(comboBox->mapToGlobal(pos)); + if (result != NULL) { + addMenuManager.handle(result, comboBox->lineEdit()); + } +} + + + +void KateSearchBar::onPowerModeChanged(int /*index*/) { + if (m_powerUi->searchMode->currentIndex() == MODE_REGEX) { + m_powerUi->matchCase->setChecked(true); + } + + sendConfig(); + indicateMatch(MatchNothing); + + givePatternFeedback(); +} + + + +/*static*/ void KateSearchBar::nextMatchForSelection(KateView * view, SearchDirection searchDirection) { + const bool selected = view->selection(); + if (selected) { + const QString pattern = view->selectionText(); + + // How to find? + Search::SearchOptions enabledOptions(KTextEditor::Search::Default); + if (searchDirection == SearchBackward) { + enabledOptions |= Search::Backwards; + } + + // Where to find? + const Range selRange = view->selectionRange(); + Range inputRange; + if (searchDirection == SearchForward) { + inputRange.setRange(selRange.end(), view->doc()->documentEnd()); + } else { + inputRange.setRange(Cursor(0, 0), selRange.start()); + } + + // Find, first try + KateMatch match(view->doc(), enabledOptions); + match.searchText(inputRange, pattern); + + if (match.isValid()) { + selectRange(view, match.range()); + } else { + // Find, second try + if (searchDirection == SearchForward) { + inputRange.setRange(Cursor(0, 0), selRange.start()); + } else { + inputRange.setRange(selRange.end(), view->doc()->documentEnd()); + } + KateMatch match2(view->doc(), enabledOptions); + match2.searchText(inputRange, pattern); + if (match2.isValid()) { + selectRange(view, match2.range()); + } + } + } else { + // Select current word so we can search for that the next time + const Cursor cursorPos = view->cursorPosition(); + view->selectWord(cursorPos); + } +} + + + +void KateSearchBar::enterPowerMode() { + QString initialPattern; + bool selectionOnly = false; + + // Guess settings from context: init pattern with current selection + const bool selected = m_view->selection(); + if (selected) { + const Range & selection = m_view->selectionRange(); + if (selection.onSingleLine()) { + // ... with current selection + initialPattern = m_view->selectionText(); + } else { + // Enable selection only + selectionOnly = true; + } + } + + // If there's no new selection, we'll use the existing pattern + if (initialPattern.isNull()) { + // Coming from power search? + const bool fromReplace = (m_powerUi != NULL) && (m_widget->isVisible()); + if (fromReplace) { + QLineEdit * const patternLineEdit = m_powerUi->pattern->lineEdit(); + Q_ASSERT(patternLineEdit != NULL); + patternLineEdit->selectAll(); + m_powerUi->pattern->setFocus(Qt::MouseFocusReason); + return; + } + + // Coming from incremental search? + const bool fromIncremental = (m_incUi != NULL) && (m_widget->isVisible()); + if (fromIncremental) { + initialPattern = m_incUi->pattern->currentText(); + } + } + + // Create dialog + const bool create = (m_powerUi == NULL); + if (create) { + // Kill incremental widget + if (m_incUi != NULL) { + // Backup current settings + const bool OF_INCREMENTAL = false; + backupConfig(OF_INCREMENTAL); + + // Kill widget + delete m_incUi; + m_incUi = NULL; + m_layout->removeWidget(m_widget); + m_widget->deleteLater(); // I didn't get a crash here but for symmetrie to the other mutate slot^ + } + + // Add power widget + m_widget = new QWidget(this); + m_powerUi = new Ui::PowerSearchBar; + m_powerUi->setupUi(m_widget); + m_layout->addWidget(m_widget); + + // Bind to shared history models + m_powerUi->pattern->setDuplicatesEnabled(false); + m_powerUi->pattern->setInsertPolicy(QComboBox::InsertAtTop); + m_powerUi->pattern->setMaxCount(m_config->maxHistorySize()); + m_powerUi->pattern->setModel(m_config->patternHistoryModel()); + m_powerUi->replacement->setDuplicatesEnabled(false); + m_powerUi->replacement->setInsertPolicy(QComboBox::InsertAtTop); + m_powerUi->replacement->setMaxCount(m_config->maxHistorySize()); + m_powerUi->replacement->setModel(m_config->replacementHistoryModel()); + + // Icons + m_powerUi->mutate->setIcon(KIcon("arrow-down-double")); + m_powerUi->findNext->setIcon(KIcon("go-down-search")); + m_powerUi->findPrev->setIcon(KIcon("go-up-search")); + m_powerUi->findAll->setIcon(KIcon("edit-find")); + + // Focus proxy + centralWidget()->setFocusProxy(m_powerUi->pattern); + + // Make completers case-sensitive + m_powerUi->pattern->completionObject()->setIgnoreCase(false); + m_powerUi->replacement->completionObject()->setIgnoreCase(false); + } + + m_powerUi->selectionOnly->setChecked(selectionOnly); + + // Restore previous settings + if (create) { + m_powerUi->matchCase->setChecked(m_powerMatchCase); + m_powerUi->searchMode->setCurrentIndex(m_powerMode); + } + + // force current index of -1 --> shows 1st completion entry instead of 2nd + m_powerUi->pattern->setCurrentIndex(-1); + m_powerUi->replacement->setCurrentIndex(-1); + + // Set initial search pattern + QLineEdit * const patternLineEdit = m_powerUi->pattern->lineEdit(); + Q_ASSERT(patternLineEdit != NULL); + patternLineEdit->setText(initialPattern); + patternLineEdit->selectAll(); + + // Set initial replacement text + QLineEdit * const replacementLineEdit = m_powerUi->replacement->lineEdit(); + Q_ASSERT(replacementLineEdit != NULL); + replacementLineEdit->setText(""); + + // Propagate settings (slots are still inactive on purpose) + onPowerPatternChanged(initialPattern); + givePatternFeedback(); + + if (create) { + // Slots + connect(m_powerUi->mutate, SIGNAL(clicked()), this, SLOT(enterIncrementalMode())); + connect(patternLineEdit, SIGNAL(textChanged(QString)), this, SLOT(onPowerPatternChanged(QString))); + connect(m_powerUi->findNext, SIGNAL(clicked()), this, SLOT(findNext())); + connect(m_powerUi->findPrev, SIGNAL(clicked()), this, SLOT(findPrevious())); + connect(m_powerUi->replaceNext, SIGNAL(clicked()), this, SLOT(replaceNext())); + connect(m_powerUi->replaceAll, SIGNAL(clicked()), this, SLOT(replaceAll())); + connect(m_powerUi->searchMode, SIGNAL(currentIndexChanged(int)), this, SLOT(onPowerModeChanged(int))); + connect(m_powerUi->matchCase, SIGNAL(toggled(bool)), this, SLOT(onMatchCaseToggled(bool))); + connect(m_powerUi->findAll, SIGNAL(clicked()), this, SLOT(findAll())); + + // Make [return] in pattern line edit trigger action + connect(patternLineEdit, SIGNAL(returnPressed()), this, SLOT(onReturnPressed())); + connect(replacementLineEdit, SIGNAL(returnPressed()), this, SLOT(replaceNext())); + + // Hook into line edit context menus + m_powerUi->pattern->setContextMenuPolicy(Qt::CustomContextMenu); + connect(m_powerUi->pattern, SIGNAL(customContextMenuRequested(QPoint)), this, + SLOT(onPowerPatternContextMenuRequest(QPoint))); + m_powerUi->replacement->setContextMenuPolicy(Qt::CustomContextMenu); + connect(m_powerUi->replacement, SIGNAL(customContextMenuRequested(QPoint)), this, + SLOT(onPowerReplacmentContextMenuRequest(QPoint))); + } + + // Focus + if (m_widget->isVisible()) { + m_powerUi->pattern->setFocus(Qt::MouseFocusReason); + } +} + + + +void KateSearchBar::enterIncrementalMode() { + QString initialPattern; + + // Guess settings from context: init pattern with current selection + const bool selected = m_view->selection(); + if (selected) { + const Range & selection = m_view->selectionRange(); + if (selection.onSingleLine()) { + // ... with current selection + initialPattern = m_view->selectionText(); + } + } + + // If there's no new selection, we'll use the existing pattern + if (initialPattern.isNull()) { + // Coming from incremental search? + const bool fromIncremental = (m_incUi != NULL) && (m_widget->isVisible()); + if (fromIncremental) { + m_incUi->pattern->lineEdit()->selectAll(); + m_incUi->pattern->setFocus(Qt::MouseFocusReason); + return; + } + + // Coming from power search? + const bool fromReplace = (m_powerUi != NULL) && (m_widget->isVisible()); + if (fromReplace) { + initialPattern = m_powerUi->pattern->currentText(); + } + } + + // Still no search pattern? Use the word under the cursor + if (initialPattern.isNull()) { + const KTextEditor::Cursor cursorPosition = m_view->cursorPosition(); + initialPattern = m_view->doc()->getWord( cursorPosition ); + } + + // Create dialog + const bool create = (m_incUi == NULL); + if (create) { + // Kill power widget + if (m_powerUi != NULL) { + // Backup current settings + const bool OF_POWER = true; + backupConfig(OF_POWER); + + // Kill widget + delete m_powerUi; + m_powerUi = NULL; + m_layout->removeWidget(m_widget); + m_widget->deleteLater(); //deleteLater, because it's not a good idea too delete the widget and there for the button triggering this slot + } + + // Add incremental widget + m_widget = new QWidget(this); + m_incUi = new Ui::IncrementalSearchBar; + m_incUi->setupUi(m_widget); + m_layout->addWidget(m_widget); + +// new QShortcut(KStandardShortcut::paste().primary(), m_incUi->pattern, SLOT(paste()), 0, Qt::WidgetWithChildrenShortcut); +// if (!KStandardShortcut::paste().alternate().isEmpty()) +// new QShortcut(KStandardShortcut::paste().alternate(), m_incUi->pattern, SLOT(paste()), 0, Qt::WidgetWithChildrenShortcut); + + + // Icons + m_incUi->mutate->setIcon(KIcon("arrow-up-double")); + m_incUi->next->setIcon(KIcon("go-down-search")); + m_incUi->prev->setIcon(KIcon("go-up-search")); + + // Ensure minimum size + m_incUi->pattern->setMinimumWidth(12 * m_incUi->pattern->fontMetrics().height()); + + // Focus proxy + centralWidget()->setFocusProxy(m_incUi->pattern); + + m_incUi->pattern->setDuplicatesEnabled(false); + m_incUi->pattern->setInsertPolicy(QComboBox::InsertAtTop); + m_incUi->pattern->setMaxCount(m_config->maxHistorySize()); + m_incUi->pattern->setModel(m_config->patternHistoryModel()); + m_incUi->pattern->setAutoCompletion(false); + } + + // Restore previous settings + if (create) { + m_incUi->matchCase->setChecked(m_incMatchCase); + } + + // force current index of -1 --> shows 1st completion entry instead of 2nd + m_incUi->pattern->setCurrentIndex(-1); + + // Set initial search pattern + if (!create) + disconnect(m_incUi->pattern, SIGNAL(editTextChanged(QString)), this, SLOT(onIncPatternChanged(QString))); + m_incUi->pattern->setEditText(initialPattern); + connect(m_incUi->pattern, SIGNAL(editTextChanged(QString)), this, SLOT(onIncPatternChanged(QString))); + m_incUi->pattern->lineEdit()->selectAll(); + + // Propagate settings (slots are still inactive on purpose) + if (initialPattern.isEmpty()) { + // Reset edit color + indicateMatch(MatchNothing); + } + + // Enable/disable next/prev + m_incUi->next->setDisabled(initialPattern.isEmpty()); + m_incUi->prev->setDisabled(initialPattern.isEmpty()); + + if (create) { + // Slots + connect(m_incUi->mutate, SIGNAL(clicked()), this, SLOT(enterPowerMode())); + connect(m_incUi->pattern->lineEdit(), SIGNAL(returnPressed()), this, SLOT(onReturnPressed())); + connect(m_incUi->next, SIGNAL(clicked()), this, SLOT(findNext())); + connect(m_incUi->prev, SIGNAL(clicked()), this, SLOT(findPrevious())); + connect(m_incUi->matchCase, SIGNAL(toggled(bool)), this, SLOT(onMatchCaseToggled(bool))); + } + + // Focus + if (m_widget->isVisible()) { + m_incUi->pattern->setFocus(Qt::MouseFocusReason); + } +} + +bool KateSearchBar::clearHighlights() +{ + if (m_infoMessage) { + delete m_infoMessage; + } + + if (m_hlRanges.isEmpty()) { + return false; + } + qDeleteAll(m_hlRanges); + m_hlRanges.clear(); + return true; +} + +void KateSearchBar::updateHighlightColors() +{ + const QColor& searchColor = m_view->renderer()->config()->searchHighlightColor(); + const QColor& replaceColor = m_view->renderer()->config()->replaceHighlightColor(); + + // init match attribute + highlightMatchAttribute->setBackground(searchColor); + highlightMatchAttribute->dynamicAttribute (Attribute::ActivateMouseIn)->setBackground(searchColor); + highlightMatchAttribute->dynamicAttribute (Attribute::ActivateCaretIn)->setBackground(searchColor); + + // init replacement attribute + highlightReplacementAttribute->setBackground(replaceColor); +} + + +void KateSearchBar::showEvent(QShowEvent * event) { + // Update init cursor + if (m_incUi != NULL) { + m_incInitCursor = m_view->cursorPosition(); + } + + updateSelectionOnly(); + KateViewBarWidget::showEvent(event); +} + + +void KateSearchBar::updateSelectionOnly() { + if (m_powerUi == NULL) { + return; + } + + // Re-init "Selection only" checkbox if power search bar open + const bool selected = m_view->selection(); + bool selectionOnly = selected; + if (selected) { + Range const & selection = m_view->selectionRange(); + selectionOnly = !selection.onSingleLine(); + } + m_powerUi->selectionOnly->setChecked(selectionOnly); +} + + +void KateSearchBar::updateIncInitCursor() { + if (m_incUi == NULL) { + return; + } + + // Update init cursor + m_incInitCursor = m_view->cursorPosition(); +} + + +void KateSearchBar::onPowerPatternContextMenuRequest(const QPoint& pos) { + const bool FOR_PATTERN = true; + showExtendedContextMenu(FOR_PATTERN, pos); +} + +void KateSearchBar::onPowerPatternContextMenuRequest() { + onPowerPatternContextMenuRequest(m_powerUi->pattern->mapFromGlobal(QCursor::pos())); +} + + +void KateSearchBar::onPowerReplacmentContextMenuRequest(const QPoint& pos) { + const bool FOR_REPLACEMENT = false; + showExtendedContextMenu(FOR_REPLACEMENT, pos); +} + +void KateSearchBar::onPowerReplacmentContextMenuRequest() { + onPowerReplacmentContextMenuRequest(m_powerUi->replacement->mapFromGlobal(QCursor::pos())); +} + + +bool KateSearchBar::isPower() const { + return m_powerUi != 0; +} + +void KateSearchBar::slotReadWriteChanged () +{ + if (!KateSearchBar::isPower()) + return; + + // perhaps disable/enable + m_powerUi->replaceNext->setEnabled (m_view->doc()->isReadWrite() && isPatternValid()); + m_powerUi->replaceAll->setEnabled (m_view->doc()->isReadWrite() && isPatternValid()); +} + +// kate: space-indent on; indent-width 4; replace-tabs on; diff --git a/kate/part/search/katesearchbar.h b/kate/part/search/katesearchbar.h new file mode 100644 index 00000000..01af39f1 --- /dev/null +++ b/kate/part/search/katesearchbar.h @@ -0,0 +1,227 @@ +/* This file is part of the KDE libraries and the Kate part. + * + * Copyright (C) 2009-2010 Bernhard Beschow + * Copyright (C) 2007 Sebastian Pipping + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef KATE_SEARCH_BAR_H +#define KATE_SEARCH_BAR_H 1 + +#include "kateviewhelpers.h" +#include "katepartinterfaces_export.h" + +#include +#include +#include + +#include +#include + +class KateView; +class KateViewConfig; + +QT_BEGIN_NAMESPACE +namespace Ui { + class IncrementalSearchBar; + class PowerSearchBar; +} +QT_END_NAMESPACE + +namespace KTextEditor { + class MovingRange; +} + + +class KATEPARTINTERFACES_EXPORT KateSearchBar : public KateViewBarWidget { + Q_OBJECT + + friend class SearchBarTest; + +public: + enum SearchMode { + // NOTE: Concrete values are important here + // to work with the combobox index! + MODE_PLAIN_TEXT = 0, + MODE_WHOLE_WORDS = 1, + MODE_ESCAPE_SEQUENCES = 2, + MODE_REGEX = 3 + }; + + enum MatchResult { + MatchFound, + MatchWrappedForward, + MatchWrappedBackward, + MatchMismatch, + MatchNothing, + MatchNeutral + }; + + enum SearchDirection { + SearchForward, + SearchBackward + }; + +public: + explicit KateSearchBar(bool initAsPower, KateView* view, KateViewConfig *config); + ~KateSearchBar(); + + virtual void closed(); + + bool isPower() const; + + QString searchPattern() const; + QString replacementPattern() const; + + bool selectionOnly() const; + bool matchCase() const; + + // Only used by KateView + static void nextMatchForSelection(KateView * view, SearchDirection searchDirection); + + void enableUnitTestMode() { m_unitTestMode = true; } + +public Q_SLOTS: + /** + * Set the current search pattern. + * @param searchPattern the search pattern + */ + void setSearchPattern(const QString &searchPattern); + + /** + * Set the current replacement pattern. + * @param replacementPattern the replacement pattern + */ + void setReplacementPattern(const QString &replacementPattern); + + void setSearchMode(SearchMode mode); + void setSelectionOnly(bool selectionOnly); + void setMatchCase(bool matchCase); + + // Called for and + + void findNext(); + void findPrevious(); + void findAll(); + + void replaceNext(); + void replaceAll(); + + // Also used by KateView + void enterPowerMode(); + void enterIncrementalMode(); + + bool clearHighlights(); + void updateHighlightColors(); + + // read write status of document changed + void slotReadWriteChanged (); + +protected: + // Overridden + virtual void showEvent(QShowEvent * event); + +private Q_SLOTS: + void onIncPatternChanged(const QString & pattern); + void onMatchCaseToggled(bool matchCase); + + void onReturnPressed(); + void updateSelectionOnly(); + void updateIncInitCursor(); + + void onPowerPatternChanged(const QString & pattern); + + void onPowerModeChanged(int index); + void onPowerPatternContextMenuRequest(); + void onPowerPatternContextMenuRequest(const QPoint&); + void onPowerReplacmentContextMenuRequest(); + void onPowerReplacmentContextMenuRequest(const QPoint&); + +private: + // Helpers + bool find(SearchDirection searchDirection = SearchForward, const QString * replacement = 0); + int findAll(KTextEditor::Range inputRange, const QString * replacement); + + bool isPatternValid() const; + + KTextEditor::Search::SearchOptions searchOptions(SearchDirection searchDirection = SearchForward) const; + + void highlightMatch(const KTextEditor::Range & range); + void highlightReplacement(const KTextEditor::Range & range); + void indicateMatch(MatchResult matchResult); + static void selectRange(KateView * view, const KTextEditor::Range & range); + void selectRange2(const KTextEditor::Range & range); + + QVector getCapturePatterns(const QString & pattern) const; + void showExtendedContextMenu(bool forPattern, const QPoint& pos); + + void givePatternFeedback(); + void addCurrentTextToHistory(QComboBox * combo); + void backupConfig(bool ofPower); + void sendConfig(); + void fixForSingleLine(KTextEditor::Range & range, SearchDirection searchDirection); + + /** + * If @p visible is true, create a new message with specified parameters and show it + * (if the message was already shown and @p blink is true it will be hidden and shown again). + * If @p visible is false, hide message. + */ + void updateMessage(QPointer& message, bool visible, const QString& text, + KTextEditor::Message::MessageType type, KTextEditor::Message::MessagePosition position, + KTextEditor::Message::AutoHideMode autoHideMode, int durationMs, bool blink); + + void showInfoMessage(const QString& text); + +private: + KateView *const m_view; + KateViewConfig *const m_config; + QList m_hlRanges; + QPointer m_infoMessage; + QPointer m_wrappedTopMessage; + QPointer m_wrappedBottomMessage; + QPointer m_notFoundMessage; + + // Shared by both dialogs + QVBoxLayout *const m_layout; + QWidget * m_widget; + + // Incremental search related + Ui::IncrementalSearchBar * m_incUi; + KTextEditor::Cursor m_incInitCursor; + + // Power search related + Ui::PowerSearchBar * m_powerUi; + + // attribute to highlight matches with + KTextEditor::Attribute::Ptr highlightMatchAttribute; + KTextEditor::Attribute::Ptr highlightReplacementAttribute; + + // Status backup + bool m_incHighlightAll : 1; + bool m_incFromCursor : 1; + bool m_incMatchCase : 1; + bool m_powerMatchCase : 1; + bool m_powerFromCursor : 1; + bool m_powerHighlightAll : 1; + unsigned int m_powerMode : 2; + bool m_unitTestMode :1 ; +}; + + + +#endif // KATE_SEARCH_BAR_H + +// kate: space-indent on; indent-width 4; replace-tabs on; diff --git a/kate/part/search/searchbarincremental.ui b/kate/part/search/searchbarincremental.ui new file mode 100644 index 00000000..a43cc518 --- /dev/null +++ b/kate/part/search/searchbarincremental.ui @@ -0,0 +1,154 @@ + + + Sebastian Pipping + IncrementalSearchBar + + + + 0 + 0 + 100 + 31 + + + + + 0 + 0 + + + + + 0 + + + + + 0 + + + + + F&ind: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + pattern + + + + + + + + 0 + 0 + + + + false + + + Qt::StrongFocus + + + true + + + Text to search for + + + true + + + QComboBox::AdjustToMinimumContentsLengthWithIcon + + + + + + + Jump to next match + + + &Next + + + + + + + + + + + + Jump to previous match + + + &Previous + + + + + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 12 + 26 + + + + + + + + &Match case + + + + + + + Switch to power search and replace bar + + + + + + + + + + + true + + + + + + + + + + KComboBox + QComboBox +
kcombobox.h
+
+
+ + +
diff --git a/kate/part/search/searchbarpower.ui b/kate/part/search/searchbarpower.ui new file mode 100644 index 00000000..0ced1a7a --- /dev/null +++ b/kate/part/search/searchbarpower.ui @@ -0,0 +1,313 @@ + + + Sebastian Pipping + PowerSearchBar + + + + 0 + 0 + 100 + 99 + + + + + 0 + 0 + + + + + 0 + + + + + 0 + + + + + F&ind: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + pattern + + + + + + + + 0 + 0 + + + + Qt::StrongFocus + + + Text to search for + + + true + + + QComboBox::AdjustToMinimumContentsLengthWithIcon + + + + + + + Jump to next match + + + &Next + + + + + + + Jump to previous match + + + &Previous + + + + + + + + + + + + Rep&lace: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + replacement + + + + + + + + 0 + 0 + + + + Text to replace with + + + true + + + QComboBox::AdjustToMinimumContentsLengthWithIcon + + + + + + + Replace next match + + + &Replace + + + + + + + Replace all matches + + + Replace &All + + + + + + + Qt::Horizontal + + + + + + + 6 + + + 6 + + + + + + 0 + 0 + + + + Search mode + + + 4 + + + true + + + + Plain text + + + + + Whole words + + + + + Escape sequences + + + + + Regular expression + + + + + + + + Qt::Horizontal + + + + 0 + 20 + + + + + + + + Case-sensitive searching + + + &Match case + + + + + + + Selection &only + + + + + + + + + Mo&de: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + searchMode + + + + + + + &Find All + + + + + + + + + + + Switch to incremental search bar + + + + + + + + + + + true + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + KPushButton + QPushButton +
kpushbutton.h
+
+ + KComboBox + QComboBox +
kcombobox.h
+
+
+ + pattern + replacement + findNext + findPrev + replaceNext + replaceAll + searchMode + matchCase + selectionOnly + findAll + mutate + + + +
diff --git a/kate/part/spellcheck/ontheflycheck.cpp b/kate/part/spellcheck/ontheflycheck.cpp new file mode 100644 index 00000000..5b70436a --- /dev/null +++ b/kate/part/spellcheck/ontheflycheck.cpp @@ -0,0 +1,932 @@ +/* This file is part of the KDE libraries and the Kate part. + * + * Copyright (C) 2008-2010 by Michel Ludwig + * Copyright (C) 2009 by Joseph Wenninger + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +/* If ever threads should be used again, thread communication and + * synchronization ought to be done with blocking queued signal connections. + */ + +#include "ontheflycheck.h" + +#include + +#include "kateconfig.h" +#include "kateglobal.h" +#include "katerenderer.h" +#include "katebuffer.h" +#include "kateview.h" +#include "spellcheck.h" +#include "spellingmenu.h" + +#define ON_THE_FLY_DEBUG kDebug(debugArea()) + +KateOnTheFlyChecker::KateOnTheFlyChecker(KateDocument *document) +: QObject(document), + m_document(document), + m_backgroundChecker(NULL), + m_currentlyCheckedItem(invalidSpellCheckQueueItem), + m_refreshView(NULL) +{ + ON_THE_FLY_DEBUG << "created"; + + m_viewRefreshTimer = new QTimer(this); + m_viewRefreshTimer->setSingleShot(true); + connect(m_viewRefreshTimer, SIGNAL(timeout()), this, SLOT(viewRefreshTimeout())); + + connect(document, SIGNAL(textInserted(KTextEditor::Document*,KTextEditor::Range)), + this, SLOT(textInserted(KTextEditor::Document*,KTextEditor::Range))); + connect(document, SIGNAL(textRemoved(KTextEditor::Document*,KTextEditor::Range)), + this, SLOT(textRemoved(KTextEditor::Document*,KTextEditor::Range))); + connect(document, SIGNAL(viewCreated(KTextEditor::Document*,KTextEditor::View*)), + this, SLOT(addView(KTextEditor::Document*,KTextEditor::View*))); + connect(document, SIGNAL(highlightingModeChanged(KTextEditor::Document*)), + this, SLOT(updateConfig())); + connect(&document->buffer(), SIGNAL(respellCheckBlock(int,int)), + this, SLOT(handleRespellCheckBlock(int,int))); + + // load the settings for the speller + updateConfig(); + + foreach(KTextEditor::View* view, document->views()) { + addView(document, view); + } + refreshSpellCheck(); +} + +KateOnTheFlyChecker::~KateOnTheFlyChecker() +{ + freeDocument(); +} + +int KateOnTheFlyChecker::debugArea() +{ + static int s_area = KDebug::registerArea("Kate (On-The-Fly Spellcheck)"); + return s_area; +} + +QPair KateOnTheFlyChecker::getMisspelledItem(const KTextEditor::Cursor &cursor) const +{ + foreach(const MisspelledItem &item, m_misspelledList) { + KTextEditor::MovingRange *movingRange = item.first; + if(movingRange->contains(cursor)) { + return QPair(*movingRange, item.second); + } + } + return QPair(KTextEditor::Range::invalid(), QString()); +} + +QString KateOnTheFlyChecker::dictionaryForMisspelledRange(const KTextEditor::Range& range) const +{ + foreach(const MisspelledItem &item, m_misspelledList) { + KTextEditor::MovingRange *movingRange = item.first; + if(*movingRange == range) { + return item.second; + } + } + return QString(); +} + +void KateOnTheFlyChecker::clearMisspellingForWord(const QString& word) +{ + MisspelledList misspelledList = m_misspelledList; // make a copy + foreach(const MisspelledItem &item, misspelledList) { + KTextEditor::MovingRange *movingRange = item.first; + if(m_document->text(*movingRange) == word) { + deleteMovingRange(movingRange); + } + } +} + +const KateOnTheFlyChecker::SpellCheckItem KateOnTheFlyChecker::invalidSpellCheckQueueItem = + SpellCheckItem(NULL, ""); + +void KateOnTheFlyChecker::handleRespellCheckBlock(int start, int end) +{ + ON_THE_FLY_DEBUG << start << end; + KTextEditor::Range range(start, 0, end, m_document->lineLength(end)); + bool listEmpty = m_modificationList.isEmpty(); + KTextEditor::MovingRange *movingRange = m_document->newMovingRange(range); + movingRange->setFeedback(this); + // we don't handle this directly as the highlighting information might not be up-to-date yet + m_modificationList.push_back(ModificationItem(TEXT_INSERTED, movingRange)); + ON_THE_FLY_DEBUG << "added" << *movingRange; + if(listEmpty) { + QTimer::singleShot(0, this, SLOT(handleModifiedRanges())); + } +} + +void KateOnTheFlyChecker::textInserted(KTextEditor::Document *document, const KTextEditor::Range &range) +{ + Q_ASSERT(document == m_document); + Q_UNUSED(document); + if(!range.isValid()) { + return; + } + + bool listEmptyAtStart = m_modificationList.isEmpty(); + + // don't consider a range that is not within the document range + const KTextEditor::Range documentIntersection = m_document->documentRange().intersect(range); + if(!documentIntersection.isValid()) { + return; + } + // for performance reasons we only want to schedule spellchecks for ranges that are visible + foreach(KTextEditor::View* i, m_document->views()) { + KateView *view = static_cast(i); + KTextEditor::Range visibleIntersection = documentIntersection.intersect(view->visibleRange()); + if(visibleIntersection.isValid()) { // allow empty intersections + // we don't handle this directly as the highlighting information might not be up-to-date yet + KTextEditor::MovingRange *movingRange = m_document->newMovingRange(visibleIntersection); + movingRange->setFeedback(this); + m_modificationList.push_back(ModificationItem(TEXT_INSERTED, movingRange)); + ON_THE_FLY_DEBUG << "added" << *movingRange; + } + } + + if(listEmptyAtStart && !m_modificationList.isEmpty()) { + QTimer::singleShot(0, this, SLOT(handleModifiedRanges())); + } +} + +void KateOnTheFlyChecker::handleInsertedText(const KTextEditor::Range &range) +{ + KTextEditor::Range consideredRange = range; + ON_THE_FLY_DEBUG << m_document << range; + + bool spellCheckInProgress = m_currentlyCheckedItem != invalidSpellCheckQueueItem; + + if(spellCheckInProgress) { + KTextEditor::MovingRange *spellCheckRange = m_currentlyCheckedItem.first; + if(spellCheckRange->contains(consideredRange)) { + consideredRange = *spellCheckRange; + stopCurrentSpellCheck(); + deleteMovingRangeQuickly(spellCheckRange); + } + else if(consideredRange.contains(*spellCheckRange)) { + stopCurrentSpellCheck(); + deleteMovingRangeQuickly(spellCheckRange); + } + else if(consideredRange.overlaps(*spellCheckRange)) { + consideredRange.expandToRange(*spellCheckRange); + stopCurrentSpellCheck(); + deleteMovingRangeQuickly(spellCheckRange); + } + else { + spellCheckInProgress = false; + } + } + for(QList::iterator i = m_spellCheckQueue.begin(); + i != m_spellCheckQueue.end();) { + KTextEditor::MovingRange *spellCheckRange = (*i).first; + if(spellCheckRange->contains(consideredRange)) { + consideredRange = *spellCheckRange; + ON_THE_FLY_DEBUG << "erasing range " << *i; + i = m_spellCheckQueue.erase(i); + deleteMovingRangeQuickly(spellCheckRange); + } + else if(consideredRange.contains(*spellCheckRange)) { + ON_THE_FLY_DEBUG << "erasing range " << *i; + i = m_spellCheckQueue.erase(i); + deleteMovingRangeQuickly(spellCheckRange); + } + else if(consideredRange.overlaps(*spellCheckRange)) { + consideredRange.expandToRange(*spellCheckRange); + ON_THE_FLY_DEBUG << "erasing range " << *i; + i = m_spellCheckQueue.erase(i); + deleteMovingRangeQuickly(spellCheckRange); + } + else { + ++i; + } + } + KTextEditor::Range spellCheckRange = findWordBoundaries(consideredRange.start(), + consideredRange.end()); + const bool emptyAtStart = m_spellCheckQueue.isEmpty(); + + queueSpellCheckVisibleRange(spellCheckRange); + + if(spellCheckInProgress || (emptyAtStart && !m_spellCheckQueue.isEmpty())) { + QTimer::singleShot(0, this, SLOT(performSpellCheck())); + } +} + +void KateOnTheFlyChecker::textRemoved(KTextEditor::Document *document, const KTextEditor::Range &range) +{ + Q_ASSERT(document == m_document); + Q_UNUSED(document); + if(!range.isValid()) { + return; + } + + bool listEmptyAtStart = m_modificationList.isEmpty(); + + // don't consider a range that is behind the end of the document + const KTextEditor::Range documentIntersection = m_document->documentRange().intersect(range); + if(!documentIntersection.isValid()) { // the intersection might however be empty if the last + return; // word has been removed, for example + } + + // for performance reasons we only want to schedule spellchecks for ranges that are visible + foreach(KTextEditor::View *i, m_document->views()) { + KateView *view = static_cast(i); + KTextEditor::Range visibleIntersection = documentIntersection.intersect(view->visibleRange()); + if(visibleIntersection.isValid()) { // see above + // we don't handle this directly as the highlighting information might not be up-to-date yet + KTextEditor::MovingRange *movingRange = m_document->newMovingRange(visibleIntersection); + movingRange->setFeedback(this); + m_modificationList.push_back(ModificationItem(TEXT_REMOVED, movingRange)); + ON_THE_FLY_DEBUG << "added" << *movingRange << view->visibleRange(); + } + } + if(listEmptyAtStart && !m_modificationList.isEmpty()) { + QTimer::singleShot(0, this, SLOT(handleModifiedRanges())); + } +} + +inline bool rangesAdjacent(const KTextEditor::Range &r1, const KTextEditor::Range &r2) +{ + return (r1.end() == r2.start()) || (r2.end() == r1.start()); +} + +void KateOnTheFlyChecker::handleRemovedText(const KTextEditor::Range &range) +{ + + ON_THE_FLY_DEBUG << range; + + QList rangesToReCheck; + for(QList::iterator i = m_spellCheckQueue.begin(); + i != m_spellCheckQueue.end();) { + KTextEditor::MovingRange *spellCheckRange = (*i).first; + if(rangesAdjacent(*spellCheckRange, range) || spellCheckRange->contains(range)) { + ON_THE_FLY_DEBUG << "erasing range " << *i; + if(!spellCheckRange->isEmpty()) { + rangesToReCheck.push_back(*spellCheckRange); + } + deleteMovingRangeQuickly(spellCheckRange); + i = m_spellCheckQueue.erase(i); + } + else { + ++i; + } + } + bool spellCheckInProgress = m_currentlyCheckedItem != invalidSpellCheckQueueItem; + const bool emptyAtStart = m_spellCheckQueue.isEmpty(); + if(spellCheckInProgress) { + KTextEditor::MovingRange *spellCheckRange = m_currentlyCheckedItem.first; + ON_THE_FLY_DEBUG << *spellCheckRange; + if(m_document->documentRange().contains(*spellCheckRange) + && (rangesAdjacent(*spellCheckRange, range) || spellCheckRange->contains(range)) + && !spellCheckRange->isEmpty()) { + rangesToReCheck.push_back(*spellCheckRange); + ON_THE_FLY_DEBUG << "added the range " << *spellCheckRange; + stopCurrentSpellCheck(); + deleteMovingRangeQuickly(spellCheckRange); + } + else if(spellCheckRange->isEmpty()) { + stopCurrentSpellCheck(); + deleteMovingRangeQuickly(spellCheckRange); + } + else { + spellCheckInProgress = false; + } + } + for(QList::iterator i = rangesToReCheck.begin(); i != rangesToReCheck.end(); ++i) { + queueSpellCheckVisibleRange(*i); + } + + KTextEditor::Range spellCheckRange = findWordBoundaries(range.start(), range.start()); + KTextEditor::Cursor spellCheckEnd = spellCheckRange.end(); + + queueSpellCheckVisibleRange(spellCheckRange); + + if(range.numberOfLines() > 0) { + //FIXME: there is no currently no way of doing this better as we only get notifications for removals of + // of single lines, i.e. we don't know here how many lines have been removed in total + KTextEditor::Cursor nextLineStart(spellCheckEnd.line() + 1, 0); + const KTextEditor::Cursor documentEnd = m_document->documentEnd(); + if(nextLineStart < documentEnd) { + KTextEditor::Range rangeBelow = KTextEditor::Range(nextLineStart, documentEnd); + + const QList& viewList = m_document->views(); + for(QList::const_iterator i = viewList.begin(); i != viewList.end(); ++i) { + KateView *view = static_cast(*i); + const KTextEditor::Range visibleRange = view->visibleRange(); + KTextEditor::Range intersection = visibleRange.intersect(rangeBelow); + if(intersection.isValid()) { + queueSpellCheckVisibleRange(view, intersection); + } + } + } + } + + ON_THE_FLY_DEBUG << "finished"; + if(spellCheckInProgress || (emptyAtStart && !m_spellCheckQueue.isEmpty())) { + QTimer::singleShot(0, this, SLOT(performSpellCheck())); + } +} + +void KateOnTheFlyChecker::freeDocument() +{ + ON_THE_FLY_DEBUG; + + // empty the spell check queue + for(QList::iterator i = m_spellCheckQueue.begin(); + i != m_spellCheckQueue.end();) { + ON_THE_FLY_DEBUG << "erasing range " << *i; + KTextEditor::MovingRange *movingRange = (*i).first; + deleteMovingRangeQuickly(movingRange); + i = m_spellCheckQueue.erase(i); + } + if(m_currentlyCheckedItem != invalidSpellCheckQueueItem) { + KTextEditor::MovingRange *movingRange = m_currentlyCheckedItem.first; + deleteMovingRangeQuickly(movingRange); + } + stopCurrentSpellCheck(); + + MisspelledList misspelledList = m_misspelledList; // make a copy! + foreach(const MisspelledItem &i, misspelledList) { + deleteMovingRange(i.first); + } + m_misspelledList.clear(); + clearModificationList(); +} + +void KateOnTheFlyChecker::performSpellCheck() +{ + if(m_currentlyCheckedItem != invalidSpellCheckQueueItem) { + ON_THE_FLY_DEBUG << "exited as a check is currently in progress"; + return; + } + if(m_spellCheckQueue.isEmpty()) { + ON_THE_FLY_DEBUG << "exited as there is nothing to do"; + return; + } + m_currentlyCheckedItem = m_spellCheckQueue.takeFirst(); + + KTextEditor::MovingRange *spellCheckRange = m_currentlyCheckedItem.first; + const QString& language = m_currentlyCheckedItem.second; + ON_THE_FLY_DEBUG << "for the range " << *spellCheckRange; + // clear all the highlights that are currently present in the range that + // is supposed to be checked + const MovingRangeList highlightsList = installedMovingRanges(*spellCheckRange); // make a copy! + deleteMovingRanges(highlightsList); + + m_currentDecToEncOffsetList.clear(); + KateDocument::OffsetList encToDecOffsetList; + QString text = m_document->decodeCharacters(*spellCheckRange, + m_currentDecToEncOffsetList, + encToDecOffsetList); + ON_THE_FLY_DEBUG << "next spell checking" << text; + if(text.isEmpty()) { // passing an empty string to Sonnet can lead to a bad allocation exception + spellCheckDone(); // (bug 225867) + return; + } + if(m_speller.language() != language) { + m_speller.setLanguage(language); + } + if(!m_backgroundChecker) { + m_backgroundChecker = new Sonnet::BackgroundChecker(m_speller, this); + connect(m_backgroundChecker, + SIGNAL(misspelling(QString,int)), + this, + SLOT(misspelling(QString,int))); + connect(m_backgroundChecker, SIGNAL(done()), this, SLOT(spellCheckDone())); + + m_backgroundChecker->restore(KGlobal::config().data()); + } + m_backgroundChecker->setSpeller(m_speller); + m_backgroundChecker->setText(text); // don't call 'start()' after this! +} + +void KateOnTheFlyChecker::removeRangeFromEverything(KTextEditor::MovingRange *movingRange) +{ + Q_ASSERT(m_document == movingRange->document()); + ON_THE_FLY_DEBUG << *movingRange << "(" << movingRange << ")"; + + if(removeRangeFromModificationList(movingRange)) { + return; // range was part of the modification queue, so we don't have + // to look further for it + } + + if(removeRangeFromSpellCheckQueue(movingRange)) { + return; // range was part of the spell check queue, so it cannot have been + // a misspelled range + } + + for(MisspelledList::iterator i = m_misspelledList.begin(); i != m_misspelledList.end();) { + if((*i).first == movingRange) { + i = m_misspelledList.erase(i); + } + else { + ++i; + } + } +} + +bool KateOnTheFlyChecker::removeRangeFromCurrentSpellCheck(KTextEditor::MovingRange *range) +{ + if(m_currentlyCheckedItem != invalidSpellCheckQueueItem + && m_currentlyCheckedItem.first == range) { + stopCurrentSpellCheck(); + return true; + } + return false; +} + +void KateOnTheFlyChecker::stopCurrentSpellCheck() +{ + m_currentDecToEncOffsetList.clear(); + m_currentlyCheckedItem = invalidSpellCheckQueueItem; + if(m_backgroundChecker) { + m_backgroundChecker->stop(); + } +} + +bool KateOnTheFlyChecker::removeRangeFromSpellCheckQueue(KTextEditor::MovingRange *range) +{ + if(removeRangeFromCurrentSpellCheck(range)) { + if(!m_spellCheckQueue.isEmpty()) { + QTimer::singleShot(0, this, SLOT(performSpellCheck())); + } + return true; + } + bool found = false; + for(QList::iterator i = m_spellCheckQueue.begin(); + i != m_spellCheckQueue.end();) { + if((*i).first == range) { + i = m_spellCheckQueue.erase(i); + found = true; + } + else { + ++i; + } + } + return found; +} + +void KateOnTheFlyChecker::rangeEmpty(KTextEditor::MovingRange *range) +{ + ON_THE_FLY_DEBUG << range->start() << range->end() << "(" << range << ")"; + deleteMovingRange (range); +} + +void KateOnTheFlyChecker::rangeInvalid (KTextEditor::MovingRange* range) +{ + ON_THE_FLY_DEBUG << range->start() << range->end() << "(" << range << ")"; + deleteMovingRange (range); +} + +void KateOnTheFlyChecker::mouseEnteredRange(KTextEditor::MovingRange *range, KTextEditor::View *view) +{ + KateView *kateView = static_cast(view); + kateView->spellingMenu()->mouseEnteredMisspelledRange(range); +} + +void KateOnTheFlyChecker::mouseExitedRange(KTextEditor::MovingRange *range, KTextEditor::View *view) +{ + KateView *kateView = static_cast(view); + kateView->spellingMenu()->mouseExitedMisspelledRange(range); +} + +/** + * It is not enough to use 'caret/Entered/ExitedRange' only as the cursor doesn't move when some + * text has been selected. + **/ +void KateOnTheFlyChecker::caretEnteredRange(KTextEditor::MovingRange *range, KTextEditor::View *view) +{ + KateView *kateView = static_cast(view); + kateView->spellingMenu()->caretEnteredMisspelledRange(range); +} + +void KateOnTheFlyChecker::caretExitedRange(KTextEditor::MovingRange *range, KTextEditor::View *view) +{ + KateView *kateView = static_cast(view); + kateView->spellingMenu()->caretExitedMisspelledRange(range); +} + +void KateOnTheFlyChecker::deleteMovingRange(KTextEditor::MovingRange *range) +{ + ON_THE_FLY_DEBUG << range; + // remove it from all our structures + removeRangeFromEverything(range); + range->setFeedback(NULL); + foreach(KTextEditor::View *view, m_document->views()) { + static_cast(view)->spellingMenu()->rangeDeleted(range); + } + delete(range); +} + +void KateOnTheFlyChecker::deleteMovingRanges(const QList& list) +{ + foreach(KTextEditor::MovingRange *r, list) { + deleteMovingRange(r); + } +} + +KTextEditor::Range KateOnTheFlyChecker::findWordBoundaries(const KTextEditor::Cursor& begin, + const KTextEditor::Cursor& end) +{ + // FIXME: QTextBoundaryFinder should be ideally used for this, but it is currently + // still broken in Qt + const QRegExp boundaryRegExp("\\b"); + const QRegExp boundaryQuoteRegExp("\\b\\w+'\\w*$"); // handle spell checking of "isn't", "doesn't", etc. + const QRegExp extendedBoundaryRegExp("(\\W|$)"); + const QRegExp extendedBoundaryQuoteRegExp("^\\w*'\\w+\\b"); // see above + KateDocument::OffsetList decToEncOffsetList, encToDecOffsetList; + const int startLine = begin.line(); + const int startColumn = begin.column(); + KTextEditor::Cursor boundaryStart, boundaryEnd; + // first we take care of the start position + const KTextEditor::Range startLineRange(startLine, 0, startLine, m_document->lineLength(startLine)); + QString decodedLineText = m_document->decodeCharacters(startLineRange, + decToEncOffsetList, + encToDecOffsetList); + int translatedColumn = m_document->computePositionWrtOffsets(encToDecOffsetList, + startColumn); + QString text = decodedLineText.mid(0, translatedColumn); + boundaryStart.setLine(startLine); + int match = text.lastIndexOf(boundaryQuoteRegExp); + if(match < 0) { + match = text.lastIndexOf(boundaryRegExp); + } + boundaryStart.setColumn(m_document->computePositionWrtOffsets(decToEncOffsetList, qMax(0, match))); + // and now the end position + const int endLine = end.line(); + const int endColumn = end.column(); + if(endLine != startLine) { + decToEncOffsetList.clear(); + encToDecOffsetList.clear(); + const KTextEditor::Range endLineRange(endLine, 0, endLine, m_document->lineLength(endLine)); + decodedLineText = m_document->decodeCharacters(endLineRange, + decToEncOffsetList, + encToDecOffsetList); + } + translatedColumn = m_document->computePositionWrtOffsets(encToDecOffsetList, + endColumn); + text = decodedLineText.mid(translatedColumn); + boundaryEnd.setLine(endLine); + match = extendedBoundaryQuoteRegExp.indexIn(text); + if(match == 0) { + match = extendedBoundaryQuoteRegExp.matchedLength(); + } + else { + match = extendedBoundaryRegExp.indexIn(text); + } + boundaryEnd.setColumn(m_document->computePositionWrtOffsets(decToEncOffsetList, + translatedColumn + qMax(0, match))); + return KTextEditor::Range(boundaryStart, boundaryEnd); +} + +void KateOnTheFlyChecker::misspelling(const QString &word, int start) +{ + if(m_currentlyCheckedItem == invalidSpellCheckQueueItem) { + ON_THE_FLY_DEBUG << "exited as no spell check is taking place"; + return; + } + int translatedStart = m_document->computePositionWrtOffsets(m_currentDecToEncOffsetList, + start); +// ON_THE_FLY_DEBUG << "misspelled " << word +// << " at line " +// << *m_currentlyCheckedItem.first +// << " column " << start; + + KTextEditor::MovingRange *spellCheckRange = m_currentlyCheckedItem.first; + int line = spellCheckRange->start().line(); + int rangeStart = spellCheckRange->start().column(); + int translatedEnd = m_document->computePositionWrtOffsets(m_currentDecToEncOffsetList, + start + word.length()); + + KTextEditor::MovingRange *movingRange = + m_document->newMovingRange(KTextEditor::Range(line, + rangeStart + translatedStart, + line, + rangeStart + translatedEnd)); + movingRange->setFeedback(this); + KTextEditor::Attribute *attribute = new KTextEditor::Attribute(); + attribute->setUnderlineStyle(QTextCharFormat::SpellCheckUnderline); + attribute->setUnderlineColor(KateRendererConfig::global()->spellingMistakeLineColor()); + + // don't print this range + movingRange->setAttributeOnlyForViews (true); + + movingRange->setAttribute(KTextEditor::Attribute::Ptr(attribute)); + m_misspelledList.push_back(MisspelledItem(movingRange, m_currentlyCheckedItem.second)); + + if(m_backgroundChecker) { + m_backgroundChecker->continueChecking(); + } +} + +void KateOnTheFlyChecker::spellCheckDone() +{ + ON_THE_FLY_DEBUG << "on-the-fly spell check done, queue length " << m_spellCheckQueue.size(); + if(m_currentlyCheckedItem == invalidSpellCheckQueueItem) { + return; + } + KTextEditor::MovingRange *movingRange = m_currentlyCheckedItem.first; + stopCurrentSpellCheck(); + deleteMovingRangeQuickly(movingRange); + + if(!m_spellCheckQueue.empty()) { + QTimer::singleShot(0, this, SLOT(performSpellCheck())); + } +} + +QList KateOnTheFlyChecker::installedMovingRanges(const KTextEditor::Range& range) +{ + ON_THE_FLY_DEBUG << range; + MovingRangeList toReturn; + + for(QList::iterator i = m_misspelledList.begin(); + i != m_misspelledList.end(); ++i) { + KTextEditor::MovingRange *movingRange = (*i).first; + if(movingRange->overlaps(range)) { + toReturn.push_back(movingRange); + } + } + return toReturn; +} + +void KateOnTheFlyChecker::updateConfig() +{ + ON_THE_FLY_DEBUG; + m_speller.restore(KGlobal::config().data()); + + if(m_backgroundChecker) { + m_backgroundChecker->restore(KGlobal::config().data()); + } +} + +void KateOnTheFlyChecker::refreshSpellCheck(const KTextEditor::Range &range) +{ + if(range.isValid()) { + textInserted(m_document, range); + } + else { + freeDocument(); + textInserted(m_document, m_document->documentRange()); + } +} + +void KateOnTheFlyChecker::addView(KTextEditor::Document *document, KTextEditor::View *view) +{ + Q_ASSERT(document == m_document); + Q_UNUSED(document); + ON_THE_FLY_DEBUG; + connect(view, SIGNAL(destroyed(QObject*)), this, SLOT(viewDestroyed(QObject*))); + connect(view, SIGNAL(displayRangeChanged(KateView*)), this, SLOT(restartViewRefreshTimer(KateView*))); + updateInstalledMovingRanges(static_cast(view)); +} + +void KateOnTheFlyChecker::viewDestroyed(QObject* obj) +{ + ON_THE_FLY_DEBUG; + KTextEditor::View *view = static_cast(obj); + m_displayRangeMap.remove(view); +} + +void KateOnTheFlyChecker::removeView(KTextEditor::View *view) +{ + ON_THE_FLY_DEBUG; + m_displayRangeMap.remove(view); +} + +void KateOnTheFlyChecker::updateInstalledMovingRanges(KateView *view) +{ + Q_ASSERT(m_document == view->document()); + ON_THE_FLY_DEBUG; + KTextEditor::Range oldDisplayRange = m_displayRangeMap[view]; + + KTextEditor::Range newDisplayRange = view->visibleRange(); + ON_THE_FLY_DEBUG << "new range: " << newDisplayRange; + ON_THE_FLY_DEBUG << "old range: " << oldDisplayRange; + QList toDelete; + foreach(const MisspelledItem &item, m_misspelledList) { + KTextEditor::MovingRange *movingRange = item.first; + if(!movingRange->overlaps(newDisplayRange)) { + bool stillVisible = false; + foreach(KTextEditor::View *it2, m_document->views()) { + KateView *view2 = static_cast(it2); + if(view != view2 && movingRange->overlaps(view2->visibleRange())) { + stillVisible = true; + break; + } + } + if(!stillVisible) { + toDelete.push_back(movingRange); + } + } + } + deleteMovingRanges (toDelete); + m_displayRangeMap[view] = newDisplayRange; + if(oldDisplayRange.isValid()) { + bool emptyAtStart = m_spellCheckQueue.empty(); + for(int line = newDisplayRange.end().line(); line >= newDisplayRange.start().line(); --line) { + if(!oldDisplayRange.containsLine(line)) { + bool visible = false; + foreach(KTextEditor::View *it2, m_document->views()) { + KateView *view2 = static_cast(it2); + if(view != view2 && view2->visibleRange().containsLine(line)) { + visible = true; + break; + } + } + if(!visible) { + queueLineSpellCheck(m_document, line); + } + } + } + if(emptyAtStart && !m_spellCheckQueue.isEmpty()) { + QTimer::singleShot(0, this, SLOT(performSpellCheck())); + } + } +} + +void KateOnTheFlyChecker::queueSpellCheckVisibleRange(const KTextEditor::Range& range) +{ + const QList& viewList = m_document->views(); + for(QList::const_iterator i = viewList.begin(); i != viewList.end(); ++i) { + queueSpellCheckVisibleRange(static_cast(*i), range); + } +} + +void KateOnTheFlyChecker::queueSpellCheckVisibleRange(KateView *view, const KTextEditor::Range& range) +{ + Q_ASSERT(m_document == view->doc()); + KTextEditor::Range visibleRange = view->visibleRange(); + KTextEditor::Range intersection = visibleRange.intersect(range); + if(intersection.isEmpty()) { + return; + } + + // clear all the highlights that are currently present in the range that + // is supposed to be checked, necessary due to highlighting + const MovingRangeList highlightsList = installedMovingRanges(intersection); + deleteMovingRanges(highlightsList); + + QList > spellCheckRanges + = KateGlobal::self()->spellCheckManager()->spellCheckRanges(m_document, + intersection, + true); + //we queue them up in reverse + QListIterator > i(spellCheckRanges); + i.toBack(); + while(i.hasPrevious()) { + QPair p = i.previous(); + queueLineSpellCheck(p.first, p.second); + } +} + +void KateOnTheFlyChecker::queueLineSpellCheck(KateDocument *kateDocument, int line) +{ + const KTextEditor::Range range = KTextEditor::Range(line, 0, line, kateDocument->lineLength(line)); + // clear all the highlights that are currently present in the range that + // is supposed to be checked, necessary due to highlighting + + const MovingRangeList highlightsList = installedMovingRanges(range); + deleteMovingRanges(highlightsList); + + QList > spellCheckRanges + = KateGlobal::self()->spellCheckManager()->spellCheckRanges(kateDocument, + range, + true); + //we queue them up in reverse + QListIterator > i(spellCheckRanges); + i.toBack(); + while(i.hasPrevious()) { + QPair p = i.previous(); + queueLineSpellCheck(p.first, p.second); + } +} + +void KateOnTheFlyChecker::queueLineSpellCheck(const KTextEditor::Range& range, const QString& dictionary) +{ + ON_THE_FLY_DEBUG << m_document << range; + + Q_ASSERT(range.onSingleLine()); + + if(range.isEmpty()) { + return; + } + + addToSpellCheckQueue(range, dictionary); +} + +void KateOnTheFlyChecker::addToSpellCheckQueue(const KTextEditor::Range& range, const QString& dictionary) +{ + addToSpellCheckQueue(m_document->newMovingRange(range), dictionary); +} + +void KateOnTheFlyChecker::addToSpellCheckQueue(KTextEditor::MovingRange *range, const QString& dictionary) +{ + ON_THE_FLY_DEBUG << m_document << *range << dictionary; + + range->setFeedback(this); + + // if the queue contains a subrange of 'range', we remove that one + for(QList::iterator i = m_spellCheckQueue.begin(); + i != m_spellCheckQueue.end();) { + KTextEditor::MovingRange *spellCheckRange = (*i).first; + if(range->contains(*spellCheckRange)) { + deleteMovingRangeQuickly(spellCheckRange); + i = m_spellCheckQueue.erase(i); + } + else { + ++i; + } + } + // leave 'push_front' here as it is a LIFO queue, i.e. a stack + m_spellCheckQueue.push_front(SpellCheckItem(range, dictionary)); + ON_THE_FLY_DEBUG << "added" + << *range << dictionary + << "to the queue, which has a length of" << m_spellCheckQueue.size(); +} + +void KateOnTheFlyChecker::viewRefreshTimeout() +{ + if(m_refreshView) { + updateInstalledMovingRanges(m_refreshView); + } + m_refreshView = NULL; +} + +void KateOnTheFlyChecker::restartViewRefreshTimer(KateView *view) +{ + if(m_refreshView && view != m_refreshView) { // a new view should be refreshed + updateInstalledMovingRanges(m_refreshView); // so refresh the old one first + } + m_refreshView = view; + m_viewRefreshTimer->start(100); +} + +void KateOnTheFlyChecker::deleteMovingRangeQuickly(KTextEditor::MovingRange *range) +{ + range->setFeedback(NULL); + foreach(KTextEditor::View *view, m_document->views()) { + static_cast(view)->spellingMenu()->rangeDeleted(range); + } + delete(range); +} + +void KateOnTheFlyChecker::handleModifiedRanges() +{ + foreach(const ModificationItem &item, m_modificationList) { + KTextEditor::MovingRange *movingRange = item.second; + KTextEditor::Range range = *movingRange; + deleteMovingRangeQuickly(movingRange); + if(item.first == TEXT_INSERTED) { + handleInsertedText(range); + } + else { + handleRemovedText(range); + } + } + m_modificationList.clear(); +} + +bool KateOnTheFlyChecker::removeRangeFromModificationList(KTextEditor::MovingRange *range) +{ + bool found = false; + for(ModificationList::iterator i = m_modificationList.begin(); i != m_modificationList.end();) { + ModificationItem item = *i; + KTextEditor::MovingRange *movingRange = item.second; + if(movingRange == range) { + found = true; + i = m_modificationList.erase(i); + } + else { + ++i; + } + } + return found; +} + +void KateOnTheFlyChecker::clearModificationList() +{ + foreach(const ModificationItem &item, m_modificationList) { + KTextEditor::MovingRange *movingRange = item.second; + deleteMovingRangeQuickly(movingRange); + } + m_modificationList.clear(); +} + +#include "moc_ontheflycheck.cpp" + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/spellcheck/ontheflycheck.h b/kate/part/spellcheck/ontheflycheck.h new file mode 100644 index 00000000..4a7ff4e8 --- /dev/null +++ b/kate/part/spellcheck/ontheflycheck.h @@ -0,0 +1,144 @@ +/* This file is part of the KDE libraries and the Kate part. + * + * Copyright (C) 2008-2010 by Michel Ludwig + * Copyright (C) 2009 by Joseph Wenninger + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef ONTHEFLYCHECK_H +#define ONTHEFLYCHECK_H + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "katedocument.h" + +namespace Sonnet { + class BackgroundChecker; +} + +class KateOnTheFlyChecker : public QObject, private KTextEditor::MovingRangeFeedback { + Q_OBJECT + + enum ModificationType {TEXT_INSERTED = 0, TEXT_REMOVED}; + + typedef QPair SpellCheckItem; + typedef QList MovingRangeList; + typedef QPair MisspelledItem; + typedef QList MisspelledList; + + typedef QPair ModificationItem; + typedef QList ModificationList; + + public: + KateOnTheFlyChecker(KateDocument *document); + ~KateOnTheFlyChecker(); + + static int debugArea(); + + QPair getMisspelledItem(const KTextEditor::Cursor &cursor) const; + QString dictionaryForMisspelledRange(const KTextEditor::Range& range) const; + + void clearMisspellingForWord(const QString& word); + + public Q_SLOTS: + void textInserted(KTextEditor::Document *document, const KTextEditor::Range &range); + void textRemoved(KTextEditor::Document *document, const KTextEditor::Range &range); + + void updateConfig(); + void refreshSpellCheck(const KTextEditor::Range &range = KTextEditor::Range::invalid()); + + void updateInstalledMovingRanges(KateView *view); + + protected: + KateDocument *const m_document; + Sonnet::Speller m_speller; + QList m_spellCheckQueue; + Sonnet::BackgroundChecker *m_backgroundChecker; + SpellCheckItem m_currentlyCheckedItem; + static const SpellCheckItem invalidSpellCheckQueueItem; + MisspelledList m_misspelledList; + ModificationList m_modificationList; + KateDocument::OffsetList m_currentDecToEncOffsetList; + QMap m_displayRangeMap; + + void freeDocument(); + + MovingRangeList installedMovingRanges(const KTextEditor::Range& range); + + void queueLineSpellCheck(KateDocument *document, int line); + /** + * 'range' must be on a single line + **/ + void queueLineSpellCheck(const KTextEditor::Range& range, const QString& dictionary); + void queueSpellCheckVisibleRange(const KTextEditor::Range& range); + void queueSpellCheckVisibleRange(KateView *view, const KTextEditor::Range& range); + + void addToSpellCheckQueue(const KTextEditor::Range& range, const QString& dictionary); + void addToSpellCheckQueue(KTextEditor::MovingRange *range, const QString& dictionary); + + QTimer *m_viewRefreshTimer; + QPointer m_refreshView; + + virtual void removeRangeFromEverything(KTextEditor::MovingRange *range); + bool removeRangeFromCurrentSpellCheck(KTextEditor::MovingRange *range); + bool removeRangeFromSpellCheckQueue(KTextEditor::MovingRange *range); + virtual void rangeEmpty(KTextEditor::MovingRange *range); + virtual void rangeInvalid (KTextEditor::MovingRange* range); + virtual void mouseEnteredRange(KTextEditor::MovingRange *range, KTextEditor::View *view); + virtual void mouseExitedRange(KTextEditor::MovingRange *range, KTextEditor::View *view); + virtual void caretEnteredRange(KTextEditor::MovingRange *range, KTextEditor::View *view); + virtual void caretExitedRange(KTextEditor::MovingRange *range, KTextEditor::View *view); + + KTextEditor::Range findWordBoundaries(const KTextEditor::Cursor& begin, + const KTextEditor::Cursor& end); + + void deleteMovingRange(KTextEditor::MovingRange *range); + void deleteMovingRanges(const QList& list); + void deleteMovingRangeQuickly(KTextEditor::MovingRange *range); + void stopCurrentSpellCheck(); + + protected Q_SLOTS: + void performSpellCheck(); + void misspelling(const QString &word, int start); + void spellCheckDone(); + + void viewDestroyed(QObject* obj); + void addView(KTextEditor::Document *document, KTextEditor::View *view); + void removeView(KTextEditor::View *view); + + void restartViewRefreshTimer(KateView *view); + void viewRefreshTimeout(); + + void handleModifiedRanges(); + void handleInsertedText(const KTextEditor::Range &range); + void handleRemovedText(const KTextEditor::Range &range); + void handleRespellCheckBlock(int start, int end); + bool removeRangeFromModificationList(KTextEditor::MovingRange *range); + void clearModificationList(); +}; + +#endif + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/spellcheck/prefixstore.cpp b/kate/part/spellcheck/prefixstore.cpp new file mode 100644 index 00000000..32528905 --- /dev/null +++ b/kate/part/spellcheck/prefixstore.cpp @@ -0,0 +1,190 @@ +/* This file is part of the KDE libraries and the Kate part. + * + * Copyright (C) 2009 by Michel Ludwig + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "prefixstore.h" + +#include + +#include "katetextline.h" + +KatePrefixStore::KatePrefixStore() +: m_longestPrefixLength(0), + m_lastAssignedState(0) +{ +} + +KatePrefixStore::~KatePrefixStore() +{ +} + +void KatePrefixStore::addPrefix(const QString& prefix) +{ + if(prefix.isEmpty()) { + return; + } + if(m_prefixSet.contains(prefix)) { + return; + } + unsigned long long state = 0; + for(int i = 0; i < prefix.length(); ++i) { + QChar c = prefix.at(i); + + CharToOccurrenceStateHash& hash = m_transitionFunction[state]; + CharToOccurrenceStateHash::iterator it = hash.find(c.unicode()); + if(it == hash.end()) { + state = nextFreeState(); + hash[c.unicode()] = QPair(1, state); + continue; + } + + ++(*it).first; + state = (*it).second; + } + // add the last state as accepting state + m_acceptingStates.insert(state); + + m_prefixSet.insert(prefix); + + if(prefix.length() > m_longestPrefixLength) { + m_longestPrefixLength = prefix.length(); + } +} + +void KatePrefixStore::removePrefix(const QString& prefix) +{ + if(prefix.isEmpty()) { + return; + } + if(!m_prefixSet.contains(prefix)) { + return; + } + m_prefixSet.remove(prefix); + + unsigned long long state = 0; + for(int i = 0; i < prefix.length(); ++i) { + QChar c = prefix.at(i); + + CharToOccurrenceStateHash& hash = m_transitionFunction[state]; + CharToOccurrenceStateHash::iterator it = hash.find(c.unicode()); + if(it == hash.end()) { + continue; + } + + state = (*it).second; + if(m_acceptingStates.contains(state) && i == prefix.length() - 1) { + m_acceptingStates.remove(state); + } + + if((*it).first <= 1) { + hash.erase(it); + m_stateFreeList.push_back(state); + } + else { + --(*it).first; + } + } + + if(prefix.length() == m_longestPrefixLength) { + m_longestPrefixLength = computeLongestPrefixLength(); + } +} + +void KatePrefixStore::dump() +{ + for(unsigned long long i = 0; i < m_lastAssignedState; ++i) { + CharToOccurrenceStateHash& hash = m_transitionFunction[i]; + for(CharToOccurrenceStateHash::iterator it = hash.begin(); it != hash.end(); ++it) { + kDebug() << i << "x" << QChar(it.key()) << "->" << it.value().first << "x" << it.value().second; + } + } + kDebug() << "Accepting states" << m_acceptingStates; +} + +QString KatePrefixStore::findPrefix(const QString& s, int start) const +{ + unsigned long long state = 0; + for(int i = start; i < s.length(); ++i) { + QChar c = s.at(i); + const CharToOccurrenceStateHash& hash = m_transitionFunction[state]; + CharToOccurrenceStateHash::const_iterator it = hash.find(c.unicode()); + if(it == hash.end()) { + return QString(); + } + + state = (*it).second; + if(m_acceptingStates.contains(state)) { + return s.mid(start, i + 1 - start); + } + } + return QString(); +} + +QString KatePrefixStore::findPrefix(const Kate::TextLine& line, int start) const +{ + unsigned long long state = 0; + for(int i = start; i < line->length(); ++i) { + QChar c = line->at(i); + const CharToOccurrenceStateHash& hash = m_transitionFunction[state]; + CharToOccurrenceStateHash::const_iterator it = hash.find(c.unicode()); + if(it == hash.end()) { + return QString(); + } + + state = (*it).second; + if(m_acceptingStates.contains(state)) { + return line->string(start, i + 1 - start); + } + } + return QString(); +} + +int KatePrefixStore::longestPrefixLength() const +{ + return m_longestPrefixLength; +} + +void KatePrefixStore::clear() +{ + m_longestPrefixLength = 0; + m_prefixSet.clear(); + m_transitionFunction.clear(); + m_acceptingStates.clear(); + m_stateFreeList.clear(); + m_lastAssignedState = 0; +} + +int KatePrefixStore::computeLongestPrefixLength() +{ + int toReturn = 0; + for(QSet::iterator i = m_prefixSet.begin(); + i != m_prefixSet.end(); ++i) { + kDebug() << "length" << (*i).length(); + toReturn = qMax(toReturn, (*i).length()); + } + return toReturn; +} + +unsigned long long KatePrefixStore::nextFreeState() +{ + if(!m_stateFreeList.isEmpty()) { + return m_stateFreeList.takeFirst(); + } + return ++m_lastAssignedState; +} diff --git a/kate/part/spellcheck/prefixstore.h b/kate/part/spellcheck/prefixstore.h new file mode 100644 index 00000000..465e3a80 --- /dev/null +++ b/kate/part/spellcheck/prefixstore.h @@ -0,0 +1,88 @@ +/* This file is part of the KDE libraries and the Kate part. + * + * Copyright (C) 2008-2009 by Michel Ludwig + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef PREFIXSTORE_H +#define PREFIXSTORE_H + +#include +#include +#include +#include +#include +#include + +#include "katetextline.h" + +/** + * This class can be used to efficiently search for occurrences of strings in + * a given string. Theoretically speaking, a finite deterministic automaton is + * constructed which exactly accepts the strings that are to be recognized. In + * order to check whether a given string contains one of the strings that are being + * searched for the constructed automaton has to applied on each position in the + * given string. + **/ +class KatePrefixStore { + public: + typedef QPair BooleanPair; + + KatePrefixStore(); + virtual ~KatePrefixStore(); + + void addPrefix(const QString& prefix); + void removePrefix(const QString& prefix); + + /** + * Returns the shortest prefix of the given string that is contained in + * this prefix store starting at position 'start'. + **/ + QString findPrefix(const QString& s, int start = 0) const; + + /** + * Returns the shortest prefix of the given string that is contained in + * this prefix store starting at position 'start'. + **/ + QString findPrefix(const Kate::TextLine& line, int start = 0) const; + + int longestPrefixLength() const; + + void clear(); + + void dump(); + + protected: + int m_longestPrefixLength; + QSet m_prefixSet; + + // State x Char -> Nr. of char occurrences in prefixes x State + typedef QHash > CharToOccurrenceStateHash; + typedef QHash TransitionFunction; + TransitionFunction m_transitionFunction; + QSet m_acceptingStates; + QList m_stateFreeList; + unsigned long long m_lastAssignedState; + + int computeLongestPrefixLength(); + unsigned long long nextFreeState(); +// bool containsPrefixOfLengthEndingWith(int length, const QChar& c); +}; + +#endif + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/spellcheck/spellcheck.cpp b/kate/part/spellcheck/spellcheck.cpp new file mode 100644 index 00000000..b781948f --- /dev/null +++ b/kate/part/spellcheck/spellcheck.cpp @@ -0,0 +1,300 @@ +/* This file is part of the KDE libraries and the Kate part. + * + * Copyright (C) 2009 by Michel Ludwig + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +/* If ever threads should be used again, thread communication and + * synchronization ought to be done with blocking queued signal connections. + */ + +#include "spellcheck.h" + +#include +#include +#include + +#include +#include +#include + +#include "katedocument.h" +#include "katehighlight.h" + +KateSpellCheckManager::KateSpellCheckManager(QObject *parent) +: QObject(parent) +{ +} + +KateSpellCheckManager::~KateSpellCheckManager() +{ +} + +QStringList KateSpellCheckManager::suggestions(const QString& word, const QString& dictionary) +{ + Sonnet::Speller speller; + speller.setLanguage(dictionary); + return speller.suggest(word); +} + +void KateSpellCheckManager::ignoreWord(const QString& word, const QString& dictionary) +{ + Sonnet::Speller speller; + speller.setLanguage(dictionary); + speller.addToSession(word); +} + +void KateSpellCheckManager::addToDictionary(const QString& word, const QString& dictionary) +{ + Sonnet::Speller speller; + speller.setLanguage(dictionary); + speller.addToPersonal(word); +} + +QList KateSpellCheckManager::rangeDifference(const KTextEditor::Range& r1, + const KTextEditor::Range& r2) +{ + Q_ASSERT(r1.contains(r2)); + QList toReturn; + KTextEditor::Range before(r1.start(), r2.start()); + KTextEditor::Range after(r2.end(), r1.end()); + if(!before.isEmpty()) { + toReturn.push_back(before); + } + if(!after.isEmpty()) { + toReturn.push_back(after); + } + return toReturn; +} + +namespace { + bool lessThanRangeDictionaryPair(const QPair &s1, + const QPair &s2) + { + return s1.first.end() <= s2.first.start(); + } +} + +QList > KateSpellCheckManager::spellCheckLanguageRanges(KateDocument *doc, + const KTextEditor::Range& range) +{ + QString defaultDict = doc->defaultDictionary(); + QList toReturn; + QList > dictionaryRanges = doc->dictionaryRanges(); + if(dictionaryRanges.isEmpty()) { + toReturn.push_back(RangeDictionaryPair(range, defaultDict)); + return toReturn; + } + QList splitQueue; + splitQueue.push_back(range); + while(!splitQueue.isEmpty()) { + bool handled = false; + KTextEditor::Range consideredRange = splitQueue.takeFirst(); + for(QList >::iterator i = dictionaryRanges.begin(); + i != dictionaryRanges.end(); ++i) { + KTextEditor::Range languageRange = *((*i).first); + KTextEditor::Range intersection = languageRange.intersect(consideredRange); + if(intersection.isEmpty()) { + continue; + } + toReturn.push_back(RangeDictionaryPair(intersection, (*i).second)); + splitQueue += rangeDifference(consideredRange, intersection); + handled = true; + break; + } + if(!handled) { + // 'consideredRange' did not intersect with any dictionary range, so we add it with the default dictionary + toReturn.push_back(RangeDictionaryPair(consideredRange, defaultDict)); + } + } + // finally, we still have to sort the list + qStableSort(toReturn.begin(), toReturn.end(), lessThanRangeDictionaryPair); + return toReturn; +} + +QList > KateSpellCheckManager::spellCheckWrtHighlightingRanges(KateDocument *document, + const KTextEditor::Range& range, + const QString& dictionary, + bool singleLine, + bool returnSingleRange) +{ + QList > toReturn; + if(range.isEmpty()) { + return toReturn; + } + + KateHighlighting *highlighting = document->highlight(); + + QList rangesToSplit; + if(!singleLine || range.onSingleLine()) { + rangesToSplit.push_back(range); + } + else { + const int startLine = range.start().line(); + const int startColumn = range.start().column(); + const int endLine = range.end().line(); + const int endColumn = range.end().column(); + for(int line = startLine; line <= endLine; ++line) { + const int start = (line == startLine) ? startColumn : 0; + const int end = (line == endLine) ? endColumn : document->lineLength(line); + KTextEditor::Range toAdd(line, start, line, end); + if(!toAdd.isEmpty()) { + rangesToSplit.push_back(toAdd); + } + } + } + for(QList::iterator i = rangesToSplit.begin(); i != rangesToSplit.end(); ++i) { + KTextEditor::Range rangeToSplit = *i; + KTextEditor::Cursor begin = KTextEditor::Cursor::invalid(); + const int startLine = rangeToSplit.start().line(); + const int startColumn = rangeToSplit.start().column(); + const int endLine = rangeToSplit.end().line(); + const int endColumn = rangeToSplit.end().column(); + bool inSpellCheckArea = false; + for(int line = startLine; line <= endLine; ++line) { + Kate::TextLine kateTextLine = document->kateTextLine(line); + if (!kateTextLine) continue; // bug #303496 + const int start = (line == startLine) ? startColumn : 0; + const int end = (line == endLine) ? endColumn : kateTextLine->length(); + for(int i = start; i < end;) { // WARNING: 'i' has to be incremented manually! + int attr = kateTextLine->attribute(i); + const KatePrefixStore& prefixStore = highlighting->getCharacterEncodingsPrefixStore(attr); + QString prefixFound = prefixStore.findPrefix(kateTextLine, i); + unsigned int attribute = kateTextLine->attribute(i); + if(!document->highlight()->attributeRequiresSpellchecking(attribute) + && prefixFound.isEmpty()) { + if(i == start) { + ++i; + continue; + } + else if(inSpellCheckArea) { + KTextEditor::Range spellCheckRange(begin, KTextEditor::Cursor(line, i)); + // work around Qt bug 6498 + trimRange(document, spellCheckRange); + if(!spellCheckRange.isEmpty()) { + toReturn.push_back(RangeDictionaryPair(spellCheckRange, dictionary)); + if(returnSingleRange) { + return toReturn; + } + } + begin = KTextEditor::Cursor::invalid(); + inSpellCheckArea = false; + } + } + else if(!inSpellCheckArea) { + begin = KTextEditor::Cursor(line, i); + inSpellCheckArea = true; + } + if(!prefixFound.isEmpty()) { + i += prefixFound.length(); + } + else { + ++i; + } + } + } + if(inSpellCheckArea) { + KTextEditor::Range spellCheckRange(begin, rangeToSplit.end()); + // work around Qt bug 6498 + trimRange(document, spellCheckRange); + if(!spellCheckRange.isEmpty()) { + toReturn.push_back(RangeDictionaryPair(spellCheckRange, dictionary)); + if(returnSingleRange) { + return toReturn; + } + } + } + } + + return toReturn; +} + +QList > KateSpellCheckManager::spellCheckRanges(KateDocument *doc, + const KTextEditor::Range& range, + bool singleLine) +{ + QList toReturn; + QList languageRangeList = spellCheckLanguageRanges(doc, range); + for(QList::iterator i = languageRangeList.begin(); i != languageRangeList.end(); ++i) { + const RangeDictionaryPair& p = *i; + toReturn += spellCheckWrtHighlightingRanges(doc, p.first, p.second, singleLine); + } + return toReturn; +} + +void KateSpellCheckManager::replaceCharactersEncodedIfNecessary(const QString& newWord, KateDocument *doc, + const KTextEditor::Range& replacementRange) +{ + const QString replacedString = doc->text(replacementRange); + int attr = doc->kateTextLine(replacementRange.start().line())->attribute(replacementRange.start().column()); + int p = doc->highlight()->getEncodedCharactersInsertionPolicy(attr); + + if((p == KateDocument::EncodeAlways) + || (p == KateDocument::EncodeWhenPresent && doc->containsCharacterEncoding(replacementRange))) { + doc->replaceText(replacementRange, newWord); + doc->replaceCharactersByEncoding(KTextEditor::Range(replacementRange.start(), + replacementRange.start() + KTextEditor::Cursor(0, newWord.length()))); + } + else { + doc->replaceText(replacementRange, newWord); + } +} + +void KateSpellCheckManager::trimRange(KateDocument *doc, KTextEditor::Range &r) +{ + if(r.isEmpty()) { + return; + } + KTextEditor::Cursor cursor = r.start(); + while(cursor < r.end()) { + if(doc->lineLength(cursor.line()) > 0 + && !doc->character(cursor).isSpace() && doc->character(cursor).category() != QChar::Other_Control) { + break; + } + cursor.setColumn(cursor.column() + 1); + if(cursor.column() >= doc->lineLength(cursor.line())) { + cursor.setPosition(cursor.line() + 1, 0); + } + } + r.start() = cursor; + if(r.isEmpty()) { + return; + } + + cursor = r.end(); + KTextEditor::Cursor prevCursor = cursor; + // the range cannot be empty now + do { + prevCursor = cursor; + if(cursor.column() <= 0) { + cursor.setPosition(cursor.line() - 1, doc->lineLength(cursor.line() - 1)); + } + else { + cursor.setColumn(cursor.column() - 1); + } + if(cursor.column() < doc->lineLength(cursor.line()) + && !doc->character(cursor).isSpace() && doc->character(cursor).category() != QChar::Other_Control) { + break; + } + } + while(cursor > r.start()); + r.end() = prevCursor; +} + +#include "moc_spellcheck.cpp" + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/spellcheck/spellcheck.h b/kate/part/spellcheck/spellcheck.h new file mode 100644 index 00000000..87d5d530 --- /dev/null +++ b/kate/part/spellcheck/spellcheck.h @@ -0,0 +1,73 @@ +/* This file is part of the KDE libraries and the Kate part. + * + * Copyright (C) 2008-2009 by Michel Ludwig + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef SPELLCHECK_H +#define SPELLCHECK_H + +#include +#include +#include +#include + +#include +#include +#include + +class KateDocument; +class KateView; + +class KateSpellCheckManager : public QObject { + Q_OBJECT + + typedef QPair RangeDictionaryPair; + + public: + KateSpellCheckManager(QObject* parent = NULL); + virtual ~KateSpellCheckManager(); + + QStringList suggestions(const QString& word, const QString& dictionary); + + void ignoreWord(const QString& word, const QString& dictionary); + void addToDictionary(const QString& word, const QString& dictionary); + + /** + * 'r2' is a subrange of 'r1', which is extracted from 'r1' and the remaining ranges are returned + **/ + static QList rangeDifference(const KTextEditor::Range& r1, const KTextEditor::Range& r2); + + public: + QList > spellCheckLanguageRanges(KateDocument *doc, const KTextEditor::Range& range); + + QList > spellCheckWrtHighlightingRanges(KateDocument *doc, const KTextEditor::Range& range, + const QString& dictionary = QString(), + bool singleLine = false, + bool returnSingleRange = false); + QList > spellCheckRanges(KateDocument *doc, const KTextEditor::Range& range, + bool singleLine = false); + + void replaceCharactersEncodedIfNecessary(const QString& newWord, KateDocument *doc, const KTextEditor::Range& replacementRange); + + private: + void trimRange(KateDocument *doc, KTextEditor::Range &r); +}; + +#endif + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/spellcheck/spellcheckdialog.cpp b/kate/part/spellcheck/spellcheckdialog.cpp new file mode 100644 index 00000000..dbfb9103 --- /dev/null +++ b/kate/part/spellcheck/spellcheckdialog.cpp @@ -0,0 +1,335 @@ +/* This file is part of the KDE libraries and the Kate part. + * + * Copyright (C) 2009-2010 by Michel Ludwig + * Copyright (C) 2008 Mirko Stocker + * Copyright (C) 2004-2005 Anders Lund + * Copyright (C) 2002 John Firebaugh + * Copyright (C) 2001-2004 Christoph Cullmann + * Copyright (C) 2001 Joseph Wenninger + * Copyright (C) 1999 Jochen Wilhelmy + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "spellcheckdialog.h" +#include "moc_spellcheckdialog.cpp" + +#include "katedocument.h" +#include "kateglobal.h" +#include "kateview.h" +#include "spellcheck/spellcheck.h" + +#include +#include +#include +#include +#include +#include +#include + +KateSpellCheckDialog::KateSpellCheckDialog( KateView* view ) + : QObject( view ) + , m_view (view) + , m_speller (NULL) + , m_backgroundChecker(NULL) + , m_sonnetDialog(NULL) + , m_globalSpellCheckRange(NULL) + , m_spellCheckCancelledByUser(false) +{ +} + +KateSpellCheckDialog::~KateSpellCheckDialog() +{ + delete m_globalSpellCheckRange; + delete m_sonnetDialog; + delete m_backgroundChecker; + delete m_speller; +} + +void KateSpellCheckDialog::createActions( KActionCollection* ac ) +{ + ac->addAction( KStandardAction::Spelling, this, SLOT(spellcheck()) ); + + KAction *a = new KAction( i18n("Spelling (from cursor)..."), this); + ac->addAction("tools_spelling_from_cursor", a ); + a->setIcon( KIcon( "tools-check-spelling" ) ); + a->setWhatsThis(i18n("Check the document's spelling from the cursor and forward")); + connect( a, SIGNAL(triggered()), this, SLOT(spellcheckFromCursor()) ); + + m_spellcheckSelection = new KAction( i18n("Spellcheck Selection..."), this ); + ac->addAction("tools_spelling_selection", m_spellcheckSelection); + m_spellcheckSelection->setIcon( KIcon( "tools-check-spelling" ) ); + m_spellcheckSelection->setWhatsThis(i18n("Check spelling of the selected text")); + connect( m_spellcheckSelection, SIGNAL(triggered()), this, SLOT(spellcheckSelection()) ); +} + +void KateSpellCheckDialog::updateActions () +{ + m_spellcheckSelection->setEnabled (m_view->selection ()); +} + +void KateSpellCheckDialog::spellcheckFromCursor() +{ + spellcheck( m_view->cursorPosition() ); +} + +void KateSpellCheckDialog::spellcheckSelection() +{ + spellcheck( m_view->selectionRange().start(), m_view->selectionRange().end() ); +} + +void KateSpellCheckDialog::spellcheck() +{ + spellcheck( KTextEditor::Cursor( 0, 0 ) ); +} + +void KateSpellCheckDialog::spellcheck( const KTextEditor::Cursor &from, const KTextEditor::Cursor &to ) +{ + KTextEditor::Cursor start = from; + KTextEditor::Cursor end = to; + + if ( end.line() == 0 && end.column() == 0 ) + { + end = m_view->doc()->documentEnd(); + } + + if ( !m_speller ) + { + m_speller = new Sonnet::Speller(); + } + m_speller->restore(KGlobal::config().data()); + + if ( !m_backgroundChecker ) + { + m_backgroundChecker = new Sonnet::BackgroundChecker(*m_speller); + } + + m_backgroundChecker->restore(KGlobal::config().data()); + + if ( !m_sonnetDialog ) + { + m_sonnetDialog = new Sonnet::Dialog(m_backgroundChecker, m_view); + m_sonnetDialog->showProgressDialog(200); + m_sonnetDialog->showSpellCheckCompletionMessage(); + m_sonnetDialog->setSpellCheckContinuedAfterReplacement(false); + + connect(m_sonnetDialog,SIGNAL(done(QString)),this,SLOT(installNextSpellCheckRange())); + + connect(m_sonnetDialog,SIGNAL(replace(QString,int,QString)), + this,SLOT(corrected(QString,int,QString))); + + connect(m_sonnetDialog,SIGNAL(misspelling(QString,int)), + this,SLOT(misspelling(QString,int))); + + connect(m_sonnetDialog,SIGNAL(cancel()), + this,SLOT(cancelClicked())); + + connect(m_sonnetDialog,SIGNAL(destroyed(QObject*)), + this,SLOT(objectDestroyed(QObject*))); + + connect(m_sonnetDialog,SIGNAL(languageChanged(QString)), + this,SLOT(languageChanged(QString))); + } + + m_userSpellCheckLanguage.clear(); + m_previousGivenSpellCheckLanguage.clear(); + delete m_globalSpellCheckRange; + // we expand to handle the situation when the last word in the range is replace by a new one + m_globalSpellCheckRange = m_view->doc()->newMovingRange (KTextEditor::Range( start, end ), + KTextEditor::MovingRange::ExpandLeft | KTextEditor::MovingRange::ExpandRight); + m_spellCheckCancelledByUser = false; + performSpellCheck( *m_globalSpellCheckRange ); +} + +KTextEditor::Cursor KateSpellCheckDialog::locatePosition( int pos ) +{ + uint remains; + + while ( m_spellLastPos < (uint)pos ) + { + remains = pos - m_spellLastPos; + uint l = m_view->doc()->lineLength( m_spellPosCursor.line() ) - m_spellPosCursor.column(); + if ( l > remains ) + { + m_spellPosCursor.setColumn( m_spellPosCursor.column() + remains ); + m_spellLastPos = pos; + } + else + { + m_spellPosCursor.setLine( m_spellPosCursor.line() + 1 ); + m_spellPosCursor.setColumn(0); + m_spellLastPos += l + 1; + } + } + + return m_spellPosCursor; +} + +void KateSpellCheckDialog::misspelling( const QString& word, int pos ) +{ + KTextEditor::Cursor cursor; + int length; + int origPos = m_view->doc()->computePositionWrtOffsets( m_currentDecToEncOffsetList, pos ); + cursor = locatePosition( origPos ); + length = m_view->doc()->computePositionWrtOffsets( m_currentDecToEncOffsetList, pos + word.length() ) + - origPos; + + m_view->setCursorPositionInternal (cursor, 1); + m_view->setSelection( KTextEditor::Range(cursor, length) ); +} + +void KateSpellCheckDialog::corrected( const QString& word, int pos, const QString& newWord ) +{ + int origPos = m_view->doc()->computePositionWrtOffsets( m_currentDecToEncOffsetList, pos ); + + int length = m_view->doc()->computePositionWrtOffsets( m_currentDecToEncOffsetList, pos + word.length() ) + - origPos; + + KTextEditor::Cursor replacementStartCursor = locatePosition( origPos ); + KTextEditor::Range replacementRange = KTextEditor::Range( replacementStartCursor, length ); + KateDocument *doc = m_view->doc(); + KateGlobal::self()->spellCheckManager()->replaceCharactersEncodedIfNecessary( newWord, doc, replacementRange ); + + m_currentSpellCheckRange.setRange( KTextEditor::Range( replacementStartCursor, m_currentSpellCheckRange.end() ) ); + // we have to be careful here: due to static word wrapping the text might change in addition to simply + // the misspelled word being replaced, i.e. new line breaks might be inserted as well. As such, the text + // in the 'Sonnet::Dialog' might be eventually out of sync with the visible text. Therefore, we 'restart' + // spell checking from the current position. + performSpellCheck( KTextEditor::Range( replacementStartCursor, m_globalSpellCheckRange->end() ) ); +} + +void KateSpellCheckDialog::performSpellCheck(const KTextEditor::Range& range) +{ + if(range.isEmpty()) { + spellCheckDone(); + } + m_languagesInSpellCheckRange = KateGlobal::self()->spellCheckManager()->spellCheckLanguageRanges(m_view->doc(), range); + m_currentLanguageRangeIterator = m_languagesInSpellCheckRange.begin(); + m_currentSpellCheckRange = KTextEditor::Range::invalid(); + installNextSpellCheckRange(); + // first check if there is really something to spell check + if(m_currentSpellCheckRange.isValid()) { + m_sonnetDialog->show(); + } +} + +void KateSpellCheckDialog::installNextSpellCheckRange() +{ + if ( m_spellCheckCancelledByUser + || m_currentLanguageRangeIterator == m_languagesInSpellCheckRange.end() ) + { + spellCheckDone(); + return; + } + KateSpellCheckManager *spellCheckManager = KateGlobal::self()->spellCheckManager(); + KTextEditor::Cursor nextRangeBegin = (m_currentSpellCheckRange.isValid() ? m_currentSpellCheckRange.end() + : KTextEditor::Cursor::invalid()); + m_currentSpellCheckRange = KTextEditor::Range::invalid(); + m_currentDecToEncOffsetList.clear(); + QList > rangeDictionaryPairList; + while ( m_currentLanguageRangeIterator != m_languagesInSpellCheckRange.end() ) + { + const KTextEditor::Range& currentLanguageRange = (*m_currentLanguageRangeIterator).first; + const QString& dictionary = (*m_currentLanguageRangeIterator).second; + KTextEditor::Range languageSubRange = (nextRangeBegin.isValid() ? KTextEditor::Range(nextRangeBegin, currentLanguageRange.end()) + : currentLanguageRange); + rangeDictionaryPairList = spellCheckManager->spellCheckWrtHighlightingRanges(m_view->doc(), + languageSubRange, + dictionary, + false, true); + Q_ASSERT(rangeDictionaryPairList.size() <= 1); + if(rangeDictionaryPairList.size() == 0) { + ++m_currentLanguageRangeIterator; + if ( m_currentLanguageRangeIterator != m_languagesInSpellCheckRange.end() ) + { + nextRangeBegin = (*m_currentLanguageRangeIterator).first.start(); + } + } + else { + m_currentSpellCheckRange = rangeDictionaryPairList.first().first; + QString dictionary = rangeDictionaryPairList.first().second; + const bool languageChanged = (dictionary != m_previousGivenSpellCheckLanguage); + m_previousGivenSpellCheckLanguage = dictionary; + + // if there was no change of dictionary stemming from the document language ranges and + // the user has set a dictionary in the dialog, we use that one + if(!languageChanged && !m_userSpellCheckLanguage.isEmpty()) { + dictionary = m_userSpellCheckLanguage; + } + // we only allow the user to override the preset dictionary within a language range + // given by the document + else if(languageChanged) { + m_userSpellCheckLanguage.clear(); + } + + m_spellPosCursor = m_currentSpellCheckRange.start(); + m_spellLastPos = 0; + + m_currentDecToEncOffsetList.clear(); + KateDocument::OffsetList encToDecOffsetList; + QString text = m_view->doc()->decodeCharacters(m_currentSpellCheckRange, + m_currentDecToEncOffsetList, + encToDecOffsetList); + // ensure that no empty string is passed on to Sonnet as this can lead to a crash + // (bug 228789) + if(text.isEmpty()) { + nextRangeBegin = m_currentSpellCheckRange.end(); + continue; + } + + if(m_speller->language() != dictionary) { + m_speller->setLanguage(dictionary); + m_backgroundChecker->setSpeller(*m_speller); + } + + m_sonnetDialog->setBuffer(text); + break; + } + } + if ( m_currentLanguageRangeIterator == m_languagesInSpellCheckRange.end() ) + { + spellCheckDone(); + return; + } +} + +void KateSpellCheckDialog::cancelClicked() +{ + m_spellCheckCancelledByUser = true; +} + +void KateSpellCheckDialog::spellCheckDone() +{ + m_currentSpellCheckRange = KTextEditor::Range::invalid(); + m_currentDecToEncOffsetList.clear(); + m_view->clearSelection(); +} + +void KateSpellCheckDialog::objectDestroyed(QObject *object) +{ + Q_UNUSED(object); + m_sonnetDialog = NULL; +} + +void KateSpellCheckDialog::languageChanged(const QString &language) +{ + m_userSpellCheckLanguage = language; +} + +//END + + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/spellcheck/spellcheckdialog.h b/kate/part/spellcheck/spellcheckdialog.h new file mode 100644 index 00000000..e2ff4fb5 --- /dev/null +++ b/kate/part/spellcheck/spellcheckdialog.h @@ -0,0 +1,122 @@ +/* This file is part of the KDE libraries and the Kate part. + * + * Copyright (C) 2009-2010 by Michel Ludwig + * Copyright (C) 2008 Mirko Stocker + * Copyright (C) 2004-2005 Anders Lund + * Copyright (C) 2002 John Firebaugh + * Copyright (C) 2001-2004 Christoph Cullmann + * Copyright (C) 2001 Joseph Wenninger + * Copyright (C) 1999 Jochen Wilhelmy + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __KATE_SPELLCHECKDIALOG_H__ +#define __KATE_SPELLCHECKDIALOG_H__ + +#include + +class KateView; + +class KAction; +class KActionCollection; +class KProgressDialog; +namespace Sonnet { + class Dialog; + class BackgroundChecker; + class Speller; +} + +#include "ktexteditor/range.h" + +namespace KTextEditor { + class MovingRange; +} + +class KateSpellCheckDialog : public QObject +{ + Q_OBJECT + + public: + explicit KateSpellCheckDialog( KateView* ); + ~KateSpellCheckDialog(); + + void createActions( KActionCollection* ); + + void updateActions (); + + // spellcheck from cursor, selection + private Q_SLOTS: + void spellcheckFromCursor(); + + // defined here in anticipation of per view selections ;) + void spellcheckSelection(); + + void spellcheck(); + + /** + * Spellcheck a defined portion of the text. + * + * @param from Where to start the check + * @param to Where to end. If this is (0,0), it will be set to the end of the document. + */ + void spellcheck( const KTextEditor::Cursor &from, const KTextEditor::Cursor &to=KTextEditor::Cursor() ); + + void misspelling( const QString&, int ); + void corrected ( const QString&, int, const QString&); + + void performSpellCheck(const KTextEditor::Range& range); + void installNextSpellCheckRange(); + + void cancelClicked(); + + void objectDestroyed(QObject *object); + + void languageChanged(const QString &language); + + private: + KTextEditor::Cursor locatePosition( int pos ); + + KateView *m_view; + KAction *m_spellcheckSelection; + + Sonnet::Speller *m_speller; + Sonnet::BackgroundChecker *m_backgroundChecker; + Sonnet::Dialog *m_sonnetDialog; + + // define the part of the text that is to be checked + KTextEditor::Range m_currentSpellCheckRange; + KTextEditor::MovingRange *m_globalSpellCheckRange; + + QList > m_currentDecToEncOffsetList; + + QList > m_languagesInSpellCheckRange; + QList >::iterator m_currentLanguageRangeIterator; + + // keep track of where we are. + KTextEditor::Cursor m_spellPosCursor; + uint m_spellLastPos; + + bool m_spellCheckCancelledByUser; + + QString m_userSpellCheckLanguage, m_previousGivenSpellCheckLanguage; + + void spellCheckDone(); +}; + +#endif + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/spellcheck/spellingmenu.cpp b/kate/part/spellcheck/spellingmenu.cpp new file mode 100644 index 00000000..47af5210 --- /dev/null +++ b/kate/part/spellcheck/spellingmenu.cpp @@ -0,0 +1,227 @@ +/* This file is part of the KDE libraries and the Kate part. + * + * Copyright (C) 2009-2010 by Michel Ludwig + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "spellingmenu.h" +#include "moc_spellingmenu.cpp" + +#include "katedocument.h" +#include "kateglobal.h" +#include "kateview.h" +#include "spellcheck/spellcheck.h" + +#include + +KateSpellingMenu::KateSpellingMenu(KateView *view) + : QObject(view), + m_view (view), + m_spellingMenuAction(NULL), + m_ignoreWordAction(NULL), + m_addToDictionaryAction(NULL), + m_spellingMenu(NULL), + m_currentMisspelledRange(NULL), // we have to use 'm_currentMisspelledRange' + // as QSignalMapper doesn't work with pairs of objects; + // it just points to the object pointed to by either + // 'm_currentMouseMisspelledRange' or 'm_currentCaretMisspelledRange' + m_currentMouseMisspelledRange(NULL), + m_currentCaretMisspelledRange(NULL), + m_useMouseForMisspelledRange(false), + m_suggestionsSignalMapper(new QSignalMapper(this)) +{ + connect(m_suggestionsSignalMapper, SIGNAL(mapped(QString)), + this, SLOT(replaceWordBySuggestion(QString))); +} + +KateSpellingMenu::~KateSpellingMenu() +{ + m_currentMisspelledRange = NULL; // it shouldn't be accessed anymore as it could + // point to a non-existing object (bug 226724) + // (for example, when it pointed to m_currentCaretMisspelledRange + // and that range got deleted after the caret had left) + m_currentCaretMisspelledRange = NULL; + m_currentMouseMisspelledRange = NULL; +} + +bool KateSpellingMenu::isEnabled() const +{ + if(!m_spellingMenuAction) { + return false; + } + return m_spellingMenuAction->isEnabled(); +} + +void KateSpellingMenu::setEnabled(bool b) +{ + if(m_spellingMenuAction) { + m_spellingMenuAction->setEnabled(b); + } + if(m_ignoreWordAction) { + m_ignoreWordAction->setEnabled(b); + } + if(m_addToDictionaryAction) { + m_addToDictionaryAction->setEnabled(b); + } +} + +void KateSpellingMenu::setVisible(bool b) +{ + if(m_spellingMenuAction) { + m_spellingMenuAction->setVisible(b); + } + if(m_ignoreWordAction) { + m_ignoreWordAction->setVisible(b); + } + if(m_addToDictionaryAction) { + m_addToDictionaryAction->setVisible(b); + } +} + +void KateSpellingMenu::createActions(KActionCollection *ac) +{ + m_spellingMenuAction = new KActionMenu(i18n("Spelling"), this); + ac->addAction("spelling_suggestions", m_spellingMenuAction); + m_spellingMenu = m_spellingMenuAction->menu(); + connect(m_spellingMenu, SIGNAL(aboutToShow()), this, SLOT(populateSuggestionsMenu())); + + m_ignoreWordAction = new KAction(i18n("Ignore Word"), this); + connect(m_ignoreWordAction, SIGNAL(triggered()), this, SLOT(ignoreCurrentWord())); + + m_addToDictionaryAction = new KAction(i18n("Add to Dictionary"), this); + connect(m_addToDictionaryAction, SIGNAL(triggered()), this, SLOT(addCurrentWordToDictionary())); + + setEnabled(false); + setVisible(false); +} + +void KateSpellingMenu::caretEnteredMisspelledRange(KTextEditor::MovingRange *range) +{ + if(m_currentCaretMisspelledRange == range) { + return; + } + m_currentCaretMisspelledRange = NULL; + setEnabled(true); + m_currentCaretMisspelledRange = range; +} + +void KateSpellingMenu::caretExitedMisspelledRange(KTextEditor::MovingRange *range) +{ + if(range != m_currentCaretMisspelledRange) { // order of 'exit' and 'entered' signals + return; // was wrong + } + setEnabled(false); + m_currentCaretMisspelledRange = NULL; +} + +void KateSpellingMenu::mouseEnteredMisspelledRange(KTextEditor::MovingRange *range) +{ + if(m_currentMouseMisspelledRange == range) { + return; + } + m_currentMouseMisspelledRange = range; +} + +void KateSpellingMenu::mouseExitedMisspelledRange(KTextEditor::MovingRange *range) +{ + if(range != m_currentMouseMisspelledRange) { // order of 'exit' and 'entered' signals + return; // was wrong + } + m_currentMouseMisspelledRange = NULL; +} + +void KateSpellingMenu::rangeDeleted(KTextEditor::MovingRange *range) +{ + if(m_currentCaretMisspelledRange == range) { + m_currentCaretMisspelledRange = NULL; + } + if(m_currentMouseMisspelledRange == range) { + m_currentMouseMisspelledRange = NULL; + } + if(m_currentMisspelledRange == range) { + m_currentMisspelledRange = NULL; + } + setEnabled(m_currentCaretMisspelledRange != NULL); +} + +void KateSpellingMenu::setUseMouseForMisspelledRange(bool b) +{ + m_useMouseForMisspelledRange = b; + if(m_useMouseForMisspelledRange) { + setEnabled(m_currentMouseMisspelledRange != NULL); + } + else if(!m_useMouseForMisspelledRange) { + setEnabled(m_currentCaretMisspelledRange != NULL); + } +} + +void KateSpellingMenu::populateSuggestionsMenu() +{ + m_spellingMenu->clear(); + if((m_useMouseForMisspelledRange && !m_currentMouseMisspelledRange) + || (!m_useMouseForMisspelledRange && !m_currentCaretMisspelledRange)) { + return; + } + m_currentMisspelledRange = (m_useMouseForMisspelledRange ? m_currentMouseMisspelledRange + : m_currentCaretMisspelledRange); + m_spellingMenu->addAction(m_ignoreWordAction); + m_spellingMenu->addAction(m_addToDictionaryAction); + m_spellingMenu->addSeparator(); + const QString& misspelledWord = m_view->doc()->text(*m_currentMisspelledRange); + const QString dictionary = m_view->doc()->dictionaryForMisspelledRange(*m_currentMisspelledRange); + m_currentSuggestions = KateGlobal::self()->spellCheckManager()->suggestions(misspelledWord, dictionary); + + int counter = 0; + for(QStringList::iterator i = m_currentSuggestions.begin(); i != m_currentSuggestions.end() && counter < 10; ++i) { + const QString& suggestion = *i; + KAction *action = new KAction(suggestion, m_spellingMenu); + connect(action, SIGNAL(triggered()), m_suggestionsSignalMapper, SLOT(map())); + m_suggestionsSignalMapper->setMapping(action, suggestion); + m_spellingMenu->addAction(action); + ++counter; + } +} + +void KateSpellingMenu::replaceWordBySuggestion(const QString& suggestion) +{ + KateDocument *doc = m_view->doc(); + KateGlobal::self()->spellCheckManager()->replaceCharactersEncodedIfNecessary(suggestion, doc, *m_currentMisspelledRange); +} + +void KateSpellingMenu::addCurrentWordToDictionary() +{ + if(!m_currentMisspelledRange) { + return; + } + const QString& misspelledWord = m_view->doc()->text(*m_currentMisspelledRange); + const QString dictionary = m_view->doc()->dictionaryForMisspelledRange(*m_currentMisspelledRange); + KateGlobal::self()->spellCheckManager()->addToDictionary(misspelledWord, dictionary); + m_view->doc()->clearMisspellingForWord(misspelledWord); // WARNING: 'm_currentMisspelledRange' is deleted here! +} + +void KateSpellingMenu::ignoreCurrentWord() +{ + if(!m_currentMisspelledRange) { + return; + } + const QString& misspelledWord = m_view->doc()->text(*m_currentMisspelledRange); + const QString dictionary = m_view->doc()->dictionaryForMisspelledRange(*m_currentMisspelledRange); + KateGlobal::self()->spellCheckManager()->ignoreWord(misspelledWord, dictionary); + m_view->doc()->clearMisspellingForWord(misspelledWord); // WARNING: 'm_currentMisspelledRange' is deleted here! +} + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/spellcheck/spellingmenu.h b/kate/part/spellcheck/spellingmenu.h new file mode 100644 index 00000000..680e134e --- /dev/null +++ b/kate/part/spellcheck/spellingmenu.h @@ -0,0 +1,93 @@ +/* This file is part of the KDE libraries and the Kate part. + * + * Copyright (C) 2008-2009 by Michel Ludwig + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef SPELLINGMENU_H +#define SPELLINGMENU_H + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +class KateDocument; +class KateView; + +class KateOnTheFlyChecker; + +class KateSpellingMenu : public QObject { + Q_OBJECT + friend class KateOnTheFlyChecker; + + public: + KateSpellingMenu(KateView *view); + virtual ~KateSpellingMenu(); + + bool isEnabled() const; + + void createActions(KActionCollection *ac); + + /** + * This method has to be called before the menu is shown in response to a context + * menu event. It will trigger that the misspelled range located under the mouse pointer + * is considered for the spelling suggestions. + **/ + void setUseMouseForMisspelledRange(bool b); + + public Q_SLOTS: + void setEnabled(bool b); + void setVisible(bool b); + + protected: + KateView *m_view; + KActionMenu *m_spellingMenuAction; + KAction *m_ignoreWordAction, *m_addToDictionaryAction; + KMenu *m_spellingMenu; + KTextEditor::MovingRange *m_currentMisspelledRange; + KTextEditor::MovingRange *m_currentMouseMisspelledRange; + KTextEditor::MovingRange *m_currentCaretMisspelledRange; + bool m_useMouseForMisspelledRange; + QStringList m_currentSuggestions; + QSignalMapper *m_suggestionsSignalMapper; + + // These methods are called from KateOnTheFlyChecker to inform about events involving + // moving ranges. + void rangeDeleted(KTextEditor::MovingRange *range); + void caretEnteredMisspelledRange(KTextEditor::MovingRange *range); + void caretExitedMisspelledRange(KTextEditor::MovingRange *range); + void mouseEnteredMisspelledRange(KTextEditor::MovingRange *range); + void mouseExitedMisspelledRange(KTextEditor::MovingRange *range); + + protected Q_SLOTS: + void populateSuggestionsMenu(); + void replaceWordBySuggestion(const QString& suggestion); + + void addCurrentWordToDictionary(); + void ignoreCurrentWord(); +}; + +#endif + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/swapfile/kateswapdiffcreator.cpp b/kate/part/swapfile/kateswapdiffcreator.cpp new file mode 100644 index 00000000..4a800972 --- /dev/null +++ b/kate/part/swapfile/kateswapdiffcreator.cpp @@ -0,0 +1,163 @@ +/* This file is part of the Kate project. + * + * Copyright (C) 2010-2012 Dominik Haumann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "kateswapdiffcreator.h" +#include "kateswapfile.h" +#include "katedocument.h" + +#include +#include +#include +#include + +//BEGIN SwapDiffCreator +SwapDiffCreator::SwapDiffCreator(Kate::SwapFile* swapFile) + : QObject (swapFile) + , m_swapFile (swapFile) + , m_proc(0) +{ +} + +SwapDiffCreator::~SwapDiffCreator () +{ +} + +void SwapDiffCreator::viewDiff() +{ + QString path = m_swapFile->fileName(); + if (path.isNull()) + return; + + QFile swp(path); + if (!swp.open(QIODevice::ReadOnly)) { + kWarning( 13020 ) << "Can't open swap file"; + return; + } + + // create all needed tempfiles + m_originalFile.setSuffix(".original"); + m_recoveredFile.setSuffix(".recovered"); + m_diffFile.setSuffix(".diff"); + + if (!m_originalFile.open() || !m_recoveredFile.open() || !m_diffFile.open()) { + kWarning( 13020 ) << "Can't open temporary files needed for diffing"; + return; + } + + // truncate files, just in case + m_originalFile.resize (0); + m_recoveredFile.resize (0); + m_diffFile.resize (0); + + // create a document with the recovered data + KateDocument recoverDoc; + recoverDoc.setText(m_swapFile->document()->text()); + + // store original text in a file as utf-8 and close it + { + QTextStream stream (&m_originalFile); + stream.setCodec (QTextCodec::codecForName("UTF-8")); + stream << recoverDoc.text (); + } + m_originalFile.close (); + + // recover data + QDataStream stream(&swp); + recoverDoc.swapFile()->recover(stream, false); + + // store recovered text in a file as utf-8 and close it + { + QTextStream stream (&m_recoveredFile); + stream.setCodec (QTextCodec::codecForName("UTF-8")); + stream << recoverDoc.text (); + } + m_recoveredFile.close (); + + // create a QProcess proc for diff + m_proc = new QProcess(this); + m_proc->setProcessChannelMode(QProcess::MergedChannels); + + connect(m_proc, SIGNAL(readyRead()), this, SLOT(slotDataAvailable())); + connect(m_proc, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(slotDiffFinished())); + +// setCursor(Qt::WaitCursor); + + QStringList procargs; + procargs << "-u" << m_originalFile.fileName() << m_recoveredFile.fileName(); + m_proc->start("diff", procargs); + + QTextStream ts(m_proc); + int lineCount = recoverDoc.lines(); + for (int line = 0; line < lineCount; ++line) + ts << recoverDoc.line(line) << '\n'; + ts.flush(); + m_proc->closeWriteChannel(); +} + +void SwapDiffCreator::slotDataAvailable() +{ + // collect diff output + m_diffFile.write (m_proc->readAll()); +} + +void SwapDiffCreator::slotDiffFinished() +{ + // collect last diff output, if any + m_diffFile.write (m_proc->readAll()); + + // get the exit status to check whether diff command run successfully + const QProcess::ExitStatus es = m_proc->exitStatus(); + delete m_proc; + m_proc = 0; + + // check exit status + if (es != QProcess::NormalExit) + { + KMessageBox::sorry(0, + i18n("The diff command failed. Please make sure that " + "diff(1) is installed and in your PATH."), + i18n("Error Creating Diff")); + deleteLater(); + return; + } + + // sanity check: is there any diff content? + if ( m_diffFile.size() == 0 ) + { + KMessageBox::information(0, + i18n("The files are identical."), + i18n("Diff Output")); + deleteLater(); + return; + } + + // close diffFile and avoid removal, KRun will do that later! + m_diffFile.close (); + m_diffFile.setAutoRemove (false); + + // KRun::runUrl should delete the file, once the client exits + KRun::runUrl (KUrl::fromPath(m_diffFile.fileName()), "text/x-patch", m_swapFile->document()->activeView(), true ); + + deleteLater(); +} + +//END SwapDiffCreator + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/swapfile/kateswapdiffcreator.h b/kate/part/swapfile/kateswapdiffcreator.h new file mode 100644 index 00000000..dcc06569 --- /dev/null +++ b/kate/part/swapfile/kateswapdiffcreator.h @@ -0,0 +1,61 @@ +/* This file is part of the Kate project. + * + * Copyright (C) 2010-2012 Dominik Haumann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef KATE_SWAP_DIFF_CREATOR_H +#define KATE_SWAP_DIFF_CREATOR_H + +#include +#include +#include + +#include + +namespace Kate { + class SwapFile; +} + +class SwapDiffCreator : public QObject +{ + Q_OBJECT + + public: + explicit SwapDiffCreator (Kate::SwapFile* swapFile); + ~SwapDiffCreator (); + + public Q_SLOTS: + void viewDiff(); + + private: + Kate::SwapFile * m_swapFile; + + protected Q_SLOTS: + void slotDataAvailable(); + void slotDiffFinished(); + + private: + QProcess* m_proc; + KTemporaryFile m_originalFile; + KTemporaryFile m_recoveredFile; + KTemporaryFile m_diffFile; +}; + +#endif //KATE_SWAP_DIFF_CREATOR_H + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/swapfile/kateswapfile.cpp b/kate/part/swapfile/kateswapfile.cpp new file mode 100644 index 00000000..3f47365f --- /dev/null +++ b/kate/part/swapfile/kateswapfile.cpp @@ -0,0 +1,629 @@ +/* This file is part of the Kate project. + * + * Copyright (C) 2010 Dominik Haumann + * Copyright (C) 2010 Diana-Victoria Tiriplica + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" + +#include "kateswapfile.h" +#include "kateconfig.h" +#include "kateswapdiffcreator.h" +#include "kateundomanager.h" + +#include + +#include +#include +#include +#include + +#include +#include +#include + + +// swap file version header +const static char * const swapFileVersionString = "Kate Swap File 2.0"; + +// tokens for swap files +const static qint8 EA_StartEditing = 'S'; +const static qint8 EA_FinishEditing = 'E'; +const static qint8 EA_WrapLine = 'W'; +const static qint8 EA_UnwrapLine = 'U'; +const static qint8 EA_InsertText = 'I'; +const static qint8 EA_RemoveText = 'R'; + +namespace Kate { + +QTimer* SwapFile::s_timer = 0; + +SwapFile::SwapFile(KateDocument *document) + : QObject(document) + , m_document(document) + , m_trackingEnabled(false) + , m_recovered(false) + , m_needSync(false) +{ + // fixed version of serialisation + m_stream.setVersion (QDataStream::Qt_4_6); + + // conect the timer + connect(syncTimer(), SIGNAL(timeout()), this, SLOT(writeFileToDisk()), Qt::DirectConnection); + + // connecting the signals + connect(&m_document->buffer(), SIGNAL(saved(QString)), this, SLOT(fileSaved(QString))); + connect(&m_document->buffer(), SIGNAL(loaded(QString,bool)), this, SLOT(fileLoaded(QString))); + connect(m_document, SIGNAL(configChanged()), this, SLOT(configChanged())); + + // tracking on! + setTrackingEnabled (true); +} + +SwapFile::~SwapFile() +{ + // only remove swap file after data recovery (bug #304576) + if (!shouldRecover()) { + removeSwapFile(); + } +} + +void SwapFile::configChanged() +{ +} + +void SwapFile::setTrackingEnabled(bool enable) +{ + if (m_trackingEnabled == enable) { + return; + } + + m_trackingEnabled = enable; + + TextBuffer &buffer = m_document->buffer(); + + if (m_trackingEnabled) { + connect(&buffer, SIGNAL(editingStarted()), this, SLOT(startEditing())); + connect(&buffer, SIGNAL(editingFinished()), this, SLOT(finishEditing())); + connect(m_document, SIGNAL(modifiedChanged(KTextEditor::Document*)), this, SLOT(modifiedChanged())); + + connect(&buffer, SIGNAL(lineWrapped(KTextEditor::Cursor)), this, SLOT(wrapLine(KTextEditor::Cursor))); + connect(&buffer, SIGNAL(lineUnwrapped(int)), this, SLOT(unwrapLine(int))); + connect(&buffer, SIGNAL(textInserted(KTextEditor::Cursor,QString)), this, SLOT(insertText(KTextEditor::Cursor,QString))); + connect(&buffer, SIGNAL(textRemoved(KTextEditor::Range,QString)), this, SLOT(removeText(KTextEditor::Range))); + } else { + disconnect(&buffer, SIGNAL(editingStarted()), this, SLOT(startEditing())); + disconnect(&buffer, SIGNAL(editingFinished()), this, SLOT(finishEditing())); + disconnect(m_document, SIGNAL(modifiedChanged(KTextEditor::Document*)), this, SLOT(modifiedChanged())); + + disconnect(&buffer, SIGNAL(lineWrapped(KTextEditor::Cursor)), this, SLOT(wrapLine(KTextEditor::Cursor))); + disconnect(&buffer, SIGNAL(lineUnwrapped(int)), this, SLOT(unwrapLine(int))); + disconnect(&buffer, SIGNAL(textInserted(KTextEditor::Cursor,QString)), this, SLOT(insertText(KTextEditor::Cursor,QString))); + disconnect(&buffer, SIGNAL(textRemoved(KTextEditor::Range,QString)), this, SLOT(removeText(KTextEditor::Range))); + } +} + +void SwapFile::fileClosed () +{ + // remove old swap file, file is now closed + if (!shouldRecover()) { + removeSwapFile(); + } else { + m_document->setReadWrite(true); + } + + // purge filename + updateFileName(); +} + +KateDocument* SwapFile::document() +{ + return m_document; +} + +bool SwapFile::isValidSwapFile(QDataStream& stream, bool checkDigest) const +{ + // read and check header + QByteArray header; + stream >> header; + + if (header != swapFileVersionString) { + kWarning( 13020 ) << "Can't open swap file, wrong version"; + return false; + } + + // read md5 digest + QByteArray digest; + stream >> digest; +// kDebug() << "DIGEST:" << digest << m_document->digest(); + if (checkDigest && digest != m_document->digest()) { + kWarning( 13020 ) << "Can't recover from swap file, digest of document has changed"; + return false; + } + + return true; +} + +void SwapFile::fileLoaded(const QString&) +{ + // look for swap file + if (!updateFileName()) + return; + + if (!m_swapfile.exists()) + { + kDebug (13020) << "No swap file"; + return; + } + + if (!QFileInfo(m_swapfile).isReadable()) + { + kWarning( 13020 ) << "Can't open swap file (missing permissions)"; + return; + } + + // sanity check + QFile peekFile(fileName()); + if (peekFile.open(QIODevice::ReadOnly)) { + QDataStream stream(&peekFile); + if (!isValidSwapFile(stream, true)) { + removeSwapFile(); + return; + } + peekFile.close(); + } else { + kWarning( 13020 ) << "Can't open swap file:" << fileName(); + return; + } + + // show swap file message + m_document->setReadWrite(false); + showSwapFileMessage(); +} + +void SwapFile::modifiedChanged() +{ + if (!m_document->isModified() && !shouldRecover()) { + m_needSync = false; + // the file is not modified and we are not in recover mode + removeSwapFile(); + } +} + +void SwapFile::recover() +{ + m_document->setReadWrite(true); + + // if isOpen() returns true, the swap file likely changed already (appended data) + // Example: The document was falsely marked as writable and the user changed + // text even though the recover bar was visible. In this case, a replay of + // the swap file across wrong document content would happen -> certainly wrong + if (m_swapfile.isOpen()) { + kWarning( 13020 ) << "Attempt to recover an already modified document. Aborting"; + removeSwapFile(); + return; + } + + // if the file doesn't exist, abort (user might have deleted it, or use two editor instances) + if (!m_swapfile.open(QIODevice::ReadOnly)) + { + kWarning( 13020 ) << "Can't open swap file"; + return; + } + + // remember that the file has recovered + m_recovered = true; + + // open data stream + m_stream.setDevice(&m_swapfile); + + // replay the swap file + bool success = recover(m_stream); + + // close swap file + m_stream.setDevice(0); + m_swapfile.close(); + + if (!success) + removeSwapFile(); + + // recover can also be called through the KTE::RecoveryInterface. + // Make sure, the message is hidden in this case as well. + if (m_swapMessage) + m_swapMessage->deleteLater(); +} + +bool SwapFile::recover(QDataStream& stream, bool checkDigest) +{ + if (!isValidSwapFile(stream, checkDigest)) { + return false; + } + + // disconnect current signals + setTrackingEnabled(false); + + // needed to set undo/redo cursors in a sane way + bool firstEditInGroup = false; + KTextEditor::Cursor undoCursor = KTextEditor::Cursor::invalid(); + KTextEditor::Cursor redoCursor = KTextEditor::Cursor::invalid(); + + // replay swapfile + bool editRunning = false; + bool brokenSwapFile = false; + while (!stream.atEnd()) { + if (brokenSwapFile) + break; + + qint8 type; + stream >> type; + switch (type) { + case EA_StartEditing: { + m_document->editStart(); + editRunning = true; + firstEditInGroup = true; + undoCursor = KTextEditor::Cursor::invalid(); + redoCursor = KTextEditor::Cursor::invalid(); + break; + } + case EA_FinishEditing: { + m_document->editEnd(); + + // empty editStart() / editEnd() groups exist: only set cursor if required + if (!firstEditInGroup) { + // set undo/redo cursor of last KateUndoGroup of the undo manager + m_document->undoManager()->setUndoRedoCursorsOfLastGroup(undoCursor, redoCursor); + m_document->undoManager()->undoSafePoint(); + } + firstEditInGroup = false; + editRunning = false; + break; + } + case EA_WrapLine: { + if (!editRunning) { + brokenSwapFile = true; + break; + } + + int line = 0, column = 0; + stream >> line >> column; + + // emulate buffer unwrapLine with document + m_document->editWrapLine(line, column, true); + + // track undo/redo cursor + if (firstEditInGroup) { + firstEditInGroup = false; + undoCursor = KTextEditor::Cursor(line, column); + } + redoCursor = KTextEditor::Cursor(line + 1, 0); + + break; + } + case EA_UnwrapLine: { + if (!editRunning) { + brokenSwapFile = true; + break; + } + + int line = 0; + stream >> line; + + // assert valid line + Q_ASSERT (line > 0); + + const int undoColumn = m_document->lineLength(line - 1); + + // emulate buffer unwrapLine with document + m_document->editUnWrapLine(line - 1, true, 0); + + // track undo/redo cursor + if (firstEditInGroup) { + firstEditInGroup = false; + undoCursor = KTextEditor::Cursor(line, 0); + } + redoCursor = KTextEditor::Cursor(line - 1, undoColumn); + + break; + } + case EA_InsertText: { + if (!editRunning) { + brokenSwapFile = true; + break; + } + + int line, column; + QByteArray text; + stream >> line >> column >> text; + m_document->insertText(KTextEditor::Cursor(line, column), QString::fromUtf8 (text.data (), text.size())); + + // track undo/redo cursor + if (firstEditInGroup) { + firstEditInGroup = false; + undoCursor = KTextEditor::Cursor(line, column); + } + redoCursor = KTextEditor::Cursor(line, column + text.size()); + + break; + } + case EA_RemoveText: { + if (!editRunning) { + brokenSwapFile = true; + break; + } + + int line, startColumn, endColumn; + stream >> line >> startColumn >> endColumn; + m_document->removeText (KTextEditor::Range(KTextEditor::Cursor(line, startColumn), KTextEditor::Cursor(line, endColumn))); + + // track undo/redo cursor + if (firstEditInGroup) { + firstEditInGroup = false; + undoCursor = KTextEditor::Cursor(line, endColumn); + } + redoCursor = KTextEditor::Cursor(line, startColumn); + + break; + } + default: { + kWarning( 13020 ) << "Unknown type:" << type; + } + } + } + + // balanced editStart and editEnd? + if (editRunning) { + brokenSwapFile = true; + m_document->editEnd(); + } + + // warn the user if the swap file is not complete + if (brokenSwapFile) { + kWarning ( 13020 ) << "Some data might be lost"; + } else { + // set sane final cursor, if possible + KTextEditor::View * view = m_document->activeView(); + redoCursor = m_document->undoManager()->lastRedoCursor(); + if (view && redoCursor.isValid()) { + view->setCursorPosition(redoCursor); + } + } + + // reconnect the signals + setTrackingEnabled(true); + + return true; +} + +void SwapFile::fileSaved(const QString&) +{ + m_needSync = false; + + // remove old swap file (e.g. if a file A was "saved as" B) + removeSwapFile(); + + // set the name for the new swap file + updateFileName(); +} + +void SwapFile::startEditing () +{ + // no swap file, no work + if (m_swapfile.fileName().isEmpty()) + return; + + // if swap file doesn't exists, open it in WriteOnly mode + // if it does, append the data to the existing swap file, + // in case you recover and start editing again + if (!m_swapfile.exists()) { + // TODO set file as read-only + m_swapfile.open(QIODevice::WriteOnly); + m_stream.setDevice(&m_swapfile); + + // write file header + m_stream << QByteArray (swapFileVersionString); + + // write md5 digest + m_stream << m_document->digest (); + } else if (m_stream.device() == 0) { + m_swapfile.open(QIODevice::Append); + m_stream.setDevice(&m_swapfile); + } + + // format: qint8 + m_stream << EA_StartEditing; +} + +void SwapFile::finishEditing () +{ + // skip if not open + if (!m_swapfile.isOpen ()) + return; + + // write the file to the disk every 15 seconds + // skip this if we disabled forced syncing + if (!m_document->config()->swapFileNoSync() && !syncTimer()->isActive()) + syncTimer()->start(15000); + + // format: qint8 + m_stream << EA_FinishEditing; + m_swapfile.flush(); +} + +void SwapFile::wrapLine (const KTextEditor::Cursor &position) +{ + // skip if not open + if (!m_swapfile.isOpen ()) + return; + + // format: qint8, int, int + m_stream << EA_WrapLine << position.line() << position.column(); + + m_needSync = true; +} + +void SwapFile::unwrapLine (int line) +{ + // skip if not open + if (!m_swapfile.isOpen ()) + return; + + // format: qint8, int + m_stream << EA_UnwrapLine << line; + + m_needSync = true; +} + +void SwapFile::insertText (const KTextEditor::Cursor &position, const QString &text) +{ + // skip if not open + if (!m_swapfile.isOpen ()) + return; + + // format: qint8, int, int, bytearray + m_stream << EA_InsertText << position.line() << position.column() << text.toUtf8 (); + + m_needSync = true; +} + +void SwapFile::removeText (const KTextEditor::Range &range) +{ + // skip if not open + if (!m_swapfile.isOpen ()) + return; + + // format: qint8, int, int, int + Q_ASSERT (range.start().line() == range.end().line()); + m_stream << EA_RemoveText + << range.start().line() << range.start().column() + << range.end().column(); + + m_needSync = true; +} + +bool SwapFile::shouldRecover() const +{ + // should not recover if the file has already recovered in another view + if (m_recovered) + return false; + + return !m_swapfile.fileName().isEmpty() && m_swapfile.exists() && m_stream.device() == 0; +} + +void SwapFile::discard() +{ + m_document->setReadWrite(true); + removeSwapFile(); + + // discard can also be called through the KTE::RecoveryInterface. + // Make sure, the message is hidden in this case as well. + if (m_swapMessage) + m_swapMessage->deleteLater(); +} + +void SwapFile::removeSwapFile() +{ + if (!m_swapfile.fileName().isEmpty() && m_swapfile.exists()) { + m_stream.setDevice(0); + m_swapfile.close(); + m_swapfile.remove(); + } +} + +bool SwapFile::updateFileName() +{ + // first clear filename + m_swapfile.setFileName (""); + + // get the new path + QString path = fileName(); + if (path.isNull()) + return false; + + m_swapfile.setFileName(path); + return true; +} + +QString SwapFile::fileName() +{ + const KUrl &url = m_document->url(); + if (url.isEmpty() || !url.isLocalFile()) + return QString(); + + QString path = url.toLocalFile(); + int poz = path.lastIndexOf(QDir::separator()); + path.insert(poz+1, "."); + path.append(".kate-swp"); + + return path; +} + +QTimer* SwapFile::syncTimer() +{ + if (s_timer == 0) { + s_timer = new QTimer(QApplication::instance()); + s_timer->setSingleShot(true); + } + + return s_timer; +} + +void SwapFile::writeFileToDisk() +{ + if (m_needSync) { + m_needSync = false; + + // ensure that the file is written to disk + #ifdef HAVE_FDATASYNC + fdatasync (m_swapfile.handle()); + #else + fsync (m_swapfile.handle()); + #endif + } +} + +void SwapFile::showSwapFileMessage() +{ + m_swapMessage = new KTextEditor::Message(i18n("The file was not closed properly."), + KTextEditor::Message::Warning); + m_swapMessage->setWordWrap(true); + + QAction* diffAction = new QAction(KIcon("split"), i18n("View Changes"), 0); + QAction* recoverAction = new QAction(KIcon("edit-redo"), i18n("Recover Data"), 0); + QAction* discardAction = new QAction(KStandardGuiItem::discard().icon(), i18n("Discard"), 0); + + m_swapMessage->addAction(diffAction, false); + m_swapMessage->addAction(recoverAction); + m_swapMessage->addAction(discardAction); + + connect(diffAction, SIGNAL(triggered()), SLOT(showDiff())); + connect(recoverAction, SIGNAL(triggered()), SLOT(recover()), Qt::QueuedConnection); + connect(discardAction, SIGNAL(triggered()), SLOT(discard()), Qt::QueuedConnection); + + m_document->postMessage(m_swapMessage); +} + +void SwapFile::showDiff() +{ + // the diff creator deletes itself thorugh deleteLater() when it's done + SwapDiffCreator* diffCreator = new SwapDiffCreator(this); + diffCreator->viewDiff(); +} + +} + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/swapfile/kateswapfile.h b/kate/part/swapfile/kateswapfile.h new file mode 100644 index 00000000..f995dac8 --- /dev/null +++ b/kate/part/swapfile/kateswapfile.h @@ -0,0 +1,114 @@ +/* This file is part of the Kate project. + * + * Copyright (C) 2010 Dominik Haumann + * Copyright (C) 2010 Diana-Victoria Tiriplica + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef KATE_SWAPFILE_H +#define KATE_SWAPFILE_H + +#include +#include +#include +#include + +#include "katepartinterfaces_export.h" +#include "katetextbuffer.h" +#include "katebuffer.h" +#include "katedocument.h" + +#include + +class KateView; + +namespace Kate { + +/** + * Class for tracking editing actions. + * In case Kate crashes, this can be used to replay all edit actions to + * recover the lost data. + */ +class KATEPARTINTERFACES_EXPORT SwapFile : public QObject +{ + Q_OBJECT + + public: + explicit SwapFile(KateDocument* document); + ~SwapFile(); + bool shouldRecover() const; + + void fileClosed (); + QString fileName(); + + KateDocument* document(); + + private: + void setTrackingEnabled(bool trackingEnabled); + void removeSwapFile(); + bool updateFileName(); + bool isValidSwapFile(QDataStream& stream, bool checkDigest) const; + + private: + KateDocument *m_document; + bool m_trackingEnabled; + + protected Q_SLOTS: + void fileSaved(const QString& filename); + void fileLoaded(const QString &filename); + void modifiedChanged(); + + void startEditing (); + void finishEditing (); + + void wrapLine (const KTextEditor::Cursor &position); + void unwrapLine (int line); + void insertText (const KTextEditor::Cursor &position, const QString &text); + void removeText (const KTextEditor::Range &range); + + public Q_SLOTS: + void discard(); + void recover(); + bool recover(QDataStream&, bool checkDigest = true); + void configChanged(); + + private: + QDataStream m_stream; + QFile m_swapfile; + bool m_recovered; + bool m_needSync; + static QTimer *s_timer; + + protected Q_SLOTS: + void writeFileToDisk(); + + private: + QTimer* syncTimer(); + + public Q_SLOTS: + void showSwapFileMessage(); + void showDiff(); + + private: + QPointer m_swapMessage; +}; + +} + +#endif // KATE_SWAPFILE_H + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/syntax/data/CMakeLists.txt b/kate/part/syntax/data/CMakeLists.txt new file mode 100644 index 00000000..cbf9f574 --- /dev/null +++ b/kate/part/syntax/data/CMakeLists.txt @@ -0,0 +1,30 @@ +########### install files ############### + +add_custom_target(GeneratePhpXmlFiles + DEPENDS + ${CMAKE_CURRENT_SOURCE_DIR}/css-php.xml + ${CMAKE_CURRENT_SOURCE_DIR}/html-php.xml + ${CMAKE_CURRENT_SOURCE_DIR}/javascript-php.xml +) + + +macro(GENERATE_PHP_XML targetFile srcFile) + add_custom_command( + OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/${targetFile} + COMMAND ${PERL_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/generate-php.pl < ${CMAKE_CURRENT_SOURCE_DIR}/${srcFile} > ${CMAKE_CURRENT_SOURCE_DIR}/${targetFile} + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${srcFile} + ) +endmacro(GENERATE_PHP_XML) + +generate_php_xml(javascript-php.xml javascript.xml) +generate_php_xml(css-php.xml css.xml) +generate_php_xml(html-php.xml html.xml) + +file(GLOB HIGHLIGHTERS "${CMAKE_CURRENT_SOURCE_DIR}/*.xml") + +install( + FILES + language.dtd syntax.template + ${HIGHLIGHTERS} + DESTINATION ${KDE4_DATA_INSTALL_DIR}/katepart/syntax +) diff --git a/kate/part/syntax/data/abap.xml b/kate/part/syntax/data/abap.xml new file mode 100644 index 00000000..40e24ad6 --- /dev/null +++ b/kate/part/syntax/data/abap.xml @@ -0,0 +1,285 @@ + + + + + + + + ADD + ADJACENT + ALL + AND + APPEND + APPENDING + AS + ASCENDING + AT + BEGIN + BETWEEN + BINARY + BLOCK + BY + CASE + CENTERED + CHAIN + CHANGING + CHECK + CHECKBOX + CLEAR + COL_BACKGROUND + COL_HEADING + COL_NORMAL + COL_TOTAL + COLOR + COMMENT + COMMIT + COMPARING + COMPUTE + CONCATENATE + CONDENSE + CONSTANTS + CONTINUE + CONTROLS + COUNTRY + DATA + DECIMALS + DEFAULT + DELETE + DELETING + DESCENDING + DESCRIBE + DO + DUPLICATES + EDIT + ELSE + ELSEIF + END + ENDCASE + ENDCHAIN + ENDDO + ENDIF + ENDLOOP + ENDMODULE + ENDSELECT + ENDWHILE + ENTRIES + EQ + EXCEPTIONS + EXCLUDING + EXIT + EXIT-COMMAND + EXPORT + EXPORTING + FIELD + FIRST + FOR + FORMAT + FRAME + FREE + FROM + GE + GROUP + GT + HEADER + HEADING + HIDE + HOTSPOT + ID + IF + IMPORT + IMPORTING + IN + INDEX + INITIAL + INNER + INPUT + INSERT + INTENSIFIED + INTERVALS + INTO + IS + JOIN + KEY + LE + LEAVE + LEFT + LEFT-JUSTIFIED + LIKE + LINE + LINE-COUNT + LINES + LINES + LINE-SIZE + LIST-PROCESSING + LOOP + LT + MASK + MEMORY + MESSAGE + MESSAGE-ID + MOD + MODIFY + MODULE + MOVE + MOVE-CORRESPONDING + NE + NEW-LINE + NEW-PAGE + NO + NO-EXTENSION + NO-GAP + NO-SCROLLING + NOT + NO-ZERO + NUMBER + OBLIGATORY + OCCURS + OF + OFF + ON + OR + OTHERS + OUTPUT + PAGE + PARAMETER + PARAMETERS + PERFORM + PF-STATUS + POS_HIGH + POS_LOW + POSITION + PROGRAM + RADIOBUTTON + RANGES + READ + REFRESH + REPORT + RESERVE + RESET + RIGHT + RIGHT-JUSTIFIED + ROLLBACK + ROWS + SCREEN + SCREEN-GROUP1 + SCREEN-GROUP2 + SCREEN-GROUP3 + SCREEN-GROUP4 + SCREEN-GROUP5 + SCREEN-INPUT + SCREEN-INTENSIFIED + SEARCH + SELECT + SELECTION + SELECTION-SCREEN + SELECT-OPTIONS + SEPARATED + SET + SHIFT + SINGLE + SKIP + SORT + SPACE + SPLIT + STANDARD + STARTING + STOP + STRLEN + STRUCTURE + SUBTRACT + SY-CUCOL + SY-DATUM + SY-DYNNR + SY-LINSZ + SY-LOOPC + SY-LSIND + SY-MSGID + SY-MSGTY + SY-MSGV1 + SY-MSGV2 + SY-MSGV3 + SY-MSGV4 + SY-PAGNO + SY-REPID + SY-STEPL + SY-SUBRC + SY-TABIX + SY-TCODE + SY-TMAXL + SY-UCOMM + SY-ULINE + SY-UNAME + SY-UZEIT + SY-VLINE + TABLE + TABLES + TABLEVIEW + TIMES + TITLE + TITLEBAR + TO + TRAILING + TRANSPORTING + TYPE + TYPE-POOLS + TYPES + ULINE + UP + UPDATE + USING + VALUE + WHEN + WHERE + WHILE + WITH + WORK + WRITE + AFTER + BEFORE + CALL + DURING + ENDFORM + END-OF-SELECTION + FORM + FUNCTION + INCLUDE + LINE-SELECTION + PROCESS + START-OF-SELECTION + TOP-OF-PAGE + TRANSACTION + USER-COMMAND + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/abc.xml b/kate/part/syntax/data/abc.xml new file mode 100644 index 00000000..8d5ff163 --- /dev/null +++ b/kate/part/syntax/data/abc.xml @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/actionscript.xml b/kate/part/syntax/data/actionscript.xml new file mode 100644 index 00000000..cea26395 --- /dev/null +++ b/kate/part/syntax/data/actionscript.xml @@ -0,0 +1,334 @@ + + + + + + + _accProps + _focusrect + _global + _highquality + _level + _parent + _quality + _root + _soundbuftime + maxscroll + scroll + this + + + + asfunction + call + chr + clearInterval + duplicateMovieClip + escape + eval + fscommand + getProperty + getTimer + getURL + getVersion + gotoAndPlay + gotoAndStop + ifFrameLoaded + int + isFinite + isNaN + length + loadMovie + loadMovieNum + loadVariables + loadVariablesNum + mbchr + mblength + mbord + mbsubstring + nextFrame + nextScene + on + onClipEvent + ord + parseFloat + parseInt + play + prevFrame + prevScene + print + printAsBitmap + printAsBitmapNum + printNum + random + removeMovieClip + setInterval + setProperty + showRedrawRegions + startDrag + stop + stopAllSounds + stopDrag + substring + targetPath + tellTarget + toggleHighQuality + trace + typeof + unescape + unloadMovie + unloadMovieNum + updateAfterEvent + + + + Accessibility + Accordion + Alert + Binding + Button + Camera + CellRenderer + CheckBox + Collection + Color + ComboBox + ComponentMixins + ContextMenu + ContextMenuItem + CustomActions + CustomFormatter + CustomValidator + DataGrid + DataHolder + DataProvider + DataSet + DataType + Date + DateChooser + DateField + Delta + DeltaItem + DeltaPacket + DepthManager + EndPoint + Error + FaultEvent + FocusManager + Form + Function + Iterator + Key + Label + List + LoadVars + Loader + LocalConnection + Log + Math + Media + Menu + MenuBar + Microphone + Mouse + MovieClip + MovieClipLoader + NetConnection + NetStream + Number + NumericStepper + PendingCall + PopUpManager + PrintJob + ProgressBar + RDBMSResolver + RadioButton + RelayResponder + SOAPCall + Screen + ScrollPane + Selection + SharedObject + Slide + Sound + Stage + StyleManager + System + TextArea + TextField + TextFormat + TextInput + TextSnapshot + TransferObject + Tree + TreeDataProvider + TypedValue + UIComponent + UIEventDispatcher + UIObject + Video + WebService + WebServiceConnector + Window + XML + XMLConnector + XUpdateResolver + + + + add + and + break + case + catch + class + continue + default + delete + do + dynamic + else + eq + extends + finally + for + function + ge + get + gt + if + implements + import + in + instanceof + interface + intrinsic + le + lt + ne + new + not + or + private + public + return + set + static + switch + throw + try + var + void + while + with + + + + false + Infinity + -Infinity + NaN + newline + null + true + undefined + + + + Array + Boolean + Number + Object + String + Void + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/kate/part/syntax/data/ada.xml b/kate/part/syntax/data/ada.xml new file mode 100644 index 00000000..f2f753c5 --- /dev/null +++ b/kate/part/syntax/data/ada.xml @@ -0,0 +1,202 @@ + + + + + + abort + abs + abstract + accept + access + aliased + all + and + array + at + begin + body + constant + declare + delay + delta + digits + do + else + elsif + end + entry + exception + exit + for + function + generic + goto + in + interface + is + limited + mod + new + not + null + of + or + others + out + overriding + package + pragma + private + procedure + protected + raise + range + rem + record + renames + requeue + return + reverse + separate + subtype + tagged + task + terminate + then + type + until + use + when + while + with + xor + + + all_calls_remote + assert + assertion_policy + asynchronous + atomic + atomic_components + attach_handler + controlled + convention + detect_blocking + discard_names + elaborate + elaborate_all + elaborate_body + export + import + inline + inspection_point + interrupt_handler + interrupt_priority + linker_options + list + locking_policy + no_return + normalize_scalars + optimize + pack + page + partition_elaboration_policy + preelaborable_initialization + preelaborate + priority + priority_specific_dispatching + profile + pure + queuing_policy + relative_deadline + remote_call_interface + remote_types + restrictions + reviewable + shared_passive + storage_size + suppress + task_dispatching_policy + unchecked_union + unsuppress + volatile + volatile_components + + + boolean + char + float + integer + long_float + long_integer + long_long_float + long_long_integer + short_float + short_integer + string + wide_string + wide_char + wide_wide_char + wide_wide_string + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/agda.xml b/kate/part/syntax/data/agda.xml new file mode 100644 index 00000000..0f39ea81 --- /dev/null +++ b/kate/part/syntax/data/agda.xml @@ -0,0 +1,107 @@ + + + +]> + + + + abstract + codata + coinductive + constructor + data + field + forall + hiding + import + in + inductive + infix + infixl + infixr + let + open + pattern + postulate + primitive + private + public + module + mutual + quote + quoteGoal + quoteTerm + record + renaming + rewrite + syntax + to + unquote + using + where + with + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/ahdl.xml b/kate/part/syntax/data/ahdl.xml new file mode 100644 index 00000000..cf0ab465 --- /dev/null +++ b/kate/part/syntax/data/ahdl.xml @@ -0,0 +1,145 @@ + + + + + + assert + bidir + bits + buried + case + clique + connected_pins + constant + defaults + define + design + device + else + elsif + for + function + generate + gnd + help_id + in + include + input + is + machine + node + of + options + others + output + parameters + returns + states + subdesign + then + title + to + tri_state_node + variable + vcc + when + with + + + carry + cascade + dffe + dff + exp + global + jkffe + jkff + latch + lcell + mcell + memory + opendrn + soft + srffe + srff + tffe + tff + tri + wire + x + + + not + and + nand + or + nor + xor + xnor + mod + div + log2 + used + ceil + floor + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/ahk.xml b/kate/part/syntax/data/ahk.xml new file mode 100644 index 00000000..8539e651 --- /dev/null +++ b/kate/part/syntax/data/ahk.xml @@ -0,0 +1,1139 @@ + + + + + + + if + ifequal + ifexist + ifgreater + ifgreaterorequal + ifinstring + ifless + iflessorequal + ifmsgbox + ifnotequal + ifnotexist + ifnotinstring + ifwinactive + ifwinexist + ifwinnotactive + ifwinnotexist + break + continue + else + exit + exitapp + gosub + goto + loop + onexit + pause + repeat + return + settimer + sleep + suspend + static + global + local + byref + while + until + for + + + autotrim + blockinput + clipwait + control + controlclick + controlfocus + controlget + controlgetfocus + controlgetpos + controlgettext + controlmove + controlsend + controlsendraw + controlsettext + coordmode + critical + detecthiddentext + detecthiddenwindows + drive + driveget + drivespacefree + edit + endrepeat + envadd + envdiv + envget + envmult + envset + envsub + envupdate + fileappend + filecopy + filecopydir + filecreatedir + filecreateshortcut + filedelete + filegetattrib + filegetshortcut + filegetsize + filegettime + filegetversion + fileinstall + filemove + filemovedir + fileread + filereadline + filerecycle + filerecycleempty + fileremovedir + fileselectfile + fileselectfolder + filesetattrib + filesettime + formattime + getkeystate + groupactivate + groupadd + groupclose + groupdeactivate + gui + guicontrol + guicontrolget + hideautoitwin + hotkey + imagesearch + inidelete + iniread + iniwrite + input + inputbox + keyhistory + keywait + listhotkeys + listlines + listvars + menu + mouseclick + mouseclickdrag + mousegetpos + mousemove + msgbox + outputdebug + pixelgetcolor + pixelsearch + postmessage + process + progress + random + regdelete + regread + regwrite + reload + run + runas + runwait + send + sendevent + sendinput + sendmessage + sendmode + sendplay + sendraw + setbatchlines + setcapslockstate + setcontroldelay + setdefaultmousespeed + setenv + setformat + setkeydelay + setmousedelay + setnumlockstate + setscrolllockstate + setstorecapslockmode + settitlematchmode + setwindelay + setworkingdir + shutdown + sort + soundbeep + soundget + soundgetwavevolume + soundplay + soundset + soundsetwavevolume + splashimage + splashtextoff + splashtexton + splitpath + statusbargettext + statusbarwait + stringcasesense + stringgetpos + stringleft + stringlen + stringlower + stringmid + stringreplace + stringright + stringsplit + stringtrimleft + stringtrimright + stringupper + sysget + thread + tooltip + transform + traytip + urldownloadtofile + winactivate + winactivatebottom + winclose + winget + wingetactivestats + wingetactivetitle + wingetclass + wingetpos + wingettext + wingettitle + winhide + winkill + winmaximize + winmenuselectitem + winminimize + winminimizeall + winminimizeallundo + winmove + winrestore + winset + winsettitle + winshow + winwait + winwaitactive + winwaitclose + winwaitnotactive + fileencoding + + + abs + acos + asc + asin + atan + ceil + chr + cos + dllcall + exp + fileexist + floor + getkeystate + numget + numput + registercallback + il_add + il_create + il_destroy + instr + islabel + isfunc + ln + log + lv_add + lv_delete + lv_deletecol + lv_getcount + lv_getnext + lv_gettext + lv_insert + lv_insertcol + lv_modify + lv_modifycol + lv_setimagelist + mod + onmessage + round + regexmatch + regexreplace + sb_seticon + sb_setparts + sb_settext + sin + sqrt + strlen + substr + tan + tv_add + tv_delete + tv_getchild + tv_getcount + tv_getnext + tv_get + tv_getparent + tv_getprev + tv_getselection + tv_gettext + tv_modify + varsetcapacity + winactive + winexist + trim + ltrim + rtrim + fileopen + strget + strput + object + array + isobject + objinsert + objremove + objminindex + objmaxindex + objsetcapacity + objgetcapacity + objgetaddress + objnewenum + objaddref + objrelease + objhaskey + objclone + _insert + _remove + _minindex + _maxindex + _setcapacity + _getcapacity + _getaddress + _newenum + _addref + _release + _haskey + _clone + comobjcreate + comobjget + comobjconnect + comobjerror + comobjactive + comobjenwrap + comobjunwrap + comobjparameter + comobjmissing + comobjtype + comobjvalue + comobjarray + comobjquery + comobjflags + + + allowsamelinecomments + clipboardtimeout + commentflag + errorstdout + escapechar + hotkeyinterval + hotkeymodifiertimeout + hotstring + if + iftimeout + ifwinactive + ifwinexist + include + includeagain + installkeybdhook + installmousehook + keyhistory + ltrim + maxhotkeysperinterval + maxmem + maxthreads + maxthreadsbuffer + maxthreadsperhotkey + menumaskkey + noenv + notrayicon + persistent + singleinstance + usehook + warn + winactivateforce + + + shift + lshift + rshift + alt + lalt + ralt + control + lcontrol + rcontrol + ctrl + lctrl + rctrl + lwin + rwin + appskey + altdown + altup + shiftdown + shiftup + ctrldown + ctrlup + lwindown + lwinup + rwindown + rwinup + lbutton + rbutton + mbutton + wheelup + wheeldown + xbutton1 + xbutton2 + joy1 + joy2 + joy3 + joy4 + joy5 + joy6 + joy7 + joy8 + joy9 + joy10 + joy11 + joy12 + joy13 + joy14 + joy15 + joy16 + joy17 + joy18 + joy19 + joy20 + joy21 + joy22 + joy23 + joy24 + joy25 + joy26 + joy27 + joy28 + joy29 + joy30 + joy31 + joy32 + joyx + joyy + joyz + joyr + joyu + joyv + joypov + joyname + joybuttons + joyaxes + joyinfo + space + tab + enter + escape + esc + backspace + bs + delete + del + insert + ins + pgup + pgdn + home + end + up + down + left + right + printscreen + ctrlbreak + pause + scrolllock + capslock + numlock + numpad0 + numpad1 + numpad2 + numpad3 + numpad4 + numpad5 + numpad6 + numpad7 + numpad8 + numpad9 + numpadmult + numpadadd + numpadsub + numpaddiv + numpaddot + numpaddel + numpadins + numpadclear + numpadup + numpaddown + numpadleft + numpadright + numpadhome + numpadend + numpadpgup + numpadpgdn + numpadenter + f1 + f2 + f3 + f4 + f5 + f6 + f7 + f8 + f9 + f10 + f11 + f12 + f13 + f14 + f15 + f16 + f17 + f18 + f19 + f20 + f21 + f22 + f23 + f24 + browser_back + browser_forward + browser_refresh + browser_stop + browser_search + browser_favorites + browser_home + volume_mute + volume_down + volume_up + media_next + media_prev + media_stop + media_play_pause + launch_mail + launch_media + launch_app1 + launch_app2 + blind + click + raw + wheelleft + wheelright + + + a_ahkpath + a_ahkversion + a_appdata + a_appdatacommon + a_autotrim + a_batchlines + a_caretx + a_carety + a_computername + a_controldelay + a_cursor + a_dd + a_ddd + a_dddd + a_defaultmousespeed + a_desktop + a_desktopcommon + a_detecthiddentext + a_detecthiddenwindows + a_endchar + a_eventinfo + a_exitreason + a_formatfloat + a_formatinteger + a_gui + a_guievent + a_guicontrol + a_guicontrolevent + a_guiheight + a_guiwidth + a_guix + a_guiy + a_hour + a_iconfile + a_iconhidden + a_iconnumber + a_icontip + a_index + a_ipaddress1 + a_ipaddress2 + a_ipaddress3 + a_ipaddress4 + a_isadmin + a_iscompiled + a_issuspended + a_keydelay + a_language + a_lasterror + a_linefile + a_linenumber + a_loopfield + a_loopfileattrib + a_loopfiledir + a_loopfileext + a_loopfilefullpath + a_loopfilelongpath + a_loopfilename + a_loopfileshortname + a_loopfileshortpath + a_loopfilesize + a_loopfilesizekb + a_loopfilesizemb + a_loopfiletimeaccessed + a_loopfiletimecreated + a_loopfiletimemodified + a_loopreadline + a_loopregkey + a_loopregname + a_loopregsubkey + a_loopregtimemodified + a_loopregtype + a_mday + a_min + a_mm + a_mmm + a_mmmm + a_mon + a_mousedelay + a_msec + a_mydocuments + a_now + a_nowutc + a_numbatchlines + a_ostype + a_osversion + a_priorhotkey + a_programfiles + a_programs + a_programscommon + a_screenheight + a_screenwidth + a_scriptdir + a_scriptfullpath + a_scriptname + a_sec + a_space + a_startmenu + a_startmenucommon + a_startup + a_startupcommon + a_stringcasesense + a_tab + a_temp + a_thishotkey + a_thismenu + a_thismenuitem + a_thismenuitempos + a_tickcount + a_timeidle + a_timeidlephysical + a_timesincepriorhotkey + a_timesincethishotkey + a_titlematchmode + a_titlematchmodespeed + a_username + a_wday + a_windelay + a_windir + a_workingdir + a_yday + a_year + a_yweek + a_yyyy + clipboard + clipboardall + comspec + errorlevel + programfiles + true + false + a_thisfunc + a_thislabel + a_ispaused + a_iscritical + a_isunicode + a_ptrsize + + + ltrim + rtrim + join + ahk_id + ahk_pid + ahk_class + ahk_group + processname + minmax + controllist + statuscd + filesystem + setlabel + alwaysontop + mainwindow + nomainwindow + useerrorlevel + altsubmit + hscroll + vscroll + imagelist + wantctrla + wantf2 + vis + visfirst + wantreturn + backgroundtrans + minimizebox + maximizebox + sysmenu + toolwindow + exstyle + check3 + checkedgray + readonly + notab + lastfound + lastfoundexist + alttab + shiftalttab + alttabmenu + alttabandmenu + alttabmenudismiss + controllisthwnd + hwnd + deref + pow + bitnot + bitand + bitor + bitxor + bitshiftleft + bitshiftright + sendandmouse + mousemove + mousemoveoff + hkey_local_machine + hkey_users + hkey_current_user + hkey_classes_root + hkey_current_config + hklm + hku + hkcu + hkcr + hkcc + reg_sz + reg_expand_sz + reg_multi_sz + reg_dword + reg_qword + reg_binary + reg_link + reg_resource_list + reg_full_resource_descriptor + reg_resource_requirements_list + reg_dword_big_endian + regex + pixel + mouse + screen + relative + rgb + low + belownormal + normal + abovenormal + high + realtime + between + contains + in + is + integer + float + number + digit + xdigit + integerfast + floatfast + alpha + upper + lower + alnum + time + date + not + or + and + topmost + top + bottom + transparent + transcolor + redraw + region + id + idlast + count + list + capacity + eject + lock + unlock + label + serial + type + status + seconds + minutes + hours + days + read + parse + logoff + close + error + single + shutdown + menu + exit + reload + tray + add + rename + check + uncheck + togglecheck + enable + disable + toggleenable + default + nodefault + standard + nostandard + color + delete + deleteall + icon + noicon + tip + click + show + edit + progress + hotkey + text + picture + pic + groupbox + button + checkbox + radio + dropdownlist + ddl + combobox + statusbar + treeview + listbox + listview + datetime + monthcal + updown + slider + tab + tab2 + iconsmall + tile + report + sortdesc + nosort + nosorthdr + grid + hdr + autosize + range + xm + ym + ys + xs + xp + yp + font + resize + owner + submit + nohide + minimize + maximize + restore + noactivate + na + cancel + destroy + center + margin + owndialogs + guiescape + guiclose + guisize + guicontextmenu + guidropfiles + tabstop + section + wrap + border + top + bottom + buttons + expand + first + lines + number + uppercase + lowercase + limit + password + multi + group + background + bold + italic + strike + underline + norm + theme + caption + delimiter + flash + style + checked + password + hidden + left + right + center + section + move + focus + hide + choose + choosestring + text + pos + enabled + disabled + visible + notimers + interrupt + priority + waitclose + unicode + tocodepage + fromcodepage\ + yes + no + ok + cancel + abort + retry + ignore + force + on + off + all + send + wanttab + monitorcount + monitorprimary + monitorname + monitorworkarea + pid + base + __get + __set + __call + __delete + useunsetlocal + useunsetglobal + useenv + localsameasglobal + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/alert.xml b/kate/part/syntax/data/alert.xml new file mode 100644 index 00000000..3e1ac03e --- /dev/null +++ b/kate/part/syntax/data/alert.xml @@ -0,0 +1,78 @@ + + + + + diff --git a/kate/part/syntax/data/alert_indent.xml b/kate/part/syntax/data/alert_indent.xml new file mode 100644 index 00000000..3c633ad1 --- /dev/null +++ b/kate/part/syntax/data/alert_indent.xml @@ -0,0 +1,40 @@ + + + + diff --git a/kate/part/syntax/data/ample.xml b/kate/part/syntax/data/ample.xml new file mode 100644 index 00000000..d5298f15 --- /dev/null +++ b/kate/part/syntax/data/ample.xml @@ -0,0 +1,3813 @@ + + + + + + + break + builtin + case + continue + do + else + extern + local + for + function + if + return + switch + while + + + void + undefined + $abort_enable + $dofile_arg1 + $dofile_arg2 + $dofile_arg3 + $dofile_arg4 + $dofile_arg5 + $dofile_arg6 + $dofile_arg7 + $dofile_arg8 + $dofile_arg9 + $dofile_arg10 + $dofile_arg11 + $dofile_arg12 + $dofile_arg13 + $dofile_arg14 + $dofile_arg15 + $dofile_arg16 + $dofile_arg17 + $dofile_arg18 + $dofile_arg19 + $dofile_arg20 + $dofile_arg_count + $dofile_result + false + off + on + pi + $stderr + $stdin + $stdout + true + two_pi + + + + + + abs + acos + $add_complex + $add_status_args + asin + atan + atan2 + $bad_status + ceil + $clear_file_error + $close_file + $complex_imaginary + $complex_real + $conjugate_complex + $constrain_value + cos + cosh + cot + $create_complex + $create_string_registry + $create_vector + csc + $current_user + $cvt_exist_file + $cvt_read_variable + $cvt_type + $cvt_write_variable + $date + deg + $divide_complex + $dofile + $e + $eof + exp + $expand_rest + $f + $file_error + $file_exist + $file_pos + $file_status + floor + $flush_file + $format + $free_stream_id + $function_help + $function_ref_help + $function_signature + $g + $generate_rand + $get_ample_status + $get_app_name + $get_app_ver + $get_env + $i + $integer_divide + length + $list_overwritten_functions + $load_library + log + log10 + $lower_string + $magnitude_complex + $multiply_complex + $n + $number_string + $open_file + $ord_to_string + $phase_complex + $pop_ample_status + pow + $qsort + rad + $raise_status + $read_file + $reads_file + $real_time + $register_alias + $register_args + $register_command + $resolve_mgc_path + $round + $round_prec + $s + sec + $seek_file + $set_function_status + $set_rand + $set_status + $set_thousands + $set_transcript_mode + sin + sinh + sqrt + $sscanf + $strcat + $strftime + $string_locate + $string_status + $string_to_ord + $subtract_complex + $suspend + $sys_time + $system + tan + tanh + $time + $traceback + $truncate + $type + $undefine_id + $upper_string + $vector_count_range + $vector_element_divide + $vector_element_multiply + $vector_find + $vector_histogram + $vector_integrate + $vector_max + $vector_min + $vector_search + $vector_slope + $vector_sum + $vendor_cpu + $where_is + $write_file + $writeln_file + $writes_file + + + $acquire_license + $activate_net + $activate_net_by_handle + $activate_net_by_name + $activate_port + $activate_port_by_handle + $activate_port_by_name + $add_cell + $add_contacts + $add_device + $add_fp_shape + $add_group_property + $add_metal + $add_multiple_text + $add_net_members_to_ports + $add_overflow + $add_overflow_by_handle + $add_panel + $add_path + $add_path_device + $add_point_device + $add_property + $add_property_group_members + $add_property_group_selections + $add_property_text + $add_route + $add_row + $add_rows_by_area + $add_ruler + $add_shape + $add_shape_device + $add_text + $add_text_on_ports + $add_to_library + $add_to_net + $add_to_port + $add_via + $align + $apply_print_vector_attributes + $attach_library + $autofloorplan + $autoplace_blocks + $autoplace_corner_cells + $autoplace_group + $autoplace_pins + $autoplace_ports + $autoplace_power_vias + $autoplace_standard_cells + $autoroute_all + $autoroute_nets + $autoroute_overflow + $backannotate_net_parameters + $build_hotplot_setup + $build_lib + $build_ports + $build_ports_shapes + $build_ports_text_location + $build_power_strap_shapes + $build_power_straps + $build_read_gdsii_optfile + $change_array + $change_aspect + $change_cursor_status + $change_device + $change_from_object_template + $change_group_property + $change_layer + $change_net + $change_object_attributes + $change_object_template + $change_overflow + $change_overflow_by_handle + $change_path + $change_port + $change_property + $change_property_group_members + $change_property_group_selections + $change_property_text + $change_row + $change_text + $change_via_type + $change_window + $checkpoint_cell + $checkpoint_cell_by_name + $check_drc + $check_fn + $check_instances + $check_overflows + $check_polygons + $check_power_pins + $check_preconditions + $check_shorts_all + $check_shorts_selected + $clear_clipboard + $close_layout_library + $close_logic + $close_selection + $close_session + $close_window + $compact + $compose_groups + $compose_layer_geometries + $compose_layer_selections + $connect_instance + $convert_dracula + $copy + $copy_edit_hotkey_settings + $copy_relative + $copy_to_clipboard + $copy_to_fp_layer + $copy_to_layer + $create_cell + $create_def_from_layout_view + $create_layout_library + $create_layout_view + $create_layout_view_from_def + $create_layout_view_from_lef + $create_layout_view_from_verilog + $create_lef_from_layout_library + $create_lef_from_layout_view + $create_library + $create_process + $create_toolbar + $create_via_cell + $create_viewpoint + $cut + $cut_stretch + $deactivate + $define_hotkey + $define_layer_alias + $define_layer_name + $define_layer_set + $define_max_gate_width + $define_mos_site_type + $define_must_connect + $define_net_pair + $define_net_shield + $define_route_transform + $delete + $delete_area + $delete_connectivity + $delete_drc_all + $delete_drc_area + $delete_drc_check + $delete_drc_current + $delete_drc_point + $delete_drc_scan + $delete_from_library + $delete_group_property + $delete_lvs_results + $delete_panel + $delete_property + $delete_property_group_members + $delete_property_group_selections + $delete_routing + $delete_ruler + $delete_rulers_all + $detach_library + $display_channels + $does_cell_exist + $edit_library + $edit_process + $edit_process_override + $enable_edit_layout_library + $export_drc_check + $extract_cell_connectivity + $extract_direct_distributed_parameters + $extract_direct_lumped_parameters + $extract_mask_distributed_parameters + $extract_mask_lumped_parameters + $fillet + $fillet_area + $filter_group + $fit_fp_shape + $flatten + $flatten_hierarchy + $flip + $flip_in_place + $flip_on_axis + $form_ic_action_buttons_gadget + $form_ic_color_paint_chip_gadget + $form_ic_display_gadget + $form_ic_layer_palette_gadget + $form_ic_patterns_list_box_gadget + $form_single_check_box_gadget + $fracture + $freeze_window + $get_absolute_points + $get_acap_info + $get_active_ic_window + $get_active_net + $get_active_port + $get_arc + $get_area + $get_area_estimate + $get_array_value + $get_auto_checkpoint + $get_basepoint + $get_bottom_orient_set + $get_boundary + $get_cap_extent + $get_cap_neg_pin + $get_cap_pos_pin + $get_cell_boundary + $get_cell_configuration + $get_cell_equivalents + $get_cell_info + $get_cell_list_dts + $get_cell_path + $get_cell_refs + $get_cell_xrefs + $get_circle + $get_closed_polygon + $get_closest_object_location + $get_closest_object_points + $get_compact_add_blkgs + $get_compact_jog_power + $get_compose_layer_geometries + $get_compose_layer_selections + $get_connectivity_layers + $get_containment + $get_context_contrast + $get_context_from_world + $get_crosshair_target_radius + $get_crosshair_target_style + $get_def_bus_bit_characters + $get_def_bus_bit_characters + $get_def_divider_character + $get_def_divider_character + $get_def_non_orthogonal + $set_def_non_orthogonal + $get_design_layers + $get_design_path + $get_design_refs + $get_dev_info + $get_dev_layer + $get_dev_rule + $get_device_alias + $get_device_iobj + $get_device_name + $get_drc_check_current_count + $get_drc_check_original_count + $get_drc_check_polygon_count + $get_drc_check_scan_count + $get_drc_current_result_check_name + $get_drc_current_result_number + $get_drc_current_result_points + $get_drc_current_result_type + $get_drc_default_layer_directory_name + $get_drc_default_summary_report_file_name + $get_drc_results_database_check_count + $get_drc_results_database_check_names + $get_drc_results_database_nonempty_check_names + $get_drc_results_database_result_count + $get_drc_results_database_scan_check_names + $get_drc_results_database_scan_result_count + $get_endangered_nets + $get_externals_info + $get_fp_compute_num_rows + $get_fp_compute_route_area_ratio + $get_fp_extend_row + $get_fractured_rectangles + $get_gadget_value + $get_graphic_preempt_list + $get_group_members + $get_group_names + $get_group_property_names + $get_highlight_count + $get_hotkey_settings + $get_ic_cell_windows + $get_ic_location + $get_ic_window_names + $get_instance_extent + $get_instance_fp_extent + $get_instance_version + $get_internal_row_layout + $get_internal_row_sequence + $get_layer_info + $get_layer_minimum_spacing + $get_layer_minimum_width + $get_layer_names + $get_layer_number + $get_layer_palette_height + $get_layers + $get_layout_counterpart + $get_layout_window + $get_lef_bus_bit_characters + $get_lef_bus_bit_characters + $get_lef_divider_character + $get_lef_divider_character + $get_lef_non_orthogonal + $set_lef_non_orthogonal + $get_lef_overlap_boundary + $set_lef_overlap_boundary + $get_left_orient_set + $get_library_cells + $get_licenses + $get_loaded_logic + $get_logic_counterpart + $get_mark + $get_mask_device_count + $get_mask_discrepancy_count + $get_mask_net_count + $get_mos_gate_pin + $get_mos_sd_extent + $get_mos_sd_pin + $get_nearest_edge + $get_net_members + $get_new_object_handles + $get_object_extent + $get_object_info + $get_outdated_cells + $get_outline + $get_panel_extent + $get_panel_names + $get_parameter_default + $get_peek_protected + $get_perimeter + $get_pid + $get_pin_members + $get_pins + $get_points + $get_port_members + $get_process + $get_process_override + $get_property_handles + $get_property_names + $get_property_value + $get_report_obj_types + $get_report_options + $get_res_extent + $get_res_neg_pin + $get_res_pos_pin + $get_reserved + $get_right_orient_set + $get_rotation_point + $get_router_add_blkgs + $get_router_align_cells + $get_router_align_mode + $get_router_block_feed_percent + $get_router_block_stub_mode + $get_router_cell_feed_percent + $get_router_channel_ocr + $get_router_connect_blk_pwr + $get_router_constrain_power + $get_router_create_power_grid + $get_router_limit_area + $get_router_overflow_mode + $get_router_pre_route + $get_router_preserve_power_width + $get_router_probe_extent_margin + $get_router_x_margin + $get_router_y_margin + $get_row_members + $get_rule_file_check_count + $get_rule_file_check_names + $get_rule_file_select_check_count + $get_rule_file_select_check_names + $get_select_area + $get_select_cell + $get_select_count + $get_select_extent + $get_select_set + $get_selectable_layers + $get_selected_edges + $get_short_segment + $get_snapped_points + $get_status_line_info + $get_top_orient_set + $get_traced_properties_numeric + $get_traced_properties_string + $get_visible_layers + $get_window_cell + $get_window_info + $get_world_from_context + $get_world_from_screen + $group + $group_by_name + $group_by_property + $group_on_selected + $group_text + $hide_layer_palette + $hide_status_line + $hide_system_toolbar + $hide_toolbar + $hide_tooltips + $highlight_all + $highlight_all_discrepancies + $highlight_all_incorrect + $highlight_all_unmatched + $highlight_by_location + $highlight_by_name + $highlight_by_property + $highlight_current_discrepancy + $highlight_discrepancy + $highlight_first_discrepancy + $highlight_group + $highlight_hierarchical_net + $highlight_next_discrepancy + $highlight_on_selected + $highlight_previous_discrepancy + $highlight_protected + $hotplot + $hotplot_invoke + $insert_posts + $iroute + $is_cell_outdated + $is_cell_reserved + $is_cell_salvage_pending + $is_edge_selected + $is_history_active + $is_layer_palette_visible + $is_library_reserved + $is_licensed + $is_process_reserved + $is_select_set_closed + $is_status_line_visible + $is_toolbar_visible + $link_floorplan_shape + $list_drc_all + $list_drc_check + $list_drc_selected + $load_design_hierarchy + $load_hotkey_settings + $load_logic + $load_mask_results + $load_process + $load_rules + $loc + $lvs_direct + $lvs_mask + $make_array + $make_cell + $make_font + $make_keypad_panels + $make_net + $make_port + $mark_instances_as_ignored + $measure_distance + $merge + $mgc_scope_name_pre_init + $minimize_levels + $minimize_vias + $modify_centerline + $move + $move_acap + $move_cursor_down + $move_cursor_left + $move_cursor_right + $move_cursor_up + $move_in_row + $move_on_row + $move_relative + $msg_cell_route_hierarchy_mode + $notch + $notch_special + $open_cell + $open_cell_map + $open_clipboard + $open_context_cell + $open_group_window + $open_hierarchy_window + $open_layout_library + $open_layout_view + $open_logic + $open_logic_by_name + $open_mask_logic + $open_selected_cell + $partition_hierarchy + $paste + $peek + $peek_area + $place + $place_on_row + $place_schematic_instances + $place_schematic_ports + $polygon_to_path + $print_cell + $prompt_for_ic_line + $prompt_for_ic_location + $prompt_for_ic_polygon + $prompt_for_ic_polyline + $prompt_for_ic_rectangle + $prompt_for_ic_route + $protect + $protect_group + $protect_nets + $read_def + $read_gdsii + $read_lef + $read_spice + $read_verilog + $read_xml + $redo + $redraw + $redraw_area + $regenerate_device + $release_license + $reload_cell + $reload_cell_by_name + $remove_contacts_by_area + $remove_contacts_by_m2 + $remove_contacts_by_via + $remove_from_net + $remove_from_port + $remove_metal_by_area + $reopen_selection + $repeat_fn + $repeat_objects + $replace_cell + $replace_toolbar + $report_active_context + $report_all_mask_devices + $report_all_mask_nets + $report_capacitor_type + $report_cell + $report_cell_list_dts + $report_current_direct_net + $report_current_discrepancy + $report_current_mask_device + $report_current_mask_discrepancy + $report_current_mask_net + $report_design_layers + $report_design_tree + $report_design_xref + $report_direct_thresholds + $report_drc_all + $report_drc_area + $report_drc_check + $report_drc_current + $report_drc_point + $report_drc_scan + $report_eco_results + $report_gb_type + $report_group + $report_group_names + $report_hotkey_settings + $report_layer_attributes + $report_layer_sets + $report_library + $report_licenses + $report_logic + $report_lvs_results + $report_mask_results + $report_mask_thresholds + $report_mos_type + $report_net_parameters + $report_nets + $report_outdated_cells + $report_panels + $report_paired_nets + $report_peek_protected + $report_place_route + $report_ports + $report_process + $report_property_names + $report_reserved + $report_resistor_type + $report_row_capacity + $report_scoring + $report_selected + $report_shown_mask_devices + $report_shown_mask_discrepancies + $report_shown_mask_nets + $report_text_file + $report_via_type + $report_windows + $reselect + $reserve_cell + $reserve_cell_by_name + $reserve_library + $reserve_process + $reset_basepoint + $reset_licensing + $resize + $restore_defaults + $restore_drc_results + $restructure_nets + $resync_cell + $resync_cell_by_name + $resync_library + $resync_viewpoint + $rotate + $route_point_to_point + $rule_file_loaded + $run_eco + $salvage_cell + $salvage_reference + $save_cell + $save_cell_as + $save_cell_by_name + $save_drc_results + $save_layout_library + $save_library + $save_process + $save_process_by_name + $save_setup + $scale_cells + $scale_context + $scan_drc_all + $scan_drc_check + $sdl_cleanup + $sdl_create_cell + $select_all + $select_area + $select_area_polygon + $select_by_name + $select_by_property + $select_drc_all + $select_drc_check + $select_edge + $select_fixed_routes + $select_folds + $select_group + $select_highlighted + $select_ignored_instances + $select_on_selected + $select_one + $select_range + $select_text + $select_unplaced_schematic_instances + $select_unplaced_schematic_ports + $send_to_prompt + $set_active_port_style + $get_active_port_style + $set_active_ruler_layer + $get_active_ruler_layer + $set_add_route_alignment + $get_add_route_alignment + $set_add_route_checking + $get_add_route_checking + $set_add_route_padding + $get_add_route_padding + $set_angle_mode + $set_angle_mode_enforcement + $get_angle_mode_enforcement + $set_anneal_cooling_factor + $get_anneal_cooling_factor + $set_arc_segments + $get_arc_segments + $set_array_display_style + $get_array_display_style + $set_auto_focus_active_window + $get_auto_focus_active_window + $set_auto_outline_mode + $get_auto_outline_mode + $set_auto_restructure + $get_auto_restructure + $set_autonotch + $get_autonotch + $set_autoselect + $get_autoselect + $set_basepoint + $set_cell_configuration + $set_cell_logical_name + $set_cell_origin + $set_cell_process + $set_cell_route_hierarchy_mode + $set_cell_type + $set_check_drc_message_mode + $set_circle_input_style + $get_circle_input_style + $set_click_distance + $get_click_distance + $set_compact_center_wires + $get_compact_center_wires + $set_compact_corner_spacing + $get_compact_corner_spacing + $set_compact_cost_factor + $get_compact_cost_factor + $set_compact_ext_cells + $get_compact_ext_cells + $set_compact_ext_rows + $get_compact_ext_rows + $set_compact_jogs + $get_compact_jogs + $set_compact_offset_vias + $get_compact_offset_vias + $set_compact_path_mini + $get_compact_path_mini + $set_compact_route_levels + $get_compact_route_levels + $set_compact_suppress_output + $get_compact_suppress_output + $set_compact_to + $get_compact_to + $set_compact_wires_mode + $get_compact_wires_mode + $set_contact_count + $set_context + $set_context_up + $set_copy_ports_on_copy + $get_copy_ports_on_copy + $set_cross_probe + $get_cross_probe + $set_cross_probe_mode + $set_crosshair_style + $get_crosshair_style + $set_crosshair_target + $set_crosshair_target_radius + $set_cull + $set_def_bus_bit_characters + $set_def_divider_character + $set_def_non_orthogonal + $set_direct_thresholds + $set_display_window_title + $get_display_window_title + $set_drag_complexity + $get_drag_complexity + $set_drag_ruler_mode + $get_drag_ruler_mode + $set_drc_check + $set_drc_first + $set_drc_fit_factor + $set_drc_jump + $set_drc_last + $set_drc_next + $set_drc_previous + $set_drc_result + $set_drc_skip + $set_duplicate_ports_policy + $set_dynamic_drc + $get_dynamic_drc + $set_dynamic_drc_complexity + $get_dynamic_drc_complexity + $set_dynamic_hotkey_mode + $get_dynamic_hotkey_mode + $set_dynamic_loading + $get_dynamic_loading + $set_dynamic_message_mode + $get_dynamic_message_mode + $set_dynamic_pre_locations + $set_error_handling + $get_error_handling + $set_fill_display + $get_fill_display + $set_filled_layers + $get_filled_layers + $set_first_direct_net + $set_first_mask_device + $set_first_mask_discrepancy + $set_first_mask_net + $set_fp_bottom_gap + $get_fp_bottom_gap + $set_fp_left_gap + $get_fp_left_gap + $set_fp_lower_aspect + $get_fp_lower_aspect + $set_fp_max_height + $get_fp_max_height + $set_fp_max_width + $get_fp_max_width + $set_fp_num_rows + $get_fp_num_rows + $set_fp_right_gap + $get_fp_right_gap + $set_fp_route_area_ratio + $get_fp_route_area_ratio + $set_fp_top_gap + $get_fp_top_gap + $set_fp_upper_aspect + $get_fp_upper_aspect + $set_fractured_route + $get_fractured_route + $set_gadget_value + $set_gate_size + $set_gds_allanglefracture + $get_gds_allanglefracture + $set_gds_anglemode + $get_gds_anglemode + $set_gds_auto_identify_vias + $get_gds_auto_identify_vias + $set_gds_cellnamecase + $get_gds_cellnamecase + $set_gds_cellnamelength + $get_gds_cellnamelength + $set_gds_cellnamemap + $get_gds_cellnamemap + $set_gds_fixpolygons + $get_gds_fixpolygons + $set_gds_layerfilter + $get_gds_layerfilter + $set_gds_layermap + $get_gds_layermap + $set_gds_library + $get_gds_library + $set_gds_lockcells + $get_gds_lockcells + $set_gds_logfile + $get_gds_logfile + $set_gds_process + $get_gds_process + $set_gds_properties + $get_gds_properties + $set_gds_read_scale + $get_gds_read_scale + $set_gds_replace + $get_gds_replace + $set_gds_save_cells + $get_gds_save_cells + $set_gds_skipunmapped + $get_gds_skipunmapped + $set_gds_textheightmap + $get_gds_textheightmap + $set_gds_transcript + $get_gds_transcript + $set_graphic_interrupt + $get_graphic_interrupt + $set_gravity_distance + $get_gravity_distance + $set_grid + $set_guideline_net_size + $get_guideline_net_size + $set_guideline_width_limit + $get_guideline_width_limit + $set_hocr + $get_hocr + $set_hotkey_mode + $get_hotkey_mode + $set_ic_array_columns + $get_ic_array_columns + $set_ic_array_rows + $get_ic_array_rows + $set_ic_aspect + $get_ic_aspect + $set_ic_cell_flip + $get_ic_cell_flip + $set_ic_cell_orientation + $get_ic_cell_orientation + $set_ic_cell_rotation + $get_ic_cell_rotation + $set_ic_cell_scale + $get_ic_cell_scale + $set_ic_layer + $get_ic_layer + $set_ic_path_end_style + $get_ic_path_end_style + $set_ic_path_old_style + $get_ic_path_old_style + $set_ic_path_padding + $get_ic_path_padding + $set_ic_path_style + $get_ic_path_style + $set_ic_path_width + $get_ic_path_width + $set_ic_property_owner + $get_ic_property_owner + $set_ic_property_replace + $get_ic_property_replace + $set_ic_property_text_height + $get_ic_property_text_height + $set_ic_property_text_horz_just + $get_ic_property_text_horz_just + $set_ic_property_text_orientation + $get_ic_property_text_orientation + $set_ic_property_text_vert_just + $get_ic_property_text_vert_just + $set_ic_row_auto_resize + $get_ic_row_auto_resize + $set_ic_row_justification + $get_ic_row_justification + $set_ic_row_site + $get_ic_row_site + $set_ic_row_slideable + $get_ic_row_slideable + $set_instance_extent_display + $get_instance_extent_display + $set_instance_name_display + $get_instance_name_display + $set_instance_origin_display + $get_instance_origin_display + $set_instance_route_hierarchy_mode + $set_iobj_points + $set_iroute_accept_at_current_loc + $set_iroute_auto_shield + $set_iroute_checking + $set_iroute_push_mode + $set_iroute_route_transforms + $set_iroute_select_with_active_layer + $set_iroute_show_alignment + $set_iroute_show_ortho_path + $set_iroute_via_generator_is_default + $set_iroute_width_change + $set_join_on_move + $get_join_on_move + $set_layer_appearance + $set_layer_palette_height + $get_layer_palette_height + $set_layer_path_width + $set_lef_bus_bit_characters + $get_lef_bus_bit_characters + $set_lef_divider_character + $get_lef_divider_character + $set_lef_non_orthogonal + $get_lef_non_orthogonal + $set_lef_overlap_boundary + $get_lef_overlap_boundary + $set_library_cell_type + $set_library_site_types + $set_location_mode + $get_location_mode + $set_logic_source + $set_logical_correspondence + $set_lvs_all_capacitor_pins_swappable + $get_lvs_all_capacitor_pins_swappable + $set_lvs_component_subtype_property + $get_lvs_component_subtype_property + $set_lvs_component_type_properties + $get_lvs_component_type_properties + $set_lvs_default_direct_source_subname + $get_lvs_default_direct_source_subname + $set_lvs_default_mask_source_subname + $get_lvs_default_mask_source_subname + $set_lvs_default_report_name + $get_lvs_default_report_name + $set_lvs_filter_unused_bipolar_transistors + $get_lvs_filter_unused_bipolar_transistors + $set_lvs_filter_unused_mos_transistors + $get_lvs_filter_unused_mos_transistors + $set_lvs_ground_names + $get_lvs_ground_names + $set_lvs_ignore_ports + $get_lvs_ignore_ports + $set_lvs_pin_name_properties + $get_lvs_pin_name_properties + $set_lvs_power_names + $get_lvs_power_names + $set_lvs_recognize_gates + $get_lvs_recognize_gates + $set_lvs_recognize_only_simple_gates + $get_lvs_recognize_only_simple_gates + $set_lvs_reduce_parallel_bipolar_transistors + $get_lvs_reduce_parallel_bipolar_transistors + $set_lvs_reduce_parallel_capacitors + $get_lvs_reduce_parallel_capacitors + $set_lvs_reduce_parallel_diodes + $get_lvs_reduce_parallel_diodes + $set_lvs_reduce_parallel_mos_transistors + $get_lvs_reduce_parallel_mos_transistors + $set_lvs_reduce_parallel_resistors + $get_lvs_reduce_parallel_resistors + $set_lvs_reduce_series_capacitors + $get_lvs_reduce_series_capacitors + $set_lvs_reduce_series_resistors + $get_lvs_reduce_series_resistors + $set_lvs_reduce_split_gates + $get_lvs_reduce_split_gates + $set_lvs_report_list_limit + $get_lvs_report_list_limit + $set_lvs_write_instance_cross_reference + $get_lvs_write_instance_cross_reference + $set_lvs_write_net_cross_reference + $get_lvs_write_net_cross_reference + $set_mark + $set_mask_auto_view + $set_mask_default_database_name + $get_mask_default_database_name + $set_mask_thresholds + $get_mask_thresholds + $set_max_auto_display + $get_max_auto_display + $set_modify_distance + $get_modify_distance + $set_mos_cell_type + $get_mos_cell_type + $set_mos_share + $get_mos_share + $set_net_priority + $set_new_window_cull + $get_new_window_cull + $set_new_window_grid + $get_new_window_grid + $set_next_direct_net + $set_next_mask_device + $set_next_mask_discrepancy + $set_next_mask_net + $set_packed_vias + $set_padding_grid + $set_path_display_style + $get_path_display_style + $set_peek_on_view + $get_peek_on_view + $set_peek_protect + $set_pex_backannotation_distributed + $get_pex_backannotation_distributed + $set_pex_backannotation_lumped + $get_pex_backannotation_lumped + $set_pex_capacitance_scale + $get_pex_capacitance_scale + $set_pex_coupled_distributed + $get_pex_coupled_distributed + $set_pex_delay + $get_pex_delay + $set_pex_exclude_distributed + $get_pex_exclude_distributed + $set_pex_include_distributed + $get_pex_include_distributed + $set_pex_netlist_distributed + $get_pex_netlist_distributed + $set_pex_netlist_lumped + $get_pex_netlist_lumped + $set_pex_netlist_simple + $get_pex_netlist_simple + $set_pex_options_lumped + $get_pex_options_lumped + $set_pex_report_distributed + $get_pex_report_distributed + $set_pex_report_lumped + $get_pex_report_lumped + $set_pin_shape_editing + $set_placement_select_mode + $get_placement_select_mode + $set_point_select_mode + $get_point_select_mode + $set_point_select_reset_basepoint + $get_point_select_reset_basepoint + $set_port_pin_name_display + $get_port_pin_name_display + $set_preferred_overflow_layers + $get_preferred_overflow_layers + $set_previous_direct_net + $set_previous_mask_device + $set_previous_mask_discrepancy + $set_previous_mask_net + $set_print_appearance + $set_print_array_style + $get_print_array_style + $set_print_cull + $get_print_cull + $set_print_grid + $get_print_grid + $set_print_layers + $get_print_layers + $set_print_levels + $get_print_levels + $set_print_peeked_only + $get_print_peeked_only + $set_process_override + $set_push_count_limit + $set_query_on_merge + $get_query_on_merge + $set_query_on_placement + $get_query_on_placement + $set_redraw_controls + $get_redraw_controls + $set_redraw_level + $get_redraw_level + $set_redraw_precision + $get_redraw_precision + $set_redraw_queue_control + $get_redraw_queue_control + $set_report_target + $get_report_target + $set_restrict_visible + $get_restrict_visible + $set_resync_on_peek + $get_resync_on_peek + $set_route_center_wires + $get_route_center_wires + $set_route_hierarchy_mode + $set_route_net_order + $get_route_net_order + $set_route_objects + $get_route_objects + $set_route_one_pass + $get_route_one_pass + $set_route_overflow_order + $get_route_overflow_order + $set_route_pack_wires + $get_route_pack_wires + $set_route_protection + $set_route_same_net_checking + $get_route_same_net_checking + $set_route_stub_direction + $get_route_stub_direction + $set_route_trim_nets + $get_route_trim_nets + $set_route_via_rotation + $get_route_via_rotation + $set_router_channel_size_variation + $get_router_channel_size_variation + $set_router_cleanup + $get_router_cleanup + $set_router_compound_path + $get_router_compound_path + $set_router_conn_order + $get_router_conn_order + $set_router_do_net_rules_check + $get_router_do_net_rules_check + $set_router_feed_bias + $get_router_feed_bias + $set_router_h_grid_size + $get_router_h_grid_size + $set_router_improve_global_val + $get_router_improve_global_val + $set_router_initial_global_val + $get_router_initial_global_val + $set_router_max_bends + $get_router_max_bends + $set_router_max_iters + $get_router_max_iters + $set_router_max_vias + $get_router_max_vias + $set_router_minimized_layers + $get_router_minimized_layers + $set_router_mode_type + $get_router_mode_type + $set_router_num_extra_tracks + $get_router_num_extra_tracks + $set_router_oper_mode_type + $get_router_oper_mode_type + $set_router_primary_layer_used + $get_router_primary_layer_used + $set_router_probe_layers + $get_router_probe_layers + $set_router_restricted_layers + $get_router_restricted_layers + $set_router_step_size + $get_router_step_size + $set_router_use_gridded_mode + $get_router_use_gridded_mode + $set_router_v_grid_size + $get_router_v_grid_size + $set_router_view_probes + $get_router_view_probes + $set_router_wrong_direction + $get_router_wrong_direction + $set_ruler_angle_mode + $get_ruler_angle_mode + $set_ruler_select_state + $get_ruler_select_state + $set_ruler_text_height + $get_ruler_text_height + $set_sd_hv_contact + $set_sdl_filter + $get_sdl_filter + $set_sdl_preserve_orientation + $get_sdl_preserve_orientation + $set_sdl_quiet + $get_sdl_quiet + $set_search_path + $get_search_path + $set_select_via_pin + $get_select_via_pin + $set_selectable_area + $get_selectable_area + $set_selectable_layers + $set_selectable_types + $get_selectable_types + $set_site_type + $set_snap_basepoint_to_grid + $get_snap_basepoint_to_grid + $set_target_mode + $get_target_mode + $set_text_cull_basis + $get_text_cull_basis + $set_text_display + $get_text_display + $set_text_origin_display + $get_text_origin_display + $set_timer + $set_toolbar_alignment + $get_toolbar_alignment + $set_toolbar_keep_items_visible + $get_toolbar_keep_items_visible + $set_undo_level + $get_undo_level + $set_undo_object_limit + $get_undo_object_limit + $set_unselect_empty + $get_unselect_empty + $set_update_connected + $get_update_connected + $set_verilog_array_delimiters + $get_verilog_array_delimiters + $set_verilog_supply0 + $get_verilog_supply0 + $set_verilog_supply1 + $get_verilog_supply1 + $set_view_controls + $get_view_controls + $set_visible_layers + $set_working_directory + $get_working_directory + $set_xml_group_protection + $get_xml_group_protection + $set_xml_logfile + $get_xml_logfile + $set_xml_other_protection + $get_xml_other_protection + $set_xml_read_cell_path_style + $get_xml_read_cell_path_style + $set_xml_replace + $get_xml_replace + $set_xml_style + $get_xml_style + $set_xml_write_cell_path_style + $get_xml_write_cell_path_style + $setup_auto_checkpoint + $setup_ic + $setup_ic_dynamics + $setup_new_windows + $setup_print + $setup_redraw_controls + $setup_reports + $setup_rulers + $get_active_ruler_bgd_color + $get_active_ruler_color + $get_active_ruler_font + $setup_sdl + $setup_select_filter + $setup_session + $setup_status_line + $show_all_mask_devices + $show_all_mask_discrepancies + $show_all_mask_nets + $show_aspect + $show_contact_gaps + $show_context + $show_current_mask_device + $show_current_mask_discrepancy + $show_current_mask_net + $show_drc_all + $show_drc_area + $show_drc_check + $show_drc_current + $show_drc_scan + $show_group + $show_guidelines + $show_layer_palette + $show_mask_device_id + $show_mask_device_name + $show_mask_device_point + $show_mask_discrepancy + $show_mask_net_id + $show_mask_net_name + $show_mask_net_point + $show_panel + $show_status_line + $show_system_toolbar + $show_toolbar + $show_tooltips + $show_unplaced_instances + $slice + $slice_array + $slice_with_polygon + $slide_route + $snap_to_grid + $snap_to_row + $startup + $stretch + $stretch_relative + $swap_logically_equivalent_nets + $toolbar_context_glyph_item + $toolbar_glyph_item + $tooltips_visible + $trace_property_numeric + $trace_property_string + $trim_invalid_nets + $trim_nets + $trim_nets_options + $undefine_layer_alias + $undefine_layer_name + $undefine_layer_set + $undefine_must_connect + $undefine_net_pairs + $undelete_drc + $undo + $unfreeze_window + $ungroup + $unhighlight_all + $unhighlight_group + $unhighlight_hierarchical_net + $unlink_floorplan_shape + $unload_closed_cells + $unload_mask_results + $unload_viewpoint + $unmake_port + $unmark_all_ignored + $unmark_instances_as_ignored + $unpeek + $unpeek_area + $unplace + $unprotect_all + $unprotect_group + $unprotect_nets + $unreserve_cell + $unreserve_cell_by_name + $unreserve_library + $unreserve_process + $unselect_all + $unselect_all_edges + $unselect_area + $unselect_area_polygon + $unselect_by_name + $unselect_by_property + $unselect_drc + $unselect_edge + $unselect_group + $unselect_range + $unset_cell_process + $unset_logic_source + $unset_process_override + $unshow_all_mask_devices + $unshow_all_mask_discrepancies + $unshow_all_mask_nets + $unshow_all_mask_results + $unshow_current_mask_device + $unshow_current_mask_discrepancy + $unshow_current_mask_net + $unshow_drc + $unshow_guidelines + $untrace_all_properties + $update_cell + $update_cell_by_name + $update_gadget + $update_group_window + $update_hier_window + $update_layout_view + $update_layout_config_view + $update_library + $view_all + $view_all_mask_devices + $view_all_mask_discrepancies + $view_all_mask_nets + $view_area + $view_centered + $view_context + $view_current_mask_device + $view_current_mask_discrepancy + $view_current_mask_net + $view_drc + $view_highlighted + $view_next + $view_panel + $view_previous + $view_selected + $write_calibre + $write_def + $write_design_lef + $write_direct_cnet + $write_direct_netlist + $write_gdsii + $write_hierarchical_netlist + $write_lef + $write_mask_cnet + $write_mask_netlist + $write_source_cnet + $write_xml + $zoom_in + $zoom_out + $zoom_to_grid + + + $$add_arc + $add_border + $add_bus + $add_circle + $add_dot + $add_fb_def + $add_fb_inst + $add_fb_pins + $add_frame + $add_ic_viewpoint_type + $add_instance + $add_line + $add_net + $add_panel + $add_parameters + $add_pin + $add_polygon + $add_polyline + $add_property + $add_property_to_handle + $add_rectangle + $add_selected_instance + $add_settings_block + $add_sheet_border + $add_text + $add_wire + $align + $allow_resizable_instances + $apply_edits + $auto_sequence_text + $begin_edit_symbol + $change_color + $change_compiled_pin_name + $change_group_visibility + $change_instance_resize_factor + $change_line_style + $change_line_width + $change_net_style + $change_net_width + $change_polygon_fill + $change_property_color + $change_property_font + $change_property_height + $change_property_justification + $change_property_name + $change_property_offset + $change_property_orientation + $change_property_stability_switch + $change_property_type + $change_property_value + $change_property_visibility + $change_property_visibility_switch + $change_text_font + $change_text_height + $change_text_justification + $change_text_value + $change_variant_display + $$check + $check_and_save + $clear_unattached_annotations + $close_design_configuration + $close_selection + $close_window + $comp_name + $connect + $connect_area + $construct_frame + $convert_fb_inst_to_def + $convert_to_comment + $convert_to_new_technology + $copy + $copy_edit_hotkey_settings + $copy_multiple + $copy_to_array + $create_design_configuration + $create_design_sheet + $create_entity + $create_fb_inst_from_def + $create_implicit_pins + $create_pin_list + $create_sheet + $create_symbol + $create_variant_viewpoint + $da_ic_crossprobe + $da_ic_crossprobe_diff + $define_hotkey + $delete + $delete_ba_property + $delete_interfaces + $delete_multiple_ba_properties + $delete_panel + $delete_parameter + $delete_property + $delete_property_owner + $delete_sheet + $delete_template_name + $direct_to_active_window + $disconnect + $disconnect_area + $disconnect_ba + $display_next_sheet + $display_prev_sheet + $display_spec_sheet + $does_selection_exist + $$dump_sim_values + $end_edit_symbol + $$exit_sim_mode + $expand_template_name + $export_spice + $export_verilog + $export_vhdl + $filter_property_check + $flip + $freeze_window + $generate_symbol + $get_active_symbol + $get_active_symbol_history + $get_apply_edits_needed + $get_attached_objects + $get_attributes + $get_auto_update_inst_handles + $get_basepoint + $get_body_text_restriction + $get_bundle_members + $get_check_schematic_status + $get_check_status + $get_comment_graphics_attributes + $get_comment_handles + $get_comment_text_attributes + $get_comment_text_restriction + $get_comment_visibility + $get_default_interface_name + $get_design_sheets + $get_diagram_location + $get_edit_mode + $get_evaluations + $get_fb_line_style_by_handle + $get_frame_attributes + $get_frame_handles + $get_grid + $get_hotkey_settings + $get_in_design_context + $get_instance_attributes + $get_instance_handles + $get_instance_models + $get_instance_pathname + $get_instance_resize_factor + $get_item_type + $get_model_path + $get_net_attributes + $get_net_handles + $get_next_active_symbol + $get_object_property_attributes + $get_objects + $get_objects_in_area + $get_origin + $get_owned_property_names + $get_parameter + $get_pathname + $get_pin_attributes + $get_pin_handles + $get_pin_names + $get_prop_text_restriction + $get_property + $get_property_attributes + $get_property_handles + $get_property_names + $get_property_owners + $get_schematic_sheets + $get_search_path + $get_select_count + $get_select_count_type + $get_select_design_paths + $get_select_extent + $get_select_handles + $get_select_handles_type + $get_select_identical + $get_select_text_exists + $get_select_text_handle + $get_select_text_name + $get_select_text_origin + $get_select_text_value + $get_sheet_design_pathname + $get_sheet_extent + $get_sheetname_viewed + $$get_sim_value + $$get_sim_version + $get_simulation_mode + $get_source_edit_allowed + $get_symbol_name + $get_text_information + $get_type_present + $get_vertex_attributes + $get_vertex_handles + $get_view_area + $get_viewpoint + $get_window_names + $get_window_sheet_list + $group + $hide_active_symbol_window + $hide_annotations + $hide_comment + $hide_context_window + $hide_panel_border + $hide_status_line + $hide_system_toolbar + $hide_toolbar + $highlight_by_handle + $highlight_by_name + $highlight_property_owner + $$hotplot_submit + $import_verilog + $insert_template + $is_active_symbol_window_visible + $is_context_window_visible + $is_handle_valid + $is_selection_open + $is_status_line_visible + $is_system_toolbar_visible + $is_toolbar_visible + $load_hotkey_settings + $make_fb + $make_polygon + $make_polyline + $make_symbol + $mark_property_attributes + $mark_property_value + $measure_distance + $merge_annotations + $modify_frame + $move + $move_cursor_incrementally + $$move_settings_block + $name_instances + $name_instances_auto + $open_design_configuration + $open_design_sheet + $open_down + $open_schematic + $open_sheet + $open_source_code + $open_symbol + $open_top + $open_up + $pivot + $place_active_symbol + $pop_to_front + $print_all_schematics + $print_design_sheets + $protect + $protect_area + $push_to_back + $recalculate_properties + $reconnect_annotations + $redo + $remove_comment_status + $reopen_selection + $replace + $replace_with_alternate_symbol + $report_broken_annotations + $$report_check + $report_default_property_settings + $report_groups + $report_hotkey_settings + $report_interfaces + $report_interfaces_selected + $report_object + $$report_object_name + $report_panels + $report_parameter + $report_unattached_annotations + $reselect + $reset_instance_colors + $resize_fb + $revalidate_models + $rotate + $route + $save_setup + $save_sheet + $save_sheet_as + $save_symbol + $save_symbol_as + $save_variant_viewpoint + $scale + $scroll_down_by_unit + $scroll_down_by_window + $scroll_hz + $scroll_left_by_unit + $scroll_left_by_window + $scroll_right_by_unit + $scroll_right_by_window + $scroll_up_by_unit + $scroll_up_by_window + $scroll_vt + $select_all + $select_area + $select_branches + $select_by_design_path + $select_by_handle + $select_by_name + $select_by_property + $select_by_property_type + $select_fb + $select_group + $select_instances + $select_nets + $select_pins + $select_property_owner + $select_template_name + $select_text + $select_vertices + $sequence_text + $set_active_symbol + $set_active_symbol_history + $set_basepoint + $set_color + $set_color_config + $set_compiler_options + $set_default_parts_menu + $set_edit_mode + $set_evaluations + $set_grid + $set_hotkey_mode + $set_next_active_symbol + $set_origin + $set_out_of_view_warn + $set_parameter + $set_previous_active_symbol + $set_property_owner + $set_property_type + $set_search_path + $set_sel_name_display + $$set_sim_value + $set_simulation_mode + $set_template_directory + $set_toolbar_alignment + $set_userrule_error + $set_userrule_warning + $set_variant_properties + $set_viewpoint + $$settings_block_visible + $setup_annotated_property_text + $setup_check_schematic + $setup_check_schematic_sheet + $$setup_check_sheet + $setup_check_symbol + $setup_color + $setup_comment + $setup_default_viewpoint + $setup_display + $setup_function_block + $setup_grid + $setup_hspice_alter + $setup_net + $setup_object_template + $setup_page + $setup_property_display + $setup_property_text + $setup_report + $setup_ripper + $setup_selection + $setup_select_filter + $setup_sim_config + $setup_sim_model_editor + $setup_symbol_body + $setup_text_restriction + $setup_unselect_filter + $show_active_symbol_window + $show_annotations + $show_comment + $show_context_window + $show_panel_border + $show_registration + $show_status_line + $show_system_toolbar + $show_toolbar + $sim_add_dspf + $sim_add_sdf + $sim_choose_library + $sim_copy_configuration_as + $sim_delete_converter + $sim_edit_commands + $sim_edit_measurements + $sim_edit_sdf + $sim_export_spice + $sim_get_adms_ini_file + $sim_hide_dcop + $sim_insert_converter_default + $sim_insert_converter_inst + $sim_insert_converter_net + $sim_insert_converter_pin + $sim_invoke + $sim_invoke_mr + $sim_invoke_ms + $$sim_merge_annotations + $sim_open_language + $sim_open_lang_model + $sim_process_extracted_netlist + $sim_restore_setup_from + $sim_run + $sim_save_selected + $sim_save_setup_as + $sim_set_additional_commands + $sim_set_include_paths + $sim_set_initial_condition + $sim_set_temperature + $sim_setup_analysis + $sim_setup_analysis_ac + $sim_setup_analysis_dc + $sim_setup_analysis_dcop + $sim_setup_analysis_mc + $sim_setup_analysis_mod_steadystate + $sim_setup_analysis_noise + $sim_setup_analysis_noisetran + $sim_setup_analysis_steadystate + $sim_setup_analysis_steadystateac + $sim_setup_analysis_steadystatenoise + $sim_setup_analysis_steadystateoscil + $sim_setup_analysis_tran + $sim_setup_netlister + $sim_setup_sim_environ + $$sim_setup_simulator_viewer + $$sim_setup_simulator_viewer_advance + $sim_simulate + $sim_view_measurements + $sim_view_output_file + $sim_write_commands + $sim_write_setup_file + $slice + $snap_to_grid + $sort_handles + $sort_handles_by_property + $stretch + $string_to_literal + $symb_name + $undo + $unfreeze_window + $ungroup + $unhighlight_by_handle + $unhighlight_by_name + $unhighlight_property_owner + $unmake_fb + $unprotect + $unprotect_area + $unselect_all + $unselect_area + $unselect_by_design_path + $unselect_by_handle + $unselect_by_property + $unselect_by_property_type + $unselect_fb + $unselect_property_owner + $unselect_vertices + $update + $update_all + $update_all_schematics + $update_border + $update_from_interface + $$update_settings_blocks + $update_title_block + $view_all + $view_area + $view_centered + $view_panel + $view_selected + $vpt_needs_save + $was_saved + $zoom_in + $zoom_out + $get_auto_name_net + $get_auto_place_instance_name + $get_check_busshorts + $get_check_functionblocks + $get_check_schematicbusshorts + $get_check_schematicnetio + $get_fb_def_color + $get_fb_def_line_style + $get_fb_inst_color + $get_fb_inst_line_style + $get_fb_int_change_popup + $get_fb_passthru + $get_fb_popupwin + $set_annotation_color + $set_annotation_visibility + $set_auto_name_net + $set_auto_place_instance_name + $set_auto_update_mode + $set_autoripper + $set_autoroute + $set_autoselect + $set_bus_width + $set_check_annotations + $set_check_busshorts + $set_check_closedots + $set_check_dangle + $set_check_expression + $set_check_filemode + $set_check_filename + $set_check_frame + $set_check_functionblocks + $set_check_initprops + $set_check_instance + $set_check_net + $set_check_notdots + $set_check_overlap + $set_check_owner + $set_check_parameter + $set_check_pins + $set_check_schematicbusshorts + $set_check_schematicinstance + $set_check_schematicinterface + $set_check_schematicnet + $set_check_schematicnetio + $set_check_schematicspecial + $set_check_schematicuserrule + $set_check_special + $set_check_symbolbody + $set_check_symbolinterface + $set_check_symbolpin + $set_check_symbolspecial + $set_check_symboluserrule + $set_check_transcript + $set_check_userrule + $set_check_window + $set_close_dot + $set_closeness_criteria + $set_dot_size + $set_dot_style + $set_dynamic_cursor + $set_dynamic_rounding_precision + $set_environment_dofile_pathname + $set_fb_def_color + $set_fb_def_line_style + $set_fb_inst_color + $set_fb_inst_line_style + $set_fb_int_change_popup + $set_fb_passthru + $set_fb_popupwin + $set_hidden_symbol_prop_display + $set_implicit_ripper + $set_line_style + $set_line_width + $set_modify_multiple_prop_filter + $set_net_style + $set_net_width + $set_new_annotation_visibility + $set_orthogonal + $set_orthogonal_angle + $set_pin_spacing + $set_polygon_fill + $set_property_font + $set_property_height + $set_property_hjustification + $set_property_orientation + $set_property_stability_switch + $set_property_transparency + $set_property_visibility + $set_property_visibility_switch + $set_property_vjustification + $set_report_filemode + $set_report_filename + $set_report_transcript + $set_report_window + $set_ripper_dot + $set_ripper_mode + $set_ripper_query + $set_ripper_symbol_pathname + $set_schem_check_mode + $set_schematicuserrules_file + $set_segment_select_mode + $set_select_aperture + $set_select_comment + $set_select_exterior + $set_select_frame + $set_select_instance + $set_select_net + $set_select_pin + $set_select_property + $set_select_segment + $set_select_symbolbody + $set_select_symbolpin + $set_select_text + $set_select_vertex + $set_selection_color + $set_selection_model + $set_snap + $set_symboluserrules_file + $set_text_font + $set_text_height + $set_text_hjustification + $set_text_orientation + $set_text_transparency + $set_text_vjustification + $set_undo_level + $set_unselect_comment + $set_unselect_exterior + $set_unselect_frame + $set_unselect_instance + $set_unselect_net + $set_unselect_pin + $set_unselect_property + $set_unselect_segment + $set_unselect_symbolbody + $set_unselect_symbolpin + $set_unselect_text + $set_unselect_vertex + $set_userrules_file + $set_user_units + + + $add_menu_item + $ask_color + $ask_frame_name + $ask_integer + $ask_number + $ask_pathname + $ask_pattern + $ask_save_edits + $ask_scope_builtin_name + $ask_scope_callable_name + $ask_scope_form_name + $ask_scope_frame_name + $ask_scope_function_name + $ask_scope_keydef_name + $ask_scope_menu_name + $ask_scope_stroke_name + $ask_string + $ask_yes_no + $ask_yes_no_cancel + $bell + $bottom_y + $c_ask_string + $change_location_map_entry + $change_workspace + $cleanup_icons + $cleanup_windows + $clear_message + $clear_saved_prompt + $close_application_windows + $close_physical_transcript + $close_session + $close_window + $collapse_dockable + $compile_userware + $create_form + $create_menu + $create_notepad + $create_prompt + $create_toolbar + $define_color + $define_userware + $delete_menu_item + $delete_stroke + $disable_interrupt + $disable_softkey_update + $dock_dockable + $dockable_collapsed + $dockable_docked + $dockable_permission + $dockable_pinned + $dockable_visible + $dockables_all_hidden + $dockables_all_shown + $dockables_locked + $double_click_distance + $edit_source + $enable_help + $enable_interrupt + $enable_ref_help + $enable_softkey_update + $eval + $execute + $execute_dynamic + $execute_last_menu + $execute_promptbar + $execute_ref + $execute_stroke + $expand_command + $float_dockable + $forget + $forget_all_promptbars + $forget_promptbar + $form_action + $form_action_buttons_gadget + $form_actual_value + $form_argument + $form_argument_gadget + $form_argument_integer_entry_gadget + $form_argument_number_entry_gadget + $form_argument_string_entry_gadget + $form_argument_value + $form_button + $form_check_boxes_gadget + $form_choice_buttons_gadget + $form_choice_stepper_gadget + $form_click_button_gadget + $form_color_paint_chip_gadget + $form_colors_list_box_gadget + $form_column + $form_display_only + $form_display_text_gadget + $form_dynamic_list_box_gadget + $form_entry_box_gadget + $form_execute_buttons + $form_gadget_value + $form_horiz_dynamic_list_box_gadget + $form_horiz_list_box_gadget + $form_item + $form_label + $form_left_justified_column + $form_list_box_gadget + $form_mouse_tracking + $form_named_argument + $form_named_argument_gadget + $form_navigator_entry + $form_navigator_gadget + $form_number_entry_box_gadget + $form_patterns_list_box_gadget + $form_prompt_text_gadget + $form_push_button_gadget + $form_radio_buttons_gadget + $form_repeat + $form_right_justified_column + $form_row + $form_scope_list_box_gadget + $form_set_no_enter + $form_single_check_box_gadget + $form_string_entry_box_gadget + $form_switch + $form_target_action + $form_target_button + $form_text_entry_box_gadget + $form_variable + $form_variable_gadget + $frame_maximized + $frame_window + $get_action_bar_bgd_color + $get_action_bar_fgd_color + $get_active_color + $get_active_window + $get_additional_workspaces + $get_app_name_and_version + $get_app_name_no_version + $get_auto_pop + $get_auto_refresh + $get_auto_resize_palette + $get_autosave + $get_base_window + $get_bgd_color + $get_border_width + $get_current_profile + $get_cursor_colors + $get_cursor_shape + $get_default_method + $get_default_notepad_font + $get_double_click_interval + $get_expanded_pathname + $get_fgd_color + $get_focus_follows_mouse + $get_focus_follows_mouse_delay + $get_font_nominal_width + $get_form_gadget_value + $get_frame_collapsed + $get_graphic_device + $get_keyboard_type + $get_last_window + $get_message_lines + $get_message_popup + $get_message_reply + $get_message_transcript + $get_notepad_document_status + $get_open_windows + $get_palette_menu_visible + $get_pattern + $get_prompt_fonts + $get_server_switch + $get_sidetab_color + $get_tabbed_workspace_trim_right + $get_text_cursor_blink + $get_transcript_output + $get_window_border_width + $get_window_frame_extent + $get_window_frame_width + $get_workspace + $graphic_x + $graphic_y + $grow_window + $help + $help_context + $help_enabled + $hide_all_dockables + $hide_command_shell + $hide_dockable + $hide_menu_bar + $hide_message_area + $hide_palette + $hide_palette_scrolls + $hide_scrolls + $hide_softkey_labels + $hide_softkeys + $hide_transcript + $hide_window_title + $identify_interrupt + $insert_menu_item + $key_ + $left_x + $load_profile + $load_userware + $location + $lock_dockables + $mark + $maximize_window + $menu_bar_item + $menu_bar_visible + $menu_context_item + $menu_name + $menu_parent_name + $menu_registered_item + $menu_separator_item + $menu_special_text_item + $menu_text_item + $menu_title_item + $message + $message_area_visible + $minimize_window + $move_cursor + $move_dockable + $move_dockable_into + $move_palette + $move_window + $next_field + $next_field_promptbar + $next_icon + $next_window + $open_notepad + $open_physical_transcript + $open_text_report + $option_form_promptbar + $palette_visible + $pause + $pin_dockable + $pop_window + $pop_window_to_top + $popup_command_line + $popup_last_menu + $popup_menu + $popup_menu_at_cursor + $popup_menu_bar + $popup_window_menu + $prev_field + $prev_field_promptbar + $prompt + $prompt_arg + $prompt_display + $prompt_dynamic + $prompt_for_location + $prompt_for_polylocation + $prompt_for_polyrectangle + $prompt_for_rectangle + $prompt_options + $read_cpu_timer + $read_cpu_timer_total + $read_map + $read_timer + $read_timer_total + $redirect_to_active_window + $ref_help + $ref_help_enabled + $refresh + $relative_location + $remove_profile + $replace_palette + $replay_physical_transcript + $report_color + $report_key + $report_stroke + $reposition_window + $reset + $reset_defaults + $reset_timer + $resize_palette + $restore_default_profile + $result + $resume + $right_x + $save_profile + $save_profile_as + $screen_x + $screen_y + $scroll_down_by_unit + $scroll_down_by_window + $scroll_hz + $scroll_left_by_unit + $scroll_left_by_window + $scroll_right_by_unit + $scroll_right_by_window + $scroll_to_bottom + $scroll_to_left + $scroll_to_right + $scroll_to_top + $scroll_up_by_unit + $scroll_up_by_window + $scroll_vt + $scrolls_visible + $select + $select_active_window + $send_ipc + $session_window_active + $set_action_bar_bgd_color + $set_action_bar_fgd_color + $set_active_color + $set_active_window + $set_additional_workspaces + $set_auto_pop + $set_auto_refresh + $set_auto_resize_palette + $set_autosave + $set_base_window + $set_bgd_color + $set_border_width + $set_cmd_line_font + $set_cursor_colors + $set_cursor_shape + $set_default_method + $set_default_notepad_font + $set_default_position + $set_double_click_distance + $set_double_click_interval + $set_fgd_color + $set_focus_follows_mouse + $set_focus_follows_mouse_delay + $set_form_gadget_value + $set_form_position + $set_frame_width + $set_graphic_device + $set_invert_text_color_on_highlight + $set_ipc_port + $set_ipc_wakeup_interval + $set_left_justify_palette_text + $set_list_gadget_double_click_action + $set_menu_rollover_color + $set_message_font_resize + $set_message_lines + $set_message_popup + $set_message_reply + $set_message_transcript + $set_palette_width + $set_pattern + $set_prompt_fonts + $set_sidetab_color + $set_softkey_bgd_color + $set_softkey_fgd_color + $set_softkey_font + $set_tabbed_workspace_trim_right + $set_text_cursor_blink + $set_transcript_output + $set_window_border_width + $set_window_frame_width + $set_working_directory + setup_registered_commands + $show_all_dockables + $show_command_shell + $show_dockable + $show_location_map + $show_menu_bar + $show_message_area + $show_palette + $show_palette_scrolls + $show_parent_palette + $show_scrolls + $show_softkey_labels + $show_softkeys + $show_sub_palette + $show_toolbar_icons + $show_toolbar_text + $show_top_palette + $show_transcript + $show_window_title + $show_workspace_tab + $softkey_bgd_color + $softkey_fgd_color + $softkey_font + $softkeys_visible + $source + $start_stroke + $start_stroke_location + $start_timer + $stop + $stop_stroke + $stop_stroke_location + $stop_timer + $stroke_extent + $stroke_identity + $stroke_ + $target_name + $title_visible + $toolbar_action_selector_item + $toolbar_add_item + $toolbar_associative_item + $toolbar_combo_box_item + $toolbar_insert_item + $toolbar_label_item + $toolbar_n_state_item + $toolbar_registered_item + $toolbar_remove_item + $toolbar_separator_item + $toolbar_text_field_item + $toolbar_text_icon_item + $top_y + $transcript_visible + $ui_message_ask_yes_no + $ui_message_fail + $update_menu_bar + $update_palette + $update_softkey_labels + $update_toolbars + $use_animations + $user_init + $user_pre_init + $user_window_init + $using_animations + $version + $wait + $window_class_name + $window_extent + $window_init + $window_interior_extent + $window_scope_name + $window_visible + $writeln + + + $add_back_annotation + $add_multiple_properties + $add_parameter + $add_primitive + $add_property + $add_substitute + $add_visible_property + $change_model + $change_property + $check_design + $clear_global_parameter + $close_design_viewpoint + $connect_back_annotation + $delete_invalid_entries + $delete_parameter + $delete_primitive + $delete_property + $delete_substitute + $delete_visible_property + $disconnect_back_annotation + $erc_check + $export_back_annotation + $export_design_configuration + $filter_property_check + $get_viewpoint_name + $import_back_annotation + $is_ba_readonly + $is_function_defined + $latch_using_label + $latch_version + $maintain_back_annotation_window + $open_back_annotation + $open_design_configuration + $open_design_viewpoint + $preset_global_parameter + $reload_model + $report_select_counts + $report_viewpoint_references + $save_design_viewpoint + $select_back_annotation + $select_design_configuration + $select_parameter + $select_primitive + $select_substitute + $select_visible_property + $unlatch_version + $unselect_back_annotation + $unselect_design_configuration + $unselect_parameter + $unselect_primitive + $unselect_substitute + $unselect_visible_property + $update_latched_version + + + + $add_bundle + $add_bundle_members + $classify_all_genlib + $classify_symbol + $close_window + $delete_bundle + $delete_bundle_members + $delete_parameter + $generate + $get_bundle + $get_bundle_list + $get_bundle_members + $get_components + $get_instances + $get_nets + $get_parameter + $get_partition_setup + $get_property_list + $get_property_setup + $get_schematics + $get_sheets + $get_symbols + $get_symbol_classification + $get_symbol_setup + $open_component_from_model + $open_component_from_tfnf + $open_design_from_viewpoint + $report_bundle_members + $report_design_info + $report_property_setup + $report_setup + $report_status_bundles + $report_symbol_classifications + $save + $save_as + $set_parameter + $set_symbol_transcript + $setup_partition + $setup_placement + $setup_property + $setup_symbol_generation + $view_all + $view_sheet + $view_symbol + $zoom_in + $zoom_out + $set_balance + $set_bundle_mode + $set_bus_partition + $set_cluster + $set_cluster_spacing + $set_connector_spacing + $set_ground_placement + $set_instance_spacing + $set_iterate + $set_lrflow + $set_net_name_prefix + $set_net_spacing + $set_partition_by_name + $set_partition_by_property + $set_partition_density + $set_partition_mode + $set_partition_sheet_count + $set_partition_sheet_size + $set_placement_technique + $set_power_placement + + + + $add_bus + $add_comment_flag + $add_group + $add_synonym + $clear_name + $clear_selection_filter + $close_all_windows + $close_selection + $delete_bus + $delete_comment_flag + $delete_group + $delete_synonym + $end_object_selection + $end_object_unselection + $freeze_window + $get_all_instance_properties + $get_all_net_properties + $get_all_pin_properties + $get_all_types + $get_average_connected_pin + $get_average_primitive_instance + $get_average_selected + $get_best_case + $get_bottom_visible + $get_bundle_names + $get_bus_parts + $get_bus_width + $get_buses + $get_comment_flags + $get_component_path + $get_connected + $get_connected_instance + $get_connected_net + $get_connected_net_name + $get_connected_pin + $get_contained + $get_default_monitor_flag_font + $get_default_monitor_flag_height + $get_effective_context + $get_equivalent_nets + $get_full_pathname + $get_groups + $get_highlighted + $get_instance_property + $get_instance_viewed + $get_last_created_window + $get_maximum_connected_pin + $get_maximum_primitive_instance + $get_maximum_selected + $get_minimum_connected_pin + $get_minimum_primitive_instance + $get_minimum_selected + $get_monitor_flag_font + $get_monitor_flag_height + $get_naming_context + $get_nearest_instance + $get_nearest_net + $get_nearest_pin + $get_nearest_property_and_owner + $get_nearest_vertex + $get_net_property + $get_pin_property + $get_protected + $get_select_count + $get_selected + $get_selected_types + $get_selection_filter + $get_sheetname_viewed + $get_source_type + $get_sum_connected_pin + $get_sum_primitive_instance + $get_sum_selected + $get_synonyms + $get_top_visible + $get_total_width_of + $get_triplet_mode + $get_typical + $get_undo_depth + $get_view_window_names + $get_window_names + $get_window_titles + $get_window_type + $get_worst_case + $goto_highlight + $highlight_instance + $highlight_net + $highlight_pin + $instance_annotated_property + $instance_exists + $instance_has_annotations + $instance_source_property + $invalidate_window + $is_window_valid + $net_annotated_property + $net_exists + $net_has_annotations + $net_source_property + $open_down + $open_selected + $open_sheet + $open_up + $pin_annotated_property + $pin_exists + $pin_has_annotations + $pin_source_property + $primitive_instance + $primitive_net + $primitive_pin + $protect + $redirect_to_all_windows + $redo + $reopen_selection + $report_buses + $report_groups + $report_highlighted + $report_interfaces + $report_naming_context + $report_objects + $report_parts_list + $report_protected + $report_synonyms + $report_wire_list + $reselect + $select_by_name + $select_by_property + $select_connected + $select_contained + $select_group + $select_messages + $select_statement + $select_whole_window + $selection_closed + $selection_filter_active + $send_to_active_window + $set_auto_locate + $set_default_method + $set_default_monitor_flag_font + $set_default_monitor_flag_height + $set_effective_context + $set_monitor_flag_font + $set_monitor_flag_height + $set_naming_context + $set_selection_filter + $set_triplet_mode + $set_undo_depth + $setup_icon + $setup_icon_font + $setup_name + $setup_window_location + $undo + $unfreeze_window + $unhighlight_all + $unhighlight_instance + $unhighlight_net + $unhighlight_pin + $unprotect + $unselect_all + $unselect_by_name + $unselect_by_property + $unselect_except_by_property + $unselect_except_group + $unselect_group + $unselect_statement + $unselect_whole_window + $update_window + $write_report + + + __da_hdl_arch_name + __da_hdl_entity_name + __da_hdl_lib_name + __da_hdl_libraries + __da_hdl_packages + __da_hdl_port_init + __da_hdl_port_mode + __da_hdl_port_type + __da_hdl_signal_type + __da_suppress_units + Addr_width + Afall, Bfall, Qfall, Qbfall, Tfall, Tbfall + Area + Arise, Brise, Qrise, Qbrise, Trise, Tbrise + Asim_coupling + Asim_param + Asim_pinorder + Asim_model + Asim_model Property in Element Statements + Asim_model_keyword + Attribute_file_required + Basic_container + Block_dir + Brd_loc + Bres_value + Buildmode + Bundle_cover_thickness + Bundle_diameter + Bundle_height + Bundle_length + Bundle_min_bend_radius + Bundle_type + Bundle_width + Cap_coupled + Cap_drive + Cap_drive1 + Cap_drive2 + Cap_net + Cap_nets_coupled + Cap_pin + Class + Cntm_bdry_model + Comp + $Compaction-group + Component_type + $Con-edge + $Con-obj + Conn_order + Conn_type + Contention + Convect_h + Convect_rank + Corr_formula + Corr_formula_rad + Cover_thickness + Cpl_cap_net + Creating_tool + Current_drive + Current_load + Data_width + DCinit + Ddms_versioned_object + Decay + Dec_cap + Default_plot_typ + Default_tool + Diff_pair + Dme_config_ignore_type + Dme_config_include_container + Do_not_jog + dont_delete + dont_shrink + Drive + Dtime + Dual_footprint + Elec_class + Element + Emissivity + Except_assy + Fall + Fall_adj + Fall_slope + Fileset_def + First_addr + Fpin_no_sci + Frexp + From_location_map + From_path + From_version + Func + $G + $Gg + Gate_class + Gate_id + Gatemode + Gdsii_cell + Gdsii_datatype + Gdsii_error + Gdsii_no_output + Gdsii_plex + Gdsii_property_# + Gdsii_text + Requirements for DRACULA II + Gdsii_textinfo + Geom + Global + Globalin + Globalinout + Global_net_cap + Globalout + Group_abut + Group_name + Group_place + Group_prio + Group_seed + Icap_net + Ignore_net + Iih + Iil + Init + Initial_Condition + Ink_id + Input_data + Inst + Instpar + Instpartol + Ioh + Ioh1, Ioh2 + Iol + Iol1, Iol2 + Iozh + Iozl + Junction_max_t + Key_file + Kfall + Krise + Large_icon + Laser + Lms_cat + Lms_component_path + Lms_geometries + Lms_index + Lms_symbols + Load + M + Mass_density + Master_pin + Match_group + Matched_net_length + Mating_connector + Max_addr + Max_cap_pin + Max_stub + Max_wire_length + Mfg + Mgc_cat + Mgc_comps + Mgc_component_path + Mgc_geoms + Mgc_index + Mgc_lib + Mgc_map_path + Mgc_models + Mgc_prop_file_index + Mgc_prop_file_path + Mgc_refine_level + Mgc_symbols + Min_node + Min_stub + Min_wire_length + Model + Modelfile + Modeltype + Ms_* + Multi_assay + Must_jog + My_net + N + Net + Net_comp + Net_comp_count + Net_comp_place + Netdelay + Spike Model Data + Example + Net_length + Net_order + Net_prio + Net_prio_protected + Net_tp_min_clearance + Net_tp_req + Net_type + Nmos_bulk_node + Node + Nodeset + Nofault + Note + Npn_sub_node + Num_blocks + Omit_wire_list + Part_no + Pcb_group + Pcb_ignore + Pcb_inst + Pcb_net + Pcb_pin + Pcb_pin_loc + Pcb_pin_pad + Phy_comp + Phy_pin + Pin + Pin_driver + Pin_gauge + Pin_group + Pin_no + Pin_order + Pin_swap + Pin_tp_req + Pins + Pins_out + Pins_spare + Pintech + Pintype + Pintype_override + Place + Placement_net_cap + Placement_region + Pmos_bulk_node + Pnp_sub_node + $Pn_ext + Portdir + Porttype + Pow_del_max + Pow_del_typ + Pow_derating + Pow_den + Pow_max + Pow_max_symb + Pow_min + Pow_typ + Pow_typ_symb + Power_nets + Power_pins + Power_route_style + Pwr + Prio + Process_var + R + Radiate_h + Rc_feedthrough + Ref + Ref_group + Refloc + Rel_preplacement + Release_comments + Release_date + Release_date_string + Released_by + Released_location_map + Released_to_path + Res_net + Res_path + Res_source + Restrict + R_hat_len + R_hat_width + Rise + Rise_adj + Rise_slope + R_len + R_min_dim + Rn_sub_node + Route_set + Rp_sub_node + R_shape + R_width + Rule + S_des + Seed + Shape + Shared + Side_pref + Size + Small_icon + Source + Source_edit_allowed + Spec_heat + Spicepar + Ss_num + Stub_dir + Substrate + Surface + Surface_area + Swapping + Swap_set + Syn_donttouch + Syn_dontuse + Syn_libname + Target_net_cap + Tech + Tegas5_lib + Temp + Terminator + Therm_cond + Therm_jc + Therm_r + _tid + Timefile + Toler + Tool_reference + Tphz, Tplz, Tpzh, Tpzl + Trace_shielding + Transmode + Trim + Undriven + Value + Variant + Version_depth + Vhdl_type + Vih + Vil + Voh + Voh1, Voh2 + Vol + Vol1, Vol2 + Voltage + Vtype + Wire_auto_calc_length + Zif_model + Zycad_type + + + + $$add_configuration_entry + $add_configuration_entry + $$add_container + $add_container + $$add_directory + $add_directory + $add_link + $add_object_property + $$add_reference + $add_reference + $add_reference_property + $add_toolbox + $$add_type + $add_versions + $browse_for_object + $$build_configuration + $build_configuration + $$change_configuration_references + $change_configuration_references + $$change_design_object_references + $change_design_object_references + $change_link_text + $change_location_map_entry + $$change_object_name + $change_object_name + $change_object_property + $$change_object_references + $change_object_references + $change_password + $change_protection + $change_reference_property + $change_reference_state + $change_version_depth + $check_references + $check_registries + $$clear_entry_filter + $$clear_global_status + $$clear_monitor + $$close_configuration + $close_hierarchy + $$close_versioned_object + $close_window + $$convert_configuration_references + $convert_configuration_references + $$convert_object_references + $convert_object_references + $$copy_configuration + $copy_configuration + $$copy_design_object + $copy_design_object + $$copy_object + $copy_object + $copy_version + $$create_configuration + $create_dm_category + $create_dm_cell + $create_dm_ext_lib + $create_dm_library + $create_dm_project + $create_dm_tech_category + $create_dm_tech_lib + $create_tech_config_object + $$create_versioned_object + $$delete_configuration + $delete_configuration + $delete_design_object + $delete_excess_versions + $$delete_object + $delete_object + $$delete_object_property + $delete_object_property + $$delete_reference + $delete_reference + $$delete_reference_handle + $$delete_reference_property + $delete_reference_property + $$delete_reference_property_handle + $$delete_version + $delete_version + $$delete_version_property + $descend_hierarchy_one_level + $descend_hierarchy_specify_level + $$duplicate_object + $edit_file + $empty_trash + $explore_contents + $explore_parent + $explore_reference_parent + $explore_references + $export_configuration_entries + $export_library + $export_location_map + $find_external_deps + $find_references + $$fix_relative_path + $$freeze_configuration + $freeze_configuration + $$freeze_version + $freeze_version + $get_area_selected_objects + $$get_children + $$get_configuration_entries + $$get_configuration_path + $$get_container_contents + $$get_date_last_modified + $get_default_tool + $$get_entry_version + $$get_fileset_members + $$get_hard_name + $$get_location_map + $$get_monitor_error_count + $$get_monitor_flag + $$get_monitor_verbosity + $$get_monitor_warning_count + $get_navigator_directory + $get_navigator_directory_hard + $get_next_tool_env + $$get_object_current_version + $$get_object_parent_path + $$get_object_path_filter + $get_object_pathname + $$get_object_properties + $$get_object_property_filter + $$get_object_property_value + $$get_object_protection + $$get_object_references + $$get_object_type + $get_object_type + $$get_object_type_filter + $get_object_version + $$get_object_versions + $$get_parent_entry + $$get_primaries + $$get_reference_properties + $$get_reference_properties_handle + $$get_reference_property_filter + $$get_reference_traversal + $$get_secondaries + $$get_soft_name + $$get_status_code + $$get_status_code_stack + $$get_status_messages + $get_subinvoke_mode + $$get_target_path + $get_technology + $get_toolbox_search_path + $get_tool_pathname + $get_tool_script + $get_tool_type + $$get_type_properties + $$get_type_property_value + $$get_version_depth + $$get_version_properties + $$get_working_directory + $goto_directory + $$handle_map_error + $$has_object_property + $$has_reference_property + $$has_reference_property_handle + $hide_secondary_entries + $hide_monitor + $import_classic_data + $import_custom_view + $import_design_kit + $import_ext_lib + $import_icstudio_library + $import_icstudio_project + $include_external_library + $invoke_bgd_tool + $invoke_tool + $$is_build_consistent + $$is_build_valid + $$is_configuration_edited + $$is_configuration_frozen + $$is_configuration_locked + $$is_container + $$is_directory + $$is_entry_container + $$is_entry_fixed + $$is_entry_primary + $$is_entry_retargetable + $$is_object_released + $$is_object_versioned + $$is_read_protected + $$is_relative_path + $$is_type_versioned + $$is_writable + $$is_write_protected + $list_references + $load_registry + $$lock_configuration + $lock_configuration + $$lock_object + $login_admin + $logged_in + $logout_admin + $maintain_hierarchy + $$monitor_global_status + $$move_design_object + $move_design_object + $$move_object + $move_object + $$object_complete + $$object_exists + $$open_configuration + $open_configuration_window + $$open_hierarchy + $open_navigator + $open_object + $open_read_only_editor + $open_session_monitor + $$open_tool + $open_tool + $open_tools_window + $open_trash_window + $open_types_window + $$open_versioned_object + $$prune_design_hierarchy + $$read_map + $read_map + $refresh_all + $$release_configuration + $release_configuration + $$release_object + $release_object + $$remove_configuration_entry + $remove_configuration_entry + $remove_external_library + $remove_toolbox + $report_configuration_info + $$report_configuration_references + $report_configuration_references + $report_entry_info + $$report_entry_verification + $report_entry_verification + $$report_global_status + $report_object_info + $report_reference_info + $report_tool_info + $report_type_info + $report_version_info + $$resolve_path + $$revert_version + $revert_version + $$salvage_object + $salvage_object + $$save_configuration + $save_configuration + $$save_configuration_as + $save_configuration_as + $$save_object + $save_toolbox_search_path + $search + $search_again + $select_all + $select_by_name + $select_by_library + $select_by_type + $select_config_entry + $select_object + $select_reference + $select_tool + $select_toolbox + $select_trash_object + $select_version + $set_build_rules + $$set_location_map_entry + $$set_monitor_flag + $$set_monitor_verbosity + $set_next_tool_env + $$set_object_path_filter + $$set_object_property + $$set_object_property_filter + $$set_object_type_filter + $set_project_refresh_heartbeat + $$set_protection + $$set_protection_numeric + $$set_reference_property + $$set_reference_property_filter + $$set_reference_property_handle + $$set_reference_traversal + $set_subinvoke_mode + $$set_target_path + $set_target_path + $set_technology + $set_toolbox_search_path + $$set_version_depth + $$set_version_property + $$set_working_directory + $set_working_directory + $setup_filter_active + $setup_filter_all + $setup_default_editor + $setup_iconic_window_layout + $setup_invoke_tool + $$setup_monitor + $setup_monitor + $setup_session_defaults + $setup_startup_windows + $show_all_files + $show_compiled_libs + $show_component_hierarchy + $show_custom_views + $show_directories + $show_ext_libs + $show_language_views + $show_layout_views + $$show_location_map + $show_location_map + $show_logic_views + $show_references + $show_monitor + $show_tech_libs + $show_versions + $trash_object + $$unfreeze_configuration + $unfreeze_configuration + $$unfreeze_version + $unfreeze_version + $$unlock_configuration + $unlock_configuration + $$unlock_object + $unselect_all + $unselect_by_name + $unselect_by_type + $unselect_config_entry + $unselect_object + $unselect_reference + $unselect_tool + $unselect_toolbox + $unselect_trash_object + $unselect_version + $unset_next_tool_env + $untrash_object + $$update_type + $update_window + $validate_technology + $view_by_icon + $view_by_name + $view_containment_hierarchy + $view_primary_hierarchy + $view_secondary_entries + $view_toolboxes + $view_tools + $write_default_startup_file + $$writeln_monitor + + + $get_current_obj_hier_path + $get_current_obj_inst_list + $idw_dh_setup_display + $idw_report_hier + $idw_open_hierarchy_window + $inst_area_extend_selection + $inst_area_select_all_items + $inst_area_select_item + $inst_area_show_instances + $inst_area_unselect_all_items + $make_obj_current + $open_new_comp_hierarchy + $open_new_hierarchy + $select_obj + $show_instance + $show_n_levels + $set_font + $setup_comp_hierarchy_display + $setup_hierarchy_selection + $write_default_startup_file + $add_components + $add_labels_to_models + $collapse_object + $delete_labels_from_models + $delete_part_interfaces + $expand_object + $forget_components_edits + $hide_body_props + $hide_labels + $hide_model + $hide_pin_properties + $hide_pins + $register_models + $remove_components + $rename_part_interface + $report_body_prop_info + $report_component_info + $report_model_entry_info + $report_models_for_each_label + $report_model_info + $report_models_with_all_labels + $report_pin_info + $save_components_edits + $select_model_object + $select_object + $set_bgd_color + $set_bgd_color_title_items + $set_bgd_color_titles + $set_constraints + $set_default_part_interface + $set_fgd_color + $set_fgd_color_title_items + $set_fgd_color_titles + $set_font + $set_part_interface_font + $show_body_props + $show_labels + $show_model + $show_pins + $show_pin_properties + $unselect_model_object + $unselect_object + $validate_models + + + quick_help + ref_help + + + + optional + default + + + + + name + callable + enum + integer + location + name + number + pathname + polylocation + polyrectangle + real + rectangle + status + string + vector + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/ansforth94.xml b/kate/part/syntax/data/ansforth94.xml new file mode 100644 index 00000000..f0a4e2ff --- /dev/null +++ b/kate/part/syntax/data/ansforth94.xml @@ -0,0 +1,601 @@ + + + + + + + ! + # + #> + #S + ' + ( + * + */ + */MOD + + + +! + +LOOP + , + - + . + ." + / + /MOD + 0< + 0= + 1+ + 1- + 2! + 2* + 2/ + 2@ + 2DROP + 2DUP + 2OVER + 2SWAP + : + ; + < + <# + = + > + >BODY + >IN + >NUMBER + >R + ?DUP + @ + ABORT + ABORT" + ABS + ACCEPT + ALIGN + ALIGNED + ALLOT + AND + BASE + BEGIN + BL + C! + C, + C@ + CELL+ + CELLS + CHAR + CHAR+ + CHARS + CONSTANT + COUNT + CR + CREATE + DECIMAL + DEPTH + DO + DOES> + DROP + DUP + ELSE + EMIT + ENVIRONMENT? + EVALUATE + EXECUTE + EXIT + FILL + FIND + FM/MOD + HERE + HOLD + I + IF + IMMEDIATE + INVERT + J + KEY + LEAVE + LITERAL + LOOP + LSHIFT + M* + MAX + MIN + MOD + MOVE + NEGATE + OR + OVER + POSTPONE + QUIT + R> + R@ + RECURSE + REPEAT + ROT + RSHIFT + S" + S>D + SIGN + SM/REM + SOURCE + SPACE + SPACES + STATE + SWAP + THEN + TYPE + U. + U< + UM* + UM/MOD + UNLOOP + UNTIL + VARIABLE + WHILE + WORD + XOR + [ + ['] + [CHAR] + ] + + + .( + .R + 0<> + 0> + 2>R + 2R> + 2R@ + :NONAME + <> + ?DO + AGAIN + C" + CASE + COMPILE, + ENDCASE + ENDOF + ERASE + FALSE + HEX + MARKER + NIP + OF + PAD + PARSE + PICK + REFILL + RESTORE-INPUT + ROLL + SAVE-INPUT + SOURCE-ID + TO + TRUE + TUCK + U.R + U> + UNUSED + VALUE + WITHIN + [COMPILE] + \ + + + #TIB + CONVERT + EXPECT + QUERY + SPAN + TIB + + + BLK + BLOCK + BUFFER + EVALUATE + FLUSH + LOAD + SAVE-BUFFERS + UPDATE + + + EMPTY-BUFFERS + LIST + REFILL + SCR + THRU + \ + + + 2CONSTANT + 2LITERAL + 2VARIABLE + D+ + D- + D. + D.R + D0< + D0= + D2* + D2/ + D< + D= + D>S + DABS + DMAX + DMIN + DNEGATE + M*/ + M+ + + + 2ROT + DU< + + + CATCH + THROW + + + ABORT + ABORT" + + + AT-XY + KEY? + PAGE + + + EKEY + EKEY>CHAR + EKEY? + EMIT? + MS + TIME&DATE + + + ( + BIN + CLOSE-FILE + CREATE-FILE + DELETE-FILE + FILE-POSITION + FILE-SIZE + INCLUDE-FILE + INCLUDED + OPEN-FILE + R/O + R/W + READ-FILE + READ-LINE + REPOSITION-FILE + RESIZE-FILE + S" + SOURCE-ID + W/O + WRITE-FILE + WRITE-LINE + + + FILE-STATUS + FLUSH-FILE + REFILL + RENAME-FILE + + + >FLOAT + D>F + F! + F* + F+ + F- + F/ + F0< + F0= + F< + F>D + F@ + FALIGN + FALIGNED + FCONSTANT + FDEPTH + FDROP + FDUP + FLITERAL + FLOAT+ + FLOATS + FLOOR + FMAX + FMIN + FNEGATE + FOVER + FROT + FROUND + FSWAP + FVARIABLE + REPRESENT + + + DF! + DF@ + DFALIGN + DFALIGNED + DFLOAT+ + DFLOATS + F** + F. + FABS + FACOS + FACOSH + FALOG + FASIN + FASINH + FATAN + FATAN2 + FATANH + FCOS + FCOSH + FE. + FEXP + FEXPM1 + FLN + FLNP1 + FLOG + FS. + FSIN + FSINCOS + FSINH + FSQRT + FTAN + FTANH + F~ + PRECISION + SET-PRECISION + SF! + SF@ + SFALIGN + SFALIGNED + SFLOAT+ + SFLOATS + + + (LOCAL) + TO + + + LOCALS| + + + ALLOCATE + FREE + RESIZE + + + .S + ? + DUMP + SEE + WORDS + + + ;CODE + AHEAD + ASSEMBLER + BYE + CODE + CS-PICK + CS-ROLL + EDITOR + STATE + [ELSE] + [IF] + [THEN] + + + FORGET + + + DEFINITIONS + FIND + FORTH-WORDLIST + GET-CURRENT + GET-ORDER + SEARCH-WORDLIST + SET-CURRENT + SET-ORDER + WORDLIST + + + ALSO + FORTH + ONLY + ORDER + PREVIOUS + + + -TRAILING + /STRING + BLANK + CMOVE + CMOVE> + COMPARE + SEARCH + SLITERAL + + + TODO + FIXME + NOTE + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/ansic89.xml b/kate/part/syntax/data/ansic89.xml new file mode 100644 index 00000000..bcd3170b --- /dev/null +++ b/kate/part/syntax/data/ansic89.xml @@ -0,0 +1,156 @@ + + + + + + + break + case + continue + default + do + else + enum + extern + for + goto + if + return + sizeof + struct + switch + typedef + union + while + + + auto + char + const + double + float + int + long + register + short + signed + static + unsigned + void + volatile + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/ansys.xml b/kate/part/syntax/data/ansys.xml new file mode 100644 index 00000000..77daa70a --- /dev/null +++ b/kate/part/syntax/data/ansys.xml @@ -0,0 +1,2064 @@ + + + + + + /BATCH + /CONFIG + /CWD + /EOF + /EXIT + /FILNAME + HELP + /INPUT + KEYW + MEMM + /MENU + /MSTART + /NERR + /OUTPUT + /STATUS + /SYP + /SYS + /UI + /UIS + /AUX2 + /AUX3 + /AUX12 + /AUX15 + FINISH + /OPT + /POST1 + /POST26 + /PREP7 + /QUIT + /RUNST + /SOLU + /ASSIGN + /CLOG + /COPY + /DELETE + /FDELE + /FTYPE + LGWRITE + *LIST + /RENAME + /COM + /GO + /GOLIST + /GOPR + /NOLIST + /NOPR + + + /CLEAR + RESUME + SAVE + /SMBC + STAT + /STITLE + /TITLE + UNDO + /UNITS + ALLSEL + ASLL + ASEL + ASLV + DOFSEL + ESEL + ESLA + ESLL + ESLN + ESLV + KSEL + KSLL + KSLN + LSEL + LSLA + LSLK + NSEL + NSLA + NSLE + NSLK + NSLL + NSLV + PARTSEL + VSEL + VSLA + CM + CMDELE + CMEDIT + CMGRP + CMLIST + CMMOD + CMPLOT + CMSEL + KWPAVE + KWPLAN + LWPLAN + NWPAVE + NWPLAN + WPAVE + WPCSYS + WPLANE + WPOFFS + WPROTA + WPSTYL + CLOCAL + CS + CSCIR + CSDELE + CSKP + CSLIST + CSWPLA + CSYS + LOCAL + FITEM + FLST + + + /CMAP + /COLOR + /DEVICE + DSYS + /DV3D + /ERASE + ERASE + /GCMD + /GCOLUMN + /GFILE + GPLOT + /GRAPHICS + /GRESUME + /GSAVE + /GTYPE + HPGL + /IMAGE + IMMED + JPEG + /MREP + /NOERASE + /PCOPY + PNGR + PSCR + /PSTATUS + /REPLOT + /RESET + /SEG + /SHOW + TIFF + /WINDOW + /ANGLE + /AUTO + /DIST + /FOCUS + /USER + /VCONE + /VIEW + /VUP + /XFRM + /ZOOM + /DSCALE + /ICLWID + /ICSCALE + /RATIO + /SHRINK + /SSCALE + /TXTRE + /VSCALE + /CPLANE + /CTYPE + /EDGE + /ESHAPE + /FACET + /GLINE + /GMARKER + GMFACE + /LIGHT + /NORMAL + /SHADE + /TRLCY + /TYPE + /CFORMAT + /CLABEL + /CONTOUR + /CVAL + /GFORMAT + /HBC + /NUMBER + /PBC + /PBF + PGSELE + /PICE + /PLOPTS + /PNUM + /PSF + /PSYMB + /TRIAD + /UDOC + /AXLAB + /GRID + /GROPT + /GRTYP + /GTHK + /XRANGE + /YRANGE + /AN3D + /ANNOT + /ANUM + /LARC + /LINE + /LSPEC + /LSYMBOL + /PCIRCLE + /PMORE + /POLYGON + /PSPEC + /PWEDGE + /TLABEL + /TSPEC + + + GET + *ABBR + ABBRES + ABBSAV + *AFUN + *ASK + *CFCLOS + *CFOPEN + *CFWRITE + *CREATE + *CYCLE + *DEL + /DFLAB + *DIM + /DIRECTORY + *DO + *DOWHILE + *ELSE + *ELSEIF + *END + *ENDDO + *ENDIF + *EXIT + *GET + *GO + *IF + /INQUIRE + /MAIL + *MFOURI + *MFUN + *MOPER + *MSG + *MWRITE + PARRES + PARSAV + /PMACRO + /PSEARCH + *REPEAT + *RETURN + *SET + *SREAD + *STATUS + *TAXIS + /TEE + *TOPER + *TREAD + /UCMD + *ULIB + *USE + *VABS + *VCOL + *VCUM + *VEDIT + *VFACT + *VFILL + *VFUN + *VGET + *VITRP + *VLEN + *VMASK + *VOPER + *VPLOT + *VPUT + *VREAD + *VSCFUN + *VSTAT + *VWRITE + /WAIT + + + AFLIST + CDREAD + CDWRITE + CDOPT + CECHECK + CHECK + CNCHECK + FC + FCCHECK + FCDELE + FCLIST + IGESOUT + NOOFFSET + NUMCMP + NUMMRG + NUMOFF + NUMSTR + DOF + ET + ETCONTROL + ETDELE + ETLIST + KEYOPT + NSVR + R + RDELE + RLIST + RMODIF + RMORE + SETFGAP + EMUNIT + *EVAL + *MOONEY + MP + MPAMOD + MPCHG + MPCOPY + MPDATA + MPDELE + MPDRES + /MPLIB + MPLIST + MPPLOT + MPREAD + MPTEMP + MPTGEN + MPTRES + MPWRITE + TBFT + UIMP + TB + TBCOPY + TBDATA + TBDELE + TBFIELD + TBLIST + TBMODIF + TBPLOT + TBPT + TBTEMP + BLC4 + BLC5 + BLOCK + CON4 + CONE + CYL4 + CYL5 + CYLIND + PCIRC + POLY + PRI2 + PRISM + PTXY + RECTNG + RPOLY + RPR4 + RPRISM + SPH4 + SPH5 + SPHERE + TORUS + GSUM + K + KBETW + KCENTER + KDELE + KDIST + KFILL + KGEN + KL + KLIST + KMODIF + KMOVE + KNODE + KPLOT + KPSCALE + KSCALE + KSUM + KSYMM + KTRAN + SOURCE + HPTCREATE + HPTDELETE + BSPLIN + CIRCLE + GSUM + L + L2ANG + L2TAN + LANG + LARC + LAREA + LCOMB + LDELE + LDIV + LDRAG + LEXTND + LFILLT + LGEN + LLIST + LPLOT + LREVERSE + LROTAT + LSSCALE + LSTR + LSUM + LSYMM + LTAN + LTRAN + SPLINE + SSLN + A + AATT + ADELE + ADGL + ADRAG + AFILLT + AGEN + AL + ALIST + ANORM + AOFFST + APLOT + AREVERSE + AROTAT + ARSCALE + ARSYM + ASKIN + ASUB + ASUM + ATRAN + GSUM + SPLOT + EXTOPT + GSUM + V + VA + VDELE + VDGL + VDRAG + VEXT + VGEN + VLIST + VLSCALE + VOFFST + VPLOT + VROTAT + VSUM + VSYMM + VTRAN + AADD + AGLUE + AINA + AINP + AINV + AOVLAP + APTN + ASBA + ASBL + ASBV + ASBW + BOPTN + BTOL + LCSL + LGLUE + LINA + LINL + LINP + LINV + LOVLAP + LPTN + LSBA + LSBL + LSBV + LSBW + VADD + VGLUE + VINP + VINV + VOVLAP + VPTN + VSBA + VSBV + VSBW + ACCAT + ACLEAR + AESIZE + AMAP + AMESH + AREFINE + CHKMSH + CLRMSHLN + CPCYC + DESIZE + EORIENT + EREFINE + ESIZE + ESYS + FVMESH + GSGDATA + IMESH + KATTA + KCLEAR + KESIZE + KMESH + KREFINE + KSCON + LATT + LCCAT + LCLEAR + LESIZE + LMESH + LREFINE + MAT + MCHECK + MODMSH + MOPT + MSHAPE + MSHCOPY + MSHKEY + MSHMID + MSHPATTERN + NREFINE + PSMESH + REAL + RTHICK + SHPP + SMRTSIZE + TCHG + TIMP + TYPE + VATT + VCLEAR + VIMP + VMESH + VEORIENT + VSWEEP + CENTER + FILL + MOVE + N + NANG + NDELE + NDIST + NGEN + NKPT + NLIST + NMODIF + NPLOT + NREAD + NROTAT + NRRANG + NSCALE + NSMOOTH + NSYM + NWRITE + QUAD + SOURCE + TRANSFER + AFSURF + E + EDELE + EGEN + EINTF + ELIST + EMID + EMODIF + EMORE + EMTGEN + EN + ENDRELEASE + ENGEN + ENORM + ENSYM + EORIENT + EPLOT + EREAD + ERRANG + ESURF + ESYM + ESYS + EWRITE + LAYLIST + LAYPLOT + LFSURF + MAT + NDSURF + REAL + SHSD + SWADD + SWDEL + SWGEN + SWLIST + TSHAP + TYPE + UPGEOM + SE + SEDLIST + SELISTL + SESYMM + SETRAN + BELLOW + BEND + BRANCH + FLANGE + MITER + PCORRO + PDRAG + PFLUID + PGAP + PINSUL + POPT + PPRES + PSPEC + PSPRNG + PTEMP + PUNIT + REDUCE + RUN + TEE + VALVE + DIG + DMOVE + DSET + DSURF + CP + CPDELE + CPINTF + CPLGEN + CPLIST + CPNGEN + CPSGEN + CE + CECYC + CEDELE + CEINTF + CELIST + CERIG + CESGEN + RBE3 + NOORDER + WAVES + WERASE + WFRONT + WMID + WMORE + WSORT + WSTART + FLDATA + FLDATA1 + FLDATA2 + FLDATA3 + FLDATA4 + FLDATA4A + FLDATA5 + FLDATA6 + FLDATA7 + FLDATA8 + FLDATA9 + FLDATA10 + FLDATA11 + FLDATA12 + FLDATA13 + FLDATA14 + FLDATA15 + FLDATA16 + FLDATA17 + FLDATA18 + FLDATA19 + FLDATA20 + FLDATA20A + FLDATA20B + FLDATA21 + FLDATA22 + FLDATA23 + FLDATA24 + FLDATA24A + FLDATA24B + FLDATA24C + FLDATA24D + FLDATA24E + FLDATA24F + FLDATA24G + FLDATA24H + FLDATA40 + FLDATA25 + FLDATA26 + FLDATA34 + FLDATA27 + FLDATA28 + FLDATA29 + FLDATA30 + FLDATA31 + FLDATA32 + FLDATA33 + FLDATA35 + FLDATA36 + FLDATA37 + FLDATA38 + FLDATA39 + ICVFRC + PLVFRC + MSADV + MSCAP + MSDATA + MSMASS + MSMETH + MSMIR + MSNOMF + MSPROP + MSQUAD + MSRELAX + MSSOLU + MSSPEC + MSTERM + MSVARY + /CYCEXPAND + CYCLIC + CYCOPT + EMSYM + HFEREFINE + PERBC2D + PHYSICS + RACE + AREAS + BOOL + CEQN + COUPLE + DIGIT + ELEM + ETYPE + FATIGUE + FEBODY + FECONS + FEFOR + FESURF + FLOTRAN + GEOMETRY + KEYPTS + LINE + MATER + MESHING + NODES + PIPE + PMETH + PRIM + RCON + REORDER + SELM + TBLE + VOLUMES + EDASMP + EDBOUND + EDBX + EDCGEN + EDCLIST + EDCMORE + EDCNSTR + EDCONTACT + EDCRB + EDCURVE + EDDBL + EDDC + EDIPART + EDLCS + EDMP + EDNB + EDNDTSD + EDNROT + EDPART + EDPC + EDSP + EDWELD + ALPFILL + ARCOLLAPSE + ARDETACH + ARFILL + ARMERGE + ARSPLIT + GAPFINISH + GAPLIST + GAPMERGE + GAPOPT + GAPPLOT + LNCOLLAPSE + LNDETACH + LNFILL + LNMERGE + LNSPLIT + SARPLOT + SLPPLOT + SLSPLOT + VCVFILL + BSAX + BSM1 + BSM2 + BSMD + BSS1 + BSS2 + BSTE + BSTQ + PRSSOL + SDELETE + SECDATA + SECJOINT + /SECLIB + SECLOCK + SECNUM + SECOFFSET + SECPLOT + SECREAD + SECSTOP + SECTYPE + SECWRITE + SLIST + SSBT + SSMT + SSPA + SSPB + SSPD + SSPE + SSPM + MORPH + DAMORPH + DEMORPH + DVMORPH + TZAMESH + TZDELE + TZEGEN + PMLOPT + PMLSIZE + FSAN + FSCO + FSDT + FSIN + FSIT + FSOR + FSOU + FSRE + FSSTAT + FSTI + FSTR + + + ABEXTRACT + ADAMS + ADAPT + ANTYPE + BCSOPTION + CECHECK + CHECK + CMATRIX + CMSOPT + CNCHECK + CUTCONTROL + CYCOPT + DMPEXT + DSPROC + DSOPT + EMATWRITE + EQSLV + ERESX + ESCHECK + ESSOLV + EXPASS + FSRS + FSSOLV + GAUGE + GMATRIX + HFEIGOPT + HFPA + HFPCSWP + HFSCAT + HFSWEEP + LMATRIX + LUMPM + MONITOR + MSAVE + OPNCONTROL + PRECISION + PSCONTROL + PSOLVE + RATE + RSTOFF + SEEXP + SEGEN + SEOPT + SOLCONTROL + /SOLU + SOLVE + SPSCAN + SPSWP + STAOPT + TOFFST + PCONV + PEXCLUDE + PINCLUDE + /PMETH + PMOPTS + PPRANGE + ARCLENA + ARCTRM + BUCOPT + CNVTOL + CRPLIM + /GST + LNSRCH + MXPAND + NCNV + NEQIT + NLDIAG + NLDPOST + NLGEOM + NLHIST + NROPT + PRED + PSTRES + SSTIF + SUBOPT + ALPHAD + BETAD + DMPRAT + HARFRQ + HREXP + HROPT + HROUT + LVSCALE + MDAMP + MDPLOT + MODOPT + MXPAND + RIGID + SUBOPT + TIMINT + TINTP + TRNOPT + ADDAM + COVAL + CQC + DSUM + FREQ + GRP + NRLSUM + PFACT + PSDCOM + PSDFRQ + PSDGRAPH + PSDSPL + PSDUNIT + PSDVAL + PSDWAV + QDVAL + ROCK + SED + SPOPT + SRSS + SV + SVTYP + VDDAM + AUTOTS + CECMOD + DELTIM + EXPSOL + HMAGSOLV + KBC + KUSE + MAGOPT + MAGSOLV + MODE + NSUBST + NUMEXP + TIME + TREF + TSRES + UPCOORD + USRCAL + WRFULL + DA + DADELE + DALIST + DK + DKDELE + DKLIST + DL + DLDELE + DLLIST + DTRAN + FK + FKDELE + FKLIST + FTRAN + SFA + SFADELE + SFALIST + SFL + SFLDELE + SFLLIST + SFTRAN + BFA + BFADELE + BFALIST + BFK + BFKDELE + BFKLIST + BFL + BFLDELE + BFLLIST + BFTRAN + BFV + BFVDELE + BFVLIST + ACEL + CGLOC + CGOMGA + CMACEL + CMDOMEGA + CMOMEGA + DCGOMG + DOMEGA + IRLF + OMEGA + BIOT + FMAGBC + HFPORT + IC + ICDELE + ICE + ICEDELE + ICELIST + ICLIST + ISFILE + MPCHG + OUTPR + OUTRES + PGRAPH + PGSAVE + PGWRITE + PLWAVE + RESCONTROL + SBCLIST + SBCTRAN + WSPRINGS + LSCLEAR + LSDELE + LSREAD + LSSOLVE + LSWRITE + M + MDELE + MGEN + MLIST + TOTAL + GP + GPDELE + GPLIST + REZONE + MAPSOLVE + REMESH + AREMESH + EALIVE + EKILL + ESTIF + D + DCUM + DDELE + DJ + DJDELE + DJLIST + DLIST + DSCALE + DSYM + GSBDATA + GSLIST + LDREAD + F + FCUM + FDELE + FJ + FJDELE + FJLIST + FLIST + FSCALE + SF + SFBEAM + SFCUM + SFDELE + SFE + SFEDELE + SFELIST + SFFUN + SFGRAD + SFLIST + SFSCALE + BF + BFCUM + BFDELE + BFE + BFECUM + BFEDELE + BFELIST + BFESCAL + BFLIST + BFSCALE + BFUNIF + LDREAD + RIMPORT + TUNIF + BIOOPT + DEACT + DYNOPT + GAP + GENOPT + INRTIA + LSOPER + MASTER + NLOPT + OUTOPT + SMBODY + SMCONS + SMFOR + SMSURF + SOLUOPT + SPTOPT + EDADAPT + EDALE + EDBVIS + EDCADAPT + EDCPU + EDCSC + EDCTS + EDDAMP + EDDRELAX + EDDUMP + EDENERGY + EDFPLOT + EDGCALE + EDHGLS + EDHIST + EDHTIME + EDINT + EDIS + EDLOAD + EDOPT + EDOUT + EDPL + EDPVEL + EDRC + EDRD + EDRI + EDRST + EDRUN + EDSHELL + EDSOLV + EDSTART + EDTERM + EDTP + EDVEL + EDWRITE + REXPORT + FLOCHECK + PEMOPTS + HEMIOPT + RADOPT + RDEC + RSURF + RSYMM + SPCNOD + SPCTEMP + STEF + V2DOPT + VFCALC + VFOPT + QSOPT + FSAN + FSCO + FSDT + FSIN + FSIT + FSOR + FSOU + FSRE + FSRS + FSSTAT + FSTI + FSTR + MFANALYSIS + MFBUCKET + MFCALC + MFCLEAR + MFCMMAND + MFCONV + MFDTIME + MFELEM + MFEM + MFEXTER + MFFNAME + MFIMPORT + MFINTER + MFITER + MFLIST + MFMAP + MFORDER + MFOUTPUT + MFRELAX + MFRSTART + MFSURFACE + MFTIME + MFTOL + MFVOLUME + + + APPEND + DETAB + DNSOL + FILE + HRCPLX + PGRSET + /POST1 + RESET + SET + SUBSET + AVPRIN + AVRES + /EFACET + ERNORM + FORCE + INRES + LAYER + RSYS + SHELL + NSORT + NUSORT + PLCONV + PLDISP + PLESOL + PLNSOL + PLVECT + PPLOT + PRCONV + PRESOL + PRJSOL + PRNLD + PRNSOL + PRRFOR + PRRSOL + PRSSOL + PRVECT + SUMTYPE + DESOL + ESORT + ETABLE + EUSORT + PLETAB + PLLS + PLVECT + PRETAB + PRVECT + SABS + SADD + SALLOW + SEXP + SFACT + SFCALC + SMAX + SMIN + SMULT + SSUM + TALLOW + VCROSS + VDOT + /FORMAT + /HEADER + IRLIST + /PAGE + PRERR + PRITER + ANCNTR + ANCUT + ANDATA + ANDSCL + ANCYC + ANDYNA + /ANFILE + ANFLOW + ANHARM + ANIM + ANISOS + ANMODE + ANMRES + ANTIME + TRTIME + PADELE + PAGET + PAPUT + PARESU + PASAVE + PATH + PCALC + PCROSS + PDEF + PDOT + PLPAGM + PLPATH + PLSECT + PMAP + PPATH + PRANGE + PRPATH + PRSECT + PSEL + PVECT + SUCALC + SUCR + SUDEL + SUEVAL + SUGET + SUMAP + SUPL + SUPR + SURESU + SUSAVE + SUSEL + SUVECT + LCABS + LCASE + LCDEF + LCFACT + LCFILE + LCOPER + LCSEL + LCSUM + LCWRITE + LCZERO + RAPPND + CURR2D + EMAGERR + EMF + EMFT + FLUXV + FMAGSUM + FOR2D + IMPD + MMF + PLF2D + POWERH + QFACT + REFLCOEF + SENERGY + SPARM + TORQ2D + TORQC2D + TORQSUM + FE + FELIST + FL + FLLIST + FP + FPLIST + FS + FSDELE + FSLIST + FSNODE + FSPLOT + FSSECT + FTCALC + FTSIZE + FTWRITE + PLTRAC + TRPDEL + TRPLIS + TRPOIN + FLREAD + BFINT + CBDOF + CMSFILE + /CYCEXPAND + CYCPHASE + /EXPAND + EXPAND + FSSPARM + FSUM + HFANG + HFARRAY + HFNEAR + HFPOWER + HFSYM + INTSRF + KCALC + NFORCE + PLCRACK + PLHFFAR + PLSCH + PLSYZ + PRHFFAR + PRSYZ + RMFLVEC + RSPLIT + SPOINT + DATADEF + DEFINE + DISPLAY + LCCALC + POINT + PRINT + SORT + SPEC + FC + FCCHECK + FCDELE + FCLIST + + + ANSOL + DATA + EDREAD + ESOL + FILE + GAPF + GSSOL + JSOL + NSOL + NSTORE + NUMVAR + /POST26 + RESET + RFORCE + /RGB + SOLU + STORE + TIMERANGE + VARDEL + VARNAM + CFACT + FORCE + LAYERP26 + SHELL + TVAR + ABS + ADD + ATAN + CLOG + CONJUG + DERIV + EXP + FILLDATA + IMAGIN + INT1 + LARGE + NLOG + PROD + QUOT + REALVAR + SMALL + SQRT + PLCPLX + PLTIME + PLVAR + SPREAD + XVAR + EXTREM + LINES + NPRINT + PRCPLX + PRTIME + PRVAR + CVAR + PMGTRAN + RESP + RPSD + SMOOTH + VGET + VPUT + DEFINE + OPERATE + PLOTTING + PRINT + + + /AUX2 + DUMP + FILEAUX2 + FORM + HBMAT + + + /AUX3 + COMPRESS + DELETE + FILEAUX3 + LIST + MODIFY + UNDELETE + + + /AUX12 + EMIS + GEOM + MPRINT + SPACE + STEF + VFQUERY + VTYPE + WRITE + + + /AUX15 + IGESIN + IOPTN + + + RALL + RFILSZ + RITER + RMEMRY + RSPEED + RSTAT + RTIMST + /RUNST + RWFRNT + + + /OPT + OPEQN + OPFACT + OPFRST + OPGRAD + OPKEEP + OPLOOP + OPPRNT + OPRAND + OPSUBP + OPSWEEP + OPTYPE + OPUSER + OPVAR + TOCOMP + TODEF + TOFREQ + TOTYPE + TOVAR + OPADD + OPCLR + OPDEL + OPMAKE + OPSEL + OPANL + OPDATA + OPRESU + OPSAVE + OPEXE + TOEXE + TOLOOP + OPLFA + OPLGR + OPLIST + OPLSW + OPRFA + OPRGR + OPRSW + PLVAROPT + PRVAROPT + TOGRAPH + TOLIST + TOPLOT + TOPRINT + TOSTAT + XVAROPT + + + /PDS + PDANL + PDCORR + PDINQR + PDPLOT + PDVAR + PDDOEL + PDDMCS + PDMETH + PDUSER + PDEXE + PDCDF + PDCMAT + PDHIST + PDPINV + PDPROB + PDROPT + PDSCAT + PDSENS + PDSHIS + PDWRITE + PDCLR + PDRESU + PDSAVE + RSFIT + RSPLOT + RSPRNT + RSSIMS + + + /CMAP + /DEVDISP + FILEDISP + HELPDISP + NOCOLOR + /SEG + /SHOWDISP + TRANS + GRPH + TERM + FINISH + PLOT + STAT + + + RMRESUME + RMSAVE + RMNDISP + RMNEVEC + RMALIST + RMANL + RMASTER + RMCAP + RMCLIST + RMMLIST + RMMRANGE + RMMSELECT + RMPORDER + RMRGENERATE + RMROPTIONS + RMRPLOT + RMRSTATUS + RMSMPLE + RMXPORT + DCVSWP + RMLVSCALE + RMUSE + + + ABS + SIGN + EXP + LOG + LOG10 + SQRT + NINT + MOD + RAND + GDIS + SIN + COS + TAN + SINH + COSH + TANH + ASIN + ACOS + ATAN + ATAN2 + VALCHR + CHRVAL + UPCASE + LWCASE + + + LINK1 + PLANE2 + BEAM3 + BEAM4 + SOLID5 + COMBIN7 + LINK8 + INFIN9 + LINK10 + LINK11 + CONTAC12 + PLANE13 + COMBIN14 + PIPE16 + PIPE17 + PIPE18 + PIPE20 + MASS21 + BEAM23 + BEAM24 + PLANE25 + MATRIX27 + SHELL28 + FLUID29 + FLUID30 + LINK31 + LINK32 + LINK33 + LINK34 + PLANE35 + SOURC36 + COMBIN37 + FLUID38 + COMBIN39 + COMBIN40 + SHELL41 + PLANE42 + SHELL43 + BEAM44 + SOLID45 + SOLID46 + INFIN47 + MATRIX50 + SHELL51 + CONTAC52 + PLANE53 + BEAM54 + PLANE55 + HYPER56 + SHELL57 + HYPER58 + PIPE59 + PIPE60 + SHELL61 + SOLID62 + SHELL63 + SOLID64 + SOLID65 + PLANE67 + LINK68 + SOLID69 + SOLID70 + MASS71 + HYPER74 + PLANE75 + PLANE77 + PLANE78 + FLUID79 + FLUID80 + FLUID81 + PLANE82 + PLANE83 + HYPER84 + HYPER86 + SOLID87 + VISCO88 + VISCO89 + SOLID90 + SHELL91 + SOLID92 + SHELL93 + CIRCU94 + SOLID95 + SOLID96 + SOLID97 + SOLID98 + SHELL99 + VISCO106 + VISCO107 + VISCO108 + TRANS109 + INFIN110 + INFIN111 + INTER115 + FLUID116 + SOLID117 + HF118 + HF119 + HF120 + PLANE121 + SOLID122 + SOLID123 + CIRCU124 + CIRCU125 + TRANS126 + SOLID127 + SOLID128 + FLUID129 + FLUID130 + SHELL131 + SHELL132 + FLUID136 + FLUID138 + FLUID139 + FLUID141 + FLUID142 + SHELL143 + ROM144 + PLANE145 + PLANE146 + SOLID147 + SOLID148 + SHELL150 + SURF151 + SURF152 + SURF153 + SURF154 + SHELL157 + HYPER158 + LINK160 + BEAM161 + PLANE162 + SHELL163 + SOLID164 + COMBI165 + MASS166 + LINK167 + SOLID168 + TARGE169 + TARGE170 + CONTA171 + CONTA172 + CONTA173 + CONTA174 + CONTA175 + CONTA178 + PRETS179 + LINK180 + SHELL181 + PLANE182 + PLANE183 + MPC184 + SOLID185 + SOLID186 + SOLID187 + BEAM188 + BEAM189 + SOLSH190 + SOLID191 + INTER192 + INTER193 + INTER194 + INTER195 + MESH200 + FOLLW201 + SHELL208 + SHELL209 + PLANE223 + SOLID226 + SOLID227 + PLANE230 + SOLID231 + SOLID232 + SURF251 + SURF252 + + + EX + EY + EZ + GXY + NUXY + ALPX + ALPY + ALPZ + CTEX + CTEY + CTEZ + THSX + THSY + THSZ + REFT + PRXY + PRYZ + PRXZ + NUXY + NUYZ + NUXZ + GXY + GYZ + GXZ + DAMP + DMPR + MU + DENS + C + ENTH + KXX + KYY + KZZ + HF + EMIS + QRATE + VISC + SONC + RSVX + RSVY + RSVZ + PERX + MURX + MURY + MURZ + MGXX + MGYY + MGZZ + LSST + SBKX + + + UX + UY + UZ + FX + FY + FZ + MX + MY + MZ + HEAT + HBOT + HE2 + HE3 + HTOP + FLOW + AMPS + CHRG + CSGX + CSGY + CSGZ + ROTX + ROTY + ROTZ + WARP + TEMP + TBOT + TE2 + TE3 + TTOP + PRES + VX + VY + VZ + ENKE + ENDS + SP01 + SP02 + SP03 + SP04 + SP05 + SP06 + VOLT + MAG + AX + AYAZ + + + STATIC + BUCKLE + MODAL + HARMIC + TRANS + SUBSTR + SPECTR + + + + + + NX + NY + NZ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/apache.xml b/kate/part/syntax/data/apache.xml new file mode 100644 index 00000000..db8b2cc1 --- /dev/null +++ b/kate/part/syntax/data/apache.xml @@ -0,0 +1,634 @@ + + + + + + + + + +AcceptFilter +AccessFileName +Action +AddAlt +AddAltByEncoding +AddAltByType +AddCharset +AddDefaultCharset +AddDescription +AddEncoding +AddHandler +AddIcon +AddIconByEncoding +AddIconByType +AddInputFilter +AddLanguage +AddModuleInfo +AddOutputFilter +AddOutputFilterByType +AddType +Alias +AliasMatch +Allow +Anonymous +AuthBasicProvider +AuthDBMGroupFile +AuthDBMUserFile +AuthDigestDomain +AuthDigestFile +AuthDigestGroupFile +AuthDigestNonceFormat +AuthDigestProvider +AuthGroupFile +AuthLDAPBindDN +AuthLDAPBindPassword +AuthLDAPCharsetConfig +AuthLDAPGroupAttribute +AuthLDAPUrl +AuthName +AuthUserFile +BrowserMatch +BrowserMatchNoCase +BS2000Account +CacheDisable +CacheEnable +CacheFile +CacheGcClean +CacheGcUnused +CacheRoot +CGIMapExtension +CharsetDefault +CharsetOptions +CharsetSourceEnc +CookieDomain +CookieLog +CookieName +CoreDumpDirectory +CustomLog +Dav +DavGenericLockDB +DavLockDB +DBDParams +DBDPrepareSQL +DBDriver +DefaultIcon +DefaultLanguage +DefaultType +DeflateFilterNote +Deny +DirectoryIndex +DocumentRoot +ErrorDocument +ErrorLog +Example +ExpiresByType +ExpiresDefault +ExtFilterDefine +ExtFilterOptions +FilterChain +FilterDeclare +FilterProtocol +FilterProvider +FilterTrace +ForceType +ForensicLog +Group +Header +HeaderName +ImapBase +Include +IndexIgnore +IndexOptions +IndexStyleSheet +ISAPICacheFile +LanguagePriority +LDAPSharedCacheFile +LDAPTrustedCA +LDAPTrustedCAType +LDAPTrustedClientCert +LDAPTrustedGlobalCert +Listen +LoadFile +LoadModule +LockFile +LogFormat +MetaDir +MetaSuffix +MimeMagicFile +MMapFile +NameVirtualHost +NoProxy +NWSSLTrustedCerts +NWSSLUpgradeable +PassEnv +PidFile +ProxyBlock +ProxyDomain +ProxyPass +ProxyPassReverse +ProxyPassReverseCookieDomain +ProxyPassReverseCookiePath +ProxyRemote +ProxyRemoteMatch +ReadmeName +Redirect +RedirectMatch +RedirectPermanent +RedirectTemp +RemoveCharset +RemoveEncoding +RemoveHandler +RemoveInputFilter +RemoveLanguage +RemoveOutputFilter +RemoveType +RequestHeader +Require +RewriteBase +RewriteCond +RewriteLock +RewriteLog +RewriteMap +RewriteRule +ScoreBoardFile +Script +ScriptAlias +ScriptAliasMatch +ScriptLog +ScriptSock +SecureListen +ServerAdmin +ServerAlias +ServerName +ServerPath +ServerRoot +SetEnv +SetEnvIf +SetEnvIfNoCase +SetHandler +SetInputFilter +SetOutputFilter +SSIEndTag +SSIErrorMsg +SSIStartTag +SSITimeFormat +SSIUndefinedEcho +SSLCACertificateFile +SSLCACertificatePath +SSLCADNRequestFile +SSLCADNRequestPath +SSLCARevocationFile +SSLCARevocationPath +SSLCertificateChainFile +SSLCertificateFile +SSLCertificateKeyFile +SSLCipherSuite +SSLCryptoDevice +SSLHonorCiperOrder +SSLPassPhraseDialog +SSLProxyCACertificateFile +SSLProxyCACertificatePath +SSLProxyCARevocationFile +SSLProxyCARevocationPath +SSLProxyCipherSuite +SSLProxyMachineCertificateFile +SSLProxyMachineCertificatePath +SSLProxyProtocol +SSLRandomSeed +SSLRequire +SSLRequireSSL +SSLUserName +SuexecUserGroup +TransferLog +TypesConfig +UnsetEnv +User +UserDir +VirtualDocumentRoot +VirtualDocumentRootIP +VirtualScriptAlias +VirtualScriptAliasIP +Win32DisableAcceptEx + + + +AllowCONNECT +AssignUserID +AuthDigestNonceLifetime +AuthDigestShmemSize +CacheDefaultExpire +CacheDirLength +CacheDirLevels +CacheForceCompletion +CacheGcDaily +CacheGcInterval +CacheGcMemUsage +CacheLastModifiedFactor +CacheMaxExpire +CacheMaxFileSize +CacheMinFileSize +CacheSize +CacheTimeMargin +ChildPerUserID +CookieExpires +DavMinTimeout +DBDExptime +DBDKeep +DBDMax +DBDMin +DBDPersist +DeflateBufferSize +DeflateCompressionLevel +DeflateMemLevel +DeflateWindowSize +IdentityCheckTimeout +ISAPIReadAheadBuffer +KeepAliveTimeout +LDAPCacheEntries +LDAPCacheTTL +LDAPConnectionTimeout +LDAPOpCacheEntries +LDAPOpCacheTTL +LDAPSharedCacheSize +LimitInternalRecursion +LimitRequestBody +LimitRequestFields +LimitRequestFieldsize +LimitRequestLine +LimitXMLRequestBody +ListenBacklog +MaxClients +MaxKeepAliveRequests +MaxMemFree +MaxRequestsPerChild +MaxRequestsPerThread +MaxSpareServers +MaxSpareThreads +MaxThreads +MaxThreadsPerChild +MCacheMaxObjectCount +MCacheMaxObjectSize +MCacheMaxStreamingBuffer +MCacheMinObjectSize +MCacheSize +MinSpareServers +MinSpareThreads +NumServers +ProxyIOBufferSize +ProxyMaxForwards +ProxyReceiveBufferSize +ProxyTimeout +RewriteLogLevel +RLimitCPU +RLimitMEM +RLimitNPROC +ScriptLogBuffer +ScriptLogLength +SendBufferSize +ServerLimit +SSLProxyVerifyDepth +SSLSessionCacheTimeout +SSLVerifyDepth +StartServers +StartThreads +ThreadLimit +ThreadsPerChild +ThreadStackSize +TimeOut + + + +AcceptMutex +AcceptPathInfo +AllowEncodedSlashes +AllowOverride +Anonymous_Authoritative +Anonymous_LogEmail +Anonymous_MustGiveEmail +Anonymous_NoUserID +Anonymous_VerifyEmail +AuthAuthoritative +AuthBasicAuthoritative +AuthBasicProvider +AuthDBMAuthoritative +AuthDBMType +AuthDefaultAuthoritative +AuthDigestAlgorithm +AuthDigestNcCheck +AuthDigestQop +AuthLDAPAuthoritative +AuthLDAPCompareDNOnServer +AuthLDAPDereferenceAliases +AuthLDAPEnabled +AuthLDAPFrontPageHack +AuthLDAPGroupAttributeIsDN +AuthLDAPRemoteUserIsDN +AuthType +AuthzDBMAuthoritative +AuthzDBMType +AuthzDefaultAuthoritative +AuthzGroupFileAuthoritative +AuthzLDAPAuthoritative +AuthzOwnerAuthoritative +AuthzUserAuthoritative +BufferedLogs +CacheExpiryCheck +CacheIgnoreCacheControl +CacheIgnoreHeaders +CacheIgnoreNoLastMod +CacheNegotiatedDocs +CacheStoreNoStore +CacheStorePrivate +CheckSpelling +ContentDigest +CookieStyle +CookieTracking +CoreDumpDirectory +CustomLog +DavDepthInfinity +DirectorySlash +DumpIOInput +DumpIOOutput +EnableExceptionHook +EnableMMAP +EnableSendfile +ExpiresActive +ExtendedStatus +FileETag +ForceLanguagePriority +HostnameLookups +IdentityCheck +ImapDefault +ImapMenu +IndexOrderDefault +ISAPIAppendLogToErrors +ISAPIAppendLogToQuery +ISAPIFakeAsync +ISAPILogNotSupported +KeepAlive +LDAPTrustedMode +LDAPVerifyServerCert +LogLevel +MCacheRemovalAlgorithm +MetaFiles +ModMimeUsePathInfo +MultiviewsMatch +Options +Order +ProtocolEcho +ProxyBadHeader +ProxyErrorOverride +ProxyPreserveHost +ProxyRequests +ProxyVia +RewriteEngine +RewriteOptions +Satisfy +ScriptInterpreterSource +ServerSignature +ServerTokens +SSLEngine +SSLMutex +SSLOptions +SSLProtocol +SSLProxyEngine +SSLProxyVerify +SSLSessionCache +SSLVerifyClient +UseCanonicalName +XBitHack + + + +On +Off +Default +flock +fcntl +posixsem +pthread +sysvsem +All +None +AuthConfig +FileInfo +Indexes +Limit +Options + +ExecCGI +FollowSymLinks +Includes +IncludesNOEXEC +Indexes +MultiViews +SymLinksIfOwnerMatch +StdEnvVars +CompatEnvVars +ExportCertData +FakeBasicAuth +StrictRequire +OptRenegotiate + +SDBM +GDBM +NDBM +DB +MD5 +MD5-sess +auth +auth-int +never +searching +finding +always +Basic +Digest +Connection +Keep-Alive +Proxy-Authenticate +Proxy-Authorization +TE +Trailers +Transfer-Encoding +Upgrade +Netscape +Cookie +Cookie2 +RFC2109 +RFC2965 +INode +MTime +Size +Prefer +Fallback +Double +error +nocontent +map +referer +formatted +semiformatted +unformatted +Ascending +Descending +Name +Date +Size +Description +SSL +TLS +STARTTLS +emerg +alert +crit +error +warn +notice +info +debug +LRU +GDSF +Any +NegotiatedOnly +Filters +Handlers +Deny,Allow +Allow,Deny +Mutual-failure +IsError +Ignore +StartBody +Full +Block +inherit +Registry +Registry-Strict +Script +EMail +Major +Minor +Min +Minimal +Prod +ProductOnly +OS +Full +optional +posixsem +sysvsem +sem +pthread +fcntl: +flock: +file: +yes +no +SSLv2 +SSLv3 +TLSv1 +require +optional_no_ca +nonenotnull +dbm: +shm: +dc: +DNS + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/asm-avr.xml b/kate/part/syntax/data/asm-avr.xml new file mode 100644 index 00000000..49bafba0 --- /dev/null +++ b/kate/part/syntax/data/asm-avr.xml @@ -0,0 +1,212 @@ + + + + + + + + adc + add + adiw + and + andi + asr + bclr + bld + brbc + brbs + brcc + brcs + break + breq + brge + brhc + brhs + brid + brie + brlo + brlt + brmi + brne + brpl + brsh + brtc + brts + brvc + brvs + bset + bst + call + cbi + cbr + clc + clh + cli + cln + clr + cls + clt + clv + clz + com + cp + cpc + cpi + cpse + dec + eicall + eijmp + elpm + eor + fmul + fmuls + fmulsu + icall + ijmp + in + inc + jmp + ld + ldd + ldi + lds + lpm + lsl + lsr + mov + movw + mul + muls + mulsu + neg + nop + or + ori + out + pop + push + rcall + ret + reti + rjmp + rol + ror + sbc + sbr + sbrc + sbrs + sec + seh + sbi + sbci + sbic + sbis + sbiw + sei + sen + ser + ses + set + sev + sez + sleep + spm + st + std + sts + sub + subi + swap + tst + wdr + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/asm-dsp56k.xml b/kate/part/syntax/data/asm-dsp56k.xml new file mode 100644 index 00000000..7daca638 --- /dev/null +++ b/kate/part/syntax/data/asm-dsp56k.xml @@ -0,0 +1,329 @@ + + + + + + + + x + x0 + x1 + y + y0 + y1 + a2 + a1 + a0 + a + a10 + ab + b2 + b1 + b0 + b + b10 + ba + + + + r0 + r1 + r2 + r3 + r4 + r5 + r6 + r7 + + + + n0 + n1 + n2 + n3 + n4 + n5 + n6 + n7 + + + + m0 + m1 + m2 + m3 + m4 + m5 + m6 + m7 + + + + la + lc + pc + ssh + ssl + omr + sr + sp + mr + ccr + + + + abs + adc + add + addl + addr + and + andi + asl + asr + bchg + bclr + bset + btst + clr + cmp + cmpm + div + do + enddo + eor + illegal + jcc + jhs + jcs + jls + jec + jeq + jes + jge + jgt + jlc + jle + jls + jlt + jmi + jne + jnr + jpl + jnn + jclr + jmp + jscc + jshs + jscs + jsls + jsec + jseq + jses + jsge + jsgt + jslc + jsle + jsls + jslt + jsmi + jsne + jsnr + jspl + jsnn + jsclr + jset + jsr + jsset + lsl + lsr + lua + mac + macr + move + movec + movem + movep + mpy + mpyr + neg + nop + norm + not + or + ori + rep + reset + rnd + rol + ror + rti + rts + sbc + stop + sub + subl + subr + swi + tcc + ths + tcs + tls + tec + teq + tes + tge + tgt + tlc + tle + tls + tlt + tmi + tne + tnr + tpl + snn + tfr + tst + wait + + + + endif + endc + else + ifne + if + ifeq + ifle + iflt + ifge + ifgt + include + incbin + printval + pass1val + pass2val + fail + endm + end + org + ds + dsm + list + nolist + macro + dc + equ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/asm-m68k.xml b/kate/part/syntax/data/asm-m68k.xml new file mode 100644 index 00000000..74430231 --- /dev/null +++ b/kate/part/syntax/data/asm-m68k.xml @@ -0,0 +1,651 @@ + + + + + + + + d0 + d1 + d2 + d3 + d4 + d5 + d6 + d7 + + + + a0 + a1 + a2 + a3 + a4 + a5 + a6 + a7 + sp + + + + ccr + sr + pc + zpc + ssp + usp + msp + isp + dfc + cacr + caar + vbr + crp + srp + urp + tc + tt0 + tt1 + mmusr + itt0 + itt1 + dtt0 + dtt1 + buscr + pcr + ic + + bc + + + + fp0 + fp1 + fp2 + fp3 + fp4 + fp5 + fp6 + fp7 + fpcr> + fpsr + fpiar + + + + abcd + adda + addi + add + addq + addx + andi + and + asl + asr + bcc + bchg + bclr + bcs + beq + bfchg + bfclr + bfexts + bfextu + bfffo + bfins + bfset + bftst + bge + bgt + bhi + bhs + bkpt + ble + blo + bls + blt + bmi + bne + bpl + bra + bset + bsr + btst + bvc + bvs + callm + cas2 + cas + chk2 + chk + cinva + cinvl + cinvp + clr + cmp2 + cmpa + cmpi + cmp + cmpm + cpusha + cpushl + cpushp + dbcc + dbcs + dbeq + dbf + dbge + dbgt + dbhi + dbhs + dble + dblo + dbls + dblt + dbmi + dbne + dbpl + dbra + dbt + dbvc + dbvc + divs + divsl + divu + divul + eori + eor + exg + extb + ext + illegal + jmp + jsr + lea + link + lpstop + lsl + lsr + move16 + movea + movec + move + movem + movep + moveq + moves + muls + mulu + nbcd + neg + negx + nop + not + ori + or + pack + pea + pflusha + pflushan + pflush + pflushn + ploadr + ploadw + plpa + pmovefd + pmove + ptestr + ptestw + reset + rol + ror + roxl + roxr + rtd + rte + rtm + rtr + rts + sbcd + scc + scs + seq + sf + sge + sgt + shi + shs + sle + slo + sls + slt + smi + sne + spl + st + stop + suba + subi + sub + subq + subx + svc + svc + swap + tas + trapcc + trapcs + trapeq + trapf + trapge + trapgt + traphi + traphs + trap + traple + traplo + trapls + traplt + trapmi + trapne + trappl + trapt + trapvc + trapvc + trapv + tst + unlk + unpk + + + + fabs + facos + fadd + fasin + fatanh + fatan + fbeq + fbf + fbge + fbgle + fbgl + fbgt + fble + fblt + fbne + fbnge + fbngle + fbngl + fbngt + fbnle + fbnlt + fboge + fbogl + fbogt + fbole + fbolt + fbor + fbseq + fbsf + fbsne + fbst + fbt + fbueq + fbuge + fbugt + fbule + fbult + fbun + fcmp + fcosh + fcos + fdabs + fdadd + fdbeq + fdbf + fdbge + fdbgle + fdbgl + fdbgt + fdble + fdblt + fdbne + fdbnge + fdbngle + fdbngl + fdbngt + fdbnle + fdbnlt + fdboge + fdbogl + fdbogt + fdbole + fdbolt + fdbor + fdbseq + fdbsf + fdbsne + fdbst + fdbt + fdbueq + fdbuge + fdbugt + fdbule + fdbult + fdbun + fddiv + fdiv + fdmove + fdmul + fdneg + fdsqrt + fdsub + fetox + fetoxm1 + fgetexp + fgetman + fint + fintrz + flog10 + flog2 + flogn + flognp1 + fmod + fmovecr + fmove + fmovem + fmul + fneg + fnop + frem + frestore + fsabs + fsadd + fsave + fscale + fsdiv + fseq + fsf + fsge + fsgldiv + fsgle + fsgl + fsglmul + fsgt + fsincos + fsinh + fsin + fsle + fslt + fsmove + fsmul + fsneg + fsne + fsnge + fsngle + fsngl + fsngt + fsnle + fsnlt + fsoge + fsogl + fsogt + fsole + fsolt + fsor + fsqrt + fsseq + fssf + fssne + fssqrt + fsst + fssub + fst + fsub + fsueq + fsuge + fsugt + fsule + fsult + fsun + ftanh + ftan + ftentox + ftrapeq + ftrapf + ftrapge + ftrapgle + ftrapgl + ftrapgt + ftraple + ftraplt + ftrapne + ftrapnge + ftrapngle + ftrapngl + ftrapngt + ftrapnle + ftrapnlt + ftrapoge + ftrapogl + ftrapogt + ftrapole + ftrapolt + ftrapor + ftrapseq + ftrapsf + ftrapsne + ftrapst + ftrapt + ftrapueq + ftrapuge + ftrapugt + ftrapule + ftrapult + ftrapun + ftst + ftwotox + + + + align + blk + bss + clrfo + clrso + cnop + code + cseg + data + dc + dcb + ds + dseg + else + end + endc + endif + endm + endr + equ + even + fo + idnt + if + ifeq + ifne + ifgt + ifge + iflt + ifle + ifd + ifnd + ifc + ifnc + incbin + incdir + include + macro + org + public + rept + rs + rsreset + rsset + section + set + setfo + setso + so + text + ttl + xdef + xref + sdreg + cpu32 + far + fpu + machine + mc68000 + mc68010 + mc68020 + mc68030 + mc68040 + mc68060 + mcf5200 + mcf5206 + mcf5307 + mcf5407 + near + opt + equr + equrl + fequr + fequrl + freg + reg + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/asm6502.xml b/kate/part/syntax/data/asm6502.xml new file mode 100644 index 00000000..8d3e7261 --- /dev/null +++ b/kate/part/syntax/data/asm6502.xml @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/asn1.xml b/kate/part/syntax/data/asn1.xml new file mode 100644 index 00000000..0e691487 --- /dev/null +++ b/kate/part/syntax/data/asn1.xml @@ -0,0 +1,69 @@ + + + + + + + DEFINITIONS + BEGIN + END + EXPORTS + IMPORTS + FROM + APPLICATION + PRIVATE + UNIVERSAL + DEFAULT + OPTIONAL + FALSE + TRUE + + + BOOLEAN + INTEGER + OCTET STRING + NULL + REAL + ENUMERATED + SEQUENCE + SET + CHOICE + OF + VisibleString + StringStore + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/asp.xml b/kate/part/syntax/data/asp.xml new file mode 100644 index 00000000..3a912b97 --- /dev/null +++ b/kate/part/syntax/data/asp.xml @@ -0,0 +1,393 @@ + + + + + + select + case + end select + + if + then + else + elseif + end if + + + while + do + until + loop + wend + + for + each + to + in + next + + exit + continue + + + dim + redim + preserve + + const + erase + nothing + set + + new + me + + + function + sub + call + + class + private + public + with + + randomize + + open + close + movenext + execute + eof + + not + true + false + or + and + xor + + + response + write + redirect + end + + request + form + querystring + servervariables + cookies + + session + + server + createobject + + abs + array + asc + atn + cbool + cbyte + ccur + cdate + cdbl + chr + cint + clng + cos + csng + cstr + date + dateadd + + DateDiff + DatePart + DateSerial + DateValue + Date + Day + Exp + Filter + Fix + FormatCurrency + FormatDateTime + FormatNumber + FormatPercent + + GetObject + Hex + Hour + InputBox + InStr + InStrRev + Int + IsArray + IsDate + IsEmpty + IsNull + IsNumeric + IsObject + Join + LBound + LCase + + Left + Len + LoadPicture + Log + LTrim + Mid + Minute + Month + MonthName + MsgBox + Now + Oct + Replace + + RGB + Right + Rnd + Round + RTrim + + ScriptEngine + ScriptEngineBuildVersion + ScriptEngineMajorVersion + ScriptEngineMinorVersion + Second + Sgn + Sin + Space + Split + Sqr + StrComp + StrReverse + String + Tan + Time + Timer + TimeSerial + TimeValue + Trim + TypeName + UBound + UCase + VarType + Weekday + WeekdayName + Year + + Add + AddFolders + BuildPath + Clear + Close + Copy + CopyFile + CopyFolder + CreateFolder + CreateTextFile + Delete + DeleteFile + DeleteFolder + DriveExists + Exists + FileExists + FolderExists + GetAbsolutePathName + GetBaseName + GetDrive + GetDriveName + GetExtensionName + GetFile + GetFileName + GetFolder + GetParentFolderName + + GetSpecialFolder + GetTempName + Items + item + Keys + Move + MoveFile + MoveFolder + OpenAsTextStream + OpenTextFile + Raise + Read + ReadAll + ReadLine + Remove + RemoveAll + Skip + SkipLine + Write + WriteBlankLines + WriteLine + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/asterisk.xml b/kate/part/syntax/data/asterisk.xml new file mode 100644 index 00000000..e76a1e35 --- /dev/null +++ b/kate/part/syntax/data/asterisk.xml @@ -0,0 +1,81 @@ + + + + + + + + AGI + Answer + Dial + Hangup + GoTo + GoToIf + GoToIfTime + NoOp + PlayBack + Read + SayDigits + SayNumber + Set + SetCallerPres + System + Wait + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/awk.xml b/kate/part/syntax/data/awk.xml new file mode 100644 index 00000000..28a2b06d --- /dev/null +++ b/kate/part/syntax/data/awk.xml @@ -0,0 +1,209 @@ + + + + + + + if + else + while + do + for + in + continue + break + print + printf + getline + function + return + next + exit + + + ARGC + ARGV + CONVFMT + ENVIRON + FILENAME + FNR + FS + NF + NR + OFMT + OFS + ORS + RS + RSTART + RLENGTH + SUBSEP + + + gsub + gensub + index + length + match + split + sprintf + sub + substr + tolower + toupper + atan2 + cos + exp + int + log + rand + sin + sqrt + srand + close + fflush + system + + + BEGIN + END + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/bash.xml b/kate/part/syntax/data/bash.xml new file mode 100644 index 00000000..a07de399 --- /dev/null +++ b/kate/part/syntax/data/bash.xml @@ -0,0 +1,956 @@ + + + + + + + +]> + + + + + + + else + for + function + in + select + until + while + elif + then + set + + + + : + source + alias + bg + bind + break + builtin + cd + caller + command + compgen + complete + continue + dirs + disown + echo + enable + eval + exec + exit + fc + fg + getopts + hash + help + history + jobs + kill + let + logout + popd + printf + pushd + pwd + return + set + shift + shopt + suspend + test + time + times + trap + type + ulimit + umask + unalias + wait + + + + export + unset + declare + typeset + local + read + readonly + + + + + arch + awk + bash + bunzip2 + bzcat + bzcmp + bzdiff + bzegrep + bzfgrep + bzgrep + bzip2 + bzip2recover + bzless + bzmore + cat + chattr + chgrp + chmod + chown + chvt + cp + date + dd + deallocvt + df + dir + dircolors + dmesg + dnsdomainname + domainname + du + dumpkeys + echo + ed + egrep + false + fgconsole + fgrep + fuser + gawk + getkeycodes + gocr + grep + groff + groups + gunzip + gzexe + gzip + hostname + igawk + install + kbd_mode + kbdrate + killall + last + lastb + link + ln + loadkeys + loadunimap + login + ls + lsattr + lsmod + lsmod.old + lzcat + lzcmp + lzdiff + lzegrep + lzfgrep + lzgrep + lzless + lzcat + lzma + lzmainfo + lzmore + mapscrn + mesg + mkdir + mkfifo + mknod + mktemp + more + mount + mv + nano + netstat + nisdomainname + nroff + openvt + pgawk + pidof + ping + ps + pstree + pwd + rbash + readlink + red + resizecons + rm + rmdir + run-parts + sash + sed + setfont + setkeycodes + setleds + setmetamode + setserial + sh + showkey + shred + sleep + ssed + stat + stty + su + sync + tar + tempfile + touch + troff + true + umount + uname + unicode_start + unicode_stop + unlink + unlzma + unxz + utmpdump + uuidgen + vdir + wall + wc + xz + xzcat + ypdomainname + zcat + zcmp + zdiff + zegrep + zfgrep + zforce + zgrep + zless + zmore + znew + zsh + + + aclocal + aconnect + aplay + apm + apmsleep + apropos + ar + arecord + as + as86 + autoconf + autoheader + automake + awk + basename + bc + bison + c++ + cal + cat + cc + cdda2wav + cdparanoia + cdrdao + cd-read + cdrecord + chfn + chgrp + chmod + chown + chroot + chsh + clear + cmp + co + col + comm + cp + cpio + cpp + cut + dc + dd + df + diff + diff3 + dir + dircolors + directomatic + dirname + du + env + expr + fbset + file + find + flex + flex++ + fmt + free + ftp + funzip + fuser + g++ + gawk + gc + gcc + gdb + getent + getopt + gettext + gettextize + gimp + gimp-remote + gimptool + gmake + gs + head + hexdump + id + install + join + kill + killall + ld + ld86 + ldd + less + lex + ln + locate + lockfile + logname + lp + lpr + ls + lynx + m4 + make + man + mkdir + mknod + msgfmt + mv + namei + nasm + nawk + nice + nl + nm + nm86 + nmap + nohup + nop + od + passwd + patch + pcregrep + pcretest + perl + perror + pidof + pr + printf + procmail + prune + ps2ascii + ps2epsi + ps2frag + ps2pdf + ps2ps + psbook + psmerge + psnup + psresize + psselect + pstops + rcs + rev + rm + scp + sed + seq + setterm + shred + size + size86 + skill + slogin + snice + sort + sox + split + ssh + ssh-add + ssh-agent + ssh-keygen + ssh-keyscan + stat + strings + strip + sudo + suidperl + sum + tac + tail + tee + test + tr + uniq + unlink + unzip + updatedb + updmap + uptime + users + vmstat + w + wc + wget + whatis + whereis + which + who + whoami + write + xargs + yacc + yes + zip + zsoelim + + + dcop + kdialog + kfile + xhost + xmodmap + xset + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/bibtex.xml b/kate/part/syntax/data/bibtex.xml new file mode 100644 index 00000000..e167c270 --- /dev/null +++ b/kate/part/syntax/data/bibtex.xml @@ -0,0 +1,108 @@ + + + + + + ]> + + + + @article + @book + @booklet + @conference + @collection + @electronic + @inbook + @incollection + @inproceedings + @manual + @mastersthesis + @misc + @online + @patent + @periodical + @proceedings + @report + @phdthesis + @set + @thesis + @techreport + @unpublished + @www + @person + @company + @place + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/bmethod.xml b/kate/part/syntax/data/bmethod.xml new file mode 100644 index 00000000..5a51dad2 --- /dev/null +++ b/kate/part/syntax/data/bmethod.xml @@ -0,0 +1,96 @@ + + + + + + + MACHINE + SETS + CONSTANTS + PROPERTIES + PROMOTES + INCLUDES + USES + SEES + VARIABLES + INVARIANT + INITIALISATION + REFINEMENT + REFINES + CONSTRAINTS + IMPLEMENTATION + IMPORTS + + + OPERATIONS + + + END + + + THEN + WHEN + ELSE + OR + WHERE + INVARIANT + DO + VARIANT + IN + ELSIF + + + PRE + IF + ANY + LET + CHOICE + CASE + SELECT + VAR + WHILE + BEGIN + + + END + + + NAT + NAT1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/boo.xml b/kate/part/syntax/data/boo.xml new file mode 100644 index 00000000..3043646c --- /dev/null +++ b/kate/part/syntax/data/boo.xml @@ -0,0 +1,314 @@ + + + + + + + + import + from + as + namespace + + + + and + assert + in + is + not + or + + + + bool + byte + sbyte + double + decimal + single + short + ushort + int + char + uint + long + ulong + object + duck + string + regex + date + timespan + + + + abstract + virtual + override + static + final + transient + macro + + protected + private + public + internal + partial + + class + struct + interface + enum + callable + of + + def + constructor + destructor + + do + get + set + event + + return + yield + + + + true + false + + + + null + self + super + + + + and + break + cast + continue + elif + else + except + ensure + for + given + goto + if + in + is + isa + not + or + otherwise + pass + raise + try + unless + when + while + ref + + + + assert + __eval__ + __switch__ + enumerate + filter + len + typeof + map + max + min + property + using + getter + required + lock + range + zip + checked + unchecked + rawArrayIndexing + normalArrayIndexing + print + array + matrix + yieldAll + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/c.xml b/kate/part/syntax/data/c.xml new file mode 100644 index 00000000..6e58a948 --- /dev/null +++ b/kate/part/syntax/data/c.xml @@ -0,0 +1,213 @@ + + + + + + + break + case + continue + default + do + else + enum + extern + for + goto + if + inline + return + sizeof + struct + switch + typedef + union + while + + + auto + char + const + double + float + int + long + register + restrict + short + signed + static + unsigned + void + volatile + int8_t + int16_t + int32_t + int64_t + uint8_t + uint16_t + uint32_t + uint64_t + wchar_t + _Imaginary + _Complex + _Bool + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/ccss.xml b/kate/part/syntax/data/ccss.xml new file mode 100644 index 00000000..acb1054f --- /dev/null +++ b/kate/part/syntax/data/ccss.xml @@ -0,0 +1,658 @@ + + + + +]> + + + + + + + azimuth + background + background-attachment + background-color + background-image + background-position + background-repeat + border + border-bottom + border-bottom-color + border-bottom-style + border-bottom-width + border-collapse + border-color + border-left + border-left-color + border-left-style + border-left-width + border-right + border-right-color + border-right-style + border-right-width + border-spacing + border-style + border-top + border-top-color + border-top-style + border-top-width + border-width + bottom + caption-side + clear + clip + color + content + counter-increment + counter-reset + cue + cue-after + cue-before + cursor + direction + display + elevation + empty-cells + float + font + font-family + font-size + font-size-adjust + font-stretch + font-style + font-variant + font-weight + height + left + letter-spacing + line-height + list-style + list-style-image + list-style-keyword + list-style-position + list-style-type + margin + margin-bottom + margin-left + margin-right + margin-top + marker-offset + max-height + max-width + min-height + min-width + orphans + outline + outline-color + outline-style + outline-width + overflow + padding + padding-bottom + padding-left + padding-right + padding-top + page + page-break-after + page-break-before + page-break-inside + pause + pause-after + pause-before + pitch + pitch-range + play-during + position + quotes + richness + right + size + speak + speak-header + speak-numeral + speak-punctuation + speech-rate + stress + table-layout + text-align + text-decoration + text-decoration-color + text-indent + text-shadow + text-transform + top + unicode-bidi + vertical-align + visibility + voice-family + volume + white-space + widows + width + word-spacing + z-index + + + border-bottom-image + border-bottom-left-image + border-bottom-left-radius + border-bottom-right-image + border-bottom-right-radius + border-corner-image + border-image + border-left-image + border-radius + border-right-image + border-top-image + border-top-left-image + border-top-left-radius + border-top-right-image + border-top-right-radius + box-align + box-shadow + box-sizing + box-orient + box-pack + opacity + outline-offset + overflow-x + overflow-y + text-overflow + text-shadow + + + -moz-border-bottom-colors + -moz-border-left-colors + -moz-border-radius + -moz-border-right-colors + -moz-border-top-colors + -moz-box-flex + + + -o-background-size + -o-text-overflow + + + -khtml-background-size + konq_bgpos_x + konq_bgpos_y + + + -webkit-background-size + -webkit-border-radius + + + filter + -ms-filter + + + font-family + font-size + font-stretch + font-style + font-variant + font-weight + unicode-range + units-per-em + src + panose-1 + stemv + stemh + slope + cap-height + x-height + ascent + descent + widths + bbox + definition-src + baseline + centerline + mathline + topline + + + + inherit + none + hidden + dotted + dashed + solid + double + groove + ridge + inset + outset + xx-small + x-small + small + medium + large + x-large + xx-large + smaller + larger + italic + oblique + small-caps + normal + bold + bolder + lighter + light + 100 + 200 + 300 + 400 + 500 + 600 + 700 + 800 + 900 + transparent + repeat + repeat-x + repeat-y + no-repeat + baseline + sub + super + top + text-top + middle + bottom + text-bottom + left + right + center + justify + konq-center + disc + circle + square + box + decimal + decimal-leading-zero + lower-roman + upper-roman + lower-greek + lower-alpha + lower-latin + upper-alpha + upper-latin + hebrew + armenian + georgian + cjk-ideographic + hiragana + katakana + hiragana-iroha + katakana-iroha + inline + inline-block + block + list-item + run-in + compact + marker + table + inline-table + table-row-group + table-header-group + table-footer-group + table-row + table-column-group + table-column + table-cell + table-caption + auto + crosshair + default + pointer + move + e-resize + ne-resize + nw-resize + n-resize + se-resize + sw-resize + s-resize + w-resize + text + wait + help + above + absolute + always + avoid + below + bidi-override + blink + both + capitalize + caption + clip + close-quote + collapse + condensed + crop + cross + ellipsis + ellipsis-word + embed + expanded + extra-condensed + extra-expanded + fixed + hand + hide + higher + icon + inside + invert + landscape + level + line-through + loud + lower + lowercase + ltr + menu + message-box + mix + narrower + no-close-quote + no-open-quote + nowrap + open-quote + outside + overline + portrait + pre + pre-line + pre-wrap + relative + rtl + scroll + semi-condensed + semi-expanded + separate + show + small-caption + static + static-position + status-bar + thick + thin + ultra-condensed + ultra-expanded + underline + uppercase + visible + wider + break + serif + sans-serif + cursive + fantasy + monospace + border-box + content-box + horizontal + + + -moz-box + + + linear + radial + + + + + + aqua + black + blue + fuchsia + gray + green + lime + maroon + navy + olive + purple + red + silver + teal + white + yellow + ActiveBorder + ActiveCaption + AppWorkspace + Background + ButtonFace + ButtonHighlight + ButtonShadow + ButtonText + CaptionText + GrayText + Highlight + HighlightText + InactiveBorder + InactiveCaption + InactiveCaptionText + InfoBackground + InfoText + Menu + MenuText + Scrollbar + ThreeDDarkShadow + ThreeDFace + ThreeDHighlight + ThreeDLightShadow + ThreeDShadow + Window + WindowFrame + WindowText + + + + url + attr + rect + rgb + rgba + hsl + hsla + counter + counters + + + local + format + + + -webkit-gradient + color-stop + + + -moz-linear-gradient + -moz-radial-gradient + + + expression + progid:DXImageTransform.Microsoft.gradient + + + + + all + aural + braille + embossed + handheld + print + projection + screen + tty + tv + + + + hover + link + visited + active + focus + first-child + last-child + only-child + first-of-type + last-of-type + only-of-type + first-letter + first-line + before + after + selection + root + empty + target + enabled + disabled + checked + indeterminate + nth-child + nth-last-child + nth-of-type + nth-last-of-type + not + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/cg.xml b/kate/part/syntax/data/cg.xml new file mode 100644 index 00000000..48ed5b20 --- /dev/null +++ b/kate/part/syntax/data/cg.xml @@ -0,0 +1,288 @@ + + + + + + + + + POSITION + COLOR + COLOR0 + COLOR1 + COLOR2 + COLOR3 + TEXCOORD0 + TEXCOORD1 + TEXCOORD2 + TEXCOORD3 + TEXCOORD4 + TEXCOORD5 + TEXCOORD6 + TEXCOORD7 + TEXCOORD8 + TEXCOORD9 + TEXCOORD10 + TEXCOORD11 + TEXCOORD12 + TEXCOORD13 + TEXCOORD14 + TEXCOORD15 + TEXUNIT0 + TEXUNIT1 + TEXUNIT2 + TEXUNIT3 + TEXUNIT4 + TEXUNIT5 + TEXUNIT6 + TEXUNIT7 + TEXUNIT8 + TEXUNIT9 + TEXUNIT10 + TEXUNIT11 + TEXUNIT12 + TEXUNIT13 + TEXUNIT14 + TEXUNIT15 + WPOS + DEPTH + BLENDWEIGHT + NORMAL + TESSFACTOR + FOGCOORD + PSIZE + BLENDINDICES + TANGENT + BINORMAL + FOG + BCOL0 + BCOL1 + FACE + + + do + while + if + else + for + struct + return + static + typedef + discard + true + false + + + bool + int + fixed + half + float + + void + sampler + sampler1D + sampler2D + sampler3D + samplerCUBE + samplerRECT + + packed + const + uniform + in + out + inout + + + FIXME + TODO + BUG + + + abs + acos + all + any + asin + atan + atan2 + ceil + clamp + cos + cosh + cross + degrees + determinant + dot + exp + exp2 + floor + fmod + frac + frexp + isfinite + isinf + isnan + ldexp + lerp + lit + log + log2 + log10 + max + min + modf + mul + noise + pow + radians + round + rsqrt + saturate + sign + sin + sincos + sinh + smoothstep + step + sqrt + tan + tanh + transpose + distance + faceforward + length + normalize + reflect + refract + tex1D + tex1Dproj + tex2D + tex2Dproj + texRECT + texRECTproj + tex3D + tex3Dproj + texCUBE + texCUBEproj + ddx + ddy + debug + pack_2half + unpack_2half + pack_2ushort + unpack_2ushort + pack_4byte + unpack_4byte + pack_4ubyte + unpack_4ubyte + + + fragout + fragout_float + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/cgis.xml b/kate/part/syntax/data/cgis.xml new file mode 100644 index 00000000..dda121fe --- /dev/null +++ b/kate/part/syntax/data/cgis.xml @@ -0,0 +1,146 @@ + + + + + + 1D + 2D + break + continue + do + else + extern + for + forall + foreach + function + if + in + inout + internal + out + reduction + return + struct + typedef + while + + + bool + bool2 + bool3 + bool4 + int + int2 + int3 + int4 + half + half2 + half3 + half4 + float + float2 + float3 + float4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/changelog.xml b/kate/part/syntax/data/changelog.xml new file mode 100644 index 00000000..aec33921 --- /dev/null +++ b/kate/part/syntax/data/changelog.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/checkdtd b/kate/part/syntax/data/checkdtd new file mode 100755 index 00000000..707e2290 --- /dev/null +++ b/kate/part/syntax/data/checkdtd @@ -0,0 +1,2 @@ +#!/bin/sh +xmllint --noout --dtdvalid language.dtd $@ diff --git a/kate/part/syntax/data/chicken.xml b/kate/part/syntax/data/chicken.xml new file mode 100644 index 00000000..0100c45f --- /dev/null +++ b/kate/part/syntax/data/chicken.xml @@ -0,0 +1,437 @@ + + + + + + + <= + < + = + => + >= + > + - + / + *,* + *) + + + + + #\nul + #\soh + #\stx + #\etx + #\eot + #\enq + #\ack + #\bel + #\bs + #\ht + #\nl + #\vt + #\np + #\cr + #\so + #\si + #\dle + #\dc1 + #\dc2 + #\dc3 + #\dc4 + #\nak + #\syn + #\etb + #\can + #\em + #\sub + #\esc + #\fs + #\gs + #\rs + #\us + #\space + #\sp + #\newline + #\nl + #\tab + #\ht + #\backspace + #\bs + #\return + #\cr + #\page + #\np + #\null + #\nul + + + define + define* + define-accessor + define-class + defined? + define-generic + define-macro + define-method + define-module + define-private + define-public + define*-public + define-reader-ctor + define-syntax + define-syntax-macro + defmacro + defmacro* + defmacro*-public + + + abs + acos + and + angle + append + applymap + asin + assoc + assq + assv + atan + begin + boolean? + break + caaaar + caaadr + caaar + caadar + caaddr + caadr + caar + cadaar + cadadr + cadar + caddar + cadddr + caddr + cadr + call/cc + call-with-current-continuation + call-with-input-file + call-with-output-file + call-with-values + car + case + catch + cdaaar + cdaadr + cdaar + cdadar + cdaddr + cdadr + cdar + cddaar + cddadr + cddar + cdddar + cddddr + cdddr + cddr + cdr + ceiling + char-alphabetic? + char-ci>=? + char-ci>? + char-ci=? + char-ci<=? + char-downcase + char->integer + char>=? + char>? + char=? + char? + char-lower-case? + char<?c + char<=? + char-numeric? + char-ready? + char-upcase + char-upper-case? + char-whitespace? + close-input-port + close-output-port + complex? + cond + cons + continue + cos + current-input-port + current-output-port + denominator + display + do + dynamic-wind + else + eof-object? + eq? + equal? + eqv? + eval + even? + exact->inexact + exact? + exp + expt + floor + force + for-each + gcd + har-ci<? + if + imag-part + inexact->exact + inexact? + input-port? + integer->char + integer? + interaction-environment + lambda + lcm + length + let + let* + letrec + letrec-syntax + let-syntax + list->string + list + list? + list-ref + list-tail + load + log + magnitude + make-polar + make-rectangular + make-string + make-vector + max + member + memq + memv + min + modulo + negative? + newline + not + null-environment + null? + number? + number->string + numerator + odd? + open-input-file + open-output-file + or + output-port? + pair? + peek-char + port? + positive? + procedure? + quotient + rational? + rationalize + read-char + read + real? + real-part + remainder + reverse + round + scheme-report-environment + set-car! + set-cdr! + sin + sqrt + string-append + string-ci>=? + string-ci>? + string-ci=? + string-ci<=? + string-ci<? + string-copy + string-fill! + string>=? + string>? + string->list + string->number + string->symbol + string=? + string + string? + string-length + string<=? + string<? + string-ref + string-set! + substring + symbol->string + symbol? + syntax-rules + tan + transcript-off + transcript-on + truncate + values + vector-fill! + vector->listlist->vector + vector + vector? + vector-length + vector-ref + vector-set! + while + with-input-from-file + with-output-to-file + write-char + write + zero? + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/kate/part/syntax/data/cisco.xml b/kate/part/syntax/data/cisco.xml new file mode 100644 index 00000000..47c78880 --- /dev/null +++ b/kate/part/syntax/data/cisco.xml @@ -0,0 +1,312 @@ + + + + + + aaa + access-list + address + alias + arp + async-bootp + banner + boot + bridge + buffers + busy-message + call-history-mib + cdp + chat-script + class-map + clock + cns + config-register + controller + crypto + default + default-value + dialer + dialer-list + dnsix-dmdp + dnsix-nat + downward-compatible-config + enable + end + exception + exit + file + frame-relay + help + hostname + interface + ip + isdn + isdn-mib + kerberos + key + line + logging + login-string + map-class + map-list + memory-size + menu + modemcap + multilink + netbios + no + ntp + partition + policy-map + priority-list + privilege + process-max-time + prompt + queue-list + resume-string + rlogin + rmon + route-map + router + rtr + scheduler + service + snmp-server + sntp + stackmaker + state-machine + subscriber-policy + tacacs-server + template + terminal-queue + tftp-server + time-range + username + virtual-profile + virtual-template + vpdn + vpdn-group + x25 + x29 + + + accounting + accounting-list + accounting-threshold + accounting-transits + address-pool + as-path + audit + auth-proxy + authentication + authorization + bgp-community + bootp + cef + classless + community-list + default-gateway + default-network + dhcp + dhcp-server + domain-list + domain-lookup + domain-name + dvmrp + exec-callback + extcommunity-list + finger + flow-aggregation + flow-cache + flow-export + forward-protocol + ftp + gratuitous-arps + host + host-routing + hp-host + http + icmp + inspect + local + mrm + mroute + msdp + multicast + multicast-routing + name-server + nat + new-model + ospf + password + password-encryption + pgm + pim + port-map + prefix-list + radius + rcmd + reflexive-list + route + routing + rsvp + rtcp + sap + sdr + security + source-route + subnet-zero + tacacs + tcp + tcp-small-servers + telnet + tftp + timestamps + udp-small-servers + vrf + wccp + + + accounting + accounting-list + accounting-threshold + accounting-transits + address-pool + as-path + audit + auth-proxy + authentication + authorization + bgp-community + bootp + cef + classless + community-list + default-gateway + default-network + dhcp + dhcp-server + domain-list + domain-lookup + domain-name + dvmrp + exec-callback + extcommunity-list + finger + flow-aggregation + flow-cache + flow-export + forward-protocol + ftp + gratuitous-arps + host + host-routing + hp-host + http + icmp + inspect + local + mrm + mroute + msdp + multicast + multicast-routing + name-server + nat + new-model + ospf + password + password-encryption + pgm + pim + port-map + prefix-list + radius + rcmd + reflexive-list + route + routing + rsvp + rtcp + sap + sdr + security + source-route + subnet-zero + tacacs + tcp + tcp-small-servers + telnet + tftp + timestamps + udp-small-servers + vrf + wccp + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/clipper.xml b/kate/part/syntax/data/clipper.xml new file mode 100644 index 00000000..8b99fab5 --- /dev/null +++ b/kate/part/syntax/data/clipper.xml @@ -0,0 +1,500 @@ + + + + + + + + .and. + announce + begin + case + command + define + do + elseif + else + endcase + enddo + endif + error + exit + field + .f. + for + function + ifdef + if + include + init + inndef + local + memvar + next + nil + .not. + .or. + other + parameters + private + procedure + public + request + return + sequence + static + stdout + .t. + traslate + undef + while + xcommand + xtranslate + + + + + accept + all + alternate + append + ascending + average + bell + blank + box + century + clear + close + coclor + color + commit + confirm + console + continue + copy + count + create + cursor + date + decimals + default + deleted + delete + delimiters + descending + device + display + do + eject + epoch + erase + escape + eval + every + exact + extended + file + filter + fixed + form + from + get + gets + go + goto + index + input + intensity + ? + ?? + @ + join + keyboard + key + label + list + locate + margin + memory + menu + message + new + on + order + pack + path + pict + printer + prompt + quit + range + read + recall + record + reindex + relation + release + rename + replace + report + rest + restore + run + save + say + scoreboard + seek + select + set + skip + softseek + sort + structure + sum + tag + to + total + typeahead + type + unique + unlock + update + use + valid + wait + when + with + wrap + zap + + + + + aadd + abs + achoice + aclone + acopy + adel + aeval + afill + ains + alert + alias + alltrim + altd + array + ascan + asize + asort + atail + at + bin2i + bin2l + bin2w + bof + break + browse + cdowchr + chr + cmonth + col + colorselect + ctod + curdir + date + day + dbappend + dbclearall + dbclearfilter + dbclearindex + dbclearrelation + dbcloseall + dbclosearea + dbcommitall + dbcommit + dbcreateindex + dbcreate + dbdelete + dbedit + dbeval + dbfilter + dbf + dbgobottom + dbgoto + dbgotop + dbrecall + dbreindex + dbrelation + dbrlock + dbrlocklist + dbrselect + dbrunlock + dbseek + dbselectarea + dbsetfilter + dbsetindex + dbsetorder + dbsetrelation + dbskip + dbstruct + dbunlockall + dbunlock + dbusearea + deleted + descend + devout + devpos + directory + dispbegin + dispbox + dispcount + dispend + dispout + dispspace + doserror + dow + dtoc + dtos + empty + eof + errorblock + errorinhandler + errorlevel + eval + exp + fclose + fcount + fcreate + ferase + ferror + fieldblock + fieldget + field + fieldname + fieldpos + fieldput + fieldwblock + file + flock + fopen + found + fread + freadstr + frename + fseek + fwrite + getactive + getenv + hardcr + header + i2bin + iif + indexext + indexkey + indexord + inkey + int + isalpha + iscolor + isdigit + islower + isprinter + isupper + l2bin + lastkey + lastrec + left + len + lock + log + lower + ltrim + lupdate + maxcol + max + maxrow + memoedit + memoline + memoread + memory + memotran + memowrit + memvarblock + min + mlcount + mlctopos + mlpos + mod + month + mpostolc + neterr + netname + nextkey + nosnow + ordbagext + ordbagname + ordcreate + orddestroy + ordfor + ordkey + ordlistadd + ordlistclear + ordlistrebuild + ordname + ordnumber + ordsetfocus + os + outerr + outstd + padc + padl + padr + pcol + pcount + proclineprocname + prow + qout + qqout + rat + rddlist + rddname + rddsetdefault + readexit + readinsert + readmodal + readvar + reccount + recno + recsize + replicate + restscreen + right + rlock + round + row + rtrim + savesreen + scroll + seconds + select + setblink + setcancel + setcolor + setcursor + setkey + setmode + setpos + setprc + soundex + space + sqrt + str + strtran + stuff + substr + time + tone + transform + trim + updated + upper + used + val + valtype + version + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/clojure.xml b/kate/part/syntax/data/clojure.xml new file mode 100644 index 00000000..13dafa47 --- /dev/null +++ b/kate/part/syntax/data/clojure.xml @@ -0,0 +1,797 @@ + + + + + + + def + def- + defalias + defhinted + definline + defmacro + defmacro- + defmethod + defmulti + defn + defn- + defnk + defn-memo + defonce + defonce- + defprotocol + defrecord + defstruct + defstruct- + deftest + deftest- + deftype + defunbound + defunbound- + defvar + defvar- + + + < + <= + = + == + > + >= + - + -> + ->> + / + . + .. + * + + + accessor + aclone + add-classpath + add-watcher + agent + agent-errors + aget + alength + alias + all-ns + alter + alter-meta! + alter-var-root + amap + ancestors + and + append-child + apply + apply-template + are + areduce + array-map + aset + aset-boolean + aset-byte + aset-char + aset-double + aset-float + aset-int + aset-long + aset-short + assert + assert-any + assert-expr + assert-predicate + assoc + assoc! + associative? + assoc-in + atom + atom? + attrs + await + await1 + await-for + bases + bean + bigdec + bigint + binding + bit-and + bit-and-not + bit-clear + bit-flip + bit-not + bit-or + bit-set + bit-shift-left + bit-shift-right + bit-test + bit-xor + boolean + boolean-array + booleans + bound-fn + bound-fn* + branch? + butlast + byte + byte-array + bytes + case + cast + catch + char + char? + char-array + char-escape-string + char-name-string + chars + children + chunk + chunk-append + chunk-buffer + chunk-cons + chunked-seq? + chunk-first + chunk-next + chunk-rest + class + class? + clear-agent-errors + clojure-version + coll? + collection-tag + comment + commute + comp + comparator + compare + compare-and-set! + compile + complement + compose-fixtures + concat + cond + condp + conj + conj! + cons + constantly + construct-proxy + contains? + content + content-handler + count + counted? + create-ns + create-struct + cycle + dec + decimal? + declare + delay + delay? + deliver + deref + derive + descendants + destructure + difference + disj + disj! + dissoc + dissoc! + distinct + distinct? + do + doall + doc + dorun + doseq + dosync + do-template + dotimes + doto + double + double-array + doubles + down + drop + drop-last + drop-while + e + edit + element + emit + emit-element + empty + empty? + end? + ensure + enumeration-seq + eval + even? + every? + extend + extenders + extend-protocol + extends? + extend-type + false? + ffirst + file-position + file-seq + filter + finally + find + find-doc + find-ns + find-var + first + float + float? + float-array + floats + flush + fn + fn? + fnext + for + force + format + function? + future + future? + future-call + future-cancel + future-cancelled? + future-done? + gen-and-load-class + gen-and-save-class + gen-class + gen-interface + gensym + get + get-child + get-child-count + get-in + get-method + get-possibly-unbound-var + get-proxy-class + get-thread-bindings + get-validator + handle + handler-case + hash + hash-map + hash-set + identical? + identity + if + if-let + ifn? + if-not + import + inc + inc-report-counter + index + init-proxy + in-ns + insert-child + insert-left + insert-right + inspect + inspect-table + inspect-tree + instance? + int + int-array + integer? + interleave + intern + interpose + intersection + into + into-array + ints + io! + is + isa? + is-leaf + iterate + iterator-seq + join + join-fixtures + juxt + key + keys + keyword + keyword? + keywordize-keys + last + lazy-cat + lazy-seq + left + leftmost + lefts + let + letfn + line-seq + list + list* + list? + list-model + list-provider + load + loaded-libs + load-file + load-reader + load-script + load-string + locking + long + long-array + longs + loop + macroexpand + macroexpand-1 + macroexpand-all + main + make-array + make-hierarchy + make-node + map + map? + mapcat + map-invert + max + max-key + memfn + memoize + merge + merge-with + meta + methods + method-sig + min + min-key + mod + name + namespace + neg? + newline + next + nfirst + nil? + nnext + node + not + not= + not-any? + not-empty + not-every? + ns + ns-aliases + ns-imports + ns-interns + ns-map + ns-name + ns-publics + ns-refers + ns-resolve + ns-unalias + ns-unmap + nth + nthnext + num + number? + odd? + or + parents + partial + partition + path + pcalls + peek + persistent! + pmap + pop + pop! + pop-thread-bindings + pos? + postwalk + postwalk-demo + postwalk-replace + pr + prefer-method + prefers + prev + prewalk + prewalk-demo + prewalk-replace + primitives-classnames + print + print-cause-trace + print-ctor + print-doc + print-dup + printf + println + println-str + print-method + print-namespace-doc + print-simple + print-special-doc + print-stack-trace + print-str + print-throwable + print-trace-element + prn + prn-str + project + promise + proxy + proxy-call-with-super + proxy-mappings + proxy-name + proxy-super + pr-str + push-thread-bindings + pvalues + quot + rand + rand-int + range + ratio? + rational? + rationalize + read + read-line + read-string + recur + reduce + ref + refer + refer-clojure + ref-history-count + re-find + ref-max-history + ref-min-history + ref-set + re-groups + reify + release-pending-sends + rem + re-matcher + re-matches + remove + remove-method + remove-ns + remove-watcher + rename + rename-keys + re-pattern + repeat + repeatedly + repl + replace + repl-caught + repl-exception + replicate + repl-prompt + repl-read + report + require + re-seq + reset! + reset-meta! + resolve + rest + resultset-seq + reverse + reversible? + right + rightmost + rights + root + rseq + rsubseq + run-all-tests + run-tests + satisfies? + second + select + select-keys + send + send-off + seq + seq? + seque + sequence + sequential? + seq-zip + set + set? + set-test + set-validator! + short + short-array + shorts + shutdown-agents + skip-if-eol + skip-whitespace + slurp + some + sort + sort-by + sorted? + sorted-map + sorted-map-by + sorted-set + sorted-set-by + special-form-anchor + special-symbol? + split-at + split-with + str + stream? + string? + stringify-keys + struct + struct-map + subs + subseq + subvec + successful? + supers + swap! + symbol + symbol? + sync + syntax-symbol-anchor + take + take-last + take-nth + take-while + test + test-all-vars + testing + testing-contexts-str + testing-vars-str + test-ns + test-var + the-ns + throw + time + to-array + to-array-2d + trampoline + transient + tree-seq + true? + try + try-expr + type + unchecked-add + unchecked-dec + unchecked-divide + unchecked-inc + unchecked-multiply + unchecked-negate + unchecked-remainder + unchecked-subtract + underive + unimport + union + unquote + unquote-splicing + up + update-in + update-proxy + use + use-fixtures + val + vals + var? + var-get + var-set + vary-meta + vec + vector + vector? + walk + when + when-first + when-let + when-not + while + with-bindings + with-bindings* + with-in-str + with-loading-context + with-local-vars + with-meta + with-open + with-out-str + with-precision + with-test + with-test-out + xml-seq + zero? + + + *1 + *2 + *3 + *agent* + *allow-unresolved-vars* + *assert* + *clojure-version* + *command-line-args* + *compile-files* + *compile-path* + *current* + *e + *err* + *file* + *flush-on-newline* + *in* + *initial-report-counters* + *load-tests* + *macro-meta* + *math-context* + *ns* + *out* + *print-dup* + *print-length* + *print-level* + *print-meta* + *print-readably* + *read-eval* + *report-counters* + *sb* + *source-path* + *stack* + *stack-trace-depth* + *state* + *testing-contexts* + *testing-vars* + *test-out* + *use-context-classloader* + *warn-on-reflection* + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/cmake-gen.sh b/kate/part/syntax/data/cmake-gen.sh new file mode 100755 index 00000000..bbdbaeef --- /dev/null +++ b/kate/part/syntax/data/cmake-gen.sh @@ -0,0 +1,108 @@ +#!/bin/bash +# Copyright 2008, 2009 Matthew Woehlke (mw_triad@users.sourceforge.net) +# Copyright 2013, Alex Turbov (i.zaufi@gmail.com) +# +# ATTENTION Since CMake 3.x this generator is broken! So last time +# (making version 1.31 of cmake.xml syntax) I had to use `kdiff3` +# to manually merge changes! +# +# TODO CMake 2 and 3 has some difference in syntax... So, this +# generator do not feasible anymore if we are trying to keep a single +# syntax files for both CMake generations. It must be fixed! (somehow) + +export LC_ALL=C + +# need cmake +CMAKE="$(type -P cmake)" +[ -x "$CMAKE" ] || exit 1 +echo "found cmake... $CMAKE" + +t=.tmp_cmake$$ + +# Get cmake version +CMAKE_VERSION="$("$CMAKE" --help | sed -n '1p')" + +count() { + wc -l $t.$1 | awk '{print $1}' +} + +# Extract before/after command list +sed -n -e '/ $t.1 +sed -e '/<\/list\s*>/ba' -e 'd' -e ':a' -e 'n;ba' cmake.xml > $t._2 +sed -n -e '/ $t.2 +sed "1,$(wc -l < $t.2)d" $t._2 | sed -e '/<\/list\s*>/ba' -e 'd' -e ':a' -e 'n;ba' > $t._3 +sed -n -e '/ $t.3 +sed "1,$(wc -l < $t.3)d" $t._3 | sed -e '/<\/list\s*>/ba' -e 'd' -e ':a' -e 'n;ba' > $t._4 +sed -n -e '/ $t.4 +sed "1,$(wc -l < $t.4)d" $t._4 | sed -e '/<\/list\s*>/ba' -e 'd' -e ':a' -e 'n;ba' > $t._5 +sed -n -e '/ $t.5 +sed "1,$(wc -l < $t.5)d" $t._5 | sed -e '/<\/context\s*>/ba' -e 'd' -e ':a' -e 'n;ba' > $t.6 + +$CMAKE --help-command-list | sed '1d' | sort > $t.commands +echo "$(count commands) commands" + +extract_args() { + sed -e '/^\s\+'"$1"'(/ba' \ + -e 'd' \ + -e ':a' \ + -e '/)/{s/^.*(\(.*\)).*$/\1/p;d}' \ + -e 'N;s/\n/ /;ba' | \ + sed -e 's/[][]//g' -e 's/|\| \+/\n/g' -e 's/<[[:upper:][:digit:]_]\+>//g' -e 's/[<>]//g' | \ + sed -n '/^[[:upper:][:digit:]_]\+$/p' | \ + # NOTE Remove some false-positives: + # 0) one-letter-commands -- found from usage examples + # 1) CMP from cmake_policy will be handled individually + # 2) fix incorrect parsing of separate_arguments(): append UNIX_COMMAND + # 3) 'VAR' actually is not a part of any command! + sed -e '/^[A-Z]$/d' -e '/^CMP$/d' -e 's/\(WINDOWS_COMMAND\)/\1\nUNIX_COMMAND/' -e '/^VAR[0-9]*$/d' \ + >> $t.args +} + +while read COMMAND ; do + echo "# Getting args of '$COMMAND' command" >>$t.args + "$CMAKE" --help-command $COMMAND | extract_args $COMMAND +done < $t.commands +sed '/^#/d' $t.args | sort -u > $t.argsu +echo "$(count args) arguments, $(count argsu) unique" +$CMAKE --help-property-list | sed -e '1d' -e '/[<>]/d' | sort -u > $t.props +echo "$(count props) properties" + +# Get builtin CMake variables list +$CMAKE --help-variable-list | sed -e '1d' | grep -v 'Project name' > $t.all_vars +grep '^[A-Za-z_0-9]\+\s*$' $t.all_vars | sort -u > $t.vars +grep -v '^[A-Za-z_0-9]\+\s*$' $t.all_vars \ + | sed 's,,[A-Za-z_][A-Za-z_0-9]*,' \ + | sed 's,,[A-Za-z_][A-Za-z_0-9]*,' \ + | sed 's,,[A-Za-z_][A-Za-z_0-9]*,' \ + | sed 's,,[A-Za-z_][A-Za-z_0-9]*,' \ + | sed 's,CMP,CMP[0-9]+,' \ + | sed 's,\[CMAKE_BUILD_TYPE\],[A-Za-z_][A-Za-z_0-9]*,' \ + | sort -u \ + > $t.varsrr +echo "$(count all_vars) builtin variables" + + +# Generate new .xml +{ + sed '//' $t.1 + echo " " + sed 's!.*! & !' $t.commands + cat $t.2 + echo " " + sed 's!.*! & !' $t.argsu + cat $t.3 + echo " " + sed 's!.*! & !' $t.props + cat $t.4 + echo " " + sed 's!.*! & !' $t.vars + cat $t.5 + echo " " + sed 's|.*| |' $t.varsrr + cat $t.6 +} > cmake.xml + +rm -f $t.* +echo "Remember to update the version!" + +# kate: tab-width 4; indent-mode normal; indent-width 4; diff --git a/kate/part/syntax/data/cmake.xml b/kate/part/syntax/data/cmake.xml new file mode 100644 index 00000000..636c4361 --- /dev/null +++ b/kate/part/syntax/data/cmake.xml @@ -0,0 +1,1902 @@ + + + + + + + + + + + add_compile_options + add_custom_command + add_custom_target + add_definitions + add_dependencies + add_executable + add_library + add_link_options + add_subdirectory + add_test + aux_source_directory + break + build_command + build_name + cmake_host_system_information + cmake_language + cmake_minimum_required + cmake_parse_arguments + cmake_path + cmake_policy + configure_file + continue + create_test_sourcelist + ctest_build + ctest_configure + ctest_coverage + ctest_empty_binary_directory + ctest_memcheck + ctest_read_custom_files + ctest_run_script + ctest_sleep + ctest_start + ctest_submit + ctest_test + ctest_update + ctest_upload + define_property + else + elseif + enable_language + enable_testing + endforeach + endfunction + endif + endmacro + endwhile + exec_program + execute_process + export + export_library_dependencies + file + find_file + find_library + find_package + find_path + find_program + fltk_wrap_ui + foreach + function + generate_export_header + get_cmake_property + get_directory_property + get_filename_component + get_property + get_source_file_property + get_target_property + get_test_property + if + include + include_directories + include_external_msproject + include_guard + include_regular_expression + install + install_files + install_programs + install_targets + link_directories + link_libraries + list + load_cache + load_command + macro + make_directory + mark_as_advanced + math + message + option + output_required_files + project + qt_wrap_cpp + qt_wrap_ui + remove + remove_definitions + return + separate_arguments + set + set_directory_properties + set_property + set_source_files_properties + set_target_properties + set_tests_properties + site_name + source_group + string + subdir_depends + subdirs + target_compile_definitions + target_compile_features + target_compile_options + target_include_directories + target_link_directories + target_link_libraries + target_link_options + target_precompile_headers + target_sources + try_compile + try_run + unset + use_mangled_mesa + utility_source + variable_requires + variable_watch + while + write_file + + + + itk_wrap_tcl + vtk_make_instantiator + vtk_wrap_java + vtk_wrap_python + vtk_wrap_tcl + + + + + + ABSOLUTE_PATH + AFTER + ALIAS + ALL + ALPHABET + ANDROID_MK + ANY + AND + APPEND + APPENDNUMBER_ERRORS + APPEND_STRING + ARCHIVE + ARCHIVE_CREATE + ARCHIVE_EXTRACT + ARGS + ASCII + AT + BASE_DIR + BASE_DIRECTORY + AUTHOR_WARNING + BASE_NAME + BEFORE + BRIEF_DOCS + BUILD + BUILD_ID + BUNDLE + BUNDLE_EXECUTABLE + BYPRODUCTS + CACHE + CACHED_VARIABLE + CALL + CANCEL_CALL + CAPTURE_CMAKE_ERROR + CASE + CDASH_UPLOAD + CDASH_UPLOAD_TYPE + CHECK_FAIL + CHECK_PASS + CHECK_START + CHMOD + CHMOD_RECURSE + CLEAR + CMAKE_FIND_ROOT_PATH_BOTH + CMAKE_FLAGS + CODE + COMMAND + COMMAND_ECHO + COMMAND_ERROR_IS_FATAL + COMMAND_EXPAND_LISTS + COMMAND_NAME + COMMENT + COMPARE + COMPILE_DEFINITIONS + COMPILE_OUTPUT_VARIABLE + COMPILE_RESULT_VAR + COMPONENT + COMPONENTS + COMPRESSION + COMPRESSION_LEVEL + CONCAT + CONDITION + CONFIG + CONFIGS + CONFIGURATION + CONFIGURATIONS + CONFIGURE + CONFIGURE_DEPENDS + CONFLICTING_DEPENDENCIES_PREFIX + CONTENT + CONVERT + COPY + COPYONLY + COPY_FILE + COPY_FILE_ERROR + COPY_ON_ERROR + CREATE_LINK + CRLF + DECIMAL + DEFECT_COUNT + DEFER + DEFINED + DEFINITION + DEPENDS + DEPFILE + DESCRIPTION + DESTINATION + DIRECTORIES + DIRECTORY + DIRECTORY_PERMISSIONS + DOC + DOS + DOWNLOAD + ECHO_ERROR_VARIABLE + ECHO_OUTPUT_VARIABLE + ENCODING + ENV + END + EQUAL + ERROR_FILE + ERROR_QUIET + ERROR_STRIP_TRAILING_WHITESPACE + ERROR_VARIABLE + ESCAPE_QUOTES + EVAL + EXACT + EXCLUDE + EXCLUDE_FIXTURE + EXCLUDE_FIXTURE_CLEANUP + EXCLUDE_FIXTURE_SETUP + EXCLUDE_FROM_ALL + EXCLUDE_LABEL + EXECUTABLES + EXISTS + EXPECTED_HASH + EXPECTED_MD5 + EXPORT + EXPORT_ANDROID_MK + EXPORT_LINK_INTERFACE_LIBRARIES + EXPR + EXTENSION + EXTRA_INCLUDE + FATAL_ERROR + FILE + FILENAME + FILES + FILES_MATCHING + FILE_PERMISSIONS + FILTER + FIND + FLAGS + FOLLOW_SYMLINKS + FOLLOW_SYMLINK_CHAIN + FOR + FORCE + FORMAT + FRAMEWORK + FULL_DOCS + FUNCTION + GENERATE + GENEX_STRIP + GET + GET_CALL + GET_CALL_IDS + GET_RUNTIME_DEPENDENCIES + GLOB + GLOBAL + GLOB_RECURSE + GREATER + GREATER_EQUAL + GROUP + GROUP_EXECUTE + GROUP_READ + GUARD + GUID + HASH + HAS_EXTENSION + HAS_FILENAME + HAS_PARENT_PATH + HAS_RELATIVE_PART + HAS_ROOT_DIRECTORY + HAS_ROOT_NAME + HAS_ROOT_PATH + HAS_STEM + HEX + HEXADECIMAL + HINTS + HOMEPAGE_URL + HTTPHEADER + ID_VAR + IMPLICIT_DEPENDS + IMPORTED + IN + INACTIVITY_TIMEOUT + INCLUDE + INCLUDES + INCLUDE_INTERNALS + INCLUDE_LABEL + INHERITED + INPUT + INPUT_FILE + INSERT + INSTALL + INTERFACE + IS_ABSOLUTE + IS_PREFIX + IS_RELATIVE + IS_DIRECTORY + IS_NEWER_THAN + IS_SYMLINK + ITEMS + JOB_POOL + JOIN + JSON + LABELS + LANGUAGES + LAST + LAST_ONLY + LENGTH + LENGTH_MAXIMUM + LENGTH_MINIMUM + LESS + LESS_EQUAL + LF + LIBRARIES + LIBRARY + LIMIT + LIMIT_COUNT + LIMIT_INPUT + LIMIT_OUTPUT + LINK_INTERFACE_LIBRARIES + LINK_LIBRARIES + LINK_OPTIONS + LINK_PRIVATE + LINK_PUBLIC + LISTS + LIST_DIRECTORIES + LIST_ONLY + LOCK + LOG + MACOSX_BUNDLE + MAIN_DEPENDENCY + MAKE_C_IDENTIFIER + MAKE_DIRECTORY + MATCH + MATCHALL + MATCHES + MD5 + MEMBER + MESSAGE_NEVER + MODULE + MODULES + MTIME + NAME + NAMELINK_COMPONENT + NAMELINK_ONLY + NAMELINK_SKIP + NAMES + NAMESPACE + NATIVE_PATH + NAMES_PER_DIR + NEW + NEWLINE_CONSUME + NEWLINE_STYLE + NEW_PROCESS + NORMALIZE + NORMAL_PATH + NOT + NOTEQUAL + NOT_EQUAL + NO_CMAKE_BUILDS_PATH + NO_CMAKE_ENVIRONMENT_PATH + NO_CMAKE_FIND_ROOT_PATH + NO_CMAKE_PACKAGE_REGISTRY + NO_CMAKE_PATH + NO_CMAKE_SYSTEM_PACKAGE_REGISTRY + NO_CMAKE_SYSTEM_PATH + NO_DEFAULT_PATH + NO_HEX_CONVERSION + NO_MODULE + NO_PACKAGE_ROOT_PATH + NO_POLICY_SCOPE + NO_SOURCE_PERMISSIONS + NO_SYSTEM_ENVIRONMENT_PATH + NUMBER_ERRORS + NUMBER_WARNINGS + OBJECT + OBJECTS + OFF + OFFSET + OLD + ON + ONLY_CMAKE_FIND_ROOT_PATH + OPTIONAL + OPTIONAL_COMPONENTS + OPTIONS + ORDER + OR + OUTPUT + OUTPUT_DIRECTORY + OUTPUT_FILE + OUTPUT_FORMAT + OUTPUT_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE + OUTPUT_VARIABLE + OWNER_EXECUTE + OWNER_READ + OWNER_WRITE + PACKAGE + PARALLEL_LEVEL + PARENT_PATH + PARENT_SCOPE + PARSE_ARGV + PARTS + PATHS + PATH_SUFFIXES + PATH_TO_MESA + PATTERN + PATTERNS + PERMISSIONS + PLATFORM + POLICY + POP + POP_BACK + POP_FRONT + POST_BUILD + POST_EXCLUDE_REGEXES + POST_INCLUDE_REGEXES + PREFIX + PREORDER + PREPEND + PRE_BUILD + PRE_EXCLUDE_REGEXES + PRE_INCLUDE_REGEXES + PRE_LINK + PRIVATE + PRIVATE_HEADER + PROCESS + PROGRAM + PROGRAMS + PROGRAM_ARGS + PROJECT_NAME + PROPERTIES + PROPERTY + PUBLIC + PUBLIC_HEADER + PUSH + QUERY + QUIET + RANDOM + RANDOM_SEED + RANGE + READ + READ_SYMLINK + READ_WITH_PREFIX + REAL_PATH + REGEX + REGULAR_EXPRESSION + RELATIVE + RELATIVE_PART + RELATIVE_PATH + RELEASE + REMOVE + REMOVE_AT + REMOVE_DUPLICATES + REMOVE_EXTENSION + REMOVE_FILENAME + REMOVE_ITEM + REMOVE_RECURSE + RENAME + REPEAT + REPLACE + REPLACE_EXTENSION + REPLACE_FILENAME + REQUIRED + REQUIRED_VARIABLE1 + REQUIRED_VARIABLE2 + RESOLVED_DEPENDENCIES_VAR + RESOURCE + RESOURCE_SPEC_FILE + RESULT + RESULTS_VARIABLE + RESULT_VAR + RESULT_VARIABLE + RETRY_COUNT + RETRY_DELAY + RETURN_VALUE + REUSE_FROM + REVERSE + ROOT_DIRECTORY + ROOT_NAME + ROOT_PATH + RUNTIME + RUNTIME_DIRECTORY + RUN_OUTPUT_VARIABLE + RUN_RESULT_VAR + SCHEDULE_RANDOM + SCRIPT + SEPARATE_ARGS + SEND_ERROR + SET + SHA1 + SHA224 + SHA256 + SHA384 + SHA512 + SHARED + SIZE + SHOW_PROGRESS + SORT + SOURCE + SOURCES + START + STATIC + STATUS + STEM + STREQUAL + STRGREATER + STOP_ON_FAILURE + STOP_TIME + STRIDE + STRINGS + STRIP + SUBLIST + STRLESS + SUBMIT_URL + SUBSTRING + SYMBOLIC + SYSTEM + TARGET + TARGETS + TARGET_DIRECTORY + TEST + TEST_LOAD + TEST_VARIABLE + TIMEOUT + TIMESTAMP + TLS_CAINFO + TLS_VERIFY + TOLOWER + TOUCH + TOUCH_NOCREATE + TOUPPER + TO_CMAKE_PATH + TO_CMAKE_PATH_LIST + TO_NATIVE_PATH + TO_NATIVE_PATH_LIST + TRACK + TRANSFORM + TREE + TYPE + UNIX + UNRESOLVED_DEPENDENCIES_VAR + UNIX_COMMAND + UNKNOWN + UPLOAD + UPPER + USES_TERMINAL + USE_SOURCE_PERMISSIONS + UTC + UUID + VALUE + VARIABLE + VERBATIM + VERBOSE + VERSION + VERSION_EQUAL + VERSION_GREATER + VERSION_LESS + WARNING + WIN32 + WINDOWS_COMMAND + WORKING_DIRECTORY + WORLD_EXECUTE + WORLD_READ + WORLD_WRITE + WRITE + ZIP_LISTS + _EXTENSIONS + _STANDARD + _STANDARD_REQUIRED + + + + + + PATH + NAME + EXT + NAME_WE + ABSOLUTE + REALPATH + + FILEPATH + + STRING + BOOL + INTERNAL + + NUMBER_OF_LOGICAL_CORES + NUMBER_OF_PHYSICAL_CORES + HOSTNAME + FQDN + TOTAL_VIRTUAL_MEMORY + AVAILABLE_VIRTUAL_MEMORY + TOTAL_PHYSICAL_MEMORY + AVAILABLE_PHYSICAL_MEMORY + + + + + + ABSTRACT + ADDITIONAL_CLEAN_FILES + ADDITIONAL_MAKE_CLEAN_FILES + ADVANCED + AIX_EXPORT_ALL_SYMBOLS + ALIASED_TARGET + ALIAS_GLOBAL + ALLOW_DUPLICATE_CUSTOM_TARGETS + ANDROID_ANT_ADDITIONAL_OPTIONS + ANDROID_API + ANDROID_API_MIN + ANDROID_ARCH + ANDROID_ASSETS_DIRECTORIES + ANDROID_GUI + ANDROID_JAR_DEPENDENCIES + ANDROID_JAR_DIRECTORIES + ANDROID_JAVA_SOURCE_DIR + ANDROID_NATIVE_LIB_DEPENDENCIES + ANDROID_NATIVE_LIB_DIRECTORIES + ANDROID_PROCESS_MAX + ANDROID_PROGUARD + ANDROID_PROGUARD_CONFIG_PATH + ANDROID_SECURE_PROPS_PATH + ANDROID_SKIP_ANT_STEP + ANDROID_STL_TYPE + ARCHIVE_OUTPUT_DIRECTORY + ARCHIVE_OUTPUT_NAME + ATTACHED_FILES + ATTACHED_FILES_ON_FAIL + AUTOGEN_BUILD_DIR + AUTOGEN_ORIGIN_DEPENDS + AUTOGEN_PARALLEL + AUTOGEN_SOURCE_GROUP + AUTOGEN_TARGETS_FOLDER + AUTOGEN_TARGET_DEPENDS + AUTOMOC + AUTOMOC_COMPILER_PREDEFINES + AUTOMOC_DEPEND_FILTERS + AUTOMOC_EXECUTABLE + AUTOMOC_MACRO_NAMES + AUTOMOC_MOC_OPTIONS + AUTOMOC_PATH_PREFIX + AUTOMOC_SOURCE_GROUP + AUTOMOC_TARGETS_FOLDER + AUTORCC + AUTORCC_EXECUTABLE + AUTORCC_OPTIONS + AUTORCC_SOURCE_GROUP + AUTOUIC + AUTOUIC_EXECUTABLE + AUTOUIC_OPTIONS + AUTOUIC_SEARCH_PATHS + BINARY_DIR + BUILDSYSTEM_TARGETS + BUILD_RPATH + BUILD_RPATH_USE_ORIGIN + BUILD_WITH_INSTALL_NAME_DIR + BUILD_WITH_INSTALL_RPATH + BUNDLE + BUNDLE_EXTENSION + CACHE_VARIABLES + CLEAN_NO_CUSTOM + CMAKE_CONFIGURE_DEPENDS + CMAKE_CUDA_KNOWN_FEATURES + CMAKE_CXX_KNOWN_FEATURES + CMAKE_C_KNOWN_FEATURES + CMAKE_ROLE + COMMON_LANGUAGE_RUNTIME + COMPATIBLE_INTERFACE_BOOL + COMPATIBLE_INTERFACE_NUMBER_MAX + COMPATIBLE_INTERFACE_NUMBER_MIN + COMPATIBLE_INTERFACE_STRING + COMPILE_DEFINITIONS + COMPILE_FEATURES + COMPILE_FLAGS + COMPILE_OPTIONS + COMPILE_PDB_NAME + COMPILE_PDB_OUTPUT_DIRECTORY + COST + CPACK_DESKTOP_SHORTCUTS + CPACK_NEVER_OVERWRITE + CPACK_PERMANENT + CPACK_STARTUP_SHORTCUTS + CPACK_START_MENU_SHORTCUTS + CPACK_WIX_ACL + CROSSCOMPILING_EMULATOR + CUDA_ARCHITECTURES + CUDA_EXTENSIONS + CUDA_PTX_COMPILATION + CUDA_RESOLVE_DEVICE_SYMBOLS + CUDA_RUNTIME_LIBRARY + CUDA_SEPARABLE_COMPILATION + CUDA_STANDARD + CUDA_STANDARD_REQUIRED + CXX_EXTENSIONS + CXX_STANDARD + CXX_STANDARD_REQUIRED + C_EXTENSIONS + C_STANDARD + C_STANDARD_REQUIRED + DEBUG_CONFIGURATIONS + DEBUG_POSTFIX + DEFINE_SYMBOL + DEFINITIONS + DEPENDS + DEPLOYMENT_ADDITIONAL_FILES + DEPLOYMENT_REMOTE_DIRECTORY + DEPRECATION + DISABLED + DISABLED_FEATURES + DISABLE_PRECOMPILE_HEADERS + DOTNET_TARGET_FRAMEWORK + DOTNET_TARGET_FRAMEWORK_VERSION + ECLIPSE_EXTRA_CPROJECT_CONTENTS + ECLIPSE_EXTRA_NATURES + ENABLED_FEATURES + ENABLED_LANGUAGES + ENABLE_EXPORTS + ENVIRONMENT + EXCLUDE_FROM_ALL + EXCLUDE_FROM_DEFAULT_BUILD + EXPORT_COMPILE_COMMANDS + EXPORT_NAME + EXPORT_PROPERTIES + EXPORT_MACRO_NAME + EXPORT_FILE_NAME + DEPRECATED_MACRO_NAME + NO_EXPORT_MACRO_NAME + STATIC_DEFINE + NO_DEPRECATED_MACRO_NAME + DEFINE_NO_DEPRECATED + PREFIX_NAME + EXTERNAL_OBJECT + EchoString + FAIL_REGULAR_EXPRESSION + FIND_LIBRARY_USE_LIB32_PATHS + FIND_LIBRARY_USE_LIB64_PATHS + FIND_LIBRARY_USE_LIBX32_PATHS + FIND_LIBRARY_USE_OPENBSD_VERSIONING + FIXTURES_CLEANUP + FIXTURES_REQUIRED + FIXTURES_SETUP + FOLDER + FRAMEWORK + FRAMEWORK_VERSION + Fortran_FORMAT + Fortran_MODULE_DIRECTORY + Fortran_PREPROCESS + GENERATED + GENERATOR_FILE_NAME + GENERATOR_IS_MULTI_CONFIG + GHS_INTEGRITY_APP + GHS_NO_SOURCE_GROUP_FILE + GLOBAL_DEPENDS_DEBUG_MODE + GLOBAL_DEPENDS_NO_CYCLES + GNUtoMS + HAS_CXX + HEADER_FILE_ONLY + HELPSTRING + IMPLICIT_DEPENDS_INCLUDE_TRANSFORM + IMPORTED + IMPORTED_COMMON_LANGUAGE_RUNTIME + IMPORTED_CONFIGURATIONS + IMPORTED_GLOBAL + IMPORTED_IMPLIB + IMPORTED_LIBNAME + IMPORTED_LINK_DEPENDENT_LIBRARIES + IMPORTED_LINK_INTERFACE_LANGUAGES + IMPORTED_LINK_INTERFACE_LIBRARIES + IMPORTED_LINK_INTERFACE_MULTIPLICITY + IMPORTED_LOCATION + IMPORTED_NO_SONAME + IMPORTED_OBJECTS + IMPORTED_SONAME + IMPORT_PREFIX + IMPORT_SUFFIX + INCLUDE_DIRECTORIES + INCLUDE_REGULAR_EXPRESSION + INSTALL_NAME_DIR + INSTALL_REMOVE_ENVIRONMENT_RPATH + INSTALL_RPATH + INSTALL_RPATH_USE_LINK_PATH + INTERFACE_AUTOUIC_OPTIONS + INTERFACE_COMPILE_DEFINITIONS + INTERFACE_COMPILE_FEATURES + INTERFACE_COMPILE_OPTIONS + INTERFACE_INCLUDE_DIRECTORIES + INTERFACE_LINK_DEPENDS + INTERFACE_LINK_DIRECTORIES + INTERFACE_LINK_LIBRARIES + INTERFACE_LINK_OPTIONS + INTERFACE_POSITION_INDEPENDENT_CODE + INTERFACE_PRECOMPILE_HEADERS + INTERFACE_SOURCES + INTERFACE_SYSTEM_INCLUDE_DIRECTORIES + INTERPROCEDURAL_OPTIMIZATION + IN_TRY_COMPILE + IOS_INSTALL_COMBINED + ISPC_HEADER_DIRECTORY + ISPC_HEADER_SUFFIX + ISPC_INSTRUCTION_SETS + JOB_POOLS + JOB_POOL_COMPILE + JOB_POOL_LINK + JOB_POOL_PRECOMPILE_HEADER + KEEP_EXTENSION + LABELS + LANGUAGE + LIBRARY_OUTPUT_DIRECTORY + LIBRARY_OUTPUT_NAME + LINKER_LANGUAGE + LINK_DEPENDS + LINK_DEPENDS_NO_SHARED + LINK_DIRECTORIES + LINK_FLAGS + LINK_INTERFACE_LIBRARIES + LINK_INTERFACE_MULTIPLICITY + LINK_LIBRARIES + LINK_OPTIONS + LINK_SEARCH_END_STATIC + LINK_SEARCH_START_STATIC + LINK_WHAT_YOU_USE + LISTFILE_STACK + LOCATION + MACHO_COMPATIBILITY_VERSION + MACHO_CURRENT_VERSION + MACOSX_BUNDLE + MACOSX_BUNDLE_INFO_PLIST + MACOSX_FRAMEWORK_INFO_PLIST + MACOSX_PACKAGE_LOCATION + MACOSX_RPATH + MACROS + MANUALLY_ADDED_DEPENDENCIES + MEASUREMENT + MODIFIED + MSVC_RUNTIME_LIBRARY + NAME + NO_SONAME + NO_SYSTEM_FROM_IMPORTED + OBJCXX_EXTENSIONS + OBJCXX_STANDARD + OBJCXX_STANDARD_REQUIRED + OBJC_EXTENSIONS + OBJC_STANDARD + OBJC_STANDARD_REQUIRED + OBJECT_DEPENDS + OBJECT_OUTPUTS + OPTIMIZE_DEPENDENCIES + OSX_ARCHITECTURES + OUTPUT_NAME + PACKAGES_FOUND + PACKAGES_NOT_FOUND + PARENT_DIRECTORY + PASS_REGULAR_EXPRESSION + PCH_INSTANTIATE_TEMPLATES + PCH_WARN_INVALID + PDB_NAME + PDB_OUTPUT_DIRECTORY + POSITION_INDEPENDENT_CODE + POST_INSTALL_SCRIPT + PRECOMPILE_HEADERS + PRECOMPILE_HEADERS_REUSE_FROM + PREDEFINED_TARGETS_FOLDER + PREFIX + PRE_INSTALL_SCRIPT + PRIVATE_HEADER + PROCESSORS + PROCESSOR_AFFINITY + PROJECT_LABEL + PUBLIC_HEADER + REPORT_UNDEFINED_PROPERTIES + REQUIRED_FILES + RESOURCE + RESOURCE_GROUPS + RESOURCE_LOCK + RULE_LAUNCH_COMPILE + RULE_LAUNCH_CUSTOM + RULE_LAUNCH_LINK + RULE_MESSAGES + RUNTIME_OUTPUT_DIRECTORY + RUNTIME_OUTPUT_NAME + RUN_SERIAL + SKIP_AUTOGEN + SKIP_AUTOMOC + SKIP_AUTORCC + SKIP_AUTOUIC + SKIP_BUILD_RPATH + SKIP_PRECOMPILE_HEADERS + SKIP_REGULAR_EXPRESSION + SKIP_RETURN_CODE + SKIP_UNITY_BUILD_INCLUSION + SOURCES + SOURCE_DIR + SOVERSION + STATIC_LIBRARY_FLAGS + STATIC_LIBRARY_OPTIONS + STRINGS + SUBDIRECTORIES + SUFFIX + SYMBOLIC + Swift_DEPENDENCIES_FILE + Swift_DIAGNOSTICS_FILE + Swift_LANGUAGE_VERSION + Swift_MODULE_DIRECTORY + Swift_MODULE_NAME + TARGET_ARCHIVES_MAY_BE_SHARED_LIBS + TARGET_MESSAGES + TARGET_SUPPORTS_SHARED_LIBS + TESTS + TEST_INCLUDE_FILE + TEST_INCLUDE_FILES + TIMEOUT + TIMEOUT_AFTER_MATCH + TYPE + UNITY_BUILD + UNITY_BUILD_BATCH_SIZE + UNITY_BUILD_CODE_AFTER_INCLUDE + UNITY_BUILD_CODE_BEFORE_INCLUDE + UNITY_BUILD_MODE + UNITY_BUILD_UNIQUE_ID + UNITY_GROUP + USE_FOLDERS + VALUE + VARIABLES + VERSION + VISIBILITY_INLINES_HIDDEN + VS_CONFIGURATION_TYPE + VS_COPY_TO_OUT_DIR + VS_DEBUGGER_COMMAND + VS_DEBUGGER_COMMAND_ARGUMENTS + VS_DEBUGGER_ENVIRONMENT + VS_DEBUGGER_WORKING_DIRECTORY + VS_DEPLOYMENT_CONTENT + VS_DEPLOYMENT_LOCATION + VS_DESKTOP_EXTENSIONS_VERSION + VS_DOTNET_DOCUMENTATION_FILE + VS_DOTNET_REFERENCES + VS_DOTNET_REFERENCES_COPY_LOCAL + VS_DOTNET_TARGET_FRAMEWORK_VERSION + VS_DPI_AWARE + VS_GLOBAL_KEYWORD + VS_GLOBAL_PROJECT_TYPES + VS_GLOBAL_ROOTNAMESPACE + VS_INCLUDE_IN_VSIX + VS_IOT_EXTENSIONS_VERSION + VS_IOT_STARTUP_TASK + VS_JUST_MY_CODE_DEBUGGING + VS_KEYWORD + VS_MOBILE_EXTENSIONS_VERSION + VS_NO_SOLUTION_DEPLOY + VS_PACKAGE_REFERENCES + VS_PLATFORM_TOOLSET + VS_PROJECT_IMPORT + VS_RESOURCE_GENERATOR + VS_SCC_AUXPATH + VS_SCC_LOCALPATH + VS_SCC_PROJECTNAME + VS_SCC_PROVIDER + VS_SDK_REFERENCES + VS_SETTINGS + VS_SHADER_DISABLE_OPTIMIZATIONS + VS_SHADER_ENABLE_DEBUG + VS_SHADER_ENTRYPOINT + VS_SHADER_FLAGS + VS_SHADER_MODEL + VS_SHADER_OBJECT_FILE_NAME + VS_SHADER_OUTPUT_HEADER_FILE + VS_SHADER_TYPE + VS_SHADER_VARIABLE_NAME + VS_SOLUTION_DEPLOY + VS_STARTUP_PROJECT + VS_TOOL_OVERRIDE + VS_USER_PROPS + VS_WINDOWS_TARGET_PLATFORM_MIN_VERSION + VS_WINRT_COMPONENT + VS_WINRT_EXTENSIONS + VS_WINRT_REFERENCES + VS_XAML_TYPE + WILL_FAIL + WIN32_EXECUTABLE + WINDOWS_EXPORT_ALL_SYMBOLS + WORKING_DIRECTORY + WRAP_EXCLUDE + __CMAKE_DELETE_CACHE_CHANGE_VARS_ + XCODE_EMBED_FRAMEWORKS_CODE_SIGN_ON_COPY + XCODE_EMBED_FRAMEWORKS_REMOVE_HEADERS_ON_COPY + XCODE_EMIT_EFFECTIVE_PLATFORM_NAME + XCODE_EXPLICIT_FILE_TYPE + XCODE_FILE_ATTRIBUTES + XCODE_GENERATE_SCHEME + XCODE_LAST_KNOWN_FILE_TYPE + XCODE_LINK_BUILD_PHASE_MODE + XCODE_PRODUCT_TYPE + XCODE_SCHEME_ADDRESS_SANITIZER + XCODE_SCHEME_ADDRESS_SANITIZER_USE_AFTER_RETURN + XCODE_SCHEME_ARGUMENTS + XCODE_SCHEME_DEBUG_AS_ROOT + XCODE_SCHEME_DEBUG_DOCUMENT_VERSIONING + XCODE_SCHEME_DISABLE_MAIN_THREAD_CHECKER + XCODE_SCHEME_DYNAMIC_LIBRARY_LOADS + XCODE_SCHEME_DYNAMIC_LINKER_API_USAGE + XCODE_SCHEME_ENVIRONMENT + XCODE_SCHEME_EXECUTABLE + XCODE_SCHEME_GUARD_MALLOC + XCODE_SCHEME_MAIN_THREAD_CHECKER_STOP + XCODE_SCHEME_MALLOC_GUARD_EDGES + XCODE_SCHEME_MALLOC_SCRIBBLE + XCODE_SCHEME_MALLOC_STACK + XCODE_SCHEME_THREAD_SANITIZER + XCODE_SCHEME_THREAD_SANITIZER_STOP + XCODE_SCHEME_UNDEFINED_BEHAVIOUR_SANITIZER + XCODE_SCHEME_UNDEFINED_BEHAVIOUR_SANITIZER_STOP + XCODE_SCHEME_WORKING_DIRECTORY + XCODE_SCHEME_ZOMBIE_OBJECTS + XCTEST + + + + + COMMANDS + DEFINITION + + + + + + ANDROID + APPLE + BORLAND + BUILD_SHARED_LIBS + CACHE + CMAKE_ABSOLUTE_DESTINATION_FILES + CMAKE_AIX_EXPORT_ALL_SYMBOLS + CMAKE_ANDROID_ANT_ADDITIONAL_OPTIONS + CMAKE_ANDROID_API + CMAKE_ANDROID_API_MIN + CMAKE_ANDROID_ARCH + CMAKE_ANDROID_ARCH_ABI + CMAKE_ANDROID_ARM_MODE + CMAKE_ANDROID_ARM_NEON + CMAKE_ANDROID_ASSETS_DIRECTORIES + CMAKE_ANDROID_EXCEPTIONS + CMAKE_ANDROID_GUI + CMAKE_ANDROID_JAR_DEPENDENCIES + CMAKE_ANDROID_JAR_DIRECTORIES + CMAKE_ANDROID_JAVA_SOURCE_DIR + CMAKE_ANDROID_NATIVE_LIB_DEPENDENCIES + CMAKE_ANDROID_NATIVE_LIB_DIRECTORIES + CMAKE_ANDROID_NDK + CMAKE_ANDROID_NDK_DEPRECATED_HEADERS + CMAKE_ANDROID_NDK_TOOLCHAIN_HOST_TAG + CMAKE_ANDROID_NDK_TOOLCHAIN_VERSION + CMAKE_ANDROID_NDK_VERSION + CMAKE_ANDROID_PROCESS_MAX + CMAKE_ANDROID_PROGUARD + CMAKE_ANDROID_PROGUARD_CONFIG_PATH + CMAKE_ANDROID_RTTI + CMAKE_ANDROID_SECURE_PROPS_PATH + CMAKE_ANDROID_SKIP_ANT_STEP + CMAKE_ANDROID_STANDALONE_TOOLCHAIN + CMAKE_ANDROID_STL_TYPE + CMAKE_APPBUNDLE_PATH + CMAKE_APPLE_SILICON_PROCESSOR + CMAKE_AR + CMAKE_ARCHIVE_OUTPUT_DIRECTORY + CMAKE_ARGC + CMAKE_ARGV0 + CMAKE_AUTOGEN_ORIGIN_DEPENDS + CMAKE_AUTOGEN_PARALLEL + CMAKE_AUTOGEN_VERBOSE + CMAKE_AUTOMOC + CMAKE_AUTOMOC_COMPILER_PREDEFINES + CMAKE_AUTOMOC_DEPEND_FILTERS + CMAKE_AUTOMOC_MACRO_NAMES + CMAKE_AUTOMOC_MOC_OPTIONS + CMAKE_AUTOMOC_PATH_PREFIX + CMAKE_AUTOMOC_RELAXED_MODE + CMAKE_AUTORCC + CMAKE_AUTORCC_OPTIONS + CMAKE_AUTOUIC + CMAKE_AUTOUIC_OPTIONS + CMAKE_AUTOUIC_SEARCH_PATHS + CMAKE_BACKWARDS_COMPATIBILITY + CMAKE_BINARY_DIR + CMAKE_BUILD_RPATH + CMAKE_BUILD_RPATH_USE_ORIGIN + CMAKE_BUILD_TOOL + CMAKE_BUILD_TYPE + CMAKE_BUILD_WITH_INSTALL_NAME_DIR + CMAKE_BUILD_WITH_INSTALL_RPATH + CMAKE_CACHEFILE_DIR + CMAKE_CACHE_MAJOR_VERSION + CMAKE_CACHE_MINOR_VERSION + CMAKE_CACHE_PATCH_VERSION + CMAKE_CFG_INTDIR + CMAKE_CLANG_VFS_OVERLAY + CMAKE_CL_64 + CMAKE_CODEBLOCKS_COMPILER_ID + CMAKE_CODEBLOCKS_EXCLUDE_EXTERNAL_FILES + CMAKE_CODELITE_USE_TARGETS + CMAKE_COLOR_MAKEFILE + CMAKE_COMMAND + CMAKE_COMPILER_2005 + CMAKE_COMPILER_IS_GNUCC + CMAKE_COMPILER_IS_GNUCXX + CMAKE_COMPILER_IS_GNUG77 + CMAKE_COMPILE_PDB_OUTPUT_DIRECTORY + CMAKE_CONFIGURATION_TYPES + CMAKE_CPACK_COMMAND + CMAKE_CROSSCOMPILING + CMAKE_CROSSCOMPILING_EMULATOR + CMAKE_CROSS_CONFIGS + CMAKE_CTEST_ARGUMENTS + CMAKE_CTEST_COMMAND + CMAKE_CUDA_ARCHITECTURES + CMAKE_CUDA_COMPILE_FEATURES + CMAKE_CUDA_EXTENSIONS + CMAKE_CUDA_HOST_COMPILER + CMAKE_CUDA_RESOLVE_DEVICE_SYMBOLS + CMAKE_CUDA_RUNTIME_LIBRARY + CMAKE_CUDA_SEPARABLE_COMPILATION + CMAKE_CUDA_STANDARD + CMAKE_CUDA_STANDARD_REQUIRED + CMAKE_CUDA_TOOLKIT_INCLUDE_DIRECTORIES + CMAKE_CURRENT_BINARY_DIR + CMAKE_CURRENT_FUNCTION + CMAKE_CURRENT_FUNCTION_LIST_DIR + CMAKE_CURRENT_FUNCTION_LIST_FILE + CMAKE_CURRENT_FUNCTION_LIST_LINE + CMAKE_CURRENT_LIST_DIR + CMAKE_CURRENT_LIST_FILE + CMAKE_CURRENT_LIST_LINE + CMAKE_CURRENT_SOURCE_DIR + CMAKE_CXX_COMPILE_FEATURES + CMAKE_CXX_EXTENSIONS + CMAKE_CXX_STANDARD_REQUIRED + CMAKE_CXX_STANDARD + CMAKE_CXX_STANDARD_REQUIRED + CMAKE_C_COMPILE_FEATURES + CMAKE_C_EXTENSIONS + CMAKE_C_STANDARD + CMAKE_C_STANDARD_REQUIRED + CMAKE_DEBUG_POSTFIX + CMAKE_DEBUG_TARGET_PROPERTIES + CMAKE_DEFAULT_BUILD_TYPE + CMAKE_DEFAULT_CONFIGS + CMAKE_DEPENDS_IN_PROJECT_ONLY + CMAKE_DEPENDS_USE_COMPILER + CMAKE_DIRECTORY_LABELS + CMAKE_DISABLE_PRECOMPILE_HEADERS + CMAKE_DL_LIBS + CMAKE_DOTNET_TARGET_FRAMEWORK + CMAKE_DOTNET_TARGET_FRAMEWORK_VERSION + CMAKE_ECLIPSE_GENERATE_LINKED_RESOURCES + CMAKE_ECLIPSE_GENERATE_SOURCE_PROJECT + CMAKE_ECLIPSE_MAKE_ARGUMENTS + CMAKE_ECLIPSE_RESOURCE_ENCODING + CMAKE_ECLIPSE_VERSION + CMAKE_EDIT_COMMAND + CMAKE_ENABLE_EXPORTS + CMAKE_ERROR_DEPRECATED + CMAKE_ERROR_ON_ABSOLUTE_INSTALL_DESTINATION + CMAKE_EXECUTABLE_SUFFIX + CMAKE_EXECUTE_PROCESS_COMMAND_ECHO + CMAKE_EXE_LINKER_FLAGS + CMAKE_EXE_LINKER_FLAGS_INIT + CMAKE_EXPORT_COMPILE_COMMANDS + CMAKE_EXPORT_NO_PACKAGE_REGISTRY + CMAKE_EXPORT_PACKAGE_REGISTRY + CMAKE_EXTRA_GENERATOR + CMAKE_EXTRA_SHARED_LIBRARY_SUFFIXES + CMAKE_FIND_APPBUNDLE + CMAKE_FIND_DEBUG_MODE + CMAKE_FIND_FRAMEWORK + CMAKE_FIND_LIBRARY_CUSTOM_LIB_SUFFIX + CMAKE_FIND_LIBRARY_PREFIXES + CMAKE_FIND_LIBRARY_SUFFIXES + CMAKE_FIND_NO_INSTALL_PREFIX + CMAKE_FIND_PACKAGE_NAME + CMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY + CMAKE_FIND_PACKAGE_NO_SYSTEM_PACKAGE_REGISTRY + CMAKE_FIND_PACKAGE_PREFER_CONFIG + CMAKE_FIND_PACKAGE_RESOLVE_SYMLINKS + CMAKE_FIND_PACKAGE_SORT_DIRECTION + CMAKE_FIND_PACKAGE_SORT_ORDER + CMAKE_FIND_PACKAGE_WARN_NO_MODULE + CMAKE_FIND_ROOT_PATH + CMAKE_FIND_ROOT_PATH_MODE_INCLUDE + CMAKE_FIND_ROOT_PATH_MODE_LIBRARY + CMAKE_FIND_ROOT_PATH_MODE_PACKAGE + CMAKE_FIND_ROOT_PATH_MODE_PROGRAM + CMAKE_FIND_USE_CMAKE_ENVIRONMENT_PATH + CMAKE_FIND_USE_CMAKE_PATH + CMAKE_FIND_USE_CMAKE_SYSTEM_PATH + CMAKE_FIND_USE_PACKAGE_REGISTRY + CMAKE_FIND_USE_PACKAGE_ROOT_PATH + CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH + CMAKE_FIND_USE_SYSTEM_PACKAGE_REGISTRY + CMAKE_FOLDER + CMAKE_FRAMEWORK + CMAKE_FRAMEWORK_PATH + CMAKE_Fortran_FORMAT + CMAKE_Fortran_MODDIR_DEFAULT + CMAKE_Fortran_MODDIR_FLAG + CMAKE_Fortran_MODOUT_FLAG + CMAKE_Fortran_MODULE_DIRECTORY + CMAKE_Fortran_PREPROCESS + CMAKE_GENERATOR + CMAKE_GENERATOR_INSTANCE + CMAKE_GENERATOR_PLATFORM + CMAKE_GENERATOR_TOOLSET + CMAKE_GHS_NO_SOURCE_GROUP_FILE + CMAKE_GLOBAL_AUTOGEN_TARGET + CMAKE_GLOBAL_AUTOGEN_TARGET_NAME + CMAKE_GLOBAL_AUTORCC_TARGET + CMAKE_GLOBAL_AUTORCC_TARGET_NAME + CMAKE_GNUtoMS + CMAKE_HOME_DIRECTORY + CMAKE_HOST_APPLE + CMAKE_HOST_SOLARIS + CMAKE_HOST_SYSTEM + CMAKE_HOST_SYSTEM_NAME + CMAKE_HOST_SYSTEM_PROCESSOR + CMAKE_HOST_SYSTEM_VERSION + CMAKE_HOST_UNIX + CMAKE_HOST_WIN32 + CMAKE_IGNORE_PATH + CMAKE_IMPORT_LIBRARY_PREFIX + CMAKE_IMPORT_LIBRARY_SUFFIX + CMAKE_INCLUDE_CURRENT_DIR + CMAKE_INCLUDE_CURRENT_DIR_IN_INTERFACE + CMAKE_INCLUDE_DIRECTORIES_BEFORE + CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE + CMAKE_INCLUDE_PATH + CMAKE_INSTALL_DEFAULT_COMPONENT_NAME + CMAKE_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS + CMAKE_INSTALL_MESSAGE + CMAKE_INSTALL_NAME_DIR + CMAKE_INSTALL_PREFIX + CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT + CMAKE_INSTALL_REMOVE_ENVIRONMENT_RPATH + CMAKE_INSTALL_RPATH + CMAKE_INSTALL_RPATH_USE_LINK_PATH + CMAKE_INTERNAL_PLATFORM_ABI + CMAKE_INTERPROCEDURAL_OPTIMIZATION + CMAKE_IOS_INSTALL_COMBINED + CMAKE_ISPC_HEADER_DIRECTORY + CMAKE_ISPC_HEADER_SUFFIX + CMAKE_ISPC_INSTRUCTION_SETS + CMAKE_JOB_POOLS + CMAKE_JOB_POOL_COMPILE + CMAKE_JOB_POOL_LINK + CMAKE_JOB_POOL_PRECOMPILE_HEADER + CMAKE_LIBRARY_ARCHITECTURE + CMAKE_LIBRARY_ARCHITECTURE_REGEX + CMAKE_LIBRARY_OUTPUT_DIRECTORY + CMAKE_LIBRARY_PATH + CMAKE_LIBRARY_PATH_FLAG + CMAKE_LINK_DEF_FILE_FLAG + CMAKE_LINK_DEPENDS_NO_SHARED + CMAKE_LINK_DIRECTORIES_BEFORE + CMAKE_LINK_INTERFACE_LIBRARIES + CMAKE_LINK_LIBRARY_FILE_FLAG + CMAKE_LINK_LIBRARY_FLAG + CMAKE_LINK_LIBRARY_SUFFIX + CMAKE_LINK_SEARCH_END_STATIC + CMAKE_LINK_SEARCH_START_STATIC + CMAKE_LINK_WHAT_YOU_USE + CMAKE_MACOSX_BUNDLE + CMAKE_MACOSX_RPATH + CMAKE_MAJOR_VERSION + CMAKE_MAKE_PROGRAM + CMAKE_MATCH_COUNT + CMAKE_MAXIMUM_RECURSION_DEPTH + CMAKE_MESSAGE_CONTEXT + CMAKE_MESSAGE_CONTEXT_SHOW + CMAKE_MESSAGE_INDENT + CMAKE_MESSAGE_LOG_LEVEL + CMAKE_MFC_FLAG + CMAKE_MINIMUM_REQUIRED_VERSION + CMAKE_MINOR_VERSION + CMAKE_MODULE_LINKER_FLAGS + CMAKE_MODULE_LINKER_FLAGS_INIT + CMAKE_MODULE_PATH + CMAKE_MSVCIDE_RUN_PATH + CMAKE_MSVC_RUNTIME_LIBRARY + CMAKE_NETRC + CMAKE_NETRC_FILE + CMAKE_NINJA_OUTPUT_PATH_PREFIX + CMAKE_NOT_USING_CONFIG_FLAGS + CMAKE_NO_BUILTIN_CHRPATH + CMAKE_NO_SYSTEM_FROM_IMPORTED + CMAKE_OBJCXX_EXTENSIONS + CMAKE_OBJCXX_STANDARD + CMAKE_OBJCXX_STANDARD_REQUIRED + CMAKE_OBJC_EXTENSIONS + CMAKE_OBJC_STANDARD + CMAKE_OBJC_STANDARD_REQUIRED + CMAKE_OBJECT_PATH_MAX + CMAKE_OPTIMIZE_DEPENDENCIES + CMAKE_OSX_ARCHITECTURES + CMAKE_OSX_DEPLOYMENT_TARGET + CMAKE_OSX_SYSROOT + CMAKE_PARENT_LIST_FILE + CMAKE_PATCH_VERSION + CMAKE_PCH_INSTANTIATE_TEMPLATES + CMAKE_PCH_WARN_INVALID + CMAKE_PDB_OUTPUT_DIRECTORY + CMAKE_POSITION_INDEPENDENT_CODE + CMAKE_PREFIX_PATH + CMAKE_PROGRAM_PATH + CMAKE_PROJECT_DESCRIPTION + CMAKE_PROJECT_HOMEPAGE_URL + CMAKE_PROJECT_INCLUDE + CMAKE_PROJECT_INCLUDE_BEFORE + CMAKE_PROJECT_NAME + CMAKE_PROJECT_VERSION + CMAKE_PROJECT_VERSION_MAJOR + CMAKE_PROJECT_VERSION_MINOR + CMAKE_PROJECT_VERSION_PATCH + CMAKE_PROJECT_VERSION_TWEAK + CMAKE_RANLIB + CMAKE_ROOT + CMAKE_RULE_MESSAGES + CMAKE_RUNTIME_OUTPUT_DIRECTORY + CMAKE_SCRIPT_MODE_FILE + CMAKE_SHARED_LIBRARY_PREFIX + CMAKE_SHARED_LIBRARY_SUFFIX + CMAKE_SHARED_LINKER_FLAGS + CMAKE_SHARED_LINKER_FLAGS_INIT + CMAKE_SHARED_MODULE_PREFIX + CMAKE_SHARED_MODULE_SUFFIX + CMAKE_SIZEOF_VOID_P + CMAKE_SKIP_BUILD_RPATH + CMAKE_SKIP_INSTALL_ALL_DEPENDENCY + CMAKE_SKIP_INSTALL_RPATH + CMAKE_SKIP_INSTALL_RULES + CMAKE_SKIP_RPATH + CMAKE_SOURCE_DIR + CMAKE_STAGING_PREFIX + CMAKE_STANDARD_LIBRARIES + CMAKE_STATIC_LIBRARY_PREFIX + CMAKE_STATIC_LIBRARY_SUFFIX + CMAKE_STATIC_LINKER_FLAGS + CMAKE_STATIC_LINKER_FLAGS_INIT + CMAKE_SUBLIME_TEXT_2_ENV_SETTINGS + CMAKE_SUBLIME_TEXT_2_EXCLUDE_BUILD_TREE + CMAKE_SUPPRESS_REGENERATION + CMAKE_SYSROOT + CMAKE_SYSROOT_COMPILE + CMAKE_SYSROOT_LINK + CMAKE_SYSTEM + CMAKE_SYSTEM_APPBUNDLE_PATH + CMAKE_SYSTEM_FRAMEWORK_PATH + CMAKE_SYSTEM_IGNORE_PATH + CMAKE_SYSTEM_INCLUDE_PATH + CMAKE_SYSTEM_LIBRARY_PATH + CMAKE_SYSTEM_NAME + CMAKE_SYSTEM_PREFIX_PATH + CMAKE_SYSTEM_PROCESSOR + CMAKE_SYSTEM_PROGRAM_PATH + CMAKE_SYSTEM_VERSION + CMAKE_Swift_LANGUAGE_VERSION + CMAKE_Swift_MODULE_DIRECTORY + CMAKE_Swift_NUM_THREADS + CMAKE_TOOLCHAIN_FILE + CMAKE_TRY_COMPILE_CONFIGURATION + CMAKE_TRY_COMPILE_PLATFORM_VARIABLES + CMAKE_TRY_COMPILE_TARGET_TYPE + CMAKE_TWEAK_VERSION + CMAKE_UNITY_BUILD + CMAKE_UNITY_BUILD_BATCH_SIZE + CMAKE_UNITY_BUILD_UNIQUE_ID + CMAKE_USER_MAKE_RULES_OVERRIDE + CMAKE_USE_RELATIVE_PATHS + CMAKE_VERBOSE_MAKEFILE + CMAKE_VERSION + CMAKE_VISIBILITY_INLINES_HIDDEN + CMAKE_VS_DEVENV_COMMAND + CMAKE_VS_GLOBALS + CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD + CMAKE_VS_INCLUDE_PACKAGE_TO_DEFAULT_BUILD + CMAKE_VS_INTEL_Fortran_PROJECT_VERSION + CMAKE_VS_JUST_MY_CODE_DEBUGGING + CMAKE_VS_MSBUILD_COMMAND + CMAKE_VS_NsightTegra_VERSION + CMAKE_VS_PLATFORM_NAME + CMAKE_VS_PLATFORM_NAME_DEFAULT + CMAKE_VS_MSDEV_COMMAND + CMAKE_VS_PLATFORM_TOOLSET + CMAKE_VS_PLATFORM_TOOLSET_CUDA + CMAKE_VS_PLATFORM_TOOLSET_CUDA_CUSTOM_DIR + CMAKE_VS_PLATFORM_TOOLSET_HOST_ARCHITECTURE + CMAKE_VS_PLATFORM_TOOLSET_VERSION + CMAKE_VS_SDK_EXCLUDE_DIRECTORIES + CMAKE_VS_SDK_EXECUTABLE_DIRECTORIES + CMAKE_VS_SDK_INCLUDE_DIRECTORIES + CMAKE_VS_SDK_LIBRARY_DIRECTORIES + CMAKE_VS_SDK_LIBRARY_WINRT_DIRECTORIES + CMAKE_VS_SDK_REFERENCE_DIRECTORIES + CMAKE_VS_SDK_SOURCE_DIRECTORIES + CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION + CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION_MAXIMUM + CMAKE_VS_WINRT_BY_DEFAULT + CMAKE_WARN_DEPRECATED + CMAKE_WARN_ON_ABSOLUTE_INSTALL_DESTINATION + CMAKE_WIN32_EXECUTABLE + CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS + CMAKE_XCODE_BUILD_SYSTEM + CMAKE_XCODE_GENERATE_SCHEME + CMAKE_XCODE_GENERATE_TOP_LEVEL_PROJECT_ONLY + CMAKE_XCODE_LINK_BUILD_PHASE_MODE + CMAKE_XCODE_PLATFORM_TOOLSET + CMAKE_XCODE_SCHEME_ADDRESS_SANITIZER + CMAKE_XCODE_SCHEME_ADDRESS_SANITIZER_USE_AFTER_RETURN + CMAKE_XCODE_SCHEME_DEBUG_DOCUMENT_VERSIONING + CMAKE_XCODE_SCHEME_DISABLE_MAIN_THREAD_CHECKER + CMAKE_XCODE_SCHEME_DYNAMIC_LIBRARY_LOADS + CMAKE_XCODE_SCHEME_DYNAMIC_LINKER_API_USAGE + CMAKE_XCODE_SCHEME_ENVIRONMENT + CMAKE_XCODE_SCHEME_GUARD_MALLOC + CMAKE_XCODE_SCHEME_MAIN_THREAD_CHECKER_STOP + CMAKE_XCODE_SCHEME_MALLOC_GUARD_EDGES + CMAKE_XCODE_SCHEME_MALLOC_SCRIBBLE + CMAKE_XCODE_SCHEME_MALLOC_STACK + CMAKE_XCODE_SCHEME_THREAD_SANITIZER + CMAKE_XCODE_SCHEME_THREAD_SANITIZER_STOP + CMAKE_XCODE_SCHEME_UNDEFINED_BEHAVIOUR_SANITIZER + CMAKE_XCODE_SCHEME_UNDEFINED_BEHAVIOUR_SANITIZER_STOP + CMAKE_XCODE_SCHEME_WORKING_DIRECTORY + CMAKE_XCODE_SCHEME_ZOMBIE_OBJECTS + CPACK_ABSOLUTE_DESTINATION_FILES + CPACK_COMPONENT_INCLUDE_TOPLEVEL_DIRECTORY + CPACK_ERROR_ON_ABSOLUTE_INSTALL_DESTINATION + CPACK_INCLUDE_TOPLEVEL_DIRECTORY + CPACK_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS + CPACK_INSTALL_SCRIPT + CPACK_PACKAGING_INSTALL_PREFIX + CPACK_SET_DESTDIR + CPACK_WARN_ON_ABSOLUTE_INSTALL_DESTINATION + CTEST_BINARY_DIRECTORY + CTEST_BUILD_COMMAND + CTEST_BUILD_NAME + CTEST_BZR_COMMAND + CTEST_BZR_UPDATE_OPTIONS + CTEST_CHANGE_ID + CTEST_CHECKOUT_COMMAND + CTEST_CONFIGURATION_TYPE + CTEST_CONFIGURE_COMMAND + CTEST_COVERAGE_COMMAND + CTEST_COVERAGE_EXTRA_FLAGS + CTEST_CURL_OPTIONS + CTEST_CUSTOM_COVERAGE_EXCLUDE + CTEST_CUSTOM_ERROR_EXCEPTION + CTEST_CUSTOM_ERROR_MATCH + CTEST_CUSTOM_ERROR_POST_CONTEXT + CTEST_CUSTOM_ERROR_PRE_CONTEXT + CTEST_CUSTOM_MAXIMUM_FAILED_TEST_OUTPUT_SIZE + CTEST_CUSTOM_MAXIMUM_NUMBER_OF_ERRORS + CTEST_CUSTOM_MAXIMUM_NUMBER_OF_WARNINGS + CTEST_CUSTOM_MAXIMUM_PASSED_TEST_OUTPUT_SIZE + CTEST_CUSTOM_MEMCHECK_IGNORE + CTEST_CUSTOM_POST_MEMCHECK + CTEST_CUSTOM_POST_TEST + CTEST_CUSTOM_PRE_MEMCHECK + CTEST_CUSTOM_PRE_TEST + CTEST_CUSTOM_TESTS_IGNORE + CTEST_CUSTOM_WARNING_EXCEPTION + CTEST_CUSTOM_WARNING_MATCH + CTEST_CVS_CHECKOUT + CTEST_CVS_COMMAND + CTEST_CVS_UPDATE_OPTIONS + CTEST_DROP_LOCATION + CTEST_DROP_METHOD + CTEST_DROP_SITE + CTEST_DROP_SITE_CDASH + CTEST_DROP_SITE_PASSWORD + CTEST_DROP_SITE_USER + CTEST_EXTRA_COVERAGE_GLOB + CTEST_GIT_COMMAND + CTEST_GIT_INIT_SUBMODULES + CTEST_GIT_UPDATE_CUSTOM + CTEST_GIT_UPDATE_OPTIONS + CTEST_HG_COMMAND + CTEST_HG_UPDATE_OPTIONS + CTEST_LABELS_FOR_SUBPROJECTS + CTEST_MEMORYCHECK_COMMAND + CTEST_MEMORYCHECK_COMMAND_OPTIONS + CTEST_MEMORYCHECK_SANITIZER_OPTIONS + CTEST_MEMORYCHECK_SUPPRESSIONS_FILE + CTEST_MEMORYCHECK_TYPE + CTEST_NIGHTLY_START_TIME + CTEST_P4_CLIENT + CTEST_P4_COMMAND + CTEST_P4_OPTIONS + CTEST_P4_UPDATE_OPTIONS + CTEST_RESOURCE_SPEC_FILE + CTEST_RUN_CURRENT_SCRIPT + CTEST_SCP_COMMAND + CTEST_SITE + CTEST_SOURCE_DIRECTORY + CTEST_SUBMIT_URL + CTEST_SVN_COMMAND + CTEST_SVN_OPTIONS + CTEST_SVN_UPDATE_OPTIONS + CTEST_TEST_LOAD + CTEST_TEST_TIMEOUT + CTEST_TRIGGER_SITE + CTEST_UPDATE_COMMAND + CTEST_UPDATE_OPTIONS + CTEST_UPDATE_VERSION_ONLY + CTEST_UPDATE_VERSION_OVERRIDE + CTEST_USE_LAUNCHERS + CYGWIN + ENV + EXECUTABLE_OUTPUT_PATH + IOS + LIBRARY_OUTPUT_PATH + MINGW + MSVC + MSVC10 + MSVC11 + MSVC12 + MSVC14 + MSVC60 + MSVC70 + MSVC71 + MSVC80 + MSVC90 + MSVC_IDE + MSVC_TOOLSET_VERSION + MSVC_VERSION + MSYS + PROJECT_BINARY_DIR + PROJECT_DESCRIPTION + PROJECT_HOMEPAGE_URL + PROJECT_NAME + PROJECT_SOURCE_DIR + PROJECT_VERSION + PROJECT_VERSION_MAJOR + PROJECT_VERSION_MINOR + PROJECT_VERSION_PATCH + PROJECT_VERSION_TWEAK + UNIX + WIN32 + WINCE + WINDOWS_PHONE + WINDOWS_STORE + XCODE + XCODE_VERSION + + + + CONFIGURATION + ANGLE-R + COMMA + SEMICOLON + INSTALL_PREFIX + + + + 0 + 1 + CONFIG + BOOL + STREQUAL + JOIN + TARGET_NAME + INSTALL_INTERFACE + BUILD_INTERFACE + C_COMPILER_ID + CXX_COMPILER_ID + VERSION_GREATER + VERSION_LESS + VERSION_EQUAL + C_COMPILER_VERSION + CXX_COMPILER_VERSION + TARGET_FILE + TARGET_LINKER_FILE + TARGET_SONAME_FILE + TARGET_FILE_DIR + TARGET_FILE_NAME + TARGET_DIR + TARGET_LINKER_FILE_DIR + TARGET_LINKER_FILE_NAME + TARGET_SONAME_FILE_DIR + TARGET_SONAME_FILE_NAME + TARGET_PROPERTY + TARGET_OBJECTS + TARGET_POLICY + AND + OR + NOT + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/coffee.xml b/kate/part/syntax/data/coffee.xml new file mode 100644 index 00000000..1e0c49af --- /dev/null +++ b/kate/part/syntax/data/coffee.xml @@ -0,0 +1,268 @@ + + + + + + + + false + true + yes + no + on + off + undefined + null + NaN + Infinity + + + + return + break + continue + throw + for + while + until + loop + if + else + unless + switch + when + then + and + or + in + do + of + by + is + isnt + not + typeof + delete + where + super + try + catch + finally + try + catch + finally + constructor + + + + class + extends + new + instanceof + + + + case + default + function + var + void + with + const + let + enum + export + import + native + __hasProp + __extends + __slice + __bind + __indexOf + + + + Object + Number + Boolean + Array + String + RegExp + Function + Date + Math + eval + setInterval + clearInterval + setTimeout + clearTimeout + isFinite + isNaN + parseFloat + parseInt + escape + unescape + console + encodeURI + encodeURIComponent + decodeURI + decodeURIComponent + + + + window + document + navigator + location + history + screen + alert + prompt + + + + process + GLOBAL + require + exports + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/coldfusion.xml b/kate/part/syntax/data/coldfusion.xml new file mode 100644 index 00000000..9e49e4af --- /dev/null +++ b/kate/part/syntax/data/coldfusion.xml @@ -0,0 +1,732 @@ + + + + + + + + + if + else + for + in + while + do + continue + break + with + try + catch + switch + case + new + var + function + return + this + delete + true + false + void + throw + typeof + const + default + + + + + + Anchor + Applet + Area + Array + Boolean + Button + Checkbox + Date + Document + Event + FileUpload + Form + Frame + Function + Hidden + History + Image + Layer + Linke + Location + Math + Navigator + Number + Object + Option + Password + Radio + RegExp + Reset + Screen + Select + String + Submit + Text + Textarea + Window + + + + + + abs + acos + alert + anchor + apply + asin + atan + atan2 + back + blur + call + captureEvents + ceil + charAt + charCodeAt + clearInterval + clearTimeout + click + close + compile + concat + confirm + cos + disableExternalCapture + enableExternalCapture + eval + exec + exp + find + floor + focus + forward + fromCharCode + getDate + getDay + getFullYear + getHours + getMilliseconds + getMinutes + getMonth + getSeconds + getSelection + getTime + getTimezoneOffset + getUTCDate + getUTCDay + getUTCFullYear + getUTCHours + getUTCMilliseconds + getUTCMinutes + getUTCMonth + getUTCSeconds + go + handleEvent + home + indexOf + javaEnabled + join + lastIndexOf + link + load + log + match + max + min + moveAbove + moveBelow + moveBy + moveTo + moveToAbsolute + open + parse + plugins.refresh + pop + pow + preference + print + prompt + push + random + releaseEvents + reload + replace + reset + resizeBy + resizeTo + reverse + round + routeEvent + scrollBy + scrollTo + search + select + setDate + setFullYear + setHours + setInterval + setMilliseconds + setMinutes + setMonth + setSeconds + setTime + setTimeout + setUTCDate + setUTCFullYear + setUTCHours + setUTCMilliseconds + setUTCMinutes + setUTCMonth + setUTCSeconds + shift + sin + slice + sort + splice + split + sqrt + stop + String formatting + submit + substr + substring + taintEnabled + tan + test + toLocaleString + toLowerCase + toSource + toString + toUpperCase + toUTCString + unshift + unwatch + UTC + valueOf + watch + write + writeln + + + + + + break + case + catch + continue + default + do + else + for + function + if + in + return + switch + try + var + while + + + + + + Abs + ACos + ArrayAppend + ArrayAvg + ArrayClear + ArrayDeleteAt + ArrayInsertAt + ArrayIsEmpty + ArrayLen + ArrayMax + ArrayMin + ArrayNew + ArrayPrepend + ArrayResize + ArraySet + ArraySort + ArraySum + ArraySwap + ArrayToList + Asc + ASin + Atn + BitAnd + BitMaskClear + BitMaskRead + BitMaskSet + BitNot + BitOr + BitSHLN + BitSHRN + BitXor + Ceiling + Chr + CJustify + Compare + CompareNoCase + Cos + CreateDate + CreateDateTime + CreateObject + CreateODBCDate + CreateODBCDateTime + CreateODBCTime + CreateTime + CreateTimeSpan + CreateUUID + DateAdd + DateCompare + DateConvert + DateDiff + DateFormat + DatePart + Day + DayOfWeek + DayOfWeekAsString + DayOfYear + DaysInMonth + DaysInYear + DE + DecimalFormat + DecrementValue + Decrypt + DeleteClientVariable + DirectoryExists + DollarFormat + Duplicate + Encrypt + Evaluate + Exp + ExpandPath + FileExists + Find + FindNoCase + FindOneOf + FirstDayOfMonth + Fix + FormatBaseN + GetAuthUser + GetBaseTagData + GetBaseTagList + GetBaseTemplatePath + GetClientVariablesList + GetCurrentTemplatePath + GetDirectoryFromPath + GetException + GetFileFromPath + GetFunctionList + GetHttpRequestData + GetHttpTimeString + GetK2ServerDocCount + GetK2ServerDocCountLimit + GetLocale + GetMetaData + GetMetricData + GetPageContext + GetProfileSections + GetProfileString + GetServiceSettings + GetTempDirectory + GetTempFile + GetTemplatePath + GetTickCount + GetTimeZoneInfo + GetToken + Hash + Hour + HTMLCodeFormat + HTMLEditFormat + IIf + IncrementValue + InputBaseN + Insert + Int + IsArray + IsBinary + IsBoolean + IsCustomFunction + IsDate + IsDebugMode + IsDefined + IsK2ServerABroker + IsK2ServerDocCountExceeded + IsK2ServerOnline + IsLeapYear + IsNumeric + IsNumericDate + IsObject + IsQuery + IsSimpleValue + IsStruct + IsUserInRole + IsWDDX + IsXmlDoc + IsXmlElement + IsXmlRoot + JavaCast + JSStringFormat + LCase + Left + Len + ListAppend + ListChangeDelims + ListContains + ListContainsNoCase + ListDeleteAt + ListFind + ListFindNoCase + ListFirst + ListGetAt + ListInsertAt + ListLast + ListLen + ListPrepend + ListQualify + ListRest + ListSetAt + ListSort + ListToArray + ListValueCount + ListValueCountNoCase + LJustify + Log + Log10 + LSCurrencyFormat + LSDateFormat + LSEuroCurrencyFormat + LSIsCurrency + LSIsDate + LSIsNumeric + LSNumberFormat + LSParseCurrency + LSParseDateTime + LSParseEuroCurrency + LSParseNumber + LSTimeFormat + LTrim + Max + Mid + Min + Minute + Month + MonthAsString + Now + NumberFormat + ParagraphFormat + ParameterExists + ParseDateTime + Pi + PreserveSingleQuotes + Quarter + QueryAddColumn + QueryAddRow + QueryNew + QuerySetCell + QuotedValueList + Rand + Randomize + RandRange + REFind + REFindNoCase + RemoveChars + RepeatString + Replace + ReplaceList + ReplaceNoCase + REReplace + REReplaceNoCase + Reverse + Right + RJustify + Round + RTrim + Second + SetEncoding + SetLocale + SetProfileString + SetVariable + Sgn + Sin + SpanExcluding + SpanIncluding + Sqr + StripCR + StructAppend + StructClear + StructCopy + StructCount + StructDelete + StructFind + StructFindKey + StructFindValue + StructGet + StructInsert + StructIsEmpty + StructKeyArray + StructKeyExists + StructKeyList + StructNew + StructSort + StructUpdate + Tan + TimeFormat + ToBase64 + ToBinary + ToString + Trim + UCase + URLDecode + URLEncodedFormat + URLSessionFormat + Val + ValueList + Week + WriteOutput + XmlChildPos + XmlElemNew + XmlFormat + XmlNew + XmlParse + XmlSearch + XmlTransform + Year + YesNoFormat + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/commonlisp.xml b/kate/part/syntax/data/commonlisp.xml new file mode 100644 index 00000000..563e5600 --- /dev/null +++ b/kate/part/syntax/data/commonlisp.xml @@ -0,0 +1,1200 @@ + + + + + + + < + <= + = + > + >= + => + - + / + /= + // + /// + * + ** + *** + + + ++ + +++ + 1- + 1+ + + + defclass + defconstant + defgeneric + define-compiler-macro + define-condition + define-method-combination + define-modify-macro + define-setf-expander + define-setf-method + define-symbol-macro + defmacro + defmethod + defpackage + defparameter + defsetf + deftype + defvar + defun + defstruct + + + abort + abs + access + acons + acos + acosh + add-method + adjoin + adjustable-array-p + adjust-array + allocate-instance + alpha-char-p + alphanumericp + and + append + apply + applyhook + apropos + apropos-list + aref + arithmetic-error + arithmetic-error-operands + arithmetic-error-operation + array + array-dimension + array-dimension-limit + array-dimensions + array-displacement + array-element-type + array-has-fill-pointer-p + array-in-bounds-p + arrayp + array-rank + array-rank-limit + array-row-major-index + array-total-size + array-total-size-limit + ash + asin + asinh + assert + assoc + assoc-if + assoc-if-not + atan + atanh + atom + base-char + base-string + bignum + bit + bit-and + bit-andc1 + bit-andc2 + bit-eqv + bit-ior + bit-nand + bit-nor + bit-not + bit-orc1 + bit-orc2 + bit-vector + bit-vector-p + bit-xor + block + boole + boole-1 + boole-2 + boolean + boole-and + boole-andc1 + boole-andc2 + boole-c1 + boole-c2 + boole-clr + boole-eqv + boole-ior + boole-nand + boole-nor + boole-orc1 + boole-orc2 + boole-set + boole-xor + both-case-p + boundp + break + broadcast-stream + broadcast-stream-streams + built-in-class + butlast + byte + byte-position + byte-size + caaaar + caaadr + caaar + caadar + caaddr + caadr + caar + cadaar + cadadr + cadar + caddar + cadddr + caddr + cadr + call-arguments-limit + call-method + call-next-method + capitalize + car + case + catch + ccase + cdaaar + cdaadr + cdaar + cdadar + cdaddr + cdadr + cdar + cddaar + cddadr + cddar + cdddar + cddddr + cdddr + cddr + cdr + ceiling + cell-error + cell-error-name + cerror + change-class + char + char< + char<= + char= + char> + char>= + char/= + character + characterp + char-bit + char-bits + char-bits-limit + char-code + char-code-limit + char-control-bit + char-downcase + char-equal + char-font + char-font-limit + char-greaterp + char-hyper-bit + char-int + char-lessp + char-meta-bit + char-name + char-not-equal + char-not-greaterp + char-not-lessp + char-super-bit + char-upcase + check-type + cis + class + class-name + class-of + clear-input + clear-output + close + clrhash + code-char + coerce + commonp + compilation-speed + compile + compiled-function + compiled-function-p + compile-file + compile-file-pathname + compiler-let + compiler-macro + compiler-macro-function + complement + complex + complexp + compute-applicable-methods + compute-restarts + concatenate + concatenated-stream + concatenated-stream-streams + cond + condition + conjugate + cons + consp + constantly + constantp + continue + control-error + copy-alist + copy-list + copy-pprint-dispatch + copy-readtable + copy-seq + copy-structure + copy-symbol + copy-tree + cos + cosh + count + count-if + count-if-not + ctypecase + debug + decf + declaim + declaration + declare + decode-float + decode-universal-time + delete + delete-duplicates + delete-file + delete-if + delete-if-not + delete-package + denominator + deposit-field + describe + describe-object + destructuring-bind + digit-char + digit-char-p + directory + directory-namestring + disassemble + division-by-zero + do + do* + do-all-symbols + documentation + do-exeternal-symbols + do-external-symbols + dolist + do-symbols + dotimes + double-float + double-float-epsilon + double-float-negative-epsilon + dpb + dribble + dynamic-extent + ecase + echo-stream + echo-stream-input-stream + echo-stream-output-stream + ed + eighth + elt + encode-universal-time + end-of-file + endp + enough-namestring + ensure-directories-exist + ensure-generic-function + eq + eql + equal + equalp + error + etypecase + eval + evalhook + eval-when + evenp + every + exp + export + expt + extended-char + fboundp + fceiling + fdefinition + ffloor + fifth + file-author + file-error + file-error-pathname + file-length + file-namestring + file-position + file-stream + file-string-length + file-write-date + fill + fill-pointer + find + find-all-symbols + find-class + find-if + find-if-not + find-method + find-package + find-restart + find-symbol + finish-output + first + fixnum + flet + float + float-digits + floating-point-inexact + floating-point-invalid-operation + floating-point-overflow + floating-point-underflow + floatp + float-precision + float-radix + float-sign + floor + fmakunbound + force-output + format + formatter + fourth + fresh-line + fround + ftruncate + ftype + funcall + function + function-keywords + function-lambda-expression + functionp + gbitp + gcd + generic-function + gensym + gentemp + get + get-decoded-time + get-dispatch-macro-character + getf + gethash + get-internal-real-time + get-internal-run-time + get-macro-character + get-output-stream-string + get-properties + get-setf-expansion + get-setf-method + get-universal-time + go + graphic-char-p + handler-bind + handler-case + hash-table + hash-table-count + hash-table-p + hash-table-rehash-size + hash-table-rehash-threshold + hash-table-size + hash-table-test + host-namestring + identity + if + if-exists + ignorable + ignore + ignore-errors + imagpart + import + incf + initialize-instance + inline + in-package + in-package + input-stream-p + inspect + int-char + integer + integer-decode-float + integer-length + integerp + interactive-stream-p + intern + internal-time-units-per-second + intersection + invalid-method-error + invoke-debugger + invoke-restart + invoke-restart-interactively + isqrt + keyword + keywordp + labels + lambda + lambda-list-keywords + lambda-parameters-limit + last + lcm + ldb + ldb-test + ldiff + least-negative-double-float + least-negative-long-float + least-negative-normalized-double-float + least-negative-normalized-long-float + least-negative-normalized-short-float + least-negative-normalized-single-float + least-negative-short-float + least-negative-single-float + least-positive-double-float + least-positive-long-float + least-positive-normalized-double-float + least-positive-normalized-long-float + least-positive-normalized-short-float + least-positive-normalized-single-float + least-positive-short-float + least-positive-single-float + length + let + let* + lisp + lisp-implementation-type + lisp-implementation-version + list + list* + list-all-packages + listen + list-length + listp + load + load-logical-pathname-translations + load-time-value + locally + log + logand + logandc1 + logandc2 + logbitp + logcount + logeqv + logical-pathname + logical-pathname-translations + logior + lognand + lognor + lognot + logorc1 + logorc2 + logtest + logxor + long-float + long-float-epsilon + long-float-negative-epsilon + long-site-name + loop + loop-finish + lower-case-p + machine-instance + machine-type + machine-version + macroexpand + macroexpand-1 + macroexpand-l + macro-function + macrolet + make-array + make-array + make-broadcast-stream + make-char + make-concatenated-stream + make-condition + make-dispatch-macro-character + make-echo-stream + make-hash-table + make-instance + make-instances-obsolete + make-list + make-load-form + make-load-form-saving-slots + make-method + make-package + make-pathname + make-random-state + make-sequence + make-string + make-string-input-stream + make-string-output-stream + make-symbol + make-synonym-stream + make-two-way-stream + makunbound + map + mapc + mapcan + mapcar + mapcon + maphash + map-into + mapl + maplist + mask-field + max + member + member-if + member-if-not + merge + merge-pathname + merge-pathnames + method + method-combination + method-combination-error + method-qualifiers + min + minusp + mismatch + mod + most-negative-double-float + most-negative-fixnum + most-negative-long-float + most-negative-short-float + most-negative-single-float + most-positive-double-float + most-positive-fixnum + most-positive-long-float + most-positive-short-float + most-positive-single-float + muffle-warning + multiple-value-bind + multiple-value-call + multiple-value-list + multiple-value-prog1 + multiple-value-seteq + multiple-value-setq + multiple-values-limit + name-char + namestring + nbutlast + nconc + next-method-p + nil + nintersection + ninth + no-applicable-method + no-next-method + not + notany + notevery + notinline + nreconc + nreverse + nset-difference + nset-exclusive-or + nstring + nstring-capitalize + nstring-downcase + nstring-upcase + nsublis + nsubst + nsubst-if + nsubst-if-not + nsubstitute + nsubstitute-if + nsubstitute-if-not + nth + nthcdr + nth-value + null + number + numberp + numerator + nunion + oddp + open + open-stream-p + optimize + or + otherwise + output-stream-p + package + package-error + package-error-package + package-name + package-nicknames + packagep + package-shadowing-symbols + package-used-by-list + package-use-list + pairlis + parse-error + parse-integer + parse-namestring + pathname + pathname-device + pathname-directory + pathname-host + pathname-match-p + pathname-name + pathnamep + pathname-type + pathname-version + peek-char + phase + pi + plusp + pop + position + position-if + position-if-not + pprint + pprint-dispatch + pprint-exit-if-list-exhausted + pprint-fill + pprint-indent + pprint-linear + pprint-logical-block + pprint-newline + pprint-pop + pprint-tab + pprint-tabular + prin1 + prin1-to-string + princ + princ-to-string + print + print-not-readable + print-not-readable-object + print-object + print-unreadable-object + probe-file + proclaim + prog + prog* + prog1 + prog2 + progn + program-error + progv + provide + psetf + psetq + push + pushnew + putprop + quote + random + random-state + random-state-p + rassoc + rassoc-if + rassoc-if-not + ratio + rational + rationalize + rationalp + read + read-byte + read-char + read-char-no-hang + read-delimited-list + reader-error + read-eval-print + read-from-string + read-line + read-preserving-whitespace + read-sequence + readtable + readtable-case + readtablep + real + realp + realpart + reduce + reinitialize-instance + rem + remf + remhash + remove + remove-duplicates + remove-if + remove-if-not + remove-method + remprop + rename-file + rename-package + replace + require + rest + restart + restart-bind + restart-case + restart-name + return + return-from + revappend + reverse + room + rotatef + round + row-major-aref + rplaca + rplacd + safety + satisfies + sbit + scale-float + schar + search + second + sequence + serious-condition + set + set-char-bit + set-difference + set-dispatch-macro-character + set-exclusive-or + setf + set-macro-character + set-pprint-dispatch + setq + set-syntax-from-char + seventh + shadow + shadowing-import + shared-initialize + shiftf + short-float + short-float-epsilon + short-float-negative-epsilon + short-site-name + signal + signed-byte + signum + simle-condition + simple-array + simple-base-string + simple-bit-vector + simple-bit-vector-p + simple-condition-format-arguments + simple-condition-format-control + simple-error + simple-string + simple-string-p + simple-type-error + simple-vector + simple-vector-p + simple-warning + sin + single-flaot-epsilon + single-float + single-float-epsilon + single-float-negative-epsilon + sinh + sixth + sleep + slot-boundp + slot-exists-p + slot-makunbound + slot-missing + slot-unbound + slot-value + software-type + software-version + some + sort + space + special + special-form-p + special-operator-p + speed + sqrt + stable-sort + standard + standard-char + standard-char-p + standard-class + standard-generic-function + standard-method + standard-object + step + storage-condition + store-value + stream + stream-element-type + stream-error + stream-error-stream + stream-external-format + streamp + streamup + string + string< + string<= + string= + string> + string>= + string/= + string-capitalize + string-char + string-char-p + string-downcase + string-equal + string-greaterp + string-left-trim + string-lessp + string-not-equal + string-not-greaterp + string-not-lessp + stringp + string-right-strim + string-right-trim + string-stream + string-trim + string-upcase + structure + structure-class + structure-object + style-warning + sublim + sublis + subseq + subsetp + subst + subst-if + subst-if-not + substitute + substitute-if + substitute-if-not + subtypep + svref + sxhash + symbol + symbol-function + symbol-macrolet + symbol-name + symbolp + symbol-package + symbol-plist + symbol-value + synonym-stream + synonym-stream-symbol + sys + system + t + tagbody + tailp + tan + tanh + tenth + terpri + the + third + throw + time + trace + translate-logical-pathname + translate-pathname + tree-equal + truename + truncase + truncate + two-way-stream + two-way-stream-input-stream + two-way-stream-output-stream + type + typecase + type-error + type-error-datum + type-error-expected-type + type-of + typep + unbound-slot + unbound-slot-instance + unbound-variable + undefined-function + unexport + unintern + union + unless + unread + unread-char + unsigned-byte + untrace + unuse-package + unwind-protect + update-instance-for-different-class + update-instance-for-redefined-class + upgraded-array-element-type + upgraded-complex-part-type + upper-case-p + use-package + user + user-homedir-pathname + use-value + values + values-list + vector + vectorp + vector-pop + vector-push + vector-push-extend + warn + warning + when + wild-pathname-p + with-accessors + with-compilation-unit + with-condition-restarts + with-hash-table-iterator + with-input-from-string + with-open-file + with-open-stream + with-output-to-string + with-package-iterator + with-simple-restart + with-slots + with-standard-io-syntax + write + write-byte + write-char + write-line + write-sequence + write-string + write-to-string + yes-or-no-p + y-or-n-p + zerop + + + + :abort + :adjustable + :append + :array + :base + :case + :circle + :conc-name + :constructor + :copier + :count + :create + :default + :defaults + :device + :direction + :directory + :displaced-index-offset + :displaced-to + :element-type + :end1 + :end2 + :end + :error + :escape + :external + :from-end + :gensym + :host + :if-does-not-exist:pretty + :if-exists:print + :include:print-function + :index + :inherited + :initial-contents + :initial-element + :initial-offset + :initial-value + :input + :internal:size + :io + :junk-allowed + :key + :length + :level + :named + :name + :new-version + :nicknames + :output-file + :output + :overwrite + :predicate + :preserve-whitespace + :probe + :radix + :read-only + :rehash-size + :rehash-threshold + :rename-and-delete + :rename + :start1 + :start2 + :start + :stream + :supersede + :test + :test-not + :type + :use + :verbose + :version + + + *applyhook* + *break-on-signals* + *break-on-signals* + *break-on-warnings* + *compile-file-pathname* + *compile-file-pathname* + *compile-file-truename* + *compile-file-truename* + *compile-print* + *compile-verbose* + *compile-verbose* + *debugger-hook* + *debug-io* + *default-pathname-defaults* + *error-output* + *evalhook* + *features* + *gensym-counter* + *load-pathname* + *load-print* + *load-truename* + *load-verbose* + *macroexpand-hook* + *modules* + *package* + *print-array* + *print-base* + *print-case* + *print-circle* + *print-escape* + *print-gensym* + *print-length* + *print-level* + *print-lines* + *print-miser-width* + *print-miser-width* + *print-pprint-dispatch* + *print-pprint-dispatch* + *print-pretty* + *print-radix* + *print-readably* + *print-right-margin* + *print-right-margin* + *query-io* + *random-state* + *read-base* + *read-default-float-format* + *read-eval* + *read-suppress* + *readtable* + *standard-input* + *standard-output* + *terminal-io* + *trace-output* + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/component-pascal.xml b/kate/part/syntax/data/component-pascal.xml new file mode 100644 index 00000000..09e70011 --- /dev/null +++ b/kate/part/syntax/data/component-pascal.xml @@ -0,0 +1,172 @@ + + + + + + + + + + + + + + + + BEGIN + BY + CASE + CLOSE + CONST + DO + ELSE + ELSIF + END + FOR + IF + IMPORT + LOOP + MODULE + NEW + OF + OUT + PROCEDURE + REPEAT + THEN + TO + TYPE + UNTIL + VAR + WHILE + WITH + + + ASSERT + EXIT + HALT + RETURN + + + ANYPTR + ANYREC + ARRAY + BOOLEAN + SHORTCHAR + CHAR + BYTE + SHORTINT + INTEGER + LONGINT + POINTER + RECORD + SHORTREAL + REAL + SET + + + ABSTRACT + EMPTY + EXTENSIBLE + LIMITED + + + ABS + ASH + BITS + CAP + CHR + DEC + ENTIER + EXCL + INC + INCL + LEN + LONG + MAX + MIN + ODD + ORD + SHORT + SIZE + + + FALSE + INF + NIL + TRUE + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/context.xml b/kate/part/syntax/data/context.xml new file mode 100755 index 00000000..ebd3faaa --- /dev/null +++ b/kate/part/syntax/data/context.xml @@ -0,0 +1,132 @@ + + + + + + \part + \chapter + \section + \subsection + \subsubsection + \title + \subject + \subsubject + \subsubsubject + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/cpp.xml b/kate/part/syntax/data/cpp.xml new file mode 100644 index 00000000..855d44c6 --- /dev/null +++ b/kate/part/syntax/data/cpp.xml @@ -0,0 +1,1577 @@ + + + + +]> + + + + + + emit + signals + slots + foreach + forever + SIGNAL + SLOT + qApp + Q_DECLARE_TR_FUNCTIONS + Q_CLASSINFO + Q_EMIT + Q_ENUMS + Q_FLAGS + Q_INTERFACES + Q_INVOKABLE + Q_OBJECT + Q_PROPERTY + Q_SIGNAL + Q_SIGNALS + Q_SLOT + Q_SLOTS + Q_NULLPTR + + Q_DECLARE_METATYPE + QT_NO_CAST_FROM_BYTEARRAY + QT_NO_CAST_FROM_ASCII + QT_NO_CAST_TO_ASCII + QT_NO_URL_CAST_FROM_STRING + QT_USE_QSTRINGBUILDER + + QBENCHMARK + QBENCHMARK_ONCE + QCOMPARE + QEXPECT_FAIL + QFAIL + QFETCH + QSKIP + QTEST + QTEST_APPLESS_MAIN + QTEST_MAIN + QTEST_NOOP_MAIN + QVERIFY2 + QVERIFY + QWARN + QAXCLASS + QAXFACTORY_BEGIN + QAXFACTORY_DEFAULT + QAXFACTORY_END + QAXFACTORY_EXPORT + QAXTYPE + QDESIGNER_WIDGET_EXPORT + QML_DECLARE_TYPE + QML_DECLARE_TYPEINFO + QTWEBKIT_VERSION + QTWEBKIT_VERSION_CHECK + QTWEBKIT_VERSION_STR + Q_ARG + Q_RETURN_ARG + Q_ATOMIC_INT_FETCH_AND_ADD_IS_ALWAYS_NATIVE + Q_ATOMIC_INT_FETCH_AND_ADD_IS_NOT_NATIVE + Q_ATOMIC_INT_FETCH_AND_ADD_IS_SOMETIMES_NATIVE + Q_ATOMIC_INT_FETCH_AND_ADD_IS_WAIT_FREE + Q_ATOMIC_INT_FETCH_AND_STORE_IS_ALWAYS_NATIVE + Q_ATOMIC_INT_FETCH_AND_STORE_IS_NOT_NATIVE + Q_ATOMIC_INT_FETCH_AND_STORE_IS_SOMETIMES_NATIVE + Q_ATOMIC_INT_FETCH_AND_STORE_IS_WAIT_FREE + Q_ATOMIC_INT_REFERENCE_COUNTING_IS_ALWAYS_NATIVE + Q_ATOMIC_INT_REFERENCE_COUNTING_IS_NOT_NATIVE + Q_ATOMIC_INT_REFERENCE_COUNTING_IS_SOMETIMES_NATIVE + Q_ATOMIC_INT_REFERENCE_COUNTING_IS_WAIT_FREE + Q_ATOMIC_INT_TEST_AND_SET_IS_ALWAYS_NATIVE + Q_ATOMIC_INT_TEST_AND_SET_IS_NOT_NATIVE + Q_ATOMIC_INT_TEST_AND_SET_IS_SOMETIMES_NATIVE + Q_ATOMIC_INT_TEST_AND_SET_IS_WAIT_FREE + Q_DECLARE_EXTENSION_INTERFACE + Q_DECLARE_INTERFACE + Q_EXPORT_PLUGIN2 + Q_IMPORT_PLUGIN + Q_NOREPLY + Q_SCRIPT_DECLARE_QMETAOBJECT + QWIDGETSIZE_MAX + + Q_ALIGNOF + Q_ASSERT + Q_ASSERT_X + Q_UNREACHABLE + Q_BIG_ENDIAN + Q_BROKEN_DEBUG_STREAM + Q_BROKEN_TEMPLATE_SPECIALIZATION + Q_BYTE_ORDER + Q_CANNOT_DELETE_CONSTANT + Q_C_CALLBACKS + Q_CC_BOR + Q_CC_CDS + Q_CC_CLANG + Q_CC_COMEAU + Q_CC_DEC + Q_CC_DIAB + Q_CC_EDG + Q_CC_GCCE + Q_CC_GHS + Q_CC_GNU + Q_CC_HIGHC + Q_CC_HPACC + Q_CC_HP + Q_CC_INTEL + Q_CC_KAI + Q_CC_MINGW + Q_CC_MIPS + Q_CC_MSVC + Q_CC_MSVC_NET + Q_CC_MWERKS + Q_CC_NOKIAX86 + Q_CC_OC + Q_CC_PGI + Q_CC_RVCT + Q_CC_SUN + Q_CC_SYM + Q_CC_USLC + Q_CC_WAT + Q_CC_XLC + Q_CHECK_PTR + Q_CLEANUP_RESOURCE + Q_COMPILER_AUTO_FUNCTION + Q_COMPILER_AUTO_TYPE + Q_COMPILER_CLASS_ENUM + Q_COMPILER_CONSTEXPR + Q_COMPILER_DECLTYPE + Q_COMPILER_DEFAULT_DELETE_MEMBERS + Q_COMPILER_EXTERN_TEMPLATES + Q_COMPILER_INITIALIZER_LISTS + Q_COMPILER_LAMBDA + Q_COMPILER_MANGLES_RETURN_TYPE + Q_COMPILER_RVALUE_REFS + Q_COMPILER_UNICODE_STRINGS + Q_COMPILER_VARIADIC_TEMPLATES + Q_CONSTRUCTOR_FUNCTION0 + Q_CONSTRUCTOR_FUNCTION + Q_DECL_ALIGN + Q_DECLARE_FLAGS + Q_DECLARE_INCOMPATIBLE_FLAGS + Q_DECLARE_OPERATORS_FOR_FLAGS + Q_DECLARE_PRIVATE_D + Q_DECLARE_PRIVATE + Q_DECLARE_PUBLIC + Q_DECLARE_SHARED + Q_DECLARE_SHARED_STL + Q_DECLARE_TYPEINFO_BODY + Q_DECLARE_TYPEINFO + Q_DECL_CONSTEXPR + Q_DECL_CONSTRUCTOR_DEPRECATED + Q_DECL_DEPRECATED + Q_DECL_HIDDEN + Q_DECL_IMPORT + Q_DECL_EXPORT + Q_DECL_VARIABLE_DEPRECATED + Q_DESTRUCTOR_FUNCTION0 + Q_DESTRUCTOR_FUNCTION + Q_DISABLE_COPY + Q_D + Q_DUMMY_COMPARISON_OPERATOR + Q_FOREACH + Q_FOREVER + Q_FULL_TEMPLATE_INSTANTIATION + Q_FUNC_INFO + Q_GLOBAL_STATIC_INIT + Q_GLOBAL_STATIC + Q_GLOBAL_STATIC_WITH_ARGS + Q_GLOBAL_STATIC_WITH_INITIALIZER + Q_INIT_RESOURCE_EXTERN + Q_INIT_RESOURCE + Q_INLINE_TEMPLATE + Q_INT64_C + Q_LIKELY + Q_LITTLE_ENDIAN + Q_NO_BOOL_TYPE + Q_NO_DATA_RELOCATION + Q_NO_DECLARED_NOT_DEFINED + Q_NO_DEPRECATED_CONSTRUCTORS + Q_NO_EXPLICIT_KEYWORD + Q_NO_PACKED_REFERENCE + Q_NO_POSIX_SIGNALS + Q_NO_TEMPLATE_FRIENDS + Q_NO_USING_KEYWORD + Q_OF_ELF + Q_OS_AIX + Q_OS_BSD4 + Q_OS_BSDI + Q_OS_CYGWIN + Q_OS_DARWIN32 + Q_OS_DARWIN64 + Q_OS_DARWIN + Q_OS_DGUX + Q_OS_DYNIX + Q_OS_FREEBSD + Q_OS_HPUX + Q_OS_HURD + Q_OS_INTEGRITY + Q_OS_IRIX + Q_OS_LINUX + Q_OS_LYNX + Q_OS_MAC32 + Q_OS_MAC64 + Q_OS_MAC + Q_OS_MACX + Q_OS_MSDOS + Q_OS_NACL + Q_OS_NETBSD + Q_OS_OPENBSD + Q_OS_OS2EMX + Q_OS_OS2 + Q_OS_OSF + Q_OS_QNX + Q_OS_RELIANT + Q_OS_SCO + Q_OS_SOLARIS + Q_OS_SYMBIAN + Q_OS_ULTRIX + Q_OS_UNIX + Q_OS_UNIXWARE + Q_OS_VXWORKS + Q_OS_WIN32 + Q_OS_WIN64 + Q_OS_WINCE + Q_OS_WIN + Q_OUTOFLINE_TEMPLATE + Q_PACKED + Q_Q + Q_REQUIRED_RESULT + Q_SYMBIAN_FIXED_POINTER_CURSORS + Q_SYMBIAN_HAS_EXTENDED_BITMAP_TYPE + Q_SYMBIAN_SEMITRANSPARENT_BG_SURFACE + Q_SYMBIAN_SUPPORTS_FIXNATIVEORIENTATION + Q_SYMBIAN_SUPPORTS_MULTIPLE_SCREENS + Q_SYMBIAN_SUPPORTS_SURFACES + Q_SYMBIAN_TRANSITION_EFFECTS + Q_SYMBIAN_WINDOW_SIZE_CACHE + QT_ARCH_I386 + QT_ASCII_CAST_WARN_CONSTRUCTOR + QT_ASCII_CAST_WARN + QT_BUILD_KEY_COMPAT + QT_BUILD_KEY + QT_CATCH + QT_COMPAT + QT_COMPAT_WARNINGS + QT_DEBUG + QT_DEPRECATED_CONSTRUCTOR + QT_DEPRECATED + QT_DEPRECATED_VARIABLE + QT_EDITION_ACADEMIC + QT_EDITION_CONSOLE + QT_EDITION_DESKTOP + QT_EDITION_DESKTOPLIGHT + QT_EDITION_EDUCATIONAL + QT_EDITION_EVALUATION + QT_EDITION + QT_EDITION_OPENSOURCE + QT_EDITION_UNIVERSAL + Q_TEMPLATEDLL + QT_ENSURE_STACK_ALIGNED_FOR_SSE + QT_FASTCALL + QT_FORWARD_DECLARE_CLASS + QT_FORWARD_DECLARE_STRUCT + QT_GRAPHICSSYSTEM_RASTER + QT_HAVE_ARMV6 + QT_LARGEFILE_SUPPORT + QT_LICENSED_MODULE + QT_LINKED_OPENSSL + QT_LINUXBASE + QT_MAC_USE_COCOA + QT_MOC_COMPAT + QT_MODULE_ACTIVEQT + QT_MODULE_CORE + QT_MODULE_DBUS + QT_MODULE_DECLARATIVE + QT_MODULE_GRAPHICSVIEW + QT_MODULE_GUI + QT_MODULE_HELP + QT_MODULE + QT_MODULE_MULTIMEDIA + QT_MODULE_NETWORK + QT_MODULE_OPENGL + QT_MODULE_OPENVG + QT_MODULE_QT3SUPPORT + QT_MODULE_QT3SUPPORTLIGHT + QT_MODULE_SCRIPT + QT_MODULE_SCRIPTTOOLS + QT_MODULE_SQL + QT_MODULE_SVG + QT_MODULE_TEST + QT_MODULE_XML + QT_MODULE_XMLPATTERNS + QT_NO_ACCESSIBILITY + QT_NO_ANIMATION + QT_NO_ARM_EABI + QT_NO_BEARERMANAGEMENT + QT_NO_BUTTONGROUP + QT_NO_CALENDARWIDGET + QT_NO_CLIPBOARD + QT_NO_CODECS + QT_NO_COLORDIALOG + QT_NO_COLUMNVIEW + QT_NO_COMBOBOX + QT_NO_COMPLETER + QT_NO_CONCURRENT_FILTER + QT_NO_CONCURRENT + QT_NO_CONCURRENT_MAP + QT_NO_CONTEXTMENU + QT_NO_COP + QT_NO_CRASHHANDLER + QT_NO_CUPS + QT_NO_DATAWIDGETMAPPER + QT_NO_DATESTRING + QT_NO_DATETIMEEDIT + QT_NO_DBUS + QT_NO_DEBUG + QT_NO_DEBUG_STREAM + QT_NO_DIAL + QT_NO_DIRMODEL + QT_NO_DOCKWIDGET + QT_NO_DRAGANDDROP + QT_NO_EGL + QT_NO_ERRORMESSAGE + QT_NO_EXCEPTIONS + QT_NO_FILEDIALOG + QT_NO_FILESYSTEMMODEL + QT_NO_FONTCOMBOBOX + QT_NO_FONTDIALOG + QT_NO_FPU + QT_NO_FSCOMPLETER + QT_NO_FTP + QT_NO_GETIFADDRS + QT_NO_GRAPHICSEFFECT + QT_NO_GRAPHICSSVGITEM + QT_NO_GRAPHICSVIEW + QT_NO_GSTREAMER + QT_NO_HOSTINFO + QT_NO_HTTP + QT_NO_ICD + QT_NO_IMAGEFORMAT_JPEG + QT_NO_IMAGEFORMAT_MNG + QT_NO_IMAGEFORMAT_TIFF + QT_NO_IMAGEFORMAT_XPM + QT_NO_IM + QT_NO_INPUTDIALOG + QT_NO_ITEMVIEWS + QT_NO_LIBRARY + QT_NO_LISTVIEW + QT_NO_LISTWIDGET + QT_NO_LPR + QT_NO_MAINWINDOW + QT_NO_MDIAREA + QT_NO_MENUBAR + QT_NO_MENU + QT_NO_NAS + QT_NO_NETWORKDISKCACHE + QT_NO_OPENVG + QT_NO_PAINT_DEBUG + QT_NO_PHONON_EFFECTWIDGET + QT_NO_PHONON + QT_NO_PHONON_PLATFORMPLUGIN + QT_NO_PHONON_SEEKSLIDER + QT_NO_PHONON_SETTINGSGROUP + QT_NO_PHONON_VIDEOPLAYER + QT_NO_PHONON_VOLUMEFADEREFFECT + QT_NO_PHONON_VOLUMESLIDER + QT_NO_PRINTDIALOG + QT_NO_PRINTER + QT_NO_PRINTPREVIEWDIALOG + QT_NO_PRINTPREVIEWWIDGET + QT_NO_PROCESS + QT_NO_PROGRESSDIALOG + QT_NO_PROXYMODEL + QT_NO_PULSEAUDIO + QT_NO_QDEBUG_MACRO + QT_NO_QFUTURE + QT_NO_QWARNING_MACRO + QT_NO_QWS_CURSOR + QT_NO_QWS_DECORATION_STYLED + QT_NO_QWS_DECORATION_WINDOWS + QT_NO_QWS_DYNAMICSCREENTRANSFORMATION + QT_NO_QWS_INPUTMETHODS + QT_NO_QWS_MANAGER + QT_NO_QWS_MULTIPROCESS + QT_NO_QWS_SOUNDSERVER + QT_NO_QWS_TRANSFORMED + QT_NO_QWS_VNC + QT_NO_RAWFONT + QT_NO_S60 + QT_NO_SCROLLAREA + QT_NO_SCROLLBAR + QT_NO_SHAREDMEMORY + QT_NO_SOCKS5 + QT_NO_SOFTKEYMANAGER + QT_NO_SORTFILTERPROXYMODEL + QT_NO_SPINBOX + QT_NO_SPLITTER + QT_NO_STANDARDITEMMODEL + QT_NO_STATEMACHINE + QT_NO_STL_WCHAR + QT_NO_STRINGLISTMODEL + QT_NO_STYLE_CDE + QT_NO_STYLE_CLEANLOOKS + QT_NO_STYLE_PLASTIQUE + QT_NO_STYLE_S60 + QT_NO_STYLE_STYLESHEET + QT_NO_STYLE_WINDOWSCE + QT_NO_STYLE_WINDOWSMOBILE + QT_NO_STYLE_WINDOWSVISTA + QT_NO_STYLE_WINDOWSXP + QT_NO_SVGGENERATOR + QT_NO_SVG + QT_NO_SVGRENDERER + QT_NO_SVGWIDGET + QT_NO_SXE + QT_NO_SYNTAXHIGHLIGHTER + QT_NO_SYSTEMSEMAPHORE + QT_NO_TABBAR + QT_NO_TABDIALOG + QT_NO_TABLEVIEW + QT_NO_TABLEWIDGET + QT_NO_TABWIDGET + QT_NO_TEMPLATE_TEMPLATE_PARAMETERS + QT_NO_TEXTBROWSER + QT_NO_TEXTCODECPLUGIN + QT_NO_TEXTEDIT + QT_NO_TEXTODFWRITER + QT_NO_TOOLBAR + QT_NO_TOOLBOX + QT_NO_TOOLBUTTON + QT_NO_TRANSLATION_UTF8 + QT_NO_TREEVIEW + QT_NO_TREEWIDGET + QT_NO_UNDOGROUP + QT_NO_UNDOSTACK + QT_NO_UNDOVIEW + QT_NO_WARNINGS + QT_NO_WEBKIT + QT_NO_WHATSTHIS + QT_NO_WIN_ACTIVEQT + QT_NO_WIZARD + QT_NO_WORKSPACE + QT_NO_XMLSTREAMREADER + QT_NO_XMLSTREAMWRITER + QT_NO_ZLIB + QT_PACKAGEDATE_STR + QT_PACKAGE_TAG + QT_POINTER_SIZE + QT_PREPEND_NAMESPACE + QT_PRODUCT_LICENSEE + QT_PRODUCT_LICENSE + QT_RETHROW + QT_RUNTIME_XCURSOR + QT_RUNTIME_XFIXES + QT_RUNTIME_XINERAMA + QT_RUNTIME_XINPUT + QT_RUNTIME_XRANDR + QT_STATIC_CONST_IMPL + QT_STATIC_CONST + QT_STRINGIFY2 + QT_STRINGIFY + QT_SUPPORTS + QT_SYMBIAN_SUPPORTS_ADVANCED_POINTER + QT_SYMBIAN_SUPPORTS_SGIMAGE + QT_THROW + QT_TRANSLATE_NOOP3 + QT_TRANSLATE_NOOP3_UTF8 + QT_TRANSLATE_NOOP + QT_TRANSLATE_NOOP_UTF8 + QT_TRAP_THROWING + QT_TRID_NOOP + QT_TR_NOOP + QT_TR_NOOP_UTF8 + QT_TRYCATCH_ERROR + QT_TRYCATCH_LEAVING + QT_TRY + QT_USE_MATH_H_FLOATS + QT_USE_NAMESPACE + QT_VERSION_CHECK + QT_VERSION + QT_VERSION_STR + QT_VISIBILITY_AVAILABLE + QT_WA_INLINE + QT_WA + QT_WIN_CALLBACK + Q_TYPENAME + Q_TYPEOF + Q_UINT64_C + Q_UNLIKELY + Q_UNUSED + Q_WRONG_SB_CTYPE_MACROS + Q_WS_MAC32 + Q_WS_MAC64 + Q_WS_MAC + Q_WS_MACX + Q_WS_PM + Q_WS_S60 + Q_WS_WIN16 + Q_WS_WIN32 + Q_WS_WIN64 + Q_WS_WINCE + Q_WS_WINCE_WM + Q_WS_WIN + Q_WS_X11 + + Q_COMPLEX_TYPE + Q_PRIMITIVE_TYPE + Q_STATIC_TYPE + Q_MOVABLE_TYPE + Q_DUMMY_TYPE + + + + + QAbstractAnimation + QAbstractButton + QAbstractEventDispatcher + QAbstractExtensionFactory + QAbstractExtensionManager + QAbstractFileEngine + QAbstractFileEngineHandler + QAbstractFileEngineIterator + QAbstractFontEngine + QAbstractFormBuilder + QAbstractGraphicsShapeItem + QAbstractItemDelegate + QAbstractItemModel + QAbstractItemView + QAbstractListModel + QAbstractMessageHandler + QAbstractNetworkCache + QAbstractPrintDialog + QAbstractProxyModel + QAbstractScrollArea + QAbstractSlider + QAbstractSocket + QAbstractSpinBox + QAbstractState + QAbstractTableModel + QAbstractTextDocumentLayout + QAbstractTransition + QAbstractUriResolver + QAbstractVideoBuffer + QAbstractVideoSurface + QAbstractXmlNodeModel + QAbstractXmlReceiver + QAccessible + QAccessibleBridge + QAccessibleBridgePlugin + QAccessibleEvent + QAccessibleInterface + QAccessibleObject + QAccessiblePlugin + QAccessibleWidget + QAction + QActionEvent + QActionGroup + QAnimationGroup + QApplication + QAtomicInt + QAtomicPointer + QAudioDeviceInfo + QAudioFormat + QAudioInput + QAudioOutput + QAuthenticator + QAxAggregated + QAxBase + QAxBindable + QAxFactory + QAxObject + QAxScript + QAxScriptEngine + QAxScriptManager + QAxWidget + QBasicTimer + QBitArray + QBitmap + QBoxLayout + QBrush + QBuffer + QButtonGroup + QByteArray + QByteArrayMatcher + QCDEStyle + QCache + QCalendarWidget + QChar + QCheckBox + QChildEvent + QCleanlooksStyle + QClipboard + QCloseEvent + QColor + QColorDialog + QColorGroup + QColormap + QColumnView + QComboBox + QCommandLinkButton + QCommonStyle + QCompleter + QConicalGradient + QConstString + QContextMenuEvent + QContiguousCache + QCopChannel + QCoreApplication + QCryptographicHash + QCursor + QCustomEvent + QCustomRasterPaintDevice + QDBusAbstractAdaptor + QDBusAbstractInterface + QDBusArgument + QDBusConnection + QDBusConnectionInterface + QDBusContext + QDBusError + QDBusInterface + QDBusMessage + QDBusObjectPath + QDBusPendingCall + QDBusPendingCallWatcher + QDBusPendingReply + QDBusReply + QDBusServiceWatcher + QDBusSignature + QDBusUnixFileDescriptor + QDBusVariant + QDataStream + QDataWidgetMapper + QDate + QDateEdit + QDateTime + QDateTimeEdit + QDebug + QDeclarativeComponent + QDeclarativeContext + QDeclarativeEngine + QDeclarativeError + QDeclarativeExpression + QDeclarativeExtensionPlugin + QDeclarativeImageProvider + QDeclarativeItem + QDeclarativeListProperty + QDeclarativeNetworkAccessManagerFactory + QDeclarativeParserStatus + QDeclarativeProperty + QDeclarativePropertyMap + QDeclarativePropertyValueSource + QDeclarativeScriptString + QDeclarativeView + QDecoration + QDecorationDefault + QDecorationFactory + QDecorationPlugin + QDesignerActionEditorInterface + QDesignerContainerExtension + QDesignerCustomWidgetCollectionInterface + QDesignerCustomWidgetInterface + QDesignerDynamicPropertySheetExtension + QDesignerFormEditorInterface + QDesignerFormWindowCursorInterface + QDesignerFormWindowInterface + QDesignerFormWindowManagerInterface + QDesignerMemberSheetExtension + QDesignerObjectInspectorInterface + QDesignerPropertyEditorInterface + QDesignerPropertySheetExtension + QDesignerTaskMenuExtension + QDesignerWidgetBoxInterface + QDesktopServices + QDesktopWidget + QDial + QDialog + QDialogButtonBox + QDir + QDirIterator + QDirModel + QDirectPainter + QDockWidget + QDomAttr + QDomCDATASection + QDomCharacterData + QDomComment + QDomDocument + QDomDocumentFragment + QDomDocumentType + QDomElement + QDomEntity + QDomEntityReference + QDomImplementation + QDomNamedNodeMap + QDomNode + QDomNodeList + QDomNotation + QDomProcessingInstruction + QDomText + QDoubleSpinBox + QDoubleValidator + QDrag + QDragEnterEvent + QDragLeaveEvent + QDragMoveEvent + QDropEvent + QDynamicPropertyChangeEvent + QEasingCurve + QElapsedTimer + QErrorMessage + QEvent + QEventLoop + QEventTransition + QExplicitlySharedDataPointer + QExtensionFactory + QExtensionManager + QFSFileEngine + QFile + QFileDialog + QFileIconProvider + QFileInfo + QFileOpenEvent + QFileSystemModel + QFileSystemWatcher + QFinalState + QFlag + QFlags + QFocusEvent + QFocusFrame + QFont + QFontComboBox + QFontDatabase + QFontDialog + QFontEngineInfo + QFontEnginePlugin + QFontInfo + QFontMetrics + QFontMetricsF + QFormBuilder + QFormLayout + QFrame + QFtp + QFuture + QFutureIterator + QFutureSynchronizer + QFutureWatcher + QGLBuffer + QGLContext + QGLFormat + QGLFramebufferObject + QGLFramebufferObjectFormat + QGLFunctions + QGLPixelBuffer + QGLShader + QGLShaderProgram + QGLWidget + QGenericArgument + QGenericMatrix + QGenericPlugin + QGenericPluginFactory + QGenericReturnArgument + QGesture + QGestureEvent + QGestureRecognizer + QGlyphRun + QGradient + QGraphicsAnchor + QGraphicsAnchorLayout + QGraphicsBlurEffect + QGraphicsColorizeEffect + QGraphicsDropShadowEffect + QGraphicsEffect + QGraphicsEllipseItem + QGraphicsGridLayout + QGraphicsItem + QGraphicsItemAnimation + QGraphicsItemGroup + QGraphicsLayout + QGraphicsLayoutItem + QGraphicsLineItem + QGraphicsLinearLayout + QGraphicsObject + QGraphicsOpacityEffect + QGraphicsPathItem + QGraphicsPixmapItem + QGraphicsPolygonItem + QGraphicsProxyWidget + QGraphicsRectItem + QGraphicsRotation + QGraphicsScale + QGraphicsScene + QGraphicsSceneContextMenuEvent + QGraphicsSceneDragDropEvent + QGraphicsSceneEvent + QGraphicsSceneHelpEvent + QGraphicsSceneHoverEvent + QGraphicsSceneMouseEvent + QGraphicsSceneMoveEvent + QGraphicsSceneResizeEvent + QGraphicsSceneWheelEvent + QGraphicsSimpleTextItem + QGraphicsSvgItem + QGraphicsTextItem + QGraphicsTransform + QGraphicsView + QGraphicsWebView + QGraphicsWidget + QGridLayout + QGroupBox + QGtkStyle + QHBoxLayout + QHash + QHashIterator + QHeaderView + QHelpContentItem + QHelpContentModel + QHelpContentWidget + QHelpEngine + QHelpEngineCore + QHelpEvent + QHelpIndexModel + QHelpIndexWidget + QHelpSearchEngine + QHelpSearchQuery + QHelpSearchQueryWidget + QHelpSearchResultWidget + QHideEvent + QHistoryState + QHostAddress + QHostInfo + QHoverEvent + QHttp + QHttpHeader + QHttpMultiPart + QHttpPart + QHttpRequestHeader + QHttpResponseHeader + QIODevice + QIcon + QIconDragEvent + QIconEngine + QIconEnginePlugin + QIconEnginePluginV2 + QIconEngineV2 + QIdentityProxyModel + QImage + QImageIOHandler + QImageIOPlugin + QImageReader + QImageWriter + QInputContext + QInputContextFactory + QInputContextPlugin + QInputDialog + QInputEvent + QInputMethodEvent + QIntValidator + QItemDelegate + QItemEditorCreator + QItemEditorCreatorBase + QItemEditorFactory + QItemSelection + QItemSelectionModel + QItemSelectionRange + QKbdDriverFactory + QKbdDriverPlugin + QKeyEvent + QKeyEventTransition + QKeySequence + QLCDNumber + QLabel + QLatin1Char + QLatin1String + QLayout + QLayoutItem + QLibrary + QLibraryInfo + QLine + QLineEdit + QLineF + QLinearGradient + QLinkedList + QLinkedListIterator + QList + QListIterator + QListView + QListWidget + QListWidgetItem + QLocalServer + QLocalSocket + QLocale + QMacCocoaViewContainer + QMacNativeWidget + QMacPasteboardMime + QMacStyle + QMainWindow + QMap + QMapIterator + QMargins + QMatrix + QMatrix4x4 + QMdiArea + QMdiSubWindow + QMenu + QMenuBar + QMenuItem + QMessageBox + QMetaClassInfo + QMetaEnum + QMetaMethod + QMetaObject + QMetaProperty + QMetaType + QMimeData + QMimeSource + QModelIndex + QMotifStyle + QMouseDriverFactory + QMouseDriverPlugin + QMouseEvent + QMouseEventTransition + QMoveEvent + QMovie + QMultiHash + QMultiMap + QMutableHashIterator + QMutableLinkedListIterator + QMutableListIterator + QMutableMapIterator + QMutableSetIterator + QMutableVectorIterator + QMutex + QMutexLocker + QNetworkAccessManager + QNetworkAddressEntry + QNetworkCacheMetaData + QNetworkConfiguration + QNetworkConfigurationManager + QNetworkCookie + QNetworkCookieJar + QNetworkDiskCache + QNetworkInterface + QNetworkProxy + QNetworkProxyFactory + QNetworkProxyQuery + QNetworkReply + QNetworkRequest + QNetworkSession + QObject + QObjectCleanupHandler + QPageSetupDialog + QPaintDevice + QPaintEngine + QPaintEngineState + QPaintEvent + QPainter + QPainterPath + QPainterPathStroker + QPair + QPalette + QPanGesture + QParallelAnimationGroup + QPauseAnimation + QPen + QPersistentModelIndex + QPicture + QPictureFormatPlugin + QPictureIO + QPinchGesture + QPixmap + QPixmapCache + QPlainTextDocumentLayout + QPlainTextEdit + QPlastiqueStyle + QPlatformCursor + QPlatformCursorImage + QPlatformFontDatabase + QPlatformWindowFormat + QPluginLoader + QPoint + QPointF + QPointer + QPolygon + QPolygonF + QPrintDialog + QPrintEngine + QPrintPreviewDialog + QPrintPreviewWidget + QPrinter + QPrinterInfo + QProcess + QProcessEnvironment + QProgressBar + QProgressDialog + QPropertyAnimation + QProxyModel + QProxyScreen + QProxyScreenCursor + QProxyStyle + QPushButton + QQuaternion + QQueue + QRadialGradient + QRadioButton + QRasterPaintEngine + QRawFont + QReadLocker + QReadWriteLock + QRect + QRectF + QRegExp + QRegExpValidator + QJsonDocument + QRegion + QResizeEvent + QResource + QRubberBand + QRunnable + QS60MainAppUi + QS60MainApplication + QS60MainDocument + QS60Style + QScopedArrayPointer + QScopedPointer + QScopedValueRollback + QScreen + QScreenCursor + QScreenDriverFactory + QScreenDriverPlugin + QScriptClass + QScriptClassPropertyIterator + QScriptContext + QScriptContextInfo + QScriptEngine + QScriptEngineAgent + QScriptEngineDebugger + QScriptExtensionPlugin + QScriptProgram + QScriptString + QScriptSyntaxCheckResult + QScriptValue + QScriptValueIterator + QScriptable + QScrollArea + QScrollBar + QSemaphore + QSequentialAnimationGroup + QSessionManager + QSet + QSetIterator + QSettings + QSharedData + QSharedDataPointer + QSharedMemory + QSharedPointer + QShortcut + QShortcutEvent + QShowEvent + QSignalMapper + QSignalSpy + QSignalTransition + QSimpleXmlNodeModel + QSize + QSizeF + QSizeGrip + QSizePolicy + QSlider + QSocketNotifier + QSortFilterProxyModel + QSound + QSourceLocation + QSpacerItem + QSpinBox + QSplashScreen + QSplitter + QSplitterHandle + QSqlDatabase + QSqlDriver + QSqlDriverCreator + QSqlDriverCreatorBase + QSqlDriverPlugin + QSqlError + QSqlField + QSqlIndex + QSqlQuery + QSqlQueryModel + QSqlRecord + QSqlRelation + QSqlRelationalDelegate + QSqlRelationalTableModel + QSqlResult + QSqlTableModel + QSslCertificate + QSslCipher + QSslConfiguration + QSslError + QSslKey + QSslSocket + QStack + QStackedLayout + QStackedWidget + QStandardItem + QStandardItemEditorCreator + QStandardItemModel + QStandardPaths + QState + QStateMachine + QStaticText + QStatusBar + QStatusTipEvent + QString + QStringList + QStringListModel + QStringMatcher + QStringRef + QStyle + QStyleFactory + QStyleHintReturn + QStyleHintReturnMask + QStyleHintReturnVariant + QStyleOption + QStyleOptionButton + QStyleOptionComboBox + QStyleOptionComplex + QStyleOptionDockWidget + QStyleOptionFocusRect + QStyleOptionFrame + QStyleOptionFrameV2 + QStyleOptionFrameV3 + QStyleOptionGraphicsItem + QStyleOptionGroupBox + QStyleOptionHeader + QStyleOptionMenuItem + QStyleOptionProgressBar + QStyleOptionProgressBarV2 + QStyleOptionQ3DockWindow + QStyleOptionQ3ListView + QStyleOptionQ3ListViewItem + QStyleOptionRubberBand + QStyleOptionSizeGrip + QStyleOptionSlider + QStyleOptionSpinBox + QStyleOptionTab + QStyleOptionTabBarBase + QStyleOptionTabBarBaseV2 + QStyleOptionTabV2 + QStyleOptionTabV3 + QStyleOptionTabWidgetFrame + QStyleOptionTabWidgetFrameV2 + QStyleOptionTitleBar + QStyleOptionToolBar + QStyleOptionToolBox + QStyleOptionToolBoxV2 + QStyleOptionToolButton + QStyleOptionViewItem + QStyleOptionViewItemV2 + QStyleOptionViewItemV3 + QStyleOptionViewItemV4 + QStylePainter + QStylePlugin + QStyledItemDelegate + QSupportedWritingSystems + QSvgGenerator + QSvgRenderer + QSvgWidget + QSwipeGesture + QSymbianEvent + QSymbianGraphicsSystemHelper + QSyntaxHighlighter + QSysInfo + QSystemLocale + QSystemSemaphore + QSystemTrayIcon + QTabBar + QTabWidget + QTableView + QTableWidget + QTableWidgetItem + QTableWidgetSelectionRange + QTabletEvent + QTapAndHoldGesture + QTapGesture + QTcpServer + QTcpSocket + QTemporaryFile + QTestEventList + QTextBlock + QTextBlockFormat + QTextBlockGroup + QTextBlockUserData + QTextBoundaryFinder + QTextBrowser + QTextCharFormat + QTextCodec + QTextCodecPlugin + QTextCursor + QTextDecoder + QTextDocument + QTextDocumentFragment + QTextDocumentWriter + QTextEdit + QTextEncoder + QTextFormat + QTextFragment + QTextFrame + QTextFrameFormat + QTextIStream + QTextImageFormat + QTextInlineObject + QTextItem + QTextLayout + QTextLength + QTextLine + QTextList + QTextListFormat + QTextOStream + QTextObject + QTextObjectInterface + QTextOption + QTextStream + QTextTable + QTextTableCell + QTextTableCellFormat + QTextTableFormat + QThread + QThreadPool + QThreadStorage + QTileRules + QTime + QTimeEdit + QTimeLine + QTimer + QTimerEvent + QToolBar + QToolBox + QToolButton + QToolTip + QTouchEvent + QTransform + QTranslator + QTreeView + QTreeWidget + QTreeWidgetItem + QTreeWidgetItemIterator + QUdpSocket + QUiLoader + QUndoCommand + QUndoGroup + QUndoStack + QUndoView + QUrl + QUrlInfo + QUuid + QVBoxLayout + QValidator + QVarLengthArray + QVariant + QVariantAnimation + QVector + QVector2D + QVector3D + QVector4D + QVectorIterator + QVideoFrame + QVideoSurfaceFormat + QWSCalibratedMouseHandler + QWSClient + QWSEmbedWidget + QWSEvent + QWSGLWindowSurface + QWSInputMethod + QWSKeyboardHandler + QWSMouseHandler + QWSPointerCalibrationData + QWSScreenSaver + QWSServer + QWSWindow + QWaitCondition + QWeakPointer + QWebDatabase + QWebElement + QWebElementCollection + QWebFrame + QWebHistory + QWebHistoryInterface + QWebHistoryItem + QWebHitTestResult + QWebInspector + QWebPage + QWebPluginFactory + QWebSecurityOrigin + QWebSettings + QWebView + QWhatsThis + QWhatsThisClickedEvent + QWheelEvent + QWidget + QWidgetAction + QWidgetItem + QWindowStateChangeEvent + QWindowsMime + QWindowsStyle + QWindowsVistaStyle + QWindowsXPStyle + QWizard + QWizardPage + QWorkspace + QWriteLocker + QX11EmbedContainer + QX11EmbedWidget + QX11Info + QXmlAttributes + QXmlContentHandler + QXmlDTDHandler + QXmlDeclHandler + QXmlDefaultHandler + QXmlEntityResolver + QXmlErrorHandler + QXmlFormatter + QXmlInputSource + QXmlItem + QXmlLexicalHandler + QXmlLocator + QXmlName + QXmlNamePool + QXmlNamespaceSupport + QXmlNodeModelIndex + QXmlParseException + QXmlQuery + QXmlReader + QXmlResultItems + QXmlSchema + QXmlSchemaValidator + QXmlSerializer + QXmlSimpleReader + QXmlStreamAttribute + QXmlStreamAttributes + QXmlStreamEntityDeclaration + QXmlStreamEntityResolver + QXmlStreamNamespaceDeclaration + QXmlStreamNotationDeclaration + QXmlStreamReader + QXmlStreamWriter + + Qt + QTest + QtConcurrent + Phonon + + QFileInfoList + QMutableStringListIterator + QTouchEventSequence + QStringListIterator + QStringBuilder + QWidgetList + + + + + connect + disconnect + staticMetaObject + tr + trUtf8 + + qFindChildren + qobject_cast + + qAbs + qBound + qCritical + qDebug + qFatal + qFuzzyCompare + qInstallMsgHandler + qMacVersion + qMakePair + qMax + qMin + qPrintable + qRound64 + qRound + qCeil + qFloor + qFabs + qSin + qCos + qTan + qAcos + qAsin + qAtan + qAtan2 + qSqrt + qLn + qExp + qPow + qVersion + qWarning + q_check_ptr + qgetenv + qrand + qsrand + qtTrId + qt_set_sequence_auto_mnemonic + qt_symbian_exception2Error + qt_symbian_exception2LeaveL + qt_symbian_throwIfError + + qBinaryFind + qCopy + qCopyBackward + qCount + qDeleteAll + qEqual + qFill + qFind + qGreater + qLess + qLowerBound + qSort + qStableSort + qSwap + qUpperBound + qChecksum + qCompress + qUncompress + qRandomUuid + qsnprintf + qstrcmp + qstrcpy + qstrdup + qstricmp + qstrlen + qstrncmp + qstrncpy + qstrnicmp + qstrnlen + qvsnprintf + qHash + qMetaTypeId + qRegisterMetaType + qRegisterMetaTypeStreamOperators + qAlpha + qBlue + qGray + qGreen + qRed + qRgb + qRgba + qAddPostRoutine + qmlInfo + qmlRegisterInterface + qmlRegisterType + qmlRegisterTypeNotAvailable + qmlRegisterUncreatableType + qWebKitMajorVersion + qWebKitMinorVersion + qWebKitVersion + qt_extension + qDBusRegisterMetaType + qdbus_cast + + + + + QtMsgHandler + QtMsgType + QObjectList + qint8 + qint16 + qint32 + qint64 + qlonglong + qptrdiff + qreal + quint8 + quint16 + quint32 + quint64 + quintptr + qulonglong + uchar + uint + ulong + ushort + Q_PID + QRgb + qScriptConnect + qScriptDisconnect + qScriptRegisterMetaType + qScriptRegisterSequenceMetaType + qScriptValueFromSequence + qScriptValueToSequence + WId + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/crk.xml b/kate/part/syntax/data/crk.xml new file mode 100644 index 00000000..2d83e3f2 --- /dev/null +++ b/kate/part/syntax/data/crk.xml @@ -0,0 +1,158 @@ + + + + + break + catch + class + continue + else + false + for + if + in + is + null + oper + return + this + true + try + typeof + while + + + bool + byte + int16 + int32 + int64 + uint16 + uint32 + uint64 + float32 + float64 + int + uint + intz + uintz + float + void + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/cs.xml b/kate/part/syntax/data/cs.xml new file mode 100644 index 00000000..260434c9 --- /dev/null +++ b/kate/part/syntax/data/cs.xml @@ -0,0 +1,166 @@ + + + + + abstract + as + base + break + case + catch + class + checked + continue + default + delegate + do + else + enum + event + explicit + extern + false + for + foreach + finally + fixed + goto + if + implicit + in + interface + internal + is + lock + namespace + new + null + operator + out + override + params + private + protected + public + readonly + ref + return + sealed + sizeof + stackalloc + static + struct + switch + this + throw + true + try + typeof + unchecked + unsafe + using + virtual + while + #if + #else + #elif + #endif + #define + #undef + #warning + #error + #line + + + bool + byte + char + const + decimal + double + float + int + long + object + uint + ushort + ulong + sbyte + short + string + void + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/css-php.xml b/kate/part/syntax/data/css-php.xml new file mode 100644 index 00000000..6a8bf4d7 --- /dev/null +++ b/kate/part/syntax/data/css-php.xml @@ -0,0 +1,955 @@ + + + + +]> + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/css.xml b/kate/part/syntax/data/css.xml new file mode 100644 index 00000000..a8502e6b --- /dev/null +++ b/kate/part/syntax/data/css.xml @@ -0,0 +1,919 @@ + + + + +]> + + + + + + + + + azimuth + background + background-attachment + background-break + background-clip + background-color + background-image + background-position + background-origin + background-repeat + border + border-bottom + border-bottom-color + border-bottom-style + border-bottom-width + border-collapse + border-color + border-left + border-left-color + border-left-style + border-left-width + border-right + border-right-color + border-right-style + border-right-width + border-spacing + border-style + border-top + border-top-color + border-top-style + border-top-width + border-width + bottom + caption-side + clear + clip + color + content + counter-increment + counter-reset + cue + cue-after + cue-before + cursor + direction + display + elevation + empty-cells + float + font + font-family + font-size + font-size-adjust + font-stretch + font-style + font-variant + font-weight + font-stretch + height + left + letter-spacing + line-height + list-style + list-style-image + list-style-keyword + list-style-position + list-style-type + margin + margin-bottom + margin-left + margin-right + margin-top + marker-offset + max-height + max-width + min-height + min-width + orphans + outline + outline-color + outline-style + outline-width + overflow + padding + padding-bottom + padding-left + padding-right + padding-top + page + page-break-after + page-break-before + page-break-inside + pause + pause-after + pause-before + pitch + pitch-range + play-during + position + quotes + richness + right + size + speak + speak-header + speak-numeral + speak-punctuation + speech-rate + stress + table-layout + text-align + text-decoration + text-decoration-color + text-indent + text-shadow + text-transform + top + unicode-bidi + vertical-align + visibility + voice-family + volume + white-space + widows + width + word-spacing + z-index + + + animation-name + animation-duration + animation-iteration + animation-direction + animation-delay + animation-play-state + animation-fill-mode + animation-timing-function + background-size + border-bottom-image + border-bottom-left-image + border-bottom-left-radius + border-bottom-right-image + border-bottom-right-radius + border-collapse + border-corner-image + border-image + border-left-image + border-radius + border-right-image + border-top-image + border-top-left-image + border-top-left-radius + border-top-right-image + border-top-right-radius + box-align + box-direction + box-flex + box-shadow + box-sizing + column-count + column-fill + column-gap + column-rule-color + column-rule-style + column-rule-width + column-span + column-wisth + hyphens + linear-gradient + opacity + outline + outline-offset + overflow-x + overflow-y + pointer-events + resize + rotation + rotation-point + table-layout + text-overflow + text-shadow + text-wrap + transform-origin + transition + transition-property + transition-duration + word-wrap + + + -moz-animation-name + -moz-animation-duration + -moz-animation-iteration + -moz-animation-direction + -moz-animation-delay + -moz-animation-play-state + -moz-animation-fill-mode + -moz-background-size + -moz-border-image + -moz-border-bottom-colors + -moz-border-left-colors + -moz-border-radius + -moz-border-radius-topleft + -moz-border-radius-topright + -moz-border-radius-bottomleft + -moz-border-radius-bottomright + -moz-border-right-colors + -moz-border-top-colors + -moz-box + -moz-box-flex + -moz-box-shadow + -moz-box-sizing + -moz-column-count + -moz-column-gap + -moz-hyphens + -moz-linear-gradient + -moz-opacity + -moz-outline-style + -moz-perspective + -moz-radial-gradient + -moz-resize + -moz-transform + -moz-transform-origin + -moz-transform-style + -moz-transition + -moz-transition-property + -moz-transition-duration + + + -o-background-size + -o-linear-gradient + -o-text-overflow + -o-transition + -o-transform-origin + + + konq_bgpos_x + konq_bgpos_y + -khtml-background-size + -khtml-border-top-left-radius + -khtml-border-top-right-radius + -khtml-border-bottom-left-radius + -khtml-border-bottom-right-radius + -khtml-border-radius + -khtml-box-shadow + -khtml-opacity + + + -webkit-appearance + -webkit-animation-name + -webkit-animation-duration + -webkit-animation-iteration + -webkit-animation-direction + -webkit-animation-delay + -webkit-animation-play-state + -webkit-animation-fill-mode + -webkit-background-size + -webkit-border-image + -webkit-border-bottom-colors + -webkit-border-left-colors + -webkit-border-radius + -webkit-border-right-colors + -webkit-border-top-colors + -webkit-border-top-left-radius + -webkit-border-top-right-radius + -webkit-border-bottom-left-radius + -webkit-border-bottom-right-radius + -webkit-border-radius-bottomleft + -webkit-border-radius-bottomright + -webkit-box-flex + -webkit-box-reflect + -webkit-box-shadow + -webkit-box-sizing + -webkit-column-count + -webkit-column-gap + -webkit-hyphens + -webkit-linear-gradient + -webkit-gradient + -webkit-perspective + -webkit-text-fill-color + -webkit-text-stroke-color + -webkit-text-stroke-width + -webkit-text-size-adjust + -webkit-transform + -webkit-transform-origin + -webkit-transform-style + -webkit-transition + -webkit-transition-property + -webkit-transition-duration + + + filter + zoom + -ms-animation-name + -ms-animation-duration + -ms-animation-iteration + -ms-animation-direction + -ms-animation-delay + -ms-animation-play-state + -ms-animation-fill-mode + -ms-box-sizing + -ms-filter + -ms-interpolation-mode + -ms-linear-gradient + -ms-text-size-adjust + -ms-transform + -ms-transition + + + font-family + font-size + font-stretch + font-style + font-variant + font-weight + unicode-range + units-per-em + src + panose-1 + stemv + stemh + slope + cap-height + x-height + ascent + descent + widths + bbox + definition-src + baseline + centerline + mathline + topline + + + + inherit + none + hidden + dotted + dashed + solid + double + groove + ridge + inset + outset + xx-small + x-small + small + medium + large + x-large + xx-large + smaller + larger + italic + oblique + small-caps + normal + bold + bolder + lighter + light + 100 + 200 + 300 + 400 + 500 + 600 + 700 + 800 + 900 + transparent + repeat + repeat-x + repeat-y + no-repeat + baseline + sub + super + top + text-top + middle + bottom + text-bottom + left + right + center + justify + konq-center + disc + circle + square + box + decimal + decimal-leading-zero + lower-roman + upper-roman + lower-greek + lower-alpha + lower-latin + upper-alpha + upper-latin + hebrew + armenian + georgian + cjk-ideographic + hiragana + katakana + hiragana-iroha + katakana-iroha + inline + inline-block + block + list-item + run-in + compact + marker + table + inline-table + table-row-group + table-header-group + table-footer-group + table-row + table-column-group + table-column + table-cell + table-caption + auto + crosshair + default + pointer + move + e-resize + ne-resize + nw-resize + n-resize + se-resize + sw-resize + s-resize + w-resize + text + wait + help + above + absolute + always + avoid + below + bidi-override + blink + both + capitalize + caption + clip + close-quote + collapse + condensed + crop + cross + ellipsis + ellipsis-word + embed + expanded + extra-condensed + extra-expanded + fixed + hand + hide + higher + icon + inside + invert + landscape + level + line-through + loud + lower + lowercase + ltr + menu + message-box + mix + narrower + no-close-quote + no-open-quote + nowrap + open-quote + outside + overline + portrait + pre + pre-line + pre-wrap + relative + rtl + scroll + semi-condensed + semi-expanded + separate + show + small-caption + static + static-position + status-bar + thick + thin + ultra-condensed + ultra-expanded + underline + uppercase + visible + wider + break + serif + sans-serif + cursive + fantasy + monospace + border-box + content-box + -epub-hyphens + + + + + aqua + black + blue + cyan + fuchsia + gray + green + lime + maroon + navy + olive + purple + red + silver + teal + white + yellow + ActiveBorder + ActiveCaption + AppWorkspace + Background + ButtonFace + ButtonHighlight + ButtonShadow + ButtonText + CaptionText + GrayText + Highlight + HighlightText + InactiveBorder + InactiveCaption + InactiveCaptionText + InfoBackground + InfoText + Menu + MenuText + Scrollbar + ThreeDDarkShadow + ThreeDFace + ThreeDHighlight + ThreeDLightShadow + ThreeDShadow + Window + WindowFrame + WindowText + + + + url + attr + rect + rgb + rgba + hsl + hsla + counter + counters + + + local + format + + + expression + + + + + all + aural + braille + embossed + handheld + print + projection + screen + speech + tty + tv + + + + not + only + + + + width + min-width + max-width + height + min-height + max-height + device-width + min-device-width + max-device-width + device-height + min-device-height + max-device-height + orientation + aspect-ratio + min-aspect-ratio + max-aspect-ratio + device-aspect-ratio + min-device-aspect-ratio + max-device-aspect-ratio + color + min-color + max-color + color-index + min-color-index + max-color-index + monochrome + min-monochrome + max-monochrome + resolution + min-resolution + max-resolution + scan + grid + + + + hover + link + visited + active + focus + first-child + last-child + only-child + first-of-type + last-of-type + only-of-type + first-letter + first-line + before + after + selection + root + empty + target + enabled + disabled + checked + indeterminate + nth-child + nth-last-child + nth-of-type + nth-last-of-type + not + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/cubescript.xml b/kate/part/syntax/data/cubescript.xml new file mode 100644 index 00000000..d2a73cf5 --- /dev/null +++ b/kate/part/syntax/data/cubescript.xml @@ -0,0 +1,456 @@ + + + + + + + + ? + alias + at + case + casef + cases + clearconsole + clearsleep + complete + concat + concatword + cond + conskip + do + echo + error + escape + exec + format + getalias + getfvarmin + getfvarmax + getvarmin + getvarmax + history + if + indexof + inputcommand + keymap + listcomplete + listdel + listfind + listlen + listsplice + local + loop + loopconcat + loopconcatword + loopfiles + looplist + loopwhile + miniconskip + nodebug + onrelease + prettylist + push + resetvar + result + rnd + saycommand + searchbinds + searcheditbinds + searchspecbinds + sleep + strcmp + strlen + strreplace + strstr + sublist + substr + tabify + toggleconsole + unescape + while + writecfg + + + bind + editbind + specbind + getbind + geteditbind + getspecbind + + + + + + * + - + +f + *f + -f + = + != + > + < + >= + <= + =f + !=f + >f + <f + >=f + <=f + ^ + ! + & + | + ~ + ^~ + &~ + |~ + << + >> + && + || + abs + absf + div + mod + divf + modf + sin + cos + tan + asin + acos + atan + sqrt + pow + loge + log2 + log10 + exp + min + max + minf + maxf + =s + !=s + <s + >s + <=s + >=s + + + + MOUSELEFT + MOUSEMIDDLE + MOUSERIGHT + MOUSEWHEELUP + MOUSEWHEELDOWN + MOUSEX1 + MOUSEX2 + + + MOUSE1 + MOUSE2 + MOUSE3 + MOUSE4 + MOUSE5 + + + BACKSPACE + TAB + CLEAR + RETURN + PAUSE + ESCAPE + SPACE + EXCLAIM + QUOTEDBL + HASH + DOLLAR + AMPERSAND + QUOTE + LEFTPAREN + RIGHTPAREN + ASTERISK + PLUS + COMMA + MINUS + PERIOD + SLASH + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + COLON + SEMICOLON + LESS + EQUALS + GREATER + QUESTION + AT + LEFTBRACKET + BACKSLASH + RIGHTBRACKET + CARET + UNDERSCORE + BACKQUOTE + A + B + C + D + E + F + G + H + I + J + K + L + M + N + O + P + Q + R + S + T + U + V + W + X + Y + Z + DELETE + KP0 + KP1 + KP2 + KP3 + KP4 + KP5 + KP6 + KP7 + KP8 + KP9 + KP_PERIOD + KP_DIVIDE + KP_MULTIPLY + KP_MINUS + KP_PLUS + KP_ENTER + KP_EQUALS + UP + DOWN + RIGHT + LEFT + INSERT + HOME + END + PAGEUP + PAGEDOWN + F1 + F2 + F3 + F4 + F5 + F6 + F7 + F8 + F9 + F10 + F11 + F12 + F13 + F14 + F15 + NUMLOCK + CAPSLOCK + SCROLLOCK + RSHIFT + LSHIFT + RCTRL + LCTRL + RALT + LALT + RMETA + LMETA + LSUPER + RSUPER + MODE + COMPOSE + HELP + PRINT + SYSREQ + BREAK + MENU + + + + commandbuf + editing + mainmenu + numargs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/cue.xml b/kate/part/syntax/data/cue.xml new file mode 100644 index 00000000..6044aae7 --- /dev/null +++ b/kate/part/syntax/data/cue.xml @@ -0,0 +1,74 @@ + + + + + + CATALOG + CDTEXTFILE + FILE + FLAGS + INDEX + ISRC + PERFORMER + PREGAP + POSTGAP + REM + SONGWRITER + TITLE + TRACK + + + AIFF + WAVE + MP3 + BINARY + MOTOTOLA + + + AUDIO + CDG + CDI + MODE1 + MODE2 + RAW + + + 4CH + DCP + PRE + SCMS + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/curry.xml b/kate/part/syntax/data/curry.xml new file mode 100644 index 00000000..4dcb9b4a --- /dev/null +++ b/kate/part/syntax/data/curry.xml @@ -0,0 +1,364 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]> + + + + case + data + do + else + external + fcase + free + if + + in + infix + infixl + infixr + let + module + of + then + type + where + + + and + all + any + appendFile + best + break + browse + browseList + chr + concat + concatMap + const + curry + div + done + doSolve + drop + dropWhile + either + elem + ensureNotFree + ensureSpine + enumFrom + enumFromThen + enumFromTo + enumFromThenTo + error + failed + filter + findall + flip + foldl + foldl1 + foldr + foldr1 + fst + getChar + getLine + id + if_then_else + iterate + head + length + lines + lookup + map + mapIO + mapIO_ + max + maybe + min + mod + negate + not + notElem + null + once + or + ord + otherwise + print + putChar + putStr + putStrLn + readFile + repeat + replicate + return + reverse + seq + sequenceIO + sequenceIO_ + show + snd + solveAll + span + splitAt + success + tail + take + takeWhile + try + uncurry + unknown + unlines + unpack + until + unwords + unzip + unzip3 + writeFile + words + zip + zip3 + zipWith + zipWith3 + + + Bool + Char + Either + Float + Int + IO + Maybe + Ordering + String + Success + + + False + True + Left + Right + Just + Nothing + EQ + LT + GT + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/d.xml b/kate/part/syntax/data/d.xml new file mode 100644 index 00000000..3a88647f --- /dev/null +++ b/kate/part/syntax/data/d.xml @@ -0,0 +1,819 @@ + + + + + + + + + + + asm + body + break + case + catch + continue + default + do + else + finally + for + foreach + foreach_reverse + goto + if + mixin + return + switch + throw + try + while + with + + + synchronized + + + + + + abstract + align + auto + const + export + final + immutable + inout + invariant + lazy + nothrow + override + package + private + protected + public + pure + ref + static + + + + out + scope + + + + + false + null + super + this + true + typeid + + + assert + cast + is + new + delete + in + + + delegate + function + + + + module + import + + + + alias + enum + typedef + class + interface + struct + union + + + + typeof + + void + bool + byte + ubyte + short + ushort + int + uint + long + ulong + cent + ucent + float + double + real + ireal + ifloat + idouble + creal + cfloat + cdouble + char + wchar + dchar + + + + macro + template + + + + init + sizeof + alignof + mangleof + stringof + tupleof + offsetof + max + min + infinity + nan + dig + epsilon + mant_dig + max_10_exp + max_exp + min_10_exp + min_exp + re + im + length + ptr + dup + idup + reverse + sort + keys + values + rehash + + + + + size_t + ptrdiff_t + hash_t + Error + Exception + Object + TypeInfo + ClassInfo + ModuleInfo + Interface + OffsetTypeInfo + TypeInfo_Typedef + TypeInfo_Enum + TypeInfo_Pointer + TypeInfo_Array + TypeInfo_StaticArray + TypeInfo_AssociativeArray + TypeInfo_Function + TypeInfo_Delegate + TypeInfo_Class + TypeInfo_Interface + TypeInfo_Struct + TypeInfo_Tuple + + + string + wstring + dstring + bit + TypeInfo_Const + TypeInfo_Invariant + + + + extern + + + C + + D + Windows + Pascal + System + + + + msg + lib + + + + exit + success + failure + + + + DigitalMars + X86 + X86_64 + Windows + Win32 + Win64 + linux + LittleEndian + BigEndian + D_Coverage + D_InlineAsm_X86 + unittest + D_Version2 + none + all + + + + __FILE__ + __LINE__ + __DATE__ + __TIME__ + __TIMESTAMP__ + __VENDOR__ + __VERSION__ + __EOF__ + + + + debug + unittest + + + + pragma + + + + version + + + + deprecated + volatile + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/debianchangelog.xml b/kate/part/syntax/data/debianchangelog.xml new file mode 100644 index 00000000..864f48fa --- /dev/null +++ b/kate/part/syntax/data/debianchangelog.xml @@ -0,0 +1,194 @@ + + + + + + urgency + + + + oldstable + oldstable-security + oldstable-proposed-updates + stable + stable-security + stable-proposed-updates + testing + testing-security + testing-proposed-updates + frozen + unstable + sid + experimental + UNRELEASED + + sarge + sarge-backports + sarge-volatile + etch + etch-backports + etch-volatile + lenny + lenny-backports + lenny-backports-sloppy + lenny-volatile + squeeze + squeeze-backports + squeeze-backports-sloppy + squeeze-volatile + wheezy + wheezy-backports + wheezy-backports-sloppy + jessie + jessie-backports + jessie-backports-sloppy + + dapper + dapper-security + dapper-proposed + dapper-updates + dapper-backports + dapper-commercial + edgy + edgy-security + edgy-proposed + edgy-updates + edgy-backports + edgy-commercial + feisty + feisty-security + feisty-proposed + feisty-updates + feisty-backports + feisty-commercial + gutsy + gutsy-security + gutsy-proposed + gutsy-updates + gutsy-backports + gutsy-partner + hardy + hardy-security + hardy-proposed + hardy-updates + hardy-backports + hardy-partner + intrepid + intrepid-security + intrepid-proposed + intrepid-updates + intrepid-backports + intrepid-partner + jaunty + jaunty-security + jaunty-proposed + jaunty-updates + jaunty-backports + jaunty-partner + karmic + karmic-security + karmic-proposed + karmic-updates + karmic-backports + lucid + lucid-security + lucid-proposed + lucid-updates + lucid-backports + maverick + maverick-security + maverick-proposed + maverick-updates + maverick-backports + natty + natty-security + natty-proposed + natty-updates + natty-backports + oneiric + oneiric-security + oneiric-proposed + oneiric-updates + oneiric-backports + precise + precise-security + precise-proposed + precise-updates + precise-backports + quantal + quantal-security + quantal-proposed + quantal-updates + quantal-backports + raring + raring-security + raring-proposed + raring-updates + raring-backports + saucy + saucy-security + saucy-proposed + saucy-updates + saucy-backports + trusty + trusty-security + trusty-proposed + trusty-updates + trusty-backports + utopic + utopic-security + utopic-proposed + utopic-updates + utopic-backports + vivid + vivid-security + vivid-proposed + vivid-updates + vivid-backports + + + + low + medium + high + emergency + bug + critical + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/debiancontrol.xml b/kate/part/syntax/data/debiancontrol.xml new file mode 100644 index 00000000..8c34c64c --- /dev/null +++ b/kate/part/syntax/data/debiancontrol.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/desktop.xml b/kate/part/syntax/data/desktop.xml new file mode 100644 index 00000000..89260d63 --- /dev/null +++ b/kate/part/syntax/data/desktop.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/diff.xml b/kate/part/syntax/data/diff.xml new file mode 100644 index 00000000..eb63a52b --- /dev/null +++ b/kate/part/syntax/data/diff.xml @@ -0,0 +1,119 @@ + + + + + +]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/djangotemplate.xml b/kate/part/syntax/data/djangotemplate.xml new file mode 100644 index 00000000..47ad9cd0 --- /dev/null +++ b/kate/part/syntax/data/djangotemplate.xml @@ -0,0 +1,330 @@ + + + +]> + + + + + + + + for + block + if + ifequal + ifnotequal + ifchanged + blocktrans + spaceless + autoescape + + + + endfor + endblock + endif + endifequal + endifnotequal + endifchanged + endblocktrans + endspaceless + endautoescape + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/dosbat.xml b/kate/part/syntax/data/dosbat.xml new file mode 100644 index 00000000..d85bcbc5 --- /dev/null +++ b/kate/part/syntax/data/dosbat.xml @@ -0,0 +1,311 @@ + + + + + + + +]> + + + + + + + assoc + break + call + cd + chdir + cls + color + copy + date + del + dir + endlocal + erase + exit + ftype + md + mkdir + move + path + pause + popd + prompt + pushd + rd + ren + rename + rmdir + setlocal + shift + start + time + title + type + ver + verify + vol + + + + echo + else + for + in + do + goto + if + not + set + + + + at + attrib + break + cacls + chcp + chkdsk + chkntfs + cmd + comp + compact + convert + diskcomp + diskcopy + doskey + fc + find + findstr + format + graftabl + help + label + mode + more + print + recover + replace + sort + subst + tree + xcopy + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/dot.xml b/kate/part/syntax/data/dot.xml new file mode 100644 index 00000000..5b0e6638 --- /dev/null +++ b/kate/part/syntax/data/dot.xml @@ -0,0 +1,172 @@ + + + + + + + + + digraph + node + edge + subgraph + + + + + center + layers + margin + mclimit + name + nodesep + nslimit + ordering + page + pagedir + rank + rankdir + ranksep + ratio + rotate + size + + distortion + fillcolor + fontcolor + fontname + fontsize + height + layer + orientation + peripheries + regular + shape + shapefile + sides + skew + width + + arrowhead + arrowsize + arrowtail + constraint + decorateP + dir + headclip + headlabel + labelangle + labeldistance + labelfontcolor + labelfontname + labelfontsize + minlen + port_label_distance + samehead + sametail + tailclip + taillabel + weight + + color + + bgcolor + label + URL + + fontcolor + fontname + fontsize + layer + style + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/doxygen.xml b/kate/part/syntax/data/doxygen.xml new file mode 100644 index 00000000..daf1cf9d --- /dev/null +++ b/kate/part/syntax/data/doxygen.xml @@ -0,0 +1,514 @@ + + +]> + + + + + + + + \arg @arg + \author @author + \authors @authors + \brief @brief + \callgraph @callgraph + \callergraph @callergraph + \date @date + \deprecated @deprecated + \details @details + \docbookonly @docbookonly + \else @else + \endcond @endcond + \enddocbookonly @enddocbookonly + \endhtmlonly @endhtmlonly + \endif @endif + \endinternal @endinternal + \endlatexonly @endlatexonly + \endlink @endlink + \endmanonly @endmanonly + \endparblock @endparblock + \endrtfonly @endrtfonly + \endsecreflist @endsecreflist + \endxmlonly @endxmlonly + \f[ @f[ + \f] @f] + \f$ @f$ + + \hideinitializer @hideinitializer + \htmlonly @htmlonly + + \internal @internal + \invariant @invariant + \latexonly @latexonly + \li @li + \manonly @manonly + \n @n + \nosubgrouping @nosubgrouping + \only @only + \parblock @parblock + \post @post + \pre @pre + \private @pivate + \privatesection @pivatesection + \protected @protected + \protectedsection @protectedsection + \public @public + \publicsection @publicsection + \pure @pure + \remark @remark + \remarks @remarks + \return @return + \returns @returns + \result @result + \rtfonly @rtfonly + \sa @sa + \secreflist @secreflist + \see @see + \short @short + \showinitializer @showinitializer + \since @since + \static @static + \tableofcontents @tableofcontents + \test @test + \version @version + \xmlonly @xmlonly + + \# @# + \$ @$ + \% @% + \& @& + \> @> + \< @< + \" @" + \:: @:: + \@ @@ + \\ @\ + \~ @~ + \. @. + \-- @-- + \--- @--- + + + + \a @a + \anchor @anchor + \b @b + \c @c + \cite @cite + \cond @cond + \copybrief @copybrief + \copydetails @copydetails + \copydoc @copydoc + \def @def + \dir @dir + \dontinclude @dontinclude + \e @e + \elseif @elseif + \em @em + \enum @enum + \example @example + \exception @exception + \exceptions @exceptions + \extends @extends + \file @file + \htmlinclude @htmlinclude + \idlexcept @idlexcept + \if @if + \ifnot @ifnot + \implements @implements + \include @include + \includelineno @includelineno + \latexinclude @latexinclude + \link @link + \memberof @memberof + \namespace @namespace + \p @p + \package @package + \property @property + \relatedalso @relatedalso + \relatesalso @relatesalso + \related @related + \relates @relates + \retval @retval + \throw @throw + \throws @throws + \verbinclude @verbinclude + \version @version + + \xrefitem @xrefitem + + + \param @param + + \tparam @tparam + + + + \image @image + + + + + \addtogroup @addtogroup + \category @category + \class @class + \diafile @diafile + \dotfile @dotfile + \defgroup @defgroup + \interface @interface + \headerfile @headerfile + \mscfile @mscfile + \page @page + \paragraph @paragraph + \protocol @prtocol + \ref @ref + \section @section + \snippet @snippet + \struct @struct + \subpage @subpage + \subsection @subsection + \subsubsection @subsubsection + \union @union + \weakgroup @weakgroup + + + \addindex @addindex + \copyright @copyright + \fn @fn + \ingroup @ingroup + \line @line + \mainpage @mainpage + \name @name + \overload @overload + \par @par + \skip @skip + \skipline @skipline + \typedef @typedef + \until @until + \var @var + \vhdlflow @vhdlflow + + + \note @note + + + \warning @warning + + + \attention @attention + \bug @bug + + + \todo @todo + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/doxygenlua.xml b/kate/part/syntax/data/doxygenlua.xml new file mode 100644 index 00000000..03fcdc9c --- /dev/null +++ b/kate/part/syntax/data/doxygenlua.xml @@ -0,0 +1,481 @@ + + +]> + + + + + + \arg + \attention + \author + \callgraph + \code + \dot + \else + \endcode + \endcond + \enddot + \endhtmlonly + \endif + \endlatexonly + \endlink + \endmanonly + \endverbatim + \endxmlonly + \f[ + \f] + \f$ + \hideinitializer + \htmlonly + \interface + \internal + \invariant + \~ + \@ + \$ + \\ + \# + \latexonly + \li + \manonly + \n + \nosubgrouping + \note + \only + \post + \pre + \remarks + \return + \returns + \sa + \see + \showinitializer + \since + \test + \todo + \verbatim + \warning + \xmlonly + + @arg + @attention + @author + @callgraph + @code + @dot + @else + @endcode + @endcond + @enddot + @endhtmlonly + @endif + @endlatexonly + @endlink + @endmanonly + @endverbatim + @endxmlonly + @f[ + @f] + @f$ + @hideinitializer + @htmlonly + @interface + @internal + @invariant + @~ + @@ + @$ + @\ + @# + @latexonly + @li + @manonly + @n + @nosubgrouping + @note + @only + @post + @pre + @remarks + @return + @returns + @sa + @see + @showinitializer + @since + @test + @todo + @verbatim + @warning + @xmlonly + + + + \addtogroup + \a + \anchor + \b + \c + \class + \cond + \copydoc + \def + \dontinclude + \dotfile + \e + \elseif + \em + \enum + \example + \exception + \exceptions + \file + \htmlinclude + \if + \ifnot + \include + \link + \namespace + \p + \package + \ref + \relatesalso + \relates + \retval + \throw + \throws + \verbinclude + \version + \xrefitem + + @addtogroup + @a + @anchor + @b + @c + @class + @cond + @copydoc + @def + @dontinclude + @dotfile + @e + @elseif + @em + @enum + @example + @exception + @exceptions + @file + @htmlinclude + @if + @ifnot + @include + @link + @namespace + @p + @package + @ref + @relatesalso + @relates + @retval + @throw + @throws + @verbinclude + @version + @xrefitem + + + \param + + @param + + + \image + + @image + + + \defgroup + \page + \paragraph + \section + \struct + \subsection + \subsubsection + \union + \weakgroup + + @defgroup + @page + @paragraph + @section + @struct + @subsection + @subsubsection + @union + @weakgroup + + + \addindex + \brief + \bug + \date + \deprecated + \fn + \ingroup + \line + \mainpage + \name + \overload + \par + \short + \skip + \skipline + \typedef + \until + \var + + @addindex + @brief + @bug + @date + @deprecated + @fn + @ingroup + @line + @mainpage + @name + @overload + @par + @short + @skip + @skipline + @typedef + @until + @var + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/dtd.xml b/kate/part/syntax/data/dtd.xml new file mode 100644 index 00000000..17914d16 --- /dev/null +++ b/kate/part/syntax/data/dtd.xml @@ -0,0 +1,110 @@ + + + +]> + + + + + EMPTY + ANY + CDATA + ID + IDREF + IDREFS + NMTOKEN + NMTOKENS + ENTITY + ENTITIES + NOTATION + PUBLIC + SYSTEM + NDATA + + + + #PCDATA + #REQUIRED + #IMPLIED + #FIXED + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/e.xml b/kate/part/syntax/data/e.xml new file mode 100644 index 00000000..c21ac288 --- /dev/null +++ b/kate/part/syntax/data/e.xml @@ -0,0 +1,261 @@ + + + + + + FALSE + MAX_INT + MIN_INT + NULL + TRUE + UNDEF + bit + bits + body + bool + byte + byte_array + continue + copy + default + external_pointer + files + file + form + global + index + init + int + it + list + load + long + me + method + module + ntv + of + pat + print + result + source_ref + string + symtab + sys + test + uint + untyped + vec + + + run + init + pre_generate + dut_error + pack + unpack + post_generate + pre_generate + set_config + hex + stop_run + append + size + delete + is_empty + deep_compare + deep_compare_physical + clear + pop0 + setup + crc_32 + + + chars + define + extend + event + ECHO + DOECHO + import + initialize + non_terminal + struct + unit + script + testgroup + type + + + C + add + also + and + as + as_a + break + code + compute + computed + delayed + do + else + each + emit + empty + end + exit + finish + for + from + if + in + is + like + log + new + no + not + only + or + out + read + repeat + return + reverse + routine + step + then + to + traceable + untraceable + var + when + while + with + write + xor + + + before + by + choose + gen + keep + keeping + matches + next + select + sequence + soft + using + + + address + cover + error + events + event + length + kind + ranges + range + sample + text + value + item + transition + illegal + + + always + all + basic + call + cycles + cycle + clock + change + check + expect + fall + first + forever + idle + initial + negedge + others + on + posedge + rise + start + that + time + task + until + verilog + vhdl + wait + within + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/eiffel.xml b/kate/part/syntax/data/eiffel.xml new file mode 100644 index 00000000..9998fd82 --- /dev/null +++ b/kate/part/syntax/data/eiffel.xml @@ -0,0 +1,125 @@ + + + + + + + agent + alias + all + and + as + assign + class + convert + create + creation + debug + deferred + do + else + elseif + end + expanded + export + external + feature + from + frozen + if + implies + indexing + infix + inherit + inspect + is + like + local + loop + not + obsolete + old + once + or + prefix + pure + redefine + reference + rename + rescue + retry + separate + then + undefine + + + + Current + False + Precursor + Result + True + TUPLE + + + + check + ensure + require + variant + invariant + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/email.xml b/kate/part/syntax/data/email.xml new file mode 100644 index 00000000..a3ef294f --- /dev/null +++ b/kate/part/syntax/data/email.xml @@ -0,0 +1,106 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/erlang.xml b/kate/part/syntax/data/erlang.xml new file mode 100644 index 00000000..8cbec015 --- /dev/null +++ b/kate/part/syntax/data/erlang.xml @@ -0,0 +1,250 @@ + + + + + + + + + +] +> + + + + + + + after + begin + case + catch + cond + end + fun + if + let + of + query + receive + all_true + some_true + + + + + div + rem + or + xor + bor + bxor + bsl + bsr + and + band + not + bnot + + + + + abs + accept + alarm + apply + atom_to_list + binary_to_list + binary_to_term + check_process_code + concat_binary + date + delete_module + disconnect_node + element + erase + exit + float + float_to_list + garbage_collect + get + get_keys + group_leader + halt + hd + integer_to_list + is_alive + is_atom + is_binary + is_boolean + is_float + is_function + is_integer + is_list + is_number + is_pid + is_port + is_process_alive + is_record + is_reference + is_tuple + length + link + list_to_atom + list_to_binary + list_to_float + list_to_integer + list_to_pid + list_to_tuple + load_module + loaded + localtime + make_ref + module_loaded + node + nodes + now + open_port + pid_to_list + port_close + port_command + port_connect + port_control + ports + pre_loaded + process_flag + process_info + processes + purge_module + put + register + registered + round + self + setelement + size + spawn + spawn_link + spawn_opt + split_binary + statistics + term_to_binary + throw + time + tl + trunc + tuple_to_list + unlink + unregister + whereis + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/euphoria.xml b/kate/part/syntax/data/euphoria.xml new file mode 100644 index 00000000..a1ca1999 --- /dev/null +++ b/kate/part/syntax/data/euphoria.xml @@ -0,0 +1,376 @@ + + + + + + + + + + abort + allocate + allocate_string + allow_break + and + and_bits + append + arccos + arcsin + arctan + atom_to_float32 + atom_to_float64 + as + begin + bits_to_int + bytes_to_int + c_func + c_proc + call + call_back + call_func + call_proc + check_break + chdir + clear_screen + close + command_line + compare + cos + crash_file + crash_message + current_dir + custom_sort + date + define_c_func + define_c_proc + define_c_var + dir + display_text_image + do + else + elsif + end + equal + exit + find + float32_to_atom + float64_to_atom + floor + flush + for + free + free_console + function + get_bytes + get_key + get_mouse + get_position + get_screen_char + getc + getenv + gets + if + include + int_to_bits + int_to_bytes + length + lock_file + log + lower + machine_func + machine_proc + match + mem_copy + mem_set + mouse_events + mouse_pointer + not + not_bits + of + open + open_dll + or + or_bits + peek + peek4 + peek4s + peek4u + platform + poke + poke4 + position + power + prepend + print + printf + procedure + profile + prompt_number + prompt_string + put_screen_char + puts + rand + read_bitmap + register_block + remainder + repeat + return + reverse + routine_id + save_bitmap + save_text_image + scroll + seek + set_rand + sin + sleep + sort + sprint + sprintf + sqrt + system + system_exec + tan + text_color + then + time + to + trace + type + unlock_file + unregister_block + upper + value + video_config + wait_key + walk_dir + where + while + wildcard_file + wildcard_match + with + without + wrap + xor + xor_bits + ? + + + + atom + constant + global + integer + object + sequence + type + + + + PI + GET_SUCCESS + + + + addto + adjustment + alignment + appendto + append_page + arrow + aspect_frame + button + calendar + cell_renderer_text + cell_renderer_toggle + check + checkbutton + check_menu_item + choice + combo + connect + drawingarea + draw_arc + draw_image + draw_line + draw_polygon + draw_point + draw_rectangle + end_submenu + entry + euget + event_box + flatten + font + frame + get + getImage + getSize + g_list + g_list_to_sequence + deallocate_strings + draw_line + hbox + hbuttonbox + hpaned + hscrollbar + hseparator + hscale + idle_add + image + image_menu_item + init + label + limit + list_store + list_view + list_view_column + main + mark_day + menu + menubar + menu_item + mouse_button + new_gc + new_group + new_menu_group + notebook + option + option_menu + pack + path + pop + progress_bar + push + quit + radio + radiobutton + radio_menu_item + rc_parse + run + separator_menu_item + set + set_submenu + str + scrolled_window + seq_to_str + setfg + setProperty + show + spinbutton + statusbar + table + textbox + timer + togglebutton + toolbar + tooltip + tree_store + tree_view + tree_view_column + vbox + vbuttonbox + vpaned + vscale + vscrollbar + vseparator + when + window + NULL + TRUE + FALSE + color_selection + file_selection + font_selection_dialog + Error + Info + Question + Warn + YesNo + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/fasm.xml b/kate/part/syntax/data/fasm.xml new file mode 100644 index 00000000..50951650 --- /dev/null +++ b/kate/part/syntax/data/fasm.xml @@ -0,0 +1,899 @@ + + + + + + + + + rax + eax + ax + ah + al + rbx + ebx + bx + bh + bl + rcx + ecx + cx + ch + cl + rdx + edx + dx + dh + dl + rbp + ebp + bp + rsi + esi + si + rdi + edi + di + rsp + esp + sp + r8 + r9 + r10 + r11 + r12 + r13 + r14 + r15 + + cs + ds + es + fs + gs + ss + + cr0 + + cr2 + cr3 + cr4 + + dr0 + dr1 + dr2 + dr3 + dr6 + dr7 + + st + + mm0 + mm1 + mm2 + mm3 + mm4 + mm5 + mm6 + mm7 + + xmm0 + xmm1 + xmm2 + xmm3 + xmm4 + xmm5 + xmm6 + xmm7 + + + aaa + aad + aam + aas + adc + add + addpd + addps + addsd + addss + addsubpd + addsubps + and + andnpd + andnps + andpd + andps + arpl + bound + bsf + bsr + bswap + bt + btc + btr + bts + call + cbw + cwde + cwd + cdq + cdqe + cqo + clc + cld + clgi + cli + clts + clflush + cmc + cmova + cmovae + cmovb + cmovbe + cmovc + cmove + cmovg + cmovge + cmovl + cmovle + cmovna + cmovnae + cmovnb + cmovnbe + cmovnc + cmovne + cmovng + cmovnge + cmovnl + cmovnle + cmovno + cmovnp + cmovns + cmovnz + cmovo + cmovp + cmovpe + cmovpo + cmovs + cmovz + cmp + cmpeqpd + cmpeqps + cmpeqsd + cmpeqss + cmplepd + cmpleps + cmplesd + cmpless + cmpltpd + cmpltps + cmpltsd + cmpltss + cmpneqpd + cmpneqps + cmpneqsd + cmpneqss + cmpnlepd + cmpnleps + cmpnlesd + cmpnless + cmpnltpd + cmpnltps + cmpnltsd + cmpnltss + cmpordpd + cmpordps + cmpordsd + cmpordss + cmppd + cmpps + cmps + cmpsb + cmpsd + cmpss + cmpsw + cmpunordpd + cmpunordps + cmpunordsd + cmpunordss + cmpxchg + cmpxchg486 + cmpxchg8b + cmpxchg16b + comisd + comiss + cpuid + cvtdq2pd + cvtdq2ps + cvtpd2dq + cvtpd2pi + cvtpd2ps + cvtpi2pd + cvtpi2ps + cvtps2dq + cvtps2pd + cvtps2pi + cvtsd2si + cvtsd2ss + cvtsi2sd + cvtsi2ss + cvtss2sd + cvtss2si + cvttpd2dq + cvttpd2pi + cvttps2dq + cvttps2pi + cvttsd2si + cvttss2si + daa + das + dec + div + divpd + divps + divsd + divss + emms + enter + f2xm1 + fabs + fadd + faddp + fbld + fbstp + fchs + fclex + fnclex + fcmovb + fcmovbe + fcmove + fcmovnb + fcmovnbe + fcmovne + fcmovnu + fcmovu + fcom + fcomp + fcompp + fcomi + fcomip + fcos + fdecstp + fdisi + feni + fdiv + fdivr + fdivp + fdivrp + femms + ffree + ffreep + fiadd + ficom + ficomp + fidiv + fidivr + fild + fimul + fincstp + finit + fist + fistp + fisttp + fisub + fisubr + fld + fld1 + fldl2e + fldl2t + fldlg2 + fldln2 + fldcw + fldenv + fldpi + fldz + fmul + fmulp + fndisi + fneni + fninit + fnop + fnsave + fnstcw + fnstenv + fnstsw + fnwait + fpatan + fptan + fprem + fprem1 + frndint + frstor + fsave + fscale + fsetpm + fsin + fsincos + fsqrt + fst + fstp + fstcw + fstenv + fstsw + fsub + fsubr + fsubp + fsubrp + ftst + fucom + fucomp + fucompp + fucomi + fucomip + fwait + fxam + fxch + fxrstor + fxsave + fxtract + fyl2x + fyl2xp1 + haddpd + haddps + hlt + hsubpd + hsubps + ibts + idiv + imul + in + inc + ins + insb + insd + insw + int + int1 + int3 + into + invd + invlpg + invlpga + iret + iretd + iretq + iretw + ja + jae + jb + jbe + jc + je + jg + jge + jl + jle + jna + jnae + jnb + jnbe + jnc + jne + jng + jnge + jnl + jnle + jno + jnp + jns + jnz + jo + jp + jpe + jpo + js + jz + jcxz + jecxz + jrcxz + jmp + lahf + lar + lddqu + ldmxcsr + lds + les + lea + leave + lfence + lfs + lgdt + lgs + lidt + lldt + lmsw + loadall + loadall286 + lods + lodsb + lodsd + lodsq + lodsw + loop + loope + loopne + loopnz + loopz + lsl + lss + ltr + maskmovdqu + maskmovq + maxpd + maxps + maxsd + maxss + mfence + minpd + minps + minsd + minss + monitor + mov + movapd + movaps + movd + movddup + movdq2q + movdqa + movdqu + movhlps + movhpd + movhps + movlhps + movlpd + movlps + movmskpd + movmskps + movntdq + movnti + movntpd + movntps + movntq + movq + movq2dq + movs + movsb + movsd + movshdup + movsldup + movsq + movss + movsx + movsxd + movsw + movupd + movups + movzx + mul + mulpd + mulps + mulsd + mulss + mwait + neg + nop + not + or + orpd + orps + out + outs + outsb + outsw + outsd + packssdw + packsswb + packuswb + paddb + paddd + paddq + paddsb + paddsw + paddusb + paddusw + paddw + pand + pandn + pause + pavgb + pavgusb + pavgw + pcmpeqb + pcmpeqw + pcmpeqd + pcmpgtb + pcmpgtw + pcmpgtd + pdistib + pextrw + pf2id + pf2iw + pfacc + pfadd + pfcmpeq + pfcmpge + pfcmpgt + pfmax + pfmin + pfmul + pfnacc + pfpnacc + pfrcp + pfrcpit1 + pfrcpit2 + pfrsqit1 + pfrsqrt + pfsub + pfsubr + pi2fd + pi2fw + pinsrw + pmachriw + pmaddwd + pmagw + pmaxsw + pmaxub + pminsw + pminub + pmovmskb + pmulhrw + pmulhuw + pmulhw + pmullw + pmuludq + pmvgezb + pmvlzb + pmvnzb + pmvzb + pop + popa + popaw + popad + popf + popfw + popfd + popfq + por + prefetch + prefetchnta + prefetcht0 + prefetcht1 + prefetcht2 + prefetchw + psadbw + pshufd + pshufhw + pshuflw + pshufw + pslld + pslldq + psllq + psllw + psrad + psraw + psrld + psrldq + psrlq + psrlw + psubb + psubd + psubq + psubsb + psubsiw + psubsw + psubusb + psubusw + psubw + pswapd + punpckhbw + punpckhdq + punpckhqdq + punpckhwd + punpcklbw + punpckldq + punpcklqdq + punpcklwd + push + pusha + pushad + pushaw + pushf + pushfd + pushfq + pushfw + pxor + rcl + rcr + rcpps + rcpss + rdmsr + rdpmc + rdshr + rdtsc + rdtscp + ret + retf + retn + rol + ror + rsdc + rsldt + rsm + rsqrtps + rsqrtss + rsts + sahf + sal + sar + salc + sbb + scas + scasb + scasd + scasq + scasw + seta + setae + setb + setbe + setc + sete + setg + setge + setl + setle + setna + setnae + setnb + setnbe + setnc + setne + setng + setnge + setnl + setnle + setno + setnp + setns + setnz + seto + setp + setpe + setpo + sets + setz + sfence + sgdt + shl + shld + shr + shrd + shufpd + shufps + sidt + skinit + sldt + smi + smint + smintold + smsw + sqrtpd + sqrtps + sqrtsd + sqrtss + stc + std + stgi + sti + stmxcsr + stos + stosb + stosd + stosq + stosw + str + sub + subpd + subps + subsd + subss + svdc + svldt + svts + swapgs + syscall + sysenter + sysexit + sysret + test + ucomisd + ucomiss + ud0 + ud1 + ud2 + umov + unpckhpd + unpckhps + unpcklpd + unpcklps + verr + verw + vmload + vmmcall + vmrun + vmsave + wait + wbinvd + wrmsr + wrshr + xadd + xbts + xchg + xlat + xlatb + xor + xorpd + xorps + + + + + db + dw + du + dd + dp + df + dq + dt + + rb + rw + rd + rp + rf + rq + rt + + file + + byte + word + dword + pword + qword + tbyte + tword + dqword + ptr + + + + append + at + break + common + display + else + end + equ + fix + foward + if + irp + irps + label + local + match + macro + purge + repeat + rept + reverse + restore + struc + times + while + virtual + + + + align + entry + extrn + format + include + invoke + data + load + from + heap + org + proc + public + section + segment + stack + store + use16 + use32 + use64 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/ferite.xml b/kate/part/syntax/data/ferite.xml new file mode 100644 index 00000000..7d154827 --- /dev/null +++ b/kate/part/syntax/data/ferite.xml @@ -0,0 +1,117 @@ + + + + + + break + case + continue + else + for + if + do + function + namespace + while + class + new + uses + global + return + self + super + null + iferr + fix + + + number + void + string + array + object + final + static + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/fgl-4gl.xml b/kate/part/syntax/data/fgl-4gl.xml new file mode 100644 index 00000000..2acbe8ff --- /dev/null +++ b/kate/part/syntax/data/fgl-4gl.xml @@ -0,0 +1,363 @@ + + + + + + at + by + go + if + in + is + no + of + on + or + to + up + + add + ALL + and + ANY + avg + day + end + ESC + for + key + let + log + max + mdy + min + not + put + red + row + run + set + sum + top + + blue + BOLD + call + case + cyan + desc + DROP + else + exit + file + form + FREE + from + goto + help + HIDE + HOLD + HOUR + into + last + left + like + line + load + LOCK + main + menu + MODE + name + NEED + next + null + open + page + PIPE + quit + READ + rows + show + skip + sort + STEP + STOP + TEMP + text + then + thru + true + user + WAIT + when + with + WORK + WRAP + year + + after + alter + ascii + BEGIN + blink + clear + close + count + DEFER + DIRTY + error + every + false + fetch + field + first + flush + green + GROUP + index + input + label + lines + month + order + outer + pause + print + right + share + sleep + space + start + TABLE + today + union + UNITS + using + where + WHILE + white + + ACCEPT + before + border + bottom + column + commit + create + cursor + define + delete + enable + ESCAPE + exists + finish + format + HAVING + header + insert + length + locate + margin + MINUTE + MODIFY + normal + option + output + PAGENO + prompt + record + report + return + revoke + SCREEN + scroll + SECOND + select + spaces + status + UNIQUE + UNLOAD + update + values + window + yellow + + between + clipped + cluster + columns + command + comment + connect + CURRENT + declare + display + execute + foreach + globals + infield + MAGENTA + matches + message + options + prepare + printer + program + reverse + trailer + upshift + waiting + without + + ABSOLUTE + continue + database + defaults + DISTINCT + EXTERNAL + function + INT_FLAG + NOTFOUND + previous + ROLLBACK + whenever + wordwrap + + attribute + committed + construct + delimiter + downshift + exclusive + INTERRUPT + ISOLATION + otherwise + quit_flag + returning + + attributes + CONSTRAINT + initialize + statistics + + fgl_lastkey + formhandler + + fgl_lastkey() + + + + + char + date + array + float + money + serial + DECIMAL + integer + NUMERIC + VARCHAR + DATETIME + FRACTION + INTERVAL + smallint + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/fgl-per.xml b/kate/part/syntax/data/fgl-per.xml new file mode 100644 index 00000000..9b3461ad --- /dev/null +++ b/kate/part/syntax/data/fgl-per.xml @@ -0,0 +1,172 @@ + + + + + + by + to + + end + not + red + + blue + cyan + keys + like + null + size + type + + black + color + green + input + today + white + + format + record + screen + tables + yellow + + default + display + include + magenta + noentry + picture + reverse + through + UPSHIFT + without + + autonext + comments + COMPRESS + database + formonly + noupdate + required + WORDWRAP + + character + downshift + invisible + underline + + attributes + delimiters + + instructions + + + char + date + array + float + money + serial + DECIMAL + integer + NUMERIC + VARCHAR + DATETIME + FRACTION + INTERVAL + smallint + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/find-trivial-regexpr.sh b/kate/part/syntax/data/find-trivial-regexpr.sh new file mode 100755 index 00000000..b034f91a --- /dev/null +++ b/kate/part/syntax/data/find-trivial-regexpr.sh @@ -0,0 +1,16 @@ +#!/bin/sh +# Copyright (C) 2008 Sebastian Pipping +grep 'RegExpr' * | grep --color=auto -E 'String=\s*(["'"'"'])'\ +'(\^(\\s[*+]?)?)?'\ +'('\ +'%?'\ +'('\ +'[a-zA-Z"'"'"'#_!/=:;<>-]'\ +'|\\[\\\]\[()\{\}.$^+*?]'\ +'|[()|]'\ +'|\(\?:'\ +'|&(amp|gt|lt|apos|quot);'\ +')'\ +'|[0-9]'\ +')*%?'\ +'\1' diff --git a/kate/part/syntax/data/fortran.xml b/kate/part/syntax/data/fortran.xml new file mode 100644 index 00000000..43817994 --- /dev/null +++ b/kate/part/syntax/data/fortran.xml @@ -0,0 +1,580 @@ + + + + + + + + + + + allocate + break + call + case + common + + continue + cycle + deallocate + default + + forall + where + elsewhere + + + equivalence + exit + external + for + go + goto + if + implicit + include + interface + intrinsic + namelist + none + nullify + operator + assignment + pause + procedure + pure + elemental + record + recursive + result + return + select + selectcase + stop + + to + use + only + entry + while + + + access + backspace + close + inquire + open + print + read + rewind + write + format + + + + unit + end + err + fmt + iostat + status + advance + size + eor + + + + unit + iostat + err + file + status + access + form + recl + blank + position + action + delim + pad + + + + unit + iostat + err + file + exist + opened + number + named + name + access + sequential + direct + form + formatted + unformatted + recl + nextrec + blank + position + action + read + write + readwrite + delim + pad + + + double + precision + parameter + save + pointer + public + private + target + allocatable + optional + sequence + + + + + + + + abs + cabs + dabs + iabs + aimag + aint + dint + anint + dnint + ceiling + cmplx + dcmplx + dimag + floor + nint + idnint + int + idint + ifix + real + float + sngl + dble + dreal + aprime + dconjg + dfloat + ddmim + rand + + modulo + conjg + dprod + dim + ddim + idim + max + amax0 + amax1 + max0 + max1 + dmax1 + min + amin0 + amin1 + min0 + min1 + dmin1 + mod + amod + dmod + sign + dsign + isign + + acos + dacos + asin + dasin + atan + datan + atan2 + datan2 + cos + ccos + dcos + cosh + dcosh + exp + cexp + dexp + log + alog + dlog + clog + log10 + alog10 + dlog10 + sin + csin + dsin + sinh + dsinh + sqrt + csqrt + dsqrt + tan + dtan + tanh + dtanh + + + achar + char + iachar + ichar + + lge + lgt + lle + llt + + adjustl + adjustr + index + len_trim + scan + verify + + logical + + exponent + fraction + nearest + rrspacing + scale + set_exponent + spacing + + btest + iand + ibclr + ibits + ibset + ieor + ior + ishft + ishftc + not + + mvbits + + merge + + + + + + associated + present + kind + + len + + digits + epsilon + huge + maxexponent + minexponent + precision + radix + range + tiny + + bit_size + + allocated + lbound + ubound + shape + size + + + + + + repeat + trim + + selected_int_kind + selected_real_kind + + transfer + + dot_product + matmul + + all + any + count + maxval + minval + product + sum + + pack + unpack + + reshape + + spread + + cshift + eoshift + + transpose + + maxloc + minloc + + + + + + date_and_time + system_clock + + random_number + random_seed + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/freebasic.xml b/kate/part/syntax/data/freebasic.xml new file mode 100644 index 00000000..d3bf4124 --- /dev/null +++ b/kate/part/syntax/data/freebasic.xml @@ -0,0 +1,609 @@ + + + + + + + #DEFINE + #ELSE + #ELSEIF + #ENDIF + #IF + #IFDEF + #IFNDEF + #UNDEF + $DYNAMIC + $INCLIB + $INCLUDE + $STATIC + + + + + Declare + + Open + + For + + For Input + For Output + For Binary + For Random + Close + + To + Step + Next + As + Break + Exit + If + Then + Else + Until + Select + System + Case + Default + EndSelect + Do + Loop + While + Wend + End + Type + DefType + Dim + Global + Function + Sub + Shared + Protected + Static + Declare + + Unsigned + + Data + Restore + Read + + + Goto + Gosub + Return + + DEFBYTE + DEFDBL + DEFINT + DEFLNG + DEFSHORT + DEFSNG + DEFSTR + DEFUBYTE + DEFUINT + DEFUSHORT + + + + + + + + AH + AL + AX + BH + BL + BP + BX + CH + CL + CS + CX + DH + DI + DL + DS + DX + EAX + EBP + EBX + ECX + EDI + EDX + ES + ESI + ESP + FS + GS + SI + SP + SS + + + + Integer + Long + Single + Double + String + Byte PTR + Dword PTR + Qword PTR + Word PTR + + + + AAA + AAD + AAM + AAS + ABS + ACOS + ADC + ADD + ALLOCATE + AND + AND + ARPL + ASC + ASIN + ASM + ATAN2 + ATN + BEEP + BIN$ + BLOAD + BOUND + BREAK + BSAVE + BSF + BSR + BSWAP + BT + BTC + BTR + BTS + BYREF + CALL + CALLOCATE + CALLS + CBW + CBYTE + CDBL + CDQ + CHAIN + CHDIR + CHR$ + CINT + CIRCLE + CLC + CLD + CLEAR + CLI + CLNG + CLOSE + CLTS + CMC + CMP + CMPS + CMPSB + CMPSD + CMPSW + CMPXCHG + COLOR + COMMAND$ + COMMON + CONST + CONTINUE + COS + CSHORT + CSIGN + CSNG + CUNSG + CURDIR$ + CVD + CVI + CVL + CVS + CWD + CWDE + DAA + DAS + DATA + DATE$ + DEALLOCATE + DEC + DIM + DIR$ + DIV + DRAW + END + ENTER + ENUM + ENVIRON + ENVIRON$ + EOF + EQV + ERASE + EXEC + EXEPATH + EXP + FIX + FLIP + FRE + FREEFILE + GET + GETKEY + GETMOUSE + HEX$ + HLT + IDIV + IMP + IMUL + IN + INC + INKEY$ + INP + INPUT + INPUT$ + INS + INSB + INSD + INSTR + INT + INT + INTO + INVD + INVLPG + IRET + IRETD + JA + JAE + JB + JBE + JC + JCXZ + JE + JECXZ + JG + JGE + JL + JLE + JMP + JNA + JNAE + JNB + JNBE + JNC + JNE + JNG + JNGE + JNL + JNLE + JNO + JNP + JNS + JNZ + JO + JP + JPE + JPO + JS + JUMP + JZ + KILL + LAHF + LAR + LBOUND + LCASE$ + LDS + LEA + LEAVE + LEAVED + LEAVEW + LEFT$ + LEN + LES + LET + LFS + LGDT + LGS + LIB + LIDT + LINE + LLDT + LMSW + LOC + LOCK + LOCK + LODS + LODSB + LODSD + LODSW + LOF + LOG + LOOPD + LOOPDE + LOOPDNE + LOOPDNZ + LOOPDZ + LOOPE + LOOPNE + LOOPNZ + LOOPW + LOOPWE + LOOPWNE + LOOPWNZ + LOOPWZ + LOOPZ + LSET + LSL + LSS + LTR + LTRIM$ + MID$ + MKD$ + MKDIR + MKI$ + MKL$ + MKS$ + MOD + MOV + MOVS + MOVSB + MOVSD + MOVSW + MOVSX + MOVZX + MUL + MULTIKEY + NAME + NEG + NOP + NOT + NOTHING + OCT$ + OPTION BASE + OPTION PRIVATE + OR + OUT + OUTS + OUTSB + OUTSD + OUTSW + PAINT + PALETTE + PCOPY + PEEK + PEEKI + PEEKS + PMAP + POINT + POKE + POKEI + POKES + POP + POPA + POPAD + POPF + POPFD + POS + PRESERVE + PRESET + PRINT + PRIVATE + PROCPTR + PSET + PTR + PUBLIC + PUSH + PUSHA + PUSHAD + PUSHF + PUSHFD + PUT + RANDOMIZE + RCL + RCR + REALLOCATE + REDIM + REM + REP + REPE + REPNE + REPNZ + REPZ + RESET + RET + RETURN + RGB + RIGHT$ + RMDIR + RND + ROL + ROR + RSET + RTRIM$ + RUN + SADD + SAHF + SAL + SAR + SBB + SCAS + SCASB + SCASD + SCASW + SCREEN + SCREENCOPY + SCREENINFO + SCREENLOCK + SCREENPTR + SCREENSET + SCREENUNLOCK + SEEK + SETA + SETAE + SETB + SETBE + SETC + SETDATE + SETE + SETENVIRON + SETG + SETGE + SETL + SETLE + SETNA + SETNAE + SETNB + SETNBE + SETNC + SETNE + SETNG + SETNGE + SETNL + SETNLE + SETNO + SETNP + SETNS + SETNZ + SETO + SETP + SETPE + SETPO + SETS + SETZ + SGDT + SGN + SHARED + SHELL + SHL + SHLD + SHR + SHRD + SIDT + SIN + SLDT + SLEEP + SMSW + SPACE$ + SQR + STATIC + STC + STD + STI + STOP + STOS + STOSB + STOSD + STOSW + STR + STR$ + STRING$ + SWAP + TAN + TEST + TIME$ + TIMER + TRIM$ + TYPE + UBOUND + UCASE$ + UNION + UNLOCK + VAL + VARPTR + VERR + VERW + VIEW + WAIT + WINDOWTITLE + WRITE + XADD + XCHG + XLAT + XLATB + XOR + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/fsharp.xml b/kate/part/syntax/data/fsharp.xml new file mode 100644 index 00000000..e5180e3b --- /dev/null +++ b/kate/part/syntax/data/fsharp.xml @@ -0,0 +1,287 @@ + + + + + + + + +]> + + + + + abstract + and + as + assert + base + + class + delegate + + + dowcast + downto + elif + else + + exception + extern + false + for + fun + function + functor + global + if + in + + inherit + inline + interfaece + internal + lazy + let + match + member + + mutable + namespace + new + not + null + + of + + or + override + private + public + rec + ref + return + + static + + then + to + true + try + type + upcast + use + val + void + when + while + with + yield + + + bool + byte + sbyte + int16 + uint16 + int + uint32 + int64 + uint64 + nativeint + unativeint + char + string + decimal + unit + void + float32 + single + float + double + bigint + option + seq + + + ? + + + end + + + sig + + + struct + + + object + + + begin + + + do + + + done + + + module + open + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/fstab.xml b/kate/part/syntax/data/fstab.xml new file mode 100644 index 00000000..c2e1b21c --- /dev/null +++ b/kate/part/syntax/data/fstab.xml @@ -0,0 +1,96 @@ + + + + + + + ext2 + ext3 + fat + vfat + reiser + proc + none + sysfs + ntfs + umsdos + swap + minix + iso9660 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/gap.xml b/kate/part/syntax/data/gap.xml new file mode 100644 index 00000000..5579e28f --- /dev/null +++ b/kate/part/syntax/data/gap.xml @@ -0,0 +1,7835 @@ + + + + + + + + + + + + + break + continue + do + elif + else + + + + + + then + + + + + + and + fail + false + not + or + true + + + + + + in + local + rec + return + + + + mod + + + + AClosVecLib + AClosestVectorCombinationsMatFFEVecFFE + AClosestVectorCombinationsMatFFEVecFFECoords + AClosestVectorDriver + ANFAutomorphism + ANonReesCongruenceOfSemigroup + APolyProd + AbelianGroup + AbelianGroupCons + AbelianInvariants + AbelianInvariantsMultiplier + AbelianInvariantsNormalClosureFpGroup + AbelianInvariantsNormalClosureFpGroupRrs + AbelianInvariantsOfList + AbelianInvariantsSubgroupFpGroup + AbelianInvariantsSubgroupFpGroupMtc + AbelianInvariantsSubgroupFpGroupRrs + AbelianNumberField + AbelianNumberFieldByReducedGaloisStabilizerInfo + AbelianPQuotient + AbelianSubfactorAction + AbsAndIrredModules + AbsInt + AbsolutIrreducibleModules + AbsoluteIrreducibleModules + AbsoluteValue + AbstractWordTietzeWord + AbstractWordTzWord + ActingAlgebra + ActingDomain + Action + ActionAbelianCSPG + ActionHomomorphism + ActionHomomorphismAttr + ActionHomomorphismConstructor + ActionKernelExternalSet + ActionSubspacesElementaryAbelianGroup + ActorOfExternalSet + Add + AddAbelianRelator + AddCoeffs + AddCosetInfoStabChain + AddDictionary + AddEquationsSQ + AddGenerator + AddGenerators + AddGeneratorsExtendSchreierTree + AddGeneratorsGenimagesExtendSchreierTree + AddHashEntry + AddImage + AddImageNC + AddNaturalHomomorphismsPool + AddNormalizingElementPcgs + AddPageNumbersToSix + AddParagraphNumbersGapDocTree + AddRefinement + AddRelator + AddRootParseTree + AddRowVector + AddRule + AddRuleReduced + AddSet + AddToListEntries + AddVectorLTM + AddendumSCTable + AdditiveCoset + AdditiveElementAsMultiplicativeElement + AdditiveElementsAsMultiplicativeElementsFamily + AdditiveGroup + AdditiveGroupByGenerators + AdditiveInverse + AdditiveInverseAttr + AdditiveInverseImmutable + AdditiveInverseMutable + AdditiveInverseOp + AdditiveInverseSM + AdditiveInverseSameMutability + AdditiveMagma + AdditiveMagmaByGenerators + AdditiveMagmaWithInverses + AdditiveMagmaWithInversesByGenerators + AdditiveMagmaWithZero + AdditiveMagmaWithZeroByGenerators + AdditiveNeutralElement + AdditivelyActingDomain + AdjointAssociativeAlgebra + AdjointBasis + AdjointMatrix + AdjointModule + AffineAction + AffineActionByMatrixGroup + AffineActionLayer + AffineOperation + AffineOperationLayer + Agemo + AgemoAbove + AgemoOp + AlgExtElm + AlgExtEmbeddedPol + AlgExtFactSQFree + AlgExtSquareHensel + AlgFacUPrep + Algebra + AlgebraByGenerators + AlgebraByStructureConstants + AlgebraByStructureConstantsArg + AlgebraGeneralMappingByImages + AlgebraHomomorphismByImages + AlgebraHomomorphismByImagesNC + AlgebraWithOne + AlgebraWithOneByGenerators + AlgebraWithOneGeneralMappingByImages + AlgebraWithOneHomomorphismByImages + AlgebraWithOneHomomorphismByImagesNC + AlgebraicElementsFamilies + AlgebraicElementsFamily + AlgebraicExtension + AlgebraicPolynomialModP + AllBlocks + AllGroups + AllIrreducibleMonicPolynomialCoeffsOfDegree + AllIrreducibleMonicPolynomials + AllIrreducibleSolvableGroups + AllLibTomNames + AllModulesSQ + AllMonicPolynomialCoeffsOfDegree + AllPrimitiveGroups + AllSmallGroups + AllTransitiveGroups + AllowableSubgroup + Alpha + AlternatingDegree + AlternatingGroup + AlternatingGroupCons + AlternatingSubgroup + AntiSymmetricParts + Append + AppendCollectedList + AppendTo + ApplicableMethod + ApplicableMethodTypes + Apply + ApplyGeneratorsToPoint + ApplyGeneratorsToPointsList + ApplyRel + ApplySimpleReflection + ApplyToNodesParseTree + ApproxRational + ApproxRootBound + ApproximateRoot + ApproximateSuborbitsStabilizerPermGroup + ArithmeticElementCreator + Arrangements + ArrangementsA + ArrangementsK + AsAlgebra + AsAlgebraWithOne + AsBinaryRelationOnPoints + AsBlockMatrix + AsCharacterMorphismFunction + AsDivisionRing + AsDuplicateFreeList + AsFLMLOR + AsFLMLORWithOne + AsField + AsFreeLeftModule + AsGroup + AsGroupGeneralMappingByImages + AsInducedPcgs + AsLeftIdeal + AsLeftMagmaIdeal + AsLeftModule + AsLeftModuleGeneralMappingByImages + AsLieAlgebra + AsList + AsListOfFreeLeftModule + AsListSorted + AsMagma + AsMagmaIdeal + AsMonoid + AsNearRing + AsPerm + AsPlist + AsPolynomial + AsRightIdeal + AsRightMagmaIdeal + AsRing + AsSSortedList + AsSSortedListList + AsSSortedListNonstored + AsSemigroup + AsSemiring + AsSemiringWithOne + AsSemiringWithOneAndZero + AsSemiringWithZero + AsSet + AsSortedList + AsSubFLMLOR + AsSubFLMLORWithOne + AsSubalgebra + AsSubalgebraWithOne + AsSubgroup + AsSubgroupOfWholeGroupByQuotient + AsSubmagma + AsSubmonoid + AsSubsemigroup + AsSubspace + AsTransformation + AsTransformationNC + AsTwoSidedIdeal + AsVectorSpace + AscendingChain + AscendingChainOp + Assert + AssertionLevel + AssignGeneratorVariables + AssignNiceMonomorphismAutomorphismGroup + AssocBWorLetRepPow + AssocWWorLetRepPow + AssocWord + AssocWordByLetterRep + AssociatedPartition + AssociatedReesMatrixSemigroupOfDClass + AssociatedSemigroup + Associates + AtlasIrrationality + AttributeMethodByNiceMonomorphism + AttributeMethodByNiceMonomorphismCollColl + AttributeMethodByNiceMonomorphismCollElm + AttributeMethodByNiceMonomorphismElmColl + AttributeValueNotSet + AugmentationIdeal + AugmentedCosetTableInWholeGroup + AugmentedCosetTableMtc + AugmentedCosetTableMtcInWholeGroup + AugmentedCosetTableNormalClosure + AugmentedCosetTableNormalClosureInWholeGroup + AugmentedCosetTableRrs + AugmentedCosetTableRrsInWholeGroup + AutoloadPackages + AutomorphismDomain + AutomorphismGroup + AutomorphismGroupAbelianGroup + AutomorphismGroupElAbGroup + AutomorphismGroupFrattFreeGroup + AutomorphismGroupPermGroup + AutomorphismGroupSolvableGroup + AutomorphismRepresentingGroup + AutomorphismsOfTable + AvoidedLayers + BPolyProd + BagStats + BarPartitions + BaseFixedSpace + BaseImage + BaseIntMat + BaseIntersectionIntMats + BaseMat + BaseMatDestructive + BaseOfBasicImageGroup + BaseOfElt + BaseOfGroup + BaseOrthogonalSpaceMat + BasePoint + BasePointOfSchreierTransversal + BaseShortVectors + BaseStabChain + BaseSteinitzVectors + BasicImageEltRepFamily + BasicImageGroup + BasicImageGroupElement + BasicWreathProductOrdering + BasicWreathProductOrderingNC + Basis + BasisForFreeModuleByNiceBasis + BasisNC + BasisNullspaceModN + BasisOfAlgebraModule + BasisOfHomCosetAddMatrixGroup + BasisOfHomCosetAddMatrixGroupFnc + BasisOfMonomialSpace + BasisOfSparseRowSpace + BasisOfWeightRepSpace + BasisVectors + BasisVectorsForMatrixAction + BasisWithReplacedLeftModule + BaumClausenInfo + BaumClausenInfoDebug + BeauzamyBound + BeauzamyBoundGcd + Bell + Bernoulli + BestQuoInt + BestSplittingMatrix + BetaSet + BiAlgebraModule + BiAlgebraModuleByGenerators + BilinearFormMat + BinaryRelationByElements + BinaryRelationByListOfImages + BinaryRelationByListOfImagesNC + BinaryRelationOnPoints + BinaryRelationOnPointsNC + BinaryRelationTransformation + BindGlobal + Binomial + BlistList + BlistStringDecode + BlockMatrix + BlockStabilizer + Blocks + BlocksAttr + BlocksInfo + BlocksOp + BlowUpCocycleSQ + BlowUpIsomorphism + BlownUpMat + BlownUpMatrix + BlownUpModule + BlownUpVector + BombieriNorm + BooleanFamily + BrauerCharacterValue + BrauerTable + BrauerTableOp + BravaisGroup + BravaisSubgroups + BravaisSupergroups + BuildIsomorphismReesMatrixSemigroupWithMap + CIUnivPols + COAffineBlocks + COComplements + COComplementsMain + CONextCentral + CONextCentralizer + CONextCocycles + CONextComplements + CalcDoubleCosets + CalcOrder + CallFuncList + CallFuncTrapError + CanComputeIndex + CanComputeIsSubset + CanComputeSize + CanComputeSizeAnySubgroup + CanEasilyCompareElements + CanEasilyCompareElementsFamily + CanEasilyComputePcgs + CanEasilySortElements + CanEasilySortElementsFamily + CanEasilyTestMembership + CanFindGeneratorOfCyclicGroup + CanFindNilpotentClassTwoElement + CanonicalBasis + CanonicalElt + CanonicalGenerators + CanonicalNiceMonomorphism + CanonicalPcElement + CanonicalPcgs + CanonicalPcgsByGeneratorsWithImages + CanonicalPcgsWrtFamilyPcgs + CanonicalPcgsWrtHomePcgs + CanonicalPcgsWrtSpecialPcgs + CanonicalRelator + CanonicalRepresentativeDeterminatorOfExternalSet + CanonicalRepresentativeOfExternalSet + CanonicalRightCosetElement + CanonicalSubgroupRepresentativePcGroup + CartanMatrix + CartanSubalgebra + Cartesian + CasesCSPG + CategoriesOfObject + CategoryCollections + CategoryFamily + CayleyGraphDualSemigroup + CayleyGraphSemigroup + Cell + CellNoPoint + CellNoPoints + Cells + Center + CenterOfCharacter + CentralCharacter + CentralIdempotentsOfAlgebra + CentralIdempotentsOfSemiring + CentralNormalSeriesByPcgs + CentralProductOfMatrixGroups + CentralRelations + CentralStelClEANSNonsolv + CentralStepClEANS + CentralStepConjugatingElement + CentralStepRatClPGroup + Centralizer + CentralizerInAssociativeGaussianMatrixAlgebra + CentralizerInFiniteDimensionalAlgebra + CentralizerInGLnZ + CentralizerInParent + CentralizerModulo + CentralizerNormalCSPG + CentralizerNormalTransCSPG + CentralizerOp + CentralizerOrder + CentralizerSizeLimitConsiderFunction + CentralizerSolvableGroup + CentralizerTransSymmCSPG + CentralizerWreath + Centre + CentreFromSCTable + CentreOfCharacter + CentrePcGroup + ChaNuPol + ChainHomomorphicImage + ChainStatistics + ChainSubgroup + ChainSubgroupByDirectProduct + ChainSubgroupByHomomorphism + ChainSubgroupByPSubgroupOfAbelian + ChainSubgroupByProjectionFunction + ChainSubgroupBySiftFunction + ChainSubgroupByStabiliser + ChainSubgroupByStabilizer + ChainSubgroupByTrivialSubgroup + ChainSubgroupQuotient + ChangeStabChain + ChangeTypeObj + ChangedBaseGroup + CharTableAlternating + CharTableDoubleCoverAlternating + CharTableDoubleCoverSymmetric + CharTableSymmetric + CharTableWeylB + CharTableWeylD + CharValueDoubleCoverSymmetric + CharValueSymmetric + CharValueWeylB + CharValueWreathSymmetric + Character + CharacterDegreePool + CharacterDegrees + CharacterMorphismGroup + CharacterMorphismOrbits + CharacterNames + CharacterParameters + CharacterString + CharacterTable + CharacterTableDirectProduct + CharacterTableDisplayPrintLegendDefault + CharacterTableDisplayStringEntryDataDefault + CharacterTableDisplayStringEntryDefault + CharacterTableFactorGroup + CharacterTableFromLibrary + CharacterTableHeadOfFactorGroupByFusion + CharacterTableIsoclinic + CharacterTableOfNormalSubgroup + CharacterTableQuaternionic + CharacterTableRegular + CharacterTableWithSortedCharacters + CharacterTableWithSortedClasses + CharacterTableWreathSymmetric + Characteristic + CharacteristicPolynomial + CharacteristicPolynomialMatrixNC + CharsFamily + CheckAndCleanGapDocTree + CheckAuto + CheckCompletionFiles + CheckConsistencyOfDefinitions + CheckCosetTableFpGroup + CheckFixedPoints + CheckForHandlingByNiceBasis + CheckGlobalName + CheckPackageLoading + CheckPermChar + CheckSchreierTreeInternalConsistency + ChevalleyBasis + ChiefNormalSeriesByPcgs + ChiefSeries + ChiefSeriesOfGroup + ChiefSeriesThrough + ChiefSeriesUnderAction + ChineseRem + Chomp + ChooseNextBasePoint + ClassComparison + ClassElementLargeGroup + ClassElementLattice + ClassElementSmallGroup + ClassFunction + ClassFunctionSameType + ClassMultiplicationCoefficient + ClassNames + ClassNamesTom + ClassNumbersElements + ClassOrbit + ClassParameters + ClassPermutation + ClassPositionsOfAgemo + ClassPositionsOfCentre + ClassPositionsOfDerivedSubgroup + ClassPositionsOfDirectProductDecompositions + ClassPositionsOfElementaryAbelianSeries + ClassPositionsOfFittingSubgroup + ClassPositionsOfKernel + ClassPositionsOfLowerCentralSeries + ClassPositionsOfMaximalNormalSubgroups + ClassPositionsOfNormalClosure + ClassPositionsOfNormalSubgroup + ClassPositionsOfNormalSubgroups + ClassPositionsOfSolvableResiduum + ClassPositionsOfSupersolvableResiduum + ClassPositionsOfUpperCentralSeries + ClassRepsPermutedTuples + ClassRoots + ClassStructureCharTable + ClassTypesTom + ClassesSolvableGroup + CleanedTailPcElement + ClearCacheStats + ClearCentralRelations + ClearDefinitionNC + ClearPQuotientStatistics + ClearProfile + CloseMutableBasis + CloseNaturalHomomorphismsPool + CloseStream + ClosedStreamType + ClosureAdditiveGroup + ClosureAdditiveMagmaDefault + ClosureAdditiveMagmaWithInverses + ClosureAlgebra + ClosureDivisionRing + ClosureField + ClosureGroup + ClosureGroupAddElm + ClosureGroupCompare + ClosureGroupDefault + ClosureGroupIntest + ClosureLeftModule + ClosureLeftOperatorRing + ClosureMagmaDefault + ClosureNearAdditiveGroup + ClosureNearAdditiveMagmaWithInverses + ClosureRandomPermGroup + ClosureRing + ClosureSemiring + ClosureSubgroup + ClosureSubgroupNC + CntOp + CoKernel + CoKernelGensIterator + CoKernelGensPermHom + CoKernelOfAdditiveGeneralMapping + CoKernelOfMultiplicativeGeneralMapping + CoSuFp + Coboundaries + CocGroup + Cochain + CochainSpace + CocycleSQ + CocycleToRelVector + Cocycles + CodeGenerators + CodePcGroup + CodePcgs + CoefficientTaylorSeries + Coefficients + CoefficientsAndMagmaElements + CoefficientsFamily + CoefficientsMultiadic + CoefficientsOfLaurentPolynomial + CoefficientsOfUnivariateLaurentPolynomial + CoefficientsOfUnivariatePolynomial + CoefficientsOfUnivariateRationalFunction + CoefficientsOfVector + CoefficientsQadic + CoefficientsRing + CoeffsCyc + CoeffsMod + CollFamRangeEqFamElms + CollFamSourceEqFamElms + CollapsedMat + CollectPolycyclic + CollectUEALatticeElement + CollectWord + CollectWordOrFail + Collected + CollectedPartition + CollectedWordSQ + CollectionsFamily + CollectorSQ + ColorPrompt + ColumnIndexOfReesMatrixSemigroupElement + ColumnIndexOfReesZeroMatrixSemigroupElement + ColumnsOfReesMatrixSemigroup + ColumnsOfReesZeroMatrixSemigroup + Combinations + CombinationsA + CombinationsK + CombinatorialCollector + CombinatorialCollectorByGenerators + CombinatoricSplit + Comm + CommutGenImgs + CommutativeDiagram + CommutatorFactorGroup + CommutatorLength + CommutatorSubgroup + Compacted + CompanionMat + CompareVersionNumbers + CompatibleConjugacyClasses + CompatibleConjugacyClassesDefault + CompatiblePairs + CompileFunc + ComplementIntMat + ComplementSystem + Complementclasses + ComplementclassesEA + ComplementclassesSolvableNC + ComplementclassesSolvableWBG + CompleteChain + CompleteGaloisGroupPElement + CompleteOrdersOfRws + CompleteSchreierTransversal + CompletionBar + ComplexConjugate + ComplexificationQuat + ComponentsOfTuplesFamily + ComposedXMLString + CompositionMapping + CompositionMaps + CompositionOfStraightLinePrograms + CompositionSeries + Compress + ComputeTails + ComputedAgemos + ComputedAscendingChains + ComputedBrauerTables + ComputedClassFusions + ComputedCyclicExtensionsTom + ComputedHallSubgroups + ComputedIndicators + ComputedIsPNilpotents + ComputedIsPSolvableCharacterTables + ComputedIsPSolvables + ComputedOmegas + ComputedPCentralSeriess + ComputedPCores + ComputedPRumps + ComputedPowerMaps + ComputedPrimeBlockss + ComputedSylowComplements + ComputedSylowSubgroups + ConcatSubos + Concatenation + Conductor + ConfluentRws + Congruences + ConjugacyClass + ConjugacyClassSubgroups + ConjugacyClasses + ConjugacyClassesByOrbits + ConjugacyClassesByRandomSearch + ConjugacyClassesFittingFreeGroup + ConjugacyClassesForSmallGroup + ConjugacyClassesMaximalSubgroups + ConjugacyClassesOfNaturalGroup + ConjugacyClassesPerfectSubgroups + ConjugacyClassesSubgroups + ConjugacyClassesSubwreath + ConjugacyClassesTry + ConjugacyClassesViaRadical + ConjugateDominantWeight + ConjugateDominantWeightWithWord + ConjugateGroup + ConjugateMatrixActionToLinearAction + ConjugateMatrixGroupToLinearAction + ConjugateMatrixGroupToLinearGroup + ConjugateStabChain + ConjugateSubgroup + ConjugateSubgroups + ConjugatedModule + Conjugates + ConjugatingElement + ConjugatorAutomorphism + ConjugatorAutomorphismNC + ConjugatorInnerAutomorphism + ConjugatorIsomorphism + ConjugatorOfConjugatorIsomorphism + ConnectGroupAndCharacterTable + ConsiderKernels + ConsiderSmallerPowerMaps + ConsiderStructureConstants + ConsiderTableAutomorphisms + ConstantInBaseRingPol + ConstantTimeAccessList + ConstituentsCompositionMapping + ConstituentsOfCharacter + ConstituentsPolynomial + ContainedCharacters + ContainedDecomposables + ContainedMaps + ContainedPossibleCharacters + ContainedPossibleVirtualCharacters + ContainedSpecialVectors + ContainedTom + ContainingTom + ContinuedFractionApproximationOfRoot + ContinuedFractionExpansionOfRoot + ConvertBasicImageGroupElement + ConvertToCharacterTable + ConvertToCharacterTableNC + ConvertToLibTom + ConvertToLibraryCharacterTableNC + ConvertToMatrixRep + ConvertToMatrixRepNC + ConvertToNormalFormMonomialElement + ConvertToRangeRep + ConvertToSiftGroup + ConvertToStringRep + ConvertToTableOfMarks + ConvertToVectorRep + ConvertToVectorRepNC + ConwayCandidates + ConwayPol + ConwayPolynomial + CopiedAugmentedCosetTable + CopyMappingAttributes + CopyOptionsDefaults + CopyRel + CopyStabChain + Core + CoreInParent + CoreOp + CorestEval + CorrectConjugacyClass + CorrespondingGeneratorsByModuloPcgs + CorrespondingPermutations + CosetLeadersInner + CosetLeadersMatFFE + CosetNumber + CosetRepAsWord + CosetTable + CosetTableBySubgroup + CosetTableDefaultLimit + CosetTableDefaultMaxLimit + CosetTableFpHom + CosetTableFromGensAndRels + CosetTableInWholeGroup + CosetTableNormalClosure + CosetTableNormalClosureInWholeGroup + CosetTableOfFpSemigroup + CosetTableStandard + CoveringTriplesCharacters + CrcFile + CreateAllCycleStructures + CreateCompletionFiles + CreateKnuthBendixRewritingSystem + CreateOrderingByLtFunction + CreateOrderingByLteqFunction + CrystGroupDefaultAction + CurrentAssertionLevel + CycList + Cycle + CycleByPosOp + CycleIndex + CycleIndexOp + CycleLength + CycleLengthOp + CycleLengthPermInt + CycleLengths + CycleLengthsOp + CycleOp + CyclePermInt + CycleStructureClass + CycleStructurePerm + CycleStructuresGroup + Cycles + CyclesOp + CyclicExtensionsTom + CyclicExtensionsTomOp + CyclicGroup + CyclicGroupCons + CyclicTopExtensions + CyclotomicField + CyclotomicPol + CyclotomicPolynomial + Cyclotomics + CyclotomicsFamily + DMYDay + DMYhmsSeconds + DTCommutator + DTConjugate + DTMultiply + DTPower + DTQuotient + DTSolution + DataObj + DataType + DayDMY + DaysInMonth + DaysInYear + DeclareAttribute + DeclareAttributeKernel + DeclareAttributeSuppCT + DeclareAutoPackage + DeclareAutoreadableVariables + DeclareCategory + DeclareCategoryCollections + DeclareCategoryFamily + DeclareCategoryKernel + DeclareComponent + DeclareConstructor + DeclareConstructorKernel + DeclareFilter + DeclareGlobalFunction + DeclareGlobalVariable + DeclareHandlingByNiceBasis + DeclareInfoClass + DeclareOperation + DeclareOperationKernel + DeclarePackage + DeclarePackageAutoDocumentation + DeclarePackageDocumentation + DeclareProperty + DeclarePropertyKernel + DeclarePropertySuppCT + DeclareRepresentation + DeclareRepresentationKernel + DeclareSynonym + DeclareSynonymAttr + DecodeTree + DecodedTreeEntry + DecomposeEltIntoPElts + DecomposeTensorProduct + DecomposedFixedPointVector + DecomposedRationalClass + Decomposition + DecompositionInt + DecompositionMatrix + DecompositionTypes + DecompositionTypesOfGroup + Decreased + DeepThoughtCollector + DeepThoughtCollectorByGenerators + DefaultField + DefaultFieldByGenerators + DefaultFieldOfMatrix + DefaultFieldOfMatrixGroup + DefaultHashLength + DefaultPackageBannerString + DefaultRing + DefaultRingByGenerators + DefaultSparseHashRepType + DefaultSparseHashWithIKRepType + DefaultStabChainOptions + DefectApproximation + DefineNewGenerators + DefiningPcgs + DefiningPolynomial + DefiningQuotientHomomorphism + Degree + DegreeAction + DegreeFFE + DegreeIndeterminate + DegreeNaturalHomomorphismsPool + DegreeOfBinaryRelation + DegreeOfCharacter + DegreeOfLaurentPolynomial + DegreeOfMatrixGroup + DegreeOfTransformation + DegreeOfTransformationSemigroup + DegreeOfUnivariateLaurentPolynomial + DegreeOperation + DegreeOverPrimeField + DeleteImage + Delta + DenominatorCyc + DenominatorOfModuloPcgs + DenominatorOfRationalFunction + DenominatorRat + DenseHashTable + DenseIntKey + DepthOfPcElement + DepthOfUpperTriangularMatrix + DepthSchreierTrees + Derangements + DerangementsK + Derivations + Derivative + DerivedLength + DerivedSeries + DerivedSeriesOfGroup + DerivedSubgroup + DerivedSubgroupTom + DerivedSubgroupsTom + DerivedSubgroupsTomPossible + DerivedSubgroupsTomUnique + DescendingListWithElementRemoved + DescriptionOfNormalizedUEAElement + DescriptionOfRootOfUnity + Determinant + DeterminantIntMat + DeterminantMat + DeterminantMatDestructive + DeterminantMatDivFree + DeterminantOfCharacter + DiagonalMat + DiagonalOfMat + DiagonalSocleAction + DiagonalizeIntMat + DiagonalizeIntMatNormDriven + DiagonalizeMat + DictionariesFamily + DictionaryByList + DictionaryByPosition + DictionaryBySort + DiffCoc + Difference + DifferenceBlist + DifferenceLists + DifferenceOfPcElement + DigitsNumber + DihedralGenerators + DihedralGroup + DihedralGroupCons + Dimension + DimensionOfHighestWeightModule + DimensionOfMatrixGroup + DimensionOfVectors + DimensionsLoewyFactors + DimensionsMat + DirProdTransversal + DirectFactorsOfGroup + DirectProduct + DirectProductDecompositionsLocal + DirectProductInfo + DirectProductOp + DirectSumDecomposition + DirectSumMat + DirectSumOfAlgebraModules + DirectSumOfAlgebras + DirectoriesFamily + DirectoriesLibrary + DirectoriesPackageLibrary + DirectoriesPackagePrograms + DirectoriesSystemPrograms + Directory + DirectoryContents + DirectoryCurrent + DirectoryTemporary + DirectoryType + DisableAttributeValueStoring + Discriminant + Display + DisplayCacheStats + DisplayCompositionSeries + DisplayEggBoxOfDClass + DisplayImfInvariants + DisplayImfReps + DisplayInformationPerfectGroups + DisplayOptions + DisplayOptionsStack + DisplayProfile + DisplayRevision + DisplaySemigroup + DisplayXMLStructure + DistVecClosVecLib + DistanceVecFFE + DistancesDistributionMatFFEVecFFE + DistancesDistributionVecFFEsVecFFE + DivisionRingByGenerators + DivisorsInt + DivisorsIntCache + DixonInit + DixonRecord + DixonRepChi + DixonRepGHchi + DixonSplit + DixontinI + DnLattice + DnLatticeIterative + DoAlgebraicExt + DoCentralSeriesPcgsIfNilpot + DoCheapActionImages + DoCheapOperationImages + DoClosurePrmGp + DoEASLS + DoExponentsConjLayerFampcgs + DoFactorCosetAction + DoGaloisType + DoImmutableMatrix + DoInducedPcgsByPcSequenceNC + DoLogModRho + DoLowIndexSubgroupsFpGroup + DoMulExt + DoNFIM + DoNormalClosurePermGroup + DoNormalizerSA + DoPcgsElementaryAbelianSeries + DoPcgsOrbitOp + DoPrintUnivariateLaurent + DoReadPkg + DoRereadPkg + DoRightTransversalPc + DoShortwordBasepoint + DoSnAnGiantTest + DoSparseActionHomomorphism + DoSparseLinearActionOnFaithfulSubset + DoTest + DoUnivTestRatfun + Domain + DomainByGenerators + DomainForAction + DominantCharacter + DominantWeights + DoubleCentralizerOrbit + DoubleCoset + DoubleCosetRepsAndSizes + DoubleCosets + DoubleCosetsNC + DoubleCosetsPcGroup + DoubleHashArraySize + DoubleHashDictSize + DownEnv + DualGModule + DumpWorkspace + DuplicateFreeList + DxActiveCols + DxCalcAllPowerMaps + DxCalcPrimeClasses + DxDegreeCandidates + DxEigenbase + DxFrobSchurInd + DxGaloisOrbits + DxGeneratePrimeCyclotomic + DxIncludeIrreducibles + DxIsInSpace + DxLiftCharacter + DxLinearCharacters + DxModProduct + DxModularValuePol + DxNiceBasis + DxPreparation + DxRegisterModularChar + DxSplitDegree + EANormalSeriesByPcgs + EAPrimeLayerSQ + ERepAssWorInv + ERepAssWorProd + ERepLettWord + Earns + Edit + EggBoxOfDClass + EichlerTransformation + Eigenspaces + Eigenvalues + EigenvaluesChar + Eigenvectors + ElementByRws + ElementOfFpAlgebra + ElementOfFpGroup + ElementOfFpMonoid + ElementOfFpSemigroup + ElementOfMagmaRing + ElementOrdersPowerMap + ElementProperty + ElementTestFunction + ElementaryAbelianGroup + ElementaryAbelianGroupCons + ElementaryAbelianSeries + ElementaryAbelianSeriesLargeSteps + ElementaryAbelianSubseries + ElementaryDivisorsMat + ElementaryDivisorsMatDestructive + Elements + ElementsFamily + ElementsStabChain + EliminatedWord + EliminationOrdering + ElmDivRingElm + ElmTimesRingElm + ElmWPObj + ElsymsPowersums + Embedding + EmptyBinaryRelation + EmptyMatrix + EmptyRBase + EmptyRowVector + EmptySCTable + EmptyStabChain + EmptyTuplesFamily + EnableAttributeValueStoring + End + EndoMappingByTransformation + EnumerateIndependentAbelianProducts + Enumerator + EnumeratorByBasis + EnumeratorByFunctions + EnumeratorByPcgs + EnumeratorOfAdditiveMagma + EnumeratorOfGroup + EnumeratorOfIdeal + EnumeratorOfMagma + EnumeratorOfMagmaIdeal + EnumeratorOfNormedRowVectors + EnumeratorOfPrimeField + EnumeratorOfRing + EnumeratorOfSemigroupIdeal + EnumeratorOfSubset + EnumeratorOfTrivialAdditiveMagmaWithZero + EnumeratorOfTrivialMagmaWithOne + EnumeratorOfTuples + EnumeratorOfZmodnZ + EnumeratorSorted + EnvelopingAlgebra + EpiPcByModpcgs + EpimorphismNilpotentQuotient + EpimorphismNilpotentQuotientOp + EpimorphismPGroup + EpimorphismQuotientSystem + EpimorphismSchurCover + EqualBoxedObj + EquivalenceClassOfElement + EquivalenceClassOfElementNC + EquivalenceClassRelation + EquivalenceClasses + EquivalenceRelationByPairs + EquivalenceRelationByPairsNC + EquivalenceRelationByPartition + EquivalenceRelationByPartitionNC + EquivalenceRelationByProperty + EquivalenceRelationByRelation + EquivalenceRelationPartition + EquivalenceType + Error + ErrorCount + EspaceBasePoints + EuclideanDegree + EuclideanQuotient + EuclideanRemainder + EulerianFunction + EulerianFunctionByTom + EvalF + EvalFpCoc + EvalStraightLineProgElm + EvalString + EvaluateConsistency + EvaluateOverlapANA + EvaluateOverlapBAN + EvaluateOverlapBNA + EvaluateOverlapCBA + EvaluateRelation + EvaluateRelators + EvectBasePoints + ExactSizeConsiderFunction + ExcludeFromAutoload + ExcludedOrders + Exec + ExecuteProcess + ExpPcElmSortedFun + Exponent + ExponentOfPcElement + ExponentOfPowering + ExponentSumWord + ExponentSums + ExponentSyllable + ExponentsConjugateLayer + ExponentsOfCommutator + ExponentsOfConjugate + ExponentsOfPcElement + ExponentsOfPcElementPermGroup + ExponentsOfRelativePower + ExportToKernelFinished + ExtOrbStabDom + ExtRepByTailVector + ExtRepDenominatorRatFun + ExtRepNumeratorRatFun + ExtRepOfObj + ExtRepPolynomialRatFun + ExtendRepresentation + ExtendSchreierTransversal + ExtendSchreierTransversalShortCube + ExtendSchreierTransversalShortTree + ExtendSeriesPermGroup + ExtendStabChain + ExtendToBasis + ExtendedGroup + ExtendedIntersectionSumPcgs + ExtendedPcgs + ExtendedT + ExtendedVectors + Extension + ExtensionNC + ExtensionOnBlocks + ExtensionRepresentatives + ExtensionSQ + Extensions + ExtensionsOfModule + ExteriorPower + ExteriorPowerOfAlgebraModule + ExternalOrbit + ExternalOrbitOp + ExternalOrbits + ExternalOrbitsStabilizers + ExternalSet + ExternalSetByFilterConstructor + ExternalSetByTypeConstructor + ExternalSubset + ExternalSubsetOp + Extract + ExtraspecialGroup + ExtraspecialGroupCons + FFEFamily + FFPFactors + FFPOrderKnownDividend + FFPPowerModCheck + FFPUpperBoundOrder + FLMLORByGenerators + FLMLORFromFFE + FLMLORWithOne + FLMLORWithOneByGenerators + FMRRemoveZero + FactorCosetAction + FactorCosetOperation + FactorFreeAlgebraByRelators + FactorFreeGroupByRelators + FactorFreeMonoidByRelations + FactorFreeSemigroupByRelations + FactorGroup + FactorGroupFpGroupByRels + FactorGroupNC + FactorGroupNormalSubgroupClasses + FactorGroupTom + FactorSemigroup + FactorSemigroupByClosure + Factorial + Factorization + Factors + FactorsCommonDegreePol + FactorsInt + FactorsOfDirectProduct + FactorsRho + FactorsSquarefree + FaithfulModule + FamElmEqFamRange + FamElmEqFamSource + FamMapFamSourceFamRange + FamRangeEqFamElm + FamRangeNotEqFamElm + FamSourceEqFamElm + FamSourceNotEqFamElm + FamSourceRgtEqFamsLft + FamiliesOfGeneralMappingsAndRanges + FamiliesOfRows + FamilyForOrdering + FamilyForRewritingSystem + FamilyObj + FamilyOfFamilies + FamilyOfTypes + FamilyPcgs + FamilyRange + FamilySource + FamilyType + FastExtSQ + Fibonacci + FibonacciGroup + Field + FieldByGenerators + FieldExtension + FieldOfMatrixGroup + FieldOfMatrixList + FieldOverItselfByGenerators + FileDescriptorOfStream + FileString + Filename + FilterSGMLMarkup + Filtered + FilteredOp + FinIndexCyclicSubgroupGenerator + FindActionKernel + FindBag + FindLayer + FindNewReps + FindNormalCSPG + FindOperationKernel + FindRegularNormalCSPG + FindWindowId + Fingerprint + FingerprintFF + FingerprintLarge + FingerprintMedium + FingerprintPerm + FingerprintSmall + FiniteField + First + FirstOp + FittingSubgroup + FixcellPoint + Fixcells + FixcellsCell + FixedPointSpace + FixpointCellNo + FlagsFamily + FlagsObj + FlagsType + Flat + FlatBlockMat + FlatHashParams + FloatsFamily + FlushCaches + ForAll + ForAllOp + ForAny + ForAnyOp + FormatParagraph + FormattedString + FpAlgebraByGeneralizedCartanMatrix + FpElmComparisonMethod + FpElmEqualityMethod + FpElmKBRWS + FpGroupPcGroupSQ + FpGroupPresentation + FpGrpMonSmgOfFpGrpMonSmgElement + FpLieAlgebraByCartanMatrix + FpLieAlgebraEnumeration + FpMonoidOfElementOfFpMonoid + FpOfModules + FpSemigroupOfElementOfFpSemigroup + FptoSCAMorphismImageElm + FrattiniSubgroup + FrattinifactorId + FrattinifactorSize + FreeAlgebra + FreeAlgebraConstructor + FreeAlgebraOfFpAlgebra + FreeAlgebraWithOne + FreeAssociativeAlgebra + FreeAssociativeAlgebraWithOne + FreeGeneratorsOfFpAlgebra + FreeGeneratorsOfFpGroup + FreeGeneratorsOfFpMonoid + FreeGeneratorsOfFpSemigroup + FreeGeneratorsOfWholeGroup + FreeGroup + FreeGroupOfBasicImageGroup + FreeGroupOfElt + FreeGroupOfFpGroup + FreeLeftModule + FreeLieAlgebra + FreeMagma + FreeMagmaRing + FreeMagmaWithOne + FreeMonoid + FreeMonoidNatHomByGeneratorsNC + FreeMonoidOfFpMonoid + FreeMonoidOfRewritingSystem + FreeSemigroup + FreeSemigroupNatHomByGeneratorsNC + FreeSemigroupOfFpSemigroup + FreeSemigroupOfRewritingSystem + FreeStructureOfRewritingSystem + FrobeniusAutomorphism + FrobeniusAutomorphismI + FrobeniusCharacterValue + FullMatrixAlgebra + FullMatrixAlgebraCentralizer + FullMatrixFLMLOR + FullMatrixLieAlgebra + FullMatrixLieFLMLOR + FullMatrixModule + FullMatrixSpace + FullRowModule + FullRowSpace + FullSparseRowSpace + FullTransformationSemigroup + FunctionAction + FunctionsFamily + FusionCharTableTom + FusionConjugacyClasses + FusionConjugacyClassesOp + FusionRationalClassesPSubgroup + FusionsAllowedByRestrictions + FusionsOfLibTom + FusionsToLibTom + FusionsTom + GAPDocManualLab + GAPInfo + GInverses + GLDegree + GLUnderlyingField + GModuleByMats + GPartitions + GPartitionsEasy + GPartitionsGreatestEQ + GPartitionsGreatestEQHelper + GPartitionsGreatestLE + GPartitionsGreatestLEEasy + GPartitionsNrParts + GPartitionsNrPartsHelper + GQuotients + GaloisConjugates + GaloisCyc + GaloisDiffResolvent + GaloisField + GaloisGroup + GaloisMat + GaloisSetResolvent + GaloisStabilizer + GaloisType + GapInputPcGroup + GapInputSCTable + GasmanLimits + GasmanMessageStatus + GasmanStatistics + GaussianIntegers + GaussianRationals + Gcd + GcdCoeffs + GcdInt + GcdOp + GcdRepresentation + GcdRepresentationOp + Gcdex + GeneralLinearGroup + GeneralLinearGroupCons + GeneralMappingByElements + GeneralMappingsFamily + GeneralOrthogonalGroup + GeneralOrthogonalGroupCons + GeneralStepClEANS + GeneralStepClEANSNonsolv + GeneralUnitaryGroup + GeneralUnitaryGroupCons + GeneralisedEigenspaces + GeneralisedEigenvalues + GeneralizedEigenspaces + GeneralizedEigenvalues + GeneralizedPcgs + GeneratingPairsOfLeftMagmaCongruence + GeneratingPairsOfMagmaCongruence + GeneratingPairsOfRightMagmaCongruence + GeneratingPairsOfSemigroupCongruence + GeneratingSetIsComplete + GeneratorNumberOfQuotient + GeneratorOfCyclicGroup + GeneratorSyllable + GeneratorTranslationAugmentedCosetTable + GeneratorsCenterPGroup + GeneratorsCentrePGroup + GeneratorsListTom + GeneratorsOfAdditiveGroup + GeneratorsOfAdditiveMagma + GeneratorsOfAdditiveMagmaWithInverses + GeneratorsOfAdditiveMagmaWithZero + GeneratorsOfAlgebra + GeneratorsOfAlgebraModule + GeneratorsOfAlgebraWithOne + GeneratorsOfDivisionRing + GeneratorsOfDomain + GeneratorsOfEquivalenceRelationPartition + GeneratorsOfExtASet + GeneratorsOfExtLSet + GeneratorsOfExtRSet + GeneratorsOfExtUSet + GeneratorsOfFLMLOR + GeneratorsOfFLMLORWithOne + GeneratorsOfField + GeneratorsOfGroup + GeneratorsOfIdeal + GeneratorsOfLayer + GeneratorsOfLeftIdeal + GeneratorsOfLeftMagmaIdeal + GeneratorsOfLeftModule + GeneratorsOfLeftOperatorAdditiveGroup + GeneratorsOfLeftOperatorRing + GeneratorsOfLeftOperatorRingWithOne + GeneratorsOfLeftVectorSpace + GeneratorsOfMagma + GeneratorsOfMagmaIdeal + GeneratorsOfMagmaWithInverses + GeneratorsOfMagmaWithOne + GeneratorsOfMonoid + GeneratorsOfNearAdditiveGroup + GeneratorsOfNearAdditiveMagma + GeneratorsOfNearAdditiveMagmaWithInverses + GeneratorsOfNearAdditiveMagmaWithZero + GeneratorsOfPresentation + GeneratorsOfRightIdeal + GeneratorsOfRightMagmaIdeal + GeneratorsOfRightModule + GeneratorsOfRightOperatorAdditiveGroup + GeneratorsOfRing + GeneratorsOfRingForIdeal + GeneratorsOfRingWithOne + GeneratorsOfRws + GeneratorsOfSemigroup + GeneratorsOfSemiring + GeneratorsOfSemiringWithOne + GeneratorsOfSemiringWithOneAndZero + GeneratorsOfSemiringWithZero + GeneratorsOfTwoSidedIdeal + GeneratorsOfVectorSpace + GeneratorsOverIntersection + GeneratorsPrimeResidues + GeneratorsSmallest + GeneratorsSmallestStab + GeneratorsSubgroupsTom + GenericFindActionKernel + GetChars + GetCommutatorNC + GetConjugateNC + GetDefinitionNC + GetETag + GetElement + GetEnt + GetFusionMap + GetHashEntry + GetHashEntryAtLastIndex + GetHashEntryIndex + GetMax + GetNaturalHomomorphismsPool + GetNumRight + GetPols + GetPowerNC + GetSTag + GetWord + GiveNumbersNIndeterminates + GlasbyCover + GlasbyIntersection + GlasbyShift + GlasbyStabilizer + GlobalPartitionOfClasses + Grading + GreensDClassOfElement + GreensDClasses + GreensDRelation + GreensHClassOfElement + GreensHClasses + GreensHRelation + GreensJClassOfElement + GreensJClasses + GreensJRelation + GreensLClassOfElement + GreensLClasses + GreensLRelation + GreensRClassOfElement + GreensRClasses + GreensRRelation + GroebnerBasis + GroebnerBasisNC + Group + GroupByGenerators + GroupByMultiplicationTable + GroupByNiceMonomorphism + GroupByPcgs + GroupByPrimeResidues + GroupByQuotientSystem + GroupByRws + GroupByRwsNC + GroupFromAdditiveGroup + GroupGeneralMappingByImages + GroupHClassOfGreensDClass + GroupHomomorphismByFunction + GroupHomomorphismByImages + GroupHomomorphismByImagesNC + GroupMethodByNiceMonomorphism + GroupMethodByNiceMonomorphismCollColl + GroupMethodByNiceMonomorphismCollElm + GroupMethodByNiceMonomorphismCollOther + GroupOfPcgs + GroupOnSubgroupsOrbit + GroupRing + GroupSeriesMethodByNiceMonomorphism + GroupSeriesMethodByNiceMonomorphismCollColl + GroupSeriesMethodByNiceMonomorphismCollElm + GroupSeriesMethodByNiceMonomorphismCollOther + GroupStabChain + GroupString + GroupToAdditiveGroupHomomorphismByFunction + GroupWithGenerators + HMSMSec + HallSubgroup + HallSubgroupOp + HallSystem + HasANonReesCongruenceOfSemigroup + HasAbelianFactorGroup + HasAbelianInvariants + HasAbelianInvariantsOfList + HasAbsoluteValue + HasActingDomain + HasActionHomomorphismAttr + HasActionKernelExternalSet + HasActorOfExternalSet + HasAdditiveElementAsMultiplicativeElement + HasAdditiveElementsAsMultiplicativeElementsFamily + HasAdditiveInverse + HasAdditiveInverseAttr + HasAdditiveInverseImmutable + HasAdditiveNeutralElement + HasAdditivelyActingDomain + HasAdjointBasis + HasAdjointModule + HasAlgebraicElementsFamilies + HasAllBlocks + HasAlpha + HasAlternatingDegree + HasAlternatingSubgroup + HasAsDuplicateFreeList + HasAsGroup + HasAsGroupGeneralMappingByImages + HasAsLeftModuleGeneralMappingByImages + HasAsList + HasAsMagma + HasAsMonoid + HasAsNearRing + HasAsPolynomial + HasAsRing + HasAsSSortedList + HasAsSemigroup + HasAsSemiring + HasAsSemiringWithOne + HasAsSemiringWithOneAndZero + HasAsSemiringWithZero + HasAsSortedList + HasAsSubgroupOfWholeGroupByQuotient + HasAssociatedReesMatrixSemigroupOfDClass + HasAssociatedSemigroup + HasAugmentationIdeal + HasAugmentedCosetTableMtcInWholeGroup + HasAugmentedCosetTableNormalClosureInWholeGroup + HasAugmentedCosetTableRrsInWholeGroup + HasAutomorphismDomain + HasAutomorphismGroup + HasAutomorphismsOfTable + HasBaseIntMat + HasBaseMat + HasBaseOfBasicImageGroup + HasBaseOfGroup + HasBaseOrthogonalSpaceMat + HasBasis + HasBasisOfHomCosetAddMatrixGroup + HasBasisVectors + HasBaumClausenInfo + HasBilinearFormMat + HasBlocksAttr + HasBlocksInfo + HasBrauerCharacterValue + HasBravaisGroup + HasBravaisSubgroups + HasBravaisSupergroups + HasCanEasilyCompareElements + HasCanEasilySortElements + HasCanFindNilpotentClassTwoElement + HasCanonicalBasis + HasCanonicalElt + HasCanonicalGenerators + HasCanonicalNiceMonomorphism + HasCanonicalPcgs + HasCanonicalPcgsWrtFamilyPcgs + HasCanonicalPcgsWrtHomePcgs + HasCanonicalPcgsWrtSpecialPcgs + HasCanonicalRepresentativeDeterminatorOfExternalSet + HasCanonicalRepresentativeOfExternalSet + HasCartanMatrix + HasCartanSubalgebra + HasCayleyGraphDualSemigroup + HasCayleyGraphSemigroup + HasCenter + HasCentralCharacter + HasCentralIdempotentsOfSemiring + HasCentralNormalSeriesByPcgs + HasCentralizerInGLnZ + HasCentralizerInParent + HasCentre + HasCentreOfCharacter + HasChainHomomorphicImage + HasChainSubgroup + HasChainSubgroupQuotient + HasCharacterDegrees + HasCharacterNames + HasCharacterParameters + HasCharacteristic + HasCharacteristicPolynomial + HasChevalleyBasis + HasChiefNormalSeriesByPcgs + HasChiefSeries + HasClassNames + HasClassNamesTom + HasClassParameters + HasClassPermutation + HasClassPositionsOfCentre + HasClassPositionsOfDerivedSubgroup + HasClassPositionsOfDirectProductDecompositions + HasClassPositionsOfElementaryAbelianSeries + HasClassPositionsOfFittingSubgroup + HasClassPositionsOfKernel + HasClassPositionsOfLowerCentralSeries + HasClassPositionsOfMaximalNormalSubgroups + HasClassPositionsOfNormalSubgroups + HasClassPositionsOfSolvableResiduum + HasClassPositionsOfSupersolvableResiduum + HasClassPositionsOfUpperCentralSeries + HasClassRoots + HasClassTypesTom + HasCoKernelOfAdditiveGeneralMapping + HasCoKernelOfMultiplicativeGeneralMapping + HasCoefficientsAndMagmaElements + HasCoefficientsFamily + HasCoefficientsOfLaurentPolynomial + HasCoefficientsOfUnivariatePolynomial + HasCoefficientsOfUnivariateRationalFunction + HasCoefficientsRing + HasCollectionsFamily + HasColumnIndexOfReesMatrixSemigroupElement + HasColumnIndexOfReesZeroMatrixSemigroupElement + HasColumnsOfReesMatrixSemigroup + HasColumnsOfReesZeroMatrixSemigroup + HasCommutatorFactorGroup + HasCommutatorLength + HasComplementSystem + HasComplexConjugate + HasComponentsOfTuplesFamily + HasCompositionSeries + HasComputedAgemos + HasComputedAscendingChains + HasComputedBrauerTables + HasComputedClassFusions + HasComputedCyclicExtensionsTom + HasComputedHallSubgroups + HasComputedIndicators + HasComputedIsPNilpotents + HasComputedIsPSolvableCharacterTables + HasComputedIsPSolvables + HasComputedOmegas + HasComputedPCentralSeriess + HasComputedPCores + HasComputedPRumps + HasComputedPowerMaps + HasComputedPrimeBlockss + HasComputedSylowComplements + HasComputedSylowSubgroups + HasConductor + HasConfluentRws + HasConjugacyClasses + HasConjugacyClassesMaximalSubgroups + HasConjugacyClassesPerfectSubgroups + HasConjugacyClassesSubgroups + HasConjugates + HasConjugatorInnerAutomorphism + HasConjugatorOfConjugatorIsomorphism + HasConstantTimeAccessList + HasConstituentsOfCharacter + HasConvertBasicImageGroupElement + HasCoreInParent + HasCosetTableFpHom + HasCosetTableInWholeGroup + HasCosetTableNormalClosureInWholeGroup + HasCosetTableOfFpSemigroup + HasCycleStructurePerm + HasCyclicExtensionsTom + HasDecompositionMatrix + HasDecompositionTypesOfGroup + HasDefaultFieldOfMatrix + HasDefaultFieldOfMatrixGroup + HasDefectApproximation + HasDefiningPcgs + HasDefiningPolynomial + HasDegreeAction + HasDegreeOfBinaryRelation + HasDegreeOfCharacter + HasDegreeOfLaurentPolynomial + HasDegreeOfMatrixGroup + HasDegreeOfTransformation + HasDegreeOfTransformationSemigroup + HasDegreeOperation + HasDegreeOverPrimeField + HasDelta + HasDenominatorOfModuloPcgs + HasDenominatorOfRationalFunction + HasDepthOfUpperTriangularMatrix + HasDerivations + HasDerivative + HasDerivedLength + HasDerivedSeriesOfGroup + HasDerivedSubgroup + HasDerivedSubgroupsTomPossible + HasDerivedSubgroupsTomUnique + HasDeterminantMat + HasDeterminantOfCharacter + HasDihedralGenerators + HasDimension + HasDimensionOfMatrixGroup + HasDimensionOfVectors + HasDimensionsLoewyFactors + HasDimensionsMat + HasDirectFactorsOfGroup + HasDirectProductInfo + HasDirectSumDecomposition + HasDisplayOptions + HasDixonRecord + HasEANormalSeriesByPcgs + HasEarns + HasEggBoxOfDClass + HasElementTestFunction + HasElementaryAbelianFactorGroup + HasElementaryAbelianSeries + HasElementaryAbelianSeriesLargeSteps + HasElementaryAbelianSubseries + HasElementsFamily + HasEmptyRowVector + HasEnumerator + HasEnumeratorByBasis + HasEnumeratorSorted + HasEquivalenceClassRelation + HasEquivalenceClasses + HasEquivalenceRelationPartition + HasExponent + HasExponentOfPowering + HasExtRepDenominatorRatFun + HasExtRepNumeratorRatFun + HasExtRepPolynomialRatFun + HasExternalOrbits + HasExternalOrbitsStabilizers + HasExternalSet + HasFactorsOfDirectProduct + HasFaithfulModule + HasFamiliesOfGeneralMappingsAndRanges + HasFamilyForOrdering + HasFamilyForRewritingSystem + HasFamilyPcgs + HasFamilyRange + HasFamilySource + HasFieldOfMatrixGroup + HasFittingSubgroup + HasFpElmComparisonMethod + HasFpElmEqualityMethod + HasFpElmKBRWS + HasFrattiniSubgroup + HasFrattinifactorId + HasFrattinifactorSize + HasFreeAlgebraOfFpAlgebra + HasFreeGeneratorsOfFpAlgebra + HasFreeGeneratorsOfFpGroup + HasFreeGeneratorsOfFpMonoid + HasFreeGeneratorsOfFpSemigroup + HasFreeGroupOfBasicImageGroup + HasFreeGroupOfFpGroup + HasFreeMonoidOfFpMonoid + HasFreeMonoidOfRewritingSystem + HasFreeSemigroupOfFpSemigroup + HasFreeSemigroupOfRewritingSystem + HasFrobeniusAutomorphism + HasFunctionAction + HasFusionConjugacyClassesOp + HasFusionsOfLibTom + HasFusionsToLibTom + HasFusionsTom + HasGLDegree + HasGLUnderlyingField + HasGaloisGroup + HasGaloisMat + HasGaloisStabilizer + HasGaloisType + HasGeneralizedPcgs + HasGeneratingPairsOfLeftMagmaCongruence + HasGeneratingPairsOfMagmaCongruence + HasGeneratingPairsOfRightMagmaCongruence + HasGeneratingSetIsComplete + HasGeneratorOfCyclicGroup + HasGeneratorsOfAdditiveGroup + HasGeneratorsOfAdditiveMagma + HasGeneratorsOfAdditiveMagmaWithInverses + HasGeneratorsOfAdditiveMagmaWithZero + HasGeneratorsOfAlgebra + HasGeneratorsOfAlgebraModule + HasGeneratorsOfAlgebraWithOne + HasGeneratorsOfDivisionRing + HasGeneratorsOfDomain + HasGeneratorsOfEquivalenceRelationPartition + HasGeneratorsOfExtASet + HasGeneratorsOfExtLSet + HasGeneratorsOfExtRSet + HasGeneratorsOfExtUSet + HasGeneratorsOfFLMLOR + HasGeneratorsOfFLMLORWithOne + HasGeneratorsOfField + HasGeneratorsOfGroup + HasGeneratorsOfIdeal + HasGeneratorsOfLeftIdeal + HasGeneratorsOfLeftMagmaIdeal + HasGeneratorsOfLeftModule + HasGeneratorsOfLeftOperatorAdditiveGroup + HasGeneratorsOfLeftOperatorRing + HasGeneratorsOfLeftOperatorRingWithOne + HasGeneratorsOfLeftVectorSpace + HasGeneratorsOfMagma + HasGeneratorsOfMagmaIdeal + HasGeneratorsOfMagmaWithInverses + HasGeneratorsOfMagmaWithOne + HasGeneratorsOfMonoid + HasGeneratorsOfNearAdditiveGroup + HasGeneratorsOfNearAdditiveMagma + HasGeneratorsOfNearAdditiveMagmaWithInverses + HasGeneratorsOfNearAdditiveMagmaWithZero + HasGeneratorsOfRightIdeal + HasGeneratorsOfRightMagmaIdeal + HasGeneratorsOfRightModule + HasGeneratorsOfRightOperatorAdditiveGroup + HasGeneratorsOfRing + HasGeneratorsOfRingWithOne + HasGeneratorsOfRws + HasGeneratorsOfSemigroup + HasGeneratorsOfSemiring + HasGeneratorsOfSemiringWithOne + HasGeneratorsOfSemiringWithOneAndZero + HasGeneratorsOfSemiringWithZero + HasGeneratorsOfTwoSidedIdeal + HasGeneratorsOfVectorSpace + HasGeneratorsSmallest + HasGeneratorsSubgroupsTom + HasGlobalPartitionOfClasses + HasGrading + HasGreensDRelation + HasGreensHRelation + HasGreensJRelation + HasGreensLRelation + HasGreensRRelation + HasGroupByPcgs + HasGroupHClassOfGreensDClass + HasGroupOfPcgs + HasHallSystem + HasHirschLength + HasHomCosetFamily + HasHomFromFreeOfBasicImageGroup + HasHomeEnumerator + HasHomePcgs + HasIBr + HasIdGroup + HasIdempotents + HasIdempotentsTom + HasIdempotentsTomInfo + HasIdentificationOfConjugacyClasses + HasIdentifier + HasIdentity + HasIdentityMapping + HasImageElt + HasImageListOfTransformation + HasImageSetOfTransformation + HasImagesSmallestGenerators + HasImagesSource + HasImfRecord + HasIndependentGeneratorsOfAbelianGroup + HasIndependentGeneratorsOfAbelianMatrixGroup + HasIndeterminateName + HasIndeterminateNumberOfLaurentPolynomial + HasIndeterminateNumberOfUnivariateLaurentPolynomial + HasIndeterminateNumberOfUnivariateRationalFunction + HasIndeterminateOfUnivariateRationalFunction + HasIndeterminatesOfPolynomialRing + HasIndexInParent + HasIndexInWholeGroup + HasIndicesCentralNormalSteps + HasIndicesChiefNormalSteps + HasIndicesEANormalSteps + HasIndicesInvolutaryGenerators + HasIndicesNormalSteps + HasIndicesOfAdjointBasis + HasIndicesPCentralNormalStepsPGroup + HasInducedPcgsWrtFamilyPcgs + HasInducedPcgsWrtHomePcgs + HasInducedPcgsWrtSpecialPcgs + HasInfoText + HasInjectionZeroMagma + HasInnerAutomorphismsAutomorphismGroup + HasInt + HasInternalRepresentative + HasInvariantBilinearForm + HasInvariantConjugateSubgroup + HasInvariantForm + HasInvariantLattice + HasInvariantQuadraticForm + HasInvariantSesquilinearForm + HasInverse + HasInverseAttr + HasInverseClasses + HasInverseGeneralMapping + HasInverseImmutable + HasIrr + HasIrrBaumClausen + HasIrrConlon + HasIrrDixonSchneider + HasIrrFacsPol + HasIrreducibleRepresentations + HasIsAbelian + HasIsAbelianNumberField + HasIsAbelianTom + HasIsAdditiveGroupGeneralMapping + HasIsAdditiveGroupHomomorphism + HasIsAdditiveGroupToGroupGeneralMapping + HasIsAdditiveGroupToGroupHomomorphism + HasIsAdditivelyCommutative + HasIsAlgebraGeneralMapping + HasIsAlgebraHomomorphism + HasIsAlgebraModule + HasIsAlgebraWithOneGeneralMapping + HasIsAlgebraWithOneHomomorphism + HasIsAlternatingGroup + HasIsAnticommutative + HasIsAntisymmetricBinaryRelation + HasIsAssociative + HasIsAutomorphismGroup + HasIsBasicWreathProductOrdering + HasIsBergerCondition + HasIsBijective + HasIsBravaisGroup + HasIsBuiltFromAdditiveMagmaWithInverses + HasIsBuiltFromGroup + HasIsBuiltFromMagma + HasIsBuiltFromMagmaWithInverses + HasIsBuiltFromMagmaWithOne + HasIsBuiltFromMonoid + HasIsBuiltFromSemigroup + HasIsCanonicalBasis + HasIsCanonicalBasisFullMatrixModule + HasIsCanonicalBasisFullRowModule + HasIsCanonicalBasisFullSCAlgebra + HasIsCanonicalNiceMonomorphism + HasIsCanonicalPcgs + HasIsCanonicalPcgsWrtSpecialPcgs + HasIsChainTypeGroup + HasIsCharacter + HasIsCharacteristicMatrixPGroup + HasIsCommutative + HasIsCommutativeFamily + HasIsConfluent + HasIsConjugatorAutomorphism + HasIsConjugatorIsomorphism + HasIsConstantRationalFunction + HasIsConstantTimeAccessGeneralMapping + HasIsCycInt + HasIsCyclic + HasIsCyclicTom + HasIsCyclotomicField + HasIsDihedralGroup + HasIsDistributive + HasIsDivisionRing + HasIsDuplicateFree + HasIsDuplicateFreeList + HasIsElementaryAbelian + HasIsEmpty + HasIsEndoGeneralMapping + HasIsEndoMapping + HasIsEquivalenceRelation + HasIsFFEMatrixGroupOverLargeSpace + HasIsFamilyPcgs + HasIsField + HasIsFieldHomomorphism + HasIsFinite + HasIsFiniteDimensional + HasIsFiniteOrdersPcgs + HasIsFiniteSemigroupGreensRelation + HasIsFinitelyGeneratedGroup + HasIsFrattiniFree + HasIsFreeMonoid + HasIsFreeSemigroup + HasIsFullFpAlgebra + HasIsFullHomModule + HasIsFullMatrixModule + HasIsFullRowModule + HasIsFullSCAlgebra + HasIsFullSubgroupGLorSLRespectingBilinearForm + HasIsFullSubgroupGLorSLRespectingQuadraticForm + HasIsFullSubgroupGLorSLRespectingSesquilinearForm + HasIsFullTransformationSemigroup + HasIsGL + HasIsGeneralLinearGroup + HasIsGeneralizedCartanMatrix + HasIsGeneratorsOfMagmaWithInverses + HasIsGreensClass + HasIsGreensDClass + HasIsGreensDRelation + HasIsGreensHClass + HasIsGreensHRelation + HasIsGreensJClass + HasIsGreensJRelation + HasIsGreensLClass + HasIsGreensLRelation + HasIsGreensRClass + HasIsGreensRRelation + HasIsGreensRelation + HasIsGroupGeneralMapping + HasIsGroupHClass + HasIsGroupHomomorphism + HasIsGroupOfAutomorphisms + HasIsGroupOfAutomorphismsFiniteGroup + HasIsGroupRing + HasIsGroupToAdditiveGroupGeneralMapping + HasIsGroupToAdditiveGroupHomomorphism + HasIsHandledByNiceMonomorphism + HasIsHasseDiagram + HasIsHomCosetOfAdditiveElt + HasIsHomCosetOfFp + HasIsHomCosetOfMatrix + HasIsHomCosetOfPerm + HasIsHomCosetOfTuple + HasIsIdealInParent + HasIsIdempotent + HasIsImpossible + HasIsInducedFromNormalSubgroup + HasIsInducedPcgsWrtSpecialPcgs + HasIsInjective + HasIsInnerAutomorphism + HasIsIntegerMatrixGroup + HasIsIntegralBasis + HasIsIntegralCyclotomic + HasIsIntegralRing + HasIsIrreducibleCharacter + HasIsJacobianRing + HasIsLDistributive + HasIsLatticeOrderBinaryRelation + HasIsLaurentPolynomial + HasIsLeftActedOnByDivisionRing + HasIsLeftAlgebraModule + HasIsLeftIdealInParent + HasIsLeftModuleGeneralMapping + HasIsLeftModuleHomomorphism + HasIsLeftSemigroupCongruence + HasIsLeftSemigroupIdeal + HasIsLieAbelian + HasIsLieAlgebra + HasIsLieNilpotent + HasIsLieSolvable + HasIsLinearlyPrimitive + HasIsMagmaHomomorphism + HasIsMapping + HasIsMatrixModule + HasIsMinimalNonmonomial + HasIsMonoid + HasIsMonomialCharacter + HasIsMonomialCharacterTable + HasIsMonomialGroup + HasIsMonomialMatrix + HasIsMonomialNumber + HasIsNaturalAlternatingGroup + HasIsNaturalGL + HasIsNaturalSL + HasIsNaturalSymmetricGroup + HasIsNearRing + HasIsNearRingWithOne + HasIsNilpQuotientSystem + HasIsNilpotentCharacterTable + HasIsNilpotentGroup + HasIsNilpotentTom + HasIsNonTrivial + HasIsNoncharacteristicMatrixPGroup + HasIsNormalBasis + HasIsNormalForm + HasIsNormalInParent + HasIsNumberField + HasIsNumeratorParentPcgsFamilyPcgs + HasIsOne + HasIsOrderingOnFamilyOfAssocWords + HasIsPGroup + HasIsPQuotientSystem + HasIsPSL + HasIsParentPcgsFamilyPcgs + HasIsPartialOrderBinaryRelation + HasIsPcgsCentralSeries + HasIsPcgsChiefSeries + HasIsPcgsElementaryAbelianSeries + HasIsPcgsPCentralSeriesPGroup + HasIsPerfectCharacterTable + HasIsPerfectGroup + HasIsPerfectTom + HasIsPolycyclicGroup + HasIsPolynomial + HasIsPositionsList + HasIsPreOrderBinaryRelation + HasIsPrimeField + HasIsPrimeOrdersPcgs + HasIsPrimitive + HasIsPrimitiveAffine + HasIsPrimitiveCharacter + HasIsPrimitiveMatrixGroup + HasIsPseudoCanonicalBasisFullHomModule + HasIsQuasiDihedralGroup + HasIsQuasiPrimitive + HasIsQuaternionGroup + HasIsRDistributive + HasIsRationalMatrixGroup + HasIsRectangularTable + HasIsReduced + HasIsReesCongruence + HasIsReesCongruenceSemigroup + HasIsReesMatrixSemigroup + HasIsReesZeroMatrixSemigroup + HasIsReflexiveBinaryRelation + HasIsRegular + HasIsRegularDClass + HasIsRegularSemigroup + HasIsRelativelySM + HasIsRestrictedLieAlgebra + HasIsRightAlgebraModule + HasIsRightIdealInParent + HasIsRightSemigroupCongruence + HasIsRightSemigroupIdeal + HasIsRing + HasIsRingGeneralMapping + HasIsRingHomomorphism + HasIsRingWithOne + HasIsRingWithOneGeneralMapping + HasIsRingWithOneHomomorphism + HasIsRowModule + HasIsSL + HasIsSSortedList + HasIsSemiEchelonized + HasIsSemiRegular + HasIsSemigroup + HasIsSemigroupCongruence + HasIsSemigroupIdeal + HasIsSemiring + HasIsSemiringWithOne + HasIsSemiringWithOneAndZero + HasIsSemiringWithZero + HasIsShortLexOrdering + HasIsSimpleAlgebra + HasIsSimpleCharacterTable + HasIsSimpleGroup + HasIsSimpleSemigroup + HasIsSingleValued + HasIsSkewFieldFamily + HasIsSmallList + HasIsSolvableCharacterTable + HasIsSolvableGroup + HasIsSolvableTom + HasIsSortedList + HasIsSpecialLinearGroup + HasIsSpecialPcgs + HasIsSporadicSimpleCharacterTable + HasIsSporadicSimpleGroup + HasIsStabChainViaChainSubgroup + HasIsSubgroupSL + HasIsSubmonoidFpMonoid + HasIsSubnormallyMonomial + HasIsSubsemigroupFpSemigroup + HasIsSubsemigroupReesMatrixSemigroup + HasIsSubsemigroupReesZeroMatrixSemigroup + HasIsSubsetLocallyFiniteGroup + HasIsSupersolvableCharacterTable + HasIsSupersolvableGroup + HasIsSurjective + HasIsSymmetricBinaryRelation + HasIsSymmetricGroup + HasIsTotal + HasIsTotalOrdering + HasIsTransformationMonoid + HasIsTransformationSemigroup + HasIsTransitive + HasIsTransitiveBinaryRelation + HasIsTranslationInvariantOrdering + HasIsTrivial + HasIsTwoSidedIdealInParent + HasIsUFDFamily + HasIsUniformMatrixGroup + HasIsUnivariatePolynomial + HasIsUnivariateRationalFunction + HasIsVectorSpaceHomomorphism + HasIsVirtualCharacter + HasIsWeightLexOrdering + HasIsWellFoundedOrdering + HasIsWeylGroup + HasIsWholeFamily + HasIsWreathProductOrdering + HasIsZero + HasIsZeroGroup + HasIsZeroMultiplicationRing + HasIsZeroRationalFunction + HasIsZeroSimpleSemigroup + HasIsZeroSquaredRing + HasIsomorphismFpAlgebra + HasIsomorphismFpFLMLOR + HasIsomorphismFpGroup + HasIsomorphismFpMonoid + HasIsomorphismFpSemigroup + HasIsomorphismMatrixAlgebra + HasIsomorphismMatrixFLMLOR + HasIsomorphismPcGroup + HasIsomorphismPermGroup + HasIsomorphismReesMatrixSemigroup + HasIsomorphismRefinedPcGroup + HasIsomorphismSCAlgebra + HasIsomorphismSCFLMLOR + HasIsomorphismSimplifiedFpGroup + HasIsomorphismSpecialPcGroup + HasIsomorphismTransformationSemigroup + HasJenningsLieAlgebra + HasJenningsSeries + HasJordanDecomposition + HasKernelOfAdditiveGeneralMapping + HasKernelOfCharacter + HasKernelOfMultiplicativeGeneralMapping + HasKernelOfTransformation + HasKillingMatrix + HasKnowsHowToDecompose + HasLClassOfHClass + HasLGFirst + HasLGHeads + HasLGLayers + HasLGLength + HasLGTails + HasLGWeights + HasLargestElementGroup + HasLargestMovedPoint + HasLargestMovedPointPerm + HasLatticeGeneratorsInUEA + HasLatticeSubgroups + HasLeadCoeffsIGS + HasLeftActingAlgebra + HasLeftActingDomain + HasLeftActingGroup + HasLeftActingRingOfIdeal + HasLeftDerivations + HasLength + HasLengthsTom + HasLessThanFunction + HasLessThanOrEqualFunction + HasLetterRepWordsLessFunc + HasLevelsOfGenerators + HasLeviMalcevDecomposition + HasLieAlgebraByDomain + HasLieCenter + HasLieCentralizerInParent + HasLieCentre + HasLieDerivedSeries + HasLieDerivedSubalgebra + HasLieFamily + HasLieLowerCentralSeries + HasLieNilRadical + HasLieNormalizerInParent + HasLieObject + HasLieSolvableRadical + HasLieUpperCentralSeries + HasLinearActionBasis + HasLinearCharacters + HasLinesOfStraightLineProgram + HasLongestWeylWordPerm + HasLowerCentralSeriesOfGroup + HasMagmaGeneratorsOfFamily + HasMappingGeneratorsImages + HasMappingOfWhichItIsAsGGMBI + HasMarksTom + HasMatTom + HasMatrixByBlockMatrix + HasMatrixDimension + HasMaximalAbelianQuotient + HasMaximalBlocksAttr + HasMaximalNormalSubgroups + HasMaximalSubgroupClassReps + HasMaximalSubgroups + HasMaximalSubgroupsLattice + HasMaximalSubgroupsTom + HasMinimalBlockDimension + HasMinimalGeneratingSet + HasMinimalNormalSubgroups + HasMinimalStabChain + HasMinimalSupergroupsLattice + HasMinimizedBombieriNorm + HasModuleOfExtension + HasModulusOfZmodnZObj + HasMoebiusTom + HasMolienSeriesInfo + HasMonoidOfRewritingSystem + HasMonomialComparisonFunction + HasMonomialExtrepComparisonFun + HasMovedPoints + HasMultiplicationTable + HasMultiplicativeNeutralElement + HasMultiplicativeZero + HasName + HasNameIsomorphismClass + HasNamesLibTom + HasNamesOfFusionSources + HasNaturalCharacter + HasNaturalHomomorphismByNormalSubgroupNCInParent + HasNaturalHomomorphismsPool + HasNegativeRootVectors + HasNegativeRoots + HasNestingDepthA + HasNestingDepthM + HasNiceAlgebraMonomorphism + HasNiceBasis + HasNiceFreeLeftModule + HasNiceFreeLeftModuleInfo + HasNiceMonomorphism + HasNiceNormalFormByExtRepFunction + HasNiceObject + HasNilpotencyClassOfGroup + HasNilpotentClassTwoElement + HasNonLieNilpotentElement + HasNonNilpotentElement + HasNorm + HasNormalBase + HasNormalClosureInParent + HasNormalMaximalSubgroups + HasNormalSeriesByPcgs + HasNormalSubgroupClassesInfo + HasNormalSubgroups + HasNormalizerInGLnZ + HasNormalizerInGLnZBravaisGroup + HasNormalizerInHomePcgs + HasNormalizerInParent + HasNormalizersTom + HasNormedRowVector + HasNormedRowVectors + HasNormedVectors + HasNotifiedFusionsOfLibTom + HasNotifiedFusionsToLibTom + HasNrConjugacyClasses + HasNrInputsOfStraightLineProgram + HasNrMovedPoints + HasNrMovedPointsPerm + HasNrSubsTom + HasNrSyllables + HasNullAlgebra + HasNullspaceIntMat + HasNullspaceMat + HasNumberGeneratorsOfRws + HasNumberSyllables + HasNumeratorOfModuloPcgs + HasNumeratorOfRationalFunction + HasONanScottType + HasOccuringVariableIndices + HasOmegaAndLowerPCentralSeries + HasOmegaSeries + HasOne + HasOneAttr + HasOneImmutable + HasOneOfPcgs + HasOperatorOfExternalSet + HasOrbitGeneratorsOfGroup + HasOrbitLengths + HasOrbitLengthsDomain + HasOrbitStabilizingParentGroup + HasOrbits + HasOrbitsDomain + HasOrder + HasOrderingOfRewritingSystem + HasOrderingOnGenerators + HasOrderingsFamily + HasOrdersClassRepresentatives + HasOrdersTom + HasOrdinaryCharacterTable + HasOrthogonalSpaceInFullRowSpace + HasPCentralLieAlgebra + HasPCentralNormalSeriesByPcgsPGroup + HasPClassPGroup + HasPSLDegree + HasPSLUnderlyingField + HasParent + HasParentAttr + HasParentPcgs + HasPartialClosureOfCongruence + HasPartialOrderOfHasseDiagram + HasPcGroupWithPcgs + HasPcSeries + HasPcgs + HasPcgsCentralSeries + HasPcgsChiefSeries + HasPcgsElementaryAbelianSeries + HasPcgsPCentralSeriesPGroup + HasPerfectIdentification + HasPerfectResiduum + HasPermutationTom + HasPositiveRootVectors + HasPositiveRoots + HasPositiveRootsAsWeights + HasPowerS + HasPowerSubalgebraSeries + HasPreBasis + HasPreImagesRange + HasPreferredGenerators + HasPrefrattiniSubgroup + HasPresentation + HasPrimaryGeneratorWords + HasPrimeField + HasPrimePGroup + HasPrimePowerComponents + HasPrimitiveElement + HasPrimitiveIdentification + HasPrimitiveRoot + HasProjectiveOrder + HasPseudoRandomSeed + HasPthPowerImages + HasQuasiDihedralGenerators + HasQuaternionGenerators + HasQuotientGroup + HasQuotientGroupHom + HasQuotientSemigroupCongruence + HasQuotientSemigroupHomomorphism + HasQuotientSemigroupPreimage + HasRClassOfHClass + HasRadicalGroup + HasRadicalOfAlgebra + HasRange + HasRankAction + HasRankMat + HasRankOfTransformation + HasRankPGroup + HasRat + HasRationalClasses + HasRationalFunctionsFamily + HasRationalizedMat + HasRealClasses + HasRecNames + HasReducedConfluentRewritingSystem + HasReesCongruenceOfSemigroupIdeal + HasReesZeroMatrixSemigroupElementIsZero + HasRefinedPcGroup + HasRegularActionHomomorphism + HasRelationsOfFpMonoid + HasRelationsOfFpSemigroup + HasRelativeOrders + HasRelatorsOfFpAlgebra + HasRelatorsOfFpGroup + HasRepresentative + HasRepresentativeSmallest + HasRepresentativesContainedRightCosets + HasRepresentativesMinimalBlocksAttr + HasRepresentativesPerfectSubgroups + HasRepresentativesSimpleSubgroups + HasRespectsAddition + HasRespectsAdditiveInverses + HasRespectsInverses + HasRespectsMultiplication + HasRespectsOne + HasRespectsScalarMultiplication + HasRespectsZero + HasRightActingAlgebra + HasRightActingDomain + HasRightActingGroup + HasRightActingRingOfIdeal + HasRightDerivations + HasRightTransversalInParent + HasRootOfDefiningPolynomial + HasRootSystem + HasRowIndexOfReesMatrixSemigroupElement + HasRowIndexOfReesZeroMatrixSemigroupElement + HasRowsOfReesMatrixSemigroup + HasRowsOfReesZeroMatrixSemigroup + HasRules + HasSLDegree + HasSLUnderlyingField + HasSandwichMatrixOfReesMatrixSemigroup + HasSandwichMatrixOfReesZeroMatrixSemigroup + HasSemiEchelonBasis + HasSemiEchelonMat + HasSemiEchelonMatTransformation + HasSemiSimpleType + HasSemidirectFactorsOfGroup + HasSemidirectProductInfo + HasSemigroupOfRewritingSystem + HasSiftFunction + HasSiftGroup + HasSignPerm + HasSimpleSystem + HasSimsNo + HasSize + HasSizesCentralizers + HasSizesConjugacyClasses + HasSmallGeneratingSet + HasSmallestGeneratorPerm + HasSmallestMovedPoint + HasSmallestMovedPointPerm + HasSocle + HasSocleComplement + HasSocleDimensions + HasSocleTypePrimitiveGroup + HasSortingPerm + HasSource + HasSparseCartanMatrix + HasSpecialPcgs + HasSplittingField + HasStabChainImmutable + HasStabChainMutable + HasStabChainOptions + HasStabilizerOfExternalSet + HasStandardGeneratorsInfo + HasStoredExcludedOrders + HasStoredGroebnerBasis + HasStraightLineProgElmType + HasStraightLineProgramsTom + HasString + HasStructureConstantsTable + HasStructureDescription + HasSubdirectProductInfo + HasSubfields + HasSubnormalSeriesInParent + HasSubsTom + HasSubspaces + HasSubspacesAll + HasSuccessors + HasSupersolvableResiduum + HasSurjectiveActionHomomorphismAttr + HasSylowSystem + HasSymmetricDegree + HasSymmetricParentGroup + HasTableOfMarks + HasTestMonomial + HasTestMonomialQuick + HasTestQuasiPrimitive + HasTestRelativelySM + HasTestSubnormallyMonomial + HasTietzeOrigin + HasTrace + HasTranformsOneIntoZero + HasTransformationRepresentation + HasTransformsAdditionIntoMultiplication + HasTransformsAdditiveInversesIntoInverses + HasTransformsInversesIntoAdditiveInverses + HasTransformsMultiplicationIntoAddition + HasTransformsZeroIntoOne + HasTransitiveIdentification + HasTransitivity + HasTransposedMat + HasTransposedMatAttr + HasTransposedMatImmutable + HasTransposedMatrixGroup + HasTransversal + HasTriangulizedNullspaceMat + HasTrivialCharacter + HasTrivialSubFLMLOR + HasTrivialSubadditiveMagmaWithZero + HasTrivialSubalgebra + HasTrivialSubgroup + HasTrivialSubmagmaWithOne + HasTrivialSubmodule + HasTrivialSubmonoid + HasTrivialSubnearAdditiveMagmaWithZero + HasTrivialSubspace + HasTwoClosure + HasTzOptions + HasTzRules + HasUnderlyingCharacterTable + HasUnderlyingCharacteristic + HasUnderlyingCollection + HasUnderlyingElementOfReesMatrixSemigroupElement + HasUnderlyingElementOfReesZeroMatrixSemigroupElement + HasUnderlyingExternalSet + HasUnderlyingFamily + HasUnderlyingField + HasUnderlyingGeneralMapping + HasUnderlyingGroup + HasUnderlyingLeftModule + HasUnderlyingLieAlgebra + HasUnderlyingMagma + HasUnderlyingRelation + HasUnderlyingSemigroupOfReesMatrixSemigroup + HasUnderlyingSemigroupOfReesZeroMatrixSemigroup + HasUnderlyingVectorSpace + HasUnits + HasUniversalEnvelopingAlgebra + HasUpperActingDomain + HasUpperCentralSeriesOfGroup + HasValuesOfClassFunction + HasWeightOfGenerators + HasWeightsTom + HasWeylGroup + HasWreathProductInfo + HasZClassRepsQClass + HasZero + HasZeroAttr + HasZeroCoefficient + HasZeroImmutable + HasZuppos + HashDictAddDictionary + HashFunct + HashKeyBag + HashKeyEnumerator + HashTable + HasnpeGL + HasnpePSL + HasnpeSL + HasseDiagramBinaryRelation + HeadPcElementByNumber + HeadsInfoOfSemiEchelonizedMat + HeadsInfoOfSemiEchelonizedMats + HenselBound + HermiteNormalFormIntegerMat + HermiteNormalFormIntegerMatTransform + HermiteNormalFormIntegerMatTransforms + HeuGcdIntPolsCoeffs + HeuGcdIntPolsExtRep + HeuristicCancelPolynomialsExtRep + HexBlistSetup + HexStringBlist + HexStringBlistEncode + HexStringInt + HideGlobalVariables + HighestWeightModule + HirschLength + Hom + HomCoset + HomCosetFamily + HomCosetWithImage + HomFromFree + HomFromFreeOfBasicImageGroup + HomTransversal + HomeEnumerator + HomePcgs + HomomorphicCanonicalPcgs + HomomorphicInducedPcgs + Homomorphism + HomomorphismFactorSemigroup + HomomorphismFactorSemigroupByClosure + HomomorphismQuotientSemigroup + HomomorphismTransformationSemigroup + HomomorphismsSeries + HumanReadableDefinition + IBr + IMFList + IMFLoad + IMFRec + IdFunc + IdGroup + IdSmallGroup + Ideal + IdealByGenerators + IdealByGeneratorsForLieAlgebra + IdealNC + Idempotents + IdempotentsTom + IdempotentsTomInfo + IdentificationGenericGroup + IdentificationOfConjugacyClasses + IdentificationPermGroup + IdentificationSolvableGroup + Identifier + IdentifierLetters + Identity + IdentityBinaryRelation + IdentityFromSCTable + IdentityMapping + IdentityMat + IdentityTransformation + IdsOfAllGroups + IdsOfAllSmallGroups + Ignore + Image + ImageElm + ImageElmActionHomomorphism + ImageElt + ImageGroup + ImageInWord + ImageKernelBlocksHomomorphism + ImageListOfTransformation + ImageMat + ImageOnAbelianCSPG + ImageSetOfTransformation + ImageSiftedBaseImage + ImageUnderWord + Images + ImagesElm + ImagesListOfBinaryRelation + ImagesRepresentative + ImagesRepresentativeGMBIByElementsList + ImagesSet + ImagesSmallestGenerators + ImagesSource + ImfInvariants + ImfMatrixGroup + ImfNumberQClasses + ImfNumberQQClasses + ImfNumberZClasses + ImfPositionNumber + ImfRecord + ImgElmSLP + ImgElmSLPNonrecursive + ImmediateImplicationsIdentityMapping + ImmediateImplicationsZeroMapping + Immutable + ImmutableBasis + ImmutableMatrix + ImproveActionDegreeByBlocks + ImproveMaps + ImproveOperationDegreeByBlocks + InParentFOA + IncorporateCentralRelations + IncreaseCounter + IndPcgsWrtSpecFromFamOrHome + IndependentGeneratorsAbelianPPermGroup + IndependentGeneratorsOfAbelianGroup + IndependentGeneratorsOfAbelianMatrixGroup + Indeterminate + IndeterminateName + IndeterminateNumberOfLaurentPolynomial + IndeterminateNumberOfUnivariateLaurentPolynomial + IndeterminateNumberOfUnivariateRationalFunction + IndeterminateOfLaurentPolynomial + IndeterminateOfUnivariateRationalFunction + Indeterminateness + IndeterminatenessInfo + IndeterminatesOfPolynomialRing + Index + IndexCosetTab + IndexInParent + IndexInWholeGroup + IndexNC + IndexOp + Indicator + IndicatorOp + IndicesCentralNormalSteps + IndicesChiefNormalSteps + IndicesEANormalSteps + IndicesInvolutaryGenerators + IndicesNormalSteps + IndicesOfAdjointBasis + IndicesPCentralNormalStepsPGroup + IndicesStabChain + Indirected + Induced + InducedActionAutGroup + InducedActionFactor + InducedAutomorphism + InducedClassFunction + InducedClassFunctions + InducedClassFunctionsByFusionMap + InducedCyclic + InducedGModule + InducedLinearAction + InducedModule + InducedModuleByFieldReduction + InducedPcgs + InducedPcgsByGenerators + InducedPcgsByGeneratorsNC + InducedPcgsByGeneratorsWithImages + InducedPcgsByPcSequence + InducedPcgsByPcSequenceAndGenerators + InducedPcgsByPcSequenceNC + InducedPcgsWrtFamilyPcgs + InducedPcgsWrtHomePcgs + InducedPcgsWrtSpecialPcgs + InducedRepFpGroup + InducedRepresentation + InducedRepresentationImagesRepresentative + InduciblePairs + InductionScheme + Inequalities + InertiaSubgroup + InfiniteListOfGenerators + InfiniteListOfNames + Inflated + Info + InfoAction + InfoAlgebra + InfoAttributes + InfoAutGrp + InfoBasicImage + InfoBckt + InfoChain + InfoCharacterTable + InfoClassFamily + InfoClasses + InfoCoh + InfoCompPairs + InfoComplement + InfoConfluence + InfoCoset + InfoData + InfoDebug + InfoDecision + InfoDoPrint + InfoExtReps + InfoFactor + InfoFpGroup + InfoGalois + InfoGlobal + InfoGroebner + InfoGroup + InfoHash + InfoHashTables + InfoHomClass + InfoIdgroup + InfoImf + InfoKnuthBendix + InfoLattice + InfoLevel + InfoMatInt + InfoMatOrb + InfoMatrix + InfoMeatAxe + InfoMethodSelection + InfoMonomial + InfoMorph + InfoNumtheor + InfoOperation + InfoOptions + InfoOverGr + InfoPcGroup + InfoPcNormalizer + InfoPcSubgroup + InfoPerformance + InfoPoly + InfoPrimeInt + InfoQuotientGroup + InfoQuotientSystem + InfoRSS + InfoRandIso + InfoSQ + InfoSchur + InfoSearchTable + InfoSpecPcgs + InfoTeaching + InfoText + InfoTiming + InfoTom + InfoTransversal + InfoTuples + InfoWarning + InfoZLattice + InitAbsAndIrredModules + InitEpimorphismSQ + InitFusion + InitPowerMap + InitialiseCentralRelations + InitializePackagesInfoRecords + InitializeSchreierTree + Injection + InjectionZeroMagma + InnerAutomorphism + InnerAutomorphismNC + InnerAutomorphismsAutomorphismGroup + InnerSubdirectProducts + InputLogTo + InputOutputLocalProcess + InputOutputStreamByPtyDefaultType + InputTextFile + InputTextFileStillOpen + InputTextFileType + InputTextNone + InputTextNoneType + InputTextString + InputTextStringType + InputTextUser + Insert + InsertElmList + InsertTrivialStabilizer + InstallAccessToGenerators + InstallAtExit + InstallAttributeFunction + InstallAttributeMethodByGroupGeneralMappingByImages + InstallCharReadHookFunc + InstallEqMethodForMappingsFromGenerators + InstallFactorMaintenance + InstallFlushableValue + InstallGlobalFunction + InstallHandlingByNiceBasis + InstallHiddenTrueMethod + InstallImmediateMethod + InstallIsomorphismMaintenance + InstallIsomorphismMaintenanceFunction + InstallMethod + InstallMonomialOrdering + InstallOtherMethod + InstallPcgsSeriesFromIndices + InstallSubsetMaintenance + InstallTrueMethod + InstallTrueMethodNewFilter + InstallValue + InstalledPackageVersion + Int + IntFFE + IntFFESymm + IntHexString + IntScalarProducts + IntVecFFE + Integers + IntegralizedMat + IntegratedStraightLineProgram + IntermediateGroup + IntermediateSubgroups + InternalRepresentative + InterpolatedPolynomial + IntersectBlist + IntersectSet + Intersection + IntersectionBlist + IntersectionNormalClosurePermGroup + IntersectionSet + IntersectionSumPcgs + IntersectionsTom + InvariantBilinearForm + InvariantElementaryAbelianSeries + InvariantForm + InvariantLattice + InvariantQuadraticForm + InvariantSesquilinearForm + InvariantSubgroupsElementaryAbelianGroup + InvariantSubspaceOrCyclicGroup + InvariantSubspaceOrUniformCyclicPGroup + Inverse + InverseAsWord + InverseAttr + InverseClasses + InverseGeneralMapping + InverseImmutable + InverseMap + InverseMatMod + InverseMutable + InverseOp + InversePcgs + InverseRepresentative + InverseRepresentativeWord + InverseSLPElm + InverseSM + InverseSameMutability + Irr + IrrBaumClausen + IrrConlon + IrrDixonSchneider + IrrFacsPol + IrredSolGroupList + IrredSolJSGens + IrreducibleDifferences + IrreducibleModules + IrreducibleRepresentations + IrreducibleRepresentationsByBaumClausen + IrreducibleRepresentationsDixon + IrreducibleSolvableGroup + IrreducibleSolvableGroupMS + IsANFAutomorphism + IsANFAutomorphismRep + IsAbelian + IsAbelianNumberField + IsAbelianNumberFieldPolynomialRing + IsAbelianTom + IsActionHomomorphism + IsActionHomomorphismAutomGroup + IsActionHomomorphismByActors + IsActionHomomorphismByBase + IsActionHomomorphismSubset + IsAdditiveCoset + IsAdditiveCosetDefaultRep + IsAdditiveElement + IsAdditiveElementAsMultiplicativeElementRep + IsAdditiveElementCollColl + IsAdditiveElementCollCollColl + IsAdditiveElementCollection + IsAdditiveElementList + IsAdditiveElementTable + IsAdditiveElementWithInverse + IsAdditiveElementWithInverseCollColl + IsAdditiveElementWithInverseCollCollColl + IsAdditiveElementWithInverseCollection + IsAdditiveElementWithInverseList + IsAdditiveElementWithInverseTable + IsAdditiveElementWithZero + IsAdditiveElementWithZeroCollColl + IsAdditiveElementWithZeroCollCollColl + IsAdditiveElementWithZeroCollection + IsAdditiveElementWithZeroList + IsAdditiveElementWithZeroTable + IsAdditiveGroup + IsAdditiveGroupGeneralMapping + IsAdditiveGroupHomomorphism + IsAdditiveGroupToGroupGeneralMapping + IsAdditiveGroupToGroupHomomorphism + IsAdditiveMagma + IsAdditiveMagmaWithInverses + IsAdditiveMagmaWithZero + IsAdditivelyCommutative + IsAdditivelyCommutativeElement + IsAdditivelyCommutativeElementCollColl + IsAdditivelyCommutativeElementCollection + IsAdditivelyCommutativeElementFamily + IsAlgBFRep + IsAlgExtRep + IsAlgebra + IsAlgebraGeneralMapping + IsAlgebraGeneralMappingByImagesDefaultRep + IsAlgebraHomomorphism + IsAlgebraHomomorphismFromFpRep + IsAlgebraModule + IsAlgebraModuleElement + IsAlgebraModuleElementCollection + IsAlgebraModuleElementFamily + IsAlgebraWithOne + IsAlgebraWithOneGeneralMapping + IsAlgebraWithOneHomomorphism + IsAlgebraicElement + IsAlgebraicElementCollColl + IsAlgebraicElementCollCollColl + IsAlgebraicElementCollection + IsAlgebraicElementFamily + IsAlgebraicExtension + IsAlgebraicExtensionDefaultRep + IsAlgebraicExtensionPolynomialRing + IsAlphaChar + IsAlternatingGroup + IsAnticommutative + IsAntisymmetricBinaryRelation + IsAssocWord + IsAssocWordCollection + IsAssocWordFamily + IsAssocWordWithInverse + IsAssocWordWithInverseCollection + IsAssocWordWithInverseFamily + IsAssocWordWithOne + IsAssocWordWithOneCollection + IsAssocWordWithOneFamily + IsAssociated + IsAssociative + IsAssociativeAOpDSum + IsAssociativeAOpESum + IsAssociativeElement + IsAssociativeElementCollColl + IsAssociativeElementCollection + IsAssociativeLOpDProd + IsAssociativeLOpEProd + IsAssociativeROpDProd + IsAssociativeROpEProd + IsAssociativeUOpDProd + IsAssociativeUOpEProd + IsAttributeStoringRep + IsAttributeStoringRepFlags + IsAutomorphismGroup + IsBLetterAssocWordRep + IsBLetterWordsFamily + IsBasicImageEltRep + IsBasicImageEltRepCollection + IsBasicImageGroup + IsBasicWreathLessThanOrEqual + IsBasicWreathProductOrdering + IsBasis + IsBasisByNiceBasis + IsBasisFiniteFieldRep + IsBasisOfAlgebraModuleElementSpace + IsBasisOfMonomialSpaceRep + IsBasisOfSparseRowSpaceRep + IsBasisOfWeightRepElementSpace + IsBasisWithReplacedLeftModuleRep + IsBergerCondition + IsBijective + IsBinaryRelation + IsBinaryRelationDefaultRep + IsBinaryRelationOnPointsRep + IsBlist + IsBlistRep + IsBlockMatrixRep + IsBlocksHomomorphism + IsBlocksOfActionHomomorphism + IsBlowUpIsomorphism + IsBool + IsBound + IsBoundElmWPObj + IsBoundGlobal + IsBracketRep + IsBrauerTable + IsBravaisGroup + IsBuiltFromAdditiveMagmaWithInverses + IsBuiltFromGroup + IsBuiltFromMagma + IsBuiltFromMagmaWithInverses + IsBuiltFromMagmaWithOne + IsBuiltFromMonoid + IsBuiltFromSemigroup + IsCanonicalBasis + IsCanonicalBasisAbelianNumberFieldRep + IsCanonicalBasisAlgebraicExtension + IsCanonicalBasisCyclotomicFieldRep + IsCanonicalBasisFreeMagmaRingRep + IsCanonicalBasisFullMatrixModule + IsCanonicalBasisFullRowModule + IsCanonicalBasisFullSCAlgebra + IsCanonicalBasisGaussianIntegersRep + IsCanonicalBasisIntegersRep + IsCanonicalBasisRationals + IsCanonicalNiceMonomorphism + IsCanonicalPcgs + IsCanonicalPcgsWrtSpecialPcgs + IsCentral + IsCentralFromGenerators + IsChainTypeGroup + IsChar + IsCharCollection + IsCharacter + IsCharacterTable + IsCharacterTableInProgress + IsCharacteristicMatrixPGroup + IsCharacteristicSubgroup + IsCheapConwayPolynomial + IsClassFunction + IsClassFunctionsSpace + IsClassFusionOfNormalSubgroup + IsClosedStream + IsCochain + IsCochainCollection + IsCochainsSpace + IsCocycle + IsCoeffsElms + IsCollCollsElms + IsCollCollsElmsElms + IsCollCollsElmsElmsX + IsCollLieCollsElms + IsCollection + IsCollectionFamily + IsCollsCollsElms + IsCollsCollsElmsX + IsCollsCollsElmsXX + IsCollsElms + IsCollsElmsColls + IsCollsElmsElms + IsCollsElmsElmsElms + IsCollsElmsElmsX + IsCollsElmsX + IsCollsElmsXElms + IsCollsElmsXX + IsCollsXElms + IsCollsXElmsX + IsCombinatorialCollectorRep + IsCommutative + IsCommutativeElement + IsCommutativeElementCollColl + IsCommutativeElementCollection + IsCommutativeFamily + IsCommutativeFromGenerators + IsCompatiblePair + IsComponentObjectRep + IsCompositionMappingRep + IsConfluent + IsCongruenceClass + IsConjugacyClassGroupRep + IsConjugacyClassPermGroupRep + IsConjugacyClassSubgroupsByStabilizerRep + IsConjugacyClassSubgroupsRep + IsConjugate + IsConjugatorAutomorphism + IsConjugatorIsomorphism + IsConsistentPolynomial + IsConstantRationalFunction + IsConstantTimeAccessGeneralMapping + IsConstantTimeAccessList + IsConstituentHomomorphism + IsContainedInSpan + IsCopyable + IsCyc + IsCycInt + IsCyclic + IsCyclicTom + IsCyclotomic + IsCyclotomicCollColl + IsCyclotomicCollCollColl + IsCyclotomicCollection + IsCyclotomicField + IsCyclotomicMatrixGroup + IsDataObjectRep + IsDeepThoughtCollectorRep + IsDefaultGeneralMappingRep + IsDefaultRhsTypeSingleCollector + IsDefaultTupleRep + IsDenseCoeffVectorRep + IsDenseHashRep + IsDenseList + IsDiagonalMat + IsDictionary + IsDictionaryDefaultRep + IsDigitChar + IsDihedralGroup + IsDirectSumElement + IsDirectSumElementCollection + IsDirectSumElementFamily + IsDirectSumElementsSpace + IsDirectory + IsDirectoryPath + IsDirectoryRep + IsDistributive + IsDistributiveLOpDProd + IsDistributiveLOpDSum + IsDistributiveLOpEProd + IsDistributiveLOpESum + IsDistributiveROpDProd + IsDistributiveROpDSum + IsDistributiveROpEProd + IsDistributiveROpESum + IsDistributiveUOpDProd + IsDistributiveUOpDSum + IsDistributiveUOpEProd + IsDistributiveUOpESum + IsDivisionRing + IsDocumentedVariable + IsDomain + IsDoneIterator + IsDoubleCoset + IsDoubleCosetDefaultRep + IsDuplicateFree + IsDuplicateFreeCollection + IsDuplicateFreeList + IsDxLargeGroup + IsElementFinitePolycyclicGroup + IsElementFinitePolycyclicGroupCollection + IsElementOfFpAlgebra + IsElementOfFpAlgebraCollection + IsElementOfFpAlgebraFamily + IsElementOfFpGroup + IsElementOfFpGroupCollection + IsElementOfFpGroupFamily + IsElementOfFpMonoid + IsElementOfFpMonoidCollection + IsElementOfFpMonoidFamily + IsElementOfFpSemigroup + IsElementOfFpSemigroupCollection + IsElementOfFpSemigroupFamily + IsElementOfFreeGroup + IsElementOfFreeGroupFamily + IsElementOfFreeMagmaRing + IsElementOfFreeMagmaRingCollection + IsElementOfFreeMagmaRingFamily + IsElementOfMagmaRingModuloRelations + IsElementOfMagmaRingModuloRelationsCollection + IsElementOfMagmaRingModuloRelationsFamily + IsElementOfMagmaRingModuloSpanOfZeroFamily + IsElementaryAbelian + IsElementsFamilyByRws + IsElmsCoeffs + IsElmsCollColls + IsElmsCollCollsX + IsElmsCollLieColls + IsElmsColls + IsElmsCollsX + IsElmsCollsXX + IsElmsLieColls + IsEmbeddingDirectProductPermGroup + IsEmbeddingImprimitiveWreathProductPermGroup + IsEmbeddingMagmaMagmaRing + IsEmbeddingProductActionWreathProductPermGroup + IsEmbeddingRingMagmaRing + IsEmbeddingWreathProductPermGroup + IsEmpty + IsEmptyRowVectorRep + IsEmptyString + IsEndOfStream + IsEndoGeneralMapping + IsEndoMapping + IsEnumeratorByFunctions + IsEnumeratorByFunctionsRep + IsEnumeratorByPcgsRep + IsEqualSet + IsEquivalenceClass + IsEquivalenceClassDefaultRep + IsEquivalenceRelation + IsEquivalenceRelationDefaultRep + IsEquivalentByFp + IsEuclideanRing + IsEvenInt + IsExecutableFile + IsExistingFile + IsExtAElement + IsExtAElementCollColl + IsExtAElementCollection + IsExtAElementList + IsExtAElementTable + IsExtASet + IsExtLElement + IsExtLElementCollColl + IsExtLElementCollection + IsExtLElementList + IsExtLElementTable + IsExtLSet + IsExtRElement + IsExtRElementCollColl + IsExtRElementCollection + IsExtRElementList + IsExtRElementTable + IsExtRSet + IsExtUSet + IsExtensibleGeneralMapping + IsExtensiblePartialMapping + IsExternalOrbit + IsExternalOrbitByStabilizerRep + IsExternalSet + IsExternalSetByActorsRep + IsExternalSetByOperatorsRep + IsExternalSetByPcgs + IsExternalSetDefaultRep + IsExternalSubset + IsFFE + IsFFECollColl + IsFFECollCollColl + IsFFECollection + IsFFEFamily + IsFFEMatrixGroup + IsFFEMatrixGroupOverLargeSpace + IsFLMLOR + IsFLMLORWithOne + IsFamFamFam + IsFamFamFamX + IsFamFamX + IsFamFamXY + IsFamLieFam + IsFamXFam + IsFamXFamY + IsFamXYFamZ + IsFamily + IsFamilyDefaultRep + IsFamilyElementOfFreeLieAlgebra + IsFamilyOfFamilies + IsFamilyOfTypes + IsFamilyOverFullCoefficientsFamily + IsFamilyPcgs + IsField + IsFieldControlledByGaloisGroup + IsFieldElementsSpace + IsFieldHomomorphism + IsFilter + IsFinite + IsFiniteBasisDefault + IsFiniteDimensional + IsFiniteFieldPolynomialRing + IsFiniteOrderElement + IsFiniteOrderElementCollColl + IsFiniteOrderElementCollection + IsFiniteOrdersPcgs + IsFiniteSemigroupGreensRelation + IsFinitelyGeneratedGroup + IsFixedStabilizer + IsFlatHashTable + IsFlexibleGeneralMapping + IsFlexiblePartialMapping + IsFpAlgebraElementsSpace + IsFpGroup + IsFpMonoid + IsFpSemigroup + IsFptoSCAMorphism + IsFrattiniFree + IsFreeGroup + IsFreeLeftModule + IsFreeMagma + IsFreeMagmaRing + IsFreeMagmaRingWithOne + IsFreeMonoid + IsFreeSemigroup + IsFrobeniusAutomorphism + IsFromFpGroupGeneralMapping + IsFromFpGroupGeneralMappingByImages + IsFromFpGroupHomomorphism + IsFromFpGroupHomomorphismByImages + IsFromFpGroupStdGensGeneralMappingByImages + IsFromFpGroupStdGensHomomorphismByImages + IsFullFpAlgebra + IsFullHomModule + IsFullMatrixModule + IsFullRowModule + IsFullSCAlgebra + IsFullSubgroupGLorSLRespectingBilinearForm + IsFullSubgroupGLorSLRespectingQuadraticForm + IsFullSubgroupGLorSLRespectingSesquilinearForm + IsFullTransformationSemigroup + IsFunction + IsGL + IsGaussInt + IsGaussRat + IsGaussianIntegers + IsGaussianMatrixSpace + IsGaussianRationals + IsGaussianRowSpace + IsGaussianSpace + IsGeneralLinearGroup + IsGeneralMapping + IsGeneralMappingCollection + IsGeneralMappingFamily + IsGeneralPcgs + IsGeneralizedCartanMatrix + IsGeneralizedDomain + IsGeneralizedRowVector + IsGeneratorsOfMagmaWithInverses + IsGenericCharacterTableRep + IsGenericFiniteSpace + IsGreensClass + IsGreensDClass + IsGreensDRelation + IsGreensHClass + IsGreensHRelation + IsGreensJClass + IsGreensJRelation + IsGreensLClass + IsGreensLRelation + IsGreensLessThanOrEqual + IsGreensRClass + IsGreensRRelation + IsGreensRelation + IsGroup + IsGroupGeneralMapping + IsGroupGeneralMappingByAsGroupGeneralMappingByImages + IsGroupGeneralMappingByImages + IsGroupGeneralMappingByPcgs + IsGroupHClass + IsGroupHomomorphism + IsGroupOfAutomorphisms + IsGroupOfAutomorphismsFiniteGroup + IsGroupOfFamily + IsGroupRing + IsGroupToAdditiveGroupGeneralMapping + IsGroupToAdditiveGroupHomomorphism + IsHandledByNiceBasis + IsHandledByNiceMonomorphism + IsHash + IsHashTable + IsHasseDiagram + IsHomCoset + IsHomCosetCollection + IsHomCosetOfAdditiveElt + IsHomCosetOfFp + IsHomCosetOfMatrix + IsHomCosetOfPerm + IsHomCosetOfTuple + IsHomCosetToAdditiveElt + IsHomCosetToAdditiveEltCollection + IsHomCosetToAdditiveEltRep + IsHomCosetToFp + IsHomCosetToFpCollection + IsHomCosetToFpRep + IsHomCosetToMatrix + IsHomCosetToMatrixCollection + IsHomCosetToMatrixRep + IsHomCosetToObjectRep + IsHomCosetToPerm + IsHomCosetToPermCollection + IsHomCosetToPermRep + IsHomCosetToTuple + IsHomCosetToTupleCollection + IsHomCosetToTupleRep + IsHomQuotientGroup + IsHomogeneousList + IsIdeal + IsIdealInParent + IsIdealOp + IsIdempotent + IsIdenticalObj + IsIdenticalObjFamiliesColObjObj + IsIdenticalObjFamiliesColObjObjObj + IsIdenticalObjFamiliesColXXXObj + IsIdenticalObjFamiliesColXXXXXXObj + IsIdenticalObjFamiliesRwsObj + IsIdenticalObjFamiliesRwsObjObj + IsIdenticalObjFamiliesRwsObjXXX + IsIdenticalObjObjObjX + IsIdenticalObjObjXObj + IsImfMatrixGroup + IsImpossible + IsInBasicOrbit + IsInCenter + IsInCentre + IsInChain + IsIncomparableUnder + IsInducedFromNormalSubgroup + IsInducedPcgs + IsInducedPcgsRep + IsInducedPcgsWrtSpecialPcgs + IsInfBitsAssocWord + IsInfBitsFamily + IsInfiniteListOfGeneratorsRep + IsInfiniteListOfNamesRep + IsInfinity + IsInfoClass + IsInfoClassCollection + IsInfoClassListRep + IsInfoSelector + IsInjective + IsInnerAutomorphism + IsInputOutputStream + IsInputOutputStreamByPtyRep + IsInputStream + IsInputTextFileRep + IsInputTextNone + IsInputTextNoneRep + IsInputTextStream + IsInputTextStringRep + IsInt + IsIntegerMatrixGroup + IsIntegers + IsIntegralBasis + IsIntegralCyclotomic + IsIntegralRing + IsInternalRep + IsInternallyConsistent + IsInverseGeneralMappingRep + IsIrreducible + IsIrreducibleCharacter + IsIrreducibleRingElement + IsIterator + IsIteratorByFunctions + IsIteratorByFunctionsRep + IsJacobianElement + IsJacobianElementCollColl + IsJacobianElementCollection + IsJacobianRing + IsKernelPcWord + IsKnuthBendixRewritingSystem + IsKnuthBendixRewritingSystemRep + IsLDistributive + IsLatticeOrderBinaryRelation + IsLatticeSubgroupsRep + IsLaurentPolynomial + IsLaurentPolynomialDefaultRep + IsLaurentPolynomialsFamily + IsLaurentPolynomialsFamilyElement + IsLeftActedOnByDivisionRing + IsLeftActedOnByRing + IsLeftActedOnBySuperset + IsLeftAlgebraModule + IsLeftAlgebraModuleElement + IsLeftAlgebraModuleElementCollection + IsLeftIdeal + IsLeftIdealFromGenerators + IsLeftIdealInParent + IsLeftIdealOp + IsLeftMagmaCongruence + IsLeftMagmaIdeal + IsLeftModule + IsLeftModuleGeneralMapping + IsLeftModuleHomomorphism + IsLeftOperatorAdditiveGroup + IsLeftOperatorRing + IsLeftOperatorRingWithOne + IsLeftSemigroupCongruence + IsLeftSemigroupIdeal + IsLeftVectorSpace + IsLessThanOrEqualUnder + IsLessThanUnder + IsLetterAssocWordRep + IsLetterWordsFamily + IsLexicographicallyLess + IsLibTomRep + IsLibraryCharacterTableRep + IsLieAbelian + IsLieAlgebra + IsLieEmbeddingRep + IsLieFamFam + IsLieMatrix + IsLieNilpotent + IsLieNilpotentElement + IsLieObject + IsLieObjectCollection + IsLieObjectsModule + IsLieSolvable + IsLinearActionHomomorphism + IsLinearGeneralMappingByImagesDefaultRep + IsLinearMapping + IsLinearMappingByMatrixDefaultRep + IsLinearMappingsModule + IsLinearlyIndependent + IsLinearlyPrimitive + IsList + IsListDefault + IsListDictionary + IsListHashTable + IsListLookupDictionary + IsListOrCollection + IsLockedRepresentationVector + IsLookupDictionary + IsLowerAlphaChar + IsLowerTriangularMat + IsMagma + IsMagmaByMultiplicationTableObj + IsMagmaCollsMagmaRingColls + IsMagmaCongruence + IsMagmaHomomorphism + IsMagmaIdeal + IsMagmaRingModuloRelations + IsMagmaRingModuloSpanOfZero + IsMagmaRingObjDefaultRep + IsMagmaRingsMagmas + IsMagmaRingsRings + IsMagmaWithInverses + IsMagmaWithInversesIfNonzero + IsMagmaWithMultiplicativeZeroAdjoinedElementRep + IsMagmaWithOne + IsMagmasMagmaRings + IsMapping + IsMappingByFunctionRep + IsMappingByFunctionWithInverseRep + IsMatchingSublist + IsMatrix + IsMatrixCollection + IsMatrixFLMLOR + IsMatrixGroup + IsMatrixModule + IsMatrixSpace + IsMemberPcSeriesPermGroup + IsMinimalNonmonomial + IsModuloPcgs + IsModuloPcgsFpGroupRep + IsModuloPcgsPermGroupRep + IsModuloPcgsRep + IsModuloTailPcgsByListRep + IsModuloTailPcgsRep + IsModulusRep + IsMonoid + IsMonomial + IsMonomialCharacter + IsMonomialCharacterTable + IsMonomialElement + IsMonomialElementCollection + IsMonomialElementFamily + IsMonomialElementRep + IsMonomialGroup + IsMonomialMatrix + IsMonomialNumber + IsMonomialOrdering + IsMonomialOrderingDefaultRep + IsMultiplicativeElement + IsMultiplicativeElementCollColl + IsMultiplicativeElementCollCollColl + IsMultiplicativeElementCollection + IsMultiplicativeElementList + IsMultiplicativeElementTable + IsMultiplicativeElementWithInverse + IsMultiplicativeElementWithInverseByPolycyclicCollector + IsMultiplicativeElementWithInverseByPolycyclicCollectorCollection + IsMultiplicativeElementWithInverseByRws + IsMultiplicativeElementWithInverseCollColl + IsMultiplicativeElementWithInverseCollCollColl + IsMultiplicativeElementWithInverseCollection + IsMultiplicativeElementWithInverseList + IsMultiplicativeElementWithInverseTable + IsMultiplicativeElementWithOne + IsMultiplicativeElementWithOneCollColl + IsMultiplicativeElementWithOneCollCollColl + IsMultiplicativeElementWithOneCollection + IsMultiplicativeElementWithOneList + IsMultiplicativeElementWithOneTable + IsMultiplicativeElementWithZero + IsMultiplicativeElementWithZeroCollection + IsMultiplicativeGeneralizedRowVector + IsMultiplicativeZero + IsMutable + IsMutableBasis + IsMutableBasisByImmutableBasisRep + IsMutableBasisOfGaussianMatrixSpaceRep + IsMutableBasisOfGaussianRowSpaceRep + IsMutableBasisViaNiceMutableBasisRep + IsMutableBasisViaUnderlyingMutableBasisRep + IsNBitsPcWordRep + IsNameOfNoninstalledTableOfMarks + IsNaturalAlternatingGroup + IsNaturalGL + IsNaturalGLnZ + IsNaturalHomomorphismPcGroupRep + IsNaturalSL + IsNaturalSLnZ + IsNaturalSymmetricGroup + IsNearAdditiveElement + IsNearAdditiveElementCollColl + IsNearAdditiveElementCollCollColl + IsNearAdditiveElementCollection + IsNearAdditiveElementList + IsNearAdditiveElementTable + IsNearAdditiveElementWithInverse + IsNearAdditiveElementWithInverseCollColl + IsNearAdditiveElementWithInverseCollCollColl + IsNearAdditiveElementWithInverseCollection + IsNearAdditiveElementWithInverseList + IsNearAdditiveElementWithInverseTable + IsNearAdditiveElementWithZero + IsNearAdditiveElementWithZeroCollColl + IsNearAdditiveElementWithZeroCollCollColl + IsNearAdditiveElementWithZeroCollection + IsNearAdditiveElementWithZeroList + IsNearAdditiveElementWithZeroTable + IsNearAdditiveGroup + IsNearAdditiveMagma + IsNearAdditiveMagmaWithInverses + IsNearAdditiveMagmaWithZero + IsNearRing + IsNearRingElement + IsNearRingElementCollColl + IsNearRingElementCollCollColl + IsNearRingElementCollection + IsNearRingElementFamily + IsNearRingElementList + IsNearRingElementTable + IsNearRingElementWithInverse + IsNearRingElementWithInverseCollColl + IsNearRingElementWithInverseCollCollColl + IsNearRingElementWithInverseCollection + IsNearRingElementWithInverseList + IsNearRingElementWithInverseTable + IsNearRingElementWithOne + IsNearRingElementWithOneCollColl + IsNearRingElementWithOneCollCollColl + IsNearRingElementWithOneCollection + IsNearRingElementWithOneList + IsNearRingElementWithOneTable + IsNearRingWithOne + IsNearlyCharacterTable + IsNegInt + IsNegRat + IsNiceMonomorphism + IsNilpQuotientSystem + IsNilpotent + IsNilpotentCharacterTable + IsNilpotentElement + IsNilpotentGroup + IsNilpotentTom + IsNoImmediateMethodsObject + IsNonGaussianMatrixSpace + IsNonGaussianRowSpace + IsNonSPGeneralMapping + IsNonSPMappingByFunctionRep + IsNonSPMappingByFunctionWithInverseRep + IsNonTrivial + IsNonassocWord + IsNonassocWordCollection + IsNonassocWordFamily + IsNonassocWordWithOne + IsNonassocWordWithOneCollection + IsNonassocWordWithOneFamily + IsNoncharacteristicMatrixPGroup + IsNonnegativeIntegers + IsNormal + IsNormalBasis + IsNormalForm + IsNormalInParent + IsNormalOp + IsNotElmsColls + IsNotIdenticalObj + IsNullMapMatrix + IsNumberField + IsNumeratorParentForExponentsRep + IsNumeratorParentPcgsFamilyPcgs + IsObjToBePrinted + IsObject + IsOddAdditiveNestingDepthFamily + IsOddAdditiveNestingDepthObject + IsOddInt + IsOne + IsOperation + IsOperationAlgebraHomomorphismDefaultRep + IsOrdering + IsOrderingOnFamilyOfAssocWords + IsOrdinaryMatrix + IsOrdinaryMatrixCollection + IsOrdinaryTable + IsOutputStream + IsOutputTextFileRep + IsOutputTextNone + IsOutputTextNoneRep + IsOutputTextStream + IsOutputTextStringRep + IsPGroup + IsPNilpotent + IsPNilpotentOp + IsPQuotientSystem + IsPSL + IsPSolvable + IsPSolvableCharacterTable + IsPSolvableCharacterTableOp + IsPSolvableOp + IsPackedElementDefaultRep + IsPadicExtensionNumber + IsPadicExtensionNumberFamily + IsPadicNumber + IsPadicNumberCollColl + IsPadicNumberCollection + IsPadicNumberFamily + IsPadicNumberList + IsPadicNumberTable + IsParentPcgsFamilyPcgs + IsPartialOrderBinaryRelation + IsPartition + IsPcGroup + IsPcGroupGeneralMappingByImages + IsPcGroupHomomorphismByImages + IsPcgs + IsPcgsCentralSeries + IsPcgsChiefSeries + IsPcgsDefaultRep + IsPcgsElementaryAbelianSeries + IsPcgsFamily + IsPcgsPCentralSeriesPGroup + IsPcgsPermGroupRep + IsPcgsToPcgsGeneralMappingByImages + IsPcgsToPcgsHomomorphism + IsPerfect + IsPerfectCharacterTable + IsPerfectGroup + IsPerfectLibraryGroup + IsPerfectTom + IsPerm + IsPermCollColl + IsPermCollection + IsPermGroup + IsPermGroupGeneralMappingByImages + IsPermGroupHomomorphismByImages + IsPermOnEnumerator + IsPlistRep + IsPolycyclicCollector + IsPolycyclicGroup + IsPolynomial + IsPolynomialDefaultRep + IsPolynomialFunction + IsPolynomialFunctionCollection + IsPolynomialFunctionsFamily + IsPolynomialFunctionsFamilyElement + IsPolynomialRing + IsPolynomialRingIdeal + IsPosInt + IsPosRat + IsPositionDictionary + IsPositionLookupDictionary + IsPositionalObjectRep + IsPositionsList + IsPositiveIntegers + IsPowerCommutatorCollector + IsPowerConjugateCollector + IsPreOrderBinaryRelation + IsPreimagesByAsGroupGeneralMappingByImages + IsPresentation + IsPresentationDefaultRep + IsPrimGrpIterRep + IsPrime + IsPrimeField + IsPrimeInt + IsPrimeOrdersPcgs + IsPrimePowerInt + IsPrimitive + IsPrimitiveAffine + IsPrimitiveCharacter + IsPrimitiveMatrixGroup + IsPrimitivePolynomial + IsPrimitiveRootMod + IsProbablyPrimeInt + IsProbablyPrimeIntWithFail + IsProjectionDirectProductPermGroup + IsProjectionSubdirectProductPermGroup + IsPseudoCanonicalBasisFullHomModule + IsPurePadicNumber + IsPurePadicNumberFamily + IsQuasiDihedralGroup + IsQuasiPrimitive + IsQuaternion + IsQuaternionCollColl + IsQuaternionCollection + IsQuaternionGroup + IsQuickPositionList + IsQuotientSemigroup + IsQuotientSystem + IsQuotientToAdditiveGroup + IsQuotientToFpGroup + IsQuotientToMatrixGroup + IsQuotientToPermGroup + IsQuotientToTupleGroup + IsRDistributive + IsRange + IsRangeRep + IsRat + IsRationalClassGroupRep + IsRationalClassPermGroupRep + IsRationalFunction + IsRationalFunctionCollection + IsRationalFunctionDefaultRep + IsRationalFunctionOverField + IsRationalFunctionsFamily + IsRationalFunctionsFamilyElement + IsRationalMatrixGroup + IsRationals + IsRationalsPolynomialRing + IsReadOnlyGVar + IsReadOnlyGlobal + IsReadableFile + IsRecord + IsRecordCollColl + IsRecordCollection + IsRectangularTable + IsRectangularTablePlist + IsReduced + IsReducedConfluentRewritingSystem + IsReducedForm + IsReductionOrdering + IsReesCongruence + IsReesCongruenceSemigroup + IsReesMatrixSemigroup + IsReesMatrixSemigroupElement + IsReesMatrixSemigroupElementCollection + IsReesMatrixSemigroupElementRep + IsReesZeroMatrixSemigroup + IsReesZeroMatrixSemigroupElement + IsReesZeroMatrixSemigroupElementCollection + IsReflexiveBinaryRelation + IsRegular + IsRegularDClass + IsRegularSemigroup + IsRegularSemigroupElement + IsRelativeBasisDefaultRep + IsRelativelySM + IsRestrictedLieAlgebra + IsRewritingSystem + IsRightActedOnByDivisionRing + IsRightActedOnByRing + IsRightActedOnBySuperset + IsRightAlgebraModule + IsRightAlgebraModuleElement + IsRightAlgebraModuleElementCollection + IsRightCoset + IsRightCosetDefaultRep + IsRightIdeal + IsRightIdealFromGenerators + IsRightIdealInParent + IsRightIdealOp + IsRightMagmaCongruence + IsRightMagmaIdeal + IsRightModule + IsRightOperatorAdditiveGroup + IsRightSemigroupCongruence + IsRightSemigroupIdeal + IsRightTransversal + IsRightTransversalCollection + IsRightTransversalFpGroupRep + IsRightTransversalPcGroupRep + IsRightTransversalPermGroupRep + IsRightTransversalRep + IsRightTransversalViaCosetsRep + IsRing + IsRingCollsMagmaRingColls + IsRingElement + IsRingElementCollColl + IsRingElementCollCollColl + IsRingElementCollection + IsRingElementFamily + IsRingElementList + IsRingElementTable + IsRingElementWithInverse + IsRingElementWithInverseCollColl + IsRingElementWithInverseCollCollColl + IsRingElementWithInverseCollection + IsRingElementWithInverseList + IsRingElementWithInverseTable + IsRingElementWithOne + IsRingElementWithOneCollColl + IsRingElementWithOneCollCollColl + IsRingElementWithOneCollection + IsRingElementWithOneList + IsRingElementWithOneTable + IsRingGeneralMapping + IsRingHomomorphism + IsRingWithOne + IsRingWithOneGeneralMapping + IsRingWithOneHomomorphism + IsRingsMagmaRings + IsRootSystem + IsRootSystemFromLieAlgebra + IsRowModule + IsRowSpace + IsRowVector + IsSCAlgebraObj + IsSCAlgebraObjCollColl + IsSCAlgebraObjCollCollColl + IsSCAlgebraObjCollection + IsSCAlgebraObjFamily + IsSCAlgebraObjSpace + IsSL + IsSPGeneralMapping + IsSPMappingByFunctionRep + IsSPMappingByFunctionWithInverseRep + IsSSortedList + IsScalar + IsScalarCollColl + IsScalarCollection + IsScalarList + IsScalarTable + IsSearchTable + IsSemiEchelonBasisOfGaussianMatrixSpaceRep + IsSemiEchelonBasisOfGaussianRowSpaceRep + IsSemiEchelonized + IsSemiRegular + IsSemigroup + IsSemigroupCongruence + IsSemigroupIdeal + IsSemiring + IsSemiringWithOne + IsSemiringWithOneAndZero + IsSemiringWithZero + IsSet + IsShortLexLessThanOrEqual + IsShortLexOrdering + IsSimple + IsSimpleAlgebra + IsSimpleCharacterTable + IsSimpleGroup + IsSimpleSemigroup + IsSingleCollectorRep + IsSingleValued + IsSkewFieldFamily + IsSlicedPerm + IsSlicedPermInv + IsSmallIntRep + IsSmallList + IsSolvable + IsSolvableCharacterTable + IsSolvableGroup + IsSolvableTom + IsSortDictionary + IsSortLookupDictionary + IsSortedList + IsSortedPcgsRep + IsSpaceOfElementsOfMagmaRing + IsSpaceOfRationalFunctions + IsSpaceOfUEAElements + IsSparseHashRep + IsSparseRowSpaceElement + IsSparseRowSpaceElementCollection + IsSparseRowSpaceElementFamily + IsSpecialLinearGroup + IsSpecialPcgs + IsSporadicSimple + IsSporadicSimpleCharacterTable + IsSporadicSimpleGroup + IsStabChainViaChainSubgroup + IsStandardGeneratorsOfGroup + IsStandardized + IsStraightLineProgElm + IsStraightLineProgram + IsStream + IsString + IsStringRep + IsSubalgebraFpAlgebra + IsSubgroup + IsSubgroupFgGroup + IsSubgroupFpGroup + IsSubgroupOfWholeGroupByQuotientRep + IsSubgroupSL + IsSubmonoidFpMonoid + IsSubnormal + IsSubnormallyMonomial + IsSubsemigroupFpSemigroup + IsSubsemigroupReesMatrixSemigroup + IsSubsemigroupReesZeroMatrixSemigroup + IsSubset + IsSubsetBlist + IsSubsetInducedNumeratorModuloTailPcgsRep + IsSubsetInducedPcgsRep + IsSubsetLocallyFiniteGroup + IsSubsetSet + IsSubspace + IsSubspacesFullRowSpaceDefaultRep + IsSubspacesVectorSpace + IsSubspacesVectorSpaceDefaultRep + IsSupersolvable + IsSupersolvableCharacterTable + IsSupersolvableGroup + IsSurjective + IsSyllableAssocWordRep + IsSyllableWordsFamily + IsSymmetricBinaryRelation + IsSymmetricGroup + IsSymmetricPowerElement + IsSymmetricPowerElementCollection + IsTable + IsTableOfMarks + IsTableOfMarksWithGens + IsTailInducedPcgsRep + IsTensorElement + IsTensorElementCollection + IsToBeDefinedObj + IsToFpGroupGeneralMappingByImages + IsToFpGroupHomomorphismByImages + IsToPcGroupGeneralMappingByImages + IsToPcGroupHomomorphismByImages + IsToPermGroupGeneralMappingByImages + IsToPermGroupHomomorphismByImages + IsTotal + IsTotalOrdering + IsTransformation + IsTransformationCollection + IsTransformationMonoid + IsTransformationRep + IsTransformationRepOfEndo + IsTransformationSemigroup + IsTransitive + IsTransitiveBinaryRelation + IsTranslationInvariantOrdering + IsTransvByDirProd + IsTransvByDirProdCollection + IsTransvByHomomorphism + IsTransvByHomomorphismCollection + IsTransvBySchreierTree + IsTransvBySchreierTreeCollection + IsTransvBySiftFunct + IsTransvBySiftFunctCollection + IsTransvByTrivSubgrp + IsTransvByTrivSubgrpCollection + IsTrivial + IsTrivialAOpEZero + IsTrivialHomCoset + IsTrivialLOpEOne + IsTrivialLOpEZero + IsTrivialRBase + IsTrivialROpEOne + IsTrivialROpEZero + IsTrivialUOpEOne + IsTrivialUOpEZero + IsTuple + IsTupleCollection + IsTupleFamily + IsTwoSidedIdeal + IsTwoSidedIdealInParent + IsTwoSidedIdealOp + IsType + IsTypeDefaultRep + IsUEALatticeElement + IsUEALatticeElementCollection + IsUEALatticeElementFamily + IsUFDFamily + IsUniformMatrixGroup + IsUniqueFactorizationRing + IsUnit + IsUnivariatePolynomial + IsUnivariatePolynomialRing + IsUnivariatePolynomialsFamily + IsUnivariatePolynomialsFamilyElement + IsUnivariateRationalFunction + IsUnivariateRationalFunctionDefaultRep + IsUnknown + IsUnknownDefaultRep + IsUnsortedPcgsRep + IsUpToDatePolycyclicCollector + IsUpperActedOnByGroup + IsUpperActedOnBySuperset + IsUpperAlphaChar + IsUpperTriangularMat + IsValidIdentifier + IsVector + IsVectorCollColl + IsVectorCollection + IsVectorList + IsVectorSearchTable + IsVectorSearchTableDefaultRep + IsVectorSpace + IsVectorSpaceHomomorphism + IsVectorTable + IsVirtualCharacter + IsWLetterAssocWordRep + IsWLetterWordsFamily + IsWPObj + IsWeakPointerObject + IsWedgeElement + IsWedgeElementCollection + IsWeightLexOrdering + IsWeightRepElement + IsWeightRepElementCollection + IsWeightRepElementFamily + IsWellFoundedOrdering + IsWeylGroup + IsWholeFamily + IsWord + IsWordCollection + IsWordWithInverse + IsWordWithOne + IsWreathProductElement + IsWreathProductElementCollection + IsWreathProductElementDefaultRep + IsWreathProductOrdering + IsWritableFile + IsZDFRE + IsZDFRECollColl + IsZDFRECollection + IsZero + IsZeroCochainRep + IsZeroCyc + IsZeroGroup + IsZeroMultiplicationRing + IsZeroRationalFunction + IsZeroSimpleSemigroup + IsZeroSquaredElement + IsZeroSquaredElementCollColl + IsZeroSquaredElementCollection + IsZeroSquaredRing + IsZmodnZObj + IsZmodnZObjNonprime + IsZmodnZObjNonprimeCollColl + IsZmodnZObjNonprimeCollCollColl + IsZmodnZObjNonprimeCollection + IsZmodnZObjNonprimeFamily + IsZmodpZObj + IsZmodpZObjLarge + IsZmodpZObjSmall + IsolatePoint + IsomorphicSubgroups + IsomorphismAbelianGroups + IsomorphismFpAlgebra + IsomorphismFpFLMLOR + IsomorphismFpGroup + IsomorphismFpGroupByCompositionSeries + IsomorphismFpGroupByGenerators + IsomorphismFpGroupByGeneratorsNC + IsomorphismFpGroupByPcgs + IsomorphismFpGroupBySubnormalSeries + IsomorphismFpMonoid + IsomorphismFpSemigroup + IsomorphismGroups + IsomorphismMatrixAlgebra + IsomorphismMatrixFLMLOR + IsomorphismPcGroup + IsomorphismPermGroup + IsomorphismPermGroupImfGroup + IsomorphismPermGroupOrFailFpGroup + IsomorphismPermGroups + IsomorphismReesMatrixSemigroup + IsomorphismRefinedPcGroup + IsomorphismSCAlgebra + IsomorphismSCFLMLOR + IsomorphismSimplifiedFpGroup + IsomorphismSolvableSmallGroups + IsomorphismSpecialPcGroup + IsomorphismToFullRowSpace + IsomorphismTransformationSemigroup + IsomorphismTypeInfoFiniteSimpleGroup + Iterated + Iterator + IteratorByBasis + IteratorByFunctions + IteratorList + IteratorSorted + IteratorsFamily + Jacobi + JenningsLieAlgebra + JenningsSeries + JoinEquivalenceRelations + JoinMagmaCongruences + JoinSemigroupCongruences + JoinStringsWithSeparator + JordanDecomposition + KBOverlaps + KappaPerp + Kernel + KernelHcommaC + KernelOfAdditiveGeneralMapping + KernelOfCharacter + KernelOfHomQuotientGroup + KernelOfMultiplicativeGeneralMapping + KernelOfTransformation + KernelUnderDualAction + KeyDependentOperation + KillingMatrix + KnownAttributesOfObject + KnownNaturalHomomorphismsPool + KnownPropertiesOfObject + KnownTruePropertiesOfObject + KnowsDictionary + KnowsHowToDecompose + KnuthBendixRewritingSystem + KroneckerProduct + KuKGenerators + LClassOfHClass + LGFirst + LGHeads + LGLayers + LGLength + LGTails + LGWeights + LLLReducedBasis + LLLReducedGramMat + LLLint + LMPSLPSeed + LaTeX + LaTeXObj + LaTeXStringDecompositionMatrix + LaTeXToHTMLString + LabsLims + Lambda + LargeGaloisField + LargestElementGroup + LargestElementStabChain + LargestMovedPoint + LargestMovedPointPerm + LargestMovedPointPerms + LargestUnknown + LastHashIndex + LastReadValue + LastSystemError + LatticeByCyclicExtension + LatticeGeneratorsInUEA + LatticeSubgroups + LatticeSubgroupsByTom + LaurentPolynomialByCoefficients + LaurentPolynomialByExtRep + Lcm + LcmInt + LcmOp + LcmPP + LeadCoeffsIGS + LeadingCoefficient + LeadingCoefficientOfPolynomial + LeadingExponentOfPcElement + LeadingMonomial + LeadingMonomialOfPolynomial + LeadingMonomialPosExtRep + LeadingTermOfPolynomial + LeadingUEALatticeMonomial + LeastBadComplementLayer + LeastBadHallLayer + LeftActingAlgebra + LeftActingDomain + LeftActingGroup + LeftActingRingOfIdeal + LeftAction + LeftAlgebraModule + LeftAlgebraModuleByGenerators + LeftDerivations + LeftIdeal + LeftIdealByGenerators + LeftIdealNC + LeftMagmaCongruence + LeftMagmaCongruenceByGeneratingPairs + LeftMagmaIdeal + LeftMagmaIdealByGenerators + LeftModuleByGenerators + LeftModuleByHomomorphismToMatAlg + LeftModuleGeneralMappingByImages + LeftModuleGeneratorsForIdealFromGenerators + LeftModuleHomomorphismByImages + LeftModuleHomomorphismByImagesNC + LeftModuleHomomorphismByMatrix + LeftNormedComm + LeftQuotient + LeftQuotientPowerPcgsElement + LeftReduceUEALatticeElement + LeftSemigroupCongruenceByGeneratingPairs + LeftSemigroupIdealEnumeratorDataGetElement + LeftShiftRowVector + Legendre + Length + LengthOfDescendingSeries + LengthOfLongestCommonPrefixOfTwoAssocWords + LengthWPObj + LengthsTom + LenstraBase + LessBoxedObj + LessThanFunction + LessThanOrEqualFunction + LetterRepAssocWord + LetterRepWordsLessFunc + LevelsOfGenerators + LeviMalcevDecomposition + LexicographicOrdering + LexicographicOrderingNC + LieAlgebra + LieAlgebraByDomain + LieAlgebraByStructureConstants + LieBracket + LieCenter + LieCentralizer + LieCentralizerInParent + LieCentre + LieCoboundaryOperator + LieDerivedSeries + LieDerivedSubalgebra + LieFamily + LieLowerCentralSeries + LieNilRadical + LieNormalizer + LieNormalizerInParent + LieObject + LieSolvableRadical + LieUpperCentralSeries + LiftAbsAndIrredModules + LiftEpimorphism + LiftEpimorphismSQ + LiftInduciblePair + LiftedInducedPcgs + LiftedPcElement + LineNumberStringPosition + LinearAction + LinearActionBasis + LinearActionLayer + LinearCharacters + LinearCombination + LinearCombinationPcgs + LinearCombinationVecs + LinearGroupParameters + LinearIndependentColumns + LinearOperation + LinearOperationLayer + LinesOfStraightLineProgram + List + ListBlist + ListHashParams + ListN + ListOp + ListPerm + ListSorted + ListStabChain + ListWithIdenticalEntries + ListX + ListXHelp + ListsFamily + LoadAllPackages + LoadDynamicModule + LoadPackage + LoadPackageDocumentation + LoadStaticModule + LoadedModules + LockNaturalHomomorphismsPool + Log + LogFFE + LogInputTo + LogInt + LogMod + LogModRhoIterate + LogModShanks + LogOutputTo + LogTo + LongestWeylWordPerm + LookupDictionary + LowIndexSubgroupsFpGroup + LowerCentralSeries + LowerCentralSeriesOfGroup + LowerTriangularMatrix + LowercaseString + Lucas + MATINTbezout + MATINTmgcdex + MATINTrgcd + MATINTsplit + Magma + MagmaByGenerators + MagmaByMultiplicationTable + MagmaByMultiplicationTableCreator + MagmaCongruenceByGeneratingPairs + MagmaCongruencePartition + MagmaElement + MagmaGeneratorsOfFamily + MagmaHomomorphismByFunctionNC + MagmaIdeal + MagmaIdealByGenerators + MagmaInputString + MagmaIsomorphismByFunctionsNC + MagmaRingModuloSpanOfZero + MagmaWithInverses + MagmaWithInversesByGenerators + MagmaWithInversesByMultiplicationTable + MagmaWithOne + MagmaWithOneByGenerators + MagmaWithOneByMultiplicationTable + MakeCanonical + MakeConfluent + MakeConsequences + MakeConsequencesPres + MakeFormulaVector + MakeGAPDocDoc + MakeHomChain + MakeImagesInfoLinearGeneralMappingByImages + MakeImmutable + MakeKnuthBendixRewritingSystemConfluent + MakeLIBTOMLIST + MakeMagmaWithInversesByFiniteGenerators + MakeMapping + MakeMonomialOrdering + MakePreImagesInfoLinearGeneralMappingByImages + MakePreImagesInfoLinearMappingByMatrix + MakePreImagesInfoOperationAlgebraHomomorphism + MakeReadOnlyGVar + MakeReadOnlyGlobal + MakeReadWriteGVar + MakeReadWriteGlobal + MakeStabChainLong + ManageableQuotientOfAbelianPGroup + ManageableQuotientOfNilpotentGroup + MappedExpression + MappedExpressionForElementOfFreeAssociativeAlgebra + MappedPcElement + MappedVector + MappedWord + MappedWordSyllableAssocWord + MappingByFunction + MappingGeneratorsImages + MappingOfWhichItIsAsGGMBI + MappingPermListList + MarksTom + MatAlgebra + MatAutomorphismsFamily + MatCharsWreathSymmetric + MatClassMultCoeffsCharTable + MatLieAlgebra + MatOrbs + MatOrbsApprox + MatScalarProducts + MatSpace + MatTom + MathieuGroup + MathieuGroupCons + MatricesOfRelator + MatrixAlgebra + MatrixAutomorphisms + MatrixByBlockMatrix + MatrixDimension + MatrixLieAlgebra + MatrixNewBasePoint + MatrixOfAction + MatrixOperationOfCP + MatrixOperationOfCPGroup + MatrixSpace + MatrixSpinCharsSn + MaxHashViewSize + MaxNumeratorCoeffAlgElm + MaximalAbelianQuotient + MaximalBlocks + MaximalBlocksAttr + MaximalBlocksOp + MaximalNormalSubgroups + MaximalSubgroupClassReps + MaximalSubgroupClassesRepsLayer + MaximalSubgroups + MaximalSubgroupsLattice + MaximalSubgroupsSymmAlt + MaximalSubgroupsTom + Maximum + MaximumList + MeetEquivalenceRelations + MeetMagmaCongruences + MeetMaps + MeetPartitionStrat + MeetPartitionStratCell + MeetSemigroupCongruences + MembershipTestKnownBase + MinimalBlockDimension + MinimalElementCosetStabChain + MinimalGeneratingSet + MinimalGensLayer + MinimalNonmonomialGroup + MinimalNormalSubgroups + MinimalPolynomial + MinimalPolynomialMatrixNC + MinimalStabChain + MinimalSupergroupsLattice + MinimalSupergroupsTom + MinimizeExplicitTransversal + MinimizedBombieriNorm + Minimum + MinimumGroupOnSubgroupsOrbit + MinimumList + MinusCharacter + ModGauss + ModifyMinGens + ModifyPcgs + ModularCharacterDegree + ModuleByRestriction + ModuleOfExtension + ModuloPcgs + ModuloPcgsByPcSequence + ModuloPcgsByPcSequenceNC + ModuloTailPcgsByList + ModulusOfZmodnZObj + MoebiusMu + MoebiusTom + MolienSeries + MolienSeriesInfo + MolienSeriesWithGivenDenominator + Monoid + MonoidByGenerators + MonoidByMultiplicationTable + MonoidOfRewritingSystem + MonomialComparisonFunction + MonomialExtGrlexLess + MonomialExtrepComparisonFun + MonomialGrevlexOrdering + MonomialGrlexOrdering + MonomialLexOrdering + MonomialOrderingsFamily + MonomialTotalDegreeLess + MorClassLoop + MorClassOrbs + MorFindGeneratingSystem + MorFroWords + MorMaxFusClasses + MorRatClasses + Morphium + MorrisRecursion + MostFrequentGeneratorFpGroup + MovedPoints + MovedPointsPerms + MulExt + MultCoeffs + MultMatrixPadicNumbersByCoefficientsList + MultRowVector + MultiplicationTable + MultiplicativeElementsWithInversesFamilyByRws + MultiplicativeNeutralElement + MultiplicativeZero + MultiplicativeZeroOp + Multiply + MutableBasis + MutableBasisOfClosureUnderAction + MutableBasisOfIdealInNonassociativeAlgebra + MutableBasisOfNonassociativeAlgebra + MutableBasisOfProductSpace + MutableCopyMat + MutableIdentityMat + MutableNullMat + MutableTransposedMat + MutableTransposedMatDestructive + MyFingerprint + MyIntCoefficients + Naive + Name + NameFunction + NameIsomorphismClass + NameMonth + NameRNam + NameWeekDay + NamesFilter + NamesGVars + NamesLibTom + NamesLocalVariablesFunction + NamesOfComponents + NamesOfFusionSources + NamesSystemGVars + NamesUserGVars + NaturalActedSpace + NaturalCharacter + NaturalHomomorphismByFixedPointSubspace + NaturalHomomorphismByGenerators + NaturalHomomorphismByHomVW + NaturalHomomorphismByIdeal + NaturalHomomorphismByInvariantSubspace + NaturalHomomorphismByNilpotentClassTwoElement + NaturalHomomorphismByNormalSubgroup + NaturalHomomorphismByNormalSubgroupInParent + NaturalHomomorphismByNormalSubgroupNC + NaturalHomomorphismByNormalSubgroupNCInParent + NaturalHomomorphismByNormalSubgroupNCOp + NaturalHomomorphismByNormalSubgroupNCOrig + NaturalHomomorphismByNormalSubgroupOp + NaturalHomomorphismBySubAlgebraModule + NaturalHomomorphismBySubspace + NaturalHomomorphismBySubspaceOntoFullRowSpace + NaturalHomomorphismsPool + NaturalIsomorphismByPcgs + NearAdditiveGroup + NearAdditiveGroupByGenerators + NearAdditiveMagma + NearAdditiveMagmaByGenerators + NearAdditiveMagmaWithInverses + NearAdditiveMagmaWithInversesByGenerators + NearAdditiveMagmaWithZero + NearAdditiveMagmaWithZeroByGenerators + NearlyCharacterTablesFamily + NegativeRootVectors + NegativeRoots + NestingDepthA + NestingDepthM + NewAttribute + NewCategory + NewConstructor + NewDictionary + NewFamily + NewFilter + NewInfoClass + NewOperation + NewProperty + NewRepresentation + NewToBeDefinedObj + NewType + NewmanInfinityCriterion + NextIterator + NextLevelRegularGroups + NextPrimeInt + NextRBasePoint + NextStepCentralizer + NiceAlgebraMonomorphism + NiceBasis + NiceBasisFiltersInfo + NiceBasisNC + NiceFreeLeftModule + NiceFreeLeftModuleForFLMLOR + NiceFreeLeftModuleInfo + NiceMonomorphism + NiceMonomorphismAutomGroup + NiceNormalFormByExtRepFunction + NiceObject + NiceVector + NicomorphismOfFFEMatrixGroup + NicomorphismOfGeneralMatrixGroup + NilpotencyClassOfGroup + NilpotentClassTwoElement + NilpotentQuotientOfFpLieAlgebra + NinKernelCSPG + NonLieNilpotentElement + NonNilpotentElement + NonPerfectCSPG + NonSplitExtensions + NonTrivialRightHandSides + NonassocWord + NonnegIntScalarProducts + NonnegativeIntegers + NorSerPermPcgs + Norm + NormalBase + NormalClosure + NormalClosureByChain + NormalClosureInParent + NormalClosureOp + NormalFormIntMat + NormalIntersection + NormalIntersectionPcgs + NormalMaximalSubgroups + NormalSeriesByPcgs + NormalSubgroupClasses + NormalSubgroupClassesInfo + NormalSubgroups + NormalSubgroupsAbove + NormalSubgroupsCalc + NormalizeNameAndKey + NormalizeWhitespace + NormalizedArgList + NormalizedElementOfMagmaRingModuloRelations + NormalizedNameAndKey + NormalizedWhitespace + Normalizer + NormalizerInGLnZ + NormalizerInGLnZBravaisGroup + NormalizerInHomePcgs + NormalizerInParent + NormalizerOp + NormalizerParentSA + NormalizerStabCSPG + NormalizerTom + NormalizersTom + NormalizingReducedGL + NormedRowVector + NormedRowVectors + NormedVectors + NotifiedFusionsOfLibTom + NotifiedFusionsToLibTom + NrAffinePrimitiveGroups + NrArrangements + NrArrangementsMSetA + NrArrangementsMSetK + NrArrangementsSetA + NrArrangementsSetK + NrArrangementsX + NrBasisVectors + NrBitsInt + NrCombinations + NrCombinationsMSetA + NrCombinationsMSetK + NrCombinationsSetA + NrCombinationsSetK + NrCombinationsX + NrCompatiblePolynomials + NrConjugacyClasses + NrConjugacyClassesGL + NrConjugacyClassesGU + NrConjugacyClassesInSupergroup + NrConjugacyClassesPGL + NrConjugacyClassesPGU + NrConjugacyClassesPSL + NrConjugacyClassesPSU + NrConjugacyClassesSL + NrConjugacyClassesSLIsogeneous + NrConjugacyClassesSU + NrConjugacyClassesSUIsogeneous + NrDerangements + NrDerangementsK + NrInputsOfStraightLineProgram + NrIrreducibleSolvableGroups + NrMovedPoints + NrMovedPointsPerm + NrMovedPointsPerms + NrOrderedPartitions + NrPartitionTuples + NrPartitions + NrPartitionsSet + NrPerfectGroups + NrPerfectLibraryGroups + NrPermutationsList + NrPolyhedralSubgroups + NrPrimitiveGroups + NrRestrictedPartitions + NrRestrictedPartitionsK + NrSmallGroups + NrSolvableAffinePrimitiveGroups + NrSubsTom + NrSyllables + NrTransitiveGroups + NrTuples + NrUnorderedTuples + NthChainSubgroup + NthFundamentalOrbit + NthRoot + NthSchreierTransversalOfChainSubgroup + NthSiftOneLevel + Nucleus + NullAlgebra + NullMapMatrix + NullMat + NullspaceIntMat + NullspaceMat + NullspaceMatDestructive + NullspaceModQ + NumBol + Number + NumberArgumentsFunction + NumberCells + NumberCoset + NumberDigits + NumberFFVector + NumberField + NumberGeneratorsOfRws + NumberIrreducibleSolvableGroups + NumberOfCommutators + NumberOfNewGenerators + NumberOp + NumberPerfectGroups + NumberPerfectLibraryGroups + NumberSmallGroups + NumberSyllables + NumeratorOfModuloPcgs + NumeratorOfRationalFunction + NumeratorRat + OCAddBigMatrices + OCAddCentralizer + OCAddComplement + OCAddGenerators + OCAddGeneratorsGeneral + OCAddGeneratorsPcgs + OCAddMatrices + OCAddRelations + OCAddSumMatrices + OCAddToFunctions + OCConjugatingWord + OCCoprimeComplement + OCEquationMatrix + OCEquationVector + OCNormalRelations + OCOneCoboundaries + OCOneCocycles + OCSmallEquationMatrix + OCSmallEquationVector + OCTestRelations + OCTestRelators + ONanScottType + ObjByExponents + ObjByExtRep + ObjByVector + Objectify + ObjectifyWithAttributes + OccuringVariableIndices + OctaveAlgebra + OddSpinVals + OldGeneratorsOfPresentation + OldKernelHcommaC + OldSubspaceVectorSpaceGroup + Omega + OmegaAndLowerPCentralSeries + OmegaOp + OmegaSeries + OminusEven + OnBreak + OnBreakMessage + OnCharReadHookExcFds + OnCharReadHookExcFuncs + OnCharReadHookExcStreams + OnCharReadHookInFds + OnCharReadHookInFuncs + OnCharReadHookInStreams + OnCharReadHookOutFds + OnCharReadHookOutFuncs + OnCharReadHookOutStreams + OnCocycle + OnIndeterminates + OnLeftAntiOperation + OnLeftInverse + OnLines + OnPairs + OnPoints + OnQuit + OnRelVector + OnRight + OnSets + OnSetsDisjointSets + OnSetsSets + OnSetsTuples + OnSubspacesByCanonicalBasis + OnTuples + OnTuplesSets + OnTuplesTuples + One + OneAttr + OneCoboundaries + OneCocycles + OneFactorBound + OneGroup + OneImmutable + OneIrreducibleSolvableGroup + OneMutable + OneNormalizerfixedBlockSystem + OneOfPcgs + OneOp + OnePrimitiveGroup + OneSM + OneSameMutability + OneSmallGroup + OneTransitiveGroup + OperationAlgebraHomomorphism + OperatorOfExternalSet + OplusEven + OpmOdd + OpmSmall + OptionsStack + Orbit + OrbitByPosOp + OrbitChar + OrbitFusions + OrbitGenerators + OrbitGeneratorsInv + OrbitGeneratorsOfGroup + OrbitLength + OrbitLengthOp + OrbitLengths + OrbitLengthsDomain + OrbitOp + OrbitPerms + OrbitPowerMaps + OrbitRepresentativesCharacters + OrbitShortVectors + OrbitSplit + OrbitStabChain + OrbitStabilizer + OrbitStabilizerAlgorithm + OrbitStabilizerOp + OrbitStabilizingParentGroup + OrbitalPartition + OrbitishFO + OrbitishReq + Orbits + OrbitsByPosOp + OrbitsCharacters + OrbitsDomain + OrbitsPartition + OrbitsPerms + OrbitsishOperation + OrbitsishReq + Order + OrderKnownDividendList + OrderMatLimit + OrderMatTrial + OrderMod + OrderModK + OrderOfRewritingSystem + OrderOfSchurLift + OrderPerm + OrderedPartitions + OrderedPartitionsA + OrderedPartitionsK + OrderingByLessThanFunctionNC + OrderingByLessThanOrEqualFunctionNC + OrderingOfRewritingSystem + OrderingOnGenerators + OrderingsFamily + OrdersClassRepresentatives + OrdersTom + Ordinal + OrdinaryCharacterTable + OrthogonalComponents + OrthogonalEmbeddings + OrthogonalEmbeddingsSpecialDimension + OrthogonalSpaceInFullRowSpace + OrthogonalityDefectEuclideanLattice + OutdatePolycyclicCollector + OutputLogTo + OutputTextFile + OutputTextFileStillOpen + OutputTextFileType + OutputTextNone + OutputTextNoneType + OutputTextString + OutputTextStringType + OutputTextUser + OzeroEven + OzeroOdd + PBIsMinimal + PCentralLieAlgebra + PCentralNormalSeriesByPcgsPGroup + PCentralSeries + PCentralSeriesOp + PClassPGroup + PCore + PCoreOp + PCover + PERFRec + PGroupGeneratorsOfAbelianGroup + PMultiplicator + PQStatistics + PQuotient + PRIMGrp + PRump + PRumpOp + PSLDegree + PSLUnderlyingField + PSp + PackageInfo + PackageVariablesInfo + PadicCoefficients + PadicExpansionByRat + PadicExtensionNumberFamily + PadicNumber + Page + PageDisplay + Pager + Parametrized + Parent + ParentAttr + ParentPcgs + ParityPol + ParseArguments + ParseBibFiles + ParseError + ParseTreeXMLString + PartialClosureOfCongruence + PartialOrderByOrderingFunction + PartialOrderOfHasseDiagram + Partition + PartitionBacktrack + PartitionSortedPoints + PartitionStabilizerPermGroup + PartitionTuples + Partitions + PartitionsA + PartitionsGreatestEQ + PartitionsGreatestLE + PartitionsK + PartitionsRecursively + PartitionsSet + PartitionsSetA + PartitionsSetK + PartitionsTest + PcElementByExponents + PcElementByExponentsNC + PcGroupClassMatrixColumn + PcGroupCode + PcGroupCodeRec + PcGroupFpGroup + PcGroupFpGroupNC + PcGroupWithPcgs + PcSeries + Pcgs + PcgsByIndependentGeneratorsOfAbelianGroup + PcgsByPcSequence + PcgsByPcSequenceCons + PcgsByPcSequenceNC + PcgsCentralSeries + PcgsChiefSeries + PcgsElAbSerFromSpecPcgs + PcgsElementaryAbelianSeries + PcgsHomSoImPow + PcgsMemberPcSeriesPermGroup + PcgsPCentralSeriesPGroup + PcgsStabChainSeries + PcgsSystemLGSeries + PcgsSystemWithComplementSystem + PcgsSystemWithHallSystem + PcgsSystemWithWf + PerfGrpConst + PerfGrpLoad + PerfectCSPG + PerfectCentralProduct + PerfectGroup + PerfectIdentification + PerfectResiduum + PerfectSubdirectProduct + PerfectSubgroupsAlternatingGroup + Perform + PermBounds + PermCandidates + PermCandidatesFaithful + PermCharInfo + PermCharInfoRelative + PermChars + PermCharsTom + PermComb + PermLeftQuoTransformation + PermList + PermListList + PermMatrixGroup + PermNatAnTestDetect + PermNewBasePoint + PermOnEnumerator + Permanent + PermgpContainsAn + PermpcgsPcGroupPcgs + Permut + Permutation + PermutationCharacter + PermutationCycle + PermutationCycleOp + PermutationGModule + PermutationMat + PermutationOp + PermutationToSortCharacters + PermutationToSortClasses + PermutationTom + PermutationsFamily + PermutationsList + PermutationsListK + Permuted + Phi + PlainListCopy + PlainListCopyOp + PointInCellNo + PolycyclicFactorGroup + PolycyclicFactorGroupByRelators + PolycyclicFactorGroupByRelatorsNC + PolycyclicFactorGroupNC + PolynomialByExtRep + PolynomialByExtRepNC + PolynomialCoefficientsOfPolynomial + PolynomialDivisionAlgorithm + PolynomialModP + PolynomialReducedRemainder + PolynomialReduction + PolynomialRing + PopOptions + PosSublOdd + PosVecEnumFF + Position + PositionBound + PositionCanonical + PositionFirstComponent + PositionMatchingDelimiter + PositionNonZero + PositionNot + PositionNthOccurrence + PositionNthTrueBlist + PositionProperty + PositionSet + PositionSorted + PositionStream + PositionSublist + PositionWord + PositionsTrueBlist + PositiveExponentsPresentationFpHom + PositiveIntegers + PositiveRootVectors + PositiveRoots + PositiveRootsAsWeights + PossibleClassFusions + PossibleFusionsCharTableTom + PossiblePowerMaps + PostMakeImmutable + Pover + PowerDecompositions + PowerMap + PowerMapByComposition + PowerMapOfGroup + PowerMapOfGroupWithInvariants + PowerMapOp + PowerMapsAllowedBySymmetrisations + PowerMapsAllowedBySymmetrizations + PowerMod + PowerModCoeffs + PowerModEvalPol + PowerModInt + PowerPartition + PowerPcgsElement + PowerS + PowerSi + PowerSubalgebraSeries + PowerWreath + PowersumsElsyms + PreBasis + PreImage + PreImageElm + PreImageSetStabBlocksHomomorphism + PreImageWord + PreImages + PreImagesElm + PreImagesRange + PreImagesRepresentative + PreImagesRepresentativeOperationAlgebraHomomorphism + PreImagesSet + PreOrbishProcessing + PreferredGenerators + PrefrattiniSubgroup + PreimagesOfTransformation + Presentation + PresentationAugmentedCosetTable + PresentationFpGroup + PresentationNormalClosure + PresentationNormalClosureRrs + PresentationRegularPermutationGroup + PresentationRegularPermutationGroupNC + PresentationSubgroup + PresentationSubgroupMtc + PresentationSubgroupRrs + PresentationViaCosetTable + PresentationsFamily + PrevPrimeInt + PriGroItNext + PrimGrpLoad + PrimaryGeneratorWords + PrimeBlocks + PrimeBlocksOp + PrimeField + PrimeOfPGroup + PrimePGroup + PrimePowerComponent + PrimePowerComponents + PrimePowerPcSequence + PrimePowersInt + PrimeResidues + PrimeResiduesCache + Primes + PrimitiveElement + PrimitiveFacExtRepRatPol + PrimitiveGroup + PrimitiveGroupSims + PrimitiveGroupsIterator + PrimitiveIdentification + PrimitiveIndexIrreducibleSolvableGroup + PrimitivePolynomial + PrimitiveRoot + PrimitiveRootMod + Print + PrintAmbiguity + PrintArray + PrintBibAsBib + PrintBibAsHTML + PrintBibAsText + PrintCharacterTable + PrintCounters + PrintFactorsInt + PrintFormattedString + PrintFormattingStatus + PrintGAPDocElementTemplates + PrintHashWithNames + PrintObj + PrintPadicExpansion + PrintRecIndent + PrintSixFile + PrintTo + ProbabilityShapes + Process + ProcessFixpoint + ProdCoefRatfun + ProdCoeffLaurpol + ProdCoeffUnivfunc + Product + ProductCoeffs + ProductMod + ProductOp + ProductPP + ProductPol + ProductRootsPol + ProductSpace + ProductX + ProductXHelp + ProfileFunctions + ProfileFunctionsInGlobalVariables + ProfileGlobalFunctions + ProfileMethods + ProfileOperations + ProfileOperationsAndMethods + ProfileOperationsAndMethodsOff + ProfileOperationsAndMethodsOn + ProfileOperationsOff + ProfileOperationsOn + ProjectedInducedPcgs + ProjectedPcElement + Projection + ProjectionMap + ProjectionOntoFullRowSpace + ProjectionOntoVectorSubspace + ProjectiveActionHomomorphismMatrixGroup + ProjectiveActionOnFullSpace + ProjectiveCharDeg + ProjectiveGeneralLinearGroup + ProjectiveGeneralLinearGroupCons + ProjectiveGeneralUnitaryGroup + ProjectiveGeneralUnitaryGroupCons + ProjectiveOrder + ProjectiveSpecialLinearGroup + ProjectiveSpecialLinearGroupCons + ProjectiveSpecialUnitaryGroup + ProjectiveSpecialUnitaryGroupCons + ProjectiveSymplecticGroup + ProjectiveSymplecticGroupCons + PropertyMethodByNiceMonomorphism + PropertyMethodByNiceMonomorphismCollColl + PropertyMethodByNiceMonomorphismCollElm + PropertyMethodByNiceMonomorphismElmColl + PseudoRandom + PseudoRandomSeed + PthPowerImage + PthPowerImages + PullBack + PullbackCSPG + PullbackKernelCSPG + PurePadicNumberFamily + PushOptions + Quadratic + QuasiDihedralGenerators + QuaternionAlgebra + QuaternionGenerators + QuickInverseRepresentative + QuoInt + QuotRemLaurpols + QuotRemPolList + QuotSysDefinitionByIndex + QuotSysIndexByDefinition + Quotient + QuotientFromSCTable + QuotientGroup + QuotientGroupByChainHomomorphicImage + QuotientGroupByHomomorphism + QuotientGroupByImages + QuotientGroupByImagesNC + QuotientGroupHom + QuotientMod + QuotientPolynomialsExtRep + QuotientRemainder + QuotientSemigroupCongruence + QuotientSemigroupHomomorphism + QuotientSemigroupPreimage + QuotientSystem + RBaseGroupsBloxPermGroup + RClassOfHClass + RNamObj + RPFactorsModPrime + RPGcdCRT + RPGcdModPrime + RPGcdRepresentationModPrime + RPIFactors + RPIGcd + RPQuotientModPrime + RPSquareHensel + RRefine + RSSDefaultOptions + RadicalGroup + RadicalOfAlgebra + RanImgSrcSurjBloho + RanImgSrcSurjTraho + Random + RandomBinaryRelationOnPoints + RandomByPcs + RandomCommutatorSubproduct + RandomElmAsWord + RandomHashKey + RandomInvertibleMat + RandomIsomorphismTest + RandomList + RandomMat + RandomNormalSubproduct + RandomPcgsSylowSubgroup + RandomPol + RandomPrimitivePolynomial + RandomSchreierSims + RandomSpecialPcgsCoded + RandomSubprod + RandomTransformation + RandomUnimodularMat + Range + Rank + RankAction + RankFilter + RankMat + RankMatDestructive + RankOfTransformation + RankPGroup + RanksOfDescendingSeries + Rat + RatClasPElmArrangeClasses + RatPairString + RationalClass + RationalClasses + RationalClassesInEANS + RationalClassesPElements + RationalClassesPermGroup + RationalClassesSolvableGroup + RationalClassesTry + RationalFunctionByExtRep + RationalFunctionByExtRepWithCancellation + RationalFunctionsFamily + RationalIdentificationPermGroup + RationalizedMat + Rationals + ReObjectify + Read + ReadAll + ReadAllIoStreamByPty + ReadAllLine + ReadAndCheckFunc + ReadAsFunction + ReadByte + ReadGapRoot + ReadGrp + ReadLib + ReadLine + ReadOrComplete + ReadPackage + ReadPkg + ReadPrim + ReadSmall + ReadSmallLib + ReadTest + ReadTom + ReadTrans + RealClasses + RealizableBrauerCharacters + RecFields + RecNames + RecordsFamily + RecurseSchreierTree + RedispatchOnCondition + ReduceCoefficientsOfRws + ReduceCoeffs + ReduceCoeffsMod + ReduceLetterRepWordsRewSys + ReduceRules + ReduceStabChain + ReduceWordUsingRewritingSystem + Reduced + ReducedAdditiveInverse + ReducedByIsomorphisms + ReducedCharacters + ReducedClassFunctions + ReducedComm + ReducedConfluentRewritingSystem + ReducedConfluentRwsFromKbrwsNC + ReducedConjugate + ReducedDifference + ReducedForm + ReducedGaloisStabilizerInfo + ReducedGroebnerBasis + ReducedInverse + ReducedLeftQuotient + ReducedOne + ReducedOrdinary + ReducedPcElement + ReducedPower + ReducedProduct + ReducedQuotient + ReducedRrsWord + ReducedSCTable + ReducedScalarProduct + ReducedSum + ReducedVectorLTM + ReducedZero + Ree + ReeGroup + ReeGroupCons + ReesCongruenceOfSemigroupIdeal + ReesMatrixSemigroup + ReesMatrixSemigroupElement + ReesMatrixSemigroupEnumeratorGetElement + ReesZeroMatrixSemigroup + ReesZeroMatrixSemigroupElement + ReesZeroMatrixSemigroupElementIsZero + ReesZeroMatrixSemigroupEnumeratorGetElement + RefinedChain + RefinedPcGroup + RefinedSymmetrisations + RefinedSymmetrizations + Refinements + ReflectionMat + ReflexiveClosureBinaryRelation + RegisterRBasePoint + RegularActionHomomorphism + RegularModule + RegularModuleByGens + RegularNinKernelCSPG + RelVectorToCocycle + RelationsOfFpMonoid + RelationsOfFpSemigroup + RelativeBasis + RelativeBasisNC + RelativeOrderOfPcElement + RelativeOrders + RelatorFixedMultiplier + RelatorMatrixAbelianizedNormalClosure + RelatorMatrixAbelianizedNormalClosureRrs + RelatorMatrixAbelianizedSubgroup + RelatorMatrixAbelianizedSubgroupMtc + RelatorMatrixAbelianizedSubgroupRrs + RelatorRepresentatives + RelatorsCode + RelatorsOfFpAlgebra + RelatorsOfFpGroup + RelatorsPermGroupHom + RelsSortedByStartGen + RelsViaCosetTable + RemInt + RemoveCharacters + RemoveElmList + RemoveFile + RemoveOuterCoeffs + RemoveRelator + RemoveRootParseTree + RemoveSet + RemoveStabChain + RenumberHighestWeightGenerators + RenumberTree + RenumberedWord + RepOpElmTuplesPermGroup + RepOpSetsPermGroup + RepeatedString + ReplacedString + RepresentationsOfMatrix + RepresentationsOfObject + Representative + RepresentativeAction + RepresentativeActionOp + RepresentativeFromGenerators + RepresentativeLinearOperation + RepresentativeSmallest + RepresentativeTom + RepresentativeTomByGenerators + RepresentativeTomByGeneratorsNC + RepresentativesContainedRightCosets + RepresentativesFusions + RepresentativesMinimalBlocks + RepresentativesMinimalBlocksAttr + RepresentativesMinimalBlocksOp + RepresentativesPerfectSubgroups + RepresentativesPowerMaps + RepresentativesSimpleSubgroups + RepsPerfSimpSub + RequirePackage + Reread + RereadAndCheckFunc + RereadGrp + RereadLib + RereadPackage + RereadPkg + RereadPrim + RereadSmall + RereadTrans + ResetFilterObj + ResetOptionsStack + ResizeFlatHashTable + ResizeListHashTable + RespectsAddition + RespectsAdditiveInverses + RespectsInverses + RespectsMultiplication + RespectsOne + RespectsScalarMultiplication + RespectsZero + RestoreStateRandom + Restricted + RestrictedClassFunction + RestrictedClassFunctions + RestrictedExternalSet + RestrictedMapping + RestrictedNiceMonomorphism + RestrictedPartitions + RestrictedPartitionsA + RestrictedPartitionsK + RestrictedPerm + RestrictedTransformation + ResultOfLineOfStraightLineProgram + ResultOfStraightLineProgram + Resultant + ReturnFail + ReturnFalse + ReturnNextBasePoint + ReturnPopOptions + ReturnTrue + Reversed + ReversedOp + Revision + RewindStream + RewriteAbelianizedSubgroupRelators + RewriteSubgroupRelators + RewriteWord + RightActingAlgebra + RightActingDomain + RightActingGroup + RightActingRingOfIdeal + RightAction + RightAlgebraModule + RightAlgebraModuleByGenerators + RightCoset + RightCosetCanonicalRepresentativeDeterminator + RightCosets + RightCosetsNC + RightDerivations + RightIdeal + RightIdealByGenerators + RightIdealNC + RightMagmaCongruence + RightMagmaCongruenceByGeneratingPairs + RightMagmaIdeal + RightMagmaIdealByGenerators + RightModuleByHomomorphismToMatAlg + RightSemigroupCongruenceByGeneratingPairs + RightSemigroupIdealEnumeratorDataGetElement + RightShiftRowVector + RightTransversal + RightTransversalInParent + RightTransversalOp + RightTransversalPermGroupConstructor + Ring + RingByGenerators + RingElmTimesElm + RingFromFFE + RingWithOne + RingWithOneByGenerators + Root + RootBound + RootInt + RootMod + RootModPrime + RootModPrimePower + RootOfDefiningPolynomial + RootSystem + RootsMod + RootsModPrime + RootsModPrimePower + RootsOfUPol + RootsRepresentativeFFPol + RootsUnityMod + RootsUnityModPrime + RootsUnityModPrimePower + RoundCyc + RoundCycDown + RowEchelonFormLTM + RowIndexOfReesMatrixSemigroupElement + RowIndexOfReesZeroMatrixSemigroupElement + RowSpace + RowsOfReesMatrixSemigroup + RowsOfReesZeroMatrixSemigroup + Rules + RunImmediateMethods + Runtime + Runtimes + SCMinSmaGens + SCRExtend + SCRExtendRecord + SCRMakeStabStrong + SCRNotice + SCRRandomPerm + SCRRandomString + SCRRandomSubproduct + SCRRestoredRecord + SCRSchTree + SCRSift + SCRStrongGenTest + SCTableEntry + SCTableProduct + SLDegree + SLUnderlyingField + SNFofREF + SPolynomial + SSortedList + SSortedListList + STDOut + STGSelFunc + SameBlock + SandwichMatrixOfReesMatrixSemigroup + SandwichMatrixOfReesZeroMatrixSemigroup + SaveWorkspace + ScalarProduct + SchreierTransversal + SchreierTreeDepth + SchreierTreeInternalConsistencyCheck + SchuMu + SchurCover + SchurCoverFP + ScriptFromString + Search + SecHMSM + SecondaryGeneratorWordsAugmentedCosetTable + SecondaryImagesAugmentedCosetTable + SecondsDMYhms + SeekPositionStream + SelectSmallGroups + SelectTransitiveGroups + SemiEchelonBasis + SemiEchelonBasisNC + SemiEchelonMat + SemiEchelonMatDestructive + SemiEchelonMatTransformation + SemiEchelonMatTransformationDestructive + SemiEchelonMats + SemiEchelonMatsDestructive + SemiEchelonMatsNoCo + SemiSimpleType + SemidirectFactorsOfGroup + SemidirectProduct + SemidirectProductInfo + Semigroup + SemigroupByGenerators + SemigroupByMultiplicationTable + SemigroupCongruenceByGeneratingPairs + SemigroupIdealByGenerators + SemigroupIdealEnumeratorDataGetElement + SemigroupOfRewritingSystem + SemigroupTCInitialTableSize + SemigroupToddCoxeterInfo + Semiring + SemiringByGenerators + SemiringWithOne + SemiringWithOneAndZero + SemiringWithOneAndZeroByGenerators + SemiringWithOneByGenerators + SemiringWithZero + SemiringWithZeroByGenerators + SeqsOrbits + Set + SetANonReesCongruenceOfSemigroup + SetAbelianInvariants + SetAbelianInvariantsOfList + SetAbsoluteValue + SetActingDomain + SetActionHomomorphismAttr + SetActionKernelExternalSet + SetActorOfExternalSet + SetActualLibFileName + SetAdditiveElementAsMultiplicativeElement + SetAdditiveElementsAsMultiplicativeElementsFamily + SetAdditiveInverse + SetAdditiveInverseAttr + SetAdditiveInverseImmutable + SetAdditiveNeutralElement + SetAdditivelyActingDomain + SetAdjointBasis + SetAdjointModule + SetAlgebraicElementsFamilies + SetAllBlocks + SetAllInfoLevels + SetAlpha + SetAlternatingDegree + SetAlternatingSubgroup + SetAsDuplicateFreeList + SetAsGroup + SetAsGroupGeneralMappingByImages + SetAsLeftModuleGeneralMappingByImages + SetAsList + SetAsMagma + SetAsMonoid + SetAsNearRing + SetAsPolynomial + SetAsRing + SetAsSSortedList + SetAsSemigroup + SetAsSemiring + SetAsSemiringWithOne + SetAsSemiringWithOneAndZero + SetAsSemiringWithZero + SetAsSortedList + SetAsSubgroupOfWholeGroupByQuotient + SetAssertionLevel + SetAssociatedReesMatrixSemigroupOfDClass + SetAssociatedSemigroup + SetAugmentationIdeal + SetAugmentedCosetTableMtcInWholeGroup + SetAugmentedCosetTableNormalClosureInWholeGroup + SetAugmentedCosetTableRrsInWholeGroup + SetAutomorphismDomain + SetAutomorphismGroup + SetAutomorphismsOfTable + SetBaseIntMat + SetBaseMat + SetBaseOfBasicImageGroup + SetBaseOfGroup + SetBaseOrthogonalSpaceMat + SetBasis + SetBasisOfHomCosetAddMatrixGroup + SetBasisVectors + SetBaumClausenInfo + SetBilinearFormMat + SetBlocksAttr + SetBlocksInfo + SetBrauerCharacterValue + SetBravaisGroup + SetBravaisSubgroups + SetBravaisSupergroups + SetCanEasilyCompareElements + SetCanEasilySortElements + SetCanFindNilpotentClassTwoElement + SetCanonicalBasis + SetCanonicalElt + SetCanonicalGenerators + SetCanonicalNiceMonomorphism + SetCanonicalPcgs + SetCanonicalPcgsWrtFamilyPcgs + SetCanonicalPcgsWrtHomePcgs + SetCanonicalPcgsWrtSpecialPcgs + SetCanonicalRepresentativeDeterminatorOfExternalSet + SetCanonicalRepresentativeOfExternalOrbitByPcgs + SetCanonicalRepresentativeOfExternalSet + SetCartanMatrix + SetCartanSubalgebra + SetCayleyGraphDualSemigroup + SetCayleyGraphSemigroup + SetCenter + SetCentralCharacter + SetCentralIdempotentsOfSemiring + SetCentralNormalSeriesByPcgs + SetCentralizerInGLnZ + SetCentralizerInParent + SetCentre + SetCentreOfCharacter + SetChainSubgroup + SetChainSubgroupQuotient + SetCharacterDegrees + SetCharacterNames + SetCharacterParameters + SetCharacteristic + SetCharacteristicPolynomial + SetChevalleyBasis + SetChiefNormalSeriesByPcgs + SetChiefSeries + SetClassNames + SetClassNamesTom + SetClassParameters + SetClassPermutation + SetClassPositionsOfCentre + SetClassPositionsOfDerivedSubgroup + SetClassPositionsOfDirectProductDecompositions + SetClassPositionsOfElementaryAbelianSeries + SetClassPositionsOfFittingSubgroup + SetClassPositionsOfKernel + SetClassPositionsOfLowerCentralSeries + SetClassPositionsOfMaximalNormalSubgroups + SetClassPositionsOfNormalSubgroups + SetClassPositionsOfSolvableResiduum + SetClassPositionsOfSupersolvableResiduum + SetClassPositionsOfUpperCentralSeries + SetClassRoots + SetClassTypesTom + SetCoKernelOfAdditiveGeneralMapping + SetCoKernelOfMultiplicativeGeneralMapping + SetCoefficientsAndMagmaElements + SetCoefficientsFamily + SetCoefficientsOfLaurentPolynomial + SetCoefficientsOfUnivariatePolynomial + SetCoefficientsOfUnivariateRationalFunction + SetCoefficientsRing + SetCollectionsFamily + SetColumnIndexOfReesMatrixSemigroupElement + SetColumnIndexOfReesZeroMatrixSemigroupElement + SetColumnsOfReesMatrixSemigroup + SetColumnsOfReesZeroMatrixSemigroup + SetCommutator + SetCommutatorANC + SetCommutatorFactorGroup + SetCommutatorLength + SetCommutatorNC + SetComplementSystem + SetComplexConjugate + SetComponentsOfTuplesFamily + SetCompositionSeries + SetComputedAgemos + SetComputedAscendingChains + SetComputedBrauerTables + SetComputedClassFusions + SetComputedCyclicExtensionsTom + SetComputedHallSubgroups + SetComputedIndicators + SetComputedIsPNilpotents + SetComputedIsPSolvableCharacterTables + SetComputedIsPSolvables + SetComputedOmegas + SetComputedPCentralSeriess + SetComputedPCores + SetComputedPRumps + SetComputedPowerMaps + SetComputedPrimeBlockss + SetComputedSylowComplements + SetComputedSylowSubgroups + SetConductor + SetConfluentRws + SetConjugacyClasses + SetConjugacyClassesMaximalSubgroups + SetConjugacyClassesPerfectSubgroups + SetConjugacyClassesSubgroups + SetConjugate + SetConjugateANC + SetConjugateNC + SetConjugates + SetConjugatorInnerAutomorphism + SetConjugatorOfConjugatorIsomorphism + SetConstantTimeAccessList + SetConstituentsOfCharacter + SetConvertBasicImageGroupElement + SetCoreInParent + SetCosetTableFpHom + SetCosetTableInWholeGroup + SetCosetTableNormalClosureInWholeGroup + SetCosetTableOfFpSemigroup + SetCrystGroupDefaultAction + SetCycleStructurePerm + SetCyclicExtensionsTom + SetDataType + SetDecompositionMatrix + SetDecompositionTypesOfGroup + SetDefaultFieldOfMatrix + SetDefaultFieldOfMatrixGroup + SetDefectApproximation + SetDefiningPcgs + SetDefiningPolynomial + SetDefinitionNC + SetDegreeAction + SetDegreeOfBinaryRelation + SetDegreeOfCharacter + SetDegreeOfLaurentPolynomial + SetDegreeOfMatrixGroup + SetDegreeOfTransformation + SetDegreeOfTransformationSemigroup + SetDegreeOperation + SetDegreeOverPrimeField + SetDelta + SetDenominatorOfModuloPcgs + SetDenominatorOfRationalFunction + SetDepthOfUpperTriangularMatrix + SetDerivations + SetDerivative + SetDerivedLength + SetDerivedSeriesOfGroup + SetDerivedSubgroup + SetDerivedSubgroupsTomPossible + SetDerivedSubgroupsTomUnique + SetDeterminantMat + SetDeterminantOfCharacter + SetDihedralGenerators + SetDimension + SetDimensionOfMatrixGroup + SetDimensionOfVectors + SetDimensionsLoewyFactors + SetDimensionsMat + SetDirectFactorsOfGroup + SetDirectProductInfo + SetDirectSumDecomposition + SetDisplayOptions + SetDixonRecord + SetEANormalSeriesByPcgs + SetEarns + SetEggBoxOfDClass + SetElementTestFunction + SetElementaryAbelianSeries + SetElementaryAbelianSeriesLargeSteps + SetElementaryAbelianSubseries + SetElementsFamily + SetElmWPObj + SetEmptyRowVector + SetEntrySCTable + SetEnumerator + SetEnumeratorByBasis + SetEnumeratorSorted + SetEquivalenceClassRelation + SetEquivalenceClasses + SetEquivalenceRelationPartition + SetErrorHandler + SetExponent + SetExponentOfPowering + SetExtRepDenominatorRatFun + SetExtRepNumeratorRatFun + SetExtRepPolynomialRatFun + SetExternalOrbits + SetExternalOrbitsStabilizers + SetExternalSet + SetFactorsOfDirectProduct + SetFaithfulModule + SetFamiliesOfGeneralMappingsAndRanges + SetFamilyForOrdering + SetFamilyForRewritingSystem + SetFamilyPcgs + SetFamilyRange + SetFamilySource + SetFeatureObj + SetFieldOfMatrixGroup + SetFilterObj + SetFittingSubgroup + SetFpElmComparisonMethod + SetFpElmEqualityMethod + SetFpElmKBRWS + SetFrattiniSubgroup + SetFrattinifactorId + SetFrattinifactorSize + SetFreeAlgebraOfFpAlgebra + SetFreeGeneratorsOfFpAlgebra + SetFreeGeneratorsOfFpGroup + SetFreeGeneratorsOfFpMonoid + SetFreeGeneratorsOfFpSemigroup + SetFreeGroupOfBasicImageGroup + SetFreeGroupOfFpGroup + SetFreeMonoidOfFpMonoid + SetFreeMonoidOfRewritingSystem + SetFreeSemigroupOfFpSemigroup + SetFreeSemigroupOfRewritingSystem + SetFrobeniusAutomorphism + SetFunctionAction + SetFusionConjugacyClassesOp + SetFusionsOfLibTom + SetFusionsToLibTom + SetFusionsTom + SetGLDegree + SetGLUnderlyingField + SetGaloisGroup + SetGaloisMat + SetGaloisStabilizer + SetGaloisType + SetGapDocHTMLOptions + SetGapDocLaTeXOptions + SetGapDocTxtOptions + SetGasmanMessageStatus + SetGeneralizedPcgs + SetGeneratingPairsOfLeftMagmaCongruence + SetGeneratingPairsOfMagmaCongruence + SetGeneratingPairsOfRightMagmaCongruence + SetGeneratingSetIsComplete + SetGeneratorOfCyclicGroup + SetGeneratorsOfAdditiveGroup + SetGeneratorsOfAdditiveMagma + SetGeneratorsOfAdditiveMagmaWithInverses + SetGeneratorsOfAdditiveMagmaWithZero + SetGeneratorsOfAlgebra + SetGeneratorsOfAlgebraModule + SetGeneratorsOfAlgebraWithOne + SetGeneratorsOfDivisionRing + SetGeneratorsOfDomain + SetGeneratorsOfEquivalenceRelationPartition + SetGeneratorsOfExtASet + SetGeneratorsOfExtLSet + SetGeneratorsOfExtRSet + SetGeneratorsOfExtUSet + SetGeneratorsOfFLMLOR + SetGeneratorsOfFLMLORWithOne + SetGeneratorsOfField + SetGeneratorsOfGroup + SetGeneratorsOfIdeal + SetGeneratorsOfLeftIdeal + SetGeneratorsOfLeftMagmaIdeal + SetGeneratorsOfLeftModule + SetGeneratorsOfLeftOperatorAdditiveGroup + SetGeneratorsOfLeftOperatorRing + SetGeneratorsOfLeftOperatorRingWithOne + SetGeneratorsOfLeftVectorSpace + SetGeneratorsOfMagma + SetGeneratorsOfMagmaIdeal + SetGeneratorsOfMagmaWithInverses + SetGeneratorsOfMagmaWithOne + SetGeneratorsOfMonoid + SetGeneratorsOfNearAdditiveGroup + SetGeneratorsOfNearAdditiveMagma + SetGeneratorsOfNearAdditiveMagmaWithInverses + SetGeneratorsOfNearAdditiveMagmaWithZero + SetGeneratorsOfRightIdeal + SetGeneratorsOfRightMagmaIdeal + SetGeneratorsOfRightModule + SetGeneratorsOfRightOperatorAdditiveGroup + SetGeneratorsOfRing + SetGeneratorsOfRingWithOne + SetGeneratorsOfRws + SetGeneratorsOfSemigroup + SetGeneratorsOfSemiring + SetGeneratorsOfSemiringWithOne + SetGeneratorsOfSemiringWithOneAndZero + SetGeneratorsOfSemiringWithZero + SetGeneratorsOfTwoSidedIdeal + SetGeneratorsOfVectorSpace + SetGeneratorsSmallest + SetGeneratorsSubgroupsTom + SetGlobalPartitionOfClasses + SetGrading + SetGreensDRelation + SetGreensHRelation + SetGreensJRelation + SetGreensLRelation + SetGreensRRelation + SetGroupByPcgs + SetGroupHClassOfGreensDClass + SetGroupOfPcgs + SetHallSystem + SetHashEntry + SetHashEntryAtLastIndex + SetHelpViewer + SetHirschLength + SetHomCosetFamily + SetHomFromFreeOfBasicImageGroup + SetHomeEnumerator + SetHomePcgs + SetIBr + SetIdGroup + SetIdempotents + SetIdempotentsTom + SetIdempotentsTomInfo + SetIdentificationOfConjugacyClasses + SetIdentifier + SetIdentity + SetIdentityMapping + SetImage + SetImageElt + SetImageListOfTransformation + SetImageSetOfTransformation + SetImagesSmallestGenerators + SetImagesSource + SetImfRecord + SetIndependentGeneratorsOfAbelianGroup + SetIndependentGeneratorsOfAbelianMatrixGroup + SetIndeterminateName + SetIndeterminateNumberOfLaurentPolynomial + SetIndeterminateNumberOfUnivariateLaurentPolynomial + SetIndeterminateNumberOfUnivariateRationalFunction + SetIndeterminateOfUnivariateRationalFunction + SetIndeterminatesOfPolynomialRing + SetIndexInParent + SetIndexInWholeGroup + SetIndicesCentralNormalSteps + SetIndicesChiefNormalSteps + SetIndicesEANormalSteps + SetIndicesInvolutaryGenerators + SetIndicesNormalSteps + SetIndicesOfAdjointBasis + SetIndicesPCentralNormalStepsPGroup + SetInducedPcgs + SetInducedPcgsWrtFamilyPcgs + SetInducedPcgsWrtHomePcgs + SetInducedPcgsWrtSpecialPcgs + SetInfoLevel + SetInfoText + SetInjectionZeroMagma + SetInnerAutomorphismsAutomorphismGroup + SetInt + SetInternalRepresentative + SetInvariantBilinearForm + SetInvariantForm + SetInvariantLattice + SetInvariantQuadraticForm + SetInvariantSesquilinearForm + SetInverse + SetInverseAttr + SetInverseClasses + SetInverseGeneralMapping + SetInverseImmutable + SetIrr + SetIrrBaumClausen + SetIrrConlon + SetIrrDixonSchneider + SetIrrFacsPol + SetIrreducibleRepresentations + SetIsAbelian + SetIsAbelianNumberField + SetIsAbelianTom + SetIsAdditiveGroupGeneralMapping + SetIsAdditiveGroupHomomorphism + SetIsAdditiveGroupToGroupGeneralMapping + SetIsAdditiveGroupToGroupHomomorphism + SetIsAdditivelyCommutative + SetIsAlgebraGeneralMapping + SetIsAlgebraHomomorphism + SetIsAlgebraModule + SetIsAlgebraWithOneGeneralMapping + SetIsAlgebraWithOneHomomorphism + SetIsAlternatingGroup + SetIsAnticommutative + SetIsAntisymmetricBinaryRelation + SetIsAssociative + SetIsAutomorphismGroup + SetIsBasicWreathProductOrdering + SetIsBergerCondition + SetIsBijective + SetIsBravaisGroup + SetIsBuiltFromAdditiveMagmaWithInverses + SetIsBuiltFromGroup + SetIsBuiltFromMagma + SetIsBuiltFromMagmaWithInverses + SetIsBuiltFromMagmaWithOne + SetIsBuiltFromMonoid + SetIsBuiltFromSemigroup + SetIsCanonicalBasis + SetIsCanonicalBasisFullMatrixModule + SetIsCanonicalBasisFullRowModule + SetIsCanonicalBasisFullSCAlgebra + SetIsCanonicalNiceMonomorphism + SetIsCanonicalPcgs + SetIsCanonicalPcgsWrtSpecialPcgs + SetIsChainTypeGroup + SetIsCharacter + SetIsCharacteristicMatrixPGroup + SetIsCommutative + SetIsCommutativeFamily + SetIsConfluent + SetIsConjugatorAutomorphism + SetIsConjugatorIsomorphism + SetIsConstantRationalFunction + SetIsConstantTimeAccessGeneralMapping + SetIsCycInt + SetIsCyclic + SetIsCyclicTom + SetIsCyclicWithSize + SetIsCyclotomicField + SetIsDihedralGroup + SetIsDistributive + SetIsDivisionRing + SetIsDuplicateFree + SetIsDuplicateFreeList + SetIsElementaryAbelian + SetIsEmpty + SetIsEndoGeneralMapping + SetIsEndoMapping + SetIsEquivalenceRelation + SetIsFFEMatrixGroupOverLargeSpace + SetIsFamilyPcgs + SetIsField + SetIsFieldHomomorphism + SetIsFinite + SetIsFiniteDimensional + SetIsFiniteOrdersPcgs + SetIsFiniteSemigroupGreensRelation + SetIsFinitelyGeneratedGroup + SetIsFrattiniFree + SetIsFreeMonoid + SetIsFreeSemigroup + SetIsFullFpAlgebra + SetIsFullHomModule + SetIsFullMatrixModule + SetIsFullRowModule + SetIsFullSCAlgebra + SetIsFullSubgroupGLorSLRespectingBilinearForm + SetIsFullSubgroupGLorSLRespectingQuadraticForm + SetIsFullSubgroupGLorSLRespectingSesquilinearForm + SetIsFullTransformationSemigroup + SetIsGL + SetIsGeneralLinearGroup + SetIsGeneralizedCartanMatrix + SetIsGeneratorsOfMagmaWithInverses + SetIsGreensClass + SetIsGreensDClass + SetIsGreensDRelation + SetIsGreensHClass + SetIsGreensHRelation + SetIsGreensJClass + SetIsGreensJRelation + SetIsGreensLClass + SetIsGreensLRelation + SetIsGreensRClass + SetIsGreensRRelation + SetIsGreensRelation + SetIsGroupGeneralMapping + SetIsGroupHClass + SetIsGroupHomomorphism + SetIsGroupOfAutomorphisms + SetIsGroupOfAutomorphismsFiniteGroup + SetIsGroupRing + SetIsGroupToAdditiveGroupGeneralMapping + SetIsGroupToAdditiveGroupHomomorphism + SetIsHandledByNiceMonomorphism + SetIsHasseDiagram + SetIsHomCosetOfAdditiveElt + SetIsHomCosetOfFp + SetIsHomCosetOfMatrix + SetIsHomCosetOfPerm + SetIsHomCosetOfTuple + SetIsIdealInParent + SetIsIdempotent + SetIsImpossible + SetIsInducedFromNormalSubgroup + SetIsInducedPcgsWrtSpecialPcgs + SetIsInjective + SetIsInnerAutomorphism + SetIsIntegerMatrixGroup + SetIsIntegralBasis + SetIsIntegralCyclotomic + SetIsIntegralRing + SetIsIrreducibleCharacter + SetIsJacobianRing + SetIsLDistributive + SetIsLatticeOrderBinaryRelation + SetIsLaurentPolynomial + SetIsLeftActedOnByDivisionRing + SetIsLeftAlgebraModule + SetIsLeftIdealInParent + SetIsLeftModuleGeneralMapping + SetIsLeftModuleHomomorphism + SetIsLeftSemigroupCongruence + SetIsLeftSemigroupIdeal + SetIsLieAbelian + SetIsLieAlgebra + SetIsLieNilpotent + SetIsLieSolvable + SetIsLinearlyPrimitive + SetIsMagmaHomomorphism + SetIsMapping + SetIsMatrixModule + SetIsMinimalNonmonomial + SetIsMonoid + SetIsMonomialCharacter + SetIsMonomialCharacterTable + SetIsMonomialGroup + SetIsMonomialMatrix + SetIsMonomialNumber + SetIsNaturalAlternatingGroup + SetIsNaturalGL + SetIsNaturalSL + SetIsNaturalSymmetricGroup + SetIsNearRing + SetIsNearRingWithOne + SetIsNilpQuotientSystem + SetIsNilpotentCharacterTable + SetIsNilpotentGroup + SetIsNilpotentTom + SetIsNonTrivial + SetIsNoncharacteristicMatrixPGroup + SetIsNormalBasis + SetIsNormalForm + SetIsNormalInParent + SetIsNumberField + SetIsNumeratorParentPcgsFamilyPcgs + SetIsOne + SetIsOrderingOnFamilyOfAssocWords + SetIsPGroup + SetIsPQuotientSystem + SetIsPSL + SetIsParentPcgsFamilyPcgs + SetIsPartialOrderBinaryRelation + SetIsPcgsCentralSeries + SetIsPcgsChiefSeries + SetIsPcgsElementaryAbelianSeries + SetIsPcgsPCentralSeriesPGroup + SetIsPerfectCharacterTable + SetIsPerfectGroup + SetIsPerfectTom + SetIsPolycyclicGroup + SetIsPolynomial + SetIsPositionsList + SetIsPreOrderBinaryRelation + SetIsPrimeField + SetIsPrimeOrdersPcgs + SetIsPrimitive + SetIsPrimitiveAffine + SetIsPrimitiveCharacter + SetIsPrimitiveMatrixGroup + SetIsPseudoCanonicalBasisFullHomModule + SetIsQuasiDihedralGroup + SetIsQuasiPrimitive + SetIsQuaternionGroup + SetIsRDistributive + SetIsRationalMatrixGroup + SetIsRectangularTable + SetIsReduced + SetIsReesCongruence + SetIsReesCongruenceSemigroup + SetIsReesMatrixSemigroup + SetIsReesZeroMatrixSemigroup + SetIsReflexiveBinaryRelation + SetIsRegular + SetIsRegularDClass + SetIsRegularSemigroup + SetIsRelativelySM + SetIsRestrictedLieAlgebra + SetIsRightAlgebraModule + SetIsRightIdealInParent + SetIsRightSemigroupCongruence + SetIsRightSemigroupIdeal + SetIsRing + SetIsRingGeneralMapping + SetIsRingHomomorphism + SetIsRingWithOne + SetIsRingWithOneGeneralMapping + SetIsRingWithOneHomomorphism + SetIsRowModule + SetIsSL + SetIsSSortedList + SetIsSemiEchelonized + SetIsSemiRegular + SetIsSemigroup + SetIsSemigroupCongruence + SetIsSemigroupIdeal + SetIsSemiring + SetIsSemiringWithOne + SetIsSemiringWithOneAndZero + SetIsSemiringWithZero + SetIsShortLexOrdering + SetIsSimpleAlgebra + SetIsSimpleCharacterTable + SetIsSimpleGroup + SetIsSimpleSemigroup + SetIsSingleValued + SetIsSkewFieldFamily + SetIsSmallList + SetIsSolvableCharacterTable + SetIsSolvableGroup + SetIsSolvableTom + SetIsSortedList + SetIsSpecialLinearGroup + SetIsSpecialPcgs + SetIsSporadicSimpleCharacterTable + SetIsSporadicSimpleGroup + SetIsStabChainViaChainSubgroup + SetIsSubgroupSL + SetIsSubmonoidFpMonoid + SetIsSubnormallyMonomial + SetIsSubsemigroupFpSemigroup + SetIsSubsemigroupReesMatrixSemigroup + SetIsSubsemigroupReesZeroMatrixSemigroup + SetIsSubsetLocallyFiniteGroup + SetIsSupersolvableCharacterTable + SetIsSupersolvableGroup + SetIsSurjective + SetIsSymmetricBinaryRelation + SetIsSymmetricGroup + SetIsTotal + SetIsTotalOrdering + SetIsTransformationMonoid + SetIsTransformationSemigroup + SetIsTransitive + SetIsTransitiveBinaryRelation + SetIsTranslationInvariantOrdering + SetIsTrivial + SetIsTwoSidedIdealInParent + SetIsUFDFamily + SetIsUniformMatrixGroup + SetIsUnivariatePolynomial + SetIsUnivariateRationalFunction + SetIsVectorSpaceHomomorphism + SetIsVirtualCharacter + SetIsWeightLexOrdering + SetIsWellFoundedOrdering + SetIsWeylGroup + SetIsWholeFamily + SetIsWreathProductOrdering + SetIsZero + SetIsZeroGroup + SetIsZeroMultiplicationRing + SetIsZeroRationalFunction + SetIsZeroSimpleSemigroup + SetIsZeroSquaredRing + SetIsomorphismFpAlgebra + SetIsomorphismFpFLMLOR + SetIsomorphismFpGroup + SetIsomorphismFpMonoid + SetIsomorphismFpSemigroup + SetIsomorphismMatrixAlgebra + SetIsomorphismMatrixFLMLOR + SetIsomorphismPcGroup + SetIsomorphismPermGroup + SetIsomorphismReesMatrixSemigroup + SetIsomorphismRefinedPcGroup + SetIsomorphismSCAlgebra + SetIsomorphismSCFLMLOR + SetIsomorphismSimplifiedFpGroup + SetIsomorphismSpecialPcGroup + SetIsomorphismTransformationSemigroup + SetJenningsLieAlgebra + SetJenningsSeries + SetJordanDecomposition + SetKernelOfAdditiveGeneralMapping + SetKernelOfCharacter + SetKernelOfMultiplicativeGeneralMapping + SetKernelOfTransformation + SetKillingMatrix + SetKnowsHowToDecompose + SetLClassOfHClass + SetLGFirst + SetLGHeads + SetLGLayers + SetLGLength + SetLGTails + SetLGWeights + SetLargestElementGroup + SetLargestMovedPoint + SetLargestMovedPointPerm + SetLatticeGeneratorsInUEA + SetLatticeSubgroups + SetLeadCoeffsIGS + SetLeftActingAlgebra + SetLeftActingDomain + SetLeftActingGroup + SetLeftActingRingOfIdeal + SetLeftDerivations + SetLength + SetLengthsTom + SetLessThanFunction + SetLessThanOrEqualFunction + SetLetterRepWordsLessFunc + SetLevelsOfGenerators + SetLeviMalcevDecomposition + SetLieAlgebraByDomain + SetLieCenter + SetLieCentralizerInParent + SetLieCentre + SetLieDerivedSeries + SetLieDerivedSubalgebra + SetLieFamily + SetLieLowerCentralSeries + SetLieNilRadical + SetLieNormalizerInParent + SetLieObject + SetLieSolvableRadical + SetLieUpperCentralSeries + SetLinearActionBasis + SetLinearCharacters + SetLinesOfStraightLineProgram + SetLongestWeylWordPerm + SetLowerCentralSeriesOfGroup + SetMagmaGeneratorsOfFamily + SetMappingGeneratorsImages + SetMappingOfWhichItIsAsGGMBI + SetMarksTom + SetMatTom + SetMatrixByBlockMatrix + SetMatrixDimension + SetMaximalAbelianQuotient + SetMaximalBlocksAttr + SetMaximalNormalSubgroups + SetMaximalSubgroupClassReps + SetMaximalSubgroups + SetMaximalSubgroupsLattice + SetMaximalSubgroupsTom + SetMinimalBlockDimension + SetMinimalGeneratingSet + SetMinimalNormalSubgroups + SetMinimalStabChain + SetMinimalSupergroupsLattice + SetMinimizedBombieriNorm + SetModuleOfExtension + SetModulusOfZmodnZObj + SetMoebiusTom + SetMolienSeriesInfo + SetMonoidOfRewritingSystem + SetMonomialComparisonFunction + SetMonomialExtrepComparisonFun + SetMovedPoints + SetMultipleAttributes + SetMultiplicationTable + SetMultiplicativeNeutralElement + SetMultiplicativeZero + SetName + SetNameIsomorphismClass + SetNamesLibTom + SetNamesOfFusionSources + SetNaturalCharacter + SetNaturalHomomorphismByNormalSubgroupNCInParent + SetNaturalHomomorphismsPool + SetNegativeRootVectors + SetNegativeRoots + SetNestingDepthA + SetNestingDepthM + SetNiceAlgebraMonomorphism + SetNiceBasis + SetNiceFreeLeftModule + SetNiceFreeLeftModuleInfo + SetNiceMonomorphism + SetNiceNormalFormByExtRepFunction + SetNiceObject + SetNilpotencyClassOfGroup + SetNilpotentClassTwoElement + SetNonLieNilpotentElement + SetNonNilpotentElement + SetNorm + SetNormalBase + SetNormalClosureInParent + SetNormalMaximalSubgroups + SetNormalSeriesByPcgs + SetNormalSubgroupClassesInfo + SetNormalSubgroups + SetNormalizerInGLnZ + SetNormalizerInGLnZBravaisGroup + SetNormalizerInHomePcgs + SetNormalizerInParent + SetNormalizersTom + SetNormedRowVector + SetNormedRowVectors + SetNormedVectors + SetNotifiedFusionsOfLibTom + SetNotifiedFusionsToLibTom + SetNrConjugacyClasses + SetNrInputsOfStraightLineProgram + SetNrMovedPoints + SetNrMovedPointsPerm + SetNrSubsTom + SetNrSyllables + SetNullAlgebra + SetNullspaceIntMat + SetNullspaceMat + SetNumberGeneratorsOfRws + SetNumberSyllables + SetNumeratorOfModuloPcgs + SetNumeratorOfRationalFunction + SetONanScottType + SetOccuringVariableIndices + SetOmegaAndLowerPCentralSeries + SetOmegaSeries + SetOne + SetOneAttr + SetOneImmutable + SetOneOfPcgs + SetOperatorOfExternalSet + SetOrbitGeneratorsOfGroup + SetOrbitLengths + SetOrbitLengthsDomain + SetOrbitStabilizingParentGroup + SetOrbits + SetOrbitsDomain + SetOrder + SetOrderingOfRewritingSystem + SetOrderingOnGenerators + SetOrderingsFamily + SetOrdersClassRepresentatives + SetOrdersTom + SetOrdinaryCharacterTable + SetOrthogonalSpaceInFullRowSpace + SetPCentralLieAlgebra + SetPCentralNormalSeriesByPcgsPGroup + SetPClassPGroup + SetPSLDegree + SetPSLUnderlyingField + SetPackageInfo + SetParent + SetParentAttr + SetParentPcgs + SetPartialClosureOfCongruence + SetPartialOrderOfHasseDiagram + SetPcGroupWithPcgs + SetPcSeries + SetPcgs + SetPcgsCentralSeries + SetPcgsChiefSeries + SetPcgsElementaryAbelianSeries + SetPcgsPCentralSeriesPGroup + SetPerfectIdentification + SetPerfectResiduum + SetPermutationTom + SetPositiveRootVectors + SetPositiveRoots + SetPositiveRootsAsWeights + SetPower + SetPowerANC + SetPowerNC + SetPowerS + SetPowerSubalgebraSeries + SetPreBasis + SetPreImagesRange + SetPreferredGenerators + SetPrefrattiniSubgroup + SetPresentation + SetPrimaryGeneratorWords + SetPrimeField + SetPrimePGroup + SetPrimePowerComponents + SetPrimitiveElement + SetPrimitiveIdentification + SetPrimitiveRoot + SetPrintFormattingStatus + SetProjectiveOrder + SetPseudoRandomSeed + SetPthPowerImages + SetQuasiDihedralGenerators + SetQuaternionGenerators + SetQuotientGroup + SetQuotientGroupHom + SetQuotientSemigroupCongruence + SetQuotientSemigroupHomomorphism + SetQuotientSemigroupPreimage + SetRClassOfHClass + SetRadicalGroup + SetRadicalOfAlgebra + SetRange + SetRankAction + SetRankMat + SetRankOfTransformation + SetRankPGroup + SetRat + SetRationalClasses + SetRationalFunctionsFamily + SetRationalizedMat + SetRealClasses + SetRecNames + SetRecursionTrapInterval + SetReducedConfluentRewritingSystem + SetReducedMultiplication + SetReesCongruenceOfSemigroupIdeal + SetReesZeroMatrixSemigroupElementIsZero + SetRefinedPcGroup + SetRegularActionHomomorphism + SetRelationsOfFpMonoid + SetRelationsOfFpSemigroup + SetRelativeOrder + SetRelativeOrderNC + SetRelativeOrders + SetRelatorsOfFpAlgebra + SetRelatorsOfFpGroup + SetRepresentative + SetRepresentativeSmallest + SetRepresentativesContainedRightCosets + SetRepresentativesMinimalBlocksAttr + SetRepresentativesPerfectSubgroups + SetRepresentativesSimpleSubgroups + SetRespectsAddition + SetRespectsAdditiveInverses + SetRespectsInverses + SetRespectsMultiplication + SetRespectsOne + SetRespectsScalarMultiplication + SetRespectsZero + SetRightActingAlgebra + SetRightActingDomain + SetRightActingGroup + SetRightActingRingOfIdeal + SetRightDerivations + SetRightTransversalInParent + SetRootOfDefiningPolynomial + SetRootSystem + SetRowIndexOfReesMatrixSemigroupElement + SetRowIndexOfReesZeroMatrixSemigroupElement + SetRowsOfReesMatrixSemigroup + SetRowsOfReesZeroMatrixSemigroup + SetRules + SetSLDegree + SetSLUnderlyingField + SetSandwichMatrixOfReesMatrixSemigroup + SetSandwichMatrixOfReesZeroMatrixSemigroup + SetSemiEchelonBasis + SetSemiEchelonMat + SetSemiEchelonMatTransformation + SetSemiSimpleType + SetSemidirectFactorsOfGroup + SetSemidirectProductInfo + SetSemigroupOfRewritingSystem + SetSiftFunction + SetSiftGroup + SetSignPerm + SetSimpleSystem + SetSimsNo + SetSize + SetSizesCentralizers + SetSizesConjugacyClasses + SetSmallGeneratingSet + SetSmallestGeneratorPerm + SetSmallestMovedPoint + SetSmallestMovedPointPerm + SetSocle + SetSocleComplement + SetSocleDimensions + SetSocleTypePrimitiveGroup + SetSortingPerm + SetSource + SetSparseCartanMatrix + SetSpecialPcgs + SetSplittingField + SetStabChainImmutable + SetStabChainMutable + SetStabChainOptions + SetStabilizerOfExternalSet + SetStandardGeneratorsInfo + SetStoredExcludedOrders + SetStoredGroebnerBasis + SetStraightLineProgElmType + SetStraightLineProgramsTom + SetString + SetStructureConstantsTable + SetStructureDescription + SetSubdirectProductInfo + SetSubfields + SetSubnormalSeriesInParent + SetSubsTom + SetSubspaces + SetSubspacesAll + SetSuccessors + SetSupersolvableResiduum + SetSurjectiveActionHomomorphismAttr + SetSylowSystem + SetSymmetricDegree + SetSymmetricParentGroup + SetTableOfMarks + SetTestMonomial + SetTestMonomialQuick + SetTestQuasiPrimitive + SetTestRelativelySM + SetTestSubnormallyMonomial + SetTietzeOrigin + SetTrace + SetTranformsOneIntoZero + SetTransformationRepresentation + SetTransformsAdditionIntoMultiplication + SetTransformsAdditiveInversesIntoInverses + SetTransformsInversesIntoAdditiveInverses + SetTransformsMultiplicationIntoAddition + SetTransformsZeroIntoOne + SetTransitiveIdentification + SetTransitivity + SetTransposedMat + SetTransposedMatAttr + SetTransposedMatImmutable + SetTransposedMatrixGroup + SetTransversal + SetTriangulizedNullspaceMat + SetTrivialCharacter + SetTrivialSubFLMLOR + SetTrivialSubadditiveMagmaWithZero + SetTrivialSubalgebra + SetTrivialSubgroup + SetTrivialSubmagmaWithOne + SetTrivialSubmodule + SetTrivialSubmonoid + SetTrivialSubnearAdditiveMagmaWithZero + SetTrivialSubspace + SetTwoClosure + SetTypeObj + SetTzOptions + SetTzRules + SetUnderlyingCharacterTable + SetUnderlyingCharacteristic + SetUnderlyingCollection + SetUnderlyingElementOfReesMatrixSemigroupElement + SetUnderlyingElementOfReesZeroMatrixSemigroupElement + SetUnderlyingExternalSet + SetUnderlyingFamily + SetUnderlyingField + SetUnderlyingGeneralMapping + SetUnderlyingGroup + SetUnderlyingLeftModule + SetUnderlyingLieAlgebra + SetUnderlyingMagma + SetUnderlyingRelation + SetUnderlyingSemigroupOfReesMatrixSemigroup + SetUnderlyingSemigroupOfReesZeroMatrixSemigroup + SetUnderlyingVectorSpace + SetUnits + SetUniversalEnvelopingAlgebra + SetUpperActingDomain + SetUpperCentralSeriesOfGroup + SetValueOption + SetValuesOfClassFunction + SetWeightOfGenerators + SetWeightsTom + SetWeylGroup + SetWreathProductInfo + SetX + SetXHelp + SetZClassRepsQClass + SetZero + SetZeroAttr + SetZeroCoefficient + SetZeroImmutable + SetZuppos + SetnpeGL + SetnpePSL + SetnpeSL + SetsOrbits + Setter + ShallowCopy + ShapeFrequencies + SharedObj + SharedType + ShiftedCoeffs + ShiftedPadicNumber + ShortLexOrdering + ShortLexOrderingNC + ShortestVectors + ShowArgument + ShowArguments + ShowDetails + ShowImpliedFilters + ShowMethods + ShowOtherMethods + ShowPackageVariables + ShrinkCoeffs + ShrinkRowVector + ShrinkableHashTable + ShrinkableSingleValuedHashTable + Sift + SiftAsWord + SiftForStrongGenerator + SiftFunction + SiftGroup + SiftOneLevel + SiftVector + SiftedPcElement + SiftedPermutation + SiftedVector + SiftedVectorForGaussianMatrixSpace + SiftedVectorForGaussianRowSpace + SiftedWord + Sigma + SignInt + SignPartition + SignPerm + SignPermGroup + SimpleLieAlgebra + SimpleLieAlgebraTypeH + SimpleLieAlgebraTypeK + SimpleLieAlgebraTypeS + SimpleLieAlgebraTypeW + SimpleSystem + SimplifiedFpGroup + SimplifyPresentation + SimsName + SimsNo + SimultaneousEigenvalues + SingleCollector + SingleCollectorByGenerators + SingleCollectorByRelators + SingleValuedHashTable + Size + SizeBlist + SizeConsiderFunction + SizeGL + SizeNumbersPerfectGroups + SizeOfChainOfGroup + SizeOfFieldOfDefinition + SizeOfGLdZmodmZ + SizePSL + SizePolynomialUnipotentClassGL + SizeSL + SizeScreen + SizeStabChain + SizeUpperBound + SizesCentralizers + SizesConjugacyClasses + SizesPerfectGroups + Sleep + SmallGeneratingSet + SmallGroup + SmallGroupsInformation + SmallSpaceCutoff + SmallerDegreePermutationRepresentation + SmallestGeneratorPerm + SmallestMovedPoint + SmallestMovedPointPerm + SmallestMovedPointPerms + SmallestPrimeDivisor + SmallestRootInt + SmithNormalFormIntegerMat + SmithNormalFormIntegerMatTransforms + SmithNormalFormSQ + Socle + SocleComplement + SocleDimensions + SocleTypePrimitiveGroup + SolutionIntMat + SolutionMat + SolutionMatDestructive + SolutionMatNoCo + SolutionNullspaceIntMat + SolutionSQ + SolvableNormalClosurePermGroup + SolvableQuotient + SomeVerbalSubgroups + Sort + SortParallel + SortRationalClasses + SortRelsSortedByStartGen + SortedCharacterTable + SortedCharacters + SortedList + SortedSparseActionHomomorphism + SortedSparseActionHomomorphismOp + SortedTom + Sortex + SortingPerm + Source + SourceElt + Sp + SpanOfMatrixGroup + SpanningTree + SparseActionHomomorphism + SparseActionHomomorphismOp + SparseCartanMatrix + SparseHashTable + SparseIntKey + SpecialLinearGroup + SpecialLinearGroupCons + SpecialOrthogonalGroup + SpecialOrthogonalGroupCons + SpecialPcgs + SpecialPcgsFactor + SpecialPcgsSubgroup + SpecialUnitaryGroup + SpecialUnitaryGroupCons + SpecializedExtRepPol + SpinInductionScheme + SpinorNorm + SplitCell + SplitCharacters + SplitExtension + SplitStep + SplitString + SplitStringInternal + SplitTwoSpace + SplitUpSublistsByFpFunc + SplitWordTail + SplittingField + Sqrt + SquareRoots + StabChain + StabChainBaseStrongGenerators + StabChainForcePoint + StabChainImmutable + StabChainMutable + StabChainOp + StabChainOptions + StabChainPermGroupToPermGroupGeneralMappingByImages + StabChainRandomPermGroup + StabChainStrong + StabChainSwap + Stabilizer + StabilizerByMatrixOperation + StabilizerFunc + StabilizerFuncOp + StabilizerOfBlockNC + StabilizerOfExternalSet + StabilizerOp + StabilizerPcgs + StandardAssociate + StandardClassMatrixColumn + StandardGeneratorsFunctions + StandardGeneratorsInfo + StandardGeneratorsOfFullHomModule + StandardGeneratorsOfFullMatrixModule + StandardGeneratorsOfGroup + StandardScalarProduct + StandardizeTable + StandardizeTableC + StarCyc + StateRandom + StatusRandom + StepModGauss + StopNumConsecSiftToOne + StopNumSift + StopSize + StoreAlgExtFam + StoreFactorsPol + StoreFusion + StoreInfoFreeMagma + StoredExcludedOrders + StoredGroebnerBasis + StraightLineProgElm + StraightLineProgElmType + StraightLineProgGens + StraightLineProgram + StraightLineProgramElmRankFilter + StraightLineProgramNC + StraightLineProgramsDefaultType + StraightLineProgramsFamily + StraightLineProgramsTom + StratMeetPartition + StreamsFamily + StretchImportantSLPElement + String + StringDate + StringFamily + StringFile + StringImType + StringOfResultOfLineOfStraightLineProgram + StringOfResultOfStraightLineProgram + StringOfUnivariateRationalPolynomialByCoefficients + StringPP + StringPrint + StringStreamInputTextFile + StringTime + StringToStraightLineProgram + StringUnivariateLaurent + StringView + StripBeginEnd + StripEscapeSequences + StrongGeneratorsStabChain + StrongGens + StronglyConnectedComponents + StructuralCopy + StructureConstantsPadicNumbers + StructureConstantsTable + StructureDescription + SuPeRfail + SubAlgebraModule + SubFLMLOR + SubFLMLORNC + SubFLMLORWithOne + SubFLMLORWithOneNC + SubGModLeadPos + SubSyllables + SubadditiveGroup + SubadditiveGroupNC + SubadditiveMagma + SubadditiveMagmaNC + SubadditiveMagmaWithInverses + SubadditiveMagmaWithInversesNC + SubadditiveMagmaWithZero + SubadditiveMagmaWithZeroNC + Subalgebra + SubalgebraNC + SubalgebraWithOne + SubalgebraWithOneNC + SubdirProdPcGroups + SubdirectDiagonalPerms + SubdirectProduct + SubdirectProductInfo + SubdirectProductOp + SubdirectProducts + Subfield + SubfieldNC + Subfields + SubgpConjSymmgp + Subgroup + SubgroupByPcgs + SubgroupByProperty + SubgroupGeneratorsCosetTable + SubgroupMethodByNiceMonomorphism + SubgroupMethodByNiceMonomorphismCollColl + SubgroupMethodByNiceMonomorphismCollElm + SubgroupMethodByNiceMonomorphismCollOther + SubgroupNC + SubgroupOfWholeGroupByCosetTable + SubgroupOfWholeGroupByQuotientSubgroup + SubgroupProperty + SubgroupShell + SubgroupsMethodByNiceMonomorphism + SubgroupsOrbitsAndNormalizers + SubgroupsSolvableGroup + Submagma + SubmagmaNC + SubmagmaWithInverses + SubmagmaWithInversesNC + SubmagmaWithOne + SubmagmaWithOneNC + Submodule + SubmoduleNC + Submonoid + SubmonoidNC + SubnearAdditiveGroup + SubnearAdditiveGroupNC + SubnearAdditiveMagma + SubnearAdditiveMagmaNC + SubnearAdditiveMagmaWithInverses + SubnearAdditiveMagmaWithInversesNC + SubnearAdditiveMagmaWithZero + SubnearAdditiveMagmaWithZeroNC + SubnormalSeries + SubnormalSeriesInParent + SubnormalSeriesOp + SuboLiBli + SuboSiBli + SuboTruePos + SuboUniteBlist + Suborbits + Subring + SubringNC + SubringWithOne + SubringWithOneNC + SubsTom + Subsemigroup + SubsemigroupNC + Subsemiring + SubsemiringNC + SubsemiringWithOne + SubsemiringWithOneAndZero + SubsemiringWithOneAndZeroNC + SubsemiringWithOneNC + SubsemiringWithZero + SubsemiringWithZeroNC + Subspace + SubspaceNC + SubspaceVectorSpaceGroup + Subspaces + SubspacesAll + SubspacesDim + SubstitutedWord + SubstitutionSublist + SubtractBlist + SubtractBlistOrbitStabChain + SubtractSet + Subtype + Subword + Successors + SuggestUpgrades + Sum + SumCoefPolynomial + SumCoefRatfun + SumCoeffLaurpol + SumCoeffUnivfunc + SumFactorizationFunctionPcgs + SumIntersectionMat + SumOfMBMAndMapping + SumOfMappingAndMBM + SumOfPcElement + SumOp + SumPcgs + SumRootsPol + SumRootsPolComp + SumX + SumXHelp + SummandMolienSeries + SupType + SupersolvableResiduum + SupersolvableResiduumDefault + SupportedCharacterTableInfo + SupportedLibraryTableComponents + SurjectiveActionHomomorphismAttr + SuzukiGroup + SuzukiGroupCons + SyllableRepAssocWord + SyllableWordObjByExtRep + SylowComplement + SylowComplementOp + SylowSubgroup + SylowSubgroupOp + SylowSubgroupPermGroup + SylowSystem + SymAdic + SymmetricClosureBinaryRelation + SymmetricDegree + SymmetricGroup + SymmetricGroupCons + SymmetricParentGroup + SymmetricParts + SymmetricPower + SymmetricPowerOfAlgebraModule + Symmetrisations + Symmetrizations + SymplecticComponents + SymplecticGroup + SymplecticGroupCons + SyzygyCriterion + Sz + TRANSGrp + TRANSProperties + TableAutomorphisms + TableHasIntKeyFun + TableOfMarks + TableOfMarksByLattice + TableOfMarksComponents + TableOfMarksCyclic + TableOfMarksDihedral + TableOfMarksFamily + TableOfMarksFrobenius + TableOfMarksFromLibrary + TailOfPcgsPermGroup + TailsInverses + Tau + TeX + TeXObj + TemporaryGlobalVarName + TensorProduct + TensorProductGModule + TensorProductOfAlgebraModules + TensorWreathProductOfMatrixGroup + Tensored + TestConsistencyMaps + TestHomogeneous + TestInducedFromNormalSubgroup + TestJacobi + TestMonomial + TestMonomialFromLattice + TestMonomialQuick + TestMonomialUseLattice + TestPackageAvailability + TestQuasiPrimitive + TestRelativelySM + TestRelativelySMFun + TestRow + TestSubnormallyMonomial + Tester + TextAttr + TextM + TietzeOrigin + TietzeWordAbstractWord + TmpDirectory + TmpName + ToBeDefinedObjFamily + ToBeDefinedObjType + ToggleEcho + TopExtensionsByAutomorphism + Trace + TraceDefinition + TraceImmediateMethods + TraceMat + TraceMethods + TraceModQF + TracePolynomial + TracedCosetFpGroup + TrailingEntriesLTM + TranformsOneIntoZero + TransArrange + TransCombinat + TransGrpLoad + TransStabCSPG + TransferDiagram + TransferPcgsInfo + TransferedExtensionPol + Transformation + TransformationData + TransformationFamily + TransformationNC + TransformationRelation + TransformationRepresentation + TransformationType + TransformingPermutationFamily + TransformingPermutations + TransformingPermutationsCharacterTables + TransformsAdditionIntoMultiplication + TransformsAdditiveInversesIntoInverses + TransformsInversesIntoAdditiveInverses + TransformsMultiplicationIntoAddition + TransformsZeroIntoOne + TransitiveClosureBinaryRelation + TransitiveGroup + TransitiveIdentification + Transitivity + TranslateString + TranslatorSubalgebra + TransposedMat + TransposedMatAttr + TransposedMatDestructive + TransposedMatImmutable + TransposedMatMutable + TransposedMatOp + TransposedMatrixGroup + TransvByDirProdFamily + TransvByHomomorphismFamily + TransvBySchreierTreeFamily + TransvBySiftFunctFamily + TransvByTrivSubgrpFamily + Transversal + TransversalBySiftFunction + TransversalByTrivial + TransversalElt + TransversalOfChainSubgroup + TreeEntry + TreeRepresentedWord + TrialQuotientRPF + TriangulizeIntegerMat + TriangulizeMat + TriangulizeMonomialElementList + TriangulizeWeightRepElementList + TriangulizedGeneratorsByMatrix + TriangulizedIntegerMat + TriangulizedIntegerMatTransform + TriangulizedIntegerMatTransforms + TriangulizedNullspaceMat + TriangulizedNullspaceMatDestructive + TriangulizedNullspaceMatNT + TrivialCharacter + TrivialGModule + TrivialGroup + TrivialGroupCons + TrivialIterator + TrivialModule + TrivialPartition + TrivialQuotientSubgroup + TrivialSubFLMLOR + TrivialSubadditiveMagmaWithZero + TrivialSubalgebra + TrivialSubgroup + TrivialSubmagmaWithOne + TrivialSubmodule + TrivialSubmonoid + TrivialSubnearAdditiveMagmaWithZero + TrivialSubspace + TryCombinations + TryConwayPolynomialForFrobeniusCharacterValue + TryCosetTableInWholeGroup + TryGcdCancelExtRepPolynomials + TryLayerSQ + TryModuleSQ + TryNextMethod + TryPcgsPermGroup + TrySecondaryImages + Tschirnhausen + Tuple + TupleNC + Tuples + TuplesFamily + TuplesK + TwoClosure + TwoClosurePermGroup + TwoCoboundaries + TwoCoboundariesSQ + TwoCocycles + TwoCocyclesSQ + TwoCohomology + TwoCohomologySQ + TwoSeqPol + TwoSidedIdeal + TwoSidedIdealByGenerators + TwoSidedIdealNC + TwoSquares + TypeObj + TypeOfDefaultGeneralMapping + TypeOfFamilies + TypeOfFamilyOfFamilies + TypeOfFamilyOfTypes + TypeOfTypes + TzCheckRecord + TzEliminate + TzEliminateFromTree + TzEliminateGen + TzEliminateGens + TzFindCyclicJoins + TzGeneratorExponents + TzGo + TzGoGo + TzImagesOldGens + TzInitGeneratorImages + TzMostFrequentPairs + TzNewGenerator + TzOccurrences + TzOccurrencesPairs + TzOptionNames + TzOptions + TzPreImagesNewGens + TzPrint + TzPrintGeneratorImages + TzPrintGenerators + TzPrintLengths + TzPrintOptions + TzPrintPairs + TzPrintPresentation + TzPrintRelators + TzPrintStatus + TzRelator + TzRemoveGenerators + TzRenumberGens + TzReplaceGens + TzRules + TzSearch + TzSearchC + TzSearchEqual + TzSort + TzSortC + TzSubstitute + TzSubstituteCyclicJoins + TzSubstituteGen + TzSubstituteWord + TzTestInitialSetup + TzUpdateGeneratorImages + TzWordAbstractWord + UNIXSelect + UglyVector + UnInstallCharReadHookFunc + UnSetImage + Unbind + UnbindElmWPObj + UnbindGlobal + UnderlyingCharacterTable + UnderlyingCharacteristic + UnderlyingCollection + UnderlyingDomainOfBinaryRelation + UnderlyingElement + UnderlyingElementOfReesMatrixSemigroupElement + UnderlyingElementOfReesZeroMatrixSemigroupElement + UnderlyingExternalSet + UnderlyingFamily + UnderlyingField + UnderlyingGeneralMapping + UnderlyingGroup + UnderlyingLeftModule + UnderlyingLieAlgebra + UnderlyingMagma + UnderlyingRelation + UnderlyingSemigroupOfReesMatrixSemigroup + UnderlyingSemigroupOfReesZeroMatrixSemigroup + UnderlyingVectorSpace + UndoRefinement + UnhideGlobalVariables + Union + UnionBlist + UnionSet + UnipotentSubgroup + Unique + UniteBlist + UniteBlistList + UniteSet + Units + UnivariateLaurentPolynomialByCoefficients + UnivariatePolynomial + UnivariatePolynomialByCoefficients + UnivariatePolynomialRing + UnivariateRationalFunctionByCoefficients + UnivariateRationalFunctionByExtRep + UnivariatenessTestRationalFunction + UniversalEnvelopingAlgebra + Unknown + UnknownsType + UnloadSmallGroupsData + UnlockNaturalHomomorphismsPool + UnmarkTree + UnorderedTuples + UnorderedTuplesK + UnprofileFunctions + UnprofileMethods + UntraceMethods + UpEnv + UpdateMap + UpdatePolycyclicCollector + UpdateWeightInfo + UpperActingDomain + UpperCentralSeries + UpperCentralSeriesOfGroup + UpperSubdiagonal + UseBasis + UseFactorRelation + UseIsomorphismRelation + UseMatrixChainSubgroups + UsePermChainSubgroups + UseStabChainViaChainSubgroup + UseSubsetRelation + UseSubsetRelationNC + VPActionHom + VSTInsertToLeft + VSTNode + ValidatePackageInfo + Valuation + Value + ValueCochain + ValueGlobal + ValueMolienSeries + ValueOption + ValuePol + ValuesOfClassFunction + VectorOfRelator + VectorSearchTable + VectorSpace + VectorSpaceByPcgsOfElementaryAbelianGroup + VerifySGS + VerifyStabilizer + View + ViewFullHomModule + ViewLength + ViewMolienSeries + ViewObj + VirtualCharacter + WallForm + WeakPointerObj + WedgeGModule + WeekDay + WeightLexOrdering + WeightLexOrderingNC + WeightOfGenerators + WeightVecFFE + WeightsTom + WeylGroup + WeylOrbitIterator + Where + WindowCmd + Word + WordAlp + WordsString + WreathElm + WreathProduct + WreathProductImprimitiveAction + WreathProductInfo + WreathProductOfMatrixGroup + WreathProductOrdering + WreathProductProductAction + WriteAll + WriteBibFile + WriteByte + WriteLine + ZClassRepsQClass + ZOp + ZassenhausIntersection + Zero + ZeroAttr + ZeroCoefficient + ZeroCoefficientRatFun + ZeroImmutable + ZeroMapping + ZeroMutable + ZeroOp + ZeroSM + ZeroSameMutability + ZevData + ZevDataValue + ZippedListQuotient + ZippedProduct + ZippedSum + ZmodnZ + ZmodnZObj + ZmodpZ + ZmodpZNC + ZumbroichBase + Zuppos + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/gcc.xml b/kate/part/syntax/data/gcc.xml new file mode 100644 index 00000000..0f78b336 --- /dev/null +++ b/kate/part/syntax/data/gcc.xml @@ -0,0 +1,581 @@ + + + + + +]> + + + diff --git a/kate/part/syntax/data/gdb.xml b/kate/part/syntax/data/gdb.xml new file mode 100644 index 00000000..85f9383f --- /dev/null +++ b/kate/part/syntax/data/gdb.xml @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/kate/part/syntax/data/gdl.xml b/kate/part/syntax/data/gdl.xml new file mode 100644 index 00000000..a41af14c --- /dev/null +++ b/kate/part/syntax/data/gdl.xml @@ -0,0 +1,313 @@ + + + + + + white + blue + red + green + yellow + magenta + cyan + darkgrey + darkgray + darkblue + darkred + darkgreen + darkyellow + darkmagenta + darkcyan + gold + lightgrey + lightgray + lightblue + lightred + lightgreen + lightyellow + lightmagenta + lightcyan + lilac + turquoise + aquamarine + khaki + purple + yellowgreen + pink + orange + orchid + black + + + box + triangle + circle + ellipse + hexagon + rhomb + rhomboid + trapeze + uptrapeze + trapezoid + uptrapezoid + lparallelogram + rparallelogram + + + unfolded + folded + boxed + clustered + wrapped + exclusive + white + + + normal + tree + forcedir + dfs + minbackward + maxdepth + maxdepthslow + mindepth + mindepthslow + minindegree + minoutdegree + maxindegree + maxoutdegree + maxdegree + mindegree + + + attraction + repulsion + randomfactor + randomimpulse + randomrounds + tempscheme + temptreshold + tempmin + tempmax + + + no + polar + circular + polcircular + orthogonal + + + toptobottom + bottomtotop + lefttoright + righttoleft + top_to_bottom + bottom_to_top + left_to_right + right_to_left + + + solid + continuous + dashed + dotted + double + triple + invisible + + + pfish + cfish + fpfish + fcfish + dpfish + dcfish + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/generate-php.pl b/kate/part/syntax/data/generate-php.pl new file mode 100755 index 00000000..0c3ab187 --- /dev/null +++ b/kate/part/syntax/data/generate-php.pl @@ -0,0 +1,58 @@ +#!/usr/bin/env perl + +# This perl script read stdin and write on stdout. It shall be an XML language file. +# +# * If the name of the language is 'HTML', then it creates the language 'PHP (HTML)' +# which shall be used for PHP hl. +# +# * If the name of the language is something else (say '*'), it creates the language '*/PHP'. +# This new language is the same as the old one, but is able to detect PHP everywhere. +# +# This script will correctly set extensions & mimetype, and will replace +# by +# +# Generated languages need a language named 'PHP/PHP', which shall take care of PHP hl itself +# and which will be called every time something like +# License: LGPL + +my $file = ""; + +while (<>) +{ + $file .= $_; +} + +$warning = "\n\n\n"; + +$file =~ s/(?=]+name="HTML"/) +{ + $root = 1; +} + +if ($root == 1) +{ + $file =~ s/]+)name="[^"]*"/]+)section="[^"]*"/]+)extensions="[^"]*"/]+)mimetype="[^"]*"/]+)name="([^"]*)"/
$' \ + | sed -e 's,
\(.*\)
, \1 ,' \ + | grep -v 'qoutputrange' + rm $tmp +else + cat < \1 ,' \ + | sort \ + | uniq \ + | grep -v EXPORT \ + | grep -v QT_BEGIN_ \ + | grep -v QT_END_ \ + | grep -v QT_MANGLE_ +else + cat < + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/git-rebase.xml b/kate/part/syntax/data/git-rebase.xml new file mode 100644 index 00000000..69f4b535 --- /dev/null +++ b/kate/part/syntax/data/git-rebase.xml @@ -0,0 +1,65 @@ + + + + + + p + pick + + + r + reword + + + e + edit + + + s + squash + + + f + fixup + + + x + exec + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/glosstex.xml b/kate/part/syntax/data/glosstex.xml new file mode 100644 index 00000000..b9a98866 --- /dev/null +++ b/kate/part/syntax/data/glosstex.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/glsl.xml b/kate/part/syntax/data/glsl.xml new file mode 100644 index 00000000..53a87226 --- /dev/null +++ b/kate/part/syntax/data/glsl.xml @@ -0,0 +1,323 @@ + + + + + + break + continue + do + for + while + + if + else + + true + false + + discard + return + + struct + + + float + int + void + bool + + mat2 + mat3 + mat4 + + vec2 + vec3 + vec4 + ivec2 + ivec3 + ivec4 + bvec2 + bvec3 + bvec4 + + sampler1D + sampler2D + sampler3D + samplerCube + sampler1DShadow + sampler2DShadow + + + attribute + const + uniform + varying + + in + out + inout + + + FIXME + TODO + BUG + + + + radians + degrees + sin + cos + tan + asin + acos + atan + + + pow + exp + log + exp2 + log2 + sqrt + inversesqrt + + + abs + sign + floor + ceil + fract + mod + min + max + clamp + mix + step + smoothstep + + + length + distance + dot + cross + normalize + ftransform + faceforward + reflect + refract + + + matrixCompMult + + + lessThan + lessThenEqual + greaterThan + greaterThanEqual + equal + notEqual + any + all + not + + + texture1D + texture1DProj + texture1DLod + texture1DProjLod + texture2D + texture2DProj + texture2DLod + texture2DProjLod + texture3D + texture3DProj + texture3DLod + texture3DProjLod + textureCube + textureCubeLod + shadow1D + shadow2D + shadow1DProj + shadow2DProj + shadow1DLod + shadow2DLod + shadow1DProjLod + shadow2DProjLod + + + dFdx + dFdy + fwidth + + + noise1 + noise2 + noise3 + noise4 + + + + gl_Position + gl_PointSize + gl_ClipVertex + + + gl_FragCoord + gl_FragFacing + gl_FragColor + gl_FragData + gl_FragDepth + + + gl_Color + gl_SecondaryColor + gl_Normal + gl_Vertex + gl_MultiTexCoord0 + gl_MultiTexCoord1 + gl_MultiTexCoord2 + gl_MultiTexCoord2 + gl_MultiTexCoord3 + gl_MultiTexCoord4 + gl_MultiTexCoord5 + gl_MultiTexCoord6 + gl_MultiTexCoord7 + gl_FogColor + + + gl_MaxLights + gl_MaxClipPlanes + gl_MaxTextureUnits + gl_MaxTextureCoords + gl_MaxVertexAttributes + gl_MaxVertexUniformComponents + gl_MaxVaryingFloats + gl_MaxVertexTextureImageUnits + gl_MaxCombinedTextureImageUnits + gl_MaxTextureImageUnits + gl_MaxFragmentUniformComponents + gl_MaxDrawBuffers + + + gl_ModelViewMatrix + gl_ProjectionMatrix + gl_ModelViewProjectionMatrix + gl_TextureMatrix + gl_NormalMatrix + gl_ModelViewMatrixInverse + gl_ProjectionMatrixInverse + gl_ModelViewProjectionMatrixInverse + gl_TextureMatrixInverse + gl_ModelViewMatrixTranspose + gl_ProjectionMatrixTranspose + gl_ModelViewProjectionMatrixTranspose + gl_TextureMatrixTranspose + gl_ModelViewMatrixInverseTranspose + gl_ProjectionMatrixInverseTranspose + gl_ModelViewProjectionMatrixInverseTranspose + gl_TextureMatrixInverseTranspose + gl_NormScale + gl_DepthRangeParameters + gl_DepthRange + gl_ClipPlane + gl_PointParameters + gl_Point + gl_MaterialParameters + gl_FrontMaterial + gl_BackMaterial + gl_LightSourceParameters + gl_LightSource + gl_LightModelParameters + gl_LightModel + gl_LightModelProducts + gl_FrontLightModelProduct + gl_BackLightModelProduct + gl_LightProducts + gl_FrontLightProduct + gl_BackLightProduct + gl_TextureEnvColor + gl_EyePlaneS + gl_EyePlaneT + gl_EyePlaneR + gl_EyePlaneQ + gl_ObjectPlaneS + gl_ObjectPlaneT + gl_ObjectPlaneR + gl_ObjectPlaneQ + gl_FogParameters + gl_Fog + + + gl_FrontColor + gl_BackColor + gl_FrontSecondaryColor + gl_BackSecondaryColor + gl_TexCoord + gl_FogFragCoord + gl_Color + gl_SecondaryColor + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/gnuassembler.xml b/kate/part/syntax/data/gnuassembler.xml new file mode 100644 index 00000000..73db06cf --- /dev/null +++ b/kate/part/syntax/data/gnuassembler.xml @@ -0,0 +1,303 @@ + + + + + + + + .abort + .align + .app-file + .appline + .ascii + .asciz + .att_syntax + .balign + .balignl + .balignw + .byte + .code16 + .code32 + .comm + .common.s + .common + .data + .dc.b + .dc.d + .dc.l + .dc.s + .dc.w + .dc.x + .dc + .dcb.b + .dcb.d + .dcb.l + .dcb.s + .dcb.w + .dcb.x + .dcb + .debug + .def + .desc + .dim + .double + .ds.b + .ds.d + .ds.l + .ds.p + .ds.s + .ds.w + .ds.x + .ds + .dsect + .eject + .else + .elsec + .elseif + .end + .endc + .endef + .endfunc + .endif + .endm + .endr + .equ + .equiv + .err + .exitm + .extend + .extern + .fail + .file + .fill + .float + .format + .func + .global + .globl + .hidden + .hword + .ident + .if + .ifc + .ifdef + .ifeq + .ifeqs + .ifge + .ifgt + .ifle + .iflt + .ifnc + .ifndef + .ifne + .ifnes + .ifnotdef + .include + .int + .intel_syntax + .internal + .irep + .irepc + .irp + .irpc + .lcomm + .lflags + .line + .linkonce + .list + .llen + .ln + .long + .lsym + .macro + .mexit + .name + .noformat + .nolist + .nopage + noprefix + .octa + .offset + .org + .p2align + .p2alignl + .p2alignw + .page + .plen + .popsection + .previous + .print + .protected + .psize + .purgem + .pushsection + .quad + .rodata + .rep + .rept + .rva + .sbttl + .scl + .sect.s + .sect + .section.s + .section + .set + .short + .single + .size + .skip + .sleb128 + .space + .spc + .stabd + .stabn + .stabs + .string + .struct + .subsection + .symver + .tag + .text + .title + .ttl + .type + .uleb128 + .use + .val + .version + .vtable_entry + .vtable_inherit + .weak + .word + .xcom + .xdef + .xref + .xstabs + .zero + + .arm + .bss + .code + .even + .force_thumb + .ldouble + .loc + .ltorg + .packed + .pool + .req + .thumb + .thumb_func + .thumb_set + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/gnuplot.xml b/kate/part/syntax/data/gnuplot.xml new file mode 100644 index 00000000..dee90da0 --- /dev/null +++ b/kate/part/syntax/data/gnuplot.xml @@ -0,0 +1,888 @@ + + + + + + noarrow + noborder + noclabel + noclip + nocontour + nodgrid3d + nogrid + nohidden3d + nokey + nolabel + nolinestyle + nomultiplot + nomx2tics + nomxtics + nomy2tics + nomytics + nomztics + nooffsets + noparametric + nopolar + nosurface + notimestamp + nox2dtics + nox2mtics + nox2tics + nox2zeroaxis + noxdtics + noxmtics + noxtics + noxzeroaxis + noy2dtics + noy2mtics + noy2tics + noy2zeroaxis + noydtics + noymtics + noytics + noyzeroaxis + nozdtics + nozeroaxis + nozmtics + noztics + + + + grid + parametric + + + + angles + arrow + bar + border + boxwidth + clip + cntrparam + data + dgrid3d + dummy + encoding + format + function + functions + hidden3d + isosamples + label + linestyle + mapping + margin + mx2tics + mxtics + my2tics + mytics + mztics + offsets + origin + parametric + pointsize + polar + samples + size + data style + function style + surface + tics + ticscale + ticslevel + timefmt + timestamp + variables + version + view + x2data + x2dtics + x2label + x2mtics + x2tics + x2zeroaxis + xdata + xdtics + xmtics + xtics + xzeroaxis + y2data + y2dtics + y2mtics + y2tics + y2zeroaxis + ydata + ydtics + ymtics + ytics + yzeroaxis + zdata + zdtics + zero + zeroaxis + zmtics + ztics + + + + clabel + title + xlabel + x2label + ylabel + y2label + zlabel + + + + rrange + trange + urange + vrange + x2range + xrange + y2range + yrange + zrange + + + + base + surface + both + + + + left + right + top + bottom + outside + below + Left + Right + noreverse + reverse + + + + noautoscale + autoscale + nologscale + + + + x + y + z + x2 + y2 + xy + + + + aed512 + aed767 + aifm + bitgraph + cgm + dumb + dxf + dxy800a + eepic + epson-180dpi + epson-60dpi + epson-lx800 + excl + gpic + hp2623a + hp2648 + kc-tek40xx + km-tek40xx + kyo + mf + mif + mp + nec-cp6 + okidata + prescribe + pstricks + qms + regis + rgip + selanar + starc + table + tandy-60dpi + tek40xx + tek410x + texdraw + uniplex + unixplot + vttek + vx384 + x11 + + + + landscape + portrait + eps + default + enhanced + noenhanced + solid + dashed + defaultplex + simplex + duplex + + + + courier + roman + default + + + + color + monochrome + dashed + rotate + norotate + auxfile + + + + transparent + notransparent + + + + small + medium + large + monochrome + gray + color + + + + landscape + portrait + solid + dashed + + + + monochrome + color + small + big + pointsmax + landscape + portrait + metric + inches + fontsize + size + thickness + depth + + + + monochrome + color + + + + FNT5X9 + FNT9X17 + FNT13X25 + + + + mode + landscape + portrait + monochrome + color + solid + dashed + letter + legal + noextended + extended + stick + univers + cg_times + zapf_dingbats + antique_olive + arial + courier + garamond_antigua + letter_gothic + cg_omega + albertus + times_new_roman + clarendon + coronet + marigold + truetype_symbols + wingdings + + + + 75 + 100 + 150 + 300 + + + + landscape + portrait + + + + acsplines + bezier + csplines + sbezier + unique + + + + x1y1 + x2y1 + x1y2 + x2y2 + + + + lines + l + points + p + linespoints + linesp + impulses + i + dots + d + steps + fsteps + histeps + + + + errorbars + xerrorbars + yerrorbars + xyerrorbars + boxes + boxerrorbars + boxxyerrorbars + financebars + candlesticks + vector + + + + linestyle + ls + linetype + lt + linewidth + lw + pointtype + pt + pointsize + ps + + + + replot + exit + quit + clear + reset + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/go.xml b/kate/part/syntax/data/go.xml new file mode 100644 index 00000000..ff44defc --- /dev/null +++ b/kate/part/syntax/data/go.xml @@ -0,0 +1,176 @@ + + + + + + + + + + + break + case + chan + const + continue + default + defer + else + fallthrough + for + func + go + goto + if + import + interface + map + package + range + return + select + struct + switch + type + var + + + bool + byte + complex64 + complex128 + error + float32 + float64 + int + int8 + int16 + int32 + int64 + rune + string + uint + uintptr + uint8 + uint16 + uint32 + uint64 + + + append + cap + close + complex + copy + delete + imag + len + make + new + panic + print + println + real + recover + + + false + nil + true + iota + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/grammar.xml b/kate/part/syntax/data/grammar.xml new file mode 100644 index 00000000..23ed2c55 --- /dev/null +++ b/kate/part/syntax/data/grammar.xml @@ -0,0 +1,145 @@ + + + + + + + ast_extra_members + export_macro + export_macro_header + namespace + parserclass + token + token_stream + parser_declaration_header + parser_bits_header + ast_header + ast_base + parser_base + bin + pre + post + tern + paren + left + right + < + > + + + try/rollback + try/recover + catch + + + public + protected + private + + + declaration + destructor + constructor + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/haml.xml b/kate/part/syntax/data/haml.xml new file mode 100644 index 00000000..48bcb131 --- /dev/null +++ b/kate/part/syntax/data/haml.xml @@ -0,0 +1,523 @@ + + + + + + + + + + + + + + + + + BEGIN + END + and + begin + break + case + defined? + do + else + elsif + end + ensure + for + if + in + include + next + not + or + redo + rescue + retry + return + then + unless + until + when + while + yield + + + + private_class_method + private + protected + public_class_method + public + + + + attr_reader + attr_writer + attr_accessor + + + + alias + module + class + def + undef + + + + self + super + nil + false + true + caller + __FILE__ + __LINE__ + + + + $stdout + $defout + $stderr + $deferr + $stdin + + + + + + abort + at_exit + autoload + autoload? + binding + block_given? + callcc + caller + catch + chomp + chomp! + chop + chop! + eval + exec + exit + exit! + fail + fork + format + getc + gets + global_variables + gsub + gsub! + iterator? + lambda + load + local_variables + loop + method_missing + open + p + print + printf + proc + putc + puts + raise + rand + readline + readlines + require + scan + select + set_trace_func + sleep + split + sprintf + srand + sub + sub! + syscall + system + test + throw + trace_var + trap + untrace_var + warn + + + + TODO + FIXME + NOTE + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/haskell.xml b/kate/part/syntax/data/haskell.xml new file mode 100644 index 00000000..a62556a8 --- /dev/null +++ b/kate/part/syntax/data/haskell.xml @@ -0,0 +1,452 @@ + + + + + + case + class + data + deriving + do + else + if + in + infixl + infixr + instance + let + module + newtype + of + primitive + then + type + where + + + FilePath + IOError + abs + acos + acosh + all + and + any + appendFile + approxRational + asTypeOf + asin + asinh + atan + atan2 + atanh + basicIORun + break + catch + ceiling + chr + compare + concat + concatMap + const + cos + cosh + curry + cycle + decodeFloat + denominator + digitToInt + div + divMod + drop + dropWhile + either + elem + encodeFloat + enumFrom + enumFromThen + enumFromThenTo + enumFromTo + error + even + exp + exponent + fail + filter + flip + floatDigits + floatRadix + floatRange + floor + fmap + foldl + foldl1 + foldr + foldr1 + fromDouble + fromEnum + fromInt + fromInteger + fromIntegral + fromRational + fst + gcd + getChar + getContents + getLine + group + head + id + inRange + index + init + intToDigit + interact + ioError + isAlpha + isAlphaNum + isAscii + isControl + isDenormalized + isDigit + isHexDigit + isIEEE + isInfinite + isLower + isNaN + isNegativeZero + isOctDigit + isPrint + isSpace + isUpper + iterate + last + lcm + length + lex + lexDigits + lexLitChar + lines + log + logBase + lookup + map + mapM + mapM_ + max + maxBound + maximum + maybe + min + minBound + minimum + mod + negate + not + notElem + null + numerator + odd + or + ord + otherwise + pack + pi + pred + primExitWith + print + product + properFraction + putChar + putStr + putStrLn + quot + quotRem + range + rangeSize + read + readDec + readFile + readFloat + readHex + readIO + readInt + readList + readLitChar + readLn + readOct + readParen + readSigned + reads + readsPrec + realToFrac + recip + rem + repeat + replicate + return + reverse + round + scaleFloat + scanl + scanl1 + scanr + scanr1 + seq + sequence + sequence_ + show + showChar + showInt + showList + showLitChar + showParen + showSigned + showString + shows + showsPrec + significand + signum + sin + sinh + snd + sort + span + splitAt + sqrt + subtract + succ + sum + tail + take + takeWhile + tan + tanh + threadToIOResult + toEnum + toInt + toInteger + toLower + toRational + toUpper + truncate + uncurry + undefined + unlines + until + unwords + unzip + unzip3 + userError + words + writeFile + zip + zip3 + zipWith + zipWith3 + + + Bounded + Enum + Eq + Floating + Fractional + Functor + Integral + Ix + Monad + Num + Ord + Read + Real + RealFloat + RealFrac + Show + + + Bool + Char + Double + Either + FilePath + Float + Int + Integer + IO + IOError + Maybe + Ordering + Ratio + Rational + ReadS + ShowS + String + ByteString + + + False + True + Left + Right + Just + Nothing + EQ + LT + GT + + + Applicative + Foldable + Traversable + + + qualified + lib + prefix + as + with + call + pure + unsafe + get + set + foreign + stable + nocode + + + as + qualified + hiding + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/haxe.xml b/kate/part/syntax/data/haxe.xml new file mode 100644 index 00000000..00f6673f --- /dev/null +++ b/kate/part/syntax/data/haxe.xml @@ -0,0 +1,181 @@ + + + + + + + + + break + + case + cast + catch + class + continue + + default + + else + enum + extends + + false + for + function + + if + implements + in + inline + interface + + new + null + + override + + private + public + + return + + static + super + switch + + this + throw + trace + true + try + typedef + + untyped + + var + + while + + + + + package + import + + + + Array + Void + Bool + Int + UInt + Float + Dynamic + String + List + Error + Unknown + Type + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/html-php.xml b/kate/part/syntax/data/html-php.xml new file mode 100644 index 00000000..01029741 --- /dev/null +++ b/kate/part/syntax/data/html-php.xml @@ -0,0 +1,265 @@ + + + +]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/html.xml b/kate/part/syntax/data/html.xml new file mode 100644 index 00000000..9bdd4b77 --- /dev/null +++ b/kate/part/syntax/data/html.xml @@ -0,0 +1,226 @@ + + + +]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/idconsole.xml b/kate/part/syntax/data/idconsole.xml new file mode 100644 index 00000000..fa2a5cff --- /dev/null +++ b/kate/part/syntax/data/idconsole.xml @@ -0,0 +1,2146 @@ + + + + + + + ForceCloseComman + _config_com_baud + _config_com_modem + _vid_default_mode + _vid_default_mode_win + _vid_wait_override + _windowed_mouse + addip + addressbook + adjust_crosshair + advancedupdate + allow_download + allow_download_maps + allow_download_models + allow_download_skins + allow_download_sounds + allskins + appenddemo + autosave + ban + banClient + banUser + banid + baseskin + begin + bf + bgetmod + bindlist + block_switch + bottomcolor + buyNow + buyequip + cache_endgather + cache_flush + cache_mapchange + cache_print + cache_profile + cache_setindex + cache_startgather + cache_usedfile + cancelselect + cd + centerview + changeVectors + changelevel + changelevel2 + changing + chase_active + cinematic + cl_deadbodyfilter + cl_gibfilter + cl_hightrack + cl_hudswap + cl_messages + cl_nodelta + cl_nolerp + cl_nopred + cl_predict_players + cl_rate + cl_sbar + cl_sbar_separator + cl_shownet + cl_sidespeed + cl_solid_players + cl_warncmd + cl_writecfg + clear + clearplayers + clientinfo + clientkick + cmd + cmdline + cmdlist + color + commands + condebug + condump + configstrings + confirm_quit + connect + contimes + coop + crash + credits + cropimages + crosshair + cvar_restart + cvarlist + d_mipcap + d_subdiv16 + deathmatch + delta_clear + delta_stats + demo + demolist + demomap + demos + developer + devmap + dir + disconnect + dlfile + dmoptions + download + drawradar + drop + dropclient + dumpuser + edict + edictcount + edicts + endmovie + entities + envmap + error + escape + exec + exit + fastsprites + fdir + filterban + firstperson + floodprot + floodprotmsg + flush + fly + force_centerview + fov + fraglogfile + freelook + freeze + front + fs_openedList + fs_referencedList + fullinfo + fullserverinfo + game + gameCompleteStatus + gamedir + gamemap + gameversion + getcertificate + gfxinfo + gg + gib + gibload + gibstats + give + gl_affinemodels + gl_clear + gl_colorlights + gl_constretch + gl_cull + gl_dlight_lightmap + gl_dlight_polyblend + gl_dlight_smooth + gl_fb_bmodels + gl_fb_models + gl_finish + gl_fires + gl_flashblend + gl_keeptjunctions + gl_lerp_anim + gl_lightmode + gl_max_size + gl_multitexture + gl_nobind + gl_nocolors + gl_picmip + gl_playermip + gl_polyblend + gl_reportjunctions + gl_sky_clip + gl_skymultipass + gl_smoothmodels + gl_texsort + gl_texturemode + gl_triplebuffer + gl_ztrick + globalservers + god + gun + gun_model + gun_next + gun_prev + gunsmoke + heartbeat + help + hideconsole + hideradar + host_speeds + hostname + hpkextract + hpklist + hpkremove + hpkval + hud_centerid + imagelist + impulse + imt + in_bind + in_paste_buffer + in_restart + in_unbind + info + interp + invdrop + inven + invnext + invnextp + invnextw + invprev + invprevp + invprevw + invuse + joinserver + joy + joy_advancedupdate + joy_enable + joyadvanced + joyadvancedupdat + joyadvancedupdate + joyname + joystick + keys + kick + kill + killserver + lefthand + link + list + listdemo + listen + listid + listip + listmaps + load + loadas8bit + loadgame + loading + loadsky + loadtranslations + loc + localinfo + localservers + log + logaddress + logfile + lookspring + lookstrafe + m_filter + main + map + map_restart + maplist + maps + maxplayers + max_smokepuffs + max_shells + mcache + meminfo + menu + menu_addressbook + menu_credits + menu_help + menu_keys + menu_load + menu_loadgame + menu_main + menu_multiplayer + menu_options + menu_playerconfig + menu_quit + menu_save + menu_savegame + menu_setup + menu_select + menu_singleplayer + menu_startserver + menu_video + menu_dmoptions + menu_game + menu_joinserver + messagemode + messagemode2 + messagemode3 + messagemode4 + model + modelist + modellist + msg + multiplayer + music + name + net_stats + new + next + nextul + nightvision + no_pogo_stick + noaim + noclip + noexit + nomonsters + noskins + nosound + notarget + options + packet + password + path + pausable + pause + paused + ping + pingservers + play + playdemo + playerconfig + players + playvol + pushlatency + pointfile + ppdemostart + pr_boundscheck + precache + prespawn + prev + profile + profilequit + prog + quit + r_drawentities + r_drawflat + r_draworder + r_drawviewmodel + r_dspeeds + r_dynamic + r_fullbright + r_lightmap + r_netgraph + r_netgraph_box + r_norefresh + r_novis + r_numedges + r_numsurfs + r_particles + r_polymodelstats + r_reportsurfout + r_shadows + r_speeds + r_timegraph + r_wateralpha + r_waterripple + r_waterwarp + r_zgraph + rcon + rcon_password + reconnect + record + registered + reload + removedemo + removeid + removeip + rerecord + reset + resetrcon + restart + retry + s_disable_a3d + s_enable_a3d + s_info + s_list + s_stop + samelevel + save + savegame + savetranslations + score + screenshot + screenshotJPEG + sectorlist + sendents + serverinfo + serverprofile + serverrecord + serverstatus + serverstop + setRecommended + setdemoinfo + setenv + setinfo + setmaster + setrom + shaderlist + show_fps + show_time + showdrop + showinfo + showip + showpackets + showpause + showram + showturtle + shutdownserver + singlePlayLink + sizedown + sizeup + skill + skin + skinlist + skins + sky + skyboxlist + slist + slot1 + slot10 + slot2 + slot3 + slot4 + slot5 + slot6 + slot7 + slot8 + slot9 + snap + snapall + snapshot + snapto + snd + snd_noextraupdate + snd_restart + snd_show + soundfade + soundinfo + soundlist + spawn + spdevmap + speak + special + specmode + spectator + spectator_password + spk + spmap + startLimboMode + startSingleplayer + startdemos + startmovie + startserver + stat + stats + status + stop + stopLimboMode + stopdemo + stoprecord + stopsound + stopul + streamingsound + stuffcmd + stuffcmds + sv + sv_allow_log + sv_allow_pings + sv_allow_status + sv_gamedir + sv_highchars + sv_mapcheck + sv_nostep + sv_spectatormaxspeed + sv_spetalk + sv_maplist + swapdemo + sys_cpuid + sys_dead_sleep + sys_extrasleep + sys_nostdout + systeminfo + taginfo + team + teamplay + tell + test + test2 + time + thirdperson + timedemo + timeleft + timerefresh + toggle + togglebrowser + togglechat + toggleconsole + togglemenu + topcolor + touchFile + trackplayer + ui_restart + unalias + unbindall + updatehunkusage + updatescreen + upload + use + user + userinfo + users + v_centerspeed + v_cshift + v_idlescale + version + vid + vid_center + vid_config_x + vid_describecurrentmode + vid_describemode + vid_describemodes + vid_forcemode + vid_fullscreen + vid_fullscreen_mode + vid_minimize + vid_nopageflip + vid_nummodes + vid_restart + vid_stretch_by_2 + vid_testmode + vid_windowed + vid_windowed_mode + vid_front + video + viewframe + viewmodel + viewnext + viewpos + viewprev + vminfo + vmprofile + voice_showbanned + votemap + vstr + wait + watervis + wave + weapon + weapon_knife + weaplast + weapnext + weapprev + windowsr_drawentities + writecfg + writeconfig + writeid + writeip + z_stats + + + + + ah + ActiveAction + _cl_color + _cl_name + _config_com_baud + _config_com_irq + _config_com_modem + _config_com_port + _config_modem_clear + _config_modem_dialtype + _config_modem_hangup + _config_modem_init + _snd_mixahead + _vid_default_mode + _vid_default_mode_win + _vid_wait_override + _windowed_mouse + address + adr + adr0 + adr1 + adr2 + adr3 + adr4 + adr5 + adr6 + adr7 + adr8 + advanced + advaxisr + advaxisu + advaxisv + advaxisx + advaxisy + advaxisz + airaccelerate + allow + allow_download_players + ambient_fade + ambient_level + anglespeedkey + arch + array + arrays + att + auto + autoskins + b + bgmbuffer + bgmvolume + bit + bitdepth + blend + bob + bob_pitch + bob_roll + bob_up + bot_aasoptimize + bot_challenge + bot_debug + bot_developer + bot_enable + bot_fastchat + bot_forceclustering + bot_forcereachability + bot_forcewrite + bot_grapple + bot_groundonly + bot_interbreedbots + bot_interbreedchar + bot_interbreedcycle + bot_interbreedwrite + bot_maxdebugpolys + bot_miniplayers + bot_minplayers + bot_nochat + bot_pause + bot_reachability + bot_reloadcharacters + bot_report + bot_rocketjump + bot_saveroutingcache + bot_testclusters + bot_testichat + bot_testrchat + bot_testsolid + bot_thinktime + bot_visualizejumppads + brighten + brightness + broken + cd + cd_loopcount + cd_looptrack + cd_nocd + cd_plugin + centermove + centerspeed + centertime + cg_autoactivate + cg_autoswitch + cg_blinktime + cg_bloodTime + cg_bobpitch + cg_bobroll + cg_bobup + cg_brassTime + cg_cameraOrbitDelay + cg_clipboardName + cg_coronafardist + cg_coronas + cg_crosshairAlpha + cg_crosshairHealth + cg_crosshairSize + cg_crosshairX + cg_crosshairY + cg_currentSelectedPlayer + cg_currentSelectedPlayerName + cg_cursorHints + cg_cycleAllWeaps + cg_deferPlayers + cg_descriptiveText + cg_draw2D + cg_draw3dIcons + cg_drawAllWeaps + cg_drawAmmoWarning + cg_drawAttacker + cg_drawCompass + cg_drawCrosshair + cg_drawCrosshairNames + cg_drawCrosshairPickups + cg_drawFPGun + cg_drawFPS + cg_drawFrags + cg_drawGun + cg_drawIcons + cg_drawNotifyText + cg_drawRewards + cg_drawSnapshot + cg_drawSpreadScale + cg_drawStatus + cg_drawTeamOverlay + cg_drawTimer + cg_emptyswitch + cg_fov + cg_forcemodel + cg_gibs + cg_hudAlpha + cg_hudFiles + cg_lagometer + cg_marks + cg_marktime + cg_noplayeranims + cg_nopredict + cg_noTaunt + cg_noVoiceChats + cg_noVoiceText + cg_particleDist + cg_particleLOD + cg_popupLimboMenu + cg_predictItems + cg_quickMessageAlt + cg_railTrailTime + cg_recoilPitch + cg_reticleBrightness + cg_reticleType + cg_runpitch + cg_runroll + cg_scorePlums + cg_selectedPlayer + cg_selectedPlayerName + cg_shadows + cg_showblood + cg_simpleItems + cg_skybox + cg_stereoSeparation + cg_teamChatHeight + cg_teamChatTime + cg_teamChatsOnly + cg_thirdperson + cg_thirdpersonrange + cg_thirdPersonAngle + cg_useWeapsForZoom + cg_uselessNostalgia + cg_viewsize + cg_voiceSpriteTime + cg_weaponCycleDelay + cg_wolfparticles + cg_zoomDefaultBinoc + cg_zoomDefaultFG + cg_zoomDefaultSniper + cg_zoomDefaultSnooper + cg_zoomStepBinoc + cg_zoomStepFG + cg_zoomStepSnooper + cg_zoomfov + cg_zoomstepsniper + chase_active + chase_back + chase_right + chase_up + cheats + cl + cl_allowDownload + cl_anglespeedkey + cl_anonymous + cl_autoexec + cl_autoskins + cl_avidemo + cl_backspeed + cl_blend + cl_bob + cl_bobcycle + cl_bobup + cl_bypassMouseInput + cl_cacheGathering + cl_camera_maxpitch + cl_camera_maxyaw + cl_chasecam + cl_chatmode + cl_conXOffset + cl_crossx + cl_crossy + cl_cshift_bonus + cl_cshift_content + cl_cshift_damage + cl_cshift_powerup + cl_debugMove + cl_debugTranslation + cl_demospeed + cl_entities + cl_footsteps + cl_forceavidemo + cl_forwardspeed + cl_freelook + cl_freezeDemo + cl_gun + cl_hidefrags + cl_hightrack + cl_hudswap + cl_language + cl_lights + cl_maxPing + cl_maxfps + cl_maxpackets + cl_motd + cl_motdString + cl_mouseAccel + cl_movespeedkey + cl_nodelta + cl_nofake + cl_nolerp + cl_nopred + cl_noprint + cl_noskins + cl_packetdup + cl_parsesay + cl_particles + cl_paused + cl_pitchspeed + cl_predict + cl_predict_players + cl_predict_players2 + cl_observercrosshair + cl_quakerc + cl_rollangle + cl_rollspeed + cl_run + cl_running + cl_serverStatusResendTime + cl_showfps + cl_showSend + cl_showServerCommands + cl_showTimeDelta + cl_showmiss + cl_showmouserate + cl_shownet + cl_shownuments + cl_sidespeed + cl_stats + cl_stereo + cl_stereo_separation + cl_testblend + cl_testentities + cl_testlights + cl_testparticles + cl_timeNudge + cl_timeout + cl_upspeed + cl_verstring + cl_visibleClients + cl_vwep + cl_waitForFire + cl_wavefilerecord + cl_yawspeed + clear + clearcolor + clientport + cm_playerCurveClip + cmd_highchars + cmd_warncmd + cmdlist + color + color1 + color2 + com_blood + com_buildScript + com_cameraMode + com_dropsim + com_hunkMegs + com_hunkused + com_introplayed + com_maxfps + com_recommendedSet + com_showtrace + com_soundMegs + com_speeds + com_zoneMegs + compiled + con_debug + con_notifytime + con_restricted + conspeed + contrast + coop + crosshair + crosshaircolor + cull + d_mipcap + d_mipscale + deathmatch + debug_protocol + debuggraph + dedicated + devdll + developer + dlabs + dmflags + dm + down + download + drawall + drawbuffer + drawentities + drawflat + draworder + drawworld + driver + dspeeds + dynamic + easter_eggs + edgefriction + empty + enforcetime + entities + entlatency + ext + filter + filterban + finish + fixedtime + flashblend + flood + flood_msgs + flood_persecond + flood_waitdelay + flushmap + footsteps + forward + forwardsensitivity + forwardspeed + forwardthreshold + fov + fraglimit + freelook + fs_basegame + fs_basepath + fs_cdpath + fs_copyfiles + fs_debug + fs_game + fs_globalcfg + fs_homepath + fs_pluginpath + fs_restrict + fs_sharepath + fs_skinbase + fs_usercfg + fs_userpath + fullbright + fullscreen + g_allowvote + g_altStopwatchMode + g_arenasFile + g_blueTeam + g_botsFile + g_complaintlimit + g_currentRound + g_friendlyFire + g_gameskill + g_gametype + g_maxlives + g_minGameClients + g_missionStats + g_nextTimeLimit + g_noTeamSwitching + g_redTeam + g_select_empty + g_spAwards + g_spScores1 + g_spScores2 + g_spScores3 + g_spScores4 + g_spScores5 + g_spSkill + g_spVideos + g_userAlliedRespawnTime + g_userAxisRespawnTime + g_userTimeLimit + game + gamecfg + gamedate + gamedir + gamename + gamestate + gamma + gender + gender_auto + gl_3dlabs_broken + gl_allow_software + gl_bitdepth + gl_clear + gl_conalpha + gl_conspin + gl_cshiftpercent + gl_cull + gl_drawbuffer + gl_driver + gl_dynamic + gl_ext_compiled_vertex_array + gl_ext_multitexture + gl_ext_palettedtexture + gl_ext_pointparameters + gl_ext_swapinterval + gl_finish + gl_flashblend + gl_keeptjunctions + gl_lightmap + gl_lightmap_align + gl_lightmap_subimage + gl_lockpvs + gl_log + gl_max_size + gl_mesh_cache + gl_mode + gl_modulate + gl_monolightmap + gl_nobind + gl_nocolors + gl_nosubimage + gl_occlusion + gl_particle_att_a + gl_particle_att_b + gl_particle_att_c + gl_particle_max_size + gl_particle_min_size + gl_particle_mip + gl_particle_size + gl_picmip + gl_playermip + gl_polyblend + gl_reporttjunctions + gl_round_down + gl_saturatelighting + gl_screenshot_byte_swap + gl_shadows + gl_showtris + gl_sky_debug + gl_sky_divide + gl_skymip + gl_smoothmodels + gl_subdivide_size + gl_swapinterval + gl_texsort + gl_texturealphamode + gl_texturemode + gl_texturesolidmode + gl_triplebuffer + gl_vertex_arrays + gl_ztrick + graphheight + graphscale + graphshift + gravity + gun + gun_x + gun_y + gun_z + hand + handicap + head + headModel + headmodel + host + host_framerate + host_speeds + hostname + hostport + hud_fastswitch + in + in_amp + in_bind_imt + in_debugjoystick + in_dga + in_dga_mouseaccel + in_dgamouse + in_grab + in_joystick + in_midi + in_mouse + in_mouse_amp + in_mouse_filter + in_mouse_pre_amp + in_pre_amp + initsound + intensity + ip + ip_clientport + ip_hostport + ipx + ipx_clientport + ipx_hostport + journal + joy + joy_advanced + joy_advaxisr + joy_advaxisu + joy_advaxisv + joy_advaxisx + joy_advaxisy + joy_advaxisz + joy_amp + joy_device + joy_forwardsensitivity + joy_forwardthreshold + joy_name + joy_pitchsensitivity + joy_pitchthreshold + joy_pre_amp + joy_sensitivity + joy_sidesensitivity + joy_sidethreshold + joy_threshold + joy_upsensitivity + joy_upthreshold + joy_yawsensitivity + joy_yawthreshold + joyadvanced + joyadvaxisr + joyadvaxisu + joyadvaxisv + joyadvaxisx + joyadvaxisy + joyadvaxisz + joyaxis1 + joyaxis2 + joyaxis3 + joyaxis4 + joyaxis5 + joyaxis6 + joyaxis7 + joyaxis8 + joyforwardsensitivity + joyforwardthreshold + joyname + joypitchsensitivity + joypitchthreshold + joysidesensitivity + joysidethreshold + joystick + joywwhack1 + joywwhack2 + joyyawsensitivity + joyyawthreshold + khz + lcd_x + lcd_yaw + lerpmodels + lightmap + lights + limit + listen + loadas + loadas8bit + localid + lockpvs + log + log_stats + logfile + lookspring + lookstrafe + loopcount + looptrack + m_filter + m_forward + m_pitch + m_side + m_yaw + mapname + maps + max + maxclients + maxedges + maxentities + maxfps + maxplayers + maxspectators + maxsurfs + maxvelocity + min + mipcap + mipscale + mixahead + mode + model + models + modex + modulate + monolightmap + mouse + mp_currentPlayerType + mp_currentTeam + mp_playerType + mp_team + mp_weapon + mp_autokick + mp_autoteambalance + mp_c4timer + mp_flashlight + mp_footsteps + mp_forcechasecam + mp_freezetime + mp_friendlyfire + mp_hostagepenalty + mp_limitteams + mp_logmessages + mp_mapvoteration + mp_roundtime + mp_timelimit + mp_tkpunish + msg + msgs + multitexture + name + net_graph + net_ip + net_messagetimeout + net_noudp + net_port + net_qport + net_restart + netdosexpire + netdosvalues + netgraph + nextdemo + nextmap + nextserver + noalttab + nobind + nocd + nocull + nodelta + noexit + nomonsters + norefresh + noreload + noskins + nosound + nosubimage + novis + palettedtexture + particle + particles + password + pausable + persecond + picmip + pitch + pitchsensitivity + pitchspeed + pitchthreshold + playermip + players + pointparameters + polyblend + polymodelstats + port + pr_checkextensions + pr_deadbeef_ents + pr_deadbeef_locals + pr_debug + pr_source_path + precache + predict + primary + printspeed + protocol + public + pushlatency + qport + r_aliastransadj + r_aliastransbase + r_allowExtensions + r_allowSoftwareGL + r_ambient + r_ambientScale + r_bonesDebug + r_cache + r_cacheModels + r_cacheShaders + r_clear + r_clearcolor + r_colorMipLevels + r_colorbits + r_compressModels + r_customaspect + r_customheight + r_customwidth + r_debugSort + r_debugSurface + r_debuglight + r_depthbits + r_detailtextures + r_directedScale + r_displayRefresh + r_dlightBacks + r_dlight_lightmap + r_dlight_max + r_drawBuffer + r_drawSun + r_drawentities + r_drawexplosions + r_drawflat + r_draworder + r_drawviewmodel + r_drawworld + r_dspeeds + r_dynamic + r_dynamiclight + r_explosionclip + r_exportCompressedModels + r_ext_compiled_vertex_array + r_ext_compress_textures + r_ext_compressed_textures + r_ext_gamma_control + r_ext_multitexture + r_ext_texture_env_add + r_facePlaneCull + r_fastsky + r_finish + r_firecolor + r_flareFade + r_flareSize + r_flares + r_fullbright + r_fullscreen + r_gamma + r_glDriver + r_glIgnoreWicked3D + r_graphheight + r_highQualit + r_highQualityVideo + r_ignore + r_ignoreFastPath + r_ignoreGLErrors + r_ignorehwgamma + r_inGameVideo + r_intensity + r_lastValidRenderer + r_lerpmodels + r_lightmap + r_lightmap_components + r_lockpvs + r_lodCurveError + r_lodbias + r_lodscale + r_logFile + r_lowMemTextureSize + r_lowMemTextureThreshold + r_mapOverBrightBits + r_maxedges + r_maxpolys + r_maxpolyverts + r_maxsurfs + r_measureOverdraw + r_mirroralpha + r_mode + r_netgraph + r_netgraph_alpha + r_nobind + r_nocull + r_nocurves + r_noportals + r_norefresh + r_novis + r_numedges + r_numsurfs + r_offsetfactor + r_offsetunits + r_overBrightBits + r_particles_max + r_particles_style + r_picmip + r_picmip2 + r_polymodelstats + r_portalOnly + r_preloadTextures + r_previousglDriver + r_primitives + r_printShaders + r_railCoreWidth + r_railSegmentLength + r_railWidth + r_reportedgeout + r_reportsurfout + r_rmse + r_roundImagesDown + r_saveFontData + r_shadows + r_showImages + r_showSmp + r_showcluster + r_shownormals + r_showsky + r_showtris + r_simpleMipMaps + r_singleShader + r_skipBackEnd + r_skyname + r_smp + r_speeds + r_stencilbits + r_stereo + r_subdivisions + r_swapInterval + r_textureMode + r_texturebits + r_timegraph + r_uiFullScreen + r_verbose + r_vertexLight + r_wateralpha + r_waterwarp + r_wolffog + r_zfar + r_znear + rate + rcon + rconAddress + rconPassword + rcon_address + rcon_password + reconnect + ref + registered + reportedgeout + reportsurfout + roll + rollangle + rollspeed + round + run + run_pitch + run_roll + s_compression + s_defaultsound + s_doppler + s_initsound + s_khz + s_loadas8bit + s_mixPreStep + s_mixahead + s_musicvolume + s_mute + s_nocompressed + s_usingA3D + s_primary + s_separation + s_show + s_testsound + s_volume + s_wavonly + samelevel + saturatelighting + saved1 + saved2 + saved3 + saved4 + savedgamecfg + scr + scr_centertime + scr_consize + scr_conspeed + scr_drawall + scr_ofsx + scr_ofsy + scr_ofsz + scr_printspeed + scr_showpause + scr_showturtle + scratch1 + scratch2 + scratch3 + scratch4 + screenshot + select + sensitivity + separation + server1 + server10 + server11 + server12 + server13 + server14 + server15 + server16 + server2 + server3 + server4 + server5 + server6 + server7 + server8 + server9 + serverprofile + sex + shadows + show + showclamp + showdrop + showmiss + shownet + showpackets + showpause + showram + showtrace + showtris + showturtle + side + sidesensitivity + sidespeed + sidethreshold + size + skill + skin + skymip + snaps + snd_bits + snd_device + snd_interp + snd_loadas8bit + snd_mixahead + snd_noextraupdate + snd_oss_mmaped + snd_output + snd_phasesep + snd_rate + snd_render + snd_show + snd_stereo + snd_volumesep + sndbits + sndchannels + snddevice + sndspeed + software + sounds + spectator + spectator_password + speeds + stats + stereo + stipplealpha + surfcacheoverride + sv + sv_accelerate + sv_aim + sv_airaccelerate + sv_allowAnonymous + sv_allowDownload + sv_cheats + sv_enforcetime + sv_floodProtect + sv_fps + sv_friction + sv_gravity + sv_hostname + sv_idealpitchscale + sv_keywords + sv_killserver + sv_mapChecksum + sv_master1 + sv_master2 + sv_master3 + sv_master4 + sv_master5 + sv_maxPing + sv_maxRate + sv_maxclients + sv_maxrate + sv_maxspeed + sv_maxtic + sv_maxvelocity + sv_minPing + sv_minqfversion + sv_mintic + sv_netdosprotect + sv_noreload + sv_nostep + sv_onlyVisibleClients + sv_padPackets + sv_pakNames + sv_paks + sv_paused + sv_phs + sv_privateClients + sv_privatePassword + sv_progs + sv_pure + sv_reconnect_limit + sv_reconnectlimit + sv_referencedPakNames + sv_referencedPaks + sv_restartround + sv_rollangle + sv_rollspeed + sv_running + sv_serverid + sv_showAverageBPS + sv_showloss + sv_spectalk + sv_stopspeed + sv_timefmt + sv_timekick + sv_timekick_fuzz + sv_timekick_interval + sv_timeout + sv_timestamps + sv_wateraccelerate + sv_waterfriction + sv_zombietime + sw + sw_allow_modex + sw_clearcolor + sw_drawflat + sw_draworder + sw_maxedges + sw_maxsurfs + sw_mipcap + sw_mipscale + sw_mode + sw_polymodelstats + sw_reportedgeout + sw_reportsurfout + sw_stipplealpha + sw_surfcacheoverride + sw_waterwarp + swapinterval + sys_cpustring + sys_nostdout + sys_sleep + sys_ticrate + team + team_headmodel + team_model + teamplay + teamtask + temp1 + testblend + testentities + testlights + testparticles + testsound + texturealphamode + texturemode + texturesolidmode + timedemo + timegraph + timelimit + timeout + timescale + topcolor + triplebuffer + ttycon + ui_Q3Model + ui_actualNetGametype + ui_bigFont + ui_browserGameType + ui_browserMaster + ui_browserShowEmpty + ui_browserShowFriendlyFire + ui_browserShowFull + ui_browserShowMaxlives + ui_browserShowTourney + ui_browserSortKey + ui_cdkeychecked + ui_class + ui_cmd + ui_ctf_capturelimit + ui_ctf_friendly + ui_ctf_timelimit + ui_currentMap + ui_currentNetMap + ui_dedicated + ui_ffa_fraglimit + ui_ffa_timelimit + ui_gametype + ui_glCustom + ui_isSpectator + ui_joinGametype + ui_limboMode + ui_limboObjective + ui_limboOptions + ui_limboPrevOptions + ui_mapIndex + ui_master + ui_menuFiles + ui_mousePitch + ui_netGametype + ui_netSource + ui_notebookCurrentPage + ui_objective + ui_prevClass + ui_prevTeam + ui_prevWeapon + ui_serverStatusTimeOut + ui_singlePlayerActive + ui_smallFont + ui_spSelection + ui_team + ui_teamArenaFirstRun + ui_team_fraglimit + ui_team_friendly + ui_team_timelimit + ui_tourney_fraglimit + ui_tourney_timelimit + ui_userAlliedRespawnTime + ui_userAxisRespawnTime + ui_userTimeLimit + ui_weapon + up + upsensitivity + upspeed + upthreshold + username + v_centermove + v_centerspeed + v_idlescale + v_ipitch_cycle + v_ipitch_level + v_iroll_cycle + v_iroll_level + v_iuaw_cycle + v_iyaw_cycle + v_iyaw_level + v_kickpitch + v_kickroll + v_kicktime + version + vertex + vid + vid_config_x + vid_config_y + vid_fullscreen + vid_fullscreen_mode + vid_gamma + vid_height + vid_mode + vid_nopageflip + vid_ref + vid_system_gamma + vid_use8bit + vid_wait + vid_width + vid_window_x + vid_window_y + vid_windowed_mode + vid_xpos + vid_ypos + viewlog + viewsize + vm_cgame + vm_game + vm_ui + volume + vwep + waitdelay + waterwarp + wavonly + win + win_noalttab + win_hinstance + win_wndproc + xpos + yaw + yawsensitivity + yawspeed + yawthreshold + ypos + zombietime + ztrick + + + + + +attack + +ttack2 + +alt1 + +activate + +back + +break + +button0 + +button1 + +button10 + +button11 + +button12 + +button13 + +button14 + +button2 + +button3 + +button4 + +button5 + +button6 + +button7 + +button8 + +button9 + +camdistance + +camin + +cammousemove + +camout + +campitchdown + +campitchup + +camyawleft + +camyawright + +commandmenu + +dropweapon + +duck + +forward + +graph + +jlook + +jump + +kick + +klook + +leanleft + +leanright + +left + +lookdown + +lookup + +mlook + +movedown + +moveleft + +moveright + +moveup + +nvgadjust + +quickgren + +reload + +right + +salute + +score + +showscores + +speed + +sprint + +strafe + +use + +useitem + +voicerecord + +wbutton7 + +zoom + -activate + -alt1 + -attack + -attack2 + -back + -break + -button0 + -button1 + -button10 + -button11 + -button12 + -button13 + -button14 + -button2 + -button3 + -button4 + -button5 + -button6 + -button7 + -button8 + -button9 + -camdistance + -camin + -cammousemove + -camout + -campitchdown + -campitchup + -camyawleft + -camyawright + -commandmenu + -dropweapon + -duck + -forward + -graph + -jlook + -jump + -kick + -klook + -leanleft + -leanright + -left + -lookdown + -lookup + -mlook + -movedown + -moveleft + -moveright + -moveup + -nvgadjust + -quickgren + -reload + -right + -salute + -score + -showscores + -speed + -sprint + -strafe + -use + -useitem + -voicerecord + -wbutton7 + -zoom + + + + + * + [ + ] + \ + / + ' + = + - + + + , + . + ` + ~ + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 0 + a + b + c + d + e + f + g + h + i + j + k + l + m + n + o + p + q + r + s + t + u + v + x + w + y + z + ALT + AUX1 + AUX10 + AUX11 + AUX12 + AUX13 + AUX14 + AUX15 + AUX16 + AUX17 + AUX18 + AUX2 + AUX20 + AUX21 + AUX22 + AUX23 + AUX24 + AUX25 + AUX26 + AUX27 + AUX28 + AUX29 + AUX3 + AUX30 + AUX31 + AUX32 + AUX4 + AUX5 + AUX6 + AUX7 + AUX8 + AUX9 + BACKSPACE + CTRL + DEL + DOWNARROW + END + ENTER + ESCAPE + F1 + F10 + F11 + F12 + F2 + F3 + F4 + F5 + F6 + F7 + F8 + F9 + HOME + INS + JOY1 + JOY2 + JOY3 + JOY4 + KP_SLASH + KP_5 + KP_UPARROW + KP_LEFTARROW + KP_RIGHTARROW + KP_DOWNARROW + KP_HOME + KP_END + KP_PGUP + KP_PGDN + KP_INS + KP_DEL + LEFTARROW + MOUSE1 + MOUSE2 + MOUSE3 + MWHEELDOWN + MWHEELUP + PAUSE + PGDN + PGUP + RIGHTARROW + SEMICOLON + CAPSLOCK + SHIFT + SPACE + TAB + UPARROW + + + + ; + $ + + + bind + unbind + + + set + seta + setu + sets + + + echo + say_team + say + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/idl.xml b/kate/part/syntax/data/idl.xml new file mode 100644 index 00000000..d75f6713 --- /dev/null +++ b/kate/part/syntax/data/idl.xml @@ -0,0 +1,116 @@ + + + + + + any + attribute + case + const + context + default + enum + exception + FALSE + fixed + public + in + inout + interface + module + Object + oneway + out + raises + readonly + sequence + struct + switch + TRUE + typedef + unsigned + union + + + boolean + char + double + float + long + octet + short + string + void + wchar + wstring + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/ilerpg.xml b/kate/part/syntax/data/ilerpg.xml new file mode 100644 index 00000000..a7060f60 --- /dev/null +++ b/kate/part/syntax/data/ilerpg.xml @@ -0,0 +1,725 @@ + + + + + + + EXTPROC + EXTPGM + OPDESC + DATFMT + DIM + LIKEDS + LIKEREC + LIKE + PROCPTR + TIMFMT + VARYING + ASCEND + CONST + NOOPT + OPTIONS + VALUE + QUALIFIED + INZ + BASED + + + DATFMT + DIM + LIKE + LIKEDS + LIKEREC + PROCPTR + TIMFMT + + + FREE + END-FREE + TITLE + EJECT + SPACE + COPY + INCLUDE + DEFINE + UNDEFINE + IF + ELSE + EXEC + END-EXEC + ELSEIF + ENDIF + EOF + + + NOT + DEFINED + + + DEFINED + + + ABS + ADDR + ALLOC + BITAND + BITNOT + BITOR + BITXOR + CHAR + CHECK + CHECKR + DATE + DAYS + DEC + DECH + DECPOS + DIFF + DIV + EDITC + EDITFLT + EDITW + ELEM + EOF + EQUAL + ERROR + FIELDS + FLOAT + FOUND + GRAPH + HOURS + INT + INTH + KDS + LEN + LOOKUP + LOOKUPLT + LOOKUPLE + LOOKUPGT + LOOKUPGE + MINUTES + MONTHS + MSECONDS + NULLIND + OCCUR + OPEN + PADDR + PARMS + REALLOC + REM + REPLACE + SCAN + SECONDS + SHTDN + SIZE + SQRT + STATUS + STR + SUBARR + SUBDT + SUBST + THIS + TIME + TIMESTAMP + TLOOKUP + TLOOKUPLT + TLOOKUPLE + TLOOKUPGT + TLOOKUPGE + TRIM + TRIML + TRIMR + UCS2 + UNS + UNSH + XFOOT + XLATE + YEARS + + + IF + + + DOW + DOU + FOR + + + WHEN + EVAL + + + EVALR + + + RETURN + + + ON-ERROR + + + DO + IN + OR + + + ACQ + ADD + AND + CAB + CAS + CAT + DIV + DOU + END + MVR + OUT + REL + SUB + TAG + + + CALL + COMP + DUMP + ELSE + EXSR + FEOD + GOTO + IFGT + IFLT + IFEQ + IFNE + IFGE + IFLE + ITER + KFLD + MOVE + MULT + NEXT + OPEN + ORGT + ORLT + OREQ + ORNE + ORGE + ORLE + PARM + POST + READ + SCAN + SQRT + TEST + TIME + + + ALLOC + ANDGT + ANDLT + ANDEQ + ANDNE + ANDGE + ANDLE + BEGSR + BITON + CABGT + CABLT + CABEQ + CABNE + CABGE + CABLE + CALLB + CALLP + CASGT + CASLT + CASEQ + CASNE + CASGE + CASLE + CHAIN + CHECK + CLEAR + CLOSE + CHECK + CLEAR + CLOSE + DOWGT + DOWLT + DOWEQ + DOWNE + DOWGE + DOWLE + DOUGT + DOULT + DOUEQ + DOUNE + DOUGE + DOULE + DSPLY + ENDCS + ENDDO + ENDIF + ENDSL + ENDSR + EXFMT + FORCE + KLIST + LEAVE + MHHZO + MHLZO + MLHZO + MLLZO + MOVEA + MOVEL + OCCUR + OTHER + PLIST + READC + READE + READP + RESET + ROLBK + SETGT + SETLL + SETON + SHTDN + SORTA + SUBST + TESTB + TESTN + TESTZ + WRITE + XFOOT + XLATE + + + ADDDUR + BITOFF + CHECKR + COMMIT + DEFINE + DELETE + EXCEPT + EXTRCT + LOOKUP + READPE + RETURN + SELECT + SETOFF + SUBDUR + UNLOCK + UPDATE + WHENGT + WHENLT + WHENEQ + WHENNR + WHENGE + WHENLE + + + DEALLOC + REALLOC + + + ACQ + BEGSR + CALLP + CHAIN + CLEAR + CLOSE + COMMIT + DEALLOC + DELETE + DOU + DOW + DSPLY + DUMP + ELSE + ELSEIF + ENDDO + ENDFOR + ENDIF + ENDMON + ENDSL + ENDSR + EVAL + EVALR + EXCEPT + EXFMT + EXSR + FEOD + FOR + FORCE + IF + IN + ITER + LEAVE + LEAVESR + MONITOR + NEXT + ON-ERROR + OPEN + OTHER + OUT + POST + READ + READC + READE + READP + READPE + REL + RESET + RETURN + ROLBK + SELECT + SETGT + SETLL + SORTA + TEST + UNLOCK + UPDATE + WHEN + WRITE + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/inform.xml b/kate/part/syntax/data/inform.xml new file mode 100644 index 00000000..91468dcd --- /dev/null +++ b/kate/part/syntax/data/inform.xml @@ -0,0 +1,405 @@ + + + + + + + + if + for + else + box + break + continue + do + until + font + give + inversion + jump + on + to + move + new_line + objectloop + print + print_ret + quit + read + remove + restore + return + rtrue + rfalse + save + spaces + spring + style + switch + + + + metaclass + parent + child + children + Achieved + AddToScope + allowpushdir + CDefArt + ChangeDefault + DefArt + DoMenu + EnglishNumber + HasLightSource + InDefArt + Locale + LoopOverScope + NextWord + NextWordStopped + NounDomain + ObjectIsUntouchable + OffersLight + PlaceInScope + PlayerTo + PrintShortName + ScopeWithin + SetTime + StartDaemon + StartTimer + StopDaemon + StopTimer + TestScope + TryNumber + UnsignedCompare + WordAddress + WordLenght + WriteListFrom + YesOrNo + ZRegion + + + Pronouns + Quit + Restore + Save + Verify + Restart + ScriptOn + ScriptOff + NotifyOn + NotifyOff + Places + Objects + Score + FullScore + Version + LMode1 + LMode2 + LMode3 + Inv + + + Inv + InvTall + InvWide + Take + Drop + Empty + Enter + Exit + GetOff + Go + GoIn + Look + Examine + Search + Give + Show + Unlock + Lock + SwitchOn + SwitchOff + Open + Close + Disrobe + Wear + Eat + + LetGo + Receive + Insert + PutOn + Transfer + Empty + EmptyT + GetOff + GoIn + Listen + Taste + Touch + + Pull + Push + Wave + Turn + PushDir + ThrowAt + ThrownAt + JumpOn + Drink + Attack + Tie + Fill + Swing + Blow + Rub + Set + SetTo + Buy + Climb + Squeeze + Climb + Burn + Cut + Dig + + Consult + Tell + Answer + Ask + AskFor + Kiss + + Sleep + Sing + WaveHands + Swim + Sorry + Sing + Strong + Mild + Smell + Pray + Jump + Think + VagueGo + Yes + No + Sing + + + + String + Routine + bold + roman + underline + fixed + nothing + true + false + on + off + sender + self + location + score + action + actor + noun + second + the_time + consult_from + consult_words + wn + actors_location + buffer + player + + + + Ifdef + Ifndef + Iftrue + Iffalse + Ifnot + Endif + End + Abbreviate + Array + Attribute + Constant + Default + Extend + Global + Ifnot + Iftrue + Iffalse + Import + Include + Link + Lowstring + Message + Property + Release + Replace + Serial + Switches + Statusline + score + System_file + Verb + + + + #ifdef + #else + #ifndef + #endif + + + + has + hasn't + in + notin + provides + ofclass + or + + + + with + private + has + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/ini.xml b/kate/part/syntax/data/ini.xml new file mode 100644 index 00000000..3cdcd1f2 --- /dev/null +++ b/kate/part/syntax/data/ini.xml @@ -0,0 +1,80 @@ + + + + + + +On +Off +Default +Defaults +Localhost +Null +True +False +Yes +No +Normal + + +E_ALL +E_ERROR +E_WARNING +E_PARSE +E_NOTICE +E_STRICT +E_CORE_ERROR +E_CORE_WARNING +E_COMPILE_ERROR +E_COMPILE_WARNING +E_USER_ERROR +E_USER_WARNING +E_USER_NOTICE + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/isocpp.xml b/kate/part/syntax/data/isocpp.xml new file mode 100644 index 00000000..ede93e7b --- /dev/null +++ b/kate/part/syntax/data/isocpp.xml @@ -0,0 +1,486 @@ + + + + + + + + + +]> + + + + + alignof + alignas + asm + auto + break + case + catch + class + constexpr + const_cast + continue + decltype + default + delete + do + dynamic_cast + else + enum + explicit + export + false + final + friend + for + goto + if + inline + namespace + new + noexcept + nullptr + operator + override + private + protected + public + reinterpret_cast + return + sizeof + static_assert + static_cast + struct + switch + template + this + throw + true + try + typedef + typeid + typename + union + using + virtual + while + + and + and_eq + bitand + bitor + compl + not + not_eq + or + or_eq + xor + xor_eq + + + + template + + + + + + noreturn + carries_dependency + + deprecated + + + + bool + char + char16_t + char32_t + double + float + int + long + short + signed + unsigned + void + int8_t + int16_t + int32_t + int64_t + uint8_t + uint16_t + uint32_t + uint64_t + wchar_t + + + const + extern + mutable + register + static + thread_local + volatile + + + __FILE__ + __LINE__ + __DATE__ + __TIME__ + __STDC__ + __STDC_VERSION__ + __STDC_HOSTED__ + __STDC_ISO_10646__ + __STDC_MB_MIGHT_NEQ_WC__ + __cplusplus + __func__ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/jam.xml b/kate/part/syntax/data/jam.xml new file mode 100644 index 00000000..0b0ebf62 --- /dev/null +++ b/kate/part/syntax/data/jam.xml @@ -0,0 +1,347 @@ + + + + + + + + actions + break + continue + for + in + if + else + include + local + on + return + rule + switch + case + while + + + local + + + existing + ignore + piecemeal + quietly + together + updated + + + bind + + : + ; + [ + ] + ( + ) + { + } + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/kate/part/syntax/data/java.xml b/kate/part/syntax/data/java.xml new file mode 100644 index 00000000..1039ba77 --- /dev/null +++ b/kate/part/syntax/data/java.xml @@ -0,0 +1,3858 @@ + + + + + + ACTIVE + ACTIVITY_COMPLETED + ACTIVITY_REQUIRED + ARG_IN + ARG_INOUT + ARG_OUT + AWTError + AWTEvent + AWTEventListener + AWTEventListenerProxy + AWTEventMulticaster + AWTException + AWTKeyStroke + AWTPermission + AbstractAction + AbstractBorder + AbstractButton + AbstractCellEditor + AbstractCollection + AbstractColorChooserPanel + AbstractDocument + AbstractDocument.AttributeContext + AbstractDocument.Content + AbstractDocument.ElementEdit + AbstractExecutorService + AbstractInterruptibleChannel + AbstractLayoutCache + AbstractLayoutCache.NodeDimensions + AbstractList + AbstractListModel + AbstractMap + AbstractMethodError + AbstractPreferences + AbstractQueue + AbstractQueuedSynchronizer + AbstractSelectableChannel + AbstractSelectionKey + AbstractSelector + AbstractSequentialList + AbstractSet + AbstractSpinnerModel + AbstractTableModel + AbstractUndoableEdit + AbstractWriter + AccessControlContext + AccessControlException + AccessController + AccessException + Accessible + AccessibleAction + AccessibleAttributeSequence + AccessibleBundle + AccessibleComponent + AccessibleContext + AccessibleEditableText + AccessibleExtendedComponent + AccessibleExtendedTable + AccessibleExtendedText + AccessibleHyperlink + AccessibleHypertext + AccessibleIcon + AccessibleKeyBinding + AccessibleObject + AccessibleRelation + AccessibleRelationSet + AccessibleResourceBundle + AccessibleRole + AccessibleSelection + AccessibleState + AccessibleStateSet + AccessibleStreamable + AccessibleTable + AccessibleTableModelChange + AccessibleText + AccessibleTextSequence + AccessibleValue + AccountException + AccountExpiredException + AccountLockedException + AccountNotFoundException + Acl + AclEntry + AclNotFoundException + Action + ActionEvent + ActionListener + ActionMap + ActionMapUIResource + Activatable + ActivateFailedException + ActivationDesc + ActivationException + ActivationGroup + ActivationGroupDesc + ActivationGroupDesc.CommandEnvironment + ActivationGroupID + ActivationGroup_Stub + ActivationID + ActivationInstantiator + ActivationMonitor + ActivationSystem + Activator + ActiveEvent + ActivityCompletedException + ActivityRequiredException + AdapterActivator + AdapterActivatorOperations + AdapterAlreadyExists + AdapterAlreadyExistsHelper + AdapterInactive + AdapterInactiveHelper + AdapterManagerIdHelper + AdapterNameHelper + AdapterNonExistent + AdapterNonExistentHelper + AdapterStateHelper + AddressHelper + Adjustable + AdjustmentEvent + AdjustmentListener + Adler32 + AffineTransform + AffineTransformOp + AlgorithmParameterGenerator + AlgorithmParameterGeneratorSpi + AlgorithmParameterSpec + AlgorithmParameters + AlgorithmParametersSpi + AllPermission + AlphaComposite + AlreadyBound + AlreadyBoundException + AlreadyBoundHelper + AlreadyBoundHolder + AlreadyConnectedException + AncestorEvent + AncestorListener + AnnotatedElement + Annotation + AnnotationFormatError + AnnotationTypeMismatchException + Any + AnyHolder + AnySeqHelper + AnySeqHelper + AnySeqHolder + AppConfigurationEntry + AppConfigurationEntry.LoginModuleControlFlag + Appendable + Applet + AppletContext + AppletInitializer + AppletStub + ApplicationException + Arc2D + Arc2D.Double + Arc2D.Float + Area + AreaAveragingScaleFilter + ArithmeticException + Array + Array + ArrayBlockingQueue + ArrayIndexOutOfBoundsException + ArrayList + ArrayStoreException + ArrayType + Arrays + AssertionError + AsyncBoxView + AsynchronousCloseException + AtomicBoolean + AtomicInteger + AtomicIntegerArray + AtomicIntegerFieldUpdater + AtomicLong + AtomicLongArray + AtomicLongFieldUpdater + AtomicMarkableReference + AtomicReference + AtomicReferenceArray + AtomicReferenceFieldUpdater + AtomicStampedReference + Attr + Attribute + Attribute + Attribute + AttributeChangeNotification + AttributeChangeNotificationFilter + AttributeException + AttributeInUseException + AttributeList + AttributeList + AttributeList + AttributeListImpl + AttributeModificationException + AttributeNotFoundException + AttributeSet + AttributeSet + AttributeSet.CharacterAttribute + AttributeSet.ColorAttribute + AttributeSet.FontAttribute + AttributeSet.ParagraphAttribute + AttributeSetUtilities + AttributeValueExp + AttributedCharacterIterator + AttributedCharacterIterator.Attribute + AttributedString + Attributes + Attributes + Attributes + Attributes.Name + Attributes2 + Attributes2Impl + AttributesImpl + AudioClip + AudioFileFormat + AudioFileFormat.Type + AudioFileReader + AudioFileWriter + AudioFormat + AudioFormat.Encoding + AudioInputStream + AudioPermission + AudioSystem + AuthPermission + AuthProvider + AuthenticationException + AuthenticationException + AuthenticationNotSupportedException + Authenticator + Authenticator.RequestorType + AuthorizeCallback + Autoscroll + BAD_CONTEXT + BAD_INV_ORDER + BAD_OPERATION + BAD_PARAM + BAD_POLICY + BAD_POLICY_TYPE + BAD_POLICY_VALUE + BAD_QOS + BAD_TYPECODE + BMPImageWriteParam + BackingStoreException + BadAttributeValueExpException + BadBinaryOpValueExpException + BadKind + BadLocationException + BadPaddingException + BadStringOperationException + BandCombineOp + BandedSampleModel + BaseRowSet + BasicArrowButton + BasicAttribute + BasicAttributes + BasicBorders + BasicBorders.ButtonBorder + BasicBorders.FieldBorder + BasicBorders.MarginBorder + BasicBorders.MenuBarBorder + BasicBorders.RadioButtonBorder + BasicBorders.RolloverButtonBorder + BasicBorders.SplitPaneBorder + BasicBorders.ToggleButtonBorder + BasicButtonListener + BasicButtonUI + BasicCheckBoxMenuItemUI + BasicCheckBoxUI + BasicColorChooserUI + BasicComboBoxEditor + BasicComboBoxEditor.UIResource + BasicComboBoxRenderer + BasicComboBoxRenderer.UIResource + BasicComboBoxUI + BasicComboPopup + BasicControl + BasicDesktopIconUI + BasicDesktopPaneUI + BasicDirectoryModel + BasicEditorPaneUI + BasicFileChooserUI + BasicFormattedTextFieldUI + BasicGraphicsUtils + BasicHTML + BasicIconFactory + BasicInternalFrameTitlePane + BasicInternalFrameUI + BasicLabelUI + BasicListUI + BasicLookAndFeel + BasicMenuBarUI + BasicMenuItemUI + BasicMenuUI + BasicOptionPaneUI + BasicOptionPaneUI.ButtonAreaLayout + BasicPanelUI + BasicPasswordFieldUI + BasicPermission + BasicPopupMenuSeparatorUI + BasicPopupMenuUI + BasicProgressBarUI + BasicRadioButtonMenuItemUI + BasicRadioButtonUI + BasicRootPaneUI + BasicScrollBarUI + BasicScrollPaneUI + BasicSeparatorUI + BasicSliderUI + BasicSpinnerUI + BasicSplitPaneDivider + BasicSplitPaneUI + BasicStroke + BasicTabbedPaneUI + BasicTableHeaderUI + BasicTableUI + BasicTextAreaUI + BasicTextFieldUI + BasicTextPaneUI + BasicTextUI + BasicTextUI.BasicCaret + BasicTextUI.BasicHighlighter + BasicToggleButtonUI + BasicToolBarSeparatorUI + BasicToolBarUI + BasicToolTipUI + BasicTreeUI + BasicViewportUI + BatchUpdateException + BeanContext + BeanContextChild + BeanContextChildComponentProxy + BeanContextChildSupport + BeanContextContainerProxy + BeanContextEvent + BeanContextMembershipEvent + BeanContextMembershipListener + BeanContextProxy + BeanContextServiceAvailableEvent + BeanContextServiceProvider + BeanContextServiceProviderBeanInfo + BeanContextServiceRevokedEvent + BeanContextServiceRevokedListener + BeanContextServices + BeanContextServicesListener + BeanContextServicesSupport + BeanContextServicesSupport.BCSSServiceProvider + BeanContextSupport + BeanContextSupport.BCSIterator + BeanDescriptor + BeanInfo + Beans + BevelBorder + Bidi + BigDecimal + BigInteger + BinaryRefAddr + BindException + Binding + Binding + BindingHelper + BindingHolder + BindingIterator + BindingIteratorHelper + BindingIteratorHolder + BindingIteratorOperations + BindingIteratorPOA + BindingListHelper + BindingListHolder + BindingType + BindingTypeHelper + BindingTypeHolder + BitSet + Blob + BlockView + BlockingQueue + Book + Boolean + BooleanControl + BooleanControl.Type + BooleanHolder + BooleanSeqHelper + BooleanSeqHolder + Border + BorderFactory + BorderLayout + BorderUIResource + BorderUIResource.BevelBorderUIResource + BorderUIResource.CompoundBorderUIResource + BorderUIResource.EmptyBorderUIResource + BorderUIResource.EtchedBorderUIResource + BorderUIResource.LineBorderUIResource + BorderUIResource.MatteBorderUIResource + BorderUIResource.TitledBorderUIResource + BoundedRangeModel + Bounds + Bounds + Box + Box.Filler + BoxLayout + BoxView + BoxedValueHelper + BreakIterator + BrokenBarrierException + Buffer + BufferCapabilities + BufferCapabilities.FlipContents + BufferOverflowException + BufferStrategy + BufferUnderflowException + BufferedImage + BufferedImageFilter + BufferedImageOp + BufferedInputStream + BufferedOutputStream + BufferedReader + BufferedWriter + Button + ButtonGroup + ButtonModel + ButtonUI + Byte + ByteArrayInputStream + ByteArrayOutputStream + ByteBuffer + ByteChannel + ByteHolder + ByteLookupTable + ByteOrder + CDATASection + CMMException + CODESET_INCOMPATIBLE + COMM_FAILURE + CRC32 + CRL + CRLException + CRLSelector + CSS + CSS.Attribute + CTX_RESTRICT_SCOPE + CacheRequest + CacheResponse + CachedRowSet + Calendar + Callable + CallableStatement + Callback + CallbackHandler + CancelablePrintJob + CancellationException + CancelledKeyException + CannotProceed + CannotProceedException + CannotProceedHelper + CannotProceedHolder + CannotRedoException + CannotUndoException + Canvas + CardLayout + Caret + CaretEvent + CaretListener + CellEditor + CellEditorListener + CellRendererPane + CertPath + CertPath.CertPathRep + CertPathBuilder + CertPathBuilderException + CertPathBuilderResult + CertPathBuilderSpi + CertPathParameters + CertPathTrustManagerParameters + CertPathValidator + CertPathValidatorException + CertPathValidatorResult + CertPathValidatorSpi + CertSelector + CertStore + CertStoreException + CertStoreParameters + CertStoreSpi + Certificate + Certificate + Certificate + Certificate.CertificateRep + CertificateEncodingException + CertificateEncodingException + CertificateException + CertificateException + CertificateExpiredException + CertificateExpiredException + CertificateFactory + CertificateFactorySpi + CertificateNotYetValidException + CertificateNotYetValidException + CertificateParsingException + CertificateParsingException + ChangeEvent + ChangeListener + ChangedCharSetException + Channel + ChannelBinding + Channels + CharArrayReader + CharArrayWriter + CharBuffer + CharConversionException + CharHolder + CharSeqHelper + CharSeqHolder + CharSequence + Character + Character.Subset + Character.UnicodeBlock + CharacterCodingException + CharacterData + CharacterIterator + Charset + CharsetDecoder + CharsetEncoder + CharsetProvider + Checkbox + CheckboxGroup + CheckboxMenuItem + CheckedInputStream + CheckedOutputStream + Checksum + Choice + ChoiceCallback + ChoiceFormat + Chromaticity + Cipher + CipherInputStream + CipherOutputStream + CipherSpi + Class + ClassCastException + ClassCircularityError + ClassDefinition + ClassDesc + ClassFileTransformer + ClassFormatError + ClassLoader + ClassLoaderRepository + ClassLoadingMXBean + ClassNotFoundException + ClientRequestInfo + ClientRequestInfoOperations + ClientRequestInterceptor + ClientRequestInterceptorOperations + Clip + Clipboard + ClipboardOwner + Clob + CloneNotSupportedException + Cloneable + Closeable + ClosedByInterruptException + ClosedChannelException + ClosedSelectorException + CodeSets + CodeSigner + CodeSource + Codec + CodecFactory + CodecFactoryHelper + CodecFactoryOperations + CodecOperations + CoderMalfunctionError + CoderResult + CodingErrorAction + CollationElementIterator + CollationKey + Collator + Collection + CollectionCertStoreParameters + Collections + Color + ColorChooserComponentFactory + ColorChooserUI + ColorConvertOp + ColorModel + ColorSelectionModel + ColorSpace + ColorSupported + ColorType + ColorUIResource + ComboBoxEditor + ComboBoxModel + ComboBoxUI + ComboPopup + Comment + CommunicationException + Comparable + Comparator + CompilationMXBean + Compiler + CompletionService + CompletionStatus + CompletionStatusHelper + Component + ComponentAdapter + ComponentColorModel + ComponentEvent + ComponentIdHelper + ComponentInputMap + ComponentInputMapUIResource + ComponentListener + ComponentOrientation + ComponentSampleModel + ComponentUI + ComponentView + Composite + CompositeContext + CompositeData + CompositeDataSupport + CompositeName + CompositeType + CompositeView + CompoundBorder + CompoundControl + CompoundControl.Type + CompoundEdit + CompoundName + Compression + ConcurrentHashMap + ConcurrentLinkedQueue + ConcurrentMap + ConcurrentModificationException + Condition + Configuration + ConfigurationException + ConfirmationCallback + ConnectException + ConnectException + ConnectIOException + Connection + ConnectionEvent + ConnectionEventListener + ConnectionPendingException + ConnectionPoolDataSource + ConsoleHandler + Constructor + Container + ContainerAdapter + ContainerEvent + ContainerListener + ContainerOrderFocusTraversalPolicy + ContentHandler + ContentHandler + ContentHandlerFactory + ContentModel + Context + Context + ContextList + ContextNotEmptyException + ContextualRenderedImageFactory + Control + Control + Control.Type + ControlFactory + ControllerEventListener + ConvolveOp + CookieHandler + CookieHolder + Copies + CopiesSupported + CopyOnWriteArrayList + CopyOnWriteArraySet + CountDownLatch + CounterMonitor + CounterMonitorMBean + CredentialException + CredentialExpiredException + CredentialNotFoundException + CropImageFilter + CubicCurve2D + CubicCurve2D.Double + CubicCurve2D.Float + Currency + Current + Current + Current + CurrentHelper + CurrentHelper + CurrentHelper + CurrentHolder + CurrentOperations + CurrentOperations + CurrentOperations + Cursor + CustomMarshal + CustomValue + Customizer + CyclicBarrier + DATA_CONVERSION + DESKeySpec + DESedeKeySpec + DGC + DHGenParameterSpec + DHKey + DHParameterSpec + DHPrivateKey + DHPrivateKeySpec + DHPublicKey + DHPublicKeySpec + DISCARDING + DOMConfiguration + DOMError + DOMErrorHandler + DOMException + DOMImplementation + DOMImplementationLS + DOMImplementationList + DOMImplementationRegistry + DOMImplementationSource + DOMLocator + DOMLocator + DOMResult + DOMSource + DOMStringList + DSAKey + DSAKeyPairGenerator + DSAParameterSpec + DSAParams + DSAPrivateKey + DSAPrivateKeySpec + DSAPublicKey + DSAPublicKeySpec + DTD + DTDConstants + DTDHandler + DataBuffer + DataBufferByte + DataBufferDouble + DataBufferFloat + DataBufferInt + DataBufferShort + DataBufferUShort + DataFlavor + DataFormatException + DataInput + DataInputStream + DataInputStream + DataLine + DataLine.Info + DataOutput + DataOutputStream + DataOutputStream + DataSource + DataTruncation + DatabaseMetaData + DatagramChannel + DatagramPacket + DatagramSocket + DatagramSocketImpl + DatagramSocketImplFactory + DatatypeConfigurationException + DatatypeConstants + DatatypeConstants.Field + DatatypeFactory + Date + Date + DateFormat + DateFormat.Field + DateFormatSymbols + DateFormatter + DateTimeAtCompleted + DateTimeAtCreation + DateTimeAtProcessing + DateTimeSyntax + DebugGraphics + DecimalFormat + DecimalFormatSymbols + DeclHandler + DefaultBoundedRangeModel + DefaultButtonModel + DefaultCaret + DefaultCellEditor + DefaultColorSelectionModel + DefaultComboBoxModel + DefaultDesktopManager + DefaultEditorKit + DefaultEditorKit.BeepAction + DefaultEditorKit.CopyAction + DefaultEditorKit.CutAction + DefaultEditorKit.DefaultKeyTypedAction + DefaultEditorKit.InsertBreakAction + DefaultEditorKit.InsertContentAction + DefaultEditorKit.InsertTabAction + DefaultEditorKit.PasteAction + DefaultFocusManager + DefaultFocusTraversalPolicy + DefaultFormatter + DefaultFormatterFactory + DefaultHandler + DefaultHandler2 + DefaultHighlighter + DefaultHighlighter.DefaultHighlightPainter + DefaultKeyboardFocusManager + DefaultListCellRenderer + DefaultListCellRenderer.UIResource + DefaultListModel + DefaultListSelectionModel + DefaultLoaderRepository + DefaultLoaderRepository + DefaultMenuLayout + DefaultMetalTheme + DefaultMutableTreeNode + DefaultPersistenceDelegate + DefaultSingleSelectionModel + DefaultStyledDocument + DefaultStyledDocument.AttributeUndoableEdit + DefaultStyledDocument.ElementSpec + DefaultTableCellRenderer + DefaultTableCellRenderer.UIResource + DefaultTableColumnModel + DefaultTableModel + DefaultTextUI + DefaultTreeCellEditor + DefaultTreeCellRenderer + DefaultTreeModel + DefaultTreeSelectionModel + DefinitionKind + DefinitionKindHelper + Deflater + DeflaterOutputStream + DelayQueue + Delayed + Delegate + Delegate + Delegate + DelegationPermission + Deprecated + Descriptor + DescriptorAccess + DescriptorSupport + DesignMode + DesktopIconUI + DesktopManager + DesktopPaneUI + Destination + DestroyFailedException + Destroyable + Dialog + Dictionary + DigestException + DigestInputStream + DigestOutputStream + Dimension + Dimension2D + DimensionUIResource + DirContext + DirObjectFactory + DirStateFactory + DirStateFactory.Result + DirectColorModel + DirectoryManager + DisplayMode + DnDConstants + Doc + DocAttribute + DocAttributeSet + DocFlavor + DocFlavor.BYTE_ARRAY + DocFlavor.CHAR_ARRAY + DocFlavor.INPUT_STREAM + DocFlavor.READER + DocFlavor.SERVICE_FORMATTED + DocFlavor.STRING + DocFlavor.URL + DocPrintJob + Document + Document + DocumentBuilder + DocumentBuilderFactory + DocumentEvent + DocumentEvent.ElementChange + DocumentEvent.EventType + DocumentFilter + DocumentFilter.FilterBypass + DocumentFragment + DocumentHandler + DocumentListener + DocumentName + DocumentParser + DocumentType + Documented + DomainCombiner + DomainManager + DomainManagerOperations + Double + DoubleBuffer + DoubleHolder + DoubleSeqHelper + DoubleSeqHolder + DragGestureEvent + DragGestureListener + DragGestureRecognizer + DragSource + DragSourceAdapter + DragSourceContext + DragSourceDragEvent + DragSourceDropEvent + DragSourceEvent + DragSourceListener + DragSourceMotionListener + Driver + DriverManager + DriverPropertyInfo + DropTarget + DropTarget.DropTargetAutoScroller + DropTargetAdapter + DropTargetContext + DropTargetDragEvent + DropTargetDropEvent + DropTargetEvent + DropTargetListener + DuplicateFormatFlagsException + DuplicateName + DuplicateNameHelper + Duration + DynAny + DynAny + DynAnyFactory + DynAnyFactoryHelper + DynAnyFactoryOperations + DynAnyHelper + DynAnyOperations + DynAnySeqHelper + DynArray + DynArray + DynArrayHelper + DynArrayOperations + DynEnum + DynEnum + DynEnumHelper + DynEnumOperations + DynFixed + DynFixed + DynFixedHelper + DynFixedOperations + DynSequence + DynSequence + DynSequenceHelper + DynSequenceOperations + DynStruct + DynStruct + DynStructHelper + DynStructOperations + DynUnion + DynUnion + DynUnionHelper + DynUnionOperations + DynValue + DynValue + DynValueBox + DynValueBoxOperations + DynValueCommon + DynValueCommonOperations + DynValueHelper + DynValueOperations + DynamicImplementation + DynamicImplementation + DynamicMBean + ECField + ECFieldF2m + ECFieldFp + ECGenParameterSpec + ECKey + ECParameterSpec + ECPoint + ECPrivateKey + ECPrivateKeySpec + ECPublicKey + ECPublicKeySpec + ENCODING_CDR_ENCAPS + EOFException + EditorKit + Element + Element + Element + ElementIterator + ElementType + Ellipse2D + Ellipse2D.Double + Ellipse2D.Float + EllipticCurve + EmptyBorder + EmptyStackException + EncodedKeySpec + Encoder + Encoding + EncryptedPrivateKeyInfo + Entity + Entity + EntityReference + EntityResolver + EntityResolver2 + Enum + EnumConstantNotPresentException + EnumControl + EnumControl.Type + EnumMap + EnumSet + EnumSyntax + Enumeration + Environment + Error + ErrorHandler + ErrorListener + ErrorManager + EtchedBorder + Event + EventContext + EventDirContext + EventHandler + EventListener + EventListenerList + EventListenerProxy + EventObject + EventQueue + EventSetDescriptor + Exception + ExceptionDetailMessage + ExceptionInInitializerError + ExceptionList + ExceptionListener + Exchanger + ExecutionException + Executor + ExecutorCompletionService + ExecutorService + Executors + ExemptionMechanism + ExemptionMechanismException + ExemptionMechanismSpi + ExpandVetoException + ExportException + Expression + ExtendedRequest + ExtendedResponse + Externalizable + FREE_MEM + FactoryConfigurationError + FailedLoginException + FeatureDescriptor + Fidelity + Field + FieldNameHelper + FieldNameHelper + FieldPosition + FieldView + File + FileCacheImageInputStream + FileCacheImageOutputStream + FileChannel + FileChannel.MapMode + FileChooserUI + FileDescriptor + FileDialog + FileFilter + FileFilter + FileHandler + FileImageInputStream + FileImageOutputStream + FileInputStream + FileLock + FileLockInterruptionException + FileNameMap + FileNotFoundException + FileOutputStream + FilePermission + FileReader + FileSystemView + FileView + FileWriter + FilenameFilter + Filter + FilterInputStream + FilterOutputStream + FilterReader + FilterWriter + FilteredImageSource + FilteredRowSet + Finishings + FixedHeightLayoutCache + FixedHolder + FlatteningPathIterator + FlavorEvent + FlavorException + FlavorListener + FlavorMap + FlavorTable + Float + FloatBuffer + FloatControl + FloatControl.Type + FloatHolder + FloatSeqHelper + FloatSeqHolder + FlowLayout + FlowView + FlowView.FlowStrategy + Flushable + FocusAdapter + FocusEvent + FocusListener + FocusManager + FocusTraversalPolicy + Font + FontFormatException + FontMetrics + FontRenderContext + FontUIResource + FormSubmitEvent + FormSubmitEvent.MethodType + FormView + Format + Format.Field + FormatConversionProvider + FormatFlagsConversionMismatchException + FormatMismatch + FormatMismatchHelper + Formattable + FormattableFlags + Formatter + Formatter + FormatterClosedException + ForwardRequest + ForwardRequest + ForwardRequestHelper + ForwardRequestHelper + Frame + Future + FutureTask + GSSContext + GSSCredential + GSSException + GSSManager + GSSName + GZIPInputStream + GZIPOutputStream + GapContent + GarbageCollectorMXBean + GatheringByteChannel + GaugeMonitor + GaugeMonitorMBean + GeneralPath + GeneralSecurityException + GenericArrayType + GenericDeclaration + GenericSignatureFormatError + GlyphJustificationInfo + GlyphMetrics + GlyphVector + GlyphView + GlyphView.GlyphPainter + GradientPaint + GraphicAttribute + Graphics + Graphics2D + GraphicsConfigTemplate + GraphicsConfiguration + GraphicsDevice + GraphicsEnvironment + GrayFilter + GregorianCalendar + GridBagConstraints + GridBagLayout + GridLayout + Group + Guard + GuardedObject + HOLDING + HTML + HTML.Attribute + HTML.Tag + HTML.UnknownTag + HTMLDocument + HTMLDocument.Iterator + HTMLEditorKit + HTMLEditorKit.HTMLFactory + HTMLEditorKit.HTMLTextAction + HTMLEditorKit.InsertHTMLTextAction + HTMLEditorKit.LinkController + HTMLEditorKit.Parser + HTMLEditorKit.ParserCallback + HTMLFrameHyperlinkEvent + HTMLWriter + Handler + HandlerBase + HandshakeCompletedEvent + HandshakeCompletedListener + HasControls + HashAttributeSet + HashDocAttributeSet + HashMap + HashPrintJobAttributeSet + HashPrintRequestAttributeSet + HashPrintServiceAttributeSet + HashSet + Hashtable + HeadlessException + HierarchyBoundsAdapter + HierarchyBoundsListener + HierarchyEvent + HierarchyListener + Highlighter + Highlighter.Highlight + Highlighter.HighlightPainter + HostnameVerifier + HttpRetryException + HttpURLConnection + HttpsURLConnection + HyperlinkEvent + HyperlinkEvent.EventType + HyperlinkListener + ICC_ColorSpace + ICC_Profile + ICC_ProfileGray + ICC_ProfileRGB + IDLEntity + IDLType + IDLTypeHelper + IDLTypeOperations + ID_ASSIGNMENT_POLICY_ID + ID_UNIQUENESS_POLICY_ID + IIOByteBuffer + IIOException + IIOImage + IIOInvalidTreeException + IIOMetadata + IIOMetadataController + IIOMetadataFormat + IIOMetadataFormatImpl + IIOMetadataNode + IIOParam + IIOParamController + IIOReadProgressListener + IIOReadUpdateListener + IIOReadWarningListener + IIORegistry + IIOServiceProvider + IIOWriteProgressListener + IIOWriteWarningListener + IMPLICIT_ACTIVATION_POLICY_ID + IMP_LIMIT + INACTIVE + INITIALIZE + INTERNAL + INTF_REPOS + INVALID_ACTIVITY + INVALID_TRANSACTION + INV_FLAG + INV_IDENT + INV_OBJREF + INV_POLICY + IOException + IOR + IORHelper + IORHolder + IORInfo + IORInfoOperations + IORInterceptor + IORInterceptorOperations + IORInterceptor_3_0 + IORInterceptor_3_0Helper + IORInterceptor_3_0Holder + IORInterceptor_3_0Operations + IRObject + IRObjectOperations + Icon + IconUIResource + IconView + IdAssignmentPolicy + IdAssignmentPolicyOperations + IdAssignmentPolicyValue + IdUniquenessPolicy + IdUniquenessPolicyOperations + IdUniquenessPolicyValue + IdentifierHelper + Identity + IdentityHashMap + IdentityScope + IllegalAccessError + IllegalAccessException + IllegalArgumentException + IllegalBlockSizeException + IllegalBlockingModeException + IllegalCharsetNameException + IllegalClassFormatException + IllegalComponentStateException + IllegalFormatCodePointException + IllegalFormatConversionException + IllegalFormatException + IllegalFormatFlagsException + IllegalFormatPrecisionException + IllegalFormatWidthException + IllegalMonitorStateException + IllegalPathStateException + IllegalSelectorException + IllegalStateException + IllegalThreadStateException + Image + ImageCapabilities + ImageConsumer + ImageFilter + ImageGraphicAttribute + ImageIO + ImageIcon + ImageInputStream + ImageInputStreamImpl + ImageInputStreamSpi + ImageObserver + ImageOutputStream + ImageOutputStreamImpl + ImageOutputStreamSpi + ImageProducer + ImageReadParam + ImageReader + ImageReaderSpi + ImageReaderWriterSpi + ImageTranscoder + ImageTranscoderSpi + ImageTypeSpecifier + ImageView + ImageWriteParam + ImageWriter + ImageWriterSpi + ImagingOpException + ImplicitActivationPolicy + ImplicitActivationPolicyOperations + ImplicitActivationPolicyValue + IncompatibleClassChangeError + IncompleteAnnotationException + InconsistentTypeCode + InconsistentTypeCode + InconsistentTypeCodeHelper + IndexColorModel + IndexOutOfBoundsException + IndexedPropertyChangeEvent + IndexedPropertyDescriptor + IndirectionException + Inet4Address + Inet6Address + InetAddress + InetSocketAddress + Inflater + InflaterInputStream + InheritableThreadLocal + Inherited + InitialContext + InitialContextFactory + InitialContextFactoryBuilder + InitialDirContext + InitialLdapContext + InlineView + InputContext + InputEvent + InputMap + InputMapUIResource + InputMethod + InputMethodContext + InputMethodDescriptor + InputMethodEvent + InputMethodHighlight + InputMethodListener + InputMethodRequests + InputMismatchException + InputSource + InputStream + InputStream + InputStream + InputStreamReader + InputSubset + InputVerifier + Insets + InsetsUIResource + InstanceAlreadyExistsException + InstanceNotFoundException + InstantiationError + InstantiationException + Instrument + Instrumentation + InsufficientResourcesException + IntBuffer + IntHolder + Integer + IntegerSyntax + Interceptor + InterceptorOperations + InternalError + InternalFrameAdapter + InternalFrameEvent + InternalFrameFocusTraversalPolicy + InternalFrameListener + InternalFrameUI + InternationalFormatter + InterruptedException + InterruptedIOException + InterruptedNamingException + InterruptibleChannel + IntrospectionException + IntrospectionException + Introspector + Invalid + InvalidActivityException + InvalidAddress + InvalidAddressHelper + InvalidAddressHolder + InvalidAlgorithmParameterException + InvalidApplicationException + InvalidAttributeIdentifierException + InvalidAttributeValueException + InvalidAttributeValueException + InvalidAttributesException + InvalidClassException + InvalidDnDOperationException + InvalidKeyException + InvalidKeyException + InvalidKeySpecException + InvalidMarkException + InvalidMidiDataException + InvalidName + InvalidName + InvalidName + InvalidNameException + InvalidNameHelper + InvalidNameHelper + InvalidNameHolder + InvalidObjectException + InvalidOpenTypeException + InvalidParameterException + InvalidParameterSpecException + InvalidPolicy + InvalidPolicyHelper + InvalidPreferencesFormatException + InvalidPropertiesFormatException + InvalidRelationIdException + InvalidRelationServiceException + InvalidRelationTypeException + InvalidRoleInfoException + InvalidRoleValueException + InvalidSearchControlsException + InvalidSearchFilterException + InvalidSeq + InvalidSlot + InvalidSlotHelper + InvalidTargetObjectTypeException + InvalidTransactionException + InvalidTypeForEncoding + InvalidTypeForEncodingHelper + InvalidValue + InvalidValue + InvalidValueHelper + InvocationEvent + InvocationHandler + InvocationTargetException + InvokeHandler + IstringHelper + ItemEvent + ItemListener + ItemSelectable + Iterable + Iterator + IvParameterSpec + JApplet + JButton + JCheckBox + JCheckBoxMenuItem + JColorChooser + JComboBox + JComboBox.KeySelectionManager + JComponent + JDesktopPane + JDialog + JEditorPane + JFileChooser + JFormattedTextField + JFormattedTextField.AbstractFormatter + JFormattedTextField.AbstractFormatterFactory + JFrame + JInternalFrame + JInternalFrame.JDesktopIcon + JLabel + JLayeredPane + JList + JMException + JMRuntimeException + JMXAuthenticator + JMXConnectionNotification + JMXConnector + JMXConnectorFactory + JMXConnectorProvider + JMXConnectorServer + JMXConnectorServerFactory + JMXConnectorServerMBean + JMXConnectorServerProvider + JMXPrincipal + JMXProviderException + JMXServerErrorException + JMXServiceURL + JMenu + JMenuBar + JMenuItem + JOptionPane + JPEGHuffmanTable + JPEGImageReadParam + JPEGImageWriteParam + JPEGQTable + JPanel + JPasswordField + JPopupMenu + JPopupMenu.Separator + JProgressBar + JRadioButton + JRadioButtonMenuItem + JRootPane + JScrollBar + JScrollPane + JSeparator + JSlider + JSpinner + JSpinner.DateEditor + JSpinner.DefaultEditor + JSpinner.ListEditor + JSpinner.NumberEditor + JSplitPane + JTabbedPane + JTable + JTable.PrintMode + JTableHeader + JTextArea + JTextComponent + JTextComponent.KeyBinding + JTextField + JTextPane + JToggleButton + JToggleButton.ToggleButtonModel + JToolBar + JToolBar.Separator + JToolTip + JTree + JTree.DynamicUtilTreeNode + JTree.EmptySelectionModel + JViewport + JWindow + JarEntry + JarException + JarFile + JarInputStream + JarOutputStream + JarURLConnection + JdbcRowSet + JobAttributes + JobAttributes.DefaultSelectionType + JobAttributes.DestinationType + JobAttributes.DialogType + JobAttributes.MultipleDocumentHandlingType + JobAttributes.SidesType + JobHoldUntil + JobImpressions + JobImpressionsCompleted + JobImpressionsSupported + JobKOctets + JobKOctetsProcessed + JobKOctetsSupported + JobMediaSheets + JobMediaSheetsCompleted + JobMediaSheetsSupported + JobMessageFromOperator + JobName + JobOriginatingUserName + JobPriority + JobPrioritySupported + JobSheets + JobState + JobStateReason + JobStateReasons + JoinRowSet + Joinable + KerberosKey + KerberosPrincipal + KerberosTicket + Kernel + Key + KeyAdapter + KeyAgreement + KeyAgreementSpi + KeyAlreadyExistsException + KeyEvent + KeyEventDispatcher + KeyEventPostProcessor + KeyException + KeyFactory + KeyFactorySpi + KeyGenerator + KeyGeneratorSpi + KeyListener + KeyManagementException + KeyManager + KeyManagerFactory + KeyManagerFactorySpi + KeyPair + KeyPairGenerator + KeyPairGeneratorSpi + KeyRep + KeyRep.Type + KeySpec + KeyStore + KeyStore.Builder + KeyStore.CallbackHandlerProtection + KeyStore.Entry + KeyStore.LoadStoreParameter + KeyStore.PasswordProtection + KeyStore.PrivateKeyEntry + KeyStore.ProtectionParameter + KeyStore.SecretKeyEntry + KeyStore.TrustedCertificateEntry + KeyStoreBuilderParameters + KeyStoreException + KeyStoreSpi + KeyStroke + KeyboardFocusManager + Keymap + LDAPCertStoreParameters + LIFESPAN_POLICY_ID + LOCATION_FORWARD + LSException + LSInput + LSLoadEvent + LSOutput + LSParser + LSParserFilter + LSProgressEvent + LSResourceResolver + LSSerializer + LSSerializerFilter + Label + LabelUI + LabelView + LanguageCallback + LastOwnerException + LayeredHighlighter + LayeredHighlighter.LayerPainter + LayoutFocusTraversalPolicy + LayoutManager + LayoutManager2 + LayoutQueue + LdapContext + LdapName + LdapReferralException + Lease + Level + LexicalHandler + LifespanPolicy + LifespanPolicyOperations + LifespanPolicyValue + LimitExceededException + Line + Line.Info + Line2D + Line2D.Double + Line2D.Float + LineBorder + LineBreakMeasurer + LineEvent + LineEvent.Type + LineListener + LineMetrics + LineNumberInputStream + LineNumberReader + LineUnavailableException + LinkException + LinkLoopException + LinkRef + LinkageError + LinkedBlockingQueue + LinkedHashMap + LinkedHashSet + LinkedList + List + List + ListCellRenderer + ListDataEvent + ListDataListener + ListIterator + ListModel + ListResourceBundle + ListSelectionEvent + ListSelectionListener + ListSelectionModel + ListUI + ListView + ListenerNotFoundException + LoaderHandler + LocalObject + Locale + LocateRegistry + Locator + Locator2 + Locator2Impl + LocatorImpl + Lock + LockSupport + LogManager + LogRecord + LogStream + Logger + LoggingMXBean + LoggingPermission + LoginContext + LoginException + LoginModule + Long + LongBuffer + LongHolder + LongLongSeqHelper + LongLongSeqHolder + LongSeqHelper + LongSeqHolder + LookAndFeel + LookupOp + LookupTable + MARSHAL + MBeanAttributeInfo + MBeanConstructorInfo + MBeanException + MBeanFeatureInfo + MBeanInfo + MBeanNotificationInfo + MBeanOperationInfo + MBeanParameterInfo + MBeanPermission + MBeanRegistration + MBeanRegistrationException + MBeanServer + MBeanServerBuilder + MBeanServerConnection + MBeanServerDelegate + MBeanServerDelegateMBean + MBeanServerFactory + MBeanServerForwarder + MBeanServerInvocationHandler + MBeanServerNotification + MBeanServerNotificationFilter + MBeanServerPermission + MBeanTrustPermission + MGF1ParameterSpec + MLet + MLetMBean + Mac + MacSpi + MalformedInputException + MalformedLinkException + MalformedObjectNameException + MalformedParameterizedTypeException + MalformedURLException + ManageReferralControl + ManagementFactory + ManagementPermission + ManagerFactoryParameters + Manifest + Map + Map.Entry + MappedByteBuffer + MarshalException + MarshalledObject + MaskFormatter + MatchResult + Matcher + Math + MathContext + MatteBorder + Media + MediaName + MediaPrintableArea + MediaSize + MediaSize.Engineering + MediaSize.ISO + MediaSize.JIS + MediaSize.NA + MediaSize.Other + MediaSizeName + MediaTracker + MediaTray + Member + MemoryCacheImageInputStream + MemoryCacheImageOutputStream + MemoryHandler + MemoryImageSource + MemoryMXBean + MemoryManagerMXBean + MemoryNotificationInfo + MemoryPoolMXBean + MemoryType + MemoryUsage + Menu + MenuBar + MenuBarUI + MenuComponent + MenuContainer + MenuDragMouseEvent + MenuDragMouseListener + MenuElement + MenuEvent + MenuItem + MenuItemUI + MenuKeyEvent + MenuKeyListener + MenuListener + MenuSelectionManager + MenuShortcut + MessageDigest + MessageDigestSpi + MessageFormat + MessageFormat.Field + MessageProp + MetaEventListener + MetaMessage + MetalBorders + MetalBorders.ButtonBorder + MetalBorders.Flush3DBorder + MetalBorders.InternalFrameBorder + MetalBorders.MenuBarBorder + MetalBorders.MenuItemBorder + MetalBorders.OptionDialogBorder + MetalBorders.PaletteBorder + MetalBorders.PopupMenuBorder + MetalBorders.RolloverButtonBorder + MetalBorders.ScrollPaneBorder + MetalBorders.TableHeaderBorder + MetalBorders.TextFieldBorder + MetalBorders.ToggleButtonBorder + MetalBorders.ToolBarBorder + MetalButtonUI + MetalCheckBoxIcon + MetalCheckBoxUI + MetalComboBoxButton + MetalComboBoxEditor + MetalComboBoxEditor.UIResource + MetalComboBoxIcon + MetalComboBoxUI + MetalDesktopIconUI + MetalFileChooserUI + MetalIconFactory + MetalIconFactory.FileIcon16 + MetalIconFactory.FolderIcon16 + MetalIconFactory.PaletteCloseIcon + MetalIconFactory.TreeControlIcon + MetalIconFactory.TreeFolderIcon + MetalIconFactory.TreeLeafIcon + MetalInternalFrameTitlePane + MetalInternalFrameUI + MetalLabelUI + MetalLookAndFeel + MetalMenuBarUI + MetalPopupMenuSeparatorUI + MetalProgressBarUI + MetalRadioButtonUI + MetalRootPaneUI + MetalScrollBarUI + MetalScrollButton + MetalScrollPaneUI + MetalSeparatorUI + MetalSliderUI + MetalSplitPaneUI + MetalTabbedPaneUI + MetalTextFieldUI + MetalTheme + MetalToggleButtonUI + MetalToolBarUI + MetalToolTipUI + MetalTreeUI + Method + MethodDescriptor + MidiChannel + MidiDevice + MidiDevice.Info + MidiDeviceProvider + MidiEvent + MidiFileFormat + MidiFileReader + MidiFileWriter + MidiMessage + MidiSystem + MidiUnavailableException + MimeTypeParseException + MinimalHTMLWriter + MissingFormatArgumentException + MissingFormatWidthException + MissingResourceException + Mixer + Mixer.Info + MixerProvider + ModelMBean + ModelMBeanAttributeInfo + ModelMBeanConstructorInfo + ModelMBeanInfo + ModelMBeanInfoSupport + ModelMBeanNotificationBroadcaster + ModelMBeanNotificationInfo + ModelMBeanOperationInfo + ModificationItem + Modifier + Monitor + MonitorMBean + MonitorNotification + MonitorSettingException + MouseAdapter + MouseDragGestureRecognizer + MouseEvent + MouseInfo + MouseInputAdapter + MouseInputListener + MouseListener + MouseMotionAdapter + MouseMotionListener + MouseWheelEvent + MouseWheelListener + MultiButtonUI + MultiColorChooserUI + MultiComboBoxUI + MultiDesktopIconUI + MultiDesktopPaneUI + MultiDoc + MultiDocPrintJob + MultiDocPrintService + MultiFileChooserUI + MultiInternalFrameUI + MultiLabelUI + MultiListUI + MultiLookAndFeel + MultiMenuBarUI + MultiMenuItemUI + MultiOptionPaneUI + MultiPanelUI + MultiPixelPackedSampleModel + MultiPopupMenuUI + MultiProgressBarUI + MultiRootPaneUI + MultiScrollBarUI + MultiScrollPaneUI + MultiSeparatorUI + MultiSliderUI + MultiSpinnerUI + MultiSplitPaneUI + MultiTabbedPaneUI + MultiTableHeaderUI + MultiTableUI + MultiTextUI + MultiToolBarUI + MultiToolTipUI + MultiTreeUI + MultiViewportUI + MulticastSocket + MultipleComponentProfileHelper + MultipleComponentProfileHolder + MultipleDocumentHandling + MultipleMaster + MutableAttributeSet + MutableComboBoxModel + MutableTreeNode + NON_EXISTENT + NO_IMPLEMENT + NO_MEMORY + NO_PERMISSION + NO_RESOURCES + NO_RESPONSE + NVList + Name + NameAlreadyBoundException + NameCallback + NameClassPair + NameComponent + NameComponentHelper + NameComponentHolder + NameDynAnyPair + NameDynAnyPairHelper + NameDynAnyPairSeqHelper + NameHelper + NameHolder + NameList + NameNotFoundException + NameParser + NameValuePair + NameValuePair + NameValuePairHelper + NameValuePairHelper + NameValuePairSeqHelper + NamedNodeMap + NamedValue + NamespaceChangeListener + NamespaceContext + NamespaceSupport + Naming + NamingContext + NamingContextExt + NamingContextExtHelper + NamingContextExtHolder + NamingContextExtOperations + NamingContextExtPOA + NamingContextHelper + NamingContextHolder + NamingContextOperations + NamingContextPOA + NamingEnumeration + NamingEvent + NamingException + NamingExceptionEvent + NamingListener + NamingManager + NamingSecurityException + NavigationFilter + NavigationFilter.FilterBypass + NegativeArraySizeException + NetPermission + NetworkInterface + NoClassDefFoundError + NoConnectionPendingException + NoContext + NoContextHelper + NoInitialContextException + NoPermissionException + NoRouteToHostException + NoServant + NoServantHelper + NoSuchAlgorithmException + NoSuchAttributeException + NoSuchElementException + NoSuchFieldError + NoSuchFieldException + NoSuchMethodError + NoSuchMethodException + NoSuchObjectException + NoSuchPaddingException + NoSuchProviderException + Node + NodeChangeEvent + NodeChangeListener + NodeList + NonReadableChannelException + NonWritableChannelException + NoninvertibleTransformException + NotActiveException + NotBoundException + NotCompliantMBeanException + NotContextException + NotEmpty + NotEmptyHelper + NotEmptyHolder + NotFound + NotFoundHelper + NotFoundHolder + NotFoundReason + NotFoundReasonHelper + NotFoundReasonHolder + NotOwnerException + NotSerializableException + NotYetBoundException + NotYetConnectedException + Notation + Notification + NotificationBroadcaster + NotificationBroadcasterSupport + NotificationEmitter + NotificationFilter + NotificationFilterSupport + NotificationListener + NotificationResult + NullCipher + NullPointerException + Number + NumberFormat + NumberFormat.Field + NumberFormatException + NumberFormatter + NumberOfDocuments + NumberOfInterveningJobs + NumberUp + NumberUpSupported + NumericShaper + OAEPParameterSpec + OBJECT_NOT_EXIST + OBJ_ADAPTER + OMGVMCID + ORB + ORB + ORBIdHelper + ORBInitInfo + ORBInitInfoOperations + ORBInitializer + ORBInitializerOperations + ObjID + Object + Object + ObjectAlreadyActive + ObjectAlreadyActiveHelper + ObjectChangeListener + ObjectFactory + ObjectFactoryBuilder + ObjectHelper + ObjectHolder + ObjectIdHelper + ObjectIdHelper + ObjectImpl + ObjectImpl + ObjectInput + ObjectInputStream + ObjectInputStream.GetField + ObjectInputValidation + ObjectInstance + ObjectName + ObjectNotActive + ObjectNotActiveHelper + ObjectOutput + ObjectOutputStream + ObjectOutputStream.PutField + ObjectReferenceFactory + ObjectReferenceFactoryHelper + ObjectReferenceFactoryHolder + ObjectReferenceTemplate + ObjectReferenceTemplateHelper + ObjectReferenceTemplateHolder + ObjectReferenceTemplateSeqHelper + ObjectReferenceTemplateSeqHolder + ObjectStreamClass + ObjectStreamConstants + ObjectStreamException + ObjectStreamField + ObjectView + Observable + Observer + OceanTheme + OctetSeqHelper + OctetSeqHolder + Oid + OpenDataException + OpenMBeanAttributeInfo + OpenMBeanAttributeInfoSupport + OpenMBeanConstructorInfo + OpenMBeanConstructorInfoSupport + OpenMBeanInfo + OpenMBeanInfoSupport + OpenMBeanOperationInfo + OpenMBeanOperationInfoSupport + OpenMBeanParameterInfo + OpenMBeanParameterInfoSupport + OpenType + OpenType + OperatingSystemMXBean + Operation + OperationNotSupportedException + OperationsException + Option + OptionPaneUI + OptionalDataException + OrientationRequested + OutOfMemoryError + OutputDeviceAssigned + OutputKeys + OutputStream + OutputStream + OutputStream + OutputStreamWriter + OverlappingFileLockException + OverlayLayout + Override + Owner + PBEKey + PBEKeySpec + PBEParameterSpec + PDLOverrideSupported + PERSIST_STORE + PKCS8EncodedKeySpec + PKIXBuilderParameters + PKIXCertPathBuilderResult + PKIXCertPathChecker + PKIXCertPathValidatorResult + PKIXParameters + POA + POAHelper + POAManager + POAManagerOperations + POAOperations + PRIVATE_MEMBER + PSSParameterSpec + PSource + PSource.PSpecified + PUBLIC_MEMBER + Pack200 + Pack200.Packer + Pack200.Unpacker + Package + PackedColorModel + PageAttributes + PageAttributes.ColorType + PageAttributes.MediaType + PageAttributes.OrientationRequestedType + PageAttributes.OriginType + PageAttributes.PrintQualityType + PageFormat + PageRanges + Pageable + PagedResultsControl + PagedResultsResponseControl + PagesPerMinute + PagesPerMinuteColor + Paint + PaintContext + PaintEvent + Panel + PanelUI + Paper + ParagraphView + ParagraphView + Parameter + ParameterBlock + ParameterDescriptor + ParameterMetaData + ParameterMode + ParameterModeHelper + ParameterModeHolder + ParameterizedType + ParseException + ParsePosition + Parser + Parser + ParserAdapter + ParserConfigurationException + ParserDelegator + ParserFactory + PartialResultException + PasswordAuthentication + PasswordCallback + PasswordView + Patch + PathIterator + Pattern + PatternSyntaxException + Permission + Permission + PermissionCollection + Permissions + PersistenceDelegate + PersistentMBean + PhantomReference + Pipe + Pipe.SinkChannel + Pipe.SourceChannel + PipedInputStream + PipedOutputStream + PipedReader + PipedWriter + PixelGrabber + PixelInterleavedSampleModel + PlainDocument + PlainView + Point + Point2D + Point2D.Double + Point2D.Float + PointerInfo + Policy + Policy + Policy + PolicyError + PolicyErrorCodeHelper + PolicyErrorHelper + PolicyErrorHolder + PolicyFactory + PolicyFactoryOperations + PolicyHelper + PolicyHolder + PolicyListHelper + PolicyListHolder + PolicyNode + PolicyOperations + PolicyQualifierInfo + PolicyTypeHelper + Polygon + PooledConnection + Popup + PopupFactory + PopupMenu + PopupMenuEvent + PopupMenuListener + PopupMenuUI + Port + Port.Info + PortUnreachableException + PortableRemoteObject + PortableRemoteObjectDelegate + Position + Position.Bias + Predicate + PreferenceChangeEvent + PreferenceChangeListener + Preferences + PreferencesFactory + PreparedStatement + PresentationDirection + Principal + Principal + PrincipalHolder + PrintEvent + PrintException + PrintGraphics + PrintJob + PrintJobAdapter + PrintJobAttribute + PrintJobAttributeEvent + PrintJobAttributeListener + PrintJobAttributeSet + PrintJobEvent + PrintJobListener + PrintQuality + PrintRequestAttribute + PrintRequestAttributeSet + PrintService + PrintServiceAttribute + PrintServiceAttributeEvent + PrintServiceAttributeListener + PrintServiceAttributeSet + PrintServiceLookup + PrintStream + PrintWriter + Printable + PrinterAbortException + PrinterException + PrinterGraphics + PrinterIOException + PrinterInfo + PrinterIsAcceptingJobs + PrinterJob + PrinterLocation + PrinterMakeAndModel + PrinterMessageFromOperator + PrinterMoreInfo + PrinterMoreInfoManufacturer + PrinterName + PrinterResolution + PrinterState + PrinterStateReason + PrinterStateReasons + PrinterURI + PriorityBlockingQueue + PriorityQueue + PrivateClassLoader + PrivateCredentialPermission + PrivateKey + PrivateMLet + PrivilegedAction + PrivilegedActionException + PrivilegedExceptionAction + Process + ProcessBuilder + ProcessingInstruction + ProfileDataException + ProfileIdHelper + ProgressBarUI + ProgressMonitor + ProgressMonitorInputStream + Properties + PropertyChangeEvent + PropertyChangeListener + PropertyChangeListenerProxy + PropertyChangeSupport + PropertyDescriptor + PropertyEditor + PropertyEditorManager + PropertyEditorSupport + PropertyPermission + PropertyResourceBundle + PropertyVetoException + ProtectionDomain + ProtocolException + Provider + Provider.Service + ProviderException + Proxy + Proxy + Proxy.Type + ProxySelector + PublicKey + PushbackInputStream + PushbackReader + QName + QuadCurve2D + QuadCurve2D.Double + QuadCurve2D.Float + Query + QueryEval + QueryExp + Queue + QueuedJobCount + RC2ParameterSpec + RC5ParameterSpec + REBIND + REQUEST_PROCESSING_POLICY_ID + RGBImageFilter + RMIClassLoader + RMIClassLoaderSpi + RMIClientSocketFactory + RMIConnection + RMIConnectionImpl + RMIConnectionImpl_Stub + RMIConnector + RMIConnectorServer + RMICustomMaxStreamFormat + RMIFailureHandler + RMIIIOPServerImpl + RMIJRMPServerImpl + RMISecurityException + RMISecurityManager + RMIServer + RMIServerImpl + RMIServerImpl_Stub + RMIServerSocketFactory + RMISocketFactory + RSAKey + RSAKeyGenParameterSpec + RSAMultiPrimePrivateCrtKey + RSAMultiPrimePrivateCrtKeySpec + RSAOtherPrimeInfo + RSAPrivateCrtKey + RSAPrivateCrtKeySpec + RSAPrivateKey + RSAPrivateKeySpec + RSAPublicKey + RSAPublicKeySpec + RTFEditorKit + Random + RandomAccess + RandomAccessFile + Raster + RasterFormatException + RasterOp + Rdn + ReadOnlyBufferException + ReadWriteLock + Readable + ReadableByteChannel + Reader + RealmCallback + RealmChoiceCallback + Receiver + Rectangle + Rectangle2D + Rectangle2D.Double + Rectangle2D.Float + RectangularShape + ReentrantLock + ReentrantReadWriteLock + ReentrantReadWriteLock.ReadLock + ReentrantReadWriteLock.WriteLock + Ref + RefAddr + Reference + Reference + ReferenceQueue + ReferenceUriSchemesSupported + Referenceable + ReferralException + ReflectPermission + ReflectionException + RefreshFailedException + Refreshable + Region + RegisterableService + Registry + RegistryHandler + RejectedExecutionException + RejectedExecutionHandler + Relation + RelationException + RelationNotFoundException + RelationNotification + RelationService + RelationServiceMBean + RelationServiceNotRegisteredException + RelationSupport + RelationSupportMBean + RelationType + RelationTypeNotFoundException + RelationTypeSupport + RemarshalException + Remote + RemoteCall + RemoteException + RemoteObject + RemoteObjectInvocationHandler + RemoteRef + RemoteServer + RemoteStub + RenderContext + RenderableImage + RenderableImageOp + RenderableImageProducer + RenderedImage + RenderedImageFactory + Renderer + RenderingHints + RenderingHints.Key + RepaintManager + ReplicateScaleFilter + RepositoryIdHelper + Request + RequestInfo + RequestInfoOperations + RequestProcessingPolicy + RequestProcessingPolicyOperations + RequestProcessingPolicyValue + RequestingUserName + RequiredModelMBean + RescaleOp + ResolutionSyntax + ResolveResult + Resolver + ResourceBundle + ResponseCache + ResponseHandler + Result + ResultSet + ResultSetMetaData + Retention + RetentionPolicy + ReverbType + Robot + Role + RoleInfo + RoleInfoNotFoundException + RoleList + RoleNotFoundException + RoleResult + RoleStatus + RoleUnresolved + RoleUnresolvedList + RootPaneContainer + RootPaneUI + RoundRectangle2D + RoundRectangle2D.Double + RoundRectangle2D.Float + RoundingMode + RowMapper + RowSet + RowSetEvent + RowSetInternal + RowSetListener + RowSetMetaData + RowSetMetaDataImpl + RowSetReader + RowSetWarning + RowSetWriter + RuleBasedCollator + RunTime + RunTimeOperations + Runnable + Runtime + RuntimeErrorException + RuntimeException + RuntimeMBeanException + RuntimeMXBean + RuntimeOperationsException + RuntimePermission + SAXException + SAXNotRecognizedException + SAXNotSupportedException + SAXParseException + SAXParser + SAXParserFactory + SAXResult + SAXSource + SAXTransformerFactory + SERVANT_RETENTION_POLICY_ID + SQLData + SQLException + SQLInput + SQLInputImpl + SQLOutput + SQLOutputImpl + SQLPermission + SQLWarning + SSLContext + SSLContextSpi + SSLEngine + SSLEngineResult + SSLEngineResult.HandshakeStatus + SSLEngineResult.Status + SSLException + SSLHandshakeException + SSLKeyException + SSLPeerUnverifiedException + SSLPermission + SSLProtocolException + SSLServerSocket + SSLServerSocketFactory + SSLSession + SSLSessionBindingEvent + SSLSessionBindingListener + SSLSessionContext + SSLSocket + SSLSocketFactory + SUCCESSFUL + SYNC_WITH_TRANSPORT + SYSTEM_EXCEPTION + SampleModel + Sasl + SaslClient + SaslClientFactory + SaslException + SaslServer + SaslServerFactory + Savepoint + Scanner + ScatteringByteChannel + ScheduledExecutorService + ScheduledFuture + ScheduledThreadPoolExecutor + Schema + SchemaFactory + SchemaFactoryLoader + SchemaViolationException + ScrollBarUI + ScrollPane + ScrollPaneAdjustable + ScrollPaneConstants + ScrollPaneLayout + ScrollPaneLayout.UIResource + ScrollPaneUI + Scrollable + Scrollbar + SealedObject + SearchControls + SearchResult + SecretKey + SecretKeyFactory + SecretKeyFactorySpi + SecretKeySpec + SecureCacheResponse + SecureClassLoader + SecureRandom + SecureRandomSpi + Security + SecurityException + SecurityManager + SecurityPermission + Segment + SelectableChannel + SelectionKey + Selector + SelectorProvider + Semaphore + SeparatorUI + Sequence + SequenceInputStream + Sequencer + Sequencer.SyncMode + SerialArray + SerialBlob + SerialClob + SerialDatalink + SerialException + SerialJavaObject + SerialRef + SerialStruct + Serializable + SerializablePermission + Servant + ServantActivator + ServantActivatorHelper + ServantActivatorOperations + ServantActivatorPOA + ServantAlreadyActive + ServantAlreadyActiveHelper + ServantLocator + ServantLocatorHelper + ServantLocatorOperations + ServantLocatorPOA + ServantManager + ServantManagerOperations + ServantNotActive + ServantNotActiveHelper + ServantObject + ServantRetentionPolicy + ServantRetentionPolicyOperations + ServantRetentionPolicyValue + ServerCloneException + ServerError + ServerException + ServerIdHelper + ServerNotActiveException + ServerRef + ServerRequest + ServerRequestInfo + ServerRequestInfoOperations + ServerRequestInterceptor + ServerRequestInterceptorOperations + ServerRuntimeException + ServerSocket + ServerSocketChannel + ServerSocketFactory + ServiceContext + ServiceContextHelper + ServiceContextHolder + ServiceContextListHelper + ServiceContextListHolder + ServiceDetail + ServiceDetailHelper + ServiceIdHelper + ServiceInformation + ServiceInformationHelper + ServiceInformationHolder + ServiceNotFoundException + ServicePermission + ServiceRegistry + ServiceRegistry.Filter + ServiceUI + ServiceUIFactory + ServiceUnavailableException + Set + SetOfIntegerSyntax + SetOverrideType + SetOverrideTypeHelper + Severity + Shape + ShapeGraphicAttribute + SheetCollate + Short + ShortBuffer + ShortBufferException + ShortHolder + ShortLookupTable + ShortMessage + ShortSeqHelper + ShortSeqHolder + Sides + Signature + SignatureException + SignatureSpi + SignedObject + Signer + SimpleAttributeSet + SimpleBeanInfo + SimpleDateFormat + SimpleDoc + SimpleFormatter + SimpleTimeZone + SimpleType + SinglePixelPackedSampleModel + SingleSelectionModel + Size2DSyntax + SizeLimitExceededException + SizeRequirements + SizeSequence + Skeleton + SkeletonMismatchException + SkeletonNotFoundException + SliderUI + Socket + SocketAddress + SocketChannel + SocketException + SocketFactory + SocketHandler + SocketImpl + SocketImplFactory + SocketOptions + SocketPermission + SocketSecurityException + SocketTimeoutException + SoftBevelBorder + SoftReference + SortControl + SortKey + SortResponseControl + SortedMap + SortedSet + SortingFocusTraversalPolicy + Soundbank + SoundbankReader + SoundbankResource + Source + SourceDataLine + SourceLocator + SpinnerDateModel + SpinnerListModel + SpinnerModel + SpinnerNumberModel + SpinnerUI + SplitPaneUI + Spring + SpringLayout + SpringLayout.Constraints + SslRMIClientSocketFactory + SslRMIServerSocketFactory + Stack + StackOverflowError + StackTraceElement + StandardMBean + StartTlsRequest + StartTlsResponse + State + StateEdit + StateEditable + StateFactory + Statement + Statement + StreamCorruptedException + StreamHandler + StreamPrintService + StreamPrintServiceFactory + StreamResult + StreamSource + StreamTokenizer + Streamable + StreamableValue + StrictMath + String + StringBuffer + StringBufferInputStream + StringBuilder + StringCharacterIterator + StringContent + StringHolder + StringIndexOutOfBoundsException + StringMonitor + StringMonitorMBean + StringNameHelper + StringReader + StringRefAddr + StringSelection + StringSeqHelper + StringSeqHolder + StringTokenizer + StringValueExp + StringValueHelper + StringWriter + Stroke + Struct + StructMember + StructMemberHelper + Stub + StubDelegate + StubNotFoundException + Style + StyleConstants + StyleConstants.CharacterConstants + StyleConstants.ColorConstants + StyleConstants.FontConstants + StyleConstants.ParagraphConstants + StyleContext + StyleSheet + StyleSheet.BoxPainter + StyleSheet.ListPainter + StyledDocument + StyledEditorKit + StyledEditorKit.AlignmentAction + StyledEditorKit.BoldAction + StyledEditorKit.FontFamilyAction + StyledEditorKit.FontSizeAction + StyledEditorKit.ForegroundAction + StyledEditorKit.ItalicAction + StyledEditorKit.StyledTextAction + StyledEditorKit.UnderlineAction + Subject + SubjectDelegationPermission + SubjectDomainCombiner + SupportedValuesAttribute + SuppressWarnings + SwingConstants + SwingPropertyChangeSupport + SwingUtilities + SyncFactory + SyncFactoryException + SyncFailedException + SyncProvider + SyncProviderException + SyncResolver + SyncScopeHelper + SynchronousQueue + SynthConstants + SynthContext + SynthGraphicsUtils + SynthLookAndFeel + SynthPainter + SynthStyle + SynthStyleFactory + Synthesizer + SysexMessage + System + SystemColor + SystemException + SystemFlavorMap + TAG_ALTERNATE_IIOP_ADDRESS + TAG_CODE_SETS + TAG_INTERNET_IOP + TAG_JAVA_CODEBASE + TAG_MULTIPLE_COMPONENTS + TAG_ORB_TYPE + TAG_POLICIES + TAG_RMI_CUSTOM_MAX_STREAM_FORMAT + TCKind + THREAD_POLICY_ID + TIMEOUT + TRANSACTION_MODE + TRANSACTION_REQUIRED + TRANSACTION_ROLLEDBACK + TRANSACTION_UNAVAILABLE + TRANSIENT + TRANSPORT_RETRY + TabExpander + TabSet + TabStop + TabableView + TabbedPaneUI + TableCellEditor + TableCellRenderer + TableColumn + TableColumnModel + TableColumnModelEvent + TableColumnModelListener + TableHeaderUI + TableModel + TableModelEvent + TableModelListener + TableUI + TableView + TabularData + TabularDataSupport + TabularType + TagElement + TaggedComponent + TaggedComponentHelper + TaggedComponentHolder + TaggedProfile + TaggedProfileHelper + TaggedProfileHolder + Target + TargetDataLine + TargetedNotification + Templates + TemplatesHandler + Text + TextAction + TextArea + TextAttribute + TextComponent + TextEvent + TextField + TextHitInfo + TextInputCallback + TextLayout + TextLayout.CaretPolicy + TextListener + TextMeasurer + TextOutputCallback + TextSyntax + TextUI + TexturePaint + Thread + Thread.State + Thread.UncaughtExceptionHandler + ThreadDeath + ThreadFactory + ThreadGroup + ThreadInfo + ThreadLocal + ThreadMXBean + ThreadPolicy + ThreadPolicyOperations + ThreadPolicyValue + ThreadPoolExecutor + ThreadPoolExecutor.AbortPolicy + ThreadPoolExecutor.CallerRunsPolicy + ThreadPoolExecutor.DiscardOldestPolicy + ThreadPoolExecutor.DiscardPolicy + Throwable + Tie + TileObserver + Time + TimeLimitExceededException + TimeUnit + TimeZone + TimeoutException + Timer + Timer + Timer + TimerAlarmClockNotification + TimerMBean + TimerNotification + TimerTask + Timestamp + Timestamp + TitledBorder + TooManyListenersException + ToolBarUI + ToolTipManager + ToolTipUI + Toolkit + Track + TransactionRequiredException + TransactionRolledbackException + TransactionService + TransactionalWriter + TransferHandler + Transferable + TransformAttribute + Transformer + TransformerConfigurationException + TransformerException + TransformerFactory + TransformerFactoryConfigurationError + TransformerHandler + Transmitter + Transparency + TreeCellEditor + TreeCellRenderer + TreeExpansionEvent + TreeExpansionListener + TreeMap + TreeModel + TreeModelEvent + TreeModelListener + TreeNode + TreePath + TreeSelectionEvent + TreeSelectionListener + TreeSelectionModel + TreeSet + TreeUI + TreeWillExpandListener + TrustAnchor + TrustManager + TrustManagerFactory + TrustManagerFactorySpi + Type + TypeCode + TypeCodeHolder + TypeInfo + TypeInfoProvider + TypeMismatch + TypeMismatch + TypeMismatch + TypeMismatchHelper + TypeMismatchHelper + TypeNotPresentException + TypeVariable + Types + UID + UIDefaults + UIDefaults.ActiveValue + UIDefaults.LazyInputMap + UIDefaults.LazyValue + UIDefaults.ProxyLazyValue + UIManager + UIManager.LookAndFeelInfo + UIResource + ULongLongSeqHelper + ULongLongSeqHolder + ULongSeqHelper + ULongSeqHolder + UNKNOWN + UNKNOWN + UNSUPPORTED_POLICY + UNSUPPORTED_POLICY_VALUE + URI + URIException + URIResolver + URISyntax + URISyntaxException + URL + URLClassLoader + URLConnection + URLDecoder + URLEncoder + URLStreamHandler + URLStreamHandlerFactory + URLStringHelper + USER_EXCEPTION + UShortSeqHelper + UShortSeqHolder + UTFDataFormatException + UUID + UndeclaredThrowableException + UndoManager + UndoableEdit + UndoableEditEvent + UndoableEditListener + UndoableEditSupport + UnexpectedException + UnicastRemoteObject + UnionMember + UnionMemberHelper + UnknownEncoding + UnknownEncodingHelper + UnknownError + UnknownException + UnknownFormatConversionException + UnknownFormatFlagsException + UnknownGroupException + UnknownHostException + UnknownHostException + UnknownObjectException + UnknownServiceException + UnknownUserException + UnknownUserExceptionHelper + UnknownUserExceptionHolder + UnmappableCharacterException + UnmarshalException + UnmodifiableClassException + UnmodifiableSetException + UnrecoverableEntryException + UnrecoverableKeyException + Unreferenced + UnresolvedAddressException + UnresolvedPermission + UnsatisfiedLinkError + UnsolicitedNotification + UnsolicitedNotificationEvent + UnsolicitedNotificationListener + UnsupportedAddressTypeException + UnsupportedAudioFileException + UnsupportedCallbackException + UnsupportedCharsetException + UnsupportedClassVersionError + UnsupportedEncodingException + UnsupportedFlavorException + UnsupportedLookAndFeelException + UnsupportedOperationException + UserDataHandler + UserException + Util + UtilDelegate + Utilities + VMID + VM_ABSTRACT + VM_CUSTOM + VM_NONE + VM_TRUNCATABLE + Validator + ValidatorHandler + ValueBase + ValueBaseHelper + ValueBaseHolder + ValueExp + ValueFactory + ValueHandler + ValueHandlerMultiFormat + ValueInputStream + ValueMember + ValueMemberHelper + ValueOutputStream + VariableHeightLayoutCache + Vector + VerifyError + VersionSpecHelper + VetoableChangeListener + VetoableChangeListenerProxy + VetoableChangeSupport + View + ViewFactory + ViewportLayout + ViewportUI + VirtualMachineError + Visibility + VisibilityHelper + VoiceStatus + Void + VolatileImage + WCharSeqHelper + WCharSeqHolder + WStringSeqHelper + WStringSeqHolder + WStringValueHelper + WeakHashMap + WeakReference + WebRowSet + WildcardType + Window + WindowAdapter + WindowConstants + WindowEvent + WindowFocusListener + WindowListener + WindowStateListener + WrappedPlainView + WritableByteChannel + WritableRaster + WritableRenderedImage + WriteAbortedException + Writer + WrongAdapter + WrongAdapterHelper + WrongPolicy + WrongPolicyHelper + WrongTransaction + WrongTransactionHelper + WrongTransactionHolder + X500Principal + X500PrivateCredential + X509CRL + X509CRLEntry + X509CRLSelector + X509CertSelector + X509Certificate + X509Certificate + X509EncodedKeySpec + X509ExtendedKeyManager + X509Extension + X509KeyManager + X509TrustManager + XAConnection + XADataSource + XAException + XAResource + XMLConstants + XMLDecoder + XMLEncoder + XMLFilter + XMLFilterImpl + XMLFormatter + XMLGregorianCalendar + XMLParseException + XMLReader + XMLReaderAdapter + XMLReaderFactory + XPath + XPathConstants + XPathException + XPathExpression + XPathExpressionException + XPathFactory + XPathFactoryConfigurationException + XPathFunction + XPathFunctionException + XPathFunctionResolver + XPathVariableResolver + Xid + XmlReader + XmlWriter + ZipEntry + ZipException + ZipFile + ZipInputStream + ZipOutputStream + ZoneView + _BindingIteratorImplBase + _BindingIteratorStub + _DynAnyFactoryStub + _DynAnyStub + _DynArrayStub + _DynEnumStub + _DynFixedStub + _DynSequenceStub + _DynStructStub + _DynUnionStub + _DynValueStub + _IDLTypeStub + _NamingContextExtStub + _NamingContextImplBase + _NamingContextStub + _PolicyStub + _Remote_Stub + _ServantActivatorStub + _ServantLocatorStub + + + + AbstractAnnotationValueVisitor6 + AbstractElementVisitor6 + AbstractMarshallerImpl + AbstractOwnableSynchronizer + AbstractProcessor + AbstractQueuedLongSynchronizer + AbstractScriptEngine + AbstractTypeVisitor6 + AbstractUnmarshallerImpl + ActivationDataFlavor + AlgorithmMethod + AnnotationMirror + AnnotationValue + AnnotationValueVisitor + ArrayDeque + AsyncHandler + AttachmentMarshaller + AttachmentPart + AttachmentUnmarshaller + Binder + BindingProvider + Bindings + BlockingDeque + BreakIteratorProvider + C14NMethodParameterSpec + CanonicalizationMethod + Characters + ClientInfoStatus + CollapsedStringAdapter + CollatorProvider + CommandInfo + CommandMap + CommandObject + CommonDataSource + Compilable + CompiledScript + Completion + Completions + CompositeDataInvocationHandler + CompositeDataView + ConcurrentNavigableMap + ConcurrentSkipListMap + ConcurrentSkipListSet + ConfigurationSpi + Console + ConstructorProperties + CookieManager + CookiePolicy + CookieStore + CurrencyNameProvider + Data + DataContentHandler + DataContentHandlerFactory + DataHandler + DatatypeConverter + DatatypeConverterInterface + DateFormatProvider + DateFormatSymbolsProvider + DecimalFormatSymbolsProvider + DeclaredType + DefaultRowSorter + DefaultValidationEventHandler + DeflaterInputStream + Deque + DescriptorKey + DescriptorRead + Desktop + Detail + DetailEntry + Diagnostic + DiagnosticCollector + DiagnosticListener + DigestMethod + DigestMethodParameterSpec + Dispatch + DOMCryptoContext + DomHandler + DOMSignContext + DOMStructure + DOMURIReference + DOMValidateContext + DropMode + ElementFilter + ElementKind + ElementKindVisitor6 + Elements + ElementScanner6 + ElementVisitor + EndDocument + EndElement + Endpoint + EntityDeclaration + ErrorType + EventException + EventFilter + EventReaderDelegate + EventTarget + ExcC14NParameterSpec + ExecutableElement + ExecutableType + FileDataSource + FileNameExtensionFilter + FileObject + Filer + FilerException + FileTypeMap + ForwardingFileObject + ForwardingJavaFileManager + ForwardingJavaFileObject + Generated + GridBagLayoutInfo + GroupLayout + HandlerChain + HandlerResolver + HexBinaryAdapter + HMACParameterSpec + Holder + HTTPBinding + HttpCookie + HTTPException + IDN + ImmutableDescriptor + InflaterOutputStream + InitParam + InterfaceAddress + Invocable + IOError + JavaCompiler + JavaFileManager + JavaFileObject + JAXBContext + JAXBElement + JAXBException + JAXBIntrospector + JAXBResult + JAXBSource + JMX + JMXAddressable + KeyInfo + KeyInfoFactory + KeyName + KeySelector + KeySelectorException + KeySelectorResult + KeyValue + LayoutPath + LayoutStyle + LinearGradientPaint + LinkedBlockingDeque + LocaleNameProvider + LocaleServiceProvider + Location + LockInfo + LogicalHandler + LogicalMessage + LogicalMessageContext + MailcapCommandMap + Marshaller + MessageContext + MessageFactory + Messager + MimeHeader + MimeHeaders + MimeType + MimeTypeParameterList + MimetypesFileTypeMap + MirroredTypeException + MirroredTypesException + MLetContent + MonitorInfo + MultipleGradientPaint + MutationEvent + MXBean + Namespace + NavigableMap + NavigableSet + NClob + NestingKind + NodeSetData + NormalizedStringAdapter + Normalizer + NoSuchMechanismException + NotationDeclaration + NotIdentifiableEvent + NotIdentifiableEventImpl + NoType + NullType + NumberFormatProvider + OctetStreamData + Oneway + OptionChecker + PackageElement + ParseConversionEvent + ParseConversionEventImpl + Path2D + PGPData + PolicySpi + PortInfo + PostConstruct + PreDestroy + PrimitiveType + PrintConversionEvent + PrintConversionEventImpl + ProcessingEnvironment + Processor + PropertyException + RadialGradientPaint + ReferenceType + RequestWrapper + Resource + Resources + Response + ResponseWrapper + RetrievalMethod + RoundEnvironment + RowFilter + RowId + RowIdLifetime + RowSorter + RowSorterEvent + RowSorterListener + RunnableFuture + RunnableScheduledFuture + SAAJMetaFactory + SAAJResult + SchemaOutputResolver + ScriptContext + ScriptEngine + ScriptEngineFactory + ScriptEngineManager + ScriptException + Service + ServiceConfigurationError + ServiceDelegate + ServiceLoader + ServiceMode + SignatureMethod + SignatureMethodParameterSpec + SignatureProperties + SignatureProperty + SignedInfo + SimpleAnnotationValueVisitor6 + SimpleBindings + SimpleElementVisitor6 + SimpleJavaFileObject + SimpleScriptContext + SimpleTypeVisitor6 + SOAPBinding + SOAPBinding + SOAPBody + SOAPBodyElement + SOAPConnection + SOAPConnectionFactory + SOAPConstants + SOAPElement + SOAPElementFactory + SOAPEnvelope + SOAPException + SOAPFactory + SOAPFault + SOAPFaultElement + SOAPFaultException + SOAPHandler + SOAPHeader + SOAPHeaderElement + SOAPMessage + SOAPMessageContext + SOAPMessageHandler + SOAPMessageHandlers + SOAPPart + SortOrder + SourceVersion + SplashScreen + SQLClientInfoException + SQLDataException + SQLFeatureNotSupportedException + SQLIntegrityConstraintViolationException + SQLInvalidAuthorizationSpecException + SQLNonTransientConnectionException + SQLNonTransientException + SQLRecoverableException + SQLSyntaxErrorException + SQLTimeoutException + SQLTransactionRollbackException + SQLTransientConnectionException + SQLTransientException + SQLXML + SSLParameters + StandardEmitterMBean + StandardJavaFileManager + StandardLocation + StartDocument + StartElement + StatementEvent + StatementEventListener + StAXResult + StAXSource + StreamFilter + StreamReaderDelegate + SupportedAnnotationTypes + SupportedOptions + SupportedSourceVersion + SwingWorker + SystemTray + TableRowSorter + TableStringConverter + TimeZoneNameProvider + Tool + ToolProvider + Transform + TransformException + TransformParameterSpec + TransformService + TrayIcon + TypeConstraintException + TypeElement + TypeKind + TypeKindVisitor6 + TypeMirror + TypeParameterElement + TypeVisitor + UIEvent + UnknownAnnotationValueException + UnknownElementException + UnknownTypeException + Unmarshaller + UnmarshallerHandler + UnsupportedDataTypeException + URIDereferencer + URIParameter + URIReference + URIReferenceException + URLDataSource + ValidationEvent + ValidationEventCollector + ValidationEventHandler + ValidationEventImpl + ValidationEventLocator + ValidationEventLocatorImpl + ValidationException + VariableElement + W3CDomHandler + WebEndpoint + WebFault + WebMethod + WebParam + WebResult + WebService + WebServiceClient + WebServiceContext + WebServiceException + WebServicePermission + WebServiceProvider + WebServiceRef + WebServiceRefs + Wrapper + X509Data + X509IssuerSerial + XmlAccessOrder + XmlAccessorOrder + XmlAccessorType + XmlAccessType + XmlAdapter + XmlAnyAttribute + XmlAnyElement + XmlAttachmentRef + XmlAttribute + XMLCryptoContext + XmlElement + XmlElementDecl + XmlElementRef + XmlElementRefs + XmlElements + XmlElementWrapper + XmlEnum + XmlEnumValue + XMLEvent + XMLEventAllocator + XMLEventConsumer + XMLEventFactory + XMLEventReader + XMLEventWriter + XmlID + XmlIDREF + XmlInlineBinaryData + XMLInputFactory + XmlJavaTypeAdapter + XmlJavaTypeAdapters + XmlList + XmlMimeType + XmlMixed + XmlNs + XmlNsForm + XMLObject + XMLOutputFactory + XmlRegistry + XMLReporter + XMLResolver + XmlRootElement + XmlSchema + XmlSchemaType + XmlSchemaTypes + XMLSignature + XMLSignatureException + XMLSignatureFactory + XMLSignContext + XMLStreamConstants + XMLStreamException + XMLStreamReader + XMLStreamWriter + XMLStructure + XmlTransient + XmlType + XMLValidateContext + XmlValue + XPathFilter2ParameterSpec + XPathFilterParameterSpec + XPathType + XSLTTransformParameterSpec + ZipError + + + + + abstract + break + case + catch + class + continue + default + do + else + enum + extends + false + finally + for + goto + if + implements + instanceof + @interface + interface + native + new + null + private + protected + public + return + super + strictfp + switch + synchronized + this + throws + throw + transient + true + try + volatile + while + + + boolean + byte + char + const + double + final + float + int + long + short + static + void + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/javadoc.xml b/kate/part/syntax/data/javadoc.xml new file mode 100644 index 00000000..2fa40ea9 --- /dev/null +++ b/kate/part/syntax/data/javadoc.xml @@ -0,0 +1,108 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/javascript-php.xml b/kate/part/syntax/data/javascript-php.xml new file mode 100644 index 00000000..cd20480e --- /dev/null +++ b/kate/part/syntax/data/javascript-php.xml @@ -0,0 +1,252 @@ + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/javascript.xml b/kate/part/syntax/data/javascript.xml new file mode 100644 index 00000000..c9b5f4bc --- /dev/null +++ b/kate/part/syntax/data/javascript.xml @@ -0,0 +1,225 @@ + + + + + + + + + break + case + catch + const + continue + debugger + default + delete + do + else + finally + for + function + if + in + instanceof + new + return + switch + this + throw + try + typeof + var + void + while + with + + + class + enum + export + extends + import + super + + + implements + interface + let + package + private + protected + public + static + yield + + + Infinity + NaN + false + null + true + undefined + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/jira.xml b/kate/part/syntax/data/jira.xml new file mode 100644 index 00000000..90f4d23a --- /dev/null +++ b/kate/part/syntax/data/jira.xml @@ -0,0 +1,293 @@ + + + + + + + + + + +]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/json.xml b/kate/part/syntax/data/json.xml new file mode 100644 index 00000000..ca03e11c --- /dev/null +++ b/kate/part/syntax/data/json.xml @@ -0,0 +1,104 @@ + + + + + + + null + true + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/jsp.xml b/kate/part/syntax/data/jsp.xml new file mode 100644 index 00000000..da448227 --- /dev/null +++ b/kate/part/syntax/data/jsp.xml @@ -0,0 +1,3070 @@ + + + + + + + ARG_IN + ARG_INOUT + ARG_OUT + AWTError + AWTEvent + AWTEventListener + AWTEventListenerProxy + AWTEventMulticaster + AWTException + AWTKeyStroke + AWTPermission + AbstractAction + AbstractBorder + AbstractButton + AbstractCellEditor + AbstractCollection + AbstractColorChooserPanel + AbstractDocument + AbstractFormatter + AbstractFormatterFactory + AbstractInterruptibleChannel + AbstractLayoutCache + AbstractList + AbstractListModel + AbstractMap + AbstractMethodError + AbstractPreferences + AbstractSelectableChannel + AbstractSelectionKey + AbstractSelector + AbstractSequentialList + AbstractSet + AbstractSpinnerModel + AbstractTableModel + AbstractUndoableEdit + AbstractWriter + AccessControlContext + AccessControlException + AccessController + AccessException + Accessible + AccessibleAction + AccessibleBundle + AccessibleComponent + AccessibleContext + AccessibleEditableText + AccessibleExtendedComponent + AccessibleExtendedTable + AccessibleHyperlink + AccessibleHypertext + AccessibleIcon + AccessibleKeyBinding + AccessibleObject + AccessibleRelation + AccessibleRelationSet + AccessibleResourceBundle + AccessibleRole + AccessibleSelection + AccessibleState + AccessibleStateSet + AccessibleTable + AccessibleTableModelChange + AccessibleText + AccessibleValue + AccountExpiredException + Acl + AclEntry + AclNotFoundException + Action + ActionEvent + ActionListener + ActionMap + ActionMapUIResource + Activatable + ActivateFailedException + ActivationDesc + ActivationException + ActivationGroup + ActivationGroupDesc + ActivationGroupID + ActivationGroup_Stub + ActivationID + ActivationInstantiator + ActivationMonitor + ActivationSystem + Activator + ActiveEvent + ActiveValue + AdapterActivator + AdapterActivatorOperations + AdapterAlreadyExists + AdapterAlreadyExistsHelper + AdapterInactive + AdapterInactiveHelper + AdapterNonExistent + AdapterNonExistentHelper + AddressHelper + Adjustable + AdjustmentEvent + AdjustmentListener + Adler32 + AffineTransform + AffineTransformOp + AlgorithmParameterGenerator + AlgorithmParameterGeneratorSpi + AlgorithmParameterSpec + AlgorithmParameters + AlgorithmParametersSpi + AlignmentAction + AllPermission + AlphaComposite + AlreadyBound + AlreadyBoundException + AlreadyBoundHelper + AlreadyBoundHolder + AlreadyConnectedException + AncestorEvent + AncestorListener + Annotation + Any + AnyHolder + AnySeqHelper + AnySeqHelper + AnySeqHolder + AppConfigurationEntry + Applet + AppletContext + AppletInitializer + AppletStub + ApplicationException + Arc2D + Area + AreaAveragingScaleFilter + ArithmeticException + Array + Array + ArrayIndexOutOfBoundsException + ArrayList + ArrayStoreException + Arrays + AssertionError + AsyncBoxView + AsynchronousCloseException + Attr + Attribute + Attribute + Attribute + Attribute + Attribute + AttributeContext + AttributeException + AttributeInUseException + AttributeList + AttributeList + AttributeListImpl + AttributeModificationException + AttributeSet + AttributeSet + AttributeSetUtilities + AttributeUndoableEdit + AttributedCharacterIterator + AttributedString + Attributes + Attributes + Attributes + AttributesImpl + AudioClip + AudioFileFormat + AudioFileReader + AudioFileWriter + AudioFormat + AudioInputStream + AudioPermission + AudioSystem + AuthPermission + AuthenticationException + AuthenticationNotSupportedException + Authenticator + Autoscroll + BAD_CONTEXT + BAD_INV_ORDER + BAD_OPERATION + BAD_PARAM + BAD_POLICY + BAD_POLICY_TYPE + BAD_POLICY_VALUE + BAD_TYPECODE + BCSIterator + BCSSServiceProvider + BYTE_ARRAY + BackingStoreException + BadKind + BadLocationException + BadPaddingException + BandCombineOp + BandedSampleModel + BasicArrowButton + BasicAttribute + BasicAttributes + BasicBorders + BasicButtonListener + BasicButtonUI + BasicCaret + BasicCheckBoxMenuItemUI + BasicCheckBoxUI + BasicColorChooserUI + BasicComboBoxEditor + BasicComboBoxRenderer + BasicComboBoxUI + BasicComboPopup + BasicDesktopIconUI + BasicDesktopPaneUI + BasicDirectoryModel + BasicEditorPaneUI + BasicFileChooserUI + BasicFormattedTextFieldUI + BasicGraphicsUtils + BasicHTML + BasicHighlighter + BasicIconFactory + BasicInternalFrameTitlePane + BasicInternalFrameUI + BasicLabelUI + BasicListUI + BasicLookAndFeel + BasicMenuBarUI + BasicMenuItemUI + BasicMenuUI + BasicOptionPaneUI + BasicPanelUI + BasicPasswordFieldUI + BasicPermission + BasicPopupMenuSeparatorUI + BasicPopupMenuUI + BasicProgressBarUI + BasicRadioButtonMenuItemUI + BasicRadioButtonUI + BasicRootPaneUI + BasicScrollBarUI + BasicScrollPaneUI + BasicSeparatorUI + BasicSliderUI + BasicSpinnerUI + BasicSplitPaneDivider + BasicSplitPaneUI + BasicStroke + BasicTabbedPaneUI + BasicTableHeaderUI + BasicTableUI + BasicTextAreaUI + BasicTextFieldUI + BasicTextPaneUI + BasicTextUI + BasicToggleButtonUI + BasicToolBarSeparatorUI + BasicToolBarUI + BasicToolTipUI + BasicTreeUI + BasicViewportUI + BatchUpdateException + BeanContext + BeanContextChild + BeanContextChildComponentProxy + BeanContextChildSupport + BeanContextContainerProxy + BeanContextEvent + BeanContextMembershipEvent + BeanContextMembershipListener + BeanContextProxy + BeanContextServiceAvailableEvent + BeanContextServiceProvider + BeanContextServiceProviderBeanInfo + BeanContextServiceRevokedEvent + BeanContextServiceRevokedListener + BeanContextServices + BeanContextServicesListener + BeanContextServicesSupport + BeanContextSupport + BeanDescriptor + BeanInfo + Beans + BeepAction + BevelBorder + BevelBorderUIResource + Bias + Bidi + BigDecimal + BigInteger + BinaryRefAddr + BindException + Binding + Binding + BindingHelper + BindingHolder + BindingIterator + BindingIteratorHelper + BindingIteratorHolder + BindingIteratorOperations + BindingIteratorPOA + BindingListHelper + BindingListHolder + BindingType + BindingTypeHelper + BindingTypeHolder + BitSet + Blob + BlockView + BoldAction + Book + Boolean + BooleanControl + BooleanHolder + BooleanSeqHelper + BooleanSeqHolder + Border + BorderFactory + BorderLayout + BorderUIResource + BoundedRangeModel + Bounds + Bounds + Box + BoxLayout + BoxPainter + BoxView + BoxedValueHelper + BreakIterator + Buffer + BufferCapabilities + BufferOverflowException + BufferStrategy + BufferUnderflowException + BufferedImage + BufferedImageFilter + BufferedImageOp + BufferedInputStream + BufferedOutputStream + BufferedReader + BufferedWriter + Button + ButtonAreaLayout + ButtonBorder + ButtonBorder + ButtonGroup + ButtonModel + ButtonUI + Byte + ByteArrayInputStream + ByteArrayOutputStream + ByteBuffer + ByteChannel + ByteHolder + ByteLookupTable + ByteOrder + CDATASection + CHAR_ARRAY + CMMException + COMM_FAILURE + CRC32 + CRL + CRLException + CRLSelector + CSS + CTX_RESTRICT_SCOPE + Calendar + CallableStatement + Callback + CallbackHandler + CancelablePrintJob + CancelledKeyException + CannotProceed + CannotProceedException + CannotProceedHelper + CannotProceedHolder + CannotRedoException + CannotUndoException + Canvas + CardLayout + Caret + CaretEvent + CaretListener + CaretPolicy + CellEditor + CellEditorListener + CellRendererPane + CertPath + CertPathBuilder + CertPathBuilderException + CertPathBuilderResult + CertPathBuilderSpi + CertPathParameters + CertPathRep + CertPathValidator + CertPathValidatorException + CertPathValidatorResult + CertPathValidatorSpi + CertSelector + CertStore + CertStoreException + CertStoreParameters + CertStoreSpi + Certificate + Certificate + Certificate + CertificateEncodingException + CertificateEncodingException + CertificateException + CertificateException + CertificateExpiredException + CertificateExpiredException + CertificateFactory + CertificateFactorySpi + CertificateNotYetValidException + CertificateNotYetValidException + CertificateParsingException + CertificateParsingException + CertificateRep + ChangeEvent + ChangeListener + ChangedCharSetException + Channel + ChannelBinding + Channels + CharArrayReader + CharArrayWriter + CharBuffer + CharConversionException + CharHolder + CharSeqHelper + CharSeqHolder + CharSequence + Character + CharacterAttribute + CharacterCodingException + CharacterConstants + CharacterData + CharacterIterator + Charset + CharsetDecoder + CharsetEncoder + CharsetProvider + Checkbox + CheckboxGroup + CheckboxMenuItem + CheckedInputStream + CheckedOutputStream + Checksum + Choice + ChoiceCallback + ChoiceFormat + Chromaticity + Cipher + CipherInputStream + CipherOutputStream + CipherSpi + Class + ClassCastException + ClassCircularityError + ClassDesc + ClassFormatError + ClassLoader + ClassNotFoundException + ClientRequestInfo + ClientRequestInfoOperations + ClientRequestInterceptor + ClientRequestInterceptorOperations + Clip + Clipboard + ClipboardOwner + Clob + CloneNotSupportedException + Cloneable + ClosedByInterruptException + ClosedChannelException + ClosedSelectorException + CodeSets + CodeSource + Codec + CodecFactory + CodecFactoryHelper + CodecFactoryOperations + CodecOperations + CoderMalfunctionError + CoderResult + CodingErrorAction + CollationElementIterator + CollationKey + Collator + Collection + CollectionCertStoreParameters + Collections + Color + ColorAttribute + ColorChooserComponentFactory + ColorChooserUI + ColorConstants + ColorConvertOp + ColorModel + ColorSelectionModel + ColorSpace + ColorSupported + ColorType + ColorUIResource + ComboBoxEditor + ComboBoxModel + ComboBoxUI + ComboPopup + CommandEnvironment + Comment + CommunicationException + Comparable + Comparator + Compiler + CompletionStatus + CompletionStatusHelper + Component + ComponentAdapter + ComponentColorModel + ComponentEvent + ComponentIdHelper + ComponentInputMap + ComponentInputMapUIResource + ComponentListener + ComponentOrientation + ComponentSampleModel + ComponentUI + ComponentView + Composite + CompositeContext + CompositeName + CompositeView + CompoundBorder + CompoundBorderUIResource + CompoundControl + CompoundEdit + CompoundName + Compression + ConcurrentModificationException + Configuration + ConfigurationException + ConfirmationCallback + ConnectException + ConnectException + ConnectIOException + Connection + ConnectionEvent + ConnectionEventListener + ConnectionPendingException + ConnectionPoolDataSource + ConsoleHandler + Constraints + Constructor + Container + ContainerAdapter + ContainerEvent + ContainerListener + ContainerOrderFocusTraversalPolicy + Content + ContentHandler + ContentHandler + ContentHandlerFactory + ContentModel + Context + Context + ContextList + ContextNotEmptyException + ContextualRenderedImageFactory + Control + Control + ControlFactory + ControllerEventListener + ConvolveOp + CookieHolder + Copies + CopiesSupported + CopyAction + CredentialExpiredException + CropImageFilter + CubicCurve2D + Currency + Current + Current + Current + CurrentHelper + CurrentHelper + CurrentHelper + CurrentHolder + CurrentOperations + CurrentOperations + CurrentOperations + Cursor + CustomMarshal + CustomValue + Customizer + CutAction + DATA_CONVERSION + DESKeySpec + DESedeKeySpec + DGC + DHGenParameterSpec + DHKey + DHParameterSpec + DHPrivateKey + DHPrivateKeySpec + DHPublicKey + DHPublicKeySpec + DOMException + DOMImplementation + DOMLocator + DOMResult + DOMSource + DSAKey + DSAKeyPairGenerator + DSAParameterSpec + DSAParams + DSAPrivateKey + DSAPrivateKeySpec + DSAPublicKey + DSAPublicKeySpec + DTD + DTDConstants + DTDHandler + DataBuffer + DataBufferByte + DataBufferDouble + DataBufferFloat + DataBufferInt + DataBufferShort + DataBufferUShort + DataFlavor + DataFormatException + DataInput + DataInputStream + DataInputStream + DataLine + DataOutput + DataOutputStream + DataOutputStream + DataSource + DataTruncation + DatabaseMetaData + DatagramChannel + DatagramPacket + DatagramSocket + DatagramSocketImpl + DatagramSocketImplFactory + Date + Date + DateEditor + DateFormat + DateFormatSymbols + DateFormatter + DateTimeAtCompleted + DateTimeAtCreation + DateTimeAtProcessing + DateTimeSyntax + DebugGraphics + DecimalFormat + DecimalFormatSymbols + DeclHandler + DefaultBoundedRangeModel + DefaultButtonModel + DefaultCaret + DefaultCellEditor + DefaultColorSelectionModel + DefaultComboBoxModel + DefaultDesktopManager + DefaultEditor + DefaultEditorKit + DefaultFocusManager + DefaultFocusTraversalPolicy + DefaultFormatter + DefaultFormatterFactory + DefaultHandler + DefaultHighlightPainter + DefaultHighlighter + DefaultKeyTypedAction + DefaultKeyboardFocusManager + DefaultListCellRenderer + DefaultListModel + DefaultListSelectionModel + DefaultMenuLayout + DefaultMetalTheme + DefaultMutableTreeNode + DefaultPersistenceDelegate + DefaultSelectionType + DefaultSingleSelectionModel + DefaultStyledDocument + DefaultTableCellRenderer + DefaultTableColumnModel + DefaultTableModel + DefaultTextUI + DefaultTreeCellEditor + DefaultTreeCellRenderer + DefaultTreeModel + DefaultTreeSelectionModel + DefinitionKind + DefinitionKindHelper + Deflater + DeflaterOutputStream + Delegate + Delegate + Delegate + DelegationPermission + DesignMode + DesktopIconUI + DesktopManager + DesktopPaneUI + Destination + DestinationType + DestroyFailedException + Destroyable + Dialog + DialogType + Dictionary + DigestException + DigestInputStream + DigestOutputStream + Dimension + Dimension2D + DimensionUIResource + DirContext + DirObjectFactory + DirStateFactory + DirectColorModel + DirectoryManager + DisplayMode + DnDConstants + Doc + DocAttribute + DocAttributeSet + DocFlavor + DocPrintJob + Document + Document + DocumentBuilder + DocumentBuilderFactory + DocumentEvent + DocumentFilter + DocumentFragment + DocumentHandler + DocumentListener + DocumentName + DocumentParser + DocumentType + DomainCombiner + DomainManager + DomainManagerOperations + Double + Double + Double + Double + Double + Double + Double + Double + Double + DoubleBuffer + DoubleHolder + DoubleSeqHelper + DoubleSeqHolder + DragGestureEvent + DragGestureListener + DragGestureRecognizer + DragSource + DragSourceAdapter + DragSourceContext + DragSourceDragEvent + DragSourceDropEvent + DragSourceEvent + DragSourceListener + DragSourceMotionListener + Driver + DriverManager + DriverPropertyInfo + DropTarget + DropTargetAdapter + DropTargetAutoScroller + DropTargetContext + DropTargetDragEvent + DropTargetDropEvent + DropTargetEvent + DropTargetListener + DuplicateName + DuplicateNameHelper + DynAny + DynAny + DynAnyFactory + DynAnyFactoryHelper + DynAnyFactoryOperations + DynAnyHelper + DynAnyOperations + DynAnySeqHelper + DynArray + DynArray + DynArrayHelper + DynArrayOperations + DynEnum + DynEnum + DynEnumHelper + DynEnumOperations + DynFixed + DynFixed + DynFixedHelper + DynFixedOperations + DynSequence + DynSequence + DynSequenceHelper + DynSequenceOperations + DynStruct + DynStruct + DynStructHelper + DynStructOperations + DynUnion + DynUnion + DynUnionHelper + DynUnionOperations + DynValue + DynValue + DynValueBox + DynValueBoxOperations + DynValueCommon + DynValueCommonOperations + DynValueHelper + DynValueOperations + DynamicImplementation + DynamicImplementation + DynamicUtilTreeNode + ENCODING_CDR_ENCAPS + EOFException + EditorKit + Element + Element + Element + ElementChange + ElementEdit + ElementIterator + ElementSpec + Ellipse2D + EmptyBorder + EmptyBorderUIResource + EmptySelectionModel + EmptyStackException + EncodedKeySpec + Encoder + Encoding + Encoding + EncryptedPrivateKeyInfo + Engineering + Entity + Entity + EntityReference + EntityResolver + Entry + EnumControl + EnumSyntax + Enumeration + Environment + Error + ErrorHandler + ErrorListener + ErrorManager + EtchedBorder + EtchedBorderUIResource + Event + EventContext + EventDirContext + EventHandler + EventListener + EventListenerList + EventListenerProxy + EventObject + EventQueue + EventSetDescriptor + EventType + EventType + Exception + ExceptionInInitializerError + ExceptionList + ExceptionListener + ExemptionMechanism + ExemptionMechanismException + ExemptionMechanismSpi + ExpandVetoException + ExportException + Expression + ExtendedRequest + ExtendedResponse + Externalizable + FREE_MEM + FactoryConfigurationError + FailedLoginException + FeatureDescriptor + Fidelity + Field + Field + Field + Field + Field + FieldBorder + FieldNameHelper + FieldNameHelper + FieldPosition + FieldView + File + FileCacheImageInputStream + FileCacheImageOutputStream + FileChannel + FileChooserUI + FileDescriptor + FileDialog + FileFilter + FileFilter + FileHandler + FileIcon16 + FileImageInputStream + FileImageOutputStream + FileInputStream + FileLock + FileLockInterruptionException + FileNameMap + FileNotFoundException + FileOutputStream + FilePermission + FileReader + FileSystemView + FileView + FileWriter + FilenameFilter + Filler + Filter + Filter + FilterBypass + FilterBypass + FilterInputStream + FilterOutputStream + FilterReader + FilterWriter + FilteredImageSource + Finishings + FixedHeightLayoutCache + FixedHolder + FlatteningPathIterator + FlavorException + FlavorMap + FlavorTable + FlipContents + Float + Float + Float + Float + Float + Float + Float + Float + Float + FloatBuffer + FloatControl + FloatHolder + FloatSeqHelper + FloatSeqHolder + FlowLayout + FlowStrategy + FlowView + Flush3DBorder + FocusAdapter + FocusEvent + FocusListener + FocusManager + FocusTraversalPolicy + FolderIcon16 + Font + FontAttribute + FontConstants + FontFamilyAction + FontFormatException + FontMetrics + FontRenderContext + FontSizeAction + FontUIResource + ForegroundAction + FormView + Format + FormatConversionProvider + FormatMismatch + FormatMismatchHelper + Formatter + ForwardRequest + ForwardRequest + ForwardRequestHelper + ForwardRequestHelper + Frame + GSSContext + GSSCredential + GSSException + GSSManager + GSSName + GZIPInputStream + GZIPOutputStream + GapContent + GatheringByteChannel + GeneralPath + GeneralSecurityException + GetField + GlyphJustificationInfo + GlyphMetrics + GlyphPainter + GlyphVector + GlyphView + GradientPaint + GraphicAttribute + Graphics + Graphics2D + GraphicsConfigTemplate + GraphicsConfiguration + GraphicsDevice + GraphicsEnvironment + GrayFilter + GregorianCalendar + GridBagConstraints + GridBagLayout + GridLayout + Group + Guard + GuardedObject + HTML + HTMLDocument + HTMLEditorKit + HTMLEditorKit + HTMLEditorKit + HTMLFrameHyperlinkEvent + HTMLWriter + Handler + HandlerBase + HandshakeCompletedEvent + HandshakeCompletedListener + HasControls + HashAttributeSet + HashDocAttributeSet + HashMap + HashPrintJobAttributeSet + HashPrintRequestAttributeSet + HashPrintServiceAttributeSet + HashSet + Hashtable + HeadlessException + HierarchyBoundsAdapter + HierarchyBoundsListener + HierarchyEvent + HierarchyListener + Highlight + HighlightPainter + Highlighter + HostnameVerifier + HttpURLConnection + HttpsURLConnection + HyperlinkEvent + HyperlinkListener + ICC_ColorSpace + ICC_Profile + ICC_ProfileGray + ICC_ProfileRGB + IDLEntity + IDLType + IDLTypeHelper + IDLTypeOperations + ID_ASSIGNMENT_POLICY_ID + ID_UNIQUENESS_POLICY_ID + IIOByteBuffer + IIOException + IIOImage + IIOInvalidTreeException + IIOMetadata + IIOMetadataController + IIOMetadataFormat + IIOMetadataFormatImpl + IIOMetadataNode + IIOParam + IIOParamController + IIOReadProgressListener + IIOReadUpdateListener + IIOReadWarningListener + IIORegistry + IIOServiceProvider + IIOWriteProgressListener + IIOWriteWarningListener + IMPLICIT_ACTIVATION_POLICY_ID + IMP_LIMIT + INITIALIZE + INPUT_STREAM + INTERNAL + INTF_REPOS + INVALID_TRANSACTION + INV_FLAG + INV_IDENT + INV_OBJREF + INV_POLICY + IOException + IOR + IORHelper + IORHolder + IORInfo + IORInfoOperations + IORInterceptor + IORInterceptorOperations + IRObject + IRObjectOperations + ISO + Icon + IconUIResource + IconView + IdAssignmentPolicy + IdAssignmentPolicyOperations + IdAssignmentPolicyValue + IdUniquenessPolicy + IdUniquenessPolicyOperations + IdUniquenessPolicyValue + IdentifierHelper + Identity + IdentityHashMap + IdentityScope + IllegalAccessError + IllegalAccessException + IllegalArgumentException + IllegalBlockSizeException + IllegalBlockingModeException + IllegalCharsetNameException + IllegalComponentStateException + IllegalMonitorStateException + IllegalPathStateException + IllegalSelectorException + IllegalStateException + IllegalThreadStateException + Image + ImageCapabilities + ImageConsumer + ImageFilter + ImageGraphicAttribute + ImageIO + ImageIcon + ImageInputStream + ImageInputStreamImpl + ImageInputStreamSpi + ImageObserver + ImageOutputStream + ImageOutputStreamImpl + ImageOutputStreamSpi + ImageProducer + ImageReadParam + ImageReader + ImageReaderSpi + ImageReaderWriterSpi + ImageTranscoder + ImageTranscoderSpi + ImageTypeSpecifier + ImageView + ImageWriteParam + ImageWriter + ImageWriterSpi + ImagingOpException + ImplicitActivationPolicy + ImplicitActivationPolicyOperations + ImplicitActivationPolicyValue + IncompatibleClassChangeError + InconsistentTypeCode + InconsistentTypeCode + InconsistentTypeCodeHelper + IndexColorModel + IndexOutOfBoundsException + IndexedPropertyDescriptor + IndirectionException + Inet4Address + Inet6Address + InetAddress + InetSocketAddress + Inflater + InflaterInputStream + Info + Info + Info + Info + Info + InheritableThreadLocal + InitialContext + InitialContextFactory + InitialContextFactoryBuilder + InitialDirContext + InitialLdapContext + InlineView + InputContext + InputEvent + InputMap + InputMapUIResource + InputMethod + InputMethodContext + InputMethodDescriptor + InputMethodEvent + InputMethodHighlight + InputMethodListener + InputMethodRequests + InputSource + InputStream + InputStream + InputStream + InputStreamReader + InputSubset + InputVerifier + InsertBreakAction + InsertContentAction + InsertHTMLTextAction + InsertTabAction + Insets + InsetsUIResource + InstantiationError + InstantiationException + Instrument + InsufficientResourcesException + IntBuffer + IntHolder + Integer + IntegerSyntax + Interceptor + InterceptorOperations + InternalError + InternalFrameAdapter + InternalFrameBorder + InternalFrameEvent + InternalFrameFocusTraversalPolicy + InternalFrameListener + InternalFrameUI + InternationalFormatter + InterruptedException + InterruptedIOException + InterruptedNamingException + InterruptibleChannel + IntrospectionException + Introspector + Invalid + InvalidAddress + InvalidAddressHelper + InvalidAddressHolder + InvalidAlgorithmParameterException + InvalidAttributeIdentifierException + InvalidAttributeValueException + InvalidAttributesException + InvalidClassException + InvalidDnDOperationException + InvalidKeyException + InvalidKeySpecException + InvalidMarkException + InvalidMidiDataException + InvalidName + InvalidName + InvalidName + InvalidNameException + InvalidNameHelper + InvalidNameHelper + InvalidNameHolder + InvalidObjectException + InvalidParameterException + InvalidParameterSpecException + InvalidPolicy + InvalidPolicyHelper + InvalidPreferencesFormatException + InvalidSearchControlsException + InvalidSearchFilterException + InvalidSeq + InvalidSlot + InvalidSlotHelper + InvalidTransactionException + InvalidTypeForEncoding + InvalidTypeForEncodingHelper + InvalidValue + InvalidValue + InvalidValueHelper + InvocationEvent + InvocationHandler + InvocationTargetException + InvokeHandler + IstringHelper + ItalicAction + ItemEvent + ItemListener + ItemSelectable + Iterator + Iterator + IvParameterSpec + JApplet + JButton + JCheckBox + JCheckBoxMenuItem + JColorChooser + JComboBox + JComponent + JDesktopIcon + JDesktopPane + JDialog + JEditorPane + JFileChooser + JFormattedTextField + JFrame + JIS + JInternalFrame + JLabel + JLayeredPane + JList + JMenu + JMenuBar + JMenuItem + JOptionPane + JPEGHuffmanTable + JPEGImageReadParam + JPEGImageWriteParam + JPEGQTable + JPanel + JPasswordField + JPopupMenu + JProgressBar + JRadioButton + JRadioButtonMenuItem + JRootPane + JScrollBar + JScrollPane + JSeparator + JSlider + JSpinner + JSplitPane + JTabbedPane + JTable + JTableHeader + JTextArea + JTextComponent + JTextField + JTextPane + JToggleButton + JToolBar + JToolTip + JTree + JViewport + JWindow + JarEntry + JarException + JarFile + JarInputStream + JarOutputStream + JarURLConnection + JobAttributes + JobHoldUntil + JobImpressions + JobImpressionsCompleted + JobImpressionsSupported + JobKOctets + JobKOctetsProcessed + JobKOctetsSupported + JobMediaSheets + JobMediaSheetsCompleted + JobMediaSheetsSupported + JobMessageFromOperator + JobName + JobOriginatingUserName + JobPriority + JobPrioritySupported + JobSheets + JobState + JobStateReason + JobStateReasons + KerberosKey + KerberosPrincipal + KerberosTicket + Kernel + Key + Key + KeyAdapter + KeyAgreement + KeyAgreementSpi + KeyBinding + KeyEvent + KeyEventDispatcher + KeyEventPostProcessor + KeyException + KeyFactory + KeyFactorySpi + KeyGenerator + KeyGeneratorSpi + KeyListener + KeyManagementException + KeyManager + KeyManagerFactory + KeyManagerFactorySpi + KeyPair + KeyPairGenerator + KeyPairGeneratorSpi + KeySelectionManager + KeySpec + KeyStore + KeyStoreException + KeyStoreSpi + KeyStroke + KeyboardFocusManager + Keymap + LDAPCertStoreParameters + LIFESPAN_POLICY_ID + LOCATION_FORWARD + Label + LabelUI + LabelView + LanguageCallback + LastOwnerException + LayerPainter + LayeredHighlighter + LayoutFocusTraversalPolicy + LayoutManager + LayoutManager2 + LayoutQueue + LazyInputMap + LazyValue + LdapContext + LdapReferralException + Lease + Level + LexicalHandler + LifespanPolicy + LifespanPolicyOperations + LifespanPolicyValue + LimitExceededException + Line + Line2D + LineBorder + LineBorderUIResource + LineBreakMeasurer + LineEvent + LineListener + LineMetrics + LineNumberInputStream + LineNumberReader + LineUnavailableException + LinkController + LinkException + LinkLoopException + LinkRef + LinkageError + LinkedHashMap + LinkedHashSet + LinkedList + List + List + ListCellRenderer + ListDataEvent + ListDataListener + ListEditor + ListIterator + ListModel + ListPainter + ListResourceBundle + ListSelectionEvent + ListSelectionListener + ListSelectionModel + ListUI + ListView + LoaderHandler + LocalObject + Locale + LocateRegistry + Locator + LocatorImpl + LogManager + LogRecord + LogStream + Logger + LoggingPermission + LoginContext + LoginException + LoginModule + LoginModuleControlFlag + Long + LongBuffer + LongHolder + LongLongSeqHelper + LongLongSeqHolder + LongSeqHelper + LongSeqHolder + LookAndFeel + LookAndFeelInfo + LookupOp + LookupTable + MARSHAL + Mac + MacSpi + MalformedInputException + MalformedLinkException + MalformedURLException + ManagerFactoryParameters + Manifest + Map + MapMode + MappedByteBuffer + MarginBorder + MarshalException + MarshalledObject + MaskFormatter + Matcher + Math + MatteBorder + MatteBorderUIResource + Media + MediaName + MediaPrintableArea + MediaSize + MediaSizeName + MediaTracker + MediaTray + MediaType + Member + MemoryCacheImageInputStream + MemoryCacheImageOutputStream + MemoryHandler + MemoryImageSource + Menu + MenuBar + MenuBarBorder + MenuBarBorder + MenuBarUI + MenuComponent + MenuContainer + MenuDragMouseEvent + MenuDragMouseListener + MenuElement + MenuEvent + MenuItem + MenuItemBorder + MenuItemUI + MenuKeyEvent + MenuKeyListener + MenuListener + MenuSelectionManager + MenuShortcut + MessageDigest + MessageDigestSpi + MessageFormat + MessageProp + MetaEventListener + MetaMessage + MetalBorders + MetalButtonUI + MetalCheckBoxIcon + MetalCheckBoxUI + MetalComboBoxButton + MetalComboBoxEditor + MetalComboBoxIcon + MetalComboBoxUI + MetalDesktopIconUI + MetalFileChooserUI + MetalIconFactory + MetalInternalFrameTitlePane + MetalInternalFrameUI + MetalLabelUI + MetalLookAndFeel + MetalPopupMenuSeparatorUI + MetalProgressBarUI + MetalRadioButtonUI + MetalRootPaneUI + MetalScrollBarUI + MetalScrollButton + MetalScrollPaneUI + MetalSeparatorUI + MetalSliderUI + MetalSplitPaneUI + MetalTabbedPaneUI + MetalTextFieldUI + MetalTheme + MetalToggleButtonUI + MetalToolBarUI + MetalToolTipUI + MetalTreeUI + Method + MethodDescriptor + MidiChannel + MidiDevice + MidiDeviceProvider + MidiEvent + MidiFileFormat + MidiFileReader + MidiFileWriter + MidiMessage + MidiSystem + MidiUnavailableException + MimeTypeParseException + MinimalHTMLWriter + MissingResourceException + Mixer + MixerProvider + ModificationItem + Modifier + MouseAdapter + MouseDragGestureRecognizer + MouseEvent + MouseInputAdapter + MouseInputListener + MouseListener + MouseMotionAdapter + MouseMotionListener + MouseWheelEvent + MouseWheelListener + MultiButtonUI + MultiColorChooserUI + MultiComboBoxUI + MultiDesktopIconUI + MultiDesktopPaneUI + MultiDoc + MultiDocPrintJob + MultiDocPrintService + MultiFileChooserUI + MultiInternalFrameUI + MultiLabelUI + MultiListUI + MultiLookAndFeel + MultiMenuBarUI + MultiMenuItemUI + MultiOptionPaneUI + MultiPanelUI + MultiPixelPackedSampleModel + MultiPopupMenuUI + MultiProgressBarUI + MultiRootPaneUI + MultiScrollBarUI + MultiScrollPaneUI + MultiSeparatorUI + MultiSliderUI + MultiSpinnerUI + MultiSplitPaneUI + MultiTabbedPaneUI + MultiTableHeaderUI + MultiTableUI + MultiTextUI + MultiToolBarUI + MultiToolTipUI + MultiTreeUI + MultiViewportUI + MulticastSocket + MultipleComponentProfileHelper + MultipleComponentProfileHolder + MultipleDocumentHandling + MultipleDocumentHandlingType + MultipleMaster + MutableAttributeSet + MutableComboBoxModel + MutableTreeNode + NA + NO_IMPLEMENT + NO_MEMORY + NO_PERMISSION + NO_RESOURCES + NO_RESPONSE + NVList + Name + Name + NameAlreadyBoundException + NameCallback + NameClassPair + NameComponent + NameComponentHelper + NameComponentHolder + NameDynAnyPair + NameDynAnyPairHelper + NameDynAnyPairSeqHelper + NameHelper + NameHolder + NameNotFoundException + NameParser + NameValuePair + NameValuePair + NameValuePairHelper + NameValuePairHelper + NameValuePairSeqHelper + NamedNodeMap + NamedValue + NamespaceChangeListener + NamespaceSupport + Naming + NamingContext + NamingContextExt + NamingContextExtHelper + NamingContextExtHolder + NamingContextExtOperations + NamingContextExtPOA + NamingContextHelper + NamingContextHolder + NamingContextOperations + NamingContextPOA + NamingEnumeration + NamingEvent + NamingException + NamingExceptionEvent + NamingListener + NamingManager + NamingSecurityException + NavigationFilter + NegativeArraySizeException + NetPermission + NetworkInterface + NoClassDefFoundError + NoConnectionPendingException + NoContext + NoContextHelper + NoInitialContextException + NoPermissionException + NoRouteToHostException + NoServant + NoServantHelper + NoSuchAlgorithmException + NoSuchAttributeException + NoSuchElementException + NoSuchFieldError + NoSuchFieldException + NoSuchMethodError + NoSuchMethodException + NoSuchObjectException + NoSuchPaddingException + NoSuchProviderException + Node + NodeChangeEvent + NodeChangeListener + NodeDimensions + NodeList + NonReadableChannelException + NonWritableChannelException + NoninvertibleTransformException + NotActiveException + NotBoundException + NotContextException + NotEmpty + NotEmptyHelper + NotEmptyHolder + NotFound + NotFoundHelper + NotFoundHolder + NotFoundReason + NotFoundReasonHelper + NotFoundReasonHolder + NotOwnerException + NotSerializableException + NotYetBoundException + NotYetConnectedException + Notation + NullCipher + NullPointerException + Number + NumberEditor + NumberFormat + NumberFormatException + NumberFormatter + NumberOfDocuments + NumberOfInterveningJobs + NumberUp + NumberUpSupported + NumericShaper + OBJECT_NOT_EXIST + OBJ_ADAPTER + OMGVMCID + ORB + ORB + ORBInitInfo + ORBInitInfoOperations + ORBInitializer + ORBInitializerOperations + ObjID + Object + Object + ObjectAlreadyActive + ObjectAlreadyActiveHelper + ObjectChangeListener + ObjectFactory + ObjectFactoryBuilder + ObjectHelper + ObjectHolder + ObjectIdHelper + ObjectImpl + ObjectImpl + ObjectInput + ObjectInputStream + ObjectInputValidation + ObjectNotActive + ObjectNotActiveHelper + ObjectOutput + ObjectOutputStream + ObjectStreamClass + ObjectStreamConstants + ObjectStreamException + ObjectStreamField + ObjectView + Observable + Observer + OctetSeqHelper + OctetSeqHolder + Oid + OpenType + Operation + OperationNotSupportedException + Option + OptionDialogBorder + OptionPaneUI + OptionalDataException + OrientationRequested + OrientationRequestedType + OriginType + Other + OutOfMemoryError + OutputDeviceAssigned + OutputKeys + OutputStream + OutputStream + OutputStream + OutputStreamWriter + OverlappingFileLockException + OverlayLayout + Owner + PBEKey + PBEKeySpec + PBEParameterSpec + PDLOverrideSupported + PERSIST_STORE + PKCS8EncodedKeySpec + PKIXBuilderParameters + PKIXCertPathBuilderResult + PKIXCertPathChecker + PKIXCertPathValidatorResult + PKIXParameters + POA + POAHelper + POAManager + POAManagerOperations + POAOperations + PRIVATE_MEMBER + PSSParameterSpec + PUBLIC_MEMBER + Package + PackedColorModel + PageAttributes + PageFormat + PageRanges + Pageable + PagesPerMinute + PagesPerMinuteColor + Paint + PaintContext + PaintEvent + PaletteBorder + PaletteCloseIcon + Panel + PanelUI + Paper + ParagraphAttribute + ParagraphConstants + ParagraphView + ParagraphView + Parameter + ParameterBlock + ParameterDescriptor + ParameterMetaData + ParameterMode + ParameterModeHelper + ParameterModeHolder + ParseException + ParsePosition + Parser + Parser + Parser + ParserAdapter + ParserCallback + ParserConfigurationException + ParserDelegator + ParserFactory + PartialResultException + PasswordAuthentication + PasswordCallback + PasswordView + PasteAction + Patch + PathIterator + Pattern + PatternSyntaxException + Permission + Permission + PermissionCollection + Permissions + PersistenceDelegate + PhantomReference + Pipe + PipedInputStream + PipedOutputStream + PipedReader + PipedWriter + PixelGrabber + PixelInterleavedSampleModel + PlainDocument + PlainView + Point + Point2D + Policy + Policy + Policy + PolicyError + PolicyErrorCodeHelper + PolicyErrorHelper + PolicyErrorHolder + PolicyFactory + PolicyFactoryOperations + PolicyHelper + PolicyHolder + PolicyListHelper + PolicyListHolder + PolicyNode + PolicyOperations + PolicyQualifierInfo + PolicyTypeHelper + Polygon + PooledConnection + Popup + PopupFactory + PopupMenu + PopupMenuBorder + PopupMenuEvent + PopupMenuListener + PopupMenuUI + Port + PortUnreachableException + PortableRemoteObject + PortableRemoteObjectDelegate + Position + PreferenceChangeEvent + PreferenceChangeListener + Preferences + PreferencesFactory + PreparedStatement + PresentationDirection + Principal + Principal + PrincipalHolder + PrintEvent + PrintException + PrintGraphics + PrintJob + PrintJobAdapter + PrintJobAttribute + PrintJobAttributeEvent + PrintJobAttributeListener + PrintJobAttributeSet + PrintJobEvent + PrintJobListener + PrintQuality + PrintQualityType + PrintRequestAttribute + PrintRequestAttributeSet + PrintService + PrintServiceAttribute + PrintServiceAttributeEvent + PrintServiceAttributeListener + PrintServiceAttributeSet + PrintServiceLookup + PrintStream + PrintWriter + Printable + PrinterAbortException + PrinterException + PrinterGraphics + PrinterIOException + PrinterInfo + PrinterIsAcceptingJobs + PrinterJob + PrinterLocation + PrinterMakeAndModel + PrinterMessageFromOperator + PrinterMoreInfo + PrinterMoreInfoManufacturer + PrinterName + PrinterResolution + PrinterState + PrinterStateReason + PrinterStateReasons + PrinterURI + PrivateCredentialPermission + PrivateKey + PrivilegedAction + PrivilegedActionException + PrivilegedExceptionAction + Process + ProcessingInstruction + ProfileDataException + ProfileIdHelper + ProgressBarUI + ProgressMonitor + ProgressMonitorInputStream + Properties + PropertyChangeEvent + PropertyChangeListener + PropertyChangeListenerProxy + PropertyChangeSupport + PropertyDescriptor + PropertyEditor + PropertyEditorManager + PropertyEditorSupport + PropertyPermission + PropertyResourceBundle + PropertyVetoException + ProtectionDomain + ProtocolException + Provider + ProviderException + Proxy + ProxyLazyValue + PublicKey + PushbackInputStream + PushbackReader + PutField + QuadCurve2D + QueuedJobCount + RC2ParameterSpec + RC5ParameterSpec + READER + REQUEST_PROCESSING_POLICY_ID + RGBImageFilter + RMIClassLoader + RMIClassLoaderSpi + RMIClientSocketFactory + RMIFailureHandler + RMISecurityException + RMISecurityManager + RMIServerSocketFactory + RMISocketFactory + RSAKey + RSAKeyGenParameterSpec + RSAMultiPrimePrivateCrtKey + RSAMultiPrimePrivateCrtKeySpec + RSAOtherPrimeInfo + RSAPrivateCrtKey + RSAPrivateCrtKeySpec + RSAPrivateKey + RSAPrivateKeySpec + RSAPublicKey + RSAPublicKeySpec + RTFEditorKit + RadioButtonBorder + Random + RandomAccess + RandomAccessFile + Raster + RasterFormatException + RasterOp + ReadOnlyBufferException + ReadableByteChannel + Reader + Receiver + Rectangle + Rectangle2D + RectangularShape + Ref + RefAddr + Reference + Reference + ReferenceQueue + ReferenceUriSchemesSupported + Referenceable + ReferralException + ReflectPermission + RefreshFailedException + Refreshable + RegisterableService + Registry + RegistryHandler + RemarshalException + Remote + RemoteCall + RemoteException + RemoteObject + RemoteRef + RemoteServer + RemoteStub + RenderContext + RenderableImage + RenderableImageOp + RenderableImageProducer + RenderedImage + RenderedImageFactory + Renderer + RenderingHints + RepaintManager + ReplicateScaleFilter + RepositoryIdHelper + Request + RequestInfo + RequestInfoOperations + RequestProcessingPolicy + RequestProcessingPolicyOperations + RequestProcessingPolicyValue + RequestingUserName + RescaleOp + ResolutionSyntax + ResolveResult + Resolver + ResourceBundle + ResponseHandler + Result + Result + ResultSet + ResultSetMetaData + ReverbType + Robot + RolloverButtonBorder + RolloverButtonBorder + RootPaneContainer + RootPaneUI + RoundRectangle2D + RowMapper + RowSet + RowSetEvent + RowSetInternal + RowSetListener + RowSetMetaData + RowSetReader + RowSetWriter + RuleBasedCollator + RunTime + RunTimeOperations + Runnable + Runtime + RuntimeException + RuntimePermission + SAXException + SAXNotRecognizedException + SAXNotSupportedException + SAXParseException + SAXParser + SAXParserFactory + SAXResult + SAXSource + SAXTransformerFactory + SERVANT_RETENTION_POLICY_ID + SERVICE_FORMATTED + SQLData + SQLException + SQLInput + SQLOutput + SQLPermission + SQLWarning + SSLContext + SSLContextSpi + SSLException + SSLHandshakeException + SSLKeyException + SSLPeerUnverifiedException + SSLPermission + SSLProtocolException + SSLServerSocket + SSLServerSocketFactory + SSLSession + SSLSessionBindingEvent + SSLSessionBindingListener + SSLSessionContext + SSLSocket + SSLSocketFactory + STRING + SUCCESSFUL + SYNC_WITH_TRANSPORT + SYSTEM_EXCEPTION + SampleModel + Savepoint + ScatteringByteChannel + SchemaViolationException + ScrollBarUI + ScrollPane + ScrollPaneAdjustable + ScrollPaneBorder + ScrollPaneConstants + ScrollPaneLayout + ScrollPaneUI + Scrollable + Scrollbar + SealedObject + SearchControls + SearchResult + SecretKey + SecretKeyFactory + SecretKeyFactorySpi + SecretKeySpec + SecureClassLoader + SecureRandom + SecureRandomSpi + Security + SecurityException + SecurityManager + SecurityPermission + Segment + SelectableChannel + SelectionKey + Selector + SelectorProvider + Separator + Separator + SeparatorUI + Sequence + SequenceInputStream + Sequencer + Serializable + SerializablePermission + Servant + ServantActivator + ServantActivatorHelper + ServantActivatorOperations + ServantActivatorPOA + ServantAlreadyActive + ServantAlreadyActiveHelper + ServantLocator + ServantLocatorHelper + ServantLocatorOperations + ServantLocatorPOA + ServantManager + ServantManagerOperations + ServantNotActive + ServantNotActiveHelper + ServantObject + ServantRetentionPolicy + ServantRetentionPolicyOperations + ServantRetentionPolicyValue + ServerCloneException + ServerError + ServerException + ServerNotActiveException + ServerRef + ServerRequest + ServerRequestInfo + ServerRequestInfoOperations + ServerRequestInterceptor + ServerRequestInterceptorOperations + ServerRuntimeException + ServerSocket + ServerSocketChannel + ServerSocketFactory + ServiceContext + ServiceContextHelper + ServiceContextHolder + ServiceContextListHelper + ServiceContextListHolder + ServiceDetail + ServiceDetailHelper + ServiceIdHelper + ServiceInformation + ServiceInformationHelper + ServiceInformationHolder + ServicePermission + ServiceRegistry + ServiceUI + ServiceUIFactory + ServiceUnavailableException + Set + SetOfIntegerSyntax + SetOverrideType + SetOverrideTypeHelper + Severity + Shape + ShapeGraphicAttribute + SheetCollate + Short + ShortBuffer + ShortBufferException + ShortHolder + ShortLookupTable + ShortMessage + ShortSeqHelper + ShortSeqHolder + Sides + SidesType + Signature + SignatureException + SignatureSpi + SignedObject + Signer + SimpleAttributeSet + SimpleBeanInfo + SimpleDateFormat + SimpleDoc + SimpleFormatter + SimpleTimeZone + SinglePixelPackedSampleModel + SingleSelectionModel + SinkChannel + Size2DSyntax + SizeLimitExceededException + SizeRequirements + SizeSequence + Skeleton + SkeletonMismatchException + SkeletonNotFoundException + SliderUI + Socket + SocketAddress + SocketChannel + SocketException + SocketFactory + SocketHandler + SocketImpl + SocketImplFactory + SocketOptions + SocketPermission + SocketSecurityException + SocketTimeoutException + SoftBevelBorder + SoftReference + SortedMap + SortedSet + SortingFocusTraversalPolicy + Soundbank + SoundbankReader + SoundbankResource + Source + SourceChannel + SourceDataLine + SourceLocator + SpinnerDateModel + SpinnerListModel + SpinnerModel + SpinnerNumberModel + SpinnerUI + SplitPaneBorder + SplitPaneUI + Spring + SpringLayout + Stack + StackOverflowError + StackTraceElement + StartTlsRequest + StartTlsResponse + State + StateEdit + StateEditable + StateFactory + Statement + Statement + StreamCorruptedException + StreamHandler + StreamPrintService + StreamPrintServiceFactory + StreamResult + StreamSource + StreamTokenizer + Streamable + StreamableValue + StrictMath + String + StringBuffer + StringBufferInputStream + StringCharacterIterator + StringContent + StringHolder + StringIndexOutOfBoundsException + StringNameHelper + StringReader + StringRefAddr + StringSelection + StringSeqHelper + StringSeqHolder + StringTokenizer + StringValueHelper + StringWriter + Stroke + Struct + StructMember + StructMemberHelper + Stub + StubDelegate + StubNotFoundException + Style + StyleConstants + StyleContext + StyleSheet + StyledDocument + StyledEditorKit + StyledTextAction + Subject + SubjectDomainCombiner + Subset + SupportedValuesAttribute + SwingConstants + SwingPropertyChangeSupport + SwingUtilities + SyncFailedException + SyncMode + SyncScopeHelper + Synthesizer + SysexMessage + System + SystemColor + SystemException + SystemFlavorMap + TAG_ALTERNATE_IIOP_ADDRESS + TAG_CODE_SETS + TAG_INTERNET_IOP + TAG_JAVA_CODEBASE + TAG_MULTIPLE_COMPONENTS + TAG_ORB_TYPE + TAG_POLICIES + TCKind + THREAD_POLICY_ID + TRANSACTION_REQUIRED + TRANSACTION_ROLLEDBACK + TRANSIENT + TRANSPORT_RETRY + TabExpander + TabSet + TabStop + TabableView + TabbedPaneUI + TableCellEditor + TableCellRenderer + TableColumn + TableColumnModel + TableColumnModelEvent + TableColumnModelListener + TableHeaderBorder + TableHeaderUI + TableModel + TableModelEvent + TableModelListener + TableUI + TableView + Tag + TagElement + TaggedComponent + TaggedComponentHelper + TaggedComponentHolder + TaggedProfile + TaggedProfileHelper + TaggedProfileHolder + TargetDataLine + Templates + TemplatesHandler + Text + TextAction + TextArea + TextAttribute + TextComponent + TextEvent + TextField + TextFieldBorder + TextHitInfo + TextInputCallback + TextLayout + TextListener + TextMeasurer + TextOutputCallback + TextSyntax + TextUI + TexturePaint + Thread + ThreadDeath + ThreadGroup + ThreadLocal + ThreadPolicy + ThreadPolicyOperations + ThreadPolicyValue + Throwable + Tie + TileObserver + Time + TimeLimitExceededException + TimeZone + Timer + Timer + TimerTask + Timestamp + TitledBorder + TitledBorderUIResource + ToggleButtonBorder + ToggleButtonBorder + ToggleButtonModel + TooManyListenersException + ToolBarBorder + ToolBarUI + ToolTipManager + ToolTipUI + Toolkit + Track + TransactionRequiredException + TransactionRolledbackException + TransactionService + TransferHandler + Transferable + TransformAttribute + Transformer + TransformerConfigurationException + TransformerException + TransformerFactory + TransformerFactoryConfigurationError + TransformerHandler + Transmitter + Transparency + TreeCellEditor + TreeCellRenderer + TreeControlIcon + TreeExpansionEvent + TreeExpansionListener + TreeFolderIcon + TreeLeafIcon + TreeMap + TreeModel + TreeModelEvent + TreeModelListener + TreeNode + TreePath + TreeSelectionEvent + TreeSelectionListener + TreeSelectionModel + TreeSet + TreeUI + TreeWillExpandListener + TrustAnchor + TrustManager + TrustManagerFactory + TrustManagerFactorySpi + Type + Type + Type + Type + Type + Type + Type + TypeCode + TypeCodeHolder + TypeMismatch + TypeMismatch + TypeMismatch + TypeMismatchHelper + TypeMismatchHelper + Types + UID + UIDefaults + UIManager + UIResource + UIResource + UIResource + UIResource + UIResource + UIResource + UIResource + ULongLongSeqHelper + ULongLongSeqHolder + ULongSeqHelper + ULongSeqHolder + UNKNOWN + UNSUPPORTED_POLICY + UNSUPPORTED_POLICY_VALUE + URI + URIException + URIResolver + URISyntax + URISyntaxException + URL + URL + URLClassLoader + URLConnection + URLDecoder + URLEncoder + URLStreamHandler + URLStreamHandlerFactory + URLStringHelper + USER_EXCEPTION + UShortSeqHelper + UShortSeqHolder + UTFDataFormatException + UndeclaredThrowableException + UnderlineAction + UndoManager + UndoableEdit + UndoableEditEvent + UndoableEditListener + UndoableEditSupport + UnexpectedException + UnicastRemoteObject + UnicodeBlock + UnionMember + UnionMemberHelper + UnknownEncoding + UnknownEncodingHelper + UnknownError + UnknownException + UnknownGroupException + UnknownHostException + UnknownHostException + UnknownObjectException + UnknownServiceException + UnknownTag + UnknownUserException + UnknownUserExceptionHelper + UnknownUserExceptionHolder + UnmappableCharacterException + UnmarshalException + UnmodifiableSetException + UnrecoverableKeyException + Unreferenced + UnresolvedAddressException + UnresolvedPermission + UnsatisfiedLinkError + UnsolicitedNotification + UnsolicitedNotificationEvent + UnsolicitedNotificationListener + UnsupportedAddressTypeException + UnsupportedAudioFileException + UnsupportedCallbackException + UnsupportedCharsetException + UnsupportedClassVersionError + UnsupportedEncodingException + UnsupportedFlavorException + UnsupportedLookAndFeelException + UnsupportedOperationException + UserException + Util + UtilDelegate + Utilities + VMID + VM_ABSTRACT + VM_CUSTOM + VM_NONE + VM_TRUNCATABLE + ValueBase + ValueBaseHelper + ValueBaseHolder + ValueFactory + ValueHandler + ValueMember + ValueMemberHelper + VariableHeightLayoutCache + Vector + VerifyError + VersionSpecHelper + VetoableChangeListener + VetoableChangeListenerProxy + VetoableChangeSupport + View + ViewFactory + ViewportLayout + ViewportUI + VirtualMachineError + Visibility + VisibilityHelper + VoiceStatus + Void + VolatileImage + WCharSeqHelper + WCharSeqHolder + WStringSeqHelper + WStringSeqHolder + WStringValueHelper + WeakHashMap + WeakReference + Window + WindowAdapter + WindowConstants + WindowEvent + WindowFocusListener + WindowListener + WindowStateListener + WrappedPlainView + WritableByteChannel + WritableRaster + WritableRenderedImage + WriteAbortedException + Writer + WrongAdapter + WrongAdapterHelper + WrongPolicy + WrongPolicyHelper + WrongTransaction + WrongTransactionHelper + WrongTransactionHolder + X500Principal + X500PrivateCredential + X509CRL + X509CRLEntry + X509CRLSelector + X509CertSelector + X509Certificate + X509Certificate + X509EncodedKeySpec + X509Extension + X509KeyManager + X509TrustManager + XAConnection + XADataSource + XAException + XAResource + XMLDecoder + XMLEncoder + XMLFilter + XMLFilterImpl + XMLFormatter + XMLReader + XMLReaderAdapter + XMLReaderFactory + Xid + ZipEntry + ZipException + ZipFile + ZipInputStream + ZipOutputStream + ZoneView + _BindingIteratorImplBase + _BindingIteratorStub + _DynAnyFactoryStub + _DynAnyStub + _DynArrayStub + _DynEnumStub + _DynFixedStub + _DynSequenceStub + _DynStructStub + _DynUnionStub + _DynValueStub + _IDLTypeStub + _NamingContextExtStub + _NamingContextImplBase + _NamingContextStub + _PolicyStub + _Remote_Stub + _ServantActivatorStub + _ServantLocatorStub + + + + abstract + assert + break + case + catch + class + continue + default + do + else + extends + false + finally + for + goto + if + implements + import + instanceof + interface + native + new + null + package + private + protected + public + return + super + strictfp + switch + synchronized + this + throws + throw + transient + true + try + volatile + while + + + + boolean + byte + char + const + double + final + float + int + long + short + static + void + + + + and + eq + gt + true + instanceof + or + ne + le + false + empty + not + lt + ge + null + div + mod + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/julia.xml b/kate/part/syntax/data/julia.xml new file mode 100644 index 00000000..39de5e8a --- /dev/null +++ b/kate/part/syntax/data/julia.xml @@ -0,0 +1,290 @@ + + + + + + + + + + begin + do + for + function + if + let + quote + try + type + while + + + catch + else + elseif + + + end + + + abstract + bitstype + break + ccall + const + continue + export + global + import + in + local + macro + module + return + typealias + + + AbstractArray + AbstractMatrix + AbstractVector + Any + Array + ASCIIString + Associative + Bool + ByteString + Char + Complex + Complex64 + Complex128 + ComplexPair + DArray + Dict + Exception + Expr + Float + Float32 + Float64 + Function + ObjectIdDict + Int + Int8 + Int16 + Int32 + Int64 + Integer + IntSet + IO + IOStream + Matrix + Nothing + None + NTuple + Number + Ptr + Range + Range1 + Ranges + Rational + Real + Regex + RegexMatch + Set + Signed + StridedArray + StridedMatrix + StridedVecOrMat + StridedVector + String + SubArray + SubString + Symbol + Task + Tuple + Type + Uint + Uint8 + Uint16 + Uint32 + Uint64 + Union + Unsigned + UTF8String + VecOrMat + Vector + Void + WeakRef + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/k.xml b/kate/part/syntax/data/k.xml new file mode 100644 index 00000000..00267fb4 --- /dev/null +++ b/kate/part/syntax/data/k.xml @@ -0,0 +1,430 @@ + + + + + + while + if + do + + + abs + acos + asin + atan + avg + bin + by + cos + delete + div + exec + exit + exp + from + getenv + i + in + insert + last + like + log + max + min + prd + select + setenv + sin + sqrt + ss + sum + tan + update + wavg + within + wsum + xexp + + + .Q.addmonths + .Q.addr + .Q.host + .Q.chk + .Q.cn + .Q.dd + .Q.dpft + .Q.dsftg + .Q.def + .Q.en + .Q.fc + .Q.fk + .Q.fmt + .Q.foo + .Q.fs + .Q.ft + .Q.fu + .Q.gc + .Q.hdpf + .Q.ind + .Q.j10 + .Q.x10 + .Q.j12 + .Q.x12 + .Q.k + .Q.l + .Q.opt + .Q.par + .Q.qp + .Q.qt + .Q.s + .Q.s1 + .Q.ty + .Q.v + .Q.V + .Q.view + .Q.w + .Q.M + .Q.pf + .Q.pt + .Q.PD + .Q.PV + .Q.pd + .Q.pv + .Q.pn + .Q.bv + .Q.vp + .Q.P + .Q.D + .Q.u + + + .h.br + .h.c0 + .h.c1 + .h.cd + .h.code + .h.data + .h.eb + .h.ec + .h.ed + .h.edsn + .h.es + .h.ex + .h.fram + .h.ha + .h.hb + .h.hc + .h.he + .h.hn + .h.hp + .h.hr + .h.ht + .h.hta + .h.htac + .h.htc + .h.html + .h.http + .h.hu + .h.hug + .h.hy + .h.iso8601 + .h.jx + .h.logo + .h.nbr + .h.pre + .h.text + .h.tx + .h.ty + .h.uh + .h.xd + .h.xmp + .h.xs + .h.xt + + + .o.B0 + .o.C0 + .o.Cols + .o.Columns + .o.FG + .o.Fkey + .o.Gkey + .o.Key + .o.PS + .o.Special + .o.Stats + .o.T + .o.T0 + .o.TI + .o.Tables + .o.Ts + .o.TypeInfo + .o.ex + .o.o + .o.t + + + .z.a + .z.ac + .z.b + .z.bm + .z.c + .z.exit + .z.f + .z.h + .z.i + .z.k + .z.K + .z.l + .z.n + .z.N + .z.o + .z.p + .z.P + .z.pc + .z.pg + .z.ph + .z.pi + .z.po + .z.pp + .z.ps + .z.pw + .z.q + .z.s + .z.ts + .z.u + .z.vs + .z.w + .z.W + .z.ws + .z.x + .z.z + .z.Z + .z.t + .z.T + .z.d + .z.D + .z.zd + + + .q.aj + .q.aj0 + .q.all + .q.and + .q.any + .q.asc + .q.asof + .q.attr + .q.avgs + .q.ceiling + .q.cols + .q.cor + .q.count + .q.cov + .q.cross + .q.csv + .q.cut + .q.deltas + .q.desc + .q.dev + .q.differ + .q.distinct + .q.each + .q.ej + .q.enlist + .q.eval + .q.except + .q.fby + .q.fills + .q.first + .q.fkeys + .q.flip + .q.floor + .q.get + .q.group + .q.gtime + .q.hclose + .q.hcount + .q.hdel + .q.hopen + .q.hsym + .q.iasc + .q.idesc + .q.ij + .q.inter + .q.inv + .q.key + .q.keys + .q.lj + .q.load + .q.lower + .q.lsq + .q.ltime + .q.ltrim + .q.mavg + .q.maxs + .q.mcount + .q.md5 + .q.mdev + .q.med + .q.meta + .q.mins + .q.mmax + .q.mmin + .q.mmu + .q.mod + .q.msum + .q.neg + .q.next + .q.not + .q.null + .q.or + .q.over + .q.parse + .q.peach + .q.pj + .q.plist + .q.prds + .q.prev + .q.prior + .q.rand + .q.rank + .q.ratios + .q.raze + .q.read0 + .q.read1 + .q.reciprocal + .q.reverse + .q.rload + .q.rotate + .q.rsave + .q.rtrim + .q.save + .q.scan + .q.set + .q.show + .q.signum + .q.ssr + .q.string + .q.sublist + .q.sums + .q.sv + .q.system + .q.tables + .q.til + .q.trim + .q.txf + .q.type + .q.uj + .q.ungroup + .q.union + .q.upper + .q.upsert + .q.value + .q.var + .q.view + .q.views + .q.vs + .q.where + .q.wj + .q.wj1 + .q.xasc + .q.xbar + .q.xcol + .q.xcols + .q.xdesc + .q.xgroup + .q.xkey + .q.xlog + .q.xprev + .q.xrank + + + TODO + FIXME + FIX + HACK + XXX + NOTE + GOTCHA + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/kbasic.xml b/kate/part/syntax/data/kbasic.xml new file mode 100644 index 00000000..638cbe82 --- /dev/null +++ b/kate/part/syntax/data/kbasic.xml @@ -0,0 +1,97 @@ + + + + + + For + Next + Do + Loop + While + Wend + Until + If + Else + End + Function + Goto + Sub + Implements + In + Sub + Private + Public + Global + As + Dim + Set + Let + Get + To + Property + True + False + Or + Not + Xor + And + Then + Exit + Put + Open + Close + Seek + Print + Input + Output + Repeat + Load + Unload + Declare + Option + Explicit + + + Integer + Long + Byte + Boolean + Variant + Single + Double + Currency + String + Object + Control + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/kconfig.xml b/kate/part/syntax/data/kconfig.xml new file mode 100644 index 00000000..231994d8 --- /dev/null +++ b/kate/part/syntax/data/kconfig.xml @@ -0,0 +1,1138 @@ + + + + + + + + +]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/language.dtd b/kate/part/syntax/data/language.dtd new file mode 100644 index 00000000..8a0b98dd --- /dev/null +++ b/kate/part/syntax/data/language.dtd @@ -0,0 +1,558 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/latex.xml b/kate/part/syntax/data/latex.xml new file mode 100644 index 00000000..d5fd8c92 --- /dev/null +++ b/kate/part/syntax/data/latex.xml @@ -0,0 +1,697 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/ld.xml b/kate/part/syntax/data/ld.xml new file mode 100644 index 00000000..fdacb32d --- /dev/null +++ b/kate/part/syntax/data/ld.xml @@ -0,0 +1,83 @@ + + + + + + + AT + ENTRY + INPUT + GROUP + OUTPUT + OUTPUT_ARCH + OUTPUT_FORMAT + SEARCH_DIR + STARTUP + TARGET + + + ABSOLUTE + ADDR + ALIGN + DATA_SEGMENT_ALIGN + DATA_SEGMENT_END + DATA_SEGMENT_RELRO_END + DEFINED + LOADADDR + MAX + MIN + NEXT + SIZEOF + SIZEOF_HEADERS + + + SECTIONS + MEMORY + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/ldif.xml b/kate/part/syntax/data/ldif.xml new file mode 100644 index 00000000..d0da293a --- /dev/null +++ b/kate/part/syntax/data/ldif.xml @@ -0,0 +1,305 @@ + + + + + + IPPhone + URL + aRecord + aliasedEntryName + aliasedObjectName + associatedDomain + associatedName + audio + authorityRevocationList + bootFile + bootParameter + buildingName + businessCategory + c + cACertificate + cNAMERecord + certificateRevocationList + cn + comment + commonName + conferenceInformation + corbaContainer + corbaRepositoryId + countryName + crossCertificatePair + custom1 + custom2 + custom3 + custom4 + dITRedirect + dSAQuality + dc + deltaRevocationList + description + destinationIndicator + distinguishedName + dmdName + dnQualifier + documentAuthor + documentIdentifier + documentLocation + documentPublisher + documentTitle + documentVersion + domainComponent + enhancedSearchGuide + facsimileTelephoneNumber + fax + gecos + generationQualifier + gidNumber + givenName + gn + homeDirectory + homePostalAddress + homeUrl + host + houseIdentifier + info + initials + internationaliSDNNumber + ipHostNumber + ipNetmaskNumber + ipNetworkNumber + ipProtocolNumber + ipServicePort + ipServiceProtocol + janetMailbox + javaClassNames + javaCodebase + javaContainer + javaDoc + javaFactory + javaReferenceAddress + javaSerializedData + knowledgeInformation + l + labeledURI + lastModifiedBy + lastModifiedTime + lmpassword + localityName + loginShell + mDRecord + mXRecord + macAddress + mail + manager + member + memberNisNetgroup + memberUid + mozillaHomeCountryName + mozillaHomeFriendlyCountryName + mozillaHomeLocalityName + mozillaHomePostalAddress2 + mozillaHomePostalCode + mozillaHomeState + mozillaPostalAddress2 + mozillaSecondemail + nSRecord + name + nisMapEntry + nisMapName + nisNetgroupTriple + ntpasswd + o + objectClass + oncRpcNumber + organizationName + organizationalStatus + organizationalUnitName + otherFacsimiletelephoneNumber + otherMailbox + ou + owner + personalSignature + personalTitle + photo + physicalDeliveryOfficeName + postOfficeBox + postalAddress + postalCode + preferredDeliveryMethod + presentationAddress + protocolInformation + rdn + registeredAddress + reports + rfc822Mailbox + roleOccupant + roomNumber + sOARecord + searchGuide + secretary + seeAlso + serialNumber + shadowExpire + shadowFlag + shadowInactive + shadowLastChange + shadowMax + shadowMin + shadowWarning + singleLevelQuality + sn + st + stateOrProvinceName + street + streetAddress + subtreeMaximumQuality + subtreeMinimumQuality + supportedAlgorithms + supportedApplicationContext + surname + telephoneNumber + teletexTerminalIdentifier + telexNumber + textEncodedORAddress + title + uid + uidNumber + uniqueIdentifier + uniqueMember + userCertificate + userClass + userPassword + userid + workUrl + x121Address + x500UniqueIdentifier + xmozillaNickname + xmozillaUseHtmlMail + xmozillanickname + xmozillausehtmlmail + + + RFC822localPart + SUP + account + alias + applicationEntity + applicationProcess + bootableDevice + cRLDistributionPoint + certificationAuthority + certificationAuthority-V2 + corbaObject + corbaObjectReference + country + dNSDomain + dSA + dcObject + deltaCRL + device + dmd + document + documentSeries + domain + domainRelatedObject + friendlyCountry + groupOfNames + groupOfUniqueNames + ieee802Device + inetOrgPerson + ipHost + ipNetwork + ipProtocol + ipService + javaClassName + javaMarshalledObject + javaNamingReference + javaObject + javaSerializedObject + labeledURIObject + locality + mozillaAbPersonObsolete + nisMap + nisNetgroup + nisObject + officePerson + oncRpc + organization + organizationalPerson + organizationalRole + organizationalUnit + pager + pagerTelephoneNumber + person + pilotDSA + pilotObject + pilotOrganization + pkiCA + pkiUser + posixAccount + posixGroup + qualityLabelledData + residentialPerson + rid + room + sambaAccount + shadowAccount + simpleSecurityObject + strongAuthenticationUser + telephoneNumber + top + uid + uidNumber + uidObject + userSecurityInformation + userid + xmozillaanyphone + zillaPerson + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/less.xml b/kate/part/syntax/data/less.xml new file mode 100644 index 00000000..1908fc55 --- /dev/null +++ b/kate/part/syntax/data/less.xml @@ -0,0 +1,818 @@ + + + + + + + + azimuth + background + background-attachment + background-break + background-clip + background-color + background-image + background-position + background-origin + background-repeat + border + border-bottom + border-bottom-color + border-bottom-style + border-bottom-width + border-collapse + border-color + border-left + border-left-color + border-left-style + border-left-width + border-right + border-right-color + border-right-style + border-right-width + border-spacing + border-style + border-top + border-top-color + border-top-style + border-top-width + border-width + bottom + caption-side + clear + clip + color + content + counter-increment + counter-reset + cue + cue-after + cue-before + cursor + direction + display + elevation + empty-cells + float + font + font-family + font-size + font-size-adjust + font-stretch + font-style + font-variant + font-weight + font-stretch + height + left + letter-spacing + line-height + list-style + list-style-image + list-style-keyword + list-style-position + list-style-type + margin + margin-bottom + margin-left + margin-right + margin-top + marker-offset + max-height + max-width + min-height + min-width + orphans + outline + outline-color + outline-style + outline-width + overflow + padding + padding-bottom + padding-left + padding-right + padding-top + page + page-break-after + page-break-before + page-break-inside + pause + pause-after + pause-before + pitch + pitch-range + play-during + position + quotes + richness + right + size + speak + speak-header + speak-numeral + speak-punctuation + speech-rate + stress + table-layout + text-align + text-decoration + text-decoration-color + text-indent + text-shadow + text-transform + top + unicode-bidi + vertical-align + visibility + voice-family + volume + white-space + widows + width + word-spacing + z-index + + + animation-name + animation-duration + animation-iteration + animation-direction + animation-delay + animation-play-state + animation-fill-mode + animation-timing-function + background-size + border-bottom-image + border-bottom-left-image + border-bottom-left-radius + border-bottom-right-image + border-bottom-right-radius + border-collapse + border-corner-image + border-image + border-left-image + border-radius + border-right-image + border-top-image + border-top-left-image + border-top-left-radius + border-top-right-image + border-top-right-radius + box-align + box-direction + box-flex + box-shadow + box-sizing + column-count + column-fill + column-gap + column-rule-color + column-rule-style + column-rule-width + column-span + column-wisth + hyphens + linear-gradient + opacity + outline + outline-offset + overflow-x + overflow-y + pointer-events + resize + rotation + rotation-point + table-layout + text-overflow + text-shadow + text-wrap + transform-origin + transition + transition-property + transition-duration + word-wrap + + + -moz-animation-name + -moz-animation-duration + -moz-animation-iteration + -moz-animation-direction + -moz-animation-delay + -moz-animation-play-state + -moz-animation-fill-mode + -moz-background-size + -moz-border-image + -moz-border-bottom-colors + -moz-border-left-colors + -moz-border-radius + -moz-border-radius-topleft + -moz-border-radius-topright + -moz-border-radius-bottomleft + -moz-border-radius-bottomright + -moz-border-right-colors + -moz-border-top-colors + -moz-box + -moz-box-flex + -moz-box-shadow + -moz-box-sizing + -moz-column-count + -moz-column-gap + -moz-hyphens + -moz-linear-gradient + -moz-opacity + -moz-outline-style + -moz-perspective + -moz-radial-gradient + -moz-resize + -moz-transform + -moz-transform-origin + -moz-transform-style + -moz-transition + -moz-transition-property + -moz-transition-duration + + + -o-background-size + -o-linear-gradient + -o-text-overflow + -o-transition + -o-transform-origin + + + konq_bgpos_x + konq_bgpos_y + -khtml-background-size + -khtml-border-top-left-radius + -khtml-border-top-right-radius + -khtml-border-bottom-left-radius + -khtml-border-bottom-right-radius + -khtml-border-radius + -khtml-box-shadow + -khtml-opacity + + + -webkit-appearance + -webkit-animation-name + -webkit-animation-duration + -webkit-animation-iteration + -webkit-animation-direction + -webkit-animation-delay + -webkit-animation-play-state + -webkit-animation-fill-mode + -webkit-background-size + -webkit-border-image + -webkit-border-bottom-colors + -webkit-border-left-colors + -webkit-border-radius + -webkit-border-right-colors + -webkit-border-top-colors + -webkit-border-top-left-radius + -webkit-border-top-right-radius + -webkit-border-bottom-left-radius + -webkit-border-bottom-right-radius + -webkit-border-radius-bottomleft + -webkit-border-radius-bottomright + -webkit-box-flex + -webkit-box-reflect + -webkit-box-shadow + -webkit-box-sizing + -webkit-column-count + -webkit-column-gap + -webkit-hyphens + -webkit-linear-gradient + -webkit-gradient + -webkit-perspective + -webkit-text-fill-color + -webkit-text-stroke-color + -webkit-text-stroke-width + -webkit-text-size-adjust + -webkit-transform + -webkit-transform-origin + -webkit-transform-style + -webkit-transition + -webkit-transition-property + -webkit-transition-duration + + + filter + zoom + -ms-animation-name + -ms-animation-duration + -ms-animation-iteration + -ms-animation-direction + -ms-animation-delay + -ms-animation-play-state + -ms-animation-fill-mode + -ms-box-sizing + -ms-filter + -ms-interpolation-mode + -ms-linear-gradient + -ms-text-size-adjust + -ms-transform + -ms-transition + + + font-family + font-size + font-stretch + font-style + font-variant + font-weight + unicode-range + units-per-em + src + panose-1 + stemv + stemh + slope + cap-height + x-height + ascent + descent + widths + bbox + definition-src + baseline + centerline + mathline + topline + + + + inherit + none + hidden + dotted + dashed + solid + double + groove + ridge + inset + outset + xx-small + x-small + small + medium + large + x-large + xx-large + smaller + larger + italic + oblique + small-caps + normal + bold + bolder + lighter + light + 100 + 200 + 300 + 400 + 500 + 600 + 700 + 800 + 900 + transparent + repeat + repeat-x + repeat-y + no-repeat + baseline + sub + super + top + text-top + middle + bottom + text-bottom + left + right + center + justify + konq-center + disc + circle + square + box + decimal + decimal-leading-zero + lower-roman + upper-roman + lower-greek + lower-alpha + lower-latin + upper-alpha + upper-latin + hebrew + armenian + georgian + cjk-ideographic + hiragana + katakana + hiragana-iroha + katakana-iroha + inline + inline-block + block + list-item + run-in + compact + marker + table + inline-table + table-row-group + table-header-group + table-footer-group + table-row + table-column-group + table-column + table-cell + table-caption + auto + crosshair + default + pointer + move + e-resize + ne-resize + nw-resize + n-resize + se-resize + sw-resize + s-resize + w-resize + text + wait + help + above + absolute + always + avoid + below + bidi-override + blink + both + capitalize + caption + clip + close-quote + collapse + condensed + crop + cross + ellipsis + ellipsis-word + embed + expanded + extra-condensed + extra-expanded + fixed + hand + hide + higher + icon + inside + invert + landscape + level + line-through + loud + lower + lowercase + ltr + menu + message-box + mix + narrower + no-close-quote + no-open-quote + nowrap + open-quote + outside + overline + portrait + pre + pre-line + pre-wrap + relative + rtl + scroll + semi-condensed + semi-expanded + separate + show + small-caption + static + static-position + status-bar + thick + thin + ultra-condensed + ultra-expanded + underline + uppercase + visible + wider + break + serif + sans-serif + cursive + fantasy + monospace + border-box + content-box + -epub-hyphens + + + + + aqua + black + blue + cyan + fuchsia + gray + green + lime + maroon + navy + olive + purple + red + silver + teal + white + yellow + ActiveBorder + ActiveCaption + AppWorkspace + Background + ButtonFace + ButtonHighlight + ButtonShadow + ButtonText + CaptionText + GrayText + Highlight + HighlightText + InactiveBorder + InactiveCaption + InactiveCaptionText + InfoBackground + InfoText + Menu + MenuText + Scrollbar + ThreeDDarkShadow + ThreeDFace + ThreeDHighlight + ThreeDLightShadow + ThreeDShadow + Window + WindowFrame + WindowText + + + + url + attr + rect + rgb + rgba + hsl + hsla + counter + counters + + + local + format + + + expression + + + + + all + aural + braille + embossed + handheld + print + projection + screen + tty + tv + + + + hover + link + visited + active + focus + first-child + last-child + only-child + first-of-type + last-of-type + only-of-type + first-letter + first-line + before + after + selection + root + empty + target + enabled + disabled + checked + indeterminate + nth-child + nth-last-child + nth-of-type + nth-last-of-type + not + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/kate/part/syntax/data/lex.xml b/kate/part/syntax/data/lex.xml new file mode 100644 index 00000000..f2daebbb --- /dev/null +++ b/kate/part/syntax/data/lex.xml @@ -0,0 +1,144 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/lilypond.xml b/kate/part/syntax/data/lilypond.xml new file mode 100644 index 00000000..b1794502 --- /dev/null +++ b/kate/part/syntax/data/lilypond.xml @@ -0,0 +1,1199 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]> + + + + + + + ChoirStaff + ChordNames + CueVoice + Devnull + DrumStaff + DrumVoice + Dynamics + FiguredBass + FretBoards + Global + GrandStaff + GregorianTranscriptionStaff + GregorianTranscriptionVoice + Lyrics + MensuralStaff + MensuralVoice + NoteNames + PianoStaff + RhythmicStaff + Score + Staff + StaffGroup + TabStaff + TabVoice + Timing + VaticanaStaff + VaticanaVoice + Voice + + + InnerChoirStaff + InnerStaffGroup + + + Accidental + AccidentalCautionary + AccidentalPlacement + AccidentalSuggestion + Ambitus + AmbitusAccidental + AmbitusLine + AmbitusNoteHead + Arpeggio + BalloonTextItem + BarLine + BarNumber + BassFigure + BassFigureAlignment + BassFigureAlignmentPositioning + BassFigureBracket + BassFigureContinuation + BassFigureLine + Beam + BendAfter + BreakAlignGroup + BreakAlignment + BreathingSign + ChordName + Clef + ClusterSpanner + ClusterSpannerBeacon + CombineTextScript + Custos + DotColumn + Dots + DoublePercentRepeat + DoublePercentRepeatCounter + DynamicLineSpanner + DynamicText + DynamicTextSpanner + Episema + Fingering + FretBoard + Glissando + GraceSpacing + GridLine + GridPoint + Hairpin + HarmonicParenthesesItem + HorizontalBracket + InstrumentName + InstrumentSwitch + KeyCancellation + KeySignature + LaissezVibrerTie + LaissezVibrerTieColumn + LedgerLineSpanner + LeftEdge + LigatureBracket + LyricExtender + LyricHyphen + LyricSpace + LyricText + MeasureGrouping + MelodyItem + MensuralLigature + MetronomeMark + MultiMeasureRest + MultiMeasureRestNumber + MultiMeasureRestText + NonMusicalPaperColumn + NoteCollision + NoteColumn + NoteHead + NoteName + NoteSpacing + OctavateEight + OttavaBracket + PaperColumn + ParenthesesItem + PercentRepeat + PercentRepeatCounter + PhrasingSlur + PianoPedalBracket + RehearsalMark + RepeatSlash + RepeatTie + RepeatTieColumn + Rest + RestCollision + Script + ScriptColumn + ScriptRow + SeparationItem + Slur + SostenutoPedal + SostenutoPedalLineSpanner + SpacingSpanner + SpanBar + StaffGrouper + StaffSpacing + StaffSymbol + StanzaNumber + Stem + StemTremolo + StringNumber + StrokeFinger + SustainPedal + SustainPedalLineSpanner + System + SystemStartBar + SystemStartBrace + SystemStartBracket + SystemStartSquare + TabNoteHead + TextScript + TextSpanner + Tie + TieColumn + TimeSignature + TrillPitchAccidental + TrillPitchGroup + TrillPitchHead + TrillSpanner + TupletBracket + TupletNumber + UnaCordaPedal + UnaCordaPedalLineSpanner + VaticanaLigature + VerticalAlignment + VerticalAxisGroup + VoiceFollower + VoltaBracket + VoltaBracketSpanner + + + aDueText + alignAboveContext + alignBassFigureAccidentals + alignBelowContext + allowBeamBreak + associatedVoice + autoAccidentals + autoBeamCheck + autoBeamSettings + autoBeaming + autoCautionaries + automaticBars + barAlways + barCheckSynchronize + barNumberVisibility + baseMoment + bassFigureFormatFunction + bassStaffProperties + beamExceptions + beatGrouping + beatLength + beatStructure + chordChanges + chordNameExceptions + chordNameExceptionsFull + chordNameExceptionsPartial + chordNameFunction + chordNameSeparator + chordNoteNamer + chordPrefixSpacer + chordRootNamer + clefGlyph + clefOctavation + clefPosition + connectArpeggios + countPercentRepeats + createKeyOnClefChange + createSpacing + crescendoSpanner + crescendoText + currentBarNumber + decrescendoSpanner + decrescendoText + defaultBarType + doubleSlurs + doubleRepeatType + drumPitchTable + drumStyleTable + dynamicAbsoluteVolumeFunction + explicitClefVisibility + explicitKeySignatureVisibility + extendersOverRests + extraNatural + figuredBassAlterationDirection + figuredBassCenterContinuations + figuredBassFormatter + figuredBassPlusDirection + fingeringOrientations + firstClef + followVoice + fontSize + forbidBreak + forceClef + gridInterval + hairpinToBarline + harmonicAccidentals + highStringOne + ignoreBarChecks + ignoreFiguredBassRest + ignoreMelismata + implicitBassFigures + implicitTimeSignatureVisibility + instrumentCueName + instrumentEqualizer + instrumentName + instrumentTransposition + internalBarNumber + keepAliveInterfaces + keyAlterationOrder + keySignature + lyricMelismaAlignment + majorSevenSymbol + markFormatter + maximumFretStretch + measureLength + measurePosition + melismaBusyProperties + metronomeMarkFormatter + middleCClefPosition + middleCOffset + middleCPosition + midiInstrument + midiMaximumVolume + midiMinimumVolume + minimumFret + minimumPageTurnLength + minimumRepeatLengthForPageTurn + noteToFretFunction + ottavation + output + pedalSostenutoStrings + pedalSostenutoStyle + pedalSustainStrings + pedalSustainStyle + pedalUnaCordaStrings + pedalUnaCordaStyle + printKeyCancellation + printOctaveNames + printPartCombineTexts + proportionalNotationDuration + recordEventSequence + rehearsalMark + repeatCommands + restNumberThreshold + scriptDefinitions + shapeNoteStyles + shortInstrumentName + shortVocalName + skipBars + skipTypesetting + soloIIText + soloText + squashedPosition + staffLineLayoutFunction + stanza + stemLeftBeamCount + stemRightBeamCount + stringNumberOrientations + stringOneTopmost + stringTunings + strokeFingerOrientations + subdivideBeams + suggestAccidentals + systemStartDelimiter + systemStartDelimiterHierarchy + tablatureFormat + tempoUnitCount + tempoUnitDuration + tempoWholesPerMinute + tieWaitForNote + timeSignatureFraction + timing + tonic + topLevelAlignment + trebleStaffProperties + tremoloFlags + tupletFullLength + tupletFullLengthNote + tupletSpannerDuration + useBassFigureExtenders + verticallySpacedContexts + vocalName + voltaOnThisStaff + voltaSpannerDuration + whichBar + + + barNumberAlignSymbol + centralCPosition + extraVerticalExtent + fingerHorizontalDirection + instr + instrument + keyAccidentalOrder + minimumVerticalExtent + rehearsalMarkAlignSymbol + soloADue + tupletNumberFormatFunction + vocNam + + + AbsoluteDynamicEvent + AnnotateOutputEvent + ApplyContext + ApplyOutputEvent + ArpeggioEvent + ArticulationEvent + AutoChangeMusic + BarCheck + BassFigureEvent + BeamEvent + BeamForbidEvent + BendAfterEvent + BreathingEvent + ClusterNoteEvent + ContextChange + ContextSpeccedMusic + CrescendoEvent + DecrescendoEvent + Event + EventChord + ExtenderEvent + FingeringEvent + GlissandoEvent + GraceMusic + HarmonicEvent + HyphenEvent + KeyChangeEvent + LabelEvent + LaissezVibrerEvent + LigatureEvent + LineBreakEvent + LyricCombineMusic + LyricEvent + MarkEvent + MultiMeasureRestEvent + MultiMeasureRestMusic + MultiMeasureTextEvent + Music + NoteEvent + NoteGroupingEvent + OverrideProperty + PageBreakEvent + PageTurnEvent + PartCombineMusic + PercentEvent + PercentRepeatedMusic + PesOrFlexaEvent + PhrasingSlurEvent + PropertySet + PropertyUnset + QuoteMusic + RelativeOctaveCheck + RelativeOctaveMusic + RepeatTieEvent + RepeatedMusic + RestEvent + RevertProperty + ScriptEvent + SequentialMusic + SimultaneousMusic + SkipEvent + SkipMusic + SlurEvent + SoloOneEvent + SoloTwoEvent + SostenutoEvent + SpacingSectionEvent + SpanEvent + StaffSpanEvent + StringNumberEvent + StrokeFingerEvent + SustainEvent + TextScriptEvent + TextSpanEvent + TieEvent + TimeScaledMusic + TransposedMusic + TremoloEvent + TremoloRepeatedMusic + TremoloSpanEvent + TrillSpanEvent + TupletSpanEvent + UnaCordaEvent + UnfoldedRepeatedMusic + UnisonoEvent + UnrelativableMusic + VoiceSeparator + VoltaRepeatedMusic + + + acousticbassdrum + acousticsnare + agh + agl + bassdrum + bd + bda + boh + bohm + boho + bol + bolm + bolo + cab + cabasa + cb + cgh + cghm + cgho + cgl + cglm + cglo + chinesecymbal + cl + claves + closedhihat + cowbell + crashcymbal + crashcymbala + crashcymbalb + cuim + cuio + cymc + cymca + cymcb + cymch + cymr + cymra + cymrb + cyms + da + db + dc + dd + de + electricsnare + fivedown + fiveup + fourdown + fourup + gui + guil + guiro + guis + halfopenhihat + handclap + hc + hh + hhc + hhho + hho + hhp + hiagogo + hibongo + hiconga + highfloortom + hightom + hihat + himidtom + hisidestick + hitimbale + hiwoodblock + loagogo + lobongo + loconga + longguiro + longwhistle + losidestick + lotimbale + lowfloortom + lowmidtom + lowoodblock + lowtom + mar + maracas + mutecuica + mutehibongo + mutehiconga + mutelobongo + muteloconga + mutetriangle + onedown + oneup + opencuica + openhibongo + openhiconga + openhihat + openlobongo + openloconga + opentriangle + pedalhihat + rb + ridebell + ridecymbal + ridecymbala + ridecymbalb + shortguiro + shortwhistle + sidestick + sn + sna + snare + sne + splashcymbal + ss + ssh + ssl + tamb + tambourine + tamtam + threedown + threeup + timh + timl + tomfh + tomfl + tomh + toml + tommh + tomml + tri + triangle + trim + trio + tt + twodown + twoup + ua + ub + uc + ud + ue + vibraslap + vibs + wbh + wbl + whl + whs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/list-keywords-ldif.awk b/kate/part/syntax/data/list-keywords-ldif.awk new file mode 100644 index 00000000..b1b02703 --- /dev/null +++ b/kate/part/syntax/data/list-keywords-ldif.awk @@ -0,0 +1,38 @@ +#!/bin/awk -f + +function printName (name) +{ + if ((name != "") && (name != "(") && (name != ")")) print type, name; +} + +function printNames (str) +{ + s = substr(str,index(str,"NAME")+4); + split (s, a, " "); +# for (i=0; i" +cat $schemas | $list_keywords | grep "attributetype" | grep -v '^#' | sed -e "s/'//g" | sort -u | awk '{print " "$2""}' +echo " " +echo " " +cat $schemas | $list_keywords | grep "objectclass" | grep -v '^#' | sed -e "s/'//g" | sort -u | awk '{print " "$2""}' +echo " " + diff --git a/kate/part/syntax/data/literate-curry.xml b/kate/part/syntax/data/literate-curry.xml new file mode 100644 index 00000000..d6c170c1 --- /dev/null +++ b/kate/part/syntax/data/literate-curry.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/literate-haskell.xml b/kate/part/syntax/data/literate-haskell.xml new file mode 100644 index 00000000..2ffcf60b --- /dev/null +++ b/kate/part/syntax/data/literate-haskell.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/logtalk.xml b/kate/part/syntax/data/logtalk.xml new file mode 100644 index 00000000..17e130c8 --- /dev/null +++ b/kate/part/syntax/data/logtalk.xml @@ -0,0 +1,206 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/lpc.xml b/kate/part/syntax/data/lpc.xml new file mode 100644 index 00000000..2b13ef7f --- /dev/null +++ b/kate/part/syntax/data/lpc.xml @@ -0,0 +1,166 @@ + + + + + + + + + + + + + private + protected + static + public + nomask + varargs + nosave + virtual + + + void + int + status + string + object + array + mapping + closure + symbol + float + mixed + + + break + continue + return + if + else + for + foreach + do + while + switch + case + inherit + default + variables + functions + publish + nolog + + + FIXME + HACK + NOTE + NOTICE + TODO + WARNING + ### + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/lsl.xml b/kate/part/syntax/data/lsl.xml new file mode 100644 index 00000000..71e41510 --- /dev/null +++ b/kate/part/syntax/data/lsl.xml @@ -0,0 +1,1050 @@ + + + + + + + + + +]> + + + + + + integer + float + string + key + vector + quaternion + rotation + list + + + default + state + event + jump + return + if + else + for + do + while + + + state_entry + state_exit + touch_start + touch + touch_end + collision_start + collision + collision_end + land_collision_start + land_collision + land_collision_end + timer + listen + sensor + no_sensor + control + print + at_target + not_at_target + at_rot_target + not_at_rot_target + money + email + run_time_permissions + changed + attach + dataserver + moving_start + moving_end + link_message + on_rez + object_rez + remote_data + http_response + + + TRUE + FALSE + STATUS_PHYSICS + STATUS_ROTATE_X + STATUS_ROTATE_Y + STATUS_ROTATE_Z + STATUS_PHANTOM + STATUS_SANDBOX + STATUS_BLOCK_GRAB + STATUS_DIE_AT_EDGE + STATUS_RETURN_AT_EDGE + STATUS_CAST_SHADOWS + + AGENT_FLYING + AGENT_ATTACHMENTS + AGENT_SCRIPTED + AGENT_MOUSELOOK + AGENT_SITTING + AGENT_ON_OBJECT + AGENT_AWAY + AGENT_WALKING + AGENT_IN_AIR + AGENT_TYPING + AGENT_CROUCHING + AGENT_BUSY + AGENT_ALWAYS_RUN + + CAMERA_PITCH + CAMERA_FOCUS_OFFSET + CAMERA_POSITION_LAG + CAMERA_FOCUS_LAG + CAMERA_DISTANCE + CAMERA_BEHINDNESS_ANGLE + CAMERA_BEHINDNESS_LAG + CAMERA_POSITION_THRESHOLD + CAMERA_FOCUS_THRESHOLD + CAMERA_ACTIVE + CAMERA_POSITION + CAMERA_FOCUS + CAMERA_POSITION_LOCKED + CAMERA_FOCUS_LOCKED + + ANIM_ON + LOOP + REVERSE + PING_PONG + SMOOTH + ROTATE + SCALE + + ALL_SIDES + LINK_ROOT + LINK_SET + LINK_ALL_OTHERS + LINK_ALL_CHILDREN + LINK_THIS + + AGENT + ACTIVE + PASSIVE + SCRIPTED + + CONTROL_FWD + CONTROL_BACK + CONTROL_LEFT + CONTROL_RIGHT + CONTROL_ROT_LEFT + CONTROL_ROT_RIGHT + CONTROL_UP + CONTROL_DOWN + CONTROL_LBUTTON + CONTROL_ML_LBUTTON + + PERMISSION_DEBIT + PERMISSION_TAKE_CONTROLS + PERMISSION_REMAP_CONTROLS + PERMISSION_TRIGGER_ANIMATION + PERMISSION_ATTACH + PERMISSION_RELEASE_OWNERSHIP + PERMISSION_CHANGE_LINKS + PERMISSION_CHANGE_JOINTS + PERMISSION_CHANGE_PERMISSIONS + PERMISSION_TRACK_CAMERA + PERMISSION_CONTROL_CAMERA + + INVENTORY_TEXTURE + INVENTORY_SOUND + INVENTORY_OBJECT + INVENTORY_SCRIPT + INVENTORY_LANDMARK + INVENTORY_CLOTHING + INVENTORY_NOTECARD + INVENTORY_BODYPART + INVENTORY_ANIMATION + INVENTORY_GESTURE + INVENTORY_ALL + INVENTORY_NONE + + CHANGED_INVENTORY + CHANGED_COLOR + CHANGED_SHAPE + CHANGED_SCALE + CHANGED_TEXTURE + CHANGED_LINK + CHANGED_ALLOWED_DROP + CHANGED_OWNER + CHANGED_REGION + CHANGED_TELEPORT + + OBJECT_UNKNOWN_DETAIL + OBJECT_NAME + OBJECT_DESC + OBJECT_POS + OBJECT_ROT + OBJECT_VELOCITY + OBJECT_OWNER + OBJECT_GROUP + OBJECT_CREATOR + + TYPE_INTEGER + TYPE_FLOAT + TYPE_STRING + TYPE_KEY + TYPE_VECTOR + TYPE_ROTATION + TYPE_INVALID + + NULL_KEY + EOF + + PI + TWO_PI + PI_BY_TWO + DEG_TO_RAD + RAD_TO_DEG + SQRT2 + + DEBUG_CHANNEL + PUBLIC_CHANNEL + + ZERO_VECTOR + ZERO_ROTATION + + ATTACH_CHEST + ATTACH_HEAD + ATTACH_LSHOULDER + ATTACH_RSHOULDER + ATTACH_LHAND + ATTACH_RHAND + ATTACH_LFOOT + ATTACH_RFOOT + ATTACH_BACK + ATTACH_PELVIS + ATTACH_MOUTH + ATTACH_CHIN + ATTACH_LEAR + ATTACH_REAR + ATTACH_LEYE + ATTACH_REYE + ATTACH_NOSE + ATTACH_RUARM + ATTACH_RLARM + ATTACH_LUARM + ATTACH_LLARM + ATTACH_RHIP + ATTACH_RULEG + ATTACH_RLLEG + ATTACH_LHIP + ATTACH_LULEG + ATTACH_LLLEG + ATTACH_BELLY + ATTACH_LPEC + ATTACH_RPEC + ATTACH_HUD_CENTER_2 + ATTACH_HUD_TOP_RIGHT + ATTACH_HUD_TOP_CENTER + ATTACH_HUD_TOP_LEFT + ATTACH_HUD_CENTER_1 + ATTACH_HUD_BOTTOM_LEFT + ATTACH_HUD_BOTTOM + ATTACH_HUD_BOTTOM_RIGHT + + LAND_LEVEL + LAND_RAISE + LAND_LOWER + LAND_SMOOTH + LAND_NOISE + LAND_REVERT + + LAND_SMALL_BRUSH + LAND_MEDIUM_BRUSH + LAND_LARGE_BRUSH + + DATA_ONLINE + DATA_NAME + DATA_BORN + DATA_RATING + DATA_SIM_POS + DATA_SIM_STATUS + DATA_SIM_RATING + DATA_PAYINFO + + PAYMENT_INFO_ON_FILE + PAYMENT_INFO_USED + + REMOTE_DATA_CHANNEL + REMOTE_DATA_REQUEST + REMOTE_DATA_REPLY + + + PSYS_PART_FLAGS + PSYS_PART_START_COLOR + PSYS_PART_START_ALPHA + PSYS_PART_START_SCALE + PSYS_PART_END_COLOR + PSYS_PART_END_ALPHA + PSYS_PART_END_SCALE + PSYS_PART_MAX_AGE + + + PSYS_PART_WIND_MASK + PSYS_PART_INTERP_COLOR_MASK + PSYS_PART_INTERP_SCALE_MASK + PSYS_PART_BOUNCE_MASK + PSYS_PART_FOLLOW_SRC_MASK + PSYS_PART_FOLLOW_VELOCITY_MASK + PSYS_PART_TARGET_POS_MASK + PSYS_PART_EMISSIVE_MASK + PSYS_PART_TARGET_LINEAR_MASK + + + PSYS_SRC_MAX_AGE + PSYS_SRC_PATTERN + PSYS_SRC_INNERANGLE + PSYS_SRC_OUTERANGLE + PSYS_SRC_ANGLE_BEGIN + PSYS_SRC_ANGLE_END + PSYS_SRC_BURST_RATE + PSYS_SRC_BURST_PART_COUNT + PSYS_SRC_BURST_RADIUS + PSYS_SRC_BURST_SPEED_MIN + PSYS_SRC_BURST_SPEED_MAX + PSYS_SRC_ACCEL + PSYS_SRC_TEXTURE + PSYS_SRC_TARGET_KEY + PSYS_SRC_OMEGA + + PSYS_SRC_OBJ_REL_MASK + + PSYS_SRC_PATTERN_DROP + PSYS_SRC_PATTERN_EXPLODE + PSYS_SRC_PATTERN_ANGLE + PSYS_SRC_PATTERN_ANGLE_CONE + PSYS_SRC_PATTERN_ANGLE_CONE_EMPTY + + + VEHICLE_TYPE_NONE + VEHICLE_TYPE_SLED + VEHICLE_TYPE_CAR + VEHICLE_TYPE_BOAT + VEHICLE_TYPE_AIRPLANE + VEHICLE_TYPE_BALLOON + + VEHICLE_REFERENCE_FRAME + VEHICLE_LINEAR_FRICTION_TIMESCALE + VEHICLE_ANGULAR_FRICTION_TIMESCALE + VEHICLE_LINEAR_MOTOR_DIRECTION + VEHICLE_ANGULAR_MOTOR_DIRECTION + VEHICLE_LINEAR_MOTOR_OFFSET + + + + VEHICLE_HOVER_HEIGHT + VEHICLE_HOVER_EFFICIENCY + VEHICLE_HOVER_TIMESCALE + VEHICLE_BUOYANCY + + VEHICLE_LINEAR_DEFLECTION_EFFICIENCY + VEHICLE_LINEAR_DEFLECTION_TIMESCALE + VEHICLE_LINEAR_MOTOR_TIMESCALE + VEHICLE_LINEAR_MOTOR_DECAY_TIMESCALE + + VEHICLE_ANGULAR_DEFLECTION_EFFICIENCY + VEHICLE_ANGULAR_DEFLECTION_TIMESCALE + VEHICLE_ANGULAR_MOTOR_TIMESCALE + VEHICLE_ANGULAR_MOTOR_DECAY_TIMESCALE + + VEHICLE_VERTICAL_ATTRACTION_EFFICIENCY + VEHICLE_VERTICAL_ATTRACTION_TIMESCALE + + VEHICLE_BANKING_EFFICIENCY + VEHICLE_BANKING_MIX + VEHICLE_BANKING_TIMESCALE + + VEHICLE_FLAG_NO_FLY_UP + VEHICLE_FLAG_NO_DEFLECTION_UP + VEHICLE_FLAG_LIMIT_ROLL_ONLY + VEHICLE_FLAG_HOVER_WATER_ONLY + VEHICLE_FLAG_HOVER_TERRAIN_ONLY + VEHICLE_FLAG_HOVER_GLOBAL_HEIGHT + VEHICLE_FLAG_HOVER_UP_ONLY + VEHICLE_FLAG_LIMIT_MOTOR_UP + VEHICLE_FLAG_MOUSELOOK_STEER + VEHICLE_FLAG_MOUSELOOK_BANK + VEHICLE_FLAG_CAMERA_DECOUPLED + + + + PRIM_TYPE + PRIM_MATERIAL + PRIM_PHYSICS + PRIM_FLEXIBLE + PRIM_POINT_LIGHT + PRIM_TEMP_ON_REZ + PRIM_PHANTOM + PRIM_CAST_SHADOWS + PRIM_POSITION + PRIM_SIZE + PRIM_ROTATION + PRIM_TEXTURE + PRIM_COLOR + PRIM_BUMP_SHINY + PRIM_FULLBRIGHT + PRIM_TEXGEN + PRIM_GLOW + + PRIM_TYPE_BOX + PRIM_TYPE_CYLINDER + PRIM_TYPE_PRISM + PRIM_TYPE_SPHERE + PRIM_TYPE_TORUS + PRIM_TYPE_TUBE + PRIM_TYPE_RING + PRIM_TYPE_SCULPT + + PRIM_HOLE_DEFAULT + PRIM_HOLE_CIRCLE + PRIM_HOLE_SQUARE + PRIM_HOLE_TRIANGLE + + PRIM_MATERIAL_STONE + PRIM_MATERIAL_METAL + PRIM_MATERIAL_GLASS + PRIM_MATERIAL_WOOD + PRIM_MATERIAL_FLESH + PRIM_MATERIAL_PLASTIC + PRIM_MATERIAL_RUBBER + PRIM_MATERIAL_LIGHT + + PRIM_SHINY_NONE + PRIM_SHINY_LOW + PRIM_SHINY_MEDIUM + PRIM_SHINY_HIGH + + PRIM_BUMP_NONE + PRIM_BUMP_BRIGHT + PRIM_BUMP_DARK + PRIM_BUMP_WOOD + PRIM_BUMP_BARK + PRIM_BUMP_BRICKS + PRIM_BUMP_CHECKER + PRIM_BUMP_CONCRETE + PRIM_BUMP_TILE + PRIM_BUMP_STONE + PRIM_BUMP_DISKS + PRIM_BUMP_GRAVEL + PRIM_BUMP_BLOBS + PRIM_BUMP_SIDING + PRIM_BUMP_LARGETILE + PRIM_BUMP_STUCCO + PRIM_BUMP_SUCTION + PRIM_BUMP_WEAVE + + PRIM_TEXGEN_DEFAULT + PRIM_TEXGEN_PLANAR + + PRIM_SCULPT_TYPE_SPHERE + PRIM_SCULPT_TYPE_TORUS + PRIM_SCULPT_TYPE_PLANE + PRIM_SCULPT_TYPE_CYLINDER + PRIM_SCULPT_TYPE_MASK + PRIM_SCULPT_FLAG_MIRROR + PRIM_SCULPT_FLAG_INVERT + + MASK_BASE + MASK_OWNER + MASK_GROUP + MASK_EVERYONE + MASK_NEXT + + PERM_TRANSFER + PERM_MODIFY + PERM_COPY + PERM_MOVE + PERM_ALL + + PARCEL_MEDIA_COMMAND_STOP + PARCEL_MEDIA_COMMAND_PAUSE + PARCEL_MEDIA_COMMAND_PLAY + PARCEL_MEDIA_COMMAND_LOOP + PARCEL_MEDIA_COMMAND_TEXTURE + PARCEL_MEDIA_COMMAND_URL + PARCEL_MEDIA_COMMAND_TIME + PARCEL_MEDIA_COMMAND_AGENT + PARCEL_MEDIA_COMMAND_UNLOAD + PARCEL_MEDIA_COMMAND_AUTO_ALIGN + PARCEL_MEDIA_COMMAND_TYPE + PARCEL_MEDIA_COMMAND_SIZE + PARCEL_MEDIA_COMMAND_DESC + PARCEL_MEDIA_COMMAND_LOOP_SET + + LIST_STAT_MAX + LIST_STAT_MIN + LIST_STAT_MEAN + LIST_STAT_MEDIAN + LIST_STAT_STD_DEV + LIST_STAT_SUM + LIST_STAT_SUM_SQUARES + LIST_STAT_NUM_COUNT + LIST_STAT_GEOMETRIC_MEAN + LIST_STAT_RANGE + + PAY_HIDE + PAY_DEFAULT + + PARCEL_FLAG_ALLOW_FLY + PARCEL_FLAG_ALLOW_GROUP_SCRIPTS + PARCEL_FLAG_ALLOW_SCRIPTS + PARCEL_FLAG_ALLOW_LANDMARK + PARCEL_FLAG_ALLOW_TERRAFORM + PARCEL_FLAG_ALLOW_DAMAGE + PARCEL_FLAG_ALLOW_CREATE_OBJECTS + PARCEL_FLAG_ALLOW_CREATE_GROUP_OBJECTS + PARCEL_FLAG_USE_ACCESS_GROUP + PARCEL_FLAG_USE_ACCESS_LIST + PARCEL_FLAG_USE_BAN_LIST + PARCEL_FLAG_USE_LAND_PASS_LIST + PARCEL_FLAG_LOCAL_SOUND_ONLY + PARCEL_FLAG_RESTRICT_PUSHOBJECT + PARCEL_FLAG_ALLOW_GROUP_OBJECT_ENTRY + PARCEL_FLAG_ALLOW_ALL_OBJECT_ENTRY + + REGION_FLAG_ALLOW_DAMAGE + REGION_FLAG_FIXED_SUN + REGION_FLAG_BLOCK_TERRAFORM + REGION_FLAG_SANDBOX + REGION_FLAG_DISABLE_COLLISIONS + REGION_FLAG_DISABLE_PHYSICS + REGION_FLAG_BLOCK_FLY + REGION_FLAG_ALLOW_DIRECT_TELEPORT + REGION_FLAG_RESTRICT_PUSHOBJECT + + HTTP_METHOD + HTTP_MIMETYPE + HTTP_BODY_MAXLENGTH + HTTP_BODY_TRUNCATED + HTTP_VERIFY_CERT + + PARCEL_COUNT_TOTAL + PARCEL_COUNT_OWNER + PARCEL_COUNT_GROUP + PARCEL_COUNT_OTHER + PARCEL_COUNT_SELECTED + PARCEL_COUNT_TEMP + + PARCEL_DETAILS_NAME + PARCEL_DETAILS_DESC + PARCEL_DETAILS_OWNER + PARCEL_DETAILS_GROUP + PARCEL_DETAILS_AREA + + STRING_TRIM_HEAD + STRING_TRIM_TAIL + STRING_TRIM + + CLICK_ACTION_NONE + CLICK_ACTION_TOUCH + CLICK_ACTION_SIT + CLICK_ACTION_BUY + CLICK_ACTION_PAY + CLICK_ACTION_OPEN + CLICK_ACTION_PLAY + CLICK_ACTION_OPEN_MEDIA + + TEXTURE_BLANK + TEXTURE_DEFAULT + TEXTURE_MEDIA + TEXTURE_PLYWOOD + TEXTURE_TRANSPARENT + + TOUCH_INVALID_FACE + TOUCH_INVALID_VECTOR + TOUCH_INVALID_TEXCOORD + + + ++ + -- + += + -= + *= + /= + %= + ; + , + = + ( + ) + - + + + * + / + % + @ + : + > + < + == + != + >= + <= + & + | + ^ + ~ + ! + && + || + << + >> + + + + + llSin + llCos + llTan + llAtan2 + llSqrt + llPow + llAbs + llFabs + llFrand + llFloor + llCeil + llRound + llVecMag + llVecNorm + llVecDist + llRot2Euler + llEuler2Rot + llAxes2Rot + llRot2Fwd + llRot2Left + llRot2Up + llRotBetween + llWhisper + llSay + llShout + llListen + llListenControl + llListenRemove + llSensor + llSensorRepeat + llSensorRemove + llDetectedName + llDetectedKey + llDetectedOwner + llDetectedType + llDetectedPos + llDetectedVel + llDetectedGrab + llDetectedRot + llDetectedGroup + llDetectedLinkNumber + llDie + llGround + llCloud + llWind + llSetStatus + llGetStatus + llSetScale + llGetScale + llSetColor + llGetAlpha + llSetAlpha + llGetColor + llSetTexture + llScaleTexture + llOffsetTexture + llRotateTexture + llGetTexture + llSetPos + llGetPos + llGetLocalPos + llSetRot + llGetRot + llGetLocalRot + llSetForce + llGetForce + llTarget + llTargetRemove + llRotTarget + llRotTargetRemove + llMoveToTarget + llStopMoveToTarget + llApplyImpulse + llApplyRotationalImpulse + llSetTorque + llGetTorque + llSetForceAndTorque + llGetVel + llGetAccel + llGetOmega + llGetTimeOfDay + llGetWallclock + llGetTime + llResetTime + llGetAndResetTime + llSound + llPlaySound + llLoopSound + llLoopSoundMaster + llLoopSoundSlave + llPlaySoundSlave + llTriggerSound + llStopSound + llPreloadSound + llGetSubString + llDeleteSubString + llInsertString + llToUpper + llToLower + llGiveMoney + llMakeExplosion + llMakeFountain + llMakeSmoke + llMakeFire + llRezObject + llLookAt + llStopLookAt + llSetTimerEvent + llSleep + llGetMass + llCollisionFilter + llTakeControls + llReleaseControls + llAttachToAvatar + llDetachFromAvatar + llTakeCamera + llReleaseCamera + llGetOwner + llInstantMessage + llEmail + llGetNextEmail + llGetKey + llSetBuoyancy + llSetHoverHeight + llStopHover + llMinEventDelay + llSoundPreload + llRotLookAt + llStringLength + llStartAnimation + llStopAnimation + llPointAt + llStopPointAt + llTargetOmega + llGetStartParameter + llGodLikeRezObject + llRequestPermissions + llGetPermissionsKey + llGetPermissions + llGetLinkNumber + llSetLinkColor + llCreateLink + llBreakLink + llBreakAllLinks + llGetLinkKey + llGetLinkName + llGetInventoryNumber + llGetInventoryName + llSetScriptState + llGetEnergy + llGiveInventory + llRemoveInventory + llSetText + llWater + llPassTouches + llRequestAgentData + llRequestInventoryData + llSetDamage + llTeleportAgentHome + llModifyLand + llCollisionSound + llCollisionSprite + llGetAnimation + llResetScript + llMessageLinked + llPushObject + llPassCollisions + llGetScriptName + llGetNumberOfSides + llAxisAngle2Rot + llRot2Axis + llRot2Angle + llAcos + llAsin + llAngleBetween + llGetInventoryKey + llAllowInventoryDrop + llGetSunDirection + llGetTextureOffset + llGetTextureScale + llGetTextureRot + llSubStringIndex + llGetOwnerKey + llGetCenterOfMass + llListSort + llGetListLength + llList2Integer + llList2Float + llList2String + llList2Key + llList2Vector + llList2Rot + llList2List + llDeleteSubList + llGetListEntryType + llList2CSV + llCSV2List + llListRandomize + llList2ListStrided + llGetRegionCorner + llListInsertList + llListFindList + llGetObjectName + llSetObjectName + llGetDate + llEdgeOfWorld + llGetAgentInfo + llAdjustSoundVolume + llSetSoundQueueing + llSetSoundRadius + llKey2Name + llSetTextureAnim + llTriggerSoundLimited + llEjectFromLand + llParseString2List + llOverMyLand + llGetLandOwnerAt + llGetNotecardLine + llGetAgentSize + llSameGroup + llUnSit + llGroundSlope + llGroundNormal + llGroundContour + llGetAttached + llGetFreeMemory + llGetRegionName + llGetRegionTimeDilation + llGetRegionFPS + + llParticleSystem + llGroundRepel + llGiveInventoryList + + llSetVehicleType + llSetVehicleFloatParam + llSetVehicleVectorParam + llSetVehicleRotationParam + llSetVehicleFlags + llRemoveVehicleFlags + llSitTarget + llAvatarOnSitTarget + llAddToLandPassList + llSetTouchText + llSetSitText + llSetCameraEyeOffset + llSetCameraAtOffset + + llDumpList2String + llScriptDanger + llDialog + llVolumeDetect + llResetOtherScript + llGetScriptState + llRemoteLoadScript + + llSetRemoteScriptAccessPin + llRemoteLoadScriptPin + + llOpenRemoteDataChannel + llSendRemoteData + llRemoteDataReply + llCloseRemoteDataChannel + + llMD5String + llSetPrimitiveParams + llStringToBase64 + llBase64ToString + llXorBase64Strings + llRemoteDataSetRegion + llLog10 + llLog + llGetAnimationList + llSetParcelMusicURL + + llGetRootPosition + llGetRootRotation + + llGetObjectDesc + llSetObjectDesc + llGetCreator + llGetTimestamp + llSetLinkAlpha + llGetNumberOfPrims + llGetNumberOfNotecardLines + + llGetBoundingBox + llGetGeometricCenter + llGetPrimitiveParams + llIntegerToBase64 + llBase64ToInteger + llGetGMTclock + llGetSimulatorHostname + + llSetLocalRot + + llParseStringKeepNulls + llRezAtRoot + + llGetObjectPermMask + llSetObjectPermMask + + llGetInventoryPermMask + llSetInventoryPermMask + llGetInventoryCreator + llOwnerSay + llRequestSimulatorData + llForceMouselook + llGetObjectMass + llListReplaceList + llLoadURL + + llParcelMediaCommandList + llParcelMediaQuery + + llModPow + + llGetInventoryType + llSetPayPrice + llGetCameraPos + llGetCameraRot + + llSetPrimURL + llRefreshPrimURL + llEscapeURL + llUnescapeURL + + llMapDestination + llAddToLandBanList + llRemoveFromLandPassList + llRemoveFromLandBanList + + llSetCameraParams + llClearCameraParams + + llListStatistics + llGetUnixTime + llGetParcelFlags + llGetRegionFlags + llXorBase64StringsCorrect + + llHTTPRequest + + llResetLandBanList + llResetLandPassList + + llGetObjectPrimCount + llGetParcelPrimOwners + llGetParcelPrimCount + llGetParcelMaxPrims + llGetParcelDetails + + llSetLinkPrimitiveParams + llSetLinkTexture + + + llStringTrim + llRegionSay + llGetObjectDetails + llSetClickAction + + llGetRegionAgentCount + llTextBox + llGetAgentLanguage + llDetectedTouchUV + llDetectedTouchFace + llDetectedTouchPos + llDetectedTouchNormal + llDetectedTouchBinormal + llDetectedTouchST + + llSHA1String + + + + + FIXME + TODO + BUG + HACK + XXX + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/lua.xml b/kate/part/syntax/data/lua.xml new file mode 100644 index 00000000..97e6b21b --- /dev/null +++ b/kate/part/syntax/data/lua.xml @@ -0,0 +1,300 @@ + + + + + + + and + function + in + local + not + or + + + break + do + else + elseif + end + for + if + repeat + return + then + until + while + + + nil + false + true + + + string.byte string.char + string.find string.len + string.lower string.rep + string.sub string.upper + string.format string.gfind + string.gsub table.concat + table.getn table.sort + table.insert table.remove + table.setn math.abs + math.sin math.cos + math.tan math.asin + math.acos math.atan + math.atan2 math.ceil + math.floor math.mod + math.frexp math.ldexp + math.squrt math.min + math.max math.log + math.log10 math.exp + math.deg math.rad + math.random math.randomseed + io.close io.flush + io.input io.lines + io.open io.output + io.read io.stderr + io.stdin io.stdout + io.tmpfile io.write + os.clock os.date + os.difftime os.execute + os.exit os.getenv + os.remove os.rename + os.setlocale os.time + os.tmpname debug.getinfo + debug.getlocal debug.setlocal + debug.sethook debug.gethook + assert collectgarbage + dofile error + next print + rawget rawset + tonumber tostring + type _ALERT + _ERRORMESSAGE call + getmetatable gcinfo + ipairs loadfile + loadstring pairs + pcall require + LUA_PATH setmetatable + _LOADED _VERSION + gettagmethod globals + newtag setglobal + settag settagmethod + setlinehook getglobals + copytagmethods dostring + getglobal tag + setglobals unpack + exit readfrom + writeto appendto + read write + getinfo getlocal + setlocal setcallhook + tinsert tremove + flush seek + setlocale execute + remove rename + tmpname getenv + getn sort + table.foreach table.foreachi + foreach foreachi + abs sin + cos tan + asin acos + atan atan2 + ceil floor + mod frexp + ldexp squrt + min max + log log10 + exp deg + rad random + randomseed strlen + strsub strlower + strupper strchar + strrep ascii + strbyte format + strfind gsub + openfile closefile + date clock + + + cgilua cgilua.lp.translate + cgilua.contentheader cgilua.script_file + cgilua.header cgilua.script_path + cgilua.htmlheader cgilua.script_pdir + cgilua.redirect cgilua.script_vdir + cgilua.mkabsoluteurl cgilua.script_vpath + cgilua.mkurlpath cgilua.servervariable + cgilua.put cgilua.urlpath + cgilua.handlelp cgilua.errorlog + cgilua.lp.compile cgilua.seterrorhandler + cgilua.lp.include cgilua.seterroroutput + cgilua.lp.setcompatmode cgilua.addclosefunction + cgilua.lp.setoutfunc cgilua.addopenfunction + cgilua.addscripthandler cgilua.addscripthandler + cgilua.buildprocesshandler cgilua.setmaxfilesize + cgilua.setmaxinput cgilua.urlcode.encodetable + cgilua.urlcode.escape cgilua.urlcode.parsequery + cgilua.urlcode.unescape cgilua.urlcode.insertfield + cgilua.setoutfunc cgilua.addopenfunction + cgilua.doif cgilua.doscript + cgilua.pack cgilua.splitpath + cgilua.cookies.get cgilua.cookies.set + cgilua.cookies.sethtml cgilua.cookies.delete + cgilua.serialize cgilua.session.close + cgilua.session.data cgilua.session.load + cgilua.session.new cgilua.session.open + cgilua.session.save cgilua.session.setsessiondir + cgilua.session.delete cgilua.session + cgilua.cookies + + numrows connect + close fetch + getcolnames getcoltypes + commit rollback + setautocommit + + lfs lfs.attributes + lfs.chdir lfs.currentdir + lfs.dir lfs.lock + lfs.mkdir lfs.rmdir + lfs.touch lfs.unlock + + zip zip.open + zip.openfile files + seek close + lines + + + + + TODO + FIXME + NOTE + + + table.foreach table.foreachi + foreach foreachi + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/m3u.xml b/kate/part/syntax/data/m3u.xml new file mode 100644 index 00000000..0c4b4ff3 --- /dev/null +++ b/kate/part/syntax/data/m3u.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/m4.xml b/kate/part/syntax/data/m4.xml new file mode 100644 index 00000000..09211f3a --- /dev/null +++ b/kate/part/syntax/data/m4.xml @@ -0,0 +1,193 @@ + + + + + + + __gnu__ + __os2__ + os2 + __unix__ + unix + __windows__ + windows + + + m4___gnu__ + m4___os2__ + m4_os2 + m4___unix__ + m4_unix + m4___windows__ + m4_windows + + + __file__ + __line__ + __program__ + builtin + changecom + changequote + changeword + debugfile + debugmode + decr + define + defn + divert + divnum + dnl + dumpdef + errprint + esyscmd + eval + format + ifdef + ifelse + include + incr + index + indir + len + m4exit + m4wrap + maketemp + mkstemp + patsubst + popdef + pushdef + regexp + shift + sinclude + substr + syscmd + sysval + traceon + traceoff + translit + undefine + undivert + + + m4___file__ + m4___line__ + m4___program__ + m4_builtin + m4_changecom + m4_changequote + m4_changeword + m4_debugfile + m4_debugmode + m4_decr + m4_define + m4_defn + m4_divert + m4_divnum + m4_dnl + m4_dumpdef + m4_errprint + m4_esyscmd + m4_eval + m4_format + m4_ifdef + m4_ifelse + m4_include + m4_incr + m4_index + m4_indir + m4_len + m4_m4exit + m4_m4wrap + m4_maketemp + m4_mkstemp + m4_patsubst + m4_popdef + m4_pushdef + m4_regexp + m4_shift + m4_sinclude + m4_substr + m4_syscmd + m4_sysval + m4_traceon + m4_traceoff + m4_translit + m4_undefine + m4_undivert + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/mab.xml b/kate/part/syntax/data/mab.xml new file mode 100644 index 00000000..338d2407 --- /dev/null +++ b/kate/part/syntax/data/mab.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/makefile.xml b/kate/part/syntax/data/makefile.xml new file mode 100644 index 00000000..e25376dd --- /dev/null +++ b/kate/part/syntax/data/makefile.xml @@ -0,0 +1,218 @@ + + + + + + + + + + + + include + define + else + endef + endif + ifdef + ifeq + ifndef + ifneq + override + + + call + subst + patsubst + strip + findstring + filter + filter-out + sort + word + wordlist + words + firstword + lastword + dir + notdir + suffix + basename + addsuffix + addprefix + join + wildcard + realpath + abspath + if + or + and + foreach + value + eval + origin + flavor + shell + error + warning + info + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/mako.xml b/kate/part/syntax/data/mako.xml new file mode 100644 index 00000000..09bba3c9 --- /dev/null +++ b/kate/part/syntax/data/mako.xml @@ -0,0 +1,255 @@ + + + +]> + + + + + endfor + endif + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/mandoc.xml b/kate/part/syntax/data/mandoc.xml new file mode 100644 index 00000000..a35b46a5 --- /dev/null +++ b/kate/part/syntax/data/mandoc.xml @@ -0,0 +1,89 @@ + + + + + + + + SH + SS + TH + + + + HP + IP + LP + P + PD + PP + RE + RS + TP + + + + B + BI + BR + I + IB + IR + RB + RI + SM + SB + + + + DT + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/markdown.xml b/kate/part/syntax/data/markdown.xml new file mode 100644 index 00000000..c5f8ec57 --- /dev/null +++ b/kate/part/syntax/data/markdown.xml @@ -0,0 +1,134 @@ + + + + + + + + + + + + + + + + + + + +]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/mason.xml b/kate/part/syntax/data/mason.xml new file mode 100644 index 00000000..cf7b3095 --- /dev/null +++ b/kate/part/syntax/data/mason.xml @@ -0,0 +1,236 @@ + + + + + + sub + bless + caller + cmp + print + echo + die + import + lt + le + local + defined + last + ! + || + eq + ne + use + elsif + my + foreach + wantarray + push + pop + dbmclose + dbmopen + dump + each + ge + gt + split + open + close + eval + chomp + chop + unless + undef + next + unlink + new + and + not + no + ref + redo + require + tied + tie + untie + or + xor + continue + do + else + for + goto + if + return + switch + while + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/mathematica.xml b/kate/part/syntax/data/mathematica.xml new file mode 100644 index 00000000..880ac54b --- /dev/null +++ b/kate/part/syntax/data/mathematica.xml @@ -0,0 +1,3229 @@ + + + + + + A + AbelianGroup + All + ArcSinh + Abort + AllowGroupClose + ArcTan + AbortKernels + AllowReverseGroupClose + ArcTanh + AbortProtect + AlphaChannel + Arg + Abs + AlternatingGroup + ArgMax + AbsoluteCurrentValue + AlternativeHypothesis + ArgMin + AbsoluteDashing + Alternatives + ArithmeticGeometricMean + AbsoluteFileName + AnchoredSearch + Array + AbsoluteOptions + And + ArrayComponents + AbsolutePointSize + AndersonDarlingTest + ArrayDepth + AbsoluteThickness + AngerJ + ArrayFlatten + AbsoluteTime + AngleBracket + ArrayPad + AbsoluteTiming + Animate + ArrayPlot + AccountingForm + AnimationDirection + ArrayQ + Accumulate + AnimationDisplayTime + ArrayRules + Accuracy + AnimationRate + Arrow + AccuracyGoal + AnimationRepetitions + Arrowheads + ActionMenu + AnimationRunning + AspectRatio + ActiveStyle + Animator + Assert + AcyclicGraphQ + Annotation + Assuming + AddTo + Annuity + Assumptions + AdjacencyGraph + AnnuityDue + AstronomicalData + AdjacencyMatrix + Antialiasing + Asynchronous + AdjustmentBox + Apart + AtomQ + AffineTransform + ApartSquareFree + Attributes + AiryAi + Appearance + AugmentedSymmetricPolynomial + AiryAiPrime + AppearanceElements + AutoAction + AiryAiZero + AppellF1 + AutoIndent + AiryBi + Append + AutoItalicWords + AiryBiPrime + AppendTo + Automatic + AiryBiZero + Apply + AutoMultiplicationSymbol + AlgebraicIntegerQ + ArcCos + AutorunSequencing + AlgebraicNumber + ArcCosh + AutoScroll + AlgebraicNumberDenominator + ArcCot + AutoSpacing + AlgebraicNumberNorm + ArcCoth + Axes + AlgebraicNumberPolynomial + ArcCsc + AxesEdge + AlgebraicNumberTrace + ArcCsch + AxesLabel + Algebraics + ArcSec + AxesOrigin + AlgebraicUnitQ + ArcSech + AxesStyle + Alignment + ArcSin + Axis + AlignmentPoint + ArcSinDistribution + B + BabyMonsterGroupB + BetaRegularized + BooleanGraph + Back + BetweennessCentrality + BooleanMaxterms + Background + BezierCurve + BooleanMinimize + Backslash + BezierFunction + BooleanMinterms + Backward + BilateralFilter + Booleans + Band + Binarize + BooleanTable + BarabasiAlbertGraphDistribution + BinaryFormat + BooleanVariables + BarChart + BinaryImageQ + BorderDimensions + BarChart3D + BinaryRead + BorelTannerDistribution + BarnesG + BinaryReadList + Bottom + BarOrigin + BinaryWrite + BottomHatTransform + BarSpacing + BinCounts + BoundaryStyle + BaseForm + BinLists + BoxData + Baseline + Binomial + Boxed + BaselinePosition + BinomialDistribution + BoxMatrix + BaseStyle + BinormalDistribution + BoxRatios + BatesDistribution + BiorthogonalSplineWavelet + BoxStyle + BattleLemarieWavelet + BipartiteGraphQ + BoxWhiskerChart + Because + BirnbaumSaundersDistribution + BracketingBar + BeckmannDistribution + BitAnd + BrayCurtisDistance + Beep + BitClear + BreadthFirstScan + Begin + BitGet + Break + BeginDialogPacket + BitLength + Brown + BeginPackage + BitNot + BrownForsytheTest + BellB + BitOr + BSplineBasis + BellY + BitSet + BSplineCurve + BenfordDistribution + BitShiftLeft + BSplineFunction + BeniniDistribution + BitShiftRight + BSplineSurface + BenktanderGibratDistribution + BitXor + BubbleChart + BenktanderWeibullDistribution + Black + BubbleChart3D + BernoulliB + Blank + BubbleScale + BernoulliDistribution + BlankNullSequence + BubbleSizes + BernoulliGraphDistribution + BlankSequence + ButterflyGraph + BernsteinBasis + Blend + Button + BesselI + Block + ButtonBar + BesselJ + BlockRandom + ButtonBox + BesselJZero + Blue + ButtonData + BesselK + Blur + ButtonFrame + BesselY + BodePlot + ButtonFunction + BesselYZero + Bold + ButtonMinHeight + Beta + Bookmarks + ButtonNotebook + BetaBinomialDistribution + Boole + ButtonSource + BetaDistribution + BooleanConvert + Byte + BetaNegativeBinomialDistribution + BooleanCountingFunction + ByteCount + BetaPrimeDistribution + BooleanFunction + ByteOrdering + C + C + CityData + ContinuousAction + CallPacket + Clear + ContinuousTimeModelQ + CanberraDistance + ClearAll + ContinuousWaveletData + Cancel + ClearAttributes + ContinuousWaveletTransform + CancelButton + ClearSystemCache + ContourDetect + CandlestickChart + ClebschGordan + ContourLabels + Cap + ClickPane + ContourPlot + CapForm + Clip + ContourPlot3D + CapitalDifferentialD + ClippingStyle + Contours + CarmichaelLambda + Clock + ContourShading + Cases + Close + ContourStyle + Cashflow + CloseKernels + ContraharmonicMean + Casoratian + ClosenessCentrality + Control + Catalan + Closing + ControlActive + CatalanNumber + ClusteringComponents + ControllabilityGramian + Catch + CMYKColor + ControllabilityMatrix + CauchyDistribution + Coefficient + ControllableDecomposition + CayleyGraph + CoefficientArrays + ControllableModelQ + CDF + CoefficientList + ControllerInformation + CDFWavelet + CoefficientRules + ControllerLinking + Ceiling + CoifletWavelet + ControllerManipulate + Cell + Collect + ControllerMethod + CellAutoOverwrite + Colon + ControllerPath + CellBaseline + ColorCombine + ControllerState + CellChangeTimes + ColorConvert + ControlPlacement + CellContext + ColorData + ControlsRendering + CellDingbat + ColorDataFunction + ControlType + CellDynamicExpression + ColorFunction + Convergents + CellEditDuplicate + ColorFunctionScaling + ConversionRules + CellEpilog + Colorize + Convolve + CellEvaluationDuplicate + ColorNegate + ConwayGroupCo1 + CellEvaluationFunction + ColorQuantize + ConwayGroupCo2 + CellEventActions + ColorRules + ConwayGroupCo3 + CellFrame + ColorSeparate + CoordinatesToolOptions + CellFrameMargins + ColorSetter + CoprimeQ + CellGroup + ColorSlider + Coproduct + CellGroupData + ColorSpace + CopulaDistribution + CellGrouping + Column + Copyable + CellLabel + ColumnAlignments + CopyDirectory + CellLabelAutoDelete + ColumnLines + CopyFile + CellMargins + ColumnsEqual + CopyToClipboard + CellOpen + ColumnSpacings + CornerFilter + CellPrint + ColumnWidths + CornerNeighbors + CellProlog + Commonest + Correlation + CellTags + CommonestFilter + CorrelationDistance + CellularAutomaton + CompilationOptions + Cos + CensoredDistribution + CompilationTarget + Cosh + Censoring + Compile + CoshIntegral + Center + Compiled + CosineDistance + CenterDot + CompiledFunction + CosIntegral + CentralMoment + Complement + Cot + CentralMomentGeneratingFunction + CompleteGraph + Coth + CForm + CompleteGraphQ + Count + ChampernowneNumber + CompleteKaryTree + CountRoots + ChanVeseBinarize + Complex + CountryData + Character + Complexes + Covariance + CharacterEncoding + ComplexExpand + CovarianceEstimatorFunction + CharacteristicFunction + ComplexInfinity + CramerVonMisesTest + CharacteristicPolynomial + ComplexityFunction + CreateArchive + CharacterRange + ComponentMeasurements + CreateDialog + Characters + ComposeList + CreateDirectory + ChartBaseStyle + ComposeSeries + CreateDocument + ChartElementFunction + Composition + CreateIntermediateDirectories + ChartElements + CompoundExpression + CreatePalette + ChartLabels + Compress + CreateScheduledTask + ChartLayout + Condition + CreateWindow + ChartLegends + ConditionalExpression + CriticalSection + ChartStyle + Conditioned + Cross + ChebyshevT + Cone + CrossingDetect + ChebyshevU + ConfidenceLevel + CrossMatrix + Check + Congruent + Csc + CheckAbort + Conjugate + Csch + Checkbox + ConjugateTranspose + Cubics + CheckboxBar + Conjunction + Cuboid + ChemicalData + ConnectedComponents + Cumulant + ChessboardDistance + ConnectedGraphQ + CumulantGeneratingFunction + ChiDistribution + ConoverTest + Cup + ChineseRemainder + Constant + CupCap + ChiSquareDistribution + ConstantArray + CurrentImage + ChoiceButtons + Constants + CurrentValue + ChoiceDialog + ContentPadding + CurvatureFlowFilter + CholeskyDecomposition + ContentSelectable + CurveClosed + Chop + ContentSize + Cyan + Circle + Context + CycleGraph + CircleDot + Contexts + Cycles + CircleMinus + ContextToFileName + CyclicGroup + CirclePlus + Continue + Cyclotomic + CircleTimes + ContinuedFraction + Cylinder + CirculantGraph + ContinuedFractionK + CylindricalDecomposition + D + D + DepthFirstScan + DiskMatrix + DagumDistribution + Derivative + Dispatch + DamerauLevenshteinDistance + DerivativeFilter + DispersionEstimatorFunction + Darker + DesignMatrix + DisplayAllSteps + Dashed + Det + DisplayEndPacket + Dashing + DGaussianWavelet + DisplayForm + DataDistribution + Diagonal + DisplayFunction + DataRange + DiagonalMatrix + DisplayPacket + DataReversed + Dialog + DistanceFunction + DateDifference + DialogInput + DistanceTransform + DateFunction + DialogNotebook + Distribute + DateList + DialogProlog + Distributed + DateListLogPlot + DialogReturn + DistributedContexts + DateListPlot + DialogSymbols + DistributeDefinitions + DatePattern + Diamond + DistributionChart + DatePlus + DiamondMatrix + DistributionFitTest + DateString + DiceDissimilarity + DistributionParameterAssumptions + DateTicksFormat + DictionaryLookup + DistributionParameterQ + DaubechiesWavelet + DifferenceDelta + Divide + DavisDistribution + DifferenceRoot + DivideBy + DawsonF + DifferenceRootReduce + Dividers + DeBruijnGraph + Differences + Divisible + DeclarePackage + DifferentialD + Divisors + Decompose + DifferentialRoot + DivisorSigma + Decrement + DifferentialRootReduce + DivisorSum + DedekindEta + DigitBlock + DMSList + Default + DigitCharacter + DMSString + DefaultAxesStyle + DigitCount + Do + DefaultBaseStyle + DigitQ + DockedCells + DefaultBoxStyle + DihedralGroup + DocumentNotebook + DefaultButton + Dilation + Dot + DefaultDuplicateCellStyle + Dimensions + DotDashed + DefaultDuration + DiracComb + DotEqual + DefaultElement + DiracDelta + Dotted + DefaultFaceGridsStyle + DirectedEdge + DoubleBracketingBar + DefaultFieldHintStyle + DirectedEdges + DoubleDownArrow + DefaultFrameStyle + DirectedGraph + DoubleLeftArrow + DefaultFrameTicksStyle + DirectedGraphQ + DoubleLeftRightArrow + DefaultGridLinesStyle + DirectedInfinity + DoubleLongLeftArrow + DefaultLabelStyle + Direction + DoubleLongLeftRightArrow + DefaultMenuStyle + Directive + DoubleLongRightArrow + DefaultNewCellStyle + Directory + DoubleRightArrow + DefaultOptions + DirectoryName + DoubleUpArrow + DefaultTicksStyle + DirectoryQ + DoubleUpDownArrow + Defer + DirectoryStack + DoubleVerticalBar + Definition + DirichletCharacter + DownArrow + Degree + DirichletConvolve + DownArrowBar + DegreeCentrality + DirichletDistribution + DownArrowUpArrow + DegreeGraphDistribution + DirichletL + DownLeftRightVector + Deinitialization + DirichletTransform + DownLeftTeeVector + Del + DiscreteConvolve + DownLeftVector + Deletable + DiscreteDelta + DownLeftVectorBar + Delete + DiscreteIndicator + DownRightTeeVector + DeleteBorderComponents + DiscreteLQEstimatorGains + DownRightVector + DeleteCases + DiscreteLQRegulatorGains + DownRightVectorBar + DeleteContents + DiscreteLyapunovSolve + DownTeeArrow + DeleteDirectory + DiscretePlot + DownValues + DeleteDuplicates + DiscretePlot3D + DragAndDrop + DeleteFile + DiscreteRatio + Drop + DeleteSmallComponents + DiscreteRiccatiSolve + DSolve + Delimiter + DiscreteShift + Dt + DelimiterFlashTime + DiscreteTimeModelQ + DualSystemsModel + Denominator + DiscreteUniformDistribution + DumpSave + DensityHistogram + DiscreteWaveletData + Dynamic + DensityPlot + DiscreteWaveletPacketTransform + DynamicEvaluationTimeout + DependentVariables + DiscreteWaveletTransform + DynamicModule + Deploy + Discriminant + DynamicModuleValues + Deployed + Disjunction + DynamicSetting + Depth + Disk + DynamicWrapper + E + E + End + ExactNumberQ + EdgeAdd + EndDialogPacket + ExampleData + EdgeCount + EndOfFile + Except + EdgeCoverQ + EndOfLine + ExcludedForms + EdgeDelete + EndOfString + ExcludePods + EdgeDetect + EndPackage + Exclusions + EdgeForm + EngineeringForm + ExclusionsStyle + EdgeIndex + EnterExpressionPacket + Exists + EdgeLabeling + EnterTextPacket + Exit + EdgeLabels + Entropy + Exp + EdgeList + EntropyFilter + Expand + EdgeQ + Environment + ExpandAll + EdgeRenderingFunction + Epilog + ExpandDenominator + EdgeRules + Equal + ExpandFileName + EdgeShapeFunction + EqualTilde + ExpandNumerator + EdgeStyle + Equilibrium + Expectation + EdgeWeight + Equivalent + ExpGammaDistribution + Editable + Erf + ExpIntegralE + EditDistance + Erfc + ExpIntegralEi + EffectiveInterest + Erfi + Exponent + Eigensystem + ErlangDistribution + ExponentFunction + Eigenvalues + Erosion + ExponentialDistribution + EigenvectorCentrality + ErrorBox + ExponentialFamily + Eigenvectors + EstimatedDistribution + ExponentialGeneratingFunction + Element + EstimatorGains + ExponentialMovingAverage + ElementData + EstimatorRegulator + ExponentialPowerDistribution + Eliminate + EuclideanDistance + ExponentStep + EllipticE + EulerE + Export + EllipticExp + EulerGamma + ExportString + EllipticExpPrime + EulerianGraphQ + Expression + EllipticF + EulerPhi + ExpressionCell + EllipticK + Evaluatable + ExpToTrig + EllipticLog + Evaluate + ExtendedGCD + EllipticNomeQ + EvaluatePacket + Extension + EllipticPi + EvaluationElements + ExtentElementFunction + EllipticTheta + EvaluationMonitor + ExtentMarkers + EllipticThetaPrime + EvaluationNotebook + ExtentSize + EmitSound + EvaluationObject + Extract + EmpiricalDistribution + Evaluator + ExtractArchive + EmptyGraphQ + EvenQ + ExtremeValueDistribution + Enabled + EventHandler + Encode + EventLabels + F + FaceForm + FindFit + FormBox + FaceGrids + FindGeneratingFunction + FortranForm + FaceGridsStyle + FindGeoLocation + Forward + Factor + FindGeometricTransform + ForwardBackward + Factorial + FindGraphIsomorphism + Fourier + Factorial2 + FindHamiltonianCycle + FourierCoefficient + FactorialMoment + FindIndependentEdgeSet + FourierCosCoefficient + FactorialMomentGeneratingFunction + FindIndependentVertexSet + FourierCosSeries + FactorialPower + FindInstance + FourierCosTransform + FactorInteger + FindIntegerNullVector + FourierDCT + FactorList + FindLibrary + FourierDST + FactorSquareFree + FindLinearRecurrence + FourierParameters + FactorSquareFreeList + FindList + FourierSequenceTransform + FactorTerms + FindMaximum + FourierSeries + FactorTermsList + FindMaxValue + FourierSinCoefficient + False + FindMinimum + FourierSinSeries + FeedbackType + FindMinValue + FourierSinTransform + Fibonacci + FindPermutation + FourierTransform + FieldHint + FindRoot + FourierTrigSeries + FieldHintStyle + FindSequenceFunction + FractionalPart + FieldMasked + FindShortestPath + FractionBox + FieldSize + FindShortestTour + Frame + FileBaseName + FindThreshold + FrameBox + FileByteCount + FindVertexCover + Framed + FileDate + FinishDynamic + FrameLabel + FileExistsQ + FiniteAbelianGroupCount + FrameMargins + FileExtension + FiniteGroupCount + FrameStyle + FileFormat + FiniteGroupData + FrameTicks + FileHash + First + FrameTicksStyle + FileNameDepth + FischerGroupFi22 + FRatioDistribution + FileNameDrop + FischerGroupFi23 + FrechetDistribution + FileNameJoin + FischerGroupFi24Prime + FreeQ + FileNames + FisherHypergeometricDistribution + FresnelC + FileNameSetter + FisherRatioTest + FresnelS + FileNameSplit + FisherZDistribution + FrobeniusNumber + FileNameTake + Fit + FrobeniusSolve + FilePrint + FittedModel + FromCharacterCode + FileType + FixedPoint + FromCoefficientRules + FilledCurve + FixedPointList + FromContinuedFraction + Filling + Flat + FromDigits + FillingStyle + Flatten + FromDMS + FillingTransform + FlattenAt + Front + FilterRules + FlipView + FrontEndDynamicExpression + FinancialBond + Floor + FrontEndEventActions + FinancialData + Fold + FrontEndExecute + FinancialDerivative + FoldList + FrontEndToken + FinancialIndicator + FontColor + FrontEndTokenExecute + Find + FontFamily + Full + FindArgMax + FontSize + FullDefinition + FindArgMin + FontSlant + FullForm + FindClique + FontSubstitutions + FullGraphics + FindClusters + FontTracking + FullSimplify + FindCurvePath + FontVariations + Function + FindDistributionParameters + FontWeight + FunctionExpand + FindDivisions + For + FunctionInterpolation + FindEdgeCover + ForAll + FunctionSpace + FindEulerianCycle + Format + FindFile + FormatType + G + GaborWavelet + GeoPositionXYZ + GraphStyle + GainMargins + GeoProjectionData + GraphUnion + GainPhaseMargins + Get + Gray + Gamma + Glaisher + GrayLevel + GammaDistribution + Glow + Greater + GammaRegularized + GoldenRatio + GreaterEqual + GapPenalty + GompertzMakehamDistribution + GreaterEqualLess + Gather + Goto + GreaterFullEqual + GatherBy + Gradient + GreaterGreater + GaussianFilter + GradientFilter + GreaterLess + GaussianIntegers + Graph + GreaterSlantEqual + GaussianMatrix + GraphCenter + GreaterTilde + GCD + GraphComplement + Green + GegenbauerC + GraphData + Grid + General + GraphDiameter + GridBox + GeneralizedLinearModelFit + GraphDifference + GridDefaultElement + GenerateConditions + GraphDisjointUnion + GridGraph + GeneratedCell + GraphDistance + GridLines + GeneratedParameters + GraphDistanceMatrix + GridLinesStyle + GeneratingFunction + GraphHighlight + GroebnerBasis + GenericCylindricalDecomposition + GraphHighlightStyle + GroupActionBase + GenomeData + Graphics + GroupCentralizer + GenomeLookup + Graphics3D + GroupElementPosition + GeodesicDilation + GraphicsColumn + GroupElementQ + GeodesicErosion + GraphicsComplex + GroupElements + GeoDestination + GraphicsGrid + GroupGenerators + GeodesyData + GraphicsGroup + GroupMultiplicationTable + GeoDirection + GraphicsRow + GroupOrbits + GeoDistance + GraphIntersection + GroupOrder + GeoGridPosition + GraphLayout + GroupPageBreakWithin + GeometricDistribution + GraphPeriphery + GroupSetwiseStabilizer + GeometricMean + GraphPlot + GroupStabilizer + GeometricMeanFilter + GraphPlot3D + GroupStabilizerChain + GeometricTransformation + GraphPower + Gudermannian + GeoPosition + GraphQ + GumbelDistribution + GeoPositionENU + GraphRadius + H + HaarWavelet + HermitianMatrixQ + Hue + HalfNormalDistribution + HessenbergDecomposition + HumpDownHump + HamiltonianGraphQ + HexadecimalCharacter + HumpEqual + HammingDistance + HighlightGraph + HurwitzLerchPhi + HankelH1 + HigmanSimsGroupHS + HurwitzZeta + HankelH2 + HilbertMatrix + HyperbolicDistribution + HankelMatrix + Histogram + HypercubeGraph + HaradaNortonGroupHN + Histogram3D + Hyperfactorial + HararyGraph + HistogramDistribution + Hypergeometric0F1 + HarmonicMean + HistogramList + Hypergeometric0F1Regularized + HarmonicMeanFilter + HitMissTransform + Hypergeometric1F1 + HarmonicNumber + HITSCentrality + Hypergeometric1F1Regularized + Hash + Hold + Hypergeometric2F1 + Haversine + HoldAll + Hypergeometric2F1Regularized + HazardFunction + HoldAllComplete + HypergeometricDistribution + Head + HoldComplete + HypergeometricPFQ + Heads + HoldFirst + HypergeometricPFQRegularized + HeavisideLambda + HoldForm + HypergeometricU + HeavisidePi + HoldPattern + Hyperlink + HeavisideTheta + HoldRest + Hyphenation + HeldGroupHe + HornerForm + HypothesisTestData + HermiteDecomposition + HotellingTSquareDistribution + HermiteH + HoytDistribution + I + I + ImportString + Interrupt + Identity + In + Intersection + IdentityMatrix + IncidenceGraph + Interval + If + IncidenceMatrix + IntervalIntersection + IgnoreCase + IncludeConstantBasis + IntervalMemberQ + Im + IncludePods + IntervalUnion + Image + Increment + Inverse + ImageAdd + IndependentEdgeSetQ + InverseBetaRegularized + ImageAdjust + IndependentVertexSetQ + InverseCDF + ImageAlign + Indeterminate + InverseChiSquareDistribution + ImageApply + IndexGraph + InverseContinuousWaveletTransform + ImageAspectRatio + InexactNumberQ + InverseDistanceTransform + ImageAssemble + Infinity + InverseEllipticNomeQ + ImageCapture + Infix + InverseErf + ImageChannels + Information + InverseErfc + ImageClip + Inherited + InverseFourier + ImageColorSpace + Initialization + InverseFourierCosTransform + ImageCompose + InitializationCell + InverseFourierSequenceTransform + ImageConvolve + Inner + InverseFourierSinTransform + ImageCooccurrence + Inpaint + InverseFourierTransform + ImageCorrelate + Input + InverseFunction + ImageCorrespondingPoints + InputAliases + InverseFunctions + ImageCrop + InputAssumptions + InverseGammaDistribution + ImageData + InputAutoReplacements + InverseGammaRegularized + ImageDeconvolve + InputField + InverseGaussianDistribution + ImageDifference + InputForm + InverseGudermannian + ImageDimensions + InputNamePacket + InverseHaversine + ImageEffect + InputNotebook + InverseJacobiCD + ImageFilter + InputPacket + InverseJacobiCN + ImageForestingComponents + InputStream + InverseJacobiCS + ImageForwardTransformation + InputString + InverseJacobiDC + ImageHistogram + InputStringPacket + InverseJacobiDN + ImageKeypoints + Insert + InverseJacobiDS + ImageLevels + InsertResults + InverseJacobiNC + ImageLines + Inset + InverseJacobiND + ImageMargins + Install + InverseJacobiNS + ImageMultiply + InstallService + InverseJacobiSC + ImagePad + InString + InverseJacobiSD + ImagePadding + Integer + InverseJacobiSN + ImagePartition + IntegerDigits + InverseLaplaceTransform + ImagePerspectiveTransformation + IntegerExponent + InversePermutation + ImageQ + IntegerLength + InverseRadon + ImageReflect + IntegerPart + InverseSeries + ImageResize + IntegerPartitions + InverseSurvivalFunction + ImageResolution + IntegerQ + InverseWaveletTransform + ImageRotate + Integers + InverseWeierstrassP + ImageScaled + IntegerString + InverseZTransform + ImageSize + Integrate + Invisible + ImageSizeAction + InteractiveTradingChart + IrreduciblePolynomialQ + ImageSizeMultipliers + Interleaving + IsolatingInterval + ImageSubtract + InternallyBalancedDecomposition + IsomorphicGraphQ + ImageTake + InterpolatingFunction + IsotopeData + ImageTransformation + InterpolatingPolynomial + Italic + ImageTrim + Interpolation + Item + ImageType + InterpolationOrder + ItemAspectRatio + ImageValue + Interpretation + ItemSize + Implies + InterpretationBox + ItemStyle + Import + InterquartileRange + J + JaccardDissimilarity + JacobiNS + JankoGroupJ4 + JacobiAmplitude + JacobiP + JarqueBeraALMTest + JacobiCD + JacobiSC + JohnsonDistribution + JacobiCN + JacobiSD + Join + JacobiCS + JacobiSN + Joined + JacobiDC + JacobiSymbol + JoinedCurve + JacobiDN + JacobiZeta + JoinForm + JacobiDS + JankoGroupJ1 + JordanDecomposition + JacobiNC + JankoGroupJ2 + JordanModelDecomposition + JacobiND + JankoGroupJ3 + K + KagiChart + KelvinKer + KolmogorovSmirnovTest + KalmanEstimator + KernelMixtureDistribution + KroneckerDelta + KarhunenLoeveDecomposition + KernelObject + KroneckerProduct + KaryTree + Kernels + KroneckerSymbol + KatzCentrality + Khinchin + KuiperTest + KCoreComponents + KirchhoffGraph + KumaraswamyDistribution + KDistribution + KirchhoffMatrix + Kurtosis + KelvinBei + KleinInvariantJ + KuwaharaFilter + KelvinBer + KnightTourGraph + KelvinKei + KnotData + L + Label + LibraryFunction + ListDensityPlot + Labeled + LibraryFunctionError + ListInterpolation + LabelingFunction + LibraryFunctionInformation + ListLineIntegralConvolutionPlot + LabelStyle + LibraryFunctionLoad + ListLinePlot + LaguerreL + LibraryFunctionUnload + ListLogLinearPlot + LandauDistribution + LibraryLoad + ListLogLogPlot + LanguageCategory + LibraryUnload + ListLogPlot + LaplaceDistribution + LiftingFilterData + ListPlay + LaplaceTransform + LiftingWaveletTransform + ListPlot + LaplacianFilter + LightBlue + ListPlot3D + LaplacianGaussianFilter + LightBrown + ListPointPlot3D + Large + LightCyan + ListPolarPlot + Larger + Lighter + ListStreamDensityPlot + Last + LightGray + ListStreamPlot + Latitude + LightGreen + ListSurfacePlot3D + LatitudeLongitude + Lighting + ListVectorDensityPlot + LatticeData + LightingAngle + ListVectorPlot + LatticeReduce + LightMagenta + ListVectorPlot3D + LaunchKernels + LightOrange + LocalizeVariables + LayeredGraphPlot + LightPink + LocationEquivalenceTest + LayerSizeFunction + LightPurple + LocationTest + LCM + LightRed + Locator + LeafCount + LightYellow + LocatorAutoCreate + LeastSquares + Likelihood + LocatorPane + Left + Limit + LocatorRegion + LeftArrow + LimitsPositioning + Locked + LeftArrowBar + LindleyDistribution + Log + LeftArrowRightArrow + Line + Log10 + LeftDownTeeVector + LinearFractionalTransform + Log2 + LeftDownVector + LinearModelFit + LogBarnesG + LeftDownVectorBar + LinearOffsetFunction + LogGamma + LeftRightArrow + LinearProgramming + LogGammaDistribution + LeftRightVector + LinearRecurrence + LogicalExpand + LeftTeeArrow + LinearSolve + LogIntegral + LeftTeeVector + LinearSolveFunction + LogisticDistribution + LeftTriangle + LineBreakChart + LogitModelFit + LeftTriangleBar + LineGraph + LogLikelihood + LeftTriangleEqual + LineIndent + LogLinearPlot + LeftUpDownVector + LineIndentMaxFraction + LogLogisticDistribution + LeftUpTeeVector + LineIntegralConvolutionPlot + LogLogPlot + LeftUpVector + LineIntegralConvolutionScale + LogNormalDistribution + LeftUpVectorBar + LineSpacing + LogPlot + LeftVector + LinkClose + LogSeriesDistribution + LeftVectorBar + LinkConnect + Longest + LegendAppearance + LinkCreate + LongestCommonSequence + Legended + LinkFunction + LongestCommonSubsequence + LegendreP + LinkInterrupt + Longitude + LegendreQ + LinkLaunch + LongLeftArrow + Length + LinkObject + LongLeftRightArrow + LengthWhile + LinkPatterns + LongRightArrow + LerchPhi + LinkProtocol + LoopFreeGraphQ + Less + LinkRead + LowerCaseQ + LessEqual + LinkReadyQ + LowerLeftArrow + LessEqualGreater + Links + LowerRightArrow + LessFullEqual + LinkWrite + LowerTriangularize + LessGreater + LiouvilleLambda + LQEstimatorGains + LessLess + List + LQGRegulator + LessSlantEqual + Listable + LQOutputRegulatorGains + LessTilde + ListAnimate + LQRegulatorGains + LetterCharacter + ListContourPlot + LucasL + LetterQ + ListContourPlot3D + LUDecomposition + Level + ListConvolve + LyapunovSolve + LeveneTest + ListCorrelate + LyonsGroupLy + LeviCivitaTensor + ListCurvePathPlot + LevyDistribution + ListDeconvolve + M + MachineNumberQ + MaxDetect + MinDetect + MachinePrecision + MaxExtraBandwidths + MinFilter + Magenta + MaxExtraConditions + MinimalPolynomial + Magnification + MaxFilter + MinimalStateSpaceModel + Magnify + Maximize + Minimize + Majority + MaxIterations + Minors + MakeBoxes + MaxMemoryUsed + MinStableDistribution + MakeExpression + MaxMixtureKernels + Minus + MangoldtLambda + MaxPlotPoints + MinusPlus + ManhattanDistance + MaxRecursion + MinValue + Manipulate + MaxStableDistribution + Missing + Manipulator + MaxStepFraction + MixtureDistribution + MannWhitneyTest + MaxSteps + Mod + MantissaExponent + MaxStepSize + Modal + Manual + MaxValue + ModularLambda + Map + MaxwellDistribution + Module + MapAll + McLaughlinGroupMcL + Modulus + MapAt + Mean + MoebiusMu + MapIndexed + MeanDeviation + Moment + MapThread + MeanFilter + MomentConvert + MarcumQ + MeanShift + MomentEvaluate + MardiaCombinedTest + MeanShiftFilter + MomentGeneratingFunction + MardiaKurtosisTest + Median + Monitor + MardiaSkewnessTest + MedianDeviation + MonomialList + MarginalDistribution + MedianFilter + MonsterGroupM + Masking + Medium + MorletWavelet + MatchingDissimilarity + MeijerG + MorphologicalBinarize + MatchLocalNames + MemberQ + MorphologicalBranchPoints + MatchQ + MemoryConstrained + MorphologicalComponents + MathieuC + MemoryInUse + MorphologicalEulerNumber + MathieuCharacteristicA + MenuCommandKey + MorphologicalGraph + MathieuCharacteristicB + MenuPacket + MorphologicalPerimeter + MathieuCharacteristicExponent + MenuSortingValue + MorphologicalTransform + MathieuCPrime + MenuStyle + Most + MathieuGroupM11 + MenuView + MouseAnnotation + MathieuGroupM12 + Mesh + MouseAppearance + MathieuGroupM22 + MeshFunctions + Mouseover + MathieuGroupM23 + MeshShading + MousePosition + MathieuGroupM24 + MeshStyle + MovingAverage + MathieuS + Message + MovingMedian + MathieuSPrime + MessageDialog + MoyalDistribution + MathMLForm + MessageList + MultiedgeStyle + MatrixExp + MessageName + Multinomial + MatrixForm + MessagePacket + MultinomialDistribution + MatrixPlot + Messages + MultinormalDistribution + MatrixPower + Method + MultiplicativeOrder + MatrixQ + MexicanHatWavelet + MultivariateHypergeometricDistribution + MatrixRank + MeyerWavelet + MultivariatePoissonDistribution + Max + Min + MultivariateTDistribution + N + N + NormalizedSquaredEuclideanDistance + NotPrecedesSlantEqual + NakagamiDistribution + NormalsFunction + NotPrecedesTilde + NameQ + NormFunction + NotReverseElement + Names + Not + NotRightTriangle + Nand + NotCongruent + NotRightTriangleBar + NArgMax + NotCupCap + NotRightTriangleEqual + NArgMin + NotDoubleVerticalBar + NotSquareSubset + NCache + Notebook + NotSquareSubsetEqual + NDSolve + NotebookApply + NotSquareSuperset + Nearest + NotebookAutoSave + NotSquareSupersetEqual + NearestFunction + NotebookClose + NotSubset + NeedlemanWunschSimilarity + NotebookDelete + NotSubsetEqual + Needs + NotebookDirectory + NotSucceeds + Negative + NotebookDynamicExpression + NotSucceedsEqual + NegativeBinomialDistribution + NotebookEvaluate + NotSucceedsSlantEqual + NegativeMultinomialDistribution + NotebookEventActions + NotSucceedsTilde + NeighborhoodGraph + NotebookFileName + NotSuperset + Nest + NotebookFind + NotSupersetEqual + NestedGreaterGreater + NotebookGet + NotTilde + NestedLessLess + NotebookInformation + NotTildeEqual + NestList + NotebookLocate + NotTildeFullEqual + NestWhile + NotebookObject + NotTildeTilde + NestWhileList + NotebookOpen + NotVerticalBar + NevilleThetaC + NotebookPrint + NProbability + NevilleThetaD + NotebookPut + NProduct + NevilleThetaN + NotebookRead + NRoots + NevilleThetaS + Notebooks + NSolve + NExpectation + NotebookSave + NSum + NextPrime + NotebookSelection + Null + NHoldAll + NotebookWrite + NullRecords + NHoldFirst + NotElement + NullSpace + NHoldRest + NotEqualTilde + NullWords + NicholsGridLines + NotExists + Number + NicholsPlot + NotGreater + NumberFieldClassNumber + NIntegrate + NotGreaterEqual + NumberFieldDiscriminant + NMaximize + NotGreaterFullEqual + NumberFieldFundamentalUnits + NMaxValue + NotGreaterGreater + NumberFieldIntegralBasis + NMinimize + NotGreaterLess + NumberFieldNormRepresentatives + NMinValue + NotGreaterSlantEqual + NumberFieldRegulator + NominalVariables + NotGreaterTilde + NumberFieldRootsOfUnity + NoncentralBetaDistribution + NotHumpDownHump + NumberFieldSignature + NoncentralChiSquareDistribution + NotHumpEqual + NumberForm + NoncentralFRatioDistribution + NotLeftTriangle + NumberFormat + NoncentralStudentTDistribution + NotLeftTriangleBar + NumberMarks + NonCommutativeMultiply + NotLeftTriangleEqual + NumberMultiplier + NonConstants + NotLess + NumberPadding + None + NotLessEqual + NumberPoint + NonlinearModelFit + NotLessFullEqual + NumberQ + NonNegative + NotLessGreater + NumberSeparator + NonPositive + NotLessLess + NumberSigns + Nor + NotLessSlantEqual + NumberString + NorlundB + NotLessTilde + Numerator + Norm + NotNestedGreaterGreater + NumericFunction + Normal + NotNestedLessLess + NumericQ + NormalDistribution + NotPrecedes + NyquistGridLines + Normalize + NotPrecedesEqual + NyquistPlot + O + O + Operate + OutputForm + ObservabilityGramian + OperatingSystem + OutputNamePacket + ObservabilityMatrix + Optional + OutputResponse + ObservableDecomposition + Options + OutputSizeLimit + ObservableModelQ + OptionsPattern + OutputStream + OddQ + OptionValue + OverBar + Off + Or + OverDot + Offset + Orange + Overflow + On + Order + OverHat + ONanGroupON + OrderDistribution + Overlaps + OneIdentity + OrderedQ + Overlay + Opacity + Ordering + Overscript + OpenAppend + Orderless + OverscriptBox + Opener + Orthogonalize + OverTilde + OpenerView + Out + OverVector + Opening + Outer + OwenT + OpenRead + OutputControllabilityMatrix + OwnValues + OpenWrite + OutputControllableModelQ + P + PackingMethod + PermutationList + PopupMenu + PaddedForm + PermutationListQ + PopupView + Padding + PermutationMax + PopupWindow + PadeApproximant + PermutationMin + Position + PadLeft + PermutationOrder + Positive + PadRight + PermutationPower + PositiveDefiniteMatrixQ + PageBreakAbove + PermutationProduct + PossibleZeroQ + PageBreakBelow + PermutationReplace + Postfix + PageBreakWithin + Permutations + Power + PageFooters + PermutationSupport + PowerDistribution + PageHeaders + Permute + PowerExpand + PageRankCentrality + PeronaMalikFilter + PowerMod + PageWidth + PERTDistribution + PowerModList + PairedBarChart + PetersenGraph + PowersRepresentations + PairedHistogram + PhaseMargins + PowerSymmetricPolynomial + PairedTTest + Pi + PrecedenceForm + PairedZTest + Pick + Precedes + PaletteNotebook + Piecewise + PrecedesEqual + Pane + PiecewiseExpand + PrecedesSlantEqual + Panel + PieChart + PrecedesTilde + Paneled + PieChart3D + Precision + PaneSelector + Pink + PrecisionGoal + ParabolicCylinderD + PixelConstrained + PreDecrement + ParagraphIndent + PixelValue + PreemptProtect + ParagraphSpacing + Placed + Prefix + ParallelArray + Placeholder + PreIncrement + ParallelCombine + PlaceholderReplace + Prepend + ParallelDo + Plain + PrependTo + ParallelEvaluate + Play + PreserveImageOptions + Parallelization + PlayRange + PriceGraphDistribution + Parallelize + Plot + Prime + ParallelMap + Plot3D + PrimeNu + ParallelNeeds + PlotLabel + PrimeOmega + ParallelProduct + PlotLayout + PrimePi + ParallelSubmit + PlotMarkers + PrimePowerQ + ParallelSum + PlotPoints + PrimeQ + ParallelTable + PlotRange + Primes + ParallelTry + PlotRangeClipping + PrimeZetaP + ParameterEstimator + PlotRangePadding + PrimitiveRoot + ParameterMixtureDistribution + PlotRegion + PrincipalComponents + ParametricPlot + PlotStyle + PrincipalValue + ParametricPlot3D + Plus + Print + ParentDirectory + PlusMinus + PrintingStyleEnvironment + ParetoDistribution + Pochhammer + PrintTemporary + Part + PodStates + Probability + ParticleData + PodWidth + ProbabilityDistribution + Partition + Point + ProbabilityPlot + PartitionsP + PointFigureChart + ProbabilityScalePlot + PartitionsQ + PointSize + ProbitModelFit + PascalDistribution + PoissonConsulDistribution + Product + PassEventsDown + PoissonDistribution + ProductDistribution + PassEventsUp + PolarAxes + ProductLog + Paste + PolarAxesOrigin + ProgressIndicator + PasteButton + PolarGridLines + Projection + Path + PolarPlot + Prolog + PathGraph + PolarTicks + Properties + PathGraphQ + PoleZeroMarkers + Property + Pattern + PolyaAeppliDistribution + PropertyList + PatternSequence + PolyGamma + PropertyValue + PatternTest + Polygon + Proportion + PauliMatrix + PolyhedronData + Proportional + PaulWavelet + PolyLog + Protect + Pause + PolynomialExtendedGCD + Protected + PDF + PolynomialGCD + ProteinData + PearsonChiSquareTest + PolynomialLCM + Pruning + PearsonDistribution + PolynomialMod + PseudoInverse + PerformanceGoal + PolynomialQ + Purple + PermutationCycles + PolynomialQuotient + Put + PermutationCyclesQ + PolynomialQuotientRemainder + PutAppend + PermutationGroup + PolynomialReduce + PermutationLength + PolynomialRemainder + Q + QBinomial + QRDecomposition + Quartiles + QFactorial + QuadraticIrrationalQ + QuartileSkewness + QGamma + Quantile + Quiet + QHypergeometricPFQ + QuantilePlot + Quit + QPochhammer + Quartics + Quotient + QPolyGamma + QuartileDeviation + QuotientRemainder + R + RadicalBox + RefreshRate + RightArrowBar + RadioButton + RegionBinarize + RightArrowLeftArrow + RadioButtonBar + RegionFunction + RightCosetRepresentative + Radon + RegionPlot + RightDownTeeVector + RamanujanTau + RegionPlot3D + RightDownVector + RamanujanTauL + RegularExpression + RightDownVectorBar + RamanujanTauTheta + Regularization + RightTeeArrow + RamanujanTauZ + ReleaseHold + RightTeeVector + RandomChoice + ReliefImage + RightTriangle + RandomComplex + ReliefPlot + RightTriangleBar + RandomGraph + Remove + RightTriangleEqual + RandomImage + RemoveAlphaChannel + RightUpDownVector + RandomInteger + RemoveProperty + RightUpTeeVector + RandomPermutation + RemoveScheduledTask + RightUpVector + RandomPrime + RenameDirectory + RightUpVectorBar + RandomReal + RenameFile + RightVector + RandomSample + RenkoChart + RightVectorBar + RandomVariate + Repeated + RogersTanimotoDissimilarity + Range + RepeatedNull + Root + RangeFilter + Replace + RootApproximant + RankedMax + ReplaceAll + RootIntervals + RankedMin + ReplaceList + RootLocusPlot + Raster + ReplacePart + RootMeanSquare + Rasterize + ReplaceRepeated + RootOfUnityQ + RasterSize + Resampling + RootReduce + Rational + Rescale + Roots + Rationalize + RescalingTransform + RootSum + Rationals + ResetDirectory + Rotate + Ratios + ResetScheduledTask + RotateLabel + RawBoxes + Residue + RotateLeft + RawData + Resolve + RotateRight + RayleighDistribution + Rest + RotationAction + Re + Resultant + RotationMatrix + Read + ResumePacket + RotationTransform + ReadList + Return + Round + ReadProtected + ReturnExpressionPacket + RoundingRadius + Real + ReturnPacket + Row + RealBlockDiagonalForm + ReturnTextPacket + RowAlignments + RealDigits + Reverse + RowBox + RealExponent + ReverseBiorthogonalSplineWavelet + RowLines + Reals + ReverseElement + RowMinHeight + Reap + ReverseEquilibrium + RowReduce + Record + ReverseGraph + RowsEqual + RecordLists + ReverseUpEquilibrium + RowSpacings + RecordSeparators + RevolutionAxis + RSolve + Rectangle + RevolutionPlot3D + RudvalisGroupRu + RectangleChart + RGBColor + Rule + RectangleChart3D + RiccatiSolve + RuleDelayed + RecurrenceTable + RiceDistribution + Run + Red + RidgeFilter + RunScheduledTask + Reduce + RiemannR + RunThrough + ReferenceLineStyle + RiemannSiegelTheta + RuntimeAttributes + Refine + RiemannSiegelZ + RuntimeOptions + ReflectionMatrix + Riffle + RussellRaoDissimilarity + ReflectionTransform + Right + Refresh + RightArrow + S + SameQ + Sin + StationaryWaveletPacketTransform + SameTest + Sinc + StationaryWaveletTransform + SampleDepth + SinghMaddalaDistribution + StatusArea + SampledSoundFunction + SingleLetterItalics + StepMonitor + SampledSoundList + SingularValueDecomposition + StieltjesGamma + SampleRate + SingularValueList + StirlingS1 + SamplingPeriod + SingularValuePlot + StirlingS2 + SatisfiabilityCount + Sinh + StopScheduledTask + SatisfiabilityInstances + SinhIntegral + StreamColorFunction + SatisfiableQ + SinIntegral + StreamColorFunctionScaling + Save + SixJSymbol + StreamDensityPlot + SaveDefinitions + Skeleton + StreamPlot + SawtoothWave + SkeletonTransform + StreamPoints + Scale + SkellamDistribution + StreamPosition + Scaled + Skewness + Streams + ScalingFunctions + SkewNormalDistribution + StreamScale + ScalingMatrix + Skip + StreamStyle + ScalingTransform + Slider + String + Scan + Slider2D + StringCases + ScheduledTaskObject + SlideView + StringCount + ScheduledTasks + Slot + StringDrop + SchurDecomposition + SlotSequence + StringExpression + ScientificForm + Small + StringForm + ScreenStyleEnvironment + SmallCircle + StringFormat + ScriptBaselineShifts + Smaller + StringFreeQ + ScriptMinSize + SmithWatermanSimilarity + StringInsert + ScriptSizeMultipliers + SmoothDensityHistogram + StringJoin + Scrollbars + SmoothHistogram + StringLength + ScrollPosition + SmoothHistogram3D + StringMatchQ + Sec + SmoothKernelDistribution + StringPosition + Sech + SokalSneathDissimilarity + StringQ + SechDistribution + Solve + StringReplace + SectorChart + SolveAlways + StringReplaceList + SectorChart3D + Sort + StringReplacePart + SectorOrigin + SortBy + StringReverse + SectorSpacing + Sound + StringSkeleton + SeedRandom + SoundNote + StringSplit + Select + SoundVolume + StringTake + Selectable + Sow + StringToStream + SelectComponents + Spacer + StringTrim + SelectedNotebook + Spacings + StructuredSelection + SelectionAnimate + Span + StruveH + SelectionCreateCell + SpanFromAbove + StruveL + SelectionEvaluate + SpanFromBoth + Stub + SelectionEvaluateCreateCell + SpanFromLeft + StudentTDistribution + SelectionMove + SparseArray + Style + SelfLoopStyle + Speak + StyleBox + SemialgebraicComponentInstances + Specularity + StyleData + SendMail + SpellingCorrection + StyleDefinitions + Sequence + Sphere + Subfactorial + SequenceAlignment + SphericalBesselJ + Subgraph + SequenceHold + SphericalBesselY + SubMinus + Series + SphericalHankelH1 + SubPlus + SeriesCoefficient + SphericalHankelH2 + Subresultants + SeriesData + SphericalHarmonicY + Subscript + SessionTime + SphericalPlot3D + SubscriptBox + Set + SphericalRegion + Subset + SetAccuracy + SpheroidalEigenvalue + SubsetEqual + SetAlphaChannel + SpheroidalJoiningFactor + Subsets + SetAttributes + SpheroidalPS + SubStar + SetDelayed + SpheroidalPSPrime + Subsuperscript + SetDirectory + SpheroidalQS + SubsuperscriptBox + SetFileDate + SpheroidalQSPrime + Subtract + SetOptions + SpheroidalRadialFactor + SubtractFrom + SetPrecision + SpheroidalS1 + Succeeds + SetProperty + SpheroidalS1Prime + SucceedsEqual + SetSelectedNotebook + SpheroidalS2 + SucceedsSlantEqual + SetSharedFunction + SpheroidalS2Prime + SucceedsTilde + SetSharedVariable + Splice + SuchThat + SetStreamPosition + SplineClosed + Sum + SetSystemOptions + SplineDegree + SumConvergence + Setter + SplineKnots + SuperDagger + SetterBar + SplineWeights + SuperMinus + Setting + Split + SuperPlus + Shallow + SplitBy + Superscript + ShannonWavelet + SpokenString + SuperscriptBox + ShapiroWilkTest + Sqrt + Superset + Share + SqrtBox + SupersetEqual + Sharpen + Square + SuperStar + ShearingMatrix + SquaredEuclideanDistance + SurvivalDistribution + ShearingTransform + SquareFreeQ + SurvivalFunction + Short + SquareIntersection + SuspendPacket + ShortDownArrow + SquaresR + SuzukiDistribution + Shortest + SquareSubset + SuzukiGroupSuz + ShortestPathFunction + SquareSubsetEqual + Switch + ShortLeftArrow + SquareSuperset + Symbol + ShortRightArrow + SquareSupersetEqual + SymbolName + ShortUpArrow + SquareUnion + SymletWavelet + Show + SquareWave + SymmetricGroup + ShowAutoStyles + StabilityMargins + SymmetricMatrixQ + ShowCellBracket + StabilityMarginsStyle + SymmetricPolynomial + ShowCellLabel + StableDistribution + SymmetricReduction + ShowCellTags + Stack + SynchronousInitialization + ShowCursorTracker + StackBegin + SynchronousUpdating + ShowGroupOpener + StackComplete + SyntaxInformation + ShowPageBreaks + StackInhibit + SyntaxLength + ShowSelection + StandardDeviation + SyntaxPacket + ShowSpecialCharacters + StandardDeviationFilter + SyntaxQ + ShowStringCharacters + StandardForm + SystemDialogInput + ShrinkingDelay + Standardize + SystemInformation + SiegelTheta + Star + SystemOpen + SiegelTukeyTest + StarGraph + SystemOptions + Sign + StartingStepSize + SystemsModelDelete + Signature + StartOfLine + SystemsModelDimensions + SignedRankTest + StartOfString + SystemsModelExtract + SignificanceLevel + StartScheduledTask + SystemsModelFeedbackConnect + SignPadding + StateFeedbackGains + SystemsModelLabels + SignTest + StateOutputEstimator + SystemsModelOrder + SimilarityRules + StateResponse + SystemsModelParallelConnect + SimpleGraph + StateSpaceModel + SystemsModelSeriesConnect + SimpleGraphQ + StateSpaceRealization + SystemsModelStateFeedbackConnect + Simplify + StateSpaceTransform + T + Table + Tilde + TraceOn + TableAlignments + TildeEqual + TraceOriginal + TableDepth + TildeFullEqual + TracePrint + TableDirections + TildeTilde + TraceScan + TableForm + TimeConstrained + TrackedSymbols + TableHeadings + TimeConstraint + TradingChart + TableSpacing + Times + TraditionalForm + TabView + TimesBy + TransferFunctionCancel + TagBox + TimeUsed + TransferFunctionExpand + TaggingRules + TimeValue + TransferFunctionFactor + TagSet + TimeZone + TransferFunctionModel + TagSetDelayed + Timing + TransferFunctionPoles + TagUnset + Tiny + TransferFunctionZeros + Take + TitsGroupT + TransformationFunction + TakeWhile + ToBoxes + TransformationFunctions + Tally + ToCharacterCode + TransformationMatrix + Tan + ToContinuousTimeModel + TransformedDistribution + Tanh + ToDiscreteTimeModel + Translate + TargetFunctions + ToeplitzMatrix + TranslationTransform + TautologyQ + ToExpression + Transparent + Temporary + Together + Transpose + TeXForm + Toggler + TreeForm + Text + TogglerBar + TreeGraph + TextAlignment + TokenWords + TreeGraphQ + TextCell + Tolerance + TreePlot + TextClipboardType + ToLowerCase + TrendStyle + TextData + ToNumberField + TriangleWave + TextJustification + Tooltip + TriangularDistribution + TextPacket + TooltipDelay + Trig + TextRecognize + Top + TrigExpand + Texture + TopHatTransform + TrigFactor + TextureCoordinateFunction + TopologicalSort + TrigFactorList + TextureCoordinateScaling + ToRadicals + Trigger + Therefore + ToRules + TrigReduce + Thick + ToString + TrigToExp + Thickness + Total + TrimmedMean + Thin + TotalVariationFilter + True + Thinning + TotalWidth + TrueQ + ThompsonGroupTh + ToUpperCase + TruncatedDistribution + Thread + Tr + TTest + ThreeJSymbol + Trace + Tube + Threshold + TraceAbove + TukeyLambdaDistribution + Through + TraceBackward + Tuples + Throw + TraceDepth + TuranGraph + Thumbnail + TraceDialog + TuringMachine + Ticks + TraceForward + TicksStyle + TraceOff + U + Uncompress + UniformSumDistribution + UpArrowBar + Undefined + Uninstall + UpArrowDownArrow + UnderBar + Union + Update + Underflow + UnionPlus + UpdateInterval + Underlined + Unique + UpDownArrow + Underoverscript + UnitBox + UpEquilibrium + UnderoverscriptBox + Unitize + UpperCaseQ + Underscript + UnitStep + UpperLeftArrow + UnderscriptBox + UnitTriangle + UpperRightArrow + UndirectedEdge + UnitVector + UpperTriangularize + UndirectedGraph + Unprotect + UpSet + UndirectedGraphQ + UnsameQ + UpSetDelayed + Unequal + UnsavedVariables + UpTeeArrow + Unevaluated + Unset + UpValues + UniformDistribution + UnsetShared + UsingFrontEnd + UniformGraphDistribution + UpArrow + V + ValidationLength + VertexAdd + VertexReplace + ValueQ + VertexColors + VertexShape + Variables + VertexComponent + VertexShapeFunction + Variance + VertexCoordinateRules + VertexSize + VarianceEquivalenceTest + VertexCoordinates + VertexStyle + VarianceEstimatorFunction + VertexCount + VertexTextureCoordinates + VarianceTest + VertexCoverQ + VertexWeight + VectorAngle + VertexDegree + VerticalBar + VectorColorFunction + VertexDelete + VerticalSeparator + VectorColorFunctionScaling + VertexEccentricity + VerticalSlider + VectorDensityPlot + VertexInComponent + VerticalTilde + VectorPlot + VertexInDegree + ViewAngle + VectorPlot3D + VertexIndex + ViewCenter + VectorPoints + VertexLabeling + ViewMatrix + VectorQ + VertexLabels + ViewPoint + VectorScale + VertexList + ViewRange + VectorStyle + VertexNormals + ViewVector + Vee + VertexOutComponent + ViewVertical + Verbatim + VertexOutDegree + Visible + VerifyConvergence + VertexQ + VonMisesDistribution + VerifyTestAssumptions + VertexRenderingFunction + W + WaitAll + WeierstrassHalfPeriods + WindowFloating + WaitNext + WeierstrassInvariants + WindowFrame + WakebyDistribution + WeierstrassP + WindowMargins + WalleniusHypergeometricDistribution + WeierstrassPPrime + WindowMovable + WaringYuleDistribution + WeierstrassSigma + WindowOpacity + WatershedComponents + WeierstrassZeta + WindowSize + WatsonUSquareTest + WeightedAdjacencyGraph + WindowStatusArea + WattsStrogatzGraphDistribution + WeightedAdjacencyMatrix + WindowTitle + WaveletBestBasis + WeightedGraphQ + WindowToolbars + WaveletFilterCoefficients + Weights + With + WaveletImagePlot + WheelGraph + WolframAlpha + WaveletListPlot + Which + Word + WaveletMapIndexed + While + WordBoundary + WaveletMatrixPlot + White + WordCharacter + WaveletPhi + Whitespace + WordData + WaveletPsi + WhitespaceCharacter + WordSearch + WaveletScale + WhittakerM + WordSeparators + WaveletScalogram + WhittakerW + WorkingPrecision + WaveletThreshold + WienerFilter + Write + WeatherData + WignerD + WriteString + WeberE + WignerSemicircleDistribution + Wronskian + Wedge + WindowClickSelect + WeibullDistribution + WindowElements + X + XMLElement + Xnor + XMLObject + Xor + Y + Yellow + YuleDissimilarity + Z + ZernikeR + ZetaZero + ZTransform + ZeroTest + ZipfDistribution + Zeta + ZTest + $ + $Aborted + $Inspector + $OutputSizeLimit + $AssertFunction + $InstallationDirectory + $Packages + $Assumptions + $IterationLimit + $ParentLink + $BaseDirectory + $KernelCount + $ParentProcessID + $BatchInput + $KernelID + $Path + $BatchOutput + $Language + $PathnameSeparator + $ByteOrdering + $LibraryPath + $PerformanceGoal + $Canceled + $LicenseExpirationDate + $Post + $CharacterEncoding + $LicenseID + $Pre + $CharacterEncodings + $LicenseServer + $PrePrint + $CommandLine + $Line + $PreRead + $CompilationTarget + $Linked + $ProcessID + $ConfiguredKernels + $MachineAddresses + $ProcessorCount + $Context + $MachineDomains + $ProcessorType + $ContextPath + $MachineEpsilon + $RecursionLimit + $ControlActiveSetting + $MachineID + $ReleaseNumber + $CreationDate + $MachineName + $RootDirectory + $CurrentLink + $MachinePrecision + $ScheduledTask + $DateStringFormat + $MachineType + $ScriptCommandLine + $DefaultImagingDevice + $MaxExtraPrecision + $SessionID + $Display + $MaxMachineNumber + $SharedFunctions + $DisplayFunction + $MaxNumber + $SharedVariables + $DistributedContexts + $MaxPiecewiseCases + $SoundDisplayFunction + $DynamicEvaluation + $MaxPrecision + $SyntaxHandler + $Echo + $MaxRootDegree + $System + $Epilog + $MessageGroups + $SystemCharacterEncoding + $ExportFormats + $MessageList + $SystemID + $Failed + $MessagePrePrint + $SystemWordLength + $FrontEnd + $Messages + $TemporaryDirectory + $FrontEndSession + $MinMachineNumber + $TimedOut + $GeoLocation + $MinNumber + $TimeUnit + $HistoryLength + $MinPrecision + $TimeZone + $HomeDirectory + $ModuleNumber + $Urgent + $IgnoreEOF + $NewMessage + $UserBaseDirectory + $ImagingDevices + $NewSymbol + $UserDocumentsDirectory + $ImportFormats + $Notebooks + $UserName + $InitialDirectory + $NumberMarks + $Version + $Input + $OperatingSystem + $VersionNumber + $InputFileName + $Output + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/matlab.xml b/kate/part/syntax/data/matlab.xml new file mode 100644 index 00000000..22d9eacb --- /dev/null +++ b/kate/part/syntax/data/matlab.xml @@ -0,0 +1,230 @@ + + + + + + + + + + + break + case + catch + classdef + continue + else + elseif + end + for + function + global + if + otherwise + parfor + persistent + return + spmd + switch + try + while + methods + properties + events + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/maxima.xml b/kate/part/syntax/data/maxima.xml new file mode 100644 index 00000000..416810e9 --- /dev/null +++ b/kate/part/syntax/data/maxima.xml @@ -0,0 +1,1889 @@ + + + + + + + + + + and + do + else + elseif + false + for + if + in + not + or + step + then + thru + true + while + + + + FIXME + TODO + + + + abasep + abs + absint + absolute_real_time + acos + acosh + acot + acoth + acsc + acsch + activate + addcol + add_edge + add_edges + addmatrices + addrow + add_vertex + add_vertices + adjacency_matrix + adjoin + adjoint + af + agd + airy_ai + airy_bi + airy_dai + airy_dbi + algsys + alg_type + alias + allroots + alphacharp + alphanumericp + antid + antidiff + AntiDifference + append + appendfile + apply + apply1 + apply2 + applyb1 + apropos + args + arithmetic + arithsum + array + arrayapply + arrayinfo + arraymake + ascii + asec + asech + asin + asinh + askinteger + asksign + assoc + assoc_legendre_p + assoc_legendre_q + assume + asympa + at + atan + atan2 + atanh + atensimp + atom + atvalue + augcoefmatrix + augmented_lagrangian_method + av + average_degree + backtrace + barsplot + bashindices + batch + batchload + bc2 + bdvac + belln + bern + bernpoly + bessel + bessel_i + bessel_j + bessel_k + bessel_y + beta + bezout + bffac + bfhzeta + bfloat + bfloatp + bfpsi + bfpsi0 + bfzeta + biconected_components + bimetric + binomial + bipartition + block + blockmatrixp + bode_gain + bode_phase + bothcoef + box + boxplot + break + bug_report + build_info + buildq + burn + cabs + canform + canten + cardinality + carg + cartan + cartesian_product + catch + cbffac + cdf_bernoulli + cdf_beta + cdf_binomial + cdf_cauchy + cdf_chi2 + cdf_continuous_uniform + cdf_discrete_uniform + cdf_exp + cdf_f + cdf_gamma + cdf_geometric + cdf_gumbel + cdf_hypergeometric + cdf_laplace + cdf_logistic + cdf_lognormal + cdf_negative_binomial + cdf_normal + cdf_pareto + cdf_poisson + cdf_rank_sum + cdf_rayleigh + cdf_signed_rank + cdf_student_t + cdf_weibull + cdisplay + ceiling + central_moment + cequal + cequalignore + cf + cfdisrep + cfexpand + cgeodesic + cgreaterp + cgreaterpignore + changename + changevar + chaosgame + charat + charfun + charfun2 + charlist + charp + charpoly + chebyshev_t + chebyshev_u + checkdiv + check_overlaps + cholesky + christof + chromatic_index + chromatic_number + cint + circulant_graph + clear_edge_weight + clear_rules + clear_vertex_label + clebsch_graph + clessp + clesspignore + close + closefile + cmetric + coeff + coefmatrix + cograd + col + collapse + collectterms + columnop + columnspace + columnswap + columnvector + combination + combine + comp2pui + compare + compfile + compile + compile_file + complement_graph + complete_bipartite_graph + complete_graph + components + concan + concat + conjugate + conmetderiv + connected_components + connect_vertices + cons + constantp + constituent + cont2part + content + continuous_freq + contortion + contour_plot + contract + contract_edge + contragrad + contrib_ode + convert + coord + copy + copy_graph + copylist + copymatrix + cor + cos + cosh + cot + coth + cov + cov1 + covdiff + covect + covers + create_graph + create_list + csc + csch + csetup + cspline + ctaylor + ct_coordsys + ctransform + ctranspose + cube_graph + cunlisp + cv + cycle_digraph + cycle_graph + dblint + deactivate + declare + declare_translated + declare_weight + decsym + defcon + define + define_variable + defint + defmatch + defrule + deftaylor + degree_sequence + del + delete + deleten + delta + demo + demoivre + denom + depends + derivdegree + derivlist + describe + desolve + determinant + dgauss_a + dgauss_b + dgeev + dgesvd + diag + diagmatrix + diag_matrix + diagmatrixp + diameter + diff + digitcharp + dimacs_export + dimacs_import + dimension + direct + discrete_freq + disjoin + disjointp + disolate + disp + dispcon + dispform + dispfun + dispJordan + display + disprule + dispterms + distrib + divide + divisors + divsum + dkummer_m + dkummer_u + dlange + dodecahedron_graph + dotproduct + dotsimp + dpart + draw + draw2d + draw3d + draw_graph + dscalar + echelon + edge_coloring + edges + eigens_by_jacobi + eigenvalues + eigenvectors + eighth + einstein + eivals + eivects + elapsed_real_time + elapsed_run_time + ele2comp + ele2polynome + ele2pui + elem + elementp + eliminate + elliptic_e + elliptic_ec + elliptic_eu + elliptic_f + elliptic_kc + elliptic_pi + ematrix + empty_graph + emptyp + endcons + entermatrix + entertensor + entier + equal + equalp + equiv_classes + erf + errcatch + error + errormsg + euler + ev + eval_string + evenp + every + evolution + evolution2d + evundiff + example + exp + expand + expandwrt + expandwrt_factored + explose + exponentialize + express + expt + exsec + extdiff + extract_linear_equations + extremal_subset + ezgcd + f90 + facsum + factcomb + factor + factorfacsum + factorial + factorout + factorsum + facts + fast_central_elements + fast_linsolve + fasttimes + featurep + fft + fib + fibtophi + fifth + filename_merge + file_search + file_type + fillarray + findde + find_root + first + fix + flatten + flength + float + floatnump + floor + flower_snark + flush + flush1deriv + flushd + flushnd + forget + fortran + fourcos + fourexpand + fourier + fourint + fourintcos + fourintsin + foursimp + foursin + fourth + fposition + frame_bracket + freeof + freshline + from_adjacency_matrix + frucht_graph + full_listify + fullmap + fullmapl + fullratsimp + fullratsubst + fullsetify + funcsolve + fundef + funmake + funp + gamma + gauss_a + gauss_b + gaussprob + gcd + gcdex + gcdivide + gcfac + gcfactor + gd + genfact + gen_laguerre + genmatrix + geometric + geometric_mean + geosum + get + get_edge_weight + get_lu_factors + get_pixel + get_vertex_label + gfactor + gfactorsum + ggf + girth + global_variances + gnuplot_close + gnuplot_replot + gnuplot_reset + gnuplot_restart + gnuplot_start + go + Gosper + GosperSum + gradef + gramschmidt + graph6_decode + graph6_encode + graph6_export + graph6_import + graph_center + graph_charpoly + graph_eigenvalues + graph_order + graph_periphery + graph_product + graph_size + graph_union + grid_graph + grind + grobner_basis + grotzch_graph + hamilton_cycle + hamilton_path + hankel + harmonic + harmonic_mean + hav + heawood_graph + hermite + hessian + hilbert_matrix + hipow + histogram + hodge + horner + ic1 + ic2 + ic_convert + ichr1 + ichr2 + icosahedron_graph + icurvature + ident + identfor + identity + idiff + idim + idummy + ieqn + ifactors + iframes + ifs + ift + igeodesic_coords + ilt + imagpart + imetric + implicit_derivative + implicit_plot + indexed_tensor + indices + induced_subgraph + inferencep + inference_result + infix + init_atensor + init_ctensor + in_neighbors + innerproduct + inpart + inprod + inrt + integerp + integer_partitions + integrate + intersect + intersection + intervalp + intopois + intosum + invariant1 + invariant2 + inverse_jacobi_cd + inverse_jacobi_cn + inverse_jacobi_cs + inverse_jacobi_dc + inverse_jacobi_dn + inverse_jacobi_ds + inverse_jacobi_nc + inverse_jacobi_nd + inverse_jacobi_ns + inverse_jacobi_sc + inverse_jacobi_sd + inverse_jacobi_sn + invert + invert_by_lu + inv_mod + is + is_biconnected + is_bipartite + is_connected + is_digraph + is_edge_in_graph + is_graph + is_graph_or_digraph + ishow + is_isomorphic + isolate + isomorphism + is_planar + isqrt + is_sconnected + is_tree + is_vertex_in_graph + items_inference + jacobi + jacobian + jacobi_cd + jacobi_cn + jacobi_cs + jacobi_dc + jacobi_dn + jacobi_ds + jacobi_nc + jacobi_nd + jacobi_ns + jacobi_p + jacobi_sc + jacobi_sd + jacobi_sn + JF + join + jordan + julia + kdels + kdelta + kill + killcontext + kostka + kron_delta + kronecker_product + kummer_m + kummer_u + kurtosis + kurtosis_bernoulli + kurtosis_beta + kurtosis_binomial + kurtosis_chi2 + kurtosis_continuous_uniform + kurtosis_discrete_uniform + kurtosis_exp + kurtosis_f + kurtosis_gamma + kurtosis_geometric + kurtosis_gumbel + kurtosis_hypergeometric + kurtosis_laplace + kurtosis_logistic + kurtosis_lognormal + kurtosis_negative_binomial + kurtosis_normal + kurtosis_pareto + kurtosis_poisson + kurtosis_rayleigh + kurtosis_student_t + kurtosis_weibull + labels + lagrange + laguerre + lambda + laplace + laplacian_matrix + last + lbfgs + lc2kdt + lcharp + lc_l + lcm + lc_u + ldefint + ldisp + ldisplay + legendre_p + legendre_q + leinstein + length + let + letrules + letsimp + levi_civita + lfreeof + lgtreillis + lhs + li + liediff + limit + Lindstedt + linear + linearinterpol + linear_program + line_graph + linsolve + listarray + list_correlations + listify + list_nc_monomials + listoftens + listofvars + listp + lmax + lmin + load + loadfile + local + locate_matrix_entry + log + logand + logarc + logcontract + logor + logxor + lopow + lorentz_gauge + lowercasep + lpart + lratsubst + lreduce + lriemann + lsquares_estimates + lsquares_estimates_approximate + lsquares_estimates_exact + lsquares_mse + lsquares_residual_mse + lsquares_residuals + lsum + ltreillis + lu_backsub + lu_factor + macroexpand + macroexpand1 + make_array + makebox + makefact + makegamma + make_level_picture + makelist + makeOrders + make_poly_continent + make_poly_country + make_polygon + make_random_state + make_rgb_picture + makeset + make_transform + mandelbrot + map + mapatom + maplist + matchdeclare + matchfix + mat_cond + mat_fullunblocker + mat_function + mat_norm + matrix + matrixmap + matrixp + matrix_size + mattrace + mat_trace + mat_unblocker + max + max_clique + max_degree + max_flow + maxi + maximize_lp + max_independent_set + max_matching + maybe + mean + mean_bernoulli + mean_beta + mean_binomial + mean_chi2 + mean_continuous_uniform + mean_deviation + mean_discrete_uniform + mean_exp + mean_f + mean_gamma + mean_geometric + mean_gumbel + mean_hypergeometric + mean_laplace + mean_logistic + mean_lognormal + mean_negative_binomial + mean_normal + mean_pareto + mean_poisson + mean_rayleigh + mean_student_t + mean_weibull + median + median_deviation + member + metricexpandall + min + min_degree + minfactorial + mini + minimalPoly + minimize_lp + minimum_spanning_tree + minor + mnewton + mod + mode_declare + mode_identity + ModeMatrix + moebius + mon2schur + mono + monomial_dimensions + multi_elem + multinomial + multinomial_coeff + multi_orbit + multi_pui + multsym + multthru + mycielski_graph + nary + nc_degree + ncexpt + ncharpoly + negative_picture + neighbors + newcontext + newdet + new_graph + newline + newton + next_prime + niceindices + ninth + noncentral_moment + nonmetricity + nonnegintegerp + nonscalarp + nonzeroandfreeof + notequal + nounify + nptetrad + nroots + nterms + ntermst + nthroot + nullity + nullspace + num + numbered_boundaries + numberp + num_distinct_partitions + numerval + numfactor + num_partitions + nusum + odd_girth + oddp + ode2 + ode_check + odelin + op + opena + openr + openw + operatorp + opsubst + optimize + orbit + orbits + ordergreat + ordergreatp + orderless + orderlessp + orthogonal_complement + orthopoly_recur + orthopoly_weight + outermap + out_neighbors + outofpois + pade + parGosper + parse_string + part + part2cont + partfrac + partition + partition_set + partpol + path_digraph + path_graph + pdf_bernoulli + pdf_beta + pdf_binomial + pdf_cauchy + pdf_chi2 + pdf_continuous_uniform + pdf_discrete_uniform + pdf_exp + pdf_f + pdf_gamma + pdf_geometric + pdf_gumbel + pdf_hypergeometric + pdf_laplace + pdf_logistic + pdf_lognormal + pdf_negative_binomial + pdf_normal + pdf_pareto + pdf_poisson + pdf_rank_sum + pdf_rayleigh + pdf_signed_rank + pdf_student_t + pdf_weibull + pearson_skewness + permanent + permut + permutation + permutations + petersen_graph + petrov + pickapart + picture_equalp + picturep + piechart + planar_embedding + playback + plog + plot2d + plot3d + plotdf + plsquares + pochhammer + poisdiff + poisexpt + poisint + poismap + poisplus + poissimp + poissubst + poistimes + poistrim + polarform + polartorect + poly_add + poly_buchberger + poly_buchberger_criterion + poly_colon_ideal + poly_content + polydecomp + poly_depends_p + poly_elimination_ideal + poly_exact_divide + poly_expand + poly_expt + poly_gcd + poly_grobner + poly_grobner_equal + poly_grobner_member + poly_grobner_subsetp + poly_ideal_intersection + poly_ideal_polysaturation + poly_ideal_polysaturation1 + poly_ideal_saturation + poly_ideal_saturation1 + poly_lcm + poly_minimization + polymod + poly_multiply + polynome2ele + polynomialp + poly_normal_form + poly_normalize + poly_normalize_list + poly_polysaturation_extension + poly_primitive_part + poly_pseudo_divide + poly_reduced_grobner + poly_reduction + poly_saturation_extension + poly_s_polynomial + poly_subtract + polytocompanion + potential + power_mod + powers + powerseries + powerset + prev_prime + primep + print + printf + print_graph + printpois + printprops + prodrac + product + properties + propvars + psi + ptriangularize + pui + pui2comp + pui2ele + pui2polynome + pui_direct + puireduc + put + qput + qrange + quad_qag + quad_qagi + quad_qags + quad_qawc + quad_qawf + quad_qawo + quad_qaws + quantile + quantile_bernoulli + quantile_beta + quantile_binomial + quantile_cauchy + quantile_chi2 + quantile_continuous_uniform + quantile_discrete_uniform + quantile_exp + quantile_f + quantile_gamma + quantile_geometric + quantile_gumbel + quantile_hypergeometric + quantile_laplace + quantile_logistic + quantile_lognormal + quantile_negative_binomial + quantile_normal + quantile_pareto + quantile_poisson + quantile_rayleigh + quantile_student_t + quantile_weibull + quartile_skewness + quit + qunit + quotient + radcan + radius + random + random_bernoulli + random_beta + random_binomial + random_cauchy + random_chi2 + random_continuous_uniform + random_digraph + random_discrete_uniform + random_exp + random_f + random_gamma + random_geometric + random_graph + random_graph1 + random_gumbel + random_hypergeometric + random_laplace + random_logistic + random_lognormal + random_negative_binomial + random_network + random_normal + random_pareto + random_permutation + random_poisson + random_rayleigh + random_regular_graph + random_student_t + random_tournament + random_tree + random_weibull + range + rank + rat + ratcoef + ratdenom + ratdiff + ratdisrep + ratexpand + rational + rationalize + ratnumer + ratnump + ratp + ratsimp + ratsubst + ratvars + ratweight + read + read_hashed_array + readline + read_lisp_array + read_list + read_matrix + read_maxima_array + read_nested_list + readonly + read_xpm + realpart + realroots + rearray + rectform + recttopolar + rediff + reduce_consts + reduce_order + region_boundaries + rem + remainder + remarray + rembox + remcomps + remcon + remcoord + remfun + remfunction + remlet + remove + remove_edge + remove_vertex + rempart + remrule + remsym + remvalue + rename + reset + residue + resolvante + resolvante_alternee1 + resolvante_bipartite + resolvante_diedrale + resolvante_klein + resolvante_klein3 + resolvante_produit_sym + resolvante_unitaire + resolvante_vierer + rest + resultant + return + reveal + reverse + revert + revert2 + rgb2level + rhs + ricci + riemann + rinvariant + risch + rk + rncombine + romberg + room + rootscontract + row + rowop + rowswap + rreduce + run_testsuite + save + scalarp + scaled_bessel_i + scaled_bessel_i0 + scaled_bessel_i1 + scalefactors + scanmap + scatterplot + schur2comp + sconcat + scopy + scsimp + scurvature + sdowncase + sec + sech + second + sequal + sequalignore + setdifference + set_edge_weight + setelmx + setequalp + setify + setp + set_partitions + set_plot_option + set_random_state + setunits + setup_autoload + set_up_dot_simplifications + set_vertex_label + seventh + sexplode + sf + shortest_path + show + showcomps + showratvars + sign + signum + similaritytransform + simple_linear_regression + simplify_sum + simplode + simpmetderiv + simtran + sin + sinh + sinsert + sinvertcase + sixth + skewness + skewness_bernoulli + skewness_beta + skewness_binomial + skewness_chi2 + skewness_continuous_uniform + skewness_discrete_uniform + skewness_exp + skewness_f + skewness_gamma + skewness_geometric + skewness_gumbel + skewness_hypergeometric + skewness_laplace + skewness_logistic + skewness_lognormal + skewness_negative_binomial + skewness_normal + skewness_pareto + skewness_poisson + skewness_rayleigh + skewness_student_t + skewness_weibull + slength + smake + smismatch + solve + solve_rec + solve_rec_rat + some + somrac + sort + sparse6_decode + sparse6_encode + sparse6_export + sparse6_import + specint + spherical_bessel_j + spherical_bessel_y + spherical_hankel1 + spherical_hankel2 + spherical_harmonic + splice + split + sposition + sprint + sqfr + sqrt + sqrtdenest + sremove + sremovefirst + sreverse + ssearch + ssort + sstatus + ssubst + ssubstfirst + staircase + status + std + std1 + std_bernoulli + std_beta + std_binomial + std_chi2 + std_continuous_uniform + std_discrete_uniform + std_exp + std_f + std_gamma + std_geometric + std_gumbel + std_hypergeometric + std_laplace + std_logistic + std_lognormal + std_negative_binomial + std_normal + std_pareto + std_poisson + std_rayleigh + std_student_t + std_weibull + stirling + stirling1 + stirling2 + strim + striml + strimr + string + stringout + stringp + strong_components + sublis + sublist + sublist_indices + submatrix + subsample + subset + subsetp + subst + substinpart + substpart + substring + subvar + subvarp + sum + sumcontract + summand_to_rec + supcase + supcontext + symbolp + symmdifference + symmetricp + system + take_channel + take_inference + tan + tanh + taylor + taylorinfo + taylorp + taylor_simplifier + taytorat + tcl_output + tcontract + tellrat + tellsimp + tellsimpafter + tentex + tenth + test_mean + test_means_difference + test_normality + test_rank_sum + test_sign + test_signed_rank + test_variance + test_variance_ratio + tex + texput + %th + third + throw + time + timedate + timer + timer_info + tldefint + tlimit + todd_coxeter + toeplitz + tokens + to_lisp + topological_sort + totaldisrep + totalfourier + totient + tpartpol + trace + tracematrix + trace_options + translate + translate_file + transpose + tree_reduce + treillis + treinat + triangularize + trigexpand + trigrat + trigreduce + trigsimp + trunc + tr_warnings_get + ueivects + uforget + ultraspherical + underlying_graph + undiff + union + unique + uniteigenvectors + unit_step + unitvector + unknown + unorder + unsum + untellrat + untimer + untrace + uppercasep + uricci + uriemann + uvect + vandermonde_matrix + var + var1 + var_bernoulli + var_beta + var_binomial + var_chi2 + var_continuous_uniform + var_discrete_uniform + var_exp + var_f + var_gamma + var_geometric + var_gumbel + var_hypergeometric + var_laplace + var_logistic + var_lognormal + var_negative_binomial + var_normal + var_pareto + var_poisson + var_rayleigh + var_student_t + var_weibull + vectorpotential + vectorsimp + verbify + vers + vertex_coloring + vertex_degree + vertex_distance + vertex_eccentricity + vertex_in_degree + vertex_out_degree + vertices + vertices_to_cycle + vertices_to_path + weyl + wheel_graph + with_stdout + write_data + writefile + wronskian + xgraph_curves + xreduce + xthru + Zeilberger + zeroequiv + zerofor + zeromatrix + zeromatrixp + zeta + zlange + + + + _ + __ + % + %% + absboxchar + activecontexts + additive + algebraic + algepsilon + algexact + aliases + all_dotsimp_denoms + allbut + allsym + arrays + askexp + assume_pos + assume_pos_pred + assumescalar + atomgrad + backsubst + berlefact + besselexpand + bftorat + bftrunc + boxchar + breakup + cauchysum + cflength + cframe_flag + cnonmet_flag + context + contexts + cosnpiflag + ctaypov + ctaypt + ctayswitch + ctayvar + ct_coords + ctorsion_flag + ctrgsimp + current_let_rule_package + debugmode + default_let_rule_package + demoivre + dependencies + derivabbrev + derivsubst + detout + diagmetric + dim + dispflag + display2d + display_format_internal + doallmxops + domain + domxexpt + domxmxops + domxnctimes + dontfactor + doscmxops + doscmxplus + dot0nscsimp + dot0simp + dot1simp + dotassoc + dotconstrules + dotdistrib + dotexptsimp + dotident + dotscrules + draw_graph_program + %edispflag + %emode + %enumer + epsilon_lp + erfflag + error + error_size + error_syms + %e_to_numlog + evflag + evfun + expandwrt_denom + expon + exponentialize + expop + exptdispflag + exptisolate + exptsubst + facexpand + factlim + factorflag + file_output_append + file_search_demo + file_search_lisp + file_search_maxima + find_root_abs + find_root_error + find_root_rel + flipflag + float2bf + fortindent + fortspaces + fpprec + fpprintprec + functions + gammalim + gdet + genindex + gensumnum + GGFCFMAX + GGFINFINITY + globalsolve + gradefs + grind + halfangles + %iargs + ibase + icounter + idummyx + ieqnprint + iframe_bracket_form + igeowedge_flag + imetric + inchar + infeval + inflag + infolists + in_netmath + integrate_use_rootsof + integration_constant + integration_constant_counter + intfaclim + isolate_wrt_times + keepfloat + labels + letrat + let_rule_packages + lhospitallim + limsubst + linechar + linel + linenum + linsolve_params + linsolvewarn + lispdisp + listarith + listconstvars + listdummyvars + lmxchar + loadprint + logabs + logarc + logconcoeffp + logexpand + lognegint + lognumer + logsimp + m1pbranch + macroexpansion + maperror + mapprint + matrix_element_add + matrix_element_mult + matrix_element_transpose + maxapplydepth + maxapplyheight + maxima_tempdir + maxima_userdir + maxnegex + maxposex + maxpsifracdenom + maxpsifracnum + maxpsinegint + maxpsiposint + maxtayorder + method + mode_check_errorp + mode_checkp + mode_check_warnp + modulus + multiplicities + myoptions + negdistrib + negsumdispflag + newtonepsilon + newtonmaxiter + niceindicespref + nolabels + nonegative_lp + noundisp + obase + opproperties + opsubst + optimprefix + optionset + outchar + packagefile + partswitch + pfeformat + %piargs + piece + plot_options + poislim + poly_coefficient_ring + poly_elimination_order + poly_grobner_algorithm + poly_grobner_debug + poly_monomial_order + poly_primary_elimination_order + poly_return_term_list + poly_secondary_elimination_order + poly_top_reduction_only + powerdisp + prederror + primep_number_of_tests + product_use_gamma + programmode + prompt + psexpand + radexpand + radsubstflag + random_beta_algorithm + random_binomial_algorithm + random_chi2_algorithm + random_exp_algorithm + random_f_algorithm + random_gamma_algorithm + random_geometric_algorithm + random_hypergeometric_algorithm + random_negative_binomial_algorithm + random_normal_algorithm + random_poisson_algorithm + random_student_t_algorithm + ratalgdenom + ratchristof + ratdenomdivide + rateinstein + ratepsilon + ratexpand + ratfac + ratmx + ratprint + ratriemann + ratsimpexpons + ratvars + ratweights + ratweyl + ratwtlvl + realonly + refcheck + rmxchar + %rnum_list + rombergabs + rombergit + rombergmin + rombergtol + rootsconmode + rootsepsilon + savedef + savefactors + scalarmatrixp + setcheck + setcheckbreak + setval + showtime + simplify_products + simpsum + sinnpiflag + solvedecomposes + solveexplicit + solvefactors + solve_inconsistent_error + solvenullwarn + solveradcan + solvetrigwarn + sparse + sqrtdispflag + stardisp + stats_numer + stringdisp + sublis_apply_lambda + sumexpand + sumsplitfact + taylordepth + taylor_logexpand + taylor_order_coefficients + taylor_truncate_polynomials + tensorkill + testsuite_files + timer_devalue + tlimswitch + transcompile + transrun + tr_array_as_ref + tr_bound_function_applyp + tr_file_tty_messagesp + tr_float_can_branch_complex + tr_function_call_default + trigexpandplus + trigexpandtimes + triginverses + trigsign + tr_numer + tr_optimize_max_loop + tr_semicompile + tr_state_vars + tr_warn_bad_function_calls + tr_warn_fexpr + tr_warn_meval + tr_warn_mode + tr_warn_undeclared + tr_warn_undefined_variable + tr_windy + ttyoff + use_fast_arrays + values + vect_cross + verbose + zerobern + zeta%pi + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/mediawiki.xml b/kate/part/syntax/data/mediawiki.xml new file mode 100644 index 00000000..e731d96e --- /dev/null +++ b/kate/part/syntax/data/mediawiki.xml @@ -0,0 +1,623 @@ + + + + + + +]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/mel.xml b/kate/part/syntax/data/mel.xml new file mode 100644 index 00000000..448b320f --- /dev/null +++ b/kate/part/syntax/data/mel.xml @@ -0,0 +1,1381 @@ + + + + + + + + break + case + continue + default + do + else + false + for + global + if + in + proc + return + switch + true + while + + + + + about + abs + addAttr + addAttributeEditorNodeHelp + addDynamic + addNewShelfTab + addPanelCategory + addPP + addPrefixToName + advanceToNextDrivenKey + affectedNet + affects + aimConstraint + air + aliasAttr + alias + alignCtx + alignCurve + align + alignSurface + allViewFit + ambientLight + angleBetween + angle + animCurveEditor + animDisplay + animView + annotate + appendStringArray + applicationName + applyAttrPreset + applyTake + arcLenDimContext + arcLengthDimension + arclen + arrayMapper + art3dPaintCtx + artAttrCtx + artAttrPaintVertexCtx + artAttrSkinPaintCtx + artAttrTool + artBuildPaintMenu + artFluidAttrCtx + artPuttyCtx + artSelectCtx + artSetPaintCtx + artUserPaintCtx + assignCommand + assignInputDevice + assignViewportFactories + attachCurve + attachDeviceAttr + attachSurface + attrColorSliderGrp + attrCompatibility + attrControlGrp + attrEnumOptionMenuGrp + attrEnumOptionMenu + attrFieldGrp + attrFieldSliderGrp + attributeExists + attributeInfo + attributeMenu + attributeQuery + attrNavigationControlGrp + attrPresetEditWin + autoKeyframe + autoPlace + bakeClip + bakeFluidShading + bakePartialHistory + bakeResults + bakeSimulation + basenameEx + basename + batchRender + bessel + bevel + bevelPlus + bindSkin + binMembership + blank + blend2 + blendShapeEditor + blendShape + blendShapePanel + blendTwoAttr + blindDataType + boneLattice + boundary + boxDollyCtx + boxZoomCtx + bufferCurve + buildBookmarkMenu + buildKeyframeMenu + button + buttonManip + cacheFileCombine + cacheFile + cacheFileMerge + cacheFileTrack + camera + cameraView + canCreateManip + canvas + capitalizeString + cat_Animation + catch + catchQuiet + cat_Effects + cat_General + cat_Language + cat_Modeling + cat_Rendering + cat_System + cat_Windows + CBG + ceil + changeSubdivComponentDisplayLevel + changeSubdivRegion + channelBox + character + characterize + characterMap + characterOutlineEditor + chdir + checkBoxGrp + checkBox + checkDefaultRenderGlobals + choice + circle + circularFillet + clamp + clearCache + clear + clipEditorCurrentTimeCtx + clipEditor + clip + clipSchedule + clipSchedulerOutliner + clipTrimBefore + closeCurve + closeSurface + cluster + cmdFileOutput + cmdScrollFieldExecuter + cmdScrollFieldReporter + cmdShell + coarsenSubdivSelectionList + collision + colorAtPoint + colorEditor + color + colorIndex + colorIndexSliderGrp + colorSliderButtonGrp + colorSliderGrp + columnLayout + commandEcho + commandLine + commandPort + compactHairSystem + componentEditor + compositingInterop + computePolysetVolume + condition + cone + confirmDialog + connectAttr + connectControl + connectDynamic + connectionInfo + connectJoint + constrain + constrainValue + constructionHistory + container + containsMultibyte + contextInfo + control + convertFromOldLayers + convertIffToPsd + convertLightmap + convertSolidTx + convertTessellation + convertUnit + copyArray + copyFlexor + copyKey + copySkinWeights + cos + cpButton + cpCache + cpClothSet + cpCollision + cpConstraint + cpConvClothToMesh + cpForces + cpGetSolverAttr + cpPanel + cpProperty + cpRigidCollisionFilter + cpSeam + cpSetEdit + cpSetSolverAttr + cpSolver + cpSolverTypes + cpTool + cpUpdateClothUVs + createDisplayLayer + createDrawCtx + createEditor + createLayeredPsdFile + createMotionField + createNewShelf + createNode + createRenderLayer + createSubdivRegion + cross + crossProduct + ctxAbort + ctxCompletion + ctxEditMode + ctxTraverse + currentCtx + currentTimeCtx + currentTime + currentUnit + curveAddPtCtx + curveCVCtx + curveEditorCtx + curveEPCtx + curve + curveIntersect + curveMoveEPCtx + curveOnSurface + curveSketchCtx + cutKey + cycleCheck + cylinder + dagPose + date + defaultLightListCheckBox + defaultNavigation + defineDataServer + defineVirtualDevice + deformer + deg_to_rad + deleteAttr + delete + deleteShadingGroupsAndMaterials + deleteShelfTab + deleteUI + deleteUnusedBrushes + delrandstr + detachCurve + detachDeviceAttr + detachSurface + deviceEditor + devicePanel + dgdirty + dgeval + dgInfo + dgtimer + dimWhen + directionalLight + directKeyCtx + dirmap + dirname + disable + disconnectAttr + disconnectJoint + diskCache + displacementToPoly + displayAffected + displayColor + displayCull + displayLevelOfDetail + displayPref + displayRGBColor + displaySmoothness + displayStats + displayString + displaySurface + distanceDimContext + distanceDimension + doBlur + dollyCtx + dolly + dopeSheetEditor + dot + dotProduct + doubleProfileBirailSurface + dragAttrContext + draggerContext + drag + dropoffLocator + duplicateCurve + duplicate + duplicateSurface + dynamicLoad + dynCache + dynControl + dynExport + dynExpression + dynGlobals + dynPaintEditor + dynParticleCtx + dynPref + dynRelEditor + dynRelEdPanel + editAttrLimits + editDisplayLayerGlobals + editDisplayLayerMembers + editor + editorTemplate + editRenderLayerAdjustment + editRenderLayerGlobals + editRenderLayerMembers + effector + emit + emitter + enableDevice + encodeString + endString + endsWith + env + equivalent + equivalentTol + erf + error + evalDeferred + evalEcho + eval + event + exactWorldBoundingBox + exclusiveLightCheckBox + exec + executeForEachObject + exists + exp + expressionEditorListen + expression + extendCurve + extendSurface + extrude + fcheck + fclose + feof + fflush + fgetline + fgetword + fileBrowserDialog + fileDialog + fileExtension + file + fileInfo + filetest + filletCurve + filterCurve + filterExpand + filter + filterStudioImport + findAllIntersections + findAnimCurves + finder + findKeyframe + findMenuItem + findRelatedSkinCluster + firstParentOf + fitBspline + flexor + floatEq + floatFieldGrp + floatField + floatScrollBar + floatSlider2 + floatSliderButtonGrp + floatSliderGrp + floatSlider + floor + flow + fluidCacheInfo + fluidEmitter + fluidVoxelInfo + flushUndo + fmod + fontDialog + fopen + format + formLayout + fprint + frameLayout + frame_search + fread + freeFormFillet + frewind + fromNativePath + fwrite + gamma + gauss + geometryConstraint + getApplicationVersionAsFloat + getAttr + getClassification + getDefaultBrush + getenv + getFileList + getFluidAttr + getInputDeviceRange + getMayaPanelTypes + getModifiers + getPanel + getParticleAttr + getpid + getPluginResource + globalStitch + glRenderEditor + glRender + gmatch + goal + gotoBindPose + grabColor + gradientControl + gradientControlNoAttr + graphDollyCtx + graphSelectContext + graphTrackCtx + gravity + grid + gridLayout + group + groupObjectsByName + hardenPointCurve + hardware + hardwareRenderPanel + headsUpDisplay + headsUpMessage + help + helpLine + hermite + HfAddAttractorToAS + HfAssignAS + HfBuildEqualMap + HfBuildFurFiles + HfBuildFurImages + HfCancelAFR + HfConnectASToHF + HfCreateAttractor + HfDeleteAS + HfEditAS + HfPerformCreateAS + HfRemoveAttractorFromAS + HfSelectAttached + HfSelectAttractors + HfUnassignAS + hide + hilite + hitTest + hotBox + hotkeyCheck + hotkey + hsv_to_rgb + hudButton + hudSliderButton + hudSlider + hwReflectionMap + hwRender + hwRenderLoad + hyperGraph + hyperPanel + hyperShade + hypot + iconTextButton + iconTextCheckBox + iconTextRadioButton + iconTextRadioCollection + iconTextScrollList + iconTextStaticLabel + ikfkDisplayMethod + ikHandleCtx + ikHandleDisplayScale + ikHandle + ikSolver + ikSplineHandleCtx + ikSystem + ikSystemInfo + illustratorCurves + image + imfPlugins + index_all + index + index_overview + index_substring + inheritTransform + insertJointCtx + insertJoint + insertKeyCtx + insertKnotCurve + insertKnotSurface + instanceable + instance + instancer + internalVar + intersect + interToUI + intFieldGrp + intField + intScrollBar + intSliderGrp + intSlider + iprEngine + isAnimCurve + isConnected + isDirty + isolateSelect + isParentOf + isSameObject + isTrue + isValidObjectName + isValidString + isValidUiName + itemFilterAttr + itemFilter + itemFilterRender + itemFilterType + jointCluster + jointCtx + jointDisplayScale + joint + jointLattice + keyframe + keyframeOutliner + keyframeRegionCurrentTimeCtx + keyframeRegionDirectKeyCtx + keyframeRegionDollyCtx + keyframeRegionInsertKeyCtx + keyframeRegionMoveKeyCtx + keyframeRegionScaleKeyCtx + keyframeRegionSelectKeyCtx + keyframeRegionSetKeyCtx + keyframeRegionTrackCtx + keyframeStats + keyTangent + lassoContext + latticeDeformKeyCtx + lattice + launch + launchImageEditor + layerButton + layeredShaderPort + layeredTexturePort + layoutDialog + layout + lightlink + lightListEditor + lightList + lightListPanel + linearPrecision + lineIntersection + linstep + listAnimatable + listAttr + listCameras + listConnections + listDeviceAttachments + listerEditor + listHistory + listInputDeviceAxes + listInputDeviceButtons + listInputDevices + listMenuAnnotation + listNodeTypes + listPanelCategories + listRelatives + listSets + listTransforms + listUnselected + loadFluid + loadNewShelf + loadPlugin + loadPluginLanguageResources + loadPrefObjects + localizedPanelLabel + lockNode + loft + log + longNameOf + lookThru + ls + lsThroughFilter + lsType + lsUI + mag + makebot + makeIdentity + makeLive + makePaintable + makeRoll + makeSingleSurface + makeTubeOn + manipMoveContext + manipMoveLimitsCtx + manipOptions + manipRotateContext + manipRotateLimitsCtx + manipScaleContext + manipScaleLimitsCtx + marker + match + max + Mayatomr + memory + menuBarLayout + menuEditor + menu + menuItem + menuItemToShelf + menuSet + menuSetPref + messageLine + min + minimizeApp + mirrorJoint + modelCurrentTimeCtx + modelEditor + modelPanel + mouse + move + moveIKtoFK + moveKeyCtx + moveVertexAlongDirection + movIn + movOut + multiProfileBirailSurface + mute + nameCommand + nameField + namespace + namespaceInfo + nav_Animation + nav_Effects + nav_General + nav_Language + nav_Modeling + nav_Rendering + nav_System + nav_Windows + newPanelItems + newton + nodeCast + nodeIconButton + nodeOutliner + nodePreset + nodeType + noise + nonLinear + normalConstraint + normalize + nParticle + nurbsBoolean + nurbsCopyUVSet + nurbsCube + nurbsEditUV + nurbsPlane + nurbsSelect + nurbsSquare + nurbsToPolygonsPref + nurbsToPoly + nurbsToSubdiv + nurbsToSubdivPref + nurbsUVSet + nurbsViewDirectionVector + objectCenter + objectLayer + objectType + objectTypeUI + objExists + obsoleteProc + oceanNurbsPreviewPlane + offsetCurve + offsetCurveOnSurface + offsetSurface + openGLExtension + openMayaPref + optionMenuGrp + optionMenu + optionVar + orbitCtx + orbit + orientConstraint + outlinerEditor + outlinerPanel + overrideModifier + paintEffectsDisplay + pairBlend + palettePort + paneLayout + panelConfiguration + panelHistory + panel + paramDimContext + paramDimension + paramLocator + parentConstraint + parent + particleExists + particle + particleInstancer + particleRenderInfo + partition + pasteKey + pathAnimation + pause + pclose + percent + performanceOptions + pfxstrokes + pickWalk + picture + pixelMove + planarSrf + plane + playbackOptions + playblast + play + plugAttr + pluginInfo + pluginResourceUtil + plugNode + pointConstraint + pointCurveConstraint + pointLight + pointMatrixMult + pointOnCurve + pointOnSurface + pointPosition + poleVectorConstraint + polyAppendFacetCtx + polyAppend + polyAppendVertex + polyAutoProjection + polyAverageNormal + polyAverageVertex + polyBevel + polyBlendColor + polyBlindData + polyBoolOp + polyBridgeEdge + polyCacheMonitor + polyCheck + polyChipOff + polyClipboard + polyCloseBorder + polyCollapseEdge + polyCollapseFacet + polyColorBlindData + polyColorDel + polyColorPerVertex + polyColorSet + polyCompare + polyCone + polyCopyUV + polyCreaseCtx + polyCrease + polyCreateFacetCtx + polyCreateFacet + polyCube + polyCutCtx + polyCut + polyCylinder + polyCylindricalProjection + polyDelEdge + polyDelFacet + polyDelVertex + polyDuplicateAndConnect + polyDuplicateEdge + polyEditUV + polyEditUVShell + polyEvaluate + polyExtrudeEdge + polyExtrudeFacet + polyExtrudeVertex + polyFlipEdge + polyFlipUV + polyForceUV + polyGeoSampler + polyHelix + polyInfo + polyInstallAction + polyLayoutUV + polyListComponentConversion + polyMapCut + polyMapDel + polyMapSew + polyMapSewMove + polyMergeEdgeCtx + polyMergeEdge + polyMergeFacetCtx + polyMergeFacet + polyMergeUV + polyMergeVertex + polyMirrorFace + polyMoveEdge + polyMoveFacet + polyMoveFacetUV + polyMoveUV + polyMoveVertex + polyNormal + polyNormalizeUV + polyNormalPerVertex + polyOptions + polyOptUvs + polyOutput + polyPipe + polyPlanarProjection + polyPlane + polyPlatonicSolid + polyPoke + polyPrimitive + polyPrism + polyProjection + polyPyramid + polyQuad + polyQueryBlindData + polyReduce + polySelectConstraint + polySelectConstraintMonitor + polySelectCtx + polySelectEditCtx + polySelect + polySeparate + polySetToFaceNormal + polySewEdge + polyShortestPathCtx + polySlideEdge + polySmooth + polySoftEdge + polySphere + polySphericalProjection + polySplitCtx + polySplitEdge + polySplit + polySplitRing + polySplitVertex + polyStraightenUVBorder + polySubdivideEdge + polySubdivideFacet + polyTorus + polyToSubdiv + polyTransfer + polyTriangulate + polyUnite + polyUVSet + polyWedgeFace + popen + popupMenu + pose + pow + preloadRefEd + print + progressBar + progressWindow + projectCurve + projectionContext + projectionManip + projectTangent + projFileViewer + promptDialog + propModCtx + propMove + psdChannelOutliner + psdEditTextureFile + psdExport + psdTextureFile + putenv + pwd + python + querySubdiv + quit + radial + radioButtonGrp + radioButton + radioCollection + radioMenuItemCollection + rad_to_deg + rampColorPort + rand + randomizeFollicles + randstate + rangeControl + readTake + rebuildCurve + rebuildSurface + recordAttr + recordDevice + redo + referenceEdit + reference + referenceQuery + refineSubdivSelectionList + refreshAE + refresh + registerPluginResource + rehash + reloadImage + removeJoint + removeMultiInstance + removePanelCategory + renameAttr + rename + renameSelectionList + renameUI + renderer + renderGlobalsNode + render + renderInfo + renderLayerButton + renderLayerParent + renderLayerPostProcess + renderLayerUnparent + renderManip + renderPartition + renderQualityNode + renderSettings + renderThumbnailUpdate + renderWindowEditor + renderWindowSelectContext + reorderDeformers + reorder + requires + reroot + resampleFluid + resetAE + resetPfxToPolyCamera + resetTool + resolutionNode + retarget + reverseCurve + reverseSurface + revolve + rgb_to_hsv + rigidBody + rigidSolver + rollCtx + roll + rootOf + rotate + rotationInterpolation + rot + roundConstantRadius + rowColumnLayout + rowLayout + runTimeCommand + runup + sampleImage + saveAllShelves + saveAttrPreset + saveFluid + saveImage + saveInitialState + saveMenu + savePrefObjects + savePrefs + saveShelf + saveToolSettings + scaleBrushBrightness + scaleComponents + scaleConstraint + scale + scaleKeyCtx + scaleKey + sceneEditor + sceneUIReplacement + scmh + scriptCtx + scriptEditorInfo + scriptedPanel + scriptedPanelType + scriptJob + scriptNode + scriptTable + scriptToShelf + scrollField + scrollLayout + sculpt + searchPathArray + seed + selectContext + selectCurveCV + selectedNodes + select + selectionConnection + selectKeyCtx + selectKeyframeRegionCtx + selectKey + selectMode + selectPref + selectPriority + selectType + selLoadSettings + separator + setAttrEnumResource + setAttr + setAttrMapping + setAttrNiceNameResource + setConstraintRestPosition + setCustomAttrEnumResource + setCustomAttrNiceNameResource + setDefaultShadingGroup + setDrivenKeyframe + setDynamic + setEditCtx + setEditor + setFluidAttr + setFocus + setInfinity + setInputDeviceMapping + setKeyCtx + setKeyframeBlendshapeTargetWts + setKeyframe + setKeyPath + setMenuMode + setNodeNiceNameResource + setNodeTypeFlag + setParent + setParticleAttr + setPfxToPolyCamera + setPluginResource + setProject + sets + setStampDensity + setStartupMessage + setState + setToolTo + setUITemplate + setXformManip + shadingConnection + shadingGeometryRelCtx + shadingLightRelCtx + shadingNetworkCompare + shadingNode + shapeCompare + shelfButton + shelfLayout + shelfTabLayout + shellField + shortNameOf + showHelp + showHidden + show + showManipCtx + showSelectionInTitle + showShadingGroupAttrEditor + showWindow + sign + simplify + singleProfileBirailSurface + sin + sizeBytes + size + skinCluster + skinPercent + smoothCurve + smoothstep + smoothTangentSurface + snap2to2 + snapKey + snapMode + snapshot + snapTogetherCtx + soft + softModCtx + softMod + sort + soundControl + sound + source + spaceLocator + sphere + sphrand + spotLight + spotLightPreviewPort + spreadSheetEditor + spring + sqrt + squareSurface + srtContext + stackTrace + startString + startsWith + stitchAndExplodeShell + stitchSurface + stitchSurfacePoints + strcmp + stringArrayCatenate + stringArrayContains + stringArrayCount + stringArrayInsertAtIndex + stringArrayIntersector + stringArrayRemoveAtIndex + stringArrayRemoveDuplicates + stringArrayRemoveExact + stringArrayRemove + stringArrayToString + stringToStringArray + strip + stripPrefixFromName + stroke + subdAutoProjection + subdCleanTopology + subdCollapse + subdDuplicateAndConnect + subdEditUV + subdivCrease + subdivDisplaySmoothness + subdiv + subdListComponentConversion + subdMapCut + subdMapSewMove + subdMatchTopology + subdMirror + subdToBlind + subdToPoly + subdTransferUVsToCache + substituteAllString + substituteGeometry + substitute + substring + surface + surfaceSampler + surfaceShaderList + swatchDisplayPort + switchTable + SymbolsButton + SymbolsCheckBox + symmetricModelling + sysFile + system + tabLayout + tangentConstraint + tan + texLatticeDeformContext + texManipContext + texMoveContext + texMoveUVShellContext + texRotateContext + texScaleContext + texSelectContext + texSelectShortestPathCtx + texSmudgeUVContext + textCurves + textFieldButtonGrp + textFieldGrp + textField + text + textManip + textScrollList + textToShelf + textureDisplacePlane + textureHairColor + texturePlacementContext + textureWindow + texWinToolCtx + threadCount + threePointArcCtx + timeControl + timePort + timerX + toggleAxis + toggle + toggleWindowVisibility + tokenize + tokenizeList + tolerance + tolower + toNativePath + toolButton + toolCollection + toolDropped + toolHasOptions + toolPropertyWindow + torus + toupper + trace + trackCtx + track + transferAttributes + transformCompare + transformLimits + translator + trim + truncateFluidCache + truncateHairCache + trunc + tumbleCtx + tumble + turbulence + twoPointArcCtx + uiRes + uiTemplate + unassignInputDevice + undo + undoInfo + ungroup + uniform + unit + unloadPlugin + untangleUV + untitledFileName + untrim + upAxis + updateAE + userCtx + uvLink + uvSnapshot + validateShelfName + vectorize + view2dToolCtx + viewCamera + viewClipPlane + viewFit + viewHeadOn + viewLookAt + viewManip + viewPlace + viewSet + visor + volumeAxis + vortex + waitCursor + warning + webBrowser + webBrowserPrefs + whatIs + window + windowPref + wireContext + wire + workspace + wrinkleContext + wrinkle + writeTake + xbmLangPathList + xform + xpmPicker + + + int + string + float + vector + matrix + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/mergetagtext.xml b/kate/part/syntax/data/mergetagtext.xml new file mode 100644 index 00000000..83b14a31 --- /dev/null +++ b/kate/part/syntax/data/mergetagtext.xml @@ -0,0 +1,145 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/metafont.xml b/kate/part/syntax/data/metafont.xml new file mode 100644 index 00000000..6ad00fe7 --- /dev/null +++ b/kate/part/syntax/data/metafont.xml @@ -0,0 +1,1009 @@ + + + + + + + + + + + true + + false + known + unknown + odd + charexists + not + + and + or + + + + + normaldeviate + length + + ASCII + oct + hex + angle + turningnumber + totalweight + + directiontime + xpart + ypart + xxpart + xypart + yxpart + + yypart + sqrt + sind + cosd + mlog + mexp + + floor + uniformdeviate + abs + div + dotprod + max + + min + mod + ceiling + + + + + tracingtitles + + tracingequations + tracingcapsules + tracingchoices + tracingspecs + tracingpens + tracingcommands + + tracingrestores + tracingmacros + tracingedges + tracingoutput + tracingonline + tracingstats + + pausing + showstopping + fontmaking + proofing + turningcheck + warningcheck + + smoothing + autorounding + granularity + fillin + year + month + + day + time + charcode + charext + charwd + charht + + chardp + charic + chardx + chardy + designsize + hppp + + vppp + xoffset + yoffset + boundarychar + + + + + + point + of + precontrol + postcontrol + penoffset + + rotated + scaled + shifted + slanted + transformed + xscaled + + yscaled + zscaled + + + + + makepath + reverse + + subpath + curl + tension + atleast + controls + cycle + + + + + + nullpen + pencircle + makepen + + + + + nullpicture + + + + + jobname + + readstring + str + char + decimal + substring + + + + + end + dump + save + interim + + newinternal + randomseed + let + delimiters + outer + everyjob + + show + showvariable + showtoken + showdependencies + showstats + message + + errmessage + errhelp + batchmode + nonstopmode + scrollmode + errorstopmode + + addto + also + contour + doublepath + withpen + withweight + + cull + keeping + dropping + display + inwindow + openwindow + + at + from + to + shipout + special + numspecial + + + + + + boolean + numeric + pair + path + + pen + picture + string + transform + + + + + + + + + + + + + + expr + suffix + + text + primary + secondary + tertiary + + primarydef + secondarydef + + tertiarydef + + + + + + + else + elseif + + + + + + step + until + upto + exitif + + + + + charlist + endinput + expandafter + extensible + + fontdimen + headerbyte + inner + input + intersectiontimes + kern + + ligtable + quote + scantokens + skipto + + + + + + + addto_currentpicture + aspect_ratio + base_name + base_version + + blacker + + blankpicture + bot + bye + byte + capsule_def + + change_width + + clear_pen_memory + clearit + clearpen + clearxy + counterclockwise + culldraw + + cullit + currentpen + currentpen_path + currentpicture + currenttransform + currentwindow + + cutdraw + cutoff + d + decr + define_blacker_pixels + define_corrected_pixels + + define_good_x_pixels + define_good_y_pixels + define_horizontal_corrected_pixels + define_pixels + define_whole_blacker_pixels + define_whole_pixels + + define_whole_vertical_blacker_pixels + define_whole_vertical_pixels + dir + direction + directionpoint + displaying + + ditto + + + down + downto + draw + drawdot + + + eps + epsilon + + + extra_setup + erase + exitunless + + fill + filldraw + fix_units + flex + font_coding_scheme + font_extra_space + + font_identifier + font_normal_shrink + font_normal_space + font_normal_stretch + font_quad + font_setup + + font_size + font_slant + font_x_height + fullcircle + generate + gfcorners + + gobble + gobbled + grayfont + h + halfcircle + hide + + hround + identity + image_rules + incr + infinity + interact + + interpath + intersectionpoint + inverse + italcorr + join_radius + killtext + + labelfont + labels + left + lft + localfont + loggingall + + lowres + lowres_fix + mag + magstep + makebox + makegrid + + makelabel + maketicks + + + + mode + mode_def + mode_name + + mode_setup + nodisplays + notransforms + number_of_modes + numtok + o_correction + + openit + origin + pen_bot + pen_lft + pen_rt + pen_top + + penlabels + penpos + penrazor + penspeck + pensquare + penstroke + + pickup + pixels_per_inch + proof + proofoffset + proofrule + proofrulethickness + + quartercircle + range + reflectedabout + relax + right + rotatedabout + + rotatedaround + round + rt + rulepen + savepen + screenchars + + screen_rows + screen_cols + screenrule + screenstrokes + shipit + showit + + slantfont + smode + smoke + softjoin + solve + stop + + superellipse + takepower + tensepath + titlefont + tolerance + top + + tracingall + tracingnone + undraw + undrawdot + unfill + unfilldraw + + unitpixel + unitsquare + unitvector + up + upto + vround + + w + whatever + + + + + + + bluepart + + clip + color + dashed + fontsize + greenpart + infont + + linecap + linejoin + llcorner + lrcorner + miterlimit + mpxbreak + + prologues + redpart + setbounds + tracinglostchars + truecorners + ulcorner + + urcorner + withcolor + + + + + autorounding + chardx + + chardy + fillin + granularity + hppp + proofing + smoothing + + tracingedges + tracingpens + turningcheck + vppp + xoffset + yoffset + + + + + + ahangle + ahlength + background + bbox + + bboxmargin + + beveled + black + blue + buildcycle + butt + + center + cutafter + cutbefore + cuttings + dashpattern + defaultfont + + defaultpen + defaultscale + dotlabel + dotlabels + drawarrow + drawdblarrow + + drawoptions + + evenly + + + green + label + labeloffset + + mitered + red + rounded + squared + thelabel + white + + base_name + base_version + upto + downto + exitunless + relax + + gobble + gobbled + interact + loggingall + tracingall + tracingnone + + eps + epsilon + infinity + right + left + up + + down + origin + quartercircle + halfcircle + fullcircle + unitsquare + + identity + blankpicture + withdots + ditto + EOF + pensquare + + penrazor + penspeck + whatever + + round + + byte + + dir + unitvector + inverse + counterclockwise + tensepath + + + + + takepower + direction + directionpoint + intersectionpoint + softjoin + incr + + decr + reflectedabout + rotatedaround + rotatedabout + + + flex + + superellipse + interpath + magstep + currentpen + currentpen_path + currentpicture + + fill + draw + filldraw + drawdot + unfill + undraw + + unfilldraw + undrawdot + erase + cutdraw + image + pickup + + numeric_pickup + pen_lft + pen_rt + pen_top + pen_bot + savepen + + clearpen + clear_pen_memory + lft + rt + top + bot + + ulft + urt + llft + lrt + penpos + penstroke + + arrowhead + makelabel + labels + penlabel + range + numtok + + thru + clearxy + clearit + clearpen + pickup + shipit + + bye + hide + stop + solve + + blacker + capsule_def + + change_width + define_blacker_pixels + define_corrected_pixels + define_good_x_pixels + define_good_y_pixels + define_horizontal_corrected_pixels + + define_pixels + define_whole_blacker_pixels + define_whole_vertical_blacker_pixels + define_whole_vertical_pixels + + + + extra_setup + + font_coding_scheme + font_extra_space + font_identifier + font_normal_shrink + font_normal_space + font_normal_stretch + + font_quad + font_size + font_slant + font_x_height + italcorr + labelfont + + makebox + makegrid + maketicks + mode_def + mode_setup + o_correction + + proofrule + proofrulethickness + rulepen + smode + cullit + currenttransform + + gfcorners + grayfont + hround + imagerules + lowres_fix + nodisplays + + notransforms + openit + proofoffset + screenchars + screenrule + screenstrokes + + showit + slantfont + titlefont + unitpixel + vround + circmargin + + defaultdx + defaultdy + boxit + boxjoin + bpath + circleit + + drawboxed + drawboxes + drawunboxed + fixpos + fixsize + pic + + + + + + beginchar + endchar + extra_beginchar + extra_endchar + + beginlogochar + beginfig + endfig + extra_beginfig + extra_endfig + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/mips.xml b/kate/part/syntax/data/mips.xml new file mode 100644 index 00000000..c08b89df --- /dev/null +++ b/kate/part/syntax/data/mips.xml @@ -0,0 +1,359 @@ + + + + + + .align + .ascii + .asciiz + .byte + .double + .extern + .float + .globl + .half + .sdata + .set + .space + .word + + + .data + .kdata + .ktext + .text + + + abs.d + abs.s + add + add.d + add.s + addi + addiu + addu + and + andi + bc0f + bc0t + bc1f + bc1t + bc2f + bc2t + bc3f + bc3t + beq + bgez + bgezal + bgtz + blez + bltz + bltzal + bne + break + c.eq.d + c.eq.s + c.seq.s + c.seq.d + c.ueq.s + c.ueq.d + c.olt.d + c.olt.s + c.ole.d + c.ole.s + c.ult.d + c.ult.s + c.ule.d + c.ule.s + c.le.d + c.le.s + c.lt.d + c.lt.s + c.un.s + c.un.d + cvt.d.s + cvt.d.w + cvt.s.d + cvt.s.w + cvt.w.d + cvt.w.s + div.d + div.s + j + jal + jalr + jr + lb + lbu + lh + lhu + lui + lw + lwc0 + lwc1 + lwc2 + lwc3 + lwl + lwr + mfc0 + mfc1 + mfc2 + mfc3 + mfhi + mflo + mtc0 + mtc1 + mtc2 + mtc3 + mthi + mtlo + mul.d + mul.s + mult + multu + nor + or + ori + rfe + sb + sh + sw + swcl + swl + swr + sll + sllv + slt + slti + sltiu + sra + srav + srl + srlv + sub + sub.d + sub.s + subu + sw + swc0 + swc1 + swc2 + swc3 + swl + swr + syscall + xor + xori + + + abs + b + beqz + bge + bgeu + bgt + bgtu + ble + bleu + blt + bltu + bnez + div + divu + l.d + l.s + la + ld + li + li.d + li.s + mfc0.d + mfc1.d + mfc2.d + mfc3.d + mov.d + mov.s + move + mul + mulo + mulou + neg + neg.d + neg.s + negu + nop + not + rem + remu + rol + ror + s.d + s.s + sd + seq + sge + sgeu + sgt + sgtu + sle + sleu + sne + ulh + ulhu + ulw + ush + usw + + + $0 + $1 + $2 + $3 + $4 + $5 + $6 + $7 + $8 + $9 + $10 + $11 + $12 + $13 + $14 + $15 + $16 + $17 + $18 + $19 + $20 + $21 + $22 + $23 + $24 + $25 + $26 + $27 + $28 + $29 + $30 + $31 + $zero + $t0 + $t1 + $t2 + $t3 + $t4 + $t5 + $t6 + $t7 + $t8 + $t9 + + + $v0 + $v1 + $a0 + $a1 + $a2 + $a3 + $k0 + $k1 + $at + $gp + $sp + $fp + $s0 + $s1 + $s2 + $s3 + $s4 + $s5 + $s6 + $s7 + $ra + + + $f0 + $f1 + $f2 + $f3 + $f4 + $f5 + $f6 + $f7 + $f8 + $f9 + $f10 + $f11 + $f12 + $f13 + $f14 + $f15 + $f16 + $f17 + $f18 + $f19 + $f20 + $f21 + $f22 + $f23 + $f24 + $f25 + $f26 + $f27 + $f28 + $f29 + $f30 + $f31 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/modelica.xml b/kate/part/syntax/data/modelica.xml new file mode 100644 index 00000000..e816a2c4 --- /dev/null +++ b/kate/part/syntax/data/modelica.xml @@ -0,0 +1,259 @@ + + + + + + + + + + + block + class + connector + function + model + package + record + + + + + type + + + + + Boolean + enumeration + ExternalObject + Integer + Real + StateSelect + String + + + + + display + fixed + max + min + nominal + quantity + start + stateSelect + unit + value + + + + + algorithm + and + annotation + assert + break + connect + constant + constrainedby + discrete + else + elseif + elsewhen + encapsulated + end + equation + expandable + extends + external + false + final + flow + for + if + import + in + inner + input + loop + not + or + outer + output + parameter + partial + protected + public + redeclare + replaceable + return + then + true + when + while + within + + + + + + time + + abs + ceil + div + floor + integer + mod + rem + sign + sqrt + + sin + cos + tan + asin + acos + atan + atan2 + sinh + cosh + tanh + exp + log + log10 + + analysisType + cardinality + change + delay + der + direction + edge + initial + isPresent + noEvent + pre + reinit + sample + semiLinear + smooth + terminal + terminate + + ndims + size + scalar + vector + matrix + array + zeros + ones + fill + identity + diagonal + linspace + min + max + sum + product + transpose + outerProduct + symmetric + cross + skew + cat + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/modelines.xml b/kate/part/syntax/data/modelines.xml new file mode 100644 index 00000000..01ef4b6e --- /dev/null +++ b/kate/part/syntax/data/modelines.xml @@ -0,0 +1,191 @@ + + + +]> + + + diff --git a/kate/part/syntax/data/modula-2.xml b/kate/part/syntax/data/modula-2.xml new file mode 100644 index 00000000..c48fc09c --- /dev/null +++ b/kate/part/syntax/data/modula-2.xml @@ -0,0 +1,188 @@ + + + + + + ASSEMBLER + ALLOCATE + DEALLOCATE + SIZE + Write + WriteString + WriteCard + WriteLn + WriteBf + WriteInt + WriteReal + WriteLongReal + Read + ReadString + ReadCard + ReadInt + ReadReal + ReadLongReal + Open + Close + OpenInput + OpenOutput + Accessible + Erase + EOF + Done + EmptyString + Assign + Append + Length + StrEq + Copy + Concat + pos + Delete + Insert + compare + CAPS + PutBf + GetArgs + GetEnv + ResetClock + UserTime + SystemTime + GetChar + GetInt + GetCard + GetString + GetReal + GetLongReal + PutChar + PutInt + PutCard + PutString + PutReal + PutLongReal + PutLn + + + AND + ARRAY + ASM + BEGIN + CASE + CONST + DIV + DO + ELSE + ELSIF + END + FOR + IF + IMPLEMENTATION + IN + SET + INCL + EXCL + ABS + BITSET + CAP + CHR + DEC + HALT + HIGH + INC + MAX + MIN + ODD + ORD + PROC + TRUNC + VAL + MOD + NIL + NOT + OF + OR + PROCEDURE + MODULE + DEFINITION + RECORD + REPEAT + THEN + TO + TYPE + UNTIL + LOOP + VAR + WHILE + WITH + EXIT + FALSE + TRUE + BY + FROM + IMPORT + EXPORT + QUALIFIED + RETURN + NEWPROCESS + TRANSFER + IOTRANSFER + FOREIGN + + + INTEGER + CARDINAL + SHORTINT + SHORTCARD + LONGINT + LONGREAL + CHAR + BOOLEAN + POINTER + ADDRESS + ADR + REAL + File + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/monobasic.xml b/kate/part/syntax/data/monobasic.xml new file mode 100644 index 00000000..aa0b93dd --- /dev/null +++ b/kate/part/syntax/data/monobasic.xml @@ -0,0 +1,215 @@ + + + + + + + + + Option + Explicit + Strict + Imports + Inherits + As + New + Dim + Redim + Private + Friend + Public + Const + ReadOnly + WriteOnly + Default + Shared + Shadows + Protected + Overloads + Overrides + NotOverridable + NotInheritable + MustInherit + MustOverride + MyBase + MyClass + Me + Delegate + Catch + Finaly + When + Throw + To + Step + Then + Else + True + False + Nothing + Call + ByVal + ByRef + Optional + ParamArray + Return + Declare + WithEvents + Event + RaiseEvent + AddHandler + And + Or + Not + Xor + AndAlso + OrElse + Goto + On + Error + Resume + + + + Boolean + Char + String + Integer + Long + Double + Object + Exception + Date + DateTime + Int16 + Int32 + Int64 + ParamArray + TimeSpan + Byte + Decimal + IntPtr + Single + Guid + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/mup.xml b/kate/part/syntax/data/mup.xml new file mode 100644 index 00000000..890cf8c4 --- /dev/null +++ b/kate/part/syntax/data/mup.xml @@ -0,0 +1,865 @@ + + + + + + + + score + music + header + header2 + footer + footer2 + top + top2 + bottom + bottom2 + staff + voice + grids + music + headshapes + block + + + + + aboveorder + addtranspose + barstyle + beamslope + beamstyle + beloworder + betweenorder + bottommargin + brace + bracket + cancelkey + chorddist + clef + crescdist + defoct + dist + division + dyndist + endingstyle + firstpage + font + fontfamily + gridfret + gridsatend + gridscale + gridswhereused + key + label + label2 + leftmargin + lyricsalign + lyricsfont + lyricsfontfamily + lyricssize + measnum + measnumfont + measnumfontfamily + measnumsize + noteheads + numbermrpt + ontheline + packexp + packfact + pad + pageheight + pagewidth + panelsperpage + pedstyle + printmultnum + rehstyle + release + restcombine + restsymmult + rightmargin + scale + scorepad + scoresep + size + stafflines + staffpad + staffs + staffscale + staffsep + stemlen + swingunit + sylposition + tabwhitebox + time + timeunit + topmargin + transpose + units + visible + vscheme + warn + + + + + y + n + 2f + 2o + 3f + 3o + mussym + octave + dyn + othertext + chord + lyrics + ending + reh + up + down + major + minor + perfect + augmented + diminished + maj + min + per + aug + dim + pedal + 8treble + treble8 + treble + frenchviolin + soprano + mezzosoprano + alto + tenor + baritone + bass + cut + common + line + alt + pedstar + top + barred + grouped + times + boxed + circled + plain + 1n + 5n + 1drum + 5drum + drum + tab + inches + cm + whereused + + + + + define + ifdef + ifndef + + + + + @ + endif + + + + + else + include + undef + + + + + exclamdown + questiondown + sterling + yen + cent + quotedblbase + quotedblleft + `` + quotedblright + '' + guillemotleft + << + guillemotright + >> + guildsinglleft + guilsinglright + dagger + daggerdbl + grave + acute + macron + breve + dotaccent + dieresis + ring + cedilla + hungarumlaut + ogonek + caron + emdash + AE + ae + ordfeminine + ordmasculine + Lslash + L/ + lslash + l/ + Oslash + O/ + oslash + o/ + OE + oe + dotlessi + germandbls + ss + Aacute + A' + aacute + a' + Acircumflex + A^ + acircumflex + a^ + Adieresis + A: + adieresis + a: + Agrave + A` + agrave + a` + Aring + Ao + aring + ao + Atilde + A~ + atilde + a~ + Ccedilla + C, + ccedilla + c, + Eacute + E' + eacute + e' + Ecircumflex + E^ + ecircumflex + e^ + Edieresis + E: + edieresis + e: + Egrave + E` + egrave + e` + Iacute + I' + iacute + i' + Icircumflex + I^ + icircumflex + i^ + Idieresis + i: + idieresis + i: + Igrave + I` + igrave + i` + Ntilde + N~ + ntilde + n~ + Oacute + O' + oacute + o' + Ocircumflex + O^ + ocircumflex + o^ + Odieresis + O: + odieresis + o: + Ograve + O` + ograve + o` + Otilde + O~ + otilde + o~ + Scaron + Sv + scaron + sv + Uacute + U' + uacute + u' + Ucircumflex + U^ + ucircumflex + u^ + Udieresis + U: + udieresis + u: + Ugrave + U` + ugrave + u` + Ydieresis + Y: + ydieresis + y: + Zcaron + Zv + zcaron + zv + bullet + space + + + + + gclef + fclef + cclef + com + cut + flat + dblflat + sharp + dblsharp + nat + 4n + 2n + 1n + dblwhole + xnote + diamond + filldiamond + dwhdiamond + dn2n + dn4n + dn8n + dn16n + dn32n + dn64n + dn128n + dn256n + up2n + up4n + up8n + up16n + up32n + up64n + up128n + up256n + upflag + dnflag + qwhrest + dwhrest + 1rest + 2rest + 4rest + 8rest + 16rest + 32rest + 64rest + 128rest + 256rest + begped + endped + pedal + tr + mor + invmor + turn + invturn + ferm + uferm + acc_gt + acc_hat + acc_uhat + leg + dot + wedge + uwedge + sign + coda + upbow + dnbow + rr + measrpt + copyright + dim + halfdim + triangle + + smgclef + smfclef + smcclef + smcom + smcut + smflat + smdblflat + smsharp + smdblsharp + smnat + sm4n + sm2n + sm1n + smdblwhole + smxnote + smdiamond + smfilldiamond + smdwhdiamond + smdn2n + smdn4n + smdn8n + smdn16n + smdn32n + smdn64n + smdn128n + smdn256n + smup2n + smup4n + smup8n + smup16n + smup32n + smup64n + smup128n + smup256n + smupflag + smdnflag + smqwhrest + smdwhrest + sm1rest + sm2rest + sm4rest + sm8rest + sm16rest + sm32rest + sm64rest + sm128rest + sm256rest + smbegped + smendped + smpedal + smtr + smmor + sminvmor + smturn + sminvturn + smferm + smuferm + smacc_gt + smacc_hat + smacc_uhat + smleg + smdot + smwedge + smuwedge + smsign + smcoda + smupbow + smdnbow + smrr + smmeasrpt + smcopyright + smdim + smhalfdim + smtriangle + + + + + print + left + right + center + title + paragraph + postscript + + + + + avantgarde + bookman + courier + helvetica + newcentury + palatino + times + + + + + rom + bold + ital + boldital + + + + + chord + analysis + figbass + dyn + + + + + dotted + dashed + wavy + wide + medium + + line + curve + to + bulge + + octave + mussym + phrase + pedal + roll + to + + down + up + with + + midi + + + + + above + below + between + all + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/nagios.xml b/kate/part/syntax/data/nagios.xml new file mode 100644 index 00000000..b94346e7 --- /dev/null +++ b/kate/part/syntax/data/nagios.xml @@ -0,0 +1,84 @@ + + + + + + host + hostgroup + service + servicegroup + contact + contactgroup + timeperiod + command + servicedependency + serviceescalation + hostdependency + hostescalation + hostextinfo + serviceextinfo + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/nasm.xml b/kate/part/syntax/data/nasm.xml new file mode 100644 index 00000000..6cbc555c --- /dev/null +++ b/kate/part/syntax/data/nasm.xml @@ -0,0 +1,893 @@ + + + + + + + + eax + ax + ah + al + ebx + bx + bh + bl + ecx + cx + ch + cl + edx + dx + dh + dl + ebp + bp + esi + si + edi + di + + esp + sp + + cs + ds + es + fs + gs + ss + + cr0 + + cr2 + cr3 + cr4 + + dr0 + dr1 + dr2 + dr3 + dr6 + dr7 + + st + + mm0 + mm1 + mm2 + mm3 + mm4 + mm5 + mm6 + mm7 + + xmm0 + xmm1 + xmm2 + xmm3 + xmm4 + xmm5 + xmm6 + xmm7 + + + aaa + aad + aam + aas + adc + add + addpd + addps + addsd + addss + addsubpd + addsubps + and + andnpd + andnps + andpd + andps + arpl + bound + bsf + bsr + bswap + bt + btc + btr + bts + call + cbw + cwde + cwd + cdq + cdqe + cqo + clc + cld + clgi + cli + clts + clflush + cmc + cmova + cmovae + cmovb + cmovbe + cmovc + cmove + cmovg + cmovge + cmovl + cmovle + cmovna + cmovnae + cmovnb + cmovnbe + cmovnc + cmovne + cmovng + cmovnge + cmovnl + cmovnle + cmovno + cmovnp + cmovns + cmovnz + cmovo + cmovp + cmovpe + cmovpo + cmovs + cmovz + cmp + cmpeqpd + cmpeqps + cmpeqsd + cmpeqss + cmplepd + cmpleps + cmplesd + cmpless + cmpltpd + cmpltps + cmpltsd + cmpltss + cmpneqpd + cmpneqps + cmpneqsd + cmpneqss + cmpnlepd + cmpnleps + cmpnlesd + cmpnless + cmpnltpd + cmpnltps + cmpnltsd + cmpnltss + cmpordpd + cmpordps + cmpordsd + cmpordss + cmppd + cmpps + cmps + cmpsb + cmpsd + cmpss + cmpsw + cmpunordpd + cmpunordps + cmpunordsd + cmpunordss + cmpxchg + cmpxchg486 + cmpxchg8b + cmpxchg16b + comisd + comiss + cpuid + cvtdq2pd + cvtdq2ps + cvtpd2dq + cvtpd2pi + cvtpd2ps + cvtpi2pd + cvtpi2ps + cvtps2dq + cvtps2pd + cvtps2pi + cvtsd2si + cvtsd2ss + cvtsi2sd + cvtsi2ss + cvtss2sd + cvtss2si + cvttpd2dq + cvttpd2pi + cvttps2dq + cvttps2pi + cvttsd2si + cvttss2si + daa + das + dec + div + divpd + divps + divsd + divss + emms + enter + f2xm1 + fabs + fadd + faddp + fbld + fbstp + fchs + fclex + fnclex + fcmovb + fcmovbe + fcmove + fcmovnb + fcmovnbe + fcmovne + fcmovnu + fcmovu + fcom + fcomp + fcompp + fcomi + fcomip + fcos + fdecstp + fdisi + feni + fdiv + fdivr + fdivp + fdivrp + femms + ffree + ffreep + fiadd + ficom + ficomp + fidiv + fidivr + fild + fimul + fincstp + finit + fist + fistp + fisttp + fisub + fisubr + fld + fld1 + fldl2e + fldl2t + fldlg2 + fldln2 + fldcw + fldenv + fldpi + fldz + fmul + fmulp + fndisi + fneni + fninit + fnop + fnsave + fnstcw + fnstenv + fnstsw + fnwait + fpatan + fptan + fprem + fprem1 + frndint + frstor + fsave + fscale + fsetpm + fsin + fsincos + fsqrt + fst + fstp + fstcw + fstenv + fstsw + fsub + fsubr + fsubp + fsubrp + ftst + fucom + fucomp + fucompp + fucomi + fucomip + fwait + fxam + fxch + fxrstor + fxsave + fxtract + fyl2x + fyl2xp1 + haddpd + haddps + hlt + hsubpd + hsubps + ibts + idiv + imul + in + inc + ins + insb + insd + insw + int + int1 + int3 + into + invd + invlpg + invlpga + iret + iretd + iretq + iretw + ja + jae + jb + jbe + jc + je + jg + jge + jl + jle + jna + jnae + jnb + jnbe + jnc + jne + jng + jnge + jnl + jnle + jno + jnp + jns + jnz + jo + jp + jpe + jpo + js + jz + jcxz + jecxz + jrcxz + jmp + lahf + lar + lddqu + ldmxcsr + lds + les + lea + leave + lfence + lfs + lgdt + lgs + lidt + lldt + lmsw + loadall + loadall286 + lods + lodsb + lodsd + lodsq + lodsw + loop + loope + loopne + loopnz + loopz + lsl + lss + ltr + maskmovdqu + maskmovq + maxpd + maxps + maxsd + maxss + mfence + minpd + minps + minsd + minss + monitor + mov + movapd + movaps + movd + movddup + movdq2q + movdqa + movdqu + movhlps + movhpd + movhps + movlhps + movlpd + movlps + movmskpd + movmskps + movntdq + movnti + movntpd + movntps + movntq + movq + movq2dq + movs + movsb + movsd + movshdup + movsldup + movsq + movss + movsx + movsxd + movsw + movupd + movups + movzx + mul + mulpd + mulps + mulsd + mulss + mwait + neg + nop + not + or + orpd + orps + out + outs + outsb + outsw + outsd + packssdw + packsswb + packuswb + paddb + paddd + paddq + paddsb + paddsw + paddusb + paddusw + paddw + pand + pandn + pause + pavgb + pavgusb + pavgw + pcmpeqb + pcmpeqw + pcmpeqd + pcmpgtb + pcmpgtw + pcmpgtd + pdistib + pextrw + pf2id + pf2iw + pfacc + pfadd + pfcmpeq + pfcmpge + pfcmpgt + pfmax + pfmin + pfmul + pfnacc + pfpnacc + pfrcp + pfrcpit1 + pfrcpit2 + pfrsqit1 + pfrsqrt + pfsub + pfsubr + pi2fd + pi2fw + pinsrw + pmachriw + pmaddwd + pmagw + pmaxsw + pmaxub + pminsw + pminub + pmovmskb + pmulhrw + pmulhuw + pmulhw + pmullw + pmuludq + pmvgezb + pmvlzb + pmvnzb + pmvzb + pop + popa + popaw + popad + popf + popfw + popfd + popfq + por + prefetch + prefetchnta + prefetcht0 + prefetcht1 + prefetcht2 + prefetchw + psadbw + pshufd + pshufhw + pshuflw + pshufw + pslld + pslldq + psllq + psllw + psrad + psraw + psrld + psrldq + psrlq + psrlw + psubb + psubd + psubq + psubsb + psubsiw + psubsw + psubusb + psubusw + psubw + pswapd + punpckhbw + punpckhdq + punpckhqdq + punpckhwd + punpcklbw + punpckldq + punpcklqdq + punpcklwd + push + pusha + pushad + pushaw + pushf + pushfd + pushfq + pushfw + pxor + rcl + rcr + rcpps + rcpss + rdmsr + rdpmc + rdshr + rdtsc + rdtscp + ret + retf + retn + rol + ror + rsdc + rsldt + rsm + rsqrtps + rsqrtss + rsts + sahf + sal + sar + salc + sbb + scas + scasb + scasd + scasq + scasw + seta + setae + setb + setbe + setc + sete + setg + setge + setl + setle + setna + setnae + setnb + setnbe + setnc + setne + setng + setnge + setnl + setnle + setno + setnp + setns + setnz + seto + setp + setpe + setpo + sets + setz + sfence + sgdt + shl + shld + shr + shrd + shufpd + shufps + sidt + skinit + sldt + smi + smint + smintold + smsw + sqrtpd + sqrtps + sqrtsd + sqrtss + stc + std + stgi + sti + stmxcsr + stos + stosb + stosd + stosq + stosw + str + sub + subpd + subps + subsd + subss + svdc + svldt + svts + swapgs + syscall + sysenter + sysexit + sysret + test + ucomisd + ucomiss + ud0 + ud1 + ud2 + umov + unpckhpd + unpckhps + unpcklpd + unpcklps + verr + verw + vmload + vmmcall + vmrun + vmsave + wait + wbinvd + wrmsr + wrshr + xadd + xbts + xchg + xlat + xlatb + xor + xorpd + xorps + + + + times + + equ + + db + dw + dd + dq + dt + + resb + resw + resd + resq + rest + + incbin + + byte + word + dword + qword + short + ptr + + + + absolute + bits + common + extern + global + org + section + seg + segment + strict + use16 + use32 + wrt + + struc + endstruc + istruc + at + iend + align + alignb + + __SECT__ + __NASM_MAJOR__ + __NASM_MINOR__ + __NASM_SUBMINOR__ + ___NASM_PATCHLEVEL__ + __NASM_VERSION_ID__ + __NASM_VER__ + __FILE__ + __LINE__ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/nemerle.xml b/kate/part/syntax/data/nemerle.xml new file mode 100644 index 00000000..edf5c734 --- /dev/null +++ b/kate/part/syntax/data/nemerle.xml @@ -0,0 +1,208 @@ + + + + + + + abstract + def + delegate + event + extern + internal + mutable + override + public + private + protected + sealed + static + volatile + virtual + new + + + + macro + namespace + using + \[Record\] + + + + array + bool + byte + char + decimal + double + enum + float + int + list + long + object + sbyte + short + string + uint + ulong + ushort + variant + void + + + + _ + as + assert + base + catch + checked + do + else + false + finally + for + foreach + fun + get + if + ignore + implements + in + is + lock + match + null + out + params + ref + repeat + set + syntax + this + throw + true + try + typeof + unchecked + unless + when + where + while + with + + + + #define + #elif + #else + #endif + #endregion + #error + #if + #line + #region + #undef + #warning + #pragma + + + + class + interface + module + struct + type + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/nesc.xml b/kate/part/syntax/data/nesc.xml new file mode 100644 index 00000000..38306ba2 --- /dev/null +++ b/kate/part/syntax/data/nesc.xml @@ -0,0 +1,192 @@ + + + + + + break + case + continue + default + do + else + enum + extern + for + goto + if + inline + return + sizeof + struct + switch + typedef + union + while interface + module + configuration + implementation + components + as + uses + provides + includes + command + event + async + task + norace + default + __attribute__ + atomic + call + signal + post + + + + auto + char + const + double + float + int + long + register + restrict + short + signed + static + unsigned + void + volatile + _Imaginary + _Complex + _Bool + bool + uint8_t + uint16_t + uint32_t + uint64_t + result_t + SUCCESS + FAIL + TRUE + FALSE + TOSH_INTERRUPT + TOSH_SIGNAL + TOS_MsgPtr + TOS_Msg + + + FIXME + TODO + ### + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/kate/part/syntax/data/noweb.xml b/kate/part/syntax/data/noweb.xml new file mode 100644 index 00000000..1281c726 --- /dev/null +++ b/kate/part/syntax/data/noweb.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/objectivec.xml b/kate/part/syntax/data/objectivec.xml new file mode 100644 index 00000000..0e931f7f --- /dev/null +++ b/kate/part/syntax/data/objectivec.xml @@ -0,0 +1,128 @@ + + + + + + break + case + continue + default + do + else + enum + extern + for + goto + if + return + sizeof + struct + switch + typedef + union + while + @class + @defs + @encode + @end + @implementation + @interface + @private + @protected + @protocol + @public + @selector + self + super + + + auto + char + const + double + float + int + long + register + short + signed + static + unsigned + void + volatile + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/objectivecpp.xml b/kate/part/syntax/data/objectivecpp.xml new file mode 100644 index 00000000..bf038d4a --- /dev/null +++ b/kate/part/syntax/data/objectivecpp.xml @@ -0,0 +1,318 @@ + + + + + + break + case + continue + default + do + else + enum + extern + for + goto + if + return + sizeof + struct + switch + typedef + union + while + @class + @defs + @encode + @end + @implementation + @interface + @private + @protected + @protocol + @public + @selector + self + super + + asm + catch + class + const_cast + delete + dynamic_cast + explicit + export + false + friend + inline + namespace + new + operator + private + protected + public + qobject_cast + reinterpret_cast + static_cast + template + this + throw + true + try + typeid + type_info + typename + using + virtual + + and + and_eq + bad_cast + bad_typeid + bitand + bitor + compl + not + not_eq + or + or_eq + xor + xor_eq + + + K_DCOP + SLOT + SIGNAL + Q_CLASSINFO + Q_ENUMS + Q_EXPORT + Q_OBJECT + Q_OVERRIDE + Q_PROPERTY + Q_SETS + Q_SIGNALS + Q_SLOTS + Q_FOREACH + Q_DECLARE_FLAGS + Q_INIT_RESOURCE + Q_CLEANUP_RESOURCE + Q_GLOBAL_STATIC + Q_GLOBAL_STATIC_WITH_ARGS + Q_DECLARE_INTERFACE + Q_DECLARE_TYPEINFO + Q_DECLARE_SHARED + Q_DECLARE_FLAGS + Q_DECLARE_OPERATORS_FOR_FLAGS + Q_FOREVER + Q_DECLARE_PRIVATE + Q_DECLARE_PUBLIC + Q_D + Q_Q + Q_DISABLE_COPY + Q_INTERFACES + Q_FLAGS + Q_SCRIPTABLE + Q_INVOKABLE + Q_GADGET + Q_ARG + Q_RETURN_ARG + Q_ASSERT + Q_ASSERT_X + TRUE + FALSE + connect + disconnect + emit + signals + slots + foreach + forever + + + auto + char + const + double + float + int + long + register + short + signed + static + unsigned + void + volatile + + bool + mutable + uchar + uint + int8_t + int16_t + int32_t + int64_t + uint8_t + uint16_t + uint32_t + uint64_t + wchar_t + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/ocaml.xml b/kate/part/syntax/data/ocaml.xml new file mode 100644 index 00000000..f86b2fac --- /dev/null +++ b/kate/part/syntax/data/ocaml.xml @@ -0,0 +1,272 @@ + + + + + + + + +]> + + + + + + + + declare + value + where + + + and + as + assert + asr + + class + closed + constraint + + + downto + else + + exception + external + false + for + fun + function + functor + if + in + + inherit + initializer + land + lazy + let + lor + lsl + lsr + lxor + match + method + mod + + mutable + new + + of + + or + parser + private + rec + + + then + to + true + try + type + val + virtual + when + while + with + + + exn + lazy_t + format + unit + int + real + char + string + ref + array + bool + list + option + + + ? + + + end + + + sig + + + struct + + + object + + + begin + + + do + + + done + + + module + open + include + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/octave.xml b/kate/part/syntax/data/octave.xml new file mode 100644 index 00000000..9e11f9a8 --- /dev/null +++ b/kate/part/syntax/data/octave.xml @@ -0,0 +1,2219 @@ + + + + + + + + + + + + all_va_args + break + case + continue + else + elseif + end_unwind_protect + global + gplot + gsplot + otherwise + persistent + replot + return + static + until + unwind_protect + unwind_protect_cleanup + varargin + varargout + + + + argv + e + eps + false + F_DUPFD + F_GETFD + F_GETFL + filesep + F_SETFD + F_SETFL + i + I + inf + Inf + j + J + NA + nan + NaN + O_APPEND + O_ASYNC + O_CREAT + OCTAVE_HOME + OCTAVE_VERSION + O_EXCL + O_NONBLOCK + O_RDONLY + O_RDWR + O_SYNC + O_TRUNC + O_WRONLY + pi + program_invocation_name + program_name + P_tmpdir + realmax + realmin + SEEK_CUR + SEEK_END + SEEK_SET + SIG + stderr + stdin + stdout + true + ans + automatic_replot + beep_on_error + completion_append_char + crash_dumps_octave_core + current_script_file_name + debug_on_error + debug_on_interrupt + debug_on_warning + debug_symtab_lookups + DEFAULT_EXEC_PATH + DEFAULT_LOADPATH + default_save_format + echo_executing_commands + EDITOR + EXEC_PATH + FFTW_WISDOM_PROGRAM + fixed_point_format + gnuplot_binary + gnuplot_command_axes + gnuplot_command_end + gnuplot_command_plot + gnuplot_command_replot + gnuplot_command_splot + gnuplot_command_title + gnuplot_command_using + gnuplot_command_with + gnuplot_has_frames + history_file + history_size + ignore_function_time_stamp + IMAGEPATH + INFO_FILE + INFO_PROGRAM + __kluge_procbuf_delay__ + LOADPATH + MAKEINFO_PROGRAM + max_recursion_depth + octave_core_file_format + octave_core_file_limit + octave_core_file_name + output_max_field_width + output_precision + page_output_immediately + PAGER + page_screen_output + print_answer_id_name + print_empty_dimensions + print_rhs_assign_val + PS1 + PS2 + PS4 + save_header_format_string + save_precision + saving_history + sighup_dumps_octave_core + sigterm_dumps_octave_core + silent_functions + split_long_rows + string_fill_char + struct_levels_to_print + suppress_verbose_help_message + variables_can_hide_functions + warn_assign_as_truth_value + warn_divide_by_zero + warn_empty_list_elements + warn_fortran_indexing + warn_function_name_clash + warn_future_time_stamp + warn_imag_to_real + warn_matlab_incompatible + warn_missing_semicolon + warn_neg_dim_as_zero + warn_num_to_str + warn_precedence_change + warn_reload_forces_clear + warn_resize_on_range_error + warn_separator_insert + warn_single_quote_string + warn_str_to_num + warn_undefined_return_values + warn_variable_switch_label + whos_line_format + + + + casesen + cd + chdir + clear + dbclear + dbstatus + dbstop + dbtype + dbwhere + diary + echo + edit_history + __end__ + format + gset + gshow + help + history + hold + iskeyword + isvarname + load + ls + mark_as_command + mislocked + mlock + more + munlock + run_history + save + set + show + type + unmark_command + which + who + whos + + + + abs + acos + acosh + all + angle + any + append + arg + argnames + asin + asinh + assignin + atan + atan2 + atanh + atexit + bitand + bitmax + bitor + bitshift + bitxor + casesen + cat + cd + ceil + cell + cell2struct + cellstr + char + chdir + class + clc + clear + clearplot + clg + closeplot + completion_matches + conj + conv + convmtx + cos + cosh + cumprod + cumsum + dbclear + dbstatus + dbstop + dbtype + dbwhere + deconv + det + dftmtx + diag + diary + disp + document + do_string_escapes + double + dup2 + echo + edit_history + __end__ + erf + erfc + ERRNO + error + __error_text__ + error_text + eval + evalin + exec + exist + exit + exp + eye + fclose + fcntl + fdisp + feof + ferror + feval + fflush + fft + fgetl + fgets + fieldnames + file_in_loadpath + file_in_path + filter + find + find_first_of_in_loadpath + finite + fix + floor + fmod + fnmatch + fopen + fork + format + formula + fprintf + fputs + fread + freport + frewind + fscanf + fseek + ftell + func2str + functions + fwrite + gamma + gammaln + getegid + getenv + geteuid + getgid + getpgrp + getpid + getppid + getuid + glob + graw + gset + gshow + help + history + hold + home + horzcat + ifft + imag + inline + input + input_event_hook + int16 + int32 + int64 + int8 + intmax + intmin + inv + inverse + ipermute + isalnum + isalpha + isascii + isbool + iscell + iscellstr + ischar + iscntrl + iscomplex + isdigit + isempty + isfield + isfinite + isglobal + isgraph + ishold + isieee + isinf + iskeyword + islist + islogical + islower + ismatrix + isna + isnan + is_nan_or_na + isnumeric + isprint + ispunct + isreal + isspace + isstream + isstreamoff + isstruct + isupper + isvarname + isxdigit + kbhit + keyboard + kill + lasterr + lastwarn + length + lgamma + link + linspace + list + load + log + log10 + ls + lstat + lu + mark_as_command + mislocked + mkdir + mkfifo + mkstemp + mlock + more + munlock + nargin + nargout + native_float_format + ndims + nth + numel + octave_config_info + octave_tmp_file_name + ones + pause + pclose + permute + pipe + popen + printf + __print_symbol_info__ + __print_symtab_info__ + prod + purge_tmp_files + putenv + puts + pwd + quit + rank + readdir + readlink + read_readline_init_file + real + rehash + rename + reshape + reverse + rmdir + rmfield + roots + round + run_history + save + scanf + set + shell_cmd + show + sign + sin + sinh + size + sizeof + sleep + sort + source + splice + sprintf + sqrt + squeeze + sscanf + stat + str2func + streamoff + struct + struct2cell + sum + sumsq + symlink + system + tan + tanh + tilde_expand + tmpfile + tmpnam + toascii + __token_count__ + tolower + toupper + type + typeinfo + uint16 + uint32 + uint64 + uint8 + umask + undo_string_escapes + unlink + unmark_command + usage + usleep + va_arg + va_start + vectorize + vertcat + vr_val + waitpid + warning + warranty + which + who + whos + zeros + airy + balance + besselh + besseli + besselj + besselk + bessely + betainc + chol + colloc + daspk + daspk_options + dasrt + dasrt_options + dassl + dassl_options + det + eig + endgrent + endpwent + expm + fft + fft2 + fftn + fftw_wisdom + filter + find + fsolve + fsolve_options + gammainc + gcd + getgrent + getgrgid + getgrnam + getpwent + getpwnam + getpwuid + getrusage + givens + gmtime + hess + ifft + ifft2 + ifftn + inv + inverse + kron + localtime + lpsolve + lpsolve_options + lsode + lsode_options + lu + max + min + minmax + mktime + odessa + odessa_options + pinv + qr + quad + quad_options + qz + rand + randn + schur + setgrent + setpwent + sort + sqrtm + strftime + strptime + svd + syl + time + abcddim + __abcddims__ + acot + acoth + acsc + acsch + analdemo + anova + arch_fit + arch_rnd + arch_test + are + arma_rnd + asctime + asec + asech + autocor + autocov + autoreg_matrix + axis + axis2dlim + __axis_label__ + bar + bartlett + bartlett_test + base2dec + bddemo + beep + bessel + beta + beta_cdf + betai + beta_inv + beta_pdf + beta_rnd + bin2dec + bincoeff + binomial_cdf + binomial_inv + binomial_pdf + binomial_rnd + bitcmp + bitget + bitset + blackman + blanks + bode + bode_bounds + __bodquist__ + bottom_title + bug_report + buildssic + c2d + cart2pol + cart2sph + cauchy_cdf + cauchy_inv + cauchy_pdf + cauchy_rnd + cellidx + center + chisquare_cdf + chisquare_inv + chisquare_pdf + chisquare_rnd + chisquare_test_homogeneity + chisquare_test_independence + circshift + clock + cloglog + close + colormap + columns + com2str + comma + common_size + commutation_matrix + compan + complement + computer + cond + contour + controldemo + conv + cor + corrcoef + cor_test + cot + coth + cov + cputime + create_set + cross + csc + csch + ctime + ctrb + cut + d2c + damp + dare + date + dcgain + deal + deblank + dec2base + dec2bin + dec2hex + deconv + delete + DEMOcontrol + demoquat + detrend + dezero + dgkfdemo + dgram + dhinfdemo + diff + diffpara + dir + discrete_cdf + discrete_inv + discrete_pdf + discrete_rnd + dkalman + dlqe + dlqg + dlqr + dlyap + dmr2d + dmult + dot + dre + dump_prefs + duplication_matrix + durbinlevinson + empirical_cdf + empirical_inv + empirical_pdf + empirical_rnd + erfinv + __errcomm__ + errorbar + __errplot__ + etime + exponential_cdf + exponential_inv + exponential_pdf + exponential_rnd + f_cdf + fftconv + fftfilt + fftshift + figure + fileparts + findstr + f_inv + fir2sys + flipdim + fliplr + flipud + flops + f_pdf + fractdiff + frdemo + freqchkw + __freqresp__ + freqz + freqz_plot + f_rnd + f_test_regression + fullfile + fv + fvl + gamma_cdf + gammai + gamma_inv + gamma_pdf + gamma_rnd + geometric_cdf + geometric_inv + geometric_pdf + geometric_rnd + gls + gram + gray + gray2ind + grid + h2norm + h2syn + hamming + hankel + hanning + hex2dec + hilb + hinf_ctr + hinfdemo + hinfnorm + hinfsyn + hinfsyn_chk + hinfsyn_ric + hist + hotelling_test + hotelling_test_2 + housh + hsv2rgb + hurst + hypergeometric_cdf + hypergeometric_inv + hypergeometric_pdf + hypergeometric_rnd + image + imagesc + impulse + imshow + ind2gray + ind2rgb + ind2sub + index + int2str + intersection + invhilb + iqr + irr + isa + is_abcd + is_bool + is_complex + is_controllable + isdefinite + is_detectable + is_dgkf + is_digital + is_duplicate_entry + is_global + is_leap_year + isletter + is_list + is_matrix + is_observable + ispc + is_sample + is_scalar + isscalar + is_signal_list + is_siso + is_square + issquare + is_stabilizable + is_stable + isstr + is_stream + is_struct + is_symmetric + issymmetric + isunix + is_vector + isvector + jet707 + kendall + kolmogorov_smirnov_cdf + kolmogorov_smirnov_test + kolmogorov_smirnov_test_2 + kruskal_wallis_test + krylov + krylovb + kurtosis + laplace_cdf + laplace_inv + laplace_pdf + laplace_rnd + lcm + lin2mu + listidx + list_primes + loadaudio + loadimage + log2 + logical + logistic_cdf + logistic_inv + logistic_pdf + logistic_regression + logistic_regression_derivatives + logistic_regression_likelihood + logistic_rnd + logit + loglog + loglogerr + logm + lognormal_cdf + lognormal_inv + lognormal_pdf + lognormal_rnd + logspace + lower + lqe + lqg + lqr + lsim + ltifr + lyap + mahalanobis + manova + mcnemar_test + mean + meansq + median + menu + mesh + meshdom + meshgrid + minfo + mod + moddemo + moment + mplot + mu2lin + multiplot + nargchk + nextpow2 + nichols + norm + normal_cdf + normal_inv + normal_pdf + normal_rnd + not + nper + npv + ntsc2rgb + null + num2str + nyquist + obsv + ocean + ols + oneplot + ord2 + orth + __outlist__ + pack + packedform + packsys + parallel + paren + pascal_cdf + pascal_inv + pascal_pdf + pascal_rnd + path + periodogram + perror + place + playaudio + plot + plot_border + __plr__ + __plr1__ + __plr2__ + __plt__ + __plt1__ + __plt2__ + __plt2mm__ + __plt2mv__ + __plt2ss__ + __plt2vm__ + __plt2vv__ + __pltopt__ + __pltopt1__ + pmt + poisson_cdf + poisson_inv + poisson_pdf + poisson_rnd + pol2cart + polar + poly + polyder + polyderiv + polyfit + polyinteg + polyout + polyreduce + polyval + polyvalm + popen2 + postpad + pow2 + ppplot + prepad + probit + prompt + prop_test_2 + pv + pvl + pzmap + qconj + qcoordinate_plot + qderiv + qderivmat + qinv + qmult + qqplot + qtrans + qtransv + qtransvmat + quaternion + qzhess + qzval + randperm + range + rank + ranks + rate + record + rectangle_lw + rectangle_sw + rem + repmat + residue + rgb2hsv + rgb2ind + rgb2ntsc + rindex + rldemo + rlocus + roots + rot90 + rotdim + rotg + rows + run_cmd + run_count + run_test + saveaudio + saveimage + sec + sech + semicolon + semilogx + semilogxerr + semilogy + semilogyerr + series + setaudio + setstr + shg + shift + shiftdim + sign_test + sinc + sinetone + sinewave + skewness + sombrero + sortcom + spearman + spectral_adf + spectral_xdf + spencer + sph2cart + split + ss + ss2sys + ss2tf + ss2zp + stairs + starp + statistics + std + stdnormal_cdf + stdnormal_inv + stdnormal_pdf + stdnormal_rnd + step + __stepimp__ + stft + str2mat + str2num + strappend + strcat + strcmp + strerror + strjust + strrep + struct_contains + struct_elements + studentize + sub2ind + subplot + substr + subwindow + swap + swapcols + swaprows + sylvester_matrix + synthesis + sys2fir + sys2ss + sys2tf + sys2zp + sysadd + sysappend + syschnames + __syschnamesl__ + syschtsam + __sysconcat__ + sysconnect + syscont + __syscont_disc__ + __sysdefioname__ + __sysdefstname__ + sysdimensions + sysdisc + sysdup + sysgetsignals + sysgettsam + sysgettype + sysgroup + __sysgroupn__ + sysidx + sysmin + sysmult + sysout + sysprune + sysreorder + sysrepdemo + sysscale + syssetsignals + syssub + sysupdate + table + t_cdf + tempdir + tempname + texas_lotto + tf + tf2ss + tf2sys + __tf2sysl__ + tf2zp + __tfl__ + tfout + tic + t_inv + title + toc + toeplitz + top_title + t_pdf + trace + triangle_lw + triangle_sw + tril + triu + t_rnd + t_test + t_test_2 + t_test_regression + tzero + tzero2 + ugain + uniform_cdf + uniform_inv + uniform_pdf + uniform_rnd + union + unix + unpacksys + unwrap + upper + u_test + values + vander + var + var_test + vec + vech + version + vol + weibull_cdf + weibull_inv + weibull_pdf + weibull_rnd + welch_test + wgt1o + wiener_rnd + wilcoxon_test + xlabel + xor + ylabel + yulewalker + zgfmul + zgfslv + zginit + __zgpbal__ + zgreduce + zgrownorm + zgscal + zgsgiv + zgshsr + zlabel + zp + zp2ss + __zp2ssg2__ + zp2sys + zp2tf + zpout + z_test + z_test_2 + + + + airy_Ai + airy_Ai_deriv + airy_Ai_deriv_scaled + airy_Ai_scaled + airy_Bi + airy_Bi_deriv + airy_Bi_deriv_scaled + airy_Bi_scaled + airy_zero_Ai + airy_zero_Ai_deriv + airy_zero_Bi + airy_zero_Bi_deriv + atanint + bchdeco + bchenco + bessel_il_scaled + bessel_In + bessel_In_scaled + bessel_Inu + bessel_Inu_scaled + bessel_jl + bessel_Jn + bessel_Jnu + bessel_kl_scaled + bessel_Kn + bessel_Kn_scaled + bessel_Knu + bessel_Knu_scaled + bessel_lnKnu + bessel_yl + bessel_Yn + bessel_Ynu + bessel_zero_J0 + bessel_zero_J1 + beta_gsl + bfgsmin + bisectionstep + builtin + bwfill + bwlabel + cell2csv + celleval + Chi + chol + Ci + clausen + conicalP_0 + conicalP_1 + conicalP_half + conicalP_mhalf + conv2 + cordflt2 + coupling_3j + coupling_6j + coupling_9j + csv2cell + csvconcat + csvexplode + cyclgen + cyclpoly + dawson + debye_1 + debye_2 + debye_3 + debye_4 + deref + dispatch + dispatch_help + display_fixed_operations + dlmread + ellint_Ecomp + ellint_Kcomp + ellipj + erfc_gsl + erf_gsl + erf_Q + erf_Z + _errcore + eta + eta_int + expint_3 + expint_E1 + expint_E2 + expint_Ei + expm1 + exp_mult + exprel + exprel_2 + exprel_n + fabs + fangle + farg + fatan2 + fceil + fconj + fcos + fcosh + fcumprod + fcumsum + fdiag + fermi_dirac_3half + fermi_dirac_half + fermi_dirac_inc_0 + fermi_dirac_int + fermi_dirac_mhalf + fexp + ffloor + fimag + finitedifference + fixed + flog + flog10 + fprod + freal + freshape + fround + fsin + fsinh + fsqrt + fsum + fsumsq + ftan + ftanh + full + gamma_gsl + gamma_inc + gamma_inc_P + gamma_inc_Q + gammainv_gsl + gammastar + gdet + gdiag + gexp + gf + gfilter + _gfweight + ginv + ginverse + glog + glu + gpick + gprod + grab + grank + graycomatrix + __grcla__ + __grclf__ + __grcmd__ + greshape + __grexit__ + __grfigure__ + __grgetstat__ + __grhold__ + __grinit__ + __grishold__ + __grnewset__ + __grsetgraph__ + gsl_sf + gsqrt + gsum + gsumsq + gtext + gzoom + hazard + houghtf + hyperg_0F1 + hzeta + is_complex_sparse + isfixed + isgalois + isprimitive + is_real_sparse + is_sparse + jpgread + jpgwrite + lambert_W0 + lambert_Wm1 + legendre_Pl + legendre_Plm + legendre_Ql + legendre_sphPlm + legendre_sphPlm_array + leval + listen + lnbeta + lncosh + lngamma_gsl + lnpoch + lnsinh + log_1plusx + log_1plusx_mx + log_erfc + lp + make_sparse + mark_for_deletion + medfilt1 + newtonstep + nnz + numgradient + numhessian + pchip_deriv + pngread + pngwrite + poch + pochrel + pretty + primpoly + psi + psi_1_int + psi_1piy + psi_n + rand + rande + randn + randp + regexp + remez + reset_fixed_operations + rotate_scale + rsdec + rsenc + samin + SBBacksub + SBEig + SBFactor + SBProd + SBSolve + Shi + Si + sinc_gsl + spabs + sparse + spfind + spimag + spinv + splu + spreal + SymBand + synchrotron_1 + synchrotron_2 + syndtable + taylorcoeff + transport_2 + transport_3 + transport_4 + transport_5 + trisolve + waitbar + xmlread + zeta + zeta_int + aar + aarmam + ac2poly + ac2rc + acorf + acovf + addpath + ademodce + adim + adsmax + amodce + anderson_darling_cdf + anderson_darling_test + anovan + apkconst + append_save + applylut + ar2poly + ar2rc + arburg + arcext + arfit2 + ar_spa + aryule + assert + au + aucapture + auload + auplot + aurecord + ausave + autumn + average_moments + awgn + azimuth + BandToFull + BandToSparse + base64encode + battery + bchpoly + bestblk + best_dir + best_dir_cov + betaln + bfgs + bfgsmin_example + bi2de + biacovf + bilinear + bisdemo + bispec + biterr + blkdiag + blkproc + bmpwrite + bone + bound_convex + boxcar + boxplot + brighten + bs_gradient + butter + buttord + bwborder + bweuler + bwlabel + bwmorph + bwselect + calendar + cceps + cdiff + cellstr + char + cheb + cheb1ord + cheb2ord + chebwin + cheby1 + cheby2 + chirp + clf + clip + cmpermute + cmunique + cohere + col2im + colfilt + colorgradient + comms + compand + complex + concat + conndef + content + contents + Contents + contourf + convhull + convmtx + cool + copper + corr2 + cosets + count + covm + cplxpair + cquadnd + create_lookup_table + crule + crule2d + crule2dgen + csape + csapi + csd + csvread + csvwrite + ctranspose + cumtrapz + czt + d2_min + datenum + datestr + datevec + dct + dct2 + dctmtx + de2bi + deal + decimate + decode + deg2rad + del2 + delaunay + delaunay3 + delta_method + demo + demodmap + deriv + detrend + dfdp + dftmtx + dhbar + dilate + dispatch + distance + dlmread + dlmwrite + dos + double + drawnow + durlev + dxfwrite + edge + edit + ellip + ellipdemo + ellipj + ellipke + ellipord + __ellip_ws + __ellip_ws_min + encode + eomday + erode + example + ExampleEigenValues + ExampleGenEigenValues + expdemo + expfit + eyediagram + factor + factorial + fail + fcnchk + feedback + fem_test + ff2n + fftconv2 + fieldnames + fill + fill3 + filter2 + filtfilt + filtic + findsym + fir1 + fir2 + fixedpoint + flag + flag_implicit_samplerate + flattopwin + flix + float + fmin + fminbnd + fmins + fminunc + fnder + fnplt + fnval + fplot + freqs + freqs_plot + fsort + fullfact + FullToBand + funm + fzero + gammaln + gapTest + gaussian + gausswin + gconv + gconvmtx + gdeconv + gdftmtx + gen2par + geomean + getfield + getfields + gfft + gftable + gfweight + gget + gifft + ginput + gmm_estimate + gmm_example + gmm_obj + gmm_results + gmm_variance + gmm_variance_inefficient + gquad + gquad2d + gquad2d6 + gquad2dgen + gquad6 + gquadnd + grace_octave_path + gradient + grayslice + grep + grid + griddata + groots + grpdelay + grule + grule2d + grule2dgen + hadamard + hammgen + hankel + hann + harmmean + hilbert + histeq + histfit + histo + histo2 + histo3 + histo4 + hot + hsv + hup + idct + idct2 + idplot + idsim + ifftshift + im2bw + im2col + imadjust + imginfo + imhist + imnoise + impad + impz + imread + imrotate + imshear + imtranslate + imwrite + innerfun + inputname + interp + interp1 + interp2 + interpft + intersect + invest0 + invest1 + invfdemo + invfreq + invfreqs + invfreqz + inz + irsa_act + irsa_actcore + irsa_check + irsa_dft + irsa_dftfp + irsa_genreal + irsa_idft + irsa_isregular + irsa_jitsp + irsa_mdsp + irsa_normalize + irsa_plotdft + irsa_resample + irsa_rgenreal + isa + isbw + isdir + isequal + isfield + isgray + isind + ismember + isprime + isrgb + issparse + isunix + jet + kaiser + kaiserord + lambertw + lattice + lauchli + leasqr + leasqrdemo + legend + legendre + levinson + lin2mu + line_min + lloyds + lookup + lookup_table + lpc + lp_test + mad + magic + makelut + MakeShears + map + mat2gray + mat2str + mdsmax + mean2 + medfilt2 + meshc + minimize + minpol + mkpp + mktheta + mle_estimate + mle_example + mle_obj + mle_results + mle_variance + modmap + mu2lin + mvaar + mvar + mvfilter + mvfreqz + myfeval + nanmax + nanmean + nanmedian + nanmin + nanstd + nansum + ncauer + nchoosek + ncrule + ndims + nelder_mead_min + newmark + nlfilter + nlnewmark + __nlnewmark_fcn__ + nmsmax + nonzeros + normplot + now + nrm + nthroot + nze + OCTAVE_FORGE_VERSION + ode23 + ode45 + ode78 + optimset + ordfilt2 + orient + pacf + padarray + parameterize + parcor + pareto + pascal + patch + pburg + pcg + pchip + pcolor + pcr + peaks + penddot + pendulum + perms + pie + pink + plot3 + __plt3__ + poly2ac + poly2ar + poly_2_ex + poly2mask + poly2rc + poly2sym + poly2th + polyarea + polyconf + polyder + polyderiv + polygcd + polystab + __power + ppval + prctile + prettyprint + prettyprint_c + primes + princomp + print + prism + proplan + pulstran + pwelch + pyulear + qaskdeco + qaskenco + qtdecomp + qtgetblk + qtsetblk + quad2dc + quad2dcgen + quad2dg + quad2dggen + quadc + quadg + quadl + quadndg + quantiz + quiver + rad2deg + rainbow + randerr + randint + randsrc + rat + rats + rc2ac + rc2ar + rc2poly + rceps + read_options + read_pdb + rectpuls + resample + rgb2gray + rk2fixed + rk4fixed + rk8fixed + rmfield + rmle + rmpath + roicolor + rosser + rotparams + rotv + rref + rsdecof + rsencof + rsgenpoly + samin_example + save_vrml + sbispec + scale_data + scatter + scatterplot + select_3D_points + selmo + setdiff + setfield + setfields + setxor + sftrans + sgolay + sgolayfilt + sinvest1 + slurp_file + sortrows + sound + soundsc + spdiags + specgram + speed + speye + spfun + sphcat + spline + splot + spones + sprand + sprandn + spring + spstats + spsum + sp_test + sptest + spvcat + spy + std2 + stem + str2double + strcmpi + stretchlim + strfind + strmatch + strncmp + strncmpi + strsort + strtok + strtoz + struct + strvcat + summer + sumskipnan + surf + surfc + sym2poly + symerr + symfsolve + tabulate + tar + temp_name + test + test_d2_min_1 + test_d2_min_2 + test_d2_min_3 + test_ellipj + test_fminunc_1 + testimio + test_inline_1 + test_min_1 + test_min_2 + test_min_3 + test_min_4 + test_minimize_1 + test_nelder_mead_min_1 + test_nelder_mead_min_2 + test_sncndn + test_struct + test_vmesh + test_vrml_faces + test_wpolyfit + text + textread + tf2zp + tfe + thfm + tics + toeplitz + toggle_grace_use + transpose + trapz + triang + tril + trimmean + tripuls + trisolve + triu + tsademo + tsearchdemo + ucp + uintlut + unique + unix + unmkpp + unscale_parameters + vec2mat + view + vmesh + voronoi + voronoin + vrml_arrow + vrml_Background + vrml_browse + vrml_cyl + vrml_demo_tutorial_1 + vrml_demo_tutorial_2 + vrml_demo_tutorial_3 + vrml_demo_tutorial_4 + vrml_ellipsoid + vrml_faces + vrml_flatten + vrml_frame + vrml_group + vrml_kill + vrml_lines + vrml_material + vrml_parallelogram + vrml_PointLight + vrml_points + vrml_select_points + vrml_surf + vrml_text + vrml_thick_surf + vrml_transfo + wavread + wavwrite + weekday + wgn + white + wilkinson + winter + wpolyfit + wpolyfitdemo + write_pdb + wsolve + xcorr + xcorr2 + xcov + xlsread + xmlwrite + y2res + zero_count + zoom + zp2tf + zplane + zscore + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/oors.xml b/kate/part/syntax/data/oors.xml new file mode 100644 index 00000000..dccff286 --- /dev/null +++ b/kate/part/syntax/data/oors.xml @@ -0,0 +1,94 @@ + + + + + + allow + apply + backtrack + copy + condition + deny + extends + graph + if + else + linear + profile + rule + ruleset + search + unsafe + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/opal.xml b/kate/part/syntax/data/opal.xml new file mode 100644 index 00000000..ac85f7c2 --- /dev/null +++ b/kate/part/syntax/data/opal.xml @@ -0,0 +1,331 @@ + + + + + + + + ALL + AND + ANDIF + ANY + AS + ASSERT + AXM + COMPLETELY + DATA + DEF + DERIVE + DFD + DISCRIMINATORS + ELSE + EX + EXTERNAL + FI + FIX + FUN + IF + IMPLEMENTATION + IMPLIES + IMPORT + IN + INHERIT + INJECTIONS + INTERFACE + INTERNAL + LAW + LAZY + LEFTASSOC + LET + MODULE + NOT + ONLY + OR + ORIF + OTHERWISE + POST + PRE + PRED + PRIORITY + PROPERTIES + REALIZES + REQUIRE + RIGHTASSOC + SELECTORS + SIGNATURE + SORT + SPC + SPEC + SPECIFICATION + STRUCTURE + THE + THEN + THEORY + THM + TYPE + UNIQ + WHERE + + + aEntry + agent + align + anchor + ans + arg + arg1 + arg2 + array + arrowWhere + bag + bitmap + bool + bstree + byte + callback + canvasEditor + capStyle + channel + char + childstat + codom + codomFrom + codomTo + color + colorModel + com + composeOp + config + configCom + cursor + dArray + data + data1 + data11 + data2 + data21 + data3 + data31 + data4 + data41 + dataFrom + dataTo + defaultPrio + denotation + device + dist + distOut + dom + domFrom + domTo + drawing + dyn + emitter + env + event + eventInfo + file + filemode + filestat + filetype + first + first1 + first2 + first3 + fission + fmt + font + from + from1 + from2 + funct + group + groupid + heap + iconfig + image + in + inData + index + inode + input + int + inter + interdom + interpreter + iseq + items + joinStyle + justifyHow + long + manager + managerRequest + map + mapEntry + mark + mid + modifier + nat + natMap + OBJECT + option + orient + out + outData + output + packOp + pair + parser + permission + point + positionRequest + process + procstat + quad + range + real + regulator + rel + relief + res + res1 + res2 + result + role + sap + script + scroller + scrollView + scrollWindow + searchOpt + second + seekMode + selector + semaphor + seq + seqEntry + set + setEntry + short + sigaction + sighandler + sigmask + signal + size + sizeRequest + some + sreal + state + stateId + stateRequest + string + subrel + tag + textEditor + time + to + tree + triple + union + user + userid + version + view + void + wconfig + wconfigCom + wday + widget + window + wrapStyle + + + true + false + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 15 + 16 + 17 + 18 + 19 + 20 + 21 + 22 + 23 + 24 + 25 + 26 + 27 + 28 + 29 + 30 + 31 + 32 + 64 + 128 + 256 + 512 + 1024 + 10000 + 100000 + 1000000 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/opencl.xml b/kate/part/syntax/data/opencl.xml new file mode 100644 index 00000000..46f1c514 --- /dev/null +++ b/kate/part/syntax/data/opencl.xml @@ -0,0 +1,257 @@ + + + + + + __constant + __global + __local + __private + __kernel + __read_only + __write_only + constant + global + local + private + kernel + read_only + write_only + break + case + continue + default + do + else + enum + for + goto + if + inline + return + sizeof + struct + switch + typedef + union + while + + + bool + uchar + uchar2 + uchar3 + uchar4 + uchar8 + uchar16 + char + char2 + char3 + char4 + char8 + char16 + const + double + double2 + double3 + double4 + double8 + double16 + event_t + float + float2 + float3 + float4 + float8 + float16 + half + half2 + half3 + half4 + half8 + half16 + image1d_t + image2d_t + image3d_t + uint + uint2 + uint3 + uint4 + uint8 + uint16 + int + int2 + int3 + int4 + int8 + int16 + ulong + ulong2 + ulong3 + ulong4 + ulong8 + ulong16 + long + long2 + long3 + long4 + long8 + long16 + restrict + sampler_t + ushort + ushort2 + ushort3 + ushort4 + ushort8 + ushort16 + short + short2 + short3 + short4 + short8 + short16 + signed + static + unsigned + void + volatile + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/pango.xml b/kate/part/syntax/data/pango.xml new file mode 100644 index 00000000..0a9fcba8 --- /dev/null +++ b/kate/part/syntax/data/pango.xml @@ -0,0 +1,159 @@ + + + + + + + + <span + <b + <big + <i + <s + <sub + <sup + <small + <tt + <u + + + + </span> + </b> + </big> + </i> + </s> + </sub> + </sup> + </small> + </tt> + </u> + + + + size= + font_size= + rise= + letter_spacing= + + + + font= + font_desc= + font_family= + face= + lang= + + + + strikethrough_color= + foreground= + fgcolor= + color= + background= + bgcolor= + underline_color= + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/pascal.xml b/kate/part/syntax/data/pascal.xml new file mode 100644 index 00000000..e8d953c4 --- /dev/null +++ b/kate/part/syntax/data/pascal.xml @@ -0,0 +1,219 @@ + + + + + + and + array + asm + case + const + div + do + downto + else + file + for + function + goto + if + in + label + mod + nil + not + of + operator + or + packed + procedure + program + record + repeat + set + then + to + type + unit + until + uses + var + while + with + xor + + + at + automated + break + continue + dispinterface + dispose + exit + false + finalization + initialization + library + new + published + resourcestring + self + true + + + abstract + as + bindable + constructor + destructor + except + export + finally + import + implementation + inherited + inline + interface + is + module + on + only + otherwise + override + private + property + protected + public + read + qualified + raise + restricted + shl + shr + threadvar + try + virtual + write + + + Integer + Cardinal + ShortInt + SmallInt + LongInt + Int64 + Byte + Word + LongWord + DWord + QWord + Char + AnsiChar + WideChar + Boolean + ByteBool + WordBool + LongBool + Single + Double + Extended + Comp + Currency + Real + Real48 + String + ShortString + AnsiString + WideString + Pointer + Variant + File + Text + + + FIXME + TODO + NOTE + ### + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/perl.xml b/kate/part/syntax/data/perl.xml new file mode 100644 index 00000000..7993cd73 --- /dev/null +++ b/kate/part/syntax/data/perl.xml @@ -0,0 +1,842 @@ + + + + + + + if + unless + else + elsif + while + until + for + each + foreach + next + last + break + continue + return + my + our + local + state + BEGIN + END + package + sub + do + given + when + default + __END__ + __DATA__ + __FILE__ + __LINE__ + __PACKAGE__ + + + = + != + ~= + += + -= + *= + /= + **= + |= + ||= + //= + &= + &&= + ?= + + + - + * + + % + || + // + && + | + & + < + << + > + >> + ^ + -> + => + . + , + ; + :: + \ + and + or + not + eq + ne + lt + gt + le + ge + cmp + + + abs + accept + alarm + atan2 + bind + binmode + bless + caller + chdir + chmod + chomp + chop + chown + chr + chroot + close + closedir + connect + cos + crypt + dbmclose + dbmopen + defined + delete + die + dump + endgrent + endhostent + endnetent + endprotoent + endpwent + endservent + eof + eval + exec + exists + exit + exp + fcntl + fileno + flock + fork + format + formline + getc + getgrent + getgrgid + getgrnam + gethostbyaddr + gethostbyname + gethostent + getlogin + getnetbyaddr + getnetbyname + getnetent + getpeername + getpgrp + getppid + getpriority + getprotobyname + getprotobynumber + getprotoent + getpwent + getpwnam + getpwuid + getservbyname + getservbyport + getservent + getsockname + getsockopt + glob + gmtime + goto + grep + hex + import + index + int + ioctl + join + keys + kill + last + lc + lcfirst + length + link + listen + localtime + lock + log + lstat + map + mkdir + msgctl + msgget + msgrcv + msgsnd + no + oct + open + opendir + ord + pack + package + pipe + pop + pos + print + printf + prototype + push + quotemeta + rand + read + readdir + readline + readlink + recv + redo + ref + rename + require + reset + return + reverse + rewinddir + rindex + rmdir + scalar + seek + seekdir + select + semctl + semget + semop + send + setgrent + sethostent + setnetent + setpgrp + setpriority + setprotoent + setpwent + setservent + setsockopt + shift + shmctl + shmget + shmread + shmwrite + shutdown + sin + sleep + socket + socketpair + sort + splice + split + sprintf + sqrt + srand + stat + study + sub + substr + symlink + syscall + sysread + sysseek + system + syswrite + tell + telldir + tie + time + times + truncate + uc + ucfirst + umask + undef + unlink + unpack + unshift + untie + use + utime + values + vec + wait + waitpid + wantarray + warn + write + + + strict + english + warnings + vars + subs + utf8 + sigtrap + locale + open + less + integer + filetest + constant + bytes + diagnostics + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/pgn.xml b/kate/part/syntax/data/pgn.xml new file mode 100644 index 00000000..5d4493b6 --- /dev/null +++ b/kate/part/syntax/data/pgn.xml @@ -0,0 +1,108 @@ + + + + + Event + Site + Date + Round + White + Black + Result + ECO + Annotator + PlyCount + EventDate + EventCountry + SourceDate + WhiteTitle + BlackTitle + FM + IM + GM + WhiteElo + BlackElo + WhiteNA + BlackNA + WhiteType + BlackType + program + human + TimeControl + FEN + Termination + abandoned + adjudication + death + emergency + normal + rules + infraction + time + forfeit + unterminated + Mode + OTB + PM + EM + ICS + TC + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/php.xml b/kate/part/syntax/data/php.xml new file mode 100644 index 00000000..3abd5e26 --- /dev/null +++ b/kate/part/syntax/data/php.xml @@ -0,0 +1,5635 @@ + + + +]> + + diff --git a/kate/part/syntax/data/picsrc.xml b/kate/part/syntax/data/picsrc.xml new file mode 100644 index 00000000..57386f55 --- /dev/null +++ b/kate/part/syntax/data/picsrc.xml @@ -0,0 +1,402 @@ + + + + + + + + + + + __BADRAM + __CONFIG + __IDLOCS + __MAXRAM + + cblock + constant + da + data + db + de + dt + dw + endc + endm + equ + error + errorlevel + exitm + fill + list + local + macro + messg + noexpand + nolist + org + page + processor + radix + res + set + space + subtitle + title + variable + end + + CBLOCK + CONSTANT + DA + DATA + DB + DE + DT + DW + ENDC + ENDM + EQU + ERROR + ERRORLEVEL + EXITM + FILL + LIST + LOCAL + MACRO + MESSG + NOEXPAND + NOLIST + ORG + PAGE + PROCESSOR + RADIX + RES + SET + SPACE + SUBTITLE + TITLE + VARIABLE + END + + + + if + else + idef + ifndef + endif + while + include + endw + { + } + + + + addcf + b + clrc + clrz + setc + setz + movfw + skpc + skpz + skpnc + skpnz + subcf + tstf + + ADDCF + B + CLRC + CLRZ + SETC + SETZ + MOVFW + SKPC + SKPZ + SKPNC + SKPNZ + SUBCF + TSTF + + + + addlw + addwf + addwfc + andlw + andwf + bc + bcf + bn + bnc + bnov + bnz + bov + bra + bsf + btg + bz + btfsc + btfss + call + clrf + clrw + clrwdt + comf + cpfseq + cpfslt + cpfsgt + daw + decf + dcfsnz + decfsz + goto + incf + incfsz + infsnz + iorlw + iorwf + lfsr + movf + movff + movlb + movlw + movwf + mullw + mulwf + negf + nop + option + pop + push + rcall + reset + retfie + retlw + return + rlcf + rlf + rlncf + rrcf + rrf + rrncf + setf + sleep + subfwb + sublw + subwf + subwfb + swapf + tblrd + tblwt + tstfsz + xorlw + xorwf + + ADDLW + ADDWF + ADDWFC + ANDLW + ANDWF + BC + BCF + BN + BNC + BNOV + BNZ + BOV + BRA + BSF + BTG + BZ + BTFSC + BTFSS + CALL + CLRF + CLRW + CLRWDT + COMF + CPFSEQ + CPFSLT + CPFSGT + DAW + DCFSNZ + DECF + DECFSZ + GOTO + INCF + INCFSZ + INFSNZ + IORLW + IORWF + LFSR + MOVF + MOVFF + MOVLB + MOVLW + MOVWF + MULLW + MULWF + NEGF + NOP + OPTION + POP + PUSH + RCALL + RESET + RETFIE + RETLW + RETURN + RLCF + RLF + RLNCF + RRCF + RRF + RRNCF + SETF + SLEEP + SUBFWB + SUBLW + SUBWF + SUBWFB + SWAPF + TBLRD + TBLWT + TSTFSZ + XORLW + XORWF + + + + A + ACCESS + BANKED + W + F + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/pig.xml b/kate/part/syntax/data/pig.xml new file mode 100644 index 00000000..a3dc318f --- /dev/null +++ b/kate/part/syntax/data/pig.xml @@ -0,0 +1,202 @@ + + + + + + + load + store + filter + foreach + order + arrange + distinct + cogroup + join + cross + union + onschema + split + into + if + all + any + as + by + using + inner + outer + parallel + group + continuously + window + tuples + generate + eval + define + returns + input + output + ship + cache + stream + through + seconds + minutes + hours + asc + desc + null + left + right + full + + + cat + cd + cp + copyFromLocal + copyToLocal + define + dump + illustrate + describe + explain + exec + help + kill + ls + mv + mkdir + pwd + quit + register + import + rm + set + + + chararray + bytearray + int + long + float + double + tuple + bag + map + + + and + is + not + or + eq + neq + gt + lt + gte + lte + matches + + + flatten + sum + count + min + max + avg + arity + tokenize + diff + size + concat + BinStorage + PigStorage + TextLoader + PigDump + IsEmpty + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/pike.xml b/kate/part/syntax/data/pike.xml new file mode 100644 index 00000000..461b0c17 --- /dev/null +++ b/kate/part/syntax/data/pike.xml @@ -0,0 +1,121 @@ + + + + + + + break + case + class + continue + default + do + else + for + foreach + if + return + switch + while + + + array + float + function + int + mapping + mixed + multiset> + object + program + static + string + void + + + catch + gauge + sscanf + typeof + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/postscript.xml b/kate/part/syntax/data/postscript.xml new file mode 100644 index 00000000..b590db23 --- /dev/null +++ b/kate/part/syntax/data/postscript.xml @@ -0,0 +1,434 @@ + + + + + + abs + add + aload + anchorsearch + and + arc + arcn + arct + arcto + array + ashow + astore + awidthshow + begin + bind + bitshift + ceiling + charpath + clear + cleartomark + clip + clippath + closepath + concat + concatmatrix + copy + count + counttomark + currentcmykcolor + currentdash + currentdict + currentfile + currentfont + currentgray + currentgstate + currenthsbcolor + currentlinecap + currentlinejoin + currentlinewidth + currentmatrix + currentpoint + currentrgbcolor + currentshared + curveto + cvi + cvlit + cvn + cvr + cvrs + cvs + cvx + def + defineusername + dict + div + dtransform + dup + end + eoclip + eofill + eoviewclip + eq + exch + exec + exit + file + fill + findfont + flattenpath + floor + flush + flushfile + for + forall + ge + get + getinterval + grestore + gsave + gstate + gt + identmatrix + idiv + idtransform + if + ifelse + image + imagemask + index + ineofill + infill + initviewclip + inueofill + inufill + invertmatrix + itransform + known + le + length + lineto + load + loop + lt + makefont + matrix + maxlength + mod + moveto + mul + ne + neg + newpath + not + null + or + pathbbox + pathforall + pop + print + printobject + put + putinterval + rcurveto + read + readhexstring + readline + readstring + rectclip + rectfill + rectstroke + rectviewclip + repeat + restore + rlineto + rmoveto + roll + rotate + round + save + scale + scalefont + search + selectfont + setbbox + setcachedevice + setcachedevice2 + setcharwidth + setcmykcolor + setdash + setfont + setgray + setgstate + sethsbcolor + setlinecap + setlinejoin + setlinewidth + setmatrix + setrgbcolor + setshared + shareddict + show + showpage + stop + stopped + store + string + stringwidth + stroke + strokepath + sub + systemdict + token + transform + translate + truncate + type + uappend + ucache + ueofill + ufill + undef + upath + userdict + ustroke + viewclip + viewclippath + where + widthshow + write + writehexstring + writeobject + writestring + wtranslation + xor + xshow + xyshow + yshow + FontDirectory + SharedFontDirectory + Courier + Courier-Bold + Courier-BoldOblique + Courier-Oblique + Helvetica + Helvetica-Bold + Helvetica-BoldOblique + Helvetica-Oblique + Symbol + Times-Bold + Times-BoldItalic + Times-Italic + Times-Roman + execuserobject + currentcolor + currentcolorspace + currentglobal + execform + filter + findresource + globaldict + makepattern + setcolor + setcolorspace + setglobal + setpagedevice + setpattern + ISOLatin1Encoding + StandardEncoding + atan + banddevice + bytesavailable + cachestatus + closefile + colorimage + condition + copypage + cos + countdictstack + countexecstack + cshow + currentblackgeneration + currentcacheparams + currentcolorscreen + currentcolortransfer + currentcontext + currentflat + currenthalftone + currenthalftonephase + currentmiterlimit + currentobjectformat + currentpacking + currentscreen + currentstrokeadjust + currenttransfer + currentundercolorremoval + defaultmatrix + definefont + deletefile + detach + deviceinfo + dictstack + echo + erasepage + errordict + execstack + executeonly + exp + false + filenameforall + fileposition + fork + framedevice + grestoreall + handleerror + initclip + initgraphics + initmatrix + instroke + inustroke + join + kshow + ln + lock + log + mark + monitor + noaccess + notify + nulldevice + packedarray + quit + rand + rcheck + readonly + realtime + renamefile + renderbands + resetfile + reversepath + rootfont + rrand + run + scheck + setblackgeneration + setcachelimit + setcacheparams + setcolorscreen + setcolortransfer + setfileposition + setflat + sethalftone + sethalftonephase + setmiterlimit + setobjectformat + setpacking + setscreen + setstrokeadjust + settransfer + setucacheparams + setundercolorremoval + sin + sqrt + srand + stack + status + statusdict + true + ucachestatus + undefinefont + usertime + ustrokepath + version + vmreclaim + vmstatus + wait + wcheck + xcheck + yield + defineuserobject + undefineuserobject + UserObjects + cleardictstack + setvmthreshold + currentcolorrendering + currentdevparams + currentoverprint + currentpagedevice + currentsystemparams + currentuserparams + defineresource + findencoding + gcheck + glyphshow + languagelevel + product + pstack + resourceforall + resourcestatus + revision + serialnumber + setcolorrendering + setdevparams + setoverprint + setsystemparams + setuserparams + startjob + undefineresource + GlobalFontDirectory + ASCII85Decode + ASCII85Encode + ASCIIHexDecode + ASCIIHexEncode + CCITTFaxDecode + CCITTFaxEncode + DCTDecode + DCTEncode + LZWDecode + LZWEncode + NullEncode + RunLengthDecode + RunLengthEncode + SubFileDecode + CIEBasedA + CIEBasedABC + DeviceCMYK + DeviceGray + DeviceRGB + Indexed + Pattern + Separation + CIEBasedDEF + CIEBasedDEFG + DeviceN + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/povray.xml b/kate/part/syntax/data/povray.xml new file mode 100644 index 00000000..bfeeda92 --- /dev/null +++ b/kate/part/syntax/data/povray.xml @@ -0,0 +1,976 @@ + + + + + + + + + + + aa_threshold + aa_level + absorption + abs + accuracy + acosh + acos + adaptive + adc_bailout + agate + agate_turb + all_intersections + all + alpha + always_sample + altitude + ambient_light + ambient + angle + aperture + append + arc_angle + area_light + array + ascii + asc + asinh + asin + assumed_gamma + atan2 + atanh + atan + average + background + bezier_spline + bicubic_patch + black_hole + blob + blue + blur_samples + bounded_by + boxed + box + bozo + brick_size + brick + brightness + brilliance + bumps + bump_map + bump_size + b_spline + camera + caustics + ceil + cells + charset + checker + chr + circular + clipped_by + clock_delta + clock_on + clock + color_map + colour_map + color + colour + component + composite + concat + cone + confidence + conic_sweep + contained_by + control0 + control1 + coords + cosh + cos + count + crackle + crand + cube + cubic_spline + cubic + cubic_wave + cylinder + cylindrical + defined + degrees + density_file + density_map + density + dents + df3 + difference + diffuse + dimensions + dimension_size + direction + disc + dispersion + dispersion_samples + distance_maximum + distance + dist_exp + div + double_illuminate + eccentricity + emission + error_bound + evaluate + exp + exponent + exterior + extinction + facets + face_indices + fade_colour + fade_color + fade_distance + fade_power + falloff_angle + falloff + false + file_exists + shadowless + filter + final_clock + final_frame + finish + fisheye + flatness + flip + floor + focal_point + fog_alt + fog_offset + fog + fog_type + form + frame_number + frequency + fresnel + function + gif + global_settings + gradient + granite + gray + gray_threshold + green + height_field + hexagon + hf_gray_16 + hierarchy + hollow + hypercomplex + image_width + image_height + initial_clock + initial_frame + interior_texture + iff + image_map + image_pattern + inside + inside_vector + interior + interpolate + intersection + intervals + int + inverse + ior + irid + irid_wavelength + isosurface + jitter + julia + julia_fractal + jpeg + lambda + lathe + leopard + light_group + light_source + linear_spline + linear_sweep + ln + load_file + location + log + looks_like + look_at + low_error_factor + magnet + major_radius + mandel + map_type + marble + material_map + material + matrix + max_extent + max_gradient + max_intersections + max_iteration + max_sample + max + max_trace_level + max_trace + media_attenuation + media_interaction + media + merge + mesh2 + mesh + metallic + method + metric + minimum_reuse + min_extent + min + mod + mortar + natural_spline + nearest_count + normal + normal_indices + normal_map + normal_vectors + no_image + no_reflection + no_shadow + no + number_of_waves + object + octaves + offset + off + omega + omnimax + once + onion + on + open + orientation + orient + orthographic + panoramic + parallel + parametric + pattern + perspective + pgm + phase + phong_size + phong + pigment_map + pigment + pi + planar + plane + png + point_at + polygon + poly + poly_wave + pot + pow + ppm + precision + precompute + pretrace_start + pretrace_end + prism + prod + projected_through + pwr + quadratic_spline + quadric + quartic + quaternion + quick_color + quick_colour + quilted + radial + radians + radiosity + radius + rainbow + ramp_wave + rand + range + ratio + reciprocal + recursion_limit + red + reflection_exponent + reflection + refraction + repeat + rgbft + rgbf + rgbt + rgb + right + ripples + rotate + roughness + samples + save_file + scale + scallop_wave + scattering + seed + select + sine_wave + sinh + sin + size + sky_sphere + sky + slice + slope_map + slope + smooth + smooth_triangle + solid + sor + specular + sphere_sweep + sphere + spherical + spiral1 + spiral2 + spline + spotlight + spotted + sqrt + sqr + statistics + strcmp + strength + strlen + strlwr + strupr + str + sturm + substr + sum + superellipsoid + sys + tanh + tan + texture_list + texture_map + texture + text + tga + thickness + threshold + tiff + tightness + tile2 + tiles + normal + tolerance + toroidal + torus + trace + transform + translate + transmit + triangle + triangle_wave + true + ttf + turbulence + turb_depth + type + t + ultra_wide_angle + union + up + use_alpha + use_color + use_colour + use_index + utf8 + uv_indices + uv_mapping + uv_vectors + u_steps + u + val + variance + vaxis_rotate + vcross + vdot + vertex_vectors + vlength + vnormalize + vrotate + vstr + vturbulence + v_steps + v + warning + warp + water_level + waves + while + width + wood + wrinkles + write + x + yes + y + z + photons + steps + pass_through + collect + autostop + gather + split_union + expand_thresholds + spacing + global + target + conserve_energy + cutaway_textures + pigment_pattern + no_bump_scale + global_lights + internal + noise_generator + + + akima_spline + aoi + basic_x_spline + camera_view + displace + exposure + exposure_gain + extended_x_spline + float + frame_step + date + start_chrono + current_chrono + general_x_spline + glow + h_align_left + h_align_right + h_align_center + v_align_top + v_align_bottom + v_align_center + inverted_normals + is + listed + mpeg + n_roots + noise_pigment + output_filename + post_process + projection + point + blur + set + sor_spline + string + structure + tcb_spline + tension + continuity + bias + user_defined + vector + unofficial_version + gravity + environment + friction + damping + simcloth + wind + neighbors + neighbours + internal_collision + viscosity + iterations + input + mesh_output + smooth_mesh + uv_mesh + output + stiffness + topology + mass + connection + face + velocity + position + mass_count + connection_count + face_count + index1 + index2 + index3 + time + time_step + step_count + start_time + end_time + fixed + collision + interaction + group + attach + field + mechsim + bounding + viscoelastic + viscoelastic_count + element + length + force + message + weight + randomize + hdr + no_radiosity + motion_blur + tone_mapping + show_samples + show_low_count + + + bicubic_patch + blob + box + cone + cubic + cylinder + difference + disc + height_field + intersection + isosurface + julia_fractal + lathe + light_source + merge + mesh + mesh2 + object + parametric + plane + poly + polygon + prism + quadric + quartic + smooth_triangle + sor + sphere + sphere_sweep + superellipsoid + text + torus + triangle + union + + + aa_threshold + aa_level + absorption + agate + agate_turb + ambient + average + black_hole + blue + boxed + brick_size + brick + brilliance + bumps + bump_map + bump_size + caustics + cells + checker + color_map + colour_map + color + colour + conserve_energy + control0 + control1 + crackle + crand + cubic_wave + cylindrical + density_file + density_map + density + dents + diffuse + dist_exp + double_illuminate + eccentricity + emission + exponent + exterior + extinction + facets + fade_colour + fade_color + fade_distance + fade_power + filter + finish + form + frequency + fresnel + gradient + granite + gray + green + hexagon + hypercomplex + interior_texture + image_map + image_pattern + interior + interpolate + intervals + ior + irid + irid_wavelength + julia + lambda + leopard + magnet + mandel + map_type + marble + material_map + material + media + metallic + method + metric + mortar + normal + normal_map + number_of_waves + octaves + omega + once + onion + orientation + phase + phong_size + phong + pigment_map + pigment + planar + quaternion + quick_color + quick_colour + quilted + radial + ramp_wave + red + reflection_exponent + reflection + repeat + rgbft + rgbf + rgbt + rgb + ripples + roughness + samples + scallop_wave + scattering + sine_wave + slope_map + slope + solid + specular + spherical + spiral1 + spiral2 + spotted + texture_list + texture_map + texture + tile2 + tiles + normal + toroidal + transmit + triangle_wave + turbulence + turb_depth + use_alpha + use_color + use_colour + use_index + uv_mapping + warp + waves + wood + wrinkles + cutaway_textures + pigment_pattern + no_bump_scale + + + matrix + rotate + scale + translate + transform + + + df3 + gif + iff + jpeg + pgm + png + pot + ppm + tga + tiff + ttf + + + clock + clock_delta + clock_on + final_clock + final_frame + frame_number + image_height + image_width + initial_clock + initial_frame + t + u + v + x + y + z + + + false + no + off + on + pi + true + yes + + + abs + acos + acosh + asc + asin + asinh + atan + atan2 + atanh + ceil + chr + concat + cos + cosh + cube + defined + degress + dimension_size + dimensions + div + exp + file_exists + floor + inside + int + ln + log + max + min + mod + pow + prod + pwr + radians + rand + seed + select + sin + sinh + sqrt + str + strcmp + strlen + strlwr + strupr + substr + sum + tan + tanh + trace + val + vaxis_rotate + vcross + vdot + vlength + vnormalize + vrotate + vstr + vturbulence + + + #debug + #default + #else + #end + #error + #fclose + #fopen + #if + #ifdef + #ifndef + #include + #range + #read + #render + #statistics + #switch + #undef + #version + #warning + #while + #write + + + FIXME + TODO + ### + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/ppd.xml b/kate/part/syntax/data/ppd.xml new file mode 100644 index 00000000..796bd900 --- /dev/null +++ b/kate/part/syntax/data/ppd.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/progress.xml b/kate/part/syntax/data/progress.xml new file mode 100644 index 00000000..985a9209 --- /dev/null +++ b/kate/part/syntax/data/progress.xml @@ -0,0 +1,1709 @@ + + + + + + + + CHAR + CHARACTER + COM-HANDLE + DECI + DECIMAL + DATE + INTE + INTEGER + ROWID + RECID + LOGI + LONGCHAR + LOGICAL + HANDLE + WIDGET-HANDLE + RAW + LONG + BYTE + SHORT + BLOB + UNSIGNED-SHORT + FLOAT + DOUBLE + CLOB + LONGCHAR + DATETIME + DATETIME-TZ + TEMP-TABLE + DATASET + DATASET-HANDLE + BUFFER + STREAM + SAX-WRITER + MEMPTR + + + + AND + OR + NOT + BEGINS + MATCHES + MODULO + EQ + NE + LT + GT + GE + LE + FALSE + TRUE + YES + NO + + + + OUT + BATCH-MODE + FILE-NAME + GLOBAL-DEFINE + LINE-NUMBER + OPSYS + ENDIF + SCOPED-DEFINE + SEQUENCE + UNDEFINE + WINDOW-SYSTEM + ANALYZE-RESUME + ANALYZE-SUSPEND + WEBSTREAM + + + + VOID + IMPLEMENTS + OVERRIDE + FINAL + INHERITS + AS + WORD-INDEX + LIKE + ALERT-BOX + AT + COLOR + COMBO-BOX + EDITOR + FORMAT + FRAME + IMAGE + ENDKEY + ERROR + QUIT + STOP + PRESELECT + QUERY-TUNING + RADIO-SET + RECORD + SELECTION-LIST + SIZE + SLIDER + TRIGGER + WIDGET + VIEW-AS + NO-UNDO + NO-ERROR + OUT + WHERE + GLOBAL + SHARED + DEFINED + EXCLUSIVE-LOCK + SHARE-LOCK + NO-LOCK + EACH + FIRST + PREV + LAST + SKIP + FORWARD + UNIQUE + UNFORMATTED + APPEND + IN + OF + BEFORE-HIDE + NO-BOX + NO-UNDERLINE + KEEP-TAB-ORDER + UNBUFFERED + BINARY + NO-CONVERT + CONVERT + TARGET + NO-FILL + BY + NO-LABEL + GROUP + BREAK + COLON-ALIGNED + WITH + NO-LABELS + NO-PAUSE + NO-MESSAGE + NO-ECHO + NO-MAP + PRIVATE + PUBLIC + PROTECTED + + + + ROW-UPDATE + VALID-OBJECT + TYPE-OF + CAST + AUDIT-ENABLED + GENERATE-UUID + GUID + HEX-DECODE + HEX-ENCODE + SET-DB-CLIENT + output-content-type + get-value + get-cgi + get-field + html-encode + url-encode + ABSOLUTE + ACCUM + ADD-INTERVAL + ALIAS + AMBIGUOUS + ASC + AVAILABLE + BASE64-DECODE + BASE64-ENCODE + CAN-DO + CAN-FIND + CAN-QUERY + CAN-SET + CAPS + CHR + CODEPAGE-CONVERT + COMPARE + CONNECTED + COUNT-OF + CURRENT-CHANGED + CURRENT-LANGUAGE + CURRENT-RESULT-ROW + CURRENT-VALUE + DATASERVERS + DATA-SOURCE-MODIFIED + DATE + DATETIME + DATETIME-TZ + DAY + DBCODEPAGE + DBCOLLATION + DBNAME + DBPARAM + DBRESTRICTIONS + DBTASKID + DBTYPE + DBVERSION + DECIMAL + DECRYPT + DYNAMIC-CURRENT-VALUE + DYNAMIC-FUNCTION + DYNAMIC-NEXT-VALUE + ENCODE + ENCRYPT + ENTERED + ENTRY + ERROR + ETIME + EXP + EXTENT + FILL + FIRST + FIRST-OF + FIX-CODEPAGE + FRAME-COL + FRAME-DB + FRAME-DOWN + FRAME-FIELD + FRAME-FILE + FRAME-INDEX + FRAME-LINE + FRAME-NAME + FRAME-ROW + FRAME-VALUE + GENERATE-PBE-KEY + GENERATE-PBE-SALT + GENERATE-RANDOM-KEY + GET-BITS + GET-BYTE + GET-BYTE-ORDER + GET-BYTES + GET-CODEPAGE + GET-CODEPAGES + GET-COLLATION + GET-COLLATIONS + GET-DOUBLE + GET-FLOAT + GET-LONG + GET-POINTER-VALUE + GET-SHORT + GET-SIZE + GET-STRING + GET-UNSIGNED-SHORT + INDEX + INTEGER + INTERVAL + IS-CODEPAGE-FIXED + IS-COLUMN-CODEPAGE + IS-LEAD-BYTE + ISO-DATE + KBLABEL + KEYCODE + KEYFUNCTION + KEYLABEL + KEYWORD + KEYWORD-ALL + LAST + LASTKEY + LAST-OF + LC + LDBNAME + LEFT-TRIM + LENGTH + LIBRARY + LINE-COUNTER + LIST-EVENTS + LIST-QUERY-ATTRS + LIST-SET-ATTRS + LIST-WIDGETS + LOCKED + LOG + LOGICAL + LOOKUP + MAXIMUM + MD5-DIGEST + MEMBER + MESSAGE-LINES + MINIMUM + MONTH + MTIME + NEW + NEXT-VALUE + NORMALIZE + NOT ENTERED + NOW + NUM-ALIASES + NUM-DBS + NUM-ENTRIES + NUM-RESULTS + OPSYS + OS-DRIVES + OS-ERROR + OS-GETENV + PAGE-NUMBER + PAGE-SIZE + PDBNAME + PROC-HANDLE + PROC-STATUS + PROGRAM-NAME + PROGRESS + PROMSGS + PROPATH + PROVERSION + QUERY-OFF-END + QUOTER + RANDOM + RAW + RECID + RECORD-LENGTH + REJECTED + REPLACE + RETRY + RETURN-VALUE + RGB-VALUE + RIGHT-TRIM + R-INDEX + ROUND + ROWID + ROW-STATE + SCREEN-LINES + SDBNAME + SEARCH + SEEK + SETUSERID + SHA1-DIGEST + SQRT + SSL-SERVER-NAME + STRING + SUBSTITUTE + SUBSTRING + SUPER + TERMINAL + TIME + TIMEZONE + TODAY + TO-ROWID + TRANSACTION + TRIM + TRUNCATE + USERID + VALID-EVENT + VALID-HANDLE + WEEKDAY + WIDGET-HANDLE + YEAR + + + + IF + THEN + ELSE + WHEN + OTHERWISE + ACCUMULATE + APPLY + ASSIGN + BELL + BUFFER-COMPARE + BUFFER-COPY + CLEAR + CLOSE + QUERY + STORED-PROCEDURE + COLOR + COMPILE + CONNECT + COPY-LOB + CREATE + CLIENT-PRINCIPAL + ALIAS + BROWSE + BUFFER + CALL + DATABASE + DATA-SOURCE + QUERY + SAX-READER + SERVER + SERVER-SOCKET + SOAP-HEADER + SOAP-HEADER-ENTRYREF + SOCKET + TEMP-TABLE + WIDGET + WIDGET-POOL + X-DOCUMENT + X-NODEREF + CURRENT-LANGUAGE + CURRENT-VALUE + DDE + ADVISE + EXECUTE + GET + INITIATE + REQUEST + SEND + TERMINATE + DEF + DEFINE + VAR + VARIABLE + BROWSE + BUFFER + BUTTON + DATA-SOURCE + FRAME + IMAGE + MENU + PARAM + PARAMETER + QUERY + RECTANGLE + STREAM + SUB-MENU + TEMP-TABLE + VARIABLE + WORK-TABLE + WORKFILE + DELETE + ALIAS + OBJECT + WIDGET + WIDGET-POOL + DICTIONARY + DISABLE + TRIGGERS + DISCONNECT + DISPLAY + DOS + DOWN + DYNAMIC-CURRENT-VALUE + EMPTY + TEMP-TABLE + ENABLE + ENTRY + EXPORT + FIND + FORM + FRAME-VALUE + GET + GET-KEY-VALUE + HIDE + IMPORT + INPUT + CLEAR + CLOSE + FROM + TO + THROUGH + INPUT-OUTPUT + THROUGH + INSERT + LEAVE + LENGTH + LOAD + LOAD-PICTURE + MESSAGE + NEXT + NEXT-PROMPT + QUERY + OPEN + OS-APPEND + OS-COMMAND + OS-COPY + OS-CREATE-DIR + OS-DELETE + OS-RENAME + OUTPUT + OVERLAY + PAGE + PAUSE + PROCESS + EVENTS + PROMPT-FOR + PROMSGS + PROPATH + PUBLISH + CURSOR + PUT + PUT-BITS + PUT-BYTE + PUT-BYTES + PUT-DOUBLE + PUT-FLOAT + PUT-KEY-VALUE + PUT-LONG + PUT-SHORT + PUT-STRING + PUT-UNSIGNED-SHORT + QUIT + RAW + RAW-TRANSFER + READKEY + RELEASE + EXTERNAL + OBJECT + REPOSITION + RETURN + RETURNS + RUN + STORED-PROCEDURE + SUPER + SAVE + CACHE + SEEK + SET + SET-BYTE-ORDER + SET-POINTER-VALUE + SET-SIZE + SHOW-STATS + STATUS + STOP + SUBSCRIBE + SUBSTRING + COLOR + SYSTEM-DIALOG + FONT + GET-DIR + GET-FILE + PRINTER-SETUP + SYSTEM-HELP + TERMINAL + TRANSACTION-MODE + AUTOMATIC + TRIGGER + UNDERLINE + UNDO + UNIX + UNLOAD + UNSUBSCRIBE + UP + UPDATE + USE + VALIDATE + VIEW + WAIT-FOR + + + + BROWSE + BUTTON + COMBO-BOX + CONTROL-FRAME + DIALOG-BOX + EDITOR + FIELD-GROUP + FILL-IN + FRAME + IMAGE + LITERAL + MENU + MENU-ITEM + RADIO-SET + RECTANGLE + SELECTION-LIST + SLIDER + SUB-MENU + TEXT + TOGGLE-BOX + WINDOW + + + + THIS-OBJECT + AUDIT-CONTROL + AUDIT-POLICY + SECURITY-POLICY + ACTIVE-WINDOW + BUFFER + BUFFER-FIELD + CALL + CLIPBOARD + CODEBASE-LOCATOR + COLOR-TABLE + COM-SELF + COMPILER + CURRENT-WINDOW + DATA-RELATION + DATE-SOURCE + DEBUGGER + DEFAULT-WINDOW + ERROR-STATUS + FILE-INFO + FIELD + FOCUS + FONT-TABLE + LAST-EVENT + LOG-MANAGER + PRODATASET + QUERY + RCODE-INFO + SAX-ATTRIBUTES + SAX-READER + SELF + SERVER SOCKET + SESSION + SOAP-FAULT + SOAP-FAULT-DETAIL + SOAP-HEADER + SOAP-HEADER-ENTRYREF + SOCKET + SOURCE-PROCEDURE + TARGET-PROCEDURE + TEMP-TABLE + THIS-PROCEDURE + TRANSACTION + WEB-CONTEXT + X-DOCUMENT + X-NODEREF + + + + CONTROL-NAME + CONTROLS + HEIGHT + HONORPROKEYS + HONORRETURNKEY + LEFT + TOP + WIDTH + TAG + + + + ANYWHERE + SCHEMA-MARSHAL + ATTACHED-PAIRLIST + DATA-SOURCE-COMPLETE-MAP + KEYS + NEXT-ROWID + NUM-REFERENCES + RESTART-ROWID + CLASS-TYPE + FIRST-OBJECT + LAST-OBJECT + MULTI-COMPILE + WIDGET-ID + SCHEMA-LOCATION + STANDALONE + STRICT + VERSION + WRITE-STATUS + NONAMESPACE-SCHEMA-LOCATION + FRAGMENT + APPL-CONTEXT-ID + AUDIT-EVENT-CONTEXT + CLIENT-TTY + CLIENT-WORKSTATION + DOMAIN-DESCRIPTION + DOMAIN-NAME + DOMAIN-TYPE + EVENT-GROUP-ID + LOGIN-EXPIRATION-TIMESTAMP + LOGIN-HOST + LOGIN-STATE + ROLES + SEAL-TIMESTAMP + SESSION-ID + STATE-DETAIL + USER-ID + ACCELERATOR + ACTIVE + ACTOR + ADM-DATA + AFTER-BUFFER + AFTER-ROWID + AFTER-TABLE + ALLOW-COLUMN-SEARCHING + ALWAYS-ON-TOP + AMBIGUOUS + APPL-ALERT-BOXES + APPSERVER-INFO + APPSERVER-PASSWORD + APPSERVER-USERID + ASYNCHRONOUS + ASYNC-REQUEST-COUNT + ASYNC-REQUEST-HANDLE + ATTRIBUTE-NAMES + ATTR-SPACE + AUTO-COMPLETION + AUTO-DELETE + AUTO-ENDKEY + AUTO-GO + AUTO-INDENT + AUTO-RESIZE + AUTO-RETURN + AUTO-VALIDATE + AUTO-ZAP + AVAILABLE + AVAILABLE-FORMATS + BACKGROUND + BASE-ADE + BASIC-LOGGING + BATCH-MODE + BEFORE-BUFFER + BEFORE-ROWID + BEFORE-TABLE + BGCOLOR + BLANK + BLOCK-ITERATION-DISPLAY + BORDER-BOTTOM-CHARS + BORDER-BOTTOM-PIXELS + BORDER-LEFT-CHARS + BORDER-LEFT-PIXELS + BORDER-RIGHT-CHARS + BORDER-RIGHT-PIXELS + BORDER-TOP-CHARS + BORDER-TOP-PIXELS + BOX + BOX-SELECTABLE + BUFFER-CHARS + BUFFER-VALUE + BYTES-READ + BYTES-WRITTEN + CACHE + CALL-NAME + CALL-TYPE + BUFFER-FIELD + BUFFER-HANDLE + BUFFER-LINES + BUFFER-NAME + CANCEL-BUTTON + CANCELLED + CAN-CREATE + CAN-DELETE + CAN-READ + CAN-WRITE + CAREFUL-PAINT + CASE-SENSITIVE + CENTERED + CHARSET + CHECKED + CHILD-BUFFER + CHILD-NUM + CLIENT-CONNECTION-ID + CLIENT-TYPE + CODE + CODEPAGE + COLUMN + COLUMN-BGCOLOR + COLUMN-DCOLOR + COLUMN-FGCOLOR + COLUMN-FONT + COLUMN-LABEL + COLUMN-MOVABLE + COLUMN-PFCOLOR + COLUMN-READ-ONLY + COLUMN-RESIZABLE + COLUMN-SCROLLING + COM-HANDLE + COMPLETE + CONTEXT-HELP + CONTEXT-HELP-FILE + CONTEXT-HELP-ID + CONTROL-BOX + CONVERT-D-COLORS + CPCASE + CPCOLL + CPINTERNAL + CPLOG + CPPRINT + CPRCODEIN + CPRCODEOUT + CPSTREAM + CPTERM + CRC-VALUE + CURRENT-CHANGED + CURRENT-COLUMN + CURRENT-ITERATION + CURRENT-RESULT-ROW + CURRENT-ROW-MODIFIED + CURRENT-WINDOW + CURSOR-CHAR + CURSOR-LINE + CURSOR-OFFSET + DATA-ENTRY-RETURN + DATA-SOURCE + DATA-TYPE + DATASET + DATE-FORMAT + DBNAME + DB-REFERENCES + DCOLOR + DDE-ERROR + DDE-ID + DDE-ITEM + DDE-NAME + DDE-TOPIC + DEBLANK + DEBUG-ALERT + DECIMALS + DEFAULT + DEFAULT-BUFFER-HANDLE + DEFAULT-BUTTON + DEFAULT-COMMIT + DELIMITER + DISABLE-AUTO-ZAP + DISPLAY-TIMEZONE + DISPLAY-TYPE + DOWN + DRAG-ENABLED + DROP-TARGET + DYNAMIC + EDGE-CHARS + EDGE-PIXELS + EDIT-CAN-PASTE + EDIT-CAN-UNDO + EMPTY + ENCODING + END-USER-PROMPT + ENTRY-TYPES-LIST + ERROR + ERROR-COLUMN + ERROR-OBJECT-DETAIL + ERROR-ROW + ERROR-STRING + EVENT-PROCEDURE + EVENT-PROCEDURE-CONTEXT + EVENT-TYPE + EXPAND + EXPANDABLE + EXTENT + FGCOLOR + FILE-CREATE-DATE + FILE-CREATE-TIME + FILE-MOD-DATE + FILE-MOD-TIME + FILE-NAME + FILE-OFFSET + FILE-SIZE + FILE-TYPE + FILLED + FILL-MODE + FILL-WHERE-STRING + FIRST-ASYNC-REQUEST + FIRST-BUFFER + FIRST-CHILD + FIRST-COLUMN + FIRST-DATASET + FIRST-DATA-SOURCE + FIRST-PROCEDURE + FIRST-QUERY + FIRST-SERVER + FIRST-SERVER-SOCKET + FIRST-SOCKET + FIRST-TAB-ITEM + FIT-LAST-COLUMN + FLAT-BUTTON + FOCUSED-ROW + FOCUSED-ROW-SELECTED + FONT + FOREGROUND + FORMAT + FORWARD-ONLY + FRAME + FRAME-COL + FRAME-NAME + FRAME-ROW + FRAME-SPACING + FRAME-X + FRAME-Y + FREQUENCY + FULL-HEIGHT-CHARS + FULL-HEIGHT-PIXELS + FULL-PATHNAME + FULL-WIDTH-CHARS + FULL-WIDTH-PIXELS + GRAPHIC-EDGE + GRID-FACTOR-HORIZONTAL + GRID-FACTOR-VERTICAL + GRID-SNAP + GRID-UNIT-HEIGHT-CHARS + GRID-UNIT-HEIGHT-PIXELS + GRID-UNIT-WIDTH-CHARS + GRID-UNIT-WIDTH-PIXELS + GRID-VISIBLE + HANDLER + HAS-LOBS + HAS-RECORDS + HEIGHT-CHARS + HEIGHT-PIXELS + HELP + HIDDEN + HORIZONTAL + HTML-CHARSET + HWND + ICFPARAMETER + ICON + IGNORE-CURRENT-MODIFIED + IMAGE + IMAGE-DOWN + IMAGE-INSENSITIVE + IMAGE-UP + IMMEDIATE-DISPLAY + INDEX + INDEX-INFORMATION + INIT + INITIAL + INNER-CHARS + INNER-LINES + INPUT-VALUE + INSTANTIATING-PROCEDURE + INTERNAL-ENTRIES + IN-HANDLE + IS-OPEN + IS-PARAMETER-SET + ITEMS-PER-ROW + KEEP-CONNECTION-OPEN + KEEP-FRAME-Z-ORDER + KEEP-SECURITY-CACHE + KEY + LABEL + LABEL-BGCOLOR + LABEL-DCOLOR + LABEL-FGCOLOR + LABEL-FONT + LABELS + LANGUAGES + LARGE + LARGE-TO-SMALL + LAST-ASYNC-REQUEST + LAST-CHILD + LAST-PROCEDURE + LAST-SERVER + LAST-SERVER-SOCKET + LAST-SOCKET + LAST-TAB-ITEM + LENGTH + LINE + LIST-ITEM-PAIRS + LIST-ITEMS + LITERAL-QUESTION + LOCAL-HOST + LOCAL-NAME + LOCAL-PORT + LOCATOR-COLUMN-NUMBER + LOCATOR-LINE-NUMBER + LOCATOR-PUBLIC-ID + LOCATOR-SYSTEM-ID + LOCATOR-TYPE + LOCKED + LOG-ENTRY-TYPES + LOG-THRESHOLD + LOGFILE-NAME + LOGGING-LEVEL + MANDATORY + MANUAL-HIGHLIGHT + MAX-BUTTON + MAX-CHARS + MAX-DATA-GUESS + MAX-HEIGHT-CHARS + MAX-HEIGHT-PIXELS + MAX-VALUE + MAX-WIDTH-CHARS + MAX-WIDTH-PIXELS + MD-VALUE + MENU-BAR + MENU-KEY + MENU-MOUSE + MESSAGE-AREA + MESSAGE-AREA-FONT + MIN-BUTTON + MIN-COLUMN-WIDTH-CHARS + MIN-COLUMN-WIDTH-PIXELS + MIN-HEIGHT-CHARS + MIN-HEIGHT-PIXELS + MIN-SCHEMA-MARSHAL + MIN-VALUE + MIN-WIDTH-CHARS + MIN-WIDTH-PIXELS + MODIFIED + MOUSE-POINTER + MOVABLE + MULTIPLE + MULTITASKING-INTERVAL + MUST-UNDERSTAND + NAMESPACE-PREFIX + NAMESPACE-URI + NEEDS-APPSERVER-PROMPT + NEEDS-PROMPT + NEW + NEW-ROW + NEXT-COLUMN + NEXT-SIBLING + NEXT-TAB-ITEM + NO-CURRENT-VALUE + NO-EMPTY-SPACE + NO-FOCUS + NO-SCHEMA-MARSHAL + NO-VALIDATE + NODE-VALUE + NUM-BUFFERS + NUM-BUTTONS + NUM-CHILD-RELATIONS + NUM-CHILDREN + NUM-COLUMNS + NUM-DROPPED-FILES + NUM-ENTRIES + NUM-FIELDS + NUM-FORMATS + NUM-HEADER-ENTRIES + NUM-ITEMS + NUM-ITERATIONS + NUM-LINES + NUM-LOCKED-COLUMNS + NUM-LOG-FILES + NUM-MESSAGES + NUM-PARAMETERS + NUM-RELATIONS + NUM-REPLACED + NUM-RESULTS + NUM-SELECTED-ROWS + NUM-SELECTED-WIDGETS + NUM-SOURCE-BUFFERS + NUM-TABS + NUM-TO-RETAIN + NUM-TOP-BUFFERS + NUM-VISIBLE-COLUMNS + NUMERIC-DECIMAL-POINT + NUMERIC-FORMAT + NUMERIC-SEPARATOR + ON-FRAME-BORDER + ORIGIN-HANDLE + ORIGIN-ROWID + OVERLAY + OWNER + OWNER-DOCUMENT + PAGE-BOTTOM + PAGE-TOP + PARAMETER + PARENT + PARENT-BUFFER + PARENT-RELATION + PARSE-STATUS + PASSWORD-FIELD + PATHNAME + PERSISTENT + PERSISTENT-CACHE-DISABLED + PERSISTENT-PROCEDURE + PFCOLOR + PIXELS-PER-COLUMN + PIXELS-PER-ROW + POPUP-MENU + POPUP-ONLY + POSITION + PREPARED + PREPARE-STRING + PREV-COLUMN + PREV-SIBLING + PREV-TAB-ITEM + PRIMARY + PRINTER-CONTROL-HANDLE + PRINTER-HDC + PRINTER-NAME + PRINTER-PORT + PRIVATE-DATA + PROCEDURE-NAME + PROGRESS-SOURCE + PROXY + PROXY-PASSWORD + PROXY-USERID + PUBLIC-ID + PUBLISHED-EVENTS + QUERY + QUERY-OFF-END + QUIT + RADIO-BUTTONS + READ-ONLY + RECID + RECORD-LENGTH + REFRESHABLE + REJECTED + RELATION-FIELDS + RELATIONS-ACTIVE + REMOTE + REMOTE-HOST + REMOTE-PORT + REPOSITION + RESIZABLE + RESIZE + RETAIN-SHAPE + RETURN-INSERTED + RETURN-VALUE + RETURN-VALUE-DATA-TYPE + ROW + COL + ROW-HEIGHT-CHARS + ROW-HEIGHT-PIXELS + ROW-STATE + ROWID + ROW-MARKERS + ROW-RESIZABLE + SAVE-WHERE-STRING + SCHEMA-CHANGE + SCHEMA-PATH + SCREEN-LINES + SCREEN-VALUE + SCROLL-BARS + SCROLLABLE + SCROLLBAR-HORIZONTAL + SCROLLBAR-VERTICAL + SELECTABLE + SELECTED + SELECTION-END + SELECTION-START + SELECTION-TEXT + SENSITIVE + SEPARATORS + SEPARATOR-FGCOLOR + SERVER + SERVER-CONNECTION-BOUND + SERVER-CONNECTION-BOUND-REQUEST + SERVER-CONNECTION-CONTEXT + SERVER-CONNECTION-ID + SERVER-OPERATING-MODE + SHOW-IN-TASKBAR + SIDE-LABEL-HANDLE + SIDE-LABELS + SKIP-DELETED-RECORD + SMALL-ICON + SMALL-TITLE + SOAP-FAULT-ACTOR + SOAP-FAULT-CODE + SOAP-FAULT-DETAIL + SOAP-FAULT-STRING + SORT + STARTUP-PARAMETERS + STATUS-AREA + STATUS-AREA-FONT + STOP + STOPPED + STREAM + STRETCH-TO-FIT + STRING-VALUE + SUBTYPE + SUPER-PROCEDURES + SUPPRESS-NAMESPACE-PROCESSING + SUPPRESS-WARNINGS + SYSTEM-ALERT-BOXES + SYSTEM-ID + TAB-POSITION + TAB-STOP + TABLE + TABLE-CRC-LIST + TABLE-HANDLE + TABLE-LIST + TABLE-NUMBER + TEMP-DIRECTORY + TEXT-SELECTED + THREE-D + TIC-MARKS + TIME-SOURCE + TITLE + TITLE-BGCOLOR + TITLE-DCOLOR + TITLE-FGCOLOR + TITLE-FONT + TOGGLE-BOX + TOOLTIP + TOOLTIPS + TOP-ONLY + TRACKING-CHANGES + TRANSACTION + TRANSPARENT + TRANS-INIT-PROCEDURE + TYPE + UNDO + UNIQUE-ID + UNIQUE-MATCH + URL + URL-PASSWORD + URL-USERID + DISPLAY + VALIDATE-EXPRESSION + VALIDATE-MESSAGE + VALIDATION-ENABLED + VALUE + VIEW-FIRST-COLUMN-ON-REOPEN + VIRTUAL-HEIGHT + VIRTUAL-WIDTH + VIRTUAL-HEIGHT-CHARS + VIRTUAL-HEIGHT-PIXELS + VIRTUAL-WIDTH-CHARS + VIRTUAL-WIDTH-PIXELS + VISIBLE + WARNING + WHERE-STRING + WIDGET-ENTER + WIDGET-LEAVE + WIDTH-CHARS + WIDTH-PIXELS + WINDOW + WINDOW-STATE + WINDOW-SYSTEM + WORD-WRAP + WORK-AREA-HEIGHT-PIXELS + WORK-AREA-WIDTH-PIXELS + WORK-AREA-X + WORK-AREA-Y + X + XML-SCHEMA-PATH + XML-SUPPRESS-NAMESPACE-PROCESSING + Y + YEAR-OFFSET + WRITE-XMLSCHEMA + READ-XMLSCHEMA + WRITE-XML + READ-XML + NESTED + XML-DATA-TYPE + XML-NODE-TYPE + FORMATTED + SET-OUTPUT-DESTINATION + START-DOCUMENT + START-ELEMENT + WRITE-CHARACTERS + END-ELEMENT + END-DOCUMENT + WRITE-DATA-ELEMENT + INSERT-ATTRIBUTE + + + + COPY-DATASET + COPY-TEMP-TABLE + GET-CALLBACK-PROC-CONTEXT + GET-CALLBACK-PROC-NAME + GET-BINARY-DATA + ADD-SCHEMA-LOCATION + DECLARE-NAMESPACE + RESET + WRITE-CDATA + WRITE-COMMENT + WRITE-EMPTY-ELEMENT + WRITE-ENTITY-REF + WRITE-EXTERNAL-DTD + WRITE-FRAGMENT + WRITE-PROCESSING-INSTRUCTION + CLEAR-LOG + CLOSE-LOG + WRITE-MESSAGE + AUTHENTICATION-FAILED + BEGIN-EVENT-GROUP + CLEAR-APPL-CONTEXT + ENCRYPT-AUDIT-MAC-KEY + END-EVENT-GROUP + EXPORT-PRINCIPAL + GET-PROPERTY + IMPORT-PRINCIPAL + LIST-PROPERTY-NAMES + LOAD-DOMAINS + LOCK-REGISTRATION + LOG-AUDIT-EVENT + LOGOUT + REFRESH-AUDIT-POLICY + REGISTER-DOMAIN + SEAL + SET-APPL-CONTEXT + SET-CLIENT + SET-PROPERTY + VALIDATE-SEAL + ACCEPT-CHANGES + ACCEPT-ROW-CHANGES + ADD-BUFFER + ADD-CALC-COLUMN + ADD-COLUMNS-FROM + ADD-EVENTS-PROCEDURE + ADD-FIELDS-FROM + ADD-FIRST + ADD-HEADER-ENTRY + ADD-INDEX-FIELD + ADD-LAST + ADD-LIKE-COLUMN + ADD-LIKE-FIELD + ADD-LIKE-INDEX + ADD-NEW-FIELD + ADD-NEW-INDEX + ADD-RELATION + ADD-SOURCE-BUFFER + ADD-SUPER-PROCEDURE + APPEND-CHILD + APPLY-CALLBACK + ATTACH-DATA-SOURCE + BUFFER-COMPARE + BUFFER-COPY + BUFFER-CREATE + BUFFER-DELETE + BUFFER-FIELD + BUFFER-RELEASE + BUFFER-VALIDATE + CANCEL-BREAK + CANCEL-REQUESTS + CLEAR + CLEAR-SELECTION + CLONE-NODE + CONNECT + CONNECTED + CONVERT-TO-OFFSET + CREATE-LIKE + CREATE-NODE + CREATE-NODE-NAMESPACE + CREATE-RESULT-LIST-ENTRY + DEBUG + DELETE + DELETE-CHAR + DELETE-CURRENT-ROW + DELETE-HEADER-ENTRY + DELETE-LINE + DELETE-NODE + DELETE-RESULT-LIST-ENTRY + DELETE-SELECTED-ROW + DELETE-SELECTED-ROWS + DESELECT-FOCUSED-ROW + DESELECT-ROWS + DESELECT-SELECTED-ROW + DETACH-DATA-SOURCE + DISABLE + DISABLE-CONNECTIONS + DISABLE-DUMP-TRIGGERS + DISABLE-LOAD-TRIGGERS + DISCONNECT + DISPLAY-MESSAGE + DUMP-LOGGING-NOW + EDIT-CLEAR + EDIT-COPY + EDIT-CUT + EDIT-PASTE + EDIT-UNDO + EMPTY-DATASET + EMPTY-TEMP-TABLE + ENABLE + ENABLE-CONNECTIONS + ENABLE-EVENTS + END-FILE-DROP + ENTRY + EXPORT + FETCH-SELECTED-ROW + FILL + FIND-BY-ROWID + FIND-CURRENT + FIND-FIRST + FIND-LAST + FIND-UNIQUE + GET-ATTRIBUTE + GET-ATTRIBUTE-NODE + GET-BLUE-VALUE + GET-BROWSE-COLUMN + GET-BUFFER-HANDLE + GET-BYTES-AVAILABLE + GET-CHANGES + GET-CHILD + GET-CHILD-RELATION + GET-CURRENT + GET-DATASET-BUFFER + GET-DOCUMENT-ELEMENT + GET-DROPPED-FILE + GET-DYNAMIC + GET-FIRST + GET-GREEN-VALUE + GET-HEADER-ENTRY + GET-INDEX-BY-NAMESPACE-NAME + GET-INDEX-BY-QNAME + GET-ITERATION + GET-LAST + GET-LOCALNAME-BY-INDEX + GET-MESSAGE + GET-NEXT + GET-NODE + GET-NUMBER + GET-PARENT + GET-PREV + GET-PRINTERS + GET-QNAME-BY-INDEX + GET-RED-VALUE + GET-RELATION + GET-REPOSITIONED-ROW + GET-RGB-VALUE + GET-SELECTED-WIDGET + GET-SERIALIZED + GET-SIGNATURE + GET-SOCKET-OPTION + GET-SOURCE-BUFFER + GET-TAB-ITEM + GET-TEXT-HEIGHT-CHARS + GET-TEXT-HEIGHT-PIXELS + GET-TEXT-WIDTH-CHARS + GET-TEXT-WIDTH-PIXELS + GET-TOP-BUFFER + GET-TYPE-BY-INDEX + GET-TYPE-BY-NAMESPACE-NAME + GET-TYPE-BY-QNAME + GET-URI-BY-INDEX + GET-VALUE-BY-INDEX + GET-VALUE-BY-NAMESPACE-NAME + GET-VALUE-BY-QNAME + GET-WAIT-STATE + IMPORT-NODE + INDEX-INFORMATION + INITIALIZE-DOCUMENT-TYPE + INITIATE + INSERT + INSERT-BACKTAB + INSERT-BEFORE + INSERT-FILE + INSERT-ROW + INSERT-STRING + INSERT-TAB + INVOKE + IS-ROW-SELECTED + IS-SELECTED + LOAD + LoadControls + LOAD-ICON + LOAD-IMAGE + LOAD-IMAGE-DOWN + LOAD-IMAGE-INSENSITIVE + LOAD-IMAGE-UP + LOAD-MOUSE-POINTER + LOAD-SMALL-ICON + LONGCHAR-TO-NODE-VALUE + LOOKUP + MEMPTR-TO-NODE-VALUE + MERGE-CHANGES + MERGE-ROW-CHANGES + MOVE-AFTER-TAB-ITEM + MOVE-BEFORE-TAB-ITEM + MOVE-COLUMN + MOVE-TO-BOTTOM + MOVE-TO-EOF + MOVE-TO-TOP + NODE-VALUE-TO-LONGCHAR + NODE-VALUE-TO-MEMPTR + NORMALIZE + QUERY-CLOSE + QUERY-OPEN + QUERY-PREPARE + RAW-TRANSFER + READ + READ-FILE + REFRESH + REJECT-CHANGES + REJECT-ROW-CHANGES + REMOVE-ATTRIBUTE + REMOVE-CHILD + REMOVE-EVENTS-PROCEDURE + REMOVE-SUPER-PROCEDURE + REPLACE + REPLACE-CHILD + REPLACE-SELECTION-TEXT + REPOSITION-BACKWARD + REPOSITION-FORWARD + REPOSITION-TO-ROW + REPOSITION-TO-ROWID + SAVE + SAVE-FILE + SAVE-ROW-CHANGES + SAX-PARSE + SAX-PARSE-FIRST + SAX-PARSE-NEXT + SCROLL-TO-CURRENT-ROW + SCROLL-TO-ITEM + SCROLL-TO-SELECTED-ROW + SEARCH + SELECT-ALL + SELECT-FOCUSED-ROW + SELECT-NEXT-ROW + SELECT-PREV-ROW + SELECT-ROW + SET-ACTOR + SET-ATTRIBUTE + SET-ATTRIBUTE-NODE + SET-BLUE-VALUE + SET-BREAK + SET-BUFFERS + SET-CALLBACK-PROCEDURE + SET-COMMIT + SET-CONNECT-PROCEDURE + SET-DYNAMIC + SET-GREEN-VALUE + SET-INPUT-SOURCE + SET-MUST-UNDERSTAND + SET-NODE + SET-NUMERIC-FORMAT + SET-PARAMETER + SET-READ-RESPONSE-PROCEDURE + SET-RED-VALUE + SET-REPOSITIONED-ROW + SET-RGB-VALUE + SET-ROLLBACK + SET-SELECTION + SET-SERIALIZED + SET-SOCKET-OPTION + SET-WAIT-STATE + STOP-PARSING + SYNCHRONIZE + TEMP-TABLE-PREPARE + VALIDATE + WRITE + MAX-HEIGHT + MAX-WIDTH + + + + CLASSPATH + DLC + EVTLEVEL + JDKCP + JDKHOME + JFCCP + JFHOME + JIT + JRECP + JREHOME + JVMEXE + PATH + PROCFG + PROCONV + PROEXE + PROGRESSCP + PROLOAD + PROMSGS + PROPATH + PROSRV + PROSTARTUP + PROTERMCAP + TERM + TERMINAL + GATEWAY_INTERFACE + SERVER_SOFTWARE + SERVER_PROTOCOL + SERVER_NAME + SERVER_PORT + REQUEST_METHOD + SCRIPT_NAME + PATH_INFO + PATH_TRANSLATED + QUERY_STRING + REMOTE_ADDR + REMOTE_IDENT + REMOTE_USER + AUTH_TYPE + REMOTE_HOST + CONTENT_TYPE + CONTENT_LENGTH + HTTP_ACCEPT + HTTP_COOKIE + HTTP_REFERER + HTTP_USER_AGENT + HTTP_REFERER + HTTPS + HTTP_REFERER + HOSTURL + SELFURL + APPPROGRAM + APPURL + APPPROGRAM + OUTPUT-CONTENT-TYPE + WEB_SRC_PATH + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/prolog.xml b/kate/part/syntax/data/prolog.xml new file mode 100644 index 00000000..41da9ce7 --- /dev/null +++ b/kate/part/syntax/data/prolog.xml @@ -0,0 +1,1035 @@ + + + + + + + + + + + + error + + + instantiation_error + uninstantiation_error + type_error + domain_error + existence_error + permission_error + representation_error + evaluation_error + resource_error + syntax_error + system_error + + + + + char_conversion + current_char_conversion + include + ensure_loaded + + atan + + xor + + + + consult + built_in + + + + + + + + + + + + initialization + + fail + repeat + call + catch + throw + true + false + once + + + + + dynamic + asserta + assertz + retractall + retract + abolish + clause + + + + + + listing + + + + + atom_concat + atom_length + atom_chars + atom_codes + arg + subsumes_term + acyclic_term + char_code + compare + copy_term + functor + number_chars + number_codes + + term_variables + unify_with_occurs_check + + + + number_atom + expand_term + term_expansion + + + display + print + format + portray_clause + portray + term_expansion + + + phrase + + + sort + keysort + + + append + delete + length + last + map_list + min_list + maplist + msort + memberchk + member + nth + permutation + reverse + select + prefix + suffix + sublist + sum_list + + + open + + set_stream_position + get_char + get_code + peek_char + peek_code + get_byte + peek_byte + + put_char + put_code + put_byte + nl + + read_term + read + write_canonical + writeq + write + + + append + seeing + seen + see + telling + tell + told + get0 + get + skip + put + tab + + + is + + + rem + mod + div + + + + + abs + sign + min + max + + + + + ceiling + floor + round + truncate + + + + pi + + + sqrt + tan + cos + sin + atan2 + acos + asin + exp + log + float + float_fractional_part + float_integer_part + + + + + multifile + discontigous + op + set_prolog_flag + + + var + nonvar + atom + integer + float + number + atomic + compound + callable + ground + + + + list + is_list + + + + + current_op + current_prolog_flag + current_input + current_output + + + set_input + set_output + + + close + + flush_output + + + at_end_of_stream + + stream_property + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/protobuf.xml b/kate/part/syntax/data/protobuf.xml new file mode 100644 index 00000000..19e7d978 --- /dev/null +++ b/kate/part/syntax/data/protobuf.xml @@ -0,0 +1,121 @@ + + + + + + message + required + optional + repeated + packed + enum + default + import + public + extensions + package + option + deprecated + extend + + + = + ; + + + double + float + int32 + int64 + uint32 + uint64 + sint32 + sint64 + fixed32 + fixed64 + sfixed32 + sfixed64 + bool + string + bytes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/puppet.xml b/kate/part/syntax/data/puppet.xml new file mode 100644 index 00000000..315906e8 --- /dev/null +++ b/kate/part/syntax/data/puppet.xml @@ -0,0 +1,711 @@ + + + + +]> + + + + + + + import + false + true + undef + + + + in + and + or + + + + and + or + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/purebasic.xml b/kate/part/syntax/data/purebasic.xml new file mode 100644 index 00000000..b38daa56 --- /dev/null +++ b/kate/part/syntax/data/purebasic.xml @@ -0,0 +1,918 @@ + + + + + + For + ForEach + To + Step + Next + Break + Continue + If + Else + ElseIf + EndIf + Debug + Repeat + Until + Select + Case + Default + EndSelect + While + Wend + End + Structure + EndStructure + Interface + EndInterface + Extends + DefType + Dim + Enumeration + EndEnumeration + Global + Procedure + EndProcedure + ProcedureReturn + Shared + Protected + Static + Declare + DataSection + EndDataSection + Data + Restore + Read + IncludeFile + XIncludeFile + IncludeBinary + IncludePath + CompilerIf + CompilerElse + CompilerEndIf + CompilerSelect + CompilerCase + CompilerDefault + CompilerEndSelect + ProcedureDLL + NewList + Goto + Gosub + Return + FakeReturn + + + Abs + ACos + ActivateGadget + ActivateRichEdit + ActivateWindow + Add3DArchive + AddBillboard + AddDate + AddElement + AddGadgetColumn + AddGadgetItem + AddKeyboardShortcut + AddMaterialLayer + AddPackFile + AddPackMemory + AddStatusBarField + AddSysTrayIcon + AdvancedGadgetEvents + AllocateMemory + AmbientColor + AnimateEntity + Asc + ASin + ATan + AvailableScreenMemory + BackColor + Base64Encoder + BillboardGroupLocate + BillboardGroupMaterial + BillboardGroupX + BillboardGroupY + BillboardGroupZ + BillboardHeight + BillboardLocate + BillboardWidth + BillboardX + BillboardY + BillboardZ + Bin + Blue + Box + ButtonGadget + ButtonImageGadget + CallCFunctionFast + CallCFunction + CallCOM + CallDX + CallFunctionFast + CallFunction + CameraBackColor + CameraFOV + CameraLocate + CameraLookAt + CameraProjection + CameraRange + CameraRenderMode + CameraX + CameraY + CameraZ + CatchImage + CatchSound + CatchSprite + CDAudioLength + CDAudioName + CDAudioStatus + CDAudioTrackLength + CDAudioTrackSeconds + CDAudioTracks + ChangeAlphaIntensity + ChangeAlphaIntensity + ChangeCurrentElement + ChangeGamma + ChangeListIconGadgetDisplay + ChangeRichEditOptions + ChangeSysTrayIcon + CheckBoxGadget + Chr + Circle + ClearBillboards + ClearClipboard + ClearConsole + ClearError + ClearGadgetItemList + ClearList + ClearScreen + ClipSprite + CloseConsole + CloseDatabase + CloseFile + CloseFont + CloseGadgetList + CloseHelp + CloseLibrary + CloseNetworkConnection + CloseNetworkServer + ClosePack + ClosePreferences + CloseRichEdit + CloseScreen + CloseSubMenu + CloseTreeGadgetNode + CloseWindow + ColorRequester + ComboBoxGadget + CompareMemory + CompareMemoryString + ConsoleColor + ConsoleCursor + ConsoleLocate + ConsoleTitle + ContainerGadget + CopyDirectory + CopyEntity + CopyFile + CopyImage + CopyLight + CopyMaterial + CopyMemory + CopyMemoryString + CopyMesh + CopySprite + CopyTexture + Cos + CountBillboards + CountGadgetItems + CountLibraryFunctions + CountList + CountMaterialLayers + CountRenderedTriangles + CountString + CountTreeGadgetNodeItems + CRC32Fingerprint + CreateBillboardGroup + CreateCamera + CreateDirectory + CreateEntity + CreateFile + CreateGadgetList + CreateImage + CreateLight + CreateMaterial + CreateMenu + CreateMesh + CreateNetworkServer + CreatePack + CreatePalette + CreateParticleEmitter + CreatePopupMenu + CreatePreferences + CreateSprite3D + CreateSprite + CreateStatusBar + CreateTerrain + CreateTexture + CreateThread + CreateToolBar + DatabaseColumnName + DatabaseColumns + DatabaseColumnType + DatabaseDriverDescription + DatabaseDriverName + DatabaseError + DatabaseQuery + DatabaseUpdate + Date + Day + DayOfWeek + DayOfYear + DefaultPrinter + Delay + DeleteDirectory + DeleteElement + DeleteFile + DESFingerprint + DetachMenu + DirectoryEntryAttributes + DirectoryEntryAttributes + DirectoryEntryName + DirectoryEntrySize + DisableGadget + DisableMaterialLighting + DisableMenuItem + DisableToolBarButton + DisASMCommand + DisplayAlphaSprite + DisplayAlphaSprite + DisplayPalette + DisplayPopupMenu + DisplayRGBFilter + DisplayShadowSprite + DisplayShadowSprite + DisplaySolidSprite + DisplaySprite3D + DisplaySprite + DisplayTranslucideSprite + DisplayTransparentSprite + DrawImage + DrawingBuffer + DrawingBufferPitch + DrawingBufferPixelFormat + DrawingFont + DrawingMode + DrawText + EditorGadget + EjectCDAudio + ElapsedMilliseconds + Ellipse + EndTimer + Engine3DFrameRate + EntityAnimationLength + EntityLocate + EntityMaterial + EntityMesh + EntityX + EntityY + EntityZ + Eof + EventGadgetID + EventlParam + EventMenuID + EventType + EventWindowID + EventwParam + ExamineDatabaseDrivers + ExamineDirectory + ExamineIPAddresses + ExamineJoystick + ExamineKeyboard + ExamineLibraryFunctions + ExamineMouse + ExamineScreenModes + ExplorerComboGadget + ExplorerListGadget + ExplorerTreeGadget + FileSeek + FileSize + FillArea + FindString + FindText + FirstDatabaseRow + FirstElement + FlipBuffers + Fog + FontDialog + FontID + FontRequester + FormatDate + Frame3DGadget + FreeBillboardGroup + FreeCamera + FreeEntity + FreeGadget + FreeImage + FreeLight + FreeMaterial + FreeMemory + FreeMenu + FreeMesh + FreeModule + FreeMovie + FreePalette + FreeParticleEmitter + FreeSound + FreeSprite3D + FreeSprite + FreeStatusBar + FreeTexture + FreeToolBar + FrontColor + GadgetHeight + GadgetID + GadgetItemID + GadgetToolTip + GadgetWidth + GadgetX + GadgetY + GetClipboardData + GetClipboardText + GetCurrentEIP + GetDatabaseFloat + GetDatabaseLong + GetDatabaseString + GetDisASMString + GetEntityAnimationTime + GetErrorAddress + GetErrorCounter + GetErrorDescription + GetErrorDLL + GetErrorLineNR + GetErrorModuleName + GetErrorNumber + GetErrorRegister + GetExtensionPart + GetFilePart + GetGadgetAttribute + GetGadgetItemAttribute + GetGadgetItemState + GetGadgetItemText + GetGadgetState + GetGadgetText + GetMaxTimerResolution + GetMenuItemState + GetMinTimerResolution + GetModulePosition + GetModuleRow + GetPaletteColor + GetPathPart + GetRichEditStyle + GetRichEditText + GetSelectedText + GetWindowTitle + GoToEIP + GrabImage + GrabSprite + Green + Hex + HideBillboardGroup + HideEntity + HideGadget + HideLight + HideMenu + HideParticleEmitter + HideWindow + Hostname + Hour + HyperLinkGadget + ImageDepth + ImageGadget + ImageHeight + ImageID + ImageOutput + ImageWidth + InitCDAudio + InitDatabase + InitEngine3D + InitJoystick + InitKeyboard + InitModule + InitMouse + InitMovie + InitNetwork + InitPalette + InitSound + InitSprite3D + InitSprite + Inkey + Input + InputRequester + InsertElement + Int + IPAddressField + IPAddressGadget + IPString + IPString + IsDatabase + IsDirectory + IsFile + IsFilename + IsFont + IsFunctionEntry + IsFunction + IsGadget + IsImage + IsLibrary + IsMenu + IsModule + IsMovie + IsPalette + IsScreenActive + IsSprite3D + IsSprite + IsStatusBar + IsSysTrayIcon + IsToolBar + IsWindow + JoystickAxisX + JoystickAxisY + JoystickButton + KeyboardInkey + KeyboardMode + KeyboardPushed + KeyboardReleased + KillThread + LastElement + LCase + Left + Len + LibraryFunctionAddress + LibraryFunctionName + LightColor + LightLocate + LightSpecularColor + Line + LineXY + ListIconGadget + ListIndex + ListViewGadget + LoadFont + LoadImage + LoadMesh + LoadModule + LoadMovie + LoadPalette + LoadSound + LoadSprite + LoadTexture + LoadWorld + Locate + Loc + Lof + Log10 + Log + LSet + LTrim + MakeIPAddress + MakeIPAddress + MaterialAmbientColor + MaterialBlendingMode + MaterialDiffuseColor + MaterialFilteringMode + MaterialID + MaterialShadingMode + MaterialSpecularColor + MD5FileFingerprint + MD5Fingerprint + MDIGadget + MemoryStringLength + MenuBar + MenuHeight + MenuID + MenuItem + MenuTitle + MeshID + MessageRequester + Mid + Minute + ModuleVolume + Month + MouseButton + MouseDeltaX + MouseDeltaY + MouseLocate + MouseWheel + MouseX + MouseY + MoveBillboardGroup + MoveBillboard + MoveCamera + MoveEntity + MoveLight + MoveParticleEmitter + MoveWindow + MovieAudio + MovieHeight + MovieInfo + MovieLength + MovieSeek + MovieStatus + MovieWidth + NetworkClientEvent + NetworkClientID + NetworkServerEvent + NewPrinterPage + NextDatabaseDriver + NextDatabaseRow + NextDirectoryEntry + NextElement + NextIPAddress + NextLibraryFunction + NextPackFile + NextScreenMode + NextSelectedFileName + OffsetOf + OnErrorExit + OnErrorGosub + OnErrorGoto + OnErrorResume + OpenComPort + OpenConsole + OpenDatabase + OpenDatabaseRequester + OpenFile + OpenFileRequester + OpenGadgetList + OpenHelp + OpenLibrary + OpenNetworkConnection + OpenPack + OpenPreferences + OpenRichEdit + OpenScreen + OpenSubMenu + OpenTreeGadgetNode + OpenWindowedScreen + OpenWindow + OptionGadget + OSVersion + PackerCallback + PackFileSize + PackMemory + PanelGadget + ParseDate + ParticleColorFader + ParticleColorRange + ParticleEmissionRate + ParticleEmitterLocate + ParticleEmitterX + ParticleEmitterY + ParticleEmitterZ + ParticleMaterial + ParticleSize + ParticleTimeToLive + ParticleVelocity + PathRequester + PauseCDAudio + PauseMovie + PauseThread + PeekB + PeekF + PeekL + PeekS + PeekW + PlayCDAudio + PlayModule + PlayMovie + PlaySound + Plot + Point + PokeB + PokeF + PokeL + PokeS + PokeW + Pow + PreferenceComment + PreferenceGroup + PreviousDatabaseRow + PreviousElement + PrinterOutput + PrinterPageHeight + PrinterPageWidth + Print + PrintN + PrintRequester + ProgramParameter + ProgressBarGadget + Random + RandomSeed + ReadByte + ReadData + ReadFile + ReadFloat + ReadLong + ReadPreferenceFloat + ReadPreferenceLong + ReadPreferenceString + ReadString + ReadWord + ReAllocateMemory + ReceiveNetworkData + ReceiveNetworkFile + Red + ReleaseMouse + RemoveBillboard + RemoveGadgetColumn + RemoveGadgetItem + RemoveKeyboardShortcut + RemoveMaterialLayer + RemoveString + RemoveSysTrayIcon + RenameFile + RenderMovieFrame + RenderWorld + ReplaceString + ReplaceText + ResetList + ResizeBillboard + ResizeEntity + ResizeGadget + ResizeImage + ResizeMovie + ResizeParticleEmitter + ResizeRichEdit + ResizeWindow + ResumeCDAudio + ResumeMovie + ResumeThread + RGB + RichEditBackgroundColor + RichEditBackground + RichEditFontFace + RichEditFont + RichEditFontSize + RichEditHeight + RichEditID + RichEditIndex + RichEditLocate + RichEditMouseX + RichEditMouseY + RichEditOptions + RichEditParent + RichEditTextColor + RichEditWidth + RichEditX + RichEditY + Right + RotateBillboardGroup + RotateCamera + RotateEntity + RotateMaterial + RotateParticleEmitter + RotateSprite3D + Round + RSet + RTrim + RunProgram + SaveFileRequester + SaveImage + SaveSprite + ScaleEntity + ScaleMaterial + ScreenID + ScreenModeDepth + ScreenModeHeight + ScreenModeRefreshRate + ScreenModeWidth + ScreenOutput + ScrollAreaGadget + ScrollBarGadget + ScrollMaterial + Second + SelectedFilePattern + SelectedFontColor + SelectedFontName + SelectedFontSize + SelectedFontStyle + SelectedRange + SelectElement + SelectText + SendNetworkData + SendNetworkFile + SendNetworkString + SetClipboardData + SetClipboardText + SetEntityAnimationTime + SetErrorNumber + SetFrameRate + SetGadgetAttribute + SetGadgetFont + SetGadgetItemAttribute + SetGadgetItemState + SetGadgetItemText + SetGadgetState + SetGadgetText + Set/GetWindowTitle + SetMenuItemState + SetMeshData + SetModulePosition + SetPaletteColor + SetRefreshRate + SetRichEditCallback + SetRichEditText + SetWindowCallback + SetWindowTitle + Sin + SizeOf + SkyBox + SkyDome + SortArray + SortList + SoundFrequency + SoundPan + SoundVolume + Space + SpinGadget + SplitterGadget + Sprite3DBlendingMode + Sprite3DQuality + SpriteCollision + SpriteDepth + SpriteHeight + SpriteOutput + SpritePixelCollision + SpriteWidth + Sqr + Start3D + StartDrawing + StartPrinting + StartSpecialFX + StartTimer + StatusBarIcon + StatusBarText + Stop3D + StopCDAudio + StopDrawing + StopModule + StopMovie + StopPrinting + StopSound + StopSpecialFX + StreamFileIn + StreamFileOut + StrF + StringField + StringGadget + Str + StrU + SysTrayIconToolTip + Tan + TerrainHeight + TextGadget + TextLength + TextureHeight + TextureID + TextureOutput + TextureWidth + ThreadPriority + ToolBarImageButton + ToolBarSeparator + ToolBarStandardButton + ToolBarToolTip + TrackBarGadget + TransformSprite3D + TransparentSpriteColor + TreeGadget + TreeGadgetItemNumber + Trim + UCase + UnpackMemory + UseBuffer + UseCDAudio + UseDatabase + UseDirectory + UseFile + UseFont + UseGadgetList + UseImage + UseJPEGImageDecoder + UseJPEGImageEncoder + UseMovie + UseOGGSoundDecoder + UsePalette + UsePNGImageDecoder + UsePNGImageEncoder + UseRichEdit + UseTGAImageDecoder + UseTIFFImageDecoder + UseWindow + ValF + Val + WaitThread + WaitWindowEvent + WebGadget + WindowEvent + WindowHeight + WindowID + WindowMouseX + WindowMouseY + WindowOutput + WindowWidth + WindowX + WindowY + WriteByte + WriteData + WriteFloat + WriteLong + WritePreferenceFloat + WritePreferenceLong + WritePreferenceString + WriteString + WriteStringN + WriteWord + Year + ZoomSprite3D + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/python.xml b/kate/part/syntax/data/python.xml new file mode 100644 index 00000000..5cafd0e0 --- /dev/null +++ b/kate/part/syntax/data/python.xml @@ -0,0 +1,605 @@ + + + + + + + + + + + + + + + + + import + from + as + + + class + def + del + global + lambda + nonlocal + + + and + in + is + not + or + + + assert + break + continue + elif + else + except + finally + for + if + pass + raise + return + try + while + with + yield + + + __import__ + abs + all + any + apply + ascii + basestring + bin + bool + buffer + bytearray + bytes + callable + chr + classmethod + cmp + coerce + compile + complex + delattr + dict + dir + divmod + enumerate + eval + exec + execfile + file + filter + float + format + frozenset + getattr + globals + hasattr + hash + help + hex + id + input + int + intern + isinstance + issubclass + iter + len + list + locals + long + map + max + memoryview + min + next + object + oct + open + ord + pow + print + property + range + raw_input + reduce + reload + repr + reversed + round + set + setattr + slice + sorted + staticmethod + str + sum + super + tuple + type + unichr + unicode + vars + xrange + zip + + + None + self + True + False + NotImplemented + Ellipsis + __debug__ + __file__ + __name__ + + + SIGNAL + SLOT + connect + + + __new__ + __init__ + __del__ + __repr__ + __str__ + __lt__ + __le__ + __eq__ + __ne__ + __gt__ + __ge__ + __cmp__ + __rcmp__ + __hash__ + __nonzero__ + __unicode__ + __getattr__ + __setattr__ + __delattr__ + __getattribute__ + __get__ + __set__ + __delete__ + __call__ + __len__ + __getitem__ + __setitem__ + __delitem__ + __iter__ + __reversed__ + __contains__ + __getslice__ + __setslice__ + __delslice__ + __add__ + __sub__ + __mul__ + __floordiv__ + __mod__ + __divmod__ + __pow__ + __lshift__ + __rshift__ + __and__ + __xor__ + __or__ + __div__ + __truediv__ + __radd__ + __rsub__ + __rmul__ + __rdiv__ + __rtruediv__ + __rfloordiv__ + __rmod__ + __rdivmod__ + __rpow__ + __rlshift__ + __rrshift__ + __rand__ + __rxor__ + __ror__ + __iadd__ + __isub__ + __imul__ + __idiv__ + __itruediv__ + __ifloordiv__ + __imod__ + __ipow__ + __ilshift__ + __irshift__ + __iand__ + __ixor__ + __ior__ + __neg__ + __pos__ + __abs__ + __invert__ + __complex__ + __int__ + __long__ + __float__ + __oct__ + __hex__ + __index__ + __coerce__ + __enter__ + __exit__ + __bytes__ + __format__ + __next__ + __dir__ + + + + ArithmeticError + AssertionError + AttributeError + BaseException + BlockingIOError + BrokenPipeError + BufferError + BytesWarning + ChildProcessError + ConnectionAbortedError + ConnectionError + ConnectionRefusedError + ConnectionResetError + DeprecationWarning + EnvironmentError + EOFError + Exception + FileExistsError + FileNotFoundError + FloatingPointError + FutureWarning + GeneratorExit + ImportError + ImportWarning + IndentationError + IndexError + InterruptedError + IOError + IsADirectoryError + KeyboardInterrupt + KeyError + LookupError + MemoryError + NameError + NotADirectoryError + NotImplementedError + OSError + OverflowError + PendingDeprecationWarning + PermissionError + ProcessLookupError + ReferenceError + ResourceWarning + RuntimeError + RuntimeWarning + StandardError + StopIteration + SyntaxError + SyntaxWarning + SystemError + SystemExit + TabError + TimeoutError + TypeError + UnboundLocalError + UnicodeDecodeError + UnicodeEncodeError + UnicodeError + UnicodeTranslateError + UnicodeWarning + UserWarning + ValueError + Warning + WindowsError + ZeroDivisionError + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/q.xml b/kate/part/syntax/data/q.xml new file mode 100644 index 00000000..37d442a0 --- /dev/null +++ b/kate/part/syntax/data/q.xml @@ -0,0 +1,210 @@ + + + + + + .Q.addmonths + .Q.addr + .Q.host + .Q.chk + .Q.cn + .Q.dd + .Q.dpft + .Q.dsftg + .Q.def + .Q.en + .Q.fc + .Q.fk + .Q.fmt + .Q.fs + .Q.ft + .Q.fu + .Q.gc + .Q.hdpf + .Q.ind + .Q.j10 + .Q.x10 + .Q.j12 + .Q.x12 + .Q.k + .Q.l + .Q.opt + .Q.par + .Q.qp + .Q.qt + .Q.s + .Q.ty + .Q.v + .Q.V + .Q.view + .Q.w + .Q.M + .Q.pf + .Q.pt + .Q.PD + .Q.PV + .Q.pd + .Q.pv + .Q.pn + .Q.bv + .Q.vp + .Q.P + .Q.D + .Q.u + + + aj + aj0 + all + and + any + asc + asof + attr + avgs + ceiling + cols + cor + count + cov + cross + csv + cut + deltas + desc + dev + differ + distinct + each + ej + enlist + eval + except + fby + fills + first + fkeys + flip + floor + from + get + group + gtime + hclose + hcount + hdel + hopen + hsym + iasc + idesc + ij + inter + inv + key + keys + lj + load + lower + lsq + ltime + ltrim + mavg + maxs + mcount + md5 + mdev + med + meta + mins + mmax + mmin + mmu + mod + msum + neg + next + not + null + or + over + parse + peach + pj + plist + prds + prev + prior + rand + rank + ratios + raze + read0 + read1 + reciprocal + reverse + rload + rotate + rsave + rtrim + save + scan + set + show + signum + ssr + string + sublist + sums + sv + system + tables + til + trim + txf + type + uj + ungroup + union + update + upper + upsert + value + var + view + views + vs + where + wj + wj1 + xasc + xbar + xcol + xcols + xdesc + xgroup + xkey + xlog + xprev + xrank + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/qmake.xml b/kate/part/syntax/data/qmake.xml new file mode 100644 index 00000000..821213a1 --- /dev/null +++ b/kate/part/syntax/data/qmake.xml @@ -0,0 +1,127 @@ + + + + + + + CONFIG + DEFINES + DESTDIR + DLLDESTDIR + FORMS + HEADERS + INCLUDEPATH + INSTALLS + LIBS + OBJECTIVE_SOURCES + OTHER_FILES + OUT_PWD + QMAKE_CXXFLAGS + QMAKE_EXTRA_COMPILERS + QMAKE_FILE_IN + QMAKE_FILE_OUT + QMAKE_LFLAGS + QMAKE_LFLAGS_SONAME + QMAKE_RPATHDIR + QMAKE_SUBSTITUTES + QT + QT_CONFIG + QT_MAJOR_VERSION + QT_MINOR_VERSION + QT_PATCH_VERSION + RCC_DIR + RESOURCES + SOURCES + SUBDIRS + TARGET + TEMPLATE + UI_DIR + MOC_DIR + OBJECTS_DIR + VPATH + + + + contains + defineReplace + defineTest + equals + error + eval + greaterThan + include + isEmpty + isEqual + message + return + unset + + + build_pass + debug + debug_and_release + linux + macx + msvc + release + unix + win32 + + + else + for + if + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/qml.xml b/kate/part/syntax/data/qml.xml new file mode 100644 index 00000000..1f0ad8f0 --- /dev/null +++ b/kate/part/syntax/data/qml.xml @@ -0,0 +1,152 @@ + + + + + + + + State + PropertyChanges + StateGroup + ParentChange + StateChangeScript + AnchorChanges + PropertyAnimation + NumberAnimation + ColorAnimation + SequentialAnimation + ParallelAnimation + PauseAnimation + PropertyAction + ParentAction + ScriptAction + Transition + SpringFollow + EaseFollow + Behavior + Binding + ListModel + ListElement + VisualItemModel + XmlListModel + XmlRole + DateTimeFormatter + NumberFormatter + Script + Connections + Component + Timer + QtObject + Item + Rectangle + Image + BorderImage + Text + TextInput + TextEdit + MouseArea + FocusScope + Flickable + Flipable + WebView + Loader + Repeater + SystemPalette + GraphicsObjectContainer + LayoutItem + ListView + GridView + PathView + Path + PathLine + PathQuad + PathCubic + PathAttribute + PathPercent + Column + Row + Grid + Scale + Rotation + Blur + Colorize + DropShadow + Opacity + Particles + ParticleMotionLinear + ParticleMotionGravity + ParticleMotionWander + + Gradient + GradientStop + MouseRegion + + + + string + int + bool + date + color + url + real + double + var + variant + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/kate/part/syntax/data/r.xml b/kate/part/syntax/data/r.xml new file mode 100644 index 00000000..bfb336f1 --- /dev/null +++ b/kate/part/syntax/data/r.xml @@ -0,0 +1,148 @@ + + + + + + + + for + in + next + break + while + repeat + if + else + switch + function + + + TRUE + FALSE + NULL + NA + NA_integer_ + NA_real_ + NA_complex_ + NA_character_ + Inf + NaN + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/rapidq.xml b/kate/part/syntax/data/rapidq.xml new file mode 100644 index 00000000..cef5f493 --- /dev/null +++ b/kate/part/syntax/data/rapidq.xml @@ -0,0 +1,432 @@ + + + + + + abs + acos + asc + asin + atn + atan + bin$ + bind + callfunc + chdir + chr$ + cint + clng + const + convbase$ + cos + data + date$ + dec + def + delete$ + dim + dir$ + direxists + doevents + end + environ + environ$ + exp + extractresource + field$ + fileexists + fix + format$ + frac + hex$ + iif + inc + inp + insert$ + instr + int + kill + lcase$ + left$ + len + log + lbound + lflush + lprint + ltrim$ + messagedlg + mid$ + mkdir + out + playwav + postmessage + quicksort + randomize + read + redim + rem + rename + replace$ + replacesubstr$ + resource + resourcecount + restore + reverse$ + rgb + right$ + rinstr + rmdir + rnd + round + rtrim$ + run + sendmessage + sgn + shell + showmessage + sin + sizeof + space$ + sqr + str$ + strf$ + string$ + swap + tally + tan + time$ + timer + ubound + ucase$ + val + varptr + varptr$ + true + false + sound + call + goto + gosub + sub + function + declare + do + doevent + loop + while + wend + until + if + then + elseif + select + case + functioni + subi + create + type + exit + messagebox + as + string + integer + word + long + byte + off + on + else + gui + for + next + with + mask + and + or + constructor + extends + defint + defstr + static + public + private + event + single + double + to + + + clipboard + printer + qbitmap + qbutton + qcanvas + qcheckbox + qcombobox + qcomport + qcoolbtn + qdirtree + qedit + qfilelistbox + qfilestream + qfont + qfontdialog + qform + qgauge + qglassframe + qgroupbox + qheader + qimage + qimagelist + qlabel + qlistbox + qlistview + qmainmenu + qmemorystream + qmenuitem + qmysql + qnotifyicondata + qopendialog + qoutline + qovalbtn + qpanel + qpopupmenu + qradiobutton + qrect + qregistry + qrichedit + qsavedialog + qscrollbar + qsocket + qsplitter + qstatusbar + qstringgrid + qstringlist + qtabcontrol + qtimer + qtrackbar + qdximagelist + qdxscreen + qdxtimer + qd3dface + qd3dframe + qd3dlight + qd3dmesh + qd3dtexture + qd3dvector + qd3dvisual + qd3dwarp + application + screen + command$ + curdir$ + sender + + + $apptype + $typecheck + $include + $resource + $define + $undef + $ifdef + $ifndef + $option + $optimize + $escapechars + $EndIf + + + left + top + width + height + open + close + readstring + readinteger + readudt + writestring + writeinteger + writeudt + rootkey + openkey + closekey + flat + cursor + showhint + hint + transparent + caption + onclick + onkeydown + onkeyup + onmousedown + onmouseup + tag + color + onchange + autosize + borderstyle + wordwrap + align + alignment + font + visible + enabled + readline + writeline + additems + addstrings + addstring + delitems + itemcount + itemindex + item + handle + icon + bmphandle + terminate + wndproc + onclose + ontimer + interval + name + size + addstyles + hidetitlebar + show + showmodal + text + button + labelstyle + center + taborder + position + delbordericons + onshow + addbordericons + onkeypress + key + mousex + mousey + onpaint + parent + icohandle + directory + filter + execute + + + filename + bevelinner + bold + checked + clear + underline + maxlength + inputmask + forecolor + + backcolor + deldrivetypes + adddrivetypes + update + bevelouter + + loadfromfile + subitem + viewstyle + rowselect + readonly + gridlines + + addcolumns + oncolumnclick + ondblclick + addsubitem + scrollbars + + addchilditems + plaintext + selstart + sellength + sorted + ColCount + + onmoved + groupindex + OnResize + RowCount + cell + TabPosition + KeyPreview + AddTabs + DelTabs + HotTrack + TabIndex + SizeGrip + AddPanels + Panel + DefaultColWidth + FixedCols + AddOptions + Separator + Col + Row + Rectangle + FillRect + Draw + Count + Line + Circle + TextWidth + TextHeight + TextOut + BeginDoc + EndDoc + CopyRect + FilterIndex + SaveToStream + LoadFromStream + PrinterIndex + Orientation + PageWidth + PageHeight + Right + Bottom + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/relaxng.xml b/kate/part/syntax/data/relaxng.xml new file mode 100644 index 00000000..974f37ff --- /dev/null +++ b/kate/part/syntax/data/relaxng.xml @@ -0,0 +1,117 @@ + + + +]> + + + + + + + anyName + attribute + choice + data + define + div + element + empty + except + externalRef + grammar + group + include + interleave + list + mixed + name + notAllowed + nsName + oneOrMore + optional + param + parentRef + ref + start + text + value + zeroOrMore + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/relaxngcompact.xml b/kate/part/syntax/data/relaxngcompact.xml new file mode 100644 index 00000000..a44012fb --- /dev/null +++ b/kate/part/syntax/data/relaxngcompact.xml @@ -0,0 +1,108 @@ + + + + + + default + datatypes + div + empty + external + grammar + include + inherit + list + mixed + namespace + notAllowed + parent + start + token + + + attribute + element + + + string + text + xsd:anyURI + xsd:base64Binary + xsd:boolean + xsd:byte + xsd:date + xsd:dateTime + xsd:decimal + xsd:double + xsd:duration + xsd:ENTITIES + xsd:ENTITY + xsd:float + xsd:gDay + xsd:gMonth + xsd:gMonthDay + xsd:gYear + xsd:gYearMonth + xsd:hexBinary + xsd:ID + xsd:IDREF + xsd:IDREFS + xsd:int + xsd:integer + xsd:language + xsd:long + xsd:Name + xsd:NCName + xsd:negativeInteger + xsd:NMTOKEN + xsd:NMTOKENS + xsd:nonNegativeInteger + xsd:nonPositiveInteger + xsd:normalizedString + xsd:NOTATION + xsd:positiveInteger + xsd:QName + xsd:short + xsd:string + xsd:time + xsd:token + xsd:unsignedByte + xsd:unsignedInt + xsd:unsignedLong + xsd:unsignedShort + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/kate/part/syntax/data/replicode.xml b/kate/part/syntax/data/replicode.xml new file mode 100644 index 00000000..08bcb335 --- /dev/null +++ b/kate/part/syntax/data/replicode.xml @@ -0,0 +1,200 @@ + + + + + + + _now + equ + neq + gtr + lsr + gte + lse + add + sub + mul + div + dis + ln + exp + log + e10 + syn + red + rnd + fvw + + + view + grp_view + pgm_view + _obj + ptn + |ptn + pgm + |pgm + _grp + grp + _fact + fact + |fact + pred + goal + cst + mdl + icst + icmd + cmd + ent + ont + dev + nod + ipgm + icpp_pgm + perf + + + mk.rdx + mk.grp_pair + mk.low_sln + mk.high_sln + mk.low_act + mk.high_act + mk.low_res + mk.sln_chg + mk.act_chg + mk.new + + + self + + + stdin + stdout + + + _inj + _eje + _mod + _set + _new_class + _del_class + _ldc + _swp + _stop + + + |nb + |bl + true + false + |[] + |nid + |did + |fid + |st + |us + forever + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/rest.xml b/kate/part/syntax/data/rest.xml new file mode 100644 index 00000000..1c86f008 --- /dev/null +++ b/kate/part/syntax/data/rest.xml @@ -0,0 +1,109 @@ + + + + +]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/rexx.xml b/kate/part/syntax/data/rexx.xml new file mode 100644 index 00000000..b22f65d0 --- /dev/null +++ b/kate/part/syntax/data/rexx.xml @@ -0,0 +1,134 @@ + + + + + + arg + drop + else + end + exit + forever + if + interpret + iterate + leave + nop + options + otherwise + pull + push + queue + return + say + select + syntax + then + + + abbrev + abs + address + bitand + bitor + bitxor + b2x + center + charin + charout + chars + c2d + c2x + compare + condition + copies + datatype + date + delstr + delword + digits + d2c + d2x + errortext + form + format + fuzz + insert + lastpos + left + linein + lineout + lines + max + min + overlay + pos + queued + random + reverse + right + sign + sourceline + space + stream + strip + substr + subword + symbol + time + trace + translate + trunc + value + verify + word + wordindex + wordlength + wordpos + words + xrange + x2b + x2c + x2d + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/rhtml.xml b/kate/part/syntax/data/rhtml.xml new file mode 100644 index 00000000..560fb6d7 --- /dev/null +++ b/kate/part/syntax/data/rhtml.xml @@ -0,0 +1,1234 @@ + + + +]> + + + + + + + + + + + + BEGIN + END + and + begin + break + case + defined? + do + else + elsif + end + ensure + for + if + in + include + next + not + or + redo + rescue + retry + return + then + unless + until + when + while + yield + + + + private_class_method + private + protected + public_class_method + public + + + + attr_reader + attr_writer + attr_accessor + + + + alias + module + class + def + undef + + + + self + super + nil + false + true + caller + __FILE__ + __LINE__ + + + + $stdout + $defout + $stderr + $deferr + $stdin + + + + + + abort + at_exit + autoload + autoload? + binding + block_given? + callcc + caller + catch + chomp + chomp! + chop + chop! + eval + exec + exit + exit! + fail + fork + format + getc + gets + global_variables + gsub + gsub! + iterator? + lambda + load + local_variables + loop + method_missing + open + p + print + printf + proc + putc + puts + raise + rand + readline + readlines + require + scan + select + set_trace_func + sleep + split + sprintf + srand + sub + sub! + syscall + system + test + throw + trace_var + trap + untrace_var + warn + + + auto_complete_field + auto_complete_result + auto_discovery_link_tag + auto_link + benchmark + button_to + cache + capture + check_box + check_box_tag + collection_select + concat + content_for + content_tag + country_options_for_select + country_select + current_page? + date_select + datetime_select + debug + define_javascript_functions + distance_of_time_in_words + distance_of_time_in_words_to_now + draggable_element + drop_receiving_element + end_form_tag + error_message_on + error_messages_for + escape_javascript + evaluate_remote_response + excerpt + file_field + file_field_tag + finish_upload_status + form + form_remote_tag + form_tag + form_tag_with_upload_progress + h + hidden_field + hidden_field_tag + highlight + human_size + image_path + image_submit_tag + image_tag + input + javascript_include_tag + javascript_path + javascript_tag + link_image_to + link_to + link_to_function + link_to_if + link_to_image + link_to_remote + link_to_unless + link_to_unless_current + mail_to + markdown + number_to_currency + number_to_human_size + number_to_percentage + number_to_phone + number_with_delimiter + number_with_precision + observe_field + observe_form + option_groups_from_collection_for_select + options_for_select + options_from_collection_for_select + pagination_links + password_field + password_field_tag + periodically_call_remote + pluralize + radio_button + radio_button_tag + register_template_handler + render + render_file + render_template + sanitize + select + select_date + select_datetime + select_day + select_hour + select_minute + select_month + select_second + select_tag + select_time + select_year + simple_format + sortable_element + start_form_tag + strip_links + stylesheet_link_tag + stylesheet_path + submit_tag + submit_to_remote + tag + text_area + text_area_tag + text_field + text_field_tag + text_field_with_auto_complete + textilize + textilize_without_paragraph + time_ago_in_words + time_zone_options_for_select + time_zone_select + truncate + update_element_function + upload_progress_status + upload_progress_text + upload_progress_update_bar_js + upload_status_progress_bar_tag + upload_status_tag + upload_status_text_tag + url_for + visual_effect + word_wrap + + + + TODO + FIXME + NOTE + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/rib.xml b/kate/part/syntax/data/rib.xml new file mode 100644 index 00000000..8472016a --- /dev/null +++ b/kate/part/syntax/data/rib.xml @@ -0,0 +1,143 @@ + + + + + + + + + + + AreaLightSource + Attribute + AttributeBegin + AttributeEnd + Begin + Bound + Clipping + Color + ColorSamples + ConcatTransform + CoordinateSystem + CropWindow + Declare + DepthOfField + Detail + DetailRange + Displacement + Display + End + Exterior + Format + FrameAspectRatio + FrameBegin + FrameEnd + GeometricApproximation + Hider + Identity + Illuminance + Illuminate + Interior + LightSource + Matte + Opacity + Option + Orientation + Perspective + PixelFilter + PixelSamples + PixelVariance + Projection + Quantize + RelativeDetail + Rotate + Scale + ScreenWindow + ShadingInterpolation + ShadingRate + Shutter + Sides + Skew + Surface + TextureCoordinates + Transform + TransformBegin + TransformEnd + TransformPoints + Translate + version + WorldBegin + WorldEnd + + + Basis + Cylinder + Disk + GeneralPolygon + Geometry + Hyperboloid + NuPatch + ObjectBegin + ObjectEnd + ObjectInstance + Patch + Paraboloid + PointsPolygons + PointsGeneralPolygons + Polygon + Procedural + SolidBegin + SolidEnd + Sphere + Torus + + + MotionBegin + MotionEnd + + + MakeBump + MakeCubeFaceEnvironment + MakeLatLongEnvironment + MakeTexture + ArchiveRecord + ErrorHandler + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/roff.xml b/kate/part/syntax/data/roff.xml new file mode 100644 index 00000000..2ecc5cc9 --- /dev/null +++ b/kate/part/syntax/data/roff.xml @@ -0,0 +1,197 @@ + + + + + + +]> + + + + + + br + sp + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/rpmspec.xml b/kate/part/syntax/data/rpmspec.xml new file mode 100644 index 00000000..f93abe52 --- /dev/null +++ b/kate/part/syntax/data/rpmspec.xml @@ -0,0 +1,505 @@ + + + + + + + + +]> + + + + + + + + Mon + Tue + Wed + Thu + Fri + Sat + Sun + + + + Jan + Feb + Mar + Apr + May + Jun + Jul + Aug + Sep + Oct + Nov + Dec + + + + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 01 + 02 + 03 + 04 + 05 + 06 + 07 + 08 + 09 + 10 + 11 + 12 + 13 + 14 + 15 + 16 + 17 + 18 + 19 + 20 + 21 + 22 + 23 + 24 + 25 + 26 + 27 + 28 + 29 + 30 + 31 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/rsiidl.xml b/kate/part/syntax/data/rsiidl.xml new file mode 100644 index 00000000..cea37646 --- /dev/null +++ b/kate/part/syntax/data/rsiidl.xml @@ -0,0 +1,442 @@ + + + + + + For + Do + Endfor + Repeat + Endrep + While + Endwhile + Until + Case + Endcase + If + Endif + Else + Endelse + Then + Begin + End + Function + Goto + Pro + Eq + Ge + Gt + Le + Lt + Ne + Mod + Or + Xor + Not + And + Then + Return + Common + Of + On_ioerror + Switch + Endswitch + + + dpi + dtor + map + pi + radeg + values + err + error_state + error + err_string + except + mouse + msg_prefix + syserror + syserr_string + warn + dir + dlm_path + edit_input + help_path + journal + more + path + prompt + quiet + version + c + d + order + p + x + y + z + stime + + + Fix + Long + Long64 + uint + Byte + Float + Double + complex + dcomplex + complexarr + dcomplexarr + String + Intarr + lonarr + lon64arr + uintarr + ulong + ulonarr + ulon64arr + Bytarr + Bytscl + Fltarr + Dblarr + Strarr + Objarr + Indgen + Findgen + Dindgen + Dcindgen + cindgen + lindgen + bindgen + sindgen + uindgen + ul64indgen + l64indgen + ulindgen + Replicate + Ptrarr + + + ABS + ACOS + ADAPT_HIST_EQUAL + ALOG + ALOG10 + ARG_PRESENT + ASIN + ASSOC + ATAN + AXIS + BESELI + BESELJ + BESELY + BLAS_AXPY + BREAKPOINT + BROYDEN + BYTEORDER + CALL_EXTERNAL + CALL_FUNCTION + CALL_METHOD + CALL_PROCEDURE + CATCH + CEIL + CHECK_MATH + CHOLDC + CHOLSOL + COLOR_CONVERT + COLOR_QUAN + COMPILE_OPT + COMPUTE_MESH_NORMALS + CONJ + CONSTRAINED_MIN + CONTOUR + CONVERT_COORD + CONVOL + CORRELATE + COS + COSH + CREATE_STRUCT + CURSOR + DEFINE_KEY + DEFSYSV + DELVAR + DEVICE + DFPMIN + DIALOG_MESSAGE + DIALOG_PICKFILE + DIALOG_PRINTERSETUP + DIALOG_PRINTJOB + DILATE + DLM_LOAD + DRAW_ROI + ELMHES + EMPTY + ENABLE_SYSRTN + ERASE + ERODE + ERRORF + EXECUTE + EXIT + EXP + EXPAND_PATH + EXPINT + FINDFILE + FINITE + FLOOR + FORMAT_AXIS_VALUES + FORWARD_FUNCTION + FSTAT + FULSTR + FZ_ROOTS + GAUSSINT + GET_KBRD + GETENV + GRID_TPS + GRID3 + HEAP_GC + HELP + HISTOGRAM + HQR + IMAGE_STATISTICS + IMAGINARY + INTERPOLATE + INVERT + ISHFT + ISOCONTOUR + ISOSURFACE + JOURNAL + KEYWORD_SET + LABEL_REGION + LINBCG + LINKIMAGE + LMGR + LNGAMMA + LNP_TEST + LOADCT + LOCALE_GET + LSODE + LUDC + LUMPROVE + LUSOL + MACHAR + MAKE_ARRAY + MAP_PROJ_INFO + MAX + MEDIAN + MESH_CLIP + MESH_DECIMATE + MESH_ISSOLID + MESH_MERGE + MESH_NUMTRIANGLES + MESH_SMOOTH + MESH_SURFACEAREA + MESH_VALIDATE + MESH_VOLUME + MESSAGE + MIN + N_ELEMENTS + N_PARAMS + N_TAGS + NEWTON + OBJ_CLASS + OBJ_DESTROY + OBJ_ISA + OBJ_NEW + OBJ_VALID + ON_ERROR + OPLOT + PARTICLE_TRACE + PLOT + PLOTS + POLY_2D + POLYFILL + POLYFILLV + POLYSHADE + POWELL + PROFILER + PTR_FREE + PTR_NEW + PTR_VALID + QROMB + QROMO + QSIMP + RANDOMN + RANDOMU + REBIN + REFORM + RETALL + RETURN + RIEMANN + RK4 + ROBERTS + ROTATE + ROUND + SET_PLOT + SET_SHADING + SETENV + SHADE_SURF + SHADE_VOLUME + SHIFT + SIN + SINH + SIZE + SMOOTH + SOBEL + SORT + SPL_INIT + SPL_INTERP + SPRSAB + SPRSAX + SPRSIN + SQRT + STOP + STRCMP + STRCOMPRESS + STREGEX + STRJOIN + STRLEN + STRLOWCASE + STRMATCH + STRMESSAGE + STRMID + STRPOS + STRPUT + STRTRIM + STRUCT_ASSIGN + STRUCT_HIDE + STRUPCASE + SURFACE + SVDC + SVSOL + SYSTIME + TAG_NAMES + TAN + TANH + TEMPORARY + TETRA_CLIP + TETRA_SURFACE + TETRA_VOLUME + THIN + THREED + TOTAL + TRANSPOSE + TRIANGULATE + TRIGRID + TRIQL + TRIRED + TRISOL + TV + TVCRS + TVLCT + TVRD + TVSCLU + USERSYM + VALUE_LOCATE + VOIGT + VOXEL_PROJ + WAIT + WATERSHED + WDELETE + WHERE + WIDGET_BASE + WIDGET_BUTTON + WIDGET_CONTROL + WIDGET_DRAW + WIDGET_DROPLIST + WIDGET_EVENT + WIDGET_INFO + WIDGET_LABEL + WIDGET_LIST + WIDGET_SLIDER + WIDGET_TABLE + WIDGET_TEXT + WINDOW + WSET + WSHOW + WTN + XYOUTS + + + Open + FLUSH + IOCTL + RESTORE + SAVE + POINT_LUN + Openr + Openw + Openu + Close + Free_lun + get_lun + assoc + catch + cd + spawn + eof + print + printf + prints + read + readf + reads + writu + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/ruby.xml b/kate/part/syntax/data/ruby.xml new file mode 100644 index 00000000..4b60a195 --- /dev/null +++ b/kate/part/syntax/data/ruby.xml @@ -0,0 +1,921 @@ + + + + + + + + + + + + + + BEGIN + END + and + begin + break + case + defined? + do + else + elsif + end + ensure + for + if + in + next + not + or + redo + rescue + retry + return + then + unless + until + when + yield + + + + private_class_method + private + protected + public_class_method + public + + + + attr_reader + attr_writer + attr_accessor + + + + alias + module + class + def + undef + + + + self + super + nil + false + true + caller + __FILE__ + __LINE__ + + + + $stdout + $defout + $stderr + $deferr + $stdin + + + + + + abort + at_exit + autoload + autoload? + binding + block_given? + callcc + caller + catch + chomp + chomp! + chop + chop! + eval + exec + exit + exit! + fail + fork + format + getc + gets + global_variables + gsub + gsub! + iterator? + lambda + load + local_variables + loop + method_missing + open + p + print + printf + proc + putc + puts + raise + rand + readline + readlines + require + require_relative + scan + select + set_trace_func + sleep + split + sprintf + srand + sub + sub! + syscall + system + test + throw + trace_var + trap + untrace_var + warn + + + + extend + include + prepend + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/sather.xml b/kate/part/syntax/data/sather.xml new file mode 100644 index 00000000..472ca878 --- /dev/null +++ b/kate/part/syntax/data/sather.xml @@ -0,0 +1,141 @@ + + + + + + and + assert + attr + break! + case + class + const + else + elsif + end + exception + external + false + if + include + initial + is + ITER + loop + new + or + post + pre + private + protect + quit + raise + readonly + result + return + ROUT + SAME + self + shared + then + true + typecase + type + until! + value + void + when + while! + yield + abstract + any + bind + fork + guard + immutable + inout + in + lock + once + out + parloop + partial + par + spread + stub + + + $OB + ARRAY + AREF + AVAL + BOOL + CHAR + EXT_OB + FLTDX + FLTD + FLTX + FLTI + FLT + INTI + INT + $REHASH + STR + SYS + + + create + invariant + main + aget + aset + div + is_eq + is_geq + is_gt + is_leq + is_lt + is_neq + minus + mod + negate + not + plus + pow + times + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/scala.xml b/kate/part/syntax/data/scala.xml new file mode 100644 index 00000000..b2171842 --- /dev/null +++ b/kate/part/syntax/data/scala.xml @@ -0,0 +1,3503 @@ + + + + + + + Actor + ActorProxy + ActorTask + ActorThread + AllRef + Any + AnyRef + Application + AppliedType + Array + ArrayBuffer + Attribute + BoxedArray + BoxedBooleanArray + BoxedByteArray + BoxedCharArray + Buffer + BufferedIterator + Char + Console + Enumeration + Fluid + Function + IScheduler + ImmutableMapAdaptor + ImmutableSetAdaptor + Int + Iterable + List + ListBuffer + None + Option + Ordered + Pair + PartialFunction + Pid + Predef + PriorityQueue + PriorityQueueProxy + Reaction + Ref + Responder + RichInt + RichString + Rule + RuleTransformer + Script + Seq + SerialVersionUID + Some + Stream + Symbol + TcpService + TcpServiceWorker + Triple + Unit + Value + WorkerThread + serializable + transient + volatile + + + ACTIVE + ACTIVITY_COMPLETED + ACTIVITY_REQUIRED + ARG_IN + ARG_INOUT + ARG_OUT + AWTError + AWTEvent + AWTEventListener + AWTEventListenerProxy + AWTEventMulticaster + AWTException + AWTKeyStroke + AWTPermission + AbstractAction + AbstractBorder + AbstractButton + AbstractCellEditor + AbstractCollection + AbstractColorChooserPanel + AbstractDocument + AbstractDocument.AttributeContext + AbstractDocument.Content + AbstractDocument.ElementEdit + AbstractExecutorService + AbstractInterruptibleChannel + AbstractLayoutCache + AbstractLayoutCache.NodeDimensions + AbstractList + AbstractListModel + AbstractMap + AbstractMethodError + AbstractPreferences + AbstractQueue + AbstractQueuedSynchronizer + AbstractSelectableChannel + AbstractSelectionKey + AbstractSelector + AbstractSequentialList + AbstractSet + AbstractSpinnerModel + AbstractTableModel + AbstractUndoableEdit + AbstractWriter + AccessControlContext + AccessControlException + AccessController + AccessException + Accessible + AccessibleAction + AccessibleAttributeSequence + AccessibleBundle + AccessibleComponent + AccessibleContext + AccessibleEditableText + AccessibleExtendedComponent + AccessibleExtendedTable + AccessibleExtendedText + AccessibleHyperlink + AccessibleHypertext + AccessibleIcon + AccessibleKeyBinding + AccessibleObject + AccessibleRelation + AccessibleRelationSet + AccessibleResourceBundle + AccessibleRole + AccessibleSelection + AccessibleState + AccessibleStateSet + AccessibleStreamable + AccessibleTable + AccessibleTableModelChange + AccessibleText + AccessibleTextSequence + AccessibleValue + AccountException + AccountExpiredException + AccountLockedException + AccountNotFoundException + Acl + AclEntry + AclNotFoundException + Action + ActionEvent + ActionListener + ActionMap + ActionMapUIResource + Activatable + ActivateFailedException + ActivationDesc + ActivationException + ActivationGroup + ActivationGroupDesc + ActivationGroupDesc.CommandEnvironment + ActivationGroupID + ActivationGroup_Stub + ActivationID + ActivationInstantiator + ActivationMonitor + ActivationSystem + Activator + ActiveEvent + ActivityCompletedException + ActivityRequiredException + AdapterActivator + AdapterActivatorOperations + AdapterAlreadyExists + AdapterAlreadyExistsHelper + AdapterInactive + AdapterInactiveHelper + AdapterManagerIdHelper + AdapterNameHelper + AdapterNonExistent + AdapterNonExistentHelper + AdapterStateHelper + AddressHelper + Adjustable + AdjustmentEvent + AdjustmentListener + Adler32 + AffineTransform + AffineTransformOp + AlgorithmParameterGenerator + AlgorithmParameterGeneratorSpi + AlgorithmParameterSpec + AlgorithmParameters + AlgorithmParametersSpi + AllPermission + AlphaComposite + AlreadyBound + AlreadyBoundException + AlreadyBoundHelper + AlreadyBoundHolder + AlreadyConnectedException + AncestorEvent + AncestorListener + AnnotatedElement + Annotation + Annotation + AnnotationFormatError + AnnotationTypeMismatchException + Any + AnyHolder + AnySeqHelper + AnySeqHelper + AnySeqHolder + AppConfigurationEntry + AppConfigurationEntry.LoginModuleControlFlag + Appendable + Applet + AppletContext + AppletInitializer + AppletStub + ApplicationException + Arc2D + Arc2D.Double + Arc2D.Float + Area + AreaAveragingScaleFilter + ArithmeticException + Array + Array + ArrayBlockingQueue + ArrayIndexOutOfBoundsException + ArrayList + ArrayStoreException + ArrayType + Arrays + AssertionError + AsyncBoxView + AsynchronousCloseException + AtomicBoolean + AtomicInteger + AtomicIntegerArray + AtomicIntegerFieldUpdater + AtomicLong + AtomicLongArray + AtomicLongFieldUpdater + AtomicMarkableReference + AtomicReference + AtomicReferenceArray + AtomicReferenceFieldUpdater + AtomicStampedReference + Attr + Attribute + Attribute + Attribute + AttributeChangeNotification + AttributeChangeNotificationFilter + AttributeException + AttributeInUseException + AttributeList + AttributeList + AttributeList + AttributeListImpl + AttributeModificationException + AttributeNotFoundException + AttributeSet + AttributeSet + AttributeSet.CharacterAttribute + AttributeSet.ColorAttribute + AttributeSet.FontAttribute + AttributeSet.ParagraphAttribute + AttributeSetUtilities + AttributeValueExp + AttributedCharacterIterator + AttributedCharacterIterator.Attribute + AttributedString + Attributes + Attributes + Attributes + Attributes.Name + Attributes2 + Attributes2Impl + AttributesImpl + AudioClip + AudioFileFormat + AudioFileFormat.Type + AudioFileReader + AudioFileWriter + AudioFormat + AudioFormat.Encoding + AudioInputStream + AudioPermission + AudioSystem + AuthPermission + AuthProvider + AuthenticationException + AuthenticationException + AuthenticationNotSupportedException + Authenticator + Authenticator.RequestorType + AuthorizeCallback + Autoscroll + BAD_CONTEXT + BAD_INV_ORDER + BAD_OPERATION + BAD_PARAM + BAD_POLICY + BAD_POLICY_TYPE + BAD_POLICY_VALUE + BAD_QOS + BAD_TYPECODE + BMPImageWriteParam + BackingStoreException + BadAttributeValueExpException + BadBinaryOpValueExpException + BadKind + BadLocationException + BadPaddingException + BadStringOperationException + BandCombineOp + BandedSampleModel + BaseRowSet + BasicArrowButton + BasicAttribute + BasicAttributes + BasicBorders + BasicBorders.ButtonBorder + BasicBorders.FieldBorder + BasicBorders.MarginBorder + BasicBorders.MenuBarBorder + BasicBorders.RadioButtonBorder + BasicBorders.RolloverButtonBorder + BasicBorders.SplitPaneBorder + BasicBorders.ToggleButtonBorder + BasicButtonListener + BasicButtonUI + BasicCheckBoxMenuItemUI + BasicCheckBoxUI + BasicColorChooserUI + BasicComboBoxEditor + BasicComboBoxEditor.UIResource + BasicComboBoxRenderer + BasicComboBoxRenderer.UIResource + BasicComboBoxUI + BasicComboPopup + BasicControl + BasicDesktopIconUI + BasicDesktopPaneUI + BasicDirectoryModel + BasicEditorPaneUI + BasicFileChooserUI + BasicFormattedTextFieldUI + BasicGraphicsUtils + BasicHTML + BasicIconFactory + BasicInternalFrameTitlePane + BasicInternalFrameUI + BasicLabelUI + BasicListUI + BasicLookAndFeel + BasicMenuBarUI + BasicMenuItemUI + BasicMenuUI + BasicOptionPaneUI + BasicOptionPaneUI.ButtonAreaLayout + BasicPanelUI + BasicPasswordFieldUI + BasicPermission + BasicPopupMenuSeparatorUI + BasicPopupMenuUI + BasicProgressBarUI + BasicRadioButtonMenuItemUI + BasicRadioButtonUI + BasicRootPaneUI + BasicScrollBarUI + BasicScrollPaneUI + BasicSeparatorUI + BasicSliderUI + BasicSpinnerUI + BasicSplitPaneDivider + BasicSplitPaneUI + BasicStroke + BasicTabbedPaneUI + BasicTableHeaderUI + BasicTableUI + BasicTextAreaUI + BasicTextFieldUI + BasicTextPaneUI + BasicTextUI + BasicTextUI.BasicCaret + BasicTextUI.BasicHighlighter + BasicToggleButtonUI + BasicToolBarSeparatorUI + BasicToolBarUI + BasicToolTipUI + BasicTreeUI + BasicViewportUI + BatchUpdateException + BeanContext + BeanContextChild + BeanContextChildComponentProxy + BeanContextChildSupport + BeanContextContainerProxy + BeanContextEvent + BeanContextMembershipEvent + BeanContextMembershipListener + BeanContextProxy + BeanContextServiceAvailableEvent + BeanContextServiceProvider + BeanContextServiceProviderBeanInfo + BeanContextServiceRevokedEvent + BeanContextServiceRevokedListener + BeanContextServices + BeanContextServicesListener + BeanContextServicesSupport + BeanContextServicesSupport.BCSSServiceProvider + BeanContextSupport + BeanContextSupport.BCSIterator + BeanDescriptor + BeanInfo + Beans + BevelBorder + Bidi + BigDecimal + BigInteger + BinaryRefAddr + BindException + Binding + Binding + BindingHelper + BindingHolder + BindingIterator + BindingIteratorHelper + BindingIteratorHolder + BindingIteratorOperations + BindingIteratorPOA + BindingListHelper + BindingListHolder + BindingType + BindingTypeHelper + BindingTypeHolder + BitSet + Blob + BlockView + BlockingQueue + Book + Boolean + BooleanControl + BooleanControl.Type + BooleanHolder + BooleanSeqHelper + BooleanSeqHolder + Border + BorderFactory + BorderLayout + BorderUIResource + BorderUIResource.BevelBorderUIResource + BorderUIResource.CompoundBorderUIResource + BorderUIResource.EmptyBorderUIResource + BorderUIResource.EtchedBorderUIResource + BorderUIResource.LineBorderUIResource + BorderUIResource.MatteBorderUIResource + BorderUIResource.TitledBorderUIResource + BoundedRangeModel + Bounds + Bounds + Box + Box.Filler + BoxLayout + BoxView + BoxedValueHelper + BreakIterator + BrokenBarrierException + Buffer + BufferCapabilities + BufferCapabilities.FlipContents + BufferOverflowException + BufferStrategy + BufferUnderflowException + BufferedImage + BufferedImageFilter + BufferedImageOp + BufferedInputStream + BufferedOutputStream + BufferedReader + BufferedWriter + Button + ButtonGroup + ButtonModel + ButtonUI + Byte + ByteArrayInputStream + ByteArrayOutputStream + ByteBuffer + ByteChannel + ByteHolder + ByteLookupTable + ByteOrder + CDATASection + CMMException + CODESET_INCOMPATIBLE + COMM_FAILURE + CRC32 + CRL + CRLException + CRLSelector + CSS + CSS.Attribute + CTX_RESTRICT_SCOPE + CacheRequest + CacheResponse + CachedRowSet + Calendar + Callable + CallableStatement + Callback + CallbackHandler + CancelablePrintJob + CancellationException + CancelledKeyException + CannotProceed + CannotProceedException + CannotProceedHelper + CannotProceedHolder + CannotRedoException + CannotUndoException + Canvas + CardLayout + Caret + CaretEvent + CaretListener + CellEditor + CellEditorListener + CellRendererPane + CertPath + CertPath.CertPathRep + CertPathBuilder + CertPathBuilderException + CertPathBuilderResult + CertPathBuilderSpi + CertPathParameters + CertPathTrustManagerParameters + CertPathValidator + CertPathValidatorException + CertPathValidatorResult + CertPathValidatorSpi + CertSelector + CertStore + CertStoreException + CertStoreParameters + CertStoreSpi + Certificate + Certificate + Certificate + Certificate.CertificateRep + CertificateEncodingException + CertificateEncodingException + CertificateException + CertificateException + CertificateExpiredException + CertificateExpiredException + CertificateFactory + CertificateFactorySpi + CertificateNotYetValidException + CertificateNotYetValidException + CertificateParsingException + CertificateParsingException + ChangeEvent + ChangeListener + ChangedCharSetException + Channel + ChannelBinding + Channels + CharArrayReader + CharArrayWriter + CharBuffer + CharConversionException + CharHolder + CharSeqHelper + CharSeqHolder + CharSequence + Character + Character.Subset + Character.UnicodeBlock + CharacterCodingException + CharacterData + CharacterIterator + Charset + CharsetDecoder + CharsetEncoder + CharsetProvider + Checkbox + CheckboxGroup + CheckboxMenuItem + CheckedInputStream + CheckedOutputStream + Checksum + Choice + ChoiceCallback + ChoiceFormat + Chromaticity + Cipher + CipherInputStream + CipherOutputStream + CipherSpi + Class + ClassCastException + ClassCircularityError + ClassDefinition + ClassDesc + ClassFileTransformer + ClassFormatError + ClassLoader + ClassLoaderRepository + ClassLoadingMXBean + ClassNotFoundException + ClientRequestInfo + ClientRequestInfoOperations + ClientRequestInterceptor + ClientRequestInterceptorOperations + Clip + Clipboard + ClipboardOwner + Clob + CloneNotSupportedException + Cloneable + Closeable + ClosedByInterruptException + ClosedChannelException + ClosedSelectorException + CodeSets + CodeSigner + CodeSource + Codec + CodecFactory + CodecFactoryHelper + CodecFactoryOperations + CodecOperations + CoderMalfunctionError + CoderResult + CodingErrorAction + CollationElementIterator + CollationKey + Collator + Collection + CollectionCertStoreParameters + Collections + Color + ColorChooserComponentFactory + ColorChooserUI + ColorConvertOp + ColorModel + ColorSelectionModel + ColorSpace + ColorSupported + ColorType + ColorUIResource + ComboBoxEditor + ComboBoxModel + ComboBoxUI + ComboPopup + Comment + CommunicationException + Comparable + Comparator + CompilationMXBean + Compiler + CompletionService + CompletionStatus + CompletionStatusHelper + Component + ComponentAdapter + ComponentColorModel + ComponentEvent + ComponentIdHelper + ComponentInputMap + ComponentInputMapUIResource + ComponentListener + ComponentOrientation + ComponentSampleModel + ComponentUI + ComponentView + Composite + CompositeContext + CompositeData + CompositeDataSupport + CompositeName + CompositeType + CompositeView + CompoundBorder + CompoundControl + CompoundControl.Type + CompoundEdit + CompoundName + Compression + ConcurrentHashMap + ConcurrentLinkedQueue + ConcurrentMap + ConcurrentModificationException + Condition + Configuration + ConfigurationException + ConfirmationCallback + ConnectException + ConnectException + ConnectIOException + Connection + ConnectionEvent + ConnectionEventListener + ConnectionPendingException + ConnectionPoolDataSource + ConsoleHandler + Constructor + Container + ContainerAdapter + ContainerEvent + ContainerListener + ContainerOrderFocusTraversalPolicy + ContentHandler + ContentHandler + ContentHandlerFactory + ContentModel + Context + Context + ContextList + ContextNotEmptyException + ContextualRenderedImageFactory + Control + Control + Control.Type + ControlFactory + ControllerEventListener + ConvolveOp + CookieHandler + CookieHolder + Copies + CopiesSupported + CopyOnWriteArrayList + CopyOnWriteArraySet + CountDownLatch + CounterMonitor + CounterMonitorMBean + CredentialException + CredentialExpiredException + CredentialNotFoundException + CropImageFilter + CubicCurve2D + CubicCurve2D.Double + CubicCurve2D.Float + Currency + Current + Current + Current + CurrentHelper + CurrentHelper + CurrentHelper + CurrentHolder + CurrentOperations + CurrentOperations + CurrentOperations + Cursor + CustomMarshal + CustomValue + Customizer + CyclicBarrier + DATA_CONVERSION + DESKeySpec + DESedeKeySpec + DGC + DHGenParameterSpec + DHKey + DHParameterSpec + DHPrivateKey + DHPrivateKeySpec + DHPublicKey + DHPublicKeySpec + DISCARDING + DOMConfiguration + DOMError + DOMErrorHandler + DOMException + DOMImplementation + DOMImplementationLS + DOMImplementationList + DOMImplementationRegistry + DOMImplementationSource + DOMLocator + DOMLocator + DOMResult + DOMSource + DOMStringList + DSAKey + DSAKeyPairGenerator + DSAParameterSpec + DSAParams + DSAPrivateKey + DSAPrivateKeySpec + DSAPublicKey + DSAPublicKeySpec + DTD + DTDConstants + DTDHandler + DataBuffer + DataBufferByte + DataBufferDouble + DataBufferFloat + DataBufferInt + DataBufferShort + DataBufferUShort + DataFlavor + DataFormatException + DataInput + DataInputStream + DataInputStream + DataLine + DataLine.Info + DataOutput + DataOutputStream + DataOutputStream + DataSource + DataTruncation + DatabaseMetaData + DatagramChannel + DatagramPacket + DatagramSocket + DatagramSocketImpl + DatagramSocketImplFactory + DatatypeConfigurationException + DatatypeConstants + DatatypeConstants.Field + DatatypeFactory + Date + Date + DateFormat + DateFormat.Field + DateFormatSymbols + DateFormatter + DateTimeAtCompleted + DateTimeAtCreation + DateTimeAtProcessing + DateTimeSyntax + DebugGraphics + DecimalFormat + DecimalFormatSymbols + DeclHandler + DefaultBoundedRangeModel + DefaultButtonModel + DefaultCaret + DefaultCellEditor + DefaultColorSelectionModel + DefaultComboBoxModel + DefaultDesktopManager + DefaultEditorKit + DefaultEditorKit.BeepAction + DefaultEditorKit.CopyAction + DefaultEditorKit.CutAction + DefaultEditorKit.DefaultKeyTypedAction + DefaultEditorKit.InsertBreakAction + DefaultEditorKit.InsertContentAction + DefaultEditorKit.InsertTabAction + DefaultEditorKit.PasteAction + DefaultFocusManager + DefaultFocusTraversalPolicy + DefaultFormatter + DefaultFormatterFactory + DefaultHandler + DefaultHandler2 + DefaultHighlighter + DefaultHighlighter.DefaultHighlightPainter + DefaultKeyboardFocusManager + DefaultListCellRenderer + DefaultListCellRenderer.UIResource + DefaultListModel + DefaultListSelectionModel + DefaultLoaderRepository + DefaultLoaderRepository + DefaultMenuLayout + DefaultMetalTheme + DefaultMutableTreeNode + DefaultPersistenceDelegate + DefaultSingleSelectionModel + DefaultStyledDocument + DefaultStyledDocument.AttributeUndoableEdit + DefaultStyledDocument.ElementSpec + DefaultTableCellRenderer + DefaultTableCellRenderer.UIResource + DefaultTableColumnModel + DefaultTableModel + DefaultTextUI + DefaultTreeCellEditor + DefaultTreeCellRenderer + DefaultTreeModel + DefaultTreeSelectionModel + DefinitionKind + DefinitionKindHelper + Deflater + DeflaterOutputStream + DelayQueue + Delayed + Delegate + Delegate + Delegate + DelegationPermission + Deprecated + Descriptor + DescriptorAccess + DescriptorSupport + DesignMode + DesktopIconUI + DesktopManager + DesktopPaneUI + Destination + DestroyFailedException + Destroyable + Dialog + Dictionary + DigestException + DigestInputStream + DigestOutputStream + Dimension + Dimension2D + DimensionUIResource + DirContext + DirObjectFactory + DirStateFactory + DirStateFactory.Result + DirectColorModel + DirectoryManager + DisplayMode + DnDConstants + Doc + DocAttribute + DocAttributeSet + DocFlavor + DocFlavor.BYTE_ARRAY + DocFlavor.CHAR_ARRAY + DocFlavor.INPUT_STREAM + DocFlavor.READER + DocFlavor.SERVICE_FORMATTED + DocFlavor.STRING + DocFlavor.URL + DocPrintJob + Document + Document + DocumentBuilder + DocumentBuilderFactory + DocumentEvent + DocumentEvent.ElementChange + DocumentEvent.EventType + DocumentFilter + DocumentFilter.FilterBypass + DocumentFragment + DocumentHandler + DocumentListener + DocumentName + DocumentParser + DocumentType + Documented + DomainCombiner + DomainManager + DomainManagerOperations + Double + DoubleBuffer + DoubleHolder + DoubleSeqHelper + DoubleSeqHolder + DragGestureEvent + DragGestureListener + DragGestureRecognizer + DragSource + DragSourceAdapter + DragSourceContext + DragSourceDragEvent + DragSourceDropEvent + DragSourceEvent + DragSourceListener + DragSourceMotionListener + Driver + DriverManager + DriverPropertyInfo + DropTarget + DropTarget.DropTargetAutoScroller + DropTargetAdapter + DropTargetContext + DropTargetDragEvent + DropTargetDropEvent + DropTargetEvent + DropTargetListener + DuplicateFormatFlagsException + DuplicateName + DuplicateNameHelper + Duration + DynAny + DynAny + DynAnyFactory + DynAnyFactoryHelper + DynAnyFactoryOperations + DynAnyHelper + DynAnyOperations + DynAnySeqHelper + DynArray + DynArray + DynArrayHelper + DynArrayOperations + DynEnum + DynEnum + DynEnumHelper + DynEnumOperations + DynFixed + DynFixed + DynFixedHelper + DynFixedOperations + DynSequence + DynSequence + DynSequenceHelper + DynSequenceOperations + DynStruct + DynStruct + DynStructHelper + DynStructOperations + DynUnion + DynUnion + DynUnionHelper + DynUnionOperations + DynValue + DynValue + DynValueBox + DynValueBoxOperations + DynValueCommon + DynValueCommonOperations + DynValueHelper + DynValueOperations + DynamicImplementation + DynamicImplementation + DynamicMBean + ECField + ECFieldF2m + ECFieldFp + ECGenParameterSpec + ECKey + ECParameterSpec + ECPoint + ECPrivateKey + ECPrivateKeySpec + ECPublicKey + ECPublicKeySpec + ENCODING_CDR_ENCAPS + EOFException + EditorKit + Element + Element + Element + ElementIterator + ElementType + Ellipse2D + Ellipse2D.Double + Ellipse2D.Float + EllipticCurve + EmptyBorder + EmptyStackException + EncodedKeySpec + Encoder + Encoding + EncryptedPrivateKeyInfo + Entity + Entity + EntityReference + EntityResolver + EntityResolver2 + Enum + EnumConstantNotPresentException + EnumControl + EnumControl.Type + EnumMap + EnumSet + EnumSyntax + Enumeration + Environment + Error + ErrorHandler + ErrorListener + ErrorManager + EtchedBorder + Event + EventContext + EventDirContext + EventHandler + EventListener + EventListenerList + EventListenerProxy + EventObject + EventQueue + EventSetDescriptor + Exception + ExceptionDetailMessage + ExceptionInInitializerError + ExceptionList + ExceptionListener + Exchanger + ExecutionException + Executor + ExecutorCompletionService + ExecutorService + Executors + ExemptionMechanism + ExemptionMechanismException + ExemptionMechanismSpi + ExpandVetoException + ExportException + Expression + ExtendedRequest + ExtendedResponse + Externalizable + FREE_MEM + FactoryConfigurationError + FailedLoginException + FeatureDescriptor + Fidelity + Field + FieldNameHelper + FieldNameHelper + FieldPosition + FieldView + File + FileCacheImageInputStream + FileCacheImageOutputStream + FileChannel + FileChannel.MapMode + FileChooserUI + FileDescriptor + FileDialog + FileFilter + FileFilter + FileHandler + FileImageInputStream + FileImageOutputStream + FileInputStream + FileLock + FileLockInterruptionException + FileNameMap + FileNotFoundException + FileOutputStream + FilePermission + FileReader + FileSystemView + FileView + FileWriter + FilenameFilter + Filter + FilterInputStream + FilterOutputStream + FilterReader + FilterWriter + FilteredImageSource + FilteredRowSet + Finishings + FixedHeightLayoutCache + FixedHolder + FlatteningPathIterator + FlavorEvent + FlavorException + FlavorListener + FlavorMap + FlavorTable + Float + FloatBuffer + FloatControl + FloatControl.Type + FloatHolder + FloatSeqHelper + FloatSeqHolder + FlowLayout + FlowView + FlowView.FlowStrategy + Flushable + FocusAdapter + FocusEvent + FocusListener + FocusManager + FocusTraversalPolicy + Font + FontFormatException + FontMetrics + FontRenderContext + FontUIResource + FormSubmitEvent + FormSubmitEvent.MethodType + FormView + Format + Format.Field + FormatConversionProvider + FormatFlagsConversionMismatchException + FormatMismatch + FormatMismatchHelper + Formattable + FormattableFlags + Formatter + Formatter + FormatterClosedException + ForwardRequest + ForwardRequest + ForwardRequestHelper + ForwardRequestHelper + Frame + Future + FutureTask + GSSContext + GSSCredential + GSSException + GSSManager + GSSName + GZIPInputStream + GZIPOutputStream + GapContent + GarbageCollectorMXBean + GatheringByteChannel + GaugeMonitor + GaugeMonitorMBean + GeneralPath + GeneralSecurityException + GenericArrayType + GenericDeclaration + GenericSignatureFormatError + GlyphJustificationInfo + GlyphMetrics + GlyphVector + GlyphView + GlyphView.GlyphPainter + GradientPaint + GraphicAttribute + Graphics + Graphics2D + GraphicsConfigTemplate + GraphicsConfiguration + GraphicsDevice + GraphicsEnvironment + GrayFilter + GregorianCalendar + GridBagConstraints + GridBagLayout + GridLayout + Group + Guard + GuardedObject + HOLDING + HTML + HTML.Attribute + HTML.Tag + HTML.UnknownTag + HTMLDocument + HTMLDocument.Iterator + HTMLEditorKit + HTMLEditorKit.HTMLFactory + HTMLEditorKit.HTMLTextAction + HTMLEditorKit.InsertHTMLTextAction + HTMLEditorKit.LinkController + HTMLEditorKit.Parser + HTMLEditorKit.ParserCallback + HTMLFrameHyperlinkEvent + HTMLWriter + Handler + HandlerBase + HandshakeCompletedEvent + HandshakeCompletedListener + HasControls + HashAttributeSet + HashDocAttributeSet + HashMap + HashPrintJobAttributeSet + HashPrintRequestAttributeSet + HashPrintServiceAttributeSet + HashSet + Hashtable + HeadlessException + HierarchyBoundsAdapter + HierarchyBoundsListener + HierarchyEvent + HierarchyListener + Highlighter + Highlighter.Highlight + Highlighter.HighlightPainter + HostnameVerifier + HttpRetryException + HttpURLConnection + HttpsURLConnection + HyperlinkEvent + HyperlinkEvent.EventType + HyperlinkListener + ICC_ColorSpace + ICC_Profile + ICC_ProfileGray + ICC_ProfileRGB + IDLEntity + IDLType + IDLTypeHelper + IDLTypeOperations + ID_ASSIGNMENT_POLICY_ID + ID_UNIQUENESS_POLICY_ID + IIOByteBuffer + IIOException + IIOImage + IIOInvalidTreeException + IIOMetadata + IIOMetadataController + IIOMetadataFormat + IIOMetadataFormatImpl + IIOMetadataNode + IIOParam + IIOParamController + IIOReadProgressListener + IIOReadUpdateListener + IIOReadWarningListener + IIORegistry + IIOServiceProvider + IIOWriteProgressListener + IIOWriteWarningListener + IMPLICIT_ACTIVATION_POLICY_ID + IMP_LIMIT + INACTIVE + INITIALIZE + INTERNAL + INTF_REPOS + INVALID_ACTIVITY + INVALID_TRANSACTION + INV_FLAG + INV_IDENT + INV_OBJREF + INV_POLICY + IOException + IOR + IORHelper + IORHolder + IORInfo + IORInfoOperations + IORInterceptor + IORInterceptorOperations + IORInterceptor_3_0 + IORInterceptor_3_0Helper + IORInterceptor_3_0Holder + IORInterceptor_3_0Operations + IRObject + IRObjectOperations + Icon + IconUIResource + IconView + IdAssignmentPolicy + IdAssignmentPolicyOperations + IdAssignmentPolicyValue + IdUniquenessPolicy + IdUniquenessPolicyOperations + IdUniquenessPolicyValue + IdentifierHelper + Identity + IdentityHashMap + IdentityScope + IllegalAccessError + IllegalAccessException + IllegalArgumentException + IllegalBlockSizeException + IllegalBlockingModeException + IllegalCharsetNameException + IllegalClassFormatException + IllegalComponentStateException + IllegalFormatCodePointException + IllegalFormatConversionException + IllegalFormatException + IllegalFormatFlagsException + IllegalFormatPrecisionException + IllegalFormatWidthException + IllegalMonitorStateException + IllegalPathStateException + IllegalSelectorException + IllegalStateException + IllegalThreadStateException + Image + ImageCapabilities + ImageConsumer + ImageFilter + ImageGraphicAttribute + ImageIO + ImageIcon + ImageInputStream + ImageInputStreamImpl + ImageInputStreamSpi + ImageObserver + ImageOutputStream + ImageOutputStreamImpl + ImageOutputStreamSpi + ImageProducer + ImageReadParam + ImageReader + ImageReaderSpi + ImageReaderWriterSpi + ImageTranscoder + ImageTranscoderSpi + ImageTypeSpecifier + ImageView + ImageWriteParam + ImageWriter + ImageWriterSpi + ImagingOpException + ImplicitActivationPolicy + ImplicitActivationPolicyOperations + ImplicitActivationPolicyValue + IncompatibleClassChangeError + IncompleteAnnotationException + InconsistentTypeCode + InconsistentTypeCode + InconsistentTypeCodeHelper + IndexColorModel + IndexOutOfBoundsException + IndexedPropertyChangeEvent + IndexedPropertyDescriptor + IndirectionException + Inet4Address + Inet6Address + InetAddress + InetSocketAddress + Inflater + InflaterInputStream + InheritableThreadLocal + Inherited + InitialContext + InitialContextFactory + InitialContextFactoryBuilder + InitialDirContext + InitialLdapContext + InlineView + InputContext + InputEvent + InputMap + InputMapUIResource + InputMethod + InputMethodContext + InputMethodDescriptor + InputMethodEvent + InputMethodHighlight + InputMethodListener + InputMethodRequests + InputMismatchException + InputSource + InputStream + InputStream + InputStream + InputStreamReader + InputSubset + InputVerifier + Insets + InsetsUIResource + InstanceAlreadyExistsException + InstanceNotFoundException + InstantiationError + InstantiationException + Instrument + Instrumentation + InsufficientResourcesException + IntBuffer + IntHolder + Integer + IntegerSyntax + Interceptor + InterceptorOperations + InternalError + InternalFrameAdapter + InternalFrameEvent + InternalFrameFocusTraversalPolicy + InternalFrameListener + InternalFrameUI + InternationalFormatter + InterruptedException + InterruptedIOException + InterruptedNamingException + InterruptibleChannel + IntrospectionException + IntrospectionException + Introspector + Invalid + InvalidActivityException + InvalidAddress + InvalidAddressHelper + InvalidAddressHolder + InvalidAlgorithmParameterException + InvalidApplicationException + InvalidAttributeIdentifierException + InvalidAttributeValueException + InvalidAttributeValueException + InvalidAttributesException + InvalidClassException + InvalidDnDOperationException + InvalidKeyException + InvalidKeyException + InvalidKeySpecException + InvalidMarkException + InvalidMidiDataException + InvalidName + InvalidName + InvalidName + InvalidNameException + InvalidNameHelper + InvalidNameHelper + InvalidNameHolder + InvalidObjectException + InvalidOpenTypeException + InvalidParameterException + InvalidParameterSpecException + InvalidPolicy + InvalidPolicyHelper + InvalidPreferencesFormatException + InvalidPropertiesFormatException + InvalidRelationIdException + InvalidRelationServiceException + InvalidRelationTypeException + InvalidRoleInfoException + InvalidRoleValueException + InvalidSearchControlsException + InvalidSearchFilterException + InvalidSeq + InvalidSlot + InvalidSlotHelper + InvalidTargetObjectTypeException + InvalidTransactionException + InvalidTypeForEncoding + InvalidTypeForEncodingHelper + InvalidValue + InvalidValue + InvalidValueHelper + InvocationEvent + InvocationHandler + InvocationTargetException + InvokeHandler + IstringHelper + ItemEvent + ItemListener + ItemSelectable + Iterable + Iterator + IvParameterSpec + JApplet + JButton + JCheckBox + JCheckBoxMenuItem + JColorChooser + JComboBox + JComboBox.KeySelectionManager + JComponent + JDesktopPane + JDialog + JEditorPane + JFileChooser + JFormattedTextField + JFormattedTextField.AbstractFormatter + JFormattedTextField.AbstractFormatterFactory + JFrame + JInternalFrame + JInternalFrame.JDesktopIcon + JLabel + JLayeredPane + JList + JMException + JMRuntimeException + JMXAuthenticator + JMXConnectionNotification + JMXConnector + JMXConnectorFactory + JMXConnectorProvider + JMXConnectorServer + JMXConnectorServerFactory + JMXConnectorServerMBean + JMXConnectorServerProvider + JMXPrincipal + JMXProviderException + JMXServerErrorException + JMXServiceURL + JMenu + JMenuBar + JMenuItem + JOptionPane + JPEGHuffmanTable + JPEGImageReadParam + JPEGImageWriteParam + JPEGQTable + JPanel + JPasswordField + JPopupMenu + JPopupMenu.Separator + JProgressBar + JRadioButton + JRadioButtonMenuItem + JRootPane + JScrollBar + JScrollPane + JSeparator + JSlider + JSpinner + JSpinner.DateEditor + JSpinner.DefaultEditor + JSpinner.ListEditor + JSpinner.NumberEditor + JSplitPane + JTabbedPane + JTable + JTable.PrintMode + JTableHeader + JTextArea + JTextComponent + JTextComponent.KeyBinding + JTextField + JTextPane + JToggleButton + JToggleButton.ToggleButtonModel + JToolBar + JToolBar.Separator + JToolTip + JTree + JTree.DynamicUtilTreeNode + JTree.EmptySelectionModel + JViewport + JWindow + JarEntry + JarException + JarFile + JarInputStream + JarOutputStream + JarURLConnection + JdbcRowSet + JobAttributes + JobAttributes.DefaultSelectionType + JobAttributes.DestinationType + JobAttributes.DialogType + JobAttributes.MultipleDocumentHandlingType + JobAttributes.SidesType + JobHoldUntil + JobImpressions + JobImpressionsCompleted + JobImpressionsSupported + JobKOctets + JobKOctetsProcessed + JobKOctetsSupported + JobMediaSheets + JobMediaSheetsCompleted + JobMediaSheetsSupported + JobMessageFromOperator + JobName + JobOriginatingUserName + JobPriority + JobPrioritySupported + JobSheets + JobState + JobStateReason + JobStateReasons + JoinRowSet + Joinable + KerberosKey + KerberosPrincipal + KerberosTicket + Kernel + Key + KeyAdapter + KeyAgreement + KeyAgreementSpi + KeyAlreadyExistsException + KeyEvent + KeyEventDispatcher + KeyEventPostProcessor + KeyException + KeyFactory + KeyFactorySpi + KeyGenerator + KeyGeneratorSpi + KeyListener + KeyManagementException + KeyManager + KeyManagerFactory + KeyManagerFactorySpi + KeyPair + KeyPairGenerator + KeyPairGeneratorSpi + KeyRep + KeyRep.Type + KeySpec + KeyStore + KeyStore.Builder + KeyStore.CallbackHandlerProtection + KeyStore.Entry + KeyStore.LoadStoreParameter + KeyStore.PasswordProtection + KeyStore.PrivateKeyEntry + KeyStore.ProtectionParameter + KeyStore.SecretKeyEntry + KeyStore.TrustedCertificateEntry + KeyStoreBuilderParameters + KeyStoreException + KeyStoreSpi + KeyStroke + KeyboardFocusManager + Keymap + LDAPCertStoreParameters + LIFESPAN_POLICY_ID + LOCATION_FORWARD + LSException + LSInput + LSLoadEvent + LSOutput + LSParser + LSParserFilter + LSProgressEvent + LSResourceResolver + LSSerializer + LSSerializerFilter + Label + LabelUI + LabelView + LanguageCallback + LastOwnerException + LayeredHighlighter + LayeredHighlighter.LayerPainter + LayoutFocusTraversalPolicy + LayoutManager + LayoutManager2 + LayoutQueue + LdapContext + LdapName + LdapReferralException + Lease + Level + LexicalHandler + LifespanPolicy + LifespanPolicyOperations + LifespanPolicyValue + LimitExceededException + Line + Line.Info + Line2D + Line2D.Double + Line2D.Float + LineBorder + LineBreakMeasurer + LineEvent + LineEvent.Type + LineListener + LineMetrics + LineNumberInputStream + LineNumberReader + LineUnavailableException + LinkException + LinkLoopException + LinkRef + LinkageError + LinkedBlockingQueue + LinkedHashMap + LinkedHashSet + LinkedList + List + List + ListCellRenderer + ListDataEvent + ListDataListener + ListIterator + ListModel + ListResourceBundle + ListSelectionEvent + ListSelectionListener + ListSelectionModel + ListUI + ListView + ListenerNotFoundException + LoaderHandler + LocalObject + Locale + LocateRegistry + Locator + Locator2 + Locator2Impl + LocatorImpl + Lock + LockSupport + LogManager + LogRecord + LogStream + Logger + LoggingMXBean + LoggingPermission + LoginContext + LoginException + LoginModule + Long + LongBuffer + LongHolder + LongLongSeqHelper + LongLongSeqHolder + LongSeqHelper + LongSeqHolder + LookAndFeel + LookupOp + LookupTable + MARSHAL + MBeanAttributeInfo + MBeanConstructorInfo + MBeanException + MBeanFeatureInfo + MBeanInfo + MBeanNotificationInfo + MBeanOperationInfo + MBeanParameterInfo + MBeanPermission + MBeanRegistration + MBeanRegistrationException + MBeanServer + MBeanServerBuilder + MBeanServerConnection + MBeanServerDelegate + MBeanServerDelegateMBean + MBeanServerFactory + MBeanServerForwarder + MBeanServerInvocationHandler + MBeanServerNotification + MBeanServerNotificationFilter + MBeanServerPermission + MBeanTrustPermission + MGF1ParameterSpec + MLet + MLetMBean + Mac + MacSpi + MalformedInputException + MalformedLinkException + MalformedObjectNameException + MalformedParameterizedTypeException + MalformedURLException + ManageReferralControl + ManagementFactory + ManagementPermission + ManagerFactoryParameters + Manifest + Map + Map.Entry + MappedByteBuffer + MarshalException + MarshalledObject + MaskFormatter + MatchResult + Matcher + Math + MathContext + MatteBorder + Media + MediaName + MediaPrintableArea + MediaSize + MediaSize.Engineering + MediaSize.ISO + MediaSize.JIS + MediaSize.NA + MediaSize.Other + MediaSizeName + MediaTracker + MediaTray + Member + MemoryCacheImageInputStream + MemoryCacheImageOutputStream + MemoryHandler + MemoryImageSource + MemoryMXBean + MemoryManagerMXBean + MemoryNotificationInfo + MemoryPoolMXBean + MemoryType + MemoryUsage + Menu + MenuBar + MenuBarUI + MenuComponent + MenuContainer + MenuDragMouseEvent + MenuDragMouseListener + MenuElement + MenuEvent + MenuItem + MenuItemUI + MenuKeyEvent + MenuKeyListener + MenuListener + MenuSelectionManager + MenuShortcut + MessageDigest + MessageDigestSpi + MessageFormat + MessageFormat.Field + MessageProp + MetaEventListener + MetaMessage + MetalBorders + MetalBorders.ButtonBorder + MetalBorders.Flush3DBorder + MetalBorders.InternalFrameBorder + MetalBorders.MenuBarBorder + MetalBorders.MenuItemBorder + MetalBorders.OptionDialogBorder + MetalBorders.PaletteBorder + MetalBorders.PopupMenuBorder + MetalBorders.RolloverButtonBorder + MetalBorders.ScrollPaneBorder + MetalBorders.TableHeaderBorder + MetalBorders.TextFieldBorder + MetalBorders.ToggleButtonBorder + MetalBorders.ToolBarBorder + MetalButtonUI + MetalCheckBoxIcon + MetalCheckBoxUI + MetalComboBoxButton + MetalComboBoxEditor + MetalComboBoxEditor.UIResource + MetalComboBoxIcon + MetalComboBoxUI + MetalDesktopIconUI + MetalFileChooserUI + MetalIconFactory + MetalIconFactory.FileIcon16 + MetalIconFactory.FolderIcon16 + MetalIconFactory.PaletteCloseIcon + MetalIconFactory.TreeControlIcon + MetalIconFactory.TreeFolderIcon + MetalIconFactory.TreeLeafIcon + MetalInternalFrameTitlePane + MetalInternalFrameUI + MetalLabelUI + MetalLookAndFeel + MetalMenuBarUI + MetalPopupMenuSeparatorUI + MetalProgressBarUI + MetalRadioButtonUI + MetalRootPaneUI + MetalScrollBarUI + MetalScrollButton + MetalScrollPaneUI + MetalSeparatorUI + MetalSliderUI + MetalSplitPaneUI + MetalTabbedPaneUI + MetalTextFieldUI + MetalTheme + MetalToggleButtonUI + MetalToolBarUI + MetalToolTipUI + MetalTreeUI + Method + MethodDescriptor + MidiChannel + MidiDevice + MidiDevice.Info + MidiDeviceProvider + MidiEvent + MidiFileFormat + MidiFileReader + MidiFileWriter + MidiMessage + MidiSystem + MidiUnavailableException + MimeTypeParseException + MinimalHTMLWriter + MissingFormatArgumentException + MissingFormatWidthException + MissingResourceException + Mixer + Mixer.Info + MixerProvider + ModelMBean + ModelMBeanAttributeInfo + ModelMBeanConstructorInfo + ModelMBeanInfo + ModelMBeanInfoSupport + ModelMBeanNotificationBroadcaster + ModelMBeanNotificationInfo + ModelMBeanOperationInfo + ModificationItem + Modifier + Monitor + MonitorMBean + MonitorNotification + MonitorSettingException + MouseAdapter + MouseDragGestureRecognizer + MouseEvent + MouseInfo + MouseInputAdapter + MouseInputListener + MouseListener + MouseMotionAdapter + MouseMotionListener + MouseWheelEvent + MouseWheelListener + MultiButtonUI + MultiColorChooserUI + MultiComboBoxUI + MultiDesktopIconUI + MultiDesktopPaneUI + MultiDoc + MultiDocPrintJob + MultiDocPrintService + MultiFileChooserUI + MultiInternalFrameUI + MultiLabelUI + MultiListUI + MultiLookAndFeel + MultiMenuBarUI + MultiMenuItemUI + MultiOptionPaneUI + MultiPanelUI + MultiPixelPackedSampleModel + MultiPopupMenuUI + MultiProgressBarUI + MultiRootPaneUI + MultiScrollBarUI + MultiScrollPaneUI + MultiSeparatorUI + MultiSliderUI + MultiSpinnerUI + MultiSplitPaneUI + MultiTabbedPaneUI + MultiTableHeaderUI + MultiTableUI + MultiTextUI + MultiToolBarUI + MultiToolTipUI + MultiTreeUI + MultiViewportUI + MulticastSocket + MultipleComponentProfileHelper + MultipleComponentProfileHolder + MultipleDocumentHandling + MultipleMaster + MutableAttributeSet + MutableComboBoxModel + MutableTreeNode + NON_EXISTENT + NO_IMPLEMENT + NO_MEMORY + NO_PERMISSION + NO_RESOURCES + NO_RESPONSE + NVList + Name + NameAlreadyBoundException + NameCallback + NameClassPair + NameComponent + NameComponentHelper + NameComponentHolder + NameDynAnyPair + NameDynAnyPairHelper + NameDynAnyPairSeqHelper + NameHelper + NameHolder + NameList + NameNotFoundException + NameParser + NameValuePair + NameValuePair + NameValuePairHelper + NameValuePairHelper + NameValuePairSeqHelper + NamedNodeMap + NamedValue + NamespaceChangeListener + NamespaceContext + NamespaceSupport + Naming + NamingContext + NamingContextExt + NamingContextExtHelper + NamingContextExtHolder + NamingContextExtOperations + NamingContextExtPOA + NamingContextHelper + NamingContextHolder + NamingContextOperations + NamingContextPOA + NamingEnumeration + NamingEvent + NamingException + NamingExceptionEvent + NamingListener + NamingManager + NamingSecurityException + NavigationFilter + NavigationFilter.FilterBypass + NegativeArraySizeException + NetPermission + NetworkInterface + NoClassDefFoundError + NoConnectionPendingException + NoContext + NoContextHelper + NoInitialContextException + NoPermissionException + NoRouteToHostException + NoServant + NoServantHelper + NoSuchAlgorithmException + NoSuchAttributeException + NoSuchElementException + NoSuchFieldError + NoSuchFieldException + NoSuchMethodError + NoSuchMethodException + NoSuchObjectException + NoSuchPaddingException + NoSuchProviderException + Node + NodeChangeEvent + NodeChangeListener + NodeList + NonReadableChannelException + NonWritableChannelException + NoninvertibleTransformException + NotActiveException + NotBoundException + NotCompliantMBeanException + NotContextException + NotEmpty + NotEmptyHelper + NotEmptyHolder + NotFound + NotFoundHelper + NotFoundHolder + NotFoundReason + NotFoundReasonHelper + NotFoundReasonHolder + NotOwnerException + NotSerializableException + NotYetBoundException + NotYetConnectedException + Notation + Notification + NotificationBroadcaster + NotificationBroadcasterSupport + NotificationEmitter + NotificationFilter + NotificationFilterSupport + NotificationListener + NotificationResult + NullCipher + NullPointerException + Number + NumberFormat + NumberFormat.Field + NumberFormatException + NumberFormatter + NumberOfDocuments + NumberOfInterveningJobs + NumberUp + NumberUpSupported + NumericShaper + OAEPParameterSpec + OBJECT_NOT_EXIST + OBJ_ADAPTER + OMGVMCID + ORB + ORB + ORBIdHelper + ORBInitInfo + ORBInitInfoOperations + ORBInitializer + ORBInitializerOperations + ObjID + Object + Object + ObjectAlreadyActive + ObjectAlreadyActiveHelper + ObjectChangeListener + ObjectFactory + ObjectFactoryBuilder + ObjectHelper + ObjectHolder + ObjectIdHelper + ObjectIdHelper + ObjectImpl + ObjectImpl + ObjectInput + ObjectInputStream + ObjectInputStream.GetField + ObjectInputValidation + ObjectInstance + ObjectName + ObjectNotActive + ObjectNotActiveHelper + ObjectOutput + ObjectOutputStream + ObjectOutputStream.PutField + ObjectReferenceFactory + ObjectReferenceFactoryHelper + ObjectReferenceFactoryHolder + ObjectReferenceTemplate + ObjectReferenceTemplateHelper + ObjectReferenceTemplateHolder + ObjectReferenceTemplateSeqHelper + ObjectReferenceTemplateSeqHolder + ObjectStreamClass + ObjectStreamConstants + ObjectStreamException + ObjectStreamField + ObjectView + Observable + Observer + OceanTheme + OctetSeqHelper + OctetSeqHolder + Oid + OpenDataException + OpenMBeanAttributeInfo + OpenMBeanAttributeInfoSupport + OpenMBeanConstructorInfo + OpenMBeanConstructorInfoSupport + OpenMBeanInfo + OpenMBeanInfoSupport + OpenMBeanOperationInfo + OpenMBeanOperationInfoSupport + OpenMBeanParameterInfo + OpenMBeanParameterInfoSupport + OpenType + OpenType + OperatingSystemMXBean + Operation + OperationNotSupportedException + OperationsException + Option + OptionPaneUI + OptionalDataException + OrientationRequested + OutOfMemoryError + OutputDeviceAssigned + OutputKeys + OutputStream + OutputStream + OutputStream + OutputStreamWriter + OverlappingFileLockException + OverlayLayout + Override + Owner + PBEKey + PBEKeySpec + PBEParameterSpec + PDLOverrideSupported + PERSIST_STORE + PKCS8EncodedKeySpec + PKIXBuilderParameters + PKIXCertPathBuilderResult + PKIXCertPathChecker + PKIXCertPathValidatorResult + PKIXParameters + POA + POAHelper + POAManager + POAManagerOperations + POAOperations + PRIVATE_MEMBER + PSSParameterSpec + PSource + PSource.PSpecified + PUBLIC_MEMBER + Pack200 + Pack200.Packer + Pack200.Unpacker + Package + PackedColorModel + PageAttributes + PageAttributes.ColorType + PageAttributes.MediaType + PageAttributes.OrientationRequestedType + PageAttributes.OriginType + PageAttributes.PrintQualityType + PageFormat + PageRanges + Pageable + PagedResultsControl + PagedResultsResponseControl + PagesPerMinute + PagesPerMinuteColor + Paint + PaintContext + PaintEvent + Panel + PanelUI + Paper + ParagraphView + ParagraphView + Parameter + ParameterBlock + ParameterDescriptor + ParameterMetaData + ParameterMode + ParameterModeHelper + ParameterModeHolder + ParameterizedType + ParseException + ParsePosition + Parser + Parser + ParserAdapter + ParserConfigurationException + ParserDelegator + ParserFactory + PartialResultException + PasswordAuthentication + PasswordCallback + PasswordView + Patch + PathIterator + Pattern + PatternSyntaxException + Permission + Permission + PermissionCollection + Permissions + PersistenceDelegate + PersistentMBean + PhantomReference + Pipe + Pipe.SinkChannel + Pipe.SourceChannel + PipedInputStream + PipedOutputStream + PipedReader + PipedWriter + PixelGrabber + PixelInterleavedSampleModel + PlainDocument + PlainView + Point + Point2D + Point2D.Double + Point2D.Float + PointerInfo + Policy + Policy + Policy + PolicyError + PolicyErrorCodeHelper + PolicyErrorHelper + PolicyErrorHolder + PolicyFactory + PolicyFactoryOperations + PolicyHelper + PolicyHolder + PolicyListHelper + PolicyListHolder + PolicyNode + PolicyOperations + PolicyQualifierInfo + PolicyTypeHelper + Polygon + PooledConnection + Popup + PopupFactory + PopupMenu + PopupMenuEvent + PopupMenuListener + PopupMenuUI + Port + Port.Info + PortUnreachableException + PortableRemoteObject + PortableRemoteObjectDelegate + Position + Position.Bias + Predicate + PreferenceChangeEvent + PreferenceChangeListener + Preferences + PreferencesFactory + PreparedStatement + PresentationDirection + Principal + Principal + PrincipalHolder + PrintEvent + PrintException + PrintGraphics + PrintJob + PrintJobAdapter + PrintJobAttribute + PrintJobAttributeEvent + PrintJobAttributeListener + PrintJobAttributeSet + PrintJobEvent + PrintJobListener + PrintQuality + PrintRequestAttribute + PrintRequestAttributeSet + PrintService + PrintServiceAttribute + PrintServiceAttributeEvent + PrintServiceAttributeListener + PrintServiceAttributeSet + PrintServiceLookup + PrintStream + PrintWriter + Printable + PrinterAbortException + PrinterException + PrinterGraphics + PrinterIOException + PrinterInfo + PrinterIsAcceptingJobs + PrinterJob + PrinterLocation + PrinterMakeAndModel + PrinterMessageFromOperator + PrinterMoreInfo + PrinterMoreInfoManufacturer + PrinterName + PrinterResolution + PrinterState + PrinterStateReason + PrinterStateReasons + PrinterURI + PriorityBlockingQueue + PriorityQueue + PrivateClassLoader + PrivateCredentialPermission + PrivateKey + PrivateMLet + PrivilegedAction + PrivilegedActionException + PrivilegedExceptionAction + Process + ProcessBuilder + ProcessingInstruction + ProfileDataException + ProfileIdHelper + ProgressBarUI + ProgressMonitor + ProgressMonitorInputStream + Properties + PropertyChangeEvent + PropertyChangeListener + PropertyChangeListenerProxy + PropertyChangeSupport + PropertyDescriptor + PropertyEditor + PropertyEditorManager + PropertyEditorSupport + PropertyPermission + PropertyResourceBundle + PropertyVetoException + ProtectionDomain + ProtocolException + Provider + Provider.Service + ProviderException + Proxy + Proxy + Proxy.Type + ProxySelector + PublicKey + PushbackInputStream + PushbackReader + QName + QuadCurve2D + QuadCurve2D.Double + QuadCurve2D.Float + Query + QueryEval + QueryExp + Queue + QueuedJobCount + RC2ParameterSpec + RC5ParameterSpec + REBIND + REQUEST_PROCESSING_POLICY_ID + RGBImageFilter + RMIClassLoader + RMIClassLoaderSpi + RMIClientSocketFactory + RMIConnection + RMIConnectionImpl + RMIConnectionImpl_Stub + RMIConnector + RMIConnectorServer + RMICustomMaxStreamFormat + RMIFailureHandler + RMIIIOPServerImpl + RMIJRMPServerImpl + RMISecurityException + RMISecurityManager + RMIServer + RMIServerImpl + RMIServerImpl_Stub + RMIServerSocketFactory + RMISocketFactory + RSAKey + RSAKeyGenParameterSpec + RSAMultiPrimePrivateCrtKey + RSAMultiPrimePrivateCrtKeySpec + RSAOtherPrimeInfo + RSAPrivateCrtKey + RSAPrivateCrtKeySpec + RSAPrivateKey + RSAPrivateKeySpec + RSAPublicKey + RSAPublicKeySpec + RTFEditorKit + Random + RandomAccess + RandomAccessFile + Raster + RasterFormatException + RasterOp + Rdn + ReadOnlyBufferException + ReadWriteLock + Readable + ReadableByteChannel + Reader + RealmCallback + RealmChoiceCallback + Receiver + Rectangle + Rectangle2D + Rectangle2D.Double + Rectangle2D.Float + RectangularShape + ReentrantLock + ReentrantReadWriteLock + ReentrantReadWriteLock.ReadLock + ReentrantReadWriteLock.WriteLock + Ref + RefAddr + Reference + Reference + ReferenceQueue + ReferenceUriSchemesSupported + Referenceable + ReferralException + ReflectPermission + ReflectionException + RefreshFailedException + Refreshable + Region + RegisterableService + Registry + RegistryHandler + RejectedExecutionException + RejectedExecutionHandler + Relation + RelationException + RelationNotFoundException + RelationNotification + RelationService + RelationServiceMBean + RelationServiceNotRegisteredException + RelationSupport + RelationSupportMBean + RelationType + RelationTypeNotFoundException + RelationTypeSupport + RemarshalException + Remote + RemoteCall + RemoteException + RemoteObject + RemoteObjectInvocationHandler + RemoteRef + RemoteServer + RemoteStub + RenderContext + RenderableImage + RenderableImageOp + RenderableImageProducer + RenderedImage + RenderedImageFactory + Renderer + RenderingHints + RenderingHints.Key + RepaintManager + ReplicateScaleFilter + RepositoryIdHelper + Request + RequestInfo + RequestInfoOperations + RequestProcessingPolicy + RequestProcessingPolicyOperations + RequestProcessingPolicyValue + RequestingUserName + RequiredModelMBean + RescaleOp + ResolutionSyntax + ResolveResult + Resolver + ResourceBundle + ResponseCache + ResponseHandler + Result + ResultSet + ResultSetMetaData + Retention + RetentionPolicy + ReverbType + Robot + Role + RoleInfo + RoleInfoNotFoundException + RoleList + RoleNotFoundException + RoleResult + RoleStatus + RoleUnresolved + RoleUnresolvedList + RootPaneContainer + RootPaneUI + RoundRectangle2D + RoundRectangle2D.Double + RoundRectangle2D.Float + RoundingMode + RowMapper + RowSet + RowSetEvent + RowSetInternal + RowSetListener + RowSetMetaData + RowSetMetaDataImpl + RowSetReader + RowSetWarning + RowSetWriter + RuleBasedCollator + RunTime + RunTimeOperations + Runnable + Runtime + RuntimeErrorException + RuntimeException + RuntimeMBeanException + RuntimeMXBean + RuntimeOperationsException + RuntimePermission + SAXException + SAXNotRecognizedException + SAXNotSupportedException + SAXParseException + SAXParser + SAXParserFactory + SAXResult + SAXSource + SAXTransformerFactory + SERVANT_RETENTION_POLICY_ID + SQLData + SQLException + SQLInput + SQLInputImpl + SQLOutput + SQLOutputImpl + SQLPermission + SQLWarning + SSLContext + SSLContextSpi + SSLEngine + SSLEngineResult + SSLEngineResult.HandshakeStatus + SSLEngineResult.Status + SSLException + SSLHandshakeException + SSLKeyException + SSLPeerUnverifiedException + SSLPermission + SSLProtocolException + SSLServerSocket + SSLServerSocketFactory + SSLSession + SSLSessionBindingEvent + SSLSessionBindingListener + SSLSessionContext + SSLSocket + SSLSocketFactory + SUCCESSFUL + SYNC_WITH_TRANSPORT + SYSTEM_EXCEPTION + SampleModel + Sasl + SaslClient + SaslClientFactory + SaslException + SaslServer + SaslServerFactory + Savepoint + Scanner + ScatteringByteChannel + ScheduledExecutorService + ScheduledFuture + ScheduledThreadPoolExecutor + Schema + SchemaFactory + SchemaFactoryLoader + SchemaViolationException + ScrollBarUI + ScrollPane + ScrollPaneAdjustable + ScrollPaneConstants + ScrollPaneLayout + ScrollPaneLayout.UIResource + ScrollPaneUI + Scrollable + Scrollbar + SealedObject + SearchControls + SearchResult + SecretKey + SecretKeyFactory + SecretKeyFactorySpi + SecretKeySpec + SecureCacheResponse + SecureClassLoader + SecureRandom + SecureRandomSpi + Security + SecurityException + SecurityManager + SecurityPermission + Segment + SelectableChannel + SelectionKey + Selector + SelectorProvider + Semaphore + SeparatorUI + Sequence + SequenceInputStream + Sequencer + Sequencer.SyncMode + SerialArray + SerialBlob + SerialClob + SerialDatalink + SerialException + SerialJavaObject + SerialRef + SerialStruct + Serializable + SerializablePermission + Servant + ServantActivator + ServantActivatorHelper + ServantActivatorOperations + ServantActivatorPOA + ServantAlreadyActive + ServantAlreadyActiveHelper + ServantLocator + ServantLocatorHelper + ServantLocatorOperations + ServantLocatorPOA + ServantManager + ServantManagerOperations + ServantNotActive + ServantNotActiveHelper + ServantObject + ServantRetentionPolicy + ServantRetentionPolicyOperations + ServantRetentionPolicyValue + ServerCloneException + ServerError + ServerException + ServerIdHelper + ServerNotActiveException + ServerRef + ServerRequest + ServerRequestInfo + ServerRequestInfoOperations + ServerRequestInterceptor + ServerRequestInterceptorOperations + ServerRuntimeException + ServerSocket + ServerSocketChannel + ServerSocketFactory + ServiceContext + ServiceContextHelper + ServiceContextHolder + ServiceContextListHelper + ServiceContextListHolder + ServiceDetail + ServiceDetailHelper + ServiceIdHelper + ServiceInformation + ServiceInformationHelper + ServiceInformationHolder + ServiceNotFoundException + ServicePermission + ServiceRegistry + ServiceRegistry.Filter + ServiceUI + ServiceUIFactory + ServiceUnavailableException + Set + SetOfIntegerSyntax + SetOverrideType + SetOverrideTypeHelper + Severity + Shape + ShapeGraphicAttribute + SheetCollate + Short + ShortBuffer + ShortBufferException + ShortHolder + ShortLookupTable + ShortMessage + ShortSeqHelper + ShortSeqHolder + Sides + Signature + SignatureException + SignatureSpi + SignedObject + Signer + SimpleAttributeSet + SimpleBeanInfo + SimpleDateFormat + SimpleDoc + SimpleFormatter + SimpleTimeZone + SimpleType + SinglePixelPackedSampleModel + SingleSelectionModel + Size2DSyntax + SizeLimitExceededException + SizeRequirements + SizeSequence + Skeleton + SkeletonMismatchException + SkeletonNotFoundException + SliderUI + Socket + SocketAddress + SocketChannel + SocketException + SocketFactory + SocketHandler + SocketImpl + SocketImplFactory + SocketOptions + SocketPermission + SocketSecurityException + SocketTimeoutException + SoftBevelBorder + SoftReference + SortControl + SortKey + SortResponseControl + SortedMap + SortedSet + SortingFocusTraversalPolicy + Soundbank + SoundbankReader + SoundbankResource + Source + SourceDataLine + SourceLocator + SpinnerDateModel + SpinnerListModel + SpinnerModel + SpinnerNumberModel + SpinnerUI + SplitPaneUI + Spring + SpringLayout + SpringLayout.Constraints + SslRMIClientSocketFactory + SslRMIServerSocketFactory + Stack + StackOverflowError + StackTraceElement + StandardMBean + StartTlsRequest + StartTlsResponse + State + StateEdit + StateEditable + StateFactory + Statement + Statement + StreamCorruptedException + StreamHandler + StreamPrintService + StreamPrintServiceFactory + StreamResult + StreamSource + StreamTokenizer + Streamable + StreamableValue + StrictMath + String + StringBuffer + StringBufferInputStream + StringBuilder + StringCharacterIterator + StringContent + StringHolder + StringIndexOutOfBoundsException + StringMonitor + StringMonitorMBean + StringNameHelper + StringReader + StringRefAddr + StringSelection + StringSeqHelper + StringSeqHolder + StringTokenizer + StringValueExp + StringValueHelper + StringWriter + Stroke + Struct + StructMember + StructMemberHelper + Stub + StubDelegate + StubNotFoundException + Style + StyleConstants + StyleConstants.CharacterConstants + StyleConstants.ColorConstants + StyleConstants.FontConstants + StyleConstants.ParagraphConstants + StyleContext + StyleSheet + StyleSheet.BoxPainter + StyleSheet.ListPainter + StyledDocument + StyledEditorKit + StyledEditorKit.AlignmentAction + StyledEditorKit.BoldAction + StyledEditorKit.FontFamilyAction + StyledEditorKit.FontSizeAction + StyledEditorKit.ForegroundAction + StyledEditorKit.ItalicAction + StyledEditorKit.StyledTextAction + StyledEditorKit.UnderlineAction + Subject + SubjectDelegationPermission + SubjectDomainCombiner + SupportedValuesAttribute + SuppressWarnings + SwingConstants + SwingPropertyChangeSupport + SwingUtilities + SyncFactory + SyncFactoryException + SyncFailedException + SyncProvider + SyncProviderException + SyncResolver + SyncScopeHelper + SynchronousQueue + SynthConstants + SynthContext + SynthGraphicsUtils + SynthLookAndFeel + SynthPainter + SynthStyle + SynthStyleFactory + Synthesizer + SysexMessage + System + SystemColor + SystemException + SystemFlavorMap + TAG_ALTERNATE_IIOP_ADDRESS + TAG_CODE_SETS + TAG_INTERNET_IOP + TAG_JAVA_CODEBASE + TAG_MULTIPLE_COMPONENTS + TAG_ORB_TYPE + TAG_POLICIES + TAG_RMI_CUSTOM_MAX_STREAM_FORMAT + TCKind + THREAD_POLICY_ID + TIMEOUT + TRANSACTION_MODE + TRANSACTION_REQUIRED + TRANSACTION_ROLLEDBACK + TRANSACTION_UNAVAILABLE + TRANSIENT + TRANSPORT_RETRY + TabExpander + TabSet + TabStop + TabableView + TabbedPaneUI + TableCellEditor + TableCellRenderer + TableColumn + TableColumnModel + TableColumnModelEvent + TableColumnModelListener + TableHeaderUI + TableModel + TableModelEvent + TableModelListener + TableUI + TableView + TabularData + TabularDataSupport + TabularType + TagElement + TaggedComponent + TaggedComponentHelper + TaggedComponentHolder + TaggedProfile + TaggedProfileHelper + TaggedProfileHolder + Target + TargetDataLine + TargetedNotification + Templates + TemplatesHandler + Text + TextAction + TextArea + TextAttribute + TextComponent + TextEvent + TextField + TextHitInfo + TextInputCallback + TextLayout + TextLayout.CaretPolicy + TextListener + TextMeasurer + TextOutputCallback + TextSyntax + TextUI + TexturePaint + Thread + Thread.State + Thread.UncaughtExceptionHandler + ThreadDeath + ThreadFactory + ThreadGroup + ThreadInfo + ThreadLocal + ThreadMXBean + ThreadPolicy + ThreadPolicyOperations + ThreadPolicyValue + ThreadPoolExecutor + ThreadPoolExecutor.AbortPolicy + ThreadPoolExecutor.CallerRunsPolicy + ThreadPoolExecutor.DiscardOldestPolicy + ThreadPoolExecutor.DiscardPolicy + Throwable + Tie + TileObserver + Time + TimeLimitExceededException + TimeUnit + TimeZone + TimeoutException + Timer + Timer + Timer + TimerAlarmClockNotification + TimerMBean + TimerNotification + TimerTask + Timestamp + Timestamp + TitledBorder + TooManyListenersException + ToolBarUI + ToolTipManager + ToolTipUI + Toolkit + Track + TransactionRequiredException + TransactionRolledbackException + TransactionService + TransactionalWriter + TransferHandler + Transferable + TransformAttribute + Transformer + TransformerConfigurationException + TransformerException + TransformerFactory + TransformerFactoryConfigurationError + TransformerHandler + Transmitter + Transparency + TreeCellEditor + TreeCellRenderer + TreeExpansionEvent + TreeExpansionListener + TreeMap + TreeModel + TreeModelEvent + TreeModelListener + TreeNode + TreePath + TreeSelectionEvent + TreeSelectionListener + TreeSelectionModel + TreeSet + TreeUI + TreeWillExpandListener + TrustAnchor + TrustManager + TrustManagerFactory + TrustManagerFactorySpi + Type + TypeCode + TypeCodeHolder + TypeInfo + TypeInfoProvider + TypeMismatch + TypeMismatch + TypeMismatch + TypeMismatchHelper + TypeMismatchHelper + TypeNotPresentException + TypeVariable + Types + UID + UIDefaults + UIDefaults.ActiveValue + UIDefaults.LazyInputMap + UIDefaults.LazyValue + UIDefaults.ProxyLazyValue + UIManager + UIManager.LookAndFeelInfo + UIResource + ULongLongSeqHelper + ULongLongSeqHolder + ULongSeqHelper + ULongSeqHolder + UNKNOWN + UNKNOWN + UNSUPPORTED_POLICY + UNSUPPORTED_POLICY_VALUE + URI + URIException + URIResolver + URISyntax + URISyntaxException + URL + URLClassLoader + URLConnection + URLDecoder + URLEncoder + URLStreamHandler + URLStreamHandlerFactory + URLStringHelper + USER_EXCEPTION + UShortSeqHelper + UShortSeqHolder + UTFDataFormatException + UUID + UndeclaredThrowableException + UndoManager + UndoableEdit + UndoableEditEvent + UndoableEditListener + UndoableEditSupport + UnexpectedException + UnicastRemoteObject + UnionMember + UnionMemberHelper + UnknownEncoding + UnknownEncodingHelper + UnknownError + UnknownException + UnknownFormatConversionException + UnknownFormatFlagsException + UnknownGroupException + UnknownHostException + UnknownHostException + UnknownObjectException + UnknownServiceException + UnknownUserException + UnknownUserExceptionHelper + UnknownUserExceptionHolder + UnmappableCharacterException + UnmarshalException + UnmodifiableClassException + UnmodifiableSetException + UnrecoverableEntryException + UnrecoverableKeyException + Unreferenced + UnresolvedAddressException + UnresolvedPermission + UnsatisfiedLinkError + UnsolicitedNotification + UnsolicitedNotificationEvent + UnsolicitedNotificationListener + UnsupportedAddressTypeException + UnsupportedAudioFileException + UnsupportedCallbackException + UnsupportedCharsetException + UnsupportedClassVersionError + UnsupportedEncodingException + UnsupportedFlavorException + UnsupportedLookAndFeelException + UnsupportedOperationException + UserDataHandler + UserException + Util + UtilDelegate + Utilities + VMID + VM_ABSTRACT + VM_CUSTOM + VM_NONE + VM_TRUNCATABLE + Validator + ValidatorHandler + ValueBase + ValueBaseHelper + ValueBaseHolder + ValueExp + ValueFactory + ValueHandler + ValueHandlerMultiFormat + ValueInputStream + ValueMember + ValueMemberHelper + ValueOutputStream + VariableHeightLayoutCache + Vector + VerifyError + VersionSpecHelper + VetoableChangeListener + VetoableChangeListenerProxy + VetoableChangeSupport + View + ViewFactory + ViewportLayout + ViewportUI + VirtualMachineError + Visibility + VisibilityHelper + VoiceStatus + Void + VolatileImage + WCharSeqHelper + WCharSeqHolder + WStringSeqHelper + WStringSeqHolder + WStringValueHelper + WeakHashMap + WeakReference + WebRowSet + WildcardType + Window + WindowAdapter + WindowConstants + WindowEvent + WindowFocusListener + WindowListener + WindowStateListener + WrappedPlainView + WritableByteChannel + WritableRaster + WritableRenderedImage + WriteAbortedException + Writer + WrongAdapter + WrongAdapterHelper + WrongPolicy + WrongPolicyHelper + WrongTransaction + WrongTransactionHelper + WrongTransactionHolder + X500Principal + X500PrivateCredential + X509CRL + X509CRLEntry + X509CRLSelector + X509CertSelector + X509Certificate + X509Certificate + X509EncodedKeySpec + X509ExtendedKeyManager + X509Extension + X509KeyManager + X509TrustManager + XAConnection + XADataSource + XAException + XAResource + XMLConstants + XMLDecoder + XMLEncoder + XMLFilter + XMLFilterImpl + XMLFormatter + XMLGregorianCalendar + XMLParseException + XMLReader + XMLReaderAdapter + XMLReaderFactory + XPath + XPathConstants + XPathException + XPathExpression + XPathExpressionException + XPathFactory + XPathFactoryConfigurationException + XPathFunction + XPathFunctionException + XPathFunctionResolver + XPathVariableResolver + Xid + XmlReader + XmlWriter + ZipEntry + ZipException + ZipFile + ZipInputStream + ZipOutputStream + ZoneView + _BindingIteratorImplBase + _BindingIteratorStub + _DynAnyFactoryStub + _DynAnyStub + _DynArrayStub + _DynEnumStub + _DynFixedStub + _DynSequenceStub + _DynStructStub + _DynUnionStub + _DynValueStub + _IDLTypeStub + _NamingContextExtStub + _NamingContextImplBase + _NamingContextStub + _PolicyStub + _Remote_Stub + _ServantActivatorStub + _ServantLocatorStub + + + abstract + case + catch + class + def + do + else + extends + false + final + finally + for + forSome + if + implicit + import + lazy + match + new + null + object + override + package + private + protected + requires + return + sealed + super + this + throw + trait + true + try + type + val + var + while + with + yield + + + boolean + byte + char + double + float + int + long + short + unit + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/scheme.xml b/kate/part/syntax/data/scheme.xml new file mode 100644 index 00000000..46e5c9aa --- /dev/null +++ b/kate/part/syntax/data/scheme.xml @@ -0,0 +1,436 @@ + + + + + + + <= + < + = + => + >= + > + - + / + *,* + *) + + + + + #\nul + #\soh + #\stx + #\etx + #\eot + #\enq + #\ack + #\bel + #\bs + #\ht + #\nl + #\vt + #\np + #\cr + #\so + #\si + #\dle + #\dc1 + #\dc2 + #\dc3 + #\dc4 + #\nak + #\syn + #\etb + #\can + #\em + #\sub + #\esc + #\fs + #\gs + #\rs + #\us + #\space + #\sp + #\newline + #\nl + #\tab + #\ht + #\backspace + #\bs + #\return + #\cr + #\page + #\np + #\null + #\nul + + + define + define* + define-accessor + define-class + defined? + define-generic + define-macro + define-method + define-module + define-private + define-public + define*-public + define-reader-ctor + define-syntax + define-syntax-macro + defmacro + defmacro* + defmacro*-public + + + abs + acos + and + angle + append + applymap + asin + assoc + assq + assv + atan + begin + boolean? + break + caaaar + caaadr + caaar + caadar + caaddr + caadr + caar + cadaar + cadadr + cadar + caddar + cadddr + caddr + cadr + call/cc + call-with-current-continuation + call-with-input-file + call-with-output-file + call-with-values + car + case + catch + cdaaar + cdaadr + cdaar + cdadar + cdaddr + cdadr + cdar + cddaar + cddadr + cddar + cdddar + cddddr + cdddr + cddr + cdr + ceiling + char-alphabetic? + char-ci>=? + char-ci>? + char-ci=? + char-ci<=? + char-downcase + char->integer + char>=? + char>? + char=? + char? + char-lower-case? + char<?c + char<=? + char-numeric? + char-ready? + char-upcase + char-upper-case? + char-whitespace? + close-input-port + close-output-port + complex? + cond + cons + continue + cos + current-input-port + current-output-port + denominator + display + do + dynamic-wind + else + eof-object? + eq? + equal? + eqv? + eval + even? + exact->inexact + exact? + exp + expt + floor + force + for-each + gcd + har-ci<? + if + imag-part + inexact->exact + inexact? + input-port? + integer->char + integer? + interaction-environment + lambda + lcm + length + let + let* + letrec + letrec-syntax + let-syntax + list->string + list + list? + list-ref + list-tail + load + log + magnitude + make-polar + make-rectangular + make-string + make-vector + max + member + memq + memv + min + modulo + negative? + newline + not + null-environment + null? + number? + number->string + numerator + odd? + open-input-file + open-output-file + or + output-port? + pair? + peek-char + port? + positive? + procedure? + quotient + rational? + rationalize + read-char + read + real? + real-part + remainder + reverse + round + scheme-report-environment + set-car! + set-cdr! + sin + sqrt + string-append + string-ci>=? + string-ci>? + string-ci=? + string-ci<=? + string-ci<? + string-copy + string-fill! + string>=? + string>? + string->list + string->number + string->symbol + string=? + string + string? + string-length + string<=? + string<? + string-ref + string-set! + substring + symbol->string + symbol? + syntax-rules + tan + transcript-off + transcript-on + truncate + values + vector-fill! + vector->listlist->vector + vector + vector? + vector-length + vector-ref + vector-set! + while + with-input-from-file + with-output-to-file + write-char + write + zero? + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/sci.xml b/kate/part/syntax/data/sci.xml new file mode 100644 index 00000000..0a792e1b --- /dev/null +++ b/kate/part/syntax/data/sci.xml @@ -0,0 +1,1191 @@ + + + + + + do + else + for + if + elseif + end + select + then + while + + + abort + break + quit + return + resume + pause + + + function + endfunction + + + error + warning + + + %F + %f + %T + %t + %e + %pi + %inf + %i + %z + %io + %s + %nan + $ + %eps + MSDOS + + + zpell + zpch2 + zpch1 + zpbutt + zgrid + zeros + zeropen + ZCROSS_f + yulewalk + xtitle + xtape + xstringl + xstringb + xstring + xsetm + xsetech + xset + xselect + xsegs + xsave + xs2fig + xrpoly + xrects + xrect + xpolys + xpoly + xpause + xnumb + xname + x_message_modeless + x_message + x_mdialog + x_matrix + xload + xlfont + xinit + xinfo + xgrid + xgraduate + xgetmouse + xgetfile + xgetech + xget + xfrect + xfpolys + xfpoly + xfarcs + xfarc + xend + x_dialog + xdel + xclip + xclick + xclear + xclea + x_choose + x_choices + xchange + xbasr + xbasimp + xbasc + xaxis + xarrows + xarcs + xarc + WRITEC_f + write4b + write + writb + winsid + window + wigner + wiener + whos + who + whereis + whereami + where + what + wfir + WFILE_f + wavwrite + wavread + warning + WaitMsg + varn + varargout + varargin + user + unsetmenu + unobs + unix_x + unix_w + unix_s + unix_g + unix + unique + union + ulink + ui_observer + uint8 + uint32 + uint16 + uimenu + uicontrol + typeof + typename + type + trzeros + triu + trisolve + tril + trianfml + trfmod + TRASH_f + translatepaths + trans_closure + trans + trace + toeplitz + tlist + TK_SetVar + TK_GetVar + TK_EvalStr + TK_EvalFile + titlepage + timer + time_id + TIME_f + tilda + tf2ss + tf2des + TEXT_f + texprint + testmatrix + tdinit + TCLSS_f + tanm + tanhm + tanh + tangent + TANBLK_f + tan + systmat + systems + system + syssize + syslin + sysfact- + sysdiag + sysconv + symbols + sylv + sylm + svplot + svd + sva + supernode + SUPER_f + sum + successors + subplot + subgraph + subf + strsubst + strong_con_nodes + strong_connex + stripblanks + strings + string + strindex + strcat + str2code + STOP_f + st_ility + st_deviation + startup + star + standard_output + standard_origin + standard_input + standard_draw + standard_define + stacksize + stabil + ssrand + ssprint + sskf + sscanf + ss2tf + ss2ss + ss2des + srkf + srfaur + squarewave + square + sqrtm + sqrt + sqroot + spzeros + sprintf + sprand + spones + SPLIT_f + split_edge + splin + spget + speye + specfact + spec + spcompack + spchol + sparse + spantwo + spanplus + spaninter + sp2adj + sound + sort + SOM_f + solve + %sn + smooth + sm2ss + sm2des + slash + size + sinm + sinhm + sinh + sincd + sinc + SINBLK_f + sin + simp_mode + simp + signm + sign + showprofile + show_nodes + show_graph + show_arcs + shortest_path + sgrid + Sgrayplot + Sfgrayplot + sfact + setscicosvars + setmenu + setfield + setbpt + set + sensi + SendMsg + semidef + semicolumn + semi + SELECT_f + secto3d + sd2sci + SCOPXY_f + SCOPE_f + scilink + ScilabEval + scilab + scifunc_block + scicos_model + scicos_menus + scicos_main + scicos_link + scicosim + scicos_graphics + scicos_cpr + scicos_block + scicos + sciargs + sci2map + sci2for + sci2exp + schur + scanf_conversion + scanf + scaling + SAWTOOTH_f + savewave + save_graph + save + SAT_f + SAMPLEHOLD_f + salesman + rtitr + rref + rpem + rowshuff + rowregul + rowinout + rowcompr + rowcomp + routh_t + round + rotate + roots + rlist + ric_desc + riccati + ricc + RFILE_f + residu + replot + repfreq + remezb + remez + RELAY_f + reglin + REGISTER_f + recur + real + readmps + READC_f + readc_ + readb + read4b + read + rdivf + rcond + rational + rat + rank + range + randpencil + RAND_f + rand + quote + quit + quaskro + quapro + QUANT_f + qr + qassign + pwd + pvm_tidtohost + pvm_start + pvm_spawn_independent + pvm_spawn + pvm_set_timer + pvm_send + pvm_sci2f77 + pvm_reduce + pvm_recv + pvm_probe + pvm_mytid + pvm_lvgroup + pvm_kill + pvm_joingroup + pvm_halt + pvm_gsize + pvm_get_timer + pvm_getinst + pvm_exit + pvm_error + pvm_delhosts + pvmd3 + pvm_config + pvm_bufinfo + pvm_bcast + pvm_addhosts + pvm + pspect + psmall + projspec + projsl + proj + profile + PROD_f + prod + printing + printf_conversion + printf + print + predef + predecessors + prbs_a + ppol + power + POWBLK_f + POSTONEG_f + portrait + portr3d + poly + polfact + polarplot + polar + pol2tex + pol2str + pol2des + pmodulo + p_margin + plzr + plus + plotprofile + plot_graph + plotframe + plot3d3 + plot3d2 + plot3d1 + plot3d + plot2d4 + plot2d3 + plot2d2 + plot2d1 + plot2d + plot + playsnd + pipe_network + pinv + phc + phasemag + pfss + pertrans + perfect_match + percent + penlaur + pencan + pen2ea + pdiv + pbig + path_2_nodes + part + parrot + parents + paramfplot2d + param3d1 + param3d + overloading + OUT_f + orth + or + optim + ones + oldsave + oldload + ode_root + odeoptions + ode_discrete + odedi + odedc + ode + obsvss + obsv_mat + obs_gram + observer + obscont1 + obscont + nyquist + numer + null + not + norm + noisegen + nodes_degrees + nodes_2_path + node_number + nnz + nlev + nf3d + newfun + newest + netwindows + netwindow + netclose + neighbors + nehari + NEGTOPOS_f + narsimul + names + MUX_f + mulf + mu2lin + mtlb_sparse + mtlb_save + mtlb_mode + mtlb_load + mtell + msscanf + msprintf + mseek + mscanf + mrfit + mputstr + mputl + mput + mps2linpro + mprintf + mopen + modulo + mode + mlist + min_weight_tree + minus + minss + minreal + min_qcost_flow + min_lcost_flow2 + min_lcost_flow1 + min_lcost_cflow + mini + MIN_f + mine + min + milk_drop + mgetstr + mgetl + mgeti + mget + mfscanf + mfprintf + mfile2sci + mfft + MFCLCK_f + metanet_sync + metanet + mesh2d + mese + meof + median + mean + mclose + MCLOCK_f + mclearerr + m_circle + maxi + max_flow + MAX_f + max_clique + max_cap_path + max + matrix + matrices + Matplot1 + Matplot + mat_2_graph + markp2ss + mapsound + manedit + man + make_graph + macrovar + macro + macr2lst + macglov + lyap + lusolve + luget + lufact + ludel + lu + ltitr + lstcat + lsslist + lqr + lqg_ltr + lqg2stan + lqg + lqe + lotest + LOOKUP_f + logspace + logm + LOGBLK_f + log2 + log10 + log + locate + loadwave + load_graph + load + lmitool + lmisolver + list + linspace + linsolve + linpro + link + linfn + linf + lines + line_graph + lindquist + lin2mu + lin + lib + lgfft + lft + lex_sort + levin + lev + less + leqr + length + legends + left + leastsq + ldivf + ldiv + lcmdiag + lcm + lcf + lattp + lattn + lasterror + kroneck + kron + krac2 + kpure + knapsack + keyboard + kernel + karmarkar + kalm + %k + jmat + isreal + isoview + isnan + isinf + isglobal + iserror + isdef + is_connex + invsyslin + invr + inv_coeff + INVBLK_f + inv + inttype + inttrap + intsplin + INTRPLBLK_f + INTRP2BLK_f + intppty + intl + intg + intersect + intersci + interpln + interp + integrate + INTEGRAL_f + intdec + intc + int8 + int3d + int32 + int2d + int16 + int + insertion + input + IN_f + imrep2ss + impl + im_inv + imag + ilib_gen_Make + ilib_gen_loader + ilib_gen_gateway + ilib_for_link + ilib_compile + ilib_build + iirlp + iirgroup + iir + IFTHEL_f + ieee + iconvert + hypermatrices + hypermat + htrianr + hrmt + householder + hotcolormap + host + horner + h_norm + histplot + hist3d + h_inf_st + h_inf + hilb + hex2dec + hess + hermit + %helps + help + h_cl + havewindow + hat + hankelsv + hank + hamilton + HALT_f + halt + h2norm + gtild + gstacksize + gspec + gsort + gschur + group + gr_menu + grep + graypolarplot + grayplot + graycolormap + graph_union + graph_sum + graph_simp + graph_power + graph-list + Graphics + graph_diameter + graph_complement + graph_center + graph_2_mat + grand + graduate + gpeche + g_margin + G_make + global + glist + glever + givens + girth + gfrancis + gfare + getversion + getvalue + getsymbol + getscicosvars + getpid + GetMsg + getmark + getlinestyle + getio + get_function_path + getfont + getfield + getf + getenv + getdate + getd + getcwd + getcolor + getblocklabel + get + geom3d + GENSQR_f + GENSIN_f + gen_net + genmarkov + genlib + genfac3d + GENERIC_f + GENERAL_f + gcf + gcd + gcare + gammaln + gamma + gamitg + gainplot + GAIN_f + GAINBLK_f + fusee + funptr + funcprot + fun2string + fullrfk + fullrf + full + fstair + fstabst + fspecg + fsolve + fsfirlin + fscanfMat + fscanf + frmag + frfit + frexp + freson + freq + frep2tf + fprintfMat + fprintf + fplot3d1 + fplot3d + fplot2d + fourplan + fort + formatman + format + flts + floor + fix + fit_dat + find_path + findobj + findm + find_freq + find + filter + fileinfo + file + figure + fgrayplot + fft + ffilt + feval + feedback + fec + fcontour2d + fcontour + fchamp + faurre + factors + fac3d + eye + extraction + external + expm + EXPBLK_f + exp + exit + exists + execstr + ExecScilab + ExeclScilab + ExecAppli + exec + excel2sci + Example + EVTGEN_f + EVTDLY_f + evstr + EVENTSCOPE_f + evans + eval3dp + eval3d + eval + error + errclear + errcatch + errbar + erfcx + erfc + erf + ereduc + equil1 + equil + equal + eqiir + eqfir + emptystr + empty + ell1mag + eigenmarkov + edit_curv + edit + edge_number + dtsi + dt_ility + dsimul + dscr + driver + drawaxis + dragrect + double + dot + DLSS_f + DLR_f + DLRADAPT_f + dlgamma + dispfile + dispbpt + disp + diophant + diary + diag + dhnorm + dft + detr + determ + det + DestroyLink + des2tf + des2ss + derivative- + derivat + denom + DEMUX_f + demos + delmenu + delip + delete_nodes + delete_arcs + delbpt + DELAYV_f + DELAY_f + degree + deff + dec2hex + debug + ddp + dcf + dbphi + date + datafit + dassl + dasrt + czt + cycle_basis + CURV_f + curblock + cumsum + cumprod + ctr_gram + cspect + csim + CreateLink + cothm + coth + cotg + cosm + coshm + cosh + COSBLK_f + cos + corr + copfac + convstr + convol + convex_hull + contrss + contract_edge + contr + contourf + contour2di + contour2d + contour + cont_mat + cont_frm + CONST_f + con_nodes + connex + conj + cond + companion + comp + Communications + colregul + colormap + colon + colnew + colinout + colcompr + colcomp + coffg + coff + coeff + code2str + cmndred + cmb_lin + CLSS_f + cls2dls + CLR_f + close + CLOCK_f + CLKSPLIT_f + CLKSOMV_f + CLKSOM_f + CLKOUTV_f + CLKOUT_f + CLKINV_f + CLKIN_f + c_link + CLINDUMMY_f + clearglobal + clearfun + clear + clean + classmarkov + circuit + chsolve + chol + chfact + chepol + check_graph + cheb2mag + cheb1mag + chdir + chart + champ1 + champ + chaintest + chain_struct + cepstrum + ceil + cdft + cdfpoi + cdfnor + cdfnbn + cdfgam + cdffnc + cdff + cdfchn + cdfchi + cdfbin + cdfbet + ccontrg + casc + canon + call + calfrq + calerf + cainv + bvode + buttmag + bstap + boucle + boolean + bool2s + bode + bloc2ss + bloc2exp + black + binomial + bilin + BIGSOM_f + bifish + bezout + best_match + bessely + besselk + besselj + besseli + bdiag + bandwr + balreal + balanc + backslash + auwrite + auread + augment + atanm + atanhm + atanh + atan + %asn + asinm + asinhm + asinh + asin + ascii + articul + artest + arsimul + armax1 + armax + armac + arma2p + arma + arl2 + arhnk + argn + arc_number + arc_graph + apropos + ans + ANIMXY_f + ANDLOG_f + and + analyze + analpf + amell + alufunctions + AFFICH_f + aff2ab + adj_lists + adj2sp + add_node + addmenu + addinter + addf + add_edge + addcolor + AdCommunications + acosm + acoshm + acosh + acos + ABSBLK_f + abs + abinv + abcd + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/scss.xml b/kate/part/syntax/data/scss.xml new file mode 100644 index 00000000..9aa1ad80 --- /dev/null +++ b/kate/part/syntax/data/scss.xml @@ -0,0 +1,852 @@ + + + + +]> + + + + + + + + + azimuth + background + background-attachment + background-break + background-clip + background-color + background-image + background-position + background-origin + background-repeat + border + border-bottom + border-bottom-color + border-bottom-style + border-bottom-width + border-collapse + border-color + border-left + border-left-color + border-left-style + border-left-width + border-right + border-right-color + border-right-style + border-right-width + border-spacing + border-style + border-top + border-top-color + border-top-style + border-top-width + border-width + bottom + caption-side + clear + clip + color + content + counter-increment + counter-reset + cue + cue-after + cue-before + cursor + direction + display + elevation + empty-cells + float + font + font-family + font-size + font-size-adjust + font-stretch + font-style + font-variant + font-weight + font-stretch + height + left + letter-spacing + line-height + list-style + list-style-image + list-style-keyword + list-style-position + list-style-type + margin + margin-bottom + margin-left + margin-right + margin-top + marker-offset + max-height + max-width + min-height + min-width + orphans + outline + outline-color + outline-style + outline-width + overflow + padding + padding-bottom + padding-left + padding-right + padding-top + page + page-break-after + page-break-before + page-break-inside + pause + pause-after + pause-before + pitch + pitch-range + play-during + position + quotes + richness + right + size + speak + speak-header + speak-numeral + speak-punctuation + speech-rate + stress + table-layout + text-align + text-decoration + text-decoration-color + text-indent + text-shadow + text-transform + top + unicode-bidi + vertical-align + visibility + voice-family + volume + white-space + widows + width + word-spacing + z-index + + + animation-name + animation-duration + animation-iteration + animation-direction + animation-delay + animation-play-state + animation-fill-mode + animation-timing-function + background-size + border-bottom-image + border-bottom-left-image + border-bottom-left-radius + border-bottom-right-image + border-bottom-right-radius + border-collapse + border-corner-image + border-image + border-left-image + border-radius + border-right-image + border-top-image + border-top-left-image + border-top-left-radius + border-top-right-image + border-top-right-radius + box-align + box-direction + box-flex + box-shadow + box-sizing + column-count + column-fill + column-gap + column-rule-color + column-rule-style + column-rule-width + column-span + column-wisth + hyphens + linear-gradient + opacity + outline + outline-offset + overflow-x + overflow-y + pointer-events + resize + rotation + rotation-point + table-layout + text-overflow + text-shadow + text-wrap + transform-origin + transition + transition-property + transition-duration + word-wrap + + + -moz-animation-name + -moz-animation-duration + -moz-animation-iteration + -moz-animation-direction + -moz-animation-delay + -moz-animation-play-state + -moz-animation-fill-mode + -moz-background-size + -moz-border-image + -moz-border-bottom-colors + -moz-border-left-colors + -moz-border-radius + -moz-border-radius-topleft + -moz-border-radius-topright + -moz-border-radius-bottomleft + -moz-border-radius-bottomright + -moz-border-right-colors + -moz-border-top-colors + -moz-box + -moz-box-flex + -moz-box-shadow + -moz-box-sizing + -moz-column-count + -moz-column-gap + -moz-hyphens + -moz-linear-gradient + -moz-opacity + -moz-outline-style + -moz-perspective + -moz-radial-gradient + -moz-resize + -moz-transform + -moz-transform-origin + -moz-transform-style + -moz-transition + -moz-transition-property + -moz-transition-duration + + + -o-background-size + -o-linear-gradient + -o-text-overflow + -o-transition + -o-transform-origin + + + konq_bgpos_x + konq_bgpos_y + -khtml-background-size + -khtml-border-top-left-radius + -khtml-border-top-right-radius + -khtml-border-bottom-left-radius + -khtml-border-bottom-right-radius + -khtml-border-radius + -khtml-box-shadow + -khtml-opacity + + + -webkit-appearance + -webkit-animation-name + -webkit-animation-duration + -webkit-animation-iteration + -webkit-animation-direction + -webkit-animation-delay + -webkit-animation-play-state + -webkit-animation-fill-mode + -webkit-background-size + -webkit-border-image + -webkit-border-bottom-colors + -webkit-border-left-colors + -webkit-border-radius + -webkit-border-right-colors + -webkit-border-top-colors + -webkit-border-top-left-radius + -webkit-border-top-right-radius + -webkit-border-bottom-left-radius + -webkit-border-bottom-right-radius + -webkit-border-radius-bottomleft + -webkit-border-radius-bottomright + -webkit-box-flex + -webkit-box-reflect + -webkit-box-shadow + -webkit-box-sizing + -webkit-column-count + -webkit-column-gap + -webkit-hyphens + -webkit-linear-gradient + -webkit-gradient + -webkit-perspective + -webkit-text-fill-color + -webkit-text-stroke-color + -webkit-text-stroke-width + -webkit-text-size-adjust + -webkit-transform + -webkit-transform-origin + -webkit-transform-style + -webkit-transition + -webkit-transition-property + -webkit-transition-duration + + + filter + zoom + -ms-animation-name + -ms-animation-duration + -ms-animation-iteration + -ms-animation-direction + -ms-animation-delay + -ms-animation-play-state + -ms-animation-fill-mode + -ms-box-sizing + -ms-filter + -ms-interpolation-mode + -ms-linear-gradient + -ms-text-size-adjust + -ms-transform + -ms-transition + + + font-family + font-size + font-stretch + font-style + font-variant + font-weight + unicode-range + units-per-em + src + panose-1 + stemv + stemh + slope + cap-height + x-height + ascent + descent + widths + bbox + definition-src + baseline + centerline + mathline + topline + + + + inherit + none + hidden + dotted + dashed + solid + double + groove + ridge + inset + outset + xx-small + x-small + small + medium + large + x-large + xx-large + smaller + larger + italic + oblique + small-caps + normal + bold + bolder + lighter + light + 100 + 200 + 300 + 400 + 500 + 600 + 700 + 800 + 900 + transparent + repeat + repeat-x + repeat-y + no-repeat + baseline + sub + super + top + text-top + middle + bottom + text-bottom + left + right + center + justify + konq-center + disc + circle + square + box + decimal + decimal-leading-zero + lower-roman + upper-roman + lower-greek + lower-alpha + lower-latin + upper-alpha + upper-latin + hebrew + armenian + georgian + cjk-ideographic + hiragana + katakana + hiragana-iroha + katakana-iroha + inline + inline-block + block + list-item + run-in + compact + marker + table + inline-table + table-row-group + table-header-group + table-footer-group + table-row + table-column-group + table-column + table-cell + table-caption + auto + crosshair + default + pointer + move + e-resize + ne-resize + nw-resize + n-resize + se-resize + sw-resize + s-resize + w-resize + text + wait + help + above + absolute + always + avoid + below + bidi-override + blink + both + capitalize + caption + clip + close-quote + collapse + condensed + crop + cross + ellipsis + ellipsis-word + embed + expanded + extra-condensed + extra-expanded + fixed + hand + hide + higher + icon + inside + invert + landscape + level + line-through + loud + lower + lowercase + ltr + menu + message-box + mix + narrower + no-close-quote + no-open-quote + nowrap + open-quote + outside + overline + portrait + pre + pre-line + pre-wrap + relative + rtl + scroll + semi-condensed + semi-expanded + separate + show + small-caption + static + static-position + status-bar + thick + thin + ultra-condensed + ultra-expanded + underline + uppercase + visible + wider + break + serif + sans-serif + cursive + fantasy + monospace + border-box + content-box + -epub-hyphens + + + + + aqua + black + blue + cyan + fuchsia + gray + green + lime + maroon + navy + olive + purple + red + silver + teal + white + yellow + ActiveBorder + ActiveCaption + AppWorkspace + Background + ButtonFace + ButtonHighlight + ButtonShadow + ButtonText + CaptionText + GrayText + Highlight + HighlightText + InactiveBorder + InactiveCaption + InactiveCaptionText + InfoBackground + InfoText + Menu + MenuText + Scrollbar + ThreeDDarkShadow + ThreeDFace + ThreeDHighlight + ThreeDLightShadow + ThreeDShadow + Window + WindowFrame + WindowText + + + + url + attr + rect + rgb + rgba + hsl + hsla + counter + counters + + + local + format + + + expression + + + + + all + aural + braille + embossed + handheld + print + projection + screen + tty + tv + + + + hover + link + visited + active + focus + first-child + last-child + only-child + first-of-type + last-of-type + only-of-type + first-letter + first-line + before + after + selection + root + empty + target + enabled + disabled + checked + indeterminate + nth-child + nth-last-child + nth-of-type + nth-last-of-type + not + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/sed.xml b/kate/part/syntax/data/sed.xml new file mode 100644 index 00000000..095d440a --- /dev/null +++ b/kate/part/syntax/data/sed.xml @@ -0,0 +1,267 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/sgml.xml b/kate/part/syntax/data/sgml.xml new file mode 100644 index 00000000..2292c319 --- /dev/null +++ b/kate/part/syntax/data/sgml.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/sieve.xml b/kate/part/syntax/data/sieve.xml new file mode 100644 index 00000000..95ef08b1 --- /dev/null +++ b/kate/part/syntax/data/sieve.xml @@ -0,0 +1,67 @@ + + + + + + + require + if + elsif + else + discard + stop + fileinto + keep + reject + redirect + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/sisu.xml b/kate/part/syntax/data/sisu.xml new file mode 100644 index 00000000..11f9e50b --- /dev/null +++ b/kate/part/syntax/data/sisu.xml @@ -0,0 +1,157 @@ + + + + + + class + const + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/sml.xml b/kate/part/syntax/data/sml.xml new file mode 100644 index 00000000..eeae780f --- /dev/null +++ b/kate/part/syntax/data/sml.xml @@ -0,0 +1,104 @@ + + + + + + abstype + and + andalso + as + case + do + datatype + else + end + eqtype + exception + false + fn + fun + functor + handle + if + in + include + infix + infixr + let + local + nonfix + of + op + open + orelse + raise + rec + sharing + sig + signature + struct + structure + then + true + type + val + where + with + withtype + while + + + unit + int + real + char + string + substring + word + ref + array + vector + bool + list + option + order + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/spice.xml b/kate/part/syntax/data/spice.xml new file mode 100644 index 00000000..03b183ac --- /dev/null +++ b/kate/part/syntax/data/spice.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/sql-mysql.xml b/kate/part/syntax/data/sql-mysql.xml new file mode 100644 index 00000000..585d460c --- /dev/null +++ b/kate/part/syntax/data/sql-mysql.xml @@ -0,0 +1,474 @@ + + + + + + + ACCESS + ADD + ALL + ALTER + ANALYZE + AND + AS + ASC + AUTO_INCREMENT + BDB + BERKELEYDB + BETWEEN + BOTH + BY + CASCADE + CASE + CHANGE + + CHARSET + COLUMN + COLUMNS + CONSTRAINT + CREATE + CROSS + CURRENT_DATE + CURRENT_TIME + CURRENT_TIMESTAMP + DATABASE + DATABASES + DAY_HOUR + DAY_MINUTE + DAY_SECOND + DEC + DEFAULT + DELAYED + DELETE + DESC + DESCRIBE + DISTINCT + DISTINCTROW + DROP + ELSE + ENCLOSED + ESCAPED + EXISTS + EXPLAIN + FIELDS + FOR + FOREIGN + FROM + FULLTEXT + FUNCTION + GRANT + GROUP + HAVING + HIGH_PRIORITY + IF + IGNORE + IN + INDEX + INFILE + INNER + INNODB + INSERT + INTERVAL + INTO + IS + JOIN + KEY + KEYS + KILL + LEADING + LEFT + LIKE + LIMIT + LINES + LOAD + LOCK + LOW_PRIORITY + MASTER_SERVER_ID + MATCH + MRG_MYISAM + NATIONAL + NATURAL + NOT + NULL + NUMERIC + ON + OPTIMIZE + OPTION + OPTIONALLY + OR + ORDER + OUTER + OUTFILE + PARTIAL + PRECISION + PRIMARY + PRIVILEGES + PROCEDURE + PURGE + READ + REFERENCES + REGEXP + RENAME + REPLACE + REQUIRE + RESTRICT + RETURNS + REVOKE + RIGHT + RLIKE + SELECT + SET + SHOW + SONAME + SQL_BIG_RESULT + SQL_CALC_FOUND_ROWS + SQL_SMALL_RESULT + SSL + STARTING + STRAIGHT_JOIN + STRIPED + TABLE + TABLES + TERMINATED + THEN + TO + TRAILING + TRUNCATE + TYPE + UNION + UNIQUE + UNLOCK + UNSIGNED + UPDATE + USAGE + USE + USER_RESOURCES + USING + VALUES + VARYING + WHEN + WHERE + WHILE + WITH + WRITE + XOR + YEAR_MONTH + ZEROFILL + + + + + - + * + / + || + = + != + <> + < + <= + > + >= + ~= + ^= + := + => + ** + .. + + + + ASCII + BIN + BIT_LENGTH + CHAR + CHARACTER_LENGTH + CHAR_LENGTH + CONCAT + CONCAT_WS + CONV + ELT + EXPORT_SET + FIELD + FIND_IN_SET + HEX + INSERT + INSTR + LCASE + LEFT + LENGTH + LOAD_FILE + LOCATE + LOWER + LPAD + LTRIM + MAKE_SET + MID + OCT + OCTET_LENGTH + ORD + POSITION + QUOTE + REPEAT + REPLACE + REVERSE + RIGHT + RPAD + RTRIM + SOUNDEX + SPACE + SUBSTRING + SUBSTRING_INDEX + TRIM + UCASE + UPPER + + ABS + ACOS + ASIN + ATAN + ATAN2 + CEILING + COS + COT + DEGREES + EXP + FLOOR + GREATEST + LEAST + LN + LOG + LOG10 + LOG2 + MOD + PI + POW + POWER + RADIANS + RAND + ROUND + SIGN + SIN + SQRT + TAN + + ADDDATE + CURDATE + CURRENT_DATE + CURRENT_TIME + CURRENT_TIMESTAMP + CURTIME + DATE_ADD + DATE_FORMAT + DATE_SUB + DAYNAME + DAYOFMONTH + DAYOFWEEK + DAYOFYEAR + EXTRACT + FROM_DAYS + FROM_UNIXTIME + HOUR + MINUTE + MONTH + MONTHNAME + NOW + PERIOD_ADD + PERIOD_DIFF + QUARTER + SECOND + SEC_TO_TIME + SUBDATE + SYSDATE + TIME_FORMAT + TIME_TO_SEC + TO_DAYS + UNIX_TIMESTAMP + WEEK + WEEKDAY + YEAR + YEARWEEK + + CAST + CONVERT + + AES_DECRYPT + AES_ENCRYPT + BENCHMARK + BIT_COUNT + CONNECTION_ID + DATABASE + DECODE + DES_DECRYPT + DES_ENCRYPT + ENCODE + ENCRYPT + FORMAT + FOUND_ROWS + GET_LOCK + INET_ATON + INET_NTOA + IS_FREE_LOCK + LAST_INSERT_ID + MASTER_POS_WAIT + MD5 + PASSWORD + RELEASE_LOCK + SESSION_USER + SHA + SHA1 + SYSTEM_USER + USER + VERSION + + AVG + BIT_AND + BIT_OR + COUNT + MAX + MIN + STD + STDDEV + SUM + + + + BINARY + BLOB + CHAR + CHARACTER + ENUM + LONGBLOB + LONGTEXT + MEDIUMBLOB + MEDIUMTEXT + TEXT + TINYBLOB + TINYTEXT + VARBINARY + VARCHAR + + + BIGINT + BIT + BOOL + BOOLEAN + DEC + DECIMAL + DOUBLE + FIXED + FLOAT + INT + INTEGER + LONG + MEDIUMINT + MIDDLEINT + NUMERIC + TINYINT + REAL + SERIAL + SMALLINT + + DATE + DATETIME + TIME + TIMESTAMP + YEAR + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/sql-postgresql.xml b/kate/part/syntax/data/sql-postgresql.xml new file mode 100644 index 00000000..a8b1101a --- /dev/null +++ b/kate/part/syntax/data/sql-postgresql.xml @@ -0,0 +1,812 @@ + + + + + + + ABORT + ACCESS + ACTION + ADD + ADMIN + AFTER + AGGREGATE + ALIAS + ALL + ALLOCATE + ALTER + ANALYSE + ANALYZE + ANY + ARE + AS + ASC + ASENSITIVE + ASSERTION + ASSIGNMENT + ASYMMETRIC + AT + ATOMIC + AUTHORIZATION + BACKWARD + BEFORE + BEGIN + BETWEEN + BINARY + BOTH + BREADTH + BY + C + CACHE + CALL + CALLED + CARDINALITY + CASCADE + CASCADED + CASE + CAST + CATALOG + CATALOG_NAME + CHAIN + CHARACTERISTICS + CHARACTER_LENGTH + CHARACTER_SET_CATALOG + CHARACTER_SET_NAME + CHARACTER_SET_SCHEMA + CHAR_LENGTH + CHECK + CHECKED + CHECKPOINT + CLASS + CLASS_ORIGIN + CLOB + CLOSE + CLUSTER + COALESCE + COBOL + COLLATE + COLLATION + COLLATION_CATALOG + COLLATION_NAME + COLLATION_SCHEMA + COLUMN + COLUMN_NAME + COMMAND_FUNCTION + COMMAND_FUNCTION_CODE + COMMENT + COMMIT + COMMITTED + COMPLETION + CONDITION_NUMBER + CONNECT + CONNECTION + CONNECTION_NAME + CONSTRAINTS + CONSTRAINT + CONSTRAINT_CATALOG + CONSTRAINT_NAME + CONSTRAINT_SCHEMA + CONSTRUCTOR + CONTAINS + CONTINUE + CONVERT + COPY + CORRESPONDING + COUNT + CREATE + CREATEDB + CREATEUSER + CROSS + CUBE + CURRENT + CURRENT_DATE + CURRENT_PATH + CURRENT_ROLE + CURRENT_TIME + CURRENT_TIMESTAMP + CURRENT_USER + CURSOR + CURSOR_NAME + CYCLE + DATA + DATABASE + DATE + DATETIME_INTERVAL_CODE + DATETIME_INTERVAL_PRECISION + DAY + DEALLOCATE + DEC + DECIMAL + DECLARE + DEFAULT + DEFERRABLE + DEFERRED + DEFINED + DEFINER + DELETE + DELIMITERS + DEPTH + DEREF + DESC + DESCRIBE + DESCRIPTOR + DESTROY + DESTRUCTOR + DETERMINISTIC + DIAGNOSTICS + DICTIONARY + DISCONNECT + DISPATCH + DISTINCT + DO + DOMAIN + DOUBLE + DROP + DYNAMIC + DYNAMIC_FUNCTION + DYNAMIC_FUNCTION_CODE + EACH + ELSE + ENCODING + ENCRYPTED + END + END-EXEC + EQUALS + ESCAPE + EVERY + EXCEPT + EXCEPTION + EXCLUSIVE + EXEC + EXECUTE + EXISTING + EXISTS + EXPLAIN + EXTERNAL + FALSE + FETCH + FINAL + FIRST + FOR + FORCE + FOREIGN + FORTRAN + FORWARD + FOUND + FREE + FREEZE + FROM + FULL + FUNCTION + G + GENERAL + GENERATED + GET + GLOBAL + GO + GOTO + GRANT + GRANTED + GROUP + GROUPING + HANDLER + HAVING + HIERARCHY + HOLD + HOST + HOUR + IDENTITY + IGNORE + ILIKE + IMMEDIATE + IMMUTABLE + IMPLEMENTATION + IN + INCREMENT + INDEX + INDICATOR + INFIX + INHERITS + INITIALIZE + INITIALLY + INNER + INOUT + INPUT + INSENSITIVE + INSERT + INSTANCE + INSTANTIABLE + INSTEAD + INTERSECT + INTERVAL + INTO + INVOKER + IS + ISNULL + ISOLATION + ITERATE + JOIN + K + KEY + KEY_MEMBER + KEY_TYPE + LANCOMPILER + LANGUAGE + LARGE + LAST + LATERAL + LEADING + LEFT + LENGTH + LESS + LEVEL + LIKE + LIMIT + LISTEN + LOAD + LOCAL + LOCALTIME + LOCALTIMESTAMP + LOCATION + LOCATOR + LOCK + LOWER + M + MAP + MATCH + MAX + MAXVALUE + MESSAGE_LENGTH + MESSAGE_OCTET_LENGTH + MESSAGE_TEXT + METHOD + MIN + MINUTE + MINVALUE + MOD + MODE + MODIFIES + MODIFY + MODULE + MONTH + MORE + MOVE + MUMPS + NAME + NAMES + NATIONAL + NATURAL + NEW + NEXT + NO + NOCREATEDB + NOCREATEUSER + NONE + NOT + NOTHING + NOTIFY + NOTNULL + NULL + NULLABLE + NULLIF + NUMBER + NUMERIC + OBJECT + OCTET_LENGTH + OF + OFF + OFFSET + OIDS + OLD + ON + ONLY + OPEN + OPERATION + OPERATOR + OPTION + OPTIONS + ORDER + ORDINALITY + OUT + OUTER + OUTPUT + OVERLAPS + OVERLAY + OVERRIDING + OWNER + PAD + PARAMETER + PARAMETERS + PARAMETER_MODE + PARAMETER_NAME + PARAMETER_ORDINAL_POSITION + PARAMETER_SPECIFIC_CATALOG + PARAMETER_SPECIFIC_NAME + PARAMETER_SPECIFIC_SCHEMA + PARTIAL + PASCAL + PASSWORD + PATH + PENDANT + PLI + POSITION + POSTFIX + PRECISION + PREFIX + PREORDER + PREPARE + PRESERVE + PRIMARY + PRIOR + PRIVILEGES + PROCEDURAL + PROCEDURE + PUBLIC + READ + READS + REAL + RECURSIVE + REF + REFERENCES + REFERENCING + REINDEX + RELATIVE + RENAME + REPEATABLE + REPLACE + RESET + RESTRICT + RESULT + RETURN + RETURNED_LENGTH + RETURNED_OCTET_LENGTH + RETURNED_SQLSTATE + RETURNS + REVOKE + RIGHT + ROLE + ROLLBACK + ROLLUP + ROUTINE + ROUTINE_CATALOG + ROUTINE_NAME + ROUTINE_SCHEMA + ROW + ROWS + ROW_COUNT + RULE + SAVEPOINT + SCALE + SCHEMA + SCHEMA_NAME + SCOPE + SCROLL + SEARCH + SECOND + SECTION + SECURITY + SELECT + SELF + SENSITIVE + SEQUENCE + SERIALIZABLE + SERVER_NAME + SESSION + SESSION_USER + SET + SETOF + SETS + SHARE + SHOW + SIMILAR + SIMPLE + SIZE + SOME + SOURCE + SPACE + SPECIFIC + SPECIFICTYPE + SPECIFIC_NAME + SQL + SQLCODE + SQLERROR + SQLEXCEPTION + SQLSTATE + SQLWARNING + STABLE + START + STATE + STATEMENT + STATIC + STATISTICS + STDIN + STDOUT + STRUCTURE + STYLE + SUBCLASS_ORIGIN + SUBLIST + SUBSTRING + SUM + SYMMETRIC + SYSID + SYSTEM + SYSTEM_USER + TABLE + TABLE_NAME + TEMP + TEMPLATE + TEMPORARY + TERMINATE + THAN + THEN + TIMEZONE_HOUR + TIMEZONE_MINUTE + TO + TOAST + TRAILING + TRANSACTION + TRANSACTIONS_COMMITTED + TRANSACTIONS_ROLLED_BACK + TRANSACTION_ACTIVE + TRANSFORM + TRANSFORMS + TRANSLATE + TRANSLATION + TREAT + TRIGGER + TRIGGER_CATALOG + TRIGGER_NAME + TRIGGER_SCHEMA + TRIM + TRUE + TRUNCATE + TRUSTED + TYPE + UNCOMMITTED + UNDER + UNENCRYPTED + UNION + UNIQUE + UNKNOWN + UNLISTEN + UNNAMED + UNNEST + UNTIL + UPDATE + UPPER + USAGE + USER + USER_DEFINED_TYPE_CATALOG + USER_DEFINED_TYPE_NAME + USER_DEFINED_TYPE_SCHEMA + USING + VACUUM + VALID + VALUE + VALUES + VARIABLE + VARYING + VERBOSE + VERSION + VIEW + VOLATILE + WHEN + WHENEVER + WHERE + WHILE + WITH + WITHOUT + WORK + WRITE + YEAR + ZONE + + + + + - + * + / + || + |/ + ||/ + ! + !! + @ + & + | + # + << + >> + % + ^ + = + != + <> + < + <= + > + >= + ~ + ~* + !~ + !~* + ^= + := + => + ** + .. + AND + OR + NOT + + ## + && + &< + &> + <-> + <^ + >^ + ?# + ?- + ?-| + @-@ + ?| + ?|| + @@ + ~= + + <<= + >>= + + + + ABS + CBRT + CEIL + DEGREES + EXP + FLOOR + LN + LOG + MOD + PI + POW + RADIANS + RANDOM + ROUND + SIGN + SQRT + TRUNC + + ACOS + ASIN + ATAN + ATAN2 + COS + COT + SIN + TAN + + BIT_LENGTH + CHAR_LENGTH + CHARACTER_LENGTH + LOWER + OCTET_LENGTH + POSITION + SUBSTRING + TRIM + UPPER + + ASCII + BTRIM + CHR + CONVERT + INITCAP + LENGTH + LPAD + LTRIM + PG_CLIENT_ENCODING + REPEAT + RPAD + RTRIM + STRPOS + SUBSTR + TO_ASCII + TRANSLATE + ENCODE + DECODE + + TO_CHAR + TO_DATE + TO_TIMESTAMP + TO_NUMBER + + AGE + DATE_PART + DATE_TRUNC + EXTRACT + ISFINITE + NOW + TIMEOFDAY + TIMESTAMP + EXTRACT + + AREA + BOX + CENTER + DIAMETER + HEIGHT + ISCLOSED + ISOPEN + PCLOSE + NPOINT + POPEN + RADIUS + WIDTH + + BOX + CIRCLE + LSEG + PATH + POINT + POLYGON + + BROADCAST + HOST + MASKLEN + SET_MASKLEN + NETMASK + NETWORK + ABBREV + + NEXTVAL + CURRVAL + SETVAL + + COALESCE + NULLIF + + HAS_TABLE_PRIVILEGE + PG_GET_VIEWDEF + PG_GET_RULEDEF + PG_GET_INDEXDEF + PG_GET_USERBYID + OBJ_DESCRIPTION + COL_DESCRIPTION + + AVG + COUNT + MAX + MIN + STDDEV + SUM + VARIANCE + + + BIGINT + BIGSERIAL + BIT + BIT VARYING + BOOL + BOOLEAN + BOX + BYTEA + CHAR + CHARACTER + CHARACTER VARYING + CIDR + CIRCLE + DATE + DECIMAL + DOUBLE PRECISION + FLOAT8 + INET + INT + INT2 + INT4 + INT8 + INTEGER + INTERVAL + LINE + LSEG + LZTEXT + MACADDR + MONEY + NUMERIC + OID + PATH + POINT + POLYGON + REAL + SERIAL + SERIAL8 + SMALLINT + TEXT + TIME + TIMESTAMP + TIMESTAMP WITH TIMEZONE + TIMESTAMPTZ + TIMETZ + VARBIT + VARCHAR + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/sql.xml b/kate/part/syntax/data/sql.xml new file mode 100644 index 00000000..316eed42 --- /dev/null +++ b/kate/part/syntax/data/sql.xml @@ -0,0 +1,956 @@ + + + + + + + + + ACCESS + ACCOUNT + ADD + ADMIN + ADMINISTER + ADVISE + AFTER + AGENT + ALL + ALLOCATE + ALL_ROWS + ALTER + ANALYZE + ANCILLARY + AND + ANY + ARCHIVE + ARCHIVELOG + AS + ASC + ASSERTION + ASSOCIATE + AT + ATTRIBUTE + ATTRIBUTES + AUDIT + AUTHENTICATED + AUTHID + AUTHORIZATION + AUTOALLOCATE + AUTOEXTEND + AUTOMATIC + BACKUP + BECOME + BEFORE + BEGIN + BEHALF + BETWEEN + BINDING + BITMAP + BLOCK + BLOCK_RANGE + BODY + BOTH + BOUND + BREAK + BROADCAST + BTITLE + BUFFER_POOL + BUILD + BULK + BY + CACHE + CACHE_INSTANCES + CALL + CANCEL + CASCADE + CASE + CATEGORY + CHAINED + CHANGE + CHECK + CHECKPOINT + CHILD + CHOOSE + CHUNK + CLASS + CLEAR + CLONE + CLOSE + CLOSE_CACHED_OPEN_CURSORS + CLUSTER + COALESCE + COLUMN + COLUMNS + COLUMN_VALUE + COMMENT + COMMIT + COMMITTED + COMPATIBILITY + COMPILE + COMPLETE + COMPOSITE_LIMIT + COMPRESS + COMPUTE + CONNECT + CONNECT_TIME + CONSIDER + CONSISTENT + CONSTANT + CONSTRAINT + CONSTRAINTS + CONTAINER + CONTENTS + CONTEXT + CONTINUE + CONTROLFILE + COPY + COST + CPU_PER_CALL + CPU_PER_SESSION + CREATE + CREATE_STORED_OUTLINES + CROSS + CUBE + CURRENT + CURSOR + CYCLE + DANGLING + DATA + DATABASE + DATAFILE + DATAFILES + DBA + DDL + DEALLOCATE + DEBUG + DECLARE + DEFAULT + DEFERRABLE + DEFERRED + DEFINER + DEGREE + DELETE + DEMAND + DESC + DETERMINES + DICTIONARY + DIMENSION + DIRECTORY + DISABLE + DISASSOCIATE + DISCONNECT + DISKGROUP + DISMOUNT + DISTINCT + DISTRIBUTED + DOMAIN + DROP + DYNAMIC + EACH + ELSE + ELSIF + EMPTY + ENABLE + END + ENFORCE + ENTRY + ESCAPE + ESTIMATE + EVENTS + EXCEPT + EXCEPTION + EXCEPTIONS + EXCHANGE + EXCLUDING + EXCLUSIVE + EXEC + EXECUTE + EXISTS + EXPIRE + EXPLAIN + EXPLOSION + EXTENDS + EXTENT + EXTENTS + EXTERNALLY + FAILED_LOGIN_ATTEMPTS + FALSE + FAST + FILE + FILTER + FIRST_ROWS + FLAGGER + FLASHBACK + FLUSH + FOLLOWING + FOR + FORCE + FOREIGN + FREELIST + FREELISTS + FRESH + FROM + FULL + FUNCTION + FUNCTIONS + GENERATED + GLOBAL + GLOBALLY + GLOBAL_NAME + GRANT + GROUP + GROUPS + HASH + HASHKEYS + HAVING + HEADER + HEAP + HIERARCHY + HOUR + ID + IDENTIFIED + IDENTIFIER + IDGENERATORS + IDLE_TIME + IF + IMMEDIATE + IN + INCLUDING + INCREMENT + INCREMENTAL + INDEX + INDEXED + INDEXES + INDEXTYPE + INDEXTYPES + INDICATOR + INITIAL + INITIALIZED + INITIALLY + INITRANS + INNER + INSERT + INSTANCE + INSTANCES + INSTEAD + INTERMEDIATE + INTERSECT + INTO + INVALIDATE + IS + ISOLATION + ISOLATION_LEVEL + JAVA + JOIN + KEEP + KEY + KILL + LABEL + LAYER + LEADING + LEFT + LESS + LEVEL + LIBRARY + LIKE + LIMIT + LINK + LIST + LOCAL + LOCATOR + LOCK + LOCKED + LOGFILE + LOGGING + LOGICAL_READS_PER_CALL + LOGICAL_READS_PER_SESSION + LOGOFF + LOGON + LOOP + MANAGE + MANAGED + MANAGEMENT + MASTER + MATERIALIZED + MAXARCHLOGS + MAXDATAFILES + MAXEXTENTS + MAXINSTANCES + MAXLOGFILES + MAXLOGHISTORY + MAXLOGMEMBERS + MAXSIZE + MAXTRANS + MAXVALUE + MEMBER + MERGE + METHOD + MINEXTENTS + MINIMIZE + MINIMUM + MINUS + MINUTE + MINVALUE + MODE + MODIFY + MONITORING + MOUNT + MOVE + MOVEMENT + MTS_DISPATCHERS + MULTISET + NAMED + NATURAL + NEEDED + NESTED + NESTED_TABLE_ID + NETWORK + NEVER + NEW + NEXT + NLS_CALENDAR + NLS_CHARACTERSET + NLS_COMP + NLS_CURRENCY + NLS_DATE_FORMAT + NLS_DATE_LANGUAGE + NLS_ISO_CURRENCY + NLS_LANG + NLS_LANGUAGE + NLS_NUMERIC_CHARACTERS + NLS_SORT + NLS_SPECIAL_CHARS + NLS_TERRITORY + NO + NOARCHIVELOG + NOAUDIT + NOCACHE + NOCOMPRESS + NOCYCLE + NOFORCE + NOLOGGING + NOMAXVALUE + NOMINIMIZE + NOMINVALUE + NOMONITORING + NONE + NOORDER + NOOVERRIDE + NOPARALLEL + NORELY + NORESETLOGS + NOREVERSE + NORMAL + NOSEGMENT + NOSORT + NOT + NOTHING + NOVALIDATE + NOWAIT + NULL + NULLS + OBJNO + OBJNO_REUSE + OF + OFF + OFFLINE + OID + OIDINDEX + OLD + ON + ONLINE + ONLY + OPCODE + OPEN + OPERATOR + OPTIMAL + OPTIMIZER_GOAL + OPTION + OR + ORDER + ORGANIZATION + OUT + OUTER + OUTLINE + OVER + OVERFLOW + OVERLAPS + OWN + PACKAGE + PACKAGES + PARALLEL + PARAMETERS + PARENT + PARTITION + PARTITIONS + PARTITION_HASH + PARTITION_RANGE + PASSWORD + PASSWORD_GRACE_TIME + PASSWORD_LIFE_TIME + PASSWORD_LOCK_TIME + PASSWORD_REUSE_MAX + PASSWORD_REUSE_TIME + PASSWORD_VERIFY_FUNCTION + PCTFREE + PCTINCREASE + PCTTHRESHOLD + PCTUSED + PCTVERSION + PERCENT + PERMANENT + PLAN + PLSQL_DEBUG + POST_TRANSACTION + PREBUILT + PRECEDING + PREPARE + PRESENT + PRESERVE + PREVIOUS + PRIMARY + PRIOR + PRIVATE + PRIVATE_SGA + PRIVILEGE + PRIVILEGES + PROCEDURE + PROFILE + PUBLIC + PURGE + QUERY + QUEUE + QUOTA + RANDOM + RANGE + RBA + READ + READS + REBUILD + RECORDS_PER_BLOCK + RECOVER + RECOVERABLE + RECOVERY + RECYCLE + REDUCED + REFERENCES + REFERENCING + REFRESH + RELY + RENAME + REPLACE + RESET + RESETLOGS + RESIZE + RESOLVE + RESOLVER + RESOURCE + RESTRICT + RESTRICTED + RESUME + RETURN + RETURNING + REUSE + REVERSE + REVOKE + REWRITE + RIGHT + ROLE + ROLES + ROLLBACK + ROLLUP + ROW + ROWNUM + ROWS + RULE + SAMPLE + SAVEPOINT + SCAN + SCAN_INSTANCES + SCHEMA + SCN + SCOPE + SD_ALL + SD_INHIBIT + SD_SHOW + SEGMENT + SEG_BLOCK + SEG_FILE + SELECT + SELECTIVITY + SEQUENCE + SERIALIZABLE + SERVERERROR + SESSION + SESSIONS_PER_USER + SESSION_CACHED_CURSORS + SET + SHARE + SHARED + SHARED_POOL + SHRINK + SHUTDOWN + SINGLETASK + SIZE + SKIP + SKIP_UNUSABLE_INDEXES + SNAPSHOT + SOME + SORT + SOURCE + SPECIFICATION + SPLIT + SQL_TRACE + STANDBY + START + STARTUP + STATEMENT_ID + STATIC + STATISTICS + STOP + STORAGE + STORE + STRUCTURE + SUBMULTISET + SUBPARTITION + SUBPARTITIONS + SUCCESSFUL + SUMMARY + SUPPLEMENTAL + SUSPEND + SWITCH + SYNONYM + SYSDBA + SYSOPER + SYSTEM + SYS_OP_BITVEC + SYS_OP_ENFORCE_NOT_NULL$ + SYS_OP_NOEXPAND + SYS_OP_NTCIMG$ + TABLE + TABLES + TABLESPACE + TABLESPACE_NO + TABNO + TEMPFILE + TEMPORARY + THAN + THE + THEN + THREAD + THROUGH + TIMEOUT + TIMEZONE_HOUR + TIMEZONE_MINUTE + TIME_ZONE + TO + TOPLEVEL + TRACE + TRACING + TRAILING + TRANSACTION + TRANSITIONAL + TRIGGER + TRIGGERS + TRUE + TRUNCATE + TYPE + TYPES + UNARCHIVED + UNBOUND + UNBOUNDED + UNDO + UNIFORM + UNION + UNIQUE + UNLIMITED + UNLOCK + UNRECOVERABLE + UNTIL + UNUSABLE + UNUSED + UPDATABLE + UPDATE + UPD_INDEXES + UPPPER + USAGE + USE + USER_DEFINED + USE_STORED_OUTLINES + USING + VALIDATE + VALIDATION + VALUES + VIEW + WHEN + WHENEVER + WHERE + WHILE + WITH + WITHOUT + WORK + WRITE + + + + + - + * + / + || + = + != + <> + < + <= + > + >= + ~= + ^= + := + => + ** + .. + + + ABS + ACOS + ADD_MONTHS + ASCII + ASCIISTR + ASIN + ATAN + ATAN2 + AVG + BFILENAME + BIN_TO_NUM + BITAND + CARDINALITY + CAST + CEIL + CHARTOROWID + CHR + COALESCE + COLLECT + COMPOSE + CONCAT + CONVERT + CORR + CORR_K + CORR_S + COS + COSH + COUNT + COVAR_POP + COVAR_SAMP + CUME_DIST + CURRENT_DATE + CURRENT_TIMESTAMP + CV + DBTIMEZONE + DECODE + DECOMPOSE + DENSE_RANK + DEPTH + DEREF + DUMP + EMPTY_BLOB + EMPTY_CLOB + EXISTSNODE + EXP + EXTRACT + EXTRACTVALUE + FIRST + FIRST_VALUE + FLOOR + FROM_TZ + GREATEST + GROUP_ID + GROUPING + GROUPING_ID + HEXTORAW + INITCAP + INSTR + INSTRB + LAG + LAST + LAST_DAY + LAST_VALUE + LEAD + LEAST + LENGTH + LENGTHB + LN + LNNVL + LOCALTIMESTAMP + LOG + LOWER + LPAD + LTRIM + MAKE_REF + MAX + MEDIAN + MIN + MOD + MONTHS_BETWEEN + NANVL + NCHR + NEW_TIME + NEXT_DAY + NLS_CHARSET_DECL_LEN + NLS_CHARSET_ID + NLS_CHARSET_NAME + NLS_INITCAP + NLS_LOWER + NLS_UPPER + NLSSORT + NTILE + NULLIF + NUMTODSINTERVAL + NUMTOYMINTERVAL + NVL + NVL2 + ORA_HASH + ORA_ROWSCN + PERCENT_RANK + PERCENTILE_CONT + PERCENTILE_DISC + POWER + POWERMULTISET + POWERMULTISET_BY_CARDINALITY + PRESENTNNV + PRESENTV + RANK + RATIO_TO_REPORT + RAWTOHEX + RAWTONHEX + REF + REFTOHEX + REGEXP_INSTR + REGEXP_LIKE + REGEXP_REPLACE + REGEXP_SUBSTR + REGR_SLOPE + REGR_INTERCEPT + REGR_COUNT + REGR_R2 + REGR_AVGX + REGR_AVGY + REGR_SXX + REGR_SYY + REGR_SXY + REMAINDER + ROUND + ROW_NUMBER + ROWIDTOCHAR + ROWIDTONCHAR + RPAD + RTRIM + SCN_TO_TIMESTAMP + SESSIONTIMEZONE + SIGN + SIN + SINH + SOUNDEX + SQRT + STATS_BINOMIAL_TEST + STATS_CROSSTAB + STATS_F_TEST + STATS_KS_TEST + STATS_MODE + STATS_MW_TEST + STATS_ONE_WAY_ANOVA + STATS_T_TEST_ONE + STATS_T_TEST_PAIRED + STATS_T_TEST_INDEP + STATS_T_TEST_INDEPU + STATS_WSR_TEST + STDDEV + STDDEV_POP + STDDEV_SAMP + SUBSTR + SUBSTRB + SUM + SYS_CONNECT_BY_PATH + SYS_CONTEXT + SYS_DBURIGEN + SYS_EXTRACT_UTC + SYS_GUID + SYS_TYPEID + SYS_XMLAGG + SYS_XMLGEN + SYSDATE + SYSTIMESTAMP + TAN + TANH + TIMESTAMP_TO_SCN + TO_BINARY_DOUBLE + TO_BINARY_FLOAT + TO_CHAR + TO_CLOB + TO_DATE + TO_DSINTERVAL + TO_LOB + TO_MULTI_BYTE + TO_NCHAR + TO_NCLOB + TO_NUMBER + TO_SINGLE_BYTE + TO_TIMESTAMP + TO_TIMESTAMP_TZ + TO_YMINTERVAL + TRANSLATE + TREAT + TRIM + TRUNC + TZ_OFFSET + UID + UNISTR + UPDATEXML + UPPER + USER + USERENV + VALUE + VAR_POP + VAR_SAMP + VARIANCE + VSIZE + WIDTH_BUCKET + XMLAGG + XMLCOLATTVAL + XMLCONCAT + XMLELEMENT + XMLFOREST + XMLSEQUENCE + XMLTRANSFORM + + + ANYDATA + ANYDATASET + ANYTYPE + ARRAY + BFILE + BINARY_DOUBLE + BINARY_FLOAT + BINARY_INTEGER + BLOB + BOOLEAN + CFILE + CHAR + CHARACTER + CLOB + DATE + DAY + DBURITYPE + DEC + DECIMAL + DOUBLE + FLOAT + FLOB + HTTPURITYPE + INT + INTEGER + INTERVAL + LOB + LONG + MLSLABEL + MONTH + NATIONAL + NCHAR + NCLOB + NUMBER + NUMERIC + NVARCHAR + OBJECT + PLS_INTEGER + PRECISION + RAW + REAL + RECORD + ROWID + SECOND + SINGLE + SMALLINT + TIME + TIMESTAMP + URIFACTORYTYPE + URITYPE + UROWID + VARCHAR + VARCHAR2 + VARRAY + VARYING + XMLTYPE + YEAR + ZONE + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/stata.xml b/kate/part/syntax/data/stata.xml new file mode 100644 index 00000000..f28600e8 --- /dev/null +++ b/kate/part/syntax/data/stata.xml @@ -0,0 +1,3231 @@ + + + + + + + + + addlabels + addlabopts + addplot + bar + barwidth + bin + blabel + caption + center + circle + circle_hollow + color + cols + combine + dot + draw + dropline + frequency + grid + hbar + imargin + labsize + legend + margin + medthick + mlabangle + mlabcolor + mlabel + mlabgap + mlabposition + mlabsize + mlabstyle + mlabtextstyle + mlabvposition + msymbol + name + nodraw + nogrid + over + plotregion + position + ring + rows + scale + size + start + subtitle + t1title + t2title + text + textsize + title + xcommon + xlabel + xline + xscale + xsize + xtitle + ycommon + ylabel + yline + yscale + ysize + ytitle + zero + + + + background + bg + black + blue + bluishgray + brown + cranberry + cyan + dimgray + dkgreen + dknavy + dkorange + ebblue + ebg + edkblue + eggshell + eltblue + eltgreen + emerald + emidblue + erose + fg + foreground + forest_green + gold + gray + green + gs + khaki + lavender + lime + ltblue + ltbluishgray + ltkhaki + magenta + maroon + midblue + midgreen + mint + navy + none + olive + olive_teal + orange + orange_red + pink + purple + red + sand + sandb + sienna + stone + teal + white + yellow + + + + accum + as + ascending + BASE + be + begin + bfgs + bhhh + brrweight + clean + close + clpatt [ern] + clwidth + cole + color + confirm + continue + cov(unstr) + cov(unstructured) + covariance + dash + define + deft + delmacs + detail + dev + deviations + dfp + difficult + dirname + do + effects + eform + else + emdots + emiterate + emlog + emonly + emtolerance + end + error + estmetric + exchangeable + exit + family + fe + fitted + force + foreach + forvalues + fpc + frequency + function + global + gtolerance + hessian + ic + identity + if + in + independent + intpoints + irr + iterate + jkrweight + lincom + linearized + local + long + ltolerance + macro + manage + meff + meft + minimize + mle + mse + multiplier + noclear + nocons + noconstant + nodots + nofetable + nogroup + noheader + nolog + nolrtest + none + nonrtolerance + noobs + noomit + noretable + nostderr + not + nr + of + offset + or + p + parse + patterns + poststrata + postweight + prefix + program + r2_p + reffects + reml + reset + resid + run + scale + shift + showstep + SITE + size + star + stats + STBPLUS + stfmt + store + strata + syntax + tempfile + tempname + tempvar + tokenize + tolerance + unstructured + until + UPDATES + weight + while + wide + window + x2 + xb + + _b[ + _coeff[ + + ereturn + rreturn + sreturn + + e( + r( + s( + + + + all + BASE + coleq + colfullnames + coln + colnames + constraint + data + dir + display + environment + format + l + label + length + list + nobreak + nofail + permname + PERSONAL + piece + PLUS + quoted + row + roweq + rowfullnames + rown + rownames + SITE + sortedby + STATA + strict + sysdir + tempfile + tempvar + tsnorm + UPDATES + value + variable + + + + allstring + append + args + aweight + born + by + bys + bysort + byte + capture + cfreq + clear + Cns + Co + collinear + columns + comma + compress + connect + console + cpercent + cr + d0 + data + datestring + decode + delimit + depnames + desc + describe + di + diparm_options + display + double + drop + eclass + encode + esample + Ev + exec + fam + fdadescribe + fdasave + fdause + filefilter + float + format + fweight + gen + generate + gr [een] + gradient + hold + include + insert + insheet + int + iweight + k + keep + label + Ld + link + load + long + longstub + macrolen + markout + marksample + maximize + meanonly + mlmatsbysum + mlout + mode + model + more + N + namelen + noextend + nofootnote + noi + noisily + nolabel + nonotes + nopreserve + norescale + noscvars + nosummary + nototal + nrtolerance + obs + odbc + off + oim + on + oneway + opg + order + outfile + outsheet + percent + permanently + post + postclose + postfile + preserve + Psi + pweight + query + qui + quietly + rawsum + red + ren + rename + replace + Replay + report + repost + restore + robust + save + saving + SD + SE + search + set + shownrtolerance + sort + sqlfile + sqlshow + STATA + statistics + sum + summarize + t1title + t2title + tab + tabulate + technique + timer + total + unhold + unique + uniquemaster + uniqusing + use + using + V + values + variable + varlist + VCE + waldtest + width + xlabel + xmlsave + xmluse + yellow + ylabel + yline + + + + + + abs + acos + asin + atan + atan2 + atanh + ceil + cloglog + comb + cos + digamma + exp + floor + invcloglog + invlogit + ln + lnfact + lnfactorial + lngamma + log + log10 + max + min + mod + reldif + round + sign + sin + sqrt + sum + tan + tanh + trigamma + trunc + + + + betaden + Binomial + binorm + binormal + chi2 + chi2tail + dgammapda + dgammapdada + dgammapdadx + dgammapdx + dgammapdxdx + F + Fden + Ftail + gammaden + gammap + ibeta + invbinomial + invchi2 + invchi2tail + invF + invFtail + invgammap + invibeta + invnchi2 + invnFtail + invnibeta + invnorm + invnormal + invttail + nbetaden + nchi2 + nFden + nFtail + nibeta + norm + normal + normalden + normd + npnchi2 + tden + ttail + uniform() + + + + abbrev + char + index + indexnot + length + lower + ltrim + match + plural + proper + real + regexm + regexr + regexs + reverse + rtrim + string + strlen + strlower + strltrim + strmatch + strofreal + strpos + strproper + strreverse + strrtrim + strtrim + strupper + subinstr + subinword + substr + trim + upper + word + wordcount + + + + _caller + autocode + byteorder + chop + clip + cond + e + epsdouble + epsfloat + group + inlist + inrange + irecode + matrix + maxbyte + maxdouble + maxfloat + maxint + maxlong + mi + minbyte + mindouble + minfloat + minint + minlong + missing + r + recode + replay + return + s + scalar + + + + d + date + day + dow + doy + halfyear + mdy + month + quarter + week + year + + + + d + daily + dofd + dofh + dofm + dofq + dofw + dofy + h + halfyearly + hofd + m + mofd + monthly + q + qofd + quarterly + tin + twithin + w + weekly + wofd + y + yearly + yh + ym + yofd + yq + yw + + + + cholesky + colnumb + colsof + corr + det + diag + diag0cnt + el + get + hadamard + I + inv + invsym + issym + issymmetric + J + matmissing + matuniform + mreldif + nullmat + rownumb + rowsof + sweep + syminv + trace + vec + vecdiag + + + + anycount + anymatch + anyvalue + at + concat + count + cut + diff + ends + field + fill + group + groupicodes + head + iqr + kurt + label + last + lname + mad + max + maxlength + maxmode + mdev + mean + median + min + minmode + missing + mode + mtr + nummodemissing + pc prop + pctile + punct + rank + rowfirst + rowlast + rowmax + rowmean + rowmin + rowmiss + rownonmiss + rowsd + rowtotal + sd + seq + skew + std + strok + tag + tail + total + track + trim + truncate + + + + _all + _column + _cons + _dta + _dup + _merge + _n + _newline + _pi + _rc + creturn + c(adopath) + c(adosize) + c(ALPHA) + c(born_date) + c(byteorder) + c(changed) + c(checksum) + c(cmdlen) + c(console) + c(copycolor) + c(current_date) + c(current_time) + c(dirsep) + c(dockable) + c(dockingguides) + c(dp) + c(epsdouble) + c(epsfloat) + c(filedate) + c(filename) + c(flavor) + c(graphics) + c(httpproxy) + c(httpproxyauth) + c(httpproxyhost) + c(httpproxyport) + c(httpproxypw) + c(httpproxyuser) + c(k) + c(level) + c(linegap) + c(linesize) + c(locksplitters) + c(logtype) + c(machine_type) + c(macrolen) + c(matacache) + c(matafavor) + c(matalibs) + c(matalnum) + c(matamofirst) + c(mataoptimize) + c(matastrict) + c(matsize) + c(max_cmdlen) + c(max_k_current) + c(max_k_theory) + c(max_macrolen) + c(max_matsize) + c(max_N_current) + c(max_N_theory) + c(max_width_current) + c(max_width_theory) + c(maxbyte) + c(maxdb) + c(maxdouble) + c(maxfloat) + c(maxint) + c(maxiter) + c(maxlong) + c(maxstrvarlen) + c(maxvar) + c(memory) + c(min_matsize) + c(minbyte) + c(mindouble) + c(minfloat) + c(minint) + c(minlong) + c(mode) + c(Mons) + c(Months) + c(more) + c(N) + c(namelen) + c(os) + c(osdtl) + c(pagesize) + c(persistfv) + c(persistvtopic) + c(pi) + c(printcolor) + c(pwd) + c(rc) + c(reventries) + c(rmsg_time) + c(rmsg) + c(scheme) + c(scrollbufsize) + c(SE) + c(searchdefault) + c(seed) + c(stata_version) + c(sysdir_base) + c(sysdir_oldplace) + c(sysdir_personal) + c(sysdir_plus) + c(sysdir_site) + c(sysdir_stata) + c(sysdir_updates) + c(timeout1) + c(timeout2) + c(trace) + c(tracedepth) + c(traceexpand) + c(tracehilite) + c(traceindent) + c(tracenumber) + c(tracesep) + c(type) + c(update_interval) + c(update_prompt) + c(update_query) + c(varabbrev) + c(varlabelpos) + c(version) + c(virtual) + c(Wdays) + c(Weekdays) + c(width) + c(xptheme) + + + + adosize + checksum + copycolor + dockable + dockingguides + dp + graphics + httpproxy + httpproxyauth + httpproxyhost + httpproxyport + httpproxypw + httpproxyuser + level + linegap + linesize + locksplitters + logtype + matacache + matafavor + matalibs + matalnum + matamofirst + mataoptimize + matastrict + matsize + maxdb + maxiter + maxvar + memory + more + pagesize + persistfv + persistvtopic + printcolor + reventries + rmsg + scheme + scrollbufsize + searchdefault + seed + timeout1 + timeout2 + trace + tracedepth + traceexpand + tracehilite + traceindent + tracenumber + tracesep + type + update_interval + update_prompt + update_query + varabbrev + varlabelpos + version + virtual + xptheme + + + + + _a_cls_msg + _addgph + _assert + _assert_mreldif + _assert_mreldifp + _assert_mreldifs + _assert_obs + _assert_streq + _at + _bigtab + _binperfect + _binperfout + _biplotmat + _brr_sum + _bs_display + _bs_sum + _btcmd + _byoptnotallowed + _ca_parse_normalize + _callerr + _cci + _check_eformopt + _check4gropts + _choice_table + _ckirfset + _cknotsvaroi + _ckvec + _clsarr2list + _cmdxel + _coef_table + _coef_table_header + _confirm_date + _confirm_number_or_date + _copy_mat_stripes + _cpmatnm + _crc2use + _crc4fld + _crcacnt + _crcar1 + _crcause + _crcbin + _crcbygr + _crcchi2 + _crcchkw + _crccip + _crceprs + _crcgldv + _crcglil + _crcird + _crcirr + _crcmeq + _crcnuse + _crcor + _crcphdr + _crcra + _crcrd + _crcrr + _crcseq + _crcshdr + _crcslbl + _crcsrvc + _crcswxx + _crcunab + _crcunit + _crcvarl + _crcwsrv + _crczsku + _cvar + _date2elapsed + _diag2mat + _diparm + _diparm_8 + _dots + _e2r + _egennoby + _evlist + _exp_list_expand + _exp_list_parse + _find_tsops + _fr_area_parse_and_log + _fr_aspect_parse_and_log + _fr_draw_rect + _fr_droplines_draw + _fr_erasearr + _fr_legend_parse_and_log + _fr_merged_implicit + _fr_runlog + _fr_sztextbox_parse_and_log + _fr_tbstyle_parse_and_log + _fr_tedits_parse_and_log + _fr_textbox_parse_and_log + _fr_title_parse_and_log + _fr_x_log_cleanup + _fr_x_log_create + _fracpp + _fracxo + _frr_sztextbox_pnl + _gany + _ganycount + _ganymatch + _ganyvalue + _gconcat + _gcount + _gcut + _gdiff + _gends + _geqany + _get_diparmopts + _get_diparmopts_8 + _get_eformopts + _get_eqspec + _get_gropts + _get_irf + _get_offopt + _getbv + _getcovcorr + _getfilename + _getnewlabelname + _getrhs + _getvarcns + _getxel + _getxel2 + _gfill + _ggroup + _giqr + _gkurt + _gm_edit + _gm_log + _gma + _gmad + _gmax + _gmdev + _gmean + _gmedian + _gmin + _gmode + _gmtr + _gneqany + _gpc + _gpctile + _gr_arrowhead + _gr_atomize_styles + _gr_common_axes + _gr_drawrect + _gr_linkstyles + _gr_symbol_of + _grank + _grfirst + _grlast + _grmax + _grmean + _grmin + _grmiss + _grobs + _growfirst + _growlast + _growmax + _growmean + _growmin + _growmiss + _grownonmiss + _growsd + _growtotal + _grsd + _grsum + _gs_addgrname + _gs_bygraph + _gs_clean_graphlist + _gs_default_bands + _gs_islivefile + _gs_parse_and_log_axoptions + _gs_parse_and_log_axtitle + _gs_parse_and_log_lines + _gs_parse_and_log_tickset + _gs_rdfilehdr + _gs_wrfilehdr + _gs_x_create + _gsd + _gseq + _gskew + _gstd + _gsum + _gtag + _gtotal + _hadamard_verify + _hw_comp + _hw_opt_d0 + _hwsa_comp + _hwsa_opt_d0 + _hwsm_comp + _hwsm_opt_d0 + _isfit + _ivreg_project + _jk_nlegend + _jk_pseudo + _jk_sum + _labels2names + _linemax + _loop_brr + _loop_jknife + _loop_jknife_fw + _loop_jknife_iw + _loop_jknife2 + _loop_rw + _lrtest7 + _m2matrix + _m2scalar + _matplot + _matsort + _mdisplay + _mds_classical + _mds_dataheader + _mds_display + _mds_display_classical + _mds_euclidean + _mds_parse_dopts + _mds_parse_method + _mds_parse_s2d + _mds_s2d + _me_der + _me_der2 + _me_derb + _me_derd + _me_l_der + _me_l_der2 + _mfrmvec + _mka2 + _mkg + _mkkmn + _mksigma + _mkvec + _mprobitestimator + _mtest + _mvec + _no_estat + _no_predict + _nobs + _on_colon_parse + _parmlist + _parse_optexp + _parsewt + _pk_p + _plotpos + _pred_me + _pred_se + _prefix_check4esample + _prefix_checkopt + _prefix_clear + _prefix_command + _prefix_display + _prefix_expand + _prefix_explist + _prefix_footnote + _prefix_getchars + _prefix_getmat + _prefix_legend + _prefix_mlogit + _prefix_model_test + _prefix_note + _prefix_reject + _prefix_relabel_eqns + _prefix_run_error + _prefix_saving + _prefix_title + _prefix_vcenotallowed + _qsort_index + _qsur + _r2e + _repost + _resample_warn + _restore_labels + _returnclear + _rmdcoll + _robust2 + _roccom1 + _roccom1_8 + _rocsen + _rotate_clear + _rotate_text + _score_spec + _set_irf_vars + _shortenpath + _sigfm + _small2dotz + _stata_internalerror + _stcurv + _strip_labels + _sttrend + _stubstar2names + _sum_table + _sumaccum + _sunflower_binar + _svar_cnsmac + _svar_eqmac + _svar_newcns + _svar_post + _svard2 + _svariden + _svaridenlr + _svarlrd2 + _svd + _svy_check_cmdopts + _svy_check_fpc + _svy_check_postw + _svy_check_predict + _svy_check_vce + _svy_fpc_note + _svy_ftest + _svy_ivreg_first + _svy_mkdeff + _svy_mkmeff + _svy_mkvmsp + _svy_mkvsrs + _svy_newrule + _svy_setup + _svy_singleton_note + _svy_subpop + _svy_subpop_note + _svy_summarize + _svy_summarize_legend + _svy_tabulate + _svy2 + _svydes_dlg + _svylc + _svyset + _sw_ood + _ts + _ts_dexp + _ts_exp + _ts_hw + _ts_hwsa + _ts_hwsm + _tsheadr + _tsinchk + _tsma + _ttest + _ttest1 + _ttest2 + _tutends + _var_mka + _varbsf + _vardisprmse + _varfcast + _varfcast_clear + _varfcast_fcast + _varfcast_graph + _varirf + _varsim + _vce_parserun + _vec_ckgraph + _vec_dreduced + _vec_grcroots + _vec_opck + _vec_pgparse + _vec_pgridplots + _vec_postvar + _vecauxdisp + _vecfcast_compute + _vecfcast_compute_w + _vecgetacns + _vecgetcv + _vecgtn + _vecmka + _vecmkapvp + _vecmkce + _vecmkgam + _vecmksi + _vecmktrend + _vecortho + _vecpclean + _vectparse + _vecu + _virf_add + _virf_char + _virf_fck + _virf_mknewfile + _virf_nlen + _virf_use + _writenum + _xtreg_chk_cl + + ac + ac_7 + acprplot + acprplot_7 + adjust + adopath + alpha + ameans + anova_estat + anova_terms + aorder + arch + arch_dr + arch_estat + arch_p + archlm + areg + areg_p + arima + arima_dr + arima_estat + arima_p + asmprobit + asmprobit_estat + asmprobit_lf + asmprobit_p + avplot + avplot_7 + avplots + avplots_7 + bcskew0 + bgodfrey + binreg + bip0_lf + biplot + bipp_lf + bipr_lf + bipr_p + biprobit + bitest + bitesti + bitowt + blogit + bmemsize + boot + bootsamp + bootstrap + bootstrap_8 + boxco_l + boxco_p + boxcox + boxcox_6 + boxcox_p + bprobit + brier + brr + brrstat + bs + bs_7 + bsampl_w + bsample + bsample_7 + bsqreg + bstat + bstat_7 + bstat_8 + bstrap + bstrap_7 + ca + ca_estat + ca_p + cabiplot + camat + canon + canon_8 + canon_8_p + canon_estat + canon_p + caprojection + cc + cchart + cchart_7 + cci + censobs_table + centile + cf + checkdlgfiles + checkhlpfiles + ci + cii + classutil + clear + clo + clog + clog_lf + clog_p + clogi + clogi_sw + clogit + clogit_lf + clogit_p + clogitp + clogl_sw + cloglog + clonevar + clslistarray + cluster + cluster_measures + cluster_stop + cluster_tree + cluster_tree_8 + clustermat + cnr + cnre + cnreg + cnreg_p + cnreg_sw + cnsreg + codebook + collaps4 + collapse + colormult_nb + colormult_nw + compare + conren + contract + copyright + copysource + corc + corr_anti + corr_kmo + corr_smc + corr2data + corrgram + cox_p + cox_sw + coxbase + coxhaz + coxvar + cprplot + cprplot_7 + crc + cross + cs + cscript + cscript_log + csi + ct + ct_is + ctset + ctst_5 + ctst_st + cttost + cumsp + cumsp_7 + cumul + cusum + cusum_7 + cutil + + d + datetof + db + dbeta + de + deff + des + desc + descr + descri + describ + describe + destring + dfbeta + dfgls + dfuller + dirstats + disp_res + disp_s + dotplot + dotplot_7 + dprobit + drawnorm + ds + ds_util + dstdize + duplicates + durbina + dwstat + dydx + egen + eivreg + emdef + eq + ereg + ereg_lf + ereg_p + ereg_sw + ereghet + ereghet_glf + ereghet_glf_sh + ereghet_gp + ereghet_ilf + ereghet_ilf_sh + ereghet_ip + est + est_cfexist + est_cfname + est_clickable + est_expand + est_hold + est_table + est_unhold + est_unholdok + estat + estat_default + estat_summ + estat_vce_only + esti + estimates + etodow + etof + etomdy + expandcl + fac + fact + facto + factor + factor_estat + factor_p + factor_pca_rotated + factor_rotate + factormat + fcast + fcast_compute + fcast_graph + fh_st + fillin + find_hlp_file + findfile + findit + findit_7 + fit + for + for5_0 + fpredict + frac_154 + frac_adj + frac_chk + frac_cox + frac_ddp + frac_dis + frac_dv + frac_in + frac_mun + frac_pp + frac_pq + frac_pv + frac_wgt + frac_xo + fracgen + fracplot + fracplot_7 + fracpoly + fracpred + fron_ex + fron_hn + fron_p + fron_tn + fron_tn2 + frontier + ftodate + ftoe + ftomdy + ftowdate + + gamhet_glf + gamhet_gp + gamhet_ilf + gamhet_ip + gamma + gamma_d2 + gamma_p + gamma_sw + gammahet + gdi_hexagon + gdi_spokes + genrank + genstd + genvmean + gladder + gladder_7 + glim_l01 + glim_l02 + glim_l03 + glim_l04 + glim_l05 + glim_l06 + glim_l07 + glim_l08 + glim_l09 + glim_l10 + glim_l11 + glim_l12 + glim_lf + glim_mu + glim_nw1 + glim_nw2 + glim_nw3 + glim_p + glim_v1 + glim_v2 + glim_v3 + glim_v4 + glim_v5 + glim_v6 + glim_v7 + glm + glm_6 + glm_p + glm_sw + glmpred + glogit + glogit_8 + glogit_p + gmeans + gnbre_lf + gnbreg + gnbreg_5 + gnbreg_p + gomp_lf + gompe_sw + gomper_p + gompertz + gompertzhet + gomphet_glf + gomphet_glf_sh + gomphet_gp + gomphet_ilf + gomphet_ilf_sh + gomphet_ip + gphdot + gphpen + gphprint + gprobi_p + gprobit + gprobit_8 + gr + gr_copy + gr_current + gr_db + gr_describe + gr_dir + gr_draw + gr_draw_replay + gr_drop + gr_edit + gr_editviewopts + gr_example + gr_example2 + gr_export + gr_print + gr_qscheme + gr_query + gr_read + gr_rename + gr_replay + gr_save + gr_set + gr_setscheme + gr_table + gr_undo + gr_use + graph + grebar + greigen + greigen_7 + greigen_8 + grmeanby + grmeanby_7 + gs_fileinfo + gs_filetype + gs_graphinfo + gs_stat + gsort + gwood + h + hadimvo + hareg + hausman + he + heck_d2 + heckma_p + heckman + heckp_lf + heckpr_p + heckprob + hel + help + hereg + hetpr_lf + hetpr_p + hetprob + hettest + hilite + hist + hist_7 + histogram + hlogit + hlu + hmeans + hotel + hotelling + hprobit + hreg + icd9 + icd9_ff + icd9p + iis + impute + imtest + inbase + integ + inten + intreg + intreg_7 + intreg_p + intrg_ll + intrg_ll2 + intrg2_ll + ipolate + iqreg + ir + irf + irf_create + irfm + iri + is_svy + is_svysum + isid + istdize + ivprob_1_lf + ivprob_lf + ivprobit + ivprobit_p + ivreg + ivreg_footnote + ivtob_1_lf + ivtob_lf + ivtobit + ivtobit_p + + jackknife + jacknife + jknife + jknife_6 + jknife_8 + jkstat + joinby + kalarma1 + kap + kap_3 + kapmeier + kappa + kapwgt + kdensity + kdensity_7 + ksm + ksmirnov + ktau + kwallis + labelbook + ladder + levels + levelsof + leverage + lfit + lfit_p + lincom + line + linktest + lloghet_glf + lloghet_glf_sh + lloghet_gp + lloghet_ilf + lloghet_ilf_sh + lloghet_ip + llogi_sw + llogis_p + llogist + llogistic + llogistichet + lnorm_lf + lnorm_sw + lnorma_p + lnormal + lnormalhet + lnormhet_glf + lnormhet_glf_sh + lnormhet_gp + lnormhet_ilf + lnormhet_ilf_sh + lnormhet_ip + lnskew0 + loadingplot + logi + logis_lf + logistic + logistic_p + logit + logit_estat + logit_p + loglogs + logrank + loneway + lookfor + lowess + lowess_7 + lpredict + lrecomp + lroc + lroc_7 + lrtest + lsens + lsens_7 + lsens_x + lstat + ltable + ltable_7 + ltriang + lv + lvr2plot + lvr2plot_7 + + makecns + manova_estat + manova_p + mantel + mat_capp + mat_order + mat_rapp + mata_matdescribe + mata_matsave + mata_matuse + matalabel + matcproc + matlist + matname + matstrik + mcc + mcci + md0_ + md1_ + md1debug_ + md2_ + md2debug_ + mds + mds_estat + mds_p + mdsconfig + mdslong + mdsmat + mdsshepard + mdytoe + mdytof + me_derd + mean + means + median + memsize + meqparse + mer + merg + merge + mfp + mfx + mhelp + mhodds + mixed_ll + mixed_ll_reparm + mkassert + mkmat + mkspline + ml + ml_5 + ml_adjs + ml_bhhhs + ml_c_d + ml_check + ml_clear + ml_cnt + ml_debug + ml_defd + ml_e0 + ml_e0_bfgs + ml_e0_cycle + ml_e0_dfp + ml_e0i + ml_e1 + ml_e1_bfgs + ml_e1_bhhh + ml_e1_cycle + ml_e1_dfp + ml_e2 + ml_e2_cycle + ml_ebfg0 + ml_ebfr0 + ml_ebfr1 + ml_ebh0q + ml_ebhh0 + ml_ebhr0 + ml_ebr0i + ml_ecr0i + ml_edfp0 + ml_edfr0 + ml_edfr1 + ml_edr0i + ml_eds + ml_eer0i + ml_egr0i + ml_elf + ml_elf_bfgs + ml_elf_bhhh + ml_elf_cycle + ml_elf_dfp + ml_elfi + ml_elfs + ml_enr0i + ml_enrr0 + ml_erdu0 + ml_erdu0_bfgs + ml_erdu0_bhhh + ml_erdu0_bhhhq + ml_erdu0_cycle + ml_erdu0_dfp + ml_erdu0_nrbfgs + ml_exde + ml_footnote + ml_geqnr + ml_grad0 + ml_graph + ml_hbhhh + ml_hd0 + ml_hold + ml_init + ml_inv + ml_log + ml_max + ml_mlout + ml_model + ml_nb0 + ml_opt + ml_p + ml_plot + ml_query + ml_rdgrd + ml_repor + ml_s_e + ml_score + ml_searc + ml_technique + ml_unhold + mlf_ + mlog + mlogi + mlogit + mlogit_footnote + mlogit_p + mlopts + mnl0_ + mprobit + mprobit_lf + mprobit_p + mrdu0_ + mrdu1_ + mvdecode + mvencode + mvreg + mvreg_estat + nbreg + nbreg_al + nbreg_lf + nbreg_p + nbreg_sw + newey + newey_7 + newey_p + nl + nl_7 + nl_p + nl_p_7 + nlcom + nlcom_p + nlexp2 + nlexp2_7 + nlexp2a + nlexp2a_7 + nlexp3 + nlexp3_7 + nlgom3 + nlgom3_7 + nlgom4 + nlgom4_7 + nlinit + nllog3 + nllog3_7 + nllog4 + nllog4_7 + nlog_rd + nlogit + nlogit_p + nlogitgen + nlogittree + nlpred + note + notes + nptrend + numlabel + old_ver + olo + olog + ologi + ologi_sw + ologit + ologit_p + ologitp + op_colnm + op_comp + op_diff + op_inv + op_str + opr + opro + oprob + oprob_sw + oprobi + oprobi_p + oprobit + oprobitp + opts_exclusive + orthog + orthpoly + ovtest + + pac + pac_7 + palette + parse_dissim + pause + pca + pca_8 + pca_display + pca_estat + pca_p + pca_rotate + pcamat + pchart + pchart_7 + pchi + pchi_7 + pcorr + pctile + pentium + pergram + pergram_7 + permute + permute_8 + personal + peto_st + pkcollapse + pkcross + pkequiv + pkexamine + pkexamine_7 + pkshape + pksumm + pksumm_7 + pnorm + pnorm_7 + poisgof + poiss_lf + poiss_sw + poisso_p + poisson + poisson_estat + pperron + prais + prais_e + prais_e2 + prais_p + predict + predictnl + print + prob + probi + probit + probit_estat + probit_p + proc_time + procoverlay + procrustes + procrustes_estat + procrustes_p + profiler + prop + proportion + prtest + prtesti + pwcorr + qby + qbys + qchi + qchi_7 + qladder + qladder_7 + qnorm + qnorm_7 + qqplot + qqplot_7 + qreg + qreg_c + qreg_p + qreg_sw + quadchk + quantile + quantile_7 + range + ranksum + ratio + rchart + rchart_7 + rcof + recast + recode + reg + reg3 + reg3_p + regdw + regr + regre + regre_p2 + regres + regres_p + regress + regress_estat + regriv_p + remap + renpfix + repeat + reshape + robvar + roccomp + roccomp_7 + roccomp_8 + rocf_lf + rocfit + rocfit_8 + rocgold + rocplot + rocplot_7 + roctab + roctab_7 + rolling + rologit + rologit_p + rot + rota + rotat + rotate + rotatemat + rreg + rreg_p + runtest + rvfplot + rvfplot_7 + rvpplot + rvpplot_7 + + safesum + sample + sampsi + savedresults + saveold + sc + scatter + scm_mine + sco + scob_lf + scob_p + scobi_sw + scobit + scor + score + scoreplot + scoreplot_help + scree + screeplot + screeplot_help + sdtest + sdtesti + separate + seperate + serrbar + serrbar_7 + set_defaults + sfrancia + shewhart + shewhart_7 + signrank + signtest + simul + simul_7 + simulate + simulate_8 + sktest + slogit + slogit_d2 + slogit_p + smooth + snapspan + spearman + spikeplot + spikeplot_7 + spikeplt + spline_x + split + sqreg + sqreg_p + ssc + st + st_ct + st_hc + st_hcd + st_hcd_sh + st_is + st_issys + st_note + st_promo + st_set + st_show + st_smpl + st_subid + stack + statsby + statsby_8 + stbase + stci + stci_7 + stcox + stcox_estat + stcox_fr + stcox_fr_ll + stcox_p + stcox_sw + stcoxkm + stcoxkm_7 + stcstat + stcurv + stcurve + stcurve_7 + stdes + stem + stepwise + stereg + stfill + stgen + stir + stjoin + stmc + stmh + stphplot + stphplot_7 + stphtest + stphtest_7 + stptime + strate + strate_7 + streg + streg_sw + streset + sts + sts_7 + stset + stsplit + stsum + sttocc + sttoct + stvary + stweib + suest + suest_8 + sunflower + sureg + survcurv + survsum + svar + svar_p + svmat + svy + svy_disp + svy_dreg + svy_est + svy_est_7 + svy_estat + svy_get + svy_gnbreg_p + svy_head + svy_header + svy_heckman_p + svy_heckprob_p + svy_intreg_p + svy_ivreg_p + svy_logistic_p + svy_logit_p + svy_mlogit_p + svy_nbreg_p + svy_ologit_p + svy_oprobit_p + svy_poisson_p + svy_probit_p + svy_regress_p + svy_sub + svy_sub_7 + svy_x + svy_x_7 + svy_x_p + svydes + svydes_8 + svygen + svygnbreg + svyheckman + svyheckprob + svyintreg + svyintreg_7 + svyintrg + svyivreg + svylc + svylog_p + svylogit + svymarkout + svymarkout_8 + svymean + svymlog + svymlogit + svynbreg + svyolog + svyologit + svyoprob + svyoprobit + svyopts + svypois + svypois_7 + svypoisson + svyprobit + svyprobt + svyprop + svyprop_7 + svyratio + svyreg + svyreg_p + svyregress + svyset + svyset_7 + svyset_8 + svytab + svytab_7 + svytest + svytotal + sw + sw_8 + swcnreg + swcox + swereg + swilk + swlogis + swlogit + swologit + swoprbt + swpois + swprobit + swqreg + swtobit + swweib + symmetry + symmi + symplot + symplot_7 + sysdescribe + sysuse + szroeter + tab_or + tab1 + tab2 + tabi + table + tabodds + tabodds_7 + tabstat + te + tes + test + testnl + testparm + teststd + tetrachoric + time_it + tis + tob + tobi + tobit + tobit_p + tobit_sw + tostring + total + treat_ll + treatr_p + treatreg + trim + trnb_cons + trnb_mean + trpoiss_d2 + trunc_ll + truncr_p + truncreg + tsappend + tset + tsfill + tsline + tsline_ex + tsreport + tsrline + tsset + tssmooth + tsunab + ttest + ttesti + tut_chk + tut_wait + tutorial + tw + tware_st + two + twoway + twoway__fpfit_serset + twoway__function_gen + twoway__histogram_gen + twoway__ipoint_serset + twoway__ipoints_serset + twoway__kdensity_gen + twoway__lfit_serset + twoway__normgen_gen + twoway__pci_serset + twoway__qfit_serset + twoway__scatteri_serset + twoway__sunflower_gen + twoway_ksm_serset + typeof + unab + unabbrev + uselabel + + var + var_mkcompanion + var_p + varbasic + varfcast + vargranger + varirf + varirf_add + varirf_cgraph + varirf_create + varirf_ctable + varirf_describe + varirf_dir + varirf_drop + varirf_erase + varirf_graph + varirf_ograph + varirf_rename + varirf_set + varirf_table + varlmar + varnorm + varsoc + varstable + varstable_w + varstable_w2 + varwle + vce + vec + vec_fevd + vec_mkphi + vec_p + vec_p_w + vecirf_create + veclmar + veclmar_w + vecnorm + vecnorm_w + vecrank + vecstable + verinst + viewsource + vif + vwls + wdatetof + webdescribe + webseek + webuse + weib_lf + weib_lf0 + weib1_lf + weib2_lf + weibhet_glf + weibhet_glf_sh + weibhet_glfa + weibhet_glfa_sh + weibhet_gp + weibhet_ilf + weibhet_ilf_sh + weibhet_ilfa + weibhet_ilfa_sh + weibhet_ip + weibu_sw + weibul_p + weibull + weibull_c + weibull_s + weibullhet + whelp + wilc_st + wilcoxon + wntestb + wntestb_7 + wntestq + xchart + xchart_7 + xcorr + xcorr_7 + xi + xi_6 + xpose + xt_iis + xt_tis + xtab_p + xtabond + xtbin_p + xtclog + xtcloglog + xtcloglog_8 + xtcloglog_d2 + xtcloglog_re_p + xtcnt_p + xtcorr + xtdata + xtdes + xtfront_p + xtfrontier + xtgee + xtgee_elink + xtgee_estat + xtgee_makeivar + xtgee_p + xtgee_plink + xtgls + xtgls_p + xthaus + xthausman + xtht_p + xthtaylor + xtile + xtint_p + xtintreg + xtintreg_8 + xtintreg_d2 + xtintreg_p + xtivp_1 + xtivp_2 + xtivreg + xtline + xtline_ex + xtlogit + xtlogit_8 + xtlogit_d2 + xtlogit_fe_p + xtlogit_pa_p + xtlogit_re_p + xtmixed + xtmixed_estat + xtmixed_p + xtnb_fe + xtnb_lf + xtnbreg + xtnbreg_pa_p + xtnbreg_refe_p + xtpcse + xtpcse_p + xtpois + xtpoisson + xtpoisson_d2 + xtpoisson_pa_p + xtpoisson_refe_p + xtpred + xtprobit + xtprobit_8 + xtprobit_d2 + xtprobit_re_p + xtps_fe + xtps_lf + xtps_ren + xtps_ren_8 + xtrar_p + xtrc + xtrc_p + xtrchh + xtrefe_p + xtreg + xtreg_be + xtreg_fe + xtreg_ml + xtreg_pa_p + xtreg_re + xtregar + xtrere_p + xtsf_ll + xtsf_llti + xtsum + xttab + xttest0 + xttobit + xttobit_8 + xttobit_p + xttrans + yx + yxview__barlike_draw + yxview_area_draw + yxview_bar_draw + yxview_dot_draw + yxview_dropline_draw + yxview_function_draw + yxview_iarrow_draw + yxview_ilabels_draw + yxview_normal_draw + yxview_pcarrow_draw + yxview_pcbarrow_draw + yxview_pccapsym_draw + yxview_pcscatter_draw + yxview_pcspike_draw + yxview_rarea_draw + yxview_rbar_draw + yxview_rbarm_draw + yxview_rcap_draw + yxview_rcapsym_draw + yxview_rconnected_draw + yxview_rline_draw + yxview_rscatter_draw + yxview_rspike_draw + yxview_spike_draw + yxview_sunflower_draw + zap_s + zinb + zinb_llf + zinb_plf + zip + zip_llf + zip_p + zip_plf + zt_ct_5 + zt_hc_5 + zt_hcd_5 + zt_is_5 + zt_iss_5 + zt_sho_5 + zt_smp_5 + ztbase_5 + ztcox_5 + ztdes_5 + ztereg_5 + ztfill_5 + ztgen_5 + ztir_5 + ztjoin_5 + ztnb + ztnb_p + ztp + ztp_p + zts_5 + ztset_5 + ztspli_5 + ztsum_5 + zttoct_5 + ztvary_5 + ztweib_5 + + + + _cholinv + _cholsolve + _corr + _edittoint + _edittointtol + _edittozero + _edittozerotol + _eigen_work + _eigensystem + _eigenvalues + _equilc + _equilr + _equilrc + _ftell + _fullsvd + _hqrd + _hqrdp + _invlower + _jumble + _lefteigensystem + _lowertriangle + _lud + _lud_la + _luinv + _lusolve + _matexpsym + _matlogsym + _matpowersym + _mprobit_outer_prod + _mprobit_quadrature + _mprobit_quadrature_eval + _mprobit_quadrature_m + _mprobit_select + _mprobit_simulator + _mprobit_simulator_case_g + _mprobit_simulator_lk + _mprobit_simulator_m + _mprobit_simulator_mi + _mprobit_validate_choice + _mprobit_weights_roots_laguerre + _perhapsequilc + _perhapsequilr + _perhapsequilrc + _pinv + _qrinv + _qrsolve + _rowswap + _solvelower + _solveupper + _sort + _svd + _svdsv + _svsolve + _svy_design + _svy_group_sum + _svy_identify + _svy_mean + _svy_mean_post + _svy_mean_std + _svy_mean_stdpost + _svy_over_expand + _svy_over_sizes + _svy_post_sizes + _svy_ratio + _svy_ratio_post + _svy_ratio_std + _svy_ratio_stdpost + _svy_srs_variance + _svy_srssub_variance + _svy_std_sizes + _svy_total + _svy_total_post + _svy_variance + _symeigen_work + _symeigensystem + _symeigenvalues + _symmatfunc_work + _uppertriangle + _xtm_beta + _xtm_blup + _xtm_blup_save + _xtm_blup_save_u + _xtm_blup_u + _xtm_cleanup + _xtm_covtype + _xtm_de_th_u + _xtm_delta_to_theta + _xtm_det_upper + _xtm_em_get_rij + _xtm_em_get_uij + _xtm_em_iter + _xtm_em_iter_u + _xtm_em_solve + _xtm_fact_expand + _xtm_ga_th_u + _xtm_gamma_to_theta + _xtm_get_delta + _xtm_get_delta_u + _xtm_invert_R + _xtm_logdetr00 + _xtm_logdetr00_u + _xtm_matexp + _xtm_matlog + _xtm_mixed_ll + _xtm_mixed_ll_u + _xtm_mixed_ll_uu + _xtm_mk_eqs + _xtm_ml_eqlist + _xtm_ml_eqlist_wrk + _xtm_setup + _xtm_start + _xtm_start_collapse + _xtm_start_u + _xtm_th_de_u + _xtm_th_ga_u + _xtm_theta_to_delta + _xtm_theta_to_gamma + acos + acosh + aggregate + array + asin + asinh + assert + asserteq + atan + atanh + blockdiag + boolean + break + byte + case + cat + catch + chdir + cholesky + cholinv + cholsolve + class + cloglog + collate + colmax + colmin + colscalefactors + colshape + colvector + complex + cond + const + continue + convolve + convolveslowly + Corr + correlation + Corrslowly + corruppercase + cosh + deconvolve + default + delegate + delete + designmatrix + det + dettriangular + diag + diag0cnt + do + double + dsign + e + editmissing + edittoint + edittointtol + edittozero + edittozerotol + editvalue + eigensystem + eigenvalues + else + eltypedef + end + enum + explicit + export + external + fft + fileexists + findfile + float + for + friend + ftell + ftfreqs + ftpad + ftperiodogram + ftretime + ftunwrap + ftwrap + fullsdiag + fullsvd + function + gamma + global + goto + helloworld + Hilbert + hqrd + hqrdmultq + hqrdmultq1t + hqrdp + hqrdq + hqrdq1 + hqrdr + hqrdr1 + if + inline + int + invcloglog + invfft + invHilbert + invlogit + invlower + invorder + invslowfft + invslowft + invvech + isdiagonal + jumble + lefteigensystem + local + log10 + logit + long + lowertriangle + lud + luinv + lusolve + makesymmetric + mat_norm + mata + matexpsym + matlogsym + matpowersym + matrix + max + mean + meanvariance + min + mkdir + mmat_ + mmat_describe + mmat_expandlist + mmat_readerror + mmat_save + mmat_use + mmat_writeerror + namespace + new + norm + NULL + numeric + operator + orgtypedef + panelsetup + panelsetup_u + panelstats + panelsubmatrix + panelsubview + pathlist + pathsubsysdir + pi + pinv + pointer + polyadd + polyadd_expand + polyderiv + polydiv + polyeval + polyinteg + polymorphic + polymult + polyroots + polysolve + polytrim + pragma + private + protected + public + qrd + qrdp + qrinv + qrsolve + quad + quadcorrelation + quadmeanvariance + quadrant + quadvariance + range + rangen + rank + rank_from_singular_values + real + return + revorder + rmdir + rowmax + rowmin + rowscalefactors + rowvector + scalar + scalar_norm + short + sign + signed + sinh + sizeof + slowfft + slowft + solve_tol + solve_tolscale + solvelower + solveupper + sort + spline3 + spline3eval + splineeval + st_islmname + static + string + struct + super + svd + svdsv + svsolve + switch + symeigensystem + symeigenvalues + tanh + template + this + throw + Toeplitz + trace + trace_prod + transmorphic + transposeonly + try + typedef + typename + union + uniqrows + unitcircle + unorder + unsigned + uppertriangle + using + Vandermonde + variance + vec + vec_norm + vech + vector + version + virtual + void + volatile + while + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/syntax.template b/kate/part/syntax/data/syntax.template new file mode 100644 index 00000000..72d699d3 --- /dev/null +++ b/kate/part/syntax/data/syntax.template @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/systemc.xml b/kate/part/syntax/data/systemc.xml new file mode 100644 index 00000000..382005bb --- /dev/null +++ b/kate/part/syntax/data/systemc.xml @@ -0,0 +1,134 @@ + + + + + + + + sensitive + + + dont_initialize + gen_unique_name + name + next_trigger + sc_assert_fail + sc_copyright + sc_cycle + sc_initialize + sc_simulation_time + sc_start + sc_stop + sc_get_default_time_unit + sc_get_default_time_resolution + sc_time_stamp + sc_version + timed_out + wait + sc_trace + sc_create_vcd_trace_file + sc_close_vcd_trace_file + + + + SC_MODULE + SC_CTOR + SC_METHOD + SC_THREAD + + + sc_time_unit + sc_clock + sc_int + sc_uint + sc_bigint + sc_biguint + sc_logic + sc_lv + sc_bit + sc_bv + sc_fixed + sc_ufixed + sc_fixed_fast + sc_ufixed_fast + sc_fix + sc_ufix + sc_buffer + sc_fifo + sc_mutex + sc_semaphore + sc_signal + sc_signal_resolved + sc_signal_rv + sc_fifo_in_if + sc_fifo_out_if + sc_mutex_if + sc_semaphore_if + sc_signal_in_if + sc_signal_inout_if + sc_master + sc_inmaster + sc_outmaster + sc_inout_master + sc_indexed + sc_slave + sc_inslave + sc_outslave + sc_inoutslave + sc_in + sc_out + sc_inout + sc_noHandshake + sc_fullHandshake + sc_memfullHandshake + sc_enable_Handshake + sc_memenHandshake + sc_link_mp + sc_trace_file + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/systemverilog.xml b/kate/part/syntax/data/systemverilog.xml new file mode 100644 index 00000000..a13d3fc1 --- /dev/null +++ b/kate/part/syntax/data/systemverilog.xml @@ -0,0 +1,1028 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + defparam + deassign + + + + DPI + DPI-C + import + export + context + + + + assert + assume + cover + expect + disable + iff + binsof + intersect + first_match + throughout + within + + + + coverpoint + cross + wildcard + bins + ignore_bins + illegal_bins + + + + genvar + + + + if + else + unique + priority + matches + + + + default + + + + forever + repeat + while + for + do + foreach + break + continue + return + + + + pulsestyle_onevent + pulsestyle_ondetect + noshowcancelled + showcancelled + ifnone + + + + initial + final + always + always_comb + always_ff + always_latch + + + + alias + assign + force + release + + + + posedge + negedge + edge + wait + wait_order + + + + timeunit + timeprecision + s + ms + ns + us + ns + ps + fs + step + + + + new + extends + this + super + protected + local + rand + randc + bind + + + + constraint + solve + before + dist + inside + with + + + + virtual + pure + extern + forkjoin + + + + design + instance + cell + liblist + use + + + + library + incdir + include + + + + modport + + + + sync_accept_on + reject_on + accept_on + sync_reject_on + restrict + let + until + until_with + unique0 + eventually + s_until + s_always + s_eventually + s_nexttime + s_until_with + global + untyped + implies + weak + strong + nexttime + + + + + parameter + localparam + specparam + + input + output + inout + ref + + byte + shortint + int + integer + longint + time + + bit + logic + reg + + supply0 + supply1 + tri + triand + trior + trireg + tri0 + tri1 + wire + uwire + wand + wor + + signed + unsigned + + shortreal + real + realtime + + type + void + + struct + union + tagged + + const + var + automatic + static + packed + vectored + scalared + + typedef + enum + string + chandle + event + + null + + + + `__FILE__ + `__LINE__ + `begin_keywords + `celldefine + `default_nettype + `define + `else + `elsif + `end_keywords + `endcelldefine + `endif + `ifdef + `ifndef + `include + `line + `nounconnected_drive + `pragma + `resetall + `timescale + `unconnected_drive + `undef + `undefineall + + + + $finish + $stop + $exit + $realtime + $stime + $time + $printtimescale + $timeformat + $bitstoreal + $realtobits + $bitstoshortreal + $shortrealtobits + $itor + $rtoi + $signed + $unsigned + $cast + $bits + $isunbounded + $typename + $unpacked_dimensions + $dimensions + $left + $right + $low + $high + $increment + $size + $clog2 + $asin + $ln + $acos + $log10 + $atan + $exp + $atan2 + $sqrt + $hypot + $pow + $sinh + $floor + $cosh + $ceil + $tanh + $sin + $asinh + $cos + $acosh + $tan + $atanh + $fatal + $error + $warning + $info + $fatal + $error + $warning + $info + $asserton + $assertoff + $assertkill + $assertpasson + $assertpassoff + $assertfailon + $assertfailoff + $assertnonvacuouson + $assertvacuousoff + $onehot + $onehot0 + $isunknown + $sampled + $rose + $fell + $stable + $changed + $past + $countones + $past_gclk + $rose_gclk + $fell_gclk + $stable_gclk + $changed_gclk + $future_gclk + $rising_gclk + $falling_gclk + $steady_gclk + $changing_gclk + $coverage_control + $coverage_get_max + $coverage_get + $coverage_merge + $coverage_save + $get_coverage + $set_coverage_db_name + $load_coverage_db + $random + $dist_chi_square + $dist_erlang + $dist_exponential + $dist_normal + $dist_poisson + $dist_t + $dist_uniform + $q_initialize + $q_add + $q_remove + $q_full + $q_exam + $async$and$array + $async$and$plane + $async$nand$array + $async$nand$plane + $async$or$array + $async$or$plane + $async$nor$array + $async$nor$plane + $sync$and$array + $sync$and$plane + $sync$nand$array + $sync$nand$plane + $sync$or$array + $sync$or$plane + $sync$nor$array + $sync$nor$plane + $system + $display + $write + $displayb + $writeb + $displayh + $writeh + $displayo + $writeo + $strobe + $monitor + $strobeb + $monitorb + $strobeh + $monitorh + $strobeo + $monitoro + $monitoroff + $monitoron + $fclose + $fopen + $fdisplay + $fwrite + $fdisplayb + $fwriteb + $fdisplayh + $fwriteh + $fdisplayo + $fwriteo + $fstrobe + $fmonitor + $fstrobeb + $fmonitorb + $fstrobeh + $fmonitorh + $fstrobeo + $fmonitoro + $swrite + $sformat + $swriteb + $sformatf + $swriteh + $fgetc + $swriteo + $ungetc + $fscanf + $fgets + $fread + $sscanf + $fseek + $rewind + $fflush + $ftell + $feof + $ferror + $readmemb + $readmemh + $writememb + $writememh + $test$plusargs + $value$plusargs + $dumpfile + $dumpvars + $dumpoff + $dumpon + $dumpall + $dumplimit + $dumpflush + $dumpports + $dumpportsoff + $dumpportson + $dumpportsall + $dumpportslimit + $dumpportsflush + + + + pullup + pulldown + cmos + rcmos + nmos + pmos + rnmos + rpmos + and + nand + or + nor + xor + xnor + not + buf + tran + rtran + tranif0 + tranif1 + rtranif0 + rtranif1 + bufif0 + bufif1 + notif0 + notif1 + + + + + strong0 + strong1 + pull0 + pull1 + weak0 + weak1 + highz0 + highz1 + + small + medium + large + + + randomize + mailbox + semaphore + put + get + try_put + try_get + peek + try_peek + process + state + self + status + kill + await + suspend + resume + + size + delete + insert + num + first + last + next + prev + pop_front + pop_back + push_front + push_back + find + find_index + find_first + find_last + find_last_index + min + max + unique_index + reverse + sort + rsort + shuffle + sum + product + + List + List_Iterator + neq + eq + data + empty + front + back + start + finish + insert_range + erase + erase_range + set + swap + clear + purge + + + + begin + + + end + + + package + + + endpackage + + + macromodule + + + module + + + endmodule + + + generate + + + endgenerate + + + program + + + endprogram + + + class + + + endclass + + + function + + + endfunction + + + case + + + casex + + + casez + + + randcase + + + endcase + + + interface + + + endinterface + + + clocking + + + endclocking + + + task + + + endtask + + + primitive + + + endprimitive + + + fork + + + join + + + join_any + + + join_none + + + covergroup + + + endgroup + + + checker + + + endchecker + + + property + + + endproperty + + + randsequence + + + sequence + + + endsequence + + + specify + + + endspecify + + + config + + + endconfig + + + table + + + endtable + + + extern + + + pure + + + typedef + + + import + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/tads3.xml b/kate/part/syntax/data/tads3.xml new file mode 100644 index 00000000..9114633e --- /dev/null +++ b/kate/part/syntax/data/tads3.xml @@ -0,0 +1,175 @@ + + + + + + + + + abort + and + argcount + break + case + catch + class + construct + continue + default + definingobj + delegated + dictionary + do + else + enum + exit + export + extern + finalize + finally + for + foreach + function + goto + grammar + if + in + inherited + intrinsic + is + local + modify + new + nil + not + object + or + property + propertyset + replace + replaced + return + self + static + switch + targetobj + targetprop + template + throw + token + transient + true + try + while + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/kate/part/syntax/data/tcl.xml b/kate/part/syntax/data/tcl.xml new file mode 100644 index 00000000..88f84c88 --- /dev/null +++ b/kate/part/syntax/data/tcl.xml @@ -0,0 +1,565 @@ + + + + + + + + + + + + after + append + AppleScript + argv + argc + array + auto_execk + auto_execok + auto_import + auto_load + auto_mkindex + auto_mkindex_old + auto_path + auto_qualify + auto_reset + beep + bell + binary + bind + bindtags + bgerror + break + button + canvas + case + catch + cd + chan + checkbutton + clipboard + clock + close + combobox + concat + console + continue + dde + destroy + dict + else + elseif + encoding + entry + env + eof + error + errorCode + errorInfo + eval + event + exec + exit + expr + fblocked + fconfigure + fcopy + file + fileevent + flush + focus + font + for + foreach + format + frame + gets + glob + global + grab + grid + history + if + image + incr + info + interp + join + label + labelframe + lappend + lassign + lindex + linsert + list + listbox + llength + load + lower + lrange + lremove + lrepeat + lreplace + lreverse + lsearch + lset + lsort + menu + menubutton + message + namespace + notebook + open + option + OptProc + pack + package + panedwindow + parray + pid + place + pkg_mkIndex + proc + progressbar + puts + pwd + radiobutton + raise + read + regexp + registry + regsub + rename + resource + return + scale + scan + scrollbar + seek + selection + send + separator + set + sizegrip + socket + source + spinbox + split + string + style + subst + switch + tclLog + tcl_endOfWord + tcl_findLibrary + tcl_library + tcl_patchLevel + tcl_platform + tcl_precision + tcl_rcFileName + tcl_rcRsrcName + tcl_startOfNextWord + tcl_startOfPreviousWord + tcl_traceCompile + tcl_traceExec + tcl_version + tcl_wordBreakAfter + tcl_wordBreakBefore + tell + text + time + tk + tkTabToWindow + tkwait + tk_chooseColor + tk_chooseDirectory + tk_focusFollowMouse + tk_focusNext + tk_focusPrev + tk_getOpenFile + tk_getSaveFile + tk_library + tk_menuSetFocus + tk_messageBox + tk_optionMenu + tk_patchLevel + tk_popup + tk_strictMotif + tk_textCopy + tk_textCut + tk_textPaste + tk_version + toplevel + trace + traverseTo + treeview + unknown + unload + unset + update + uplevel + upvar + variable + vwait + while + winfo + wm + + + + add + args + atime + attributes + body + bytelength + cancel + channels + clicks + cmdcount + commands + compare + complete + convertfrom + convertto + copy + default + delete + dirname + equal + executable + exists + extension + first + forget + format + functions + globals + hostname + idle + ifneeded + index + info + is + isdirectory + isfile + join + last + length + level + library + link + loaded + locals + lstat + map + match + mkdir + mtime + nameofexecutable + names + nativename + normalize + number + owned + patchlevel + pathtype + present + procs + provide + range + readable + readlink + remove + rename + repeat + replace + require + rootname + scan + script + seconds + separator + sharedlibextension + size + split + stat + system + tail + tclversion + tolower + totitle + toupper + trim + trimleft + trimright + type + unknown + variable + vars + vcompare + vdelete + versions + vinfo + volumes + vsatisfies + wordend + wordstart + writable + + activate + actual + addtag + append + appname + aspect + atom + atomname + bbox + bind + broadcast + canvasx + canvasy + caret + cells + cget + children + class + clear + client + clone + colormapfull + colormapwindows + command + configure + containing + coords + create + current + curselection + dchars + debug + deiconify + delta + depth + deselect + dlineinfo + dtag + dump + edit + entrycget + entryconfigure + families + find + flash + focus + focusmodel + fpixels + fraction + frame + generate + geometry + get + gettags + grid + group + handle + height + hide + iconbitmap + iconify + iconmask + iconname + iconposition + iconwindow + icursor + id + identify + image + insert + interps + inuse + invoke + ismapped + itemcget + itemconfigure + keys + lower + manager + mark + maxsize + measure + metrics + minsize + move + name + nearest + overrideredirect + own + panecget + paneconfigure + panes + parent + pathname + pixels + pointerx + pointerxy + pointery + positionfrom + post + postcascade + postscript + protocol + proxy + raise + release + reqheight + reqwidth + resizable + rgb + rootx + rooty + scale + scaling + screen + screencells + screendepth + screenheight + screenmmheight + screenmmwidth + screenvisual + screenwidth + search + see + select + selection + server + set + show + sizefrom + stackorder + state + status + tag + title + toplevel + transient + types + unpost + useinputmethods + validate + values + viewable + visual + visualid + visualsavailable + vrootheight + vrootwidth + vrootx + vrooty + width + window + windowingsystem + withdraw + x + xview + y + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/tcsh.xml b/kate/part/syntax/data/tcsh.xml new file mode 100644 index 00000000..bed4bfe6 --- /dev/null +++ b/kate/part/syntax/data/tcsh.xml @@ -0,0 +1,868 @@ + + + + + + + +]> + + + + + + + else + for + function + in + select + until + while + . + then + + + + : + alias + alloc + bg + bindkey + break + builtins + bye + cd + chdir + complete + continue + dirs + echo + echotc + eval + exec + exit + fg + filetest + glob + hashstat + history + hup + inlib + jobs + kill + limit + log + login + logout + ls-F + migrate + newgrp + nice + nohup + notify + onintr + popd + printenv + pushd + rehash + repeat + sched + settc + setty + shift + source + stop + suspend + telltc + time + umask + unalias + uncomplete + unhash + unlimit + ver + wait + watchlog + where + which + + + + unset + unsetenv + + + + + arch + awk + bash + bunzip2 + bzcat + bzcmp + bzdiff + bzegrep + bzfgrep + bzgrep + bzip2 + bzip2recover + bzless + bzmore + cat + chattr + chgrp + chmod + chown + chvt + cp + date + dd + deallocvt + df + dir + dircolors + dmesg + dnsdomainname + domainname + du + dumpkeys + echo + ed + egrep + false + fgconsole + fgrep + fuser + gawk + getkeycodes + gocr + grep + groff + groups + gunzip + gzexe + gzip + hostname + igawk + install + kbd_mode + kbdrate + killall + last + lastb + link + ln + loadkeys + loadunimap + login + ls + lsattr + lsmod + lsmod.old + mapscrn + mesg + mkdir + mkfifo + mknod + mktemp + more + mount + mv + nano + netstat + nisdomainname + nroff + openvt + pgawk + pidof + ping + ps + pstree + pwd + rbash + readlink + red + resizecons + rm + rmdir + run-parts + sash + sed + setfont + setkeycodes + setleds + setmetamode + setserial + sh + showkey + shred + sleep + ssed + stat + stty + su + sync + tar + tempfile + touch + troff + true + umount + uname + unicode_start + unicode_stop + unlink + utmpdump + uuidgen + vdir + wall + wc + ypdomainname + zcat + zcmp + zdiff + zegrep + zfgrep + zforce + zgrep + zless + zmore + znew + zsh + + + aclocal + aconnect + aplay + apm + apmsleep + apropos + ar + arecord + as + as86 + autoconf + autoheader + automake + awk + basename + bc + bison + c++ + cal + cat + cc + cdda2wav + cdparanoia + cdrdao + cd-read + cdrecord + chfn + chgrp + chmod + chown + chroot + chsh + clear + cmp + co + col + comm + cp + cpio + cpp + cut + dc + dd + df + diff + diff3 + dir + dircolors + directomatic + dirname + du + env + expr + fbset + file + find + flex + flex++ + fmt + free + ftp + funzip + fuser + g++ + gawk + gc + gcc + gdb + getent + getopt + gettext + gettextize + gimp + gimp-remote + gimptool + gmake + gs + head + hexdump + id + install + join + kill + killall + ld + ld86 + ldd + less + lex + ln + locate + lockfile + logname + lp + lpr + ls + lynx + m4 + make + man + mkdir + mknod + msgfmt + mv + namei + nasm + nawk + nice + nl + nm + nm86 + nmap + nohup + nop + od + passwd + patch + pcregrep + pcretest + perl + perror + pidof + pr + printf + procmail + prune + ps2ascii + ps2epsi + ps2frag + ps2pdf + ps2ps + psbook + psmerge + psnup + psresize + psselect + pstops + rcs + rev + rm + scp + sed + seq + setterm + shred + size + size86 + skill + slogin + snice + sort + sox + split + ssh + ssh-add + ssh-agent + ssh-keygen + ssh-keyscan + stat + strings + strip + sudo + suidperl + sum + tac + tail + tee + test + tr + uniq + unlink + unzip + updatedb + updmap + uptime + users + vmstat + w + wc + wget + whatis + whereis + which + who + whoami + write + xargs + yacc + yes + zip + zsoelim + + + dcop + kdialog + kfile + xhost + xmodmap + xset + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/template-toolkit.xml b/kate/part/syntax/data/template-toolkit.xml new file mode 100644 index 00000000..8014df40 --- /dev/null +++ b/kate/part/syntax/data/template-toolkit.xml @@ -0,0 +1,331 @@ + + + +]> + + + + + + + SET + GET + CALL + DEFAULT + IF + ELSIF + ELSE + UNLESS + LAST + NEXT + FOR + FOREACH + WHILE + SWITCH + CASE + PROCESS + INCLUDE + INSERT + WRAPPER + BLOCK + MACRO + END + USE + IN + FILTER + TRY + THROW + CATCH + FINAL + META + TAGS + DEBUG + PERL + + + + constants + + template + component + loop + error + content + + + + + defined + length + repeat + replace + match + search + split + chunk + list + hash + size + + keys + values + each + sort + nsort + import + defined + exists + item + + first + last + max + reverse + join + grep + + unshift + push + shift + pop + unique + merge + slice + splice + count + + format + upper + lower + ucfirst + lcfirst + trim + collapse + html + html_entity + html_para + html_break + html_para_break + html_line_break + uri + url + indent + truncate + repeat + remove + replace + redirect + eval + evaltt + perl + evalperl + stdout + stderr + null + latex + + + = + != + ~= + += + -= + *= + /= + **= + |= + ||= + &= + &&= + ?= + + + - + * + + % + || + && + | + & + < + << + > + >> + ^ + -> + => + . + , + ; + :: + \ + and + or + not + eq + ne + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/texinfo.xml b/kate/part/syntax/data/texinfo.xml new file mode 100644 index 00000000..d827a7a0 --- /dev/null +++ b/kate/part/syntax/data/texinfo.xml @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/textile.xml b/kate/part/syntax/data/textile.xml new file mode 100644 index 00000000..50aa5880 --- /dev/null +++ b/kate/part/syntax/data/textile.xml @@ -0,0 +1,103 @@ + + + + + + + + +]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/kate/part/syntax/data/tibasic.xml b/kate/part/syntax/data/tibasic.xml new file mode 100644 index 00000000..0b2d67db --- /dev/null +++ b/kate/part/syntax/data/tibasic.xml @@ -0,0 +1,71 @@ + + + + + + If + Then + Else + For + While + Repeat + End + Pause + Lbl + Goto + IS> + DS< + Menu + prgm + Return + DelVar + GraphStyle + Input + Prompt + Disp + DispGraph + DispTable + Output + getKey + ClrHome + ClrTable + GetCalc + Get + Send + prgm + + + net + eogt + eolt + sqrt + %THETA + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/txt2tags.xml b/kate/part/syntax/data/txt2tags.xml new file mode 100644 index 00000000..581f469f --- /dev/null +++ b/kate/part/syntax/data/txt2tags.xml @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/uscript.xml b/kate/part/syntax/data/uscript.xml new file mode 100644 index 00000000..e4c7b567 --- /dev/null +++ b/kate/part/syntax/data/uscript.xml @@ -0,0 +1,186 @@ + + + + + + break + continue + if + else + switch + while + for + do + foreach + true + false + null + new + instanceof + state + auto + exec + function + defaultproperties + native + noexport + var + out + local + event + return + static + Static + synchronized + transient + volatile + final + throws + extends + expands + public + protected + private + abstract + case + default + final + simulated + Dot + nativereplication + replication + unreliable + reliable + ignores + localized + latent + singular + Cross + config + enum + struct + operator + preoperator + postoperator + iterator + coerce + optional + const + editconst + array + export + editinline + editinlinenew + editinlineuse + cpptext + placeable + virtual + hidecategories + super + global + none + self + + + + boolean + char + byte + short + int + long + float + double + void + Pawn + sound + ipaddr + ELightType + actor + ammo + bool + vector + rotator + name + string + object + plane + staticmesh + package + color + coords + material + class + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/vala.xml b/kate/part/syntax/data/vala.xml new file mode 100644 index 00000000..322083ac --- /dev/null +++ b/kate/part/syntax/data/vala.xml @@ -0,0 +1,282 @@ + + + + + + + abstract + as + async + base + break + case + catch + class + const + construct + continue + default + delegate + delete + do + dynamic + else + enum + ensures + errordomain + extern + false + finally + for + foreach + get + global + if + in + inline + interface + internal + is + lock + namespace + new + null + out + override + owned + private + protected + public + ref + requires + result + return + set + signal + sizeof + static + struct + switch + this + throw + throws + true + try + typeof + unowned + using + value + virtual + weak + while + yield + + + connect + connect_after + disconnect + emit + + + bool + char + double + float + int + int8 + int16 + int32 + int64 + long + short + size_t + ssize_t + string + uchar + uint + uint8 + uint16 + uint32 + uint64 + ulong + unichar + ushort + var + void + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/valgrind-suppression.xml b/kate/part/syntax/data/valgrind-suppression.xml new file mode 100644 index 00000000..9ac94984 --- /dev/null +++ b/kate/part/syntax/data/valgrind-suppression.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/varnish.xml b/kate/part/syntax/data/varnish.xml new file mode 100644 index 00000000..7f457635 --- /dev/null +++ b/kate/part/syntax/data/varnish.xml @@ -0,0 +1,349 @@ + + + + + + vcl_recv + vcl_pipe + vcl_pass + vcl_hash + vcl_hit + vcl_miss + vcl_fetch + vcl_deliver + vcl_init + vcl_fini + vcl_error + + + if + else + elsif + elseif + + + set + call + error + esi + include + remove + unset + panic + rollback + purge + synthetic + + + hash_data + regsub + regsuball + ban + ban_url + + + deliver + error + fetch + hash + hit_for_pass + lookup + ok + pass + pipe + restart + + + random + client + hash + round-robin + dns + fallback + + + + std + example + abtest + threescale + authentication + boltsort + crashhandler + curl + digest + dns + header + memcached + null + querystring + shield + softpurge + statsd + throttle + timeutils + urlcode + var + timers + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/varnishtest.xml b/kate/part/syntax/data/varnishtest.xml new file mode 100644 index 00000000..2fcd7c3e --- /dev/null +++ b/kate/part/syntax/data/varnishtest.xml @@ -0,0 +1,482 @@ + + + + + + fatal + gunzip + rxchunk + + + accept + close + expect_close + fatal + rxbody + rxhdrs + rxreq + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/vcard.xml b/kate/part/syntax/data/vcard.xml new file mode 100644 index 00000000..3eeed3c0 --- /dev/null +++ b/kate/part/syntax/data/vcard.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/velocity.xml b/kate/part/syntax/data/velocity.xml new file mode 100644 index 00000000..dc2424ec --- /dev/null +++ b/kate/part/syntax/data/velocity.xml @@ -0,0 +1,51 @@ + + + + + + #set + #foreach + #end + #if + #else + #elseif + #parse + #macro + #stop + #include + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/vera.xml b/kate/part/syntax/data/vera.xml new file mode 100644 index 00000000..aadb4b4a --- /dev/null +++ b/kate/part/syntax/data/vera.xml @@ -0,0 +1,648 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + after + all + any + around + assoc_index + assoc_size + async + bad_state + bad_trans + before + begin + big_endian + bind + bin_activation + bit_normal + bit_reverse + break + breakpoint + case + casex + casez + class + constraint + continue + coverage + coverage_block + coverage_def + coverage_depth + coverage_goal + coverage_group + coverage_option + coverage_val + cross_num_print_missing + cross_auto_bin_max + cov_comment + default + depth + dist + do + else + end + enum + exhaustive + export + extends + extern + for + foreach + fork + function + hdl_task + hdl_node + hide + if + illegal_self_transition + illegal_state + illegal_transition + in + interface + invisible + join + little_endian + local + m_bad_state + m_bad_trans + m_state + m_trans + negedge + new + newcov + non_rand + none + not + null + or + ordered + packed + port + posedge + proceed + prod + prodget + prodset + program + protected + public + rand + randc + randcase + randseq + repeat + return + rules + sample + sample_event + shadow + soft + state + static + super + task + terminate + this + trans + typedef + unpacked + var + vca + vector + verilog_node + verilog_task + vhdl_node + vhdl_task + virtual + virtuals + visible + void + while + wildcard + with + + + integer + bit + reg + string + bind_var + event + inout + input + output + ASYNC + CLOCK + NDRIVE + NHOLD + NRX + NRZ + NR0 + NR1 + NSAMPLE + PDRIVE + PHOLD + PRX + PRZ + PR0 + PR1 + PSAMPLE + + + gnr + grx + grz + gr0 + gr1 + nr + rx + rz + r0 + r1 + snr + srx + srz + sr0 + sr1 + + + + alloc + call_func + call_task + cast_assign + close_conn + cm_coverage + cm_get_coverage + cm_get_limit + coverage_backup_database_file + coverage_save_database + delay + error + error_mode + error_wait + exit + fclose + feof + ferror + fflush + flag + fopen + fprintf + freadb + freadb + freadh + freadstr + get_bind + get_bind_id + get_conn_err + get_cycle + get_env + get_memsize + get_plus_arg + get_systime + get_time + get_time_unit + getstate + initstate + lock_file + mailbox_get + mailbox_put + mailbox_receive + mailbox_send + make_client + make_server + os_command + printf + psprintf + query + query_str + query_x + rand48 + random + region_enter + region_exit + rewind + semaphore_get + semaphore_put + setstate + signal_connect + simwave_plot + srandom + sprintf + sscanf + stop + suspend_thread + sync + timeout + trace + trigger + unit_delay + unlock_file + up_connections + urand48 + urandom + urandom_range + vera_bit_reverse + vera_crc + vera_pack + vera_pack_big_endian + vera_plot + vera_report_profile + vera_unpack + vera_unpack_big_endian + vsv_call_func + vsv_call_task + vsv_close_conn + vsv_get_conn_err + vsv_make_client + vsv_make_server + vsv_up_connections + vsv_wait_for_done + vsv_wait_for_input + wait_child + wait_var + + Configure + DisableTrigger + DoAction + EnableCount + EnableTrigger + Event + GetAssert + GetCount + GetFirstAssert + GetName + GetNextAssert + Wait + atobin + atohex + atoi + atooct + backref + bittostr + capacity + compare + constraint_mode + delete + empty + find + find_index + first + first_index + get_at_least + get_auto_bin + get_cov_weight + get_coverage_goal + get_cross_bin_max + get_status + get_status_msg + getc + hash + icompare + insert + inst_get_at_least + inst_get_auto_bin_max + inst_get_collect + inst_get_cov_weight + inst_get_coverage_goal + inst_getcross_bin_max + inst_query + inst_set_at_least + inst_set_auto_bin_max + inst_set_bin_activiation + inst_set_collect + inst_set_cov_weight + inst_set_coverage_goal + inst_set_cross_bin_max + itoa + last + last_index + len + load + match + max + max_index + min + min_index + object_compare + object_copy + object_print + pack + pick_index + pop_back + pop_front + post_pack + post_randomize + post_unpack + postmatch + pre_pack + pre_randomize + prematch + push_back + push_front + putc + query + query_str + rand_mode + randomize + reserve + reverse + rsort + search + set_at_least + set_auto_bin_max + set_bin_activiation + set_cov_weight + set_coverage_goal + set_cross_bin_max + set_name + size + sort + substr + sum + thismatch + tolower + toupper + unique_index + unpack + + new + object_compare + post_boundary + post_pack + post_randomize + post_unpack + pre-randomize + pre_boundary + pre_pack + pre_unpack + + ALL + ANY + BAD_STATE + BAD_TRANS + CALL + CHECK + CHGEDGE + CLEAR + COPY_NO_WAIT + COPY_WAIT + CROSS + CROSS_TRANS + DEBUG + DELETE + EC_ARRAYX + EC_CODE_END + EC_CONFLICT + EC_EVNTIMOUT + EC_EXPECT + EC_FULLEXPECT + EC_MBXTMOUT + EC_NEXPECT + EC_RETURN + EC_RGNTMOUT + EC_SCONFLICT + EC_SEMTMOUT + EC_SEXPECT + EC_SFULLEXPECT + EC_SNEXTPECT + EC_USERSET + EQ + EVENT + FAIL + FIRST + FORK + GE + GOAL + GT + HAND_SHAKE + HI + HIGH + HNUM + LE + LIC_EXIT + LIC_PRERR + LIC_PRWARN + LIC_WAIT + LO + LOAD + LOW + LT + MAILBOX + MAX_COM + NAME + NE + NEGEDGE + NEXT + NO_OVERLAP + NO_OVERLAP_STATE + NO_OVERLAP_TRANS + NO_VARS + NO_WAIT + NUM + NUM_BIN + NUM_DET + OFF + OK + OK_LAST + ON + ONE_BLAST + ONE_SHOT + ORDER + PAST_IT + PERCENT + POSEDGE + PROGRAM + RAWIN + REGION + REPORT + SAMPLE + SAVE + SEMAPHORE + SET + SILENT + STATE + STR + STR_ERR_OUT_OF_RANGE + STR_ERR_REGEXP_SYNTAX + SUM + TRANS + VERBOSE + WAIT + stderr + stdin + stdout + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/verilog.xml b/kate/part/syntax/data/verilog.xml new file mode 100644 index 00000000..12619f2e --- /dev/null +++ b/kate/part/syntax/data/verilog.xml @@ -0,0 +1,257 @@ + + + + + + macromodule + table + endtable + specify + specparam + endspecify + + defparam + default + if + ifnone + else + forever + while + for + wait + repeat + disable + + assign + deassign + force + release + + always + initial + edge + posedge + negedge + + + config + endconfig + library + design + liblist + cell + use + instance + + + + begin + fork + module + case + casex + casez + task + function + generate + + + + end + join + endmodule + endcase + endtask + endfunction + endgenerate + + + + + strong0 + strong1 + pull0 + pull1 + weak0 + weak1 + highz0 + highz1 + + small + medium + large + + + + pullup + pulldown + cmos + rcmos + nmos + pmos + rnmos + rpmos + and + nand + or + nor + xor + xnor + not + buf + tran + rtran + tranif0 + tranif1 + rtranif0 + rtranif1 + bufif0 + bufif1 + notif0 + notif1 + + + + + input + output + inout + + wire + tri + tri0 + tri1 + wand + wor + triand + trior + supply0 + supply1 + + reg + integer + real + realtime + time + + vectored + scalared + trireg + + parameter + event + + signed + automatic + genvar + localparam + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/vhdl.xml b/kate/part/syntax/data/vhdl.xml new file mode 100644 index 00000000..8d9b8fa8 --- /dev/null +++ b/kate/part/syntax/data/vhdl.xml @@ -0,0 +1,646 @@ + + + + + + + + +]> + + + + file + library + use + + + + access + after + alias + all + array + assert + assume + assume_guarantee + attribute + begin + block + body + bus + component + constant + context + cover + default + disconnect + downto + end + exit + fairness + falling_edge + file + force + function + generate + generic + group + guarded + impure + inertial + is + label + linkage + literal + map + new + next + null + of + on + open + others + parameter + port + postponed + procedure + process + property + protected + pure + range + record + register + reject + release + report + return + rising_edge + select + sequence + severity + signal + shared + strong + subtype + to + transport + type + unaffected + units + until + variable + vmode + vprop + vunit + wait + when + with + note + warning + error + failure + in + inout + out + buffer + and + abs + or + xor + xnor + not + mod + nand + nor + rem + rol + ror + sla + sra + sll + srl + + + + if + else + elsif + then + + + + loop + + + + in + inout + out + buffer + linkage + + + + signal + variable + constant + type + attribute + + + + to + downto + others + + + + case + when + + + + fs + ps + ns + us + ms + sec + min + hr + + + + + bit + bit_vector + character + boolean + boolean_vector + integer + integer_vector + real + real_vector + time + time_vector + delay_length + string + severity_level + positive + natural + file_open_kind + file_open_status + signed + unsigned + unresolved_unsigned + unresolved_signed + line + text + side + width + std_logic + std_logic_vector + std_ulogic + std_ulogic_vector + x01 + x01z + ux01 + ux01z + qsim_state + qsim_state_vector + qsim_12state + qsim_12state_vector + qsim_strength + mux_bit + mux_vector + reg_bit + reg_vector + wor_bit + wor_vector + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/vrml.xml b/kate/part/syntax/data/vrml.xml new file mode 100644 index 00000000..40bdea3d --- /dev/null +++ b/kate/part/syntax/data/vrml.xml @@ -0,0 +1,139 @@ + + + + + + DEF + EXTERNPROTO + FALSE + IS + NULL + PROTO + ROUTE + TO + TRUE + USE + eventIn + eventOut + exposedField + field + + + MFColor + MFFloat + MFInt32 + MFNode + MFRotation + MFString + MFTime + MFVec2f + MFVec3f + SFBool + SFColor + SFFloat + SFImage + SFInt32 + SFNode + SFRotation + SFString + SFTime + SFVec2f + SFVec3f + + + Anchor + AudioClip + Appearance + Background + Billboard + Box + Collision + Color + ColorInterpolator + Cone + Coordinate + CoordinateInterpolator + Cylinder + CylinderSensor + DirectionalLight + ElevationGrid + Extrusion + Fog + FontStyle + Group + ImageTexture + IndexedFaceSet + IndexedLineSet + Inline + LOD + Material + MovieTexture + NavigationInfo + Normal + NormalInterpolator + OrientationInterpolator + PixelTexture + Plane + PlaneSensor + PointLight + PointSet + PositionInterpolator + ProximitySensor + ScalarInterpolator + Script + Shape + Sensor + Sound + Sphere + SphereSensor + SpotLight + Switch + Text + TextureCoordinate + TextureTransform + TimeSensor + TouchSensor + Transform + Viewpoint + VisibilitySensor + WorldInfo + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/winehq.xml b/kate/part/syntax/data/winehq.xml new file mode 100644 index 00000000..07919a86 --- /dev/null +++ b/kate/part/syntax/data/winehq.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/wml.xml b/kate/part/syntax/data/wml.xml new file mode 100644 index 00000000..a7ad7567 --- /dev/null +++ b/kate/part/syntax/data/wml.xml @@ -0,0 +1,210 @@ + + + + + + + + wmllint + wmlindent + wmlscope + po + + + #textdomain + #else + #undef + #error + #warning + + + #define + #ifdef + #ifndef + #ifhave + #ifnhave + #ifver + #ifnver + + + #enddef + #endif + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/xharbour.xml b/kate/part/syntax/data/xharbour.xml new file mode 100644 index 00000000..02ed03ee --- /dev/null +++ b/kate/part/syntax/data/xharbour.xml @@ -0,0 +1,543 @@ + + + + + + + local + global + extern + field + each + as + set + clear + screen + databases + all + close + color + date + else + elseif + in + to + do + loop + catch + exit + box + say + case + switch + self + super + say + get + read + use + select + otherwise + index + alias + like + has + return + static + on + off + nil + ? + class + external + + + + FOR + IF + SWITCH + WHILE + TRY + BEGIN + PROCEDURE + FUNCTION + METHOD + + + + NEXT + END + ENDIF + ENDDO + ENDCASE + + + + data + inline + method + classdata + init + from + hidden + + + + INVALID + EXACT + FIXED + DECIMALS + DATEFORMAT + EPOCH + PATH + DEFAULT + EXCLUSIVE + SOFTSEEK + UNIQUE + DELETED + CANCEL + DEBUG + TYPEAHEAD + COLOR + CURSOR + CONSOLE + ALTERNATE + ALTFILE + DEVICE + EXTRA + EXTRAFILE + PRINTER + PRINTFILE + MARGIN + BELL + CONFIRM + ESCAPE + INSERT + EXIT + INTENSITY + SCOREBOARD + DELIMITERS + DELIMCHARS + WRAP + MESSAGE + MCENTER + SCROLLBREAK + EVENTMASK + VIDEOMODE + MBLOCKSIZE + MFILEEXT + STRICTREAD + OPTIMIZE + AUTOPEN + AUTORDER + AUTOSHARE + LANGUAGE + IDLEREPEAT + TRACE + TRACEFILE + TRACESTACK + FILECASE + DIRCASE + DIRSEPARATOR + + + + aadd + adel + achoice + aclone + aeval + ains + ascan + asize + adir + afill + atail + asort + array + TAssociativeArray + + bin21 + bin2l + bin2u + bin2w + i2bin + l2bin + u2bin + w2bin + + eval + fieldblock + fieldwblock + + inkey + lastkey + mcol + mrow + nextkey + + empty + word + descend + __dbdelim + __dbsdf + + os + __run + + alert + browse + dbedit + outerr + outstd + readkey + readvar + __atprompt + __input + __menuto + __nonoallert + __typefile + __xrestscreen + __xsavescreen + + DBAPPEND + DBCLEARFILTER + DBCLOSEALL + DBCLOSEAREA + DBCOMMIT + DBCOMMITALL + DBCREATE + DBDELETE + DBEVAL + DBF + DBFILTER + DBGOBOTTOM + DBGOTO + DBGOTOP + DBRECALL + DBRLOCK + DBRLOCKLIST + DBRUNLOCK + DBSEEK + DBSELECTAREA + DBSETDRIVER + DBSETFILTER + DBSKIP + DBSTRUCT + DBUNLOCK + DBUNLOCKALL + DBUSEAREA + INDEXEXT + INDEXKEY + INDEXORD + ORDBAGEXT + ORDBAGNAME + ORDCONDSET + ORDCREATE + ORDDESTROY + ORDFOR + ORDKEY + ORDLISTADD + ORDLISTCLEAR + ORDLISTREBUILD + ORDNAME + ORDNUMBER + ORDSETFOCUS + RDDLIST + RDDNAME + RDDSETDEFAULT + __DBCONTINUE + __DBZAP + __FLEDIT + __RDDSETDEFAULT + __dbCopyStruct + __dbCopyXStruct + __dbCreate + __dbStructFilter + dbSkipper + CDOW + CMONTH + CTOD + DATE + DAY + DAYS + DOW + DTOC + DTOS + MONTH + YEAR + + GETENV + SET + SETMODE + SETTYPEAHEAD + VERSION + __SETCENTURY + __SetFunction + + break + errorsys + throw + errornew + + HB_SETKEYSAVE + HB_SetKeyCheck + HB_SetKeyGet + SETKEY + __QUIT + __WAIT + + file + frename + __dir + + col + maxcol + maxrow + row + hb_colorindex + + CURDIR + DIRCHANGE + DIRREMOVE + DISKSPACE + FCLOSE + FCREATE + FERASE + FERROR + FOPEN + FREAD + FREADSTR + FSEEK + FWRITE + HB_DISKSPACE + HB_FEOF + ISDISK + MAKEDIR + + ABS + EXP + INT + LOG + MAX + MIN + MOD + ROUND + SQRT + + HB_ISBYREF + PROCFILE + PROCLINE + PROCNAME + TYPE + VALTYPE + valtoprg + tone + + HB_LANGNAME + HB_LANGSELECT + ISAFFIRM + ISNEGATIVE + NATIONMSG + + pcount + HB_pvalue + + ALLTRIM + ASC + AT + CHR + HARDCR + HB_ANSITOOEM + HB_OEMTOANSI + HB_VALTOSTR + ISALPHA + ISDIGIT + ISLOWER + ISUPPER + LEFT + LEN + LOWER + LTRIM + MEMOTRAN + PADC + PADL + PADR + RAT + REPLICATE + RIGHT + RTRIM + SPACE + STR + STRTRAN + STRZERO + SUBSTR + TRANSFORM + TRIM + UPPER + VAL + + devoutpict + + elaptime + seconds + secs + time + + do + + ThreadStart + ThreadStop + ThreadSleep + ThreadKill + ThreadJoin + CreateMutex + DestroyMutex + MutexLock + MutexUnlock + Subscribe + SubscribeNow + Notify + NotifyAll + WaitForThreads + KillAllThreads + + InetInit + InetCleanup + InetCreate + InetDestroy + InetConnect + InetServer + InetAccept + InetSetTimeout + InetGetTimeout + InetClearTimeout + InetRecv + InetRecvAll + InetSend + InetSendAll + InetDGram + InetDGramRecv + InetDGramSend + InetAddress + InetPort + InetError + InetErrorDesc + InetGetHosts + InetConnectIP + + hb_regex + hb_regexmatch + hb_regexsplit + hb_regexcomp + hb_readini + hb_writeini + hb_random + hb_chechsum + hb_crypt + hb_decrypt + hb_hextonum + hb_numtohex + hb_exec + hb_execfromarray + + hb_class + hb_keyput + hb_osnewline + + + + + #include + #if + #ifdef + #ifndef + #endif + #else + #define + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/xml.xml b/kate/part/syntax/data/xml.xml new file mode 100644 index 00000000..65e9be2b --- /dev/null +++ b/kate/part/syntax/data/xml.xml @@ -0,0 +1,150 @@ + + + + +]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/xmldebug.xml b/kate/part/syntax/data/xmldebug.xml new file mode 100644 index 00000000..1f3e09ba --- /dev/null +++ b/kate/part/syntax/data/xmldebug.xml @@ -0,0 +1,594 @@ + + + + + + CDATA + ID + IDREF + IDREFS + ENTITY + ENTITIES + NMTOKEN + NMTOKENS + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/xorg.xml b/kate/part/syntax/data/xorg.xml new file mode 100644 index 00000000..14964347 --- /dev/null +++ b/kate/part/syntax/data/xorg.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/xslt.xml b/kate/part/syntax/data/xslt.xml new file mode 100644 index 00000000..bde62b05 --- /dev/null +++ b/kate/part/syntax/data/xslt.xml @@ -0,0 +1,439 @@ + + + + +]> + + + + + + + xsl:value-of + xsl:output + xsl:decimal-format + xsl:apply-templates + xsl:param + xsl:transform + xsl:namespace-alias + xsl:comment + xsl:element + xsl:attribute + xsl:apply-imports + xsl:text + xsl:when + xsl:template + xsl:processing-instruction + xsl:include + xsl:copy-of + xsl:copy + xsl:with-param + xsl:stylesheet + xsl:for-each + xsl:choose + xsl:sort + xsl:otherwise + xsl:key + xsl:variable + xsl:number + xsl:message + xsl:fallback + xsl:strip-space + xsl:import + xsl:preserve-space + xsl:if + xsl:call-template + xsl:attribute-set + + + + xsl:perform-sort + xsl:import-schema + xsl:for-each-group + xsl:sequence + xsl:non-matching-substring + xsl:namespace + xsl:next-match + xsl:function + xsl:analyze-string + xsl:output-character + xsl:matching-substring + xsl:result-document + xsl:character-map + xsl:document + + + + format-number + position + lang + substring-before + substring + normalize-space + round + translate + starts-with + concat + local-name + key + count + document + system-property + current + boolean + number + contains + name + last + unparsed-entity-uri + sum + generate-id + function-available + element-available + false + substring-after + not + string-length + id + floor + ceiling + namespace-uri + true + string + text + + + + zero-or-one + replace + namespace-uri-for-prefix + current-grouping-key + seconds-from-duration + resolve-uri + node-kind + minutes-from-dateTime + implicit-timezone + exactly-one + current-time + current-dateTime + unordered + subtract-dates-yielding-dayTimeDuration + string-join + static-base-uri + months-from-duration + input + exists + default-collation + dateTime + current-group + current-date + collection + timezone-from-time + matches + local-name-from-QName + day-from-date + timezone-from-date + round-half-to-even + month-from-dateTime + month-from-date + hours-from-duration + escape-uri + distinct-values + avg + years-from-duration + unparsed-text + unparsed-entity-public-id + subtract-dateTimes-yielding-dayTimeDuration + subtract-dates-yielding-yearMonthDuration + string-to-codepoints + sequence-node-identical + hours-from-time + hours-from-dateTime + format-time + codepoints-to-string + trace + tokenize + subtract-dateTimes-yielding-yearMonthDuration + subsequence + seconds-from-dateTime + regex-group + one-or-more + node-name + namespace-uri-from-QName + min + idref + format-dateTime + format-date + days-from-duration + compare + base-uri + seconds-from-time + in-scope-prefixes + expanded-QName + adjust-date-to-timezone + year-from-date + resolve-QName + remove + QName + minutes-from-time + max + lower-case + index-of + doc + deep-equal + data + minutes-from-duration + adjust-dateTime-to-timezone + abs + timezone-from-dateTime + reverse + error + ends-with + day-from-dateTime + year-from-dateTime + upper-case + root + normalize-unicode + empty + insert-before + document-uri + adjust-time-to-timezone + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/xul.xml b/kate/part/syntax/data/xul.xml new file mode 100644 index 00000000..a93f0c06 --- /dev/null +++ b/kate/part/syntax/data/xul.xml @@ -0,0 +1,627 @@ + + + +]> + + + + + if + else + for + in + while + do + continue + break + with + try + catch + finally + switch + case + new + var + function + return + delete + true + false + void + throw + typeof + const + default + + + escape + isFinite + isNaN + Number + parseFloat + parseInt + reload + taint + unescape + untaint + write + + + Anchor + Applet + Area + Array + Boolean + Button + Checkbox + Date + document + window + Image + FileUpload + Form + Frame + Function + Hidden + Link + MimeType + Math + Max + Min + Layer + navigator + Object + Password + Plugin + Radio + RegExp + Reset + Screen + Select + String + Text + Textarea + this + Window + + + abs + acos + asin + atan + atan2 + ceil + cos + ctg + E + exp + floor + LN2 + LN10 + log + LOG2E + LOG10E + PI + pow + round + sin + sqrt + SQRT1_2 + SQRT2 + tan + + + onAbort + onBlur + onChange + onClick + onError + onFocus + onLoad + onMouseOut + onMouseOver + onReset + onSelect + onSubmit + onUnload + + + above + action + alinkColor + alert + anchor + anchors + appCodeName + applets + apply + appName + appVersion + argument + arguments + arity + availHeight + availWidth + back + background + below + bgColor + border + big + blink + blur + bold + border + call + caller + charAt + charCodeAt + checked + clearInterval + clearTimeout + click + clip + close + closed + colorDepth + complete + compile + constructor + confirm + cookie + current + cursor + data + defaultChecked + defaultSelected + defaultStatus + defaultValue + description + disableExternalCapture + domain + elements + embeds + enabledPlugin + enableExternalCapture + encoding + eval + exec + fgColor + filename + find + fixed + focus + fontcolor + fontsize + form + forms + formName + forward + frames + fromCharCode + getDate + getDay + getHours + getMiliseconds + getMinutes + getMonth + getSeconds + getSelection + getTime + getTimezoneOffset + getUTCDate + getUTCDay + getUTCFullYear + getUTCHours + getUTCMilliseconds + getUTCMinutes + getUTCMonth + getUTCSeconds + getYear + global + go + hash + height + history + home + host + hostname + href + hspace + ignoreCase + images + index + indexOf + innerHeight + innerWidth + input + italics + javaEnabled + join + language + lastIndex + lastIndexOf + lastModified + lastParen + layers + layerX + layerY + left + leftContext + length + link + linkColor + links + location + locationbar + load + lowsrc + match + MAX_VALUE + menubar + method + mimeTypes + MIN_VALUE + modifiers + moveAbove + moveBelow + moveBy + moveTo + moveToAbsolute + multiline + name + NaN + NEGATIVE_INFINITY + negative_infinity + next + open + opener + options + outerHeight + outerWidth + pageX + pageY + pageXoffset + pageYoffset + parent + parse + pathname + personalbar + pixelDepth + platform + plugins + pop + port + POSITIVE_INFINITY + positive_infinity + preference + previous + print + prompt + protocol + prototype + push + referrer + refresh + releaseEvents + reload + replace + reset + resizeBy + resizeTo + reverse + rightContext + screenX + screenY + scroll + scrollbar + scrollBy + scrollTo + search + select + selected + selectedIndex + self + setDate + setHours + setMinutes + setMonth + setSeconds + setTime + setTimeout + setUTCDate + setUTCDay + setUTCFullYear + setUTCHours + setUTCMilliseconds + setUTCMinutes + setUTCMonth + setUTCSeconds + setYear + shift + siblingAbove + siblingBelow + small + sort + source + splice + split + src + status + statusbar + strike + sub + submit + substr + substring + suffixes + sup + taintEnabled + target + test + text + title + toGMTString + toLocaleString + toLowerCase + toolbar + toSource + toString + top + toUpperCase + toUTCString + type + URL + unshift + unwatch + userAgent + UTC + value + valueOf + visibility + vlinkColor + vspace + width + watch + which + width + write + writeln + x + y + zIndex + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/yacas.xml b/kate/part/syntax/data/yacas.xml new file mode 100644 index 00000000..cd8ff3d7 --- /dev/null +++ b/kate/part/syntax/data/yacas.xml @@ -0,0 +1,237 @@ + + + + + + And + ApplyPure + ArrayCreate + ArrayGet + ArraySet + ArraySize + Atom + Berlekamp + BitAnd + BitOr + BitXor + Bodied + CTokenizer + Check + Clear + CommonLispTokenizer + Concat + ConcatStrings + CurrentFile + CurrentLine + CustomEval + CustomEval'Expression + CustomEval'Locals + CustomEval'Result + CustomEval'Stop + DefLoad + DefLoadFunction + DefMacroRuleBase + DefMacroRuleBaseListed + DefaultDirectory + DefaultTokenizer + Delete + DestructiveDelete + DestructiveInsert + DestructiveReplace + DestructiveReverse + DllEnumerate + DllLoad + DllUnload + Equals + Eval + FastArcCos + FastArcSin + FastArcTan + FastAssoc + FastCos + FastExp + FastIsPrime + FastLog + FastPower + FastSin + FastTan + FindFile + FindFunction + FlatCopy + FromBase + FromFile + FromString + FullForm + GarbageCollect + GenericTypeName + GetExtraInfo + GetPrecision + GreaterThan + Head + Hold + HoldArg + If + Infix + Insert + IsAtom + IsBodied + IsBound + IsFunction + IsGeneric + IsInfix + IsInteger + IsList + IsNumber + IsPostfix + IsPrefix + IsString + LazyGlobal + LeftPrecedence + Length + LessThan + LispRead + LispReadListed + List + Listify + Load + Local + LocalSymbols + MacroClear + MacroLocal + MacroRule + MacroRuleBase + MacroRuleBaseListed + MacroRulePattern + MacroSet + MathAbs + MathAdd + MathAnd + MathArcCos + MathArcSin + MathArcTan + MathCeil + MathCos + MathDiv + MathDivide + MathExp + MathFac + MathFloor + MathGcd + MathGetExactBits + MathLibrary + MathLog + MathMod + MathMultiply + MathNot + MathNth + MathOr + MathPi + MathPower + MathSetExactBits + MathSin + MathSqrt + MathSubtract + MathTan + MaxEvalDepth + Not + OpLeftPrecedence + OpPrecedence + OpRightPrecedence + Or + PatchLoad + PatchString + PatternCreate + PatternMatches + Postfix + Precision + Prefix + PrettyPrinter + Prog + Read + ReadToken + Replace + Retract + RightAssociative + RightPrecedence + Rule + RuleBase + RuleBaseArgList + RuleBaseDefined + RuleBaseListed + RulePattern + Secure + Set + SetExtraInfo + SetStringMid + ShiftLeft + ShiftRight + String + StringMid + Subst + SystemCall + Tail + ToBase + ToFile + ToString + TraceRule + TraceStack + Type + UnFence + UnList + Use + Version + While + Write + WriteString + XmlExplodeTag + XmlTokenizer + ` + = + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/yacc.xml b/kate/part/syntax/data/yacc.xml new file mode 100644 index 00000000..3a10b1ef --- /dev/null +++ b/kate/part/syntax/data/yacc.xml @@ -0,0 +1,164 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/yaml.xml b/kate/part/syntax/data/yaml.xml new file mode 100644 index 00000000..f67e1969 --- /dev/null +++ b/kate/part/syntax/data/yaml.xml @@ -0,0 +1,198 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/zonnon.xml b/kate/part/syntax/data/zonnon.xml new file mode 100644 index 00000000..e2c46644 --- /dev/null +++ b/kate/part/syntax/data/zonnon.xml @@ -0,0 +1,144 @@ + + + + + + accept + activity + array + as + await + begin + by + case + const + definition + div + do + else + elsif + end + exception + exit + for + if + implementation + implements + import + in + is + loop + mod + module + new + nil + object + of + on + operator + or + procedure + protocol + record + refines + repeat + return + self + termination + then + to + type + until + var + while + + + barrier + immutable + locked + private + protected + public + ref + sealed + shared + value + + + abs + assert + cap + copy + copyvalue + dec + excl + false + halt + inc + len + low + max + min + odd + pred + read + readln + reason + size + succ + true + write + writeln + + + boolean + cardinal + char + fixed + integer + real + set + string + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/data/zsh.xml b/kate/part/syntax/data/zsh.xml new file mode 100644 index 00000000..9a429a96 --- /dev/null +++ b/kate/part/syntax/data/zsh.xml @@ -0,0 +1,959 @@ + + + + + + + +]> + + + + + + + + else + for + function + in + select + until + while + elif + then + set + + + + - + . + : + alias + autoload + bg + bindkey + break + builtin + bye + cap + cd + chdir + clone + command + comparguments + compcall + compctl + compdescribe + compfiles + compgroups + compquote + comptags + comptry + compvalues + continue + dirs + disable + disown + echo + echotc + echoti + emulate + enable + eval + exec + exit + false + fc + fg + functions + getcap + getopts + hash + history + jobs + kill + let + limit + log + logout + noglob + popd + print + printf + pushd + pushln + pwd + r + rehash + return + sched + set + setcap + setopt + shift + source + stat + suspend + test + times + trap + true + ttyctl + type + ulimit + umask + unalias + unfunction + unhash + unlimit + unset + unsetopt + vared + wait + whence + where + which + zcompile + zformat + zftp + zle + zmodload + zparseopts + zprof + zpty + zregexparse + zsocket + zstyle + ztcp + + + + declare + export + float + getln + integer + unset + declare + typeset + local + read + readonly + + + + + arch + awk + bash + bunzip2 + bzcat + bzcmp + bzdiff + bzegrep + bzfgrep + bzgrep + bzip2 + bzip2recover + bzless + bzmore + cat + chattr + chgrp + chmod + chown + chvt + cp + date + dd + deallocvt + df + dir + dircolors + dmesg + dnsdomainname + domainname + du + dumpkeys + echo + ed + egrep + false + fgconsole + fgrep + fuser + gawk + getkeycodes + gocr + grep + groff + groups + gunzip + gzexe + gzip + hostname + igawk + install + kbd_mode + kbdrate + killall + last + lastb + link + ln + loadkeys + loadunimap + login + ls + lsattr + lsmod + lsmod.old + lzcat + lzcmp + lzdiff + lzegrep + lzfgrep + lzgrep + lzless + lzcat + lzma + lzmainfo + lzmore + mapscrn + mesg + mkdir + mkfifo + mknod + mktemp + more + mount + mv + nano + netstat + nisdomainname + nroff + openvt + pgawk + pidof + ping + ps + pstree + pwd + rbash + readlink + red + resizecons + rm + rmdir + run-parts + sash + sed + setfont + setkeycodes + setleds + setmetamode + setserial + sh + showkey + shred + sleep + ssed + stat + stty + su + sync + tar + tempfile + touch + troff + true + umount + uname + unicode_start + unicode_stop + unlink + unlzma + unxz + utmpdump + uuidgen + vdir + wall + wc + xz + xzcat + ypdomainname + zcat + zcmp + zdiff + zegrep + zfgrep + zforce + zgrep + zless + zmore + znew + zsh + + + aclocal + aconnect + aplay + apm + apmsleep + apropos + ar + arecord + as + as86 + autoconf + autoheader + automake + awk + basename + bc + bison + c++ + cal + cat + cc + cdda2wav + cdparanoia + cdrdao + cd-read + cdrecord + chfn + chgrp + chmod + chown + chroot + chsh + clear + cmp + co + col + comm + cp + cpio + cpp + cut + dc + dd + df + diff + diff3 + dir + dircolors + directomatic + dirname + du + env + expr + fbset + file + find + flex + flex++ + fmt + free + ftp + funzip + fuser + g++ + gawk + gc + gcc + gdb + getent + getopt + gettext + gettextize + gimp + gimp-remote + gimptool + gmake + gs + head + hexdump + id + install + join + kill + killall + ld + ld86 + ldd + less + lex + ln + locate + lockfile + logname + lp + lpr + ls + lynx + m4 + make + man + mkdir + mknod + msgfmt + mv + namei + nasm + nawk + nice + nl + nm + nm86 + nmap + nohup + nop + od + passwd + patch + pcregrep + pcretest + perl + perror + pidof + pr + printf + procmail + prune + ps2ascii + ps2epsi + ps2frag + ps2pdf + ps2ps + psbook + psmerge + psnup + psresize + psselect + pstops + rcs + rev + rm + scp + sed + seq + setterm + shred + size + size86 + skill + slogin + snice + sort + sox + split + ssh + ssh-add + ssh-agent + ssh-keygen + ssh-keyscan + stat + strings + strip + sudo + suidperl + sum + tac + tail + tee + test + tr + uniq + unlink + unzip + updatedb + updmap + uptime + users + vmstat + w + wc + wget + whatis + whereis + which + who + whoami + write + xargs + yacc + yes + zip + zsoelim + + + dcop + kdialog + kfile + xhost + xmodmap + xset + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kate/part/syntax/kateextendedattribute.cpp b/kate/part/syntax/kateextendedattribute.cpp new file mode 100644 index 00000000..463dd78f --- /dev/null +++ b/kate/part/syntax/kateextendedattribute.cpp @@ -0,0 +1,87 @@ +/* This file is part of the KDE libraries + Copyright (C) 2003, 2004 Anders Lund + Copyright (C) 2003, 2005 Hamish Rodda + Copyright (C) 2001,2002 Joseph Wenninger + Copyright (C) 2001 Christoph Cullmann + Copyright (C) 1999 Jochen Wilhelmy + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "kateextendedattribute.h" + +#include + +KateExtendedAttribute::KateExtendedAttribute(const QString& name, int defaultStyleIndex) +{ + setName(name); + setDefaultStyleIndex(defaultStyleIndex); + setPerformSpellchecking(true); +} + +int KateExtendedAttribute::indexForStyleName( const QString & name ) +{ + if (name=="dsNormal") return KTextEditor::HighlightInterface::dsNormal; + else if (name=="dsKeyword") return KTextEditor::HighlightInterface::dsKeyword; + else if (name=="dsDataType") return KTextEditor::HighlightInterface::dsDataType; + else if (name=="dsDecVal") return KTextEditor::HighlightInterface::dsDecVal; + else if (name=="dsBaseN") return KTextEditor::HighlightInterface::dsBaseN; + else if (name=="dsFloat") return KTextEditor::HighlightInterface::dsFloat; + else if (name=="dsChar") return KTextEditor::HighlightInterface::dsChar; + else if (name=="dsString") return KTextEditor::HighlightInterface::dsString; + else if (name=="dsComment") return KTextEditor::HighlightInterface::dsComment; + else if (name=="dsOthers") return KTextEditor::HighlightInterface::dsOthers; + else if (name=="dsAlert") return KTextEditor::HighlightInterface::dsAlert; + else if (name=="dsFunction") return KTextEditor::HighlightInterface::dsFunction; + else if (name=="dsRegionMarker") return KTextEditor::HighlightInterface::dsRegionMarker; + else if (name=="dsError") return KTextEditor::HighlightInterface::dsError; + + return KTextEditor::HighlightInterface::dsNormal; +} + +QString KateExtendedAttribute::name( ) const +{ + return stringProperty(AttributeName); +} + +void KateExtendedAttribute::setName( const QString & name ) +{ + setProperty(AttributeName, name); +} + +bool KateExtendedAttribute::isDefaultStyle( ) const +{ + return hasProperty(AttributeDefaultStyleIndex); +} + +int KateExtendedAttribute::defaultStyleIndex( ) const +{ + return intProperty(AttributeDefaultStyleIndex); +} + +void KateExtendedAttribute::setDefaultStyleIndex( int index ) +{ + setProperty(AttributeDefaultStyleIndex, QVariant(index)); +} + +bool KateExtendedAttribute::performSpellchecking( ) const +{ + return boolProperty(Spellchecking); +} + +void KateExtendedAttribute::setPerformSpellchecking(bool spellchecking) +{ + setProperty(Spellchecking, QVariant(spellchecking)); +} diff --git a/kate/part/syntax/kateextendedattribute.h b/kate/part/syntax/kateextendedattribute.h new file mode 100644 index 00000000..1c4d920f --- /dev/null +++ b/kate/part/syntax/kateextendedattribute.h @@ -0,0 +1,61 @@ +/* This file is part of the KDE libraries + Copyright (C) 2001,2002 Joseph Wenninger + Copyright (C) 2001 Christoph Cullmann + Copyright (C) 1999 Jochen Wilhelmy + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KATEEXTENDEDATTRIBUTE_H +#define KATEEXTENDEDATTRIBUTE_H + +#include + +class KateExtendedAttribute; + +typedef QList KateAttributeList; + +/** + * An extension of the KTextEditor::Attribute class, with convenience functions + * for access to extra kate-specific information, and a parent heirachy system + * for display in the config + */ +class KateExtendedAttribute : public KTextEditor::Attribute +{ + public: + typedef KSharedPtr Ptr; + + explicit KateExtendedAttribute(const QString& name, int defaultStyleIndex = -1); + + enum InternalProperties { + AttributeName = AttributeInternalProperty, + AttributeDefaultStyleIndex, + Spellchecking + }; + + static int indexForStyleName(const QString& name); + + QString name() const; + void setName(const QString& name); + + bool isDefaultStyle() const; + int defaultStyleIndex() const; + void setDefaultStyleIndex(int index); + + bool performSpellchecking() const; + void setPerformSpellchecking(bool spellchecking); +}; + +#endif diff --git a/kate/part/syntax/katehighlight.cpp b/kate/part/syntax/katehighlight.cpp new file mode 100644 index 00000000..31175ab1 --- /dev/null +++ b/kate/part/syntax/katehighlight.cpp @@ -0,0 +1,2227 @@ +/* This file is part of the KDE libraries + Copyright (C) 2007 Mirko Stocker + Copyright (C) 2007 Matthew Woehlke + Copyright (C) 2003, 2004 Anders Lund + Copyright (C) 2003 Hamish Rodda + Copyright (C) 2001,2002 Joseph Wenninger + Copyright (C) 2001 Christoph Cullmann + Copyright (C) 1999 Jochen Wilhelmy + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +//BEGIN INCLUDES +#include "katehighlight.h" + +#include "katehighlighthelpers.h" +#include "katetextline.h" +#include "katedocument.h" +#include "katesyntaxdocument.h" +#include "katerenderer.h" +#include "kateglobal.h" +#include "kateschema.h" +#include "kateconfig.h" +#include "kateextendedattribute.h" +#include "katedefaultcolors.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +//END + +//BEGIN defines +// x is a QString. if x is "true" or "1" this expression returns "true" +#define IS_TRUE(x) x.toLower() == QLatin1String("true") || x.toInt() == 1 +//END defines + +//BEGIN STATICS +namespace { +const QString stdDeliminator = QString (" \t.():!+,-<=>%&*/;?[]^{|}~\\"); + +QColor toColor(const QString& configEntry) +{ + // note: color is stored in hex format in the config files, i.e.: ffafafae + // it's not using the leading hash though so the QString ctor taking a QStirng + // fails. + return QColor(QRgb(configEntry.toUInt(0, 16))); +} + +} +//END + +//BEGIN KateHighlighting +KateHighlighting::KateHighlighting(const KateSyntaxModeListItem *def) : refCount(0) +{ + errorsAndWarnings = ""; + building=false; + noHl = false; + m_foldingIndentationSensitive = false; + folding=false; + + if (def == 0) + { + noHl = true; + iName = "None"; // not translated internal name (for config and more) + iNameTranslated = i18nc("Syntax highlighting", "None"); // user visible name + iSection = ""; + makeNoneContext(); + } + else + { + iName = def->name; + iNameTranslated = def->nameTranslated; + iSection = def->section; + iHidden = def->hidden; + identifier = def->identifier; + iVersion=def->version; + iStyle = def->style; + iAuthor=def->author; + iLicense=def->license; + } + + deliminator = stdDeliminator; +} + +KateHighlighting::~KateHighlighting() +{ + // cleanup ;) + cleanup (); + + qDeleteAll(m_additionalData); +} + +void KateHighlighting::makeNoneContext() +{ + iHidden = false; + m_additionalData.insert( "none", new HighlightPropertyBag ); + m_additionalData["none"]->deliminator = stdDeliminator; + m_additionalData["none"]->wordWrapDeliminator = stdDeliminator; + m_hlIndex[0] = "none"; + m_ctxIndex[0]= "none"; + m_contexts.push_back(new KateHlContext("None", + 0, + KateHlContextModification(), + false, + KateHlContextModification(), + false, + false, + false, + KateHlContextModification())); +} + +void KateHighlighting::cleanup () +{ + qDeleteAll (m_contexts); + m_contexts.clear (); + + qDeleteAll (m_hlItemCleanupList); + m_hlItemCleanupList.clear (); + + m_attributeArrays.clear (); + + internalIDList.clear(); +} + +KateHlContext *KateHighlighting::generateContextStack (Kate::TextLineData::ContextStack &contextStack, + KateHlContextModification modification, + int &indexLastContextPreviousLine) +{ + while (true) + { + switch (modification.type) + { + /** + * stay, do nothing, just return the last context + * in the stack or 0 + */ + case KateHlContextModification::doNothing: + return contextNum (contextStack.isEmpty() ? 0 : contextStack.last()); + + /** + * just add a new context to the stack + * and return this one + */ + case KateHlContextModification::doPush: + contextStack.append (modification.newContext); + return contextNum (modification.newContext); + + /** + * pop some contexts + add a new one afterwards, immediate.... + */ + case KateHlContextModification::doPopsAndPush: + // resize stack + contextStack.resize ((modification.pops >= contextStack.size()) ? 0 : (contextStack.size() - modification.pops)); + + // push imediate the new context.... + // don't handle the previous line stuff at all.... + // ### TODO ### think about this + contextStack.append (modification.newContext); + return contextNum (modification.newContext); + + /** + * do only pops... + */ + default: + { + // resize stack + contextStack.resize ((modification.pops >= contextStack.size()) ? 0 : (contextStack.size() - modification.pops)); + + // handling of context of previous line.... + if (indexLastContextPreviousLine >= (contextStack.size()-1)) + { + // set new index, if stack is empty, this is -1, done for eternity... + indexLastContextPreviousLine = contextStack.size() - 1; + + // stack already empty, nothing to do... + if ( contextStack.isEmpty() ) + return contextNum (0); + + KateHlContext *c = contextNum(contextStack.last()); + + // this must be a valid context, or our context stack is borked.... + Q_ASSERT (c); + + // handle line end context as new modificationContext + modification = c->lineEndContext; + continue; + } + + return contextNum (contextStack.isEmpty() ? 0 : contextStack.last()); + } + } + } + + // should never be reached + Q_ASSERT (false); + + return contextNum (0); +} + +/** + * Creates a new dynamic context or reuse an old one if it has already been created. + */ +int KateHighlighting::makeDynamicContext(KateHlContext *model, const QStringList *args) +{ + QPair key(model, args->front()); + short value; + + if (dynamicCtxs.contains(key)) + value = dynamicCtxs[key]; + else + { +#ifdef HIGHLIGHTING_DEBUG + kDebug(13010) << "new stuff: " << startctx; +#endif + + KateHlContext *newctx = model->clone(args); + + m_contexts.push_back (newctx); + + value = startctx++; + dynamicCtxs[key] = value; + KateHlManager::self()->incDynamicCtxs(); + } + + // kDebug(13010) << "Dynamic context: using context #" << value << " (for model " << model << " with args " << *args << ")"; + + return value; +} + +/** + * Drop all dynamic contexts. Shall be called with extreme care, and shall be immediately + * followed by a full HL invalidation. + */ +void KateHighlighting::dropDynamicContexts() +{ + if (refCount == 0) // unused highlighting - nothing to drop + return; + + if (noHl) // "normal texts" highlighting - no context list + return; + + qDeleteAll(m_contexts.begin()+base_startctx, m_contexts.end()); // delete dynamic contexts (after base_startctx) + m_contexts.resize (base_startctx); + + dynamicCtxs.clear(); + startctx = base_startctx; +} + +void KateHighlighting::doHighlight ( const Kate::TextLineData *_prevLine, + Kate::TextLineData *textLine, + const Kate::TextLineData *nextLine, + bool &ctxChanged, + int tabWidth, + QVector* contextChanges) +{ + if (!textLine) + return; + + // in all cases, remove old hl, or we will grow to infinite ;) + textLine->clearAttributes (); + + // reset folding start + textLine->clearMarkedAsFoldingStart (); + + // no hl set, nothing to do more than the above cleaning ;) + if (noHl) + return; + + const bool firstLine = (_prevLine == 0); + static const Kate::TextLineData dummyData; + const Kate::TextLineData * prevLine = firstLine ? &dummyData : _prevLine; + + int previousLine = -1; + KateHlContext *context; + + // duplicate the ctx stack, only once ! + Kate::TextLineData::ContextStack ctx (prevLine->contextStack()); + + if (ctx.isEmpty()) + { + // If the stack is empty, we assume to be in Context 0 (Normal) + if (firstLine) { + context = contextNum(0); + } else { + context = generateContextStack(ctx, contextNum(0)->lineEndContext, previousLine); //get stack ID to use + } + } + else + { + //kDebug(13010) << "\t\tctxNum = " << ctxNum << " contextList[ctxNum] = " << contextList[ctxNum]; // ellis + + //if (lineContinue) kDebug(13010)<hlLineContinue()) + previousLine--; + else + context = generateContextStack(ctx, context->lineEndContext, previousLine); //get stack ID to use + + //kDebug(13010)<<"test1-2-1-text4"; + + //if (lineContinue) kDebug(13010)<string(); + const int len = textLine->length(); + + // calc at which char the first char occurs, set it to length of line if never + const int firstChar = textLine->firstChar(); + const int startNonSpace = (firstChar == -1) ? len : firstChar; + + // last found item + KateHlItem *item = 0; + + // loop over the line, offset gives current offset + int offset = 0; + + KateHighlighting::HighlightPropertyBag* additionalData = m_additionalData[context->hlId]; + KateHlContext* oldContext = context; + + // optimization: list of highlighting items that need their cache reset + static QVarLengthArray cachingItems; + + // catch empty lines + if (len == 0) { + // regenerate context stack if needed + if (context->emptyLineContext) + context = generateContextStack (ctx, context->emptyLineContextModification, previousLine); + } else { + /** + * check if the folding begin/ends are balanced! + * constructed on demand! + */ + QHash *foldingStartToCount = 0; + + /** + * loop over line content! + */ + QChar lastDelimChar = 0; + KateHlContext* previous = context; + while (offset < len) + { + // If requested (happens from completion), return where context changes occur. + if ( contextChanges && ( offset == 0 || context != previous ) ) + { + previous = context; + const ContextChange change = {context, offset}; + contextChanges->append(change); + } + bool anItemMatched = false; + bool customStartEnableDetermined = false; + + foreach (KateHlItem *it, context->items) + { + item = it; + // does we only match if we are firstNonSpace? + if (item->firstNonSpace && (offset > startNonSpace)) + continue; + + // have we a column specified? if yes, only match at this column + if ((item->column != -1) && (item->column != offset)) + continue; + + if (!item->alwaysStartEnable) + { + if (item->customStartEnable) + { + if ( oldContext != context ) { + oldContext = context; + additionalData = m_additionalData[oldContext->hlId]; + } + if (customStartEnableDetermined || additionalData->deliminator.contains(lastChar)) + customStartEnableDetermined = true; + else + continue; + } + else + { + if (lastDelimChar == lastChar) { + } else if ( stdDeliminator.contains(lastChar) ) { + lastDelimChar = lastChar; + } else { + continue; + } + } + } + + int offset2 = item->checkHgl(text, offset, len-offset); + if ( item->haveCache && !item->cachingHandled ) { + cachingItems.append(item); + item->cachingHandled = true; + } + + if (offset2 <= offset) + continue; + + // dominik: on lookAhead, do not preocess any data by fixing offset2 + if (item->lookAhead) { + offset2 = offset; + } else { + // make sure the rule does not violate the text line length + if (offset2 > len) + offset2 = len; + } + + // BUG 144599: Ignore a context change that would push the same context + // without eating anything... this would be an infinite loop! + if ( item->lookAhead && ( item->ctx.pops < 2 && item->ctx.newContext == ( ctx.isEmpty() ? 0 : ctx.last() ) ) ) + continue; + + // regenerate context stack if needed + context = generateContextStack (ctx, item->ctx, previousLine); + + // dynamic context: substitute the model with an 'instance' + if (context->dynamic) + { + // try to retrieve captures from regexp + QStringList captures; + item->capturedTexts (captures); + if (!captures.empty()) + { + // Replace the top of the stack and the current context + int newctx = makeDynamicContext(context, &captures); + if (ctx.size() > 0) + ctx[ctx.size() - 1] = newctx; + + context = contextNum(newctx); + } + } + + // handle folding end or begin + if (item->region || item->region2) { + /** + * for each end region, decrement counter for that type, erase if count reaches 0! + */ + if (item->region2 && foldingStartToCount) { + QHash::iterator end = foldingStartToCount->find (-item->region2); + if (end != foldingStartToCount->end()) { + if (end.value() > 1) + --(end.value()); + else + foldingStartToCount->erase (end); + } + } + + /** + * increment counter for each begin region! + */ + if (item->region) { + // construct on demand! + if (!foldingStartToCount) + foldingStartToCount = new QHash (); + + ++(*foldingStartToCount)[item->region]; + } + } + + // even set attributes or end of region! ;) + int attribute = item->onlyConsume ? context->attr : item->attr; + if ((attribute > 0 && !item->lookAhead) || item->region2) + textLine->addAttribute (Kate::TextLineData::Attribute (offset, offset2-offset, attribute, item->region2)); + + // create 0 length attribute for begin of region, if any! + if (item->region) + textLine->addAttribute (Kate::TextLineData::Attribute (offset2, 0, attribute, item->region)); + + // only process, if lookAhead is false + if (!item->lookAhead) { + offset = offset2; + lastChar = text[offset-1]; + } + + anItemMatched = true; + break; + } + + // something matched, continue loop + if (anItemMatched) + continue; + + item = 0; + + // nothing found: set attribute of one char + // anders: unless this context does not want that! + if ( context->fallthrough ) + { + // set context to context->ftctx. + context=generateContextStack(ctx, context->ftctx, previousLine); //regenerate context stack + + //kDebug(13010)<<"context num after fallthrough at col "<attr > 0) + textLine->addAttribute (Kate::TextLineData::Attribute (offset, 1, context->attr, 0)); + + lastChar = text[offset]; + offset++; + } + } + + /** + * check if folding is not balanced and we have more starts then ends + * then this line is a possible folding start! + */ + if (foldingStartToCount) { + /** + * possible folding start, if imbalanced, aka hash not empty! + */ + if (!foldingStartToCount->isEmpty()) + textLine->markAsFoldingStartAttribute (); + + /** + * kill hash + */ + delete foldingStartToCount; + foldingStartToCount = 0; + } + } + + /** + * has the context stack changed? + */ + if ((ctxChanged = (ctx != textLine->contextStack()))) { + /** + * try to share the simple stack that contains only 0 + */ + static const Kate::TextLineData::ContextStack onlyDefaulContext (1, 0); + if (ctx == onlyDefaulContext) + textLine->setContextStack(onlyDefaulContext); + + /** + * next try: try to share data with last line + */ + else if (ctx == prevLine->contextStack()) + textLine->setContextStack(prevLine->contextStack()); + + /** + * ok, really use newly constructed stack! + */ + else + textLine->setContextStack(ctx); + } + + // write hl continue flag + textLine->setHlLineContinue (item && item->lineContinue()); + + // check for indentation based folding + if (m_foldingIndentationSensitive && (tabWidth > 0) && !textLine->markedAsFoldingStartAttribute ()) { + bool skipIndentationBasedFolding = false; + for(int i = ctx.size() - 1; i >= 0; --i) { + if (contextNum(ctx[i])->noIndentationBasedFolding) { + skipIndentationBasedFolding = true; + break; + } + } + + /** + * compute if we increase indentation in next line + */ + if (!skipIndentationBasedFolding && !isEmptyLine (textLine) && !isEmptyLine (nextLine) + && (textLine->indentDepth (tabWidth) < nextLine->indentDepth (tabWidth))) + textLine->markAsFoldingStartIndentation (); + } + + // invalidate caches + for ( int i = 0; i < cachingItems.size(); ++i) { + cachingItems[i]->cachingHandled = false; + cachingItems[i]->haveCache = false; + } + cachingItems.clear(); +} + +void KateHighlighting::getKateExtendedAttributeList (const QString &schema, QList &list, KConfig* cfg) +{ + KConfigGroup config(cfg?cfg:KateHlManager::self()->getKConfig(), + "Highlighting " + iName + " - Schema " + schema); + + list.clear(); + createKateExtendedAttribute(list); + + foreach (KateExtendedAttribute::Ptr p, list) + { + Q_ASSERT(p); + + QStringList s = config.readEntry(p->name(), QStringList()); + +// kDebug(13010)<name<0) + { + + while(s.count()<10) s<<""; + QString name = p->name(); + bool spellCheck = p->performSpellchecking(); + p->clear(); + p->setName(name); + p->setPerformSpellchecking(spellCheck); + + QString tmp=s[0]; if (!tmp.isEmpty()) p->setDefaultStyleIndex(tmp.toInt()); + + tmp=s[1]; if (!tmp.isEmpty()) p->setForeground(toColor(tmp)); + + tmp=s[2]; if (!tmp.isEmpty()) p->setSelectedForeground(toColor(tmp)); + + tmp=s[3]; if (!tmp.isEmpty()) p->setFontBold(tmp!="0"); + + tmp=s[4]; if (!tmp.isEmpty()) p->setFontItalic(tmp!="0"); + + tmp=s[5]; if (!tmp.isEmpty()) p->setFontStrikeOut(tmp!="0"); + + tmp=s[6]; if (!tmp.isEmpty()) p->setFontUnderline(tmp!="0"); + + tmp=s[7]; if (!tmp.isEmpty()) p->setBackground(toColor(tmp)); + + tmp=s[8]; if (!tmp.isEmpty()) p->setSelectedBackground(toColor(tmp)); + + tmp=s[9]; if (!tmp.isEmpty() && tmp!=QLatin1String("---")) p->setFontFamily(tmp); + + } + } +} + +void KateHighlighting::getKateExtendedAttributeListCopy( const QString &schema, QList< KateExtendedAttribute::Ptr >& list, KConfig* cfg ) +{ + QList attributes; + getKateExtendedAttributeList(schema, attributes,cfg); + + list.clear(); + + foreach (const KateExtendedAttribute::Ptr &attribute, attributes) + list.append(KateExtendedAttribute::Ptr(new KateExtendedAttribute(*attribute.data()))); +} + + +/** + * Saves the attribute definitions to the config file. + * + * @param schema The id of the schema group to save + * @param list QList containing the data to be used + */ +void KateHighlighting::setKateExtendedAttributeList(const QString &schema, QList &list, KConfig *cfg, bool writeDefaultsToo) +{ + KConfigGroup config(cfg?cfg:KateHlManager::self()->getKConfig(), + "Highlighting " + iName + " - Schema "+ schema); + + QStringList settings; + + KateAttributeList defList; + KateHlManager::self()->getDefaults(schema, defList); + + foreach (const KateExtendedAttribute::Ptr& p, list) + { + Q_ASSERT(p); + + settings.clear(); + uint defStyle=p->defaultStyleIndex(); + KTextEditor::Attribute::Ptr a(defList[defStyle]); + settings<defaultStyleIndex(),10); + settings<<(p->hasProperty(QTextFormat::ForegroundBrush)?QString::number(p->foreground().color().rgb(),16):(writeDefaultsToo?QString::number(a->foreground().color().rgb(),16):"")); + settings<<(p->hasProperty(KTextEditor::Attribute::SelectedForeground)?QString::number(p->selectedForeground().color().rgb(),16):(writeDefaultsToo?QString::number(a->selectedForeground().color().rgb(),16):"")); + settings<<(p->hasProperty(QTextFormat::FontWeight)?(p->fontBold()?"1":"0"):(writeDefaultsToo?(a->fontBold()?"1":"0"):"")); + settings<<(p->hasProperty(QTextFormat::FontItalic)?(p->fontItalic()?"1":"0"):(writeDefaultsToo?(a->fontItalic()?"1":"0"):"")); + settings<<(p->hasProperty(QTextFormat::FontStrikeOut)?(p->fontStrikeOut()?"1":"0"):(writeDefaultsToo?(a->fontStrikeOut()?"1":"0"):"")); + settings<<(p->hasProperty(QTextFormat::FontUnderline)?(p->fontUnderline()?"1":"0"):(writeDefaultsToo?(a->fontUnderline()?"1":"0"):"")); + settings<<(p->hasProperty(QTextFormat::BackgroundBrush)?QString::number(p->background().color().rgb(),16):((writeDefaultsToo && a->hasProperty(QTextFormat::BackgroundBrush))?QString::number(a->background().color().rgb(),16):"")); + settings<<(p->hasProperty(KTextEditor::Attribute::SelectedBackground)?QString::number(p->selectedBackground().color().rgb(),16):((writeDefaultsToo&& a->hasProperty(KTextEditor::Attribute::SelectedBackground))?QString::number(a->selectedBackground().color().rgb(),16):"")); + settings<<(p->hasProperty(QTextFormat::FontFamily)?(p->fontFamily()):(writeDefaultsToo?a->fontFamily():QString())); + settings<<"---"; + config.writeEntry(p->name(),settings); + } +} + +const QHash& KateHighlighting::getCharacterEncodings( int attrib ) const +{ + return m_additionalData[ hlKeyForAttrib( attrib ) ]->characterEncodings; +} + +const KatePrefixStore& KateHighlighting::getCharacterEncodingsPrefixStore( int attrib ) const +{ + return m_additionalData[ hlKeyForAttrib( attrib ) ]->characterEncodingsPrefixStore; +} + +const QHash& KateHighlighting::getReverseCharacterEncodings( int attrib ) const +{ + return m_additionalData[ hlKeyForAttrib( attrib ) ]->reverseCharacterEncodings; +} + +int KateHighlighting::getEncodedCharactersInsertionPolicy( int attrib ) const +{ + return m_additionalData[ hlKeyForAttrib( attrib ) ]->encodedCharactersInsertionPolicy; +} + +void KateHighlighting::addCharacterEncoding( const QString& key, const QString& encoding, const QChar& c ) +{ + m_additionalData[ key ]->characterEncodingsPrefixStore.addPrefix(encoding); + m_additionalData[ key ]->characterEncodings[ encoding ] = c; + m_additionalData[ key ]->reverseCharacterEncodings[ c ] = encoding; +} + +/** + * Increase the usage count, and trigger initialization if needed. + */ +void KateHighlighting::use() +{ + if (refCount == 0) + init(); + + refCount++; +} + +/** + * Decrease the usage count, and trigger cleanup if needed. + */ +void KateHighlighting::release() +{ + refCount--; + + if (refCount == 0) + done(); +} + +/** + * Initialize a context for the first time. + */ + +void KateHighlighting::init() +{ + if (noHl) + return; + + qDeleteAll(m_contexts); + m_contexts.clear (); + + makeContextList(); + + if (noHl) // something went wrong, fill something in + makeNoneContext(); +} + + +/** + * If there is no document using the highlighting style free the complete + * context structure. + */ +void KateHighlighting::done() +{ + if (noHl) + return; + + cleanup (); +} + +/** + * KateHighlighting - createKateExtendedAttribute + * This function reads the itemData entries from the config file, which specifies the + * default attribute styles for matched items/contexts. + * + * @param list A reference to the internal list containing the parsed default config + */ +void KateHighlighting::createKateExtendedAttribute(QList &list) +{ + // If the internal list isn't already available read the config file + if (!noHl) { + if (internalIDList.isEmpty()) + makeContextList(); + + list=internalIDList; + } + + // If no highlighting is selected or we have no attributes we need only one default. + if (noHl || list.isEmpty()) + list.append(KateExtendedAttribute::Ptr(new KateExtendedAttribute(i18n("Normal Text"), KTextEditor::HighlightInterface::dsNormal))); +} + +/** + * Adds the styles of the currently parsed highlight to the itemdata list + */ +void KateHighlighting::addToKateExtendedAttributeList() +{ + //Tell the syntax document class which file we want to parse and which data group + KateHlManager::self()->syntax->setIdentifier(buildIdentifier); + KateSyntaxContextData *data = KateHlManager::self()->syntax->getGroupInfo("highlighting","itemData"); + + KateDefaultColors colors; + //begin with the real parsing + while (KateHlManager::self()->syntax->nextGroup(data)) + { + // read all attributes + QString color = KateHlManager::self()->syntax->groupData(data,QString("color")); + QString selColor = KateHlManager::self()->syntax->groupData(data,QString("selColor")); + QString bold = KateHlManager::self()->syntax->groupData(data,QString("bold")); + QString italic = KateHlManager::self()->syntax->groupData(data,QString("italic")); + QString underline = KateHlManager::self()->syntax->groupData(data,QString("underline")); + QString strikeOut = KateHlManager::self()->syntax->groupData(data,QString("strikeOut")); + QString bgColor = KateHlManager::self()->syntax->groupData(data,QString("backgroundColor")); + QString selBgColor = KateHlManager::self()->syntax->groupData(data,QString("selBackgroundColor")); + QString spellChecking = KateHlManager::self()->syntax->groupData(data,QString("spellChecking")); + QString fontFamily = KateHlManager::self()->syntax->groupData(data,QString("fontFamily")); + + KateExtendedAttribute::Ptr newData(new KateExtendedAttribute( + buildPrefix+KateHlManager::self()->syntax->groupData(data,QString("name")).simplified(), + KateExtendedAttribute::indexForStyleName(KateHlManager::self()->syntax->groupData(data,QString("defStyleNum"))))); + + /* here the custom style overrides are specified, if needed */ + if (!color.isEmpty()) + newData->setForeground(colors.adaptToScheme(QColor(color), KateDefaultColors::ForegroundColor)); + if (!selColor.isEmpty()) + newData->setSelectedForeground(colors.adaptToScheme(QColor(selColor), KateDefaultColors::ForegroundColor)); + if (!bold.isEmpty()) newData->setFontBold( IS_TRUE(bold) ); + if (!italic.isEmpty()) newData->setFontItalic( IS_TRUE(italic) ); + // new attributes for the new rendering view + if (!underline.isEmpty()) newData->setFontUnderline( IS_TRUE(underline) ); + if (!strikeOut.isEmpty()) newData->setFontStrikeOut( IS_TRUE(strikeOut) ); + if (!bgColor.isEmpty()) + newData->setBackground(colors.adaptToScheme(QColor(bgColor), KateDefaultColors::BackgroundColor)); + if (!selBgColor.isEmpty()) + newData->setSelectedBackground(colors.adaptToScheme(QColor(selBgColor), KateDefaultColors::BackgroundColor)); + // is spellchecking desired? + if (!spellChecking.isEmpty()) newData->setPerformSpellchecking( IS_TRUE(spellChecking) ); + if (!fontFamily.isEmpty()) newData->setFontFamily(fontFamily); + + internalIDList.append(newData); + } + + //clean up + if (data) + KateHlManager::self()->syntax->freeGroupInfo(data); +} + +/** + * KateHighlighting - lookupAttrName + * This function is a helper for makeContextList and createKateHlItem. It looks the given + * attribute name in the itemData list up and returns its index + * + * @param name the attribute name to lookup + * @param iDl the list containing all available attributes + * + * @return The index of the attribute, or 0 if the attribute isn't found + */ +int KateHighlighting::lookupAttrName(const QString& name, QList &iDl) +{ + const QString needle = buildPrefix + name; + for (int i = 0; i < iDl.count(); i++) + if (iDl.at(i)->name() == needle) + return i; + +#ifdef HIGHLIGHTING_DEBUG + kDebug(13010)<<"Couldn't resolve itemDataName:"<index translation + * @param RegionList list of code folding region names + * @param ContextNameList list of context names + * + * @return A pointer to the newly created item object + */ +KateHlItem *KateHighlighting::createKateHlItem(KateSyntaxContextData *data, + QList &iDl, + QStringList *RegionList, + QStringList *ContextNameList) +{ + // No highlighting -> exit + if (noHl) + return 0; + + // get the (tagname) itemd type + QString dataname=KateHlManager::self()->syntax->groupItemData(data,QString("")); + + // code folding region handling: + QString beginRegionStr=KateHlManager::self()->syntax->groupItemData(data,QString("beginRegion")); + QString endRegionStr=KateHlManager::self()->syntax->groupItemData(data,QString("endRegion")); + + signed char regionId=0; + signed char regionId2=0; + + if (!beginRegionStr.isEmpty()) + { + regionId = RegionList->indexOf(beginRegionStr); + + if (regionId==-1) // if the region name doesn't already exist, add it to the list + { + (*RegionList)<indexOf(beginRegionStr); + } + + regionId++; + +#ifdef HIGHLIGHTING_DEBUG + kDebug(13010) << "########### BEG REG: " << beginRegionStr << " NUM: " << regionId; +#endif + } + + if (!endRegionStr.isEmpty()) + { + regionId2 = RegionList->indexOf(endRegionStr); + + if (regionId2==-1) // if the region name doesn't already exist, add it to the list + { + (*RegionList)<indexOf(endRegionStr); + } + + regionId2 = -regionId2 - 1; + +#ifdef HIGHLIGHTING_DEBUG + kDebug(13010) << "########### END REG: " << endRegionStr << " NUM: " << regionId2; +#endif + } + + int attr = 0; + QString tmpAttr=KateHlManager::self()->syntax->groupItemData(data,QString("attribute")).simplified(); + bool onlyConsume = tmpAttr.isEmpty(); + + // only relevant for non consumer + if (!onlyConsume) + { + if (QString("%1").arg(tmpAttr.toInt())==tmpAttr) + { + errorsAndWarnings+=i18n( + "%1: Deprecated syntax. Attribute (%2) not addressed by symbolic name
", + buildIdentifier, tmpAttr); + attr=tmpAttr.toInt(); + } + else + attr=lookupAttrName(tmpAttr,iDl); + } + + // Info about context switch + KateHlContextModification context = -1; + QString unresolvedContext; + QString tmpcontext=KateHlManager::self()->syntax->groupItemData(data,QString("context")); + if (!tmpcontext.isEmpty()) + context=getContextModificationFromString(ContextNameList, tmpcontext,unresolvedContext); + + // Get the char parameter (eg DetectChar) + QChar chr; + if (! KateHlManager::self()->syntax->groupItemData(data,QString("char")).isEmpty()) { + chr= (KateHlManager::self()->syntax->groupItemData(data,QString("char")))[0]; + } + + // Get the String parameter (eg. StringDetect) + QString stringdata=KateHlManager::self()->syntax->groupItemData(data,QString("String")); + + // Get a second char parameter (char1) (eg Detect2Chars) + QChar chr1; + if (! KateHlManager::self()->syntax->groupItemData(data,QString("char1")).isEmpty()) { + chr1= (KateHlManager::self()->syntax->groupItemData(data,QString("char1")))[0]; + } + + // Will be removed eventually. Atm used for StringDetect, WordDetect, keyword and RegExp + const QString & insensitive_str = KateHlManager::self()->syntax->groupItemData(data,QString("insensitive")); + bool insensitive = IS_TRUE( insensitive_str ); + + // for regexp only + bool minimal = IS_TRUE( KateHlManager::self()->syntax->groupItemData(data,QString("minimal")) ); + + // dominik: look ahead and do not change offset. so we can change contexts w/o changing offset1. + bool lookAhead = IS_TRUE( KateHlManager::self()->syntax->groupItemData(data,QString("lookAhead")) ); + + bool dynamic= IS_TRUE(KateHlManager::self()->syntax->groupItemData(data,QString("dynamic")) ); + + bool firstNonSpace = IS_TRUE(KateHlManager::self()->syntax->groupItemData(data,QString("firstNonSpace")) ); + + int column = -1; + QString colStr = KateHlManager::self()->syntax->groupItemData(data,QString("column")); + if (!colStr.isEmpty()) + column = colStr.toInt(); + + // Create the item corresponding to its type and set its parameters + KateHlItem *tmpItem; + + if (dataname=="keyword") + { + bool keywordInsensitive = insensitive_str.isEmpty() ? !casesensitive : insensitive; + KateHlKeyword *keyword=new KateHlKeyword(attr,context,regionId,regionId2,keywordInsensitive, + m_additionalData[ buildIdentifier ]->deliminator); + + //Get the entries for the keyword lookup list + keyword->addList(KateHlManager::self()->syntax->finddata("highlighting",stringdata)); + tmpItem=keyword; + } + else if (dataname=="Float") tmpItem= (new KateHlFloat(attr,context,regionId,regionId2)); + else if (dataname=="Int") tmpItem=(new KateHlInt(attr,context,regionId,regionId2)); + else if (dataname=="DetectChar") tmpItem=(new KateHlCharDetect(attr,context,regionId,regionId2,chr)); + else if (dataname=="Detect2Chars") tmpItem=(new KateHl2CharDetect(attr,context,regionId,regionId2,chr,chr1)); + else if (dataname=="RangeDetect") tmpItem=(new KateHlRangeDetect(attr,context,regionId,regionId2, chr, chr1)); + else if (dataname=="LineContinue") tmpItem=(new KateHlLineContinue(attr, context, regionId, regionId2, chr)); + else if (dataname=="StringDetect") tmpItem=(new KateHlStringDetect(attr,context,regionId,regionId2,stringdata,insensitive)); + else if (dataname=="WordDetect") tmpItem=(new KateHlWordDetect(attr,context,regionId,regionId2,stringdata,insensitive)); + else if (dataname=="AnyChar") tmpItem=(new KateHlAnyChar(attr,context,regionId,regionId2,stringdata)); + else if (dataname=="RegExpr") tmpItem=(new KateHlRegExpr(attr,context,regionId,regionId2,stringdata, insensitive, minimal)); + else if (dataname=="HlCChar") tmpItem= ( new KateHlCChar(attr,context,regionId,regionId2)); + else if (dataname=="HlCHex") tmpItem= (new KateHlCHex(attr,context,regionId,regionId2)); + else if (dataname=="HlCOct") tmpItem= (new KateHlCOct(attr,context,regionId,regionId2)); + else if (dataname=="HlCFloat") tmpItem= (new KateHlCFloat(attr,context,regionId,regionId2)); + else if (dataname=="HlCStringChar") tmpItem= (new KateHlCStringChar(attr,context,regionId,regionId2)); + else if (dataname=="DetectSpaces") tmpItem= (new KateHlDetectSpaces(attr,context,regionId,regionId2)); + else if (dataname=="DetectIdentifier") tmpItem= (new KateHlDetectIdentifier(attr,context,regionId,regionId2)); + else + { + // oops, unknown type. Perhaps a spelling error in the xml file + return 0; + } + + // set lookAhead & dynamic properties + tmpItem->lookAhead = lookAhead; + tmpItem->dynamic = dynamic; + tmpItem->firstNonSpace = firstNonSpace; + tmpItem->column = column; + tmpItem->onlyConsume = onlyConsume; + + if (!unresolvedContext.isEmpty()) + { + unresolvedContextReferences.insert(&(tmpItem->ctx),unresolvedContext); + } + + // remember all to delete them + m_hlItemCleanupList.append (tmpItem); + + return tmpItem; +} + +int KateHighlighting::attribute(int ctx) const +{ + return m_contexts[ctx]->attr; +} + +bool KateHighlighting::attributeRequiresSpellchecking( int attr ) +{ + QList attributeList = attributes(""); + if(attr < attributeList.length() && attributeList[attr]->hasProperty(KateExtendedAttribute::Spellchecking)) { + return attributeList[attr]->boolProperty(KateExtendedAttribute::Spellchecking); + } + return true; +} + +QString KateHighlighting::hlKeyForContext(int i) const +{ + int k = 0; + QMap::const_iterator it = m_ctxIndex.constEnd(); + while ( it != m_ctxIndex.constBegin() ) + { + --it; + k = it.key(); + if ( i >= k ) + break; + } + return it.value(); +} + +QString KateHighlighting::hlKeyForAttrib( int i ) const +{ + // find entry. This is faster than QMap::find. m_hlIndex always has an entry + // for key '0' (it is "none"), so the result is always valid. + int k = 0; + QMap::const_iterator it = m_hlIndex.constEnd(); + while ( it != m_hlIndex.constBegin() ) + { + --it; + k = it.key(); + if ( i >= k ) + break; + } + return it.value(); +} + +bool KateHighlighting::isInWord( QChar c, int attrib ) const +{ + return m_additionalData[ hlKeyForAttrib( attrib ) ]->deliminator.indexOf(c) < 0 + && !c.isSpace() + && c != QChar::fromAscii('"') && c != QChar::fromAscii('\'') && c != QChar::fromAscii('`'); +} + +bool KateHighlighting::canBreakAt( QChar c, int attrib ) const +{ + return (m_additionalData[ hlKeyForAttrib( attrib ) ]->wordWrapDeliminator.indexOf(c) != -1) + && (c != QChar::fromAscii('"') && c != QChar::fromAscii('\'')); +} + +QList KateHighlighting::emptyLines(int attrib) const +{ +#ifdef HIGHLIGHTING_DEBUG + kDebug(13010)<<"hlKeyForAttrib: "<emptyLines; +} + +signed char KateHighlighting::commentRegion(int attr) const { + QString commentRegion=m_additionalData[ hlKeyForAttrib( attr ) ]->multiLineRegion; + return (commentRegion.isEmpty()?0:(commentRegion.toShort())); +} + +bool KateHighlighting::canComment( int startAttrib, int endAttrib ) const +{ + QString k = hlKeyForAttrib( startAttrib ); + return ( k == hlKeyForAttrib( endAttrib ) && + ( ( !m_additionalData[k]->multiLineCommentStart.isEmpty() && !m_additionalData[k]->multiLineCommentEnd.isEmpty() ) || + ! m_additionalData[k]->singleLineCommentMarker.isEmpty() ) ); +} + +QString KateHighlighting::getCommentStart( int attrib ) const +{ + return m_additionalData[ hlKeyForAttrib( attrib) ]->multiLineCommentStart; +} + +QString KateHighlighting::getCommentEnd( int attrib ) const +{ + return m_additionalData[ hlKeyForAttrib( attrib ) ]->multiLineCommentEnd; +} + +QString KateHighlighting::getCommentSingleLineStart( int attrib ) const +{ + return m_additionalData[ hlKeyForAttrib( attrib) ]->singleLineCommentMarker; +} + +KateHighlighting::CSLPos KateHighlighting::getCommentSingleLinePosition( int attrib ) const +{ + return m_additionalData[ hlKeyForAttrib( attrib) ]->singleLineCommentPosition; +} + +const QHash& KateHighlighting::characterEncodings( int attrib ) const +{ + return m_additionalData[ hlKeyForAttrib( attrib) ]->characterEncodings; +} + +/** + * Helper for makeContextList. It parses the xml file for + * information, how single or multi line comments are marked + */ +void KateHighlighting::readCommentConfig() +{ + KateHlManager::self()->syntax->setIdentifier(buildIdentifier); + KateSyntaxContextData *data=KateHlManager::self()->syntax->getGroupInfo("general","comment"); + + QString cmlStart="", cmlEnd="", cmlRegion="", cslStart=""; + CSLPos cslPosition=CSLPosColumn0; + + if (data) + { + while (KateHlManager::self()->syntax->nextGroup(data)) + { + if (KateHlManager::self()->syntax->groupData(data,"name")=="singleLine") + { + cslStart=KateHlManager::self()->syntax->groupData(data,"start"); + QString cslpos=KateHlManager::self()->syntax->groupData(data,"position"); + if (cslpos=="afterwhitespace") + cslPosition=CSLPosAfterWhitespace; + else + cslPosition=CSLPosColumn0; + } + else if (KateHlManager::self()->syntax->groupData(data,"name")=="multiLine") + { + cmlStart=KateHlManager::self()->syntax->groupData(data,"start"); + cmlEnd=KateHlManager::self()->syntax->groupData(data,"end"); + cmlRegion=KateHlManager::self()->syntax->groupData(data,"region"); + } + } + + KateHlManager::self()->syntax->freeGroupInfo(data); + } + + m_additionalData[buildIdentifier]->singleLineCommentMarker = cslStart; + m_additionalData[buildIdentifier]->singleLineCommentPosition = cslPosition; + m_additionalData[buildIdentifier]->multiLineCommentStart = cmlStart; + m_additionalData[buildIdentifier]->multiLineCommentEnd = cmlEnd; + m_additionalData[buildIdentifier]->multiLineRegion = cmlRegion; +} + + + + +void KateHighlighting::readEmptyLineConfig() +{ + KateHlManager::self()->syntax->setIdentifier(buildIdentifier); + KateSyntaxContextData *data=KateHlManager::self()->syntax->getGroupInfo("general","emptyLine"); + + QList exprList; + + if (data) + { + while (KateHlManager::self()->syntax->nextGroup(data)) + { +#ifdef HIGHLIGHTING_DEBUG + kDebug(13010)<<"creating an empty line regular expression"; +#endif + + QString regexprline=KateHlManager::self()->syntax->groupData(data,"regexpr"); + bool regexprcase=(KateHlManager::self()->syntax->groupData(data,"casesensitive").toUpper().compare("TRUE")==0); + exprList.append(QRegExp(regexprline,regexprcase?Qt::CaseSensitive:Qt::CaseInsensitive)); + } + KateHlManager::self()->syntax->freeGroupInfo(data); + } + + m_additionalData[buildIdentifier]->emptyLines = exprList; +} + + + + + + +/** + * Helper for makeContextList. It parses the xml file for information, + * if keywords should be treated case(in)sensitive and creates the keyword + * delimiter list. Which is the default list, without any given weak deliminiators + */ +void KateHighlighting::readGlobalKeywordConfig() +{ + deliminator = stdDeliminator; + +#ifdef HIGHLIGHTING_DEBUG + kDebug(13010)<<"readGlobalKeywordConfig:BEGIN"; +#endif + + // Tell the syntax document class which file we want to parse + KateHlManager::self()->syntax->setIdentifier(buildIdentifier); + KateSyntaxContextData *data = KateHlManager::self()->syntax->getConfig("general","keywords"); + + if (data) + { +#ifdef HIGHLIGHTING_DEBUG + kDebug(13010)<<"Found global keyword config"; +#endif + + casesensitive = IS_TRUE( KateHlManager::self()->syntax->groupItemData(data,QString("casesensitive")) ); + + //get the weak deliminators + weakDeliminator=(KateHlManager::self()->syntax->groupItemData(data,QString("weakDeliminator"))); + +#ifdef HIGHLIGHTING_DEBUG + kDebug(13010)<<"weak delimiters are: "< -1) + deliminator.remove (f, 1); + } + + QString addDelim = (KateHlManager::self()->syntax->groupItemData(data,QString("additionalDeliminator"))); + + if (!addDelim.isEmpty()) + deliminator=deliminator+addDelim; + + KateHlManager::self()->syntax->freeGroupInfo(data); + } + else + { + //Default values + casesensitive=true; + weakDeliminator=QString(""); + } + +#ifdef HIGHLIGHTING_DEBUG + kDebug(13010)<<"readGlobalKeywordConfig:END"; + kDebug(13010)<<"delimiterCharacters are: "<deliminator = deliminator; +} + +/** + * Helper for makeContextList. It parses the xml file for any wordwrap + * deliminators, characters * at which line can be broken. In case no keyword + * tag is found in the xml file, the wordwrap deliminators list defaults to the + * standard denominators. In case a keyword tag is defined, but no + * wordWrapDeliminator attribute is specified, the deliminator list as computed + * in readGlobalKeywordConfig is used. + * + * @return the computed delimiter string. + */ +void KateHighlighting::readWordWrapConfig() +{ +#ifdef HIGHLIGHTING_DEBUG + kDebug(13010)<<"readWordWrapConfig:BEGIN"; +#endif + + // Tell the syntax document class which file we want to parse + KateHlManager::self()->syntax->setIdentifier(buildIdentifier); + KateSyntaxContextData *data = KateHlManager::self()->syntax->getConfig("general","keywords"); + + QString wordWrapDeliminator = stdDeliminator; + if (data) + { +#ifdef HIGHLIGHTING_DEBUG + kDebug(13010)<<"Found global keyword config"; +#endif + + wordWrapDeliminator = (KateHlManager::self()->syntax->groupItemData(data,QString("wordWrapDeliminator"))); + //when no wordWrapDeliminator is defined use the deliminator list + if ( wordWrapDeliminator.length() == 0 ) wordWrapDeliminator = deliminator; + +#ifdef HIGHLIGHTING_DEBUG + kDebug(13010) << "word wrap deliminators are " << wordWrapDeliminator; +#endif + + KateHlManager::self()->syntax->freeGroupInfo(data); + } + +#ifdef HIGHLIGHTING_DEBUG + kDebug(13010)<<"readWordWrapConfig:END"; +#endif + + m_additionalData[buildIdentifier]->wordWrapDeliminator = wordWrapDeliminator; +} + +void KateHighlighting::readIndentationConfig() +{ + m_indentation = ""; + + KateHlManager::self()->syntax->setIdentifier(buildIdentifier); + KateSyntaxContextData *data = KateHlManager::self()->syntax->getConfig("general","indentation"); + + if (data) + { + m_indentation = (KateHlManager::self()->syntax->groupItemData(data,QString("mode"))); + + KateHlManager::self()->syntax->freeGroupInfo(data); + } +} + +void KateHighlighting::readFoldingConfig() +{ +#ifdef HIGHLIGHTING_DEBUG + kDebug(13010)<<"readfoldignConfig:BEGIN"; +#endif + + // Tell the syntax document class which file we want to parse + KateHlManager::self()->syntax->setIdentifier(buildIdentifier); + KateSyntaxContextData *data = KateHlManager::self()->syntax->getConfig("general","folding"); + + if (data) + { +#ifdef HIGHLIGHTING_DEBUG + kDebug(13010)<<"Found global keyword config"; +#endif + + m_foldingIndentationSensitive = IS_TRUE( KateHlManager::self()->syntax->groupItemData(data, QString("indentationsensitive")) ); + + KateHlManager::self()->syntax->freeGroupInfo(data); + } + else + { + //Default values + m_foldingIndentationSensitive = false; + } + +#ifdef HIGHLIGHTING_DEBUG + kDebug(13010)<<"readfoldingConfig:END"; + kDebug(13010)<<"############################ use indent for fold are: "<syntax->setIdentifier(buildIdentifier); + KateSyntaxContextData *data=KateHlManager::self()->syntax->getGroupInfo("spellchecking","encoding"); + + if (data) + { + while (KateHlManager::self()->syntax->nextGroup(data)) + { + QString encoding = KateHlManager::self()->syntax->groupData(data,"string"); + QString character = KateHlManager::self()->syntax->groupData(data,"char"); + QString ignored = KateHlManager::self()->syntax->groupData(data,"ignored"); + + const bool ignoredIsTrue = IS_TRUE(ignored); + if(encoding.isEmpty() || (character.isEmpty() && !ignoredIsTrue)) + { + continue; + } + QRegExp newLineRegExp("\\r|\\n"); + if(encoding.indexOf(newLineRegExp) >= 0) + { + encoding.replace(newLineRegExp, "<\\n|\\r>"); + +#ifdef HIGHLIGHTING_DEBUG + kDebug() << "Encoding" << encoding + << "contains new-line characters. Ignored."; +#endif + } + QChar c = (character.isEmpty() || ignoredIsTrue) ? QChar() : character[0]; + addCharacterEncoding(buildIdentifier, encoding, c); + } + + KateHlManager::self()->syntax->freeGroupInfo(data); + } + + data=KateHlManager::self()->syntax->getConfig("spellchecking","configuration"); + if (data) + { + QString policy = KateHlManager::self()->syntax->groupItemData(data,"encodingReplacementPolicy"); + QString policyLowerCase = policy.toLower(); + int p; + + if(policyLowerCase == "encodewhenpresent") { + p = KateDocument::EncodeWhenPresent; + } + else if(policyLowerCase == "encodealways") { + p = KateDocument::EncodeAlways; + } + else { + p = KateDocument::EncodeNever; + } + + m_additionalData[buildIdentifier]->encodedCharactersInsertionPolicy = p; + + KateHlManager::self()->syntax->freeGroupInfo(data); + } +} + +void KateHighlighting::createContextNameList(QStringList *ContextNameList,int ctx0) +{ +#ifdef HIGHLIGHTING_DEBUG + kDebug(13010)<<"creatingContextNameList:BEGIN"; +#endif + + if (ctx0 == 0) + ContextNameList->clear(); + + KateHlManager::self()->syntax->setIdentifier(buildIdentifier); + + KateSyntaxContextData *data=KateHlManager::self()->syntax->getGroupInfo("highlighting","context"); + + int id=ctx0; + + if (data) + { + while (KateHlManager::self()->syntax->nextGroup(data)) + { + QString tmpAttr=KateHlManager::self()->syntax->groupData(data,QString("name")).simplified(); + if (tmpAttr.isEmpty()) + { + tmpAttr = QString("!KATE_INTERNAL_DUMMY! %1").arg(id); + errorsAndWarnings += i18n("%1: Deprecated syntax. Context %2 has no symbolic name
", buildIdentifier, id-ctx0); + } + else tmpAttr = buildPrefix + tmpAttr; + (*ContextNameList) << tmpAttr; + id++; + } + KateHlManager::self()->syntax->freeGroupInfo(data); + } + +#ifdef HIGHLIGHTING_DEBUG + kDebug(13010)<<"creatingContextNameList:END"; +#endif +} + +KateHlContextModification KateHighlighting::getContextModificationFromString(QStringList *ContextNameList, QString tmpLineEndContext, /*NO CONST*/ QString &unres) +{ + // nothing unresolved + unres = ""; + + // context to push on stack + int context = -1; + + // number of contexts to pop + int pops = 0; + + // we allow arbitrary #stay and #pop at the start + bool anyFound = false; + while (tmpLineEndContext.startsWith(QLatin1String("#stay")) || + tmpLineEndContext.startsWith(QLatin1String("#pop"))) + { + // ignore stay + if (tmpLineEndContext.startsWith(QLatin1String("#stay"))) + { + tmpLineEndContext.remove (0, 5); + } + else // count the pops + { + ++pops; + tmpLineEndContext.remove (0, 4); + } + + anyFound = true; + } + + /** + * we want a ! if we have found any pop or push and still have stuff in the string... + */ + if (anyFound && !tmpLineEndContext.isEmpty()) + { + if (tmpLineEndContext.startsWith('!')) + tmpLineEndContext.remove (0, 1); + } + + /** + * empty string, done + */ + if (tmpLineEndContext.isEmpty()) + return KateHlContextModification (context, pops); + + /** + * handle the remaining string, this might be a ##contextname + * or a normal contextname.... + */ + if ( tmpLineEndContext.contains("##")) + { + int o = tmpLineEndContext.indexOf("##"); + // FIXME at least with 'foo##bar'-style contexts the rules are picked up + // but the default attribute is not + QString tmp=tmpLineEndContext.mid(o+2); + if (!embeddedHls.contains(tmp)) embeddedHls.insert(tmp,KateEmbeddedHlInfo()); + unres=tmp+':'+tmpLineEndContext.left(o); + +#ifdef HIGHLIGHTING_DEBUG + kDebug(13010) << "unres = " << unres; +#endif + + context=0; + } + + else + { + context=ContextNameList->indexOf(buildPrefix+tmpLineEndContext); + if (context==-1) + { + context=tmpLineEndContext.toInt(); + errorsAndWarnings+=i18n( + "%1:Deprecated syntax. Context %2 not addressed by a symbolic name" + , buildIdentifier, tmpLineEndContext); + } +//#warning restructure this the name list storage. +// context=context+buildContext0Offset; + } + + return KateHlContextModification (context, pops); +} + +/** + * The most important initialization function for each highlighting. It's called + * each time a document gets a highlighting style assigned. parses the xml file + * and creates a corresponding internal structure + */ +void KateHighlighting::makeContextList() +{ + if (noHl) // if this a highlighting for "normal texts" only, tere is no need for a context list creation + return; + + embeddedHls.clear(); + embeddedHighlightingModes.clear(); + unresolvedContextReferences.clear(); + RegionList.clear(); + ContextNameList.clear(); + + // prepare list creation. To reuse as much code as possible handle this + // highlighting the same way as embedded onces + embeddedHls.insert(iName,KateEmbeddedHlInfo()); + + bool something_changed; + // the context "0" id is 0 for this hl, all embedded context "0"s have offsets + startctx=base_startctx=0; + // inform everybody that we are building the highlighting contexts and itemlists + building=true; + + do + { +#ifdef HIGHLIGHTING_DEBUG + kDebug(13010)<<"**************** Outer loop in make ContextList"; + kDebug(13010)<<"**************** Hl List count:"<identifierForName(it.key()); + +#ifdef HIGHLIGHTING_DEBUG + kDebug(13010)<<"Location is:"<< identifierToUse; +#endif + + if (identifierToUse.isEmpty() ) + kWarning(13010)<<"Unknown highlighting description referenced:" << it.key() << "in" << identifier; + + buildPrefix=it.key()+':'; // attribute names get prefixed by the names + // of the highlighting definitions they belong to + + +#ifdef HIGHLIGHTING_DEBUG + kDebug(13010)<<"setting ("< 0); +} + +void KateHighlighting::handleKateHlIncludeRules() +{ + // if there are noe include rules to take care of, just return +#ifdef HIGHLIGHTING_DEBUG + kDebug(13010)<<"KateHlIncludeRules, which need attention: " <incCtx.newContext==-1) // context unresolved ? + { + + if ((*it)->incCtxN.isEmpty()) + { + // no context name given, and no valid context id set, so this item is + // going to be removed + KateHlIncludeRules::iterator it1=it; + ++it1; + delete (*it); + includeRules.erase(it); + it=it1; + } + else + { + // resolve name to id + (*it)->incCtx=getContextModificationFromString(&ContextNameList,(*it)->incCtxN,dummy).newContext; + +#ifdef HIGHLIGHTING_DEBUG + kDebug(13010)<<"Resolved "<<(*it)->incCtxN<< " to "<<(*it)->incCtx.newContext<<" for include rule"; +#endif + + // It would be good to look here somehow, if the result is valid + } + } + else ++it; //nothing to do, already resolved (by the cross definition reference resolver) + } + + // now that all KateHlIncludeRule items should be valid and completely resolved, + // do the real inclusion of the rules. + // recursiveness is needed, because context 0 could include context 1, which + // itself includes context 2 and so on. + // In that case we have to handle context 2 first, then 1, 0 + //TODO: catch circular references: eg 0->1->2->3->1 + for (int i=0; i= list->count()) return; //invalid iterator, shouldn't happen, but better have a rule prepared ;) + + int index1 = index; + int ctx = list->at(index1)->ctx; + + if (ctx == -1) return; // skip already processed entries + + // find the last entry for the given context in the KateHlIncludeRules list + // this is need if one context includes more than one. This saves us from + // updating all insert positions: + // eg: context 0: + // pos 3 - include context 2 + // pos 5 - include context 3 + // During the building of the includeRules list the items are inserted in + // ascending order, now we need it descending to make our life easier. + while (index < list->count() && list->at(index)->ctx == ctx) + { + index1 = index; + ++index; + } + + // iterate over each include rule for the context the function has been called for. + while (index1 >= 0 && index1 < list->count() && list->at(index1)->ctx == ctx) + { + KateHlContextModification ctx1 = list->at(index1)->incCtx; + + //let's see, if the included context includes other contexts + for (int index2 = 0; index2 < list->count(); ++index2) + { + if (list->at(index2)->ctx == ctx1.newContext) + { + if (index2 == index1) { + // prevent accidental infinite recursion + kWarning() << "infinite recursion in IncludeRules in language file for " << iName << "in context" << list->at(index1)->incCtxN; + continue; + } + //yes it does, so first handle that include rules, since we want to + // include those subincludes too + handleKateHlIncludeRulesRecursive(index2, list); + break; + } + } + + // if the context we want to include had sub includes, they are already inserted there. + KateHlContext *dest=m_contexts[ctx]; + KateHlContext *src=m_contexts[ctx1.newContext]; +// kDebug(3010)<<"linking included rules from "<at(index1)->includeAttrib ) + dest->attr = src->attr; + + // insert the included context's rules starting at position p + int p = list->at(index1)->pos; + + // remember some stuff + int oldLen = dest->items.size(); + uint itemsToInsert = src->items.size(); + + // resize target + dest->items.resize (oldLen + itemsToInsert); + + // move old elements + for (int i=oldLen-1; i >= p; --i) + dest->items[i+itemsToInsert] = dest->items[i]; + + // insert new stuff + for (uint i=0; i < itemsToInsert; ++i ) + dest->items[p+i] = src->items[i]; + + index = index1; //backup the iterator + --index1; //move to the next entry, which has to be take care of + list->at(index)->ctx = -1; // set ctx to -1 to mark already processed entries + } +} + +/** + * Add one highlight to the contextlist. + * + * @return the number of contexts after this is added. + */ +int KateHighlighting::addToContextList(const QString &ident, int ctx0) +{ + //kDebug(13010)<<"=== Adding hl with ident '"< iDl = internalIDList; + + createContextNameList(&ContextNameList,ctx0); + +#ifdef HIGHLIGHTING_DEBUG + kDebug(13010)<<"Parsing Context structure"; +#endif + + //start the real work + uint i=buildContext0Offset; + KateSyntaxContextData * data = KateHlManager::self()->syntax->getGroupInfo("highlighting", "context"); + if (data) + { + while (KateHlManager::self()->syntax->nextGroup(data)) + { +#ifdef HIGHLIGHTING_DEBUG + kDebug(13010)<<"Found a context in file, building structure now"; +#endif + + //BEGIN - Translation of the attribute parameter + QString tmpAttr=KateHlManager::self()->syntax->groupData(data,QString("attribute")).simplified(); + int attr; + if (QString("%1").arg(tmpAttr.toInt())==tmpAttr) + attr=tmpAttr.toInt(); + else + attr=lookupAttrName(tmpAttr,iDl); + //END - Translation of the attribute parameter + + QString tmpLineEndContext=KateHlManager::self()->syntax->groupData(data,QString("lineEndContext")).simplified(); + KateHlContextModification context; + + context=getContextModificationFromString(&ContextNameList, tmpLineEndContext,dummy); + + QString tmpNIBF = KateHlManager::self()->syntax->groupData(data, QString("noIndentationBasedFolding") ); + bool noIndentationBasedFolding=IS_TRUE(tmpNIBF); + + //BEGIN get fallthrough props + KateHlContextModification ftc = 0; // fallthrough context + QString tmpFt = KateHlManager::self()->syntax->groupData(data, QString("fallthrough") ); + const bool ft = IS_TRUE(tmpFt); + if ( ft ) + { + QString tmpFtc = KateHlManager::self()->syntax->groupData( data, QString("fallthroughContext") ); + + ftc=getContextModificationFromString(&ContextNameList, tmpFtc,dummy); + + // stay is not allowed, we need to #pop or push some context... + if (ftc.type == KateHlContextModification::doNothing) ftc = 0; + +#ifdef HIGHLIGHTING_DEBUG + kDebug(13010)<<"Setting fall through context (context "<syntax->groupData( data, QString("lineEmptyContext") ); + KateHlContextModification emptyLineContextModification; + if (!emptyLineContext.isEmpty()) + emptyLineContextModification = getContextModificationFromString(&ContextNameList, emptyLineContext, dummy); + + bool dynamic = false; + QString tmpDynamic = KateHlManager::self()->syntax->groupData(data, QString("dynamic") ); + if ( tmpDynamic.toLower() == "true" || tmpDynamic.toInt() == 1 ) + dynamic = true; + + KateHlContext *ctxNew = new KateHlContext ( + ident, + attr, + context, + ft, ftc, dynamic, noIndentationBasedFolding, + !emptyLineContext.isEmpty(), emptyLineContextModification); + + m_contexts.push_back (ctxNew); + +#ifdef HIGHLIGHTING_DEBUG + kDebug(13010) << "INDEX: " << i << " LENGTH " << m_contexts.size()-1; +#endif + + //Let's create all items for the context + while (KateHlManager::self()->syntax->nextItem(data)) + { +// kDebug(13010)<< "In make Contextlist: Item:"; + + // KateHlIncludeRules : add a pointer to each item in that context + // TODO add a attrib includeAttrib + QString tag = KateHlManager::self()->syntax->groupItemData(data,QString("")); + if ( tag == "IncludeRules" ) //if the new item is an Include rule, we have to take special care + { + QString incCtx = KateHlManager::self()->syntax->groupItemData( data, QString("context")); + QString incAttrib = KateHlManager::self()->syntax->groupItemData( data, QString("includeAttrib")); + bool includeAttrib = IS_TRUE( incAttrib ); + + // only context refernces of type Name, ##Name, and Subname##Name are allowed + if (incCtx.startsWith("##") || (!incCtx.startsWith('#'))) + { + int incCtxi = incCtx.indexOf ("##"); + //#stay, #pop is not interesting here + if (incCtxi >= 0) + { + QString incSet = incCtx.mid(incCtxi + 2); + QString incCtxN = incSet + ':' + incCtx.left(incCtxi); + + //a cross highlighting reference +#ifdef HIGHLIGHTING_DEBUG + kDebug(13010)<<"Cross highlight reference , context "<items.count(),incCtxN,includeAttrib); + + //use the same way to determine cross hl file references as other items do + if (!embeddedHls.contains(incSet)) + embeddedHls.insert(incSet,KateEmbeddedHlInfo()); +#ifdef HIGHLIGHTING_DEBUG + else + kDebug(13010)<<"Skipping embeddedHls.insert for "<incCtx), incCtxN); + + includeRules.append(ir); + } + else + { + // a local reference -> just initialize the include rule structure + incCtx=buildPrefix+incCtx.simplified (); + includeRules.append(new KateHlIncludeRule(i,m_contexts[i]->items.count(),incCtx, includeAttrib)); + } + } + + continue; + } +// TODO -- can we remove the block below?? +#if 0 + QString tag = KateHlManager::self()->syntax->groupKateExtendedAttribute(data,QString("")); + if ( tag == "IncludeRules" ) { + // attrib context: the index (jowenn, i think using names here + // would be a cool feat, goes for mentioning the context in + // any item. a map or dict?) + int ctxId = getIdFromString(&ContextNameList, + KateHlManager::self()->syntax->groupKateExtendedAttribute( data, QString("context")),dummy); // the index is *required* + if ( ctxId > -1) { // we can even reuse rules of 0 if we want to:) + kDebug(13010)<<"makeContextList["<items.first(); c; c = m_contexts[ctxId]->items.next() ) + m_contexts[i]->items.append(c); + } + else + kDebug(13010)<<"Context "<items.append(c); + + // Not supported completely atm and only one level. Subitems.(all have + // to be matched to at once) + KateSyntaxContextData * datasub = KateHlManager::self()->syntax->getSubItems(data); + for (bool tmpbool=KateHlManager::self()->syntax->nextItem(datasub); + tmpbool; + tmpbool=KateHlManager::self()->syntax->nextItem(datasub)) + { + c->subItems.resize (c->subItems.size()+1); + c->subItems[c->subItems.size()-1] = createKateHlItem(datasub,iDl,&RegionList,&ContextNameList); + } + KateHlManager::self()->syntax->freeGroupInfo(datasub); + } + } + i++; + } + + KateHlManager::self()->syntax->freeGroupInfo(data); + } else { + // error handling: no "context" element at all in the xml file + noHl = true; + kWarning() << "There is no \"context\" in the highlighting file:" << buildIdentifier; + } + + + if (RegionList.count()!=1) + folding=true; + + folding = folding || m_foldingIndentationSensitive; + + //BEGIN Resolve multiline region if possible + if (!m_additionalData[ ident ]->multiLineRegion.isEmpty()) { + long commentregionid=RegionList.indexOf( m_additionalData[ ident ]->multiLineRegion ); + if (-1==commentregionid) { + errorsAndWarnings+=i18n( + "%1: Specified multiline comment region (%2) could not be resolved
" + , buildIdentifier, m_additionalData[ ident ]->multiLineRegion ); + m_additionalData[ ident ]->multiLineRegion.clear(); + +#ifdef HIGHLIGHTING_DEBUG + kDebug(13010)<<"ERROR comment region attribute could not be resolved"; +#endif + } else { + m_additionalData[ ident ]->multiLineRegion=QString::number(commentregionid+1); + +#ifdef HIGHLIGHTING_DEBUG + kDebug(13010)<<"comment region resolved to:"<multiLineRegion; +#endif + } + } + //END Resolve multiline region if possible + return i; +} + +void KateHighlighting::clearAttributeArrays () +{ + QMutableHashIterator< QString, QList > it = m_attributeArrays; + while (it.hasNext()) + { + it.next(); + + // k, schema correct, let create the data + KateAttributeList defaultStyleList; + + KateHlManager::self()->getDefaults(it.key(), defaultStyleList); + + QList itemDataList; + getKateExtendedAttributeList(it.key(), itemDataList); + + uint nAttribs = itemDataList.count(); + QList& array = it.value(); + array.clear(); + + for (uint z = 0; z < nAttribs; z++) + { + KateExtendedAttribute::Ptr itemData = itemDataList.at(z); + KTextEditor::Attribute::Ptr newAttribute( new KTextEditor::Attribute(*defaultStyleList.at(itemData->defaultStyleIndex())) ); + + if (itemData && itemData->hasAnyProperty()) + *newAttribute += *itemData; + + array.append(newAttribute); + } + } +} + +QList KateHighlighting::attributes (const QString &schema) +{ + // found it, already floating around + if (m_attributeArrays.contains(schema)) + return m_attributeArrays[schema]; + + // k, schema correct, let create the data + QList array; + KateAttributeList defaultStyleList; + + KateHlManager::self()->getDefaults(schema, defaultStyleList); + + QList itemDataList; + getKateExtendedAttributeList(schema, itemDataList); + + uint nAttribs = itemDataList.count(); + for (uint z = 0; z < nAttribs; z++) + { + KateExtendedAttribute::Ptr itemData = itemDataList.at(z); + KTextEditor::Attribute::Ptr newAttribute( new KTextEditor::Attribute(*defaultStyleList.at(itemData->defaultStyleIndex())) ); + + if (itemData && itemData->hasAnyProperty()) + *newAttribute += *itemData; + + array.append(newAttribute); + } + + m_attributeArrays.insert(schema, array); + + return array; +} + +QStringList KateHighlighting::getEmbeddedHighlightingModes() const +{ + return embeddedHighlightingModes; +} + +bool KateHighlighting::isEmptyLine(const Kate::TextLineData *textline) const +{ + const QString &txt=textline->string(); + if (txt.isEmpty()) + return true; + + QList l; + l=emptyLines(textline->attribute(0)); + if (l.isEmpty()) return false; + foreach(const QRegExp &re,l) { + if (re.exactMatch(txt)) return true; + } + return false; +} + +//END + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/syntax/katehighlight.h b/kate/part/syntax/katehighlight.h new file mode 100644 index 00000000..00933ffd --- /dev/null +++ b/kate/part/syntax/katehighlight.h @@ -0,0 +1,413 @@ +/* This file is part of the KDE libraries + Copyright (C) 2001,2002 Joseph Wenninger + Copyright (C) 2001 Christoph Cullmann + Copyright (C) 1999 Jochen Wilhelmy + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef __KATE_HIGHLIGHT_H__ +#define __KATE_HIGHLIGHT_H__ + +#include "katetextline.h" +#include "kateextendedattribute.h" +#include "katesyntaxmanager.h" +#include "spellcheck/prefixstore.h" + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +class KateHlContext; +class KateHlItem; +class KateHlIncludeRule; +class KateTextLine; +class KateSyntaxModeListItem; +class KateSyntaxContextData; + +// same as in kmimemagic, no need to feed more data +#define KATE_HL_HOWMANY 1024 + +// min. x seconds between two dynamic contexts reset +#define KATE_DYNAMIC_CONTEXTS_RESET_DELAY (30 * 1000) + + +/** + * describe a modification of the context stack + */ +class KateHlContextModification +{ + public: + enum modType { + doNothing = 0, + doPush = 1, + doPops = 2, + doPopsAndPush = 3 + }; + + /** + * Constructor + * @param _newContext new context to push on stack + * @param _pops number of contexts to remove from stack in advance + */ + KateHlContextModification (int _newContext = -1, int _pops = 0) : type (doNothing), newContext (_newContext), pops (_pops) //krazy:exclude=explicit + { + if (newContext >= 0 && pops == 0) type = doPush; + else if (newContext < 0 && pops > 0) type = doPops; + else if (newContext >= 0 && pops > 0) type = doPopsAndPush; + else type = doNothing; + } + + public: + /** + * indicates what this modification does, for speed + */ + char type; + + /** + * new context to push on the stack + * if this is < 0, push nothing on the stack + */ + int newContext; + + /** + * number of contexts to pop from the stack + * before pushing a new context on it + */ + int pops; +}; + +class KateEmbeddedHlInfo +{ + public: + KateEmbeddedHlInfo() {loaded=false;context0=-1;} + KateEmbeddedHlInfo(bool l, int ctx0) {loaded=l;context0=ctx0;} + + public: + bool loaded; + int context0; +}; + +// some typedefs +typedef QList KateHlIncludeRules; +typedef QMap KateEmbeddedHlInfos; +typedef QMap KateHlUnresolvedCtxRefs; + +class KateHighlighting +{ + public: + KateHighlighting(const KateSyntaxModeListItem *def); + ~KateHighlighting(); + + private: + /** + * this method frees mem ;) + * used by the destructor and done(), therefor + * not only delete elements but also reset the array + * sizes, as we will reuse this object later and refill ;) + */ + void cleanup (); + + void makeNoneContext(); + + public: + struct ContextChange { + KateHlContext* toContext; + int pos; + }; + + /** + * Parse the text and fill in the context array and folding list array + * + * @param prevLine The previous line, the context array is picked up from that if present. + * @param textLine The text line to parse + * @param nextLine The next line, to check if indentation changed for indentation based folding. + * @param ctxChanged will be set to reflect if the context changed + * @param tabWidth tab width for indentation based folding, if wanted, else 0 + */ + void doHighlight ( const Kate::TextLineData *prevLine, + Kate::TextLineData *textLine, + const Kate::TextLineData *nextLine, + bool &ctxChanged, + int tabWidth = 0, + QVector* contextChanges = 0); + + void setKateExtendedAttributeList(const QString &schema, QList &, + KConfig* cfg=0 /*if 0 standard kate config*/, bool writeDefaultsToo=false); + + const QString &name() const {return iName;} + const QString &nameTranslated() const {return iNameTranslated;} + const QString §ion() const {return iSection;} + bool hidden() const {return iHidden;} + const QString &version() const {return iVersion;} + const QString &style() const { return iStyle; } + const QString &author () const { return iAuthor; } + const QString &license () const { return iLicense; } + const QString &getIdentifier() const {return identifier;} + void use(); + void release(); + + /** + * @return true if the character @p c is not a deliminator character + * for the corresponding highlight. + */ + bool isInWord( QChar c, int attrib=0 ) const; + + /** + * @return true if the character @p c is a wordwrap deliminator as specified + * in the general keyword section of the xml file. + */ + bool canBreakAt( QChar c, int attrib=0 ) const; + /** + * + */ + QList emptyLines(int attribute=0) const; + + bool isEmptyLine(const Kate::TextLineData *textline) const; + + /** + * @return true if @p beginAttr and @p endAttr are members of the same + * highlight, and there are comment markers of either type in that. + */ + bool canComment( int startAttr, int endAttr ) const; + + /** + * @return 0 if highlighting which attr is a member of does not + * define a comment region, otherwise the region is returned + */ + signed char commentRegion(int attr) const; + + /** + * @return the mulitiline comment start marker for the highlight + * corresponding to @p attrib. + */ + QString getCommentStart( int attrib=0 ) const; + + /** + * @return the muiltiline comment end marker for the highlight corresponding + * to @p attrib. + */ + QString getCommentEnd( int attrib=0 ) const; + + /** + * @return the single comment marker for the highlight corresponding + * to @p attrib. + */ + QString getCommentSingleLineStart( int attrib=0 ) const; + + + const QHash& characterEncodings( int attrib = 0 ) const; + + /** + * This enum is used for storing the information where a single line comment marker should be inserted + */ + enum CSLPos { CSLPosColumn0=0,CSLPosAfterWhitespace=1}; + + /** + * @return the single comment marker position for the highlight corresponding + * to @p attrib. + */ + CSLPos getCommentSingleLinePosition( int attrib=0 ) const; + + /** + * @return the attribute for @p context. + */ + int attribute( int context ) const; + + bool attributeRequiresSpellchecking( int attr ); + + /** + * map attribute to its highlighting file. + * the returned string is used as key for m_additionalData. + */ + QString hlKeyForAttrib( int attrib ) const; + QString hlKeyForContext( int attrib ) const; + + int defaultStyleForAttribute( int attrib ) const; + + void clearAttributeArrays (); + + QList attributes (const QString &schema); + + inline bool noHighlighting () const { return noHl; } + + // be carefull: all documents hl should be invalidated after calling this method! + void dropDynamicContexts(); + + QString indentation () { return m_indentation; } + + void getKateExtendedAttributeList(const QString &schema, QList &, KConfig* cfg=0); + void getKateExtendedAttributeListCopy(const QString &schema, QList &,KConfig* cfg=0); + + const QHash& getCharacterEncodings( int attrib ) const; + const KatePrefixStore& getCharacterEncodingsPrefixStore( int attrib ) const; + const QHash& getReverseCharacterEncodings( int attrib ) const; + int getEncodedCharactersInsertionPolicy( int attrib ) const; + + /** + * Returns a list of names of embedded modes. + */ + QStringList getEmbeddedHighlightingModes() const; + + KateHlContext *contextNum (int n) { if (n >= 0 && n < m_contexts.size()) return m_contexts[n]; Q_ASSERT (0); return m_contexts[0]; } + + private: + /** + * 'encoding' must not contain new line characters, i.e. '\n' or '\r'! + **/ + void addCharacterEncoding( const QString& key, const QString& encoding, const QChar& c ); + + private: + void init(); + void done(); + void makeContextList (); + int makeDynamicContext(KateHlContext *model, const QStringList *args); + void handleKateHlIncludeRules (); + void handleKateHlIncludeRulesRecursive(int index, KateHlIncludeRules *list); + int addToContextList(const QString &ident, int ctx0); + void addToKateExtendedAttributeList(); + void createKateExtendedAttribute (QList &list); + void readGlobalKeywordConfig(); + void readWordWrapConfig(); + void readCommentConfig(); + void readEmptyLineConfig(); + void readIndentationConfig (); + void readFoldingConfig (); + void readSpellCheckingConfig(); + + /** + * update given context stack + * @param contextStack context stack to manipulate + * @param modification description of the modification of the stack to execute + * @param indexLastContextPreviousLine index of the last context from the previous line which still is in the stack + * @return current active context, last one of the stack or default context 0 for empty stack + */ + KateHlContext *generateContextStack(Kate::TextLineData::ContextStack &contextStack, KateHlContextModification modification, int &indexLastContextPreviousLine); + + KateHlItem *createKateHlItem(KateSyntaxContextData *data, QList &iDl, QStringList *RegionList, QStringList *ContextList); + int lookupAttrName(const QString& name, QList &iDl); + + void createContextNameList(QStringList *ContextNameList, int ctx0); + KateHlContextModification getContextModificationFromString(QStringList *ContextNameList, QString tmpLineEndContext,/*NO CONST*/ QString &unres); + + QList internalIDList; + + QVector m_contexts; + + QMap< QPair, short> dynamicCtxs; + + // make them pointers perhaps + // NOTE: gets cleaned once makeContextList() finishes + KateEmbeddedHlInfos embeddedHls; + // valid once makeContextList() finishes + QStringList embeddedHighlightingModes; + KateHlUnresolvedCtxRefs unresolvedContextReferences; + QStringList RegionList; + QStringList ContextNameList; + + bool noHl; + bool folding; + bool casesensitive; + QString weakDeliminator; + QString deliminator; + + QString iName; + QString iNameTranslated; + QString iSection; + bool iHidden; + QString identifier; + QString iVersion; + QString iStyle; + QString iAuthor; + QString iLicense; + QString m_indentation; + int refCount; + int startctx, base_startctx; + + QString errorsAndWarnings; + QString buildIdentifier; + QString buildPrefix; + bool building; + uint itemData0; + uint buildContext0Offset; + KateHlIncludeRules includeRules; + bool m_foldingIndentationSensitive; + + // map schema name to attributes... + QHash< QString, QList > m_attributeArrays; + + // list of all created items to delete them later + QList m_hlItemCleanupList; + + /** + * This class holds the additional properties for one highlight + * definition, such as comment strings, deliminators etc. + * + * When a highlight is added, a instance of this class is appended to + * m_additionalData, and the current position in the attrib and context + * arrays are stored in the indexes for look up. You can then use + * hlKeyForAttrib or hlKeyForContext to find the relevant instance of this + * class from m_additionalData. + * + * If you need to add a property to a highlight, add it here. + */ + class HighlightPropertyBag { + public: + QString singleLineCommentMarker; + QString multiLineCommentStart; + QString multiLineCommentEnd; + QString multiLineRegion; + CSLPos singleLineCommentPosition; + QString deliminator; + QString wordWrapDeliminator; + QList emptyLines; + QHash characterEncodings; + KatePrefixStore characterEncodingsPrefixStore; + QHash reverseCharacterEncodings; + int encodedCharactersInsertionPolicy; + }; + + /** + * Highlight properties for each included highlight definition. + * The key is the identifier + */ + QHash m_additionalData; + + /** + * Fast lookup of hl properties, based on attribute index + * The key is the starting index in the attribute array for each file. + * @see hlKeyForAttrib + */ + QMap m_hlIndex; + QMap m_ctxIndex; + public: + inline bool foldingIndentationSensitive () { return m_foldingIndentationSensitive; } + inline bool allowsFolding(){return folding;} +}; + +#endif + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/syntax/katehighlighthelpers.cpp b/kate/part/syntax/katehighlighthelpers.cpp new file mode 100644 index 00000000..6da4aebb --- /dev/null +++ b/kate/part/syntax/katehighlighthelpers.cpp @@ -0,0 +1,890 @@ +/* This file is part of the KDE libraries + Copyright (C) 2003, 2004 Anders Lund + Copyright (C) 2003 Hamish Rodda + Copyright (C) 2001,2002 Joseph Wenninger + Copyright (C) 2001 Christoph Cullmann + Copyright (C) 1999 Jochen Wilhelmy + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +//BEGIN INCLUDES +#include "katehighlighthelpers.h" + +#include "katetextline.h" +#include "katedocument.h" +#include "katesyntaxdocument.h" +#include "katerenderer.h" +#include "kateglobal.h" +#include "kateschema.h" +#include "kateconfig.h" +#include "kateextendedattribute.h" + +#include +//END + +//BEGIN KateHlItem +KateHlItem::KateHlItem(int attribute, KateHlContextModification context,signed char regionId,signed char regionId2) + : attr(attribute), + ctx(context), + region(regionId), + region2(regionId2), + lookAhead(false), + dynamic(false), + dynamicChild(false), + firstNonSpace(false), + onlyConsume(false), + column (-1), + alwaysStartEnable (true), + customStartEnable (false), + haveCache(false), + cachingHandled(false) +{ +} + +KateHlItem::~KateHlItem() +{ +} + +void KateHlItem::dynamicSubstitute(QString &str, const QStringList *args) +{ + for (int i = 0; i < str.length() - 1; ++i) + { + if (str[i] == '%') + { + char c = str[i + 1].toLatin1(); + if (c == '%') + str.remove(i, 1); + else if (c >= '0' && c <= '9') + { + if ((int)(c - '0') < args->size()) + { + str.replace(i, 2, (*args)[c - '0']); + i += ((*args)[c - '0']).length() - 1; + } + else + { + str.remove(i, 2); + --i; + } + } + } + } +} +//END + +//BEGIN KateHlCharDetect +KateHlCharDetect::KateHlCharDetect(int attribute, KateHlContextModification context, signed char regionId,signed char regionId2, QChar c) + : KateHlItem(attribute,context,regionId,regionId2) + , sChar(c) +{ +} + +int KateHlCharDetect::checkHgl(const QString& text, int offset, int /*len*/) +{ + if (text[offset] == sChar) + return offset + 1; + + return 0; +} + +KateHlItem *KateHlCharDetect::clone(const QStringList *args) +{ + char c = sChar.toLatin1(); + + if (c < '0' || c > '9' || (c - '0') >= args->size()) + return this; + + KateHlCharDetect *ret = new KateHlCharDetect(attr, ctx, region, region2, (*args)[c - '0'][0]); + ret->dynamicChild = true; + return ret; +} +//END + +//BEGIN KateHl2CharDetect +KateHl2CharDetect::KateHl2CharDetect(int attribute, KateHlContextModification context, signed char regionId,signed char regionId2, QChar ch1, QChar ch2) + : KateHlItem(attribute,context,regionId,regionId2) + , sChar1 (ch1) + , sChar2 (ch2) +{ +} + +int KateHl2CharDetect::checkHgl(const QString& text, int offset, int len) +{ + if ((len >= 2) && text[offset++] == sChar1 && text[offset++] == sChar2) + return offset; + + return 0; +} + +KateHlItem *KateHl2CharDetect::clone(const QStringList *args) +{ + char c1 = sChar1.toLatin1(); + char c2 = sChar2.toLatin1(); + + if (c1 < '0' || c1 > '9' || (c1 - '0') >= args->size()) + return this; + + if (c2 < '0' || c2 > '9' || (c2 - '0') >= args->size()) + return this; + + KateHl2CharDetect *ret = new KateHl2CharDetect(attr, ctx, region, region2, (*args)[c1 - '0'][0], (*args)[c2 - '0'][0]); + ret->dynamicChild = true; + return ret; +} +//END + +//BEGIN KateHlStringDetect +KateHlStringDetect::KateHlStringDetect(int attribute, KateHlContextModification context, signed char regionId,signed char regionId2,const QString &s, bool inSensitive) + : KateHlItem(attribute, context,regionId,regionId2) + , str(inSensitive ? s.toUpper() : s) + , strLen (str.length()) + , _inSensitive(inSensitive) +{ +} + +int KateHlStringDetect::checkHgl(const QString& text, int offset, int len) +{ + if (len < strLen) + return 0; + + if (_inSensitive) + { + for (int i=0; i < strLen; i++) + if (text[offset++].toUpper() != str[i]) + return 0; + + return offset; + } + else + { + for (int i=0; i < strLen; i++) + if (text[offset++] != str[i]) + return 0; + + return offset; + } + + return 0; +} + +KateHlItem *KateHlStringDetect::clone(const QStringList *args) +{ + QString newstr = str; + + dynamicSubstitute(newstr, args); + + if (newstr == str) + return this; + + KateHlStringDetect *ret = new KateHlStringDetect(attr, ctx, region, region2, newstr, _inSensitive); + ret->dynamicChild = true; + return ret; +} +//END + +//BEGIN KateHlWordDetect + +KateHlWordDetect::KateHlWordDetect(int attribute, KateHlContextModification context, signed char regionId, signed char regionId2, const QString& s, bool inSensitive) + : KateHlStringDetect(attribute, context, regionId, regionId2, s, inSensitive) +{ +} + +inline bool isWordCharacter(const QChar& c) +{ + // The Qt docs say for word characters: + // \w - Matches a word character (QChar::isLetterOrNumber(), QChar::isMark(), or '_'). + // see also: http://doc.trolltech.com/qregexp.html + return c.isLetterOrNumber() || c.isMark() || c.unicode() == '_'; +} + +int KateHlWordDetect::checkHgl(const QString& text, int offset, int len) +{ + //NOTE: word boundary means: any non-word character. + + // make sure there is no letter or number before the word starts + if (offset > 0 && isWordCharacter(text.at(offset - 1))) { + return 0; + } + offset = KateHlStringDetect::checkHgl(text, offset, len); + // make sure there is no letter or number after the word ends + if (offset && offset < text.length() && isWordCharacter(text.at(offset))) { + return 0; + } + return offset; +} + +KateHlItem* KateHlWordDetect::clone(const QStringList* args) +{ + QString newstr = str; + + dynamicSubstitute(newstr, args); + + if (newstr == str) + return this; + + KateHlWordDetect *ret = new KateHlWordDetect(attr, ctx, region, region2, newstr, _inSensitive); + ret->dynamicChild = true; + return ret; +} + + +//END KateHlWordDetect + +//BEGIN KateHlRangeDetect +KateHlRangeDetect::KateHlRangeDetect(int attribute, KateHlContextModification context, signed char regionId,signed char regionId2, QChar ch1, QChar ch2) + : KateHlItem(attribute,context,regionId,regionId2) + , sChar1 (ch1) + , sChar2 (ch2) +{ +} + +int KateHlRangeDetect::checkHgl(const QString& text, int offset, int len) +{ + if (text[offset] == sChar1) + { + do + { + offset++; + len--; + if (len < 1) return 0; + } + while (text[offset] != sChar2); + + return offset + 1; + } + return 0; +} +//END + +//BEGIN KateHlKeyword +KateHlKeyword::KateHlKeyword (int attribute, KateHlContextModification context, signed char regionId,signed char regionId2, bool insensitive, const QString& delims) + : KateHlItem(attribute,context,regionId,regionId2) + , _insensitive(insensitive) + , minLen (0xFFFFFF) + , maxLen (0) +{ + alwaysStartEnable = false; + customStartEnable = true; + foreach (const QChar &c, delims) + deliminators << c; +} + +KateHlKeyword::~KateHlKeyword () +{ + qDeleteAll(dict); +} + +QSet KateHlKeyword::allKeywords() const +{ + QSet result; + foreach ( const QSet* v, dict ) { + if ( ! v ) { + continue; + } + result.unite(*v); + } + return result; +} + +void KateHlKeyword::addList(const QStringList& list) +{ + for(int i=0; i < list.count(); ++i) + { + int len = list[i].length(); + + if (minLen > len) + minLen = len; + + if (maxLen < len) + maxLen = len; + + if (len >= dict.size()) + { + uint oldSize = dict.size(); + dict.resize (len+1); + + for (int m=oldSize; m < dict.size(); ++m) + dict[m] = 0; + } + + if (!dict[len]) + dict[len] = new QSet (); + + if (!_insensitive) + dict[len]->insert(list[i]); + else + dict[len]->insert(list[i].toLower()); + } +} + +int KateHlKeyword::checkHgl(const QString& text, int offset, int len) +{ + int offset2 = offset; + int wordLen = 0; + + while ((len > wordLen) && !deliminators.contains(text[offset2])) + { + offset2++; + wordLen++; + + if (wordLen > maxLen) return 0; + } + + if (wordLen < minLen || !dict[wordLen]) return 0; + + if (!_insensitive) + { + if (dict[wordLen]->contains(QString::fromRawData(text.unicode() + offset, wordLen)) ) + return offset2; + } + else + { + if (dict[wordLen]->contains(QString::fromRawData(text.unicode() + offset, wordLen).toLower()) ) + return offset2; + } + + return 0; +} +//END + +//BEGIN KateHlInt +KateHlInt::KateHlInt(int attribute, KateHlContextModification context, signed char regionId,signed char regionId2) + : KateHlItem(attribute,context,regionId,regionId2) +{ + alwaysStartEnable = false; +} + +int KateHlInt::checkHgl(const QString& text, int offset, int len) +{ + int offset2 = offset; + + while ((len > 0) && text[offset2].isDigit()) + { + offset2++; + len--; + } + + if (offset2 > offset) + { + if (len > 0) + { + for (int i=0; i < subItems.size(); i++) + { + if ( (offset = subItems[i]->checkHgl(text, offset2, len)) ) + return offset; + } + } + + return offset2; + } + + return 0; +} +//END + +//BEGIN KateHlFloat +KateHlFloat::KateHlFloat(int attribute, KateHlContextModification context, signed char regionId,signed char regionId2) + : KateHlItem(attribute,context, regionId,regionId2) +{ + alwaysStartEnable = false; +} + +int KateHlFloat::checkHgl(const QString& text, int offset, int len) +{ + bool b = false; + bool p = false; + + while ((len > 0) && text[offset].isDigit()) + { + offset++; + len--; + b = true; + } + + if ((len > 0) && (p = (text[offset] == '.'))) + { + offset++; + len--; + + while ((len > 0) && text[offset].isDigit()) + { + offset++; + len--; + b = true; + } + } + + if (!b) + return 0; + + if ((len > 0) && ((text[offset].toAscii() & 0xdf) == 'E')) + { + offset++; + len--; + } + else + { + if (!p) + return 0; + else + { + if (len > 0) + { + for (int i=0; i < subItems.size(); ++i) + { + int offset2 = subItems[i]->checkHgl(text, offset, len); + + if (offset2) + return offset2; + } + } + + return offset; + } + } + + if ((len > 0) && (text[offset] == '-' || text[offset] =='+')) + { + offset++; + len--; + } + + b = false; + + while ((len > 0) && text[offset].isDigit()) + { + offset++; + len--; + b = true; + } + + if (b) + { + if (len > 0) + { + for (int i=0; i < subItems.size(); ++i) + { + int offset2 = subItems[i]->checkHgl(text, offset, len); + + if (offset2) + return offset2; + } + } + + return offset; + } + + return 0; +} +//END + +//BEGIN KateHlCOct +KateHlCOct::KateHlCOct(int attribute, KateHlContextModification context, signed char regionId,signed char regionId2) + : KateHlItem(attribute,context,regionId,regionId2) +{ + alwaysStartEnable = false; +} + +int KateHlCOct::checkHgl(const QString& text, int offset, int len) +{ + if (text[offset].toAscii() == '0') + { + offset++; + len--; + + int offset2 = offset; + + while ((len > 0) && (text[offset2].toAscii() >= '0' && text[offset2].toAscii() <= '7')) + { + offset2++; + len--; + } + + if (offset2 > offset) + { + if ((len > 0) && ((text[offset2].toAscii() & 0xdf) == 'L' || (text[offset].toAscii() & 0xdf) == 'U' )) + offset2++; + + return offset2; + } + } + + return 0; +} +//END + +//BEGIN KateHlCHex +KateHlCHex::KateHlCHex(int attribute, KateHlContextModification context,signed char regionId,signed char regionId2) + : KateHlItem(attribute,context,regionId,regionId2) +{ + alwaysStartEnable = false; +} + +int KateHlCHex::checkHgl(const QString& text, int offset, int len) +{ + if ((len > 1) && (text[offset++].toAscii() == '0') && ((text[offset++].toAscii() & 0xdf) == 'X' )) + { + len -= 2; + + int offset2 = offset; + + while ((len > 0) && (text[offset2].isDigit() || ((text[offset2].toAscii() & 0xdf) >= 'A' && (text[offset2].toAscii() & 0xdf) <= 'F'))) + { + offset2++; + len--; + } + + if (offset2 > offset) + { + if ((len > 0) && ((text[offset2].toAscii() & 0xdf) == 'L' || (text[offset2].toAscii() & 0xdf) == 'U' )) + offset2++; + + return offset2; + } + } + + return 0; +} +//END + +//BEGIN KateHlCFloat +KateHlCFloat::KateHlCFloat(int attribute, KateHlContextModification context, signed char regionId,signed char regionId2) + : KateHlFloat(attribute,context,regionId,regionId2) +{ + alwaysStartEnable = false; +} + +int KateHlCFloat::checkIntHgl(const QString& text, int offset, int len) +{ + int offset2 = offset; + + while ((len > 0) && text[offset].isDigit()) { + offset2++; + len--; + } + + if (offset2 > offset) + return offset2; + + return 0; +} + +int KateHlCFloat::checkHgl(const QString& text, int offset, int len) +{ + int offset2 = KateHlFloat::checkHgl(text, offset, len); + + if (offset2) + { + if ((text[offset2].toAscii() & 0xdf) == 'F' ) + offset2++; + + return offset2; + } + else + { + offset2 = checkIntHgl(text, offset, len); + + if (offset2 && ((text[offset2].toAscii() & 0xdf) == 'F' )) + return ++offset2; + else + return 0; + } +} +//END + +//BEGIN KateHlAnyChar +KateHlAnyChar::KateHlAnyChar(int attribute, KateHlContextModification context, signed char regionId,signed char regionId2, const QString& charList) + : KateHlItem(attribute, context,regionId,regionId2) + , _charList(charList) +{ +} + +int KateHlAnyChar::checkHgl(const QString& text, int offset, int) +{ + if (_charList.contains(text[offset])) + return ++offset; + + return 0; +} +//END + +//BEGIN KateHlRegExpr +KateHlRegExpr::KateHlRegExpr( int attribute, KateHlContextModification context, signed char regionId,signed char regionId2, const QString ®exp, bool insensitive, bool minimal) + : KateHlItem(attribute, context, regionId,regionId2) + , handlesLinestart (regexp.startsWith('^')) + , _regexp(regexp) + , _insensitive(insensitive) + , _minimal(minimal) + , _lastOffset(-2) // -2 is start value, -1 is "not found at all" + , Expr (regexp, _insensitive ? Qt::CaseInsensitive : Qt::CaseSensitive) +{ + // minimal or not ;) + Expr.setMinimal(_minimal); +} + +int KateHlRegExpr::checkHgl(const QString& text, int offset, int /*len*/) +{ + if (offset && handlesLinestart) + return 0; + + // optimization: if we check something on the same text as the last time, + // try to reuse what we got that time + if ( haveCache ) { + if ( offset < _lastOffset || _lastOffset == -1 ) { + // reuse last match: not found or offset before match + return 0; + } else if ( offset == _lastOffset ) { + // reuse last match: found at this position + return (_lastOffset + _lastOffsetLength); + } + } + + haveCache = true; + _lastOffset = Expr.indexIn( text, offset, QRegExp::CaretAtOffset ); + + if (_lastOffset == -1) return 0; + + _lastOffsetLength = Expr.matchedLength(); + + if ( _lastOffset == offset ) { + // only valid when we match at the exact offset + return (_lastOffset + _lastOffsetLength); + } else { + return 0; + } +} + +void KateHlRegExpr::capturedTexts (QStringList &list) +{ + list = Expr.capturedTexts(); +} + +KateHlItem *KateHlRegExpr::clone(const QStringList *args) +{ + QString regexp = _regexp; + QStringList escArgs = *args; + + for (QStringList::Iterator it = escArgs.begin(); it != escArgs.end(); ++it) + { + (*it).replace(QRegExp("(\\W)"), "\\\\1"); + } + + dynamicSubstitute(regexp, &escArgs); + + if (regexp == _regexp) + return this; + + // kDebug (13010) << "clone regexp: " << regexp; + + KateHlRegExpr *ret = new KateHlRegExpr(attr, ctx, region, region2, regexp, _insensitive, _minimal); + ret->dynamicChild = true; + return ret; +} +// //END + +//BEGIN KateHlLineContinue +KateHlLineContinue::KateHlLineContinue(int attribute, KateHlContextModification context, signed char regionId, signed char regionId2, QChar c) + : KateHlItem(attribute, context, regionId, regionId2) + , m_trailer(c.isNull() ? QLatin1Char('\\') : c) +{ +} + +int KateHlLineContinue::checkHgl(const QString& text, int offset, int len) +{ + if ((len == 1) && (text[offset] == m_trailer)) + return ++offset; + + return 0; +} +//END + +//BEGIN KateHlCStringChar +KateHlCStringChar::KateHlCStringChar(int attribute, KateHlContextModification context,signed char regionId,signed char regionId2) + : KateHlItem(attribute,context,regionId,regionId2) { +} + +// checks for C escaped chars \n and escaped hex/octal chars +static int checkEscapedChar(const QString& text, int offset, int& len) +{ + int i; + if (text[offset] == '\\' && len > 1) + { + offset++; + len--; + + switch(text[offset].toAscii()) + { + case 'a': // checks for control chars + case 'b': // we want to fall through + case 'e': + case 'f': + + case 'n': + case 'r': + case 't': + case 'v': + case '\'': + case '\"': + case '?' : // added ? ANSI C classifies this as an escaped char + case '\\': + offset++; + len--; + break; + + case 'x': // if it's like \xff + offset++; // eat the x + len--; + // these for loops can probably be + // replaced with something else but + // for right now they work + // check for hexdigits + for (i = 0; (len > 0) && (i < 2); i++) + { + const char ch = text[offset].toAscii(); + if (((ch >= '0') && (ch <= '9')) || (((ch & 0xdf) >= 'A') && ((ch & 0xdf) <= 'F'))) { + offset++; + len--; + } else { + break; + } + } + + if (i == 0) + return 0; // takes care of case '\x' + + break; + + case '0': case '1': case '2': case '3' : + case '4': case '5': case '6': case '7' : + for (i = 0; (len > 0) && (i < 3) && (text[offset] >='0'&& text[offset] <='7'); i++) + { + offset++; + len--; + } + break; + + default: + return 0; + } + + return offset; + } + + return 0; +} + +int KateHlCStringChar::checkHgl(const QString& text, int offset, int len) +{ + return checkEscapedChar(text, offset, len); +} +//END + +//BEGIN KateHlCChar +KateHlCChar::KateHlCChar(int attribute, KateHlContextModification context,signed char regionId,signed char regionId2) + : KateHlItem(attribute,context,regionId,regionId2) { +} + +int KateHlCChar::checkHgl(const QString& text, int offset, int len) +{ + if ((len > 1) && (text[offset] == '\'') && (text[offset+1] != '\'')) + { + int oldl; + oldl = len; + + len--; + + int offset2 = checkEscapedChar(text, offset + 1, len); + + if (!offset2) + { + if (oldl > 2) + { + offset2 = offset + 2; + len = oldl - 2; + } + else + { + return 0; + } + } + + if ((len > 0) && (text[offset2] == '\'')) + return ++offset2; + } + + return 0; +} +//END + +//BEGIN KateHl2CharDetect +KateHl2CharDetect::KateHl2CharDetect(int attribute, KateHlContextModification context, signed char regionId,signed char regionId2, const QChar *s) + : KateHlItem(attribute,context,regionId,regionId2) { + sChar1 = s[0]; + sChar2 = s[1]; + } +//END KateHl2CharDetect + +//BEGIN KateHlContext +KateHlContext::KateHlContext (const QString &_hlId, int attribute, KateHlContextModification _lineEndContext, bool _fallthrough, + KateHlContextModification _fallthroughContext, bool _dynamic, bool _noIndentationBasedFolding, + bool _emptyLineContext, KateHlContextModification _emptyLineContextModification) +{ + hlId = _hlId; + attr = attribute; + lineEndContext = _lineEndContext; + fallthrough = _fallthrough; + ftctx = _fallthroughContext; + dynamic = _dynamic; + dynamicChild = false; + noIndentationBasedFolding=_noIndentationBasedFolding; + emptyLineContext = _emptyLineContext; + emptyLineContextModification = _emptyLineContextModification; + if (_noIndentationBasedFolding) kDebug(13010)<dynamic ? item->clone(args) : item); + ret->items.append(i); + } + + ret->dynamicChild = true; + + return ret; +} + +KateHlContext::~KateHlContext() +{ + if (dynamicChild) + { + for (int n=0; n < items.size(); ++n) + { + if (items[n]->dynamicChild) + delete items[n]; + } + } +} +//END + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/syntax/katehighlighthelpers.h b/kate/part/syntax/katehighlighthelpers.h new file mode 100644 index 00000000..02ac40e1 --- /dev/null +++ b/kate/part/syntax/katehighlighthelpers.h @@ -0,0 +1,359 @@ +/* This file is part of the KDE libraries + Copyright (C) 2001,2002 Joseph Wenninger + Copyright (C) 2001 Christoph Cullmann + Copyright (C) 1999 Jochen Wilhelmy + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef __KATE_HIGHLIGHTHELPERS_H__ +#define __KATE_HIGHLIGHTHELPERS_H__ + +#include "katehighlight.h" + +#include + +class KateHlItem +{ + public: + KateHlItem(int attribute, KateHlContextModification context,signed char regionId, signed char regionId2); + virtual ~KateHlItem(); + + public: + // caller must keep in mind: LEN > 0 is a must !!!!!!!!!!!!!!!!!!!!!1 + // Now, the function returns the offset detected, or 0 if no match is found. + // bool linestart isn't needed, this is equivalent to offset == 0. + virtual int checkHgl(const QString& text, int offset, int len) = 0; + + virtual bool lineContinue(){return false;} + + virtual void capturedTexts (QStringList &) { } + virtual KateHlItem *clone(const QStringList *) {return this;} + + static void dynamicSubstitute(QString& str, const QStringList *args); + + QVector subItems; + int attr; + KateHlContextModification ctx; + signed char region; + signed char region2; + + bool lookAhead; + + bool dynamic; + bool dynamicChild; + bool firstNonSpace; + bool onlyConsume; + int column; + + // start enable flags, nicer than the virtual methodes + // saves function calls + bool alwaysStartEnable; + bool customStartEnable; + + // set to true when you cached something + bool haveCache; + // internal for doHighlight, don't set it in the items + bool cachingHandled; +}; + +class KateHlContext +{ + public: + KateHlContext(const QString &_hlId, int attribute, KateHlContextModification _lineEndContext, + bool _fallthrough, KateHlContextModification _fallthroughContext, bool _dynamic,bool _noIndentationBasedFolding, + bool _emptyLineContex, KateHlContextModification _emptyLineContextModification + ); + virtual ~KateHlContext(); + KateHlContext *clone(const QStringList *args); + + QVector items; + QString hlId; ///< A unique highlight identifier. Used to look up correct properties. + int attr; + KateHlContextModification lineEndContext; + /** @internal anders: possible escape if no rules matches. + false unless 'fallthrough="1|true"' (insensitive) + if true, go to ftcxt w/o eating of string. + ftctx is "fallthroughContext" in xml files, valid values are int or #pop[..] + see in KateHighlighting::doHighlight */ + bool fallthrough; + KateHlContextModification ftctx; // where to go after no rules matched + + bool dynamic; + bool dynamicChild; + bool noIndentationBasedFolding; + + bool emptyLineContext; + KateHlContextModification emptyLineContextModification; +}; + +class KateHlIncludeRule +{ + public: + explicit KateHlIncludeRule(int ctx_=0, uint pos_=0, const QString &incCtxN_="", bool incAttrib=false) + : ctx(ctx_) + , pos( pos_) + , incCtxN( incCtxN_ ) + , includeAttrib( incAttrib ) + { + incCtx=-1; + } + //KateHlIncludeRule(int ctx_, uint pos_, bool incAttrib) {ctx=ctx_;pos=pos_;incCtx=-1;incCtxN="";includeAttrib=incAttrib} + + public: + int ctx; + uint pos; + KateHlContextModification incCtx; + QString incCtxN; + bool includeAttrib; +}; + +class KateHlCharDetect : public KateHlItem +{ + public: + KateHlCharDetect(int attribute, KateHlContextModification context,signed char regionId,signed char regionId2, QChar); + + virtual int checkHgl(const QString& text, int offset, int len); + virtual KateHlItem *clone(const QStringList *args); + + private: + QChar sChar; +}; + +class KateHl2CharDetect : public KateHlItem +{ + public: + KateHl2CharDetect(int attribute, KateHlContextModification context, signed char regionId,signed char regionId2, QChar ch1, QChar ch2); + KateHl2CharDetect(int attribute, KateHlContextModification context,signed char regionId,signed char regionId2, const QChar *ch); + + virtual int checkHgl(const QString& text, int offset, int len); + virtual KateHlItem *clone(const QStringList *args); + + private: + QChar sChar1; + QChar sChar2; +}; + +class KateHlStringDetect : public KateHlItem +{ + public: + KateHlStringDetect(int attribute, KateHlContextModification context, signed char regionId,signed char regionId2, const QString &, bool inSensitive=false); + + virtual int checkHgl(const QString& text, int offset, int len); + virtual KateHlItem *clone(const QStringList *args); + + protected: + const QString str; + const int strLen; + const bool _inSensitive; +}; + +class KateHlWordDetect : public KateHlStringDetect +{ + public: + KateHlWordDetect(int attribute, KateHlContextModification context, signed char regionId, signed char regionId2, const QString &, bool inSensitive = false); + + virtual int checkHgl(const QString& text, int offset, int len); + virtual KateHlItem *clone(const QStringList *args); +}; + +class KateHlRangeDetect : public KateHlItem +{ + public: + KateHlRangeDetect(int attribute, KateHlContextModification context, signed char regionId,signed char regionId2, QChar ch1, QChar ch2); + + virtual int checkHgl(const QString& text, int offset, int len); + + private: + QChar sChar1; + QChar sChar2; +}; + +class KateHlKeyword : public KateHlItem +{ + public: + KateHlKeyword(int attribute, KateHlContextModification context,signed char regionId,signed char regionId2, bool insensitive, const QString& delims); + virtual ~KateHlKeyword (); + + void addList(const QStringList &); + virtual int checkHgl(const QString& text, int offset, int len); + QSet allKeywords() const; + + private: + QVector< QSet* > dict; + bool _insensitive; + QSet deliminators; + int minLen; + int maxLen; +}; + +class KateHlInt : public KateHlItem +{ + public: + KateHlInt(int attribute, KateHlContextModification context, signed char regionId,signed char regionId2); + + virtual int checkHgl(const QString& text, int offset, int len); +}; + +class KateHlFloat : public KateHlItem +{ + public: + KateHlFloat(int attribute, KateHlContextModification context, signed char regionId,signed char regionId2); + virtual ~KateHlFloat () {} + + virtual int checkHgl(const QString& text, int offset, int len); +}; + +class KateHlCFloat : public KateHlFloat +{ + public: + KateHlCFloat(int attribute, KateHlContextModification context, signed char regionId,signed char regionId2); + + virtual int checkHgl(const QString& text, int offset, int len); + int checkIntHgl(const QString& text, int offset, int len); +}; + +class KateHlCOct : public KateHlItem +{ + public: + KateHlCOct(int attribute, KateHlContextModification context, signed char regionId,signed char regionId2); + + virtual int checkHgl(const QString& text, int offset, int len); +}; + +class KateHlCHex : public KateHlItem +{ + public: + KateHlCHex(int attribute, KateHlContextModification context, signed char regionId,signed char regionId2); + + virtual int checkHgl(const QString& text, int offset, int len); +}; + +class KateHlLineContinue : public KateHlItem +{ + public: + KateHlLineContinue(int attribute, KateHlContextModification context, signed char regionId,signed char regionId2, QChar); + + virtual bool endEnable(QChar c) {return c == '\0';} + virtual int checkHgl(const QString& text, int offset, int len); + virtual bool lineContinue(){return true;} + + private: + QChar m_trailer; +}; + +class KateHlCStringChar : public KateHlItem +{ + public: + KateHlCStringChar(int attribute, KateHlContextModification context, signed char regionId,signed char regionId2); + + virtual int checkHgl(const QString& text, int offset, int len); +}; + +class KateHlCChar : public KateHlItem +{ + public: + KateHlCChar(int attribute, KateHlContextModification context,signed char regionId,signed char regionId2); + + virtual int checkHgl(const QString& text, int offset, int len); +}; + +class KateHlAnyChar : public KateHlItem +{ + public: + KateHlAnyChar(int attribute, KateHlContextModification context, signed char regionId,signed char regionId2, const QString& charList); + + virtual int checkHgl(const QString& text, int offset, int len); + + private: + const QString _charList; +}; + +class KateHlRegExpr : public KateHlItem +{ + public: + KateHlRegExpr(int attribute, KateHlContextModification context,signed char regionId,signed char regionId2 ,const QString &expr, bool insensitive, bool minimal); + + virtual int checkHgl(const QString& text, int offset, int len); + + virtual void capturedTexts (QStringList &); + + virtual KateHlItem *clone(const QStringList *args); + + private: + bool handlesLinestart; + QString _regexp; + bool _insensitive; + bool _minimal; + + // optimization stuff below + /// index of the last match + int _lastOffset; + /// length of the last match + int _lastOffsetLength; + + QRegExp Expr; +}; + +class KateHlDetectSpaces : public KateHlItem +{ + public: + KateHlDetectSpaces (int attribute, KateHlContextModification context,signed char regionId,signed char regionId2) + : KateHlItem(attribute,context,regionId,regionId2) {} + + virtual int checkHgl(const QString& text, int offset, int len) + { + int len2 = offset + len; + while ((offset < len2) && text[offset].isSpace()) offset++; + return offset; + } +}; + +class KateHlDetectIdentifier : public KateHlItem +{ + public: + KateHlDetectIdentifier (int attribute, KateHlContextModification context,signed char regionId,signed char regionId2) + : KateHlItem(attribute,context,regionId,regionId2) { alwaysStartEnable = false; } + + virtual int checkHgl(const QString& text, int offset, int len) + { + // first char should be a letter or underscore + if ( text[offset].isLetter() || text[offset] == QChar ('_') ) + { + // memorize length + int len2 = offset+len; + + // one char seen + offset++; + + // now loop for all other thingies + while ( + (offset < len2) + && (text[offset].isLetterOrNumber() || (text[offset] == QChar ('_'))) + ) + offset++; + + return offset; + } + + return 0; + } +}; + +//END + +#endif + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/syntax/katehighlightmenu.cpp b/kate/part/syntax/katehighlightmenu.cpp new file mode 100644 index 00000000..bb558fd2 --- /dev/null +++ b/kate/part/syntax/katehighlightmenu.cpp @@ -0,0 +1,127 @@ +/* This file is part of the KDE libraries + Copyright (C) 2001-2003 Christoph Cullmann + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +//BEGIN Includes +#include "katehighlightmenu.h" +#include "moc_katehighlightmenu.cpp" + +#include "katedocument.h" +#include "kateconfig.h" +#include "kateview.h" +#include "kateglobal.h" +#include "katesyntaxmanager.h" +#include "katesyntaxdocument.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +//END Includes + +KateHighlightingMenu::~KateHighlightingMenu() +{ + qDeleteAll (subMenus); +} + +void KateHighlightingMenu::init() +{ + m_doc = 0; + + connect(menu(),SIGNAL(aboutToShow()),this,SLOT(slotAboutToShow())); + m_actionGroup = new QActionGroup(menu()); +} + +void KateHighlightingMenu::updateMenu (KateDocument *doc) +{ + m_doc = doc; +} + +void KateHighlightingMenu::slotAboutToShow() +{ + for (int z=0; z < KateHlManager::self()->highlights(); z++) + { + QString hlName = KateHlManager::self()->hlNameTranslated (z); + QString hlSection = KateHlManager::self()->hlSection (z); + + if (!KateHlManager::self()->hlHidden(z)) + { + if ( !hlSection.isEmpty() && !names.contains(hlName) ) + { + if (!subMenusName.contains(hlSection)) + { + subMenusName << hlSection; + QMenu *qmenu = new QMenu ('&'+hlSection); + subMenus.append(qmenu); + menu()->addMenu( qmenu ); + } + + int m = subMenusName.indexOf (hlSection); + names << hlName; + QAction *a=subMenus.at(m)->addAction( '&' + hlName, this, SLOT(setHl())); + m_actionGroup->addAction(a); + a->setData(KateHlManager::self()->hlName (z)); + a->setCheckable(true); + subActions.append(a); + } + else if (!names.contains(hlName)) + { + names << hlName; + QAction *a=menu()->addAction ( '&' + hlName, this, SLOT(setHl())); + m_actionGroup->addAction(a); + a->setData(KateHlManager::self()->hlName (z)); + a->setCheckable(true); + subActions.append(a); + } + } + } + + if (!m_doc) return; + QString mode=m_doc->highlightingMode(); + for (int i=0;isetChecked(subActions[i]->data().toString()==mode); + } +} + +void KateHighlightingMenu::setHl () +{ + if (!m_doc || !sender()) return; + QAction *action=qobject_cast(sender()); + if (!action) return; + QString mode=action->data().toString(); + m_doc->setHighlightingMode(mode); + + // use change, honor this + m_doc->setDontChangeHlOnSave(); +} + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/syntax/katehighlightmenu.h b/kate/part/syntax/katehighlightmenu.h new file mode 100644 index 00000000..f9cf6d34 --- /dev/null +++ b/kate/part/syntax/katehighlightmenu.h @@ -0,0 +1,62 @@ +/* This file is part of the KDE libraries + Copyright (C) 2001-2003 Christoph Cullmann + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KATE_HIGHLIGHTMENU_H__ +#define KATE_HIGHLIGHTMENU_H__ + +#include +#include +#include + +#include + +class KateDocument; + +class KateHighlightingMenu : public KActionMenu +{ + Q_OBJECT + + public: + KateHighlightingMenu(const QString& text, QObject *parent) + : KActionMenu(text, parent) { init(); } + + ~KateHighlightingMenu(); + + void updateMenu (KateDocument *doc); + + private: + void init(); + + QPointer m_doc; + QStringList subMenusName; + QStringList names; + QList subMenus; + QList subActions; + QActionGroup *m_actionGroup; + + public Q_SLOTS: + void slotAboutToShow(); + + private Q_SLOTS: + void setHl (); +}; + + +#endif + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/syntax/katesyntaxdocument.cpp b/kate/part/syntax/katesyntaxdocument.cpp new file mode 100644 index 00000000..6bc0323f --- /dev/null +++ b/kate/part/syntax/katesyntaxdocument.cpp @@ -0,0 +1,509 @@ +/* This file is part of the KDE libraries + Copyright (C) 2001 Joseph Wenninger + Copyright (C) 2000 Scott Manson + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "katesyntaxdocument.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +// use this to turn on over verbose debug output... +#undef KSD_OVER_VERBOSE + +KateSyntaxDocument::KateSyntaxDocument(KConfig *config, bool force) + : QDomDocument() + , m_config (config) +{ + // Let's build the Mode List (katesyntaxhighlightingrc) + setupModeList(force); +} + +KateSyntaxDocument::~KateSyntaxDocument() +{ + for (int i=0; i < myModeList.size(); i++) + delete myModeList[i]; +} + +/** If the open hl file is different from the one needed, it opens + the new one and assign some other things. + identifier = File name and path of the new xml needed +*/ +bool KateSyntaxDocument::setIdentifier(const QString& identifier) +{ + // if the current file is the same as the new one don't do anything. + if(currentFile != identifier) + { + // let's open the new file + QFile f( identifier ); + + if ( f.open(QIODevice::ReadOnly) ) + { + // Let's parse the contets of the xml file + /* The result of this function should be check for robustness, + a false returned means a parse error */ + QString errorMsg; + int line, col; + bool success=setContent(&f,&errorMsg,&line,&col); + + // Ok, now the current file is the pretended one (identifier) + currentFile = identifier; + + // Close the file, is not longer needed + f.close(); + + if (!success) + { + KMessageBox::error(QApplication::activeWindow(),i18n("The error %4
has been detected in the file %1 at %2/%3
", identifier, + line, col, i18nc("QXml",errorMsg.toUtf8()))); + return false; + } + } + else + { + // Oh o, we couldn't open the file. + KMessageBox::error(QApplication::activeWindow(), i18n("Unable to open %1", identifier) ); + return false; + } + } + return true; +} + +/** + * Jump to the next group, KateSyntaxContextData::currentGroup will point to the next group + */ +bool KateSyntaxDocument::nextGroup( KateSyntaxContextData* data) +{ + if(!data) + return false; + + // No group yet so go to first child + if (data->currentGroup.isNull()) + { + // Skip over non-elements. So far non-elements are just comments + QDomNode node = data->parent.firstChild(); + while (node.isComment()) + node = node.nextSibling(); + + data->currentGroup = node.toElement(); + } + else + { + // common case, iterate over siblings, skipping comments as we go + QDomNode node = data->currentGroup.nextSibling(); + while (node.isComment()) + node = node.nextSibling(); + + data->currentGroup = node.toElement(); + } + + return !data->currentGroup.isNull(); +} + +/** + * Jump to the next item, KateSyntaxContextData::item will point to the next item + */ +bool KateSyntaxDocument::nextItem( KateSyntaxContextData* data) +{ + if(!data) + return false; + + if (data->item.isNull()) + { + QDomNode node = data->currentGroup.firstChild(); + while (node.isComment()) + node = node.nextSibling(); + + data->item = node.toElement(); + } + else + { + QDomNode node = data->item.nextSibling(); + while (node.isComment()) + node = node.nextSibling(); + + data->item = node.toElement(); + } + + return !data->item.isNull(); +} + +/** + * This function is used to fetch the atributes of the tags of the item in a KateSyntaxContextData. + */ +QString KateSyntaxDocument::groupItemData( const KateSyntaxContextData* data, const QString& name){ + if(!data) + return QString(); + + // If there's no name just return the tag name of data->item + if ( (!data->item.isNull()) && (name.isEmpty())) + { + return data->item.tagName(); + } + + // if name is not empty return the value of the attribute name + if (!data->item.isNull()) + { + return data->item.attribute(name); + } + + return QString(); + +} + +QString KateSyntaxDocument::groupData( const KateSyntaxContextData* data,const QString& name) +{ + if(!data) + return QString(); + + if (!data->currentGroup.isNull()) + { + return data->currentGroup.attribute(name); + } + else + { + return QString(); + } +} + +void KateSyntaxDocument::freeGroupInfo( KateSyntaxContextData* data) +{ + delete data; +} + +KateSyntaxContextData* KateSyntaxDocument::getSubItems(KateSyntaxContextData* data) +{ + KateSyntaxContextData *retval = new KateSyntaxContextData; + + if (data != 0) + { + retval->parent = data->currentGroup; + retval->currentGroup = data->item; + } + + return retval; +} + +bool KateSyntaxDocument::getElement (QDomElement &element, const QString &mainGroupName, const QString &config) +{ +#ifdef KSD_OVER_VERBOSE + kDebug(13010) << "Looking for \"" << mainGroupName << "\" -> \"" << config << "\"."; +#endif + + QDomNodeList nodes = documentElement().childNodes(); + + // Loop over all these child nodes looking for mainGroupName + for (int i=0; iitem = element; + return data; + } + return 0; +} + +/** + * Get the KateSyntaxContextData of the QDomElement Config inside mainGroupName + * KateSyntaxContextData::parent will contain the QDomElement found + */ +KateSyntaxContextData* KateSyntaxDocument::getGroupInfo(const QString& mainGroupName, const QString &group) +{ + QDomElement element; + if (getElement(element, mainGroupName, group+'s')) + { + KateSyntaxContextData *data = new KateSyntaxContextData; + data->parent = element; + return data; + } + return 0; +} + +/** + * Returns a list with all the keywords inside the list type + */ +QStringList& KateSyntaxDocument::finddata(const QString& mainGroup, const QString& type, bool clearList) +{ +#ifdef KSD_OVER_VERBOSE + kDebug(13010)<<"Create a list of keywords \""< generalConfig.readEntry ("CachedVersion",0)) + { + generalConfig.writeEntry ("CachedVersion", generalConfig.readEntry ("Version",0)); + force = true; + } + + // Let's get a list of all the xml files for hl + const QStringList list = KGlobal::dirs()->findAllResources("data","katepart/syntax/*.xml", + KStandardDirs::NoDuplicates); + + // Let's iterate through the list and build the Mode List + for ( QStringList::ConstIterator it = list.begin(); it != list.end(); ++it ) + { + // Each file has a group called: + QString Group="Cache "+ *it; + + // Let's go to this group + KConfigGroup config(m_config, Group); + + // stat the file + KDE_struct_stat sbuf; + memset (&sbuf, 0, sizeof(sbuf)); + KDE::stat(*it, &sbuf); + + // If the group exist and we're not forced to read the xml file, let's build myModeList for katesyntax..rc + if (!force && config.exists() && (sbuf.st_mtime == config.readEntry("lastModified",0))) + { + // Let's make a new KateSyntaxModeListItem to instert in myModeList from the information in katesyntax..rc + KateSyntaxModeListItem *mli=new KateSyntaxModeListItem; + mli->name = config.readEntry("name"); + mli->nameTranslated = i18nc("Language",mli->name.toUtf8()); + mli->section = i18nc("Language Section",config.readEntry("section").toUtf8()); + mli->mimetype = config.readEntry("mimetype"); + mli->extension = config.readEntry("extension"); + mli->version = config.readEntry("version"); + mli->priority = config.readEntry("priority"); + mli->style = config.readEntry("style"); + mli->author = config.readEntry("author"); + mli->license = config.readEntry("license"); + mli->indenter = config.readEntry("indenter"); + mli->hidden = config.readEntry("hidden", false); + mli->identifier = *it; + + // Apend the item to the list + myModeList.append(mli); + } + else + { +#ifdef KSD_OVER_VERBOSE + kDebug (13010) << "UPDATE hl cache for: " << *it; +#endif + + // We're forced to read the xml files or the mode doesn't exist in the katesyntax...rc + QFile f(*it); + + if (f.open(QIODevice::ReadOnly)) + { + // Ok we opened the file, let's read the contents and close the file + /* the return of setContent should be checked because a false return shows a parsing error */ + QString errMsg; + int line, col; + + bool success = setContent(&f,&errMsg,&line,&col); + + f.close(); + + if (success) + { + QDomElement root = documentElement(); + + if (!root.isNull()) + { + // If the 'first' tag is language, go on + if (root.tagName()=="language") + { + // let's make the mode list item. + KateSyntaxModeListItem *mli = new KateSyntaxModeListItem; + + mli->name = root.attribute("name"); + mli->section = root.attribute("section"); + mli->mimetype = root.attribute("mimetype"); + mli->extension = root.attribute("extensions"); + mli->version = root.attribute("version"); + mli->priority = root.attribute("priority"); + mli->style = root.attribute("style"); + mli->author = root.attribute("author"); + mli->license = root.attribute("license"); + mli->indenter = root.attribute("indenter"); + + QString hidden = root.attribute("hidden"); + mli->hidden = (hidden == "true" || hidden == "TRUE"); + + mli->identifier = *it; + + // Now let's write or overwrite (if force==true) the entry in katesyntax...rc + config = KConfigGroup(m_config, Group); + config.writeEntry("name",mli->name); + config.writeEntry("section",mli->section); + config.writeEntry("mimetype",mli->mimetype); + config.writeEntry("extension",mli->extension); + config.writeEntry("version",mli->version); + config.writeEntry("priority",mli->priority); + config.writeEntry("style",mli->style); + config.writeEntry("author",mli->author); + config.writeEntry("license",mli->license); + config.writeEntry("indenter",mli->indenter); + config.writeEntry("hidden",mli->hidden); + + // modified time to keep cache in sync + config.writeEntry("lastModified", int(sbuf.st_mtime)); + + // Now that the data is in the config file, translate section + mli->section = i18nc("Language Section",mli->section.toUtf8()); + mli->nameTranslated = i18nc("Language",mli->name.toUtf8()); + + // Append the new item to the list. + myModeList.append(mli); + } + } + } + else + { + KateSyntaxModeListItem *emli=new KateSyntaxModeListItem; + + emli->section=i18n("Errors!"); + emli->mimetype="invalid_file/invalid_file"; + emli->extension="invalid_file.invalid_file"; + emli->version="1."; + emli->name=QString ("Error: %1").arg(*it); // internal + emli->nameTranslated=i18n("Error: %1", *it); // translated + emli->identifier=(*it); + + myModeList.append(emli); + } + } + } + } + + // Synchronize with the file katesyntax...rc + generalConfig.sync(); +} + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/syntax/katesyntaxdocument.h b/kate/part/syntax/katesyntaxdocument.h new file mode 100644 index 00000000..1db3a808 --- /dev/null +++ b/kate/part/syntax/katesyntaxdocument.h @@ -0,0 +1,178 @@ +/* This file is part of the KDE libraries + Copyright (C) 2001 Joseph Wenninger + Copyright (C) 2000 Scott Manson + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef __KATE_SYNTAXDOCUMENT_H__ +#define __KATE_SYNTAXDOCUMENT_H__ + +#include +#include +#include + +class KConfig; + +/** + * Information about each syntax hl Mode. This is documented in Kate's + * user guide + * and repeated briefly here. + */ +class KateSyntaxModeListItem +{ + public: + QString name; ///< Name of the mode (eg. Asm6502) + QString nameTranslated; ///< i18n of same, for display purposes + QString section; ///< Submenu section (eg. Assembly) + QString mimetype; ///< Mimetypes this mode applies to + QString extension; ///< Semicolon-separated list of file extensions + QString identifier; + QString version; + QString priority; /**< Priority (mapped to an integer?) for conflict- + resolution when the same file extension has + multiple highlihgting definitions. */ + QString style; ///< Default styles provided by the highlighter + QString author; ///< Author's name + QString license; ///< License; for example: "LGPL" + QString indenter; ///< Indenter to use for this highlighting + bool hidden; ///< Hides the mode from Kate's menus +}; + +/** + * List of the KateSyntaxModeListItems holding all the syntax mode list items + */ +typedef QList KateSyntaxModeList; + +/** + * Class holding the data around the current QDomElement + */ +class KateSyntaxContextData +{ + public: + QDomElement parent; + QDomElement currentGroup; + QDomElement item; +}; + +/** + * Store and manage the information about Syntax Highlighting. + */ +class KateSyntaxDocument : public QDomDocument +{ + public: + /** + * Constructor + * Sets the current file to nothing and build the ModeList (katesyntaxhighlightingrc) + * @param force fore the update of the hl cache + */ + explicit KateSyntaxDocument(KConfig *config, bool force = false); + + /** + * Desctructor + */ + ~KateSyntaxDocument(); + + /** + * If the open hl file is different from the one needed, it opens + * the new one and assign some other things. + * @param identifier file name and path of the new xml needed + * @return success + */ + bool setIdentifier(const QString& identifier); + + /** + * Get the mode list + * @return mode list + */ + const KateSyntaxModeList &modeList() { return myModeList; } + + /** + * Jump to the next group, KateSyntaxContextData::currentGroup will point to the next group + * @param data context + * @return success + */ + bool nextGroup(KateSyntaxContextData* data); + + /** + * Jump to the next item, KateSyntaxContextData::item will point to the next item + * @param data context + * @return success + */ + bool nextItem(KateSyntaxContextData* data); + + /** + * This function is used to fetch the atributes of the tags. + */ + QString groupItemData(const KateSyntaxContextData* data,const QString& name); + QString groupData(const KateSyntaxContextData* data,const QString& name); + + void freeGroupInfo(KateSyntaxContextData* data); + KateSyntaxContextData* getSubItems(KateSyntaxContextData* data); + + /** + * Get the KateSyntaxContextData of the DomElement Config inside mainGroupName + * It just fills KateSyntaxContextData::item + */ + KateSyntaxContextData* getConfig(const QString& mainGroupName, const QString &config); + + /** + * Get the KateSyntaxContextData of the QDomElement Config inside mainGroupName + * KateSyntaxContextData::parent will contain the QDomElement found + */ + KateSyntaxContextData* getGroupInfo(const QString& mainGroupName, const QString &group); + + /** + * Returns a list with all the keywords inside the list type + */ + QStringList& finddata(const QString& mainGroup,const QString& type,bool clearList=true); + + private: + /** + * Generate the list of hl modes, store them in myModeList + * @param force if true forces to rebuild the Mode List from the xml files (instead of katesyntax...rc) + */ + void setupModeList(bool force); + + /** + * Used by getConfig and getGroupInfo to traverse the xml nodes and + * evenually return the found element + */ + bool getElement (QDomElement &element, const QString &mainGroupName, const QString &config); + + /** + * List of mode items + */ + KateSyntaxModeList myModeList; + + /** + * current parsed filename + */ + QString currentFile; + + /** + * last found data out of the xml + */ + QStringList m_data; + + /** + * global config, deleted by hlmanager... + */ + KConfig *m_config; +}; + +#endif + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/syntax/katesyntaxmanager.cpp b/kate/part/syntax/katesyntaxmanager.cpp new file mode 100644 index 00000000..2bde6d32 --- /dev/null +++ b/kate/part/syntax/katesyntaxmanager.cpp @@ -0,0 +1,419 @@ +/* This file is part of the KDE libraries + Copyright (C) 2007 Matthew Woehlke + Copyright (C) 2003, 2004 Anders Lund + Copyright (C) 2003 Hamish Rodda + Copyright (C) 2001,2002 Joseph Wenninger + Copyright (C) 2001 Christoph Cullmann + Copyright (C) 1999 Jochen Wilhelmy + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +//BEGIN INCLUDES +#include "katesyntaxmanager.h" +#include "moc_katesyntaxmanager.cpp" + +#include "katetextline.h" +#include "katedocument.h" +#include "katesyntaxdocument.h" +#include "katerenderer.h" +#include "kateglobal.h" +#include "kateschema.h" +#include "kateconfig.h" +#include "kateextendedattribute.h" +#include "katehighlight.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +//END + +using namespace KTextEditor; + +bool compareKateHighlighting(const KateHighlighting* const left, const KateHighlighting* const right) +{ + int comparison = left->section().compare(right->section(), Qt::CaseInsensitive); + if (comparison == 0) { + comparison = left->nameTranslated().compare(right->nameTranslated(), Qt::CaseInsensitive); + } + return comparison < 0; +} + +//BEGIN KateHlManager +KateHlManager::KateHlManager() + : QObject() + , m_config ("katesyntaxhighlightingrc", KConfig::NoGlobals) + , commonSuffixes (QString(".orig;.new;~;.bak;.BAK").split(';')) + , syntax (new KateSyntaxDocument(&m_config)) + , dynamicCtxsCount(0) + , forceNoDCReset(false) +{ + KateSyntaxModeList modeList = syntax->modeList(); + hlList.reserve(modeList.size() + 1); + hlDict.reserve(modeList.size() + 1); + for (int i=0; i < modeList.count(); i++) + { + KateHighlighting *hl = new KateHighlighting(modeList[i]); + + hlList.insert (qLowerBound(hlList.begin(), hlList.end(), hl, compareKateHighlighting), hl); + hlDict.insert (hl->name(), hl); + } + + // Normal HL + KateHighlighting *hl = new KateHighlighting(0); + hlList.prepend (hl); + hlDict.insert (hl->name(), hl); + + lastCtxsReset.start(); +} + +KateHlManager::~KateHlManager() +{ + delete syntax; + qDeleteAll(hlList); +} + +KateHlManager *KateHlManager::self() +{ + return KateGlobal::self ()->hlManager (); +} + +KateHighlighting *KateHlManager::getHl(int n) +{ + if (n < 0 || n >= hlList.count()) + n = 0; + + return hlList.at(n); +} + +int KateHlManager::nameFind(const QString &name) +{ + for(int i = 0; i < hlList.count(); ++i) { + if (hlList.at(i)->name().compare(name, Qt::CaseInsensitive) == 0) { + return i; + } + } + + return -1; +} + +uint KateHlManager::defaultStyles() +{ + return 14; +} + +QString KateHlManager::defaultStyleName(int n, bool translateNames) +{ + static QStringList names; + static QStringList translatedNames; + + if (names.isEmpty()) + { + names << "Normal"; + names << "Keyword"; + names << "Data Type"; + names << "Decimal/Value"; + names << "Base-N Integer"; + names << "Floating Point"; + names << "Character"; + names << "String"; + names << "Comment"; + names << "Others"; + names << "Alert"; + names << "Function"; + // this next one is for denoting the beginning/end of a user defined folding region + names << "Region Marker"; + // this one is for marking invalid input + names << "Error"; + + translatedNames << i18nc("@item:intable Text context", "Normal"); + translatedNames << i18nc("@item:intable Text context", "Keyword"); + translatedNames << i18nc("@item:intable Text context", "Data Type"); + translatedNames << i18nc("@item:intable Text context", "Decimal/Value"); + translatedNames << i18nc("@item:intable Text context", "Base-N Integer"); + translatedNames << i18nc("@item:intable Text context", "Floating Point"); + translatedNames << i18nc("@item:intable Text context", "Character"); + translatedNames << i18nc("@item:intable Text context", "String"); + translatedNames << i18nc("@item:intable Text context", "Comment"); + translatedNames << i18nc("@item:intable Text context", "Others"); + translatedNames << i18nc("@item:intable Text context", "Alert"); + translatedNames << i18nc("@item:intable Text context", "Function"); + // this next one is for denoting the beginning/end of a user defined folding region + translatedNames << i18nc("@item:intable Text context", "Region Marker"); + // this one is for marking invalid input + translatedNames << i18nc("@item:intable Text context", "Error"); + } + + return translateNames ? translatedNames[n] : names[n]; +} + +void KateHlManager::getDefaults(const QString &schema, KateAttributeList &list, KConfig *cfg) +{ + KColorScheme scheme(QPalette::Active, KColorScheme::View); + KColorScheme schemeSelected(QPalette::Active, KColorScheme::Selection); + + ///NOTE: it's important to append in the order of the HighlightInterface::DefaultStyle + /// enum, to make KateDocument::defaultStyle() work properly. + + { // dsNormal + Attribute::Ptr attrib(new KTextEditor::Attribute()); + attrib->setForeground( scheme.foreground().color() ); + attrib->setSelectedForeground( schemeSelected.foreground().color() ); + list.append(attrib); + } + { // dsKeyword + Attribute::Ptr attrib(new KTextEditor::Attribute()); + attrib->setForeground( scheme.foreground().color() ); + attrib->setSelectedForeground( schemeSelected.foreground().color() ); + attrib->setFontBold(true); + list.append(attrib); + } + { // dsDataType + Attribute::Ptr attrib(new KTextEditor::Attribute()); + attrib->setForeground( scheme.foreground(KColorScheme::LinkText).color() ); + attrib->setSelectedForeground( schemeSelected.foreground(KColorScheme::LinkText).color() ); + list.append(attrib); + } + { // dsDecVal + Attribute::Ptr attrib(new KTextEditor::Attribute()); + attrib->setForeground( scheme.foreground(KColorScheme::NeutralText).color() ); + attrib->setSelectedForeground( schemeSelected.foreground(KColorScheme::NeutralText).color() ); + list.append(attrib); + } + { // dsBaseN + Attribute::Ptr attrib(new KTextEditor::Attribute()); + attrib->setForeground( scheme.foreground(KColorScheme::NeutralText).color() ); + attrib->setSelectedForeground( schemeSelected.foreground(KColorScheme::NeutralText).color() ); + list.append(attrib); + } + { // dsFloat + Attribute::Ptr attrib(new KTextEditor::Attribute()); + attrib->setForeground( scheme.foreground(KColorScheme::NeutralText).color() ); + attrib->setSelectedForeground( schemeSelected.foreground(KColorScheme::NeutralText).color() ); + list.append(attrib); + } + { // dsChar + Attribute::Ptr attrib(new KTextEditor::Attribute()); + attrib->setForeground( scheme.foreground(KColorScheme::ActiveText).color() ); + attrib->setSelectedForeground( schemeSelected.foreground(KColorScheme::ActiveText).color() ); + list.append(attrib); + } + { // dsString + Attribute::Ptr attrib(new KTextEditor::Attribute()); + attrib->setForeground( scheme.foreground(KColorScheme::NegativeText).color() ); + attrib->setSelectedForeground( schemeSelected.foreground(KColorScheme::NegativeText).color() ); + list.append(attrib); + } + { // dsComment + Attribute::Ptr attrib(new KTextEditor::Attribute()); + attrib->setForeground( scheme.foreground(KColorScheme::InactiveText).color() ); + attrib->setSelectedForeground( schemeSelected.foreground(KColorScheme::InactiveText).color() ); + list.append(attrib); + } + { // dsOthers + Attribute::Ptr attrib(new KTextEditor::Attribute()); + attrib->setForeground( scheme.foreground(KColorScheme::PositiveText).color() ); + attrib->setSelectedForeground( schemeSelected.foreground(KColorScheme::PositiveText).color() ); + list.append(attrib); + } + { // dsAlert + Attribute::Ptr attrib(new KTextEditor::Attribute()); + attrib->setForeground( scheme.foreground(KColorScheme::NegativeText).color() ); + attrib->setSelectedForeground( schemeSelected.foreground(KColorScheme::NegativeText).color() ); + attrib->setFontBold(true); + attrib->setBackground( scheme.background(KColorScheme::NegativeBackground).color() ); + list.append(attrib); + } + { // dsFunction + Attribute::Ptr attrib(new KTextEditor::Attribute()); + attrib->setForeground( scheme.foreground(KColorScheme::VisitedText).color() ); + attrib->setSelectedForeground( schemeSelected.foreground(KColorScheme::VisitedText).color() ); + list.append(attrib); + } + { // dsRegionMarker + Attribute::Ptr attrib(new KTextEditor::Attribute()); + attrib->setForeground( scheme.foreground(KColorScheme::LinkText).color() ); + attrib->setSelectedForeground( schemeSelected.foreground(KColorScheme::LinkText).color() ); + attrib->setBackground( scheme.background(KColorScheme::LinkBackground).color() ); + list.append(attrib); + } + { // dsError + Attribute::Ptr attrib(new KTextEditor::Attribute()); + attrib->setForeground( scheme.foreground(KColorScheme::NegativeText) ); + attrib->setSelectedForeground( schemeSelected.foreground(KColorScheme::NegativeText).color() ); + attrib->setFontUnderline(true); + list.append(attrib); + } + + KConfigGroup config(cfg?cfg:KateHlManager::self()->self()->getKConfig(), + "Default Item Styles - Schema " + schema); + + for (uint z = 0; z < defaultStyles(); z++) + { + KTextEditor::Attribute::Ptr i = list.at(z); + QStringList s = config.readEntry(defaultStyleName(z), QStringList()); + if (!s.isEmpty()) + { + while( s.count()<9) + s << ""; + + QString tmp; + QRgb col; + + tmp=s[0]; if (!tmp.isEmpty()) { + col=tmp.toUInt(0,16); i->setForeground(QColor(col)); } + + tmp=s[1]; if (!tmp.isEmpty()) { + col=tmp.toUInt(0,16); i->setSelectedForeground(QColor(col)); } + + tmp=s[2]; if (!tmp.isEmpty()) i->setFontBold(tmp!="0"); + + tmp=s[3]; if (!tmp.isEmpty()) i->setFontItalic(tmp!="0"); + + tmp=s[4]; if (!tmp.isEmpty()) i->setFontStrikeOut(tmp!="0"); + + tmp=s[5]; if (!tmp.isEmpty()) i->setFontUnderline(tmp!="0"); + + tmp=s[6]; if (!tmp.isEmpty()) { + if ( tmp != "-" ) + { + col=tmp.toUInt(0,16); + i->setBackground(QColor(col)); + } + else + i->clearBackground(); + } + tmp=s[7]; if (!tmp.isEmpty()) { + if ( tmp != "-" ) + { + col=tmp.toUInt(0,16); + i->setSelectedBackground(QColor(col)); + } + else + i->clearProperty(KTextEditor::Attribute::SelectedBackground); + } + tmp=s[8]; if (!tmp.isEmpty() && tmp!=QLatin1String("---")) i->setFontFamily(tmp); + } + } +} + +void KateHlManager::setDefaults(const QString &schema, KateAttributeList &list,KConfig *cfg) +{ + cfg=cfg?cfg:KateHlManager::self()->self()->getKConfig(); + KConfigGroup config(cfg, + "Default Item Styles - Schema " + schema); + + for (uint z = 0; z < defaultStyles(); z++) + { + QStringList settings; + KTextEditor::Attribute::Ptr p = list.at(z); + + settings<<(p->hasProperty(QTextFormat::ForegroundBrush)?QString::number(p->foreground().color().rgb(),16):""); + settings<<(p->hasProperty(KTextEditor::Attribute::SelectedForeground)?QString::number(p->selectedForeground().color().rgb(),16):""); + settings<<(p->hasProperty(QTextFormat::FontWeight)?(p->fontBold()?"1":"0"):""); + settings<<(p->hasProperty(QTextFormat::FontItalic)?(p->fontItalic()?"1":"0"):""); + settings<<(p->hasProperty(QTextFormat::FontStrikeOut)?(p->fontStrikeOut()?"1":"0"):""); + settings<<(p->hasProperty(QTextFormat::FontUnderline)?(p->fontUnderline()?"1":"0"):""); + settings<<(p->hasProperty(QTextFormat::BackgroundBrush)?QString::number(p->background().color().rgb(),16):"-"); + settings<<(p->hasProperty(KTextEditor::Attribute::SelectedBackground)?QString::number(p->selectedBackground().color().rgb(),16):"-"); + settings<<(p->hasProperty(QTextFormat::FontFamily)?(p->fontFamily()):QString()); + settings<<"---"; + + config.writeEntry(defaultStyleName(z),settings); + } + + emit changed(); +} + +int KateHlManager::highlights() +{ + return (int) hlList.count(); +} + +QString KateHlManager::hlName(int n) +{ + return hlList.at(n)->name(); +} + +QString KateHlManager::hlNameTranslated(int n) +{ + return hlList.at(n)->nameTranslated(); +} + +QString KateHlManager::hlSection(int n) +{ + return hlList.at(n)->section(); +} + +bool KateHlManager::hlHidden(int n) +{ + return hlList.at(n)->hidden(); +} + +QString KateHlManager::identifierForName(const QString& name) +{ + if (hlDict.contains(name)) + return hlDict[name]->getIdentifier(); + + return QString(); +} + +QString KateHlManager::nameForIdentifier(const QString& identifier) +{ + for ( QHash::iterator it = hlDict.begin(); + it != hlDict.end(); ++it ) + { + if ( (*it)->getIdentifier() == identifier ) { + return it.key(); + } + } + + return QString(); +} + +bool KateHlManager::resetDynamicCtxs() +{ + if (forceNoDCReset) + return false; + + if (lastCtxsReset.elapsed() < KATE_DYNAMIC_CONTEXTS_RESET_DELAY) + return false; + + foreach (KateHighlighting *hl, hlList) + hl->dropDynamicContexts(); + + dynamicCtxsCount = 0; + lastCtxsReset.start(); + + return true; +} +//END + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/syntax/katesyntaxmanager.h b/kate/part/syntax/katesyntaxmanager.h new file mode 100644 index 00000000..52252dbe --- /dev/null +++ b/kate/part/syntax/katesyntaxmanager.h @@ -0,0 +1,115 @@ +/* This file is part of the KDE libraries + Copyright (C) 2001,2002 Joseph Wenninger + Copyright (C) 2001 Christoph Cullmann + Copyright (C) 1999 Jochen Wilhelmy + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef __KATE_SYNTAXMANAGER_H__ +#define __KATE_SYNTAXMANAGER_H__ + +#include "katetextline.h" +#include "kateextendedattribute.h" + +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +class KateSyntaxDocument; +class KateHighlighting; + + +class KateHlManager : public QObject +{ + Q_OBJECT + + public: + KateHlManager(); + ~KateHlManager(); + + static KateHlManager *self(); + + KateSyntaxDocument *syntaxDocument () { return syntax; } + + inline KConfig *getKConfig() { return &m_config; } + + KateHighlighting *getHl(int n); + int nameFind(const QString &name); + + QString identifierForName(const QString&); + /** + * Returns the mode name for a given identifier, as e.g. + * returned by KateHighlighting::hlKeyForAttrib(). + */ + QString nameForIdentifier(const QString&); + + // methodes to get the default style count + names + static uint defaultStyles(); + static QString defaultStyleName(int n, bool translateNames = false); + + void getDefaults(const QString &schema, KateAttributeList &, KConfig *cfg=0); + void setDefaults(const QString &schema, KateAttributeList &, KConfig *cfg=0); + + int highlights(); + QString hlName(int n); + QString hlNameTranslated (int n); + QString hlSection(int n); + bool hlHidden(int n); + + void incDynamicCtxs() { ++dynamicCtxsCount; } + int countDynamicCtxs() { return dynamicCtxsCount; } + void setForceNoDCReset(bool b) { forceNoDCReset = b; } + + // be carefull: all documents hl should be invalidated after having successfully called this method! + bool resetDynamicCtxs(); + + Q_SIGNALS: + void changed(); + + private: + friend class KateHighlighting; + + // This list owns objects it holds, thus they should be deleted when the object is removed + QList hlList; + // This hash does not own the objects it holds, thus they should not be deleted + QHash hlDict; + + KConfig m_config; + QStringList commonSuffixes; + + KateSyntaxDocument *syntax; + + int dynamicCtxsCount; + QTime lastCtxsReset; + bool forceNoDCReset; +}; + +#endif + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/undo/katemodifiedundo.cpp b/kate/part/undo/katemodifiedundo.cpp new file mode 100644 index 00000000..213b5902 --- /dev/null +++ b/kate/part/undo/katemodifiedundo.cpp @@ -0,0 +1,461 @@ +/* This file is part of the Kate project. + * + * Copyright (C) 2011 Dominik Haumann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "katemodifiedundo.h" + +#include "kateundomanager.h" +#include "katedocument.h" + +#include +#include + +KateModifiedInsertText::KateModifiedInsertText (KateDocument *document, int line, int col, const QString &text) + : KateEditInsertTextUndo (document, line, col, text) +{ + setFlag(RedoLine1Modified); + Kate::TextLine tl = document->plainKateTextLine(line); + Q_ASSERT(tl); + if (tl->markedAsModified()) { + setFlag(UndoLine1Modified); + } else { + setFlag(UndoLine1Saved); + } +} + +KateModifiedRemoveText::KateModifiedRemoveText (KateDocument *document, int line, int col, const QString &text) + : KateEditRemoveTextUndo (document, line, col, text) +{ + setFlag(RedoLine1Modified); + Kate::TextLine tl = document->plainKateTextLine(line); + Q_ASSERT(tl); + if (tl->markedAsModified()) { + setFlag(UndoLine1Modified); + } else { + setFlag(UndoLine1Saved); + } +} + +KateModifiedWrapLine::KateModifiedWrapLine (KateDocument *document, int line, int col, int len, bool newLine) + : KateEditWrapLineUndo (document, line, col, len, newLine) +{ + Kate::TextLine tl = document->plainKateTextLine(line); + Q_ASSERT(tl); + if (len > 0 || tl->markedAsModified()) { + setFlag(RedoLine1Modified); + } else if (tl->markedAsSavedOnDisk()) { + setFlag(RedoLine1Saved); + } + + if (col > 0 || len == 0 || tl->markedAsModified()) { + setFlag(RedoLine2Modified); + } else if (tl->markedAsSavedOnDisk()) { + setFlag(RedoLine2Saved); + } + + if (tl->markedAsModified()) { + setFlag(UndoLine1Modified); + } else if ((len > 0 && col > 0) || tl->markedAsSavedOnDisk()) { + setFlag(UndoLine1Saved); + } +} + +KateModifiedUnWrapLine::KateModifiedUnWrapLine (KateDocument *document, int line, int col, int len, bool removeLine) + : KateEditUnWrapLineUndo (document, line, col, len, removeLine) +{ + Kate::TextLine tl = document->plainKateTextLine(line); + Kate::TextLine nextLine = document->plainKateTextLine(line + 1); + Q_ASSERT(tl); + Q_ASSERT(nextLine); + + const int len1 = tl->length(); + const int len2 = nextLine->length(); + + if (len1 > 0 && len2 > 0) { + setFlag(RedoLine1Modified); + + if (tl->markedAsModified()) { + setFlag(UndoLine1Modified); + } else { + setFlag(UndoLine1Saved); + } + + if (nextLine->markedAsModified()) { + setFlag(UndoLine2Modified); + } else { + setFlag(UndoLine2Saved); + } + } else if (len1 == 0) { + if (nextLine->markedAsModified()) { + setFlag(RedoLine1Modified); + } else if (nextLine->markedAsSavedOnDisk()) { + setFlag(RedoLine1Saved); + } + + if (tl->markedAsModified()) { + setFlag(UndoLine1Modified); + } else { + setFlag(UndoLine1Saved); + } + + if (nextLine->markedAsModified()) { + setFlag(UndoLine2Modified); + } else if (nextLine->markedAsSavedOnDisk()) { + setFlag(UndoLine2Saved); + } + } else { // len2 == 0 + if (nextLine->markedAsModified()) { + setFlag(RedoLine1Modified); + } else if (nextLine->markedAsSavedOnDisk()) { + setFlag(RedoLine1Saved); + } + + if (tl->markedAsModified()) { + setFlag(UndoLine1Modified); + } else if (tl->markedAsSavedOnDisk()) { + setFlag(UndoLine1Saved); + } + + if (nextLine->markedAsModified()) { + setFlag(UndoLine2Modified); + } else { + setFlag(UndoLine2Saved); + } + } +} + +KateModifiedInsertLine::KateModifiedInsertLine (KateDocument *document, int line, const QString &text) + : KateEditInsertLineUndo (document, line, text) +{ + setFlag(RedoLine1Modified); +} + +KateModifiedRemoveLine::KateModifiedRemoveLine (KateDocument *document, int line, const QString &text) + : KateEditRemoveLineUndo (document, line, text) +{ + Kate::TextLine tl = document->plainKateTextLine(line); + Q_ASSERT(tl); + if (tl->markedAsModified()) { + setFlag(UndoLine1Modified); + } else { + setFlag(UndoLine1Saved); + } +} + +void KateModifiedInsertText::undo () +{ + KateEditInsertTextUndo::undo(); + + KateDocument *doc = document(); + Kate::TextLine tl = doc->plainKateTextLine(line()); + Q_ASSERT(tl); + + tl->markAsModified(isFlagSet(UndoLine1Modified)); + tl->markAsSavedOnDisk(isFlagSet(UndoLine1Saved)); +} + +void KateModifiedRemoveText::undo () +{ + KateEditRemoveTextUndo::undo(); + + KateDocument *doc = document(); + Kate::TextLine tl = doc->plainKateTextLine(line()); + Q_ASSERT(tl); + + tl->markAsModified(isFlagSet(UndoLine1Modified)); + tl->markAsSavedOnDisk(isFlagSet(UndoLine1Saved)); +} + +void KateModifiedWrapLine::undo () +{ + KateEditWrapLineUndo::undo(); + + KateDocument *doc = document(); + Kate::TextLine tl = doc->plainKateTextLine(line()); + Q_ASSERT(tl); + + tl->markAsModified(isFlagSet(UndoLine1Modified)); + tl->markAsSavedOnDisk(isFlagSet(UndoLine1Saved)); +} + +void KateModifiedUnWrapLine::undo () +{ + KateEditUnWrapLineUndo::undo(); + + KateDocument *doc = document(); + Kate::TextLine tl = doc->plainKateTextLine(line()); + Q_ASSERT(tl); + + tl->markAsModified(isFlagSet(UndoLine1Modified)); + tl->markAsSavedOnDisk(isFlagSet(UndoLine1Saved)); + + Kate::TextLine nextLine = doc->plainKateTextLine(line() + 1); + Q_ASSERT(nextLine); + nextLine->markAsModified(isFlagSet(UndoLine2Modified)); + nextLine->markAsSavedOnDisk(isFlagSet(UndoLine2Saved)); +} + +void KateModifiedInsertLine::undo () +{ + KateEditInsertLineUndo::undo(); + + // no line modification needed, since the line is removed +} + +void KateModifiedRemoveLine::undo () +{ + KateEditRemoveLineUndo::undo(); + + KateDocument *doc = document(); + Kate::TextLine tl = doc->plainKateTextLine(line()); + Q_ASSERT(tl); + + tl->markAsModified(isFlagSet(UndoLine1Modified)); + tl->markAsSavedOnDisk(isFlagSet(UndoLine1Saved)); +} + + +void KateModifiedRemoveText::redo () +{ + KateEditRemoveTextUndo::redo(); + + KateDocument *doc = document(); + Kate::TextLine tl = doc->plainKateTextLine(line()); + Q_ASSERT(tl); + + tl->markAsModified(isFlagSet(RedoLine1Modified)); + tl->markAsSavedOnDisk(isFlagSet(RedoLine1Saved)); +} + +void KateModifiedInsertText::redo () +{ + KateEditInsertTextUndo::redo(); + + KateDocument *doc = document(); + Kate::TextLine tl = doc->plainKateTextLine(line()); + Q_ASSERT(tl); + + tl->markAsModified(isFlagSet(RedoLine1Modified)); + tl->markAsSavedOnDisk(isFlagSet(RedoLine1Saved)); +} + +void KateModifiedUnWrapLine::redo () +{ + KateEditUnWrapLineUndo::redo(); + + KateDocument *doc = document(); + Kate::TextLine tl = doc->plainKateTextLine(line()); + Q_ASSERT(tl); + + tl->markAsModified(isFlagSet(RedoLine1Modified)); + tl->markAsSavedOnDisk(isFlagSet(RedoLine1Saved)); +} + +void KateModifiedWrapLine::redo () +{ + KateEditWrapLineUndo::redo (); + + KateDocument *doc = document(); + Kate::TextLine tl = doc->plainKateTextLine(line()); + Q_ASSERT(tl); + + tl->markAsModified(isFlagSet(RedoLine1Modified)); + tl->markAsSavedOnDisk(isFlagSet(RedoLine1Saved)); + + Kate::TextLine nextLine = doc->plainKateTextLine(line() + 1); + Q_ASSERT(nextLine); + + nextLine->markAsModified(isFlagSet(RedoLine2Modified)); + nextLine->markAsSavedOnDisk(isFlagSet(RedoLine2Saved)); +} + +void KateModifiedRemoveLine::redo () +{ + KateEditRemoveLineUndo::redo(); + + // no line modification needed, since the line is removed +} + +void KateModifiedInsertLine::redo () +{ + KateEditInsertLineUndo::redo(); + + KateDocument *doc = document(); + Kate::TextLine tl = doc->plainKateTextLine(line()); + Q_ASSERT(tl); + + tl->markAsModified(isFlagSet(RedoLine1Modified)); + tl->markAsSavedOnDisk(isFlagSet(RedoLine1Saved)); +} + +void KateModifiedInsertText::updateRedoSavedOnDiskFlag(QBitArray & lines) +{ + if (line() >= lines.size()) { + lines.resize(line() + 1); + } + + if (!lines.testBit(line())) { + lines.setBit(line()); + + unsetFlag(RedoLine1Modified); + setFlag(RedoLine1Saved); + } +} + +void KateModifiedInsertText::updateUndoSavedOnDiskFlag(QBitArray & lines) +{ + if (line() >= lines.size()) { + lines.resize(line() + 1); + } + + if (!lines.testBit(line())) { + lines.setBit(line()); + + unsetFlag(UndoLine1Modified); + setFlag(UndoLine1Saved); + } +} + +void KateModifiedRemoveText::updateRedoSavedOnDiskFlag(QBitArray & lines) +{ + if (line() >= lines.size()) { + lines.resize(line() + 1); + } + + if (!lines.testBit(line())) { + lines.setBit(line()); + + unsetFlag(RedoLine1Modified); + setFlag(RedoLine1Saved); + } +} + +void KateModifiedRemoveText::updateUndoSavedOnDiskFlag(QBitArray & lines) +{ + if (line() >= lines.size()) { + lines.resize(line() + 1); + } + + if (!lines.testBit(line())) { + lines.setBit(line()); + + unsetFlag(UndoLine1Modified); + setFlag(UndoLine1Saved); + } +} + +void KateModifiedWrapLine::updateRedoSavedOnDiskFlag(QBitArray & lines) +{ + if (line() + 1 >= lines.size()) { + lines.resize(line() + 2); + } + + if (isFlagSet(RedoLine1Modified) && !lines.testBit(line())) { + lines.setBit(line()); + + unsetFlag(RedoLine1Modified); + setFlag(RedoLine1Saved); + } + + if (isFlagSet(RedoLine2Modified) && !lines.testBit(line() + 1)) { + lines.setBit(line() + 1); + + unsetFlag(RedoLine2Modified); + setFlag(RedoLine2Saved); + } +} + +void KateModifiedWrapLine::updateUndoSavedOnDiskFlag(QBitArray & lines) +{ + if (line() >= lines.size()) { + lines.resize(line() + 1); + } + + if (isFlagSet(UndoLine1Modified) && !lines.testBit(line())) { + lines.setBit(line()); + + unsetFlag(UndoLine1Modified); + setFlag(UndoLine1Saved); + } +} + +void KateModifiedUnWrapLine::updateRedoSavedOnDiskFlag(QBitArray & lines) +{ + if (line() >= lines.size()) { + lines.resize(line() + 1); + } + + if (isFlagSet(RedoLine1Modified) && !lines.testBit(line())) { + lines.setBit(line()); + + unsetFlag(RedoLine1Modified); + setFlag(RedoLine1Saved); + } +} + +void KateModifiedUnWrapLine::updateUndoSavedOnDiskFlag(QBitArray & lines) +{ + if (line() + 1 >= lines.size()) { + lines.resize(line() + 2); + } + + if (isFlagSet(UndoLine1Modified) && !lines.testBit(line())) { + lines.setBit(line()); + + unsetFlag(UndoLine1Modified); + setFlag(UndoLine1Saved); + } + + if (isFlagSet(UndoLine2Modified) && !lines.testBit(line() + 1)) { + lines.setBit(line() + 1); + + unsetFlag(UndoLine2Modified); + setFlag(UndoLine2Saved); + } +} + +void KateModifiedInsertLine::updateRedoSavedOnDiskFlag(QBitArray & lines) +{ + if (line() >= lines.size()) { + lines.resize(line() + 1); + } + + if (!lines.testBit(line())) { + lines.setBit(line()); + + unsetFlag(RedoLine1Modified); + setFlag(RedoLine1Saved); + } +} + +void KateModifiedRemoveLine::updateUndoSavedOnDiskFlag(QBitArray & lines) +{ + if (line() >= lines.size()) { + lines.resize(line() + 1); + } + + if (!lines.testBit(line())) { + lines.setBit(line()); + + unsetFlag(UndoLine1Modified); + setFlag(UndoLine1Saved); + } +} + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/undo/katemodifiedundo.h b/kate/part/undo/katemodifiedundo.h new file mode 100644 index 00000000..4e47c81c --- /dev/null +++ b/kate/part/undo/katemodifiedundo.h @@ -0,0 +1,140 @@ +/* This file is part of the Kate project. + * + * Copyright (C) 2011 Dominik Haumann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef KATE_MODIFIED_UNDO_H +#define KATE_MODIFIED_UNDO_H + +#include "kateundo.h" + +class KateModifiedInsertText : public KateEditInsertTextUndo +{ + public: + KateModifiedInsertText (KateDocument *document, int line, int col, const QString &text); + + /** + * @copydoc KateUndo::undo() + */ + void undo(); + + /** + * @copydoc KateUndo::redo() + */ + void redo(); + + void updateUndoSavedOnDiskFlag(QBitArray & lines); + void updateRedoSavedOnDiskFlag(QBitArray & lines); +}; + +class KateModifiedRemoveText : public KateEditRemoveTextUndo +{ + public: + KateModifiedRemoveText (KateDocument *document, int line, int col, const QString &text); + + /** + * @copydoc KateUndo::undo() + */ + void undo(); + + /** + * @copydoc KateUndo::redo() + */ + void redo(); + + void updateUndoSavedOnDiskFlag(QBitArray & lines); + void updateRedoSavedOnDiskFlag(QBitArray & lines); +}; + +class KateModifiedWrapLine : public KateEditWrapLineUndo +{ + public: + KateModifiedWrapLine (KateDocument *document, int line, int col, int len, bool newLine); + + /** + * @copydoc KateUndo::undo() + */ + void undo(); + + /** + * @copydoc KateUndo::redo() + */ + void redo(); + + void updateUndoSavedOnDiskFlag(QBitArray & lines); + void updateRedoSavedOnDiskFlag(QBitArray & lines); +}; + +class KateModifiedUnWrapLine : public KateEditUnWrapLineUndo +{ + public: + KateModifiedUnWrapLine (KateDocument *document, int line, int col, int len, bool removeLine); + + /** + * @copydoc KateUndo::undo() + */ + void undo(); + + /** + * @copydoc KateUndo::redo() + */ + void redo(); + + void updateUndoSavedOnDiskFlag(QBitArray & lines); + void updateRedoSavedOnDiskFlag(QBitArray & lines); +}; + +class KateModifiedInsertLine : public KateEditInsertLineUndo +{ + public: + KateModifiedInsertLine (KateDocument *document, int line, const QString &text); + + /** + * @copydoc KateUndo::undo() + */ + void undo(); + + /** + * @copydoc KateUndo::redo() + */ + void redo(); + + void updateRedoSavedOnDiskFlag(QBitArray & lines); +}; + +class KateModifiedRemoveLine : public KateEditRemoveLineUndo +{ + public: + KateModifiedRemoveLine (KateDocument *document, int line, const QString &text); + + /** + * @copydoc KateUndo::undo() + */ + void undo(); + + /** + * @copydoc KateUndo::redo() + */ + void redo(); + + void updateUndoSavedOnDiskFlag(QBitArray & lines); +}; + +#endif // KATE_MODIFIED_UNDO_H + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/undo/kateundo.cpp b/kate/part/undo/kateundo.cpp new file mode 100644 index 00000000..a7f57e8f --- /dev/null +++ b/kate/part/undo/kateundo.cpp @@ -0,0 +1,416 @@ +/* This file is part of the KDE libraries + Copyright (C) 2011 Dominik Haumann + Copyright (C) 2009-2010 Bernhard Beschow + Copyright (C) 2002 John Firebaugh + Copyright (C) 2001 Christoph Cullmann + Copyright (C) 2001 Joseph Wenninger + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "kateundo.h" + +#include "kateundomanager.h" +#include "katedocument.h" + +#include +#include + +KateUndo::KateUndo (KateDocument *document) +: m_document (document) +, m_lineModFlags(0x00) +{ +} + +KateUndo::~KateUndo () +{ +} + +KateEditInsertTextUndo::KateEditInsertTextUndo (KateDocument *document, int line, int col, const QString &text) + : KateUndo (document) + , m_line (line) + , m_col (col) + , m_text (text) +{ +} + +KateEditRemoveTextUndo::KateEditRemoveTextUndo (KateDocument *document, int line, int col, const QString &text) + : KateUndo (document) + , m_line (line) + , m_col (col) + , m_text (text) +{ +} + +KateEditWrapLineUndo::KateEditWrapLineUndo (KateDocument *document, int line, int col, int len, bool newLine) + : KateUndo (document) + , m_line (line) + , m_col (col) + , m_len (len) + , m_newLine (newLine) +{ +} + +KateEditUnWrapLineUndo::KateEditUnWrapLineUndo (KateDocument *document, int line, int col, int len, bool removeLine) + : KateUndo (document) + , m_line (line) + , m_col (col) + , m_len (len) + , m_removeLine (removeLine) +{ +} + +KateEditInsertLineUndo::KateEditInsertLineUndo (KateDocument *document, int line, const QString &text) + : KateUndo (document) + , m_line (line) + , m_text (text) +{ +} + +KateEditRemoveLineUndo::KateEditRemoveLineUndo (KateDocument *document, int line, const QString &text) + : KateUndo (document) + , m_line (line) + , m_text (text) +{ +} + + +bool KateUndo::isEmpty() const +{ + return false; +} + +bool KateEditInsertTextUndo::isEmpty() const +{ + return len() == 0; +} + +bool KateEditRemoveTextUndo::isEmpty() const +{ + return len() == 0; +} + +bool KateUndo::mergeWith (const KateUndo* /*undo*/) +{ + return false; +} + +bool KateEditInsertTextUndo::mergeWith (const KateUndo* undo) +{ + const KateEditInsertTextUndo *u = dynamic_cast (undo); + if (u != 0 + && m_line == u->m_line + && (m_col + len()) == u->m_col) + { + m_text += u->m_text; + return true; + } + + return false; +} + +bool KateEditRemoveTextUndo::mergeWith (const KateUndo* undo) +{ + const KateEditRemoveTextUndo *u = dynamic_cast (undo); + + if (u != 0 + && m_line == u->m_line + && m_col == (u->m_col + u->len())) + { + m_text.prepend(u->m_text); + m_col = u->m_col; + return true; + } + + return false; +} + +void KateEditInsertTextUndo::undo () +{ + KateDocument *doc = document(); + + doc->editRemoveText (m_line, m_col, len()); +} + +void KateEditRemoveTextUndo::undo () +{ + KateDocument *doc = document(); + + doc->editInsertText (m_line, m_col, m_text); +} + +void KateEditWrapLineUndo::undo () +{ + KateDocument *doc = document(); + + doc->editUnWrapLine (m_line, m_newLine, m_len); +} + +void KateEditUnWrapLineUndo::undo () +{ + KateDocument *doc = document(); + + doc->editWrapLine (m_line, m_col, m_removeLine); +} + +void KateEditInsertLineUndo::undo () +{ + KateDocument *doc = document(); + + doc->editRemoveLine (m_line); +} + +void KateEditRemoveLineUndo::undo () +{ + KateDocument *doc = document(); + + doc->editInsertLine (m_line, m_text); +} + +void KateEditMarkLineAutoWrappedUndo::undo () +{ + KateDocument *doc = document(); + + doc->editMarkLineAutoWrapped (m_line, m_autowrapped); +} + +void KateEditRemoveTextUndo::redo () +{ + KateDocument *doc = document(); + + doc->editRemoveText (m_line, m_col, len()); +} + +void KateEditInsertTextUndo::redo () +{ + KateDocument *doc = document(); + + doc->editInsertText (m_line, m_col, m_text); +} + +void KateEditUnWrapLineUndo::redo () +{ + KateDocument *doc = document(); + + doc->editUnWrapLine (m_line, m_removeLine, m_len); +} + +void KateEditWrapLineUndo::redo () +{ + KateDocument *doc = document(); + + doc->editWrapLine (m_line, m_col, m_newLine); +} + +void KateEditRemoveLineUndo::redo () +{ + KateDocument *doc = document(); + + doc->editRemoveLine (m_line); +} + +void KateEditInsertLineUndo::redo () +{ + KateDocument *doc = document(); + + doc->editInsertLine (m_line, m_text); +} + +void KateEditMarkLineAutoWrappedUndo::redo () +{ + KateDocument *doc = document(); + + doc->editMarkLineAutoWrapped (m_line, m_autowrapped); +} + +KateUndoGroup::KateUndoGroup (KateUndoManager *manager, const KTextEditor::Cursor &cursorPosition, const KTextEditor::Range &selectionRange) + : m_manager (manager) + , m_safePoint(false) + , m_undoSelection(selectionRange) + , m_redoSelection(-1, -1, -1, -1) + , m_undoCursor(cursorPosition) + , m_redoCursor(-1, -1) +{ +} + +KateUndoGroup::~KateUndoGroup () +{ + qDeleteAll (m_items); +} + +void KateUndoGroup::undo (KTextEditor::View *view) +{ + if (m_items.isEmpty()) + return; + + m_manager->startUndo (); + + for (int i=m_items.size()-1; i >= 0; --i) + m_items[i]->undo(); + + if (view != 0) { + if (m_undoSelection.isValid()) + view->setSelection(m_undoSelection); + else + view->removeSelection(); + + if (m_undoCursor.isValid()) + view->setCursorPosition(m_undoCursor); + } + + m_manager->endUndo (); +} + +void KateUndoGroup::redo (KTextEditor::View *view) +{ + if (m_items.isEmpty()) + return; + + m_manager->startUndo (); + + for (int i=0; i < m_items.size(); ++i) + m_items[i]->redo(); + + if (view != 0) { + if (m_redoSelection.isValid()) + view->setSelection(m_redoSelection); + else + view->removeSelection(); + + if (m_redoCursor.isValid()) + view->setCursorPosition(m_redoCursor); + } + + m_manager->endUndo (); +} + +void KateUndoGroup::editEnd(const KTextEditor::Cursor &cursorPosition, const KTextEditor::Range selectionRange) +{ + m_redoCursor = cursorPosition; + m_redoSelection = selectionRange; +} + +void KateUndoGroup::addItem(KateUndo* u) +{ + if (u->isEmpty()) + delete u; + else if (!m_items.isEmpty() && m_items.last()->mergeWith(u)) + delete u; + else + m_items.append(u); +} + +bool KateUndoGroup::merge (KateUndoGroup* newGroup,bool complex) +{ + if (m_safePoint) + return false; + + if (newGroup->isOnlyType(singleType()) || complex) { + // Take all of its items first -> last + KateUndo* u = newGroup->m_items.isEmpty() ? 0 : newGroup->m_items.takeFirst (); + while (u) { + addItem(u); + u = newGroup->m_items.isEmpty() ? 0 : newGroup->m_items.takeFirst (); + } + + if (newGroup->m_safePoint) + safePoint(); + + m_redoCursor = newGroup->m_redoCursor; + m_redoSelection = newGroup->m_redoSelection; + + return true; + } + + return false; +} + +void KateUndoGroup::safePoint (bool safePoint) +{ + m_safePoint=safePoint; +} + +void KateUndoGroup::flagSavedAsModified() +{ + foreach (KateUndo *item, m_items) { + if (item->isFlagSet(KateUndo::UndoLine1Saved)) { + item->unsetFlag(KateUndo::UndoLine1Saved); + item->setFlag(KateUndo::UndoLine1Modified); + } + + if (item->isFlagSet(KateUndo::UndoLine2Saved)) { + item->unsetFlag(KateUndo::UndoLine2Saved); + item->setFlag(KateUndo::UndoLine2Modified); + } + + if (item->isFlagSet(KateUndo::RedoLine1Saved)) { + item->unsetFlag(KateUndo::RedoLine1Saved); + item->setFlag(KateUndo::RedoLine1Modified); + } + + if (item->isFlagSet(KateUndo::RedoLine2Saved)) { + item->unsetFlag(KateUndo::RedoLine2Saved); + item->setFlag(KateUndo::RedoLine2Modified); + } + } +} + +void KateUndoGroup::markUndoAsSaved(QBitArray & lines) +{ + for (int i = m_items.size() - 1; i >= 0; --i) { + KateUndo* item = m_items[i]; + item->updateUndoSavedOnDiskFlag(lines); + } +} + +void KateUndoGroup::markRedoAsSaved(QBitArray & lines) +{ + for (int i = m_items.size() - 1; i >= 0; --i) { + KateUndo* item = m_items[i]; + item->updateRedoSavedOnDiskFlag(lines); + } +} + +KTextEditor::Document *KateUndoGroup::document() +{ + return m_manager->document(); +} + +KateUndo::UndoType KateUndoGroup::singleType() const +{ + KateUndo::UndoType ret = KateUndo::editInvalid; + + Q_FOREACH(const KateUndo *item, m_items) { + if (ret == KateUndo::editInvalid) + ret = item->type(); + else if (ret != item->type()) + return KateUndo::editInvalid; + } + + return ret; +} + +bool KateUndoGroup::isOnlyType(KateUndo::UndoType type) const +{ + if (type == KateUndo::editInvalid) return false; + + Q_FOREACH(const KateUndo *item, m_items) + if (item->type() != type) + return false; + + return true; +} + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/undo/kateundo.h b/kate/part/undo/kateundo.h new file mode 100644 index 00000000..a2574573 --- /dev/null +++ b/kate/part/undo/kateundo.h @@ -0,0 +1,505 @@ +/* This file is part of the KDE libraries + Copyright (C) 2011 Dominik Haumann + Copyright (C) 2009-2010 Bernhard Beschow + Copyright (C) 2002 John Firebaugh + Copyright (C) 2001 Christoph Cullmann + Copyright (C) 2001 Joseph Wenninger + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KATE_UNDO_H +#define KATE_UNDO_H + +#include + +#include +#include + +class KateUndoManager; +class KateDocument; + +namespace KTextEditor { + class View; +} + +/** + * Base class for Kate undo commands. + */ +class KateUndo +{ + public: + /** + * Constructor + * @param document the document the undo item belongs to + */ + KateUndo (KateDocument *document); + + /** + * Destructor + */ + virtual ~KateUndo(); + + public: + /** + * Types for undo items + */ + enum UndoType + { + editInsertText, + editRemoveText, + editWrapLine, + editUnWrapLine, + editInsertLine, + editRemoveLine, + editMarkLineAutoWrapped, + editInvalid + }; + + public: + /** + * Check whether the item is empty. + * + * @return whether the item is empty + */ + virtual bool isEmpty() const; + + /** + * merge an undo item + * Saves a bit of memory and potentially many calls when undo/redoing. + * @param undo undo item to merge + * @return success + */ + virtual bool mergeWith(const KateUndo* undo); + + /** + * undo this item + */ + virtual void undo() = 0; + + /** + * redo this item + */ + virtual void redo() = 0; + + /** + * type of item + * @return type + */ + virtual KateUndo::UndoType type() const = 0; + + protected: + /** + * Return the document the undo item belongs to. + * @return the document the undo item belongs to + */ + inline KateDocument *document() { return m_document; } + + private: + /** + * the document the undo item belongs to + */ + KateDocument *m_document; + + // + // Line modification system + // + public: + enum ModificationFlag { + UndoLine1Modified = 1, + UndoLine2Modified = 2, + UndoLine1Saved = 4, + UndoLine2Saved = 8, + RedoLine1Modified = 16, + RedoLine2Modified = 32, + RedoLine1Saved = 64, + RedoLine2Saved = 128 + }; + + inline void setFlag(ModificationFlag flag) { + m_lineModFlags |= flag; + } + + inline void unsetFlag(ModificationFlag flag) { + m_lineModFlags &= (~flag); + } + + inline bool isFlagSet(ModificationFlag flag) const { + return m_lineModFlags & flag; + } + + virtual void updateUndoSavedOnDiskFlag(QBitArray & lines) { Q_UNUSED(lines) } + virtual void updateRedoSavedOnDiskFlag(QBitArray & lines) { Q_UNUSED(lines) } + + private: + uchar m_lineModFlags; +}; + +class KateEditInsertTextUndo : public KateUndo +{ + public: + KateEditInsertTextUndo (KateDocument *document, int line, int col, const QString &text); + + /** + * @copydoc KateUndo::isEmpty() + */ + bool isEmpty() const; + + /** + * @copydoc KateUndo::undo() + */ + void undo(); + + /** + * @copydoc KateUndo::redo() + */ + void redo(); + + /** + * @copydoc KateUndo::mergeWith(const KateUndo) + */ + bool mergeWith (const KateUndo *undo); + + /** + * @copydoc KateUndo::type() + */ + KateUndo::UndoType type() const { return KateUndo::editInsertText; } + + protected: + inline int len() const { return m_text.length(); } + inline int line() const { return m_line; } + + private: + const int m_line; + const int m_col; + QString m_text; +}; + +class KateEditRemoveTextUndo : public KateUndo +{ + public: + KateEditRemoveTextUndo (KateDocument *document, int line, int col, const QString &text); + + /** + * @copydoc KateUndo::isEmpty() + */ + bool isEmpty() const; + + /** + * @copydoc KateUndo::undo() + */ + void undo(); + + /** + * @copydoc KateUndo::redo() + */ + void redo(); + + /** + * @copydoc KateUndo::mergeWith(const KateUndo) + */ + bool mergeWith (const KateUndo *undo); + + /** + * @copydoc KateUndo::type() + */ + KateUndo::UndoType type() const { return KateUndo::editRemoveText; } + + protected: + inline int len() const { return m_text.length(); } + inline int line() const { return m_line; } + + private: + const int m_line; + int m_col; + QString m_text; +}; + +class KateEditMarkLineAutoWrappedUndo : public KateUndo +{ + public: + KateEditMarkLineAutoWrappedUndo (KateDocument *document, int line, bool autowrapped) + : KateUndo (document) + , m_line (line) + , m_autowrapped (autowrapped) + {} + + /** + * @copydoc KateUndo::undo() + */ + void undo(); + + /** + * @copydoc KateUndo::redo() + */ + void redo(); + + /** + * @copydoc KateUndo::type() + */ + KateUndo::UndoType type() const { return KateUndo::editMarkLineAutoWrapped; } + + private: + const int m_line; + const bool m_autowrapped; +}; + +class KateEditWrapLineUndo : public KateUndo +{ + public: + KateEditWrapLineUndo (KateDocument *document, int line, int col, int len, bool newLine); + + /** + * @copydoc KateUndo::undo() + */ + void undo(); + + /** + * @copydoc KateUndo::redo() + */ + void redo(); + + /** + * @copydoc KateUndo::type() + */ + KateUndo::UndoType type() const { return KateUndo::editWrapLine; } + + protected: + inline int line() const { return m_line; } + + private: + const int m_line; + const int m_col; + const int m_len; + const bool m_newLine; +}; + +class KateEditUnWrapLineUndo : public KateUndo +{ + public: + KateEditUnWrapLineUndo (KateDocument *document, int line, int col, int len, bool removeLine); + + /** + * @copydoc KateUndo::undo() + */ + void undo(); + + /** + * @copydoc KateUndo::redo() + */ + void redo(); + + /** + * @copydoc KateUndo::type() + */ + KateUndo::UndoType type() const { return KateUndo::editUnWrapLine; } + + protected: + inline int line() const { return m_line; } + + private: + const int m_line; + const int m_col; + const int m_len; + const bool m_removeLine; +}; + +class KateEditInsertLineUndo : public KateUndo +{ + public: + KateEditInsertLineUndo (KateDocument *document, int line, const QString &text); + + /** + * @copydoc KateUndo::undo() + */ + void undo(); + + /** + * @copydoc KateUndo::redo() + */ + void redo(); + + /** + * @copydoc KateUndo::type() + */ + KateUndo::UndoType type() const { return KateUndo::editInsertLine; } + + protected: + inline int line() const { return m_line; } + + private: + const int m_line; + const QString m_text; +}; + +class KateEditRemoveLineUndo : public KateUndo +{ + public: + KateEditRemoveLineUndo (KateDocument *document, int line, const QString &text); + + /** + * @copydoc KateUndo::undo() + */ + void undo(); + + /** + * @copydoc KateUndo::redo() + */ + void redo(); + + /** + * @copydoc KateUndo::type() + */ + KateUndo::UndoType type() const { return KateUndo::editRemoveLine; } + + protected: + inline int line() const { return m_line; } + + private: + const int m_line; + const QString m_text; +}; + +/** + * Class to manage a group of undo items + */ +class KateUndoGroup +{ + public: + /** + * Constructor + * @param manager KateUndoManager this undo group will belong to + */ + explicit KateUndoGroup (KateUndoManager *manager, const KTextEditor::Cursor &cursorPosition, const KTextEditor::Range &selectionRange); + + /** + * Destructor + */ + ~KateUndoGroup(); + + public: + /** + * Undo the contained undo items + */ + void undo(KTextEditor::View *view); + + /** + * Redo the contained undo items + */ + void redo(KTextEditor::View *view); + + void editEnd(const KTextEditor::Cursor &cursorPosition, const KTextEditor::Range selectionRange); + + /** + * merge this group with an other + * @param newGroup group to merge into this one + * @param complex set if a complex undo + * @return success + */ + bool merge (KateUndoGroup* newGroup,bool complex); + + /** + * set group as as savepoint. the next group will not merge with this one + */ + void safePoint (bool safePoint=true); + + /** + * is this undogroup empty? + */ + bool isEmpty() const { return m_items.isEmpty(); } + + /** + * Change all LineSaved flags to LineModified of the line modification system. + */ + void flagSavedAsModified(); + + void markUndoAsSaved(QBitArray & lines); + void markRedoAsSaved(QBitArray & lines); + + /** + * Set the undo cursor to @p cursor. + */ + inline void setUndoCursor(const KTextEditor::Cursor & cursor) + { m_undoCursor = cursor; } + + /** + * Set the redo cursor to @p cursor. + */ + inline void setRedoCursor(const KTextEditor::Cursor & cursor) + { m_redoCursor = cursor; } + + inline const KTextEditor::Cursor & redoCursor() const + { return m_redoCursor; } + + private: + KTextEditor::Document *document(); + + /** + * singleType + * @return the type if it's only one type, or editInvalid if it contains multiple types. + */ + KateUndo::UndoType singleType() const; + + /** + * are we only of this type ? + * @param type type to query + * @return we contain only the given type + */ + bool isOnlyType(KateUndo::UndoType type) const; + + public: + /** + * add an undo item + * @param u item to add + */ + void addItem (KateUndo *u); + + private: + KateUndoManager *const m_manager; + + /** + * list of items contained + */ + QList m_items; + + /** + * prohibit merging with the next group + */ + bool m_safePoint; + + /** + * the text selection of the active view before the edit step + */ + const KTextEditor::Range m_undoSelection; + + /** + * the text selection of the active view after the edit step + */ + KTextEditor::Range m_redoSelection; + + /** + * the cursor position of the active view before the edit step + */ + KTextEditor::Cursor m_undoCursor; + + /** + * the cursor position of the active view after the edit step + */ + KTextEditor::Cursor m_redoCursor; +}; + +#endif + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/undo/kateundomanager.cpp b/kate/part/undo/kateundomanager.cpp new file mode 100644 index 00000000..bc4c1fc2 --- /dev/null +++ b/kate/part/undo/kateundomanager.cpp @@ -0,0 +1,439 @@ +/* This file is part of the KDE libraries + Copyright (C) 2009-2010 Bernhard Beschow + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +#include "kateundomanager.h" + +#include + +#include "katedocument.h" +#include "katemodifiedundo.h" + +#include + +KateUndoManager::KateUndoManager (KateDocument *doc) + : QObject (doc) + , m_document (doc) + , m_undoComplexMerge (false) + , m_isActive (true) + , m_editCurrentUndo (0) + , lastUndoGroupWhenSaved(0) + , lastRedoGroupWhenSaved(0) + , docWasSavedWhenUndoWasEmpty(true) + , docWasSavedWhenRedoWasEmpty(true) +{ + connect(this, SIGNAL(undoEnd(KTextEditor::Document*)), this, SIGNAL(undoChanged())); + connect(this, SIGNAL(redoEnd(KTextEditor::Document*)), this, SIGNAL(undoChanged())); + + connect(doc, SIGNAL(viewCreated(KTextEditor::Document*,KTextEditor::View*)), SLOT(viewCreated(KTextEditor::Document*,KTextEditor::View*))); +} + +KateUndoManager::~KateUndoManager() +{ + delete m_editCurrentUndo; + + // cleanup the undo/redo items, very important, truee :/ + qDeleteAll(undoItems); + undoItems.clear(); + qDeleteAll(redoItems); + redoItems.clear(); +} + +KTextEditor::Document *KateUndoManager::document() +{ + return m_document; +} + +void KateUndoManager::viewCreated (KTextEditor::Document *, KTextEditor::View *newView) +{ + connect(newView, SIGNAL(cursorPositionChanged(KTextEditor::View*,KTextEditor::Cursor)), SLOT(undoCancel())); +} + +void KateUndoManager::editStart() +{ + if (!m_isActive) + return; + + // editStart() and editEnd() must be called in alternating fashion + Q_ASSERT(m_editCurrentUndo == 0); // make sure to enter a clean state + + const KTextEditor::Cursor cursorPosition = activeView() ? activeView()->cursorPosition() : KTextEditor::Cursor::invalid(); + const KTextEditor::Range selectionRange = activeView() ? activeView()->selectionRange() : KTextEditor::Range::invalid(); + + // new current undo item + m_editCurrentUndo = new KateUndoGroup(this, cursorPosition, selectionRange); + + Q_ASSERT(m_editCurrentUndo != 0); // a new undo group must be created by this method +} + +void KateUndoManager::editEnd() +{ + if (!m_isActive) + return; + + // editStart() and editEnd() must be called in alternating fashion + Q_ASSERT(m_editCurrentUndo != 0); // an undo group must have been created by editStart() + + const KTextEditor::Cursor cursorPosition = activeView() ? activeView()->cursorPosition() : KTextEditor::Cursor::invalid(); + const KTextEditor::Range selectionRange = activeView() ? activeView()->selectionRange() : KTextEditor::Range::invalid(); + + m_editCurrentUndo->editEnd(cursorPosition, selectionRange); + + bool changedUndo = false; + + if (m_editCurrentUndo->isEmpty()) { + delete m_editCurrentUndo; + } else if (!undoItems.isEmpty() + && undoItems.last()->merge(m_editCurrentUndo, m_undoComplexMerge)) { + delete m_editCurrentUndo; + } else { + undoItems.append(m_editCurrentUndo); + changedUndo = true; + } + + m_editCurrentUndo = 0L; + + if (changedUndo) + emit undoChanged(); + + Q_ASSERT(m_editCurrentUndo == 0); // must be 0 after calling this method +} + +void KateUndoManager::inputMethodStart() +{ + setActive(false); + m_document->editStart(); +} + +void KateUndoManager::inputMethodEnd() +{ + m_document->editEnd(); + setActive(true); +} + +void KateUndoManager::startUndo() +{ + setActive(false); + m_document->editStart(); +} + +void KateUndoManager::endUndo() +{ + m_document->editEnd(); + setActive(true); +} + +void KateUndoManager::slotTextInserted(int line, int col, const QString &s) +{ + if (m_editCurrentUndo != 0) // do we care about notifications? + addUndoItem(new KateModifiedInsertText(m_document, line, col, s)); +} + +void KateUndoManager::slotTextRemoved(int line, int col, const QString &s) +{ + if (m_editCurrentUndo != 0) // do we care about notifications? + addUndoItem(new KateModifiedRemoveText(m_document, line, col, s)); +} + +void KateUndoManager::slotMarkLineAutoWrapped(int line, bool autowrapped) +{ + if (m_editCurrentUndo != 0) // do we care about notifications? + addUndoItem(new KateEditMarkLineAutoWrappedUndo(m_document, line, autowrapped)); +} + +void KateUndoManager::slotLineWrapped(int line, int col, int length, bool newLine) +{ + if (m_editCurrentUndo != 0) // do we care about notifications? + addUndoItem(new KateModifiedWrapLine(m_document, line, col, length, newLine)); +} + +void KateUndoManager::slotLineUnWrapped(int line, int col, int length, bool lineRemoved) +{ + if (m_editCurrentUndo != 0) // do we care about notifications? + addUndoItem(new KateModifiedUnWrapLine(m_document, line, col, length, lineRemoved)); +} + +void KateUndoManager::slotLineInserted(int line, const QString &s) +{ + if (m_editCurrentUndo != 0) // do we care about notifications? + addUndoItem(new KateModifiedInsertLine(m_document, line, s)); +} + +void KateUndoManager::slotLineRemoved(int line, const QString &s) +{ + if (m_editCurrentUndo != 0) // do we care about notifications? + addUndoItem(new KateModifiedRemoveLine(m_document, line, s)); +} + +void KateUndoManager::undoCancel() +{ + // Don't worry about this when an edit is in progress + if (m_document->isEditRunning()) + return; + + undoSafePoint(); +} + +void KateUndoManager::undoSafePoint() { + KateUndoGroup *undoGroup = m_editCurrentUndo; + + if (undoGroup == 0 && !undoItems.isEmpty()) + undoGroup = undoItems.last(); + + if (undoGroup == 0) + return; + + undoGroup->safePoint(); +} + +void KateUndoManager::addUndoItem(KateUndo *undo) +{ + Q_ASSERT(undo != 0); // don't add null pointers to our history + Q_ASSERT(m_editCurrentUndo != 0); // make sure there is an undo group for our item + + m_editCurrentUndo->addItem(undo); + + // Clear redo buffer + qDeleteAll(redoItems); + redoItems.clear(); +} + +void KateUndoManager::setActive(bool enabled) +{ + Q_ASSERT(m_editCurrentUndo == 0); // must not already be in edit mode + Q_ASSERT(m_isActive != enabled); + + m_isActive = enabled; + + emit isActiveChanged(enabled); +} + +uint KateUndoManager::undoCount () const +{ + return undoItems.count (); +} + +uint KateUndoManager::redoCount () const +{ + return redoItems.count (); +} + +void KateUndoManager::undo() +{ + Q_ASSERT(m_editCurrentUndo == 0); // undo is not supported while we care about notifications (call editEnd() first) + + if (undoItems.count() > 0) + { + emit undoStart(document()); + + undoItems.last()->undo(activeView()); + redoItems.append (undoItems.last()); + undoItems.removeLast (); + updateModified(); + + emit undoEnd(document()); + } +} + +void KateUndoManager::redo() +{ + Q_ASSERT(m_editCurrentUndo == 0); // redo is not supported while we care about notifications (call editEnd() first) + + if (redoItems.count() > 0) + { + emit redoStart(document()); + + redoItems.last()->redo(activeView()); + undoItems.append (redoItems.last()); + redoItems.removeLast (); + updateModified(); + + emit redoEnd(document()); + } +} + +void KateUndoManager::updateModified() +{ + /* + How this works: + + After noticing that there where to many scenarios to take into + consideration when using 'if's to toggle the "Modified" flag + I came up with this baby, flexible and repetitive calls are + minimal. + + A numeric unique pattern is generated by toggling a set of bits, + each bit symbolizes a different state in the Undo Redo structure. + + undoItems.isEmpty() != null BIT 1 + redoItems.isEmpty() != null BIT 2 + docWasSavedWhenUndoWasEmpty == true BIT 3 + docWasSavedWhenRedoWasEmpty == true BIT 4 + lastUndoGroupWhenSavedIsLastUndo BIT 5 + lastUndoGroupWhenSavedIsLastRedo BIT 6 + lastRedoGroupWhenSavedIsLastUndo BIT 7 + lastRedoGroupWhenSavedIsLastRedo BIT 8 + + If you find a new pattern, please add it to the patterns array + */ + + unsigned char currentPattern = 0; + const unsigned char patterns[] = {5,16,21,24,26,88,90,93,133,144,149,154,165}; + const unsigned char patternCount = sizeof(patterns); + KateUndoGroup* undoLast = 0; + KateUndoGroup* redoLast = 0; + + if (undoItems.isEmpty()) + { + currentPattern |= 1; + } + else + { + undoLast = undoItems.last(); + } + + if (redoItems.isEmpty()) + { + currentPattern |= 2; + } + else + { + redoLast = redoItems.last(); + } + + if (docWasSavedWhenUndoWasEmpty) currentPattern |= 4; + if (docWasSavedWhenRedoWasEmpty) currentPattern |= 8; + if (lastUndoGroupWhenSaved == undoLast) currentPattern |= 16; + if (lastUndoGroupWhenSaved == redoLast) currentPattern |= 32; + if (lastRedoGroupWhenSaved == undoLast) currentPattern |= 64; + if (lastRedoGroupWhenSaved == redoLast) currentPattern |= 128; + + // This will print out the pattern information + + kDebug() << "Pattern:" << static_cast(currentPattern); + + for (uint patternIndex = 0; patternIndex < patternCount; ++patternIndex) + { + if ( currentPattern == patterns[patternIndex] ) + { + // Note: m_document->setModified() calls KateUndoManager::setModified! + m_document->setModified( false ); + // (dominik) whenever the doc is not modified, succeeding edits + // should not be merged + undoSafePoint(); + kDebug() << "setting modified to false!"; + break; + } + } +} + +void KateUndoManager::clearUndo() +{ + qDeleteAll(undoItems); + undoItems.clear (); + + lastUndoGroupWhenSaved = 0; + docWasSavedWhenUndoWasEmpty = false; + + emit undoChanged (); +} + +void KateUndoManager::clearRedo() +{ + qDeleteAll(redoItems); + redoItems.clear (); + + lastRedoGroupWhenSaved = 0; + docWasSavedWhenRedoWasEmpty = false; + + emit undoChanged (); +} + +void KateUndoManager::setModified(bool modified) +{ + if ( !modified ) + { + if ( ! undoItems.isEmpty() ) { + lastUndoGroupWhenSaved = undoItems.last(); + } + + if ( ! redoItems.isEmpty() ) { + lastRedoGroupWhenSaved = redoItems.last(); + } + + docWasSavedWhenUndoWasEmpty = undoItems.isEmpty(); + docWasSavedWhenRedoWasEmpty = redoItems.isEmpty(); + } +} + +void KateUndoManager::updateLineModifications() +{ + // change LineSaved flag of all undo & redo items to LineModified + foreach (KateUndoGroup* undoGroup, undoItems) + undoGroup->flagSavedAsModified(); + + foreach (KateUndoGroup* undoGroup, redoItems) + undoGroup->flagSavedAsModified(); + + // iterate all undo/redo items to find out, which item sets the flag LineSaved + QBitArray lines(document()->lines(), false); + for (int i = undoItems.size() - 1; i >= 0; --i) { + undoItems[i]->markRedoAsSaved(lines); + } + + lines.fill(false); + for (int i = redoItems.size() - 1; i >= 0; --i) { + redoItems[i]->markUndoAsSaved(lines); + } +} + +void KateUndoManager::setUndoRedoCursorsOfLastGroup(const KTextEditor::Cursor undoCursor, + const KTextEditor::Cursor redoCursor) +{ + Q_ASSERT(m_editCurrentUndo == 0); + if (undoItems.size()) { + KateUndoGroup * last = undoItems.last(); + last->setUndoCursor(undoCursor); + last->setRedoCursor(redoCursor); + } +} + +KTextEditor::Cursor KateUndoManager::lastRedoCursor() const +{ + Q_ASSERT(m_editCurrentUndo == 0); + if (undoItems.size()) { + KateUndoGroup * last = undoItems.last(); + return last->redoCursor(); + } + return KTextEditor::Cursor::invalid(); +} + +void KateUndoManager::updateConfig () +{ + emit undoChanged (); +} + +void KateUndoManager::setAllowComplexMerge(bool allow) +{ + m_undoComplexMerge = allow; +} + +KTextEditor::View* KateUndoManager::activeView() +{ + return m_document->activeView(); +} + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/undo/kateundomanager.h b/kate/part/undo/kateundomanager.h new file mode 100644 index 00000000..35101555 --- /dev/null +++ b/kate/part/undo/kateundomanager.h @@ -0,0 +1,224 @@ +/* This file is part of the KDE libraries + Copyright (C) 2009-2010 Bernhard Beschow + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KATEUNDOMANAGER_H +#define KATEUNDOMANAGER_H + +#include + +#include "katepartinterfaces_export.h" + +#include + +class KateDocument; +class KateUndo; +class KateUndoGroup; + +namespace KTextEditor { + class Document; + class View; + class Cursor; +} + +/** + * KateUndoManager implements a document's history. It is in either of the two states: + * @li the default state, which allows rolling back and forth the history of a document, and + * @li a state in which a new element is being added to the history. + * + * The state of the KateUndomanager can be switched using editStart() and editEnd(). + */ +class KATEPARTINTERFACES_EXPORT KateUndoManager : public QObject +{ + Q_OBJECT + + public: + /** + * Creates a clean undo history. + * + * @param doc the document the KateUndoManager will belong to + */ + KateUndoManager (KateDocument *doc); + + ~KateUndoManager(); + + KTextEditor::Document *document(); + + /** + * Returns how many undo() actions can be performed. + * + * @return the number of undo groups which can be undone + */ + uint undoCount () const; + + /** + * Returns how many redo() actions can be performed. + * + * @return the number of undo groups which can be redone + */ + uint redoCount () const; + + /** + * Prevent latest KateUndoGroup from being merged with the next one. + */ + void undoSafePoint(); + + /** + * Allows or disallows merging of "complex" undo groups. + * + * When an undo group contains different types of undo items, it is considered + * a "complex" group. + * + * @param allow whether complex merging is allowed + */ + void setAllowComplexMerge(bool allow); + + bool isActive() const { return m_isActive; } + + void setModified( bool modified ); + void updateConfig (); + void updateLineModifications(); + + /** + * Used by the swap file recovery, this function afterwards manipulates + * the undo/redo cursors of the last KateUndoGroup. + * This function should not be used other than by Kate::SwapFile. + * @param undoCursor the undo cursor + * @param redoCursor the redo cursor + */ + void setUndoRedoCursorsOfLastGroup(const KTextEditor::Cursor undoCursor, + const KTextEditor::Cursor redoCursor); + + /** + * Returns the redo cursor of the last undo group. + * Needed for the swap file recovery. + */ + KTextEditor::Cursor lastRedoCursor() const; + + public Q_SLOTS: + /** + * Undo the latest undo group. + * + * Make sure isDefaultState() is true when calling this method. + */ + void undo (); + + /** + * Redo the latest undo group. + * + * Make sure isDefaultState() is true when calling this method. + */ + void redo (); + + void clearUndo (); + void clearRedo (); + + /** + * Notify KateUndoManager about the beginning of an edit. + */ + void editStart(); + + /** + * Notify KateUndoManager about the end of an edit. + */ + void editEnd(); + + void startUndo(); + void endUndo(); + + void inputMethodStart(); + void inputMethodEnd(); + + /** + * Notify KateUndoManager that text was inserted. + */ + void slotTextInserted(int line, int col, const QString &s); + + /** + * Notify KateUndoManager that text was removed. + */ + void slotTextRemoved(int line, int col, const QString &s); + + /** + * Notify KateUndoManager that a line was marked as autowrapped. + */ + void slotMarkLineAutoWrapped(int line, bool autowrapped); + + /** + * Notify KateUndoManager that a line was wrapped. + */ + void slotLineWrapped(int line, int col, int length, bool newLine); + + /** + * Notify KateUndoManager that a line was un-wrapped. + */ + void slotLineUnWrapped(int line, int col, int length, bool lineRemoved); + + /** + * Notify KateUndoManager that a line was inserted. + */ + void slotLineInserted(int line, const QString &s); + + /** + * Notify KateUndoManager that a line was removed. + */ + void slotLineRemoved(int line, const QString &s); + + Q_SIGNALS: + void undoChanged (); + void undoStart (KTextEditor::Document*); + void undoEnd (KTextEditor::Document*); + void redoStart (KTextEditor::Document*); + void redoEnd (KTextEditor::Document*); + void isActiveChanged(bool enabled); + + private Q_SLOTS: + /** + * @short Add an undo item to the current undo group. + * + * @param undo undo item to be added, must be non-null + */ + void addUndoItem(KateUndo *undo); + + void setActive(bool active); + + void updateModified(); + + void undoCancel(); + void viewCreated (KTextEditor::Document *, KTextEditor::View *newView); + + private: + KTextEditor::View *activeView(); + + private: + KateDocument *m_document; + bool m_undoComplexMerge; + bool m_isActive; + KateUndoGroup* m_editCurrentUndo; + QList undoItems; + QList redoItems; + // these two variables are for resetting the document to + // non-modified if all changes have been undone... + KateUndoGroup* lastUndoGroupWhenSaved; + KateUndoGroup* lastRedoGroupWhenSaved; + bool docWasSavedWhenUndoWasEmpty; + bool docWasSavedWhenRedoWasEmpty; +}; + +#endif + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/utils/kateautoindent.cpp b/kate/part/utils/kateautoindent.cpp new file mode 100644 index 00000000..36a55354 --- /dev/null +++ b/kate/part/utils/kateautoindent.cpp @@ -0,0 +1,388 @@ +/* This file is part of the KDE libraries + Copyright (C) 2003 Jesse Yurkovich + Copyright (C) 2004 >Anders Lund (KateVarIndent class) + Copyright (C) 2005 Dominik Haumann (basic support for config page) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "kateautoindent.h" +#include "moc_kateautoindent.cpp" + +#include "kateconfig.h" +#include "katehighlight.h" +#include "kateglobal.h" +#include "kateview.h" +#include "kateextendedattribute.h" +#include "katedocument.h" +// #include "katebuffer.h" + +#include +#include +#include + +#include + +const QString MODE_NONE = QLatin1String("none"); +const QString MODE_NORMAL = QLatin1String("normal"); + +//BEGIN KateAutoIndent + +QStringList KateAutoIndent::listModes () +{ + QStringList l; + + for (int i = 0; i < modeCount(); ++i) + l << modeDescription(i); + + return l; +} + +QStringList KateAutoIndent::listIdentifiers () +{ + QStringList l; + + for (int i = 0; i < modeCount(); ++i) + l << modeName(i); + + return l; +} + +int KateAutoIndent::modeCount () +{ + // inbuild modes + return 2; +} + + +QString KateAutoIndent::modeName (int mode) +{ + if (mode == 0 || mode >= modeCount ()) + return MODE_NONE; + + if (mode == 1) + return MODE_NORMAL; + + return QString(); +} + +QString KateAutoIndent::modeDescription (int mode) +{ + if (mode == 0 || mode >= modeCount ()) + return i18nc ("Autoindent mode", "None"); + + if (mode == 1) + return i18nc ("Autoindent mode", "Normal"); + + return QString(); +} + +QString KateAutoIndent::modeRequiredStyle(int mode) +{ + if (mode == 0 || mode == 1 || mode >= modeCount()) + return QString(); + + return QString(); +} + +uint KateAutoIndent::modeNumber (const QString &name) +{ + for (int i = 0; i < modeCount(); ++i) + if (modeName(i) == name) + return i; + + return 0; +} + +KateAutoIndent::KateAutoIndent (KateDocument *_doc) + : QObject(_doc), doc(_doc) +{ + // don't call updateConfig() here, document might is not ready for that.... +} + +KateAutoIndent::~KateAutoIndent () +{ +} + +QString KateAutoIndent::tabString (int length, int align) const +{ + QString s; + length = qMin (length, 256); // sanity check for large values of pos + int spaces = qBound(0, align - length, 256); + + if (!useSpaces) + { + s.append (QString (length / tabWidth, '\t')); + length = length % tabWidth; + } + s.append(QString(length + spaces, ' ')); + + return s; +} + +bool KateAutoIndent::doIndent(int line, int indentDepth, int align) +{ + kDebug (13060) << "doIndent: line: " << line << " indentDepth: " << indentDepth << " align: " << align; + + Kate::TextLine textline = doc->plainKateTextLine(line); + + // textline not found, cu + if (!textline) + return false; + + // sanity check + if (indentDepth < 0) + indentDepth = 0; + + const QString oldIndentation = textline->leadingWhitespace(); + + // Preserve existing "tabs then spaces" alignment if and only if: + // - no alignment was passed to doIndent and + // - we aren't using spaces for indentation and + // - we aren't rounding indentation up to the next multiple of the indentation width and + // - we aren't using a combination to tabs and spaces for alignment, or in other words + // the indent width is a multiple of the tab width. + bool preserveAlignment = !useSpaces && keepExtra && indentWidth % tabWidth == 0; + if (align == 0 && preserveAlignment) + { + // Count the number of consecutive spaces at the end of the existing indentation + int i = oldIndentation.size() - 1; + while (i >= 0 && oldIndentation.at(i) == ' ') + --i; + // Use the passed indentDepth as the alignment, and set the indentDepth to + // that value minus the number of spaces found (but don't let it get negative). + align = indentDepth; + indentDepth = qMax(0, align - (oldIndentation.size() - 1 - i)); + } + + QString indentString = tabString(indentDepth, align); + + // Modify the document *ONLY* if smth has really changed! + if (oldIndentation != indentString) + { + // remove leading whitespace, then insert the leading indentation + doc->editStart (); + doc->editRemoveText (line, 0, oldIndentation.length()); + doc->editInsertText (line, 0, indentString); + doc->editEnd (); + } + + return true; +} + +bool KateAutoIndent::doIndentRelative(int line, int change) +{ + kDebug (13060) << "doIndentRelative: line: " << line << " change: " << change; + + Kate::TextLine textline = doc->plainKateTextLine(line); + + // get indent width of current line + int indentDepth = textline->indentDepth (tabWidth); + int extraSpaces = indentDepth % indentWidth; + + // add change + indentDepth += change; + + // if keepExtra is off, snap to a multiple of the indentWidth + if (!keepExtra && extraSpaces > 0) + { + if (change < 0) + indentDepth += indentWidth - extraSpaces; + else + indentDepth -= extraSpaces; + } + + // do indent + return doIndent(line, indentDepth); +} + +void KateAutoIndent::keepIndent ( int line ) +{ + // no line in front, no work... + if (line <= 0) + return; + + // keep indentation: find line with content + int nonEmptyLine = line - 1; + while (nonEmptyLine >= 0) { + if (doc->lineLength(nonEmptyLine) > 0) { + break; + } + --nonEmptyLine; + } + Kate::TextLine prevTextLine = doc->plainKateTextLine(nonEmptyLine); + Kate::TextLine textLine = doc->plainKateTextLine(line); + + // textline not found, cu + if (!prevTextLine || !textLine) + return; + + const QString previousWhitespace = prevTextLine->leadingWhitespace(); + + // remove leading whitespace, then insert the leading indentation + doc->editStart (); + + if (!keepExtra) + { + const QString currentWhitespace = textLine->leadingWhitespace(); + doc->editRemoveText (line, 0, currentWhitespace.length()); + } + + doc->editInsertText (line, 0, previousWhitespace); + doc->editEnd (); +} + +void KateAutoIndent::reloadScript() +{ + // small trick to force reload + QString currentMode = m_mode; + m_mode = QString(); + setMode(currentMode); +} + +void KateAutoIndent::setMode (const QString &name) +{ + // bail out, already set correct mode... + if (m_mode == name) + return; + + // first, catch easy stuff... normal mode and none, easy... + if ( name.isEmpty() || name == MODE_NONE ) + { + m_mode = MODE_NONE; + return; + } + + if ( name == MODE_NORMAL ) + { + m_mode = MODE_NORMAL; + return; + } + + // Fall back to normal + m_mode = MODE_NORMAL; +} + +void KateAutoIndent::checkRequiredStyle() +{ + doc->config()->setIndentationMode(MODE_NORMAL); +} + +void KateAutoIndent::updateConfig () +{ + KateDocumentConfig *config = doc->config(); + + useSpaces = config->replaceTabsDyn(); + keepExtra = config->keepExtraSpaces(); + tabWidth = config->tabWidth(); + indentWidth = config->indentationWidth(); +} + + +bool KateAutoIndent::changeIndent (const KTextEditor::Range &range, int change) +{ + QList skippedLines; + + // loop over all lines given... + for (int line = range.start().line () < 0 ? 0 : range.start().line (); + line <= qMin (range.end().line (), doc->lines()-1); ++line) + { + // don't indent empty lines + if (doc->line(line).isEmpty()) + { + skippedLines.append (line); + continue; + } + // don't indent the last line when the cursor is on the first column + if (line == range.end().line() && range.end().column() == 0) + { + skippedLines.append (line); + continue; + } + + doIndentRelative(line, change * indentWidth); + } + + if (skippedLines.count() > range.numberOfLines()) + { + // all lines were empty, so indent them nevertheless + foreach (int line, skippedLines) + doIndentRelative(line, change * indentWidth); + } + + return true; +} + +void KateAutoIndent::indent (KateView *view, const KTextEditor::Range &range) +{ +} + +void KateAutoIndent::userTypedChar (KateView *view, const KTextEditor::Cursor &position, QChar typedChar) +{ + // normal mode + if (m_mode == MODE_NORMAL) + { + // only indent on new line, per default + if (typedChar != '\n') + return; + + // keep indent of previous line + keepIndent (position.line()); + } +} +//END KateAutoIndent + +//BEGIN KateViewIndentAction +KateViewIndentationAction::KateViewIndentationAction(KateDocument *_doc, const QString& text, QObject *parent) + : KActionMenu (text, parent), doc(_doc) +{ + connect(menu(),SIGNAL(aboutToShow()),this,SLOT(slotAboutToShow())); + actionGroup = new QActionGroup(menu()); +} + +void KateViewIndentationAction::slotAboutToShow() +{ + QStringList modes = KateAutoIndent::listModes (); + + menu()->clear (); + foreach (QAction *action, actionGroup->actions()) { + actionGroup->removeAction(action); + } + for (int z=0; zaddAction( '&' + KateAutoIndent::modeDescription(z).replace('&', "&&") ); + actionGroup->addAction(action); + action->setCheckable( true ); + action->setData( z ); + + QString requiredStyle = KateAutoIndent::modeRequiredStyle(z); + action->setEnabled(requiredStyle.isEmpty() || requiredStyle == doc->highlight()->style()); + + if ( doc->config()->indentationMode() == KateAutoIndent::modeName (z) ) + action->setChecked( true ); + } + + disconnect( menu(), SIGNAL(triggered(QAction*)), this, SLOT(setMode(QAction*)) ); + connect( menu(), SIGNAL(triggered(QAction*)), this, SLOT(setMode(QAction*)) ); +} + +void KateViewIndentationAction::setMode (QAction *action) +{ + // set new mode + doc->config()->setIndentationMode(KateAutoIndent::modeName (action->data().toInt())); + doc->rememberUserDidSetIndentationMode (); +} +//END KateViewIndentationAction + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/utils/kateautoindent.h b/kate/part/utils/kateautoindent.h new file mode 100644 index 00000000..29a9f0ca --- /dev/null +++ b/kate/part/utils/kateautoindent.h @@ -0,0 +1,240 @@ +/* This file is part of the KDE libraries + Copyright (C) 2003 Jesse Yurkovich + Copyright (C) 2004 >Anders Lund (KateVarIndent class) + Copyright (C) 2005 Dominik Haumann (basic support for config page) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef __KATE_AUTO_INDENT_H__ +#define __KATE_AUTO_INDENT_H__ + +#include "kateconfig.h" + +#include + +#include +#include + +class KateDocument; +class KateHighlighting; + +/** + * Provides Auto-Indent functionality for katepart. + * This baseclass is a real dummy, does nothing beside remembering the document it belongs too, + * only to have the object around + */ +class KateAutoIndent : public QObject +{ + Q_OBJECT + /* + * Static methods to list indention modes + */ + public: + /** + * List all possible modes by name, i.e. "C Style", "XML Style", ... + * @return list of modes + */ + static QStringList listModes (); + + /** + * List all possible names, i.e. "cstyle", "xml", ... + * @return list of indenter identifiers + */ + static QStringList listIdentifiers (); + + /** + * Return the mode name given the mode + * @param mode mode index + * @return name for this mode index + */ + static QString modeName (int mode); + + /** + * Return the mode description + * @param mode mode index + * @return mode index + */ + static QString modeDescription (int mode); + + /** + * Return the syntax highlighting style required to use this mode + * @param mode mode index + * @return required style, or empty if the mode doesn't require any style + */ + static QString modeRequiredStyle(int mode); + + /** + * Maps name -> index + * @param name mode name + * @return mode index + */ + static uint modeNumber (const QString &name); + + /** + * count of modes + * @return number of existing modes + */ + static int modeCount (); + + /* + * Construction + Destruction + */ + public: + /** + * Constructor, creates dummy indenter "None" + * \param doc parent document + */ + explicit KateAutoIndent (KateDocument *doc); + + /** + * Destructor + */ + ~KateAutoIndent (); + + /* + * Internal helper for the subclasses and itself + */ + private: + /** + * Produces a string with the proper indentation characters for its length. + * + * @param length The length of the indention in characters. + * @param align Length of alignment, ignored if less of equal to length + * @return A QString representing @p length characters (factoring in tabs and spaces) + */ + QString tabString (int length, int align) const; + + /** + * Set the indent level of the line. + * \param line line to change indent for + * \param change set indentation to given number of spaces + * \param align if align is higher than indentDepth, the difference + * represents a number of spaces to be added after the indent + */ + bool doIndent(int line, int indentDepth, int align = 0); + + /** + * Change the indent of the specified line by the number of levels + * specified by change. Positive values will indent more, negative values + * will indent less. + * \param line line to change indent for + * \param change change the indentation by given number of spaces + */ + bool doIndentRelative(int line, int change); + + /** + * Reuse the indent of the previous line + * \param line line to change indent for + */ + void keepIndent ( int line ); + + public: + /** + * Switch indenter + * Nop if already set to given mode + * Otherwise switch to given indenter or to "None" if no suitable found... + * @param name indention mode wanted + */ + void setMode (const QString &name); + + /** + * Check if the current highlighting mode provides the style required by the + * current indenter. If not, deactivate the indenter by changing to "normal" + * mode. + */ + void checkRequiredStyle(); + + /** + * mode name + */ + const QString &modeName () const { return m_mode; } + + /** + * Update indenter's configuration (indention width, etc.) + * Is called in the updateConfig() of the document and after creation of the indenter... + */ + void updateConfig (); + + /** + * Function to provide the common indent/unindent/clean indent functionality to the document + * This should be generic for all indenters, internally it uses the doIndent function. + * This works equal for all indenters, even for "none" or the scripts + * \param range range of text to change indent for + * \param change level of indents to add or remove, zero will still trigger cleaning of indentation + * and removal of extra spaces, if option set + * \return \e true on success, otherwise \e false + */ + bool changeIndent (const KTextEditor::Range &range, int change); + + /** + * The document requests the indenter to indent the given range of existing text. + * This may happen to indent text pasted or to reindent existing text. + * For "none" and "normal" this is a nop, for the scripts, the expression + * will be asked for indent level for each line + * \param view the view the user work at + * \param range the range of text to indent... + */ + void indent (KateView *view, const KTextEditor::Range &range); + + /** + * The user typed some char, the indenter can react on this + * '\n' will be send as char if the user wraps a line + * \param view the view the user work at + * \param position current cursor position, after the inserted char... + * \param typedChar the inserted char + */ + void userTypedChar (KateView *view, const KTextEditor::Cursor &position, QChar typedChar); + + public Q_SLOTS: + void reloadScript(); + + /* + * needed data + */ + private: + KateDocument *doc; //!< the document the indenter works on + int tabWidth; //!< The number of characters simulated for a tab + int indentWidth; //!< The number of characters used when tabs are replaced by spaces + bool useSpaces; //!< Should we use spaces or tabs to indent + bool keepExtra; //!< Keep indentation that is not on indentation boundaries + QString m_mode; +}; + +/** + * This action provides a list of available indenters and gets plugged + * into the KateView's KActionCollection. + */ +class KateViewIndentationAction : public KActionMenu +{ + Q_OBJECT + + public: + KateViewIndentationAction(KateDocument *_doc, const QString& text, QObject *parent); + + private: + KateDocument* doc; + QActionGroup *actionGroup; + + public Q_SLOTS: + void slotAboutToShow(); + + private Q_SLOTS: + void setMode (QAction*); +}; + +#endif + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/utils/katebookmarks.cpp b/kate/part/utils/katebookmarks.cpp new file mode 100644 index 00000000..7ca9c549 --- /dev/null +++ b/kate/part/utils/katebookmarks.cpp @@ -0,0 +1,295 @@ +/* This file is part of the KDE libraries + Copyright (C) 2002, 2003, 2004 Anders Lund + Copyright (C) 2002 John Firebaugh + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "katebookmarks.h" +#include "moc_katebookmarks.cpp" + +#include "katedocument.h" +#include "kateview.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace KTextEditor{ class Document; } + +KateBookmarks::KateBookmarks( KateView* view, Sorting sort ) + : QObject( view ) + , m_view( view ) + , m_bookmarkClear (0) + , m_sorting( sort ) +{ + setObjectName( "kate bookmarks" ); + connect (view->doc(), SIGNAL(marksChanged(KTextEditor::Document*)), this, SLOT(marksChanged())); + _tries=0; + m_bookmarksMenu = 0L; +} + +KateBookmarks::~KateBookmarks() +{ +} + +void KateBookmarks::createActions( KActionCollection* ac ) +{ + m_bookmarkToggle = new KToggleAction( i18n("Set &Bookmark"), this ); + ac->addAction( "bookmarks_toggle", m_bookmarkToggle ); + m_bookmarkToggle->setIcon( KIcon( "bookmark-new" ) ); + m_bookmarkToggle->setShortcut( Qt::CTRL+Qt::Key_B ); + m_bookmarkToggle->setWhatsThis(i18n("If a line has no bookmark then add one, otherwise remove it.")); + connect( m_bookmarkToggle, SIGNAL(triggered()), this, SLOT(toggleBookmark()) ); + + m_bookmarkClear = new KAction( i18n("Clear &All Bookmarks"), this ); + ac->addAction("bookmarks_clear", m_bookmarkClear); + m_bookmarkClear->setWhatsThis(i18n("Remove all bookmarks of the current document.")); + connect( m_bookmarkClear, SIGNAL(triggered()), this, SLOT(clearBookmarks()) ); + + m_goNext = new KAction( i18n("Next Bookmark"), this); + ac->addAction("bookmarks_next", m_goNext); + m_goNext->setIcon( KIcon( "go-down-search" ) ); + m_goNext->setShortcut( Qt::ALT + Qt::Key_PageDown ); + m_goNext->setWhatsThis(i18n("Go to the next bookmark.")); + connect( m_goNext, SIGNAL(triggered()), this, SLOT(goNext()) ); + + m_goPrevious = new KAction( i18n("Previous Bookmark"), this); + ac->addAction("bookmarks_previous", m_goPrevious); + m_goPrevious->setIcon( KIcon( "go-up-search" ) ); + m_goPrevious->setShortcut( Qt::ALT + Qt::Key_PageUp ); + m_goPrevious->setWhatsThis(i18n("Go to the previous bookmark.")); + connect( m_goPrevious, SIGNAL(triggered()), this, SLOT(goPrevious()) ); + + KActionMenu *actionMenu = new KActionMenu(i18n("&Bookmarks"), this); + ac->addAction("bookmarks", actionMenu); + m_bookmarksMenu = actionMenu->menu(); + + connect( m_bookmarksMenu, SIGNAL(aboutToShow()), this, SLOT(bookmarkMenuAboutToShow())); + + marksChanged (); + + // Always want the actions with shortcuts plugged into something so their shortcuts can work + m_view->addAction(m_bookmarkToggle); + m_view->addAction(m_bookmarkClear); + m_view->addAction(m_goNext); + m_view->addAction(m_goPrevious); +} + +void KateBookmarks::toggleBookmark () +{ + uint mark = m_view->doc()->mark( m_view->cursorPosition().line() ); + if( mark & KTextEditor::MarkInterface::markType01 ) + m_view->doc()->removeMark( m_view->cursorPosition().line(), + KTextEditor::MarkInterface::markType01 ); + else + m_view->doc()->addMark( m_view->cursorPosition().line(), + KTextEditor::MarkInterface::markType01 ); +} + +void KateBookmarks::clearBookmarks () +{ + QHash m = m_view->doc()->marks(); + for (QHash::const_iterator i = m.constBegin(); i != m.constEnd(); ++i) + m_view->doc()->removeMark( i.value()->line, KTextEditor::MarkInterface::markType01 ); + + // just to be sure ;) + // dominik: the following line can be deleted afaics, as Document::removeMark emits this signal. + marksChanged (); +} + +void KateBookmarks::insertBookmarks( QMenu& menu ) +{ + int line = m_view->cursorPosition().line(); + const QRegExp re("&(?!&)"); + int next = -1; // -1 means next bookmark doesn't exist + int prev = -1; // -1 means previous bookmark doesn't exist + + const QHash &m = m_view->doc()->marks(); + QVector bookmarkLineArray; // Array of line numbers which have bookmarks + + if ( m.isEmpty() ) + return; + + // Find line numbers where bookmarks are set & store those line numbers in bookmarkLineArray + for (QHash::const_iterator it = m.constBegin(); it != m.constEnd(); ++it) + { + if( it.value()->type & KTextEditor::MarkInterface::markType01 ) + { + bookmarkLineArray.append(it.value()->line); + } + } + + if ( m_sorting == Position ) + { + qSort(bookmarkLineArray.begin(), bookmarkLineArray.end()); + } + + QAction* firstNewAction = menu.addSeparator(); + // Consider each line with a bookmark one at a time + for (int i = 0; i < bookmarkLineArray.size(); ++i) + { + // Get text in this particular line in a QString + QString bText = menu.fontMetrics().elidedText + ( m_view->doc()->line( bookmarkLineArray.at(i) ), + Qt::ElideRight, + menu.fontMetrics().maxWidth() * 32 ); + bText.replace(re, "&&"); // kill undesired accellerators! + bText.replace('\t', ' '); // kill tabs, as they are interpreted as shortcuts + + QAction *before=0; + if ( m_sorting == Position ) + { + // 3 actions already present + if (menu.actions().size() <= i+3) + before=0; + else + before=menu.actions()[i+3]; + } + + // Adding action for this bookmark in menu + if (before) { + QAction *a=new QAction(QString("%1 %3 - \"%2\"") + .arg( bookmarkLineArray.at(i) + 1 ).arg( bText ) + .arg(false),&menu); + menu.insertAction(before,a); + connect(a,SIGNAL(activated()),this,SLOT(gotoLine())); + a->setData(bookmarkLineArray.at(i)); + if (!firstNewAction) firstNewAction = a; + + } else { + QAction* a = menu.addAction(QString("%1 %3 - \"%2\"") + .arg( bookmarkLineArray.at(i) + 1 ).arg( bText ) + .arg(false), + this, SLOT(gotoLine())); + a->setData(bookmarkLineArray.at(i)); + } + + // Find the line number of previous & next bookmark (if present) in relation to the cursor + if ( bookmarkLineArray.at(i) < line ) + { + if ( (prev == -1) || prev < (bookmarkLineArray.at(i)) ) + prev = bookmarkLineArray.at(i); + } + else if ( bookmarkLineArray.at(i) > line ) + { + if ( (next == -1) || next > (bookmarkLineArray.at(i)) ) + next = bookmarkLineArray.at(i); + } + } + + if ( next != -1 ) + { + // Insert action for next bookmark + m_goNext->setText( i18n("&Next: %1 - \"%2\"", next + 1 , + KStringHandler::rsqueeze( m_view->doc()->line( next ), 24 ) ) ); + menu.insertAction(firstNewAction, m_goNext); + firstNewAction = m_goNext; + } + if ( prev != -1 ) + { + // Insert action for previous bookmark + m_goPrevious->setText( i18n("&Previous: %1 - \"%2\"", prev + 1 , + KStringHandler::rsqueeze( m_view->doc()->line( prev ), 24 ) ) ); + menu.insertAction(firstNewAction, m_goPrevious); + firstNewAction = m_goPrevious; + } + + if ( next != -1 || prev != -1 ) + menu.insertSeparator(firstNewAction); +} + +void KateBookmarks::gotoLine() +{ + if (!sender()) return; + gotoLine(((QAction*)(sender()))->data().toInt()); +} + +void KateBookmarks::gotoLine (int line) +{ + m_view->setCursorPosition(KTextEditor::Cursor(line, 0)); +} + +void KateBookmarks::bookmarkMenuAboutToShow() +{ + m_bookmarksMenu->clear(); + m_bookmarkToggle->setChecked( m_view->doc()->mark( m_view->cursorPosition().line() ) + & KTextEditor::MarkInterface::markType01 ); + m_bookmarksMenu->addAction(m_bookmarkToggle); + m_bookmarksMenu->addAction(m_bookmarkClear); + + m_goNext->setText( i18n("Next Bookmark") ); + m_goPrevious->setText( i18n("Previous Bookmark") ); + + insertBookmarks(*m_bookmarksMenu); +} + +void KateBookmarks::goNext() +{ + const QHash &m = m_view->doc()->marks(); + if (m.isEmpty()) + return; + + int line = m_view->cursorPosition().line(); + int found = -1; + + for (QHash::const_iterator it = m.constBegin(); it != m.constEnd(); ++it) + { + if ( (it.value()->line > line) && ((found == -1) || (found > it.value()->line)) ) + found = it.value()->line; + } + + if (found != -1) + gotoLine ( found ); +} + +void KateBookmarks::goPrevious() +{ + const QHash &m = m_view->doc()->marks(); + if (m.isEmpty()) + return; + + int line = m_view->cursorPosition().line(); + int found = -1; + + for (QHash::const_iterator it = m.constBegin(); it != m.constEnd(); ++it) + { + if ((it.value()->line < line) && ((found == -1) || (found < it.value()->line))) + found = it.value()->line; + } + + if (found != -1) + gotoLine ( found ); +} + +void KateBookmarks::marksChanged () +{ + if (m_bookmarkClear) + m_bookmarkClear->setEnabled( !m_view->doc()->marks().isEmpty() ); +} + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/utils/katebookmarks.h b/kate/part/utils/katebookmarks.h new file mode 100644 index 00000000..75dfc183 --- /dev/null +++ b/kate/part/utils/katebookmarks.h @@ -0,0 +1,81 @@ +/* This file is part of the KDE libraries + Copyright (C) 2002, 2003 Anders Lund + Copyright (C) 2002 John Firebaugh + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef __KATE_BOOKMARKS_H__ +#define __KATE_BOOKMARKS_H__ + +#include + +class KateView; + +namespace KTextEditor { class Mark; class View; } + +class KAction; +class KToggleAction; +class KActionCollection; +#include + +class KateBookmarks : public QObject +{ + Q_OBJECT + + public: + enum Sorting { Position, Creation }; + explicit KateBookmarks( KateView* parent, Sorting sort=Position ); + virtual ~KateBookmarks(); + + void createActions( KActionCollection* ); + + KateBookmarks::Sorting sorting() { return m_sorting; } + void setSorting( Sorting s ) { m_sorting = s; } + + protected: + void insertBookmarks( QMenu& menu); + + private Q_SLOTS: + void toggleBookmark(); + void clearBookmarks(); + + void gotoLine(); + void gotoLine (int line); + + void bookmarkMenuAboutToShow(); + + void goNext(); + void goPrevious(); + + void marksChanged (); + + private: + KateView* m_view; + KToggleAction* m_bookmarkToggle; + KAction* m_bookmarkClear; + KAction* m_goNext; + KAction* m_goPrevious; + + Sorting m_sorting; + QMenu* m_bookmarksMenu; + + uint _tries; +}; + +#endif + +// kate: space-indent on; indent-width 2; replace-tabs on; +// vim: noet ts=2 diff --git a/kate/part/utils/katecmd.cpp b/kate/part/utils/katecmd.cpp new file mode 100644 index 00000000..a5ce4992 --- /dev/null +++ b/kate/part/utils/katecmd.cpp @@ -0,0 +1,247 @@ +/* This file is part of the KDE libraries and the Kate part. + * + * Copyright (C) 2001-2010 Christoph Cullmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "katecmd.h" +#include "kateglobal.h" + +#include + +//BEGIN KateCmd +#define CMD_HIST_LENGTH 256 + +KateCmd::KateCmd () +{ + m_cmdCompletion.addItem("help"); +} + +KateCmd::~KateCmd () +{ +} + +bool KateCmd::registerCommand (KTextEditor::Command *cmd) +{ + QStringList l = cmd->cmds (); + + for (int z=0; z::const_iterator i = m_dict.constBegin(); + while (i != m_dict.constEnd()) { + if (i.value()==cmd) l << i.key(); + ++i; + } + + for ( QStringList::Iterator it1 = l.begin(); it1 != l.end(); ++it1 ) { + m_dict.remove(*it1); + m_cmdCompletion.removeItem(*it1); + //kDebug(13050)<<"Removed command:"<<*it1; + } + + return true; +} + +KTextEditor::Command *KateCmd::queryCommand (const QString &cmd) const +{ + // a command can be named ".*[\w\-]+" with the constrain that it must + // contain at least one letter. + int f = 0; + bool b = false; + + // special case: '-' and '_' can be part of a command name, but if the + // command is 's' (substitute), it should be considered the delimiter and + // should not be counted as part of the command name + if ( cmd.length() >= 2 && cmd.at(0) == 's' && ( cmd.at(1) == '-' || cmd.at(1) == '_') ) { + return m_dict.value(QString("s")); + } + + for ( ; f < cmd.length(); f++ ) + { + if ( cmd[f].isLetter() ) + b = true; + if ( b && ( ! cmd[f].isLetterOrNumber() && cmd[f] != '-' && cmd[f] != '_' ) ) + break; + } + return m_dict.value(cmd.left(f)); +} + +QList KateCmd::commands() const +{ + return m_dict.values(); +} + +QStringList KateCmd::commandList () const +{ + return m_cmds; +} + +KateCmd *KateCmd::self () +{ + return KateGlobal::self()->cmdManager (); +} + +void KateCmd::appendHistory( const QString &cmd ) +{ + if (!m_history.isEmpty()) //this line should be backported to 3.x + if ( m_history.last() == cmd ) + return; + + if ( m_history.count() == CMD_HIST_LENGTH ) + m_history.removeFirst(); + + m_history.append( cmd ); +} + +const QString KateCmd::fromHistory( int index ) const +{ + if ( index < 0 || index > m_history.count() - 1 ) + return QString(); + return m_history[ index ]; +} + +KCompletion* KateCmd::commandCompletionObject() +{ + return &m_cmdCompletion; +} +//END KateCmd + +//BEGIN KateCmdShellCompletion +/* + A lot of the code in the below class is copied from + kdelibs/kio/kio/kshellcompletion.cpp + Copyright (C) 2000 David Smith + Copyright (C) 2004 Anders Lund +*/ +KateCmdShellCompletion::KateCmdShellCompletion() + : KCompletion() +{ + m_word_break_char = ' '; + m_quote_char1 = '\"'; + m_quote_char2 = '\''; + m_escape_char = '\\'; +} + +QString KateCmdShellCompletion::makeCompletion( const QString &text ) +{ + // Split text at the last unquoted space + // + splitText(text, m_text_start, m_text_compl); + + // Make completion on the last part of text + // + return KCompletion::makeCompletion( m_text_compl ); +} + +void KateCmdShellCompletion::postProcessMatch( QString *match ) const +{ + if ( match->isNull() ) + return; + + match->prepend( m_text_start ); +} + +void KateCmdShellCompletion::postProcessMatches( QStringList *matches ) const +{ + for ( QStringList::Iterator it = matches->begin(); + it != matches->end(); it++ ) + if ( !(*it).isNull() ) + (*it).prepend( m_text_start ); +} + +void KateCmdShellCompletion::postProcessMatches( KCompletionMatches *matches ) const +{ + for ( KCompletionMatches::Iterator it = matches->begin(); + it != matches->end(); it++ ) + if ( !(*it).value().isNull() ) + (*it).value().prepend( m_text_start ); +} + +void KateCmdShellCompletion::splitText(const QString &text, QString &text_start, + QString &text_compl) const +{ + bool in_quote = false; + bool escaped = false; + QChar p_last_quote_char; + int last_unquoted_space = -1; + int end_space_len = 0; + + for (int pos = 0; pos < text.length(); pos++) { + + end_space_len = 0; + + if ( escaped ) { + escaped = false; + } + else if ( in_quote && text[pos] == p_last_quote_char ) { + in_quote = false; + } + else if ( !in_quote && text[pos] == m_quote_char1 ) { + p_last_quote_char = m_quote_char1; + in_quote = true; + } + else if ( !in_quote && text[pos] == m_quote_char2 ) { + p_last_quote_char = m_quote_char2; + in_quote = true; + } + else if ( text[pos] == m_escape_char ) { + escaped = true; + } + else if ( !in_quote && text[pos] == m_word_break_char ) { + + end_space_len = 1; + + while ( pos+1 < text.length() && text[pos+1] == m_word_break_char ) { + end_space_len++; + pos++; + } + + if ( pos+1 == text.length() ) + break; + + last_unquoted_space = pos; + } + } + + text_start = text.left( last_unquoted_space + 1 ); + + // the last part without trailing blanks + text_compl = text.mid( last_unquoted_space + 1 ); +} + +//END KateCmdShellCompletion + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/utils/katecmd.h b/kate/part/utils/katecmd.h new file mode 100644 index 00000000..260a53b7 --- /dev/null +++ b/kate/part/utils/katecmd.h @@ -0,0 +1,107 @@ +/* This file is part of the KDE libraries and the Kate part. + * + * Copyright (C) 2001-2010 Christoph Cullmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef KATE_CMD_H +#define KATE_CMD_H + +#include "katepartinterfaces_export.h" + +#include + +#include + +#include +#include + +class KATEPARTINTERFACES_EXPORT KateCmd +{ + public: + KateCmd (); + ~KateCmd (); + + static KateCmd *self (); + + bool registerCommand (KTextEditor::Command *cmd); + bool unregisterCommand (KTextEditor::Command *cmd); + KTextEditor::Command *queryCommand (const QString &cmd) const; + QList commands() const; + QStringList commandList() const; + + QStringList cmds (); + void appendHistory( const QString &cmd ); + const QString fromHistory( int i ) const; + uint historyLength() const { return m_history.count(); } + + KCompletion* commandCompletionObject(); + + private: + QHash m_dict; + QStringList m_cmds; + QStringList m_history; + KCompletion m_cmdCompletion; // shared completion object for all KateCmdLineEdits in each KTE::View +}; + +/** + * A KCompletion object that completes last ?unquoted? word in the string + * passed. Do not mistake "shell" for anything related to quoting, this + * simply mimics shell tab completion by completing the last word in the + * provided text. + */ +class KateCmdShellCompletion : public KCompletion +{ + public: + KateCmdShellCompletion(); + + /** + * Finds completions to the given text. + * The first match is returned and emitted in the signal match(). + * @param text the text to complete + * @return the first match, or QString() if not found + */ + QString makeCompletion(const QString &text); + + protected: + // Called by KCompletion + void postProcessMatch( QString *match ) const; + void postProcessMatches( QStringList *matches ) const; + void postProcessMatches( KCompletionMatches *matches ) const; + + private: + /** + * Split text at the last unquoted space + * + * @param text_start will be set to the text at the left, including the space + * @param text_compl Will be set to the text at the right. This is the text to complete. + */ + void splitText( const QString &text, QString &text_start, QString &text_compl ) const; + + QChar m_word_break_char; + QChar m_quote_char1; + QChar m_quote_char2; + QChar m_escape_char; + + QString m_text_start; + QString m_text_compl; + +}; + +#endif + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/utils/katecmds.cpp b/kate/part/utils/katecmds.cpp new file mode 100644 index 00000000..1dbaad2a --- /dev/null +++ b/kate/part/utils/katecmds.cpp @@ -0,0 +1,1008 @@ +/* This file is part of the KDE libraries and the Kate part. + * + * Copyright (C) 2003-2005 Anders Lund + * Copyright (C) 2001-2010 Christoph Cullmann + * Copyright (C) 2001 Charles Samuels + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "katecmds.h" + +#include "katedocument.h" +#include "kateview.h" +#include "kateconfig.h" +#include "kateautoindent.h" +#include "katetextline.h" +#include "katesyntaxmanager.h" +#include "kateglobal.h" +#include "katerenderer.h" +#include "katecmd.h" + +#include +#include +#include +#include + +#include +#include + +//BEGIN CoreCommands +KateCommands::CoreCommands* KateCommands::CoreCommands::m_instance = 0; + +// this returns wheather the string s could be converted to +// a bool value, one of on|off|1|0|true|false. the argument val is +// set to the extracted value in case of success +static bool getBoolArg( const QString &t, bool *val ) +{ + bool res( false ); + QString s = t.toLower(); + res = (s == "on" || s == "1" || s == "true"); + if ( res ) + { + *val = true; + return true; + } + res = (s == "off" || s == "0" || s == "false"); + if ( res ) + { + *val = false; + return true; + } + return false; +} + +const QStringList &KateCommands::CoreCommands::cmds() +{ + static QStringList l; + + if (l.isEmpty()) + l << "indent" << "unindent" << "cleanindent" << "fold" << "tfold" << "unfold" + << "comment" << "uncomment" << "goto" << "kill-line" + << "set-tab-width" << "set-replace-tabs" << "set-show-tabs" + << "set-indent-width" + << "set-indent-mode" << "set-auto-indent" + << "set-line-numbers" << "set-folding-markers" << "set-icon-border" + << "set-indent-pasted-text" << "set-word-wrap" << "set-word-wrap-column" + << "set-replace-tabs-save" << "set-remove-trailing-spaces" + << "set-highlight" << "set-mode" << "set-show-indent" + << "print"; + + return l; +} + +bool KateCommands::CoreCommands::help(KTextEditor::View *, const QString &cmd, QString &msg) +{ + QString realcmd=cmd.trimmed(); + if (realcmd=="indent") { + msg=i18n("

indent

" + "

Indents the selected lines or the current line

"); + return true; + } else if (realcmd=="unindent") { + msg=i18n("

unindent

" + "

Unindents the selected lines or current line.

"); + return true; + } else if (realcmd=="cleanindent") { + msg=i18n("

cleanindent

" + "

Cleans up the indentation of the selected lines or current line according to the indentation settings in the document.

"); + return true; + } else if (realcmd=="comment") { + msg=i18n("

comment

" + "

Inserts comment markers to make the selection or selected lines or current line a comment according to the text format as defined by the syntax highlight definition for the document.

"); + return true; + } else if (realcmd=="uncomment") { + msg=i18n("

uncomment

" + "

Removes comment markers from the selection or selected lines or current line according to the text format as defined by the syntax highlight definition for the document.

"); + return true; + } else if (realcmd=="goto") { + msg=i18n("

goto line number

" + "

This command navigates to the specified line number.

"); + return true; + } else if (realcmd=="set-indent-pasted-text") { + msg=i18n("

set-indent-pasted-text enable

" + "

If enabled, indentation of text pasted from the clipboard is adjusted using the current indenter.

" + "

Possible true values: 1 on true
" + "possible false values: 0 off false

"); + return true; + } else if (realcmd=="kill-line") { + msg=i18n("Deletes the current line."); + return true; + } else if (realcmd=="set-tab-width") { + msg=i18n("

set-tab-width width

" + "

Sets the tab width to the number width

"); + return true; + } else if (realcmd=="set-replace-tab") { + msg=i18n("

set-replace-tab enable

" + "

If enabled, tabs are replaced with spaces as you type.

" + "

Possible true values: 1 on true
" + "possible false values: 0 off false

"); + return true; + } else if (realcmd=="set-show-tabs") { + msg=i18n("

set-show-tabs enable

" + "

If enabled, TAB characters and trailing whitespace will be visualized by a small dot.

" + "

Possible true values: 1 on true
" + "possible false values: 0 off false

"); + return true; + } else if (realcmd=="set-remove-trailing-spaces") { + msg=i18n("

set-remove-trailing-spaces mode

" + "

Removes the trailing spaces in the document depending on the mode.

" + "

Possible values:" + "

    " + "
  • none: never remove trailing spaces.
  • " + "
  • modified: remove trailing spaces only of modified lines.
  • " + "
  • all: remove trailing spaces in the entire document.
  • " + "

"); + return true; + } else if (realcmd=="set-indent-width") { + msg=i18n("

set-indent-width width

" + "

Sets the indentation width to the number width. Used only if you are indenting with spaces.

"); + return true; + } else if (realcmd=="set-indent-mode") { + msg=i18n("

set-indent-mode mode

" + "

The mode parameter is a value as seen in the Tools - Indentation menu

"); + return true; + } else if (realcmd=="set-auto-indent") { + msg=i18n("

set-auto-indent enable

" + "

Enable or disable autoindentation.

" + "

possible true values: 1 on true
" + "possible false values: 0 off false

"); + return true; + } else if (realcmd=="set-line-numbers") { + msg=i18n("

set-line-numbers enable

" + "

Sets the visibility of the line numbers pane.

" + "

possible true values: 1 on true
" + "possible false values: 0 off false

"); + return true; + } else if (realcmd=="set-folding-markers") { + msg=i18n("

set-folding-markers enable

" + "

Sets the visibility of the folding markers pane.

" + "

possible true values: 1 on true
" + "possible false values: 0 off false

"); + return true; + } else if (realcmd=="set-icon-border") { + msg=i18n("

set-icon-border enable

" + "

Sets the visibility of the icon border.

" + "

possible true values: 1 on true
" + "possible false values: 0 off false

"); + return true; + } else if (realcmd=="set-word-wrap") { + msg=i18n("

set-word-wrap enable

" + "

Enables dynamic word wrap according to enable

" + "

possible true values: 1 on true
" + "possible false values: 0 off false

"); + return true; + } else if (realcmd=="set-word-wrap-column") { + msg=i18n("

set-word-wrap-column width

" + "

Sets the line width for hard wrapping to width. This is used if you are having your text wrapped automatically.

"); + return true; + } else if (realcmd=="set-replace-tabs-save") { + msg=i18n("

set-replace-tabs-save enable

" + "

When enabled, tabs will be replaced with whitespace whenever the document is saved.

" + "

possible true values: 1 on true
" + "possible false values: 0 off false

"); + return true; + } else if (realcmd=="set-highlight") { + msg=i18n("

set-highlight highlight

" + "

Sets the syntax highlighting system for the document. The argument must be a valid highlight name, as seen in the Tools → Highlighting menu. This command provides an autocompletion list for its argument.

"); + return true; + } else if (realcmd=="set-mode") { + msg=i18n("

set-mode mode

" + "

Sets the mode as seen in Tools - Mode

"); + return true; + } else if (realcmd=="set-show-indent") { + msg=i18n("

set-show-indent enable

" + "

If enabled, indentation will be visualized by a vertical dotted line.

" + "

possible true values: 1 on true
" + "possible false values: 0 off false

"); + return true; + } else if (realcmd=="print") { + msg=i18n("

Open the Print dialog to print the current document.

"); + return true; + } else + return false; +} + +bool KateCommands::CoreCommands::exec(KTextEditor::View *view, + const QString &_cmd, + QString &errorMsg) +{ + return exec( view, _cmd, errorMsg, KTextEditor::Range::invalid() ); +} + +bool KateCommands::CoreCommands::exec(KTextEditor::View *view, + const QString &_cmd, + QString &errorMsg, + const KTextEditor::Range& range) +{ +#define KCC_ERR(s) { errorMsg=s; return false; } + // cast it hardcore, we know that it is really a kateview :) + KateView *v = static_cast(view); + + if ( ! v ) + KCC_ERR( i18n("Could not access view") ); + + //create a list of args + QStringList args(_cmd.split( QRegExp("\\s+"), QString::SkipEmptyParts)) ; + QString cmd ( args.takeFirst() ); + + // ALL commands that takes no arguments. + if ( cmd == "indent" ) + { + if ( range.isValid() ) { + v->doc()->editStart(); + for ( int line = range.start().line(); line <= range.end().line(); line++ ) { + v->doc()->indent( KTextEditor::Range(line, 0, line, 0), 1 ); + } + v->doc()->editEnd(); + } else { + v->indent(); + } + return true; + } + else if ( cmd == "unindent" ) + { + if ( range.isValid() ) { + v->doc()->editStart(); + for ( int line = range.start().line(); line <= range.end().line(); line++ ) { + v->doc()->indent( KTextEditor::Range(line, 0, line, 0), -1 ); + } + v->doc()->editEnd(); + } else { + v->unIndent(); + } + return true; + } + else if ( cmd == "cleanindent" ) + { + if ( range.isValid() ) { + v->doc()->editStart(); + for ( int line = range.start().line(); line <= range.end().line(); line++ ) { + v->doc()->indent( KTextEditor::Range(line, 0, line, 0), 0 ); + } + v->doc()->editEnd(); + } else { + v->cleanIndent(); + } + return true; + } + else if ( cmd == "fold" ) + { + return (v->textFolding().newFoldingRange (range.isValid() ? range : v->selectionRange(), Kate::TextFolding::Persistent | Kate::TextFolding::Folded) != -1); + } + else if ( cmd == "tfold" ) + { + return (v->textFolding().newFoldingRange (range.isValid() ? range : v->selectionRange(), Kate::TextFolding::Folded) != -1); + } + else if ( cmd == "unfold" ) + { + QVector > startingRanges = v->textFolding().foldingRangesStartingOnLine (v->cursorPosition().line()); + bool unfolded = false; + for (int i = 0; i < startingRanges.size(); ++i) { + if (startingRanges[i].second & Kate::TextFolding::Folded) { + unfolded = v->textFolding().unfoldRange (startingRanges[i].first) || unfolded; + } + } + return unfolded; + } + else if ( cmd == "comment" ) + { + if ( range.isValid() ) { + v->doc()->editStart(); + for ( int line = range.start().line(); line <= range.end().line(); line++ ) { + v->doc()->comment( v, line, 0, 1 ); + } + v->doc()->editEnd(); + } else { + v->comment(); + } + return true; + } + else if ( cmd == "uncomment" ) + { + if ( range.isValid() ) { + v->doc()->editStart(); + for ( int line = range.start().line(); line <= range.end().line(); line++ ) { + v->doc()->comment( v, line, 0, -1 ); + } + v->doc()->editEnd(); + } else { + v->uncomment(); + } + return true; + } + else if ( cmd == "kill-line" ) + { + if ( range.isValid() ) { + v->doc()->editStart(); + for ( int line = range.start().line(); line <= range.end().line(); line++ ) { + v->doc()->removeLine( range.start().line() ); + } + v->doc()->editEnd(); + } else { + v->killLine(); + } + return true; + } + else if ( cmd == "print" ) + { + v->doc()->printDialog(); + return true; + } + + // ALL commands that take a string argument + else if ( cmd == "set-indent-mode" || + cmd == "set-highlight" || + cmd == "set-mode" ) + { + // need at least one item, otherwise args.first() crashes + if ( ! args.count() ) + KCC_ERR( i18n("Missing argument. Usage: %1 ", cmd ) ); + + if ( cmd == "set-indent-mode" ) + { + v->doc()->config()->setIndentationMode( args.join(" ") ); + v->doc()->rememberUserDidSetIndentationMode (); + return true; + } + else if ( cmd == "set-highlight" ) + { + if ( v->doc()->setHighlightingMode( args.first()) ) + { + static_cast(v->doc())->setDontChangeHlOnSave (); + return true; + } + + KCC_ERR( i18n("No such highlighting '%1'", args.first() ) ); + } + else if ( cmd == "set-mode" ) + { + if ( v->doc()->setMode( args.first()) ) + return true; + + KCC_ERR( i18n("No such mode '%1'", args.first() ) ); + } + } + // ALL commands that takes exactly one integer argument. + else if ( cmd == "set-tab-width" || + cmd == "set-indent-width" || + cmd == "set-word-wrap-column" || + cmd == "goto" ) + { + // find a integer value > 0 + if ( ! args.count() ) + KCC_ERR( i18n("Missing argument. Usage: %1 ", cmd ) ); + bool ok; + int val ( args.first().toInt( &ok, 10 ) ); // use base 10 even if the string starts with '0' + if ( !ok ) + KCC_ERR( i18n("Failed to convert argument '%1' to integer.", + args.first() ) ); + + if ( cmd == "set-tab-width" ) + { + if ( val < 1 ) + KCC_ERR( i18n("Width must be at least 1.") ); + v->doc()->config()->setTabWidth( val ); + } + else if ( cmd == "set-indent-width" ) + { + if ( val < 1 ) + KCC_ERR( i18n("Width must be at least 1.") ); + v->doc()->config()->setIndentationWidth( val ); + } + else if ( cmd == "set-word-wrap-column" ) + { + if ( val < 2 ) + KCC_ERR( i18n("Column must be at least 1.") ); + v->doc()->setWordWrapAt( val ); + } + else if ( cmd == "goto" ) + { + if ( args.first().at(0) == '-' || args.first().at(0) == '+' ) { + // if the number starts with a minus or plus sign, add/subract the number + val = v->cursorPosition().line() + val; + } else { + val--; // convert given line number to the internal representation of line numbers + } + + // constrain cursor to the range [0, number of lines] + if ( val < 0 ) { + val = 0; + } else if ( val > v->doc()->lines()-1 ) { + val = v->doc()->lines()-1; + } + + v->setCursorPosition( KTextEditor::Cursor( val, 0 ) ); + return true; + } + return true; + } + + // ALL commands that takes 1 boolean argument. + else if ( cmd == "set-icon-border" || + cmd == "set-folding-markers" || + cmd == "set-indent-pasted-text" || + cmd == "set-line-numbers" || + cmd == "set-replace-tabs" || + cmd == "set-show-tabs" || + cmd == "set-word-wrap" || + cmd == "set-wrap-cursor" || + cmd == "set-replace-tabs-save" || + cmd == "set-show-indent" ) + { + if ( ! args.count() ) + KCC_ERR( i18n("Usage: %1 on|off|1|0|true|false", cmd ) ); + bool enable = false; + KateDocumentConfig * const config = v->doc()->config(); + if ( getBoolArg( args.first(), &enable ) ) + { + if ( cmd == "set-icon-border" ) + v->setIconBorder( enable ); + else if (cmd == "set-folding-markers") + v->setFoldingMarkersOn( enable ); + else if ( cmd == "set-line-numbers" ) + v->setLineNumbersOn( enable ); + else if ( cmd == "set-show-indent" ) + v->renderer()->setShowIndentLines( enable ); + else if (cmd == "set-indent-pasted-text") + config->setIndentPastedText( enable ); + else if ( cmd == "set-replace-tabs" ) + config->setReplaceTabsDyn( enable ); + else if ( cmd == "set-show-tabs" ) + config->setShowTabs( enable ); + else if ( cmd == "set-show-trailing-spaces" ) + config->setShowSpaces( enable ); + else if ( cmd == "set-word-wrap" ) + v->doc()->setWordWrap( enable ); + + return true; + } + else + KCC_ERR( i18n("Bad argument '%1'. Usage: %2 on|off|1|0|true|false", + args.first() , cmd ) ); + } + else if ( cmd == "set-remove-trailing-spaces" ) { + // need at least one item, otherwise args.first() crashes + if ( args.count() != 1 ) + KCC_ERR( i18n("Usage: set-remove-trailing-spaces 0|-|none or 1|+|mod|modified or 2|*|all") ); + + QString tmp = args.first().toLower().trimmed(); + if (tmp == "1" || tmp == "modified" || tmp == "mod" || tmp == "+") { + v->doc()->config()->setRemoveSpaces(1); + } else if (tmp == "2" || tmp == "all" || tmp == "*") { + v->doc()->config()->setRemoveSpaces(2); + } else { + v->doc()->config()->setRemoveSpaces(0); + } + } + + // unlikely.. + KCC_ERR( i18n("Unknown command '%1'", cmd) ); +} + +bool KateCommands::CoreCommands::supportsRange(const QString &range) +{ + static QStringList l; + + if (l.isEmpty()) + l << "indent" << "unindent" << "cleanindent" + << "comment" << "uncomment" << "kill-line" << "fold" << "tfold"; + + return l.contains(range); +} + +KCompletion *KateCommands::CoreCommands::completionObject( KTextEditor::View *view, const QString &cmd ) +{ + Q_UNUSED(view) + + if ( cmd == "set-highlight" ) + { + QStringList l; + for ( int i = 0; i < KateHlManager::self()->highlights(); i++ ) + l << KateHlManager::self()->hlName (i); + + KateCmdShellCompletion *co = new KateCmdShellCompletion(); + co->setItems( l ); + co->setIgnoreCase( true ); + return co; + } + else if ( cmd == "set-remove-trailing-spaces" ) + { + QStringList l; + l << "none" << "modified" << "all"; + + KateCmdShellCompletion *co = new KateCmdShellCompletion(); + co->setItems( l ); + co->setIgnoreCase( true ); + return co; + } + else if ( cmd == "set-indent-mode" ) + { + QStringList l = KateAutoIndent::listIdentifiers(); + KateCmdShellCompletion *co = new KateCmdShellCompletion(); + co->setItems( l ); + co->setIgnoreCase( true ); + return co; + } + + return 0L; +} +//END CoreCommands + +// BEGIN AppCommands +KateCommands::AppCommands* KateCommands::AppCommands::m_instance = 0; + +KateCommands::AppCommands::AppCommands() + : KTextEditor::Command() +{ + re_write.setPattern("w"); // temporarily add :w + //re_write.setPattern("w(a)?"); + //re_quit.setPattern("(w)?q?(a)?"); + //re_exit.setPattern("x(a)?"); + //re_changeBuffer.setPattern("b(n|p)"); + //re_edit.setPattern("e(dit)?"); + //re_new.setPattern("(v)?new"); +} + +const QStringList& KateCommands::AppCommands::cmds() +{ + static QStringList l; + + if (l.empty()) { + //l << "q" << "qa" << "w" << "wq" << "wa" << "wqa" << "x" << "xa" + //<< "bn" << "bp" << "new" << "vnew" << "e" << "edit" << "enew"; + l << "w"; + } + + return l; +} + +// commands that don't need to live in the hosting application should be +// implemented here. things such as quitting and splitting the view can not be +// done from the editor part and needs to be implemented in the hosting +// application. +bool KateCommands::AppCommands::exec(KTextEditor::View *view, + const QString &cmd, QString &msg ) +{ + QStringList args(cmd.split( QRegExp("\\s+"), QString::SkipEmptyParts)) ; + QString command( args.takeFirst() ); + QString file = args.join(QString(' ')); + + if (re_write.exactMatch(command)) { // w, wa + /* if (!re_write.cap(1).isEmpty()) { // [a]ll + view->document()->saveAll(); + msg = i18n("All documents written to disk"); + } else { // w*/ + // Save file + if (file.isEmpty()) { + view->document()->documentSave(); + } else { + KUrl base = view->document()->url(); + KUrl url( base.isValid() ? base : KUrl( QDir::homePath() ), file ); + view->document()->saveAs( url ); + } + msg = i18n("Document written to disk"); + } + + return true; +} + +bool KateCommands::AppCommands::help(KTextEditor::View *view, const QString &cmd, QString &msg) +{ + Q_UNUSED(view); + + if (re_write.exactMatch(cmd)) { + msg = i18n("

w/wa — write document(s) to disk

" + "

Usage: w[a]

" + "

Writes the current document(s) to disk. " + "It can be called in two ways:
" + " w — writes the current document to disk
" + " wa — writes all document to disk.

" + "

If no file name is associated with the document, " + "a file dialog will be shown.

"); + return true; + } + return false; +} +//END AppCommands + +//BEGIN SedReplace +KateCommands::SedReplace* KateCommands::SedReplace::m_instance = 0; + +static int backslashString(const QString &haystack, const QString &needle, int index) +{ + int len = haystack.length(); + int searchlen = needle.length(); + bool evenCount = true; + while (index < len) + { + if (haystack[index] == '\\') + { + evenCount = !evenCount; + } + else + { // isn't a slash + if (!evenCount) + { + if (haystack.mid(index, searchlen) == needle) + return index - 1; + } + evenCount = true; + } + ++index; + + } + + return -1; +} + +// exchange "\t" for the actual tab character, for example +static void exchangeAbbrevs(QString &str) +{ + // the format is (findreplace)*[nullzero] + const char *magic = "a\x07t\tn\n"; + + while (*magic) + { + int index = 0; + char replace = magic[1]; + while ((index = backslashString(str, QString (QChar::fromAscii(*magic)), index)) != -1) + { + str.replace(index, 2, QChar(replace)); + ++index; + } + ++magic; + ++magic; + } +} + +bool KateCommands::SedReplace::exec (KTextEditor::View *view, const QString &cmd, QString &msg) +{ + return exec(view, cmd, msg, KTextEditor::Range::invalid()); +} + +bool KateCommands::SedReplace::exec (class KTextEditor::View *view, const QString &cmd, + QString &msg, const KTextEditor::Range &r) +{ + kDebug(13025) << "SedReplace::execCmd( " << cmd << " )"; + if (r.isValid()) { + kDebug(13025) << "Range: " << r; + } + + int findBeginPos = -1; + int findEndPos = -1; + int replaceBeginPos = -1; + int replaceEndPos = -1; + QString delimiter; + if (!parse(cmd, delimiter, findBeginPos, findEndPos, replaceBeginPos, replaceEndPos)) + { + return false; + } + + const QString searchParamsString = cmd.mid(cmd.lastIndexOf(delimiter)); + const bool noCase = searchParamsString.contains('i'); + const bool repeat = searchParamsString.contains('g'); + const bool interactive = searchParamsString.contains('c'); + + QString find = cmd.mid(findBeginPos, findEndPos - findBeginPos + 1); + kDebug(13025) << "SedReplace: find =" << find; + + QString replace = cmd.mid(replaceBeginPos, replaceEndPos - replaceBeginPos + 1); + exchangeAbbrevs(replace); + kDebug(13025) << "SedReplace: replace =" << replace; + + if (find.isEmpty()) + { + // Nothing to do. + return true; + } + + KateView *kateView = static_cast(view); + KateDocument *doc = kateView->doc(); + if ( !doc ) return false; + // Only current line ... + int startLine = kateView->cursorPosition().line(); + int endLine = kateView->cursorPosition().line(); + // ... unless a range was provided. + if (r.isValid()) + { + startLine = r.start().line(); + endLine = r.end().line(); + } + + QSharedPointer interactiveSedReplacer(new InteractiveSedReplacer(doc, find, replace, !noCase, !repeat, startLine, endLine)); + + if (interactive) + { + kDebug(13025) << "Interactive sedreplace is only currently supported with Vi mode plus Vi emulated command bar."; + return false; + } + + kateView->setSearchPattern(find); + + interactiveSedReplacer->replaceAllRemaining(); + msg = interactiveSedReplacer->finalStatusReportMessage(); + + return true; +} + +bool KateCommands::SedReplace::parse(const QString& sedReplaceString, QString& destDelim, int& destFindBeginPos, int& destFindEndPos, int& destReplaceBeginPos, int& destReplaceEndPos) +{ + // valid delimiters are all non-word, non-space characters plus '_' + QRegExp delim("^s\\s*([^\\w\\s]|_)"); + if ( delim.indexIn( sedReplaceString ) < 0 ) return false; + + QString d = delim.cap(1); + kDebug(13025) << "SedReplace: delimiter is '" << d << "'"; + + QRegExp splitter( QString("^s\\s*") + d + "((?:[^\\\\\\" + d + "]|\\\\.)*)\\" + + d + "((?:[^\\\\\\" + d + "]|\\\\.)*)(\\" + d + "[igc]{0,3})?$" ); + if (splitter.indexIn(sedReplaceString) < 0) return false; + + const QString find = splitter.cap(1); + const QString replace = splitter.cap(2); + + destDelim = d; + destFindBeginPos = splitter.pos(1); + destFindEndPos = splitter.pos(1) + find.length() - 1; + destReplaceBeginPos = splitter.pos(2); + destReplaceEndPos = splitter.pos(2) + replace.length() - 1; + + return true; +} + +KateCommands::SedReplace::InteractiveSedReplacer::InteractiveSedReplacer(KateDocument* doc, const QString& findPattern, const QString& replacePattern, bool caseSensitive, bool onlyOnePerLine, int startLine, int endLine) + : m_findPattern(findPattern), + m_replacePattern(replacePattern), + m_onlyOnePerLine(onlyOnePerLine), + m_endLine(endLine), + m_doc(doc), + m_regExpSearch(doc, caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive), + m_numReplacementsDone(0), + m_numLinesTouched(0), + m_lastChangedLineNum(-1) +{ + m_currentSearchPos = KTextEditor::Cursor(startLine, 0); +} + +KTextEditor::Range KateCommands::SedReplace::InteractiveSedReplacer::currentMatch() +{ + QVector matches = fullCurrentMatch(); + + if (matches.isEmpty()) { + return KTextEditor::Range::invalid(); + } + + if (matches.first().start().line() > m_endLine) { + return KTextEditor::Range::invalid(); + } + + return matches.first(); +} + +void KateCommands::SedReplace::InteractiveSedReplacer::skipCurrentMatch() +{ + const KTextEditor::Range currentMatch = this->currentMatch(); + m_currentSearchPos = currentMatch.end(); + if (m_onlyOnePerLine && currentMatch.start().line() == m_currentSearchPos.line()) + { + m_currentSearchPos = KTextEditor::Cursor(m_currentSearchPos.line() + 1, 0); + } +} + +void KateCommands::SedReplace::InteractiveSedReplacer::replaceCurrentMatch() +{ + const KTextEditor::Range currentMatch = this->currentMatch(); + const QString currentMatchText = m_doc->text(currentMatch); + const QString replacementText = replacementTextForCurrentMatch(); + + m_doc->editBegin(); + m_doc->removeText(currentMatch); + m_doc->insertText(currentMatch.start(), replacementText); + m_doc->editEnd(); + + // Begin next search from directly after replacement. + if (!replacementText.contains('\n')) { + const int moveChar = currentMatch.isEmpty() ? 1 : 0; // if the search was for \s*, make sure we advance a char + const int col = currentMatch.start().column() + replacementText.length() + moveChar; + + m_currentSearchPos = KTextEditor::Cursor(currentMatch.start().line(), col); + } else { + m_currentSearchPos = KTextEditor::Cursor(currentMatch.start().line() + replacementText.count('\n'), + replacementText.length() - replacementText.lastIndexOf('\n') - 1); + } + if (m_onlyOnePerLine) + { + // Drop down to next line. + m_currentSearchPos = KTextEditor::Cursor(m_currentSearchPos.line() + 1, 0); + } + + // Adjust end line down by the number of new newlines just added, minus the number taken away. + m_endLine += replacementText.count('\n'); + m_endLine -= currentMatchText.count('\n'); + + m_numReplacementsDone++; + if (m_lastChangedLineNum != currentMatch.start().line()) + { + // Counting "swallowed" lines as being "touched". + m_numLinesTouched += currentMatchText.count('\n') + 1; + } + m_lastChangedLineNum = m_currentSearchPos.line(); +} + +void KateCommands::SedReplace::InteractiveSedReplacer::replaceAllRemaining() +{ + m_doc->editBegin(); + while (currentMatch().isValid()) + { + replaceCurrentMatch(); + } + m_doc->editEnd(); +} + +QString KateCommands::SedReplace::InteractiveSedReplacer::currentMatchReplacementConfirmationMessage() +{ + return i18n("replace with %1?", replacementTextForCurrentMatch().replace('\n', "\\n")); +} + +QString KateCommands::SedReplace::InteractiveSedReplacer::finalStatusReportMessage() +{ + return i18ncp("%2 is the translation of the next message", + "1 replacement done on %2", "%1 replacements done on %2", m_numReplacementsDone, + i18ncp("substituted into the previous message", + "1 line", "%1 lines", m_numLinesTouched)); + +} + + +const QVector< KTextEditor::Range > KateCommands::SedReplace::InteractiveSedReplacer::fullCurrentMatch() +{ + if (m_currentSearchPos > m_doc->documentEnd()) { + return QVector(); + } + + return m_regExpSearch.search(m_findPattern, KTextEditor::Range(m_currentSearchPos, m_doc->documentEnd())); +} + +QString KateCommands::SedReplace::InteractiveSedReplacer::replacementTextForCurrentMatch() +{ + const KTextEditor::Range currentMatch = this->currentMatch(); + const QVector captureRanges = fullCurrentMatch(); + QStringList captureTexts; + foreach(const KTextEditor::Range& captureRange, captureRanges) + { + captureTexts << m_doc->text(captureRange); + } + const QString replacementText = m_regExpSearch.buildReplacement(m_replacePattern, captureTexts, 0); + return replacementText; + +} + + + +//END SedReplace + +//BEGIN Character +KateCommands::Character* KateCommands::Character::m_instance = 0; + +bool KateCommands::Character::help (class KTextEditor::View *, const QString &cmd, QString &msg) +{ + if (cmd.trimmed()=="char") { + msg = i18n("

char identifier

" + "

This command allows you to insert literal characters by their numerical identifier, in decimal, octal or hexadecimal form.

" + "

Examples:

    " + "
  • char 234
  • " + "
  • char 0x1234
  • " + "

"); + return true; + } + return false; +} + +bool KateCommands::Character::exec (KTextEditor::View *view, const QString &_cmd, QString &) +{ + QString cmd = _cmd; + + // hex, octal, base 9+1 + QRegExp num("^char *(0?x[0-9A-Fa-f]{1,4}|0[0-7]{1,6}|[0-9]{1,5})$"); + if (num.indexIn(cmd)==-1) return false; + + cmd=num.cap(1); + + // identify the base + + unsigned short int number=0; + int base=10; + if (cmd[0]=='x' || cmd.startsWith(QLatin1String("0x"))) + { + cmd.remove(QRegExp("^0?x")); + base=16; + } + else if (cmd[0]=='0') + base=8; + bool ok; + number=cmd.toUShort(&ok, base); + if (!ok || number==0) return false; + if (number<=255) + { + char buf[2]; + buf[0]=(char)number; + buf[1]=0; + + view->document()->insertText(view->cursorPosition(), QString(buf)); + } + else + { // do the unicode thing + QChar c(number); + + view->document()->insertText(view->cursorPosition(), QString(&c, 1)); + } + + return true; +} + +//END Character + +//BEGIN Date +KateCommands::Date* KateCommands::Date::m_instance = 0; + +bool KateCommands::Date::help (class KTextEditor::View *, const QString &cmd, QString &msg) +{ + if (cmd.trimmed()=="date") { + msg = i18n("

date or date format

" + "

Inserts a date/time string as defined by the specified format, or the format yyyy-MM-dd hh:mm:ss if none is specified.

" + "

Possible format specifiers are:" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "
dThe day as number without a leading zero (1-31).
ddThe day as number with a leading zero (01-31).
dddThe abbreviated localized day name (e.g. 'Mon'..'Sun').
ddddThe long localized day name (e.g. 'Monday'..'Sunday').
MThe month as number without a leading zero (1-12).
MMThe month as number with a leading zero (01-12).
MMMThe abbreviated localized month name (e.g. 'Jan'..'Dec').
yyThe year as two digit number (00-99).
yyyyThe year as four digit number (1752-8000).
hThe hour without a leading zero (0..23 or 1..12 if AM/PM display).
hhThe hour with a leading zero (00..23 or 01..12 if AM/PM display).
mThe minute without a leading zero (0..59).
mmThe minute with a leading zero (00..59).
sThe second without a leading zero (0..59).
ssThe second with a leading zero (00..59).
zThe milliseconds without leading zeroes (0..999).
zzzThe milliseconds with leading zeroes (000..999).
APUse AM/PM display. AP will be replaced by either \"AM\" or \"PM\".
apUse am/pm display. ap will be replaced by either \"am\" or \"pm\".

"); + return true; + } + return false; +} + +bool KateCommands::Date::exec (KTextEditor::View *view, const QString &cmd, QString &) +{ + if (!cmd.startsWith(QLatin1String("date"))) + return false; + + if (QDateTime::currentDateTime().toString(cmd.mid(5, cmd.length()-5)).length() > 0) + view->document()->insertText(view->cursorPosition(), QDateTime::currentDateTime().toString(cmd.mid(5, cmd.length()-5))); + else + view->document()->insertText(view->cursorPosition(), QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")); + + return true; +} + +//END Date + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/utils/katecmds.h b/kate/part/utils/katecmds.h new file mode 100644 index 00000000..256057c2 --- /dev/null +++ b/kate/part/utils/katecmds.h @@ -0,0 +1,324 @@ +/* This file is part of the KDE libraries and the Kate part. + * + * Copyright (C) 2003-2005 Anders Lund + * Copyright (C) 2001-2010 Christoph Cullmann + * Copyright (C) 2001 Charles Samuels + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __KATE_CMDS_H__ +#define __KATE_CMDS_H__ + +#include +#include "kateregexpsearch.h" + +#include + +class KateDocument; +class KCompletion; + +/** + * The KateCommands namespace collects subclasses of KTextEditor::Command + * for specific use in kate. + */ +namespace KateCommands +{ + +/** + * This KTextEditor::Command provides access to a lot of the core functionality + * of kate part, settings, utilities, navigation etc. + * it needs to get a kateview pointer, it will cast the kate::view pointer + * hard to kateview + */ +class CoreCommands : public KTextEditor::Command, public KTextEditor::CommandExtension, + public KTextEditor::RangeCommand +{ + CoreCommands() { } + static CoreCommands* m_instance; + + public: + ~CoreCommands() { m_instance = 0; } + + /** + * execute command + * @param view view to use for execution + * @param cmd cmd string + * @param errorMsg error to return if no success + * @return success + */ + bool exec( class KTextEditor::View *view, const QString &cmd, QString &errorMsg ); + + /** + * execute command on given range + * @param view view to use for execution + * @param cmd cmd string + * @param errorMsg error to return if no success + * @param rangeStart first line in range + * @param rangeEnd last line in range + * @return success + */ + bool exec( class KTextEditor::View *view, const QString &cmd, QString &errorMsg, + const KTextEditor::Range &range = KTextEditor::Range(-1, -0, -1, 0) ); + + bool supportsRange(const QString &range); + + /** This command does not have help. @see KTextEditor::Command::help */ + bool help( class KTextEditor::View *, const QString &, QString & ); + + /** + * supported commands as prefixes + * @return prefix list + */ + const QStringList &cmds(); + + /** + * override completionObject from interfaces/document.h . + */ + KCompletion *completionObject( KTextEditor::View *, const QString & ); + + virtual void flagCompletions( QStringList& ) {} + virtual bool wantsToProcessText( const QString & ) { return false; } + virtual void processText( KTextEditor::View *, const QString & ) {} + + static CoreCommands* self() { + if (m_instance == 0) { + m_instance = new CoreCommands(); + } + return m_instance; + } +}; + +/** + * This KTextEditor::Command provides vi commands for the application + */ +class AppCommands : public KTextEditor::Command +{ + AppCommands(); + static AppCommands* m_instance; + + public: + ~AppCommands() { m_instance = 0; } + + /** + * execute command + * @param view view to use for execution + * @param cmd cmd string + * @param msg message returned from running the command + * @return success + */ + bool exec( class KTextEditor::View *view, const QString &cmd, QString &msg ); + + /** Help for AppCommands */ + bool help( class KTextEditor::View *, const QString &, QString & ); + + /** + * supported commands as prefixes + * @return prefix list + */ + const QStringList &cmds(); + + static AppCommands* self() { + if (m_instance == 0) { + m_instance = new AppCommands(); + } + return m_instance; + } + + private: + QRegExp re_write; + /*QRegExp re_quit; + QRegExp re_exit; + QRegExp re_changeBuffer; + QRegExp re_edit; + QRegExp re_new;*/ +}; +/** + * Support vim/sed style search and replace + * @author Charles Samuels + **/ +class SedReplace : public KTextEditor::Command, public KTextEditor::RangeCommand +{ + SedReplace() { } + static SedReplace* m_instance; + + public: + ~SedReplace() { m_instance = 0; } + + /** + * Execute command. Valid command strings are: + * - s/search/replace/ find @c search, replace it with @c replace + * on this line + * - \%s/search/replace/ do the same to the whole file + * - s/search/replace/i do the search and replace case insensitively + * - $s/search/replace/ do the search are replacement to the + * selection only + * + * @note $s/// is currently unsupported + * @param view view to use for execution + * @param cmd cmd string + * @param errorMsg error to return if no success + * @return success + */ + bool exec (class KTextEditor::View *view, const QString &cmd, QString &errorMsg); + + bool exec (class KTextEditor::View *view, const QString &cmd, QString &errorMsg, + const KTextEditor::Range &r); + + bool supportsRange(const QString &) { return true; } + + /** This command does not have help. @see KTextEditor::Command::help */ + bool help (class KTextEditor::View *, const QString &, QString &) { return false; } + + /** + * supported commands as prefixes + * @return prefix list + */ + const QStringList &cmds () { static QStringList l("s"); if (l.isEmpty()) l << "%s" << "$s"; return l; } + + static SedReplace* self() { + if (m_instance == 0) { + m_instance = new SedReplace(); + } + return m_instance; + } + + /** + * Parses @c sedReplaceString to see if it is a valid sed replace expression (e.g. "s/find/replace/gi"). + * If it is, returns true and sets @c delimiter to the delimiter used in the expression; + * @c destFindBeginPos and @c destFindEndPos to the positions in * @c sedReplaceString of the + * begin and end of the "find" term; and @c destReplaceBeginPos and @c destReplaceEndPos to the begin + * and end positions of the "replace" term. + */ + static bool parse(const QString& sedReplaceString, QString& destDelim, int& destFindBeginPos, int& destFindEndPos, int& destReplaceBeginPos, int& destReplaceEndPos); + + class InteractiveSedReplacer + { + public: + InteractiveSedReplacer(KateDocument* doc, const QString& findPattern, const QString& replacePattern, bool caseSensitive, bool onlyOnePerLine, int startLine, int endLine); + /** + * Will return invalid Range if there are no further matches. + */ + KTextEditor::Range currentMatch(); + void skipCurrentMatch(); + void replaceCurrentMatch(); + void replaceAllRemaining(); + QString currentMatchReplacementConfirmationMessage(); + QString finalStatusReportMessage(); + private: + const QString m_findPattern; + const QString m_replacePattern; + bool m_onlyOnePerLine; + int m_endLine; + KateDocument *m_doc; + KateRegExpSearch m_regExpSearch; + + int m_numReplacementsDone; + int m_numLinesTouched; + int m_lastChangedLineNum; + + KTextEditor::Cursor m_currentSearchPos; + const QVector fullCurrentMatch(); + QString replacementTextForCurrentMatch(); + }; + + private: +}; + +/** + * insert a unicode or ascii character + * base 9+1: 1234 + * hex: 0x1234 or x1234 + * octal: 01231 + * + * prefixed with "char:" + **/ +class Character : public KTextEditor::Command +{ + Character() { } + static Character* m_instance; + + public: + ~Character() { m_instance = 0; } + + /** + * execute command + * @param view view to use for execution + * @param cmd cmd string + * @param errorMsg error to return if no success + * @return success + */ + bool exec (class KTextEditor::View *view, const QString &cmd, QString &errorMsg); + + /** This command does not have help. @see KTextEditor::Command::help */ + bool help (class KTextEditor::View *, const QString &, QString &); + + /** + * supported commands as prefixes + * @return prefix list + */ + const QStringList &cmds () { static QStringList test("char"); return test; } + + static Character* self() { + if (m_instance == 0) { + m_instance = new Character(); + } + return m_instance; + } +}; + +/** + * insert the current date/time in the given format + */ +class Date : public KTextEditor::Command +{ + Date() { } + static Date* m_instance; + + public: + ~Date() { m_instance = 0; } + + /** + * execute command + * @param view view to use for execution + * @param cmd cmd string + * @param errorMsg error to return if no success + * @return success + */ + bool exec (class KTextEditor::View *view, const QString &cmd, QString &errorMsg); + + /** This command does not have help. @see KTextEditor::Command::help */ + bool help (class KTextEditor::View *, const QString &, QString &); + + /** + * supported commands as prefixes + * @return prefix list + */ + const QStringList &cmds () { static QStringList test("date"); return test; } + + static Date* self() { + if (m_instance == 0) { + m_instance = new Date(); + } + return m_instance; + } +}; + + +} // namespace KateCommands +#endif + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/utils/kateconfig.cpp b/kate/part/utils/kateconfig.cpp new file mode 100644 index 00000000..dd1fa586 --- /dev/null +++ b/kate/part/utils/kateconfig.cpp @@ -0,0 +1,2733 @@ +/* This file is part of the KDE libraries + Copyright (C) 2007, 2008 Matthew Woehlke + Copyright (C) 2003 Christoph Cullmann + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "kateconfig.h" + +#include "kateglobal.h" +#include "katedefaultcolors.h" +#include "katerenderer.h" +#include "kateview.h" +#include "katedocument.h" +#include "kateschema.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +//BEGIN KateConfig +KateConfig::KateConfig () + : configSessionNumber (0), configIsRunning (false) +{ +} + +KateConfig::~KateConfig () +{ +} + +void KateConfig::configStart () +{ + configSessionNumber++; + + if (configSessionNumber > 1) + return; + + configIsRunning = true; +} + +void KateConfig::configEnd () +{ + if (configSessionNumber == 0) + return; + + configSessionNumber--; + + if (configSessionNumber > 0) + return; + + configIsRunning = false; + + updateConfig (); +} +//END + +//BEGIN KateDocumentConfig +KateGlobalConfig *KateGlobalConfig::s_global = 0; +KateDocumentConfig *KateDocumentConfig::s_global = 0; +KateViewConfig *KateViewConfig::s_global = 0; +KateRendererConfig *KateRendererConfig::s_global = 0; + +KateGlobalConfig::KateGlobalConfig () +{ + s_global = this; + + // init with defaults from config or really hardcoded ones + KConfigGroup cg( KGlobal::config(), "Kate Part Defaults"); + readConfig (cg); +} + +KateGlobalConfig::~KateGlobalConfig () +{ +} + +namespace +{ + const char * const KEY_FALLBACK_ENCODING = "Fallback Encoding"; +} + +void KateGlobalConfig::readConfig (const KConfigGroup &config) +{ + configStart (); + + setFallbackEncoding (config.readEntry(KEY_FALLBACK_ENCODING, "")); + + configEnd (); +} + +void KateGlobalConfig::writeConfig (KConfigGroup &config) +{ + config.writeEntry(KEY_FALLBACK_ENCODING, fallbackEncoding()); +} + +void KateGlobalConfig::updateConfig () +{ +} + +const QString &KateGlobalConfig::fallbackEncoding () const +{ + return m_fallbackEncoding; +} + +QTextCodec *KateGlobalConfig::fallbackCodec () const +{ + if (m_fallbackEncoding.isEmpty()) + return QTextCodec::codecForName("ISO-8859-15"); + + return KGlobal::charsets()->codecForName (m_fallbackEncoding); +} + +bool KateGlobalConfig::setFallbackEncoding (const QString &encoding) +{ + QTextCodec *codec; + bool found = false; + if (encoding.isEmpty()) + { + codec = s_global->fallbackCodec(); + found = true; + } + else + codec = KGlobal::charsets()->codecForName (encoding, found); + + if (!found || !codec) + return false; + + configStart (); + m_fallbackEncoding = codec->name(); + configEnd (); + return true; +} + +KateDocumentConfig::KateDocumentConfig () + : m_indentationWidth (2), + m_tabWidth (8), + m_tabHandling (tabSmart), + m_configFlags (0), + m_wordWrapAt (80), + m_tabWidthSet (false), + m_indentationWidthSet (false), + m_indentationModeSet (false), + m_wordWrapSet (false), + m_wordWrapAtSet (false), + m_pageUpDownMovesCursorSet (false), + m_keepExtraSpacesSet (false), + m_indentPastedTextSet (false), + m_backspaceIndentsSet (false), + m_smartHomeSet (false), + m_showTabsSet (false), + m_showSpacesSet (false), + m_replaceTabsDynSet (false), + m_removeSpacesSet (false), + m_newLineAtEofSet (false), + m_overwiteModeSet (false), + m_tabIndentsSet (false), + m_encodingSet (false), + m_eolSet (false), + m_bomSet (false), + m_allowEolDetectionSet (false), + m_allowSimpleModeSet (false), + m_backupFlagsSet (false), + m_searchDirConfigDepthSet (false), + m_backupPrefixSet (false), + m_backupSuffixSet (false), + m_swapFileNoSyncSet (false), + m_onTheFlySpellCheckSet (false), + m_lineLengthLimitSet (false), + m_doc (0) +{ + s_global = this; + + // init with defaults from config or really hardcoded ones + KConfigGroup cg( KGlobal::config(), "Kate Document Defaults"); + readConfig (cg); +} + +KateDocumentConfig::KateDocumentConfig (const KConfigGroup &cg) + : m_indentationWidth (2), + m_tabWidth (8), + m_tabHandling (tabSmart), + m_configFlags (0), + m_wordWrapAt (80), + m_tabWidthSet (false), + m_indentationWidthSet (false), + m_indentationModeSet (false), + m_wordWrapSet (false), + m_wordWrapAtSet (false), + m_pageUpDownMovesCursorSet (false), + m_keepExtraSpacesSet (false), + m_indentPastedTextSet (false), + m_backspaceIndentsSet (false), + m_smartHomeSet (false), + m_showTabsSet (false), + m_showSpacesSet (false), + m_replaceTabsDynSet (false), + m_removeSpacesSet (false), + m_newLineAtEofSet (false), + m_overwiteModeSet (false), + m_tabIndentsSet (false), + m_encodingSet (false), + m_eolSet (false), + m_bomSet (false), + m_allowEolDetectionSet (false), + m_allowSimpleModeSet (false), + m_backupFlagsSet (false), + m_searchDirConfigDepthSet (false), + m_backupPrefixSet (false), + m_backupSuffixSet (false), + m_swapFileNoSyncSet (false), + m_onTheFlySpellCheckSet (false), + m_lineLengthLimitSet (false), + m_doc (0) +{ + // init with defaults from config or really hardcoded ones + readConfig (cg); +} + +KateDocumentConfig::KateDocumentConfig (KateDocument *doc) + : m_tabHandling (tabSmart), + m_configFlags (0), + m_tabWidthSet (false), + m_indentationWidthSet (false), + m_indentationModeSet (false), + m_wordWrapSet (false), + m_wordWrapAtSet (false), + m_pageUpDownMovesCursorSet (false), + m_keepExtraSpacesSet (false), + m_indentPastedTextSet (false), + m_backspaceIndentsSet (false), + m_smartHomeSet (false), + m_showTabsSet (false), + m_showSpacesSet (false), + m_replaceTabsDynSet (false), + m_removeSpacesSet (false), + m_newLineAtEofSet (false), + m_overwiteModeSet (false), + m_tabIndentsSet (false), + m_encodingSet (false), + m_eolSet (false), + m_bomSet (false), + m_allowEolDetectionSet (false), + m_allowSimpleModeSet (false), + m_backupFlagsSet (false), + m_searchDirConfigDepthSet (false), + m_backupPrefixSet (false), + m_backupSuffixSet (false), + m_swapFileNoSyncSet (false), + m_onTheFlySpellCheckSet (false), + m_lineLengthLimitSet (false), + m_doc (doc) +{ +} + +KateDocumentConfig::~KateDocumentConfig () +{ +} + +namespace +{ + const char * const KEY_TAB_WIDTH = "Tab Width"; + const char * const KEY_INDENTATION_WIDTH = "Indentation Width"; + const char * const KEY_INDENTATION_MODE = "Indentation Mode"; + const char * const KEY_TAB_HANDLING = "Tab Handling"; + const char * const KEY_WORD_WRAP = "Word Wrap"; + const char * const KEY_WORD_WRAP_AT = "Word Wrap Column"; + const char * const KEY_PAGEUP_DOWN_MOVES_CURSOR = "PageUp/PageDown Moves Cursor"; + const char * const KEY_SMART_HOME = "Smart Home"; + const char * const KEY_SHOW_TABS = "Show Tabs"; + const char * const KEY_TAB_INDENTS = "Indent On Tab"; + const char * const KEY_KEEP_EXTRA_SPACES = "Keep Extra Spaces"; + const char * const KEY_INDENT_PASTED_TEXT = "Indent On Text Paste"; + const char * const KEY_BACKSPACE_INDENTS = "Indent On Backspace"; + const char * const KEY_SHOW_SPACES = "Show Spaces"; + const char * const KEY_REPLACE_TABS_DYN = "ReplaceTabsDyn"; + const char * const KEY_REMOVE_SPACES = "Remove Spaces"; + const char * const KEY_NEWLINE_AT_EOF = "Newline At EOF"; + const char * const KEY_OVR = "Overwrite Mode"; + const char * const KEY_ENCODING = "Encoding"; + const char * const KEY_EOL = "End of Line"; + const char * const KEY_ALLOW_EOL_DETECTION = "Allow End of Line Detection"; + const char * const KEY_BOM = "BOM"; + const char * const KEY_ALLOW_SIMPLE_MODE = "Allow Simple Mode"; + const char * const KEY_BACKUP_FLAGS = "Backup Flags"; + const char * const KEY_SEARCH_DIR_CONFIG_DEPTH = "Search Dir Config Depth"; + const char * const KEY_BACKUP_PREFIX = "Backup Prefix"; + const char * const KEY_BACKUP_SUFFIX = "Backup Suffix"; + const char * const KEY_SWAP_FILE_NO_SYNC = "No sync"; + const char * const KEY_ON_THE_FLY_SPELLCHECK = "On-The-Fly Spellcheck"; + const char * const KEY_LINE_LENGTH_LIMIT = "Line Length Limit"; +} + +void KateDocumentConfig::readConfig (const KConfigGroup &config) +{ + configStart (); + + setTabWidth (config.readEntry(KEY_TAB_WIDTH, 8)); + + setIndentationWidth (config.readEntry(KEY_INDENTATION_WIDTH, 2)); + + setIndentationMode (config.readEntry(KEY_INDENTATION_MODE, "normal")); + + setTabHandling (config.readEntry(KEY_TAB_HANDLING, int(KateDocumentConfig::tabSmart))); + + setWordWrap (config.readEntry(KEY_WORD_WRAP, false)); + setWordWrapAt (config.readEntry(KEY_WORD_WRAP_AT, 80)); + setPageUpDownMovesCursor (config.readEntry(KEY_PAGEUP_DOWN_MOVES_CURSOR, false)); + + setSmartHome (config.readEntry(KEY_SMART_HOME, true)); + setShowTabs (config.readEntry(KEY_SHOW_TABS, true)); + setTabIndents (config.readEntry(KEY_TAB_INDENTS, true)); + setKeepExtraSpaces (config.readEntry(KEY_KEEP_EXTRA_SPACES, false)); + setIndentPastedText (config.readEntry(KEY_INDENT_PASTED_TEXT, false)); + setBackspaceIndents (config.readEntry(KEY_BACKSPACE_INDENTS, false)); + setShowSpaces (config.readEntry(KEY_SHOW_SPACES, false)); + setReplaceTabsDyn (config.readEntry(KEY_REPLACE_TABS_DYN, false)); + setRemoveSpaces (config.readEntry(KEY_REMOVE_SPACES, 0)); + setNewLineAtEof (config.readEntry(KEY_NEWLINE_AT_EOF, false)); + setOvr (config.readEntry(KEY_OVR, false)); + + setEncoding (config.readEntry(KEY_ENCODING, "")); + + setEol (config.readEntry(KEY_EOL, 0)); + setAllowEolDetection (config.readEntry(KEY_ALLOW_EOL_DETECTION, true)); + + setBom (config.readEntry(KEY_BOM,false)); + + setAllowSimpleMode (config.readEntry(KEY_ALLOW_SIMPLE_MODE, true)); + + setBackupFlags (config.readEntry(KEY_BACKUP_FLAGS, 0)); + + setSearchDirConfigDepth (config.readEntry(KEY_SEARCH_DIR_CONFIG_DEPTH, 9)); + + setBackupPrefix (config.readEntry(KEY_BACKUP_PREFIX, QString (""))); + + setBackupSuffix (config.readEntry(KEY_BACKUP_SUFFIX, QString ("~"))); + + setSwapFileNoSync (config.readEntry(KEY_SWAP_FILE_NO_SYNC, false)); + + setOnTheFlySpellCheck(config.readEntry(KEY_ON_THE_FLY_SPELLCHECK, false)); + + setLineLengthLimit(config.readEntry(KEY_LINE_LENGTH_LIMIT, 4096)); + + configEnd (); +} + +void KateDocumentConfig::writeConfig (KConfigGroup &config) +{ + config.writeEntry(KEY_TAB_WIDTH, tabWidth()); + + config.writeEntry(KEY_INDENTATION_WIDTH, indentationWidth()); + config.writeEntry(KEY_INDENTATION_MODE, indentationMode()); + + config.writeEntry(KEY_TAB_HANDLING, tabHandling()); + + config.writeEntry(KEY_WORD_WRAP, wordWrap()); + config.writeEntry(KEY_WORD_WRAP_AT, wordWrapAt()); + + config.writeEntry(KEY_PAGEUP_DOWN_MOVES_CURSOR, pageUpDownMovesCursor()); + + config.writeEntry(KEY_SMART_HOME, smartHome()); + config.writeEntry(KEY_SHOW_TABS, showTabs()); + config.writeEntry(KEY_TAB_INDENTS, tabIndentsEnabled()); + config.writeEntry(KEY_KEEP_EXTRA_SPACES, keepExtraSpaces()); + config.writeEntry(KEY_INDENT_PASTED_TEXT, indentPastedText()); + config.writeEntry(KEY_BACKSPACE_INDENTS, backspaceIndents()); + config.writeEntry(KEY_SHOW_SPACES, showSpaces()); + config.writeEntry(KEY_REPLACE_TABS_DYN, replaceTabsDyn()); + config.writeEntry(KEY_REMOVE_SPACES, removeSpaces()); + config.writeEntry(KEY_NEWLINE_AT_EOF, newLineAtEof()); + config.writeEntry(KEY_OVR, ovr()); + + config.writeEntry(KEY_ENCODING, encoding()); + + config.writeEntry(KEY_EOL, eol()); + config.writeEntry(KEY_ALLOW_EOL_DETECTION, allowEolDetection()); + + config.writeEntry(KEY_BOM,bom()); + + config.writeEntry(KEY_ALLOW_SIMPLE_MODE, allowSimpleMode()); + + config.writeEntry(KEY_BACKUP_FLAGS, backupFlags()); + + config.writeEntry(KEY_SEARCH_DIR_CONFIG_DEPTH, searchDirConfigDepth()); + + config.writeEntry(KEY_BACKUP_PREFIX, backupPrefix()); + + config.writeEntry(KEY_BACKUP_SUFFIX, backupSuffix()); + + config.writeEntry(KEY_SWAP_FILE_NO_SYNC, swapFileNoSync()); + + config.writeEntry(KEY_ON_THE_FLY_SPELLCHECK, onTheFlySpellCheck()); + + config.writeEntry(KEY_LINE_LENGTH_LIMIT, lineLengthLimit()); +} + +void KateDocumentConfig::updateConfig () +{ + if (m_doc) + { + m_doc->updateConfig (); + return; + } + + if (isGlobal()) + { + for (int z=0; z < KateGlobal::self()->kateDocuments().size(); ++z) + (KateGlobal::self()->kateDocuments())[z]->updateConfig (); + } +} + +int KateDocumentConfig::tabWidth () const +{ + if (m_tabWidthSet || isGlobal()) + return m_tabWidth; + + return s_global->tabWidth(); +} + +void KateDocumentConfig::setTabWidth (int tabWidth) +{ + if (tabWidth < 1) + return; + + if (m_tabWidthSet && m_tabWidth == tabWidth) + return; + + configStart (); + + m_tabWidthSet = true; + m_tabWidth = tabWidth; + + configEnd (); +} + +int KateDocumentConfig::indentationWidth () const +{ + if (m_indentationWidthSet || isGlobal()) + return m_indentationWidth; + + return s_global->indentationWidth(); +} + +void KateDocumentConfig::setIndentationWidth (int indentationWidth) +{ + if (indentationWidth < 1) + return; + + if (m_indentationWidthSet && m_indentationWidth == indentationWidth) + return; + + configStart (); + + m_indentationWidthSet = true; + m_indentationWidth = indentationWidth; + + configEnd (); +} + +const QString &KateDocumentConfig::indentationMode () const +{ + if (m_indentationModeSet || isGlobal()) + return m_indentationMode; + + return s_global->indentationMode(); +} + +void KateDocumentConfig::setIndentationMode (const QString &indentationMode) +{ + if (m_indentationModeSet && m_indentationMode == indentationMode) + return; + + configStart (); + + m_indentationModeSet = true; + m_indentationMode = indentationMode; + + configEnd (); +} + +uint KateDocumentConfig::tabHandling () const +{ + // This setting is purly a user preference, + // hence, there exists only the global setting. + if (isGlobal()) + return m_tabHandling; + + return s_global->tabHandling(); +} + +void KateDocumentConfig::setTabHandling (uint tabHandling) +{ + configStart (); + + m_tabHandling = tabHandling; + + configEnd (); +} + +bool KateDocumentConfig::wordWrap () const +{ + if (m_wordWrapSet || isGlobal()) + return m_wordWrap; + + return s_global->wordWrap(); +} + +void KateDocumentConfig::setWordWrap (bool on) +{ + if (m_wordWrapSet && m_wordWrap == on) + return; + + configStart (); + + m_wordWrapSet = true; + m_wordWrap = on; + + configEnd (); +} + +int KateDocumentConfig::wordWrapAt () const +{ + if (m_wordWrapAtSet || isGlobal()) + return m_wordWrapAt; + + return s_global->wordWrapAt(); +} + +void KateDocumentConfig::setWordWrapAt (int col) +{ + if (col < 1) + return; + + if (m_wordWrapAtSet && m_wordWrapAt == col) + return; + + configStart (); + + m_wordWrapAtSet = true; + m_wordWrapAt = col; + + configEnd (); +} + +bool KateDocumentConfig::pageUpDownMovesCursor () const +{ + if (m_pageUpDownMovesCursorSet || isGlobal()) + return m_pageUpDownMovesCursor; + + return s_global->pageUpDownMovesCursor(); +} + +void KateDocumentConfig::setPageUpDownMovesCursor (bool on) +{ + if (m_pageUpDownMovesCursorSet && m_pageUpDownMovesCursor == on) + return; + + configStart (); + + m_pageUpDownMovesCursorSet = true; + m_pageUpDownMovesCursor = on; + + configEnd (); +} + +void KateDocumentConfig::setKeepExtraSpaces(bool on) +{ + if (m_keepExtraSpacesSet && m_keepExtraSpaces == on) + return; + + configStart (); + + m_keepExtraSpacesSet = true; + m_keepExtraSpaces = on; + + configEnd (); +} + +bool KateDocumentConfig::keepExtraSpaces() const +{ + if (m_keepExtraSpacesSet || isGlobal()) + return m_keepExtraSpaces; + + return s_global->keepExtraSpaces(); +} + +void KateDocumentConfig::setIndentPastedText(bool on) +{ + if (m_indentPastedTextSet && m_indentPastedText == on) + return; + + configStart (); + + m_indentPastedTextSet = true; + m_indentPastedText = on; + + configEnd (); +} + +bool KateDocumentConfig::indentPastedText() const +{ + if (m_indentPastedTextSet || isGlobal()) + return m_indentPastedText; + + return s_global->indentPastedText(); +} + +void KateDocumentConfig::setBackspaceIndents(bool on) +{ + if (m_backspaceIndentsSet && m_backspaceIndents == on) + return; + + configStart (); + + m_backspaceIndentsSet = true; + m_backspaceIndents = on; + + configEnd (); +} + +bool KateDocumentConfig::backspaceIndents() const +{ + if (m_backspaceIndentsSet || isGlobal()) + return m_backspaceIndents; + + return s_global->backspaceIndents(); +} + +void KateDocumentConfig::setSmartHome(bool on) +{ + if (m_smartHomeSet && m_smartHome == on) + return; + + configStart (); + + m_smartHomeSet = true; + m_smartHome = on; + + configEnd (); +} + +bool KateDocumentConfig::smartHome() const +{ + if (m_smartHomeSet || isGlobal()) + return m_smartHome; + + return s_global->smartHome(); +} + +void KateDocumentConfig::setShowTabs(bool on) +{ + if (m_showTabsSet && m_showTabs == on) + return; + + configStart (); + + m_showTabsSet = true; + m_showTabs = on; + + configEnd (); +} + +bool KateDocumentConfig::showTabs() const +{ + if (m_showTabsSet || isGlobal()) + return m_showTabs; + + return s_global->showTabs(); +} + +void KateDocumentConfig::setShowSpaces(bool on) +{ + if (m_showSpacesSet && m_showSpaces == on) + return; + + configStart (); + + m_showSpacesSet = true; + m_showSpaces = on; + + configEnd (); +} + +bool KateDocumentConfig::showSpaces() const +{ + if (m_showSpacesSet || isGlobal()) + return m_showSpaces; + + return s_global->showSpaces(); +} + +void KateDocumentConfig::setReplaceTabsDyn(bool on) +{ + if (m_replaceTabsDynSet && m_replaceTabsDyn == on) + return; + + configStart (); + + m_replaceTabsDynSet = true; + m_replaceTabsDyn = on; + + configEnd (); +} + +bool KateDocumentConfig::replaceTabsDyn() const +{ + if (m_replaceTabsDynSet || isGlobal()) + return m_replaceTabsDyn; + + return s_global->replaceTabsDyn(); +} + +void KateDocumentConfig::setRemoveSpaces(int triState) +{ + if (m_removeSpacesSet && m_removeSpaces == triState) + return; + + configStart (); + + m_removeSpacesSet = true; + m_removeSpaces = triState; + + configEnd (); +} + +int KateDocumentConfig::removeSpaces() const +{ + if (m_removeSpacesSet || isGlobal()) + return m_removeSpaces; + + return s_global->removeSpaces(); +} + +void KateDocumentConfig::setNewLineAtEof (bool on) +{ + if (m_newLineAtEofSet && m_newLineAtEof == on) + return; + + configStart (); + + m_newLineAtEofSet = true; + m_newLineAtEof = on; + + configEnd (); +} + +bool KateDocumentConfig::newLineAtEof () const +{ + if (m_newLineAtEofSet || isGlobal()) + return m_newLineAtEof; + + return s_global->newLineAtEof(); +} + +void KateDocumentConfig::setOvr(bool on) +{ + if (m_overwiteModeSet && m_overwiteMode == on) + return; + + configStart (); + + m_overwiteModeSet = true; + m_overwiteMode = on; + + configEnd (); +} + +bool KateDocumentConfig::ovr() const +{ + if (m_overwiteModeSet || isGlobal()) + return m_overwiteMode; + + return s_global->ovr(); +} + +void KateDocumentConfig::setTabIndents(bool on) +{ + if (m_tabIndentsSet && m_tabIndents == on) + return; + + configStart (); + + m_tabIndentsSet = true; + m_tabIndents = on; + + configEnd (); +} + +bool KateDocumentConfig::tabIndentsEnabled() const +{ + if (m_tabIndentsSet || isGlobal()) + return m_tabIndents; + + return s_global->tabIndentsEnabled(); +} + +const QString &KateDocumentConfig::encoding () const +{ + if (m_encodingSet || isGlobal()) + return m_encoding; + + return s_global->encoding(); +} + +QTextCodec *KateDocumentConfig::codec () const +{ + if (m_encodingSet || isGlobal()) + { + if (m_encoding.isEmpty() && isGlobal()) + return KGlobal::locale()->codecForEncoding(); + else if (m_encoding.isEmpty()) + return s_global->codec (); + else + return KGlobal::charsets()->codecForName (m_encoding); + } + + return s_global->codec (); +} + +bool KateDocumentConfig::setEncoding (const QString &encoding) +{ + QTextCodec *codec; + bool found = false; + if (encoding.isEmpty()) + { + codec = s_global->codec(); + found=true; + } + else + codec = KGlobal::charsets()->codecForName (encoding, found); + + if (!found || !codec) + return false; + + configStart (); + m_encodingSet = true; + m_encoding = codec->name(); + + if (isGlobal()) + KateGlobal::self()->setDefaultEncoding (m_encoding); + + configEnd (); + return true; +} + +bool KateDocumentConfig::isSetEncoding () const +{ + return m_encodingSet; +} + +int KateDocumentConfig::eol () const +{ + if (m_eolSet || isGlobal()) + return m_eol; + + return s_global->eol(); +} + +QString KateDocumentConfig::eolString () +{ + if (eol() == KateDocumentConfig::eolUnix) + return QString ("\n"); + else if (eol() == KateDocumentConfig::eolDos) + return QString ("\r\n"); + else if (eol() == KateDocumentConfig::eolMac) + return QString ("\r"); + + return QString ("\n"); +} + +void KateDocumentConfig::setEol (int mode) +{ + if (m_eolSet && m_eol == mode) + return; + + configStart (); + + m_eolSet = true; + m_eol = mode; + + configEnd (); +} + +void KateDocumentConfig::setBom (bool bom) +{ + if (m_bomSet && m_bom == bom) + return; + + configStart (); + + m_bomSet = true; + m_bom = bom; + + configEnd (); +} + +bool KateDocumentConfig::bom() const +{ + if (m_bomSet || isGlobal()) + return m_bom; + + return s_global->bom(); +} + +bool KateDocumentConfig::allowEolDetection () const +{ + if (m_allowEolDetectionSet || isGlobal()) + return m_allowEolDetection; + + return s_global->allowEolDetection(); +} + +void KateDocumentConfig::setAllowEolDetection (bool on) +{ + if (m_allowEolDetectionSet && m_allowEolDetection == on) + return; + + configStart (); + + m_allowEolDetectionSet = true; + m_allowEolDetection = on; + + configEnd (); +} + +bool KateDocumentConfig::allowSimpleMode () const +{ + if (m_allowSimpleModeSet || isGlobal()) + return m_allowSimpleMode; + + return s_global->allowSimpleMode(); +} + +void KateDocumentConfig::setAllowSimpleMode (bool on) +{ + if (m_allowSimpleModeSet && m_allowSimpleMode == on) + return; + + configStart (); + + m_allowSimpleModeSet = true; + m_allowSimpleMode = on; + + configEnd (); +} + +uint KateDocumentConfig::backupFlags () const +{ + if (m_backupFlagsSet || isGlobal()) + return m_backupFlags; + + return s_global->backupFlags(); +} + +void KateDocumentConfig::setBackupFlags (uint flags) +{ + if (m_backupFlagsSet && m_backupFlags == flags) + return; + + configStart (); + + m_backupFlagsSet = true; + m_backupFlags = flags; + + configEnd (); +} + +const QString &KateDocumentConfig::backupPrefix () const +{ + if (m_backupPrefixSet || isGlobal()) + return m_backupPrefix; + + return s_global->backupPrefix(); +} + +const QString &KateDocumentConfig::backupSuffix () const +{ + if (m_backupSuffixSet || isGlobal()) + return m_backupSuffix; + + return s_global->backupSuffix(); +} + +void KateDocumentConfig::setBackupPrefix (const QString &prefix) +{ + if (m_backupPrefixSet && m_backupPrefix == prefix) + return; + + configStart (); + + m_backupPrefixSet = true; + m_backupPrefix = prefix; + + configEnd (); +} + +void KateDocumentConfig::setBackupSuffix (const QString &suffix) +{ + if (m_backupSuffixSet && m_backupSuffix == suffix) + return; + + configStart (); + + m_backupSuffixSet = true; + m_backupSuffix = suffix; + + configEnd (); +} + +bool KateDocumentConfig::swapFileNoSync() const +{ + if (m_swapFileNoSyncSet || isGlobal()) + return m_swapFileNoSync; + + return s_global->swapFileNoSync(); +} + +void KateDocumentConfig::setSwapFileNoSync(bool on) +{ + if (m_swapFileNoSyncSet && m_swapFileNoSync == on) + return; + + configStart(); + + m_swapFileNoSyncSet = true; + m_swapFileNoSync = on; + + configEnd(); +} + +int KateDocumentConfig::searchDirConfigDepth () const +{ + if (m_searchDirConfigDepthSet || isGlobal()) + return m_searchDirConfigDepth; + + return s_global->searchDirConfigDepth (); +} + +void KateDocumentConfig::setSearchDirConfigDepth (int depth) +{ + if (m_searchDirConfigDepthSet && m_searchDirConfigDepth == depth) + return; + + configStart (); + + m_searchDirConfigDepthSet = true; + m_searchDirConfigDepth = depth; + + configEnd (); +} + +bool KateDocumentConfig::onTheFlySpellCheck() const +{ + if(isGlobal()) { + // WARNING: this is slightly hackish, but it's currently the only way to + // do it, see also the KTextEdit class + KConfigGroup configGroup(KGlobal::config(), "Spelling"); + return configGroup.readEntry("checkerEnabledByDefault", false); + } + if (m_onTheFlySpellCheckSet) { + return m_onTheFlySpellCheck; + } + + return s_global->onTheFlySpellCheck(); +} + +void KateDocumentConfig::setOnTheFlySpellCheck(bool on) +{ + if (m_onTheFlySpellCheckSet && m_onTheFlySpellCheck == on) + return; + + configStart (); + + m_onTheFlySpellCheckSet = true; + m_onTheFlySpellCheck = on; + + configEnd (); +} + + +int KateDocumentConfig::lineLengthLimit() const +{ + if (m_lineLengthLimitSet || isGlobal()) + return m_lineLengthLimit; + + return s_global->lineLengthLimit(); +} + +void KateDocumentConfig::setLineLengthLimit(int lineLengthLimit) +{ + if (m_lineLengthLimitSet && m_lineLengthLimit == lineLengthLimit) + return; + + configStart(); + + m_lineLengthLimitSet = true; + m_lineLengthLimit = lineLengthLimit; + + configEnd(); +} + + + +//END + +//BEGIN KateViewConfig +KateViewConfig::KateViewConfig () + : + m_dynWordWrapSet (false), + m_dynWordWrapIndicatorsSet (false), + m_dynWordWrapAlignIndentSet (false), + m_lineNumbersSet (false), + m_scrollBarMarksSet (false), + m_scrollBarMiniMapSet (false), + m_scrollBarMiniMapAllSet (false), + m_scrollBarMiniMapWidthSet (false), + m_showScrollbarsSet (false), + m_iconBarSet (false), + m_foldingBarSet (false), + m_lineModificationSet (false), + m_bookmarkSortSet (false), + m_autoCenterLinesSet (false), + m_searchFlagsSet (false), + m_defaultMarkTypeSet (false), + m_persistentSelectionSet (false), + m_viInputModeSet (false), + m_viInputModeStealKeysSet (false), + m_viRelativeLineNumbersSet (false), + m_viInputModeEmulateCommandBarSet(false), + m_automaticCompletionInvocationSet (false), + m_wordCompletionSet (false), + m_keywordCompletionSet (false), + m_wordCompletionMinimalWordLengthSet (false), + m_smartCopyCutSet (false), + m_scrollPastEndSet (false), + m_allowMarkMenu (true), + m_wordCompletionRemoveTailSet (false), + m_foldFirstLineSet (false), + m_view (0) +{ + s_global = this; + + // init with defaults from config or really hardcoded ones + KConfigGroup config( KGlobal::config(), "Kate View Defaults"); + readConfig (config); +} + +KateViewConfig::KateViewConfig (KateView *view) + : + m_searchFlags (PowerModePlainText), + m_maxHistorySize (100), + m_dynWordWrapSet (false), + m_dynWordWrapIndicatorsSet (false), + m_dynWordWrapAlignIndentSet (false), + m_lineNumbersSet (false), + m_scrollBarMarksSet (false), + m_scrollBarMiniMapSet (false), + m_scrollBarMiniMapAllSet (false), + m_scrollBarMiniMapWidthSet (false), + m_showScrollbarsSet (false), + m_iconBarSet (false), + m_foldingBarSet (false), + m_lineModificationSet (false), + m_bookmarkSortSet (false), + m_autoCenterLinesSet (false), + m_searchFlagsSet (false), + m_defaultMarkTypeSet (false), + m_persistentSelectionSet (false), + m_viInputModeSet (false), + m_viInputModeStealKeysSet (false), + m_viRelativeLineNumbersSet (false), + m_viInputModeEmulateCommandBarSet(false), + m_automaticCompletionInvocationSet (false), + m_wordCompletionSet (false), + m_keywordCompletionSet (false), + m_wordCompletionMinimalWordLengthSet (false), + m_smartCopyCutSet (false), + m_scrollPastEndSet (false), + m_allowMarkMenu (true), + m_wordCompletionRemoveTailSet (false), + m_foldFirstLineSet (false), + m_view (view) +{ +} + +KateViewConfig::~KateViewConfig () +{ +} + +namespace +{ + const char * const KEY_SEARCH_REPLACE_FLAGS = "Search/Replace Flags"; + const char * const KEY_PATTERN_HISTORY = "Search Pattern History"; + const char * const KEY_REPLACEMENT_HISTORY = "Replacement Text History"; + const char * const KEY_DYN_WORD_WRAP = "Dynamic Word Wrap"; + const char * const KEY_DYN_WORD_WRAP_INDICATORS = "Dynamic Word Wrap Indicators"; + const char * const KEY_DYN_WORD_WRAP_ALIGN_INDENT = "Dynamic Word Wrap Align Indent"; + const char * const KEY_LINE_NUMBERS = "Line Numbers"; + const char * const KEY_SCROLL_BAR_MARKS = "Scroll Bar Marks"; + const char * const KEY_SCROLL_BAR_MINI_MAP = "Scroll Bar Mini Map"; + const char * const KEY_SCROLL_BAR_MINI_MAP_ALL = "Scroll Bar Mini Map All"; + const char * const KEY_SCROLL_BAR_MINI_MAP_WIDTH = "Scroll Bar Mini Map Width"; + const char * const KEY_SHOW_SCROLLBARS = "Show Scrollbars"; + const char * const KEY_ICON_BAR = "Icon Bar"; + const char * const KEY_FOLDING_BAR = "Folding Bar"; + const char * const KEY_LINE_MODIFICATION = "Line Modification"; + const char * const KEY_BOOKMARK_SORT = "Bookmark Menu Sorting"; + const char * const KEY_AUTO_CENTER_LINES = "Auto Center Lines"; + const char * const KEY_MAX_HISTORY_SIZE = "Maximum Search History Size"; + const char * const KEY_DEFAULT_MARK_TYPE = "Default Mark Type"; + const char * const KEY_ALLOW_MARK_MENU = "Allow Mark Menu"; + const char * const KEY_PERSISTENT_SELECTION = "Persistent Selection"; + const char * const KEY_VI_INPUT_MODE = "Vi Input Mode"; + const char * const KEY_VI_INPUT_MODE_STEAL_KEYS = "Vi Input Mode Steal Keys"; + const char * const KEY_VI_RELATIVE_LINE_NUMBERS = "Vi Relative Line Numbers"; + const char * const KEY_VI_INPUT_MODE_EMULATE_COMMAND_BAR = "Vi Input Mode Emulate Command Bar"; + const char * const KEY_AUTOMATIC_COMPLETION_INVOCATION = "Auto Completion"; + const char * const KEY_WORD_COMPLETION = "Word Completion"; + const char * const KEY_KEYWORD_COMPLETION = "Keyword Completion"; + const char * const KEY_WORD_COMPLETION_MINIMAL_WORD_LENGTH = "Word Completion Minimal Word Length"; + const char * const KEY_WORD_COMPLETION_REMOVE_TAIL = "Word Completion Remove Tail"; + const char * const KEY_SMART_COPY_CUT = "Smart Copy Cut"; + const char * const KEY_SCROLL_PAST_END = "Scroll Past End"; + const char * const KEY_FOLD_FIRST_LINE = "Fold First Line"; +} + +void KateViewConfig::readConfig ( const KConfigGroup &config) +{ + configStart (); + + // default off again, until this is usable for large size documents + setDynWordWrap (config.readEntry( KEY_DYN_WORD_WRAP, false )); + setDynWordWrapIndicators (config.readEntry( KEY_DYN_WORD_WRAP_INDICATORS, 1 )); + setDynWordWrapAlignIndent (config.readEntry( KEY_DYN_WORD_WRAP_ALIGN_INDENT, 80 )); + + setLineNumbers (config.readEntry( KEY_LINE_NUMBERS, false)); + + setScrollBarMarks (config.readEntry( KEY_SCROLL_BAR_MARKS, false)); + + setScrollBarMiniMap (config.readEntry( KEY_SCROLL_BAR_MINI_MAP, false)); + + setScrollBarMiniMapAll (config.readEntry( KEY_SCROLL_BAR_MINI_MAP_ALL, false)); + + setScrollBarMiniMapWidth (config.readEntry( KEY_SCROLL_BAR_MINI_MAP_WIDTH, 60)); + + setShowScrollbars (config.readEntry( KEY_SHOW_SCROLLBARS, static_cast(AlwaysOn))); + + setIconBar (config.readEntry( KEY_ICON_BAR, false )); + + setFoldingBar (config.readEntry( KEY_FOLDING_BAR, true)); + + setLineModification (config.readEntry( KEY_LINE_MODIFICATION, false)); + + setBookmarkSort (config.readEntry( KEY_BOOKMARK_SORT, 0 )); + + setAutoCenterLines (config.readEntry( KEY_AUTO_CENTER_LINES, 0 )); + + setSearchFlags(config.readEntry(KEY_SEARCH_REPLACE_FLAGS, + IncFromCursor|PowerMatchCase|PowerModePlainText)); + + m_maxHistorySize = config.readEntry(KEY_MAX_HISTORY_SIZE, 100); + + setDefaultMarkType (config.readEntry( KEY_DEFAULT_MARK_TYPE, int(KTextEditor::MarkInterface::markType01) )); + setAllowMarkMenu (config.readEntry( KEY_ALLOW_MARK_MENU, true )); + + setPersistentSelection (config.readEntry( KEY_PERSISTENT_SELECTION, false )); + + setViInputMode (config.readEntry( KEY_VI_INPUT_MODE, false)); + setViInputModeStealKeys (config.readEntry( KEY_VI_INPUT_MODE_STEAL_KEYS, false)); + setViRelativeLineNumbers(config.readEntry( KEY_VI_RELATIVE_LINE_NUMBERS, false)); + setViInputModeEmulateCommandBar (config.readEntry( KEY_VI_INPUT_MODE_EMULATE_COMMAND_BAR, false)); + + setAutomaticCompletionInvocation (config.readEntry( KEY_AUTOMATIC_COMPLETION_INVOCATION, true )); + setWordCompletion (config.readEntry( KEY_WORD_COMPLETION, true )); + setKeywordCompletion (config.readEntry( KEY_KEYWORD_COMPLETION, true )); + setWordCompletionMinimalWordLength (config.readEntry( KEY_WORD_COMPLETION_MINIMAL_WORD_LENGTH, 3 )); + setWordCompletionRemoveTail (config.readEntry( KEY_WORD_COMPLETION_REMOVE_TAIL, true )); + setSmartCopyCut (config.readEntry( KEY_SMART_COPY_CUT, false )); + setScrollPastEnd (config.readEntry( KEY_SCROLL_PAST_END, false )); + setFoldFirstLine (config.readEntry( KEY_FOLD_FIRST_LINE, false )); + + if (isGlobal()) { + // Read search pattern history + QStringList patternHistory = config.readEntry(KEY_PATTERN_HISTORY, QStringList()); + m_patternHistoryModel.setStringList(patternHistory); + + // Read replacement text history + QStringList replacementHistory = config.readEntry(KEY_REPLACEMENT_HISTORY, QStringList()); + m_replacementHistoryModel.setStringList(replacementHistory); + } + + configEnd (); +} + +void KateViewConfig::writeConfig (KConfigGroup &config) +{ + config.writeEntry( KEY_DYN_WORD_WRAP, dynWordWrap() ); + config.writeEntry( KEY_DYN_WORD_WRAP_INDICATORS, dynWordWrapIndicators() ); + config.writeEntry( KEY_DYN_WORD_WRAP_ALIGN_INDENT, dynWordWrapAlignIndent() ); + + config.writeEntry( KEY_LINE_NUMBERS, lineNumbers() ); + + config.writeEntry( KEY_SCROLL_BAR_MARKS, scrollBarMarks() ); + + config.writeEntry( KEY_SCROLL_BAR_MINI_MAP, scrollBarMiniMap() ); + + config.writeEntry( KEY_SCROLL_BAR_MINI_MAP_ALL, scrollBarMiniMapAll() ); + + config.writeEntry( KEY_SCROLL_BAR_MINI_MAP_WIDTH, scrollBarMiniMapWidth() ); + + config.writeEntry( KEY_SHOW_SCROLLBARS, showScrollbars() ); + + config.writeEntry( KEY_ICON_BAR, iconBar() ); + + config.writeEntry( KEY_FOLDING_BAR, foldingBar() ); + + config.writeEntry( KEY_LINE_MODIFICATION, lineModification() ); + + config.writeEntry( KEY_BOOKMARK_SORT, bookmarkSort() ); + + config.writeEntry( KEY_AUTO_CENTER_LINES, autoCenterLines() ); + + config.writeEntry(KEY_SEARCH_REPLACE_FLAGS, int(searchFlags())); + + config.writeEntry(KEY_MAX_HISTORY_SIZE, m_maxHistorySize); + + config.writeEntry(KEY_DEFAULT_MARK_TYPE, defaultMarkType()); + + config.writeEntry(KEY_ALLOW_MARK_MENU, allowMarkMenu()); + + config.writeEntry(KEY_PERSISTENT_SELECTION, persistentSelection()); + + config.writeEntry( KEY_AUTOMATIC_COMPLETION_INVOCATION, automaticCompletionInvocation()); + config.writeEntry( KEY_WORD_COMPLETION, wordCompletion()); + config.writeEntry( KEY_KEYWORD_COMPLETION, keywordCompletion()); + config.writeEntry( KEY_WORD_COMPLETION_MINIMAL_WORD_LENGTH, wordCompletionMinimalWordLength()); + config.writeEntry( KEY_WORD_COMPLETION_REMOVE_TAIL, wordCompletionRemoveTail()); + + config.writeEntry( KEY_SMART_COPY_CUT, smartCopyCut() ); + config.writeEntry( KEY_SCROLL_PAST_END , scrollPastEnd() ); + config.writeEntry( KEY_FOLD_FIRST_LINE, foldFirstLine() ); + + config.writeEntry( KEY_VI_INPUT_MODE, viInputMode()); + config.writeEntry( KEY_VI_INPUT_MODE_STEAL_KEYS, viInputModeStealKeys()); + config.writeEntry( KEY_VI_RELATIVE_LINE_NUMBERS, viRelativeLineNumbers()); + config.writeEntry( KEY_VI_INPUT_MODE_EMULATE_COMMAND_BAR, viInputModeEmulateCommandBar()); + + + if (isGlobal()) { + // Write search pattern history + config.writeEntry(KEY_PATTERN_HISTORY, m_patternHistoryModel.stringList()); + + // Write replacement text history + config.writeEntry(KEY_REPLACEMENT_HISTORY, m_replacementHistoryModel.stringList()); + } +} + +void KateViewConfig::updateConfig () +{ + if (m_view) + { + m_view->updateConfig (); + return; + } + + if (isGlobal()) + { + for (int z=0; z < KateGlobal::self()->views().size(); ++z) + (KateGlobal::self()->views())[z]->updateConfig (); + } +} + +bool KateViewConfig::dynWordWrap () const +{ + if (m_dynWordWrapSet || isGlobal()) + return m_dynWordWrap; + + return s_global->dynWordWrap(); +} + +void KateViewConfig::setDynWordWrap (bool wrap) +{ + if (m_dynWordWrapSet && m_dynWordWrap == wrap) + return; + + configStart (); + + m_dynWordWrapSet = true; + m_dynWordWrap = wrap; + + configEnd (); +} + +int KateViewConfig::dynWordWrapIndicators () const +{ + if (m_dynWordWrapIndicatorsSet || isGlobal()) + return m_dynWordWrapIndicators; + + return s_global->dynWordWrapIndicators(); +} + +void KateViewConfig::setDynWordWrapIndicators (int mode) +{ + if (m_dynWordWrapIndicatorsSet && m_dynWordWrapIndicators == mode) + return; + + configStart (); + + m_dynWordWrapIndicatorsSet = true; + m_dynWordWrapIndicators = qBound(0, mode, 80); + + configEnd (); +} + +int KateViewConfig::dynWordWrapAlignIndent () const +{ + if (m_dynWordWrapAlignIndentSet || isGlobal()) + return m_dynWordWrapAlignIndent; + + return s_global->dynWordWrapAlignIndent(); +} + +void KateViewConfig::setDynWordWrapAlignIndent (int indent) +{ + if (m_dynWordWrapAlignIndentSet && m_dynWordWrapAlignIndent == indent) + return; + + configStart (); + + m_dynWordWrapAlignIndentSet = true; + m_dynWordWrapAlignIndent = indent; + + configEnd (); +} + +bool KateViewConfig::lineNumbers () const +{ + if (m_lineNumbersSet || isGlobal()) + return m_lineNumbers; + + return s_global->lineNumbers(); +} + +void KateViewConfig::setLineNumbers (bool on) +{ + if (m_lineNumbersSet && m_lineNumbers == on) + return; + + configStart (); + + m_lineNumbersSet = true; + m_lineNumbers = on; + + configEnd (); +} + +bool KateViewConfig::scrollBarMarks () const +{ + if (m_scrollBarMarksSet || isGlobal()) + return m_scrollBarMarks; + + return s_global->scrollBarMarks(); +} + +void KateViewConfig::setScrollBarMarks (bool on) +{ + if (m_scrollBarMarksSet && m_scrollBarMarks == on) + return; + + configStart (); + + m_scrollBarMarksSet = true; + m_scrollBarMarks = on; + + configEnd (); +} + +bool KateViewConfig::scrollBarMiniMap () const +{ + if (m_scrollBarMiniMapSet || isGlobal()) + return m_scrollBarMiniMap; + + return s_global->scrollBarMiniMap(); +} + +void KateViewConfig::setScrollBarMiniMap (bool on) +{ + if (m_scrollBarMiniMapSet && m_scrollBarMiniMap == on) + return; + + configStart (); + + m_scrollBarMiniMapSet = true; + m_scrollBarMiniMap = on; + + configEnd (); +} + +bool KateViewConfig::scrollBarMiniMapAll () const +{ + if (m_scrollBarMiniMapAllSet || isGlobal()) + return m_scrollBarMiniMapAll; + + return s_global->scrollBarMiniMapAll(); +} + +void KateViewConfig::setScrollBarMiniMapAll (bool on) +{ + if (m_scrollBarMiniMapAllSet && m_scrollBarMiniMapAll == on) + return; + + configStart (); + + m_scrollBarMiniMapAllSet = true; + m_scrollBarMiniMapAll = on; + + configEnd (); +} + +int KateViewConfig::scrollBarMiniMapWidth () const +{ + if (m_scrollBarMiniMapWidthSet || isGlobal()) + return m_scrollBarMiniMapWidth; + + return s_global->scrollBarMiniMapWidth(); +} + +void KateViewConfig::setScrollBarMiniMapWidth (int width) +{ + if (m_scrollBarMiniMapWidthSet && m_scrollBarMiniMapWidth == width) + return; + + configStart (); + + m_scrollBarMiniMapWidthSet = true; + m_scrollBarMiniMapWidth = width; + + configEnd (); +} + +int KateViewConfig::showScrollbars() const +{ + if (m_showScrollbarsSet || isGlobal()) + return m_showScrollbars; + + return s_global->showScrollbars(); +} + +void KateViewConfig::setShowScrollbars(int mode) +{ + if (m_showScrollbarsSet && m_showScrollbars == mode) + return; + + configStart (); + + m_showScrollbarsSet = true; + m_showScrollbars = qBound(0, mode, 80); + + configEnd (); +} + +bool KateViewConfig::iconBar () const +{ + if (m_iconBarSet || isGlobal()) + return m_iconBar; + + return s_global->iconBar(); +} + +void KateViewConfig::setIconBar (bool on) +{ + if (m_iconBarSet && m_iconBar == on) + return; + + configStart (); + + m_iconBarSet = true; + m_iconBar = on; + + configEnd (); +} + +bool KateViewConfig::foldingBar () const +{ + if (m_foldingBarSet || isGlobal()) + return m_foldingBar; + + return s_global->foldingBar(); +} + +void KateViewConfig::setFoldingBar (bool on) +{ + if (m_foldingBarSet && m_foldingBar == on) + return; + + configStart (); + + m_foldingBarSet = true; + m_foldingBar = on; + + configEnd (); +} + +bool KateViewConfig::lineModification () const +{ + if (m_lineModificationSet || isGlobal()) + return m_lineModification; + + return s_global->lineModification(); +} + +void KateViewConfig::setLineModification (bool on) +{ + if (m_lineModificationSet && m_lineModification == on) + return; + + configStart (); + + m_lineModificationSet = true; + m_lineModification = on; + + configEnd (); +} + +int KateViewConfig::bookmarkSort () const +{ + if (m_bookmarkSortSet || isGlobal()) + return m_bookmarkSort; + + return s_global->bookmarkSort(); +} + +void KateViewConfig::setBookmarkSort (int mode) +{ + if (m_bookmarkSortSet && m_bookmarkSort == mode) + return; + + configStart (); + + m_bookmarkSortSet = true; + m_bookmarkSort = mode; + + configEnd (); +} + +int KateViewConfig::autoCenterLines () const +{ + if (m_autoCenterLinesSet || isGlobal()) + return m_autoCenterLines; + + return s_global->autoCenterLines(); +} + +void KateViewConfig::setAutoCenterLines (int lines) +{ + if (lines < 0) + return; + + if (m_autoCenterLinesSet && m_autoCenterLines == lines) + return; + + configStart (); + + m_autoCenterLinesSet = true; + m_autoCenterLines = lines; + + configEnd (); +} + +long KateViewConfig::searchFlags () const +{ + if (m_searchFlagsSet || isGlobal()) + return m_searchFlags; + + return s_global->searchFlags(); +} + +void KateViewConfig::setSearchFlags (long flags) +{ + if (m_searchFlagsSet && m_searchFlags == flags) + return; + + configStart (); + + m_searchFlagsSet = true; + m_searchFlags = flags; + + configEnd (); +} + +QStringListModel *KateViewConfig::patternHistoryModel() +{ + // always return global history + if (isGlobal()) + return &m_patternHistoryModel; + + return s_global->patternHistoryModel(); +} + +int KateViewConfig::maxHistorySize() const +{ + return m_maxHistorySize; +} + +QStringListModel *KateViewConfig::replacementHistoryModel() +{ + // always return global history + if (isGlobal()) + return &m_replacementHistoryModel; + + return s_global->replacementHistoryModel(); +} + +uint KateViewConfig::defaultMarkType () const +{ + if (m_defaultMarkTypeSet || isGlobal()) + return m_defaultMarkType; + + return s_global->defaultMarkType(); +} + +void KateViewConfig::setDefaultMarkType (uint type) +{ + if (m_defaultMarkTypeSet && m_defaultMarkType == type) + return; + + configStart (); + + m_defaultMarkTypeSet = true; + m_defaultMarkType = type; + + configEnd (); +} + +bool KateViewConfig::allowMarkMenu() const +{ + return m_allowMarkMenu; +} + +void KateViewConfig::setAllowMarkMenu (bool allow) +{ + m_allowMarkMenu = allow; +} + +bool KateViewConfig::persistentSelection () const +{ + if (m_persistentSelectionSet || isGlobal()) + return m_persistentSelection; + + return s_global->persistentSelection(); +} + +void KateViewConfig::setPersistentSelection (bool on) +{ + if (m_persistentSelectionSet && m_persistentSelection == on) + return; + + configStart (); + + m_persistentSelectionSet = true; + m_persistentSelection = on; + + configEnd (); +} + +bool KateViewConfig::viInputMode () const +{ + if (m_viInputModeSet || isGlobal()) + return m_viInputMode; + + return s_global->viInputMode(); +} + +void KateViewConfig::setViInputMode (bool on) +{ + if (m_viInputModeSet && m_viInputMode == on) + return; + + configStart (); + + m_viInputModeSet = true; + m_viInputMode = on; + + // make sure to turn off edits mergin when leaving vi input mode + if (!on && m_view) { + m_view->doc()->setUndoMergeAllEdits(false); + } + + configEnd (); +} + +bool KateViewConfig::viInputModeStealKeys () const +{ + if (m_viInputModeStealKeysSet || isGlobal()) + return m_viInputModeStealKeys; + + return s_global->viInputModeStealKeys(); +} + +void KateViewConfig::setViInputModeStealKeys (bool on) +{ + if (m_viInputModeStealKeysSet && m_viInputModeStealKeys == on) + return; + + configStart (); + m_viInputModeStealKeysSet = true; + m_viInputModeStealKeys = on; + configEnd (); +} + +bool KateViewConfig::viRelativeLineNumbers() const +{ + if (m_viRelativeLineNumbersSet || isGlobal()) + return m_viRelativeLineNumbers; + + return s_global->viRelativeLineNumbers(); +} + +void KateViewConfig::setViRelativeLineNumbers(bool on) +{ + if (m_viRelativeLineNumbersSet && m_viRelativeLineNumbers == on) + return; + + configStart(); + m_viRelativeLineNumbersSet = true; + m_viRelativeLineNumbers = on; + configEnd(); +} + +bool KateViewConfig::viInputModeEmulateCommandBar() const +{ + if (m_viInputModeEmulateCommandBarSet || isGlobal()) + return m_viInputModeEmulateCommandBar; + + return s_global->viInputModeEmulateCommandBar(); +} + +void KateViewConfig::setViInputModeEmulateCommandBar(bool on) +{ + if (m_viInputModeEmulateCommandBarSet && m_viInputModeEmulateCommandBar == on) + return; + + configStart(); + m_viInputModeEmulateCommandBarSet = true; + m_viInputModeEmulateCommandBar = on; + configEnd(); +} + +bool KateViewConfig::automaticCompletionInvocation () const +{ + if (m_automaticCompletionInvocationSet || isGlobal()) + return m_automaticCompletionInvocation; + + return s_global->automaticCompletionInvocation(); +} + +void KateViewConfig::setAutomaticCompletionInvocation (bool on) +{ + if (m_automaticCompletionInvocationSet && m_automaticCompletionInvocation == on) + return; + + configStart (); + m_automaticCompletionInvocationSet = true; + m_automaticCompletionInvocation = on; + configEnd (); +} + +bool KateViewConfig::wordCompletion () const +{ + if (m_wordCompletionSet || isGlobal()) + return m_wordCompletion; + + return s_global->wordCompletion(); +} + +void KateViewConfig::setWordCompletion (bool on) +{ + if (m_wordCompletionSet && m_wordCompletion == on) + return; + + configStart (); + m_wordCompletionSet = true; + m_wordCompletion = on; + configEnd (); +} + +bool KateViewConfig::keywordCompletion() const +{ + if (m_keywordCompletionSet || isGlobal()) + return m_keywordCompletion; + return s_global->keywordCompletion(); +} + +void KateViewConfig::setKeywordCompletion(bool on) +{ + if (m_keywordCompletionSet && m_keywordCompletion == on) + return; + configStart(); + m_keywordCompletionSet = true; + m_keywordCompletion = on; + configEnd(); +} + +int KateViewConfig::wordCompletionMinimalWordLength () const +{ + if (m_wordCompletionMinimalWordLengthSet || isGlobal()) + return m_wordCompletionMinimalWordLength; + + return s_global->wordCompletionMinimalWordLength(); +} + +void KateViewConfig::setWordCompletionMinimalWordLength (int length) +{ + if (m_wordCompletionMinimalWordLengthSet && m_wordCompletionMinimalWordLength == length) + return; + + configStart (); + + m_wordCompletionMinimalWordLengthSet = true; + m_wordCompletionMinimalWordLength = length; + + configEnd (); +} + +bool KateViewConfig::wordCompletionRemoveTail () const +{ + if (m_wordCompletionRemoveTailSet || isGlobal()) + return m_wordCompletionRemoveTail; + + return s_global->wordCompletionRemoveTail(); +} + +void KateViewConfig::setWordCompletionRemoveTail (bool on) +{ + if (m_wordCompletionRemoveTailSet && m_wordCompletionRemoveTail == on) + return; + + configStart (); + m_wordCompletionRemoveTailSet = true; + m_wordCompletionRemoveTail = on; + configEnd (); +} + +bool KateViewConfig::smartCopyCut () const +{ + if (m_smartCopyCutSet || isGlobal()) + return m_smartCopyCut; + + return s_global->smartCopyCut(); +} + +void KateViewConfig::setSmartCopyCut (bool on) +{ + if (m_smartCopyCutSet && m_smartCopyCut == on) + return; + + configStart (); + + m_smartCopyCutSet = true; + m_smartCopyCut = on; + + configEnd (); +} + +bool KateViewConfig::scrollPastEnd () const +{ + if (m_scrollPastEndSet || isGlobal()) + return m_scrollPastEnd; + + return s_global->scrollPastEnd(); +} + +void KateViewConfig::setScrollPastEnd (bool on) +{ + if (m_scrollPastEndSet && m_scrollPastEnd == on) + return; + + configStart (); + + m_scrollPastEndSet = true; + m_scrollPastEnd = on; + + configEnd (); +} + +bool KateViewConfig::foldFirstLine() const +{ + if (m_foldFirstLineSet || isGlobal()) + return m_foldFirstLine; + + return s_global->foldFirstLine(); +} + +void KateViewConfig::setFoldFirstLine(bool on) +{ + if (m_foldFirstLineSet && m_foldFirstLine == on) + return; + + configStart (); + + m_foldFirstLineSet = true; + m_foldFirstLine = on; + + configEnd (); +} + +//END + +//BEGIN KateRendererConfig +KateRendererConfig::KateRendererConfig () + : m_fontMetrics(QFont()), + m_lineMarkerColor (KTextEditor::MarkInterface::reservedMarkersCount()), + m_wordWrapMarker (false), + m_showIndentationLines (false), + m_showWholeBracketExpression (false), + m_animateBracketMatching (false), + m_schemaSet (false), + m_fontSet (false), + m_wordWrapMarkerSet (false), + m_showIndentationLinesSet (false), + m_showWholeBracketExpressionSet (false), + m_backgroundColorSet (false), + m_selectionColorSet (false), + m_highlightedLineColorSet (false), + m_highlightedBracketColorSet (false), + m_wordWrapMarkerColorSet (false), + m_tabMarkerColorSet (false), + m_indentationLineColorSet (false), + m_iconBarColorSet (false), + m_foldingColorSet (false), + m_lineNumberColorSet (false), + m_separatorColorSet (false), + m_spellingMistakeLineColorSet (false), + m_modifiedLineColorSet (false), + m_savedLineColorSet (false), + m_searchHighlightColorSet (false), + m_replaceHighlightColorSet (false), + m_lineMarkerColorSet (m_lineMarkerColor.size()), + m_renderer (0) +{ + // init bitarray + m_lineMarkerColorSet.fill (true); + + s_global = this; + + // init with defaults from config or really hardcoded ones + KConfigGroup config(KGlobal::config(), "Kate Renderer Defaults"); + readConfig (config); +} + +KateRendererConfig::KateRendererConfig (KateRenderer *renderer) + : m_fontMetrics(QFont()), + m_lineMarkerColor (KTextEditor::MarkInterface::reservedMarkersCount()), + m_schemaSet (false), + m_fontSet (false), + m_wordWrapMarkerSet (false), + m_showIndentationLinesSet (false), + m_showWholeBracketExpressionSet (false), + m_backgroundColorSet (false), + m_selectionColorSet (false), + m_highlightedLineColorSet (false), + m_highlightedBracketColorSet (false), + m_wordWrapMarkerColorSet (false), + m_tabMarkerColorSet (false), + m_indentationLineColorSet (false), + m_iconBarColorSet (false), + m_foldingColorSet (false), + m_lineNumberColorSet (false), + m_separatorColorSet (false), + m_spellingMistakeLineColorSet (false), + m_modifiedLineColorSet(false), + m_savedLineColorSet(false), + m_searchHighlightColorSet(false), + m_replaceHighlightColorSet(false), + m_lineMarkerColorSet (m_lineMarkerColor.size()), + m_renderer (renderer) +{ + // init bitarray + m_lineMarkerColorSet.fill (false); +} + +KateRendererConfig::~KateRendererConfig () +{ +} + +namespace +{ + const char * const KEY_SCHEMA = "Schema"; + const char * const KEY_WORD_WRAP_MARKER = "Word Wrap Marker"; + const char * const KEY_SHOW_INDENTATION_LINES = "Show Indentation Lines"; + const char * const KEY_SHOW_WHOLE_BRACKET_EXPRESSION = "Show Whole Bracket Expression"; + const char * const KEY_ANIMATE_BRACKET_MATCHING = "Animate Bracket Matching"; +} + +void KateRendererConfig::readConfig (const KConfigGroup &config) +{ + configStart (); + + // "Normal" Schema MUST BE THERE, see global kateschemarc + setSchema (config.readEntry(KEY_SCHEMA, "Normal")); + + setWordWrapMarker (config.readEntry(KEY_WORD_WRAP_MARKER, false )); + + setShowIndentationLines (config.readEntry( KEY_SHOW_INDENTATION_LINES, false)); + + setShowWholeBracketExpression (config.readEntry( KEY_SHOW_WHOLE_BRACKET_EXPRESSION, false)); + + setAnimateBracketMatching(config.readEntry(KEY_ANIMATE_BRACKET_MATCHING, false)); + + configEnd (); +} + +void KateRendererConfig::writeConfig (KConfigGroup& config) +{ + config.writeEntry (KEY_SCHEMA, schema()); + + config.writeEntry(KEY_WORD_WRAP_MARKER, wordWrapMarker() ); + + config.writeEntry(KEY_SHOW_INDENTATION_LINES, showIndentationLines()); + + config.writeEntry(KEY_SHOW_WHOLE_BRACKET_EXPRESSION, showWholeBracketExpression()); + + config.writeEntry(KEY_ANIMATE_BRACKET_MATCHING, animateBracketMatching()); +} + +void KateRendererConfig::updateConfig () +{ + if (m_renderer) + { + m_renderer->updateConfig (); + return; + } + + if (isGlobal()) + { + for (int z=0; z < KateGlobal::self()->views().size(); ++z) + (KateGlobal::self()->views())[z]->renderer()->updateConfig (); + } +} + +const QString &KateRendererConfig::schema () const +{ + if (m_schemaSet || isGlobal()) + return m_schema; + + return s_global->schema(); +} + +void KateRendererConfig::setSchema (const QString &schema) +{ + if (m_schemaSet && m_schema == schema) + return; + + configStart (); + m_schemaSet = true; + m_schema = schema; + setSchemaInternal( schema ); + configEnd (); +} + +void KateRendererConfig::reloadSchema() +{ + if ( isGlobal() ) { + setSchemaInternal( m_schema ); + foreach (KateView* view, KateGlobal::self()->views() ) + view->renderer()->config()->reloadSchema(); + } + + else if ( m_renderer && m_schemaSet ) + setSchemaInternal( m_schema ); +} + +void KateRendererConfig::setSchemaInternal( const QString &schema ) +{ + m_schemaSet = true; + m_schema = schema; + + KConfigGroup config = KateGlobal::self()->schemaManager()->schema(schema); + + KateDefaultColors colors; + + m_backgroundColor = config.readEntry("Color Background", colors.color(Kate::Background)); + m_backgroundColorSet = true; + m_selectionColor = config.readEntry("Color Selection", colors.color(Kate::SelectionBackground)); + m_selectionColorSet = true; + m_highlightedLineColor = config.readEntry("Color Highlighted Line", colors.color(Kate::HighlightedLineBackground)); + m_highlightedLineColorSet = true; + m_highlightedBracketColor = config.readEntry("Color Highlighted Bracket", colors.color(Kate::HighlightedBracket)); + m_highlightedBracketColorSet = true; + m_wordWrapMarkerColor = config.readEntry("Color Word Wrap Marker", colors.color(Kate::WordWrapMarker)); + m_wordWrapMarkerColorSet = true; + m_tabMarkerColor = config.readEntry("Color Tab Marker", colors.color(Kate::TabMarker)); + m_tabMarkerColorSet = true; + m_indentationLineColor = config.readEntry("Color Indentation Line", colors.color(Kate::IndentationLine)); + m_indentationLineColorSet = true; + m_iconBarColor = config.readEntry("Color Icon Bar", colors.color(Kate::IconBar)); + m_iconBarColorSet = true; + m_foldingColor = config.readEntry("Color Code Folding", colors.color(Kate::CodeFolding)); + m_foldingColorSet = true; + m_lineNumberColor = config.readEntry("Color Line Number", colors.color(Kate::LineNumber)); + m_lineNumberColorSet = true; + m_separatorColor = config.readEntry("Color Separator", colors.color(Kate::Separator)); + m_separatorColorSet = true; + m_spellingMistakeLineColor = config.readEntry("Color Spelling Mistake Line", colors.color(Kate::SpellingMistakeLine)); + m_spellingMistakeLineColorSet = true; + + m_modifiedLineColor = config.readEntry("Color Modified Lines", colors.color(Kate::ModifiedLine)); + m_modifiedLineColorSet = true; + m_savedLineColor = config.readEntry("Color Saved Lines", colors.color(Kate::SavedLine)); + m_savedLineColorSet = true; + m_searchHighlightColor = config.readEntry("Color Search Highlight", colors.color(Kate::SearchHighlight)); + m_searchHighlightColorSet = true; + m_replaceHighlightColor = config.readEntry("Color Replace Highlight", colors.color(Kate::ReplaceHighlight)); + m_replaceHighlightColorSet = true; + + for (int i = Kate::FIRST_MARK; i <= Kate::LAST_MARK; i++) { + QColor col = config.readEntry(QString("Color MarkType %1").arg(i + 1), colors.mark(i)); + m_lineMarkerColorSet[i] = true; + m_lineMarkerColor[i] = col; + } + + QFont f (KGlobalSettings::fixedFont()); + + m_font = config.readEntry("Font", f); + m_fontMetrics = QFontMetricsF (m_font); + m_fontSet = true; +} + +const QFont& KateRendererConfig::font() const +{ + if (m_fontSet || isGlobal()) + return m_font; + + return s_global->font(); +} + +const QFontMetricsF& KateRendererConfig::fontMetrics() const +{ + if (m_fontSet || isGlobal()) + return m_fontMetrics; + + return s_global->fontMetrics(); +} + +void KateRendererConfig::setFont(const QFont &font) +{ + if (m_fontSet && m_font == font) + return; + + configStart (); + + m_fontSet = true; + m_font = font; + m_fontMetrics = QFontMetricsF (m_font); + + configEnd (); +} + +bool KateRendererConfig::wordWrapMarker () const +{ + if (m_wordWrapMarkerSet || isGlobal()) + return m_wordWrapMarker; + + return s_global->wordWrapMarker(); +} + +void KateRendererConfig::setWordWrapMarker (bool on) +{ + if (m_wordWrapMarkerSet && m_wordWrapMarker == on) + return; + + configStart (); + + m_wordWrapMarkerSet = true; + m_wordWrapMarker = on; + + configEnd (); +} + +const QColor& KateRendererConfig::backgroundColor() const +{ + if (m_backgroundColorSet || isGlobal()) + return m_backgroundColor; + + return s_global->backgroundColor(); +} + +void KateRendererConfig::setBackgroundColor (const QColor &col) +{ + if (m_backgroundColorSet && m_backgroundColor == col) + return; + + configStart (); + + m_backgroundColorSet = true; + m_backgroundColor = col; + + configEnd (); +} + +const QColor& KateRendererConfig::selectionColor() const +{ + if (m_selectionColorSet || isGlobal()) + return m_selectionColor; + + return s_global->selectionColor(); +} + +void KateRendererConfig::setSelectionColor (const QColor &col) +{ + if (m_selectionColorSet && m_selectionColor == col) + return; + + configStart (); + + m_selectionColorSet = true; + m_selectionColor = col; + + configEnd (); +} + +const QColor& KateRendererConfig::highlightedLineColor() const +{ + if (m_highlightedLineColorSet || isGlobal()) + return m_highlightedLineColor; + + return s_global->highlightedLineColor(); +} + +void KateRendererConfig::setHighlightedLineColor (const QColor &col) +{ + if (m_highlightedLineColorSet && m_highlightedLineColor == col) + return; + + configStart (); + + m_highlightedLineColorSet = true; + m_highlightedLineColor = col; + + configEnd (); +} + +const QColor& KateRendererConfig::lineMarkerColor(KTextEditor::MarkInterface::MarkTypes type) const +{ + int index = 0; + if (type > 0) { while((type >> index++) ^ 1) {} } + index -= 1; + + if ( index < 0 || index >= KTextEditor::MarkInterface::reservedMarkersCount() ) + { + static QColor dummy; + return dummy; + } + + if (m_lineMarkerColorSet[index] || isGlobal()) + return m_lineMarkerColor[index]; + + return s_global->lineMarkerColor( type ); +} + +void KateRendererConfig::setLineMarkerColor (const QColor &col, KTextEditor::MarkInterface::MarkTypes type) +{ + int index = static_cast( log(static_cast(type)) / log(2.0) ); + Q_ASSERT( index >= 0 && index < KTextEditor::MarkInterface::reservedMarkersCount() ); + + if (m_lineMarkerColorSet[index] && m_lineMarkerColor[index] == col) + return; + + configStart (); + + m_lineMarkerColorSet[index] = true; + m_lineMarkerColor[index] = col; + + configEnd (); +} + +const QColor& KateRendererConfig::highlightedBracketColor() const +{ + if (m_highlightedBracketColorSet || isGlobal()) + return m_highlightedBracketColor; + + return s_global->highlightedBracketColor(); +} + +void KateRendererConfig::setHighlightedBracketColor (const QColor &col) +{ + if (m_highlightedBracketColorSet && m_highlightedBracketColor == col) + return; + + configStart (); + + m_highlightedBracketColorSet = true; + m_highlightedBracketColor = col; + + configEnd (); +} + +const QColor& KateRendererConfig::wordWrapMarkerColor() const +{ + if (m_wordWrapMarkerColorSet || isGlobal()) + return m_wordWrapMarkerColor; + + return s_global->wordWrapMarkerColor(); +} + +void KateRendererConfig::setWordWrapMarkerColor (const QColor &col) +{ + if (m_wordWrapMarkerColorSet && m_wordWrapMarkerColor == col) + return; + + configStart (); + + m_wordWrapMarkerColorSet = true; + m_wordWrapMarkerColor = col; + + configEnd (); +} + +const QColor& KateRendererConfig::tabMarkerColor() const +{ + if (m_tabMarkerColorSet || isGlobal()) + return m_tabMarkerColor; + + return s_global->tabMarkerColor(); +} + +void KateRendererConfig::setTabMarkerColor (const QColor &col) +{ + if (m_tabMarkerColorSet && m_tabMarkerColor == col) + return; + + configStart (); + + m_tabMarkerColorSet = true; + m_tabMarkerColor = col; + + configEnd (); +} + +const QColor& KateRendererConfig::indentationLineColor() const +{ + if (m_indentationLineColorSet || isGlobal()) + return m_indentationLineColor; + + return s_global->indentationLineColor(); +} + +void KateRendererConfig::setIndentationLineColor (const QColor &col) +{ + if (m_indentationLineColorSet && m_indentationLineColor == col) + return; + + configStart (); + + m_indentationLineColorSet = true; + m_indentationLineColor = col; + + configEnd (); +} + +const QColor& KateRendererConfig::iconBarColor() const +{ + if (m_iconBarColorSet || isGlobal()) + return m_iconBarColor; + + return s_global->iconBarColor(); +} + +void KateRendererConfig::setIconBarColor (const QColor &col) +{ + if (m_iconBarColorSet && m_iconBarColor == col) + return; + + configStart (); + + m_iconBarColorSet = true; + m_iconBarColor = col; + + configEnd (); +} + +const QColor& KateRendererConfig::foldingColor() const +{ + if (m_foldingColorSet || isGlobal()) + return m_foldingColor; + + return s_global->foldingColor(); +} + +void KateRendererConfig::setFoldingColor (const QColor &col) +{ + if (m_foldingColorSet && m_foldingColor == col) + return; + + configStart (); + + m_foldingColorSet = true; + m_foldingColor = col; + + configEnd (); +} + +const QColor& KateRendererConfig::lineNumberColor() const +{ + if (m_lineNumberColorSet || isGlobal()) + return m_lineNumberColor; + + return s_global->lineNumberColor(); +} + +void KateRendererConfig::setLineNumberColor (const QColor &col) +{ + if (m_lineNumberColorSet && m_lineNumberColor == col) + return; + + configStart (); + + m_lineNumberColorSet = true; + m_lineNumberColor = col; + + configEnd (); +} + +const QColor& KateRendererConfig::separatorColor() const +{ + if (m_separatorColorSet || isGlobal()) + return m_separatorColor; + + return s_global->separatorColor(); +} + +void KateRendererConfig::setSeparatorColor(const QColor& col) +{ + if (m_separatorColorSet && m_separatorColor == col) + return; + + configStart (); + + m_separatorColorSet = true; + m_separatorColor = col; + + configEnd (); +} + +const QColor& KateRendererConfig::spellingMistakeLineColor() const +{ + if (m_spellingMistakeLineColorSet || isGlobal()) + return m_spellingMistakeLineColor; + + return s_global->spellingMistakeLineColor(); +} + +void KateRendererConfig::setSpellingMistakeLineColor (const QColor &col) +{ + if (m_spellingMistakeLineColorSet && m_spellingMistakeLineColor == col) + return; + + configStart (); + + m_spellingMistakeLineColorSet = true; + m_spellingMistakeLineColor = col; + + configEnd (); +} + +const QColor& KateRendererConfig::modifiedLineColor() const +{ + if (m_modifiedLineColorSet || isGlobal()) + return m_modifiedLineColor; + + return s_global->modifiedLineColor(); +} + +void KateRendererConfig::setModifiedLineColor(const QColor &col) +{ + if (m_modifiedLineColorSet && m_modifiedLineColor == col) + return; + + configStart (); + + m_modifiedLineColorSet = true; + m_modifiedLineColor = col; + + configEnd (); +} + +const QColor& KateRendererConfig::savedLineColor() const +{ + if (m_savedLineColorSet || isGlobal()) + return m_savedLineColor; + + return s_global->savedLineColor(); +} + +void KateRendererConfig::setSavedLineColor(const QColor &col) +{ + if (m_savedLineColorSet && m_savedLineColor == col) + return; + + configStart (); + + m_savedLineColorSet = true; + m_savedLineColor = col; + + configEnd (); +} + +const QColor& KateRendererConfig::searchHighlightColor() const +{ + if (m_searchHighlightColorSet || isGlobal()) + return m_searchHighlightColor; + + return s_global->searchHighlightColor(); +} + +void KateRendererConfig::setSearchHighlightColor(const QColor &col) +{ + if (m_searchHighlightColorSet && m_searchHighlightColor == col) + return; + + configStart (); + + m_searchHighlightColorSet = true; + m_searchHighlightColor = col; + + configEnd (); +} + +const QColor& KateRendererConfig::replaceHighlightColor() const +{ + if (m_replaceHighlightColorSet || isGlobal()) + return m_replaceHighlightColor; + + return s_global->replaceHighlightColor(); +} + +void KateRendererConfig::setReplaceHighlightColor(const QColor &col) +{ + if (m_replaceHighlightColorSet && m_replaceHighlightColor == col) + return; + + configStart (); + + m_replaceHighlightColorSet = true; + m_replaceHighlightColor = col; + + configEnd (); +} + + +bool KateRendererConfig::showIndentationLines () const +{ + if (m_showIndentationLinesSet || isGlobal()) + return m_showIndentationLines; + + return s_global->showIndentationLines(); +} + +void KateRendererConfig::setShowIndentationLines (bool on) +{ + if (m_showIndentationLinesSet && m_showIndentationLines == on) + return; + + configStart (); + + m_showIndentationLinesSet = true; + m_showIndentationLines = on; + + configEnd (); +} + +bool KateRendererConfig::showWholeBracketExpression () const +{ + if (m_showWholeBracketExpressionSet || isGlobal()) + return m_showWholeBracketExpression; + + return s_global->showWholeBracketExpression(); +} + +void KateRendererConfig::setShowWholeBracketExpression (bool on) +{ + if (m_showWholeBracketExpressionSet && m_showWholeBracketExpression == on) + return; + + configStart (); + + m_showWholeBracketExpressionSet = true; + m_showWholeBracketExpression = on; + + configEnd (); +} + +bool KateRendererConfig::animateBracketMatching () const +{ + return s_global->m_animateBracketMatching; +} + +void KateRendererConfig::setAnimateBracketMatching (bool on) +{ + if (!isGlobal()) { + s_global->setAnimateBracketMatching (on); + } else if (on != m_animateBracketMatching) { + configStart (); + m_animateBracketMatching = on; + configEnd (); + } +} + +//END + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/utils/kateconfig.h b/kate/part/utils/kateconfig.h new file mode 100644 index 00000000..302b36aa --- /dev/null +++ b/kate/part/utils/kateconfig.h @@ -0,0 +1,794 @@ + +/* This file is part of the KDE libraries + Copyright (C) 2003 Christoph Cullmann + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef __KATE_CONFIG_H__ +#define __KATE_CONFIG_H__ + +#include "katepartinterfaces_export.h" + +#include + +#include +#include +#include +#include +#include +#include +#include + +class KConfigGroup; +class KateView; +class KateDocument; +class KateRenderer; + +class KConfig; + +#include + + +/** + * Base Class for the Kate Config Classes + */ +class KateConfig +{ + public: + /** + * Default Constructor + */ + KateConfig (); + + /** + * Virtual Destructor + */ + virtual ~KateConfig (); + + public: + /** + * start some config changes + * this method is needed to init some kind of transaction + * for config changes, update will only be done once, at + * configEnd() call + */ + void configStart (); + + /** + * end a config change transaction, update the concerned + * documents/views/renderers + */ + void configEnd (); + + protected: + /** + * do the real update + */ + virtual void updateConfig () = 0; + + private: + /** + * recursion depth + */ + uint configSessionNumber; + + /** + * is a config session running + */ + bool configIsRunning; +}; + +class KATEPARTINTERFACES_EXPORT KateGlobalConfig : public KateConfig +{ + private: + friend class KateGlobal; + + /** + * only used in KateGlobal for the static global fallback !!! + */ + KateGlobalConfig (); + + /** + * Destructor + */ + ~KateGlobalConfig (); + + public: + static KateGlobalConfig *global () { return s_global; } + + public: + /** + * Read config from object + */ + void readConfig (const KConfigGroup &config); + + /** + * Write config to object + */ + void writeConfig (KConfigGroup &config); + + protected: + void updateConfig (); + + public: + QTextCodec *fallbackCodec () const; + const QString &fallbackEncoding () const; + bool setFallbackEncoding (const QString &encoding); + + private: + QString m_fallbackEncoding; + + private: + static KateGlobalConfig *s_global; +}; + +class KATEPARTINTERFACES_EXPORT KateDocumentConfig : public KateConfig +{ + private: + friend class KateGlobal; + + KateDocumentConfig (); + + public: + KateDocumentConfig (const KConfigGroup &cg); + + /** + * Construct a DocumentConfig + */ + KateDocumentConfig (KateDocument *doc); + + /** + * Cu DocumentConfig + */ + ~KateDocumentConfig (); + + inline static KateDocumentConfig *global () { return s_global; } + + inline bool isGlobal () const { return (this == global()); } + + public: + /** + * Read config from object + */ + void readConfig (const KConfigGroup &config); + + /** + * Write config to object + */ + void writeConfig (KConfigGroup &config); + + protected: + void updateConfig (); + + public: + int tabWidth () const; + void setTabWidth (int tabWidth); + + int indentationWidth () const; + void setIndentationWidth (int indentationWidth); + + const QString &indentationMode () const; + void setIndentationMode (const QString &identationMode); + + enum TabHandling + { + tabInsertsTab = 0, + tabIndents = 1, + tabSmart = 2 //!< indents in leading space, otherwise inserts tab + }; + + uint tabHandling () const; + void setTabHandling (uint tabHandling); + + bool wordWrap () const; + void setWordWrap (bool on); + + int wordWrapAt () const; + void setWordWrapAt (int col); + + bool pageUpDownMovesCursor () const; + void setPageUpDownMovesCursor (bool on); + + void setKeepExtraSpaces (bool on); + bool keepExtraSpaces () const; + + void setIndentPastedText (bool on); + bool indentPastedText () const; + + void setBackspaceIndents (bool on); + bool backspaceIndents () const; + + void setSmartHome (bool on); + bool smartHome () const; + + void setShowTabs (bool on); + bool showTabs() const; + + void setShowSpaces (bool on); + bool showSpaces() const; + + void setReplaceTabsDyn (bool on); + bool replaceTabsDyn() const; + + /** + * Remove trailing spaces on save. + * triState = 0: never remove trailing spaces + * triState = 1: remove trailing spaces of modified lines (line modification system) + * triState = 2: remove trailing spaces in entire document + */ + void setRemoveSpaces (int triState); + int removeSpaces () const; + + void setNewLineAtEof (bool on); + bool newLineAtEof () const; + + void setOvr (bool on); + bool ovr () const; + + void setTabIndents (bool on); + bool tabIndentsEnabled () const; + + QTextCodec *codec () const; + const QString &encoding () const; + bool setEncoding (const QString &encoding); + bool isSetEncoding () const; + + enum Eol + { + eolUnix = 0, + eolDos = 1, + eolMac = 2 + }; + + int eol () const; + QString eolString (); + + void setEol (int mode); + + bool bom () const; + void setBom(bool bom); + + bool allowEolDetection () const; + void setAllowEolDetection (bool on); + + bool allowSimpleMode () const; + void setAllowSimpleMode (bool on); + + enum BackupFlags + { + LocalFiles=1, + RemoteFiles=2 + }; + + uint backupFlags () const; + void setBackupFlags (uint flags); + + const QString &backupPrefix () const; + void setBackupPrefix (const QString &prefix); + + const QString &backupSuffix () const; + void setBackupSuffix (const QString &suffix); + + bool swapFileNoSync() const; + void setSwapFileNoSync(bool on); + + /** + * Should Kate Part search for dir wide config file + * and if, how depth? + * @return search depth (< 0 no search) + */ + int searchDirConfigDepth () const; + + void setSearchDirConfigDepth (int depth); + + bool onTheFlySpellCheck() const; + void setOnTheFlySpellCheck(bool on); + + int lineLengthLimit() const; + void setLineLengthLimit(int limit); + + + private: + QString m_indentationMode; + int m_indentationWidth; + int m_tabWidth; + uint m_tabHandling; + uint m_configFlags; + int m_wordWrapAt; + bool m_wordWrap; + bool m_pageUpDownMovesCursor; + bool m_allowEolDetection; + bool m_allowSimpleMode; + int m_eol; + bool m_bom; + uint m_backupFlags; + int m_searchDirConfigDepth; + QString m_encoding; + QString m_backupPrefix; + QString m_backupSuffix; + bool m_swapFileNoSync; + bool m_onTheFlySpellCheck; + int m_lineLengthLimit; + + bool m_tabWidthSet : 1; + bool m_indentationWidthSet : 1; + bool m_indentationModeSet : 1; + bool m_wordWrapSet : 1; + bool m_wordWrapAtSet : 1; + bool m_pageUpDownMovesCursorSet : 1; + + bool m_keepExtraSpacesSet : 1; + bool m_keepExtraSpaces : 1; + bool m_indentPastedTextSet : 1; + bool m_indentPastedText : 1; + bool m_backspaceIndentsSet : 1; + bool m_backspaceIndents : 1; + bool m_smartHomeSet : 1; + bool m_smartHome : 1; + bool m_showTabsSet : 1; + bool m_showTabs : 1; + bool m_showSpacesSet : 1; + bool m_showSpaces : 1; + bool m_replaceTabsDynSet : 1; + bool m_replaceTabsDyn : 1; + bool m_removeSpacesSet : 1; + uint m_removeSpaces : 2; + bool m_newLineAtEofSet : 1; + bool m_newLineAtEof : 1; + bool m_overwiteModeSet : 1; + bool m_overwiteMode : 1; + bool m_tabIndentsSet : 1; + bool m_tabIndents : 1; + + bool m_encodingSet : 1; + bool m_eolSet : 1; + bool m_bomSet :1; + bool m_allowEolDetectionSet : 1; + bool m_allowSimpleModeSet : 1; + bool m_backupFlagsSet : 1; + bool m_searchDirConfigDepthSet : 1; + bool m_backupPrefixSet : 1; + bool m_backupSuffixSet : 1; + bool m_swapFileNoSyncSet : 1; + bool m_onTheFlySpellCheckSet : 1; + bool m_lineLengthLimitSet : 1; + + private: + static KateDocumentConfig *s_global; + KateDocument *m_doc; +}; + +class KATEPARTINTERFACES_EXPORT KateViewConfig : public KateConfig +{ + private: + friend class KateGlobal; + + /** + * only used in KateGlobal for the static global fallback !!! + */ + KateViewConfig (); + + public: + /** + * Construct a DocumentConfig + */ + explicit KateViewConfig (KateView *view); + + /** + * Cu DocumentConfig + */ + ~KateViewConfig (); + + inline static KateViewConfig *global () { return s_global; } + + inline bool isGlobal () const { return (this == global()); } + + public: + /** + * Read config from object + */ + void readConfig (const KConfigGroup &config); + + /** + * Write config to object + */ + void writeConfig (KConfigGroup &config); + + protected: + void updateConfig (); + + public: + bool dynWordWrap () const; + void setDynWordWrap (bool wrap); + + int dynWordWrapIndicators () const; + void setDynWordWrapIndicators (int mode); + + int dynWordWrapAlignIndent () const; + void setDynWordWrapAlignIndent (int indent); + + bool lineNumbers () const; + void setLineNumbers (bool on); + + bool scrollBarMarks () const; + void setScrollBarMarks (bool on); + + bool scrollBarMiniMap () const; + void setScrollBarMiniMap (bool on); + + bool scrollBarMiniMapAll () const; + void setScrollBarMiniMapAll (bool on); + + int scrollBarMiniMapWidth () const; + void setScrollBarMiniMapWidth (int width); + + /* Whether to show scrollbars */ + enum ScrollbarMode { + AlwaysOn = 0, + ShowWhenNeeded, + AlwaysOff + }; + + int showScrollbars () const; + void setShowScrollbars (int mode); + + bool iconBar () const; + void setIconBar (bool on); + + bool foldingBar () const; + void setFoldingBar (bool on); + + bool lineModification() const; + void setLineModification(bool on); + + int bookmarkSort () const; + void setBookmarkSort (int mode); + + int autoCenterLines() const; + void setAutoCenterLines (int lines); + + enum SearchFlags { + IncMatchCase = 1 << 0, + IncHighlightAll = 1 << 1, + IncFromCursor = 1 << 2, + PowerMatchCase = 1 << 3, + PowerHighlightAll = 1 << 4, + PowerFromCursor = 1 << 5, + // PowerSelectionOnly = 1 << 6, Better not save to file // Sebastian + PowerModePlainText = 1 << 7, + PowerModeWholeWords = 1 << 8, + PowerModeEscapeSequences = 1 << 9, + PowerModeRegularExpression = 1 << 10, + PowerUsePlaceholders = 1 << 11 + }; + + long searchFlags () const; + void setSearchFlags (long flags); + + int maxHistorySize() const; + + QStringListModel *patternHistoryModel(); + QStringListModel *replacementHistoryModel(); + + uint defaultMarkType () const; + void setDefaultMarkType (uint type); + + bool allowMarkMenu () const; + void setAllowMarkMenu (bool allow); + + bool persistentSelection () const; + void setPersistentSelection (bool on); + + bool viInputMode () const; + void setViInputMode (bool on); + + bool viInputModeStealKeys () const; + void setViInputModeStealKeys (bool on); + + bool viRelativeLineNumbers() const; + void setViRelativeLineNumbers(bool on); + + bool viInputModeEmulateCommandBar() const; + void setViInputModeEmulateCommandBar(bool on); + + // Do we still need the enum and related functions below? + enum TextToSearch + { + Nowhere = 0, + SelectionOnly = 1, + SelectionWord = 2, + WordOnly = 3, + WordSelection = 4 + }; + + bool automaticCompletionInvocation () const; + void setAutomaticCompletionInvocation (bool on); + + bool wordCompletion () const; + void setWordCompletion (bool on); + + bool keywordCompletion () const; + void setKeywordCompletion (bool on); + + int wordCompletionMinimalWordLength () const; + void setWordCompletionMinimalWordLength (int length); + + bool wordCompletionRemoveTail () const; + void setWordCompletionRemoveTail (bool on); + + bool smartCopyCut() const; + void setSmartCopyCut(bool on); + + bool scrollPastEnd() const; + void setScrollPastEnd(bool on); + + bool foldFirstLine() const; + void setFoldFirstLine(bool on); + + private: + bool m_dynWordWrap; + int m_dynWordWrapIndicators; + int m_dynWordWrapAlignIndent; + bool m_lineNumbers; + bool m_scrollBarMarks; + bool m_scrollBarMiniMap; + bool m_scrollBarMiniMapAll; + int m_scrollBarMiniMapWidth; + int m_showScrollbars; + bool m_iconBar; + bool m_foldingBar; + bool m_lineModification; + int m_bookmarkSort; + int m_autoCenterLines; + long m_searchFlags; + int m_maxHistorySize; + QStringListModel m_patternHistoryModel; + QStringListModel m_replacementHistoryModel; + uint m_defaultMarkType; + bool m_persistentSelection; + bool m_viInputMode; + bool m_viInputModeStealKeys; + bool m_viRelativeLineNumbers; + bool m_viInputModeEmulateCommandBar; + bool m_automaticCompletionInvocation; + bool m_wordCompletion; + bool m_keywordCompletion; + int m_wordCompletionMinimalWordLength; + bool m_wordCompletionRemoveTail; + bool m_smartCopyCut; + bool m_scrollPastEnd; + bool m_foldFirstLine; + + bool m_dynWordWrapSet : 1; + bool m_dynWordWrapIndicatorsSet : 1; + bool m_dynWordWrapAlignIndentSet : 1; + bool m_lineNumbersSet : 1; + bool m_scrollBarMarksSet : 1; + bool m_scrollBarMiniMapSet : 1; + bool m_scrollBarMiniMapAllSet : 1; + bool m_scrollBarMiniMapWidthSet : 1; + bool m_showScrollbarsSet : 1; + bool m_iconBarSet : 1; + bool m_foldingBarSet : 1; + bool m_lineModificationSet : 1; + bool m_bookmarkSortSet : 1; + bool m_autoCenterLinesSet : 1; + bool m_searchFlagsSet : 1; + bool m_defaultMarkTypeSet : 1; + bool m_persistentSelectionSet : 1; + bool m_viInputModeSet : 1; + bool m_viInputModeStealKeysSet : 1; + bool m_viRelativeLineNumbersSet : 1; + bool m_viInputModeEmulateCommandBarSet : 1; + bool m_automaticCompletionInvocationSet : 1; + bool m_wordCompletionSet : 1; + bool m_keywordCompletionSet : 1; + bool m_wordCompletionMinimalWordLengthSet : 1; + bool m_smartCopyCutSet : 1; + bool m_scrollPastEndSet : 1; + bool m_allowMarkMenu : 1; + bool m_wordCompletionRemoveTailSet : 1; + bool m_foldFirstLineSet : 1; + + private: + static KateViewConfig *s_global; + KateView *m_view; +}; + +class KATEPARTINTERFACES_EXPORT KateRendererConfig : public KateConfig +{ + private: + friend class KateGlobal; + + /** + * only used in KateGlobal for the static global fallback !!! + */ + KateRendererConfig (); + + + public: + /** + * Construct a DocumentConfig + */ + KateRendererConfig (KateRenderer *renderer); + + /** + * Cu DocumentConfig + */ + ~KateRendererConfig (); + + inline static KateRendererConfig *global () { return s_global; } + + inline bool isGlobal () const { return (this == global()); } + + public: + /** + * Read config from object + */ + void readConfig (const KConfigGroup &config); + + /** + * Write config to object + */ + void writeConfig (KConfigGroup &config); + + protected: + void updateConfig (); + + public: + const QString &schema () const; + void setSchema (const QString &schema); + + /** + * Reload the schema from the schema manager. + * For the global instance, have all other instances reload. + * Used by the schema config page to apply changes. + */ + void reloadSchema(); + + const QFont& font() const; + const QFontMetricsF& fontMetrics() const; + void setFont(const QFont &font); + + bool wordWrapMarker () const; + void setWordWrapMarker (bool on); + + const QColor& backgroundColor() const; + void setBackgroundColor (const QColor &col); + + const QColor& selectionColor() const; + void setSelectionColor (const QColor &col); + + const QColor& highlightedLineColor() const; + void setHighlightedLineColor (const QColor &col); + + const QColor& lineMarkerColor(KTextEditor::MarkInterface::MarkTypes type = KTextEditor::MarkInterface::markType01) const; // markType01 == Bookmark + void setLineMarkerColor (const QColor &col, KTextEditor::MarkInterface::MarkTypes type = KTextEditor::MarkInterface::markType01); + + const QColor& highlightedBracketColor() const; + void setHighlightedBracketColor (const QColor &col); + + const QColor& wordWrapMarkerColor() const; + void setWordWrapMarkerColor (const QColor &col); + + const QColor& tabMarkerColor() const; + void setTabMarkerColor (const QColor &col); + + const QColor& indentationLineColor() const; + void setIndentationLineColor (const QColor &col); + + const QColor& iconBarColor() const; + void setIconBarColor (const QColor &col); + + const QColor& foldingColor() const; + void setFoldingColor (const QColor &col); + + // the line number color is used for the line numbers on the left bar + const QColor& lineNumberColor() const; + void setLineNumberColor (const QColor &col); + + // the color of the separator between line numbers and icon bar + const QColor& separatorColor() const; + void setSeparatorColor(const QColor &col); + + const QColor& spellingMistakeLineColor() const; + void setSpellingMistakeLineColor (const QColor &col); + + bool showIndentationLines () const; + void setShowIndentationLines (bool on); + + bool showWholeBracketExpression () const; + void setShowWholeBracketExpression (bool on); + + bool animateBracketMatching () const; + void setAnimateBracketMatching (bool on); + + const QColor& modifiedLineColor() const; + void setModifiedLineColor(const QColor &col); + + const QColor& savedLineColor() const; + void setSavedLineColor(const QColor &col); + + const QColor& searchHighlightColor() const; + void setSearchHighlightColor(const QColor &col); + + const QColor& replaceHighlightColor() const; + void setReplaceHighlightColor(const QColor &col); + + private: + /** + * Read the schema properties from the config file. + */ + void setSchemaInternal(const QString &schema); + + QString m_schema; + QFont m_font; + QFontMetricsF m_fontMetrics; + QColor m_backgroundColor; + QColor m_selectionColor; + QColor m_highlightedLineColor; + QColor m_highlightedBracketColor; + QColor m_wordWrapMarkerColor; + QColor m_tabMarkerColor; + QColor m_indentationLineColor; + QColor m_iconBarColor; + QColor m_foldingColor; + QColor m_lineNumberColor; + QColor m_separatorColor; + QColor m_spellingMistakeLineColor; + QVector m_lineMarkerColor; + + QColor m_modifiedLineColor; + QColor m_savedLineColor; + QColor m_searchHighlightColor; + QColor m_replaceHighlightColor; + + bool m_wordWrapMarker; + bool m_showIndentationLines; + bool m_showWholeBracketExpression; + bool m_animateBracketMatching; + + bool m_schemaSet : 1; + bool m_fontSet : 1; + bool m_wordWrapMarkerSet : 1; + bool m_showIndentationLinesSet : 1; + bool m_showWholeBracketExpressionSet : 1; + bool m_backgroundColorSet : 1; + bool m_selectionColorSet : 1; + bool m_highlightedLineColorSet : 1; + bool m_highlightedBracketColorSet : 1; + bool m_wordWrapMarkerColorSet : 1; + bool m_tabMarkerColorSet : 1; + bool m_indentationLineColorSet : 1; + bool m_iconBarColorSet : 1; + bool m_foldingColorSet : 1; + bool m_lineNumberColorSet : 1; + bool m_separatorColorSet : 1; + bool m_spellingMistakeLineColorSet : 1; + bool m_modifiedLineColorSet : 1; + bool m_savedLineColorSet : 1; + bool m_searchHighlightColorSet : 1; + bool m_replaceHighlightColorSet : 1; + QBitArray m_lineMarkerColorSet; + + private: + static KateRendererConfig *s_global; + KateRenderer *m_renderer; +}; + +#endif + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/utils/katedefaultcolors.cpp b/kate/part/utils/katedefaultcolors.cpp new file mode 100644 index 00000000..a29180d7 --- /dev/null +++ b/kate/part/utils/katedefaultcolors.cpp @@ -0,0 +1,126 @@ +/* This file is part of the KDE libraries and the Kate part. + * + * Copyright (C) 2014 Milian Wolff + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "katedefaultcolors.h" + +#include + +#include + +using namespace Kate; + +KateDefaultColors::KateDefaultColors() + : m_view(QPalette::Active, KColorScheme::View) + , m_window(QPalette::Active, KColorScheme::Window) + , m_selection(QPalette::Active, KColorScheme::Selection) + , m_inactiveSelection(QPalette::Inactive, KColorScheme::Selection) + , m_background(m_view.background().color()) + , m_foreground(m_view.foreground().color()) + , m_backgroundLuma(KColorUtils::luma(m_background)) + , m_foregroundLuma(KColorUtils::luma(m_foreground)) +{ +} + +QColor KateDefaultColors::color(ColorRole role) const +{ + switch(role) { + case Background: + return m_background; + case SelectionBackground: + return m_selection.background().color(); + case HighlightedLineBackground: + return m_view.background(KColorScheme::AlternateBackground).color(); + case HighlightedBracket: + return KColorUtils::tint(m_background, m_view.decoration(KColorScheme::HoverColor).color()); + case WordWrapMarker: + return KColorUtils::shade(m_background, m_backgroundLuma > 0.3 ? -0.15 : 0.03); + case TabMarker: + return KColorUtils::shade(m_background, m_backgroundLuma > 0.7 ? -0.35 : 0.3); + case IndentationLine: + return KColorUtils::shade(m_background, m_backgroundLuma > 0.7 ? -0.35 : 0.3); + case IconBar: + return m_window.background().color(); + case CodeFolding: + return m_inactiveSelection.background().color(); + case LineNumber: + return m_window.foreground().color(); + case Separator: + return m_view.foreground(KColorScheme::InactiveText).color(); + case SpellingMistakeLine: + return m_view.foreground(KColorScheme::NegativeText).color(); + case ModifiedLine: + return m_view.background(KColorScheme::NegativeBackground).color(); + case SavedLine: + return m_view.background(KColorScheme::PositiveBackground).color(); + case SearchHighlight: + return adaptToScheme(Qt::yellow, BackgroundColor); + case ReplaceHighlight: + return adaptToScheme(Qt::green, BackgroundColor); + case TemplateBackground: + return m_window.background().color(); + case TemplateFocusedEditablePlaceholder: + return m_view.background(KColorScheme::PositiveBackground).color(); + case TemplateEditablePlaceholder: + return m_view.background(KColorScheme::PositiveBackground).color(); + case TemplateNotEditablePlaceholder: + return m_view.background(KColorScheme::NegativeBackground).color(); + } + qFatal("Unhandled color requested: %d\n", role); + return QColor(); +} + +QColor KateDefaultColors::mark(Mark mark) const +{ + // note: the mark color is used as background color at very low opacity (around 0.1) + // hence, make sure the color returned here has a high saturation + switch (mark) { + case Bookmark: + return adaptToScheme(Qt::blue, BackgroundColor); + case ActiveBreakpoint: + return adaptToScheme(Qt::red, BackgroundColor); + case ReachedBreakpoint: + return adaptToScheme(Qt::yellow, BackgroundColor); + case DisabledBreakpoint: + return adaptToScheme(Qt::magenta, BackgroundColor); + case Execution: + return adaptToScheme(Qt::gray, BackgroundColor); + case Warning: + return m_view.foreground(KColorScheme::NeutralText).color(); + case Error: + return m_view.foreground(KColorScheme::NegativeText).color(); + } + qFatal("Unhandled color for mark requested: %d\n", mark); + return QColor(); +} + +QColor KateDefaultColors::mark(int i) const +{ + Q_ASSERT(i >= FIRST_MARK && i <= LAST_MARK); + return mark(static_cast(i)); +} + +QColor KateDefaultColors::adaptToScheme(const QColor& color, ColorType type) const +{ + if (m_foregroundLuma > m_backgroundLuma) { + // for dark color schemes, produce a fitting color by tinting with the foreground/background color + return KColorUtils::tint(type == ForegroundColor ? m_foreground : m_background, color, 0.5); + } + return color; +} diff --git a/kate/part/utils/katedefaultcolors.h b/kate/part/utils/katedefaultcolors.h new file mode 100644 index 00000000..eb3b57c3 --- /dev/null +++ b/kate/part/utils/katedefaultcolors.h @@ -0,0 +1,96 @@ +/* This file is part of the KDE libraries and the Kate part. + * + * Copyright (C) 2014 Milian Wolff + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __KATE_DEFAULTCOLORS_H__ +#define __KATE_DEFAULTCOLORS_H__ + +#include + +namespace Kate { + +enum ColorRole { + // editor backgrounds + Background, + SelectionBackground, + HighlightedLineBackground, + SearchHighlight, + ReplaceHighlight, + // text decorations + HighlightedBracket, + TabMarker, + IndentationLine, + SpellingMistakeLine, + // icon border + WordWrapMarker, + IconBar, + CodeFolding, + LineNumber, + Separator, + ModifiedLine, + SavedLine, + // templates + TemplateBackground, + TemplateFocusedEditablePlaceholder, + TemplateEditablePlaceholder, + TemplateNotEditablePlaceholder +}; + +enum Mark { + Bookmark = 0, + ActiveBreakpoint, + ReachedBreakpoint, + DisabledBreakpoint, + Execution, + Warning, + Error, + + FIRST_MARK = Bookmark, + LAST_MARK = Error +}; + +} + +class KateDefaultColors +{ +public: + KateDefaultColors(); + + QColor color(Kate::ColorRole color) const; + QColor mark(Kate::Mark mark) const; + QColor mark(int mark) const; + + enum ColorType { + ForegroundColor, + BackgroundColor + }; + QColor adaptToScheme(const QColor& color, ColorType type) const; + +private: + KColorScheme m_view; + KColorScheme m_window; + KColorScheme m_selection; + KColorScheme m_inactiveSelection; + QColor m_background; + QColor m_foreground; + qreal m_backgroundLuma; + qreal m_foregroundLuma; +}; + +#endif // __KATE_DEFAULTCOLORS_H__ diff --git a/kate/part/utils/katefactory.cpp b/kate/part/utils/katefactory.cpp new file mode 100644 index 00000000..6e5db191 --- /dev/null +++ b/kate/part/utils/katefactory.cpp @@ -0,0 +1,87 @@ +/* This file is part of the KDE libraries and the Kate part. + * + * Copyright (C) 2001-2010 Christoph Cullmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "kateglobal.h" +#include "katedocument.h" + +#include + +/** + * wrapper factory to be sure nobody external deletes our kateglobal object + * each instance will just increment the reference counter of our internal + * super private global instance ;) + */ +class KateFactory : public KTextEditor::Factory +{ + public: + /** + * constructor, ref the editor, too keep it alive + * @param parent parent object + * @param name name of factory + */ + KateFactory ( QObject *parent = 0 ) + : KTextEditor::Factory (parent) + { + KateGlobal::incRef (); + } + + /** + * destructor, release editor + */ + virtual ~KateFactory () + { + KateGlobal::decRef (); + } + + KTextEditor::Editor *editor () { return KateGlobal::self(); } + + /** + * reimplemented create object method + * @param parentWidget parent widget + * @param parent QObject parent + * @param args additional arguments + * @return constructed part object + */ + KParts::Part *createPartObject ( QWidget *parentWidget, QObject *parent, const char *_classname, const QStringList & ) + { + QByteArray classname( _classname ); + + // default to the kparts::* behavior of having one single widget() if the user don't requested a pure document + bool bWantSingleView = ( classname != "KTextEditor::Document" ); + + // does user want browserview? not konqueror + bool bWantBrowserView = false; + + // should we be readonly? + bool bWantReadOnly = (bWantBrowserView || ( classname == "KParts::ReadOnlyPart" )); + + // set simple mode on for read-only part per default + KateGlobal::self ()->setSimpleMode (bWantReadOnly); + + KParts::ReadWritePart *part = new KateDocument (bWantSingleView, bWantBrowserView, bWantReadOnly, parentWidget, parent); + part->setReadWrite( !bWantReadOnly ); + + return part; + } +}; + +K_EXPORT_PLUGIN( KateFactory ) + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/utils/kateglobal.cpp b/kate/part/utils/kateglobal.cpp new file mode 100644 index 00000000..d680313f --- /dev/null +++ b/kate/part/utils/kateglobal.cpp @@ -0,0 +1,533 @@ +/* This file is part of the KDE libraries and the Kate part. + * + * Copyright (C) 2001-2010 Christoph Cullmann + * Copyright (C) 2009 Erlend Hamberg + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "kateglobal.h" +#include "moc_kateglobal.cpp" + +#include "katedocument.h" +#include "kateview.h" +#include "katerenderer.h" +#include "katecmds.h" +#include "katemodemanager.h" +#include "kateschema.h" +#include "kateschemaconfig.h" +#include "kateconfig.h" +#include "katecmd.h" +#include "katebuffer.h" +#include "katepartpluginmanager.h" +#include "katewordcompletion.h" +#include "katekeywordcompletion.h" +#include "spellcheck/spellcheck.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +KateGlobal *KateGlobal::s_self = 0; + +int KateGlobal::s_ref = 0; + +QString KateGlobal::katePartVersion() +{ + return QString("3.7"); +} + +KateGlobal::KateGlobal () + : KTextEditor::Editor (0) + , m_aboutData ("katepart", 0, ki18n("Kate Part"), katePartVersion().toLatin1(), + ki18n( "Embeddable editor component" ), KAboutData::License_LGPL_V2, + ki18n( "(c) 2000-2013 The Kate Authors" ), KLocalizedString(), "http://www.kate-editor.org") + , m_componentData (&m_aboutData) + , m_sessionConfig (KGlobal::config()) +{ + // set s_self + s_self = this; + + // load the kate part translation catalog + KGlobal::locale()->insertCatalog("katepart4"); + + // + // fill about data + // + m_aboutData.setProgramIconName("preferences-plugin"); + m_aboutData.addAuthor (ki18n("Christoph Cullmann"), ki18n("Maintainer"), "cullmann@kde.org", "http://www.cullmann.io"); + m_aboutData.addAuthor (ki18n("Dominik Haumann"), ki18n("Core Developer"), "dhaumann@kde.org"); + m_aboutData.addAuthor (ki18n("Milian Wolff"), ki18n("Core Developer"), "mail@milianw.de", "http://milianw.de"); + m_aboutData.addAuthor (ki18n("Joseph Wenninger"), ki18n("Core Developer"), "jowenn@kde.org","http://stud3.tuwien.ac.at/~e9925371"); + m_aboutData.addAuthor (ki18n("Erlend Hamberg"), ki18n("Vi Input Mode"), "ehamberg@gmail.com", "http://hamberg.no/erlend"); + m_aboutData.addAuthor (ki18n("Bernhard Beschow"), ki18n("Developer"), "bbeschow@cs.tu-berlin.de", "https://user.cs.tu-berlin.de/~bbeschow"); + m_aboutData.addAuthor (ki18n("Anders Lund"), ki18n("Core Developer"), "anders@alweb.dk", "http://www.alweb.dk"); + m_aboutData.addAuthor (ki18n("Michel Ludwig"), ki18n("On-the-fly spell checking"), "michel.ludwig@kdemail.net"); + m_aboutData.addAuthor (ki18n("Pascal Létourneau"), ki18n("Large scale bug fixing"), "pascal.letourneau@gmail.com"); + m_aboutData.addAuthor (ki18n("Hamish Rodda"), ki18n("Core Developer"), "rodda@kde.org"); + m_aboutData.addAuthor (ki18n("Waldo Bastian"), ki18n( "The cool buffersystem" ), "bastian@kde.org" ); + m_aboutData.addAuthor (ki18n("Charles Samuels"), ki18n("The Editing Commands"), "charles@kde.org"); + m_aboutData.addAuthor (ki18n("Matt Newell"), ki18n("Testing, ..."), "newellm@proaxis.com"); + m_aboutData.addAuthor (ki18n("Michael Bartl"), ki18n("Former Core Developer"), "michael.bartl1@chello.at"); + m_aboutData.addAuthor (ki18n("Michael McCallum"), ki18n("Core Developer"), "gholam@xtra.co.nz"); + m_aboutData.addAuthor (ki18n("Michael Koch"), ki18n("KWrite port to KParts"), "koch@kde.org"); + m_aboutData.addAuthor (ki18n("Christian Gebauer"), KLocalizedString(), "gebauer@kde.org" ); + m_aboutData.addAuthor (ki18n("Simon Hausmann"), KLocalizedString(), "hausmann@kde.org" ); + m_aboutData.addAuthor (ki18n("Glen Parker"), ki18n("KWrite Undo History, Kspell integration"), "glenebob@nwlink.com"); + m_aboutData.addAuthor (ki18n("Scott Manson"), ki18n("KWrite XML Syntax highlighting support"), "sdmanson@alltel.net"); + m_aboutData.addAuthor (ki18n("John Firebaugh"), ki18n("Patches and more"), "jfirebaugh@kde.org"); + m_aboutData.addAuthor (ki18n("Andreas Kling"), ki18n("Developer"), "kling@impul.se"); + m_aboutData.addAuthor (ki18n("Mirko Stocker"), ki18n("Various bugfixes"), "me@misto.ch", "http://misto.ch/"); + m_aboutData.addAuthor (ki18n("Matthew Woehlke"), ki18n("Selection, KColorScheme integration"), "mw_triad@users.sourceforge.net"); + m_aboutData.addAuthor (ki18n("Sebastian Pipping"), ki18n("Search bar back- and front-end"), "webmaster@hartwork.org", "http://www.hartwork.org/"); + m_aboutData.addAuthor (ki18n("Jochen Wilhelmy"), ki18n( "Original KWrite Author" ), "digisnap@cs.tu-berlin.de" ); + m_aboutData.addAuthor (ki18n("Gerald Senarclens de Grancy"), ki18n("QA and Scripting"), "oss@senarclens.eu", "http://find-santa.eu/"); + + m_aboutData.addCredit (ki18n("Matteo Merli"), ki18n("Highlighting for RPM Spec-Files, Perl, Diff and more"), "merlim@libero.it"); + m_aboutData.addCredit (ki18n("Rocky Scaletta"), ki18n("Highlighting for VHDL"), "rocky@purdue.edu"); + m_aboutData.addCredit (ki18n("Yury Lebedev"), ki18n("Highlighting for SQL"),""); + m_aboutData.addCredit (ki18n("Chris Ross"), ki18n("Highlighting for Ferite"),""); + m_aboutData.addCredit (ki18n("Nick Roux"), ki18n("Highlighting for ILERPG"),""); + m_aboutData.addCredit (ki18n("Carsten Niehaus"), ki18n("Highlighting for LaTeX"),""); + m_aboutData.addCredit (ki18n("Per Wigren"), ki18n("Highlighting for Makefiles, Python"),""); + m_aboutData.addCredit (ki18n("Jan Fritz"), ki18n("Highlighting for Python"),""); + m_aboutData.addCredit (ki18n("Daniel Naber")); + m_aboutData.addCredit (ki18n("Roland Pabel"), ki18n("Highlighting for Scheme"),""); + m_aboutData.addCredit (ki18n("Cristi Dumitrescu"), ki18n("PHP Keyword/Datatype list"),""); + m_aboutData.addCredit (ki18n("Carsten Pfeiffer"), ki18n("Very nice help"), ""); + m_aboutData.addCredit (ki18n("Bruno Massa"), ki18n("Highlighting for Lua"), "brmassa@gmail.com"); + + m_aboutData.addCredit (ki18n("All people who have contributed and I have forgotten to mention")); + + m_aboutData.setTranslator(ki18nc("NAME OF TRANSLATORS","Your names"), ki18nc("EMAIL OF TRANSLATORS","Your emails")); + + // + // dir watch + // + m_dirWatch = new KDirWatch (); + + // + // command manager + // + m_cmdManager = new KateCmd (); + + // + // hl manager + // + m_hlManager = new KateHlManager (); + + // + // mode man + // + m_modeManager = new KateModeManager (); + + // + // schema man + // + m_schemaManager = new KateSchemaManager (); + + // + // spell check manager + // + m_spellCheckManager = new KateSpellCheckManager (); + + // config objects + m_globalConfig = new KateGlobalConfig (); + m_documentConfig = new KateDocumentConfig (); + m_viewConfig = new KateViewConfig (); + m_rendererConfig = new KateRendererConfig (); + + // + // plugin manager + // + m_pluginManager = new KatePartPluginManager (); + + // + // init the cmds + // + m_cmds.push_back( KateCommands::CoreCommands::self() ); + m_cmds.push_back( KateCommands::AppCommands::self() ); + m_cmds.push_back( KateCommands::SedReplace::self() ); + m_cmds.push_back( KateCommands::Character::self() ); + m_cmds.push_back( KateCommands::Date::self() ); + + for ( QList::iterator it = m_cmds.begin(); it != m_cmds.end(); ++it ) + m_cmdManager->registerCommand (*it); + + // global word completion model + m_wordCompletionModel = new KateWordCompletionModel (this); + // global keyword completion model + m_keywordCompletionModel = new KateKeywordCompletionModel (this); + + // + // finally setup connections + // + connect(KGlobalSettings::self(), SIGNAL(kdisplayPaletteChanged()), this, SLOT(updateColorPalette())); + + //required for setting sessionConfig property + qRegisterMetaType("KSharedConfig::Ptr"); +} + +KateGlobal::~KateGlobal() +{ + delete m_pluginManager; + + delete m_globalConfig; + delete m_documentConfig; + delete m_viewConfig; + delete m_rendererConfig; + + delete m_modeManager; + delete m_schemaManager; + + delete m_dirWatch; + + // you too + qDeleteAll (m_cmds); + + // cu managers + delete m_hlManager; + delete m_cmdManager; + + delete m_spellCheckManager; + + // cu model + delete m_wordCompletionModel; + + s_self = 0; +} + +KTextEditor::Document *KateGlobal::createDocument ( QObject *parent ) +{ + KateDocument *doc = new KateDocument (false, false, false, 0, parent); + + emit documentCreated (this, doc); + + return doc; +} + +const QList &KateGlobal::documents () +{ + return m_docs; +} + +//BEGIN KTextEditor::Editor config stuff +void KateGlobal::readConfig(KConfig *config) +{ + if( !config ) + config = KGlobal::config().data(); + + KateGlobalConfig::global()->readConfig (KConfigGroup(config, "Kate Part Defaults")); + + KateDocumentConfig::global()->readConfig (KConfigGroup(config, "Kate Document Defaults")); + + KateViewConfig::global()->readConfig (KConfigGroup(config, "Kate View Defaults")); + + KateRendererConfig::global()->readConfig (KConfigGroup(config, "Kate Renderer Defaults")); +} + +void KateGlobal::writeConfig(KConfig *config) +{ + if( !config ) + config = KGlobal::config().data(); + + KConfigGroup cgGlobal(config, "Kate Part Defaults"); + KateGlobalConfig::global()->writeConfig (cgGlobal); + + KConfigGroup cg(config, "Kate Document Defaults"); + KateDocumentConfig::global()->writeConfig (cg); + + KConfigGroup cgDefault(config, "Kate View Defaults"); + KateViewConfig::global()->writeConfig (cgDefault); + + KConfigGroup cgRenderer(config, "Kate Renderer Defaults"); + KateRendererConfig::global()->writeConfig (cgRenderer); + + config->sync(); +} +//END KTextEditor::Editor config stuff + +bool KateGlobal::configDialogSupported () const +{ + return true; +} + +void KateGlobal::configDialog(QWidget *parent) +{ + QPointer kd = new KPageDialog(parent); + kd->setCaption( i18n("Configure") ); + kd->setButtons( KDialog::Ok | KDialog::Cancel | KDialog::Apply | KDialog::Help ); + kd->setFaceType( KPageDialog::List ); + kd->setHelp( QString(), KGlobal::mainComponent().componentName() ); + + QList editorPages; + + for (int i = 0; i < configPages (); ++i) + { + const QString name = configPageName (i); + + QFrame *page = new QFrame(); + + KPageWidgetItem *item = kd->addPage( page, name ); + item->setHeader( configPageFullName (i) ); + item->setIcon( configPageIcon(i) ); + + QVBoxLayout *topLayout = new QVBoxLayout( page ); + topLayout->setMargin( 0 ); + + KTextEditor::ConfigPage *cp = configPage(i, page); + connect(kd, SIGNAL(applyClicked ( )), cp, SLOT(apply())); + topLayout->addWidget( cp); + editorPages.append (cp); + } + + if (kd->exec() && kd) + { + KateGlobalConfig::global()->configStart (); + KateDocumentConfig::global()->configStart (); + KateViewConfig::global()->configStart (); + KateRendererConfig::global()->configStart (); + + for (int i=0; i < editorPages.count(); ++i) + { + editorPages.at(i)->apply(); + } + + KateGlobalConfig::global()->configEnd (); + KateDocumentConfig::global()->configEnd (); + KateViewConfig::global()->configEnd (); + KateRendererConfig::global()->configEnd (); + } + + delete kd; +} + +int KateGlobal::configPages () const +{ + return 5; +} + +KTextEditor::ConfigPage *KateGlobal::configPage (int number, QWidget *parent) +{ + switch( number ) + { + case 0: + return new KateViewDefaultsConfig (parent); + + case 1: + return new KateSchemaConfigPage (parent); + + case 2: + return new KateEditConfigTab (parent); + + case 3: + return new KateSaveConfigTab (parent); + + case 4: + return new KatePartPluginConfigPage (parent); + + default: + return 0; + } + + return 0; +} + +QString KateGlobal::configPageName (int number) const +{ + switch( number ) + { + case 0: + return i18n ("Appearance"); + + case 1: + return i18n ("Fonts & Colors"); + + case 2: + return i18n ("Editing"); + + case 3: + return i18n("Open/Save"); + + case 4: + return i18n ("Extensions"); + + default: + return QString (""); + } + + return QString (""); +} + +QString KateGlobal::configPageFullName (int number) const +{ + switch( number ) + { + case 0: + return i18n("Appearance"); + + case 1: + return i18n ("Font & Color Schemas"); + + case 2: + return i18n ("Editing Options"); + + case 3: + return i18n("File Opening & Saving"); + + case 4: + return i18n ("Extensions Manager"); + + default: + return QString (""); + } + + return QString (""); +} + +KIcon KateGlobal::configPageIcon (int number) const +{ + switch( number ) + { + case 0: + return KIcon("preferences-desktop-theme"); + + case 1: + return KIcon("preferences-desktop-color"); + + case 2: + return KIcon("accessories-text-editor"); + + case 3: + return KIcon("document-save"); + + case 4: + return KIcon("preferences-plugin"); + + default: + return KIcon("document-properties"); + } + + return KIcon("document-properties"); +} + +KateGlobal *KateGlobal::self () +{ + if (!s_self) { + new KateGlobal (); + } + + return s_self; +} + +void KateGlobal::registerDocument ( KateDocument *doc ) +{ + KateGlobal::incRef (); + m_documents.append( doc ); + m_docs.append (doc); +} + +void KateGlobal::deregisterDocument ( KateDocument *doc ) +{ + m_docs.removeAll (doc); + m_documents.removeAll( doc ); + KateGlobal::decRef (); +} + +void KateGlobal::registerView ( KateView *view ) +{ + KateGlobal::incRef (); + m_views.append( view ); +} + +void KateGlobal::deregisterView ( KateView *view ) +{ + m_views.removeAll( view ); + KateGlobal::decRef (); +} + +//BEGIN command interface +bool KateGlobal::registerCommand (KTextEditor::Command *cmd) +{return m_cmdManager->registerCommand(cmd);} + +bool KateGlobal::unregisterCommand (KTextEditor::Command *cmd) +{return m_cmdManager->unregisterCommand(cmd);} + +KTextEditor::Command *KateGlobal::queryCommand (const QString &cmd) const +{return m_cmdManager->queryCommand(cmd);} + +QList KateGlobal::commands() const +{return m_cmdManager->commands();} + +QStringList KateGlobal::commandList() const +{return m_cmdManager->commandList();} +//END command interface + + +//BEGIN container interface +QObject * KateGlobal::container() +{return m_container.data();} + +void KateGlobal::setContainer( QObject * container ) +{m_container=container;} +//END container interface + +void KateGlobal::updateColorPalette() +{ + // reload the global schema (triggers reload for every view as well) + m_rendererConfig->reloadSchema(); + + // force full update of all view caches and colors + m_rendererConfig->updateConfig(); +} + +void KateGlobal::copyToClipboard (const QString &text) +{ + /** + * empty => nop + */ + if (text.isEmpty()) + return; + + /** + * move to clipboard + */ + QApplication::clipboard()->setText (text, QClipboard::Clipboard); + + /** + * remember in history + * cut after 10 entries + */ + m_clipboardHistory.prepend (text); + if (m_clipboardHistory.size () > 10) + m_clipboardHistory.removeLast (); + + /** + * notify about change + */ + emit clipboardHistoryChanged (); +} + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/utils/kateglobal.h b/kate/part/utils/kateglobal.h new file mode 100644 index 00000000..2a6a35e4 --- /dev/null +++ b/kate/part/utils/kateglobal.h @@ -0,0 +1,542 @@ +/* This file is part of the KDE libraries and the Kate part. + * + * Copyright (C) 2001-2010 Christoph Cullmann + * Copyright (C) 2009 Erlend Hamberg + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __KATE_GLOBAL_H__ +#define __KATE_GLOBAL_H__ + +#include "katepartinterfaces_export.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class KateCmd; +class KateModeManager; +class KateSchemaManager; +class KateGlobalConfig; +class KateDocumentConfig; +class KateViewConfig; +class KateRendererConfig; +class KateDocument; +class KateRenderer; +class KateView; +class KDirWatch; +class KateHlManager; +class KatePartPluginManager; +class KateSpellCheckManager; +class KateViGlobal; +class KateWordCompletionModel; +class KateKeywordCompletionModel; + +namespace Kate { + class Command; +} + +Q_DECLARE_METATYPE(KSharedConfig::Ptr) + +/** + * loader block size, load 256 kb at once per default + * if file size is smaller, fall back to file size + * must be a multiple of 2 + */ +static const qint64 KATE_FILE_LOADER_BS = QT_BUFFSIZE; + +/** + * loader hash sum algorithm + */ +#if QT_VERSION >= 0x041200 +static const QCryptographicHash::Algorithm KATE_HASH_ALGORITHM = QCryptographicHash::KAT; +#else +static const QCryptographicHash::Algorithm KATE_HASH_ALGORITHM = QCryptographicHash::Sha1; +#endif + +/** + * KateGlobal + * One instance of this class is hold alive during + * a kate part session, as long as any factory, document + * or view stay around, here is the place to put things + * which are needed and shared by all this objects ;) + */ +class KATEPARTINTERFACES_EXPORT KateGlobal : public KTextEditor::Editor, public KTextEditor::CommandInterface, public KTextEditor::ContainerInterface +{ + Q_OBJECT + Q_INTERFACES(KTextEditor::CommandInterface) + Q_INTERFACES(KTextEditor::ContainerInterface) + + public: + /** + * property to tell the editor to use a given session config for session related + * configuration instead of KGlobal::config(). + * MUST be set directly after first creation of the editor as otherwise + * some parts might not pick this up. + */ + Q_PROPERTY (KSharedConfig::Ptr sessionConfig READ sessionConfig WRITE setSessionConfig) + + /** + * Get session config, defaults to KGlobal::config() + * @return session config + */ + KSharedConfig::Ptr sessionConfig () + { + return m_sessionConfig; + } + + /** + * Set session config + * @param sessionConfig new session config + */ + void setSessionConfig (KSharedConfig::Ptr sessionConfig) + { + m_sessionConfig = sessionConfig; + } + + // for setDefaultEncoding + friend class KateDocumentConfig; + + private: + /** + * Default constructor, private, as singleton + */ + KateGlobal (); + + public: + /** + * Destructor + */ + ~KateGlobal (); + + /** + * Create a new document object + * @param parent parent object + * @return created KTextEditor::Document + */ + KTextEditor::Document *createDocument ( QObject *parent ); + + /** + * Returns a list of all documents of this editor. + * @return list of all existing documents + */ + const QList &documents (); + + /** + * General Information about this editor + */ + public: + /** + * return the about data + * @return about data of this editor part + */ + const KAboutData* aboutData() const { return &m_aboutData; } + + /** + * Configuration management + */ + public: + /** + * Read editor configuration from given config object + * @param config config object + */ + void readConfig (KConfig *config = 0); + + /** + * Write editor configuration to given config object + * @param config config object + */ + void writeConfig (KConfig *config = 0); + + /** + * Does this editor support a config dialog + * @return does this editor have a config dialog? + */ + bool configDialogSupported () const; + + /** + * Shows a config dialog for the part, changes will be applied + * to the editor, but not saved anywhere automagically, call + * writeConfig to save them + */ + void configDialog (QWidget *parent); + + /** + * Number of available config pages + * If the editor returns a number < 1, it doesn't support this + * and the embedding app should use the configDialog () instead + * @return number of config pages + */ + int configPages () const; + + /** + * returns config page with the given number, + * config pages from 0 to configPages()-1 are available + * if configPages() > 0 + */ + KTextEditor::ConfigPage *configPage (int number, QWidget *parent); + + QString configPageName (int number) const; + + QString configPageFullName (int number) const; + + KIcon configPageIcon (int number) const; + + /** + * Kate Part Internal stuff ;) + */ + public: + /** + * singleton accessor + * @return instance of the factory + */ + static KateGlobal *self (); + + /** + * increment reference counter + */ + static void incRef () { ++s_ref; } + + /** + * decrement reference counter + */ + static void decRef () { if (s_ref > 0) --s_ref; if (s_ref == 0) { delete s_self; s_self = 0L; } } + + /** + * Returns the current version of Kate Part: KDE X.Y.Z contains Kate Part X-1.Y + * @return version of type x.y, e.g. 3.2 + */ + static QString katePartVersion(); + + /** + * public accessor to the instance + * @return instance + */ + const KComponentData &componentData() { return m_componentData; } + + /** + * register document at the factory + * this allows us to loop over all docs for example on config changes + * @param doc document to register + */ + void registerDocument ( KateDocument *doc ); + + /** + * unregister document at the factory + * @param doc document to register + */ + void deregisterDocument ( KateDocument *doc ); + + /** + * register view at the factory + * this allows us to loop over all views for example on config changes + * @param view view to register + */ + void registerView ( KateView *view ); + + /** + * unregister view at the factory + * @param view view to unregister + */ + void deregisterView ( KateView *view ); + + /** + * return a list of all registered docs + * @return all known documents + */ + QList &kateDocuments () { return m_documents; } + + /** + * return a list of all registered views + * @return all known views + */ + QList &views () { return m_views; } + + /** + * global plugin manager + * @return kate part plugin manager + */ + KatePartPluginManager *pluginManager () { return m_pluginManager; } + + /** + * global dirwatch + * @return dirwatch instance + */ + KDirWatch *dirWatch () { return m_dirWatch; } + + /** + * global mode manager + * used to manage the modes centrally + * @return mode manager + */ + KateModeManager *modeManager () { return m_modeManager; } + + /** + * manager for the katepart schemas + * @return schema manager + */ + KateSchemaManager *schemaManager () { return m_schemaManager; } + + /** + * fallback document config + * @return default config for all documents + */ + KateDocumentConfig *documentConfig () { return m_documentConfig; } + + /** + * fallback view config + * @return default config for all views + */ + KateViewConfig *viewConfig () { return m_viewConfig; } + + /** + * fallback renderer config + * @return default config for all renderers + */ + KateRendererConfig *rendererConfig () { return m_rendererConfig; } + + /** + * hl manager + * @return hl manager + */ + KateHlManager *hlManager () { return m_hlManager; } + + /** + * command manager + * @return command manager + */ + KateCmd *cmdManager () { return m_cmdManager; } + + /** + * spell check manager + * @return spell check manager + */ + KateSpellCheckManager *spellCheckManager () { return m_spellCheckManager; } + + /** + * global instance of the simple word completion mode + * @return global instance of the simple word completion mode + */ + KateWordCompletionModel *wordCompletionModel () { return m_wordCompletionModel; } + + /** + * global instance of the language-aware keyword completion model + * @return global instance of the keyword completion model + */ + KateKeywordCompletionModel *keywordCompletionModel () { return m_keywordCompletionModel; } + + /** + * register given command + * this works global, for all documents + * @param cmd command to register + * @return success + */ + bool registerCommand (KTextEditor::Command *cmd); + + /** + * unregister given command + * this works global, for all documents + * @param cmd command to unregister + * @return success + */ + bool unregisterCommand (KTextEditor::Command *cmd); + + /** + * query for command + * @param cmd name of command to query for + * @return found command or 0 + */ + KTextEditor::Command *queryCommand (const QString &cmd) const; + + /** + * Get a list of all registered commands. + * \return list of all commands + */ + QList commands() const; + + /** + * Get a list of available commandline strings. + * \return commandline strings + */ + QStringList commandList() const; + + + /** + * Get the currently associated Container object + * \return container object + */ + QObject * container(); + + /** + * Set the associated container object + */ + void setContainer( QObject * container ); + + /** + * Copy text to clipboard an remember it in the history + * @param text text to copy to clipboard, does nothing if empty! + */ + void copyToClipboard (const QString &text); + + /** + * Clipboard history, filled with text we ever copied + * to clipboard via copyToClipboard. + */ + const QStringList &clipboardHistory () const + { + return m_clipboardHistory; + } + + Q_SIGNALS: + /** + * Emitted if the history of clipboard changes via copyToClipboard + */ + void clipboardHistoryChanged (); + + private Q_SLOTS: + void updateColorPalette(); + + private: + /** + * instance of this factory + */ + static KateGlobal *s_self; + + /** + * reference counter + */ + static int s_ref; + + /** + * about data (authors and more) + */ + KAboutData m_aboutData; + + /** + * our kinstance + */ + KComponentData m_componentData; + + /** + * registered docs + */ + QList m_documents; + + /** + * registered views + */ + QList m_views; + + /** + * global dirwatch object + */ + KDirWatch *m_dirWatch; + + /** + * mode manager + */ + KateModeManager *m_modeManager; + + /** + * schema manager + */ + KateSchemaManager *m_schemaManager; + + /** + * at start found plugins + */ + KatePartPluginManager *m_pluginManager; + + /** + * global config + */ + KateGlobalConfig *m_globalConfig; + + /** + * fallback document config + */ + KateDocumentConfig *m_documentConfig; + + /** + * fallback view config + */ + KateViewConfig *m_viewConfig; + + /** + * fallback renderer config + */ + KateRendererConfig *m_rendererConfig; + + /** + * internal commands + */ + QList m_cmds; + + /** + * hl manager + */ + KateHlManager *m_hlManager; + + /** + * command manager + */ + KateCmd *m_cmdManager; + + /** + * spell check manager + */ + KateSpellCheckManager *m_spellCheckManager; + + QList m_docs; + + /** + * container interface + */ + QPointer m_container; + + /** + * global instance of the simple word completion mode + */ + KateWordCompletionModel *m_wordCompletionModel; + + /** + * global instance of the language-specific keyword completion model + */ + KateKeywordCompletionModel *m_keywordCompletionModel; + + /** + * session config + */ + KSharedConfig::Ptr m_sessionConfig; + + /** + * clipboard history + */ + QStringList m_clipboardHistory; +}; + +#endif + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/utils/katepartpluginmanager.cpp b/kate/part/utils/katepartpluginmanager.cpp new file mode 100644 index 00000000..058bb5b3 --- /dev/null +++ b/kate/part/utils/katepartpluginmanager.cpp @@ -0,0 +1,301 @@ +/* This file is part of the KDE libraries and the Kate part. + * + * Copyright (C) 2001-2010 Christoph Cullmann + * Copyright (C) 2001 Joseph Wenninger + * Copyright (C) 2001 Anders Lund + * Copyright (C) 2007 Dominik Haumann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "katepartpluginmanager.h" +#include "moc_katepartpluginmanager.cpp" + +#include "kateglobal.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +KatePartPluginInfo::KatePartPluginInfo(KService::Ptr service) + : m_pluginInfo(service) +{ +} + +QString KatePartPluginInfo::saveName() const +{ + QString saveName = m_pluginInfo.pluginName(); + if (saveName.isEmpty()) + saveName = service()->library(); + return saveName; +} + +KatePartPluginManager::KatePartPluginManager() + : QObject(), + m_config(new KConfig("katepartpluginsrc", KConfig::NoGlobals)) +{ + setupPluginList (); + loadConfig (); +} + +KatePartPluginManager::~KatePartPluginManager() +{ + writeConfig(); + // than unload the plugins + unloadAllPlugins (); + delete m_config; + m_config = 0; +} + +KatePartPluginManager *KatePartPluginManager::self() +{ + return KateGlobal::self()->pluginManager (); +} + +void KatePartPluginManager::setupPluginList () +{ + KService::List traderList = KServiceTypeTrader::self()-> + query("KTextEditor/Plugin"); + + foreach(const KService::Ptr &ptr, traderList) + { + QVariant version = ptr->property("X-KDE-Version", QVariant::String); + QStringList numbers = qvariant_cast(version).split('.'); + unsigned int kdeVersion = KDE_MAKE_VERSION(numbers.value(0).toUInt(), + numbers.value(1).toUInt(), + numbers.value(2).toUInt()); + + if (KDE_MAKE_VERSION(4,0,0) <= kdeVersion && kdeVersion <= KDE::version()) + { + KatePartPluginInfo info(ptr); + + info.load = false; + info.plugin = 0L; + + m_pluginList.push_back (info); + } + } +} + +void KatePartPluginManager::addDocument(KTextEditor::Document *doc) +{ + //kDebug() << doc; + for (KatePartPluginList::iterator it = m_pluginList.begin(); + it != m_pluginList.end(); ++it) + { + if (it->load) { + it->plugin->addDocument(doc); + } + } +} + +void KatePartPluginManager::removeDocument(KTextEditor::Document *doc) +{ + //kDebug() << doc; + for (KatePartPluginList::iterator it = m_pluginList.begin(); + it != m_pluginList.end(); ++it) + { + if (it->load) { + it->plugin->removeDocument(doc); + } + } +} + +void KatePartPluginManager::addView(KTextEditor::View *view) +{ + //kDebug() << view; + for (KatePartPluginList::iterator it = m_pluginList.begin(); + it != m_pluginList.end(); ++it) + { + if (it->load) { + it->plugin->addView(view); + } + } +} + +void KatePartPluginManager::removeView(KTextEditor::View *view) +{ + //kDebug() << view; + for (KatePartPluginList::iterator it = m_pluginList.begin(); + it != m_pluginList.end(); ++it) + { + if (it->load) { + it->plugin->removeView(view); + } + } +} + +void KatePartPluginManager::loadConfig () +{ + // first: unload the plugins + unloadAllPlugins (); + + KConfigGroup cg = KConfigGroup(m_config, "Kate Part Plugins"); + + // disable all plugin if no config... + foreach (const KatePartPluginInfo &plugin, m_pluginList) { + bool enabledByDefault = plugin.isEnabledByDefault(); + plugin.load = cg.readEntry (plugin.saveName(), enabledByDefault); + } + + loadAllPlugins(); +} + +void KatePartPluginManager::writeConfig() +{ + KConfigGroup cg = KConfigGroup( m_config, "Kate Part Plugins" ); + foreach(const KatePartPluginInfo &it, m_pluginList) + { + cg.writeEntry (it.saveName(), it.load); + } +} + +void KatePartPluginManager::loadAllPlugins () +{ + for (KatePartPluginList::iterator it = m_pluginList.begin(); + it != m_pluginList.end(); ++it) + { + if (it->load) + { + loadPlugin(*it); + enablePlugin(*it); + } + } +} + +void KatePartPluginManager::unloadAllPlugins () +{ + for (KatePartPluginList::iterator it = m_pluginList.begin(); + it != m_pluginList.end(); ++it) + { + if (it->plugin) { + disablePlugin(*it); + unloadPlugin(*it); + } + } +} + +void KatePartPluginManager::loadPlugin (KatePartPluginInfo &item) +{ + if (item.plugin) return; + + // make sure all dependencies are loaded beforehand + QStringList openDependencies = item.dependencies(); + if ( !openDependencies.empty() ) + { + for (KatePartPluginList::iterator it = m_pluginList.begin(); + it != m_pluginList.end(); ++it) + { + if ( openDependencies.contains( it->saveName() ) ) + { + loadPlugin( *it ); + openDependencies.removeAll( it->saveName() ); + } + } + Q_ASSERT( openDependencies.empty() ); + } + + // try to load, else reset load flag! + QString error; + item.plugin = item.service()->createInstance(this, QVariantList(), &error); + if ( !item.plugin ) + kWarning() << "failed to load plugin" << item.service()->name() << ":" << error; + item.load = (item.plugin != 0); +} + +void KatePartPluginManager::unloadPlugin (KatePartPluginInfo &item) +{ + if ( !item.plugin ) return; + + // make sure dependent plugins are unloaded beforehand + for (KatePartPluginList::iterator it = m_pluginList.begin(); + it != m_pluginList.end(); ++it) + { + if ( !it->plugin ) continue; + + if ( it->dependencies().contains( item.saveName() ) ) + { + unloadPlugin( *it ); + } + } + + delete item.plugin; + item.plugin = 0L; + item.load = false; +} + +void KatePartPluginManager::enablePlugin (KatePartPluginInfo &item) +{ + // plugin around at all? + if (!item.plugin || !item.load) + return; + + // register docs and views + foreach (KTextEditor::Document *doc, KateGlobal::self()->documents()) { + if (!doc) + continue; + + foreach (KTextEditor::View *view, doc->views()) { + if (!view) + continue; + + KXMLGUIFactory *viewFactory = view->factory(); + if (viewFactory) + viewFactory->removeClient(view); + + item.plugin->addView(view); + + if (viewFactory) + viewFactory->addClient(view); + } + } +} + +void KatePartPluginManager::disablePlugin (KatePartPluginInfo &item) +{ + // plugin around at all? + if (!item.plugin || !item.load) + return; + + // de-register docs and views + foreach (KTextEditor::Document *doc, KateGlobal::self()->documents()) { + if (!doc) + continue; + + foreach (KTextEditor::View *view, doc->views()) { + if (!view) + continue; + + KXMLGUIFactory *viewFactory = view->factory(); + if (viewFactory) + viewFactory->removeClient(view); + + item.plugin->removeView(view); + + if (viewFactory) + viewFactory->addClient(view); + } + } +} + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/utils/katepartpluginmanager.h b/kate/part/utils/katepartpluginmanager.h new file mode 100644 index 00000000..5543bb0f --- /dev/null +++ b/kate/part/utils/katepartpluginmanager.h @@ -0,0 +1,98 @@ +/* This file is part of the KDE libraries and the Kate part. + * + * Copyright (C) 2001-2010 Christoph Cullmann + * Copyright (C) 2001 Joseph Wenninger + * Copyright (C) 2001 Anders Lund + * Copyright (C) 2007 Dominik Haumann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef KATEPARTPLUGINMANAGER_H +#define KATEPARTPLUGINMANAGER_H + +#include +#include + +#include +#include + +namespace KTextEditor { + class Plugin; + class Document; + class View; +} + +class KatePartPluginInfo +{ + public: + KatePartPluginInfo(KService::Ptr service); + mutable bool load; + KTextEditor::Plugin *plugin; + QString saveName() const; + KService::Ptr service() const { return m_pluginInfo.service(); } + QStringList dependencies() const { return m_pluginInfo.dependencies(); } + bool isEnabledByDefault() const { return m_pluginInfo.isPluginEnabledByDefault(); } + private: + KPluginInfo m_pluginInfo; + QString m_saveName; +}; + +typedef QList KatePartPluginList; + +class KatePartPluginManager : public QObject +{ + Q_OBJECT + + public: + KatePartPluginManager(); + ~KatePartPluginManager(); + + static KatePartPluginManager *self(); + + void loadConfig (); + void writeConfig (); + + void addDocument(KTextEditor::Document *doc); + void removeDocument(KTextEditor::Document *doc); + + void addView(KTextEditor::View *view); + void removeView(KTextEditor::View *view); + + void loadAllPlugins (); + void unloadAllPlugins (); + + void loadPlugin (KatePartPluginInfo &item); + void unloadPlugin (KatePartPluginInfo &item); + + void enablePlugin (KatePartPluginInfo &item); + void disablePlugin (KatePartPluginInfo &item); + + inline KatePartPluginList & pluginList () + { + return m_pluginList; + } + + private: + void setupPluginList (); + + KConfig *m_config; + KatePartPluginList m_pluginList; +}; + +#endif // KATEPARTPLUGINMANAGER_H + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/utils/kateprinter.cpp b/kate/part/utils/kateprinter.cpp new file mode 100644 index 00000000..93db1c92 --- /dev/null +++ b/kate/part/utils/kateprinter.cpp @@ -0,0 +1,1275 @@ +/* This file is part of the KDE libraries and the Kate part. + * + * Copyright (C) 2002-2010 Anders Lund + * + * Rewritten based on code of Copyright (c) 2002 Michael Goffioul + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "kateprinter.h" + +#include "kateconfig.h" +#include "katedocument.h" +#include "kateglobal.h" +#include "katehighlight.h" +#include "katetextlayout.h" +#include "katerenderer.h" +#include "kateschema.h" +#include "katetextline.h" +#include "kateview.h" +#include "katebuffer.h" +#include "katetextfolding.h" + +#include +#include +#include +#include +#include +#include +#include +#include // for loginName +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +//BEGIN KatePrinter +void KatePrinter::readSettings(QPrinter& printer) +{ + // NOTE: Saving & loading the margins works around QPrinter/QPrintDialog bugs: + // - https://bugreports.qt.nokia.com/browse/QTBUG-15351 + // - https://bugs.kde.org/show_bug.cgi?id=205802 + // - https://bugs.kde.org/show_bug.cgi?id=180051 + // Changing the margins now works. However, when you reopen the print dialog + // later, the WRONG margins are displayed. The correct ones are still used. + // This is a critical bug in Qt. + + KSharedConfigPtr config = KGlobal::config(); + KConfigGroup group(config, "Kate Print Settings"); + KConfigGroup margins(&group, "Margins"); + + qreal left, right, top, bottom; + printer.getPageMargins(&left, &top, &right, &bottom, QPrinter::Millimeter); + + left = margins.readEntry("left", left); + top = margins.readEntry("top", top); + right = margins.readEntry("right", right); + bottom = margins.readEntry("bottom", bottom); + + printer.setPageMargins(left, top, right, bottom, QPrinter::Millimeter); +} + +void KatePrinter::writeSettings(QPrinter& printer) +{ + KSharedConfigPtr config = KGlobal::config(); + KConfigGroup group(config, "Kate Print Settings"); + KConfigGroup margins(&group, "Margins"); + + qreal left, right, top, bottom; + printer.getPageMargins(&left, &top, &right, &bottom, QPrinter::Millimeter); + + margins.writeEntry( "left", left); + margins.writeEntry( "top", top); + margins.writeEntry( "right", right); + margins.writeEntry( "bottom", bottom); +} + +bool KatePrinter::print (KateDocument *doc) +{ + QPrinter printer; + readSettings(printer); + + // docname is now always there, including the right Untitled name + printer.setDocName(doc->documentName()); + + KatePrintTextSettings *kpts = new KatePrintTextSettings; + KatePrintHeaderFooter *kphf = new KatePrintHeaderFooter; + KatePrintLayout *kpl = new KatePrintLayout; + + QList tabs; + tabs << kpts; + tabs << kphf; + tabs << kpl; + + QWidget *parentWidget=doc->widget(); + + if ( !parentWidget ) + parentWidget=QApplication::activeWindow(); + + QScopedPointer printDialog(KdePrint::createPrintDialog(&printer, KdePrint::SystemSelectsPages, tabs, parentWidget)); + + if ( doc->activeView()->selection() ) { + printer.setPrintRange(QPrinter::Selection); + printDialog->setOption(QAbstractPrintDialog::PrintSelection, true); + } + + if ( printDialog->exec() ) + { + writeSettings(printer); + + Kate::TextFolding folding (doc->buffer()); + KateRenderer renderer(doc, folding, doc->activeKateView()); + renderer.config()->setSchema (kpl->colorScheme()); + renderer.setPrinterFriendly(true); + + QPainter paint( &printer ); + /* + * We work in tree cycles: + * 1) initialize variables and retrieve print settings + * 2) prepare data according to those settings + * 3) draw to the printer + */ + uint pdmWidth = printer.width(); + uint pdmHeight = printer.height(); + int y = 0; + uint xstart = 0; // beginning point for painting lines + uint lineCount = 0; + uint maxWidth = pdmWidth; + int headerWidth = pdmWidth; + bool pageStarted = true; + int remainder = 0; // remaining sublines from a wrapped line (for the top of a new page) + + // Text Settings Page + bool selectionOnly = (printDialog->printRange() == QAbstractPrintDialog::Selection); + bool useGuide = kpts->printGuide(); + + bool printLineNumbers = kpts->printLineNumbers(); + uint lineNumberWidth( 0 ); + + // Header/Footer Page + QFont headerFont(kphf->font()); // used for header/footer + + bool useHeader = kphf->useHeader(); + QColor headerBgColor(kphf->headerBackground()); + QColor headerFgColor(kphf->headerForeground()); + uint headerHeight( 0 ); // further init only if needed + QStringList headerTagList; // do + bool headerDrawBg = false; // do + + bool useFooter = kphf->useFooter(); + QColor footerBgColor(kphf->footerBackground()); + QColor footerFgColor(kphf->footerForeground()); + uint footerHeight( 0 ); // further init only if needed + QStringList footerTagList; // do + bool footerDrawBg = false; // do + + // Layout Page + renderer.config()->setSchema( kpl->colorScheme() ); + bool useBackground = kpl->useBackground(); + bool useBox = kpl->useBox(); + int boxWidth(kpl->boxWidth()); + QColor boxColor(kpl->boxColor()); + int innerMargin = useBox ? kpl->boxMargin() : 6; + + // Post initialization + int maxHeight = (useBox ? pdmHeight-innerMargin : pdmHeight); + uint currentPage( 1 ); + uint lastline = doc->lastLine(); // necessary to print selection only + uint firstline( 0 ); + const int fontHeight = renderer.fontHeight(); + KTextEditor::Range selectionRange; + + /* + * Now on for preparations... + * during preparations, variable names starting with a "_" means + * those variables are local to the enclosing block. + */ + { + if ( selectionOnly ) + { + // set a line range from the first selected line to the last + selectionRange = doc->activeView()->selectionRange(); + firstline = selectionRange.start().line(); + lastline = selectionRange.end().line(); + lineCount = firstline; + } + + if ( printLineNumbers ) + { + // figure out the horiizontal space required + QString s( QString("%1 ").arg( doc->lines() ) ); + s.fill('5', -1); // some non-fixed fonts haven't equally wide numbers + // FIXME calculate which is actually the widest... + lineNumberWidth = renderer.currentFontMetrics().width( s ); + // a small space between the line numbers and the text + int _adj = renderer.currentFontMetrics().width( "5" ); + // adjust available width and set horizontal start point for data + maxWidth -= (lineNumberWidth + _adj); + xstart += lineNumberWidth + _adj; + } + + if ( useHeader || useFooter ) + { + // Set up a tag map + // This retrieves all tags, ued or not, but + // none of theese operations should be expensive, + // and searcing each tag in the format strings is avoided. + QDateTime dt = QDateTime::currentDateTime(); + QMap tags; + + KUser u (KUser::UseRealUserID); + tags["u"] = u.loginName(); + + tags["d"] = KGlobal::locale()->formatDateTime(dt, KLocale::ShortDate); + tags["D"] = KGlobal::locale()->formatDateTime(dt, KLocale::LongDate); + tags["h"] = KGlobal::locale()->formatTime(dt.time(), false); + tags["y"] = KGlobal::locale()->formatDate(dt.date(), KLocale::ShortDate); + tags["Y"] = KGlobal::locale()->formatDate(dt.date(), KLocale::LongDate); + tags["f"] = doc->url().fileName(); + tags["U"] = doc->url().pathOrUrl(); + if ( selectionOnly ) + { + QString s( i18n("(Selection of) ") ); + tags["f"].prepend( s ); + tags["U"].prepend( s ); + } + + QRegExp reTags( "%([dDfUhuyY])" ); // TODO tjeck for "%%" + + if (useHeader) + { + headerDrawBg = kphf->useHeaderBackground(); + headerHeight = QFontMetrics( headerFont ).height(); + if ( useBox || headerDrawBg ) + headerHeight += innerMargin * 2; + else + headerHeight += 1 + QFontMetrics( headerFont ).leading(); + + headerTagList = kphf->headerFormat(); + QMutableStringListIterator it(headerTagList); + while ( it.hasNext() ) { + QString tag = it.next(); + int pos = reTags.indexIn( tag ); + QString rep; + while ( pos > -1 ) + { + rep = tags[reTags.cap( 1 )]; + tag.replace( (uint)pos, 2, rep ); + pos += rep.length(); + pos = reTags.indexIn( tag, pos ); + } + it.setValue( tag ); + } + + if (!headerBgColor.isValid()) + headerBgColor = Qt::lightGray; + if (!headerFgColor.isValid()) + headerFgColor = Qt::black; + } + + if (useFooter) + { + footerDrawBg = kphf->useFooterBackground(); + footerHeight = QFontMetrics( headerFont ).height(); + if ( useBox || footerDrawBg ) + footerHeight += 2*innerMargin; + else + footerHeight += 1; // line only + + footerTagList = kphf->footerFormat(); + QMutableStringListIterator it(footerTagList); + while ( it.hasNext() ) { + QString tag = it.next(); + int pos = reTags.indexIn( tag ); + QString rep; + while ( pos > -1 ) + { + rep = tags[reTags.cap( 1 )]; + tag.replace( (uint)pos, 2, rep ); + pos += rep.length(); + pos = reTags.indexIn( tag, pos ); + } + it.setValue( tag ); + } + + if (!footerBgColor.isValid()) + footerBgColor = Qt::lightGray; + if (!footerFgColor.isValid()) + footerFgColor = Qt::black; + // adjust maxheight, so we can know when/where to print footer + maxHeight -= footerHeight; + } + } // if ( useHeader || useFooter ) + + if ( useBackground ) + { + if ( ! useBox ) + { + xstart += innerMargin; + maxWidth -= innerMargin * 2; + } + } + + if ( useBox ) + { + if (!boxColor.isValid()) + boxColor = Qt::black; + if (boxWidth < 1) // shouldn't be pssible no more! + boxWidth = 1; + // set maxwidth to something sensible + maxWidth -= ( ( boxWidth + innerMargin ) * 2 ); + xstart += boxWidth + innerMargin; + // maxheight too.. + maxHeight -= boxWidth; + } + else + boxWidth = 0; + + // now that we know the vertical amount of space needed, + // it is possible to calculate the total number of pages + // if needed, that is if any header/footer tag contains "%P". + if ( !headerTagList.filter("%P").isEmpty() || !footerTagList.filter("%P").isEmpty() ) + { + kDebug(13020)<<"'%P' found! calculating number of pages..."; + int pageHeight = maxHeight; + if ( useHeader ) + pageHeight -= ( headerHeight + innerMargin ); + if ( useFooter ) + pageHeight -= innerMargin; + const int linesPerPage = pageHeight / fontHeight; +// kDebug() << "Lines per page:" << linesPerPage; + + // calculate total layouted lines in the document + int totalLines = 0; + // TODO: right now ignores selection printing + for (unsigned int i = firstline; i <= lastline; ++i) { + KateLineLayoutPtr rangeptr(new KateLineLayout(renderer)); + rangeptr->setLine(i); + renderer.layoutLine(rangeptr, (int)maxWidth, false); + totalLines += rangeptr->viewLineCount(); + } + int totalPages = (totalLines / linesPerPage) + + ((totalLines % linesPerPage) > 0 ? 1 : 0); +// kDebug() << "_______ pages:" << (totalLines / linesPerPage); +// kDebug() << "________ rest:" << (totalLines % linesPerPage); + + // TODO: add space for guide if required +// if ( useGuide ) +// _lt += (guideHeight + (fontHeight /2)) / fontHeight; + + // substitute both tag lists + QString re("%P"); + QStringList::Iterator it; + for ( it=headerTagList.begin(); it!=headerTagList.end(); ++it ) + (*it).replace( re, QString( "%1" ).arg( totalPages ) ); + for ( it=footerTagList.begin(); it!=footerTagList.end(); ++it ) + (*it).replace( re, QString( "%1" ).arg( totalPages ) ); + } + } // end prepare block + + /* + On to draw something :-) + */ + while ( lineCount <= lastline ) + { + if ( y + fontHeight > maxHeight ) + { + kDebug(13020)<<"Starting new page,"<backgroundColor()); + } + + if ( useBox ) + { + paint.setPen(QPen(boxColor, boxWidth)); + paint.drawRect(0, 0, pdmWidth, pdmHeight); + if (useHeader) + paint.drawLine(0, headerHeight, headerWidth, headerHeight); + else + y += innerMargin; + + if ( useFooter ) // drawline is not trustable, grr. + paint.fillRect( 0, maxHeight+innerMargin, headerWidth, boxWidth, boxColor ); + } + + if ( useGuide && currentPage == 1 ) + { // FIXME - this may span more pages... + // draw a box unless we have boxes, in which case we end with a box line + int _ystart = y; + QString _hlName = doc->highlight()->name(); + + QList _attributes; // list of highlight attributes for the legend + doc->highlight()->getKateExtendedAttributeList(kpl->colorScheme(), _attributes); + + KateAttributeList _defaultAttributes; + KateHlManager::self()->getDefaults ( renderer.config()->schema(), _defaultAttributes ); + + QColor _defaultPen = _defaultAttributes.at(0)->foreground().color(); + paint.setPen(_defaultPen); + + int _marg = 0; + if ( useBox ) + _marg += (2*boxWidth) + (2*innerMargin); + else + { + if ( useBackground ) + _marg += 2*innerMargin; + _marg += 1; + y += 1 + innerMargin; + } + + // draw a title string + QFont _titleFont = renderer.config()->font(); + _titleFont.setBold(true); + paint.setFont( _titleFont ); + QRect _r; + paint.drawText( QRect(_marg, y, pdmWidth-(2*_marg), maxHeight - y), + Qt::AlignTop|Qt::AlignHCenter, + i18n("Typographical Conventions for %1", _hlName ), &_r ); + int _w = pdmWidth - (_marg*2) - (innerMargin*2); + int _x = _marg + innerMargin; + y += _r.height() + innerMargin; + paint.drawLine( _x, y, _x + _w, y ); + y += 1 + innerMargin; + + int _widest( 0 ); + foreach (const KateExtendedAttribute::Ptr &attribute, _attributes) + _widest = qMax(QFontMetrics(attribute->font()).width(attribute->name().section(':',1,1)), _widest); + + int _guideCols = _w/( _widest + innerMargin ); + + // draw attrib names using their styles + int _cw = _w/_guideCols; + int _i(0); + + _titleFont.setUnderline(true); + QString _currentHlName; + foreach (const KateExtendedAttribute::Ptr &attribute, _attributes) + { + QString _hl = attribute->name().section(':',0,0); + QString _name = attribute->name().section(':',1,1); + if ( _hl != _hlName && _hl != _currentHlName ) { + _currentHlName = _hl; + if ( _i%_guideCols ) + y += fontHeight; + y += innerMargin; + paint.setFont(_titleFont); + paint.setPen(_defaultPen); + paint.drawText( _x, y, _w, fontHeight, Qt::AlignTop, _hl + ' ' + i18n("text") ); + y += fontHeight; + _i = 0; + } + + KTextEditor::Attribute _attr = *_defaultAttributes[attribute->defaultStyleIndex()]; + _attr += *attribute; + paint.setPen( _attr.foreground().color() ); + paint.setFont( _attr.font() ); + + if (_attr.hasProperty(QTextFormat::BackgroundBrush) ) { + QRect _rect = QFontMetrics(_attr.font()).boundingRect(_name); + _rect.moveTo(_x + ((_i%_guideCols)*_cw), y); + paint.fillRect(_rect, _attr.background() ); + } + + paint.drawText(( _x + ((_i%_guideCols)*_cw)), y, _cw, fontHeight, Qt::AlignTop, _name ); + + _i++; + if ( _i && ! ( _i%_guideCols ) ) + y += fontHeight; + } + + if ( _i%_guideCols ) + y += fontHeight;// last row not full + + // draw a box around the legend + paint.setPen ( _defaultPen ); + if ( useBox ) + paint.fillRect( 0, y+innerMargin, headerWidth, boxWidth, boxColor ); + else + { + _marg -=1; + paint.drawRect( _marg, _ystart, pdmWidth-(2*_marg), y-_ystart+innerMargin ); + } + + y += ( useBox ? boxWidth : 1 ) + (innerMargin*2); + } // useGuide + + paint.translate(xstart,y); + pageStarted = false; + } // pageStarted; move on to contents:) + + if ( printLineNumbers /*&& ! startCol*/ ) // don't repeat! + { + paint.setFont( renderer.config()->font() ); + paint.setPen( renderer.config()->lineNumberColor() ); + paint.drawText( (( useBox || useBackground ) ? innerMargin : 0)-xstart, 0, + lineNumberWidth, fontHeight, + Qt::AlignRight, QString("%1").arg( lineCount + 1 ) ); + } + + // HA! this is where we print [part of] a line ;]] + // FIXME Convert this function + related functionality to a separate KatePrintView + KateLineLayoutPtr rangeptr(new KateLineLayout(renderer)); + rangeptr->setLine(lineCount); + renderer.layoutLine(rangeptr, (int)maxWidth, false); + + // selectionOnly: clip non-selection parts and adjust painter position if needed + int _xadjust = 0; + if (selectionOnly) { + if (doc->activeView()->blockSelection()) { + int _x = renderer.cursorToX(rangeptr->viewLine(0), selectionRange.start()); + int _x1 = renderer.cursorToX(rangeptr->viewLine(rangeptr->viewLineCount()-1), selectionRange.end()); + _xadjust = _x; + paint.translate(-_xadjust, 0); + paint.setClipRegion(QRegion( _x, 0, _x1 - _x, rangeptr->viewLineCount()*fontHeight)); + } + + else if (lineCount == firstline || lineCount == lastline) { + QRegion region(0, 0, maxWidth, rangeptr->viewLineCount()*fontHeight); + + if ( lineCount == firstline) { + region = region.subtracted(QRegion(0, 0, renderer.cursorToX(rangeptr->viewLine(0), selectionRange.start()), fontHeight)); + } + + if (lineCount == lastline) { + int _x = renderer.cursorToX(rangeptr->viewLine(rangeptr->viewLineCount()-1), selectionRange.end()); + region = region.subtracted(QRegion(_x, 0, maxWidth-_x, fontHeight)); + } + + paint.setClipRegion(region); + } + } + + // If the line is too long (too many 'viewlines') to fit the remaining vertical space, + // clip and adjust the painter position as necessary + int _lines = rangeptr->viewLineCount(); // number of "sublines" to paint. + + int proceedLines = _lines; + if (remainder) { + proceedLines = qMin((maxHeight - y) / fontHeight, remainder); + + paint.translate(0, -(_lines-remainder)*fontHeight+1); + paint.setClipRect(0, (_lines-remainder)*fontHeight+1, maxWidth, proceedLines*fontHeight); //### drop the crosspatch in printerfriendly mode??? + remainder -= proceedLines; + } + else if (y + fontHeight * _lines > maxHeight) { + remainder = _lines - ((maxHeight-y)/fontHeight); + paint.setClipRect(0, 0, maxWidth, (_lines-remainder)*fontHeight+1); //### drop the crosspatch in printerfriendly mode??? + } + + renderer.paintTextLine(paint, rangeptr, 0, (int)maxWidth); + + paint.setClipping(false); + paint.translate(_xadjust, (fontHeight * (_lines-remainder))); + + y += fontHeight * proceedLines; + + if ( ! remainder ) + lineCount++; + } // done lineCount <= lastline + + paint.end(); + return true; + } + return false; +} +//END KatePrinter + +//BEGIN KatePrintTextSettings +KatePrintTextSettings::KatePrintTextSettings( QWidget *parent ) + : QWidget( parent ) +{ + setWindowTitle( i18n("Te&xt Settings") ); + + QVBoxLayout *lo = new QVBoxLayout ( this ); + + cbLineNumbers = new QCheckBox( i18n("Print line &numbers"), this ); + lo->addWidget( cbLineNumbers ); + + cbGuide = new QCheckBox( i18n("Print &legend"), this ); + lo->addWidget( cbGuide ); + + lo->addStretch( 1 ); + + // set defaults - nothing to do :-) + + // whatsthis + cbLineNumbers->setWhatsThis(i18n( + "

If enabled, line numbers will be printed on the left side of the page(s).

") ); + cbGuide->setWhatsThis(i18n( + "

Print a box displaying typographical conventions for the document type, as " + "defined by the syntax highlighting being used.

") ); + + readSettings(); +} + +KatePrintTextSettings::~KatePrintTextSettings() +{ + writeSettings(); +} + +bool KatePrintTextSettings::printLineNumbers() +{ + return cbLineNumbers->isChecked(); +} + +bool KatePrintTextSettings::printGuide() +{ + return cbGuide->isChecked(); +} + +void KatePrintTextSettings::readSettings() +{ + KSharedConfigPtr config = KGlobal::config(); + KConfigGroup printGroup( config, "Kate Print Settings" ); + + KConfigGroup textGroup( &printGroup, "Text" ); + bool isLineNumbersChecked = textGroup.readEntry( "LineNumbers", false ); + cbLineNumbers->setChecked( isLineNumbersChecked ); + + bool isLegendChecked = textGroup.readEntry( "Legend", false ); + cbGuide->setChecked( isLegendChecked ); +} + +void KatePrintTextSettings::writeSettings() +{ + KSharedConfigPtr config = KGlobal::config(); + KConfigGroup printGroup( config, "Kate Print Settings" ); + + KConfigGroup textGroup( &printGroup, "Text" ); + textGroup.writeEntry( "LineNumbers", printLineNumbers() ); + textGroup.writeEntry( "Legend", printGuide() ); + + config->sync(); +} + +//END KatePrintTextSettings + +//BEGIN KatePrintHeaderFooter +KatePrintHeaderFooter::KatePrintHeaderFooter( QWidget *parent ) + : QWidget( parent ) +{ + setWindowTitle( i18n("Hea&der && Footer") ); + + QVBoxLayout *lo = new QVBoxLayout ( this ); + + // enable + QHBoxLayout *lo1 = new QHBoxLayout (); + lo->addLayout( lo1 ); + cbEnableHeader = new QCheckBox( i18n("Pr&int header"), this ); + lo1->addWidget( cbEnableHeader ); + cbEnableFooter = new QCheckBox( i18n("Pri&nt footer"), this ); + lo1->addWidget( cbEnableFooter ); + + // font + QHBoxLayout *lo2 = new QHBoxLayout(); + lo->addLayout( lo2 ); + lo2->addWidget( new QLabel( i18n("Header/footer font:"), this ) ); + lFontPreview = new QLabel( this ); + lFontPreview->setFrameStyle( QFrame::Panel|QFrame::Sunken ); + lo2->addWidget( lFontPreview ); + lo2->setStretchFactor( lFontPreview, 1 ); + QPushButton *btnChooseFont = new QPushButton( i18n("Choo&se Font..."), this ); + lo2->addWidget( btnChooseFont ); + connect( btnChooseFont, SIGNAL(clicked()), this, SLOT(setHFFont()) ); + + // header + gbHeader = new QGroupBox( this ); + gbHeader->setTitle(i18n("Header Properties")); + QGridLayout* grid = new QGridLayout(gbHeader); + lo->addWidget( gbHeader ); + + QLabel *lHeaderFormat = new QLabel( i18n("&Format:"), gbHeader ); + grid->addWidget(lHeaderFormat, 0, 0); + + KHBox *hbHeaderFormat = new KHBox( gbHeader ); + grid->addWidget(hbHeaderFormat, 0, 1); + + leHeaderLeft = new KLineEdit( hbHeaderFormat ); + leHeaderCenter = new KLineEdit( hbHeaderFormat ); + leHeaderRight = new KLineEdit( hbHeaderFormat ); + lHeaderFormat->setBuddy( leHeaderLeft ); + + leHeaderLeft->setContextMenuPolicy(Qt::CustomContextMenu); + leHeaderCenter->setContextMenuPolicy(Qt::CustomContextMenu); + leHeaderRight->setContextMenuPolicy(Qt::CustomContextMenu); + connect(leHeaderLeft, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showContextMenu(QPoint))); + connect(leHeaderCenter, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showContextMenu(QPoint))); + connect(leHeaderRight, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showContextMenu(QPoint))); + + grid->addWidget(new QLabel( i18n("Colors:"), gbHeader ), 1, 0); + + KHBox *hbHeaderColors = new KHBox( gbHeader ); + grid->addWidget(hbHeaderColors, 1, 1); + + hbHeaderColors->setSpacing( -1 ); + QLabel *lHeaderFgCol = new QLabel( i18n("Foreground:"), hbHeaderColors ); + kcbtnHeaderFg = new KColorButton( hbHeaderColors ); + lHeaderFgCol->setBuddy( kcbtnHeaderFg ); + cbHeaderEnableBgColor = new QCheckBox( i18n("Bac&kground"), hbHeaderColors ); + kcbtnHeaderBg = new KColorButton( hbHeaderColors ); + + gbFooter = new QGroupBox( this ); + gbFooter->setTitle(i18n("Footer Properties")); + grid = new QGridLayout(gbFooter); + lo->addWidget( gbFooter ); + + // footer + QLabel *lFooterFormat = new QLabel( i18n("For&mat:"), gbFooter ); + grid->addWidget(lFooterFormat, 0, 0); + + KHBox *hbFooterFormat = new KHBox( gbFooter ); + grid->addWidget(hbFooterFormat, 0, 1); + + hbFooterFormat->setSpacing( -1 ); + leFooterLeft = new KLineEdit( hbFooterFormat ); + leFooterCenter = new KLineEdit( hbFooterFormat ); + leFooterRight = new KLineEdit( hbFooterFormat ); + lFooterFormat->setBuddy( leFooterLeft ); + + leFooterLeft->setContextMenuPolicy(Qt::CustomContextMenu); + leFooterCenter->setContextMenuPolicy(Qt::CustomContextMenu); + leFooterRight->setContextMenuPolicy(Qt::CustomContextMenu); + connect(leFooterLeft, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showContextMenu(QPoint))); + connect(leFooterCenter, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showContextMenu(QPoint))); + connect(leFooterRight, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showContextMenu(QPoint))); + + grid->addWidget(new QLabel( i18n("Colors:"), gbFooter ), 1, 0); + + KHBox *hbFooterColors = new KHBox( gbFooter ); + grid->addWidget(hbFooterColors, 1, 1); + + hbFooterColors->setSpacing( -1 ); + QLabel *lFooterBgCol = new QLabel( i18n("Foreground:"), hbFooterColors ); + kcbtnFooterFg = new KColorButton( hbFooterColors ); + lFooterBgCol->setBuddy( kcbtnFooterFg ); + cbFooterEnableBgColor = new QCheckBox( i18n("&Background"), hbFooterColors ); + kcbtnFooterBg = new KColorButton( hbFooterColors ); + + lo->addStretch( 1 ); + + // user friendly + connect( cbEnableHeader, SIGNAL(toggled(bool)), gbHeader, SLOT(setEnabled(bool)) ); + connect( cbEnableFooter, SIGNAL(toggled(bool)), gbFooter, SLOT(setEnabled(bool)) ); + connect( cbHeaderEnableBgColor, SIGNAL(toggled(bool)), kcbtnHeaderBg, SLOT(setEnabled(bool)) ); + connect( cbFooterEnableBgColor, SIGNAL(toggled(bool)), kcbtnFooterBg, SLOT(setEnabled(bool)) ); + + // set defaults + cbEnableHeader->setChecked( true ); + leHeaderLeft->setText( "%y" ); + leHeaderCenter->setText( "%f" ); + leHeaderRight->setText( "%p" ); + kcbtnHeaderFg->setColor( QColor("black") ); + cbHeaderEnableBgColor->setChecked( false ); + kcbtnHeaderBg->setColor( QColor("lightgrey") ); + + cbEnableFooter->setChecked( true ); + leFooterRight->setText( "%U" ); + kcbtnFooterFg->setColor( QColor("black") ); + cbFooterEnableBgColor->setChecked( false ); + kcbtnFooterBg->setColor( QColor("lightgrey") ); + + // whatsthis + QString s = i18n("

Format of the page header. The following tags are supported:

"); + QString s1 = i18n( + "
  • %u: current user name
  • " + "
  • %d: complete date/time in short format
  • " + "
  • %D: complete date/time in long format
  • " + "
  • %h: current time
  • " + "
  • %y: current date in short format
  • " + "
  • %Y: current date in long format
  • " + "
  • %f: file name
  • " + "
  • %U: full URL of the document
  • " + "
  • %p: page number
  • " + "
  • %P: total amount of pages
  • " + "

"); + leHeaderRight->setWhatsThis(s + s1 ); + leHeaderCenter->setWhatsThis(s + s1 ); + leHeaderLeft->setWhatsThis(s + s1 ); + s = i18n("

Format of the page footer. The following tags are supported:

"); + leFooterRight->setWhatsThis(s + s1 ); + leFooterCenter->setWhatsThis(s + s1 ); + leFooterLeft->setWhatsThis(s + s1 ); + + readSettings(); +} + +KatePrintHeaderFooter::~KatePrintHeaderFooter() +{ + writeSettings(); +} + +QFont KatePrintHeaderFooter::font() +{ + return lFontPreview->font(); +} + +bool KatePrintHeaderFooter::useHeader() +{ + return cbEnableHeader->isChecked(); +} + +QStringList KatePrintHeaderFooter::headerFormat() +{ + QStringList l; + l << leHeaderLeft->text() << leHeaderCenter->text() << leHeaderRight->text(); + return l; +} + +QColor KatePrintHeaderFooter::headerForeground() +{ + return kcbtnHeaderFg->color(); +} + +QColor KatePrintHeaderFooter::headerBackground() +{ + return kcbtnHeaderBg->color(); +} + +bool KatePrintHeaderFooter::useHeaderBackground() +{ + return cbHeaderEnableBgColor->isChecked(); +} + +bool KatePrintHeaderFooter::useFooter() +{ + return cbEnableFooter->isChecked(); +} + +QStringList KatePrintHeaderFooter::footerFormat() +{ + QStringList l; + l<< leFooterLeft->text() << leFooterCenter->text() << leFooterRight->text(); + return l; +} + +QColor KatePrintHeaderFooter::footerForeground() +{ + return kcbtnFooterFg->color(); +} + +QColor KatePrintHeaderFooter::footerBackground() +{ + return kcbtnFooterBg->color(); +} + +bool KatePrintHeaderFooter::useFooterBackground() +{ + return cbFooterEnableBgColor->isChecked(); +} + +void KatePrintHeaderFooter::setHFFont() +{ + QFont fnt( lFontPreview->font() ); + // display a font dialog + if ( KFontDialog::getFont( fnt, KFontChooser::NoDisplayFlags, this ) == KFontDialog::Accepted ) + { + // set preview + lFontPreview->setFont( fnt ); + lFontPreview->setText( QString(fnt.family() + ", %1pt").arg( fnt.pointSize() ) ); + } +} + +void KatePrintHeaderFooter::showContextMenu(const QPoint& pos) +{ + QLineEdit* lineEdit = qobject_cast(sender()); + if (!lineEdit) { + return; + } + + QMenu* const contextMenu = lineEdit->createStandardContextMenu(); + if (contextMenu == NULL) { + return; + } + contextMenu->addSeparator(); + + // create original context menu + QMenu* menu = contextMenu->addMenu(i18n("Add Placeholder...")); + menu->setIcon(KIcon("list-add")); + QAction* a = menu->addAction(i18n("Current User Name") + "\t%u"); + a->setData("%u"); + a = menu->addAction(i18n("Complete Date/Time (short format)") + "\t%d"); + a->setData("%d"); + a = menu->addAction(i18n("Complete Date/Time (long format)") + "\t%D"); + a->setData("%D"); + a = menu->addAction(i18n("Current Time") + "\t%h"); + a->setData("%h"); + a = menu->addAction(i18n("Current Date (short format)") + "\t%y"); + a->setData("%y"); + a = menu->addAction(i18n("Current Date (long format)") + "\t%Y"); + a->setData("%Y"); + a = menu->addAction(i18n("File Name") + "\t%f"); + a->setData("%f"); + a = menu->addAction(i18n("Full document URL") + "\t%U"); + a->setData("%U"); + a = menu->addAction(i18n("Page Number") + "\t%p"); + a->setData("%p"); + a = menu->addAction(i18n("Total Amount of Pages") + "\t%P"); + a->setData("%P"); + + QAction* const result = contextMenu->exec(lineEdit->mapToGlobal(pos)); + if (result) { + QString placeHolder = result->data().toString(); + if (!placeHolder.isEmpty()) { + lineEdit->insert(placeHolder); + } + } +} + +void KatePrintHeaderFooter::readSettings() +{ + KSharedConfigPtr config = KGlobal::config(); + KConfigGroup printGroup( config, "Kate Print Settings" ); + + // Header + KConfigGroup headerFooterGroup( &printGroup, "HeaderFooter" ); + bool isHeaderEnabledChecked = headerFooterGroup.readEntry( "HeaderEnabled", true ); + cbEnableHeader->setChecked( isHeaderEnabledChecked ); + + QString headerFormatLeft = headerFooterGroup.readEntry( "HeaderFormatLeft", "%y" ); + leHeaderLeft->setText( headerFormatLeft ); + + QString headerFormatCenter = headerFooterGroup.readEntry( "HeaderFormatCenter", "%f" ); + leHeaderCenter->setText( headerFormatCenter ); + + QString headerFormatRight = headerFooterGroup.readEntry( "HeaderFormatRight", "%p" ); + leHeaderRight->setText( headerFormatRight ); + + QColor headerForeground = headerFooterGroup.readEntry( "HeaderForeground", QColor("black") ); + kcbtnHeaderFg->setColor( headerForeground ); + + bool isHeaderBackgroundChecked = headerFooterGroup.readEntry( "HeaderBackgroundEnabled", false ); + cbHeaderEnableBgColor->setChecked( isHeaderBackgroundChecked ); + + QColor headerBackground = headerFooterGroup.readEntry( "HeaderBackground", QColor("lightgrey") ); + kcbtnHeaderBg->setColor( headerBackground ); + + // Footer + bool isFooterEnabledChecked = headerFooterGroup.readEntry( "FooterEnabled", true ); + cbEnableFooter->setChecked( isFooterEnabledChecked ); + + QString footerFormatLeft = headerFooterGroup.readEntry( "FooterFormatLeft", QString() ); + leFooterLeft->setText( footerFormatLeft ); + + QString footerFormatCenter = headerFooterGroup.readEntry( "FooterFormatCenter", QString() ); + leFooterCenter->setText( footerFormatCenter ); + + QString footerFormatRight = headerFooterGroup.readEntry( "FooterFormatRight", "%U" ); + leFooterRight->setText( footerFormatRight ); + + QColor footerForeground = headerFooterGroup.readEntry( "FooterForeground", QColor("black") ); + kcbtnFooterFg->setColor( footerForeground ); + + bool isFooterBackgroundChecked = headerFooterGroup.readEntry( "FooterBackgroundEnabled", false ); + cbFooterEnableBgColor->setChecked( isFooterBackgroundChecked ); + + QColor footerBackground = headerFooterGroup.readEntry( "FooterBackground", QColor("lightgrey") ); + kcbtnFooterBg->setColor( footerBackground ); + + // Font + QFont headerFooterFont = headerFooterGroup.readEntry( "HeaderFooterFont", QFont() ); + lFontPreview->setFont( headerFooterFont ); + lFontPreview->setText( QString(headerFooterFont.family() + ", %1pt").arg( headerFooterFont.pointSize() ) ); +} + +void KatePrintHeaderFooter::writeSettings() +{ + KSharedConfigPtr config = KGlobal::config(); + KConfigGroup printGroup( config, "Kate Print Settings" ); + + // Header + KConfigGroup headerFooterGroup( &printGroup, "HeaderFooter" ); + headerFooterGroup.writeEntry( "HeaderEnabled", useHeader() ); + + QStringList format = headerFormat(); + headerFooterGroup.writeEntry( "HeaderFormatLeft", format[0] ); + headerFooterGroup.writeEntry( "HeaderFormatCenter", format[1] ); + headerFooterGroup.writeEntry( "HeaderFormatRight", format[2] ); + headerFooterGroup.writeEntry( "HeaderForeground", headerForeground() ); + headerFooterGroup.writeEntry( "HeaderBackgroundEnabled", useHeaderBackground() ); + headerFooterGroup.writeEntry( "HeaderBackground", headerBackground() ); + + // Footer + headerFooterGroup.writeEntry( "FooterEnabled", useFooter() ); + + format = footerFormat(); + headerFooterGroup.writeEntry( "FooterFormatLeft", format[0] ); + headerFooterGroup.writeEntry( "FooterFormatCenter", format[1] ); + headerFooterGroup.writeEntry( "FooterFormatRight", format[2] ); + headerFooterGroup.writeEntry( "FooterForeground", footerForeground() ); + headerFooterGroup.writeEntry( "FooterBackgroundEnabled", useFooterBackground() ); + headerFooterGroup.writeEntry( "FooterBackground", footerBackground() ); + + // Font + headerFooterGroup.writeEntry( "HeaderFooterFont", font() ); + + config->sync(); +} + +//END KatePrintHeaderFooter + +//BEGIN KatePrintLayout + +KatePrintLayout::KatePrintLayout( QWidget *parent) + : QWidget( parent ) +{ + setWindowTitle( i18n("L&ayout") ); + + QVBoxLayout *lo = new QVBoxLayout ( this ); + + KHBox *hb = new KHBox( this ); + lo->addWidget( hb ); + QLabel *lSchema = new QLabel( i18n("&Schema:"), hb ); + cmbSchema = new KComboBox( hb ); + cmbSchema->setEditable( false ); + lSchema->setBuddy( cmbSchema ); + + cbDrawBackground = new QCheckBox( i18n("Draw bac&kground color"), this ); + lo->addWidget( cbDrawBackground ); + + cbEnableBox = new QCheckBox( i18n("Draw &boxes"), this ); + lo->addWidget( cbEnableBox ); + + gbBoxProps = new QGroupBox( this ); + gbBoxProps->setTitle(i18n("Box Properties")); + QGridLayout* grid = new QGridLayout(gbBoxProps); + lo->addWidget( gbBoxProps ); + + QLabel *lBoxWidth = new QLabel( i18n("W&idth:"), gbBoxProps ); + grid->addWidget(lBoxWidth, 0, 0); + sbBoxWidth = new KIntSpinBox( gbBoxProps ); + sbBoxWidth->setRange( 1, 100 ); + sbBoxWidth->setSingleStep( 1 ); + grid->addWidget(sbBoxWidth, 0, 1); + lBoxWidth->setBuddy( sbBoxWidth ); + + QLabel *lBoxMargin = new QLabel( i18n("&Margin:"), gbBoxProps ); + grid->addWidget(lBoxMargin, 1, 0); + sbBoxMargin = new KIntSpinBox( gbBoxProps ); + sbBoxMargin->setRange( 0, 100 ); + sbBoxMargin->setSingleStep( 1 ); + grid->addWidget(sbBoxMargin, 1, 1); + lBoxMargin->setBuddy( sbBoxMargin ); + + QLabel *lBoxColor = new QLabel( i18n("Co&lor:"), gbBoxProps ); + grid->addWidget(lBoxColor, 2, 0); + kcbtnBoxColor = new KColorButton( gbBoxProps ); + grid->addWidget(kcbtnBoxColor, 2, 1); + lBoxColor->setBuddy( kcbtnBoxColor ); + + connect( cbEnableBox, SIGNAL(toggled(bool)), gbBoxProps, SLOT(setEnabled(bool)) ); + + lo->addStretch( 1 ); + // set defaults: + sbBoxMargin->setValue( 6 ); + gbBoxProps->setEnabled( false ); + + Q_FOREACH (const KateSchema &schema, KateGlobal::self()->schemaManager()->list()) + cmbSchema->addItem (schema.translatedName(), QVariant (schema.rawName)); + + // default is printing, MUST BE THERE + cmbSchema->setCurrentIndex (cmbSchema->findData (QVariant("Printing"))); + + // whatsthis + cmbSchema->setWhatsThis(i18n( + "Select the color scheme to use for the print." ) ); + cbDrawBackground->setWhatsThis(i18n( + "

If enabled, the background color of the editor will be used.

" + "

This may be useful if your color scheme is designed for a dark background.

") ); + cbEnableBox->setWhatsThis(i18n( + "

If enabled, a box as defined in the properties below will be drawn " + "around the contents of each page. The Header and Footer will be separated " + "from the contents with a line as well.

") ); + sbBoxWidth->setWhatsThis(i18n( + "The width of the box outline" ) ); + sbBoxMargin->setWhatsThis(i18n( + "The margin inside boxes, in pixels") ); + kcbtnBoxColor->setWhatsThis(i18n( + "The line color to use for boxes") ); + + readSettings(); +} + +KatePrintLayout::~KatePrintLayout() +{ + writeSettings(); +} + +QString KatePrintLayout::colorScheme() +{ + return cmbSchema->itemData(cmbSchema->currentIndex()).toString(); +} + +bool KatePrintLayout::useBackground() +{ + return cbDrawBackground->isChecked(); +} + +bool KatePrintLayout::useBox() +{ + return cbEnableBox->isChecked(); +} + +int KatePrintLayout::boxWidth() +{ + return sbBoxWidth->value(); +} + +int KatePrintLayout::boxMargin() +{ + return sbBoxMargin->value(); +} + +QColor KatePrintLayout::boxColor() +{ + return kcbtnBoxColor->color(); +} + +void KatePrintLayout::readSettings() +{ + KSharedConfigPtr config = KGlobal::config(); + KConfigGroup printGroup(config, "Kate Print Settings"); + + KConfigGroup layoutGroup(&printGroup, "Layout"); + + // get color schema back + QString colorScheme = layoutGroup.readEntry( "ColorScheme", "Printing" ); + int index = cmbSchema->findData (QVariant (colorScheme)); + if (index != -1) + cmbSchema->setCurrentIndex ( index ); + + bool isBackgroundChecked = layoutGroup.readEntry( "BackgroundColorEnabled", false ); + cbDrawBackground->setChecked( isBackgroundChecked ); + + bool isBoxChecked = layoutGroup.readEntry( "BoxEnabled", false ); + cbEnableBox->setChecked( isBoxChecked ); + + int boxWidth = layoutGroup.readEntry( "BoxWidth", 1 ); + sbBoxWidth->setValue( boxWidth ); + + int boxMargin = layoutGroup.readEntry( "BoxMargin", 6 ); + sbBoxMargin->setValue( boxMargin ); + + QColor boxColor = layoutGroup.readEntry( "BoxColor", QColor() ); + kcbtnBoxColor->setColor( boxColor ); +} + +void KatePrintLayout::writeSettings() +{ + KSharedConfigPtr config = KGlobal::config(); + KConfigGroup printGroup(config, "Kate Print Settings"); + + KConfigGroup layoutGroup(&printGroup, "Layout"); + layoutGroup.writeEntry( "ColorScheme", colorScheme() ); + layoutGroup.writeEntry( "BackgroundColorEnabled", useBackground() ); + layoutGroup.writeEntry( "BoxEnabled", useBox() ); + layoutGroup.writeEntry( "BoxWidth", boxWidth() ); + layoutGroup.writeEntry( "BoxMargin", boxMargin() ); + layoutGroup.writeEntry( "BoxColor", boxColor() ); + + config->sync(); +} + +//END KatePrintLayout + +#include "moc_kateprinter.cpp" + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/utils/kateprinter.h b/kate/part/utils/kateprinter.h new file mode 100644 index 00000000..10d3849d --- /dev/null +++ b/kate/part/utils/kateprinter.h @@ -0,0 +1,159 @@ +/* This file is part of the KDE libraries and the Kate part. + * + * Copyright (C) 2002-2010 Anders Lund + * + * Rewritten based on code of Copyright (c) 2002 Michael Goffioul + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __KATE_PRINTER_H__ +#define __KATE_PRINTER_H__ + +#include +#include +#include +#include +#include +#include + +class KateDocument; +class KColorButton; +class KLineEdit; +class KIntSpinBox; +class KComboBox; + +class KatePrinter +{ + public: + static bool print (KateDocument *doc); + static void readSettings(QPrinter& printer); + static void writeSettings(QPrinter& printer); +}; + +//BEGIN Text settings +/* + Text settings page: + - Print Line Numbers + () Smart () Yes () No +*/ +class KatePrintTextSettings : public QWidget +{ + Q_OBJECT + public: + explicit KatePrintTextSettings( QWidget *parent=0 ); + ~KatePrintTextSettings(); + + bool printLineNumbers(); + bool printGuide(); + + private: + void readSettings(); + void writeSettings(); + + QCheckBox *cbLineNumbers; + QCheckBox *cbGuide; +}; +//END Text Settings + +//BEGIN Header/Footer +/* + Header & Footer page: + - enable header/footer + - header/footer props + o formats + o colors +*/ + +class KatePrintHeaderFooter : public QWidget +{ + Q_OBJECT + public: + explicit KatePrintHeaderFooter( QWidget *parent=0 ); + ~KatePrintHeaderFooter(); + + QFont font(); + + bool useHeader(); + QStringList headerFormat(); + QColor headerForeground(); + QColor headerBackground(); + bool useHeaderBackground(); + + bool useFooter(); + QStringList footerFormat(); + QColor footerForeground(); + QColor footerBackground(); + bool useFooterBackground(); + + public Q_SLOTS: + void setHFFont(); + void showContextMenu(const QPoint& pos); + + private: + void readSettings(); + void writeSettings(); + + QCheckBox *cbEnableHeader, *cbEnableFooter; + QLabel *lFontPreview; + QGroupBox *gbHeader, *gbFooter; + KLineEdit *leHeaderLeft, *leHeaderCenter, *leHeaderRight; + KColorButton *kcbtnHeaderFg, *kcbtnHeaderBg; + QCheckBox *cbHeaderEnableBgColor; + KLineEdit *leFooterLeft, *leFooterCenter, *leFooterRight; + KColorButton *kcbtnFooterFg, *kcbtnFooterBg; + QCheckBox *cbFooterEnableBgColor; +}; + +//END Header/Footer + +//BEGIN Layout +/* + Layout page: + - Color scheme + - Use Box + - Box properties + o Width + o Margin + o Color +*/ +class KatePrintLayout : public QWidget +{ + Q_OBJECT + public: + explicit KatePrintLayout( QWidget *parent=0 ); + ~KatePrintLayout(); + + QString colorScheme(); + bool useBackground(); + bool useBox(); + int boxWidth(); + int boxMargin(); + QColor boxColor(); + + private: + void readSettings(); + void writeSettings(); + + KComboBox *cmbSchema; + QCheckBox *cbEnableBox, *cbDrawBackground; + QGroupBox *gbBoxProps; + KIntSpinBox *sbBoxWidth, *sbBoxMargin; + KColorButton* kcbtnBoxColor; +}; +//END Layout + +#endif diff --git a/kate/part/variableeditor/katehelpbutton.cpp b/kate/part/variableeditor/katehelpbutton.cpp new file mode 100644 index 00000000..d61d4b4a --- /dev/null +++ b/kate/part/variableeditor/katehelpbutton.cpp @@ -0,0 +1,65 @@ +/* This file is part of the KDE project + + Copyright (C) 2011 Dominik Haumann + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "katehelpbutton.h" + +#include +#include +#include + +KateHelpButton::KateHelpButton(QWidget* parent) + : QToolButton(parent) +{ + setAutoRaise(true); + setIconState(IconColored); + setToolTip(i18n("Kate Handbook.")); + + connect(this, SIGNAL(clicked()), SLOT(invokeHelp())); +} + +KateHelpButton::~KateHelpButton() +{ +} + +void KateHelpButton::setIconState(IconState state) +{ + if (state == IconGrayscaled) { + setIcon(SmallIcon("help-contents", 0, KIconLoader::DisabledState)); + } else if (state == IconHidden) { + setIcon(QIcon()); + } else { + setIcon(SmallIcon("help-contents", 0, KIconLoader::DefaultState)); + } + + update(); +} + +void KateHelpButton::invokeHelp() +{ + KToolInvocation::invokeHelp(m_section, "kate"); +} + +void KateHelpButton::setSection(const QString& section) +{ + m_section = section; +} + + +// kate: indent-width 2; replace-tabs on; diff --git a/kate/part/variableeditor/katehelpbutton.h b/kate/part/variableeditor/katehelpbutton.h new file mode 100644 index 00000000..f5ed1811 --- /dev/null +++ b/kate/part/variableeditor/katehelpbutton.h @@ -0,0 +1,53 @@ +/* This file is part of the KDE project + + Copyright (C) 2011 Dominik Haumann + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KATE_HELP_BUTTON_H +#define KATE_HELP_BUTTON_H + +#include + +class KateHelpButton : public QToolButton +{ + Q_OBJECT + +public: + enum IconState { + IconColored = 0, + IconGrayscaled, + IconHidden + }; + + void setSection(const QString& section); + +public: + KateHelpButton(QWidget* parent = 0); + virtual ~KateHelpButton(); + +public Q_SLOTS: + void setIconState(IconState state); + void invokeHelp(); + +private: + QString m_section; +}; + +#endif + +// kate: indent-width 2; replace-tabs on; diff --git a/kate/part/variableeditor/variableeditor.cpp b/kate/part/variableeditor/variableeditor.cpp new file mode 100644 index 00000000..3b132351 --- /dev/null +++ b/kate/part/variableeditor/variableeditor.cpp @@ -0,0 +1,361 @@ +/* This file is part of the KDE project + + Copyright (C) 2011 Dominik Haumann + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "variableeditor.h" +#include "variableitem.h" +#include "katehelpbutton.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +//BEGIN VariableEditor +VariableEditor::VariableEditor(VariableItem* item, QWidget* parent) + : QWidget(parent) + , m_item(item) +{ + setAttribute(Qt::WA_Hover); + + setAutoFillBackground(true); + QGridLayout* l = new QGridLayout(this); + l->setMargin(10); + + m_checkBox = new QCheckBox(this); + m_variable = new QLabel(item->variable(), this); + m_variable->setFocusPolicy(Qt::ClickFocus); + m_variable->setFocusProxy(m_checkBox); + m_btnHelp = new KateHelpButton(this); + m_btnHelp->setIconState(KateHelpButton::IconHidden); + m_btnHelp->setEnabled(false); + m_btnHelp->setSection(QLatin1String("variable-") + item->variable()); + + m_helpText = new QLabel(item->helpText(), this); + m_helpText->setWordWrap(true); + + l->addWidget(m_checkBox, 0, 0, Qt::AlignLeft); + l->addWidget(m_variable, 0, 1, Qt::AlignLeft); + l->addWidget(m_btnHelp, 0, 3, Qt::AlignRight); + l->addWidget(m_helpText, 1, 1, 1, 3); + + l->setColumnStretch(0, 0); + l->setColumnStretch(1, 1); + l->setColumnStretch(2, 1); + l->setColumnStretch(3, 0); + + setLayout(l); + + connect(m_checkBox, SIGNAL(toggled(bool)), this, SLOT(itemEnabled(bool))); + m_checkBox->setChecked(item->isActive()); + + connect(m_checkBox, SIGNAL(toggled(bool)), this, SIGNAL(valueChanged())); + setMouseTracking(true); +} + +VariableEditor::~VariableEditor() +{ +} + +void VariableEditor::enterEvent(QEvent* event) +{ + QWidget::enterEvent(event); + m_btnHelp->setIconState(KateHelpButton::IconColored); + m_btnHelp->setEnabled(true); + + update(); +} + +void VariableEditor::leaveEvent(QEvent* event) +{ + QWidget::leaveEvent(event); + m_btnHelp->setIconState(KateHelpButton::IconHidden); + m_btnHelp->setEnabled(false); + + update(); +} + +void VariableEditor::paintEvent(QPaintEvent* event) +{ + QWidget::paintEvent(event); + + // draw highlighting rect like in plasma + if (underMouse()) { + QPainter painter(this); + + painter.setRenderHint(QPainter::Antialiasing); + + QColor cornerColor = palette().color(QPalette::Highlight); + cornerColor.setAlphaF(0.2); + + QColor midColor = palette().color(QPalette::Highlight); + midColor.setAlphaF(0.5); + + QRect highlightRect = rect().adjusted(2, 2, -2, -2); + + QPen outlinePen; + outlinePen.setWidth(2); + + QLinearGradient gradient(highlightRect.topLeft(), highlightRect.topRight()); + gradient.setColorAt(0, cornerColor); + gradient.setColorAt(0.3, midColor); + gradient.setColorAt(1, cornerColor); + outlinePen.setBrush(gradient); + painter.setPen(outlinePen); + + const int radius = 5; + painter.drawRoundedRect(highlightRect, radius, radius); + } +} + +void VariableEditor::itemEnabled(bool enabled) +{ + if (enabled) { + m_variable->setText("" + m_item->variable() + ""); + } else { + m_variable->setText(m_item->variable()); + } + m_item->setActive(enabled); +} + +void VariableEditor::activateItem() +{ + m_checkBox->setChecked(true); +} + +VariableItem* VariableEditor::item() const +{ + return m_item; +} +//END VariableEditor + + + + +//BEGIN VariableUintEditor +VariableIntEditor::VariableIntEditor(VariableIntItem* item, QWidget* parent) + : VariableEditor(item, parent) +{ + QGridLayout* l = (QGridLayout *) layout(); + + m_spinBox = new QSpinBox(this); + m_spinBox->setValue(item->value()); + m_spinBox->setMinimum(item->minValue()); + m_spinBox->setMaximum(item->maxValue()); + + l->addWidget(m_spinBox, 0, 2, Qt::AlignLeft); + + connect(m_spinBox, SIGNAL(valueChanged(int)), this, SIGNAL(valueChanged())); + connect(m_spinBox, SIGNAL(valueChanged(int)), this, SLOT(activateItem())); + connect(m_spinBox, SIGNAL(valueChanged(int)), this, SLOT(setItemValue(int))); +} + +void VariableIntEditor::setItemValue(int newValue) +{ + static_cast(item())->setValue(newValue); +} +//END VariableUintEditor + + + +//BEGIN VariableBoolEditor +VariableBoolEditor::VariableBoolEditor(VariableBoolItem* item, QWidget* parent) + : VariableEditor(item, parent) +{ + QGridLayout* l = (QGridLayout *) layout(); + + m_comboBox = new QComboBox(this); + m_comboBox->addItem(i18n("true")); + m_comboBox->addItem(i18n("false")); + m_comboBox->setCurrentIndex(item->value() ? 0 : 1); + l->addWidget(m_comboBox, 0, 2, Qt::AlignLeft); + + connect(m_comboBox, SIGNAL(currentIndexChanged(int)), this, SIGNAL(valueChanged())); + connect(m_comboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(activateItem())); + connect(m_comboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(setItemValue(int))); +} + +void VariableBoolEditor::setItemValue(int enabled) +{ + static_cast(item())->setValue(enabled == 0); +} +//END VariableBoolEditor + + + + +//BEGIN VariableStringListEditor +VariableStringListEditor::VariableStringListEditor(VariableStringListItem* item, QWidget* parent) + : VariableEditor(item, parent) +{ + QGridLayout* l = (QGridLayout *) layout(); + + m_comboBox = new QComboBox(this); + m_comboBox->addItems(item->stringList()); + int index = 0; + for (int i = 0; i < item->stringList().size(); ++i) { + if (item->stringList().at(i) == item->value()) { + index = i; + break; + } + } + m_comboBox->setCurrentIndex(index); + l->addWidget(m_comboBox, 0, 2, Qt::AlignLeft); + + connect(m_comboBox, SIGNAL(currentIndexChanged(int)), this, SIGNAL(valueChanged())); + connect(m_comboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(activateItem())); + connect(m_comboBox, SIGNAL(currentIndexChanged(QString)), this, SLOT(setItemValue(QString))); +} + +void VariableStringListEditor::setItemValue(const QString& newValue) +{ + static_cast(item())->setValue(newValue); +} +//END VariableStringListEditor + + + +//BEGIN VariableColorEditor +VariableColorEditor::VariableColorEditor(VariableColorItem* item, QWidget* parent) + : VariableEditor(item, parent) +{ + QGridLayout* l = (QGridLayout *) layout(); + + m_comboBox = new KColorCombo(this); + m_comboBox->setColor(item->value()); + l->addWidget(m_comboBox, 0, 2, Qt::AlignLeft); + + connect(m_comboBox, SIGNAL(activated(QColor)), this, SIGNAL(valueChanged())); + connect(m_comboBox, SIGNAL(activated(QColor)), this, SLOT(activateItem())); + connect(m_comboBox, SIGNAL(activated(QColor)), this, SLOT(setItemValue(QColor))); +} + +void VariableColorEditor::setItemValue(const QColor& newValue) +{ + static_cast(item())->setValue(newValue); +} +//END VariableColorEditor + + + +//BEGIN VariableFontEditor +VariableFontEditor::VariableFontEditor(VariableFontItem* item, QWidget* parent) + : VariableEditor(item, parent) +{ + QGridLayout* l = (QGridLayout *) layout(); + + m_comboBox = new KFontComboBox(this); + m_comboBox->setCurrentFont(item->value()); + l->addWidget(m_comboBox, 0, 2, Qt::AlignLeft); + + connect(m_comboBox, SIGNAL(currentFontChanged(QFont)), this, SIGNAL(valueChanged())); + connect(m_comboBox, SIGNAL(currentFontChanged(QFont)), this, SLOT(activateItem())); + connect(m_comboBox, SIGNAL(currentFontChanged(QFont)), this, SLOT(setItemValue(QFont))); +} + +void VariableFontEditor::setItemValue(const QFont& newValue) +{ + static_cast(item())->setValue(newValue); +} +//END VariableFontEditor + + + +//BEGIN VariableStringEditor +VariableStringEditor::VariableStringEditor(VariableStringItem *item, QWidget *parent) + :VariableEditor(item, parent) +{ + QGridLayout *l = (QGridLayout*) layout(); + + m_lineEdit = new QLineEdit(this); + m_lineEdit->setText(item->value()); + l->addWidget(m_lineEdit, 0, 2, Qt::AlignLeft); + + connect(m_lineEdit, SIGNAL(textChanged(QString)), this, SIGNAL(valueChanged())); + connect(m_lineEdit, SIGNAL(textChanged(QString)), this, SLOT(activateItem())); + connect(m_lineEdit, SIGNAL(textChanged(QString)), this, SLOT(setItemValue(QString))); +} + +void VariableStringEditor::setItemValue(const QString &newValue) +{ + static_cast (item())->setValue(newValue); +} +//END VariableStringEditor + + + +//BEGIN VariableSpellCheckEditor +VariableSpellCheckEditor::VariableSpellCheckEditor(VariableSpellCheckItem *item, QWidget *parent) + : VariableEditor(item, parent) +{ + QGridLayout *l = (QGridLayout*) layout(); + + m_dictionaryCombo = new Sonnet::DictionaryComboBox(this); + m_dictionaryCombo->setCurrentByDictionary(item->value()); + l->addWidget(m_dictionaryCombo, 0, 2, Qt::AlignLeft); + + connect(m_dictionaryCombo, SIGNAL(dictionaryNameChanged(QString)), this, SIGNAL(valueChanged())); + connect(m_dictionaryCombo, SIGNAL(dictionaryNameChanged(QString)), this, SLOT(activateItem())); + connect(m_dictionaryCombo, SIGNAL(dictionaryChanged(QString)), this, SLOT(setItemValue(QString))); +} + +void VariableSpellCheckEditor::setItemValue(const QString &newValue) +{ + static_cast (item())->setValue(newValue); +} + +//END VariableSpellCheckEditor + + + +//BEGIN VariableRemoveSpacesEditor +VariableRemoveSpacesEditor::VariableRemoveSpacesEditor(VariableRemoveSpacesItem* item, QWidget* parent) + : VariableEditor(item, parent) +{ + QGridLayout* l = (QGridLayout *) layout(); + + m_comboBox = new QComboBox(this); + m_comboBox->addItem(i18nc("value for variable remove-trailing-spaces", "none")); + m_comboBox->addItem(i18nc("value for variable remove-trailing-spaces", "modified")); + m_comboBox->addItem(i18nc("value for variale remove-trailing-spaces", "all")); + m_comboBox->setCurrentIndex(item->value()); + l->addWidget(m_comboBox, 0, 2, Qt::AlignLeft); + + connect(m_comboBox, SIGNAL(currentIndexChanged(int)), this, SIGNAL(valueChanged())); + connect(m_comboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(activateItem())); + connect(m_comboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(setItemValue(int))); +} + +void VariableRemoveSpacesEditor::setItemValue(int enabled) +{ + static_cast(item())->setValue(enabled == 0); +} +//END VariableRemoveSpacesEditor + +// kate: indent-width 2; replace-tabs on; diff --git a/kate/part/variableeditor/variableeditor.h b/kate/part/variableeditor/variableeditor.h new file mode 100644 index 00000000..c0cabe03 --- /dev/null +++ b/kate/part/variableeditor/variableeditor.h @@ -0,0 +1,191 @@ +/* This file is part of the KDE project + + Copyright (C) 2011 Dominik Haumann + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef VARIABLE_EDITOR_H +#define VARIABLE_EDITOR_H + +#include + +class KateHelpButton; + +class VariableBoolItem; +class VariableColorItem; +class VariableFontItem; +class VariableItem; +class VariableStringListItem; +class VariableIntItem; +class VariableStringItem; +class VariableSpellCheckItem; +class VariableRemoveSpacesItem; + +class KColorCombo; +class KFontComboBox; +#include +#include +#include +#include +#include + +namespace Sonnet { + class DictionaryComboBox; +} + +class VariableEditor : public QWidget +{ + Q_OBJECT + +public: + VariableEditor(VariableItem* item, QWidget* parent = 0); + virtual ~VariableEditor(); + + VariableItem* item() const; + +Q_SIGNALS: + void valueChanged(); + +protected Q_SLOTS: + void itemEnabled(bool enabled); + void activateItem(); + +protected: + virtual void paintEvent(QPaintEvent* event); + virtual void enterEvent(QEvent* event); + virtual void leaveEvent(QEvent* event); + + +private: + VariableItem* m_item; + + QCheckBox* m_checkBox; + QLabel* m_variable; + QLabel* m_helpText; + KateHelpButton* m_btnHelp; +}; + +class VariableIntEditor : public VariableEditor +{ + Q_OBJECT +public: + VariableIntEditor(VariableIntItem* item, QWidget* parent); + +protected Q_SLOTS: + void setItemValue(int newValue); + +private: + QSpinBox* m_spinBox; +}; + +class VariableBoolEditor : public VariableEditor +{ + Q_OBJECT +public: + VariableBoolEditor(VariableBoolItem* item, QWidget* parent); + +protected Q_SLOTS: + void setItemValue(int enabled); + +private: + QComboBox* m_comboBox; +}; + +class VariableStringListEditor : public VariableEditor +{ + Q_OBJECT +public: + VariableStringListEditor(VariableStringListItem* item, QWidget* parent); + +protected Q_SLOTS: + void setItemValue(const QString& newValue); + +private: + QComboBox* m_comboBox; +}; + +class VariableColorEditor : public VariableEditor +{ + Q_OBJECT +public: + VariableColorEditor(VariableColorItem* item, QWidget* parent); + +protected Q_SLOTS: + void setItemValue(const QColor& newValue); + +private: + KColorCombo* m_comboBox; +}; + +class VariableFontEditor : public VariableEditor +{ + Q_OBJECT +public: + VariableFontEditor(VariableFontItem* item, QWidget* parent); + +protected Q_SLOTS: + void setItemValue(const QFont& newValue); + +private: + KFontComboBox* m_comboBox; +}; + + +class VariableStringEditor : public VariableEditor +{ + Q_OBJECT +public: + VariableStringEditor(VariableStringItem* item, QWidget* parent); + +protected Q_SLOTS: + void setItemValue(const QString& newValue); + +private: + QLineEdit* m_lineEdit; +}; + + +class VariableSpellCheckEditor : public VariableEditor +{ + Q_OBJECT +public: + VariableSpellCheckEditor(VariableSpellCheckItem* item, QWidget* parent); + +protected Q_SLOTS: + void setItemValue(const QString& newValue); + +private: + Sonnet::DictionaryComboBox *m_dictionaryCombo; +}; + + +class VariableRemoveSpacesEditor : public VariableEditor +{ + Q_OBJECT +public: + VariableRemoveSpacesEditor(VariableRemoveSpacesItem* item, QWidget* parent); + +protected Q_SLOTS: + void setItemValue(int enabled); + +private: + QComboBox* m_comboBox; +}; + +#endif + +// kate: indent-width 2; replace-tabs on; diff --git a/kate/part/variableeditor/variableitem.cpp b/kate/part/variableeditor/variableitem.cpp new file mode 100644 index 00000000..16c3c855 --- /dev/null +++ b/kate/part/variableeditor/variableitem.cpp @@ -0,0 +1,379 @@ +/* This file is part of the KDE project + + Copyright (C) 2011 Dominik Haumann + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "variableitem.h" +#include "variableeditor.h" + + +//BEGIN class VariableItem +VariableItem::VariableItem(const QString& variable) + : m_variable(variable) + , m_active(false) +{ +} + +VariableItem::~VariableItem() +{ +} + +QString VariableItem::variable() const +{ + return m_variable; +} + +QString VariableItem::helpText() const +{ + return m_helpText; +} + +void VariableItem::setHelpText(const QString& text) +{ + m_helpText = text; +} + +bool VariableItem::isActive() const +{ + return m_active; +} + +void VariableItem::setActive(bool active) +{ + m_active = active; +} +//END class VariableItem + + + +//BEGIN class VariableIntItem +VariableIntItem::VariableIntItem(const QString& variable, int value) + : VariableItem(variable) + , m_value(value) + , m_minValue(-20000) + , m_maxValue(20000) +{ +} + +int VariableIntItem::value() const +{ + return m_value; +} + +void VariableIntItem::setValue(int newValue) +{ + m_value = newValue; +} + +void VariableIntItem::setValueByString(const QString& value) +{ + setValue(value.toInt()); +} + +QString VariableIntItem::valueAsString() const +{ + return QString::number(value()); +} + +VariableEditor* VariableIntItem::createEditor(QWidget* parent) +{ + return new VariableIntEditor(this, parent); +} + +void VariableIntItem::setRange(int minValue, int maxValue) +{ + m_minValue = minValue; + m_maxValue = maxValue; +} + +int VariableIntItem::minValue() const +{ + return m_minValue; +} + +int VariableIntItem::maxValue() const +{ + return m_maxValue; +} +//END class VariableIntItem + + + +//BEGIN class VariableStringListItem +VariableStringListItem::VariableStringListItem(const QString& variable, const QStringList& slist, const QString& value) + : VariableItem(variable) + , m_list(slist) + , m_value(value) +{ +} + +QStringList VariableStringListItem::stringList() const +{ + return m_list; +} + +QString VariableStringListItem::value() const +{ + return m_value; +} + +void VariableStringListItem::setValue(const QString& newValue) +{ + m_value = newValue; +} + +void VariableStringListItem::setValueByString(const QString& value) +{ + setValue(value); +} + +QString VariableStringListItem::valueAsString() const +{ + return value(); +} + +VariableEditor* VariableStringListItem::createEditor(QWidget* parent) +{ + return new VariableStringListEditor(this, parent); +} +//END class VariableStringListItem + + + +//BEGIN class VariableBoolItem +VariableBoolItem::VariableBoolItem(const QString& variable, bool value) + : VariableItem(variable) + , m_value(value) +{ +} + +bool VariableBoolItem::value() const +{ + return m_value; +} + +void VariableBoolItem::setValue(bool enabled) +{ + m_value = enabled; +} + +void VariableBoolItem::setValueByString(const QString& value) +{ + QString tmp = value.trimmed().toLower(); + bool enabled = (tmp == "on") || (tmp == "1") || (tmp == "true"); + setValue(enabled); +} + +QString VariableBoolItem::valueAsString() const +{ + return value() ? QString("true") : QString("false"); +} + +VariableEditor* VariableBoolItem::createEditor(QWidget* parent) +{ + return new VariableBoolEditor(this, parent); +} +//END class VariableBoolItem + + + +//BEGIN class VariableColorItem +VariableColorItem::VariableColorItem(const QString& variable, const QColor& value) + : VariableItem(variable) + , m_value(value) +{ +} + +QColor VariableColorItem::value() const +{ + return m_value; +} + +void VariableColorItem::setValue(const QColor& value) +{ + m_value = value; +} + +void VariableColorItem::setValueByString(const QString& value) +{ + setValue(QColor(value)); +} + +QString VariableColorItem::valueAsString() const +{ + return value().name(); +} + +VariableEditor* VariableColorItem::createEditor(QWidget* parent) +{ + return new VariableColorEditor(this, parent); +} +//END class VariableColorItem + + + +//BEGIN class VariableFontItem +VariableFontItem::VariableFontItem(const QString& variable, const QFont& value) + : VariableItem(variable) + , m_value(value) +{ +} + +QFont VariableFontItem::value() const +{ + return m_value; +} + +void VariableFontItem::setValue(const QFont& value) +{ + m_value = value; +} + +void VariableFontItem::setValueByString(const QString& value) +{ + setValue(QFont(value)); +} + +QString VariableFontItem::valueAsString() const +{ + return value().family(); +} + +VariableEditor* VariableFontItem::createEditor(QWidget* parent) +{ + return new VariableFontEditor(this, parent); +} +//END class VariableFontItem + + + +//BEGIN class VariableStringItem +VariableStringItem::VariableStringItem(const QString& variable, const QString& value) + : VariableItem(variable) + , m_value(value) +{ +} + +void VariableStringItem::setValue(const QString& value) +{ + m_value = value; +} + +void VariableStringItem::setValueByString(const QString& value) +{ + m_value = value; +} + +QString VariableStringItem::value() const +{ + return m_value; +} + +QString VariableStringItem::valueAsString() const +{ + return m_value; +} + +VariableEditor* VariableStringItem::createEditor(QWidget* parent) +{ + return new VariableStringEditor(this, parent); +} +//END class VariableStringItem + + +//BEGIN class VariableSpellCheckItem +VariableSpellCheckItem::VariableSpellCheckItem(const QString &variable, const QString &value) + : VariableItem(variable) + , m_value(value) +{ +} + +QString VariableSpellCheckItem::value() const +{ + return m_value; +} + +void VariableSpellCheckItem::setValue(const QString &value) +{ + m_value = value; +} + +QString VariableSpellCheckItem::valueAsString() const +{ + return m_value; +} + +void VariableSpellCheckItem::setValueByString(const QString &value) +{ + m_value = value; +} + +VariableEditor* VariableSpellCheckItem::createEditor(QWidget *parent) +{ + return new VariableSpellCheckEditor(this, parent); +} +//END class VariableSpellCheckItem + +//BEGIN class VariableRemoveSpacesItem +VariableRemoveSpacesItem::VariableRemoveSpacesItem(const QString &variable, int value) + : VariableItem(variable) + , m_value(value) +{ +} + +int VariableRemoveSpacesItem::value() const +{ + return m_value; +} + +void VariableRemoveSpacesItem::setValue(int value) +{ + m_value = value; +} + +QString VariableRemoveSpacesItem::valueAsString() const +{ + if (m_value == 2) { + return "all"; + } else if (m_value == 1) { + return "modified"; + } else { + return "none"; + } +} + +void VariableRemoveSpacesItem::setValueByString(const QString &value) +{ + QString tmp = value.trimmed().toLower(); + + if (tmp == "1" || tmp == "modified" || tmp == "mod" || tmp == "+") { + m_value = 1; + } else if (tmp == "2" || tmp == "all" || tmp == "*") { + m_value = 2; + } else { + m_value = 0; + } +} + +VariableEditor* VariableRemoveSpacesItem::createEditor(QWidget *parent) +{ + return new VariableRemoveSpacesEditor(this, parent); +} +//END class VariableRemoveSpacesItem + +// kate: indent-width 2; replace-tabs on; diff --git a/kate/part/variableeditor/variableitem.h b/kate/part/variableeditor/variableitem.h new file mode 100644 index 00000000..4db9d136 --- /dev/null +++ b/kate/part/variableeditor/variableitem.h @@ -0,0 +1,239 @@ +/* This file is part of the KDE project + + Copyright (C) 2011 Dominik Haumann + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef VARIABLE_ITEM_H +#define VARIABLE_ITEM_H + +#include +#include +#include +#include + +class VariableEditor; + +//BEGIN class VariableItem +class VariableItem +{ +public: + VariableItem(const QString& variable); + virtual ~VariableItem(); + + QString variable() const; + QString helpText() const; + void setHelpText(const QString& text); + + bool isActive() const; + void setActive(bool active); + + virtual void setValueByString(const QString& value) = 0; + virtual QString valueAsString() const = 0; + + virtual VariableEditor* createEditor(QWidget* parent) = 0; + +private: + // not implemented: copy constructor and operator= + VariableItem(const VariableItem& copy); + VariableItem& operator=(const VariableItem& other); + + QString m_variable; + QString m_helpText; + bool m_active; +}; +//END class VariableItem + + +// +// DERIVE A CLASS FOR EACH TYPE +// + +//BEGIN class VariableIntItem +class VariableIntItem : public VariableItem +{ +public: + VariableIntItem(const QString& variable, int value); + + int value() const; + void setValue(int newValue); + void setRange(int minValue, int maxValue); + int minValue() const; + int maxValue() const; + +public: + virtual void setValueByString(const QString& value); + virtual QString valueAsString() const; + virtual VariableEditor* createEditor(QWidget* parent); + +private: + int m_value; + int m_minValue; + int m_maxValue; + +}; +//END class VariableIntItem + + +//BEGIN class VariableStringListItem +class VariableStringListItem : public VariableItem +{ +public: + VariableStringListItem(const QString& variable, const QStringList& slist, const QString& value); + + QStringList stringList() const; + + QString value() const; + void setValue(const QString& newValue); + +public: + virtual void setValueByString(const QString& value); + virtual QString valueAsString() const; + virtual VariableEditor* createEditor(QWidget* parent); + +private: + QStringList m_list; + QString m_value; +}; +//END class VariableStringListItem + + + +//BEGIN class VariableBoolItem +class VariableBoolItem : public VariableItem +{ +public: + VariableBoolItem(const QString& variable, bool value); + + bool value() const; + void setValue(bool enabled); + +public: + virtual void setValueByString(const QString& value); + virtual QString valueAsString() const; + virtual VariableEditor* createEditor(QWidget* parent); + +private: + bool m_value; +}; +//END class VariableBoolItem + + + +//BEGIN class VariableColorItem +class VariableColorItem : public VariableItem +{ +public: + VariableColorItem(const QString& variable, const QColor& value); + + QColor value() const; + void setValue(const QColor& color); + +public: + virtual void setValueByString(const QString& value); + virtual QString valueAsString() const; + virtual VariableEditor* createEditor(QWidget* parent); + +private: + QColor m_value; +}; +//END class VariableColorItem + + + +//BEGIN class VariableFontItem +class VariableFontItem : public VariableItem +{ +public: + VariableFontItem(const QString& variable, const QFont& value); + + QFont value() const; + void setValue(const QFont& value); + +public: + virtual void setValueByString(const QString& value); + virtual QString valueAsString() const; + virtual VariableEditor* createEditor(QWidget* parent); + +private: + QFont m_value; +}; +//END class VariableFontItem + + + +//BEGIN class VariableStringItem +class VariableStringItem : public VariableItem +{ +public: + VariableStringItem(const QString &variable, const QString &value); + + QString value() const; + void setValue(const QString& value); + +public: + virtual void setValueByString(const QString& value); // Same as setValue() in this case, implemented for uniformity + virtual QString valueAsString() const; // Same as value() in this case, implemented for uniformity + virtual VariableEditor* createEditor(QWidget* parent); + +private: + QString m_value; +}; + +//END class VariableStringItem + +//BEGIN class VariableSpellCheckItem +class VariableSpellCheckItem : public VariableItem +{ +public: + VariableSpellCheckItem(const QString &variable, const QString &value); + + QString value() const; + void setValue(const QString& value); + +public: + virtual void setValueByString(const QString &value); // Same as setValue() in this case, implemented for uniformity + virtual QString valueAsString() const; // Same as value() in this case, implemented for uniformity + virtual VariableEditor* createEditor(QWidget *parent); + +private: + QString m_value; +}; +//END class VariableSpellCheckItem + +//BEGIN class VariableRemoveSpacesItem +class VariableRemoveSpacesItem : public VariableItem +{ +public: + VariableRemoveSpacesItem(const QString &variable, int value); + + int value() const; + void setValue(int value); + +public: + virtual void setValueByString(const QString &value); // Same as setValue() in this case, implemented for uniformity + virtual QString valueAsString() const; // Same as value() in this case, implemented for uniformity + virtual VariableEditor* createEditor(QWidget *parent); + +private: + int m_value; +}; +//END class VariableRemoveSpacesItem + +#endif + +// kate: indent-width 2; replace-tabs on; diff --git a/kate/part/variableeditor/variablelineedit.cpp b/kate/part/variableeditor/variablelineedit.cpp new file mode 100644 index 00000000..d3ec19c2 --- /dev/null +++ b/kate/part/variableeditor/variablelineedit.cpp @@ -0,0 +1,363 @@ +/* This file is part of the KDE project + + Copyright (C) 2011 Dominik Haumann + Copyright (C) 2013 Gerald Senarclens de Grancy + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "variablelineedit.h" + +#include "variableitem.h" +#include "variablelistview.h" +#include "kateautoindent.h" +#include "katesyntaxmanager.h" +#include "kateschema.h" +#include "kateview.h" +#include "katedocument.h" +#include "kateglobal.h" +#include "katerenderer.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +VariableLineEdit::VariableLineEdit(QWidget* parent) + : QWidget(parent) +{ + m_listview = 0; + + QHBoxLayout* hl = new QHBoxLayout(); + hl->setMargin(0); + hl->setSpacing(KDialog::spacingHint()); + setLayout(hl); + + m_lineedit = new QLineEdit(this); + m_button= new QToolButton(this); + m_button->setIcon(KIcon("tools-wizard")); + m_button->setToolTip(i18n("Show list of valid variables.")); + + hl->addWidget(m_lineedit); + hl->addWidget(m_button); + + m_popup = new QFrame(0, Qt::Popup); + m_popup->setFrameStyle(QFrame::StyledPanel | QFrame::Raised); + QVBoxLayout* l = new QVBoxLayout(m_popup); + l->setSpacing(0); + l->setMargin(0); + m_popup->setLayout(l); + + // forward text changed signal + connect(m_lineedit, SIGNAL(textChanged(QString)), this, SIGNAL(textChanged(QString))); + + // open popup on button click + connect(m_button, SIGNAL(clicked()), this, SLOT(editVariables())); +} + +VariableLineEdit::~VariableLineEdit() +{ +} + +void VariableLineEdit::editVariables() +{ + m_listview = new VariableListView(m_lineedit->text(), m_popup); + addKateItems(m_listview); + connect(m_listview, SIGNAL(aboutToHide()), this, SLOT(updateVariableLine())); + + m_popup->layout()->addWidget(m_listview); + + if (layoutDirection() == Qt::LeftToRight) { + QPoint topLeft = mapToGlobal(m_lineedit->geometry().bottomLeft()); + const int w = m_button->geometry().right() - m_lineedit->geometry().left(); + const int h = 300; //(w * 2) / 4; + m_popup->setGeometry(QRect(topLeft, QSize(w, h))); + } else { + QPoint topLeft = mapToGlobal(m_button->geometry().bottomLeft()); + const int w = m_lineedit->geometry().right() - m_button->geometry().left(); + const int h = 300; //(w * 2) / 4; + m_popup->setGeometry(QRect(topLeft, QSize(w, h))); + } + m_popup->show(); +} + +void VariableLineEdit::updateVariableLine() +{ + QString variables = m_listview->variableLine(); + m_lineedit->setText(variables); + + m_popup->layout()->removeWidget(m_listview); + m_listview->deleteLater(); + m_listview = 0; +} + +void VariableLineEdit::addKateItems(VariableListView* listview) +{ + VariableItem* item = 0; + + // If a current active doc is available + KateView* activeView = 0; + KateDocument* activeDoc = 0; + + KateDocumentConfig* docConfig = KateDocumentConfig::global(); + KateViewConfig* viewConfig = KateViewConfig::global(); + KateRendererConfig *rendererConfig = KateRendererConfig::global(); + + KTextEditor::MdiContainer *iface = qobject_cast(KateGlobal::self()->container()); + if (iface) { + activeView = qobject_cast(iface->activeView()); + + if (activeView) { + activeDoc = activeView->doc(); + viewConfig = activeView->config(); + docConfig = activeDoc->config(); + rendererConfig = activeView->renderer()->config(); + } + } + + // Add 'auto-center-lines' to list + item = new VariableIntItem("auto-center-lines", viewConfig->autoCenterLines()); + static_cast(item)->setRange(1, 100); + item->setHelpText(i18nc("short translation please", "Set the number of autocenter lines.")); + listview->addItem(item); + + // Add 'auto-insert-doxygen' to list + item = new VariableBoolItem("auto-insert-doxygen", false); + item->setHelpText(i18nc("short translation please", "Auto insert asterisk in doxygen comments.")); + listview->addItem(item); + + // Add 'background-color' to list + item = new VariableColorItem("background-color", rendererConfig->backgroundColor()); + item->setHelpText(i18nc("short translation please", "Set the document background color.")); + listview->addItem(item); + + // Add 'backspace-indents' to list + item = new VariableBoolItem("backspace-indents", docConfig->backspaceIndents()); + item->setHelpText(i18nc("short translation please", "Pressing backspace in leading whitespace unindents.")); + listview->addItem(item); + + // Add 'block-selection' to list + item = new VariableBoolItem("block-selection", false); + if (activeView) static_cast(item)->setValue(activeView->blockSelection()); + item->setHelpText(i18nc("short translation please", "Enable block selection mode.")); + listview->addItem(item); + + // Add 'byte-order-marker' (bom) to list + item = new VariableBoolItem("byte-order-marker", docConfig->bom()); + item->setHelpText(i18nc("short translation please", "Enable the byte order marker when saving unicode files.")); + listview->addItem(item); + + // Add 'bracket-highlight-color' to list + item = new VariableColorItem("bracket-highlight-color", rendererConfig->highlightedBracketColor()); + item->setHelpText(i18nc("short translation please", "Set the color for the bracket highlight.")); + listview->addItem(item); + + // Add 'current-line-color' to list + item = new VariableColorItem("current-line-color", rendererConfig->highlightedLineColor()); + item->setHelpText(i18nc("short translation please", "Set the background color for the current line.")); + listview->addItem(item); + + // Add 'default-dictionary' to list + Sonnet::Speller speller; + item = new VariableSpellCheckItem("default-dictionary", speller.defaultLanguage()); + item->setHelpText(i18nc("short translation please", "Set the default dictionary used for spell checking.")); + listview->addItem(item); + + // Add 'dynamic-word-wrap' to list + item = new VariableBoolItem("dynamic-word-wrap", viewConfig->dynWordWrap()); + item->setHelpText(i18nc("short translation please", "Enable dynamic word wrap of long lines.")); + listview->addItem(item); + + // Add 'end-of-line' (eol) to list + item = new VariableStringListItem("end-of-line", QStringList() << "unix" << "mac" << "dos", docConfig->eolString()); + item->setHelpText(i18nc("short translation please", "Sets the end of line mode.")); + listview->addItem(item); + + // Add 'folding-markers' to list + item = new VariableBoolItem("folding-markers", viewConfig->foldingBar()); + item->setHelpText(i18nc("short translation please", "Enable folding markers in the editor border.")); + listview->addItem(item); + + // Add 'font-size' to list + item = new VariableIntItem("font-size", rendererConfig->font().pointSize()); + static_cast(item)->setRange(4, 128); + item->setHelpText(i18nc("short translation please", "Set the point size of the document font.")); + listview->addItem(item); + + // Add 'font' to list + item = new VariableFontItem("font", rendererConfig->font()); + item->setHelpText(i18nc("short translation please", "Set the font of the document.")); + listview->addItem(item); + + // Add 'syntax' (hl) to list + /* Prepare list of highlighting modes */ + const int count = KateHlManager::self()->highlights(); + QStringList hl; + for (int z = 0; z < count; ++z) + hl << KateHlManager::self()->hlName(z); + + item = new VariableStringListItem("syntax", hl, hl.at(0)); + if (activeDoc) static_cast(item)->setValue(activeDoc->highlightingMode()); + item->setHelpText(i18nc("short translation please", "Set the syntax highlighting.")); + listview->addItem(item); + + // Add 'icon-bar-color' to list + item = new VariableColorItem("icon-bar-color", rendererConfig->iconBarColor()); + item->setHelpText(i18nc("short translation please", "Set the icon bar color.")); + listview->addItem(item); + + // Add 'icon-border' to list + item = new VariableBoolItem("icon-border", viewConfig->iconBar()); + item->setHelpText(i18nc("short translation please", "Enable the icon border in the editor view.")); + listview->addItem(item); + + // Add 'indent-mode' to list + item = new VariableStringListItem("indent-mode", KateAutoIndent::listIdentifiers(), docConfig->indentationMode()); + item->setHelpText(i18nc("short translation please", "Set the auto indentation style.")); + listview->addItem(item); + + // Add 'indent-pasted-text' to list + item = new VariableBoolItem("indent-pasted-text", docConfig->indentPastedText()); + item->setHelpText(i18nc("short translation please", "Adjust indentation of text pasted from the clipboard.")); + listview->addItem(item); + + // Add 'indent-width' to list + item = new VariableIntItem("indent-width", docConfig->indentationWidth()); + static_cast(item)->setRange(1, 16); + item->setHelpText(i18nc("short translation please", "Set the indentation depth for each indent level.")); + listview->addItem(item); + + // Add 'keep-extra-spaces' to list + item = new VariableBoolItem("keep-extra-spaces", docConfig->keepExtraSpaces()); + item->setHelpText(i18nc("short translation please", "Allow odd indentation level (no multiple of indent width).")); + listview->addItem(item); + + // Add 'line-numbers' to list + item = new VariableBoolItem("line-numbers", viewConfig->lineNumbers()); + item->setHelpText(i18nc("short translation please", "Show line numbers.")); + listview->addItem(item); + + // Add 'newline-at-eof' to list + item = new VariableBoolItem("newline-at-eof", docConfig->ovr()); + item->setHelpText(i18nc("short translation please", "Insert newline at end of file on save.")); + listview->addItem(item); + + // Add 'overwrite-mode' to list + item = new VariableBoolItem("overwrite-mode", docConfig->ovr()); + item->setHelpText(i18nc("short translation please", "Enable overwrite mode in the document.")); + listview->addItem(item); + + // Add 'persistent-selection' to list + item = new VariableBoolItem("persistent-selection", viewConfig->persistentSelection()); + item->setHelpText(i18nc("short translation please", "Enable persistent text selection.")); + listview->addItem(item); + + // Add 'replace-tabs-save' to list + item = new VariableBoolItem("replace-tabs-save", false); + item->setHelpText(i18nc("short translation please", "Replace tabs with spaces when saving the document.")); + listview->addItem(item); + + // Add 'replace-tabs' to list + item = new VariableBoolItem("replace-tabs", docConfig->replaceTabsDyn()); + item->setHelpText(i18nc("short translation please", "Replace tabs with spaces.")); + listview->addItem(item); + + // Add 'remove-trailing-spaces' to list + item = new VariableRemoveSpacesItem("remove-trailing-spaces", docConfig->removeSpaces()); + item->setHelpText(i18nc("short translation please", "Remove trailing spaces when saving the document.")); + listview->addItem(item); + + // Add 'scheme' to list + QStringList schemas; + Q_FOREACH (const KateSchema &schema, KateGlobal::self()->schemaManager()->list()) + schemas.append (schema.rawName); + item = new VariableStringListItem("scheme", schemas, rendererConfig->schema()); + item->setHelpText(i18nc("short translation please", "Set the color scheme.")); + listview->addItem(item); + + // Add 'selection-color' to list + item = new VariableColorItem("selection-color", rendererConfig->selectionColor()); + item->setHelpText(i18nc("short translation please", "Set the text selection color.")); + listview->addItem(item); + + // Add 'show-tabs' to list + item = new VariableBoolItem("show-tabs", docConfig->showTabs()); + item->setHelpText(i18nc("short translation please", "Visualize tabs and trailing spaces.")); + listview->addItem(item); + + // Add 'smart-home' to list + item = new VariableBoolItem("smart-home", docConfig->smartHome()); + item->setHelpText(i18nc("short translation please", "Enable smart home navigation.")); + listview->addItem(item); + + // Add 'tab-indents' to list + item = new VariableBoolItem("tab-indents", docConfig->tabIndentsEnabled()); + item->setHelpText(i18nc("short translation please", "Pressing TAB key indents.")); + listview->addItem(item); + + // Add 'tab-width' to list + item = new VariableIntItem("tab-width", docConfig->tabWidth()); + static_cast(item)->setRange(1, 16); + item->setHelpText(i18nc("short translation please", "Set the tab display width.")); + listview->addItem(item); + + // Add 'undo-steps' to list + item = new VariableIntItem("undo-steps", 0); + static_cast(item)->setRange(0, 100); + item->setHelpText(i18nc("short translation please", "Set the number of undo steps to remember (0 equals infinity).")); + listview->addItem(item); + + // Add 'word-wrap-column' to list + item = new VariableIntItem("word-wrap-column", docConfig->wordWrapAt()); + static_cast(item)->setRange(20, 200); + item->setHelpText(i18nc("short translation please", "Set the word wrap column.")); + listview->addItem(item); + + // Add 'word-wrap-marker-color' to list + item = new VariableColorItem("word-wrap-marker-color", rendererConfig->wordWrapMarkerColor()); + item->setHelpText(i18nc("short translation please", "Set the word wrap marker color.")); + listview->addItem(item); + + // Add 'word-wrap' to list + item = new VariableBoolItem("word-wrap", docConfig->wordWrap()); + item->setHelpText(i18nc("short translation please", "Enable word wrap while typing text.")); + listview->addItem(item); +} + +void VariableLineEdit::setText(const QString &text) +{ + m_lineedit->setText(text); +} + +void VariableLineEdit::clear() +{ + m_lineedit->clear(); +} + +QString VariableLineEdit::text() +{ + return m_lineedit->text(); +} + +// kate: indent-width 2; replace-tabs on; diff --git a/kate/part/variableeditor/variablelineedit.h b/kate/part/variableeditor/variablelineedit.h new file mode 100644 index 00000000..d6329000 --- /dev/null +++ b/kate/part/variableeditor/variablelineedit.h @@ -0,0 +1,60 @@ +/* This file is part of the KDE project + + Copyright (C) 2011 Dominik Haumann + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef VARIABLE_LINE_EDIT_H +#define VARIABLE_LINE_EDIT_H + +#include + +#include +#include +#include +class VariableListView; + +class VariableLineEdit : public QWidget +{ + Q_OBJECT + +public: + VariableLineEdit(QWidget* parent = 0); + virtual ~VariableLineEdit(); + + void addKateItems(VariableListView* listview); + QString text(); + +public Q_SLOTS: + void editVariables(); + void setText(const QString &text); + void clear(); + void updateVariableLine(); + +Q_SIGNALS: + void textChanged(const QString&); + +private: + QFrame* m_popup; + QLineEdit* m_lineedit; + QToolButton* m_button; + VariableListView* m_listview; +}; + +#endif + +// kate: indent-width 2; replace-tabs on; diff --git a/kate/part/variableeditor/variablelistview.cpp b/kate/part/variableeditor/variablelistview.cpp new file mode 100644 index 00000000..b1e0c004 --- /dev/null +++ b/kate/part/variableeditor/variablelistview.cpp @@ -0,0 +1,142 @@ +/* This file is part of the KDE project + + Copyright (C) 2011 Dominik Haumann + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "variableeditor.h" +#include "variablelistview.h" +#include "variableitem.h" + +#include + +VariableListView::VariableListView(const QString& variableLine, QWidget* parent) + : QScrollArea(parent) +{ + setBackgroundRole(QPalette::Base); + + setWidget(new QWidget(this)); + + parseVariables(variableLine); +} + +VariableListView::~VariableListView() +{ +} + +void VariableListView::parseVariables(const QString& line) +{ + QString tmp = line.trimmed(); + if (tmp.startsWith("kate:")) { + tmp.remove(0, 5); + } + + QStringList variables = tmp.split(';', QString::SkipEmptyParts); + + QRegExp sep("\\s+"); + for (int i = 0; i < variables.size(); ++i) { + QStringList pair = variables[i].split(sep, QString::SkipEmptyParts); + if (pair.size() < 2) { + continue; + } + if (pair.size() > 2) { // e.g. fonts have spaces in the value. Hence, join all value items again + QString key = pair[0]; + pair.removeAt(0); + QString value = pair.join(" "); + pair.clear(); + pair << key << value; + } + + m_variables[pair[0]] = pair[1]; + } +} + +void VariableListView::addItem(VariableItem* item) +{ + // overwrite default value when variable exists in modeline + if (m_variables.contains(item->variable())) { + item->setValueByString(m_variables[item->variable()]); + item->setActive(true); + } + + VariableEditor* editor = item->createEditor(widget()); + editor->setBackgroundRole((m_editors.size() % 2) ? QPalette::AlternateBase : QPalette::Base); + + m_editors << editor; + m_items << item; + + connect(editor, SIGNAL(valueChanged()), this, SIGNAL(changed())); +} + +void VariableListView::resizeEvent(QResizeEvent* event) +{ + QScrollArea::resizeEvent(event); + + // calculate sum of all editor heights + int listHeight = 0; + foreach (QWidget* w, m_editors) { + listHeight += w->sizeHint().height(); + } + + // resize scroll area widget + QWidget* top = widget(); + top->resize(event->size().width(), listHeight); + + // set client geometries correctly + int h = 0; + foreach (QWidget* w, m_editors) { + w->setGeometry(0, h, top->width(), w->sizeHint().height()); + h += w->sizeHint().height(); + } +} + +void VariableListView::hideEvent(QHideEvent* event) +{ + if (!event->spontaneous()) { + emit aboutToHide(); + } + QScrollArea::hideEvent(event); +} + +QString VariableListView::variableLine() +{ + for (int i = 0; i < m_items.size(); ++i) { + VariableItem* item = m_items[i]; + + if (item->isActive()) { + m_variables[item->variable()] = item->valueAsString(); + } else if (m_variables.contains(item->variable())) { + m_variables.remove(item->variable()); + } + } + + QString line; + QMap::const_iterator it = m_variables.constBegin(); + while (it != m_variables.constEnd()) { + if (!line.isEmpty()) { + line += " "; + } + line += it.key() + ' ' + it.value() + ';'; + + ++it; + } + + line.prepend("kate: "); + + return line; +} +// kate: indent-width 2; replace-tabs on; diff --git a/kate/part/variableeditor/variablelistview.h b/kate/part/variableeditor/variablelistview.h new file mode 100644 index 00000000..197b0193 --- /dev/null +++ b/kate/part/variableeditor/variablelistview.h @@ -0,0 +1,61 @@ +/* This file is part of the KDE project + + Copyright (C) 2011 Dominik Haumann + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef VARIABLE_LIST_VIEW_H +#define VARIABLE_LIST_VIEW_H + +#include +#include + +class VariableItem; +class VariableEditor; + +class VariableListView : public QScrollArea +{ + Q_OBJECT + +public: + VariableListView(const QString& variableLine, QWidget* parent = 0); + virtual ~VariableListView(); + + void addItem(VariableItem* item); + + /// always returns the up-to-date variables line + QString variableLine(); + +Q_SIGNALS: + void aboutToHide(); + void changed(); // unused right now + +protected: + virtual void resizeEvent(QResizeEvent* event); + virtual void hideEvent(QHideEvent* event); + + void parseVariables(const QString& line); + + QVector m_items; + QVector m_editors; + + QMap m_variables; +}; + +#endif + +// kate: indent-width 2; replace-tabs on; diff --git a/kate/part/view/kateanimation.cpp b/kate/part/view/kateanimation.cpp new file mode 100644 index 00000000..84abfc04 --- /dev/null +++ b/kate/part/view/kateanimation.cpp @@ -0,0 +1,119 @@ +/* This file is part of the KDE and the Kate project + * + * Copyright (C) 2013 Dominik Haumann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "kateanimation.h" +#include "moc_kateanimation.cpp" +#include + +#include +#include +#include + +KateAnimation::KateAnimation(KMessageWidget* widget, EffectType effect) + : QObject(widget) + , m_widget(widget) + , m_fadeEffect(0) +{ + Q_ASSERT(m_widget != 0); + + // create wanted effect + if (effect == FadeEffect) { + m_fadeEffect = new KateFadeEffect(widget); + } + + // create tracking timer for hiding the widget + m_hideTimer = new QTimer(this); + m_hideTimer->setInterval(550); // 500 from KMessageWidget + 50 to make sure the hide animation is really finished + m_hideTimer->setSingleShot(true); + connect(m_hideTimer, SIGNAL(timeout()), this, SIGNAL(widgetHidden())); + + // create tracking timer for showing the widget + m_showTimer = new QTimer(this); + m_showTimer->setInterval(550); // 500 from KMessageWidget + 50 to make sure the show animation is really finished + m_showTimer->setSingleShot(true); + connect(m_showTimer, SIGNAL(timeout()), this, SIGNAL(widgetShown())); +} + +bool KateAnimation::hideAnimationActive() const +{ + return m_hideTimer->isActive(); +} + +bool KateAnimation::showAnimationActive() const +{ + return m_showTimer->isActive(); +} + +void KateAnimation::show() +{ + Q_ASSERT(m_widget != 0); + + // stop hide timer if needed + if (m_hideTimer->isActive()) { + m_hideTimer->stop(); + } + + // show according to effects config + if (!(KGlobalSettings::graphicEffectsLevel() & KGlobalSettings::SimpleAnimationEffects)) { + m_widget->show(); + emit widgetShown(); + } else { + // launch show effect + // NOTE: use a singleShot timer to avoid resizing issues when showing the message widget the first time (bug #316666) + if (m_fadeEffect) { + QTimer::singleShot(0, m_fadeEffect, SLOT(fadeIn())); + } else { + QTimer::singleShot(0, m_widget, SLOT(animatedShow())); + } + + // start timer in order to track when showing is done (this effectively works + // around the fact, that KMessageWidget does not have a hidden signal) + m_showTimer->start(); + } +} + +void KateAnimation::hide() +{ + Q_ASSERT(m_widget != 0); + + // stop show timer if needed + if (m_showTimer->isActive()) { + m_showTimer->stop(); + } + + // hide according to effects config + if (!(KGlobalSettings::graphicEffectsLevel() & KGlobalSettings::SimpleAnimationEffects)) { + m_widget->hide(); + emit widgetHidden(); + } else { + // hide depending on effect + if (m_fadeEffect) { + m_fadeEffect->fadeOut(); + } else { + m_widget->animatedHide(); + } + + // start timer in order to track when hiding is done (this effectively works + // around the fact, that KMessageWidget does not have a hidden signal) + m_hideTimer->start(); + } +} + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/view/kateanimation.h b/kate/part/view/kateanimation.h new file mode 100644 index 00000000..e9636a13 --- /dev/null +++ b/kate/part/view/kateanimation.h @@ -0,0 +1,103 @@ +/* This file is part of the KDE and the Kate project + * + * Copyright (C) 2013 Dominik Haumann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef KATE_ANIMATION_H +#define KATE_ANIMATION_H + +#include +#include + +#include + +class KMessageWidget; +class KateFadeEffect; +/** + * This class provides a fade in/out effect for KMessageWidget%s. + * Example: + * \code + * KateAnimation* animation = new KateAnimation(someMessageWidget); + * animation->show(); + * //... + * animation->hide(); + * \endcode + */ +class KateAnimation : public QObject +{ + Q_OBJECT + + public: + /** + * The type of supported animation effects + */ + enum EffectType{ + FadeEffect = 0, ///< fade in/out + GrowEffect ///< grow / shrink + }; + + public: + /** + * Constructor. + */ + KateAnimation(KMessageWidget* widget, EffectType effect); + + /** + * Returns true, if the hide animation is running, otherwise false. + */ + bool hideAnimationActive() const; + + /** + * Returns true, if the how animation is running, otherwise false. + */ + bool showAnimationActive() const; + + public Q_SLOTS: + /** + * Call to hide the widget. + */ + void hide(); + + /** + * Call to show and fade in the widget + */ + void show(); + + Q_SIGNALS: + /** + * This signal is emitted when the hiding animation is finished. + * At this point, the associated widget is hidden. + */ + void widgetHidden(); + + /** + * This signal is emitted when the showing animation is finished. + * At this point, the associated widget is hidden. + */ + void widgetShown(); + + private: + QPointer m_widget; ///< the widget to animate + KateFadeEffect * m_fadeEffect; ///< the fade effect + QTimer * m_hideTimer; ///< timer to track hide animation + QTimer * m_showTimer; ///< timer to track show animation +}; + +#endif + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/view/katefadeeffect.cpp b/kate/part/view/katefadeeffect.cpp new file mode 100644 index 00000000..c1f7cf9b --- /dev/null +++ b/kate/part/view/katefadeeffect.cpp @@ -0,0 +1,94 @@ +/* This file is part of the KDE and the Kate project + * + * Copyright (C) 2013 Dominik Haumann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "katefadeeffect.h" +#include "moc_katefadeeffect.cpp" + +#include +#include +#include +#include + +KateFadeEffect::KateFadeEffect(QWidget* widget) + : QObject(widget) + , m_widget(widget) + , m_effect(0) // effect only exists during fading animation +{ + m_timeLine = new QTimeLine(500, this); + m_timeLine->setUpdateInterval(40); + + connect(m_timeLine, SIGNAL(valueChanged(qreal)), this, SLOT(opacityChanged(qreal))); + connect(m_timeLine, SIGNAL(finished()), this, SLOT(animationFinished())); +} + +void KateFadeEffect::fadeIn() +{ + // stop time line if still running + if (m_timeLine->state() == QTimeLine::Running) { + m_timeLine->stop(); + } + + // assign new graphics effect, old one is deleted in setGraphicsEffect() + m_effect = new QGraphicsOpacityEffect(this); + m_effect->setOpacity(0.0); + m_widget->setGraphicsEffect(m_effect); + + // show widget and start fade in animation + m_widget->show(); + m_timeLine->setDirection(QTimeLine::Forward); + m_timeLine->start(); +} + +void KateFadeEffect::fadeOut() +{ + // stop time line if still running + if (m_timeLine->state() == QTimeLine::Running) { + m_timeLine->stop(); + } + + // assign new graphics effect, old one is deleted in setGraphicsEffect() + m_effect = new QGraphicsOpacityEffect(this); + m_effect->setOpacity(1.0); + m_widget->setGraphicsEffect(m_effect); + + // start fade out animation + m_timeLine->setDirection(QTimeLine::Backward); + m_timeLine->start(); +} + +void KateFadeEffect::opacityChanged(qreal value) +{ + Q_ASSERT(m_effect); + m_effect->setOpacity(value); +} + +void KateFadeEffect::animationFinished() +{ + // fading finished: remove graphics effect, deletes the effect as well + m_widget->setGraphicsEffect(0); + Q_ASSERT(!m_effect); + + if (m_timeLine->direction() == QTimeLine::Backward) { + m_widget->hide(); + emit widgetHidden(); + } +} + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/view/katefadeeffect.h b/kate/part/view/katefadeeffect.h new file mode 100644 index 00000000..d5dcc7dd --- /dev/null +++ b/kate/part/view/katefadeeffect.h @@ -0,0 +1,86 @@ +/* This file is part of the KDE and the Kate project + * + * Copyright (C) 2013 Dominik Haumann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef KATE_FADE_EFFECT_H +#define KATE_FADE_EFFECT_H + +#include +#include + +#include +#include +#include +/** + * This class provides a fade in/out effect for arbitrary QWidget%s. + * Example: + * \code + * KateFadeEffect* fadeEffect = new KateFadeEffect(someWidget); + * fadeEffect->fadeIn(); + * //... + * fadeEffect->fadeOut(); + * \endcode + */ +class KateFadeEffect : public QObject +{ + Q_OBJECT + + public: + /** + * Constructor. + * By default, the widget is fully opaque (opacity = 1.0). + */ + KateFadeEffect(QWidget* widget = 0); + + public Q_SLOTS: + /** + * Call to fade out and hide the widget. + */ + void fadeOut(); + /** + * Call to show and fade in the widget + */ + void fadeIn(); + + Q_SIGNALS: + /** + * This signal is emitted when the hiding animation is finished. + * At this point, the associated widget is hidden. + */ + void widgetHidden(); + + protected Q_SLOTS: + /** + * Helper to update opacity value + */ + void opacityChanged(qreal value); + /** + * When the animation is finished, hide the widget if fading out. + */ + void animationFinished(); + + private: + QPointer m_widget; ///< the fading widget + QTimeLine* m_timeLine; ///< update time line + QPointer m_effect; ///< graphics opacity effect +}; + +#endif + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/view/katemessagewidget.cpp b/kate/part/view/katemessagewidget.cpp new file mode 100644 index 00000000..80fdfd74 --- /dev/null +++ b/kate/part/view/katemessagewidget.cpp @@ -0,0 +1,292 @@ +/* This file is part of the KDE and the Kate project + * + * Copyright (C) 2012 Dominik Haumann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "katemessagewidget.h" +#include "moc_katemessagewidget.cpp" + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +static const int s_defaultAutoHideTime = 6 * 1000; + +KateMessageWidget::KateMessageWidget(QWidget* parent, bool applyFadeEffect) + : QWidget(parent) + , m_animation(0) + , m_autoHideTimer(new QTimer(this)) + , m_autoHideTime(-1) +{ + QVBoxLayout* l = new QVBoxLayout(); + l->setMargin(0); + + m_messageWidget = new KMessageWidget(this); + m_messageWidget->setCloseButtonVisible(false); + + l->addWidget(m_messageWidget); + setLayout(l); + + // tell the widget to always use the minimum size. + setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum); + + // install event filter so we catch the end of the hide animation + m_messageWidget->installEventFilter(this); + + // by default, hide widgets + m_messageWidget->hide(); + hide(); + + // create animation controller, and connect widgetHidden() to showNextMessage() + m_animation = new KateAnimation(m_messageWidget, applyFadeEffect ? KateAnimation::FadeEffect : KateAnimation::GrowEffect); + connect(m_animation, SIGNAL(widgetHidden()), this, SLOT(showNextMessage())); + + // setup autoHide timer details + m_autoHideTimer->setSingleShot(true); + + // KMessageWidget::linkHovered() is new in KDE 4.11 + connect(m_messageWidget, SIGNAL(linkHovered(const QString&)), this, SLOT(linkHovered(const QString&))); +} + +void KateMessageWidget::showNextMessage() +{ + // at this point, we should not have a currently shown message + Q_ASSERT(m_currentMessage == 0); + + // if not message to show, just stop + if (m_messageQueue.size() == 0) { + hide(); + return; + } + + // track current message + m_currentMessage = m_messageQueue[0]; + + // set text etc. + m_messageWidget->setText(m_currentMessage->text()); + // KMessageWidget::setIcon() requires KDE >= 4.11 + m_messageWidget->setIcon(m_currentMessage->icon()); + + // connect textChanged() and iconChanged(), so it's possible to change this on the fly + connect(m_currentMessage, SIGNAL(textChanged(const QString&)), + m_messageWidget, SLOT(setText(const QString&)), Qt::UniqueConnection); + connect(m_currentMessage, SIGNAL(iconChanged(const QIcon&)), + m_messageWidget, SLOT(setIcon(const QIcon&)), Qt::UniqueConnection); + + // the enums values do not necessarily match, hence translate with switch + switch (m_currentMessage->messageType()) { + case KTextEditor::Message::Positive: + m_messageWidget->setMessageType(KMessageWidget::Positive); + break; + case KTextEditor::Message::Information: + m_messageWidget->setMessageType(KMessageWidget::Information); + break; + case KTextEditor::Message::Warning: + m_messageWidget->setMessageType(KMessageWidget::Warning); + break; + case KTextEditor::Message::Error: + m_messageWidget->setMessageType(KMessageWidget::Error); + break; + default: + m_messageWidget->setMessageType(KMessageWidget::Information); + break; + } + + // remove all actions from the message widget + foreach (QAction* a, m_messageWidget->actions()) + m_messageWidget->removeAction(a); + + // add new actions to the message widget + foreach (QAction* a, m_currentMessage->actions()) + m_messageWidget->addAction(a); + + // set word wrap of the message + setWordWrap(m_currentMessage); + + // setup auto-hide timer, and start if requested + m_autoHideTime = m_currentMessage->autoHide(); + m_autoHideTimer->stop(); + if (m_autoHideTime >= 0) { + connect(m_autoHideTimer, SIGNAL(timeout()), m_currentMessage, SLOT(deleteLater()), Qt::UniqueConnection); + if (m_currentMessage->autoHideMode() == KTextEditor::Message::Immediate) { + m_autoHideTimer->start(m_autoHideTime == 0 ? s_defaultAutoHideTime : m_autoHideTime); + } + } + + // finally show + show(); + m_animation->show(); +} + +void KateMessageWidget::setWordWrap(KTextEditor::Message* message) +{ + // want word wrap anyway? -> ok + if (message->wordWrap()) { + m_messageWidget->setWordWrap(message->wordWrap()); + return; + } + + // word wrap not wanted, that's ok if a parent widget does not exist + if (!parentWidget()) { + m_messageWidget->setWordWrap(false); + return; + } + + // word wrap not wanted -> enable word wrap if it breaks the layout otherwise + int margin = 0; + if (parentWidget()->layout()) { + // get left/right margin of the layout, since we need to subtract these + int leftMargin = 0, rightMargin = 0; + parentWidget()->layout()->getContentsMargins(&leftMargin, 0, &rightMargin, 0); + margin = leftMargin + rightMargin; + } + + // if word wrap enabled, first disable it + if (m_messageWidget->wordWrap()) + m_messageWidget->setWordWrap(false); + + // make sure the widget's size is up-to-date in its hidden state + m_messageWidget->ensurePolished(); + m_messageWidget->adjustSize(); + + // finally enable word wrap, if there is not enough free horizontal space + const int freeSpace = (parentWidget()->width() - margin) - m_messageWidget->width(); + if (freeSpace < 0) { +// kDebug() << "force word wrap to avoid breaking the layout" << freeSpace; + m_messageWidget->setWordWrap(true); + } +} + +void KateMessageWidget::postMessage(KTextEditor::Message* message, + QList > actions) +{ + Q_ASSERT(!m_messageHash.contains(message)); + m_messageHash[message] = actions; + + // insert message sorted after priority + int i = 0; + for (; i < m_messageQueue.count(); ++i) { + if (message->priority() > m_messageQueue[i]->priority()) + break; + } + + // queue message + m_messageQueue.insert(i, message); + + // catch if the message gets deleted + connect(message, SIGNAL(closed(KTextEditor::Message*)), SLOT(messageDestroyed(KTextEditor::Message*))); + + if (i == 0 && !m_animation->hideAnimationActive()) { + // if message has higher priority than the one currently shown, + // then hide the current one and then show the new one. + if (m_currentMessage) { + + // autoHide timer may be running for currently shown message, therefore + // simply disconnect autoHide timer to all timeout() receivers + disconnect(m_autoHideTimer, SIGNAL(timeout()), 0, 0); + m_autoHideTimer->stop(); + + // if there is a current message, the message queue must contain 2 messages + Q_ASSERT(m_messageQueue.size() > 1); + Q_ASSERT(m_currentMessage == m_messageQueue[1]); + + // a bit unnice: disconnect textChanged() and iconChanged() signals of previously visible message + disconnect(m_currentMessage, SIGNAL(textChanged(const QString&)), + m_messageWidget, SLOT(setText(const QString&))); + disconnect(m_currentMessage, SIGNAL(iconChanged(const QIcon&)), + m_messageWidget, SLOT(setIcon(const QIcon&))); + + m_currentMessage = 0; + m_animation->hide(); + } else { + showNextMessage(); + } + } +} + +void KateMessageWidget::messageDestroyed(KTextEditor::Message* message) +{ + // last moment when message is valid, since KTE::Message is already in + // destructor we have to do the following: + // 1. remove message from m_messageQueue, so we don't care about it anymore + // 2. activate hide animation or show a new message() + + // remove widget from m_messageQueue + int i = 0; + for (; i < m_messageQueue.count(); ++i) { + if (m_messageQueue[i] == message) { + break; + } + } + + // the message must be in the list + Q_ASSERT(i < m_messageQueue.count()); + + // remove message + m_messageQueue.removeAt(i); + + // remove message from hash -> release QActions + Q_ASSERT(m_messageHash.contains(message)); + m_messageHash.remove(message); + + // if deleted message is the current message, launch hide animation + if (message == m_currentMessage) { + m_currentMessage = 0; + m_animation->hide(); + } +} + +void KateMessageWidget::startAutoHideTimer() +{ + // message does not want autohide, or timer already running + if ( !m_currentMessage // no message, nothing to do + || m_autoHideTime < 0 // message does not want auto-hide + || m_autoHideTimer->isActive() // auto-hide timer is already active + || m_animation->hideAnimationActive() // widget is in hide animation phase + || m_animation->showAnimationActive() // widget is in show animation phase + ) { + return; + } + + // safety checks: the message must still be valid + Q_ASSERT(m_messageQueue.size()); + Q_ASSERT(m_currentMessage->autoHide() == m_autoHideTime); + + // start autoHide timer as requrested + m_autoHideTimer->start(m_autoHideTime == 0 ? s_defaultAutoHideTime : m_autoHideTime); +} + +void KateMessageWidget::linkHovered(const QString& link) +{ + QToolTip::showText(QCursor::pos(), link, m_messageWidget); +} + +QString KateMessageWidget::text() const +{ + return m_messageWidget->text(); +} +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/view/katemessagewidget.h b/kate/part/view/katemessagewidget.h new file mode 100644 index 00000000..00368f21 --- /dev/null +++ b/kate/part/view/katemessagewidget.h @@ -0,0 +1,106 @@ +/* This file is part of the KDE and the Kate project + * + * Copyright (C) 2012 Dominik Haumann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef KATE_MESSAGE_WIDGET_H +#define KATE_MESSAGE_WIDGET_H + +#include +#include +#include +#include + +#include "katepartinterfaces_export.h" + +namespace KTextEditor +{ + class Message; +} + +class KMessageWidget; +class KateAnimation; + +/** + * This class implements a message widget based on KMessageWidget. + * It is used to show messages through the KTextEditior::MessageInterface. + */ +class KATEPARTINTERFACES_EXPORT KateMessageWidget : public QWidget +{ + Q_OBJECT + + public: + /** + * Constructor. By default, the widget is hidden. + */ + KateMessageWidget(QWidget* parent, bool applyFadeEffect = false); + + /** + * Post a new incoming message. Show either directly, or queue + */ + void postMessage(KTextEditor::Message* message, QList > actions); + + // for unit test + QString text() const; + + protected Q_SLOTS: + /** + * Show the next message in the queue. + */ + void showNextMessage(); + + /** + * Helper that enables word wrap to avoid breaking the layout + */ + void setWordWrap(KTextEditor::Message* message); + + /** + * catch when a message is deleted, then show next one, if applicable. + */ + void messageDestroyed(KTextEditor::Message* message); + /** + * Start autoHide timer if requested + */ + void startAutoHideTimer(); + /** + * User hovers on a link in the message widget. + */ + void linkHovered(const QString& link); + + private: + // sorted list of pending messages + QList m_messageQueue; + // pointer to current Message + QPointer m_currentMessage; + // shared pointers to QActions as guard + QHash > > m_messageHash; + // the message widget, showing the actual contents + KMessageWidget* m_messageWidget; + // the show / hide effect controller + KateAnimation* m_animation; + + private: // some state variables + // autoHide only once user interaction took place + QTimer *m_autoHideTimer; + // flag: save message's autohide time + int m_autoHideTime; +}; + +#endif + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/view/katetextanimation.cpp b/kate/part/view/katetextanimation.cpp new file mode 100644 index 00000000..503452a4 --- /dev/null +++ b/kate/part/view/katetextanimation.cpp @@ -0,0 +1,122 @@ +/* This file is part of the Kate project. + * + * Copyright (C) 2013 Dominik Haumann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +#include "katetextanimation.h" + +#include "kateviewinternal.h" +#include "katerenderer.h" + +#include + +#include +#include +#include +#include +#include + +#include + +KateTextAnimation::KateTextAnimation(const KTextEditor::Range & range, KTextEditor::Attribute::Ptr attribute, KateViewInternal * view) + : QObject(view) + , m_range(range) + , m_attribute(attribute) + , m_doc(view->view()->doc()) + , m_view(view) + , m_timeLine(new QTimeLine(250, this)) + , m_value(0.0) +{ + m_text = view->view()->doc()->text(range); + + connect(m_timeLine, SIGNAL(valueChanged(qreal)), this, SLOT(nextFrame(qreal))); + connect(m_timeLine, SIGNAL(finished()), this, SLOT(deleteLater())); + + m_timeLine->setCurveShape(QTimeLine::SineCurve); + m_timeLine->start(); +} + +KateTextAnimation::~KateTextAnimation() +{ + // if still running, we need to update the view a last time to avoid artifacts + if (m_timeLine->state() == QTimeLine::Running) { + m_timeLine->stop(); + nextFrame(0.0); + } +} + +QRectF KateTextAnimation::rectForText() +{ + const QFontMetricsF fm = m_view->view()->renderer()->currentFontMetrics(); + const int lineHeight = m_view->view()->renderer()->lineHeight(); + QPoint pixelPos = m_view->cursorToCoordinate(m_range.start(), /*bool realCursor*/ true, /*bool includeBorder*/ false); + + if (pixelPos.x() == -1 || pixelPos.y() == -1) { + return QRectF(); + } else { + QRectF rect(pixelPos.x(), pixelPos.y(), + fm.width(m_view->view()->doc()->text(m_range)), lineHeight); + const QPointF center = rect.center(); + const qreal factor = 1.0 + 0.5 * m_value; + rect.setWidth(rect.width() * factor); + rect.setHeight(rect.height() * factor); + rect.moveCenter(center); + return rect; + } +} + +void KateTextAnimation::draw(QPainter & painter) +{ + // could happen in corner cases: timeLine emitted finished(), but this object + // is not yet deleted. Therefore, draw() might be called in paintEvent(). + if (m_timeLine->state() == QTimeLine::NotRunning) { + return; + } + + // get current rect and fill background + QRectF rect = rectForText(); + painter.fillRect(rect, m_attribute->background()); + + // scale font with animation + QFont f = m_view->view()->renderer()->currentFont(); + f.setBold(m_attribute->fontBold()); + f.setPointSizeF(f.pointSizeF() * (1.0 + 0.5 * m_value)); + painter.setFont(f); + + painter.setPen(m_attribute->foreground().color()); + // finally draw contents on the view + painter.drawText(rect, m_text); +} + +void KateTextAnimation::nextFrame(qreal value) +{ + // cache previous rect for update + const QRectF prevRect = rectForText(); + + m_value = value; + + // next rect is used to draw the text + const QRectF nextRect = rectForText(); + + // due to rounding errors, increase the rect 1px to avoid artifacts + const QRect updateRect = nextRect.united(prevRect).adjusted(-1, -1, 1, 1).toRect(); + + // request repaint + m_view->update(updateRect); +} + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/view/katetextanimation.h b/kate/part/view/katetextanimation.h new file mode 100644 index 00000000..0851a421 --- /dev/null +++ b/kate/part/view/katetextanimation.h @@ -0,0 +1,72 @@ +/* This file is part of the Kate project. + * + * Copyright (C) 2013 Dominik Haumann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +#ifndef KATE_TEXT_ANIMATION_H +#define KATE_TEXT_ANIMATION_H + +#include +#include +#include + +#include +#include +#include + +class KateDocument; +class KateViewInternal; +#include +#include + +/** + * This class is used to flash text in the text view. + * The duration of the flash animation is about 250 milliseconds. + * When the animation is finished, it deletes itself. + */ +class KateTextAnimation : public QObject +{ + Q_OBJECT +public: + KateTextAnimation(const KTextEditor::Range & range, KTextEditor::Attribute::Ptr attribute, KateViewInternal * view); + virtual ~KateTextAnimation(); + + // draw the text to highlight, given the current animation progress + void draw(QPainter & painter); + +public Q_SLOTS: + // request repaint from view of the respective region + void nextFrame(qreal value); + +private: + // calculate rect for the text to highlight, given the current animation progress + QRectF rectForText(); + +private: + KTextEditor::Range m_range; + QString m_text; + KTextEditor::Attribute::Ptr m_attribute; + + KateDocument * m_doc; + KateViewInternal * m_view; + QTimeLine * m_timeLine; + qreal m_value; +}; + +#endif // KATE_TEXT_ANIMATION_H + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/view/kateview.cpp b/kate/part/view/kateview.cpp new file mode 100644 index 00000000..3adf8825 --- /dev/null +++ b/kate/part/view/kateview.cpp @@ -0,0 +1,3111 @@ +/* This file is part of the KDE libraries + Copyright (C) 2009 Michel Ludwig + Copyright (C) 2007 Mirko Stocker + Copyright (C) 2003 Hamish Rodda + Copyright (C) 2002 John Firebaugh + Copyright (C) 2001-2004 Christoph Cullmann + Copyright (C) 2001-2010 Joseph Wenninger + Copyright (C) 1999 Jochen Wilhelmy + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +//BEGIN includes +#include "kateview.h" +#include "moc_kateview.cpp" + +#include "kateviewinternal.h" +#include "kateviewhelpers.h" +#include "katerenderer.h" +#include "katedocument.h" +#include "kateundomanager.h" +#include "katedocumenthelpers.h" +#include "kateglobal.h" +#include "katehighlight.h" +#include "katehighlightmenu.h" +#include "katedialogs.h" +#include "katetextline.h" +#include "kateschema.h" +#include "katebookmarks.h" +#include "kateconfig.h" +#include "katemodemenu.h" +#include "kateautoindent.h" +#include "katecompletionwidget.h" +#include "katesearchbar.h" +#include "katepartpluginmanager.h" +#include "katewordcompletion.h" +#include "katekeywordcompletion.h" +#include "katelayoutcache.h" +#include "spellcheck/spellcheck.h" +#include "spellcheck/spellcheckdialog.h" +#include "spellcheck/spellingmenu.h" +#include "katebuffer.h" +#include "katemessagewidget.h" + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +//#define VIEW_RANGE_DEBUG + +//END includes + +namespace { + +bool hasCommentInFirstLine(KateDocument* doc) +{ + const Kate::TextLine& line = doc->kateTextLine(0); + Q_ASSERT(line); + return doc->isComment(0, line->firstChar()); +} + +} + +void KateView::blockFix(KTextEditor::Range& range) +{ + if (range.start().column() > range.end().column()) + { + int tmp = range.start().column(); + range.start().setColumn(range.end().column()); + range.end().setColumn(tmp); + } +} + +KateView::KateView( KateDocument *doc, QWidget *parent ) + : KTextEditor::View( parent ) + , m_completionWidget(0) + , m_annotationModel(0) + , m_hasWrap( false ) + , m_doc( doc ) + , m_textFolding (doc->buffer()) + , m_config( new KateViewConfig( this ) ) + , m_renderer( new KateRenderer( doc, m_textFolding, this ) ) + , m_viewInternal( new KateViewInternal( this ) ) + , m_spell( new KateSpellCheckDialog( this ) ) + , m_bookmarks( new KateBookmarks( this ) ) + , m_startingUp (true) + , m_updatingDocumentConfig (false) + , m_selection (m_doc->buffer(), KTextEditor::Range::invalid(), Kate::TextRange::ExpandLeft, Kate::TextRange::AllowEmpty) + , blockSelect (false) + , m_bottomViewBar (0) + , m_topViewBar (0) + , m_cmdLine (0) + , m_searchBar (0) + , m_gotoBar (0) + , m_dictionaryBar(NULL) + , m_spellingMenu( new KateSpellingMenu( this ) ) + , m_userContextMenuSet( false ) + , m_delayedUpdateTriggered (false) + , m_lineToUpdateMin (-1) + , m_lineToUpdateMax (-1) + , m_floatTopMessageWidget (0) + , m_floatBottomMessageWidget (0) +{ + // queued connect to collapse view updates for range changes, INIT THIS EARLY ENOUGH! + connect(this, SIGNAL(delayedUpdateOfView()), this, SLOT(slotDelayedUpdateOfView()), Qt::QueuedConnection); + + setComponentData ( KateGlobal::self()->componentData () ); + + // selection if for this view only and will invalidate if becoming empty + m_selection.setView (this); + + // use z depth defined in moving ranges interface + m_selection.setZDepth (-100000.0); + + KateGlobal::self()->registerView( this ); + + KTextEditor::ViewBarContainer *viewBarContainer=qobject_cast( KateGlobal::self()->container() ); + QWidget *bottomBarParent=viewBarContainer?viewBarContainer->getViewBarParent(this,KTextEditor::ViewBarContainer::BottomBar):0; + QWidget *topBarParent=viewBarContainer?viewBarContainer->getViewBarParent(this,KTextEditor::ViewBarContainer::TopBar):0; + + m_bottomViewBar=new KateViewBar (bottomBarParent!=0,KTextEditor::ViewBarContainer::BottomBar,bottomBarParent?bottomBarParent:this,this); + m_topViewBar=new KateViewBar (topBarParent!=0,KTextEditor::ViewBarContainer::TopBar,topBarParent?topBarParent:this,this); + + // ugly workaround: + // Force the layout to be left-to-right even on RTL deskstop, as discussed + // on the mailing list. This will cause the lines and icons panel to be on + // the left, even for Arabic/Hebrew/Farsi/whatever users. + setLayoutDirection ( Qt::LeftToRight ); + + // layouting ;) + m_vBox = new QVBoxLayout (this); + m_vBox->setMargin (0); + m_vBox->setSpacing (0); + + // add top viewbar... + if (topBarParent) + viewBarContainer->addViewBarToLayout(this,m_topViewBar,KTextEditor::ViewBarContainer::TopBar); + else + m_vBox->addWidget(m_topViewBar); + + m_bottomViewBar->installEventFilter(m_viewInternal); + + // add KateMessageWidget for KTE::MessageInterface immediately above view + m_topMessageWidget = new KateMessageWidget(this); + m_vBox->addWidget(m_topMessageWidget); + m_topMessageWidget->hide(); + + // add hbox: KateIconBorder | KateViewInternal | KateScrollBar + QHBoxLayout *hbox = new QHBoxLayout (); + m_vBox->addLayout (hbox, 100); + hbox->setMargin (0); + hbox->setSpacing (0); + + QStyleOption option; + option.initFrom(this); + + if (style()->styleHint(QStyle::SH_ScrollView_FrameOnlyAroundContents, &option, this)) { + QHBoxLayout *extrahbox = new QHBoxLayout (); + QFrame * frame = new QFrame(this); + extrahbox->setMargin (0); + extrahbox->setSpacing (0); + extrahbox->addWidget (m_viewInternal->m_leftBorder); + extrahbox->addWidget (m_viewInternal); + frame->setLayout (extrahbox); + hbox->addWidget (frame); + hbox->addSpacing (style()->pixelMetric(QStyle::PM_ScrollView_ScrollBarSpacing, &option, this)); + frame->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken); + } + else { + hbox->addWidget (m_viewInternal->m_leftBorder); + hbox->addWidget (m_viewInternal); + } + hbox->addWidget (m_viewInternal->m_lineScroll); + + if (style()->styleHint(QStyle::SH_ScrollView_FrameOnlyAroundContents, &option, this)) { + m_vBox->addSpacing (style()->pixelMetric(QStyle::PM_ScrollView_ScrollBarSpacing, &option, this)); + } + + // add hbox: ColumnsScrollBar | Dummy + hbox = new QHBoxLayout (); + m_vBox->addLayout (hbox); + hbox->setMargin (0); + hbox->setSpacing (0); + + hbox->addWidget (m_viewInternal->m_columnScroll); + hbox->addWidget (m_viewInternal->m_dummy); + + // add KateMessageWidget for KTE::MessageInterface immediately above view + m_bottomMessageWidget = new KateMessageWidget(this); + m_vBox->addWidget(m_bottomMessageWidget); + m_bottomMessageWidget->hide(); + + // add bottom viewbar... + if (bottomBarParent) + viewBarContainer->addViewBarToLayout(this,m_bottomViewBar,KTextEditor::ViewBarContainer::BottomBar); + else + m_vBox->addWidget(m_bottomViewBar); + + // add layout for floating message widgets to KateViewInternal + m_notificationLayout = new QVBoxLayout(m_viewInternal); + m_notificationLayout->setContentsMargins(20, 20, 20, 20); + m_viewInternal->setLayout(m_notificationLayout); + + // this really is needed :) + m_viewInternal->updateView (); + + doc->addView( this ); + + setFocusProxy( m_viewInternal ); + setFocusPolicy( Qt::StrongFocus ); + + // default ui file with all features + QString uifile = "katepartui.rc"; + + // simple mode + if (doc->simpleMode ()) + uifile = "katepartsimpleui.rc"; + + setXMLFile( uifile ); + + setupConnections(); + setupActions(); + + // auto word completion + new KateWordCompletionView (this, actionCollection ()); + + // enable the plugins of this view + KatePartPluginManager::self()->addView(this); + + // update the enabled state of the undo/redo actions... + slotUpdateUndo(); + + m_startingUp = false; + updateConfig (); + + slotHlChanged(); + KCursor::setAutoHideCursor( m_viewInternal, true ); + + // user interaction (scrollling) starts notification auto-hide timer + connect(this, SIGNAL(displayRangeChanged(KateView*)), m_topMessageWidget, SLOT(startAutoHideTimer())); + connect(this, SIGNAL(displayRangeChanged(KateView*)), m_bottomMessageWidget, SLOT(startAutoHideTimer())); + + // user interaction (cursor navigation) starts notification auto-hide timer + connect(this, SIGNAL(cursorPositionChanged(KTextEditor::View*, const KTextEditor::Cursor&)), m_topMessageWidget, SLOT(startAutoHideTimer())); + connect(this, SIGNAL(cursorPositionChanged(KTextEditor::View*, const KTextEditor::Cursor&)), m_bottomMessageWidget, SLOT(startAutoHideTimer())); + + // folding restoration on reload + connect(m_doc, SIGNAL(aboutToReload(KTextEditor::Document*)), SLOT(saveFoldingState())); + connect(m_doc, SIGNAL(reloaded(KTextEditor::Document*)), SLOT(applyFoldingState())); +} + +KateView::~KateView() +{ + // invalidate update signal + m_delayedUpdateTriggered = false; + + // remove from xmlgui factory, to be safe + if (factory()) + factory()->removeClient (this); + + KTextEditor::ViewBarContainer *viewBarContainer=qobject_cast( KateGlobal::self()->container() ); + if (viewBarContainer) { + viewBarContainer->deleteViewBarForView(this,KTextEditor::ViewBarContainer::BottomBar); + m_bottomViewBar=0; + viewBarContainer->deleteViewBarForView(this,KTextEditor::ViewBarContainer::TopBar); + m_topViewBar=0; + } + + KatePartPluginManager::self()->removeView(this); + + m_doc->removeView( this ); + + delete m_viewInternal; + + delete m_renderer; + + delete m_config; + + KateGlobal::self()->deregisterView (this); +} + +void KateView::setupConnections() +{ + connect( m_doc, SIGNAL(undoChanged()), + this, SLOT(slotUpdateUndo()) ); + connect( m_doc, SIGNAL(highlightingModeChanged(KTextEditor::Document*)), + this, SLOT(slotHlChanged()) ); + connect( m_doc, SIGNAL(canceled(QString)), + this, SLOT(slotSaveCanceled(QString)) ); + connect( m_viewInternal, SIGNAL(dropEventPass(QDropEvent*)), + this, SIGNAL(dropEventPass(QDropEvent*)) ); + + connect( m_doc, SIGNAL(annotationModelChanged(KTextEditor::AnnotationModel*,KTextEditor::AnnotationModel*)), + m_viewInternal->m_leftBorder, SLOT(annotationModelChanged(KTextEditor::AnnotationModel*,KTextEditor::AnnotationModel*)) ); + + if ( m_doc->browserView() ) + { + connect( this, SIGNAL(dropEventPass(QDropEvent*)), + this, SLOT(slotDropEventPass(QDropEvent*)) ); + } +} + +void KateView::setupActions() +{ + KActionCollection *ac = actionCollection (); + KAction *a; + + m_toggleWriteLock = 0; + + m_cut = a = ac->addAction(KStandardAction::Cut, this, SLOT(cut())); + a->setWhatsThis(i18n("Cut the selected text and move it to the clipboard")); + + m_paste = a = ac->addAction(KStandardAction::PasteText, this, SLOT(paste())); + a->setWhatsThis(i18n("Paste previously copied or cut clipboard contents")); + + m_copy = a = ac->addAction(KStandardAction::Copy, this, SLOT(copy())); + a->setWhatsThis(i18n( "Use this command to copy the currently selected text to the system clipboard.")); + + m_pasteMenu = ac->addAction("edit_paste_menu", new KatePasteMenu (i18n("Clipboard &History"), this)); + connect (KateGlobal::self(), SIGNAL(clipboardHistoryChanged()), this, SLOT(slotClipboardHistoryChanged())); + + if (!m_doc->readOnly()) + { + a = ac->addAction(KStandardAction::Save, m_doc, SLOT(documentSave())); + a->setWhatsThis(i18n("Save the current document")); + + a = m_editUndo = ac->addAction(KStandardAction::Undo, m_doc, SLOT(undo())); + a->setWhatsThis(i18n("Revert the most recent editing actions")); + + a = m_editRedo = ac->addAction(KStandardAction::Redo, m_doc, SLOT(redo())); + a->setWhatsThis(i18n("Revert the most recent undo operation")); + + a = ac->addAction("tools_apply_wordwrap"); + a->setText(i18n("Apply &Word Wrap")); + a->setWhatsThis(i18n("Use this command to wrap all lines of the current document which are longer than the width of the" + " current view, to fit into this view.

This is a static word wrap, meaning it is not updated" + " when the view is resized.")); + connect(a, SIGNAL(triggered(bool)), SLOT(applyWordWrap())); + + a = ac->addAction("tools_cleanIndent"); + a->setText(i18n("&Clean Indentation")); + a->setWhatsThis(i18n("Use this to clean the indentation of a selected block of text (only tabs/only spaces).

" + "You can configure whether tabs should be honored and used or replaced with spaces, in the configuration dialog.")); + connect(a, SIGNAL(triggered(bool)), SLOT(cleanIndent())); + + a = ac->addAction("tools_align"); + a->setText(i18n("&Align")); + a->setWhatsThis(i18n("Use this to align the current line or block of text to its proper indent level.")); + connect(a, SIGNAL(triggered(bool)), SLOT(align())); + + a = ac->addAction("tools_comment"); + a->setText(i18n("C&omment")); + a->setShortcut(QKeySequence(Qt::CTRL+Qt::Key_D)); + a->setWhatsThis(i18n("This command comments out the current line or a selected block of text.

" + "The characters for single/multiple line comments are defined within the language's highlighting.")); + connect(a, SIGNAL(triggered(bool)), SLOT(comment())); + + a = ac->addAction("tools_uncomment"); + a->setText(i18n("Unco&mment")); + a->setShortcut(QKeySequence(Qt::CTRL+Qt::SHIFT+Qt::Key_D)); + a->setWhatsThis(i18n("This command removes comments from the current line or a selected block of text.

" + "The characters for single/multiple line comments are defined within the language's highlighting.")); + connect(a, SIGNAL(triggered(bool)), SLOT(uncomment())); + + a = ac->addAction("tools_toggle_comment"); + a->setText(i18n("Toggle Comment")); + connect(a, SIGNAL(triggered(bool)), SLOT(toggleComment())); + + a = m_toggleWriteLock = new KToggleAction(i18n("&Read Only Mode"), this); + a->setWhatsThis( i18n("Lock/unlock the document for writing") ); + a->setChecked( !m_doc->isReadWrite() ); + connect(a, SIGNAL(triggered(bool)), SLOT(toggleWriteLock())); + ac->addAction("tools_toggle_write_lock", a); + + a = ac->addAction("tools_uppercase"); + a->setText(i18n("Uppercase")); + a->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_U)); + a->setWhatsThis( i18n("Convert the selection to uppercase, or the character to the " + "right of the cursor if no text is selected.") ); + connect(a, SIGNAL(triggered(bool)), SLOT(uppercase())); + + a = ac->addAction( "tools_lowercase" ); + a->setText( i18n("Lowercase") ); + a->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_U)); + a->setWhatsThis( i18n("Convert the selection to lowercase, or the character to the " + "right of the cursor if no text is selected.") ); + connect(a, SIGNAL(triggered(bool)), SLOT(lowercase())); + + a = ac->addAction( "tools_capitalize" ); + a->setText( i18n("Capitalize") ); + a->setShortcut(QKeySequence(Qt::CTRL + Qt::ALT + Qt::Key_U)); + a->setWhatsThis( i18n("Capitalize the selection, or the word under the " + "cursor if no text is selected.") ); + connect(a, SIGNAL(triggered(bool)), SLOT(capitalize())); + + a = ac->addAction( "tools_join_lines" ); + a->setText( i18n("Join Lines") ); + a->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_J)); + connect(a, SIGNAL(triggered(bool)), SLOT(joinLines())); + + a = ac->addAction( "tools_invoke_code_completion" ); + a->setText( i18n("Invoke Code Completion") ); + a->setWhatsThis(i18n("Manually invoke command completion, usually by using a shortcut bound to this action.")); + a->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Space)); + connect(a, SIGNAL(triggered(bool)), SLOT(userInvokedCompletion())); + } + else + { + m_cut->setEnabled (false); + m_paste->setEnabled (false); + m_pasteMenu->setEnabled (false); + m_editUndo = 0; + m_editRedo = 0; + } + + a = ac->addAction( KStandardAction::Print, m_doc, SLOT(print()) ); + a->setWhatsThis(i18n("Print the current document.")); + + a = ac->addAction( "file_reload" ); + a->setIcon(KIcon("view-refresh")); + a->setText(i18n("Reloa&d")); + a->setShortcuts(KStandardShortcut::reload()); + a->setWhatsThis(i18n("Reload the current document from disk.")); + connect(a, SIGNAL(triggered(bool)), SLOT(reloadFile())); + + a = ac->addAction( KStandardAction::SaveAs, m_doc, SLOT(documentSaveAs()) ); + a->setWhatsThis(i18n("Save the current document to disk, with a name of your choice.")); + + a = ac->addAction( KStandardAction::GotoLine, this, SLOT(gotoLine()) ); + a->setWhatsThis(i18n("This command opens a dialog and lets you choose a line that you want the cursor to move to.")); + + a = ac->addAction(QLatin1String("modified_line_up")); + a->setText(i18n("Move to Previous Modified Line")); + a->setWhatsThis(i18n("Move upwards to the previous modified line.")); + connect(a, SIGNAL(triggered(bool)), SLOT(toPrevModifiedLine())); + + a = ac->addAction(QLatin1String("modified_line_down")); + a->setText(i18n("Move to Next Modified Line")); + a->setWhatsThis(i18n("Move downwards to the next modified line.")); + connect(a, SIGNAL(triggered(bool)), SLOT(toNextModifiedLine())); + + a = ac->addAction("set_confdlg"); + a->setText(i18n("&Configure Editor...")); + a->setWhatsThis(i18n("Configure various aspects of this editor.")); + connect(a, SIGNAL(triggered(bool)), SLOT(slotConfigDialog())); + + KateModeMenu *ftm = new KateModeMenu (i18n("&Mode"), this); + ac->addAction("tools_mode", ftm); + ftm->setWhatsThis(i18n("Here you can choose which mode should be used for the current document. This will influence the highlighting and folding being used, for example.")); + ftm->updateMenu (m_doc); + + KateHighlightingMenu *menu = new KateHighlightingMenu (i18n("&Highlighting"), this); + ac->addAction("tools_highlighting", menu); + menu->setWhatsThis(i18n("Here you can choose how the current document should be highlighted.")); + menu->updateMenu (m_doc); + + KateViewSchemaAction *schemaMenu = new KateViewSchemaAction (i18n("&Schema"), this); + ac->addAction("view_schemas", schemaMenu); + schemaMenu->updateMenu (this); + + // indentation menu + KateViewIndentationAction *indentMenu = new KateViewIndentationAction(m_doc, i18n("&Indentation"), this); + ac->addAction("tools_indentation", indentMenu); + + m_selectAll = a= ac->addAction( KStandardAction::SelectAll, this, SLOT(selectAll()) ); + a->setWhatsThis(i18n("Select the entire text of the current document.")); + + m_deSelect = a= ac->addAction( KStandardAction::Deselect, this, SLOT(clearSelection()) ); + a->setWhatsThis(i18n("If you have selected something within the current document, this will no longer be selected.")); + + a = ac->addAction("view_inc_font_sizes"); + a->setIcon(KIcon("zoom-in")); + a->setText(i18n("Enlarge Font")); + a->setShortcut(KStandardShortcut::zoomIn()); + a->setWhatsThis(i18n("This increases the display font size.")); + connect(a, SIGNAL(triggered(bool)), m_viewInternal, SLOT(slotIncFontSizes())); + + a = ac->addAction("view_dec_font_sizes"); + a->setIcon(KIcon("zoom-out")); + a->setText(i18n("Shrink Font")); + a->setShortcut(KStandardShortcut::zoomOut()); + a->setWhatsThis(i18n("This decreases the display font size.")); + connect(a, SIGNAL(triggered(bool)), m_viewInternal, SLOT(slotDecFontSizes())); + + a = m_toggleBlockSelection = new KToggleAction(i18n("Bl&ock Selection Mode"), this); + ac->addAction("set_verticalSelect", a); + a->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_B)); + a->setWhatsThis(i18n("This command allows switching between the normal (line based) selection mode and the block selection mode.")); + connect(a, SIGNAL(triggered(bool)), SLOT(toggleBlockSelection())); + + a = m_toggleInsert = new KToggleAction(i18n("Overwr&ite Mode"), this); + ac->addAction("set_insert", a); + a->setShortcut(QKeySequence(Qt::Key_Insert)); + a->setWhatsThis(i18n("Choose whether you want the text you type to be inserted or to overwrite existing text.")); + connect(a, SIGNAL(triggered(bool)), SLOT(toggleInsert())); + + KToggleAction *toggleAction; + a = m_toggleDynWrap = toggleAction = new KToggleAction(i18n("&Dynamic Word Wrap"), this); + ac->addAction("view_dynamic_word_wrap", a); + a->setShortcut(QKeySequence(Qt::Key_F10)); + a->setWhatsThis(i18n("If this option is checked, the text lines will be wrapped at the view border on the screen.")); + connect(a, SIGNAL(triggered(bool)), SLOT(toggleDynWordWrap())); + + a = m_setDynWrapIndicators = new KSelectAction(i18n("Dynamic Word Wrap Indicators"), this); + ac->addAction("dynamic_word_wrap_indicators", a); + a->setWhatsThis(i18n("Choose when the Dynamic Word Wrap Indicators should be displayed")); + + connect(m_setDynWrapIndicators, SIGNAL(triggered(int)), this, SLOT(setDynWrapIndicators(int))); + QStringList list2; + list2.append(i18n("&Off")); + list2.append(i18n("Follow &Line Numbers")); + list2.append(i18n("&Always On")); + m_setDynWrapIndicators->setItems(list2); + m_setDynWrapIndicators->setEnabled(m_toggleDynWrap->isChecked()); // only synced on real change, later + + a = toggleAction = m_toggleFoldingMarkers = new KToggleAction(i18n("Show Folding &Markers"), this); + ac->addAction("view_folding_markers", a); + a->setShortcut(QKeySequence(Qt::Key_F9)); + a->setWhatsThis(i18n("You can choose if the codefolding marks should be shown, if codefolding is possible.")); + connect(a, SIGNAL(triggered(bool)), SLOT(toggleFoldingMarkers())); + + a = m_toggleIconBar = toggleAction = new KToggleAction(i18n("Show &Icon Border"), this); + ac->addAction("view_border", a); + a->setShortcut(QKeySequence(Qt::Key_F6)); + a->setWhatsThis(i18n("Show/hide the icon border.

The icon border shows bookmark symbols, for instance.")); + connect(a, SIGNAL(triggered(bool)), SLOT(toggleIconBorder())); + + a = toggleAction = m_toggleLineNumbers = new KToggleAction(i18n("Show &Line Numbers"), this); + ac->addAction("view_line_numbers", a); + a->setShortcut(QKeySequence(Qt::Key_F11)); + a->setWhatsThis(i18n("Show/hide the line numbers on the left hand side of the view.")); + connect(a, SIGNAL(triggered(bool)), SLOT(toggleLineNumbersOn())); + + a = m_toggleScrollBarMarks = toggleAction = new KToggleAction(i18n("Show Scroll&bar Marks"), this); + ac->addAction("view_scrollbar_marks", a); + a->setWhatsThis(i18n("Show/hide the marks on the vertical scrollbar.

The marks show bookmarks, for instance.")); + connect(a, SIGNAL(triggered(bool)), SLOT(toggleScrollBarMarks())); + + a = m_toggleScrollBarMiniMap = toggleAction = new KToggleAction(i18n("Show Scrollbar Mini-Map"), this); + ac->addAction("view_scrollbar_minimap", a); + a->setWhatsThis(i18n("Show/hide the mini-map on the vertical scrollbar.

The mini-map shows an overview of the whole document.")); + connect(a, SIGNAL(triggered(bool)), SLOT(toggleScrollBarMiniMap())); + +// a = m_toggleScrollBarMiniMapAll = toggleAction = new KToggleAction(i18n("Show the whole document in the Mini-Map"), this); +// ac->addAction("view_scrollbar_minimap_all", a); +// a->setWhatsThis(i18n("Display the whole document in the mini-map.

With this option set the whole document will be visible in the mini-map.")); +// connect(a, SIGNAL(triggered(bool)), SLOT(toggleScrollBarMiniMapAll())); +// connect(m_toggleScrollBarMiniMap, SIGNAL(triggered(bool)), m_toggleScrollBarMiniMapAll, SLOT(setEnabled(bool))); + + a = toggleAction = m_toggleWWMarker = new KToggleAction(i18n("Show Static &Word Wrap Marker"), this); + ac->addAction("view_word_wrap_marker", a); + a->setWhatsThis( i18n( + "Show/hide the Word Wrap Marker, a vertical line drawn at the word " + "wrap column as defined in the editing properties" )); + connect(a, SIGNAL(triggered(bool)), SLOT(toggleWWMarker())); + + a = m_switchCmdLine = ac->addAction("switch_to_cmd_line"); + a->setText(i18n("Switch to Command Line")); + a->setShortcut(QKeySequence(Qt::Key_F7)); + a->setWhatsThis(i18n("Show/hide the command line on the bottom of the view.")); + connect(a, SIGNAL(triggered(bool)), SLOT(switchToCmdLine())); + + a = m_setEndOfLine = new KSelectAction(i18n("&End of Line"), this); + ac->addAction("set_eol", a); + a->setWhatsThis(i18n("Choose which line endings should be used, when you save the document")); + QStringList list; + list.append(i18nc("@item:inmenu End of Line", "&UNIX")); + list.append(i18nc("@item:inmenu End of Line", "&Windows/DOS")); + list.append(i18nc("@item:inmenu End of Line", "&Macintosh")); + m_setEndOfLine->setItems(list); + m_setEndOfLine->setCurrentItem (m_doc->config()->eol()); + connect(m_setEndOfLine, SIGNAL(triggered(int)), this, SLOT(setEol(int))); + + a=m_addBom=new KToggleAction(i18n("Add &Byte Order Mark (BOM)"),this); + m_addBom->setChecked(m_doc->config()->bom()); + ac->addAction("add_bom",a); + a->setWhatsThis(i18n("Enable/disable adding of byte order markers for UTF-8/UTF-16 encoded files while saving")); + connect(m_addBom,SIGNAL(triggered(bool)),this,SLOT(setAddBom(bool))); + // encoding menu + KateViewEncodingAction *encodingAction = new KateViewEncodingAction(m_doc, this, i18n("E&ncoding"), this); + ac->addAction("set_encoding", encodingAction); + + a = ac->addAction( KStandardAction::Find, this, SLOT(find()) ); + a->setWhatsThis(i18n("Look up the first occurrence of a piece of text or regular expression.")); + addAction(a); + + a = ac->addAction("edit_find_selected"); + a->setText(i18n("Find Selected")); + a->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_H)); + a->setWhatsThis(i18n("Finds next occurrence of selected text.")); + connect(a, SIGNAL(triggered(bool)), SLOT(findSelectedForwards())); + + a = ac->addAction("edit_find_selected_backwards"); + a->setText(i18n("Find Selected Backwards")); + a->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_H)); + a->setWhatsThis(i18n("Finds previous occurrence of selected text.")); + connect(a, SIGNAL(triggered(bool)), SLOT(findSelectedBackwards())); + + a = ac->addAction( KStandardAction::FindNext, this, SLOT(findNext()) ); + a->setWhatsThis(i18n("Look up the next occurrence of the search phrase.")); + addAction(a); + + a = ac->addAction( KStandardAction::FindPrev, "edit_find_prev", this, SLOT(findPrevious()) ); + a->setWhatsThis(i18n("Look up the previous occurrence of the search phrase.")); + addAction(a); + + a = ac->addAction( KStandardAction::Replace, this, SLOT(replace()) ); + a->setWhatsThis(i18n("Look up a piece of text or regular expression and replace the result with some given text.")); + + m_spell->createActions( ac ); + m_toggleOnTheFlySpellCheck = new KToggleAction(i18n("Automatic Spell Checking"), this); + m_toggleOnTheFlySpellCheck->setWhatsThis(i18n("Enable/disable automatic spell checking")); + m_toggleOnTheFlySpellCheck->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_O)); + connect(m_toggleOnTheFlySpellCheck, SIGNAL(triggered(bool)), SLOT(toggleOnTheFlySpellCheck(bool))); + ac->addAction("tools_toggle_automatic_spell_checking", m_toggleOnTheFlySpellCheck); + + a = ac->addAction("tools_change_dictionary"); + a->setText(i18n("Change Dictionary...")); + a->setWhatsThis(i18n("Change the dictionary that is used for spell checking.")); + connect(a, SIGNAL(triggered()), SLOT(changeDictionary())); + + a = ac->addAction("tools_clear_dictionary_ranges"); + a->setText(i18n("Clear Dictionary Ranges")); + a->setVisible(false); + a->setWhatsThis(i18n("Remove all the separate dictionary ranges that were set for spell checking.")); + connect(a, SIGNAL(triggered()), m_doc, SLOT(clearDictionaryRanges())); + connect(m_doc, SIGNAL(dictionaryRangesPresent(bool)), a, SLOT(setVisible(bool))); + + m_spellingMenu->createActions( ac ); + + if (!m_doc->simpleMode ()) + m_bookmarks->createActions( ac ); + + slotSelectionChanged (); + + //Now setup the editing actions before adding the associated + //widget and setting the shortcut context + setupEditActions(); + setupCodeFolding(); + slotClipboardHistoryChanged (); + + ac->addAssociatedWidget(m_viewInternal); + + foreach (QAction* action, ac->actions()) + action->setShortcutContext(Qt::WidgetWithChildrenShortcut); + + connect (this, SIGNAL(selectionChanged(KTextEditor::View*)), this, SLOT(slotSelectionChanged())); +} + +void KateView::slotConfigDialog () +{ + KateGlobal::self ()->configDialog (this); + + // write config to global settings, else simple programs don't get config saved ever + // like Konqueror, Dolphin, ... + KateGlobal::self ()->writeConfig (KGlobal::config().data()); +} + +void KateView::setupEditActions() +{ + //If you add an editing action to this + //function make sure to include the line + //m_editActions << a after creating the action + KActionCollection* ac = actionCollection(); + + KAction* a = ac->addAction("word_left"); + a->setText(i18n("Move Word Left")); + a->setShortcuts(KStandardShortcut::backwardWord()); + connect(a, SIGNAL(triggered(bool)), SLOT(wordLeft())); + m_editActions << a; + + a = ac->addAction("select_char_left"); + a->setText(i18n("Select Character Left")); + a->setShortcut(QKeySequence(Qt::SHIFT + Qt::Key_Left)); + connect(a, SIGNAL(triggered(bool)), SLOT(shiftCursorLeft())); + m_editActions << a; + + a = ac->addAction("select_word_left"); + a->setText(i18n("Select Word Left")); + a->setShortcut(QKeySequence(Qt::SHIFT + Qt::CTRL + Qt::Key_Left)); + connect(a, SIGNAL(triggered(bool)), SLOT(shiftWordLeft())); + m_editActions << a; + + a = ac->addAction("word_right"); + a->setText(i18n("Move Word Right")); + a->setShortcuts(KStandardShortcut::forwardWord()); + connect(a, SIGNAL(triggered(bool)), SLOT(wordRight())); + m_editActions << a; + + a = ac->addAction("select_char_right"); + a->setText(i18n("Select Character Right")); + a->setShortcut(QKeySequence(Qt::SHIFT + Qt::Key_Right)); + connect(a, SIGNAL(triggered(bool)), SLOT(shiftCursorRight())); + m_editActions << a; + + a = ac->addAction("select_word_right"); + a->setText(i18n("Select Word Right")); + a->setShortcut(QKeySequence(Qt::SHIFT + Qt::CTRL + Qt::Key_Right)); + connect(a, SIGNAL(triggered(bool)), SLOT(shiftWordRight())); + m_editActions << a; + + a = ac->addAction("beginning_of_line"); + a->setText(i18n("Move to Beginning of Line")); + a->setShortcuts(KStandardShortcut::beginningOfLine()); + connect(a, SIGNAL(triggered(bool)), SLOT(home())); + m_editActions << a; + + a = ac->addAction("beginning_of_document"); + a->setText(i18n("Move to Beginning of Document")); + a->setShortcuts(KStandardShortcut::begin()); + connect(a, SIGNAL(triggered(bool)), SLOT(top())); + m_editActions << a; + + a = ac->addAction("select_beginning_of_line"); + a->setText(i18n("Select to Beginning of Line")); + a->setShortcut(QKeySequence(Qt::SHIFT + Qt::Key_Home)); + connect(a, SIGNAL(triggered(bool)), SLOT(shiftHome())); + m_editActions << a; + + a = ac->addAction("select_beginning_of_document"); + a->setText(i18n("Select to Beginning of Document")); + a->setShortcut(QKeySequence(Qt::SHIFT + Qt::CTRL + Qt::Key_Home)); + connect(a, SIGNAL(triggered(bool)), SLOT(shiftTop())); + m_editActions << a; + + + a = ac->addAction("end_of_line"); + a->setText(i18n("Move to End of Line")); + a->setShortcuts(KStandardShortcut::endOfLine()); + connect(a, SIGNAL(triggered(bool)), SLOT(end())); + m_editActions << a; + + a = ac->addAction("end_of_document"); + a->setText(i18n("Move to End of Document")); + a->setShortcuts(KStandardShortcut::end()); + connect(a, SIGNAL(triggered(bool)), SLOT(bottom())); + m_editActions << a; + + a = ac->addAction("select_end_of_line"); + a->setText(i18n("Select to End of Line")); + a->setShortcut(QKeySequence(Qt::SHIFT + Qt::Key_End)); + connect(a, SIGNAL(triggered(bool)), SLOT(shiftEnd())); + m_editActions << a; + + a = ac->addAction("select_end_of_document"); + a->setText(i18n("Select to End of Document")); + a->setShortcut(QKeySequence(Qt::SHIFT + Qt::CTRL + Qt::Key_End)); + connect(a, SIGNAL(triggered(bool)), SLOT(shiftBottom())); + m_editActions << a; + + + a = ac->addAction("select_line_up"); + a->setText(i18n("Select to Previous Line")); + a->setShortcut(QKeySequence(Qt::SHIFT + Qt::Key_Up)); + connect(a, SIGNAL(triggered(bool)), SLOT(shiftUp())); + m_editActions << a; + + a = ac->addAction("scroll_line_up"); + a->setText(i18n("Scroll Line Up")); + a->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Up)); + connect(a, SIGNAL(triggered(bool)), SLOT(scrollUp())); + m_editActions << a; + + + a = ac->addAction("move_line_down"); + a->setText(i18n("Move to Next Line")); + a->setShortcut(QKeySequence(Qt::Key_Down)); + connect(a, SIGNAL(triggered(bool)), SLOT(down())); + m_editActions << a; + + + a = ac->addAction("move_line_up"); + a->setText(i18n("Move to Previous Line")); + a->setShortcut(QKeySequence(Qt::Key_Up)); + connect(a, SIGNAL(triggered(bool)), SLOT(up())); + m_editActions << a; + + + a = ac->addAction("move_cursor_right"); + a->setText(i18n("Move Cursor Right")); + a->setShortcut(QKeySequence(Qt::Key_Right)); + connect(a, SIGNAL(triggered(bool)), SLOT(cursorRight())); + m_editActions << a; + + + a = ac->addAction("move_cusor_left"); + a->setText(i18n("Move Cursor Left")); + a->setShortcut(QKeySequence(Qt::Key_Left)); + connect(a, SIGNAL(triggered(bool)), SLOT(cursorLeft())); + m_editActions << a; + + + a = ac->addAction("select_line_down"); + a->setText(i18n("Select to Next Line")); + a->setShortcut(QKeySequence(Qt::SHIFT + Qt::Key_Down)); + connect(a, SIGNAL(triggered(bool)), SLOT(shiftDown())); + m_editActions << a; + + a = ac->addAction("scroll_line_down"); + a->setText(i18n("Scroll Line Down")); + a->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Down)); + connect(a, SIGNAL(triggered(bool)), SLOT(scrollDown())); + m_editActions << a; + + + a = ac->addAction("scroll_page_up"); + a->setText(i18n("Scroll Page Up")); + a->setShortcuts(KStandardShortcut::prior()); + connect(a, SIGNAL(triggered(bool)), SLOT(pageUp())); + m_editActions << a; + + a = ac->addAction("select_page_up"); + a->setText(i18n("Select Page Up")); + a->setShortcut(QKeySequence(Qt::SHIFT + Qt::Key_PageUp)); + connect(a, SIGNAL(triggered(bool)), SLOT(shiftPageUp())); + m_editActions << a; + + a = ac->addAction("move_top_of_view"); + a->setText(i18n("Move to Top of View")); + a->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_PageUp)); + connect(a, SIGNAL(triggered(bool)), SLOT(topOfView())); + m_editActions << a; + + a = ac->addAction("select_top_of_view"); + a->setText(i18n("Select to Top of View")); + a->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_PageUp)); + connect(a, SIGNAL(triggered(bool)), SLOT(shiftTopOfView())); + m_editActions << a; + + + a = ac->addAction("scroll_page_down"); + a->setText(i18n("Scroll Page Down")); + a->setShortcuts(KStandardShortcut::next()); + connect(a, SIGNAL(triggered(bool)), SLOT(pageDown())); + m_editActions << a; + + a = ac->addAction("select_page_down"); + a->setText(i18n("Select Page Down")); + a->setShortcut(QKeySequence(Qt::SHIFT + Qt::Key_PageDown)); + connect(a, SIGNAL(triggered(bool)), SLOT(shiftPageDown())); + m_editActions << a; + + a = ac->addAction("move_bottom_of_view"); + a->setText(i18n("Move to Bottom of View")); + a->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_PageDown)); + connect(a, SIGNAL(triggered(bool)), SLOT(bottomOfView())); + m_editActions << a; + + a = ac->addAction("select_bottom_of_view"); + a->setText(i18n("Select to Bottom of View")); + a->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_PageDown)); + connect(a, SIGNAL(triggered(bool)), SLOT(shiftBottomOfView())); + m_editActions << a; + + a = ac->addAction("to_matching_bracket"); + a->setText(i18n("Move to Matching Bracket")); + a->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_6)); + connect(a, SIGNAL(triggered(bool)), SLOT(toMatchingBracket())); + m_editActions << a; + + a = ac->addAction("select_matching_bracket"); + a->setText(i18n("Select to Matching Bracket")); + a->setShortcut(QKeySequence(Qt::SHIFT + Qt::CTRL + Qt::Key_6)); + connect(a, SIGNAL(triggered(bool)), SLOT(shiftToMatchingBracket())); + m_editActions << a; + + + // anders: shortcuts doing any changes should not be created in browserextension + if ( !m_doc->readOnly() ) + { + a = ac->addAction("transpose_char"); + a->setText(i18n("Transpose Characters")); + a->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_T)); + connect(a, SIGNAL(triggered(bool)), SLOT(transpose())); + m_editActions << a; + + a = ac->addAction("delete_line"); + a->setText(i18n("Delete Line")); + a->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_K)); + connect(a, SIGNAL(triggered(bool)), SLOT(killLine())); + m_editActions << a; + + a = ac->addAction("delete_word_left"); + a->setText(i18n("Delete Word Left")); + a->setShortcuts(KStandardShortcut::deleteWordBack()); + connect(a, SIGNAL(triggered(bool)), SLOT(deleteWordLeft())); + m_editActions << a; + + a = ac->addAction("delete_word_right"); + a->setText(i18n("Delete Word Right")); + a->setShortcuts(KStandardShortcut::deleteWordForward()); + connect(a, SIGNAL(triggered(bool)), SLOT(deleteWordRight())); + m_editActions << a; + + a = ac->addAction("delete_next_character"); + a->setText(i18n("Delete Next Character")); + a->setShortcut(QKeySequence(Qt::Key_Delete)); + connect(a, SIGNAL(triggered(bool)), SLOT(keyDelete())); + m_editActions << a; + + a = ac->addAction("backspace"); + a->setText(i18n("Backspace")); + QList scuts; + scuts << QKeySequence(Qt::Key_Backspace) + << QKeySequence(Qt::SHIFT + Qt::Key_Backspace); + a->setShortcuts(scuts); + connect(a, SIGNAL(triggered(bool)), SLOT(backspace())); + m_editActions << a; + + a = ac->addAction("insert_tabulator"); + a->setText(i18n("Insert Tab")); + connect(a, SIGNAL(triggered(bool)), SLOT(insertTab())); + m_editActions << a; + + a = ac->addAction("smart_newline"); + a->setText(i18n("Insert Smart Newline")); + a->setWhatsThis(i18n("Insert newline including leading characters of the current line which are not letters or numbers.")); + scuts.clear(); + scuts << QKeySequence(Qt::SHIFT + Qt::Key_Return) + << QKeySequence(Qt::SHIFT + Qt::Key_Enter); + a->setShortcuts(scuts); + connect(a, SIGNAL(triggered(bool)), SLOT(smartNewline())); + m_editActions << a; + + a = ac->addAction("tools_indent"); + a->setIcon(KIcon("format-indent-more")); + a->setText(i18n("&Indent")); + a->setWhatsThis(i18n("Use this to indent a selected block of text.

" + "You can configure whether tabs should be honored and used or replaced with spaces, in the configuration dialog.")); + a->setShortcut(QKeySequence(Qt::CTRL+Qt::Key_I)); + connect(a, SIGNAL(triggered(bool)), SLOT(indent())); + + a = ac->addAction("tools_unindent"); + a->setIcon(KIcon("format-indent-less")); + a->setText(i18n("&Unindent")); + a->setWhatsThis(i18n("Use this to unindent a selected block of text.")); + a->setShortcut(QKeySequence(Qt::CTRL+Qt::SHIFT+Qt::Key_I)); + connect(a, SIGNAL(triggered(bool)), SLOT(unIndent())); + } + + if( hasFocus() ) + slotGotFocus(); + else + slotLostFocus(); +} + +void KateView::setupCodeFolding() +{ + //FIXME: FOLDING + KActionCollection *ac=this->actionCollection(); + + KAction* a; + + a = ac->addAction("folding_toplevel"); + a->setText(i18n("Fold Toplevel Nodes")); + a->setShortcut(QKeySequence(Qt::CTRL+Qt::SHIFT+Qt::Key_Minus)); + connect(a, SIGNAL(triggered(bool)), SLOT(slotFoldToplevelNodes())); + + /*a = ac->addAction("folding_expandtoplevel"); + a->setText(i18n("Unfold Toplevel Nodes")); + a->setShortcut(QKeySequence(Qt::CTRL+Qt::SHIFT+Qt::Key_Plus)); + connect(a, SIGNAL(triggered(bool)), m_doc->foldingTree(), SLOT(expandToplevelNodes())); + + a = ac->addAction("folding_expandall"); + a->setText(i18n("Unfold All Nodes")); + connect(a, SIGNAL(triggered(bool)), m_doc->foldingTree(), SLOT(expandAll())); + + a = ac->addAction("folding_collapse_dsComment"); + a->setText(i18n("Fold Multiline Comments")); + connect(a, SIGNAL(triggered(bool)), m_doc->foldingTree(), SLOT(collapseAll_dsComments())); +*/ + a = ac->addAction("folding_collapselocal"); + a->setText(i18n("Fold Current Node")); + connect(a, SIGNAL(triggered(bool)), SLOT(slotCollapseLocal())); + + a = ac->addAction("folding_expandlocal"); + a->setText(i18n("Unfold Current Node")); + connect(a, SIGNAL(triggered(bool)), SLOT(slotExpandLocal())); +} + +void KateView::slotFoldToplevelNodes() +{ + // FIXME: more performant implementation + for (int line = 0; line < doc()->lines(); ++line) { + if (textFolding().isLineVisible(line)) { + foldLine(line); + } + } +} + +void KateView::slotCollapseLocal() +{ + foldLine (cursorPosition().line()); +} + +void KateView::slotExpandLocal() +{ + unfoldLine (cursorPosition().line()); +} + +void KateView::slotCollapseLevel() +{ + //FIXME: FOLDING +#if 0 + if (!sender()) return; + QAction *action = qobject_cast(sender()); + if (!action) return; + + const int level = action->data().toInt(); + Q_ASSERT(level > 0); + m_doc->foldingTree()->collapseLevel(level); +#endif +} + +void KateView::slotExpandLevel() +{ + //FIXME: FOLDING +#if 0 + if (!sender()) return; + QAction *action = qobject_cast(sender()); + if (!action) return; + + const int level = action->data().toInt(); + Q_ASSERT(level > 0); + m_doc->foldingTree()->expandLevel(level); +#endif +} + +void KateView::foldLine (int startLine) +{ + // only for valid lines + if (startLine < 0) + return; + + // try to fold all known ranges + QVector > startingRanges = textFolding().foldingRangesStartingOnLine (startLine); + for (int i = 0; i < startingRanges.size(); ++i) + textFolding().foldRange (startingRanges[i].first); + + // try if the highlighting can help us and create a fold + textFolding().newFoldingRange (doc()->buffer().computeFoldingRangeForStartLine (startLine), Kate::TextFolding::Folded); +} + +void KateView::unfoldLine (int startLine) +{ + // only for valid lines + if (startLine < 0) + return; + + // try to unfold all known ranges + QVector > startingRanges = textFolding().foldingRangesStartingOnLine (startLine); + for (int i = 0; i < startingRanges.size(); ++i) + textFolding().unfoldRange (startingRanges[i].first); +} + +KTextEditor::View::EditMode KateView::viewEditMode() const +{ + return isOverwriteMode() ? EditOverwrite : EditInsert; +} + +QString KateView::viewMode () const +{ + /** + * normal two modes + */ + QString currentMode = isOverwriteMode() ? i18n("OVR") : i18n ("INS"); + + /** + * append read-only if needed + */ + if (!m_doc->isReadWrite()) + currentMode = i18n ("%1 (R/O)", currentMode); + + /** + * return full mode + */ + return currentMode; +} + +void KateView::slotGotFocus() +{ + //kDebug(13020) << "KateView::slotGotFocus"; + + activateEditActions(); + emit focusIn ( this ); +} + +void KateView::slotLostFocus() +{ + //kDebug(13020) << "KateView::slotLostFocus"; + + deactivateEditActions(); + emit focusOut ( this ); +} + +void KateView::setDynWrapIndicators(int mode) +{ + config()->setDynWordWrapIndicators (mode); +} + +bool KateView::isOverwriteMode() const +{ + return m_doc->config()->ovr(); +} + +void KateView::reloadFile() +{ + // bookmarks and cursor positions are temporarily saved by the document + m_doc->documentReload(); +} + +void KateView::slotReadWriteChanged () +{ + if ( m_toggleWriteLock ) + m_toggleWriteLock->setChecked( ! m_doc->isReadWrite() ); + + m_cut->setEnabled (m_doc->isReadWrite() && (selection() || m_config->smartCopyCut())); + m_paste->setEnabled (m_doc->isReadWrite()); + m_pasteMenu->setEnabled (m_doc->isReadWrite() && !KateGlobal::self()->clipboardHistory().isEmpty()); + m_setEndOfLine->setEnabled (m_doc->isReadWrite()); + + QStringList l; + + l << "edit_replace" << "tools_spelling" << "tools_indent" + << "tools_unindent" << "tools_cleanIndent" << "tools_align" << "tools_comment" + << "tools_uncomment" << "tools_toggle_comment" << "tools_uppercase" << "tools_lowercase" + << "tools_capitalize" << "tools_join_lines" << "tools_apply_wordwrap" + << "tools_spelling_from_cursor" + << "tools_spelling_selection"; + + QAction *a = 0; + for (int z = 0; z < l.size(); z++) + if ((a = actionCollection()->action( l[z].toAscii().constData() ))) + a->setEnabled (m_doc->isReadWrite()); + slotUpdateUndo(); + + // inform search bar + if (m_searchBar) + m_searchBar->slotReadWriteChanged (); + + // => view mode changed + emit viewModeChanged(this); + emit viewEditModeChanged(this,viewEditMode()); +} + +void KateView::slotClipboardHistoryChanged () +{ + m_pasteMenu->setEnabled (m_doc->isReadWrite() && !KateGlobal::self()->clipboardHistory().isEmpty()); +} + +void KateView::slotUpdateUndo() +{ + if (m_doc->readOnly()) + return; + + m_editUndo->setEnabled(m_doc->isReadWrite() && m_doc->undoCount() > 0); + m_editRedo->setEnabled(m_doc->isReadWrite() && m_doc->redoCount() > 0); +} + +void KateView::slotDropEventPass( QDropEvent * ev ) +{ + const KUrl::List lstDragURLs=KUrl::List::fromMimeData(ev->mimeData()); + bool ok = !lstDragURLs.isEmpty(); + + KParts::BrowserExtension * ext = KParts::BrowserExtension::childObject( doc() ); + if ( ok && ext ) + emit ext->openUrlRequest( lstDragURLs.first() ); +} + +void KateView::contextMenuEvent( QContextMenuEvent *ev ) +{ + if ( !m_doc || !m_doc->browserExtension() ) + return; + KParts::OpenUrlArguments args; + args.setMimeType( QLatin1String("text/plain") ); + emit m_doc->browserExtension()->popupMenu( ev->globalPos(), m_doc->url(), S_IFREG, args ); + ev->accept(); +} + +bool KateView::setCursorPositionInternal( const KTextEditor::Cursor& position, uint tabwidth, bool calledExternally ) +{ + Kate::TextLine l = m_doc->kateTextLine( position.line() ); + + if (!l) + return false; + + QString line_str = m_doc->line( position.line() ); + + int x = 0; + int z = 0; + for (; z < line_str.length() && z < position.column(); z++) { + if (line_str[z] == QChar('\t')) x += tabwidth - (x % tabwidth); else x++; + } + + if (blockSelection()) + if (z < position.column()) + x += position.column() - z; + + m_viewInternal->updateCursor( KTextEditor::Cursor(position.line(), x), false, true, calledExternally ); + + return true; +} + +void KateView::toggleInsert() +{ + m_doc->config()->setOvr(!m_doc->config()->ovr()); + m_toggleInsert->setChecked (isOverwriteMode ()); + + emit viewModeChanged(this); + emit viewEditModeChanged(this,viewEditMode()); +} + +void KateView::slotSaveCanceled( const QString& error ) +{ + if ( !error.isEmpty() ) // happens when canceling a job + KMessageBox::error( this, error ); +} + +void KateView::gotoLine() +{ + gotoBar()->updateData(); + bottomViewBar()->showBarWidget(gotoBar()); +} + +void KateView::changeDictionary() +{ + dictionaryBar()->updateData(); + bottomViewBar()->showBarWidget(dictionaryBar()); +} + +void KateView::joinLines() +{ + int first = selectionRange().start().line(); + int last = selectionRange().end().line(); + //int left = m_doc->line( last ).length() - m_doc->selEndCol(); + if ( first == last ) + { + first = cursorPosition().line(); + last = first + 1; + } + m_doc->joinLines( first, last ); +} + +void KateView::readSessionConfig(const KConfigGroup& config) +{ + // cursor position + setCursorPositionInternal(KTextEditor::Cursor(config.readEntry("CursorLine",0), config.readEntry("CursorColumn",0))); + + // TODO: text folding state +// m_savedFoldingState = config.readEntry("TextFolding", QVariantList()); +// applyFoldingState (); +} + +void KateView::writeSessionConfig(KConfigGroup& config) +{ + // cursor position + config.writeEntry("CursorLine",m_viewInternal->m_cursor.line()); + config.writeEntry("CursorColumn",m_viewInternal->m_cursor.column()); + + // TODO: text folding state +// saveFoldingState(); +// config.writeEntry("TextFolding", m_savedFoldingState); +// m_savedFoldingState.clear (); +} + +int KateView::getEol() const +{ + return m_doc->config()->eol(); +} + +void KateView::setEol(int eol) +{ + if (!doc()->isReadWrite()) + return; + + if (m_updatingDocumentConfig) + return; + + if (eol != m_doc->config()->eol()) { + m_doc->setModified(true); // mark modified (bug #143120) + m_doc->config()->setEol (eol); + } +} + +void KateView::setAddBom(bool enabled) +{ + if (!doc()->isReadWrite()) + return; + + if (m_updatingDocumentConfig) + return; + + m_doc->config()->setBom (enabled); + m_doc->bomSetByUser(); +} + +void KateView::setIconBorder( bool enable ) +{ + config()->setIconBar (enable); +} + +void KateView::toggleIconBorder() +{ + config()->setIconBar (!config()->iconBar()); +} + +void KateView::setLineNumbersOn( bool enable ) +{ + config()->setLineNumbers (enable); +} + +void KateView::toggleLineNumbersOn() +{ + config()->setLineNumbers (!config()->lineNumbers()); +} + +void KateView::setScrollBarMarks( bool enable ) +{ + config()->setScrollBarMarks (enable); +} + +void KateView::toggleScrollBarMarks() +{ + config()->setScrollBarMarks (!config()->scrollBarMarks()); +} + +void KateView::setScrollBarMiniMap( bool enable ) +{ + config()->setScrollBarMiniMap (enable); +} + +void KateView::toggleScrollBarMiniMap() +{ + config()->setScrollBarMiniMap (!config()->scrollBarMiniMap()); +} + +void KateView::setScrollBarMiniMapAll( bool enable ) +{ + config()->setScrollBarMiniMapAll (enable); +} + +void KateView::toggleScrollBarMiniMapAll() +{ + config()->setScrollBarMiniMapAll (!config()->scrollBarMiniMapAll()); +} + +void KateView::setScrollBarMiniMapWidth( int width ) +{ + config()->setScrollBarMiniMapWidth (width); +} + +void KateView::toggleDynWordWrap() +{ + config()->setDynWordWrap( !config()->dynWordWrap() ); +} + +void KateView::toggleWWMarker() +{ + m_renderer->config()->setWordWrapMarker (!m_renderer->config()->wordWrapMarker()); +} + +void KateView::setFoldingMarkersOn( bool enable ) +{ + config()->setFoldingBar ( enable ); +} + +void KateView::toggleFoldingMarkers() +{ + config()->setFoldingBar ( !config()->foldingBar() ); +} + +bool KateView::iconBorder() { + return m_viewInternal->m_leftBorder->iconBorderOn(); +} + +bool KateView::lineNumbersOn() { + return m_viewInternal->m_leftBorder->lineNumbersOn(); +} + +bool KateView::scrollBarMarks() { + return m_viewInternal->m_lineScroll->showMarks(); +} + +bool KateView::scrollBarMiniMap() { + return m_viewInternal->m_lineScroll->showMiniMap(); +} + +int KateView::dynWrapIndicators() { + return m_viewInternal->m_leftBorder->dynWrapIndicators(); +} + +bool KateView::foldingMarkersOn() { + return m_viewInternal->m_leftBorder->foldingMarkersOn(); +} + +void KateView::toggleWriteLock() +{ + m_doc->setReadWrite( ! m_doc->isReadWrite() ); +} + +void KateView::enableTextHints(int timeout) +{ + m_viewInternal->enableTextHints(timeout); +} + +void KateView::disableTextHints() +{ + m_viewInternal->disableTextHints(); +} + +void KateView::find() +{ + const bool INIT_HINT_AS_INCREMENTAL = false; + KateSearchBar * const bar = searchBar(INIT_HINT_AS_INCREMENTAL); + bar->enterIncrementalMode(); + bottomViewBar()->addBarWidget(bar); + bottomViewBar()->showBarWidget(bar); + bar->setFocus(); +} + +void KateView::findSelectedForwards() +{ + KateSearchBar::nextMatchForSelection(this, KateSearchBar::SearchForward); +} + +void KateView::findSelectedBackwards() +{ + KateSearchBar::nextMatchForSelection(this, KateSearchBar::SearchBackward); +} + +void KateView::replace() +{ + const bool INIT_HINT_AS_POWER = true; + KateSearchBar * const bar = searchBar(INIT_HINT_AS_POWER); + bar->enterPowerMode(); + bottomViewBar()->addBarWidget(bar); + bottomViewBar()->showBarWidget(bar); + bar->setFocus(); +} + +void KateView::findNext() +{ + searchBar()->findNext(); +} + +void KateView::findPrevious() +{ + searchBar()->findPrevious(); +} + +void KateView::slotSelectionChanged () +{ + m_copy->setEnabled (selection() || m_config->smartCopyCut()); + m_deSelect->setEnabled (selection()); + + if (m_doc->readOnly()) + return; + + m_cut->setEnabled (selection() || m_config->smartCopyCut() ); + + m_spell->updateActions (); +} + +void KateView::switchToCmdLine () +{ + // if the user has selected text, insert the selection's range (start line to end line) in the + // command line when opened + if (selectionRange().start().line() != -1 && selectionRange().end().line() != -1) { + cmdLineBar()->setText(QString::number(selectionRange().start().line()+1)+',' + +QString::number(selectionRange().end().line()+1)); + } + bottomViewBar()->showBarWidget(cmdLineBar()); + cmdLineBar()->setFocus (); +} + +KateRenderer *KateView::renderer () +{ + return m_renderer; +} + +void KateView::updateConfig () +{ + if (m_startingUp) + return; + + // dyn. word wrap & markers + if (m_hasWrap != config()->dynWordWrap()) { + m_viewInternal->prepareForDynWrapChange(); + + m_hasWrap = config()->dynWordWrap(); + + m_viewInternal->dynWrapChanged(); + + m_setDynWrapIndicators->setEnabled(config()->dynWordWrap()); + m_toggleDynWrap->setChecked( config()->dynWordWrap() ); + } + + m_viewInternal->m_leftBorder->setDynWrapIndicators( config()->dynWordWrapIndicators() ); + m_setDynWrapIndicators->setCurrentItem( config()->dynWordWrapIndicators() ); + + // line numbers + m_viewInternal->m_leftBorder->setLineNumbersOn( config()->lineNumbers() ); + m_toggleLineNumbers->setChecked( config()->lineNumbers() ); + + // icon bar + m_viewInternal->m_leftBorder->setIconBorderOn( config()->iconBar() ); + m_toggleIconBar->setChecked( config()->iconBar() ); + + // scrollbar marks + m_viewInternal->m_lineScroll->setShowMarks( config()->scrollBarMarks() ); + m_toggleScrollBarMarks->setChecked( config()->scrollBarMarks() ); + + // scrollbar mini-map + m_viewInternal->m_lineScroll->setShowMiniMap( config()->scrollBarMiniMap() ); + m_toggleScrollBarMiniMap->setChecked( config()->scrollBarMiniMap() ); + + // scrollbar mini-map - (whole document) + m_viewInternal->m_lineScroll->setMiniMapAll( config()->scrollBarMiniMapAll() ); + //m_toggleScrollBarMiniMapAll->setChecked( config()->scrollBarMiniMapAll() ); + + // scrollbar mini-map.width + m_viewInternal->m_lineScroll->setMiniMapWidth( config()->scrollBarMiniMapWidth() ); + + // misc edit + m_toggleBlockSelection->setChecked( blockSelection() ); + m_toggleInsert->setChecked( isOverwriteMode() ); + + updateFoldingConfig (); + + // bookmark + m_bookmarks->setSorting( (KateBookmarks::Sorting) config()->bookmarkSort() ); + + m_viewInternal->setAutoCenterLines(config()->autoCenterLines ()); + + // whether relative line numbers should be used or not. + m_viewInternal->m_leftBorder->setViRelLineNumbersOn(config()->viRelativeLineNumbers()); + + reflectOnTheFlySpellCheckStatus(m_doc->isOnTheFlySpellCheckingEnabled()); + + // register/unregister word completion... + unregisterCompletionModel (KateGlobal::self()->wordCompletionModel()); + unregisterCompletionModel (KateGlobal::self()->keywordCompletionModel()); + if (config()->wordCompletion ()) + registerCompletionModel (KateGlobal::self()->wordCompletionModel()); + if (config()->keywordCompletion ()) + registerCompletionModel (KateGlobal::self()->keywordCompletionModel()); + + m_cut->setEnabled(m_doc->isReadWrite() && (selection() || m_config->smartCopyCut())); + m_copy->setEnabled(selection() || m_config->smartCopyCut()); + + // now redraw... + m_viewInternal->cache()->clear(); + tagAll (); + updateView (true); + + if (hasCommentInFirstLine(m_doc)) { + if (config()->foldFirstLine()) { + foldLine(0); + } else { + unfoldLine(0); + } + } + + emit configChanged(); +} + +void KateView::updateDocumentConfig() +{ + if (m_startingUp) + return; + + m_updatingDocumentConfig = true; + + m_setEndOfLine->setCurrentItem (m_doc->config()->eol()); + + m_addBom->setChecked(m_doc->config()->bom()); + + m_updatingDocumentConfig = false; + + // maybe block selection or wrap-cursor mode changed + ensureCursorColumnValid(); + + // first change this + m_renderer->setTabWidth (m_doc->config()->tabWidth()); + m_renderer->setIndentWidth (m_doc->config()->indentationWidth()); + + // now redraw... + m_viewInternal->cache()->clear(); + tagAll (); + updateView (true); +} + +void KateView::updateRendererConfig() +{ + if (m_startingUp) + return; + + m_toggleWWMarker->setChecked( m_renderer->config()->wordWrapMarker() ); + + m_viewInternal->updateBracketMarkAttributes(); + m_viewInternal->updateBracketMarks(); + + if (m_searchBar) { + m_searchBar->updateHighlightColors(); + } + + // now redraw... + m_viewInternal->cache()->clear(); + tagAll (); + m_viewInternal->updateView (true); + + // update the left border right, for example linenumbers + m_viewInternal->m_leftBorder->updateFont(); + m_viewInternal->m_leftBorder->repaint (); + + m_viewInternal->m_lineScroll->queuePixmapUpdate (); + +// @@ showIndentLines is not cached anymore. +// m_renderer->setShowIndentLines (m_renderer->config()->showIndentationLines()); + emit configChanged(); +} + +void KateView::updateFoldingConfig () +{ + // folding bar + m_viewInternal->m_leftBorder->setFoldingMarkersOn(config()->foldingBar()); + m_toggleFoldingMarkers->setChecked( config()->foldingBar() ); + +#if 0 + // FIXME: FOLDING + QStringList l; + + l << "folding_toplevel" << "folding_expandtoplevel" + << "folding_collapselocal" << "folding_expandlocal"; + + QAction *a = 0; + for (int z = 0; z < l.size(); z++) + if ((a = actionCollection()->action( l[z].toAscii().constData() ))) + a->setEnabled (m_doc->highlight() && m_doc->highlight()->allowsFolding()); +#endif +} + +void KateView::ensureCursorColumnValid() +{ + KTextEditor::Cursor c = m_viewInternal->getCursor(); + + // make sure the cursor is valid: + // - in block selection mode or if wrap cursor is off, the column is arbitrary + // - otherwise: it's bounded by the line length + if (!blockSelection() && wrapCursor() + && (!c.isValid() || c.column() > m_doc->lineLength(c.line()))) + { + c.setColumn(m_doc->kateTextLine(cursorPosition().line())->length()); + setCursorPosition(c); + } +} + +//BEGIN EDIT STUFF +void KateView::editStart () +{ + m_viewInternal->editStart (); +} + +void KateView::editEnd (int editTagLineStart, int editTagLineEnd, bool tagFrom) +{ + m_viewInternal->editEnd (editTagLineStart, editTagLineEnd, tagFrom); +} + +void KateView::editSetCursor (const KTextEditor::Cursor &cursor) +{ + m_viewInternal->editSetCursor (cursor); +} +//END + +//BEGIN TAG & CLEAR +bool KateView::tagLine (const KTextEditor::Cursor& virtualCursor) +{ + return m_viewInternal->tagLine (virtualCursor); +} + +bool KateView::tagRange(const KTextEditor::Range& range, bool realLines) +{ + return m_viewInternal->tagRange(range, realLines); +} + +bool KateView::tagLines (int start, int end, bool realLines) +{ + return m_viewInternal->tagLines (start, end, realLines); +} + +bool KateView::tagLines (KTextEditor::Cursor start, KTextEditor::Cursor end, bool realCursors) +{ + return m_viewInternal->tagLines (start, end, realCursors); +} + +void KateView::tagAll () +{ + m_viewInternal->tagAll (); +} + +void KateView::clear () +{ + m_viewInternal->clear (); +} + +void KateView::repaintText (bool paintOnlyDirty) +{ + if (paintOnlyDirty) + m_viewInternal->updateDirty(); + else + m_viewInternal->update(); +} + +void KateView::updateView (bool changed) +{ + //kDebug(13020) << "KateView::updateView"; + + m_viewInternal->updateView (changed); + m_viewInternal->m_leftBorder->update(); +} + +//END + +void KateView::slotHlChanged() +{ + KateHighlighting *hl = m_doc->highlight(); + bool ok ( !hl->getCommentStart(0).isEmpty() || !hl->getCommentSingleLineStart(0).isEmpty() ); + + if (actionCollection()->action("tools_comment")) + actionCollection()->action("tools_comment")->setEnabled( ok ); + + if (actionCollection()->action("tools_uncomment")) + actionCollection()->action("tools_uncomment")->setEnabled( ok ); + + if (actionCollection()->action("tools_toggle_comment")) + actionCollection()->action("tools_toggle_comment")->setEnabled( ok ); + + // show folding bar if "view defaults" says so, otherwise enable/disable only the menu entry + updateFoldingConfig (); +} + +int KateView::virtualCursorColumn() const +{ + return m_doc->toVirtualColumn(m_viewInternal->getCursor()); +} + +void KateView::notifyMousePositionChanged(const KTextEditor::Cursor& newPosition) +{ + emit mousePositionChanged(this, newPosition); +} + +//BEGIN KTextEditor::SelectionInterface stuff + +bool KateView::setSelection( const KTextEditor::Range &selection ) +{ + /** + * anything to do? + */ + if (selection == m_selection) + return true; + + /** + * backup old range + */ + KTextEditor::Range oldSelection = m_selection; + + /** + * set new range + */ + m_selection.setRange (selection.isEmpty() ? KTextEditor::Range::invalid() : selection); + + /** + * trigger update of correct area + */ + tagSelection(oldSelection); + repaintText(true); + + /** + * emit holy signal + */ + emit selectionChanged (this); + + /** + * be done + */ + return true; +} + +bool KateView::clearSelection() +{ + return clearSelection (true); +} + +bool KateView::clearSelection(bool redraw, bool finishedChangingSelection) +{ + /** + * no selection, nothing to do... + */ + if( !selection() ) + return false; + + /** + * backup old range + */ + KTextEditor::Range oldSelection = m_selection; + + /** + * invalidate current selection + */ + m_selection.setRange (KTextEditor::Range::invalid()); + + /** + * trigger update of correct area + */ + tagSelection(oldSelection); + if (redraw) + repaintText(true); + + /** + * emit holy signal + */ + if (finishedChangingSelection) + emit selectionChanged (this); + + /** + * be done + */ + return true; +} + +bool KateView::selection() const +{ + if (!wrapCursor()) + return m_selection != KTextEditor::Range::invalid(); + else + return m_selection.toRange().isValid(); +} + +QString KateView::selectionText() const +{ + return m_doc->text(m_selection, blockSelect); +} + +bool KateView::removeSelectedText() +{ + if (!selection()) + return false; + + m_doc->editStart (); + + // Optimization: clear selection before removing text + KTextEditor::Range selection = m_selection; + + m_doc->removeText(selection, blockSelect); + + // don't redraw the cleared selection - that's done in editEnd(). + if (blockSelect) { + int selectionColumn = qMin(m_doc->toVirtualColumn(selection.start()), m_doc->toVirtualColumn(selection.end())); + KTextEditor::Range newSelection = selection; + newSelection.start().setColumn(m_doc->fromVirtualColumn(newSelection.start().line(), selectionColumn)); + newSelection.end().setColumn(m_doc->fromVirtualColumn(newSelection.end().line(), selectionColumn)); + setSelection(newSelection); + setCursorPositionInternal(newSelection.start()); + } + else + clearSelection(false); + + m_doc->editEnd (); + + return true; +} + +bool KateView::selectAll() +{ + setBlockSelection (false); + top(); + shiftBottom(); + return true; +} + +bool KateView::cursorSelected(const KTextEditor::Cursor& cursor) +{ + KTextEditor::Cursor ret = cursor; + if ( (!blockSelect) && (ret.column() < 0) ) + ret.setColumn(0); + + if (blockSelect) + return cursor.line() >= m_selection.start().line() && ret.line() <= m_selection.end().line() + && ret.column() >= m_selection.start().column() && ret.column() <= m_selection.end().column(); + else + return m_selection.toRange().contains(cursor) || m_selection.end() == cursor; +} + +bool KateView::lineSelected (int line) +{ + return !blockSelect && m_selection.toRange().containsLine(line); +} + +bool KateView::lineEndSelected (const KTextEditor::Cursor& lineEndPos) +{ + return (!blockSelect) + && (lineEndPos.line() > m_selection.start().line() || (lineEndPos.line() == m_selection.start().line() && (m_selection.start().column() < lineEndPos.column() || lineEndPos.column() == -1))) + && (lineEndPos.line() < m_selection.end().line() || (lineEndPos.line() == m_selection.end().line() && (lineEndPos.column() <= m_selection.end().column() && lineEndPos.column() != -1))); +} + +bool KateView::lineHasSelected (int line) +{ + return selection() && m_selection.toRange().containsLine(line); +} + +bool KateView::lineIsSelection (int line) +{ + return (line == m_selection.start().line() && line == m_selection.end().line()); +} + +void KateView::tagSelection(const KTextEditor::Range &oldSelection) +{ + if (selection()) { + if (oldSelection.start().line() == -1) { + // We have to tag the whole lot if + // 1) we have a selection, and: + // a) it's new; or + tagLines(m_selection, true); + + } else if (blockSelection() && (oldSelection.start().column() != m_selection.start().column() || oldSelection.end().column() != m_selection.end().column())) { + // b) we're in block selection mode and the columns have changed + tagLines(m_selection, true); + tagLines(oldSelection, true); + + } else { + if (oldSelection.start() != m_selection.start()) { + if (oldSelection.start() < m_selection.start()) + tagLines(oldSelection.start(), m_selection.start(), true); + else + tagLines(m_selection.start(), oldSelection.start(), true); + } + + if (oldSelection.end() != m_selection.end()) { + if (oldSelection.end() < m_selection.end()) + tagLines(oldSelection.end(), m_selection.end(), true); + else + tagLines(m_selection.end(), oldSelection.end(), true); + } + } + + } else { + // No more selection, clean up + tagLines(oldSelection, true); + } +} + +void KateView::selectWord( const KTextEditor::Cursor& cursor ) +{ + // TODO: KDE5: reuse KateDocument::getWord() or rather KTextEditor::Document::wordRangeAt() + // avoid code duplication of selectWord() here, and KateDocument::getWord() + int start, end, len; + + Kate::TextLine textLine = m_doc->plainKateTextLine(cursor.line()); + + if (!textLine) + return; + + len = textLine->length(); + start = end = cursor.column(); + while (start > 0 && m_doc->highlight()->isInWord(textLine->at(start - 1), textLine->attribute(start - 1))) start--; + while (end < len && m_doc->highlight()->isInWord(textLine->at(end), textLine->attribute(start - 1))) end++; + if (end <= start) return; + + setSelection (KTextEditor::Range(cursor.line(), start, cursor.line(), end)); +} + +void KateView::selectLine( const KTextEditor::Cursor& cursor ) +{ + int line = cursor.line(); + if ( line+1 >= m_doc->lines() ) + setSelection (KTextEditor::Range(line, 0, line, m_doc->lineLength(line))); + else + setSelection (KTextEditor::Range(line, 0, line+1, 0)); +} + +void KateView::cut() +{ + if (!selection() && !m_config->smartCopyCut()) + return; + + copy(); + if (!selection()) + selectLine(m_viewInternal->m_cursor); + removeSelectedText(); +} + +void KateView::copy() const +{ + QString text = selectionText(); + + if (!selection()) { + if (!m_config->smartCopyCut()) + return; + text = m_doc->line(m_viewInternal->m_cursor.line()) + '\n'; + m_viewInternal->moveEdge(KateViewInternal::left, false); + } + + // copy to clipboard and our history! + KateGlobal::self()->copyToClipboard (text); +} + +void KateView::applyWordWrap () +{ + if (selection()) + m_doc->wrapText (selectionRange().start().line(), selectionRange().end().line()); + else + m_doc->wrapText (0, m_doc->lastLine()); +} + +//END + +//BEGIN KTextEditor::BlockSelectionInterface stuff + +bool KateView::blockSelection () const +{ + return blockSelect; +} + +bool KateView::setBlockSelection (bool on) +{ + if (on != blockSelect) + { + blockSelect = on; + + KTextEditor::Range oldSelection = m_selection; + + const bool hadSelection = clearSelection(false, false); + + setSelection(oldSelection); + + m_toggleBlockSelection->setChecked( blockSelection() ); + + // when leaving block selection mode, if cursor is at an invalid position or past the end of the + // line, move the cursor to the last column of the current line unless cursor wrapping is off + ensureCursorColumnValid(); + + if (!hadSelection) { + // emit selectionChanged() according to the KTextEditor::View api + // documentation also if there is no selection around. This is needed, + // as e.g. the Kate App status bar uses this signal to update the state + // of the selection mode (block selection, line based selection) + emit selectionChanged(this); + } + } + + return true; +} + +bool KateView::toggleBlockSelection () +{ + m_toggleBlockSelection->setChecked (!blockSelect); + return setBlockSelection (!blockSelect); +} + +bool KateView::wrapCursor () const +{ + return !blockSelection(); +} + +//END + + +void KateView::slotTextInserted ( KTextEditor::View *view, const KTextEditor::Cursor &position, const QString &text) +{ + emit textInserted ( view, position, text); +} + +bool KateView::insertTemplateTextImplementation ( const KTextEditor::Cursor& c, + const QString &templateString, + const QMap &initialValues) +{ + return insertTemplateTextImplementation(c, templateString, initialValues, 0); +} + +bool KateView::insertTemplateTextImplementation ( const KTextEditor::Cursor& c, + const QString &templateString, + const QMap &initialValues, + KTextEditor::TemplateScript* templateScript) +{ + /** + * no empty templates + */ + if (templateString.isEmpty()) + return false; + + /** + * not for read-only docs + */ + if (!m_doc->isReadWrite()) + return false; + + return true; +} + + +bool KateView::tagLines( KTextEditor::Range range, bool realRange ) +{ + return tagLines(range.start(), range.end(), realRange); +} + +void KateView::deactivateEditActions() +{ + foreach(QAction *action, m_editActions) + action->setEnabled(false); +} + +void KateView::activateEditActions() +{ + foreach(QAction *action, m_editActions) + action->setEnabled(true); +} + +bool KateView::mouseTrackingEnabled( ) const +{ + // FIXME support + return true; +} + +bool KateView::setMouseTrackingEnabled( bool ) +{ + // FIXME support + return true; +} + +bool KateView::isCompletionActive( ) const +{ + return completionWidget()->isCompletionActive(); +} + +KateCompletionWidget* KateView::completionWidget() const +{ + if (!m_completionWidget) + m_completionWidget = new KateCompletionWidget(const_cast(this)); + + return m_completionWidget; +} + +void KateView::startCompletion( const KTextEditor::Range & word, KTextEditor::CodeCompletionModel * model ) +{ + completionWidget()->startCompletion(word, model); +} + +void KateView::abortCompletion( ) +{ + completionWidget()->abortCompletion(); +} + +void KateView::forceCompletion( ) +{ + completionWidget()->execute(); +} + +void KateView::registerCompletionModel(KTextEditor::CodeCompletionModel* model) +{ + completionWidget()->registerCompletionModel(model); +} + +void KateView::unregisterCompletionModel(KTextEditor::CodeCompletionModel* model) +{ + completionWidget()->unregisterCompletionModel(model); +} + +bool KateView::isAutomaticInvocationEnabled() const +{ + return m_config->automaticCompletionInvocation(); +} + +void KateView::setAutomaticInvocationEnabled(bool enabled) +{ + config()->setAutomaticCompletionInvocation(enabled); +} + +void KateView::sendCompletionExecuted(const KTextEditor::Cursor& position, KTextEditor::CodeCompletionModel* model, const QModelIndex& index) +{ + emit completionExecuted(this, position, model, index); +} + +void KateView::sendCompletionAborted() +{ + emit completionAborted(this); +} + +void KateView::paste(const QString *textToPaste) +{ + const bool completionEnabled = isAutomaticInvocationEnabled(); + if (completionEnabled) { + setAutomaticInvocationEnabled(false); + } + + m_doc->paste( this, textToPaste ? *textToPaste : QApplication::clipboard()->text(QClipboard::Clipboard) ); + + if (completionEnabled) { + setAutomaticInvocationEnabled(true); + } +} + +void KateView::setCaretStyle( KateRenderer::caretStyles style, bool repaint ) +{ + m_viewInternal->setCaretStyle( style, repaint ); +} + +bool KateView::setCursorPosition( KTextEditor::Cursor position ) +{ + return setCursorPositionInternal( position, 1, true ); +} + +KTextEditor::Cursor KateView::cursorPosition( ) const +{ + return m_viewInternal->getCursor(); +} + +KTextEditor::Cursor KateView::cursorPositionVirtual( ) const +{ + return KTextEditor::Cursor (m_viewInternal->getCursor().line(), virtualCursorColumn()); +} + +QPoint KateView::cursorToCoordinate( const KTextEditor::Cursor & cursor ) const +{ + return m_viewInternal->cursorToCoordinate(cursor); +} + +KTextEditor::Cursor KateView::coordinatesToCursor(const QPoint& coords) const +{ + return m_viewInternal->coordinatesToCursor(coords); +} + +QPoint KateView::cursorPositionCoordinates( ) const +{ + return m_viewInternal->cursorCoordinates(); +} + +bool KateView::setCursorPositionVisual( const KTextEditor::Cursor & position ) +{ + return setCursorPositionInternal( position, m_doc->config()->tabWidth(), true ); +} + +QString KateView::currentTextLine( ) +{ + return m_doc->line( cursorPosition().line() ); +} + +QString KateView::searchPattern() const +{ + if (hasSearchBar()) { + return m_searchBar->searchPattern(); + } else { + return QString(); + } +} + +QString KateView::replacementPattern() const +{ + if (hasSearchBar()) { + return m_searchBar->replacementPattern(); + } else { + return QString(); + } +} + +void KateView::setSearchPattern(const QString &searchPattern) +{ + searchBar()->setSearchPattern(searchPattern); +} + +void KateView::setReplacementPattern(const QString &replacementPattern) +{ + searchBar()->setReplacementPattern(replacementPattern); +} + +void KateView::indent( ) +{ + KTextEditor::Cursor c(cursorPosition().line(), 0); + KTextEditor::Range r = selection() ? selectionRange() : KTextEditor::Range(c, c); + m_doc->indent( r, 1 ); +} + +void KateView::unIndent( ) +{ + KTextEditor::Cursor c(cursorPosition().line(), 0); + KTextEditor::Range r = selection() ? selectionRange() : KTextEditor::Range(c, c); + m_doc->indent( r, -1 ); +} + +void KateView::cleanIndent( ) +{ + KTextEditor::Cursor c(cursorPosition().line(), 0); + KTextEditor::Range r = selection() ? selectionRange() : KTextEditor::Range(c, c); + m_doc->indent( r, 0 ); +} + +void KateView::align( ) +{ + // no selection: align current line; selection: use selection range + const int line = cursorPosition().line(); + KTextEditor::Range alignRange(KTextEditor::Cursor (line,0), KTextEditor::Cursor (line,0)); + if (selection()) { + alignRange = selectionRange(); + } + + m_doc->align( this, alignRange ); +} + +void KateView::comment( ) +{ + m_selection.setInsertBehaviors(Kate::TextRange::ExpandLeft | Kate::TextRange::ExpandRight); + m_doc->comment( this, cursorPosition().line(), cursorPosition().column(), 1 ); + m_selection.setInsertBehaviors(Kate::TextRange::ExpandRight); +} + +void KateView::uncomment( ) +{ + m_doc->comment( this, cursorPosition().line(), cursorPosition().column(),-1 ); +} + +void KateView::toggleComment( ) +{ + m_selection.setInsertBehaviors(Kate::TextRange::ExpandLeft | Kate::TextRange::ExpandRight); + m_doc->comment( this, cursorPosition().line(), cursorPosition().column(), 0 ); + m_selection.setInsertBehaviors(Kate::TextRange::ExpandRight); +} + +void KateView::uppercase( ) +{ + m_doc->transform( this, m_viewInternal->m_cursor, KateDocument::Uppercase ); +} + +void KateView::killLine( ) +{ + if (m_selection.isEmpty()) { + m_doc->removeLine(cursorPosition().line()); + } else { + m_doc->editStart(); + for (int line = m_selection.end().line(); line >= m_selection.start().line(); line--) { + m_doc->removeLine(line); + } + m_doc->editEnd(); + } +} + +void KateView::lowercase( ) +{ + m_doc->transform( this, m_viewInternal->m_cursor, KateDocument::Lowercase ); +} + +void KateView::capitalize( ) +{ + m_doc->editStart(); + m_doc->transform( this, m_viewInternal->m_cursor, KateDocument::Lowercase ); + m_doc->transform( this, m_viewInternal->m_cursor, KateDocument::Capitalize ); + m_doc->editEnd(); +} + +void KateView::keyReturn( ) +{ + m_viewInternal->doReturn(); +} + +void KateView::smartNewline( ) +{ + m_viewInternal->doSmartNewline(); +} + +void KateView::backspace( ) +{ + m_viewInternal->doBackspace(); +} + +void KateView::insertTab( ) +{ + m_viewInternal->doTabulator(); +} + +void KateView::deleteWordLeft( ) +{ + m_viewInternal->doDeletePrevWord(); +} + +void KateView::keyDelete( ) +{ + m_viewInternal->doDelete(); +} + +void KateView::deleteWordRight( ) +{ + m_viewInternal->doDeleteNextWord(); +} + +void KateView::transpose( ) +{ + m_viewInternal->doTranspose(); +} + +void KateView::cursorLeft( ) +{ + if (m_viewInternal->m_view->currentTextLine().isRightToLeft()) + m_viewInternal->cursorNextChar(); + else + m_viewInternal->cursorPrevChar(); +} + +void KateView::shiftCursorLeft( ) +{ + if (m_viewInternal->m_view->currentTextLine().isRightToLeft()) + m_viewInternal->cursorNextChar(true); + else + m_viewInternal->cursorPrevChar(true); +} + +void KateView::cursorRight( ) +{ + if (m_viewInternal->m_view->currentTextLine().isRightToLeft()) + m_viewInternal->cursorPrevChar(); + else + m_viewInternal->cursorNextChar(); +} + +void KateView::shiftCursorRight( ) +{ + if (m_viewInternal->m_view->currentTextLine().isRightToLeft()) + m_viewInternal->cursorPrevChar(true); + else + m_viewInternal->cursorNextChar(true); +} + +void KateView::wordLeft( ) +{ + if (m_viewInternal->m_view->currentTextLine().isRightToLeft()) + m_viewInternal->wordNext(); + else + m_viewInternal->wordPrev(); +} + +void KateView::shiftWordLeft( ) +{ + if (m_viewInternal->m_view->currentTextLine().isRightToLeft()) + m_viewInternal->wordNext(true); + else + m_viewInternal->wordPrev(true); +} + +void KateView::wordRight( ) +{ + if (m_viewInternal->m_view->currentTextLine().isRightToLeft()) + m_viewInternal->wordPrev(); + else + m_viewInternal->wordNext(); +} + +void KateView::shiftWordRight( ) +{ + if (m_viewInternal->m_view->currentTextLine().isRightToLeft()) + m_viewInternal->wordPrev(true); + else + m_viewInternal->wordNext(true); +} + +void KateView::home( ) +{ + m_viewInternal->home(); +} + +void KateView::shiftHome( ) +{ + m_viewInternal->home(true); +} + +void KateView::end( ) +{ + m_viewInternal->end(); +} + +void KateView::shiftEnd( ) +{ + m_viewInternal->end(true); +} + +void KateView::up( ) +{ + m_viewInternal->cursorUp(); +} + +void KateView::shiftUp( ) +{ + m_viewInternal->cursorUp(true); +} + +void KateView::down( ) +{ + m_viewInternal->cursorDown(); +} + +void KateView::shiftDown( ) +{ + m_viewInternal->cursorDown(true); +} + +void KateView::scrollUp( ) +{ + m_viewInternal->scrollUp(); +} + +void KateView::scrollDown( ) +{ + m_viewInternal->scrollDown(); +} + +void KateView::topOfView( ) +{ + m_viewInternal->topOfView(); +} + +void KateView::shiftTopOfView( ) +{ + m_viewInternal->topOfView(true); +} + +void KateView::bottomOfView( ) +{ + m_viewInternal->bottomOfView(); +} + +void KateView::shiftBottomOfView( ) +{ + m_viewInternal->bottomOfView(true); +} + +void KateView::pageUp( ) +{ + m_viewInternal->pageUp(); +} + +void KateView::shiftPageUp( ) +{ + m_viewInternal->pageUp(true); +} + +void KateView::pageDown( ) +{ + m_viewInternal->pageDown(); +} + +void KateView::shiftPageDown( ) +{ + m_viewInternal->pageDown(true); +} + +void KateView::top( ) +{ + m_viewInternal->top_home(); +} + +void KateView::shiftTop( ) +{ + m_viewInternal->top_home(true); +} + +void KateView::bottom( ) +{ + m_viewInternal->bottom_end(); +} + +void KateView::shiftBottom( ) +{ + m_viewInternal->bottom_end(true); +} + +void KateView::toMatchingBracket( ) +{ + m_viewInternal->cursorToMatchingBracket(); +} + +void KateView::shiftToMatchingBracket( ) +{ + m_viewInternal->cursorToMatchingBracket(true); +} + +void KateView::toPrevModifiedLine() +{ + const int startLine = m_viewInternal->m_cursor.line() - 1; + const int line = m_doc->findModifiedLine(startLine, false); + if (line >= 0) { + KTextEditor::Cursor c(line, 0); + m_viewInternal->updateSelection(c, false); + m_viewInternal->updateCursor(c); + } +} + +void KateView::toNextModifiedLine() +{ + const int startLine = m_viewInternal->m_cursor.line() + 1; + const int line = m_doc->findModifiedLine(startLine, true); + if (line >= 0) { + KTextEditor::Cursor c(line, 0); + m_viewInternal->updateSelection(c, false); + m_viewInternal->updateCursor(c); + } +} + +const KTextEditor::Range & KateView::selectionRange( ) const +{ + // update the cache + m_holdSelectionRangeForAPI = m_selection; + + // return cached value, has right type! + return m_holdSelectionRangeForAPI; +} + +KTextEditor::Document * KateView::document( ) const +{ + return m_doc; +} + +void KateView::setContextMenu( QMenu * menu ) +{ + if (m_contextMenu) { + disconnect(m_contextMenu, SIGNAL(aboutToShow()), this, SLOT(aboutToShowContextMenu())); + disconnect(m_contextMenu, SIGNAL(aboutToHide()), this, SLOT(aboutToHideContextMenu())); + } + m_contextMenu = menu; + m_userContextMenuSet=true; + + if (m_contextMenu) { + connect(m_contextMenu, SIGNAL(aboutToShow()), this, SLOT(aboutToShowContextMenu())); + connect(m_contextMenu, SIGNAL(aboutToHide()), this, SLOT(aboutToHideContextMenu())); + } +} + +QMenu *KateView::contextMenu( ) const +{ + if (m_userContextMenuSet) + return m_contextMenu; + else + { + KXMLGUIClient* client = const_cast(this); + while (client->parentClient()) + client = client->parentClient(); + + //kDebug() << "looking up all menu containers"; + if (client->factory()){ + QList conts = client->factory()->containers("menu"); + foreach (QWidget *w, conts) + { + if (w->objectName() == "ktexteditor_popup") + {//perhaps optimize this block + QMenu* menu=(QMenu*)w; + disconnect(menu, SIGNAL(aboutToShow()), this, SLOT(aboutToShowContextMenu())); + disconnect(menu, SIGNAL(aboutToHide()), this, SLOT(aboutToHideContextMenu())); + connect(menu, SIGNAL(aboutToShow()), this, SLOT(aboutToShowContextMenu())); + connect(menu, SIGNAL(aboutToHide()), this, SLOT(aboutToHideContextMenu())); + return menu; + } + } + } + } + return 0; +} + +QMenu * KateView::defaultContextMenu(QMenu* menu) const +{ + if (!menu) + menu = new KMenu(const_cast(this)); + + menu->addAction(m_editUndo); + menu->addAction(m_editRedo); + menu->addSeparator(); + menu->addAction(m_cut); + menu->addAction(m_copy); + menu->addAction(m_paste); + menu->addSeparator(); + menu->addAction(m_selectAll); + menu->addAction(m_deSelect); + if (QAction *spellingSuggestions = actionCollection()->action("spelling_suggestions")) { + menu->addSeparator(); + menu->addAction(spellingSuggestions); + } + if (QAction* bookmark = actionCollection()->action("bookmarks")) { + menu->addSeparator(); + menu->addAction(bookmark); + } + return menu; +} + +void KateView::aboutToShowContextMenu( ) +{ + QMenu* menu = qobject_cast(sender()); + + if (menu) { + emit contextMenuAboutToShow(this, menu); + } +} + +void KateView::aboutToHideContextMenu( ) +{ + m_spellingMenu->setUseMouseForMisspelledRange(false); +} + +// BEGIN ConfigInterface stff +QStringList KateView::configKeys() const +{ + return QStringList() << "icon-bar" << "line-numbers" << "dynamic-word-wrap" + << "background-color" << "selection-color" + << "search-highlight-color" << "replace-highlight-color" + << "folding-bar" << "icon-border-color" << "folding-marker-color" + << "line-number-color" << "modification-markers"; +} + +QVariant KateView::configValue(const QString &key) +{ + if (key == "icon-bar") + return config()->iconBar(); + else if (key == "line-numbers") + return config()->lineNumbers(); + else if (key == "dynamic-word-wrap") + return config()->dynWordWrap(); + else if (key == "background-color") + return renderer()->config()->backgroundColor(); + else if (key == "selection-color") + return renderer()->config()->selectionColor(); + else if (key == "search-highlight-color") + return renderer()->config()->searchHighlightColor(); + else if (key == "replace-highlight-color") + return renderer()->config()->replaceHighlightColor(); + else if (key == "default-mark-type") + return config()->defaultMarkType(); + else if (key == "allow-mark-menu") + return config()->allowMarkMenu(); + else if (key == "folding-bar") + return config()->foldingBar(); + else if (key == "icon-border-color") + return renderer()->config()->iconBarColor(); + else if (key == "folding-marker-color") + return renderer()->config()->foldingColor(); + else if (key == "line-number-color") + return renderer()->config()->lineNumberColor(); + else if (key == "modification-markers") + return config()->lineModification(); + + // return invalid variant + return QVariant(); +} + +void KateView::setConfigValue(const QString &key, const QVariant &value) +{ + if ( value.canConvert(QVariant::Color) ) { + if (key == "background-color") + renderer()->config()->setBackgroundColor(value.value()); + else if (key == "selection-color") + renderer()->config()->setSelectionColor(value.value()); + else if (key == "search-highlight-color") + renderer()->config()->setSearchHighlightColor(value.value()); + else if (key == "replace-highlight-color") + renderer()->config()->setReplaceHighlightColor(value.value()); + else if (key == "icon-border-color") + renderer()->config()->setIconBarColor(value.value()); + else if (key == "folding-marker-color") + renderer()->config()->setFoldingColor(value.value()); + else if (key == "line-number-color") + renderer()->config()->setLineNumberColor(value.value()); + } else if ( value.type() == QVariant::Bool ) { + // Note explicit type check above. If we used canConvert, then + // values of type UInt will be trapped here. + if (key == "icon-bar") + config()->setIconBar(value.toBool()); + else if (key == "line-numbers") + config()->setLineNumbers(value.toBool()); + else if (key == "dynamic-word-wrap") + config()->setDynWordWrap(value.toBool()); + else if (key == "allow-mark-menu") + config()->setAllowMarkMenu(value.toBool()); + else if (key == "folding-bar") + config()->setFoldingBar(value.toBool()); + else if (key == "modification-markers") + config()->setLineModification(value.toBool()); + } else if ( value.canConvert(QVariant::UInt) ) { + if (key == "default-mark-type") + config()->setDefaultMarkType(value.toUInt()); + } +} + +// END ConfigInterface + +void KateView::userInvokedCompletion() +{ + completionWidget()->userInvokedCompletion(); +} + +KateViewBar *KateView::topViewBar() const +{ + return m_topViewBar; +} + +KateViewBar *KateView::bottomViewBar() const +{ + return m_bottomViewBar; +} + +KateCommandLineBar *KateView::cmdLineBar () +{ + if (!m_cmdLine) { + m_cmdLine = new KateCommandLineBar (this, bottomViewBar()); + bottomViewBar()->addBarWidget(m_cmdLine); + } + + return m_cmdLine; +} + +KateSearchBar *KateView::searchBar (bool initHintAsPower) +{ + if (!m_searchBar) { + m_searchBar = new KateSearchBar(initHintAsPower, this, KateViewConfig::global()); + } + return m_searchBar; +} + +KateGotoBar *KateView::gotoBar () +{ + if (!m_gotoBar) { + m_gotoBar = new KateGotoBar (this); + bottomViewBar()->addBarWidget(m_gotoBar); + } + + return m_gotoBar; +} + +KateDictionaryBar *KateView::dictionaryBar () +{ + if(!m_dictionaryBar) { + m_dictionaryBar = new KateDictionaryBar(this); + bottomViewBar()->addBarWidget(m_dictionaryBar); + } + + return m_dictionaryBar; +} + +void KateView::setAnnotationModel( KTextEditor::AnnotationModel* model ) +{ + KTextEditor::AnnotationModel* oldmodel = m_annotationModel; + m_annotationModel = model; + m_viewInternal->m_leftBorder->annotationModelChanged(oldmodel, m_annotationModel); +} + +KTextEditor::AnnotationModel* KateView::annotationModel() const +{ + return m_annotationModel; +} + +void KateView::setAnnotationBorderVisible( bool visible ) +{ + m_viewInternal->m_leftBorder->setAnnotationBorderOn( visible ); +} + +bool KateView::isAnnotationBorderVisible() const +{ + return m_viewInternal->m_leftBorder->annotationBorderOn(); +} + +KTextEditor::Range KateView::visibleRange() +{ + //ensure that the view is up-to-date, otherwise 'endPos()' might fail! + m_viewInternal->updateView(); + return KTextEditor::Range(m_viewInternal->toRealCursor(m_viewInternal->startPos()), + m_viewInternal->toRealCursor(m_viewInternal->endPos())); +} + +void KateView::toggleOnTheFlySpellCheck(bool b) +{ + m_doc->onTheFlySpellCheckingEnabled(b); +} + +void KateView::reflectOnTheFlySpellCheckStatus(bool enabled) +{ + m_spellingMenu->setVisible(enabled); + m_toggleOnTheFlySpellCheck->setChecked(enabled); +} + +KateSpellingMenu* KateView::spellingMenu() +{ + return m_spellingMenu; +} + +void KateView::notifyAboutRangeChange (int startLine, int endLine, bool rangeWithAttribute) +{ +#ifdef VIEW_RANGE_DEBUG + // output args + kDebug() << "trigger attribute changed from" << startLine << "to" << endLine << "rangeWithAttribute" << rangeWithAttribute; +#endif + + // first call: + if (!m_delayedUpdateTriggered) { + m_delayedUpdateTriggered = true; + m_lineToUpdateMin = -1; + m_lineToUpdateMax = -1; + + // only set initial line range, if range with attribute! + if (rangeWithAttribute) { + m_lineToUpdateMin = startLine; + m_lineToUpdateMax = endLine; + } + + // emit queued signal and be done + emit delayedUpdateOfView (); + return; + } + + // ignore lines if no attribute + if (!rangeWithAttribute) + return; + + // update line range + if (startLine != -1 && (m_lineToUpdateMin == -1 || startLine < m_lineToUpdateMin)) + m_lineToUpdateMin = startLine; + + if (endLine != -1 && endLine > m_lineToUpdateMax) + m_lineToUpdateMax = endLine; +} + +void KateView::slotDelayedUpdateOfView () +{ + if (!m_delayedUpdateTriggered) + return; + +#ifdef VIEW_RANGE_DEBUG + // output args + kDebug() << "delayed attribute changed from" << m_lineToUpdateMin << "to" << m_lineToUpdateMax; +#endif + + // update ranges in + updateRangesIn (KTextEditor::Attribute::ActivateMouseIn); + updateRangesIn (KTextEditor::Attribute::ActivateCaretIn); + + // update view, if valid line range, else only feedback update wanted anyway + if (m_lineToUpdateMin != -1 && m_lineToUpdateMax != -1) { + tagLines (m_lineToUpdateMin, m_lineToUpdateMax, true); + updateView (true); + } + + // reset flags + m_delayedUpdateTriggered = false; + m_lineToUpdateMin = -1; + m_lineToUpdateMax = -1; +} + +void KateView::updateRangesIn (KTextEditor::Attribute::ActivationType activationType) +{ + // new ranges with cursor in, default none + QSet newRangesIn; + + // on which range set we work? + QSet &oldSet = (activationType == KTextEditor::Attribute::ActivateMouseIn) ? m_rangesMouseIn : m_rangesCaretIn; + + // which cursor position to honor? + KTextEditor::Cursor currentCursor = (activationType == KTextEditor::Attribute::ActivateMouseIn) ? m_viewInternal->getMouse() : m_viewInternal->getCursor (); + + // first: validate the remembered ranges + QSet validRanges; + foreach (Kate::TextRange *range, oldSet) + if (m_doc->buffer().rangePointerValid(range)) + validRanges.insert (range); + + // cursor valid? else no new ranges can be found + if (currentCursor.isValid () && currentCursor.line() < m_doc->buffer().lines()) { + // now: get current ranges for the line of cursor with an attribute + QList rangesForCurrentCursor = m_doc->buffer().rangesForLine (currentCursor.line(), this, false); + + // match which ranges really fit the given cursor + foreach (Kate::TextRange *range, rangesForCurrentCursor) { + // range has no dynamic attribute of right type and no feedback object + if ((!range->attribute() || !range->attribute()->dynamicAttribute (activationType)) && !range->feedback()) + continue; + + // range doesn't contain cursor, not interesting + if ((range->start().insertBehavior() == KTextEditor::MovingCursor::StayOnInsert) + ? (currentCursor < range->start().toCursor ()) : (currentCursor <= range->start().toCursor ())) + continue; + + if ((range->end().insertBehavior() == KTextEditor::MovingCursor::StayOnInsert) + ? (range->end().toCursor () <= currentCursor) : (range->end().toCursor () < currentCursor)) + continue; + + // range contains cursor, was it already in old set? + if (validRanges.contains (range)) { + // insert in new, remove from old, be done with it + newRangesIn.insert (range); + validRanges.remove (range); + continue; + } + + // oh, new range, trigger update and insert into new set + newRangesIn.insert (range); + + if (range->attribute() && range->attribute()->dynamicAttribute (activationType)) + notifyAboutRangeChange (range->start().line(), range->end().line(), true); + + // feedback + if (range->feedback ()) { + if (activationType == KTextEditor::Attribute::ActivateMouseIn) + range->feedback ()->mouseEnteredRange (range, this); + else + range->feedback ()->caretEnteredRange (range, this); + } + +#ifdef VIEW_RANGE_DEBUG + // found new range for activation + kDebug() << "activated new range" << range << "by" << activationType; +#endif + } + } + + // now: notify for left ranges! + foreach (Kate::TextRange *range, validRanges) { + // range valid + right dynamic attribute, trigger update + if (range->toRange().isValid() && range->attribute() && range->attribute()->dynamicAttribute (activationType)) + notifyAboutRangeChange (range->start().line(), range->end().line(), true); + + // feedback + if (range->feedback ()) { + if (activationType == KTextEditor::Attribute::ActivateMouseIn) + range->feedback ()->mouseExitedRange (range, this); + else + range->feedback ()->caretExitedRange (range, this); + } + } + + // set new ranges + oldSet = newRangesIn; +} + +void KateView::postMessage(KTextEditor::Message* message, + QList > actions) +{ + // just forward to KateMessageWidget :-) + if (message->position() == KTextEditor::Message::AboveView) { + m_topMessageWidget->postMessage(message, actions); + } else if (message->position() == KTextEditor::Message::BelowView) { + m_bottomMessageWidget->postMessage(message, actions); + } else if (message->position() == KTextEditor::Message::TopInView) { + if (!m_floatTopMessageWidget) { + m_floatTopMessageWidget = new KateMessageWidget(m_viewInternal, true); + m_notificationLayout->insertWidget(0, m_floatTopMessageWidget, 0, Qt::AlignTop | Qt::AlignRight); + connect(this, SIGNAL(displayRangeChanged(KateView*)), m_floatTopMessageWidget, SLOT(startAutoHideTimer())); + connect(this, SIGNAL(cursorPositionChanged(KTextEditor::View*, const KTextEditor::Cursor&)), m_floatTopMessageWidget, SLOT(startAutoHideTimer())); + } + m_floatTopMessageWidget->postMessage(message, actions); + } else if (message->position() == KTextEditor::Message::BottomInView) { + if (!m_floatBottomMessageWidget) { + m_floatBottomMessageWidget = new KateMessageWidget(m_viewInternal, true); + m_notificationLayout->addWidget(m_floatBottomMessageWidget, 0, Qt::AlignBottom | Qt::AlignRight); + connect(this, SIGNAL(displayRangeChanged(KateView*)), m_floatBottomMessageWidget, SLOT(startAutoHideTimer())); + connect(this, SIGNAL(cursorPositionChanged(KTextEditor::View*, const KTextEditor::Cursor&)), m_floatBottomMessageWidget, SLOT(startAutoHideTimer())); + } + m_floatBottomMessageWidget->postMessage(message, actions); + } +} + +void KateView::saveFoldingState () +{ + m_savedFoldingState = m_textFolding.exportFoldingRanges (); +} + +void KateView::applyFoldingState () +{ + m_textFolding.importFoldingRanges (m_savedFoldingState); + m_savedFoldingState.clear (); +} + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/view/kateview.h b/kate/part/view/kateview.h new file mode 100644 index 00000000..2c43ca91 --- /dev/null +++ b/kate/part/view/kateview.h @@ -0,0 +1,841 @@ + /* This file is part of the KDE libraries + Copyright (C) 2002 John Firebaugh + Copyright (C) 2001 Christoph Cullmann + Copyright (C) 2001-2010 Joseph Wenninger + Copyright (C) 1999 Jochen Wilhelmy + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KATE_VIEW_H +#define KATE_VIEW_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "katetextrange.h" +#include "katetextfolding.h" +#include "katerenderer.h" + +namespace KTextEditor +{ + class AnnotationModel; + class Message; +} + +class KateDocument; +class KateBookmarks; +class KateCommandLineBar; +class KateViewConfig; +class KateRenderer; +class KateSpellCheckDialog; +class KateCompletionWidget; +class KateViewInternal; +class KateSearchBar; +class KateViewBar; +class KateGotoBar; +class KateDictionaryBar; +class KateSpellingMenu; +class KateMessageWidget; + +class KToggleAction; +class KAction; +class KSelectAction; + +#include + +// +// Kate KTextEditor::View class ;) +// +class KATEPARTINTERFACES_EXPORT KateView : public KTextEditor::View, + public KTextEditor::TextHintInterface, + public KTextEditor::SessionConfigInterface, + public KTextEditor::TemplateInterface2, + public KTextEditor::CodeCompletionInterface, + public KTextEditor::ConfigInterface, + public KTextEditor::AnnotationViewInterface, + public KTextEditor::CoordinatesToCursorInterface +{ + Q_OBJECT + Q_INTERFACES(KTextEditor::TextHintInterface) + Q_INTERFACES(KTextEditor::SessionConfigInterface) + Q_INTERFACES(KTextEditor::TemplateInterface) + Q_INTERFACES(KTextEditor::TemplateInterface2) + Q_INTERFACES(KTextEditor::ConfigInterface) + Q_INTERFACES(KTextEditor::CodeCompletionInterface) + Q_INTERFACES(KTextEditor::AnnotationViewInterface) + Q_INTERFACES(KTextEditor::CoordinatesToCursorInterface) + + friend class KateViewInternal; + friend class KateIconBorder; + + public: + KateView( KateDocument* doc, QWidget* parent ); + ~KateView (); + + KTextEditor::Document *document () const; + + QString viewMode () const; + + // + // KTextEditor::ClipboardInterface + // + public Q_SLOTS: + void paste(const QString *textToPaste = 0); + void cut(); + void copy() const; + + private Q_SLOTS: + /** + * internal use, apply word wrap + */ + void applyWordWrap (); + + // + // KTextEditor::PopupMenuInterface + // + public: + void setContextMenu( QMenu* menu ); + QMenu* contextMenu() const; + QMenu* defaultContextMenu(QMenu* menu = 0L) const; + + private Q_SLOTS: + void aboutToShowContextMenu(); + void aboutToHideContextMenu(); + + private: + QPointer m_contextMenu; + + // + // KTextEditor::ViewCursorInterface + // + public: + /** + * Set the caret's style. + * The caret can be a box or a line; see the documentation + * of KateRenderer::caretStyles for other options. + * @param style the caret style + * @param repaint whether to update the caret instantly. + * This also resets the caret's timer. + */ + void setCaretStyle( KateRenderer::caretStyles style, bool repaint = false ); + + bool setCursorPosition (KTextEditor::Cursor position); + + KTextEditor::Cursor cursorPosition () const; + + KTextEditor::Cursor cursorPositionVirtual () const; + + QPoint cursorToCoordinate(const KTextEditor::Cursor& cursor) const; + + KTextEditor::Cursor coordinatesToCursor(const QPoint& coord) const; + + QPoint cursorPositionCoordinates() const; + + bool setCursorPositionVisual( const KTextEditor::Cursor& position ); + + /** + * Return the virtual cursor column, each tab is expanded into the + * document's tabWidth characters. If word wrap is off, the cursor may be + * behind the line's length. + */ + int virtualCursorColumn() const; + + virtual bool mouseTrackingEnabled() const; + virtual bool setMouseTrackingEnabled(bool enable); + + private: + void notifyMousePositionChanged(const KTextEditor::Cursor& newPosition); + + // Internal + public: + bool setCursorPositionInternal( const KTextEditor::Cursor& position, uint tabwidth = 1, bool calledExternally = false ); + + // + // KTextEditor::ConfigInterface + // + public: + QStringList configKeys() const; + QVariant configValue(const QString &key); + void setConfigValue(const QString &key, const QVariant &value); + + Q_SIGNALS: + void configChanged(); + + public: + /** + * Try to fold starting at the given line. + * This will both try to fold existing folding ranges of this line and to query the highlighting what to fold. + * @param startLine start line to fold at + */ + void foldLine (int startLine); + + /** + * Try to unfold all foldings starting at the given line. + * @param startLine start line to unfold at + */ + void unfoldLine (int startLine); + + // + // KTextEditor::CodeCompletionInterface2 + // + public: + virtual bool isCompletionActive() const; + virtual void startCompletion(const KTextEditor::Range& word, KTextEditor::CodeCompletionModel* model); + virtual void abortCompletion(); + virtual void forceCompletion(); + virtual void registerCompletionModel(KTextEditor::CodeCompletionModel* model); + virtual void unregisterCompletionModel(KTextEditor::CodeCompletionModel* model); + virtual bool isAutomaticInvocationEnabled() const; + virtual void setAutomaticInvocationEnabled(bool enabled = true); + + Q_SIGNALS: + void completionExecuted(KTextEditor::View* view, const KTextEditor::Cursor& position, KTextEditor::CodeCompletionModel* model, const QModelIndex&); + void completionAborted(KTextEditor::View* view); + + public Q_SLOTS: + void userInvokedCompletion(); + + public: + KateCompletionWidget* completionWidget() const; + mutable KateCompletionWidget* m_completionWidget; + void sendCompletionExecuted(const KTextEditor::Cursor& position, KTextEditor::CodeCompletionModel* model, const QModelIndex& index); + void sendCompletionAborted(); + + // + // KTextEditor::TextHintInterface + // + public: + void enableTextHints(int timeout); + void disableTextHints(); + + Q_SIGNALS: + void needTextHint(const KTextEditor::Cursor& position, QString &text); + + public: + bool dynWordWrap() const { return m_hasWrap; } + + // + // KTextEditor::SelectionInterface stuff + // + public Q_SLOTS: + virtual bool setSelection ( const KTextEditor::Range &selection ); + + // unhide method... + bool setSelection (const KTextEditor::Cursor &c, int i, bool b) + { return KTextEditor::View::setSelection (c, i, b); } + + virtual bool removeSelection () { return clearSelection(); } + + virtual bool removeSelectionText () { return removeSelectedText(); } + + virtual bool setBlockSelection (bool on); + bool toggleBlockSelection (); + + bool clearSelection (); + bool clearSelection (bool redraw, bool finishedChangingSelection = true); + + bool removeSelectedText (); + + bool selectAll(); + + public: + virtual bool selection() const; + virtual QString selectionText() const; + virtual bool blockSelection() const; + virtual const KTextEditor::Range &selectionRange() const; + + static void blockFix(KTextEditor::Range& range); + + private: + mutable KTextEditor::Range m_holdSelectionRangeForAPI; + + // + // Arbitrary Syntax HL + Action extensions + // + public: + // Action association extension + void deactivateEditActions(); + void activateEditActions(); + + // + // internal helper stuff, for katerenderer and so on + // + public: + // should cursor be wrapped ? take config + blockselection state in account + bool wrapCursor () const; + + // some internal functions to get selection state of a line/col + bool cursorSelected(const KTextEditor::Cursor& cursor); + bool lineSelected (int line); + bool lineEndSelected (const KTextEditor::Cursor& lineEndPos); + bool lineHasSelected (int line); + bool lineIsSelection (int line); + + void ensureCursorColumnValid(); + + void tagSelection (const KTextEditor::Range &oldSelection); + + void selectWord( const KTextEditor::Cursor& cursor ); + void selectLine( const KTextEditor::Cursor& cursor ); + + + //BEGIN EDIT STUFF + public: + void editStart (); + void editEnd (int editTagLineStart, int editTagLineEnd, bool tagFrom); + + void editSetCursor (const KTextEditor::Cursor &cursor); + //END + + //BEGIN TAG & CLEAR + public: + bool tagLine (const KTextEditor::Cursor& virtualCursor); + + bool tagRange (const KTextEditor::Range& range, bool realLines = false); + bool tagLines (int start, int end, bool realLines = false ); + bool tagLines (KTextEditor::Cursor start, KTextEditor::Cursor end, bool realCursors = false); + bool tagLines (KTextEditor::Range range, bool realRange = false); + + void tagAll (); + + void clear (); + + void repaintText (bool paintOnlyDirty = false); + + void updateView (bool changed = false); + //END + + // + // KTextEditor::AnnotationView + // + public: + void setAnnotationModel( KTextEditor::AnnotationModel* model ); + KTextEditor::AnnotationModel* annotationModel() const; + void setAnnotationBorderVisible( bool visible); + bool isAnnotationBorderVisible() const; + + Q_SIGNALS: + void annotationContextMenuAboutToShow( KTextEditor::View* view, QMenu* menu, int line ); + void annotationActivated( KTextEditor::View* view, int line ); + void annotationBorderVisibilityChanged( View* view, bool visible ); + + void navigateLeft(); + void navigateRight(); + void navigateUp(); + void navigateDown(); + void navigateAccept(); + void navigateBack(); + + private: + KTextEditor::AnnotationModel* m_annotationModel; + + // + // KTextEditor::View + // + public: + void emitNavigateLeft() { + emit navigateLeft(); + } + void emitNavigateRight() { + emit navigateRight(); + } + void emitNavigateUp() { + emit navigateUp(); + } + void emitNavigateDown() { + emit navigateDown(); + } + void emitNavigateAccept() { + emit navigateAccept(); + } + void emitNavigateBack() { + emit navigateBack(); + } + /** + Return values for "save" related commands. + */ + bool isOverwriteMode() const; + EditMode viewEditMode() const; + QString currentTextLine(); + + /** + * The current search pattern. + * This is set by the last search. + * @return the search pattern or the empty string if not set + */ + QString searchPattern() const; + + /** + * The current replacement string. + * This is set by the last search and replace. + * @return the replacment string or the empty string if not set + */ + QString replacementPattern() const; + + /** + * Set the current search pattern. + * @param searchPattern the search pattern + */ + void setSearchPattern(const QString &searchPattern); + + /** + * Set the current replacement pattern. + * @param replacementPattern the replacement pattern + */ + void setReplacementPattern(const QString &replacementPattern); + + public Q_SLOTS: + void indent(); + void unIndent(); + void cleanIndent(); + void align(); + void comment(); + void uncomment(); + void toggleComment(); + void killLine(); + + /** + Uppercases selected text, or an alphabetic character next to the cursor. + */ + void uppercase(); + /** + Lowercases selected text, or an alphabetic character next to the cursor. + */ + void lowercase(); + /** + Capitalizes the selection (makes each word start with an uppercase) or + the word under the cursor. + */ + void capitalize(); + /** + Joins lines touched by the selection + */ + void joinLines(); + + // Note - the following functions simply forward to KateViewInternal + void keyReturn(); + void smartNewline(); + void backspace(); + void deleteWordLeft(); + void keyDelete(); + void deleteWordRight(); + void transpose(); + void cursorLeft(); + void shiftCursorLeft(); + void cursorRight(); + void shiftCursorRight(); + void wordLeft(); + void shiftWordLeft(); + void wordRight(); + void shiftWordRight(); + void home(); + void shiftHome(); + void end(); + void shiftEnd(); + void up(); + void shiftUp(); + void down(); + void shiftDown(); + void scrollUp(); + void scrollDown(); + void topOfView(); + void shiftTopOfView(); + void bottomOfView(); + void shiftBottomOfView(); + void pageUp(); + void shiftPageUp(); + void pageDown(); + void shiftPageDown(); + void top(); + void shiftTop(); + void bottom(); + void shiftBottom(); + void toMatchingBracket(); + void shiftToMatchingBracket(); + void toPrevModifiedLine(); + void toNextModifiedLine(); + void insertTab(); + + void gotoLine(); + + // config file / session management functions + public: + void readSessionConfig(const KConfigGroup&); + void writeSessionConfig(KConfigGroup&); + + public Q_SLOTS: + void setEol( int eol ); + void setAddBom( bool enabled); + void find(); + void findSelectedForwards(); + void findSelectedBackwards(); + void replace(); + void findNext(); + void findPrevious(); + + void setFoldingMarkersOn( bool enable ); // Not in KTextEditor::View, but should be + void setIconBorder( bool enable ); + void setLineNumbersOn( bool enable ); + void setScrollBarMarks( bool enable ); + void setScrollBarMiniMap( bool enable ); + void setScrollBarMiniMapAll( bool enable ); + void setScrollBarMiniMapWidth( int width ); + void toggleFoldingMarkers(); + void toggleIconBorder(); + void toggleLineNumbersOn(); + void toggleScrollBarMarks(); + void toggleScrollBarMiniMap(); + void toggleScrollBarMiniMapAll(); + void toggleDynWordWrap (); + + void setDynWrapIndicators(int mode); + + public: + int getEol() const; + + public: + KateRenderer *renderer (); + + bool iconBorder(); + bool lineNumbersOn(); + bool scrollBarMarks(); + bool scrollBarMiniMap(); + bool scrollBarMiniMapAll(); + int dynWrapIndicators(); + bool foldingMarkersOn(); + + private Q_SLOTS: + /** + * used to update actions after selection changed + */ + void slotSelectionChanged (); + + public: + /** + * accessor to katedocument pointer + * @return pointer to document + */ + KateDocument* doc() { return m_doc; } + const KateDocument* doc() const { return m_doc; } + + public Q_SLOTS: + void slotUpdateUndo(); + void toggleInsert(); + void reloadFile(); + void toggleWWMarker(); + void toggleWriteLock(); + void switchToCmdLine (); + void slotReadWriteChanged (); + void slotClipboardHistoryChanged (); + + Q_SIGNALS: + void dropEventPass(QDropEvent*); + + public: + /** + * Folding handler for this view. + * @return folding handler + */ + Kate::TextFolding &textFolding () + { + return m_textFolding; + } + + public: + void slotTextInserted ( KTextEditor::View *view, const KTextEditor::Cursor &position, const QString &text); + + protected: + void contextMenuEvent( QContextMenuEvent* ); + + private Q_SLOTS: + void slotGotFocus(); + void slotLostFocus(); + void slotDropEventPass( QDropEvent* ev ); + void slotSaveCanceled( const QString& error ); + void slotConfigDialog (); + + public Q_SLOTS: // TODO: turn into good interface, see kte5/foldinginterface.h + void slotFoldToplevelNodes(); + void slotCollapseLocal(); + void slotCollapseLevel(); + void slotExpandLevel(); + void slotExpandLocal(); + + private: + void setupConnections(); + void setupActions(); + void setupEditActions(); + void setupCodeFolding(); + + QList m_editActions; + KAction* m_editUndo; + KAction* m_editRedo; + KAction* m_pasteMenu; + KToggleAction* m_toggleFoldingMarkers; + KToggleAction* m_toggleIconBar; + KToggleAction* m_toggleLineNumbers; + KToggleAction* m_toggleScrollBarMarks; + KToggleAction* m_toggleScrollBarMiniMap; + KToggleAction* m_toggleScrollBarMiniMapAll; + KToggleAction* m_toggleDynWrap; + KSelectAction* m_setDynWrapIndicators; + KToggleAction* m_toggleWWMarker; + KAction* m_switchCmdLine; + KToggleAction* m_viInputModeAction; + + KSelectAction* m_setEndOfLine; + KToggleAction* m_addBom; + + QAction *m_cut; + QAction *m_copy; + QAction *m_paste; + QAction *m_selectAll; + QAction *m_deSelect; + + KToggleAction *m_toggleBlockSelection; + KToggleAction *m_toggleInsert; + KToggleAction *m_toggleWriteLock; + + bool m_hasWrap; + + KateDocument *const m_doc; + Kate::TextFolding m_textFolding; + KateViewConfig *const m_config; + KateRenderer *const m_renderer; + KateViewInternal *const m_viewInternal; + KateSpellCheckDialog *m_spell; + KateBookmarks *const m_bookmarks; + + QVBoxLayout *m_vBox; + + private Q_SLOTS: + void slotHlChanged(); + + /** + * Configuration + */ + public: + inline KateViewConfig *config () { return m_config; } + + void updateConfig (); + + void updateDocumentConfig(); + + void updateRendererConfig(); + + private Q_SLOTS: + void updateFoldingConfig (); + + private: + bool m_startingUp; + bool m_updatingDocumentConfig; + + // stores the current selection + Kate::TextRange m_selection; + + // do we select normal or blockwise ? + bool blockSelect; + + // + // TemplateInterface + TemplateInterface2 + // + public: + virtual bool insertTemplateTextImplementation ( const KTextEditor::Cursor&, const QString &templateString, const QMap &initialValues); + virtual bool insertTemplateTextImplementation ( const KTextEditor::Cursor&, const QString &templateString, const QMap &initialValues, KTextEditor::TemplateScript* templateScript); + /** + * Accessors to the bars... + */ + public: + KateViewBar *topViewBar() const; + KateViewBar *bottomViewBar() const; + KateCommandLineBar *cmdLineBar (); + KateDictionaryBar *dictionaryBar(); + + private: + KateSearchBar *searchBar (bool initHintAsPower = false); + bool hasSearchBar () const { return m_searchBar != 0; } + KateGotoBar *gotoBar (); + + /** + * viewbar + its widgets + * they are created on demand... + */ + private: + // created in constructor of the view + KateViewBar *m_bottomViewBar; + KateViewBar *m_topViewBar; + // created on demand..., only access them through the above accessors.... + KateCommandLineBar *m_cmdLine; + KateSearchBar *m_searchBar; + KateGotoBar *m_gotoBar; + KateDictionaryBar *m_dictionaryBar; + + public: + KTextEditor::Range visibleRange(); + + Q_SIGNALS: + void displayRangeChanged(KateView *view); + + protected: + KToggleAction* m_toggleOnTheFlySpellCheck; + KateSpellingMenu *m_spellingMenu; + + protected Q_SLOTS: + void toggleOnTheFlySpellCheck(bool b); + + public Q_SLOTS: + void changeDictionary(); + void reflectOnTheFlySpellCheckStatus(bool enabled); + + public: + KateSpellingMenu* spellingMenu(); + private: + bool m_userContextMenuSet; + + private Q_SLOTS: + /** + * save folding state before document reload + */ + void saveFoldingState (); + + /** + * restore folding state after document reload + */ + void applyFoldingState (); + + private: + /** + * saved folding state + */ + QVariantList m_savedFoldingState; + +public: + /** + * Attribute of a range changed or range with attribute changed in given line range. + * @param startLine start line of change + * @param endLine end line of change + * @param rangeWithAttribute attribute changed or is active, this will perhaps lead to repaints + */ + void notifyAboutRangeChange (int startLine, int endLine, bool rangeWithAttribute); + + private Q_SLOTS: + /** + * Delayed update for view after text ranges changed + */ + void slotDelayedUpdateOfView (); + + Q_SIGNALS: + /** + * Delayed update for view after text ranges changed + */ + void delayedUpdateOfView (); + + public: + /** + * set of ranges which had the mouse inside last time, used for rendering + * @return set of ranges which had the mouse inside last time checked + */ + const QSet *rangesMouseIn () const { return &m_rangesMouseIn; } + + /** + * set of ranges which had the caret inside last time, used for rendering + * @return set of ranges which had the caret inside last time checked + */ + const QSet *rangesCaretIn () const { return &m_rangesCaretIn; } + + /** + * check if ranges changed for mouse in and caret in + * @param activationType type of activation to check + */ + void updateRangesIn (KTextEditor::Attribute::ActivationType activationType); + + // + // helpers for delayed view update after ranges changes + // + private: + /** + * update already inited? + */ + bool m_delayedUpdateTriggered; + + /** + * minimal line to update + */ + int m_lineToUpdateMin; + + /** + * maximal line to update + */ + int m_lineToUpdateMax; + + /** + * set of ranges which had the mouse inside last time + */ + QSet m_rangesMouseIn; + + /** + * set of ranges which had the caret inside last time + */ + QSet m_rangesCaretIn; + + // + // forward impl for KTextEditor::MessageInterface + // + public: + /** + * Used by Document::postMessage(). + */ + void postMessage(KTextEditor::Message* message, QList > actions); + + private: + /** Message widget showing KTextEditor::Messages above the View. */ + KateMessageWidget* m_topMessageWidget; + /** Message widget showing KTextEditor::Messages below the View. */ + KateMessageWidget* m_bottomMessageWidget; + /** Message widget showing KTextEditor::Messages as view overlay in top right corner. */ + KateMessageWidget* m_floatTopMessageWidget; + /** Message widget showing KTextEditor::Messages as view overlay in bottom left corner. */ + KateMessageWidget* m_floatBottomMessageWidget; + /** Layout for floating notifications */ + QVBoxLayout* m_notificationLayout; + + // for unit test 'tests/messagetest.cpp' + public: + KateMessageWidget* messageWidget() { return m_floatTopMessageWidget; } +}; + +/** + * metatype register + */ +Q_DECLARE_METATYPE(KTextEditor::Cursor) +Q_DECLARE_METATYPE(KTextEditor::Range) + +#endif + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/view/kateviewhelpers.cpp b/kate/part/view/kateviewhelpers.cpp new file mode 100644 index 00000000..e779462f --- /dev/null +++ b/kate/part/view/kateviewhelpers.cpp @@ -0,0 +1,2598 @@ +/* This file is part of the KDE libraries + Copyright (C) 2008, 2009 Matthew Woehlke + Copyright (C) 2007 Mirko Stocker + Copyright (C) 2002 John Firebaugh + Copyright (C) 2001 Anders Lund + Copyright (C) 2001 Christoph Cullmann + Copyright (C) 2011 Svyatoslav Kuzmich + Copyright (C) 2012 Kåre Särs (Minimap) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "kateviewhelpers.h" + +#include "katecmd.h" +#include +#include +#include +#include +#include +#include "kateconfig.h" +#include "katedocument.h" +#include +#include "katerenderer.h" +#include "kateview.h" +#include "kateviewinternal.h" +#include "katelayoutcache.h" +#include "katetextlayout.h" +#include "kateglobal.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ktoolinvocation.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +//BEGIN KateScrollBar +static const int s_lineWidth = 100; +static const int s_pixelMargin = 8; +static const int s_linePixelIncLimit = 6; + +static const unsigned char s_characterOpacity[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // <- 15 + 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 255, 0, 0, 0, 0, 0, // <- 31 + 0, 125, 41, 221, 138, 195, 218, 21, 142, 142, 137, 137, 97, 87, 87, 140, // <- 47 + 223, 164, 183, 190, 191, 193, 214, 158, 227, 216, 103, 113, 146, 140, 146, 149, // <- 63 + 248, 204, 240, 174, 217, 197, 178, 205, 209, 176, 168, 211, 160, 246, 238, 218, // <- 79 + 195, 229, 227, 196, 167, 212, 188, 238, 197, 169, 189, 158, 21, 151, 115, 90, // <- 95 + 15, 192, 209, 153, 208, 187, 162, 221, 183, 149, 161, 191, 146, 203, 167, 182, // <- 111 + 208, 203, 139, 166, 158, 167, 157, 189, 164, 179, 156, 167, 145, 166, 109, 0, // <- 127 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // <- 143 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // <- 159 + 0, 125, 184, 187, 146, 201, 127, 203, 89, 194, 156, 141, 117, 87, 202, 88, // <- 175 + 115, 165, 118, 121, 85, 190, 236, 87, 88, 111, 151, 140, 194, 191, 203, 148, // <- 191 + 215, 215, 222, 224, 223, 234, 230, 192, 208, 208, 216, 217, 187, 187, 194, 195, // <- 207 + 228, 255, 228, 228, 235, 239, 237, 150, 255, 222, 222, 229, 232, 180, 197, 225, // <- 223 + 208, 208, 216, 217, 212, 230, 218, 170, 202, 202, 211, 204, 156, 156, 165, 159, // <- 239 + 214, 194, 197, 197, 206, 206, 201, 132, 214, 183, 183, 192, 187, 195, 227, 198 +}; + + +KateScrollBar::KateScrollBar (Qt::Orientation orientation, KateViewInternal* parent) + : QScrollBar (orientation, parent->m_view) + , m_middleMouseDown (false) + , m_leftMouseDown (false) + , m_view(parent->m_view) + , m_doc(parent->doc()) + , m_viewInternal(parent) + , m_showMarks(false) + , m_showMiniMap(false) + , m_miniMapAll(true) + , m_miniMapWidth(40) + , m_grooveHeight(height()) +{ + connect(this, SIGNAL(valueChanged(int)), this, SLOT(sliderMaybeMoved(int))); + connect(m_doc, SIGNAL(marksChanged(KTextEditor::Document*)), this, SLOT(marksChanged())); + + styleChange(*style()); + + m_updateTimer.setInterval(300); + m_updateTimer.setSingleShot(true); + QTimer::singleShot(10, this, SLOT(updatePixmap())); +} + +void KateScrollBar::setShowMiniMap(bool b) +{ + if (b && !m_showMiniMap) { + connect(m_view, SIGNAL(selectionChanged(KTextEditor::View*)), &m_updateTimer, SLOT(start()), Qt::UniqueConnection); + connect(m_doc, SIGNAL(textChanged(KTextEditor::Document*)), &m_updateTimer, SLOT(start()), Qt::UniqueConnection); + connect(m_view, SIGNAL(delayedUpdateOfView()), &m_updateTimer, SLOT(start()), Qt::UniqueConnection); + connect(&m_updateTimer, SIGNAL(timeout()), this, SLOT(updatePixmap()), Qt::UniqueConnection); + connect(&(m_view->textFolding()), SIGNAL(foldingRangesChanged()), &m_updateTimer, SLOT(start()), Qt::UniqueConnection); + } + else if (!b) { + disconnect(&m_updateTimer); + } + + m_showMiniMap = b; + + updateGeometry(); + update(); +} + +QSize KateScrollBar::sizeHint() const +{ + if (m_showMiniMap) { + return QSize(m_miniMapWidth, QScrollBar::sizeHint().height()); + } + return QScrollBar::sizeHint(); +} + +int KateScrollBar::minimapYToStdY(int y) +{ + // Check if the minimap fills the whole scrollbar + if (m_stdGroveRect.height() == m_mapGroveRect.height()){ + return y; + } + + // check if y is on the step up/down + if ((y < m_stdGroveRect.top()) || (y > m_stdGroveRect.bottom())) { + return y; + } + + if (y < m_mapGroveRect.top()) { + return m_stdGroveRect.top() + 1; + } + + if (y > m_mapGroveRect.bottom()) { + return m_stdGroveRect.bottom() - 1; + } + + // check for div/0 + if (m_mapGroveRect.height() == 0) { + return y; + } + + int newY = (y - m_mapGroveRect.top()) * m_stdGroveRect.height() / m_mapGroveRect.height(); + newY += m_stdGroveRect.top(); + return newY; +} + +void KateScrollBar::mousePressEvent(QMouseEvent* e) +{ + if (m_showMiniMap) { + QMouseEvent eMod(QEvent::MouseButtonPress, + QPoint(6, minimapYToStdY(e->pos().y())), + e->button(), e->buttons(), e->modifiers()); + QScrollBar::mousePressEvent(&eMod); + } + else { + QScrollBar::mousePressEvent(e); + } + + if (e->button() == Qt::MiddleButton) + m_middleMouseDown = true; + else if (e->button() == Qt::LeftButton) + m_leftMouseDown = true; + + m_toolTipPos = e->globalPos() - QPoint(e->pos().x(), 0); + const int fromLine = m_viewInternal->toRealCursor(m_viewInternal->startPos()).line() + 1; + const int lastLine = m_viewInternal->toRealCursor(m_viewInternal->endPos()).line() + 1; + QToolTip::showText(m_toolTipPos, i18nc("from line - to line", "
%1

%2
", fromLine, lastLine), this); + + redrawMarks(); +} + +void KateScrollBar::mouseReleaseEvent(QMouseEvent* e) +{ + if (e->button() == Qt::MiddleButton) + m_middleMouseDown = false; + else if (e->button() == Qt::LeftButton) + m_leftMouseDown = false; + + redrawMarks(); + + if (m_leftMouseDown || m_middleMouseDown) { + QToolTip::hideText(); + } + + if (m_showMiniMap) { + QMouseEvent eMod(QEvent::MouseButtonRelease, + QPoint(e->pos().x(), minimapYToStdY(e->pos().y())), + e->button(), e->buttons(), e->modifiers()); + QScrollBar::mouseReleaseEvent(&eMod); + } + else { + QScrollBar::mouseReleaseEvent(e); + } +} + +void KateScrollBar::mouseMoveEvent(QMouseEvent* e) +{ + if (m_showMiniMap) { + QMouseEvent eMod(QEvent::MouseMove, + QPoint(e->pos().x(), minimapYToStdY(e->pos().y())), + e->button(), e->buttons(), e->modifiers()); + QScrollBar::mouseMoveEvent(&eMod); + } + else { + QScrollBar::mouseMoveEvent(e); + } + + if (e->buttons() & (Qt::LeftButton | Qt::MiddleButton)) { + redrawMarks(); + + // current line tool tip + m_toolTipPos = e->globalPos() - QPoint(e->pos().x(), 0); + const int fromLine = m_viewInternal->toRealCursor(m_viewInternal->startPos()).line() + 1; + const int lastLine = m_viewInternal->toRealCursor(m_viewInternal->endPos()).line() + 1; + QToolTip::showText(m_toolTipPos, i18nc("from line - to line", "
%1

%2
", fromLine, lastLine), this); + } +} + +void KateScrollBar::paintEvent(QPaintEvent *e) +{ + if (m_doc->marks().size() != m_lines.size()) { + recomputeMarksPositions(); + } + if (m_showMiniMap) { + miniMapPaintEvent(e); + } + else { + normalPaintEvent(e); + } +} + +// This function is optimized for bing called in sequence. +const QColor KateScrollBar::charColor(const QVector &attributes, int &attributeIndex, + const QList &decorations, + const QColor &defaultColor, int x, QChar ch) +{ + QColor color = defaultColor; + + bool styleFound = false; + + // Query the decorations, that is, things like search highlighting, or the + // KDevelop DUChain highlighting, for a color to use + foreach (const QTextLayout::FormatRange& range, decorations) { + if (range.start <= x && range.start + range.length > x) { + // If there's a different background color set (search markers, ...) + // use that, otherwise use the foreground color. + if ( range.format.hasProperty(QTextFormat::BackgroundBrush) ) { + color = range.format.background().color(); + } + else { + color = range.format.foreground().color(); + } + styleFound = true; + break; + } + } + + // If there's no decoration set for the current character (this will mostly be the case for + // plain Kate), query the styles, that is, the default kate syntax highlighting. + if (!styleFound) { + // go to the block containing x + while ((attributeIndex < attributes.size()) && + ((attributes[attributeIndex].offset + attributes[attributeIndex].length) < x)) + { + ++attributeIndex; + } + if ((attributeIndex < attributes.size()) && (x < attributes[attributeIndex].offset + attributes[attributeIndex].length)) { + color = m_view->renderer()->attribute(attributes[attributeIndex].attributeValue)->foreground().color(); + } + } + + // Query how much "blackness" the character has. + // This causes for example a dot or a dash to appear less intense + // than an A or similar. + // This gives the pixels created a bit of structure, which makes it look more + // like real text. + color.setAlpha((ch.unicode() < 256) ? s_characterOpacity[ch.unicode()] : 1.0); + + return color; +} + +void KateScrollBar::updatePixmap() +{ + //QTime time; + //time.start(); + + if (!m_showMiniMap) { + // make sure no time is wasted if the option is disabled + return; + } + + // For performance reason, only every n-th line will be drawn if the widget is + // sufficiently small compared to the amount of lines in the document. + int docLineCount = m_view->textFolding().visibleLines(); + int pixmapLineCount = docLineCount; + if (m_view->config()->scrollPastEnd()) { + pixmapLineCount += pageStep(); + } + int pixmapLinesUnscaled = pixmapLineCount; + if (m_grooveHeight < 5) m_grooveHeight = 5; + int lineDivisor = pixmapLinesUnscaled/m_grooveHeight; + if (lineDivisor < 1) lineDivisor = 1; + int charIncrement = 1; + int lineIncrement = 1; + if ( (m_grooveHeight > 10) && (pixmapLineCount >= m_grooveHeight*2) ) { + charIncrement = pixmapLineCount / m_grooveHeight; + while (charIncrement > s_linePixelIncLimit) { + lineIncrement++; + pixmapLineCount = pixmapLinesUnscaled/lineIncrement; + charIncrement = pixmapLineCount / m_grooveHeight; + } + pixmapLineCount /= charIncrement; + } + + int pixmapLineWidth = s_pixelMargin + s_lineWidth/charIncrement; + + //kDebug(13040) << "l" << lineIncrement << "c" << charIncrement << "d" << lineDivisor; + //kDebug(13040) << "pixmap" << pixmapLineCount << pixmapLineWidth << "docLines" << m_view->textFolding().visibleLines() << "height" << m_grooveHeight; + + QColor backgroundColor; + QColor defaultTextColor; + if (m_doc->defaultStyle(KTextEditor::HighlightInterface::dsNormal)) { + backgroundColor = m_doc->defaultStyle(KTextEditor::HighlightInterface::dsNormal)->background().color(); + defaultTextColor = m_doc->defaultStyle(KTextEditor::HighlightInterface::dsNormal)->foreground().color(); + } + else { + backgroundColor = palette().color(QPalette::Base); + defaultTextColor = palette().color(QPalette::Text); + } + QColor modifiedLineColor = m_view->renderer()->config()->modifiedLineColor(); + QColor savedLineColor = m_view->renderer()->config()->savedLineColor(); + // move the modified line color away from the background color + modifiedLineColor.setHsv(modifiedLineColor.hue(), 255, 255 - backgroundColor.value()/3); + savedLineColor.setHsv(savedLineColor.hue(), 100, 255 - backgroundColor.value()/3); + + m_pixmap = QPixmap(pixmapLineWidth, pixmapLineCount); + m_pixmap.fill(QColor("transparent")); + + QPainter painter; + if ( painter.begin(&m_pixmap) ) { + // Do not force updates of the highlighting if the document is very large + bool simpleMode = m_doc->lines() > 7500; + + int pixelY = 0; + int drawnLines = 0; + + // Iterate over all visible lines, drawing them. + for (int virtualLine=0; virtualLine < docLineCount; virtualLine += lineIncrement) { + + int realLineNumber = m_view->textFolding().visibleLineToLine(virtualLine); + QString lineText = m_doc->line(realLineNumber); + + // use this to control the offset of the text from the left + int pixelX = s_pixelMargin; + + if (!simpleMode) { + m_doc->buffer().ensureHighlighted(realLineNumber); + } + const Kate::TextLine& kateline = m_doc->plainKateTextLine(realLineNumber); + + const QVector &attributes = kateline->attributesList(); + QList< QTextLayout::FormatRange > decorations = m_view->renderer()->decorationsForLine(kateline, realLineNumber); + int attributeIndex = 0; + + // The color to draw the currently selected text in; change the alpha value to make it + // more or less intense + QColor selectionColor = palette().color(QPalette::HighlightedText); + selectionColor.setAlpha(180); + + painter.setPen(defaultTextColor); + // Iterate over all the characters in the current line + for (int x = 0; (x < lineText.size() && x < s_lineWidth); x += charIncrement) { + if (pixelX >= s_lineWidth + s_pixelMargin) { + break; + } + + // draw the pixels + if (lineText[x] == ' ') { + pixelX++; + } + else if (lineText[x] == '\t') { + pixelX += qMax(4/charIncrement, 1); // FIXME: tab width... + } + else { + painter.setPen(charColor(attributes, attributeIndex, decorations, defaultTextColor, x, lineText[x])); + + // Actually draw the pixel with the color queried from the renderer. + painter.drawPoint(pixelX, pixelY); + + pixelX++; + } + } + drawnLines++; + if (((drawnLines) % charIncrement) == 0) { + pixelY++; + } + + } + //kDebug(13040) << drawnLines; + // Draw line modification marker map. + // Disable this if the document is really huge, + // since it requires querying every line. + if ( m_doc->lines() < 50000 ) { + for ( int lineno = 0; lineno < docLineCount; lineno++ ) { + int realLineNo = m_view->textFolding().visibleLineToLine(lineno); + const Kate::TextLine& line = m_doc->plainKateTextLine(realLineNo); + if ( line->markedAsModified() ) { + painter.setPen(modifiedLineColor); + } + else if ( line->markedAsSavedOnDisk() ) { + painter.setPen(savedLineColor); + } + else { + continue; + } + painter.drawRect(2, lineno/lineDivisor, 3, 1); + } + } + } + //kDebug(13040) << time.elapsed(); + // Redraw the scrollbar widget with the updated pixmap. + update(); +} + +void KateScrollBar::miniMapPaintEvent(QPaintEvent *e) +{ + QScrollBar::paintEvent(e); + + QPainter painter(this); + + QStyleOptionSlider opt; + opt.init(this); + opt.subControls = QStyle::SC_None; + opt.activeSubControls = QStyle::SC_None; + opt.orientation = orientation(); + opt.minimum = minimum(); + opt.maximum = maximum(); + opt.sliderPosition = sliderPosition(); + opt.sliderValue = value(); + opt.singleStep = singleStep(); + opt.pageStep = pageStep(); + + int docXMargin = 1; + QRect grooveRect = style()->subControlRect(QStyle::CC_ScrollBar, &opt, QStyle::SC_ScrollBarGroove, this); + m_stdGroveRect = grooveRect; + if (style()->subControlRect(QStyle::CC_ScrollBar, &opt, QStyle::SC_ScrollBarSubLine, this).height() == 0) { + int alignMargin = style()->pixelMetric(QStyle::PM_FocusFrameVMargin, &opt, this); + grooveRect.moveTop(alignMargin); + grooveRect.setHeight(grooveRect.height() - alignMargin); + } + if (style()->subControlRect(QStyle::CC_ScrollBar, &opt, QStyle::SC_ScrollBarAddLine, this).height() == 0) { + int alignMargin = style()->pixelMetric(QStyle::PM_FocusFrameVMargin, &opt, this); + grooveRect.setHeight(grooveRect.height() - alignMargin); + } + m_grooveHeight = grooveRect.height(); + + QRect sliderRect = style()->subControlRect(QStyle::CC_ScrollBar, &opt, QStyle::SC_ScrollBarSlider, this); + m_stdSliderRect = sliderRect; + sliderRect.adjust(docXMargin+1, 1, -(docXMargin+1), -1); + + //style()->drawControl(QStyle::CE_ScrollBarAddLine, &opt, &painter, this); + //style()->drawControl(QStyle::CE_ScrollBarSubLine, &opt, &painter, this); + + // calculate the document size and position + int docHeight = qMin(grooveRect.height(), m_pixmap.height()*2) - 2*docXMargin; + int yoffset = 1; // top-aligned in stead of center-aligned (grooveRect.height() - docHeight) / 2; + QRect docRect(QPoint(grooveRect.left()+docXMargin, yoffset+grooveRect.top()), QSize(grooveRect.width()-2*docXMargin, docHeight)); + m_mapGroveRect = docRect; + + // calculate the visible area + int max = qMax(maximum()+1, 1); + int visibleStart = value()*docHeight/(max+pageStep()) + docRect.top(); + int visibleEnd = (value()+pageStep())*docHeight/(max+pageStep()) + docRect.top(); + QRect visibleRect = docRect; + visibleRect.moveTop(visibleStart); + visibleRect.setHeight(visibleEnd-visibleStart); + m_mapSliderRect = visibleRect; + + // calculate colors + QColor backgroundColor; + QColor foregroundColor; + // TODO KDE5: If HighlightInterface is a View interface, use HighlightInterface::defaultStyle() again. + if (m_doc->defaultStyle(KTextEditor::HighlightInterface::dsNormal)) { + backgroundColor = m_doc->defaultStyle(KTextEditor::HighlightInterface::dsNormal)->background().color(); + foregroundColor = m_doc->defaultStyle(KTextEditor::HighlightInterface::dsNormal)->foreground().color(); + } + else { + backgroundColor = palette().color(QPalette::Base); + foregroundColor = palette().color(QPalette::Text); + } + int backgroundLightness = backgroundColor.lightness(); + int foregroundLightness = foregroundColor.lightness(); + int lighnessDiff = (foregroundLightness - backgroundLightness); + + // get a color suited for the color theme + QColor darkShieldColor = palette().color(QPalette::Mid); + int hue, sat, light; + darkShieldColor.getHsl(&hue, &sat, &light); + // apply suitable lightness + darkShieldColor.setHsl(hue, sat, backgroundLightness + lighnessDiff * 0.35); + // gradient for nicer results + QLinearGradient gradient(0, 0, width(), 0); + gradient.setColorAt(0, darkShieldColor); + gradient.setColorAt(0.3, darkShieldColor.lighter(115)); + gradient.setColorAt(1, darkShieldColor); + + QColor lightShieldColor; + lightShieldColor.setHsl(hue, sat, backgroundLightness + lighnessDiff * 0.15); + + QColor outlineColor; + outlineColor.setHsl(hue, sat, backgroundLightness + lighnessDiff * 0.5); + + // draw the grove background in case the document is small + painter.setPen(QPen(QColor("transparent"),0)); + painter.setBrush(palette().brush(QPalette::Dark)); + painter.drawRect(grooveRect); + + // dark "shield" of non-slider parts + painter.setBrush(gradient); + painter.drawRect(docRect); + + + // light "shield" non-visible parts + if ((docHeight+2*docXMargin >= grooveRect.height()) && (sliderRect.height() > visibleRect.height()+2)) { + // slider + painter.setPen(QPen(QColor("transparent"),0)); + painter.setBrush(lightShieldColor); + sliderRect.adjust(1,0,-1,0); + painter.drawRect(sliderRect); + + // visible area + visibleRect.adjust(2,0,-3,0); + painter.setPen(QPen(backgroundColor,1)); + painter.setBrush(backgroundColor); + painter.drawRect(visibleRect); + + // slider outline + painter.setRenderHint(QPainter::Antialiasing); + painter.setPen(QPen(outlineColor, 2)); + painter.setBrush(QColor("transparent")); + sliderRect.adjust(-1,0,1,0); + painter.drawRoundedRect(sliderRect, 4, 4); + painter.setRenderHint(QPainter::Antialiasing, false); + } + else { + // visible area with outline + painter.setRenderHint(QPainter::Antialiasing); + painter.setPen(QPen(outlineColor, 2)); + painter.setBrush(backgroundColor); + visibleRect.adjust(1,0,-1,2); + painter.drawRoundedRect(visibleRect, 4, 4); + painter.setRenderHint(QPainter::Antialiasing, false); + } + + // Smooth transform only when squeezing + if (grooveRect.height() < m_pixmap.height()) { + painter.setRenderHint(QPainter::SmoothPixmapTransform); + } + + // draw the modified lines margin + QRect pixmapMarginRect(QPoint(0, 0), QSize(s_pixelMargin, m_pixmap.height())); + QRect docPixmapMarginRect(QPoint(0, docRect.top()), QSize(s_pixelMargin, docRect.height())); + painter.drawPixmap(docPixmapMarginRect, m_pixmap, pixmapMarginRect); + + // calculate the stretch and draw the stretched lines + QRect pixmapRect(QPoint(s_pixelMargin, 0), QSize(m_pixmap.width() - s_pixelMargin, m_pixmap.height())); + QRect docPixmapRect(QPoint(s_pixelMargin, docRect.top()), QSize(docRect.width()-s_pixelMargin, docRect.height())); + painter.drawPixmap(docPixmapRect, m_pixmap, pixmapRect); + + if (!m_showMarks) return; + + QHashIterator it = m_lines; + QPen penBg; + penBg.setWidth(4); + lightShieldColor.setAlpha(180); + penBg.setColor(lightShieldColor); + painter.setPen(penBg); + while (it.hasNext()) + { + it.next(); + int y = (it.key()-grooveRect.top()) * docHeight/grooveRect.height() + docRect.top();; + painter.drawLine(6, y, width()-6, y); + } + + it = m_lines; + QPen pen; + pen.setWidth(2); + while (it.hasNext()) + { + it.next(); + pen.setColor(it.value()); + painter.setPen(pen); + int y = (it.key()-grooveRect.top()) * docHeight/grooveRect.height() + docRect.top();; + painter.drawLine(6, y, width()-6, y); + } +} + +void KateScrollBar::normalPaintEvent(QPaintEvent *e) +{ + QScrollBar::paintEvent(e); + + if (!m_showMarks) return; + + QPainter painter(this); + + QStyleOptionSlider opt; + opt.init(this); + opt.subControls = QStyle::SC_None; + opt.activeSubControls = QStyle::SC_None; + opt.orientation = orientation(); + opt.minimum = minimum(); + opt.maximum = maximum(); + opt.sliderPosition = sliderPosition(); + opt.sliderValue = value(); + opt.singleStep = singleStep(); + opt.pageStep = pageStep(); + + QRect rect = style()->subControlRect(QStyle::CC_ScrollBar, &opt, QStyle::SC_ScrollBarSlider, this); + int sideMargin = width() - rect.width(); + if (sideMargin < 4) sideMargin = 4; + sideMargin /= 2; + + QHashIterator it = m_lines; + while (it.hasNext()) + { + it.next(); + painter.setPen(it.value()); + if (it.key() < rect.top() || it.key() > rect.bottom()) + { + painter.drawLine(0, it.key(), width(), it.key()); + } + else + { + painter.drawLine(0, it.key(), sideMargin, it.key()); + painter.drawLine(width()-sideMargin, it.key(), width(), it.key()); + } + } +} + +void KateScrollBar::resizeEvent(QResizeEvent *e) +{ + QScrollBar::resizeEvent(e); + m_updateTimer.start(); + m_lines.clear(); + update(); +} + +void KateScrollBar::styleChange(QStyle &s) +{ + QScrollBar::styleChange(s); + m_lines.clear(); + update(); +} + +void KateScrollBar::sliderChange ( SliderChange change ) +{ + // call parents implementation + QScrollBar::sliderChange (change); + + if (change == QAbstractSlider::SliderValueChange) + { + redrawMarks(); + } + else if (change == QAbstractSlider::SliderRangeChange) + { + marksChanged(); + } + + if (m_leftMouseDown || m_middleMouseDown) { + const int fromLine = m_viewInternal->toRealCursor(m_viewInternal->startPos()).line() + 1; + const int lastLine = m_viewInternal->toRealCursor(m_viewInternal->endPos()).line() + 1; + QToolTip::showText(m_toolTipPos, i18nc("from line - to line", "
%1

%2
", fromLine, lastLine), this); + } +} + +void KateScrollBar::marksChanged() +{ + m_lines.clear(); + update(); +} + +void KateScrollBar::redrawMarks() +{ + if (!m_showMarks) return; + update(); +} + +void KateScrollBar::recomputeMarksPositions() +{ + // get the style options to compute the scrollbar pixels + QStyleOptionSlider opt; + initStyleOption(&opt); + QRect grooveRect = style()->subControlRect(QStyle::CC_ScrollBar, &opt, QStyle::SC_ScrollBarGroove, this); + + // cache top margin and groove height + const int top = grooveRect.top(); + const int h = grooveRect.height() - 1; + + // make sure we have a sane height + if (h <= 0) return; + + // get total visible (=without folded) lines in the document + int visibleLines = m_view->textFolding().visibleLines() - 1; + if (m_view->config()->scrollPastEnd()) { + visibleLines += m_viewInternal->linesDisplayed() - 1; + visibleLines -= m_view->config()->autoCenterLines(); + } + + // now repopulate the scrollbar lines list + m_lines.clear(); + const QHash &marks = m_doc->marks(); + for (QHash::const_iterator i = marks.constBegin(); i != marks.constEnd(); ++i) + { + KTextEditor::Mark *mark = i.value(); + const int line = m_view->textFolding().lineToVisibleLine(mark->line); + const double ratio = static_cast(line) / visibleLines; + m_lines.insert(top + (int)(h * ratio), + KateRendererConfig::global()->lineMarkerColor((KTextEditor::MarkInterface::MarkTypes)mark->type)); + } +} + +void KateScrollBar::sliderMaybeMoved(int value) +{ + if (m_middleMouseDown) { + // we only need to emit this signal once, as for the following slider + // movements the signal sliderMoved() is already emitted. + // Thus, set m_middleMouseDown to false right away. + m_middleMouseDown = false; + emit sliderMMBMoved(value); + } +} +//END + + +//BEGIN KateCmdLineEditFlagCompletion +/** + * This class provide completion of flags. It shows a short description of + * each flag, and flags are appended. + */ +class KateCmdLineEditFlagCompletion : public KCompletion +{ + public: + KateCmdLineEditFlagCompletion() {;} + + QString makeCompletion( const QString & /*s*/ ) + { + return QString(); + } + +}; +//END KateCmdLineEditFlagCompletion + +//BEGIN KateCmdLineEdit +KateCommandLineBar::KateCommandLineBar (KateView *view, QWidget *parent) + : KateViewBarWidget (true, parent) +{ + QHBoxLayout *topLayout = new QHBoxLayout (); + centralWidget()->setLayout(topLayout); + topLayout->setMargin(0); + m_lineEdit = new KateCmdLineEdit (this, view); + connect(m_lineEdit, SIGNAL(hideRequested()), SIGNAL(hideMe())); + topLayout->addWidget (m_lineEdit); + + QToolButton *helpButton = new QToolButton(this); + helpButton->setAutoRaise(true); + helpButton->setIcon(KIcon(KIcon ("help-contextual"))); + topLayout->addWidget(helpButton); + connect (helpButton,SIGNAL(clicked()),this,SLOT(showHelpPage())); + + setFocusProxy (m_lineEdit); +} + +void KateCommandLineBar::showHelpPage() { + KToolInvocation::invokeHelp("advanced-editing-tools-commandline","kate"); +} + +KateCommandLineBar::~KateCommandLineBar() +{ +} + +// inserts the given string in the command line edit and (if selected = true) selects it so the user +// can type over it if they want to +void KateCommandLineBar::setText(const QString &text, bool selected) +{ + m_lineEdit->setText(text); + if (selected) { + m_lineEdit->selectAll(); + } +} + +void KateCommandLineBar::execute(const QString &text) +{ + m_lineEdit->slotReturnPressed(text); +} + +KateCmdLineEdit::KateCmdLineEdit (KateCommandLineBar *bar, KateView *view) + : KLineEdit () + , m_view (view) + , m_bar (bar) + , m_msgMode (false) + , m_histpos( 0 ) + , m_cmdend( 0 ) + , m_command( 0L ) +{ + connect (this, SIGNAL(returnPressed(QString)), + this, SLOT(slotReturnPressed(QString))); + + setCompletionObject(KateCmd::self()->commandCompletionObject()); + setAutoDeleteCompletionObject( false ); + + m_hideTimer = new QTimer(this); + m_hideTimer->setSingleShot(true); + connect(m_hideTimer, SIGNAL(timeout()), this, SLOT(hideLineEdit())); + + // make sure the timer is stopped when the user switches views. if not, focus will be given to the + // wrong view when KateViewBar::hideCurrentBarWidget() is called after 4 seconds. (the timer is + // used for showing things like "Success" for four seconds after the user has used the kate + // command line) + connect(m_view, SIGNAL(focusOut(KTextEditor::View*)), m_hideTimer, SLOT(stop())); +} + +void KateCmdLineEdit::hideEvent(QHideEvent *e) +{ + Q_UNUSED(e); +} + + +QString KateCmdLineEdit::helptext( const QPoint & ) const +{ + QString beg = "
Help: "; + QString mid = "
"; + QString end = "
"; + + QString t = text(); + QRegExp re( "\\s*help\\s+(.*)" ); + if ( re.indexIn( t ) > -1 ) + { + QString s; + // get help for command + QString name = re.cap( 1 ); + if ( name == "list" ) + { + return beg + i18n("Available Commands") + mid + + KateCmd::self()->commandList().join(" ") + + i18n("

For help on individual commands, do 'help <command>'

") + + end; + } + else if ( ! name.isEmpty() ) + { + KTextEditor::Command *cmd = KateCmd::self()->queryCommand( name ); + if ( cmd ) + { + if ( cmd->help( m_view, name, s ) ) + return beg + name + mid + s + end; + else + return beg + name + mid + i18n("No help for '%1'", name ) + end; + } + else + return beg + mid + i18n("No such command %1", name) + end; + } + } + + return beg + mid + i18n( + "

This is the Katepart command line.
" + "Syntax: command [ arguments ]
" + "For a list of available commands, enter help list
" + "For help for individual commands, enter help <command>

") + + end; +} + + + +bool KateCmdLineEdit::event(QEvent *e) { + if (e->type() == QEvent::QueryWhatsThis) { + setWhatsThis(helptext(QPoint())); + e->accept(); + return true; + } + return KLineEdit::event(e); +} + + +/** + * Parse the text as a command. + * + * The following is a simple PEG grammar for the syntax of the command. + * (A PEG grammar is like a BNF grammar, except that "/" stands for + * ordered choice: only the first matching rule is used. In other words, + * the parsing is short-circuited in the manner of the "or" operator in + * programming languages, and so the grammar is unambiguous.) + * + * Text <- Range? Command + * / Position + * Range <- Position ("," Position)? + * / "%" + * Position <- Base Offset? + * Base <- Line + * / LastLine + * / ThisLine + * / Mark + * Offset <- [+-] Base + * Line <- [0-9]+ + * LastLine <- "$" + * ThisLine <- "." + * Mark <- "'" [a-z] + */ + +void KateCmdLineEdit::slotReturnPressed ( const QString& text ) +{ + if (text.isEmpty()) return; + // silently ignore leading space characters and colon characters (for vi-heads) + uint n = 0; + const uint textlen=text.length(); + while( (n=textlen) return; + + QString cmd = text.mid( n ); + + // Built in help: if the command starts with "help", [try to] show some help + if ( cmd.startsWith( QLatin1String("help") ) ) + { + QWhatsThis::showText(mapToGlobal(QPoint(0,0)), helptext( QPoint() ) ); + clear(); + KateCmd::self()->appendHistory( cmd ); + m_histpos = KateCmd::self()->historyLength(); + m_oldText.clear(); + return; + } + + if (cmd.length () > 0) + { + KTextEditor::Command *p = KateCmd::self()->queryCommand (cmd); + + m_oldText = cmd; + m_msgMode = true; + + // the following commands changes the focus themselves, so bar should be hidden before execution. + if (QRegExp("buffer|b|new|vnew|bp|bprev|bn|bnext|bf|bfirst|bl|blast|edit|e").exactMatch(cmd.split(" ").at(0))) { + emit hideRequested(); + } + + // we got a range and a valid command, but the command does not inherit the RangeCommand + // extension. bail out. + if (p) + { + QString msg; + const bool cmdresult = p->exec(m_view, cmd, msg); + + if (msg.length() > 0) { + if (msg.contains('\n')) { + // multiline error, use widget with more space + QWhatsThis::showText(mapToGlobal(QPoint(0,0)), msg); + } else { + setText(msg); + } + } else if (!cmdresult) { + setText (i18n ("Command \"%1\" failed.", cmd)); + } + } + else + { + setText (i18n ("No such command: \"%1\"", cmd)); + } + KNotification::beep(); + } + + // clean up + if (completionObject() != KateCmd::self()->commandCompletionObject()) + { + KCompletion *c = completionObject(); + setCompletionObject(KateCmd::self()->commandCompletionObject()); + delete c; + } + m_command = 0; + m_cmdend = 0; + + // the following commands change the focus themselves + if (!QRegExp("buffer|b|new|vnew|bp|bprev|bn|bnext|bf|bfirst|bl|blast|edit|e").exactMatch(cmd.split(" ").at(0))) { + m_view->setFocus (); + } + + if (isVisible()) { + m_hideTimer->start(4000); + } +} + +void KateCmdLineEdit::hideLineEdit () // unless i have focus ;) +{ + if ( ! hasFocus() ) { + emit hideRequested(); + } +} + +void KateCmdLineEdit::focusInEvent ( QFocusEvent *ev ) +{ + if (m_msgMode) + { + m_msgMode = false; + setText (m_oldText); + selectAll(); + } + + KLineEdit::focusInEvent (ev); +} + +void KateCmdLineEdit::keyPressEvent( QKeyEvent *ev ) +{ + if (ev->key() == Qt::Key_Escape || + (ev->key() == Qt::Key_BracketLeft && ev->modifiers() == Qt::ControlModifier)) + { + m_view->setFocus (); + hideLineEdit(); + clear(); + } + else if ( ev->key() == Qt::Key_Up ) + fromHistory( true ); + else if ( ev->key() == Qt::Key_Down ) + fromHistory( false ); + + uint cursorpos = cursorPosition(); + KLineEdit::keyPressEvent (ev); + + // during typing, let us see if we have a valid command + if ( ! m_cmdend || cursorpos <= m_cmdend ) + { + QChar c; + if ( ! ev->text().isEmpty() ) + c = ev->text()[0]; + + if ( ! m_cmdend && ! c.isNull() ) // we have no command, so lets see if we got one + { + if ( ! c.isLetterOrNumber() && c != '-' && c != '_' ) + { + m_command = KateCmd::self()->queryCommand( text().trimmed() ); + if ( m_command ) + { + //kDebug(13025)<<"keypress in commandline: We have a command! "<queryCommand( text().trimmed() ); + if ( m_command ) + { + //kDebug(13025)<<"keypress in commandline: We have a command! "<commandCompletionObject()) + { + KCompletion *c = completionObject(); + setCompletionObject(KateCmd::self()->commandCompletionObject()); + delete c; + } + + m_cmdend = 0; + } + } + + // if we got a command, check if it wants to do something. + if ( m_command ) + { + //kDebug(13025)<<"Checking for CommandExtension.."; + KTextEditor::CommandExtension *ce = dynamic_cast(m_command); + if ( ce ) + { + KCompletion *cmpl = ce->completionObject( m_view, text().left( m_cmdend ).trimmed() ); + if ( cmpl ) + { + // We need to prepend the current command name + flag string + // when completion is done + //kDebug(13025)<<"keypress in commandline: Setting completion object!"; + + setCompletionObject( cmpl ); + } + } + } + } + else if ( m_command )// check if we should call the commands processText() + { + KTextEditor::CommandExtension *ce = dynamic_cast( m_command ); + if ( ce && ce->wantsToProcessText( text().left( m_cmdend ).trimmed() ) + && ! ( ev->text().isNull() || ev->text().isEmpty() ) ) + ce->processText( m_view, text() ); + } +} + +void KateCmdLineEdit::fromHistory( bool up ) +{ + if ( ! KateCmd::self()->historyLength() ) + return; + + QString s; + + if ( up ) + { + if ( m_histpos > 0 ) + { + m_histpos--; + s = KateCmd::self()->fromHistory( m_histpos ); + } + } + else + { + if ( m_histpos < ( KateCmd::self()->historyLength() - 1 ) ) + { + m_histpos++; + s = KateCmd::self()->fromHistory( m_histpos ); + } + else + { + m_histpos = KateCmd::self()->historyLength(); + setText( m_oldText ); + } + } + if ( ! s.isEmpty() ) + { + // Select the argument part of the command, so that it is easy to overwrite + setText( s ); + static QRegExp reCmd = QRegExp(".*[\\w\\-]+(?:[^a-zA-Z0-9_-]|:\\w+)(.*)"); + if ( reCmd.indexIn( text() ) == 0 ) + setSelection( text().length() - reCmd.cap(1).length(), reCmd.cap(1).length() ); + } +} +//END KateCmdLineEdit + +//BEGIN KateIconBorder +using namespace KTextEditor; + +KateIconBorder::KateIconBorder ( KateViewInternal* internalView, QWidget *parent ) + : QWidget(parent) + , m_view( internalView->m_view ) + , m_doc( internalView->doc() ) + , m_viewInternal( internalView ) + , m_iconBorderOn( false ) + , m_lineNumbersOn( false ) + , m_viRelLineNumbersOn( false ) + , m_updateViRelLineNumbers ( false ) + , m_foldingMarkersOn( false ) + , m_dynWrapIndicatorsOn( false ) + , m_annotationBorderOn( false ) + , m_dynWrapIndicators( 0 ) + , m_cachedLNWidth( 0 ) + , m_maxCharWidth( 0.0 ) + , iconPaneWidth (16) + , m_annotationBorderWidth (6) + , m_foldingRange(0) + , m_nextHighlightBlock(-2) + , m_currentBlockLine(-1) +{ + setAttribute( Qt::WA_StaticContents ); + setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Minimum ); + setMouseTracking(true); + m_doc->setMarkDescription( MarkInterface::markType01, i18n("Bookmark") ); + m_doc->setMarkPixmap( MarkInterface::markType01, KIcon("bookmarks").pixmap(16, 16) ); + + updateFont(); + + m_delayFoldingHlTimer.setSingleShot(true); + m_delayFoldingHlTimer.setInterval(150); + connect(&m_delayFoldingHlTimer, SIGNAL(timeout()), this, SLOT(showBlock())); +} + +KateIconBorder::~KateIconBorder() +{ + delete m_foldingRange; + m_foldingRange = 0; +} + +void KateIconBorder::setIconBorderOn( bool enable ) +{ + if( enable == m_iconBorderOn ) + return; + + m_iconBorderOn = enable; + + updateGeometry(); + + QTimer::singleShot( 0, this, SLOT(update()) ); +} + +void KateIconBorder::setAnnotationBorderOn( bool enable ) +{ + if( enable == m_annotationBorderOn ) + return; + + m_annotationBorderOn = enable; + + emit m_view->annotationBorderVisibilityChanged(m_view, enable); + + updateGeometry(); + + QTimer::singleShot( 0, this, SLOT(update()) ); +} + +void KateIconBorder::removeAnnotationHovering() +{ + // remove hovering if it's still there + if (m_annotationBorderOn && !m_hoveredAnnotationGroupIdentifier.isEmpty()) + { + m_hoveredAnnotationGroupIdentifier.clear(); + hideAnnotationTooltip(); + QTimer::singleShot( 0, this, SLOT(update()) ); + } +} + +void KateIconBorder::setLineNumbersOn( bool enable ) +{ + if( enable == m_lineNumbersOn ) + return; + + m_lineNumbersOn = enable; + m_dynWrapIndicatorsOn = (m_dynWrapIndicators == 1) ? enable : m_dynWrapIndicators; + + updateGeometry(); + + QTimer::singleShot( 0, this, SLOT(update()) ); +} + +void KateIconBorder::setViRelLineNumbersOn(bool enable) +{ + if( enable == m_viRelLineNumbersOn ) + return; + + m_viRelLineNumbersOn = enable; + /* + * We don't have to touch the m_dynWrapIndicatorsOn because + * we already got it right from the m_lineNumbersOn + */ + updateGeometry(); + + QTimer::singleShot( 0, this, SLOT(update()) ); +} + +void KateIconBorder::updateViRelLineNumbers() +{ + if (m_viRelLineNumbersOn) { + m_updateViRelLineNumbers = true; + update(); + } +} + +void KateIconBorder::setDynWrapIndicators( int state ) +{ + if (state == m_dynWrapIndicators ) + return; + + m_dynWrapIndicators = state; + m_dynWrapIndicatorsOn = (state == 1) ? m_lineNumbersOn : state; + + updateGeometry (); + + QTimer::singleShot( 0, this, SLOT(update()) ); +} + +void KateIconBorder::setFoldingMarkersOn( bool enable ) +{ + if( enable == m_foldingMarkersOn ) + return; + + m_foldingMarkersOn = enable; + + updateGeometry(); + + QTimer::singleShot( 0, this, SLOT(update()) ); +} + +QSize KateIconBorder::sizeHint() const +{ + int w = 0; + + if (m_iconBorderOn) + w += iconPaneWidth + 2; + + if (m_annotationBorderOn) + { + w += m_annotationBorderWidth + 2; + } + + if (m_lineNumbersOn || (m_view->dynWordWrap() && m_dynWrapIndicatorsOn)) { + w += lineNumberWidth() + 2; + } + + if (m_foldingMarkersOn) + w += iconPaneWidth; + + // space for the line modification system border + if (m_view->config()->lineModification()) { + w += 3; + } + + // two pixel space + w += 2; + + return QSize( w, 0 ); +} + +// This function (re)calculates the maximum width of any of the digit characters (0 -> 9) +// for graceful handling of variable-width fonts as the linenumber font. +void KateIconBorder::updateFont() +{ + const QFontMetricsF &fm = m_view->renderer()->config()->fontMetrics(); + m_maxCharWidth = 0.0; + // Loop to determine the widest numeric character in the current font. + // 48 is ascii '0' + for (int i = 48; i < 58; i++) { + const qreal charWidth = ceil(fm.width( QChar(i) )); + m_maxCharWidth = qMax(m_maxCharWidth, charWidth); + } + + // the icon pane scales with the font... + iconPaneWidth = fm.height(); + + updateGeometry(); + + QTimer::singleShot( 0, this, SLOT(update()) ); +} + +int KateIconBorder::lineNumberWidth() const +{ + // width = (number of digits + 1) * char width + const int digits = (int) ceil(log10((double)(m_view->doc()->lines() + 1))); + int width = m_lineNumbersOn ? (int)ceil((digits + 1) * m_maxCharWidth) : 0; + + if (m_view->dynWordWrap() && m_dynWrapIndicatorsOn) { + // HACK: 16 == style().scrollBarExtent().width() + width = qMax(16 + 4, width); + + if (m_cachedLNWidth != width || m_oldBackgroundColor != m_view->renderer()->config()->iconBarColor()) { + int w = 16;// HACK: 16 == style().scrollBarExtent().width() style().scrollBarExtent().width(); + int h = m_view->renderer()->lineHeight(); + + QSize newSize(w, h); + if ((m_arrow.size() != newSize || m_oldBackgroundColor != m_view->renderer()->config()->iconBarColor()) && !newSize.isEmpty()) { + m_arrow = QPixmap(newSize); + + QPainter p(&m_arrow); + p.fillRect( 0, 0, w, h, m_view->renderer()->config()->iconBarColor() ); + + h = m_view->renderer()->config()->fontMetrics().ascent(); + + p.setPen(m_view->renderer()->config()->lineNumberColor()); + + QPainterPath path; + path.moveTo(w/2, h/2); + path.lineTo(w/2, 0); + path.lineTo(w/4, h/4); + path.lineTo(0, 0); + path.lineTo(0, h/2); + path.lineTo(w/2, h-1); + path.lineTo(w*3/4, h-1); + path.lineTo(w-1, h*3/4); + path.lineTo(w*3/4, h/2); + path.lineTo(0, h/2); + p.drawPath(path); + } + } + } + + return width; +} + +void KateIconBorder::paintEvent(QPaintEvent* e) +{ + paintBorder(e->rect().x(), e->rect().y(), e->rect().width(), e->rect().height()); +} + +static void paintTriangle (QPainter &painter, QColor c, int xOffset, int yOffset, int width, int height, bool open) +{ + painter.setRenderHint(QPainter::Antialiasing); + + qreal size = qMin (width, height); + + if ( KColorUtils::luma( c ) > 0.25 ) + c = KColorUtils::darken( c ); + else + c = KColorUtils::shade( c, 0.1 ); + + QPen pen; + pen.setJoinStyle (Qt::RoundJoin); + pen.setColor (c); + pen.setWidthF (1.5); + painter.setPen ( pen ); + + painter.setBrush (c); + + // let some border, if possible + size *= 0.6; + + qreal halfSize = size / 2; + qreal halfSizeP = halfSize * 0.6; + QPointF middle (xOffset + (qreal)width / 2, yOffset + (qreal)height / 2); + + if (open) + { + QPointF points[3] = { middle+QPointF(-halfSize, -halfSizeP), middle+QPointF(halfSize, -halfSizeP), middle+QPointF(0, halfSizeP) }; + painter.drawConvexPolygon(points, 3); + } + else + { + QPointF points[3] = { middle+QPointF(-halfSizeP, -halfSize), middle+QPointF(-halfSizeP, halfSize), middle+QPointF(halfSizeP, 0) }; + painter.drawConvexPolygon(points, 3); + } + + painter.setRenderHint(QPainter::Antialiasing, false); +} + +void KateIconBorder::paintBorder (int /*x*/, int y, int /*width*/, int height) +{ + uint h = m_view->renderer()->lineHeight(); + uint startz = (y / h); + uint endz = startz + 1 + (height / h); + uint lineRangesSize = m_viewInternal->cache()->viewCacheLineCount(); + + // center the folding boxes + int m_px = (h - 11) / 2; + if (m_px < 0) + m_px = 0; + + int lnWidth( 0 ); + if ( m_lineNumbersOn || (m_view->dynWordWrap() && m_dynWrapIndicatorsOn) ) // avoid calculating unless needed ;-) + { + lnWidth = lineNumberWidth(); + if ( lnWidth != m_cachedLNWidth || m_oldBackgroundColor != m_view->renderer()->config()->iconBarColor() ) + { + // we went from n0 ->n9 lines or vice verca + // this causes an extra updateGeometry() first time the line numbers + // are displayed, but sizeHint() is supposed to be const so we can't set + // the cached value there. + m_cachedLNWidth = lnWidth; + m_oldBackgroundColor = m_view->renderer()->config()->iconBarColor(); + updateGeometry(); + update (); + return; + } + } + + int w( this->width() ); // sane value/calc only once + + QPainter p ( this ); + p.setRenderHints (QPainter::TextAntialiasing); + p.setFont ( m_view->renderer()->config()->font() ); // for line numbers + + KTextEditor::AnnotationModel *model = m_view->annotationModel() ? + m_view->annotationModel() : m_doc->annotationModel(); + + for (uint z=startz; z <= endz; z++) + { + int y = h * z; + int realLine = -1; + + if (z < lineRangesSize) + realLine = m_viewInternal->cache()->viewLine(z).line(); + + int lnX = 0; + + p.fillRect( 0, y, w-5, h, m_view->renderer()->config()->iconBarColor() ); + p.fillRect( w-5, y, 5, h, m_view->renderer()->config()->backgroundColor() ); + + // icon pane + if( m_iconBorderOn ) + { + p.setPen ( m_view->renderer()->config()->separatorColor() ); + p.setBrush ( m_view->renderer()->config()->separatorColor() ); + p.drawLine(lnX+iconPaneWidth+1, y, lnX+iconPaneWidth+1, y+h); + + if( (realLine > -1) && (m_viewInternal->cache()->viewLine(z).startCol() == 0) ) + { + uint mrk ( m_doc->mark( realLine ) ); // call only once + + if ( mrk ) + { + for( uint bit = 0; bit < 32; bit++ ) + { + MarkInterface::MarkTypes markType = (MarkInterface::MarkTypes)(1<markPixmap( markType )); + + if (!px_mark.isNull() && h > 0 && iconPaneWidth > 0) + { + if (iconPaneWidth < px_mark.width() || h < (uint)px_mark.height()) + px_mark = px_mark.scaled (iconPaneWidth, h, Qt::KeepAspectRatio); + + // center the mark pixmap + int x_px = (iconPaneWidth - px_mark.width()) / 2; + if (x_px < 0) + x_px = 0; + + int y_px = (h - px_mark.height()) / 2; + if (y_px < 0) + y_px = 0; + + p.drawPixmap( lnX+x_px, y+y_px, px_mark); + } + } + } + } + } + + lnX += iconPaneWidth + 2; + } + + // annotation information + if( m_annotationBorderOn ) + { + // Draw a border line between annotations and the line numbers + p.setPen ( m_view->renderer()->config()->lineNumberColor() ); + p.setBrush ( m_view->renderer()->config()->lineNumberColor() ); + + int borderWidth = m_annotationBorderWidth; + p.drawLine(lnX+borderWidth+1, y, lnX+borderWidth+1, y+h); + + if( (realLine > -1) && model ) + { + // Fetch data from the model + QVariant text = model->data( realLine, Qt::DisplayRole ); + QVariant foreground = model->data( realLine, Qt::ForegroundRole ); + QVariant background = model->data( realLine, Qt::BackgroundRole ); + // Fill the background + if( background.isValid() ) + { + p.fillRect( lnX, y, borderWidth + 1, h, background.value() ); + } + // Set the pen for drawing the foreground + if( foreground.isValid() ) + { + p.setPen( foreground.value() ); + } + + // Draw a border around all adjacent entries that have the same text as the currently hovered one + const QVariant identifier = model->data( realLine, (Qt::ItemDataRole) KTextEditor::AnnotationModel::GroupIdentifierRole ); + if( m_hoveredAnnotationGroupIdentifier == identifier.toString() ) + { + p.drawLine( lnX, y, lnX, y+h ); + p.drawLine( lnX+borderWidth, y, lnX+borderWidth, y+h ); + + QVariant beforeText = model->data( realLine-1, Qt::DisplayRole ); + QVariant afterText = model->data( realLine+1, Qt::DisplayRole ); + if( ((beforeText.isValid() && beforeText.canConvert() + && text.isValid() && text.canConvert() + && beforeText.toString() != text.toString()) || realLine == 0) + && m_viewInternal->cache()->viewLine(z).viewLine() == 0) + { + p.drawLine( lnX+1, y, lnX+borderWidth, y ); + } + + if( ((afterText.isValid() && afterText.canConvert() + && text.isValid() && text.canConvert() + && afterText.toString() != text.toString()) + || realLine == m_view->doc()->lines() - 1) + && m_viewInternal->cache()->viewLine(z).viewLine() == m_viewInternal->cache()->viewLineCount(realLine)-1) + { + p.drawLine( lnX+1, y+h-1, lnX+borderWidth, y+h-1 ); + } + } + if( foreground.isValid() ) + { + QPen pen = p.pen(); + pen.setWidth( 1 ); + p.setPen( pen ); + } + + // Now draw the normal text + if( text.isValid() && text.canConvert() && (m_viewInternal->cache()->viewLine(z).startCol() == 0) ) + { + p.drawText( lnX+3, y, borderWidth-3, h, Qt::AlignLeft|Qt::AlignVCenter, text.toString() ); + } + } + + // adjust current X position and reset the pen and brush + lnX += borderWidth + 2; + } + + // line number + if( m_lineNumbersOn || (m_view->dynWordWrap() && m_dynWrapIndicatorsOn) ) + { + p.setPen ( m_view->renderer()->config()->lineNumberColor() ); + p.setBrush ( m_view->renderer()->config()->lineNumberColor() ); + + if (realLine > -1) { + if (m_viewInternal->cache()->viewLine(z).startCol() == 0) { + if (m_lineNumbersOn) { + p.drawText( lnX + m_maxCharWidth / 2, y, lnWidth - m_maxCharWidth, h, + Qt::TextDontClip|Qt::AlignRight|Qt::AlignVCenter, QString("%1").arg( realLine + 1 ) ); + } + } else if (m_view->dynWordWrap() && m_dynWrapIndicatorsOn) { + p.drawPixmap(lnX + lnWidth - m_arrow.width() - 2, y, m_arrow); + } + } + + lnX += lnWidth + 2; + } + + // folding markers + if( m_foldingMarkersOn) + { + // first icon border background + p.fillRect(lnX, y, iconPaneWidth, h, m_view->renderer()->config()->iconBarColor()); + + // possible additional folding highlighting + if ((realLine >= 0) && m_foldingRange && m_foldingRange->overlapsLine (realLine)) { + p.save(); + + // use linear gradient as brush + QLinearGradient g(lnX, y, lnX + iconPaneWidth, y); + const QColor foldingColor(m_view->renderer()->config()->foldingColor()); + g.setColorAt(0, foldingColor); + g.setColorAt(0.3, foldingColor.lighter(110)); + g.setColorAt(1, foldingColor); + p.setBrush(g); + p.setPen(foldingColor); + + p.setClipRect(lnX, y, iconPaneWidth, h); + p.setRenderHint(QPainter::Antialiasing); + + const qreal r = 4.0; + if (m_foldingRange->start().line() == realLine && + m_viewInternal->cache()->viewLine(z).viewLine() == 0) + { + p.drawRect(lnX, y, iconPaneWidth, h + r); + } else if (m_foldingRange->end().line() == realLine && + m_viewInternal->cache()->viewLine(z).viewLine() == m_viewInternal->cache()->viewLineCount(realLine)-1) + { + p.drawRect(lnX, y-r, iconPaneWidth, h + r); + } else { + p.drawRect(lnX, y-r, iconPaneWidth, h+2*r); + } + p.restore(); + } + + if ((realLine >= 0) && (m_viewInternal->cache()->viewLine(z).startCol() == 0)) + { + QVector > startingRanges = m_view->textFolding().foldingRangesStartingOnLine (realLine); + bool anyFolded = false; + for (int i = 0; i < startingRanges.size(); ++i) + if (startingRanges[i].second & Kate::TextFolding::Folded) + anyFolded = true; + + Kate::TextLine tl = m_doc->kateTextLine(realLine); + + if (!startingRanges.isEmpty() || tl->markedAsFoldingStart()) + { + if (anyFolded) + { + paintTriangle (p, m_view->renderer()->config()->foldingColor(), lnX, y, iconPaneWidth, h, false); + } + else + { + paintTriangle (p, m_view->renderer()->config()->foldingColor(), lnX, y, iconPaneWidth, h, true); + } + } + } + + lnX += iconPaneWidth; + } + + // modified line system + if (m_view->config()->lineModification() && + realLine > -1 && !m_doc->url().isEmpty()) + { + // one pixel space + ++lnX; + + Kate::TextLine tl = m_doc->plainKateTextLine(realLine); + if (tl->markedAsModified()) { + p.fillRect(lnX, y, 3, h, m_view->renderer()->config()->modifiedLineColor()); + } + if (tl->markedAsSavedOnDisk()) { + p.fillRect(lnX, y, 3, h, m_view->renderer()->config()->savedLineColor()); + } + } + } +} + +KateIconBorder::BorderArea KateIconBorder::positionToArea( const QPoint& p ) const +{ + // see KateIconBorder::sizeHint() for pixel spacings + int x = 0; + if( m_iconBorderOn ) { + x += iconPaneWidth; + if( p.x() <= x ) + return IconBorder; + x += 2; + } + if( this->m_annotationBorderOn ) { + x += m_annotationBorderWidth; + if( p.x() <= x ) + return AnnotationBorder; + x += 2; + } + if( m_lineNumbersOn || m_dynWrapIndicators ) { + x += lineNumberWidth(); + if( p.x() <= x ) + return LineNumbers; + x += 2; + } + if( m_foldingMarkersOn ) { + x += iconPaneWidth; + if( p.x() <= x ) + return FoldingMarkers; + } + if (m_view->config()->lineModification()) { + x += 3 + 2; + if( p.x() <= x ) + return ModificationBorder; + } + return None; +} + +void KateIconBorder::mousePressEvent( QMouseEvent* e ) +{ + const KateTextLayout& t = m_viewInternal->yToKateTextLayout(e->y()); + if (t.isValid()) { + m_lastClickedLine = t.line(); + if ( positionToArea( e->pos() ) != IconBorder && positionToArea( e->pos() ) != AnnotationBorder ) + { + QMouseEvent forward( QEvent::MouseButtonPress, + QPoint( 0, e->y() ), e->button(), e->buttons(),e->modifiers() ); + m_viewInternal->mousePressEvent( &forward ); + } + return e->accept(); + } + + QWidget::mousePressEvent(e); +} + +void KateIconBorder::showDelayedBlock(int line) +{ + // save the line over which the mouse hovers + // either we start the timer for delay, or we show the block immediately + // if the moving range already exists + m_nextHighlightBlock = line; + if (!m_foldingRange) { + if (!m_delayFoldingHlTimer.isActive()) { + m_delayFoldingHlTimer.start(); + } + } else { + showBlock(); + } +} + +void KateIconBorder::showBlock() +{ + if (m_nextHighlightBlock == m_currentBlockLine) return; + m_currentBlockLine = m_nextHighlightBlock; + + /** + * compute to which folding range we belong + * FIXME: optimize + perhaps have some better threshold or use timers! + */ + KTextEditor::Range newRange = KTextEditor::Range::invalid(); + for (int line = m_currentBlockLine; line >= qMax(0, m_currentBlockLine-1024); --line) { + /** + * try if we have folding range from that line, should be fast per call + */ + KTextEditor::Range foldingRange = m_doc->buffer().computeFoldingRangeForStartLine (line); + if (!foldingRange.isValid()) + continue; + + /** + * does the range reach us? + */ + if (foldingRange.overlapsLine (m_currentBlockLine)) { + newRange = foldingRange; + break; + } + } + + if (newRange.isValid() && m_foldingRange && *m_foldingRange == newRange) { + // new range equals the old one, nothing to do. + return; + } else { // the ranges differ, delete the old, if it exists + delete m_foldingRange; + m_foldingRange = 0; + } + + if (newRange.isValid()) { + kDebug(13025) << "new folding hl-range:" << newRange; + m_foldingRange = m_doc->newMovingRange(newRange, KTextEditor::MovingRange::ExpandRight); + KTextEditor::Attribute::Ptr attr(new KTextEditor::Attribute()); + + /** + * create highlighting color with alpha for the range! + */ + QColor result = m_view->renderer()->config()->foldingColor(); + result.setAlphaF (0.5); + attr->setBackground(QBrush( result )); + + m_foldingRange->setView (m_view); + // use z depth defined in moving ranges interface + m_foldingRange->setZDepth (-100.0); + m_foldingRange->setAttribute(attr); + } + + /** + * repaint + */ + repaint (); +} + +void KateIconBorder::hideBlock() +{ + if (m_delayFoldingHlTimer.isActive()) { + m_delayFoldingHlTimer.stop(); + } + + m_nextHighlightBlock = -2; + m_currentBlockLine = -1; + delete m_foldingRange; + m_foldingRange = 0; +} + +void KateIconBorder::leaveEvent(QEvent *event) +{ + hideBlock(); + removeAnnotationHovering(); + + QWidget::leaveEvent(event); +} + +void KateIconBorder::mouseMoveEvent( QMouseEvent* e ) +{ + const KateTextLayout& t = m_viewInternal->yToKateTextLayout(e->y()); + if (t.isValid()) { + if ( positionToArea( e->pos() ) == FoldingMarkers) showDelayedBlock(t.line()); + else hideBlock(); + if ( positionToArea( e->pos() ) == AnnotationBorder ) + { + KTextEditor::AnnotationModel *model = m_view->annotationModel() ? + m_view->annotationModel() : m_doc->annotationModel(); + if (model) + { + m_hoveredAnnotationGroupIdentifier = model->data( t.line(), + (Qt::ItemDataRole) KTextEditor::AnnotationModel::GroupIdentifierRole ).toString(); + showAnnotationTooltip( t.line(), e->globalPos() ); + QTimer::singleShot( 0, this, SLOT(update()) ); + } + } + else + { + if( positionToArea( e->pos() ) == IconBorder ) + m_doc->requestMarkTooltip( t.line(), e->globalPos() ); + + m_hoveredAnnotationGroupIdentifier.clear(); + hideAnnotationTooltip(); + QTimer::singleShot( 0, this, SLOT(update()) ); + } + if ( positionToArea( e->pos() ) != IconBorder ) + { + QPoint p = m_viewInternal->mapFromGlobal( e->globalPos() ); + QMouseEvent forward( QEvent::MouseMove, p, e->button(), e->buttons(), e->modifiers() ); + m_viewInternal->mouseMoveEvent( &forward ); + } + } + else + { + // remove hovering if it's still there + removeAnnotationHovering(); + } + + QWidget::mouseMoveEvent(e); +} + +void KateIconBorder::mouseReleaseEvent( QMouseEvent* e ) +{ + int cursorOnLine = m_viewInternal->yToKateTextLayout(e->y()).line(); + + if (cursorOnLine == m_lastClickedLine && + cursorOnLine <= m_doc->lastLine() ) + { + BorderArea area = positionToArea( e->pos() ); + if( area == IconBorder) { + if (e->button() == Qt::LeftButton) { + if( !m_doc->handleMarkClick(cursorOnLine) ) { + KateViewConfig *config = m_view->config(); + if( m_doc->editableMarks() & config->defaultMarkType() ) { + if( m_doc->mark( cursorOnLine ) & config->defaultMarkType() ) + m_doc->removeMark( cursorOnLine, config->defaultMarkType() ); + else + m_doc->addMark( cursorOnLine, config->defaultMarkType() ); + } else if (config->allowMarkMenu()) { + showMarkMenu( cursorOnLine, QCursor::pos() ); + } + } + } + else + if (e->button() == Qt::RightButton) { + showMarkMenu( cursorOnLine, QCursor::pos() ); + } + } + + if ( area == FoldingMarkers) { + // ask the folding info for this line, if any folds are around! + QVector > startingRanges = m_view->textFolding().foldingRangesStartingOnLine (cursorOnLine); + bool anyFolded = false; + for (int i = 0; i < startingRanges.size(); ++i) + if (startingRanges[i].second & Kate::TextFolding::Folded) + anyFolded = true; + + // fold or unfold all ranges, remember if any action happened! + bool actionDone = false; + for (int i = 0; i < startingRanges.size(); ++i) + actionDone = (anyFolded ? m_view->textFolding().unfoldRange (startingRanges[i].first) : m_view->textFolding().foldRange (startingRanges[i].first)) || actionDone; + + // if no action done, try to fold it, create non-persistent folded range, if possible! + if (!actionDone) { + // either use the fold for this line or the range that is highlighted ATM if any! + KTextEditor::Range foldingRange = m_view->doc()->buffer().computeFoldingRangeForStartLine (cursorOnLine); + if (!foldingRange.isValid() && m_foldingRange) + foldingRange = m_foldingRange->toRange (); + m_view->textFolding().newFoldingRange (foldingRange, Kate::TextFolding::Folded); + } + } + + if ( area == AnnotationBorder ) { + if( e->button() == Qt::LeftButton && KGlobalSettings::singleClick() ) { + emit m_view->annotationActivated( m_view, cursorOnLine ); + } else if ( e->button() == Qt::RightButton ) { + showAnnotationMenu( cursorOnLine, e->globalPos() ); + } + } + } + + QMouseEvent forward( QEvent::MouseButtonRelease, + QPoint( 0, e->y() ), e->button(), e->buttons(),e->modifiers() ); + m_viewInternal->mouseReleaseEvent( &forward ); +} + +void KateIconBorder::mouseDoubleClickEvent( QMouseEvent* e ) +{ + int cursorOnLine = m_viewInternal->yToKateTextLayout(e->y()).line(); + + if (cursorOnLine == m_lastClickedLine && + cursorOnLine <= m_doc->lastLine() ) + { + BorderArea area = positionToArea( e->pos() ); + if( area == AnnotationBorder && !KGlobalSettings::singleClick() ) { + emit m_view->annotationActivated( m_view, cursorOnLine ); + } + } + QMouseEvent forward( QEvent::MouseButtonDblClick, + QPoint( 0, e->y() ), e->button(), e->buttons(),e->modifiers() ); + m_viewInternal->mouseDoubleClickEvent( &forward ); +} + +void KateIconBorder::wheelEvent(QWheelEvent *e) +{ + QCoreApplication::sendEvent(m_viewInternal, e); +} + +void KateIconBorder::showMarkMenu( uint line, const QPoint& pos ) +{ + if( m_doc->handleMarkContextMenu( line, pos ) ) + return; + + if( !m_view->config()->allowMarkMenu() ) + return; + + KMenu markMenu; + KMenu selectDefaultMark; + + QVector vec( 33 ); + int i=1; + + for( uint bit = 0; bit < 32; bit++ ) { + MarkInterface::MarkTypes markType = (MarkInterface::MarkTypes)(1<editableMarks() & markType) ) + continue; + + QAction *mA; + QAction *dMA; + if( !m_doc->markDescription( markType ).isEmpty() ) { + mA=markMenu.addAction( m_doc->markDescription( markType )); + dMA=selectDefaultMark.addAction( m_doc->markDescription( markType )); + } else { + mA=markMenu.addAction( i18n("Mark Type %1", bit + 1 )); + dMA=selectDefaultMark.addAction( i18n("Mark Type %1", bit + 1 )); + } + mA->setData(i); + mA->setCheckable(true); + dMA->setData(i+100); + dMA->setCheckable(true); + if( m_doc->mark( line ) & markType ) + mA->setChecked(true ); + + if( markType & KateViewConfig::global()->defaultMarkType() ) + dMA->setChecked(true ); + + vec[i++] = markType; + } + + if( markMenu.actions().count() == 0 ) + return; + + if( markMenu.actions().count() > 1 ) + markMenu.addAction( i18n("Set Default Mark Type" ))->setMenu(&selectDefaultMark); + + QAction *rA = markMenu.exec( pos ); + if( !rA ) + return; + int result=rA->data().toInt(); + if ( result > 100) + { + KateViewConfig::global()->setDefaultMarkType (vec[result-100]); + // flush config, otherwise it isn't necessarily done + KConfigGroup cg(KGlobal::config(), "Kate View Defaults"); + KateViewConfig::global()->writeConfig(cg); + } + else + { + MarkInterface::MarkTypes markType = (MarkInterface::MarkTypes) vec[result]; + if( m_doc->mark( line ) & markType ) { + m_doc->removeMark( line, markType ); + } else { + m_doc->addMark( line, markType ); + } + } +} + +void KateIconBorder::showAnnotationTooltip( int line, const QPoint& pos ) +{ + KTextEditor::AnnotationModel *model = m_view->annotationModel() ? + m_view->annotationModel() : m_doc->annotationModel(); + + if( model ) + { + QVariant data = model->data( line, Qt::ToolTipRole ); + QString tip = data.toString(); + if (!tip.isEmpty()) + QToolTip::showText( pos, data.toString(), this ); + } +} + + +int KateIconBorder::annotationLineWidth( int line ) +{ + KTextEditor::AnnotationModel *model = m_view->annotationModel() ? + m_view->annotationModel() : m_doc->annotationModel(); + + if( model ) + { + QVariant data = model->data( line, Qt::DisplayRole ); + return data.toString().length() * m_maxCharWidth + 8; + } + return 8; +} + +void KateIconBorder::updateAnnotationLine( int line ) +{ + if( annotationLineWidth(line) > m_annotationBorderWidth ) + { + m_annotationBorderWidth = annotationLineWidth(line); + updateGeometry(); + + QTimer::singleShot( 0, this, SLOT(update()) ); + } +} + +void KateIconBorder::showAnnotationMenu( int line, const QPoint& pos) +{ + KMenu menu; + QAction a(i18n("Disable Annotation Bar"), &menu); + a.setIcon(KIcon("dialog-close")); + menu.addAction(&a); + emit m_view->annotationContextMenuAboutToShow( m_view, &menu, line ); + if (menu.exec(pos) == &a) + m_view->setAnnotationBorderVisible(false); +} + +void KateIconBorder::hideAnnotationTooltip() +{ + QToolTip::hideText(); +} + +void KateIconBorder::updateAnnotationBorderWidth( ) +{ + m_annotationBorderWidth = 6; + KTextEditor::AnnotationModel *model = m_view->annotationModel() ? + m_view->annotationModel() : m_doc->annotationModel(); + + if( model ) { + for( int i = 0; i < m_view->doc()->lines(); i++ ) { + int curwidth = annotationLineWidth( i ); + if( curwidth > m_annotationBorderWidth ) + m_annotationBorderWidth = curwidth; + } + } + + updateGeometry(); + + QTimer::singleShot( 0, this, SLOT(update()) ); +} + + + +void KateIconBorder::annotationModelChanged( KTextEditor::AnnotationModel * oldmodel, KTextEditor::AnnotationModel * newmodel ) +{ + if( oldmodel ) + { + oldmodel->disconnect( this ); + } + if( newmodel ) + { + connect( newmodel, SIGNAL(reset()), this, SLOT(updateAnnotationBorderWidth()) ); + connect( newmodel, SIGNAL(lineChanged(int)), this, SLOT(updateAnnotationLine(int)) ); + } + updateAnnotationBorderWidth(); +} + +//END KateIconBorder + +//BEGIN KateViewEncodingAction +// Acording to http://www.iana.org/assignments/ianacharset-mib +// the default/unknown mib value is 2. +#define MIB_DEFAULT 2 + +class KateViewEncodingAction::Private +{ + public: + Private(KateViewEncodingAction *parent) + : q(parent), + currentSubAction(0) + { + } + + void init(); + + void _k_subActionTriggered(QAction*); + + KateViewEncodingAction *q; + QAction *currentSubAction; +}; + +bool lessThanAction(KSelectAction *a, KSelectAction *b) +{ + return a->text() < b->text(); +} + +void KateViewEncodingAction::Private::init() +{ + QList actions; + + q->setToolBarMode(MenuMode); + + int i; + foreach(const QStringList &encodingsForScript, KGlobal::charsets()->encodingsByScript()) + { + KSelectAction* tmp = new KSelectAction(encodingsForScript.at(0),q); + + for (i=1; iaddAction(encodingsForScript.at(i)); + } + q->connect(tmp,SIGNAL(triggered(QAction*)),q,SLOT(_k_subActionTriggered(QAction*))); + //tmp->setCheckable(true); + actions << tmp; + } + qSort(actions.begin(), actions.end(), lessThanAction); + foreach (KSelectAction *action, actions) + q->addAction(action); +} + +void KateViewEncodingAction::Private::_k_subActionTriggered(QAction *action) +{ + if (currentSubAction==action) + return; + currentSubAction=action; + bool ok = false; + int mib = q->mibForName(action->iconText(), &ok); + if (ok) + { + emit q->KSelectAction::triggered(action->text()); + emit q->triggered(q->codecForMib(mib)); + } +} + +KateViewEncodingAction::KateViewEncodingAction(KateDocument *_doc, KateView *_view, const QString& text, QObject *parent) +: KSelectAction(text, parent), doc(_doc), view (_view), d(new Private(this)) +{ + d->init(); + + connect(menu(),SIGNAL(aboutToShow()),this,SLOT(slotAboutToShow())); + connect(this,SIGNAL(triggered(QString)),this,SLOT(setEncoding(QString))); +} + +KateViewEncodingAction::~KateViewEncodingAction() +{ + delete d; +} + +void KateViewEncodingAction::slotAboutToShow() +{ + setCurrentCodec(doc->config()->encoding()); +} + +void KateViewEncodingAction::setEncoding (const QString &e) +{ + doc->userSetEncodingForNextReload (); + doc->setEncoding(e); + view->reloadFile(); +} + +int KateViewEncodingAction::mibForName(const QString &codecName, bool *ok) const +{ + // FIXME logic is good but code is ugly + + bool success = false; + int mib = MIB_DEFAULT; + KCharsets *charsets = KGlobal::charsets(); + + QTextCodec *codec = charsets->codecForName(codecName, success); + if (!success) + { + // Maybe we got a description name instead + codec = charsets->codecForName(charsets->encodingForName(codecName), success); + } + + if (codec) + mib = codec->mibEnum(); + + if (ok) + *ok = success; + + if (success) + return mib; + + kWarning() << "Invalid codec name: " << codecName; + return MIB_DEFAULT; +} + +QTextCodec *KateViewEncodingAction::codecForMib(int mib) const +{ + if (mib == MIB_DEFAULT) + { + // FIXME offer to change the default codec + return QTextCodec::codecForLocale(); + } + else + return QTextCodec::codecForMib(mib); +} + +QTextCodec *KateViewEncodingAction::currentCodec() const +{ + return codecForMib(currentCodecMib()); +} + +bool KateViewEncodingAction::setCurrentCodec( QTextCodec *codec ) +{ + disconnect(this,SIGNAL(triggered(QString)),this,SLOT(setEncoding(QString))); + + int i,j; + for (i=0;imenu()) + { + for (j=0;jmenu()->actions().size();++j) + { + if (!j && !actions().at(i)->menu()->actions().at(j)->data().isNull()) + continue; + if (actions().at(i)->menu()->actions().at(j)->isSeparator()) + continue; + + if (codec==KGlobal::charsets()->codecForName(actions().at(i)->menu()->actions().at(j)->text())) + { + d->currentSubAction=actions().at(i)->menu()->actions().at(j); + d->currentSubAction->setChecked(true); + } + else + actions().at(i)->menu()->actions().at(j)->setChecked (false); + } + } + } + + connect(this,SIGNAL(triggered(QString)),this,SLOT(setEncoding(QString))); + return true; +} + +QString KateViewEncodingAction::currentCodecName() const +{ + return d->currentSubAction->text(); +} + +bool KateViewEncodingAction::setCurrentCodec( const QString &codecName ) +{ + return setCurrentCodec(KGlobal::charsets()->codecForName(codecName)); +} + +int KateViewEncodingAction::currentCodecMib() const +{ + return mibForName(currentCodecName()); +} + +bool KateViewEncodingAction::setCurrentCodec( int mib ) +{ + return setCurrentCodec(codecForMib(mib)); +} +//END KateViewEncodingAction + +//BEGIN KateViewBar related classes + +KateViewBarWidget::KateViewBarWidget (bool addCloseButton, QWidget *parent) + : QWidget (parent) + , m_viewBar(0) +{ + QHBoxLayout *layout = new QHBoxLayout (this); + + // NOTE: Here be cosmetics. + layout->setMargin(0); + + // hide button + if (addCloseButton) { + QToolButton *hideButton = new QToolButton(this); + hideButton->setAutoRaise(true); + hideButton->setIcon(KIcon("dialog-close")); + connect(hideButton, SIGNAL(clicked()), SIGNAL(hideMe())); + layout->addWidget(hideButton); + layout->setAlignment( hideButton, Qt::AlignLeft|Qt::AlignTop ); + } + + // widget to be used as parent for the real content + m_centralWidget = new QWidget (this); + layout->addWidget(m_centralWidget); + + setLayout(layout); + setFocusProxy(m_centralWidget); +} + + +KateViewBar::KateViewBar (bool external,KTextEditor::ViewBarContainer::Position pos,QWidget *parent, KateView *view) + : QWidget (parent), m_external(external), m_pos(pos),m_view (view), m_permanentBarWidget(0) + +{ + m_layout = new QVBoxLayout(this); + m_stack = new QStackedWidget(this); + m_layout->addWidget(m_stack); + m_layout->setMargin(0); + + m_stack->hide(); + hide (); +} + +void KateViewBar::addBarWidget (KateViewBarWidget *newBarWidget) +{ + if (hasBarWidget(newBarWidget)) { + kDebug(13025) << "this bar widget is already added"; + return; + } + // add new widget, invisible... + newBarWidget->hide(); + m_stack->addWidget (newBarWidget); + newBarWidget->setAssociatedViewBar(this); + connect(newBarWidget, SIGNAL(hideMe()), SLOT(hideCurrentBarWidget())); + + kDebug(13025)<<"add barwidget " << newBarWidget; +} + +void KateViewBar::removeBarWidget (KateViewBarWidget *barWidget) +{ + if (hasBarWidget(barWidget)) { + m_stack->removeWidget(barWidget); + barWidget->setAssociatedViewBar(0); + barWidget->hide(); + disconnect(barWidget, 0, this, 0); + } +} + +void KateViewBar::addPermanentBarWidget (KateViewBarWidget *barWidget) +{ + // remove old widget from layout (if any) + if (m_permanentBarWidget) { + m_permanentBarWidget->hide(); + m_layout->removeWidget(m_permanentBarWidget); + } + + m_layout->addWidget(barWidget, 0, Qt::AlignBottom); + m_permanentBarWidget = barWidget; + m_permanentBarWidget->show(); + + setViewBarVisible(true); +} + +void KateViewBar::removePermanentBarWidget (KateViewBarWidget *barWidget) +{ + if (m_permanentBarWidget != barWidget) { + kDebug(13025) << "no such permanent widget exists in bar"; + return; + } + + if (!m_permanentBarWidget) + return; + + m_permanentBarWidget->hide(); + m_layout->removeWidget(m_permanentBarWidget); + m_permanentBarWidget = 0; + + if (!m_stack->isVisible()) { + setViewBarVisible(false); + } +} + +bool KateViewBar::hasPermanentWidget (KateViewBarWidget *barWidget ) const +{ + return (m_permanentBarWidget == barWidget); +} + +void KateViewBar::showBarWidget (KateViewBarWidget *barWidget) +{ + Q_ASSERT(barWidget != 0); + + if (barWidget != qobject_cast(m_stack->currentWidget())) { + hideCurrentBarWidget(); + } + + // raise correct widget + m_stack->setCurrentWidget (barWidget); + barWidget->show(); + barWidget->setFocus(Qt::ShortcutFocusReason); + m_stack->show(); + + // if we have any permanent widget, bar is always visible, + // no need to show it + if (!m_permanentBarWidget) { + setViewBarVisible(true); + } +} + +bool KateViewBar::hasBarWidget(KateViewBarWidget* barWidget) const +{ + return m_stack->indexOf(barWidget) != -1; +} + +void KateViewBar::hideCurrentBarWidget () +{ + KateViewBarWidget *current=qobject_cast(m_stack->currentWidget()); + if (current) { + current->closed(); + } + m_stack->hide(); + + // if we have any permanent widget, bar is always visible, + // no need to hide it + if (!m_permanentBarWidget) { + setViewBarVisible(false); + } + + m_view->setFocus(); + kDebug(13025)<<"hide barwidget"; +} + +void KateViewBar::setViewBarVisible (bool visible) +{ + if (m_external) { + KTextEditor::ViewBarContainer *viewBarContainer=qobject_cast( KateGlobal::self()->container() ); + if (viewBarContainer) { + if (visible) { + viewBarContainer->showViewBarForView(m_view,m_pos); + } else { + viewBarContainer->hideViewBarForView(m_view,m_pos); + } + } + } else { + setVisible (visible); + } +} + +void KateViewBar::keyPressEvent(QKeyEvent* event) +{ + if (event->key() == Qt::Key_Escape) { + hideCurrentBarWidget(); + return; + } + QWidget::keyPressEvent(event); + +} + +void KateViewBar::hideEvent(QHideEvent* event) +{ + Q_UNUSED(event); +// if (!event->spontaneous()) +// m_view->setFocus(); +} + +//END KateViewBar related classes + +KatePasteMenu::KatePasteMenu (const QString& text, KateView *view) + : KActionMenu(text, view) + , m_view (view) +{ + connect(menu(),SIGNAL(aboutToShow()),this,SLOT(slotAboutToShow())); +} + +void KatePasteMenu::slotAboutToShow() +{ + menu()->clear (); + + /** + * insert complete paste history + */ + int i = 0; + Q_FOREACH (const QString &text, KateGlobal::self()->clipboardHistory()) { + /** + * get text for the menu ;) + */ + QString leftPart = (text.size() > 48) ? (text.left(48) + "...") : text; + QAction *a=menu()->addAction (leftPart.replace ("\n", " "), this, SLOT(paste())); + a->setData(i++); + } +} + +void KatePasteMenu::paste () +{ + if (!sender()) + return; + + QAction *action = qobject_cast(sender()); + if (!action) + return; + + // get index + int i = action->data().toInt(); + if (i >= KateGlobal::self()->clipboardHistory().size()) + return; + + // paste + m_view->paste (&KateGlobal::self()->clipboardHistory()[i]); +} + + +#include "moc_kateviewhelpers.cpp" + +// kate: space-indent on; indent-width 2; replace-tabs on; + diff --git a/kate/part/view/kateviewhelpers.h b/kate/part/view/kateviewhelpers.h new file mode 100644 index 00000000..e076a891 --- /dev/null +++ b/kate/part/view/kateviewhelpers.h @@ -0,0 +1,497 @@ +/* This file is part of the KDE libraries + Copyright (C) 2002 John Firebaugh + Copyright (C) 2001 Anders Lund + Copyright (C) 2001 Christoph Cullmann + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef __KATE_VIEW_HELPERS_H__ +#define __KATE_VIEW_HELPERS_H__ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "katepartinterfaces_export.h" +#include "katetextline.h" +#include "katedocument.h" + +class KateDocument; +class KateView; +class KateViewInternal; + +#define MAXFOLDINGCOLORS 16 + +class KateLineInfo; + +namespace KTextEditor { + class Command; + class AnnotationModel; + class MovingRange; +} + +#include +#include + +/** + * This class is required because QScrollBar's sliderMoved() signal is + * really supposed to be a sliderDragged() signal... so this way we can capture + * MMB slider moves as well + * + * Also, it adds some useful indicators on the scrollbar. + */ +class KateScrollBar : public QScrollBar +{ + Q_OBJECT + + public: + KateScrollBar(Qt::Orientation orientation, class KateViewInternal *parent); + QSize sizeHint() const; + + inline bool showMarks() { return m_showMarks; } + inline void setShowMarks(bool b) { m_showMarks = b; update(); } + + inline bool showMiniMap() { return m_showMiniMap; } + void setShowMiniMap(bool b); + + inline bool miniMapAll() { return m_miniMapAll; } + inline void setMiniMapAll(bool b) { m_miniMapAll = b; updateGeometry(); update(); } + + inline bool miniMapWidth() { return m_miniMapWidth; } + inline void setMiniMapWidth(int width) { m_miniMapWidth = width; updateGeometry(); update(); } + + inline void queuePixmapUpdate() { m_updateTimer.start(); } + +Q_SIGNALS: + void sliderMMBMoved(int value); + + protected: + virtual void mousePressEvent(QMouseEvent* e); + virtual void mouseReleaseEvent(QMouseEvent* e); + virtual void mouseMoveEvent (QMouseEvent* e); + virtual void paintEvent(QPaintEvent *e); + virtual void resizeEvent(QResizeEvent *); + virtual void styleChange(QStyle &oldStyle); + virtual void sliderChange ( SliderChange change ); + + protected Q_SLOTS: + void sliderMaybeMoved(int value); + void marksChanged(); + + public Q_SLOTS: + void updatePixmap(); + + private: + void redrawMarks(); + void recomputeMarksPositions(); + + void miniMapPaintEvent(QPaintEvent *e); + void normalPaintEvent(QPaintEvent *e); + + int minimapYToStdY(int y); + + const QColor charColor(const QVector &attributes, int &attributeIndex, + const QList &decorations, + const QColor &defaultColor, int x, QChar ch); + + bool m_middleMouseDown; + bool m_leftMouseDown; + + KateView *m_view; + KateDocument *m_doc; + class KateViewInternal *m_viewInternal; + + QHash m_lines; + + bool m_showMarks; + bool m_showMiniMap; + bool m_miniMapAll; + int m_miniMapWidth; + + QPixmap m_pixmap; + int m_grooveHeight; + QRect m_stdGroveRect; + QRect m_mapGroveRect; + QRect m_stdSliderRect; + QRect m_mapSliderRect; + QTimer m_updateTimer; + QPoint m_toolTipPos; + + // lists of lines added/removed recently to avoid scrollbar flickering + QHash m_linesAdded; +}; + +class KateIconBorder : public QWidget +{ + Q_OBJECT + + public: + KateIconBorder( KateViewInternal* internalView, QWidget *parent ); + virtual ~KateIconBorder(); + // VERY IMPORTANT ;) + virtual QSize sizeHint() const; + + void updateFont(); + int lineNumberWidth() const; + + void setIconBorderOn( bool enable ); + void setLineNumbersOn( bool enable ); + void setViRelLineNumbersOn( bool enable ); + void setAnnotationBorderOn( bool enable ); + void setDynWrapIndicators(int state ); + int dynWrapIndicators() const { return m_dynWrapIndicators; } + bool dynWrapIndicatorsOn() const { return m_dynWrapIndicatorsOn; } + void setFoldingMarkersOn( bool enable ); + void toggleIconBorder() { setIconBorderOn( !iconBorderOn() ); } + void toggleLineNumbers() { setLineNumbersOn( !lineNumbersOn() ); } + void toggleFoldingMarkers() { setFoldingMarkersOn( !foldingMarkersOn() ); } + inline bool iconBorderOn() const { return m_iconBorderOn; } + inline bool lineNumbersOn() const { return m_lineNumbersOn; } + inline bool viRelNumbersOn() const { return m_viRelLineNumbersOn; } + inline bool foldingMarkersOn() const { return m_foldingMarkersOn; } + inline bool annotationBorderOn() const { return m_annotationBorderOn; } + + void updateViRelLineNumbers(); + + enum BorderArea { None, LineNumbers, IconBorder, FoldingMarkers, AnnotationBorder, ModificationBorder }; + BorderArea positionToArea( const QPoint& ) const; + + public Q_SLOTS: + void updateAnnotationBorderWidth(); + void updateAnnotationLine( int line ); + void annotationModelChanged( KTextEditor::AnnotationModel* oldmodel, KTextEditor::AnnotationModel* newmodel ); + + private: + void paintEvent( QPaintEvent* ); + void paintBorder (int x, int y, int width, int height); + + void mousePressEvent( QMouseEvent* ); + void mouseMoveEvent( QMouseEvent* ); + void mouseReleaseEvent( QMouseEvent* ); + void mouseDoubleClickEvent( QMouseEvent* ); + void leaveEvent(QEvent *event); + void wheelEvent(QWheelEvent *e); + + void showMarkMenu( uint line, const QPoint& pos ); + + void showAnnotationTooltip( int line, const QPoint& pos ); + void hideAnnotationTooltip(); + void removeAnnotationHovering(); + void showAnnotationMenu( int line, const QPoint& pos); + int annotationLineWidth( int line ); + + KateView *m_view; + KateDocument *m_doc; + KateViewInternal *m_viewInternal; + + bool m_iconBorderOn:1; + bool m_lineNumbersOn:1; + bool m_viRelLineNumbersOn:1; + bool m_updateViRelLineNumbers:1; + bool m_foldingMarkersOn:1; + bool m_dynWrapIndicatorsOn:1; + bool m_annotationBorderOn:1; + int m_dynWrapIndicators; + + int m_lastClickedLine; + + int m_cachedLNWidth; + + qreal m_maxCharWidth; + int iconPaneWidth; + int m_annotationBorderWidth; + + mutable QPixmap m_arrow; + mutable QColor m_oldBackgroundColor; + + + KTextEditor::MovingRange *m_foldingRange; + int m_nextHighlightBlock; + int m_currentBlockLine; + QTimer m_delayFoldingHlTimer; + void showDelayedBlock(int line); + void hideBlock(); + + private Q_SLOTS: + void showBlock(); + + private: + QString m_hoveredAnnotationGroupIdentifier; + + void initializeFoldingColors(); +}; + +class KateViewEncodingAction: public KSelectAction +{ + Q_OBJECT + + Q_PROPERTY(QString codecName READ currentCodecName WRITE setCurrentCodec) + Q_PROPERTY(int codecMib READ currentCodecMib) + + public: + KateViewEncodingAction(KateDocument *_doc, KateView *_view, const QString& text, QObject *parent); + + ~KateViewEncodingAction(); + + int mibForName(const QString &codecName, bool *ok = 0) const; + QTextCodec *codecForMib(int mib) const; + + QTextCodec *currentCodec() const; + bool setCurrentCodec(QTextCodec *codec); + + QString currentCodecName() const; + bool setCurrentCodec(const QString &codecName); + + int currentCodecMib() const; + bool setCurrentCodec(int mib); + + Q_SIGNALS: + /** + * Specific (proper) codec was selected + */ + void triggered(QTextCodec *codec); + + private: + KateDocument* doc; + KateView *view; + class Private; + Private* const d; + Q_PRIVATE_SLOT( d, void _k_subActionTriggered(QAction*) ) + + private Q_SLOTS: + void setEncoding (const QString &e); + void slotAboutToShow(); +}; + +class KateViewBar; + +class KateViewBarWidget : public QWidget +{ + Q_OBJECT + friend class KateViewBar; + + public: + explicit KateViewBarWidget (bool addCloseButton, QWidget* parent = 0); + + virtual void closed() {} + + /// returns the currently associated KateViewBar and 0, if it is not associated + KateViewBar* viewBar() { return m_viewBar; } + + protected: + /** + * @return widget that should be used to add controls to bar widget + */ + QWidget *centralWidget() { return m_centralWidget; } + + signals: + void hideMe(); + + // for friend class KateViewBar + private: + void setAssociatedViewBar(KateViewBar* bar) { m_viewBar = bar;} + + private: + QWidget *m_centralWidget; + KateViewBar* m_viewBar; // 0-pointer, if not added to a view bar +}; + +class KateViewBar : public QWidget +{ + Q_OBJECT + + public: + KateViewBar (bool external, + KTextEditor::ViewBarContainer::Position pos, + QWidget *parent, + KateView *view); + + + /** + * Adds a widget to this viewbar. + * Widget is initially invisible, you should call showBarWidget, to show it. + * Several widgets can be added to the bar, but only one can be visible + */ + void addBarWidget (KateViewBarWidget *newBarWidget); + + /** + * Removes a widget from this viewbar. + * Removing a widget makes sense if it takes a lot of space vertically, + * because we use a QStackedWidget to maintain the same height for all + * widgets in the viewbar. + */ + void removeBarWidget (KateViewBarWidget *barWidget); + + /** + * @return if viewbar has widget @p barWidget + */ + bool hasBarWidget(KateViewBarWidget* barWidget) const; + + /** + * Shows barWidget that was previously added with addBarWidget. + * @see hideCurrentBarWidget + */ + void showBarWidget (KateViewBarWidget *barWidget); + + /** + * Adds widget that will be always shown in the viewbar. + * After adding permanent widget viewbar is immediately shown. + * ViewBar with permanent widget won't hide itself + * until permanent widget is removed. + * OTOH showing/hiding regular barWidgets will work as usual + * (they will be shown above permanent widget) + * + * If permanent widget already exists, new one replaces old one + * Old widget is not deleted, caller can do it if it wishes + */ + void addPermanentBarWidget (KateViewBarWidget *barWidget); + + /** + * Removes permanent bar widget from viewbar. + * If no other viewbar widgets are shown, viewbar gets hidden. + * + * barWidget is not deleted, caller must do it if it wishes + */ + void removePermanentBarWidget (KateViewBarWidget *barWidget); + + /** + * @return if viewbar has permanent widget @p barWidget + */ + bool hasPermanentWidget (KateViewBarWidget *barWidget) const; + + public Q_SLOTS: + /** + * Hides currently shown bar widget + */ + void hideCurrentBarWidget(); + + protected: + virtual void keyPressEvent(QKeyEvent* event); + virtual void hideEvent(QHideEvent* event); + + private: + /** + * Shows or hides whole viewbar + */ + void setViewBarVisible(bool visible); + + bool m_external; + KTextEditor::ViewBarContainer::Position m_pos; + + private: + KateView *m_view; + QStackedWidget *m_stack; + KateViewBarWidget *m_permanentBarWidget; + QVBoxLayout *m_layout; +}; + +class KATEPARTINTERFACES_EXPORT KateCommandLineBar : public KateViewBarWidget +{ + Q_OBJECT + + public: + explicit KateCommandLineBar(KateView *view, QWidget *parent = 0); + ~KateCommandLineBar(); + + void setText(const QString &text, bool selected = true); + void execute(const QString &text); + + public Q_SLOTS: + void showHelpPage(); + + private: + class KateCmdLineEdit *m_lineEdit; +}; + +class KateCmdLineEdit : public KLineEdit +{ + Q_OBJECT + + public: + KateCmdLineEdit (KateCommandLineBar *bar, KateView *view); + virtual bool event(QEvent *e); + + void hideEvent (QHideEvent *e); + + signals: + void hideRequested(); + + public Q_SLOTS: + void slotReturnPressed ( const QString& cmd ); + + private Q_SLOTS: + void hideLineEdit(); + + protected: + void focusInEvent ( QFocusEvent *ev ); + void keyPressEvent( QKeyEvent *ev ); + + private: + /** + * Parse an expression denoting a position in the document. + * Return the position as an integer. + * Examples of expressions are "10" (the 10th line), + * "$" (the last line), "." (the current line), + * "'a" (the mark 'a), "/foo/" (a forwards search for "foo"), + * and "?bar?" (a backwards search for "bar"). + * @param string the expression to parse + * @return the position, an integer + */ + int calculatePosition( QString string ); + void fromHistory( bool up ); + QString helptext( const QPoint & ) const; + + KateView *m_view; + KateCommandLineBar *m_bar; + bool m_msgMode; + QString m_oldText; + uint m_histpos; ///< position in the history + uint m_cmdend; ///< the point where a command ends in the text, if we have a valid one. + KTextEditor::Command *m_command; ///< For completing flags/args and interactiveness + class KateCmdLnWhatsThis *m_help; + + QTimer *m_hideTimer; +}; + +class KatePasteMenu : public KActionMenu +{ + Q_OBJECT + + public: + KatePasteMenu (const QString& text, KateView *view); + + private: + KateView *m_view; + + private Q_SLOTS: + void slotAboutToShow(); + void paste (); +}; + +#endif + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/view/kateviewinternal.cpp b/kate/part/view/kateviewinternal.cpp new file mode 100644 index 00000000..7bb6d5f0 --- /dev/null +++ b/kate/part/view/kateviewinternal.cpp @@ -0,0 +1,3620 @@ +/* This file is part of the KDE libraries + Copyright (C) 2002 John Firebaugh + Copyright (C) 2002 Joseph Wenninger + Copyright (C) 2002,2003 Christoph Cullmann + Copyright (C) 2002-2007 Hamish Rodda + Copyright (C) 2003 Anakim Border + Copyright (C) 2007 Mirko Stocker + Copyright (C) 2007 Matthew Woehlke + Copyright (C) 2008 Erlend Hamberg + + Based on: + KWriteView : Copyright (C) 1999 Jochen Wilhelmy + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "kateviewinternal.h" +#include "moc_kateviewinternal.cpp" + +#include "kateview.h" +#include "kateviewhelpers.h" +#include "katehighlight.h" +#include "katebuffer.h" +#include "katerenderer.h" +#include "kateconfig.h" +#include "katelayoutcache.h" +#include "katecompletionwidget.h" +#include "katesearchbar.h" +#include "spellcheck/spellingmenu.h" +#include "katetextanimation.h" +#include "katemessagewidget.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +static const bool debugPainting = false; + +KateViewInternal::KateViewInternal(KateView *view) + : QWidget (view) + , editSessionNumber (0) + , editIsRunning (false) + , m_view (view) + , m_cursor(doc()->buffer(), KTextEditor::Cursor(0, 0), Kate::TextCursor::MoveOnInsert) + , m_mouse() + , m_possibleTripleClick (false) + , m_completionItemExpanded (false) + , m_bm(doc()->newMovingRange(KTextEditor::Range::invalid(), KTextEditor::MovingRange::DoNotExpand)) + , m_bmStart(doc()->newMovingRange(KTextEditor::Range::invalid(), KTextEditor::MovingRange::DoNotExpand)) + , m_bmEnd(doc()->newMovingRange(KTextEditor::Range::invalid(), KTextEditor::MovingRange::DoNotExpand)) + , m_bmLastFlashPos(doc()->newMovingCursor(KTextEditor::Cursor::invalid())) + , m_dummy (0) + + // stay on cursor will avoid that the view scroll around on press return at beginning + , m_startPos (doc()->buffer(), KTextEditor::Cursor (0, 0), Kate::TextCursor::StayOnInsert) + + , m_visibleLineCount(0) + , m_madeVisible(false) + , m_shiftKeyPressed (false) + , m_autoCenterLines(0) + , m_minLinesVisible(0) + , m_selChangedByUser (false) + , m_selectAnchor (-1, -1) + , m_selectionMode( Default ) + , m_layoutCache(new KateLayoutCache(renderer(), this)) + , m_preserveX(false) + , m_preservedX(0) + , m_cachedMaxStartPos(-1, -1) + , m_dragScrollTimer(this) + , m_scrollTimer (this) + , m_cursorTimer (this) + , m_textHintTimer (this) + , m_textHintEnabled(false) + , m_textHintPos(-1, -1) + , m_imPreeditRange(0) +{ + setMinimumSize (0,0); + setAttribute(Qt::WA_OpaquePaintEvent); +#ifndef QT_KATIE + setAttribute(Qt::WA_InputMethodEnabled); +#endif + + // invalidate m_selectionCached.start(), or keyb selection is screwed initially + m_selectionCached = KTextEditor::Range::invalid(); + + // bracket markers are only for this view and should not be printed + m_bm->setView (m_view); + m_bmStart->setView (m_view); + m_bmEnd->setView (m_view); + m_bm->setAttributeOnlyForViews (true); + m_bmStart->setAttributeOnlyForViews (true); + m_bmEnd->setAttributeOnlyForViews (true); + + // use z depth defined in moving ranges interface + m_bm->setZDepth (-1000.0); + m_bmStart->setZDepth (-1000.0); + m_bmEnd->setZDepth (-1000.0); + + // update mark attributes + updateBracketMarkAttributes(); + + // + // scrollbar for lines + // + m_lineScroll = new KateScrollBar(Qt::Vertical, this); + m_lineScroll->show(); + m_lineScroll->setTracking (true); + m_lineScroll->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Expanding ); + + // Hijack the line scroller's controls, so we can scroll nicely for word-wrap + connect(m_lineScroll, SIGNAL(actionTriggered(int)), SLOT(scrollAction(int))); + connect(m_lineScroll, SIGNAL(sliderMoved(int)), SLOT(scrollLines(int))); + connect(m_lineScroll, SIGNAL(sliderMMBMoved(int)), SLOT(scrollLines(int))); + connect(m_lineScroll, SIGNAL(valueChanged(int)), SLOT(scrollLines(int))); + + // catch wheel events, completing the hijack + m_lineScroll->installEventFilter(this); + + // + // scrollbar for columns + // + m_columnScroll = new QScrollBar(Qt::Horizontal,m_view); + + if (m_view->dynWordWrap()) + m_columnScroll->hide(); + else + m_columnScroll->show(); + + m_columnScroll->setTracking(true); + m_startX = 0; + + connect(m_columnScroll, SIGNAL(valueChanged(int)), SLOT(scrollColumns(int))); + + // bottom corner box + m_dummy = new QWidget(m_view); + m_dummy->setFixedSize(m_lineScroll->width(), m_columnScroll->sizeHint().height()); + m_dummy->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed ); + + if (m_view->dynWordWrap()) + m_dummy->hide(); + else + m_dummy->show(); + + cache()->setWrap(m_view->dynWordWrap()); + + // + // iconborder ;) + // + m_leftBorder = new KateIconBorder( this, m_view ); + m_leftBorder->show (); + + // update view if folding ranges change + connect( &m_view->textFolding(), SIGNAL(foldingRangesChanged()), SLOT(slotRegionVisibilityChanged())); + + m_displayCursor.setPosition(0, 0); + + setAcceptDrops( true ); + + // event filter + installEventFilter(this); + + // set initial cursor + m_mouseCursor = Qt::IBeamCursor; + setCursor(m_mouseCursor); + + // call mouseMoveEvent also if no mouse button is pressed + setMouseTracking(true); + + m_dragInfo.state = diNone; + + // timers + connect( &m_dragScrollTimer, SIGNAL(timeout()), + this, SLOT(doDragScroll()) ); + + connect( &m_scrollTimer, SIGNAL(timeout()), + this, SLOT(scrollTimeout()) ); + + connect( &m_cursorTimer, SIGNAL(timeout()), + this, SLOT(cursorTimeout()) ); + + connect( &m_textHintTimer, SIGNAL(timeout()), + this, SLOT(textHintTimeout()) ); + + // selection changed to set anchor + connect( m_view, SIGNAL(selectionChanged(KTextEditor::View*)), + this, SLOT(viewSelectionChanged()) ); + + // update is called in KateView, after construction and layout is over + // but before any other kateviewinternal call +} + +KateViewInternal::~KateViewInternal () +{ + // delete text animation object here, otherwise it updates the view in its destructor + if (m_textAnimation) { + delete m_textAnimation; + } + + // kill preedit ranges + delete m_imPreeditRange; + qDeleteAll (m_imPreeditRangeChildren); + + // delete bracket markers + delete m_bm; + delete m_bmStart; + delete m_bmEnd; +} + +void KateViewInternal::prepareForDynWrapChange() +{ + // Which is the current view line? + m_wrapChangeViewLine = cache()->displayViewLine(m_displayCursor, true); +} + +void KateViewInternal::dynWrapChanged() +{ + m_dummy->setFixedSize(m_lineScroll->width(), m_columnScroll->sizeHint().height()); + if (m_view->dynWordWrap()) + { + m_columnScroll->hide(); + m_dummy->hide(); + + } + else + { + // column scrollbar + bottom corner box + m_columnScroll->show(); + m_dummy->show(); + } + + cache()->setWrap(m_view->dynWordWrap()); + updateView(); + + if (m_view->dynWordWrap()) + scrollColumns(0); + + // Determine where the cursor should be to get the cursor on the same view line + if (m_wrapChangeViewLine != -1) { + KTextEditor::Cursor newStart = viewLineOffset(m_displayCursor, -m_wrapChangeViewLine); + makeVisible(newStart, newStart.column(), true); + + } else { + update(); + } +} + +KTextEditor::Cursor KateViewInternal::endPos() const +{ + // Hrm, no lines laid out at all?? + if (!cache()->viewCacheLineCount()) + return KTextEditor::Cursor(); + + for (int i = qMin(linesDisplayed() - 1, cache()->viewCacheLineCount() - 1); i >= 0; i--) { + const KateTextLayout& thisLine = cache()->viewLine(i); + + if (thisLine.line() == -1) continue; + + if (thisLine.virtualLine() >= m_view->textFolding().visibleLines()) { + // Cache is too out of date + return KTextEditor::Cursor(m_view->textFolding().visibleLines() - 1, doc()->lineLength(m_view->textFolding().visibleLineToLine(m_view->textFolding().visibleLines() - 1))); + } + + return KTextEditor::Cursor(thisLine.virtualLine(), thisLine.wrap() ? thisLine.endCol() - 1 : thisLine.endCol()); + } + + return KTextEditor::Cursor(); +} + +int KateViewInternal::endLine() const +{ + return endPos().line(); +} + +KateTextLayout KateViewInternal::yToKateTextLayout(int y) const +{ + if (y < 0 || y > size().height()) + return KateTextLayout::invalid(); + + int range = y / renderer()->lineHeight(); + + // lineRanges is always bigger than 0, after the initial updateView call + if (range >= 0 && range < cache()->viewCacheLineCount()) + return cache()->viewLine(range); + + return KateTextLayout::invalid(); +} + +int KateViewInternal::lineToY(int viewLine) const +{ + return (viewLine-startLine()) * renderer()->lineHeight(); +} + +void KateViewInternal::slotIncFontSizes() +{ + renderer()->increaseFontSizes(); + update(); +} + +void KateViewInternal::slotDecFontSizes() +{ + renderer()->decreaseFontSizes(); + update(); +} + +/** + * Line is the real line number to scroll to. + */ +void KateViewInternal::scrollLines ( int line ) +{ + KTextEditor::Cursor newPos(line, 0); + scrollPos(newPos); +} + +// This can scroll less than one true line +void KateViewInternal::scrollViewLines(int offset) +{ + KTextEditor::Cursor c = viewLineOffset(startPos(), offset); + scrollPos(c); + + bool blocked = m_lineScroll->blockSignals(true); + m_lineScroll->setValue(startLine()); + m_lineScroll->blockSignals(blocked); +} + +void KateViewInternal::scrollAction( int action ) +{ + switch (action) { + case QAbstractSlider::SliderSingleStepAdd: + scrollNextLine(); + break; + + case QAbstractSlider::SliderSingleStepSub: + scrollPrevLine(); + break; + + case QAbstractSlider::SliderPageStepAdd: + scrollNextPage(); + break; + + case QAbstractSlider::SliderPageStepSub: + scrollPrevPage(); + break; + + case QAbstractSlider::SliderToMinimum: + top_home(); + break; + + case QAbstractSlider::SliderToMaximum: + bottom_end(); + break; + } +} + +void KateViewInternal::scrollNextPage() +{ + scrollViewLines(qMax( linesDisplayed() - 1, 0 )); +} + +void KateViewInternal::scrollPrevPage() +{ + scrollViewLines(-qMax( linesDisplayed() - 1, 0 )); +} + +void KateViewInternal::scrollPrevLine() +{ + scrollViewLines(-1); +} + +void KateViewInternal::scrollNextLine() +{ + scrollViewLines(1); +} + +KTextEditor::Cursor KateViewInternal::maxStartPos(bool changed) +{ + cache()->setAcceptDirtyLayouts(true); + + if (m_cachedMaxStartPos.line() == -1 || changed) + { + KTextEditor::Cursor end(m_view->textFolding().visibleLines() - 1, doc()->lineLength(m_view->textFolding().visibleLineToLine(m_view->textFolding().visibleLines() - 1))); + + if (m_view->config()->scrollPastEnd()) + m_cachedMaxStartPos = viewLineOffset(end, -m_minLinesVisible); + else + m_cachedMaxStartPos = viewLineOffset(end, -(linesDisplayed() - 1)); + } + + cache()->setAcceptDirtyLayouts(false); + + return m_cachedMaxStartPos; +} + +// c is a virtual cursor +void KateViewInternal::scrollPos(KTextEditor::Cursor& c, bool force, bool calledExternally) +{ + if (!force && ((!m_view->dynWordWrap() && c.line() == startLine()) || c == startPos())) + return; + + if (c.line() < 0) + c.setLine(0); + + KTextEditor::Cursor limit = maxStartPos(); + if (c > limit) { + c = limit; + + // Re-check we're not just scrolling to the same place + if (!force && ((!m_view->dynWordWrap() && c.line() == startLine()) || c == startPos())) + return; + } + + int viewLinesScrolled = 0; + + // only calculate if this is really used and useful, could be wrong here, please recheck + // for larger scrolls this makes 2-4 seconds difference on my xeon with dyn. word wrap on + // try to get it really working ;) + bool viewLinesScrolledUsable = !force + && (c.line() >= startLine() - linesDisplayed() - 1) + && (c.line() <= endLine() + linesDisplayed() + 1); + + if (viewLinesScrolledUsable) { + viewLinesScrolled = cache()->displayViewLine(c); + } + + m_startPos.setPosition(c); + + // set false here but reversed if we return to makeVisible + m_madeVisible = false; + + if (viewLinesScrolledUsable) + { + int lines = linesDisplayed(); + if (m_view->textFolding().visibleLines() < lines) { + KTextEditor::Cursor end(m_view->textFolding().visibleLines() - 1, doc()->lineLength(m_view->textFolding().visibleLineToLine(m_view->textFolding().visibleLines() - 1))); + lines = qMin(linesDisplayed(), cache()->displayViewLine(end) + 1); + } + + Q_ASSERT(lines >= 0); + + if (!calledExternally && qAbs(viewLinesScrolled) < lines) + { + updateView(false, viewLinesScrolled); + + int scrollHeight = -(viewLinesScrolled * (int)renderer()->lineHeight()); + + // scroll excluding child widgets (floating notifications) + scroll(0, scrollHeight, rect()); + m_leftBorder->scroll(0, scrollHeight); + + if ((m_view->m_floatTopMessageWidget && m_view->m_floatTopMessageWidget->isVisible()) + || (m_view->m_bottomMessageWidget && m_view->m_bottomMessageWidget->isVisible())) + { + // NOTE: on some machines we must update if the floating widget is visible + // otherwise strange painting bugs may occur during scrolling... + update(); + } + + emit m_view->verticalScrollPositionChanged( m_view, c ); + emit m_view->displayRangeChanged(m_view); + return; + } + } + + updateView(); + update(); + m_leftBorder->update(); + emit m_view->verticalScrollPositionChanged( m_view, c ); + emit m_view->displayRangeChanged(m_view); +} + +void KateViewInternal::scrollColumns ( int x ) +{ + if (x < 0) + x = 0; + + if (x > m_columnScroll->maximum()) + x = m_columnScroll->maximum(); + + if (x == m_startX) + return; + + int dx = m_startX - x; + m_startX = x; + + if (qAbs(dx) < width()) { + // scroll excluding child widgets (floating notifications) + scroll(dx, 0, rect()); + } else + update(); + + emit m_view->horizontalScrollPositionChanged( m_view ); + emit m_view->displayRangeChanged(m_view); + + bool blocked = m_columnScroll->blockSignals(true); + m_columnScroll->setValue(m_startX); + m_columnScroll->blockSignals(blocked); +} + +// If changed is true, the lines that have been set dirty have been updated. +void KateViewInternal::updateView(bool changed, int viewLinesScrolled) +{ + doUpdateView(changed, viewLinesScrolled); + + if (changed) + updateDirty(); +} + +void KateViewInternal::doUpdateView(bool changed, int viewLinesScrolled) +{ + if(!isVisible() && !viewLinesScrolled && !changed ) + return; //When this view is not visible, don't do anything + + bool blocked = m_lineScroll->blockSignals(true); + + if (width() != cache()->viewWidth()) { + cache()->setViewWidth(width()); + changed = true; + } + + /* It was observed that height() could be negative here -- + when the main Kate view has 0 as size (during creation), + and there frame arount KateViewInternal. In which + case we'd set the view cache to 0 (or less!) lines, and + start allocating huge chunks of data, later. */ + int newSize = (qMax (0, height()) / renderer()->lineHeight()) + 1; + cache()->updateViewCache(startPos(), newSize, viewLinesScrolled); + m_visibleLineCount = newSize; + + KTextEditor::Cursor maxStart = maxStartPos(changed); + int maxLineScrollRange = maxStart.line(); + if (m_view->dynWordWrap() && maxStart.column() != 0) + maxLineScrollRange++; + m_lineScroll->setRange(0, maxLineScrollRange); + + m_lineScroll->setValue(startPos().line()); + m_lineScroll->setSingleStep(1); + m_lineScroll->setPageStep(qMax (0, height()) / renderer()->lineHeight()); + m_lineScroll->blockSignals(blocked); + + KateViewConfig::ScrollbarMode show_scrollbars = static_cast(view()->config()->showScrollbars()); + + bool visible = ( (show_scrollbars == KateViewConfig::AlwaysOn) || + ((show_scrollbars == KateViewConfig::ShowWhenNeeded) && (maxLineScrollRange != 0)) ); + bool visible_dummy = visible; + + m_lineScroll->setVisible( visible ); + + if (!m_view->dynWordWrap()) + { + int max = maxLen(startLine()) - width(); + if (max < 0) + max = 0; + + // if we lose the ability to scroll horizontally, move view to the far-left + if (max == 0) + { + scrollColumns(0); + } + + blocked = m_columnScroll->blockSignals(true); + + // disable scrollbar + m_columnScroll->setDisabled (max == 0); + + visible = ( (show_scrollbars == KateViewConfig::AlwaysOn) || + ((show_scrollbars == KateViewConfig::ShowWhenNeeded) && (max != 0)) ); + visible_dummy &= visible; + m_columnScroll->setVisible( visible ); + + m_columnScroll->setRange(0, max); + + m_columnScroll->setValue(m_startX); + + // Approximate linescroll + m_columnScroll->setSingleStep(renderer()->config()->fontMetrics().width('a')); + m_columnScroll->setPageStep(width()); + + m_columnScroll->blockSignals(blocked); + } else { + visible_dummy = false; + } + + m_dummy->setVisible( visible_dummy ); +} + +/** + * this function ensures a certain location is visible on the screen. + * if endCol is -1, ignore making the columns visible. + */ +void KateViewInternal::makeVisible (const KTextEditor::Cursor& c, int endCol, bool force, bool center, bool calledExternally) +{ + //kDebug(13030) << "MakeVisible start " << startPos() << " end " << endPos() << " -> request: " << c;// , new start [" << scroll.line << "," << scroll.col << "] lines " << (linesDisplayed() - 1) << " height " << height(); + // if the line is in a folded region, unfold all the way up + //if ( doc()->foldingTree()->findNodeForLine( c.line )->visible ) + // kDebug(13030)<<"line ("< endPos())) + { + KTextEditor::Cursor scroll = viewLineOffset(c, -int(linesDisplayed()) / 2); + scrollPos(scroll, false, calledExternally); + } + else if ( c > viewLineOffset(startPos(), linesDisplayed() - m_minLinesVisible - 1) ) + { + KTextEditor::Cursor scroll = viewLineOffset(c, -(linesDisplayed() - m_minLinesVisible - 1)); + scrollPos(scroll, false, calledExternally); + } + else if ( c < viewLineOffset(startPos(), m_minLinesVisible) ) + { + KTextEditor::Cursor scroll = viewLineOffset(c, -m_minLinesVisible); + scrollPos(scroll, false, calledExternally); + } + else + { + // Check to see that we're not showing blank lines + KTextEditor::Cursor max = maxStartPos(); + if (startPos() > max) { + scrollPos(max, max.column(), calledExternally); + } + } + + if (!m_view->dynWordWrap() && (endCol != -1 || m_view->wrapCursor())) + { + KTextEditor::Cursor rc = toRealCursor(c); + int sX = renderer()->cursorToX(cache()->textLayout(rc), rc, !m_view->wrapCursor()); + + int sXborder = sX-8; + if (sXborder < 0) + sXborder = 0; + + if (sX < m_startX) + scrollColumns (sXborder); + else if (sX > m_startX + width()) + scrollColumns (sX - width() + 8); + } + + m_madeVisible = !force; +} + +void KateViewInternal::slotRegionVisibilityChanged() +{ + kDebug(13030); + cache()->clear(); + + m_cachedMaxStartPos.setLine(-1); + KTextEditor::Cursor max = maxStartPos(); + if (startPos() > max) + scrollPos(max); + + // if text was folded: make sure the cursor is on a visible line + qint64 foldedRangeId = -1; + if (!m_view->textFolding().isLineVisible (m_cursor.line(), &foldedRangeId)) { + KTextEditor::Range foldingRange = m_view->textFolding().foldingRange(foldedRangeId); + Q_ASSERT(foldingRange.start().isValid()); + + // set cursor to start of folding region + updateCursor(foldingRange.start(), true); + } + + updateView(); + update(); + m_leftBorder->update(); + emit m_view->displayRangeChanged(m_view); +} + +void KateViewInternal::slotRegionBeginEndAddedRemoved(unsigned int) +{ + kDebug(13030); + // FIXME: performance problem + m_leftBorder->update(); +} + +void KateViewInternal::showEvent ( QShowEvent *e ) +{ + updateView (); + + QWidget::showEvent (e); +} + +int KateViewInternal::linesDisplayed() const +{ + int h = height(); + + // catch zero heights, even if should not happen + int fh = qMax (1, renderer()->lineHeight()); + + // default to 1, there is always one line around.... + // too many places calc with linesDisplayed() - 1 + return qMax (1, (h - (h % fh)) / fh); +} + +QPoint KateViewInternal::cursorToCoordinate( const KTextEditor::Cursor & cursor, bool realCursor, bool includeBorder ) const +{ + if (cursor.line() >= doc()->lines()) { + return QPoint(-1, -1); + } + int viewLine = cache()->displayViewLine(realCursor ? toVirtualCursor(cursor) : cursor, true); + + if (viewLine < 0 || viewLine >= cache()->viewCacheLineCount()) + return QPoint(-1, -1); + + int y = (int)viewLine * renderer()->lineHeight(); + + KateTextLayout layout = cache()->viewLine(viewLine); + int x = 0; + + // only set x value if we have a valid layout (bug #171027) + if (layout.isValid()) + x = (int)layout.lineLayout().cursorToX(cursor.column()); +// else +// kDebug() << "Invalid Layout"; + + if (includeBorder) x += m_leftBorder->width(); + + x -= startX(); + + return QPoint(x, y); +} + +QPoint KateViewInternal::cursorCoordinates(bool includeBorder) const +{ + return cursorToCoordinate(m_displayCursor, false, includeBorder); +} + +KTextEditor::Cursor KateViewInternal::findMatchingBracket() +{ + KTextEditor::Cursor c; + + if (!m_bm->toRange().isValid()) + return KTextEditor::Cursor::invalid(); + + Q_ASSERT(m_bmEnd->toRange().isValid()); + Q_ASSERT(m_bmStart->toRange().isValid()); + + if (m_bmStart->toRange().contains(m_cursor) || m_bmStart->end() == m_cursor.toCursor()) { + c = m_bmEnd->end(); + } else if (m_bmEnd->toRange().contains(m_cursor) || m_bmEnd->end() == m_cursor.toCursor()) { + c = m_bmStart->start(); + } else { + // should never happen: a range exists, but the cursor position is + // neither at the start nor at the end... + return KTextEditor::Cursor::invalid(); + } + + return c; +} + +void KateViewInternal::doReturn() +{ + doc()->newLine( m_view ); + m_leftBorder->updateViRelLineNumbers(); + updateView(); +} + +void KateViewInternal::doSmartNewline() +{ + int ln = m_cursor.line(); + Kate::TextLine line = doc()->kateTextLine(ln); + int col = qMin(m_cursor.column(), line->firstChar()); + if (col != -1) { + while (line->length() > col && + !(line->at(col).isLetterOrNumber() || line->at(col) == QLatin1Char('_')) && + col < m_cursor.column()) ++col; + } else { + col = line->length(); // stay indented + } + doc()->editStart(); + doc()->editWrapLine(ln, m_cursor.column()); + doc()->insertText(KTextEditor::Cursor(ln + 1, 0), line->string(0, col)); + doc()->editEnd(); + + updateView(); +} + +void KateViewInternal::doDelete() +{ + doc()->del( m_view, m_cursor ); +} + +void KateViewInternal::doBackspace() +{ + doc()->backspace( m_view, m_cursor ); +} + +void KateViewInternal::doTabulator() +{ + doc()->insertTab( m_view, m_cursor ); +} + +void KateViewInternal::doTranspose() +{ + doc()->transpose( m_cursor ); +} + +void KateViewInternal::doDeletePrevWord() +{ + doc()->editStart(); + wordPrev( true ); + KTextEditor::Range selection = m_view->selectionRange(); + m_view->removeSelectedText(); + doc()->editEnd(); + tagRange(selection, true); + updateDirty(); +} + +void KateViewInternal::doDeleteNextWord() +{ + doc()->editStart(); + wordNext( true ); + KTextEditor::Range selection = m_view->selectionRange(); + m_view->removeSelectedText(); + doc()->editEnd(); + tagRange(selection, true); + updateDirty(); +} + +class CalculatingCursor : public KTextEditor::Cursor { +public: + // These constructors constrain their arguments to valid positions + // before only the third one did, but that leads to crashs + // see bug 227449 + CalculatingCursor(KateViewInternal* vi) + : KTextEditor::Cursor() + , m_vi(vi) + { + makeValid(); + } + + CalculatingCursor(KateViewInternal* vi, const KTextEditor::Cursor& c) + : KTextEditor::Cursor(c) + , m_vi(vi) + { + makeValid(); + } + + CalculatingCursor(KateViewInternal* vi, int line, int col) + : KTextEditor::Cursor(line, col) + , m_vi(vi) + { + makeValid(); + } + + + virtual CalculatingCursor& operator+=( int n ) = 0; + + virtual CalculatingCursor& operator-=( int n ) = 0; + + CalculatingCursor& operator++() { return operator+=( 1 ); } + + CalculatingCursor& operator--() { return operator-=( 1 ); } + + void makeValid() { + setLine(qBound( 0, line(), int( doc()->lines() - 1 ) ) ); + if (view()->wrapCursor()) + m_column = qBound( 0, column(), doc()->lineLength( line() ) ); + else + m_column = qMax( 0, column() ); + Q_ASSERT( valid() ); + } + + void toEdge( KateViewInternal::Bias bias ) { + if( bias == KateViewInternal::left ) m_column = 0; + else if( bias == KateViewInternal::right ) m_column = doc()->lineLength( line() ); + } + + bool atEdge() const { return atEdge( KateViewInternal::left ) || atEdge( KateViewInternal::right ); } + + bool atEdge( KateViewInternal::Bias bias ) const { + switch( bias ) { + case KateViewInternal::left: return column() == 0; + case KateViewInternal::none: return atEdge(); + case KateViewInternal::right: return column() >= doc()->lineLength( line() ); + default: Q_ASSERT(false); return false; + } + } + +protected: + bool valid() const { + return line() >= 0 && + line() < doc()->lines() && + column() >= 0 && + (!view()->wrapCursor() || column() <= doc()->lineLength( line() )); + } + KateView* view() { return m_vi->m_view; } + const KateView* view() const { return m_vi->m_view; } + KateDocument* doc() { return view()->doc(); } + const KateDocument* doc() const { return view()->doc(); } + KateViewInternal* m_vi; +}; + +class BoundedCursor : public CalculatingCursor { +public: + BoundedCursor(KateViewInternal* vi) + : CalculatingCursor( vi ) {} + BoundedCursor(KateViewInternal* vi, const KTextEditor::Cursor& c ) + : CalculatingCursor( vi, c ) {} + BoundedCursor(KateViewInternal* vi, int line, int col ) + : CalculatingCursor( vi, line, col ) {} + virtual CalculatingCursor& operator+=( int n ) { + KateLineLayoutPtr thisLine = m_vi->cache()->line(line()); + if (!thisLine->isValid()) { + kWarning() << "Did not retrieve valid layout for line " << line(); + return *this; + } + + const bool wrapCursor = view()->wrapCursor(); + int maxColumn = -1; + if (n >= 0) { + for (int i = 0; i < n; i++) { + if (m_column >= thisLine->length()) { + if (wrapCursor) { + break; + + } else if (view()->dynWordWrap()) { + // Don't go past the edge of the screen in dynamic wrapping mode + if (maxColumn == -1) + maxColumn = thisLine->length() + ((m_vi->width() - thisLine->widthOfLastLine()) / m_vi->renderer()->spaceWidth()) - 1; + + if (m_column >= maxColumn) { + m_column = maxColumn; + break; + } + + ++m_column; + + } else { + ++m_column; + } + + } else { + m_column = thisLine->layout()->nextCursorPosition(m_column); + } + } + } else { + for (int i = 0; i > n; i--) { + if (m_column >= thisLine->length()) + --m_column; + else if (m_column == 0) + break; + else + m_column = thisLine->layout()->previousCursorPosition(m_column); + } + } + + Q_ASSERT( valid() ); + return *this; + } + virtual CalculatingCursor& operator-=( int n ) { + return operator+=( -n ); + } +}; + +class WrappingCursor : public CalculatingCursor { +public: + WrappingCursor(KateViewInternal* vi) + : CalculatingCursor( vi) {} + WrappingCursor(KateViewInternal* vi, const KTextEditor::Cursor& c ) + : CalculatingCursor( vi, c ) {} + WrappingCursor(KateViewInternal* vi, int line, int col ) + : CalculatingCursor( vi, line, col ) {} + + virtual CalculatingCursor& operator+=( int n ) { + KateLineLayoutPtr thisLine = m_vi->cache()->line(line()); + if (!thisLine->isValid()) { + kWarning() << "Did not retrieve a valid layout for line " << line(); + return *this; + } + + if (n >= 0) { + for (int i = 0; i < n; i++) { + if (m_column >= thisLine->length()) { + // Have come to the end of a line + if (line() >= doc()->lines() - 1) + // Have come to the end of the document + break; + + // Advance to the beginning of the next line + m_column = 0; + setLine(line() + 1); + + // Retrieve the next text range + thisLine = m_vi->cache()->line(line()); + if (!thisLine->isValid()) { + kWarning() << "Did not retrieve a valid layout for line " << line(); + return *this; + } + + continue; + } + + m_column = thisLine->layout()->nextCursorPosition(m_column); + } + + } else { + for (int i = 0; i > n; i--) { + if (m_column == 0) { + // Have come to the start of the document + if (line() == 0) + break; + + // Start going back to the end of the last line + setLine(line() - 1); + + // Retrieve the next text range + thisLine = m_vi->cache()->line(line()); + if (!thisLine->isValid()) { + kWarning() << "Did not retrieve a valid layout for line " << line(); + return *this; + } + + // Finish going back to the end of the last line + m_column = thisLine->length(); + + continue; + } + + if (m_column > thisLine->length()) + --m_column; + else + m_column = thisLine->layout()->previousCursorPosition(m_column); + } + } + + Q_ASSERT(valid()); + return *this; + } + virtual CalculatingCursor& operator-=( int n ) { + return operator+=( -n ); + } +}; + +void KateViewInternal::moveChar( KateViewInternal::Bias bias, bool sel ) +{ + KTextEditor::Cursor c; + if ( m_view->wrapCursor() ) { + c = WrappingCursor( this, m_cursor ) += bias; + } else { + c = BoundedCursor( this, m_cursor ) += bias; + } + + updateSelection( c, sel ); + updateCursor( c ); +} + +void KateViewInternal::cursorPrevChar( bool sel ) +{ + if ( ! m_view->wrapCursor() && m_cursor.column() == 0 ) + return; + + moveChar( KateViewInternal::left, sel ); +} + +void KateViewInternal::cursorNextChar( bool sel ) +{ + moveChar( KateViewInternal::right, sel ); +} + +void KateViewInternal::wordPrev( bool sel ) +{ + WrappingCursor c( this, m_cursor ); + + // First we skip backwards all space. + // Then we look up into which category the current position falls: + // 1. a "word" character + // 2. a "non-word" character (except space) + // 3. the beginning of the line + // and skip all preceding characters that fall into this class. + // The code assumes that space is never part of the word character class. + + KateHighlighting* h = doc()->highlight(); + if( !c.atEdge( left ) ) { + + while( !c.atEdge( left ) && doc()->line( c.line() )[ c.column() - 1 ].isSpace() ) + --c; + } + if( c.atEdge( left ) ) + { + --c; + } + else if( h->isInWord( doc()->line( c.line() )[ c.column() - 1 ] ) ) + { + while( !c.atEdge( left ) && h->isInWord( doc()->line( c.line() )[ c.column() - 1 ] ) ) + --c; + } + else + { + while( !c.atEdge( left ) + && !h->isInWord( doc()->line( c.line() )[ c.column() - 1 ] ) + // in order to stay symmetric to wordLeft() + // we must not skip space preceding a non-word sequence + && !doc()->line( c.line() )[ c.column() - 1 ].isSpace() ) + { + --c; + } + } + + updateSelection( c, sel ); + updateCursor( c ); +} + +void KateViewInternal::wordNext( bool sel ) +{ + WrappingCursor c( this, m_cursor ); + + // We look up into which category the current position falls: + // 1. a "word" character + // 2. a "non-word" character (except space) + // 3. the end of the line + // and skip all following characters that fall into this class. + // If the skipped characters are followed by space, we skip that too. + // The code assumes that space is never part of the word character class. + + KateHighlighting* h = doc()->highlight(); + if( c.atEdge( right ) ) + { + ++c; + } + else if( h->isInWord( doc()->line( c.line() )[ c.column() ] ) ) + { + while( !c.atEdge( right ) && h->isInWord( doc()->line( c.line() )[ c.column() ] ) ) + ++c; + } + else + { + while( !c.atEdge( right ) + && !h->isInWord( doc()->line( c.line() )[ c.column() ] ) + // we must not skip space, because if that space is followed + // by more non-word characters, we would skip them, too + && !doc()->line( c.line() )[ c.column() ].isSpace() ) + { + ++c; + } + } + + while( !c.atEdge( right ) && doc()->line( c.line() )[ c.column() ].isSpace() ) + ++c; + + updateSelection( c, sel ); + updateCursor( c ); +} + +void KateViewInternal::moveEdge( KateViewInternal::Bias bias, bool sel ) +{ + BoundedCursor c( this, m_cursor ); + c.toEdge( bias ); + updateSelection( c, sel ); + updateCursor( c ); +} + +void KateViewInternal::home( bool sel ) +{ + if (m_view->dynWordWrap() && currentLayout().startCol()) { + // Allow us to go to the real start if we're already at the start of the view line + if (m_cursor.column() != currentLayout().startCol()) { + KTextEditor::Cursor c = currentLayout().start(); + updateSelection( c, sel ); + updateCursor( c ); + return; + } + } + + if( !doc()->config()->smartHome() ) { + moveEdge( left, sel ); + return; + } + + Kate::TextLine l = doc()->kateTextLine( m_cursor.line() ); + + if (!l) + return; + + KTextEditor::Cursor c = m_cursor; + int lc = l->firstChar(); + + if( lc < 0 || c.column() == lc ) { + c.setColumn(0); + } else { + c.setColumn(lc); + } + + updateSelection( c, sel ); + updateCursor( c, true ); +} + +void KateViewInternal::end( bool sel ) +{ + KateTextLayout layout = currentLayout(); + + if (m_view->dynWordWrap() && layout.wrap()) { + // Allow us to go to the real end if we're already at the end of the view line + if (m_cursor.column() < layout.endCol() - 1) { + KTextEditor::Cursor c(m_cursor.line(), layout.endCol() - 1); + updateSelection( c, sel ); + updateCursor( c ); + return; + } + } + + if( !doc()->config()->smartHome() ) { + moveEdge( right, sel ); + return; + } + + Kate::TextLine l = doc()->kateTextLine( m_cursor.line() ); + + if (!l) + return; + + // "Smart End", as requested in bugs #78258 and #106970 + if (m_cursor.column() == doc()->lineLength(m_cursor.line())) { + KTextEditor::Cursor c = m_cursor; + c.setColumn(l->lastChar() + 1); + updateSelection(c, sel); + updateCursor(c, true); + } else { + moveEdge(right, sel); + } +} + +KateTextLayout KateViewInternal::currentLayout() const +{ + return cache()->textLayout(m_cursor); +} + +KateTextLayout KateViewInternal::previousLayout() const +{ + int currentViewLine = cache()->viewLine(m_cursor); + + if (currentViewLine) + return cache()->textLayout(m_cursor.line(), currentViewLine - 1); + else + return cache()->textLayout(m_view->textFolding().visibleLineToLine(m_displayCursor.line() - 1), -1); +} + +KateTextLayout KateViewInternal::nextLayout() const +{ + int currentViewLine = cache()->viewLine(m_cursor) + 1; + + if (currentViewLine >= cache()->line(m_cursor.line())->viewLineCount()) { + currentViewLine = 0; + return cache()->textLayout(m_view->textFolding().visibleLineToLine(m_displayCursor.line() + 1), currentViewLine); + } else { + return cache()->textLayout(m_cursor.line(), currentViewLine); + } +} + +/* + * This returns the cursor which is offset by (offset) view lines. + * This is the main function which is called by code not specifically dealing with word-wrap. + * The opposite conversion (cursor to offset) can be done with cache()->displayViewLine(). + * + * The cursors involved are virtual cursors (ie. equivalent to m_displayCursor) + */ + +KTextEditor::Cursor KateViewInternal::viewLineOffset(const KTextEditor::Cursor& virtualCursor, int offset, bool keepX) +{ + if (!m_view->dynWordWrap()) { + KTextEditor::Cursor ret(qMin((int)m_view->textFolding().visibleLines() - 1, virtualCursor.line() + offset), 0); + + if (ret.line() < 0) + ret.setLine(0); + + if (keepX) { + int realLine = m_view->textFolding().visibleLineToLine(ret.line()); + KateTextLayout t = cache()->textLayout(realLine, 0); + Q_ASSERT(t.isValid()); + + ret.setColumn(renderer()->xToCursor(t, m_preservedX, !m_view->wrapCursor()).column()); + } + + return ret; + } + + KTextEditor::Cursor realCursor = virtualCursor; + realCursor.setLine(m_view->textFolding().visibleLineToLine(m_view->textFolding().lineToVisibleLine(virtualCursor.line()))); + + int cursorViewLine = cache()->viewLine(realCursor); + + int currentOffset = 0; + int virtualLine = 0; + + bool forwards = (offset > 0) ? true : false; + + if (forwards) { + currentOffset = cache()->lastViewLine(realCursor.line()) - cursorViewLine; + if (offset <= currentOffset) { + // the answer is on the same line + KateTextLayout thisLine = cache()->textLayout(realCursor.line(), cursorViewLine + offset); + Q_ASSERT(thisLine.virtualLine() == virtualCursor.line()); + return KTextEditor::Cursor(virtualCursor.line(), thisLine.startCol()); + } + + virtualLine = virtualCursor.line() + 1; + + } else { + offset = -offset; + currentOffset = cursorViewLine; + if (offset <= currentOffset) { + // the answer is on the same line + KateTextLayout thisLine = cache()->textLayout(realCursor.line(), cursorViewLine - offset); + Q_ASSERT(thisLine.virtualLine() == (int) m_view->textFolding().lineToVisibleLine(virtualCursor.line())); + return KTextEditor::Cursor(virtualCursor.line(), thisLine.startCol()); + } + + virtualLine = virtualCursor.line() - 1; + } + + currentOffset++; + + while (virtualLine >= 0 && virtualLine < (int)m_view->textFolding().visibleLines()) + { + int realLine = m_view->textFolding().visibleLineToLine(virtualLine); + KateLineLayoutPtr thisLine = cache()->line(realLine, virtualLine); + if (!thisLine) + break; + + for (int i = 0; i < thisLine->viewLineCount(); ++i) { + if (offset == currentOffset) { + KateTextLayout thisViewLine = thisLine->viewLine(i); + + if (!forwards) { + // We actually want it the other way around + int requiredViewLine = cache()->lastViewLine(realLine) - thisViewLine.viewLine(); + if (requiredViewLine != thisViewLine.viewLine()) { + thisViewLine = thisLine->viewLine(requiredViewLine); + } + } + + KTextEditor::Cursor ret(virtualLine, thisViewLine.startCol()); + + // keep column position + if (keepX) { + KTextEditor::Cursor realCursor = toRealCursor(virtualCursor); + KateTextLayout t = cache()->textLayout(realCursor); + // renderer()->cursorToX(t, realCursor, !m_view->wrapCursor()); + + realCursor = renderer()->xToCursor(thisViewLine, m_preservedX, !m_view->wrapCursor()); + ret.setColumn(realCursor.column()); + } + + return ret; + } + + currentOffset++; + } + + if (forwards) + virtualLine++; + else + virtualLine--; + } + + // Looks like we were asked for something a bit exotic. + // Return the max/min valid position. + if (forwards) + return KTextEditor::Cursor(m_view->textFolding().visibleLines() - 1, doc()->lineLength(m_view->textFolding().visibleLineToLine (m_view->textFolding().visibleLines() - 1))); + else + return KTextEditor::Cursor(0, 0); +} + +int KateViewInternal::lineMaxCursorX(const KateTextLayout& range) +{ + if (!m_view->wrapCursor() && !range.wrap()) + return INT_MAX; + + int maxX = range.endX(); + + if (maxX && range.wrap()) { + QChar lastCharInLine = doc()->kateTextLine(range.line())->at(range.endCol() - 1); + maxX -= renderer()->config()->fontMetrics().width(lastCharInLine); + } + + return maxX; +} + +int KateViewInternal::lineMaxCol(const KateTextLayout& range) +{ + int maxCol = range.endCol(); + + if (maxCol && range.wrap()) + maxCol--; + + return maxCol; +} + +void KateViewInternal::cursorUp(bool sel) +{ + if(!sel && m_view->completionWidget()->isCompletionActive()) { + m_view->completionWidget()->cursorUp(); + return; + } + + if (m_displayCursor.line() == 0 && (!m_view->dynWordWrap() || cache()->viewLine(m_cursor) == 0)) + return; + + m_preserveX = true; + + KateTextLayout thisLine = currentLayout(); + // This is not the first line because that is already simplified out above + KateTextLayout pRange = previousLayout(); + + // Ensure we're in the right spot + Q_ASSERT(m_cursor.line() == thisLine.line()); + Q_ASSERT(m_cursor.column() >= thisLine.startCol()); + Q_ASSERT(!thisLine.wrap() || m_cursor.column() < thisLine.endCol()); + + KTextEditor::Cursor c = renderer()->xToCursor(pRange, m_preservedX, !m_view->wrapCursor()); + + updateSelection( c, sel ); + updateCursor( c ); +} + +void KateViewInternal::cursorDown(bool sel) +{ + if(!sel && m_view->completionWidget()->isCompletionActive()) { + m_view->completionWidget()->cursorDown(); + return; + } + + if ((m_displayCursor.line() >= m_view->textFolding().visibleLines() - 1) && (!m_view->dynWordWrap() || cache()->viewLine(m_cursor) == cache()->lastViewLine(m_cursor.line()))) + return; + + m_preserveX = true; + + KateTextLayout thisLine = currentLayout(); + // This is not the last line because that is already simplified out above + KateTextLayout nRange = nextLayout(); + + // Ensure we're in the right spot + Q_ASSERT((m_cursor.line() == thisLine.line()) && + (m_cursor.column() >= thisLine.startCol()) && + (!thisLine.wrap() || m_cursor.column() < thisLine.endCol())); + + KTextEditor::Cursor c = renderer()->xToCursor(nRange, m_preservedX, !m_view->wrapCursor()); + + updateSelection(c, sel); + updateCursor(c); +} + +void KateViewInternal::cursorToMatchingBracket( bool sel ) +{ + KTextEditor::Cursor c = findMatchingBracket(); + + if (c.isValid()) { + updateSelection( c, sel ); + updateCursor( c ); + } +} + +void KateViewInternal::topOfView( bool sel ) +{ + KTextEditor::Cursor c = viewLineOffset(startPos(), m_minLinesVisible); + updateSelection( toRealCursor(c), sel ); + updateCursor( toRealCursor(c) ); +} + +void KateViewInternal::bottomOfView( bool sel ) +{ + KTextEditor::Cursor c = viewLineOffset(endPos(), -m_minLinesVisible); + updateSelection( toRealCursor(c), sel ); + updateCursor( toRealCursor(c) ); +} + +// lines is the offset to scroll by +void KateViewInternal::scrollLines( int lines, bool sel ) +{ + KTextEditor::Cursor c = viewLineOffset(m_displayCursor, lines, true); + + // Fix the virtual cursor -> real cursor + c.setLine(m_view->textFolding().visibleLineToLine(c.line())); + + updateSelection( c, sel ); + updateCursor( c ); +} + +// This is a bit misleading... it's asking for the view to be scrolled, not the cursor +void KateViewInternal::scrollUp() +{ + KTextEditor::Cursor newPos = viewLineOffset(startPos(), -1); + scrollPos(newPos); +} + +void KateViewInternal::scrollDown() +{ + KTextEditor::Cursor newPos = viewLineOffset(startPos(), 1); + scrollPos(newPos); +} + +void KateViewInternal::setAutoCenterLines(int viewLines, bool updateView) +{ + m_autoCenterLines = viewLines; + m_minLinesVisible = qMin(int((linesDisplayed() - 1)/2), m_autoCenterLines); + if (updateView) + KateViewInternal::updateView(); +} + +void KateViewInternal::pageUp( bool sel, bool half ) +{ + if (m_view->isCompletionActive()) { + m_view->completionWidget()->pageUp(); + return; + } + + // remember the view line and x pos + int viewLine = cache()->displayViewLine(m_displayCursor); + bool atTop = startPos().atStartOfDocument(); + + // Adjust for an auto-centering cursor + int lineadj = m_minLinesVisible; + + int linesToScroll; + if ( ! half ) + linesToScroll = -qMax( (linesDisplayed() - 1) - lineadj, 0 ); + else + linesToScroll = -qMax( (linesDisplayed()/2 - 1) - lineadj, 0 ); + + m_preserveX = true; + + if (!doc()->pageUpDownMovesCursor () && !atTop) { + KTextEditor::Cursor newStartPos = viewLineOffset(startPos(), linesToScroll - 1); + scrollPos(newStartPos); + + // put the cursor back approximately where it was + KTextEditor::Cursor newPos = toRealCursor(viewLineOffset(newStartPos, viewLine, true)); + + KateTextLayout newLine = cache()->textLayout(newPos); + + newPos = renderer()->xToCursor(newLine, m_preservedX, !m_view->wrapCursor()); + + m_preserveX = true; + updateSelection( newPos, sel ); + updateCursor(newPos); + + } else { + scrollLines( linesToScroll, sel ); + } +} + +void KateViewInternal::pageDown( bool sel ,bool half) +{ + if (m_view->isCompletionActive()) { + m_view->completionWidget()->pageDown(); + return; + } + + // remember the view line + int viewLine = cache()->displayViewLine(m_displayCursor); + bool atEnd = startPos() >= m_cachedMaxStartPos; + + // Adjust for an auto-centering cursor + int lineadj = m_minLinesVisible; + + int linesToScroll; + if ( ! half ) + linesToScroll = qMax( (linesDisplayed() - 1) - lineadj, 0 ); + else + linesToScroll = qMax( (linesDisplayed()/2 - 1) - lineadj, 0 ); + + m_preserveX = true; + + if (!doc()->pageUpDownMovesCursor () && !atEnd) { + KTextEditor::Cursor newStartPos = viewLineOffset(startPos(), linesToScroll + 1); + scrollPos(newStartPos); + + // put the cursor back approximately where it was + KTextEditor::Cursor newPos = toRealCursor(viewLineOffset(newStartPos, viewLine, true)); + + KateTextLayout newLine = cache()->textLayout(newPos); + + newPos = renderer()->xToCursor(newLine, m_preservedX, !m_view->wrapCursor()); + + m_preserveX = true; + updateSelection( newPos, sel ); + updateCursor(newPos); + + } else { + scrollLines( linesToScroll, sel ); + } +} + +int KateViewInternal::maxLen(int startLine) +{ + Q_ASSERT(!m_view->dynWordWrap()); + + int displayLines = (m_view->height() / renderer()->lineHeight()) + 1; + + int maxLen = 0; + + for (int z = 0; z < displayLines; z++) { + int virtualLine = startLine + z; + + if (virtualLine < 0 || virtualLine >= (int)m_view->textFolding().visibleLines()) + break; + + maxLen = qMax(maxLen, cache()->line(m_view->textFolding().visibleLineToLine(virtualLine))->width()); + } + + return maxLen; +} + +bool KateViewInternal::columnScrollingPossible () +{ + return !m_view->dynWordWrap() && m_columnScroll->isEnabled() && (m_columnScroll->maximum() > 0); +} + +void KateViewInternal::top( bool sel ) +{ + KTextEditor::Cursor newCursor(0, 0); + + newCursor = renderer()->xToCursor(cache()->textLayout(newCursor), m_preservedX, !m_view->wrapCursor()); + + updateSelection( newCursor, sel ); + updateCursor( newCursor ); +} + +void KateViewInternal::bottom( bool sel ) +{ + KTextEditor::Cursor newCursor(doc()->lastLine(), 0); + + newCursor = renderer()->xToCursor(cache()->textLayout(newCursor), m_preservedX, !m_view->wrapCursor()); + + updateSelection( newCursor, sel ); + updateCursor( newCursor ); +} + +void KateViewInternal::top_home( bool sel ) +{ + if (m_view->isCompletionActive()) { + m_view->completionWidget()->top(); + return; + } + + KTextEditor::Cursor c( 0, 0 ); + updateSelection( c, sel ); + updateCursor( c ); +} + +void KateViewInternal::bottom_end( bool sel ) +{ + if (m_view->isCompletionActive()) { + m_view->completionWidget()->bottom(); + return; + } + + KTextEditor::Cursor c( doc()->lastLine(), doc()->lineLength( doc()->lastLine() ) ); + updateSelection( c, sel ); + updateCursor( c ); +} + +void KateViewInternal::updateSelection( const KTextEditor::Cursor& _newCursor, bool keepSel ) +{ + KTextEditor::Cursor newCursor = _newCursor; + if( keepSel ) + { + if ( !m_view->selection() || (m_selectAnchor.line() == -1) + //don't kill the selection if we have a persistent selection and + //the cursor is inside or at the boundaries of the selected area + || (m_view->config()->persistentSelection() + && !(m_view->selectionRange().contains(m_cursor) + || m_view->selectionRange().boundaryAtCursor(m_cursor))) ) + { + m_selectAnchor = m_cursor; + setSelection( KTextEditor::Range(m_cursor, newCursor) ); + } + else + { + bool doSelect = true; + switch (m_selectionMode) + { + case Word: + { + // Restore selStartCached if needed. It gets nuked by + // viewSelectionChanged if we drag the selection into non-existence, + // which can legitimately happen if a shift+DC selection is unable to + // set a "proper" (i.e. non-empty) cached selection, e.g. because the + // start was on something that isn't a word. Word select mode relies + // on the cached selection being set properly, even if it is empty + // (i.e. selStartCached == selEndCached). + if ( !m_selectionCached.isValid() ) + m_selectionCached.start() = m_selectionCached.end(); + + int c; + if ( newCursor > m_selectionCached.start() ) + { + m_selectAnchor = m_selectionCached.start(); + + Kate::TextLine l = doc()->kateTextLine( newCursor.line() ); + + c = newCursor.column(); + if ( c > 0 && doc()->highlight()->isInWord( l->at( c-1 ) ) ) { + for ( ; c < l->length(); c++ ) + if ( !doc()->highlight()->isInWord( l->at( c ) ) ) + break; + } + + newCursor.setColumn( c ); + } + else if ( newCursor < m_selectionCached.start() ) + { + m_selectAnchor = m_selectionCached.end(); + + Kate::TextLine l = doc()->kateTextLine( newCursor.line() ); + + c = newCursor.column(); + if ( c > 0 && c < doc()->lineLength( newCursor.line() ) + && doc()->highlight()->isInWord( l->at( c ) ) + && doc()->highlight()->isInWord( l->at( c-1 ) ) ) { + for ( c -= 2; c >= 0; c-- ) + if ( !doc()->highlight()->isInWord( l->at( c ) ) ) + break; + newCursor.setColumn( c+1 ); + } + } + else + doSelect = false; + + } + break; + case Line: + if ( !m_selectionCached.isValid() ) { + m_selectionCached = KTextEditor::Range(endLine(), 0, endLine(), 0); + } + if ( newCursor.line() > m_selectionCached.start().line() ) + { + if (newCursor.line() + 1 >= doc()->lines() ) + newCursor.setColumn( doc()->line( newCursor.line() ).length() ); + else + newCursor.setPosition( newCursor.line() + 1, 0 ); + // Grow to include the entire line + m_selectAnchor = m_selectionCached.start(); + m_selectAnchor.setColumn( 0 ); + } + else if ( newCursor.line() < m_selectionCached.start().line() ) + { + newCursor.setColumn( 0 ); + // Grow to include entire line + m_selectAnchor = m_selectionCached.end(); + if ( m_selectAnchor.column() > 0 ) + { + if ( m_selectAnchor.line()+1 >= doc()->lines() ) + m_selectAnchor.setColumn( doc()->line( newCursor.line() ).length() ); + else + m_selectAnchor.setPosition( m_selectAnchor.line() + 1, 0 ); + } + } + else // same line, ignore + doSelect = false; + break; + case Mouse: + { + if ( !m_selectionCached.isValid() ) + break; + + if ( newCursor > m_selectionCached.end() ) + m_selectAnchor = m_selectionCached.start(); + else if ( newCursor < m_selectionCached.start() ) + m_selectAnchor = m_selectionCached.end(); + else + doSelect = false; + } + break; + default: /* nothing special to do */; + } + + if ( doSelect ) + setSelection( KTextEditor::Range(m_selectAnchor, newCursor) ); + else if ( m_selectionCached.isValid() ) // we have a cached selection, so we restore that + setSelection( m_selectionCached ); + } + + m_selChangedByUser = true; + } + else if ( !m_view->config()->persistentSelection() ) + { + m_view->clearSelection(); + + m_selectionCached = KTextEditor::Range::invalid(); + m_selectAnchor = KTextEditor::Cursor::invalid(); + } +} + +void KateViewInternal::setCaretStyle( KateRenderer::caretStyles style, bool repaint ) +{ + renderer()->setCaretStyle( style ); + + if ( repaint ) { + if ( m_cursorTimer.isActive() && + KApplication::cursorFlashTime() > 0 ) { + m_cursorTimer.start( KApplication::cursorFlashTime() / 2 ); + } + renderer()->setDrawCaret(true); + paintCursor(); + } +} + +void KateViewInternal::setSelection( const KTextEditor::Range &range ) +{ + disconnect(m_view, SIGNAL(selectionChanged(KTextEditor::View*)), this, SLOT(viewSelectionChanged())); + m_view->setSelection(range); + connect(m_view, SIGNAL(selectionChanged(KTextEditor::View*)), this, SLOT(viewSelectionChanged())); +} + +void KateViewInternal::moveCursorToSelectionEdge() +{ + if (!m_view->selection()) + return; + + int tmp = m_minLinesVisible; + m_minLinesVisible = 0; + + if ( m_view->selectionRange().start() < m_selectAnchor ) + updateCursor( m_view->selectionRange().start() ); + else + updateCursor( m_view->selectionRange().end() ); + + m_minLinesVisible = tmp; +} + +void KateViewInternal::updateCursor( const KTextEditor::Cursor& newCursor, bool force, bool center, bool calledExternally ) +{ + if ( !force && (m_cursor.toCursor() == newCursor) ) + { + m_displayCursor = toVirtualCursor(newCursor); + if ( !m_madeVisible && m_view == doc()->activeView() ) + { + // unfold if required + m_view->textFolding().ensureLineIsVisible ( newCursor.line() ); + + makeVisible ( m_displayCursor, m_displayCursor.column(), false, center, calledExternally ); + } + + return; + } + + if (m_cursor.line() != newCursor.line()) { + m_leftBorder->updateViRelLineNumbers(); + } + + // unfold if required + m_view->textFolding().ensureLineIsVisible ( newCursor.line() ); + + KTextEditor::Cursor oldDisplayCursor = m_displayCursor; + + m_displayCursor = toVirtualCursor(newCursor); + m_cursor.setPosition( newCursor ); + + if ( m_view == doc()->activeView() ) + makeVisible ( m_displayCursor, m_displayCursor.column(), false, center, calledExternally ); + + updateBracketMarks(); + + // It's efficient enough to just tag them both without checking to see if they're on the same view line +/* kdDebug()<<"oldDisplayCursor:"< 0 ) + m_cursorTimer.start( KApplication::cursorFlashTime() / 2 ); + renderer()->setDrawCaret(true); + } + + // Remember the maximum X position if requested + if (m_preserveX) + m_preserveX = false; + else + m_preservedX = renderer()->cursorToX(cache()->textLayout(m_cursor), m_cursor, !m_view->wrapCursor()); + + //kDebug(13030) << "m_preservedX: " << m_preservedX << " (was "<< oldmaxx << "), m_cursorX: " << m_cursorX; + //kDebug(13030) << "Cursor now located at real " << cursor.line << "," << cursor.col << ", virtual " << m_displayCursor.line << ", " << m_displayCursor.col << "; Top is " << startLine() << ", " << startPos().col; + + cursorMoved(); + + updateDirty(); //paintText(0, 0, width(), height(), true); + + emit m_view->cursorPositionChanged(m_view, m_cursor); +} + +void KateViewInternal::updateBracketMarkAttributes() +{ + KTextEditor::Attribute::Ptr bracketFill = KTextEditor::Attribute::Ptr(new KTextEditor::Attribute()); + bracketFill->setBackground(m_view->m_renderer->config()->highlightedBracketColor()); + bracketFill->setBackgroundFillWhitespace(false); + if (QFontInfo(renderer()->currentFont()).fixedPitch()) { + // make font bold only for fixed fonts, otherwise text jumps around + bracketFill->setFontBold(); + } + + m_bmStart->setAttribute(bracketFill); + m_bmEnd->setAttribute(bracketFill); + + if (m_view->m_renderer->config()->showWholeBracketExpression()) { + KTextEditor::Attribute::Ptr expressionFill = KTextEditor::Attribute::Ptr(new KTextEditor::Attribute()); + expressionFill->setBackground(m_view->m_renderer->config()->highlightedBracketColor()); + expressionFill->setBackgroundFillWhitespace(false); + + m_bm->setAttribute(expressionFill); + } else { + m_bm->setAttribute(KTextEditor::Attribute::Ptr(new KTextEditor::Attribute())); + } +} + +void KateViewInternal::updateBracketMarks() +{ + // add some limit to this, this is really endless on big files without limit + int maxLines = 5000; + KTextEditor::Range newRange; + doc()->newBracketMark( m_cursor, newRange, maxLines ); + + // new range valid, then set ranges to it + if (newRange.isValid ()) { + if (m_bm->toRange() == newRange) { + return; + } + + // modify full range + m_bm->setRange (newRange); + + // modify start and end ranges + m_bmStart->setRange (KTextEditor::Range (m_bm->start(), KTextEditor::Cursor (m_bm->start().line(), m_bm->start().column() + 1))); + m_bmEnd->setRange (KTextEditor::Range (m_bm->end(), KTextEditor::Cursor (m_bm->end().line(), m_bm->end().column() + 1))); + + // flash matching bracket + if (!renderer()->config()->animateBracketMatching()) { + return; + } + const KTextEditor::Cursor flashPos = (m_cursor == m_bmStart->start() || m_cursor == m_bmStart->end()) ? m_bmEnd->start() : m_bm->start(); + if (flashPos != m_bmLastFlashPos->toCursor()) { + m_bmLastFlashPos->setPosition(flashPos); + + KTextEditor::Attribute::Ptr attribute = doc()->attributeAt(flashPos); + attribute->setBackground(m_view->m_renderer->config()->highlightedBracketColor()); + attribute->setFontBold(m_bmStart->attribute()->fontBold()); + + flashChar(flashPos, attribute); + } + return; + } + + // new range was invalid + m_bm->setRange (KTextEditor::Range::invalid()); + m_bmStart->setRange (KTextEditor::Range::invalid()); + m_bmEnd->setRange (KTextEditor::Range::invalid()); + m_bmLastFlashPos->setPosition (KTextEditor::Cursor::invalid()); +} + +bool KateViewInternal::tagLine(const KTextEditor::Cursor& virtualCursor) +{ + // FIXME may be a more efficient way for this + if ((int)m_view->textFolding().visibleLineToLine(virtualCursor.line()) > doc()->lastLine()) + return false; + // End FIXME + + int viewLine = cache()->displayViewLine(virtualCursor, true); + if (viewLine >= 0 && viewLine < cache()->viewCacheLineCount()) { + cache()->viewLine(viewLine).setDirty(); + m_leftBorder->update (0, lineToY(viewLine), m_leftBorder->width(), renderer()->lineHeight()); + return true; + } + return false; +} + +bool KateViewInternal::tagLines( int start, int end, bool realLines ) +{ + return tagLines(KTextEditor::Cursor(start, 0), KTextEditor::Cursor(end, -1), realLines); +} + +bool KateViewInternal::tagLines(KTextEditor::Cursor start, KTextEditor::Cursor end, bool realCursors) +{ + if (realCursors) + { + cache()->relayoutLines(start.line(), end.line()); + + //kDebug(13030)<<"realLines is true"; + start = toVirtualCursor(start); + end = toVirtualCursor(end); + + } else { + cache()->relayoutLines(toRealCursor(start).line(), toRealCursor(end).line()); + } + + if (end.line() < startLine()) + { + //kDebug(13030)<<"end endLine(), but cache may not be valid when checking, so use a + // less optimal but still adequate approximation (potential overestimation but minimal performance difference) + if (start.line() > startLine() + cache()->viewCacheLineCount()) + { + //kDebug(13030)<<"start> endLine"<updateViewCache(startPos()); + + //kDebug(13030) << "tagLines( [" << start << "], [" << end << "] )"; + + bool ret = false; + + for (int z = 0; z < cache()->viewCacheLineCount(); z++) + { + KateTextLayout& line = cache()->viewLine(z); + if ((line.virtualLine() > start.line() || (line.virtualLine() == start.line() && line.endCol() >= start.column() && start.column() != -1)) && + (line.virtualLine() < end.line() || (line.virtualLine() == end.line() && (line.startCol() <= end.column() || end.column() == -1)))) { + ret = true; + break; + //kDebug(13030) << "Tagged line " << line.line(); + } + } + + if (!m_view->dynWordWrap()) + { + int y = lineToY( start.line() ); + // FIXME is this enough for when multiple lines are deleted + int h = (end.line() - start.line() + 2) * renderer()->lineHeight(); + if (end.line() >= m_view->textFolding().visibleLines() - 1) + h = height(); + + m_leftBorder->update (0, y, m_leftBorder->width(), h); + } + else + { + // FIXME Do we get enough good info in editRemoveText to optimize this more? + //bool justTagged = false; + for (int z = 0; z < cache()->viewCacheLineCount(); z++) + { + KateTextLayout& line = cache()->viewLine(z); + if (!line.isValid() || + ((line.virtualLine() > start.line() || (line.virtualLine() == start.line() && line.endCol() >= start.column() && start.column() != -1)) && + (line.virtualLine() < end.line() || (line.virtualLine() == end.line() && (line.startCol() <= end.column() || end.column() == -1))))) + { + //justTagged = true; + m_leftBorder->update (0, z * renderer()->lineHeight(), m_leftBorder->width(), m_leftBorder->height()); + break; + } + /*else if (justTagged) + { + justTagged = false; + leftBorder->update (0, z * doc()->viewFont.fontHeight, leftBorder->width(), doc()->viewFont.fontHeight); + break; + }*/ + } + } + + return ret; +} + +bool KateViewInternal::tagRange(const KTextEditor::Range& range, bool realCursors) +{ + return tagLines(range.start(), range.end(), realCursors); +} + +void KateViewInternal::tagAll() +{ + // clear the cache... + cache()->clear (); + + m_leftBorder->updateFont(); + m_leftBorder->update(); +} + +void KateViewInternal::paintCursor() +{ + if (tagLine(m_displayCursor)) + updateDirty(); //paintText (0,0,width(), height(), true); +} + +// Point in content coordinates +void KateViewInternal::placeCursor( const QPoint& p, bool keepSelection, bool updateSelection ) +{ + KateTextLayout thisLine = yToKateTextLayout(p.y()); + KTextEditor::Cursor c; + + if (!thisLine.isValid()) // probably user clicked below the last line -> use the last line + thisLine = cache()->textLayout(doc()->lines() - 1, -1); + + c = renderer()->xToCursor(thisLine, startX() + p.x(), !m_view->wrapCursor()); + + if (c.line () < 0 || c.line() >= doc()->lines()) { + return; + } + + if (updateSelection) + KateViewInternal::updateSelection( c, keepSelection ); + + int tmp = m_minLinesVisible; + m_minLinesVisible = 0; + updateCursor( c ); + m_minLinesVisible = tmp; + + if (updateSelection && keepSelection) + moveCursorToSelectionEdge(); +} + +// Point in content coordinates +bool KateViewInternal::isTargetSelected( const QPoint& p ) +{ + const KateTextLayout& thisLine = yToKateTextLayout(p.y()); + if (!thisLine.isValid()) + return false; + + return m_view->cursorSelected(renderer()->xToCursor(thisLine, startX() + p.x(), !m_view->wrapCursor())); +} + +//BEGIN EVENT HANDLING STUFF + +bool KateViewInternal::eventFilter( QObject *obj, QEvent *e ) +{ + if (obj == m_lineScroll) + { + // the second condition is to make sure a scroll on the vertical bar doesn't cause a horizontal scroll ;) + if (e->type() == QEvent::Wheel && m_lineScroll->minimum() != m_lineScroll->maximum()) + { + wheelEvent((QWheelEvent*)e); + return true; + } + + // continue processing + return QWidget::eventFilter( obj, e ); + } + + switch( e->type() ) + { + case QEvent::ChildAdded: + case QEvent::ChildRemoved: { + QChildEvent* c = static_cast(e); + if (c->added()) { + c->child()->installEventFilter(this); + /*foreach (QWidget* child, c->child()->findChildren()) + child->installEventFilter(this);*/ + + } else if (c->removed()) { + c->child()->removeEventFilter(this); + + /*foreach (QWidget* child, c->child()->findChildren()) + child->removeEventFilter(this);*/ + } + } break; + + case QEvent::ShortcutOverride: + { + QKeyEvent *k = static_cast(e); + + if (k->key() == Qt::Key_Escape && k->modifiers() == Qt::NoModifier) { + if (m_view->isCompletionActive()) { + m_view->abortCompletion(); + k->accept(); + //kDebug() << obj << "shortcut override" << k->key() << "aborting completion"; + return true; + } else if (m_view->bottomViewBar()->isVisible()) { + m_view->bottomViewBar()->hideCurrentBarWidget(); + k->accept(); + //kDebug() << obj << "shortcut override" << k->key() << "closing view bar"; + return true; + } else if (!m_view->config()->persistentSelection() && m_view->selection()) { + m_view->clearSelection(); + k->accept(); + //kDebug() << obj << "shortcut override" << k->key() << "clearing selection"; + return true; + } + } + } break; + + case QEvent::KeyPress: + { + QKeyEvent *k = static_cast(e); + + // Override all other single key shortcuts which do not use a modifier other than Shift + if (obj == this && (!k->modifiers() || k->modifiers() == Qt::ShiftModifier)) { + keyPressEvent( k ); + if (k->isAccepted()) { + //kDebug() << obj << "shortcut override" << k->key() << "using keystroke"; + return true; + } + } + + //kDebug() << obj << "shortcut override" << k->key() << "ignoring"; + } break; + + case QEvent::DragMove: + { + QPoint currentPoint = ((QDragMoveEvent*) e)->pos(); + + QRect doNotScrollRegion( s_scrollMargin, s_scrollMargin, + width() - s_scrollMargin * 2, + height() - s_scrollMargin * 2 ); + + if ( !doNotScrollRegion.contains( currentPoint ) ) + { + startDragScroll(); + // Keep sending move events + ( (QDragMoveEvent*)e )->accept( QRect(0,0,0,0) ); + } + + dragMoveEvent((QDragMoveEvent*)e); + } break; + + case QEvent::DragLeave: + // happens only when pressing ESC while dragging + stopDragScroll(); + break; + + case QEvent::WindowBlocked: + // next focus originates from an internal dialog: + // don't show the modonhd prompt + if (isVisible()) { + doc()->ignoreModifiedOnDiskOnce(); + } + break; + + default: + break; + } + + return QWidget::eventFilter( obj, e ); +} + +void KateViewInternal::keyPressEvent( QKeyEvent* e ) +{ + if( e->key() == Qt::Key_Left && e->modifiers() == Qt::AltModifier ) { + m_view->emitNavigateLeft(); + e->setAccepted(true); + return; + } + if( e->key() == Qt::Key_Right && e->modifiers() == Qt::AltModifier ) { + m_view->emitNavigateRight(); + e->setAccepted(true); + return; + } + if( e->key() == Qt::Key_Up && e->modifiers() == Qt::AltModifier ) { + m_view->emitNavigateUp(); + e->setAccepted(true); + return; + } + if( e->key() == Qt::Key_Down && e->modifiers() == Qt::AltModifier ) { + m_view->emitNavigateDown(); + e->setAccepted(true); + return; + } + if( e->key() == Qt::Key_Return && e->modifiers() == Qt::AltModifier ) { + m_view->emitNavigateAccept(); + e->setAccepted(true); + return; + } + if( e->key() == Qt::Key_Backspace && e->modifiers() == Qt::AltModifier ) { + m_view->emitNavigateBack(); + e->setAccepted(true); + return; + } + + if( e->key() == Qt::Key_Alt && m_view->completionWidget()->isCompletionActive() ) { + m_completionItemExpanded = m_view->completionWidget()->toggleExpanded(true); + m_view->completionWidget()->resetHadNavigation(); + m_altDownTime = QTime::currentTime(); + } + + // Note: AND'ing with is a quick hack to fix Key_Enter + const int key = e->key() | (e->modifiers() & Qt::ShiftModifier); + + if (m_view->isCompletionActive()) + { + if( key == Qt::Key_Enter || key == Qt::Key_Return ) { + m_view->completionWidget()->execute(); + e->accept(); + return; + } + } + + if( !doc()->isReadWrite() ) + { + e->ignore(); + return; + } + + if ((key == Qt::Key_Return) || (key == Qt::Key_Enter) || (key == Qt::SHIFT + Qt::Key_Return) || (key == Qt::SHIFT + Qt::Key_Enter)) + { + doReturn(); + e->accept(); + return; + } + + if (key == Qt::Key_Backspace || key == Qt::SHIFT + Qt::Key_Backspace) + { + //m_view->backspace(); + e->accept(); + + return; + } + + if (key == Qt::Key_Tab || key == Qt::SHIFT+Qt::Key_Backtab || key == Qt::Key_Backtab) + { + if(m_view->completionWidget()->isCompletionActive()) + { + e->accept(); + m_view->completionWidget()->tab(key != Qt::Key_Tab); + return; + } + + if( key == Qt::Key_Tab ) + { + uint tabHandling = doc()->config()->tabHandling(); + // convert tabSmart into tabInsertsTab or tabIndents: + if (tabHandling == KateDocumentConfig::tabSmart) + { + // multiple lines selected + if (m_view->selection() && !m_view->selectionRange().onSingleLine()) + { + tabHandling = KateDocumentConfig::tabIndents; + } + + // otherwise: take look at cursor position + else + { + // if the cursor is at or before the first non-space character + // or on an empty line, + // Tab indents, otherwise it inserts a tab character. + Kate::TextLine line = doc()->kateTextLine( m_cursor.line() ); + int first = line->firstChar(); + if (first < 0 || m_cursor.column() <= first) + tabHandling = KateDocumentConfig::tabIndents; + else + tabHandling = KateDocumentConfig::tabInsertsTab; + } + } + + if (tabHandling == KateDocumentConfig::tabInsertsTab) + doc()->typeChars( m_view, QString("\t") ); + else + doc()->indent( m_view->selection() ? m_view->selectionRange() : KTextEditor::Range(m_cursor.line(), 0, m_cursor.line(), 0), 1 ); + + e->accept(); + + return; + } + else if (doc()->config()->tabHandling() != KateDocumentConfig::tabInsertsTab) + { + // key == Qt::SHIFT+Qt::Key_Backtab || key == Qt::Key_Backtab + doc()->indent( m_view->selection() ? m_view->selectionRange() : KTextEditor::Range(m_cursor.line(), 0, m_cursor.line(), 0), -1 ); + e->accept(); + + return; + } + } + + if ( !(e->modifiers() & Qt::ControlModifier) && !e->text().isEmpty() && doc()->typeChars ( m_view, e->text() ) ) + { + e->accept(); + + return; + } + + // allow composition of AltGr + (q|2|3) on windows + static const int altGR = Qt::ControlModifier | Qt::AltModifier; + if( (e->modifiers() & altGR) == altGR && !e->text().isEmpty() && doc()->typeChars ( m_view, e->text() ) ) + { + e->accept(); + + return; + } + + e->ignore(); +} + +void KateViewInternal::keyReleaseEvent( QKeyEvent* e ) +{ + if( e->key() == Qt::Key_Alt && m_view->completionWidget()->isCompletionActive() && ((m_completionItemExpanded && (m_view->completionWidget()->hadNavigation() || m_altDownTime.msecsTo(QTime::currentTime()) > 300)) || (!m_completionItemExpanded && !m_view->completionWidget()->hadNavigation())) ) { + + m_view->completionWidget()->toggleExpanded(false, true); + } + + if ((e->modifiers() & Qt::SHIFT) == Qt::SHIFT) + { + m_shiftKeyPressed = true; + } + else + { + if (m_shiftKeyPressed) + { + m_shiftKeyPressed = false; + + if (m_selChangedByUser) + { + if (m_view->selection()) + QApplication::clipboard()->setText(m_view->selectionText (), QClipboard::Selection); + + m_selChangedByUser = false; + } + } + } + + e->ignore(); + return; +} + +void KateViewInternal::contextMenuEvent ( QContextMenuEvent * e ) +{ + // try to show popup menu + + QPoint p = e->pos(); + + if ( doc()->browserView() ) + { + m_view->contextMenuEvent( e ); + return; + } + + if ( e->reason() == QContextMenuEvent::Keyboard ) + { + makeVisible( m_displayCursor, 0 ); + p = cursorCoordinates(false); + p.rx() -= startX(); + } + else if ( ! m_view->selection() || m_view->config()->persistentSelection() ) + placeCursor( e->pos() ); + + // popup is a qguardedptr now + if (m_view->contextMenu()) { + m_view->spellingMenu()->setUseMouseForMisspelledRange((e->reason() == QContextMenuEvent::Mouse)); + m_view->contextMenu()->popup( mapToGlobal( p ) ); + e->accept (); + } +} + +void KateViewInternal::mousePressEvent( QMouseEvent* e ) +{ + switch (e->button()) + { + case Qt::LeftButton: + m_selChangedByUser = false; + + if (m_possibleTripleClick) + { + m_possibleTripleClick = false; + + m_selectionMode = Line; + + if ( e->modifiers() & Qt::ShiftModifier ) + { + updateSelection( m_cursor, true ); + } + else + { + m_view->selectLine( m_cursor ); + if (m_view->selection()) + m_selectAnchor = m_view->selectionRange().start(); + } + + if (m_view->selection()) + QApplication::clipboard()->setText(m_view->selectionText (), QClipboard::Selection); + + // Keep the line at the select anchor selected during further + // mouse selection + if ( m_selectAnchor.line() > m_view->selectionRange().start().line() ) + { + // Preserve the last selected line + if ( m_selectAnchor == m_view->selectionRange().end() && m_selectAnchor.column() == 0 ) + m_selectionCached.start().setPosition( m_selectAnchor.line()-1, 0 ); + else + m_selectionCached.start().setPosition( m_selectAnchor.line(), 0 ); + m_selectionCached.end() = m_view->selectionRange().end(); + } + else + { + // Preserve the first selected line + m_selectionCached.start() = m_view->selectionRange().start(); + if ( m_view->selectionRange().end().line() > m_view->selectionRange().start().line() ) + m_selectionCached.end().setPosition( m_view->selectionRange().start().line()+1, 0 ); + else + m_selectionCached.end() = m_view->selectionRange().end(); + } + + moveCursorToSelectionEdge(); + + m_scrollX = 0; + m_scrollY = 0; + m_scrollTimer.start (50); + + e->accept(); + return; + } + else if ( m_selectionMode == Default ) + { + m_selectionMode = Mouse; + } + + // request the software keyboard, if any + if ( e->button() == Qt::LeftButton && qApp->autoSipEnabled() ) + { + QStyle::RequestSoftwareInputPanel behavior = QStyle::RequestSoftwareInputPanel( style()->styleHint( QStyle::SH_RequestSoftwareInputPanel ) ); + if ( hasFocus() || behavior == QStyle::RSIP_OnMouseClick ) + { + QEvent event( QEvent::RequestSoftwareInputPanel ); + QApplication::sendEvent( this, &event ); + } + } + + if ( e->modifiers() & Qt::ShiftModifier ) + { + if ( !m_selectAnchor.isValid() ) + m_selectAnchor = m_cursor; + } + else + { + m_selectionCached = KTextEditor::Range::invalid(); + } + + if( !(e->modifiers() & Qt::ShiftModifier) && isTargetSelected( e->pos() ) ) + { + m_dragInfo.state = diPending; + m_dragInfo.start = e->pos(); + } + else + { + m_dragInfo.state = diNone; + + if ( e->modifiers() & Qt::ShiftModifier ) + { + placeCursor( e->pos(), true, false ); + if ( m_selectionCached.start().isValid() ) + { + if ( m_cursor.toCursor() < m_selectionCached.start() ) + m_selectAnchor = m_selectionCached.end(); + else + m_selectAnchor = m_selectionCached.start(); + } + setSelection( KTextEditor::Range( m_selectAnchor, m_cursor ) ); + } + else + { + placeCursor( e->pos() ); + } + + m_scrollX = 0; + m_scrollY = 0; + + m_scrollTimer.start (50); + } + + e->accept (); + break; + + default: + e->ignore (); + break; + } +} + +void KateViewInternal::mouseDoubleClickEvent(QMouseEvent *e) +{ + switch (e->button()) + { + case Qt::LeftButton: + m_selectionMode = Word; + + if ( e->modifiers() & Qt::ShiftModifier ) + { + KTextEditor::Range oldSelection = m_view->selectionRange(); + + // Now select the word under the select anchor + int cs, ce; + Kate::TextLine l = doc()->kateTextLine( m_selectAnchor.line() ); + + ce = m_selectAnchor.column(); + if ( ce > 0 && doc()->highlight()->isInWord( l->at(ce) ) ) { + for (; ce < l->length(); ce++ ) + if ( !doc()->highlight()->isInWord( l->at(ce) ) ) + break; + } + + cs = m_selectAnchor.column() - 1; + if ( cs < doc()->lineLength( m_selectAnchor.line() ) + && doc()->highlight()->isInWord( l->at(cs) ) ) { + for ( cs--; cs >= 0; cs-- ) + if ( !doc()->highlight()->isInWord( l->at(cs) ) ) + break; + } + + // ...and keep it selected + if (cs+1 < ce) + { + m_selectionCached.start().setPosition( m_selectAnchor.line(), cs+1 ); + m_selectionCached.end().setPosition( m_selectAnchor.line(), ce ); + } + else + { + m_selectionCached.start() = m_selectAnchor; + m_selectionCached.end() = m_selectAnchor; + } + // Now word select to the mouse cursor + placeCursor( e->pos(), true ); + } + else + { + // first clear the selection, otherwise we run into bug #106402 + // ...and set the cursor position, for the same reason (otherwise there + // are *other* idiosyncrasies we can't fix without reintroducing said + // bug) + // Parameters: don't redraw, and don't emit selectionChanged signal yet + m_view->clearSelection( false, false ); + placeCursor( e->pos() ); + m_view->selectWord( m_cursor ); + cursorToMatchingBracket(true); + + if (m_view->selection()) + { + m_selectAnchor = m_view->selectionRange().start(); + m_selectionCached = m_view->selectionRange(); + } + else + { + m_selectAnchor = m_cursor; + m_selectionCached = KTextEditor::Range(m_cursor, m_cursor); + } + } + + // Move cursor to end (or beginning) of selected word + if (m_view->selection()) + QApplication::clipboard()->setText( m_view->selectionText(), QClipboard::Selection ); + + moveCursorToSelectionEdge(); + m_possibleTripleClick = true; + QTimer::singleShot ( QApplication::doubleClickInterval(), this, SLOT(tripleClickTimeout()) ); + + m_scrollX = 0; + m_scrollY = 0; + + m_scrollTimer.start (50); + + e->accept (); + break; + + default: + e->ignore (); + break; + } +} + +void KateViewInternal::tripleClickTimeout() +{ + m_possibleTripleClick = false; +} + +void KateViewInternal::mouseReleaseEvent( QMouseEvent* e ) +{ + switch (e->button()) + { + case Qt::LeftButton: + m_selectionMode = Default; +// m_selectionCached.start().setLine( -1 ); + + if (m_selChangedByUser) + { + if (m_view->selection()) { + QApplication::clipboard()->setText(m_view->selectionText (), QClipboard::Selection); + } + moveCursorToSelectionEdge(); + + m_selChangedByUser = false; + } + + if (m_dragInfo.state == diPending) + placeCursor( e->pos(), e->modifiers() & Qt::ShiftModifier ); + else if (m_dragInfo.state == diNone) + m_scrollTimer.stop (); + + m_dragInfo.state = diNone; + + e->accept (); + break; + + case Qt::MiddleButton: + placeCursor( e->pos() ); + + if( doc()->isReadWrite() ) { + QString clipboard = QApplication::clipboard()->text(QClipboard::Selection); + m_view->paste( &clipboard ); + } + + e->accept (); + break; + + default: + e->ignore (); + break; + } +} + +void KateViewInternal::leaveEvent( QEvent* ) +{ + m_textHintTimer.stop(); + + // fix bug 194452, scrolling keeps going if you scroll via mouse drag and press and other mouse + // button outside the view area + if (m_dragInfo.state == diNone) + m_scrollTimer.stop (); +} + +KTextEditor::Cursor KateViewInternal::coordinatesToCursor(const QPoint& _coord, bool includeBorder) const +{ + QPoint coord(_coord); + + KTextEditor::Cursor ret = KTextEditor::Cursor::invalid(); + + if (includeBorder) coord.rx() -= m_leftBorder->width(); + coord.rx() += startX(); + + const KateTextLayout& thisLine = yToKateTextLayout(coord.y()); + if (thisLine.isValid()) + ret = renderer()->xToCursor(thisLine, coord.x(), !m_view->wrapCursor()); + + if (ret.column() == view()->document()->lineLength(ret.line())) { + // The cursor is beyond the end of the line; in that case the renderer + // gives the index of the character behind the last one. + return KTextEditor::Cursor::invalid(); + } + + return ret; +} + +void KateViewInternal::mouseMoveEvent( QMouseEvent* e ) +{ + KTextEditor::Cursor newPosition = coordinatesToCursor (e->pos(), false); + if (newPosition != m_mouse) { + m_mouse = newPosition; + mouseMoved(); + } + + if( e->buttons() & Qt::LeftButton ) + { + if (m_dragInfo.state == diPending) + { + // we had a mouse down, but haven't confirmed a drag yet + // if the mouse has moved sufficiently, we will confirm + QPoint p( e->pos() - m_dragInfo.start ); + + // we've left the drag square, we can start a real drag operation now + if( p.manhattanLength() > KGlobalSettings::dndEventDelay() ) + doDrag(); + + return; + } + else if (m_dragInfo.state == diDragging) + { + // Don't do anything after a canceled drag until the user lets go of + // the mouse button! + return; + } + + m_mouseX = e->x(); + m_mouseY = e->y(); + + m_scrollX = 0; + m_scrollY = 0; + int d = renderer()->lineHeight(); + + if (m_mouseX < 0) + m_scrollX = -d; + + if (m_mouseX > width()) + m_scrollX = d; + + if (m_mouseY < 0) + { + m_mouseY = 0; + m_scrollY = -d; + } + + if (m_mouseY > height()) + { + m_mouseY = height(); + m_scrollY = d; + } + + placeCursor( QPoint( m_mouseX, m_mouseY ), true ); + + } + else + { + if (isTargetSelected( e->pos() ) ) { + // mouse is over selected text. indicate that the text is draggable by setting + // the arrow cursor as other Qt text editing widgets do + if (m_mouseCursor != Qt::ArrowCursor) { + m_mouseCursor = Qt::ArrowCursor; + setCursor(m_mouseCursor); + } + } else { + // normal text cursor + if (m_mouseCursor != Qt::IBeamCursor) { + m_mouseCursor = Qt::IBeamCursor; + setCursor(m_mouseCursor); + } + } + //We need to check whether the mouse position is actually within the widget, + //because other widgets like the icon border forward their events to this, + //and we will create invalid text hint requests if we don't check + if (m_textHintEnabled && geometry().contains(parentWidget()->mapFromGlobal(e->globalPos()))) + { + if ( QToolTip::isVisible() ) { + QToolTip::hideText(); + } + m_textHintTimer.start(m_textHintTimeout); + m_textHintPos = e->pos(); + } + } +} + +void KateViewInternal::updateDirty( ) +{ + uint h = renderer()->lineHeight(); + + int currentRectStart = -1; + int currentRectEnd = -1; + + QRegion updateRegion; + + { + for (int i = 0; i < cache()->viewCacheLineCount(); ++i) { + if (cache()->viewLine(i).isDirty()) { + if (currentRectStart == -1) { + currentRectStart = h * i; + currentRectEnd = h; + } else { + currentRectEnd += h; + } + + } else if (currentRectStart != -1) { + updateRegion += QRect(0, currentRectStart, width(), currentRectEnd); + currentRectStart = -1; + currentRectEnd = -1; + } + } + } + + + if (currentRectStart != -1) + updateRegion += QRect(0, currentRectStart, width(), currentRectEnd); + + if (!updateRegion.isEmpty()) { + if (debugPainting) kDebug( 13030 ) << "Update dirty region " << updateRegion; + update(updateRegion); + } +} + +void KateViewInternal::hideEvent(QHideEvent* e) +{ + Q_UNUSED(e); + if(m_view->isCompletionActive()) + m_view->completionWidget()->abortCompletion(); +} + +void KateViewInternal::paintEvent(QPaintEvent *e) +{ + if (debugPainting) kDebug (13030) << "GOT PAINT EVENT: Region" << e->region(); + + const QRect& unionRect = e->rect(); + + int xStart = startX() + unionRect.x(); + int xEnd = xStart + unionRect.width(); + uint h = renderer()->lineHeight(); + uint startz = (unionRect.y() / h); + uint endz = startz + 1 + (unionRect.height() / h); + uint lineRangesSize = cache()->viewCacheLineCount(); + + QPainter paint(this); + paint.setRenderHints (QPainter::Antialiasing); + + paint.save(); + + setCaretStyle( m_view->isOverwriteMode() ? KateRenderer::Block : KateRenderer::Line ); + renderer()->setShowTabs(doc()->config()->showTabs()); + renderer()->setShowTrailingSpaces(doc()->config()->showSpaces()); + + int sy = startz * h; + paint.translate(unionRect.x(), startz * h); + + for (uint z=startz; z <= endz; z++) + { + if ( (z >= lineRangesSize) || (cache()->viewLine(z).line() == -1) ) + { + if (!(z >= lineRangesSize)) + cache()->viewLine(z).setDirty(false); + + paint.fillRect( 0, 0, unionRect.width(), h, renderer()->config()->backgroundColor() ); + } + else + { + //kDebug( 13030 )<<"KateViewInternal::paintEvent(QPaintEvent *e):cache()->viewLine"<viewLine(z); + + /* If viewLine() returns non-zero, then a document line was split + in several visual lines, and we're trying to paint visual line + that is not the first. In that case, this line was already + painted previously, since KateRenderer::paintTextLine paints + all visual lines. + Except if we're at the start of the region that needs to + be painted -- when no previous calls to paintTextLine were made. + */ + if (!thisLine.viewLine() || z == startz) { + // Don't bother if we're not in the requested update region + if (!e->region().contains(QRect(unionRect.x(), startz * h, unionRect.width(), h))) + continue; + + //kDebug (13030) << "paint text: line: " << thisLine.line() << " viewLine " << thisLine.viewLine() << " x: " << unionRect.x() << " y: " << sy + // << " width: " << xEnd-xStart << " height: " << h << endl; + + if (thisLine.viewLine()) + paint.translate(QPoint(0, h * - thisLine.viewLine())); + + // The paintTextLine function should be well behaved, but if not, this clipping may be needed + //paint.setClipRect(QRect(xStart, 0, xEnd - xStart, h * (thisLine.kateLineLayout()->viewLineCount()))); + + KTextEditor::Cursor pos = m_cursor; + renderer()->paintTextLine(paint, thisLine.kateLineLayout(), xStart, xEnd, &pos); + + //paint.setClipping(false); + + if (thisLine.viewLine()) + paint.translate(0, h * thisLine.viewLine()); + + thisLine.setDirty(false); + } + } + + paint.translate(0, h); + sy += h; + } + + paint.restore(); + if (m_textAnimation) + m_textAnimation->draw(paint); +} + +void KateViewInternal::resizeEvent(QResizeEvent* e) +{ + bool expandedHorizontally = width() > e->oldSize().width(); + bool expandedVertically = height() > e->oldSize().height(); + bool heightChanged = height() != e->oldSize().height(); + + m_dummy->setFixedSize(m_lineScroll->width(), m_columnScroll->sizeHint().height()); + m_madeVisible = false; + + if (heightChanged) { + setAutoCenterLines(m_autoCenterLines, false); + m_cachedMaxStartPos.setPosition(-1, -1); + } + + if (m_view->dynWordWrap()) { + bool dirtied = false; + + for (int i = 0; i < cache()->viewCacheLineCount(); i++) { + // find the first dirty line + // the word wrap updateView algorithm is forced to check all lines after a dirty one + KateTextLayout viewLine = cache()->viewLine(i); + + if (viewLine.wrap() || viewLine.isRightToLeft() || viewLine.width() > width()) { + dirtied = true; + viewLine.setDirty(); + break; + } + } + + if (dirtied || heightChanged) { + updateView(true); + m_leftBorder->update(); + } + } else { + updateView(); + + if (expandedHorizontally && startX() > 0) + scrollColumns(startX() - (width() - e->oldSize().width())); + } + + if (width() < e->oldSize().width() && !m_view->wrapCursor()) { + // May have to restrain cursor to new smaller width... + if (m_cursor.column() > doc()->lineLength(m_cursor.line())) { + KateTextLayout thisLine = currentLayout(); + + KTextEditor::Cursor newCursor(m_cursor.line(), thisLine.endCol() + ((width() - thisLine.xOffset() - (thisLine.width()- m_startX)) / renderer()->spaceWidth()) - 1); + if (newCursor.column() < m_cursor.column()) + updateCursor(newCursor); + } + } + + + if (expandedVertically) { + KTextEditor::Cursor max = maxStartPos(); + if (startPos() > max) { + scrollPos(max); + return; // already fired displayRangeChanged + } + } + emit m_view->displayRangeChanged(m_view); +} + +void KateViewInternal::scrollTimeout () +{ + if (m_scrollX || m_scrollY) + { + scrollLines (startPos().line() + (m_scrollY / (int) renderer()->lineHeight())); + placeCursor( QPoint( m_mouseX, m_mouseY ), true ); + } +} + +void KateViewInternal::cursorTimeout () +{ + if (!debugPainting) { + renderer()->setDrawCaret(!renderer()->drawCaret()); + paintCursor(); + } +} + +void KateViewInternal::textHintTimeout () +{ + m_textHintTimer.stop (); + + KTextEditor::Cursor c = coordinatesToCursor(m_textHintPos, false); + if (!c.isValid()) return; + + QString tmp; + + emit m_view->needTextHint(c, tmp); + + if (!tmp.isEmpty()) { + kDebug(13030) << "Hint text: " << tmp; + QPoint pos(startX() + m_textHintPos.x(), m_textHintPos.y()); + QToolTip::showText(mapToGlobal(pos), tmp); + } +} + +void KateViewInternal::focusInEvent (QFocusEvent *) +{ + if (KApplication::cursorFlashTime() > 0) + m_cursorTimer.start ( KApplication::cursorFlashTime() / 2 ); + + paintCursor(); + + doc()->setActiveView( m_view ); + + // this will handle focus stuff in kateview + m_view->slotGotFocus (); +} + +void KateViewInternal::focusOutEvent (QFocusEvent *) +{ + //if (m_view->isCompletionActive()) + //m_view->abortCompletion(); + + m_cursorTimer.stop(); + m_view->renderer()->setDrawCaret(true); + paintCursor(); + + m_textHintTimer.stop(); + + m_view->slotLostFocus (); +} + +void KateViewInternal::doDrag() +{ + m_dragInfo.state = diDragging; + m_dragInfo.dragObject = new QDrag(this); + QMimeData *mimeData=new QMimeData(); + mimeData->setText(m_view->selectionText()); + m_dragInfo.dragObject->setMimeData(mimeData); + m_dragInfo.dragObject->start(Qt::MoveAction); +} + +void KateViewInternal::dragEnterEvent( QDragEnterEvent* event ) +{ + if (event->source()==this) event->setDropAction(Qt::MoveAction); + event->setAccepted( (event->mimeData()->hasText() && doc()->isReadWrite()) || + KUrl::List::canDecode(event->mimeData()) ); +} + +void KateViewInternal::fixDropEvent(QDropEvent* event) { + if (event->source()!=this) event->setDropAction(Qt::CopyAction); + else { + Qt::DropAction action=Qt::MoveAction; + if (event->keyboardModifiers() & Qt::ControlModifier) + action = Qt::CopyAction; + event->setDropAction(action); + } +} + +void KateViewInternal::dragMoveEvent( QDragMoveEvent* event ) +{ + // track the cursor to the current drop location + placeCursor( event->pos(), true, false ); + + // important: accept action to switch between copy and move mode + // without this, the text will always be copied. + fixDropEvent(event); +} + +void KateViewInternal::dropEvent( QDropEvent* event ) +{ + if ( KUrl::List::canDecode(event->mimeData()) ) { + + emit dropEventPass(event); + + } else if ( event->mimeData()->hasText() && doc()->isReadWrite() ) { + + QString text=event->mimeData()->text(); + + // is the source our own document? + bool priv = false; + if (KateViewInternal* vi = qobject_cast(event->source())) + priv = doc()->ownedView( vi->m_view ); + + // dropped on a text selection area? + bool selected = m_view->cursorSelected(m_cursor); + + fixDropEvent(event); + + if( priv && selected && event->dropAction() != Qt::CopyAction ) { + // this is a drag that we started and dropped on our selection + // ignore this case + return; + } + + // fix the cursor position before editStart(), so that it is correctly + // stored for the undo action + KTextEditor::Cursor targetCursor(m_cursor); // backup current cursor + int selectionWidth = m_view->selectionRange().columnWidth(); // for block selection + int selectionHeight = m_view->selectionRange().numberOfLines(); // for block selection + + if ( event->dropAction() != Qt::CopyAction ) { + editSetCursor(m_view->selectionRange().end()); + } else { + m_view->clearSelection(); + } + + // use one transaction + doc()->editStart (); + + // on move: remove selected text; on copy: duplicate text + doc()->insertText(targetCursor, text, m_view->blockSelection()); + + Kate::TextCursor startCursor(doc()->buffer(), targetCursor, KTextEditor::MovingCursor::MoveOnInsert); + + if ( event->dropAction() != Qt::CopyAction ) + m_view->removeSelectedText(); + + Kate::TextCursor endCursor1(doc()->buffer(), startCursor, KTextEditor::MovingCursor::MoveOnInsert); + + if ( !m_view->blockSelection() ) { + endCursor1.move(text.length()); + } else { + endCursor1.setColumn(startCursor.column()+selectionWidth); + endCursor1.setLine(startCursor.line()+selectionHeight); + } + + KTextEditor::Cursor endCursor(endCursor1); + kDebug( 13030 )<editEnd (); + + event->acceptProposedAction(); + updateView(); + } + + // finally finish drag and drop mode + m_dragInfo.state = diNone; + // important, because the eventFilter`s DragLeave does not occur + stopDragScroll(); +} +//END EVENT HANDLING STUFF + +void KateViewInternal::clear() +{ + m_startPos.setPosition (0, 0); + m_displayCursor = KTextEditor::Cursor(0, 0); + m_cursor.setPosition(0, 0); + cache()->clear(); + updateView(true); +} + +void KateViewInternal::wheelEvent(QWheelEvent* e) +{ + // zoom text, if ctrl is pressed + if (e->modifiers() == Qt::ControlModifier) { + if (e->delta() > 0) { + slotIncFontSizes(); + } else { + slotDecFontSizes(); + } + e->accept(); + return; + } + + // scroll up/down or left/right, if possible + if (m_lineScroll->minimum() != m_lineScroll->maximum() && e->orientation() != Qt::Horizontal) { + // React to this as a vertical event + if ( e->modifiers() & Qt::ShiftModifier ) { + if (e->delta() > 0) + scrollPrevPage(); + else + scrollNextPage(); + } else { + const int scrollLines = QApplication::wheelScrollLines(); + scrollViewLines(e->delta() > 0 ? -scrollLines : scrollLines); + e->accept(); + return; + } + + } else if (columnScrollingPossible()) { + QWheelEvent copy = *e; + QApplication::sendEvent(m_columnScroll, ©); + + } else { + e->ignore(); + } +} + +void KateViewInternal::startDragScroll() +{ + if ( !m_dragScrollTimer.isActive() ) { + m_dragScrollTimer.start( s_scrollTime ); + } +} + +void KateViewInternal::stopDragScroll() +{ + m_dragScrollTimer.stop(); + updateView(); +} + +void KateViewInternal::doDragScroll() +{ + QPoint p = this->mapFromGlobal( QCursor::pos() ); + + int dx = 0, dy = 0; + if ( p.y() < s_scrollMargin ) { + dy = p.y() - s_scrollMargin; + } else if ( p.y() > height() - s_scrollMargin ) { + dy = s_scrollMargin - (height() - p.y()); + } + + if ( p.x() < s_scrollMargin ) { + dx = p.x() - s_scrollMargin; + } else if ( p.x() > width() - s_scrollMargin ) { + dx = s_scrollMargin - (width() - p.x()); + } + + dy /= 4; + + if (dy) + scrollLines(startPos().line() + dy); + + if (columnScrollingPossible () && dx) + scrollColumns(qMin (m_startX + dx, m_columnScroll->maximum())); + + if (!dy && !dx) + stopDragScroll(); +} + +void KateViewInternal::enableTextHints(int timeout) +{ + if (timeout >= 0) { + m_textHintTimeout = timeout; + m_textHintEnabled = true; + m_textHintTimer.start(timeout); + } else { + kWarning() << "Attempt to enable text hints with negative timeout:" << timeout; + } +} + +void KateViewInternal::disableTextHints() +{ + if (m_textHintEnabled) { + m_textHintEnabled = false; + m_textHintTimer.stop (); + } +} + +//BEGIN EDIT STUFF +void KateViewInternal::editStart() +{ + editSessionNumber++; + + if (editSessionNumber > 1) + return; + + editIsRunning = true; + editOldCursor = m_cursor; + editOldSelection = m_view->selectionRange(); +} + +void KateViewInternal::editEnd(int editTagLineStart, int editTagLineEnd, bool tagFrom) +{ + if (editSessionNumber == 0) + return; + + editSessionNumber--; + + if (editSessionNumber > 0) + return; + + // fix start position, might have moved from column 0 + // try to clever calculate the right start column for the tricky dyn word wrap case + int col = 0; + if (m_view->dynWordWrap()) { + if (KateLineLayoutPtr layout = cache()->line(m_startPos.line())) { + int index = layout->viewLineForColumn (m_startPos.column()); + if (index >= 0 && index < layout->viewLineCount()) + col = layout->viewLine (index).startCol(); + } + } + m_startPos.setPosition (m_startPos.line(), col); + + if (tagFrom && (editTagLineStart <= int(m_view->textFolding().visibleLineToLine(startLine())))) + tagAll(); + else + tagLines (editTagLineStart, tagFrom ? qMax(doc()->lastLine() + 1, editTagLineEnd) : editTagLineEnd, true); + + if (editOldCursor == m_cursor.toCursor()) + updateBracketMarks(); + + updateView(true); + + if (editOldCursor != m_cursor.toCursor() || m_view == doc()->activeView()) + { + // Only scroll the view to the cursor if the insertion happens at the cursor. + // This might not be the case for e.g. collaborative editing, when a remote user + // inserts text at a position not at the caret. + if ( m_cursor.line() >= editTagLineStart && m_cursor.line() <= editTagLineEnd ) { + m_madeVisible = false; + updateCursor ( m_cursor, true ); + } + } + + /** + * selection changed? + * fixes bug 316226 + */ + if (editOldSelection != m_view->selectionRange() + || (editOldSelection.isValid() && !editOldSelection.isEmpty() && !(editTagLineStart > editOldSelection.end().line() && editTagLineEnd < editOldSelection.start().line()))) + emit m_view->selectionChanged (m_view); + + editIsRunning = false; +} + +void KateViewInternal::editSetCursor (const KTextEditor::Cursor &_cursor) +{ + if (m_cursor.toCursor() != _cursor) + { + m_cursor.setPosition(_cursor); + } +} +//END + +void KateViewInternal::viewSelectionChanged () +{ + if (!m_view->selection()) { + m_selectAnchor = KTextEditor::Cursor::invalid(); + } else { + m_selectAnchor = m_view->selectionRange().start(); + } + // Do NOT nuke the entire range! The reason is that a shift+DC selection + // might (correctly) set the range to be empty (i.e. start() == end()), and + // subsequent dragging might shrink the selection into non-existence. When + // this happens, we use the cached end to restore the cached start so that + // updateSelection is not confused. See also comments in updateSelection. + m_selectionCached.start() = KTextEditor::Cursor::invalid(); +} + +KateLayoutCache* KateViewInternal::cache( ) const +{ + return m_layoutCache; +} + +KTextEditor::Cursor KateViewInternal::toRealCursor( const KTextEditor::Cursor & virtualCursor ) const +{ + return KTextEditor::Cursor(m_view->textFolding().visibleLineToLine(virtualCursor.line()), virtualCursor.column()); +} + +KTextEditor::Cursor KateViewInternal::toVirtualCursor( const KTextEditor::Cursor & realCursor ) const +{ + /** + * only convert valid lines, folding doesn't like invalid input! + * don't validate whole cursor, column might be -1 + */ + if (realCursor.line() < 0) + return KTextEditor::Cursor::invalid (); + + return KTextEditor::Cursor(m_view->textFolding().lineToVisibleLine(realCursor.line()), realCursor.column()); +} + +KateRenderer * KateViewInternal::renderer( ) const +{ + return m_view->renderer(); +} + +void KateViewInternal::mouseMoved( ) +{ + m_view->notifyMousePositionChanged(m_mouse); + m_view->updateRangesIn (KTextEditor::Attribute::ActivateMouseIn); +} + +void KateViewInternal::cursorMoved( ) +{ + m_view->updateRangesIn (KTextEditor::Attribute::ActivateCaretIn); +} + +bool KateViewInternal::rangeAffectsView(const KTextEditor::Range& range, bool realCursors) const +{ + int startLine = m_startPos.line(); + int endLine = startLine + (int)m_visibleLineCount; + + if ( realCursors ) { + startLine = (int)m_view->textFolding().visibleLineToLine(startLine); + endLine = (int)m_view->textFolding().visibleLineToLine(endLine); + } + + return (range.end().line() >= startLine) || (range.start().line() <= endLine); +} + +//BEGIN IM INPUT STUFF +#ifndef QT_KATIE +QVariant KateViewInternal::inputMethodQuery ( Qt::InputMethodQuery query ) const +{ + switch (query) { + case Qt::ImMicroFocus: { + // Cursor placement code is changed for Asian input method that + // shows candidate window. This behavior is same as Qt/E 2.3.7 + // which supports Asian input methods. Asian input methods need + // start point of IM selection text to place candidate window as + // adjacent to the selection text. + return QRect (cursorToCoordinate(m_cursor, true, false), QSize(0, renderer()->lineHeight())); + } + + case Qt::ImFont: + return renderer()->currentFont(); + + case Qt::ImCursorPosition: + return m_cursor.column(); + + case Qt::ImAnchorPosition: + // If selectAnchor is at the same line, return the real anchor position + // Otherwise return the same position of cursor + if (m_view->selection() && m_selectAnchor.line() == m_cursor.line()) + return m_selectAnchor.column(); + else + return m_cursor.column(); + + case Qt::ImSurroundingText: + if (Kate::TextLine l = doc()->kateTextLine(m_cursor.line())) + return l->string(); + else + return QString(); + + case Qt::ImCurrentSelection: + if (m_view->selection()) + return m_view->selectionText(); + else + return QString(); + default: + /* values: ImMaximumTextLength */ + break; + } + + return QWidget::inputMethodQuery(query); +} + +void KateViewInternal::inputMethodEvent(QInputMethodEvent* e) +{ + if ( doc()->readOnly() ) { + e->ignore(); + return; + } + + //kDebug( 13030 ) << "Event: cursor" << m_cursor << "commit" << e->commitString() << "preedit" << e->preeditString() << "replacement start" << e->replacementStart() << "length" << e->replacementLength(); + + if (!m_imPreeditRange) { + m_imPreeditRange = doc()->newMovingRange (KTextEditor::Range(m_cursor, m_cursor), KTextEditor::MovingRange::ExpandLeft | KTextEditor::MovingRange::ExpandRight); + } + + if (!m_imPreeditRange->toRange().isEmpty()) { + doc()->inputMethodStart(); + doc()->removeText(*m_imPreeditRange); + doc()->inputMethodEnd(); + } + + if (!e->commitString().isEmpty() || e->replacementLength()) { + m_view->removeSelectedText(); + + KTextEditor::Range preeditRange = *m_imPreeditRange; + + KTextEditor::Cursor start(m_imPreeditRange->start().line(), m_imPreeditRange->start().column() + e->replacementStart()); + KTextEditor::Cursor removeEnd = start + KTextEditor::Cursor(0, e->replacementLength()); + + doc()->editStart(); + if (start != removeEnd) + doc()->removeText(KTextEditor::Range(start, removeEnd)); + if (!e->commitString().isEmpty()) { + // if the input method event is text that should be inserted, call KateDocument::typeChars() + // with the text. that method will handle the input and take care of overwrite mode, etc. + doc()->typeChars(m_view, e->commitString()); + } + doc()->editEnd(); + + // Revert to the same range as above + m_imPreeditRange->setRange(preeditRange); + } + + if (!e->preeditString().isEmpty()) { + doc()->inputMethodStart(); + doc()->insertText(m_imPreeditRange->start(), e->preeditString()); + doc()->inputMethodEnd(); + // The preedit range gets automatically repositioned + } + + // Finished this input method context? + if (m_imPreeditRange && e->preeditString().isEmpty()) { + // delete the range and reset the pointer + delete m_imPreeditRange; + m_imPreeditRange = 0L; + qDeleteAll (m_imPreeditRangeChildren); + m_imPreeditRangeChildren.clear (); + + if ( KApplication::cursorFlashTime() > 0 ) + renderer()->setDrawCaret(false); + renderer()->setCaretOverrideColor(QColor()); + + e->accept(); + return; + } + + KTextEditor::Cursor newCursor = m_cursor; + bool hideCursor = false; + QColor caretColor; + + if (m_imPreeditRange) { + qDeleteAll (m_imPreeditRangeChildren); + m_imPreeditRangeChildren.clear (); + + int decorationColumn = 0; + foreach (const QInputMethodEvent::Attribute &a, e->attributes()) { + if (a.type == QInputMethodEvent::Cursor) { + newCursor = m_imPreeditRange->start() + KTextEditor::Cursor(0, a.start); + hideCursor = !a.length; + QColor c = qvariant_cast(a.value); + if (c.isValid()) + caretColor = c; + + } else if (a.type == QInputMethodEvent::TextFormat) { + QTextCharFormat f = qvariant_cast(a.value).toCharFormat(); + if (f.isValid() && decorationColumn <= a.start) { + KTextEditor::Range fr(m_imPreeditRange->start().line(), m_imPreeditRange->start().column() + a.start, m_imPreeditRange->start().line(), m_imPreeditRange->start().column() + a.start + a.length); + KTextEditor::MovingRange* formatRange = doc()->newMovingRange (fr); + KTextEditor::Attribute::Ptr attribute(new KTextEditor::Attribute()); + attribute->merge(f); + formatRange->setAttribute(attribute); + decorationColumn = a.start + a.length; + m_imPreeditRangeChildren.push_back (formatRange); + } + } + } + } + + renderer()->setDrawCaret(hideCursor); + renderer()->setCaretOverrideColor(caretColor); + + if (newCursor != m_cursor.toCursor()) + updateCursor(newCursor); + + e->accept(); +} +#endif +//END IM INPUT STUFF + +void KateViewInternal::flashChar(const KTextEditor::Cursor & pos, KTextEditor::Attribute::Ptr attribute) +{ + Q_ASSERT(pos.isValid()); + Q_ASSERT(!attribute.isNull()); + + // if line is folded away, do nothing + if (!m_view->textFolding().isLineVisible (pos.line())) { + return; + } + + KTextEditor::Range range(pos, KTextEditor::Cursor(pos.line(), pos.column() + 1)); + if (m_textAnimation) m_textAnimation->deleteLater(); + m_textAnimation = new KateTextAnimation(range, attribute, this); +} + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/part/view/kateviewinternal.h b/kate/part/view/kateviewinternal.h new file mode 100644 index 00000000..052244bb --- /dev/null +++ b/kate/part/view/kateviewinternal.h @@ -0,0 +1,430 @@ +/* This file is part of the KDE libraries + Copyright (C) 2002-2007 Hamish Rodda + Copyright (C) 2002 John Firebaugh + Copyright (C) 2002 Joseph Wenninger + Copyright (C) 2002 Christoph Cullmann + Copyright (C) 2007 Mirko Stocker + + Based on: + KWriteView : Copyright (C) 1999 Jochen Wilhelmy + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef _KATE_VIEW_INTERNAL_ +#define _KATE_VIEW_INTERNAL_ + +#include + +#include "katetextcursor.h" +#include "katelinelayout.h" +#include "katetextline.h" +#include "katedocument.h" +#include "kateview.h" +#include "katerenderer.h" + +#include +#include +#include +#include +#include +#include + +namespace KTextEditor { + class MovingRange; +} + +class KateIconBorder; +class KateScrollBar; +class KateTextLayout; +class KateTextAnimation; + +#include + +class KateViewInternal : public QWidget +{ + Q_OBJECT + + friend class KateView; + friend class KateIconBorder; + friend class KateScrollBar; + friend class CalculatingCursor; + friend class BoundedCursor; + friend class WrappingCursor; + friend class KateViModeBase; + + public: + enum Bias + { + left = -1, + none = 0, + right = 1 + }; + + public: + KateViewInternal ( KateView *view ); + ~KateViewInternal (); + KateView *view() const { return m_view; } + + //BEGIN EDIT STUFF + public: + void editStart (); + void editEnd (int editTagLineStart, int editTagLineEnd, bool tagFrom); + + void editSetCursor (const KTextEditor::Cursor &cursor); + + private: + uint editSessionNumber; + bool editIsRunning; + KTextEditor::Cursor editOldCursor; + KTextEditor::Range editOldSelection; + //END + + //BEGIN TAG & CLEAR & UPDATE STUFF + public: + bool tagLine (const KTextEditor::Cursor& virtualCursor); + + bool tagLines (int start, int end, bool realLines = false); + // cursors not const references as they are manipulated within + bool tagLines (KTextEditor::Cursor start, KTextEditor::Cursor end, bool realCursors = false); + + bool tagRange(const KTextEditor::Range& range, bool realCursors); + + void tagAll (); + + void updateDirty(); + + void clear (); + //END + + private Q_SLOTS: + // Updates the view and requests a redraw. + void updateView (bool changed = false, int viewLinesScrolled = 0); + + private: + // Actually performs the updating, but doesn't call update(). + void doUpdateView(bool changed = false, int viewLinesScrolled = 0); + void makeVisible (const KTextEditor::Cursor& c, int endCol, bool force = false, bool center = false, bool calledExternally = false); + + public: + // Start Position is a virtual cursor + KTextEditor::Cursor startPos() const { return m_startPos; } + int startLine () const { return m_startPos.line(); } + int startX () const { return m_startX; } + + KTextEditor::Cursor endPos () const; + int endLine () const; + + KateTextLayout yToKateTextLayout(int y) const; + + void prepareForDynWrapChange(); + void dynWrapChanged(); + + public Q_SLOTS: + void slotIncFontSizes(); + void slotDecFontSizes(); + + private Q_SLOTS: + void scrollLines(int line); // connected to the sliderMoved of the m_lineScroll + void scrollViewLines(int offset); + void scrollAction(int action); + void scrollNextPage(); + void scrollPrevPage(); + void scrollPrevLine(); + void scrollNextLine(); + void scrollColumns (int x); // connected to the valueChanged of the m_columnScroll + void viewSelectionChanged (); + + public: + void doReturn(); + void doSmartNewline(); + void doDelete(); + void doBackspace(); + void doTabulator(); + void doTranspose(); + void doDeletePrevWord(); + void doDeleteNextWord(); + + /** + * Set the caret's style. + * The caret can be a box or a line; see the documentation + * of KateRenderer::caretStyles for other options. + * @param style the caret style + * @param repaint whether to update the caret instantly. + * This also resets the caret's timer. + */ + void setCaretStyle( KateRenderer::caretStyles style, bool repaint = false ); + void cursorPrevChar(bool sel=false); + void cursorNextChar(bool sel=false); + void wordPrev(bool sel=false); + void wordNext(bool sel=false); + void home(bool sel=false); + void end(bool sel=false); + void cursorUp(bool sel=false); + void cursorDown(bool sel=false); + void cursorToMatchingBracket(bool sel=false); + void scrollUp(); + void scrollDown(); + void topOfView(bool sel=false); + void bottomOfView(bool sel=false); + void pageUp(bool sel=false, bool half=false); + void pageDown(bool sel=false, bool half=false); + void top(bool sel=false); + void bottom(bool sel=false); + void top_home(bool sel=false); + void bottom_end(bool sel=false); + + KTextEditor::Cursor getCursor() const { return m_cursor; } + KTextEditor::Cursor getMouse() const { return m_mouse; } + + QPoint cursorToCoordinate(const KTextEditor::Cursor& cursor, bool realCursor = true, bool includeBorder = true) const; + // by default, works on coordinates of the whole widget, eg. offsetted by the border + KTextEditor::Cursor coordinatesToCursor(const QPoint& coord, bool includeBorder = true) const; + QPoint cursorCoordinates(bool includeBorder = true) const; + KTextEditor::Cursor findMatchingBracket(); + + // EVENT HANDLING STUFF - IMPORTANT + private: + void fixDropEvent(QDropEvent *event); + protected: + virtual void hideEvent(QHideEvent* e); + virtual void paintEvent(QPaintEvent *e); + virtual bool eventFilter( QObject *obj, QEvent *e ); + virtual void keyPressEvent( QKeyEvent* ); + virtual void keyReleaseEvent( QKeyEvent* ); + virtual void resizeEvent( QResizeEvent* ); + virtual void mousePressEvent( QMouseEvent* ); + virtual void mouseDoubleClickEvent( QMouseEvent* ); + virtual void mouseReleaseEvent( QMouseEvent* ); + virtual void mouseMoveEvent( QMouseEvent* ); + virtual void leaveEvent( QEvent* ); + virtual void dragEnterEvent( QDragEnterEvent* ); + virtual void dragMoveEvent( QDragMoveEvent* ); + virtual void dropEvent( QDropEvent* ); + virtual void showEvent ( QShowEvent *); + virtual void wheelEvent(QWheelEvent* e); + virtual void focusInEvent (QFocusEvent *); + virtual void focusOutEvent (QFocusEvent *); +#ifndef QT_KATIE + virtual void inputMethodEvent(QInputMethodEvent* e); +#endif + + void contextMenuEvent ( QContextMenuEvent * e ); + + private Q_SLOTS: + void tripleClickTimeout(); + + Q_SIGNALS: + // emitted when KateViewInternal is not handling its own URI drops + void dropEventPass(QDropEvent*); + + private Q_SLOTS: + void slotRegionVisibilityChanged(); + void slotRegionBeginEndAddedRemoved(unsigned int); + + private: + void moveChar( Bias bias, bool sel ); + void moveEdge( Bias bias, bool sel ); + KTextEditor::Cursor maxStartPos(bool changed = false); + void scrollPos(KTextEditor::Cursor& c, bool force = false, bool calledExternally = false); + void scrollLines( int lines, bool sel ); + + int linesDisplayed() const; + + int lineToY(int viewLine) const; + + void updateSelection( const KTextEditor::Cursor&, bool keepSel ); + void setSelection( const KTextEditor::Range& ); + void moveCursorToSelectionEdge(); + void updateCursor( const KTextEditor::Cursor& newCursor, bool force = false, bool center = false, bool calledExternally = false ); + void updateBracketMarks(); + + void paintCursor(); + + void placeCursor( const QPoint& p, bool keepSelection = false, bool updateSelection = true ); + bool isTargetSelected( const QPoint& p ); + //Returns whether the given range affects the area currently visible in the view + bool rangeAffectsView(const KTextEditor::Range& range, bool realCursors) const; + + void doDrag(); + + KateRenderer* renderer() const; + + KateView *m_view; + class KateIconBorder *m_leftBorder; + + int m_mouseX; + int m_mouseY; + int m_scrollX; + int m_scrollY; + + Qt::CursorShape m_mouseCursor; + + Kate::TextCursor m_cursor; + KTextEditor::Cursor m_mouse; + KTextEditor::Cursor m_displayCursor; + + bool m_possibleTripleClick; + + //Whether the current completion-item was expanded while the last press of ALT + bool m_completionItemExpanded; + QTime m_altDownTime; + + // Bracket mark and corresponding decorative ranges + KTextEditor::MovingRange *m_bm, *m_bmStart, *m_bmEnd; + KTextEditor::MovingCursor * m_bmLastFlashPos; + void updateBracketMarkAttributes(); + + enum DragState { diNone, diPending, diDragging }; + + struct _dragInfo { + DragState state; + QPoint start; + QDrag* dragObject; + } m_dragInfo; + + // + // line scrollbar + first visible (virtual) line in the current view + // + KateScrollBar *m_lineScroll; + QWidget* m_dummy; + + // These are now cursors to account for word-wrap. + // Start Position is a virtual cursor + Kate::TextCursor m_startPos; + //Count of lines that are visible behind m_startPos. + //This does not respect dynamic word wrap, so take it as an approximation. + uint m_visibleLineCount; + + // This is set to false on resize or scroll (other than that called by makeVisible), + // so that makeVisible is again called when a key is pressed and the cursor is in the same spot + bool m_madeVisible; + bool m_shiftKeyPressed; + + // How many lines to should be kept visible above/below the cursor when possible + void setAutoCenterLines(int viewLines, bool updateView = true); + int m_autoCenterLines; + int m_minLinesVisible; + + // + // column scrollbar + x position + // + QScrollBar *m_columnScroll; + int m_startX; + + // has selection changed while your mouse or shift key is pressed + bool m_selChangedByUser; + KTextEditor::Cursor m_selectAnchor; + + enum SelectionMode { Default=0, Mouse, Word, Line }; ///< for drag selection. + uint m_selectionMode; + // when drag selecting after double/triple click, keep the initial selected + // word/line independent of direction. + // They get set in the event of a double click, and is used with mouse move + leftbutton + KTextEditor::Range m_selectionCached; + + // maximal length of textlines visible from given startLine + int maxLen(int startLine); + + // are we allowed to scroll columns? + bool columnScrollingPossible (); + + // returns the maximum X value / col value a cursor can take for a specific line range + int lineMaxCursorX(const KateTextLayout& line); + int lineMaxCol(const KateTextLayout& line); + + class KateLayoutCache* cache() const; + KateLayoutCache* m_layoutCache; + + // convenience methods + KateTextLayout currentLayout() const; + KateTextLayout previousLayout() const; + KateTextLayout nextLayout() const; + + // find the cursor offset by (offset) view lines from a cursor. + // when keepX is true, the column position will be calculated based on the x + // position of the specified cursor. + KTextEditor::Cursor viewLineOffset(const KTextEditor::Cursor& virtualCursor, int offset, bool keepX = false); + + KTextEditor::Cursor toRealCursor(const KTextEditor::Cursor& virtualCursor) const; + KTextEditor::Cursor toVirtualCursor(const KTextEditor::Cursor& realCursor) const; + + // These variable holds the most recent maximum real & visible column number + bool m_preserveX; + int m_preservedX; + + int m_wrapChangeViewLine; + KTextEditor::Cursor m_cachedMaxStartPos; + + // + // implementation details for KTextEditor::FlashTextInterface + // + public: + void flashChar(const KTextEditor::Cursor & pos, KTextEditor::Attribute::Ptr attribute); + private: + QPointer m_textAnimation; + + private Q_SLOTS: + void doDragScroll(); + void startDragScroll(); + void stopDragScroll(); + + private: + // Timers + QTimer m_dragScrollTimer; + QTimer m_scrollTimer; + QTimer m_cursorTimer; + QTimer m_textHintTimer; + + static const int s_scrollTime = 30; + static const int s_scrollMargin = 16; + + private Q_SLOTS: + void scrollTimeout (); + void cursorTimeout (); + void textHintTimeout (); + + //TextHint + public: + void enableTextHints(int timeout); + void disableTextHints(); + + private: + bool m_textHintEnabled; + int m_textHintTimeout; + QPoint m_textHintPos; + + /** + * IM input stuff + */ +#ifndef QT_KATIE + public: + virtual QVariant inputMethodQuery(Qt::InputMethodQuery query) const; +#endif + + private: + KTextEditor::MovingRange *m_imPreeditRange; + QList m_imPreeditRangeChildren; + + void mouseMoved(); + void cursorMoved(); + + inline KateDocument *doc() { return m_view->doc(); } + inline KateDocument *doc() const { return m_view->doc(); } +}; + +#endif + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/run-tests-in-xvfb.sh b/kate/run-tests-in-xvfb.sh new file mode 100755 index 00000000..e7195945 --- /dev/null +++ b/kate/run-tests-in-xvfb.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +# want build dir as first parameter +BUILDDIR=$1 + +# go to build dir or die +cd $BUILDDIR || exit 1 + +# start Xvfb +Xvfb :7 -ac >/dev/null 2>&1 & + +# save pid for later kill +XPID=$! + +# wait bit for X +echo "Waiting for Xvfb with pid $XPID to launch..." +sleep 2 + +# execute tests on that screen +DISPLAY=:7 make test + +# kill the poor X again +echo "Killing Xvfb with pid $XPID..." +kill $XPID diff --git a/kate/src/CMakeLists.txt b/kate/src/CMakeLists.txt new file mode 100644 index 00000000..b366ea95 --- /dev/null +++ b/kate/src/CMakeLists.txt @@ -0,0 +1,10 @@ +project(kate) + +add_definitions(-DKDE_DEFAULT_DEBUG_AREA=13001) + +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/interfaces) + +add_subdirectory(interfaces/kate) +add_subdirectory(app) +add_subdirectory(filetree) +add_subdirectory(data) diff --git a/kate/src/Messages.sh b/kate/src/Messages.sh new file mode 100644 index 00000000..c4c55212 --- /dev/null +++ b/kate/src/Messages.sh @@ -0,0 +1,5 @@ +#! /usr/bin/env bash +(cd data && $PREPARETIPS > ../tips.cpp) +$EXTRACTRC `find . -name \*.rc -o -name \*.ui` >> rc.cpp || exit 11 +$XGETTEXT `find . -name "*.cpp" -o -name "*.h"` -o $podir/kate.pot +rm -f tips.cpp diff --git a/kate/src/app/CMakeLists.txt b/kate/src/app/CMakeLists.txt new file mode 100644 index 00000000..544b49fa --- /dev/null +++ b/kate/src/app/CMakeLists.txt @@ -0,0 +1,67 @@ +########### next target ############### + +set(kateinterfaces_LIB_SRCS + kateappadaptor.cpp + ../interfaces/kate/application.cpp + ../interfaces/kate/mainwindow.cpp + ../interfaces/kate/documentmanager.cpp + ../interfaces/kate/pluginmanager.cpp + ../interfaces/kate/plugin.cpp + ../interfaces/kate/pluginconfigpageinterface.cpp + kateapp.cpp + kateconfigdialog.cpp + kateconfigplugindialogpage.cpp + katedocmanager.cpp + katemainwindow.cpp + katepluginmanager.cpp + kateviewmanager.cpp + kateviewspace.cpp + katesavemodifieddialog.cpp + katemwmodonhddialog.cpp + katesession.cpp + katemdi.cpp + katecontainer.cpp + katerunninginstanceinfo.cpp + kateappcommands.cpp + katequickopen.cpp +) + + + +add_library(kateinterfaces ${LIBRARY_TYPE} ${kateinterfaces_LIB_SRCS}) + +target_link_libraries(kateinterfaces PUBLIC + ${QT_QTXML_LIBRARY} + ${KDE4_KTEXTEDITOR_LIBS} + ${KDE4_KPARTS_LIBS} +) + +set_target_properties(kateinterfaces PROPERTIES + VERSION ${GENERIC_LIB_VERSION} + SOVERSION ${GENERIC_LIB_SOVERSION} +) + +########### next target ############### + +add_executable(kate katemain.cpp) + +target_link_libraries(kate + ${QT_QTGUI_LIBRARY} + ${KDE4_KDECORE_LIBS} + kateinterfaces +) + +generate_export_header(kateinterfaces BASE_NAME kate) + +install( + TARGETS + kateinterfaces + kate + ${INSTALL_TARGETS_DEFAULT_ARGS} +) + +install( + FILES ${CMAKE_CURRENT_BINARY_DIR}/kate_export.h + DESTINATION ${KDE4_INCLUDE_INSTALL_DIR} + COMPONENT Devel +) diff --git a/kate/src/app/kateapp.cpp b/kate/src/app/kateapp.cpp new file mode 100644 index 00000000..ead92da8 --- /dev/null +++ b/kate/src/app/kateapp.cpp @@ -0,0 +1,445 @@ +/* This file is part of the KDE project + Copyright (C) 2001 Christoph Cullmann + Copyright (C) 2002 Joseph Wenninger + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "kateapp.h" +#include "moc_kateapp.cpp" + +#include "katedocmanager.h" +#include "katepluginmanager.h" +#include "kateviewmanager.h" +#include "katesession.h" +#include "katemainwindow.h" +#include "kateappcommands.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include "kateappadaptor.h" + +KateApp::KateApp (KCmdLineArgs *args) + : KApplication () + , m_shouldExit(false) + , m_args (args) +{ + setQuitOnLastWindowClosed (false); + + // application interface + m_application = new Kate::Application (this); + + // doc man + m_docManager = new KateDocManager (this); + + // init all normal plugins + m_pluginManager = new KatePluginManager (this); + + // session manager up + m_sessionManager = new KateSessionManager (this); + + // dbus + m_adaptor = new KateAppAdaptor( this ); + + // real init + initKate (); + + m_appCommands = KateAppCommands::self(); +} + +KateApp::~KateApp () +{ + // unregister... + m_adaptor->emitExiting (); + QDBusConnection::sessionBus().unregisterObject( QLatin1String("/MainApplication") ); + delete m_adaptor; + + // l8r, app commands + delete m_appCommands; + + // cu session manager + delete m_sessionManager; + + // cu plugin manager + delete m_pluginManager; + + // delete this now, or we crash + delete m_docManager; + + // cu kate app + delete m_application; +} + +KateApp *KateApp::self () +{ + return static_cast(kapp); +} + +Kate::Application *KateApp::application () +{ + return m_application; +} + +/** + * Has always been the Kate Versioning Scheme: + * KDE X.Y.Z contains Kate X-1.Y.Z + */ +QString KateApp::kateVersion (bool fullVersion) +{ + return fullVersion ? QString ("%1.%2.%3").arg(KDE::versionMajor() - 1).arg(KDE::versionMinor()).arg(KDE::versionRelease()) + : QString ("%1.%2").arg(KDE::versionMajor() - 1).arg(KDE::versionMinor()); +} + +void KateApp::initKate () +{ + + kDebug() << "Setting KATE_PID: '" << getpid() << "'"; + ::setenv( "KATE_PID", QString("%1").arg(getpid()).toLatin1(), 1 ); + + // handle restore different + if (isSessionRestored()) + { + restoreKate (); + } + else + { + // let us handle our command line args and co ;) + // we can exit here if session chooser decides + if (!startupKate ()) + { + kDebug() << "startupKate returned false"; + m_shouldExit = true; + return ; + } + } + + // application dbus interface + QDBusConnection::sessionBus().registerObject( QLatin1String("/MainApplication"), this ); +} + +void KateApp::restoreKate () +{ + // activate again correct session!!! + QString lastSession (sessionConfig()->group("General").readEntry ("Last Session", QString())); + sessionManager()->activateSession (KateSession::Ptr(new KateSession (sessionManager(), lastSession)), false, false, false); + + // plugins + KatePluginManager::self ()->loadConfig (sessionConfig()); + + // restore the files we need + m_docManager->restoreDocumentList (sessionConfig()); + + // restore all windows ;) + for (int n = 1; KMainWindow::canBeRestored(n); n++) + newMainWindow(sessionConfig(), QString ("%1").arg(n)); + + // oh, no mainwindow, create one, should not happen, but make sure ;) + if (mainWindows() == 0) + newMainWindow (); +} + +bool KateApp::startupKate () +{ + // user specified session to open + if (m_args->isSet ("startanon")) + { + sessionManager()->activateSession (sessionManager()->giveSession (""), false, false); + } + else if (m_args->isSet ("start")) + { + sessionManager()->activateSession (sessionManager()->giveSession (m_args->getOption("start")), false, false); + } + else if (!m_args->isSet( "stdin" ) && (m_args->count() == 0)) // only start session if no files specified + { + // let the user choose session if possible + if (!sessionManager()->chooseSession ()) + { + kDebug() << "chooseSession returned false, exiting"; + // we will exit kate now, notify the rest of the world we are done +#ifdef Q_WS_X11 + KStartupInfo::appStarted (startupId()); +#endif + return false; + } + } + else + { + sessionManager()->activateSession( KateSession::Ptr(new KateSession (sessionManager(), QString())), false, false ); + } + + // oh, no mainwindow, create one, should not happen, but make sure ;) + if (mainWindows() == 0) + newMainWindow (); + + // notify about start +#ifdef Q_WS_X11 + KStartupInfo::setNewStartupId( activeMainWindow(), startupId()); +#endif + QTextCodec *codec = m_args->isSet("encoding") ? QTextCodec::codecForName(m_args->getOption("encoding").toUtf8()) : 0; + + bool tempfileSet = KCmdLineArgs::isTempFileSet(); + + KTextEditor::Document *doc = 0; + const QString codec_name = codec ? codec->name() : QString(); + KateDocManager::self()->setSuppressOpeningErrorDialogs(true); + QList urls; + for (int z = 0; z < m_args->count(); z++) + { + // this file is no local dir, open it, else warn + const bool noDir = !m_args->url(z).isLocalFile() || !QFileInfo (m_args->url(z).toLocalFile()).isDir(); + + if (noDir) { + urls << m_args->url(z); + } else { + KMessageBox::sorry( activeMainWindow(), + i18n("The file '%1' could not be opened: it is not a normal file, it is a folder.", m_args->url(z).url()) ); + } + } + doc = activeMainWindow()->viewManager()->openUrls(urls, codec_name, tempfileSet); + KateDocManager::self()->setSuppressOpeningErrorDialogs(false); + + // handle stdin input + if( m_args->isSet( "stdin" ) ) + { + QTextStream input(stdin, QIODevice::ReadOnly); + + // set chosen codec + if (codec) + input.setCodec (codec); + + QString line; + QString text; + + do + { + line = input.readLine(); + text.append( line + '\n' ); + } + while( !line.isNull() ); + + openInput (text); + } + else if ( doc ) + activeMainWindow()->viewManager()->activateView( doc ); + + if ( activeMainWindow()->viewManager()->viewCount () == 0 ) + activeMainWindow()->viewManager()->activateView(m_docManager->document (0)); + + int line = 0; + int column = 0; + bool nav = false; + + if (m_args->isSet ("line")) + { + line = m_args->getOption ("line").toInt() - 1; + nav = true; + } + + if (m_args->isSet ("column")) + { + column = m_args->getOption ("column").toInt() - 1; + nav = true; + } + + if (nav && activeMainWindow()->viewManager()->activeView ()) + activeMainWindow()->viewManager()->activeView ()->setCursorPosition (KTextEditor::Cursor (line, column)); + + // show the nice tips + KTipDialog::showTip(activeMainWindow()); + + activeMainWindow()->setAutoSaveSettings(); + + kDebug() << "KateApplication::init finished successful"; + return true; +} + +void KateApp::shutdownKate (KateMainWindow *win) +{ + if (!win->queryClose_internal()) + return; + + sessionManager()->saveActiveSession(true); + + // cu main windows + while (!m_mainWindows.isEmpty()) + { + // mainwindow itself calls KateApp::removeMainWindow(this) + delete m_mainWindows[0]; + } + + quit (); +} + +KatePluginManager *KateApp::pluginManager() +{ + return m_pluginManager; +} + +KateDocManager *KateApp::documentManager () +{ + return m_docManager; +} + +KateSessionManager *KateApp::sessionManager () +{ + return m_sessionManager; +} + +bool KateApp::openUrl (const KUrl &url, const QString &encoding, bool isTempFile) +{ + return openDocUrl(url,encoding,isTempFile); +} + +KTextEditor::Document* KateApp::openDocUrl (const KUrl &url, const QString &encoding, bool isTempFile) +{ + KateMainWindow *mainWindow = activeMainWindow (); + + if (!mainWindow) + return 0; + + QTextCodec *codec = encoding.isEmpty() ? 0 : QTextCodec::codecForName(encoding.toLatin1()); + + // this file is no local dir, open it, else warn + bool noDir = !url.isLocalFile() || !QFileInfo (url.toLocalFile()).isDir(); + + KTextEditor::Document *doc=0; + + if (noDir) + { + // show no errors... + documentManager()->setSuppressOpeningErrorDialogs (true); + + // open a normal file + if (codec) + doc=mainWindow->viewManager()->openUrl( url, codec->name(), true, isTempFile); + else + doc=mainWindow->viewManager()->openUrl( url, QString(), true, isTempFile ); + + // back to normal.... + documentManager()->setSuppressOpeningErrorDialogs (false); + } + else + KMessageBox::sorry( mainWindow, + i18n("The file '%1' could not be opened: it is not a normal file, it is a folder.", url.url()) ); + + return doc; +} + +bool KateApp::setCursor (int line, int column) +{ + KateMainWindow *mainWindow = activeMainWindow (); + + if (!mainWindow) + return false; + + if (mainWindow->viewManager()->activeView ()) + mainWindow->viewManager()->activeView ()->setCursorPosition (KTextEditor::Cursor (line, column)); + + return true; +} + +bool KateApp::openInput (const QString &text) +{ + activeMainWindow()->viewManager()->openUrl( KUrl(), "", true ); + + if (!activeMainWindow()->viewManager()->activeView ()) + return false; + + KTextEditor::Document *doc = activeMainWindow()->viewManager()->activeView ()->document(); + + if (!doc) + return false; + + return doc->setText (text); +} + +KateMainWindow *KateApp::newMainWindow (KConfig *sconfig_, const QString &sgroup_) +{ + KConfig *sconfig = sconfig_ ? sconfig_ : KGlobal::config().data(); + QString sgroup = !sgroup_.isEmpty() ? sgroup_ : "MainWindow0"; + + KateMainWindow *mainWindow = new KateMainWindow (sconfig, sgroup); + mainWindow->show (); + + return mainWindow; +} + +void KateApp::addMainWindow (KateMainWindow *mainWindow) +{ + m_mainWindows.push_back (mainWindow); + m_mainWindowsInterfaces.push_back (mainWindow->mainWindow()); +} + +void KateApp::removeMainWindow (KateMainWindow *mainWindow) +{ + m_mainWindowsInterfaces.removeAll(mainWindow->mainWindow()); + m_mainWindows.removeAll(mainWindow); +} + +KateMainWindow *KateApp::activeMainWindow () +{ + if (m_mainWindows.isEmpty()) + return 0; + + int n = m_mainWindows.indexOf (static_cast(activeWindow())); + + if (n < 0) + n = 0; + + return m_mainWindows[n]; +} + +int KateApp::mainWindows () const +{ + return m_mainWindows.size(); +} + +int KateApp::mainWindowID(KateMainWindow *window) +{ + for (int i = 0;i < m_mainWindows.size();i++) + if (window == m_mainWindows[i]) return i; + return -1; +} + +KateMainWindow *KateApp::mainWindow (int n) +{ + if (n < m_mainWindows.size()) + return m_mainWindows[n]; + + return 0; +} + +void KateApp::emitDocumentClosed(const QString& token) +{ + m_adaptor->emitDocumentClosed(token); +} + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/src/app/kateapp.h b/kate/src/app/kateapp.h new file mode 100644 index 00000000..1f9f60ff --- /dev/null +++ b/kate/src/app/kateapp.h @@ -0,0 +1,286 @@ +/* This file is part of the KDE project + Copyright (C) 2001 Christoph Cullmann + Copyright (C) 2002 Joseph Wenninger + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef __KATE_APP_H__ +#define __KATE_APP_H__ + +#include + +#include "katemain.h" +#include + +#include + +#include + +class KateSessionManager; +class KateMainWindow; +class KatePluginManager; +class KateDocManager; +class KateAppCommands; +class KateAppAdaptor; + +namespace KTextEditor +{ + class Document; +} + +namespace Kate +{ + class Application; +} + +class KCmdLineArgs; + +/** + * Kate Application + * This class represents the core kate application object + */ +class KATE_EXPORT KateApp : public KApplication +{ + Q_OBJECT + + /** + * constructors & accessor to app object + plugin interface for it + */ + public: + /** + * application constructor + * @param args parsed command line args + */ + KateApp (KCmdLineArgs *args); + + /** + * application destructor + */ + ~KateApp (); + + /** + * static accessor to avoid casting ;) + * @return app instance + */ + static KateApp *self (); + + /** + * accessor to the Kate::Application plugin interface + * @return application plugin interface + */ + Kate::Application *application (); + + /** + * Returns the current Kate version (X.Y) or (X.Y.Z) + * @param fullVersion should full version be returned? + * @return Kate version + */ + static QString kateVersion (bool fullVersion = true); + + /** + * kate init + */ + private: + /** + * get kate inited + */ + void initKate (); + + /** + * restore a old kate session + */ + void restoreKate (); + + /** + * try to start kate + * @return success, if false, kate should exit + */ + bool startupKate (); + + /** + * kate shutdown + */ + public: + /** + * shutdown kate application + * @param win mainwindow which is used for dialogs + */ + void shutdownKate (KateMainWindow *win); + + /** + * other accessors for global unique instances + */ + public: + /** + * accessor to plugin manager + * @return plugin manager instance + */ + KatePluginManager *pluginManager(); + + /** + * accessor to document manager + * @return document manager instance + */ + KateDocManager *documentManager (); + + /** + * accessor to session manager + * @return session manager instance + */ + KateSessionManager *sessionManager (); + + /** + * window management + */ + public: + /** + * create a new main window, use given config if any for restore + * @param sconfig session config object + * @param sgroup session group for this window + * @return new constructed main window + */ + KateMainWindow *newMainWindow (KConfig *sconfig = 0, const QString &sgroup = ""); + + /** + * add the mainwindow given + * should be called in mainwindow constructor + * @param mainWindow window to remove + */ + void addMainWindow (KateMainWindow *mainWindow); + + /** + * removes the mainwindow given, DOES NOT DELETE IT + * should be called in mainwindow destructor + * @param mainWindow window to remove + */ + void removeMainWindow (KateMainWindow *mainWindow); + + /** + * give back current active main window + * can only be 0 at app start or exit + * @return current active main window + */ + KateMainWindow *activeMainWindow (); + + /** + * give back number of existing main windows + * @return number of main windows + */ + int mainWindows () const; + + /** + * give back the window you want + * @param n window index + * @return requested main window + */ + KateMainWindow *mainWindow (int n); + + int mainWindowID(KateMainWindow *window); + + /** + * some stuff for the dcop API + */ + public: + /** + * open url with given encoding + * used by kate if --use given + * @param url filename + * @param encoding encoding name + * @return success + */ + bool openUrl (const KUrl &url, const QString &encoding, bool isTempFile); + + KTextEditor::Document* openDocUrl (const KUrl &url, const QString &encoding, bool isTempFile); + + void emitDocumentClosed(const QString& token); + + /** + * position cursor in current active view + * @param line line to set + * @param column column to set + * @return success + */ + bool setCursor (int line, int column); + + /** + * helper to handle stdin input + * open a new document/view, fill it with the text given + * @param text text to fill in the new doc/view + * @return success + */ + bool openInput (const QString &text); + + bool shouldExit() const + { + return m_shouldExit; + } + + /** + * Get a list of all mainwindows interfaces for the plugins. + * @return all mainwindows + * @see activeMainWindow() + */ + const QList &mainWindowsInterfaces () const + { + return m_mainWindowsInterfaces; + } + + private: + bool m_shouldExit; + /** + * kate's command line args + */ + KCmdLineArgs *m_args; + + /** + * plugin interface + */ + Kate::Application *m_application; + + /** + * document manager + */ + KateDocManager *m_docManager; + + /** + * plugin manager + */ + KatePluginManager *m_pluginManager; + + /** + * session manager + */ + KateSessionManager *m_sessionManager; + + /** + * dbus interface + */ + KateAppAdaptor *m_adaptor; + + /** + * known main windows + */ + QList m_mainWindows; + QList m_mainWindowsInterfaces; + + // various vim-inspired command line commands + KateAppCommands *m_appCommands; + +}; + +#endif +// kate: space-indent on; indent-width 2; replace-tabs on; + diff --git a/kate/src/app/kateappadaptor.cpp b/kate/src/app/kateappadaptor.cpp new file mode 100644 index 00000000..b75e9048 --- /dev/null +++ b/kate/src/app/kateappadaptor.cpp @@ -0,0 +1,113 @@ +/* This file is part of the KDE project + Copyright (C) 2001 Christoph Cullmann + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "kateappadaptor.h" + +#include "kateapp.h" +#include "katesession.h" +#include "katedocmanager.h" +#include "katemainwindow.h" +#include "moc_kateappadaptor.cpp" + +#include +#include + +KateAppAdaptor::KateAppAdaptor (KateApp *app) + : QDBusAbstractAdaptor( app ) + , m_app (app) +{} + +void KateAppAdaptor::activate () +{ + KateMainWindow *win = m_app->activeMainWindow(); + if (!win) + return; + + win->show(); + win->activateWindow(); + win->raise(); + +#ifdef Q_WS_X11 + KWindowSystem::forceActiveWindow (win->winId ()); + KWindowSystem::raiseWindow (win->winId ()); + KWindowSystem::demandAttention (win->winId ()); +#endif +} + +bool KateAppAdaptor::openUrl (QString url, QString encoding) +{ + return m_app->openUrl (url, encoding, false); +} + +bool KateAppAdaptor::openUrl (QString url, QString encoding, bool isTempFile) +{ + kDebug () << "openURL"; + + return m_app->openUrl (url, encoding, isTempFile); +} + +//----------- +QString KateAppAdaptor::tokenOpenUrl (QString url, QString encoding) +{ + KTextEditor::Document *doc=m_app->openDocUrl (url, encoding, false); + if (!doc) return QString("ERROR"); + return QString("%1").arg((qptrdiff)doc); +} + +QString KateAppAdaptor::tokenOpenUrl (QString url, QString encoding, bool isTempFile) +{ + kDebug () << "openURL"; + KTextEditor::Document *doc=m_app->openDocUrl (url, encoding, isTempFile); + if (!doc) return QString("ERROR"); + return QString("%1").arg((qptrdiff)doc); +} +//-------- + +bool KateAppAdaptor::setCursor (int line, int column) +{ + return m_app->setCursor (line, column); +} + +bool KateAppAdaptor::openInput (QString text) +{ + return m_app->openInput (text); +} + +bool KateAppAdaptor::activateSession (QString session) +{ + m_app->sessionManager()->activateSession (m_app->sessionManager()->giveSession (session)); + + return true; +} + +QString KateAppAdaptor::activeSession() +{ + return m_app->sessionManager()->activeSession()->sessionName(); +} + +void KateAppAdaptor::emitExiting () +{ + emit exiting (); +} + +void KateAppAdaptor::emitDocumentClosed(const QString& token) +{ + documentClosed(token); +} + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/src/app/kateappadaptor.h b/kate/src/app/kateappadaptor.h new file mode 100644 index 00000000..26388ed5 --- /dev/null +++ b/kate/src/app/kateappadaptor.h @@ -0,0 +1,112 @@ +/* This file is part of the KDE project + Copyright (C) 2001 Christoph Cullmann + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef _kateapp_adaptor_h_ +#define _kateapp_adaptor_h_ + +#include + +class KateApp; + +class KateAppAdaptor : public QDBusAbstractAdaptor +{ + Q_OBJECT + Q_CLASSINFO("D-Bus Interface", "org.kde.Kate.Application") + Q_PROPERTY(QString activeSession READ activeSession) + public: + KateAppAdaptor (KateApp *app); + + /** + * emit the exiting signal + */ + void emitExiting (); + void emitDocumentClosed(const QString& token); + + public Q_SLOTS: + /** + * open a file with given url and encoding + * will get view created + * @param url url of the file + * @param encoding encoding name + * @return success + */ + bool openUrl (QString url, QString encoding); + + /** + * open a file with given url and encoding + * will get view created + * @param url url of the file + * @param encoding encoding name + * @return token or ERROR + */ + QString tokenOpenUrl (QString url, QString encoding); + + /** + * Like the above, but adds an option to let the documentManager know + * if the file should be deleted when closed. + * @p isTempFile should be set to true with the --tempfile option set ONLY, + * files opened with this set to true will be deleted when closed. + */ + bool openUrl(QString url, QString encoding, bool isTempFile); + + QString tokenOpenUrl(QString url, QString encoding, bool isTempFile); + + /** + * set cursor of active view in active main window + * @param line line for cursor + * @param column column for cursor + * @return success + */ + bool setCursor (int line, int column); + + /** + * helper to handle stdin input + * open a new document/view, fill it with the text given + * @param text text to fill in the new doc/view + * @return success + */ + bool openInput (QString text); + + /** + * activate a given session + * @param session session name + * @return success + */ + bool activateSession (QString session); + + /** + * activate this kate instance + */ + void activate (); + + Q_SIGNALS: + /** + * Notify the world that this kate instance is exiting. + * All apps should stop using the dbus interface of this instance after this signal got emitted. + */ + void exiting (); + void documentClosed(const QString& token); + public: + QString activeSession(); + private: + KateApp *m_app; +}; + +#endif +// kate: space-indent on; indent-width 2; replace-tabs on; + diff --git a/kate/src/app/kateappcommands.cpp b/kate/src/app/kateappcommands.cpp new file mode 100644 index 00000000..99b5cadc --- /dev/null +++ b/kate/src/app/kateappcommands.cpp @@ -0,0 +1,271 @@ +/* This file is part of the KDE project + Copyright (C) 2009 Erlend Hamberg + Copyright (C) 2011 Svyatoslav Kuzmich + + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include +#include +#include + +#include "kateapp.h" +#include "kateappcommands.h" +#include "katemainwindow.h" +#include "katedocmanager.h" +#include "kateviewmanager.h" + +KateAppCommands* KateAppCommands::m_instance = 0; + +KateAppCommands::KateAppCommands() + : KTextEditor::Command() +{ + KTextEditor::Editor *editor = KateDocManager::self()->editor(); + KTextEditor::CommandInterface *iface = qobject_cast(editor); + + if (iface) { + iface->registerCommand(this); + } + + re_write.setPattern("w(a)?"); + re_close.setPattern("bd(elete)?|tabc(lose)?"); + re_quit.setPattern("(w)?q(a|all)?(!)?"); + re_exit.setPattern("x(a)?"); + re_edit.setPattern("e(dit)?|tabe(dit)?|tabnew"); + re_new.setPattern("(v)?new"); + re_split.setPattern("sp(lit)?"); + re_vsplit.setPattern("vs(plit)?"); + re_only.setPattern("on(ly)?"); +} + +KateAppCommands::~KateAppCommands() +{ + KTextEditor::Editor *editor = KateDocManager::self()->editor(); + KTextEditor::CommandInterface *iface = qobject_cast(editor); + + if (iface) { + iface->unregisterCommand(this); + } + + m_instance = 0; +} + +const QStringList& KateAppCommands::cmds() +{ + static QStringList l; + + if (l.empty()) { + l << "q" << "qa" << "qall" << "q!" << "qa!" << "qall!" + << "wq" << "wa" << "wqa" << "x" << "xa" << "new" + << "vnew" << "e" << "edit" << "enew" << "sp" << "split" << "vs" + << "vsplit" << "only" << "tabe" << "tabedit" << "tabnew" << "bd" + << "bdelete" << "tabc" << "tabclose"; + } + + return l; +} + +bool KateAppCommands::exec(KTextEditor::View *view, const QString &cmd, QString &msg) +{ + QStringList args(cmd.split( QRegExp("\\s+"), QString::SkipEmptyParts)) ; + QString command( args.takeFirst() ); + + KateMainWindow *mainWin = KateApp::self()->activeMainWindow(); + + if (re_write.exactMatch(command)) { //TODO: handle writing to specific file + if (!re_write.cap(1).isEmpty()) { // [a]ll + KateDocManager::self()->saveAll(); + msg = i18n("All documents written to disk"); + } else { + view->document()->documentSave(); + msg = i18n("Document written to disk"); + } + } + // Other buffer commands are implemented by the KateFileTree plugin + else if (re_close.exactMatch(command)) { + QTimer::singleShot(0, mainWin, SLOT(slotFileClose())); + } + else if (re_quit.exactMatch(command)) { + const bool save = !re_quit.cap(1).isEmpty(); // :[w]q + const bool allDocuments = !re_quit.cap(2).isEmpty(); // :q[all] + const bool doNotPromptForSave = !re_quit.cap(3).isEmpty(); // :q[!] + + if (allDocuments) { + if (save) + KateDocManager::self()->saveAll(); + + if (doNotPromptForSave) { + foreach ( KTextEditor::Document *doc, KateDocManager::self()->documentList() ) + if ( doc->isModified() ) + doc->setModified(false); + } + + QTimer::singleShot(0, mainWin, SLOT(slotFileQuit())); + } else { + if (save && view->document()->isModified()) + view->document()->documentSave(); + + if (doNotPromptForSave) + view->document()->setModified(false); + + if (mainWin->viewManager()->count() > 1) { + QTimer::singleShot(0, mainWin->viewManager(), SLOT(slotCloseCurrentViewSpace())); + } else { + if (KateDocManager::self()->documents() > 1) + QTimer::singleShot(0, mainWin, SLOT(slotFileClose())); + else + QTimer::singleShot(0, mainWin, SLOT(slotFileQuit())); + } + } + } else if (re_exit.exactMatch(command)) { + if (!re_exit.cap(1).isEmpty()) { // a[ll] + KateDocManager::self()->saveAll(); + QTimer::singleShot(0, mainWin, SLOT(slotFileQuit())); + } else { + if (view->document()->isModified()) { + view->document()->documentSave(); + } + + if (KateDocManager::self()->documents() > 1) + QTimer::singleShot(0, mainWin, SLOT(slotFileClose())); + else + QTimer::singleShot(0, mainWin, SLOT(slotFileQuit())); + } + QTimer::singleShot(0, mainWin, SLOT(slotFileQuit())); + } + else if (re_edit.exactMatch(command)) { + QString argument = args.join(QString(' ')); + if (argument.isEmpty() || argument == "!") { + view->document()->documentReload(); + } else { + KUrl base = mainWin->activeDocumentUrl(); + KUrl url; + KUrl arg2path(argument); + if (base.isValid()) { // first try to use the same path as the current open document has + url = KUrl(base.resolved(arg2path)); //resolved handles the case where the args is a relative path, and is the same as using KUrl(args) elsewise + } else { // else use the cwd + url = KUrl(KUrl(QDir::currentPath() + "/").resolved(arg2path)); // + "/" is needed because of http://lists.qt.nokia.com/public/qt-interest/2011-May/033913.html + } + QFileInfo file( url.toLocalFile() ); + KTextEditor::Document *doc = KateDocManager::self()->findDocument( url ); + if (doc) { + mainWin->viewManager()->activateView( doc ); + } else if (file.exists()) { + mainWin->viewManager()->openUrl( url, QString(), true ); + } else { + mainWin->viewManager()->openUrl( KUrl(), QString(), true )->saveAs ( url ); + } + } + } + else if (re_new.exactMatch(command)) { + if (re_new.cap(1) == "v") { // vertical split + mainWin->viewManager()->slotSplitViewSpaceVert(); + } else { // horizontal split + mainWin->viewManager()->slotSplitViewSpaceHoriz(); + } + mainWin->viewManager()->slotDocumentNew(); + } + else if (command == "enew") { + mainWin->viewManager()->slotDocumentNew(); + } + else if (re_split.exactMatch(command)) { + mainWin->viewManager()->slotSplitViewSpaceHoriz(); + } + else if (re_vsplit.exactMatch(command)) { + mainWin->viewManager()->slotSplitViewSpaceVert(); + } else if (re_only.exactMatch(command)) { + mainWin->viewManager()->slotCloseOtherViews(); + } + + return true; +} + +bool KateAppCommands::help(KTextEditor::View *view, const QString &cmd, QString &msg) +{ + Q_UNUSED(view); + + if (re_write.exactMatch(cmd)) { + msg = i18n("

w/wa — write document(s) to disk

" + "

Usage: w[a]

" + "

Writes the current document(s) to disk. " + "It can be called in two ways:
" + " w — writes the current document to disk
" + " wa — writes all documents to disk.

" + "

If no file name is associated with the document, " + "a file dialog will be shown.

"); + return true; + } + else if (re_quit.exactMatch(cmd)) { + msg = i18n("

q/qa/wq/wqa — [write and] quit

" + "

Usage: [w]q[a]

" + "

Quits the application. If w is prepended, it also writes" + " the document(s) to disk. This command " + "can be called in several ways:
" + " q — closes the current view.
" + " qa — closes all views, effectively quitting the application.
" + " wq — writes the current document to disk and closes its view.
" + " wqa — writes all documents to disk and quits.

" + "

In all cases, if the view being closed is the last view, the application quits. " + "If no file name is associated with the document and it should be written to disk, " + "a file dialog will be shown.

"); + return true; + } + else if (re_exit.exactMatch(cmd)) { + msg = i18n("

x/xa — write and quit

" + "

Usage: x[a]

" + "

Saves document(s) and quits (exits). This command " + "can be called in two ways:
" + " x — closes the current view.
" + " xa — closes all views, effectively quitting the application.

" + "

In all cases, if the view being closed is the last view, the application quits. " + "If no file name is associated with the document and it should be written to disk, " + "a file dialog will be shown.

" + "

Unlike the 'w' commands, this command only writes the document if it is modified." + "

"); + return true; + } + else if (re_split.exactMatch(cmd)) { + msg = i18n("

sp,split— Split horizontally the current view into two

" + "

Usage: sp[lit]

" + "

The result is two views on the same document.

"); + return true; + } + else if (re_vsplit.exactMatch(cmd)) { + msg = i18n("

vs,vsplit— Split vertically the current view into two

" + "

Usage: vs[plit]

" + "

The result is two views on the same document.

"); + return true; + } + else if (re_new.exactMatch(cmd)) { + msg = i18n("

[v]new — split view and create new document

" + "

Usage: [v]new

" + "

Splits the current view and opens a new document in the new view." + " This command can be called in two ways:
" + " new — splits the view horizontally and opens a new document.
" + " vnew — splits the view vertically and opens a new document.
" + "

"); + return true; + } + else if (re_edit.exactMatch(cmd)) { + msg = i18n("

e[dit] — reload current document

" + "

Usage: e[dit]

" + "

Starts editing the current document again. This is useful to re-edit" + " the current file, when it has been changed by another program.

"); + return true; + } + + return false; +} diff --git a/kate/src/app/kateappcommands.h b/kate/src/app/kateappcommands.h new file mode 100644 index 00000000..a53c5f7e --- /dev/null +++ b/kate/src/app/kateappcommands.h @@ -0,0 +1,56 @@ +/* This file is part of the KDE project + Copyright (C) 2009 Erlend Hamberg + Copyright (C) 2011 Svyatoslav Kuzmich + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KATE_APP_COMMANDS_INCLUDED +#define KATE_APP_COMMANDS_INCLUDED + +#include +#include + +class KateAppCommands : public KTextEditor::Command +{ + KateAppCommands(); + static KateAppCommands* m_instance; + + public: + virtual ~KateAppCommands(); + virtual const QStringList &cmds (); + virtual bool exec (KTextEditor::View *view, const QString &cmd, QString &msg); + virtual bool help (KTextEditor::View *view, const QString &cmd, QString &msg); + + static KateAppCommands* self() { + if (m_instance == 0) { + m_instance = new KateAppCommands(); + } + return m_instance; + } + + private: + QRegExp re_write; + QRegExp re_close; + QRegExp re_quit; + QRegExp re_exit; + QRegExp re_edit; + QRegExp re_new; + QRegExp re_split; + QRegExp re_vsplit; + QRegExp re_only; +}; + +#endif diff --git a/kate/src/app/kateconfigdialog.cpp b/kate/src/app/kateconfigdialog.cpp new file mode 100644 index 00000000..3cf65308 --- /dev/null +++ b/kate/src/app/kateconfigdialog.cpp @@ -0,0 +1,393 @@ +/* This file is part of the KDE project + Copyright (C) 2001 Christoph Cullmann + Copyright (C) 2002 Joseph Wenninger + Copyright (C) 2007 Mirko Stocker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "kateconfigdialog.h" +#include "moc_kateconfigdialog.cpp" + +#include "katemainwindow.h" + +#include "katedocmanager.h" +#include "katepluginmanager.h" +#include "kateconfigplugindialogpage.h" +#include "kateviewmanager.h" +#include "kateapp.h" +#include "katesession.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +KateConfigDialog::KateConfigDialog ( KateMainWindow *parent, KTextEditor::View *view ) + : KPageDialog( parent ) + , m_mainWindow( parent ) +{ + setFaceType( Tree ); + setCaption( i18n("Configure") ); + setButtons( Ok | Apply | Cancel | Help ); + setDefaultButton( Ok ); + setObjectName( "configdialog" ); + setHelp( QString(), KGlobal::mainComponent().componentName() ); + + KSharedConfig::Ptr config = KGlobal::config(); + KConfigGroup cgGeneral = KConfigGroup( config, "General" ); + + enableButton( Apply, false ); + + KPageWidgetItem *applicationItem = addPage( new QWidget, i18n("Application") ); + applicationItem->setIcon( KIcon( "preferences-other" ) ); + applicationItem->setHeader( i18n("Application Options") ); + m_applicationPage = applicationItem; + + //BEGIN General page + QFrame* generalFrame = new QFrame; + KPageWidgetItem *item = addSubPage( applicationItem, generalFrame, i18n("General") ); + item->setHeader( i18n("General Options") ); + item->setIcon( KIcon( "go-home" ) ); + setCurrentPage (item); + + QVBoxLayout *layout = new QVBoxLayout( generalFrame ); + layout->setMargin(0); + + // GROUP with the one below: "Behavior" + QGroupBox *buttonGroup = new QGroupBox( i18n("&Behavior"), generalFrame ); + QVBoxLayout *vbox = new QVBoxLayout; + layout->addWidget( buttonGroup ); + + // modified files notification + m_modNotifications = new QCheckBox( + i18n("Wa&rn about files modified by foreign processes"), buttonGroup ); + m_modNotifications->setChecked( parent->modNotification ); + m_modNotifications->setWhatsThis( i18n( + "If enabled, when Kate receives focus you will be asked what to do with " + "files that have been modified on the hard disk. If not enabled, you will " + "be asked what to do with a file that has been modified on the hard disk only " + "when that file is tried to be saved.") ); + connect( m_modNotifications, SIGNAL(toggled(bool)), + this, SLOT(slotChanged()) ); + + vbox->addWidget(m_modNotifications); + buttonGroup->setLayout(vbox); + + // GROUP with the one below: "Meta-information" + buttonGroup = new QGroupBox( i18n("Meta-Information"), generalFrame ); + vbox = new QVBoxLayout; + layout->addWidget( buttonGroup ); + + // save meta infos + m_saveMetaInfos = new QCheckBox( buttonGroup ); + m_saveMetaInfos->setText(i18n("Keep &meta-information past sessions")); + m_saveMetaInfos->setChecked(KateDocManager::self()->getSaveMetaInfos()); + m_saveMetaInfos->setWhatsThis( i18n( + "Check this if you want document configuration like for example " + "bookmarks to be saved past editor sessions. The configuration will be " + "restored if the document has not changed when reopened.")); + connect( m_saveMetaInfos, SIGNAL(toggled(bool)), this, SLOT(slotChanged()) ); + + vbox->addWidget(m_saveMetaInfos); + + // meta infos days + KHBox *metaInfos = new KHBox( buttonGroup ); + metaInfos->setEnabled(KateDocManager::self()->getSaveMetaInfos()); + QLabel *label = new QLabel( i18n("&Delete unused meta-information after:"), metaInfos ); + m_daysMetaInfos = new QSpinBox( metaInfos ); + m_daysMetaInfos->setMaximum( 180 ); + m_daysMetaInfos->setSpecialValueText(i18n("(never)")); + m_daysMetaInfos->setValue( KateDocManager::self()->getDaysMetaInfos() ); + label->setBuddy( m_daysMetaInfos ); + connect( m_saveMetaInfos, SIGNAL(toggled(bool)), metaInfos, SLOT(setEnabled(bool)) ); + connect( m_daysMetaInfos, SIGNAL(valueChanged(int)), this, SLOT(slotChanged()) ); + + vbox->addWidget(metaInfos); + buttonGroup->setLayout(vbox); + + layout->addStretch(1); // :-] works correct without autoadd + //END General page + + + //BEGIN Session page + QFrame* sessionsFrame = new QFrame; + item = addSubPage( applicationItem, sessionsFrame, i18n("Sessions") ); + item->setHeader( i18n("Session Management") ); + item->setIcon( KIcon( "view-history" ) ); + + layout = new QVBoxLayout( sessionsFrame ); + layout->setMargin(0); + + // GROUP with the one below: "Startup" + buttonGroup = new QGroupBox( i18n("Elements of Sessions"), sessionsFrame ); + vbox = new QVBoxLayout; + layout->addWidget( buttonGroup ); + + // restore view config + m_restoreVC = new QCheckBox( buttonGroup ); + m_restoreVC->setText(i18n("Include &window configuration")); + m_restoreVC->setChecked( cgGeneral.readEntry("Restore Window Configuration", true) ); + m_restoreVC->setWhatsThis( i18n("Check this if you want all your views and frames restored each time you open Kate")); + connect( m_restoreVC, SIGNAL(toggled(bool)), this, SLOT(slotChanged()) ); + + vbox->addWidget(m_restoreVC); + buttonGroup->setLayout(vbox); + + QGroupBox* sessionsStart = new QGroupBox( i18n("Behavior on Application Startup"), sessionsFrame ); + vbox = new QVBoxLayout; + layout->addWidget(sessionsStart); + + m_startNewSessionRadioButton = new QRadioButton( i18n("&Start new session"), sessionsStart ); + m_loadLastUserSessionRadioButton = new QRadioButton( i18n("&Load last-used session"), sessionsStart ); + m_manuallyChooseSessionRadioButton = new QRadioButton( i18n("&Manually choose a session"), sessionsStart ); + + QString sesStart (cgGeneral.readEntry ("Startup Session", "manual")); + if (sesStart == "new") + m_startNewSessionRadioButton->setChecked (true); + else if (sesStart == "last") + m_loadLastUserSessionRadioButton->setChecked (true); + else + m_manuallyChooseSessionRadioButton->setChecked (true); + + connect(m_startNewSessionRadioButton, SIGNAL(toggled(bool)), this, SLOT(slotChanged())); + connect(m_loadLastUserSessionRadioButton, SIGNAL(toggled(bool)), this, SLOT(slotChanged())); + connect(m_manuallyChooseSessionRadioButton, SIGNAL(toggled(bool)), this, SLOT(slotChanged())); + + vbox->addWidget(m_startNewSessionRadioButton); + vbox->addWidget(m_loadLastUserSessionRadioButton); + vbox->addWidget(m_manuallyChooseSessionRadioButton); + sessionsStart->setLayout(vbox); + + layout->addStretch(1); // :-] works correct without autoadd + //END Session page + + //BEGIN Plugins page + KVBox *page = new KVBox(); + page->setSpacing( -1 ); + KateConfigPluginPage *configPluginPage = new KateConfigPluginPage(page, this); + connect( configPluginPage, SIGNAL(changed()), this, SLOT(slotChanged()) ); + + item = addSubPage( applicationItem, page, i18n("Plugins") ); + item->setHeader( i18n("Plugin Manager") ); + item->setIcon( KIcon( "preferences-plugin" ) ); + + KatePluginList &pluginList (KatePluginManager::self()->pluginList()); + foreach (const KatePluginInfo &plugin, pluginList) + { + if ( plugin.load + && Kate::pluginConfigPageInterface(plugin.plugin) ) + addPluginPage (plugin.plugin); + } + //END Plugins page + +// editor widgets from kwrite/kwdialog + m_editorPage = addPage( new QWidget, i18n("Editor Component") ); + m_editorPage->setIcon( KIcon( "accessories-text-editor" ) ); + m_editorPage->setHeader( i18n("Editor Component Options") ); + + addEditorPages(); + + connect(this, SIGNAL(okClicked()), this, SLOT(slotOk())); + connect(this, SIGNAL(applyClicked()), this, SLOT(slotApply())); + connect(this, SIGNAL(currentPageChanged(KPageWidgetItem*,KPageWidgetItem*)), + this, SLOT(slotCurrentPageChanged(KPageWidgetItem*,KPageWidgetItem*))); + m_dataChanged = false; + + resize(minimumSizeHint()); + slotChanged(); +} + +KateConfigDialog::~KateConfigDialog() +{} + + +void KateConfigDialog::addEditorPages() { + + for (int i = 0; i < KateDocManager::self()->editor()->configPages (); ++i) + { + KTextEditor::ConfigPage *page = KateDocManager::self()->editor()->configPage(i, this); + connect( page, SIGNAL(changed()), this, SLOT(slotChanged()) ); + m_editorPages.push_back (page); + KPageWidgetItem *item=addSubPage( m_editorPage, page, KateDocManager::self()->editor()->configPageName(i) ); + item->setHeader( KateDocManager::self()->editor()->configPageFullName(i) ); + item->setIcon( KateDocManager::self()->editor()->configPageIcon(i) ); + } +} + +void KateConfigDialog::addPluginPage (Kate::Plugin *plugin) +{ + if (!Kate::pluginConfigPageInterface(plugin)) + return; + + for (uint i = 0; i < Kate::pluginConfigPageInterface(plugin)->configPages(); i++) + { + KVBox *page = new KVBox(); + page->setSpacing( -1 ); + + KPageWidgetItem *item = addSubPage( m_applicationPage, page, Kate::pluginConfigPageInterface(plugin)->configPageName(i) ); + item->setHeader( Kate::pluginConfigPageInterface(plugin)->configPageFullName(i) ); + item->setIcon( Kate::pluginConfigPageInterface(plugin)->configPageIcon(i)); + + PluginPageListItem *info = new PluginPageListItem; + info->plugin = plugin; + info->configPageInterface=Kate::pluginConfigPageInterface(plugin); + info->pageParent=page; + info->pluginPage = 0; //info->configPageInterface->configPage (i, page); + info->idInPlugin=i; + info->pageWidgetItem = item; + //connect( info->page, SIGNAL(changed()), this, SLOT(slotChanged()) ); + m_pluginPages.insert(item,info); + } +} + +void KateConfigDialog::slotCurrentPageChanged( KPageWidgetItem *current, KPageWidgetItem * /*before*/ ) +{ + PluginPageListItem *info=m_pluginPages[current]; + if (!info) return; + if (info->pluginPage) return; + kDebug()<<"creating config page"; + info->pluginPage=info->configPageInterface->configPage(info->idInPlugin,info->pageParent); + info->pluginPage->show(); + connect( info->pluginPage, SIGNAL(changed()), this, SLOT(slotChanged()) ); +} + +void KateConfigDialog::removePluginPage (Kate::Plugin *plugin) +{ + if (!Kate::pluginConfigPageInterface(plugin)) + return; + + QList remove; + for (QHash::const_iterator it=m_pluginPages.constBegin();it!=m_pluginPages.constEnd();++it) { + PluginPageListItem *pli=it.value(); + if (!pli) continue; + if (pli->plugin==plugin) + remove.append(it.key()); + } + + kDebug()<pluginPage; + delete pItem->pageParent; + removePage(wItem); + delete pItem; + } +} + +void KateConfigDialog::slotOk() +{ + slotApply(); + accept(); +} + +void KateConfigDialog::slotApply() +{ + KSharedConfig::Ptr config = KGlobal::config(); + + // if data changed apply the kate app stuff + if( m_dataChanged ) + { + KConfigGroup cg = KConfigGroup( config, "General" ); + + cg.writeEntry("Restore Window Configuration", m_restoreVC->isChecked()); + + if (m_startNewSessionRadioButton->isChecked()) + cg.writeEntry ("Startup Session", "new"); + else if (m_loadLastUserSessionRadioButton->isChecked()) + cg.writeEntry ("Startup Session", "last"); + else + cg.writeEntry ("Startup Session", "manual"); + + cg.writeEntry("Save Meta Infos", m_saveMetaInfos->isChecked()); + KateDocManager::self()->setSaveMetaInfos(m_saveMetaInfos->isChecked()); + + cg.writeEntry("Days Meta Infos", m_daysMetaInfos->value() ); + KateDocManager::self()->setDaysMetaInfos(m_daysMetaInfos->value()); + + cg.writeEntry("Modified Notification", m_modNotifications->isChecked()); + m_mainWindow->modNotification = m_modNotifications->isChecked(); + + // patch document modified warn state + const QList &docs = KateDocManager::self()->documentList (); + foreach (KTextEditor::Document *doc, docs) + if (qobject_cast(doc)) + qobject_cast(doc)->setModifiedOnDiskWarning (!m_modNotifications->isChecked()); + + m_mainWindow->saveOptions (); + + // save plugin config !! + KateSessionManager *sessionmanager = KateSessionManager::self(); + KConfig *sessionConfig = sessionmanager->activeSession()->configWrite(); + KateApp::self()->pluginManager()->writeConfig (sessionConfig); + } + + foreach (PluginPageListItem *plugin, m_pluginPages.values()) + { + if (!plugin) continue; + if (plugin->pluginPage) + plugin->pluginPage->apply(); + } + + // apply ktexteditor pages + foreach (KTextEditor::ConfigPage *page, m_editorPages) + page->apply(); + + // write back the editor config + KateDocManager::self()->editor()->writeConfig(config.data()); + + config->sync(); + + m_dataChanged = false; + enableButton( Apply, false ); +} + +void KateConfigDialog::slotChanged() +{ + m_dataChanged = true; + enableButton( Apply, true ); + m_daysMetaInfos->setSuffix(i18ncp("The suffix of 'Delete unused meta-information after'", " day", " days", m_daysMetaInfos->value())); +} + +void KateConfigDialog::showAppPluginPage(Kate::PluginConfigPageInterface *configpageinterface,uint id) +{ + foreach (PluginPageListItem *plugin, m_pluginPages.values()) + { + if ((plugin->configPageInterface==configpageinterface) && (id==plugin->idInPlugin)) + { + setCurrentPage(plugin->pageWidgetItem); + break; + } + } +} + +// kate: space-indent on; indent-width 2; replace-tabs on; + diff --git a/kate/src/app/kateconfigdialog.h b/kate/src/app/kateconfigdialog.h new file mode 100644 index 00000000..ffd1d7d0 --- /dev/null +++ b/kate/src/app/kateconfigdialog.h @@ -0,0 +1,93 @@ +/* This file is part of the KDE project + Copyright (C) 2001 Christoph Cullmann + Copyright (C) 2002 Joseph Wenninger + Copyright (C) 2007 Mirko Stocker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef __kate_configdialog_h__ +#define __kate_configdialog_h__ + +#include "katemain.h" + +#include +#include + +#include + +#include +#include +#include + +#include +#include +class KateMainWindow; + +struct PluginPageListItem +{ + Kate::Plugin *plugin; + Kate::PluginConfigPageInterface *configPageInterface; + uint idInPlugin; + Kate::PluginConfigPage *pluginPage; + QWidget *pageParent; + KPageWidgetItem *pageWidgetItem; +}; + +class KateConfigDialog : public KPageDialog +{ + Q_OBJECT + + public: + KateConfigDialog (KateMainWindow *parent, KTextEditor::View *view); + ~KateConfigDialog (); + + public: + void addPluginPage (Kate::Plugin *plugin); + void removePluginPage (Kate::Plugin *plugin); + void showAppPluginPage(Kate::PluginConfigPageInterface *configpageinterface,uint id); + protected Q_SLOTS: + void slotOk(); + void slotApply(); + void slotChanged(); + + void slotCurrentPageChanged( KPageWidgetItem *current, KPageWidgetItem *before ); + + private: + KateMainWindow *m_mainWindow; + + bool m_dataChanged; + + QCheckBox *m_modNotifications; + QCheckBox *m_saveMetaInfos; + QSpinBox *m_daysMetaInfos; + QCheckBox *m_restoreVC; + + // sessions start group: + QRadioButton *m_startNewSessionRadioButton; + QRadioButton *m_loadLastUserSessionRadioButton; + QRadioButton *m_manuallyChooseSessionRadioButton; + + QHash m_pluginPages; + QList m_editorPages; + KPageWidgetItem *m_applicationPage; + KPageWidgetItem *m_editorPage; + + void addEditorPages(); +}; + +#endif +// kate: space-indent on; indent-width 2; replace-tabs on; + diff --git a/kate/src/app/kateconfigplugindialogpage.cpp b/kate/src/app/kateconfigplugindialogpage.cpp new file mode 100644 index 00000000..5525b9cc --- /dev/null +++ b/kate/src/app/kateconfigplugindialogpage.cpp @@ -0,0 +1,116 @@ +/* This file is part of the KDE project + Copyright (C) 2001 Christoph Cullmann + Copyright (C) 2002 Joseph Wenninger + Copyright (C) 2007 Mirko Stocker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "kateconfigplugindialogpage.h" +#include "moc_kateconfigplugindialogpage.cpp" + +#include "katepluginmanager.h" +#include "kateconfigdialog.h" + +class KatePluginListItem : public QTreeWidgetItem +{ + public: + KatePluginListItem(bool checked, KatePluginInfo *info); + + KatePluginInfo *info() const + { + return mInfo; + } + + protected: + void stateChange(bool); + + private: + KatePluginInfo *mInfo; +}; + +KatePluginListItem::KatePluginListItem(bool checked, KatePluginInfo *info) + : QTreeWidgetItem() + , mInfo(info) +{ + setCheckState(0, checked ? Qt::Checked : Qt::Unchecked); + // skip plugins that will be loaded always! + setDisabled (info->alwaysLoad); +} + +KatePluginListView::KatePluginListView(QWidget *parent) + : QTreeWidget(parent) +{ + setRootIsDecorated(false); + connect(this, SIGNAL(itemChanged(QTreeWidgetItem*,int)), this, SLOT(stateChanged(QTreeWidgetItem*))); +} + +void KatePluginListView::stateChanged(QTreeWidgetItem *item) +{ + emit stateChange(static_cast(item), item->checkState(0) == Qt::Checked); +} + +KateConfigPluginPage::KateConfigPluginPage(QWidget *parent, KateConfigDialog *dialog) + : KVBox(parent) + , myDialog(dialog) +{ + KatePluginListView* listView = new KatePluginListView(this); + QStringList headers; + headers << i18n("Name") << i18n("Comment"); + listView->setHeaderLabels(headers); + listView->setWhatsThis(i18n("Here you can see all available Kate plugins. Those with a check mark are loaded, and will be loaded again the next time Kate is started.")); + + KatePluginList &pluginList (KatePluginManager::self()->pluginList()); + for (KatePluginList::iterator it = pluginList.begin();it != pluginList.end(); ++it) { + if (it->alwaysLoad) continue; + QTreeWidgetItem *item = new KatePluginListItem(it->load, &(*it)); + item->setText(0, it->service->name()); + item->setText(1, it->service->comment()); + listView->addTopLevelItem(item); + } + + listView->resizeColumnToContents(0); + listView->sortByColumn(0, Qt::AscendingOrder); + connect(listView, SIGNAL(stateChange(KatePluginListItem*,bool)), this, SLOT(stateChange(KatePluginListItem*,bool))); +} + +void KateConfigPluginPage::stateChange(KatePluginListItem *item, bool b) +{ + if(b) + loadPlugin(item); + else + unloadPlugin(item); + + emit changed(); +} + +void KateConfigPluginPage::loadPlugin (KatePluginListItem *item) +{ + KatePluginManager::self()->loadPlugin (item->info()); + KatePluginManager::self()->enablePluginGUI (item->info()); + myDialog->addPluginPage (item->info()->plugin); + + item->setCheckState(0, Qt::Checked); +} + +void KateConfigPluginPage::unloadPlugin (KatePluginListItem *item) +{ + myDialog->removePluginPage (item->info()->plugin); + KatePluginManager::self()->unloadPlugin (item->info()); + + item->setCheckState(0, Qt::Unchecked); +} +// kate: space-indent on; indent-width 2; replace-tabs on; + diff --git a/kate/src/app/kateconfigplugindialogpage.h b/kate/src/app/kateconfigplugindialogpage.h new file mode 100644 index 00000000..c7b4f2f4 --- /dev/null +++ b/kate/src/app/kateconfigplugindialogpage.h @@ -0,0 +1,65 @@ +/* This file is part of the KDE project + Copyright (C) 2001 Christoph Cullmann + Copyright (C) 2002 Joseph Wenninger + Copyright (C) 2007 Mirko Stocker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef __KATE_CONFIGPLUGINDIALOGPAGE_H__ +#define __KATE_CONFIGPLUGINDIALOGPAGE_H__ + +#include +#include + +class KatePluginListItem; + +class KatePluginListView : public QTreeWidget +{ + Q_OBJECT + + public: + KatePluginListView (QWidget *parent = 0); + + Q_SIGNALS: + void stateChange(KatePluginListItem *, bool); + + private Q_SLOTS: + void stateChanged(QTreeWidgetItem *); +}; + +class KateConfigPluginPage: public KVBox +{ + Q_OBJECT + + public: + KateConfigPluginPage(QWidget *parent, class KateConfigDialog *dialog); + + private: + class KateConfigDialog *myDialog; + + Q_SIGNALS: + void changed(); + + private Q_SLOTS: + void stateChange(KatePluginListItem *, bool); + + void loadPlugin (KatePluginListItem *); + void unloadPlugin (KatePluginListItem *); +}; + +#endif +// kate: space-indent on; indent-width 2; replace-tabs on; + diff --git a/kate/src/app/katecontainer.cpp b/kate/src/app/katecontainer.cpp new file mode 100644 index 00000000..5fa230c6 --- /dev/null +++ b/kate/src/app/katecontainer.cpp @@ -0,0 +1,172 @@ +/* This file is part of the KDE project + Copyright (C) 2008 Joseph Wenninger + Copyright (C) 2010 Dominik Haumann + + In addition to the following license there is an exception, that the KDE e.V. + may decide on other forms of relicensing. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "katecontainer.h" +#include "kateapp.h" +#include "katemainwindow.h" +#include "kateviewmanager.h" + +#include + +KateContainer::KateContainer(KateApp* parent) + : QObject(parent) + , KTextEditor::ViewBarContainer() + , KTextEditor::MdiContainer() +{ +} + +KateContainer::~KateContainer() { +} + +//BEGIN KTextEditor::ViewBarContainer +QWidget* KateContainer::getViewBarParent(KTextEditor::View *view,KTextEditor::ViewBarContainer::Position position) { + if (position==KTextEditor::ViewBarContainer::BottomBar) { + KateMainWindow* mainWindow=qobject_cast(view->window()); + if (!mainWindow) { + kDebug()<<"returning hardcoded 0, views window is not a KateMainWindow"; + return 0; + } + //Toplevel is a KateMainWindow + return mainWindow->bottomViewBarContainer(); + } else if (position==KTextEditor::ViewBarContainer::TopBar) { + KateMainWindow* mainWindow=qobject_cast(view->window()); + if (!mainWindow) { + kDebug()<<"returning hardcoded 0, views window is not a KateMainWindow"; + return 0; + } + //Toplevel is a KateMainWindow + return mainWindow->topViewBarContainer(); + } + return 0; +} + +void KateContainer::addViewBarToLayout(KTextEditor::View *view,QWidget *bar, KTextEditor::ViewBarContainer::Position position) { + KateMainWindow* mainWindow=qobject_cast(view->window()); + if (!mainWindow) { + kDebug()<<"main window is not a katemainwindow"; + return; + } + + if (position==KTextEditor::ViewBarContainer::BottomBar) { + mainWindow->addToBottomViewBarContainer(view,bar); + } else if (position==KTextEditor::ViewBarContainer::TopBar) { + mainWindow->addToTopViewBarContainer(view,bar); + } + +} + +void KateContainer::showViewBarForView(KTextEditor::View *view, KTextEditor::ViewBarContainer::Position position) { + KateMainWindow* mainWindow=qobject_cast(view->window()); + if (!mainWindow) { + kDebug()<<"main window is not a katemainwindow"; + return; + } + + if (position==KTextEditor::ViewBarContainer::BottomBar) { + mainWindow->showBottomViewBarForView(view); + } else if (position==KTextEditor::ViewBarContainer::TopBar) { + mainWindow->showTopViewBarForView(view); + } +} + +void KateContainer::hideViewBarForView(KTextEditor::View *view, KTextEditor::ViewBarContainer::Position position) { + KateMainWindow* mainWindow=qobject_cast(view->window()); + if (!mainWindow) { + kDebug()<<"main window is not a katemainwindow"; + return; + } + + if (position==KTextEditor::ViewBarContainer::BottomBar) { + mainWindow->hideBottomViewBarForView(view); + } else if (position==KTextEditor::ViewBarContainer::TopBar) { + mainWindow->hideTopViewBarForView(view); + } +} + +void KateContainer::deleteViewBarForView(KTextEditor::View *view, KTextEditor::ViewBarContainer::Position position) { + KateMainWindow* mainWindow=qobject_cast(view->window()); + if (!mainWindow) { + kDebug()<<"main window is not a katemainwindow"; + return; + } + + if (position==KTextEditor::ViewBarContainer::BottomBar) { + mainWindow->deleteBottomViewBarForView(view); + } else if (position==KTextEditor::ViewBarContainer::TopBar) { + mainWindow->deleteTopViewBarForView(view); + } +} +//END KTextEditor::ViewBarContainer + + + +//BEGIN KTextEditor::MdiContainer +void KateContainer::setActiveView(KTextEditor::View *view) +{ + if ( view && KateApp::self()->activeMainWindow()->viewManager()->viewList().contains(view) ) { + KateApp::self()->activeMainWindow()->viewManager()->setActiveView(view); + } +} + +KTextEditor::View *KateContainer::activeView() +{ + return KateApp::self()->activeMainWindow()->viewManager()->activeView(); +} + +KTextEditor::Document *KateContainer::createDocument() +{ + return KateDocManager::self()->createDoc(); +} + +bool KateContainer::closeDocument(KTextEditor::Document *doc) +{ + if ( doc && KateDocManager::self()->findDocument(doc) ) { + return KateDocManager::self()->closeDocument(doc); + } + return false; +} + +KTextEditor::View *KateContainer::createView(KTextEditor::Document *doc) +{ + if ( ! doc || ! KateApp::self()->documentManager()->findDocument(doc) ) { + return 0; + } + const bool success = KateApp::self()->activeMainWindow()->viewManager()->createView(doc); + if (success) { + return KateApp::self()->activeMainWindow()->viewManager()->activeView(); + } + return 0; +} + +bool KateContainer::closeView(KTextEditor::View *view) +{ + if ( view && KateApp::self()->activeMainWindow()->viewManager()->viewList().contains(view) ) { + return view->close(); + } + return false; +} +//END KTextEditor::MdiContainer + + +#include "moc_katecontainer.cpp" +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/src/app/katecontainer.h b/kate/src/app/katecontainer.h new file mode 100644 index 00000000..27865a12 --- /dev/null +++ b/kate/src/app/katecontainer.h @@ -0,0 +1,70 @@ +/* This file is part of the KDE project + Copyright (C) 2008 Joseph Wenninger + Copyright (C) 2010 Dominik Haumann + + In addition to the following license there is an exception, that the KDE e.V. + may decide on other forms of relicensing. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef _KATE_CONTAINER_ +#define _KATE_CONTAINER_ + +#include + +class KateApp; +#include +namespace KTextEditor { + class View; +} + +class KateContainer + : public QObject + , public KTextEditor::ViewBarContainer + , public KTextEditor::MdiContainer +{ + Q_OBJECT + Q_INTERFACES( KTextEditor::ViewBarContainer ) + Q_INTERFACES( KTextEditor::MdiContainer ) + public: + KateContainer(KateApp* parent); + virtual ~KateContainer(); + + // + // KTextEditor::ViewBarContainer + // + public: + virtual QWidget* getViewBarParent(KTextEditor::View *view,KTextEditor::ViewBarContainer::Position position); + virtual void addViewBarToLayout(KTextEditor::View *view,QWidget *bar,KTextEditor::ViewBarContainer::Position position); + virtual void showViewBarForView(KTextEditor::View *view,KTextEditor::ViewBarContainer::Position position); + virtual void hideViewBarForView(KTextEditor::View *view,KTextEditor::ViewBarContainer::Position position); + virtual void deleteViewBarForView(KTextEditor::View *view,KTextEditor::ViewBarContainer::Position position); + + // + // KTextEditor::MdiContainer + // + public: + virtual void setActiveView( KTextEditor::View * view ); + virtual KTextEditor::View * activeView(); + virtual KTextEditor::Document * createDocument(); + virtual bool closeDocument( KTextEditor::Document * doc ); + virtual KTextEditor::View * createView( KTextEditor::Document * doc ); + virtual bool closeView( KTextEditor::View * view ); +}; + +#endif +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/src/app/katedocmanager.cpp b/kate/src/app/katedocmanager.cpp new file mode 100644 index 00000000..9a9167b7 --- /dev/null +++ b/kate/src/app/katedocmanager.cpp @@ -0,0 +1,787 @@ +/* This file is part of the KDE project + Copyright (C) 2001 Christoph Cullmann + Copyright (C) 2002 Joseph Wenninger + Copyright (C) 2007 Flavio Castelli + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "katedocmanager.h" +#include "moc_katedocmanager.cpp" +#include "kateapp.h" +#include "katemainwindow.h" +#include "kateviewmanager.h" +#include "katecontainer.h" +#include "katesavemodifieddialog.h" + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +KateDocManager::KateDocManager (QObject *parent) + : QObject(parent) + , m_saveMetaInfos(true) + , m_daysMetaInfos(0) + , m_documentStillToRestore (0) + , m_suppressOpeningErrorDialogs (false) +{ + // Constructed the beloved editor ;) + m_editor = KTextEditor::EditorChooser::editor(); + + if ( !m_editor ) + { + KMessageBox::error(0, i18n("A KDE text-editor component could not be found.\n" + "Please check your KDE installation.")); + exit(1); + } + + KTextEditor::ContainerInterface * iface = qobject_cast( m_editor ); + if (iface != NULL) { + iface->setContainer( new KateContainer(KateApp::self()) ); + } else { + kDebug()<<"Editor does not support the container interface"; + } + + // read in editor config + m_editor->readConfig(KGlobal::config().data()); + + m_documentManager = new Kate::DocumentManager (this); + + m_metaInfos = new KConfig("metainfos", KConfig::NoGlobals, "appdata" ); + + createDoc (); +} + +KateDocManager::~KateDocManager () +{ + // write editor config + m_editor->writeConfig(KGlobal::config().data()); + + // write metainfos? + if (m_saveMetaInfos) + { + // saving meta-infos when file is saved is not enough, we need to do it once more at the end + saveMetaInfos( m_docList ); + + // purge saved filesessions + if (m_daysMetaInfos > 0) + { + const QStringList groups = m_metaInfos->groupList(); + QDateTime def(QDate(1970, 1, 1)); + for (QStringList::const_iterator it = groups.begin(); it != groups.end(); ++it) + { + QDateTime last = m_metaInfos->group(*it).readEntry("Time", def); + if (last.daysTo(QDateTime::currentDateTime()) > m_daysMetaInfos) + m_metaInfos->deleteGroup(*it); + } + } + } + + qDeleteAll( m_docInfos ); + delete m_metaInfos; + delete m_documentManager; + // delete m_editor; don't delete this here - it's cleaned up when the plugin is unloaded +} + +KateDocManager *KateDocManager::self () +{ + return KateApp::self()->documentManager (); +} + +void KateDocManager::setSuppressOpeningErrorDialogs (bool suppress) +{ + m_suppressOpeningErrorDialogs = suppress; +} + +KTextEditor::Document *KateDocManager::createDoc (const KateDocumentInfo& docInfo) +{ + kDebug()<<"createDoc"<createDocument(this); + + // turn of the editorpart's own modification dialog, we have our own one, too! + KSharedConfig::Ptr config = KGlobal::config(); + const KConfigGroup generalGroup(config, "General"); + bool ownModNotification = generalGroup.readEntry("Modified Notification", false); + if (qobject_cast(doc)) + qobject_cast(doc)->setModifiedOnDiskWarning (!ownModNotification); + + m_docList.append(doc); + m_docInfos.insert (doc, new KateDocumentInfo (docInfo)); + + // connect internal signals... + connect(doc, SIGNAL(modifiedChanged(KTextEditor::Document*)), this, SLOT(slotModChanged1(KTextEditor::Document*))); + connect(doc, SIGNAL(modifiedOnDisk(KTextEditor::Document*,bool,KTextEditor::ModificationInterface::ModifiedOnDiskReason)), + this, SLOT(slotModifiedOnDisc(KTextEditor::Document*,bool,KTextEditor::ModificationInterface::ModifiedOnDiskReason))); + + // we have a new document, show it the world + + // m_documentManager->documentCreated must come first + // as to signal plugins and other api users about the change + // before the view manager gets a chance to signal a viewChanged + emit m_documentManager->documentCreated (doc); + emit documentCreated (doc); + + // return our new document + return doc; +} + +void KateDocManager::deleteDoc (KTextEditor::Document *doc) +{ + KateApp::self()->emitDocumentClosed(QString("%1").arg((qptrdiff)doc)); + kDebug()<<"deleting document with name:"<documentName(); + + // document will be deleted, soon + emit m_documentManager->documentWillBeDeleted (doc); + + // really delete the document and its infos + delete m_docInfos.take (doc); + delete m_docList.takeAt (m_docList.indexOf(doc)); + + //??????????????? AT THIS POINT THE REFERENCED POINTER IS INVALID + // document is gone, emit our signals + emit documentDeleted (doc); + emit m_documentManager->documentDeleted (doc); +} + +KTextEditor::Document *KateDocManager::document (uint n) +{ + return m_docList.at(n); +} + +KateDocumentInfo *KateDocManager::documentInfo (KTextEditor::Document *doc) +{ + return m_docInfos.contains(doc) ? m_docInfos[doc] : 0; +} + +int KateDocManager::findDocument (KTextEditor::Document *doc) +{ + return m_docList.indexOf (doc); +} + +uint KateDocManager::documents () +{ + return m_docList.count (); +} + +KTextEditor::Document *KateDocManager::findDocument (const KUrl &url) const +{ + KUrl u(url); + u.cleanPath(); + + foreach (KTextEditor::Document* it, m_docList) + { + if ( it->url() == u) + return it; + } + + return 0; +} + +bool KateDocManager::isOpen(KUrl url) +{ + // return just if we found some document with this url + return findDocument (url) != 0; +} + +QList KateDocManager::openUrls(const QList &urls, const QString &encoding, bool isTempFile, const KateDocumentInfo& docInfo) +{ + QList docs; + + emit m_documentManager->aboutToLoadDocuments(); + + foreach (const KUrl &url, urls) { + docs << openUrl(url, encoding, isTempFile, docInfo); + } + + emit m_documentManager->documentsLoaded(docs); + + return docs; +} + +KTextEditor::Document *KateDocManager::openUrl (const KUrl& url, const QString &encoding, bool isTempFile, const KateDocumentInfo& docInfo) +{ + KUrl u(url); + u.cleanPath(); + // special handling if still only the first initial doc is there + if (!documentList().isEmpty() && (documentList().count() == 1) && (!documentList().at(0)->isModified() && documentList().at(0)->url().isEmpty())) + { + KTextEditor::Document* doc = documentList().first(); + + doc->setEncoding(encoding); + + if (!u.isEmpty()) + { + doc->setSuppressOpeningErrorDialogs (m_suppressOpeningErrorDialogs); + + (*documentInfo(doc)) = docInfo; + if (!loadMetaInfos(doc, u)) + doc->openUrl (u); + else if (! encoding.isEmpty()) // set encoding again if provided, as metainfos sets it. + doc->setEncoding(encoding); + + doc->setSuppressOpeningErrorDialogs (false); + + if ( isTempFile && u.isLocalFile() ) + { + QFileInfo fi( u.toLocalFile() ); + if ( fi.exists() ) + { + m_tempFiles[ doc] = qMakePair(u, fi.lastModified()); + kDebug(13001) << "temporary file will be deleted after use unless modified: " << u.pathOrUrl(); + } + } + } + + connect(doc, SIGNAL(modifiedChanged(KTextEditor::Document*)), this, SLOT(slotModChanged(KTextEditor::Document*))); + + emit initialDocumentReplaced(); + + return doc; + } + + KTextEditor::Document *doc = 0; + + // always new document if url is empty... + if (!u.isEmpty()) + doc = findDocument (u); + + if ( !doc ) + { + doc = createDoc (docInfo); + + doc->setEncoding(encoding); + + if (!u.isEmpty()) + { + doc->setSuppressOpeningErrorDialogs (m_suppressOpeningErrorDialogs); + + if (!loadMetaInfos(doc, u)) + doc->openUrl (u); + + doc->setSuppressOpeningErrorDialogs (false); + } + } + + return doc; +} + +bool KateDocManager::closeDocuments(const QList &documents, bool closeUrl) +{ + if (documents.isEmpty()) return false; + + saveMetaInfos( documents ); + + emit m_documentManager->aboutToDeleteDocuments(documents); + + int last = 0; + foreach(KTextEditor::Document *doc, documents) + { + if (closeUrl && !doc->closeUrl()) + return false; // get out on first error + + for (int i = 0; i < KateApp::self()->mainWindows (); i++ ) { + KateApp::self()->mainWindow(i)->viewManager()->closeViews(doc); + } + + if ( closeUrl && m_tempFiles.contains( doc ) ) + { + QFileInfo fi( m_tempFiles[ doc ].first.toLocalFile() ); + if ( fi.lastModified() <= m_tempFiles[ doc ].second || + KMessageBox::questionYesNo( KateApp::self()->activeMainWindow(), + i18n("The supposedly temporary file %1 has been modified. " + "Do you want to delete it anyway?", m_tempFiles[ doc ].first.pathOrUrl()), + i18n("Delete File?") ) == KMessageBox::Yes ) + { + KIO::del( m_tempFiles[ doc ].first, KIO::HideProgressInfo ); + kDebug(13001) << "Deleted temporary file " << m_tempFiles[ doc ].first; + m_tempFiles.remove( doc ); + } + else + { + m_tempFiles.remove(doc); + kDebug(13001) << "The supposedly temporary file " << m_tempFiles[ doc ].first.pathOrUrl() << " have been modified since loaded, and has not been deleted."; + } + } + + deleteDoc (doc); + last++; + } + + emit m_documentManager->documentsDeleted(documents.mid(last)); + + // never ever empty the whole document list + if (m_docList.isEmpty()) + createDoc (); + + return true; +} + +bool KateDocManager::closeDocument(KTextEditor::Document *doc, bool closeUrl) +{ + if (!doc) return false; + + QList documents; + documents.append( doc ); + + return closeDocuments( documents, closeUrl ); +} + +bool KateDocManager::closeOtherDocuments(uint n) +{ + return closeOtherDocuments (document(n)); +} + +bool KateDocManager::closeDocumentList(QList documents) +{ + bool res = true; + + for (int i = 0; i < KateApp::self()->mainWindows (); i++ ) + KateApp::self()->mainWindow(i)->viewManager()->setViewActivationBlocked(true); + + QList modifiedDocuments; + foreach(KTextEditor::Document* document, documents) { + if (document->isModified()) { + modifiedDocuments.append(document); + } + } + + if(modifiedDocuments.size() > 0 && !KateSaveModifiedDialog::queryClose(0, modifiedDocuments)) { + return false; + } + + res = closeDocuments( documents, false ); // Do not show save/discard dialog + + for (int i = 0; i < KateApp::self()->mainWindows (); i++ ) + { + KateApp::self()->mainWindow(i)->viewManager()->setViewActivationBlocked(false); + if (!KateApp::self()->mainWindow(i)->viewManager()->activeView()) + KateApp::self()->mainWindow(i)->viewManager()->activateView (m_docList.at(0)); + else + KateApp::self()->mainWindow(i)->viewManager()->reactivateActiveView(); + } + + return res; +} + +bool KateDocManager::closeAllDocuments(bool closeUrl) +{ + bool res = true; + + QList docs = m_docList; + + for (int i = 0; i < KateApp::self()->mainWindows (); i++ ) + KateApp::self()->mainWindow(i)->viewManager()->setViewActivationBlocked(true); + + res = closeDocuments( docs, closeUrl ); + + for (int i = 0; i < KateApp::self()->mainWindows (); i++ ) + { + KateApp::self()->mainWindow(i)->viewManager()->setViewActivationBlocked(false); + KateApp::self()->mainWindow(i)->viewManager()->activateView (m_docList.at(0)); + } + + return res; +} + +bool KateDocManager::closeOtherDocuments(KTextEditor::Document* doc) +{ + bool res = true; + + QList documents = m_docList; + + for (int i = 0; i < KateApp::self()->mainWindows (); i++ ) + KateApp::self()->mainWindow(i)->viewManager()->setViewActivationBlocked(true); + + documents.removeOne( doc ); + + res = closeDocuments( documents ); + + for (int i = 0; i < KateApp::self()->mainWindows (); i++ ) + { + KateApp::self()->mainWindow(i)->viewManager()->setViewActivationBlocked(false); + KateApp::self()->mainWindow(i)->viewManager()->activateView (m_docList.at(0)); + } + + return res; +} + +/** + * Find all modified documents. + * @return Return the list of all modified documents. + */ +QList KateDocManager::modifiedDocumentList() +{ + QList modified; + foreach (KTextEditor::Document* doc, m_docList) + { + if (doc->isModified()) + { + modified.append(doc); + } + } + return modified; +} + + +bool KateDocManager::queryCloseDocuments(KateMainWindow *w) +{ + int docCount = m_docList.count(); + foreach (KTextEditor::Document *doc, m_docList) + { + if (doc->url().isEmpty() && doc->isModified()) + { + int msgres = KMessageBox::warningYesNoCancel( w, + i18n("

The document '%1' has been modified, but not saved.

" + "

Do you want to save your changes or discard them?

", doc->documentName() ), + i18n("Close Document"), KStandardGuiItem::save(), KStandardGuiItem::discard() ); + + if (msgres == KMessageBox::Cancel) + return false; + + if (msgres == KMessageBox::Yes) + { + KEncodingFileDialog::Result r = KEncodingFileDialog::getSaveUrlAndEncoding( doc->encoding(), QString(), QString(), w, i18n("Save As")); + + doc->setEncoding( r.encoding ); + + if (!r.URLs.isEmpty()) + { + KUrl tmp = r.URLs.first(); + + if ( !doc->saveAs( tmp ) ) + return false; + } + else + return false; + } + } + else + { + if (!doc->queryClose()) + return false; + } + } + + // document count changed while queryClose, abort and notify user + if (m_docList.count() > docCount) + { + KMessageBox::information (w, + i18n ("New file opened while trying to close Kate, closing aborted."), + i18n ("Closing Aborted")); + return false; + } + + return true; +} + + +void KateDocManager::saveAll() +{ + foreach ( KTextEditor::Document *doc, m_docList ) + if ( doc->isModified() ) + doc->documentSave(); +} + +void KateDocManager::saveSelected(const QList &docList) +{ + foreach(KTextEditor::Document* doc, docList) + { + if ( doc->isModified() ) + doc->documentSave(); + } +} + +void KateDocManager::reloadAll() +{ + // reload all docs that are NOT modified on disk + foreach ( KTextEditor::Document *doc, m_docList ) { + if ( ! documentInfo( doc )->modifiedOnDisc ) + doc->documentReload(); + } + + // take care of all documents that ARE modified on disk + KateApp::self()->activeMainWindow()->showModOnDiskPrompt(); +} + +void KateDocManager::closeOrphaned() +{ + QList documents; + + foreach ( KTextEditor::Document *doc, m_docList ) + { + KateDocumentInfo* info = documentInfo( doc ); + if (info && !info->openSuccess) + documents.append( doc ); + } + + closeDocuments( documents ); +} + +void KateDocManager::saveDocumentList (KConfig* config) +{ + KConfigGroup openDocGroup(config, "Open Documents"); + + openDocGroup.writeEntry ("Count", m_docList.count()); + + int i = 0; + foreach ( KTextEditor::Document *doc, m_docList) + { + KConfigGroup cg( config, QString("Document %1").arg(i) ); + if (KTextEditor::ParameterizedSessionConfigInterface *iface = + qobject_cast(doc)) + { + iface->writeParameterizedSessionConfig(cg, KTextEditor::ParameterizedSessionConfigInterface::SkipNone); + } + + i++; + } +} + +void KateDocManager::restoreDocumentList (KConfig* config) +{ + KConfigGroup openDocGroup(config, "Open Documents"); + unsigned int count = openDocGroup.readEntry("Count", 0); + + if (count == 0) + { + return; + } + + KProgressDialog *pd = new KProgressDialog(0, + i18n("Starting Up"), + i18n("Reopening files from the last session...")); + pd->setModal(true); + pd->setAllowCancel(false); + pd->progressBar()->setRange(0, count); + + m_documentStillToRestore = count; + m_openingErrors.clear(); + for (unsigned int i = 0; i < count; i++) + { + KConfigGroup cg( config, QString("Document %1").arg(i)); + KTextEditor::Document *doc = 0; + + if (i == 0) { + doc = document (0); + } + else + doc = createDoc (); + doc->setSuppressOpeningErrorDialogs(true); + connect(doc, SIGNAL(completed()), this, SLOT(documentOpened())); + connect(doc, SIGNAL(canceled(QString)), this, SLOT(documentOpened())); + if (KTextEditor::ParameterizedSessionConfigInterface *iface = + qobject_cast(doc)) + { + iface->readParameterizedSessionConfig(cg, KTextEditor::ParameterizedSessionConfigInterface::SkipNone); + } + + pd->progressBar()->setValue(pd->progressBar()->value() + 1); + } + delete pd; +} + +void KateDocManager::slotModifiedOnDisc (KTextEditor::Document *doc, bool b, KTextEditor::ModificationInterface::ModifiedOnDiskReason reason) +{ + if (m_docInfos.contains(doc)) + { + m_docInfos[doc]->modifiedOnDisc = b; + m_docInfos[doc]->modifiedOnDiscReason = reason; + slotModChanged1(doc); + } +} + +/** + * Load file and file's meta-information if the digest didn't change since last time. + */ +bool KateDocManager::loadMetaInfos(KTextEditor::Document *doc, const KUrl &url) +{ + if (!m_saveMetaInfos) + return false; + + if (!m_metaInfos->hasGroup(url.prettyUrl())) + return false; + + QByteArray checksum; + bool ok = true; + + if (computeUrlChecksum(url, checksum)) + { + KConfigGroup urlGroup( m_metaInfos, url.prettyUrl() ); + const QString old_checksum = urlGroup.readEntry("Checksum"); + + if (checksum == old_checksum) + { + if (KTextEditor::ParameterizedSessionConfigInterface *iface = + qobject_cast(doc)) + { + KTextEditor::ParameterizedSessionConfigInterface::SessionConfigParameter flags = + KTextEditor::ParameterizedSessionConfigInterface::SkipNone; + if (documentInfo(doc)->openedByUser) { + flags = KTextEditor::ParameterizedSessionConfigInterface::SkipEncoding; + } + iface->readParameterizedSessionConfig(urlGroup, flags); + } + } + else + { + urlGroup.deleteGroup(); + ok = false; + } + + m_metaInfos->sync(); + } + + return ok && doc->url() == url; +} + +/** + * Save file's meta-information if doc is in 'unmodified' state + */ + +void KateDocManager::saveMetaInfos(const QList &documents) +{ + if (!m_saveMetaInfos) + return; + + QByteArray sha1; + QDateTime now = QDateTime::currentDateTime(); + + foreach(const KTextEditor::Document *doc, documents) + { + if (doc->isModified()) + continue; + + if (computeUrlChecksum(doc->url(), sha1)) + { + KConfigGroup urlGroup( m_metaInfos, doc->url().prettyUrl() ); + + if (KTextEditor::SessionConfigInterface *iface = qobject_cast(doc)) + iface->writeSessionConfig(urlGroup); + + urlGroup.writeEntry("Checksum", sha1.constData()); + urlGroup.writeEntry("Time", now); + } + } + + m_metaInfos->sync(); +} + +// TODO: KDE 5: KateDocument computes the digest when loading a file, and +// when saving a file. Maybe add "QString KTextEditor::Document::checksum()" const? +bool KateDocManager::computeUrlChecksum(const KUrl &url, QByteArray &result) +{ + QFile f(url.toLocalFile()); + + if (f.exists() && f.open(QIODevice::ReadOnly)) + { +#if QT_VERSION >= 0x041200 + QCryptographicHash crypto(QCryptographicHash::KAT); +#else + QCryptographicHash crypto(QCryptographicHash::Sha1); +#endif + crypto.addData(&f); + QByteArray sha1 = crypto.result(); + + if (sha1.isEmpty()) + return false; + + result = sha1.toHex(); + f.close(); + } + else + return false; + + return true; +} + +void KateDocManager::slotModChanged(KTextEditor::Document * doc) +{ + QList documents; + documents.append( doc ); + saveMetaInfos( documents ); +} + +void KateDocManager::slotModChanged1(KTextEditor::Document * doc) +{ + const KateDocumentInfo *info = documentInfo(doc); + + if (info && info->modifiedOnDisc) + { + QMetaObject::invokeMethod(KateApp::self()->activeMainWindow(), "queueModifiedOnDisc", + Qt::QueuedConnection, Q_ARG(KTextEditor::Document *, doc)); + } +} + +void KateDocManager::documentOpened() +{ + KColorScheme colors(QPalette::Active); + + KTextEditor::Document *doc = qobject_cast(sender()); + if (!doc) return; // should never happen, but who knows + doc->setSuppressOpeningErrorDialogs(false); + disconnect(doc, SIGNAL(completed()), this, SLOT(documentOpened())); + disconnect(doc, SIGNAL(canceled(QString)), this, SLOT(documentOpened())); + if (doc->openingError()) + { + m_openingErrors += '\n' + doc->openingErrorMessage()+"\n\n"; + KateDocumentInfo* info = documentInfo(doc); + if (info) { + info->openSuccess = false; + } + } + --m_documentStillToRestore; + + if (m_documentStillToRestore == 0) + QTimer::singleShot(0, this, SLOT(showRestoreErrors())); +} + +void KateDocManager::showRestoreErrors () +{ + if (!m_openingErrors.isEmpty()) + { + KMessageBox::information (0, + m_openingErrors, + i18n ("Errors/Warnings while opening documents")); + + // clear errors + m_openingErrors.clear (); + } +} + +// kate: space-indent on; indent-width 2; replace-tabs on; + + diff --git a/kate/src/app/katedocmanager.h b/kate/src/app/katedocmanager.h new file mode 100644 index 00000000..a37dcdba --- /dev/null +++ b/kate/src/app/katedocmanager.h @@ -0,0 +1,229 @@ +/* This file is part of the KDE project + Copyright (C) 2001 Christoph Cullmann + Copyright (C) 2002 Joseph Wenninger + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef __KATE_DOCMANAGER_H__ +#define __KATE_DOCMANAGER_H__ + +#include "katemain.h" +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace KParts +{ + class Factory; +} + +class KConfig; +class KateMainWindow; + +class KateDocumentInfo +{ + public: + enum CustomRoles {RestoreOpeningFailedRole }; + + public: + KateDocumentInfo () + : modifiedOnDisc (false) + , modifiedOnDiscReason (KTextEditor::ModificationInterface::OnDiskUnmodified) + , openedByUser(false) + , openSuccess(true) + {} + + bool modifiedOnDisc; + KTextEditor::ModificationInterface::ModifiedOnDiskReason modifiedOnDiscReason; + + bool openedByUser; + bool openSuccess; +}; + +class KateDocManager : public QObject +{ + Q_OBJECT + + public: + KateDocManager (QObject *parent); + ~KateDocManager (); + + /** + * should the document manager suppress all opening error dialogs on openUrl? + + @param suppress suppress dialogs? + */ + void setSuppressOpeningErrorDialogs (bool suppress); + + static KateDocManager *self (); + + Kate::DocumentManager *documentManager () + { + return m_documentManager; + } + KTextEditor::Editor *editor() + { + return m_editor; + } + + KTextEditor::Document *createDoc (const KateDocumentInfo& docInfo = KateDocumentInfo()); + + void deleteDoc (KTextEditor::Document *doc); + + KTextEditor::Document *document (uint n); + + KateDocumentInfo *documentInfo (KTextEditor::Document *doc); + + int findDocument (KTextEditor::Document *doc); + /** Returns the documentNumber of the doc with url URL or -1 if no such doc is found */ + KTextEditor::Document *findDocument (const KUrl &url) const; + + bool isOpen(KUrl url); + + uint documents (); + + const QList &documentList () const + { + return m_docList; + } + + KTextEditor::Document *openUrl(const KUrl&, + const QString &encoding = QString(), + bool isTempFile = false, + const KateDocumentInfo& docInfo = KateDocumentInfo()); + + QList openUrls(const QList&, + const QString &encoding = QString(), + bool isTempFile = false, + const KateDocumentInfo& docInfo = KateDocumentInfo()); + + bool closeDocument(KTextEditor::Document *, bool closeUrl = true); + bool closeDocuments(const QList &documents, bool closeUrl = true); + bool closeDocumentList(QList documents); + bool closeAllDocuments(bool closeUrl = true); + bool closeOtherDocuments(KTextEditor::Document*); + bool closeOtherDocuments(uint); + + QList modifiedDocumentList(); + bool queryCloseDocuments(KateMainWindow *w); + + void saveDocumentList (KConfig *config); + void restoreDocumentList (KConfig *config); + + inline bool getSaveMetaInfos() + { + return m_saveMetaInfos; + } + inline void setSaveMetaInfos(bool b) + { + m_saveMetaInfos = b; + } + + inline int getDaysMetaInfos() + { + return m_daysMetaInfos; + } + inline void setDaysMetaInfos(int i) + { + m_daysMetaInfos = i; + } + + public Q_SLOTS: + /** + * saves all documents that has at least one view. + * documents with no views are ignored :P + */ + void saveAll(); + + /** + * reloads all documents that has at least one view. + * documents with no views are ignored :P + */ + void reloadAll(); + + /** + * close all documents, which could not be reopened + */ + void closeOrphaned(); + + /** + * save selected documents from the File List + */ + void saveSelected(const QList&); + + Q_SIGNALS: + + /** + * This signal is emitted when the \p document was created. + */ + void documentCreated (KTextEditor::Document *document); + + /** + * This signal is emitted when the \p document has been deleted. + * + * Warning !!! DO NOT ACCESS THE DATA REFERENCED BY THE POINTER, IT IS ALREADY INVALID + * Use the pointer only to remove mappings in hash or maps + */ + void documentDeleted (KTextEditor::Document *document); + + void initialDocumentReplaced (); + + private Q_SLOTS: + void slotModifiedOnDisc (KTextEditor::Document *doc, bool b, KTextEditor::ModificationInterface::ModifiedOnDiskReason reason); + void slotModChanged(KTextEditor::Document *doc); + void slotModChanged1(KTextEditor::Document *doc); + + void showRestoreErrors (); + private: + bool loadMetaInfos(KTextEditor::Document *doc, const KUrl &url); + void saveMetaInfos(const QList &docs); + bool computeUrlChecksum(const KUrl &url, QByteArray &result); + + Kate::DocumentManager *m_documentManager; + QList m_docList; + QHash m_docInfos; + + KConfig *m_metaInfos; + bool m_saveMetaInfos; + int m_daysMetaInfos; + + + //KParts::Factory *m_factory; + KTextEditor::Editor *m_editor; + + typedef QPair TPair; + QMap m_tempFiles; + QString m_openingErrors; + int m_documentStillToRestore; + + // suppress error dialogs while opening? + bool m_suppressOpeningErrorDialogs; + + private Q_SLOTS: + void documentOpened(); +}; + +#endif +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/src/app/katemain.cpp b/kate/src/app/katemain.cpp new file mode 100644 index 00000000..1a8e1971 --- /dev/null +++ b/kate/src/app/katemain.cpp @@ -0,0 +1,386 @@ + + +/* This file is part of the KDE project + Copyright (C) 2001 Christoph Cullmann + Copyright (C) 2002 Joseph Wenninger + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "kateapp.h" +#include "katerunninginstanceinfo.h" +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +class KateWaiter : public QObject { + Q_OBJECT + + private: + QCoreApplication *m_app; + QString m_service; + QStringList m_tokens; + public: + KateWaiter (QCoreApplication *app, const QString &service,const QStringList &tokens) + : QObject (app), m_app (app), m_service (service),m_tokens(tokens) { + connect ( QDBusConnection::sessionBus().interface(), SIGNAL(serviceOwnerChanged(QString,QString,QString)) + , this, SLOT(serviceOwnerChanged(QString,QString,QString)) ); + } + + public Q_SLOTS: + void exiting () { + m_app->quit (); + } + + void documentClosed(const QString& token) { + m_tokens.removeAll(token); + if (m_tokens.count()==0) m_app->quit(); + } + + void serviceOwnerChanged( const QString & name, const QString &, const QString &) { + if (name != m_service) + return; + + m_app->quit (); + } +}; + + +int main( int argc, char **argv ) +{ + // here we go, construct the Kate version + QByteArray kateVersion = KateApp::kateVersion().toLatin1(); + + KAboutData aboutData ("kate", 0, ki18n("Kate"), kateVersion, + ki18n( "Kate - Advanced Text Editor" ), KAboutData::License_LGPL_V2, + ki18n( "(c) 2000-2013 The Kate Authors" ), KLocalizedString(), "http://www.kate-editor.org"); + aboutData.setOrganizationDomain("kde.org"); + aboutData.addAuthor (ki18n("Christoph Cullmann"), ki18n("Maintainer"), "cullmann@kde.org", "http://www.cullmann.io"); + aboutData.addAuthor (ki18n("Anders Lund"), ki18n("Core Developer"), "anders@alweb.dk", "http://www.alweb.dk"); + aboutData.addAuthor (ki18n("Joseph Wenninger"), ki18n("Core Developer"), "jowenn@kde.org", "http://stud3.tuwien.ac.at/~e9925371"); + aboutData.addAuthor (ki18n("Hamish Rodda"), ki18n("Core Developer"), "rodda@kde.org"); + aboutData.addAuthor (ki18n("Dominik Haumann"), ki18n("Developer & Highlight wizard"), "dhdev@gmx.de"); + aboutData.addAuthor (ki18n("Alexander Neundorf"), ki18n("Developer"), "neundorf@kde.org"); + aboutData.addAuthor (ki18n("Waldo Bastian"), ki18n( "The cool buffersystem" ), "bastian@kde.org" ); + aboutData.addAuthor (ki18n("Charles Samuels"), ki18n("The Editing Commands"), "charles@kde.org"); + aboutData.addAuthor (ki18n("Matt Newell"), ki18n("Testing, ..."), "newellm@proaxis.com"); + aboutData.addAuthor (ki18n("Michael Bartl"), ki18n("Former Core Developer"), "michael.bartl1@chello.at"); + aboutData.addAuthor (ki18n("Michael McCallum"), ki18n("Core Developer"), "gholam@xtra.co.nz"); + aboutData.addAuthor (ki18n("Jochen Wilhemly"), ki18n( "KWrite Author" ), "digisnap@cs.tu-berlin.de" ); + aboutData.addAuthor (ki18n("Michael Koch"), ki18n("KWrite port to KParts"), "koch@kde.org"); + aboutData.addAuthor (ki18n("Christian Gebauer"), KLocalizedString(), "gebauer@kde.org" ); + aboutData.addAuthor (ki18n("Simon Hausmann"), KLocalizedString(), "hausmann@kde.org" ); + aboutData.addAuthor (ki18n("Glen Parker"), ki18n("KWrite Undo History, Kspell integration"), "glenebob@nwlink.com"); + aboutData.addAuthor (ki18n("Scott Manson"), ki18n("KWrite XML Syntax highlighting support"), "sdmanson@alltel.net"); + aboutData.addAuthor (ki18n("John Firebaugh"), ki18n("Patches and more"), "jfirebaugh@kde.org"); + aboutData.addAuthor (ki18n("Pablo Martín"), ki18n("Python Plugin Developer"), "goinnn@gmail.com", "http://github.com/goinnn/"); + aboutData.addAuthor (ki18n("Gerald Senarclens de Grancy"), ki18n("QA and Scripting"), "oss@senarclens.eu", "http://find-santa.eu/"); + + aboutData.addCredit (ki18n("Matteo Merli"), ki18n("Highlighting for RPM Spec-Files, Perl, Diff and more"), "merlim@libero.it"); + aboutData.addCredit (ki18n("Rocky Scaletta"), ki18n("Highlighting for VHDL"), "rocky@purdue.edu"); + aboutData.addCredit (ki18n("Yury Lebedev"), ki18n("Highlighting for SQL")); + aboutData.addCredit (ki18n("Chris Ross"), ki18n("Highlighting for Ferite")); + aboutData.addCredit (ki18n("Nick Roux"), ki18n("Highlighting for ILERPG")); + aboutData.addCredit (ki18n("Carsten Niehaus"), ki18n("Highlighting for LaTeX")); + aboutData.addCredit (ki18n("Per Wigren"), ki18n("Highlighting for Makefiles, Python")); + aboutData.addCredit (ki18n("Jan Fritz"), ki18n("Highlighting for Python")); + aboutData.addCredit (ki18n("Daniel Naber")); + aboutData.addCredit (ki18n("Roland Pabel"), ki18n("Highlighting for Scheme")); + aboutData.addCredit (ki18n("Cristi Dumitrescu"), ki18n("PHP Keyword/Datatype list")); + aboutData.addCredit (ki18n("Carsten Pfeiffer"), ki18n("Very nice help")); + aboutData.addCredit (ki18n("All people who have contributed and I have forgotten to mention")); + + // command line args init and co + KCmdLineArgs::init (argc, argv, &aboutData); + + KCmdLineOptions options; + options.add("s"); + options.add("start ", ki18n("Start Kate with a given session")); + options.add("startanon", ki18n("Start Kate with a new anonymous session, implies '-n'")); + options.add("n"); + options.add("new", ki18n("Force start of a new kate instance (is ignored if start is used and another kate instance already has the given session opened), forced if no parameters and no URLs are given at all")); + options.add("b"); + options.add("block", ki18n("If using an already running kate instance, block until it exits, if URLs given to open")); + options.add("p"); + options.add("pid ", ki18n("Only try to reuse kate instance with this pid (is ignored if start is used and another kate instance already has the given session opened)")); + options.add("e"); + options.add("encoding ", ki18n("Set encoding for the file to open")); + options.add("l"); + options.add("line ", ki18n("Navigate to this line")); + options.add("c"); + options.add("column ", ki18n("Navigate to this column")); + options.add("i"); + options.add("stdin", ki18n("Read the contents of stdin")); + options.add("u"); + options.add("use", ki18n("Reuse existing Kate instance; default, only for compatibility")); + options.add("+[URL]", ki18n("Document to open")); + KCmdLineArgs::addCmdLineOptions (options); + KCmdLineArgs::addTempFileOption(); + + // get our command line args ;) + KCmdLineArgs* args = KCmdLineArgs::parsedArgs(); + + QDBusConnectionInterface *i = QDBusConnection::sessionBus().interface (); + + KateRunningInstanceMap mapSessionRii; + if (!fillinRunningKateAppInstances(&mapSessionRii)) return 1; + + QStringList kateServices; + for(KateRunningInstanceMap::const_iterator it=mapSessionRii.constBegin();it!=mapSessionRii.constEnd();++it) + { + kateServices<<(*it)->serviceName; + } + QString serviceName; + + + bool force_new=args->isSet("new"); + if (!force_new) { + if ( !( + args->isSet("start") || + args->isSet("new") || + args->isSet("pid") || + args->isSet("encoding") || + args->isSet("line") || + args->isSet("column") || + args->isSet("stdin") || + args->isSet("use") + ) && (args->count()==0)) force_new=true; + } + + QString start_session; + bool session_already_opened=false; + + //check if we try to start an already opened session + if (args->isSet("startanon")) + { + force_new=true; + } + else if (args->isSet("start")) + { + start_session=args->getOption("start"); + if (mapSessionRii.contains(start_session)) { + serviceName=mapSessionRii[start_session]->serviceName; + force_new=false; + session_already_opened=true; + } + } + + //cleanup map + cleanupRunningKateAppInstanceMap(&mapSessionRii); + + //if no new instance is forced and no already opened session is requested, + //check if a pid is given, which should be reused. + // two possibilities: pid given or not... + if ((!force_new) && serviceName.isEmpty()) + { + if ( (args->isSet("pid")) || (!qgetenv("KATE_PID").isEmpty())) + { + QString usePid; + if (args->isSet("pid")) usePid = args->getOption("pid"); + else usePid = QString::fromLocal8Bit(qgetenv("KATE_PID")); + + serviceName = "org.kde.kate-" + usePid; + if (!kateServices.contains(serviceName)) serviceName.clear(); + } + } + + if ( (!force_new) && ( serviceName.isEmpty())) + { + if (kateServices.count()>0) + serviceName=kateServices[0]; + } + + + //check again if service is still running + bool foundRunningService = false; + if (!serviceName.isEmpty ()) + { + QDBusReply there = i->isServiceRegistered (serviceName); + foundRunningService = there.isValid () && there.value(); + } + + + if (foundRunningService) + { + // open given session + if (args->isSet ("start") && (!session_already_opened) ) + { + QDBusMessage m = QDBusMessage::createMethodCall (serviceName, + QLatin1String("/MainApplication"), "org.kde.Kate.Application", "activateSession"); + + QList dbusargs; + dbusargs.append(args->getOption("start")); + m.setArguments(dbusargs); + + QDBusConnection::sessionBus().call (m); + } + + QString enc = args->isSet("encoding") ? args->getOption("encoding") : QByteArray(""); + + bool tempfileSet = KCmdLineArgs::isTempFileSet(); + + // only block, if files to open there.... + bool needToBlock = args->isSet( "block" ) && (args->count() > 0); + + QStringList tokens; + + // open given files... + for (int z = 0; z < args->count(); z++) + { + QDBusMessage m = QDBusMessage::createMethodCall (serviceName, + QLatin1String("/MainApplication"), "org.kde.Kate.Application", "tokenOpenUrl"); + + QList dbusargs; + dbusargs.append(args->url(z).url()); + dbusargs.append(enc); + dbusargs.append(tempfileSet); + m.setArguments(dbusargs); + + QDBusMessage res=QDBusConnection::sessionBus().call (m); + if (res.type()==QDBusMessage::ReplyMessage) + { + if (res.arguments().count()==1) + { + QVariant v=res.arguments()[0]; + if (v.isValid()) + { + QString s=v.toString(); + if ((!s.isEmpty()) && (s!=QString("ERROR"))) + { + tokens<isSet( "stdin" ) ) + { + QTextStream input(stdin, QIODevice::ReadOnly); + + // set chosen codec + QTextCodec *codec = args->isSet("encoding") ? QTextCodec::codecForName(args->getOption("encoding").toUtf8()) : 0; + + if (codec) + input.setCodec (codec); + + QString line; + QString text; + + do + { + line = input.readLine(); + text.append( line + '\n' ); + } + while( !line.isNull() ); + + QDBusMessage m = QDBusMessage::createMethodCall (serviceName, + QLatin1String("/MainApplication"), "org.kde.Kate.Application", "openInput"); + + QList dbusargs; + dbusargs.append(text); + m.setArguments(dbusargs); + + QDBusConnection::sessionBus().call (m); + } + + int line = 0; + int column = 0; + bool nav = false; + + if (args->isSet ("line")) + { + line = args->getOption ("line").toInt() - 1; + nav = true; + } + + if (args->isSet ("column")) + { + column = args->getOption ("column").toInt() - 1; + nav = true; + } + + if (nav) + { + QDBusMessage m = QDBusMessage::createMethodCall (serviceName, + QLatin1String("/MainApplication"), "org.kde.Kate.Application", "setCursor"); + + QList args; + args.append(line); + args.append(column); + m.setArguments(args); + + QDBusConnection::sessionBus().call (m); + } + + // activate the used instance + QDBusMessage activateMsg = QDBusMessage::createMethodCall (serviceName, + QLatin1String("/MainApplication"), "org.kde.Kate.Application", "activate"); + QDBusConnection::sessionBus().call (activateMsg); + + + // application object to have event loop + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + // It's too bad, that we have to use KApplication here, since this forces us to + // register a service to dbus. If we don't use KApplication we cannot use KStartupInfo + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + // + KApplication app; + + // do no session management for this app, just client + app.disableSessionManagement (); + + // connect dbus signal + if (needToBlock) { + KateWaiter *waiter = new KateWaiter (&app, serviceName,tokens); + QDBusConnection::sessionBus().connect(serviceName, QString("/MainApplication"), "org.kde.Kate.Application", "exiting", waiter, SLOT(exiting())); + QDBusConnection::sessionBus().connect(serviceName, QString("/MainApplication"), "org.kde.Kate.Application", "documentClosed", waiter, SLOT(documentClosed(QString))); + } + +#ifdef Q_WS_X11 + // make the world happy, we are started, kind of... + KStartupInfo::appStarted (); +#endif + + // this will wait until exiting is emitted by the used instance, if wanted... + return needToBlock ? app.exec () : 0; + } + + // construct the real kate app object ;) + KateApp app (args); + if (app.shouldExit()) return 0; + + // execute ourself ;) + return app.exec(); +} + +// kate: space-indent on; indent-width 2; replace-tabs on; + +#include "katemain.moc" diff --git a/kate/src/app/katemain.h b/kate/src/app/katemain.h new file mode 100644 index 00000000..da227938 --- /dev/null +++ b/kate/src/app/katemain.h @@ -0,0 +1,44 @@ +/* This file is part of the KDE project + Copyright (C) 2001 Christoph Cullmann + Copyright (C) 2001 Joseph Wenninger + Copyright (C) 2001 Anders Lund + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef __KATE_MAIN_H__ +#define __KATE_MAIN_H__ + +/** + * This header has some global forward declarations for the whole application + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + + + +#endif +// kate: space-indent on; indent-width 2; replace-tabs on; + diff --git a/kate/src/app/katemainwindow.cpp b/kate/src/app/katemainwindow.cpp new file mode 100644 index 00000000..2d407a24 --- /dev/null +++ b/kate/src/app/katemainwindow.cpp @@ -0,0 +1,1071 @@ +/* This file is part of the KDE project + Copyright (C) 2001 Christoph Cullmann + Copyright (C) 2001 Joseph Wenninger + Copyright (C) 2001 Anders Lund + Copyright (C) 2007 Flavio Castelli + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +//BEGIN Includes +#include "katemainwindow.h" +#include "moc_katemainwindow.cpp" + +#include "kateconfigdialog.h" +#include "katedocmanager.h" +#include "katepluginmanager.h" +#include "kateconfigplugindialogpage.h" +#include "kateviewmanager.h" +#include "kateapp.h" +#include "katesavemodifieddialog.h" +#include "katemwmodonhddialog.h" +#include "katesession.h" +#include "kateviewspace.h" +#include "katequickopen.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +//END + +uint KateMainWindow::uniqueID = 1; +KateMwModOnHdDialog *KateMainWindow::s_modOnHdDialog=0; + +KateContainerStackedLayout::KateContainerStackedLayout(QWidget* parent) + : QStackedLayout(parent) +{} + +QSize KateContainerStackedLayout::sizeHint() const +{ + if (currentWidget()) + return currentWidget()->sizeHint(); + return QStackedLayout::sizeHint(); +} + +QSize KateContainerStackedLayout::minimumSize() const +{ + if (currentWidget()) + return currentWidget()->minimumSize(); + return QStackedLayout::minimumSize(); +} + + +KateMainWindow::KateMainWindow (KConfig *sconfig, const QString &sgroup) + : KateMDI::MainWindow (0) +{ + setObjectName((QString("__KateMainWindow#%1").arg(uniqueID)).toLatin1()); + // first the very important id + myID = uniqueID; + uniqueID++; + + m_modignore = false; + + // here we go, set some usable default sizes + if (!initialGeometrySet()) + { + int scnum = QApplication::desktop()->screenNumber(parentWidget()); + QRect desk = QApplication::desktop()->screenGeometry(scnum); + + QSize size; + + // try to load size + if (sconfig) + { + KConfigGroup cg( sconfig, sgroup ); + size.setWidth (cg.readEntry( QString::fromLatin1("Width %1").arg(desk.width()), 0 )); + size.setHeight (cg.readEntry( QString::fromLatin1("Height %1").arg(desk.height()), 0 )); + } + + // if thats fails, try to reuse size + if (size.isEmpty()) + { + // first try to reuse size known from current or last created main window ;=) + if (KateApp::self()->mainWindows () > 0) + { + KateMainWindow *win = KateApp::self()->activeMainWindow (); + + if (!win) + win = KateApp::self()->mainWindow (KateApp::self()->mainWindows () - 1); + + size = win->size(); + } + else // now fallback to hard defaults ;) + { + // first try global app config + KConfigGroup cg( KGlobal::config(), "MainWindow" ); + size.setWidth (cg.readEntry( QString::fromLatin1("Width %1").arg(desk.width()), 0 )); + size.setHeight (cg.readEntry( QString::fromLatin1("Height %1").arg(desk.height()), 0 )); + + if (size.isEmpty()) + size = QSize (qMin (700, desk.width()), qMin(480, desk.height())); + } + + resize (size); + } + } + + // start session restore if needed + startRestore (sconfig, sgroup); + + m_mainWindow = new Kate::MainWindow (this); + + // setup most important actions first, needed by setupMainWindow + setupImportantActions (); + + // setup the most important widgets + setupMainWindow(); + + // setup the actions + setupActions(); + + setStandardToolBarMenuEnabled( true ); + setXMLFile( "kateui.rc" ); + createShellGUI ( true ); + + //kDebug() << "****************************************************************************" << sconfig; + + // register mainwindow in app + KateApp::self()->addMainWindow (this); + + // enable plugin guis + KatePluginManager::self()->enableAllPluginsGUI (this, sconfig); + + // caption update + for (uint i = 0; i < KateDocManager::self()->documents(); i++) + slotDocumentCreated (KateDocManager::self()->document(i)); + + connect(KateDocManager::self(), SIGNAL(documentCreated(KTextEditor::Document*)), this, SLOT(slotDocumentCreated(KTextEditor::Document*))); + + readOptions(); + + if (sconfig) + m_viewManager->restoreViewConfiguration (KConfigGroup(sconfig, sgroup) ); + + finishRestore (); + + fileOpenRecent->loadEntries( KConfigGroup(sconfig, "Recent Files" ) ); + + setAcceptDrops(true); + + connect(KateSessionManager::self(), SIGNAL(sessionChanged()), this, SLOT(updateCaption())); + + connect(this,SIGNAL(sigShowPluginConfigPage(Kate::PluginConfigPageInterface *,uint)),this,SLOT(showPluginConfigPage(Kate::PluginConfigPageInterface *,uint))); + + // prior to this there was (possibly) no view, therefore not context menu. + // Hence, we have to take care of the menu bar here + toggleShowMenuBar(false); +} + +KateMainWindow::~KateMainWindow() +{ + // first, save our fallback window size ;) + saveWindowSize (KConfigGroup(KGlobal::config(), "MainWindow")); + + // save other options ;=) + saveOptions(); + + // unregister mainwindow in app + KateApp::self()->removeMainWindow (this); + + // disable all plugin guis, delete all pluginViews + KatePluginManager::self()->disableAllPluginsGUI (this); +} + +void KateMainWindow::setupImportantActions () +{ + // settings + m_paShowStatusBar = KStandardAction::showStatusbar(this, SLOT(toggleShowStatusBar()), actionCollection()); + m_paShowStatusBar->setWhatsThis(i18n("Use this command to show or hide the view's statusbar")); + m_paShowMenuBar = KStandardAction::showMenubar(this, SLOT(toggleShowMenuBar()), actionCollection()); + + m_paShowPath = new KToggleAction( i18n("Sho&w Path in Titlebar"), this ); + actionCollection()->addAction( "settings_show_full_path", m_paShowPath ); + connect( m_paShowPath, SIGNAL(toggled(bool)), this, SLOT(updateCaption()) ); + m_paShowPath->setWhatsThis(i18n("Show the complete document path in the window caption")); +} + +void KateMainWindow::setupMainWindow () +{ + setToolViewStyle( KMultiTabBar::KDEV3ICON ); + + m_topViewBarContainer=new QWidget(centralWidget()); + m_topContainerStack = new KateContainerStackedLayout(m_topViewBarContainer); + + /** + * create central stacked widget with its children + */ + m_mainStackedWidget = new QStackedWidget (centralWidget()); + ((QBoxLayout*)(centralWidget()->layout()))->setStretchFactor(m_mainStackedWidget,100); + + m_quickOpen = new KateQuickOpen (m_mainStackedWidget, this); + m_mainStackedWidget->addWidget (m_quickOpen); + + m_viewManager = new KateViewManager (m_mainStackedWidget, this); + m_mainStackedWidget->addWidget (m_viewManager); + + // make view manager default visible! + m_mainStackedWidget->setCurrentWidget (m_viewManager); + + m_bottomViewBarContainer=new QWidget(centralWidget()); + m_bottomContainerStack = new KateContainerStackedLayout(m_bottomViewBarContainer); +} + +void KateMainWindow::setupActions() +{ + KAction *a; + + actionCollection()->addAction( KStandardAction::New, "file_new", m_viewManager, SLOT(slotDocumentNew()) ) + ->setWhatsThis(i18n("Create a new document")); + actionCollection()->addAction( KStandardAction::Open, "file_open", m_viewManager, SLOT(slotDocumentOpen()) ) + ->setWhatsThis(i18n("Open an existing document for editing")); + + fileOpenRecent = KStandardAction::openRecent (m_viewManager, SLOT(openUrl(KUrl)), this); + actionCollection()->addAction(fileOpenRecent->objectName(), fileOpenRecent); + fileOpenRecent->setWhatsThis(i18n("This lists files which you have opened recently, and allows you to easily open them again.")); + + a = actionCollection()->addAction( "file_save_all" ); + a->setIcon( KIcon("document-save-all") ); + a->setText( i18n("Save A&ll") ); + a->setShortcut( QKeySequence(Qt::CTRL + Qt::Key_L) ); + connect( a, SIGNAL(triggered()), KateDocManager::self(), SLOT(saveAll()) ); + a->setWhatsThis(i18n("Save all open, modified documents to disk.")); + + a = actionCollection()->addAction( "file_reload_all" ); + a->setText( i18n("&Reload All") ); + connect( a, SIGNAL(triggered()), KateDocManager::self(), SLOT(reloadAll()) ); + a->setWhatsThis(i18n("Reload all open documents.")); + + a = actionCollection()->addAction( "file_close_orphaned" ); + a->setText( i18n("Close Orphaned") ); + connect( a, SIGNAL(triggered()), KateDocManager::self(), SLOT(closeOrphaned()) ); + a->setWhatsThis(i18n("Close all documents in the file list that could not be reopened, because they are not accessible anymore.")); + + actionCollection()->addAction( KStandardAction::Close, "file_close", m_viewManager, SLOT(slotDocumentClose()) ) + ->setWhatsThis(i18n("Close the current document.")); + + a = actionCollection()->addAction( "file_close_other" ); + a->setText( i18n( "Close Other" ) ); + connect( a, SIGNAL(triggered()), this, SLOT(slotDocumentCloseOther()) ); + a->setWhatsThis(i18n("Close other open documents.")); + + a = actionCollection()->addAction( "file_close_all" ); + a->setText( i18n( "Clos&e All" ) ); + connect( a, SIGNAL(triggered()), this, SLOT(slotDocumentCloseAll()) ); + a->setWhatsThis(i18n("Close all open documents.")); + + a = actionCollection()->addAction( KStandardAction::Quit, "file_quit" ); + // Qt::QueuedConnection: delay real shutdown, as we are inside menu action handling (bug #185708) + connect( a, SIGNAL(triggered()), this, SLOT(slotFileQuit()), Qt::QueuedConnection ); + a->setWhatsThis(i18n("Close this window")); + + a = actionCollection()->addAction( "view_new_view" ); + a->setIcon( KIcon("window-new") ); + a->setText( i18n("&New Window") ); + connect( a, SIGNAL(triggered()), this, SLOT(newWindow()) ); + a->setWhatsThis(i18n("Create a new Kate view (a new window with the same document list).")); + + a = actionCollection()->addAction( "view_quick_open" ); + a->setIcon( KIcon("fork") ); + a->setText( i18n("&Quick Open") ); + QList scuts; + scuts << QKeySequence(Qt::CTRL + Qt::ALT + Qt::Key_O) + << QKeySequence(Qt::CTRL + Qt::Key_Tab); + a->setShortcuts(scuts); + connect( a, SIGNAL(triggered()), this, SLOT(slotQuickOpen()) ); + a->setWhatsThis(i18n("Open a form to quick open documents.")); + + KToggleAction* showFullScreenAction = KStandardAction::fullScreen( 0, 0, this, this); + actionCollection()->addAction( showFullScreenAction->objectName(), showFullScreenAction ); + connect( showFullScreenAction, SIGNAL(toggled(bool)), this, SLOT(slotFullScreen(bool))); + + documentOpenWith = new KActionMenu(i18n("Open W&ith"), this); + actionCollection()->addAction("file_open_with", documentOpenWith); + documentOpenWith->setWhatsThis(i18n("Open the current document using another application registered for its file type, or an application of your choice.")); + connect(documentOpenWith->menu(), SIGNAL(aboutToShow()), this, SLOT(mSlotFixOpenWithMenu())); + connect(documentOpenWith->menu(), SIGNAL(triggered(QAction*)), this, SLOT(slotOpenWithMenuAction(QAction*))); + + a = KStandardAction::keyBindings(this, SLOT(editKeys()), actionCollection()); + a->setWhatsThis(i18n("Configure the application's keyboard shortcut assignments.")); + + a = KStandardAction::configureToolbars(this, SLOT(slotEditToolbars()), actionCollection()); + a->setWhatsThis(i18n("Configure which items should appear in the toolbar(s).")); + + QAction* settingsConfigure = KStandardAction::preferences(this, SLOT(slotConfigure()), actionCollection()); + settingsConfigure->setWhatsThis(i18n("Configure various aspects of this application and the editing component.")); + + // tip of the day :-) + actionCollection()->addAction( KStandardAction::TipofDay, this, SLOT(tipOfTheDay()) ) + ->setWhatsThis(i18n("This shows useful tips on the use of this application.")); + + if (KatePluginManager::self()->pluginList().count() > 0) + { + a = actionCollection()->addAction( "help_plugins_contents" ); + a->setText( i18n("&Plugins Handbook") ); + connect( a, SIGNAL(triggered()), this, SLOT(pluginHelp()) ); + a->setWhatsThis(i18n("This shows help files for various available plugins.")); + } + + a = actionCollection()->addAction( "help_about_editor" ); + a->setIcon( KIcon("preferences-plugin") ); + a->setText( i18n("&About Editor Component") ); + connect( a, SIGNAL(triggered()), this, SLOT(aboutEditor()) ); + + connect(m_viewManager, SIGNAL(viewChanged()), m_mainWindow, SIGNAL(viewChanged())); + connect(m_viewManager, SIGNAL(viewCreated(KTextEditor::View*)), m_mainWindow, SIGNAL(viewCreated(KTextEditor::View*))); + connect(m_viewManager, SIGNAL(viewChanged()), this, SLOT(slotWindowActivated())); + connect(m_viewManager, SIGNAL(viewChanged()), this, SLOT(slotUpdateOpenWith())); + connect(m_viewManager, SIGNAL(viewChanged()), this, SLOT(slotUpdateBottomViewBar())); + connect(m_viewManager, SIGNAL(viewChanged()), this, SLOT(slotUpdateTopViewBar())); + + slotWindowActivated (); + + // session actions + a = actionCollection()->addAction( "sessions_new" ); + a->setIcon( KIcon("document-new") ); + a->setText( i18nc("Menu entry Session->New", "&New") ); + // Qt::QueuedConnection to avoid deletion of code that is executed when reducing the amount of mainwindows. (bug #227008) + connect( a, SIGNAL(triggered()), KateSessionManager::self(), SLOT(sessionNew()), Qt::QueuedConnection ); + a = actionCollection()->addAction( "sessions_open" ); + a->setIcon( KIcon("document-open") ); + a->setText( i18n("&Open Session") ); + // Qt::QueuedConnection to avoid deletion of code that is executed when reducing the amount of mainwindows. (bug #227008) + connect( a, SIGNAL(triggered()), KateSessionManager::self(), SLOT(sessionOpen()), Qt::QueuedConnection ); + a = actionCollection()->addAction( "sessions_save" ); + a->setIcon( KIcon("document-save") ); + a->setText( i18n("&Save Session") ); + connect( a, SIGNAL(triggered()), KateSessionManager::self(), SLOT(sessionSave()) ); + a = actionCollection()->addAction( "sessions_save_as" ); + a->setIcon( KIcon("document-save-as") ); + a->setText( i18n("Save Session &As...") ); + connect( a, SIGNAL(triggered()), KateSessionManager::self(), SLOT(sessionSaveAs()) ); + a = actionCollection()->addAction( "sessions_manage" ); + a->setIcon( KIcon("view-choose") ); + a->setText( i18n("&Manage Sessions...") ); + // Qt::QueuedConnection to avoid deletion of code that is executed when reducing the amount of mainwindows. (bug #227008) + connect( a, SIGNAL(triggered()), KateSessionManager::self(), SLOT(sessionManage()), Qt::QueuedConnection ); + + // quick open menu ;) + a = new KateSessionsAction (i18n("&Quick Open Session"), this); + actionCollection()->addAction("sessions_list", a); +} + +void KateMainWindow::slotDocumentCloseAll() +{ + if ( KateDocManager::self()->documents() >= 1 && KMessageBox::warningContinueCancel(this, + i18n ("This will close all open documents. Are you sure you want to continue?"), + i18n ("Close all documents"), + KStandardGuiItem::cont(), + KStandardGuiItem::cancel(), + QString("closeAll")) != KMessageBox::Cancel) + { + if (queryClose_internal()) + KateDocManager::self()->closeAllDocuments(false); + } +} + + +void KateMainWindow::slotDocumentCloseOther(KTextEditor::Document *document) +{ + if (queryClose_internal(document)) + KateDocManager::self()->closeOtherDocuments(document); +} + +void KateMainWindow::slotDocumentCloseSelected(const QList &docList) +{ + QList documents; + foreach(KTextEditor::Document *doc, docList) + { + if(queryClose_internal(doc)) + documents.append(doc); + } + + KateDocManager::self()->closeDocuments(documents); +} + +void KateMainWindow::slotDocumentCloseOther() +{ + if (queryClose_internal(m_viewManager->activeView()->document())) + KateDocManager::self()->closeOtherDocuments(m_viewManager->activeView()->document()); +} + +bool KateMainWindow::queryClose_internal(KTextEditor::Document* doc) +{ + uint documentCount = KateDocManager::self()->documents(); + + if ( ! showModOnDiskPrompt() ) + return false; + + QList modifiedDocuments = KateDocManager::self()->modifiedDocumentList(); + modifiedDocuments.removeAll(doc); + bool shutdown = (modifiedDocuments.count() == 0); + + if (!shutdown) + { + shutdown = KateSaveModifiedDialog::queryClose(this, modifiedDocuments); + } + + if ( KateDocManager::self()->documents() > documentCount ) + { + KMessageBox::information (this, + i18n ("New file opened while trying to close Kate, closing aborted."), + i18n ("Closing Aborted")); + shutdown = false; + } + + return shutdown; +} + +/** + * queryClose(), take care that after the last mainwindow the stuff is closed + */ +bool KateMainWindow::queryClose() +{ + // session saving, can we close all views ? + // just test, not close them actually + if (KateApp::self()->sessionSaving()) + { + return queryClose_internal (); + } + + // normal closing of window + // allow to close all windows until the last without restrictions + if ( KateApp::self()->mainWindows () > 1 ) + return true; + + // last one: check if we can close all documents, try run + // and save docs if we really close down ! + if ( queryClose_internal () ) + { + KateApp::self()->sessionManager()->saveActiveSession(true); + return true; + } + + return false; +} + +void KateMainWindow::newWindow () +{ + KateApp::self()->newMainWindow (KateApp::self()->sessionManager()->activeSession()->configRead()); +} + +void KateMainWindow::slotEditToolbars() +{ + saveMainWindowSettings(KConfigGroup(KGlobal::config(), "MainWindow")); + KEditToolBar dlg( factory() ); + + connect( &dlg, SIGNAL(newToolBarConfig()), this, SLOT(slotNewToolbarConfig()) ); + dlg.exec(); +} + +void KateMainWindow::slotNewToolbarConfig() +{ + applyMainWindowSettings(KConfigGroup(KGlobal::config(), "MainWindow")); +} + +void KateMainWindow::slotFileQuit() +{ + KateApp::self()->shutdownKate (this); +} + +void KateMainWindow::slotFileClose() +{ + m_viewManager->slotDocumentClose(); +} + +void KateMainWindow::slotOpenDocument(KUrl url) +{ + m_viewManager->openUrl(url, + QString(), + true, + false); +} + +void KateMainWindow::readOptions () +{ + KSharedConfig::Ptr config = KGlobal::config(); + + const KConfigGroup generalGroup(config, "General"); + modNotification = generalGroup.readEntry("Modified Notification", false); + KateDocManager::self()->setSaveMetaInfos(generalGroup.readEntry("Save Meta Infos", true)); + KateDocManager::self()->setDaysMetaInfos(generalGroup.readEntry("Days Meta Infos", 30)); + + m_paShowPath->setChecked (generalGroup.readEntry("Show Full Path in Title", false)); + m_paShowStatusBar->setChecked (generalGroup.readEntry("Show Status Bar", true)); + m_paShowMenuBar->setChecked(generalGroup.readEntry("Show Menu Bar", true)); + + // emit signal to hide/show statusbars + toggleShowStatusBar (); + + // update visibility of menu bar + toggleShowMenuBar(false); +} + +void KateMainWindow::saveOptions () +{ + KSharedConfig::Ptr config = KGlobal::config(); + + KConfigGroup generalGroup(config, "General"); + + generalGroup.writeEntry("Save Meta Infos", KateDocManager::self()->getSaveMetaInfos()); + + generalGroup.writeEntry("Days Meta Infos", KateDocManager::self()->getDaysMetaInfos()); + + generalGroup.writeEntry("Show Full Path in Title", m_paShowPath->isChecked()); + generalGroup.writeEntry("Show Status Bar", m_paShowStatusBar->isChecked()); + generalGroup.writeEntry("Show Menu Bar", m_paShowMenuBar->isChecked()); +} + +void KateMainWindow::toggleShowMenuBar(bool showMessage) +{ + if (m_paShowMenuBar->isChecked()) { + menuBar()->show(); + removeMenuBarActionFromContextMenu(); + } else { + if (showMessage) { + const QString accel = m_paShowMenuBar->shortcut().toString(); + KMessageBox::information(this, i18n("This will hide the menu bar completely." + " You can show it again by typing %1.", accel), + i18n("Hide menu bar"), QLatin1String("HideMenuBarWarning")); + } + menuBar()->hide(); + addMenuBarActionToContextMenu(); + } +} + +void KateMainWindow::addMenuBarActionToContextMenu() +{ + if (m_viewManager->activeView()) { + m_viewManager->activeView()->contextMenu()->addAction(m_paShowMenuBar); + } +} + +void KateMainWindow::removeMenuBarActionFromContextMenu() +{ + if (m_viewManager->activeView()) { + m_viewManager->activeView()->contextMenu()->removeAction(m_paShowMenuBar); + } +} + +void KateMainWindow::toggleShowStatusBar () +{ + emit statusBarToggled (); +} + +bool KateMainWindow::showStatusBar () +{ + return m_paShowStatusBar->isChecked (); +} + +void KateMainWindow::slotWindowActivated () +{ + if (m_viewManager->activeView()) + updateCaption (m_viewManager->activeView()->document()); + + // show view manager in any case + if (m_mainStackedWidget->currentWidget () != m_viewManager) + m_mainStackedWidget->setCurrentWidget (m_viewManager); + + // update proxy + centralWidget()->setFocusProxy (m_viewManager->activeView()); +} + +void KateMainWindow::slotUpdateOpenWith() +{ + if (m_viewManager->activeView()) + documentOpenWith->setEnabled(!m_viewManager->activeView()->document()->url().isEmpty()); + else + documentOpenWith->setEnabled(false); +} + +void KateMainWindow::dragEnterEvent( QDragEnterEvent *event ) +{ + if (!event->mimeData()) return; + const bool accept = KUrl::List::canDecode(event->mimeData()) // files + || event->mimeData()->hasText(); // text + event->setAccepted(accept); +} + +void KateMainWindow::dropEvent( QDropEvent *event ) +{ + slotDropEvent(event); +} + +void KateMainWindow::slotDropEvent( QDropEvent * event ) +{ + if (event->mimeData() == 0) return; + + // + // are we dropping files? + // + if (KUrl::List::canDecode(event->mimeData())) { + KUrl::List textlist = KUrl::List::fromMimeData(event->mimeData()); + + // Try to get the KTextEditor::View that sent this, and activate it, so that the file opens in the + // view where it was dropped + KTextEditor::View *kVsender = qobject_cast(QObject::sender()); + if (kVsender != 0) { + QWidget *parent = kVsender->parentWidget(); + if (parent != 0) { + KateViewSpace* vs = qobject_cast(parent->parentWidget()); + if (vs != 0) m_viewManager->setActiveSpace(vs); + } + } + + for (KUrl::List::Iterator i = textlist.begin(); i != textlist.end(); ++i) + { + // if url has no file component, try and recursively scan dir + KFileItem kitem( KFileItem::Unknown, KFileItem::Unknown, *i, true ); + if( kitem.isDir() ) { + KIO::ListJob *list_job = KIO::listRecursive(*i, KIO::DefaultFlags, false); + connect(list_job, SIGNAL(entries(KIO::Job*,KIO::UDSEntryList)), + this, SLOT(slotListRecursiveEntries(KIO::Job*,KIO::UDSEntryList))); + } + else { + m_viewManager->openUrl (*i); + } + } + } + // + // or are we dropping text? + // + else if (event->mimeData()->hasText()) { + KTextEditor::Document * doc = + KateDocManager::self()->createDoc(); + doc->setText(event->mimeData()->text()); + m_viewManager->activateView(doc); + } +} + +void KateMainWindow::slotListRecursiveEntries(KIO::Job *job, const KIO::UDSEntryList &list) +{ + const KUrl dir = static_cast( job )->url(); + foreach( const KIO::UDSEntry &entry, list ) + { + KUrl currentUrl = dir; + currentUrl.addPath( entry.stringValue( KIO::UDSEntry::UDS_NAME ) ); + if( !entry.isDir() ) + { + m_viewManager->openUrl(currentUrl); + } + } +} + +void KateMainWindow::editKeys() +{ + KShortcutsDialog dlg ( KShortcutsEditor::AllActions, KShortcutsEditor::LetterShortcutsAllowed, this ); + + QList clients = guiFactory()->clients(); + + foreach(KXMLGUIClient *client, clients) { + // FIXME there appear to be invalid clients after session switching +// kDebug(13001)<<"adding client to shortcut editor"; +// kDebug(13001)<actionCollection(); +// kDebug(13001)<componentData().aboutData(); +// kDebug(13001)<componentData().aboutData()->programName(); + dlg.addCollection ( client->actionCollection(), client->componentData().aboutData()->programName() ); + } + dlg.configure(); + + QList l = KateDocManager::self()->documentList(); + for (int i = 0;i < l.count();i++) + { +// kDebug(13001)<<"reloading Keysettings for document "<reloadXML(); + QList l1 = l.at(i)->views (); + for (int i1 = 0;i1 < l1.count();i1++) + { + l1.at(i1)->reloadXML(); +// kDebug(13001)<<"reloading Keysettings for view "<openUrl (KUrl(name)); +} + +void KateMainWindow::slotConfigure() +{ + showPluginConfigPage(0,0); +} + +void KateMainWindow::showPluginConfigPage(Kate::PluginConfigPageInterface *configpageinterface,uint id) +{ + if (!m_viewManager->activeView()) + return; + + KateConfigDialog* dlg = new KateConfigDialog (this, m_viewManager->activeView()); + if (configpageinterface) { + dlg->showAppPluginPage(configpageinterface,id); + } + dlg->exec(); + + delete dlg; + + m_viewManager->reactivateActiveView(); // gui (toolbars...) needs to be updated, because + // of possible changes that the configure dialog + // could have done on it, specially for plugins. +} + +KUrl KateMainWindow::activeDocumentUrl() +{ + // anders: i make this one safe, as it may be called during + // startup (by the file selector) + KTextEditor::View *v = m_viewManager->activeView(); + if ( v ) + return v->document()->url(); + return KUrl(); +} + +void KateMainWindow::mSlotFixOpenWithMenu() +{ + // dh: in bug #307699, this slot is called when launching the Kate application + // unfortunately, noone ever could reproduce except users. + KTextEditor::View *activeView = m_viewManager->activeView(); + if (! activeView) + return; + + // cleanup menu + KMenu *menu = documentOpenWith->menu(); + menu->clear(); + + // get a list of appropriate services. + KMimeType::Ptr mime = KMimeType::mimeType(activeView->document()->mimeType()); + //kDebug(13001) << "mime type: " << mime->name(); + + QAction *a = 0; + KService::List offers = KMimeTypeTrader::self()->query(mime->name(), "Application"); + // add all default open-with-actions except "Kate" + for(KService::List::Iterator it = offers.begin(); it != offers.end(); ++it) + { + KService::Ptr service = *it; + if (service->name() == "Kate") continue; + a = menu->addAction(KIcon(service->icon()), service->name()); + a->setData(service->entryPath()); + } + // append "Other..." to call the KDE "open with" dialog. + a = documentOpenWith->menu()->addAction(i18n("&Other...")); + a->setData(QString()); +} + +void KateMainWindow::slotOpenWithMenuAction(QAction* a) +{ + KUrl::List list; + list.append( m_viewManager->activeView()->document()->url() ); + + const QString openWith = a->data().toString(); + if (openWith.isEmpty()) + { + // display "open with" dialog + KOpenWithDialog dlg(list); + if (dlg.exec()) + KRun::run(*dlg.service(), list, this); + return; + } + + KService::Ptr app = KService::serviceByDesktopPath(openWith); + if (app) + { + KRun::run(*app, list, this); + } + else + { + KMessageBox::error(this, i18n("Application '%1' not found.", openWith), i18n("Application not found")); + } +} + +void KateMainWindow::pluginHelp() +{ + KToolInvocation::invokeHelp (QString(), "kate-plugins"); +} + +void KateMainWindow::aboutEditor() +{ + KAboutApplicationDialog ad(KateDocManager::self()->editor()->aboutData(),this); + ad.exec(); +} + +void KateMainWindow::tipOfTheDay() +{ + KTipDialog::showTip( /*0*/this, QString(), true ); +} + +void KateMainWindow::slotFullScreen(bool t) +{ + KToggleFullScreenAction::setFullScreen(this, t); +} + +bool KateMainWindow::showModOnDiskPrompt() +{ + DocVector list; + list.reserve( KateDocManager::self()->documents() ); + foreach( KTextEditor::Document *doc, KateDocManager::self()->documentList()) + { + if ( KateDocManager::self()->documentInfo( doc )->modifiedOnDisc ) + { + list.append( doc ); + } + } + + if ( !list.isEmpty() && !m_modignore ) + { + KateMwModOnHdDialog mhdlg( list, this ); + m_modignore = true; + bool res = mhdlg.exec(); + m_modignore = false; + + return res; + } + return true; +} + +void KateMainWindow::slotDocumentCreated (KTextEditor::Document *doc) +{ + connect(doc, SIGNAL(modifiedChanged(KTextEditor::Document*)), this, SLOT(updateCaption(KTextEditor::Document*))); + connect(doc, SIGNAL(readWriteChanged(KTextEditor::Document*)), this, SLOT(updateCaption(KTextEditor::Document*))); + connect(doc, SIGNAL(documentNameChanged(KTextEditor::Document*)), this, SLOT(updateCaption(KTextEditor::Document*))); + connect(doc, SIGNAL(documentNameChanged(KTextEditor::Document*)), this, SLOT(slotUpdateOpenWith())); + + updateCaption (doc); +} + +void KateMainWindow::updateCaption () +{ + if (m_viewManager->activeView()) + updateCaption(m_viewManager->activeView()->document()); +} + +void KateMainWindow::updateCaption (KTextEditor::Document *doc) +{ + if (!m_viewManager->activeView()) + { + setCaption ("", false); + return; + } + + // block signals from inactive docs + if (!((KTextEditor::Document*)m_viewManager->activeView()->document() == doc)) + return; + + QString c; + if (m_viewManager->activeView()->document()->url().isEmpty() || (!m_paShowPath || !m_paShowPath->isChecked())) + { + c = ((KTextEditor::Document*)m_viewManager->activeView()->document())->documentName(); + } + else + { + c = m_viewManager->activeView()->document()->url().pathOrUrl(); + } + + QString sessName = KateApp::self()->sessionManager()->activeSession()->sessionName(); + if ( !sessName.isEmpty() ) + sessName = QString("%1: ").arg( sessName ); + + QString readOnlyCaption; + if (!m_viewManager->activeView()->document()->isReadWrite()) + readOnlyCaption=i18n(" [read only]"); + + setCaption( sessName + c+readOnlyCaption, + m_viewManager->activeView()->document()->isModified()); +} + +void KateMainWindow::saveProperties(KConfigGroup& config) +{ + saveSession(config); + + // store all plugin view states + int id = KateApp::self()->mainWindowID (this); + foreach(const KatePluginInfo &item, KatePluginManager::self()->pluginList()) + { + if (item.plugin && pluginViews().contains(item.plugin)) { + pluginViews().value(item.plugin)->writeSessionConfig (config.config(), + QString("Plugin:%1:MainWindow:%2").arg(item.saveName()).arg(id) ); + } + } + + fileOpenRecent->saveEntries( KConfigGroup(config.config(), "Recent Files" ) ); + m_viewManager->saveViewConfiguration (config); +} + +void KateMainWindow::readProperties(const KConfigGroup& config) +{ + // KDE5: TODO startRestore should take a const KConfigBase*, or even just a const KConfigGroup&, + // but this propagates down to interfaces/kate/plugin.h so all plugins have to be ported + KConfigBase* configBase = const_cast(config.config()); + startRestore(configBase, config.name()); + + // perhaps enable plugin guis + KatePluginManager::self()->enableAllPluginsGUI (this, configBase); + + finishRestore (); + + fileOpenRecent->loadEntries( KConfigGroup(config.config(), "Recent Files" ) ); + m_viewManager->restoreViewConfiguration (config); +} + +void KateMainWindow::saveGlobalProperties( KConfig* sessionConfig ) +{ + KateDocManager::self()->saveDocumentList (sessionConfig); + + KConfigGroup cg( sessionConfig, "General"); + cg.writeEntry ("Last Session", KateApp::self()->sessionManager()->activeSession()->sessionFileRelative()); + + // save plugin config !! + KateApp::self()->pluginManager()->writeConfig (sessionConfig); + +} + +void KateMainWindow::saveWindowConfig(const KConfigGroup &_config) +{ + KConfigGroup config( _config ); + saveMainWindowSettings(config); + saveWindowSize(config); + config.writeEntry("WindowState", int(((KParts::MainWindow*)this)->windowState())); + config.sync(); +} + +void KateMainWindow::restoreWindowConfig(const KConfigGroup &config) +{ + setWindowState(Qt::WindowNoState); + applyMainWindowSettings(config); + restoreWindowSize(config); + setWindowState( QFlags(config.readEntry("WindowState", int(Qt::WindowActive))) ); +} + +void KateMainWindow::slotUpdateBottomViewBar() +{ + //kDebug()<<"slotUpdateHorizontalViewBar()"<activeView(); + BarState bs=m_bottomViewBarMapping[view]; + if (bs.bar() && bs.state()) { + m_bottomContainerStack->setCurrentWidget(bs.bar()); + m_bottomContainerStack->currentWidget()->show(); + m_bottomViewBarContainer->show(); + } else { + QWidget *wid=m_bottomContainerStack->currentWidget(); + if (wid) wid->hide(); + //kDebug()<hide(); + } +} + + +void KateMainWindow::slotUpdateTopViewBar() +{ + //kDebug()<<"slotUpdateHorizontalViewBar()"<activeView(); + BarState bs=m_topViewBarMapping[view]; + if (bs.bar() && bs.state()) { + m_topContainerStack->setCurrentWidget(bs.bar()); + m_topContainerStack->currentWidget()->show(); + m_topViewBarContainer->show(); + } else { + QWidget *wid=m_topContainerStack->currentWidget(); + if (wid) wid->hide(); + //kDebug()<hide(); + } +} + + +void KateMainWindow::queueModifiedOnDisc(KTextEditor::Document *doc) +{ + if (!modNotification) return; + + if (s_modOnHdDialog==0) { + DocVector list; + list.append(doc); + + s_modOnHdDialog= new KateMwModOnHdDialog( list, this ); + m_modignore = true; + KWindowSystem::setOnAllDesktops( s_modOnHdDialog->winId(), true); + s_modOnHdDialog->exec(); + delete s_modOnHdDialog; // s_modOnHdDialog is set to 0 in destructor of KateMwModOnHdDialog (jowenn!!!) + m_modignore = false; + } else { + s_modOnHdDialog->addDocument(doc); + } +} + +bool KateMainWindow::event( QEvent *e ) +{ + if (e->type() == QEvent::ShortcutOverride) { + QKeyEvent *k = static_cast(e); + emit unhandledShortcutOverride (k); + } + return KateMDI::MainWindow::event(e); +} + +Kate::PluginView *KateMainWindow::pluginView (const QString &name) +{ + Kate::Plugin *plugin = KateApp::self()->pluginManager()->plugin (name); + if (!plugin) + return 0; + + return m_pluginViews.contains(plugin) ? m_pluginViews.value (plugin) : 0; +} + +void KateMainWindow::slotQuickOpen () +{ + /** + * show quick open and pass focus to it + */ + m_quickOpen->update (); + m_mainStackedWidget->setCurrentWidget (m_quickOpen); + centralWidget()->setFocusProxy (m_quickOpen); + m_quickOpen->setFocus (); +} + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/src/app/katemainwindow.h b/kate/src/app/katemainwindow.h new file mode 100644 index 00000000..a6bd550e --- /dev/null +++ b/kate/src/app/katemainwindow.h @@ -0,0 +1,342 @@ +/* This file is part of the KDE project + Copyright (C) 2001 Christoph Cullmann + Copyright (C) 2001 Joseph Wenninger + Copyright (C) 2001 Anders Lund + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef __KATE_MAINWINDOW_H__ +#define __KATE_MAINWINDOW_H__ + +#include "katemain.h" +#include "katemdi.h" + +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace KIO +{ + class UDSEntry; + typedef class QList UDSEntryList; +} + +namespace Kate +{ + class MainWindow; + class Plugin; + class PluginView; + class PluginConfigPageInterface; +} + +class KFileItem; +class KRecentFilesAction; + +class KateViewManager; +class KateMwModOnHdDialog; +class KateQuickOpen; + +#include +// Helper layout class to always provide minimum size +class KateContainerStackedLayout : public QStackedLayout +{ + Q_OBJECT +public: + KateContainerStackedLayout(QWidget* parent); + virtual QSize sizeHint() const; + virtual QSize minimumSize() const; +}; + + +class KateMainWindow : public KateMDI::MainWindow, virtual public KParts::PartBase +{ + Q_OBJECT + + friend class KateConfigDialog; + friend class KateViewManager; + + public: + /** + * Construct the window and restore its state from given config if any + * @param sconfig session config for this window, 0 if none + * @param sgroup session config group to use + */ + KateMainWindow (KConfig *sconfig, const QString &sgroup); + + /** + * Destruct the nice window + */ + ~KateMainWindow(); + + /** + * Accessor methodes for interface and child objects + */ + public: + Kate::MainWindow *mainWindow () + { + return m_mainWindow; + } + + KateViewManager *viewManager () + { + return m_viewManager; + } + + /** + * get a plugin view with identifier \p name. + * \param name the plugin's name + * \return pointer to the plugin view if a plugin with \p name is loaded and has a view for this mainwindow, + * otherwise null + */ + Kate::PluginView *pluginView (const QString &name); + + public: + /** Returns the URL of the current document. + * anders: I add this for use from the file selector. */ + KUrl activeDocumentUrl(); + + uint mainWindowNumber () const + { + return myID; + } + + /** + * Prompts the user for what to do with files that are modified on disk if any. + * This is optionally run when the window receives focus, and when the last + * window is closed. + * @return true if no documents are modified on disk, or all documents were + * handled by the dialog; otherwise (the dialog was canceled) false. + */ + bool showModOnDiskPrompt(); + + public: + /*reimp*/ void readProperties(const KConfigGroup& config); + /*reimp*/ void saveProperties(KConfigGroup& config); + /*reimp*/ void saveGlobalProperties( KConfig* sessionConfig ); + + public: + bool queryClose_internal(KTextEditor::Document *doc = NULL); + + /** + * save the settings, size and state of this window in + * the provided config group + */ + void saveWindowConfig(const KConfigGroup &); + /** + * restore the settings, size and state of this window from + * the provided config group. + */ + void restoreWindowConfig(const KConfigGroup &); + + private: + /** + * Setup actions which pointers are needed already in setupMainWindow + */ + void setupImportantActions (); + + void setupMainWindow(); + void setupActions(); + bool queryClose(); + void addMenuBarActionToContextMenu(); + void removeMenuBarActionFromContextMenu(); + + /** + * read some global options from katerc + */ + void readOptions(); + + /** + * save some global options to katerc + */ + void saveOptions(); + + void dragEnterEvent( QDragEnterEvent * ); + void dropEvent( QDropEvent * ); + + public Q_SLOTS: + void slotFileClose(); + void slotFileQuit(); + void queueModifiedOnDisc(KTextEditor::Document *doc); + + /** + * slots used for actions in the menus/toolbars + * or internal signal connections + */ + private Q_SLOTS: + void newWindow (); + + void slotConfigure(); + + void slotOpenWithMenuAction(QAction* a); + + void slotEditToolbars(); + void slotNewToolbarConfig(); + void slotUpdateOpenWith(); + void slotOpenDocument(KUrl); + + void slotDropEvent(QDropEvent *); + void editKeys(); + void mSlotFixOpenWithMenu(); + + /** + * Show quick open + */ + void slotQuickOpen (); + + void tipOfTheDay(); + + /* to update the caption */ + void slotDocumentCreated (KTextEditor::Document *doc); + void updateCaption (KTextEditor::Document *doc); + // calls updateCaption(doc) with the current document + void updateCaption (); + + void pluginHelp (); + void aboutEditor(); + void slotFullScreen(bool); + + void slotListRecursiveEntries(KIO::Job *job, const KIO::UDSEntryList &list); + + private Q_SLOTS: + void toggleShowStatusBar (); + void toggleShowMenuBar(bool showMessage = true); + + public: + bool showStatusBar (); + + Q_SIGNALS: + void statusBarToggled (); + void unhandledShortcutOverride (QEvent *e); + + public: + void openUrl (const QString &name = QString()); + + QHash &pluginViews () + { + return m_pluginViews; + } + + inline QWidget *bottomViewBarContainer() {return m_bottomViewBarContainer;} + inline void addToBottomViewBarContainer(KTextEditor::View *view,QWidget *bar){m_bottomContainerStack->addWidget (bar); m_bottomViewBarMapping[view]=BarState(bar);} + inline void hideBottomViewBarForView(KTextEditor::View *view) {QWidget *bar; BarState state=m_bottomViewBarMapping.value(view); bar=state.bar(); if (bar) {m_bottomContainerStack->setCurrentWidget(bar); bar->hide(); state.setState(false); m_bottomViewBarMapping[view]=state;} m_bottomViewBarContainer->hide();} + inline void showBottomViewBarForView(KTextEditor::View *view) {QWidget *bar; BarState state=m_bottomViewBarMapping.value(view); bar=state.bar(); if (bar) {m_bottomContainerStack->setCurrentWidget(bar); bar->show(); state.setState(true); m_bottomViewBarMapping[view]=state; m_bottomViewBarContainer->show();}} + inline void deleteBottomViewBarForView(KTextEditor::View *view) {QWidget *bar; BarState state=m_bottomViewBarMapping.take(view); bar=state.bar(); if (bar) {if (m_bottomContainerStack->currentWidget()==bar) m_bottomViewBarContainer->hide(); delete bar;}} + + inline QWidget *topViewBarContainer() {return m_topViewBarContainer;} + inline void addToTopViewBarContainer(KTextEditor::View *view,QWidget *bar){m_topContainerStack->addWidget (bar); m_topViewBarMapping[view]=BarState(bar);} + inline void hideTopViewBarForView(KTextEditor::View *view) {QWidget *bar; BarState state=m_topViewBarMapping.value(view); bar=state.bar(); if (bar) {m_topContainerStack->setCurrentWidget(bar); bar->hide(); state.setState(false); m_topViewBarMapping[view]=state;} m_topViewBarContainer->hide();} + inline void showTopViewBarForView(KTextEditor::View *view) {QWidget *bar; BarState state=m_topViewBarMapping.value(view); bar=state.bar(); if (bar) {m_topContainerStack->setCurrentWidget(bar); bar->show(); state.setState(true); m_topViewBarMapping[view]=state; m_topViewBarContainer->show();}} + inline void deleteTopViewBarForView(KTextEditor::View *view) {QWidget *bar; BarState state=m_topViewBarMapping.take(view); bar=state.bar(); if (bar) {if (m_topContainerStack->currentWidget()==bar) m_topViewBarContainer->hide(); delete bar;}} + + private Q_SLOTS: + void slotUpdateBottomViewBar(); + void slotUpdateTopViewBar(); + + private Q_SLOTS: + void slotDocumentCloseAll(); + void slotDocumentCloseOther(); + void slotDocumentCloseOther(KTextEditor::Document *document); + void slotDocumentCloseSelected(const QList&); + private: + static uint uniqueID; + uint myID; + + Kate::MainWindow *m_mainWindow; + + bool modNotification; + + /** + * stacked widget containing the central area, aka view manager, quickopen, ... + */ + QStackedWidget *m_mainStackedWidget; + + /** + * quick open to fast switch documents + */ + KateQuickOpen *m_quickOpen; + + /** + * keeps track of views + */ + KateViewManager *m_viewManager; + + KRecentFilesAction *fileOpenRecent; + + KActionMenu* documentOpenWith; + + KToggleAction* settingsShowFileselector; + + bool m_modignore; + + // all plugin views for this mainwindow, used by the pluginmanager + QHash m_pluginViews; + + // options: show statusbar + show path + KToggleAction *m_paShowPath; + KToggleAction *m_paShowStatusBar; + KToggleAction *m_paShowMenuBar; + QWidget *m_bottomViewBarContainer; + KateContainerStackedLayout *m_bottomContainerStack; + QWidget *m_topViewBarContainer; + KateContainerStackedLayout *m_topContainerStack; + + class BarState{ + public: + BarState():m_bar(0),m_state(false){} + BarState(QWidget* bar):m_bar(bar),m_state(false){} + ~BarState(){} + QWidget *bar(){return m_bar;} + bool state(){return m_state;} + void setState(bool state){m_state=state;} + private: + QWidget *m_bar; + bool m_state; + }; + QHash m_bottomViewBarMapping; + QHash m_topViewBarMapping; + + public: + static void unsetModifiedOnDiscDialogIfIf(KateMwModOnHdDialog* diag) { + if (s_modOnHdDialog==diag) s_modOnHdDialog=0; + } + private: + static KateMwModOnHdDialog *s_modOnHdDialog; + + public Q_SLOTS: + void showPluginConfigPage(Kate::PluginConfigPageInterface *configpageinterface,uint id); + + void slotWindowActivated (); + + protected: + virtual bool event( QEvent *e ); +}; + +#endif + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/src/app/katemdi.cpp b/kate/src/app/katemdi.cpp new file mode 100644 index 00000000..533c8bc2 --- /dev/null +++ b/kate/src/app/katemdi.cpp @@ -0,0 +1,1024 @@ +/* This file is part of the KDE libraries + Copyright (C) 2005 Christoph Cullmann + Copyright (C) 2002, 2003 Joseph Wenninger + + GUIClient partly based on ktoolbarhandler.cpp: Copyright (C) 2002 Simon Hausmann + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "katemdi.h" +#include "moc_katemdi.cpp" + +#include "kate/pluginconfigpageinterface.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +//Added by qt3to4: +#include +#include +#include +#include +#include +namespace KateMDI +{ + +//BEGIN TOGGLETOOLVIEWACTION +// + ToggleToolViewAction::ToggleToolViewAction ( const QString& text, ToolView *tv, + QObject* parent ) + : KToggleAction(text, parent) + , m_tv(tv) + { + connect(this, SIGNAL(toggled(bool)), this, SLOT(slotToggled(bool))); + connect(m_tv, SIGNAL(toolVisibleChanged(bool)), this, SLOT(toolVisibleChanged(bool))); + + setChecked(m_tv->toolVisible()); + } + + ToggleToolViewAction::~ToggleToolViewAction() + {} + + void ToggleToolViewAction::toolVisibleChanged(bool) + { + if (isChecked() != m_tv->toolVisible()) + setChecked (m_tv->toolVisible()); + } + + void ToggleToolViewAction::slotToggled(bool t) + { + if (t) + { + m_tv->mainWindow()->showToolView (m_tv); + m_tv->setFocus (); + } + else + { + m_tv->mainWindow()->hideToolView (m_tv); + m_tv->mainWindow()->centralWidget()->setFocus (); + } + } + +//END TOGGLETOOLVIEWACTION + + +//BEGIN GUICLIENT + + static const char *actionListName = "kate_mdi_view_actions"; + + static const char *guiDescription = "" + "" + "" + " " + " " + " " + "" + ""; + + GUIClient::GUIClient ( MainWindow *mw ) + : QObject ( mw ) + , KXMLGUIClient ( mw ) + , m_mw (mw) + { + connect( m_mw->guiFactory(), SIGNAL(clientAdded(KXMLGUIClient*)), + this, SLOT(clientAdded(KXMLGUIClient*)) ); + + if ( domDocument().documentElement().isNull() ) + { + QString completeDescription = QString::fromLatin1( guiDescription ) + .arg( actionListName ); + + setXML( completeDescription, false /*merge*/ ); + } + + m_toolMenu = new KActionMenu(i18n("Tool &Views"), this); + actionCollection()->addAction("kate_mdi_toolview_menu", m_toolMenu); + m_showSidebarsAction = new KToggleAction( i18n("Show Side&bars"), this ); + actionCollection()->addAction( "kate_mdi_sidebar_visibility", m_showSidebarsAction ); + m_showSidebarsAction->setShortcut( Qt::CTRL | Qt::ALT | Qt::SHIFT | Qt::Key_F ); + + m_showSidebarsAction->setChecked( m_mw->sidebarsVisible() ); + connect( m_showSidebarsAction, SIGNAL(toggled(bool)), + m_mw, SLOT(setSidebarsVisible(bool)) ); + + m_toolMenu->addAction( m_showSidebarsAction ); + QAction *sep_act = new QAction( this ); + sep_act->setSeparator( true ); + m_toolMenu->addAction( sep_act ); + + // read shortcuts + actionCollection()->setConfigGroup( "Shortcuts" ); + actionCollection()->readSettings(); + + actionCollection()->addAssociatedWidget(m_mw); + foreach (QAction* action, actionCollection()->actions()) + action->setShortcutContext(Qt::WidgetWithChildrenShortcut); + } + + GUIClient::~GUIClient() +{} + + void GUIClient::updateSidebarsVisibleAction() + { + m_showSidebarsAction->setChecked( m_mw->sidebarsVisible() ); + } + + void GUIClient::registerToolView (ToolView *tv) + { + QString aname = QString("kate_mdi_toolview_") + tv->id; + + // try to read the action shortcut + KShortcut sc; + KSharedConfig::Ptr cfg = KGlobal::config(); + sc = KShortcut( cfg->group("Shortcuts").readEntry( aname, QString() ) ); + + KToggleAction *a = new ToggleToolViewAction(i18n("Show %1", tv->text), tv, this ); + a->setShortcut(sc, KAction::ActiveShortcut); // no DefaultShortcut! see bug #144945 + actionCollection()->addAction( aname.toLatin1(), a ); + + m_toolViewActions.append(a); + m_toolMenu->addAction(a); + + m_toolToAction.insert (tv, a); + + updateActions(); + } + + void GUIClient::unregisterToolView (ToolView *tv) + { + KAction *a = m_toolToAction[tv]; + + if (!a) + return; + + m_toolViewActions.removeAt( m_toolViewActions.indexOf(a) ); + delete a; + + m_toolToAction.remove (tv); + + updateActions(); + } + + void GUIClient::clientAdded( KXMLGUIClient *client ) + { + if ( client == this ) + updateActions(); + } + + void GUIClient::updateActions() + { + if ( !factory() ) + return; + + unplugActionList( actionListName ); + + QList addList; + addList.append(m_toolMenu); + + plugActionList( actionListName, addList ); + } + +//END GUICLIENT + + +//BEGIN TOOLVIEW + + ToolView::ToolView (MainWindow *mainwin, Sidebar *sidebar, QWidget *parent) + : KVBox (parent) + , m_mainWin (mainwin) + , m_sidebar (sidebar) + , m_toolVisible (false) + , persistent (false) + { + // try to fix resize policy + setSizePolicy (QSizePolicy (QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding)); + } + + ToolView::~ToolView () + { + m_mainWin->toolViewDeleted (this); + } + + void ToolView::setToolVisible (bool vis) + { + if (m_toolVisible == vis) + return; + + m_toolVisible = vis; + emit toolVisibleChanged (m_toolVisible); + } + + bool ToolView::toolVisible () const + { + return m_toolVisible; + } + + void ToolView::childEvent ( QChildEvent *ev ) + { + // set the widget to be focus proxy if possible + if ((ev->type() == QEvent::ChildAdded) && qobject_cast(ev->child())) + setFocusProxy (qobject_cast(ev->child())); + + KVBox::childEvent (ev); + } + +//END TOOLVIEW + + +//BEGIN SIDEBAR + + Sidebar::Sidebar (KMultiTabBar::KMultiTabBarPosition pos, MainWindow *mainwin, QWidget *parent) + : KMultiTabBar (pos, parent) + , m_mainWin (mainwin) + , m_splitter (0) + , m_ownSplit (0) + , m_lastSize (0) + { + hide (); + } + + Sidebar::~Sidebar () + {} + + void Sidebar::setSplitter (QSplitter *sp) + { + m_splitter = sp; + m_ownSplit = new QSplitter ((position() == KMultiTabBar::Top || position() == KMultiTabBar::Bottom) ? Qt::Horizontal : Qt::Vertical, m_splitter); + m_ownSplit->setOpaqueResize( KGlobalSettings::opaqueResize() ); + m_ownSplit->setChildrenCollapsible( false ); + m_ownSplit->hide (); + } + + ToolView *Sidebar::addWidget (const QPixmap &icon, const QString &text, ToolView *widget) + { + static int id = 0; + + if (widget) + { + if (widget->sidebar() == this) + return widget; + + widget->sidebar()->removeWidget (widget); + } + + int newId = ++id; + + appendTab (icon, newId, text); + + if (!widget) + { + widget = new ToolView (m_mainWin, this, m_ownSplit); + widget->hide (); + widget->icon = icon; + widget->text = text; + } + else + { + widget->hide (); + widget->setParent (m_ownSplit); + widget->m_sidebar = this; + } + + // save its pos ;) + widget->persistent = false; + + m_idToWidget.insert (newId, widget); + m_widgetToId.insert (widget, newId); + m_toolviews.push_back (widget); + + // widget => size, for correct size restoration after hide/show + // starts with invalid size + m_widgetToSize.insert (widget, QSize()); + + show (); + + connect(tab(newId), SIGNAL(clicked(int)), this, SLOT(tabClicked(int))); + tab(newId)->installEventFilter(this); + + return widget; + } + + bool Sidebar::removeWidget (ToolView *widget) + { + if (!m_widgetToId.contains(widget)) + return false; + + removeTab(m_widgetToId[widget]); + + m_idToWidget.remove (m_widgetToId[widget]); + m_widgetToId.remove (widget); + m_widgetToSize.remove (widget); + m_toolviews.removeAt (m_toolviews.indexOf (widget)); + + bool anyVis = false; + QMapIterator it( m_idToWidget ); + while ( it.hasNext() ) + { + it.next(); + if (!anyVis) + anyVis = it.value()->isVisible(); + } + + if (m_idToWidget.isEmpty()) + { + m_ownSplit->hide (); + hide (); + } + else if (!anyVis) + m_ownSplit->hide (); + + return true; + } + + bool Sidebar::showWidget (ToolView *widget) + { + if (!m_widgetToId.contains(widget)) + return false; + + // hide other non-persistent views + QMapIterator it( m_idToWidget ); + while ( it.hasNext() ) + { + it.next(); + if ((it.value() != widget) && !it.value()->persistent) + { + it.value()->hide(); + setTab (it.key(), false); + it.value()->setToolVisible(false); + } + } + + setTab (m_widgetToId[widget], true); + + // set minimum size again to 80,80! + // we changed that on hide! + widget->setMinimumSize (80, 80); + + /** + * resize to right size again and show, else artefacts + */ + if (m_widgetToSize[widget].isValid()) + widget->resize (m_widgetToSize[widget]); + widget->show (); + + /** + * resize to right size again and show, else artefacts + * same as for widget, both needed + */ + if (m_preHideSize.isValid()) + m_ownSplit->resize (m_preHideSize); + m_ownSplit->show (); + + /** + * we are visible again! + */ + widget->setToolVisible (true); + return true; + } + + bool Sidebar::hideWidget (ToolView *widget) + { + if (!m_widgetToId.contains(widget)) + return false; + + bool anyVis = false; + + updateLastSize (); + + QMapIterator it( m_idToWidget ); + while ( it.hasNext() ) + { + it.next(); + if (it.value() == widget) + { + // remember size and hide + if (widget->isVisible()) + m_widgetToSize[widget] = widget->size(); + continue; + } + + if (!anyVis) + anyVis = it.value()->isVisible(); + } + + // lower tab + setTab (m_widgetToId[widget], false); + + if (!anyVis) { + if (m_ownSplit->isVisible()) + m_preHideSize = m_ownSplit->size (); + m_ownSplit->hide (); + } + + // set minimum size == size, this avoid artifical resizes on show + // there we will reset this again to 80,80! + widget->setMinimumSize (widget->size()); + + widget->hide (); + + widget->setToolVisible (false); + + return true; + } + + void Sidebar::tabClicked(int i) + { + ToolView *w = m_idToWidget[i]; + + if (!w) + return; + + if (isTabRaised(i)) + { + showWidget (w); + w->setFocus (); + } + else + { + hideWidget (w); + m_mainWin->centralWidget()->setFocus (); + } + } + + bool Sidebar::eventFilter(QObject *obj, QEvent *ev) + { + if (ev->type() == QEvent::ContextMenu) + { + QContextMenuEvent *e = (QContextMenuEvent *) ev; + KMultiTabBarTab *bt = dynamic_cast(obj); + if (bt) + { + //kDebug() << "Request for popup"; + + m_popupButton = bt->id(); + + ToolView *w = m_idToWidget[m_popupButton]; + + if (w) + { + KMenu *p = new KMenu (this); + + if (!w->plugin.isNull()) { + Kate::PluginConfigPageInterface* pcpi=dynamic_cast(w->plugin.data()); + if (pcpi) { + if (pcpi->configPages()>0) + p->addAction(i18n("Configure ..."))->setData(20); + } + } + + p->addTitle(SmallIcon("view_remove"), i18n("Behavior")); + + p->addAction(w->persistent ? KIcon("view-restore") : KIcon("view-fullscreen"), + w->persistent ? i18n("Make Non-Persistent") : i18n("Make Persistent") ) -> setData(10); + + p->addTitle(SmallIcon("move"), i18n("Move To")); + + if (position() != 0) + p->addAction(KIcon("go-previous"), i18n("Left Sidebar"))->setData(0); + + if (position() != 1) + p->addAction(KIcon("go-next"), i18n("Right Sidebar"))->setData(1); + + if (position() != 2) + p->addAction(KIcon("go-up"), i18n("Top Sidebar"))->setData(2); + + if (position() != 3) + p->addAction(KIcon("go-down"), i18n("Bottom Sidebar"))->setData(3); + + connect(p, SIGNAL(triggered(QAction*)), + this, SLOT(buttonPopupActivate(QAction*))); + + p->exec(e->globalPos()); + delete p; + + return true; + } + } + } + + return false; + } + + void Sidebar::setVisible(bool visible) + { + // visible==true means show-request + if( visible && ( m_idToWidget.isEmpty() || !m_mainWin->sidebarsVisible() ) ) + return; + + KMultiTabBar::setVisible(visible); + } + + void Sidebar::buttonPopupActivate (QAction *a) + { + int id = a->data().toInt(); + ToolView *w = m_idToWidget[m_popupButton]; + + if (!w) + return; + + // move ids + if (id < 4) + { + // move + show ;) + m_mainWin->moveToolView (w, (KMultiTabBar::KMultiTabBarPosition) id); + m_mainWin->showToolView (w); + } + + // toggle persistent + if (id == 10) + w->persistent = !w->persistent; + + // configure actionCollection + if (id==20) { + if (!w->plugin.isNull()) { + Kate::PluginConfigPageInterface* pcpi=dynamic_cast(w->plugin.data()); + if (pcpi) { + if (pcpi->configPages()>0) + emit sigShowPluginConfigPage(pcpi,0); + } + } + } + } + + void Sidebar::updateLastSize () + { + QList s = m_splitter->sizes (); + + int i = 0; + if ((position() == KMultiTabBar::Right || position() == KMultiTabBar::Bottom)) + i = 2; + + // little threshold + if (s[i] > 2) + m_lastSize = s[i]; + } + + class TmpToolViewSorter + { + public: + ToolView *tv; + unsigned int pos; + }; + + void Sidebar::restoreSession (KConfigGroup& config) + { + // get the last correct placed toolview + int firstWrong = 0; + for ( ; firstWrong < m_toolviews.size(); ++firstWrong ) + { + ToolView *tv = m_toolviews[firstWrong]; + + int pos = config.readEntry (QString ("Kate-MDI-ToolView-%1-Sidebar-Position").arg(tv->id), firstWrong); + + if (pos != firstWrong) + break; + } + + // we need to reshuffle, ahhh :( + if (firstWrong < m_toolviews.size()) + { + // first: collect the items to reshuffle + QList toSort; + for (int i = firstWrong; i < m_toolviews.size(); ++i) + { + TmpToolViewSorter s; + s.tv = m_toolviews[i]; + s.pos = config.readEntry (QString ("Kate-MDI-ToolView-%1-Sidebar-Position").arg(m_toolviews[i]->id), i); + toSort.push_back (s); + } + + // now: sort the stuff we need to reshuffle + for (int m = 0; m < toSort.size(); ++m) + for (int n = m + 1; n < toSort.size(); ++n) + if (toSort[n].pos < toSort[m].pos) + { + TmpToolViewSorter tmp = toSort[n]; + toSort[n] = toSort[m]; + toSort[m] = tmp; + } + + // then: remove this items from the button bar + // do this backwards, to minimize the relayout efforts + for (int i = m_toolviews.size() - 1; i >= (int)firstWrong; --i) + { + removeTab (m_widgetToId[m_toolviews[i]]); + } + + // insert the reshuffled things in order :) + for (int i = 0; i < toSort.size(); ++i) + { + ToolView *tv = toSort[i].tv; + + m_toolviews[firstWrong+i] = tv; + + // readd the button + int newId = m_widgetToId[tv]; + appendTab (tv->icon, newId, tv->text); + connect(tab(newId), SIGNAL(clicked(int)), this, SLOT(tabClicked(int))); + tab(newId)->installEventFilter(this); + + // reshuffle in splitter: move to last + m_ownSplit->addWidget(tv); + } + } + + // update last size if needed + updateLastSize (); + + // restore the own splitter sizes + QList s = config.readEntry (QString ("Kate-MDI-Sidebar-%1-Splitter").arg(position()), QList()); + m_ownSplit->setSizes (s); + + // show only correct toolviews, remember persistent values ;) + bool anyVis = false; + for ( int i = 0; i < m_toolviews.size(); ++i ) + { + ToolView *tv = m_toolviews[i]; + + tv->persistent = config.readEntry (QString ("Kate-MDI-ToolView-%1-Persistent").arg(tv->id), false); + tv->setToolVisible (config.readEntry (QString ("Kate-MDI-ToolView-%1-Visible").arg(tv->id), false)); + + if (!anyVis) + anyVis = tv->toolVisible(); + + setTab (m_widgetToId[tv], tv->toolVisible()); + + if (tv->toolVisible()) + tv->show(); + else + tv->hide (); + } + + if (anyVis) + m_ownSplit->show(); + else + m_ownSplit->hide(); + } + + void Sidebar::saveSession (KConfigGroup& config) + { + // store the own splitter sizes + QList s = m_ownSplit->sizes(); + config.writeEntry (QString ("Kate-MDI-Sidebar-%1-Splitter").arg(position()), s); + + // store the data about all toolviews in this sidebar ;) + for ( int i = 0; i < m_toolviews.size(); ++i ) + { + ToolView *tv = m_toolviews[i]; + + config.writeEntry (QString ("Kate-MDI-ToolView-%1-Position").arg(tv->id), int(tv->sidebar()->position())); + config.writeEntry (QString ("Kate-MDI-ToolView-%1-Sidebar-Position").arg(tv->id), i); + config.writeEntry (QString ("Kate-MDI-ToolView-%1-Visible").arg(tv->id), tv->toolVisible()); + config.writeEntry (QString ("Kate-MDI-ToolView-%1-Persistent").arg(tv->id), tv->persistent); + } + } + +//END SIDEBAR + + +//BEGIN MAIN WINDOW + + MainWindow::MainWindow (QWidget* parentWidget) + : KParts::MainWindow(parentWidget, Qt::Window) + , m_sidebarsVisible(true) + , m_restoreConfig (0) + , m_guiClient (new GUIClient (this)) + { + // init the internal widgets + KHBox *hb = new KHBox (this); + setCentralWidget(hb); + + m_sidebars[KMultiTabBar::Left] = new Sidebar (KMultiTabBar::Left, this, hb); + + m_hSplitter = new QSplitter (Qt::Horizontal, hb); + m_hSplitter->setOpaqueResize( KGlobalSettings::opaqueResize() ); + + m_sidebars[KMultiTabBar::Left]->setSplitter (m_hSplitter); + + KVBox *vb = new KVBox (m_hSplitter); + m_hSplitter->setCollapsible( m_hSplitter->indexOf(vb), false); + m_hSplitter->setStretchFactor( m_hSplitter->indexOf(vb), 1); + + m_sidebars[KMultiTabBar::Top] = new Sidebar (KMultiTabBar::Top, this, vb); + + m_vSplitter = new QSplitter (Qt::Vertical, vb); + m_vSplitter->setOpaqueResize( KGlobalSettings::opaqueResize() ); + + m_sidebars[KMultiTabBar::Top]->setSplitter (m_vSplitter); + + m_centralWidget = new KVBox (m_vSplitter); + m_centralWidget->layout()->setSpacing( 0 ); + m_centralWidget->layout()->setMargin( 0 ); + m_vSplitter->setCollapsible( m_vSplitter->indexOf(m_centralWidget), false); + m_vSplitter->setStretchFactor( m_vSplitter->indexOf(m_centralWidget), 1); + + m_sidebars[KMultiTabBar::Bottom] = new Sidebar (KMultiTabBar::Bottom, this, vb); + m_sidebars[KMultiTabBar::Bottom]->setSplitter (m_vSplitter); + + m_sidebars[KMultiTabBar::Right] = new Sidebar (KMultiTabBar::Right, this, hb); + m_sidebars[KMultiTabBar::Right]->setSplitter (m_hSplitter); + + for (int i=0;i<4;i++) + connect(m_sidebars[i],SIGNAL(sigShowPluginConfigPage(Kate::PluginConfigPageInterface *,uint)),this,SIGNAL(sigShowPluginConfigPage(Kate::PluginConfigPageInterface *,uint))); + + + } + + MainWindow::~MainWindow () + { + // cu toolviews + while (!m_toolviews.isEmpty()) + delete m_toolviews[0]; + + // seems like we really should delete this by hand ;) + delete m_centralWidget; + + for (unsigned int i = 0; i < 4; ++i) + delete m_sidebars[i]; + } + + QWidget *MainWindow::centralWidget () const + { + return m_centralWidget; + } + + ToolView *MainWindow::createToolView (Kate::Plugin* plugin, const QString &identifier, KMultiTabBar::KMultiTabBarPosition pos, const QPixmap &icon, const QString &text) + { + if (m_idToWidget[identifier]) + return 0; + + // try the restore config to figure out real pos + if (m_restoreConfig && m_restoreConfig->hasGroup (m_restoreGroup)) + { + KConfigGroup cg(m_restoreConfig, m_restoreGroup); + pos = (KMultiTabBar::KMultiTabBarPosition) cg.readEntry (QString ("Kate-MDI-ToolView-%1-Position").arg(identifier), int(pos)); + } + + ToolView *v = m_sidebars[pos]->addWidget (icon, text, 0); + v->id = identifier; + v->plugin = plugin; + v->setMinimumSize(80, 80); + + m_idToWidget.insert (identifier, v); + m_toolviews.push_back (v); + + // register for menu stuff + m_guiClient->registerToolView (v); + + return v; + } + + ToolView *MainWindow::toolView (const QString &identifier) const + { + return m_idToWidget[identifier]; + } + + void MainWindow::toolViewDeleted (ToolView *widget) + { + if (!widget) + return; + + if (widget->mainWindow() != this) + return; + + // unregister from menu stuff + m_guiClient->unregisterToolView (widget); + + widget->sidebar()->removeWidget (widget); + + m_idToWidget.remove (widget->id); + m_toolviews.removeAt ( m_toolviews.indexOf(widget) ); + + } + + void MainWindow::setSidebarsVisible( bool visible ) + { + bool old_visible=m_sidebarsVisible; + m_sidebarsVisible = visible; + + m_sidebars[0]->setVisible(visible); + m_sidebars[1]->setVisible(visible); + m_sidebars[2]->setVisible(visible); + m_sidebars[3]->setVisible(visible); + + m_guiClient->updateSidebarsVisibleAction(); + + // show information message box, if the users hides the sidebars + if( old_visible && (!m_sidebarsVisible) ) + { + KMessageBox::information( this, + i18n("You are about to hide the sidebars. With " + "hidden sidebars it is not possible to directly " + "access the tool views with the mouse anymore, " + "so if you need to access the sidebars again " + "invoke View > Tool Views > Show Sidebars " + "in the menu. It is still possible to show/hide " + "the tool views with the assigned shortcuts."), + QString(), "Kate hide sidebars notification message" ); + } + } + + bool MainWindow::sidebarsVisible() const + { + return m_sidebarsVisible; + } + + void MainWindow::setToolViewStyle (KMultiTabBar::KMultiTabBarStyle style) + { + m_sidebars[0]->setStyle(style); + m_sidebars[1]->setStyle(style); + m_sidebars[2]->setStyle(style); + m_sidebars[3]->setStyle(style); + } + + KMultiTabBar::KMultiTabBarStyle MainWindow::toolViewStyle () const + { + // all sidebars have the same style, so just take Top + return m_sidebars[KMultiTabBar::Top]->tabStyle(); + } + + bool MainWindow::moveToolView (ToolView *widget, KMultiTabBar::KMultiTabBarPosition pos) + { + if (!widget || widget->mainWindow() != this) + return false; + + // try the restore config to figure out real pos + if (m_restoreConfig && m_restoreConfig->hasGroup (m_restoreGroup)) + { + KConfigGroup cg(m_restoreConfig, m_restoreGroup); + pos = (KMultiTabBar::KMultiTabBarPosition) cg.readEntry (QString ("Kate-MDI-ToolView-%1-Position").arg(widget->id), int(pos)); + } + + m_sidebars[pos]->addWidget (widget->icon, widget->text, widget); + + return true; + } + + bool MainWindow::showToolView (ToolView *widget) + { + if (!widget || widget->mainWindow() != this) + return false; + + // skip this if happens during restoring, or we will just see flicker + if (m_restoreConfig && m_restoreConfig->hasGroup (m_restoreGroup)) + return true; + + return widget->sidebar()->showWidget (widget); + } + + bool MainWindow::hideToolView (ToolView *widget) + { + if (!widget || widget->mainWindow() != this) + return false; + + // skip this if happens during restoring, or we will just see flicker + if (m_restoreConfig && m_restoreConfig->hasGroup (m_restoreGroup)) + return true; + + return widget->sidebar()->hideWidget (widget); + } + + void MainWindow::startRestore (KConfigBase *config, const QString &group) + { + // first save this stuff + m_restoreConfig = config; + m_restoreGroup = group; + + if (!m_restoreConfig || !m_restoreConfig->hasGroup (m_restoreGroup)) + { + // if no config around, set already now sane default sizes + // otherwise, set later in ::finishRestore(), since it does not work + // if set already now (see bug #164438) + QList hs = (QList() << 200 << 100 << 200); + QList vs = (QList() << 150 << 100 << 200); + + m_sidebars[0]->setLastSize (hs[0]); + m_sidebars[1]->setLastSize (hs[2]); + m_sidebars[2]->setLastSize (vs[0]); + m_sidebars[3]->setLastSize (vs[2]); + + m_hSplitter->setSizes(hs); + m_vSplitter->setSizes(vs); + return; + } + + // apply size once, to get sizes ready ;) + KConfigGroup cg(m_restoreConfig, m_restoreGroup); + restoreWindowSize (cg); + + setToolViewStyle( (KMultiTabBar::KMultiTabBarStyle)cg.readEntry ("Kate-MDI-Sidebar-Style", (int)toolViewStyle()) ); + // after reading m_sidebarsVisible, update the GUI toggle action + m_sidebarsVisible = cg.readEntry ("Kate-MDI-Sidebar-Visible", true ); + m_guiClient->updateSidebarsVisibleAction(); + } + + void MainWindow::finishRestore () + { + if (!m_restoreConfig) + return; + + if (m_restoreConfig->hasGroup (m_restoreGroup)) + { + // apply all settings, like toolbar pos and more ;) + KConfigGroup cg(m_restoreConfig, m_restoreGroup); + applyMainWindowSettings(cg); + + // reshuffle toolviews only if needed + for ( int i = 0; i < m_toolviews.size(); ++i ) + { + KMultiTabBar::KMultiTabBarPosition newPos = (KMultiTabBar::KMultiTabBarPosition) cg.readEntry (QString ("Kate-MDI-ToolView-%1-Position").arg(m_toolviews[i]->id), int(m_toolviews[i]->sidebar()->position())); + + if (m_toolviews[i]->sidebar()->position() != newPos) + { + moveToolView (m_toolviews[i], newPos); + } + } + + // restore the sidebars + for (unsigned int i = 0; i < 4; ++i) + m_sidebars[i]->restoreSession (cg); + + // restore splitter sizes + QList hs = (QList() << 200 << 100 << 200); + QList vs = (QList() << 150 << 100 << 200); + + // get main splitter sizes ;) + hs = cg.readEntry ("Kate-MDI-H-Splitter", hs); + vs = cg.readEntry ("Kate-MDI-V-Splitter", vs); + + m_sidebars[0]->setLastSize (hs[0]); + m_sidebars[1]->setLastSize (hs[2]); + m_sidebars[2]->setLastSize (vs[0]); + m_sidebars[3]->setLastSize (vs[2]); + + m_hSplitter->setSizes(hs); + m_vSplitter->setSizes(vs); + } + + // clear this stuff, we are done ;) + m_restoreConfig = 0; + m_restoreGroup.clear(); + } + + void MainWindow::saveSession (KConfigGroup& config) + { + saveMainWindowSettings (config); + + // save main splitter sizes ;) + QList hs = m_hSplitter->sizes(); + QList vs = m_vSplitter->sizes(); + + if (hs[0] <= 2 && !m_sidebars[0]->splitterVisible ()) + hs[0] = m_sidebars[0]->lastSize(); + if (hs[2] <= 2 && !m_sidebars[1]->splitterVisible ()) + hs[2] = m_sidebars[1]->lastSize(); + if (vs[0] <= 2 && !m_sidebars[2]->splitterVisible ()) + vs[0] = m_sidebars[2]->lastSize(); + if (vs[2] <= 2 && !m_sidebars[3]->splitterVisible ()) + vs[2] = m_sidebars[3]->lastSize(); + + config.writeEntry ("Kate-MDI-H-Splitter", hs); + config.writeEntry ("Kate-MDI-V-Splitter", vs); + + // save sidebar style + config.writeEntry ("Kate-MDI-Sidebar-Style", (int)toolViewStyle()); + config.writeEntry ("Kate-MDI-Sidebar-Visible", m_sidebarsVisible ); + + // save the sidebars + for (unsigned int i = 0; i < 4; ++i) + m_sidebars[i]->saveSession (config); + } + +//END MAIN WINDOW + +} // namespace KateMDI + +// kate: space-indent on; indent-width 2; diff --git a/kate/src/app/katemdi.h b/kate/src/app/katemdi.h new file mode 100644 index 00000000..d8f32a5f --- /dev/null +++ b/kate/src/app/katemdi.h @@ -0,0 +1,464 @@ +/* This file is part of the KDE libraries + Copyright (C) 2005 Christoph Cullmann + Copyright (C) 2002, 2003 Joseph Wenninger + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef __KATE_MDI_H__ +#define __KATE_MDI_H__ + +#include "kate/plugin.h" + +#include + + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +class KActionMenu; +#include +class KConfigBase; + +namespace Kate { + class PluginConfigPageInterface; +}; + +namespace KateMDI +{ + + class ToolView; + + class ToggleToolViewAction : public KToggleAction + { + Q_OBJECT + + public: + ToggleToolViewAction ( const QString& text, ToolView *tv, QObject *parent ); + + virtual ~ToggleToolViewAction(); + + protected Q_SLOTS: + void slotToggled(bool); + void toolVisibleChanged(bool); + + private: + ToolView *m_tv; + }; + + class GUIClient : public QObject, public KXMLGUIClient + { + Q_OBJECT + + public: + GUIClient ( class MainWindow *mw ); + virtual ~GUIClient(); + + void registerToolView (ToolView *tv); + void unregisterToolView (ToolView *tv); + void updateSidebarsVisibleAction(); + + private Q_SLOTS: + void clientAdded( KXMLGUIClient *client ); + void updateActions(); + + private: + MainWindow *m_mw; + KToggleAction *m_showSidebarsAction; + QList m_toolViewActions; + QMap m_toolToAction; + KActionMenu *m_toolMenu; + }; + + class ToolView : public KVBox + { + Q_OBJECT + + friend class Sidebar; + friend class MainWindow; + friend class GUIClient; + friend class ToggleToolViewAction; + + protected: + /** + * ToolView + * Objects of this clas represent a toolview in the mainwindow + * you should only add one widget as child to this toolview, it will + * be automatically set to be the focus proxy of the toolview + * @param mainwin main window for this toolview + * @param sidebar sidebar of this toolview + * @param parent parent widget, e.g. the splitter of one of the sidebars + */ + ToolView (class MainWindow *mainwin, class Sidebar *sidebar, QWidget *parent); + + public: + /** + * destuct me, this is allowed for all, will care itself that the toolview is removed + * from the mainwindow and sidebar + */ + virtual ~ToolView (); + + Q_SIGNALS: + /** + * toolview hidden or shown + * @param visible is this toolview made visible? + */ + void toolVisibleChanged (bool visible); + + /** + * some internal methodes needed by the main window and the sidebars + */ + protected: + MainWindow *mainWindow () + { + return m_mainWin; + } + + Sidebar *sidebar () + { + return m_sidebar; + } + + void setToolVisible (bool vis); + + public: + bool toolVisible () const; + + protected: + void childEvent ( QChildEvent *ev ); + + private: + MainWindow *m_mainWin; + Sidebar *m_sidebar; + + ///plugin this view belongs to, may be 0 + QPointer plugin; + + /** + * unique id + */ + QString id; + + /** + * is visible in sidebar + */ + bool m_toolVisible; + + /** + * is this view persistent? + */ + bool persistent; + + QPixmap icon; + QString text; + }; + + class Sidebar : public KMultiTabBar + { + Q_OBJECT + + public: + Sidebar (KMultiTabBar::KMultiTabBarPosition pos, class MainWindow *mainwin, QWidget *parent); + virtual ~Sidebar (); + + void setSplitter (QSplitter *sp); + + public: + ToolView *addWidget (const QPixmap &icon, const QString &text, ToolView *widget); + bool removeWidget (ToolView *widget); + + bool showWidget (ToolView *widget); + bool hideWidget (ToolView *widget); + + void setLastSize (int s) + { + m_lastSize = s; + } + int lastSize () const + { + return m_lastSize; + } + void updateLastSize (); + + bool splitterVisible () const + { + return m_ownSplit->isVisible(); + } + + void restoreSession (); + + /** + * restore the current session config from given object, use current group + * @param config config object to use + */ + void restoreSession (KConfigGroup& config); + + /** + * save the current session config to given object, use current group + * @param config config object to use + */ + void saveSession (KConfigGroup& config); + + public Q_SLOTS: + // reimplemented, to block a show() call if all sidebars are forced hidden + virtual void setVisible(bool visible); + private Q_SLOTS: + void tabClicked(int); + + protected: + bool eventFilter(QObject *obj, QEvent *ev); + + private Q_SLOTS: + void buttonPopupActivate (QAction *); + + private: + MainWindow *m_mainWin; + + KMultiTabBar::KMultiTabBarPosition m_pos; + QSplitter *m_splitter; + KMultiTabBar *m_tabBar; + QSplitter *m_ownSplit; + + QMap m_idToWidget; + QMap m_widgetToId; + QMap m_widgetToSize; + + /** + * list of all toolviews around in this sidebar + */ + QList m_toolviews; + + int m_lastSize; + + QSize m_preHideSize; + + int m_popupButton; + + Q_SIGNALS: + void sigShowPluginConfigPage(Kate::PluginConfigPageInterface *configpageinterface,uint id); + + }; + + class MainWindow : public KParts::MainWindow + { + Q_OBJECT + + friend class ToolView; + + // + // Constructor area + // + public: + /** + * Constructor + */ + MainWindow (QWidget* parentWidget = 0); + + /** + * Destructor + */ + virtual ~MainWindow (); + + // + // public interfaces + // + public: + /** + * central widget ;) + * use this as parent for your content + * this widget will get focus if a toolview is hidden + * @return central widget + */ + QWidget *centralWidget () const; + + /** + * add a given widget to the given sidebar if possible, name is very important + * @param identifier unique identifier for this toolview + * @param pos position for the toolview, if we are in session restore, this is only a preference + * @param icon icon to use for the toolview + * @param text text to use in addition to icon + * @return created toolview on success or 0 + */ + ToolView *createToolView (Kate::Plugin* plugin, const QString &identifier, KMultiTabBar::KMultiTabBarPosition pos, const QPixmap &icon, const QString &text); + + /** + * give you handle to toolview for the given name, 0 if no toolview around + * @param identifier toolview name + * @return toolview if existing, else 0 + */ + ToolView *toolView (const QString &identifier) const; + + /** + * set the toolview's tabbar style. + * @param style the tabbar style. + */ + void setToolViewStyle (KMultiTabBar::KMultiTabBarStyle style); + + /** + * get the toolview's tabbar style. Call this before @p startRestore(), + * otherwise you overwrite the usersettings. + * @return toolview's tabbar style + */ + KMultiTabBar::KMultiTabBarStyle toolViewStyle () const; + + /** + * get the sidebars' visibility. + * @return false, if the sidebars' visibility is forced hidden, otherwise true + */ + bool sidebarsVisible() const; + + public Q_SLOTS: + /** + * set the sidebars' visibility to @p visible. If false, the sidebars + * are @e always hidden. Usually you do not have to call this because + * the user can set this in the menu. + * @param visible sidebars visibility + */ + void setSidebarsVisible( bool visible ); + + protected: + /** + * called by toolview destructor + * @param widget toolview which is destroyed + */ + void toolViewDeleted (ToolView *widget); + + /** + * modifiers for existing toolviews + */ + public: + /** + * move a toolview around + * @param widget toolview to move + * @param pos position to move too, during session restore, only preference + * @return success + */ + bool moveToolView (ToolView *widget, KMultiTabBar::KMultiTabBarPosition pos); + + /** + * show given toolview, discarded while session restore + * @param widget toolview to show + * @return success + */ + bool showToolView (ToolView *widget); + + /** + * hide given toolview, discarded while session restore + * @param widget toolview to hide + * @return success + */ + bool hideToolView (ToolView *widget); + + /** + * session saving and restore stuff + */ + public: + /** + * start the restore + * @param config config object to use + * @param group config group to use + */ + void startRestore (KConfigBase *config, const QString &group); + + /** + * finish the restore + */ + void finishRestore (); + + /** + * save the current session config to given object and group + * @param config config object to use + * @param group config group to use + */ + void saveSession (KConfigGroup &group); + + /** + * internal data ;) + */ + private: + /** + * map identifiers to widgets + */ + QMap m_idToWidget; + + /** + * list of all toolviews around + */ + QList m_toolviews; + + + /** + * widget, which is the central part of the + * main window ;) + */ + QWidget *m_centralWidget; + + /** + * horizontal splitter + */ + QSplitter *m_hSplitter; + + /** + * vertical splitter + */ + QSplitter *m_vSplitter; + + /** + * sidebars for the four sides + */ + Sidebar *m_sidebars[4]; + + /** + * sidebars state. + */ + bool m_sidebarsVisible; + + /** + * config object for session restore, only valid between + * start and finish restore calls + */ + KConfigBase *m_restoreConfig; + + /** + * restore group + */ + QString m_restoreGroup; + + /** + * out guiclient + */ + GUIClient *m_guiClient; + + Q_SIGNALS: + void sigShowPluginConfigPage(Kate::PluginConfigPageInterface *configpageinterface,uint id); + + }; + +} + +#endif + +// kate: space-indent on; indent-width 2; diff --git a/kate/src/app/katemwmodonhddialog.cpp b/kate/src/app/katemwmodonhddialog.cpp new file mode 100644 index 00000000..4654e6ae --- /dev/null +++ b/kate/src/app/katemwmodonhddialog.cpp @@ -0,0 +1,345 @@ +/* + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + + --- + Copyright (C) 2004, Anders Lund +*/ + +#include "katemwmodonhddialog.h" +#include "moc_katemwmodonhddialog.cpp" + +#include "katedocmanager.h" +#include "katemainwindow.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +class KateDocItem : public QTreeWidgetItem +{ + public: + KateDocItem( KTextEditor::Document *doc, const QString &status, QTreeWidget *tw ) + : QTreeWidgetItem( tw ), + document( doc ) + { + setText( 0, doc->url().pathOrUrl() ); + setText( 1, status ); + if ( ! doc->isModified() ) + setCheckState( 0, Qt::Checked ); + else + setCheckState( 0, Qt::Unchecked ); + } + ~KateDocItem() + {} + + KTextEditor::Document *document; +}; + + +KateMwModOnHdDialog::KateMwModOnHdDialog( DocVector docs, QWidget *parent, const char *name ) + : KDialog( parent ), + m_proc( 0 ), + m_diffFile( 0 ) +{ + setCaption( i18n("Documents Modified on Disk") ); + setButtons( User1 | User2 | User3 ); + setButtonGuiItem( User1, KGuiItem (i18n("&Ignore Changes"), "dialog-warning") ); + setButtonGuiItem( User2, KStandardGuiItem::overwrite() ); + setButtonGuiItem( User3, KGuiItem (i18n("&Reload"), "view-refresh") ); + + setObjectName( name ); + setModal( true ); + setDefaultButton( KDialog::User3 ); + + setButtonToolTip( User1, i18n( + "Remove modified flag from selected documents") ); + setButtonToolTip( User2, i18n( + "Overwrite selected documents, discarding disk changes") ); + setButtonToolTip( User3, i18n( + "Reload selected documents from disk") ); + + KVBox *w = new KVBox( this ); + setMainWidget( w ); + w->setSpacing( KDialog::spacingHint() ); + + KHBox *lo1 = new KHBox( w ); + + // dialog text + QLabel *icon = new QLabel( lo1 ); + icon->setPixmap( DesktopIcon("dialog-warning") ); + + QLabel *t = new QLabel( i18n( + "The documents listed below have changed on disk.

Select one " + "or more at once, and press an action button until the list is empty.

"), lo1 ); + lo1->setStretchFactor( t, 1000 ); + + // document list + twDocuments = new QTreeWidget( w ); + QStringList header; + header << i18n("Filename") << i18n("Status on Disk"); + twDocuments->setHeaderLabels(header); + twDocuments->setSelectionMode( QAbstractItemView::SingleSelection ); + twDocuments->setRootIsDecorated( false ); + + + m_stateTexts << "" << i18n("Modified") << i18n("Created") << i18n("Deleted"); + for ( int i = 0; i < docs.size(); i++ ) + { + new KateDocItem( docs[i], m_stateTexts[ (uint)KateDocManager::self()->documentInfo( docs[i] )->modifiedOnDiscReason ], twDocuments ); + } + twDocuments->header()->setStretchLastSection(false); + twDocuments->header()->setResizeMode(0, QHeaderView::Stretch); + twDocuments->header()->setResizeMode(1, QHeaderView::ResizeToContents); + + connect( twDocuments, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), this, SLOT(slotSelectionChanged(QTreeWidgetItem*,QTreeWidgetItem*)) ); + + // diff button + KHBox *lo2 = new KHBox ( w ); + QWidget *d = new QWidget (lo2); + lo2->setStretchFactor (d, 2); + btnDiff = new KPushButton( KGuiItem (i18n("&View Difference"), "document-preview"), lo2 ); + + btnDiff->setWhatsThis(i18n( + "Calculates the difference between the editor contents and the disk " + "file for the selected document, and shows the difference with the " + "default application. Requires diff(1).") ); + connect( btnDiff, SIGNAL(clicked()), this, SLOT(slotDiff()) ); + connect( this, SIGNAL(user1Clicked()), this, SLOT(slotUser1()) ); + connect( this, SIGNAL(user2Clicked()), this, SLOT(slotUser2()) ); + connect( this, SIGNAL(user3Clicked()), this, SLOT(slotUser3()) ); + + slotSelectionChanged(NULL, NULL); +} + +KateMwModOnHdDialog::~KateMwModOnHdDialog() +{ + KateMainWindow::unsetModifiedOnDiscDialogIfIf(this); + delete m_proc; + m_proc = 0; + if (m_diffFile) { + m_diffFile->setAutoRemove(true); + delete m_diffFile; + m_diffFile = 0; + } +} + +void KateMwModOnHdDialog::slotUser1() +{ + handleSelected( Ignore ); +} + +void KateMwModOnHdDialog::slotUser2() +{ + handleSelected( Overwrite ); +} + +void KateMwModOnHdDialog::slotUser3() +{ + handleSelected( Reload ); +} + +void KateMwModOnHdDialog::handleSelected( int action ) +{ + // collect all items we can remove + QList itemsToDelete; + for ( QTreeWidgetItemIterator it ( twDocuments ); *it; ++it ) + { + KateDocItem *item = (KateDocItem *) * it; + if ( item->checkState(0) == Qt::Checked ) + { + KTextEditor::ModificationInterface::ModifiedOnDiskReason reason = KateDocManager::self()->documentInfo( item->document )->modifiedOnDiscReason; + bool success = true; + + if (KTextEditor::ModificationInterface *iface = qobject_cast(item->document)) + iface->setModifiedOnDisk( KTextEditor::ModificationInterface::OnDiskUnmodified ); + + switch ( action ) + { + case Overwrite: + success = item->document->save(); + if ( ! success ) + { + KMessageBox::sorry( this, + i18n("Could not save the document \n'%1'", + item->document->url().pathOrUrl() ) ); + } + break; + + case Reload: + item->document->documentReload(); + break; + + default: + break; + } + + if ( success ) + itemsToDelete.append( item ); + else + { + if (KTextEditor::ModificationInterface *iface = qobject_cast(item->document)) + iface->setModifiedOnDisk( reason ); + } + } + } + + // remove the marked items + for (int i = 0; i < itemsToDelete.count(); ++i) + delete itemsToDelete[i]; + +// any documents left unhandled? + if ( ! twDocuments->topLevelItemCount() ) + done( Ok ); +} + +void KateMwModOnHdDialog::slotSelectionChanged(QTreeWidgetItem *current, QTreeWidgetItem *) +{ + // set the diff button enabled + btnDiff->setEnabled( current && + KateDocManager::self()->documentInfo( (static_cast(current))->document )->modifiedOnDiscReason != KTextEditor::ModificationInterface::OnDiskDeleted ); +} + +// ### the code below is slightly modified from kdelibs/kate/part/katedialogs, +// class KateModOnHdPrompt. +void KateMwModOnHdDialog::slotDiff() +{ + if ( !btnDiff->isEnabled()) // diff button already pressed, proc not finished yet + return; + + if ( ! twDocuments->currentItem() ) + return; + + KTextEditor::Document *doc = (static_cast(twDocuments->currentItem()))->document; + + // don't try to diff a deleted file + if ( KateDocManager::self()->documentInfo( doc )->modifiedOnDiscReason == KTextEditor::ModificationInterface::OnDiskDeleted ) + return; + + if (m_diffFile) + return; + + m_diffFile = new KTemporaryFile(); + m_diffFile->open(); + + // Start a QProcess that creates a diff + m_proc = new QProcess( this ); + m_proc->setProcessChannelMode( QProcess::MergedChannels ); + connect( m_proc, SIGNAL(readyRead()), this, SLOT(slotDataAvailable()) ); + connect( m_proc, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(slotPDone()) ); + + setCursor( Qt::WaitCursor ); + btnDiff->setEnabled(false); + + QStringList procargs; + procargs << "-ub" << "-" << doc->url().toLocalFile(); + m_proc->start("diff", procargs); + + QTextStream ts(m_proc); + int lastln = doc->lines() - 1; + for ( int l = 0; l < lastln; ++l ) { + ts << doc->line( l ) << '\n'; + } + ts << doc->line(lastln); + ts.flush(); + m_proc->closeWriteChannel(); +} + +void KateMwModOnHdDialog::slotDataAvailable() +{ + m_diffFile->write(m_proc->readAll()); +} + +void KateMwModOnHdDialog::slotPDone() +{ + setCursor( Qt::ArrowCursor ); + slotSelectionChanged(twDocuments->currentItem(), 0); + + const QProcess::ExitStatus es = m_proc->exitStatus(); + delete m_proc; + m_proc = 0; + + if ( es != QProcess::NormalExit ) + { + KMessageBox::sorry( this, + i18n("The diff command failed. Please make sure that " + "diff(1) is installed and in your PATH."), + i18n("Error Creating Diff") ); + delete m_diffFile; + m_diffFile = 0; + return; + } + + if ( m_diffFile->size() == 0 ) + { + KMessageBox::information( this, + i18n("Ignoring amount of white space changed, the files are identical."), + i18n("Diff Output") ); + delete m_diffFile; + m_diffFile = 0; + return; + } + + m_diffFile->setAutoRemove(false); + KUrl url = KUrl::fromPath(m_diffFile->fileName()); + delete m_diffFile; + m_diffFile = 0; + + // KRun::runUrl should delete the file, once the client exits + KRun::runUrl( url, "text/x-patch", this, true ); +} + +void KateMwModOnHdDialog::addDocument(KTextEditor::Document *doc) +{ + for ( QTreeWidgetItemIterator it ( twDocuments ); *it; ++it ) + { + KateDocItem *item = (KateDocItem *) * it; + if (item->document==doc) + { + delete item; + break; + } + } + new KateDocItem( doc, m_stateTexts[ (uint)KateDocManager::self()->documentInfo( doc )->modifiedOnDiscReason ], twDocuments ); +} + +void KateMwModOnHdDialog::keyPressEvent( QKeyEvent *event ) +{ + if ( event->modifiers() == 0 ) + { + if ( event->key() == Qt::Key_Escape ) + { + event->accept(); + return; + } + } + KDialog::keyPressEvent(event); +} + +void KateMwModOnHdDialog::closeEvent( QCloseEvent *e ) +{ + if ( ! twDocuments->topLevelItemCount() ) + KDialog::closeEvent(e); + else e->ignore(); +} +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/src/app/katemwmodonhddialog.h b/kate/src/app/katemwmodonhddialog.h new file mode 100644 index 00000000..8bd733a3 --- /dev/null +++ b/kate/src/app/katemwmodonhddialog.h @@ -0,0 +1,75 @@ +/* + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + + --- + Copyright (C) 2004, Anders Lund +*/ + +#ifndef _KATE_MW_MODONHD_DIALOG_H_ +#define _KATE_MW_MODONHD_DIALOG_H_ + +#include + +#include + +#include + +#include +#include + +typedef QVector DocVector; +#include +class KTemporaryFile; + +/** + * A dialog for handling multiple documents modified on disk + * from within KateMainWindow + */ +class KateMwModOnHdDialog : public KDialog +{ + Q_OBJECT + public: + explicit KateMwModOnHdDialog( DocVector docs, QWidget *parent = 0, const char *name = 0 ); + ~KateMwModOnHdDialog(); + void addDocument(KTextEditor::Document *doc); + + protected Q_SLOTS: + void slotUser1(); + void slotUser2(); + void slotUser3(); + + private Q_SLOTS: + void slotDiff(); + void slotSelectionChanged(QTreeWidgetItem *current, QTreeWidgetItem *); + void slotDataAvailable(); + void slotPDone(); + + private: + enum Action { Ignore, Overwrite, Reload }; + void handleSelected( int action ); + class QTreeWidget *twDocuments; + class QPushButton *btnDiff; + QProcess *m_proc; + KTemporaryFile *m_diffFile; + QStringList m_stateTexts; + + protected: + virtual void closeEvent( QCloseEvent *e ); + virtual void keyPressEvent( QKeyEvent* ); + +}; + +#endif // _KATE_MW_MODONHD_DIALOG_H_ +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/src/app/katepluginmanager.cpp b/kate/src/app/katepluginmanager.cpp new file mode 100644 index 00000000..0f1cd151 --- /dev/null +++ b/kate/src/app/katepluginmanager.cpp @@ -0,0 +1,320 @@ +/* This file is part of the KDE project + Copyright (C) 2001 Christoph Cullmann + Copyright (C) 2001 Joseph Wenninger + Copyright (C) 2001 Anders Lund + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "katepluginmanager.h" +#include "moc_katepluginmanager.cpp" + +#include "kateapp.h" +#include "katemainwindow.h" + +#include + +#include + +#include +#include +#include +#include + +QString KatePluginInfo::saveName() const +{ + return service->library(); +} + +KatePluginManager::KatePluginManager(QObject *parent) : QObject (parent) +{ + m_pluginManager = new Kate::PluginManager (this); + setupPluginList (); +} + +KatePluginManager::~KatePluginManager() +{ + // than unload the plugins + unloadAllPlugins (); +} + +KatePluginManager *KatePluginManager::self() +{ + return KateApp::self()->pluginManager (); +} + +void KatePluginManager::setupPluginList () +{ + KService::List traderList = KServiceTypeTrader::self()->query("Kate/Plugin"); + + KatePluginList alwaysLoad; + KatePluginList others; + foreach(const KService::Ptr &ptr, traderList) + { + double pVersion = ptr->property("X-Kate-Version").toDouble(); + + // don't use plugins out of 3.x release series + if ((pVersion >= 2.8) && (pVersion <= KateApp::kateVersion(false).toDouble())) + { + KatePluginInfo info; + info.service = ptr; + info.alwaysLoad=info.service->property("X-Kate-LoadAlways").toBool(); + info.load = false; + info.plugin = 0L; + + if (info.alwaysLoad) + alwaysLoad.push_back (info); + else + others.push_back (info); + } + } + + /** + * prefer always load plugins in handling + */ + m_pluginList = alwaysLoad; + m_pluginList << others; + + /** + * construct fast lookup map + */ + m_name2Plugin.clear (); + for (int i = 0; i < m_pluginList.size(); ++i) + m_name2Plugin[m_pluginList[i].service->library()] = &(m_pluginList[i]); +} + +void KatePluginManager::loadConfig (KConfig* config) +{ + // first: unload the plugins + unloadAllPlugins (); + + /** + * ask config object + */ + if (config) { + KConfigGroup cg = KConfigGroup(config, "Kate Plugins"); + + // disable all plugin if no config..., beside if marked with load + for (int i = 0; i < m_pluginList.size(); ++i) + m_pluginList[i].load = cg.readEntry (m_pluginList[i].service->library(), m_pluginList[i].service->property("X-Kate-Load").toBool()); + } + + /** + * some plugins should be always loaded, like the holy filetree + */ + for (int i = 0; i < m_pluginList.size(); ++i) + if (m_pluginList[i].service->property("X-Kate-LoadAlways").toBool()) + m_pluginList[i].load = true; + + for (KatePluginList::iterator it = m_pluginList.begin();it != m_pluginList.end(); ++it) + { + if (it->load) + { + loadPlugin(&(*it)); + + // restore config + if (it->plugin) + it->plugin->readSessionConfig(config, QString("Plugin:%1:").arg(it->saveName())); + } + } +} + +void KatePluginManager::writeConfig(KConfig* config) +{ + Q_ASSERT( config ); + + KConfigGroup cg = KConfigGroup( config, "Kate Plugins" ); + foreach(const KatePluginInfo &plugin, m_pluginList) + { + QString saveName = plugin.saveName(); + + cg.writeEntry (saveName, plugin.load); + + if (plugin.plugin) + plugin.plugin->writeSessionConfig(config, QString("Plugin:%1:").arg(saveName)); + } +} + +void KatePluginManager::unloadAllPlugins () +{ + for (KatePluginList::iterator it = m_pluginList.begin();it != m_pluginList.end(); ++it) + { + if (it->plugin) + unloadPlugin(&(*it)); + } +} + +void KatePluginManager::enableAllPluginsGUI (KateMainWindow *win, KConfigBase *config) +{ + for (KatePluginList::iterator it = m_pluginList.begin();it != m_pluginList.end(); ++it) + { + if (it->plugin) + enablePluginGUI(&(*it), win, config); + } +} + +void KatePluginManager::disableAllPluginsGUI (KateMainWindow *win) +{ + for (KatePluginList::iterator it = m_pluginList.begin();it != m_pluginList.end(); ++it) + { + if (it->plugin) + disablePluginGUI(&(*it), win); + } +} + +void KatePluginManager::loadPlugin (KatePluginInfo *item) +{ + QString pluginName = item->service->library(); + item->load = (item->plugin = item->service->createInstance(Kate::application(), QVariantList() << pluginName)); + if (item->plugin) + emit m_pluginManager->pluginLoaded (pluginName, item->plugin); +} + +void KatePluginManager::unloadPlugin (KatePluginInfo *item) +{ + disablePluginGUI (item); + delete item->plugin; + item->plugin = 0L; + item->load=false; + emit m_pluginManager->pluginUnloaded (item->service->library(), item->plugin); +} + +void KatePluginManager::enablePluginGUI (KatePluginInfo *item, KateMainWindow *win, KConfigBase *config) +{ + // plugin around at all? + if (!item->plugin) + return; + + // lookup if there is already a view for it.. + Kate::PluginView *createdView = 0; + if (!win->pluginViews().contains(item->plugin)) + { + // create the view + try to correctly load shortcuts, if it's a GUI Client + createdView = item->plugin->createView(win->mainWindow()); + if (createdView) + win->pluginViews().insert (item->plugin, createdView); + } + + // load session config if needed + if (config && win->pluginViews().contains(item->plugin)) + { + win->pluginViews().value(item->plugin)->readSessionConfig(config, QString("Plugin:%1:MainWindow:0").arg(item->saveName())); + } + + if (createdView) + emit win->mainWindow()->pluginViewCreated (item->service->library(), createdView); +} + +void KatePluginManager::enablePluginGUI (KatePluginInfo *item) +{ + // plugin around at all? + if (!item->plugin) + return; + + // enable the gui for all mainwindows... + for (int i = 0; i < KateApp::self()->mainWindows(); i++) + enablePluginGUI (item, KateApp::self()->mainWindow(i), 0); +} + +void KatePluginManager::disablePluginGUI (KatePluginInfo *item, KateMainWindow *win) +{ + // plugin around at all? + if (!item->plugin) + return; + + // lookup if there is a view for it.. + if (!win->pluginViews().contains(item->plugin)) + return; + + // really delete the view of this plugin + Kate::PluginView *deletedView = win->pluginViews().value(item->plugin); + delete deletedView; + win->pluginViews().remove (item->plugin); + emit win->mainWindow()->pluginViewDeleted (item->service->library(), deletedView); +} + +void KatePluginManager::disablePluginGUI (KatePluginInfo *item) +{ + // plugin around at all? + if (!item->plugin) + return; + + // disable the gui for all mainwindows... + for (int i = 0; i < KateApp::self()->mainWindows(); i++) + disablePluginGUI (item, KateApp::self()->mainWindow(i)); +} + +Kate::Plugin *KatePluginManager::plugin (const QString &name) +{ + /** + * name known? + */ + if (!m_name2Plugin.contains(name)) + return 0; + + /** + * real plugin instance, if any ;) + */ + return m_name2Plugin.value(name)->plugin; +} + +bool KatePluginManager::pluginAvailable (const QString &name) +{ + return m_name2Plugin.contains (name); +} + +class Kate::Plugin *KatePluginManager::loadPlugin (const QString &name, bool permanent) +{ + /** + * name known? + */ + if (!m_name2Plugin.contains(name)) + return 0; + + /** + * load, bail out on error + */ + loadPlugin (m_name2Plugin.value(name)); + if (!m_name2Plugin.value(name)->plugin) + return 0; + + /** + * perhaps patch not load again back to "ok, load it once again on next loadConfig" + */ + m_name2Plugin.value(name)->load = permanent; + return m_name2Plugin.value(name)->plugin; +} + +void KatePluginManager::unloadPlugin (const QString &name, bool permanent) +{ + /** + * name known? + */ + if (!m_name2Plugin.contains(name)) + return; + + /** + * unload + */ + unloadPlugin (m_name2Plugin.value(name)); + + /** + * perhaps patch load again back to "ok, load it once again on next loadConfig" + */ + m_name2Plugin.value(name)->load = !permanent; +} + +// kate: space-indent on; indent-width 2; replace-tabs on; + diff --git a/kate/src/app/katepluginmanager.h b/kate/src/app/katepluginmanager.h new file mode 100644 index 00000000..798ae991 --- /dev/null +++ b/kate/src/app/katepluginmanager.h @@ -0,0 +1,111 @@ +/* This file is part of the KDE project + Copyright (C) 2001 Christoph Cullmann + Copyright (C) 2001 Joseph Wenninger + Copyright (C) 2001 Anders Lund + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef __KATE_PLUGINMANAGER_H__ +#define __KATE_PLUGINMANAGER_H__ + +#include "katemain.h" + +#include +#include + +#include +#include + +#include +#include + +class KateMainWindow; + +class KatePluginInfo +{ + public: + bool load; + bool alwaysLoad; + KService::Ptr service; + Kate::Plugin *plugin; + QString saveName() const; +}; + +typedef QList KatePluginList; + +class KatePluginManager : public QObject +{ + Q_OBJECT + + public: + KatePluginManager(QObject *parent); + ~KatePluginManager(); + + static KatePluginManager *self(); + + Kate::PluginManager *pluginManager () const + { + return m_pluginManager; + } + + void unloadAllPlugins (); + + void enableAllPluginsGUI (KateMainWindow *win, KConfigBase *config = 0); + void disableAllPluginsGUI (KateMainWindow *win); + + void loadConfig (KConfig*); + void writeConfig (KConfig*); + + void loadPlugin (KatePluginInfo *item); + void unloadPlugin (KatePluginInfo *item); + + void enablePluginGUI (KatePluginInfo *item, KateMainWindow *win, KConfigBase *config = 0); + void enablePluginGUI (KatePluginInfo *item); + + void disablePluginGUI (KatePluginInfo *item, KateMainWindow *win); + void disablePluginGUI (KatePluginInfo *item); + + inline KatePluginList & pluginList () + { + return m_pluginList; + } + + Kate::Plugin *plugin (const QString &name); + bool pluginAvailable (const QString &name); + + Kate::Plugin *loadPlugin (const QString &name, bool permanent = true); + void unloadPlugin (const QString &name, bool permanent = true); + + private: + Kate::PluginManager *m_pluginManager; + + void setupPluginList (); + + /** + * all known plugins + */ + KatePluginList m_pluginList; + + /** + * fast access map from name => plugin info + * uses the info stored in the plugin list + */ + QMap m_name2Plugin; +}; + +#endif +// kate: space-indent on; indent-width 2; replace-tabs on; + diff --git a/kate/src/app/katequickopen.cpp b/kate/src/app/katequickopen.cpp new file mode 100644 index 00000000..4cc40c6e --- /dev/null +++ b/kate/src/app/katequickopen.cpp @@ -0,0 +1,297 @@ +/* + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + --- + Copyright (C) 2007,2009 Joseph Wenninger +*/ + +#include "katequickopen.h" +#include "moc_katequickopen.cpp" +#include "katemainwindow.h" +#include "kateviewmanager.h" + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +Q_DECLARE_METATYPE(QPointer) + +const int DocumentRole=Qt::UserRole+1; +const int UrlRole=Qt::UserRole+2; +const int SortFilterRole=Qt::UserRole+3; + +KateQuickOpen::KateQuickOpen(QWidget *parent, KateMainWindow *mainWindow) + : QWidget(parent) + , m_mainWindow (mainWindow) +{ + QVBoxLayout *layout = new QVBoxLayout(); + layout->setSpacing(0); + layout->setMargin(0); + setLayout (layout); + + m_inputLine = new KLineEdit(); + setFocusProxy (m_inputLine); + m_inputLine->setClickMessage (i18n ("Quick Open Search")); + + layout->addWidget(m_inputLine); + + m_listView = new QTreeView(); + layout->addWidget(m_listView, 1); + m_listView->setTextElideMode(Qt::ElideLeft); + + m_base_model = new QStandardItemModel(0, 2, this); + + m_model = new QSortFilterProxyModel(this); + m_model->setFilterRole(SortFilterRole); + m_model->setSortRole(SortFilterRole); + m_model->setFilterCaseSensitivity(Qt::CaseInsensitive); + m_model->setSortCaseSensitivity(Qt::CaseInsensitive); + + connect(m_inputLine, SIGNAL(textChanged(QString)), m_model, SLOT(setFilterFixedString(QString))); + connect(m_inputLine, SIGNAL(returnPressed()), this, SLOT(slotReturnPressed())); + connect(m_model, SIGNAL(rowsInserted(QModelIndex, int, int)), this, SLOT(reselectFirst())); + connect(m_model, SIGNAL(rowsRemoved(QModelIndex, int, int)), this, SLOT(reselectFirst())); + + connect(m_listView, SIGNAL(activated(QModelIndex)), this, SLOT(slotReturnPressed())); + + m_listView->setModel(m_model); + m_model->setSourceModel(m_base_model); + + m_inputLine->installEventFilter(this); + m_listView->installEventFilter(this); + m_listView->setHeaderHidden(true); + m_listView->setRootIsDecorated(false); +} + +bool KateQuickOpen::eventFilter(QObject *obj, QEvent *event) +{ + if (event->type() == QEvent::KeyPress) { + QKeyEvent *keyEvent=static_cast(event); + if (obj == m_inputLine) { + const bool forward2list = (keyEvent->key() == Qt::Key_Up) + || (keyEvent->key() == Qt::Key_Down) + || (keyEvent->key() == Qt::Key_PageUp) + || (keyEvent->key() == Qt::Key_PageDown); + if (forward2list) { + QCoreApplication::sendEvent(m_listView, event); + return true; + } + + if (keyEvent->key() == Qt::Key_Escape) { + m_mainWindow->slotWindowActivated (); + m_inputLine->clear (); + return true; + } + } else { + const bool forward2input = (keyEvent->key() != Qt::Key_Up) + && (keyEvent->key() != Qt::Key_Down) + && (keyEvent->key() != Qt::Key_PageUp) + && (keyEvent->key() != Qt::Key_PageDown) + && (keyEvent->key() != Qt::Key_Tab) + && (keyEvent->key() != Qt::Key_Backtab); + if (forward2input) { + QCoreApplication::sendEvent(m_inputLine, event); + return true; + } + } + } + return QWidget::eventFilter(obj, event); +} + +void KateQuickOpen::reselectFirst() { + QModelIndex index = m_model->index(0, 0); + m_listView->setCurrentIndex(index); +} + +void KateQuickOpen::update () +{ + /** + * new base mode creation + */ + QStandardItemModel *base_model = new QStandardItemModel(0, 2, this); + + /** + * remember local file names to avoid dupes with project files + */ + QSet alreadySeenFiles; + QSet alreadySeenDocs; + + /** + * get views in lru order + */ + QMap sortedViews; + QHashIterator i(m_mainWindow->viewManager()->lruViews()); + while (i.hasNext()) { + i.next (); + sortedViews[i.value()] = i.key(); + } + + /** + * now insert them in order + */ + QModelIndex idxToSelect; + int linecount = 0; + QMapIterator i2(sortedViews); + while (i2.hasNext()) { + i2.next(); + + KTextEditor::Document *doc = i2.value()->document(); + + if (alreadySeenDocs.contains(doc)) + continue; + + alreadySeenDocs.insert (doc); + + //QStandardItem *item=new QStandardItem(i18n("%1: %2",doc->documentName(),doc->url().pathOrUrl())); + QStandardItem *itemName = new QStandardItem(doc->documentName()); + + itemName->setData(qVariantFromValue(QPointer (doc)), DocumentRole); + itemName->setData(QString("%1: %2").arg(doc->documentName()).arg(doc->url().pathOrUrl()), SortFilterRole); + itemName->setEditable(false); + QFont font = itemName->font(); + font.setBold(true); + itemName->setFont(font); + + QStandardItem *itemUrl = new QStandardItem(doc->url().pathOrUrl()); + itemUrl->setEditable(false); + base_model->setItem(linecount, 0, itemName); + base_model->setItem(linecount, 1, itemUrl); + linecount++; + + if (!doc->url().isEmpty() && doc->url().isLocalFile()) + alreadySeenFiles.insert (doc->url().toLocalFile()); + + // select second document, that is the last used (beside the active one) + if (linecount == 2) + idxToSelect = itemName->index(); + } + + /** + * get all open documents + */ + QList docs = Kate::application()->documentManager()->documents(); + foreach(KTextEditor::Document *doc, docs) { + /** + * skip docs already open + */ + if (alreadySeenDocs.contains (doc)) + continue; + + //QStandardItem *item=new QStandardItem(i18n("%1: %2",doc->documentName(),doc->url().pathOrUrl())); + QStandardItem *itemName = new QStandardItem(doc->documentName()); + + itemName->setData(qVariantFromValue(QPointer (doc)), DocumentRole); + itemName->setData(QString("%1: %2").arg(doc->documentName()).arg(doc->url().pathOrUrl()), SortFilterRole); + itemName->setEditable(false); + QFont font = itemName->font(); + font.setBold(true); + itemName->setFont(font); + + QStandardItem *itemUrl = new QStandardItem(doc->url().pathOrUrl()); + itemUrl->setEditable(false); + base_model->setItem(linecount, 0, itemName); + base_model->setItem(linecount, 1, itemUrl); + linecount++; + + if (!doc->url().isEmpty() && doc->url().isLocalFile()) + alreadySeenFiles.insert (doc->url().toLocalFile()); + } + + /** + * insert all project files, if any project around + */ + if (Kate::PluginView *projectView = m_mainWindow->mainWindow()->pluginView ("kateprojectplugin")) { + QStringList projectFiles = projectView->property ("projectFiles").toStringList(); + foreach (const QString &file, projectFiles) { + /** + * skip files already open + */ + if (alreadySeenFiles.contains (file)) + continue; + + QFileInfo fi (file); + QStandardItem *itemName = new QStandardItem(fi.fileName()); + + itemName->setData(qVariantFromValue(KUrl::fromPath (file)), UrlRole); + itemName->setData(QString("%1: %2").arg(fi.fileName()).arg(file), SortFilterRole); + itemName->setEditable(false); + QFont font = itemName->font(); + font.setBold(true); + itemName->setFont(font); + + QStandardItem *itemUrl = new QStandardItem(file); + itemUrl->setEditable(false); + base_model->setItem(linecount, 0, itemName); + base_model->setItem(linecount, 1, itemUrl); + linecount++; + } + } + + /** + * swap models and kill old one + */ + m_model->setSourceModel (base_model); + delete m_base_model; + m_base_model = base_model; + + if(idxToSelect.isValid()) + m_listView->setCurrentIndex(m_model->mapFromSource(idxToSelect)); + else + reselectFirst(); + + /** + * adjust view + */ + m_listView->resizeColumnToContents(0); +} + +void KateQuickOpen::slotReturnPressed () +{ + /** + * open document for first element, if possible + * prefer to use the document pointer + */ + KTextEditor::Document *doc = m_listView->currentIndex().data (DocumentRole).value >(); + if (doc) { + m_mainWindow->mainWindow()->activateView (doc); + } else { + KUrl url = m_listView->currentIndex().data (UrlRole).value(); + if (!url.isEmpty()) + m_mainWindow->mainWindow()->openUrl (url); + } + + /** + * in any case, switch back to view manager + */ + m_mainWindow->slotWindowActivated (); + m_inputLine->clear (); +} diff --git a/kate/src/app/katequickopen.h b/kate/src/app/katequickopen.h new file mode 100644 index 00000000..8177235c --- /dev/null +++ b/kate/src/app/katequickopen.h @@ -0,0 +1,81 @@ +/* + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + --- + Copyright (C) 2007,2009 Joseph Wenninger +*/ + +#ifndef KATE_QUICK_OPEN_H +#define KATE_QUICK_OPEN_H + +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include +class KLineEdit; +class KateMainWindow; +#include + +namespace KTextEditor { + class Document; +} + +class KateQuickOpen : public QWidget { + Q_OBJECT + public: + KateQuickOpen(QWidget *parent, KateMainWindow *mainWindow); + /** + * update state + * will fill model with current open documents, project documents, ... + */ + void update (); + + protected: + bool eventFilter(QObject *obj, QEvent *event); + + private Q_SLOTS: + void reselectFirst(); + + /** + * Return pressed, activate the selected document + * and go back to background + */ + void slotReturnPressed (); + + private: + KateMainWindow *m_mainWindow; + QTreeView *m_listView; + KLineEdit *m_inputLine; + + /** + * our model we search in + */ + QStandardItemModel *m_base_model; + + /** + * filtered model we search in + */ + QSortFilterProxyModel *m_model; +}; + +#endif diff --git a/kate/src/app/katerunninginstanceinfo.cpp b/kate/src/app/katerunninginstanceinfo.cpp new file mode 100644 index 00000000..66eb93d9 --- /dev/null +++ b/kate/src/app/katerunninginstanceinfo.cpp @@ -0,0 +1,70 @@ +/* This file is part of the KDE project + + Copyright (C) 2009 Joseph Wenninger + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "katerunninginstanceinfo.h" +#include +#include +#include +#include + + +int KateRunningInstanceInfo::dummy_session=0; + +bool fillinRunningKateAppInstances(KateRunningInstanceMap *map) +{ + QDBusConnectionInterface *i = QDBusConnection::sessionBus().interface (); + + // look up all running kate instances and there sessions + QDBusReply servicesReply = i->registeredServiceNames (); + QStringList services; + if (servicesReply.isValid()) + services = servicesReply.value (); + + QString serviceName; + + QString my_pid(QString("%1").arg(getpid()).toLatin1()); + + foreach (const QString &s, services) + { + if (s.startsWith ("org.kde.kate-")) + { + if (s.contains(my_pid)) continue; + KateRunningInstanceInfo* rii=new KateRunningInstanceInfo(s); + if (rii->valid) + { + if (map->contains(rii->sessionName)) return false; //ERROR no two instances may have the same session name + map->insert(rii->sessionName,rii); + //std::cerr<sessionName.toUtf8().data()<constBegin();it!=map->constEnd();++it) + { + delete it.value(); + } + map->clear(); +} + + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/src/app/katerunninginstanceinfo.h b/kate/src/app/katerunninginstanceinfo.h new file mode 100644 index 00000000..be578ffb --- /dev/null +++ b/kate/src/app/katerunninginstanceinfo.h @@ -0,0 +1,80 @@ +/* This file is part of the KDE project + + Copyright (C) 2009 Joseph Wenninger + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef _KATE_RUNNING_INSTANCE_INFO_ +#define _KATE_RUNNING_INSTANCE_INFO_ + +#include + +#include +#include +#include +#include +#include + +class KateRunningInstanceInfo: public QObject { + Q_OBJECT + + public: + KateRunningInstanceInfo(const QString& serviceName_): + QObject(),valid(false), + serviceName(serviceName_), + dbus_if(new QDBusInterface(serviceName_,QLatin1String("/MainApplication"), + QString(), //I don't know why it does not work if I specify org.kde.Kate.Application here + QDBusConnection::sessionBus(),this)) { + if (!dbus_if->isValid()) { + std::cerr<property("activeSession"); +/* std::cerr< KateRunningInstanceMap; + + +KATE_EXPORT bool fillinRunningKateAppInstances(KateRunningInstanceMap *map); +KATE_EXPORT void cleanupRunningKateAppInstanceMap(KateRunningInstanceMap *map); + +#endif + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/src/app/katesavemodifieddialog.cpp b/kate/src/app/katesavemodifieddialog.cpp new file mode 100644 index 00000000..5bdbbca0 --- /dev/null +++ b/kate/src/app/katesavemodifieddialog.cpp @@ -0,0 +1,281 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Joseph Wenninger + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "katesavemodifieddialog.h" +#include "moc_katesavemodifieddialog.cpp" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +class AbstractKateSaveModifiedDialogCheckListItem: public QTreeWidgetItem +{ + public: + AbstractKateSaveModifiedDialogCheckListItem(const QString& title, const QString& url): QTreeWidgetItem() + { + setFlags(flags() | Qt::ItemIsUserCheckable); + setText(0, title); + setText(1, url); + setCheckState(0, Qt::Checked); + setState(InitialState); + } + virtual ~AbstractKateSaveModifiedDialogCheckListItem() + {} + virtual bool synchronousSave(QWidget *dialogParent) = 0; + enum STATE{InitialState, SaveOKState, SaveFailedState}; + STATE state() const + { + return m_state; + } + void setState(enum STATE state) + { + m_state = state; + KIconLoader *loader = KIconLoader::global(); + switch (state) + { + case InitialState: + setIcon(0, QIcon()); + break; + case SaveOKState: + setIcon(0, loader->loadIcon("dialog-ok", KIconLoader::NoGroup,/*height()*/16)); + // "ok" icon should probably be "dialog-success", but we don't have that icon in KDE 4.0 + break; + case SaveFailedState: + setIcon(0, loader->loadIcon("dialog-error", KIconLoader::NoGroup,/*height()*/16)); + break; + } + } + private: + STATE m_state; +}; + +class KateSaveModifiedDocumentCheckListItem: public AbstractKateSaveModifiedDialogCheckListItem +{ + public: + KateSaveModifiedDocumentCheckListItem(KTextEditor::Document *document) + : AbstractKateSaveModifiedDialogCheckListItem(document->documentName(), document->url().pathOrUrl()) + { + m_document = document; + } + virtual ~KateSaveModifiedDocumentCheckListItem() + {} + virtual bool synchronousSave(QWidget *dialogParent) + { + if (m_document->url().isEmpty() ) + { + KEncodingFileDialog::Result r = KEncodingFileDialog::getSaveUrlAndEncoding( + m_document->encoding(), QString(), QString(), dialogParent, i18n("Save As (%1)", m_document->documentName())); + + if (!r.URLs.isEmpty()) + { + KUrl tmp = r.URLs.first(); + + // check for overwriting a file + if( tmp.isLocalFile() ) + { + QFileInfo info( tmp.path() ); + if( info.exists() ) { + if (KMessageBox::Cancel == KMessageBox::warningContinueCancel( dialogParent, + i18n( "A file named \"%1\" already exists. " + "Are you sure you want to overwrite it?" , info.fileName() ), + i18n( "Overwrite File?" ), KStandardGuiItem::overwrite(), + KStandardGuiItem::cancel(), QString(), KMessageBox::Notify | KMessageBox::Dangerous )) + { + setState(SaveFailedState); + return false; + } + } + } + + m_document->setEncoding( r.encoding ); + + if ( !m_document->saveAs( tmp ) ) + { + setState(SaveFailedState); + setText(1, m_document->url().pathOrUrl()); + return false; + } + else + { + bool sc = m_document->waitSaveComplete(); + setText(1, m_document->url().pathOrUrl()); + if (!sc) + { + setState(SaveFailedState); + return false; + } + else + { + setState(SaveOKState); + return true; + } + } + } + else + { + //setState(SaveFailedState); + return false; + } + } + else + { //document has an exising location + if ( !m_document->save() ) + { + setState(SaveFailedState); + setText(1, m_document->url().pathOrUrl()); + return false; + } + else + { + bool sc = m_document->waitSaveComplete(); + setText(1, m_document->url().pathOrUrl()); + if (!sc) + { + setState(SaveFailedState); + return false; + } + else + { + setState(SaveOKState); + return true; + } + } + + } + + return false; + + } + private: + KTextEditor::Document *m_document; +}; + +KateSaveModifiedDialog::KateSaveModifiedDialog(QWidget *parent, QList documents): + KDialog( parent) +{ + + setCaption( i18n("Save Documents") ); + setButtons( KDialog::User1 | KDialog::No | Cancel ); + setObjectName( "KateSaveModifiedDialog" ); + setModal( true ); + + setButtonGuiItem(KDialog::User1, KStandardGuiItem::save()); + connect(this, SIGNAL(user1Clicked()), this, SLOT(slotSaveSelected())); + + setButtonGuiItem(KDialog::No, KStandardGuiItem::discard()); + connect(this, SIGNAL(noClicked()), this, SLOT(slotDoNotSave())); + + setButtonGuiItem(KDialog::Cancel, KStandardGuiItem::cancel()); + setDefaultButton(KDialog::Cancel); + setButtonFocus(KDialog::Cancel); + + KVBox *box = new KVBox(this); + setMainWidget(box); + new QLabel(i18n("The following documents have been modified. Do you want to save them before closing?"), box); + m_list = new QTreeWidget(box); + m_list->setColumnCount(2); + m_list->setHeaderLabels(QStringList() << i18n("Documents") << i18n("Location")); + m_list->setRootIsDecorated(true); + + foreach (KTextEditor::Document* doc, documents) { + m_list->addTopLevelItem(new KateSaveModifiedDocumentCheckListItem(doc)); + } + m_list->resizeColumnToContents(0); + + connect(m_list, SIGNAL(itemChanged(QTreeWidgetItem*,int)), SLOT(slotItemActivated(QTreeWidgetItem*,int))); + connect(new QPushButton(i18n("Se&lect All"), box), SIGNAL(clicked()), this, SLOT(slotSelectAll())); +} + +KateSaveModifiedDialog::~KateSaveModifiedDialog() +{} + +void KateSaveModifiedDialog::slotItemActivated(QTreeWidgetItem*, int) +{ + bool enableSaveButton = false; + + for (int i = 0; i < m_list->topLevelItemCount(); ++i) { + if (m_list->topLevelItem(i)->checkState(0) == Qt::Checked) { + enableSaveButton = true; + break; + } + } + + // enable or disable the Save button + enableButton(KDialog::User1, enableSaveButton); +} + +void KateSaveModifiedDialog::slotSelectAll() +{ + for (int i = 0; i < m_list->topLevelItemCount(); ++i) { + m_list->topLevelItem(i)->setCheckState(0, Qt::Checked); + } + + // enable Save button + enableButton(KDialog::User1, true); +} + + +void KateSaveModifiedDialog::slotSaveSelected() +{ + if (doSave()) done(QDialog::Accepted); +} + +void KateSaveModifiedDialog::slotDoNotSave() +{ + done(QDialog::Accepted); +} + +bool KateSaveModifiedDialog::doSave() +{ + for (int i = 0; i < m_list->topLevelItemCount(); ++i) { + AbstractKateSaveModifiedDialogCheckListItem * cit = static_cast(m_list->topLevelItem(i)); + + if (cit->checkState(0) == Qt::Checked && (cit->state() != AbstractKateSaveModifiedDialogCheckListItem::SaveOKState)) + { + if (!cit->synchronousSave(this /*perhaps that should be the kate mainwindow*/)) + { + if (cit->state()==AbstractKateSaveModifiedDialogCheckListItem::SaveFailedState) + KMessageBox::sorry( this, i18n("Data you requested to be saved could not be written. Please choose how you want to proceed.")); + return false; + } + } + else if ((cit->checkState(0) != Qt::Checked) && (cit->state() == AbstractKateSaveModifiedDialogCheckListItem::SaveFailedState)) + { + cit->setState(AbstractKateSaveModifiedDialogCheckListItem::InitialState); + } + } + + return true; +} + +bool KateSaveModifiedDialog::queryClose(QWidget *parent, QList documents) +{ + KateSaveModifiedDialog d(parent, documents); + return (d.exec() != QDialog::Rejected); +} +// kate: space-indent on; indent-width 2; replace-tabs on; + diff --git a/kate/src/app/katesavemodifieddialog.h b/kate/src/app/katesavemodifieddialog.h new file mode 100644 index 00000000..5218f18b --- /dev/null +++ b/kate/src/app/katesavemodifieddialog.h @@ -0,0 +1,51 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Joseph Wenninger + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef _KATE_SAVE_MODIFIED_DIALOG_ +#define _KATE_SAVE_MODIFIED_DIALOG_ + +#include + +#include +#include + +#include +#include + +class KateSaveModifiedDialog: public KDialog +{ + Q_OBJECT + public: + KateSaveModifiedDialog(QWidget *parent, QList documents); + virtual ~KateSaveModifiedDialog(); + static bool queryClose(QWidget *parent, QList documents); + protected: + bool doSave(); + protected Q_SLOTS: + void slotSelectAll(); + void slotItemActivated(QTreeWidgetItem*, int); + void slotSaveSelected(); + void slotDoNotSave(); + + private: + QTreeWidget *m_list; +}; + +#endif +// kate: space-indent on; indent-width 2; replace-tabs on; + diff --git a/kate/src/app/katesession.cpp b/kate/src/app/katesession.cpp new file mode 100644 index 00000000..17e0003d --- /dev/null +++ b/kate/src/app/katesession.cpp @@ -0,0 +1,1014 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Christoph Cullmann + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "config.h" + +#include "katesession.h" +#include "moc_katesession.cpp" + +#include "kateapp.h" +#include "katemainwindow.h" +#include "katedocmanager.h" +#include "katepluginmanager.h" +#include "katerunninginstanceinfo.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +bool katesessions_compare_sessions_ptr(const KateSession::Ptr &s1, const KateSession::Ptr &s2) { + return KStringHandler::naturalCompare(s1->sessionName(),s2->sessionName())==-1; +} + +//BEGIN KateSession + +KateSession::KateSession (KateSessionManager *manager, const QString &fileName) + : m_sessionFileRel (fileName) + , m_documents (0) + , m_manager (manager) + , m_readConfig (0) + , m_writeConfig (0) +{ + m_sessionName = QUrl::fromPercentEncoding(QFile::encodeName(fileName)); + m_sessionName.chop(12);//.katesession==12 + init (); +} + +void KateSession::init () +{ + // given file exists, use it to load some stuff ;) + if (!m_sessionFileRel.isEmpty() && KGlobal::dirs()->exists(sessionFile ())) + { + KConfig config (sessionFile (), KConfig::SimpleConfig); + + // get the document count + m_documents = config.group("Open Documents").readEntry("Count", 0); + + return; + } + + if (!m_sessionFileRel.isEmpty() && !KGlobal::dirs()->exists(sessionFile ())) + kDebug() << "Warning, session file not found: " << m_sessionFileRel; +} + +KateSession::~KateSession () +{ + delete m_readConfig; + delete m_writeConfig; +} + +QString KateSession::sessionFile () const +{ + if (m_sessionFileRel.isEmpty()) { + return QString(); + } + + return m_manager->sessionsDir() + '/' + m_sessionFileRel; +} + +bool KateSession::create (const QString &name, bool force) +{ + if (!force && (name.isEmpty() || !m_sessionFileRel.isEmpty())) + return false; + + delete m_writeConfig; + m_writeConfig = 0; + + delete m_readConfig; + m_readConfig = 0; + + m_sessionName = name; + QString oldSessionFileRel = m_sessionFileRel; + m_sessionFileRel = QUrl::toPercentEncoding(name, "", ".") + QString(".katesession"); + if (KGlobal::dirs()->exists(sessionFile ())) + { + m_sessionFileRel = oldSessionFileRel; + return false; + } + + // create the file, write name to it! + KConfig config (sessionFile (), KConfig::SimpleConfig); +// config.group("General").writeEntry ("Name", m_sessionName); + config.sync (); + + // reinit ourselfs ;) + init (); + + return true; +} + +bool KateSession::rename (const QString &name) +{ + if (name.isEmpty () || m_sessionFileRel.isEmpty()) + return false; + + if (name == m_sessionName) return true; + QString oldRel = m_sessionFileRel; + QString oldSessionFile = sessionFile(); + m_sessionFileRel = QUrl::toPercentEncoding(name, "", ".") + QString(".katesession"); + if (KGlobal::dirs()->exists(sessionFile ())) + { + m_sessionFileRel = oldRel; + return false; + } + KUrl srcUrl(QString("file://")); + srcUrl.addPath(oldSessionFile); + KUrl destUrl(QString("file://")); + destUrl.addPath(sessionFile()); + KIO::CopyJob *job = KIO::move(srcUrl, destUrl, KIO::HideProgressInfo); + if ( ! KIO::NetAccess::synchronousRun(job, 0) ) + { + m_sessionFileRel = oldRel; + return false; + } + m_sessionName = name; + delete m_writeConfig; + m_writeConfig=0; + delete m_readConfig; + m_readConfig=0; + return true; +} + +KConfig *KateSession::configRead () +{ + if (m_sessionFileRel.isEmpty()) + return KGlobal::config().data(); + + if (m_readConfig) + return m_readConfig; + + return m_readConfig = new KConfig (sessionFile (), KConfig::SimpleConfig); +} + +KConfig *KateSession::configWrite () +{ + if (m_sessionFileRel.isEmpty()) + return KGlobal::config().data(); + + if (m_writeConfig) + return m_writeConfig; + + m_writeConfig = new KConfig (sessionFile (), KConfig::SimpleConfig); + + return m_writeConfig; +} + +void KateSession::makeAnonymous() +{ + delete m_readConfig; + m_readConfig = 0; + + delete m_writeConfig; + m_writeConfig = 0; + + m_sessionFileRel.clear(); + m_sessionName.clear(); +} + +//END KateSession + + +//BEGIN KateSessionManager + +KateSessionManager::KateSessionManager (QObject *parent) + : QObject (parent) + , m_sessionsDir (KStandardDirs::locateLocal( "data", "kate/sessions")) + , m_activeSession (new KateSession (this, QString())) +{ + kDebug() << "LOCAL SESSION DIR: " << m_sessionsDir; + + // create dir if needed + KGlobal::dirs()->makeDir (m_sessionsDir); +} + +KateSessionManager::~KateSessionManager() +{} + +KateSessionManager *KateSessionManager::self() +{ + return KateApp::self()->sessionManager (); +} + +void KateSessionManager::dirty (const QString &) +{ + updateSessionList (); +} + +void KateSessionManager::updateSessionList () +{ + m_sessionList.clear (); + + // Let's get a list of all session we have atm + QDir dir (m_sessionsDir, "*.katesession"); + + for (unsigned int i = 0; i < dir.count(); ++i) + { + KateSession *session = new KateSession (this, dir[i]); + if (m_activeSession) + if (session->sessionName()==m_activeSession->sessionName()) + { + delete session; + session=m_activeSession.data(); + } + m_sessionList.append (KateSession::Ptr(session)); + + //kDebug () << "FOUND SESSION: " << session->sessionName() << " FILE: " << session->sessionFile() << " dir[i];" << dir[i]; + } + + qSort(m_sessionList.begin(), m_sessionList.end(), katesessions_compare_sessions_ptr); +} + +bool KateSessionManager::activateSession (KateSession::Ptr session, + bool closeLast, + bool saveLast, + bool loadNew) +{ + if ( (!session->sessionName().isEmpty()) && (m_activeSession!=session)) { + //check if the requested session is already open in another instance + KateRunningInstanceMap instances; + if (!fillinRunningKateAppInstances(&instances)) + { + KMessageBox::error(0,i18n("Internal error: there is more than one instance open for a given session.")); + return false; + } + + if (instances.contains(session->sessionName())) + { + if (KMessageBox::questionYesNo(0,i18n("Session '%1' is already opened in another kate instance, change there instead of reopening?",session->sessionName()), + QString(),KStandardGuiItem::yes(),KStandardGuiItem::no(),"katesessionmanager_switch_instance")==KMessageBox::Yes) + { + instances[session->sessionName()]->dbus_if->call("activate"); + cleanupRunningKateAppInstanceMap(&instances); + return false; + } + } + + cleanupRunningKateAppInstanceMap(&instances); + } + // try to close last session + if (closeLast) + { + if (KateApp::self()->activeMainWindow()) + { + if (!KateApp::self()->activeMainWindow()->queryClose_internal()) + return true; + } + } + + // save last session or not? + if (saveLast) + saveActiveSession (); + + // really close last + if (closeLast) + KateDocManager::self()->closeAllDocuments (); + + // set the new session + m_activeSession = session; + + if (loadNew) + { + // open the new session + KConfig *sc = activeSession()->configRead(); + const bool loadDocs = (sc != KGlobal::config().data()); // do not load docs for new sessions + + // if we have no session config object, try to load the default + // (anonymous/unnamed sessions) + if ( !sc ) + sc = KGlobal::config().data(); + // load plugin config + plugins + KatePluginManager::self()->loadConfig (sc); + + if (sc && loadDocs) + KateApp::self()->documentManager()->restoreDocumentList (sc); + + // window config + KConfigGroup c(KGlobal::config(), "General"); + + if (c.readEntry("Restore Window Configuration", true)) + { + // a new, named session, read settings of the default session. + if ( ! sc->hasGroup("Open MainWindows") ) + sc = KGlobal::config().data(); + + int wCount = sc->group("Open MainWindows").readEntry("Count", 1); + + for (int i = 0; i < wCount; ++i) + { + if (i >= KateApp::self()->mainWindows()) + { + KateApp::self()->newMainWindow(sc, QString ("MainWindow%1").arg(i)); + } + else + { + KateApp::self()->mainWindow(i)->readProperties(KConfigGroup(sc, QString ("MainWindow%1").arg(i) )); + } + + KateApp::self()->mainWindow(i)->restoreWindowConfig(KConfigGroup(sc, QString ("MainWindow%1 Settings").arg(i))); + } + + // remove mainwindows we need no longer... + if (wCount > 0) + { + while (wCount < KateApp::self()->mainWindows()) + delete KateApp::self()->mainWindow(KateApp::self()->mainWindows() - 1); + } + } + } + + emit sessionChanged(); + return true; +} + +KateSession::Ptr KateSessionManager::giveSession (const QString &name) +{ + if (name.isEmpty()) + return KateSession::Ptr(new KateSession (this, QString())); + + updateSessionList(); + + for (int i = 0; i < m_sessionList.count(); ++i) + { + if (m_sessionList[i]->sessionName() == name) + return m_sessionList[i]; + } + + KateSession::Ptr s(new KateSession (this, QString())); + s->create (name); + return s; +} + +// helper function to save the session to a given config object +static void saveSessionTo(KConfig *sc) +{ + // save plugin configs and which plugins to load + KatePluginManager::self()->writeConfig(sc); + + // save document configs + which documents to load + KateDocManager::self()->saveDocumentList (sc); + + sc->group("Open MainWindows").writeEntry ("Count", KateApp::self()->mainWindows ()); + + // save config for all windows around ;) + bool saveWindowConfig = KConfigGroup(KGlobal::config(), "General").readEntry("Restore Window Configuration", true); + for (int i = 0; i < KateApp::self()->mainWindows (); ++i ) + { + KConfigGroup cg(sc, QString ("MainWindow%1").arg(i) ); + KateApp::self()->mainWindow(i)->saveProperties (cg); + if (saveWindowConfig) + KateApp::self()->mainWindow(i)->saveWindowConfig(KConfigGroup(sc, QString ("MainWindow%1 Settings").arg(i))); + } + + sc->sync(); + + /** + * try to sync file to disk + */ + QFile fileToSync (sc->name()); + if (fileToSync.open (QIODevice::ReadOnly)) { + // ensure that the file is written to disk +#ifdef HAVE_FDATASYNC + fdatasync (fileToSync.handle()); +#else + fsync (fileToSync.handle()); +#endif + } +} + +bool KateSessionManager::saveActiveSession (bool rememberAsLast) +{ +// if (activeSession()->isAnonymous()) +// newSessionName(); + + KConfig *sc = activeSession()->configWrite(); + + if (!sc) + return false; + + saveSessionTo(sc); + + if (rememberAsLast) + { + KSharedConfig::Ptr c = KGlobal::config(); + c->group("General").writeEntry ("Last Session", activeSession()->sessionFileRelative()); + c->sync (); + } + return true; +} + +bool KateSessionManager::chooseSession () +{ + bool success = true; + + // app config + KConfigGroup c(KGlobal::config(), "General"); + + // get last used session, default to default session + QString lastSession (c.readEntry ("Last Session", QString())); + QString sesStart (c.readEntry ("Startup Session", "manual")); + + // uhh, just open last used session, show no chooser + if (sesStart == "last") + { + activateSession (KateSession::Ptr(new KateSession (this, lastSession)), false, false); + return success; + } + + // start with empty new session + // also, if no sessions exist + if (sesStart == "new" || sessionList().size() == 0) + { + activateSession (KateSession::Ptr(new KateSession (this, QString())), false, false); + return success; + } + + KateSessionChooser *chooser = new KateSessionChooser (0, lastSession); + + bool retry = true; + int res = 0; + while (retry) + { + res = chooser->exec (); + + switch (res) + { + case KateSessionChooser::resultOpen: + { + KateSession::Ptr s = chooser->selectedSession (); + + if (!s) + { + KMessageBox::error (chooser, i18n("No session selected to open."), i18n ("No Session Selected")); + break; + } + + success = activateSession (s, false, false); + retry = false; + break; + } + + // exit the app lateron + case KateSessionChooser::resultQuit: + success = false; + retry = false; + break; + + case KateSessionChooser::resultNew: { + success = true; + retry = false; + activateSession (KateSession::Ptr(new KateSession (this, QString())), false, false); + break; + } + + case KateSessionChooser::resultCopy: { + KateSession::Ptr s = chooser->selectedSession (); + if (!s) { + KMessageBox::error (chooser, i18n("No session selected to copy."), i18n ("No Session Selected")); + break; + } + + success = true; + retry = false; + activateSession (s, false, false); + s->makeAnonymous(); + // emit signal again, now we are anonymous + emit sessionChanged(); + break; + } + + default: + activateSession (KateSession::Ptr(new KateSession (this, QString())), false, false) ; + retry = false; + break; + } + } + + // write back our nice boolean :) + if (success && chooser->reopenLastSession ()) + { + KConfigGroup generalConfig(KGlobal::config(), "General"); + + if (res == KateSessionChooser::resultOpen) + generalConfig.writeEntry ("Startup Session", "last"); + else if (res == KateSessionChooser::resultNew) + generalConfig.writeEntry ("Startup Session", "new"); + + generalConfig.sync (); + } + + delete chooser; + + return success; +} + +void KateSessionManager::sessionNew () +{ + activateSession (KateSession::Ptr(new KateSession (this, ""))); +} + +void KateSessionManager::sessionOpen () +{ + KateSessionOpenDialog *chooser = new KateSessionOpenDialog (0); + + int res = chooser->exec (); + + if (res == KateSessionOpenDialog::resultCancel) + { + delete chooser; + return; + } + + KateSession::Ptr s = chooser->selectedSession (); + + if (s) + activateSession (s); + + delete chooser; +} + +void KateSessionManager::sessionSave () +{ + // if the active session is valid, just save it :) + if (saveActiveSession ()) + return; + + sessionSaveAs(); +} + +void KateSessionManager::sessionSaveAs () +{ + newSessionName(); + saveActiveSession (); + emit sessionChanged(); +} + +bool KateSessionManager::newSessionName() +{ + bool alreadyExists = false; + QString name; + do { + bool ok = false; + name = KInputDialog::getText ( + i18n("Specify New Name for Current Session"), + alreadyExists ? i18n("There is already an existing session with your chosen name.\nPlease choose a different one\nSession name:") : i18n("Session name:") + , name, &ok); + + if (!ok) + return false; + + if (name.isEmpty()) + KMessageBox::sorry (0, i18n("To save a session, you must specify a name."), i18n ("Missing Session Name")); + + alreadyExists = true; + } + while (!activeSession()->create (name, true)); + return true; +} + +void KateSessionManager::sessionManage () +{ + KateSessionManageDialog *dlg = new KateSessionManageDialog (0); + + dlg->exec (); + + delete dlg; +} + +//END KateSessionManager + +//BEGIN CHOOSER DIALOG + +class KateSessionChooserItem : public QTreeWidgetItem +{ + public: + KateSessionChooserItem (QTreeWidget *tw, KateSession::Ptr s) + : QTreeWidgetItem (tw, QStringList(s->sessionName())) + , session (s) + { + QString docs; + docs.setNum (s->documents()); + setText (1, docs); + } + + KateSession::Ptr session; +}; + +KateSessionChooser::KateSessionChooser (QWidget *parent, const QString &lastSession) + : KDialog ( parent ) +{ + setCaption( i18n ("Session Chooser") ); + setButtons( User1 | User2 | User3 ); + setButtonGuiItem( User1, KStandardGuiItem::quit() ); + setButtonGuiItem( User2, KGuiItem (i18n ("Open Session"), "document-open") ); + setButtonGuiItem( User3, KGuiItem (i18n ("New Session"), "document-new") ); + + //showButtonSeparator(true); + QFrame *page = new QFrame (this); + QVBoxLayout *tll = new QVBoxLayout(page); + page->setMinimumSize (400, 200); + setMainWidget(page); + + m_sessions = new QTreeWidget (page); + tll->addWidget(m_sessions); + QStringList header; + header << i18n("Session Name"); + header << i18nc("The number of open documents", "Open Documents"); + m_sessions->setHeaderLabels(header); + m_sessions->setRootIsDecorated( false ); + m_sessions->setItemsExpandable( false ); + m_sessions->setAllColumnsShowFocus( true ); + m_sessions->setSelectionBehavior(QAbstractItemView::SelectRows); + m_sessions->setSelectionMode (QAbstractItemView::SingleSelection); + + connect (m_sessions, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), this, SLOT(selectionChanged(QTreeWidgetItem*,QTreeWidgetItem*))); + + QMenu* popup = new QMenu(this); + button(KDialog::User3)->setDelayedMenu(popup); + QAction *a = popup->addAction(i18n("Use selected session as template")); + connect(a, SIGNAL(triggered()), this, SLOT(slotCopySession())); + + const KateSessionList &slist (KateSessionManager::self()->sessionList()); + kDebug()<<"Last session is:"<sessionName()<<"........"<sessionFileRelative(); + if (slist[i]->sessionFileRelative() == lastSession) + m_sessions->setCurrentItem (item); + } + + m_sessions->resizeColumnToContents(0); + + m_useLast = new QCheckBox (i18n ("&Always use this choice"), page); + tll->addWidget(m_useLast); + + setResult (resultNone); + + // trigger action update + selectionChanged (NULL, NULL); + connect(this, SIGNAL(user1Clicked()), this, SLOT(slotUser1())); + connect(this, SIGNAL(user2Clicked()), this, SLOT(slotUser2())); + connect(m_sessions, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)), this, SLOT(slotUser2())); + connect(this, SIGNAL(user3Clicked()), this, SLOT(slotUser3())); + enableButton (KDialog::User2, m_sessions->currentIndex().isValid()); + + setDefaultButton(KDialog::User2); + setEscapeButton(KDialog::User1); + setButtonFocus(KDialog::User2); +} + +KateSessionChooser::~KateSessionChooser () +{} + +void KateSessionChooser::slotCopySession() +{ + done(resultCopy); +} + +KateSession::Ptr KateSessionChooser::selectedSession () +{ + KateSessionChooserItem *item = static_cast(m_sessions->currentItem ()); + + if (!item) + return KateSession::Ptr(); + + return item->session; +} + +bool KateSessionChooser::reopenLastSession () +{ + return m_useLast->isChecked (); +} + +void KateSessionChooser::slotUser2 () +{ + done (resultOpen); +} + +void KateSessionChooser::slotUser3 () +{ + done (resultNew); +} + +void KateSessionChooser::slotUser1 () +{ + done (resultQuit); +} + +void KateSessionChooser::selectionChanged(QTreeWidgetItem *current, QTreeWidgetItem *) +{ + enableButton (KDialog::User2, current); +} + +//END CHOOSER DIALOG + +//BEGIN OPEN DIALOG + +KateSessionOpenDialog::KateSessionOpenDialog (QWidget *parent) + : KDialog ( parent ) + +{ + setCaption( i18n ("Open Session") ); + setButtons( User1 | User2 ); + setButtonGuiItem( User1, KStandardGuiItem::cancel() ); + // don't use KStandardGuiItem::open() here which has trailing ellipsis! + setButtonGuiItem( User2, KGuiItem( i18n("&Open"), "document-open") ); + setDefaultButton( KDialog::User2 ); + enableButton( KDialog::User2, false ); + //showButtonSeparator(true); + /*QFrame *page = new QFrame (this); + page->setMinimumSize (400, 200); + setMainWidget(page); + + QHBoxLayout *hb = new QHBoxLayout (page); + + QVBoxLayout *vb = new QVBoxLayout (); + hb->addItem(vb);*/ + m_sessions = new QTreeWidget (this); + m_sessions->setMinimumSize(400, 200); + setMainWidget(m_sessions); + //vb->addWidget(m_sessions); + QStringList header; + header << i18n("Session Name"); + header << i18nc("The number of open documents", "Open Documents"); + m_sessions->setHeaderLabels(header); + m_sessions->setRootIsDecorated( false ); + m_sessions->setItemsExpandable( false ); + m_sessions->setAllColumnsShowFocus( true ); + m_sessions->setSelectionBehavior(QAbstractItemView::SelectRows); + m_sessions->setSelectionMode (QAbstractItemView::SingleSelection); + + const KateSessionList &slist (KateSessionManager::self()->sessionList()); + for (int i = 0; i < slist.count(); ++i) + { + new KateSessionChooserItem (m_sessions, slist[i]); + } + m_sessions->resizeColumnToContents(0); + + setResult (resultCancel); + connect(this, SIGNAL(user1Clicked()), this, SLOT(slotUser1())); + connect(this, SIGNAL(user2Clicked()), this, SLOT(slotUser2())); + connect(m_sessions, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), this, SLOT(selectionChanged(QTreeWidgetItem*,QTreeWidgetItem*))); + connect(m_sessions, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)), this, SLOT(slotUser2())); + +} + +KateSessionOpenDialog::~KateSessionOpenDialog () +{} + +KateSession::Ptr KateSessionOpenDialog::selectedSession () +{ + KateSessionChooserItem *item = static_cast(m_sessions->currentItem ()); + + if (!item) + return KateSession::Ptr(); + + return item->session; +} + +void KateSessionOpenDialog::slotUser1 () +{ + done (resultCancel); +} + +void KateSessionOpenDialog::slotUser2 () +{ + done (resultOk); +} + +void KateSessionOpenDialog::selectionChanged(QTreeWidgetItem *current, QTreeWidgetItem *) +{ + enableButton (KDialog::User2, current); +} + +//END OPEN DIALOG + +//BEGIN MANAGE DIALOG + +KateSessionManageDialog::KateSessionManageDialog (QWidget *parent) + : KDialog ( parent ) +{ + setCaption( i18n ("Manage Sessions") ); + setButtons( User1 | User2 ); + setButtonGuiItem( User1, KStandardGuiItem::close() ); + // don't use KStandardGuiItem::open() here which has trailing ellipsis! + setButtonGuiItem( User2, KGuiItem( i18n("&Open"), "document-open") ); + + setDefaultButton(KDialog::User1); + QFrame *page = new QFrame (this); + page->setMinimumSize (400, 200); + setMainWidget(page); + + QHBoxLayout *hb = new QHBoxLayout (page); + hb->setSpacing (KDialog::spacingHint()); + + m_sessions = new QTreeWidget (page); + hb->addWidget(m_sessions); + m_sessions->setColumnCount(2); + QStringList header; + header << i18n("Session Name"); + header << i18nc("The number of open documents", "Open Documents"); + m_sessions->setHeaderLabels(header); + m_sessions->setRootIsDecorated( false ); + m_sessions->setItemsExpandable( false ); + m_sessions->setAllColumnsShowFocus( true ); + m_sessions->setSelectionBehavior(QAbstractItemView::SelectRows); + m_sessions->setSelectionMode (QAbstractItemView::SingleSelection); + + connect (m_sessions, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), this, SLOT(selectionChanged(QTreeWidgetItem*,QTreeWidgetItem*))); + + updateSessionList (); + m_sessions->resizeColumnToContents(0); + + QVBoxLayout *vb = new QVBoxLayout (); + hb->addItem(vb); + vb->setSpacing (KDialog::spacingHint()); + + m_rename = new KPushButton (i18n("&Rename..."), page); + connect (m_rename, SIGNAL(clicked()), this, SLOT(rename())); + vb->addWidget (m_rename); + + m_del = new KPushButton (KStandardGuiItem::del (), page); + connect (m_del, SIGNAL(clicked()), this, SLOT(del())); + vb->addWidget (m_del); + + vb->addStretch (); + + // trigger action update + selectionChanged (NULL, NULL); + connect(this, SIGNAL(user1Clicked()), this, SLOT(slotUser1())); + connect(this, SIGNAL(user2Clicked()), this, SLOT(open())); +} + +KateSessionManageDialog::~KateSessionManageDialog () +{} + +void KateSessionManageDialog::slotUser1 () +{ + done (0); +} + +void KateSessionManageDialog::selectionChanged (QTreeWidgetItem *current, QTreeWidgetItem *) +{ + const bool validItem = (current != NULL); + + m_rename->setEnabled (validItem); + m_del->setEnabled (validItem && (static_cast(current))->session!=KateSessionManager::self()->activeSession()); + button(User2)->setEnabled (validItem); +} + +void KateSessionManageDialog::rename () +{ + KateSessionChooserItem *item = static_cast(m_sessions->currentItem ()); + + if (!item) + return; + + bool ok = false; + QString name = KInputDialog::getText (i18n("Specify New Name for Session"), i18n("Session name:"), item->session->sessionName(), &ok); + + if (!ok) + return; + + if (name.isEmpty()) + { + KMessageBox::sorry (this, i18n("To save a session, you must specify a name."), i18n ("Missing Session Name")); + return; + } + + if (item->session->rename (name)) { + if (item->session==KateSessionManager::self()->activeSession()) { + emit KateSessionManager::self()->sessionChanged(); + } + updateSessionList (); + } + else + KMessageBox::sorry(this, i18n("The session could not be renamed to \"%1\", there already exists another session with the same name", name), i18n("Session Renaming")); +} + +void KateSessionManageDialog::del () +{ + KateSessionChooserItem *item = static_cast(m_sessions->currentItem ()); + + if (!item) + return; + + QFile::remove (item->session->sessionFile()); + KateSessionManager::self()->updateSessionList (); + updateSessionList (); +} + +void KateSessionManageDialog::open () +{ + KateSessionChooserItem *item = static_cast(m_sessions->currentItem ()); + + if (!item) + return; + + hide(); + KateSessionManager::self()->activateSession (item->session); + done(0); +} + +void KateSessionManageDialog::updateSessionList () +{ + m_sessions->clear (); + + const KateSessionList &slist (KateSessionManager::self()->sessionList()); + for (int i = 0; i < slist.count(); ++i) + { + new KateSessionChooserItem (m_sessions, slist[i]); + } +} + +//END MANAGE DIALOG + + +KateSessionsAction::KateSessionsAction(const QString& text, QObject* parent) + : KActionMenu(text, parent) +{ + connect(menu(), SIGNAL(aboutToShow()), this, SLOT(slotAboutToShow())); + + sessionsGroup = new QActionGroup( menu() ); + + // reason for Qt::QueuedConnection: when switching session with N mainwindows + // to e.g. 1 mainwindow, the last N - 1 mainwindows are deleted. Invoking + // a session switch without queued connection deletes a mainwindow in which + // the current code path is executed ---> crash. See bug #227008. + connect(sessionsGroup, SIGNAL(triggered(QAction*)), this, SLOT(openSession(QAction*)), Qt::QueuedConnection); + + connect(KateSessionManager::self(), SIGNAL(sessionChanged()), this, SLOT(slotSessionChanged())); + setDisabled(KateSessionManager::self()->sessionList().size() == 0); +} + +void KateSessionsAction::slotAboutToShow() +{ + qDeleteAll( sessionsGroup->actions() ); + + const KateSessionList &slist (KateSessionManager::self()->sessionList()); + for (int i = 0; i < slist.count(); ++i) + { + QString sessionName = slist[i]->sessionName(); + sessionName.replace("&", "&&"); + QAction *action = new QAction( sessionName, sessionsGroup ); + action->setData(QVariant(i)); + menu()->addAction (action); + } +} + +void KateSessionsAction::openSession (QAction *action) +{ + const KateSessionList &slist (KateSessionManager::self()->sessionList()); + + int i = action->data().toInt(); + if (i >= slist.count()) + return; + + KateSessionManager::self()->activateSession(slist[i]); +} + +void KateSessionsAction::slotSessionChanged() +{ + setDisabled(KateSessionManager::self()->sessionList().size() == 0); +} + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/src/app/katesession.h b/kate/src/app/katesession.h new file mode 100644 index 00000000..14a4ae1c --- /dev/null +++ b/kate/src/app/katesession.h @@ -0,0 +1,468 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Christoph Cullmann + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef __KATE_SESSION_H__ +#define __KATE_SESSION_H__ + +#include "katemain.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +class KateSessionManager; +class KPushButton; + +#include + +class KateSession : public QSharedData +{ + public: + /** + * Define a Shared-Pointer type + */ + typedef KSharedPtr Ptr; + + public: + /** + * create a session from given file + * @param manager pointer to the manager + * @param fileName session filename, relative + */ + KateSession (KateSessionManager *manager, const QString &fileName); + + /** + * init the session object, after construction or create + */ + void init (); + + /** + * destruct me + */ + ~KateSession (); + + /** + * session filename, absolute, calculated out of relative filename + session dir + * @return absolute path to session file + */ + QString sessionFile () const; + + /** + * relative session filename + * @return relative filename for this session + */ + const QString &sessionFileRelative () const + { + return m_sessionFileRel; + } + + /** + * session name + * @return name for this session + */ + const QString &sessionName () const + { + return m_sessionName; + } + + bool isAnonymous() const + { + return !(m_readConfig && m_writeConfig); + } + + void makeAnonymous(); + + /** + * create the session file, if not existing + * @param name name for this session + * @param force force to create new file + * @return true if created, false if no creation needed + */ + bool create (const QString &name, bool force = false); + + /** + * rename this session + * @param name new name + * @return success + */ + bool rename (const QString &name); + + /** + * config to read + * on first access, will create the config object, delete will be done automagic + * return 0 if we have no file to read config from atm + * @return config to read from + * @note never delete configRead(), because the return value might be + * KGlobal::config(). Only delete the member variables directly. + */ + KConfig *configRead (); + + /** + * config to write + * on first access, will create the config object, delete will be done automagic + * return 0 if we have no file to write config to atm + * @return config to write from + * @note never delete configWrite(), because the return value might be + * KGlobal::config(). Only delete the member variables directly. + */ + KConfig *configWrite (); + + /** + * count of documents in this session + * @return documents count + */ + unsigned int documents () const + { + return m_documents; + } + + private: + /** + * session filename, in local location we can write to + * relative filename to the session dirs :) + */ + QString m_sessionFileRel; + + /** + * session name, extracted from the file, to display to the user + */ + QString m_sessionName; + + /** + * number of document of this session + */ + unsigned int m_documents; + + /** + * KateSessionMananger + */ + KateSessionManager *m_manager; + + /** + * simpleconfig to read from + */ + KConfig *m_readConfig; + + /** + * simpleconfig to write to + */ + KConfig *m_writeConfig; +}; + +typedef QList KateSessionList; + +class KateSessionManager : public QObject +{ + Q_OBJECT + + public: + KateSessionManager(QObject *parent); + ~KateSessionManager(); + + /** + * allow access to this :) + * @return instance of the session manager + */ + static KateSessionManager *self(); + + /** + * allow access to the session list + * kept up to date by watching the dir + */ + inline KateSessionList & sessionList () + { + updateSessionList (); + return m_sessionList; + } + + /** + * activate a session + * first, it will look if a session with this name exists in list + * if yes, it will use this session, else it will create a new session file + * @param session session to activate + * @param closeLast try to close last session or not? + * @param saveLast try to save last session or not? + * @param loadNew load new session stuff? + * @return false==session has been delegated, true==session has been activated in this distance + */ + bool activateSession (KateSession::Ptr session, bool closeLast = true, bool saveLast = true, bool loadNew = true); + + /** + * return session with given name + * if no existing session matches, create new one with this name + * @param name session name + */ + KateSession::Ptr giveSession (const QString &name); + + /** + * save current session + * for sessions without filename: save nothing + * @param rememberAsLast remember this session as last used? + * @return success + */ + bool saveActiveSession (bool rememberAsLast = false); + + /** + * return the current active session + * sessionFile == empty means we have no session around for this instance of kate + * @return session active atm + */ + inline KateSession::Ptr activeSession () + { + return m_activeSession; + } + + /** + * session dir + * @return global session dir + */ + inline const QString &sessionsDir () const + { + return m_sessionsDir; + } + + /** + * initial session chooser, on app start + * @return success, if false, app should exit + */ + bool chooseSession (); + + public Q_SLOTS: + /** + * try to start a new session + * asks user first for name + */ + void sessionNew (); + + /** + * try to open a existing session + */ + void sessionOpen (); + + /** + * try to save current session + */ + void sessionSave (); + + /** + * try to save as current session + */ + void sessionSaveAs (); + + /** + * show dialog to manage our sessions + */ + void sessionManage (); + + Q_SIGNALS: + /** + * Emitted, whenever the session changes, e.g. when it was renamed. + */ + friend class KateSessionManageDialog; + void sessionChanged(); + + private Q_SLOTS: + void dirty (const QString &path); + + public: + /** + * trigger update of session list + */ + void updateSessionList (); + + private: + /** + * Asks the user for a new session name. Used by save as for example. + */ + bool newSessionName(); + + private: + /** + * absolute path to dir in home dir where to store the sessions + */ + QString m_sessionsDir; + + /** + * list of current available sessions + */ + KateSessionList m_sessionList; + + /** + * current active session + */ + KateSession::Ptr m_activeSession; +}; + +class KateSessionChooser : public KDialog +{ + Q_OBJECT + + public: + KateSessionChooser (QWidget *parent, const QString &lastSession); + ~KateSessionChooser (); + + KateSession::Ptr selectedSession (); + bool reopenLastSession (); + + enum { + resultQuit = QDialog::Rejected, + resultOpen, + resultNew, + resultNone, + resultCopy + }; + + protected Q_SLOTS: + /** + * quit kate + */ + void slotUser1 (); + + /** + * open session + */ + void slotUser2 (); + + /** + * new session + */ + void slotUser3 (); + + void slotCopySession(); + + /** + * selection has changed + */ + void selectionChanged (QTreeWidgetItem *current, QTreeWidgetItem *previous); + + private: + QTreeWidget *m_sessions; + QCheckBox *m_useLast; +}; + +class KateSessionOpenDialog : public KDialog +{ + Q_OBJECT + + public: + KateSessionOpenDialog (QWidget *parent); + ~KateSessionOpenDialog (); + + KateSession::Ptr selectedSession (); + + enum { + resultOk, + resultCancel + }; + + protected Q_SLOTS: + /** + * cancel pressed + */ + void slotUser1 (); + + /** + * ok pressed + */ + void slotUser2 (); + + /** + * selection has changed + */ + void selectionChanged (QTreeWidgetItem *current, QTreeWidgetItem *previous); + + private: + QTreeWidget *m_sessions; +}; + +class KateSessionManageDialog : public KDialog +{ + Q_OBJECT + + public: + KateSessionManageDialog (QWidget *parent); + ~KateSessionManageDialog (); + + protected Q_SLOTS: + /** + * close pressed + */ + void slotUser1 (); + + /** + * selection has changed + */ + void selectionChanged (QTreeWidgetItem *current, QTreeWidgetItem *previous); + + /** + * try to rename session + */ + void rename (); + + /** + * try to delete session + */ + void del (); + + /** + * close dialog and open the selected session + */ + void open (); + + private: + /** + * update our list + */ + void updateSessionList (); + + private: + QTreeWidget *m_sessions; + KPushButton *m_rename; + KPushButton *m_del; +}; + +class KateSessionsAction : public KActionMenu +{ + Q_OBJECT + + public: + KateSessionsAction(const QString& text, QObject *parent); + ~KateSessionsAction () + { + } + + public Q_SLOTS: + void slotAboutToShow(); + + void openSession (QAction *action); + void slotSessionChanged(); + + private: + QActionGroup *sessionsGroup; +}; + +#endif + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/src/app/kateviewmanager.cpp b/kate/src/app/kateviewmanager.cpp new file mode 100644 index 00000000..b4eea1f5 --- /dev/null +++ b/kate/src/app/kateviewmanager.cpp @@ -0,0 +1,1096 @@ +/* This file is part of the KDE project + Copyright (C) 2001 Christoph Cullmann + Copyright (C) 2001 Joseph Wenninger + Copyright (C) 2001 Anders Lund + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +//BEGIN Includes +#include "kateviewmanager.h" +#include "moc_kateviewmanager.cpp" + +#include "katemainwindow.h" +#include "katedocmanager.h" +#include "kateviewspace.h" + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include +#include +#include +#include +#include +#include + +//END Includes + +static const qint64 FileSizeAboveToAskUserIfProceedWithOpen = 10 * 1024 * 1024; // 10MB should suffice + +KateViewManager::KateViewManager (QWidget *parentW, KateMainWindow *parent) + : QSplitter (parentW) + , m_mainWindow(parent) + , m_blockViewCreationAndActivation (false) + , m_activeViewRunning (false) + , m_minAge (0) +{ + // while init + m_init = true; + + // important, set them up, as we use them in other methodes + setupActions (); + + guiMergedView = 0; + + // resize mode + setOpaqueResize( KGlobalSettings::opaqueResize() ); + + KateViewSpace* vs = new KateViewSpace( this, 0 ); + addWidget (vs); + + vs->setActive( true ); + m_viewSpaceList.append(vs); + + connect( this, SIGNAL(viewChanged()), this, SLOT(slotViewChanged()) ); + connect(KateDocManager::self(), SIGNAL(initialDocumentReplaced()), this, SIGNAL(viewChanged())); + + connect(KateDocManager::self(), SIGNAL(documentCreated(KTextEditor::Document*)), this, SLOT(documentCreated(KTextEditor::Document*))); + connect(KateDocManager::self(), SIGNAL(documentDeleted(KTextEditor::Document*)), this, SLOT(documentDeleted(KTextEditor::Document*))); + + // register all already existing documents + m_blockViewCreationAndActivation = true; + const QList &docs = KateDocManager::self()->documentList (); + foreach (KTextEditor::Document *doc, docs) + documentCreated (doc); + m_blockViewCreationAndActivation = false; + + // init done + m_init = false; +} + +KateViewManager::~KateViewManager () +{ + // make sure all xml gui clients are removed to avoid warnings on exit + foreach (KTextEditor::View* view, m_viewList) + mainWindow()->guiFactory()->removeClient(view); +} + +void KateViewManager::setupActions () +{ + KAction *a; + + /** + * view splitting + */ + a = m_mainWindow->actionCollection()->addAction("view_split_vert"); + a->setIcon( KIcon("view-split-left-right") ); + a->setText( i18n("Split Ve&rtical") ); + a->setShortcut( Qt::CTRL + Qt::SHIFT + Qt::Key_L ); + connect(a, SIGNAL(triggered()), this, SLOT(slotSplitViewSpaceVert())); + + a->setWhatsThis(i18n("Split the currently active view vertically into two views.")); + + a = m_mainWindow->actionCollection()->addAction("view_split_horiz"); + a->setIcon( KIcon("view-split-top-bottom") ); + a->setText( i18n("Split &Horizontal") ); + a->setShortcut( Qt::CTRL + Qt::SHIFT + Qt::Key_T ); + connect(a, SIGNAL(triggered()), this, SLOT(slotSplitViewSpaceHoriz())); + + a->setWhatsThis(i18n("Split the currently active view horizontally into two views.")); + + m_closeView = m_mainWindow->actionCollection()->addAction("view_close_current_space"); + m_closeView->setIcon( KIcon("view-close") ); + m_closeView->setText( i18n("Cl&ose Current View") ); + m_closeView->setShortcut( Qt::CTRL + Qt::SHIFT + Qt::Key_R ); + connect(m_closeView, SIGNAL(triggered()), this, SLOT(slotCloseCurrentViewSpace())); + + m_closeView->setWhatsThis(i18n("Close the currently active split view")); + + m_closeOtherViews = m_mainWindow->actionCollection()->addAction("view_close_others"); + m_closeOtherViews->setIcon(KIcon("view-close")); + m_closeOtherViews->setText(i18n("Close Inactive Views")); + connect(m_closeOtherViews, SIGNAL(triggered()), this, SLOT(slotCloseOtherViews())); + + m_closeOtherViews->setWhatsThis(i18n("Close every view but the active one")); + + goNext = m_mainWindow->actionCollection()->addAction( "go_next_split_view" ); + goNext->setText( i18n("Next Split View") ); + goNext->setShortcut( Qt::Key_F8 ); + connect(goNext, SIGNAL(triggered()), this, SLOT(activateNextView())); + + goNext->setWhatsThis(i18n("Make the next split view the active one.")); + + goPrev = m_mainWindow->actionCollection()->addAction( "go_prev_split_view" ); + goPrev->setText( i18n("Previous Split View") ); + goPrev->setShortcut( Qt::SHIFT + Qt::Key_F8 ); + connect(goPrev, SIGNAL(triggered()), this, SLOT(activatePrevView())); + + goPrev->setWhatsThis(i18n("Make the previous split view the active one.")); + + a = m_mainWindow->actionCollection()->addAction("view_split_move_right"); + a->setText( i18n("Move Splitter Right") ); + connect(a, SIGNAL(triggered()), this, SLOT(moveSplitterRight())); + + a->setWhatsThis(i18n("Move the splitter of the current view to the right")); + + a = m_mainWindow->actionCollection()->addAction("view_split_move_left"); + a->setText( i18n("Move Splitter Left") ); + connect(a, SIGNAL(triggered()), this, SLOT(moveSplitterLeft())); + + a->setWhatsThis(i18n("Move the splitter of the current view to the left")); + + a = m_mainWindow->actionCollection()->addAction("view_split_move_up"); + a->setText( i18n("Move Splitter Up") ); + connect(a, SIGNAL(triggered()), this, SLOT(moveSplitterUp())); + + a->setWhatsThis(i18n("Move the splitter of the current view up")); + + a = m_mainWindow->actionCollection()->addAction("view_split_move_down"); + a->setText( i18n("Move Splitter Down") ); + connect(a, SIGNAL(triggered()), this, SLOT(moveSplitterDown())); + + a->setWhatsThis(i18n("Move the splitter of the current view down")); + + /* + * Status Bar Items menu + */ + m_cursorPosToggle = new KToggleAction(i18n("Show Cursor Position"), this); + m_mainWindow->actionCollection()->addAction( "show_cursor_pos", m_cursorPosToggle ); + connect(m_cursorPosToggle, SIGNAL(toggled(bool)), + this, SIGNAL(cursorPositionItemVisibilityChanged(bool))); + + m_charCountToggle = new KToggleAction(i18n("Show Characters Count"), this); + m_mainWindow->actionCollection()->addAction( "show_char_count", m_charCountToggle ); + connect(m_charCountToggle, SIGNAL(toggled(bool)), + this, SIGNAL(charactersCountItemVisibilityChanged(bool))); + + m_insertModeToggle = new KToggleAction(i18n("Show Insertion Mode"), this); + m_mainWindow->actionCollection()->addAction( "show_insert_mode", m_insertModeToggle ); + connect(m_insertModeToggle, SIGNAL(toggled(bool)), + this, SIGNAL(insertModeItemVisibilityChanged(bool))); + + m_selectModeToggle = new KToggleAction(i18n("Show Selection Mode"), this); + m_mainWindow->actionCollection()->addAction( "show_select_mode", m_selectModeToggle ); + connect(m_selectModeToggle, SIGNAL(toggled(bool)), + this, SIGNAL(selectModeItemVisibilityChanged(bool))); + + m_encodingToggle = new KToggleAction(i18n("Show Encoding"), this); + m_mainWindow->actionCollection()->addAction( "show_encoding", m_encodingToggle ); + connect(m_encodingToggle, SIGNAL(toggled(bool)), + this, SIGNAL(encodingItemVisibilityChanged(bool))); + + m_docNameToggle = new KToggleAction(i18n("Show Document Name"), this); + m_mainWindow->actionCollection()->addAction( "show_doc_name", m_docNameToggle ); + connect(m_docNameToggle, SIGNAL(toggled(bool)), + this, SIGNAL(documentNameItemVisibilityChanged(bool))); +} + +void KateViewManager::updateViewSpaceActions () +{ + m_closeView->setEnabled (viewSpaceCount() > 1); + m_closeOtherViews->setEnabled(viewSpaceCount() > 1); + goNext->setEnabled (viewSpaceCount() > 1); + goPrev->setEnabled (viewSpaceCount() > 1); +} + +void KateViewManager::setViewActivationBlocked (bool block) +{ + m_blockViewCreationAndActivation = block; +} + +void KateViewManager::slotDocumentNew () +{ + createView (); +} + +void KateViewManager::slotDocumentOpen () +{ + KTextEditor::View *cv = activeView(); + + if (cv) + { + KEncodingFileDialog::Result r = KEncodingFileDialog::getOpenUrlsAndEncoding( + KateDocManager::self()->editor()->defaultEncoding(), + cv->document()->url().url(), + QString(), m_mainWindow, i18n("Open File")); + + KateDocumentInfo docInfo; + docInfo.openedByUser = true; + + QString fileList; + + foreach ( const KUrl &url, r.URLs ) + { + qint64 size = QFile( url.toLocalFile() ).size(); + + if ( size > FileSizeAboveToAskUserIfProceedWithOpen ) + { + fileList += QString("
  • %1 (%2MB)
  • ").arg( url.fileName() ).arg( size / 1024 / 1024 ); + } + } + + if ( !fileList.isEmpty() ) + { + QString text = i18n( "

    You are attempting to open one or more large files:

      %1

    Do you want to proceed?

    Beware that kate may stop responding for some time when opening large files.

    ", fileList ); + + int ret = KMessageBox::warningYesNo( this, text, i18n("Opening Large File"), KStandardGuiItem::cont(), KStandardGuiItem::stop() ); + if ( ret == KMessageBox::No ) + return; + } + + KTextEditor::Document *lastID = openUrls(r.URLs, r.encoding, false, docInfo); + + if (lastID) + activateView (lastID); + } +} + +void KateViewManager::slotDocumentClose(KTextEditor::Document *document) { +// prevent close document if only one view alive and the document of + // it is not modified and empty !!! + if ( (KateDocManager::self()->documents() == 1) + && !document->isModified() + && document->url().isEmpty() + && document->documentEnd() == KTextEditor::Cursor::start() ) + { + document->closeUrl(); + return; + } + + // close document + KateDocManager::self()->closeDocument (document); +} + + +void KateViewManager::slotDocumentClose () +{ + // no active view, do nothing + if (!activeView()) return; + + slotDocumentClose(activeView()->document()); + + +} + +KTextEditor::Document *KateViewManager::openUrl (const KUrl &url, + const QString& encoding, + bool activate, + bool isTempFile, + const KateDocumentInfo& docInfo) +{ + KTextEditor::Document *doc = KateDocManager::self()->openUrl (url, encoding, isTempFile, docInfo); + + if (!doc->url().isEmpty()) + m_mainWindow->fileOpenRecent->addUrl( doc->url() ); + + if (activate) + activateView( doc ); + + return doc; +} + +KTextEditor::Document *KateViewManager::openUrls (const QList &urls, + const QString& encoding, + bool isTempFile, + const KateDocumentInfo& docInfo) +{ + QList docs = KateDocManager::self()->openUrls(urls, encoding, isTempFile, docInfo); + + foreach (const KTextEditor::Document *doc, docs) { + if (!doc->url().isEmpty()) { + m_mainWindow->fileOpenRecent->addUrl( doc->url() ); + } + } + + return docs.isEmpty() ? 0 : docs.last(); +} + +KTextEditor::View *KateViewManager::openUrlWithView (const KUrl &url, const QString& encoding) +{ + KTextEditor::Document *doc = KateDocManager::self()->openUrl (url, encoding); + + if (!doc) + return 0; + + if (!doc->url().isEmpty()) + m_mainWindow->fileOpenRecent->addUrl( doc->url() ); + + activateView( doc ); + + return activeView (); +} + +void KateViewManager::openUrl (const KUrl &url) +{ + openUrl (url, QString()); +} + +KateMainWindow *KateViewManager::mainWindow() +{ + return m_mainWindow; +} + +bool KateViewManager::isCursorPositionVisible() const +{ + return m_cursorPosToggle->isChecked(); +} + +bool KateViewManager::isCharactersCountVisible() const +{ + return m_charCountToggle->isChecked(); +} + +bool KateViewManager::isInsertModeVisible() const +{ + return m_insertModeToggle->isChecked(); +} + +bool KateViewManager::isSelectModeVisible() const +{ + return m_selectModeToggle->isChecked(); +} + +bool KateViewManager::isEncodingVisible() const +{ + return m_encodingToggle->isChecked(); +} + +bool KateViewManager::isDocumentNameVisible() const +{ + return m_docNameToggle->isChecked(); +} + +// VIEWSPACE + +void KateViewManager::documentCreated (KTextEditor::Document *doc) +{ + // to update open recent files on saving + connect (doc, SIGNAL(documentSavedOrUploaded(KTextEditor::Document*,bool)), this, SLOT(documentSavedOrUploaded(KTextEditor::Document*,bool))); + + if (m_blockViewCreationAndActivation) return; + + if (!activeView()) + activateView (doc); +} + +void KateViewManager::documentDeleted (KTextEditor::Document *) +{ + if (m_blockViewCreationAndActivation) return; + + // just for the case we close a document out of many and this was the active one + // if all docs are closed, this will be handled by the documentCreated + if (!activeView() && (KateDocManager::self()->documents() > 0)) + createView (KateDocManager::self()->document(KateDocManager::self()->documents() - 1)); +} + +void KateViewManager::documentSavedOrUploaded(KTextEditor::Document *doc, bool) +{ + if (!doc->url().isEmpty()) + m_mainWindow->fileOpenRecent->addUrl( doc->url() ); +} + +bool KateViewManager::createView ( KTextEditor::Document *doc ) +{ + if (m_blockViewCreationAndActivation) return false; + + // create doc + if (!doc) + doc = KateDocManager::self()->createDoc (); + + // create view, registers its XML gui itself + KTextEditor::View *view = (KTextEditor::View *) doc->createView (activeViewSpace()->stack); + + m_viewList.append (view); + m_activeStates[view] = false; + + // disable settings dialog action + delete view->actionCollection()->action( "set_confdlg" ); + delete view->actionCollection()->action( "editor_options" ); + + //view->setContextMenu(view->defaultContextMenu()); + + connect(view, SIGNAL(dropEventPass(QDropEvent*)), mainWindow(), SLOT(slotDropEvent(QDropEvent*))); + connect(view, SIGNAL(focusIn(KTextEditor::View*)), this, SLOT(activateSpace(KTextEditor::View*))); + + activeViewSpace()->addView( view ); + + viewCreated(view); + + + activateView( view ); + + return true; +} + +bool KateViewManager::deleteView (KTextEditor::View *view, bool delViewSpace) +{ + if (!view) return true; + + KateViewSpace *viewspace = static_cast(view->parentWidget()->parentWidget()); + + viewspace->removeView (view); + + mainWindow()->guiFactory ()->removeClient (view); + + + // kill LRU mapping + m_lruViews.remove (view); + + // remove view from list and memory !! + m_viewList.removeAt ( m_viewList.indexOf (view) ); + m_activeStates.remove (view); + delete view; + view = 0L; + + if (delViewSpace) + if ( viewspace->viewCount() == 0 ) + removeViewSpace( viewspace ); + + return true; +} + +KateViewSpace* KateViewManager::activeViewSpace () +{ + for ( QList::const_iterator it = m_viewSpaceList.constBegin(); + it != m_viewSpaceList.constEnd(); ++it ) + { + if ( (*it)->isActiveSpace() ) + return *it; + } + + // none active, so use the first we grab + if ( !m_viewSpaceList.isEmpty() ) + { + m_viewSpaceList.first()->setActive( true ); + return m_viewSpaceList.first(); + } + + return 0L; +} + +KTextEditor::View* KateViewManager::activeView () +{ + if (m_activeViewRunning) + return 0L; + + m_activeViewRunning = true; + + for ( QList::const_iterator it = m_viewList.constBegin(); + it != m_viewList.constEnd(); ++it ) + { + if ( m_activeStates[*it] ) + { + m_activeViewRunning = false; + return *it; + } + } + + // if we get to here, no view isActive() + // first, try to get one from activeViewSpace() + KateViewSpace* vs = activeViewSpace(); + if ( vs && vs->currentView() ) + { + activateView (vs->currentView()); + + m_activeViewRunning = false; + return vs->currentView(); + } + + // last attempt: just pick first + if ( !m_viewList.isEmpty() ) + { + activateView (m_viewList.first()); + + m_activeViewRunning = false; + return m_viewList.first(); + } + + m_activeViewRunning = false; + + // no views exists! + return 0L; +} + +void KateViewManager::setActiveSpace ( KateViewSpace* vs ) +{ + if (activeViewSpace()) + activeViewSpace()->setActive( false ); + + vs->setActive( true, viewSpaceCount() > 1 ); +} + +void KateViewManager::setActiveView ( KTextEditor::View* view ) +{ + if (activeView()) + m_activeStates[activeView()] = false; + + m_activeStates[view] = true; +} + +void KateViewManager::activateSpace (KTextEditor::View* v) +{ + if (!v) return; + + KateViewSpace* vs = static_cast(v->parentWidget()->parentWidget()); + + if (!vs->isActiveSpace()) + { + setActiveSpace (vs); + activateView(v); + } +} + +void KateViewManager::reactivateActiveView() +{ + KTextEditor::View *view = activeView(); + if (view) + { + m_activeStates[view] = false; + activateView(view); + } +} + +void KateViewManager::activateView ( KTextEditor::View *view ) +{ + if (!view) return; + + + if (!m_activeStates[view]) + { + if ( !activeViewSpace()->showView(view) ) + { + // since it wasn't found, give'em a new one + createView( view->document() ); + return; + } + + setActiveView (view); + + mainWindow()->setUpdatesEnabled( false ); + bool toolbarVisible = mainWindow()->toolBar()->isVisible(); + if (toolbarVisible) + mainWindow()->toolBar()->hide(); // hide to avoid toolbar flickering + + if (guiMergedView) + mainWindow()->guiFactory()->removeClient( guiMergedView ); + + guiMergedView = view; + + if (!m_blockViewCreationAndActivation) + mainWindow()->guiFactory()->addClient( view ); + + if (toolbarVisible) + mainWindow()->toolBar()->show(); + mainWindow()->setUpdatesEnabled( true ); + + // remember age of this view + m_lruViews[view] = m_minAge--; + + emit viewChanged(); + } +} + +KTextEditor::View *KateViewManager::activateView( KTextEditor::Document *d ) +{ + // no doc with this id found + if (!d) + return activeView (); + + // activate existing view if possible + if ( activeViewSpace()->showView(d) ) + { + activateView( activeViewSpace()->currentView() ); + return activeView (); + } + + // create new view otherwise + createView (d); + return activeView (); +} + +int KateViewManager::viewCount () const +{ + return m_viewList.count(); +} + +int KateViewManager::viewSpaceCount () const +{ + return m_viewSpaceList.count(); +} + +void KateViewManager::slotViewChanged() +{ + if ( activeView() && !activeView()->hasFocus()) + activeView()->setFocus(); +} + +void KateViewManager::activateNextView() +{ + int i = m_viewSpaceList.indexOf (activeViewSpace()) + 1; + + if (i >= m_viewSpaceList.count()) + i = 0; + + setActiveSpace (m_viewSpaceList.at(i)); + activateView(m_viewSpaceList.at(i)->currentView()); +} + +void KateViewManager::activatePrevView() +{ + int i = m_viewSpaceList.indexOf (activeViewSpace()) - 1; + + if (i < 0) + i = m_viewSpaceList.count() - 1; + + setActiveSpace (m_viewSpaceList.at(i)); + activateView(m_viewSpaceList.at(i)->currentView()); +} + +void KateViewManager::closeViews(KTextEditor::Document *doc) +{ + QList closeList; + + for ( QList::const_iterator it = m_viewList.constBegin(); + it != m_viewList.constEnd(); ++it) + { + if ( (*it)->document() == doc ) + closeList.append( *it ); + } + + while ( !closeList.isEmpty() ) + deleteView( closeList.takeFirst(), true ); + + if (m_blockViewCreationAndActivation) return; + QTimer::singleShot(0, this, SIGNAL(viewChanged())); +} + +void KateViewManager::splitViewSpace( KateViewSpace* vs, // = 0 + Qt::Orientation o )// = Qt::Horizontal +{ + // emergency: fallback to activeViewSpace, and if still invalid, abort + if (!vs) vs = activeViewSpace(); + if (!vs) return; + + // get current splitter, and abort if null + QSplitter *currentSplitter = qobject_cast(vs->parentWidget()); + if (!currentSplitter) return; + + // index where to insert new splitter/viewspace + const int index = currentSplitter->indexOf( vs ); + + // create new viewspace + KateViewSpace* vsNew = new KateViewSpace( this ); + + // only 1 children -> we are the root container. So simply set the orientation + // and add the new view space, then correct the sizes to 50%:50% + if (currentSplitter->count() == 1) + { + if( currentSplitter->orientation() != o ) + currentSplitter->setOrientation( o ); + QList sizes = currentSplitter->sizes(); + sizes << (sizes.first() - currentSplitter->handleWidth() ) / 2; + sizes[0] = sizes[1]; + currentSplitter->insertWidget( index, vsNew ); + currentSplitter->setSizes( sizes ); + } + else + { + // create a new QSplitter and replace vs with the splitter. vs and newVS are + // the new children of the new QSplitter + QSplitter* newContainer = new QSplitter( o ); + newContainer->setOpaqueResize( KGlobalSettings::opaqueResize() ); + QList currentSizes = currentSplitter->sizes(); + + newContainer->addWidget( vs ); + newContainer->addWidget( vsNew ); + currentSplitter->insertWidget( index, newContainer ); + newContainer->show(); + + // fix sizes of children of old and new splitter + currentSplitter->setSizes( currentSizes ); + QList newSizes = newContainer->sizes(); + newSizes[0] = (newSizes[0] + newSizes[1] - newContainer->handleWidth()) / 2; + newSizes[1] = newSizes[0]; + newContainer->setSizes( newSizes ); + } + + m_viewSpaceList.append( vsNew ); + activeViewSpace()->setActive( false ); + vsNew->setActive( true, true ); + vsNew->show(); + + createView ((KTextEditor::Document*)activeView()->document()); + + updateViewSpaceActions (); +} + +void KateViewManager::removeViewSpace (KateViewSpace *viewspace) +{ + // abort if viewspace is 0 + if (!viewspace) return; + + // abort if this is the last viewspace + if (m_viewSpaceList.count() < 2) return; + + // get current splitter + QSplitter *currentSplitter = qobject_cast(viewspace->parentWidget()); + + // no splitter found, bah + if (!currentSplitter) + return; + + // delete views of the viewspace + while (viewspace->viewCount() > 0 && viewspace->currentView()) + { + deleteView( viewspace->currentView(), false ); + } + + // cu viewspace + m_viewSpaceList.removeAt( m_viewSpaceList.indexOf( viewspace ) ); + delete viewspace; + + // at this point, the splitter has exactly 1 child + Q_ASSERT( currentSplitter->count() == 1 ); + + // if we are not the root splitter, move the child one level up and delete + // the splitter then. + if (currentSplitter != this) + { + // get parent splitter + QSplitter *parentSplitter = qobject_cast(currentSplitter->parentWidget()); + + // only do magic if found ;) + if (parentSplitter) + { + int index = parentSplitter->indexOf (currentSplitter); + + // save current splitter size, as the removal of currentSplitter looses the info + QList parentSizes = parentSplitter->sizes(); + parentSplitter->insertWidget (index, currentSplitter->widget (0)); + delete currentSplitter; + // now restore the sizes again + parentSplitter->setSizes(parentSizes); + } + } + else if( QSplitter* splitter = qobject_cast(currentSplitter->widget(0)) ) + { + // we are the root splitter and have only one child, which is also a splitter + // -> eliminate the redundant splitter and move both children into the root splitter + QList sizes = splitter->sizes(); + // adapt splitter orientation to the splitter we are about to delete + currentSplitter->setOrientation(splitter->orientation()); + currentSplitter->addWidget( splitter->widget(0) ); + currentSplitter->addWidget( splitter->widget(0) ); + delete splitter; + currentSplitter->setSizes( sizes ); + } + + // find the view that is now active. + KTextEditor::View* v = activeViewSpace()->currentView(); + if ( v ) + activateView( v ); + + updateViewSpaceActions (); + + emit viewChanged(); +} + +void KateViewManager::slotCloseCurrentViewSpace() +{ + removeViewSpace(activeViewSpace()); +} + +void KateViewManager::slotCloseOtherViews() +{ + KateViewSpace* active = activeViewSpace(); + foreach (KateViewSpace *v, m_viewSpaceList) { + if (active != v) { + removeViewSpace(v); + } + } +} + + +/** + * session config functions + */ + +void KateViewManager::saveViewConfiguration(KConfigGroup& config) +{ + // set Active ViewSpace to 0, just in case there is none active (would be + // strange) and config somehow has previous value set + config.writeEntry("Active ViewSpace", 0); + + m_splitterIndex = 0; + saveSplitterConfig(this, config.config(), config.name()); + + // Status Bar Items + config.writeEntry("Cursor Position Visible", m_cursorPosToggle->isChecked()); + config.writeEntry("Characters Count Visible", m_charCountToggle->isChecked()); + config.writeEntry("Insertion Mode Visible", m_insertModeToggle->isChecked()); + config.writeEntry("Selection Mode Visible", m_selectModeToggle->isChecked()); + config.writeEntry("Encoding Visible", m_encodingToggle->isChecked()); + config.writeEntry("Document Name Visible", m_docNameToggle->isChecked()); +} + +void KateViewManager::restoreViewConfiguration (const KConfigGroup& config) +{ + // remove all views and viewspaces + remove their xml gui clients + for (int i = 0; i < m_viewList.count(); ++i) + mainWindow()->guiFactory ()->removeClient (m_viewList.at(i)); + + qDeleteAll( m_viewList ); + m_viewList.clear(); + qDeleteAll( m_viewSpaceList ); + m_viewSpaceList.clear(); + m_activeStates.clear(); + + + // reset lru history, too! + m_lruViews.clear(); + m_minAge = 0; + + // start recursion for the root splitter (Splitter 0) + restoreSplitter( config.config(), config.name() + "-Splitter 0", this, config.name() ); + + // finally, make the correct view from the last session active + int lastViewSpace = config.readEntry("Active ViewSpace", 0); + if( lastViewSpace > m_viewSpaceList.size() ) lastViewSpace = 0; + if( lastViewSpace >= 0 && lastViewSpace < m_viewSpaceList.size()) + { + setActiveSpace( m_viewSpaceList.at( lastViewSpace ) ); + // activate correct view (wish #195435, #188764) + activateView( m_viewSpaceList.at( lastViewSpace )->currentView() ); + // give view the focus to avoid focus stealing by toolviews / plugins + m_viewSpaceList.at( lastViewSpace )->currentView()->setFocus(); + } + + // emergency + if (m_viewSpaceList.empty()) + { + // kill bad children + while (count()) + delete widget (0); + + KateViewSpace* vs = new KateViewSpace( this, 0 ); + addWidget (vs); + vs->setActive( true ); + m_viewSpaceList.append(vs); + } + + // Status Bar Items + bool cursorPosVisible = config.readEntry("Cursor Position Visible", true); + m_cursorPosToggle->setChecked(cursorPosVisible); + emit cursorPositionItemVisibilityChanged(cursorPosVisible); + + bool charCountVisible = config.readEntry("Characters Count Visible", false); + m_charCountToggle->setChecked(charCountVisible); + emit charactersCountItemVisibilityChanged(charCountVisible); + + bool insertModeVisible = config.readEntry("Insertion Mode Visible", true); + m_insertModeToggle->setChecked(insertModeVisible); + emit insertModeItemVisibilityChanged(insertModeVisible); + + bool selectModeVisible = config.readEntry("Selection Mode Visible", true); + m_selectModeToggle->setChecked(selectModeVisible); + emit selectModeItemVisibilityChanged(selectModeVisible); + + bool encodingVisible = config.readEntry("Encoding Visible", true); + m_encodingToggle->setChecked(encodingVisible); + emit encodingItemVisibilityChanged(encodingVisible); + + bool docNameVisible = config.readEntry("Document Name Visible", true); + m_docNameToggle->setChecked(docNameVisible); + emit documentNameItemVisibilityChanged(docNameVisible); + + updateViewSpaceActions(); +} + +void KateViewManager::saveSplitterConfig( QSplitter* s, KConfigBase* configBase, const QString& viewConfGrp ) +{ + QString grp = QString(viewConfGrp + "-Splitter %1").arg(m_splitterIndex); + KConfigGroup config( configBase, grp ); + + // Save sizes, orient, children for this splitter + config.writeEntry( "Sizes", s->sizes() ); + config.writeEntry( "Orientation", int(s->orientation()) ); + + QStringList childList; + // a QSplitter has two children, either QSplitters and/or KateViewSpaces + // special case: root splitter might have only one child (just for info) + for (int it = 0; it < s->count(); ++it) + { + QWidget *obj = s->widget(it); + KateViewSpace* kvs = qobject_cast(obj); + + QString n; // name for child list, see below + // For KateViewSpaces, ask them to save the file list. + if ( kvs ) + { + n = QString(viewConfGrp + "-ViewSpace %1").arg( m_viewSpaceList.indexOf(kvs) ); + kvs->saveConfig ( configBase, m_viewSpaceList.indexOf(kvs), viewConfGrp); + // save active viewspace + if ( kvs->isActiveSpace() ) + { + KConfigGroup viewConfGroup(configBase, viewConfGrp); + viewConfGroup.writeEntry("Active ViewSpace", m_viewSpaceList.indexOf(kvs) ); + } + } + // for QSplitters, recurse + else if ( QSplitter* splitter = qobject_cast(obj) ) + { + ++m_splitterIndex; + n = QString(viewConfGrp + "-Splitter %1").arg( m_splitterIndex ); + saveSplitterConfig( splitter, configBase, viewConfGrp); + } + + childList.append( n ); + } + + config.writeEntry("Children", childList); +} + +void KateViewManager::restoreSplitter( const KConfigBase* configBase, const QString &group, + QSplitter* parent, const QString& viewConfGrp) +{ + KConfigGroup config( configBase, group ); + + parent->setOrientation((Qt::Orientation)config.readEntry("Orientation", int(Qt::Horizontal))); + + QStringList children = config.readEntry( "Children", QStringList() ); + for (QStringList::Iterator it = children.begin(); it != children.end(); ++it) + { + // for a viewspace, create it and open all documents therein. + if ( (*it).startsWith(viewConfGrp + "-ViewSpace") ) + { + KateViewSpace* vs = new KateViewSpace( this, 0 ); + m_viewSpaceList.append( vs ); + // make active so that the view created in restoreConfig has this + // new view space as parent. + setActiveSpace( vs ); + + parent->addWidget( vs ); + vs->restoreConfig (this, configBase, *it); + vs->show(); + } + else + { + // for a splitter, recurse. + restoreSplitter( configBase, *it, new QSplitter( parent ), viewConfGrp ); + } + } + + // set sizes + parent->setSizes( config.readEntry("Sizes", QList()) ); + parent->show(); +} + +void KateViewManager::moveSplitter(Qt::Key key, int repeats) +{ + if (repeats < 1) return; + + KateViewSpace *vs = activeViewSpace(); + if (!vs) return; + + QSplitter *currentSplitter = qobject_cast(vs->parentWidget()); + + if (!currentSplitter) return; + if (currentSplitter->count() == 1) return; + + int move = 4 * repeats; + // try to use font height in pixel to move splitter + KTextEditor::HighlightInterface *hi = qobject_cast(vs->currentView()->document()); + if (hi) { + KTextEditor::Attribute::Ptr attrib(hi->defaultStyle(KTextEditor::HighlightInterface::dsNormal)); + QFontMetrics fm(attrib->font()); + move = fm.height() * repeats; + } + + QWidget* currentWidget = (QWidget*)vs; + bool foundSplitter = false; + + // find correct splitter to be moved + while ( currentSplitter && currentSplitter->count() != 1 ) { + + if ( currentSplitter->orientation() == Qt::Horizontal + && ( key == Qt::Key_Right || key == Qt::Key_Left )) + foundSplitter = true; + + if ( currentSplitter->orientation() == Qt::Vertical + && ( key == Qt::Key_Up || key == Qt::Key_Down )) + foundSplitter = true; + + // if the views within the current splitter can be resized, resize them + if (foundSplitter) { + QList currentSizes = currentSplitter->sizes(); + int index = currentSplitter->indexOf(currentWidget); + + if (( index == 0 && ( key == Qt::Key_Left || key == Qt::Key_Up )) + ||( index == 1 && ( key == Qt::Key_Right || key == Qt::Key_Down ))) + currentSizes[index] -= move; + + if (( index == 0 && ( key == Qt::Key_Right || key == Qt::Key_Down )) + ||( index == 1 && ( key == Qt::Key_Left || key == Qt::Key_Up ))) + currentSizes[index] += move; + + if ( index == 0 && ( key == Qt::Key_Right || key == Qt::Key_Down )) + currentSizes[index+1] -= move; + + if ( index == 0 && ( key == Qt::Key_Left || key == Qt::Key_Up )) + currentSizes[index+1] += move; + + if( index == 1 && ( key == Qt::Key_Right || key == Qt::Key_Down )) + currentSizes[index-1] += move; + + if( index == 1 && ( key == Qt::Key_Left || key == Qt::Key_Up )) + currentSizes[index-1] -= move; + + currentSplitter->setSizes(currentSizes); + break; + } + + currentWidget = (QWidget*)currentSplitter; + // the parent of the current splitter will become the current splitter + currentSplitter = qobject_cast(currentSplitter->parentWidget()); + } +} + + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/src/app/kateviewmanager.h b/kate/src/app/kateviewmanager.h new file mode 100644 index 00000000..a9a0b4bf --- /dev/null +++ b/kate/src/app/kateviewmanager.h @@ -0,0 +1,307 @@ +/* This file is part of the KDE project + Copyright (C) 2001 Christoph Cullmann + Copyright (C) 2001 Joseph Wenninger + Copyright (C) 2001 Anders Lund + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef __KATE_VIEWMANAGER_H__ +#define __KATE_VIEWMANAGER_H__ + +#include "katemain.h" +#include "katedocmanager.h" + + +#include +#include + +#include +#include +#include +#include + +#include + + +class KateDocumentInfo; + +class KConfigGroup; +class KConfigBase; +class KateMainWindow; +class KateViewSpace; +class KAction; + +#include + +class KateViewManager : public QSplitter +{ + Q_OBJECT + + friend class KateViewSpace; + friend class KateVSStatusBar; + + public: + KateViewManager (QWidget *parentW, KateMainWindow *parent); + ~KateViewManager (); + + void updateViewSpaceActions (); + + private: + /** + * create all actions needed for the view manager + */ + void setupActions (); + + public: + /* This will save the splitter configuration */ + void saveViewConfiguration(KConfigGroup& group); + + /* restore it */ + void restoreViewConfiguration (const KConfigGroup& group); + + KTextEditor::Document *openUrl (const KUrl &url, + const QString& encoding, + bool activate = true, + bool isTempFile = false, + const KateDocumentInfo& docInfo = KateDocumentInfo()); + + KTextEditor::Document *openUrls (const QList &url, + const QString& encoding, + bool isTempFile = false, + const KateDocumentInfo& docInfo = KateDocumentInfo()); + + KTextEditor::View *openUrlWithView (const KUrl &url, const QString& encoding); + + public Q_SLOTS: + void openUrl (const KUrl &url); + + public: + + void setViewActivationBlocked (bool block); + + + public: + void closeViews(KTextEditor::Document *doc); + KateMainWindow *mainWindow(); + + bool isCursorPositionVisible() const; + bool isCharactersCountVisible() const; + bool isInsertModeVisible() const; + bool isSelectModeVisible() const; + bool isEncodingVisible() const; + bool isDocumentNameVisible() const; + + private Q_SLOTS: + void activateView ( KTextEditor::View *view ); + void activateSpace ( KTextEditor::View* v ); + + public Q_SLOTS: + void slotDocumentNew (); + void slotDocumentOpen (); + void slotDocumentClose (); + void slotDocumentClose (KTextEditor::Document *document); + + void setActiveSpace ( KateViewSpace* vs ); + void setActiveView ( KTextEditor::View* view ); + + void activateNextView(); + void activatePrevView(); + + protected: + QPointer guiMergedView; + + Q_SIGNALS: + void statChanged (); + void viewChanged (); + void viewCreated (KTextEditor::View *); + + void cursorPositionItemVisibilityChanged(bool); + void charactersCountItemVisibilityChanged(bool); + void insertModeItemVisibilityChanged(bool); + void selectModeItemVisibilityChanged(bool); + void encodingItemVisibilityChanged(bool); + void documentNameItemVisibilityChanged(bool); + + public: + inline QList &viewList () + { + return m_viewList; + } + + private: + /** + * create and activate a new view for doc, if doc == 0, then + * create a new document + */ + bool createView ( KTextEditor::Document *doc = 0L ); + + bool deleteView ( KTextEditor::View *view, bool delViewSpace = true); + + void moveViewtoSplit (KTextEditor::View *view); + void moveViewtoStack (KTextEditor::View *view); + + /* Save the configuration of a single splitter. + * If child splitters are found, it calls it self with those as the argument. + * If a viewspace child is found, it is asked to save its filelist. + */ + void saveSplitterConfig(QSplitter* s, KConfigBase* config, const QString& viewConfGrp); + + /** Restore a single splitter. + * This is all the work is done for @see saveSplitterConfig() + */ + void restoreSplitter ( const KConfigBase* config, const QString &group, QSplitter* parent, const QString& viewConfGrp); + + void removeViewSpace (KateViewSpace *viewspace); + + public: + KTextEditor::View* activeView (); + KateViewSpace* activeViewSpace (); + + int viewCount () const; + int viewSpaceCount () const; + + bool isViewActivationBlocked() + { + return m_blockViewCreationAndActivation; + } + + private Q_SLOTS: + void slotViewChanged(); + + void documentCreated (KTextEditor::Document *doc); + void documentDeleted (KTextEditor::Document *doc); + + void documentSavedOrUploaded(KTextEditor::Document* document,bool saveAs); + + public Q_SLOTS: + /** + * Splits a KateViewSpace into two in the following steps: + * 1. create a QSplitter in the parent of the KateViewSpace to be split + * 2. move the to-be-split KateViewSpace to the new splitter + * 3. create new KateViewSpace and added to the new splitter + * 4. create KateView to populate the new viewspace. + * 5. The new KateView is made the active one, because createView() does that. + * If no viewspace is provided, the result of activeViewSpace() is used. + * The orientation of the new splitter is determined by the value of o. + * Note: horizontal splitter means vertically aligned views. + */ + void splitViewSpace( KateViewSpace* vs = 0L, Qt::Orientation o = Qt::Horizontal ); + + /** + * activate view for given document + * @param doc document to activate view for + */ + KTextEditor::View *activateView ( KTextEditor::Document *doc ); + + /** Splits the active viewspace horizontally */ + void slotSplitViewSpaceHoriz () + { + splitViewSpace(0L, Qt::Vertical); + } + + /** Splits the active viewspace vertically */ + void slotSplitViewSpaceVert () + { + splitViewSpace(); + } + + /** moves the splitter according to the key that has been pressed */ + void moveSplitter(Qt::Key key, int repeats = 1); + + /** moves the splitter to the right */ + void moveSplitterRight() + { + moveSplitter(Qt::Key_Right); + } + + /** moves the splitter to the left */ + void moveSplitterLeft() + { + moveSplitter(Qt::Key_Left); + } + + /** moves the splitter up */ + void moveSplitterUp() + { + moveSplitter(Qt::Key_Up); + } + + /** moves the splitter down */ + void moveSplitterDown() + { + moveSplitter(Qt::Key_Down); + } + + void slotCloseCurrentViewSpace(); + + /** closes every view but the active one */ + void slotCloseOtherViews(); + + void reactivateActiveView(); + + /** + * get views => age mapping + * useful to show views in a LRU way + * important: smallest age ==> latest used view + */ + const QHash &lruViews () const + { + return m_lruViews; + } + + private: + KateMainWindow *m_mainWindow; + bool m_init; + + KAction *m_closeView; + KAction *m_closeOtherViews; + KAction *goNext; + KAction *goPrev; + KAction *m_cursorPosToggle; + KAction *m_charCountToggle; + KAction *m_insertModeToggle; + KAction *m_selectModeToggle; + KAction *m_encodingToggle; + KAction *m_docNameToggle; + + QList m_viewSpaceList; + QList m_viewList; + QHash m_activeStates; + + + bool m_blockViewCreationAndActivation; + + bool m_activeViewRunning; + + int m_splitterIndex; // used during saving splitter config. + + /** + * history of view activations + * map view => number, the lower, the more recent activated + */ + QHash m_lruViews; + + /** + * current minimal age + */ + qint64 m_minAge; + + friend class KateContainer; +}; + +#endif +// kate: space-indent on; indent-width 2; replace-tabs on; + diff --git a/kate/src/app/kateviewspace.cpp b/kate/src/app/kateviewspace.cpp new file mode 100644 index 00000000..ebf5f3ff --- /dev/null +++ b/kate/src/app/kateviewspace.cpp @@ -0,0 +1,498 @@ +/* This file is part of the KDE project + Copyright (C) 2001 Christoph Cullmann + Copyright (C) 2001 Joseph Wenninger + Copyright (C) 2001, 2005 Anders Lund + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +#include "kateviewspace.h" + +#include +#include "moc_kateviewspace.cpp" + +#include "katemainwindow.h" +#include "kateviewmanager.h" +#include "katedocmanager.h" +#include "kateapp.h" +#include "katesession.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +//BEGIN KateViewSpace +KateViewSpace::KateViewSpace( KateViewManager *viewManager, + QWidget* parent, const char* name ) + : KVBox(parent), + m_viewManager( viewManager ) +{ + setObjectName(name); + + stack = new QStackedWidget( this ); + stack->setFocus(); + stack->setSizePolicy (QSizePolicy (QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding)); + + mStatusBar = new KateVSStatusBar(this); + mIsActiveSpace = false; + + setMinimumWidth (mStatusBar->minimumWidth()); + m_group.clear(); + + // connect signal to hide/show statusbar + connect (m_viewManager->mainWindow(), SIGNAL(statusBarToggled()), this, SLOT(statusBarToggled())); + + connect (m_viewManager, SIGNAL(cursorPositionItemVisibilityChanged(bool)), + mStatusBar, SLOT(cursorPositionItemVisibilityChanged(bool))); + connect (m_viewManager, SIGNAL(charactersCountItemVisibilityChanged(bool)), + mStatusBar, SLOT(charactersCountItemVisibilityChanged(bool))); + connect (m_viewManager, SIGNAL(insertModeItemVisibilityChanged(bool)), + mStatusBar, SLOT(insertModeItemVisibilityChanged(bool))); + connect (m_viewManager, SIGNAL(selectModeItemVisibilityChanged(bool)), + mStatusBar, SLOT(selectModeItemVisibilityChanged(bool))); + connect (m_viewManager, SIGNAL(encodingItemVisibilityChanged(bool)), + mStatusBar, SLOT(encodingItemVisibilityChanged(bool))); + connect (m_viewManager, SIGNAL(documentNameItemVisibilityChanged(bool)), + mStatusBar, SLOT(documentNameItemVisibilityChanged(bool))); + + // init the visibility of the statusbar items + mStatusBar->cursorPositionItemVisibilityChanged(m_viewManager->isCursorPositionVisible()); + mStatusBar->charactersCountItemVisibilityChanged(m_viewManager->isCharactersCountVisible()); + mStatusBar->insertModeItemVisibilityChanged(m_viewManager->isInsertModeVisible()); + mStatusBar->selectModeItemVisibilityChanged(m_viewManager->isSelectModeVisible()); + mStatusBar->encodingItemVisibilityChanged(m_viewManager->isEncodingVisible()); + mStatusBar->documentNameItemVisibilityChanged(m_viewManager->isDocumentNameVisible()); + + // init the statusbar... + statusBarToggled (); +} + +KateViewSpace::~KateViewSpace() +{} + +void KateViewSpace::statusBarToggled () +{ + // show or hide the bar? + if (m_viewManager->mainWindow()->showStatusBar()) + mStatusBar->show (); + else + mStatusBar->hide (); +} + +void KateViewSpace::addView(KTextEditor::View* v, bool show) +{ + // restore the config of this view if possible + if ( !m_group.isEmpty() ) + { + QString fn = v->document()->url().prettyUrl(); + if ( ! fn.isEmpty() ) + { + QString vgroup = QString("%1 %2").arg(m_group).arg(fn); + + KateSession::Ptr as = KateSessionManager::self()->activeSession (); + if ( as->configRead() && as->configRead()->hasGroup( vgroup ) ) + { + KConfigGroup cg( as->configRead(), vgroup ); + + if (KTextEditor::SessionConfigInterface *iface = qobject_cast(v)) + iface->readSessionConfig ( cg ); + } + } + } + + stack->addWidget(v); + if (show) + { + mViewList.append(v); + showView( v ); + } + else + { + KTextEditor::View* c = (KTextEditor::View*)stack->currentWidget(); + mViewList.prepend( v ); + showView( c ); + } + + // signals for the statusbar + connect(v, SIGNAL(cursorPositionChanged(KTextEditor::View*,KTextEditor::Cursor)), mStatusBar, SLOT(cursorPositionChanged(KTextEditor::View*))); + connect(v, SIGNAL(viewModeChanged(KTextEditor::View*)), mStatusBar, SLOT(viewModeChanged(KTextEditor::View*))); + connect(v, SIGNAL(selectionChanged(KTextEditor::View*)), mStatusBar, SLOT(selectionChanged(KTextEditor::View*))); + connect(v, SIGNAL(informationMessage(KTextEditor::View*,QString)), mStatusBar, SLOT(informationMessage(KTextEditor::View*,QString))); + connect(v->document(), SIGNAL(modifiedChanged(KTextEditor::Document*)), mStatusBar, SLOT(modifiedChanged())); + connect(v->document(), SIGNAL(modifiedOnDisk(KTextEditor::Document*,bool,KTextEditor::ModificationInterface::ModifiedOnDiskReason)), mStatusBar, SLOT(modifiedChanged()) ); + connect(v->document(), SIGNAL(documentNameChanged(KTextEditor::Document*)), mStatusBar, SLOT(documentNameChanged())); + connect(v->document(), SIGNAL(configChanged()), mStatusBar, SLOT(documentConfigChanged())); +} + +void KateViewSpace::removeView(KTextEditor::View* v) +{ + bool active = ( v == currentView() ); + + mViewList.removeAt ( mViewList.indexOf ( v ) ); + stack->removeWidget (v); + + if ( ! active ) + return; + + // the last recently used viewspace is always at the end of the list + if (!mViewList.isEmpty()) + showView(mViewList.last()); +} + +bool KateViewSpace::showView(KTextEditor::Document *document) +{ + QList::const_iterator it = mViewList.constEnd(); + while( it != mViewList.constBegin() ) + { + --it; + if ((*it)->document() == document) + { + KTextEditor::View* kv = *it; + + // move view to end of list + mViewList.removeAt( mViewList.indexOf(kv) ); + mViewList.append( kv ); + stack->setCurrentWidget( kv ); + kv->show(); + + mStatusBar->updateStatus (); + + return true; + } + } + return false; +} + + +KTextEditor::View* KateViewSpace::currentView() +{ + // stack->currentWidget() returns NULL, if stack.count() == 0, + // i.e. if mViewList.isEmpty() + return (KTextEditor::View*)stack->currentWidget(); +} + +bool KateViewSpace::isActiveSpace() +{ + return mIsActiveSpace; +} + +void KateViewSpace::setActive( bool active, bool ) +{ + mIsActiveSpace = active; + + // change the statusbar palette according to the activation state + mStatusBar->setEnabled(active); +} + +void KateViewSpace::saveConfig ( KConfigBase* config, int myIndex , const QString& viewConfGrp) +{ +// kDebug()<<"KateViewSpace::saveConfig("<document()->url().prettyUrl() ); + + // Save file list, including cursor position in this instance. + int idx = 0; + for (QList::iterator it = mViewList.begin(); + it != mViewList.end(); ++it) + { + if ( !(*it)->document()->url().isEmpty() ) + { + group.writeEntry( QString("View %1").arg( idx ), (*it)->document()->url().prettyUrl() ); + + // view config, group: "ViewSpace url" + QString vgroup = QString("%1 %2").arg(groupname).arg((*it)->document()->url().prettyUrl()); + KConfigGroup viewGroup( config, vgroup ); + + if (KTextEditor::SessionConfigInterface *iface = qobject_cast(*it)) + iface->writeSessionConfig( viewGroup ); + } + + ++idx; + } +} + +void KateViewSpace::restoreConfig ( KateViewManager *viewMan, const KConfigBase* config, const QString &groupname ) +{ + KConfigGroup group (config, groupname); + QString fn = group.readEntry( "Active View" ); + + if ( !fn.isEmpty() ) + { + KTextEditor::Document *doc = KateDocManager::self()->findDocument (KUrl(fn)); + + if (doc) + { + // view config, group: "ViewSpace url" + QString vgroup = QString("%1 %2").arg(groupname).arg(fn); + KConfigGroup configGroup( config, vgroup ); + + viewMan->createView (doc); + + KTextEditor::View *v = viewMan->activeView (); + + if (KTextEditor::SessionConfigInterface *iface = qobject_cast(v)) + iface->readSessionConfig( configGroup ); + } + } + + if (mViewList.isEmpty()) + viewMan->createView (KateDocManager::self()->document(0)); + + m_group = groupname; // used for restroing view configs later +} +//END KateViewSpace + +//BEGIN KateVSStatusBar +KateVSStatusBar::KateVSStatusBar ( KateViewSpace *parent) + : KStatusBar( parent), + m_viewSpace( parent ) +{ + QString lineColText = i18n(" Line: %1 Col: %2 ", KGlobal::locale()->formatNumber(4444, 0), + KGlobal::locale()->formatNumber(44, 0)); + + m_lineColLabel = new QLabel( this ); + m_lineColLabel->setMinimumWidth( m_lineColLabel->fontMetrics().width( lineColText ) ); + addWidget( m_lineColLabel, 0 ); + m_lineColLabel->installEventFilter( this ); + + QString charsText = i18n(" Characters: %1 ", KGlobal::locale()->formatNumber(4444, 0)); + + m_charsLabel = new QLabel( this ); + m_charsLabel->setMinimumWidth( m_charsLabel->fontMetrics().width( charsText ) ); + addWidget( m_charsLabel, 0 ); + m_charsLabel->installEventFilter( this ); + + m_modifiedLabel = new QLabel( this ); + m_modifiedLabel->setFixedSize( 16, 16 ); + addWidget( m_modifiedLabel, 0 ); + m_modifiedLabel->setAlignment( Qt::AlignCenter ); + m_modifiedLabel->installEventFilter( this ); + + m_selectModeLabel = new QLabel( i18n(" LINE "), this ); + addWidget( m_selectModeLabel, 0 ); + m_selectModeLabel->setAlignment( Qt::AlignCenter ); + m_selectModeLabel->installEventFilter( this ); + + m_insertModeLabel = new QLabel( i18n(" INS "), this ); + addWidget( m_insertModeLabel, 0 ); + m_insertModeLabel->setAlignment( Qt::AlignVCenter | Qt::AlignLeft ); + m_insertModeLabel->installEventFilter( this ); + + m_fileNameLabel = new KSqueezedTextLabel( this ); + addPermanentWidget( m_fileNameLabel, 1 ); + m_fileNameLabel->setTextFormat(Qt::PlainText); + m_fileNameLabel->setMinimumSize( 0, 0 ); + m_fileNameLabel->setSizePolicy(QSizePolicy( QSizePolicy::Ignored, QSizePolicy::Fixed )); + m_fileNameLabel->setAlignment(Qt::AlignVCenter | Qt::AlignRight); + m_fileNameLabel->installEventFilter( this ); + + m_encodingLabel = new QLabel( "", this ); + addPermanentWidget( m_encodingLabel, 0 ); + m_encodingLabel->setAlignment( Qt::AlignCenter ); + m_encodingLabel->installEventFilter( this ); + + + installEventFilter( this ); + m_modPm = KIcon("document-save").pixmap(16); + m_modDiscPm = KIcon("dialog-warning").pixmap(16); + m_modmodPm = KIcon("document-save", 0, QStringList () << "emblem-important").pixmap(16); +} + +KateVSStatusBar::~KateVSStatusBar () +{} + +void KateVSStatusBar::showMenu() +{ + KXmlGuiWindow* mainWindow = static_cast( window() ); + QMenu* menu = static_cast( mainWindow->factory()->container("viewspace_popup", mainWindow ) ); + + if (menu) + menu->exec(QCursor::pos()); +} + +bool KateVSStatusBar::eventFilter(QObject*, QEvent *e) +{ + if (e->type() == QEvent::MouseButtonPress) + { + if ( m_viewSpace->currentView() ) + m_viewSpace->currentView()->setFocus(); + + if ( ((QMouseEvent*)e)->button() == Qt::RightButton) + showMenu(); + + return true; + } + + return false; +} + +void KateVSStatusBar::updateStatus () +{ + if (!m_viewSpace->currentView()) + return; + + KTextEditor::View* view = m_viewSpace->currentView(); + viewModeChanged (view); + cursorPositionChanged (view); + selectionChanged (view); + modifiedChanged (); + documentNameChanged (); + documentConfigChanged (); +} + +void KateVSStatusBar::viewModeChanged ( KTextEditor::View *view ) +{ + if (view != m_viewSpace->currentView()) + return; + + m_insertModeLabel->setText( QString (" %1 ").arg (view->viewMode()) ); +} + +void KateVSStatusBar::cursorPositionChanged ( KTextEditor::View *view ) +{ + if (view != m_viewSpace->currentView()) + return; + + KTextEditor::Cursor position (view->cursorPositionVirtual()); + + m_lineColLabel->setText( + i18n( + " Line: %1 of %2 Col: %3 " + , KGlobal::locale()->formatNumber(position.line() + 1, 0) + , view->document()->lines() + , KGlobal::locale()->formatNumber(position.column() + 1, 0) + ) + ); + + if (!m_charsLabel->isHidden()) + { + m_charsLabel->setText( + i18n(" Characters: %1 ", KGlobal::locale()->formatNumber(view->document()->totalCharacters(), 0))); + } +} + +void KateVSStatusBar::selectionChanged (KTextEditor::View *view) +{ + if (view != m_viewSpace->currentView()) + return; + + m_selectModeLabel->setText( view->blockSelection() ? i18n(" BLOCK ") : i18n(" LINE ") ); +} + +void KateVSStatusBar::informationMessage (KTextEditor::View *view, const QString &message) +{ + if (view != m_viewSpace->currentView()) + return; + + m_fileNameLabel->setText( message ); + + // timer to reset this after 4 seconds + QTimer::singleShot(4000, this, SLOT(documentNameChanged())); +} + +void KateVSStatusBar::modifiedChanged() +{ + KTextEditor::View *v = m_viewSpace->currentView(); + + if ( v ) + { + bool mod = v->document()->isModified(); + + const KateDocumentInfo *info + = KateDocManager::self()->documentInfo ( v->document() ); + + bool modOnHD = info && info->modifiedOnDisc; + + m_modifiedLabel->setPixmap( + mod ? + info && modOnHD ? + m_modmodPm : + m_modPm : + info && modOnHD ? + m_modDiscPm : + QPixmap() + ); + } +} + +void KateVSStatusBar::documentNameChanged () +{ + KTextEditor::View *v = m_viewSpace->currentView(); + + if ( v ) + m_fileNameLabel->setText( KStringHandler::lsqueeze(v->document()->documentName (), 64) ); +} + +void KateVSStatusBar::documentConfigChanged () +{ + KTextEditor::View *v = m_viewSpace->currentView(); + + if ( v ) + m_encodingLabel->setText( QString (" %1 ").arg (v->document()->encoding()) ); +} + +void KateVSStatusBar::cursorPositionItemVisibilityChanged(bool visible) +{ + m_lineColLabel->setVisible(visible); +} + +void KateVSStatusBar::charactersCountItemVisibilityChanged(bool visible) +{ + m_charsLabel->setVisible(visible); + + if (visible) + { + updateStatus(); + } +} + +void KateVSStatusBar::insertModeItemVisibilityChanged(bool visible) +{ + m_insertModeLabel->setVisible(visible); +} + +void KateVSStatusBar::selectModeItemVisibilityChanged(bool visible) +{ + m_selectModeLabel->setVisible(visible); +} + +void KateVSStatusBar::encodingItemVisibilityChanged(bool visible) +{ + m_encodingLabel->setVisible(visible); +} + +void KateVSStatusBar::documentNameItemVisibilityChanged(bool visible) +{ + m_fileNameLabel->setVisible(visible); +} + +//END KateVSStatusBar + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/src/app/kateviewspace.h b/kate/src/app/kateviewspace.h new file mode 100644 index 00000000..2c3f88a8 --- /dev/null +++ b/kate/src/app/kateviewspace.h @@ -0,0 +1,142 @@ +/* This file is part of the KDE project + Copyright (C) 2001 Christoph Cullmann + Copyright (C) 2001 Joseph Wenninger + Copyright (C) 2001 Anders Lund + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KATE_VIEWSPACE_H +#define KATE_VIEWSPACE_H + +#include "katemain.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +class KConfigBase; +class KSqueezedTextLabel; +class KateViewManager; +class KateViewSpace; +#include + +class KateVSStatusBar : public KStatusBar +{ + Q_OBJECT + + public: + KateVSStatusBar ( KateViewSpace *parent = 0L); + virtual ~KateVSStatusBar (); + + /** + * stuff to update the statusbar on view changes + */ + public Q_SLOTS: + void updateStatus (); + + void viewModeChanged ( KTextEditor::View *view ); + + void cursorPositionChanged ( KTextEditor::View *view ); + + void selectionChanged (KTextEditor::View *view); + + void modifiedChanged(); + + void documentNameChanged (); + + void documentConfigChanged (); + + void informationMessage (KTextEditor::View *view, const QString &message); + + void cursorPositionItemVisibilityChanged(bool visible); + void charactersCountItemVisibilityChanged(bool visible); + void insertModeItemVisibilityChanged(bool visible); + void selectModeItemVisibilityChanged(bool visible); + void encodingItemVisibilityChanged(bool visible); + void documentNameItemVisibilityChanged(bool visible); + + protected: + virtual bool eventFilter (QObject*, QEvent *); + virtual void showMenu (); + + private: + QLabel* m_lineColLabel; + QLabel* m_charsLabel; + QLabel* m_modifiedLabel; + QLabel* m_insertModeLabel; + QLabel* m_selectModeLabel; + QLabel* m_encodingLabel; + KSqueezedTextLabel* m_fileNameLabel; + QPixmap m_modPm, m_modDiscPm, m_modmodPm; + class KateViewSpace *m_viewSpace; +}; + +class KateViewSpace : public KVBox +{ + friend class KateViewManager; + friend class KateVSStatusBar; + + Q_OBJECT + + public: + explicit KateViewSpace(KateViewManager *, QWidget* parent = 0, const char* name = 0); + ~KateViewSpace(); + bool isActiveSpace(); + void setActive(bool b, bool showled = false); + QStackedWidget* stack; + void addView(KTextEditor::View* v, bool show = true); + void removeView(KTextEditor::View* v); + + bool showView(KTextEditor::View *view) + { + return showView(view->document()); + } + bool showView(KTextEditor::Document *document); + + KTextEditor::View* currentView(); + int viewCount() const + { + return mViewList.count(); + } + + void saveConfig (KConfigBase* config, int myIndex, const QString& viewConfGrp); + void restoreConfig ( KateViewManager *viewMan, const KConfigBase* config, const QString &group ); + + private Q_SLOTS: + void statusBarToggled (); + + private: + bool mIsActiveSpace; + KateVSStatusBar* mStatusBar; + /// This list is necessary to only save the order of the accessed views. + /// The order is important. The least recently viewed view is always the + /// last entry in the list, i.e. mViewList.last() + /// mViewList.count() == stack.count() is always true! + QList mViewList; + KateViewManager *m_viewManager; + QString m_group; +}; + +#endif +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/src/data/CMakeLists.txt b/kate/src/data/CMakeLists.txt new file mode 100644 index 00000000..8aed5fb9 --- /dev/null +++ b/kate/src/data/CMakeLists.txt @@ -0,0 +1,25 @@ +# desktop files and co +install( + PROGRAMS kate.desktop + DESTINATION ${KDE4_XDG_APPS_INSTALL_DIR} +) + +install( + FILES katerc + DESTINATION ${KDE4_CONFIG_INSTALL_DIR} +) + +install( + FILES + tips + kateui.rc + DESTINATION ${KDE4_DATA_INSTALL_DIR}/kate +) + +install( + FILES kateplugin.desktop + DESTINATION ${KDE4_SERVICETYPES_INSTALL_DIR} +) + +# kate application icons +add_subdirectory(icons) diff --git a/kate/src/data/icons/CMakeLists.txt b/kate/src/data/icons/CMakeLists.txt new file mode 100644 index 00000000..50bf6c73 --- /dev/null +++ b/kate/src/data/icons/CMakeLists.txt @@ -0,0 +1,2 @@ +########### install files ############### +kde4_install_icons(${KDE4_ICON_INSTALL_DIR}) diff --git a/kate/src/data/icons/hi128-app-kate.png b/kate/src/data/icons/hi128-app-kate.png new file mode 100644 index 00000000..454cf5ac Binary files /dev/null and b/kate/src/data/icons/hi128-app-kate.png differ diff --git a/kate/src/data/icons/hi16-app-kate.png b/kate/src/data/icons/hi16-app-kate.png new file mode 100644 index 00000000..749cf30c Binary files /dev/null and b/kate/src/data/icons/hi16-app-kate.png differ diff --git a/kate/src/data/icons/hi22-app-kate.png b/kate/src/data/icons/hi22-app-kate.png new file mode 100644 index 00000000..18ebac24 Binary files /dev/null and b/kate/src/data/icons/hi22-app-kate.png differ diff --git a/kate/src/data/icons/hi32-app-kate.png b/kate/src/data/icons/hi32-app-kate.png new file mode 100644 index 00000000..c86fd027 Binary files /dev/null and b/kate/src/data/icons/hi32-app-kate.png differ diff --git a/kate/src/data/icons/hi48-app-kate.png b/kate/src/data/icons/hi48-app-kate.png new file mode 100644 index 00000000..28ef540f Binary files /dev/null and b/kate/src/data/icons/hi48-app-kate.png differ diff --git a/kate/src/data/icons/hi64-app-kate.png b/kate/src/data/icons/hi64-app-kate.png new file mode 100644 index 00000000..8d11b627 Binary files /dev/null and b/kate/src/data/icons/hi64-app-kate.png differ diff --git a/kate/src/data/icons/hisc-app-kate.svgz b/kate/src/data/icons/hisc-app-kate.svgz new file mode 100644 index 00000000..6f0dbb63 Binary files /dev/null and b/kate/src/data/icons/hisc-app-kate.svgz differ diff --git a/kate/src/data/kate.desktop b/kate/src/data/kate.desktop new file mode 100755 index 00000000..f6b43532 --- /dev/null +++ b/kate/src/data/kate.desktop @@ -0,0 +1,131 @@ +[Desktop Entry] +GenericName=Advanced Text Editor +GenericName[ar]=محرّر نصوص متقدّم +GenericName[ast]=Editor de testu avanzáu +GenericName[bg]=Усъвършенстван текстов редактор +GenericName[bs]=Napredni uređivač teksta +GenericName[ca]=Editor de text avançat +GenericName[ca@valencia]=Editor de text avançat +GenericName[cs]=Pokročilý textový editor +GenericName[da]=Avanceret teksteditor +GenericName[de]=Erweiterter Texteditor +GenericName[el]=Προχωρημένος επεξεργαστής κειμένου +GenericName[en_GB]=Advanced Text Editor +GenericName[es]=Editor de texto avanzado +GenericName[et]=Täiustatud tekstiredaktor +GenericName[eu]=Testu-editore aurreratua +GenericName[fi]=Kehittynyt tekstimuokkain +GenericName[fr]=Éditeur de texte avancé +GenericName[ga]=Ardeagarthóir Téacs +GenericName[gl]=Editor avanzado de textos +GenericName[he]=עורך טקסט מתקדם +GenericName[hr]=Napredni uređivač teksta +GenericName[hu]=Speciális szövegszerkesztő +GenericName[ia]=Editor avantiate de texto +GenericName[is]=Þróaður textaritill +GenericName[it]=Editor di testi avanzato +GenericName[ja]=高度なテキストエディタ +GenericName[kk]=Үздік мәтін редакторы +GenericName[km]=កម្មវិធី​និពន្ធ​អត្ថបទ​កម្រិត​ខ្ពស់ +GenericName[ko]=고급 텍스트 편집기 +GenericName[lt]=Sudėtingesnis teksto redaktorius +GenericName[lv]=Jaudīgs teksta redaktors +GenericName[mr]=प्रगत पाठ्य संपादक +GenericName[nb]=Avansert skriveprogram +GenericName[nds]=Verwiedert Texteditor +GenericName[ne]=उन्नत पाठ सम्पादक +GenericName[nl]=Geavanceerde teksteditor +GenericName[nn]=Avansert skriveprogram +GenericName[pa]=ਤਕਨੀਕੀ ਟੈਕਸਟ ਐਡੀਟਰ +GenericName[pl]=Zaawansowany edytor tekstu +GenericName[pt]=Editor de Texto Avançado +GenericName[pt_BR]=Editor de textos avançado +GenericName[ro]=Redactor de text avansat +GenericName[ru]=Улучшенный текстовый редактор +GenericName[si]=උසස් පෙළ සකසනය +GenericName[sk]=Pokročilý textový editor +GenericName[sl]=Napreden urejevalnik besedil +GenericName[sr]=Напредни уређивач текста +GenericName[sr@ijekavian]=Напредни уређивач текста +GenericName[sr@ijekavianlatin]=Napredni uređivač teksta +GenericName[sr@latin]=Napredni uređivač teksta +GenericName[sv]=Avancerad texteditor +GenericName[tg]=Таҳриргари матнии беҳтаршуда +GenericName[tr]=Gelişmiş Metin Düzenleyici +GenericName[ug]=ئالىي تېكىست تەھرىرلىگۈچ +GenericName[uk]=Потужний текстовий редактор +GenericName[x-test]=xxAdvanced Text Editorxx +GenericName[zh_CN]=高级文本编辑器 +GenericName[zh_TW]=進階文字編輯器 +Name=Kate +Name[ar]=كيت +Name[ast]=Kate +Name[be]=Kate +Name[bg]=Kate +Name[bs]=Kate +Name[ca]=Kate +Name[ca@valencia]=Kate +Name[cs]=Kate +Name[da]=Kate +Name[de]=Kate +Name[el]=Kate +Name[en_GB]=Kate +Name[eo]=Kate +Name[es]=Kate +Name[et]=Kate +Name[eu]=Kate +Name[fi]=Kate +Name[fr]=Kate +Name[ga]=Kate +Name[gl]=Kate +Name[he]=Kate +Name[hu]=Kate +Name[ia]=Kate +Name[it]=Kate +Name[ja]=Kate +Name[kk]=Kate +Name[km]=Kate +Name[ko]=Kate +Name[lt]=Kate +Name[lv]=Kate +Name[mr]=केट +Name[ms]=Kate +Name[nb]=Kate +Name[nds]=Kate +Name[ne]=केट +Name[nl]=Kate +Name[nn]=Kate +Name[pa]=ਕੇਟ +Name[pl]=Kate +Name[pt]=Kate +Name[pt_BR]=Kate +Name[ro]=Kate +Name[ru]=Kate +Name[si]=Kate +Name[sk]=Kate +Name[sl]=Kate +Name[sr]=Кејт +Name[sr@ijekavian]=Кејт +Name[sr@ijekavianlatin]=Kate +Name[sr@latin]=Kate +Name[sv]=Kate +Name[tg]=Кейт +Name[tr]=Kate +Name[ug]=Kate +Name[uk]=Kate +Name[wa]=Kate +Name[x-test]=xxKatexx +Name[zh_CN]=Kate +Name[zh_TW]=Kate +MimeType=text/plain; +Exec=kate --icon '%i' --caption '%c' -b %U +X-KDE-StartupNotify=true +X-KDE-HasTempFileOption=true +Icon=kate +X-DocPath=kate/index.html +Type=Application +Terminal=false +InitialPreference=9 +X-DBUS-StartupType=Multi +X-DBUS-ServiceName=org.kde.kate +Categories=Qt;KDE;Utility;TextEditor; diff --git a/kate/src/data/kateplugin.desktop b/kate/src/data/kateplugin.desktop new file mode 100644 index 00000000..130853ba --- /dev/null +++ b/kate/src/data/kateplugin.desktop @@ -0,0 +1,71 @@ +[Desktop Entry] +Type=ServiceType +X-KDE-ServiceType=Kate/Plugin +X-KDE-Derived= +Comment=Kate Plugin +Comment[ar]=ملحقة كيت +Comment[ast]=Complementu de Kate +Comment[bg]=Приставка за Kate +Comment[bs]=Priključak za Kate +Comment[ca]=Connector per al Kate +Comment[ca@valencia]=Connector per al Kate +Comment[cs]=Modul Kate +Comment[da]=Kate-plugin +Comment[de]=Kate-Modul +Comment[el]=Πρόσθετο Kate +Comment[en_GB]=Kate Plugin +Comment[es]=Complemento de Kate +Comment[et]=Kate plugin +Comment[eu]=Kate-ren plugina +Comment[fi]=Kate-liitännäinen +Comment[fr]=Module externe pour Kate +Comment[ga]=Breiseán Kate +Comment[gl]=Complemento de Kate +Comment[he]=תוסף Kate +Comment[hu]=Kate-bővítmény +Comment[ia]=Plug-in de Kate +Comment[is]=Kate íforrit +Comment[it]=Estensione di Kate +Comment[ja]=Kate プラグイン +Comment[kk]=Kate плагині +Comment[km]=កម្មវិធី​ជំនួយ Kate +Comment[ko]=Kate 플러그인 +Comment[lt]=Kate priedas +Comment[lv]=Kate spraudnis +Comment[mr]=केट प्लगइन +Comment[nb]=Kate programtillegg +Comment[nds]=Kate-Moduul +Comment[ne]=केट प्लगइन +Comment[nl]=Kate-plug-in +Comment[nn]=Kate-tillegg +Comment[pa]=ਕੇਟ ਪਲੱਗਇਨ +Comment[pl]=Wtyczka Kate +Comment[pt]='Plugin' do Kate +Comment[pt_BR]=Plugin do Kate +Comment[ro]=Modul Kate +Comment[ru]=Модуль Kate +Comment[si]=Kate ප්ලගින +Comment[sk]=Kate Plugin +Comment[sl]=Vstavek za Kate +Comment[sr]=Прикључак за Кејт +Comment[sr@ijekavian]=Прикључак за Кејт +Comment[sr@ijekavianlatin]=Priključak za Kate +Comment[sr@latin]=Priključak za Kate +Comment[sv]=Kate insticksprogram +Comment[tg]=Плагини Кейт +Comment[tr]=Kate Eklentisi +Comment[ug]=Kate قىستۇرما +Comment[uk]=Додаток до Kate +Comment[wa]=Tchôke-divins di Kate +Comment[x-test]=xxKate Pluginxx +Comment[zh_CN]=Kate 插件 +Comment[zh_TW]=Kate 外掛程式 + +[PropertyDef::X-Kate-Version] +Type=QString + +[PropertyDef::X-Kate-Load] +Type=bool + +[PropertyDef::X-Kate-LoadAlways] +Type=bool diff --git a/kate/src/data/katerc b/kate/src/data/katerc new file mode 100644 index 00000000..cb245503 --- /dev/null +++ b/kate/src/data/katerc @@ -0,0 +1,24 @@ +[MainWindow0] +Active ViewSpace=0 +Kate-MDI-H-Splitter=200,560,200 +Kate-MDI-Sidebar-0-Splitter=480 +Kate-MDI-Sidebar-1-Splitter= +Kate-MDI-Sidebar-2-Splitter= +Kate-MDI-Sidebar-3-Splitter=0 +Kate-MDI-Sidebar-Style=3 +Kate-MDI-Sidebar-Visible=true +Kate-MDI-ToolView-kate_filelist-Persistent=false +Kate-MDI-ToolView-kate_filelist-Position=0 +Kate-MDI-ToolView-kate_filelist-Sidebar-Position=0 +Kate-MDI-ToolView-kate_filelist-Visible=true +Kate-MDI-ToolView-kate_private_plugin_katefindinfilesplugin-Persistent=false +Kate-MDI-ToolView-kate_private_plugin_katefindinfilesplugin-Position=3 +Kate-MDI-ToolView-kate_private_plugin_katefindinfilesplugin-Sidebar-Position=0 +Kate-MDI-ToolView-kate_private_plugin_katefindinfilesplugin-Visible=false +Kate-MDI-V-Splitter=150,461,200 +State=AAAA/wAAAAD9AAAAAAAAAxoAAAHlAAAABAAAAAQAAAAIAAAACPwAAAABAAAAAgAAAAEAAAAWAG0AYQBpAG4AVABvAG8AbABCAGEAcgEAAAAAAAADGgAAAAAAAAAA + +[MainWindow0-Splitter 0] +Children=MainWindow0-ViewSpace 0 +Orientation=1 +Sizes=560 diff --git a/kate/src/data/kateui.rc b/kate/src/data/kateui.rc new file mode 100644 index 00000000..84d9444b --- /dev/null +++ b/kate/src/data/kateui.rc @@ -0,0 +1,152 @@ + + + + + &File + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + &Edit + + + + + + + + + + + &View + + + + + + + Split View + + + + + + + + + + + + + + + + + + + + + + + + + + &Tools + + + + + + + + Sess&ions + + + + + + + + + + + &Settings + + + + &Help + + + + +Main Toolbar + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + &Status Bar Items + + + + + + + + + + diff --git a/kate/src/data/tips b/kate/src/data/tips new file mode 100644 index 00000000..497fb557 --- /dev/null +++ b/kate/src/data/tips @@ -0,0 +1,121 @@ + + +

    Kate comes with a nice set of plugins, providing simple +and advanced features of all sorts.

    +

    You can enable/disable plugins to suit your needs in the configuration dialog, +choose Settings ->configure to launch that.

    + +
    + + +

    You can swap the characters on each side of the cursor just by pressing +Ctrl+T

    + +
    + + +

    You can export the current document as a HTML file, including +syntax highlighting.

    +

    Just choose File -> Export -> HTML...

    + +
    + + +

    You can split the Kate editor as many times as you like and +in either direction. Each frame has its own status bar and +can display any open document.

    +

    Just choose
    View -> Split View -> Split [ Horizontal | Vertical ]

    + +
    + + +

    You can move the Tool views to any side of the main window through the +context menu.

    + +
    + + +

    Kate has a built-in terminal emulator, just click on "Terminal" at +the bottom to show or hide it as you desire.

    + +
    + + +

    Kate can highlight the current line with a +
    different +background color.|

    +

    You can set the color in the Colors page of the configuration +dialog.

    + +
    + + +

    You can open the currently edited file in any other application from within +Kate.

    +

    Choose File -> Open With for the list of programs +configured +for the document type. There is also an option Other... to +choose any application on your system.

    + +
    + + +

    You can configure the editor to always display the line numbers and/or +bookmark panes when started from the Appearance page of the +configuration dialog.

    + +
    + + +

    You can download new or updated Syntax highlight definitions from +the Open/Save page in the configuration dialog.

    +

    Just click the Download... button on the Open/Save -> Modes & Filetypes +tab (You have to be online, of course...).

    + +
    + + +

    You can cycle through all open documents by pressing Alt+Left +or Alt+Right. The next/previous document will immediately be displayed +in the active frame.

    + +
    + + +

    You can do cool sed-like regular expression replacements using Command Line.

    +

    For example, press F7 and enter s /oldtext/newtext/g +to replace "oldtext" with "newtext" throughout the current +line.

    + +
    + + +

    You can repeat your last search by just pressing F3, or +Shift+F3 if you want to search backwards.

    + +
    + + +

    You can filter the files displayed in the Filesystem Browser tool view. +

    +

    Simply enter your filter in the filter entry at the bottom, for example: +*.html *.php if you only want to see HTML and PHP files in the +current folder.

    +

    The Filesystem Browser will even remember your filters for you.

    + +
    + + +

    You can have two views - or even more - of the same document in Kate. Editing +in either will be reflected in both.

    +

    So if you find yourself scrolling up and down to look at text at the other +end of a document, just press Ctrl+Shift+T to split +horizontally.

    + +
    + + +

    Press F8 or Shift+F8 to switch to the +next/previous frame.

    + +
    diff --git a/kate/src/filetree/CMakeLists.txt b/kate/src/filetree/CMakeLists.txt new file mode 100644 index 00000000..a53113fc --- /dev/null +++ b/kate/src/filetree/CMakeLists.txt @@ -0,0 +1,37 @@ +project(katefiletreeplugin) + +set(katefiletreeplugin_PART_SRCS + katefiletreeplugin.cpp + katefiletree.cpp + katefiletreemodel.cpp + katefiletreeproxymodel.cpp + katefiletreeconfigpage.cpp + katefiletreepluginsettings.cpp +) + +include_directories(${CMAKE_CURRENT_BINARY_DIR}) + +kde4_add_plugin(katefiletreeplugin ${katefiletreeplugin_PART_SRCS}) + +target_link_libraries(katefiletreeplugin + ${KDE4_KFILE_LIBS} + ${KDE4_KPARTS_LIBS} + ${KDE4_KTEXTEDITOR_LIBS} + kateinterfaces +) + +install( + TARGETS katefiletreeplugin + DESTINATION ${KDE4_PLUGIN_INSTALL_DIR} +) + +########### install files ############### + +install( + FILES ui.rc + DESTINATION ${KDE4_DATA_INSTALL_DIR}/kate/plugins/filetree +) +install( + FILES katefiletreeplugin.desktop + DESTINATION ${KDE4_SERVICES_INSTALL_DIR} +) diff --git a/kate/src/filetree/katefiletree.cpp b/kate/src/filetree/katefiletree.cpp new file mode 100644 index 00000000..0569a5c9 --- /dev/null +++ b/kate/src/filetree/katefiletree.cpp @@ -0,0 +1,547 @@ +/* This file is part of the KDE project + Copyright (C) 2010 Thomas Fjellstrom + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +//BEGIN Includes +#include "katefiletree.h" +#include "moc_katefiletree.cpp" +#include "katefiletreemodel.h" +#include "katefiletreeproxymodel.h" +#include "katefiletreedebug.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +//END Includes + + +//BEGIN KateFileTree + +KateFileTree::KateFileTree(QWidget *parent): QTreeView(parent) +{ + setAcceptDrops(false); + setIndentation(12); + setAllColumnsShowFocus(true); + + connect( this, SIGNAL(clicked(QModelIndex)), this, SLOT(mouseClicked(QModelIndex))); + + m_filelistCloseDocument = new QAction( KIcon("window-close"), i18n( "Close" ), this ); + connect( m_filelistCloseDocument, SIGNAL(triggered()), this, SLOT(slotDocumentClose()) ); + m_filelistCloseDocument->setWhatsThis(i18n("Close the current document.")); + + m_filelistCopyFilename = new QAction( KIcon("edit-copy"), i18n( "Copy Filename" ), this ); + connect( m_filelistCopyFilename, SIGNAL(triggered()), this, SLOT(slotCopyFilename()) ); + m_filelistCopyFilename->setWhatsThis(i18n("Copy the filename of the file.")); + + QActionGroup *modeGroup = new QActionGroup(this); + + m_treeModeAction = setupOption(modeGroup, KIcon("view-list-tree"), i18n("Tree Mode"), + i18n("Set view style to Tree Mode"), + SLOT(slotTreeMode()), true); + + m_listModeAction = setupOption(modeGroup, KIcon("view-list-text"), i18n("List Mode"), + i18n("Set view style to List Mode"), + SLOT(slotListMode()), false); + + QActionGroup *sortGroup = new QActionGroup(this); + + m_sortByFile = setupOption(sortGroup, KIcon(), i18n("Document Name"), + i18n("Sort by Document Name"), + SLOT(slotSortName()), true); + + + m_sortByPath = setupOption(sortGroup, KIcon(), i18n("Document Path"), + i18n("Sort by Document Path"), + SLOT(slotSortPath()), false); + + m_sortByOpeningOrder = setupOption(sortGroup, KIcon(), i18n("Opening Order"), + i18n("Sort by Opening Order"), + SLOT(slotSortOpeningOrder()), false); + + QPalette p = palette(); + p.setColor(QPalette::Inactive, QPalette::Highlight, p.color(QPalette::Active, QPalette::Highlight)); + p.setColor(QPalette::Inactive, QPalette::HighlightedText, p.color(QPalette::Active, QPalette::HighlightedText)); + setPalette(p); +} + +KateFileTree::~KateFileTree() +{} + +QAction *KateFileTree::setupOption( + QActionGroup *group, + const KIcon &icon, + const QString &label, + const QString &whatsThis, + const char *slot, + bool checked +) +{ + QAction *new_action = new QAction( icon, label, this ); + new_action->setWhatsThis(whatsThis); + new_action->setActionGroup(group); + new_action->setCheckable(true); + new_action->setChecked(checked); + connect( new_action, SIGNAL(triggered()), this, slot ); + return new_action; +} + +void KateFileTree::slotListMode() +{ + emit viewModeChanged(true); +} + +void KateFileTree::slotTreeMode() +{ + emit viewModeChanged(false); +} + +void KateFileTree::slotSortName() +{ + emit sortRoleChanged(Qt::DisplayRole); +} + +void KateFileTree::slotSortPath() +{ + emit sortRoleChanged(KateFileTreeModel::PathRole); +} + +void KateFileTree::slotSortOpeningOrder() +{ + emit sortRoleChanged(KateFileTreeModel::OpeningOrderRole); +} + +void KateFileTree::slotCurrentChanged ( const QModelIndex ¤t, const QModelIndex &previous ) +{ + kDebug(debugArea()) << "current:" << current << "previous:" << previous; + + if(!current.isValid()) + return; + + KTextEditor::Document *doc = model()->data(current, KateFileTreeModel::DocumentRole).value(); + if(doc) { + kDebug(debugArea()) << "got doc, setting prev:" << current; + m_previouslySelected = current; + } +} + +void KateFileTree::mouseClicked ( const QModelIndex &index ) +{ + kDebug(debugArea()) << "got index" << index; + + KTextEditor::Document *doc = model()->data(index, KateFileTreeModel::DocumentRole).value(); + if(doc) { + kDebug(debugArea()) << "got doc" << index << "setting prev:" << QModelIndex(); + emit activateDocument(doc); + //m_previouslySelected = QModelIndex(); + } + else { + kDebug(debugArea()) << "selecting previous item" << m_previouslySelected; + + selectionModel()->setCurrentIndex(m_previouslySelected,QItemSelectionModel::ClearAndSelect); + } + +} + +void KateFileTree::contextMenuEvent ( QContextMenuEvent * event ) +{ + m_indexContextMenu = selectionModel()->currentIndex(); + + selectionModel()->setCurrentIndex(m_indexContextMenu, QItemSelectionModel::ClearAndSelect); + + KateFileTreeProxyModel *ftpm = static_cast(model()); + KateFileTreeModel *ftm = static_cast(ftpm->sourceModel()); + + bool listMode = ftm->listMode(); + m_treeModeAction->setChecked(!listMode); + m_listModeAction->setChecked(listMode); + + int sortRole = ftpm->sortRole(); + m_sortByFile->setChecked(sortRole == Qt::DisplayRole); + m_sortByPath->setChecked(sortRole == KateFileTreeModel::PathRole); + m_sortByOpeningOrder->setChecked(sortRole == KateFileTreeModel::OpeningOrderRole); + + const bool isFile = (0 != m_indexContextMenu.data(KateFileTreeModel::DocumentRole).value()); + + QMenu menu; + menu.addAction(m_filelistCloseDocument); + if (isFile) { + menu.addAction(m_filelistCopyFilename); + QMenu *openWithMenu=menu.addMenu(i18n("Open With")); + connect(openWithMenu, SIGNAL(aboutToShow()), this, SLOT(slotFixOpenWithMenu())); + connect(openWithMenu, SIGNAL(triggered(QAction*)), this, SLOT(slotOpenWithMenuAction(QAction*))); + } + menu.addSeparator(); + QMenu *view_menu = menu.addMenu(i18n("View Mode")); + view_menu->addAction(m_treeModeAction); + view_menu->addAction(m_listModeAction); + + QMenu *sort_menu = menu.addMenu(i18n("Sort By")); + sort_menu->addAction(m_sortByFile); + sort_menu->addAction(m_sortByPath); + sort_menu->addAction(m_sortByOpeningOrder); + + menu.exec(viewport()->mapToGlobal(event->pos())); + + if (m_previouslySelected.isValid()) { + selectionModel()->setCurrentIndex(m_previouslySelected,QItemSelectionModel::ClearAndSelect); + } + + event->accept(); +} + + +void KateFileTree::slotFixOpenWithMenu() +{ + QMenu *menu = (QMenu*)sender(); + menu->clear(); + + KTextEditor::Document *doc = model()->data(m_indexContextMenu, KateFileTreeModel::DocumentRole).value(); + if (!doc) return; + + // get a list of appropriate services. + KMimeType::Ptr mime = KMimeType::mimeType(doc->mimeType()); + //kDebug(13001) << "mime type: " << mime->name(); + + QAction *a = 0; + KService::List offers = KMimeTypeTrader::self()->query(mime->name(), "Application"); + // for each one, insert a menu item... + for(KService::List::Iterator it = offers.begin(); it != offers.end(); ++it) + { + KService::Ptr service = *it; + if (service->name() == "Kate") continue; + a = menu->addAction(KIcon(service->icon()), service->name()); + a->setData(service->entryPath()); + } + // append "Other..." to call the KDE "open with" dialog. + a = menu->addAction(i18n("&Other...")); + a->setData(QString()); +} + +void KateFileTree::slotOpenWithMenuAction(QAction* a) +{ + KUrl::List list; + + KTextEditor::Document *doc = model()->data(m_indexContextMenu, KateFileTreeModel::DocumentRole).value(); + if (!doc) return; + + + list.append( doc->url() ); + + const QString openWith = a->data().toString(); + if (openWith.isEmpty()) + { + // display "open with" dialog + KOpenWithDialog dlg(list); + if (dlg.exec()) + KRun::run(*dlg.service(), list, this); + return; + } + + KService::Ptr app = KService::serviceByDesktopPath(openWith); + if (app) + { + KRun::run(*app, list, this); + } + else + { + KMessageBox::error(this, i18n("Application '%1' not found.", openWith), i18n("Application not found")); + } +} + + +#include "metatype_qlist_ktexteditor_document_pointer.h" + +void KateFileTree::slotDocumentClose() +{ + m_previouslySelected = QModelIndex(); + QVariant v = m_indexContextMenu.data(KateFileTreeModel::DocumentTreeRole); + if (!v.isValid()) return; + QList closingDocuments = v.value >(); + Kate::application()->documentManager()->closeDocumentList(closingDocuments); +} + +void KateFileTree::slotCopyFilename() +{ + KTextEditor::Document *doc = model()->data(m_indexContextMenu, KateFileTreeModel::DocumentRole).value(); + if (doc) { + QApplication::clipboard()->setText(doc->url().url()); + } +} + +void KateFileTree::switchDocument( const QString &doc ) +{ + if (doc.isEmpty()) { + // no argument: switch to the previous document + slotDocumentPrev(); + } else if (doc.toInt() > 0 && + doc.toInt() <= model()->rowCount( model()->parent(currentIndex()) )) { + // numerical argument: switch to the nth document + int i = doc.toInt() - 1; + KTextEditor::Document *doc = + model()->data(model()->index(i - 1, 0), + KateFileTreeModel::DocumentRole).value(); + if (doc) { + emit activateDocument(doc); + } + } else { + // string argument: switch to the given file + QModelIndexList matches = + model()->match(model()->index(0, 0), Qt::DisplayRole, QVariant(doc), 1, Qt::MatchContains); + if (!matches.isEmpty()) { + KTextEditor::Document *doc = + model()->data(matches.takeFirst(), + KateFileTreeModel::DocumentRole).value(); + if (doc) { + emit activateDocument(doc); + } + } + } +} + +void KateFileTree::slotDocumentFirst() +{ + KTextEditor::Document *doc = + model()->data(model()->index(0, 0), + KateFileTreeModel::DocumentRole).value(); + if (doc) { + emit activateDocument(doc); + } +} + +void KateFileTree::slotDocumentLast() +{ + int count = model()->rowCount( model()->parent(currentIndex()) ); + KTextEditor::Document *doc = + model()->data(model()->index(count - 1, 0), + KateFileTreeModel::DocumentRole).value(); + if (doc) { + emit activateDocument(doc); + } +} + +void KateFileTree::slotDocumentPrev() +{ + kDebug(debugArea()) << "BEGIN"; + KateFileTreeProxyModel *ftpm = static_cast(model()); + + + + QModelIndex current_index = currentIndex(); + QModelIndex prev; + + // scan up the tree skipping any dir nodes + + //kDebug(debugArea()) << "cur" << ftpm->data(current_index, Qt::DisplayRole); + while(current_index.isValid()) { + if(current_index.row() > 0) { + current_index = ftpm->sibling(current_index.row()-1, current_index.column(), current_index); + //kDebug(debugArea()) << "get prev" << ftpm->data(current_index, Qt::DisplayRole); + if(!current_index.isValid()) { + //kDebug(debugArea()) << "somehow getting prev index from sibling didn't work :("; + break; + } + + if(ftpm->isDir(current_index)) { + // try and select the last child in this parent + //kDebug(debugArea()) << "is a dir"; + int children = ftpm->rowCount(current_index); + current_index = ftpm->index(children-1, 0, current_index); + //kDebug(debugArea()) << "child" << ftpm->data(current_index, Qt::DisplayRole); + if(ftpm->isDir(current_index)) { + // since we're a dir, keep going + //kDebug(debugArea()) << "child is a dir"; + while(ftpm->isDir(current_index)) { + children = ftpm->rowCount(current_index); + current_index = ftpm->index(children-1, 0, current_index); + } + + if(!ftpm->isDir(current_index)) { + prev = current_index; + break; + } + + continue; + } else { + // we're the previous file, set prev + //kDebug(debugArea()) << "got doc 1"; + prev = current_index; + break; + } + } else { // found document item + //kDebug(debugArea()) << "got doc 2"; + prev = current_index; + break; + } + } + else { + //kDebug(debugArea()) << "get parent"; + // just select the parent, the logic above will handle the rest + current_index = ftpm->parent(current_index); + //kDebug(debugArea()) << "got parent" << ftpm->data(current_index, Qt::DisplayRole); + if(!current_index.isValid()) { + // paste the root node here, try and wrap around + //kDebug(debugArea()) << "parent invalid"; + + int children = ftpm->rowCount(current_index); + QModelIndex last_index = ftpm->index(children-1, 0, current_index); + //kDebug(debugArea()) << "last" << ftpm->data(last_index, Qt::DisplayRole); + if(!last_index.isValid()) + break; + + if(ftpm->isDir(last_index)) { + // last node is a dir, select last child row + //kDebug(debugArea()) << "last root is a dir, select child"; + int last_children = ftpm->rowCount(last_index); + prev = ftpm->index(last_children-1, 0, last_index); + //kDebug(debugArea()) << "last child" << ftpm->data(current_index, Qt::DisplayRole); + // bug here? + break; + } + else { + // got last file node + //kDebug(debugArea()) << "got doc"; + prev = last_index; + break; + } + } + } + } + + if(prev.isValid()) { + //kDebug(debugArea()) << "got prev node:" << prev; + //kDebug(debugArea()) << "doc:" << ftpm->data(prev, Qt::DisplayRole).value(); + + KTextEditor::Document *doc = model()->data(prev, KateFileTreeModel::DocumentRole).value(); + emit activateDocument(doc); + } + else { + kDebug(debugArea()) << "didn't get prev node :("; + } + + kDebug(debugArea()) << "END"; +} + +/* +plan: + +default: select next sibling +if cur is a dir, select it + +*/ + +void KateFileTree::slotDocumentNext() +{ + kDebug(debugArea()) << "BEGIN"; + + KateFileTreeProxyModel *ftpm = static_cast(model()); + + QModelIndex current_index = currentIndex(); + int parent_row_count = ftpm->rowCount( ftpm->parent(current_index) ); + QModelIndex next; + + // scan down the tree skipping any dir nodes + while(current_index.isValid()) { + if(current_index.row() < parent_row_count-1) { + current_index = ftpm->sibling(current_index.row()+1, current_index.column(), current_index); + if(!current_index.isValid()) { + break; + } + + if(ftpm->isDir(current_index)) { + // we have a dir node + while(ftpm->isDir(current_index)) { + current_index = ftpm->index(0, 0, current_index); + } + + parent_row_count = ftpm->rowCount( ftpm->parent(current_index) ); + + if(!ftpm->isDir(current_index)) { + next = current_index; + break; + } + } else { // found document item + next = current_index; + break; + } + } + else { + // select the parent's next sibling + QModelIndex parent_index = ftpm->parent(current_index); + int grandparent_row_count = ftpm->rowCount( ftpm->parent(parent_index) ); + + current_index = parent_index; + parent_row_count = grandparent_row_count; + + // at least if we're not past the last node + if(!current_index.isValid()) { + // paste the root node here, try and wrap around + QModelIndex last_index = ftpm->index(0, 0, QModelIndex()); + if(!last_index.isValid()) { + break; + } + + if(ftpm->isDir(last_index)) { + // last node is a dir, select first child row + while(ftpm->isDir(last_index)) { + if(ftpm->rowCount(last_index)) { + // has children, select first + last_index = ftpm->index(0, 0, last_index); + } + } + + next = last_index; + break; + } + else { + // got first file node + next = last_index; + break; + } + } + } + } + + if(next.isValid()) { + //kDebug(debugArea()) << "got next node:" << next; + //kDebug(debugArea()) << "doc:" << ftpm->data(next, Qt::DisplayRole).value(); + + KTextEditor::Document *doc = model()->data(next, KateFileTreeModel::DocumentRole).value(); + emit activateDocument(doc); + } + else { + kDebug(debugArea()) << "didn't get next node :("; + } + + kDebug(debugArea()) << "END"; +} +//END KateFileTree + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/src/filetree/katefiletree.h b/kate/src/filetree/katefiletree.h new file mode 100644 index 00000000..d93effd5 --- /dev/null +++ b/kate/src/filetree/katefiletree.h @@ -0,0 +1,96 @@ +/* This file is part of the KDE project + Copyright (C) 2010 Thomas Fjellstrom + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KATE_FILETREE_H +#define KATE_FILETREE_H + +#include +#include +#include + +namespace KTextEditor { + class Document; +} + +#include + +class KateFileTree: public QTreeView +{ + Q_OBJECT + + public: + + KateFileTree(QWidget * parent); + virtual ~KateFileTree(); + + private: + QAction *m_filelistCloseDocument; + QAction *m_filelistCopyFilename; + + + QAction *m_treeModeAction; + QAction *m_listModeAction; + + QAction *m_sortByFile; + QAction *m_sortByPath; + QAction *m_sortByOpeningOrder; + + QPersistentModelIndex m_previouslySelected; + QPersistentModelIndex m_indexContextMenu; + + public Q_SLOTS: + void slotDocumentClose(); + void slotCopyFilename(); + void slotCurrentChanged( const QModelIndex ¤t, const QModelIndex &previous ); + void switchDocument( const QString &doc ); + void slotDocumentFirst(); + void slotDocumentLast(); + void slotDocumentNext(); + void slotDocumentPrev(); + + protected: + virtual void contextMenuEvent ( QContextMenuEvent * event ); + + Q_SIGNALS: + void closeDocument(KTextEditor::Document*); + void activateDocument(KTextEditor::Document*); + + void openDocument(KUrl); + + void viewModeChanged(bool treeMode); + void sortRoleChanged(int); + + private Q_SLOTS: + void mouseClicked(const QModelIndex &index); + + void slotTreeMode(); + void slotListMode(); + + void slotSortName(); + void slotSortPath(); + void slotSortOpeningOrder(); + void slotFixOpenWithMenu(); + void slotOpenWithMenuAction(QAction* a); + + private: + QAction *setupOption(QActionGroup *group, const KIcon &, const QString &, const QString &, const char *slot, bool checked=false); +}; + +#endif // KATE_FILETREE_H + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/src/filetree/katefiletreeconfigpage.cpp b/kate/src/filetree/katefiletreeconfigpage.cpp new file mode 100644 index 00000000..7470499b --- /dev/null +++ b/kate/src/filetree/katefiletreeconfigpage.cpp @@ -0,0 +1,201 @@ +/* This file is part of the KDE project + Copyright (C) 2001 Christoph Cullmann + Copyright (C) 2001 Joseph Wenninger + Copyright (C) 2001, 2007 Anders Lund + Copyright (C) 2009, Abhishek Patil + Copyright (C) 2010, Thomas Fjellstrom + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +/* +Config stuff plan: +----------------- + +main default config is stored in KGlobal::config()+":filetree" +when main config is set, it needs to tell view's to delete +existing customized settings, and use the global ones (somehow) +(maybe some kind of "customized" flag?) + +view needs to pull default settings from the main plugin config + +*/ + +#include "katefiletreeconfigpage.h" +#include "katefiletreeplugin.h" +#include "katefiletreedebug.h" +#include "katefiletreemodel.h" +#include "katefiletreeproxymodel.h" + +#include +#include +#include + +#include +#include +#include +#include + +KateFileTreeConfigPage::KateFileTreeConfigPage( QWidget* parent, KateFileTreePlugin *fl ) + : Kate::PluginConfigPage( parent ), + m_plug( fl ), + m_changed( false ) +{ + kDebug(debugArea()) << "BEGIN"; + + QVBoxLayout *layout = new QVBoxLayout( this ); + layout->setMargin( 0 ); + + gbEnableShading = new QGroupBox( i18n("Background Shading"), this ); + gbEnableShading->setCheckable(true); + layout->addWidget( gbEnableShading ); + + QGridLayout *lo = new QGridLayout( gbEnableShading); + + kcbViewShade = new KColorButton( gbEnableShading ); + lViewShade = new QLabel( i18n("&Viewed documents' shade:"), gbEnableShading ); + lViewShade->setBuddy( kcbViewShade ); + lo->addWidget( lViewShade, 2, 0 ); + lo->addWidget( kcbViewShade, 2, 1 ); + + kcbEditShade = new KColorButton( gbEnableShading ); + lEditShade = new QLabel( i18n("&Modified documents' shade:"), gbEnableShading ); + lEditShade->setBuddy( kcbEditShade ); + lo->addWidget( lEditShade, 3, 0 ); + lo->addWidget( kcbEditShade, 3, 1 ); + + // sorting + QHBoxLayout *lo2 = new QHBoxLayout; + layout->addLayout( lo2 ); + lSort = new QLabel( i18n("&Sort by:"), this ); + lo2->addWidget( lSort ); + cmbSort = new KComboBox( this ); + lo2->addWidget( cmbSort ); + lSort->setBuddy( cmbSort ); + cmbSort->addItem(i18n("Opening Order"), (int)KateFileTreeModel::OpeningOrderRole); + cmbSort->addItem(i18n("Document Name"), (int)Qt::DisplayRole); + cmbSort->addItem(i18n("Url"), (int)KateFileTreeModel::PathRole); + + + // view mode + QHBoxLayout *lo3 = new QHBoxLayout; + layout->addLayout( lo3 ); + lMode = new QLabel( i18n("&View Mode:"), this ); + lo3->addWidget( lMode ); + cmbMode = new KComboBox( this ); + lo3->addWidget( cmbMode ); + lMode->setBuddy( cmbMode ); + cmbMode->addItem(i18n("Tree View"), QVariant(false)); + cmbMode->addItem(i18n("List View"), QVariant(true)); + + // Show Full Path on Roots? + QHBoxLayout *lo4 = new QHBoxLayout; + layout->addLayout( lo4 ); + cbShowFullPath = new QCheckBox( i18n("&Show Full Path"), this ); + lo4->addWidget( cbShowFullPath ); + + layout->insertStretch( -1, 10 ); + + gbEnableShading->setWhatsThis( i18n( + "When background shading is enabled, documents that have been viewed " + "or edited within the current session will have a shaded background. " + "The most recent documents have the strongest shade.") ); + kcbViewShade->setWhatsThis( i18n( + "Set the color for shading viewed documents.") ); + kcbEditShade->setWhatsThis( i18n( + "Set the color for modified documents. This color is blended into " + "the color for viewed files. The most recently edited documents get " + "most of this color.") ); + + cbShowFullPath->setWhatsThis( i18n( + "When enabled, in tree mode, top level folders will show up with their full path " + "rather than just the last folder name." ) ); + +// cmbSort->setWhatsThis( i18n( +// "Set the sorting method for the documents.") ); + + reset(); + + connect( gbEnableShading, SIGNAL(toggled(bool)), this, SLOT(slotMyChanged()) ); + connect( kcbViewShade, SIGNAL(changed(QColor)), this, SLOT(slotMyChanged()) ); + connect( kcbEditShade, SIGNAL(changed(QColor)), this, SLOT(slotMyChanged()) ); + connect( cmbSort, SIGNAL(activated(int)), this, SLOT(slotMyChanged()) ); + connect( cmbMode, SIGNAL(activated(int)), this, SLOT(slotMyChanged()) ); + connect( cbShowFullPath, SIGNAL(stateChanged(int)), this, SLOT(slotMyChanged()) ); + + kDebug(debugArea()) << "END"; +} + +void KateFileTreeConfigPage::apply() +{ + kDebug(debugArea()) << "BEGIN"; + if ( ! m_changed ) { + kDebug(debugArea()) << "END !changed"; + return; + } + + m_changed = false; + + // apply config to views + m_plug->applyConfig( + gbEnableShading->isChecked(), + kcbViewShade->color(), + kcbEditShade->color(), + cmbMode->itemData(cmbMode->currentIndex()).toBool(), + cmbSort->itemData(cmbSort->currentIndex()).toInt(), + cbShowFullPath->checkState() == Qt::Checked + ); + + kDebug(debugArea()) << "END"; +} + +void KateFileTreeConfigPage::reset() +{ + kDebug(debugArea()) << "BEGIN"; + + const KateFileTreePluginSettings &settings = m_plug->settings(); + + gbEnableShading->setChecked( settings.shadingEnabled() ); + kcbEditShade->setColor( settings.editShade() ); + kcbViewShade->setColor( settings.viewShade() ); + cmbSort->setCurrentIndex( cmbSort->findData( settings.sortRole() ) ); + cmbMode->setCurrentIndex( settings.listMode() ); + cbShowFullPath->setCheckState( settings.showFullPathOnRoots() ? Qt::Checked : Qt::Unchecked ); + + m_changed = false; + kDebug(debugArea()) << "END"; +} + +void KateFileTreeConfigPage::defaults() +{ + kDebug(debugArea()) << "BEGIN"; + + // m_plug->settings().revertToDefaults() ?? + // not sure the above is ever needed... + + reset(); + + kDebug(debugArea()) << "END"; +} + +void KateFileTreeConfigPage::slotMyChanged() +{ + kDebug(debugArea()) << "BEGIN"; + m_changed = true; + emit changed(); + kDebug(debugArea()) << "END"; +} + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/src/filetree/katefiletreeconfigpage.h b/kate/src/filetree/katefiletreeconfigpage.h new file mode 100644 index 00000000..9f39ee25 --- /dev/null +++ b/kate/src/filetree/katefiletreeconfigpage.h @@ -0,0 +1,65 @@ +/* This file is part of the KDE project + Copyright (C) 2001 Christoph Cullmann + Copyright (C) 2001,2006 Joseph Wenninger + Copyright (C) 2001, 2007 Anders Lund + Copyright (C) 2010 Thomas Fjellstrom + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KATE_FILETREE_CONFIGPAGE_H +#define KATE_FILETREE_CONFIGPAGE_H + +#include +#include +#include + +#include + +class KColorButton; +class KComboBox; +class KateFileTreePlugin; + +class KateFileTreeConfigPage : public Kate::PluginConfigPage { + Q_OBJECT + public: + explicit KateFileTreeConfigPage( QWidget* parent=0, KateFileTreePlugin *plug=0 ); + ~KateFileTreeConfigPage() {}; + + public Q_SLOTS: + void apply(); + void defaults(); + void reset(); + + //Q_SIGNALS: + // void changed(); + + private Q_SLOTS: + void slotMyChanged(); + + private: + QGroupBox *gbEnableShading; + KColorButton *kcbViewShade, *kcbEditShade; + QLabel *lEditShade, *lViewShade, *lSort, *lMode; + KComboBox *cmbSort, *cmbMode; + QCheckBox *cbShowFullPath; + KateFileTreePlugin *m_plug; + + bool m_changed; +}; + +#endif /* KATE_FILETREE_CONFIGPAGE_H */ + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/src/filetree/katefiletreedebug.h b/kate/src/filetree/katefiletreedebug.h new file mode 100644 index 00000000..00924cc3 --- /dev/null +++ b/kate/src/filetree/katefiletreedebug.h @@ -0,0 +1,30 @@ +/* This file is part of the KDE project + Copyright (C) 2010 Thomas Fjellstrom + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KATE_FILETREEDEBUG_H +#define KATE_FILETREEDEBUG_H + +#include + +inline int debugArea() +{ + static int s_area = KDebug::registerArea("kate-filetree"); + return s_area; +} + +#endif /* KATE_FILETREEDEBUG_H */ diff --git a/kate/src/filetree/katefiletreemodel.cpp b/kate/src/filetree/katefiletreemodel.cpp new file mode 100644 index 00000000..90a05a31 --- /dev/null +++ b/kate/src/filetree/katefiletreemodel.cpp @@ -0,0 +1,1318 @@ +/* This file is part of the KDE project + Copyright (C) 2010 Thomas Fjellstrom + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "katefiletreemodel.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include "katefiletreedebug.h" + +class ProxyItemDir; +class ProxyItem { + friend class KateFileTreeModel; + + public: + enum Flag { None = 0, Dir = 1, Modified = 2, ModifiedExternally = 4, DeletedExternally = 8, Empty = 16, ShowFullPath = 32, Host=64 }; + Q_DECLARE_FLAGS(Flags, Flag) + + ProxyItem(QString n, ProxyItemDir *p = 0, Flags f = ProxyItem::None); + ~ProxyItem(); + + int addChild(ProxyItem *p); + void remChild(ProxyItem *p); + + ProxyItemDir *parent(); + + ProxyItem *child(int idx); + int childCount(); + + int row(); + + QString display(); + QString path(); + QString documentName(); + void setPath(const QString &str); + + void setIcon(KIcon i); + KIcon icon(); + + QList &children(); + + void setDoc(KTextEditor::Document *doc); + KTextEditor::Document *doc(); + QList docTree() const; + + void setFlags(Flags flags); + void setFlag(Flag flag); + void clearFlag(Flag flag); + bool flag(Flag flag); + void setHost(const QString &host); + const QString& host() const; + + private: + QString m_path; + QString m_documentName; + ProxyItemDir *m_parent; + QList m_children; + int m_row; + Flags m_flags; + + QString m_display; + KIcon m_icon; + KTextEditor::Document *m_doc; + QString m_host; + protected: + void initDisplay(); +}; + +QDebug operator<<(QDebug dbg, ProxyItem *item) +{ + if(!item) { + dbg.nospace() << "ProxyItem(0x0) "; + return dbg.maybeSpace(); + } + + void *parent = static_cast(item->parent()); + + dbg.nospace() << "ProxyItem(" << (void*)item << ","; + dbg.nospace() << parent << "," << item->row() << ","; + dbg.nospace() << item->doc() << "," << item->path() << ") "; + return dbg.maybeSpace(); +} + + +class ProxyItemDir : public ProxyItem +{ + public: + ProxyItemDir(QString n, ProxyItemDir *p = 0) : ProxyItem(n, p) { setFlag(ProxyItem::Dir); initDisplay();} +}; + +QDebug operator<<(QDebug dbg, ProxyItemDir *item) +{ + if(!item) { + dbg.nospace() << "ProxyItemDir(0x0) "; + return dbg.maybeSpace(); + } + + void *parent = static_cast(item->parent()); + + dbg.nospace() << "ProxyItemDir(" << (void*)item << ","; + dbg.nospace() << parent << "," << item->row() << ","; + dbg.nospace() << item->path() << ", children:" << item->childCount() << ") "; + return dbg.maybeSpace(); +} + +Q_DECLARE_OPERATORS_FOR_FLAGS(ProxyItem::Flags) + +ProxyItem::ProxyItem(QString d, ProxyItemDir *p, ProxyItem::Flags f) + : m_path(d), m_parent(p), m_row(-1), m_flags(f), m_doc(0) +{ + kDebug(debugArea()) << this; + initDisplay(); + + if(p) + p->addChild(this); + +} + +ProxyItem::~ProxyItem() +{ + foreach(ProxyItem *item, m_children) { + delete item; + } +} + +void ProxyItem::initDisplay() +{ + // triggers only if this is a top level node and the root has the show full path flag set. + if (flag(ProxyItem::Dir) && m_parent && !m_parent->m_parent && m_parent->flag(ProxyItem::ShowFullPath)) { + m_display = m_path; + if(m_display.startsWith(QDir::homePath())) { + m_display.replace(0, QDir::homePath().length(), "~"); + } + } else { + m_display = m_path.section(QLatin1Char('/'), -1, -1); + if (flag(ProxyItem::Host) && (!m_parent || (m_parent && !m_parent->m_parent))) { + QString hostPrefix="["+host()+"]"; + if (hostPrefix!=m_display) + m_display=hostPrefix+m_display; + } + } + +} + +int ProxyItem::addChild(ProxyItem *item) +{ + int item_row = m_children.count(); + item->m_row = item_row; + m_children.append(item); + item->m_parent = static_cast(this); + + item->initDisplay(); + + kDebug(debugArea()) << "added" << item << "to" << item->m_parent; + return item_row; +} + +void ProxyItem::remChild(ProxyItem *item) +{ + kDebug(debugArea()) << "remove" << item << "from" << static_cast(this); + m_children.removeOne(item); + // fix up item rows + // could be done a little better, but this'll work. + for(int i = 0; i < m_children.count(); i++) { + m_children[i]->m_row = i; + } + + item->m_parent = 0; +} + +ProxyItemDir *ProxyItem::parent() +{ + return m_parent; +} + +ProxyItem *ProxyItem::child(int idx) +{ + if(idx < 0 || idx >= m_children.count()) return 0; + return m_children[idx]; +} + +int ProxyItem::childCount() +{ + return m_children.count(); +} + +int ProxyItem::row() +{ + return m_row; +} + +KIcon ProxyItem::icon() +{ + if(m_children.count()) + return KIcon("folder"); + + return m_icon; +} + +void ProxyItem::setIcon(KIcon i) +{ + m_icon = i; +} + +QString ProxyItem::documentName() { + return m_documentName; +} + +QString ProxyItem::display() +{ + return m_display; +} + +QString ProxyItem::path() +{ + return m_path; +} + +void ProxyItem::setPath(const QString &p) +{ + m_path = p; + initDisplay(); +} + +QList &ProxyItem::children() +{ + return m_children; +} + +void ProxyItem::setDoc(KTextEditor::Document *doc) +{ + m_doc = doc; + if (!doc) + m_documentName=QString(); + else { + QString docName=doc->documentName(); + if (flag(ProxyItem::Host)) + m_documentName="["+m_host+"]"+docName; + else + m_documentName=docName; + } +} + +KTextEditor::Document *ProxyItem::doc() +{ + return m_doc; +} + +QList ProxyItem::docTree() const +{ + QList result; + if (m_doc) { + result.append(m_doc); + } + for (QList::const_iterator iter = m_children.constBegin(); iter != m_children.constEnd(); ++iter) { + result.append((*iter)->docTree()); + } + return result; +} + +bool ProxyItem::flag(Flag f) +{ + return m_flags & f; +} + +void ProxyItem::setFlag(Flag f) +{ + m_flags |= f; +} + +void ProxyItem::setFlags(Flags f) +{ + m_flags = f; +} + +void ProxyItem::clearFlag(Flag f) +{ + m_flags &= ~f; +} + +void ProxyItem::setHost(const QString& host) +{ + QString docName; + if (m_doc) + docName=m_doc->documentName(); + if (host.isEmpty()) { + clearFlag(Host); + m_documentName=docName; + } else { + setFlag(Host); + m_documentName="["+host+"]"+docName; + } + m_host=host; + + initDisplay(); +} + +const QString& ProxyItem::host() const +{ + return m_host; +} + +KateFileTreeModel::KateFileTreeModel(QObject *p) + : QAbstractItemModel(p), + m_root(new ProxyItemDir(QString("m_root"), 0)) +{ + + // setup default settings + // session init will set these all soon + KColorScheme colors(QPalette::Active); + QColor bg = colors.background().color(); + m_editShade = KColorUtils::tint(bg, colors.foreground(KColorScheme::ActiveText).color(), 0.5); + m_viewShade = KColorUtils::tint(bg, colors.foreground(KColorScheme::VisitedText).color(), 0.5); + m_shadingEnabled = true; + m_listMode = false; + + initModel(); +} + +KateFileTreeModel::~KateFileTreeModel() +{ + +} + +bool KateFileTreeModel::shadingEnabled() +{ + return m_shadingEnabled; +} + +void KateFileTreeModel::setShadingEnabled(bool se) +{ + if(m_shadingEnabled != se) { + updateBackgrounds(true); + m_shadingEnabled = se; + } +} + +QColor KateFileTreeModel::editShade() +{ + return m_editShade; +} + +void KateFileTreeModel::setEditShade(QColor es) +{ + m_editShade = es; +} + +QColor KateFileTreeModel::viewShade() +{ + return m_viewShade; +} + +void KateFileTreeModel::setViewShade(QColor vs) +{ + m_viewShade = vs; +} + +bool KateFileTreeModel::showFullPathOnRoots(void) +{ + return m_root->flag(ProxyItem::ShowFullPath); +} + +void KateFileTreeModel::setShowFullPathOnRoots(bool s) +{ + if(s) + m_root->setFlag(ProxyItem::ShowFullPath); + else + m_root->clearFlag(ProxyItem::ShowFullPath); + + foreach(ProxyItem *root, m_root->children()) { + root->initDisplay(); + } +} + +// FIXME: optimize this later to insert all at once if possible +// maybe add a "bool emitSignals" to documentOpened +// and possibly use beginResetModel here? I dunno. + +void KateFileTreeModel::initModel() +{ + // add already existing documents + foreach( KTextEditor::Document* doc, Kate::application()->documentManager()->documents() ) + documentOpened( doc ); +} + +void KateFileTreeModel::clearModel() +{ + // remove all items + // can safely ignore documentClosed here + + beginRemoveRows(QModelIndex(), 0, m_root->childCount()-1); + + delete m_root; + m_root = new ProxyItemDir(QString("m_root"), 0); + + m_docmap.clear(); + m_viewHistory.clear(); + m_editHistory.clear(); + m_brushes.clear(); + + endRemoveRows(); +} + +void KateFileTreeModel::connectDocument(const KTextEditor::Document *doc) +{ + connect(doc, SIGNAL(documentNameChanged(KTextEditor::Document*)), this, SLOT(documentNameChanged(KTextEditor::Document*))); + connect(doc, SIGNAL(documentUrlChanged(KTextEditor::Document*)), this, SLOT(documentNameChanged(KTextEditor::Document*))); + connect(doc, SIGNAL(modifiedChanged(KTextEditor::Document*)), this, SLOT(documentModifiedChanged(KTextEditor::Document*))); + connect(doc, SIGNAL(modifiedOnDisk(KTextEditor::Document*,bool,KTextEditor::ModificationInterface::ModifiedOnDiskReason)), + this, SLOT(documentModifiedOnDisc(KTextEditor::Document*,bool,KTextEditor::ModificationInterface::ModifiedOnDiskReason)) ); +} + +QModelIndex KateFileTreeModel::docIndex(KTextEditor::Document *d) +{ + kDebug(debugArea()) << "BEGIN!"; + ProxyItem *item = m_docmap[d]; + if(!item) { + kDebug(debugArea()) << "doc" << d << "does not exist"; + return QModelIndex(); + } + + kDebug(debugArea()) << "END!"; + return createIndex(item->row(), 0, item); +} + +Qt::ItemFlags KateFileTreeModel::flags( const QModelIndex &index ) const +{ + Qt::ItemFlags flags = Qt::ItemIsEnabled; + + if(!index.isValid()) + return 0; + + ProxyItem *item = static_cast(index.internalPointer()); + if(item && !item->childCount()) { + flags |= Qt::ItemIsSelectable; + } + + return flags; +} + +#include "metatype_qlist_ktexteditor_document_pointer.h" + +QVariant KateFileTreeModel::data( const QModelIndex &index, int role ) const +{ + //kDebug(debugArea()) << "BEGIN!"; + if(!index.isValid()) { + kDebug(debugArea()) << "index is invalid!"; + return QVariant(); + } + + ProxyItem *item = static_cast(index.internalPointer()); + if(!item) { + kDebug(debugArea()) << "internal pointer is null!"; + return QVariant(); + } + + switch(role) { + case KateFileTreeModel::PathRole: + // allow to sort with hostname + path, bug 271488 + return (item->doc() && !item->doc()->url().isEmpty()) ? item->doc()->url().pathOrUrl() : item->path(); + + case KateFileTreeModel::DocumentRole: + return QVariant::fromValue(item->doc()); + + case KateFileTreeModel::OpeningOrderRole: + return item->row(); + + case KateFileTreeModel::DocumentTreeRole: + return QVariant::fromValue(item->docTree()); + + case Qt::DisplayRole: + // in list mode we want to use kate's fancy names. + if(m_listMode) { + return item->documentName(); + } else + return item->display(); + + case Qt::DecorationRole: + return item->icon(); + + case Qt::ToolTipRole: { + QString tooltip = item->path(); + if (item->flag(ProxyItem::DeletedExternally) || item->flag(ProxyItem::ModifiedExternally)) { + tooltip = i18nc("%1 is the full path", "

    %1

    The document has been modified by another application.

    ", item->path()); + } + + return tooltip; + } + + case Qt::ForegroundRole: { + KColorScheme colors(QPalette::Active); + if(!item->flag(ProxyItem::Dir) && (!item->doc() || item->doc()->openingError())) return colors.foreground(KColorScheme::InactiveText).color(); + } break; + + case Qt::BackgroundRole: + // TODO: do that funky shading the file list does... + if(m_shadingEnabled && m_brushes.contains(item)) + return m_brushes[item]; + break; + } + + //kDebug(debugArea()) << "END!"; + return QVariant(); +} + +QVariant KateFileTreeModel::headerData( int section, Qt::Orientation orientation, int role ) const +{ + Q_UNUSED(orientation); + Q_UNUSED(role); + + if(section == 0) + return QString("a header"); + + return QVariant(); +} + +int KateFileTreeModel::rowCount( const QModelIndex &parent ) const +{ + if(!parent.isValid()) + return m_root->childCount(); + + ProxyItem *item = static_cast(parent.internalPointer()); + if(!item) { + kDebug(debugArea()) << "internal pointer is invalid"; + return 0; + } + + return item->childCount(); +} + +int KateFileTreeModel::columnCount( const QModelIndex &parent ) const +{ + Q_UNUSED(parent); + return 1; +} + + +QModelIndex KateFileTreeModel::parent( const QModelIndex &index ) const +{ + if(!index.isValid()) { + kDebug(debugArea()) << "index is invalid"; + return QModelIndex(); + } + + ProxyItem *item = static_cast(index.internalPointer()); + if(!item) { + kDebug(debugArea()) << "internal pointer is invalid"; + return QModelIndex(); + } + + if(!item->parent()) { + kDebug(debugArea()) << "parent pointer is null"; + return QModelIndex(); + } + + if(item->parent() == m_root) + return QModelIndex(); + + return createIndex(item->parent()->row(), 0, item->parent()); +} + +QModelIndex KateFileTreeModel::index( int row, int column, const QModelIndex &parent ) const +{ + ProxyItem *p = 0; + if(column != 0) { + kDebug(debugArea()) << "column is invalid"; + return QModelIndex(); + } + + if(!parent.isValid()) + p = m_root; + else + p = static_cast(parent.internalPointer()); + + if(!p) { + kDebug(debugArea()) << "internal pointer is invalid"; + return QModelIndex(); + } + + if(row < 0 || row >= p->childCount()) { + kDebug(debugArea()) << "row is out of bounds (" << row << " < 0 || " << row << " >= " << p->childCount() << ")"; + return QModelIndex(); + } + + return createIndex(row, 0, p->child(row)); +} + +bool KateFileTreeModel::hasChildren( const QModelIndex & parent ) const +{ + if(!parent.isValid()) + return m_root->childCount() > 0; + + ProxyItem *item = static_cast(parent.internalPointer()); + if(!item) { + kDebug(debugArea()) << "internal pointer is null"; + return false; + } + + return item->childCount() > 0; +} + +bool KateFileTreeModel::isDir(const QModelIndex &index) +{ + if(!index.isValid()) + return true; + + ProxyItem *item = static_cast(index.internalPointer()); + if(!item) { + kDebug(debugArea()) << "internal pointer is null"; + return false; + } + + return item->flag(ProxyItem::Dir); +} + +bool KateFileTreeModel::listMode() +{ + return m_listMode; +} + +void KateFileTreeModel::setListMode(bool lm) +{ + if(lm != m_listMode) { + m_listMode = lm; + + clearModel(); + initModel(); + } +} + +void KateFileTreeModel::documentOpened(KTextEditor::Document *doc) +{ + QString path = doc->url().path(); + bool isEmpty = false; + QString host; + + if(doc->url().isEmpty()) { + path = doc->documentName(); + isEmpty = true; + } else { + host=doc->url().host(); + if (!host.isEmpty()) + path="["+host+"]"+path; + + } + + ProxyItem *item = new ProxyItem(path, 0); + + if(isEmpty) + item->setFlag(ProxyItem::Empty); + + m_debugmap[item] = item; + + item->setDoc(doc); + item->setHost(host); + kDebug(debugArea()) << "before add:" << item; + setupIcon(item); + handleInsert(item); + m_docmap[doc] = item; + connectDocument(doc); + + kDebug(debugArea()) << "after add:" << item; + +} + +void KateFileTreeModel::documentsOpened(const QList &docs) +{ + foreach(KTextEditor::Document *doc, docs) { + if (m_docmap.contains(doc)) { + documentNameChanged(doc); + } else { + documentOpened(doc); + } + } +} + +void KateFileTreeModel::documentModifiedChanged(KTextEditor::Document *doc) +{ + kDebug(debugArea()) << "BEGIN!"; + + ProxyItem *item = m_docmap[doc]; + if(!item) + return; + + if(doc->isModified()) { + item->setFlag(ProxyItem::Modified); + kDebug(debugArea()) << "modified!"; + } + else { + item->clearFlag(ProxyItem::Modified); + item->clearFlag(ProxyItem::ModifiedExternally); + item->clearFlag(ProxyItem::DeletedExternally); + kDebug(debugArea()) << "saved!"; + } + + setupIcon(item); + + QModelIndex idx = createIndex(item->row(), 0, item); + emit dataChanged(idx, idx); + + kDebug(debugArea()) << "END!"; +} + +void KateFileTreeModel::documentModifiedOnDisc(KTextEditor::Document *doc, bool modified, KTextEditor::ModificationInterface::ModifiedOnDiskReason reason ) +{ + Q_UNUSED(modified); + kDebug(debugArea()) << "BEGIN!"; + ProxyItem *item = m_docmap[doc]; + if(!item) + return; + + // This didn't do what I thought it did, on an ignore + // we'd get !modified causing the warning icons to disappear + if(!modified) { + item->clearFlag(ProxyItem::ModifiedExternally); + item->clearFlag(ProxyItem::DeletedExternally); + } else { + if(reason == KTextEditor::ModificationInterface::OnDiskDeleted) { + item->setFlag(ProxyItem::DeletedExternally); + kDebug(debugArea()) << "deleted!"; + } + else if(reason == KTextEditor::ModificationInterface::OnDiskModified) { + item->setFlag(ProxyItem::ModifiedExternally); + kDebug(debugArea()) << "modified!"; + } + else if(reason == KTextEditor::ModificationInterface::OnDiskCreated) { + kDebug(debugArea()) << "created!"; + // with out this, on "reload" we don't get the icons removed :( + item->clearFlag(ProxyItem::ModifiedExternally); + item->clearFlag(ProxyItem::DeletedExternally); + } + } + + setupIcon(item); + + QModelIndex idx = createIndex(item->row(), 0, item); + emit dataChanged(idx, idx); + kDebug(debugArea()) << "END!"; +} + +void KateFileTreeModel::documentActivated(KTextEditor::Document *doc) +{ + kDebug(debugArea()) << "BEGIN!"; + + if(!m_docmap.contains(doc)) { + kDebug(debugArea()) << "invalid doc" << doc; + return; + } + + ProxyItem *item = m_docmap[doc]; + kDebug(debugArea()) << "adding viewHistory" << item; + m_viewHistory.removeAll(item); + m_viewHistory.prepend(item); + + while (m_viewHistory.count() > 10) m_viewHistory.removeLast(); + + updateBackgrounds(); + + kDebug(debugArea()) << "END!"; +} + +void KateFileTreeModel::documentEdited(KTextEditor::Document *doc) +{ + kDebug(debugArea()) << "BEGIN!"; + + if(!m_docmap.contains(doc)) { + kDebug(debugArea()) << "invalid doc" << doc; + return; + } + + ProxyItem *item = m_docmap[doc]; + kDebug(debugArea()) << "adding editHistory" << item; + m_editHistory.removeAll(item); + m_editHistory.prepend(item); + while (m_editHistory.count() > 10) m_editHistory.removeLast(); + + updateBackgrounds(); + + kDebug(debugArea()) << "END!"; +} + +void KateFileTreeModel::slotAboutToDeleteDocuments(const QList &docs) +{ + foreach (const KTextEditor::Document *doc, docs) { + disconnect(doc, SIGNAL(documentNameChanged(KTextEditor::Document*)), this, SLOT(documentNameChanged(KTextEditor::Document*))); + disconnect(doc, SIGNAL(documentUrlChanged(KTextEditor::Document*)), this, SLOT(documentNameChanged(KTextEditor::Document*))); + disconnect(doc, SIGNAL(modifiedChanged(KTextEditor::Document*)), this, SLOT(documentModifiedChanged(KTextEditor::Document*))); + disconnect(doc, SIGNAL(modifiedOnDisk(KTextEditor::Document*,bool,KTextEditor::ModificationInterface::ModifiedOnDiskReason)), + this, SLOT(documentModifiedOnDisc(KTextEditor::Document*,bool,KTextEditor::ModificationInterface::ModifiedOnDiskReason)) ); + } +} + +void KateFileTreeModel::slotDocumentsDeleted(const QList &docs) +{ + foreach (const KTextEditor::Document *doc, docs) { + connectDocument(doc); + } +} + +class EditViewCount +{ + public: + EditViewCount(): edit(0), view(0) + {} + int edit; + int view; +}; + +void KateFileTreeModel::updateBackgrounds(bool force) +{ + if (!m_shadingEnabled && !force) return; + + kDebug(debugArea()) << "BEGIN!"; + + QMap helper; + int i = 1; + + foreach (ProxyItem *item, m_viewHistory) + { + helper[item].view = i; + if(!m_debugmap.contains(item)) { + kDebug(debugArea()) << "m_viewHistory contains an item that doesn't exist?" << item; + } + i++; + } + + i = 1; + foreach (ProxyItem *item, m_editHistory) + { + helper[item].edit = i; + if(!m_debugmap.contains(item)) { + kDebug(debugArea()) << "m_editHistory contains an item that doesn't exist?" << item; + } + i++; + } + + kDebug(debugArea()) << "m_editHistory contains " << m_editHistory.count()<<" elements"; + + QMap oldBrushes = m_brushes; + m_brushes.clear(); + + int hc = m_viewHistory.count(); + int ec = m_editHistory.count(); + + for (QMap::iterator it = helper.begin();it != helper.end();++it) + { + QColor shade( m_viewShade ); + QColor eshade( m_editShade ); + + if (it.value().edit > 0) + { + int v = hc - it.value().view; + int e = ec - it.value().edit + 1; + + e = e * e; + + int n = qMax(v + e, 1); + + shade.setRgb( + ((shade.red()*v) + (eshade.red()*e)) / n, + ((shade.green()*v) + (eshade.green()*e)) / n, + ((shade.blue()*v) + (eshade.blue()*e)) / n + ); + } + + // blend in the shade color; latest is most colored. + double t = double(hc - it.value().view + 1) / double(hc); + + m_brushes[it.key()] = QBrush(KColorUtils::mix(QPalette().color(QPalette::Base), shade, t)); +// kdDebug()<<"m_brushes[it.key()]"<row(), 0, item); + dataChanged(idx, idx); + } + + foreach(ProxyItem *item, oldBrushes.keys()) + { + QModelIndex idx = createIndex(item->row(), 0, item); + dataChanged(idx, idx); + } + + kDebug(debugArea()) << "END!"; +} + +void KateFileTreeModel::handleEmptyParents(ProxyItemDir *item) +{ + kDebug(debugArea()) << "BEGIN!"; + Q_ASSERT(item != 0); + + if(!item || !item->parent()) { + kDebug(debugArea()) << "parent" << item << "grandparent" << (item ? item->parent() : 0); + return; + } + + ProxyItemDir *parent = item->parent(); + //emit layoutAboutToBeChanged(); + + kDebug(debugArea()) << "item" << item << "parent" << parent; + while(parent) { + + kDebug(debugArea()) << "item" << item << "parent" << parent; + if(!item->childCount()) { + QModelIndex parent_index = parent == m_root ? QModelIndex() : createIndex(parent->row(), 0, parent); + beginRemoveRows(parent_index, item->row(), item->row()); + parent->remChild(item); + endRemoveRows(); + kDebug(debugArea()) << "deleted" << item; + delete item; + } + else { + // breakout early, if this node isn't empty, theres no use in checking its parents + kDebug(debugArea()) << "END!"; + return; + } + + item = parent; + parent = item->parent(); + } + + //emit layoutChanged(); + kDebug(debugArea()) << "END!"; +} + +void KateFileTreeModel::documentClosed(KTextEditor::Document *doc) +{ + QString path = doc->url().path(); + + if(!m_docmap.contains(doc)) { + kDebug(debugArea()) << "docmap doesn't contain doc" << doc; + return; + } + + kDebug(debugArea()) << path << m_docmap[doc]; + + if(m_shadingEnabled) { + ProxyItem *toRemove = m_docmap[doc]; + if(m_brushes.contains(toRemove)) { + m_brushes.remove(toRemove); + kDebug(debugArea()) << "removing brush" << toRemove; + } + + if(m_viewHistory.contains(toRemove)) { + m_viewHistory.removeAll(toRemove); + kDebug(debugArea()) << "removing viewHistory" << toRemove; + } + + if(m_editHistory.contains(toRemove)) { + m_editHistory.removeAll(toRemove); + kDebug(debugArea()) << "removing editHistory" << toRemove; + } + } + + ProxyItem *node = m_docmap[doc]; + ProxyItemDir *parent = node->parent(); + + QModelIndex parent_index = parent == m_root ? QModelIndex() : createIndex(parent->row(), 0, parent); + beginRemoveRows(parent_index, node->row(), node->row()); + node->parent()->remChild(node); + endRemoveRows(); + + m_debugmap.remove(node); + + delete node; + handleEmptyParents(parent); + + m_docmap.remove(doc); +} + +void KateFileTreeModel::documentNameChanged(KTextEditor::Document *doc) +{ + kDebug(debugArea()) << "BEGIN!"; + + if(!m_docmap.contains(doc)) { + kDebug(debugArea()) << "docmap doesn't contain doc" << doc; + return; + } + + ProxyItem *item = m_docmap[doc]; + QString path = doc->url().path(); + QString host; + if(doc->url().isEmpty()) { + kDebug(debugArea()) << "change to unnamed item"; + path = doc->documentName(); + item->setFlag(ProxyItem::Empty); + } + else { + item->clearFlag(ProxyItem::Empty); + host=doc->url().host(); + if (!host.isEmpty()) + path="["+host+"]"+path; + } + + kDebug(debugArea()) << item; + kDebug(debugArea()) << item->display() << "->" << path; + + + if(m_shadingEnabled) { + ProxyItem *toRemove = m_docmap[doc]; + if(m_brushes.contains(toRemove)) { + QBrush brush=m_brushes[toRemove]; + m_brushes.remove(toRemove); + m_brushes.insert(item,brush); + kDebug(debugArea()) << "removing brush" << toRemove; + } + + if(m_viewHistory.contains(toRemove)) { + int idx=m_viewHistory.indexOf(toRemove); + if (idx!=-1) + m_viewHistory.replace(idx,item); + kDebug(debugArea()) << "removing/replacing view history" << toRemove; + } + + if(m_editHistory.contains(toRemove)) { + int idx=m_editHistory.indexOf(toRemove); + if (idx!=-1) + m_editHistory.replace(idx,item); + kDebug(debugArea()) << "removing/replacing edit history" << toRemove; + } + } + + handleNameChange(item, path, host); + + triggerViewChangeAfterNameChange(); + + kDebug(debugArea()) << "END!"; +} + +ProxyItemDir *KateFileTreeModel::findRootNode(const QString &name, int r) +{ + QString base = name.section(QLatin1Char('/'), 0, -2); + foreach(ProxyItem *item, m_root->children()) { + QString path = item->path().section(QLatin1Char('/'), 0, -r); + if (!item->flag(ProxyItem::Host) && !QFileInfo(path).isAbsolute()) { + continue; + } + + // make sure we're actually matching against the right dir, + // previously the check below would match /foo/xy against /foo/x + // and return /foo/x rather than /foo/xy + // this seems a bit hackish, but is the simplest way to solve the + // current issue. + path += QLatin1Char('/'); + + if(name.startsWith(path) && item->flag(ProxyItem::Dir)) { + return static_cast(item); + } + } + + return 0; +} + +ProxyItemDir *KateFileTreeModel::findChildNode(ProxyItemDir *parent, const QString &name) +{ + Q_ASSERT(parent != 0); + + if(!parent || !parent->childCount()) { + kDebug(debugArea()) << "invalid parent or no children" << parent; + return 0; + } + + foreach(ProxyItem *item, parent->children()) { + if(item->display() == name) { + if(!item->flag(ProxyItem::Dir)) { + kDebug(debugArea()) << "found" << item << "but its not a dir?"; + return 0; + } + + kDebug(debugArea()) << "found" << item; + return static_cast(item); + } + } + + kDebug(debugArea()) << "!found:" << name; + return 0; +} + +void KateFileTreeModel::insertItemInto(ProxyItemDir *root, ProxyItem *item) +{ + kDebug(debugArea()) << "BEGIN!"; + + Q_ASSERT(root != 0); + Q_ASSERT(item != 0); + + QString sep; + QString tail = item->path(); + tail.remove(0, root->path().length()); + QStringList parts = tail.split(QLatin1Char('/'), QString::SkipEmptyParts); + ProxyItemDir *ptr = root; + QStringList current_parts; + current_parts.append(root->path()); + + // seems this can be empty, see bug 286191 + if (!parts.isEmpty()) + parts.pop_back(); + + kDebug(debugArea()) << "creating tree for" << item; + foreach(const QString &part, parts) { + current_parts.append(part); + ProxyItemDir *find = findChildNode(ptr, part); + if(!find) { + QString new_name = current_parts.join(QLatin1String("/")); + QModelIndex parent_index = createIndex(ptr->row(), 0, ptr); + kDebug(debugArea()) << "adding" << part << "to" << ptr; + beginInsertRows(ptr == m_root ? QModelIndex() : parent_index, ptr->childCount(), ptr->childCount()); + ptr = new ProxyItemDir(new_name, ptr); + endInsertRows(); + } + else { + ptr = find; + } + } + + kDebug(debugArea()) << "adding" << item << "to" << ptr; + QModelIndex parent_index = createIndex(ptr->row(), 0, ptr); + beginInsertRows(ptr == m_root ? QModelIndex() : parent_index, ptr->childCount(), ptr->childCount()); + ptr->addChild(item); + endInsertRows(); + + kDebug(debugArea()) << "END!"; +} + +void KateFileTreeModel::handleInsert(ProxyItem *item) +{ + kDebug(debugArea()) << "BEGIN!"; + + Q_ASSERT(item != 0); + + if(m_listMode) { + kDebug(debugArea()) << "list mode, inserting into m_root"; + beginInsertRows(QModelIndex(), m_root->childCount(), m_root->childCount()); + m_root->addChild(item); + endInsertRows(); + return; + } + + if(item->flag(ProxyItem::Empty)) { + kDebug(debugArea()) << "empty item"; + beginInsertRows(QModelIndex(), m_root->childCount(), m_root->childCount()); + m_root->addChild(item); + endInsertRows(); + return; + } + + ProxyItemDir *root = findRootNode(item->path()); + if(root) { + kDebug(debugArea()) << "got a root, inserting into it"; + insertItemInto(root, item); + } else { + kDebug(debugArea()) << "creating a new root"; + + // trim off trailing file and dir + QString base = item->path().section(QLatin1Char('/'), 0, -2); + + // create new root + ProxyItemDir *new_root = new ProxyItemDir(base, 0); + new_root->setHost(item->host()); + + // add new root to m_root + kDebug(debugArea()) << "add" << new_root << "to m_root"; + beginInsertRows(QModelIndex(), m_root->childCount(), m_root->childCount()); + m_root->addChild(new_root); + endInsertRows(); + + // same fix as in findRootNode, try to match a full dir, instead of a partial path + base += QLatin1Char('/'); + + // try and merge existing roots with the new root node. + kDebug(debugArea()) << "attempting to merge some existing roots"; + foreach(ProxyItem *root, m_root->children()) { + if(root == new_root || !root->flag(ProxyItem::Dir)) + continue; + + if(root->path().startsWith(base)) { + kDebug(debugArea()) << "removing" << root << "from m_root"; + beginRemoveRows(QModelIndex(), root->row(), root->row()); + m_root->remChild(root); + endRemoveRows(); + + kDebug(debugArea()) << "adding" << root << "to" << new_root; + //beginInsertRows(new_root_index, new_root->childCount(), new_root->childCount()); + // this can't use new_root->addChild directly, or it'll potentially miss a bunch of subdirs + insertItemInto(new_root, root); + //endInsertRows(); + } + } + + // add item to new root + kDebug(debugArea()) << "adding" << item << "to" << new_root; + // have to call begin/endInsertRows here, or the new item won't show up. + QModelIndex new_root_index = createIndex(new_root->row(), 0, new_root); + beginInsertRows(new_root_index, new_root->childCount(), new_root->childCount()); + new_root->addChild(item); + endInsertRows(); + + } + + kDebug(debugArea()) << "END!"; +} + +void KateFileTreeModel::handleNameChange(ProxyItem *item, const QString &new_name, const QString& new_host) +{ + kDebug(debugArea()) << "BEGIN!"; + + Q_ASSERT(item != 0); + + if(m_listMode) { + item->setPath(new_name); + item->setHost(new_host); + QModelIndex idx = createIndex(item->row(), 0, item); + setupIcon(item); + emit dataChanged(idx, idx); + kDebug(debugArea()) << "list mode, short circuit"; + return; + } + + // for some reason we get useless name changes + if(item->path() == new_name) { + kDebug(debugArea()) << "bogus name change"; + return; + } + + // in either case (new/change) we want to remove the item from its parent + + ProxyItemDir *parent = item->parent(); + if(!parent) { + item->setPath(new_name); + item->setHost(new_host); + kDebug(debugArea()) << "ERROR: item" << item << "does not have a parent?"; + return; + } + + item->setPath(new_name); + item->setHost(new_host); + + kDebug(debugArea()) << "removing" << item << "from" << parent; + QModelIndex parent_index = parent == m_root ? QModelIndex() : createIndex(parent->row(), 0, parent); + beginRemoveRows(parent_index, item->row(), item->row()); + parent->remChild(item); + endRemoveRows(); + + // remove empty parent nodes here, recursively. + handleEmptyParents(parent); + + // set new path + //item->setPath(new_name); + + // clear all but Empty flag + if(item->flag(ProxyItem::Empty)) + item->setFlags(ProxyItem::Empty); + else + item->setFlags(ProxyItem::None); + + setupIcon(item); + + // new item + kDebug(debugArea()) << "inserting" << item; + handleInsert(item); + + kDebug(debugArea()) << "END!"; +} + +void KateFileTreeModel::setupIcon(ProxyItem *item) +{ + kDebug(debugArea()) << "BEGIN!"; + + Q_ASSERT(item != 0); + + QStringList emblems; + QString icon_name; + + if(item->flag(ProxyItem::Modified)) { + icon_name = "document-save"; + } + else { + KUrl url = item->path(); + icon_name = KMimeType::findByUrl(url, 0, true, true)->iconName(); + } + + if(item->flag(ProxyItem::ModifiedExternally) || item->flag(ProxyItem::DeletedExternally)) { + emblems << "emblem-important"; + kDebug(debugArea()) << "modified!"; + } + + item->setIcon(KIcon(icon_name, 0, emblems)); + + kDebug(debugArea()) << "END!"; +} + +// kate: space-indent on; indent-width 2; replace-tabs on; mixed-indent off; diff --git a/kate/src/filetree/katefiletreemodel.h b/kate/src/filetree/katefiletreemodel.h new file mode 100644 index 00000000..a1756ab7 --- /dev/null +++ b/kate/src/filetree/katefiletreemodel.h @@ -0,0 +1,128 @@ +/* This file is part of the KDE project + Copyright (C) 2010 Thomas Fjellstrom + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KATEFILETREEMODEL_H +#define KATEFILETREEMODEL_H + +#include +#include + +#include +namespace KTextEditor { + class Document; +} + +class ProxyItem; +class ProxyItemDir; + +QDebug operator<<(QDebug dbg, ProxyItem *item); +QDebug operator<<(QDebug dbg, ProxyItemDir *item); + +class KateFileTreeModel : public QAbstractItemModel +{ + Q_OBJECT + + public: + enum { DocumentRole = Qt::UserRole+1, PathRole, OpeningOrderRole, DocumentTreeRole }; + + KateFileTreeModel(QObject *p); + virtual ~KateFileTreeModel(); + + virtual Qt::ItemFlags flags( const QModelIndex & index ) const; + virtual QVariant data( const QModelIndex & index, int role = Qt::DisplayRole ) const; + virtual QVariant headerData( int section, Qt::Orientation orientation, int role = Qt::DisplayRole ) const; + virtual int rowCount( const QModelIndex & parent = QModelIndex() ) const; + virtual int columnCount( const QModelIndex & parent = QModelIndex() ) const; + + virtual QModelIndex parent( const QModelIndex & index ) const; + virtual QModelIndex index( int row, int column, const QModelIndex & parent = QModelIndex() ) const; + virtual bool hasChildren ( const QModelIndex & parent = QModelIndex() ) const; + + QModelIndex docIndex(KTextEditor::Document *); + + bool isDir(const QModelIndex &index); + + bool listMode(); + void setListMode(bool); + + bool shadingEnabled(); + void setShadingEnabled(bool); + + QColor editShade(); + void setEditShade(QColor); + + QColor viewShade(); + void setViewShade(QColor); + + bool showFullPathOnRoots(void); + void setShowFullPathOnRoots(bool); + + public Q_SLOTS: + void documentOpened(KTextEditor::Document *); + void documentClosed(KTextEditor::Document *); + void documentNameChanged(KTextEditor::Document *); + void documentModifiedChanged(KTextEditor::Document *); + void documentModifiedOnDisc(KTextEditor::Document*, bool, KTextEditor::ModificationInterface::ModifiedOnDiskReason); + void documentsOpened(const QList &); + + /* used strictly for the item coloring */ + void documentActivated(KTextEditor::Document*); + void documentEdited(KTextEditor::Document*); + + void slotAboutToDeleteDocuments(const QList &); + void slotDocumentsDeleted(const QList &); + + Q_SIGNALS: + void triggerViewChangeAfterNameChange(); + private: + ProxyItemDir *m_root; + QHash m_docmap; + QString m_base; + + bool m_shadingEnabled; + + QList m_viewHistory; + QList m_editHistory; + QMap m_brushes; + + QColor m_editShade; + QColor m_viewShade; + + bool m_listMode; + + ProxyItemDir *findRootNode(const QString &name, int r = 1); + ProxyItemDir *findChildNode(ProxyItemDir *parent, const QString &name); + void insertItemInto(ProxyItemDir *root, ProxyItem *item); + void handleInsert(ProxyItem *item); + void handleNameChange(ProxyItem *item, const QString &new_name, const QString& new_host); + void handleEmptyParents(ProxyItemDir *item); + void setupIcon(ProxyItem *item); + + void updateBackgrounds(bool force = false); + + void initModel(); + void clearModel(); + void connectDocument(const KTextEditor::Document *); + + // Debug crap + QHash m_debugmap; +}; + +#endif /* KATEFILETREEMODEL_H */ + +// kate: space-indent on; indent-width 2; replace-tabs on; mixed-indent off; diff --git a/kate/src/filetree/katefiletreeplugin.cpp b/kate/src/filetree/katefiletreeplugin.cpp new file mode 100644 index 00000000..a4c28f9a --- /dev/null +++ b/kate/src/filetree/katefiletreeplugin.cpp @@ -0,0 +1,543 @@ +/* This file is part of the KDE project + Copyright (C) 2010 Thomas Fjellstrom + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +//BEGIN Includes + +#include "katefiletreeplugin.h" +#include "moc_katefiletreeplugin.cpp" +#include "katefiletree.h" +#include "katefiletreemodel.h" +#include "katefiletreeproxymodel.h" +#include "katefiletreeconfigpage.h" + +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include + +#include + +#include "katefiletreedebug.h" + +//END Includes + +K_PLUGIN_FACTORY(KateFileTreeFactory, registerPlugin();) +K_EXPORT_PLUGIN(KateFileTreeFactory(KAboutData("filetree","katefiletreeplugin",ki18n("Document Tree"), "0.1", ki18n("Show open documents in a tree"), KAboutData::License_LGPL_V2)) ) + +//BEGIN KateFileTreePlugin +KateFileTreePlugin::KateFileTreePlugin(QObject* parent, const QList&) + : Kate::Plugin ((Kate::Application*)parent), + m_fileCommand(0) +{ + KTextEditor::CommandInterface* iface = + qobject_cast(Kate::application()->editor()); + if (iface) { + m_fileCommand = new KateFileTreeCommand(this); + iface->registerCommand(m_fileCommand); + } +} + +KateFileTreePlugin::~KateFileTreePlugin() +{ + m_settings.save(); + KTextEditor::CommandInterface* iface = + qobject_cast(Kate::application()->editor()); + if (iface && m_fileCommand) { + iface->unregisterCommand(m_fileCommand); + } +} + +Kate::PluginView *KateFileTreePlugin::createView (Kate::MainWindow *mainWindow) +{ + KateFileTreePluginView* view = new KateFileTreePluginView (mainWindow, this); + connect(view, SIGNAL(destroyed(QObject*)), this, SLOT(viewDestroyed(QObject*))); + connect(m_fileCommand, SIGNAL(showToolView()), view, SLOT(showToolView())); + connect(m_fileCommand, SIGNAL(slotDocumentPrev()), view->tree(), SLOT(slotDocumentPrev())); + connect(m_fileCommand, SIGNAL(slotDocumentNext()), view->tree(), SLOT(slotDocumentNext())); + connect(m_fileCommand, SIGNAL(slotDocumentFirst()), view->tree(), SLOT(slotDocumentFirst())); + connect(m_fileCommand, SIGNAL(slotDocumentLast()), view->tree(), SLOT(slotDocumentLast())); + connect(m_fileCommand, SIGNAL(switchDocument(QString)), view->tree(), SLOT(switchDocument(QString))); + m_views.append(view); + + return view; +} + +void KateFileTreePlugin::viewDestroyed(QObject* view) +{ + // do not access the view pointer, since it is partially destroyed already + m_views.removeAll(static_cast(view)); +} + +uint KateFileTreePlugin::configPages() const +{ + return 1; +} + + +QString KateFileTreePlugin::configPageName (uint number) const +{ + if(number != 0) + return QString(); + + return QString(i18n("Documents")); +} + +QString KateFileTreePlugin::configPageFullName (uint number) const +{ + if(number != 0) + return QString(); + + return QString(i18n("Configure Documents")); +} + +KIcon KateFileTreePlugin::configPageIcon (uint number) const +{ + if(number != 0) + return KIcon(); + + return KIcon("view-list-tree"); +} + +Kate::PluginConfigPage *KateFileTreePlugin::configPage (uint number, QWidget *parent, const char *name) +{ + Q_UNUSED(name); + if(number != 0) + return 0; + + KateFileTreeConfigPage *page = new KateFileTreeConfigPage(parent, this); + return page; +} + +const KateFileTreePluginSettings &KateFileTreePlugin::settings() +{ + return m_settings; +} + +void KateFileTreePlugin::applyConfig(bool shadingEnabled, QColor viewShade, QColor editShade, bool listMode, int sortRole, bool showFullPath) +{ + // save to settings + m_settings.setShadingEnabled(shadingEnabled); + m_settings.setViewShade(viewShade); + m_settings.setEditShade(editShade); + + m_settings.setListMode(listMode); + m_settings.setSortRole(sortRole); + m_settings.setShowFullPathOnRoots(showFullPath); + m_settings.save(); + + // update views + foreach(KateFileTreePluginView *view, m_views) { + view->setHasLocalPrefs(false); + view->model()->setShadingEnabled( shadingEnabled ); + view->model()->setViewShade( viewShade ); + view->model()->setEditShade( editShade ); + view->setListMode( listMode ); + view->proxy()->setSortRole( sortRole ); + view->model()->setShowFullPathOnRoots( showFullPath ); + } +} + + +//END KateFileTreePlugin + + + + +//BEGIN KateFileTreePluginView +KateFileTreePluginView::KateFileTreePluginView (Kate::MainWindow *mainWindow, KateFileTreePlugin *plug) + : Kate::PluginView (mainWindow) + , Kate::XMLGUIClient(KateFileTreeFactory::componentData()) + , m_loadingDocuments(false) + , m_plug(plug) +{ + // init console + kDebug(debugArea()) << "BEGIN: mw:" << mainWindow; + + m_toolView = mainWindow->createToolView (plug,"kate_private_plugin_katefiletreeplugin", Kate::MainWindow::Left, SmallIcon("document-open"), i18n("Documents")); + m_fileTree = new KateFileTree(m_toolView); + m_fileTree->setSortingEnabled(true); + + connect(m_fileTree, SIGNAL(activateDocument(KTextEditor::Document*)), + this, SLOT(activateDocument(KTextEditor::Document*))); + + connect(m_fileTree, SIGNAL(viewModeChanged(bool)), this, SLOT(viewModeChanged(bool))); + connect(m_fileTree, SIGNAL(sortRoleChanged(int)), this, SLOT(sortRoleChanged(int))); + + m_documentModel = new KateFileTreeModel(this); + m_proxyModel = new KateFileTreeProxyModel(this); + m_proxyModel->setSourceModel(m_documentModel); + m_proxyModel->setDynamicSortFilter(true); + + m_documentModel->setShowFullPathOnRoots(m_plug->settings().showFullPathOnRoots()); + m_documentModel->setShadingEnabled(m_plug->settings().shadingEnabled()); + m_documentModel->setViewShade(m_plug->settings().viewShade()); + m_documentModel->setEditShade(m_plug->settings().editShade()); + + Kate::DocumentManager *dm = Kate::application()->documentManager(); + + connect(dm, SIGNAL(documentWillBeDeleted(KTextEditor::Document*)), + m_documentModel, SLOT(documentClosed(KTextEditor::Document*))); + + connect(dm, SIGNAL(documentCreated(KTextEditor::Document*)), + this, SLOT(documentOpened(KTextEditor::Document*))); + connect(dm, SIGNAL(documentWillBeDeleted(KTextEditor::Document*)), + this, SLOT(documentClosed(KTextEditor::Document*))); + connect(dm, SIGNAL(aboutToLoadDocuments()), this, SLOT(slotAboutToLoadDocuments())); + connect(dm, SIGNAL(documentsLoaded(QList)), + this, SLOT(slotDocumentsLoaded(QList))); + connect(dm, SIGNAL(aboutToDeleteDocuments(QList)), + m_documentModel, SLOT(slotAboutToDeleteDocuments(QList))); + connect(dm, SIGNAL(documentsDeleted(QList)), + m_documentModel, SLOT(slotDocumentsDeleted(QList))); + + connect(m_documentModel,SIGNAL(triggerViewChangeAfterNameChange()),this,SLOT(viewChanged())); + m_fileTree->setModel(m_proxyModel); + + m_fileTree->setDragEnabled(false); + m_fileTree->setDragDropMode(QAbstractItemView::InternalMove); + m_fileTree->setDropIndicatorShown(false); + + m_fileTree->setSelectionMode(QAbstractItemView::SingleSelection); + + connect( m_fileTree->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), m_fileTree, SLOT(slotCurrentChanged(QModelIndex,QModelIndex))); + + connect(mainWindow, SIGNAL(viewChanged()), this, SLOT(viewChanged())); + + KAction *show_active = actionCollection()->addAction("filetree_show_active_document", mainWindow); + show_active->setText(i18n("&Show Active")); + show_active->setIcon(KIcon("folder-sync")); + connect( show_active, SIGNAL(triggered(bool)), this, SLOT(showActiveDocument()) ); + + /** + * back + forward + */ + actionCollection()->addAction( KStandardAction::Back, "filetree_prev_document", m_fileTree, SLOT(slotDocumentPrev()) )->setText(i18n("Previous Document")); + actionCollection()->addAction( KStandardAction::Forward, "filetree_next_document", m_fileTree, SLOT(slotDocumentNext()) )->setText(i18n("Next Document")); + + mainWindow->guiFactory()->addClient(this); + + m_proxyModel->setSortRole(Qt::DisplayRole); + + m_proxyModel->sort(0, Qt::AscendingOrder); + m_proxyModel->invalidate(); +} + +KateFileTreePluginView::~KateFileTreePluginView () +{ + mainWindow()->guiFactory()->removeClient(this); + + // clean up tree and toolview + delete m_fileTree->parentWidget(); + // delete m_toolView; + // and TreeModel + delete m_documentModel; +} + +KateFileTreeModel *KateFileTreePluginView::model() +{ + return m_documentModel; +} + +KateFileTreeProxyModel *KateFileTreePluginView::proxy() +{ + return m_proxyModel; +} + +KateFileTree *KateFileTreePluginView::tree() +{ + return m_fileTree; +} + +void KateFileTreePluginView::documentOpened(KTextEditor::Document *doc) +{ + kDebug(debugArea()) << "open" << doc; + + if (!m_loadingDocuments) { + m_documentModel->documentOpened(doc); + m_proxyModel->invalidate(); + } + + connect(doc, SIGNAL(modifiedChanged(KTextEditor::Document*)), + m_documentModel, SLOT(documentEdited(KTextEditor::Document*))); +} + +void KateFileTreePluginView::documentClosed(KTextEditor::Document *doc) +{ + kDebug(debugArea()) << "close" << doc; + m_proxyModel->invalidate(); +} + +void KateFileTreePluginView::viewChanged() +{ + kDebug(debugArea()) << "BEGIN!"; + + KTextEditor::View *view = mainWindow()->activeView(); + if(!view) + return; + + KTextEditor::Document *doc = view->document(); + QModelIndex index = m_proxyModel->docIndex(doc); + kDebug(debugArea()) << "selected doc=" << doc << index; + + QString display = m_proxyModel->data(index, Qt::DisplayRole).toString(); + kDebug(debugArea()) << "display="<documentActivated(doc); + + m_fileTree->selectionModel()->setCurrentIndex(index, QItemSelectionModel::ClearAndSelect); + + m_fileTree->scrollTo(index); + + while(index != QModelIndex()) { + m_fileTree->expand(index); + index = index.parent(); + } + + kDebug(debugArea()) << "END!"; +} + +void KateFileTreePluginView::setListMode(bool listMode) +{ + kDebug(debugArea()) << "BEGIN"; + + if(listMode) { + kDebug(debugArea()) << "listMode"; + m_documentModel->setListMode(true); + m_fileTree->setRootIsDecorated(false); + } + else { + kDebug(debugArea()) << "treeMode"; + m_documentModel->setListMode(false); + m_fileTree->setRootIsDecorated(true); + } + + m_proxyModel->sort(0, Qt::AscendingOrder); + m_proxyModel->invalidate(); + + kDebug(debugArea()) << "END"; +} + +void KateFileTreePluginView::viewModeChanged(bool listMode) +{ + kDebug(debugArea()) << "BEGIN"; + setHasLocalPrefs(true); + setListMode(listMode); + kDebug(debugArea()) << "END"; +} + +void KateFileTreePluginView::sortRoleChanged(int role) +{ + kDebug(debugArea()) << "BEGIN"; + setHasLocalPrefs(true); + m_proxyModel->setSortRole(role); + m_proxyModel->invalidate(); + kDebug(debugArea()) << "END"; +} + +void KateFileTreePluginView::activateDocument(KTextEditor::Document *doc) +{ + mainWindow()->activateView(doc); +} + +void KateFileTreePluginView::showToolView() +{ + mainWindow()->showToolView(m_toolView); + m_toolView->setFocus(); +} + +void KateFileTreePluginView::hideToolView() +{ + mainWindow()->hideToolView(m_toolView); + mainWindow()->centralWidget()->setFocus(); +} + +void KateFileTreePluginView::switchDocument(const QString &doc) +{ + m_fileTree->switchDocument(doc); +} + +void KateFileTreePluginView::showActiveDocument() +{ + // hack? + viewChanged(); + // make the tool view show if it was hidden + showToolView(); +} + +bool KateFileTreePluginView::hasLocalPrefs() +{ + return m_hasLocalPrefs; +} + +void KateFileTreePluginView::setHasLocalPrefs(bool h) +{ + m_hasLocalPrefs = h; +} + +void KateFileTreePluginView::readSessionConfig(KConfigBase* config, const QString& group) +{ + KConfigGroup g = config->group(group); + + if(g.exists()) + m_hasLocalPrefs = true; + else + m_hasLocalPrefs = false; + + // we chain to the global settings by using them as the defaults + // here in the session view config loading. + const KateFileTreePluginSettings &defaults = m_plug->settings(); + + bool listMode = g.readEntry("listMode", defaults.listMode()); + + setListMode(listMode); + + int sortRole = g.readEntry("sortRole", defaults.sortRole()); + m_proxyModel->setSortRole(sortRole); + +} + +void KateFileTreePluginView::writeSessionConfig(KConfigBase* config, const QString& group) +{ + KConfigGroup g = config->group(group); + + if(m_hasLocalPrefs) { + g.writeEntry("listMode", QVariant(m_documentModel->listMode())); + g.writeEntry("sortRole", int(m_proxyModel->sortRole())); + } + else { + g.deleteEntry("listMode"); + g.deleteEntry("sortRole"); + } + + g.sync(); +} + +void KateFileTreePluginView::slotAboutToLoadDocuments() +{ + m_loadingDocuments = true; +} + +void KateFileTreePluginView::slotDocumentsLoaded(const QList &docs) +{ + m_documentModel->documentsOpened(docs); + m_loadingDocuments = false; + viewChanged(); +} + +//END KateFileTreePluginView + +//BEGIN KateFileTreeCommand +KateFileTreeCommand::KateFileTreeCommand(QObject *parent) + : QObject(parent), KTextEditor::Command() +{ +} + +const QStringList& KateFileTreeCommand::cmds() +{ + static QStringList sl; + + if (sl.empty()) { + sl << "ls" + << "b" << "buffer" + << "bn" << "bnext" << "bp" << "bprevious" + << "tabn" << "tabnext" << "tabp" << "tabprevious" + << "bf" << "bfirst" << "bl" << "blast" + << "tabf" << "tabfirst" << "tabl" << "tablast"; + } + + return sl; +} + +bool KateFileTreeCommand::exec(KTextEditor::View * /*view*/, const QString &cmd, QString & /*msg*/) +{ + // create list of args + QStringList args(cmd.split(' ', QString::KeepEmptyParts)); + QString command = args.takeFirst(); // same as cmd if split failed + QString argument = args.join(QString(' ')); + + if (command == "ls") { + emit showToolView(); + } else if (command == "b" || command == "buffer") { + emit switchDocument(argument); + } else if (command == "bp" || command == "bprevious" || + command == "tabp" || command == "tabprevious") { + emit slotDocumentPrev(); + } else if (command == "bn" || command == "bnext" || + command == "tabn" || command == "tabnext") { + emit slotDocumentNext(); + } else if (command == "bf" || command == "bfirst" || + command == "tabf" || command == "tabfirst") { + emit slotDocumentFirst(); + } else if (command == "bl" || command == "blast" || + command == "tabl" || command == "tablast") { + emit slotDocumentLast(); + } + + return true; +} + +bool KateFileTreeCommand::help(KTextEditor::View * /*view*/, const QString &cmd, QString &msg) +{ + if (cmd == "b" || cmd == "buffer") { + msg = i18n("

    b,buffer — Edit document N from the document list

    " + "

    Usage: b[uffer] [N]

    "); + return true; + } else if (cmd == "bp" || cmd == "bprevious" || + cmd == "tabp" || cmd == "tabprevious") { + msg = i18n("

    bp,bprev — previous buffer

    " + "

    Usage: bp[revious] [N]

    " + "

    Goes to [N]th previous document (\"buffer\") in document list.

    " + "

    [N] defaults to one.

    " + "

    Wraps around the start of the document list.

    "); + return true; + } else if (cmd == "bn" || cmd == "bnext" || + cmd == "tabn" || cmd == "tabnext") { + msg = i18n("

    bn,bnext — switch to next document

    " + "

    Usage: bn[ext] [N]

    " + "

    Goes to [N]th next document (\"buffer\") in document list." + "[N] defaults to one.

    " + "

    Wraps around the end of the document list.

    "); + return true; + } else if (cmd == "bf" || cmd == "bfirst" || + cmd == "tabf" || cmd == "tabfirst") { + msg = i18n("

    bf,bfirst — first document

    " + "

    Usage: bf[irst]

    " + "

    Goes to the first document (\"buffer\") in document list.

    "); + return true; + } else if (cmd == "bl" || cmd == "blast" || + cmd == "tabl" || cmd == "tablast") { + msg = i18n("

    bl,blast — last document

    " + "

    Usage: bl[ast]

    " + "

    Goes to the last document (\"buffer\") in document list.

    "); + return true; + } + + return false; +} +//END KateFileTreeCommand + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/src/filetree/katefiletreeplugin.desktop b/kate/src/filetree/katefiletreeplugin.desktop new file mode 100644 index 00000000..a86a1a64 --- /dev/null +++ b/kate/src/filetree/katefiletreeplugin.desktop @@ -0,0 +1,116 @@ +[Desktop Entry] +Type=Service +ServiceTypes=Kate/Plugin +X-KDE-Library=katefiletreeplugin +X-Kate-Version=2.8 +X-Kate-LoadAlways=True +Name=File Tree +Name[ar]=شجرة الملفات +Name[bg]=Дърво с файлове +Name[bs]=Stablo datoteka +Name[ca]=Arbre de fitxers +Name[ca@valencia]=Arbre de fitxers +Name[cs]=Strom souborů +Name[da]=Filtræ +Name[de]=Dateibaum +Name[el]=Δένδρο αρχείων +Name[en_GB]=File Tree +Name[es]=Árbol de archivos +Name[et]=Failipuu +Name[eu]=Fitxategien zuhaitza +Name[fi]=Tiedostopuu +Name[fr]=Arborescence de fichiers +Name[ga]=Crann Comhad +Name[gl]=Árbore de ficheiros +Name[he]=עץ קבצים +Name[hu]=Fastruktúra +Name[ia]=Arbore de file +Name[is]=Skráagreinar +Name[it]=Albero di file +Name[ja]=ファイルツリー +Name[kk]=Файлдар бұтағы +Name[km]=មែកធាង​របស់​ពុម្ព​អក្សរ​ +Name[ko]=파일 트리 +Name[lt]=Failų medis +Name[lv]=Datņu koks +Name[mr]=फाईल वृक्ष +Name[nb]=Fil-tre +Name[nds]=Dateiboom +Name[nl]=Bestandsboomstructuur +Name[nn]=Fil-tre +Name[pa]=ਫਾਇਲ ਟਰੀ +Name[pl]=Drzewo plików +Name[pt]=Árvore de Ficheiros +Name[pt_BR]=Árvore de arquivos +Name[ro]=Arborele fișierelor +Name[ru]=Дерево файлов +Name[si]=ගොනු ගස +Name[sk]=Strom súborov +Name[sl]=Drevo datotek +Name[sr]=Стабло фајлова +Name[sr@ijekavian]=Стабло фајлова +Name[sr@ijekavianlatin]=Stablo fajlova +Name[sr@latin]=Stablo fajlova +Name[sv]=Filträd +Name[tg]=Дарахти файлҳо +Name[tr]=Dosya Ağacı +Name[ug]=ھۆججەت شېخى +Name[uk]=Дерево файлів +Name[wa]=Coxhlaedje di fitchîs +Name[x-test]=xxFile Treexx +Name[zh_CN]=文件树 +Name[zh_TW]=檔案樹狀圖 +Comment=Displays the open documents in a tree +Comment[ar]=يعرض المستندات المفتوحة في شجرة +Comment[bg]=Показване на отворените документи в дърво +Comment[bs]=Prikazuje otvorene dokumente kao stablo +Comment[ca]=Mostra els documents oberts en un arbre +Comment[ca@valencia]=Mostra els documents oberts en un arbre +Comment[cs]=Zobrazí otevřené dokumenty ve stromu +Comment[da]=Viser de åbne dokumenter som et træ +Comment[de]=Zeigt geöffnete Dokumente als Baum an +Comment[el]=Εμφανίζει τα ανοιγμένα έγγραφα σε δενδρική δομή +Comment[en_GB]=Displays the open documents in a tree +Comment[es]=Muestra los documentos abiertos en un árbol +Comment[et]=Avatud dokumentide näitamine puuna +Comment[eu]=Irekitako dokumentuak zuhaitz batean bistaratzen ditu +Comment[fi]=Näyttää avoimet tiedostot puuna +Comment[fr]=Affiche les documents ouverts dans une arborescence +Comment[ga]=Taispeánann sé cáipéisí oscailte i gcrann +Comment[gl]=Mostra nunha árbore os documentos abertos +Comment[he]=הצג את הקבצים הפתוחים בעץ +Comment[hu]=Megnyitott dokumentumok megjelenítése fastruktúrában +Comment[ia]=Monstra documentos aperite in un arbore +Comment[it]=Visualizza i documenti aperti in un albero +Comment[kk]=Ашылған құжаттарды бұтақтап көрсету +Comment[km]=បើក​បង្ហាញ​ឯកសារ​ក្នុង​មែក​ធាង​ +Comment[ko]=열린 문서를 트리 구조로 표시합니다 +Comment[lt]=Rodo atvertus dokumentus medyje +Comment[lv]=Parāda atvērtos dokumentus datņu kokā +Comment[mr]=उघडे दस्तऐवज वृक्षाप्रमाणे दर्शवितो +Comment[nb]=Viser de åpne dokumentene i et tre +Comment[nds]=Boomansicht för opmaakt Dokmenten +Comment[nl]=Toont de geopende documenten in een boomstructuur +Comment[nn]=Viser dei opne dokumenta i eit tre +Comment[pa]=ਖੁੱਲ੍ਹੇ ਡੌਕੂਮੈਂਟ ਟਰੀ ਵਿੱਚ ਵੇਖਾਓ +Comment[pl]=Wyświetla otwarte dokumenty w drzewie +Comment[pt]=Mostra os documentos abertos em árvore +Comment[pt_BR]=Exibe os documentos aberto na árvore +Comment[ro]=Afișează documentele deschise într-un arbore +Comment[ru]=Просмотр открытых документов в дереве папок +Comment[si]=විවෘත ලේඛන වෘක්‍ෂයක පෙන්වයි +Comment[sk]=Ukázať otvorené dokumenty v strome +Comment[sl]=Odprte dokumente prikaže v drevesu +Comment[sr]=Приказује отворене документе као стабло +Comment[sr@ijekavian]=Приказује отворене документе као стабло +Comment[sr@ijekavianlatin]=Prikazuje otvorene dokumente kao stablo +Comment[sr@latin]=Prikazuje otvorene dokumente kao stablo +Comment[sv]=Visar öppna dokument i ett träd +Comment[tg]=Ҳуҷҷатҳои кушодашударо ҳамчун дарахт нишон медиҳад +Comment[tr]=Açık belgeleri bir ağaç yapısı içinde gösterir +Comment[ug]=ئېچىلغان پۈتۈكلەرنى شاخسىمان ھالەتتە كۆرسەت +Comment[uk]=Показує ієрархію відкритих документів +Comment[wa]=Håyene les documints drovous e coxhlaedje +Comment[x-test]=xxDisplays the open documents in a treexx +Comment[zh_CN]=以树状显示打开的文档 +Comment[zh_TW]=以樹狀圖顯示開啟的文件 diff --git a/kate/src/filetree/katefiletreeplugin.h b/kate/src/filetree/katefiletreeplugin.h new file mode 100644 index 00000000..84a2f6ad --- /dev/null +++ b/kate/src/filetree/katefiletreeplugin.h @@ -0,0 +1,157 @@ +/* This file is part of the KDE project + Copyright (C) 2010 Thomas Fjellstrom + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KATE_FILETREE_PLUGIN_H +#define KATE_FILETREE_PLUGIN_H + +#include + +#include +#include +#include +#include +#include +#include + +#include "katefiletreepluginsettings.h" + +class KateFileTree; +class KateFileTreeModel; +class KateFileTreeProxyModel; +class KateFileTreeConfigPage; +class KateFileTreePluginView; +class KateFileTreeCommand; + +class KateFileTreePlugin: public Kate::Plugin, public Kate::PluginConfigPageInterface +{ + Q_OBJECT + Q_INTERFACES(Kate::PluginConfigPageInterface) + + public: + explicit KateFileTreePlugin( QObject* parent = 0, const QList& = QList() ); + virtual ~KateFileTreePlugin(); + + Kate::PluginView *createView (Kate::MainWindow *mainWindow); + + virtual uint configPages() const; + + virtual QString configPageName (uint number = 0) const; + virtual QString configPageFullName (uint number = 0) const; + virtual KIcon configPageIcon (uint number = 0) const; + virtual Kate::PluginConfigPage *configPage (uint number = 0, QWidget *parent = 0, const char *name = 0 ); + + const KateFileTreePluginSettings &settings(); + + void applyConfig(bool shadingEnabled, QColor viewShade, QColor editShade, bool listMode, int sortRole, bool showFulPath); + + public Q_SLOTS: + void viewDestroyed(QObject* view); + + private: + QList m_views; + KateFileTreePluginSettings m_settings; + KateFileTreeCommand* m_fileCommand; +}; + +class KateFileTreePluginView : public Kate::PluginView, public Kate::XMLGUIClient +{ + Q_OBJECT + + public: + /** + * Constructor. + */ + KateFileTreePluginView (Kate::MainWindow *mainWindow, KateFileTreePlugin *plug); + + /** + * Virtual destructor. + */ + ~KateFileTreePluginView (); + + virtual void readSessionConfig (KConfigBase* config, const QString& groupPrefix); + virtual void writeSessionConfig (KConfigBase* config, const QString& groupPrefix); + + /** + * The file tree model. + * @return the file tree model + */ + KateFileTreeModel *model(); + /** + * The file tree proxy model. + * @return the file tree proxy model + */ + KateFileTreeProxyModel *proxy(); + /** + * The file tree. + * @return the file tree + */ + KateFileTree *tree(); + + void setListMode(bool listMode); + + bool hasLocalPrefs(); + void setHasLocalPrefs(bool); + + private: + QWidget *m_toolView; + KateFileTree *m_fileTree; + KateFileTreeProxyModel *m_proxyModel; + KateFileTreeModel *m_documentModel; + bool m_hasLocalPrefs; + bool m_loadingDocuments; + KateFileTreePlugin *m_plug; + + private Q_SLOTS: + void showToolView(); + void hideToolView(); + void switchDocument(const QString &doc); + void showActiveDocument(); + void activateDocument(KTextEditor::Document *); + void viewChanged(); + void documentOpened(KTextEditor::Document *); + void documentClosed(KTextEditor::Document *); + void viewModeChanged(bool); + void sortRoleChanged(int); + void slotAboutToLoadDocuments(); + void slotDocumentsLoaded(const QList &); +}; + +class KateFileTreeCommand : public QObject, public KTextEditor::Command +{ + Q_OBJECT + + public: + KateFileTreeCommand(QObject *parent); + + Q_SIGNALS: + void showToolView(); + void slotDocumentPrev(); + void slotDocumentNext(); + void slotDocumentFirst(); + void slotDocumentLast(); + void switchDocument(const QString&); + + public: + const QStringList& cmds(); + bool exec(KTextEditor::View *view, const QString &cmd, QString &msg); + bool help(KTextEditor::View *view, const QString &cmd, QString &msg); +}; + +#endif //KATE_FILETREE_PLUGIN_H + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/src/filetree/katefiletreepluginsettings.cpp b/kate/src/filetree/katefiletreepluginsettings.cpp new file mode 100644 index 00000000..bb61793a --- /dev/null +++ b/kate/src/filetree/katefiletreepluginsettings.cpp @@ -0,0 +1,116 @@ +/* This file is part of the KDE project + Copyright (C) 2010 Thomas Fjellstrom + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "katefiletreepluginsettings.h" +#include +#include +#include +#include "katefiletreedebug.h" + +KateFileTreePluginSettings::KateFileTreePluginSettings() + : m_group(KGlobal::config(), "filetree") +{ + KColorScheme colors(QPalette::Active); + QColor bg = colors.background().color(); + QColor viewShade = KColorUtils::tint(bg, colors.foreground(KColorScheme::VisitedText).color(), 0.5); + QColor editShade = KColorUtils::tint(bg, colors.foreground(KColorScheme::ActiveText).color(), 0.5); + + m_shadingEnabled = m_group.readEntry("shadingEnabled", true); + m_viewShade = m_group.readEntry("viewShade", viewShade); + m_editShade = m_group.readEntry("editShade", editShade); + + m_listMode = m_group.readEntry("listMode", false); + m_sortRole = m_group.readEntry("sortRole", int(Qt::DisplayRole)); + + m_showFullPathOnRoots = m_group.readEntry("showFullPathOnRoots", false); +} + +void KateFileTreePluginSettings::save() +{ + m_group.writeEntry("shadingEnabled", m_shadingEnabled); + m_group.writeEntry("viewShade", m_viewShade); + m_group.writeEntry("editShade", m_editShade); + m_group.writeEntry("listMode", m_listMode); + m_group.writeEntry("sortRole", m_sortRole); + m_group.writeEntry("showFullPathOnRoots", m_showFullPathOnRoots); + + kDebug(debugArea()) << "save config!"; + m_group.sync(); +} + +bool KateFileTreePluginSettings::shadingEnabled() const +{ + return m_shadingEnabled; +} + +void KateFileTreePluginSettings::setShadingEnabled(bool shadingEnabled) +{ + m_shadingEnabled = shadingEnabled; +} + +const QColor &KateFileTreePluginSettings::viewShade() const +{ + return m_viewShade; +} + +void KateFileTreePluginSettings::setViewShade(const QColor &viewShade) +{ + m_viewShade = viewShade; +} + +const QColor &KateFileTreePluginSettings::editShade() const +{ + return m_editShade; +} + +void KateFileTreePluginSettings::setEditShade(const QColor &editShade) +{ + m_editShade = editShade; +} + +bool KateFileTreePluginSettings::listMode() const +{ + return m_listMode; +} + +void KateFileTreePluginSettings::setListMode(bool listMode) +{ + m_listMode = listMode; +} + +int KateFileTreePluginSettings::sortRole() const +{ + return m_sortRole; +} + +void KateFileTreePluginSettings::setSortRole(int sortRole) +{ + m_sortRole = sortRole; +} + +bool KateFileTreePluginSettings::showFullPathOnRoots() const +{ + return m_showFullPathOnRoots; +} + +void KateFileTreePluginSettings::setShowFullPathOnRoots(bool s) +{ + m_showFullPathOnRoots = s; +} + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/src/filetree/katefiletreepluginsettings.h b/kate/src/filetree/katefiletreepluginsettings.h new file mode 100644 index 00000000..e7bc92f9 --- /dev/null +++ b/kate/src/filetree/katefiletreepluginsettings.h @@ -0,0 +1,66 @@ +/* This file is part of the KDE project + Copyright (C) 2010 Thomas Fjellstrom + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KATE_FILETREE_PLUGIN_SETTINGS_H +#define KATE_FILETREE_PLUGIN_SETTINGS_H + +#include +#include + +// Geh, should make this into a proper class, setters and getters. +class KateFileTreePluginSettings +{ + public: + KateFileTreePluginSettings(); + + void save(); + + bool shadingEnabled() const; + void setShadingEnabled(bool); + + const QColor &viewShade() const; + void setViewShade(const QColor &); + + const QColor &editShade() const; + void setEditShade(const QColor &); + + bool listMode() const; + void setListMode(bool); + + int sortRole() const; + void setSortRole(int); + + bool showFullPathOnRoots() const; + void setShowFullPathOnRoots(bool); + + private: + KConfigGroup m_group; + + bool m_shadingEnabled; + QColor m_viewShade; + QColor m_editShade; + + bool m_listMode; + int m_sortRole; + + bool m_showFullPathOnRoots; +}; + +#endif //KATE_FILETREE_PLUGIN_H + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/src/filetree/katefiletreeproxymodel.cpp b/kate/src/filetree/katefiletreeproxymodel.cpp new file mode 100644 index 00000000..07474471 --- /dev/null +++ b/kate/src/filetree/katefiletreeproxymodel.cpp @@ -0,0 +1,77 @@ +/* This file is part of the KDE project + Copyright (C) 2010 Thomas Fjellstrom + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "katefiletreeproxymodel.h" +#include "katefiletreemodel.h" +#include "katefiletreedebug.h" + +#include +#include + +KateFileTreeProxyModel::KateFileTreeProxyModel(QObject *parent) + : QSortFilterProxyModel(parent) +{ + kDebug(debugArea()) << "BEGIN!"; +} + +bool KateFileTreeProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const +{ + //kDebug(debugArea()) << ": BEGIN!"; + KateFileTreeModel *model = static_cast(sourceModel()); + + bool left_isdir = model->isDir(left); + bool right_isdir = model->isDir(right); + + // in tree mode, there will be parent nodes, we want to put those first + if(left_isdir != right_isdir) { + return ((left_isdir - right_isdir)) > 0; + } + + switch(sortRole()) { + case Qt::DisplayRole: { + QString left_name = model->data(left).toString(); + QString right_name = model->data(right).toString(); + return KStringHandler::naturalCompare(left_name, right_name, Qt::CaseInsensitive) < 0; + } + + case KateFileTreeModel::PathRole: { + QString left_name = model->data(left, KateFileTreeModel::PathRole).toString(); + QString right_name = model->data(right, KateFileTreeModel::PathRole).toString(); + return KStringHandler::naturalCompare(left_name, right_name, Qt::CaseInsensitive) < 0; + } + + case KateFileTreeModel::OpeningOrderRole: + return (left.row() - right.row()) < 0; + } + + kDebug(debugArea()) << "this shouldn't happen!"; + return false; +} + +QModelIndex KateFileTreeProxyModel::docIndex(KTextEditor::Document *doc) +{ + kDebug(debugArea()) << "!"; + return mapFromSource(static_cast(sourceModel())->docIndex(doc)); +} + +bool KateFileTreeProxyModel::isDir(const QModelIndex &index) +{ + return static_cast(sourceModel())->isDir(mapToSource(index)); +} + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/src/filetree/katefiletreeproxymodel.h b/kate/src/filetree/katefiletreeproxymodel.h new file mode 100644 index 00000000..8395e7cf --- /dev/null +++ b/kate/src/filetree/katefiletreeproxymodel.h @@ -0,0 +1,44 @@ +/* This file is part of the KDE project + Copyright (C) 2010 Thomas Fjellstrom + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KATE_FILETREEPROXYMODEL_H +#define KATE_FILETREEPROXYMODEL_H + +#include + +namespace KTextEditor { + class Document; +} + +class KateFileTreeProxyModel : public QSortFilterProxyModel +{ + Q_OBJECT + + public: + KateFileTreeProxyModel(QObject *p = 0); + QModelIndex docIndex(KTextEditor::Document *); + bool isDir(const QModelIndex &i); + + protected: + bool lessThan(const QModelIndex &left, const QModelIndex &right) const; + +}; + +#endif /* KATE_FILETREEPROXYMODEL_H */ + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/src/filetree/metatype_qlist_ktexteditor_document_pointer.h b/kate/src/filetree/metatype_qlist_ktexteditor_document_pointer.h new file mode 100644 index 00000000..092ef8bb --- /dev/null +++ b/kate/src/filetree/metatype_qlist_ktexteditor_document_pointer.h @@ -0,0 +1,24 @@ +/* This file is part of the KDE project + Copyright (C) 2010 Thomas Fjellstrom + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef METATYPE_QLIST_KTEXTEDITOR_DOCUMENT_POINTER_H +#define METATYPE_QLIST_KTEXTEDITOR_DOCUMENT_POINTER_H + +Q_DECLARE_METATYPE(QList) + +#endif diff --git a/kate/src/filetree/ui.rc b/kate/src/filetree/ui.rc new file mode 100644 index 00000000..b1c12edc --- /dev/null +++ b/kate/src/filetree/ui.rc @@ -0,0 +1,21 @@ + + + + + + &View + + + + + + + +Main Toolbar + + + + + + + diff --git a/kate/src/interfaces/kate/CMakeLists.txt b/kate/src/interfaces/kate/CMakeLists.txt new file mode 100644 index 00000000..b19d4e94 --- /dev/null +++ b/kate/src/interfaces/kate/CMakeLists.txt @@ -0,0 +1,12 @@ +# install development headers +install( + FILES + application.h + documentmanager.h + mainwindow.h + plugin.h + pluginconfigpageinterface.h + pluginmanager.h + DESTINATION ${KDE4_INCLUDE_INSTALL_DIR}/kate + COMPONENT Devel +) diff --git a/kate/src/interfaces/kate/application.cpp b/kate/src/interfaces/kate/application.cpp new file mode 100644 index 00000000..081e4b97 --- /dev/null +++ b/kate/src/interfaces/kate/application.cpp @@ -0,0 +1,96 @@ +/* This file is part of the KDE project + Copyright (C) 2001 Christoph Cullmann + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "application.h" +#include "moc_application.cpp" + +#include "documentmanager.h" +#include "mainwindow.h" +#include "pluginmanager.h" + +#include "../app/kateapp.h" +#include "../app/katedocmanager.h" +#include "../app/katepluginmanager.h" +#include "../app/katemainwindow.h" +#include + +namespace Kate +{ + + class PrivateApplication + { + public: + PrivateApplication () + {} + + ~PrivateApplication () + { + } + + KateApp *app; + }; + + Application::Application (void *application) : QObject ((KateApp *) application) + { + d = new PrivateApplication; + d->app = (KateApp *) application; + } + + Application::~Application () + { + delete d; + } + + DocumentManager *Application::documentManager () + { + return d->app->documentManager ()->documentManager (); + } + + PluginManager *Application::pluginManager () + { + return d->app->pluginManager ()->pluginManager (); + } + + MainWindow *Application::activeMainWindow () + { + if (!d->app->activeMainWindow()) + return 0; + + return d->app->activeMainWindow()->mainWindow(); + } + + const QList &Application::mainWindows () const + { + return d->app->mainWindowsInterfaces (); + } + + Application *application () + { + return KateApp::self()->application (); + } + + KTextEditor::Editor *Application::editor () + { + return d->app->documentManager ()->editor(); + } + + +} + +// kate: space-indent on; indent-width 2; replace-tabs on; + diff --git a/kate/src/interfaces/kate/application.h b/kate/src/interfaces/kate/application.h new file mode 100644 index 00000000..c351addb --- /dev/null +++ b/kate/src/interfaces/kate/application.h @@ -0,0 +1,139 @@ +/* This file is part of the KDE project + Copyright (C) 2001 Christoph Cullmann + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef _KATE_APPLICATION_INCLUDE_ +#define _KATE_APPLICATION_INCLUDE_ + +#include + +#include + +#include + +namespace KTextEditor +{ + class Editor; +} + +/** + * \brief Namespace for Kate application interfaces. + * + * This namespace contains interfaces for plugin developers for the Kate + * application. + */ +namespace Kate +{ + + class DocumentManager; + class PluginManager; + class MainWindow; + + /** + * \brief Central interface to the application. + * + * The class Application is central as it provides access to the core + * application, which includes the + * - document manager, access it with documentManager() + * - plugin manager, access it with pluginManager() + * - main windows, access a main window with mainWindow() or activeMainWindow() + * - used editor component, access it with editor(). + * + * To access the application use the global accessor function application(). + * You should never have to create an instance of this class yourself. + * + * \author Christoph Cullmann \ + */ + class KATE_EXPORT Application : public QObject + { + friend class PrivateApplication; + + Q_OBJECT + + public: + /** + * Construtor. + * + * The constructor is internally used by the Kate application, so it is + * of no interest for plugin developers. Plugin developers should use the + * global accessor application() instead. + * + * \internal + */ + Application (void *application); + + /** + * Virtual desctructor. + */ + virtual ~Application (); + + public: + /** + * Accessor to the document manager. + * There is always an document manager, so it is not necessary + * to test the returned value for NULL. + * \return a pointer to the document manager + */ + Kate::DocumentManager *documentManager (); + + /** + * Accessor to the plugin manager. + * There is always an plugin manager, so it is not necessary + * to test the returned value for NULL. + * \return a pointer to the plugin manager + */ + Kate::PluginManager *pluginManager (); + + /** + * Accessor to the global editor part. + * There is always an editor part, so it is not necessary + * to test the returned value for NULL. + * \return KTextEditor component + */ + KTextEditor::Editor *editor(); + + /** + * Accessor to the active mainwindow. + * There is always an active mainwindow, so it is not necessary + * to test the returned value for NULL. + * \return a pointer to the active mainwindow + */ + Kate::MainWindow *activeMainWindow (); + + /** + * Get a list of all mainwindows. At least one entry, always. + * @return all mainwindows + * @see activeMainWindow() + */ + const QList &mainWindows () const; + + private: + class PrivateApplication *d; + }; + + /** + * Global accessor to the application object. Always existing during + * lifetime of a plugin. + * \return application object + */ + KATE_EXPORT Application *application (); + +} + +#endif +// kate: space-indent on; indent-width 2; replace-tabs on; + diff --git a/kate/src/interfaces/kate/documentmanager.cpp b/kate/src/interfaces/kate/documentmanager.cpp new file mode 100644 index 00000000..970939cd --- /dev/null +++ b/kate/src/interfaces/kate/documentmanager.cpp @@ -0,0 +1,88 @@ +/* This file is part of the KDE project + Copyright (C) 2001 Christoph Cullmann + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "documentmanager.h" +#include "moc_documentmanager.cpp" + +#include "plugin.h" +#include "pluginmanager.h" + +#include "application.h" + +#include "../app/katedocmanager.h" + +namespace Kate +{ + + class PrivateDocumentManager + { + public: + PrivateDocumentManager () + {} + + ~PrivateDocumentManager () + {} + + KateDocManager *docMan; + }; + + DocumentManager::DocumentManager (void *documentManager) : QObject ((KateDocManager*) documentManager) + { + d = new PrivateDocumentManager (); + d->docMan = (KateDocManager*) documentManager; + } + + DocumentManager::~DocumentManager () + { + delete d; + } + + const QList &DocumentManager::documents () const + { + return d->docMan->documentList (); + } + + KTextEditor::Document *DocumentManager::findUrl (const KUrl &url) const + { + return d->docMan->findDocument (url); + } + + KTextEditor::Document *DocumentManager::openUrl(const KUrl&url, const QString &encoding) + { + return d->docMan->openUrl (url, encoding); + } + + bool DocumentManager::closeDocument(KTextEditor::Document *document) + { + return d->docMan->closeDocument (document); + } + + bool DocumentManager::closeDocumentList(QList documents) + { + return d->docMan->closeDocumentList(documents); + } + + DocumentManager *documentManager () + { + return application()->documentManager (); + } + +} + +// kate: space-indent on; indent-width 2; replace-tabs on; + diff --git a/kate/src/interfaces/kate/documentmanager.h b/kate/src/interfaces/kate/documentmanager.h new file mode 100644 index 00000000..9f832f49 --- /dev/null +++ b/kate/src/interfaces/kate/documentmanager.h @@ -0,0 +1,189 @@ +/* This file is part of the KDE project + Copyright (C) 2001 Christoph Cullmann + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef DOCUMENTMANAGER_H +#define DOCUMENTMANAGER_H + +#include + +#include +#include + +namespace KTextEditor +{ + class Document; +} + +namespace Kate +{ + /** + * \brief Interface for the document manager. + * + * This interface provides access to Kate's document manager. The document + * manager manages all documents. Use document() get a given document, + * activeDocument() to retrieve the active document. Check with isOpen() + * whether an URL is opened and use findDocument() to get it. To get the + * number of managed documents use documents(). + * + * Open new documents with openUrl() and close a document with closeDocument() + * or closeAllDocuments(). Several signals are provided, documentChanged() is + * emitted whenever the document's content changed, documentCreated() when a + * new document was created and documentDeleted() when a document was closed. + * + * To access the document manager use the global accessor function + * documentManager() or Application::documentManager(). You should never have + * to create an instance of this class yourself. + * + * \author Christoph Cullmann \ + */ + class KATE_EXPORT DocumentManager : public QObject + { + friend class PrivateDocumentManager; + + Q_OBJECT + + public: + /** + * Construtor. + * + * The constructor is internally used by the Kate application, so it is + * of no interest for plugin developers. Plugin developers should use the + * global accessor pluginManager() instead. + * + * \param documentManager internal usage + * + * \internal + */ + DocumentManager ( void *documentManager ); + /** + * Virtual destructor. + */ + virtual ~DocumentManager (); + + public: + /** + * Get a list of all documents. + * @return all documents + */ + const QList &documents () const; + + /** + * Get the document with the URL \p url. + * if multiple documents match the searched url, return the first found one... + * \param url the document's URL + * \return the document with the given \p url or NULL, if no such document + * is in the document manager's internal list. + */ + KTextEditor::Document *findUrl (const KUrl &url) const; + + /** + * Open the document \p url with the given \p encoding. + * if the url is empty, a new empty document will be created + * \param url the document's url + * \param encoding the preferred encoding. If encoding is QString() the + * encoding will be guessed or the default encoding will be used. + * \return a pointer to the created document + */ + KTextEditor::Document *openUrl (const KUrl &url, const QString &encoding = QString()); + + /** + * Close the given \p document. + * \param document the document to be closed + * \return \e true on success, otherwise \e false + */ + bool closeDocument (KTextEditor::Document* document); + + /** + * Close a list of documents. If any of them are modified, show a "save modified" dialog. + * \param documents list of documents to be closed + * \return \e true on success, otherwise \e false + */ + bool closeDocumentList (QList documents); + + // + // SIGNALS !!! + // +#ifndef Q_MOC_RUN +#undef signals +#define signals public +#endif + signals: +#ifndef Q_MOC_RUN +#undef signals +#define signals protected +#endif + + /** + * This signal is emitted when the \p document was created. + */ + void documentCreated (KTextEditor::Document *document); + + /** + * This signal is emitted before a \p document which should be closed is deleted + * The document is still accessible and usable, but it will be deleted + * after this signal was send. + */ + void documentWillBeDeleted (KTextEditor::Document *document); + + /** + * This signal is emitted when the \p document has been deleted. + * + * Warning !!! DO NOT ACCESS THE DATA REFERENCED BY THE POINTER, IT IS ALREADY INVALID + * Use the pointer only to remove mappings in hash or maps + */ + void documentDeleted (KTextEditor::Document *document); + + /** + * will be loading a bunch of documents, you can step back for a while + */ + void aboutToLoadDocuments(); + + /** + * bunch of documents have been loaded, you can come back + */ + void documentsLoaded(const QList &); + + /** + * signal which documents are going to be deleted soon + * + * note that the batch can be interupted in the middle and only some + * of the documents may be actually deleted. See @documentsDeleted signal. + */ + void aboutToDeleteDocuments(const QList &); + + /** + * the batch closing signal for @aboutToDeleteDocuments + * @documents the documents that weren't deleted after all + */ + void documentsDeleted(const QList &documents); + + private: + class PrivateDocumentManager *d; + }; + + /** + * Global accessor to the document manager object. + * \return document manager object + */ + KATE_EXPORT DocumentManager *documentManager (); + +} + +#endif // DOCUMENTMANAGER_H + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/src/interfaces/kate/mainwindow.cpp b/kate/src/interfaces/kate/mainwindow.cpp new file mode 100644 index 00000000..b5ddb0cd --- /dev/null +++ b/kate/src/interfaces/kate/mainwindow.cpp @@ -0,0 +1,141 @@ +/* This file is part of the KDE project + Copyright (C) 2001 Christoph Cullmann + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "mainwindow.h" +#include "moc_mainwindow.cpp" + +#include "documentmanager.h" +#include "plugin.h" +#include "pluginmanager.h" + +#include "../app/katemainwindow.h" +#include "../app/kateviewmanager.h" +#include "pluginconfigpageinterface.h" +namespace Kate +{ + + class PrivateMainWindow + { + public: + PrivateMainWindow () + {} + + ~PrivateMainWindow () + { + } + + KateMainWindow *win; + }; + + MainWindow::MainWindow (void *mainWindow) : QObject ((KateMainWindow*) mainWindow) + { + d = new PrivateMainWindow; + d->win = (KateMainWindow*) mainWindow; + connect(d->win, SIGNAL(unhandledShortcutOverride(QEvent*)), + this, SIGNAL(unhandledShortcutOverride(QEvent*))); + } + + MainWindow::~MainWindow () + { + delete d; + } + + KXMLGUIFactory *MainWindow::guiFactory() const + { + return d->win->guiFactory(); + } + + QWidget *MainWindow::window() const + { + return d->win; + } + + QWidget *MainWindow::centralWidget() const + { + return d->win->centralWidget(); + } + + const QList &MainWindow::views () const + { + return d->win->viewManager()->viewList(); + } + + KTextEditor::View *MainWindow::activeView() + { + return d->win->viewManager()->activeView(); + } + + KTextEditor::View *MainWindow::activateView ( KTextEditor::Document *doc ) + { + return d->win->viewManager()->activateView( doc ); + } + + KTextEditor::View *MainWindow::openUrl (const KUrl &url, const QString &encoding) + { + return d->win->viewManager()->openUrlWithView (url, encoding); + } + + QWidget *MainWindow::createToolView (const QString &identifier, MainWindow::Position pos, const QPixmap &icon, const QString &text) + { + return createToolView((Kate::Plugin*)0,identifier, pos, icon, text); + } + + QWidget *MainWindow::createToolView (Kate::Plugin* plugin, const QString &identifier, MainWindow::Position pos, const QPixmap &icon, const QString &text) + { + return d->win->createToolView (plugin,identifier, (KMultiTabBar::KMultiTabBarPosition)pos, icon, text); + } + + + bool MainWindow::moveToolView (QWidget *widget, MainWindow::Position pos) + { + if (!widget || !qobject_cast(widget)) + return false; + + return d->win->moveToolView (qobject_cast(widget), (KMultiTabBar::KMultiTabBarPosition)pos); + } + + bool MainWindow::showToolView(QWidget *widget) + { + if (!widget || !qobject_cast(widget)) + return false; + + return d->win->showToolView (qobject_cast(widget)); + } + + bool MainWindow::hideToolView(QWidget *widget) + { + if (!widget || !qobject_cast(widget)) + return false; + + return d->win->hideToolView (qobject_cast(widget)); + } + + void MainWindow::showPluginConfigPage(PluginConfigPageInterface *configpageinterface,uint id) + { + d->win->showPluginConfigPage(configpageinterface,id); + } + + PluginView *MainWindow::pluginView (const QString &name) + { + return d->win->pluginView (name); + } + +} + +// kate: space-indent on; indent-width 2; replace-tabs on; + diff --git a/kate/src/interfaces/kate/mainwindow.h b/kate/src/interfaces/kate/mainwindow.h new file mode 100644 index 00000000..670e93ae --- /dev/null +++ b/kate/src/interfaces/kate/mainwindow.h @@ -0,0 +1,291 @@ +/* This file is part of the KDE project + Copyright (C) 2001 Christoph Cullmann + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef _KATE_MAINWINDOW_INCLUDE_ +#define _KATE_MAINWINDOW_INCLUDE_ + +#include + +#include +#include + +#include +#include + +#include + +namespace KTextEditor +{ + class View; + class Document; +} + +namespace Kate +{ + class PluginConfigPageInterface; + class Plugin; + class PluginView; + + /** + * \brief Interface to a mainwindow. + * + * \section mainwindow_intro Introduction + * The class MainWindow represents a toplevel window, with menu bar, + * statusbar etc, get it with window(). A mainwindow usually has an active + * View, access it with activeView(). To set another active view use + * activateView(). + * + * \section mainwindow_toolviews Toolviews + * + * It is possible to embedd new toolviews into a mainwindow. To create a + * toolview use createToolView(), then you can move, hide or show the toolview + * by using moveToolView(), hideToolView() or showToolView(). + * + * With guiFactory() you can access the KXMLGUIFactory framework and add + * gui clients. + * + * To access a mainwindow use the Application object. + * You should never have to create an instance of this class yourself. + * + * \author Christoph Cullmann \ + * \see KXMLGUIFactory + */ + class KATE_EXPORT MainWindow : public QObject + { + friend class PrivateMainWindow; + + Q_OBJECT + + public: + /** + * Constructor. + * + * Create a new mainwindow. You as a plugin developer should never have + * to create a new mainwindow yourself. Access the mainwindows via the + * global Application. + * \param mainWindow internal usage + * \internal + */ + MainWindow (void *mainWindow); + /** + * Virtual destructor. + */ + virtual ~MainWindow (); + + public: /*these are slots for kjs*/ + /** + * Accesstor to the XMLGUIFactory. + * \return the mainwindow's KXMLGUIFactory. + */ + KXMLGUIFactory *guiFactory() const; + + /** + * Get the toplevel widget. + * \return the kate main window. + */ + class QWidget *window() const; + + /** + * Access the widget (in the middle of the four sidebars) in which the + * editor component and the KateTabBar are embedded. This widget is a + * KVBox, so other child widgets can be embedded themselves under the + * editor widget. + * + * \return the central widget + */ + class QWidget *centralWidget() const; + + /** + * Get a list of all views. + * @return all views + */ + const QList &views () const; + + /* + * View stuff, here all stuff belong which allows to + * access and manipulate the KTextEditor::View's we have in this windows + */ + public: + /** + * Access the active view. + * \return active view + */ + KTextEditor::View *activeView (); + + /** + * Activate the view with the corresponding \p document. + * If none exist for this document, create one + * \param document the document + * \return activated view of this document + */ + KTextEditor::View *activateView ( KTextEditor::Document *document ); + + /** + * Open the document \p url with the given \p encoding. + * \param url the document's url + * \param encoding the preferred encoding. If encoding is QString() the + * encoding will be guessed or the default encoding will be used. + * \return a pointer to the created view for the new document, if a document + * with this url is already existing, its view will be activated + */ + KTextEditor::View *openUrl (const KUrl &url, const QString &encoding = QString()); + + // + // SIGNALS !!! + // +#ifndef Q_MOC_RUN +#undef signals +#define signals public +#endif + signals: +#ifndef Q_MOC_RUN +#undef signals +#define signals protected +#endif + + /** + * This signal is emitted whenever the active view changes. + */ + // TODO: KDE5 add View* parameter: + // void viewChanged (KTextEditor::View* view); + void viewChanged (); + + /** + * This signal is emitted whenever a new view is created + * @since 4.2 + */ + void viewCreated(KTextEditor::View * view); + + /** + * This signal is emitted for every unhandled ShortcutOverride in a view + */ + void unhandledShortcutOverride (QEvent *e); + + /** + * This signal is emitted when a Plugin::View is created for this main window. + * + * @param name name of plugin + * @param pluginView the new plugin view + */ + void pluginViewCreated (const QString &name, Kate::PluginView *pluginView); + + /** + * This signal is emitted when the Plugin::View got deleted. + * + * @param name name of plugin + * @param pluginView the deleted plugin view + * + * Warning !!! DO NOT ACCESS THE DATA REFERENCED BY THE POINTER, IT IS ALREADY INVALID + * Use the pointer only to remove mappings in hash or maps + */ + void pluginViewDeleted (const QString &name, Kate::PluginView *pluginView); + + /* + * ToolView stuff, here all stuff belong which allows to + * add/remove and manipulate the toolview of this main windows + */ + public: + /** + * Toolview position. + * A toolview can only be at one side at a time. + */ + enum Position { + Left = 0, /**< Left side. */ + Right = 1, /**< Right side. */ + Top = 2, /**< Top side. */ + Bottom = 3 /**< Bottom side. */ + }; + + /** + * Create a new toolview with unique \p identifier at side \p pos + * with \p icon and caption \p text. Use the returned widget to embedd + * your widgets. + * \param identifier unique identifier for this toolview + * \param pos position for the toolview, if we are in session restore, + * this is only a preference + * \param icon icon to use in the sidebar for the toolview + * \param text translated text (i18n()) to use in addition to icon + * \return created toolview on success, otherwise NULL + * @deprecated + */ + QWidget *createToolView (const QString &identifier, MainWindow::Position pos, const QPixmap &icon, const QString &text); + + /** + * Create a new toolview with unique \p identifier at side \p pos + * with \p icon and caption \p text. Use the returned widget to embedd + * your widgets. + * \param plugin which owns this tool view + * \param identifier unique identifier for this toolview + * \param pos position for the toolview, if we are in session restore, + * this is only a preference + * \param icon icon to use in the sidebar for the toolview + * \param text translated text (i18n()) to use in addition to icon + * \return created toolview on success, otherwise NULL + */ + QWidget *createToolView (Kate::Plugin* plugin, const QString &identifier, MainWindow::Position pos, const QPixmap &icon, const QString &text); + + /** + * Move the toolview \p widget to position \p pos. + * \param widget the toolview to move, where the widget was constructed + * by createToolView(). + * \param pos new position to move widget to + * \return \e true on success, otherwise \e false + */ + bool moveToolView (QWidget *widget, MainWindow::Position pos); + + /** + * Show the toolview \p widget. + * \param widget the toolview to show, where the widget was constructed + * by createToolView(). + * \return \e true on success, otherwise \e false + * \todo add focus parameter: bool showToolView (QWidget *widget, bool giveFocus ); + */ + bool showToolView (QWidget *widget); + + /** + * Hide the toolview \p widget. + * \param widget the toolview to hide, where the widget was constructed + * by createToolView(). + * \return \e true on success, otherwise \e false + */ + bool hideToolView (QWidget *widget); + + /** + * This function is used by a plugin to open the kate configuration dialog + * at one of its own config pages. + * \param configpageinterface the plugin config page interface of the plugin which requests the settings dialog + * \param id the positional id of the page within the configuration + */ + void showPluginConfigPage(Kate::PluginConfigPageInterface *configpageinterface,uint id); + + /** + * Get a plugin view with identifier \p name. + * \param name the plugin's name + * \return pointer to the plugin view if a plugin with \p name is loaded and has a view for this mainwindow, + * otherwise NULL + */ + PluginView *pluginView (const QString &name); + + private: + class PrivateMainWindow *d; + }; +} + +#endif +// kate: space-indent on; indent-width 2; replace-tabs on; + diff --git a/kate/src/interfaces/kate/plugin.cpp b/kate/src/interfaces/kate/plugin.cpp new file mode 100644 index 00000000..e8d3254f --- /dev/null +++ b/kate/src/interfaces/kate/plugin.cpp @@ -0,0 +1,140 @@ +/* This file is part of the KDE project + Copyright (C) 2001 Christoph Cullmann + Copyright (C) 2001,2002,2005,2009 Joseph Wenninger + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +#include "plugin.h" + +#include "application.h" +#include "mainwindow.h" + +#include "moc_plugin.cpp" + +#include +#include +#include +#include + +#include + +namespace Kate +{ + class PrivatePluginView + { + public: + PrivatePluginView () + {} + + ~PrivatePluginView () + {} + + MainWindow *mainWindow; + }; + + Plugin::Plugin( Application *application, const char *name ) : QObject (application ) + { + setObjectName( name ); + } + + Plugin::~Plugin() + {} + + Plugin *createPlugin ( const char* libname, Application *application, + const QStringList &args ) // KDE5: s/QStringList/QVariantList/ + { + KPluginLoader loader( libname ); + KPluginFactory* factory = loader.factory(); + if (!factory) { + return NULL; + } else { + QVariantList variantlist; + Q_FOREACH(const QString& str, args) + variantlist << QVariant(str); + return factory->create( application, variantlist ); + } + } + + Application *Plugin::application () const + { + return Kate::application(); + } + + PluginView *Plugin::createView (MainWindow *) + { + return 0; + } + + void Plugin::readSessionConfig (KConfigBase*, const QString&) + {} + + void Plugin::writeSessionConfig (KConfigBase*, const QString&) + {} + + PluginView::PluginView (MainWindow *mainWindow) + : QObject (mainWindow), d (new PrivatePluginView ()) + { + // remember mainWindow of this view... + d->mainWindow = mainWindow; + } + + PluginView::~PluginView () + { + delete d; + } + + MainWindow *PluginView::mainWindow() const + { + return d->mainWindow; + } + + void PluginView::readSessionConfig (KConfigBase*, const QString&) + {} + + void PluginView::writeSessionConfig (KConfigBase*, const QString&) + {} + + + + XMLGUIClient::XMLGUIClient(const KComponentData &componentData) + : KXMLGUIClient() + { + setComponentData (componentData); + setXMLFile( xmlDataFile( componentData, "ui.rc" )); + setLocalXMLFile( localXmlDataFile( componentData, "ui.rc" )); + } + + QString XMLGUIClient::xmlDataFile(const KComponentData &componentData, const QString &filename) + { + const QString filter = "kate/plugins/" + componentData.componentName() + '/' + filename; + const QStringList allFiles = KGlobal::dirs()->findAllResources("data", filter); + QString file; + QString doc; + if (!allFiles.isEmpty()) + file = KXMLGUIClient::findMostRecentXMLFile(allFiles, doc); + return file; + } + + QString XMLGUIClient::localXmlDataFile(const KComponentData &componentData, const QString &filename) + { + QString result = KStandardDirs::locateLocal( "data", "kate/plugins/" + componentData.componentName() + QDir::separator() + filename); + kDebug(13000) << "File for shortcut storage" << result; + return result; + } + +} + +// kate: space-indent on; indent-width 2; replace-tabs on; + diff --git a/kate/src/interfaces/kate/plugin.h b/kate/src/interfaces/kate/plugin.h new file mode 100644 index 00000000..b75696b5 --- /dev/null +++ b/kate/src/interfaces/kate/plugin.h @@ -0,0 +1,320 @@ +/* This file is part of the KDE project + Copyright (C) 2001 Christoph Cullmann + Copyright (C) 2001,2002,2005,2009 Joseph Wenninger + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef _KATE_PLUGIN_INCLUDE_ +#define _KATE_PLUGIN_INCLUDE_ + +#include + +#include +#include + +#include +#include +#include + +class KConfigBase; + +namespace Kate +{ + + class Application; + class MainWindow; + class PluginView; + + /** + * \brief Kate plugin interface. + * + * Topics: + * - \ref intro + * - \ref config + * - \ref views + * - \ref configpages + * + * \section intro Introduction + * + * The Plugin class is the central part of a Kate plugin. It is possible to + * represent your plugin in the GUI with a subclass of PluginView. Furthermore + * if the plugin is configurable (and thus has config pages) you have to + * additionally derive your plugin from PluginConfigPageInterface. + * + * \section config Configuration Management + * + * When Kate loads a session it calls readSessionConfig(), so if you have + * config settings use this function to load them. To save config settings + * for a session use writeSessionConfig(), as it will be called whenever a + * session is saved/closed. + * + * If you want to save config settings which are not bound to a session but + * valid for all plugin instances you have to create your own KConfig like + * this: + * \code + * KConfig* myConfig = new KConfig("katemypluginrc"); + * \endcode + * + * \section views Plugin Views + * + * If your plugin needs to be present in the GUI (e.g. menu or toolbar + * entries) you have to subclass PluginView and return a new instance of your + * plugin view, like this: + * \code + * class MyPluginView : public Kate::PluginView + * { + * Q_OBJECT + * public: + * MyPluginView(MainWindow *mainWindow); + * + * // possibilities of gui: + * // - hook into the menus with KXMLGUIClient + * // - create a toolView and put a widget into it with MainWindow::createToolView() + * }; + * + * class MyPlugin : public Kate::Plugin + * { + * Q_OBJECT + * + * public: + * // other methods etc... + * PluginView *createView(MainWindow *mainWindow) + * { + * return new MyPluginView(mainWindow); + * } + * }; + * \endcode + * The Kate application takes care and deletes all plugin views. Further + * information can be found in the class documentation of PluginView. + * + * \section configpages Config Pages + * + * If your plugin is configurable it makes sense to have config pages which + * appear in Kate's settings dialog. To tell the plugin loader that your + * plugin supports config pages you have to additionally derive your plugin + * from the class PluginConfigPageInterface. Read the class documentation for + * PluginConfigPageInterface to see how to do this right. + * + * \see PluginView, PluginConfigPageInterface + * \author Christoph Cullmann \ + */ + class KATE_EXPORT Plugin : public QObject + { + Q_OBJECT + + public: + /** + * Constructor. + * \param application the Kate application + * \param name identifier + */ + explicit Plugin (Application *application = 0, const char *name = 0 ); + /** + * Virtual destructor. + */ + virtual ~Plugin (); + + /** + * Accessor to the Kate application. + * \return the application object + */ + Application *application() const; + + /** + * Create a new View for this plugin for the given Kate MainWindow + * This may be called arbitrary often by the application to create as much + * views as mainwindows are around, the application will take care to delete + * this views if mainwindows close, you don't need to handle this yourself in + * the plugin. + * The default implementation just doesn't create any view and returns a NULL + * pointer + * \param mainWindow the MainWindow for which a view should be created + * \return the new created view or NULL + */ + virtual PluginView *createView (MainWindow *mainWindow); + + /** + * Load session specific settings here. + * This function is called whenever a Kate session is loaded. You + * should use the given \p config and prefix \p groupPrefix to store the + * data. The group prefix exist so that the group does not clash with + * other applications that use the same config file. + * \param config the KConfig object which is to be used + * \param groupPrefix the group prefix which is to be used + * \see writeSessionConfig() + */ + virtual void readSessionConfig (KConfigBase* config, const QString& groupPrefix); + + /** + * Store session specific settings here. + * This function is called whenever a Kate session is saved. You + * should use the given \p config and prefix \p groupPrefix to store the + * data. The group prefix exists so that the group does not clash with + * other applications that use the same config file. + * \param config the KConfig object which is to be used + * \param groupPrefix the group prefix which is to be used + * \see readSessionConfig() + */ + virtual void writeSessionConfig (KConfigBase* config, const QString& groupPrefix); + }; + + /** + * Helper function for the Kate application to create new plugins. + * \param libname the plugin/library name + * \param application the application + * \param args arguments + * \return the plugin on success, otherwise NULL + */ + KATE_EXPORT Plugin *createPlugin ( const char* libname, Application *application = 0, + const QStringList &args = QStringList() ); + + /** + * \brief PluginView interface. + * + * Topics: + * - \ref intro + * - \ref views + * - \ref example + * + * \section intro Introduction + * + * The class PluginView is a interface for the view of a plugin. + * + * \section views Plugin Views + * + * The Kate application supports multiple mainwindows (Window > New Window). + * For every Kate MainWindow Plugin::createView() is called, i.e. overwrite + * createView() in your Plugin derived class and hook your view into the given + * mainwindow's KXMLGUIFactory. That means + * you have to create an own KXMLGUIClient derived \e PluginView class and + * create an own instance for \e every mainwindow. One PluginView then is + * bound to this specific MainWindow. + * + * As already mentioned in the Plugin class documentation, readSessionConfig() + * and writeSessionConfig() are called to load and save session related data. + * + * \section example Basic PluginView Example + * + * A PluginView is bound to a single MainWindow. To add GUI elements KDE's + * GUI XML frameworks is used, i.e. the MainWindow provides a KXMLGUIFactory + * into which the KXMLGUIClient is to be hooked. So the plugin view must + * inherit from Kate::XMLGUIClient, the following example shows the basic + * skeleton of the PluginView. + * \code + * class PluginView : public QObject, public Kate::XMLGUIClient + * { + * Q_OBJECT + * public: + * // Constructor and other methods + * PluginView( Kate::MainWindow* mainwindow ) + * : QObject( mainwindow ) + * , Kate::XMLGUIClient( YourPluginFactory::componentData() ) + * , m_mainwindow( mainwindow ) + * { ... } + * // ... + * private: + * Kate::MainWindow* m_mainwindow; + * }; + * \endcode + * To embedd a plugin view as a tool view you have to call + * MainWindow::createToolView() and hook your gui into the returned widget. + * + * \see Plugin, XMLGUIClient, KXMLGUIClient, MainWindow + * \author Christoph Cullmann \ + */ + class KATE_EXPORT PluginView : public QObject + { + friend class PrivatePluginView; + + Q_OBJECT + + public: + /** + * Constructor. + */ + PluginView (MainWindow *mainWindow); + + /** + * Virtual destructor. + */ + virtual ~PluginView (); + + /** + * Accessor to the Kate mainwindow of this view. + * \return the mainwindow object + */ + MainWindow *mainWindow() const; + + /** + * Load session specific settings here. + * This function is called whenever a Kate session is loaded. You + * should use the given \p config and prefix \p groupPrefix to store the + * data. The group prefix exist so that the group does not clash with + * other applications that use the same config file. + * \param config the KConfig object which is to be used + * \param groupPrefix the group prefix which is to be used + * \see writeSessionConfig() + */ + virtual void readSessionConfig (KConfigBase* config, const QString& groupPrefix); + + /** + * Store session specific settings here. + * This function is called whenever a Kate session is saved. You + * should use the given \p config and prefix \p groupPrefix to store the + * data. The group prefix exists so that the group does not clash with + * other applications that use the same config file. + * \param config the KConfig object which is to be used + * \param groupPrefix the group prefix which is to be used + * \see readSessionConfig() + */ + virtual void writeSessionConfig (KConfigBase* config, const QString& groupPrefix); + + private: + class PrivatePluginView *d; + }; + + /** + * \brief The KXMLGUIClient client for Kate application plugins. + * + * The class Kate::XMLGUIClient derives from KXMLGUIClient to work around + * some bugs: + * - we need the component data in order to make the shortcuts work in the + * shortcut dialog + * - usually the component data makes the plugin save in the folder + * share/apps/your-plugin/, but we want it to be + * share/apps/kate/plugins/your-plugin/. + * + * The constructor of this class makes sure the component data and the + * paths are set correctly in order to get the desired behaviour. + * + * \see PluginView, KXMLGUIClient + */ + class KATE_EXPORT XMLGUIClient : public KXMLGUIClient + { + public: + explicit XMLGUIClient(const KComponentData& componentData); + + private: + QString xmlDataFile(const KComponentData &componentData, const QString &filename); + QString localXmlDataFile(const KComponentData &componentData, const QString &filename); + + }; + +} + +#endif +// kate: space-indent on; indent-width 2; replace-tabs on; + diff --git a/kate/src/interfaces/kate/pluginconfigpageinterface.cpp b/kate/src/interfaces/kate/pluginconfigpageinterface.cpp new file mode 100644 index 00000000..3121c5b1 --- /dev/null +++ b/kate/src/interfaces/kate/pluginconfigpageinterface.cpp @@ -0,0 +1,74 @@ +/* This file is part of the KDE project + Copyright (C) 2001 Christoph Cullmann + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "pluginconfigpageinterface.h" +#include "moc_pluginconfigpageinterface.cpp" + +#include "plugin.h" + +namespace Kate +{ + + class PrivatePluginConfigPageInterface + { + public: + PrivatePluginConfigPageInterface() + {} + ~PrivatePluginConfigPageInterface() + {} + }; + + PluginConfigPage::PluginConfigPage ( QWidget *parent, const char *name ) : QWidget (parent) + { + setObjectName( name ); + } + + PluginConfigPage::~PluginConfigPage () + { } + + unsigned int PluginConfigPageInterface::globalPluginConfigPageInterfaceNumber = 0; + + PluginConfigPageInterface::PluginConfigPageInterface() + { + globalPluginConfigPageInterfaceNumber++; + myPluginConfigPageInterfaceNumber = globalPluginConfigPageInterfaceNumber++; + + d = new PrivatePluginConfigPageInterface(); + } + + PluginConfigPageInterface::~PluginConfigPageInterface() + { + delete d; + } + + unsigned int PluginConfigPageInterface::pluginConfigPageInterfaceNumber () const + { + return myPluginConfigPageInterfaceNumber; + } + + PluginConfigPageInterface *pluginConfigPageInterface (Plugin *plugin) + { + if (!plugin) + return 0; + + return qobject_cast(plugin); + } + +} +// kate: space-indent on; indent-width 2; replace-tabs on; + diff --git a/kate/src/interfaces/kate/pluginconfigpageinterface.h b/kate/src/interfaces/kate/pluginconfigpageinterface.h new file mode 100644 index 00000000..902f1c2f --- /dev/null +++ b/kate/src/interfaces/kate/pluginconfigpageinterface.h @@ -0,0 +1,218 @@ +/* This file is part of the KDE project + Copyright (C) 2001 Christoph Cullmann + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KATE_PLUGINCONFIGPAGEINTERFACE_H +#define KATE_PLUGINCONFIGPAGEINTERFACE_H + +#include +#include +#include +#include + +namespace Kate +{ + + /** + * \brief Plugin config page widget. + * + * The class PluginConfigPage is a base class for config pages for plugins. + * If you want to add support for a config page to your Kate plugin you have + * to derive a class from PluginConfigPage and overwrite apply(), reset() + * and default() as plublic slots. + * + * Whenever settings were changed in your config page emit the signal + * changed(). + * + * \see Plugin, PluginConfigPageInterface + * \author Christoph Cullmann \ + */ + class KATE_EXPORT PluginConfigPage : public QWidget + { + Q_OBJECT + + public: + /** + * Constructor. + * \param parent parent widget. + * \param name identifier + */ + explicit PluginConfigPage ( QWidget *parent = 0, const char *name = 0 ); + /** + * Virtual destructor. + */ + virtual ~PluginConfigPage (); + + // + // reimplement as public slots !!! + // + public: + /** + * This slot is called when the \e Apply button was clicked. + * Reimplement this function as a public slot and apply the changed + * settings. + */ + virtual void apply () = 0; + + /** + * This slot is called when the \e Reset button was clicked. + * Reimplement this function as a public slot and reset the settings + * to the current ones. + */ + virtual void reset () = 0; + + /** + * This slot is called when the \e Defaults button was clicked. + * Reimplement this function as a public slot and set every option to its + * default value. + */ + virtual void defaults () = 0; + + // + // SIGNALS !!! + // + Q_SIGNALS: + /** + * Emit this signal whenever a option changed. + * When the \e Apply button was clicked the public slot apply() will be + * called \e only \e if you emitted this signal beforehand. + * \see apply() + */ + void changed(); + }; + + /** + * \brief Plugin config page extension interface. + * + * The class PluginConfigPageInterface is an extension interface for plugins. + * If you want to add config pages to a plugin you have to make sure you + * -# derive your plugin from this interface (multiple inheritance) and + * overwrite the abstract methods + * -# return the number of config pages your plugin supports in configPages() + * -# return an instance the requested config pages in configPage() + * + * Your plugin header then looks like this: + * \code + * class MyPlugin : public Kate::Plugin, + * public Kate::PluginConfigPageInterface + * { + * Q_OBJECT + * Q_INTERFACES(Kate::PluginConfigPageInterface) + * + * public: + * // other methods etc... + * }; + * \endcode + * The line \p Q_INTERFACES... is important, otherwise the qobject_cast from + * Plugin to PluginConfigPageInterface fails so that your config page interface + * will not be recognized. + * + * \see Plugin, PluginConfigPage + * \author Christoph Cullmann \ + */ + class KATE_EXPORT PluginConfigPageInterface + { + friend class PrivatePluginConfigPageInterface; + + public: + /** + * Constructor. + */ + PluginConfigPageInterface(); + /** + * Virtual destructor. + */ + virtual ~PluginConfigPageInterface(); + + /** + * For internal reason every config page interface has a unique global + * number. + * \return unique identifier + */ + unsigned int pluginConfigPageInterfaceNumber () const; + + // + // slots !!! + // + public: + /** + * Reimplement this function and return the number of config pages your + * plugin supports. + * \return number of config pages + * \see configPage() + */ + virtual uint configPages () const = 0; + + /** + * Return the config page with the given \p number and \p parent. + * Assume you return \c N in configPages(), then you have to return + * config pages for the numbers 0 to \p N-1. + * + * \note This function will only be called if configPages() > 0. + * + * \param number the config page for the given \p number + * \param parent use this widget as parent widget for the config page + * \param name config page identifier + * \return a config page + * \see configPages() + */ + virtual PluginConfigPage *configPage (uint number = 0, QWidget *parent = 0, const char *name = 0 ) = 0; + + /** + * Return a short name for the config page with the given \p number, for + * example 'Autobookmarker'. + * \param number the config page for the given \p number + * \return a short name + */ + virtual QString configPageName (uint number = 0) const = 0; + /** + * Return the full name for the config page with the given \p number, for + * example 'Configure Autobookmarker'. + * \param number the config page for the given \p number + * \return a more descriptive name + */ + virtual QString configPageFullName (uint number = 0) const = 0; + /** + * Return an icon for for the config page with the given \p number. + * \param number the config page for the given \p number + * \return the icon for the config page + */ + virtual KIcon configPageIcon (uint number = 0) const = 0; + + private: + class PrivatePluginConfigPageInterface *d; + static unsigned int globalPluginConfigPageInterfaceNumber; + unsigned int myPluginConfigPageInterfaceNumber; + }; + + class Plugin; + /** + * Helper function that returns the PluginConfigPageInterface of the \p plugin + * or NULL if the \p plugin does not support the interface. + * \param plugin the plugin for which the plugin config page interface should + * be returned + * \return the plugin config page interface or NULL if the plugin does not + * support the interface + */ + KATE_EXPORT PluginConfigPageInterface *pluginConfigPageInterface (Plugin *plugin); + +} + +Q_DECLARE_INTERFACE(Kate::PluginConfigPageInterface, "org.kde.Kate.PluginConfigPageInterface") +#endif +// kate: space-indent on; indent-width 2; replace-tabs on; + diff --git a/kate/src/interfaces/kate/pluginmanager.cpp b/kate/src/interfaces/kate/pluginmanager.cpp new file mode 100644 index 00000000..26573cbe --- /dev/null +++ b/kate/src/interfaces/kate/pluginmanager.cpp @@ -0,0 +1,77 @@ +/* This file is part of the KDE project + Copyright (C) 2001 Christoph Cullmann + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +#include "pluginmanager.h" + +#include "moc_pluginmanager.cpp" + +#include "plugin.h" +#include "documentmanager.h" + +#include "../app/katepluginmanager.h" +#include "../app/kateapp.h" + +namespace Kate +{ + + class PrivatePluginManager + { + public: + PrivatePluginManager () + {} + + ~PrivatePluginManager () + {} + + KatePluginManager *pluginMan; + }; + + PluginManager::PluginManager (void *pluginManager) : QObject ((KatePluginManager*) pluginManager) + { + d = new PrivatePluginManager (); + d->pluginMan = (KatePluginManager*) pluginManager; + } + + PluginManager::~PluginManager () + { + delete d; + } + + Plugin *PluginManager::plugin(const QString &name) + { + return d->pluginMan->plugin(name); + } + + bool PluginManager::pluginAvailable(const QString &name) + { + return d->pluginMan->pluginAvailable (name); + } + + Plugin *PluginManager::loadPlugin(const QString &name, bool permanent) + { + return d->pluginMan->loadPlugin (name, permanent); + } + + void PluginManager::unloadPlugin(const QString &name, bool permanent) + { + d->pluginMan->unloadPlugin (name, permanent); + } + +} + +// kate: space-indent on; indent-width 2; replace-tabs on; + diff --git a/kate/src/interfaces/kate/pluginmanager.h b/kate/src/interfaces/kate/pluginmanager.h new file mode 100644 index 00000000..6257788d --- /dev/null +++ b/kate/src/interfaces/kate/pluginmanager.h @@ -0,0 +1,148 @@ +/* This file is part of the KDE project + Copyright (C) 2001 Christoph Cullmann + Copyright (C) 2002 Joseph Wenninger + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef _KATE_PLUGINMANAGER_INCLUDE_ +#define _KATE_PLUGINMANAGER_INCLUDE_ + +#include + +#include +#include + +namespace Kate +{ + /** + * \brief Interface to the plugin manager. + * + * This interface provides access to Kate's plugin manager. To load a plugin + * call loadPlugin(), to unload a plugin call unloadPlugin(). To check, + * whether a plugin is loaded use plugin(), and to check a plugin's + * availability use pluginAvailable(). + * + * To access the plugin manager use Application::pluginManager(). + * You should never have to create an instance of this class yourself. + * + * \note Usually the Kate application itself loads/unloads the plugins, so + * plugins actually never have to call loadPlugin() or unloadPlugin(). + * + * + * \author Christoph Cullmann \ + * \see Plugin + */ + class KATE_EXPORT PluginManager : public QObject + { + friend class PrivatePluginManager; + + Q_OBJECT + + public: + /** + * Constructor. + * + * Create a new plugin manager. You as a plugin developer should never + * have to create a plugin manager yourself. Get the plugin manager + * with the Application instance. + * \param pluginManager internal usage + */ + PluginManager ( void *pluginManager ); + + /** + * Virtual destructor. + */ + virtual ~PluginManager (); + + public: + /** + * Get a plugin with identifier \p name. + * \param name the plugin's name + * \return pointer to the plugin if a plugin with \p name is loaded, + * otherwise NULL + */ + class Plugin *plugin (const QString &name); + + /** + * Check, whether a plugin with \p name exists. + * + * \return \e true if the plugin exists, otherwise \e false + */ + bool pluginAvailable (const QString &name); + + /** + * Load the plugin \p name. + * + * If the plugin does not exist the return value is NULL. + * \param name the plugin name + * \param permanent if \e true the plugin will be loaded at the next Kate + * startup, otherwise it will only be loaded temporarily during the + * current session. + * \return pointer to the plugin on success, otherwise NULL + * \see unloadPlugin() + */ + class Plugin *loadPlugin (const QString &name, bool permanent = true); + + /** + * Unload the plugin \p name. + * + * \param name the plugin name + * \param permanent if \e true the plugin will \e not be loaded on the + * next Kate startup, \e even if it was loaded with permanent set + * to \e true. + * \see loadPlugin() + */ + void unloadPlugin (const QString &name, bool permanent = true); + + // + // SIGNALS !!! + // +#ifndef Q_MOC_RUN +#undef signals +#define signals public +#endif + signals: +#ifndef Q_MOC_RUN +#undef signals +#define signals protected +#endif + + /** + * New plugin loaded. + * @param name name of new loaded plugin + * @param plugin new loaded plugin + */ + void pluginLoaded (const QString &name, Kate::Plugin *plugin); + + /** + * This signal is emitted when the plugin has been unloaded, aka deleted. + * + * @warning DO NOT ACCESS THE DATA REFERENCED BY THE POINTER, IT IS ALREADY INVALID + * Use the pointer only to remove mappings in hash or maps + * + * @param name name of unloaded plugin + * @param plugin unloaded plugin, already deleted + */ + void pluginUnloaded (const QString &name, Kate::Plugin *plugin); + + private: + class PrivatePluginManager *d; + }; +} + +#endif + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/tests/CMakeLists.txt b/kate/tests/CMakeLists.txt new file mode 100644 index 00000000..6f06f6f2 --- /dev/null +++ b/kate/tests/CMakeLists.txt @@ -0,0 +1,212 @@ +include_directories( + ${CMAKE_SOURCE_DIR}/kate/part + ${CMAKE_SOURCE_DIR}/kate/part/buffer + ${CMAKE_SOURCE_DIR}/kate/part/completion + ${CMAKE_SOURCE_DIR}/kate/part/dialogs + ${CMAKE_SOURCE_DIR}/kate/part/document + ${CMAKE_SOURCE_DIR}/kate/part/mode + ${CMAKE_SOURCE_DIR}/kate/part/render + ${CMAKE_SOURCE_DIR}/kate/part/search + ${CMAKE_SOURCE_DIR}/kate/part/syntax + ${CMAKE_SOURCE_DIR}/kate/part/undo + ${CMAKE_SOURCE_DIR}/kate/part/utils + ${CMAKE_SOURCE_DIR}/kate/part/view + ${CMAKE_SOURCE_DIR}/kate/part/kte5 # KDE5 remove + ${CMAKE_BINARY_DIR}/kate/part + ${KDE4_KIO_INCLUDES} +) + +set(KATE_TEST_LINK_LIBS + ${KDE4_KDECORE_LIBS} + ${QT_QTTEST_LIBRARY} + ${KDE4_KCMUTILS_LIBS} + ktexteditor + katepartinterfaces +) + +# buffer test +kde4_add_test(kate-katetextbuffertest katetextbuffertest.cpp katetextbuffertest.h) +target_link_libraries(kate-katetextbuffertest ${KATE_TEST_LINK_LIBS}) + +########### range test ############### + +kde4_add_test(kate-range_test range_test.cpp) + +target_link_libraries(kate-range_test + ${KDE4_KDEUI_LIBS} + ${QT_QTTEST_LIBRARY} + ${KATE_TEST_LINK_LIBS} + katepartinterfaces +) + +########### undomanager test ############### + +kde4_add_test(kate-undomanager_test undomanager_test.cpp) + +target_link_libraries(kate-undomanager_test ${KATE_TEST_LINK_LIBS}) + +########### plaintextsearch test ############### + +kde4_add_test(kate-plaintextsearch_test plaintextsearch_test.cpp) + +target_link_libraries(kate-plaintextsearch_test ${KATE_TEST_LINK_LIBS}) + +########### regexpsearch test ############### + +kde4_add_test(kate-regexpsearch_test regexpsearch_test.cpp) + +target_link_libraries(kate-regexpsearch_test ${KATE_TEST_LINK_LIBS}) + +########### completion test ############### + +set(completion_test_SRCS + completion_test.cpp + codecompletiontestmodel.cpp +) + +kde4_add_test(kate-completion_test ${completion_test_SRCS}) + +target_link_libraries(kate-completion_test ${KATE_TEST_LINK_LIBS}) + +########### word completion test ############### + +kde4_add_test(kate-wordcompletion_test wordcompletiontest.cpp) + +target_link_libraries(kate-wordcompletion_test ${KATE_TEST_LINK_LIBS}) + +########### searchbar test ############### + +kde4_add_test(kate-searchbar_test searchbar_test.cpp) + +target_link_libraries(kate-searchbar_test + ${KDE4_KDEUI_LIBS} + ${QT_QTTEST_LIBRARY} + ${KATE_TEST_LINK_LIBS} + katepartinterfaces +) + +########### cursor test ############### + +kde4_add_test(kate-movingcursor_test movingcursor_test.cpp) + +target_link_libraries(kate-movingcursor_test + ${KDE4_KDEUI_LIBS} + ${QT_QTTEST_LIBRARY} + ${KATE_TEST_LINK_LIBS} + katepartinterfaces +) + +########### moving range and feedback test ############### + +kde4_add_test(kate-movingrange_test movingrange_test.cpp) + +target_link_libraries(kate-movingrange_test + ${KDE4_KDEUI_LIBS} + ${QT_QTTEST_LIBRARY} + ${KATE_TEST_LINK_LIBS} + katepartinterfaces +) + +########### document test ############### + +kde4_add_test(kate-katedocument_test katedocument_test.cpp) + +target_link_libraries(kate-katedocument_test + ${KDE4_KDEUI_LIBS} + ${QT_QTTEST_LIBRARY} + ${KATE_TEST_LINK_LIBS} + katepartinterfaces +) + +########### view test ############### + +kde4_add_test(kate-kateview_test kateview_test.cpp) + +target_link_libraries(kate-kateview_test + ${KDE4_KDEUI_LIBS} + ${QT_QTTEST_LIBRARY} + ${KATE_TEST_LINK_LIBS} + katepartinterfaces +) + +########### revision test ############### + +kde4_add_test(kate-revision_test revision_test.cpp) + +target_link_libraries(kate-revision_test + ${KDE4_KDEUI_LIBS} + ${QT_QTTEST_LIBRARY} + ${KATE_TEST_LINK_LIBS} + katepartinterfaces +) + +########### line modification test ############### + +kde4_add_test(kate-modificationsystem_test modificationsystem_test.cpp) + +target_link_libraries(kate-modificationsystem_test + ${KATE_TEST_LINK_LIBS} + ${KDE4_KDEUI_LIBS} + ${QT_QTTEST_LIBRARY} + ${KATE_TEST_LINK_LIBS} + katepartinterfaces +) + +########### text folding test ############### + +kde4_add_test(kate-codefoldingtest katefoldingtest.cpp) + +target_link_libraries(kate-codefoldingtest + ${KDE4_KDEUI_LIBS} + ${QT_QTTEST_LIBRARY} + ${KATE_TEST_LINK_LIBS} + katepartinterfaces +) + +########### bug 286887 test ############### + +kde4_add_test(kate-bug286887_test bug286887.cpp) +set_tests_properties(kate-bug286887_test PROPERTIES TIMEOUT 4) + +target_link_libraries(kate-bug286887_test + ${KDE4_KDEUI_LIBS} + ${QT_QTTEST_LIBRARY} + ${KATE_TEST_LINK_LIBS} + katepartinterfaces +) + +########### bug 313769 test ############### + +kde4_add_test(kate-bug313769_test bug313769.cpp) + +target_link_libraries(kate-bug313769_test + ${KDE4_KDEUI_LIBS} + ${QT_QTTEST_LIBRARY} + ${KATE_TEST_LINK_LIBS} + katepartinterfaces +) + +########### KTextEditor::DocumentCursor test ############### + +kde4_add_test(kate-documentcursor_test kte_documentcursor.cpp) + +target_link_libraries(kate-documentcursor_test + ${KDE4_KDEUI_LIBS} + ${QT_QTTEST_LIBRARY} + ${KATE_TEST_LINK_LIBS} + katepartinterfaces +) + +########### KTextEditor::MessageInterface test ############### + +kde4_add_test(kate-messageinterface messagetest.cpp) + +target_link_libraries(kate-messageinterface + ${KDE4_KDEUI_LIBS} + ${QT_QTTEST_LIBRARY} + ${KATE_TEST_LINK_LIBS} + katepartinterfaces +) + +# encoding tests +add_subdirectory (encoding) diff --git a/kate/tests/bug286887.cpp b/kate/tests/bug286887.cpp new file mode 100644 index 00000000..e01c2121 --- /dev/null +++ b/kate/tests/bug286887.cpp @@ -0,0 +1,91 @@ +/* This file is part of the KDE libraries + Copyright (C) 2012 Dominik Haumann + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "bug286887.h" +#include "moc_bug286887.cpp" + +#include + +#include +#include +#include +#include + +QTEST_KDEMAIN(BugTest, GUI) + +using namespace KTextEditor; + +BugTest::BugTest() + : QObject() +{ +} + +BugTest::~BugTest() +{ +} + +void BugTest::initTestCase() +{ + KateGlobal::self()->incRef(); +} + +void BugTest::cleanupTestCase() +{ + KateGlobal::self()->decRef(); +} + +void BugTest::ctrlShiftLeft() +{ + KateDocument doc(false, false, false); + + // view must be visible... + KateView* view = static_cast(doc.createView(0)); + view->show(); + view->resize(400, 300); + + // enable block mode, then set cursor after last character, then shift+left + doc.clear(); + view->setBlockSelection(true); + view->setCursorPosition(Cursor(0, 2)); + view->shiftCursorLeft(); + + QTest::qWait(500); + + // enable block mode, then set cursor after last character, then delete word left + doc.clear(); + view->setBlockSelection(true); + view->setCursorPosition(Cursor(0, 2)); + view->deleteWordLeft(); + + QTest::qWait(500); + + // disable wrap-cursor, then set cursor after last character, then shift+left + doc.clear(); + view->setBlockSelection(false); + view->setCursorPosition(Cursor(0, 2)); + view->shiftCursorLeft(); + + QTest::qWait(500); + + // disable wrap-cursor, then set cursor after last character, then delete word left + doc.clear(); + view->setCursorPosition(Cursor(0, 2)); + view->deleteWordLeft(); +} + diff --git a/kate/tests/bug286887.h b/kate/tests/bug286887.h new file mode 100644 index 00000000..b6a7a35a --- /dev/null +++ b/kate/tests/bug286887.h @@ -0,0 +1,40 @@ +/* This file is part of the KDE libraries + Copyright (C) 2012 Dominik Haumann + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KATE_BUG_286887_TEST_H +#define KATE_BUG_286887_TEST_H + +#include + +class BugTest : public QObject +{ + Q_OBJECT + +public: + BugTest(); + ~BugTest(); + +private Q_SLOTS: + void initTestCase(); + void cleanupTestCase(); + + void ctrlShiftLeft(); +}; + +#endif diff --git a/kate/tests/bug313769.cpp b/kate/tests/bug313769.cpp new file mode 100644 index 00000000..6be54acc --- /dev/null +++ b/kate/tests/bug313769.cpp @@ -0,0 +1,100 @@ +/* This file is part of the KDE libraries + Copyright (C) 2012 Dominik Haumann + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "bug313769.h" +#include "moc_bug313769.cpp" + +#include + +#include +#include +#include +#include +#include +#include + +QTEST_KDEMAIN(BugTest, GUI) + +using namespace KTextEditor; + +BugTest::BugTest() + : QObject() +{ +} + +BugTest::~BugTest() +{ +} + +void BugTest::initTestCase() +{ + KateGlobal::self()->incRef(); +} + +void BugTest::cleanupTestCase() +{ + KateGlobal::self()->decRef(); +} + +void BugTest::tryCrash() +{ + KateDocument doc(false, false, false); + QString url = KDESRCDIR + QString("data/bug313769.cpp"); + doc.openUrl(url); + doc.discardDataRecovery(); + doc.setHighlightingMode("C++"); + doc.buffer().ensureHighlighted (doc.lines()); + + // view must be visible... + KateView* view = static_cast(doc.createView(0)); + view->show(); + view->resize(900, 800); + view->config()->setDynWordWrap(true); + view->setSelection(Range(2, 0, 74, 0)); + view->setCursorPosition(Cursor(74, 0)); + + doc.editBegin(); + QString text = doc.line(1); + doc.insertLine(74, text); + doc.removeLine(1); + view->setCursorPosition(Cursor(1, 0)); + doc.editEnd(); + + QTest::qWait(200); + // fold toplevel nodes + for (int line = 0; line < doc.lines(); ++line) { + if (view->textFolding().isLineVisible(line)) { + view->foldLine(line); + } + } + doc.buffer().ensureHighlighted (doc.lines()); + + view->setCursorPosition(Cursor(0, 0)); + + QTest::qWait(100); + doc.undo(); + QTest::qWait(100); + doc.redo(); + QTest::qWait(500); + qDebug() << "!!! Does undo crash?"; + doc.undo(); + + QTest::qWait(500); + qDebug() << "!!! No crash (qWait not long enough)? Nice!"; +} diff --git a/kate/tests/bug313769.h b/kate/tests/bug313769.h new file mode 100644 index 00000000..41c40d57 --- /dev/null +++ b/kate/tests/bug313769.h @@ -0,0 +1,40 @@ +/* This file is part of the KDE libraries + Copyright (C) 2012 Dominik Haumann + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KATE_BUG_313769_TEST_H +#define KATE_BUG_313769_TEST_H + +#include + +class BugTest : public QObject +{ + Q_OBJECT + +public: + BugTest(); + ~BugTest(); + +private Q_SLOTS: + void initTestCase(); + void cleanupTestCase(); + + void tryCrash(); +}; + +#endif diff --git a/kate/tests/codecompletiontestmodel.cpp b/kate/tests/codecompletiontestmodel.cpp new file mode 100644 index 00000000..c35d06f1 --- /dev/null +++ b/kate/tests/codecompletiontestmodel.cpp @@ -0,0 +1,197 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Hamish Rodda + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "codecompletiontestmodel.h" + +#include +#include +#include +#include +#include + +CodeCompletionTestModel::CodeCompletionTestModel(KTextEditor::View* parent, const QString &startText) + : KTextEditor::CodeCompletionModel(parent), m_startText(startText), m_autoStartText(m_startText.isEmpty()) +{ + setRowCount(40); + + Q_ASSERT(cc()); + + cc()->setAutomaticInvocationEnabled(true); + cc()->unregisterCompletionModel(KateGlobal::self()->wordCompletionModel()); //would add additional items, we don't want that in tests + cc()->registerCompletionModel(this); +} + +// Fake a series of completions +QVariant CodeCompletionTestModel::data( const QModelIndex & index, int role ) const +{ + switch (role) { + case Qt::DisplayRole: + if (index.row() < rowCount() / 2) + switch (index.column()) { + case Prefix: + switch (index.row() % 3) { + default: + return "void "; + case 1: + return "const QString& "; + case 2: + if (index.row() % 6) + return "inline virtual bool "; + return "virtual bool "; + } + + case Scope: + switch (index.row() % 4) { + default: + return QString(); + case 1: + return "KTextEditor::"; + case 2: + return "::"; + case 3: + return "std::"; + } + + case Name: + return QString(m_startText + QString("%1%2%3").arg(QChar('a' + (index.row() % 3))).arg(QChar('a' + index.row())).arg(index.row())); + + case Arguments: + switch (index.row() % 5) { + default: + return "()"; + case 1: + return "(bool trigger)"; + case 4: + return "(const QString& name, Qt::CaseSensitivity cs)"; + case 5: + return "(int count)"; + } + + case Postfix: + switch (index.row() % 3) { + default: + return " const"; + case 1: + return " Q_DECL_DEPRECATED"; + case 2: + return ""; + } + } + else + switch (index.column()) { + case Prefix: + switch (index.row() % 3) { + default: + return "void "; + case 1: + return "const QString "; + case 2: + return "bool "; + } + + case Scope: + switch (index.row() % 4) { + default: + return QString(); + case 1: + return "KTextEditor::"; + case 2: + return "::"; + case 3: + return "std::"; + } + + case Name: + return QString(m_startText + QString("%1%2%3").arg(QChar('a' + (index.row() % 3))).arg(QChar('a' + index.row())).arg(index.row())); + + default: + return ""; + } + break; + + case Qt::DecorationRole: + break; + + case CompletionRole: { + CompletionProperties p; + if (index.row() < rowCount() / 2) + p |= Function; + else + p |= Variable; + switch (index.row() % 3) { + case 0: + p |= Const | Public; + break; + case 1: + p |= Protected; + break; + case 2: + p |= Private; + break; + } + return (int)p; + } + + case ScopeIndex: + return (index.row() % 4 ) - 1; + } + + return QVariant(); +} + +KTextEditor::View* CodeCompletionTestModel::view( ) const +{ + return static_cast(const_cast(QObject::parent())); +} + +KTextEditor::CodeCompletionInterface * CodeCompletionTestModel::cc( ) const +{ + return dynamic_cast(const_cast(QObject::parent())); +} + +void CodeCompletionTestModel::completionInvoked(KTextEditor::View* view, const KTextEditor::Range& range, InvocationType invocationType) +{ + Q_UNUSED(invocationType) + + if (m_autoStartText) { + m_startText = view->document()->text(KTextEditor::Range(range.start(), view->cursorPosition())); + } + kDebug() << m_startText; +} + + +AbbreviationCodeCompletionTestModel::AbbreviationCodeCompletionTestModel(KTextEditor::View* parent, + const QString& startText) + : CodeCompletionTestModel(parent, startText) +{ + m_items << "SomeCoolAbbreviation" << "someCoolAbbreviation" << "sca" << "SCA"; + m_items << "some_cool_abbreviation" << "Some_Cool_Abbreviation"; + m_items << "thisContainsSomeWord" << "this_contains_some_word" << "thiscontainssomeword"; + m_items << "notmatchedbecausemissingcaps" << "not_m_atch_ed_because_underscores"; + setRowCount(m_items.size()); +} + +QVariant AbbreviationCodeCompletionTestModel::data(const QModelIndex& index, int role) const +{ + if ( index.column() == Name && role == Qt::DisplayRole ) { + return m_items[index.row()]; + } + return QVariant(); +} + +#include "moc_codecompletiontestmodel.cpp" diff --git a/kate/tests/codecompletiontestmodel.h b/kate/tests/codecompletiontestmodel.h new file mode 100644 index 00000000..ff7f4e01 --- /dev/null +++ b/kate/tests/codecompletiontestmodel.h @@ -0,0 +1,60 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Hamish Rodda + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef CODECOMPLETIONTEST_H +#define CODECOMPLETIONTEST_H + +#include +#include + +namespace KTextEditor { + class View; + class CodeCompletionInterface; +} + +class CodeCompletionTestModel : public KTextEditor::CodeCompletionModel +{ + Q_OBJECT + + public: + CodeCompletionTestModel(KTextEditor::View* parent = 0L, const QString &startText = QString()); + + KTextEditor::View* view() const; + KTextEditor::CodeCompletionInterface* cc() const; + + virtual void completionInvoked(KTextEditor::View* view, const KTextEditor::Range& range, InvocationType invocationType); + virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const; + + private: + QString m_startText; + bool m_autoStartText; +}; + +class AbbreviationCodeCompletionTestModel : public CodeCompletionTestModel { + Q_OBJECT + + public: + AbbreviationCodeCompletionTestModel(KTextEditor::View* parent = 0L, const QString& startText = QString()); + + virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const; + +private: + QStringList m_items; +}; + +#endif diff --git a/kate/tests/codecompletiontestmodels.h b/kate/tests/codecompletiontestmodels.h new file mode 100644 index 00000000..8f0d275d --- /dev/null +++ b/kate/tests/codecompletiontestmodels.h @@ -0,0 +1,166 @@ +/* This file is part of the KDE libraries + Copyright (C) 2008 Niko Sams + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KATE_COMPLETIONTESTMODELS_H +#define KATE_COMPLETIONTESTMODELS_H + +#include "codecompletiontestmodel.h" +#include + +#include +#include + +using namespace KTextEditor; + + +class CustomRangeModel : public CodeCompletionTestModel, public CodeCompletionModelControllerInterface +{ + Q_OBJECT + Q_INTERFACES(KTextEditor::CodeCompletionModelControllerInterface) +public: + CustomRangeModel(KTextEditor::View* parent = 0L, const QString &startText = QString()) + : CodeCompletionTestModel(parent, startText) + {} + Range completionRange(View* view, const Cursor &position) + { + Range range = CodeCompletionModelControllerInterface::completionRange(view, position); + if (range.start().column() > 0) { + KTextEditor::Range preRange(Cursor(range.start().line(), range.start().column()-1), + Cursor(range.start().line(), range.start().column())); + kDebug() << preRange << view->document()->text(preRange); + if (view->document()->text(preRange) == "$") { + range.expandToRange(preRange); + kDebug() << "using custom completion range" << range; + } + } + return range; + } + + bool shouldAbortCompletion(View* view, const Range& range, const QString ¤tCompletion) + { + Q_UNUSED(view); + Q_UNUSED(range); + static const QRegExp allowedText("^\\$?(\\w*)"); + return !allowedText.exactMatch(currentCompletion); + } +}; + +class CustomAbortModel : public CodeCompletionTestModel, public CodeCompletionModelControllerInterface +{ + Q_OBJECT + Q_INTERFACES(KTextEditor::CodeCompletionModelControllerInterface) +public: + CustomAbortModel(KTextEditor::View* parent = 0L, const QString &startText = QString()) + : CodeCompletionTestModel(parent, startText) + {} + + bool shouldAbortCompletion(View* view, const Range& range, const QString ¤tCompletion) + { + Q_UNUSED(view); + Q_UNUSED(range); + static const QRegExp allowedText("^([\\w-]*)"); + return !allowedText.exactMatch(currentCompletion); + } +}; + +class EmptyFilterStringModel : public CodeCompletionTestModel, public CodeCompletionModelControllerInterface +{ + Q_OBJECT + Q_INTERFACES(KTextEditor::CodeCompletionModelControllerInterface) +public: + EmptyFilterStringModel(KTextEditor::View* parent = 0L, const QString &startText = QString()) + : CodeCompletionTestModel(parent, startText) + {} + + QString filterString(View*, const Range&, const Cursor &) + { + return QString(); + } +}; + +class UpdateCompletionRangeModel : public CodeCompletionTestModel, public CodeCompletionModelControllerInterface +{ + Q_OBJECT + Q_INTERFACES(KTextEditor::CodeCompletionModelControllerInterface) +public: + UpdateCompletionRangeModel(KTextEditor::View* parent = 0L, const QString &startText = QString()) + : CodeCompletionTestModel(parent, startText) + {} + + Range updateCompletionRange(View* view, const Range& range) + { + Q_UNUSED(view); + if (view->document()->text(range) == QString("ab")) { + return Range(Cursor(range.start().line(), 0), range.end()); + } + return range; + } + bool shouldAbortCompletion(View* view, const Range &range, const QString ¤tCompletion) + { + Q_UNUSED(view); + Q_UNUSED(range); + Q_UNUSED(currentCompletion); + return false; + } +}; + +class StartCompletionModel : public CodeCompletionTestModel, public CodeCompletionModelControllerInterface +{ + Q_OBJECT + Q_INTERFACES(KTextEditor::CodeCompletionModelControllerInterface) +public: + StartCompletionModel(KTextEditor::View* parent = 0L, const QString &startText = QString()) + : CodeCompletionTestModel(parent, startText) + {} + + bool shouldStartCompletion(View* view, const QString &insertedText, bool userInsertion, const Cursor &position) + { + Q_UNUSED(view); + Q_UNUSED(userInsertion); + Q_UNUSED(position); + if(insertedText.isEmpty()) + return false; + + QChar lastChar = insertedText.at(insertedText.count() - 1); + if (lastChar == '%') { + return true; + } + return false; + } +}; + +class ImmideatelyAbortCompletionModel : public CodeCompletionTestModel, public CodeCompletionModelControllerInterface +{ + Q_OBJECT + Q_INTERFACES(KTextEditor::CodeCompletionModelControllerInterface) +public: + ImmideatelyAbortCompletionModel(KTextEditor::View* parent = 0L, const QString &startText = QString()) + : CodeCompletionTestModel(parent, startText) + {} + + virtual bool shouldAbortCompletion(KTextEditor::View* view, const KTextEditor::Range& range, const QString& currentCompletion) + { + Q_UNUSED(view); + Q_UNUSED(range); + Q_UNUSED(currentCompletion); + return true; + } +}; + +#endif diff --git a/kate/tests/completion_test.cpp b/kate/tests/completion_test.cpp new file mode 100644 index 00000000..7fc25460 --- /dev/null +++ b/kate/tests/completion_test.cpp @@ -0,0 +1,495 @@ +/* This file is part of the KDE libraries + Copyright (C) 2008 Niko Sams + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "completion_test.h" + +#include "codecompletiontestmodels.h" +#include "moc_codecompletiontestmodels.cpp" + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +QTEST_KDEMAIN(CompletionTest, GUI) + + +using namespace KTextEditor; + +int countItems(KateCompletionModel *model) +{ + int ret = 0; + for (int i=0; i < model->rowCount(QModelIndex()); ++i) { + ret += model->rowCount(model->index(i, 0)); + } + return ret; +} + +static void verifyCompletionStarted(KateView* view) +{ + const QDateTime startTime = QDateTime::currentDateTime(); + while (startTime.msecsTo(QDateTime::currentDateTime()) < 1000) + { + QApplication::processEvents(); + if (view->completionWidget()->isCompletionActive()) + { + break; + } + } + QVERIFY(view->completionWidget()->isCompletionActive()); +} + +static void verifyCompletionAborted(KateView* view) +{ + const QDateTime startTime = QDateTime::currentDateTime(); + while (startTime.msecsTo(QDateTime::currentDateTime()) < 1000) + { + QApplication::processEvents(); + if (!view->completionWidget()->isCompletionActive()) + { + break; + } + } + QVERIFY(!view->completionWidget()->isCompletionActive()); +} + +static void invokeCompletionBox(KateView* view) +{ + view->userInvokedCompletion(); + verifyCompletionStarted(view); +} + +void CompletionTest::init() +{ + if ( !KSycoca::isAvailable() ) + QSKIP( "ksycoca not available", SkipAll ); + + Editor* editor = EditorChooser::editor(); + QVERIFY(editor); + + m_doc = editor->createDocument(this); + QVERIFY(m_doc); + m_doc->setText("aa bb cc\ndd"); + + KTextEditor::View *v = m_doc->createView(0); + QApplication::setActiveWindow(v); + m_view = static_cast(v); + Q_ASSERT(m_view); + + //view needs to be shown as completion won't work if the cursor is off screen + m_view->show(); +} + +void CompletionTest::cleanup() +{ + delete m_view; + delete m_doc; +} + +void CompletionTest::testFilterEmptyRange() +{ + KateCompletionModel *model = m_view->completionWidget()->model(); + + new CodeCompletionTestModel(m_view, "a"); + m_view->setCursorPosition(Cursor(0, 0)); + invokeCompletionBox(m_view); + + QCOMPARE(countItems(model), 40); + m_view->insertText("aa"); + QTest::qWait(1000); // process events + QCOMPARE(countItems(model), 14); +} + +void CompletionTest::testFilterWithRange() +{ + KateCompletionModel *model = m_view->completionWidget()->model(); + + CodeCompletionTestModel* testModel = new CodeCompletionTestModel(m_view, "a"); + m_view->setCursorPosition(Cursor(0, 2)); + invokeCompletionBox(m_view); + + Range complRange = *m_view->completionWidget()->completionRange(testModel); + QCOMPARE(complRange, Range(Cursor(0, 0), Cursor(0, 2))); + QCOMPARE(countItems(model), 14); + + m_view->insertText("a"); + QTest::qWait(1000); // process events + QCOMPARE(countItems(model), 1); +} + + +void CompletionTest::testAbortCursorMovedOutOfRange() +{ + KateCompletionModel *model = m_view->completionWidget()->model(); + + new CodeCompletionTestModel(m_view, "a"); + m_view->setCursorPosition(Cursor(0, 2)); + invokeCompletionBox(m_view); + + QCOMPARE(countItems(model), 14); + QVERIFY(m_view->completionWidget()->isCompletionActive()); + + m_view->setCursorPosition(Cursor(0, 4)); + QTest::qWait(1000); // process events + QVERIFY(!m_view->completionWidget()->isCompletionActive()); +} + +void CompletionTest::testAbortInvalidText() +{ + KateCompletionModel *model = m_view->completionWidget()->model(); + + new CodeCompletionTestModel(m_view, "a"); + m_view->setCursorPosition(Cursor(0, 2)); + invokeCompletionBox(m_view); + + QCOMPARE(countItems(model), 14); + QVERIFY(m_view->completionWidget()->isCompletionActive()); + + m_view->insertText("."); + verifyCompletionAborted(m_view); +} + +void CompletionTest::testCustomRange1() +{ + m_doc->setText("$aa bb cc\ndd"); + KateCompletionModel *model = m_view->completionWidget()->model(); + + CodeCompletionTestModel* testModel = new CustomRangeModel(m_view, "$a"); + m_view->setCursorPosition(Cursor(0, 3)); + invokeCompletionBox(m_view); + + Range complRange = *m_view->completionWidget()->completionRange(testModel); + kDebug() << complRange; + QCOMPARE(complRange, Range(Cursor(0, 0), Cursor(0, 3))); + QCOMPARE(countItems(model), 14); + + m_view->insertText("a"); + QTest::qWait(1000); // process events + QCOMPARE(countItems(model), 1); +} + +void CompletionTest::testCustomRange2() +{ + m_doc->setText("$ bb cc\ndd"); + KateCompletionModel *model = m_view->completionWidget()->model(); + + CodeCompletionTestModel* testModel = new CustomRangeModel(m_view, "$a"); + m_view->setCursorPosition(Cursor(0, 1)); + invokeCompletionBox(m_view); + + Range complRange = *m_view->completionWidget()->completionRange(testModel); + QCOMPARE(complRange, Range(Cursor(0, 0), Cursor(0, 1))); + QCOMPARE(countItems(model), 40); + + m_view->insertText("aa"); + QTest::qWait(1000); // process events + QCOMPARE(countItems(model), 14); +} + +void CompletionTest::testCustomRangeMultipleModels() +{ + m_doc->setText("$a bb cc\ndd"); + KateCompletionModel *model = m_view->completionWidget()->model(); + + CodeCompletionTestModel* testModel1 = new CustomRangeModel(m_view, "$a"); + CodeCompletionTestModel* testModel2 = new CodeCompletionTestModel(m_view, "a"); + m_view->setCursorPosition(Cursor(0, 1)); + invokeCompletionBox(m_view); + + QCOMPARE(Range(*m_view->completionWidget()->completionRange(testModel1)), Range(Cursor(0, 0), Cursor(0, 2))); + QCOMPARE(Range(*m_view->completionWidget()->completionRange(testModel2)), Range(Cursor(0, 1), Cursor(0, 2))); + QCOMPARE(model->currentCompletion(testModel1), QString("$")); + QCOMPARE(model->currentCompletion(testModel2), QString("")); + QCOMPARE(countItems(model), 80); + + + m_view->insertText("aa"); + QTest::qWait(1000); // process events + QCOMPARE(model->currentCompletion(testModel1), QString("$aa")); + QCOMPARE(model->currentCompletion(testModel2), QString("aa")); + QCOMPARE(countItems(model), 14*2); +} + +void CompletionTest::testAbortController() +{ + KateCompletionModel *model = m_view->completionWidget()->model(); + + new CustomRangeModel(m_view, "$a"); + m_view->setCursorPosition(Cursor(0, 0)); + invokeCompletionBox(m_view); + + QCOMPARE(countItems(model), 40); + QVERIFY(m_view->completionWidget()->isCompletionActive()); + + m_view->insertText("$a"); + QTest::qWait(1000); // process events + QVERIFY(m_view->completionWidget()->isCompletionActive()); + + m_view->insertText("."); + verifyCompletionAborted(m_view); +} + +void CompletionTest::testAbortControllerMultipleModels() +{ + KateCompletionModel *model = m_view->completionWidget()->model(); + + CodeCompletionTestModel* testModel1 = new CodeCompletionTestModel(m_view, "aa"); + CodeCompletionTestModel* testModel2 = new CustomAbortModel(m_view, "a-"); + m_view->setCursorPosition(Cursor(0, 0)); + invokeCompletionBox(m_view); + + QCOMPARE(countItems(model), 80); + QVERIFY(m_view->completionWidget()->isCompletionActive()); + + m_view->insertText("a"); + QTest::qWait(1000); // process events + QVERIFY(m_view->completionWidget()->isCompletionActive()); + QCOMPARE(countItems(model), 80); + + m_view->insertText("-"); + QTest::qWait(1000); // process events + QVERIFY(m_view->completionWidget()->isCompletionActive()); + QVERIFY(!m_view->completionWidget()->completionRanges().contains(testModel1)); + QVERIFY(m_view->completionWidget()->completionRanges().contains(testModel2)); + + QCOMPARE(countItems(model), 40); + + m_view->insertText(" "); + QTest::qWait(1000); // process events + QVERIFY(!m_view->completionWidget()->isCompletionActive()); +} + +void CompletionTest::testEmptyFilterString() +{ + KateCompletionModel *model = m_view->completionWidget()->model(); + + new EmptyFilterStringModel(m_view, "aa"); + m_view->setCursorPosition(Cursor(0, 0)); + invokeCompletionBox(m_view); + + QCOMPARE(countItems(model), 40); + + m_view->insertText("a"); + QTest::qWait(1000); // process events + QCOMPARE(countItems(model), 40); + + m_view->insertText("bam"); + QTest::qWait(1000); // process events + QCOMPARE(countItems(model), 40); +} + +void CompletionTest::testUpdateCompletionRange() +{ + m_doc->setText("ab bb cc\ndd"); + KateCompletionModel *model = m_view->completionWidget()->model(); + + CodeCompletionTestModel* testModel = new UpdateCompletionRangeModel(m_view, "ab ab"); + m_view->setCursorPosition(Cursor(0, 3)); + invokeCompletionBox(m_view); + + QCOMPARE(countItems(model), 40); + QCOMPARE(Range(*m_view->completionWidget()->completionRange(testModel)), Range(Cursor(0, 3), Cursor(0, 3))); + + m_view->insertText("ab"); + QTest::qWait(1000); // process events + QCOMPARE(Range(*m_view->completionWidget()->completionRange(testModel)), Range(Cursor(0, 0), Cursor(0, 5))); + QCOMPARE(countItems(model), 40); +} + +void CompletionTest::testCustomStartCompl() +{ + KateCompletionModel *model = m_view->completionWidget()->model(); + + m_view->completionWidget()->setAutomaticInvocationDelay(1); + + new StartCompletionModel(m_view, "aa"); + + m_view->setCursorPosition(Cursor(0, 0)); + m_view->insertText("%"); + QTest::qWait(1000); + + QVERIFY(m_view->completionWidget()->isCompletionActive()); + QCOMPARE(countItems(model), 40); +} + +void CompletionTest::testKateCompletionModel() +{ + KateCompletionModel *model = m_view->completionWidget()->model(); + CodeCompletionTestModel* testModel1 = new CodeCompletionTestModel(m_view, "aa"); + CodeCompletionTestModel* testModel2 = new CodeCompletionTestModel(m_view, "bb"); + + model->setCompletionModel(testModel1); + QCOMPARE(countItems(model), 40); + + model->addCompletionModel(testModel2); + QCOMPARE(countItems(model), 80); + + model->removeCompletionModel(testModel2); + QCOMPARE(countItems(model), 40); +} + +void CompletionTest::testAbortImmideatelyAfterStart() +{ + new ImmideatelyAbortCompletionModel(m_view); + m_view->setCursorPosition(Cursor(0, 3)); + QVERIFY(!m_view->completionWidget()->isCompletionActive()); + emit m_view->userInvokedCompletion(); + QVERIFY(!m_view->completionWidget()->isCompletionActive()); +} + +void CompletionTest::testJumpToListBottomAfterCursorUpWhileAtTop() +{ + new CodeCompletionTestModel(m_view, "aa"); + invokeCompletionBox(m_view); + + m_view->completionWidget()->cursorUp(); + m_view->completionWidget()->bottom(); + // TODO - better way of finding the index? + QCOMPARE(m_view->completionWidget()->treeView()->selectionModel()->currentIndex().row(), 39); +} + +void CompletionTest::testAbbreviationEngine() +{ + QVERIFY(KateCompletionModel::matchesAbbreviation("FooBar", "fb")); + QVERIFY(KateCompletionModel::matchesAbbreviation("FooBar", "foob")); + QVERIFY(KateCompletionModel::matchesAbbreviation("FooBar", "fbar")); + QVERIFY(KateCompletionModel::matchesAbbreviation("FooBar", "fba")); + QVERIFY(KateCompletionModel::matchesAbbreviation("FooBar", "foba")); + QVERIFY(KateCompletionModel::matchesAbbreviation("FooBarBazBang", "fbbb")); + QVERIFY(KateCompletionModel::matchesAbbreviation("foo_bar_cat", "fbc")); + QVERIFY(KateCompletionModel::matchesAbbreviation("foo_bar_cat", "fb")); + QVERIFY(KateCompletionModel::matchesAbbreviation("FooBarArr", "fba")); + QVERIFY(KateCompletionModel::matchesAbbreviation("FooBarArr", "fbara")); + QVERIFY(KateCompletionModel::matchesAbbreviation("FooBarArr", "fobaar")); + QVERIFY(KateCompletionModel::matchesAbbreviation("FooBarArr", "fb")); + + QVERIFY(KateCompletionModel::matchesAbbreviation("QualifiedIdentifier", "qid")); + QVERIFY(KateCompletionModel::matchesAbbreviation("QualifiedIdentifier", "qualid")); + QVERIFY(KateCompletionModel::matchesAbbreviation("QualifiedIdentifier", "qualidentifier")); + QVERIFY(KateCompletionModel::matchesAbbreviation("QualifiedIdentifier", "qi")); + QVERIFY(KateCompletionModel::matchesAbbreviation("KateCompletionModel", "kcmodel")); + QVERIFY(KateCompletionModel::matchesAbbreviation("KateCompletionModel", "kc")); + QVERIFY(KateCompletionModel::matchesAbbreviation("KateCompletionModel", "kcomplmodel")); + QVERIFY(KateCompletionModel::matchesAbbreviation("KateCompletionModel", "kacomplmodel")); + QVERIFY(KateCompletionModel::matchesAbbreviation("KateCompletionModel", "kacom")); + + QVERIFY(! KateCompletionModel::matchesAbbreviation("QualifiedIdentifier", "identifier")); + QVERIFY(! KateCompletionModel::matchesAbbreviation("FooBarArr", "fobaara")); + QVERIFY(! KateCompletionModel::matchesAbbreviation("FooBarArr", "fbac")); + QVERIFY(! KateCompletionModel::matchesAbbreviation("KateCompletionModel", "kamodel")); + + QVERIFY(KateCompletionModel::matchesAbbreviation("AbcdefBcdefCdefDefEfFzZ", "AbcdefBcdefCdefDefEfFzZ")); + QVERIFY(! KateCompletionModel::matchesAbbreviation("AbcdefBcdefCdefDefEfFzZ", "ABCDEFX")); + QVERIFY(! KateCompletionModel::matchesAbbreviation("AaaaaaBbbbbCcccDddEeFzZ", "XZYBFA")); +} + +void CompletionTest::benchAbbreviationEngineGoodCase() +{ + QBENCHMARK { + for ( int i = 0; i < 10000; i++ ) { + QVERIFY(! KateCompletionModel::matchesAbbreviation("AaaaaaBbbbbCcccDddEeFzZ", "XZYBFA")); + } + } +} + +void CompletionTest::benchAbbreviationEngineNormalCase() +{ + QBENCHMARK { + for ( int i = 0; i < 10000; i++ ) { + QVERIFY(! KateCompletionModel::matchesAbbreviation("AaaaaaBbbbbCcccDddEeFzZ", "ABCDEFX")); + } + } +} + +void CompletionTest::benchAbbreviationEngineWorstCase() +{ + QBENCHMARK { + for ( int i = 0; i < 10000; i++ ) { + // This case is quite horrible, because it requires a branch at every letter. + // The current code will at some point drop out and just return false. + KateCompletionModel::matchesAbbreviation("XxBbbbbbBbbbbbBbbbbBbbbBbbbbbbBbbbbbBbbbbbBbbbFox", "XbbbbbbbbbbbbbbbbbbbbFx"); + } + } +} + +void CompletionTest::testAbbrevAndContainsMatching() +{ + KateCompletionModel *model = m_view->completionWidget()->model(); + + new AbbreviationCodeCompletionTestModel(m_view, QString()); + + m_view->document()->setText("SCA"); + invokeCompletionBox(m_view); + QCOMPARE(model->filteredItemCount(), (uint) 6); + + m_view->document()->setText("SC"); + invokeCompletionBox(m_view); + QCOMPARE(model->filteredItemCount(), (uint) 6); + + m_view->document()->setText("sca"); + invokeCompletionBox(m_view); + QCOMPARE(model->filteredItemCount(), (uint) 6); + + m_view->document()->setText("contains"); + invokeCompletionBox(m_view); + QCOMPARE(model->filteredItemCount(), (uint) 2); + + m_view->document()->setText("CONTAINS"); + invokeCompletionBox(m_view); + QCOMPARE(model->filteredItemCount(), (uint) 2); + + m_view->document()->setText("containssome"); + invokeCompletionBox(m_view); + QCOMPARE(model->filteredItemCount(), (uint) 1); + + m_view->document()->setText("matched"); + m_view->userInvokedCompletion(); + QApplication::processEvents(); + QCOMPARE(model->filteredItemCount(), (uint) 0); +} + +void CompletionTest::benchCompletionModel() +{ + const QString text("abcdefg abcdef"); + m_doc->setText(text); + CodeCompletionTestModel* testModel1 = new CodeCompletionTestModel(m_view, "abcdefg"); + testModel1->setRowCount(500); + CodeCompletionTestModel* testModel2 = new CodeCompletionTestModel(m_view, "abcdef"); + testModel2->setRowCount(500); + CodeCompletionTestModel* testModel3 = new CodeCompletionTestModel(m_view, "abcde"); + testModel3->setRowCount(500); + CodeCompletionTestModel* testModel4 = new CodeCompletionTestModel(m_view, "abcd"); + testModel4->setRowCount(5000); + QBENCHMARK_ONCE { + for(int i = 0; i < text.size(); ++i) { + m_view->setCursorPosition(Cursor(0, i)); + invokeCompletionBox(m_view); + } + } +} + +#include "moc_completion_test.cpp" diff --git a/kate/tests/completion_test.h b/kate/tests/completion_test.h new file mode 100644 index 00000000..866fb148 --- /dev/null +++ b/kate/tests/completion_test.h @@ -0,0 +1,68 @@ +/* This file is part of the KDE libraries + Copyright (C) 2008 Niko Sams + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KATE_COMPLETIONTEST_H +#define KATE_COMPLETIONTEST_H + +#include + +namespace KTextEditor { + class Document; +} +class KateView; + +class CompletionTest : public QObject +{ + Q_OBJECT + + public: + CompletionTest() {} + virtual ~CompletionTest() {} + + private Q_SLOTS: + void init(); + void cleanup(); + void testFilterEmptyRange(); + void testFilterWithRange(); + void testCustomRange1(); + void testCustomRange2(); + void testCustomRangeMultipleModels(); + void testAbortCursorMovedOutOfRange(); + void testAbortInvalidText(); + void testAbortController(); + void testAbortControllerMultipleModels(); + void testEmptyFilterString(); + void testUpdateCompletionRange(); + void testCustomStartCompl(); + void testKateCompletionModel(); + void testAbortImmideatelyAfterStart(); + void testJumpToListBottomAfterCursorUpWhileAtTop(); + void testAbbrevAndContainsMatching(); + void testAbbreviationEngine(); + void benchAbbreviationEngineNormalCase(); + void benchAbbreviationEngineWorstCase(); + void benchAbbreviationEngineGoodCase(); + void benchCompletionModel(); + + private: + KTextEditor::Document* m_doc; + KateView *m_view; +}; + +#endif diff --git a/kate/tests/data/bug311866.cpp b/kate/tests/data/bug311866.cpp new file mode 100644 index 00000000..d9b74126 --- /dev/null +++ b/kate/tests/data/bug311866.cpp @@ -0,0 +1,6 @@ +int main() +{ + int this_line_is_safe=1; + int that_line_will_cause_havoc=1; +} + diff --git a/kate/tests/data/bug313769.cpp b/kate/tests/data/bug313769.cpp new file mode 100644 index 00000000..b1492869 --- /dev/null +++ b/kate/tests/data/bug313769.cpp @@ -0,0 +1,74 @@ +/* This file is part of the KDE libraries + Copyright (C) 1999 Jochen Wilhelmy +*/ +//BEGIN includes +#include "kateview.h" +#include "moc_kateview.cpp" + +//#define VIEW_RANGE_DEBUG + +//END includes + +void KateView::blockFix(KTextEditor::Range& range) +{ + if (range.start().column() > range.end().column()) + { + int tmp = range.start().column(); + range.start().setColumn(range.end().column()); + range.end().setColumn(tmp); + } +} + +KateView::KateView( KateDocument *doc, QWidget *parent ) +{ + setComponentData ( KateGlobal::self()->componentData () ); + + // selection if for this view only and will invalidate if becoming empty + m_selection.setView (this); + + // use z depth defined in moving ranges interface + m_selection.setZDepth (-100000.0); + + KateGlobal::self()->registerView( this ); + + KTextEditor::ViewBarContainer *viewBarContainer=qobject_cast( KateGlobal::self()->container() ); + QWidget *bottomBarParent=viewBarContainer?viewBarContainer->getViewBarParent(this,KTextEditor::ViewBarContainer::BottomBar):0; + QWidget *topBarParent=viewBarContainer?viewBarContainer->getViewBarParent(this,KTextEditor::ViewBarContainer::TopBar):0; + + m_bottomViewBar=new KateViewBar (bottomBarParent!=0,KTextEditor::ViewBarContainer::BottomBar,bottomBarParent?bottomBarParent:this,this); + m_topViewBar=new KateViewBar (topBarParent!=0,KTextEditor::ViewBarContainer::TopBar,topBarParent?topBarParent:this,this); + + // ugly workaround: + // Force the layout to be left-to-right even on RTL deskstop, as discussed + // on the mailing list. This will cause the lines and icons panel to be on + // the left, even for Arabic/Hebrew/Farsi/whatever users. + setLayoutDirection ( Qt::LeftToRight ); + + // layouting ;) + m_vBox = new QVBoxLayout (this); + m_vBox->setMargin (0); + m_vBox->setSpacing (0); + + // add top viewbar... + if (topBarParent) + viewBarContainer->addViewBarToLayout(this,m_topViewBar,KTextEditor::ViewBarContainer::TopBar); + else + m_vBox->addWidget(m_topViewBar); + + m_bottomViewBar->installEventFilter(m_viewInternal); + + // add KateMessageWidget for KTE::MessageInterface immediately above view + m_topMessageWidget = new KateMessageWidget(this); + m_vBox->addWidget(m_topMessageWidget); + m_topMessageWidget->hide(); + + // add hbox: KateIconBorder | KateViewInternal | KateScrollBar + QHBoxLayout *hbox = new QHBoxLayout (); + m_vBox->addLayout (hbox, 100); + hbox->setMargin (0); + hbox->setSpacing (0); + + QStyleOption option; + option.initFrom(this); + + if (style()->styleHint(QStyle::SH_ScrollView_FrameOnlyAroundContents, &option, this)) { diff --git a/kate/tests/data/sha1checksum.txt b/kate/tests/data/sha1checksum.txt new file mode 100644 index 00000000..445adcaa --- /dev/null +++ b/kate/tests/data/sha1checksum.txt @@ -0,0 +1,1066 @@ +/* This file is part of the KDE libraries and the Kate part. + * + * Copyright (C) 2003-2005 Anders Lund + * Copyright (C) 2001-2010 Christoph Cullmann + * Copyright (C) 2001 Charles Samuels + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "katecmds.h" + +#include "katedocument.h" +#include "kateview.h" +#include "kateconfig.h" +#include "kateautoindent.h" +#include "katetextline.h" +#include "katesyntaxmanager.h" +#include "kateglobal.h" +#include "kateviglobal.h" +#include "katevinormalmode.h" +#include "katerenderer.h" +#include "katecmd.h" + +#include +#include +#include +#include + +#include +#include + +//BEGIN CoreCommands +KateCommands::CoreCommands* KateCommands::CoreCommands::m_instance = 0; + +// this returns wheather the string s could be converted to +// a bool value, one of on|off|1|0|true|false. the argument val is +// set to the extracted value in case of success +static bool getBoolArg( const QString &t, bool *val ) +{ + bool res( false ); + QString s = t.toLower(); + res = (s == "on" || s == "1" || s == "true"); + if ( res ) + { + *val = true; + return true; + } + res = (s == "off" || s == "0" || s == "false"); + if ( res ) + { + *val = false; + return true; + } + return false; +} + +const QStringList &KateCommands::CoreCommands::cmds() +{ + static QStringList l; + + if (l.isEmpty()) + l << "indent" << "unindent" << "cleanindent" + << "comment" << "uncomment" << "goto" << "kill-line" + << "set-tab-width" << "set-replace-tabs" << "set-show-tabs" + << "set-remove-trailing-space" + << "set-indent-width" + << "set-indent-mode" << "set-auto-indent" + << "set-line-numbers" << "set-folding-markers" << "set-icon-border" + << "set-wrap-cursor" + << "set-word-wrap" << "set-word-wrap-column" + << "set-replace-tabs-save" << "set-remove-trailing-space-save" + << "set-highlight" << "set-mode" << "set-show-indent" + << "print"; + + return l; +} + +bool KateCommands::AppCommands::help(KTextEditor::View *view, const QString &cmd, QString &msg) +{ + QString realcmd=cmd.trimmed(); +if (realcmd=="cleanindent") { + msg=; + return true; + } else if (realcmd=="comment") { + msg=; + return true; + } else if (realcmd=="uncomment") { + msg=; + return true; + } else if (realcmd=="goto") { + msg=; + return true; + } else if (realcmd=="kill-line") { + msg=; + return true; + } else if (realcmd=="set-tab-width") { + msg=; + return true; + } else if (realcmd=="set-replace-tab") { + msg=; + return true; + } else if (realcmd=="set-show-tabs") { + msg=; + return true; + } else if (realcmd=="set-remove-trailing-space") { + msg=; + return true; + } else if (realcmd=="set-indent-width") { + msg=; + return true; + } else if (realcmd=="set-indent-mode") { + msg=; + return true; + } else if (realcmd=="set-auto-indent") { + msg=; + return true; + } else if (realcmd=="set-line-numbers") { + msg=; + return true; + } else if (realcmd=="set-folding-markers") { + msg=; + return true; + } else if (realcmd=="set-icon-border") { + msg=; + return true; + } else if (realcmd=="set-wrap-cursor") { + msg=; + return true; + } else if (realcmd=="set-word-wrap") { + msg=; + return true; + } else if (realcmd=="set-word-wrap-column") { + msg=; + return true; + } else if (realcmd=="set-replace-tabs-save") { + msg=; + return true; + } else if (realcmd=="set-remove-trailing-space-save") { + msg=; + return true; + } else if (realcmd=="set-highlight") { + msg=; + return true; + } else if (realcmd=="set-mode") { + msg=; + return true; + } else if (realcmd=="set-show-indent") { + msg=; + return true; + } else if (realcmd=="print") { + msg=; + return true; + } else + + + return false; +} + +bool KateCommands::CoreCommands::exec(KTextEditor::View *view, + const QString &_cmd, + QString &errorMsg) +{ + return exec( view, _cmd, errorMsg, KTextEditor::Range::invalid() ); +} + +bool KateCommands::CoreCommands::exec(KTextEditor::View *view, + const QString &_cmd, + QString &errorMsg, + const KTextEditor::Range& range) +{ +#define KCC_ERR(s) { errorMsg=s; return false; } + // cast it hardcore, we know that it is really a kateview :) + KateView *v = static_cast(view); + + if ( ! v ) + KCC_ERR( i18n("Could not access view") ); + + //create a list of args + QStringList args(_cmd.split( QRegExp("\\s+"), QString::SkipEmptyParts)) ; + QString cmd ( args.takeFirst() ); + + // ALL commands that takes no arguments. + if ( cmd == "indent" ) + { + if ( range.isValid() ) { + v->doc()->editStart(); + for ( int line = range.start().line(); line <= range.end().line(); line++ ) { + v->doc()->indent( KTextEditor::Range(line, 0, line, 0), 1 ); + } + v->doc()->editEnd(); + } else { + v->indent(); + } + return true; + } + else if ( cmd == "unindent" ) + { + if ( range.isValid() ) { + v->doc()->editStart(); + for ( int line = range.start().line(); line <= range.end().line(); line++ ) { + v->doc()->indent( KTextEditor::Range(line, 0, line, 0), -1 ); + } + v->doc()->editEnd(); + } else { + v->unIndent(); + } + return true; + } + else if ( cmd == "cleanindent" ) + { + if ( range.isValid() ) { + v->doc()->editStart(); + for ( int line = range.start().line(); line <= range.end().line(); line++ ) { + v->doc()->indent( KTextEditor::Range(line, 0, line, 0), 0 ); + } + v->doc()->editEnd(); + } else { + v->cleanIndent(); + } + return true; + } + else if ( cmd == "comment" ) + { + if ( range.isValid() ) { + v->doc()->editStart(); + for ( int line = range.start().line(); line <= range.end().line(); line++ ) { + v->doc()->comment( v, line, 0, 1 ); + } + v->doc()->editEnd(); + } else { + v->comment(); + } + return true; + } + else if ( cmd == "uncomment" ) + { + if ( range.isValid() ) { + v->doc()->editStart(); + for ( int line = range.start().line(); line <= range.end().line(); line++ ) { + v->doc()->comment( v, line, 0, -1 ); + } + v->doc()->editEnd(); + } else { + v->uncomment(); + } + return true; + } + else if ( cmd == "kill-line" ) + { + if ( range.isValid() ) { + v->doc()->editStart(); + for ( int line = range.start().line(); line <= range.end().line(); line++ ) { + v->doc()->removeLine( range.start().line() ); + } + v->doc()->editEnd(); + } else { + v->killLine(); + } + return true; + } + else if ( cmd == "print" ) + { + v->doc()->printDialog(); + return true; + } + + // ALL commands that take a string argument + else if ( cmd == "set-indent-mode" || + cmd == "set-highlight" || + cmd == "set-mode" ) + { + // need at least one item, otherwise args.first() crashes + if ( ! args.count() ) + KCC_ERR( i18n("Missing argument. Usage: %1 ", cmd ) ); + + if ( cmd == "set-indent-mode" ) + { + v->doc()->config()->setIndentationMode( args.first() ); + return true; + } + else if ( cmd == "set-highlight" ) + { + if ( v->doc()->setHighlightingMode( args.first()) ) + { + static_cast(v->doc())->setDontChangeHlOnSave (); + return true; + } + + KCC_ERR( i18n("No such highlighting '%1'", args.first() ) ); + } + else if ( cmd == "set-mode" ) + { + if ( v->doc()->setMode( args.first()) ) + return true; + + KCC_ERR( i18n("No such mode '%1'", args.first() ) ); + } + } + // ALL commands that takes exactly one integer argument. + else if ( cmd == "set-tab-width" || + cmd == "set-indent-width" || + cmd == "set-word-wrap-column" || + cmd == "goto" ) + { + // find a integer value > 0 + if ( ! args.count() ) + KCC_ERR( i18n("Missing argument. Usage: %1 ", cmd ) ); + bool ok; + int val ( args.first().toInt( &ok, 10 ) ); // use base 10 even if the string starts with '0' + if ( !ok ) + KCC_ERR( i18n("Failed to convert argument '%1' to integer.", + args.first() ) ); + + if ( cmd == "set-tab-width" ) + { + if ( val < 1 ) + KCC_ERR( i18n("Width must be at least 1.") ); + v->doc()->config()->setTabWidth( val ); + } + else if ( cmd == "set-indent-width" ) + { + if ( val < 1 ) + KCC_ERR( i18n("Width must be at least 1.") ); + v->doc()->config()->setIndentationWidth( val ); + } + else if ( cmd == "set-word-wrap-column" ) + { + if ( val < 2 ) + KCC_ERR( i18n("Column must be at least 1.") ); + v->doc()->setWordWrapAt( val ); + } + else if ( cmd == "goto" ) + { + if ( args.first().at(0) == '-' || args.first().at(0) == '+' ) { + // if the number starts with a minus or plus sign, add/subract the number + val = v->cursorPosition().line() + val; + } else { + val--; // convert given line number to the internal representation of line numbers + } + + // constrain cursor to the range [0, number of lines] + if ( val < 0 ) { + val = 0; + } else if ( val > v->doc()->lines()-1 ) { + val = v->doc()->lines()-1; + } + + v->setCursorPosition( KTextEditor::Cursor( val, 0 ) ); + return true; + } + return true; + } + + // ALL commands that takes 1 boolean argument. + else if ( cmd == "set-icon-border" || + cmd == "set-folding-markers" || + cmd == "set-line-numbers" || + cmd == "set-replace-tabs" || + cmd == "set-remove-trailing-space" || + cmd == "set-show-tabs" || + cmd == "set-word-wrap" || + cmd == "set-wrap-cursor" || + cmd == "set-replace-tabs-save" || + cmd == "set-remove-trailing-space-save" || + cmd == "set-show-indent" ) + { + if ( ! args.count() ) + KCC_ERR( i18n("Usage: %1 on|off|1|0|true|false", cmd ) ); + bool enable = false; + KateDocumentConfig * const config = v->doc()->config(); + if ( getBoolArg( args.first(), &enable ) ) + { + if ( cmd == "set-icon-border" ) + v->setIconBorder( enable ); + else if (cmd == "set-folding-markers") + v->setFoldingMarkersOn( enable ); + else if ( cmd == "set-line-numbers" ) + v->setLineNumbersOn( enable ); + else if ( cmd == "set-show-indent" ) + v->renderer()->setShowIndentLines( enable ); + else if ( cmd == "set-replace-tabs" ) + config->setReplaceTabsDyn( enable ); + else if ( cmd == "set-remove-trailing-space" ) + config->setRemoveTrailingDyn( enable ); + else if ( cmd == "set-show-tabs" ) + config->setShowTabs( enable ); + else if ( cmd == "set-show-trailing-spaces" ) + config->setShowSpaces( enable ); + else if ( cmd == "set-word-wrap" ) + v->doc()->setWordWrap( enable ); + else if ( cmd == "set-remove-trailing-space-save" ) + config->setRemoveSpaces( enable ); + else if ( cmd == "set-wrap-cursor" ) + config->setWrapCursor( enable ); + + return true; + } + else + KCC_ERR( i18n("Bad argument '%1'. Usage: %2 on|off|1|0|true|false", + args.first() , cmd ) ); + } + + // unlikely.. + KCC_ERR( i18n("Unknown command '%1'", cmd) ); +} + +bool KateCommands::CoreCommands::supportsRange(const QString &range) +{ + static QStringList l; + + if (l.isEmpty()) + l << "indent" << "unindent" << "cleanindent" + << "comment" << "uncomment" << "kill-line"; + + return l.contains(range); +} + +KCompletion *KateCommands::CoreCommands::completionObject( KTextEditor::View *view, const QString &cmd ) +{ + Q_UNUSED(view) + + if ( cmd == "set-highlight" ) + { + QStringList l; + for ( int i = 0; i < KateHlManager::self()->highlights(); i++ ) + l << KateHlManager::self()->hlName (i); + + KateCmdShellCompletion *co = new KateCmdShellCompletion(); + co->setItems( l ); + co->setIgnoreCase( true ); + return co; + } + return 0L; +} +//END CoreCommands + +// BEGIN ViCommands +KateCommands::ViCommands* KateCommands::ViCommands::m_instance = 0; + +const QStringList &KateCommands::ViCommands::cmds() +{ + static QStringList l; + + if (l.isEmpty()) + l << "nnoremap" << "nn" << "d" << "delete" << "j" << "c" << "change" << "<" << ">" << "y" << "yank" << + "ma" << "mark" << "k"; + + return l; +} + +bool KateCommands::ViCommands::exec(KTextEditor::View *view, + const QString &_cmd, + QString &msg) +{ + return exec( view, _cmd, msg, KTextEditor::Range::invalid() ); +} + +bool KateCommands::ViCommands::exec(KTextEditor::View *view, + const QString &_cmd, + QString &msg, + const KTextEditor::Range& range) +{ + Q_UNUSED(range) + // cast it hardcore, we know that it is really a kateview :) + KateView *v = static_cast(view); + + if ( !v ) { + msg = i18n("Could not access view"); + return false; + } + + //create a list of args + QStringList args(_cmd.split( QRegExp("\\s+"), QString::SkipEmptyParts)) ; + QString cmd ( args.takeFirst() ); + + // ALL commands that takes no arguments. + if ( cmd == "nnoremap" || cmd == "nn" ) + { + if ( args.count() == 1 ) { + msg = KateGlobal::self()->viInputModeGlobal()->getMapping( NormalMode, args.at( 0 ), true ); + if ( msg.isEmpty() ) { + msg = i18n( "No mapping found for \"%1\"", args.at(0) ); + return false; + } else { + msg = i18n( "\"%1\" is mapped to \"%2\"", args.at( 0 ), msg ); + } + } else if ( args.count() == 2 ) { + KateGlobal::self()->viInputModeGlobal()->addMapping( NormalMode, args.at( 0 ), args.at( 1 ) ); + } else { + msg = i18n("Missing argument(s). Usage: %1 []", cmd ); + return false; + } + + return true; + } + + KateViNormalMode* nm = v->getViInputModeManager()->getViNormalMode(); + + if (cmd == "d" || cmd == "delete" || cmd == "j" || + cmd == "c" || cmd == "change" || cmd == "<" || cmd == ">" || + cmd == "y" || cmd == "yank") { + + KTextEditor::Cursor start_cursor_position = v->cursorPosition(); + + int count = 1; + if (range.isValid()){ + count = qAbs(range.end().line() - range.start().line())+1; + if (cmd == "j") count-=1; + v->setCursorPosition(KTextEditor::Cursor(qMin(range.start().line(), + range.end().line()),0)); + } + + QRegExp number("^(\\d+)$"); + for (int i = 0; i < args.count(); i++) { + if (number.indexIn(args.at(i)) != -1) + count += number.cap().toInt() - 1; + + QChar r = args.at(i).at(0); + if (args.at(i).size() == 1 && ( (r >= 'a' && r <= 'z') || r == '_' || r == '+' || r == '*' )) + nm->setRegister(r); + } + + nm->setCount(count); + + if (cmd == "d" || cmd == "delete" ) + nm->commandDeleteLine(); + if (cmd == "j") + nm->commandJoinLines(); + if (cmd == "c" || cmd == "change" ) + nm->commandChangeLine(); + if (cmd == "<") + nm->commandUnindentLine(); + if (cmd == ">") + nm->commandIndentLine(); + if (cmd == "y" || cmd == "yank" ){ + nm->commandYankLine(); + v->setCursorPosition(start_cursor_position); + } + + // TODO - should we resetParser, here? We'd have to make it public, if so. + // Or maybe synthesise a KateViCommand to execute instead ... ? + nm->setCount(0); + + return true; + } + + if (cmd == "mark" || cmd == "ma" || cmd == "k" ) { + if (args.count() == 0){ + if (cmd == "mark"){ + // TODO: show up mark list; + } else { + msg = i18n("Wrong arguments"); + return false; + } + } else if (args.count() == 1) { + + QChar r = args.at(0).at(0); + int line; + if ( (r >= 'a' && r <= 'z') || r == '_' || r == '+' || r == '*' ) { + if (range.isValid()) + line = qMax(range.end().line(),range.start().line()); + else + line = v->cursorPosition().line(); + + v->getViInputModeManager()->addMark(v->doc(),r,KTextEditor::Cursor(line, 0)); + } + } else { + msg = i18n("Wrong arguments"); + return false; + } + return true; + } + + // should not happen :) + msg = i18n("Unknown command '%1'", cmd); + return false; +} + +bool KateCommands::ViCommands::supportsRange(const QString &range) +{ + static QStringList l; + + if (l.isEmpty()) + l << "d" << "delete" << "j" << "c" << "change" << "<" << + ">" << "y" << "yank" << "ma" << "mark" << "k"; + + return l.contains(range.split(" ").at(0)); +} + +KCompletion *KateCommands::ViCommands::completionObject( KTextEditor::View *view, const QString &cmd ) +{ + Q_UNUSED(view) + + KateView *v = static_cast(view); + + if ( v && ( cmd == "nn" || cmd == "nnoremap" ) ) + { + QStringList l = KateGlobal::self()->viInputModeGlobal()->getMappings( NormalMode ); + + KateCmdShellCompletion *co = new KateCmdShellCompletion(); + co->setItems( l ); + co->setIgnoreCase( false ); + return co; + } + return 0L; +} +//END ViCommands + +// BEGIN AppCommands +KateCommands::AppCommands* KateCommands::AppCommands::m_instance = 0; + +KateCommands::AppCommands::AppCommands() + : KTextEditor::Command() +{ + re_write.setPattern("w"); // temporarily add :w + //re_write.setPattern("w(a)?"); + //re_quit.setPattern("(w)?q?(a)?"); + //re_exit.setPattern("x(a)?"); + //re_changeBuffer.setPattern("b(n|p)"); + //re_edit.setPattern("e(dit)?"); + //re_new.setPattern("(v)?new"); +} + +const QStringList& KateCommands::AppCommands::cmds() +{ + static QStringList l; + + if (l.empty()) { + //l << "q" << "qa" << "w" << "wq" << "wa" << "wqa" << "x" << "xa" + //<< "bn" << "bp" << "new" << "vnew" << "e" << "edit" << "enew"; + l << "w"; + } + + return l; +} + +// commands that don't need to live in the hosting application should be +// implemented here. things such as quitting and splitting the view can not be +// done from the editor part and needs to be implemented in the hosting +// application. +bool KateCommands::AppCommands::exec(KTextEditor::View *view, + const QString &cmd, QString &msg ) +{ + QStringList args(cmd.split( QRegExp("\\s+"), QString::SkipEmptyParts)) ; + QString command( args.takeFirst() ); + QString file = args.join(QString(' ')); + + if (re_write.exactMatch(command)) { // w, wa + /* if (!re_write.cap(1).isEmpty()) { // [a]ll + view->document()->saveAll(); + msg = i18n("All documents written to disk"); + } else { // w*/ + // Save file + if (file.isEmpty()) { + view->document()->documentSave(); + } else { + KUrl base = view->document()->url(); + KUrl url( base.isValid() ? base : KUrl( QDir::homePath() ), file ); + view->document()->saveAs( url ); + } + msg = i18n("Document written to disk"); + } + + return true; +} + +bool KateCommands::AppCommands::help(KTextEditor::View *view, const QString &cmd, QString &msg) +{ + Q_UNUSED(view); + + if (re_write.exactMatch(cmd)) { + msg = i18n("

    w/wa — write document(s) to disk

    " + "

    Usage: w[a]

    " + "

    Writes the current document(s) to disk. " + "It can be called in two ways:
    " + " w — writes the current document to disk
    " + " wa — writes all document to disk.

    " + "

    If no file name is associated with the document, " + "a file dialog will be shown.

    "); + return true; + } + return false; +} +//END AppCommands + +//BEGIN SedReplace +KateCommands::SedReplace* KateCommands::SedReplace::m_instance = 0; + +static void replace(QString &s, const QString &needle, const QString &with) +{ + int pos = 0; + while (1) + { + pos = s.indexOf(needle, pos); + if (pos == -1) break; + s.replace(pos, needle.length(), with); + pos += with.length(); + } + +} + +static int backslashString(const QString &haystack, const QString &needle, int index) +{ + int len = haystack.length(); + int searchlen = needle.length(); + bool evenCount = true; + while (index < len) + { + if (haystack[index] == '\\') + { + evenCount = !evenCount; + } + else + { // isn't a slash + if (!evenCount) + { + if (haystack.mid(index, searchlen) == needle) + return index - 1; + } + evenCount = true; + } + ++index; + + } + + return -1; +} + +// exchange "\t" for the actual tab character, for example +static void exchangeAbbrevs(QString &str) +{ + // the format is (findreplace)*[nullzero] + const char *magic = "a\x07t\tn\n"; + + while (*magic) + { + int index = 0; + char replace = magic[1]; + while ((index = backslashString(str, QString (QChar::fromAscii(*magic)), index)) != -1) + { + str.replace(index, 2, QChar(replace)); + ++index; + } + ++magic; + ++magic; + } +} + +int KateCommands::SedReplace::sedMagic( KateDocument *doc, int &line, + const QString &find, const QString &repOld, const QString &delim, + bool noCase, bool repeat, + int startcol, int endcol ) +{ + Kate::TextLine ln = doc->kateTextLine( line ); + if ( !ln || !ln->length() ) return 0; + + // HANDLING "\n"s in PATTERN + // * Create a list of patterns, splitting PATTERN on (unescaped) "\n" + // * insert $s and ^s to match line ends/beginnings + // * When matching patterns after the first one, replace \N with the captured + // text. + // * If all patterns in the list match sequential lines, there is a match, so + // * remove line/start to line + patterns.count()-1/patterns.last.length + // * handle captures by putting them in one list. + // * the existing insertion is fine, including the line calculation. + + QStringList patterns(find.split( QRegExp("(^\\\\n|(?![^\\\\])\\\\n)"), QString::KeepEmptyParts)); + if ( patterns.count() > 1 ) + { + for ( int i = 0; i < patterns.count(); ++i ) + { + if ( i < patterns.count() - 1 ) + patterns[i].append("$"); + if ( i ) + patterns[i].prepend("^"); + + kDebug(13025) << "patterns[" << i << "] =" << patterns[i]; + } + } + + QRegExp matcher(patterns[0], noCase ?Qt::CaseSensitive:Qt::CaseInsensitive); + + int matches = 0; + + while ( (startcol = matcher.indexIn(ln->string(), startcol)) >= 0 ) + { + const int len = matcher.matchedLength(); + + if ( endcol >= 0 && startcol + len > endcol ) + break; + + ++matches; + + + QString rep = repOld; + + // now set the backreferences in the replacement + const QStringList backrefs = matcher.capturedTexts(); + int refnum = 1; + + QStringList::ConstIterator i = backrefs.begin(); + ++i; + + for (; i != backrefs.end(); ++i) + { + // I need to match "\\" or "", but not "\" + QString number = QString::number(refnum); + + int index = 0; + while (index != -1) + { + index = backslashString(rep, number, index); + if (index >= 0) + { + rep.replace(index, 2, *i); + index += (*i).length(); + } + } + + ++refnum; + } + + replace(rep, "\\\\", "\\"); + replace(rep, "\\" + delim, delim); + + doc->removeText( KTextEditor::Range (line, startcol, line, startcol + len) ); + doc->insertText( KTextEditor::Cursor (line, startcol), rep ); + + // TODO if replace contains \n, + // change the line number and + // check for text that needs be searched behind the last inserted newline. + int lns = rep.count(QChar::fromLatin1('\n')); + if ( lns > 0 ) + { + line += lns; + + if ( doc->lineLength( line ) > 0 && ( endcol < 0 || endcol >= startcol + len ) ) + { + // if ( endcol >= startcol + len ) + endcol -= (startcol + len); + uint sc = rep.length() - rep.lastIndexOf('\n') - 1; + matches += sedMagic( doc, line, find, repOld, delim, noCase, repeat, sc, endcol ); + } + } + + if (!repeat) break; + startcol += rep.length(); + + // sanity check -- avoid infinite loops eg with %s,.*,,g ;) + int ll = ln->length(); + if ( !ll || startcol > (ll - 1) ) + break; + } + + return matches; +} + +bool KateCommands::SedReplace::exec (KTextEditor::View *view, const QString &cmd, QString &msg) +{ + return exec(view, cmd, msg, KTextEditor::Range::invalid()); +} + +bool KateCommands::SedReplace::exec (class KTextEditor::View *view, const QString &cmd, + QString &msg, const KTextEditor::Range &r) +{ + kDebug(13025) << "SedReplace::execCmd( " << cmd << " )"; + if (r.isValid()) { + kDebug(13025) << "Range: " << r; + } + + QRegExp delim("^s\\s*([^\\w\\s])"); + if ( delim.indexIn( cmd ) < 0 ) return false; + + bool noCase = cmd[cmd.length() - 1] == 'i' || cmd[cmd.length() - 2] == 'i'; + bool repeat = cmd[cmd.length() - 1] == 'g' || cmd[cmd.length() - 2] == 'g'; + + QString d = delim.cap(1); + kDebug(13025) << "SedReplace: delimiter is '" << d << "'"; + + QRegExp splitter( QString("^s\\s*") + d + "((?:[^\\\\\\" + d + "]|\\\\.)*)\\" + + d + "((?:[^\\\\\\" + d + "]|\\\\.)*)(\\" + d + "[ig]{0,2})?$" ); + if (splitter.indexIn(cmd) < 0) return false; + + QString find = splitter.cap(1); + kDebug(13025) << "SedReplace: find =" << find; + + QString replace = splitter.cap(2); + exchangeAbbrevs(replace); + kDebug(13025) << "SedReplace: replace =" << replace; + + if ( find.contains("\\n") ) + { + // FIXME: make replacing newlines work + msg = i18n("Sorry, but Kate is not able to replace newlines, yet"); + return false; + } + + KateDocument *doc = static_cast(view)->doc(); + if ( !doc ) return false; + + static_cast(view)->setSearchPattern(find); + doc->editStart(); + + int replacementsDone = 0; + int linesTouched = 0; + int linesAdded = 0; + + if (r.isValid()) { // given range + for (int line = r.start().line(); line <= r.end().line() + linesAdded; ++line) { + int temp = replacementsDone; + int r = sedMagic( doc, line, find, replace, d, !noCase, repeat ); + replacementsDone += r; + + // if we replaced the text with n newlines, we have n new lines to look at + if (replace.contains('\n')) { + linesAdded += r * replace.count('\n'); + } + + if (replacementsDone > temp) { + ++linesTouched; + } + } + } else { // current line + int line = view->cursorPosition().line(); + replacementsDone += sedMagic(doc, line, find, replace, d, !noCase, repeat); + if (replacementsDone > 0) { + linesTouched = 1; + } + } + + msg = i18ncp("%2 is the translation of the next message", + "1 replacement done on %2", "%1 replacements done on %2", replacementsDone, + i18ncp("substituted into the previous message", + "1 line", "%1 lines", linesTouched)); + + doc->editEnd(); + + return true; +} + +//END SedReplace + +//BEGIN Character +KateCommands::Character* KateCommands::Character::m_instance = 0; + +bool KateCommands::Character::help (class KTextEditor::View *, const QString &cmd, QString &msg) +{ + if (cmd.trimmed()=="char") { + msg = i18n("

    char identifier

    " + "

    This command allows you to insert literal characters by their numerical identifier, in decimal, octal or hexadecimal form.

    " + "

    Examples:

      " + "
    • char 234
    • " + "
    • char 0x1234
    • " + "

    "); + return true; + } + return false; +} + +bool KateCommands::Character::exec (KTextEditor::View *view, const QString &_cmd, QString &) +{ + QString cmd = _cmd; + + // hex, octal, base 9+1 + QRegExp num("^char *(0?x[0-9A-Fa-f]{1,4}|0[0-7]{1,6}|[0-9]{1,5})$"); + if (num.indexIn(cmd)==-1) return false; + + cmd=num.cap(1); + + // identify the base + + unsigned short int number=0; + int base=10; + if (cmd[0]=='x' || cmd.startsWith(QLatin1String("0x"))) + { + cmd.remove(QRegExp("^0?x")); + base=16; + } + else if (cmd[0]=='0') + base=8; + bool ok; + number=cmd.toUShort(&ok, base); + if (!ok || number==0) return false; + if (number<=255) + { + char buf[2]; + buf[0]=(char)number; + buf[1]=0; + + view->document()->insertText(view->cursorPosition(), QString(buf)); + } + else + { // do the unicode thing + QChar c(number); + + view->document()->insertText(view->cursorPosition(), QString(&c, 1)); + } + + return true; +} + +//END Character + +//BEGIN Date +KateCommands::Date* KateCommands::Date::m_instance = 0; + +bool KateCommands::Date::help (class KTextEditor::View *, const QString &cmd, QString &msg) +{ + if (cmd.trimmed()=="date") { + msg = i18n("

    date or date format

    " + "

    Inserts a date/time string as defined by the specified format, or the format yyyy-MM-dd hh:mm:ss if none is specified." + "

    Possible format specifiers are:" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "
    ddThe day as number with a leading zero (01-31).
    dddThe abbreviated localized day name (e.g. 'Mon'..'Sun').
    ddddThe long localized day name (e.g. 'Monday'..'Sunday').
    MThe month as number without a leading zero (1-12).
    MMThe month as number with a leading zero (01-12).
    MMMThe abbreviated localized month name (e.g. 'Jan'..'Dec').
    yyThe year as two digit number (00-99).
    yyyyThe year as four digit number (1752-8000).
    hThe hour without a leading zero (0..23 or 1..12 if AM/PM display).
    hhThe hour with a leading zero (00..23 or 01..12 if AM/PM display).
    mThe minute without a leading zero (0..59).
    mmThe minute with a leading zero (00..59).
    sThe second without a leading zero (0..59).
    ssThe second with a leading zero (00..59).
    zThe milliseconds without leading zeroes (0..999).
    zzzThe milliseconds with leading zeroes (000..999).
    APUse AM/PM display. AP will be replaced by either \"AM\" or \"PM\".
    apUse am/pm display. ap will be replaced by either \"am\" or \"pm\".

    "); + return true; + } + return false; +} + +bool KateCommands::Date::exec (KTextEditor::View *view, const QString &cmd, QString &) +{ + if (!cmd.startsWith(QLatin1String("date"))) + return false; + + if (QDateTime::currentDateTime().toString(cmd.mid(5, cmd.length()-5)).length() > 0) + view->document()->insertText(view->cursorPosition(), QDateTime::currentDateTime().toString(cmd.mid(5, cmd.length()-5))); + else + view->document()->insertText(view->cursorPosition(), QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")); + + return true; +} + +//END Date + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/tests/encoding/CMakeLists.txt b/kate/tests/encoding/CMakeLists.txt new file mode 100644 index 00000000..3c3b88ac --- /dev/null +++ b/kate/tests/encoding/CMakeLists.txt @@ -0,0 +1,42 @@ +# test executable for encoding +kde4_add_manual_test(kateencodingtest kateencodingtest.cpp) +target_link_libraries(kateencodingtest ${KATE_TEST_LINK_LIBS}) + +# test macro for encoding tests +MACRO(KATE_ENCODING_TEST _encoding _testname) + ADD_TEST (${_testname}_create kateencodingtest ${_encoding} ${CMAKE_CURRENT_SOURCE_DIR}/${_testname} ${CMAKE_CURRENT_BINARY_DIR}/${_testname} ) + ADD_TEST (${_testname}_diff ${CMAKE_COMMAND} -E compare_files ${CMAKE_CURRENT_SOURCE_DIR}/${_testname} ${CMAKE_CURRENT_BINARY_DIR}/${_testname} ) +ENDMACRO(KATE_ENCODING_TEST) + +# add tests + +# this file is utf-8, simple +KATE_ENCODING_TEST ("utf-8" "utf8.txt") + +# this file is latin15, but fallback should work! +KATE_ENCODING_TEST ("utf-8" "latin15.txt") + +# this file is utf32, little endian, but fallback should work! +KATE_ENCODING_TEST ("utf-8" "utf32.txt") + +# this file is utf16, little endian, but fallback should work! +KATE_ENCODING_TEST ("utf-8" "utf16.txt") + +# this file is utf32, big endian, but fallback should work! +KATE_ENCODING_TEST ("utf-8" "utf32be.txt") + +# this file is utf16, big endian, but fallback should work! +KATE_ENCODING_TEST ("utf-8" "utf16be.txt") + +# cyrillic utf-8 +KATE_ENCODING_TEST ("utf-8" "cyrillic_utf8.txt") + +# cyrillic cp1251 +KATE_ENCODING_TEST ("utf-8" "cp1251.txt") + +# cyrillic koi8-r +KATE_ENCODING_TEST ("utf-8" "koi8-r.txt") + +# one character latin-15 test, segfaulted +KATE_ENCODING_TEST ("utf-8" "one-char-latin-15.txt") + diff --git a/kate/tests/encoding/cp1251.txt b/kate/tests/encoding/cp1251.txt new file mode 100644 index 00000000..eaac6328 --- /dev/null +++ b/kate/tests/encoding/cp1251.txt @@ -0,0 +1,2 @@ +Testing + \ No newline at end of file diff --git a/kate/tests/encoding/cyrillic_utf8.txt b/kate/tests/encoding/cyrillic_utf8.txt new file mode 100644 index 00000000..51888b2e --- /dev/null +++ b/kate/tests/encoding/cyrillic_utf8.txt @@ -0,0 +1,2 @@ +Testing +Тестовый текст раз два три \ No newline at end of file diff --git a/kate/tests/encoding/kateencodingtest.cpp b/kate/tests/encoding/kateencodingtest.cpp new file mode 100644 index 00000000..3d71c6c4 --- /dev/null +++ b/kate/tests/encoding/kateencodingtest.cpp @@ -0,0 +1,66 @@ +/* This file is part of the Kate project. + * + * Copyright (C) 2010 Christoph Cullmann + * Copyright (C) 2010 Dominik Haumann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "katetextbuffer.h" + +#include +#include + +int main (int argc, char *argv[]) +{ + if (argc < 4) { + qWarning() << "usage: "; + return 2; + } + + // construct core app + QCoreApplication app (argc, argv); + + // get arguments + QString encoding = app.arguments().at(1); + QString inFile = app.arguments().at(2); + QString outFile = app.arguments().at(3); + + Kate::TextBuffer buffer (0); + + // set codec + buffer.setFallbackTextCodec (QTextCodec::codecForName ("ISO-8859-15")); + buffer.setTextCodec (QTextCodec::codecForName (encoding.toLatin1())); + + // switch to Mac EOL, this will test eol detection, as files are normal unix or dos + buffer.setEndOfLineMode (Kate::TextBuffer::eolMac); + + // load file + bool encodingErrors = false; + bool tooLongLines = false; + if (!buffer.load (inFile, encodingErrors, tooLongLines, false) || encodingErrors) { + qWarning() << "encodingErrors" << encodingErrors << "tooLongLines" << tooLongLines; + return 1; + } + + // save file + if (!buffer.save (outFile)) { + qWarning() << "could not save file"; + return 2; + } + + return 0; +} diff --git a/kate/tests/encoding/koi8-r.txt b/kate/tests/encoding/koi8-r.txt new file mode 100644 index 00000000..eb61ad70 --- /dev/null +++ b/kate/tests/encoding/koi8-r.txt @@ -0,0 +1,2 @@ +Testing + \ No newline at end of file diff --git a/kate/tests/encoding/latin15.txt b/kate/tests/encoding/latin15.txt new file mode 100644 index 00000000..974d9ab0 --- /dev/null +++ b/kate/tests/encoding/latin15.txt @@ -0,0 +1,7 @@ +Test +Strae +Christoph +uerung +berall +Khler +$perlvar \ No newline at end of file diff --git a/kate/tests/encoding/one-char-latin-15.txt b/kate/tests/encoding/one-char-latin-15.txt new file mode 100644 index 00000000..92a39f39 --- /dev/null +++ b/kate/tests/encoding/one-char-latin-15.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/kate/tests/encoding/utf16.txt b/kate/tests/encoding/utf16.txt new file mode 100644 index 00000000..9c4a0ecc Binary files /dev/null and b/kate/tests/encoding/utf16.txt differ diff --git a/kate/tests/encoding/utf16be.txt b/kate/tests/encoding/utf16be.txt new file mode 100644 index 00000000..ec14d6ad Binary files /dev/null and b/kate/tests/encoding/utf16be.txt differ diff --git a/kate/tests/encoding/utf32.txt b/kate/tests/encoding/utf32.txt new file mode 100644 index 00000000..b234c067 Binary files /dev/null and b/kate/tests/encoding/utf32.txt differ diff --git a/kate/tests/encoding/utf32be.txt b/kate/tests/encoding/utf32be.txt new file mode 100644 index 00000000..31e6e01e Binary files /dev/null and b/kate/tests/encoding/utf32be.txt differ diff --git a/kate/tests/encoding/utf8.txt b/kate/tests/encoding/utf8.txt new file mode 100644 index 00000000..73c6f28d --- /dev/null +++ b/kate/tests/encoding/utf8.txt @@ -0,0 +1,7 @@ +Test +Straße +Christoph +Äußerung +Überall +Köhler +$perlvar \ No newline at end of file diff --git a/kate/tests/katedocument_test.cpp b/kate/tests/katedocument_test.cpp new file mode 100644 index 00000000..2ec41704 --- /dev/null +++ b/kate/tests/katedocument_test.cpp @@ -0,0 +1,362 @@ +/* This file is part of the KDE libraries + Copyright (C) 2010 Dominik Haumann + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "katedocument_test.h" +#include "moc_katedocument_test.cpp" + +#include + +#include +#include +#include +#include + +///TODO: is there a FindValgrind cmake command we could use to +/// define this automatically? +// comment this out and run the test case with: +// valgrind --tool=callgrind --instr-atstart=no ./katedocument_test testSetTextPerformance +// or similar +// +// #define USE_VALGRIND + +#ifdef USE_VALGRIND + #include +#endif + +using namespace KTextEditor; + +QTEST_KDEMAIN(KateDocumentTest, GUI) + +QT_BEGIN_NAMESPACE +namespace QTest { + template<> + char *toString(const KTextEditor::Cursor &cursor) + { + QByteArray ba = "Cursor[" + QByteArray::number(cursor.line()) + + ", " + QByteArray::number(cursor.column()) + "]"; + return qstrdup(ba.data()); + } +} +QT_END_NAMESPACE + +class MovingRangeInvalidator : public QObject { + Q_OBJECT +public: + explicit MovingRangeInvalidator( QObject* parent = 0 ) + : QObject(parent) + { + } + + void addRange(MovingRange* range) + { + m_ranges << range; + } + QList ranges() const + { + return m_ranges; + } + +public slots: + void aboutToInvalidateMovingInterfaceContent() + { + qDeleteAll(m_ranges); + m_ranges.clear(); + } + +private: + QList m_ranges; +}; + + +KateDocumentTest::KateDocumentTest() + : QObject() +{ +} + +KateDocumentTest::~KateDocumentTest() +{ +} + +// tests: +// KateDocument::insertText with word wrap enabled. It is checked whether the +// text is correctly wrapped and whether the moving cursors maintain the correct +// position. +// see also: http://bugs.kde.org/show_bug.cgi?id=168534 +void KateDocumentTest::testWordWrap() +{ + KateDocument doc (false, false, false); + doc.setWordWrap(true); + doc.setWordWrapAt(80); + + const QString content = ".........1.........2.........3.........4.........5.........6 ........7 ........8"; + + // space after 7 is now kept + // else we kill indentation... + const QString firstWrap = ".........1.........2.........3.........4.........5.........6 ........7 \n....x....8"; + + // space after 6 is now kept + // else we kill indentation... + const QString secondWrap = ".........1.........2.........3.........4.........5.........6 \n....ooooooooooo....7 ....x....8"; + + doc.setText(content); + MovingCursor* c = doc.newMovingCursor(Cursor(0, 75), MovingCursor::MoveOnInsert); + + QCOMPARE(doc.text(), content); + QCOMPARE(c->toCursor(), Cursor(0, 75)); + + // type a character at (0, 75) + doc.insertText (c->toCursor(), "x"); + QCOMPARE(doc.text(), firstWrap); + QCOMPARE(c->toCursor(), Cursor(1, 5)); + + // set cursor to (0, 65) and type "ooooooooooo" + c->setPosition(Cursor(0, 65)); + doc.insertText (c->toCursor(), "ooooooooooo"); + QCOMPARE(doc.text(), secondWrap); + QCOMPARE(c->toCursor(), Cursor(1, 15)); +} + +void KateDocumentTest::testReplaceQStringList() +{ + KateDocument doc(false, false, false); + doc.setWordWrap(false); + doc.setText("asdf\n" + "foo\n" + "foo\n" + "bar\n"); + doc.replaceText( Range(1, 0, 3, 0), QStringList() << "new" << "text" << "", false ); + QCOMPARE(doc.text(), QString("asdf\n" + "new\n" + "text\n" + "bar\n")); +} + +void KateDocumentTest::testMovingInterfaceSignals() +{ + KateDocument* doc = new KateDocument(false, false, false); + QSignalSpy aboutToDeleteSpy(doc, SIGNAL(aboutToDeleteMovingInterfaceContent(KTextEditor::Document*))); + QSignalSpy aboutToInvalidateSpy(doc, SIGNAL(aboutToInvalidateMovingInterfaceContent(KTextEditor::Document*))); + + QCOMPARE(doc->revision(), qint64(0)); + + QCOMPARE(aboutToInvalidateSpy.count(), 0); + QCOMPARE(aboutToDeleteSpy.count(), 0); + + KTemporaryFile f; + f.open(); + doc->openUrl(KUrl::fromLocalFile(f.fileName())); + QCOMPARE(doc->revision(), qint64(0)); + //TODO: gets emitted once in closeFile and once in openFile - is that OK? + QCOMPARE(aboutToInvalidateSpy.count(), 2); + QCOMPARE(aboutToDeleteSpy.count(), 0); + + doc->documentReload(); + QCOMPARE(doc->revision(), qint64(0)); + QCOMPARE(aboutToInvalidateSpy.count(), 4); + //TODO: gets emitted once in closeFile and once in openFile - is that OK? + QCOMPARE(aboutToDeleteSpy.count(), 0); + + delete doc; + QCOMPARE(aboutToInvalidateSpy.count(), 4); + QCOMPARE(aboutToDeleteSpy.count(), 1); +} + +void KateDocumentTest::testSetTextPerformance() +{ + const int lines = 150; + const int columns = 80; + const int rangeLength = 4; + const int rangeGap = 1; + + Q_ASSERT(columns % (rangeLength + rangeGap) == 0); + + KateDocument doc(false, false, false); + MovingRangeInvalidator invalidator; + connect(&doc, SIGNAL(aboutToInvalidateMovingInterfaceContent(KTextEditor::Document*)), + &invalidator, SLOT(aboutToInvalidateMovingInterfaceContent())); + + QString text; + QVector ranges; + ranges.reserve(lines * columns / (rangeLength + rangeGap)); + const QString line = QString().fill('a', columns); + for(int l = 0; l < lines; ++l) { + text.append(line); + text.append('\n'); + for(int c = 0; c < columns; c += rangeLength + rangeGap) { + ranges << Range(l, c, l, c + rangeLength); + } + } + + // replace + QBENCHMARK { + // init + doc.setText(text); + foreach(const Range& range, ranges) { + invalidator.addRange(doc.newMovingRange(range)); + } + QCOMPARE(invalidator.ranges().size(), ranges.size()); + + #ifdef USE_VALGRIND + CALLGRIND_START_INSTRUMENTATION + #endif + + doc.setText(text); + + #ifdef USE_VALGRIND + CALLGRIND_STOP_INSTRUMENTATION + #endif + + QCOMPARE(doc.text(), text); + QVERIFY(invalidator.ranges().isEmpty()); + } +} + +void KateDocumentTest::testRemoveTextPerformance() +{ + const int lines = 5000; + const int columns = 80; + + KateDocument doc(false, false, false); + + QString text; + const QString line = QString().fill('a', columns); + for(int l = 0; l < lines; ++l) { + text.append(line); + text.append('\n'); + } + + doc.setText(text); + + // replace + QBENCHMARK_ONCE { + #ifdef USE_VALGRIND + CALLGRIND_START_INSTRUMENTATION + #endif + + doc.editStart(); + + doc.removeText(doc.documentRange()); + + doc.editEnd(); + + #ifdef USE_VALGRIND + CALLGRIND_STOP_INSTRUMENTATION + #endif + } +} + +void KateDocumentTest::testForgivingApiUsage() +{ + KateDocument doc(false, false, false); + + QVERIFY(doc.isEmpty()); + QVERIFY(doc.replaceText(Range(0, 0, 100, 100), "asdf")); + QCOMPARE(doc.text(), QString("asdf")); + QCOMPARE(doc.lines(), 1); + QVERIFY(doc.replaceText(Range(2, 99, 2, 100), "asdf")); + QEXPECT_FAIL("", "replacing text behind the document end will add 5 lines out of nowhere", Continue); + QCOMPARE(doc.lines(), 3); + + QVERIFY(doc.removeText(Range(0, 0, 1000, 1000))); + QVERIFY(doc.removeText(Range(0, 0, 0, 100))); + QVERIFY(doc.isEmpty()); + doc.insertText(Cursor(100, 0), "foobar"); + QCOMPARE(doc.line(100), QString("foobar")); + + doc.setText("nY\nnYY\n"); + QVERIFY(doc.removeText(Range(0, 0, 0, 1000))); +} + +/** + * Provides slots to check data sent in specific signals. Slot names are derived from corresponding test names. + */ +class SignalHandler : public QObject +{ + Q_OBJECT +public slots: + void slotMultipleLinesRemoved(KTextEditor::Document*, const KTextEditor::Range&, const QString& oldText) + { + QCOMPARE(oldText, QString("line2\nline3\n")); + } + + void slotNewlineInserted(KTextEditor::Document*, const KTextEditor::Range& range) + { + QCOMPARE(range, Range(Cursor(1, 4), Cursor(2, 0))); + } +}; + +void KateDocumentTest::testRemoveMultipleLines() +{ + KateDocument doc(false, false, false); + + doc.setText("line1\n" + "line2\n" + "line3\n" + "line4\n"); + + SignalHandler handler; + connect(&doc, SIGNAL(textRemoved(KTextEditor::Document*,KTextEditor::Range,QString)), &handler, SLOT(slotMultipleLinesRemoved(KTextEditor::Document*,KTextEditor::Range,QString))); + doc.removeText(Range(1, 0, 3, 0)); +} + +void KateDocumentTest::testInsertNewline() +{ + KateDocument doc(false, false, false); + + doc.setText("this is line\n" + "this is line2\n"); + + SignalHandler handler; + connect(&doc, SIGNAL(textInserted(KTextEditor::Document*,KTextEditor::Range)), &handler, SLOT(slotNewlineInserted(KTextEditor::Document*,KTextEditor::Range))); + doc.editWrapLine(1, 4); +} + +// we have two different ways of creating the Sha1 checksum: +// in KateFileLoader and KateDocument::createDigest. Make +// sure, these two implementations result in the same checksum. +void KateDocumentTest::testDigest() +{ + // QCryptographicHash is used, therefore we need fromHex here +#if QT_VERSION >= 0x041200 + // Katie checksum of data/sha1checksum.txt: ba8bd2ee351783dffd6bb7b750271549e1b0c356e28aced4b0480d9f262d869d + const QByteArray fileDigest = QByteArray::fromHex("ba8bd2ee351783dffd6bb7b750271549e1b0c356e28aced4b0480d9f262d869d"); +#else + // sha1sum of data/sha1checksum.txt: 58527e1f4c8574d61450ff18cffaa8cd41c3554d + const QByteArray fileDigest = QByteArray::fromHex("58527e1f4c8574d61450ff18cffaa8cd41c3554d"); +#endif + + // make sure, Kate::TextBuffer and KateDocument::createDigest() equal + KateDocument doc(false, false, false); + doc.openUrl(QString(KDESRCDIR + QString("data/sha1checksum.txt"))); + const QByteArray bufferDigest(doc.digest()); + QVERIFY(doc.createDigest()); + const QByteArray docDigest(doc.digest()); + + QCOMPARE(bufferDigest, fileDigest); + QCOMPARE(docDigest, fileDigest); +} + +void KateDocumentTest::testDefStyleNum() +{ + KateDocument doc; + doc.setText("foo\nbar\nasdf"); + QCOMPARE(doc.defStyleNum(0, 0), -1); +} + +#include "katedocument_test.moc" diff --git a/kate/tests/katedocument_test.h b/kate/tests/katedocument_test.h new file mode 100644 index 00000000..a8699c4f --- /dev/null +++ b/kate/tests/katedocument_test.h @@ -0,0 +1,51 @@ +/* This file is part of the KDE libraries + Copyright (C) 2010 Dominik Haumann + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KATE_DOCUMENT_TEST_H +#define KATE_DOCUMENT_TEST_H + +#include + +class KateDocumentTest : public QObject +{ + Q_OBJECT + +public: + KateDocumentTest(); + ~KateDocumentTest(); + +private Q_SLOTS: + void testWordWrap(); + void testReplaceQStringList(); + void testMovingInterfaceSignals(); + + void testSetTextPerformance(); + void testRemoveTextPerformance(); + + void testForgivingApiUsage(); + + void testRemoveMultipleLines(); + void testInsertNewline(); + + void testDigest(); + + void testDefStyleNum(); +}; + +#endif // KATE_DOCUMENT_TEST_H diff --git a/kate/tests/katefoldingtest.cpp b/kate/tests/katefoldingtest.cpp new file mode 100644 index 00000000..c0f02c5c --- /dev/null +++ b/kate/tests/katefoldingtest.cpp @@ -0,0 +1,125 @@ +/* This file is part of the Kate project. + * + * Copyright (C) 2013 Dominik Haumann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "katefoldingtest.h" + +#include + +#include +#include +#include +#include +#include +#include + +using namespace KTextEditor; + +QTEST_KDEMAIN(KateFoldingTest, GUI) + +QT_BEGIN_NAMESPACE +namespace QTest { + template<> + char *toString(const KTextEditor::Cursor &cursor) + { + QByteArray ba = "Cursor[" + QByteArray::number(cursor.line()) + + ", " + QByteArray::number(cursor.column()) + "]"; + return qstrdup(ba.data()); + } +} +QT_END_NAMESPACE + +void KateFoldingTest::initTestCase() +{ + KateGlobal::self()->incRef(); +} + +void KateFoldingTest::cleanupTestCase() +{ + KateGlobal::self()->decRef(); +} + +// This is a unit test for bug 311866 (http://bugs.kde.org/show_bug.cgi?id=311866) +// It loads 5 lines of C++ code, places the cursor in line 4, and then folds +// the code. +// Expected behavior: the cursor should be moved so it stays visible +// Buggy behavior: the cursor is hidden, and moving the hidden cursor crashes kate +void KateFoldingTest::testCrash311866() +{ + KateDocument doc(false, false, false); + QString url = KDESRCDIR + QString("data/bug311866.cpp"); + doc.openUrl(url); + doc.setHighlightingMode("C++"); + doc.buffer().ensureHighlighted (6); + + KateView* view = static_cast(doc.createView(0)); + view->show(); + view->resize(400, 300); + view->setCursorPosition(Cursor(3, 0)); + QTest::qWait(100); + + view->slotFoldToplevelNodes(); + doc.buffer().ensureHighlighted (6); + + qDebug() << "!!! Does the next line crash?"; + view->up(); +} + +// This test makes sure that, +// - if you have selected text +// - that spans a folded range, +// - and the cursor is at the end of the text selection, +// - and you type a char, e.g. 'x', +// then the resulting text is correct, and changing region +// visibility does not mess around with the text cursor. +// +// See https://bugs.kde.org/show_bug.cgi?id=295632 +void KateFoldingTest::testBug295632() +{ + KateDocument doc(false, false, false); + QString text = "oooossssssss\n" + "{\n" + "\n" + "}\n" + "ssssss----------"; + doc.setText(text); + + // view must be visible... + KateView* view = static_cast(doc.createView(0)); + view->show(); + view->resize(400, 300); + + qint64 foldId = view->textFolding().newFoldingRange (KTextEditor::Range(1, 0, 3, 1)); + view->textFolding().foldRange(foldId); + QVERIFY(view->textFolding().isLineVisible(0)); + QVERIFY(view->textFolding().isLineVisible(1)); + QVERIFY(!view->textFolding().isLineVisible(2)); + QVERIFY(!view->textFolding().isLineVisible(3)); + QVERIFY(view->textFolding().isLineVisible(4)); + + view->setSelection(Range(Cursor(0,4), Cursor(4, 6))); + view->setCursorPosition(Cursor(4, 6)); + + QTest::qWait(100); + doc.typeChars(view, "x"); + QTest::qWait(100); + + QString line = doc.line(0); + QCOMPARE(line, QString("oooox----------")); +} diff --git a/kate/tests/katefoldingtest.h b/kate/tests/katefoldingtest.h new file mode 100644 index 00000000..80b14464 --- /dev/null +++ b/kate/tests/katefoldingtest.h @@ -0,0 +1,38 @@ +/* This file is part of the KDE libraries + Copyright (C) 2013 Dominik Haumann + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KATE_FOLDING_TEST_H +#define KATE_FOLDING_TEST_H + +#include + +class KateFoldingTest : public QObject +{ + Q_OBJECT + +public Q_SLOTS: + void initTestCase(); + void cleanupTestCase(); + +private Q_SLOTS: + void testCrash311866(); + void testBug295632(); +}; + +#endif // KATE_FOLDING_TEST_H diff --git a/kate/tests/katetextbuffertest.cpp b/kate/tests/katetextbuffertest.cpp new file mode 100644 index 00000000..4d006125 --- /dev/null +++ b/kate/tests/katetextbuffertest.cpp @@ -0,0 +1,451 @@ +/* This file is part of the Kate project. + * + * Copyright (C) 2010 Christoph Cullmann + * Copyright (C) 2010 Dominik Haumann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "katetextbuffertest.h" +#include "katetextbuffer.h" +#include "katetextcursor.h" +#include "katetextfolding.h" + +#include + +QTEST_MAIN(KateTextBufferTest) + +KateTextBufferTest::KateTextBufferTest() + : QObject() +{ +} + +KateTextBufferTest::~KateTextBufferTest() +{ +} + +void KateTextBufferTest::basicBufferTest() +{ + // construct an empty text buffer + Kate::TextBuffer buffer (0, 1); + + // one line per default + QVERIFY (buffer.lines() == 1); + QVERIFY (buffer.text () == ""); + + //FIXME: use QTestLib macros for checking the correct state + // start editing + buffer.startEditing (); + + // end editing + buffer.finishEditing (); +} + +void KateTextBufferTest::wrapLineTest() +{ + // construct an empty text buffer + Kate::TextBuffer buffer (0, 1); + + // wrap first empty line -> we should have two empty lines + buffer.startEditing (); + buffer.wrapLine(KTextEditor::Cursor(0, 0)); + buffer.finishEditing (); + buffer.debugPrint ("Two empty lines"); + QVERIFY (buffer.text () == "\n"); + + // unwrap again -> only one empty line + buffer.startEditing (); + buffer.unwrapLine(1); + buffer.finishEditing (); + + // print debug + buffer.debugPrint ("Empty Buffer"); + QVERIFY (buffer.text () == ""); +} + +void KateTextBufferTest::insertRemoveTextTest() +{ + // construct an empty text buffer + Kate::TextBuffer buffer (0, 1); + + // wrap first line + buffer.startEditing (); + buffer.wrapLine (KTextEditor::Cursor (0, 0)); + buffer.finishEditing (); + buffer.debugPrint ("Two empty lines"); + QVERIFY (buffer.text () == "\n"); + + // remember second line + Kate::TextLine second = buffer.line (1); + + // unwrap second line + buffer.startEditing (); + buffer.unwrapLine (1); + buffer.finishEditing (); + buffer.debugPrint ("One empty line"); + QVERIFY (buffer.text () == ""); + + // second text line should be still there + //const QString &secondText = second->text (); + //QVERIFY (secondText == "") + + // insert text + buffer.startEditing (); + buffer.insertText (KTextEditor::Cursor (0, 0), "testremovetext"); + buffer.finishEditing (); + buffer.debugPrint ("One line"); + QVERIFY (buffer.text () == "testremovetext"); + + // remove text + buffer.startEditing (); + buffer.removeText (KTextEditor::Range (KTextEditor::Cursor (0, 4), KTextEditor::Cursor (0, 10))); + buffer.finishEditing (); + buffer.debugPrint ("One line"); + QVERIFY (buffer.text () == "testtext"); + + // wrap text + buffer.startEditing (); + buffer.wrapLine (KTextEditor::Cursor (0, 2)); + buffer.finishEditing (); + buffer.debugPrint ("Two line"); + QVERIFY (buffer.text () == "te\nsttext"); + + // unwrap text + buffer.startEditing (); + buffer.unwrapLine (1); + buffer.finishEditing (); + buffer.debugPrint ("One line"); + QVERIFY (buffer.text () == "testtext"); +} + +void KateTextBufferTest::cursorTest() +{ + // last buffer content, for consistence checks + QString lastBufferContent; + + // test with different block sizes + for (int i = 1; i <= 4; ++i) { + // construct an empty text buffer + Kate::TextBuffer buffer (0, i); + + // wrap first line + buffer.startEditing (); + buffer.insertText (KTextEditor::Cursor (0, 0), "sfdfjdsklfjlsdfjlsdkfjskldfjklsdfjklsdjkfl"); + buffer.wrapLine (KTextEditor::Cursor (0, 8)); + buffer.wrapLine (KTextEditor::Cursor (1, 8)); + buffer.wrapLine (KTextEditor::Cursor (2, 8)); + buffer.finishEditing (); + buffer.debugPrint ("Cursor buffer"); + + // construct cursor + Kate::TextCursor *cursor1 = new Kate::TextCursor (buffer, KTextEditor::Cursor (0, 0), Kate::TextCursor::MoveOnInsert); + QVERIFY (cursor1->toCursor () == KTextEditor::Cursor (0, 0)); + printf ("cursor %d, %d\n", cursor1->line(), cursor1->column()); + + Kate::TextCursor *cursor2 = new Kate::TextCursor (buffer, KTextEditor::Cursor (1, 8), Kate::TextCursor::MoveOnInsert); + printf ("cursor %d, %d\n", cursor2->line(), cursor2->column()); + + Kate::TextCursor *cursor3 = new Kate::TextCursor (buffer, KTextEditor::Cursor (0, 123), Kate::TextCursor::MoveOnInsert); + printf ("cursor %d, %d\n", cursor3->line(), cursor3->column()); + + Kate::TextCursor *cursor4 = new Kate::TextCursor (buffer, KTextEditor::Cursor (1323, 1), Kate::TextCursor::MoveOnInsert); + printf ("cursor %d, %d\n", cursor4->line(), cursor4->column()); + + // insert text + buffer.startEditing (); + buffer.insertText (KTextEditor::Cursor (0, 0), "hallo"); + buffer.finishEditing (); + buffer.debugPrint ("Cursor buffer"); + + printf ("cursor %d, %d\n", cursor1->line(), cursor1->column()); + QVERIFY (cursor1->toCursor () == KTextEditor::Cursor (0, 5)); + + // remove text + buffer.startEditing (); + buffer.removeText (KTextEditor::Range (KTextEditor::Cursor (0, 4), KTextEditor::Cursor (0, 10))); + buffer.finishEditing (); + buffer.debugPrint ("Cursor buffer"); + + printf ("cursor %d, %d\n", cursor1->line(), cursor1->column()); + QVERIFY (cursor1->toCursor () == KTextEditor::Cursor (0, 4)); + + // wrap line + buffer.startEditing (); + buffer.wrapLine (KTextEditor::Cursor (0, 3)); + buffer.finishEditing (); + buffer.debugPrint ("Cursor buffer"); + + printf ("cursor %d, %d\n", cursor1->line(), cursor1->column()); + QVERIFY (cursor1->toCursor () == KTextEditor::Cursor (1, 1)); + + // unwrap line + buffer.startEditing (); + buffer.unwrapLine (1); + buffer.finishEditing (); + buffer.debugPrint ("Cursor buffer"); + + printf ("cursor %d, %d\n", cursor1->line(), cursor1->column()); + QVERIFY (cursor1->toCursor () == KTextEditor::Cursor (0, 4)); + + // verify content + if (i > 1) + QVERIFY (lastBufferContent == buffer.text ()); + + // remember content + lastBufferContent = buffer.text (); + } +} + +void KateTextBufferTest::foldingTest() +{ + // construct an empty text buffer & folding info + Kate::TextBuffer buffer (0, 1); + Kate::TextFolding folding (buffer); + + // insert some text + buffer.startEditing (); + for (int i = 0; i < 100; ++i) { + buffer.insertText (KTextEditor::Cursor (i, 0), "1234567890"); + if (i < 99) + buffer.wrapLine (KTextEditor::Cursor (i, 10)); + } + buffer.finishEditing (); + QVERIFY (buffer.lines() == 100); + + // starting with empty folding! + folding.debugPrint ("Empty Folding"); + QVERIFY (folding.debugDump() == "tree - folded "); + + // check visibility + QVERIFY (folding.isLineVisible (0)); + QVERIFY (folding.isLineVisible (99)); + + // all visible + QVERIFY (folding.visibleLines() == 100); + + // we shall be able to insert new range + QVERIFY (folding.newFoldingRange (KTextEditor::Range (KTextEditor::Cursor (5,0), KTextEditor::Cursor (10,0))) == 0); + + // we shall have now exactly one range toplevel, that is not folded! + folding.debugPrint ("One Toplevel Fold"); + QVERIFY (folding.debugDump() == "tree [5:0 10:0] - folded "); + + // fold the range! + QVERIFY(folding.foldRange (0)); + + folding.debugPrint ("One Toplevel Fold - Folded"); + QVERIFY (folding.debugDump() == "tree [5:0 f 10:0] - folded [5:0 f 10:0]"); + + // check visibility + QVERIFY (folding.isLineVisible (5)); + for (int i = 6; i <= 10; ++i) + QVERIFY (!folding.isLineVisible (i)); + QVERIFY (folding.isLineVisible (11)); + + // 5 lines are hidden + QVERIFY (folding.visibleLines() == (100 - 5)); + + // check line mapping + QVERIFY (folding.visibleLineToLine (5) == 5); + for (int i = 6; i <= 50; ++i) + QVERIFY (folding.visibleLineToLine (i) == (i + 5)); + + // there shall be one range starting at 5 + QVector > forLine = folding.foldingRangesStartingOnLine (5); + QVERIFY (forLine.size() == 1); + QVERIFY (forLine[0].first == 0); + QVERIFY (forLine[0].second & Kate::TextFolding::Folded); + + // we shall be able to insert new range + QVERIFY (folding.newFoldingRange (KTextEditor::Range (KTextEditor::Cursor (20,0), KTextEditor::Cursor (30,0)), Kate::TextFolding::Folded) == 1); + + // we shall have now exactly two range toplevel + folding.debugPrint ("Two Toplevel Folds"); + QVERIFY (folding.debugDump() == "tree [5:0 f 10:0] [20:0 f 30:0] - folded [5:0 f 10:0] [20:0 f 30:0]"); + + // check visibility + QVERIFY (folding.isLineVisible (5)); + for (int i = 6; i <= 10; ++i) + QVERIFY (!folding.isLineVisible (i)); + QVERIFY (folding.isLineVisible (11)); + + QVERIFY (folding.isLineVisible (20)); + for (int i = 21; i <= 30; ++i) + QVERIFY (!folding.isLineVisible (i)); + QVERIFY (folding.isLineVisible (31)); + + // 15 lines are hidden + QVERIFY (folding.visibleLines() == (100 - 5 - 10)); + + // check line mapping + QVERIFY (folding.visibleLineToLine (5) == 5); + for (int i = 6; i <= 15; ++i) + QVERIFY (folding.visibleLineToLine (i) == (i + 5)); + for (int i = 16; i <= 50; ++i) + QVERIFY (folding.visibleLineToLine (i) == (i + 15)); + + // check line mapping + QVERIFY (folding.lineToVisibleLine (5) == 5); + for (int i = 11; i <= 20; ++i) + QVERIFY (folding.lineToVisibleLine (i) == (i - 5)); + for (int i = 31; i <= 40; ++i) + QVERIFY (folding.lineToVisibleLine (i) == (i - 15)); + + // there shall be one range starting at 20 + forLine = folding.foldingRangesStartingOnLine (20); + QVERIFY (forLine.size() == 1); + QVERIFY (forLine[0].first == 1); + QVERIFY (forLine[0].second & Kate::TextFolding::Folded); + + // this shall fail to be inserted, as it badly overlaps with the first range! + QVERIFY (folding.newFoldingRange (KTextEditor::Range (KTextEditor::Cursor (6,0), KTextEditor::Cursor (15,0)), Kate::TextFolding::Folded) == -1); + + // this shall fail to be inserted, as it badly overlaps with the second range! + QVERIFY (folding.newFoldingRange (KTextEditor::Range (KTextEditor::Cursor (15,0), KTextEditor::Cursor (25,0)), Kate::TextFolding::Folded) == -1); + + // we shall still have now exactly two range toplevel + folding.debugPrint ("Still Two Toplevel Folds"); + QVERIFY (folding.debugDump() == "tree [5:0 f 10:0] [20:0 f 30:0] - folded [5:0 f 10:0] [20:0 f 30:0]"); + + // still 15 lines are hidden + QVERIFY (folding.visibleLines() == (100 - 5 - 10)); + + // we shall be able to insert new range, should lead to nested folds! + QVERIFY (folding.newFoldingRange (KTextEditor::Range (KTextEditor::Cursor (15,0), KTextEditor::Cursor (35,0)), Kate::TextFolding::Folded) == 2); + + // we shall have now exactly two range toplevel and one embedded fold + folding.debugPrint ("Two Toplevel Folds, One Nested Fold"); + QVERIFY (folding.debugDump() == "tree [5:0 f 10:0] [15:0 f [20:0 f 30:0] 35:0] - folded [5:0 f 10:0] [15:0 f 35:0]"); + + // 25 lines are hidden + QVERIFY (folding.visibleLines() == (100 - 5 - 20)); + + // check line mapping + QVERIFY (folding.lineToVisibleLine (5) == 5); + for (int i = 11; i <= 15; ++i) + QVERIFY (folding.lineToVisibleLine (i) == (i - 5)); + + // special case: hidden lines, should fall ack to last visible one! + for (int i = 16; i <= 35; ++i) + QVERIFY (folding.lineToVisibleLine (i) == 10); + + for (int i = 36; i <= 40; ++i) + QVERIFY (folding.lineToVisibleLine (i) == (i - 25)); + + // we shall be able to insert new range, should lead to nested folds! + QVERIFY (folding.newFoldingRange (KTextEditor::Range (KTextEditor::Cursor (0,0), KTextEditor::Cursor (50,0)), Kate::TextFolding::Folded) == 3); + + // we shall have now exactly one range toplevel and many embedded fold + folding.debugPrint ("One Toplevel + Embedded Folds"); + QVERIFY (folding.debugDump() == "tree [0:0 f [5:0 f 10:0] [15:0 f [20:0 f 30:0] 35:0] 50:0] - folded [0:0 f 50:0]"); + + // there shall still be one range starting at 20 + forLine = folding.foldingRangesStartingOnLine (20); + QVERIFY (forLine.size() == 1); + QVERIFY (forLine[0].first == 1); + QVERIFY (forLine[0].second & Kate::TextFolding::Folded); + + // add more regions starting at 20 + QVERIFY (folding.newFoldingRange (KTextEditor::Range (KTextEditor::Cursor (20,5), KTextEditor::Cursor (24,0)), Kate::TextFolding::Folded) == 4); + QVERIFY (folding.newFoldingRange (KTextEditor::Range (KTextEditor::Cursor (20,3), KTextEditor::Cursor (25,0)), Kate::TextFolding::Folded) == 5); + folding.debugPrint ("More ranges at 20"); + + // there shall still be three ranges starting at 20 + forLine = folding.foldingRangesStartingOnLine (20); + QVERIFY (forLine.size() == 3); + QVERIFY (forLine[0].first == 1); + QVERIFY (forLine[0].second & Kate::TextFolding::Folded); + QVERIFY (forLine[1].first == 5); + QVERIFY (forLine[1].second & Kate::TextFolding::Folded); + QVERIFY (forLine[2].first == 4); + QVERIFY (forLine[2].second & Kate::TextFolding::Folded); + + // 50 lines are hidden + QVERIFY (folding.visibleLines() == (100 - 50)); + + // save state + QVariantList folds = folding.exportFoldingRanges (); + QString textDump = folding.debugDump(); + + // clear folds + folding.clear (); + QVERIFY (folding.debugDump() == "tree - folded "); + + // restore state + folding.importFoldingRanges (folds); + QVERIFY (folding.debugDump() == textDump); + +} + +void KateTextBufferTest::nestedFoldingTest() +{ + // construct an empty text buffer & folding info + Kate::TextBuffer buffer (0, 1); + Kate::TextFolding folding (buffer); + + // insert two nested folds in 5 lines + buffer.startEditing (); + for (int i = 0; i < 4; ++i) + buffer.wrapLine(KTextEditor::Cursor(0, 0)); + buffer.finishEditing (); + + QVERIFY (buffer.lines() == 5); + + // folding for line 1 + QVERIFY (folding.newFoldingRange (KTextEditor::Range (KTextEditor::Cursor (0,0), KTextEditor::Cursor (3,0)), Kate::TextFolding::Folded) == 0); + QVERIFY (folding.newFoldingRange (KTextEditor::Range (KTextEditor::Cursor (1,0), KTextEditor::Cursor (2,0)), Kate::TextFolding::Folded) == 1); + + QVERIFY(folding.foldRange (1)); + QVERIFY(folding.foldRange (0)); + + QVERIFY(folding.unfoldRange (0)); + QVERIFY(folding.unfoldRange (1)); +} + +void KateTextBufferTest::saveFileInUnwritableFolder() +{ + const QString folder_name = QString("katetest_%1").arg(QCoreApplication::applicationPid()); + const QString file_path = QDir::tempPath() + '/' + folder_name + "/foo"; + QVERIFY(QDir::temp().mkdir(folder_name)); + + QFile f(file_path); + QVERIFY(f.open(QIODevice::WriteOnly | QIODevice::Truncate)); + f.write("1234567890"); + QVERIFY(f.flush()); + f.close(); + + QFile::setPermissions(QDir::tempPath() + '/' + folder_name, QFile::ExeOwner); + + Kate::TextBuffer buffer(0, 1); + buffer.setTextCodec(QTextCodec::codecForName("UTF-8")); + buffer.setFallbackTextCodec(QTextCodec::codecForName("UTF-8")); + bool a, b; + buffer.load(file_path, a, b, true); + buffer.clear(); + buffer.startEditing(); + buffer.insertText (KTextEditor::Cursor (0, 0), "ABC"); + buffer.finishEditing(); + qDebug() << buffer.text(); + buffer.save(file_path); + + f.open(QIODevice::ReadOnly); + QCOMPARE(f.readAll(), QByteArray("ABC")); + f.close(); + + QFile::setPermissions(QDir::tempPath() + '/' + folder_name, QFile::WriteOwner | QFile::ExeOwner); + Q_ASSERT(f.remove()); + Q_ASSERT(QDir::temp().rmdir(folder_name)); +} diff --git a/kate/tests/katetextbuffertest.h b/kate/tests/katetextbuffertest.h new file mode 100644 index 00000000..391c5195 --- /dev/null +++ b/kate/tests/katetextbuffertest.h @@ -0,0 +1,45 @@ +/* This file is part of the Kate project. + * + * Copyright (C) 2010 Christoph Cullmann + * Copyright (C) 2010 Dominik Haumann + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef KATEBUFFERTEST_H +#define KATEBUFFERTEST_H + +#include +#include + +class KateTextBufferTest : public QObject +{ + Q_OBJECT + + public: + KateTextBufferTest(); + virtual ~KateTextBufferTest(); + + private Q_SLOTS: + void basicBufferTest(); + void wrapLineTest(); + void insertRemoveTextTest(); + void cursorTest(); + void foldingTest(); + void nestedFoldingTest(); + void saveFileInUnwritableFolder(); +}; + +#endif // KATEBUFFERTEST_H diff --git a/kate/tests/kateview_test.cpp b/kate/tests/kateview_test.cpp new file mode 100644 index 00000000..ffec0af0 --- /dev/null +++ b/kate/tests/kateview_test.cpp @@ -0,0 +1,366 @@ +/* This file is part of the KDE libraries + Copyright (C) 2010 Milian Wolff + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "kateview_test.h" +#include "moc_kateview_test.cpp" + +#include + +#include +#include +#include +#include +#include +#include + +using namespace KTextEditor; + +QTEST_KDEMAIN(KateViewTest, GUI) + +QT_BEGIN_NAMESPACE +namespace QTest { + template<> + char *toString(const KTextEditor::Cursor &cursor) + { + QByteArray ba = "Cursor[" + QByteArray::number(cursor.line()) + + ", " + QByteArray::number(cursor.column()) + "]"; + return qstrdup(ba.data()); + } +} +QT_END_NAMESPACE + +KateViewTest::KateViewTest() + : QObject() +{ +} + +KateViewTest::~KateViewTest() +{ +} + +void KateViewTest::testCoordinatesToCursor() +{ + KateDocument doc(false, false, false); + doc.setText("Hi World!\nHi\n"); + + KateView* view1 = static_cast(doc.createView(0)); + view1->show(); + + QCOMPARE(view1->coordinatesToCursor(view1->cursorToCoordinate(KTextEditor::Cursor(0, 2))), + KTextEditor::Cursor(0, 2)); + QCOMPARE(view1->coordinatesToCursor(view1->cursorToCoordinate(KTextEditor::Cursor(1, 1))), + KTextEditor::Cursor(1, 1)); + // behind end of line should give an invalid cursor + QCOMPARE(view1->coordinatesToCursor(view1->cursorToCoordinate(KTextEditor::Cursor(1, 5))), + KTextEditor::Cursor::invalid()); + QCOMPARE(view1->cursorToCoordinate(KTextEditor::Cursor(3, 1)), QPoint(-1, -1)); +} + +void KateViewTest::testCursorToCoordinates() +{ + KateDocument doc(false, false, false); + doc.setText("int a;"); + + KateView* view = static_cast(doc.createView(0)); + // this is important + view->config()->setDynWordWrap(true); + view->show(); + + // don't crash, see https://bugs.kde.org/show_bug.cgi?id=337863 + view->cursorToCoordinate(Cursor(0, 0)); + view->cursorToCoordinate(Cursor(1, 0)); + view->cursorToCoordinate(Cursor(-1, 0)); +} + +void KateViewTest::testReloadMultipleViews() +{ + KTemporaryFile file; + file.setSuffix(".cpp"); + file.open(); + QTextStream stream(&file); + const QString line = "const char* foo = \"asdf\"\n"; + for ( int i = 0; i < 200; ++i ) { + stream << line; + } + stream << flush; + file.close(); + + KateDocument doc(false, false, false); + QVERIFY(doc.openUrl(KUrl(file.fileName()))); + QCOMPARE(doc.highlightingMode(), QString("C++")); + + KateView* view1 = new KateView(&doc, 0); + KateView* view2 = new KateView(&doc, 0); + view1->show(); + view2->show(); + QCOMPARE(doc.views().count(), 2); + + QVERIFY(doc.documentReload()); +} + +void KateViewTest::testLowerCaseBlockSelection() +{ + // testcase for https://bugs.kde.org/show_bug.cgi?id=258480 + KateDocument doc(false, false, false); + doc.setText("nY\nnYY\n"); + + KateView* view1 = new KateView(&doc, 0); + view1->setBlockSelection(true); + view1->setSelection(Range(0, 1, 1, 3)); + view1->lowercase(); + + QCOMPARE(doc.text(), QString("ny\nnyy\n")); +} + +void KateViewTest::testFolding_data() +{ + QTest::addColumn("dynWordWrap"); + QTest::addColumn("scrollPastEnd"); + QTest::addColumn("autoCenterLines"); + + QTest::newRow("dynWordWrap") << true << false << 0; + QTest::newRow("dynWordWrap+scrollPastEnd") << true << true << 0; + QTest::newRow("dynWordWrap+autoCenterLines") << true << false << 10; + QTest::newRow("dynWordWrap+scrollPastEnd+autoCenterLines") << true << true << 10; + QTest::newRow("scrollPastEnd") << false << true << 0; + QTest::newRow("scrollPastEnd+autoCenterLines") << false << true << 10; +} + +void KateViewTest::testFolding() +{ +#if 0 + KTemporaryFile file; + file.setSuffix(".cpp"); + file.open(); + QTextStream stream(&file); + stream << "int main() {\n" + << " asdf;\n" + << "}\n"; + stream << flush; + file.close(); + + KateDocument doc(false, false, false); + QVERIFY(doc.openUrl(KUrl(file.fileName()))); + QCOMPARE(doc.highlightingMode(), QString("C++")); + + KateView* view = new KateView(&doc, 0); + QAction* collapseAction = view->action("folding_toplevel"); + QVERIFY(collapseAction); + QAction* expandAction = view->action("folding_expandtoplevel"); + QVERIFY(expandAction); + + QFETCH(bool, dynWordWrap); + QFETCH(bool, scrollPastEnd); + QFETCH(int, autoCenterLines); + + view->config()->setDynWordWrap(dynWordWrap); + view->config()->setScrollPastEnd(scrollPastEnd); + view->config()->setAutoCenterLines(autoCenterLines); + + for(int i = 0; i < doc.lines(); ++i) { + doc.buffer().ensureHighlighted(i); + } + QCOMPARE(doc.visibleLines(), 4u); + + collapseAction->trigger(); + QCOMPARE(doc.visibleLines(), 2u); + + expandAction->trigger(); + QCOMPARE(doc.visibleLines(), 4u); +#endif +} + +void KateViewTest::testBug287291() +{ +#if 0 + // see also: https://bugs.kde.org/show_bug.cgi?id=287291 + KTemporaryFile file; + file.setSuffix(".cpp"); + file.open(); + QTextStream stream(&file); + stream << "int main() {\n" + << " asdf;\n" + << "}\n"; + stream << flush; + file.close(); + + KateDocument doc(false, false, false); + QVERIFY(doc.openUrl(KUrl(file.fileName()))); + QCOMPARE(doc.highlightingMode(), QString("C++")); + + KateView* view = new KateView(&doc, 0); + QAction* collapseAction = view->action("folding_toplevel"); + QVERIFY(collapseAction); + QAction* expandAction = view->action("folding_expandtoplevel"); + QVERIFY(expandAction); + + for(int i = 0; i < doc.lines(); ++i) { + doc.buffer().ensureHighlighted(i); + // make sure this works without crash + KateLineInfo info; + doc.lineInfo(&info, i); + } + + QCOMPARE(doc.visibleLines(), 4u); + collapseAction->trigger(); + QCOMPARE(doc.visibleLines(), 2u); + for(int i = 0; i < doc.lines(); ++i) { + // make sure this works without crash + KateLineInfo info; + doc.lineInfo(&info, i); + } + doc.clear(); + QCOMPARE(doc.visibleLines(), 1u); + doc.undo(); + QCOMPARE(doc.visibleLines(), 5u); + for(int i = 0; i < doc.lines(); ++i) { + // make sure this works without crash + KateLineInfo info; + doc.lineInfo(&info, i); + } + + // now add a newline after the last } + QCOMPARE(doc.text(Range(2, 0, 2, 1)), QLatin1String("}")); + view->setCursorPosition(Cursor(2, 1)); + doc.newLine(view); + for(int i = 0; i < doc.lines(); ++i) { + // make sure this works without crash + KateLineInfo info; + doc.lineInfo(&info, i); + } +#endif +} + + +void KateViewTest::testSelection() +{ + // see also: https://bugs.kde.org/show_bug.cgi?id=277422 + // wrong behavior before: + // Open file with text + // click at end of some line (A) and drag to right, i.e. without selecting anything + // click somewhere else (B) + // shift click to another place (C) + // => expected: selection from B to C + // => actual: selection from A to C + + KTemporaryFile file; + file.setSuffix(".txt"); + file.open(); + QTextStream stream(&file); + stream << "A\n" + << "B\n" + << "C"; + stream << flush; + file.close(); + + KateDocument doc(false, false, false); + QVERIFY(doc.openUrl(KUrl(file.fileName()))); + + KateView* view = new KateView(&doc, 0); + view->resize(100, 100); + view->show(); + + // hackish but works: access to KateViewInternal + QObject* internalView = view->childAt(50, 50); + QCOMPARE(internalView->metaObject()->className(), "KateViewInternal"); + + const QPoint afterA = view->cursorToCoordinate(Cursor(0, 1)); + const QPoint afterB = view->cursorToCoordinate(Cursor(1, 1)); + const QPoint afterC = view->cursorToCoordinate(Cursor(2, 1)); + + // click after A + QCoreApplication::sendEvent(internalView, new QMouseEvent(QEvent::MouseButtonPress, afterA, + Qt::LeftButton, Qt::LeftButton, + Qt::NoModifier)); + + QCoreApplication::sendEvent(internalView, new QMouseEvent(QEvent::MouseButtonRelease, afterA, + Qt::LeftButton, Qt::LeftButton, + Qt::NoModifier)); + QCOMPARE(view->cursorPosition(), Cursor(0, 1)); + // drag to right + QCoreApplication::sendEvent(internalView, new QMouseEvent(QEvent::MouseButtonPress, afterA, + Qt::LeftButton, Qt::LeftButton, + Qt::NoModifier)); + + QCoreApplication::sendEvent(internalView, new QMouseEvent(QEvent::MouseMove, afterA + QPoint(50, 0), + Qt::LeftButton, Qt::LeftButton, + Qt::NoModifier)); + + QCoreApplication::sendEvent(internalView, new QMouseEvent(QEvent::MouseButtonRelease, afterA + QPoint(50, 0), + Qt::LeftButton, Qt::LeftButton, + Qt::NoModifier)); + + QCOMPARE(view->cursorPosition(), Cursor(0, 1)); + QVERIFY(!view->selection()); + + // click after C + QCoreApplication::sendEvent(internalView, new QMouseEvent(QEvent::MouseButtonPress, afterC, + Qt::LeftButton, Qt::LeftButton, + Qt::NoModifier)); + + QCoreApplication::sendEvent(internalView, new QMouseEvent(QEvent::MouseButtonRelease, afterC, + Qt::LeftButton, Qt::LeftButton, + Qt::NoModifier)); + + QCOMPARE(view->cursorPosition(), Cursor(2, 1)); + // shift+click after B + QCoreApplication::sendEvent(internalView, new QMouseEvent(QEvent::MouseButtonPress, afterB, + Qt::LeftButton, Qt::LeftButton, + Qt::ShiftModifier)); + + QCoreApplication::sendEvent(internalView, new QMouseEvent(QEvent::MouseButtonRelease, afterB, + Qt::LeftButton, Qt::LeftButton, + Qt::ShiftModifier)); + + QCOMPARE(view->cursorPosition(), Cursor(1, 1)); + QCOMPARE(view->selectionRange(), Range(1, 1, 2, 1)); +} + +void KateViewTest::testKillline() +{ + KateDocument doc(false, false, false); + doc.insertLines(0, QStringList() + << "foo" + << "bar" + << "baz" + ); + + KateView *view = new KateView(&doc, 0); + + view->setCursorPositionInternal(KTextEditor::Cursor(1, 2)); + view->killLine(); + + QCOMPARE(doc.text(), QLatin1String("foo\nbaz\n")); + + doc.clear(); + QVERIFY(doc.isEmpty()); + + doc.insertLines(0, QStringList() + << "foo" + << "bar" + << "baz" + << "xxx" + ); + + view->setCursorPositionInternal(KTextEditor::Cursor(1, 2)); + view->shiftDown(); + view->killLine(); + + QCOMPARE(doc.text(), QLatin1String("foo\nxxx\n")); +} \ No newline at end of file diff --git a/kate/tests/kateview_test.h b/kate/tests/kateview_test.h new file mode 100644 index 00000000..c997c326 --- /dev/null +++ b/kate/tests/kateview_test.h @@ -0,0 +1,50 @@ +/* This file is part of the KDE libraries + Copyright (C) 2010 Milian Wolff + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KATE_VIEW_TEST_H +#define KATE_VIEW_TEST_H + +#include + +class KateViewTest : public QObject +{ + Q_OBJECT + +public: + KateViewTest(); + ~KateViewTest(); + +private Q_SLOTS: + void testReloadMultipleViews(); + + void testLowerCaseBlockSelection(); + + void testFolding_data(); + void testFolding(); + + void testCoordinatesToCursor(); + void testCursorToCoordinates(); + + void testBug287291(); + + void testSelection(); + void testKillline(); +}; + +#endif // KATE_VIEW_TEST_H diff --git a/kate/tests/kte_documentcursor.cpp b/kate/tests/kte_documentcursor.cpp new file mode 100644 index 00000000..e1da817c --- /dev/null +++ b/kate/tests/kte_documentcursor.cpp @@ -0,0 +1,286 @@ +/* This file is part of the KDE libraries + Copyright (C) 2012 Dominik Haumann + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "kte_documentcursor.h" +#include "moc_kte_documentcursor.cpp" + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +QTEST_KDEMAIN(DocumentCursorTest, GUI) + +using namespace KTextEditor; + +DocumentCursorTest::DocumentCursorTest() + : QObject() +{ +} + +DocumentCursorTest::~DocumentCursorTest() +{ +} + +void DocumentCursorTest::initTestCase() +{ + KateGlobal::self()->incRef(); +} + +void DocumentCursorTest::cleanupTestCase() +{ + KateGlobal::self()->decRef(); +} + +// tests: +// - atStartOfDocument +// - atStartOfLine +// - atEndOfDocument +// - atEndOfLine +// - move forward with Wrap +// - move forward with NoWrap +// - move backward +// - gotoNextLine +// - gotoPreviousLine +void DocumentCursorTest::testConvenienceApi() +{ + KateDocument doc (false, false, false); + doc.setText("\n" + "1\n" + "22\n" + "333\n" + "4444\n" + "55555"); + + // check start and end of document + DocumentCursor startOfDoc(&doc); startOfDoc.setPosition(Cursor(0, 0)); + DocumentCursor endOfDoc(&doc); endOfDoc.setPosition(Cursor(5, 5)); + QVERIFY(startOfDoc.atStartOfDocument()); + QVERIFY(startOfDoc.atStartOfLine()); + QVERIFY(endOfDoc.atEndOfDocument()); + QVERIFY(endOfDoc.atEndOfLine()); + + // set cursor to (2, 2) and then move to the left two times + DocumentCursor moving(&doc); moving.setPosition(Cursor(2, 2)); + QVERIFY(moving.atEndOfLine()); // at 2, 2 + QVERIFY(moving.move(-1)); // at 2, 1 + QCOMPARE(moving.toCursor(), Cursor(2, 1)); + QVERIFY(!moving.atEndOfLine()); + QVERIFY(moving.move(-1)); // at 2, 0 + QCOMPARE(moving.toCursor(), Cursor(2, 0)); + QVERIFY(moving.atStartOfLine()); + + // now move again to the left, should wrap to (1, 1) + QVERIFY(moving.move(-1)); // at 1, 1 + QCOMPARE(moving.toCursor(), Cursor(1, 1)); + QVERIFY(moving.atEndOfLine()); + + // advance 7 characters to position (3, 3) + QVERIFY(moving.move(7)); // at 3, 3 + QCOMPARE(moving.toCursor(), Cursor(3, 3)); + + // advance 20 characters in NoWrap mode, then go back 10 characters + QVERIFY(moving.move(20, DocumentCursor::NoWrap)); // at 3, 23 + QCOMPARE(moving.toCursor(), Cursor(3, 23)); + QVERIFY(moving.move(-10)); // at 3, 13 + QCOMPARE(moving.toCursor(), Cursor(3, 13)); + + // still at invalid text position. move one char to wrap around + QVERIFY(!moving.isValidTextPosition()); // at 3, 13 + QVERIFY(moving.move(1)); // at 4, 0 + QCOMPARE(moving.toCursor(), Cursor(4, 0)); + + // moving 11 characters in wrap mode moves to (5, 6), which is not a valid + // text position anymore. Hence, moving should be rejected. + QVERIFY(!moving.move(11)); + QVERIFY(moving.move(10)); + QVERIFY(moving.atEndOfDocument()); + + // try to move to next line, which fails. then go to previous line + QVERIFY(!moving.gotoNextLine()); + QVERIFY(moving.gotoPreviousLine()); + QCOMPARE(moving.toCursor(), Cursor(4, 0)); +} + +void DocumentCursorTest::testOperators() +{ + KateDocument doc (false, false, false); + doc.setText("--oo--\n" + "--oo--\n" + "--oo--"); + + // create lots of cursors for comparison + Cursor invalid = Cursor::invalid(); + Cursor c02(0, 2); + Cursor c04(0, 4); + Cursor c14(1, 4); + + DocumentCursor m02(&doc); + DocumentCursor m04(&doc); + DocumentCursor m14(&doc); + + QVERIFY(m02 == invalid); + QVERIFY(m04 == invalid); + QVERIFY(m14 == invalid); + + m02.setPosition(Cursor(0, 2)); + m04.setPosition(Cursor(0, 4)); + m14.setPosition(Cursor(1, 4)); + + // invalid comparison + //cppcheck-suppress duplicateExpression + QVERIFY(invalid == invalid); + QVERIFY(invalid <= c02); + QVERIFY(invalid < c02); + QVERIFY(!(invalid > c02)); + QVERIFY(!(invalid >= c02)); + + QVERIFY(!(invalid == m02)); + QVERIFY(invalid <= m02); + QVERIFY(invalid < m02); + QVERIFY(!(invalid > m02)); + QVERIFY(!(invalid >= m02)); + + QVERIFY(!(m02 == invalid)); + QVERIFY(!(m02 <= invalid)); + QVERIFY(!(m02 < invalid)); + QVERIFY(m02 > invalid); + QVERIFY(m02 >= invalid); + + // DocumentCursor <-> DocumentCursor + //cppcheck-suppress duplicateExpression + QVERIFY(m02 == m02); + //cppcheck-suppress duplicateExpression + QVERIFY(m02 <= m02); + //cppcheck-suppress duplicateExpression + QVERIFY(m02 >= m02); + //cppcheck-suppress duplicateExpression + QVERIFY(!(m02 < m02)); + //cppcheck-suppress duplicateExpression + QVERIFY(!(m02 > m02)); + //cppcheck-suppress duplicateExpression + QVERIFY(!(m02 != m02)); + + QVERIFY(!(m02 == m04)); + QVERIFY(m02 <= m04); + QVERIFY(!(m02 >= m04)); + QVERIFY(m02 < m04); + QVERIFY(!(m02 > m04)); + QVERIFY(m02 != m04); + + QVERIFY(!(m04 == m02)); + QVERIFY(!(m04 <= m02)); + QVERIFY(m04 >= m02); + QVERIFY(!(m04 < m02)); + QVERIFY(m04 > m02); + QVERIFY(m04 != m02); + + QVERIFY(!(m02 == m14)); + QVERIFY(m02 <= m14); + QVERIFY(!(m02 >= m14)); + QVERIFY(m02 < m14); + QVERIFY(!(m02 > m14)); + QVERIFY(m02 != m14); + + QVERIFY(!(m14 == m02)); + QVERIFY(!(m14 <= m02)); + QVERIFY(m14 >= m02); + QVERIFY(!(m14 < m02)); + QVERIFY(m14 > m02); + QVERIFY(m14 != m02); + + // DocumentCursor <-> Cursor + QVERIFY(m02 == c02); + QVERIFY(m02 <= c02); + QVERIFY(m02 >= c02); + QVERIFY(!(m02 < c02)); + QVERIFY(!(m02 > c02)); + QVERIFY(!(m02 != c02)); + + QVERIFY(!(m02 == c04)); + QVERIFY(m02 <= c04); + QVERIFY(!(m02 >= c04)); + QVERIFY(m02 < c04); + QVERIFY(!(m02 > c04)); + QVERIFY(m02 != c04); + + QVERIFY(!(m04 == c02)); + QVERIFY(!(m04 <= c02)); + QVERIFY(m04 >= c02); + QVERIFY(!(m04 < c02)); + QVERIFY(m04 > c02); + QVERIFY(m04 != c02); + + QVERIFY(!(m02 == c14)); + QVERIFY(m02 <= c14); + QVERIFY(!(m02 >= c14)); + QVERIFY(m02 < c14); + QVERIFY(!(m02 > c14)); + QVERIFY(m02 != c14); + + QVERIFY(!(m14 == c02)); + QVERIFY(!(m14 <= c02)); + QVERIFY(m14 >= c02); + QVERIFY(!(m14 < c02)); + QVERIFY(m14 > c02); + QVERIFY(m14 != c02); + + // Cursor <-> DocumentCursor + QVERIFY(c02 == m02); + QVERIFY(c02 <= m02); + QVERIFY(c02 >= m02); + QVERIFY(!(c02 < m02)); + QVERIFY(!(c02 > m02)); + QVERIFY(!(c02 != m02)); + + QVERIFY(!(c02 == m04)); + QVERIFY(c02 <= m04); + QVERIFY(!(c02 >= m04)); + QVERIFY(c02 < m04); + QVERIFY(!(c02 > m04)); + QVERIFY(c02 != m04); + + QVERIFY(!(c04 == m02)); + QVERIFY(!(c04 <= m02)); + QVERIFY(c04 >= m02); + QVERIFY(!(c04 < m02)); + QVERIFY(c04 > m02); + QVERIFY(c04 != m02); + + QVERIFY(!(c02 == m14)); + QVERIFY(c02 <= m14); + QVERIFY(!(c02 >= m14)); + QVERIFY(c02 < m14); + QVERIFY(!(c02 > m14)); + QVERIFY(c02 != m14); + + QVERIFY(!(c14 == m02)); + QVERIFY(!(c14 <= m02)); + QVERIFY(c14 >= m02); + QVERIFY(!(c14 < m02)); + QVERIFY(c14 > m02); + QVERIFY(c14 != m02); +} diff --git a/kate/tests/kte_documentcursor.h b/kate/tests/kte_documentcursor.h new file mode 100644 index 00000000..ccb22839 --- /dev/null +++ b/kate/tests/kte_documentcursor.h @@ -0,0 +1,41 @@ +/* This file is part of the KDE libraries + Copyright (C) 2012 Dominik Haumann + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KTE_DOCUMENTCURSOR_TEST_H +#define KTE_DOCUMENTCURSOR_TEST_H + +#include + +class DocumentCursorTest : public QObject +{ + Q_OBJECT + +public: + DocumentCursorTest(); + ~DocumentCursorTest(); + +private Q_SLOTS: + void initTestCase(); + void cleanupTestCase(); + + void testConvenienceApi(); + void testOperators(); +}; + +#endif diff --git a/kate/tests/messagetest.cpp b/kate/tests/messagetest.cpp new file mode 100644 index 00000000..1745dc76 --- /dev/null +++ b/kate/tests/messagetest.cpp @@ -0,0 +1,407 @@ +/* This file is part of the Kate project. + * + * Copyright (C) 2013 Dominik Haumann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "messagetest.h" + +#include + +#include +#include +#include +#include +#include + +using namespace KTextEditor; + +QTEST_KDEMAIN(MessageTest, GUI) + +void MessageTest::initTestCase() +{ + KateGlobal::self()->incRef(); +} + +void MessageTest::cleanupTestCase() +{ + KateGlobal::self()->decRef(); +} + +void MessageTest::testPostMessage() +{ + KateDocument doc(false, false, false); + + KateView* view = static_cast(doc.createView(0)); + view->show(); + view->resize(400, 300); + + QPointer message = new Message("Message text", Message::Information); + message->setPosition(Message::TopInView); + + // posing message should succeed + QVERIFY(doc.postMessage(message)); + + // + // show message for one second, then delete again + // + QTest::qWait(500); + QVERIFY(view->messageWidget()); + QVERIFY(view->messageWidget()->isVisible()); + + QVERIFY(message != 0); + delete message; + QTest::qWait(600); // fadeout animation takes 500 ms + QVERIFY(!view->messageWidget()->isVisible()); +} + +void MessageTest::testAutoHide() +{ + KateDocument doc(false, false, false); + + KateView* view = static_cast(doc.createView(0)); + view->show(); + view->resize(400, 300); + QTest::qWait(500); // make sure the widget is really shown in correct size + + // + // show a message with autoHide. Check, if it's deleted correctly + // auto hide mode: Message::Immediate + // + QPointer message = new Message("Message text", Message::Information); + message->setPosition(Message::TopInView); + message->setAutoHide(2000); + message->setAutoHideMode(Message::Immediate); + + doc.postMessage(message); + + QTest::qWait(500); + QVERIFY(view->messageWidget()->isVisible()); + + // should be deleted after 2.1 seconds + QTest::qWait(1600); + QVERIFY(message.data() == 0); + + // message widget should be hidden after 2.6 seconds + QTest::qWait(500); + QVERIFY(!view->messageWidget()->isVisible()); +} + +void MessageTest::testAutoHideAfterUserInteraction() +{ + KateDocument doc(false, false, false); + + KateView* view = static_cast(doc.createView(0)); + view->show(); + view->resize(400, 300); + QTest::qWait(500); // make sure the widget is really shown in correct size + + // + // show a message with autoHide. Check, if it's deleted correctly + // auto hide mode: Message::AfterUserInteraction + // + QPointer message = new Message("Message text", Message::Information); + message->setPosition(Message::TopInView); + message->setAutoHide(2000); + QVERIFY(message->autoHideMode() == Message::AfterUserInteraction); + + doc.postMessage(message); + + QTest::qWait(1000); + QVERIFY(view->messageWidget()->isVisible()); + + // now trigger user interaction after 1 second + view->resize(400, 310); + + // should still be there after deleted after another 1.9 seconds + QTest::qWait(1900); + QVERIFY(message.data() != 0); + QVERIFY(view->messageWidget()->isVisible()); + + // another 200ms later: 3.1 seconds are gone, message should be deleted + // and fade animation should be active + QTest::qWait(200); + QVERIFY(message.data() == 0); + QVERIFY(view->messageWidget()->isVisible()); + + // after a total of 3.6 seconds, widget should be hidden + QTest::qWait(500); + QVERIFY(!view->messageWidget()->isVisible()); +} + +void MessageTest::testMessageQueue() +{ + KateDocument doc(false, false, false); + + KateView* view = static_cast(doc.createView(0)); + view->show(); + view->resize(400, 300); + QTest::qWait(500); // make sure the widget is really shown in correct size + + // + // add two messages, both with autoHide to 1 second, and check that the queue is processed correctly + // auto hide mode: Message::Immediate + // + QPointer m1 = new Message("Info text", Message::Information); + m1->setPosition(Message::TopInView); + m1->setAutoHide(1000); + m1->setAutoHideMode(Message::Immediate); + + QPointer m2 = new Message("Error text", Message::Error); + m2->setPosition(Message::TopInView); + m2->setAutoHide(1000); + m2->setAutoHideMode(Message::Immediate); + + // post both messages + QVERIFY(doc.postMessage(m1)); + QVERIFY(doc.postMessage(m2)); + + // after 0.5s, first message should be visible, (timer of m1 triggered) + QTest::qWait(500); + QVERIFY(view->messageWidget()->isVisible()); + QVERIFY(m1.data() != 0); + QVERIFY(m2.data() != 0); + + // after 1.2s, first message is deleted, and hide animation is active + QTest::qWait(700); + QVERIFY(view->messageWidget()->isVisible()); + QVERIFY(m1.data() == 0); + QVERIFY(m2.data() != 0); + + // timer of m2 triggered after 1.5s, i.e. after hide animation if finished + QTest::qWait(500); + + // after 2.1s, second message should be visible + QTest::qWait(500); + QVERIFY(view->messageWidget()->isVisible()); + QVERIFY(m2.data() != 0); + + // after 2.6s, second message is deleted, and hide animation is active + QTest::qWait(500); + QVERIFY(view->messageWidget()->isVisible()); + QVERIFY(m2.data() == 0); + + // after a total of 3.1s, animation is finished and widget is hidden + QTest::qWait(500); + QVERIFY(!view->messageWidget()->isVisible()); +} + +void MessageTest::testPriority() +{ + KateDocument doc(false, false, false); + + KateView* view = static_cast(doc.createView(0)); + view->show(); + view->resize(400, 300); + QTest::qWait(500); // make sure the widget is really shown in correct size + + // + // add two messages + // - m1: no auto hide timer, priority 0 + // - m2: auto hide timer of 1 second, priority 1 + // test: + // - m1 should be hidden in favour of m2 + // - changing text of m1 while m2 is displayed should not change the displayed text + // + QPointer m1 = new Message("m1", Message::Positive); + m1->setPosition(Message::TopInView); + QVERIFY(m1->priority() == 0); + + QPointer m2 = new Message("m2", Message::Error); + m2->setPosition(Message::TopInView); + m2->setAutoHide(1000); + m2->setAutoHideMode(Message::Immediate); + m2->setPriority(1); + QVERIFY(m2->priority() == 1); + + // post m1 + QVERIFY(doc.postMessage(m1)); + + // after 1s, message should be displayed + QTest::qWait(1000); + QVERIFY(view->messageWidget()->isVisible()); + QCOMPARE(view->messageWidget()->text(), QString("m1")); + QVERIFY(m1.data() != 0); + + // post m2, m1 should be hidden, and m2 visible + QVERIFY(doc.postMessage(m2)); + QVERIFY(m2.data() != 0); + + // alter text of m1 when m2 is visible, shouldn't influence m2 + QTest::qWait(600); + m1->setText("m1 changed"); + + // after 0.7 seconds, m2 is visible + QTest::qWait(100); + QCOMPARE(view->messageWidget()->text(), QString("m2")); + QVERIFY(m2.data() != 0); + + // after 1.6 seconds, m2 is hidden again and m1 is visible again + QTest::qWait(900); + QVERIFY(view->messageWidget()->isVisible()); + QVERIFY(m1.data() != 0); + QVERIFY(m2.data() == 0); + + // finally check m1 agagin + QTest::qWait(1000); + QCOMPARE(view->messageWidget()->text(), QString("m1 changed")); +} + +void MessageTest::testCreateView() +{ + KateDocument doc(false, false, false); + + // + // - first post a message + // - then create two views + // + // test: + // - verify that both views get the message + // - verify that, once the message is deleted, both views hide the message + // + QPointer m1 = new Message("message", Message::Positive); + m1->setPosition(Message::TopInView); + QVERIFY(m1->priority() == 0); + + // first post message to doc without views + QVERIFY(doc.postMessage(m1)); + + // now create views + KateView* v1 = static_cast(doc.createView(0)); + KateView* v2 = static_cast(doc.createView(0)); + v1->show(); + v2->show(); + v1->resize(400, 300); + v2->resize(400, 300); + + // make sure both views show the message + QTest::qWait(500); + QVERIFY(v1->messageWidget()->isVisible()); + QVERIFY(v2->messageWidget()->isVisible()); + QCOMPARE(v1->messageWidget()->text(), QString("message")); + QCOMPARE(v2->messageWidget()->text(), QString("message")); + QVERIFY(m1.data() != 0); + + // delete message, then check after fadeout time 0f 0.5s whether message is gone + delete m1; + QTest::qWait(600); + QVERIFY(!v1->messageWidget()->isVisible()); + QVERIFY(!v2->messageWidget()->isVisible()); +} + +void MessageTest::testHideView() +{ + KateDocument doc(false, false, false); + + KateView* view = static_cast(doc.createView(0)); + view->show(); + view->resize(400, 300); + + // create message that hides after 2s immediately + QPointer message = new Message("Message text", Message::Information); + message->setAutoHide(2000); + message->setAutoHideMode(Message::Immediate); + message->setPosition(Message::TopInView); + + // posting message should succeed + QVERIFY(doc.postMessage(message)); + + // + // test: + // - show the message for 1.5s, then hide the view + // - the auto hide timer will continue, no matter what + // - showing the view again after the auto hide timer is finished + animation time really hide the widget + // + QTest::qWait(1100); + QVERIFY(view->messageWidget()->isVisible()); + QCOMPARE(view->messageWidget()->text(), QString("Message text")); + + // hide view + view->hide(); + + // wait 1s, message should be null (after total of 2100 ms) + QTest::qWait(1000); + QVERIFY(message.data() == 0); + + // show view again, message contents should be fading for the lasting 400 ms + view->show(); + QVERIFY(view->messageWidget()->isVisible()); + QCOMPARE(view->messageWidget()->text(), QString("Message text")); + + // wait another 0.5s, then message widget should be hidden + QTest::qWait(500); + QVERIFY(message.data() == 0); + QVERIFY(!view->messageWidget()->isVisible()); +} + +void MessageTest::testHideViewAfterUserInteraction() +{ + KateDocument doc(false, false, false); + + KateView* view = static_cast(doc.createView(0)); + view->show(); + view->resize(400, 300); + QTest::qWait(100); + + // create message that hides after 2s immediately + QPointer message = new Message("Message text", Message::Information); + message->setAutoHide(2000); + QVERIFY(message->autoHideMode() == Message::AfterUserInteraction); + message->setPosition(Message::TopInView); + + // posting message should succeed + QVERIFY(doc.postMessage(message)); + + // + // test: + // - show the message for 1.5s, then hide the view + // - this should stop the autoHide timer + // - showing the view again should restart the autoHide timer (again 2s) + // + QTest::qWait(1500); + QVERIFY(view->messageWidget()->isVisible()); + QCOMPARE(view->messageWidget()->text(), QString("Message text")); + + // hide view + view->hide(); + + // wait 1s, check that message is still valid + QTest::qWait(1000); + QVERIFY(message.data() != 0); + + // show view again, and trigger user interaction through resize + view->show(); + QTest::qWait(500); + view->resize(400, 310); + + // wait 1.5s and check that message is still displayed + QTest::qWait(1500); + QVERIFY(message.data() != 0); + QVERIFY(view->messageWidget()->isVisible()); + QCOMPARE(view->messageWidget()->text(), QString("Message text")); + + // wait another 0.6s, then the message is deleted + QTest::qWait(600); + QVERIFY(message.data() == 0); + QVERIFY(view->messageWidget()->isVisible()); + + // another 0.5s, and the message widget should be hidden + QTest::qWait(600); + QVERIFY(!view->messageWidget()->isVisible()); +} + +// kate: indent-width 4; remove-trailing-spaces all; diff --git a/kate/tests/messagetest.h b/kate/tests/messagetest.h new file mode 100644 index 00000000..7c815f9a --- /dev/null +++ b/kate/tests/messagetest.h @@ -0,0 +1,44 @@ +/* This file is part of the KDE libraries + Copyright (C) 2013 Dominik Haumann + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KTEXTEDITOR_MESSAGE_TEST_H +#define KTEXTEDITOR_MESSAGE_TEST_H + +#include + +class MessageTest : public QObject +{ + Q_OBJECT + +public Q_SLOTS: + void initTestCase(); + void cleanupTestCase(); + +private Q_SLOTS: + void testPostMessage(); + void testAutoHide(); + void testAutoHideAfterUserInteraction(); + void testMessageQueue(); + void testPriority(); + void testCreateView(); + void testHideView(); + void testHideViewAfterUserInteraction(); +}; + +#endif // KTEXTEDITOR_MESSAGE_TEST_H diff --git a/kate/tests/modificationsystem_test.cpp b/kate/tests/modificationsystem_test.cpp new file mode 100644 index 00000000..65ba6f18 --- /dev/null +++ b/kate/tests/modificationsystem_test.cpp @@ -0,0 +1,742 @@ +/* This file is part of the KDE libraries + Copyright (C) 2011 Dominik Haumann + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "modificationsystem_test.h" +#include "moc_modificationsystem_test.cpp" + +#include + +#include +#include +#include +#include + +QTEST_KDEMAIN(ModificationSystemTest, GUI) + +using namespace KTextEditor; + +void ModificationSystemTest::initTestCase() +{ + KateGlobal::self()->incRef(); +} + +void ModificationSystemTest::cleanupTestCase() +{ + KateGlobal::self()->decRef(); +} + +static void clearModificationFlags(KateDocument* doc) +{ + for (int i = 0; i < doc->lines(); ++i) { + Kate::TextLine line = doc->plainKateTextLine(i); + line->markAsModified(false); + line->markAsSavedOnDisk(false); + } +} + +static void markModifiedLinesAsSaved(KateDocument* doc) +{ + for (int i = 0; i < doc->lines(); ++i) { + Kate::TextLine textLine = doc->plainKateTextLine(i); + if (textLine->markedAsModified()) + textLine->markAsSavedOnDisk(true); + } +} + +void ModificationSystemTest::testInsertText() +{ + KateDocument* doc = qobject_cast(KateGlobal::self()->createDocument(0)); + + const QString content("first line\n"); + doc->setText(content); + + Kate::TextLine line0 = doc->plainKateTextLine(0); + + // now all lines should have state "Modified" + QVERIFY(line0->markedAsModified()); + QVERIFY(!line0->markedAsSavedOnDisk()); + + + // clear all modification flags, forces no flags + doc->setModified(false); + doc->undoManager()->updateLineModifications(); + clearModificationFlags(doc); + + QVERIFY(!line0->markedAsModified()); + QVERIFY(!line0->markedAsSavedOnDisk()); + + // now we have an a unmodified file, start real tests + // insert text in line 0, then undo and redo + doc->insertText(Cursor(0, 2), "_"); + QVERIFY(line0->markedAsModified()); + QVERIFY(!line0->markedAsSavedOnDisk()); + + doc->undo(); + QVERIFY(!line0->markedAsModified()); + QVERIFY(line0->markedAsSavedOnDisk()); + + doc->redo(); + QVERIFY(line0->markedAsModified()); + QVERIFY(!line0->markedAsSavedOnDisk()); + + + // + // now simulate "save", then do the undo/redo tests again + // + doc->setModified(false); + markModifiedLinesAsSaved(doc); + doc->undoManager()->updateLineModifications(); + + // now no line should have state "Modified" + QVERIFY(!line0->markedAsModified()); + QVERIFY(line0->markedAsSavedOnDisk()); + + // undo the text insertion + doc->undo(); + QVERIFY(line0->markedAsModified()); + QVERIFY(!line0->markedAsSavedOnDisk()); + + doc->redo(); + QVERIFY(!line0->markedAsModified()); + QVERIFY(line0->markedAsSavedOnDisk()); + + delete doc; +} + +void ModificationSystemTest::testRemoveText() +{ + KateDocument* doc = qobject_cast(KateGlobal::self()->createDocument(0)); + + const QString content("first line\n"); + doc->setText(content); + + Kate::TextLine line0 = doc->plainKateTextLine(0); + + // now all lines should have state "Modified" + QVERIFY(line0->markedAsModified()); + QVERIFY(!line0->markedAsSavedOnDisk()); + + + // clear all modification flags, forces no flags + doc->setModified(false); + doc->undoManager()->updateLineModifications(); + clearModificationFlags(doc); + + QVERIFY(!line0->markedAsModified()); + QVERIFY(!line0->markedAsSavedOnDisk()); + + // now we have an a unmodified file, start real tests + // remove text in line 0, then undo and redo + doc->removeText(Range(Cursor(0, 1), Cursor(0, 2))); + QVERIFY(line0->markedAsModified()); + QVERIFY(!line0->markedAsSavedOnDisk()); + + doc->undo(); + QVERIFY(!line0->markedAsModified()); + QVERIFY(line0->markedAsSavedOnDisk()); + + doc->redo(); + QVERIFY(line0->markedAsModified()); + QVERIFY(!line0->markedAsSavedOnDisk()); + + + // + // now simulate "save", then do the undo/redo tests again + // + doc->setModified(false); + markModifiedLinesAsSaved(doc); + doc->undoManager()->updateLineModifications(); + + // now no line should have state "Modified" + QVERIFY(!line0->markedAsModified()); + QVERIFY(line0->markedAsSavedOnDisk()); + + // undo the text insertion + doc->undo(); + QVERIFY(line0->markedAsModified()); + QVERIFY(!line0->markedAsSavedOnDisk()); + + doc->redo(); + QVERIFY(!line0->markedAsModified()); + QVERIFY(line0->markedAsSavedOnDisk()); + + delete doc; +} + +void ModificationSystemTest::testInsertLine() +{ + KateDocument* doc = qobject_cast(KateGlobal::self()->createDocument(0)); + + const QString content("0\n" + "2"); + doc->setText(content); + + // clear all modification flags, forces no flags + doc->setModified(false); + doc->undoManager()->updateLineModifications(); + clearModificationFlags(doc); + + // insert at line 1 + doc->insertLine(1, "1"); + + QVERIFY(!doc->plainKateTextLine(0)->markedAsModified()); + QVERIFY(doc->plainKateTextLine(1)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(2)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(0)->markedAsSavedOnDisk()); + QVERIFY(!doc->plainKateTextLine(1)->markedAsSavedOnDisk()); + QVERIFY(!doc->plainKateTextLine(2)->markedAsSavedOnDisk()); + + doc->undo(); + QVERIFY(!doc->plainKateTextLine(0)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(1)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(0)->markedAsSavedOnDisk()); + QVERIFY(!doc->plainKateTextLine(1)->markedAsSavedOnDisk()); + + doc->redo(); + QVERIFY(!doc->plainKateTextLine(0)->markedAsModified()); + QVERIFY(doc->plainKateTextLine(1)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(2)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(0)->markedAsSavedOnDisk()); + QVERIFY(!doc->plainKateTextLine(1)->markedAsSavedOnDisk()); + QVERIFY(!doc->plainKateTextLine(2)->markedAsSavedOnDisk()); + + + // + // now simulate "save", then do the undo/redo tests again + // + doc->setModified(false); + markModifiedLinesAsSaved(doc); + doc->undoManager()->updateLineModifications(); + + // now no line should have state "Modified" + QVERIFY(!doc->plainKateTextLine(0)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(1)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(2)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(0)->markedAsSavedOnDisk()); + QVERIFY(doc->plainKateTextLine(1)->markedAsSavedOnDisk()); + QVERIFY(!doc->plainKateTextLine(2)->markedAsSavedOnDisk()); + + // undo the text insertion + doc->undo(); + QVERIFY(!doc->plainKateTextLine(0)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(1)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(0)->markedAsSavedOnDisk()); + QVERIFY(!doc->plainKateTextLine(1)->markedAsSavedOnDisk()); + + doc->redo(); + QVERIFY(!doc->plainKateTextLine(0)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(1)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(2)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(0)->markedAsSavedOnDisk()); + QVERIFY(doc->plainKateTextLine(1)->markedAsSavedOnDisk()); + QVERIFY(!doc->plainKateTextLine(2)->markedAsSavedOnDisk()); + + delete doc; +} + +void ModificationSystemTest::testRemoveLine() +{ + KateDocument* doc = qobject_cast(KateGlobal::self()->createDocument(0)); + + const QString content("0\n" + "1\n" + "2"); + doc->setText(content); + + // clear all modification flags, forces no flags + doc->setModified(false); + doc->undoManager()->updateLineModifications(); + clearModificationFlags(doc); + + // remove at line 1 + doc->removeLine(1); + + QVERIFY(!doc->plainKateTextLine(0)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(1)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(0)->markedAsSavedOnDisk()); + QVERIFY(!doc->plainKateTextLine(1)->markedAsSavedOnDisk()); + + doc->undo(); + QVERIFY(!doc->plainKateTextLine(0)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(1)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(2)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(0)->markedAsSavedOnDisk()); + QVERIFY(doc->plainKateTextLine(1)->markedAsSavedOnDisk()); + QVERIFY(!doc->plainKateTextLine(2)->markedAsSavedOnDisk()); + + doc->redo(); + QVERIFY(!doc->plainKateTextLine(0)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(1)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(0)->markedAsSavedOnDisk()); + QVERIFY(!doc->plainKateTextLine(1)->markedAsSavedOnDisk()); + + + // + // now simulate "save", then do the undo/redo tests again + // + doc->setModified(false); + markModifiedLinesAsSaved(doc); + doc->undoManager()->updateLineModifications(); + + // now no line should have state "Modified" + QVERIFY(!doc->plainKateTextLine(0)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(1)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(0)->markedAsSavedOnDisk()); + QVERIFY(!doc->plainKateTextLine(1)->markedAsSavedOnDisk()); + + // undo the text insertion + doc->undo(); + QVERIFY(!doc->plainKateTextLine(0)->markedAsModified()); + QVERIFY(doc->plainKateTextLine(1)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(2)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(0)->markedAsSavedOnDisk()); + QVERIFY(!doc->plainKateTextLine(1)->markedAsSavedOnDisk()); + QVERIFY(!doc->plainKateTextLine(2)->markedAsSavedOnDisk()); + + doc->redo(); + QVERIFY(!doc->plainKateTextLine(0)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(1)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(0)->markedAsSavedOnDisk()); + QVERIFY(!doc->plainKateTextLine(1)->markedAsSavedOnDisk()); + + delete doc; +} + +void ModificationSystemTest::testWrapLineMid() +{ + for (int i = 0; i < 2; ++i) { + bool insertNewLine = (i == 1); + KateDocument* doc = qobject_cast(KateGlobal::self()->createDocument(0)); + + const QString content("aaaa\n" + "bbbb\n" + "cccc"); + doc->setText(content); + + // clear all modification flags, forces no flags + doc->setModified(false); + doc->undoManager()->updateLineModifications(); + clearModificationFlags(doc); + + // wrap line 1 at |: bb|bb + doc->editWrapLine(1, 2, insertNewLine); + + QVERIFY(!doc->plainKateTextLine(0)->markedAsModified()); + QVERIFY(doc->plainKateTextLine(1)->markedAsModified()); + QVERIFY(doc->plainKateTextLine(2)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(0)->markedAsSavedOnDisk()); + QVERIFY(!doc->plainKateTextLine(1)->markedAsSavedOnDisk()); + QVERIFY(!doc->plainKateTextLine(2)->markedAsSavedOnDisk()); + + doc->undo(); + QVERIFY(!doc->plainKateTextLine(0)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(1)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(0)->markedAsSavedOnDisk()); + QVERIFY(doc->plainKateTextLine(1)->markedAsSavedOnDisk()); + + doc->redo(); + QVERIFY(!doc->plainKateTextLine(0)->markedAsModified()); + QVERIFY(doc->plainKateTextLine(1)->markedAsModified()); + QVERIFY(doc->plainKateTextLine(2)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(0)->markedAsSavedOnDisk()); + QVERIFY(!doc->plainKateTextLine(1)->markedAsSavedOnDisk()); + QVERIFY(!doc->plainKateTextLine(2)->markedAsSavedOnDisk()); + + + // + // now simulate "save", then do the undo/redo tests again + // + doc->setModified(false); + markModifiedLinesAsSaved(doc); + doc->undoManager()->updateLineModifications(); + + // now no line should have state "Modified" + QVERIFY(!doc->plainKateTextLine(0)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(1)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(2)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(0)->markedAsSavedOnDisk()); + QVERIFY(doc->plainKateTextLine(1)->markedAsSavedOnDisk()); + QVERIFY(doc->plainKateTextLine(2)->markedAsSavedOnDisk()); + + // undo the text insertion + doc->undo(); + QVERIFY(!doc->plainKateTextLine(0)->markedAsModified()); + QVERIFY(doc->plainKateTextLine(1)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(0)->markedAsSavedOnDisk()); + QVERIFY(!doc->plainKateTextLine(1)->markedAsSavedOnDisk()); + + doc->redo(); + QVERIFY(!doc->plainKateTextLine(0)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(1)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(2)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(0)->markedAsSavedOnDisk()); + QVERIFY(doc->plainKateTextLine(1)->markedAsSavedOnDisk()); + QVERIFY(doc->plainKateTextLine(2)->markedAsSavedOnDisk()); + + delete doc; + } +} + +void ModificationSystemTest::testWrapLineAtEnd() +{ + KateDocument* doc = qobject_cast(KateGlobal::self()->createDocument(0)); + + const QString content("aaaa\n" + "bbbb"); + doc->setText(content); + + // clear all modification flags, forces no flags + doc->setModified(false); + doc->undoManager()->updateLineModifications(); + clearModificationFlags(doc); + + // wrap line 0 at end + doc->editWrapLine(0, 4); + + QVERIFY(!doc->plainKateTextLine(0)->markedAsModified()); + QVERIFY(doc->plainKateTextLine(1)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(2)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(0)->markedAsSavedOnDisk()); + QVERIFY(!doc->plainKateTextLine(1)->markedAsSavedOnDisk()); + QVERIFY(!doc->plainKateTextLine(2)->markedAsSavedOnDisk()); + + doc->undo(); + QVERIFY(!doc->plainKateTextLine(0)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(1)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(0)->markedAsSavedOnDisk()); + QVERIFY(!doc->plainKateTextLine(1)->markedAsSavedOnDisk()); + + doc->redo(); + QVERIFY(!doc->plainKateTextLine(0)->markedAsModified()); + QVERIFY(doc->plainKateTextLine(1)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(2)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(0)->markedAsSavedOnDisk()); + QVERIFY(!doc->plainKateTextLine(1)->markedAsSavedOnDisk()); + QVERIFY(!doc->plainKateTextLine(2)->markedAsSavedOnDisk()); + + + // + // now simulate "save", then do the undo/redo tests again + // + doc->setModified(false); + markModifiedLinesAsSaved(doc); + doc->undoManager()->updateLineModifications(); + + // now no line should have state "Modified" + QVERIFY(!doc->plainKateTextLine(0)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(1)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(2)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(0)->markedAsSavedOnDisk()); + QVERIFY(doc->plainKateTextLine(1)->markedAsSavedOnDisk()); + QVERIFY(!doc->plainKateTextLine(2)->markedAsSavedOnDisk()); + + // undo the text insertion + doc->undo(); + QVERIFY(!doc->plainKateTextLine(0)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(1)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(0)->markedAsSavedOnDisk()); + QVERIFY(!doc->plainKateTextLine(1)->markedAsSavedOnDisk()); + + doc->redo(); + QVERIFY(!doc->plainKateTextLine(0)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(1)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(2)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(0)->markedAsSavedOnDisk()); + QVERIFY(doc->plainKateTextLine(1)->markedAsSavedOnDisk()); + QVERIFY(!doc->plainKateTextLine(2)->markedAsSavedOnDisk()); + + delete doc; +} + +void ModificationSystemTest::testWrapLineAtStart() +{ + KateDocument* doc = qobject_cast(KateGlobal::self()->createDocument(0)); + + const QString content("aaaa\n" + "bbbb"); + doc->setText(content); + + // clear all modification flags, forces no flags + doc->setModified(false); + doc->undoManager()->updateLineModifications(); + clearModificationFlags(doc); + + // wrap line 0 at end + doc->editWrapLine(0, 0); + + QVERIFY(doc->plainKateTextLine(0)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(1)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(2)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(0)->markedAsSavedOnDisk()); + QVERIFY(!doc->plainKateTextLine(1)->markedAsSavedOnDisk()); + QVERIFY(!doc->plainKateTextLine(2)->markedAsSavedOnDisk()); + + doc->undo(); + QVERIFY(!doc->plainKateTextLine(0)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(1)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(0)->markedAsSavedOnDisk()); + QVERIFY(!doc->plainKateTextLine(1)->markedAsSavedOnDisk()); + + doc->redo(); + QVERIFY(doc->plainKateTextLine(0)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(1)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(2)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(0)->markedAsSavedOnDisk()); + QVERIFY(!doc->plainKateTextLine(1)->markedAsSavedOnDisk()); + QVERIFY(!doc->plainKateTextLine(2)->markedAsSavedOnDisk()); + + + // + // now simulate "save", then do the undo/redo tests again + // + doc->setModified(false); + markModifiedLinesAsSaved(doc); + doc->undoManager()->updateLineModifications(); + + // now no line should have state "Modified" + QVERIFY(!doc->plainKateTextLine(0)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(1)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(2)->markedAsModified()); + QVERIFY(doc->plainKateTextLine(0)->markedAsSavedOnDisk()); + QVERIFY(!doc->plainKateTextLine(1)->markedAsSavedOnDisk()); + QVERIFY(!doc->plainKateTextLine(2)->markedAsSavedOnDisk()); + + // undo the text insertion + doc->undo(); + QVERIFY(!doc->plainKateTextLine(0)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(1)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(0)->markedAsSavedOnDisk()); + QVERIFY(!doc->plainKateTextLine(1)->markedAsSavedOnDisk()); + + doc->redo(); + QVERIFY(!doc->plainKateTextLine(0)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(1)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(2)->markedAsModified()); + QVERIFY(doc->plainKateTextLine(0)->markedAsSavedOnDisk()); + QVERIFY(!doc->plainKateTextLine(1)->markedAsSavedOnDisk()); + QVERIFY(!doc->plainKateTextLine(2)->markedAsSavedOnDisk()); + + delete doc; +} + +void ModificationSystemTest::testUnWrapLine() +{ + KateDocument* doc = qobject_cast(KateGlobal::self()->createDocument(0)); + + const QString content("aaaa\n" + "bbbb\n" + "cccc"); + doc->setText(content); + + // clear all modification flags, forces no flags + doc->setModified(false); + doc->undoManager()->updateLineModifications(); + clearModificationFlags(doc); + + // join line 0 and 1 + doc->editUnWrapLine(0); + + QVERIFY(doc->plainKateTextLine(0)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(1)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(0)->markedAsSavedOnDisk()); + QVERIFY(!doc->plainKateTextLine(1)->markedAsSavedOnDisk()); + + doc->undo(); + QVERIFY(!doc->plainKateTextLine(0)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(1)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(2)->markedAsModified()); + QVERIFY(doc->plainKateTextLine(0)->markedAsSavedOnDisk()); + QVERIFY(doc->plainKateTextLine(1)->markedAsSavedOnDisk()); + QVERIFY(!doc->plainKateTextLine(2)->markedAsSavedOnDisk()); + + doc->redo(); + QVERIFY(doc->plainKateTextLine(0)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(1)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(0)->markedAsSavedOnDisk()); + QVERIFY(!doc->plainKateTextLine(1)->markedAsSavedOnDisk()); + + + // + // now simulate "save", then do the undo/redo tests again + // + doc->setModified(false); + markModifiedLinesAsSaved(doc); + doc->undoManager()->updateLineModifications(); + + // now no line should have state "Modified" + QVERIFY(!doc->plainKateTextLine(0)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(1)->markedAsModified()); + QVERIFY(doc->plainKateTextLine(0)->markedAsSavedOnDisk()); + QVERIFY(!doc->plainKateTextLine(1)->markedAsSavedOnDisk()); + + // undo the text insertion + doc->undo(); + QVERIFY(doc->plainKateTextLine(0)->markedAsModified()); + QVERIFY(doc->plainKateTextLine(1)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(2)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(0)->markedAsSavedOnDisk()); + QVERIFY(!doc->plainKateTextLine(1)->markedAsSavedOnDisk()); + QVERIFY(!doc->plainKateTextLine(2)->markedAsSavedOnDisk()); + + doc->redo(); + QVERIFY(!doc->plainKateTextLine(0)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(1)->markedAsModified()); + QVERIFY(doc->plainKateTextLine(0)->markedAsSavedOnDisk()); + QVERIFY(!doc->plainKateTextLine(1)->markedAsSavedOnDisk()); + + delete doc; +} + +void ModificationSystemTest::testUnWrapLine1Empty() +{ + KateDocument* doc = qobject_cast(KateGlobal::self()->createDocument(0)); + + const QString content("aaaa\n" + "\n" + "bbbb"); + doc->setText(content); + + // clear all modification flags, forces no flags + doc->setModified(false); + doc->undoManager()->updateLineModifications(); + clearModificationFlags(doc); + + // join line 1 and 2 + doc->editUnWrapLine(1); + + QVERIFY(!doc->plainKateTextLine(0)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(1)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(0)->markedAsSavedOnDisk()); + QVERIFY(!doc->plainKateTextLine(1)->markedAsSavedOnDisk()); + + doc->undo(); + QVERIFY(!doc->plainKateTextLine(0)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(1)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(2)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(0)->markedAsSavedOnDisk()); + QVERIFY(doc->plainKateTextLine(1)->markedAsSavedOnDisk()); + QVERIFY(!doc->plainKateTextLine(2)->markedAsSavedOnDisk()); + + doc->redo(); + QVERIFY(!doc->plainKateTextLine(0)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(1)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(0)->markedAsSavedOnDisk()); + QVERIFY(!doc->plainKateTextLine(1)->markedAsSavedOnDisk()); + + + // + // now simulate "save", then do the undo/redo tests again + // + doc->setModified(false); + markModifiedLinesAsSaved(doc); + doc->undoManager()->updateLineModifications(); + + // now no line should have state "Modified" + QVERIFY(!doc->plainKateTextLine(0)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(1)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(0)->markedAsSavedOnDisk()); + QVERIFY(!doc->plainKateTextLine(1)->markedAsSavedOnDisk()); + + // undo the text insertion + doc->undo(); + QVERIFY(!doc->plainKateTextLine(0)->markedAsModified()); + QVERIFY(doc->plainKateTextLine(1)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(2)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(0)->markedAsSavedOnDisk()); + QVERIFY(!doc->plainKateTextLine(1)->markedAsSavedOnDisk()); + QVERIFY(!doc->plainKateTextLine(2)->markedAsSavedOnDisk()); + + doc->redo(); + QVERIFY(!doc->plainKateTextLine(0)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(1)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(0)->markedAsSavedOnDisk()); + QVERIFY(!doc->plainKateTextLine(1)->markedAsSavedOnDisk()); + + delete doc; +} + +void ModificationSystemTest::testUnWrapLine2Empty() +{ + KateDocument* doc = qobject_cast(KateGlobal::self()->createDocument(0)); + + const QString content("aaaa\n" + "\n" + "bbbb"); + doc->setText(content); + + // clear all modification flags, forces no flags + doc->setModified(false); + doc->undoManager()->updateLineModifications(); + clearModificationFlags(doc); + + // join line 0 and 1 + doc->editUnWrapLine(0); + + QVERIFY(!doc->plainKateTextLine(0)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(1)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(0)->markedAsSavedOnDisk()); + QVERIFY(!doc->plainKateTextLine(1)->markedAsSavedOnDisk()); + + doc->undo(); + QVERIFY(!doc->plainKateTextLine(0)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(1)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(2)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(0)->markedAsSavedOnDisk()); + QVERIFY(doc->plainKateTextLine(1)->markedAsSavedOnDisk()); + QVERIFY(!doc->plainKateTextLine(2)->markedAsSavedOnDisk()); + + doc->redo(); + QVERIFY(!doc->plainKateTextLine(0)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(1)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(0)->markedAsSavedOnDisk()); + QVERIFY(!doc->plainKateTextLine(1)->markedAsSavedOnDisk()); + + + // + // now simulate "save", then do the undo/redo tests again + // + doc->setModified(false); + markModifiedLinesAsSaved(doc); + doc->undoManager()->updateLineModifications(); + + // now no line should have state "Modified" + QVERIFY(!doc->plainKateTextLine(0)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(1)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(0)->markedAsSavedOnDisk()); + QVERIFY(!doc->plainKateTextLine(1)->markedAsSavedOnDisk()); + + // undo the text insertion + doc->undo(); + QVERIFY(!doc->plainKateTextLine(0)->markedAsModified()); + QVERIFY(doc->plainKateTextLine(1)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(2)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(0)->markedAsSavedOnDisk()); + QVERIFY(!doc->plainKateTextLine(1)->markedAsSavedOnDisk()); + QVERIFY(!doc->plainKateTextLine(2)->markedAsSavedOnDisk()); + + doc->redo(); + QVERIFY(!doc->plainKateTextLine(0)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(1)->markedAsModified()); + QVERIFY(!doc->plainKateTextLine(0)->markedAsSavedOnDisk()); + QVERIFY(!doc->plainKateTextLine(1)->markedAsSavedOnDisk()); + + delete doc; +} + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/tests/modificationsystem_test.h b/kate/tests/modificationsystem_test.h new file mode 100644 index 00000000..d1f1d39c --- /dev/null +++ b/kate/tests/modificationsystem_test.h @@ -0,0 +1,56 @@ +/* This file is part of the KDE libraries + Copyright (C) 2011 Dominik Haumann + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KATE_MODIFICATION_SYSTEM_TEST_H +#define KATE_MODIFICATION_SYSTEM_TEST_H + +#include + +/** + * Test the complete Line Modification System. + * Covered classes: + * - KateModification* in part/undo/ + * - modification flags in Kate::TextLine in part/buffer/ + */ +class ModificationSystemTest : public QObject +{ + Q_OBJECT + + private Q_SLOTS: + void initTestCase(); + void cleanupTestCase(); + + void testInsertText(); + void testRemoveText(); + + void testInsertLine(); + void testRemoveLine(); + + void testWrapLineMid(); + void testWrapLineAtEnd(); + void testWrapLineAtStart(); + + void testUnWrapLine(); + void testUnWrapLine1Empty(); + void testUnWrapLine2Empty(); +}; + +#endif + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/tests/movingcursor_test.cpp b/kate/tests/movingcursor_test.cpp new file mode 100644 index 00000000..075d0847 --- /dev/null +++ b/kate/tests/movingcursor_test.cpp @@ -0,0 +1,348 @@ +/* This file is part of the KDE libraries + Copyright (C) 2010 Dominik Haumann + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "movingcursor_test.h" +#include "moc_movingcursor_test.cpp" + +#include + +#include +#include + +using namespace KTextEditor; + +QTEST_KDEMAIN(MovingCursorTest, GUI) + +QT_BEGIN_NAMESPACE +namespace QTest { + template<> + char *toString(const KTextEditor::Cursor &cursor) + { + QByteArray ba = "Cursor[" + QByteArray::number(cursor.line()) + + ", " + QByteArray::number(cursor.column()) + "]"; + return qstrdup(ba.data()); + } +} +QT_END_NAMESPACE + +MovingCursorTest::MovingCursorTest() + : QObject() +{ +} + +MovingCursorTest::~MovingCursorTest() +{ +} + +// tests: +// - MovingCursor with StayOnInsert +// - MovingCursor with MoveOnInsert +void MovingCursorTest::testMovingCursor() +{ + KateDocument doc (false, false, false); + MovingCursor* invalid = doc.newMovingCursor(Cursor::invalid()); + MovingCursor* moveOnInsert = doc.newMovingCursor(Cursor(0, 0), MovingCursor::MoveOnInsert); + MovingCursor* stayOnInsert = doc.newMovingCursor(Cursor(0, 0), MovingCursor::StayOnInsert); + + // verify initial conditions + QVERIFY(!invalid->isValid()); + QCOMPARE(moveOnInsert->toCursor(), Cursor(0, 0)); + QCOMPARE(stayOnInsert->toCursor(), Cursor(0, 0)); + + // insert some text + doc.insertText(Cursor(0, 0), "\n" + "1\n" + "22"); + + // check new cursor positions + QCOMPARE(moveOnInsert->toCursor(), Cursor(2, 2)); + QCOMPARE(stayOnInsert->toCursor(), Cursor(0, 0)); + + // set position to (1, 1) and insert text before cursor + stayOnInsert->setPosition(Cursor(1, 1)); + QCOMPARE(stayOnInsert->toCursor(), Cursor(1, 1)); + doc.insertText(Cursor(1, 0), "test"); + QCOMPARE(stayOnInsert->toCursor(), Cursor(1, 5)); + doc.undo(); + QCOMPARE(stayOnInsert->toCursor(), Cursor(1, 1)); + + // position still at (1, 1). insert text at cursor + doc.insertText(Cursor(1, 1), "test"); + QCOMPARE(stayOnInsert->toCursor(), Cursor(1, 1)); + doc.undo(); + QCOMPARE(stayOnInsert->toCursor(), Cursor(1, 1)); + + // + // same tests with the moveOnInsert cursor + // + // set position to (1, 1) and insert text before cursor + moveOnInsert->setPosition(Cursor(1, 1)); + QCOMPARE(moveOnInsert->toCursor(), Cursor(1, 1)); + doc.insertText(Cursor(1, 0), "test"); + QCOMPARE(moveOnInsert->toCursor(), Cursor(1, 5)); + doc.undo(); + QCOMPARE(moveOnInsert->toCursor(), Cursor(1, 1)); + + // position still at (1, 1). insert text at cursor + doc.insertText(Cursor(1, 1), "test"); + QCOMPARE(moveOnInsert->toCursor(), Cursor(1, 5)); + doc.undo(); + QCOMPARE(moveOnInsert->toCursor(), Cursor(1, 1)); + + // set both cursors to (2, 1) then delete text range that contains cursors + moveOnInsert->setPosition(Cursor(2, 1)); + stayOnInsert->setPosition(Cursor(2, 1)); + doc.removeText(Range(Cursor(2, 0), Cursor(2, 2))); + QCOMPARE(moveOnInsert->toCursor(), Cursor(2, 0)); + QCOMPARE(moveOnInsert->toCursor(), Cursor(2, 0)); +} + +// tests: +// - atStartOfDocument +// - atStartOfLine +// - atEndOfDocument +// - atEndOfLine +// - move forward with Wrap +// - move forward with NoWrap +// - move backward +// - gotoNextLine +// - gotoPreviousLine +void MovingCursorTest::testConvenienceApi() +{ + KateDocument doc (false, false, false); + doc.setText("\n" + "1\n" + "22\n" + "333\n" + "4444\n" + "55555"); + + // check start and end of document + MovingCursor *startOfDoc = doc.newMovingCursor(Cursor(0, 0)); + MovingCursor *endOfDoc = doc.newMovingCursor(Cursor(5, 5)); + QVERIFY(startOfDoc->atStartOfDocument()); + QVERIFY(startOfDoc->atStartOfLine()); + QVERIFY(endOfDoc->atEndOfDocument()); + QVERIFY(endOfDoc->atEndOfLine()); + + // set cursor to (2, 2) and then move to the left two times + MovingCursor *moving = doc.newMovingCursor(Cursor(2, 2)); + QVERIFY(moving->atEndOfLine()); // at 2, 2 + QVERIFY(moving->move(-1)); // at 2, 1 + QCOMPARE(moving->toCursor(), Cursor(2, 1)); + QVERIFY(!moving->atEndOfLine()); + QVERIFY(moving->move(-1)); // at 2, 0 + QCOMPARE(moving->toCursor(), Cursor(2, 0)); + QVERIFY(moving->atStartOfLine()); + + // now move again to the left, should wrap to (1, 1) + QVERIFY(moving->move(-1)); // at 1, 1 + QCOMPARE(moving->toCursor(), Cursor(1, 1)); + QVERIFY(moving->atEndOfLine()); + + // advance 7 characters to position (3, 3) + QVERIFY(moving->move(7)); // at 3, 3 + QCOMPARE(moving->toCursor(), Cursor(3, 3)); + + // advance 20 characters in NoWrap mode, then go back 10 characters + QVERIFY(moving->move(20, MovingCursor::NoWrap)); // at 3, 23 + QCOMPARE(moving->toCursor(), Cursor(3, 23)); + QVERIFY(moving->move(-10)); // at 3, 13 + QCOMPARE(moving->toCursor(), Cursor(3, 13)); + + // still at invalid text position. move one char to wrap around + QVERIFY(!moving->isValidTextPosition()); // at 3, 13 + QVERIFY(moving->move(1)); // at 4, 0 + QCOMPARE(moving->toCursor(), Cursor(4, 0)); + + // moving 11 characters in wrap mode moves to (5, 6), which is not a valid + // text position anymore. Hence, moving should be rejected. + QVERIFY(!moving->move(11)); + QVERIFY(moving->move(10)); + QVERIFY(moving->atEndOfDocument()); + + // try to move to next line, which fails. then go to previous line + QVERIFY(!moving->gotoNextLine()); + QVERIFY(moving->gotoPreviousLine()); + QCOMPARE(moving->toCursor(), Cursor(4, 0)); +} + +void MovingCursorTest::testOperators() +{ + KateDocument doc (false, false, false); + doc.setText("--oo--\n" + "--oo--\n" + "--oo--"); + + // create lots of cursors for comparison + Cursor invalid = Cursor::invalid(); + Cursor c02(0, 2); + Cursor c04(0, 4); + Cursor c14(1, 4); + + MovingCursor* m02 = doc.newMovingCursor(Cursor(0, 2)); + MovingCursor* m04 = doc.newMovingCursor(Cursor(0, 4)); + MovingCursor* m14 = doc.newMovingCursor(Cursor(1, 4)); + + // invalid comparison + QVERIFY(invalid == invalid); + QVERIFY(invalid <= c02); + QVERIFY(invalid < c02); + QVERIFY(!(invalid > c02)); + QVERIFY(!(invalid >= c02)); + + QVERIFY(!(invalid == *m02)); + QVERIFY(invalid <= *m02); + QVERIFY(invalid < *m02); + QVERIFY(!(invalid > *m02)); + QVERIFY(!(invalid >= *m02)); + + QVERIFY(!(*m02 == invalid)); + QVERIFY(!(*m02 <= invalid)); + QVERIFY(!(*m02 < invalid)); + QVERIFY(*m02 > invalid); + QVERIFY(*m02 >= invalid); + + // MovingCursor <-> MovingCursor + QVERIFY(*m02 == *m02); + QVERIFY(*m02 <= *m02); + QVERIFY(*m02 >= *m02); + QVERIFY(!(*m02 < *m02)); + QVERIFY(!(*m02 > *m02)); + QVERIFY(!(*m02 != *m02)); + + QVERIFY(!(*m02 == *m04)); + QVERIFY(*m02 <= *m04); + QVERIFY(!(*m02 >= *m04)); + QVERIFY(*m02 < *m04); + QVERIFY(!(*m02 > *m04)); + QVERIFY(*m02 != *m04); + + QVERIFY(!(*m04 == *m02)); + QVERIFY(!(*m04 <= *m02)); + QVERIFY(*m04 >= *m02); + QVERIFY(!(*m04 < *m02)); + QVERIFY(*m04 > *m02); + QVERIFY(*m04 != *m02); + + QVERIFY(!(*m02 == *m14)); + QVERIFY(*m02 <= *m14); + QVERIFY(!(*m02 >= *m14)); + QVERIFY(*m02 < *m14); + QVERIFY(!(*m02 > *m14)); + QVERIFY(*m02 != *m14); + + QVERIFY(!(*m14 == *m02)); + QVERIFY(!(*m14 <= *m02)); + QVERIFY(*m14 >= *m02); + QVERIFY(!(*m14 < *m02)); + QVERIFY(*m14 > *m02); + QVERIFY(*m14 != *m02); + + // MovingCursor <-> Cursor + QVERIFY(*m02 == c02); + QVERIFY(*m02 <= c02); + QVERIFY(*m02 >= c02); + QVERIFY(!(*m02 < c02)); + QVERIFY(!(*m02 > c02)); + QVERIFY(!(*m02 != c02)); + + QVERIFY(!(*m02 == c04)); + QVERIFY(*m02 <= c04); + QVERIFY(!(*m02 >= c04)); + QVERIFY(*m02 < c04); + QVERIFY(!(*m02 > c04)); + QVERIFY(*m02 != c04); + + QVERIFY(!(*m04 == c02)); + QVERIFY(!(*m04 <= c02)); + QVERIFY(*m04 >= c02); + QVERIFY(!(*m04 < c02)); + QVERIFY(*m04 > c02); + QVERIFY(*m04 != c02); + + QVERIFY(!(*m02 == c14)); + QVERIFY(*m02 <= c14); + QVERIFY(!(*m02 >= c14)); + QVERIFY(*m02 < c14); + QVERIFY(!(*m02 > c14)); + QVERIFY(*m02 != c14); + + QVERIFY(!(*m14 == c02)); + QVERIFY(!(*m14 <= c02)); + QVERIFY(*m14 >= c02); + QVERIFY(!(*m14 < c02)); + QVERIFY(*m14 > c02); + QVERIFY(*m14 != c02); + + // Cursor <-> MovingCursor + QVERIFY(c02 == *m02); + QVERIFY(c02 <= *m02); + QVERIFY(c02 >= *m02); + QVERIFY(!(c02 < *m02)); + QVERIFY(!(c02 > *m02)); + QVERIFY(!(c02 != *m02)); + + QVERIFY(!(c02 == *m04)); + QVERIFY(c02 <= *m04); + QVERIFY(!(c02 >= *m04)); + QVERIFY(c02 < *m04); + QVERIFY(!(c02 > *m04)); + QVERIFY(c02 != *m04); + + QVERIFY(!(c04 == *m02)); + QVERIFY(!(c04 <= *m02)); + QVERIFY(c04 >= *m02); + QVERIFY(!(c04 < *m02)); + QVERIFY(c04 > *m02); + QVERIFY(c04 != *m02); + + QVERIFY(!(c02 == *m14)); + QVERIFY(c02 <= *m14); + QVERIFY(!(c02 >= *m14)); + QVERIFY(c02 < *m14); + QVERIFY(!(c02 > *m14)); + QVERIFY(c02 != *m14); + + QVERIFY(!(c14 == *m02)); + QVERIFY(!(c14 <= *m02)); + QVERIFY(c14 >= *m02); + QVERIFY(!(c14 < *m02)); + QVERIFY(c14 > *m02); + QVERIFY(c14 != *m02); +} + +void MovingCursorTest::testInvalidMovingCursor() +{ + KateDocument* doc = new KateDocument(false, false, false); + + // add invalid MovingCursor. Inserts c into KateBuffer::m_invalidCursors + MovingCursor* c = doc->newMovingCursor(Cursor(-1, -1)); + QVERIFY(Cursor(-1, -1) == *c); + + c->setPosition(Cursor(0, 0)); + QVERIFY(Cursor(0, 0) == *c); + + // now c should be removed from KateBuffer::m_invalidCursors + delete c; + + // crash in bug https://bugs.kde.org/show_bug.cgi?id=248926 + // if it crashes: c is still in KateBuffer::m_invalidCursors -> double deletion + delete doc; +} diff --git a/kate/tests/movingcursor_test.h b/kate/tests/movingcursor_test.h new file mode 100644 index 00000000..953c31b8 --- /dev/null +++ b/kate/tests/movingcursor_test.h @@ -0,0 +1,40 @@ +/* This file is part of the KDE libraries + Copyright (C) 2010 Dominik Haumann + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KATE_MOVINGCURSOR_TEST_H +#define KATE_MOVINGCURSOR_TEST_H + +#include + +class MovingCursorTest : public QObject +{ + Q_OBJECT + +public: + MovingCursorTest(); + ~MovingCursorTest(); + +private Q_SLOTS: + void testMovingCursor(); + void testConvenienceApi(); + void testOperators(); + void testInvalidMovingCursor(); +}; + +#endif // KATE_MOVINGCURSOR_TEST_H diff --git a/kate/tests/movingrange_test.cpp b/kate/tests/movingrange_test.cpp new file mode 100644 index 00000000..b69a6348 --- /dev/null +++ b/kate/tests/movingrange_test.cpp @@ -0,0 +1,426 @@ +/* This file is part of the KDE libraries + Copyright (C) 2010 Dominik Haumann + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "movingrange_test.h" +#include "moc_movingrange_test.cpp" + +#include +#include + +#include +#include +#include +#include + +using namespace KTextEditor; + +QTEST_KDEMAIN(MovingRangeTest, GUI) + +QT_BEGIN_NAMESPACE +namespace QTest { + template<> + char *toString(const KTextEditor::Cursor &cursor) + { + QByteArray ba = "Cursor[" + QByteArray::number(cursor.line()) + + ", " + QByteArray::number(cursor.column()) + "]"; + return qstrdup(ba.data()); + } +} +QT_END_NAMESPACE + +class RangeFeedback : public MovingRangeFeedback +{ + public: + RangeFeedback() : MovingRangeFeedback() { reset(); } + + virtual void rangeEmpty (MovingRange* /*range*/) { + m_rangeEmptyCalled = true; + } + + virtual void rangeInvalid (MovingRange* /*range*/) { + m_rangeInvalidCalled = true; + } + + virtual void mouseEnteredRange (MovingRange* /*range*/, View* /*view*/) { + m_mouseEnteredRangeCalled = true; + } + + virtual void mouseExitedRange (MovingRange* /*range*/, View* /*view*/) { + m_mouseExitedRangeCalled = true; + } + + virtual void caretEnteredRange (MovingRange* /*range*/, View* /*view*/) { + m_caretEnteredRangeCalled = true; + } + + virtual void caretExitedRange (MovingRange* /*range*/, View* /*view*/) { + m_caretExitedRangeCalled = true; + } + + // + // Test functions to reset feedback watcher + // + public: + void reset() { + m_rangeEmptyCalled = false; + m_rangeInvalidCalled = false; + m_mouseEnteredRangeCalled = false; + m_mouseExitedRangeCalled = false; + m_caretEnteredRangeCalled = false; + m_caretExitedRangeCalled = false; + } + + void verifyReset() { + QVERIFY(!m_rangeEmptyCalled); + QVERIFY(!m_rangeInvalidCalled); + QVERIFY(!m_mouseEnteredRangeCalled); + QVERIFY(!m_mouseExitedRangeCalled); + QVERIFY(!m_caretEnteredRangeCalled); + QVERIFY(!m_caretExitedRangeCalled); + } + + bool rangeEmptyCalled() const { return m_rangeEmptyCalled; } + bool rangeInvalidCalled() const { return m_rangeInvalidCalled; } + bool mouseEnteredRangeCalled() const { return m_mouseEnteredRangeCalled; } + bool mouseExitedRangeCalled() const { return m_mouseExitedRangeCalled; } + bool caretEnteredRangeCalled() const { return m_caretEnteredRangeCalled; } + bool caretExitedRangeCalled() const { return m_caretExitedRangeCalled; } + + private: + bool m_rangeEmptyCalled; + bool m_rangeInvalidCalled; + bool m_mouseEnteredRangeCalled; + bool m_mouseExitedRangeCalled; + bool m_caretEnteredRangeCalled; + bool m_caretExitedRangeCalled; +}; + + +MovingRangeTest::MovingRangeTest() + : QObject() +{ +} + +MovingRangeTest::~MovingRangeTest() +{ +} + +// tests: +// - RangeFeedback::rangeEmpty +void MovingRangeTest::testFeedbackEmptyRange() +{ + KateDocument doc (false, false, false); + // the range created below will span the 'x' characters + QString text("..xxxx\n" + "xxxx.."); + doc.setText(text); + + // create range feedback + RangeFeedback rf; + + // allow empty + MovingRange* range = doc.newMovingRange(Range(Cursor(0, 2), Cursor(1, 4)), + KTextEditor::MovingRange::DoNotExpand, + KTextEditor::MovingRange::AllowEmpty); + range->setFeedback(&rf); + rf.verifyReset(); + + // remove exact range + doc.removeText(range->toRange()); + QVERIFY(rf.rangeEmptyCalled()); + QVERIFY(!rf.rangeInvalidCalled()); + QVERIFY(!rf.mouseEnteredRangeCalled()); + QVERIFY(!rf.mouseExitedRangeCalled()); + QVERIFY(!rf.caretEnteredRangeCalled()); + QVERIFY(!rf.caretExitedRangeCalled()); + + // clear document: should call rangeInvalid + rf.reset(); + rf.verifyReset(); + doc.clear(); + QVERIFY(rf.rangeInvalidCalled()); + QVERIFY(!rf.rangeEmptyCalled()); + QVERIFY(!rf.mouseEnteredRangeCalled()); + QVERIFY(!rf.mouseExitedRangeCalled()); + QVERIFY(!rf.caretEnteredRangeCalled()); + QVERIFY(!rf.caretExitedRangeCalled()); + + // setText: should behave just like clear document: call rangeInvalid again + doc.setText(text); + range->setRange(Range(Cursor(0, 2), Cursor(1, 4))); + rf.reset(); + rf.verifyReset(); + doc.setText("--yyyy\nyyyy--"); + QVERIFY(rf.rangeInvalidCalled()); + QVERIFY(!rf.rangeEmptyCalled()); + QVERIFY(!rf.mouseEnteredRangeCalled()); + QVERIFY(!rf.mouseExitedRangeCalled()); + QVERIFY(!rf.caretEnteredRangeCalled()); + QVERIFY(!rf.caretExitedRangeCalled()); + + // now remove entire document range. In this case, emptyRange should be called + // instead of rangeInvalid + doc.setText(text); + range->setRange(Range(Cursor(0, 2), Cursor(1, 4))); + rf.reset(); + rf.verifyReset(); + doc.removeText(doc.documentRange()); + QVERIFY(rf.rangeEmptyCalled()); + QVERIFY(!rf.rangeInvalidCalled()); + QVERIFY(!rf.mouseEnteredRangeCalled()); + QVERIFY(!rf.mouseExitedRangeCalled()); + QVERIFY(!rf.caretEnteredRangeCalled()); + QVERIFY(!rf.caretExitedRangeCalled()); +} + +// tests: +// - RangeFeedback::rangeInvalid +void MovingRangeTest::testFeedbackInvalidRange() +{ + KateDocument doc (false, false, false); + // the range created below will span the 'x' characters + QString text("..xxxx\n" + "xxxx.."); + doc.setText(text); + + // create range feedback + RangeFeedback rf; + + // allow empty + MovingRange* range = doc.newMovingRange(Range(Cursor(0, 2), Cursor(1, 4)), + KTextEditor::MovingRange::DoNotExpand, + KTextEditor::MovingRange::InvalidateIfEmpty); + range->setFeedback(&rf); + rf.verifyReset(); + + // remove exact range + doc.removeText(range->toRange()); + QVERIFY(!rf.rangeEmptyCalled()); + QVERIFY(rf.rangeInvalidCalled()); + QVERIFY(!rf.mouseEnteredRangeCalled()); + QVERIFY(!rf.mouseExitedRangeCalled()); + QVERIFY(!rf.caretEnteredRangeCalled()); + QVERIFY(!rf.caretExitedRangeCalled()); + + // clear document: should call rangeInvalid again + doc.setText(text); + range->setRange(Range(Cursor(0, 2), Cursor(1, 4))); + rf.reset(); + rf.verifyReset(); + doc.clear(); + QVERIFY(rf.rangeInvalidCalled()); + QVERIFY(!rf.rangeEmptyCalled()); + QVERIFY(!rf.mouseEnteredRangeCalled()); + QVERIFY(!rf.mouseExitedRangeCalled()); + QVERIFY(!rf.caretEnteredRangeCalled()); + QVERIFY(!rf.caretExitedRangeCalled()); + + // setText: should behave just like clear document: call rangeInvalid again + doc.setText(text); + range->setRange(Range(Cursor(0, 2), Cursor(1, 4))); + rf.reset(); + rf.verifyReset(); + doc.setText("--yyyy\nyyyy--"); + QVERIFY(rf.rangeInvalidCalled()); + QVERIFY(!rf.rangeEmptyCalled()); + QVERIFY(!rf.mouseEnteredRangeCalled()); + QVERIFY(!rf.mouseExitedRangeCalled()); + QVERIFY(!rf.caretEnteredRangeCalled()); + QVERIFY(!rf.caretExitedRangeCalled()); + + // now remove entire document range. Call rangeInvalid again + doc.setText(text); + range->setRange(Range(Cursor(0, 2), Cursor(1, 4))); + rf.reset(); + rf.verifyReset(); + doc.removeText(doc.documentRange()); + QVERIFY(rf.rangeInvalidCalled()); + QVERIFY(!rf.rangeEmptyCalled()); + QVERIFY(!rf.mouseEnteredRangeCalled()); + QVERIFY(!rf.mouseExitedRangeCalled()); + QVERIFY(!rf.caretEnteredRangeCalled()); + QVERIFY(!rf.caretExitedRangeCalled()); +} + +// tests: +// - RangeFeedback::caretEnteredRange +// - RangeFeedback::caretExitedRange +void MovingRangeTest::testFeedbackCaret() +{ + KateDocument doc (false, false, false); + // the range created below will span the 'x' characters + QString text("..xxxx\n" + "xxxx.."); + doc.setText(text); + + KateView* view = static_cast(doc.createView(0)); + + // create range feedback + RangeFeedback rf; + + // first test: with ExpandLeft | ExpandRight + { + view->setCursorPosition(Cursor(1, 6)); + + MovingRange* range = doc.newMovingRange(Range(Cursor(0, 2), Cursor(1, 4)), + KTextEditor::MovingRange::ExpandLeft | + KTextEditor::MovingRange::ExpandRight, + KTextEditor::MovingRange::InvalidateIfEmpty); + rf.reset(); + range->setFeedback(&rf); + rf.verifyReset(); + + // left + view->cursorLeft(); + QCOMPARE(view->cursorPosition(), Cursor(1, 5)); + QVERIFY(!rf.caretEnteredRangeCalled()); + QVERIFY(!rf.caretExitedRangeCalled()); + + view->cursorLeft(); + QCOMPARE(view->cursorPosition(), Cursor(1, 4)); + QVERIFY(rf.caretEnteredRangeCalled()); // ExpandRight: include cursor already now + QVERIFY(!rf.caretExitedRangeCalled()); + + rf.reset(); + view->cursorLeft(); + QCOMPARE(view->cursorPosition(), Cursor(1, 3)); + QVERIFY(!rf.caretEnteredRangeCalled()); + QVERIFY(!rf.caretExitedRangeCalled()); + + rf.reset(); + view->up(); + QCOMPARE(view->cursorPosition(), Cursor(0, 3)); + QVERIFY(!rf.caretEnteredRangeCalled()); + QVERIFY(!rf.caretExitedRangeCalled()); + + rf.reset(); + view->cursorLeft(); + QCOMPARE(view->cursorPosition(), Cursor(0, 2)); + QVERIFY(!rf.caretEnteredRangeCalled()); + QVERIFY(!rf.caretExitedRangeCalled()); + + rf.reset(); + view->cursorLeft(); + QCOMPARE(view->cursorPosition(), Cursor(0, 1)); // ExpandLeft: now we left it, not before + QVERIFY(!rf.caretEnteredRangeCalled()); + QVERIFY(rf.caretExitedRangeCalled()); + + delete range; + } + + + // second test: with DoNotExpand + { + view->setCursorPosition(Cursor(1, 6)); + + MovingRange* range = doc.newMovingRange(Range(Cursor(0, 2), Cursor(1, 4)), + KTextEditor::MovingRange::DoNotExpand, + KTextEditor::MovingRange::InvalidateIfEmpty); + rf.reset(); + range->setFeedback(&rf); + rf.verifyReset(); + + // left + view->cursorLeft(); + QCOMPARE(view->cursorPosition(), Cursor(1, 5)); + QVERIFY(!rf.caretEnteredRangeCalled()); + QVERIFY(!rf.caretExitedRangeCalled()); + + view->cursorLeft(); + QCOMPARE(view->cursorPosition(), Cursor(1, 4)); + QVERIFY(!rf.caretEnteredRangeCalled()); // DoNotExpand: does not include cursor + QVERIFY(!rf.caretExitedRangeCalled()); + + rf.reset(); + view->cursorLeft(); + QCOMPARE(view->cursorPosition(), Cursor(1, 3)); + QVERIFY(rf.caretEnteredRangeCalled()); + QVERIFY(!rf.caretExitedRangeCalled()); + + rf.reset(); + view->up(); + QCOMPARE(view->cursorPosition(), Cursor(0, 3)); + QVERIFY(!rf.caretEnteredRangeCalled()); + QVERIFY(!rf.caretExitedRangeCalled()); + + rf.reset(); + view->cursorLeft(); + QCOMPARE(view->cursorPosition(), Cursor(0, 2)); + QVERIFY(!rf.caretEnteredRangeCalled()); + QVERIFY(rf.caretExitedRangeCalled()); // DoNotExpand: that's why we leave already now + + rf.reset(); + view->cursorLeft(); + QCOMPARE(view->cursorPosition(), Cursor(0, 1)); + QVERIFY(!rf.caretEnteredRangeCalled()); + QVERIFY(!rf.caretExitedRangeCalled()); + + delete range; + } +} + +// tests: +// - RangeFeedback::mouseEnteredRange +// - RangeFeedback::mouseExitedRange +void MovingRangeTest::testFeedbackMouse() +{ + KateDocument doc (false, false, false); + // the range created below will span the 'x' characters + QString text("..xxxx\n" + "xxxx.."); + doc.setText(text); + + KateView* view = static_cast(doc.createView(0)); + view->setCursorPosition(Cursor(1, 6)); + view->show(); + view->resize(200, 100); + + // create range feedback + RangeFeedback rf; + QVERIFY(!rf.mouseEnteredRangeCalled()); + QVERIFY(!rf.mouseExitedRangeCalled()); + + // allow empty + MovingRange* range = doc.newMovingRange(Range(Cursor(0, 2), Cursor(1, 4)), + KTextEditor::MovingRange::ExpandLeft | + KTextEditor::MovingRange::ExpandRight, + KTextEditor::MovingRange::InvalidateIfEmpty); + range->setFeedback(&rf); + rf.verifyReset(); + + // left (nothing) + QTest::mouseMove(view, view->cursorToCoordinate(Cursor(0, 0)) + QPoint(0, 5)); + QTest::qWait(200); // process mouse events. do not move mouse manually + QVERIFY(!rf.mouseEnteredRangeCalled()); + QVERIFY(!rf.mouseExitedRangeCalled()); + + // middle (enter) + rf.reset(); + QTest::mouseMove(view, view->cursorToCoordinate(Cursor(0, 3)) + QPoint(0, 5)); + QTest::qWait(200); // process mouse events. do not move mouse manually + QVERIFY(rf.mouseEnteredRangeCalled()); + QVERIFY(!rf.mouseExitedRangeCalled()); + + // right (exit) + rf.reset(); + QTest::mouseMove(view, view->cursorToCoordinate(Cursor(1, 6)) + QPoint(10, 5)); + QTest::qWait(200); // process mouse events. do not move mouse manually + QVERIFY(!rf.mouseEnteredRangeCalled()); + QVERIFY(rf.mouseExitedRangeCalled()); +} diff --git a/kate/tests/movingrange_test.h b/kate/tests/movingrange_test.h new file mode 100644 index 00000000..3b9eade1 --- /dev/null +++ b/kate/tests/movingrange_test.h @@ -0,0 +1,40 @@ +/* This file is part of the KDE libraries + Copyright (C) 2010 Dominik Haumann + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KATE_MOVINGRANGE_TEST_H +#define KATE_MOVINGRANGE_TEST_H + +#include + +class MovingRangeTest : public QObject +{ + Q_OBJECT + +public: + MovingRangeTest(); + ~MovingRangeTest(); + +private Q_SLOTS: + void testFeedbackEmptyRange(); + void testFeedbackInvalidRange(); + void testFeedbackCaret(); + void testFeedbackMouse(); +}; + +#endif // KATE_MOVINGRANGE_TEST_H diff --git a/kate/tests/plaintextsearch_test.cpp b/kate/tests/plaintextsearch_test.cpp new file mode 100644 index 00000000..0ee7851b --- /dev/null +++ b/kate/tests/plaintextsearch_test.cpp @@ -0,0 +1,183 @@ +/* This file is part of the KDE libraries + Copyright (C) 2010 Bernhard Beschow + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "plaintextsearch_test.h" +#include "moc_plaintextsearch_test.cpp" + +#include + +#include +#include + +Q_DECLARE_METATYPE(KTextEditor::Range) + +QTEST_KDEMAIN(PlainTextSearchTest, GUI) + +QT_BEGIN_NAMESPACE +namespace QTest { + template<> + char *toString(const KTextEditor::Range &range) + { + QByteArray ba = "Range["; + ba += QByteArray::number(range.start().line()) + ", " + QByteArray::number(range.start().column()) + ", "; + ba += QByteArray::number(range.end().line()) + ", " + QByteArray::number(range.end().column()); + ba += "]"; + return qstrdup(ba.data()); + } +} +QT_END_NAMESPACE + +QtMsgHandler PlainTextSearchTest::s_msgHandler = 0; + +void myMessageOutput(QtMsgType type, const char *msg) +{ + switch (type) { + case QtDebugMsg: + /* do nothing */ + break; + default: + PlainTextSearchTest::s_msgHandler(type, msg); + } +} + +void PlainTextSearchTest::initTestCase() +{ + s_msgHandler = qInstallMsgHandler(myMessageOutput); +} + +void PlainTextSearchTest::cleanupTestCase() +{ + qInstallMsgHandler(0); +} + +PlainTextSearchTest::PlainTextSearchTest() + : QObject() + , m_doc(0) + , m_search(0) +{ +} + +PlainTextSearchTest::~PlainTextSearchTest() +{ +} + +void PlainTextSearchTest::init() +{ + m_doc = new KateDocument(false, false, false, 0, this); + m_search = new KatePlainTextSearch(m_doc, Qt::CaseSensitive, false); +} + +void PlainTextSearchTest::cleanup() +{ + delete m_search; + delete m_doc; +} + +void PlainTextSearchTest::testSearchBackward_data() +{ + QTest::addColumn("searchRange"); + QTest::addColumn("expectedResult"); + + QTest::newRow("") << KTextEditor::Range(0, 0, 1, 10) << KTextEditor::Range(1, 6, 1, 10); + QTest::newRow("") << KTextEditor::Range(0, 0, 1, 5) << KTextEditor::Range(1, 0, 1, 4); + QTest::newRow("") << KTextEditor::Range(0, 0, 1, 0) << KTextEditor::Range(0, 10, 0, 14); +} + +void PlainTextSearchTest::testSearchBackward() +{ + QFETCH(KTextEditor::Range, searchRange); + QFETCH(KTextEditor::Range, expectedResult); + + m_doc->setText("aaaa aaaa aaaa\n" + "aaaa aaaa"); + + QCOMPARE(m_search->search("aaaa", searchRange, true), expectedResult); +} + +void PlainTextSearchTest::testSingleLineDocument_data() +{ + QTest::addColumn("searchRange"); + QTest::addColumn("forwardResult"); + QTest::addColumn("backwardResult"); + + QTest::newRow("[a a a a a a a a a a a a]") << KTextEditor::Range(0, 0, 0, 23) << KTextEditor::Range(0, 0, 0, 5) << KTextEditor::Range(0, 18, 0, 23); + QTest::newRow("[a a a a a a a a a a a ]a") << KTextEditor::Range(0, 0, 0, 22) << KTextEditor::Range(0, 0, 0, 5) << KTextEditor::Range(0, 16, 0, 21); + QTest::newRow("a[ a a a a a a a a a a a]") << KTextEditor::Range(0, 1, 0, 23) << KTextEditor::Range(0, 2, 0, 7) << KTextEditor::Range(0, 18, 0, 23); + QTest::newRow("a[ a a a a a a a a a a ]a") << KTextEditor::Range(0, 1, 0, 22) << KTextEditor::Range(0, 2, 0, 7) << KTextEditor::Range(0, 16, 0, 21); + QTest::newRow("[a a a a] a a a a a a a a") << KTextEditor::Range(0, 0, 0, 7) << KTextEditor::Range(0, 0, 0, 5) << KTextEditor::Range(0, 2, 0, 7); + QTest::newRow("[a a a ]a a a a a a a a a") << KTextEditor::Range(0, 0, 0, 6) << KTextEditor::Range(0, 0, 0, 5) << KTextEditor::Range(0, 0, 0, 5); + QTest::newRow("[a a a] a a a a a a a a a") << KTextEditor::Range(0, 0, 0, 5) << KTextEditor::Range(0, 0, 0, 5) << KTextEditor::Range(0, 0, 0, 5); + QTest::newRow("[a a ]a a a a a a a a a a") << KTextEditor::Range(0, 0, 0, 4) << KTextEditor::Range::invalid() << KTextEditor::Range::invalid(); + QTest::newRow("a a a a a a a a [a a a a]") << KTextEditor::Range(0, 16, 0, 23) << KTextEditor::Range(0, 16, 0, 21) << KTextEditor::Range(0, 18, 0, 23); + QTest::newRow("a a a a a a a a a[ a a a]") << KTextEditor::Range(0, 17, 0, 23) << KTextEditor::Range(0, 18, 0, 23) << KTextEditor::Range(0, 18, 0, 23); + QTest::newRow("a a a a a a a a a [a a a]") << KTextEditor::Range(0, 18, 0, 23) << KTextEditor::Range(0, 18, 0, 23) << KTextEditor::Range(0, 18, 0, 23); + QTest::newRow("a a a a a a a a a a[ a a]") << KTextEditor::Range(0, 19, 0, 23) << KTextEditor::Range::invalid() << KTextEditor::Range::invalid(); + QTest::newRow("a a a a a[ a a a a] a a a") << KTextEditor::Range(0, 9, 0, 17) << KTextEditor::Range(0, 10, 0, 15) << KTextEditor::Range(0, 12, 0, 17); + QTest::newRow("a a a a a[ a a] a a a a a") << KTextEditor::Range(0, 9, 0, 13) << KTextEditor::Range::invalid() << KTextEditor::Range::invalid(); +} + +void PlainTextSearchTest::testSingleLineDocument() +{ + QFETCH(KTextEditor::Range, searchRange); + QFETCH(KTextEditor::Range, forwardResult); + QFETCH(KTextEditor::Range, backwardResult); + + m_doc->setText("a a a a a a a a a a a a"); + + QCOMPARE(m_search->search("a a a", searchRange, false), forwardResult); + QCOMPARE(m_search->search("a a a", searchRange, true), backwardResult); +} + +void PlainTextSearchTest::testMultilineSearch_data() +{ + QTest::addColumn("pattern"); + QTest::addColumn("inputRange"); + QTest::addColumn("forwardResult"); + + QTest::newRow("") << "a a a\na a\na a a" << KTextEditor::Range(0, 0, 2, 5) << KTextEditor::Range(0, 0, 2, 5); + QTest::newRow("") << "a a a\na a\na a " << KTextEditor::Range(0, 0, 2, 5) << KTextEditor::Range(0, 0, 2, 4); + QTest::newRow("") << "a a a\na a\na a" << KTextEditor::Range(0, 0, 2, 5) << KTextEditor::Range(0, 0, 2, 3); + QTest::newRow("") << "a a a\na a\na" << KTextEditor::Range(0, 0, 2, 5) << KTextEditor::Range(0, 0, 2, 1); + QTest::newRow("") << "a a a\na a\n" << KTextEditor::Range(0, 0, 2, 5) << KTextEditor::Range(0, 0, 2, 0); + QTest::newRow("") << "a a a\na a" << KTextEditor::Range(0, 0, 2, 5) << KTextEditor::Range(0, 0, 1, 3); + QTest::newRow("") << "a a\na a" << KTextEditor::Range(0, 0, 2, 5) << KTextEditor::Range(0, 2, 1, 3); + QTest::newRow("") << "a a\na a\na a" << KTextEditor::Range(0, 0, 2, 5) << KTextEditor::Range(0, 2, 2, 3); + QTest::newRow("") << "\na a\na a" << KTextEditor::Range(0, 0, 2, 5) << KTextEditor::Range(0, 5, 2, 3); + QTest::newRow("") << "\na a\n" << KTextEditor::Range(0, 0, 2, 5) << KTextEditor::Range(0, 5, 2, 0); + + QTest::newRow("") << "a a a\na a\na a a" << KTextEditor::Range(0, 0, 2, 4) << KTextEditor::Range::invalid(); + QTest::newRow("") << "a a a\na a\na a " << KTextEditor::Range(0, 0, 2, 4) << KTextEditor::Range(0, 0, 2, 4); + QTest::newRow("") << "a a a\na a\n" << KTextEditor::Range(0, 0, 2, 0) << KTextEditor::Range(0, 0, 2, 0); + QTest::newRow("") << "a a a\na a\n" << KTextEditor::Range(0, 0, 1, 3) << KTextEditor::Range::invalid(); + QTest::newRow("") << "a a\n" << KTextEditor::Range(0, 0, 1, 3) << KTextEditor::Range(0, 2, 1, 0); + QTest::newRow("") << "a \n" << KTextEditor::Range(0, 0, 1, 3) << KTextEditor::Range::invalid(); +} + +void PlainTextSearchTest::testMultilineSearch() +{ + QFETCH(QString, pattern); + QFETCH(KTextEditor::Range, inputRange); + QFETCH(KTextEditor::Range, forwardResult); + + m_doc->setText("a a a\n" + "a a\n" + "a a a"); + + QCOMPARE(m_search->search(pattern, inputRange, false), forwardResult); +} diff --git a/kate/tests/plaintextsearch_test.h b/kate/tests/plaintextsearch_test.h new file mode 100644 index 00000000..7b39ee55 --- /dev/null +++ b/kate/tests/plaintextsearch_test.h @@ -0,0 +1,60 @@ +/* This file is part of the KDE libraries + Copyright (C) 2010 Bernhard Beschow + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KATE_PLAINTEXTSEARCH_TEST_H +#define KATE_PLAINTEXTSEARCH_TEST_H + +#include + +class KateDocument; +class KatePlainTextSearch; + +class PlainTextSearchTest : public QObject +{ + Q_OBJECT + + public: + PlainTextSearchTest(); + virtual ~PlainTextSearchTest(); + + private Q_SLOTS: + void initTestCase(); + void cleanupTestCase(); + + void init(); + void cleanup(); + + void testSearchBackward_data(); + void testSearchBackward(); + + void testSingleLineDocument_data(); + void testSingleLineDocument(); + + void testMultilineSearch_data(); + void testMultilineSearch(); + + private: + KateDocument *m_doc; + KatePlainTextSearch *m_search; + + public: + static QtMsgHandler s_msgHandler; +}; + +#endif diff --git a/kate/tests/range_test.cpp b/kate/tests/range_test.cpp new file mode 100644 index 00000000..c80762a0 --- /dev/null +++ b/kate/tests/range_test.cpp @@ -0,0 +1,153 @@ +/* This file is part of the KDE libraries + Copyright (C) 2010 Christoph Cullmann + Copyright (C) 2005 Hamish Rodda + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "range_test.h" +#include "moc_range_test.cpp" + +#include + +#include +#include +#include +#include +#include + +QTEST_KDEMAIN(RangeTest, GUI) + +QT_BEGIN_NAMESPACE +namespace QTest { + template<> + char *toString(const KTextEditor::Range &range) + { + QByteArray ba = "Range["; + ba += QByteArray::number(range.start().line()) + ", " + QByteArray::number(range.start().column()) + ", "; + ba += QByteArray::number(range.end().line()) + ", " + QByteArray::number(range.end().column()); + ba += "]"; + return qstrdup(ba.data()); + } +} +QT_END_NAMESPACE + +#define testNewRow() (QTest::newRow(QString("line %1").arg(__LINE__).toAscii().data())) + +RangeTest::RangeTest() + : QObject() +{ +} + +RangeTest::~RangeTest() +{ +} + +void RangeTest::rangeCheck ( KTextEditor::Range & valid ) +{ + QVERIFY(valid.isValid() && valid.start() <= valid.end()); + + KTextEditor::Cursor before(0,1), start(0,2), end(1,4), after(1,10); + + KTextEditor::Range result(start, end); + QVERIFY(valid.isValid() && valid.start() <= valid.end()); + + valid.setRange(start, end); + QVERIFY(valid.isValid() && valid.start() <= valid.end()); + QCOMPARE(valid, result); + + valid.setRange(end, start); + QVERIFY(valid.isValid() && valid.start() <= valid.end()); + QCOMPARE(valid, result); + + valid.start() = after; + QVERIFY(valid.isValid() && valid.start() <= valid.end()); + QCOMPARE(valid, KTextEditor::Range(after, after)); + + valid = result; + QCOMPARE(valid, result); + + valid.end() = before; + QVERIFY(valid.isValid() && valid.start() <= valid.end()); + QCOMPARE(valid, KTextEditor::Range(before, before)); +} + +void RangeTest::testTextEditorRange() +{ + // test simple range + KTextEditor::Range range; + rangeCheck (range); +} + +void RangeTest::testTextRange() +{ + // test text range + KateDocument doc (false, false, false); + KTextEditor::MovingRange *complexRange = doc.newMovingRange (KTextEditor::Range()); + KTextEditor::Range range = *complexRange; + rangeCheck (range); + delete complexRange; +} + +void RangeTest::testInsertText() +{ + KateDocument doc (false, false, false); + + // Multi-line insert + KTextEditor::MovingCursor* cursor1 = doc.newMovingCursor(KTextEditor::Cursor(), KTextEditor::MovingCursor::StayOnInsert); + KTextEditor::MovingCursor* cursor2 = doc.newMovingCursor(KTextEditor::Cursor(), KTextEditor::MovingCursor::MoveOnInsert); + + doc.insertText(KTextEditor::Cursor(), "Test Text\nMore Test Text"); + QCOMPARE(doc.documentEnd(), KTextEditor::Cursor(1,14)); + + QString text = doc.text(KTextEditor::Range(1,0,1,14)); + QCOMPARE(text, QString("More Test Text")); + + // Check cursors and ranges have moved properly + QCOMPARE(cursor1->toCursor(), KTextEditor::Cursor(0,0)); + QCOMPARE(cursor2->toCursor(), KTextEditor::Cursor(1,14)); + + KTextEditor::Cursor cursor3 = doc.endOfLine(1); + + // Set up a few more lines + doc.insertText(*cursor2, "\nEven More Test Text"); + QCOMPARE(doc.documentEnd(), KTextEditor::Cursor(2,19)); + QCOMPARE(cursor3, doc.endOfLine(1)); +} + +void RangeTest::testCornerCaseInsertion() +{ + KateDocument doc (false, false, false); + + // lock first revision + doc.lockRevision (0); + + KTextEditor::MovingRange* rangeEdit = doc.newMovingRange(KTextEditor::Range(0,0,0,0)); + QCOMPARE(rangeEdit->toRange(), KTextEditor::Range(0,0,0,0)); + + doc.insertText(KTextEditor::Cursor(0,0), "\n"); + QCOMPARE(rangeEdit->toRange(), KTextEditor::Range(1,0,1,0)); + + // test translate + KTextEditor::Range translateTest (0,0,0,0); + doc.transformRange (translateTest, KTextEditor::MovingRange::DoNotExpand, KTextEditor::MovingRange::AllowEmpty, 0); + QCOMPARE(translateTest, KTextEditor::Range(1,0,1,0)); + + // test translate reverse + KTextEditor::Range reverseTranslateTest (1,0,1,0); + doc.transformRange (reverseTranslateTest, KTextEditor::MovingRange::DoNotExpand, KTextEditor::MovingRange::AllowEmpty, -1, 0); + QCOMPARE(reverseTranslateTest, KTextEditor::Range(0,0,0,0)); +} diff --git a/kate/tests/range_test.h b/kate/tests/range_test.h new file mode 100644 index 00000000..abdf1e94 --- /dev/null +++ b/kate/tests/range_test.h @@ -0,0 +1,46 @@ +/* This file is part of the KDE libraries + Copyright (C) 2010 Christoph Cullmann + Copyright (C) 2005 Hamish Rodda + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KATE_RANGE_TEST_H +#define KATE_RANGE_TEST_H + +#include + +#include + +class RangeTest : public QObject +{ + Q_OBJECT + +public: + RangeTest(); + ~RangeTest(); + +private Q_SLOTS: + void testTextEditorRange(); + void testTextRange(); + void testInsertText(); + void testCornerCaseInsertion(); + +private: + void rangeCheck ( KTextEditor::Range & valid ); +}; + +#endif diff --git a/kate/tests/regexpsearch_test.cpp b/kate/tests/regexpsearch_test.cpp new file mode 100644 index 00000000..50bf5d1f --- /dev/null +++ b/kate/tests/regexpsearch_test.cpp @@ -0,0 +1,301 @@ +/* This file is part of the KDE libraries + Copyright (C) 2010 Bernhard Beschow + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "regexpsearch_test.h" +#include "moc_regexpsearch_test.cpp" + +#include + +#include +#include + +Q_DECLARE_METATYPE(KTextEditor::Range) + +QTEST_KDEMAIN(RegExpSearchTest, GUI) + +QT_BEGIN_NAMESPACE +namespace QTest { + template<> + char *toString(const KTextEditor::Range &range) + { + QByteArray ba = "Range["; + ba += QByteArray::number(range.start().line()) + ", " + QByteArray::number(range.start().column()) + ", "; + ba += QByteArray::number(range.end().line()) + ", " + QByteArray::number(range.end().column()); + ba += "]"; + return qstrdup(ba.data()); + } +} +QT_END_NAMESPACE + +#define testNewRow() (QTest::newRow(QString("line %1").arg(__LINE__).toAscii().data())) + +using namespace KTextEditor; + +RegExpSearchTest::RegExpSearchTest() + : QObject() +{ +} + +RegExpSearchTest::~RegExpSearchTest() +{ +} + +void RegExpSearchTest::testReplaceEscapeSequences_data() +{ + QTest::addColumn("pattern"); + QTest::addColumn("expected"); + + testNewRow() << "\\" << "\\"; + testNewRow() << "\\0" << "0"; + testNewRow() << "\\00" << "00"; + testNewRow() << "\\000" << "000"; + testNewRow() << "\\0000" << QString(QChar(0)); + testNewRow() << "\\0377" << QString(QChar(0377)); + testNewRow() << "\\0378" << "0378"; + testNewRow() << "\\a" << "\a"; + testNewRow() << "\\f" << "\f"; + testNewRow() << "\\n" << "\n"; + testNewRow() << "\\r" << "\r"; + testNewRow() << "\\t" << "\t"; + testNewRow() << "\\v" << "\v"; + testNewRow() << "\\x" << "x"; + testNewRow() << "\\x0" << "x0"; + testNewRow() << "\\x00" << "x00"; + testNewRow() << "\\x000" << "x000"; + testNewRow() << "\\x0000" << QString(QChar(0x0000)); + testNewRow() << "\\x00000" << QString(QChar(0x0000) + '0'); + testNewRow() << "\\xaaaa" << QString(QChar(0xaaaa)); + testNewRow() << "\\xFFFF" << QString(QChar(0xFFFF)); + testNewRow() << "\\xFFFg" << "xFFFg"; +} + +void RegExpSearchTest::testReplaceEscapeSequences() +{ + QFETCH(QString, pattern); + QFETCH(QString, expected); + + const QString result1 = KateRegExpSearch::escapePlaintext(pattern); + const QString result2 = KateRegExpSearch::buildReplacement(pattern, QStringList(), 0); + + QCOMPARE(result1, expected); + QCOMPARE(result2, expected); +} + +void RegExpSearchTest::testReplacementReferences_data() +{ + QTest::addColumn("pattern"); + QTest::addColumn("expected"); + QTest::addColumn("capturedTexts"); + + testNewRow() << "\\0" << "b" << (QStringList() << "b"); + testNewRow() << "\\00" << "b0" << (QStringList() << "b"); + testNewRow() << "\\000" << "b00" << (QStringList() << "b"); + testNewRow() << "\\0000" << QString(QChar(0)) << (QStringList() << "b"); + testNewRow() << "\\1" << "1" << (QStringList() << "b"); + testNewRow() << "\\0" << "b" << (QStringList() << "b" << "c"); + testNewRow() << "\\1" << "c" << (QStringList() << "b" << "c"); +} + +void RegExpSearchTest::testReplacementReferences() +{ + QFETCH(QString, pattern); + QFETCH(QString, expected); + QFETCH(QStringList, capturedTexts); + + const QString result = KateRegExpSearch::buildReplacement(pattern, capturedTexts, 1); + + QCOMPARE(result, expected); +} + +void RegExpSearchTest::testReplacementCaseConversion_data() +{ + QTest::addColumn("pattern"); + QTest::addColumn("expected"); + + testNewRow() << "a\\Uaa" << "aAA"; + testNewRow() << "a\\UAa" << "aAA"; + testNewRow() << "a\\UaA" << "aAA"; + + testNewRow() << "a\\uaa" << "aAa"; + testNewRow() << "a\\uAa" << "aAa"; + testNewRow() << "a\\uaA" << "aAA"; + + testNewRow() << "A\\LAA" << "Aaa"; + testNewRow() << "A\\LaA" << "Aaa"; + testNewRow() << "A\\LAa" << "Aaa"; + + testNewRow() << "A\\lAA" << "AaA"; + testNewRow() << "A\\lAa" << "Aaa"; + testNewRow() << "A\\laA" << "AaA"; + + testNewRow() << "a\\EaA" << "aaA"; + testNewRow() << "A\\EAa" << "AAa"; +} + +void RegExpSearchTest::testReplacementCaseConversion() +{ + QFETCH(QString, pattern); + QFETCH(QString, expected); + + const QString result = KateRegExpSearch::buildReplacement(pattern, QStringList(), 1); + + QCOMPARE(result, expected); +} + +void RegExpSearchTest::testReplacementCounter_data() +{ + QTest::addColumn("pattern"); + QTest::addColumn("counter"); + QTest::addColumn("expected"); + + testNewRow() << "a\\#b" << 1 << "a1b"; + testNewRow() << "a\\#b" << 10 << "a10b"; + testNewRow() << "a\\#####b" << 1 << "a00001b"; +} + +void RegExpSearchTest::testReplacementCounter() +{ + QFETCH(QString, pattern); + QFETCH(int, counter); + QFETCH(QString, expected); + + const QString result = KateRegExpSearch::buildReplacement(pattern, QStringList(), counter); + + QCOMPARE(result, expected); +} + +void RegExpSearchTest::testAnchoredRegexp_data() +{ + QTest::addColumn("pattern"); + QTest::addColumn("inputRange"); + QTest::addColumn("backwards"); + QTest::addColumn("expected"); + + testNewRow() << "fe" << Range(0, 0, 0, 8) << false << Range(0, 0, 0, 2); + testNewRow() << "fe" << Range(0, 0, 0, 8) << true << Range(0, 6, 0, 8); + + testNewRow() << "^fe" << Range(0, 0, 0, 8) << false << Range(0, 0, 0, 2); + testNewRow() << "^fe" << Range(0, 0, 0, 1) << false << Range::invalid(); + testNewRow() << "^fe" << Range(0, 0, 0, 2) << false << Range(0, 0, 0, 2); + testNewRow() << "^fe" << Range(0, 3, 0, 8) << false << Range::invalid(); // only match at line start + testNewRow() << "^fe" << Range(0, 0, 0, 2) << true << Range(0, 0, 0, 2); + testNewRow() << "^fe" << Range(0, 0, 0, 1) << true << Range::invalid(); + testNewRow() << "^fe" << Range(0, 0, 0, 2) << true << Range(0, 0, 0, 2); + testNewRow() << "^fe" << Range(0, 3, 0, 8) << true << Range::invalid(); + + testNewRow() << "fe$" << Range(0, 0, 0, 8) << false << Range(0, 6, 0, 8); + testNewRow() << "fe$" << Range(0, 7, 0, 8) << false << Range::invalid(); + testNewRow() << "fe$" << Range(0, 6, 0, 8) << false << Range(0, 6, 0, 8); +// testNewRow() << "fe$" << Range(0, 0, 0, 5) << false << Range::invalid(); // only match at line end, fails + testNewRow() << "fe$" << Range(0, 0, 0, 8) << true << Range(0, 6, 0, 8); + testNewRow() << "fe$" << Range(0, 7, 0, 8) << true << Range::invalid(); + testNewRow() << "fe$" << Range(0, 6, 0, 8) << true << Range(0, 6, 0, 8); +// testNewRow() << "fe$" << Range(0, 0, 0, 5) << true << Range::invalid(); // fails due to $-shortcoming in QRegExp + + testNewRow() << "^fe fe fe$" << Range(0, 0, 0, 8) << false << Range(0, 0, 0, 8); + testNewRow() << "^fe fe fe$" << Range(0, 3, 0, 8) << false << Range::invalid(); + testNewRow() << "^fe fe fe$" << Range(0, 0, 0, 5) << false << Range::invalid(); + testNewRow() << "^fe fe fe$" << Range(0, 3, 0, 5) << false << Range::invalid(); + testNewRow() << "^fe fe fe$" << Range(0, 0, 0, 8) << true << Range(0, 0, 0, 8); + testNewRow() << "^fe fe fe$" << Range(0, 3, 0, 8) << true << Range::invalid(); + testNewRow() << "^fe fe fe$" << Range(0, 0, 0, 5) << true << Range::invalid(); + testNewRow() << "^fe fe fe$" << Range(0, 3, 0, 5) << true << Range::invalid(); + + testNewRow() << "^fe( fe)*$" << Range(0, 0, 0, 8) << false << Range(0, 0, 0, 8); + testNewRow() << "^fe( fe)*" << Range(0, 0, 0, 8) << false << Range(0, 0, 0, 8); + testNewRow() << "fe( fe)*$" << Range(0, 0, 0, 8) << false << Range(0, 0, 0, 8); + testNewRow() << "fe( fe)*" << Range(0, 0, 0, 8) << false << Range(0, 0, 0, 8); + testNewRow() << "^fe( fe)*$" << Range(0, 3, 0, 8) << false << Range::invalid(); + testNewRow() << "fe( fe)*$" << Range(0, 3, 0, 8) << false << Range(0, 3, 0, 8); +// testNewRow() << "^fe( fe)*$" << Range(0, 0, 0, 5) << false << Range::invalid(); // fails due to $-shortcoming in QRegExp + testNewRow() << "^fe( fe)*" << Range(0, 0, 0, 5) << false << Range(0, 0, 0, 5); + testNewRow() << "^fe( fe)*$" << Range(0, 0, 0, 8) << true << Range(0, 0, 0, 8); + testNewRow() << "^fe( fe)*" << Range(0, 0, 0, 8) << true << Range(0, 0, 0, 8); +// testNewRow() << "fe( fe)*$" << Range(0, 0, 0, 8) << true << Range(0, 0, 0, 8); // fails, shouldn't matching be greedy? +// testNewRow() << "fe( fe)*" << Range(0, 0, 0, 8) << true << Range(0, 0, 0, 8); // fails, shouldn't matching be greedy? + testNewRow() << "^fe( fe)*$" << Range(0, 3, 0, 8) << true << Range::invalid(); +// testNewRow() << "fe( fe)*$" << Range(0, 3, 0, 8) << true << Range(0, 3, 0, 8); // fails, shouldn't matching be greedy? +// testNewRow() << "^fe( fe)*$" << Range(0, 0, 0, 5) << true << Range::invalid(); // fails due to $-shortcoming in QRegExp + + testNewRow() << "^fe|fe$" << Range(0, 0, 0, 5) << false << Range(0, 0, 0, 2); + testNewRow() << "^fe|fe$" << Range(0, 3, 0, 8) << false << Range(0, 6, 0, 8); +// testNewRow() << "^fe|fe$" << Range(0, 0, 0, 5) << true << Range(0, 0, 0, 2); // fails due to $-shortcoming in QRegExp + testNewRow() << "^fe|fe$" << Range(0, 3, 0, 8) << true << Range(0, 6, 0, 8); +} + +void RegExpSearchTest::testAnchoredRegexp() +{ + QFETCH(QString, pattern); + QFETCH(Range, inputRange); + QFETCH(bool, backwards); + QFETCH(Range, expected); + + KateDocument doc(false, false, false); + doc.setText("fe fe fe"); + + KateRegExpSearch searcher(&doc, Qt::CaseInsensitive); + + static int i = 0; + if (i == 34 || i == 36) + qDebug() << i; + i++; + + const Range result = searcher.search(pattern, inputRange, backwards)[0]; + + QCOMPARE(result, expected); +} + +void RegExpSearchTest::testSearchForward() +{ + KateDocument doc(false, false, false); + doc.setText(" \\piinfercong"); + + KateRegExpSearch search(&doc, Qt::CaseSensitive); + const Range result = search.search("\\\\piinfer(\\w)", Range(0, 2, 0, 15))[0]; + + QCOMPARE(result, Range(0, 2, 0, 11)); +} + +void RegExpSearchTest::testSearchBackwardInSelection() +{ + KateDocument doc(false, false, false); + doc.setText("foobar foo bar foo bar foo"); + + KateRegExpSearch search(&doc, Qt::CaseSensitive); + const Range result = search.search("foo", Range(0, 0, 0, 15), true)[0]; + + QCOMPARE(result, Range(0, 7, 0, 10)); +} + +void RegExpSearchTest::test() +{ + KateDocument doc(false, false, false); + doc.setText("\\newcommand{\\piReductionOut}"); + + KateRegExpSearch search(&doc, Qt::CaseSensitive); + const QVector result = search.search("\\\\piReduction(\\S)", Range(0, 10, 0, 28), true); + + QCOMPARE(result.size(), 2); + QCOMPARE(result[0], Range(0, 12, 0, 25)); + QCOMPARE(result[1], Range(0, 24, 0, 25)); + QCOMPARE(doc.text(result[0]), QString("\\piReductionO")); + QCOMPARE(doc.text(result[1]), QString("O")); +} + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/tests/regexpsearch_test.h b/kate/tests/regexpsearch_test.h new file mode 100644 index 00000000..967da4ac --- /dev/null +++ b/kate/tests/regexpsearch_test.h @@ -0,0 +1,58 @@ +/* This file is part of the KDE libraries + Copyright (C) 2010 Bernhard Beschow + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KATE_REGEXPSEARCH_TEST_H +#define KATE_REGEXPSEARCH_TEST_H + +#include + +class RegExpSearchTest : public QObject +{ + Q_OBJECT + + public: + RegExpSearchTest(); + virtual ~RegExpSearchTest(); + + private Q_SLOTS: + void testReplaceEscapeSequences_data(); + void testReplaceEscapeSequences(); + + void testReplacementReferences_data(); + void testReplacementReferences(); + + void testReplacementCaseConversion_data(); + void testReplacementCaseConversion(); + + void testReplacementCounter_data(); + void testReplacementCounter(); + + void testAnchoredRegexp_data(); + void testAnchoredRegexp(); + + void testSearchForward(); + + void testSearchBackwardInSelection(); + + void test(); +}; + +#endif + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/tests/revision_test.cpp b/kate/tests/revision_test.cpp new file mode 100644 index 00000000..325d7208 --- /dev/null +++ b/kate/tests/revision_test.cpp @@ -0,0 +1,181 @@ +/* This file is part of the KDE libraries + Copyright (C) 2010 Dominik Haumann + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "revision_test.h" +#include "moc_revision_test.cpp" + +#include + +#include +#include +#include +#include + +using namespace KTextEditor; + +QTEST_KDEMAIN(RevisionTest, GUI) + +QT_BEGIN_NAMESPACE +namespace QTest { + template<> + char *toString(const KTextEditor::Cursor &cursor) + { + QByteArray ba = "Cursor[" + QByteArray::number(cursor.line()) + + ", " + QByteArray::number(cursor.column()) + "]"; + return qstrdup(ba.data()); + } + + template<> + char *toString(const KTextEditor::Range &range) + { + QByteArray ba = "Range[" + QByteArray::number(range.start().line()) + + ", " + QByteArray::number(range.start().column()) + " - " + + QByteArray::number(range.end().line()) + + ", " + QByteArray::number(range.end().column()) + "]"; + return qstrdup(ba.data()); + } +} +QT_END_NAMESPACE + +RevisionTest::RevisionTest() + : QObject() +{ +} + +RevisionTest::~RevisionTest() +{ +} + +// tests: MovingInterface +// - lockRevision() +// - revision() +// - unlockRevision() +// - transformCursor() +void RevisionTest::testTransformCursor() +{ + KateDocument doc (false, false, false); + + // initial saved revision of unsaved document is -1 + QVERIFY(doc.lastSavedRevision() == -1); + + // initial revision is always 0 + QCOMPARE(doc.revision(), (qint64) 0); + + // one edit action -> revision now 1, last saved still -1 + doc.insertText(Cursor(0, 0), "0000"); + + qint64 rev = doc.revision(); + QCOMPARE(rev, (qint64) 1); + + // now lock current revision 1 + doc.lockRevision(rev); + + // wrapLine + insertText + wrapLine + insertText + doc.insertText(Cursor(0, 2), "\n1111\n2222"); + + // create some cursors, then transform them + Cursor c01(0, 1); + Cursor stayOnInsert(0, 2); + Cursor moveOnInsert(0, 2); + + doc.transformCursor(c01, MovingCursor::MoveOnInsert, rev, -1); + doc.transformCursor(moveOnInsert, MovingCursor::MoveOnInsert, rev, -1); + doc.transformCursor(stayOnInsert, MovingCursor::StayOnInsert, rev, -1); + + QCOMPARE(c01, Cursor(0, 1)); + QCOMPARE(stayOnInsert, Cursor(0, 2)); + QCOMPARE(moveOnInsert, Cursor(2, 4)); + + + // free revision and lock current again + doc.unlockRevision(rev); + rev = doc.revision(); + doc.lockRevision(rev); + + // now undo, the cursors should move to original positions again + doc.undo(); + + // inverse transformation + doc.transformCursor(c01, MovingCursor::MoveOnInsert, rev, -1); + doc.transformCursor(moveOnInsert, MovingCursor::MoveOnInsert, rev, -1); + doc.transformCursor(stayOnInsert, MovingCursor::StayOnInsert, rev, -1); + + QCOMPARE(c01, Cursor(0, 1)); + QCOMPARE(stayOnInsert, Cursor(0, 2)); + QCOMPARE(moveOnInsert, Cursor(0, 2)); +} + + +// tests: +// - transformRange() +void RevisionTest::testTransformRange() +{ + KateDocument doc (false, false, false); + + QCOMPARE(doc.revision(), (qint64) 0); + + doc.setText("00\n" + "11"); + + // now lock current revision + qint64 rev = doc.revision(); + doc.lockRevision(rev); + + + Range r1(Cursor(0, 0), Cursor(1, 2)); + Range r2(Cursor(0, 1), Cursor(1, 1)); + Range invalidOnEmpty(Cursor(0, 1), Cursor(1, 1)); + + // remove text + doc.removeText(Range(Cursor(0, 0), Cursor(1, 2))); + + doc.transformRange(r1, MovingRange::ExpandLeft | MovingRange::ExpandRight, + MovingRange::AllowEmpty, rev, -1); + doc.transformRange(r2, MovingRange::ExpandLeft | MovingRange::ExpandRight, + MovingRange::AllowEmpty, rev, -1); + doc.transformRange(invalidOnEmpty, MovingRange::ExpandLeft | MovingRange::ExpandRight, + MovingRange::InvalidateIfEmpty, rev, -1); + + + QCOMPARE(r1, Range(Cursor(0, 0), Cursor(0, 0))); + QCOMPARE(r2, Range(Cursor(0, 0), Cursor(0, 0))); + QCOMPARE(invalidOnEmpty, Range::invalid()); + + // free revision and lock current again + doc.unlockRevision(rev); + rev = doc.revision(); + doc.lockRevision(rev); + + // now undo + doc.undo(); + + // r1 should span the entire document + // r2 should be empty at end of document + // invalidOnEmpty should stay invalid + doc.transformRange(r1, MovingRange::ExpandLeft | MovingRange::ExpandRight, + MovingRange::AllowEmpty, rev, -1); + doc.transformRange(r2, MovingRange::ExpandRight, + MovingRange::AllowEmpty, rev, -1); + doc.transformRange(invalidOnEmpty, MovingRange::ExpandLeft | MovingRange::ExpandRight, + MovingRange::AllowEmpty, rev, -1); + + QCOMPARE(r1, Range(Cursor(0, 0), Cursor(1, 2))); + QCOMPARE(r2, Range(Cursor(1, 2), Cursor(1, 2))); + QCOMPARE(invalidOnEmpty, Range::invalid()); +} diff --git a/kate/tests/revision_test.h b/kate/tests/revision_test.h new file mode 100644 index 00000000..f7869180 --- /dev/null +++ b/kate/tests/revision_test.h @@ -0,0 +1,38 @@ +/* This file is part of the KDE libraries + Copyright (C) 2010 Dominik Haumann + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KATE_REVISION_TEST_H +#define KATE_REVISION_TEST_H + +#include + +class RevisionTest : public QObject +{ + Q_OBJECT + +public: + RevisionTest(); + ~RevisionTest(); + +private Q_SLOTS: + void testTransformCursor(); + void testTransformRange(); +}; + +#endif // KATE_REVISION_TEST_H diff --git a/kate/tests/searchbar_test.cpp b/kate/tests/searchbar_test.cpp new file mode 100644 index 00000000..b1c7771c --- /dev/null +++ b/kate/tests/searchbar_test.cpp @@ -0,0 +1,652 @@ +/* This file is part of the KDE libraries + Copyright (C) 2010 Bernhard Beschow + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "searchbar_test.h" +#include "moc_searchbar_test.cpp" + +#include "ui_searchbarincremental.h" +#include "ui_searchbarpower.h" + +#include + +#include +#include +#include +#include +#include + +QTEST_KDEMAIN(SearchBarTest, GUI) + +QT_BEGIN_NAMESPACE +namespace QTest { + template<> + char *toString(const KTextEditor::Range &range) + { + QByteArray ba = "Range["; + ba += QByteArray::number(range.start().line()) + ", " + QByteArray::number(range.start().column()) + ", "; + ba += QByteArray::number(range.end().line()) + ", " + QByteArray::number(range.end().column()); + ba += "]"; + return qstrdup(ba.data()); + } +} +QT_END_NAMESPACE + +#define testNewRow() (QTest::newRow(QString("line %1").arg(__LINE__).toAscii().data())) + + +using namespace KTextEditor; + + +SearchBarTest::SearchBarTest() + : QObject() +{ +} + +SearchBarTest::~SearchBarTest() +{ +} + + +void SearchBarTest::testFindNextIncremental() +{ + KateDocument doc(false, false, false); + doc.setText("a a a b b"); + + KateView view(&doc, 0); + KateViewConfig config(&view); + + KateSearchBar bar(false, &view, &config); + bar.enableUnitTestMode(); + + bar.setSearchPattern("b"); + + QCOMPARE(view.selectionRange(), Range(0, 6, 0, 7)); + + bar.findNext(); + + QCOMPARE(view.selectionRange(), Range(0, 8, 0, 9)); + + bar.setSearchPattern("a"); + + QCOMPARE(view.selectionRange(), Range(0, 0, 0, 1)); + + bar.findNext(); + + QCOMPARE(view.selectionRange(), Range(0, 2, 0, 3)); + + bar.findNext(); + + QCOMPARE(view.selectionRange(), Range(0, 4, 0, 5)); + + bar.findNext(); + + QCOMPARE(view.selectionRange(), Range(0, 0, 0, 1)); +} + + +void SearchBarTest::testSetMatchCaseIncremental() +{ + KateDocument doc(false, false, false); + KateView view(&doc, 0); + KateViewConfig config(&view); + + doc.setText("a A a"); + KateSearchBar bar(false, &view, &config); + bar.enableUnitTestMode(); + + QVERIFY(!bar.isPower()); + QVERIFY(!view.selection()); + + bar.setMatchCase(false); + bar.setSearchPattern("A"); + + QVERIFY(!bar.matchCase()); + QCOMPARE(view.selectionRange(), Range(0, 0, 0, 1)); + + bar.setMatchCase(true); + + QVERIFY(bar.matchCase()); + QCOMPARE(view.selectionRange(), Range(0, 2, 0, 3)); + + bar.setMatchCase(false); + + QVERIFY(!bar.matchCase()); + QCOMPARE(view.selectionRange(), Range(0, 0, 0, 1)); + + bar.setMatchCase(true); + + QVERIFY(bar.matchCase()); + QCOMPARE(view.selectionRange(), Range(0, 2, 0, 3)); +} + + +void SearchBarTest::testSetMatchCasePower() +{ + KateDocument doc(false, false, false); + KateView view(&doc, 0); + KateViewConfig config(&view); + + doc.setText("a A a"); + view.setCursorPosition(Cursor(0, 0)); + + KateSearchBar bar(true, &view, &config); + bar.enableUnitTestMode(); + + QVERIFY(bar.isPower()); + QVERIFY(!view.selection()); + + bar.setMatchCase(false); + bar.setSearchPattern("A"); + bar.findNext(); + + QCOMPARE(bar.searchPattern(), QString("A")); + QVERIFY(!bar.matchCase()); + QCOMPARE(view.selectionRange(), Range(0, 0, 0, 1)); + + bar.setMatchCase(true); + + QCOMPARE(view.selectionRange(), Range(0, 0, 0, 1)); + + bar.findNext(); + + QVERIFY(bar.matchCase()); + QCOMPARE(view.selectionRange(), Range(0, 2, 0, 3)); + + bar.setMatchCase(false); + + QVERIFY(!bar.matchCase()); + QCOMPARE(view.selectionRange(), Range(0, 2, 0, 3)); + + bar.findNext(); + + QCOMPARE(view.selectionRange(), Range(0, 4, 0, 5)); +} + + + +void SearchBarTest::testSetSelectionOnlyPower() +{ + KateDocument doc(false, false, false); + KateView view(&doc, 0); + KateViewConfig config(&view); + + doc.setText("a a a"); + KateSearchBar bar(true, &view, &config); + bar.enableUnitTestMode(); + + bar.setSearchPattern("a"); + + QVERIFY(bar.isPower()); + QVERIFY(!view.selection()); + + bar.setSelectionOnly(false); + bar.findNext(); + + QVERIFY(!bar.selectionOnly()); + QCOMPARE(view.selectionRange(), Range(0, 0, 0, 1)); + + view.setSelection(Range(0, 2, 0, 5)); + bar.setSelectionOnly(true); + + QVERIFY(bar.selectionOnly()); + + bar.findNext(); + + QCOMPARE(view.selectionRange(), Range(0, 2, 0, 3)); + QVERIFY(bar.selectionOnly()); + + bar.setSelectionOnly(false); + bar.findNext(); + + QCOMPARE(view.selectionRange(), Range(0, 4, 0, 5)); + QVERIFY(!bar.selectionOnly()); +} + + +void SearchBarTest::testSetSearchPattern_data() +{ + QTest::addColumn("power"); + QTest::addColumn("numMatches2"); + + testNewRow() << false << 0; + testNewRow() << true << 3; +} + + +void SearchBarTest::testSetSearchPattern() +{ + QFETCH(bool, power); + QFETCH(int, numMatches2); + + KateDocument doc(false, false, false); + KateView view(&doc, 0); + KateViewConfig config(&view); + + doc.setText("a a a"); + + KateSearchBar bar(power, &view, &config); + bar.enableUnitTestMode(); + + bar.setSearchPattern("a"); + bar.findAll(); + + QCOMPARE(bar.m_hlRanges.size(), 3); + + bar.setSearchPattern("a "); + + QCOMPARE(bar.m_hlRanges.size(), numMatches2); + + bar.findAll(); + + QCOMPARE(bar.m_hlRanges.size(), 2); +} + + +void SearchBarTest::testSetSelectionOnly() +{ + KateDocument doc(false, false, false); + KateView view(&doc, 0); + KateViewConfig config(&view); + + doc.setText("a a a"); + view.setSelection(Range(0, 0, 0, 3)); + + KateSearchBar bar(false, &view, &config); + bar.enableUnitTestMode(); + + bar.setSelectionOnly(false); + bar.setSearchPattern("a"); + bar.findAll(); + + QCOMPARE(bar.m_hlRanges.size(), 3); + + bar.setSelectionOnly(true); + + QCOMPARE(bar.m_hlRanges.size(), 3); +} + + +void SearchBarTest::testFindAll_data() +{ + QTest::addColumn("power"); + QTest::addColumn("numMatches2"); + QTest::addColumn("numMatches4"); + + testNewRow() << false << 0 << 0; + testNewRow() << true << 3 << 2; +} + + +void SearchBarTest::testFindAll() +{ + QFETCH(bool, power); + QFETCH(int, numMatches2); + QFETCH(int, numMatches4); + + KateDocument doc(false, false, false); + KateView view(&doc, 0); + KateViewConfig config(&view); + + doc.setText("a a a"); + KateSearchBar bar(power, &view, &config); + bar.enableUnitTestMode(); + + QCOMPARE(bar.isPower(), power); + + bar.setSearchPattern("a"); + bar.findAll(); + + QCOMPARE(bar.m_hlRanges.size(), 3); + QCOMPARE(bar.m_hlRanges.at(0)->toRange(), Range(0, 0, 0, 1)); + QCOMPARE(bar.m_hlRanges.at(1)->toRange(), Range(0, 2, 0, 3)); + QCOMPARE(bar.m_hlRanges.at(2)->toRange(), Range(0, 4, 0, 5)); + + bar.setSearchPattern("a "); + + QCOMPARE(bar.m_hlRanges.size(), numMatches2); + + bar.findAll(); + + QCOMPARE(bar.m_hlRanges.size(), 2); + + bar.setSearchPattern("a "); + + QCOMPARE(bar.m_hlRanges.size(), numMatches4); + + bar.findAll(); + + QCOMPARE(bar.m_hlRanges.size(), 0); +} + +void SearchBarTest::testReplaceAll() +{ + KateDocument doc(false, false, false); + KateView view(&doc, 0); + KateViewConfig config(&view); + + doc.setText("a a a"); + KateSearchBar bar(true, &view, &config); + bar.enableUnitTestMode(); + + bar.setSearchPattern("a"); + bar.setReplacementPattern(""); + bar.replaceAll(); + + QCOMPARE(bar.m_hlRanges.size(), 3); + QCOMPARE(bar.m_hlRanges.at(0)->toRange(), Range(0, 0, 0, 0)); + QCOMPARE(bar.m_hlRanges.at(1)->toRange(), Range(0, 1, 0, 1)); + QCOMPARE(bar.m_hlRanges.at(2)->toRange(), Range(0, 2, 0, 2)); + + bar.setSearchPattern(" "); + bar.setReplacementPattern("b"); + bar.replaceAll(); + + QCOMPARE(bar.m_hlRanges.size(), 2); + QCOMPARE(bar.m_hlRanges.at(0)->toRange(), Range(0, 0, 0, 1)); + QCOMPARE(bar.m_hlRanges.at(1)->toRange(), Range(0, 1, 0, 2)); +} + +void SearchBarTest::testFindSelectionForward_data() +{ + QTest::addColumn("text"); + QTest::addColumn("selectionOnly"); + QTest::addColumn("selectionRange"); + QTest::addColumn("match"); + + testNewRow() << "a a a" << false << Range(0, 0, 0, 1) << Range(0, 0, 0, 2); + testNewRow() << "a a a" << true << Range(0, 0, 0, 1) << Range(0, 0, 0, 1); + + testNewRow() << "a a a" << false << Range(0, 0, 0, 2) << Range(0, 2, 0, 4); + testNewRow() << "a a a" << true << Range(0, 0, 0, 2) << Range(0, 0, 0, 2); + + testNewRow() << "a a a" << false << Range(0, 0, 0, 3) << Range(0, 0, 0, 2); + testNewRow() << "a a a" << true << Range(0, 0, 0, 3) << Range(0, 0, 0, 2); + + testNewRow() << "a a a" << false << Range(0, 2, 0, 4) << Range(0, 0, 0, 2); + testNewRow() << "a a a" << true << Range(0, 2, 0, 4) << Range(0, 2, 0, 4); + + testNewRow() << "a a a" << false << Range(0, 3, 0, 4) << Range(0, 0, 0, 2); + testNewRow() << "a a a" << true << Range(0, 3, 0, 4) << Range(0, 3, 0, 4); +} + +void SearchBarTest::testFindSelectionForward() +{ + QFETCH(QString, text); + QFETCH(bool, selectionOnly); + QFETCH(Range, selectionRange); + QFETCH(Range, match); + + KateDocument doc(false, false, false); + KateView view(&doc, 0); + KateViewConfig config(&view); + + doc.setText(text); + + view.setSelection(Range(0, 0, 0, 2)); + + KateSearchBar bar(true, &view, &config); + bar.enableUnitTestMode(); + QVERIFY(bar.searchPattern() == QString("a ")); + + view.setSelection(selectionRange); + QCOMPARE(view.selectionRange(), selectionRange); + bar.setSelectionOnly(selectionOnly); + + bar.findNext(); + + QCOMPARE(view.selectionRange(), match); +} + +void SearchBarTest::testRemoveWithSelectionForward_data() +{ + QTest::addColumn("selectionRange"); + QTest::addColumn("match"); + + testNewRow() << Range(0, 0, 0, 1) << Range(0, 0, 0, 2); + testNewRow() << Range(0, 0, 0, 2) << Range(0, 0, 0, 2); + testNewRow() << Range(0, 0, 0, 3) << Range(0, 0, 0, 2); + testNewRow() << Range(0, 2, 0, 4) << Range(0, 0, 0, 2); + testNewRow() << Range(0, 3, 0, 4) << Range(0, 0, 0, 2); +} + +void SearchBarTest::testRemoveWithSelectionForward() +{ + QFETCH(Range, selectionRange); + QFETCH(Range, match); + + KateDocument doc(false, false, false); + KateView view(&doc, 0); + KateViewConfig config(&view); + + doc.setText("a a a"); + view.setSelection(selectionRange); + + KateSearchBar bar(true, &view, &config); + bar.enableUnitTestMode(); + bar.setSearchPattern("a "); + bar.setSelectionOnly(false); + + bar.replaceNext(); + + QCOMPARE(view.selectionRange(), match); +} + +void SearchBarTest::testRemoveInSelectionForward_data() +{ + QTest::addColumn("selectionRange"); + QTest::addColumn("match"); + + testNewRow() << Range(0, 0, 0, 1) << Range(0, 0, 0, 1); + testNewRow() << Range(0, 0, 0, 2) << Range(0, 0, 0, 0); + testNewRow() << Range(0, 0, 0, 3) << Range(0, 0, 0, 2); + testNewRow() << Range(0, 0, 0, 4) << Range(0, 0, 0, 2); + testNewRow() << Range(0, 2, 0, 4) << Range(0, 2, 0, 2); + testNewRow() << Range(0, 3, 0, 4) << Range(0, 3, 0, 4); +} + +void SearchBarTest::testRemoveInSelectionForward() +{ + QFETCH(Range, selectionRange); + QFETCH(Range, match); + + KateDocument doc(false, false, false); + KateView view(&doc, 0); + KateViewConfig config(&view); + + doc.setText("a a a"); + view.setSelection(selectionRange); + + KateSearchBar bar(true, &view, &config); + bar.enableUnitTestMode(); + bar.setSearchPattern("a "); + bar.setSelectionOnly(true); + + QVERIFY(bar.replacementPattern().isEmpty()); + + bar.replaceNext(); + + QCOMPARE(view.selectionRange(), match); +} + +void SearchBarTest::testReplaceWithDoubleSelecion_data() +{ + QTest::addColumn("text"); + QTest::addColumn("selectionRange"); + QTest::addColumn("result"); + QTest::addColumn("match"); + +// testNewRow() << "a" << Range(0, 0, 0, 1) << "aa" << Range(?, ?, ?, ?); + testNewRow() << "aa" << Range(0, 1, 0, 2) << "aaa" << Range(0, 0, 0, 1); + testNewRow() << "aa" << Range(0, 0, 0, 1) << "aaa" << Range(0, 2, 0, 3); + +// testNewRow() << "ab" << Range(0, 0, 0, 1) << "aab" << Range(?, ?, ?, ?); + testNewRow() << "aab" << Range(0, 0, 0, 1) << "aaab" << Range(0, 2, 0, 3); + testNewRow() << "aba" << Range(0, 0, 0, 1) << "aaba" << Range(0, 3, 0, 4); + +// testNewRow() << "ab" << Range(0, 0, 0, 2) << "abab" << Range(?, ?, ?, ?); + testNewRow() << "abab" << Range(0, 0, 0, 2) << "ababab" << Range(0, 4, 0, 6); + testNewRow() << "abab" << Range(0, 2, 0, 4) << "ababab" << Range(0, 0, 0, 2); +} + +void SearchBarTest::testReplaceWithDoubleSelecion() +{ + QFETCH(QString, text); + QFETCH(Range, selectionRange); + QFETCH(QString, result); + QFETCH(Range, match); + + KateDocument doc(false, false, false); + KateView view(&doc, 0); + KateViewConfig config(&view); + + doc.setText(text); + view.setSelection(selectionRange); + + KateSearchBar bar(true, &view, &config); + bar.enableUnitTestMode(); + + bar.setSelectionOnly(false); + bar.setReplacementPattern(bar.searchPattern() + bar.searchPattern()); + bar.replaceNext(); + + QCOMPARE(doc.text(), result); + QCOMPARE(view.selectionRange(), match); +} + +void SearchBarTest::testReplaceDollar() +{ + KateDocument doc(false, false, false); + KateView view(&doc, 0); + KateViewConfig config(&view); + + doc.setText("aaa\nbbb\nccc\n\n\naaa\nbbb\nccc\nddd\n"); + + KateSearchBar bar(true, &view, &config); + bar.enableUnitTestMode(); + + bar.setSearchPattern("$"); + bar.setSearchMode(KateSearchBar::MODE_REGEX); + bar.setReplacementPattern("D"); + + bar.replaceAll(); + + QCOMPARE(doc.text(), QString("aaaD\nbbbD\ncccD\nD\nD\naaaD\nbbbD\ncccD\ndddD\n")); +} + +void SearchBarTest::testSearchHistoryIncremental() +{ + KateDocument doc(false, false, false); + KateView view(&doc, 0); + KateViewConfig *const config = view.config(); + + doc.setText("foo bar"); + + QCOMPARE(config->patternHistoryModel()->stringList(), QStringList()); + + KateSearchBar bar(false, &view, config); + bar.enableUnitTestMode(); + + bar.setSearchPattern("foo"); + bar.findNext(); + + QCOMPARE(bar.m_incUi->pattern->findText("foo"), 0); + + bar.setSearchPattern("bar"); + bar.findNext(); + + QCOMPARE(bar.m_incUi->pattern->findText("bar"), 0); + QCOMPARE(bar.m_incUi->pattern->findText("foo"), 1); + + KateDocument doc2(false, false, false); + KateView view2(&doc2, 0); + KateViewConfig *const config2 = view2.config(); + KateSearchBar bar2(false, &view2, config2); + bar2.enableUnitTestMode(); + + QCOMPARE(bar2.m_incUi->pattern->findText("bar"), 0); + QCOMPARE(bar2.m_incUi->pattern->findText("foo"), 1); + + //testcase for https://bugs.kde.org/show_bug.cgi?id=248305 + bar2.m_incUi->pattern->setCurrentIndex(1); + QCOMPARE(bar2.searchPattern(), QLatin1String("foo")); + bar2.findNext(); + QCOMPARE(bar2.searchPattern(), QLatin1String("foo")); +} + +void SearchBarTest::testSearchHistoryPower() +{ + KateDocument doc(false, false, false); + KateView view(&doc, 0); + KateViewConfig *const config = view.config(); + + doc.setText("foo bar"); + + KateSearchBar bar(true, &view, config); + bar.enableUnitTestMode(); + + QCOMPARE(bar.m_powerUi->pattern->count(), 0); + + bar.setSearchPattern("foo"); + bar.findNext(); + + QCOMPARE(bar.m_powerUi->pattern->findText("foo"), 0); + + bar.findNext(); + + QCOMPARE(bar.m_powerUi->pattern->findText("foo"), 0); + QCOMPARE(bar.m_powerUi->pattern->count(), 1); + + bar.setSearchPattern("bar"); + bar.findNext(); + + QCOMPARE(bar.m_powerUi->pattern->findText("bar"), 0); + QCOMPARE(bar.m_powerUi->pattern->findText("foo"), 1); + QCOMPARE(bar.m_powerUi->pattern->count(), 2); + + KateDocument doc2(false, false, false); + KateView view2(&doc2, 0); + KateViewConfig *const config2 = view2.config(); + KateSearchBar bar2(true, &view2, config2); + bar2.enableUnitTestMode(); + + QCOMPARE(bar2.m_powerUi->pattern->findText("bar"), 0); + QCOMPARE(bar2.m_powerUi->pattern->findText("foo"), 1); +} + +// Make sure Kate doesn't replace anything outside selection in block mode (see bug 253191) +void SearchBarTest::testReplaceInBlockMode() { + KateDocument doc(false, false, false); + KateView view(&doc, 0); + KateViewConfig config(&view); + + doc.setText("111\n111"); + view.setBlockSelection(true); + view.setSelection(KTextEditor::Range(0, 1, 1, 2)); + + KateSearchBar bar(true, &view, &config); + bar.enableUnitTestMode(); + + bar.setSearchPattern("1"); + bar.setReplacementPattern("2"); + bar.replaceAll(); + + QCOMPARE(doc.text(), QString("121\n121")); +} + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/tests/searchbar_test.h b/kate/tests/searchbar_test.h new file mode 100644 index 00000000..cd30c7e6 --- /dev/null +++ b/kate/tests/searchbar_test.h @@ -0,0 +1,74 @@ +/* This file is part of the KDE libraries + Copyright (C) 2010 Bernhard Beschow + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KATE_SEARCHBAR_TEST_H +#define KATE_SEARCHBAR_TEST_H + +#include + +class SearchBarTest : public QObject +{ + Q_OBJECT + +public: + SearchBarTest(); + ~SearchBarTest(); + +private Q_SLOTS: + void testFindNextIncremental(); + + void testSetMatchCaseIncremental(); + + void testSetMatchCasePower(); + + void testSetSelectionOnlyPower(); + + void testSetSearchPattern_data(); + void testSetSearchPattern(); + + void testSetSelectionOnly(); + + void testFindAll_data(); + void testFindAll(); + + void testReplaceAll(); + + void testFindSelectionForward_data(); + void testFindSelectionForward(); + + void testRemoveWithSelectionForward_data(); + void testRemoveWithSelectionForward(); + + void testRemoveInSelectionForward_data(); + void testRemoveInSelectionForward(); + + void testReplaceWithDoubleSelecion_data(); + void testReplaceWithDoubleSelecion(); + + void testReplaceDollar(); + + void testSearchHistoryIncremental(); + void testSearchHistoryPower(); + + void testReplaceInBlockMode(); +}; + +#endif + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/tests/searchbarincremental.ui b/kate/tests/searchbarincremental.ui new file mode 120000 index 00000000..dca42c00 --- /dev/null +++ b/kate/tests/searchbarincremental.ui @@ -0,0 +1 @@ +../part/search/searchbarincremental.ui \ No newline at end of file diff --git a/kate/tests/searchbarpower.ui b/kate/tests/searchbarpower.ui new file mode 120000 index 00000000..fcefbad2 --- /dev/null +++ b/kate/tests/searchbarpower.ui @@ -0,0 +1 @@ +../part/search/searchbarpower.ui \ No newline at end of file diff --git a/kate/tests/test.uc b/kate/tests/test.uc new file mode 100644 index 00000000..be53bcce --- /dev/null +++ b/kate/tests/test.uc @@ -0,0 +1,266 @@ +//============================================================================= +// Shield Gun +//============================================================================= +class ShieldGun extends Weapon + config(user); + +#EXEC OBJ LOAD FILE=InterfaceContent.utx + +var Sound ShieldHitSound; +var String ShieldHitForce; + +replication +{ + reliable if (Role == ROLE_Authority) + ClientTakeHit; +} + +simulated function DoAutoSwitch() +{ +} + +simulated event RenderOverlays( Canvas Canvas ) +{ + local int m; + + if ((Hand < -1.0) || (Hand > 1.0)) + { + for (m = 0; m < NUM_FIRE_MODES; m++) + { + if (FireMode[m] != None) + { + FireMode[m].DrawMuzzleFlash(Canvas); + } + } + } + Super.RenderOverlays(Canvas); +} + +// AI Interface +function GiveTo(Pawn Other, optional Pickup Pickup) +{ + Super.GiveTo(Other, Pickup); + + if ( Bot(Other.Controller) != None ) + Bot(Other.Controller).bHasImpactHammer = true; +} + +function bool CanAttack(Actor Other) +{ + return true; +} + +simulated function Timer() +{ + local Bot B; + + if (ClientState == WS_BringUp) + { + // check if owner is bot waiting to do impact jump + B = Bot(Instigator.Controller); + if ( (B != None) && B.bPreparingMove && (B.ImpactTarget != None) ) + { + B.ImpactJump(); + B = None; + } + } + Super.Timer(); + if ( (B != None) && (B.Enemy != None) ) + BotFire(false); +} + +function FireHack(byte Mode) +{ + if ( Mode == 0 ) + { + FireMode[0].PlayFiring(); + FireMode[0].FlashMuzzleFlash(); + FireMode[0].StartMuzzleSmoke(); + IncrementFlashCount(0); + } +} + +/* BestMode() +choose between regular or alt-fire +*/ +function byte BestMode() +{ + local float EnemyDist; + local bot B; + + B = Bot(Instigator.Controller); + if ( (B == None) || (B.Enemy == None) ) + return 1; + + EnemyDist = VSize(B.Enemy.Location - Instigator.Location); + if ( EnemyDist > 2 * Instigator.GroundSpeed ) + return 1; + if ( (B.MoveTarget != B.Enemy) && ((EnemyDist > 0.5 * Instigator.GroundSpeed) + || (((B.Enemy.Location - Instigator.Location) Dot vector(Instigator.Rotation)) <= 0)) ) + return 1; + return 0; +} + +// super desireable for bot waiting to impact jump +function float GetAIRating() +{ + local Bot B; + local float EnemyDist; + + B = Bot(Instigator.Controller); + if ( B == None ) + return AIRating; + + if ( B.bPreparingMove && (B.ImpactTarget != None) ) + return 9; + + if ( B.PlayerReplicationInfo.HasFlag != None ) + { + if ( Instigator.Health < 50 ) + return AIRating + 0.35; + return AIRating + 0.25; + } + + if ( B.Enemy == None ) + return AIRating; + + EnemyDist = VSize(B.Enemy.Location - Instigator.Location); + if ( B.Stopped() && (EnemyDist > 100) ) + return 0.1; + + if ( (EnemyDist < 750) && (B.Skill <= 2) && !B.Enemy.IsA('Bot') && (ShieldGun(B.Enemy.Weapon) != None) ) + return FClamp(300/(EnemyDist + 1), 0.6, 0.75); + + if ( EnemyDist > 400 ) + return 0.1; + if ( (Instigator.Weapon != self) && (EnemyDist < 120) ) + return 0.25; + + return ( FMin(0.6, 90/(EnemyDist + 1)) ); +} + +// End AI interface + +function AdjustPlayerDamage( out int Damage, Pawn InstigatedBy, Vector HitLocation, + out Vector Momentum, class DamageType) +{ + local int Drain; + local vector Reflect; + local vector HitNormal; + local float DamageMax; + + DamageMax = 100.0; + if ( DamageType == class'Fell' ) + DamageMax = 20.0; + else if( !DamageType.default.bArmorStops || (DamageType == class'DamTypeShieldImpact' && InstigatedBy == Instigator) ) + return; + + if ( CheckReflect(HitLocation, HitNormal, 0) ) + { + Drain = Min( Ammo[1].AmmoAmount*2, Damage ); + Drain = Min(Drain,DamageMax); + Reflect = MirrorVectorByNormal( Normal(Location - HitLocation), Vector(Instigator.Rotation) ); + Damage -= Drain; + Momentum *= 1.25; + Ammo[1].UseAmmo(Drain/2); + DoReflectEffect(Drain/2); + } +} + +function DoReflectEffect(int Drain) +{ + PlaySound(ShieldHitSound, SLOT_None); + ShieldAltFire(FireMode[1]).TakeHit(Drain); + ClientTakeHit(Drain); +} + +simulated function ClientTakeHit(int Drain) +{ + ClientPlayForceFeedback(ShieldHitForce); + ShieldAltFire(FireMode[1]).TakeHit(Drain); +} + +function bool CheckReflect( Vector HitLocation, out Vector RefNormal, int AmmoDrain ) +{ + local Vector HitDir; + local Vector FaceDir; + + if (!FireMode[1].bIsFiring || Ammo[0].AmmoAmount == 0) return false; + + FaceDir = Vector(Instigator.Controller.Rotation); + HitDir = Normal(Instigator.Location - HitLocation + Vect(0,0,8)); + //Log(self@"HitDir"@(FaceDir dot HitDir)); + + RefNormal = FaceDir; + + if ( FaceDir dot HitDir < -0.37 ) // 68 degree protection arc + { + if (AmmoDrain > 0) + Ammo[0].UseAmmo(AmmoDrain); + return true; + } + return false; +} + +function AnimEnd(int channel) +{ + if (FireMode[0].bIsFiring) + { + LoopAnim('Charged'); + } + else if (!FireMode[1].bIsFiring) + { + Super.AnimEnd(channel); + } +} + +function float SuggestAttackStyle() +{ + return 0.8; +} + +function float SuggestDefenseStyle() +{ + return -0.8; +} + +simulated function float ChargeBar() +{ + return FMin(1,FireMode[0].HoldTime/ShieldFire(FireMode[0]).FullyChargedTime); +} + +defaultproperties +{ + ItemName="Shield Gun" + IconMaterial=Material'InterfaceContent.Hud.SkinA' + IconCoords=(X1=200,Y1=281,X2=321,Y2=371) + + bShowChargingBar=true + bCanThrow=false + FireModeClass(0)=ShieldFire + FireModeClass(1)=ShieldAltFire + InventoryGroup=1 + Mesh=mesh'Weapons.ShieldGun_1st' + BobDamping=2.2 + PickupClass=class'ShieldGunPickup' + EffectOffset=(X=15.0,Y=6.7,Z=1.2) + bMeleeWeapon=true + ShieldHitSound=Sound'WeaponSounds.ShieldGun.ShieldReflection' + DrawScale=0.4 + PutDownAnim=PutDown + DisplayFOV=60 + PlayerViewOffset=(X=2,Y=-0.7,Z=-2.7) + PlayerViewPivot=(Pitch=500,Roll=0,Yaw=500) + + UV2Texture=Material'XGameShaders.WeaponEnvShader' + + AttachmentClass=class'ShieldAttachment' + SelectSound=Sound'WeaponSounds.ShieldGun_change' + SelectForce="ShieldGun_change" + ShieldHitForce="ShieldReflection" + + AIRating=0.35 + CurrentRating=0.35 + + DefaultPriority=2 +} diff --git a/kate/tests/undomanager_test.cpp b/kate/tests/undomanager_test.cpp new file mode 100644 index 00000000..0ccba375 --- /dev/null +++ b/kate/tests/undomanager_test.cpp @@ -0,0 +1,232 @@ +/* This file is part of the KDE libraries + Copyright (C) 2010 Bernhard Beschow + Copyright (C) 2009 Dominik Haumann + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "undomanager_test.h" +#include "moc_undomanager_test.cpp" + +#include + +#include +#include +#include + +QTEST_KDEMAIN(UndoManagerTest, GUI) + +using namespace KTextEditor; + +class UndoManagerTest::TestDocument : public KateDocument +{ +public: + TestDocument() + : KateDocument(false, false, false, 0, 0) + {} +}; + +void UndoManagerTest::testUndoRedoCount() +{ + TestDocument doc; + KateUndoManager *undoManager = doc.undoManager(); + + // no undo/redo items at the beginning + QCOMPARE(undoManager->undoCount(), 0u); + QCOMPARE(undoManager->redoCount(), 0u); + + doc.insertText(Cursor(0, 0), "a"); + + // create one insert-group + QCOMPARE(undoManager->undoCount(), 1u); + QCOMPARE(undoManager->redoCount(), 0u); + + doc.undo(); + + // move insert-group to redo items + QCOMPARE(undoManager->undoCount(), 0u); + QCOMPARE(undoManager->redoCount(), 1u); + + doc.redo(); + + // move insert-group back to undo items + QCOMPARE(undoManager->undoCount(), 1u); + QCOMPARE(undoManager->redoCount(), 0u); + + doc.insertText(Cursor(0, 1), "b"); + + // merge "b" into insert-group + QCOMPARE(undoManager->undoCount(), 1u); + QCOMPARE(undoManager->redoCount(), 0u); + + doc.removeText(Range(0, 1, 0, 2)); + + // create an additional remove-group + QCOMPARE(undoManager->undoCount(), 2u); + QCOMPARE(undoManager->redoCount(), 0u); + + doc.undo(); + + // move remove-group to redo items + QCOMPARE(undoManager->undoCount(), 1u); + QCOMPARE(undoManager->redoCount(), 1u); + + doc.insertText(Cursor(0, 1), "b"); + + // merge "b" into insert-group + // and remove remove-group + QCOMPARE(undoManager->undoCount(), 1u); + QCOMPARE(undoManager->redoCount(), 0u); +} + +void UndoManagerTest::testSafePoint() +{ + TestDocument doc; + KateUndoManager *undoManager = doc.undoManager(); + + doc.insertText(Cursor(0, 0), "a"); + + // create one undo group + QCOMPARE(undoManager->undoCount(), 1u); + QCOMPARE(undoManager->redoCount(), 0u); + + undoManager->undoSafePoint(); + doc.insertText(Cursor(0, 1), "b"); + + // create a second undo group (don't merge) + QCOMPARE(undoManager->undoCount(), 2u); + + doc.undo(); + + // move second undo group to redo items + QCOMPARE(undoManager->undoCount(), 1u); + QCOMPARE(undoManager->redoCount(), 1u); + + doc.insertText(Cursor(0, 1), "b"); + + // create a second undo group again, (don't merge) + QCOMPARE(undoManager->undoCount(), 2u); + QCOMPARE(undoManager->redoCount(), 0u); + + doc.editStart(); + doc.insertText(Cursor(0, 2), "c"); + undoManager->undoSafePoint(); + doc.insertText(Cursor(0, 3), "d"); + doc.editEnd(); + + // merge both edits into second undo group + QCOMPARE(undoManager->undoCount(), 2u); + QCOMPARE(undoManager->redoCount(), 0u); + + doc.insertText(Cursor(0, 4), "e"); + + // create a third undo group (don't merge) + QCOMPARE(undoManager->undoCount(), 3u); + QCOMPARE(undoManager->redoCount(), 0u); +} + +void UndoManagerTest::testCursorPosition() +{ + TestDocument doc; + KateView *view = static_cast(doc.createView(0)); + + doc.setText("aaaa bbbb cccc\n" + "dddd ffff"); + view->setCursorPosition(KTextEditor::Cursor(1, 5)); + + doc.typeChars(view, "eeee"); + + // cursor position: "dddd eeee| ffff" + QCOMPARE(view->cursorPosition(), KTextEditor::Cursor(1, 9)); + + // undo once to remove "eeee", cursor position: "dddd | ffff" + doc.undo(); + QCOMPARE(view->cursorPosition(), KTextEditor::Cursor(1, 5)); + + // redo once to insert "eeee" again. cursor position: "dddd eeee| ffff" + doc.redo(); + QCOMPARE(view->cursorPosition(), KTextEditor::Cursor(1, 9)); + + delete view; +} + +void UndoManagerTest::testSelectionUndo() +{ + TestDocument doc; + KateView *view = static_cast(doc.createView(0)); + + doc.setText("aaaa bbbb cccc\n" + "dddd eeee ffff"); + view->setCursorPosition(KTextEditor::Cursor(1, 9)); + KTextEditor::Range selectionRange(KTextEditor::Cursor(0, 5), + KTextEditor::Cursor(1, 9)); + view->setSelection(selectionRange); + + doc.typeChars(view, "eeee"); + + // cursor position: "aaaa eeee| ffff", no selection anymore + QCOMPARE(view->cursorPosition(), KTextEditor::Cursor(0, 9)); + QCOMPARE(view->selection(), false); + + // undo to remove "eeee" and add selection and text again + doc.undo(); + QCOMPARE(view->cursorPosition(), KTextEditor::Cursor(1, 9)); + QCOMPARE(view->selection(), true); + QCOMPARE(view->selectionRange(), selectionRange); + + // redo to insert "eeee" again and remove selection + // cursor position: "aaaa eeee| ffff", no selection anymore + doc.redo(); + QCOMPARE(view->cursorPosition(), KTextEditor::Cursor(0, 9)); + QCOMPARE(view->selection(), false); + + delete view; +} + +void UndoManagerTest::testUndoWordWrapBug301367() +{ + TestDocument doc; + doc.setWordWrap(true); + doc.setWordWrapAt(20); + KateView *view = static_cast(doc.createView(0)); + + QString text = "1234 1234 1234 1234\n" + "1234 1234 1234 1234"; + + doc.setText(text); + view->setCursorPosition(KTextEditor::Cursor(0, 0)); + + doc.typeChars(view, " "); + + while (doc.undoCount() > 1) + doc.undo(); + + // test must be exactly the same as before + QCOMPARE(doc.text (), text); + + while (doc.redoCount() > 1) + doc.redo(); + + while (doc.undoCount() > 1) + doc.undo(); + + // test must be exactly the same as before + QCOMPARE(doc.text (), text); + + delete view; +} + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/tests/undomanager_test.h b/kate/tests/undomanager_test.h new file mode 100644 index 00000000..299b5b45 --- /dev/null +++ b/kate/tests/undomanager_test.h @@ -0,0 +1,43 @@ +/* This file is part of the KDE libraries + Copyright (C) 2010 Bernhard Beschow + Copyright (C) 2009 Dominik Haumann + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KATE_UNDOMANAGER_TEST_H +#define KATE_UNDOMANAGER_TEST_H + +#include + +class UndoManagerTest : public QObject +{ + Q_OBJECT + + private Q_SLOTS: + void testUndoRedoCount(); + void testSafePoint(); + void testCursorPosition(); + void testSelectionUndo(); + void testUndoWordWrapBug301367(); + + private: + class TestDocument; +}; + +#endif + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kate/tests/wordcompletiontest.cpp b/kate/tests/wordcompletiontest.cpp new file mode 100644 index 00000000..adacbae6 --- /dev/null +++ b/kate/tests/wordcompletiontest.cpp @@ -0,0 +1,117 @@ +/* + * + * Copyright 2013 Sven Brauch + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +#include "wordcompletiontest.h" + +#include +#include +#include +#include + +#include +#include + +QTEST_KDEMAIN(WordCompletionTest, GUI) + +static const int count = 50000; + +using namespace KTextEditor; + +void WordCompletionTest::initTestCase() +{ + Editor* editor = EditorChooser::editor(); + QVERIFY(editor); + + m_doc = editor->createDocument(this); + QVERIFY(m_doc); +} + +void WordCompletionTest::cleanupTestCase() +{ +} + +void WordCompletionTest::init() +{ + m_doc->clear(); +} + +void WordCompletionTest::cleanup() +{ +} + +void WordCompletionTest::benchWordRetrievalMixed() +{ + const int distinctWordRatio = 100; + QStringList s; + s.reserve(count); + for ( int i = 0; i < count; i++ ) { + s.append(QLatin1String("HelloWorld") + QString::number(i / distinctWordRatio)); + } + s.prepend("\n"); + m_doc->setText(s); + + // creating the view only after inserting the text makes test execution much faster + QScopedPointer v(m_doc->createView(0)); + QBENCHMARK { + KateWordCompletionModel m(0); + QCOMPARE(m.allMatches(v.data(), KTextEditor::Range()).size(), count / distinctWordRatio); + } +} + +void WordCompletionTest::benchWordRetrievalSame() +{ + QStringList s; + s.reserve(count); + // add a number so the words have roughly the same length as in the other tests + const QString str = QLatin1String("HelloWorld") + QString::number(count); + for ( int i = 0; i < count; i++ ) { + s.append(str); + } + s.prepend("\n"); + m_doc->setText(s); + + QScopedPointer v(m_doc->createView(0)); + QBENCHMARK { + KateWordCompletionModel m(0); + QCOMPARE(m.allMatches(v.data(), KTextEditor::Range()).size(), 1); + } +} + +void WordCompletionTest::benchWordRetrievalDistinct() +{ + QStringList s; + s.reserve(count); + for ( int i = 0; i < count; i++ ) { + s.append(QLatin1String("HelloWorld") + QString::number(i)); + } + s.prepend("\n"); + m_doc->setText(s); + + QScopedPointer v(m_doc->createView(0)); + QBENCHMARK { + KateWordCompletionModel m(0); + QCOMPARE(m.allMatches(v.data(), KTextEditor::Range()).size(), count); + } +} + +#include "moc_wordcompletiontest.cpp" + +// kate: indent-width 2 diff --git a/kate/tests/wordcompletiontest.h b/kate/tests/wordcompletiontest.h new file mode 100644 index 00000000..97589a23 --- /dev/null +++ b/kate/tests/wordcompletiontest.h @@ -0,0 +1,47 @@ +/* + * + * Copyright 2013 Sven Brauch + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef KATE_WORDCOMPLETIONTEST_H +#define KATE_WORDCOMPLETIONTEST_H + +#include +#include + +class WordCompletionTest : public QObject +{ + Q_OBJECT +private slots: + void initTestCase(); + void cleanupTestCase(); + + void init(); + void cleanup(); + + void benchWordRetrievalDistinct(); + void benchWordRetrievalSame(); + void benchWordRetrievalMixed(); + +private: + KTextEditor::Document* m_doc; +}; + +#endif // KATE_WORDCOMPLETIONTEST_H diff --git a/kcontrol/CMakeLists.txt b/kcontrol/CMakeLists.txt index c20d4eff..aee74cfc 100644 --- a/kcontrol/CMakeLists.txt +++ b/kcontrol/CMakeLists.txt @@ -45,3 +45,6 @@ if(FONTCONFIG_FOUND AND FREETYPE_FOUND) add_subdirectory( kfontinst ) add_subdirectory( fonts ) endif() + +add_subdirectory( ebrowsing ) +add_subdirectory( kio ) diff --git a/kcontrol/ebrowsing/CMakeLists.txt b/kcontrol/ebrowsing/CMakeLists.txt new file mode 100644 index 00000000..4b4e5236 --- /dev/null +++ b/kcontrol/ebrowsing/CMakeLists.txt @@ -0,0 +1,15 @@ +kde4_add_plugin(kcm_kurifilt main.cpp) + +target_link_libraries(kcm_kurifilt + ${KDE4_KIO_LIBS} +) + +install( + TARGETS kcm_kurifilt + DESTINATION ${KDE4_PLUGIN_INSTALL_DIR} +) + +install( + FILES ebrowsing.desktop + DESTINATION ${KDE4_SERVICES_INSTALL_DIR} +) diff --git a/kcontrol/ebrowsing/Messages.sh b/kcontrol/ebrowsing/Messages.sh new file mode 100644 index 00000000..eebe877a --- /dev/null +++ b/kcontrol/ebrowsing/Messages.sh @@ -0,0 +1,2 @@ +#! /usr/bin/env bash +$XGETTEXT *.cpp -o $podir/kcmkurifilt.pot diff --git a/kcontrol/ebrowsing/ebrowsing.desktop b/kcontrol/ebrowsing/ebrowsing.desktop new file mode 100644 index 00000000..64e3b971 --- /dev/null +++ b/kcontrol/ebrowsing/ebrowsing.desktop @@ -0,0 +1,246 @@ +[Desktop Entry] +Type=Service +X-KDE-ServiceTypes=KCModule +X-DocPath=kcontrol/ebrowsing/index.html +Icon=preferences-web-browser-shortcuts +Exec=kcmshell4 ebrowsing + +X-KDE-Library=kcm_kurifilt +X-KDE-ParentApp=kcontrol + +# used by kpart, konsole, krunner and more +X-KDE-System-Settings-Parent-Category=workspace-appearance-and-behavior + +Name=Web Shortcuts +Name[af]=Web Kortpaaie +Name[ar]=اختصارات الوِب +Name[as]=ৱেবৰ চমু পথ +Name[ast]=Accesos rápidos pa web +Name[be]=Скароты Сеціва +Name[be@latin]=Skaroty dla sieciva +Name[bg]=Уеб съкращения +Name[bn]=ওয়েব শর্টকাট +Name[bn_IN]=ওয়েব শর্ট-কাট +Name[br]=Berradennoù ar gwiad +Name[bs]=Veb prečice +Name[ca]=Dreceres web +Name[ca@valencia]=Dreceres web +Name[cs]=Webové zkratky +Name[csb]=Sécowé skrodzënë +Name[cy]=Byr-lwybrau Gwe +Name[da]=Webgenveje +Name[de]=Webkürzel +Name[el]=Συντομεύσεις ιστού +Name[en_GB]=Web Shortcuts +Name[eo]=TTT klavokombinoj +Name[es]=Accesos rápidos para web +Name[et]=Veebi kiirkorraldused +Name[eu]=Weben lasterbideak +Name[fa]=میان‌برهای وب +Name[fi]=WWW-pikavalinnat +Name[fr]=Raccourcis Web +Name[fy]=Webskeakels +Name[ga]=Aicearraí Gréasáin +Name[gl]=Atallos web +Name[gu]=વેબ ટૂંકાણો +Name[he]=קיצורי דרך אינטרנטיים +Name[hi]=वेब शॉर्टकट +Name[hne]=वेब सार्टकट +Name[hr]=Internetski prečaci +Name[hsb]=Skrótšenki za web +Name[hu]=Keresési azonosítók +Name[ia]=Vias Breve de web +Name[id]=Jalan Pintas Web +Name[is]=Vefskammstafanir +Name[it]=Scorciatoie del web +Name[ja]=ウェブショートカット +Name[ka]=ვებგვერდების მალსახმობი +Name[kk]=Веб қысқартулары +Name[km]=ផ្លូវកាត់​បណ្ដាញ +Name[kn]=ಜಾಲ ಶೀಘ್ರಮಾರ್ಗಗಳು (ಶಾರ್ಟ್ ಕಟ್) +Name[ko]=웹 바로 가기 +Name[ku]=Kurteriyên Webê +Name[lt]=Žiniatinklio trumpės +Name[lv]=Ātrās meklēšanas īsceļi +Name[mai]=वेब शार्टकट +Name[mk]=Интернет-кратенки +Name[ml]=വെബിലെ കുറക്കുവഴികള്‍ +Name[mr]=वेब शॉर्टकट +Name[ms]=Jalan Pintas Web +Name[nb]=Nettsnarveier +Name[nds]=Söökafkörten +Name[ne]=वेब सर्टकट +Name[nl]=Websnelkoppelingen +Name[nn]=Vevsnarvegar +Name[or]=ୱେବ ସଂକ୍ଷିପ୍ତ ପଥଗୁଡ଼ିକ +Name[pa]=ਵੈਬ ਸ਼ਾਰਟਕੱਟ +Name[pl]=Skróty sieciowe +Name[pt]=Atalhos Web +Name[pt_BR]=Atalhos da Web +Name[ro]=Acceleratori de web +Name[ru]=Веб-сокращения +Name[se]=Fierpmádatlávkestagat +Name[si]=වෙබ් කෙටිමං +Name[sk]=Webové skratky +Name[sl]=Spletne bližnjice +Name[sr]=Веб пречице +Name[sr@ijekavian]=Веб пречице +Name[sr@ijekavianlatin]=Veb prečice +Name[sr@latin]=Veb prečice +Name[sv]=Webbgenvägar +Name[ta]=இணைய சுருக்கு வழிகள் +Name[te]=వెబ్ లఘువులు +Name[tg]=Миёнбурҳои интернетӣ +Name[th]=ทางลัดเว็บ +Name[tr]=Web Kısayolları +Name[ug]=تور تېزلەتمىسى +Name[uk]=Вебскорочення +Name[uz]=Veb-qisqartmalari +Name[uz@cyrillic]=Веб-қисқартмалари +Name[vi]=Lối tắt Mạng +Name[wa]=Rascourtis waibe +Name[xh]=Ezimfutshane ze Web +Name[x-test]=xxWeb Shortcutsxx +Name[zh_CN]=网页快捷方式 +Name[zh_TW]=網頁捷徑 + +Comment=Configure enhanced browsing +Comment[af]=Konfigureer verbeterde blaaiïng +Comment[ar]=اضبط التصفح المحسّن +Comment[as]=উন্নতভাবে চৰণ কৰা বিন্যাস কৰক +Comment[ast]=Configuración de la navegación ameyorada +Comment[be]=Настаўленні прасунутага вандравання +Comment[be@latin]=Naładź lepšaje hartańnie +Comment[bg]=Настройване на уеб съкращения и бързи препратки +Comment[bn]=বর্ধিত ব্রাউজিং কনফিগার করুন +Comment[bn_IN]=উন্নত বৈশিষ্ট্যসহ ব্রাউজিং কনফিগার করুন +Comment[br]=Kefluniañ ar furchal gwellaet +Comment[bs]=Podešavanje poboljšanog pregledanja +Comment[ca]=Aquí podeu configurar la navegació millorada +Comment[ca@valencia]=Ací podeu configurar la navegació millorada +Comment[cs]=Nastavení rozšířeného prohledávání +Comment[csb]=Kònfigùracëjô rozszérzonëch cechów przezéraniô +Comment[cy]=Ffurfweddu pori uchel +Comment[da]=Indstil udvidet webbrowsing +Comment[de]=Die Verwendung von Web-Kürzeln einrichten +Comment[el]=Διαμορφώστε την εμπλουτισμένη περιήγηση +Comment[en_GB]=Configure enhanced browsing +Comment[eo]=Agordi la plibonigitan foliumadon +Comment[es]=Configuración de la navegación mejorada +Comment[et]=Täiustatud lehitsemise konfigureerimine +Comment[eu]=Konfiguratu arakatze hobetua +Comment[fa]=پیکربندی مرور بهبودیافته +Comment[fi]=Selauksen lisäasetukset +Comment[fr]=Configuration de la navigation avancée +Comment[fy]=Hjir kinne jo it avansearre blêdzjen ynstelle +Comment[ga]=Cumraigh brabhsáil fheabhsaithe +Comment[gl]=Configurar a navegación mellorada +Comment[gu]=વધુ સારું બ્રાઉઝિંગ રૂપરેખાંકિત કરો +Comment[he]=הגדרת גלישה משופרת +Comment[hi]=एनहेंस्ड ब्राउज़िंग कॉन्फ़िगर करें +Comment[hne]=एनहेंस्ड ब्राउजिंग कान्फिगर करव +Comment[hr]=Konfiguriranje poboljšanog pregledavanja +Comment[hsb]=Konfiguracija zlěpšeneho browsowanja +Comment[hu]=A keresési beállításokat lehet módosítani +Comment[ia]=Configura navigation avantiate +Comment[id]=Atur perambanan yang ditingkatkan +Comment[is]=Hér getur þú stillt endurbætur á vefskoðara +Comment[it]=Configura la navigazione avanzata +Comment[ja]=拡張ブラウズの設定 +Comment[kk]=Вебті шарлағанда қолданатын қысқартулар +Comment[km]=កំណត់​រចនាសម្ព័ន្ធ​ការ​រុករក​ឲ្យ​កាន់​តែ​ប្រសើរ +Comment[kn]=ಊರ್ಜಿತ ವೀಕ್ಷಣೆಯನ್ನು ಸಂರಚಿಸು +Comment[ko]=확장 브라우징 설정 +Comment[ku]=Avakirina Gerîna Pêşkeftî +Comment[lt]=Konfigūruoti išplėstinį naršymą +Comment[lv]=Šeit jūs varat konfigurēt pārkūkošanas uzlabojumus +Comment[mai]=एनहेंस्ड ब्राउजिंग बिन्यस्त करू +Comment[mk]=Конфигурирајте го напредното прелистување +Comment[ml]=മെച്ചപ്പെട്ട ബ്രൌസിങ്ങ് ക്രമീകരിയ്ക്കുക +Comment[mr]=प्रगत ब्राऊजिंग संयोजीत करा +Comment[ms]=Konfigur pelayaran lanjutan +Comment[nb]=Sett opp utvidet surfing +Comment[nds]=Söökafkörten för't gaue Söken in't Nett inrichten +Comment[ne]=बढाइएको ब्राउजिङ कन्फिगर गर्नुहोस् +Comment[nl]=Hier kunt u het geavanceerd browsen instellen +Comment[nn]=Oppsett av utvida surfing +Comment[or]=ଉନ୍ନତ ବ୍ରାଉଜିଙ୍ଗ ବିନ୍ୟାସ କରନ୍ତୁ +Comment[pa]=ਸਹਾਇਕ ਝਲਕਾਰਾ ਸੰਰਚਨਾ +Comment[pl]=Ustawienia rozszerzonych cech przeglądania +Comment[pt]=Configuração da navegação melhorada +Comment[pt_BR]=Configura a navegação melhorada +Comment[ro]=Configurează navigarea de web avansată +Comment[ru]=Настройка расширенных возможностей браузера +Comment[se]=Heivet viiddiduvvon fierbmegolgama +Comment[si]=දියුණු කළ ගවේෂණ සැකසුම් +Comment[sk]=Nastavenie rozšíreného prehliadania +Comment[sl]=Nastavitve izboljšanega brskanja +Comment[sr]=Подешавање побољшаног прегледања +Comment[sr@ijekavian]=Подешавање побољшаног прегледања +Comment[sr@ijekavianlatin]=Podešavanje poboljšanog pregledanja +Comment[sr@latin]=Podešavanje poboljšanog pregledanja +Comment[sv]=Anpassa utökad webbläsning +Comment[ta]=மேம்பட்ட தேடலை வடிவமைக்க +Comment[te]=విస్తరించిన అన్వేషణను ఆకృతీకరించుము +Comment[tg]=Танзимоти намоиши Интернет +Comment[th]=ปรับแต่งการเรียกดูแบบเพิ่มความสามารถ +Comment[tr]=Gelişmiş taramayı yapılandır +Comment[ug]=كۈچەيتىلگەن توركۆرگۈ سەپلىمىسى +Comment[uk]=Налаштування спецфункцій навігації +Comment[uz]=Tezlashtirilgan veb-koʻrishni moslash +Comment[uz@cyrillic]=Тезлаштирилган веб-кўришни мослаш +Comment[vi]=Cấu hình khả năng duyệt tăng cường +Comment[wa]=Apontyî li naiviaedje waibe avancî +Comment[xh]=Qwalasela ukhangelo lwencwadi olongezelelweyo +Comment[x-test]=xxConfigure enhanced browsingxx +Comment[zh_CN]=配置增强浏览特性 +Comment[zh_TW]=設定增強式瀏覽 + +X-KDE-Keywords=Enhanced Browsing,Browsing,WWW,Internet,Internet Filters,Network,Search Engines,Shortcuts +X-KDE-Keywords[bg]=Сърфиране,Уеб,Интернет,Мрежа,Търсене,Търсачка,Enhanced Browsing,Browsing,WWW,Internet,Internet Filters,Network,Search Engines,Shortcuts +X-KDE-Keywords[bs]=Enhanced Browsing,Browsing,WWW,Internet,Internet Filters,Network,Search Engines,Shortcuts +X-KDE-Keywords[ca]=Navegació millorada,navegació,WWW,Internet,Filtres d'Internet,Xarxa,Motors de cerca,Dreceres +X-KDE-Keywords[ca@valencia]=Navegació millorada,navegació,WWW,Internet,Filtres d'Internet,Xarxa,Motors de cerca,Dreceres +X-KDE-Keywords[da]=Forbedret browsing,browsing,www,internet,internetfiltre,netværk,søgemaskiner,genveje +X-KDE-Keywords[de]=Browsen,Erweitertes Browsen,WWW,Internet,Stichwörter,Filter,Netz,Internetfilter,Suchmaschinen,Kurzbefehle,Netzwerk +X-KDE-Keywords[el]=Εμπλουτισμένη περιήγηση,περιήγηση,www,διαδίκτυο,διαδικτυακά φίλτρα,δίκτυο,μηχανές αναζήτησης,συντομεύσεις +X-KDE-Keywords[en_GB]=Enhanced Browsing,Browsing,WWW,Internet,Internet Filters,Network,Search Engines,Shortcuts +X-KDE-Keywords[es]=Navegación mejorada,Navegación,WWW,Internet,Filtros de Internet,Red,Motores de búsqueda,Accesos rápidos +X-KDE-Keywords[et]=Täiustatud sirvimine,Sirvimine,WWW,Internet,Internetifiltrid,Võrk,Otsimootorid,Kiirkorraldused +X-KDE-Keywords[fi]=selaimen lisäasetukset,edistynyt selailu,selailu,selaus,WWW,Internet,Internet-suodattimet,verkko,hakukoneet,pikavalinnat +X-KDE-Keywords[fr]=Navigation avancée, navigation, WWW, Internet, filtres Internet, réseau, moteur de recherche, raccourcis +X-KDE-Keywords[ga]=Brabhsáil Fheabhsaithe,Brabhsáil,WWW,Gréasán,Idirlíon,Scagairí Idirlín,Líonra,Innill Chuardaigh,Aicearraí +X-KDE-Keywords[gl]=Navegación mellorada,navegar,www.internet,filtros de internet,rede, motores de busca,atallos +X-KDE-Keywords[he]=Enhanced Browsing,Browsing,WWW,Internet,Internet Filters,Network,Search Engines,Shortcuts,גלישה משופרת, גלישה, משופרת, אינטרנט, ווב, רשת,חפש, חיפוש, מנוע, קיצור +X-KDE-Keywords[hu]=Fejlesztett böngészés,Böngészés,WWW,Internet,Internetes szűrők,Hálózat,Keresőmotorok,Gyorsbillentyűk +X-KDE-Keywords[ia]=Navigation avantiate, Navigation,WWW,Internet,Filtros de Internet,Rete, Motores de cerca,Vias breve +X-KDE-Keywords[id]=Meramban Canggih,Meramban,WWW,Internet,Filter Internet,Jaringan,Mesin Pencari,Jalan Pintas +X-KDE-Keywords[it]=Navigazione avanzata,navigazione,WWW,Internet,filtri Internet,rete,motori di ricerca,scorciatoie +X-KDE-Keywords[kk]=Enhanced Browsing,Browsing,WWW,Internet,Internet Filters,Network,Search Engines,Shortcuts +X-KDE-Keywords[km]=ការ​រកមើល​ដែល​បាន​ធ្វើ​ឲ្យ​ប្រសើរ ការ​រកមើល WWW អ៊ីនធឺណិត តម្រង​អ៊ីនធឺណិត បណ្ដាញ ម៉ាស៊ីន​ស្វែងរក ផ្លូវកាត់ +X-KDE-Keywords[ko]=Enhanced Browsing,Browsing,WWW,Internet,Internet Filters,Network,Search Engines,Shortcuts,웹 브라우징,브라우징,네트워크,검색,검색 엔진,바로 가기 +X-KDE-Keywords[lt]=Išplėstinis naršymas,Naršymas,WWW,Internetas,Interneto filtrai,Tinklas,Paieškos varikliai,trumpiniai +X-KDE-Keywords[lv]=Uzlabota pārlūkošana,pārlūkošana,WWW,internets,interneta filtri,tīkls,meklēšanas dzinēji,saīnes +X-KDE-Keywords[nb]=Utvidet surfing,Surfing,WWW,Internett,Internettfiltre,Nettverk,Søkemotorer,Snarveier +X-KDE-Keywords[nds]=Verwiedert Nettkieken,Nettkieken,WWW,Internet,Internet-Filtern,Nettwark,Söökmaschinen,Afkörten,Tastkombinatschonen +X-KDE-Keywords[nl]=Verbeterd browsen,browsen,www,internet,internetfilters,netwerk,zoekmachines,sneltoetsen +X-KDE-Keywords[pl]=Ulepszone przeglądanie,Przeglądanie,WWW,Internet,Filtry internetowe,Sieć,Wyszukiwarki,Silniki wyszukiwania,Skróty +X-KDE-Keywords[pt]=Navegação Melhorada,Navegação,WWW,Internet,Filtros da Internet,Rede,Motores de Busca,Atalhos +X-KDE-Keywords[pt_BR]=Navegação melhorada,Navegação,WWW,Internet,Filtros da Internet,Rede,Mecanismos de busca,Atalhos +X-KDE-Keywords[ro]=navigare avansată,navigare,WWW,internet,filtre internet,rețea,motoare de căutare,scurtături +X-KDE-Keywords[ru]=Enhanced Browsing,Browsing,WWW,Internet,Internet Filters,Network,Search Engines,Shortcuts,веб-сёрфинг,Интернет,сетевые фильтры,сеть,поисковые движки,веб-сокращения,комбинации клавиш +X-KDE-Keywords[sk]=Rozšírené surfovanie,Surfovanie,WWW,Internet,Internetové filtre,Sieť,Vyhľadávacie nástroje,Odkazy +X-KDE-Keywords[sl]=Razširjeno brskanje,Brskanje,WWW,Internet,Internetni filtri,Omrežni iskalni pogoni,Bližnjice +X-KDE-Keywords[sr]=Enhanced Browsing,Browsing,WWW,Internet,Internet Filters,Network,Search Engines,Shortcuts,побољшано прегледање,прегледање,веб,интернет,филтери,мрежа,претраживачи,пречице +X-KDE-Keywords[sr@ijekavian]=Enhanced Browsing,Browsing,WWW,Internet,Internet Filters,Network,Search Engines,Shortcuts,побољшано прегледање,прегледање,веб,интернет,филтери,мрежа,претраживачи,пречице +X-KDE-Keywords[sr@ijekavianlatin]=Enhanced Browsing,Browsing,WWW,Internet,Internet Filters,Network,Search Engines,Shortcuts,poboljšano pregledanje,pregledanje,veb,internet,filteri,mreža,pretraživači,prečice +X-KDE-Keywords[sr@latin]=Enhanced Browsing,Browsing,WWW,Internet,Internet Filters,Network,Search Engines,Shortcuts,poboljšano pregledanje,pregledanje,veb,internet,filteri,mreža,pretraživači,prečice +X-KDE-Keywords[sv]=Förbättrad webbläsning,Webbläsning,WWW,Internet,Internetfilter,Nätverk,Söktjänster,Genvägar +X-KDE-Keywords[tr]=Gelişmiş Tarama,Tarama,WWW,İnternet,İnternet Filtreleri,Ağ,Arama Motorları,Kısayollar +X-KDE-Keywords[uk]=Enhanced Browsing;Browsing;WWW;Internet;Internet Filters;Network;Search Engines;Shortcuts;додатково;додатковий;інтернет;фільтри;фільтрування;фільтр;мережа;рушій;пошук;рушії пошуку;скорочення;вебскорочення +X-KDE-Keywords[wa]=Betchtaedje amidré,Betchtaedje,Naiviaedje,WWW,Daegntoele,Passetes Dagntoele,Rantoele,Moteurs di cweraedje,Ricwerresses,Rascourtis +X-KDE-Keywords[x-test]=xxEnhanced Browsing,Browsing,WWW,Internet,Internet Filters,Network,Search Engines,Shortcutsxx +X-KDE-Keywords[zh_CN]=Enhanced Browsing,Browsing,WWW,Internet,Internet Filters,Network,Search Engines,Shortcuts,增强浏览,浏览,互联网,互联网过滤,网络,搜索引擎,快捷方式 +X-KDE-Keywords[zh_TW]=Enhanced Browsing,Browsing,WWW,Internet,Internet Filters,Network,Search Engines,Shortcuts + +Categories=Qt;KDE;X-KDE-settings-webbrowsing; diff --git a/kcontrol/ebrowsing/main.cpp b/kcontrol/ebrowsing/main.cpp new file mode 100644 index 00000000..150ef782 --- /dev/null +++ b/kcontrol/ebrowsing/main.cpp @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2000 Yves Arrouye + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +// Own +#include "main.h" + +// Qt +#include +#include + +// KDE +#include +#include +#include +#include +#include + + +K_PLUGIN_FACTORY(KURIFactory, + registerPlugin(); + ) +K_EXPORT_PLUGIN(KURIFactory("kcmkurifilt")) + +KURIFilterModule::KURIFilterModule(QWidget *parent, const QVariantList &) + : KCModule(KURIFactory::componentData(), parent), + m_widget(0) +{ + + filter = KUriFilter::self(); + + setQuickHelp( i18n("

    Enhanced Browsing

    In this module you can configure some enhanced browsing" + " features of KDE. " + "

    Web Shortcuts

    Web Shortcuts are a quick way of using Web search engines. For example, type \"altavista:frobozz\"" + " or \"av:frobozz\" and Konqueror will do a search on AltaVista for \"frobozz\"." + " Even easier: just press Alt+F2 (if you have not" + " changed this shortcut) and enter the shortcut in the KDE Run Command dialog.")); + + QVBoxLayout *layout = new QVBoxLayout(this); + + QMap helper; + // Load the plugins. This saves a public method in KUriFilter just for this. + const KService::List offers = KServiceTypeTrader::self()->query( "KUriFilter/Plugin" ); + KService::List::ConstIterator it = offers.begin(); + const KService::List::ConstIterator end = offers.end(); + for (; it != end; ++it ) + { + KUriFilterPlugin *plugin = ( *it )->createInstance( this ); + if (plugin) { + KCModule *module = plugin->configModule(this, 0); + if (module) { + modules.append(module); + helper.insert(plugin->configName(), module); + connect(module, SIGNAL(changed(bool)), SIGNAL(changed(bool))); + } + } + } + + if (modules.count() > 1) + { + QTabWidget *tab = new QTabWidget(this); + + QMap::iterator it2; + for (it2 = helper.begin(); it2 != helper.end(); ++it2) + tab->addTab(it2.value(), it2.key()); + + tab->setCurrentIndex(tab->indexOf(modules.first())); + m_widget = tab; + } + else if (modules.count() == 1) + { + m_widget = modules.first(); + if (m_widget->layout()) + m_widget->layout()->setMargin(0); + } + + if (m_widget) { + layout->addWidget(m_widget); + } +} + +void KURIFilterModule::load() +{ +// seems not to be necessary, since modules automatically call load() on show (uwolfer) +// foreach( KCModule* module, modules ) +// { +// module->load(); +// } +} + +void KURIFilterModule::save() +{ + foreach( KCModule* module, modules ) + { + module->save(); + } +} + +void KURIFilterModule::defaults() +{ + foreach( KCModule* module, modules ) + { + module->defaults(); + } +} + +KURIFilterModule::~KURIFilterModule() +{ + qDeleteAll( modules ); +} + +#include "moc_main.cpp" diff --git a/kcontrol/ebrowsing/main.h b/kcontrol/ebrowsing/main.h new file mode 100644 index 00000000..12dcc467 --- /dev/null +++ b/kcontrol/ebrowsing/main.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2000 Yves Arrouye + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#ifndef MAIN_H +#define MAIN_H + +#include + +class KUriFilter; + +class KURIFilterModule : public KCModule { + Q_OBJECT + +public: + KURIFilterModule(QWidget *parent, const QVariantList &); + ~KURIFilterModule(); + + void load(); + void save(); + void defaults(); + +private: + KUriFilter *filter; + + QWidget *m_widget; + QList modules; +}; + +#endif diff --git a/kcontrol/kio/CMakeLists.txt b/kcontrol/kio/CMakeLists.txt new file mode 100644 index 00000000..a4816bbd --- /dev/null +++ b/kcontrol/kio/CMakeLists.txt @@ -0,0 +1,31 @@ +########### next target ############### + +set(kcm_kio_PART_SRCS + main.cpp + kproxydlg.cpp + netpref.cpp + bookmarks.cpp + ksaveioconfig.cpp +) + +kde4_add_plugin(kcm_kio ${kcm_kio_PART_SRCS}) + +target_link_libraries(kcm_kio + ${KDE4_KCMUTILS_LIBS} + ${KDE4_KIO_LIBS} +) + +install( + TARGETS kcm_kio + DESTINATION ${KDE4_PLUGIN_INSTALL_DIR} +) + +########### install files ############### + +install( + FILES + bookmarks.desktop + netpref.desktop + proxy.desktop + DESTINATION ${KDE4_SERVICES_INSTALL_DIR} +) diff --git a/kcontrol/kio/Messages.sh b/kcontrol/kio/Messages.sh new file mode 100644 index 00000000..807818d9 --- /dev/null +++ b/kcontrol/kio/Messages.sh @@ -0,0 +1,3 @@ +#! /usr/bin/env bash +$EXTRACTRC *.ui *.kcfg >> rc.cpp +$XGETTEXT *.cpp -o $podir/kcmkio.pot diff --git a/kcontrol/kio/bookmarks.cpp b/kcontrol/kio/bookmarks.cpp new file mode 100644 index 00000000..82123a94 --- /dev/null +++ b/kcontrol/kio/bookmarks.cpp @@ -0,0 +1,109 @@ +/* +Copyright (C) 2008 Xavier Vello + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +// Own +#include "bookmarks.h" + +// Qt +#include +#include +#include +#include +#include +#include + +// KDE +#include +#include +#include +#include + +K_PLUGIN_FACTORY_DECLARATION(KioConfigFactory) + +BookmarksConfigModule::BookmarksConfigModule(QWidget *parent, const QVariantList &) + :KCModule(KioConfigFactory::componentData(), parent) +{ + ui.setupUi(this); +} + +BookmarksConfigModule::~BookmarksConfigModule() +{ +} + +void BookmarksConfigModule::load() +{ + KConfig *c = new KConfig("kiobookmarksrc"); + KConfigGroup group = c->group("General"); + + ui.sbColumns->setValue(group.readEntry("Columns", 4)); + ui.cbShowBackgrounds->setChecked(group.readEntry("ShowBackgrounds", true)); + ui.cbShowRoot->setChecked(group.readEntry("ShowRoot", true)); + ui.cbFlattenTree->setChecked(group.readEntry("FlattenTree", false)); + ui.cbShowPlaces->setChecked(group.readEntry("ShowPlaces", true)); + ui.sbCacheSize->setValue(group.readEntry("CacheSize", 5*1024)); + + // Config changed notifications... + connect ( ui.sbColumns, SIGNAL(valueChanged(int)), SLOT(configChanged()) ); + connect ( ui.cbShowBackgrounds, SIGNAL(toggled(bool)), SLOT(configChanged()) ); + connect ( ui.cbShowRoot, SIGNAL(toggled(bool)), SLOT(configChanged()) ); + connect ( ui.cbFlattenTree, SIGNAL(toggled(bool)), SLOT(configChanged()) ); + connect ( ui.cbShowPlaces, SIGNAL(toggled(bool)), SLOT(configChanged()) ); + connect ( ui.sbCacheSize, SIGNAL(valueChanged(int)), SLOT(configChanged()) ); + + delete c; + emit changed( false ); +} + +void BookmarksConfigModule::save() +{ + KConfig *c = new KConfig("kiobookmarksrc"); + KConfigGroup group = c->group("General"); + group.writeEntry("Columns", ui.sbColumns->value() ); + group.writeEntry("ShowBackgrounds", ui.cbShowBackgrounds->isChecked() ); + group.writeEntry("ShowRoot", ui.cbShowRoot->isChecked() ); + group.writeEntry("FlattenTree", ui.cbFlattenTree->isChecked() ); + group.writeEntry("ShowPlaces", ui.cbShowPlaces->isChecked() ); + group.writeEntry("CacheSize", ui.sbCacheSize->value() ); + + c->sync(); + delete c; + emit changed( false ); +} + +void BookmarksConfigModule::defaults() +{ + ui.sbColumns->setValue( 4 ); + ui.cbShowBackgrounds->setChecked( true ); + ui.cbShowRoot->setChecked( true ); + ui.cbShowPlaces->setChecked( true ); + ui.cbFlattenTree->setChecked( false ); + ui.sbCacheSize->setValue( 5*1024 ); +} + +QString BookmarksConfigModule::quickHelp() const +{ + return i18n( "

    My Bookmarks

    This module lets you configure the bookmarks home page.

    " + "

    The bookmarks home page is accessible at bookmarks:/.

    " ); +} + +void BookmarksConfigModule::configChanged() +{ + emit changed( true ); +} + +#include "moc_bookmarks.cpp" diff --git a/kcontrol/kio/bookmarks.desktop b/kcontrol/kio/bookmarks.desktop new file mode 100644 index 00000000..150d07c5 --- /dev/null +++ b/kcontrol/kio/bookmarks.desktop @@ -0,0 +1,237 @@ +[Desktop Entry] +Type=Service +X-KDE-ServiceTypes=KCModule +X-DocPath=kcontrol/bookmarks/index.html +Icon=bookmarks +Exec=kcmshell4 bookmarks + +X-KDE-Library=kcm_kio +X-KDE-PluginKeyword=bookmarks +X-KDE-ParentApp=kcontrol + +X-KDE-System-Settings-Parent-Category=network-and-connectivity + +Name=Bookmarks +Name[af]=Boekmerke +Name[ar]=العلامات +Name[as]=পত্ৰচিহ্ন +Name[ast]=Marcadores +Name[be]=Закладкі +Name[be@latin]=Zakładki +Name[bg]=Отметки +Name[bn]=বুকমার্ক +Name[bn_IN]=বুকমার্ক +Name[br]=Sinedoù +Name[bs]=Obeleživači +Name[ca]=Adreces d'interès +Name[ca@valencia]=Adreces d'interés +Name[cs]=Záložky +Name[csb]=Załóżczi +Name[cy]=Nodau Tudalen +Name[da]=Bogmærker +Name[de]=Lesezeichen +Name[el]=Σελιδοδείκτες +Name[en_GB]=Bookmarks +Name[eo]=Legosignoj +Name[es]=Marcadores +Name[et]=Järjehoidjad +Name[eu]=Laster-markak +Name[fa]=چوب الفها +Name[fi]=Kirjanmerkit +Name[fr]=Signets +Name[fy]=Blêdwizers +Name[ga]=Leabharmharcanna +Name[gl]=Marcadores +Name[gu]=બુકમાર્ક્સ +Name[he]=סימניות +Name[hi]= पसंदीदा +Name[hne]=निसानी +Name[hr]=Oznake +Name[hsb]=Lubuški +Name[hu]=Könyvjelzők +Name[ia]=Marcatores de libro +Name[id]=Penanda +Name[is]=Bókamerki +Name[it]=Segnalibri +Name[ja]=ブックマーク +Name[ka]=სანიშნეები +Name[kk]=Бетбелгі +Name[km]=ចំណាំ +Name[kn]=ಅಂಕನಗಳು (ಬುಕ್ ಮಾರ್ಕ್) +Name[ko]=책갈피 +Name[ku]=Bijare +Name[lt]=Žymelės +Name[lv]=Grāmatzīmes +Name[mai]=पुस्तकचिह्न +Name[mk]=Обележувачи +Name[ml]=ഓര്‍മ്മക്കുറിപ്പുകള്‍ +Name[mr]=ओळखचिन्ह +Name[ms]=Tanda Buku +Name[nb]=Bokmerker +Name[nds]=Leesteken +Name[ne]=पुस्तकचिनो +Name[nl]=Bladwijzers +Name[nn]=Bokmerke +Name[oc]=Marca-paginas +Name[or]=ଚିହ୍ନିତ ସ୍ଥାନ +Name[pa]=ਬੁੱਕਮਾਰਕ +Name[pl]=Zakładki +Name[pt]=Favoritos +Name[pt_BR]=Favoritos +Name[ro]=Semne de carte +Name[ru]=Закладки +Name[se]=Girjemearkkat +Name[si]=පිටු සලකුණු +Name[sk]=Záložky +Name[sl]=Zaznamki +Name[sr]=Обележивачи +Name[sr@ijekavian]=Обиљеживачи +Name[sr@ijekavianlatin]=Obilježivači +Name[sr@latin]=Obeleživači +Name[sv]=Bokmärken +Name[ta]=நினைவுக்குறிகள் +Name[te]=పేజి గుర్తులు +Name[tg]=Хатчӯбҳо +Name[th]=ที่คั่นหน้า +Name[tr]=Yer İmleri +Name[ug]=خەتكۈشلەر +Name[uk]=Закладки +Name[uz]=Xatchoʻplar +Name[uz@cyrillic]=Хатчўплар +Name[vi]=Dấu nhớ +Name[wa]=Rimåkes +Name[xh]=Amanqaku eencwadi +Name[x-test]=xxBookmarksxx +Name[zh_CN]=书签 +Name[zh_TW]=書籤 +Comment=Configure the bookmarks home page +Comment[ar]=اضبط الصفحة البيت للعلامات +Comment[as]=পত্ৰচিহ্নৰ ঘৰৰ পৃষ্ঠা বিন্যাস কৰক +Comment[ast]=Configuración de la páxina inicial de los marcadores +Comment[be@latin]=Naładź chatniuju staronku z zakładkami +Comment[bg]=Настройване на отметките за уеб страниците +Comment[bn]=বুকমার্ক হোম-পেজ কনফিগার করুন +Comment[bs]=Podešavanje domaće stranice obeleživača +Comment[ca]=Configura la pàgina inicial de les adreces d'interès +Comment[ca@valencia]=Configura la pàgina inicial de les adreces d'interés +Comment[cs]=Nastavení domovské stránky záložek +Comment[csb]=Kònfigùracëjô załóżków +Comment[da]=Indstil bogmærke-startsiden +Comment[de]=Lesezeichen-Startseite einrichten +Comment[el]=Διαμόρφωση της αρχικής σελίδας των σελιδοδεικτών +Comment[en_GB]=Configure the bookmarks home page +Comment[eo]=Agordi la ĉefpaĝon de la legosignoj +Comment[es]=Configuración de la página inicial de los marcadores +Comment[et]=Järjehoidjate kodulehekülje seadistamine +Comment[eu]=Konfiguratu laster-marken hasierako orria +Comment[fi]=Muokkaa kirjanmerkkien kotisivua +Comment[fr]=Configure la page d'accueil des signets +Comment[fy]=De wepbagina's foar blêdwizers ynstelle +Comment[ga]=Cumraigh leathanach baile na leabharmharcanna +Comment[gl]=Configura a páxina de inicio dos marcadores +Comment[gu]=બુકમાર્ક્સ મુખપૃષ્ઠને રૂપરેખાંકિત કરો +Comment[he]=הגדרת עמוד־הבית של הסימניות +Comment[hi]=पसंदीदा मुख पृष्ठ कॉन्फ़िगर करें +Comment[hne]=घर पेज निसानी कान्फिगर करव +Comment[hr]=Konfiguriranje zabilješki početne web-stranice +Comment[hsb]=Domjacu stronu lubuškow konfigurować +Comment[hu]=A könyvjelzőoldal beállítása +Comment[ia]=Configura le pagina principal/domo del marcator de libros +Comment[id]=Atur penanda laman +Comment[is]=Stillta bókamerkjaheimasíðuna +Comment[it]=Configura la pagina di partenza dei segnalibri +Comment[ja]=ブックマークのホームページを設定 +Comment[kk]=Бетбелгілердің мекен парағын баптау +Comment[km]=កំណត់​រចនាសម្ព័ន្ធ​គេហទំព័រ​ចំណាំ +Comment[kn]=ಅಂಕನಗಳ (ಬುಕ್ ಮಾರ್ಕ್) ನೆಲೆ ಪುಟವನ್ನು ಸಂರಚಿಸಿ +Comment[ko]=책갈피 홈 페이지 설정 +Comment[ku]=Bijareyên malperê ava bike +Comment[lt]=Konfigūruoti žymelių namų puslapį +Comment[lv]=Šeit jūs varat konfigurēt grāmatzīmju mājas lapu +Comment[mai]=पसंदीदा मुख पृष्ठकेँ विन्यस्त करू +Comment[mk]=Конфигурирајте ја домашната страница за обележувачи +Comment[ml]=ബുക്മാര്‍ക്കുകളുടെ വീട് (ഹോം) താള്‍ ക്രമീകരിക്കുക +Comment[mr]=ओळखचिन्हाचे मुख्य पान संयोजीत करा +Comment[nb]=Still inn hjemmeside for bokmerker +Comment[nds]=De Leesteken-Tohuussiet instellen +Comment[nl]=De webpagina's voor bladwijzers instellen +Comment[nn]=Nettside for oppsett av bokmerke +Comment[or]=ଚିହ୍ନିତ ସ୍ଥାନ ମୂଖ୍ୟ ପୃଷ୍ଠା ବିନ୍ୟାସ କରନ୍ତୁ +Comment[pa]=ਬੁੱਕਮਾਰਕ ਮੁੱਖ ਪੇਜ਼ ਸੰਰਚਨਾ +Comment[pl]=Ustawienia strony domowej zakładek +Comment[pt]=Configurar a página inicial dos favoritos +Comment[pt_BR]=Configura a página inicial dos favoritos +Comment[ro]=Configurează pagina de pornire a semnelor de carte +Comment[ru]=Настройка страницы закладок +Comment[se]=Heivet girjemearkkaid ruovttosiidu +Comment[si]=පොත්සලකුණු නිවෙස් පිටුව සකසන්න +Comment[sk]=Nastavenie domovskej stránky záložiek +Comment[sl]=Nastavitve domače strani z zaznamki +Comment[sr]=Подешавање домаће странице обележивача +Comment[sr@ijekavian]=Подешавање домаће странице обиљеживача +Comment[sr@ijekavianlatin]=Podešavanje domaće stranice obilježivača +Comment[sr@latin]=Podešavanje domaće stranice obeleživača +Comment[sv]=Anpassa bokmärkenas hemsida +Comment[ta]=நினைவுப்பக்கங்களின் அகப்பக்கத்தை வடிவமை +Comment[tg]=Танзимоти хатчӯбҳои саҳифаи асосӣ +Comment[th]=ปรับแต่งการทำคั่นหน้าของหน้าเว็บ +Comment[tr]=Yer imleri ana sayfasını yapılandır +Comment[ug]=خەتكۈش باش بەت سەپلىمىسى +Comment[uk]=Налаштування домашньої сторінки закладок +Comment[wa]=Apontyî li pådje waibe måjhon des rmåkes +Comment[x-test]=xxConfigure the bookmarks home pagexx +Comment[zh_CN]=配置书签主页 +Comment[zh_TW]=設定書籤首頁 +X-KDE-Keywords=Bookmarks +X-KDE-Keywords[ar]=العلامات +X-KDE-Keywords[bg]=Отметки +X-KDE-Keywords[bs]=Zabilješke +X-KDE-Keywords[ca]=Adreces d'interès +X-KDE-Keywords[ca@valencia]=Adreces d'interés +X-KDE-Keywords[cs]=Záložky +X-KDE-Keywords[da]=Bogmærker +X-KDE-Keywords[de]=Lesezeichen +X-KDE-Keywords[el]=Σελιδοδείκτες +X-KDE-Keywords[en_GB]=Bookmarks +X-KDE-Keywords[es]=Marcadores +X-KDE-Keywords[et]=Järjehoidjad +X-KDE-Keywords[fi]=Kirjanmerkit +X-KDE-Keywords[fr]=Signets +X-KDE-Keywords[ga]=Leabharmharcanna +X-KDE-Keywords[gl]=Marcadores +X-KDE-Keywords[he]=סימניות +X-KDE-Keywords[hu]=Könyvjelzők +X-KDE-Keywords[ia]=Marcatores de libro +X-KDE-Keywords[id]=Penanda +X-KDE-Keywords[is]=Bókamerki +X-KDE-Keywords[it]=Segnalibri +X-KDE-Keywords[kk]=Бетбелгі +X-KDE-Keywords[km]=ចំណាំ +X-KDE-Keywords[ko]=책갈피 +X-KDE-Keywords[lt]=Žymelės +X-KDE-Keywords[lv]=Grāmatzīmes +X-KDE-Keywords[mr]=ओळखचिन्ह +X-KDE-Keywords[nb]=Bokmerker +X-KDE-Keywords[nds]=Leestekens +X-KDE-Keywords[nl]=Bladwijzers +X-KDE-Keywords[nn]=Bokmerke +X-KDE-Keywords[pa]=ਬੁੱਕਮਾਰਕ +X-KDE-Keywords[pl]=Zakładki +X-KDE-Keywords[pt]=Favoritos +X-KDE-Keywords[pt_BR]=Favoritos +X-KDE-Keywords[ro]=Semne de carte +X-KDE-Keywords[ru]=Закладки +X-KDE-Keywords[sk]=Záložky +X-KDE-Keywords[sl]=Zaznamki +X-KDE-Keywords[sr]=Bookmarks,обележивачи +X-KDE-Keywords[sr@ijekavian]=Bookmarks,обиљеживачи +X-KDE-Keywords[sr@ijekavianlatin]=Bookmarks,obilježivači +X-KDE-Keywords[sr@latin]=Bookmarks,obeleživači +X-KDE-Keywords[sv]=Bokmärken +X-KDE-Keywords[tr]=Yer İmleri +X-KDE-Keywords[ug]=خەتكۈشلەر +X-KDE-Keywords[uk]=закладки,закладка +X-KDE-Keywords[wa]=Rimarkes +X-KDE-Keywords[x-test]=xxBookmarksxx +X-KDE-Keywords[zh_CN]=Bookmarks,书签 +X-KDE-Keywords[zh_TW]=Bookmarks diff --git a/kcontrol/kio/bookmarks.h b/kcontrol/kio/bookmarks.h new file mode 100644 index 00000000..ce7bc824 --- /dev/null +++ b/kcontrol/kio/bookmarks.h @@ -0,0 +1,49 @@ +/* +Copyright (C) 2008 Xavier Vello + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#ifndef KCM_BOOKMARKS_H +#define KCM_BOOKMARKS_H + +// KDE +#include + +// Local +#include "ui_bookmarks.h" + +class BookmarksConfigModule : public KCModule +{ + Q_OBJECT + +public: + BookmarksConfigModule(QWidget *parent, const QVariantList &args); + ~BookmarksConfigModule(); + + void load(); + void save(); + void defaults(); + QString quickHelp() const; + +private Q_SLOTS: + void configChanged(); + +private: + Ui::BookmarksConfigUI ui; +}; + +#endif // KCM_BOOKMARKS_H + diff --git a/kcontrol/kio/bookmarks.ui b/kcontrol/kio/bookmarks.ui new file mode 100644 index 00000000..eba394e0 --- /dev/null +++ b/kcontrol/kio/bookmarks.ui @@ -0,0 +1,200 @@ + + + BookmarksConfigUI + + + + 0 + 0 + 704 + 636 + + + + + + + Bookmarks + + + + + + If this option is unchecked, bookmarks at the root of the hierarchy (not in a folder) are not displayed. +If checked, they are gathered in a "root" folder. + + + &Show bookmarks without folder + + + true + + + + + + + Sub-folders are shown within their parent by default. If you activate this option, sub-folders are displayed on their own. +It looks less nice but it may help if you have a very big folder you want to spread in two columns. + + + &Flatten bookmarks tree + + + + + + + Show a box with KDE places (Home, Network, ...). Useful if you use konqueror as a file manager. + + + Show system &places + + + true + + + + + + + + + + General Settings + + + + + + + + Folders are automatically distributed in several columns. The optimal number of columns depends on the width of the konqueror window and the number of bookmarks you have. + + + Number of columns to show: + + + + + + + + + + 1 + + + 4 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Disable it on slow system to disable background images. + + + Show folder &backgrounds + + + true + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + How much disk space is used to cache the pixmaps + + + Pixmap Cache + + + + + + true + + + Cache size: + + + sbCacheSize + + + + + + + true + + + kB + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + KIntSpinBox + QSpinBox +
    knuminput.h
    +
    + + KIntNumInput + QWidget +
    knuminput.h
    +
    +
    + + +
    diff --git a/kcontrol/kio/kio_ftprc.kcfg b/kcontrol/kio/kio_ftprc.kcfg new file mode 100644 index 00000000..610b2e68 --- /dev/null +++ b/kcontrol/kio/kio_ftprc.kcfg @@ -0,0 +1,22 @@ + + + + + + + + When FTP connections are passive the client connects to the server, instead of the other way round, so firewalls do not block the connection; old FTP servers may not support Passive FTP though. + false + + + + + While a file is being uploaded its extension is ".part". When fully uploaded it is renamed to its real name. + true + + + + diff --git a/kcontrol/kio/kio_ftprc.kcfgc b/kcontrol/kio/kio_ftprc.kcfgc new file mode 100644 index 00000000..7b5ca780 --- /dev/null +++ b/kcontrol/kio/kio_ftprc.kcfgc @@ -0,0 +1,5 @@ +File=kio_ftprc.kcfg +ClassName=KioFtp +Singleton=true +Mutators=true + diff --git a/kcontrol/kio/kioslave.kcfg b/kcontrol/kio/kioslave.kcfg new file mode 100644 index 00000000..6b8934df --- /dev/null +++ b/kcontrol/kio/kioslave.kcfg @@ -0,0 +1,50 @@ + + + + + + false + + + 10 + 2 + 3600 + + + 20 + 2 + 3600 + + + 15 + 2 + 3600 + + + 600 + 2 + 3600 + + + 5120 + + + false + + + + + + + + + + + + + + + diff --git a/kcontrol/kio/kioslave.kcfgc b/kcontrol/kio/kioslave.kcfgc new file mode 100644 index 00000000..fd522c04 --- /dev/null +++ b/kcontrol/kio/kioslave.kcfgc @@ -0,0 +1,5 @@ +File=kioslave.kcfg +ClassName=KioSlave +Singleton=true +Mutators=true + diff --git a/kcontrol/kio/kproxydlg.cpp b/kcontrol/kio/kproxydlg.cpp new file mode 100644 index 00000000..4a57e75a --- /dev/null +++ b/kcontrol/kio/kproxydlg.cpp @@ -0,0 +1,514 @@ +/* + kproxydlg.cpp - Proxy configuration dialog + + Copyright (C) 2001, 2011 Dawit Alemayehu + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License (GPL) version 2 as published by the Free Software + Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +// Own +#include "kproxydlg.h" + +// Local +#include "ksaveioconfig.h" + +// KDE +#include +#include +#include +#include +#include + +// Qt +#include +#include +#include +#include + + +#define QL1C(x) QLatin1Char(x) +#define QL1S(x) QLatin1String(x) + +#define ENV_HTTP_PROXY QL1S("HTTP_PROXY,http_proxy,HTTPPROXY,httpproxy,PROXY,proxy") +#define ENV_HTTPS_PROXY QL1S("HTTPS_PROXY,https_proxy,HTTPSPROXY,httpsproxy,PROXY,proxy") +#define ENV_FTP_PROXY QL1S("FTP_PROXY,ftp_proxy,FTPPROXY,ftpproxy,PROXY,proxy") +#define ENV_SOCKS_PROXY QL1S("SOCKS_PROXY,socks_proxy,SOCKSPROXY,socksproxy,PROXY,proxy") +#define ENV_NO_PROXY QL1S("NO_PROXY,no_proxy") + +K_PLUGIN_FACTORY_DECLARATION (KioConfigFactory) + + +class InputValidator : public QValidator +{ +public: + State validate(QString& input, int& pos) const { + if (input.isEmpty()) + return Acceptable; + + const QChar ch = input.at((pos > 0 ? pos - 1 : pos)); + if (ch.isSpace()) + return Invalid; + + return Acceptable; + } +}; + + +static QString manualProxyToText(const QLineEdit* edit, const QSpinBox* spinBox, const QChar& separator) +{ + QString value; + + value = edit->text(); + value += separator; + value += QString::number(spinBox->value()); + + return value; +} + +static void setManualProxyFromText(const QString& value, QLineEdit* edit, QSpinBox* spinBox) +{ + if (value.isEmpty()) + return; + + const QStringList values = value.split(QL1S(" ")); + edit->setText(values.at(0)); + bool ok = false; + const int num = values.at(1).toInt(&ok); + if (ok) { + spinBox->setValue(num); + } +} + +static void showSystemProxyUrl(QLineEdit* edit, QString* value) +{ + Q_ASSERT(edit); + Q_ASSERT(value); + + *value = edit->text(); + edit->setEnabled(false); + const QByteArray envVar(edit->text().toUtf8()); + edit->setText(QString::fromUtf8(qgetenv(envVar.constData()))); +} + +static QString proxyUrlFromInput(const QLineEdit* edit, const QSpinBox* spinBox) +{ + Q_ASSERT(edit); + Q_ASSERT(spinBox); + + QString proxyStr; + + if (edit->text().isEmpty()) + return proxyStr; + + KUriFilterData data; + data.setData(edit->text()); + data.setCheckForExecutables(false); + + if (KUriFilter::self()->filterUri(data, QStringList() << QL1S("kshorturifilter"))) { + KUrl url = data.uri(); + const int portNum = (spinBox->value() > 0 ? spinBox->value() : url.port()); + url.setPort(-1); + + proxyStr = url.url(); + proxyStr += QL1C(' '); + if (portNum > -1) { + proxyStr += QString::number(portNum); + } + } else { + proxyStr = edit->text(); + if (spinBox->value() > 0) { + proxyStr += QL1C(' '); + proxyStr += QString::number(spinBox->value()); + } + } + + return proxyStr; +} + +static void setProxyInformation(const QString& value, + int proxyType, + QLineEdit* manEdit, + QLineEdit* sysEdit, + QSpinBox* spinBox) +{ + const bool isSysProxy = (!value.contains(QL1C(' ')) && + !value.contains(QL1C('.')) && + !value.contains(QL1C(',')) && + !value.contains(QL1C(':'))); + + if (proxyType == KProtocolManager::EnvVarProxy || isSysProxy) { +#if defined(Q_OS_LINUX) || defined (Q_OS_UNIX) + sysEdit->setText(value); +#endif + return; + } + + if (spinBox) { + QString urlStr; + int portNum = -1; + int index = value.lastIndexOf(QL1C(' ')); + if (index == -1) + index = value.lastIndexOf(QL1C(':')); + + if (index > 0) { + bool ok = false; + portNum = value.mid(index+1).toInt(&ok); + if (!ok) { + portNum = -1; + } + urlStr = value.left(index).trimmed(); + } else { + urlStr = value.trimmed(); + } + + KUriFilterData data; + data.setData(urlStr); + data.setCheckForExecutables(false); + + if (KUriFilter::self()->filterUri(data, QStringList() << QL1S("kshorturifilter"))) { + KUrl url (data.uri()); + if (portNum == -1 && url.port() > -1) { + portNum = url.port(); + } + + url.setPort(-1); + url.setUser(QString()); + url.setPass(QString()); + url.setPath(QString()); + + manEdit->setText(url.url()); + } else { + manEdit->setText(urlStr); + } + + if (spinBox && portNum > -1) { + spinBox->setValue(portNum); + } + return; + } + + manEdit->setText(value); // Manual proxy exception... +} + +KProxyDialog::KProxyDialog(QWidget* parent, const QVariantList& args) + : KCModule(KioConfigFactory::componentData(), parent) +{ + Q_UNUSED(args); + mUi.setupUi(this); + + mUi.systemProxyGroupBox->setVisible(false); + mUi.manualProxyGroupBox->setVisible(false); + mUi.autoDetectButton->setVisible(false); + + InputValidator* v = new InputValidator; + mUi.manualProxyHttpEdit->setValidator(v); + mUi.manualProxyHttpsEdit->setValidator(v); + mUi.manualProxyFtpEdit->setValidator(v); + mUi.manualProxySocksEdit->setValidator(v); + mUi.manualNoProxyEdit->setValidator(v); + +#if defined(Q_OS_LINUX) || defined (Q_OS_UNIX) + connect(mUi.systemProxyRadioButton, SIGNAL(toggled(bool)), mUi.systemProxyGroupBox, SLOT(setVisible(bool))); +#else + mUi.autoDetectButton->setVisible(false); + connect(mUi.systemProxyRadioButton, SIGNAL(clicked()), SLOT(slotChanged())); +#endif + + // signals and slots connections + connect(mUi.noProxyRadioButton, SIGNAL(clicked()), SLOT(slotChanged())); + connect(mUi.manualProxyRadioButton, SIGNAL(clicked()), SLOT(slotChanged())); + connect(mUi.systemProxyRadioButton, SIGNAL(clicked()), SLOT(slotChanged())); + connect(mUi.noProxyRadioButton, SIGNAL(clicked()), SLOT(slotChanged())); + connect(mUi.useReverseProxyCheckBox, SIGNAL(clicked()), SLOT(slotChanged())); + connect(mUi.useSameProxyCheckBox, SIGNAL(clicked()), SLOT(slotChanged())); + + connect(mUi.manualProxyHttpEdit, SIGNAL(textChanged(QString)), SLOT(slotChanged())); + connect(mUi.manualProxyHttpsEdit, SIGNAL(textChanged(QString)), SLOT(slotChanged())); + connect(mUi.manualProxyFtpEdit, SIGNAL(textChanged(QString)), SLOT(slotChanged())); + connect(mUi.manualProxySocksEdit, SIGNAL(textChanged(QString)), SLOT(slotChanged())); + connect(mUi.manualNoProxyEdit, SIGNAL(textChanged(QString)), SLOT(slotChanged())); + + connect(mUi.manualProxyHttpSpinBox, SIGNAL(valueChanged(int)), SLOT(slotChanged())); + connect(mUi.manualProxyHttpsSpinBox, SIGNAL(valueChanged(int)), SLOT(slotChanged())); + connect(mUi.manualProxyFtpSpinBox, SIGNAL(valueChanged(int)), SLOT(slotChanged())); + connect(mUi.manualProxySocksSpinBox, SIGNAL(valueChanged(int)), SLOT(slotChanged())); + + connect(mUi.systemProxyHttpEdit, SIGNAL(textEdited(QString)), SLOT(slotChanged())); + connect(mUi.systemProxyHttpsEdit, SIGNAL(textEdited(QString)), SLOT(slotChanged())); + connect(mUi.systemProxyFtpEdit, SIGNAL(textEdited(QString)), SLOT(slotChanged())); + connect(mUi.systemProxySocksEdit, SIGNAL(textEdited(QString)), SLOT(slotChanged())); + connect(mUi.systemNoProxyEdit, SIGNAL(textEdited(QString)), SLOT(slotChanged())); +} + +KProxyDialog::~KProxyDialog() +{ +} + +void KProxyDialog::load() +{ + mProxyMap[QL1S("HttpProxy")] = KProtocolManager::proxyFor(QL1S("http")); + mProxyMap[QL1S("HttpsProxy")] = KProtocolManager::proxyFor(QL1S("https")); + mProxyMap[QL1S("FtpProxy")] = KProtocolManager::proxyFor(QL1S("ftp")); + mProxyMap[QL1S("SocksProxy")] = KProtocolManager::proxyFor(QL1S("socks")); + mProxyMap[QL1S("NoProxy")] = KSaveIOConfig::noProxyFor(); + + const int proxyType = KProtocolManager::proxyType(); + + // Make sure showEnvValueCheckBox is unchecked before setting proxy env var names + mUi.showEnvValueCheckBox->setChecked(false); + + setProxyInformation(mProxyMap.value(QL1S("HttpProxy")), proxyType, mUi.manualProxyHttpEdit, mUi.systemProxyHttpEdit, mUi.manualProxyHttpSpinBox); + setProxyInformation(mProxyMap.value(QL1S("HttpsProxy")), proxyType, mUi.manualProxyHttpsEdit, mUi.systemProxyHttpsEdit, mUi.manualProxyHttpsSpinBox); + setProxyInformation(mProxyMap.value(QL1S("FtpProxy")), proxyType, mUi.manualProxyFtpEdit, mUi.systemProxyFtpEdit, mUi.manualProxyFtpSpinBox); + setProxyInformation(mProxyMap.value(QL1S("SocksProxy")), proxyType, mUi.manualProxySocksEdit, mUi.systemProxySocksEdit, mUi.manualProxySocksSpinBox); + setProxyInformation(mProxyMap.value(QL1S("NoProxy")), proxyType, mUi.manualNoProxyEdit, mUi.systemNoProxyEdit, 0); + + // Check the "Use this proxy server for all protocols" if all the proxy URLs are the same... + const QString httpProxy(mUi.manualProxyHttpEdit->text()); + if (!httpProxy.isEmpty()) { + const int httpProxyPort = mUi.manualProxyHttpSpinBox->value(); + mUi.useSameProxyCheckBox->setChecked(httpProxy == mUi.manualProxyHttpsEdit->text() && + httpProxy == mUi.manualProxyFtpEdit->text() && + httpProxy == mUi.manualProxySocksEdit->text() && + httpProxyPort == mUi.manualProxyHttpsSpinBox->value() && + httpProxyPort == mUi.manualProxyFtpSpinBox->value() && + httpProxyPort == mUi.manualProxySocksSpinBox->value()); + } + + // Set use reverse proxy checkbox... + mUi.useReverseProxyCheckBox->setChecked((!mProxyMap.value(QL1S("NoProxy")).isEmpty() + && KProtocolManager::useReverseProxy())); + + switch (proxyType) { + case KProtocolManager::ManualProxy: + mUi.manualProxyRadioButton->setChecked(true); + break; + case KProtocolManager::EnvVarProxy: + mUi.systemProxyRadioButton->setChecked(true); + break; + case KProtocolManager::NoProxy: + default: + mUi.noProxyRadioButton->setChecked(true); + break; + } +} + +void KProxyDialog::save() +{ + const KProtocolManager::ProxyType lastProxyType = KProtocolManager::proxyType(); + KProtocolManager::ProxyType proxyType = KProtocolManager::NoProxy; + if (mUi.manualProxyRadioButton->isChecked()) { + proxyType = KProtocolManager::ManualProxy; + mProxyMap[QL1S("HttpProxy")] = proxyUrlFromInput(mUi.manualProxyHttpEdit, mUi.manualProxyHttpSpinBox); + mProxyMap[QL1S("HttpsProxy")] = proxyUrlFromInput(mUi.manualProxyHttpsEdit, mUi.manualProxyHttpsSpinBox); + mProxyMap[QL1S("FtpProxy")] = proxyUrlFromInput(mUi.manualProxyFtpEdit, mUi.manualProxyFtpSpinBox); + mProxyMap[QL1S("SocksProxy")] = proxyUrlFromInput(mUi.manualProxySocksEdit, mUi.manualProxySocksSpinBox); + mProxyMap[QL1S("NoProxy")] = mUi.manualNoProxyEdit->text(); + } else if (mUi.systemProxyRadioButton->isChecked()) { + proxyType = KProtocolManager::EnvVarProxy; + if (!mUi.showEnvValueCheckBox->isChecked()) { + mProxyMap[QL1S("HttpProxy")] = mUi.systemProxyHttpEdit->text(); + mProxyMap[QL1S("HttpsProxy")] = mUi.systemProxyHttpsEdit->text(); + mProxyMap[QL1S("FtpProxy")] = mUi.systemProxyFtpEdit->text(); + mProxyMap[QL1S("SocksProxy")] = mUi.systemProxySocksEdit->text(); + mProxyMap[QL1S("NoProxy")] = mUi.systemNoProxyEdit->text(); + } + else { + mProxyMap[QL1S("HttpProxy")] = mProxyMap.take(mUi.systemProxyHttpEdit->objectName()); + mProxyMap[QL1S("HttpsProxy")] = mProxyMap.take(mUi.systemProxyHttpsEdit->objectName()); + mProxyMap[QL1S("FtpProxy")] = mProxyMap.take(mUi.systemProxyFtpEdit->objectName()); + mProxyMap[QL1S("SocksProxy")] = mProxyMap.take(mUi.systemProxySocksEdit->objectName()); + mProxyMap[QL1S("NoProxy")] = mProxyMap.take(mUi.systemNoProxyEdit->objectName()); + } + } + + KSaveIOConfig::setProxyType(proxyType); + KSaveIOConfig::setUseReverseProxy(mUi.useReverseProxyCheckBox->isChecked()); + + // Save the common proxy setting... + KSaveIOConfig::setProxyFor(QL1S("http"), mProxyMap.value(QL1S("HttpProxy"))); + KSaveIOConfig::setProxyFor(QL1S("https"), mProxyMap.value(QL1S("HttpsProxy"))); + KSaveIOConfig::setProxyFor(QL1S("ftp"), mProxyMap.value(QL1S("FtpProxy"))); + KSaveIOConfig::setProxyFor(QL1S("socks"), mProxyMap.value(QL1S("SocksProxy"))); + + KSaveIOConfig::setNoProxyFor (mProxyMap.value(QL1S("NoProxy"))); + + KSaveIOConfig::updateRunningIOSlaves (this); + + emit changed (false); +} + +void KProxyDialog::defaults() +{ + mUi.noProxyRadioButton->setChecked(true); + + mUi.manualProxyHttpEdit->clear(); + mUi.manualProxyHttpsEdit->clear(); + mUi.manualProxyFtpEdit->clear(); + mUi.manualProxySocksEdit->clear(); + mUi.manualNoProxyEdit->clear(); + + mUi.manualProxyHttpSpinBox->setValue(0); + mUi.manualProxyHttpsSpinBox->setValue(0); + mUi.manualProxyFtpSpinBox->setValue(0); + mUi.manualProxySocksSpinBox->setValue(0); + + mUi.systemProxyHttpEdit->clear(); + mUi.systemProxyHttpsEdit->clear(); + mUi.systemProxyFtpEdit->clear(); + mUi.systemProxySocksEdit->clear(); + + emit changed (true); +} + +bool KProxyDialog::autoDetectSystemProxy(QLineEdit* edit, const QString& envVarStr, bool showValue) +{ + const QStringList envVars = envVarStr.split(QL1S(","), QString::SkipEmptyParts); + Q_FOREACH (const QString & envVar, envVars) { + const QByteArray envVarUtf8(envVar.toUtf8()); + const QByteArray envVarValue = qgetenv(envVarUtf8.constData()); + if (!envVarValue.isEmpty()) { + if (showValue) { + mProxyMap[edit->objectName()] = envVar; + edit->setText(envVarValue); + } else { + edit->setText(envVar); + } + edit->setEnabled(!showValue); + return true; + } + } + return false; +} + +void KProxyDialog::on_autoDetectButton_clicked() +{ + const bool showValue = mUi.showEnvValueCheckBox->isChecked(); + bool wasChanged = false; + + wasChanged |= autoDetectSystemProxy(mUi.systemProxyHttpEdit, ENV_HTTP_PROXY, showValue); + wasChanged |= autoDetectSystemProxy(mUi.systemProxyHttpsEdit, ENV_HTTPS_PROXY, showValue); + wasChanged |= autoDetectSystemProxy(mUi.systemProxyFtpEdit, ENV_FTP_PROXY, showValue); + wasChanged |= autoDetectSystemProxy(mUi.systemProxySocksEdit, ENV_SOCKS_PROXY, showValue); + wasChanged |= autoDetectSystemProxy(mUi.systemNoProxyEdit, ENV_NO_PROXY, showValue); + + if (wasChanged) + emit changed (true); +} + +void KProxyDialog::on_manualProxyHttpEdit_textChanged(const QString& text) +{ + mUi.useSameProxyCheckBox->setEnabled(!text.isEmpty()); +} + +void KProxyDialog::on_manualNoProxyEdit_textChanged (const QString& text) +{ + mUi.useReverseProxyCheckBox->setEnabled(!text.isEmpty()); +} + +void KProxyDialog::on_manualProxyHttpEdit_textEdited(const QString& text) +{ + if (!mUi.useSameProxyCheckBox->isChecked()) { + return; + } + + mUi.manualProxyHttpsEdit->setText(text); + mUi.manualProxyFtpEdit->setText(text); + mUi.manualProxySocksEdit->setText(text); +} + +void KProxyDialog::on_manualProxyHttpSpinBox_valueChanged (int value) +{ + if (!mUi.useSameProxyCheckBox->isChecked()) { + return; + } + + mUi.manualProxyHttpsSpinBox->setValue(value); + mUi.manualProxyFtpSpinBox->setValue(value); + mUi.manualProxySocksSpinBox->setValue(value); +} + +void KProxyDialog::on_showEnvValueCheckBox_toggled (bool on) +{ + if (on) { + showSystemProxyUrl(mUi.systemProxyHttpEdit, &mProxyMap[mUi.systemProxyHttpEdit->objectName()]); + showSystemProxyUrl(mUi.systemProxyHttpsEdit, &mProxyMap[mUi.systemProxyHttpsEdit->objectName()]); + showSystemProxyUrl(mUi.systemProxyFtpEdit, &mProxyMap[mUi.systemProxyFtpEdit->objectName()]); + showSystemProxyUrl(mUi.systemProxySocksEdit, &mProxyMap[mUi.systemProxySocksEdit->objectName()]); + showSystemProxyUrl(mUi.systemNoProxyEdit, &mProxyMap[mUi.systemNoProxyEdit->objectName()]); + return; + } + + mUi.systemProxyHttpEdit->setText(mProxyMap.take(mUi.systemProxyHttpEdit->objectName())); + mUi.systemProxyHttpEdit->setEnabled(true); + mUi.systemProxyHttpsEdit->setText(mProxyMap.take(mUi.systemProxyHttpsEdit->objectName())); + mUi.systemProxyHttpsEdit->setEnabled(true); + mUi.systemProxyFtpEdit->setText(mProxyMap.take(mUi.systemProxyFtpEdit->objectName())); + mUi.systemProxyFtpEdit->setEnabled(true); + mUi.systemProxySocksEdit->setText(mProxyMap.take(mUi.systemProxySocksEdit->objectName())); + mUi.systemProxySocksEdit->setEnabled(true); + mUi.systemNoProxyEdit->setText(mProxyMap.take(mUi.systemNoProxyEdit->objectName())); + mUi.systemNoProxyEdit->setEnabled(true); +} + +void KProxyDialog::on_useSameProxyCheckBox_clicked(bool on) +{ + if (on) { + mProxyMap[QL1S("ManProxyHttps")] = manualProxyToText (mUi.manualProxyHttpsEdit, mUi.manualProxyHttpsSpinBox, QL1C (' ')); + mProxyMap[QL1S("ManProxyFtp")] = manualProxyToText (mUi.manualProxyFtpEdit, mUi.manualProxyFtpSpinBox, QL1C (' ')); + mProxyMap[QL1S("ManProxySocks")] = manualProxyToText (mUi.manualProxySocksEdit, mUi.manualProxySocksSpinBox, QL1C (' ')); + + const QString& httpProxy = mUi.manualProxyHttpEdit->text(); + if (!httpProxy.isEmpty()) { + mUi.manualProxyHttpsEdit->setText(httpProxy); + mUi.manualProxyFtpEdit->setText(httpProxy); + mUi.manualProxySocksEdit->setText(httpProxy); + } + const int httpProxyPort = mUi.manualProxyHttpSpinBox->value(); + if (httpProxyPort > 0) { + mUi.manualProxyHttpsSpinBox->setValue(httpProxyPort); + mUi.manualProxyFtpSpinBox->setValue(httpProxyPort); + mUi.manualProxySocksSpinBox->setValue(httpProxyPort); + } + return; + } + + setManualProxyFromText(mProxyMap.take (QL1S("ManProxyHttps")), mUi.manualProxyHttpsEdit, mUi.manualProxyHttpsSpinBox); + setManualProxyFromText(mProxyMap.take (QL1S("ManProxyFtp")), mUi.manualProxyFtpEdit, mUi.manualProxyFtpSpinBox); + setManualProxyFromText(mProxyMap.take (QL1S("ManProxySocks")), mUi.manualProxySocksEdit, mUi.manualProxySocksSpinBox); +} + +void KProxyDialog::slotChanged() +{ + emit changed(true); +} + +QString KProxyDialog::quickHelp() const +{ + return i18n ("

    Proxy

    " + "

    A proxy server is an intermediate program that sits between " + "your machine and the Internet and provides services such as " + "web page caching and/or filtering.

    " + "

    Caching proxy servers give you faster access to sites you have " + "already visited by locally storing or caching the content of those " + "pages; filtering proxy servers, on the other hand, provide the " + "ability to block out requests for ads, spam, or anything else you " + "want to block.

    " + "

    Note: Some proxy servers provide both services.

    "); +} + +#include "moc_kproxydlg.cpp" + diff --git a/kcontrol/kio/kproxydlg.h b/kcontrol/kio/kproxydlg.h new file mode 100644 index 00000000..8829e23e --- /dev/null +++ b/kcontrol/kio/kproxydlg.h @@ -0,0 +1,61 @@ +/* + kproxydlg.h - Proxy configuration dialog + + Copyright (C) 2001, 2011 Dawit Alemayehu + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License (GPL) version 2 as published by the Free Software + Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KPROXYDLG_H +#define KPROXYDLG_H + +#include +#include "ui_kproxydlg.h" + +class KProxyDialog : public KCModule +{ + Q_OBJECT + +public: + KProxyDialog(QWidget* parent, const QVariantList& args); + ~KProxyDialog(); + + virtual void load(); + virtual void save(); + virtual void defaults(); + QString quickHelp() const; + +private Q_SLOTS: + void on_autoDetectButton_clicked(); + void on_showEnvValueCheckBox_toggled(bool); + void on_useSameProxyCheckBox_clicked(bool); + + void on_manualProxyHttpEdit_textChanged(const QString&); + void on_manualNoProxyEdit_textChanged(const QString&); + void on_manualProxyHttpEdit_textEdited(const QString&); + void on_manualProxyHttpSpinBox_valueChanged(int); + + void slotChanged(); + +private: + bool autoDetectSystemProxy(QLineEdit* edit, const QString& envVarStr, bool showValue); + + Ui::ProxyDialogUI mUi; + QStringList mNoProxyForList; + QMap mProxyMap; +}; + +#endif // KPROXYDLG_H diff --git a/kcontrol/kio/kproxydlg.ui b/kcontrol/kio/kproxydlg.ui new file mode 100644 index 00000000..a78c78c7 --- /dev/null +++ b/kcontrol/kio/kproxydlg.ui @@ -0,0 +1,868 @@ + + + ProxyDialogUI + + + + 0 + 0 + 513 + 578 + + + + <qt> +Setup proxy configuration. +<p> +A proxy server is an intermediate machine that sits between your computer and the Internet and provides services such as web page caching and filtering. Caching proxy servers give you faster access to web sites you have already visited by locally storing or caching those pages; filtering proxy servers usually provide the ability to block out requests for ads, spam, or anything else you want to block. +<p> +If you are uncertain whether or not you need to use a proxy server to connect to the Internet, consult your Internet service provider's setup guide or your system administrator. +</qt> + + + + + + + + + true + + + + QFormLayout::ExpandingFieldsGrow + + + 20 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + HTTP Proxy: + + + false + + + manualProxyHttpEdit + + + + + + + + + Enter the address of the HTTP proxy server. + + + + + + + Port: + + + manualProxyHttpSpinBox + + + + + + + + 32767 + 32767 + + + + Enter the port number of the HTTP proxy server. + + + 65536 + + + + + + + + + false + + + Use this proxy server for a&ll protocols + + + + + + + + 0 + 0 + + + + SSL Proxy: + + + false + + + manualProxyHttpsEdit + + + + + + + + + Enter the address of the HTTPS proxy server. + + + + + + + Port: + + + manualProxyHttpsSpinBox + + + + + + + Enter the port number of the HTTPS proxy server. + + + 65536 + + + + + + + + + + 0 + 0 + + + + FTP Proxy: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + false + + + manualProxyFtpEdit + + + + + + + + + Enter the address of the FTP proxy server. + + + + + + + Port: + + + manualProxyFtpSpinBox + + + + + + + Enter the port number of the FTP proxy server. + + + 65536 + + + + + + + + + + 0 + 0 + + + + SOCKS Proxy: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + manualProxySocksEdit + + + + + + + + + Enter the address of the SOCKS proxy server. + + + + + + + Port: + + + manualProxySocksSpinBox + + + + + + + Enter the port number of the SOCKS proxy server. + + + 65536 + + + + + + + + + <qt> +Enter the environment variable, e.g. <b>NO_PROXY</b>, used to store the addresses of sites for which the proxy server should not be used.<p> +Alternatively, you can click on the <b>"Auto Detect"</b> button to attempt an automatic discovery of this variable. +</qt> + + + Exceptions: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + false + + + manualNoProxyEdit + + + + + + + + 0 + 0 + + + + <qt> +<p>Enter a comma separated list of hostnames or ip addresses that should be excluded from using the above proxy settings.</p> +<p>If you want to exclude all hosts for a given domain, then simply enter the domain name preceded by a dot. For example, to exclude all hostnames for <i>kde.org</i>, enter <i>.kde.org</i>. Wildcard characters such as '*' or '?' are not supported and will have no effect.</p> +<p>Additionally, you can also enter IP addresses, e.g. 127.0.0.1 and IP addresses with a subnet, e.g. 192.168.0.1/24.</p> +</qt> + + + + + + + false + + + <qt> +Check this box if you want the above proxy settings to apply only to the addresses listed in the <i>Exceptions</i> list.</qt> + + + Use proxy settings only for addresses in the Exceptions list + + + + + + + + + + Qt::Vertical + + + QSizePolicy::MinimumExpanding + + + + 20 + 42 + + + + + + + + <qt>Attempt automatic discovery of the environment variables used for setting system wide proxy information.<p> This feature works by searching for commonly used variable names such as HTTP_PROXY, FTP_PROXY and NO_PROXY.</qt> + + + Auto D&etect + + + + + + + true + + + + 0 + 0 + + + + <qt><p>Use proxy settings defined on the system.</p> +<p>Some platforms offer system wide proxy configuration information and selecting this option allows you to use those settings.</p> +<p>On Mac platforms</p> +<p>On Windows platforms</p> +<p>On Unix and Linux platforms, such system proxy settings are usually defined through environment variables. The following environment variables are detected and used when present: <b>HTTP_PROXY</b>, <b>HTTPS_PROXY</b>, <b>FTP_PROXY</b>, <b>NO_PROXY</b>.</p> +</qt> + + + Use system proxy configuration: + + + + + + + true + + + + 0 + 0 + + + + Manually enter proxy server configuration information. + + + Use manually specified proxy configuration: + + + + + + + true + + + + 20 + + + 0 + + + 0 + + + 0 + + + + + <qt> +Enter the name of the environment variable, e.g. <b>HTTP_PROXY</b>, used to store the address of the HTTP proxy server.<p> +Alternatively, you can click on the <b>"Auto Detect"</b> button to attempt automatic discovery of this variable.</p> +</qt> + + + HTTP Proxy: + + + false + + + systemProxyHttpEdit + + + + + + + + 0 + 0 + + + + <qt> +Enter the name of the environment variable, e.g. <b>HTTP_PROXY</b>, used to store the address of the HTTP proxy server.<p> +Alternatively, you can click on the <b>"Auto Detect"</b> button to attempt automatic discovery of this variable.</p> +</qt> + + + + + + + <qt> +Enter the name of the environment variable, e.g. <b>HTTPS_PROXY</b>, used to store the address of the HTTPS proxy server.<p> +Alternatively, you can click on the <b>"Auto Detect"</b> button to attempt an automatic discovery of this variable.</p> +</qt> + + + SSL Proxy: + + + false + + + systemProxyHttpsEdit + + + + + + + + 0 + 0 + + + + <qt> +Enter the name of the environment variable, e.g. <b>HTTPS_PROXY</b>, used to store the address of the HTTPS proxy server.<p> +Alternatively, you can click on the <b>"Auto Detect"</b> button to attempt an automatic discovery of this variable.</p> +</qt> + + + + + + + <qt> +Enter the name of the environment variable, e.g. <b>FTP_PROXY</b>, used to store the address of the FTP proxy server.<p> +Alternatively, you can click on the <b>"Auto Detect"</b> button to attempt an automatic discovery of this variable.</p> +</qt> + + + FTP Proxy: + + + false + + + systemProxyFtpEdit + + + + + + + + 0 + 0 + + + + <qt> +Enter the name of the environment variable, e.g. <b>FTP_PROXY</b>, used to store the address of the FTP proxy server.<p> +Alternatively, you can click on the <b>"Auto Detect"</b> button to attempt an automatic discovery of this variable.</p> +</qt> + + + + + + + <qt> +Enter the name of the environment variable, e.g. <b>SOCKS_PROXY</b>, used to store the address of the SOCKS proxy server.<p> +Alternatively, you can click on the <b>"Auto Detect"</b> button to attempt an automatic discovery of this variable.</p> +</qt> + + + SOCKS Proxy: + + + false + + + systemProxySocksEdit + + + + + + + + 0 + 0 + + + + <qt>Enter the name of the environment variable, e.g. <b>SOCKS_PROXY</b>, used to store the address of the SOCKS proxy server.<p>Alternatively, you can click on the <b>&quot;Auto Detect&quot;</b> button to attempt an automatic discovery of this variable.</p></qt> + + + + + + + <qt> +Enter the environment variable, e.g. <b>NO_PROXY</b>, used to store the addresses of sites for which the proxy server should not be used.<p> +Alternatively, you can click on the <b>"Auto Detect"</b> button to attempt an automatic discovery of this variable. +</qt> + + + Exceptions: + + + false + + + systemNoProxyEdit + + + + + + + + 0 + 0 + + + + <qt>Enter the environment variable, e.g. <b>NO_PROXY</b>, used to store the addresses of sites for which the above proxy settings should not be used.<p>Alternatively, you can click on the <b>&quot;Auto Detect&quot;</b> button to attempt an automatic discovery of this variable.</p></qt> + + + + + + + Show the &value of the environment variables + + + + + + + + + + Connect to the Internet directly. + + + No Proxy + + + true + + + + + + + + KLineEdit + QLineEdit +
    klineedit.h
    +
    + + KIntSpinBox + QSpinBox +
    knuminput.h
    +
    +
    + + noProxyRadioButton + systemProxyRadioButton + autoDetectButton + systemProxyHttpEdit + systemProxyHttpsEdit + systemProxyFtpEdit + systemProxySocksEdit + systemNoProxyEdit + showEnvValueCheckBox + manualProxyRadioButton + manualProxyHttpEdit + manualProxyHttpSpinBox + useSameProxyCheckBox + manualProxyHttpsEdit + manualProxyHttpsSpinBox + manualProxyFtpEdit + manualProxyFtpSpinBox + manualProxySocksEdit + manualProxySocksSpinBox + manualNoProxyEdit + useReverseProxyCheckBox + + + kpushbutton.h + + + + + systemProxyRadioButton + toggled(bool) + systemProxyGroupBox + setVisible(bool) + + + 97 + 121 + + + 23 + 150 + + + + + manualProxyRadioButton + toggled(bool) + manualProxyGroupBox + setVisible(bool) + + + 76 + 304 + + + 18 + 333 + + + + + useSameProxyCheckBox + toggled(bool) + manualProxyHttpsEdit + setDisabled(bool) + + + 249 + 370 + + + 250 + 384 + + + + + useSameProxyCheckBox + toggled(bool) + manualProxyFtpEdit + setDisabled(bool) + + + 218 + 364 + + + 215 + 414 + + + + + useSameProxyCheckBox + toggled(bool) + manualProxySocksEdit + setDisabled(bool) + + + 191 + 362 + + + 187 + 440 + + + + + showEnvValueCheckBox + toggled(bool) + systemNoProxyEdit + setDisabled(bool) + + + 352 + 282 + + + 353 + 259 + + + + + showEnvValueCheckBox + toggled(bool) + systemProxySocksEdit + setDisabled(bool) + + + 275 + 278 + + + 273 + 231 + + + + + showEnvValueCheckBox + toggled(bool) + systemProxyFtpEdit + setDisabled(bool) + + + 299 + 279 + + + 299 + 206 + + + + + showEnvValueCheckBox + toggled(bool) + systemProxyHttpsEdit + setDisabled(bool) + + + 349 + 280 + + + 336 + 185 + + + + + showEnvValueCheckBox + toggled(bool) + systemProxyHttpEdit + setDisabled(bool) + + + 272 + 279 + + + 236 + 156 + + + + + systemProxyRadioButton + toggled(bool) + autoDetectButton + setVisible(bool) + + + 186 + 119 + + + 433 + 124 + + + + + useSameProxyCheckBox + toggled(bool) + manualProxyHttpsSpinBox + setDisabled(bool) + + + 155 + 361 + + + 464 + 385 + + + + + useSameProxyCheckBox + toggled(bool) + manualProxyFtpSpinBox + setDisabled(bool) + + + 139 + 364 + + + 451 + 421 + + + + + useSameProxyCheckBox + toggled(bool) + manualProxySocksSpinBox + setDisabled(bool) + + + 157 + 363 + + + 438 + 449 + + + + +
    diff --git a/kcontrol/kio/ksaveioconfig.cpp b/kcontrol/kio/ksaveioconfig.cpp new file mode 100644 index 00000000..2aa1c8e1 --- /dev/null +++ b/kcontrol/kio/ksaveioconfig.cpp @@ -0,0 +1,167 @@ +/* + Copyright (C) 2001 Dawit Alemayehu + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +// Own +#include "ksaveioconfig.h" + +// Qt +#include + +// KDE +#include +#include +#include +#include +#include +#include + +class KSaveIOConfigPrivate +{ +public: + KSaveIOConfigPrivate (); + ~KSaveIOConfigPrivate (); + + KConfig* config; +}; + +K_GLOBAL_STATIC(KSaveIOConfigPrivate, d) + +KSaveIOConfigPrivate::KSaveIOConfigPrivate () + : config(0) +{ +} + +KSaveIOConfigPrivate::~KSaveIOConfigPrivate () +{ + delete config; +} + +static KConfig* config() +{ + if (!d->config) + d->config = new KConfig("kioslaverc", KConfig::NoGlobals); + + return d->config; +} + +void KSaveIOConfig::reparseConfiguration () +{ + delete d->config; + d->config = 0; +} + +void KSaveIOConfig::setReadTimeout( int _timeout ) +{ + KConfigGroup cfg (config(), QString()); + cfg.writeEntry("ReadTimeout", qMax(MIN_TIMEOUT_VALUE,_timeout)); + cfg.sync(); +} + +void KSaveIOConfig::setConnectTimeout( int _timeout ) +{ + KConfigGroup cfg (config(), QString()); + cfg.writeEntry("ConnectTimeout", qMax(MIN_TIMEOUT_VALUE,_timeout)); + cfg.sync(); +} + +void KSaveIOConfig::setProxyConnectTimeout( int _timeout ) +{ + KConfigGroup cfg (config(), QString()); + cfg.writeEntry("ProxyConnectTimeout", qMax(MIN_TIMEOUT_VALUE,_timeout)); + cfg.sync(); +} + +void KSaveIOConfig::setResponseTimeout( int _timeout ) +{ + KConfigGroup cfg (config(), QString()); + cfg.writeEntry("ResponseTimeout", qMax(MIN_TIMEOUT_VALUE,_timeout)); + cfg.sync(); +} + + +void KSaveIOConfig::setMarkPartial( bool _mode ) +{ + KConfigGroup cfg (config(), QString()); + cfg.writeEntry( "MarkPartial", _mode ); + cfg.sync(); +} + +void KSaveIOConfig::setMinimumKeepSize( int _size ) +{ + KConfigGroup cfg (config(), QString()); + cfg.writeEntry( "MinimumKeepSize", _size ); + cfg.sync(); +} + +void KSaveIOConfig::setAutoResume( bool _mode ) +{ + KConfigGroup cfg (config(), QString()); + cfg.writeEntry( "AutoResume", _mode ); + cfg.sync(); +} + +void KSaveIOConfig::setUseReverseProxy( bool mode ) +{ + KConfigGroup cfg (config(), "Proxy Settings"); + cfg.writeEntry("ReversedException", mode); + cfg.sync(); +} + +void KSaveIOConfig::setProxyType(KProtocolManager::ProxyType type) +{ + KConfigGroup cfg (config(), "Proxy Settings"); + cfg.writeEntry("ProxyType", static_cast(type)); + cfg.sync(); +} + +QString KSaveIOConfig::noProxyFor() +{ + KConfigGroup cfg(config(), "Proxy Settings"); + return cfg.readEntry("NoProxyFor"); +} + +void KSaveIOConfig::setNoProxyFor( const QString& _noproxy ) +{ + KConfigGroup cfg (config(), "Proxy Settings"); + cfg.writeEntry("NoProxyFor", _noproxy); + cfg.sync(); +} + +void KSaveIOConfig::setProxyFor( const QString& protocol, + const QString& _proxy ) +{ + KConfigGroup cfg (config(), "Proxy Settings"); + cfg.writeEntry(protocol.toLower() + "Proxy", _proxy); + cfg.sync(); +} + +void KSaveIOConfig::updateRunningIOSlaves (QWidget *parent) +{ + // Inform all running io-slaves about the changes... + // if we cannot update, ioslaves inform the end user... + QDBusMessage message = QDBusMessage::createSignal("/KIO/Scheduler", "org.kde.KIO.Scheduler", "reparseSlaveConfiguration"); + message << QString(); + if (!QDBusConnection::sessionBus().send(message)) + { + KMessageBox::information (parent, + i18n("You have to restart the running applications " + "for these changes to take effect."), + i18nc("@title:window", "Update Failed")); + } +} diff --git a/kcontrol/kio/ksaveioconfig.h b/kcontrol/kio/ksaveioconfig.h new file mode 100644 index 00000000..b7974474 --- /dev/null +++ b/kcontrol/kio/ksaveioconfig.h @@ -0,0 +1,65 @@ +/* + Copyright (C) 2001 Dawit Alemayehu + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KSAVEIO_CONFIG_H_ +#define KSAVEIO_CONFIG_H_ + +#include + +#include + +namespace KSaveIOConfig +{ +/* Reload config file (kioslaverc) */ +void reparseConfiguration(); + +/** Timeout Settings */ +void setReadTimeout (int); + +void setConnectTimeout (int); + +void setProxyConnectTimeout (int); + +void setResponseTimeout (int); + + +/** Proxy Settings */ +void setUseReverseProxy (bool); + +void setProxyType (KProtocolManager::ProxyType); + +void setProxyFor (const QString&, const QString&); + +QString noProxyFor(); +void setNoProxyFor (const QString&); + + +/** Miscellaneous Settings */ +void setMarkPartial (bool); + +void setMinimumKeepSize (int); + +void setAutoResume (bool); + +/** Update all running io-slaves */ +void updateRunningIOSlaves (QWidget* parent = 0L); + +} + +#endif diff --git a/kcontrol/kio/main.cpp b/kcontrol/kio/main.cpp new file mode 100644 index 00000000..2a01ff5b --- /dev/null +++ b/kcontrol/kio/main.cpp @@ -0,0 +1,46 @@ +// (c) Torben Weis 1998 +// (c) David Faure 1998 +/* + * main.cpp for creating the konqueror kio kcm modules + * + * Copyright (C) 2000,2001,2009 Alexander Neundorf + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +// Qt +#include +#include +#include +#include + +// KDE +#include +#include +#include +#include + +// Local +#include "netpref.h" +#include "kproxydlg.h" +#include "bookmarks.h" + +K_PLUGIN_FACTORY(KioConfigFactory, + registerPlugin("netpref"); + registerPlugin("proxy"); + registerPlugin("bookmarks"); + ) +K_EXPORT_PLUGIN(KioConfigFactory("kcmkio")) + diff --git a/kcontrol/kio/netpref.cpp b/kcontrol/kio/netpref.cpp new file mode 100644 index 00000000..14bd548b --- /dev/null +++ b/kcontrol/kio/netpref.cpp @@ -0,0 +1,175 @@ + +// Own +#include "netpref.h" + +// Qt +#include +#include +#include +#include +#include + +// KDE +#include +#include +#include +#include +#include +#include + +// Local +#include "ksaveioconfig.h" + +#define MAX_TIMEOUT_VALUE 3600 + +K_PLUGIN_FACTORY_DECLARATION(KioConfigFactory) + +KIOPreferences::KIOPreferences(QWidget *parent, const QVariantList &) + :KCModule(KioConfigFactory::componentData(), parent) +{ + QVBoxLayout* mainLayout = new QVBoxLayout( this ); + mainLayout->setMargin(0); + gb_Timeout = new QGroupBox( i18n("Timeout Values"), this ); + gb_Timeout->setWhatsThis( i18np("Here you can set timeout values. " + "You might want to tweak them if your " + "connection is very slow. The maximum " + "allowed value is 1 second." , + "Here you can set timeout values. " + "You might want to tweak them if your " + "connection is very slow. The maximum " + "allowed value is %1 seconds.", MAX_TIMEOUT_VALUE)); + mainLayout->addWidget( gb_Timeout ); + + QFormLayout* timeoutLayout = new QFormLayout(gb_Timeout); + sb_socketRead = new KIntNumInput( this ); + sb_socketRead->setSuffix( ki18np( " second", " seconds" ) ); + connect(sb_socketRead, SIGNAL(valueChanged(int)), SLOT(configChanged())); + timeoutLayout->addRow(i18n( "Soc&ket read:" ), sb_socketRead); + + sb_proxyConnect = new KIntNumInput( 0, this ); + sb_proxyConnect->setSuffix( ki18np( " second", " seconds" ) ); + connect(sb_proxyConnect, SIGNAL(valueChanged(int)), SLOT(configChanged())); + timeoutLayout->addRow(i18n( "Pro&xy connect:" ), sb_proxyConnect); + + sb_serverConnect = new KIntNumInput( 0, this ); + sb_serverConnect->setSuffix( ki18np( " second", " seconds" ) ); + connect(sb_serverConnect, SIGNAL(valueChanged(int)), SLOT(configChanged())); + timeoutLayout->addRow(i18n("Server co&nnect:"), sb_serverConnect); + + sb_serverResponse = new KIntNumInput( 0, this ); + sb_serverResponse->setSuffix( ki18np( " second", " seconds" ) ); + connect(sb_serverResponse, SIGNAL(valueChanged(int)), SLOT(configChanged())); + timeoutLayout->addRow(i18n("&Server response:"), sb_serverResponse); + + gb_Ftp = new QGroupBox( i18n( "FTP Options" ), this ); + mainLayout->addWidget( gb_Ftp ); + QVBoxLayout* ftpLayout = new QVBoxLayout(gb_Ftp); + + cb_ftpEnablePasv = new QCheckBox( i18n( "Enable passive &mode (PASV)" ), this ); + cb_ftpEnablePasv->setWhatsThis( i18n("Enables FTP's \"passive\" mode. " + "This is required to allow FTP to " + "work from behind firewalls.") ); + connect(cb_ftpEnablePasv, SIGNAL(toggled(bool)), SLOT(configChanged())); + ftpLayout->addWidget(cb_ftpEnablePasv); + + cb_ftpMarkPartial = new QCheckBox( i18n( "Mark &partially uploaded files" ), this ); + cb_ftpMarkPartial->setWhatsThis( i18n( "

    Marks partially uploaded FTP " + "files.

    When this option is " + "enabled, partially uploaded files " + "will have a \".part\" extension. " + "This extension will be removed " + "once the transfer is complete.

    ") ); + connect(cb_ftpMarkPartial, SIGNAL(toggled(bool)), SLOT(configChanged())); + ftpLayout->addWidget(cb_ftpMarkPartial); + + gb_Misc = new QGroupBox( i18n( "Miscellaneous" ), this ); + mainLayout->addWidget( gb_Misc ); + QFormLayout* miscLayout = new QFormLayout(gb_Misc); + + sb_minimumKeepSize = new KIntNumInput( 0, this ); + sb_minimumKeepSize->setSuffix( ki18np( " bytes", " bytes" ) ); + connect(sb_minimumKeepSize, SIGNAL(valueChanged(int)), SLOT(configChanged())); + miscLayout->addRow(i18n( "Minimum keep size:" ), sb_minimumKeepSize); + + cb_AutoResume = new QCheckBox( this ); + cb_AutoResume->setWhatsThis( i18n("

    Transfers will be auto-resumed.

    ") ); + connect(cb_AutoResume, SIGNAL(toggled(bool)), SLOT(configChanged())); + miscLayout->addRow(i18n( "Enable auto-resuming" ), cb_AutoResume); + + mainLayout->addStretch( 1 ); +} + +KIOPreferences::~KIOPreferences() +{ +} + +void KIOPreferences::load() +{ + KProtocolManager proto; + + sb_socketRead->setRange( MIN_TIMEOUT_VALUE, MAX_TIMEOUT_VALUE ); + sb_serverResponse->setRange( MIN_TIMEOUT_VALUE, MAX_TIMEOUT_VALUE ); + sb_serverConnect->setRange( MIN_TIMEOUT_VALUE, MAX_TIMEOUT_VALUE ); + sb_proxyConnect->setRange( MIN_TIMEOUT_VALUE, MAX_TIMEOUT_VALUE ); + + sb_socketRead->setValue( proto.readTimeout() ); + sb_serverResponse->setValue( proto.responseTimeout() ); + sb_serverConnect->setValue( proto.connectTimeout() ); + sb_proxyConnect->setValue( proto.proxyConnectTimeout() ); + + KConfig config( "kio_ftprc", KConfig::NoGlobals ); + cb_ftpEnablePasv->setChecked( !config.group("").readEntry( "DisablePassiveMode", false ) ); + cb_ftpMarkPartial->setChecked( config.group("").readEntry( "MarkPartial", true ) ); + + sb_minimumKeepSize->setValue( proto.minimumKeepSize() ); + cb_AutoResume->setChecked( proto.autoResume() ); + + emit changed( false ); +} + +void KIOPreferences::save() +{ + KSaveIOConfig::setReadTimeout( sb_socketRead->value() ); + KSaveIOConfig::setResponseTimeout( sb_serverResponse->value() ); + KSaveIOConfig::setConnectTimeout( sb_serverConnect->value() ); + KSaveIOConfig::setProxyConnectTimeout( sb_proxyConnect->value() ); + + KConfig config("kio_ftprc", KConfig::NoGlobals); + config.group("").writeEntry( "DisablePassiveMode", !cb_ftpEnablePasv->isChecked() ); + config.group("").writeEntry( "MarkPartial", cb_ftpMarkPartial->isChecked() ); + config.sync(); + + KSaveIOConfig::setMinimumKeepSize( sb_minimumKeepSize->value() ); + KSaveIOConfig::setAutoResume( cb_AutoResume->isChecked() ); + + KSaveIOConfig::updateRunningIOSlaves(this); + + emit changed( false ); +} + +void KIOPreferences::defaults() +{ + sb_socketRead->setValue( DEFAULT_READ_TIMEOUT ); + sb_serverResponse->setValue( DEFAULT_RESPONSE_TIMEOUT ); + sb_serverConnect->setValue( DEFAULT_CONNECT_TIMEOUT ); + sb_proxyConnect->setValue( DEFAULT_PROXY_CONNECT_TIMEOUT ); + + cb_ftpEnablePasv->setChecked( true ); + cb_ftpMarkPartial->setChecked( true ); + + sb_minimumKeepSize->setValue( DEFAULT_MINIMUM_KEEP_SIZE ); + cb_AutoResume->setChecked( false ); + + emit changed(true); +} + +QString KIOPreferences::quickHelp() const +{ + return i18n("

    Network Preferences

    Here you can define" + " the behavior of KDE programs when using Internet" + " and network connections. If you experience timeouts" + " or use a modem to connect to the Internet, you might" + " want to adjust these settings." ); +} + +#include "moc_netpref.cpp" diff --git a/kcontrol/kio/netpref.desktop b/kcontrol/kio/netpref.desktop new file mode 100644 index 00000000..ccde0496 --- /dev/null +++ b/kcontrol/kio/netpref.desktop @@ -0,0 +1,242 @@ +[Desktop Entry] +Exec=kcmshell4 netpref +Icon=preferences-system-network-connection +Type=Service +X-KDE-ServiceTypes=KCModule +X-DocPath=kcontrol/netpref/index.html + +X-KDE-Library=kcm_kio +X-KDE-PluginKeyword=netpref +X-KDE-ParentApp=kcontrol + +X-KDE-System-Settings-Parent-Category=network-settings +X-KDE-Weight=60 + +Name=Connection Preferences +Name[af]=Koppeling Voorkeure +Name[ar]=تفضيلات الإتصال +Name[as]=সংযোগৰ পছন্দ +Name[ast]=Preferencies de conexón +Name[be]=Настаўленні злучэння +Name[be@latin]=Ułasnyja nałady spałučeńnia +Name[bg]=Настройки на връзката +Name[bn]=যোগাযোগ পছন্দসমূহ +Name[bn_IN]=সংযোগ সংক্রান্ত পছন্দসই মান +Name[br]=Kefluniadur ar gevreadenn +Name[bs]=Postavke povezivanja +Name[ca]=Preferències de connexió +Name[ca@valencia]=Preferències de connexió +Name[cs]=Nastavení připojení +Name[csb]=Kònfigùracëjô sparłãczeniów +Name[da]=Forbindelsesindstillinger +Name[de]=Verbindungseinstellungen +Name[el]=Προτιμήσεις σύνδεσης +Name[en_GB]=Connection Preferences +Name[eo]=Agordo de konekto +Name[es]=Preferencias de conexión +Name[et]=Ühenduse seadistused +Name[eu]=Konexioaren hobespenak +Name[fa]=تنظیمات اتصال +Name[fi]=Yhteysasetukset +Name[fr]=Préférences de connexion +Name[fy]=Ferbiningfoarkar +Name[ga]=Sainroghanna Ceangail +Name[gl]=Preferencias da conexión +Name[gu]=જોડાણ પ્રાથમિકતાઓ +Name[he]=העדפות התחברות +Name[hi]=कनेक्शन प्राथमिकताएँ +Name[hne]=कनेक्सन प्राथमिकता +Name[hr]=Postavke povezivanja +Name[hsb]=Preferency za zwisk +Name[hu]=Hálózati paraméterek +Name[ia]=Preferentias de connexion +Name[id]=Pengaturan Koneksi +Name[is]=Stillingar tenginga +Name[it]=Preferenze delle connessioni +Name[ja]=接続設定 +Name[ka]=კავშირის თვისებები +Name[kk]=Қосылым параметрлері +Name[km]=ចំណូល​ចិត្ត​ការ​តភ្ជាប់ +Name[kn]=ಸಂಪರ್ಕ ಐಚ್ಛಿಕಗಳು (ಪ್ರಿಫರೆನ್ಸ್) +Name[ko]=연결 설정 +Name[ku]=Vebijêrkên Girêdanê +Name[lt]=Prisijungimo pasirinkimai +Name[lv]=Savienojumu parametri +Name[mai]=कनेक्शन प्राथमिकतासभ +Name[mk]=Параметри на поврзувањето +Name[ml]=കണക്ഷന്‍ മുന്‍ഗണനകള്‍ +Name[mr]=प्राधान्यता जुळवणी +Name[ms]=Keutamaan Sambungan +Name[nb]=Forbindelsesinnstillinger +Name[nds]=Instellen för de Verbinnen +Name[ne]=जडान प्राथमिकता +Name[nl]=Verbindingsvoorkeuren +Name[nn]=Innstillingar for samband +Name[or]=ସଂଯୋଗ ପସନ୍ଦ +Name[pa]=ਕੁਨੈਕਸ਼ਨ ਪਸੰਦ +Name[pl]=Ustawienia połączeń +Name[pt]=Preferências da Ligação +Name[pt_BR]=Preferências da conexão +Name[ro]=Preferințe conexiune +Name[ru]=Параметры сети +Name[se]=Oktavuohta oidimat +Name[si]=සම්බන්ධතා අභිමතයන් +Name[sk]=Predvoľby pripojenia +Name[sl]=Možnosti povezave +Name[sr]=Поставке повезивања +Name[sr@ijekavian]=Поставке повезивања +Name[sr@ijekavianlatin]=Postavke povezivanja +Name[sr@latin]=Postavke povezivanja +Name[sv]=Anslutningsinställningar +Name[ta]=இணைப்பு முன்னுரிமைகள் +Name[te]=అనుసంధానం అభీష్టాలు +Name[tg]=Танзимоти пайвастшавӣ +Name[th]=ตั้งค่าสำหรับการเชื่อมต่อ +Name[tr]=Bağlantı Tercihleri +Name[ug]=باغلىنىش مايىللىقى +Name[uk]=Параметри з'єднання +Name[uz]=Aloqa parametrlari +Name[uz@cyrillic]=Алоқа параметрлари +Name[vi]=Tùy thích kết nối +Name[wa]=Preferinces di raloyaedje +Name[x-test]=xxConnection Preferencesxx +Name[zh_CN]=连接首选项 +Name[zh_TW]=連線偏好設定 + +Comment=Configure generic network preferences, like timeout values +Comment[af]=Konfigureer generies netwerk voorkeure, hou van tydverstreke waardes +Comment[ar]=اضبط تفضيلات الشبكة العامة، مثل قيم مهلة الانتظار +Comment[ast]=Configuración de les preferencies xenériques de rede, como los tiempos d'espera llímite +Comment[be]=Настаўленні агульных уласцівасцяў сеткі, напр., тэрмінаў чакання +Comment[be@latin]=Hałoŭnyja sietkavyja nałady, naprykład, terminy čakańnia vynikaŭ ad aperacyjaŭ. +Comment[bg]=Настройване на общите мрежови параметри +Comment[bn]=বিবিধ নেটওয়ার্ক সেটিংস কনফিগারেশন, যথা টাইম-আউট মানসমূহ +Comment[bn_IN]=নেটওয়ার্ক সংক্রান্ত সাধান মান, যেমন সময়সীমা, কনফিগার করুন +Comment[bs]=Podešavanje generičkih parametara mreže, npr. prekovremenâ +Comment[ca]=Configuració de les preferències genèriques de la xarxa, com ara els valors per als temps d'expiració +Comment[ca@valencia]=Configuració de les preferències genèriques de la xarxa, com ara els valors per als temps d'expiració +Comment[cs]=Nastavení obecných parametrů sítě, např. časových limitů +Comment[csb]=Kònfigùracëjô sécowëch nastôwów jakno np. limitu czasu +Comment[cy]=Ffurfweddu dewisiadau rhwydwaith generig, fel gwerthoedd goramser +Comment[da]=Generelle netværksindstillinger såsom værdier for tidsudløb +Comment[de]=Allgemeine Netzwerkeinstellungen vornehmen, wie z. B. Werte für Zeitüberschreitungen +Comment[el]=Διαμόρφωση γενικών προτιμήσεων δικτύου, όπως τιμές χρονικών ορίων +Comment[en_GB]=Configure generic network preferences, like timeout values +Comment[eo]=Agordi la ĝeneralajn retopciojn, kiel tempolimojn ktp. +Comment[es]=Configuración de las preferencias genéricas de red, como los valores de tiempo de espera +Comment[et]=Üldised võrguseadistused +Comment[eu]=Konfiguratu sarearen hobespen orokorrak (denbora-mugen balioak, besteak beste) +Comment[fa]=پیکربندی تنظیمات شبکه محلی، مانند مقادیر اتمام‌ وقت +Comment[fi]=Yleiset verkkoasetukset +Comment[fr]=Configuration générique du réseau comme, par exemple, les valeurs des délais d'attente +Comment[fy]=ynstelle fan algemiene netwurkfoarkar, sa as tiidslimytwearden +Comment[ga]=Cumraigh sainroghanna ginearálta líonra, mar shampla teorainneacha ama +Comment[gl]=Configurar as preferencias xenéricas de rede, como os tempos límite +Comment[gu]=નેટવર્ક પ્રાથમિકતાઓ., જેવીકે સમયસમાપ્તિ કિંમતો રૂપરેખાંકિત કરો +Comment[he]=הגדרת העדפות רשת כלליות, כגון ערכי timeout +Comment[hi]=जेनेरिक नेटवर्क प्राथमिकताएँ, जैसे टाइम-आउट मूल्य- कॉन्फ़िगर करें +Comment[hne]=जेनेरिक नेटवर्क प्राथमिकता, जइसन कि टाइम-आउट मूल्य - कान्फिगर करव +Comment[hr]=Konfiguriranje generičkih mrežnih vrijednosti, poput vremena prekoračenja +Comment[hsb]=Powšitkowne syćowe preferency kaž časowe mjezy připrawić +Comment[hu]=A hálózati paraméterek, például a várakozási idők beállítása +Comment[ia]=Configura le preferentia de rete generic, como valores pro expirationes de tempore +Comment[id]=Atur pengaturan jaringan generik, seperti nilai waktu habis +Comment[is]=Tilgreina almennar netstillingar eins og hámarkstíma o.þ.h +Comment[it]=Configura le preferenze di rete generiche, come i tempi di scadenza +Comment[ja]=タイムアウト値などの一般的なネットワーク設定 +Comment[ka]=ქსელის ზოგადი პარამეტრები, ისეთი როგორიცაა ტაიმაუტი +Comment[kk]=Күту уақыты секілді желінің негізгі параметрлерін баптау +Comment[km]=កំណត់​រចនាសម្ព័ន្ធ​​ចំណូល​ចិត្ត​បណ្ដាញ​ទូទៅ ដូចជា​តម្លៃ​អស់​ពេល​ជា​ដើម +Comment[kn]=ಸಮಯೋಲ್ಲಂಘನ ಮೌಲ್ಯದಂತಹ (ಟೈಮ್ ಔಟ್) ಅಲಿಪ್ತ (ಜೆನೆರಿಕ್) ಜಾಲ ಐಚ್ಛಿಕಗಳನ್ನು (ಪ್ರಿಫರೆನ್ಸ್) ಸಂರಚಿಸು +Comment[ko]=제한 시간 값 같이 일반 네트워크에서 기본이 되는 값 설정하기 +Comment[ku]=Vebijêrkên torê generîk ên wekî nirxên demajoyê veava bike +Comment[lt]=Konfigūruoti bendrus tinklo pasirinkimus, tokius, kaip laiko limitai +Comment[lv]=Konfigurē pamata tīkla iestatījumus, piem., noildzes vērtības +Comment[mai]=जेनेरिक नेटवर्क प्राथमिकतासभ, जहिना टाइम-आउट मूल्यसभ केँ- बिन्यस्त करू +Comment[mk]=Конфигурирајте ги општите мрежни параметри, како тајмаут вредности +Comment[ml]=എത്ര സമയം കാത്തുനില്‍ക്കണം എന്നതുപോലുള്ള പൊതുവായുള്ള ശൃംഖലയുമായി ബന്ധപ്പെട്ട മുന്‍ഗണനകള്‍ ക്രമീകരിയ്ക്കുക +Comment[mr]=जेनेरिक संजाळ प्राधान्यता, जसे की टाइम-आउट मूल्य संयोजीत करा +Comment[ms]=Konfigur keutamaan rangkaian generik, seperti nilai waktu rehat +Comment[nb]=Sett opp generelle nettverksinnstillinger. F.eks. verdier for tidsavbrudd +Comment[nds]=Standard-Nettwarkinstellen, as t.B. Tietgrenzen +Comment[ne]=समय समाप्ति मान जस्तै जेनेरिक सञ्जाल प्राथमिकता कन्फिगर गर्नुहोस् +Comment[nl]=Instellen van algemene netwerkvoorkeuren, zoals tijdslimietwaarden +Comment[nn]=Oppsett av generelle nettverksinnstillingar, som for eksempel tidsgrenser +Comment[or]=ବର୍ଗୀୟ ନେଟୱର୍କ ପସନ୍ଦଗୁଡ଼ିକୁ ବିନ୍ୟାସ କରନ୍ତୁ, ସମୟ ସମାପ୍ତ ମୂଲ୍ୟଗୁଡ଼ିକ ପରି +Comment[pa]=ਨੈੱਟਵਰਕ ਪਸੰਦ ਸੰਰਚਨਾ, ਜਿਵੇਂ ਟਾਈਮ-ਆਉਟ ਮੁੱਲ +Comment[pl]=Ustawienia niskopoziomowych ustawień sieciowych np. limitu czasu +Comment[pt]=Configuração das opções gerais da rede, p.ex. tempos-limite +Comment[pt_BR]=Configura as preferências gerais da rede, tais como os valores de tempo de espera +Comment[ro]=Configurează opțiuni de rețea precum valorile temporizărilor +Comment[ru]=Общие параметры сети, такие как значения времени ожидания +Comment[se]=Oppalaš fierbmeoidimat, nugo áigemearit +Comment[si]=සාමාන්‍ය ජාල රුචි සකසන්න, කල් ඉකුත්වීමේ අගයන් වැනි +Comment[sk]=Nastavenie všeobecných parametrov siete, napr. hodnoty časových limitov +Comment[sl]=Nastavitve splošnih možnosti omrežja, kot na primer vrednosti časovnih omejitev +Comment[sr]=Подешавање генеричких параметара мреже, нпр. прековременâ +Comment[sr@ijekavian]=Подешавање генеричких параметара мреже, нпр. прековременâ +Comment[sr@ijekavianlatin]=Podešavanje generičkih parametara mreže, npr. prekovremenâ +Comment[sr@latin]=Podešavanje generičkih parametara mreže, npr. prekovremenâ +Comment[sv]=Anpassa allmänna nätverksinställningar, som tidsgränser +Comment[ta]=காலவதி மதிப்பு போன்ற பொதுவான பிணைய முன்னுரிமைகளை வடிவமை +Comment[te]=కాలముగింపు విలువల వంటి, సాదారణ నెట్వర్‍క్ అభీష్టాలను ఆకృతీకరించుము +Comment[tg]=Танзимоти хусусиятҳои шабака, монанди вақти фаъол +Comment[th]=ปรับแต่งค่าระบบเครือข่ายทั่วไป เช่น ค่าหมดเวลา เป็นต้น +Comment[tr]=Zaman aşımı değerleri gibi genel ağ tercihlerini yapılandır +Comment[ug]=ئادەتتىكى تور مايىللىق سەپلىمىسى، مۆھلەت قىممىتىگە ئوخشاش +Comment[uk]=Налаштування загальних параметрів мережі, зокрема значень тайм-аутів +Comment[uz]=Taymaut qiymatiga oʻxshagan tarmoq parametrlarni moslash +Comment[uz@cyrillic]=Таймаут қийматига ўхшаган тармоқ параметрларни мослаш +Comment[vi]=Cấu hình tùy thích mạng giống loài, như giá trị thời hạn +Comment[wa]=Apontyî les preferinces rantoele djenerikes, come les tins d' tårdjaedje +Comment[xh]=Qwalasela iinketho zomsebenzi womnatha ka wonke-wonke, njengamaxabiso ophelelo lwexesha +Comment[x-test]=xxConfigure generic network preferences, like timeout valuesxx +Comment[zh_CN]=配置通用网络首选项,例如超时值 +Comment[zh_TW]=設定一般網路喜好設定,例如逾時值 + +X-KDE-Keywords=timeout,iopref,netpref,network preferences,ftp +X-KDE-Keywords[bg]=изчакване,мрежови настройки,timeout,iopref,netpref,network preferences,ftp +X-KDE-Keywords[bs]=timeout,iopref,netpref,network preferences,ftp +X-KDE-Keywords[ca]=temps d'espera,iopref,netpref,preferències de xarxa,ftp +X-KDE-Keywords[ca@valencia]=temps d'espera,iopref,netpref,preferències de xarxa,ftp +X-KDE-Keywords[da]=tidsudløb,timeout,iopref,netpref,netværksindstillinger,ftp +X-KDE-Keywords[de]=Zeitlimit,Zeitüberschreitung,Netzwerkeinstellungen,Ein-/Ausgabemodul-Einstellungen,FTP +X-KDE-Keywords[el]=χρονικά όρια,iopref,netpref,προτιμήσεις δικτύου,ftp +X-KDE-Keywords[en_GB]=timeout,iopref,netpref,network preferences,ftp +X-KDE-Keywords[es]=tiempo de espera,iopref,netpref,preferencias de red,ftp +X-KDE-Keywords[et]=aegumine,iopref,netpref,võrk,eelistused,ftp +X-KDE-Keywords[fi]=aikakatkaisu,iopref,netpref,verkkoasetukset,ftp +X-KDE-Keywords[fr]=délai dépassé, iopref, netpref, paramètres réseau, ftp +X-KDE-Keywords[ga]=timeout,iopref,netpref,sainroghanna líonra,ftp +X-KDE-Keywords[gl]=tempo límite,iopref,netpref,rede,preferencias,ftp +X-KDE-Keywords[he]=timeout,iopref,netpref,network preferences,ftp +X-KDE-Keywords[hu]=időtúllépés,iopref,netpref,hálózati beállítások,ftp +X-KDE-Keywords[ia]=expiration,iopref,prefderete,rete,preferentias,ftp +X-KDE-Keywords[id]=waktu habis,iopref,netpref,pengaturan jaringan,ftp +X-KDE-Keywords[it]=scadenza,iopref,netpref,preferenze di rete,ftp +X-KDE-Keywords[kk]=timeout,iopref,netpref,network preferences,ftp +X-KDE-Keywords[km]=អស់​ពេល iopref netpref ចំណូលចិត្ត​បណ្ដាញ ftp +X-KDE-Keywords[ko]=timeout,iopref,netpref,network preferences,ftp,시간,시간 초과,네트워크 설정 +X-KDE-Keywords[lt]=skirtasis laikas,ionust,tinklonust,tinklo nustatymai,ftp +X-KDE-Keywords[lv]=noildze,io iestat,tīkla iestat,tīkla iestatījumi,ftp +X-KDE-Keywords[nb]=tidsavbrudd,iopref,netpref,nettverksoppsett,ftp +X-KDE-Keywords[nds]=Tietgrenz,In- un Utgaav-Instellen,Nettwarkinstellen,Nettwark,Instellen,FTP +X-KDE-Keywords[nl]=timeout,iopref,netpref,netwerk voorkeuren,ftp +X-KDE-Keywords[pl]=czas oczekiwania,iopref,netpref,ustawienia sieci,ftp +X-KDE-Keywords[pt]=tempo-limite,iopref,netpref,preferências de rede,ftp +X-KDE-Keywords[pt_BR]=tempo-limite,iopref,netpref,preferências de rede,ftp +X-KDE-Keywords[ro]=limită de timp,expirare,iopref,netpref,preferințe rețea,ftp +X-KDE-Keywords[ru]=timeout,iopref,netpref,network preferences,ftp,задержка,время ожидания,таймаут,параметры сети +X-KDE-Keywords[sk]=timeout,iopref,netpref,sieťové nastavenia,ftp +X-KDE-Keywords[sl]=časovna omejitev,iopref,netpref,možnosti omrežja,ftp +X-KDE-Keywords[sr]=timeout,iopref,netpref,network preferences,ftp,прековреме,мрежне поставке,ФТП +X-KDE-Keywords[sr@ijekavian]=timeout,iopref,netpref,network preferences,ftp,прековреме,мрежне поставке,ФТП +X-KDE-Keywords[sr@ijekavianlatin]=timeout,iopref,netpref,network preferences,ftp,prekovreme,mrežne postavke,FTP +X-KDE-Keywords[sr@latin]=timeout,iopref,netpref,network preferences,ftp,prekovreme,mrežne postavke,FTP +X-KDE-Keywords[sv]=tidsgräns,iopref,netpref,nätverk,inställningar,ftp +X-KDE-Keywords[tr]=zamanaşımı,girdi çıktı tercihleri,ağ tercihleri,ağ ayarları,ftp +X-KDE-Keywords[uk]=timeout;iopref;netpref;network preferences;ftp;очікування;тайм-аут;мережа;параметри;налаштування +X-KDE-Keywords[wa]=astådje,iopref,netpref,preferinces del rantoele,ftp +X-KDE-Keywords[x-test]=xxtimeout,iopref,netpref,network preferences,ftpxx +X-KDE-Keywords[zh_CN]=timeout,iopref,netpref,network preferences,ftp,超时,网络首选项 +X-KDE-Keywords[zh_TW]=timeout,iopref,netpref,network preferences,ftp diff --git a/kcontrol/kio/netpref.h b/kcontrol/kio/netpref.h new file mode 100644 index 00000000..a79635b6 --- /dev/null +++ b/kcontrol/kio/netpref.h @@ -0,0 +1,45 @@ +#ifndef NETPREF_H +#define NETPREF_H + +#include + +#include +#include + +class KIntNumInput; + +class KIOPreferences : public KCModule +{ + Q_OBJECT + +public: + KIOPreferences(QWidget *parent, const QVariantList &args); + ~KIOPreferences(); + + void load(); + void save(); + void defaults(); + + QString quickHelp() const; + +protected Q_SLOTS: + void configChanged() { emit changed(true); } + +private: + QGroupBox* gb_Ftp; + QGroupBox* gb_Timeout; + QGroupBox* gb_Misc; + + QCheckBox* cb_ftpEnablePasv; + QCheckBox* cb_ftpMarkPartial; + + KIntNumInput* sb_socketRead; + KIntNumInput* sb_proxyConnect; + KIntNumInput* sb_serverConnect; + KIntNumInput* sb_serverResponse; + + KIntNumInput* sb_minimumKeepSize; + QCheckBox* cb_AutoResume; +}; + +#endif // NETPREF_H diff --git a/kcontrol/kio/proxy.desktop b/kcontrol/kio/proxy.desktop new file mode 100644 index 00000000..c78fa689 --- /dev/null +++ b/kcontrol/kio/proxy.desktop @@ -0,0 +1,248 @@ +[Desktop Entry] +Type=Service +X-KDE-ServiceTypes=KCModule +X-DocPath=kcontrol/proxy/index.html +Icon=preferences-system-network-proxy +Exec=kcmshell4 proxy + +X-KDE-Library=kcm_kio +X-KDE-PluginKeyword=proxy +X-KDE-ParentApp=kcontrol + +X-KDE-System-Settings-Parent-Category=network-settings +X-KDE-Weight=50 + +Name=Proxy +Name[af]=Volmag +Name[ar]=الوكيل +Name[as]=প্ৰশমক +Name[ast]=Proxy +Name[be]=Проксі +Name[be@latin]=Proxy +Name[bg]=Прокси сървър +Name[bn]=প্রক্সি +Name[bn_IN]=প্রক্সি +Name[br]=Proksi +Name[bs]=Proksi +Name[ca]=Intermediari +Name[ca@valencia]=Intermediari +Name[cs]=Proxy +Name[csb]=Pòstrzédnik (Proxy) +Name[cy]=Dirprwy +Name[da]=Proxy +Name[de]=Proxy +Name[el]=Διαμεσολαβητής +Name[en_GB]=Proxy +Name[eo]=Prokuriloj +Name[es]=Proxy +Name[et]=Puhverserver +Name[eu]=Proxy-a +Name[fa]=پیشکار +Name[fi]=Välityspalvelin +Name[fr]=Serveur mandataire (proxy) +Name[fy]=Proxy +Name[ga]=Seachfhreastalaí +Name[gl]=Proxy +Name[gu]=પ્રોક્સી +Name[he]=מתווך +Name[hi]=प्रॉक्सी +Name[hne]=प्राक्सी +Name[hr]=Proxy +Name[hsb]=Proxy +Name[hu]=Proxy +Name[ia]=Proxy +Name[id]=Proxy +Name[is]=Milliþjónn +Name[it]=Proxy +Name[ja]=プロキシ +Name[ka]=პროქსი +Name[kk]=Прокси +Name[km]=ប្រូកស៊ី +Name[kn]=ಪ್ರಾತಿನಿಧಿಕ (ಪ್ರಾಕ್ಸಿ) +Name[ko]=프록시 +Name[ku]=Cihgir +Name[lt]=Įgaliotasis serveris +Name[lv]=Starpniekserveris +Name[mai]=प्राक्सी +Name[mk]=Прокси +Name[ml]=പ്രോക്സി +Name[mr]=प्रॉक्सी +Name[ms]=Proksi +Name[nb]=Mellomtjener +Name[nds]=Proxy +Name[ne]=प्रोक्सी +Name[nl]=Proxy +Name[nn]=Mellomtenar +Name[oc]=Proxy +Name[or]=ପ୍ରକ୍ସି +Name[pa]=ਪਰਾਕਸੀ +Name[pl]=Pośrednik +Name[pt]=Proxy +Name[pt_BR]=Proxy +Name[ro]=Proxy +Name[ru]=Прокси-сервер +Name[se]=Gaskabálvá +Name[si]=ප්‍රොක්සි +Name[sk]=Proxy +Name[sl]=Posredniški strežnik +Name[sr]=Прокси +Name[sr@ijekavian]=Прокси +Name[sr@ijekavianlatin]=Proksi +Name[sr@latin]=Proksi +Name[sv]=Proxyservrar +Name[ta]=போலி +Name[te]=ప్రాక్సి +Name[tg]=Proxy +Name[th]=พร็อกซี +Name[tr]=Vekil Sunucu +Name[ug]=ۋاكالەتچى +Name[uk]=Проксі +Name[uz]=Proksi +Name[uz@cyrillic]=Прокси +Name[vi]=Ủy nhiệm +Name[wa]=Procsi +Name[xh]=Umntu onegunya lokusebenzela omnye +Name[x-test]=xxProxyxx +Name[zh_CN]=代理 +Name[zh_TW]=代理伺服器 + +Comment=Configure the proxy servers used +Comment[af]=Konfigureer die volmag bedieners gebruik word +Comment[ar]=اضبط خوادم الوكيل المستخدمة +Comment[as]=ব্যৱহাৰ কৰা প্ৰশমক সেৱক বিন্যাস কৰক +Comment[ast]=Configuración de los sirvidores proxy usaos +Comment[be]=Настаўленні сервераў проксі +Comment[be@latin]=Naładź dziejnyja proxy-servery +Comment[bg]=Настройки на прокси сървъра +Comment[bn]=ব্যবহৃত প্রক্সি সার্ভার কনফিগার করুন +Comment[bn_IN]=ব্যবহৃত প্রক্সি সার্ভার(গুলি) কনফিগার করুন +Comment[br]=Kefluniañ ar servijeroù proksi implijet +Comment[bs]=Podešavanje proksi servera +Comment[ca]=Configura els servidors intermediaris emprats +Comment[ca@valencia]=Configura els servidors intermediaris emprats +Comment[cs]=Nastavení proxy serverů +Comment[csb]=Kònfigùracëjô pòstrzédniczącëch serwerów (proxy) +Comment[cy]=Ffurfweddu gweinyddion dirprwyol i'w defnyddio +Comment[da]=Indstil de proxyservere der bruges +Comment[de]=Proxy-Server einrichten +Comment[el]=Διαμορφώστε τους διαμεσολαβητές που χρησιμοποιούνται +Comment[en_GB]=Configure the proxy servers used +Comment[eo]=Agordi la prokurilon +Comment[es]=Configuración de los servidores proxy usados +Comment[et]=Kasutatavate puhverserverite seadistamine +Comment[eu]=Konfiguratu erabilitako proxy zerbitzariak +Comment[fa]=پیکربندی پیشکارهای استفاده‌شده +Comment[fi]=Välityspalvelimien asetukset +Comment[fr]=Configuration des serveurs mandataires (proxy) utilisés +Comment[fy]=Hjir kinne jo de Proxy-tsjinner ynstelle +Comment[ga]=Cumraigh na seachfhreastalaithe +Comment[gl]=Configurar os servidores proxy empregados +Comment[gu]=ઉપયોગ કરેલ પ્રોક્સી સર્વરો રૂપરેખાંકિત કરો +Comment[he]=הגדרת שרתי הפרוקסי שבשימוש +Comment[hi]=उपयोग में प्रॉक्सी सर्वर कॉन्फ़िगर करें +Comment[hne]=उपयोग मं प्राक्सी सर्वर कान्फिगर करव +Comment[hr]=Konfiguriranje proxy poslužitelja +Comment[hsb]=Nastajić, kotre proxyje so wužiwaja +Comment[hu]=A proxy kiszolgálók beállításai +Comment[ia]=Configura le servitores de proxy usate +Comment[id]=Atur server proxy yang digunakan +Comment[is]=Stilla milliþjóna sem á að nota +Comment[it]=Configura i server proxy da usare +Comment[ja]=プロキシサーバの設定 +Comment[ka]=გამოყენებული პროქსების კონფიგურაცია +Comment[kk]=Қолданатын прокси серверлерді баптау +Comment[km]=កំណត់​រចនាសម្ព័ន្ធ​ម៉ាស៊ីន​បម្រើ​ប្រូកស៊ី​​ដែល​បាន​ប្រើ +Comment[kn]=ಬಳಸಲಾದ ಪ್ರಾತಿನಿಧಿಕ ಪರಿಚಾರಕಗಳನ್ನು (ಪ್ರಾಕ್ಸಿ ಸರ್ವರ್) ಸಂರಚಿಸು +Comment[ko]=사용할 프록시 서버 설정 +Comment[ku]=Proksiyan ava bike +Comment[lt]=Konfigūruoti naudojamus įgaliotuosius serverius +Comment[lv]=Šeit jūs varat konfigurēt izmantojamos starpniekserverus +Comment[mai]=उपयोग मे प्राक्सी सर्वर केँ बिन्यस्त करू +Comment[mk]=Конфигурирајте ги прокси-серверите кои се користат +Comment[ml]=ഉപയോഗിച്ചിരിയ്ക്കുന്ന പ്രോക്സി സെര്‍വറുകള്‍ ക്രമീകരിയ്ക്കുക +Comment[mr]=वापरलेले प्रॉक्सी सर्व्हर्स संयोजीत करा +Comment[ms]=Konfigur pelayan proksi yang digunakan +Comment[nb]=Sett opp mellomtjenere +Comment[nds]=De bruukten Proxies instellen +Comment[ne]=प्रयोग गरिएको प्रोक्सी सर्भर कन्फिगर गर्नुहोस् +Comment[nl]=Hier kunt u de Proxy-servers instellen +Comment[nn]=Oppsett av mellomtenarar +Comment[or]=ବ୍ୟବହୃତ ପ୍ରକ୍ସି ସର୍ଭରକୁ ବିନ୍ୟାସ କରନ୍ତୁ +Comment[pa]=ਵਰਤੇ ਜਾਂਦੇ ਪਰਾਕਸੀ ਸਰਵਰਾਂ ਦੀ ਸੰਰਚਨਾ +Comment[pl]=Ustawienia serwerów pośredniczących (proxy) +Comment[pt]=Configurar os servidores 'proxy' usados +Comment[pt_BR]=Configura os servidores proxy usados +Comment[ro]=Configurează serverele proxy utilizate +Comment[ru]=Настройка соединения через прокси-сервер +Comment[se]=Heivet gaskabálváid mat geavahuvvojit +Comment[si]=භාවිතවන ප්‍රොක්සි සේවා දැකසුම් +Comment[sk]=Nastavenie proxy serverov +Comment[sl]=Nastavitve posredniških strežnikov +Comment[sr]=Подешавање прокси сервера +Comment[sr@ijekavian]=Подешавање прокси сервера +Comment[sr@ijekavianlatin]=Podešavanje proksi servera +Comment[sr@latin]=Podešavanje proksi servera +Comment[sv]=Anpassa proxyservrar som används +Comment[ta]=பயன்படுத்தப்படும் போலி வழங்கிகளை வடிவமை +Comment[te]=ఉపయోగించిన ప్రోక్సీ సేవికలను ఆకృతీకరించుము +Comment[tg]=Танзимоти хидматҳои proxy истифодашуда +Comment[th]=ปรับแต่งเซิร์ฟเวอร์พร็อกซี +Comment[tr]=Kullanılan vekil sunucuları yapılandır +Comment[ug]=ئىشلەتكەن ۋاكالەتچى مۇلازىمېتىر سەپلىمىسى +Comment[uk]=Налаштування проксі-сервера +Comment[uz]=Proksi serverlarini moslash +Comment[uz@cyrillic]=Прокси серверларини мослаш +Comment[vi]=Cấu hình máy phục vụ ủy nhiệm được sử dụng +Comment[wa]=Apontyî les sierveus procsi eployîs +Comment[xh]=Qwalasela indlela abasebenziswa ngayo abancedisi be proxy +Comment[x-test]=xxConfigure the proxy servers usedxx +Comment[zh_CN]=配置所用的代理服务器 +Comment[zh_TW]=設定使用的代理伺服器 + +X-KDE-Keywords=Proxy,Proxy server,Firewall,Squid +X-KDE-Keywords[bg]=Прокси,Прокси сървър,Защитна стена,Proxy,Proxy server,Firewall,Squid +X-KDE-Keywords[bs]=Proxy,Proxy server,Firewall,Squid +X-KDE-Keywords[ca]=Intermediari,Servidor intermediari,Tallafocs,Squid +X-KDE-Keywords[ca@valencia]=Intermediari,Servidor intermediari,Tallafocs,Squid +X-KDE-Keywords[da]=Proxy,Proxy-server,Firewall,Squid +X-KDE-Keywords[de]=Proxy,Proxy-Server,Firewall,Squid +X-KDE-Keywords[el]=Διαμεσολαβητής,εξυπηρετητής διαμεσολάβησης,τείχος προστασίας,squid +X-KDE-Keywords[en_GB]=Proxy,Proxy server,Firewall,Squid +X-KDE-Keywords[es]=Proxy,Servidor proxy,Cortafuegos,Squid +X-KDE-Keywords[et]=Puhver,Puhverserver,Tulemüür,Squid +X-KDE-Keywords[fi]=Proxy,Välitys,Välityspalvelin,Firewall,Palomuuri,Squid +X-KDE-Keywords[fr]=Serveur mandataire, serveur (proxy), pare-feu, squid +X-KDE-Keywords[ga]=Seachfhreastalaí,Balla Dóiteáin,Squid +X-KDE-Keywords[gl]=Proxy,servidor proxy,devasa,Squid +X-KDE-Keywords[he]=Proxy,Proxy server,Firewall,Squid,פרוקסי,מתווך,שרת,חמת אש, חומה +X-KDE-Keywords[hu]=Proxy,Proxy kiszolgáló,Tűzfal,Squid +X-KDE-Keywords[ia]=Proxy,Servitor de Proxy,Talia Foco,Squid +X-KDE-Keywords[id]=Proxy,server Proxy,Firewall,Squid +X-KDE-Keywords[it]=Proxy,Server proxy server,Firewall,Squid +X-KDE-Keywords[kk]=Proxy,Proxy server,Firewall,Squid,Прокси +X-KDE-Keywords[km]=ប្រូកស៊ី ម៉ាស៊ីន​បម្រើ​ប្រូកស៊ី ជញ្ជាំង​ភ្លើង Squid +X-KDE-Keywords[ko]=Proxy,Proxy server,Firewall,Squid,proxy,프록시,프록시 서버,방화벽 +X-KDE-Keywords[lt]=Įgaliotasis serveris,Įgaliotasis,Užkarda,Squid +X-KDE-Keywords[nb]=Mellomtjener,Brannmur,Squid +X-KDE-Keywords[nds]=Proxy,Proxy-Server,Nettdiek,Squid +X-KDE-Keywords[nl]=Proxy,proxy-server,firewall,squid +X-KDE-Keywords[pa]=ਪਰਾਕਸੀ,ਪਰਾਕਸੀ ਸਰਵਰ,ਫਾਇਰਵਾਲ,ਸੁਕਵਿਡ +X-KDE-Keywords[pl]=Pośrednik,Serwer pośrednika,Zapora sieciowa,Squid +X-KDE-Keywords[pt]=Proxy,servidor proxy,Firewall,Squid +X-KDE-Keywords[pt_BR]=Proxy,Servidor proxy,Firewall,Squid +X-KDE-Keywords[ro]=Proxy,server proxy,paravan de protecție,squid +X-KDE-Keywords[ru]=Proxy,Proxy server,Firewall,Squid,прокси,прокси-сервер,брандмауэр,защитный экран +X-KDE-Keywords[sk]=Proxy,Proxy server,Firewall,Squid +X-KDE-Keywords[sl]=Proxy,Posredniški strežnik,Požarni zid,Squid +X-KDE-Keywords[sr]=Proxy,Proxy server,Firewall,Squid,прокси,прокси сервер,Сквид,заштитни зид +X-KDE-Keywords[sr@ijekavian]=Proxy,Proxy server,Firewall,Squid,прокси,прокси сервер,Сквид,заштитни зид +X-KDE-Keywords[sr@ijekavianlatin]=Proxy,Proxy server,Firewall,Squid,proksi,proksi server,Squid,zaštitni zid +X-KDE-Keywords[sr@latin]=Proxy,Proxy server,Firewall,Squid,proksi,proksi server,Squid,zaštitni zid +X-KDE-Keywords[sv]=Proxy,Proxyserver,Brandvägg,Squid +X-KDE-Keywords[tr]=Vekil,Vekil sunucu,Firewall,Squid +X-KDE-Keywords[uk]=Proxy;Proxy server;Firewall;Squid;proxy;проксі;сервер;проксі-сервер;брандмауер;файрвол +X-KDE-Keywords[wa]=Procsi,sierveu Procsi,Côpe-feu,Squid +X-KDE-Keywords[x-test]=xxProxy,Proxy server,Firewall,Squidxx +X-KDE-Keywords[zh_CN]=Proxy,Proxy server,Firewall,Squid,代理,代理服务器,防火墙 +X-KDE-Keywords[zh_TW]=Proxy,Proxy server,Firewall,Squid + diff --git a/kdepasswd/CMakeLists.txt b/kdepasswd/CMakeLists.txt new file mode 100644 index 00000000..44d34ef6 --- /dev/null +++ b/kdepasswd/CMakeLists.txt @@ -0,0 +1,29 @@ +include_directories( + ${CMAKE_SOURCE_DIR}/libs/konq + ${CMAKE_BINARY_DIR}/libs/konq +) + +add_subdirectory(kcm) + +set(kdepasswd_SRCS + kdepasswd.cpp + passwd.cpp + passwddlg.cpp + process.cpp +) + +add_executable(kdepasswd ${kdepasswd_SRCS}) + +target_link_libraries(kdepasswd + ${KDE4_KIO_LIBS} + ${KDE4_KPTY_LIBS} +) + +install( + TARGETS kdepasswd + ${INSTALL_TARGETS_DEFAULT_ARGS} +) +install( + PROGRAMS kdepasswd.desktop + DESTINATION ${KDE4_XDG_APPS_INSTALL_DIR} +) diff --git a/kdepasswd/Messages.sh b/kdepasswd/Messages.sh new file mode 100644 index 00000000..db167210 --- /dev/null +++ b/kdepasswd/Messages.sh @@ -0,0 +1,2 @@ +#! /usr/bin/env bash +$XGETTEXT *.cpp -o $podir/kdepasswd.pot diff --git a/kdepasswd/README b/kdepasswd/README new file mode 100644 index 00000000..50bbe54f --- /dev/null +++ b/kdepasswd/README @@ -0,0 +1,3 @@ +kdepasswd: A KDE front end to the Unix passwd command. + +Please report bugs to Geert Jansen diff --git a/kdepasswd/kcm/CMakeLists.txt b/kdepasswd/kcm/CMakeLists.txt new file mode 100644 index 00000000..9b3c8a92 --- /dev/null +++ b/kdepasswd/kcm/CMakeLists.txt @@ -0,0 +1,29 @@ +add_subdirectory(pics) + +set(kcm_useraccount_PART_SRCS + main.cpp + chfacedlg.cpp + main_widget.ui + faceDlg.ui +) + +kde4_add_kcfg_files(kcm_useraccount_PART_SRCS settings.kcfgc pass.kcfgc) + +kde4_add_plugin(kcm_useraccount ${kcm_useraccount_PART_SRCS}) + +target_link_libraries(kcm_useraccount konq ${KDE4_KIO_LIBS} ${KDE4_KDESU_LIBS}) + +install( + TARGETS kcm_useraccount + DESTINATION ${KDE4_PLUGIN_INSTALL_DIR} +) +install( + FILES kcm_useraccount.desktop + DESTINATION ${KDE4_SERVICES_INSTALL_DIR} +) +install( + FILES + kcm_useraccount.kcfg + kcm_useraccount_pass.kcfg + DESTINATION ${KDE4_KCFG_INSTALL_DIR} +) diff --git a/kdepasswd/kcm/Messages.sh b/kdepasswd/kcm/Messages.sh new file mode 100644 index 00000000..d65ca906 --- /dev/null +++ b/kdepasswd/kcm/Messages.sh @@ -0,0 +1,3 @@ +#! /usr/bin/env bash +$EXTRACTRC *.ui *.kcfg >> rc.cpp +$XGETTEXT *.cpp -o $podir/useraccount.pot diff --git a/kdepasswd/kcm/README b/kdepasswd/kcm/README new file mode 100644 index 00000000..563ce7e5 --- /dev/null +++ b/kdepasswd/kcm/README @@ -0,0 +1,27 @@ + +Thu Jan 29 00:34:49 CET 2004 +Frans Englich + +KCM useraccount is a merge of the former kdebase/kcontrol/email +and kdeutils/kdepasswd/userinfo/. They existed in KDE 3.1, at least. + +As a bonus, on top of saving the stuff with KEMailSettings it tries +also to save the realname to /etc/passwd. This is done via chfn, wrapped +in ChfnProcess, chfnprocess.h - which is the place to ifdef/modify so +other systems/ychfn/whatever works. +/etc/passwd is not the primary goal, the focus is on KDE's settings. The KCM +tries to hide the implementation differences and play nice with the +user - keep that in mind. + +The "face" term is rather scary.. For example I don't think the user immediately +associate to the login image when a phrase such as this is thrown in the face: +"Your administrator has disallowed changing your face". Keep it in mind.. + +Some information which was available in userinfo is left out - the home +folder and shell info. A typical user is not interested in the info nor +knows what it means. And the advanced users already knows it. + +If further information is added, think twice if it should not be +added in a "Details..." dialog - is it useful for the majority or not? +The SMTP setting as well as UID should be moved to that dialog too, IMO. + diff --git a/kdepasswd/kcm/chfacedlg.cpp b/kdepasswd/kcm/chfacedlg.cpp new file mode 100644 index 00000000..80715d8f --- /dev/null +++ b/kdepasswd/kcm/chfacedlg.cpp @@ -0,0 +1,182 @@ +/** + * Copyright 2003 Braden MacDonald + * Copyright 2003 Ravikiran Rajagopal + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * + * + * Please see the README + * + */ + +/** + * @file UserInfo's Dialog for changing your face. + * @author Braden MacDonald + */ + +#include "chfacedlg.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "settings.h" // KConfigXT + + + +/** + * TODO: It would be nice if the widget were in a .ui + */ +ChFaceDlg::ChFaceDlg(const QString& picsdir, QWidget *parent) + : KDialog( parent ) +{ + setCaption( i18nc("@title:window", "Change your Face") ); + setButtons( Ok|Cancel|User1|User2 ); + setDefaultButton( Ok ); + + setButtonText( User1, i18n("Custom Image...") ); + setButtonText( User2, i18n("Remove Image") ); + + QWidget *faceDlg = new QWidget; + ui.setupUi(faceDlg); + + setMainWidget(faceDlg); + + connect( ui.m_FacesWidget, SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)), SLOT(slotFaceWidgetSelectionChanged(QListWidgetItem*)) ); + + connect( ui.m_FacesWidget, SIGNAL(doubleClicked(QModelIndex)), SLOT(accept()) ); + connect( this, SIGNAL(okClicked()), this, SLOT(accept())); + + connect( this, SIGNAL(user1Clicked()), this, SLOT(slotGetCustomImage()) ); + + connect( this, SIGNAL(user2Clicked()), this, SLOT(slotRemoveImage()) ); + +#if 0 + QPushButton *acquireBtn = new QPushButton( i18n("&Acquire Image..."), page ); + acquireBtn->setEnabled( false ); + morePics->addWidget( acquireBtn ); +#endif + + // Filling the icon view + QDir facesDir( picsdir ); + if ( facesDir.exists() ) + { + const QStringList picslist = facesDir.entryList( QDir::Files ); + for ( QStringList::const_iterator it = picslist.constBegin(); it != picslist.constEnd(); ++it ) + new QListWidgetItem( QIcon( picsdir + *it ), (*it).section('.',0,0), ui.m_FacesWidget ); + } + facesDir.setPath( KCFGUserAccount::userFaceDir() ); + if ( facesDir.exists() ) + { + const QStringList picslist = facesDir.entryList( QDir::Files ); + for ( QStringList::const_iterator it = picslist.constBegin(); it != picslist.constEnd(); ++it ) + new QListWidgetItem( QIcon( KCFGUserAccount::userFaceDir() + *it ), + QString('/'+(*it)) == KCFGUserAccount::customFaceFile() ? + i18n("(Custom)") : (*it).section('.',0,0), + ui.m_FacesWidget ); + } + + + enableButtonOk( false ); // since no item is pre-selected, we must only enable the Ok button once a selection is done! + //connect( this, SIGNAL(okClicked()), SLOT(slotSaveCustomImage()) ); + + resize( 420, 400 ); +} + +void ChFaceDlg::addCustomPixmap( const QString &imPath, bool saveCopy ) +{ + QImage pix( imPath ); + // TODO: save pix to TMPDIR/userinfo-tmp, + // then scale and copy *that* to ~/.faces + + if (pix.isNull()) + { + KMessageBox::sorry( this, i18n("There was an error loading the image.") ); + return; + } + if ( (pix.width() > KCFGUserAccount::faceSize()) + || (pix.height() > KCFGUserAccount::faceSize()) ) + pix = pix.scaled( KCFGUserAccount::faceSize(), KCFGUserAccount::faceSize(), Qt::KeepAspectRatio );// Should be no bigger than certain size. + + if ( saveCopy ) + { + // If we should save a copy: + QDir userfaces( KCFGUserAccount::userFaceDir() ); + if ( !userfaces.exists( ) ) + userfaces.mkdir( userfaces.absolutePath() ); + + pix.save( userfaces.absolutePath() + "/.userinfo-tmp" , "PNG" ); + KonqOperations::copy( this, KonqOperations::COPY, KUrl::List( KUrl( userfaces.absolutePath() + "/.userinfo-tmp" ) ), KUrl( userfaces.absolutePath() + '/' + QFileInfo(imPath).fileName().section('.',0,0) ) ); +#if 0 + if ( !pix.save( userfaces.absolutePath() + '/' + imPath , "PNG" ) ) + KMessageBox::sorry(this, i18n("There was an error saving the image:\n%1", userfaces.absolutePath() ) ); +#endif + } + + QListWidgetItem* newface = new QListWidgetItem( QIcon(QPixmap::fromImage(pix)), QFileInfo(imPath).fileName().section('.',0,0), ui.m_FacesWidget ); + ui.m_FacesWidget->scrollToItem( newface ); + ui.m_FacesWidget->setCurrentItem( newface ); +} + +void ChFaceDlg::slotGetCustomImage( ) +{ + QCheckBox* checkWidget = new QCheckBox( i18n("&Save copy in custom faces folder for future use"), 0 ); + + KFileDialog dlg( QDir::homePath(), KImageIO::pattern( KImageIO::Reading ), + this, checkWidget); + + dlg.setOperationMode( KFileDialog::Opening ); + dlg.setCaption( i18nc("@title:window", "Choose Image") ); + dlg.setMode( KFile::File | KFile::LocalOnly ); + + KImageFilePreview *ip = new KImageFilePreview( &dlg ); + dlg.setPreviewWidget( ip ); + if (dlg.exec() == QDialog::Accepted) + addCustomPixmap( dlg.selectedFile(), checkWidget->isChecked() ); +} + +void ChFaceDlg::slotRemoveImage() +{ + ui.m_FacesWidget->clearSelection(); + accept(); +} + +#if 0 +void ChFaceDlg::slotSaveCustomImage() +{ + if ( m_FacesWidget->currentItem()->key() == USER_CUSTOM_KEY) + { + QDir userfaces( QDir::homePath() + USER_FACES_DIR ); + if ( !userfaces.exists( ) ) + userfaces.mkdir( userfaces.absolutePath() ); + + if ( !m_FacesWidget->currentItem()->pixmap()->save( userfaces.absolutePath() + USER_CUSTOM_FILE , "PNG" ) ) + KMessageBox::sorry(this, i18n("There was an error saving the image:\n%1", userfaces.absolutePath() ) ); + } +} +#endif + +#include "moc_chfacedlg.cpp" diff --git a/kdepasswd/kcm/chfacedlg.h b/kdepasswd/kcm/chfacedlg.h new file mode 100644 index 00000000..43511f12 --- /dev/null +++ b/kdepasswd/kcm/chfacedlg.h @@ -0,0 +1,75 @@ +/** + * Copyright 2003 Braden MacDonald + * Copyright 2003 Ravikiran Rajagopal + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * + * + * Please see the README + * + */ + +/** + * @file UserInfo-chface: Dialog for choosing a new face for your user. + * @author Braden MacDonald + */ + +#ifndef CHFACEDLG_H +#define CHFACEDLG_H + +#include +#include + +#include + +#include "ui_faceDlg.h" + +enum FacePerm { adminOnly = 1, adminFirst = 2, userFirst = 3, userOnly = 4}; + +class ChFaceDlg : public KDialog +{ + Q_OBJECT +public: + + + explicit ChFaceDlg(const QString& picsdirs, + QWidget *parent=0); + + + /** + * Will return the currently selected face, or a null pixmap if the user hit the "remove image" button + */ + QPixmap getFaceImage() const + { + if(ui.m_FacesWidget->currentItem()) + return ui.m_FacesWidget->currentItem()->icon().pixmap(64); + else + return QPixmap(); + } + +private Q_SLOTS: + void slotFaceWidgetSelectionChanged( QListWidgetItem *item ) + { enableButton( Ok, !item->icon().isNull() ); } + + void slotGetCustomImage(); + //void slotSaveCustomImage(); + void slotRemoveImage(); + +private: + void addCustomPixmap( const QString &imPath, bool saveCopy ); + + Ui::faceDlg ui; +}; + +#endif // CHFACEDLG_H diff --git a/kdepasswd/kcm/faceDlg.ui b/kdepasswd/kcm/faceDlg.ui new file mode 100644 index 00000000..90965ea6 --- /dev/null +++ b/kdepasswd/kcm/faceDlg.ui @@ -0,0 +1,51 @@ + + + faceDlg + + + + 0 + 0 + 400 + 306 + + + + + 400 + 199 + + + + + + + + + Select a new face: + + + + + + + + 64 + 64 + + + + QListView::Adjust + + + QListView::IconMode + + + + + + + + + + diff --git a/kdepasswd/kcm/kcm_useraccount.desktop b/kdepasswd/kcm/kcm_useraccount.desktop new file mode 100644 index 00000000..4eaaba0d --- /dev/null +++ b/kdepasswd/kcm/kcm_useraccount.desktop @@ -0,0 +1,243 @@ +[Desktop Entry] +Icon=preferences-desktop-user-account +Categories=QT;KDE;X-KDE-settings-security; +X-KDE-ParentApp=kcontrol +X-KDE-Library=kcm_useraccount +X-KDE-System-Settings-Parent-Category=account-details +X-KDE-Weight=50 +X-DocPath=kdepasswd/index.html + +Type=Service +X-KDE-ServiceTypes=KCModule + +Exec=kcmshell4 kcm_useraccount +Name=Password & User Account +Name[af]=Gebruiker rekening en Wagwoord +Name[ar]=كلمة السر و حساب المستخدم +Name[as]=গুপ্তশব্দ & ব্যৱহাৰকৰোঁতাৰ হিচাপ +Name[ast]=Contraseña y cuenta d'usuariu +Name[be]=Пароль і рахунак карыстальніка +Name[be@latin]=Parol i kont karystańnika +Name[bg]=Парола и сметка +Name[bn]=পাসওয়ার্ড এবং ব্যবহারকারী অ্যাকাউন্ট +Name[bn_IN]=পাসওয়ার্ড ও ব্যবহারকারীদের অ্যাকাউন্ট +Name[br]=Tremenger ha gont an arveriad +Name[bs]=Lozinka i korisnički nalog +Name[ca]=Contrasenya i compte d'usuari +Name[ca@valencia]=Contrasenya i compte d'usuari +Name[cs]=Heslo a uživatelský účet +Name[csb]=Parola ë kònto brëkòwnika +Name[cy]=Cyfrinair a Chyfrif Defnyddiwr +Name[da]=Adgangskode og brugerkonto +Name[de]=Passwort & Benutzerzugang +Name[el]=Κωδικός πρόσβασης & Πληροφορίες χρήστη +Name[en_GB]=Password & User Account +Name[eo]=Pasvorto kaj uzanta konto +Name[es]=Contraseña y cuenta de usuario +Name[et]=Parool ja kasutaja konto +Name[eu]=Pasahitza eta erabiltzailearen kontua +Name[fa]=اسم رمز و حساب کاربری +Name[fi]=Salasana ja käyttäjätili +Name[fr]=Compte utilisateur et mot de passe +Name[fy]=Wachtwurd en brûkersbehear +Name[ga]=Focal Faire agus Cuntas Úsáideora +Name[gl]=Contrasinal e conta de usuario +Name[gu]=પાસવર્ડ અને વપરાશકર્તા ખાતું +Name[he]=סיסמה וחשבון משתמש +Name[hi]=पासवर्ड तथा उपयोक्ता खाता +Name[hne]=पासवर्ड अउ कमइया खाता +Name[hr]=Zaporka i korisnički račun +Name[hsb]=Hesło a konto wužiwarja +Name[hu]=Név és jelszó +Name[ia]=Conto de usator & contrasigno +Name[id]=Sandi & Akun Pengguna +Name[is]=Lykilorð og notandaaðgangur +Name[it]=Password e account utente +Name[ja]=パスワード & ユーザアカウント +Name[ka]=მომხმარებლის ანგარიშები და პაროლები +Name[kk]=Пароль мен тіркелгі +Name[km]=ពាក្យ​សម្ងាត់ និង​គណនី​អ្នក​ប្រើ +Name[kn]=ಗುಪ್ತಪದ ಮತ್ತು ಬಳಕೆದಾರ ಖಾತೆ +Name[ko]=암호와 사용자 계정 +Name[ku]=Hesabê nasnav û Bikarhêner +Name[lt]=Slaptažodis ir Naudotojo paskyra +Name[lv]=Paroles un lietotāju konti +Name[mai]=पासवर्ड आओर प्रयोक्ता खाता +Name[mk]=Лозинка и корисничка сметка +Name[ml]=അടയാളവാക്കും ഉപയോക്താവിന്റെ അക്കൌണ്ടും +Name[mr]=गुप्तशब्द व वापरकर्ता खाते +Name[ms]=Kata Laluan & Akaun Pengguna +Name[nb]=Passord og brukerkonto +Name[nds]=Passwoort un Brukerkonto +Name[ne]=पासवर्ड र प्रयोगकर्ता खाता +Name[nl]=Wachtwoord en gebruikerbeheer +Name[nn]=Passord og brukarkonto +Name[or]=ପ୍ରବେଶ ସଂକେତ & ଚାଳକ ଖାତା +Name[pa]=ਯੂਜ਼ਰ ਅਕਾਊਂਟ ਅਤੇ ਪਾਸਵਰਡ +Name[pl]=Hasło i konto użytkownika +Name[pt]=Senha e Conta do Utilizador +Name[pt_BR]=Senha e conta do usuário +Name[ro]=Parolă și cont utilizator +Name[ru]=Профиль пользователя +Name[se]=Beassansánit ja geavaheaddjekontut +Name[si]=පරිශීලක ගිණුම් සහ රහස්පද +Name[sk]=Heslo a účet +Name[sl]=Uporabniški račun +Name[sr]=Лозинка и кориснички налог +Name[sr@ijekavian]=Лозинка и кориснички налог +Name[sr@ijekavianlatin]=Lozinka i korisnički nalog +Name[sr@latin]=Lozinka i korisnički nalog +Name[sv]=Lösenord och användarkonto +Name[ta]=கடவுச்சொல் & பயனர் கணக்கு +Name[te]=పాస్ వర్డ్ & యూజర్ ఖాతా +Name[tg]=Парол ва &Ҳисоби корбар +Name[th]=รหัสผ่านและบัญชีผู้ใช้ +Name[tr]=Parola ve Kullanıcı Hesabı +Name[ug]=ئىم ۋە ئىشلەتكۈچى ھېساباتى +Name[uk]=Пароль і обліковий запис користувача +Name[uz]=Maxfiy soʻz va foydalanuvchi hisobi +Name[uz@cyrillic]=Махфий сўз ва фойдаланувчи ҳисоби +Name[vi]=Mật khẩu và Tài khoản Người dùng +Name[wa]=Sicret & conte uzeu +Name[x-test]=xxPassword & User Accountxx +Name[zh_CN]=密码和用户信息 +Name[zh_TW]=密碼與帳號資訊 +X-KDE-Keywords=password;email;name;organization;realname;login image;face; echo mode; +X-KDE-Keywords[bg]=парола;е-поща;име;организация;лице;аватар;password;email;name;organization;realname;login image;face; echo mode; +X-KDE-Keywords[bs]=password;email;name;organization;realname;login image;face; echo mode; +X-KDE-Keywords[ca]=contrasenya;correu electrònic;nom;organització;nom real;imatge d'inici de sessió;cara;mode d'eco; +X-KDE-Keywords[ca@valencia]=contrasenya;correu electrònic;nom;organització;nom real;imatge d'inici de sessió;cara;mode d'eco; +X-KDE-Keywords[da]=adgangskode;email;navn;organisation;ægtenavn;login-billede;ansigt;ekko-tilstand; +X-KDE-Keywords[de]=Passwort,E-Mail,Name,Organisation,Tatsächlicher Name,Anmeldebild,Gesicht,Echomodus +X-KDE-Keywords[el]=κωδικός πρόσβασης;email;όνομα;οργανισμός;πραγματικό όνομα;εικόνα στοιχείων εισόδου;πρόσωπο; λειτουργία echo +X-KDE-Keywords[en_GB]=password;email;name;organisation;realname;login image;face; echo mode; +X-KDE-Keywords[es]=contraseña;correo;nombre;organización;nombre real;imagen de inicio;cara; modo de eco; +X-KDE-Keywords[et]=parool;e-post;nimi;organisatsioon;tegelik nimi;sisselogimispilt;nägu;echo režiim; +X-KDE-Keywords[fi]=salasana;email;sähköposti;nimi;organisaatio;järjestö;oikea nimi;todellinen nimi;tunnuskuva;kaiutustila;salasanakenttä; +X-KDE-Keywords[fr]=mot de passe ; courrier électronique ; nom ; organisation ; nom réel ; image de connexion ; visage ; mode rappel ; +X-KDE-Keywords[ga]=focal faire;ríomhphost;ainm;eagras;fíorainm;íomhá logála isteach;aghaidh;mód macalla; +X-KDE-Keywords[gl]=contrasinal;correo electrónico;email;organización;nome real;cara; +X-KDE-Keywords[he]=password;email;name;organization;realname;login image;face; echo mode;ססמה;דוא"ל;דואל;שם;ארגון;חיבור;פנים;פרצוף;תמונה +X-KDE-Keywords[hu]=jelszó;e-mail;név;szervezet;valódi név;bejelentkezési kép;arckép;visszajelzési mód; +X-KDE-Keywords[ia]=contrasigno;e-posta;nomine;organization;ver nomine;image de accesso;facie;modo echo; +X-KDE-Keywords[id]=sandi;email;nama;organisasi;namaasli;gambar log masuk;wajah; mode echo; +X-KDE-Keywords[it]=password;email;posta elettronica;nome;organizzazione;nome vero;immagine di login;face;modalità echo +X-KDE-Keywords[kk]=password;email;name;organization;realname;login image;face; echo mode; +X-KDE-Keywords[km]=ពាក្យសម្ងាត់;អ៊ីមែល;ឈ្មោះ;ស្ថាប័ន;ឈ្មោះ​ពិត;រូបភាព​ចូល;មុខមាត់; របៀប​អេកូ; +X-KDE-Keywords[ko]=password;email;name;organization;realname;login image;face; echo mode;암호;비밀번호;이름;조직;실명;로그인 그림;프로필 이미지;얼굴 +X-KDE-Keywords[lt]=password;email;name;organization;realname;login image;face; echo mode; +X-KDE-Keywords[lv]=parole;e-pasts;vārds;organizācija;īstais vārds;pieteikšanās attēls;seja; atbalss režīms; +X-KDE-Keywords[mr]=गुप्तशब्द; इमेल; नाव; संस्था; खरे नाव; प्रवेश प्रतिमा; चेहरा; प्रतिध्वनि पद्धत; +X-KDE-Keywords[nb]=passord;e-post;navn;organisasjon;fullt navn; innloggingsbilde;ansikt;ekkomodus; +X-KDE-Keywords[nds]=Passwoort;Nettpost;Nettpostadress;Afdelen;Organisatschoon;Reaalnaam;Anmell-Bild;Torüchmellen-Bedrief; +X-KDE-Keywords[nl]=wachtwoord;e-mail;naam;organisatie;echte naam;aanmeldafbeelding;gezicht; echomodus; +X-KDE-Keywords[pl]=hasło;email;nazwa;organizacja;rzeczywista nazwa;obraz loginu;twarz; tryb echo; +X-KDE-Keywords[pt]=senha;e-mail;nome;organização;nome verdadeiro;imagem de autenticação;cara;modo de eco; +X-KDE-Keywords[pt_BR]=senha;e-mail;nome;organização;nome verdadeiro;nome real;imagem de autenticação;face;rosto;modo de eco; +X-KDE-Keywords[ro]=parolă;email;nume;organizație;numereal;imagine autentificare;față; +X-KDE-Keywords[ru]=password;email;name;organization;realname;login image;face; echo mode;пароль;электронная почта;имя;организация;настоящее имя;изображение пользователя;лицо;показ пароля +X-KDE-Keywords[sk]=heslo;e-mail;meno;organizácia;skutočné meno;obrázok;tvár;režim výpisu; +X-KDE-Keywords[sl]=geslo;pošta;ime;organizacija;pravo ime;slika ob prijavi;obraz;način odmeva; +X-KDE-Keywords[sr]=password;email;name;organization;realname;login image;face; echo mode;лозинка;е‑пошта;име;организација;пуно име;пријавна слика;лице;ехо +X-KDE-Keywords[sr@ijekavian]=password;email;name;organization;realname;login image;face; echo mode;лозинка;е‑пошта;име;организација;пуно име;пријавна слика;лице;ехо +X-KDE-Keywords[sr@ijekavianlatin]=password;email;name;organization;realname;login image;face; echo mode;lozinka;e‑pošta;ime;organizacija;puno ime;prijavna slika;lice;eho +X-KDE-Keywords[sr@latin]=password;email;name;organization;realname;login image;face; echo mode;lozinka;e‑pošta;ime;organizacija;puno ime;prijavna slika;lice;eho +X-KDE-Keywords[sv]=lösenord,e-post,namn,organisation,verkligt namn,inloggning,bild,ansikte,ekoläge +X-KDE-Keywords[tr]=parola;e-posta;isim;kurum;gerçek isim;giriş resmi;fotoğraf; yazma kipi; +X-KDE-Keywords[uk]=password;email;name;organization;realname;login image;face;echomode;пароль;повідомлення;назва;організація;установа;ім’я;зображення;обличчя;показ пароля +X-KDE-Keywords[wa]=sicret;emile;no;organizåcion;vraiy no;imådje d' elodjaedje;môde esco; +X-KDE-Keywords[x-test]=xxpassword;email;name;organization;realname;login image;face; echo mode;xx +X-KDE-Keywords[zh_CN]=password;email;name;organization;realname;login image;face; echo mode;密码;邮箱;姓名;组织;真名;登录图像;脸; +X-KDE-Keywords[zh_TW]=password;email;name;organization;realname;login image;face; echo mode; +Comment=User information such as password, name and email +Comment[af]=Gebruiker informasie, soos naam, wagwoord en e-pos +Comment[ar]=معلومات عن المستخدم مثل كلمة السر ، الاسم والبريد الإلكتروني +Comment[as]=ব্যৱহাৰকৰোঁতাৰ তথ্য যেনে গুপ্তশব্দ, নাম আৰু ঈ-মেইল +Comment[ast]=Información d'usuariu, como contraseña, nome o corréu-e +Comment[be]=Звесткі пра карыстальніка, напр. пароль, імя і электронны адрас +Comment[be@latin]=Źviestki pra karystańnika: parol, nazva, e-maił i h.d. +Comment[bg]=Настройване на потребителската сметка, паролата, името и е-пощата +Comment[bn]=ব্যবহারকারী তথ্য, যথা পাসওয়ার্ড, নাম এবং ই-মেইল +Comment[bn_IN]=ব্যবহারকারীদের বিভিন্ন তথ্য যেমন পাসওয়ার্ড, নাম ও ই-মেইল +Comment[br]=Titouroù diwar-benn an arveriad (tremenger, anv, chomlec'h postel) +Comment[bs]=Podaci o korisniku, poput lozinke, imena i e‑pošte +Comment[ca]=Informació de l'usuari com ara contrasenya, nom i correu +Comment[ca@valencia]=Informació de l'usuari com ara contrasenya, nom i correu +Comment[cs]=Informace o uživateli, jako heslo, jméno nebo e-mail +Comment[csb]=Wëdowiédzô ò kònce brëkòwnika: parola, miono ë nôzwëskò, e-maila ëtd. +Comment[cy]=Gwybodaeth ddefnyddiwr fel cyfrinair, enw ac ebost +Comment[da]=Brugerinformation såsom adgangskode, navn og e-mail +Comment[de]=Benutzerinformationen wie Passwort, Name, E-Mail-Adresse +Comment[el]=Πληροφορίες χρήστη όπως ο κωδικός πρόσβασης, το όνομα και το email +Comment[en_GB]=User information such as password, name and email +Comment[eo]=Uzantaj informoj kiel pasvorto, nomo kaj retpoŝto +Comment[es]=Información del usuario, como contraseña, nombre o correo electrónico +Comment[et]=Kasutajainfo, nt. parool, nimi ja e-posti aadress +Comment[eu]=Erabiltzaileari buruzko informazioa; hala nola pasahitza, izena eta helbide elektronikoa. +Comment[fa]=استفاده از اطلاعاتی نظیر اسم رمز، نام و رایانامه +Comment[fi]=Käyttäjätiedot, kuten salasana, nimi ja sähköpostiosoite +Comment[fr]=Informations sur l'utilisateur comme le mot de passe, le nom et l'adresse électronique +Comment[fy]=Brûkersynformaasje sa as wachtwurd, namme en e-adres +Comment[ga]=Eolas pearsanta ar nós focal faire, ainm agus seoladh ríomhphoist +Comment[gl]=Información do usuario como o contrasinal, nome e correo electrónico +Comment[gu]=વપરાશકર્તા માહિતી જેવીકે પાસવર્ડ, નામ અને ઇમેલ +Comment[he]=מידע אודות המשתמש, כגון סיסמאות, שם וכתובות דואר־אלקטרוני +Comment[hi]=उपयोक्ता जानकारी जैसे कि पासवर्ड, नाम तथा ई-मेल +Comment[hne]=कमइया जानकारी जइसन कि पासवर्ड, नाम अउ ई-मेल +Comment[hr]=Korisnički podaci poput lozinke, imena i e-pošte +Comment[hsb]=Informacija wo wužiwarju kaž hesło, mjeno a emejlowa adresa +Comment[hu]=A felhasználójellemzők, például az e-mail cím vagy a jelszó beállítása +Comment[ia]=Information de usator tal como contrasigno, nomine e e-posta +Comment[id]=Informasi pengguna seperti sandi, nama dan email +Comment[is]=Upplýsingar um notandann eins og til dæmis póstfang, lykilorð og nafn +Comment[it]=Informazioni utente come password, nome e indirizzo di posta +Comment[ja]=パスワード、名前、メールアドレスなどのユーザ情報 +Comment[ka]=ცნობები მომხმარებლების შესახებ, როგორიცაა პაროლი, სახელი და ელ-ფოსტა +Comment[kk]=Паролі, аты және эл.пошта адресі секілді пайдаланушының мәліметі +Comment[km]=ព័ត៌មាន​អ្នក​ប្រើ​ដូចជា ពាក្យ​សម្ងាត់ ឈ្មោះ និង​អ៊ីមែល +Comment[kn]=ಬಳಕೆದಾರರ ಹೆಸರು, ಗುಪ್ತಪದ, ವಿ-ಅಂಚೆ ಯಂತಹ ಮಾಹಿತಿ +Comment[ko]=암호, 이름, 전자우편 주소 같은 개인 정보 +Comment[ku]=Agahiyên bikarhêneran yên wekî nasnav, nav û epeyam +Comment[lt]=Naudotojo informacija, tokia, kaip slaptažodis, vardas ir el. pašto adresas +Comment[lv]=Lietotāja informācija, piemēram, parole, vārds un e-pasts +Comment[mai]=प्रयोक्ता सूचना जहिना जे पासवर्ड, नाम आओर ई-मेल +Comment[mk]=Кориснички информации како лозинка, име и е-пошта +Comment[ml]=അടയാളവാക്കു്, പേരു്, ഇമെയില്‍ തുടങ്ങി ഉപയോക്താവിനെക്കുറിച്ചുള്ള വിവരം +Comment[mr]=वापरकर्ता माहिती जसे कि गुप्तशब्द, नाव व ई-मेल +Comment[ms]=Maklumat pengguna seperti kata laluan, nama dan e-mel +Comment[nb]=Brukerinformasjon som passord, navn og e-postadresse +Comment[nds]=Brukerinformatschonen, as a.B. Passwoort, Naam un Nettpostadress +Comment[ne]=पासवर्ड, नाम, र इमेल जस्तो प्रयोगकर्ता सूचना +Comment[nl]=Gebruikerinformatie zoals wachtwoord, naam en e-mailadres +Comment[nn]=Brukarinformasjon som passord, namn og e-postadresse +Comment[or]=ଚାଳକ ସୂଚନା ଯେପରି କି ପ୍ରବେଶ ସଂକେତ, ନାମ ଏବଂ ଇମେଲ +Comment[pa]=ਯੂਜ਼ਰ ਜਾਣਕਾਰੀ ਜਿਵੇਂ ਕਿ ਪਾਸਵਰਡ, ਨਾਂ ਅਤੇ ਈ-ਮੇਲ +Comment[pl]=Informacje o koncie użytkownika: hasło, imię i nazwisko, e-mail itd. +Comment[pt]=Informações do utilizador, como a senha, o nome e o e-mail +Comment[pt_BR]=Informações do usuário, como a senha, nome e e-mail +Comment[ro]=Informații utilizator, cum ar fi parolă, nume și e-mail +Comment[ru]=Сведения о пользователе, такие как пароль, имя и адрес электронной почты +Comment[se]=Geavaheaddjedieđut nugo beassansátni, namma ja e-boasta +Comment[si]=මුරපද, නාම හා විද්‍යුත් ලිපින වැනි පරිශීලක දත්ත +Comment[sk]=Informácie o užívateľovi, ako heslo, meno a e-mail +Comment[sl]=Podatki o uporabniku, kot so geslo, ime in e-pošta +Comment[sr]=Подаци о кориснику, попут лозинке, имена и е‑поште +Comment[sr@ijekavian]=Подаци о кориснику, попут лозинке, имена и е‑поште +Comment[sr@ijekavianlatin]=Podaci o korisniku, poput lozinke, imena i e‑pošte +Comment[sr@latin]=Podaci o korisniku, poput lozinke, imena i e‑pošte +Comment[sv]=Användarinformation som lösenord, namn och e-post +Comment[ta]=கடவுச் சொல் போன்ற பயனர் தகவல், பெயர் மற்றும் மின்னஞ்சல் +Comment[te]=సంకేతపదము, పేరు మరియు ఈమెయిల్ వంటి వినియోగదారి సమాచారం +Comment[tg]=Маълумоти дар бораи корбар монанди парол, ном ва почтаи электронӣ +Comment[th]=ข้อมูลของผู้ใช้ เช่น รหัสผ่าน, ชื่อ และอีเมล +Comment[tr]=Parola, isim ve e-posta gibi kullanıcı bilgileri +Comment[ug]=ئىم، ئات ۋە ئېلخەتكە ئوخشاش ئىشلەتكۈچى ئۇچۇرى +Comment[uk]=Дані про користувача: пароль, ім'я та адреса електронної пошти +Comment[uz]=Foydalanuvchining ismi, maxfiy soʻzi va elektron pochtasi kabi maʼlumot +Comment[uz@cyrillic]=Фойдаланувчининг исми, махфий сўзи ва электрон почтаси каби маълумот +Comment[vi]=Thông tin về người dùng như mật khẩu, tên và địa chỉ thư +Comment[wa]=Infôrmåcions d' l' uzeu come si scret, no et emile +Comment[x-test]=xxUser information such as password, name and emailxx +Comment[zh_CN]=用户信息,包括密码、姓名和电子邮件等 +Comment[zh_TW]=使用者資訊如密碼、名稱和電子郵件 + diff --git a/kdepasswd/kcm/kcm_useraccount.kcfg b/kdepasswd/kcm/kcm_useraccount.kcfg new file mode 100644 index 00000000..fa74d36c --- /dev/null +++ b/kdepasswd/kcm/kcm_useraccount.kcfg @@ -0,0 +1,40 @@ + + + kglobal.h + kstandarddirs.h + + + + + KGlobal::dirs()->resourceDirs("data").last() + "kdm/faces" + '/' + + + + $HOME/.faces/ + + + PreferAdmin + + + + + 64 + + + + .default.face.icon + + + + Custom.png + + + + $HOME/.face.icon + + + diff --git a/kdepasswd/kcm/kcm_useraccount_pass.kcfg b/kdepasswd/kcm/kcm_useraccount_pass.kcfg new file mode 100644 index 00000000..c76e4a65 --- /dev/null +++ b/kdepasswd/kcm/kcm_useraccount_pass.kcfg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + OneStar + + + diff --git a/kdepasswd/kcm/main.cpp b/kdepasswd/kcm/main.cpp new file mode 100644 index 00000000..443f0f4a --- /dev/null +++ b/kdepasswd/kcm/main.cpp @@ -0,0 +1,313 @@ + +/** + * Copyright (C) 2004 Frans Englich + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * + * + * Please see the README + * + */ + +#include "main.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "settings.h" +#include "pass.h" +#include +#include + + +K_PLUGIN_FACTORY(Factory, registerPlugin();) +K_EXPORT_PLUGIN(Factory("useraccount")) + +KCMUserAccount::KCMUserAccount( QWidget *parent, const QVariantList &) + : KCModule( Factory::componentData(), parent) +{ + QVBoxLayout *topLayout = new QVBoxLayout(this); + topLayout->setSpacing(KDialog::spacingHint()); + topLayout->setMargin(0); + + _mw = new MainWidget(this); + installEventFilter(this); + setAcceptDrops(true); + topLayout->addWidget( _mw ); + + connect( _mw->btnChangeFace, SIGNAL(clicked()), SLOT(slotFaceButtonClicked())); + connect( _mw->btnChangePassword, SIGNAL(clicked()), SLOT(slotChangePassword())); + _mw->btnChangePassword->setGuiItem( KGuiItem( i18n("Change &Password..."), "preferences-desktop-user-password" )); + + connect( _mw->leRealname, SIGNAL(textChanged(QString)), SLOT(changed())); + connect( _mw->leOrganization, SIGNAL(textChanged(QString)), SLOT(changed())); + connect( _mw->leEmail, SIGNAL(textChanged(QString)), SLOT(changed())); + connect( _mw->leSMTP, SIGNAL(textChanged(QString)), SLOT(changed())); + + _ku = new KUser(); + _kes = new KEMailSettings(); + + _mw->lblUsername->setText( _ku->loginName() ); + QFont font( _mw->lblUsername->font() ); + font.setPointSizeF( font.pointSizeF() * 1.41 ); + font.setBold( true ); + _mw->lblUsername->setFont( font ); + _mw->lblUID->setText( QString().number(_ku->uid()) ); + + KAboutData *about = new KAboutData("kcm_useraccount", 0, + ki18n("Password & User Information"), 0, KLocalizedString(), + KAboutData::License_GPL, + ki18n("(C) 2002, Braden MacDonald, " + "(C) 2004 Ravikiran Rajagopal")); + + about->addAuthor(ki18n("Frans Englich"), ki18n("Maintainer"), "frans.englich@telia.com"); + about->addAuthor(ki18n("Ravikiran Rajagopal"), KLocalizedString(), "ravi@kde.org"); + about->addAuthor(ki18n("Michael H\303\244ckel"), KLocalizedString(), "haeckel@kde.org" ); + + about->addAuthor(ki18n("Braden MacDonald"), ki18n("Face editor"), "bradenm_k@shaw.ca"); + about->addAuthor(ki18n("Geert Jansen"), ki18n("Password changer"), "jansen@kde.org", + "http://www.stack.nl/~geertj/"); + about->addAuthor(ki18n("Daniel Molkentin")); + about->addAuthor(ki18n("Alex Zepeda")); + about->addAuthor(ki18n("Hans Karlsson"), ki18n("Icons"), "karlsson.h@home.se"); + about->addAuthor(ki18n("Hermann Thomas"), ki18n("Icons"), "h.thomas@gmx.de"); + setAboutData(about); + + setQuickHelp( i18n("Here you can change your personal information, which " + "will be used, for instance, in mail programs and word processors. You can " + "change your login password by clicking Change Password....") ); + + addConfig( KCFGPassword::self(), this ); + load(); +} + +void KCMUserAccount::slotChangePassword() +{ + QString bin = KStandardDirs::findExe("kdepasswd"); + if ( bin.isEmpty() ) { + kDebug() << "kcm_useraccount: kdepasswd was not found."; + KMessageBox::sorry ( this, i18n( "A program error occurred: the internal " + "program 'kdepasswd' could not be found. You will " + "not be able to change your password.")); + + _mw->btnChangePassword->setEnabled(false); + return; + } + QStringList lst; + lst << _ku->loginName(); + QProcess::startDetached(bin,lst); +} + + +KCMUserAccount::~KCMUserAccount() +{ + delete _ku; + delete _kes; +} + +void KCMUserAccount::load() +{ + _mw->lblUsername->setText(_ku->loginName()); + + _kes->setProfile(_kes->defaultProfileName()); + + QString realName = _kes->getSetting( KEMailSettings::RealName ); + + if (realName.isEmpty()) { + realName = _ku->property(KUser::FullName).toString(); + } + + _mw->leRealname->setText( realName ); + _mw->leEmail->setText( _kes->getSetting( KEMailSettings::EmailAddress )); + _mw->leOrganization->setText( _kes->getSetting( KEMailSettings::Organization )); + _mw->leSMTP->setText( _kes->getSetting( KEMailSettings::OutServer )); + + // load user face + _facePixmap = QPixmap( KCFGUserAccount::faceFile() ); + _mw->btnChangeFace->setIcon( KIcon(_facePixmap) ); + if (!_facePixmap.isNull()) { + _mw->btnChangeFace->setIconSize(_facePixmap.size()); + } + + KCModule::load(); /* KConfigXT */ + +} + +void KCMUserAccount::save() +{ + KCModule::save(); /* KConfigXT */ + +/* + * FIXME: there is apparently no way to set full name + * non-interactively as a normal user on FreeBSD. + */ +#if !defined(Q_OS_FREEBSD) && !defined(Q_OS_DRAGONFLY) + /* Save realname to /etc/passwd */ + if ( _mw->leRealname->isModified() ) { + // save icon file also with accountsservice + QDBusInterface ainterface("org.freedesktop.Accounts", + "/org/freedesktop/Accounts", + "org.freedesktop.Accounts", + QDBusConnection::systemBus()); + QDBusReply reply = ainterface.call("FindUserById", qlonglong(_ku->uid())); + if (reply.isValid() && !reply.error().isValid()) { + QDBusInterface uinterface("org.freedesktop.Accounts", + reply.value().path(), + "org.freedesktop.Accounts.User", + QDBusConnection::systemBus(), + this); + + QString name = _mw->leRealname->text(); + QDBusReply ureply = uinterface.call("SetRealName", name); + if (!ureply.isValid() || ureply.error().isValid()) { + kDebug() << ureply.error().message(); + KMessageBox::error( this, i18n("There was an error setting the name: %1", name) ); + } + } + } +#endif + + /* Save the image */ + if( !_facePixmap.isNull() ) + { + if( !_facePixmap.save( KCFGUserAccount::faceFile(), "PNG" )) { + KMessageBox::error( this, i18n("There was an error saving the image: %1", + KCFGUserAccount::faceFile()) ); + } + // save icon file also with accountsservice + QDBusInterface ainterface("org.freedesktop.Accounts", + "/org/freedesktop/Accounts", + "org.freedesktop.Accounts", + QDBusConnection::systemBus()); + QDBusReply reply = ainterface.call("FindUserById", qlonglong(_ku->uid())); + if (reply.isValid()) { + QDBusInterface uinterface("org.freedesktop.Accounts", + reply.value().path(), + "org.freedesktop.Accounts.User", + QDBusConnection::systemBus(), + this); + QDBusReply ureply = uinterface.call("SetIconFile", KCFGUserAccount::faceFile()); + if (!ureply.isValid()) { + kDebug() << ureply.error().message(); + KMessageBox::error( this, i18n("There was an error setting the image: %1", + KCFGUserAccount::faceFile()) ); + } + } + } else { + // delete existing image + if (QFile::exists(KCFGUserAccount::faceFile())) { + if ( !KIO::NetAccess::del(KCFGUserAccount::faceFile(), this) ) { + KMessageBox::error( this, i18n("There was an error deleting the image: %1", + KCFGUserAccount::faceFile()) ); + } + } + } + + /* Save KDE's homebrewn settings */ + _kes->setSetting( KEMailSettings::RealName, _mw->leRealname->text() ); + _kes->setSetting( KEMailSettings::EmailAddress, _mw->leEmail->text() ); + _kes->setSetting( KEMailSettings::Organization, _mw->leOrganization->text() ); + _kes->setSetting( KEMailSettings::OutServer, _mw->leSMTP->text() ); +} + +void KCMUserAccount::changeFace(const QPixmap &pix) +{ + _facePixmap = pix; + _mw->btnChangeFace->setIcon( KIcon(_facePixmap) ); + if ( !_facePixmap.isNull() ) { + _mw->btnChangeFace->setIconSize(_facePixmap.size()); + } + emit changed( true ); +} + +void KCMUserAccount::slotFaceButtonClicked() +{ + ChFaceDlg* pDlg = new ChFaceDlg( KGlobal::dirs()->resourceDirs("data").last() + + "/kdm/pics/users/", this ); + + if ( pDlg->exec() == QDialog::Accepted ) { + changeFace( pDlg->getFaceImage() ); + } + + delete pDlg; +} + +/** + * I merged faceButtonDropEvent into this /Frans + * The function was called after checking event type and + * the code is now below that if statement + */ +bool KCMUserAccount::eventFilter(QObject *, QEvent *e) +{ + QDragEnterEvent *ee = (QDragEnterEvent *) e; + if (e->type() == QEvent::DragEnter) { + KUrl::List uris = KUrl::List::fromMimeData(ee->mimeData()); + if (!uris.isEmpty()) { + ee->accept(); + } else { + ee->ignore(); + } + return true; + } else if (e->type() == QEvent::Drop) { + KUrl::List uris = KUrl::List::fromMimeData(ee->mimeData()); + if (!uris.isEmpty()) { + KUrl *url = new KUrl(uris.first()); + + bool tempfile = false; + QString pixPath; + if (url->isLocalFile()) { + pixPath = url->path(); + } else { + tempfile = true; + KIO::NetAccess::download(*url, pixPath, this); + } + QImageReader reader(pixPath); + if ( !reader.canRead() ) { + KMessageBox::sorry( this, i18n( "%1 does not appear to be an image file.\n", url->path())); + } else { + changeFace( QPixmap( pixPath ) ); + } + if (tempfile) { + KIO::NetAccess::removeTempFile(pixPath); + } + } + return true; + } + return false; +} + +#include "moc_main.cpp" + diff --git a/kdepasswd/kcm/main.h b/kdepasswd/kcm/main.h new file mode 100644 index 00000000..9df7dc44 --- /dev/null +++ b/kdepasswd/kcm/main.h @@ -0,0 +1,86 @@ + +/** + * Copyright (C) 2004 Frans Englich + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + */ + +#ifndef MAIN_H +#define MAIN_H + +#include +#include + +#include "ui_main_widget.h" + +#include "chfacedlg.h" + +class KUser; +#include +#include +class KUrl; + +class MainWidget : public QWidget, public Ui::MainWidget +{ +public: + MainWidget( QWidget *parent ) : QWidget( parent ) { + setupUi( this ); + } +}; + + +/** + * Please see the README + */ +class KCMUserAccount : public KCModule +{ + Q_OBJECT + +public: + explicit KCMUserAccount(QWidget* parent, const QVariantList& list=QVariantList()); + ~KCMUserAccount(); + + /** + * The user data is loaded from chfn(/etc/password) and then + * written back as well as to KDE's own(KEmailSettings). + * The user won't notice this(assuming they change the KDE settings via + * this KCM) and will make KDE play nice with environments which use + * /etc/password. + */ + void load(); + + void save(); + +protected: + /** + * For the face button + */ + bool eventFilter(QObject *, QEvent *e); + +private Q_SLOTS: + void slotChangePassword(); + //void configChanged() { emit changed(true); }; + void slotFaceButtonClicked(); + +private: + void changeFace(const QPixmap& pix); + + KEMailSettings *_kes; + KUser *_ku; + MainWidget *_mw; + QPixmap _facePixmap; + +}; + +#endif // MAIN_H diff --git a/kdepasswd/kcm/main_widget.ui b/kdepasswd/kcm/main_widget.ui new file mode 100644 index 00000000..06a301ef --- /dev/null +++ b/kdepasswd/kcm/main_widget.ui @@ -0,0 +1,316 @@ + + + Frans Englich <frans.englich@telia.com> + MainWidget + + + + 0 + 0 + 399 + 557 + + + + + 0 + + + + + + + + 0 + 0 + + + + + 74 + 74 + + + + + 74 + 74 + + + + true + + + Change your image + + + + + + true + + + + + + + 0 + + + + + + + + Qt::AlignVCenter + + + false + + + + + + + <i>Click to change your image</i> + + + Qt::AlignVCenter + + + false + + + + + + + + + Change Password... + + + + + + + Qt::Horizontal + + + QSizePolicy::Expanding + + + + 158 + 18 + + + + + + + + + + + + + + User Information + + + + + + &Name: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + false + + + leRealname + + + + + + + true + + + + + + + &Organization: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + false + + + leOrganization + + + + + + + true + + + + + + + &Email address: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + false + + + leEmail + + + + + + + true + + + + + + + &SMTP server: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + false + + + leSMTP + + + + + + + true + + + + + + + User ID: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + false + + + + + + + + + + false + + + + + + + + + + At Password Prompt + + + + + + Show one bullet for each letter + + + + + + + Show three bullets for each letter + + + + + + + Show nothing + + + + + + + + + + Qt::Vertical + + + QSizePolicy::Expanding + + + + 20 + 16 + + + + + + + + + KButtonGroup + QGroupBox +
    kbuttongroup.h
    + 1 +
    + + KLineEdit + QLineEdit +
    klineedit.h
    +
    + + KPushButton + QPushButton +
    kpushbutton.h
    +
    +
    + + btnChangeFace + leRealname + leOrganization + leEmail + leSMTP + btnChangePassword + + + +
    diff --git a/kdepasswd/kcm/pass.kcfgc b/kdepasswd/kcm/pass.kcfgc new file mode 100644 index 00000000..21b4bb78 --- /dev/null +++ b/kdepasswd/kcm/pass.kcfgc @@ -0,0 +1,5 @@ +File=kcm_useraccount_pass.kcfg +ClassName=KCFGPassword +Singleton=true +Mutators=true + diff --git a/kdepasswd/kcm/pics/Blackbox.png b/kdepasswd/kcm/pics/Blackbox.png new file mode 100644 index 00000000..911b3bbe Binary files /dev/null and b/kdepasswd/kcm/pics/Blackbox.png differ diff --git a/kdepasswd/kcm/pics/CMakeLists.txt b/kdepasswd/kcm/pics/CMakeLists.txt new file mode 100644 index 00000000..e00f85e2 --- /dev/null +++ b/kdepasswd/kcm/pics/CMakeLists.txt @@ -0,0 +1,2 @@ +FILE(GLOB userpics *.png) +install(FILES ${userpics} DESTINATION ${KDE4_DATA_INSTALL_DIR}/kdm/pics/users) diff --git a/kdepasswd/kcm/pics/Green.png b/kdepasswd/kcm/pics/Green.png new file mode 100644 index 00000000..3768596d Binary files /dev/null and b/kdepasswd/kcm/pics/Green.png differ diff --git a/kdepasswd/kcm/pics/Happy.jpg b/kdepasswd/kcm/pics/Happy.jpg new file mode 100644 index 00000000..710f915e Binary files /dev/null and b/kdepasswd/kcm/pics/Happy.jpg differ diff --git a/kdepasswd/kcm/pics/Happy.png b/kdepasswd/kcm/pics/Happy.png new file mode 100644 index 00000000..e21fa050 Binary files /dev/null and b/kdepasswd/kcm/pics/Happy.png differ diff --git a/kdepasswd/kcm/pics/Listening.png b/kdepasswd/kcm/pics/Listening.png new file mode 100644 index 00000000..acfff236 Binary files /dev/null and b/kdepasswd/kcm/pics/Listening.png differ diff --git a/kdepasswd/kcm/pics/Notme.png b/kdepasswd/kcm/pics/Notme.png new file mode 100644 index 00000000..d9f75ef0 Binary files /dev/null and b/kdepasswd/kcm/pics/Notme.png differ diff --git a/kdepasswd/kcm/pics/TV.png b/kdepasswd/kcm/pics/TV.png new file mode 100644 index 00000000..5febb4dc Binary files /dev/null and b/kdepasswd/kcm/pics/TV.png differ diff --git a/kdepasswd/kcm/pics/bomb.png b/kdepasswd/kcm/pics/bomb.png new file mode 100644 index 00000000..1ac2468f Binary files /dev/null and b/kdepasswd/kcm/pics/bomb.png differ diff --git a/kdepasswd/kcm/pics/sources/blackbox.svgz b/kdepasswd/kcm/pics/sources/blackbox.svgz new file mode 100644 index 00000000..d6899f16 Binary files /dev/null and b/kdepasswd/kcm/pics/sources/blackbox.svgz differ diff --git a/kdepasswd/kcm/pics/sources/bomb.svgz b/kdepasswd/kcm/pics/sources/bomb.svgz new file mode 100644 index 00000000..771f6d2c Binary files /dev/null and b/kdepasswd/kcm/pics/sources/bomb.svgz differ diff --git a/kdepasswd/kcm/pics/sources/green.svgz b/kdepasswd/kcm/pics/sources/green.svgz new file mode 100644 index 00000000..2d430690 Binary files /dev/null and b/kdepasswd/kcm/pics/sources/green.svgz differ diff --git a/kdepasswd/kcm/pics/sources/happy.svgz b/kdepasswd/kcm/pics/sources/happy.svgz new file mode 100644 index 00000000..7c9fc497 Binary files /dev/null and b/kdepasswd/kcm/pics/sources/happy.svgz differ diff --git a/kdepasswd/kcm/pics/sources/listening.svgz b/kdepasswd/kcm/pics/sources/listening.svgz new file mode 100644 index 00000000..7e0e5ef2 Binary files /dev/null and b/kdepasswd/kcm/pics/sources/listening.svgz differ diff --git a/kdepasswd/kcm/pics/sources/notme.svgz b/kdepasswd/kcm/pics/sources/notme.svgz new file mode 100644 index 00000000..5b4a4743 Binary files /dev/null and b/kdepasswd/kcm/pics/sources/notme.svgz differ diff --git a/kdepasswd/kcm/pics/sources/tv.svgz b/kdepasswd/kcm/pics/sources/tv.svgz new file mode 100644 index 00000000..d6665063 Binary files /dev/null and b/kdepasswd/kcm/pics/sources/tv.svgz differ diff --git a/kdepasswd/kcm/settings.kcfgc b/kdepasswd/kcm/settings.kcfgc new file mode 100644 index 00000000..772c87a6 --- /dev/null +++ b/kdepasswd/kcm/settings.kcfgc @@ -0,0 +1,5 @@ +File=kcm_useraccount.kcfg +ClassName=KCFGUserAccount +Singleton=true +Mutators=false + diff --git a/kdepasswd/kdepasswd.cpp b/kdepasswd/kdepasswd.cpp new file mode 100644 index 00000000..866f454e --- /dev/null +++ b/kdepasswd/kdepasswd.cpp @@ -0,0 +1,95 @@ +/* vi: ts=8 sts=4 sw=4 + * + * This file is part of the KDE project, module kdesu. + * Copyright (C) 2000 Geert Jansen + + Permission to use, copy, modify, and distribute this software + and its documentation for any purpose and without fee is hereby + granted, provided that the above copyright notice appear in all + copies and that both that the copyright notice and this + permission notice and warranty disclaimer appear in supporting + documentation, and that the name of the author not be used in + advertising or publicity pertaining to distribution of the + software without specific, written prior permission. + + The author disclaim all warranties with regard to this + software, including all implied warranties of merchantability + and fitness. In no event shall the author be liable for any + special, indirect or consequential damages or any damages + whatsoever resulting from loss of use, data or profits, whether + in an action of contract, negligence or other tortious action, + arising out of or in connection with the use or performance of + this software. + + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "passwd.h" +#include "passwddlg.h" + + +int main(int argc, char **argv) +{ + KAboutData aboutData("kdepasswd", 0, ki18n("KDE passwd"), + KDE_VERSION_STRING, ki18n("Changes a UNIX password."), + KAboutData::License_Artistic, ki18n("Copyright (c) 2000 Geert Jansen")); + aboutData.addAuthor(ki18n("Geert Jansen"), ki18n("Maintainer"), + "jansen@kde.org"); + aboutData.setProgramIconName( "preferences-desktop-user-password" ); + + KCmdLineArgs::init(argc, argv, &aboutData); + + KCmdLineOptions options; + options.add("+[user]", ki18n("Change password of this user")); + KCmdLineArgs::addCmdLineOptions(options); + KUniqueApplication::addCmdLineOptions(); + + + if (!KUniqueApplication::start()) { + kDebug() << "kdepasswd is already running"; + return 2; + } + + KUniqueApplication app; + + KUser ku; + QString user; + bool bRoot = ku.isSuperUser(); + KCmdLineArgs *args = KCmdLineArgs::parsedArgs(); + + if (args->count()) + user = args->arg(0); + + /* You must be able to run "kdepasswd loginName" */ + if ( !user.isEmpty() && user!=KUser().loginName() && !bRoot) + { + KMessageBox::sorry(0, i18n("You need to be root to change the password of other users.")); + return 1; + } + + QByteArray oldpass; + if (!bRoot) + { + int result = KDEpasswd1Dialog::getPassword(oldpass); + if (result != KDEpasswd1Dialog::Accepted) + return 1; + } + + KDEpasswd2Dialog *dlg = new KDEpasswd2Dialog(oldpass, user.toLocal8Bit()); + + + dlg->exec(); + if (dlg->result() == KDEpasswd2Dialog::Rejected) + return 1; + + return 0; +} + diff --git a/kdepasswd/kdepasswd.desktop b/kdepasswd/kdepasswd.desktop new file mode 100755 index 00000000..66e152c7 --- /dev/null +++ b/kdepasswd/kdepasswd.desktop @@ -0,0 +1,99 @@ +[Desktop Entry] +Exec=kdepasswd +Icon=preferences-desktop-user-password +Type=Application +Categories=Qt;KDE;Settings; +NoDisplay=true +X-DBUS-StartupType=Unique +Name=Change Password +Name[af]=Verander Wagwoord +Name[ar]=غيّر كلمة السر +Name[as]=গুপ্তশব্দ সলনি কৰক +Name[ast]=Camudar la contraseña +Name[be]=Змяніць пароль +Name[be@latin]=Źmiena parola +Name[bg]=Промяна на парола +Name[bn]=পাসওয়ার্ড পরিবর্তন +Name[bn_IN]=পাসওয়ার্ড পরিবর্তন +Name[br]=Kemmañ an Tremenger +Name[bs]=Promijeni lozinku +Name[ca]=Canvia contrasenya +Name[ca@valencia]=Canvia contrasenya +Name[cs]=Změna hesla +Name[csb]=Zmieni parolã +Name[cy]=Newid Cyfrinair +Name[da]=Skift adgangskode +Name[de]=Passwort ändern +Name[el]=Αλλαγή κωδικού πρόσβασης +Name[en_GB]=Change Password +Name[eo]=Ŝanĝi la pasvorton +Name[es]=Cambiar la contraseña +Name[et]=Parooli muutmine +Name[eu]=Aldatu pasahitza +Name[fa]=تغییر اسم رمز +Name[fi]= +Name[fr]=Modifier le mot de passe +Name[fy]=Wachtwurd wizigje +Name[ga]=Athraigh Focal Faire +Name[gl]=Mudar o contrasinal +Name[gu]=પાસવર્ડ બદલો +Name[he]=שינוי סיסמה +Name[hi]=पासवर्ड बदलें +Name[hne]=पासवर्ड बदलव +Name[hr]=Promjena lozinke +Name[hsb]=Hesło změnić +Name[hu]=Jelszóváltoztatás +Name[ia]=Cambia contrasigno +Name[id]=Ubah Sandi +Name[is]=Breyta lykilorði +Name[it]=Cambia password +Name[ja]=パスワードを変更 +Name[ka]=პაროლის შეცვლა +Name[kk]=Парольді өзгерту +Name[km]=ផ្លាស់ប្ដូរ​ពាក្យ​សម្ងាត់ +Name[kn]=ಗುಪ್ತಪದವನ್ನು (ಪಾಸ್ ವರ್ಡ್) ಬದಲಾಯಿಸು +Name[ko]=암호 변경 +Name[ku]=Nasnavê Biguherîne +Name[lt]=Pakeiskite slaptažodį +Name[lv]=Mainīt paroli +Name[mai]=कूटशब्द बदलू +Name[mk]=Измени лозинка +Name[ml]=അടയാളവാക്ക് മാറ്റുക +Name[mr]=गुप्तशब्द बदला +Name[ms]=Ubah Kata Laluan +Name[nb]=Endre passord +Name[nds]=Passwoort ännern +Name[ne]=पासवर्ड परिवर्तन गर्नुहोस् +Name[nl]=Wachtwoord wijzigen +Name[nn]=Endra passord +Name[oc]=Modificar lo mot de pas +Name[or]=ପ୍ରବେଶ ସଂକେତ ପରିବର୍ତ୍ତନ କରନ୍ତୁ +Name[pa]=ਪਾਸਵਰਡ ਬਦਲੋ +Name[pl]=Zmień hasło +Name[pt]=Mudar a Senha +Name[pt_BR]=Alterar senha +Name[ro]=Schimbare parolă +Name[ru]=Изменить пароль +Name[se]=Rievdat beassansáni +Name[si]=රහස්පදය වෙනස් කරන්න +Name[sk]=Zmena hesla +Name[sl]=Spremeni geslo +Name[sr]=Промени лозинку +Name[sr@ijekavian]=Промијени лозинку +Name[sr@ijekavianlatin]=Promijeni lozinku +Name[sr@latin]=Promeni lozinku +Name[sv]=Ändra lösenord +Name[ta]=கடவுச்சொல்லை மாற்று +Name[te]=పాస్ వర్డు మార్చండి +Name[tg]=Ивази парол +Name[th]=เปลี่ยนรหัสผ่าน +Name[tr]=Parola Değiştir +Name[ug]=ئىم ئۆزگەرتىش +Name[uk]=Зміна пароля +Name[uz]=Maxfiy soʻzni oʻzgartirish +Name[uz@cyrillic]=Махфий сўзни ўзгартириш +Name[vi]=Đổi mật khẩu +Name[wa]=Candjî li scret +Name[x-test]=xxChange Passwordxx +Name[zh_CN]=更改密码 +Name[zh_TW]=改變密碼 diff --git a/kdepasswd/passwd.cpp b/kdepasswd/passwd.cpp new file mode 100644 index 00000000..670da5cc --- /dev/null +++ b/kdepasswd/passwd.cpp @@ -0,0 +1,293 @@ +/* vi: ts=8 sts=4 sw=4 + * + * This file is part of the KDE project, module kdesu. + * Copyright (C) 1999,2000 Geert Jansen + + Permission to use, copy, modify, and distribute this software + and its documentation for any purpose and without fee is hereby + granted, provided that the above copyright notice appear in all + copies and that both that the copyright notice and this + permission notice and warranty disclaimer appear in supporting + documentation, and that the name of the author not be used in + advertising or publicity pertaining to distribution of the + software without specific, written prior permission. + + The author disclaim all warranties with regard to this + software, including all implied warranties of merchantability + and fitness. In no event shall the author be liable for any + special, indirect or consequential damages or any damages + whatsoever resulting from loss of use, data or profits, whether + in an action of contract, negligence or other tortious action, + arising out of or in connection with the use or performance of + this software. + + * passwd.cpp: Change a user's password. + */ + +#include "passwd.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + + +PasswdProcess::PasswdProcess(const QByteArray &user) +{ + struct passwd *pw; + + if (user.isEmpty()) + { + pw = getpwuid(getuid()); + if (pw == 0L) + { + kDebug(1512) << "You don't exist!\n"; + return; + } + m_User = pw->pw_name; + } else + { + pw = getpwnam(user); + if (pw == 0L) + { + kDebug(1512) << "User " << user << "does not exist.\n"; + return; + } + m_User = user; + } + bOtherUser = (pw->pw_uid != getuid()); +} + + +PasswdProcess::~PasswdProcess() +{ +} + + +int PasswdProcess::checkCurrent(const char *oldpass) +{ + return exec(oldpass, 0L, 1); +} + + +int PasswdProcess::exec(const char *oldpass, const char *newpass, + int check) +{ + if (m_User.isEmpty()) + return -1; + // if (check) + // setTerminal(true); + + // Try to set the default locale to make the parsing of the output + // of `passwd' easier. + ::setenv("LANG","C", true /* override */); + + QList args; + if(bOtherUser) + args += m_User; + int ret = PtyProcess::exec("passwd", args); + if (ret < 0) + { + kDebug(1512) << "Passwd not found!\n"; + return PasswdNotFound; + } + + ret = ConversePasswd(oldpass, newpass, check); + if (ret < 0) + kDebug(1512) << "Conversation with passwd failed. pid = " << pid(); + + if ((waitForChild() != 0) && !check) + return PasswordNotGood; + + return ret; +} + + +/* + * The tricky thing is to make this work with a lot of different passwd + * implementations. We _don't_ want implementation specific routines. + * Return values: -1 = unknown error, 0 = ok, >0 = error code. + */ + +int PasswdProcess::ConversePasswd(const char *oldpass, const char *newpass, + int check) +{ + QByteArray line, errline; + int state = 0; + + while (state != 7) + { + line = readLine(); + if (line.isNull()) + { + return -1; + } + + if (state == 0 && isPrompt(line, "new")) + // If root is changing a user's password, + // passwd can't prompt for the original password. + // Therefore, we have to start at state=2. + state=2; + + switch (state) + { + case 0: + // Eat garbage, wait for prompt + m_Error += line+'\n'; + if (isPrompt(line, "password")) + { + WaitSlave(); + write(fd(), oldpass, strlen(oldpass)); + write(fd(), "\n", 1); + state++; + break; + } + if (m_bTerminal) + fputs(line, stdout); + break; + + case 1: case 3: case 6: + // Wait for \n + if (line.isEmpty()) + { + state++; + break; + } + // error + return -1; + + case 2: + m_Error = ""; + if( line.contains("again")) + { + m_Error = line; + kill(m_Pid, SIGKILL); + waitForChild(); + return PasswordIncorrect; + } + // Wait for second prompt. + errline = line; // use first line for error message + while (!isPrompt(line, "new")) + { + line = readLine(); + if (line.isNull()) + { + // We didn't get the new prompt so assume incorrect password. + if (m_bTerminal) + fputs(errline, stdout); + m_Error = errline; + return PasswordIncorrect; + } + } + + // we have the new prompt + if (check) + { + kill(m_Pid, SIGKILL); + waitForChild(); + return 0; + } + WaitSlave(); + write(fd(), newpass, strlen(newpass)); + write(fd(), "\n", 1); + state++; + break; + + case 4: + // Wait for third prompt + if (isPrompt(line, "re")) + { + WaitSlave(); + write(fd(), newpass, strlen(newpass)); + write(fd(), "\n", 1); + state += 2; + break; + } + // Warning or error about the new password + if (m_bTerminal) + fputs(line, stdout); + m_Error = line + '\n'; + state++; + break; + + case 5: + // Wait for either a "Reenter password" or a "Enter password" prompt + if (isPrompt(line, "re")) + { + WaitSlave(); + write(fd(), newpass, strlen(newpass)); + write(fd(), "\n", 1); + state++; + break; + } + else if (isPrompt(line, "password")) + { + kill(m_Pid, SIGKILL); + waitForChild(); + return PasswordNotGood; + } + if (m_bTerminal) + fputs(line, stdout); + m_Error += line + '\n'; + break; + } + } + + // Are we ok or do we still get an error thrown at us? + m_Error = ""; + state = 0; + while (state != 1) + { + line = readLine(); + if (line.isNull()) + { + // No more input... OK + return 0; + } + if (isPrompt(line, "password")) + { + // Uh oh, another prompt. Not good! + kill(m_Pid, SIGKILL); + waitForChild(); + return PasswordNotGood; + } + m_Error += line + '\n'; // Collect error message + } + + kDebug(1512) << "Conversation ended successfully.\n"; + return 0; +} + + +bool PasswdProcess::isPrompt(const QByteArray &line, const char *word) +{ + unsigned i, j, colon; + unsigned int lineLength(line.length()); + for (i=0,j=0,colon=0; i + + Permission to use, copy, modify, and distribute this software + and its documentation for any purpose and without fee is hereby + granted, provided that the above copyright notice appear in all + copies and that both that the copyright notice and this + permission notice and warranty disclaimer appear in supporting + documentation, and that the name of the author not be used in + advertising or publicity pertaining to distribution of the + software without specific, written prior permission. + + The author disclaim all warranties with regard to this + software, including all implied warranties of merchantability + and fitness. In no event shall the author be liable for any + special, indirect or consequential damages or any damages + whatsoever resulting from loss of use, data or profits, whether + in an action of contract, negligence or other tortious action, + arising out of or in connection with the use or performance of + this software. + + */ + +#ifndef PASSWD_H +#define PASSWD_H + +#include +#include "process.h" + +/** + * A C++ API to passwd. + */ + +class PasswdProcess + : public PtyProcess +{ +public: + PasswdProcess(const QByteArray &user = QByteArray()); + ~PasswdProcess(); + + enum Errors { PasswdNotFound=1, PasswordIncorrect, PasswordNotGood }; + + int checkCurrent(const char *oldpass); + int exec(const char *oldpass, const char *newpass, int check=0); + + QByteArray error() { return m_Error; } + +private: + bool isPrompt(const QByteArray &line, const char *word=0L); + int ConversePasswd(const char *oldpass, const char *newpass, + int check); + + QByteArray m_User, m_Error; + bool bOtherUser; +}; + + +#endif // PASSWD_H diff --git a/kdepasswd/passwddlg.cpp b/kdepasswd/passwddlg.cpp new file mode 100644 index 00000000..e4d8a100 --- /dev/null +++ b/kdepasswd/passwddlg.cpp @@ -0,0 +1,191 @@ +/* vi: ts=8 sts=4 sw=4 + * + * This file is part of the KDE project, module kdesu. + * Copyright (C) 2000 Geert Jansen + + Permission to use, copy, modify, and distribute this software + and its documentation for any purpose and without fee is hereby + granted, provided that the above copyright notice appear in all + copies and that both that the copyright notice and this + permission notice and warranty disclaimer appear in supporting + documentation, and that the name of the author not be used in + advertising or publicity pertaining to distribution of the + software without specific, written prior permission. + + The author disclaim all warranties with regard to this + software, including all implied warranties of merchantability + and fitness. In no event shall the author be liable for any + special, indirect or consequential damages or any damages + whatsoever resulting from loss of use, data or profits, whether + in an action of contract, negligence or other tortious action, + arising out of or in connection with the use or performance of + this software. + */ + +#include "passwddlg.h" +#include "passwd.h" + +#include +#include + +KDEpasswd1Dialog::KDEpasswd1Dialog() + : KPasswordDialog() +{ + setCaption(i18nc("@title:window", "Change Password")); + setPrompt(i18n("Please enter your current password:")); +} + + +KDEpasswd1Dialog::~KDEpasswd1Dialog() +{ +} + +void KDEpasswd1Dialog::accept() +{ + PasswdProcess proc(0); + + int ret = proc.checkCurrent(password().toLocal8Bit()); + switch (ret) + { + case -1: + { + QString msg = QString::fromLocal8Bit(proc.error()); + if (!msg.isEmpty()) + msg = "

    \"" + msg + "\""; + msg = "" + i18n("Conversation with 'passwd' failed.") + msg; + KMessageBox::error(this, msg); + done(Rejected); + return; + } + + case 0: + return KPasswordDialog::accept(); + + case PasswdProcess::PasswdNotFound: + KMessageBox::error(this, i18n("Could not find the program 'passwd'.")); + done(Rejected); + return; + + case PasswdProcess::PasswordIncorrect: + KMessageBox::sorry(this, i18n("Incorrect password. Please try again.")); + return; + + default: + KMessageBox::error(this, i18n("Internal error: illegal return value " + "from PasswdProcess::checkCurrent.")); + done(Rejected); + return; + } +} + + +// static +int KDEpasswd1Dialog::getPassword(QByteArray &password) +{ + KDEpasswd1Dialog *dlg = new KDEpasswd1Dialog(); + int res = dlg->exec(); + if (res == Accepted) + password = dlg->password().toLocal8Bit(); + delete dlg; + return res; +} + + + +KDEpasswd2Dialog::KDEpasswd2Dialog(const char *oldpass, const QByteArray &user) + : KNewPasswordDialog() +{ + m_Pass = oldpass; + m_User = user; + + setCaption(i18nc("@title:window", "Change Password")); + if (m_User.isEmpty()) + setPrompt(i18n("Please enter your new password:")); + else + setPrompt(i18n("Please enter the new password for user %1:", QString::fromLocal8Bit(m_User))); +} + + +KDEpasswd2Dialog::~KDEpasswd2Dialog() +{ +} + + +void KDEpasswd2Dialog::accept() +{ + PasswdProcess proc(m_User); + + QString p; + if(!checkAndGetPassword(&p)){ + return; + } + + if (p.length() > 8) + { + switch(KMessageBox::warningYesNoCancel(this, + m_User.isEmpty() ? + i18n("Your password is longer than 8 characters. On some " + "systems, this can cause problems. You can truncate " + "the password to 8 characters, or leave it as it is.") : + i18n("The password is longer than 8 characters. On some " + "systems, this can cause problems. You can truncate " + "the password to 8 characters, or leave it as it is.") + , + i18n("Password Too Long"), + KGuiItem(i18n("Truncate")), + KGuiItem(i18n("Use as Is")), + KStandardGuiItem::cancel(), + "truncatePassword")) + { + case KMessageBox::Yes : + p=p.left(8); + break; + case KMessageBox::No : + break; + default : return; + } + } + + int ret = proc.exec(m_Pass, p.toLocal8Bit()); + switch (ret) + { + case 0: + { + hide(); + QString msg = QString::fromLocal8Bit(proc.error()); + if (!msg.isEmpty()) + msg = "

    \"" + msg + "\""; + msg = "" + i18n("Your password has been changed.") + msg; + KMessageBox::information(0L, msg); + return KNewPasswordDialog::accept(); + } + + case PasswdProcess::PasswordNotGood: + { + QString msg = QString::fromLocal8Bit(proc.error()); + if (!msg.isEmpty()) + msg = "

    \"" + msg + "\""; + msg = "" + i18n("Your password has not been changed.") + msg; + + // The pw change did not succeed. Print the error. + KMessageBox::sorry(this, msg); + return; + } + + default: + QString msg = QString::fromLocal8Bit(proc.error()); + if (!msg.isEmpty()) + msg = "

    \"" + msg + "\""; + msg = "" + i18n("Conversation with 'passwd' failed.") + msg; + KMessageBox::sorry(this, msg); + done(Rejected); + return; + } + + return KNewPasswordDialog::accept(); + +} + + +#include "moc_passwddlg.cpp" + diff --git a/kdepasswd/passwddlg.h b/kdepasswd/passwddlg.h new file mode 100644 index 00000000..155da24e --- /dev/null +++ b/kdepasswd/passwddlg.h @@ -0,0 +1,65 @@ +/* vi: ts=8 sts=4 sw=4 + * + * This file is part of the KDE project, module kdesu. + * Copyright (C) 2000 Geert Jansen + + Permission to use, copy, modify, and distribute this software + and its documentation for any purpose and without fee is hereby + granted, provided that the above copyright notice appear in all + copies and that both that the copyright notice and this + permission notice and warranty disclaimer appear in supporting + documentation, and that the name of the author not be used in + advertising or publicity pertaining to distribution of the + software without specific, written prior permission. + + The author disclaim all warranties with regard to this + software, including all implied warranties of merchantability + and fitness. In no event shall the author be liable for any + special, indirect or consequential damages or any damages + whatsoever resulting from loss of use, data or profits, whether + in an action of contract, negligence or other tortious action, + arising out of or in connection with the use or performance of + this software. + */ + +#ifndef PASSWDDLG_H +#define PASSWDDLG_H + +#include +#include +#include + +class KDEpasswd1Dialog + : public KPasswordDialog +{ + Q_OBJECT + +public: + KDEpasswd1Dialog(); + ~KDEpasswd1Dialog(); + + static int getPassword(QByteArray &password); + + void accept(); +}; + + +class KDEpasswd2Dialog + : public KNewPasswordDialog +{ + Q_OBJECT + +public: + KDEpasswd2Dialog(const char *oldpass, const QByteArray &user); + ~KDEpasswd2Dialog(); + + void accept(); + +private: + const char *m_Pass; + QByteArray m_User; +}; + + + +#endif // PASSWDDLG_H diff --git a/kdepasswd/process.cpp b/kdepasswd/process.cpp new file mode 100644 index 00000000..e3864ba3 --- /dev/null +++ b/kdepasswd/process.cpp @@ -0,0 +1,538 @@ +/* vi: ts=8 sts=4 sw=4 + * + * This file is part of the KDE project, module kdesu. + * Copyright (C) 1999,2000 Geert Jansen + * + * This file contains code from TEShell.C of the KDE konsole. + * Copyright (c) 1997,1998 by Lars Doelle + * + * This is free software; you can use this library under the GNU Library + * General Public License, version 2. See the file "COPYING.LIB" for the + * exact licensing terms. + * + * process.cpp: Functionality to build a front end to password asking + * terminal programs. + */ + +#include "process.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include // Needed on some systems. + +#include +#include + +#include +#include +#include +#include +#include + +/* +** Wait for @p ms miliseconds +** @param fd file descriptor +** @param ms time to wait in miliseconds +** @return +*/ +int PtyProcess::waitMS(int fd,int ms) +{ + struct timeval tv; + tv.tv_sec = 0; + tv.tv_usec = 1000*ms; + + fd_set fds; + FD_ZERO(&fds); + FD_SET(fd,&fds); + return select(fd+1, &fds, 0L, 0L, &tv); +} + +// XXX this function is nonsense: +// - for our child, we could use waitpid(). +// - the configurability at this place it *complete* braindamage +/* +** Basic check for the existence of @p pid. +** Returns true iff @p pid is an extant process. +*/ +bool PtyProcess::checkPid(pid_t pid) +{ + KSharedConfig::Ptr config = KGlobal::config(); + KConfigGroup cg(config, "super-user-command"); + QString superUserCommand = cg.readEntry("super-user-command", "sudo"); + //sudo does not accept signals from user so we except it + if (superUserCommand == "sudo") { + return true; + } else { + return kill(pid, 0) == 0; + } +} + +/* +** Check process exit status for process @p pid. +** On error (no child, no exit), return Error (-1). +** If child @p pid has exited, return its exit status, +** (which may be zero). +** If child @p has not exited, return NotExited (-2). +*/ + +int PtyProcess::checkPidExited(pid_t pid) +{ + int state, ret; + ret = waitpid(pid, &state, WNOHANG); + + if (ret < 0) + { + kError(1512) << "waitpid():" << ::strerror(errno); + return Error; + } + if (ret == pid) + { + if (WIFEXITED(state)) + return WEXITSTATUS(state); + return Killed; + } + + return NotExited; +} + + +class PtyProcess::PtyProcessPrivate +{ +public: + PtyProcessPrivate() : m_pPTY(0L) {} + ~PtyProcessPrivate() + { + delete m_pPTY; + } + QList env; + KPty *m_pPTY; + QByteArray m_Inbuf; +}; + + +PtyProcess::PtyProcess() + :d(new PtyProcessPrivate) +{ + m_bTerminal = false; + m_bErase = false; +} + + +int PtyProcess::init() +{ + delete d->m_pPTY; + d->m_pPTY = new KPty(); + if (!d->m_pPTY->open()) + { + kError(1512) << "Failed to open PTY."; + return -1; + } + d->m_Inbuf.resize(0); + return 0; +} + + +PtyProcess::~PtyProcess() +{ + delete d; +} + +/** Set additional environment variables. */ +void PtyProcess::setEnvironment( const QList &env ) +{ + d->env = env; +} + +int PtyProcess::fd() const +{ + return d->m_pPTY ? d->m_pPTY->masterFd() : -1; +} + +int PtyProcess::pid() const +{ + return m_Pid; +} + +/** Returns the additional environment variables set by setEnvironment() */ +QList PtyProcess::environment() const +{ + return d->env; +} + + +QByteArray PtyProcess::readAll(bool block) +{ + QByteArray ret; + if (!d->m_Inbuf.isEmpty()) + { + // if there is still something in the buffer, we need not block. + // we should still try to read any further output, from the fd, though. + block = false; + ret = d->m_Inbuf; + d->m_Inbuf.resize(0); + } + + int flags = fcntl(fd(), F_GETFL); + if (flags < 0) + { + kError(1512) << "fcntl(F_GETFL):" << ::strerror(errno); + return ret; + } + int oflags = flags; + if (block) + flags &= ~O_NONBLOCK; + else + flags |= O_NONBLOCK; + + if ((flags != oflags) && (fcntl(fd(), F_SETFL, flags) < 0)) + { + // We get an error here when the child process has closed + // the file descriptor already. + return ret; + } + + while (1) + { + ret.reserve(ret.size() + 0x8000); + int nbytes = read(fd(), ret.data() + ret.size(), 0x8000); + if (nbytes == -1) + { + if (errno == EINTR) + continue; + else break; + } + if (nbytes == 0) + break; // nothing available / eof + + ret.resize(ret.size() + nbytes); + break; + } + + return ret; +} + + +QByteArray PtyProcess::readLine(bool block) +{ + d->m_Inbuf = readAll(block); + + QByteArray ret; + if (!d->m_Inbuf.isEmpty()) + { + int pos = d->m_Inbuf.indexOf('\n'); + if (pos == -1) + { + // NOTE: this means we return something even if there in no full line! + ret = d->m_Inbuf; + d->m_Inbuf.resize(0); + } else + { + ret = d->m_Inbuf.left(pos); + d->m_Inbuf.remove(0, pos+1); + } + } + + return ret; +} + + +void PtyProcess::writeLine(const QByteArray &line, bool addnl) +{ + if (!line.isEmpty()) + write(fd(), line, line.length()); + if (addnl) + write(fd(), "\n", 1); +} + + +void PtyProcess::unreadLine(const QByteArray &line, bool addnl) +{ + QByteArray tmp = line; + if (addnl) + tmp += '\n'; + if (!tmp.isEmpty()) + d->m_Inbuf.prepend(tmp); +} + +void PtyProcess::setExitString(const QByteArray &exit) +{ + m_Exit = exit; +} + +/* + * Fork and execute the command. This returns in the parent. + */ + +int PtyProcess::exec(const QByteArray &command, const QList &args) +{ + kDebug(1512) << "Running" << command; + + if (init() < 0) + return -1; + + if ((m_Pid = fork()) == -1) + { + kError(1512) << "fork():" << ::strerror(errno); + return -1; + } + + // Parent + if (m_Pid) + { + d->m_pPTY->closeSlave(); + return 0; + } + + // Child + if (setupTTY() < 0) + _exit(1); + + for (int i = 0; i < d->env.count(); ++i) + { + putenv(const_cast(d->env.at(i).constData())); + } + unsetenv("KDE_FULL_SESSION"); + // for : Qt: Session management error + unsetenv("SESSION_MANAGER"); + // QMutex::lock , deadlocks without that. + // you cannot connect to the user's session bus from another UID + unsetenv("DBUS_SESSION_BUS_ADDRESS"); + + // set temporarily LC_ALL to C, for su (to be able to parse "Password:") + const QByteArray old_lc_all = qgetenv( "LC_ALL" ); + if( !old_lc_all.isEmpty() ) + qputenv( "KDESU_LC_ALL", old_lc_all ); + else + unsetenv( "KDESU_LC_ALL" ); + qputenv("LC_ALL", "C"); + + // From now on, terminal output goes through the tty. + + QByteArray path; + if (command.contains('/')) { + path = command; + } else { + QString file = KStandardDirs::findExe(command); + if (file.isEmpty()) { + kError(1512) << command << "not found."; + _exit(1); + } + path = QFile::encodeName(file); + } + + const char **argp = (const char **)malloc((args.count()+2)*sizeof(char *)); + int i = 1; + argp[i] = path; + foreach (QByteArray it, args) { + argp[i] = it; + i++; + } + argp[i] = NULL; + + execv(path, const_cast(argp)); + free(argp); + + kError(1512) << "execv(" << path << "):" << ::strerror(errno); + _exit(1); + return -1; // Shut up compiler. Never reached. +} + + +/* + * Wait until the terminal is set into no echo mode. At least one su + * (RH6 w/ Linux-PAM patches) sets noecho mode AFTER writing the Password: + * prompt, using TCSAFLUSH. This flushes the terminal I/O queues, possibly + * taking the password with it. So we wait until no echo mode is set + * before writing the password. + * Note that this is done on the slave fd. While Linux allows tcgetattr() on + * the master side, Solaris doesn't. + */ + +int PtyProcess::WaitSlave() +{ + kDebug(1512) << "Child pid" << m_Pid; + + struct termios tio; + while (1) + { + if (!checkPid(m_Pid)) + { + kError(1512) << "process has exited while waiting for password."; + return -1; + } + if (!d->m_pPTY->tcGetAttr(&tio)) + { + kError(1512) << "tcgetattr():" << ::strerror(errno); + return -1; + } + if (tio.c_lflag & ECHO) + { + kDebug(1512) << "Echo mode still on."; + usleep(10000); + continue; + } + break; + } + return 0; +} + + +int PtyProcess::enableLocalEcho(bool enable) +{ + return d->m_pPTY->setEcho(enable) ? 0 : -1; +} + + +void PtyProcess::setTerminal(bool terminal) +{ + m_bTerminal = terminal; +} + +void PtyProcess::setErase(bool erase) +{ + m_bErase = erase; +} + +/* + * Copy output to stdout until the child process exits, or a line of output + * matches `m_Exit'. + * We have to use waitpid() to test for exit. Merely waiting for EOF on the + * pty does not work, because the target process may have children still + * attached to the terminal. + */ + +int PtyProcess::waitForChild() +{ + fd_set fds; + FD_ZERO(&fds); + QByteArray remainder; + + while (1) + { + FD_SET(fd(), &fds); + + // specify timeout to make sure select() does not block, even if the + // process is dead / non-responsive. It does not matter if we abort too + // early. In that case 0 is returned, and we'll try again in the next + // iteration. (As long as we don't consitently time out in each iteration) + timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = 100000; + int ret = select(fd()+1, &fds, 0L, 0L, &timeout); + if (ret == -1) + { + if (errno != EINTR) + { + kError(1512) << "select():" << ::strerror(errno); + return -1; + } + ret = 0; + } + + if (ret) + { + forever { + QByteArray output = readAll(false); + if (output.isEmpty()) + break; + if (m_bTerminal) + { + fwrite(output.constData(), output.size(), 1, stdout); + fflush(stdout); + } + if (!m_Exit.isEmpty()) + { + // match exit string only at line starts + remainder += output; + while (remainder.length() >= m_Exit.length()) { + if (remainder.startsWith(m_Exit)) { + kill(m_Pid, SIGTERM); + remainder.remove(0, m_Exit.length()); + } + int off = remainder.indexOf('\n'); + if (off < 0) + break; + remainder.remove(0, off + 1); + } + } + } + } + + ret = checkPidExited(m_Pid); + if (ret == Error) + { + if (errno == ECHILD) return 0; + else return 1; + } + else if (ret == Killed) + { + return 0; + } + else if (ret == NotExited) + { + // keep checking + } + else + { + return ret; + } + } +} + +/* + * SetupTTY: Creates a new session. The filedescriptor "fd" should be + * connected to the tty. It is closed after the tty is reopened to make it + * our controlling terminal. This way the tty is always opened at least once + * so we'll never get EIO when reading from it. + */ + +int PtyProcess::setupTTY() +{ + // Reset signal handlers + for (int sig = 1; sig < NSIG; sig++) + KDE_signal(sig, SIG_DFL); + KDE_signal(SIGHUP, SIG_IGN); + + d->m_pPTY->setCTty(); + + // Connect stdin, stdout and stderr + int slave = d->m_pPTY->slaveFd(); + dup2(slave, 0); dup2(slave, 1); dup2(slave, 2); + + // Close all file handles + // XXX this caused problems in KProcess - not sure why anymore. -- ??? + // Because it will close the start notification pipe. -- ossi + struct rlimit rlp; + getrlimit(RLIMIT_NOFILE, &rlp); + for (int i = 3; i < (int)rlp.rlim_cur; i++) + close(i); + + // Disable OPOST processing. Otherwise, '\n' are (on Linux at least) + // translated to '\r\n'. + struct ::termios tio; + if (tcgetattr(0, &tio) < 0) + { + kError(1512) << "tcgetattr():" << ::strerror(errno); + return -1; + } + tio.c_oflag &= ~OPOST; + if (tcsetattr(0, TCSANOW, &tio) < 0) + { + kError(1512) << "tcsetattr():" << ::strerror(errno); + return -1; + } + + return 0; +} diff --git a/kdepasswd/process.h b/kdepasswd/process.h new file mode 100644 index 00000000..db88c4b0 --- /dev/null +++ b/kdepasswd/process.h @@ -0,0 +1,195 @@ +///////// XXX migrate it to kprocess ///////////////// + +/* vi: ts=8 sts=4 sw=4 + * + * This file is part of the KDE project, module kdesu. + * Copyright (C) 1999,2000 Geert Jansen + * + * This is free software; you can use this library under the GNU Library + * General Public License, version 2. See the file "COPYING.LIB" for the + * exact licensing terms. + */ + +#ifndef __Process_h_Included__ +#define __Process_h_Included__ + +#include + +#include +#include +#include +#include + +#include + +/** \class PtyProcess process.h kdesu/process.h + * Synchronous communication with tty programs. + * + * PtyProcess provides synchronous communication with tty based programs. + * The communications channel used is a pseudo tty (as opposed to a pipe) + * This means that programs which require a terminal will work. + */ + +class PtyProcess +{ +public: + PtyProcess(); + virtual ~PtyProcess(); + + /** + * Forks off and execute a command. The command's standard in and output + * are connected to the pseudo tty. They are accessible with readLine + * and writeLine. + * @param command The command to execute. + * @param args The arguments to the command. + * @return 0 on success, -1 on error. errno might give more information then. + */ + int exec(const QByteArray &command, const QList &args); + + /** + * Reads a line from the program's standard out. Depending on the @em block + * parameter, this call blocks until something was read. + * Note that in some situations this function will return less than a full + * line of output, but never more. Newline characters are stripped. + * @param block Block until a full line is read? + * @return The output string. + */ + QByteArray readLine(bool block=true); + + /** + * Read all available output from the program's standard out. + * @param block If no output is in the buffer, should the function block + * (else it will return an empty QByteArray)? + * @return The output. + */ + QByteArray readAll(bool block=true); + + /** + * Writes a line of text to the program's standard in. + * @param line The text to write. + * @param addNewline Adds a '\n' to the line. + */ + void writeLine(const QByteArray &line, bool addNewline=true); + + /** + * Puts back a line of input. + * @param line The line to put back. + * @param addNewline Adds a '\n' to the line. + */ + void unreadLine(const QByteArray &line, bool addNewline=true); + + /** + * Sets the exit string. If a line of program output matches this, + * waitForChild() will terminate the program and return. + */ + void setExitString(const QByteArray &exit); + + /** + * Waits for the child to exit. See also setExitString. + */ + int waitForChild(); + + /** + * Waits until the pty has cleared the ECHO flag. This is useful + * when programs write a password prompt before they disable ECHO. + * Disabling it might flush any input that was written. + */ + int WaitSlave(); + + /** + * Enables/disables local echo on the pseudo tty. + */ + int enableLocalEcho(bool enable=true); + + /** + * Enables/disables terminal output. Relevant only to some subclasses. + */ + void setTerminal(bool terminal); + + /** + * Overwrites the password as soon as it is used. Relevant only to + * some subclasses. + */ + void setErase(bool erase); + + /** + * Set additinal environment variables. + */ + void setEnvironment( const QList &env ); + + /** + * Returns the filedescriptor of the process. + */ + int fd() const; + + /** + * Returns the pid of the process. + */ + int pid() const; + +public /* static */: + /* + ** This is a collection of static functions that can be + ** used for process control inside kdesu. I'd suggest + ** against using this publicly. There are probably + ** nicer Qt based ways to do what you want. + */ + + /** + ** Wait @p ms miliseconds (ie. 1/10th of a second is 100ms), + ** using @p fd as a filedescriptor to wait on. Returns + ** select(2)'s result, which is -1 on error, 0 on timeout, + ** or positive if there is data on one of the selected fd's. + ** + ** @p ms must be in the range 0..999 (i.e. the maximum wait + ** duration is 999ms, almost one second). + */ + static int waitMS(int fd,int ms); + + + /** + ** Basic check for the existence of @p pid. + ** Returns true iff @p pid is an extant process, + ** (one you could kill - see man kill(2) for signal 0). + */ + static bool checkPid(pid_t pid); + + + /** Error return values for checkPidExited() */ + enum checkPidStatus { Error=-1, /**< No child */ + NotExited=-2, /**< Child hasn't exited */ + Killed=-3 /**< Child terminated by signal */ + } ; + + /** + ** Check process exit status for process @p pid. + ** If child @p pid has exited, return its exit status, + ** (which may be zero). + ** On error (no child, no exit), return -1. + ** If child @p has not exited, return -2. + */ + static int checkPidExited(pid_t pid); + + +protected: + QList environment() const; + + bool m_bErase, /**< @see setErase() */ + m_bTerminal; /**< Indicates running in a terminal, causes additional + newlines to be printed after output. Set to @c false + in constructor. @see setTerminal() */ + int m_Pid; /**< PID of child process */ + QByteArray m_Command, /**< Unused */ + m_Exit; /**< String to scan for in output that indicates + child has exited. */ + +private: + int init(); + int setupTTY(); + +private: + class PtyProcessPrivate; + PtyProcessPrivate* const d; +}; + +#endif diff --git a/kdesudo/CMakeLists.txt b/kdesudo/CMakeLists.txt new file mode 100644 index 00000000..204a9f66 --- /dev/null +++ b/kdesudo/CMakeLists.txt @@ -0,0 +1,34 @@ +include(CheckSymbolExists) +include(CheckIncludeFiles) + +# Linux +check_symbol_exists(PR_SET_DUMPABLE "sys/prctl.h" HAVE_PR_SET_DUMPABLE) +add_feature_info("pr_set_dumpable" HAVE_PR_SET_DUMPABLE "Used to disallow process tracing") + +# FreeBSD +check_symbol_exists(procctl "sys/procctl.h" HAVE_PROCCTL) +add_feature_info("procctl" HAVE_PROCCTL "Used to disallow process tracing") + +configure_file(config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config.h) + +include_directories( + ${KDE4_KDECORE_INCLUDES} + ${KDE4_KDEUI_INCLUDES} +) + +set(KDESUDO_SRC + main.cpp + kdesudo.cpp + kcookie.cpp +) + +add_executable(kdesudo ${KDESUDO_SRC}) + +target_link_libraries(kdesudo + ${KDE4_KDEUI_LIBS} +) + +install( + TARGETS kdesudo + ${INSTALL_TARGETS_DEFAULT_ARGS} +) diff --git a/kdesudo/Messages.sh b/kdesudo/Messages.sh new file mode 100755 index 00000000..c0ed512d --- /dev/null +++ b/kdesudo/Messages.sh @@ -0,0 +1,2 @@ +#! /usr/bin/env bash +$XGETTEXT `find . -name \*.cpp` -o $podir/kdesudo.pot diff --git a/kdesudo/config.h.cmake b/kdesudo/config.h.cmake new file mode 100644 index 00000000..cdba104a --- /dev/null +++ b/kdesudo/config.h.cmake @@ -0,0 +1,2 @@ +#cmakedefine HAVE_PR_SET_DUMPABLE +#cmakedefine HAVE_PROCCTL diff --git a/kdesudo/kcookie.cpp b/kdesudo/kcookie.cpp new file mode 100644 index 00000000..c69d9c7a --- /dev/null +++ b/kdesudo/kcookie.cpp @@ -0,0 +1,91 @@ +/* vi: ts=8 sts=4 sw=4 + * + * This file is part of the KDE project, module kdesu. + * Copyright (C) 1999,2000 Geert Jansen + * + * This is free software; you can use this library under the GNU Library + * General Public License, version 2. See the file "COPYING.LIB" for the + * exact licensing terms. + * + * kcookie.cpp: KDE authentication cookies. + */ + +#include "kcookie.h" + +#include + +#include +#include +#include + +#include + +namespace KDESu +{ + namespace KDESuPrivate + { + + class KCookie::KCookiePrivate + { + public: + QByteArray m_Display; +#ifdef Q_WS_X11 + QByteArray m_DisplayAuth; +#endif + }; + + + KCookie::KCookie() + : d(new KCookiePrivate) + { +#ifdef Q_WS_X11 + d->m_Display = qgetenv("DISPLAY"); + if (d->m_Display.isEmpty()) { + kError(900) << "$DISPLAY is not set.\n"; + return; + } + QByteArray disp = d->m_Display; + if (disp.startsWith("localhost:")) { + disp.remove(0, 9); + } + + QProcess proc; + proc.start("xauth", QStringList() << "list" << disp); + if (!proc.waitForStarted()) { + kError(900) << "Could not run xauth.\n"; + return; + } + proc.waitForReadyRead(100); + QByteArray output = proc.readLine().simplified(); + if (output.isEmpty()) { + kWarning(900) << "No X authentication info set for display " << + d->m_Display << endl; return; + } + QList lst = output.split(' '); + if (lst.count() != 3) { + kError(900) << "parse error.\n"; + return; + } + d->m_DisplayAuth = (lst[1] + ' ' + lst[2]); + proc.waitForFinished(100); // give QProcess a chance to clean up gracefully +#endif + } + + KCookie::~KCookie() + { + delete d; + } + + QByteArray KCookie::display() const + { + return d->m_Display; + } + +#ifdef Q_WS_X11 + QByteArray KCookie::displayAuth() const + { + return d->m_DisplayAuth; + } +#endif + } +} diff --git a/kdesudo/kcookie.h b/kdesudo/kcookie.h new file mode 100644 index 00000000..4174f683 --- /dev/null +++ b/kdesudo/kcookie.h @@ -0,0 +1,55 @@ +/* vi: ts=8 sts=4 sw=4 + * + * This file is part of the KDE project, module kdesu + * Copyright (C) 1999,2000 Geert Jansen + * + * This is free software; you can use this library under the GNU Library + * General Public License, version 2. See the file "COPYING.LIB" for the + * exact licensing terms. + */ + +#ifndef __KCookie_h_Included__ +#define __KCookie_h_Included__ + +#include + + +namespace KDESu +{ + + namespace KDESuPrivate + { + + /** + * Utility class to access the authentication tokens needed to run a KDE + * program (X11 cookies on X11, for instance). + * @internal + */ + + class KCookie + { + public: + KCookie(); + ~KCookie(); + + /** + * Returns the X11 display. + */ + QByteArray display() const; + +#ifdef Q_WS_X11 + /** + * Returns the X11 magic cookie, if available. + */ + QByteArray displayAuth() const; +#endif + + private: + class KCookiePrivate; + KCookiePrivate *const d; + }; + + } +} + +#endif // __KCookie_h_Included__ diff --git a/kdesudo/kdesudo.cpp b/kdesudo/kdesudo.cpp new file mode 100644 index 00000000..b7835a63 --- /dev/null +++ b/kdesudo/kdesudo.cpp @@ -0,0 +1,384 @@ +/*************************************************************************** + kdesudo.cpp - the implementation of the + admin granting sudo widget + ------------------- + begin : Sam Feb 15 15:42:12 CET 2003 + copyright : (C) 2003 by Robert Gruber + + (C) 2007 by Martin Böhm + Anthony Mercatante + Canonical Ltd (Jonathan Riddell + ) + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "kdesudo.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +KdeSudo::KdeSudo(const QString &icon, const QString &appname) : + QObject(), + m_process(0), + m_error(false), + m_pCookie(new KDESu::KDESuPrivate::KCookie) +{ + KCmdLineArgs *args = KCmdLineArgs::parsedArgs(); + + bool realtime = args->isSet("r"); + bool priority = args->isSet("p"); + bool showCommand = (!args->isSet("d")); + bool changeUID = true; + QString runas = args->getOption("u"); + QString cmd; + int winid = -1; + bool attach = args->isSet("attach"); + + if (!args->isSet("c") && !args->count()) { + KMessageBox::information(0, i18n("No command arguments supplied!\n" + "Usage: kdesudo [-u ] \n" + "KdeSudo will now exit...") + ); + exit(0); + } + + m_dialog = new KPasswordDialog; + m_dialog->setDefaultButton(KDialog::Ok); + + if (attach) { + winid = args->getOption("attach").toInt(&attach, 0); + KWindowSystem::setMainWindow(m_dialog, (WId)winid); + } + + m_process = new QProcess; + + /* load the icon */ + m_dialog->setPixmap(icon); + + // Parsins args + + /* Get the comment out of cli args */ + QByteArray commentBytes = args->getOption("comment").toUtf8(); + QTextCodec *tCodecConv = QTextCodec::codecForLocale(); + QString comment = tCodecConv->toUnicode(commentBytes, commentBytes.size()); + + if (args->isSet("f")) { + // If file is writeable, do not change uid + QString file = args->getOption("f"); + if (!file.isEmpty()) { + if (file.at(0) != '/') { + KStandardDirs dirs; + file = dirs.findResource("config", file); + if (file.isEmpty()) { + kWarning(1206) << "Config file not found: " << file << "\n"; + exit(1); + } + } + QFileInfo fi(file); + if (!fi.exists()) { + kWarning(1206) << "File does not exist: " << file << "\n"; + exit(1); + } + if (fi.isWritable()) { + changeUID = false; + } + } + } + + connect(m_process, SIGNAL(readyReadStandardOutput()), + this, SLOT(parseOutput())); + + connect(m_process, SIGNAL(readyReadStandardError()), + this, SLOT(parseOutput())); + + connect(m_process, SIGNAL(finished(int)), + this, SLOT(procExited(int))); + + connect(m_dialog, SIGNAL(gotPassword(const QString & , bool)), + this, SLOT(pushPassword(const QString &))); + + connect(m_dialog, SIGNAL(rejected()), + this, SLOT(slotCancel())); + + // Generate the xauth cookie and put it in a tempfile + // set the environment variables to reflect that. + // Default cookie-timeout is 60 sec. . + // 'man xauth' for more info on xauth cookies. + + QTemporaryFile *tmpFile = new QTemporaryFile("/tmp/kdesudo-XXXXXX-xauth"); + tmpFile->open(); + QString m_tmpName = tmpFile->fileName(); + delete tmpFile; + + QByteArray disp = m_pCookie->display(); + + // Create two processes, one for each xauth call + QProcess xauth_ext; + QProcess xauth_merge; + + // This makes "xauth extract - $DISPLAY | xauth -f /tmp/kdesudo-... merge -" + xauth_ext.setStandardOutputProcess(&xauth_merge); + + // Start the first + xauth_ext.start("xauth", QStringList() << "extract" << "-" << QString::fromLocal8Bit(disp), QIODevice::ReadOnly); + if (!xauth_ext.waitForStarted()) { + return; + } + + // Start the second + xauth_merge.start("xauth", QStringList() << "-f" << m_tmpName << "merge" << "-", QIODevice::WriteOnly); + if (!xauth_merge.waitForStarted()) { + return; + } + + // If they ended, close it all + if (!xauth_merge.waitForFinished()) { + return; + } + xauth_merge.close(); + + if (!xauth_ext.waitForFinished()) { + return; + } + xauth_ext.close(); + + // non root users need to be able to read the xauth file. + // the xauth file is deleted when kdesudo exits. security? + QFile tf; + tf.setFileName(m_tmpName); + + if (!runas.isEmpty() && runas != "root" && tf.exists()) { + chmod(QFile::encodeName(m_tmpName), 0644); + } + + QProcessEnvironment processEnv = QProcessEnvironment::systemEnvironment(); + processEnv.insert("DISPLAY", disp); + processEnv.insert("XAUTHORITY", m_tmpName); + m_process->setProcessEnvironment(processEnv); + + QStringList processArgs; + { + // Do not cache credentials to avoid security risks caused by the fact + // that kdesudo could be invoked from anyting inside the user session + // potentially in such a way that it uses the cached credentials of a + // previously kdesudo run in that same scope. + processArgs << "-k"; + if (changeUID) { + processArgs << "-H" << "-S" << "-p" << "passprompt"; + + if (!runas.isEmpty()) { + processArgs << "-u" << runas; + } + processArgs << "--"; + } + + if (realtime) { + processArgs << "nice" << "-n" << "10"; + m_dialog->addCommentLine(i18n("Priority:"), i18n("realtime:") + + QChar(' ') + QString("50/100")); + processArgs << "--"; + } else if (priority) { + QString n = args->getOption("p"); + int intn = atoi(n.toUtf8()); + intn = (intn * 40 / 100) - (20 + 0.5); + + QString strn; + strn.sprintf("%d", intn); + + processArgs << "nice" << "-n" << strn; + m_dialog->addCommentLine(i18n("Priority:"), n + QString("/100")); + processArgs << "--"; + } + + if (args->isSet("dbus")) { + processArgs << "dbus-launch"; + } + + if (args->isSet("c")) { + QString command = args->getOption("c"); + cmd += command; + processArgs << "sh"; + processArgs << "-c"; + processArgs << command; + } + + else if (args->count()) { + for (int i = 0; i < args->count(); i++) { + if ((!args->isSet("c")) && (i == 0)) { + QStringList argsSplit = KShell::splitArgs(args->arg(i)); + for (int j = 0; j < argsSplit.count(); j++) { + processArgs << validArg(argsSplit[j]); + if (j == 0) { + cmd += validArg(argsSplit[j]) + QChar(' '); + } else { + cmd += KShell::quoteArg(validArg(argsSplit[j])) + QChar(' '); + } + } + } else { + processArgs << validArg(args->arg(i)); + cmd += validArg(args->arg(i)) + QChar(' '); + } + } + } + // strcmd needs to be defined + if (showCommand && !cmd.isEmpty()) { + m_dialog->addCommentLine(i18n("Command:"), cmd); + } + } + + if (comment.isEmpty()) { + QString defaultComment = "%1 " + i18n("needs administrative privileges. "); + + if (runas.isEmpty() || runas == "root") { + defaultComment += i18n("Please enter your password."); + } else { + defaultComment += i18n("Please enter password for %1.", runas); + } + + if (!appname.isEmpty()) { + m_dialog->setPrompt(defaultComment.arg(appname)); + } else { + m_dialog->setPrompt(defaultComment.arg(cmd)); + } + } else { + m_dialog->setPrompt(comment); + } + + m_process->setProcessChannelMode(QProcess::MergedChannels); + + m_process->start("sudo", processArgs); +} + +KdeSudo::~KdeSudo() +{ + delete m_dialog; +} + +void KdeSudo::error(const QString &msg) +{ + m_error = true; + KMessageBox::error(0, msg); + KApplication::kApplication()->exit(1); +} + +void KdeSudo::parseOutput() +{ + QString strOut = m_process->readAllStandardOutput(); + + static int badpass = 0; + + if (strOut.contains("try again")) { + badpass++; + if (badpass == 1) { + m_dialog->addCommentLine(i18n("Warning: "), i18n("Incorrect password, please try again.")); + m_dialog->show(); + } else if (badpass == 2) { + m_dialog->show(); + } else { + error(i18n("Wrong password! Exiting...")); + } + + } else if (strOut.contains("command not found")) { + error(i18n("Command not found!")); + } else if (strOut.contains("is not in the sudoers file")) { + error(i18n("Your username is unknown to sudo!")); + } else if (strOut.contains("is not allowed to execute")) { + error(i18n("Your user is not allowed to run the specified command!")); + } else if (strOut.contains("is not allowed to run sudo on")) { + error(i18n("Your user is not allowed to run sudo on this host!")); + } else if (strOut.contains("may not run sudo on")) { + error(i18n("Your user is not allowed to run sudo on this host!")); + } else if ((strOut.contains("passprompt")) || (strOut.contains("PIN (CHV2)"))) { + m_dialog->setPassword(QString()); + m_dialog->show(); + } else { + fprintf(stdout, "%s", strOut.toLocal8Bit().constData()); + } +} + +void KdeSudo::procExited(int exitCode) +{ + if (!m_error) { + if (!m_tmpName.isEmpty()) { + QFile::remove(m_tmpName); + } + } + KApplication::kApplication()->exit(exitCode); +} + +void KdeSudo::pushPassword(const QString &pwd) +{ + m_process->write(pwd.toLocal8Bit() + "\n"); +} + +void KdeSudo::slotCancel() +{ + KApplication::kApplication()->exit(1); +} + +void KdeSudo::slotUser1() +{ + m_dialog->done(AsUser); +} + +void KdeSudo::blockSigChild() +{ + sigset_t sset; + sigemptyset(&sset); + sigaddset(&sset, SIGCHLD); + sigprocmask(SIG_BLOCK, &sset, 0L); +} + +void KdeSudo::unblockSigChild() +{ + sigset_t sset; + sigemptyset(&sset); + sigaddset(&sset, SIGCHLD); + sigprocmask(SIG_UNBLOCK, &sset, 0L); +} + + +QString KdeSudo::validArg(QString arg) +{ + QChar firstChar = arg.at(0); + QChar lastChar = arg.at(arg.length() - 1); + + if ((firstChar == '"' && lastChar == '"') || (firstChar == '\'' && lastChar == '\'')) { + arg = arg.remove(0, 1); + arg = arg.remove(arg.length() - 1, 1); + } + return arg; +} diff --git a/kdesudo/kdesudo.h b/kdesudo/kdesudo.h new file mode 100644 index 00000000..3b7f2d19 --- /dev/null +++ b/kdesudo/kdesudo.h @@ -0,0 +1,93 @@ +/*************************************************************************** + kdesudo.cpp - description + ------------------- + begin : Sam Feb 15 15:42:12 CET 2003 + copyright : (C) 2003 by Robert Gruber + (C) 2007 by Martin Böhm + Anthony Mercatante + Canonical Ltd (Jonathan Riddell ) + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef KDESUDO_H +#define KDESUDO_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include +#include + +#include "kcookie.h" +/* +* KdeSudo is the base class of the project +* +* @version 3.1 +*/ + +/* buffer is used when reading from the QProcess child */ +#define BUFSIZE 1024 + +class KdeSudo : QObject +{ + Q_OBJECT +public: + KdeSudo(const QString &icon = QString(), const QString &generic = QString()); + ~KdeSudo(); + + enum ResultCodes { + AsUser = 10 + }; + +private slots: + /** + * This slot gets executed if sudo creates some output + * -- well, in theory it should. Even though the code + * seems to be doing what the API says, it doesn't + * yet do what we need. + **/ + void parseOutput(); + + /** + * This slot gets exectuted when sudo exits + **/ + void procExited(int exitCode); + + /** + * This slot overrides the slot from KPasswordDialog + * @see KPasswordDialog + **/ + void pushPassword(const QString &); + void slotCancel(); + void slotUser1(); + QString validArg(QString arg); + +private: + void error(const QString &); + QProcess *m_process; + bool m_error; + bool useTerm; + bool noExec; + QString m_tmpName; + QString iceauthorityFile; + KDESu::KDESuPrivate::KCookie *m_pCookie; + void blockSigChild(); + void unblockSigChild(); + + KPasswordDialog *m_dialog; +}; + +#endif // KDESUDO_H diff --git a/kdesudo/main.cpp b/kdesudo/main.cpp new file mode 100644 index 00000000..fb727c0a --- /dev/null +++ b/kdesudo/main.cpp @@ -0,0 +1,164 @@ +/*************************************************************************** + kdesudo.cpp - description + ------------------- + begin : Sam Feb 15 15:42:12 CET 2003 + copyright : (C) 2003 by Robert Gruber + + (C) 2007 by Martin Böhm + Anthony Mercatante + Canonical Ltd (Jonathan Riddell + ) + (C) 2009-2015 by Harald Sitter + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#if defined(HAVE_PR_SET_DUMPABLE) +# include +#elif defined(HAVE_PROCCTL) +# include +# include +#endif + +#include "kdesudo.h" + +int main(int argc, char **argv) +{ + // Disable tracing to prevent arbitrary apps reading password out of memory. +#if defined(HAVE_PR_SET_DUMPABLE) + prctl(PR_SET_DUMPABLE, 0); +#elif defined(HAVE_PROCCTL) + int ctldata = PROC_TRACE_CTL_DISABLE; + procctl(P_PID, ::getpid(), PROC_TRACE_CTL, &ctldata); +#endif + + KAboutData about( + "kdesudo", 0, ki18n("KdeSudo"), + "3.4.2.3", ki18n("Sudo frontend for KDE"), + KAboutData::License_GPL, + ki18n("(C) 2007 - 2008 Anthony Mercatante"), + KLocalizedString(), + "https://code.launchpad.net/kdesudo/"); + + about.setBugAddress("https://launchpad.net/kdesudo/+filebug"); + + about.addAuthor(ki18n("Robert Gruber"), KLocalizedString(), + "rgruber@users.sourceforge.net", "http://test.com"); + about.addAuthor(ki18n("Anthony Mercatante"), KLocalizedString(), + "tonio@ubuntu.com"); + about.addAuthor(ki18n("Martin Böhm"), KLocalizedString(), + "martin.bohm@kubuntu.org"); + about.addAuthor(ki18n("Jonathan Riddell"), KLocalizedString(), + "jriddell@ubuntu.com"); + about.addAuthor(ki18n("Harald Sitter"), KLocalizedString(), + "apachelogger@ubuntu.com"); + + KCmdLineArgs::init(argc, argv, &about); + + KCmdLineOptions options; + options.add("u ", ki18n("sets a runas user")); + options.add("c ", ki18n("The command to execute")); + options.add("i ", ki18n("Specify icon to use in the password" + " dialog")); + options.add("d", ki18n("Do not show the command to be run in the dialog")); + options.add("p ", ki18n("Process priority, between 0 and 100," + " 0 the lowest [50]")); + options.add("r", ki18n("Use realtime scheduling")); + options.add("f ", ki18n("Use target UID if is not writeable")); + options.add("nodbus", ki18n("Do not start a message bus")); + options.add("comment

    ", ki18n("The comment that should be " + "displayed in the dialog")); + options.add("attach ", ki18n("Makes the dialog transient for an X app specified by winid")); + options.add("desktop ", ki18n("Manual override for " + "automatic desktop file detection")); + options.add("noignorebutton", ki18n("Fake option for KDE's KdeSu compatibility")); + options.add("t", ki18n("Fake option for KDE's KdeSu compatibility")); + // nothing is making use of it AFAICT + // options.add("nonewdcop", ki18n("Fake option for compatibility")); + options.add("s", ki18n("Fake option for compatibility")); + options.add("n", ki18n("Fake option for compatibility")); + + options.add("+command", ki18n("The command to execute")); + + KCmdLineArgs::addCmdLineOptions(options); + KCmdLineArgs *args = KCmdLineArgs::parsedArgs(); + + KApplication a; + + QString executable, arg, command, icon; + QStringList executableList, commandlist; + KDesktopFile *desktopFile; + + if (args->isSet("c")) { + executable = args->getOption("c"); + } + + if (args->count() && executable.isEmpty()) { + command = args->arg(0); + commandlist = command.split(" "); + executable = commandlist[0]; + } + + /* We have to make sure the executable is only the binary name */ + executableList = executable.split(" "); + executable = executableList[0]; + + executableList = executable.split("/"); + executable = executableList[executableList.count() - 1]; + + /* Kubuntu has a bug in it - this is a workaround for it */ + KGlobal::dirs()->addResourceDir("apps", "/usr/share/applications/kde4"); + KGlobal::dirs()->addResourceDir("apps", "/usr/share/kde4/services"); + KGlobal::dirs()->addResourceDir("apps", "/usr/share/applications"); + + QString path = getenv("PATH"); + QStringList pathList = path.split(":"); + for (int i = 0; i < pathList.count(); i++) { + executable.remove(pathList[i]); + } + + if (args->isSet("desktop")) { + desktopFile = new KDesktopFile(args->getOption("desktop")); + } else { + desktopFile = new KDesktopFile(executable + ".desktop"); + } + + /* icon parsing */ + if (args->isSet("i")) { + icon = args->getOption("i"); + } else { + QString iconName = desktopFile->readIcon(); + KIconLoader *loader = KIconLoader::global(); + icon = loader->iconPath(iconName, -1 * KIconLoader::StdSizes( + KIconLoader::SizeHuge), true); + } + + /* generic name parsing */ + QString name = desktopFile->readName(); + + a.setQuitOnLastWindowClosed(false); + KdeSudo kdesudo(icon, name); + return a.exec(); +} diff --git a/kdialog/CMakeLists.txt b/kdialog/CMakeLists.txt new file mode 100644 index 00000000..275fb1d5 --- /dev/null +++ b/kdialog/CMakeLists.txt @@ -0,0 +1,35 @@ +set(kdialog_SRCS + kdialog.cpp + widgets.cpp + klistboxdialog.cpp + progressdialog.cpp) + +qt4_add_dbus_adaptor(kdialog_SRCS + org.kde.kdialog.ProgressDialog.xml + progressdialog.h + KProgressDialog +) + +add_executable(kdialog ${kdialog_SRCS}) + +# Need libkfile due to the code that adjusts the geometry of the KDirSelectDialog +target_link_libraries(kdialog + ${KDE4_KFILE_LIBS} + ${KDE4_KIO_LIBS} +) + +if(Q_WS_X11) + target_link_libraries(kdialog ${X11_X11_LIB}) + if (QT_QTDBUS_FOUND) + target_link_libraries(kdialog ${QT_QTDBUS_LIBRARY}) + endif () +endif() + +install( + TARGETS kdialog + ${INSTALL_TARGETS_DEFAULT_ARGS} +) +install( + FILES org.kde.kdialog.ProgressDialog.xml + DESTINATION ${KDE4_DBUS_INTERFACES_INSTALL_DIR} +) diff --git a/kdialog/Messages.sh b/kdialog/Messages.sh new file mode 100644 index 00000000..dc7a6f06 --- /dev/null +++ b/kdialog/Messages.sh @@ -0,0 +1,2 @@ +#! /usr/bin/env bash +$XGETTEXT *.cpp -o $podir/kdialog.pot diff --git a/kdialog/README b/kdialog/README new file mode 100644 index 00000000..e42f7b1c --- /dev/null +++ b/kdialog/README @@ -0,0 +1,14 @@ +kdialog allows you to display dialog boxes from shell scripts. +The syntax is very much inspired from the "dialog" command +(which shows text mode dialogs). + +However the width and height attributes have been removed for +most dialogs - Qt/KDE have layouts ;) + +A tutorial on using kdialog is available at +http://techbase.kde.org/Development/Tutorials/Shell_Scripting_with_KDE_Dialogs +If you change or add any functionality, please contact Brad +Hards to ensure it is reflected in the +tutorial. + +Current maintainer: David Faure diff --git a/kdialog/dialog-compat.txt b/kdialog/dialog-compat.txt new file mode 100644 index 00000000..6825593d --- /dev/null +++ b/kdialog/dialog-compat.txt @@ -0,0 +1,613 @@ +u It would be very useful in some circumstances if kdialog +could be used as a direct replacement for dialog and/or xdialog. + +In general, the differences between the kdialog we shipped with +KDE3 and the dialog / xdialog options are pretty minor, except +for two things: +1. KDE3 kdialog doesn't accept (and doesn't need) the size options +(height and width). We don't need them because of the use of layouts +in KDE. +2. There are some dialog types that aren't supported by KDE3 kdialog. + +There is more than one kind of dialog, and they take different options. +See below for the cdialog options: + - - - - +cdialog (ComeOn Dialog!) version 1.0-20051107 +Copyright (C) 2005 Thomas E. Dickey +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +* Display dialog boxes from shell scripts * + +Usage: dialog { --and-widget } +where options are "common" options, followed by "box" options + +Special options: + [--create-rc "file"] +Common options: + [--aspect ] [--backtitle ] [--begin ] + [--cancel-label ] [--clear] [--colors] [--cr-wrap] + [--default-item ] [--defaultno] [--exit-label ] + [--extra-button] [--extra-label ] [--help-button] + [--help-label ] [--help-status] [--ignore] [--input-fd ] + [--insecure] [--item-help] [--keep-window] [--max-input ] + [--no-cancel] [--no-collapse] [--no-kill] [--no-label ] + [--no-shadow] [--ok-label ] [--output-fd ] [--print-maxsize] + [--print-size] [--print-version] [--separate-output] + [--separate-widget ] [--shadow] [--single-quoted] [--size-err] + [--sleep ] [--stderr] [--stdout] [--tab-correct] [--tab-len ] + [--timeout ] [--title ] [--trim] [--visit-items] + [--version] [--yes-label <str>] +Box options: + --calendar <text> <height> <width> <day> <month> <year> + --checklist <text> <height> <width> <list height> <tag1> <item1> <status1>... + --form <text> <height> <width> <form height> <label1> <l_y1> <l_x1> <item1> <i_y1> <i_x1> <flen1> <ilen1>... + --fselect <filepath> <height> <width> + --gauge <text> <height> <width> [<percent>] + --infobox <text> <height> <width> + --inputbox <text> <height> <width> [<init>] + --inputmenu <text> <height> <width> <menu height> <tag1> <item1>... + --menu <text> <height> <width> <menu height> <tag1> <item1>... + --msgbox <text> <height> <width> + --passwordbox <text> <height> <width> [<init>] + --pause <text> <height> <width> <seconds> + --radiolist <text> <height> <width> <list height> <tag1> <item1> <status1>... + --tailbox <file> <height> <width> + --tailboxbg <file> <height> <width> + --textbox <file> <height> <width> + --timebox <text> <height> <width> <hour> <minute> <second> + --yesno <text> <height> <width> + +Auto-size with height and width = 0. Maximize with height and width = -1. +Global-auto-size if also menu_height/list_height = 0. + + - - - - + + +The Dragonfly BSD version (and presumably other BSD flavours) +takes a different set of options. + +NAME + + dialog - display dialog boxes from shell scripts + + +SYNOPSIS + + dialog --clear + dialog --create-rc file + dialog [ --title title ] [ --clear ] [ --hline line ] [ --hfile file ] + box-options + + +DESCRIPTION + + Dialog is a program which allows you to present a variety of questions + or display messages in dialog box form from a shell script. The fol- + lowing types of dialog objects are currently supported: + + yes/no box, menu box, input box, message box, text box, info box, + checklist box, program box, ftree and tree boxes. + + +OPTIONS + + --clear + The screen will be cleared to the screen attribute on exit. + + --create-rc file + Since dialog supports run-time configuration, this can be used + to dump a sample configuration file to the file specified by + file. + + --title title + Specifies a title string to be displayed at the top of the dia- + log box. + + --hline line + Specifies a line string to be displayed at the bottom of the + dialog box. + + --hfile file + Specifies a file to be displayed by pressing ? or F1. + + Box Options + + --yesno text height width + A yes/no dialog box of size height rows by width columns will be + displayed. The string specified by text is displayed inside the + dialog box. If this string is too long to fit in one line, it + will be automatically divided into multiple lines at the appro- + priate points. The text string may also contain the sub-string + "\n" or newline characters `\n' to control line breaking explic- + itly. This dialog box is useful for asking questions that + require the user to answer either yes or no. The dialog box has + a Yes button and a No button, in which the user can switch + between by pressing the TAB key. + + --msgbox text height width + A message box is very similar to a yes/no box. The only differ- + ence between a message box and a yes/no box is that a message + box has only a single OK button. You can use this dialog box to + display any message you like. After reading the message, the + user can press the ENTER key so that dialog will exit and the + calling shell script can continue its operation. + + --infobox text height width + An info box is basically a message box. However, in this case, + dialog will exit immediately after displaying the message to the + user. The screen is not cleared when dialog exits, so that the + message will remain on the screen until the calling shell script + clears it later. This is useful when you want to inform the user + that some operations are carrying on that may require some time + to finish. + + --inputbox text height width + An input box is useful when you want to ask questions that + require the user to input a string as the answer. When inputing + the string, the BACKSPACE key can be used to correct typing + errors. If the input string is longer than can be fitted in the + dialog box, the input field will be scrolled. On exit, the input + string will be printed on stderr. + + --textbox file height width + A text box lets you display the contents of a text file in a + dialog box. It is like a simple text file viewer. The user can + move through the file by using the UP/DOWN, PGUP/PGDN and + HOME/END keys available on most keyboards. If the lines are too + long to be displayed in the box, the LEFT/RIGHT keys can be used + to scroll the text region horizontally. For more convenience, + forward and backward searching functions are also provided. + + --menu text height width menu-height [ tag item ] ... + As its name suggests, a menu box is a dialog box that can be + used to present a list of choices in the form of a menu for the + user to choose. Each menu entry consists of a tag string and an + item string. The tag gives the entry a name to distinguish it + from the other entries in the menu. The item is a short descrip- + tion of the option that the entry represents. The user can move + between the menu entries by pressing the UP/DOWN keys, the first + letter of the tag as a hot-key, or the number keys 1-9. There + are menu-height entries displayed in the menu at one time, but + the menu will be scrolled if there are more entries than that. + When dialog exits, the tag of the chosen menu entry will be + printed on stderr. + + --prgbox command height width + A program box lets you display output of command in dialog box. + + --checklist text height width list-height [ tag item status ] ... + A checklist box is similar to a menu box in that there are mul- + tiple entries presented in the form of a menu. Instead of choos- + ing one entry among the entries, each entry can be turned on or + off by the user. The initial on/off state of each entry is spec- + ified by status. On exit, a list of the tag strings of those + entries that are turned on will be printed on stderr. + + --ftree file FS text height width menu-height + ftree box is a dialog box showing the tree described by the data + from the file file. The data in the file should look like + find(1) output. For the find output, the field separator FS + will be '/'. If height and width are positive numbers, they set + the absolute size of the whole ftree box. If height and width + are negative numbers, the size of the ftree box will be selected + automatically. menu-height sets the height of the tree subwin- + dow inside the ftree box and must be set. text is shown inside + the ftree box above the tree subwindow and can contain newline + characters '\n' to split lines. One can navigate in the tree by + pressing UP/DOWN or '+'/'-', PG_UP/PG_DOWN or 'b'/SPACE and + HOME/END or 'g'/'G'. A leaf of the tree is selected by pressing + TAB or LEFT/RIGHT the OK button and pressing ENTER. The + selected leaf (to be more exact, the full path to it from the + root of the tree) is printed to stderr. If Cancel and then + ENTER is pressed, nothing is printed to stderr. file may con- + tain data like find(1) output, as well as like the output of + find(1) with -d option. Some of the transient paths to the + leaves of the tree may be absent. Such data is corrected when + fed from file. + + --tree FS text height width menu-height [ item ] ... + tree box is like ftree box with some exceptions. First, the data + is not entered from a file, but from the command line as item + item ... Second, the data thus entered is not corrected in any + way. Thus, the data like the output of find(1) with -d option + will look incorrectly. + +RUN-TIME CONFIGURATION + 1. Create a sample configuration file by typing: + + "dialog --create-rc <file>" + + 2. At start, dialog determines the settings to use as follows: + + a) if environment variable DIALOGRC is set, its value determines + the name of the configuration file. + + b) if the file in (a) can't be found, use the file $HOME/.dialogrc + as the configuration file. + + c) if the file in (b) can't be found, use compiled in defaults. + + 3. Edit the sample configuration file and copy it to some place that + dialog can find, as stated in step 2 above. + + +ENVIRONMENT + + DIALOGRC Define this variable if you want to specify the name of + the configuration file to use. + + +FILES + + $HOME/.dialogrc default configuration file + + +DIAGNOSTICS + + Exit status is 0 if dialog is exited by pressing the Yes or OK button, + and 1 if the No or Cancel button is pressed. Otherwise, if errors occur + inside dialog or dialog is exited by pressing the ESC key, the exit + status is -1. + + + - - - - +Xdialog has an inordinately large collection of options. As of 2.3.1, the +help text offers the following: +[<common options>] [<transient options>] <box option> ... + +Common options: + --wmclass <name> + --rc-file <gtkrc filename> + --backtitle <backtitle> + --title <title> + --allow-close | --no-close + --screen-center | --under-mouse | --auto-placement + --center | --right | --left | --fill + --no-wrap | --wrap + --cr-wrap | --no-cr-wrap + --stderr | --stdout + --separator <character> | --separate-output + --buttons-style default|icon|text + +Transient options: + --fixed-font + --password (may be repeated 2 or 3 times before --2inputsbox or --3inputsbox) + --password=1|2 (for --2inputsbox or --3inputsbox) + --editable + --time-stamp | --date-stamp + --reverse + --keep-colors + --interval <timeout> + --timeout <timeout> (in seconds) + --no-tags + --item-help (if used, the {...} parameters are needed in menus/lists widgets) + --default-item <tag> + --icon <xpm filename> + --no-ok + --no-cancel + --no-buttons + --default-no + --wizard + --help <help> + --print <printer> (1) + --check <label> [<status>] + --ok-label <label> + --cancel-label <label> + --beep + --beep-after + --begin <Yorg> <Xorg> + --ignore-eof + --smooth + +Box options: + --yesno <text> <height> <width> + --msgbox <text> <height> <width> + --infobox <text> <height> <width> [<timeout>] + --gauge <text> <height> <width> [<percent>] + --progress <text> <height> <width> [<maxdots> [[-]<msglen>]] + --inputbox <text> <height> <width> [<init>] + --2inputsbox <text> <height> <width> <label1> <init1> <label2> <init2> + --3inputsbox <text> <height> <width> <label1> <init1> <label2> <init2> <label3> <init3> + --combobox <text> <height> <width> <item1> ... <itemN> + --rangebox <text> <height> <width> <min value> <max value> [<default value>] + --2rangesbox <text> <height> <width> <label1> <min1> <max1> <def1> <label2> <min2> <max2> <def2> + --3rangesbox <text> <height> <width> <label1> <min1> <max1> <def1> ... <label3> <min3> <max3> <def3> + --spinbox <text> <height> <width> <min value> <max value> <default value> <label> + --2spinsbox <text> <height> <width> <min1> <max1> <def1> <label1> <min2> <max2> <def2> <label2> + --3spinsbox <text> <height> <width> <min1> <max1> <def1> <label1> ... <min3> <max3> <def3> <label3> + --textbox <file> <height> <width> + --editbox <file> <height> <width> + --tailbox <file> <height> <width> + --logbox <file> <height> <width> + --menubox <text> <height> <width> <menu height> <tag1> <item1> {<help1>}... + --checklist <text> <height> <width> <list height> <tag1> <item1> <status1> {<help1>}... + --radiolist <text> <height> <width> <list height> <tag1> <item1> <status1> {<help1>}... + --buildlist <text> <height> <width> <list height> <tag1> <item1> <status1> {<help1>}... + --treeview <text> <height> <width> <list height> <tag1> <item1> <status1> <item_depth1> {<help1>}... + --fselect <file> <height> <width> + --dselect <directory> <height> <width> + --colorsel <text> <height> <width> [<red> <green> <blue>] + --fontsel <font name> <height> <width> + --calendar <text> <height> <width> [<day> <month> <year>] + --timebox <text> <height> <width> [<hours> <minutes> <seconds>] + +Special options: + --version (prints version number to stderr and exits). + --print-version (same as above in a cdialog-compatible way). + --print-maxsize (prints maximum menu size in characters and exits). + +Note that <height> and <width> are in characters and may be replaced by a single +XSIZExYSIZE[+/-XORG+/-YORG] parameter (like the one passed in the -geometry option +of X) which will represent the size of the Xdialog window in pixels. Specifying +a size of 0 0 (or 0x0) will auto-size Xdialog, while a size of -1 -1 (or -1x-1) +will maximize it. + + - - - - +The KDE3 version of kdialog has yet another set of options: + +Usage: kdialog [Qt-options] [KDE-options] [options] [arg] + +KDialog can be used to show nice dialog boxes from shell scripts + +Generic options: + --help Show help about options + --help-qt Show Qt specific options + --help-kde Show KDE specific options + --help-all Show all options + --author Show author information + -v, --version Show version information + --license Show license information + -- End of options + +Options: + --yesno <text> Question message box with yes/no buttons + --yesnocancel <text> Question message box with yes/no/cancel buttons + --warningyesno <text> Warning message box with yes/no buttons + --warningcontinuecancel <text> Warning message box with continue/cancel buttons + --warningyesnocancel <text> Warning message box with yes/no/cancel buttons + --sorry <text> 'Sorry' message box + --error <text> 'Error' message box + --msgbox <text> Message Box dialog + --inputbox <text> <init> Input Box dialog + --password <text> Password dialog + --textbox <file> [width] [height] Text Box dialog + --textinputbox <text> <init> [width] [height] Text Input Box dialog + --combobox <text> [tag item] [tag item] ... ComboBox dialog + --menu <text> [tag item] [tag item] ... Menu dialog + --checklist <text> [tag item status] ... Check List dialog + --radiolist <text> [tag item status] ... Radio List dialog + --passivepopup <text> <timeout> Passive Popup + --getopenfilename [startDir] [filter] File dialog to open an existing file + --getsavefilename [startDir] [filter] File dialog to save a file + --getexistingdirectory [startDir] File dialog to select an existing directory + --getopenurl [startDir] [filter] File dialog to open an existing URL + --getsaveurl [startDir] [filter] File dialog to save a URL + --geticon [group] [context] Icon chooser dialog + --progressbar <text> [totalsteps] Progress bar dialog, returns a DCOP reference for communication + --title <text> Dialog title + --default <text> Default entry to use for combobox and menu + --multiple Allows the --getopenurl and --getopenfilename options to return multiple files + --separate-output Return list items on separate lines (for checklist option and file open with --multiple) + --print-winid Outputs the winId of each dialog + --embed <winid> Makes the dialog transient for an X app specified by winid + --dontagain <file:entry> Config file and option name for saving the "dont-show/ask-again" state + +Arguments: + arg Arguments - depending on main option + + - - - - + +Some of those options are common + +So the dialog types we need for KDE4 are: + --yesno <text> + --yesno <text> <height> <width> + --yesnocancel <text> + --warningyesno <text> + --warningcontinuecancel <text> + --warningyesnocancel <text> + --sorry <text> + --error <text> + --msgbox <text> + --msgbox <text> <height> <width> + --infobox <text> <height> <width> [<timeout>] + --inputbox <text> <init> + --inputbox <text> <height> <width> [<init>] + --inputmenu <text> <height> <width> <menu height> <tag1> <item1>... + --textinputbox <text> <init> [width] [height] + --password <text> + --passwordbox <text> <height> <width> [<init>] + --textbox <file> [width] [height] + --combobox <text> [tag item] [tag item] ... + --combobox <text> <height> <width> <item1> ... <itemN> + --menu <text> [tag item] [tag item] ... + --menu <text> <height> <width> <menu height> <tag1> <item1>... + --menubox <text> <height> <width> <menu height> <tag1> <item1> {<help1>}... + --checklist <text> [tag item status] ... + --checklist <text> <height> <width> <list height> <tag1> <item1> <status1> {<help1>}... + --checklist <text> <height> <width> <list height> <tag1> <item1> <status1>... + --radiolist <text> [tag item status] ... + --radiolist <text> <height> <width> <list height> <tag1> <item1> <status1>... + --passivepopup <text> <timeout> + --getopenfilename [startDir] [filter] + --fselect <file> <height> <width> + --getsavefilename [startDir] [filter] + --getexistingdirectory [startDir] + --dselect <directory> <height> <width> + --getopenurl [startDir] [filter] + --getsaveurl [startDir] [filter] + --geticon [group] [context] + --progressbar <text> [totalsteps] + --gauge <text> <height> <width> [<percent>] + (also guage) + --progress <text> <height> <width> [<maxdots> [[-]<msglen>]] + --prgbox command height width + --2inputsbox <text> <height> <width> <label1> <init1> <label2> <init2> + --3inputsbox <text> <height> <width> <label1> <init1> <label2> <init2> <label3> <init3> + --rangebox <text> <height> <width> <min value> <max value> [<default value>] + --2rangesbox <text> <height> <width> <label1> <min1> <max1> <def1> <label2> <min2> <max2> <def2> + --3rangesbox <text> <height> <width> <label1> <min1> <max1> <def1> ... <label3> <min3> <max3> <def3> + --spinbox <text> <height> <width> <min value> <max value> <default value> <label> + --2spinsbox <text> <height> <width> <min1> <max1> <def1> <label1> <min2> <max2> <def2> <label2> + --3spinsbox <text> <height> <width> <min1> <max1> <def1> <label1> ... <min3> <max3> <def3> <label3> + --editbox <file> <height> <width> + --tailbox <file> <height> <width> + --logbox <file> <height> <width> + --radiolist <text> <height> <width> <list height> <tag1> <item1> <status1> {<help1>}... + --buildlist <text> <height> <width> <list height> <tag1> <item1> <status1> {<help1>}... + --treeview <text> <height> <width> <list height> <tag1> <item1> <status1> <item_depth1> {<help1>}... + --colorsel <text> <height> <width> [<red> <green> <blue>] + --fontsel <font name> <height> <width> + --calendar <text> <height> <width> [<day> <month> <year>] + --timebox <text> <height> <width> [<hours> <minutes> <seconds>] + --ftree file FS text height width menu-height + --tree FS text height width menu-height [ item ] ... + --calendar <text> <height> <width> <day> <month> <year> + --form <text> <height> <width> <form height> <label1> <l_y1> <l_x1> <item1> <i_y1> <i_x1> <flen1> <ilen1>... + --pause <text> <height> <width> <seconds> + --tailbox <file> <height> <width> + --tailboxbg <file> <height> <width> + +The non-specific options we need to handle include: + --title <text> Dialog title + --default <text> Default entry to use for combobox and menu + --multiple Allows the --getopenurl and --getopenfilename options to return multiple files + --separate-output Return list items on separate lines (for checklist option and file open with --multiple) + --print-winid Outputs the winId of each dialog + --embed <winid> Makes the dialog transient for an X app specified by winid + --dontagain <file:entry> Config file and option name for saving the "dont-show/ask-again" state + --caption <caption> Use 'caption' as name in the titlebar + --icon <icon> Use 'icon' as the application icon + --miniicon <icon> Use 'icon' as the icon in the titlebar + --config <filename> Use alternative configuration file + --dcopserver <server> Use the DCOP Server specified by 'server' + --nocrashhandler Disable crash handler, to get core dumps + --waitforwm Waits for a WM_NET compatible windowmanager + --style <style> sets the application GUI style + --geometry <geometry> sets the client geometry of the main widget - see man X for the argument format + --display <displayname> Use the X-server display 'displayname' + --session <sessionId> Restore the application for the given 'sessionId' + --cmap Causes the application to install a private color + map on an 8-bit display + --ncols <count> Limits the number of colors allocated in the color + cube on an 8-bit display, if the application is + using the QApplication::ManyColor color + specification + --nograb tells Qt to never grab the mouse or the keyboard + --dograb running under a debugger can cause an implicit + -nograb, use -dograb to override + --sync switches to synchronous mode for debugging + --fn, --font <fontname> defines the application font + --bg, --background <color> sets the default background color and an + application palette (light and dark shades are + calculated) + --fg, --foreground <color> sets the default foreground color + --btn, --button <color> sets the default button color + --name <name> sets the application name + --title <title> sets the application title (caption) + --visual TrueColor forces the application to use a TrueColor visual on + an 8-bit display + --inputstyle <inputstyle> sets XIM (X Input Method) input style. Possible + values are onthespot, overthespot, offthespot and + root + --im <XIM server> set XIM server + --noxim disable XIM + --reverse mirrors the whole layout of widgets + --version (prints version number to stderr and exits). + --print-version (same as above in a cdialog-compatible way). + --print-maxsize (prints maximum menu size in characters and exits). + --wmclass <name> + --rc-file <gtkrc filename> + --backtitle <backtitle> + --title <title> + --allow-close | --no-close + --screen-center | --under-mouse | --auto-placement + --center | --right | --left | --fill + --no-wrap | --wrap + --cr-wrap | --no-cr-wrap + --stderr | --stdout + --separator <character> | --separate-output + --buttons-style default|icon|text + --fixed-font + --password (may be repeated 2 or 3 times before --2inputsbox or --3inputsbox) + --password=1|2 (for --2inputsbox or --3inputsbox) + --editable + --time-stamp | --date-stamp + --reverse + --keep-colors + --interval <timeout> + --timeout <timeout> (in seconds) + --no-tags + --item-help (if used, the {...} parameters are needed in menus/lists widgets) + --default-item <tag> + --icon <xpm filename> + --no-ok + --no-cancel + --no-buttons + --default-no + --wizard + --help <help> + --print <printer> (1) + --check <label> [<status>] + --ok-label <label> + --cancel-label <label> + --beep + --beep-after + --begin <Yorg> <Xorg> + --ignore-eof + --smooth + --clear + --create-rc file + --title title + --hline line + --hfile file + --aspect <ratio> + --backtitle <backtitle> + --begin <y> <x> + --cancel-label <str> + --clear + --colors + --cr-wrap + --default-item <str> + --defaultno + --exit-label <str> + --extra-button + --extra-label <str> + --help-button + --help-label <str> + --help-status + --ignore + --input-fd <fd> + --insecure + --item-help + --keep-window + --max-input <n> + --no-cancel + --no-collapse + --no-kill + --no-label <str> + --no-shadow + --ok-label <str> + --output-fd <fd> + --print-maxsize + --print-size + --print-version + --separate-output + --separate-widget <str> + --shadow + --single-quoted + --size-err + --sleep <secs> + --stderr + --stdout + --tab-correct + --tab-len <n> + --timeout <secs> + --title <title> + --trim + --visit-items + --version + --yes-label <str> + + diff --git a/kdialog/kdialog.cpp b/kdialog/kdialog.cpp new file mode 100644 index 00000000..3fbb5d37 --- /dev/null +++ b/kdialog/kdialog.cpp @@ -0,0 +1,890 @@ +// +// Copyright (C) 1998 Matthias Hoelzer <hoelzer@kde.org> +// Copyright (C) 2002 David Faure <faure@kde.org> +// Copyright (C) 2005 Brad Hards <bradh@frogmouth.net> +// Copyright (C) 2008 by Dmitry Suzdalev <dimsuz@gmail.com> +// Copyright (C) 2011 Kai Uwe Broulik <kde@privat.broulik.de> +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the7 implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// + +#include <QtCore/qdatetime.h> +#include <kdebug.h> +#include "widgets.h" + +#include <kmessagebox.h> +#include <kapplication.h> +#include <kpassivepopup.h> +#include <krecentdocument.h> +#include <kcmdlineargs.h> +#include <kaboutdata.h> +#include <kfiledialog.h> +#include <kfileitem.h> +#include <kicondialog.h> +#include <kdirselectdialog.h> +#include <kcolordialog.h> +#include <kwindowsystem.h> +#include <kiconloader.h> +#include <klocale.h> + +#include <QtCore/QTimer> +#include <QDesktopWidget> + +#include <iostream> + +#if defined Q_WS_X11 && ! defined K_WS_QTONLY +#include <netwm.h> +#endif + +#ifdef QT_DBUS_LIB +#include <QtDBus/QDBusConnection> +#include <QtDBus/QDBusConnectionInterface> +#endif + +#include <unistd.h> + +using namespace std; + +// this class hooks into the eventloop and outputs the id +// of shown dialogs or makes the dialog transient for other winids. +// Will destroy itself on app exit. +class WinIdEmbedder: public QObject +{ +public: + WinIdEmbedder(bool printID, WId winId): + QObject(qApp), print(printID), id(winId) + { + if (qApp) + qApp->installEventFilter(this); + } +protected: + bool eventFilter(QObject *o, QEvent *e); +private: + bool print; + WId id; +}; + +bool WinIdEmbedder::eventFilter(QObject *o, QEvent *e) +{ + if (e->type() == QEvent::Show && o->isWidgetType() + && o->inherits("KDialog")) + { + QWidget *w = static_cast<QWidget*>(o); + if (print) + cout << "winId: " << w->winId() << endl; + if (id) + KWindowSystem::setMainWindow(w, id); + deleteLater(); // WinIdEmbedder is not needed anymore after the first dialog was shown + return false; + } + return QObject::eventFilter(o, e); +} + +/** + * Display a passive notification popup using the D-Bus interface, if possible. + * @return true if the notification was successfully sent, false otherwise. + */ +bool sendVisualNotification(const QString &text, const QString &title, const QString &icon, int timeout) +{ +#ifdef QT_DBUS_LIB + const QString dbusServiceName = "org.freedesktop.Notifications"; + const QString dbusInterfaceName = "org.freedesktop.Notifications"; + const QString dbusPath = "/org/freedesktop/Notifications"; + + // check if service already exists on plugin instantiation + QDBusConnectionInterface* interface = QDBusConnection::sessionBus().interface(); + + if (!interface || !interface->isServiceRegistered(dbusServiceName)) { + //kDebug() << dbusServiceName << "D-Bus service not registered"; + return false; + } + + if (timeout == 0) + timeout = 10 * 1000; + + QDBusMessage m = QDBusMessage::createMethodCall(dbusServiceName, dbusPath, dbusInterfaceName, "Notify"); + QList<QVariant> args; + + args.append("kdialog"); // app_name + args.append(0U); // replaces_id + args.append(icon); // app_icon + args.append(title); // summary + args.append(text); // body + args.append(QStringList()); // actions - unused for plain passive popups + args.append(QVariantMap()); // hints - unused atm + args.append(timeout); // expire timout + + m.setArguments(args); + + QDBusMessage replyMsg = QDBusConnection::sessionBus().call(m); + if(replyMsg.type() == QDBusMessage::ReplyMessage) { + if (!replyMsg.arguments().isEmpty()) { + return true; + } + // Not displaying any error messages as this is optional for kdialog + // and KPassivePopup is a perfectly valid fallback. + //else { + // kDebug() << "Error: received reply with no arguments."; + //} + } else if (replyMsg.type() == QDBusMessage::ErrorMessage) { + //kDebug() << "Error: failed to send D-Bus message"; + //kDebug() << replyMsg; + } else { + //kDebug() << "Unexpected reply type"; + } +#endif + return false; +} + +static void outputStringList(const QStringList &list, bool separateOutput) +{ + if ( separateOutput) { + for ( QStringList::ConstIterator it = list.begin(); it != list.end(); ++it ) { + cout << (*it).toLocal8Bit().data() << endl; + } + } else { + for ( QStringList::ConstIterator it = list.begin(); it != list.end(); ++it ) { + cout << (*it).toLocal8Bit().data() << " "; + } + cout << endl; + } +} + + +KGuiItem configuredYes(const QString &text) +{ + return KGuiItem( text, "dialog-ok" ); +} + +KGuiItem configuredNo(const QString &text) +{ + return KGuiItem( text, "process-stop" ); +} + +KGuiItem configuredCancel(const QString &text) +{ + return KGuiItem( text, "dialog-cancel" ); +} + +KGuiItem configuredContinue(const QString &text) +{ + return KGuiItem( text, "arrow-right" ); +} + +static int directCommand(KCmdLineArgs *args) +{ + QString title; + bool separateOutput = false; + bool printWId = args->isSet("print-winid"); + QString defaultEntry; + + // --title text + KCmdLineArgs *qtargs = KCmdLineArgs::parsedArgs("qt"); // --title is a qt option + if(qtargs->isSet("title")) { + title = qtargs->getOption("title"); + } + + // --separate-output + if (args->isSet("separate-output")) + { + separateOutput = true; + } + + WId winid = 0; + bool attach = args->isSet("attach"); + if(attach) { + winid = args->getOption("attach").toLong(&attach, 0); //C style parsing. If the string begins with "0x", base 16 is used; if the string begins with "0", base 8 is used; otherwise, base 10 is used. + } else if(args->isSet("embed")) { + /* KDialog originally used --embed for attaching the dialog box. However this is misleading and so we changed to --attach. + * For consistancy, we silently map --embed to --attach */ + attach = true; + winid = args->getOption("embed").toLong(&attach, 0); //C style parsing. If the string begins with "0x", base 16 is used; if the string begins with "0", base 8 is used; otherwise, base 10 is used. + } + + if (printWId || attach) + { + (void)new WinIdEmbedder(printWId, winid); + } + + // button labels + // Initialize with default labels + KGuiItem yesButton = KStandardGuiItem::yes(); + KGuiItem noButton = KStandardGuiItem::no(); + KGuiItem cancelButton = KStandardGuiItem::cancel(); + KGuiItem continueButton = KStandardGuiItem::cont(); + + // Customize the asked labels + if (args->isSet("yes-label")) { + yesButton = configuredYes( args->getOption("yes-label") ); + } + if (args->isSet("no-label")) { + noButton = configuredNo( args->getOption("no-label") ); + } + if (args->isSet("cancel-label")) { + cancelButton = configuredCancel( args->getOption("cancel-label") ); + } + if (args->isSet("continue-label")) { + continueButton = configuredContinue( args->getOption("continue-label") ); + } + + // --yesno and other message boxes + KMessageBox::DialogType type = (KMessageBox::DialogType) 0; + QByteArray option; + if (args->isSet("yesno")) { + option = "yesno"; + type = KMessageBox::QuestionYesNo; + } + else if (args->isSet("yesnocancel")) { + option = "yesnocancel"; + type = KMessageBox::QuestionYesNoCancel; + } + else if (args->isSet("warningyesno")) { + option = "warningyesno"; + type = KMessageBox::WarningYesNo; + } + else if (args->isSet("warningcontinuecancel")) { + option = "warningcontinuecancel"; + type = KMessageBox::WarningContinueCancel; + } + else if (args->isSet("warningyesnocancel")) { + option = "warningyesnocancel"; + type = KMessageBox::WarningYesNoCancel; + } + else if (args->isSet("sorry")) { + option = "sorry"; + type = KMessageBox::Sorry; + } + else if (args->isSet("detailedsorry")) { + option = "detailedsorry"; + } + else if (args->isSet("error")) { + option = "error"; + type = KMessageBox::Error; + } + else if (args->isSet("detailederror")) { + option = "detailederror"; + } + else if (args->isSet("msgbox")) { + option = "msgbox"; + type = KMessageBox::Information; + } + + if ( !option.isEmpty() ) + { + KConfig* dontagaincfg = NULL; + // --dontagain + QString dontagain; // QString() + if (args->isSet("dontagain")) + { + QString value = args->getOption("dontagain"); + QStringList values = value.split( ':', QString::SkipEmptyParts ); + if( values.count() == 2 ) + { + dontagaincfg = new KConfig( values[ 0 ] ); + KMessageBox::setDontShowAskAgainConfig( dontagaincfg ); + dontagain = values[ 1 ]; + } + else + qDebug( "Incorrect --dontagain!" ); + } + int ret = 0; + + QString text = Widgets::parseString(args->getOption(option)); + + QString details; + if (args->count() == 1) { + details = Widgets::parseString(args->arg(0)); + } + + if ( type == KMessageBox::WarningContinueCancel ) { + ret = KMessageBox::messageBox( 0, type, text, title, continueButton, + noButton, cancelButton, dontagain ); + } else if (option == "detailedsorry") { + KMessageBox::detailedSorry( 0, text, details, title ); + } else if (option == "detailederror") { + KMessageBox::detailedError( 0, text, details, title ); + } else { + ret = KMessageBox::messageBox( 0, type, text, title, + yesButton, noButton, cancelButton, dontagain ); + } + delete dontagaincfg; + // ret is 1 for Ok, 2 for Cancel, 3 for Yes, 4 for No and 5 for Continue. + // We want to return 0 for ok, yes and continue, 1 for no and 2 for cancel + return (ret == KMessageBox::Ok || ret == KMessageBox::Yes || ret == KMessageBox::Continue) ? 0 + : ( ret == KMessageBox::No ? 1 : 2 ); + } + + // --inputbox text [init] + if (args->isSet("inputbox")) + { + QString result; + QString init; + + if (args->count() > 0) + init = args->arg(0); + + const bool retcode = Widgets::inputBox(0, title, args->getOption("inputbox"), init, result); + cout << result.toLocal8Bit().data() << endl; + return retcode ? 0 : 1; + } + + + // --password text + if (args->isSet("password")) + { + QString result; + const bool retcode = Widgets::passwordBox(0, title, args->getOption("password"), result); + cout << qPrintable(result) << endl; + return retcode ? 0 : 1; + } + + // --passivepopup + if (args->isSet("passivepopup")) + { + int timeout = 0; + if (args->count() > 0) { + timeout = 1000 * args->arg(0).toInt(); + } + + if (timeout < 0) { + timeout = -1; + } + + // since --icon is a kde option, we need to parse the kde options here as well + KCmdLineArgs *kdeargs = KCmdLineArgs::parsedArgs("kde"); + + // Use --icon parameter for passivepopup as well + QString icon; + if (kdeargs->isSet("icon")) { + icon = kdeargs->getOption("icon"); + } else { + icon = "dialog-information"; // Use generic (i)-icon if none specified + } + + // try to use more stylish notifications + if (sendVisualNotification(Widgets::parseString(args->getOption("passivepopup")), title, icon, timeout)) + return 0; + + // ...did not work, use KPassivePopup as fallback + + // parse timeout time again, so it does not auto-close the fallback (timer cannot handle -1 time) + if (args->count() > 0) { + timeout = 1000 * args->arg(0).toInt(); + } + if (timeout <= 0) { + timeout = 10*1000; // 10 seconds should be a decent time for auto-closing (you can override this using a parameter) + } + + QPixmap passiveicon; + if (kdeargs->isSet("icon")) { // Only show icon if explicitly requested + passiveicon = KIconLoader::global()->loadIcon(icon, KIconLoader::Dialog); + } + KPassivePopup *popup = KPassivePopup::message( KPassivePopup::Boxed, // style + title, + Widgets::parseString(args->getOption("passivepopup")), + passiveicon, + (QWidget*)0UL, // parent + timeout ); + KDialog::centerOnScreen( popup ); + QTimer *timer = new QTimer(); + QObject::connect( timer, SIGNAL(timeout()), qApp, SLOT(quit()) ); + QObject::connect( popup, SIGNAL(clicked()), qApp, SLOT(quit()) ); + timer->setSingleShot( true ); + timer->start( timeout ); + +#ifdef Q_WS_X11 + QString geometry; + KCmdLineArgs *args = KCmdLineArgs::parsedArgs("kde"); + if (args && args->isSet("geometry")) + geometry = args->getOption("geometry"); + if ( !geometry.isEmpty()) { + int x, y; + int w, h; + int m = XParseGeometry( geometry.toLatin1(), &x, &y, (unsigned int*)&w, (unsigned int*)&h); + if ( (m & XNegative) ) + x = KApplication::desktop()->width() + x - w; + if ( (m & YNegative) ) + y = KApplication::desktop()->height() + y - h; + popup->setAnchor( QPoint(x, y) ); + } +#endif + qApp->exec(); + return 0; + } + + // --textbox file [width] [height] + if (args->isSet("textbox")) + { + int w = 0; + int h = 0; + + if (args->count() == 2) { + w = args->arg(0).toInt(); + h = args->arg(1).toInt(); + } + + return Widgets::textBox(0, w, h, title, args->getOption("textbox")); + } + + // --textinputbox file [width] [height] + if (args->isSet("textinputbox")) + { + int w = 400; + int h = 200; + + if (args->count() >= 3) { + w = args->arg(1).toInt(); + h = args->arg(2).toInt(); + } + + QString init; + if (args->count() >= 1) { + init = Widgets::parseString(args->arg(0)); + } + + QString result; + int ret = Widgets::textInputBox(0, w, h, title, Widgets::parseString(args->getOption("textinputbox")), init, result); + cout << qPrintable(result) << endl; + return ret; + } + + // --combobox <text> item [item] ..." + if (args->isSet("combobox")) { + QStringList list; + if (args->count() >= 1) { + for (int i = 0; i < args->count(); i++) { + list.append(args->arg(i)); + } + const QString text = Widgets::parseString(args->getOption("combobox")); + if (args->isSet("default")) { + defaultEntry = args->getOption("default"); + } + QString result; + const bool retcode = Widgets::comboBox(0, title, text, list, defaultEntry, result); + cout << result.toLocal8Bit().data() << endl; + return retcode ? 0 : 1; + } + return -1; + } + + // --menu text [tag item] [tag item] ... + if (args->isSet("menu")) { + QStringList list; + if (args->count() >= 2) { + for (int i = 0; i < args->count(); i++) { + list.append(args->arg(i)); + } + const QString text = Widgets::parseString(args->getOption("menu")); + if (args->isSet("default")) { + defaultEntry = args->getOption("default"); + } + QString result; + const bool retcode = Widgets::listBox(0, title, text, list, defaultEntry, result); + if (1 == retcode) { // OK was selected + cout << result.toLocal8Bit().data() << endl; + } + return retcode ? 0 : 1; + } + return -1; + } + + // --checklist text [tag item status] [tag item status] ... + if (args->isSet("checklist")) { + QStringList list; + if (args->count() >= 3) { + for (int i = 0; i < args->count(); i++) { + list.append(args->arg(i)); + } + + const QString text = Widgets::parseString(args->getOption("checklist")); + QStringList result; + + const bool retcode = Widgets::checkList(0, title, text, list, separateOutput, result); + + for (int i=0; i<result.count(); i++) + if (!result.at(i).toLocal8Bit().isEmpty()) { + cout << result.at(i).toLocal8Bit().data() << endl; + } + exit( retcode ? 0 : 1 ); + } + return -1; + } + + // --radiolist text width height menuheight [tag item status] + if (args->isSet("radiolist")) { + QStringList list; + if (args->count() >= 3) { + for (int i = 0; i < args->count(); i++) { + list.append(args->arg(i)); + } + + const QString text = Widgets::parseString(args->getOption("radiolist")); + QString result; + const bool retcode = Widgets::radioBox(0, title, text, list, result); + cout << result.toLocal8Bit().data() << endl; + exit( retcode ? 0 : 1 ); + } + return -1; + } + + // getopenfilename [startDir] [filter] + if (args->isSet("getopenfilename")) { + QString startDir; + QString filter; + startDir = args->getOption("getopenfilename"); + if (args->count() >= 1) { + filter = Widgets::parseString(args->arg(0)); + } + KFileDialog dlg( startDir, filter, 0 ); + dlg.setOperationMode( KFileDialog::Opening ); + + if (args->isSet("multiple")) { + dlg.setMode(KFile::Files | KFile::LocalOnly); + } else { + dlg.setMode(KFile::File | KFile::LocalOnly); + } + Widgets::handleXGeometry(&dlg); + kapp->setTopWidget( &dlg ); + dlg.setCaption(title.isEmpty() ? i18nc("@title:window", "Open") : title); + dlg.exec(); + + if (args->isSet("multiple")) { + QStringList result = dlg.selectedFiles(); + if ( !result.isEmpty() ) { + outputStringList( result, separateOutput ); + return 0; + } + } else { + QString result = dlg.selectedFile(); + if (!result.isEmpty()) { + cout << result.toLocal8Bit().data() << endl; + return 0; + } + } + return 1; // canceled + } + + + // getsaveurl [startDir] [filter] + // getsavefilename [startDir] [filter] + if ( (args->isSet("getsavefilename") ) || (args->isSet("getsaveurl") ) ) { + QString startDir; + QString filter; + if ( args->isSet("getsavefilename") ) { + startDir = args->getOption("getsavefilename"); + } else { + startDir = args->getOption("getsaveurl"); + } + if (args->count() >= 1) { + filter = Widgets::parseString(args->arg(0)); + } + // copied from KFileDialog::getSaveFileName(), so we can add geometry + bool specialDir = ( startDir.at(0) == ':' ); + if ( !specialDir ) { + KFileItem kfi(KFileItem::Unknown, KFileItem::Unknown, KUrl(startDir)); + specialDir = kfi.isDir(); + } + KFileDialog dlg( specialDir ? startDir : QString(), filter, 0 ); + if ( !specialDir ) + dlg.setSelection( startDir ); + dlg.setOperationMode( KFileDialog::Saving ); + Widgets::handleXGeometry(&dlg); + kapp->setTopWidget( &dlg ); + dlg.setCaption(title.isEmpty() ? i18nc("@title:window", "Save As") : title); + dlg.exec(); + + if ( args->isSet("getsaveurl") ) { + KUrl result = dlg.selectedUrl(); + if ( result.isValid()) { + + cout << result.url().toLocal8Bit().data() << endl; + return 0; + } + } else { // getsavefilename + QString result = dlg.selectedFile(); + if (!result.isEmpty()) { + KRecentDocument::add(result); + cout << result.toLocal8Bit().data() << endl; + return 0; + } + } + return 1; // canceled + } + + // getexistingdirectory [startDir] + if (args->isSet("getexistingdirectory")) { + QString startDir; + startDir = args->getOption("getexistingdirectory"); + QString result; + KUrl url; + KDirSelectDialog myDialog( startDir, true, 0 ); + + kapp->setTopWidget( &myDialog ); + + Widgets::handleXGeometry(&myDialog); + if ( !title.isEmpty() ) + myDialog.setCaption( title ); + + if ( myDialog.exec() == QDialog::Accepted ) + url = myDialog.url(); + + if ( url.isValid() ) + result = url.path(); + if (!result.isEmpty()) { + cout << result.toLocal8Bit().data() << endl; + return 0; + } + return 1; // canceled + } + + // getopenurl [startDir] [filter] + if (args->isSet("getopenurl")) { + QString startDir; + QString filter; + startDir = args->getOption("getopenurl"); + if (args->count() >= 1) { + filter = Widgets::parseString(args->arg(0)); + } + KFileDialog dlg( startDir, filter, 0 ); + dlg.setOperationMode( KFileDialog::Opening ); + + if (args->isSet("multiple")) { + dlg.setMode(KFile::Files); + } else { + dlg.setMode(KFile::File); + } + Widgets::handleXGeometry(&dlg); + kapp->setTopWidget( &dlg ); + dlg.setCaption(title.isEmpty() ? i18nc("@title:window", "Open") : title); + dlg.exec(); + + if (args->isSet("multiple")) { + KUrl::List result = dlg.selectedUrls(); + if ( !result.isEmpty() ) { + outputStringList( result.toStringList(), separateOutput ); + return 0; + } + } else { + KUrl result = dlg.selectedUrl(); + if (!result.isEmpty()) { + cout << result.url().toLocal8Bit().data() << endl; + return 0; + } + } + return 1; // canceled + } + + // geticon [group] [context] + if (args->isSet("geticon")) { + QString groupStr, contextStr; + groupStr = args->getOption("geticon"); + if (args->count() >= 1) { + contextStr = args->arg(0); + } + const KIconLoader::Group group = + ( groupStr == QLatin1String( "Desktop" ) ) ? KIconLoader::Desktop : + ( groupStr == QLatin1String( "Toolbar" ) ) ? KIconLoader::Toolbar : + ( groupStr == QLatin1String( "MainToolbar" ) ) ? KIconLoader::MainToolbar : + ( groupStr == QLatin1String( "Small" ) ) ? KIconLoader::Small : + ( groupStr == QLatin1String( "Panel" ) ) ? KIconLoader::Panel : + ( groupStr == QLatin1String( "Dialog" ) ) ? KIconLoader::Dialog : + ( groupStr == QLatin1String( "User" ) ) ? KIconLoader::User : + /* else */ KIconLoader::NoGroup; + + const KIconLoader::Context context = + ( contextStr == QLatin1String( "Action" ) ) ? KIconLoader::Action : + ( contextStr == QLatin1String( "Application" ) ) ? KIconLoader::Application : + ( contextStr == QLatin1String( "Device" ) ) ? KIconLoader::Device : + ( contextStr == QLatin1String( "MimeType" ) ) ? KIconLoader::MimeType : + ( contextStr == QLatin1String( "Animation" ) ) ? KIconLoader::Animation : + ( contextStr == QLatin1String( "Category" ) ) ? KIconLoader::Category : + ( contextStr == QLatin1String( "Emblem" ) ) ? KIconLoader::Emblem : + ( contextStr == QLatin1String( "Emote" ) ) ? KIconLoader::Emote : + ( contextStr == QLatin1String( "International" ) ) ? KIconLoader::International : + ( contextStr == QLatin1String( "Place" ) ) ? KIconLoader::Place : + ( contextStr == QLatin1String( "StatusIcon" ) ) ? KIconLoader::StatusIcon : + /* else */ KIconLoader::Any; + + KIconDialog dlg((QWidget*)0L); + kapp->setTopWidget( &dlg ); + dlg.setup( group, context); + dlg.setIconSize(KIconLoader::SizeHuge); + + if (!title.isEmpty()) + dlg.setCaption(title); + + Widgets::handleXGeometry(&dlg); + + QString result = dlg.openDialog(); + + if (!result.isEmpty()) { + cout << result.toLocal8Bit().data() << endl; + return 0; + } + return 1; // canceled + } + + // --progressbar text totalsteps + if (args->isSet("progressbar")) + { + cout << "org.kde.kdialog-" << getpid() << " /ProgressDialog" << endl; + if (fork()) + _exit(0); + close(1); + + int totalsteps = 100; + const QString text = Widgets::parseString(args->getOption("progressbar")); + + if (args->count() == 1) + totalsteps = args->arg(0).toInt(); + + return Widgets::progressBar(0, title, text, totalsteps) ? 1 : 0; + } + + // --getcolor + if (args->isSet("getcolor")) { + KColorDialog dlg((QWidget*)0L, true); + + if (args->isSet("default")) { + defaultEntry = args->getOption("default"); + dlg.setColor(defaultEntry); + } + Widgets::handleXGeometry(&dlg); + kapp->setTopWidget(&dlg); + dlg.setCaption(title.isEmpty() ? i18nc("@title:window", "Choose Color") : title); + + if (dlg.exec() == KColorDialog::Accepted) { + QString result; + if (dlg.color().isValid()) { + result = dlg.color().name(); + } else { + result = dlg.defaultColor().name(); + } + cout << result.toLocal8Bit().data() << endl; + return 0; + } + return 1; // cancelled + } + if (args->isSet("slider")) + { + int miniValue = 0; + int maxValue = 0; + int step = 0; + const QString text = Widgets::parseString(args->getOption("slider")); + if ( args->count() == 3 ) + { + miniValue = args->arg(0).toInt(); + maxValue = args->arg( 1 ).toInt(); + step = args->arg( 2 ).toInt(); + } + int result = 0; + + const bool returnCode = Widgets::slider(0, title, text, miniValue, maxValue, step, result); + if ( returnCode ) + cout << result << endl; + return returnCode; + } + if (args->isSet("calendar")) + { + const QString text = Widgets::parseString(args->getOption("calendar")); + QDate result; + + const bool returnCode = Widgets::calendar(0, title, text, result); + if ( returnCode ) + cout << result.toString().toLocal8Bit().data() << endl; + return returnCode; + } + + KCmdLineArgs::usage(); + return -2; // NOTREACHED +} + + +int main(int argc, char *argv[]) +{ + KAboutData aboutData( "kdialog", 0, ki18n("KDialog"), + "1.0", ki18n( "KDialog can be used to show nice dialog boxes from shell scripts" ), + KAboutData::License_GPL, + ki18n("(C) 2000, Nick Thompson")); + aboutData.addAuthor(ki18n("David Faure"), ki18n("Current maintainer"),"faure@kde.org"); + aboutData.addAuthor(ki18n("Brad Hards"), KLocalizedString(), "bradh@frogmouth.net"); + aboutData.addAuthor(ki18n("Nick Thompson"),KLocalizedString(), 0/*"nickthompson@lucent.com" bounces*/); + aboutData.addAuthor(ki18n("Matthias Hölzer"),KLocalizedString(),"hoelzer@kde.org"); + aboutData.addAuthor(ki18n("David Gümbel"),KLocalizedString(),"david.guembel@gmx.net"); + aboutData.addAuthor(ki18n("Richard Moore"),KLocalizedString(),"rich@kde.org"); + aboutData.addAuthor(ki18n("Dawit Alemayehu"),KLocalizedString(),"adawit@kde.org"); + aboutData.addAuthor(ki18n("Kai Uwe Broulik"),KLocalizedString(),"kde@privat.broulik.de"); + aboutData.setProgramIconName("system-run"); + + KCmdLineArgs::init(argc, argv, &aboutData); + + KCmdLineOptions options; + options.add("yesno <text>", ki18n("Question message box with yes/no buttons")); + options.add("yesnocancel <text>", ki18n("Question message box with yes/no/cancel buttons")); + options.add("warningyesno <text>", ki18n("Warning message box with yes/no buttons")); + options.add("warningcontinuecancel <text>", ki18n("Warning message box with continue/cancel buttons")); + options.add("warningyesnocancel <text>", ki18n("Warning message box with yes/no/cancel buttons")); + options.add("yes-label <text>", ki18n("Use text as Yes button label")); + options.add("no-label <text>", ki18n("Use text as No button label")); + options.add("cancel-label <text>", ki18n("Use text as Cancel button label")); + options.add("continue-label <text>", ki18n("Use text as Continue button label")); + options.add("sorry <text>", ki18n("'Sorry' message box")); + options.add("detailedsorry <text> <details>", ki18n("'Sorry' message box with expandable Details field")); + options.add("error <text>", ki18n("'Error' message box")); + options.add("detailederror <text> <details>", ki18n("'Error' message box with expandable Details field")); + options.add("msgbox <text>", ki18n("Message Box dialog")); + options.add("inputbox <text> <init>", ki18n("Input Box dialog")); + options.add("password <text>", ki18n("Password dialog")); + options.add("textbox <file> [width] [height]", ki18n("Text Box dialog")); + options.add("textinputbox <text> <init> [width] [height]", ki18n("Text Input Box dialog")); + options.add("combobox <text> item [item] [item] ...", ki18n("ComboBox dialog")); + options.add("menu <text> [tag item] [tag item] ...", ki18n("Menu dialog")); + options.add("checklist <text> [tag item status] ...", ki18n("Check List dialog")); + options.add("radiolist <text> [tag item status] ...", ki18n("Radio List dialog")); + options.add("passivepopup <text> <timeout>", ki18n("Passive Popup")); + options.add("getopenfilename [startDir] [filter]", ki18n("File dialog to open an existing file")); + options.add("getsavefilename [startDir] [filter]", ki18n("File dialog to save a file")); + options.add("getexistingdirectory [startDir]", ki18n("File dialog to select an existing directory")); + options.add("getopenurl [startDir] [filter]", ki18n("File dialog to open an existing URL")); + options.add("getsaveurl [startDir] [filter]", ki18n("File dialog to save a URL")); + options.add("geticon [group] [context]", ki18n("Icon chooser dialog")); + options.add("progressbar <text> [totalsteps]", ki18n("Progress bar dialog, returns a D-Bus reference for communication")); + options.add("getcolor", ki18n("Color dialog to select a color")); + // TODO gauge stuff, reading values from stdin + options.add("title <text>", ki18n("Dialog title")); + options.add("default <text>", ki18n("Default entry to use for combobox, menu and color")); + options.add("multiple", ki18n("Allows the --getopenurl and --getopenfilename options to return multiple files")); + options.add("separate-output", ki18n("Return list items on separate lines (for checklist option and file open with --multiple)")); + options.add("print-winid", ki18n("Outputs the winId of each dialog")); + options.add("dontagain <file:entry>", ki18n("Config file and option name for saving the \"do-not-show/ask-again\" state")); + options.add( "slider <text> [minvalue] [maxvalue] [step]", ki18n( "Slider dialog box, returns selected value" ) ); + options.add( "calendar <text>", ki18n( "Calendar dialog box, returns selected date" ) ); + /* kdialog originally used --embed for attaching the dialog box. However this is misleading and so we changed to --attach. + * For backwards compatibility, we silently map --embed to --attach */ + options.add("attach <winid>", ki18n("Makes the dialog transient for an X app specified by winid")); + options.add("embed <winid>"); + + options.add("+[arg]", ki18n("Arguments - depending on main option")); + + KCmdLineArgs::addCmdLineOptions( options ); // Add our own options. + + KApplication app; + + KCmdLineArgs *args = KCmdLineArgs::parsedArgs(); + + // execute direct kdialog command + return directCommand(args); +} + diff --git a/kdialog/klistboxdialog.cpp b/kdialog/klistboxdialog.cpp new file mode 100644 index 00000000..99b726c5 --- /dev/null +++ b/kdialog/klistboxdialog.cpp @@ -0,0 +1,62 @@ +// +// Copyright (C) 1998-2005 Matthias Hoelzer +// email: hoelzer@physik.uni-wuerzburg.de +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the7 implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +#include "klistboxdialog.h" +#include "moc_klistboxdialog.cpp" + +#include <QLabel> +#include <kvbox.h> + +#include "klocale.h" + +KListBoxDialog::KListBoxDialog(const QString &text, QWidget *parent) + : KDialog( parent ) +{ + setModal(true); + setButtons( Ok | Cancel ); + + KVBox *page = new KVBox(this); + setMainWidget(page); + + label = new QLabel(text, page); + label->setAlignment(Qt::AlignCenter); + + table = new QListWidget(page); + table->setFocus(); +} + +void KListBoxDialog::insertItem(const QString& item) +{ + table->addItem(item); + table->setCurrentItem(0); +} + +void KListBoxDialog::setCurrentItem(const QString& item) +{ + for ( int i=0; i < (int) table->count(); i++ ) { + if ( table->item(i)->text() == item ) { + table->setCurrentItem(table->item(i)); + break; + } + } +} + +int KListBoxDialog::currentItem() const +{ + return table->currentRow(); +} diff --git a/kdialog/klistboxdialog.h b/kdialog/klistboxdialog.h new file mode 100644 index 00000000..b39d00e0 --- /dev/null +++ b/kdialog/klistboxdialog.h @@ -0,0 +1,52 @@ +// +// Copyright (C) 1998-2005 Matthias Hoelzer +// email: hoelzer@physik.uni-wuerzburg.de +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the7 implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// + + +#ifndef KLISTBOXDIALOG_H +#define KLISTBOXDIALOG_H + +#include <kdialog.h> + +#include <QListWidget> + +#include <QLabel> + +class KListBoxDialog : public KDialog +{ + Q_OBJECT + +public: + + explicit KListBoxDialog(const QString &text, QWidget *parent=0); + ~KListBoxDialog() {} + + QListWidget &getTable() { return *table; } + + void insertItem( const QString& text ); + void setCurrentItem ( const QString& text ); + int currentItem() const; + +protected: + + QListWidget *table; + QLabel *label; + +}; + +#endif diff --git a/kdialog/org.kde.kdialog.ProgressDialog.xml b/kdialog/org.kde.kdialog.ProgressDialog.xml new file mode 100644 index 00000000..266592fc --- /dev/null +++ b/kdialog/org.kde.kdialog.ProgressDialog.xml @@ -0,0 +1,19 @@ +<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" +"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> +<node> + <interface name="org.kde.kdialog.ProgressDialog"> + <property name="maximum" type="i" access="readwrite"/> + <property name="value" type="i" access="readwrite"/> + <property name="autoClose" type="b" access="readwrite"/> + <method name="setLabelText"> + <arg type="s" name="label" direction="in"/> + </method> + <method name="showCancelButton"> + <arg name="value" type="b" direction="in"/> + </method> + <method name="wasCancelled"> + <arg type="b" direction="out"/> + </method> + <method name="close"/> + </interface> +</node> diff --git a/kdialog/progresscanceldemo b/kdialog/progresscanceldemo new file mode 100755 index 00000000..373255ab --- /dev/null +++ b/kdialog/progresscanceldemo @@ -0,0 +1,10 @@ +dbusRef=`kdialog --progressbar "Press Cancel at Any time" 10` +qdbus $dbusRef org.kde.kdialog.ProgressDialog.showCancelButton true + +until test "true" = `qdbus $dbusRef org.kde.kdialog.ProgressDialog.wasCancelled`; do + sleep 1 + inc=$((`qdbus $dbusRef org.freedesktop.DBus.Properties.Get org.kde.kdialog.ProgressDialog value` + 1)) + qdbus $dbusRef org.freedesktop.DBus.Properties.Set org.kde.kdialog.ProgressDialog value $inc; +done + +qdbus $dbusRef org.kde.kdialog.ProgressDialog.close diff --git a/kdialog/progressdemo b/kdialog/progressdemo new file mode 100755 index 00000000..0b71081b --- /dev/null +++ b/kdialog/progressdemo @@ -0,0 +1,17 @@ +dbusRef=`kdialog --geometry 300x200+100-100 --progressbar "Initialising" 6` +qdbus $dbusRef Set org.kde.kdialog.ProgressDialog value 1 +qdbus $dbusRef setLabelText "Thinking really hard" +sleep 2 +qdbus $dbusRef Set org.kde.kdialog.ProgressDialog value 2 +sleep 2 +qdbus $dbusRef setLabelText "Thinking some more" +qdbus $dbusRef Set org.kde.kdialog.ProgressDialog value 3 +sleep 2 +qdbus $dbusRef Set org.kde.kdialog.ProgressDialog value 4 +sleep 2 +qdbus $dbusRef setLabelText "Finishing up" +qdbus $dbusRef Set org.kde.kdialog.ProgressDialog value 5 +sleep 1 +qdbus $dbusRef Set org.kde.kdialog.ProgressDialog value 6 +sleep 1 +qdbus $dbusRef org.kde.kdialog.ProgressDialog.close diff --git a/kdialog/progressdialog.cpp b/kdialog/progressdialog.cpp new file mode 100644 index 00000000..62728fed --- /dev/null +++ b/kdialog/progressdialog.cpp @@ -0,0 +1,96 @@ +// +// Copyright (C) 2004-2005 Stephan Binner <binner@kde.org> +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the7 implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// + +#include "progressdialog.h" +#include "kdebug.h" +#include "widgets.h" +#include <kprogressdialog.h> +#include "progressdialogadaptor.h" + +ProgressDialog::ProgressDialog(QWidget* parent, const QString& caption, const QString& text, int totalSteps) + : KProgressDialog(parent, caption, text, 0) +{ + (void)new ProgressDialogAdaptor( this ); + QDBusConnection::sessionBus().registerObject( QLatin1String("/ProgressDialog"), this ); + setAutoClose( false ); + progressBar()->setMaximum( totalSteps ); + showCancelButton( false ); + Widgets::handleXGeometry(this); +} + +void ProgressDialog::setMaximum( int totalSteps ) +{ + progressBar()->setMaximum( totalSteps ); + if ( progressBar()->value()>=totalSteps ) + showCancelButton( false ); +} + +int ProgressDialog::maximum() const +{ + return progressBar()->maximum(); +} + +void ProgressDialog::setValue( int progress ) +{ + progressBar()->setValue( progress ); + if (progress>=maximum() ) + showCancelButton( false ); +} + +int ProgressDialog::value() const +{ + return progressBar()->value(); +} + +void ProgressDialog::setLabelText(const QString& label) +{ + KProgressDialog::setLabelText( label ); +} + +void ProgressDialog::showCancelButton( bool show ) +{ + setAllowCancel( false ); + KProgressDialog::showCancelButton( show ); +} + +bool ProgressDialog::wasCancelled() const +{ + return KProgressDialog::wasCancelled(); +} + +void ProgressDialog::ignoreCancel() +{ + KProgressDialog::ignoreCancel(); +} + +void ProgressDialog::setAutoClose( bool close ) +{ + KProgressDialog::setAutoClose( close ); +} + +bool ProgressDialog::autoClose() const +{ + return KProgressDialog::autoClose(); +} + +void ProgressDialog::close() +{ + slotButtonClicked(KDialog::Close); +} + +#include "moc_progressdialog.cpp" diff --git a/kdialog/progressdialog.h b/kdialog/progressdialog.h new file mode 100644 index 00000000..bac64298 --- /dev/null +++ b/kdialog/progressdialog.h @@ -0,0 +1,53 @@ +// +// Copyright (C) 2004-2005 Stephan Binner <binner@kde.org> +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the7 implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// + +#ifndef PROGRESSDIALOG_H +#define PROGRESSDIALOG_H + +#include <kprogressdialog.h> +class ProgressDialog : public KProgressDialog +{ + Q_OBJECT + Q_PROPERTY(int value READ value WRITE setValue) + Q_PROPERTY(int maximum READ maximum WRITE setMaximum) + Q_PROPERTY(bool autoClose READ autoClose WRITE setAutoClose) + + public: + explicit ProgressDialog(QWidget* parent = 0, + const QString& caption = QString(), + const QString& text = QString(), int totalSteps = 100); + + void setMaximum( int ); + int maximum() const; + + void setValue( int ); + int value() const; + + void setLabelText(const QString&); + + void showCancelButton(bool show); + bool wasCancelled() const; + void ignoreCancel(); + + void setAutoClose( bool ); + bool autoClose() const; + + void close(); +}; + +#endif // PROGRESSDIALOG_H diff --git a/kdialog/test b/kdialog/test new file mode 100755 index 00000000..5c00f3b4 --- /dev/null +++ b/kdialog/test @@ -0,0 +1,73 @@ +echo "yesno box:" +kdialog --geometry 400x300+100+50 --title "This is a yesno box" --yesno "Choose: yes or no" +if [ $? = 0 ]; then + echo " your choice was: yes" +else + echo " your choice was: no" +fi + +echo "continue or cancel warning box:" +kdialog --geometry 200x300+100-50 --title "This is a warningcontinuecancel box" --warningcontinuecancel "Choose: continue or cancel" +if [ $? = 0 ]; then + echo " your choice was: continue" +else + echo " your choice was: cancel" +fi + +echo "message box:" +kdialog --geometry 300x400-100-50 --title "This is a message" --msgbox "Well, this is it:\nthe message" + +echo "input box:" +kdialog --title "This is a input box" --inputbox "What is your name" "Joe User" +if [ $? = 0 ]; then + echo " you accepted" +else + echo " you did not accept" +fi + +echo "input box, with geometry:" +kdialog --geometry 300x400-100-50 --title "This is a input box" --inputbox "What is your name" "Joe User" +if [ $? = 0 ]; then + echo " you accepted" +else + echo " you did not accept" +fi + +echo "text box:" +kdialog --geometry 300x400-100-50 --icon "about-kde" --title "This is a text box" --textbox widgets.h 400 300 + +echo "menu:" +kdialog --icon "about-kde" --geometry 300x400-100-50 --title "This is a menu" --default "French" --menu "Choose one of these" a English b German c French d Spanish + +echo "checklist:" +kdialog --geometry 400x300+100+50 --icon "about-kde" --title "This is a checklist" --checklist "Choose some of these" a English on b German off c French off d Spanish on + +echo "radiolist:" +kdialog --geometry 400x300+100+50 --icon "about-kde" --title "This is a radiolist" --radiolist "Choose one of these" a English off b German off c French on d Spanish off + +echo "combobox:" +kdialog --geometry 400x300+100+50 --icon "about-kde" --default "Chocolate" --title "This is a combobox" --combobox "Pick your favorite ice-cream flavor:" "Vanilla" "Chocolate" "Strawberry" "Fudge" + +echo "passivepopup:" +kdialog --geometry 1x1+200+350 --title "This is a passive popup" --passivepopup "It will disappear in about 10 seconds" 10 + +echo "password:" +kdialog --title "This is a password dialog" --geometry 400x300+100+50 --icon "desktop" --icon "about-kde" --password "Enter the password:" + +echo "Open File:" +kdialog --getopenfilename . "*.cpp *.cc *.c |C and C++ Source Files" + +echo "Open multiple files:" +kdialog --multiple --getopenfilename . "*.cpp *.cc *.c |C and C++ Source Files" + +echo "Open multiple files, output on separate lines:" +kdialog --multiple --separate-output --getopenfilename . "*.cpp *.cc *.c |C and C++ Source Files" + +echo "Open URL:" +kdialog --getopenurl . "*.cpp *.cc *.c |C and C++ Source Files" + +echo "Open multiple URLs:" +kdialog --multiple --getopenurl . "*.cpp *.cc *.c |C and C++ Source Files" + +echo "Open multiple URLs, output on separate lines:" +kdialog --multiple --separate-output --getopenurl . "*.cpp *.cc *.c |C and C++ Source Files" diff --git a/kdialog/widgets.cpp b/kdialog/widgets.cpp new file mode 100644 index 00000000..b26333cc --- /dev/null +++ b/kdialog/widgets.cpp @@ -0,0 +1,402 @@ +// +// Copyright (C) 1998 Matthias Hoelzer <hoelzer@kde.org> +// Copyright (C) 2002-2005 David Faure <faure@kde.org> +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the7 implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// + + +// Own +#include "widgets.h" +#include <kdatepicker.h> + +// Qt +#include <QtCore/QFile> +#include <QDesktopWidget> +#include <QtCore/QTextStream> +#include <QTextCursor> +#include <QLabel> + +// KDE +#include <klocale.h> +#include <kmessagebox.h> +#include <kinputdialog.h> +#include <kpassworddialog.h> +#include <kcombobox.h> +#include <kdebug.h> +#include <kapplication.h> +#include <kcmdlineargs.h> +#include <ktextedit.h> +#include <kvbox.h> + +// Local +#include "klistboxdialog.h" +#include "progressdialog.h" + + +#if defined Q_WS_X11 && ! defined K_WS_QTONLY +#include <netwm.h> +#endif + +void Widgets::handleXGeometry(QWidget * dlg) +{ +#ifdef Q_WS_X11 + QString geometry; + KCmdLineArgs *args = KCmdLineArgs::parsedArgs("kde"); + if (args && args->isSet("geometry")) + geometry = args->getOption("geometry"); + if ( ! geometry.isEmpty()) { + int x, y; + int w, h; + int m = XParseGeometry( geometry.toLatin1(), &x, &y, (unsigned int*)&w, (unsigned int*)&h); + if ( (m & XNegative) ) + x = KApplication::desktop()->width() + x - w; + if ( (m & YNegative) ) + y = KApplication::desktop()->height() + y - h; + dlg->setGeometry(x, y, w, h); + // kDebug() << "x: " << x << " y: " << y << " w: " << w << " h: " << h; + } +#endif +} + +bool Widgets::inputBox(QWidget *parent, const QString& title, const QString& text, const QString& init, QString &result) +{ + bool ok; + const QString str = KInputDialog::getText( title, text, init, &ok, parent ); + if ( ok ) + result = str; + return ok; +} + +bool Widgets::passwordBox(QWidget *parent, const QString& title, const QString& text, QString &result) +{ + KPasswordDialog dlg( parent ); + kapp->setTopWidget( &dlg ); + dlg.setCaption(title); + dlg.setPrompt(text); + + handleXGeometry(&dlg); + + bool retcode = (dlg.exec() == QDialog::Accepted); + if ( retcode ) + result = dlg.password(); + return retcode; +} + +int Widgets::textBox(QWidget *parent, int width, int height, const QString& title, const QString& file) +{ +// KTextBox dlg(parent, 0, true, width, height, file); + KDialog dlg( parent ); + dlg.setCaption( title ); + dlg.setButtons( KDialog::Ok ); + dlg.setModal( true ); + + kapp->setTopWidget( &dlg ); + KVBox* vbox = new KVBox(&dlg); + dlg.setMainWidget(vbox); + + KTextEdit *edit = new KTextEdit( vbox ); + edit->setReadOnly(true); + edit->setFocus(); + + QFile f(file); + if (!f.open(QIODevice::ReadOnly)) + { + kError() << i18n("kdialog: could not open file %1", file) << endl; + return -1; + } + QTextStream s(&f); + + while (!s.atEnd()) + edit->append(s.readLine()); + + edit->setTextCursor(QTextCursor(edit->document())); + + f.close(); + + if ( width > 0 && height > 0 ) + dlg.setInitialSize( QSize( width, height ) ); + + handleXGeometry(&dlg); + dlg.setCaption(title); + return (dlg.exec() == KDialog::Accepted) ? 0 : 1; +} + +int Widgets::textInputBox(QWidget *parent, int width, int height, const QString& title, const QString& text, const QString& init, QString &result) +{ +// KTextBox dlg(parent, 0, true, width, height, file); + KDialog dlg( parent ); + dlg.setCaption( title ); + dlg.setButtons( KDialog::Ok ); + dlg.setModal( true ); + + kapp->setTopWidget( &dlg ); + KVBox* vbox = new KVBox(&dlg); + + dlg.setMainWidget(vbox); + + if( !text.isEmpty() ) + { + QLabel *label = new QLabel(vbox); + label->setText(text); + } + + KTextEdit *edit = new KTextEdit( vbox ); + edit->setReadOnly(false); + edit->setFocus(); + + edit->insertPlainText( init ); + + if ( width > 0 && height > 0 ) + dlg.setInitialSize( QSize( width, height ) ); + + handleXGeometry(&dlg); + dlg.setCaption(title); + const int returnDialogCode = dlg.exec(); + result = edit->toPlainText(); + return (returnDialogCode == KDialog::Accepted ? 0 : 1); +} + +bool Widgets::comboBox(QWidget *parent, const QString& title, const QString& text, const QStringList& args, + const QString& defaultEntry, QString &result) +{ + KDialog dlg( parent ); + kapp->setTopWidget( &dlg ); + dlg.setCaption( title ); + dlg.setButtons( KDialog::Ok|KDialog::Cancel ); + dlg.setModal( true ); + dlg.setDefaultButton( KDialog::Ok ); + + KVBox* vbox = new KVBox( &dlg ); + dlg.setMainWidget( vbox ); + + QLabel label (vbox); + label.setText (text); + KComboBox combo (vbox); + combo.insertItems (0, args); + combo.setCurrentIndex( combo.findText( defaultEntry ) ); + combo.setFocus(); + + handleXGeometry(&dlg); + + bool retcode = (dlg.exec() == QDialog::Accepted); + + if (retcode) + result = combo.currentText(); + + return retcode; +} + +bool Widgets::listBox(QWidget *parent, const QString& title, const QString& text, const QStringList& args, + const QString& defaultEntry, QString &result) +{ + KListBoxDialog box(text,parent); + + kapp->setTopWidget( &box ); + box.setCaption(title); + + for (int i = 0; i+1<args.count(); i += 2) { + box.insertItem(args[i+1]); + } + box.setCurrentItem( defaultEntry ); + + handleXGeometry(&box); + + const bool retcode = (box.exec() == QDialog::Accepted); + if ( retcode ) + result = args[ box.currentItem()*2 ]; + return retcode; +} + + +bool Widgets::checkList(QWidget *parent, const QString& title, const QString& text, const QStringList& args, bool separateOutput, QStringList &result) +{ + QStringList entries, tags; + QString rs; + + result.clear(); + + KListBoxDialog box(text,parent); + + QListWidget &table = box.getTable(); + + kapp->setTopWidget( &box ); + box.setCaption(title); + + for (int i=0; i+2<args.count(); i += 3) { + tags.append(args[i]); + entries.append(args[i+1]); + } + + table.addItems(entries); + table.setSelectionMode(QListWidget::MultiSelection); + table.setCurrentItem(0); // This is to circumvent a Qt bug + + for (int i=0; i+2<args.count(); i += 3) { + table.item( i/3 )->setSelected( args[i+2] == QLatin1String("on") ); + } + + handleXGeometry(&box); + + const bool retcode = (box.exec() == QDialog::Accepted); + + if ( retcode ) { + if (separateOutput) { + for (int i=0; i<table.count(); i++) + if (table.item(i)->isSelected()) + result.append(tags[i]); + } else { + for (int i=0; i<table.count(); i++) + if (table.item(i)->isSelected()) + rs += QLatin1String("\"") + tags[i] + QLatin1String("\" "); + result.append(rs); + } + } + return retcode; +} + + +bool Widgets::radioBox(QWidget *parent, const QString& title, const QString& text, const QStringList& args, QString &result) +{ + QStringList entries, tags; + + KListBoxDialog box(text,parent); + + QListWidget &table = box.getTable(); + + kapp->setTopWidget( &box ); + box.setCaption(title); + + for (int i=0; i+2<args.count(); i += 3) { + tags.append(args[i]); + entries.append(args[i+1]); + } + + table.addItems(entries); + + for (int i=0; i+2<args.count(); i += 3) { + if (args[i+2] == QLatin1String("on")) { + table.setCurrentRow(i/3); + } + } + + handleXGeometry(&box); + + const bool retcode = (box.exec() == QDialog::Accepted); + if ( retcode ) + result = tags[ table.currentRow() ]; + return retcode; +} + +bool Widgets::progressBar(QWidget *parent, const QString& title, const QString& text, int totalSteps) +{ + ProgressDialog dlg( parent, title, text, totalSteps ); + kapp->setTopWidget( &dlg ); + dlg.setCaption( title ); + handleXGeometry(&dlg); + dlg.exec(); + return dlg.wasCancelled(); +} + + +bool Widgets::slider( QWidget *parent, const QString& title, const QString& text, int minValue, int maxValue, int step, int &result ) +{ + KDialog dlg( parent ); + kapp->setTopWidget( &dlg ); + dlg.setCaption( title ); + dlg.setButtons( KDialog::Ok|KDialog::Cancel ); + dlg.setModal( true ); + dlg.setDefaultButton( KDialog::Ok ); + + KVBox* vbox = new KVBox( &dlg ); + dlg.setMainWidget( vbox ); + + QLabel label (vbox); + label.setText (text); + QSlider slider (vbox); + slider.setMinimum( minValue ); + slider.setMaximum( maxValue ); + slider.setSingleStep( step ); + slider.setTickPosition ( QSlider::TicksAbove ); + slider.setOrientation( Qt::Horizontal ); + slider.setFocus(); + handleXGeometry(&dlg); + + const bool retcode = (dlg.exec() == QDialog::Accepted); + + if (retcode) + result = slider.value(); + + return retcode; +} + +bool Widgets::calendar( QWidget *parent, const QString &title, const QString &text, QDate & result ) +{ + KDialog dlg( parent ); + kapp->setTopWidget( &dlg ); + dlg.setCaption( title ); + dlg.setButtons( KDialog::Ok|KDialog::Cancel ); + dlg.setModal( true ); + dlg.setDefaultButton( KDialog::Ok ); + + KVBox* vbox = new KVBox( &dlg ); + dlg.setMainWidget( vbox ); + + QLabel label (vbox); + label.setText (text); + KDatePicker dateWidget( vbox ); + dateWidget.setFocus(); + handleXGeometry(&dlg); + + const bool retcode = (dlg.exec() == QDialog::Accepted); + + if (retcode) + result = dateWidget.date(); + + return retcode; +} + +QString Widgets::parseString(const QString &str) +{ + QString ret; + ret.reserve(str.size()); + bool escaped = false; + for (int i = 0; i < str.size(); i++) { + QChar c = str.at(i); + if (escaped) { + escaped = false; + if (c == '\\') { + ret += c; + } else if (c == 'n') { + ret += '\n'; + } else { + kWarning() << qPrintable(QString::fromLatin1("Unrecognized escape sequence \\%1").arg(c)); + ret += '\\'; + ret += c; + } + } else { + if (c == '\\') { + escaped = true; + } else { + ret += c; + } + } + } + if (escaped) { + kWarning() << "Unterminated escape sequence"; + ret += '\\'; + } + return ret; +} diff --git a/kdialog/widgets.h b/kdialog/widgets.h new file mode 100644 index 00000000..aaf944e7 --- /dev/null +++ b/kdialog/widgets.h @@ -0,0 +1,48 @@ +// +// Copyright (C) 1998 Matthias Hoelzer <hoelzer@kde.org> +// Copyright (C) 2002-2005 David Faure <faure@kde.org> +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the7 implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// + + +#ifndef WIDGETS_H +#define WIDGETS_H + +#include <QtCore/qdatetime.h> +#include <QtCore/qbytearray.h> +#include <QWidget> +#include <QtCore/QStringList> + +namespace Widgets +{ + bool inputBox(QWidget *parent, const QString& title, const QString& text, const QString& init, QString &result); + bool passwordBox(QWidget *parent, const QString& title, const QString& text, QString &result); + int textBox(QWidget *parent, int width, int height, const QString& title, const QString& file); + int textInputBox(QWidget *parent, int width, int height, const QString& title, const QString& text, const QString& init, QString &result); + bool listBox(QWidget *parent, const QString& title, const QString& text, const QStringList& args, const QString &defaultEntry, QString &result); + bool checkList(QWidget *parent, const QString& title, const QString& text, const QStringList& args, bool separateOutput, QStringList &result); + bool radioBox(QWidget *parent, const QString& title, const QString& text, const QStringList& args, QString &result); + bool comboBox(QWidget *parent, const QString& title, const QString& text, const QStringList& args, const QString& defaultEntry, QString &result); + bool progressBar(QWidget *parent, const QString& title, const QString& text, int totalSteps); + bool slider( QWidget *parent, const QString& title, const QString& test, int minValue, int maxValue, int step, int &result ); + bool calendar( QWidget *parent, const QString &title, const QString &text, QDate & result ); + + void handleXGeometry(QWidget * dlg); + + QString parseString(const QString &str); +} + +#endif diff --git a/keditbookmarks/CMakeLists.txt b/keditbookmarks/CMakeLists.txt new file mode 100644 index 00000000..a38b6aa4 --- /dev/null +++ b/keditbookmarks/CMakeLists.txt @@ -0,0 +1,82 @@ +include_directories(${CMAKE_CURRENT_BINARY_DIR}/kbookmarkmodel) + +add_subdirectory(kbookmarkmodel) + +########### next target ############### + +add_executable(kbookmarkmerger kbookmarkmerger.cpp) + +target_link_libraries(kbookmarkmerger ${KDE4_KIO_LIBS}) + +install( + TARGETS kbookmarkmerger + ${INSTALL_TARGETS_DEFAULT_ARGS} +) + + +########### next target ############### + +set(keditbookmarks_SRCS + main.cpp + toplevel.cpp + globalbookmarkmanager.cpp + actionsimpl.cpp + importers.cpp + bookmarkiterator.cpp + testlink.cpp + favicons.cpp + faviconupdater.cpp + exporters.cpp + bookmarkinfowidget.cpp + kebsearchline.cpp + bookmarklistview.cpp +) + +qt4_generate_dbus_interface( + ${CMAKE_CURRENT_SOURCE_DIR}/toplevel.h + org.kde.keditbookmarks.xml +) + +qt4_add_dbus_interface(keditbookmarks_SRCS + ${CMAKE_CURRENT_BINARY_DIR}/org.kde.keditbookmarks.xml + toplevel_interface +) + +qt4_add_dbus_interface(keditbookmarks_SRCS + ${CMAKE_SOURCE_DIR}/libs/konq/favicons/org.kde.FavIcon.xml + favicon_interface +) + +kde4_add_kcfg_files(keditbookmarks_SRCS settings.kcfgc) + +add_executable(keditbookmarks ${keditbookmarks_SRCS}) + +target_link_libraries(keditbookmarks + ${KDE4_KPARTS_LIBS} + kbookmarkmodel_private + konq +) + +install( + TARGETS keditbookmarks + ${INSTALL_TARGETS_DEFAULT_ARGS} +) + +########### install files ############### + +install( + FILES keditbookmarks.kcfg + DESTINATION ${KDE4_KCFG_INSTALL_DIR} +) + +install( + FILES + keditbookmarksui.rc + keditbookmarks-genui.rc + DESTINATION ${KDE4_DATA_INSTALL_DIR}/keditbookmarks +) + +install( + PROGRAMS keditbookmarks.desktop + DESTINATION ${KDE4_XDG_APPS_INSTALL_DIR} +) diff --git a/keditbookmarks/DESIGN b/keditbookmarks/DESIGN new file mode 100644 index 00000000..9ef300f8 --- /dev/null +++ b/keditbookmarks/DESIGN @@ -0,0 +1,20 @@ +four main layers: + toplevel : startup, initialisation + listview : listview, selection, action forwarding + commands : bookmark undo/redo mechanism implementation + actionsimpl : the actual slots, almost all of 'em + +various other thingies: + search : incremental search implementation + favicons : iterating action implementation using bookmarkiterator + importers : forwarders to kio/bookmarks code + exporters : forwarders to kio/bookmarks code, and html export code + dcop : dcop handling, internal interface + bookmarkiterator : is a baseclass for iterating actions, of sorts... + updater : favicon updating base stuff - kio/khtml crap + testlink : link testing stuff + +3 different selection styles: + bookmark iterators (ITR_ACTION) + single bookmark (ITEM_ACTION) + normal selection (SELC_ACTION) diff --git a/keditbookmarks/Messages.sh b/keditbookmarks/Messages.sh new file mode 100755 index 00000000..04bc311d --- /dev/null +++ b/keditbookmarks/Messages.sh @@ -0,0 +1,4 @@ +#! /usr/bin/env bash +$EXTRACTRC `find . -name \*.rc` >> rc.cpp || exit 11 +$XGETTEXT -kaliasLocal `find . -name \*.cc -o -name \*.cpp -o -name \*.h` -o $podir/keditbookmarks.pot +rm -f rc.cpp diff --git a/keditbookmarks/TODO b/keditbookmarks/TODO new file mode 100644 index 00000000..dddc4350 --- /dev/null +++ b/keditbookmarks/TODO @@ -0,0 +1,85 @@ +toolbar is not updating on save in editor +editor does not have undo after save +deleting folders from the quick actions menu is b0rked + +(#1) +68161 : Favicons are lost on upgrade + - nothing to do with bookmarks, but, no idea where to put it, + so, whatever, i'll handle it anyways... + + - problem: .kde/share/cache/favicons moved to .kde/`hostname`-cache/favicons + + - fix: either 1) a symlink, 2) upgrade path + +67635 : Bookmarks toolbar edits don't take effect until restart + - completely unable to confirm, and can't recall fixing this in the past.. umm.. + +67614 : abort bookmark's context menu causes bookmark to load + - can't reproduce... maybe a new / old qt bug... + +67686 : bookmark bar first displayed empty + - can't reproduce, maybe need to check from an empty .kde, thus no previous + +67958 : bookmark toolbar intermittenly disappears + - maybe just another dup of tokoe's bug + +67685 : crash often kills bookmarks AND backup + - how the heck does a crash wipe the bookmarks.xml in the first place? + well, whatever. i admit this can be improved, but only through new i18n's + so thats not going to happen until i get permission for 3.2.1... + +somehow multi selected deletes are broken again: + no longer crashes, but doesn't select current well + TODO note added to program, quite a difficult problem... +a selected folder only takes one click to get a rename??? +tbcache: don't create a tbcache file when its gonna be empty in any case!!! +historymgr: get dirtyness working for the kbookmarkmap +proper readonly support - rmb should work but things should be disabled? no addbookmark, etc. +general xbel bug: http://bugzilla.gnome.org/show_bug.cgi?id=69702 + +possible qt bug: + multi selection deletion then shift down selects roots + valgrind doesn't show anything either + logging all the setSelected calls i can see doesn't help + +STUFF FROM BEINERI: + + And the "Cancel" entries in the "Bookmark" menus looks strange too. How about + a KMail-like progress bar (in a status bar?) with an "X" button to stop them? + And "Advanced Add Bookmark" doesn't work for "Add Bookmark" in context menu? + +--- + + Btw, I think when you have no tab and call "Bookmarks Tabs as Folder..." + it should not only create a folder but bookmark the current page within + the folder unconditional if it's a tab or not. :-) + +DONE TESTING: +------------ + + favicon and status check updates: + removal of items while checks are happening + program exit with checks and updates are still going on + random deletion of all items - TODO + +THINGS FOR 3.3+ +------------------ + +tools: + recursive 404 finder + duplicate finder + +basic gui: + split view stuff - including filtered views + need to get keyboard shortcut changes working somehow (wheels) + have to get dynamic filtering (e.g sorting, pruning), + +main bookmark config: + make the keditbookmarks prefs persistant + and make a simple include selector using + for example kautoconfig, including a + simple dynamic menu config gui + +bookmarklets: add some examples! + +tips dialog - fixes bugs - shortcuts for keyboard selection are wierd diff --git a/keditbookmarks/actionsimpl.cpp b/keditbookmarks/actionsimpl.cpp new file mode 100644 index 00000000..1f464245 --- /dev/null +++ b/keditbookmarks/actionsimpl.cpp @@ -0,0 +1,594 @@ +// -*- indent-tabs-mode:nil -*- +// vim: set ts=4 sts=4 sw=4 et: +/* This file is part of the KDE project + Copyright (C) 2000 David Faure <faure@kde.org> + Copyright (C) 2002-2003 Alexander Kellett <lypanov@kde.org> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License version 2 as published by the Free Software Foundation. + + 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; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "actionsimpl.h" +#include "globalbookmarkmanager.h" + +#include "toplevel.h" // for KEBApp +#include "kbookmarkmodel/model.h" +#include "kbookmarkmodel/commands.h" +#include "kbookmarkmodel/commandhistory.h" +#include "importers.h" +#include "favicons.h" +#include "testlink.h" +#include "exporters.h" +#include "bookmarkinfowidget.h" + +#include <stdlib.h> + +#include <QClipboard> +#include <QtCore/QMimeData> +#include <QApplication> + +#include <kdebug.h> + +#include <kaction.h> +#include <kactioncollection.h> +#include <kicon.h> +#include <kicondialog.h> +#include <kiconloader.h> +#include <kinputdialog.h> +#include <klocale.h> +#include <kstandardaction.h> +#include <kfiledialog.h> +#include <krun.h> +#include <kstandarddirs.h> + +#include <kbookmark.h> +#include <kbookmarkmanager.h> +#include <kbookmarkimporter.h> + +#include <kbookmarkimporter_ie.h> +#include <kbookmarkimporter_opera.h> +#include <kbookmarkimporter_ns.h> +#include <kbookmarkexporter.h> + +// decoupled from resetActions in toplevel.cpp +// as resetActions simply uses the action groups +// specified in the ui.rc file +void KEBApp::createActions() { + + m_actionsImpl = new ActionsImpl(this, GlobalBookmarkManager::self()->model()); + + connect(m_actionsImpl->testLinkHolder(), SIGNAL(setCancelEnabled(bool)), + this, SLOT(setCancelTestsEnabled(bool))); + connect(m_actionsImpl->favIconHolder(), SIGNAL(setCancelEnabled(bool)), + this, SLOT(setCancelFavIconUpdatesEnabled(bool))); + + // save and quit should probably not be in the toplevel??? + (void) KStandardAction::quit( + this, SLOT(close()), actionCollection()); + KStandardAction::keyBindings(guiFactory(), SLOT(configureShortcuts()), actionCollection()); + (void) KStandardAction::configureToolbars( + this, SLOT(slotConfigureToolbars()), actionCollection()); + + if (m_browser) { + (void) KStandardAction::open( + m_actionsImpl, SLOT(slotLoad()), actionCollection()); + (void) KStandardAction::saveAs( + m_actionsImpl, SLOT(slotSaveAs()), actionCollection()); + } + + (void) KStandardAction::cut(m_actionsImpl, SLOT(slotCut()), actionCollection()); + (void) KStandardAction::copy(m_actionsImpl, SLOT(slotCopy()), actionCollection()); + (void) KStandardAction::paste(m_actionsImpl, SLOT(slotPaste()), actionCollection()); + + // actions + KAction* m_actionsImplDelete = actionCollection()->addAction("delete"); + m_actionsImplDelete->setIcon(KIcon("edit-delete")); + m_actionsImplDelete->setText(i18n("&Delete")); + m_actionsImplDelete->setShortcut(Qt::Key_Delete); + connect(m_actionsImplDelete, SIGNAL(triggered()), m_actionsImpl, SLOT(slotDelete())); + + KAction* m_actionsImplRename = actionCollection()->addAction("rename"); + m_actionsImplRename->setIcon(KIcon("edit-rename")); + m_actionsImplRename->setText(i18n("Rename")); + m_actionsImplRename->setShortcut(Qt::Key_F2); + connect(m_actionsImplRename, SIGNAL(triggered()), m_actionsImpl, SLOT(slotRename())); + + KAction* m_actionsImplChangeURL = actionCollection()->addAction("changeurl"); + m_actionsImplChangeURL->setIcon(KIcon("edit-rename")); + m_actionsImplChangeURL->setText(i18n("C&hange Location")); + m_actionsImplChangeURL->setShortcut(Qt::Key_F3); + connect(m_actionsImplChangeURL, SIGNAL(triggered()), m_actionsImpl, SLOT(slotChangeURL())); + + KAction* m_actionsImplChangeComment = actionCollection()->addAction("changecomment"); + m_actionsImplChangeComment->setIcon(KIcon("edit-rename")); + m_actionsImplChangeComment->setText(i18n("C&hange Comment")); + m_actionsImplChangeComment->setShortcut(Qt::Key_F4); + connect(m_actionsImplChangeComment, SIGNAL(triggered()), m_actionsImpl, SLOT(slotChangeComment())); + + KAction* m_actionsImplChangeIcon = actionCollection()->addAction("changeicon"); + m_actionsImplChangeIcon->setIcon(KIcon("preferences-desktop-icons")); + m_actionsImplChangeIcon->setText(i18n("Chan&ge Icon...")); + connect(m_actionsImplChangeIcon, SIGNAL(triggered()), m_actionsImpl, SLOT(slotChangeIcon())); + + KAction* m_actionsImplUpdateFavIcon = actionCollection()->addAction("updatefavicon"); + m_actionsImplUpdateFavIcon->setText(i18n("Update Favicon")); + connect(m_actionsImplUpdateFavIcon, SIGNAL(triggered()), m_actionsImpl, SLOT(slotUpdateFavIcon())); + + KAction* m_actionsImplRecursiveSort = actionCollection()->addAction("recursivesort"); + m_actionsImplRecursiveSort->setText(i18n("Recursive Sort")); + connect(m_actionsImplRecursiveSort, SIGNAL(triggered()), m_actionsImpl, SLOT(slotRecursiveSort())); + + KAction* m_actionsImplNewFolder = actionCollection()->addAction("newfolder"); + m_actionsImplNewFolder->setIcon(KIcon("folder-new")); + m_actionsImplNewFolder->setText(i18n("&New Folder...")); + m_actionsImplNewFolder->setShortcut(Qt::CTRL+Qt::Key_N); + connect(m_actionsImplNewFolder, SIGNAL(triggered()), m_actionsImpl, SLOT(slotNewFolder())); + + KAction* m_actionsImplNewBookmark = actionCollection()->addAction("newbookmark"); + m_actionsImplNewBookmark->setIcon(KIcon("bookmark-new")); + m_actionsImplNewBookmark->setText(i18n("&New Bookmark")); + connect(m_actionsImplNewBookmark, SIGNAL(triggered()), m_actionsImpl, SLOT(slotNewBookmark())); + + KAction* m_actionsImplInsertSeparator = actionCollection()->addAction("insertseparator"); + m_actionsImplInsertSeparator->setText(i18n("&Insert Separator")); + m_actionsImplInsertSeparator->setShortcut(Qt::CTRL+Qt::Key_I); + connect(m_actionsImplInsertSeparator, SIGNAL(triggered()), m_actionsImpl, SLOT(slotInsertSeparator())); + + KAction* m_actionsImplSort = actionCollection()->addAction("sort"); + m_actionsImplSort->setText(i18n("&Sort Alphabetically")); + connect(m_actionsImplSort, SIGNAL(triggered()), m_actionsImpl, SLOT(slotSort())); + + KAction* m_actionsImplSetAsToolbar = actionCollection()->addAction("setastoolbar"); + m_actionsImplSetAsToolbar->setIcon(KIcon("bookmark-toolbar")); + m_actionsImplSetAsToolbar->setText(i18n("Set as T&oolbar Folder")); + connect(m_actionsImplSetAsToolbar, SIGNAL(triggered()), m_actionsImpl, SLOT(slotSetAsToolbar())); + + KAction* m_actionsImplExpandAll = actionCollection()->addAction("expandall"); + m_actionsImplExpandAll->setText(i18n("&Expand All Folders")); + connect(m_actionsImplExpandAll, SIGNAL(triggered()), m_actionsImpl, SLOT(slotExpandAll())); + + KAction* m_actionsImplCollapseAll = actionCollection()->addAction("collapseall"); + m_actionsImplCollapseAll->setText(i18n("Collapse &All Folders")); + connect(m_actionsImplCollapseAll, SIGNAL(triggered()), m_actionsImpl, SLOT(slotCollapseAll())); + + KAction* m_actionsImplOpenLink = actionCollection()->addAction("openlink"); + m_actionsImplOpenLink->setIcon(KIcon("document-open")); + m_actionsImplOpenLink->setText(i18n("&Open in Konqueror")); + connect(m_actionsImplOpenLink, SIGNAL(triggered()), m_actionsImpl, SLOT(slotOpenLink())); + + KAction* m_actionsImplTestSelection = actionCollection()->addAction("testlink"); + m_actionsImplTestSelection->setIcon(KIcon("bookmarks")); + m_actionsImplTestSelection->setText(i18n("Check &Status")); + connect(m_actionsImplTestSelection, SIGNAL(triggered()), m_actionsImpl, SLOT(slotTestSelection())); + + KAction* m_actionsImplTestAll = actionCollection()->addAction("testall"); + m_actionsImplTestAll->setText(i18n("Check Status: &All")); + connect(m_actionsImplTestAll, SIGNAL(triggered()), m_actionsImpl, SLOT(slotTestAll())); + + KAction* m_actionsImplUpdateAllFavIcons = actionCollection()->addAction("updateallfavicons"); + m_actionsImplUpdateAllFavIcons->setText(i18n("Update All &Favicons")); + connect(m_actionsImplUpdateAllFavIcons, SIGNAL(triggered()), m_actionsImpl, SLOT(slotUpdateAllFavIcons())); + + KAction* m_actionsImplCancelAllTests = actionCollection()->addAction("canceltests"); + m_actionsImplCancelAllTests->setText(i18n("Cancel &Checks")); + connect(m_actionsImplCancelAllTests, SIGNAL(triggered()), m_actionsImpl, SLOT(slotCancelAllTests())); + + KAction* m_actionsImplCancelFavIconUpdates = actionCollection()->addAction("cancelfaviconupdates"); + m_actionsImplCancelFavIconUpdates->setText(i18n("Cancel &Favicon Updates")); + connect(m_actionsImplCancelFavIconUpdates, SIGNAL(triggered()), m_actionsImpl, SLOT(slotCancelFavIconUpdates())); + + KAction* m_actionsImplImportNS = actionCollection()->addAction("importNS"); + m_actionsImplImportNS->setObjectName( QLatin1String("NS" )); + m_actionsImplImportNS->setIcon(KIcon("netscape")); + m_actionsImplImportNS->setText(i18n("Import &Netscape Bookmarks...")); + connect(m_actionsImplImportNS, SIGNAL(triggered()), m_actionsImpl, SLOT(slotImport())); + + KAction* m_actionsImplImportOpera = actionCollection()->addAction("importOpera"); + m_actionsImplImportOpera->setObjectName( QLatin1String("Opera" )); + m_actionsImplImportOpera->setIcon(KIcon("opera")); + m_actionsImplImportOpera->setText(i18n("Import &Opera Bookmarks...")); + connect(m_actionsImplImportOpera, SIGNAL(triggered()), m_actionsImpl, SLOT(slotImport())); +/* + KAction* m_actionsImplImportCrashes = actionCollection()->addAction("importCrashes"); + m_actionsImplImportCrashes->setObjectName( QLatin1String("Crashes" )); + m_actionsImplImportCrashes->setText(i18n("Import All &Crash Sessions as Bookmarks...")); + connect(m_actionsImplImportCrashes, SIGNAL(triggered()), m_actionsImpl, SLOT(slotImport())); +*/ + KAction* m_actionsImplImportGaleon = actionCollection()->addAction("importGaleon"); + m_actionsImplImportGaleon->setObjectName( QLatin1String("Galeon" )); + m_actionsImplImportGaleon->setText(i18n("Import &Galeon Bookmarks...")); + connect(m_actionsImplImportGaleon, SIGNAL(triggered()), m_actionsImpl, SLOT(slotImport())); + + KAction* m_actionsImplImportKDE2 = actionCollection()->addAction("importKDE2"); + m_actionsImplImportKDE2->setObjectName( QLatin1String("KDE2" )); + m_actionsImplImportKDE2->setIcon(KIcon("kde")); + m_actionsImplImportKDE2->setText(i18n("Import &KDE 2 or KDE 3 Bookmarks...")); + + connect(m_actionsImplImportKDE2, SIGNAL(triggered()), m_actionsImpl, SLOT(slotImport())); + + KAction* m_actionsImplImportIE = actionCollection()->addAction("importIE"); + m_actionsImplImportIE->setObjectName( QLatin1String("IE" )); + m_actionsImplImportIE->setText(i18n("Import &Internet Explorer Bookmarks...")); + connect(m_actionsImplImportIE, SIGNAL(triggered()), m_actionsImpl, SLOT(slotImport())); + + KAction* m_actionsImplImportMoz = actionCollection()->addAction("importMoz"); + m_actionsImplImportMoz->setObjectName( QLatin1String("Moz" )); + m_actionsImplImportMoz->setIcon(KIcon("mozilla")); + m_actionsImplImportMoz->setText(i18n("Import &Mozilla Bookmarks...")); + connect(m_actionsImplImportMoz, SIGNAL(triggered()), m_actionsImpl, SLOT(slotImport())); + + KAction* m_actionsImplExportNS = actionCollection()->addAction("exportNS"); + m_actionsImplExportNS->setIcon(KIcon("netscape")); + m_actionsImplExportNS->setText(i18n("Export &Netscape Bookmarks")); + connect(m_actionsImplExportNS, SIGNAL(triggered()), m_actionsImpl, SLOT(slotExportNS())); + + KAction* m_actionsImplExportOpera = actionCollection()->addAction("exportOpera"); + m_actionsImplExportOpera->setIcon(KIcon("opera")); + m_actionsImplExportOpera->setText(i18n("Export &Opera Bookmarks...")); + connect(m_actionsImplExportOpera, SIGNAL(triggered()), m_actionsImpl, SLOT(slotExportOpera())); + + KAction* m_actionsImplExportHTML = actionCollection()->addAction("exportHTML"); + m_actionsImplExportHTML->setIcon(KIcon("text-html")); + m_actionsImplExportHTML->setText(i18n("Export &HTML Bookmarks...")); + connect(m_actionsImplExportHTML, SIGNAL(triggered()), m_actionsImpl, SLOT(slotExportHTML())); + + KAction* m_actionsImplExportIE = actionCollection()->addAction("exportIE"); + m_actionsImplExportIE->setText(i18n("Export &Internet Explorer Bookmarks...")); + connect(m_actionsImplExportIE, SIGNAL(triggered()), m_actionsImpl, SLOT(slotExportIE())); + + KAction* m_actionsImplExportMoz = actionCollection()->addAction("exportMoz"); + m_actionsImplExportMoz->setIcon(KIcon("mozilla")); + m_actionsImplExportMoz->setText(i18n("Export &Mozilla Bookmarks...")); + connect(m_actionsImplExportMoz, SIGNAL(triggered()), m_actionsImpl, SLOT(slotExportMoz())); +} + +ActionsImpl::ActionsImpl(QObject* parent, KBookmarkModel* model) + : QObject(parent), m_model(model), + m_testLinkHolder(new TestLinkItrHolder(this, model)), + m_favIconHolder(new FavIconsItrHolder(this, model)) +{ + Q_ASSERT(m_model); +} + +void ActionsImpl::slotLoad() +{ + QString bookmarksFile + = KFileDialog::getOpenFileName(QString(), "*.xml", KEBApp::self()); + if (bookmarksFile.isNull()) + return; + KEBApp::self()->reset(QString(), bookmarksFile); +} + +void ActionsImpl::slotSaveAs() { + KEBApp::self()->bkInfo()->commitChanges(); + QString saveFilename + = KFileDialog::getSaveFileName(QString(), "*.xml", KEBApp::self()); + if (!saveFilename.isEmpty()) + GlobalBookmarkManager::self()->saveAs(saveFilename); +} + +void GlobalBookmarkManager::doExport(ExportType type, const QString & _path) { + //it can be null when we use command line to export => there is not interface + if ( KEBApp::self() && KEBApp::self()->bkInfo() ) + KEBApp::self()->bkInfo()->commitChanges(); + QString path(_path); + // TODO - add a factory and make all this use the base class + if (type == OperaExport) { + if (path.isNull()) + path = KOperaBookmarkImporterImpl().findDefaultLocation(true); + KOperaBookmarkExporterImpl exporter(mgr(), path); + exporter.write(mgr()->root()); + return; + + } else if (type == HTMLExport) { + if (path.isNull()) + path = KFileDialog::getSaveFileName( + QDir::homePath(), + i18n("*.html|HTML Bookmark Listing"), + KEBApp::self() ); + HTMLExporter exporter; + exporter.write(mgr()->root(), path); + return; + + } else if (type == IEExport) { + if (path.isNull()) + path = KIEBookmarkImporterImpl().findDefaultLocation(true); + KIEBookmarkExporterImpl exporter(mgr(), path); + exporter.write(mgr()->root()); + return; + } + + bool moz = (type == MozillaExport); + + if (path.isNull()) { + if (moz) { + KMozillaBookmarkImporterImpl importer; + path = importer.findDefaultLocation(true); + } + else { + KNSBookmarkImporterImpl importer; + path = importer.findDefaultLocation(true); + } + } + + if (!path.isEmpty()) { + KNSBookmarkExporterImpl exporter(mgr(), path); + exporter.write(mgr()->root()); + } +} + +void KEBApp::setCancelFavIconUpdatesEnabled(bool enabled) { + actionCollection()->action("cancelfaviconupdates")->setEnabled(enabled); +} + +void KEBApp::setCancelTestsEnabled(bool enabled) { + actionCollection()->action("canceltests")->setEnabled(enabled); +} + +void ActionsImpl::slotCut() { + KEBApp::self()->bkInfo()->commitChanges(); + slotCopy(); + DeleteManyCommand *mcmd = new DeleteManyCommand(m_model, i18nc("(qtundo-format)", "Cut Items"), KEBApp::self()->selectedBookmarks() ); + commandHistory()->addCommand(mcmd); + +} + +void ActionsImpl::slotCopy() +{ + KEBApp::self()->bkInfo()->commitChanges(); + // this is not a command, because it can't be undone + KBookmark::List bookmarks = KEBApp::self()->selectedBookmarksExpanded(); + QMimeData *mimeData = new QMimeData; + bookmarks.populateMimeData(mimeData); + QApplication::clipboard()->setMimeData( mimeData ); +} + +void ActionsImpl::slotPaste() { + KEBApp::self()->bkInfo()->commitChanges(); + + QString addr; + KBookmark bk = KEBApp::self()->firstSelected(); + if(bk.isGroup()) + addr = bk.address() + "/0"; //FIXME internal + else + addr = bk.address(); + + QUndoCommand *mcmd = CmdGen::insertMimeSource( m_model, i18nc("(qtundo-format)", "Paste"), QApplication::clipboard()->mimeData(), addr); + commandHistory()->addCommand(mcmd); +} + +/* -------------------------------------- */ + +void ActionsImpl::slotNewFolder() +{ + KEBApp::self()->bkInfo()->commitChanges(); + bool ok; + QString str = KInputDialog::getText( i18nc( "@title:window", "Create New Bookmark Folder" ), + i18n( "New folder:" ), QString(), &ok, KEBApp::self() ); + if (!ok) + return; + + CreateCommand *cmd = new CreateCommand(m_model, + KEBApp::self()->insertAddress(), + str, "bookmark_folder", /*open*/ true); + commandHistory()->addCommand(cmd); +} + +void ActionsImpl::slotNewBookmark() +{ + KEBApp::self()->bkInfo()->commitChanges(); + // TODO - make a setCurrentItem(Command *) which uses finaladdress interface + CreateCommand * cmd = new CreateCommand(m_model, + KEBApp::self()->insertAddress(), + QString(), "www", KUrl("http://")); + commandHistory()->addCommand(cmd); +} + +void ActionsImpl::slotInsertSeparator() +{ + KEBApp::self()->bkInfo()->commitChanges(); + CreateCommand * cmd = new CreateCommand(m_model, KEBApp::self()->insertAddress()); + commandHistory()->addCommand(cmd); +} + +void ActionsImpl::slotImport() { + KEBApp::self()->bkInfo()->commitChanges(); + qDebug() << "ActionsImpl::slotImport() where sender()->name() == " + << sender()->objectName() << endl; + ImportCommand* import + = ImportCommand::performImport(m_model, sender()->objectName(), KEBApp::self()); + if (!import) + return; + commandHistory()->addCommand(import); + //FIXME select import->groupAddress +} + +// TODO - this is getting ugly and repetitive. cleanup! + +void ActionsImpl::slotExportOpera() { + KEBApp::self()->bkInfo()->commitChanges(); + GlobalBookmarkManager::self()->doExport(GlobalBookmarkManager::OperaExport); } +void ActionsImpl::slotExportHTML() { + KEBApp::self()->bkInfo()->commitChanges(); + GlobalBookmarkManager::self()->doExport(GlobalBookmarkManager::HTMLExport); } +void ActionsImpl::slotExportIE() { + KEBApp::self()->bkInfo()->commitChanges(); + GlobalBookmarkManager::self()->doExport(GlobalBookmarkManager::IEExport); } +void ActionsImpl::slotExportNS() { + KEBApp::self()->bkInfo()->commitChanges(); + GlobalBookmarkManager::self()->doExport(GlobalBookmarkManager::NetscapeExport); } +void ActionsImpl::slotExportMoz() { + KEBApp::self()->bkInfo()->commitChanges(); + GlobalBookmarkManager::self()->doExport(GlobalBookmarkManager::MozillaExport); } + +/* -------------------------------------- */ + +void ActionsImpl::slotCancelFavIconUpdates() { + m_favIconHolder->cancelAllItrs(); +} + +void ActionsImpl::slotCancelAllTests() { + m_testLinkHolder->cancelAllItrs(); +} + +void ActionsImpl::slotTestAll() { + m_testLinkHolder->insertIterator( + new TestLinkItr(m_testLinkHolder, KEBApp::self()->allBookmarks())); +} + +void ActionsImpl::slotUpdateAllFavIcons() { + m_favIconHolder->insertIterator( + new FavIconsItr(m_favIconHolder, KEBApp::self()->allBookmarks())); +} + +ActionsImpl::~ActionsImpl() { + delete m_favIconHolder; + delete m_testLinkHolder; +} + +/* -------------------------------------- */ + +void ActionsImpl::slotTestSelection() { + KEBApp::self()->bkInfo()->commitChanges(); + m_testLinkHolder->insertIterator(new TestLinkItr(m_testLinkHolder, KEBApp::self()->selectedBookmarksExpanded())); +} + +void ActionsImpl::slotUpdateFavIcon() { + KEBApp::self()->bkInfo()->commitChanges(); + m_favIconHolder->insertIterator(new FavIconsItr(m_favIconHolder, KEBApp::self()->selectedBookmarksExpanded())); +} + +/* -------------------------------------- */ + +class KBookmarkGroupList : private KBookmarkGroupTraverser { +public: + KBookmarkGroupList(KBookmarkManager *); + QList<KBookmark> getList(const KBookmarkGroup &); +private: + virtual void visit(const KBookmark &) {} + virtual void visitEnter(const KBookmarkGroup &); + virtual void visitLeave(const KBookmarkGroup &) {} +private: + KBookmarkManager *m_manager; + QList<KBookmark> m_list; +}; + +KBookmarkGroupList::KBookmarkGroupList( KBookmarkManager *manager ) { + m_manager = manager; +} + +QList<KBookmark> KBookmarkGroupList::getList( const KBookmarkGroup &grp ) { + traverse(grp); + return m_list; +} + +void KBookmarkGroupList::visitEnter(const KBookmarkGroup &grp) { + m_list << grp; +} + +void ActionsImpl::slotRecursiveSort() { + KEBApp::self()->bkInfo()->commitChanges(); + KBookmark bk = KEBApp::self()->firstSelected(); + Q_ASSERT(bk.isGroup()); + KEBMacroCommand *mcmd = new KEBMacroCommand(i18nc("(qtundo-format)", "Recursive Sort")); + KBookmarkGroupList lister(GlobalBookmarkManager::self()->mgr()); + QList<KBookmark> bookmarks = lister.getList(bk.toGroup()); + bookmarks << bk.toGroup(); + for (QList<KBookmark>::ConstIterator it = bookmarks.constBegin(); it != bookmarks.constEnd(); ++it) { + new SortCommand(m_model, "", (*it).address(), mcmd); + } + commandHistory()->addCommand(mcmd); +} + +void ActionsImpl::slotSort() { + KEBApp::self()->bkInfo()->commitChanges(); + KBookmark bk = KEBApp::self()->firstSelected(); + Q_ASSERT(bk.isGroup()); + SortCommand *cmd = new SortCommand(m_model, i18nc("(qtundo-format)", "Sort Alphabetically"), bk.address()); + commandHistory()->addCommand(cmd); +} + +/* -------------------------------------- */ + +void ActionsImpl::slotDelete() { + KEBApp::self()->bkInfo()->commitChanges(); + DeleteManyCommand *mcmd = new DeleteManyCommand(m_model, i18nc("(qtundo-format)", "Delete Items"), KEBApp::self()->selectedBookmarks()); + commandHistory()->addCommand(mcmd); +} + +void ActionsImpl::slotOpenLink() +{ + KEBApp::self()->bkInfo()->commitChanges(); + QList<KBookmark> bookmarks = KEBApp::self()->selectedBookmarksExpanded(); + QList<KBookmark>::const_iterator it, end; + end = bookmarks.constEnd(); + for (it = bookmarks.constBegin(); it != end; ++it) { + if ((*it).isGroup() || (*it).isSeparator()) + continue; + (void)new KRun((*it).url(), KEBApp::self()); + } +} + +/* -------------------------------------- */ + +void ActionsImpl::slotRename() { + KEBApp::self()->bkInfo()->commitChanges(); + KEBApp::self()->startEdit( KEBApp::NameColumn ); +} + +void ActionsImpl::slotChangeURL() { + KEBApp::self()->bkInfo()->commitChanges(); + KEBApp::self()->startEdit( KEBApp::UrlColumn ); +} + +void ActionsImpl::slotChangeComment() { + KEBApp::self()->bkInfo()->commitChanges(); + KEBApp::self()->startEdit( KEBApp::CommentColumn ); +} + +void ActionsImpl::slotSetAsToolbar() { + KEBApp::self()->bkInfo()->commitChanges(); + KBookmark bk = KEBApp::self()->firstSelected(); + Q_ASSERT(bk.isGroup()); + QUndoCommand *mcmd = CmdGen::setAsToolbar(m_model, bk); + commandHistory()->addCommand(mcmd); +} + +void ActionsImpl::slotChangeIcon() { + KEBApp::self()->bkInfo()->commitChanges(); + KBookmark bk = KEBApp::self()->firstSelected(); + const QString newIcon = KIconDialog::getIcon(KIconLoader::Small, KIconLoader::Place, false, 0, false, KEBApp::self()); + if (newIcon.isEmpty()) + return; + EditCommand *cmd = new EditCommand(m_model, bk.address(), -1, newIcon); + + commandHistory()->addCommand(cmd); +} + +void ActionsImpl::slotExpandAll() +{ + KEBApp::self()->expandAll(); +} + +void ActionsImpl::slotCollapseAll() +{ + KEBApp::self()->collapseAll(); +} + +CommandHistory* ActionsImpl::commandHistory() +{ + return m_model->commandHistory(); +} + +#include "moc_actionsimpl.cpp" diff --git a/keditbookmarks/actionsimpl.h b/keditbookmarks/actionsimpl.h new file mode 100644 index 00000000..f46bc3d1 --- /dev/null +++ b/keditbookmarks/actionsimpl.h @@ -0,0 +1,81 @@ +// vim: set ts=4 sts=4 sw=4 et: +/* This file is part of the KDE project + Copyright (C) 2003 Alexander Kellett <lypanov@kde.org> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License version 2 or at your option version 3 as published by + the Free Software Foundation. + + 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; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef ACTIONSIMPL_H +#define ACTIONSIMPL_H + +#include <QtCore/QObject> +class FavIconsItrHolder; +class TestLinkItrHolder; +class CommandHistory; +class KBookmarkModel; + +class ActionsImpl : public QObject +{ + Q_OBJECT + +public: + ActionsImpl(QObject* parent, KBookmarkModel* model); + ~ActionsImpl(); + bool save(); + + TestLinkItrHolder* testLinkHolder() { return m_testLinkHolder; } + FavIconsItrHolder* favIconHolder() { return m_favIconHolder; } + +public Q_SLOTS: + void slotLoad(); + void slotSaveAs(); + void slotCut(); + void slotCopy(); + void slotPaste(); + void slotRename(); + void slotChangeURL(); + void slotChangeComment(); + void slotChangeIcon(); + void slotDelete(); + void slotNewFolder(); + void slotNewBookmark(); + void slotInsertSeparator(); + void slotSort(); + void slotSetAsToolbar(); + void slotOpenLink(); + void slotTestSelection(); + void slotTestAll(); + void slotCancelAllTests(); + void slotUpdateFavIcon(); + void slotRecursiveSort(); + void slotUpdateAllFavIcons(); + void slotCancelFavIconUpdates(); + void slotExpandAll(); + void slotCollapseAll(); + void slotImport(); + void slotExportOpera(); + void slotExportHTML(); + void slotExportIE(); + void slotExportNS(); + void slotExportMoz(); +private: + CommandHistory* commandHistory(); + KBookmarkModel* m_model; + TestLinkItrHolder* m_testLinkHolder; + FavIconsItrHolder* m_favIconHolder; +}; + +#endif diff --git a/keditbookmarks/bookmarkinfowidget.cpp b/keditbookmarks/bookmarkinfowidget.cpp new file mode 100644 index 00000000..e219883e --- /dev/null +++ b/keditbookmarks/bookmarkinfowidget.cpp @@ -0,0 +1,275 @@ +/* This file is part of the KDE project + Copyright (C) 2003 Alexander Kellett <lypanov@kde.org> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License version 2 as published by the Free Software Foundation. + + 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; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "bookmarkinfowidget.h" +#include "bookmarklistview.h" +#include "kbookmarkmodel/commands.h" +#include "kbookmarkmodel/commandhistory.h" +#include "globalbookmarkmanager.h" +#include "kbookmarkmodel/model.h" + +#include <stdlib.h> + +#include <QtCore/QTimer> +#include <QLabel> +#include <QHBoxLayout> +#include <QFormLayout> + +#include <klocale.h> +#include <kdebug.h> + +#include <klineedit.h> + +#include <kbookmark.h> + +// SHUFFLE all these functions around, the order is just plain stupid +void BookmarkInfoWidget::showBookmark(const KBookmark &bk) +{ + // Fast exit if already shown, otherwise editing a title leads to a command after each keypress + if (m_bk == bk) + return; + + commitChanges(); + m_bk = bk; + + if (m_bk.isNull()) { + // all read only and blank + + m_title_le->setReadOnly(true); + m_title_le->setText(QString()); + + m_url_le->setReadOnly(true); + m_url_le->setText(QString()); + + m_comment_le->setReadOnly(true); + m_comment_le->setText(QString()); + + m_visitdate_le->setReadOnly(true); + m_visitdate_le->setText(QString()); + + m_credate_le->setReadOnly(true); + m_credate_le->setText(QString()); + + m_visitcount_le->setReadOnly(true); + m_visitcount_le->setText(QString()); + + return; + } + + // read/write fields + m_title_le->setReadOnly( (bk.isSeparator()|| !bk.hasParent() )? true : false); + if (bk.fullText() != m_title_le->text()) + m_title_le->setText(bk.fullText()); + + m_url_le->setReadOnly(bk.isGroup() || bk.isSeparator()); + if (bk.isGroup()) { + m_url_le->setText(QString()); + } + else { + // Update the text if and only if the text represents a different URL to that + // of the current bookmark - the old method, "m_url_le->text() != bk.url().pathOrUrl()", + // created difficulties due to the ambiguity of converting URLs to text. (#172647) + if (KUrl(m_url_le->text()) != bk.url()) { + const int cursorPosition = m_url_le->cursorPosition(); + m_url_le->setText(bk.url().pathOrUrl()); + m_url_le->setCursorPosition(cursorPosition); + } + } + + m_comment_le->setReadOnly((bk.isSeparator()|| !bk.hasParent()) ? true : false ); + QString commentText = bk.description(); + if (m_comment_le->text() != commentText) { + const int cursorPosition = m_comment_le->cursorPosition(); + m_comment_le->setText(commentText); + m_comment_le->setCursorPosition(cursorPosition); + } + + // readonly fields + updateStatus(); + +} + +void BookmarkInfoWidget::updateStatus() +{ + //FIXME we don't want every metadata element, but only that with owner "http://www.kde.org" + QString visitDate = + GlobalBookmarkManager::makeTimeStr(m_bk.metaDataItem("time_visited")); + m_visitdate_le->setReadOnly(true); + m_visitdate_le->setText(visitDate); + + QString creationDate = + GlobalBookmarkManager::makeTimeStr(m_bk.metaDataItem("time_added")); + m_credate_le->setReadOnly(true); + m_credate_le->setText(creationDate); + + // TODO - get the actual field name from the spec if it exists, else copy galeon + m_visitcount_le->setReadOnly(true); + m_visitcount_le->setText(m_bk.metaDataItem("visit_count")); +} + +void BookmarkInfoWidget::commitChanges() +{ + commitTitle(); + commitURL(); + commitComment(); +} + +void BookmarkInfoWidget::commitTitle() +{ + // no more change compression + titlecmd = 0; +} + +void BookmarkInfoWidget::slotTextChangedTitle(const QString &str) +{ + if (m_bk.isNull() || !m_title_le->isModified()) + return; + + timer->start(1000); + + if(titlecmd) + { + titlecmd->modify(str); + titlecmd->redo(); + } + else + { + titlecmd = new EditCommand(m_model, m_bk.address(), 0, str); + m_model->commandHistory()->addCommand(titlecmd); + } +} + +void BookmarkInfoWidget::commitURL() +{ + urlcmd = 0; +} + +void BookmarkInfoWidget::slotTextChangedURL(const QString &str) { + if (m_bk.isNull() || !m_url_le->isModified()) + return; + + timer->start(1000); + + if(urlcmd) + { + urlcmd->modify(str); + urlcmd->redo(); + } + else + { + urlcmd = new EditCommand(m_model, m_bk.address(), 1, str); + m_model->commandHistory()->addCommand(urlcmd); + } +} + +void BookmarkInfoWidget::commitComment() +{ + commentcmd = 0; +} + +void BookmarkInfoWidget::slotTextChangedComment(const QString &str) { + if (m_bk.isNull() || !m_comment_le->isModified()) + return; + + timer->start(1000); + + if(commentcmd) + { + commentcmd->modify(str); + commentcmd->redo(); + } + else + { + commentcmd = new EditCommand(m_model, m_bk.address(), 2, str); + m_model->commandHistory()->addCommand(commentcmd); + } +} + +void BookmarkInfoWidget::slotUpdate() +{ + const QModelIndexList & list = mBookmarkListView->selectionModel()->selectedRows(); + if( list.count() == 1) + { + QModelIndex index = *list.constBegin(); + showBookmark( mBookmarkListView->bookmarkModel()->bookmarkForIndex(index) ); + } + else + showBookmark( KBookmark() ); +} + +BookmarkInfoWidget::BookmarkInfoWidget(BookmarkListView * lv, KBookmarkModel* model, QWidget *parent) + : QWidget(parent), m_model(model), mBookmarkListView(lv) { + + connect(mBookmarkListView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), + SLOT(slotUpdate())); + + connect(mBookmarkListView->model(), SIGNAL(dataChanged(QModelIndex,QModelIndex)), + SLOT(slotUpdate())); + + timer = new QTimer(this); + timer->setSingleShot(true); + connect(timer, SIGNAL(timeout()), SLOT(commitChanges())); + + titlecmd = 0; + urlcmd = 0; + commentcmd = 0; + + QHBoxLayout *layout = new QHBoxLayout(this); + QFormLayout *form1 = new QFormLayout(); + QFormLayout *form2 = new QFormLayout(); + layout->addLayout(form1); + layout->addLayout(form2); + + m_title_le = new KLineEdit(this); + m_title_le->setClearButtonShown(true); + form1->addRow(i18n("Name:"), m_title_le); + + connect(m_title_le, SIGNAL(textChanged(QString)), + SLOT(slotTextChangedTitle(QString))); + connect(m_title_le, SIGNAL(editingFinished()), SLOT(commitTitle())); + + m_url_le = new KLineEdit(this); + m_url_le->setClearButtonShown(true); + form1->addRow(i18n("Location:"), m_url_le); + + connect(m_url_le, SIGNAL(textChanged(QString)), + SLOT(slotTextChangedURL(QString))); + connect(m_url_le, SIGNAL(editingFinished()), SLOT(commitURL())); + + m_comment_le = new KLineEdit(this); + m_comment_le->setClearButtonShown(true); + form1->addRow(i18n("Comment:"), m_comment_le); + + connect(m_comment_le, SIGNAL(textChanged(QString)), + SLOT(slotTextChangedComment(QString))); + connect(m_comment_le, SIGNAL(editingFinished()), SLOT(commitComment())); + + m_credate_le = new KLineEdit(this); + form2->addRow(i18n("First viewed:"), m_credate_le); + + m_visitdate_le = new KLineEdit(this); + form2->addRow(i18n("Viewed last:"), m_visitdate_le); + + m_visitcount_le = new KLineEdit(this); + form2->addRow(i18n("Times visited:"), m_visitcount_le); + + showBookmark(KBookmark()); +} + +#include "moc_bookmarkinfowidget.cpp" + diff --git a/keditbookmarks/bookmarkinfowidget.h b/keditbookmarks/bookmarkinfowidget.h new file mode 100644 index 00000000..6f129f5e --- /dev/null +++ b/keditbookmarks/bookmarkinfowidget.h @@ -0,0 +1,67 @@ +// vim: set ts=4 sts=4 sw=4 et: +/* This file is part of the KDE project + Copyright (C) 2000 David Faure <faure@kde.org> + Copyright (C) 2002-2003 Alexander Kellett <lypanov@kde.org> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) version 3. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/> +*/ + +#ifndef BOOKMARKINFOWIDGET_H +#define BOOKMARKINFOWIDGET_H + +#include <kbookmark.h> +#include <QWidget> + +class KBookmarkModel; +class BookmarkListView; +class EditCommand; + +class KLineEdit; + +#include <QTimer> + +class BookmarkInfoWidget : public QWidget { + Q_OBJECT +public: + explicit BookmarkInfoWidget(BookmarkListView * lv, KBookmarkModel* model, QWidget * = 0); + + KBookmark bookmark() { return m_bk; } + void updateStatus(); //FIXME where was this called? + +public Q_SLOTS: + void slotTextChangedURL(const QString &); + void slotTextChangedTitle(const QString &); + void slotTextChangedComment(const QString &); + + void slotUpdate(); + + void commitChanges(); + void commitTitle(); + void commitURL(); + void commitComment(); + +private: + void showBookmark(const KBookmark &bk); + EditCommand * titlecmd, * urlcmd, * commentcmd; + QTimer * timer; + KLineEdit *m_title_le, *m_url_le, + *m_comment_le; + KLineEdit *m_visitdate_le, *m_credate_le, + *m_visitcount_le; + KBookmark m_bk; + KBookmarkModel* m_model; + BookmarkListView * mBookmarkListView; +}; + +#endif diff --git a/keditbookmarks/bookmarkiterator.cpp b/keditbookmarks/bookmarkiterator.cpp new file mode 100644 index 00000000..2824ea9c --- /dev/null +++ b/keditbookmarks/bookmarkiterator.cpp @@ -0,0 +1,126 @@ +/* This file is part of the KDE project + Copyright (C) 2000, 2010 David Faure <faure@kde.org> + Copyright (C) 2002-2003 Alexander Kellett <lypanov@kde.org> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License version 2 or at your option version 3 as published by + the Free Software Foundation. + + 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; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "bookmarkiterator.h" +#include "kbookmarkmodel/model.h" +#include <kbookmarkmanager.h> + +#include <kdebug.h> +#include <QtCore/QTimer> + +BookmarkIterator::BookmarkIterator(BookmarkIteratorHolder* holder, const QList<KBookmark>& bks) + : QObject(holder), m_bookmarkList(bks), m_holder(holder) +{ + delayedEmitNextOne(); +} + +BookmarkIterator::~BookmarkIterator() +{ +} + +void BookmarkIterator::delayedEmitNextOne() +{ + QTimer::singleShot(1, this, SLOT(nextOne())); +} + +KBookmark BookmarkIterator::currentBookmark() +{ + return m_bk; +} + +void BookmarkIterator::nextOne() +{ + // kDebug() << "BookmarkIterator::nextOne"; + + // Look for an interesting bookmark + while (!m_bookmarkList.isEmpty()) { + KBookmark bk = m_bookmarkList.takeFirst(); + if (bk.hasParent() && isApplicable(bk)) { + m_bk = bk; + doAction(); + // Async action started, we'll have to come back later + return; + } + } + if (m_bookmarkList.isEmpty()) { + holder()->removeIterator(this); // deletes "this" + return; + } +} + +KBookmarkModel* BookmarkIterator::model() +{ + return m_holder->model(); +} + +/* --------------------------- */ + +BookmarkIteratorHolder::BookmarkIteratorHolder(QObject* parent, KBookmarkModel* model) + : QObject(parent), m_model(model) +{ + Q_ASSERT(m_model); +} + +void BookmarkIteratorHolder::insertIterator(BookmarkIterator *itr) +{ + m_iterators.prepend(itr); + doIteratorListChanged(); +} + +void BookmarkIteratorHolder::removeIterator(BookmarkIterator *itr) +{ + m_iterators.removeAll(itr); + itr->deleteLater(); + doIteratorListChanged(); +} + +void BookmarkIteratorHolder::cancelAllItrs() +{ + Q_FOREACH(BookmarkIterator* iterator, m_iterators) { + iterator->cancel(); + } + qDeleteAll(m_iterators); + m_iterators.clear(); + doIteratorListChanged(); +} + +void BookmarkIteratorHolder::addAffectedBookmark(const QString & address) +{ + kDebug() << address; + if(m_affectedBookmark.isNull()) + m_affectedBookmark = address; + else + m_affectedBookmark = KBookmark::commonParent(m_affectedBookmark, address); + kDebug() << "m_affectedBookmark is now" << m_affectedBookmark; +} + +void BookmarkIteratorHolder::doIteratorListChanged() +{ + kDebug() << count() << "iterators"; + emit setCancelEnabled(count() > 0); + if(count() == 0) { + kDebug() << "Notifing managers" << m_affectedBookmark; + KBookmarkManager* mgr = m_model->bookmarkManager(); + model()->notifyManagers(mgr->findByAddress(m_affectedBookmark).toGroup()); + m_affectedBookmark.clear(); + } +} + +#include "moc_bookmarkiterator.cpp" diff --git a/keditbookmarks/bookmarkiterator.h b/keditbookmarks/bookmarkiterator.h new file mode 100644 index 00000000..613cc7a2 --- /dev/null +++ b/keditbookmarks/bookmarkiterator.h @@ -0,0 +1,90 @@ +/* This file is part of the KDE project + Copyright (C) 2010 David Faure <faure@kde.org> + Copyright (C) 2002-2003 Alexander Kellett <lypanov@kde.org> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License version 2 or at your option version 3 as published by + the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/> +*/ + +#ifndef BOOKMARKITERATOR_H +#define BOOKMARKITERATOR_H + +#include <QtCore/QObject> +#include <QtCore/QList> +#include <kbookmark.h> + +class KBookmarkModel; +class BookmarkIteratorHolder; + +/** + * A bookmark iterator goes through every bookmark and performs an asynchronous + * action (e.g. downloading the favicon or testing whether the url exists). + */ +class BookmarkIterator : public QObject +{ + Q_OBJECT + +public: + BookmarkIterator(BookmarkIteratorHolder* holder, const QList<KBookmark>& bks); + virtual ~BookmarkIterator(); + BookmarkIteratorHolder* holder() const { return m_holder; } + KBookmarkModel* model(); + void delayedEmitNextOne(); + virtual void cancel() = 0; + +public Q_SLOTS: + void nextOne(); + +protected: + virtual void doAction() = 0; + virtual bool isApplicable(const KBookmark &bk) const = 0; + KBookmark currentBookmark(); + +private: + KBookmark m_bk; + QList<KBookmark> m_bookmarkList; + BookmarkIteratorHolder* m_holder; +}; + +/** + * The "bookmark iterator holder" handles all concurrent iterators for a given + * functionality: e.g. all favicon iterators. + * + * BookmarkIteratorHolder is the base class for the favicon and testlink holders. + */ +class BookmarkIteratorHolder : public QObject +{ + Q_OBJECT +public: + void cancelAllItrs(); + void removeIterator(BookmarkIterator*); + void insertIterator(BookmarkIterator*); + void addAffectedBookmark(const QString & address); + KBookmarkModel* model() { return m_model; } + +Q_SIGNALS: + void setCancelEnabled(bool canCancel); + +protected: + BookmarkIteratorHolder(QObject* parent, KBookmarkModel* model); + virtual ~BookmarkIteratorHolder() {} + void doIteratorListChanged(); + int count() const { return m_iterators.count(); } + KBookmarkModel* m_model; + +private: + QString m_affectedBookmark; + QList<BookmarkIterator *> m_iterators; +}; + +#endif diff --git a/keditbookmarks/bookmarklistview.cpp b/keditbookmarks/bookmarklistview.cpp new file mode 100644 index 00000000..00219b48 --- /dev/null +++ b/keditbookmarks/bookmarklistview.cpp @@ -0,0 +1,210 @@ +/* This file is part of the KDE project + Copyright (C) 2000 David Faure <faure@kde.org> + Copyright (C) 2002-2003 Alexander Kellett <lypanov@kde.org> + Copyright (C) 2005 Daniel Teske <teske@squorn.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) version 3. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/> +*/ + +#include "bookmarklistview.h" +#include "globalbookmarkmanager.h" +#include "kbookmarkmodel/model.h" +#include "toplevel.h" // for KEBApp +#include "settings.h" +#include "kbookmarkmodel/commands.h" + +#include <QHeaderView> +#include <QtGui/qitemselectionmodel.h> +#include <QMenu> +#include <QtGui/qevent.h> +#include <QBrush> +#include <QPalette> + +#include <kdebug.h> + +BookmarkFolderView::BookmarkFolderView( BookmarkListView * view, QWidget * parent ) + : KBookmarkView(parent), mview(view) +{ + mmodel = new BookmarkFolderViewFilterModel(parent); + mmodel->setSourceModel(view->model()); + setModel(mmodel); + header()->setVisible(false); + setRootIsDecorated(false); + setDropIndicatorShown(true); + setCurrentIndex( mmodel->index(0,0, QModelIndex())); + + connect(mmodel, SIGNAL(modelReset()), this, SLOT(slotReset())); +} + + +BookmarkFolderView::~BookmarkFolderView() +{ + +} + +void BookmarkFolderView::selectionChanged ( const QItemSelection & deselected, const QItemSelection & selected) +{ + const QModelIndexList & list = selectionModel()->selectedIndexes(); + if(list.count()) + mview->setRootIndex( mmodel->mapToSource(list.at(0)) ); + else + mview->setRootIndex( QModelIndex()); + KBookmarkView::selectionChanged(deselected, selected); +} + +void BookmarkFolderView::slotReset() +{ + setCurrentIndex( mmodel->index(0,0, QModelIndex())); + loadFoldedState(); +} + +KBookmark BookmarkFolderView::bookmarkForIndex(const QModelIndex & idx) const +{ + qDebug()<<"BookmarkFolderView::bookmarkForIndex"<<idx; + const QModelIndex & index = mmodel->mapToSource(idx); + return static_cast<KBookmarkModel *>(mmodel->sourceModel())->bookmarkForIndex(index); +} + +/********/ + + +BookmarkListView::BookmarkListView( QWidget * parent ) + : KBookmarkView(parent) +{ + setDragEnabled(true); +} + + +void BookmarkListView::setModel(QAbstractItemModel * model) +{ + KBookmarkView::setModel(model); +} + +KBookmarkModel* BookmarkListView::bookmarkModel() const +{ + return dynamic_cast<KBookmarkModel*>(QTreeView::model()); +} + +KBookmark BookmarkListView::bookmarkForIndex(const QModelIndex & idx) const +{ + return bookmarkModel()->bookmarkForIndex(idx); +} + + +BookmarkListView::~BookmarkListView() +{ + saveColumnSetting(); +} + +void BookmarkListView::contextMenuEvent ( QContextMenuEvent * e ) +{ + QModelIndex index = indexAt(e->pos()); + KBookmark bk; + if(index.isValid()) + bk = bookmarkForIndex(index); + + QMenu* popup; + if( !index.isValid() + || (bk.address() == GlobalBookmarkManager::self()->root().address()) + || (bk.isGroup())) //FIXME add empty folder padder + { + popup = KEBApp::self()->popupMenuFactory("popup_folder"); + } + else + { + popup = KEBApp::self()->popupMenuFactory("popup_bookmark"); + } + if (popup) + popup->popup(e->globalPos()); +} + +void BookmarkListView::loadColumnSetting() +{ + header()->resizeSection(KEBApp::NameColumn, KEBSettings::name()); + header()->resizeSection(KEBApp::UrlColumn, KEBSettings::uRL()); + header()->resizeSection(KEBApp::CommentColumn, KEBSettings::comment()); + header()->resizeSection(KEBApp::StatusColumn, KEBSettings::status()); +} + +void BookmarkListView::saveColumnSetting() +{ + KEBSettings::setName( header()->sectionSize(KEBApp::NameColumn)); + KEBSettings::setURL( header()->sectionSize(KEBApp::UrlColumn)); + KEBSettings::setComment( header()->sectionSize(KEBApp::CommentColumn)); + KEBSettings::setStatus( header()->sectionSize(KEBApp::StatusColumn)); + KEBSettings::self()->writeConfig(); +} + +/************/ + +BookmarkFolderViewFilterModel::BookmarkFolderViewFilterModel(QObject * parent) + : QSortFilterProxyModel(parent) +{ +} + +QStringList BookmarkFolderViewFilterModel::mimeTypes() const +{ + return sourceModel()->mimeTypes(); +} + +bool BookmarkFolderViewFilterModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) +{ + QModelIndex dropDestProxyIndex; + bool isInsertBetweenOp = false; + if(row == -1) + { + dropDestProxyIndex = parent; + } + else + { + dropDestProxyIndex = index(row, column, parent); + isInsertBetweenOp = true; + } + QModelIndex dropDestIndex = mapToSource(dropDestProxyIndex); + if (!isInsertBetweenOp) + { + // Just dropping it onto dropDestIndex - ignore row and column. + return sourceModel()->dropMimeData( data, action, -1, -1, dropDestIndex); + } + else + { + // Dropping before dropDestIndex. We want to keep row and column + // relative to the parent. + // I'm reasonably certain the parent must be valid in this case. If you get a crash here - nag me! + Q_ASSERT(parent.isValid()); + QModelIndex dropDestParentIndex = mapToSource(parent); + return sourceModel()->dropMimeData( data, action, dropDestIndex.row(), dropDestIndex.column(), dropDestParentIndex); + } +} + +BookmarkFolderViewFilterModel::~BookmarkFolderViewFilterModel() +{ +} + +bool BookmarkFolderViewFilterModel::filterAcceptsColumn ( int source_column, const QModelIndex & source_parent ) const +{ + Q_UNUSED(source_parent); + + //Show name, hide everything else + return (source_column == 0); +} + +bool BookmarkFolderViewFilterModel::filterAcceptsRow ( int source_row, const QModelIndex & source_parent ) const +{ + QModelIndex index = sourceModel()->index(source_row, 0, source_parent); + const KBookmark bk = index.data(KBookmarkModel::KBookmarkRole).value<KBookmark>(); + return bk.isGroup(); +} + +#include "moc_bookmarklistview.cpp" diff --git a/keditbookmarks/bookmarklistview.h b/keditbookmarks/bookmarklistview.h new file mode 100644 index 00000000..8a77ba87 --- /dev/null +++ b/keditbookmarks/bookmarklistview.h @@ -0,0 +1,77 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Daniel Teske <teske@squorn.de> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License version 2 as published by the Free Software Foundation. + + 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; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef BOOKMARKLISTVIEW_H +#define BOOKMARKLISTVIEW_H + +#include <QTreeView> +#include <QSortFilterProxyModel> + +#include "kbookmarkmodel/view.h" + +class KBookmarkModel; +class BookmarkListView; +class BookmarkFolderViewFilterModel; + +class BookmarkFolderView : public KBookmarkView +{ + Q_OBJECT +public: + explicit BookmarkFolderView( BookmarkListView * view, QWidget * parent = 0 ); + virtual ~BookmarkFolderView(); + virtual void selectionChanged ( const QItemSelection & selected, const QItemSelection & deselected ); + virtual KBookmark bookmarkForIndex(const QModelIndex & idx) const; +private Q_SLOTS: + void slotReset(); +private: + BookmarkListView * mview; + BookmarkFolderViewFilterModel * mmodel; +}; + +class BookmarkListView : public KBookmarkView +{ + Q_OBJECT +public: + BookmarkListView( QWidget * parent = 0 ); + virtual ~BookmarkListView(); + void loadColumnSetting(); + void saveColumnSetting(); + virtual void setModel(QAbstractItemModel * model); + virtual KBookmark bookmarkForIndex(const QModelIndex & idx) const; + KBookmarkModel * bookmarkModel() const; +protected: + virtual void contextMenuEvent ( QContextMenuEvent * e ); +}; + + +class BookmarkFolderViewFilterModel : public QSortFilterProxyModel +{ + Q_OBJECT +public: + BookmarkFolderViewFilterModel(QObject * parent = 0); + virtual ~BookmarkFolderViewFilterModel(); + virtual QStringList mimeTypes() const; + virtual bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent); +protected: + bool filterAcceptsColumn ( int source_column, const QModelIndex & source_parent ) const; + bool filterAcceptsRow ( int source_row, const QModelIndex & source_parent ) const; + //FIXME check + virtual Qt::DropActions supportedDropActions() const + { return sourceModel()->supportedDropActions(); } +}; +#endif diff --git a/keditbookmarks/exporters.cpp b/keditbookmarks/exporters.cpp new file mode 100644 index 00000000..175c9fac --- /dev/null +++ b/keditbookmarks/exporters.cpp @@ -0,0 +1,89 @@ +// -*- indent-tabs-mode:nil -*- +// vim: set ts=4 sts=4 sw=4 et: +/* This file is part of the KDE project + Copyright (C) 2003 Alexander Kellett <lypanov@kde.org> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) version 3. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/> +*/ + +#include "exporters.h" + +#include <kdebug.h> +#include <klocale.h> + +#include <QtCore/QFile> +#include <QtCore/QTextStream> + +HTMLExporter::HTMLExporter() + : m_out(&m_string, QIODevice::WriteOnly) { +} + +void HTMLExporter::write(const KBookmarkGroup &grp, const QString &filename, bool showAddress) { + QFile file(filename); + if (!file.open(QIODevice::WriteOnly)) { + kError(7043) << "Can't write to file " << filename << endl; + return; + } + QTextStream tstream(&file); + tstream.setCodec("UTF-8"); + tstream << toString(grp, showAddress); +} + +QString HTMLExporter::toString(const KBookmarkGroup &grp, bool showAddress) +{ + m_showAddress = showAddress; + traverse(grp); + return "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n" + "<html><head><title>"+i18n("My Bookmarks")+"\n" + "" + "\n" + "\n" + "
    " + + m_string + + "
    \n" + "\n\n"; +} + +void HTMLExporter::visit(const KBookmark &bk) { + // kDebug() << "visit(" << bk.text() << ")"; + if(bk.isSeparator()) + { + m_out << bk.fullText() << "
    "<"<< endl; + m_out << "
    " << bk.url().url().toUtf8() << "
    "; + } + else + { + m_out << ""; + m_out << bk.fullText() << "
    " << endl; + } + } +} + +void HTMLExporter::visitEnter(const KBookmarkGroup &grp) { + // kDebug() << "visitEnter(" << grp.text() << ")"; + m_out << "" << grp.fullText() << "
    " << endl; + m_out << "
    "<< endl; +} + +void HTMLExporter::visitLeave(const KBookmarkGroup &) { + // kDebug() << "visitLeave()"; + m_out << "
    " << endl; +} + diff --git a/keditbookmarks/exporters.h b/keditbookmarks/exporters.h new file mode 100644 index 00000000..0a814120 --- /dev/null +++ b/keditbookmarks/exporters.h @@ -0,0 +1,41 @@ +/* This file is part of the KDE project + Copyright (C) 2003 Alexander Kellett + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) version 3. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see +*/ + +#ifndef EXPORTERS_H +#define EXPORTERS_H + +#include +//Added by qt3to4: +#include + +class HTMLExporter : private KBookmarkGroupTraverser { +public: + HTMLExporter(); + virtual ~HTMLExporter(){} + QString toString(const KBookmarkGroup &, bool showAddress = false); + void write(const KBookmarkGroup &, const QString &, bool showAddress = false); +private: + virtual void visit(const KBookmark &); + virtual void visitEnter(const KBookmarkGroup &); + virtual void visitLeave(const KBookmarkGroup &); +private: + QString m_string; + QTextStream m_out; + bool m_showAddress; +}; + +#endif diff --git a/keditbookmarks/favicons.cpp b/keditbookmarks/favicons.cpp new file mode 100644 index 00000000..fdd999b7 --- /dev/null +++ b/keditbookmarks/favicons.cpp @@ -0,0 +1,86 @@ +/* This file is part of the KDE project + Copyright (C) 2002-2003 Alexander Kellett + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License version 2 or at your option version 3 as published by + the Free Software Foundation. + + 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; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "favicons.h" + +#include "bookmarkiterator.h" +#include "faviconupdater.h" +#include "kbookmarkmodel/commands.h" +#include "kbookmarkmodel/model.h" + +#include +#include + +FavIconsItrHolder::FavIconsItrHolder(QObject* parent, KBookmarkModel* model) + : BookmarkIteratorHolder(parent, model) +{ +} + +/* -------------------------- */ + +FavIconsItr::FavIconsItr(BookmarkIteratorHolder* holder, const QList& bks) + : BookmarkIterator(holder, bks), m_updater(0) +{ +} + +FavIconsItr::~FavIconsItr() +{ + delete m_updater; +} + +void FavIconsItr::setStatus(const QString & status) +{ + currentBookmark().setMetaDataItem("favstate", status); + model()->emitDataChanged(currentBookmark()); +} + +void FavIconsItr::slotDone(bool succeeded, const QString& errorString) +{ + // kDebug() << "FavIconsItr::slotDone()"; + setStatus(succeeded ? i18n("OK") : errorString); + holder()->addAffectedBookmark(KBookmark::parentAddress(currentBookmark().address())); + delayedEmitNextOne(); +} + +bool FavIconsItr::isApplicable(const KBookmark &bk) const +{ + if (bk.isGroup() || bk.isSeparator()) + return false; + return bk.url().protocol().startsWith("http"); +} + +void FavIconsItr::doAction() +{ + // kDebug() << "FavIconsItr::doAction()"; + m_oldStatus = currentBookmark().metaDataItem("favstate"); + setStatus(i18n("Updating favicon...")); + if (!m_updater) { + m_updater = new FavIconUpdater(this); + connect(m_updater, SIGNAL(done(bool,QString)), + this, SLOT(slotDone(bool,QString)) ); + } + m_updater->downloadIcon(currentBookmark()); +} + +void FavIconsItr::cancel() +{ + setStatus(m_oldStatus); +} + +#include "moc_favicons.cpp" diff --git a/keditbookmarks/favicons.h b/keditbookmarks/favicons.h new file mode 100644 index 00000000..5a286ccd --- /dev/null +++ b/keditbookmarks/favicons.h @@ -0,0 +1,59 @@ +// -*- c-basic-offset: 4; indent-tabs-mode:nil -*- +// vim: set ts=4 sts=4 sw=4 et: +/* This file is part of the KDE project + Copyright (C) 2002-2003 Alexander Kellett + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) version 3. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see +*/ + +#ifndef FAVICONS_H +#define FAVICONS_H + +#include + +#include "bookmarkiterator.h" + +class FavIconsItrHolder : public BookmarkIteratorHolder { +public: + FavIconsItrHolder(QObject* parent, KBookmarkModel* model); +}; + +class KBookmarkModel; +class FavIconUpdater; + +class FavIconsItr : public BookmarkIterator +{ + Q_OBJECT + +public: + FavIconsItr(BookmarkIteratorHolder* holder, const QList& bks); + ~FavIconsItr(); + + virtual void cancel(); + +public Q_SLOTS: + void slotDone(bool succeeded, const QString& errorString); + +protected: + virtual void doAction(); + virtual bool isApplicable(const KBookmark &bk) const; + +private: + void setStatus(const QString & status); + FavIconUpdater *m_updater; + QString m_oldStatus; +}; + +#endif + diff --git a/keditbookmarks/faviconupdater.cpp b/keditbookmarks/faviconupdater.cpp new file mode 100644 index 00000000..ddcea113 --- /dev/null +++ b/keditbookmarks/faviconupdater.cpp @@ -0,0 +1,106 @@ +// -*- indent-tabs-mode:nil -*- +// vim: set ts=4 sts=4 sw=4 et: +/* This file is part of the KDE project + Copyright (C) 2003 Alexander Kellett + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License version 2 or at your option version 3 as published by + the Free Software Foundation. + + 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; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "faviconupdater.h" + +#include "bookmarkiterator.h" +#include "toplevel.h" + +#include +#include + +#include + +#include +#include +#include +#include + +FavIconUpdater::FavIconUpdater(QObject *parent) + : QObject(parent), + m_favIconModule("org.kde.kded", "/modules/favicons", QDBusConnection::sessionBus()) +{ + connect(&m_favIconModule, SIGNAL(iconChanged(bool,QString,QString)), + this, SLOT(notifyChange(bool,QString,QString)) ); + connect(&m_favIconModule, SIGNAL(error(bool,QString,QString)), + this, SLOT(slotFavIconError(bool,QString,QString)) ); +} + +void FavIconUpdater::downloadIcon(const KBookmark &bk) +{ + m_bk = bk; + const QString url = bk.url().url(); + const QString favicon = KMimeType::favIconForUrl(url); + if (!favicon.isEmpty()) { + kDebug() << "got favicon" << favicon; + m_bk.setIcon(favicon); + KEBApp::self()->notifyCommandExecuted(); + // kDebug() << "emit done(true)"; + emit done(true, QString()); + + } else { + kDebug() << "no favicon found"; + m_favIconModule.forceDownloadHostIcon(url); + } +} + +FavIconUpdater::~FavIconUpdater() +{ +} + +// webkit callback +void FavIconUpdater::setIconUrl(const KUrl &iconURL) +{ + m_favIconModule.setIconForUrl(m_bk.url().url(), iconURL.url()); + // The above call will make the kded module start the download and emit iconChanged or error. +} + +bool FavIconUpdater::isFavIconSignalRelevant(bool isHost, const QString& hostOrURL) const +{ + // Is this signal interesting to us? (Don't react on an unrelated favicon) + return (isHost && hostOrURL == m_bk.url().host()) || + (!isHost && hostOrURL == m_bk.url().url()); // should we use the api that ignores trailing slashes? +} + +void FavIconUpdater::notifyChange(bool isHost, + const QString& hostOrURL, + const QString& iconName) +{ + kDebug() << hostOrURL << iconName; + if (isFavIconSignalRelevant(isHost, hostOrURL)) { + if (iconName.isEmpty()) { // old version of the kded module could emit with an empty iconName on error + slotFavIconError(isHost, hostOrURL, QString()); + } else { + m_bk.setIcon(iconName); + emit done(true, QString()); + } + } +} + +void FavIconUpdater::slotFavIconError(bool isHost, const QString& hostOrURL, const QString& errorString) +{ + kDebug() << hostOrURL << errorString; + if (isFavIconSignalRelevant(isHost, hostOrURL)) { + emit done(false, errorString); + } +} + +#include "moc_faviconupdater.cpp" diff --git a/keditbookmarks/faviconupdater.h b/keditbookmarks/faviconupdater.h new file mode 100644 index 00000000..eda65d83 --- /dev/null +++ b/keditbookmarks/faviconupdater.h @@ -0,0 +1,52 @@ +/* This file is part of the KDE project + Copyright (C) 2003 Alexander Kellett + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) version 3. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see +*/ + +#ifndef FAVICONUPDATER_H +#define FAVICONUPDATER_H + +#include +#include "favicon_interface.h" // org::kde::FavIcon + +#include + +class FavIconUpdater : public QObject +{ + Q_OBJECT + +public: + FavIconUpdater(QObject *parent); + ~FavIconUpdater(); + void downloadIcon(const KBookmark &bk); + +private Q_SLOTS: + void setIconUrl(const KUrl &iconURL); + void notifyChange(bool isHost, const QString& hostOrURL, const QString& iconName); + void slotFavIconError(bool isHost, const QString& hostOrURL, const QString& errorString); + +Q_SIGNALS: + void done(bool succeeded, const QString& error); + +private: + bool isFavIconSignalRelevant(bool isHost, const QString& hostOrURL) const; + +private: + KBookmark m_bk; + org::kde::FavIcon m_favIconModule; +}; + +#endif + diff --git a/keditbookmarks/globalbookmarkmanager.cpp b/keditbookmarks/globalbookmarkmanager.cpp new file mode 100644 index 00000000..6a06aeed --- /dev/null +++ b/keditbookmarks/globalbookmarkmanager.cpp @@ -0,0 +1,107 @@ +/* This file is part of the KDE project + Copyright (C) 2000, 2010 David Faure + Copyright (C) 2002-2003 Alexander Kellett + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License version 2 or at your option version 3 as published by + the Free Software Foundation. + + 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; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "globalbookmarkmanager.h" +#include +#include +#include +#include +#include "kbookmarkmanager.h" +#include "kbookmarkmodel/model.h" +#include "kbookmarkmodel/commandhistory.h" + +GlobalBookmarkManager *GlobalBookmarkManager::s_mgr = 0; + +GlobalBookmarkManager::GlobalBookmarkManager() + : QObject(0), + m_mgr(0), m_model(0) +{ +} + +GlobalBookmarkManager::~GlobalBookmarkManager() +{ +} + +KBookmarkGroup GlobalBookmarkManager::root() +{ + return mgr()->root(); +} + +KBookmark GlobalBookmarkManager::bookmarkAt(const QString &a) +{ + return self()->mgr()->findByAddress(a); +} + +bool GlobalBookmarkManager::managerSave() { return mgr()->save(); } +void GlobalBookmarkManager::saveAs(const QString &fileName) { mgr()->saveAs(fileName); } +void GlobalBookmarkManager::setUpdate(bool update) { mgr()->setUpdate(update); } +QString GlobalBookmarkManager::path() const { return mgr()->path(); } + +void GlobalBookmarkManager::createManager(const QString &filename, const QString &dbusObjectName, CommandHistory* commandHistory) { + if (m_mgr) { + kDebug()<<"createManager called twice"; + delete m_mgr; + } + + kDebug()<<"DBus Object name: "<setBookmarkManager(m_mgr); + + if ( m_model ) { + m_model->setRoot(root()); + } else { + m_model = new KBookmarkModel(root(), commandHistory, this); + } +} + +void GlobalBookmarkManager::notifyManagers(const KBookmarkGroup& grp) +{ + m_model->notifyManagers(grp); +} + +void GlobalBookmarkManager::notifyManagers() { + notifyManagers( root() ); +} + +void GlobalBookmarkManager::reloadConfig() { + mgr()->emitConfigChanged(); +} + +QString GlobalBookmarkManager::makeTimeStr(const QString & in) +{ + int secs; + bool ok; + secs = in.toInt(&ok); + if (!ok) + return QString(); + return makeTimeStr(secs); +} + +QString GlobalBookmarkManager::makeTimeStr(int b) +{ + QDateTime dt; + dt.setTime_t(b); + return (dt.daysTo(QDateTime::currentDateTime()) > 31) + ? KGlobal::locale()->formatDate(dt.date(), KLocale::LongDate) + : KGlobal::locale()->formatDateTime(dt, KLocale::LongDate); +} + +#include "moc_globalbookmarkmanager.cpp" diff --git a/keditbookmarks/globalbookmarkmanager.h b/keditbookmarks/globalbookmarkmanager.h new file mode 100644 index 00000000..944494eb --- /dev/null +++ b/keditbookmarks/globalbookmarkmanager.h @@ -0,0 +1,68 @@ +/* This file is part of the KDE project + Copyright (C) 2000, 2010 David Faure + Copyright (C) 2002-2003 Alexander Kellett + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) version 3. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see +*/ + +#ifndef GLOBALBOOKMARKMANAGER_H +#define GLOBALBOOKMARKMANAGER_H + +#include +#include + +class CommandHistory; +class KBookmarkModel; +class KBookmark; +class KBookmarkManager; + +class GlobalBookmarkManager : public QObject +{ + Q_OBJECT +public: + typedef enum {HTMLExport, OperaExport, IEExport, MozillaExport, NetscapeExport} ExportType; + + // TODO port to K_GLOBAL_STATIC if we keep this class + static GlobalBookmarkManager* self() { if (!s_mgr) { s_mgr = new GlobalBookmarkManager(); } return s_mgr; } + ~GlobalBookmarkManager(); + KBookmarkGroup root(); + static KBookmark bookmarkAt(const QString & a); + + KBookmarkModel* model() const { return m_model; } + KBookmarkManager* mgr() const { return m_mgr; } + QString path() const; + + void createManager(const QString &filename, const QString &dbusObjectName, CommandHistory* commandHistory); + void notifyManagers(const KBookmarkGroup& grp); + void notifyManagers(); + bool managerSave(); + void saveAs(const QString &fileName); + void doExport(ExportType type, const QString & path = QString()); + void setUpdate(bool update); + + void reloadConfig(); + + // TODO move out + static QString makeTimeStr(const QString &); + static QString makeTimeStr(int); + +private: + GlobalBookmarkManager(); + KBookmarkManager *m_mgr; + KBookmarkModel *m_model; + static GlobalBookmarkManager *s_mgr; +}; + +#endif /* GLOBALBOOKMARKMANAGER_H */ + diff --git a/keditbookmarks/importers.cpp b/keditbookmarks/importers.cpp new file mode 100644 index 00000000..f6b36c50 --- /dev/null +++ b/keditbookmarks/importers.cpp @@ -0,0 +1,307 @@ +// -*- indent-tabs-mode:nil -*- +// vim: set ts=4 sts=4 sw=4 et: +/* This file is part of the KDE project + Copyright (C) 2000 David Faure + Copyright (C) 2002-2003 Alexander Kellett + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License version 2 or at your option version 3 as published by + the Free Software Foundation. + + 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; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "importers.h" +#include "globalbookmarkmanager.h" + +#include "kbookmarkmodel/commands.h" +#include "toplevel.h" // for KEBApp +#include "kbookmarkmodel/model.h" + +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include +//#include +#include +#include + + +ImportCommand::ImportCommand(KBookmarkModel* model) + : QUndoCommand(), m_model(model), m_utf8(false), m_folder(false), m_cleanUpCmd(0) +{ +} + +void ImportCommand::setVisibleName(const QString& visibleName) +{ + m_visibleName = visibleName; + setText(i18nc("(qtundo-format)", "Import %1 Bookmarks", visibleName)); +} + +QString ImportCommand::folder() const { + return m_folder ? i18n("%1 Bookmarks", visibleName()) : QString(); +} + +ImportCommand* ImportCommand::importerFactory(KBookmarkModel* model, const QString &type) +{ + if (type == "Galeon") return new GaleonImportCommand(model); + else if (type == "IE") return new IEImportCommand(model); + else if (type == "KDE2") return new KDE2ImportCommand(model); + else if (type == "Opera") return new OperaImportCommand(model); + //else if (type == "Crashes") return new CrashesImportCommand(); + else if (type == "Moz") return new MozImportCommand(model); + else if (type == "NS") return new NSImportCommand(model); + else { + kError() << "ImportCommand::importerFactory() - invalid type (" << type << ")!" << endl; + return 0; + } +} + +ImportCommand* ImportCommand::performImport(KBookmarkModel* model, const QString &type, QWidget *top) +{ + ImportCommand *importer = ImportCommand::importerFactory(model, type); + + Q_ASSERT(importer); + + QString mydirname = importer->requestFilename(); + if (mydirname.isEmpty()) { + delete importer; + return 0; + } + + int answer = + KMessageBox::questionYesNoCancel( + top, i18n("Import as a new subfolder or replace all the current bookmarks?"), + i18nc("@title:window", "%1 Import", importer->visibleName()), + KGuiItem(i18n("As New Folder")), KGuiItem(i18n("Replace"))); + + if (answer == KMessageBox::Cancel) { + delete importer; + return 0; + } + + importer->import(mydirname, answer == KMessageBox::Yes); + return importer; +} + +void ImportCommand::doCreateHoldingFolder(KBookmarkGroup &bkGroup) { + bkGroup = GlobalBookmarkManager::self()->mgr() + ->root().createNewFolder(folder()); + bkGroup.setIcon(m_icon); + m_group = bkGroup.address(); +} + +void ImportCommand::redo() +{ + KBookmarkGroup bkGroup; + + if (!folder().isNull()) { + doCreateHoldingFolder(bkGroup); + + } else { + // import into the root, after cleaning it up + bkGroup = GlobalBookmarkManager::self()->root(); + delete m_cleanUpCmd; + m_cleanUpCmd = DeleteCommand::deleteAll(m_model, bkGroup); + + new DeleteCommand(m_model, bkGroup.address(), + true /* contentOnly */, m_cleanUpCmd); + m_cleanUpCmd->redo(); + + // import at the root + m_group = ""; + } + + doExecute(bkGroup); + + // notify the model that the data has changed + // + // FIXME Resetting the model completely has the unwanted + // side-effect of collapsing all items in tree views + // (and possibly other side effects) + m_model->resetModel(); +} + +void ImportCommand::undo() +{ + if ( !folder().isEmpty() ) { + // we created a group -> just delete it + DeleteCommand cmd(m_model, m_group); + cmd.redo(); + + } else { + // we imported at the root -> delete everything + KBookmarkGroup root = GlobalBookmarkManager::self()->root(); + QUndoCommand *cmd = DeleteCommand::deleteAll(m_model, root); + + cmd->redo(); + delete cmd; + + // and recreate what was there before + m_cleanUpCmd->undo(); + } +} + +QString ImportCommand::affectedBookmarks() const +{ + QString rootAdr = GlobalBookmarkManager::self()->root().address(); + if(m_group == rootAdr) + return m_group; + else + return KBookmark::parentAddress(m_group); +} + +/* -------------------------------------- */ + +QString MozImportCommand::requestFilename() const { + static KMozillaBookmarkImporterImpl importer; + return importer.findDefaultLocation(); +} + +QString NSImportCommand::requestFilename() const { + static KNSBookmarkImporterImpl importer; + return importer.findDefaultLocation(); +} + +QString OperaImportCommand::requestFilename() const { + static KOperaBookmarkImporterImpl importer; + return importer.findDefaultLocation(); +} + +QString IEImportCommand::requestFilename() const { + static KIEBookmarkImporterImpl importer; + return importer.findDefaultLocation(); +} + +// following two are really just xbel + +QString GaleonImportCommand::requestFilename() const { + return KFileDialog::getOpenFileName( + QString(QDir::homePath() + "/.galeon"), + i18n("*.xbel|Galeon Bookmark Files (*.xbel)"), + KEBApp::self()); +} + +#include "kstandarddirs.h" + +QString KDE2ImportCommand::requestFilename() const { + return KFileDialog::getOpenFileName( + KStandardDirs::locateLocal("data", "konqueror"), + i18n("*.xml|KDE Bookmark Files (*.xml)"), + KEBApp::self()); +} + +/* -------------------------------------- */ + +static void parseInto(const KBookmarkGroup &bkGroup, KBookmarkImporterBase *importer) { + KBookmarkDomBuilder builder(bkGroup, GlobalBookmarkManager::self()->mgr()); + builder.connectImporter(importer); + importer->parse(); +} + +void OperaImportCommand::doExecute(const KBookmarkGroup &bkGroup) { + KOperaBookmarkImporterImpl importer; + importer.setFilename(m_fileName); + parseInto(bkGroup, &importer); +} + +void IEImportCommand::doExecute(const KBookmarkGroup &bkGroup) { + KIEBookmarkImporterImpl importer; + importer.setFilename(m_fileName); + parseInto(bkGroup, &importer); +} + +void HTMLImportCommand::doExecute(const KBookmarkGroup &bkGroup) { + KNSBookmarkImporterImpl importer; + importer.setFilename(m_fileName); + importer.setUtf8(m_utf8); + parseInto(bkGroup, &importer); +} + +/* -------------------------------------- */ + +void XBELImportCommand::doCreateHoldingFolder(KBookmarkGroup &) { + // rather than reuse the old group node we transform the + // root xbel node into the group when doing an xbel import +} + +void XBELImportCommand::doExecute(const KBookmarkGroup &/*bkGroup*/) { + // check if already open first??? + KBookmarkManager *pManager = KBookmarkManager::managerForFile(m_fileName, QString()); + + QDomDocument doc = GlobalBookmarkManager::self()->mgr()->internalDocument(); + + // get the xbel + QDomNode subDoc = pManager->internalDocument().namedItem("xbel").cloneNode(); + if (subDoc.isProcessingInstruction()) + subDoc = subDoc.nextSibling(); + if (subDoc.isDocumentType()) + subDoc = subDoc.nextSibling(); + if (subDoc.nodeName() != "xbel") + return; + + if (!folder().isEmpty()) { + // transform into folder + subDoc.toElement().setTagName("folder"); + + // clear attributes + QStringList tags; + for (int i = 0; i < subDoc.attributes().count(); i++) + tags << subDoc.attributes().item(i).toAttr().name(); + for (QStringList::const_iterator it = tags.constBegin(); it != tags.constEnd(); ++it) + subDoc.attributes().removeNamedItem((*it)); + + subDoc.toElement().setAttribute("icon", m_icon); + + // give the folder a name + QDomElement textElem = doc.createElement("title"); + subDoc.insertBefore(textElem, subDoc.firstChild()); + textElem.appendChild(doc.createTextNode(folder())); + } + + // import and add it + QDomNode node = doc.importNode(subDoc, true); + + if (!folder().isEmpty()) { + GlobalBookmarkManager::self()->root().internalElement().appendChild(node); + m_group = KBookmarkGroup(node.toElement()).address(); + + } else { + QDomElement root = GlobalBookmarkManager::self()->root().internalElement(); + + QList childList; + + QDomNode n = subDoc.firstChild().toElement(); + while (!n.isNull()) { + QDomElement e = n.toElement(); + if (!e.isNull()) + childList.append(e); + n = n.nextSibling(); + } + + QList::Iterator it = childList.begin(); + QList::Iterator end = childList.end(); + for (; it!= end ; ++it) + root.appendChild((*it)); + } +} + +#include "moc_importers.cpp" diff --git a/keditbookmarks/importers.h b/keditbookmarks/importers.h new file mode 100644 index 00000000..681278cd --- /dev/null +++ b/keditbookmarks/importers.h @@ -0,0 +1,174 @@ +// vim: set ts=4 sts=4 sw=4 et: +/* This file is part of the KDE project + Copyright (C) 2000 David Faure + Copyright (C) 2002-2003 Alexander Kellett + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) version 3. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see +*/ + +#ifndef IMPORTERS_H +#define IMPORTERS_H + +#include "kbookmarkmodel/commands.h" +#include + +#include + +class KBookmark; + +// TODO: remove QObject base class? doesn't seem to be used. +class ImportCommand : public QObject, public QUndoCommand, public IKEBCommand +{ + Q_OBJECT +public: + ImportCommand(KBookmarkModel* model); + + virtual void import(const QString &fileName, bool folder) = 0; + + void setVisibleName(const QString& visibleName); + QString visibleName() const { return m_visibleName; } + virtual QString requestFilename() const = 0; + + static ImportCommand* performImport(KBookmarkModel* model, const QString &, QWidget *); + static ImportCommand* importerFactory(KBookmarkModel* model, const QString &); + + virtual ~ImportCommand() + {} + + virtual void redo(); + virtual void undo(); + virtual QString affectedBookmarks() const; + + QString groupAddress() const { return m_group; } + QString folder() const; + +protected: + /** + * @param fileName HTML file to import + * @param folder name of the folder to create. Empty for no creation (root()). + * @param icon icon for the new folder, if @p folder isn't empty + * @param utf8 true if the HTML is in utf-8 encoding + */ + void init(const QString &fileName, bool folder, const QString &icon, bool utf8) + { + m_fileName = fileName; + m_folder = folder; + m_icon = icon; + m_utf8 = utf8; + } + + virtual void doCreateHoldingFolder(KBookmarkGroup &bkGroup); + virtual void doExecute(const KBookmarkGroup &) = 0; + +protected: + KBookmarkModel* m_model; + QString m_visibleName; + QString m_fileName; + QString m_icon; + QString m_group; + bool m_utf8; + +private: + bool m_folder; + QUndoCommand *m_cleanUpCmd; +}; + +// part pure +class XBELImportCommand : public ImportCommand +{ +public: + XBELImportCommand(KBookmarkModel* model) : ImportCommand(model) {} + virtual void import(const QString &fileName, bool folder) = 0; + virtual QString requestFilename() const = 0; +private: + virtual void doCreateHoldingFolder(KBookmarkGroup &bkGroup); + virtual void doExecute(const KBookmarkGroup &); +}; + +class GaleonImportCommand : public XBELImportCommand +{ +public: + GaleonImportCommand(KBookmarkModel* model) : XBELImportCommand(model) { setVisibleName(i18n("Galeon")); } + virtual void import(const QString &fileName, bool folder) { + init(fileName, folder, "", false); + } + virtual QString requestFilename() const; +}; + +class KDE2ImportCommand : public XBELImportCommand +{ +public: + KDE2ImportCommand(KBookmarkModel* model) : XBELImportCommand(model) { setVisibleName(i18n("KDE")); } + virtual void import(const QString &fileName, bool folder) { + init(fileName, folder, "", false); + } + virtual QString requestFilename() const; +}; + +// part pure +class HTMLImportCommand : public ImportCommand +{ +public: + HTMLImportCommand(KBookmarkModel* model) : ImportCommand(model) {} + virtual void import(const QString &fileName, bool folder) = 0; + virtual QString requestFilename() const = 0; +private: + virtual void doExecute(const KBookmarkGroup &); +}; + +class NSImportCommand : public HTMLImportCommand +{ +public: + NSImportCommand(KBookmarkModel* model) : HTMLImportCommand(model) { setVisibleName(i18n("Netscape")); } + virtual void import(const QString &fileName, bool folder) { + init(fileName, folder, "netscape", false); + } + virtual QString requestFilename() const; +}; + +class MozImportCommand : public HTMLImportCommand +{ +public: + MozImportCommand(KBookmarkModel* model) : HTMLImportCommand(model) { setVisibleName(i18n("Mozilla")); } + virtual void import(const QString &fileName, bool folder) { + init(fileName, folder, "mozilla", true); + } + virtual QString requestFilename() const; +}; + +class IEImportCommand : public ImportCommand +{ +public: + IEImportCommand(KBookmarkModel* model) : ImportCommand(model) { setVisibleName(i18n("IE")); } + virtual void import(const QString &fileName, bool folder) { + init(fileName, folder, "", false); + } + virtual QString requestFilename() const; +private: + virtual void doExecute(const KBookmarkGroup &); +}; + +class OperaImportCommand : public ImportCommand +{ +public: + OperaImportCommand(KBookmarkModel* model) : ImportCommand(model) { setVisibleName(i18n("Opera")); } + virtual void import(const QString &fileName, bool folder) { + init(fileName, folder, "opera", false); + } + virtual QString requestFilename() const; +private: + virtual void doExecute(const KBookmarkGroup &); +}; + +#endif diff --git a/keditbookmarks/kbookmarkmerger.cpp b/keditbookmarks/kbookmarkmerger.cpp new file mode 100644 index 00000000..75f4b774 --- /dev/null +++ b/keditbookmarks/kbookmarkmerger.cpp @@ -0,0 +1,111 @@ +// -*- tab-width:4; indent-tabs-mode:t -*- +/** + * kbookmarkmerger.cpp - Copyright (C) 2005 Frerich Raabe + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +int main( int argc, char**argv ) +{ + KAboutData aboutData( "kbookmarkmerger", "keditbookmarks", ki18n( "KBookmarkMerger" ), + "1.0", ki18n( "Merges bookmarks installed by 3rd parties into the user's bookmarks" ), + KAboutData::License_BSD, + ki18n( "Copyright © 2005 Frerich Raabe" ) ); + aboutData.addAuthor( ki18n("Frerich Raabe"), ki18n( "Original author" ), + "raabe@kde.org" ); + + KCmdLineArgs::init( argc, argv, &aboutData ); + + KCmdLineOptions cmdLineOptions; + cmdLineOptions.add("+directory", ki18n( "Directory to scan for extra bookmarks" )); + KCmdLineArgs::addCmdLineOptions( cmdLineOptions ); + + QCoreApplication app(argc, argv); + + KCmdLineArgs *args = KCmdLineArgs::parsedArgs(); + if ( args->count() != 1 ) { + kError() << "No directory to scan for bookmarks specified." << endl; + return 1; + } + + KBookmarkManager *konqBookmarks = KBookmarkManager::userBookmarksManager(); + QStringList mergedFiles; + { + KBookmarkGroup root = konqBookmarks->root(); + for ( KBookmark bm = root.first(); !bm.isNull(); bm = root.next( bm ) ) { + if ( bm.isGroup() ) { + continue; + } + + QString mergedFrom = bm.metaDataItem( "merged_from" ); + if ( !mergedFrom.isNull() ) { + mergedFiles << mergedFrom; + } + } + } + + bool didMergeBookmark = false; + + QString extraBookmarksDirName = args->arg( 0 ); + QDir extraBookmarksDir( extraBookmarksDirName, "*.xml" ); + if ( !extraBookmarksDir.isReadable() ) { + kError() << "Failed to read files in directory " << extraBookmarksDirName << endl; + return 1; + } + + for ( unsigned int i = 0; i < extraBookmarksDir.count(); ++i ) { + const QString fileName = extraBookmarksDir[ i ]; + if ( mergedFiles.contains( fileName ) ) { + continue; + } + + const QString absPath = extraBookmarksDir.filePath( fileName ); + KBookmarkManager *mgr = KBookmarkManager::managerForFile( absPath, QString() ); + KBookmarkGroup root = mgr->root(); + for ( KBookmark bm = root.first(); !bm.isNull(); bm = root.next( bm ) ) { + if ( bm.isGroup() ) { + continue; + } + bm.setMetaDataItem( "merged_from", fileName ); + konqBookmarks->root().addBookmark( bm ); + didMergeBookmark = true; + } + } + + if ( didMergeBookmark ) { + konqBookmarks->emitChanged( konqBookmarks->root() ); // calls save + // see TODO in emitChanged... if it returns false, it would be nice to return 1 + // here. + } + return 0; +} + diff --git a/keditbookmarks/kbookmarkmodel/CMakeLists.txt b/keditbookmarks/kbookmarkmodel/CMakeLists.txt new file mode 100644 index 00000000..86a55d15 --- /dev/null +++ b/keditbookmarks/kbookmarkmodel/CMakeLists.txt @@ -0,0 +1,29 @@ +if(ENABLE_TESTING) + add_subdirectory(tests) +endif() + +set(kbookmarkmodel_SRCS + commandhistory.cpp + commands.cpp + model.cpp + treeitem.cpp + view.cpp +) + +add_library(kbookmarkmodel_private SHARED ${kbookmarkmodel_SRCS}) + +target_link_libraries(kbookmarkmodel_private + ${KDE4_KIO_LIBS} +) + +set_target_properties(kbookmarkmodel_private PROPERTIES + VERSION ${GENERIC_LIB_VERSION} + SOVERSION ${GENERIC_LIB_SOVERSION} +) + +generate_export_header(kbookmarkmodel_private BASE_NAME KBOOKMARKMODEL) + +install( + TARGETS kbookmarkmodel_private + ${INSTALL_TARGETS_DEFAULT_ARGS} +) diff --git a/keditbookmarks/kbookmarkmodel/commandhistory.cpp b/keditbookmarks/kbookmarkmodel/commandhistory.cpp new file mode 100644 index 00000000..256ae5df --- /dev/null +++ b/keditbookmarks/kbookmarkmodel/commandhistory.cpp @@ -0,0 +1,124 @@ +/* This file is part of the KDE project + Copyright (C) 2000, 2009 David Faure + Copyright (C) 2002-2003 Alexander Kellett + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) version 3. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see +*/ + +#include "commandhistory.h" +#include "commands.h" + +#include +#include +#include +#include + +#include +#include + +class CommandHistory::Private +{ +public: + Private() : m_manager(0), m_undoStack() {} + KBookmarkManager* m_manager; + KUndoStack m_undoStack; +}; + +CommandHistory::CommandHistory(QObject* parent) + : QObject(parent), d(new CommandHistory::Private) +{ +} + +CommandHistory::~CommandHistory() +{ + delete d; +} + +void CommandHistory::setBookmarkManager(KBookmarkManager* manager) +{ + clearHistory(); // we can't keep old commands pointing to the wrong model/manager... + d->m_manager = manager; +} + +void CommandHistory::createActions(KActionCollection *actionCollection) +{ + // TODO use QUndoView? + + QAction* undoAction = d->m_undoStack.createUndoAction(actionCollection); + disconnect(undoAction, SIGNAL(triggered()), &d->m_undoStack, 0); + connect(undoAction, SIGNAL(triggered()), this, SLOT(undo())); + + QAction* redoAction = d->m_undoStack.createRedoAction(actionCollection); + disconnect(redoAction, SIGNAL(triggered()), &d->m_undoStack, 0); + connect(redoAction, SIGNAL(triggered()), this, SLOT(redo())); +} + +void CommandHistory::undo() +{ + const int idx = d->m_undoStack.index(); + const QUndoCommand* cmd = d->m_undoStack.command(idx-1); + if (cmd) { + d->m_undoStack.undo(); + commandExecuted(cmd); + } +} + +void CommandHistory::redo() +{ + const int idx = d->m_undoStack.index(); + const QUndoCommand* cmd = d->m_undoStack.command(idx); + if (cmd) { + d->m_undoStack.redo(); + commandExecuted(cmd); + } +} + +void CommandHistory::commandExecuted(const QUndoCommand *k) +{ + const IKEBCommand * cmd = dynamic_cast(k); + Q_ASSERT(cmd); + + KBookmark bk = d->m_manager->findByAddress(cmd->affectedBookmarks()); + Q_ASSERT(bk.isGroup()); + + emit notifyCommandExecuted(bk.toGroup()); +} + +void CommandHistory::notifyDocSaved() +{ + d->m_undoStack.setClean(); +} + +void CommandHistory::addCommand(QUndoCommand *cmd) +{ + if (!cmd) + return; + d->m_undoStack.push(cmd); // calls cmd->redo() + CommandHistory::commandExecuted(cmd); +} + +void CommandHistory::clearHistory() +{ + if (d->m_undoStack.count() > 0) { + d->m_undoStack.clear(); + emit notifyCommandExecuted(d->m_manager->root()); // not really, but we still want to update the GUI + } +} + +KBookmarkManager* CommandHistory::bookmarkManager() +{ + return d->m_manager; +} + +#include "moc_commandhistory.cpp" diff --git a/keditbookmarks/kbookmarkmodel/commandhistory.h b/keditbookmarks/kbookmarkmodel/commandhistory.h new file mode 100644 index 00000000..9bc667b9 --- /dev/null +++ b/keditbookmarks/kbookmarkmodel/commandhistory.h @@ -0,0 +1,66 @@ +/* This file is part of the KDE project + Copyright (C) 2000, 2009 David Faure + Copyright (C) 2002-2003 Alexander Kellett + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) version 3. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see +*/ + +#ifndef COMMANDHISTORY_H +#define COMMANDHISTORY_H + +#include "kbookmarkmodel_export.h" + +#include +#include + +class KBookmarkGroup; +class KBookmarkManager; +class KActionCollection; + +// TODO namespacing +class KBOOKMARKMODEL_EXPORT CommandHistory : public QObject +{ + Q_OBJECT +public: + CommandHistory(QObject* parent = 0); + virtual ~CommandHistory(); + + // Call this before putting any commands into the history! + void setBookmarkManager(KBookmarkManager* manager); + KBookmarkManager* bookmarkManager(); + + void createActions(KActionCollection *collection); + + void notifyDocSaved(); + + void clearHistory(); + void addCommand(QUndoCommand *); + +Q_SIGNALS: + void notifyCommandExecuted(const KBookmarkGroup&); + +public Q_SLOTS: + void undo(); + void redo(); + +private: + void commandExecuted(const QUndoCommand *k); + +private: + class Private; + Private * const d; +}; + +#endif /* COMMANDHISTORY_H */ + diff --git a/keditbookmarks/kbookmarkmodel/commands.cpp b/keditbookmarks/kbookmarkmodel/commands.cpp new file mode 100644 index 00000000..bc1f2932 --- /dev/null +++ b/keditbookmarks/kbookmarkmodel/commands.cpp @@ -0,0 +1,539 @@ +// -*- indent-tabs-mode:nil -*- +// vim: set ts=4 sts=4 sw=4 et: +/* This file is part of the KDE project + Copyright (C) 2000 David Faure + Copyright (C) 2002-2003 Alexander Kellett + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License version 2 or at your option version 3 as published by + the Free Software Foundation. + + 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; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "commands.h" +#include "commands_p.h" +#include "model.h" +#include "kinsertionsort_p.h" + +#include +#include +#include +#include + +QString KEBMacroCommand::affectedBookmarks() const +{ + const int commandCount = childCount(); + if (commandCount == 0) { + return QString(); + } + // Need to use dynamic_cast here due to going cross-hierarchy, but it should never return 0. + int i = 0; + QString affectBook = dynamic_cast(child(i))->affectedBookmarks(); + ++i; + for(; i < commandCount; ++i) { + affectBook = KBookmark::commonParent( affectBook, dynamic_cast(child(i))->affectedBookmarks()); + } + return affectBook; +} + +DeleteManyCommand::DeleteManyCommand(KBookmarkModel* model, const QString &name, const QList & bookmarks) + : KEBMacroCommand(name) +{ + QList::const_iterator it, begin; + begin = bookmarks.constBegin(); + it = bookmarks.constEnd(); + while(begin != it) + { + --it; + new DeleteCommand(model, (*it).address(), false, this); + } +} + +//// + +CreateCommand::CreateCommand(KBookmarkModel* model, const QString &address, QUndoCommand* parent) + : QUndoCommand(parent), m_model(model), m_to(address), + m_group(false), m_separator(true), m_originalBookmark(QDomElement()) +{ + setText(i18nc("(qtundo-format)", "Insert Separator")); +} + +CreateCommand::CreateCommand(KBookmarkModel* model, const QString &address, + const QString &text, const QString &iconPath, + const KUrl &url, QUndoCommand* parent) + : QUndoCommand(parent), m_model(model), m_to(address), m_text(text), m_iconPath(iconPath), m_url(url), + m_group(false), m_separator(false), m_originalBookmark(QDomElement()) +{ + setText(i18nc("(qtundo-format)", "Create Bookmark")); +} + +CreateCommand::CreateCommand(KBookmarkModel* model, const QString &address, + const QString &text, const QString &iconPath, + bool open, QUndoCommand* parent) + : QUndoCommand(parent), m_model(model), m_to(address), m_text(text), m_iconPath(iconPath), + m_group(true), m_separator(false), m_open(open), m_originalBookmark(QDomElement()) +{ + setText(i18nc("(qtundo-format)", "Create Folder")); +} + +CreateCommand::CreateCommand(KBookmarkModel* model, const QString &address, + const KBookmark &original, const QString &name, QUndoCommand* parent) + : QUndoCommand(parent), m_model(model), m_to(address), m_group(false), m_separator(false), + m_open(false), m_originalBookmark(original), + m_originalBookmarkDocRef(m_originalBookmark.internalElement().ownerDocument()) +{ + setText(i18nc("(qtundo-format)", "Copy %1", name)); +} + +void CreateCommand::redo() +{ + QString parentAddress = KBookmark::parentAddress(m_to); + KBookmarkGroup parentGroup = + m_model->bookmarkManager()->findByAddress(parentAddress).toGroup(); + + QString previousSibling = KBookmark::previousAddress(m_to); + + // kDebug() << "previousSibling=" << previousSibling; + KBookmark prev = (previousSibling.isEmpty()) + ? KBookmark(QDomElement()) + : m_model->bookmarkManager()->findByAddress(previousSibling); + + KBookmark bk = KBookmark(QDomElement()); + const int pos = KBookmark::positionInParent(m_to); + m_model->beginInsert(parentGroup, pos, pos); + + if (m_separator) { + bk = parentGroup.createNewSeparator(); + + } else if (m_group) { + Q_ASSERT(!m_text.isEmpty()); + bk = parentGroup.createNewFolder(m_text); + bk.internalElement().setAttribute("folded", (m_open ? "no" : "yes")); + if (!m_iconPath.isEmpty()) { + bk.setIcon(m_iconPath); + } + } else if(!m_originalBookmark.isNull()) { + QDomElement element = m_originalBookmark.internalElement().cloneNode().toElement(); + bk = KBookmark(element); + parentGroup.addBookmark(bk); + } else { + bk = parentGroup.addBookmark(m_text, m_url, m_iconPath); + } + + // move to right position + parentGroup.moveBookmark(bk, prev); + if (!(text().isEmpty()) && !parentAddress.isEmpty() ) { + // open the parent (useful if it was empty) - only for manual commands + Q_ASSERT( parentGroup.internalElement().tagName() != "xbel" ); + parentGroup.internalElement().setAttribute("folded", "no"); + } + + Q_ASSERT(bk.address() == m_to); + m_model->endInsert(); +} + +QString CreateCommand::finalAddress() const +{ + Q_ASSERT( !m_to.isEmpty() ); + return m_to; +} + +void CreateCommand::undo() +{ + KBookmark bk = m_model->bookmarkManager()->findByAddress(m_to); + Q_ASSERT(!bk.isNull() && !bk.parentGroup().isNull()); + + m_model->removeBookmark(bk); +} + +QString CreateCommand::affectedBookmarks() const +{ + return KBookmark::parentAddress(m_to); +} + +/* -------------------------------------- */ + +EditCommand::EditCommand(KBookmarkModel* model, const QString & address, int col, const QString & newValue, QUndoCommand* parent) + : QUndoCommand(parent), m_model(model), mAddress(address), mCol(col) +{ + kDebug() << address << col << newValue; + if(mCol == 1) + { + const KUrl u(newValue); + if (!(u.isEmpty() && !newValue.isEmpty())) // prevent emptied line if the currently entered url is invalid + mNewValue = u.url(KUrl::LeaveTrailingSlash); + else + mNewValue = newValue; + } + else + mNewValue = newValue; + + // -2 is "toolbar" attribute change, but that's only used internally. + if (mCol == -1) + setText(i18nc("(qtundo-format)", "Icon Change")); + else if (mCol == 0) + setText(i18nc("(qtundo-format)", "Title Change")); + else if (mCol == 1) + setText(i18nc("(qtundo-format)", "URL Change")); + else if (mCol == 2) + setText(i18nc("(qtundo-format)", "Comment Change")); +} + +void EditCommand::redo() +{ + KBookmark bk = m_model->bookmarkManager()->findByAddress(mAddress); + if(mCol==-2) + { + if (mOldValue.isEmpty()) + mOldValue = bk.internalElement().attribute("toolbar"); + bk.internalElement().setAttribute("toolbar", mNewValue); + } + else if(mCol==-1) + { + if (mOldValue.isEmpty()) + mOldValue = bk.icon(); + bk.setIcon(mNewValue); + } + else if(mCol==0) + { + if (mOldValue.isEmpty()) // only the first time, not when compressing changes in modify() + mOldValue = bk.fullText(); + kDebug() << "mOldValue=" << mOldValue; + bk.setFullText(mNewValue); + } + else if(mCol==1) + { + if (mOldValue.isEmpty()) + mOldValue = bk.url().prettyUrl(); + const KUrl newUrl(mNewValue); + if (!(newUrl.isEmpty() && !mNewValue.isEmpty())) // prevent emptied line if the currently entered url is invalid + bk.setUrl(newUrl); + } + else if(mCol==2) + { + if (mOldValue.isEmpty()) + mOldValue = bk.description(); + bk.setDescription(mNewValue); + } + m_model->emitDataChanged(bk); +} + +void EditCommand::undo() +{ + kDebug() << "Setting old value" << mOldValue << "in bk" << mAddress << "col" << mCol; + KBookmark bk = m_model->bookmarkManager()->findByAddress(mAddress); + if(mCol==-2) + { + bk.internalElement().setAttribute("toolbar", mOldValue); + } + else if(mCol==-1) + { + bk.setIcon(mOldValue); + } + else if(mCol==0) + { + bk.setFullText(mOldValue); + } + else if(mCol==1) + { + bk.setUrl(KUrl(mOldValue)); + } + else if(mCol==2) + { + bk.setDescription(mOldValue); + } + m_model->emitDataChanged(bk); +} + +void EditCommand::modify(const QString &newValue) +{ + if(mCol == 1) + { + const KUrl u(newValue); + if (!(u.isEmpty() && !newValue.isEmpty())) // prevent emptied line if the currently entered url is invalid + mNewValue = u.url(KUrl::LeaveTrailingSlash); + else + mNewValue = newValue; + } + else + mNewValue = newValue; +} + +/* -------------------------------------- */ + +DeleteCommand::DeleteCommand(KBookmarkModel* model, const QString &from, bool contentOnly, QUndoCommand* parent) + : QUndoCommand(parent), m_model(model), m_from(from), m_cmd(0), m_subCmd(0), m_contentOnly(contentOnly) +{ + // NOTE - DeleteCommand needs no text, it is always embedded in a macrocommand +} + +void DeleteCommand::redo() +{ + KBookmark bk = m_model->bookmarkManager()->findByAddress(m_from); + Q_ASSERT(!bk.isNull()); + + if (m_contentOnly) { + QDomElement groupRoot = bk.internalElement(); + + QDomNode n = groupRoot.firstChild(); + while (!n.isNull()) { + QDomElement e = n.toElement(); + if (!e.isNull()) { + // kDebug() << e.tagName(); + } + QDomNode next = n.nextSibling(); + groupRoot.removeChild(n); + n = next; + } + return; + } + + // TODO - bug - unparsed xml is lost after undo, + // we must store it all therefore + +//FIXME this removes the comments, that's bad! + if (!m_cmd) { + if (bk.isGroup()) { + m_cmd = new CreateCommand(m_model, + m_from, bk.fullText(), bk.icon(), + bk.internalElement().attribute("folded") == "no"); + m_subCmd = deleteAll(m_model, bk.toGroup()); + m_subCmd->redo(); + + } else { + m_cmd = (bk.isSeparator()) + ? new CreateCommand(m_model, m_from) + : new CreateCommand(m_model, m_from, bk.fullText(), + bk.icon(), bk.url()); + } + } + m_cmd->undo(); +} + +void DeleteCommand::undo() +{ + // kDebug() << "DeleteCommand::undo " << m_from; + + if (m_contentOnly) { + // TODO - recover saved metadata + return; + } + + m_cmd->redo(); + + if (m_subCmd) { + m_subCmd->undo(); + } +} + +QString DeleteCommand::affectedBookmarks() const +{ + return KBookmark::parentAddress(m_from); +} + +KEBMacroCommand* DeleteCommand::deleteAll(KBookmarkModel* model, const KBookmarkGroup & parentGroup) +{ + KEBMacroCommand *cmd = new KEBMacroCommand(QString()); + QStringList lstToDelete; + // we need to delete from the end, to avoid index shifting + for (KBookmark bk = parentGroup.first(); + !bk.isNull(); bk = parentGroup.next(bk)) + lstToDelete.prepend(bk.address()); + for (QStringList::const_iterator it = lstToDelete.constBegin(); + it != lstToDelete.constEnd(); ++it) { + new DeleteCommand(model, (*it), false, cmd); + } + return cmd; +} + +/* -------------------------------------- */ + +MoveCommand::MoveCommand(KBookmarkModel* model, const QString &from, const QString &to, const QString &name, QUndoCommand* parent) + : QUndoCommand(parent), m_model(model), m_from(from), m_to(to), m_cc(0), m_dc(0) +{ + setText(i18nc("(qtundo-format)", "Move %1", name)); +} + +void MoveCommand::redo() +{ + // kDebug() << "moving from=" << m_from << "to=" << m_to; + + KBookmark fromBk = m_model->bookmarkManager()->findByAddress( m_from ); + + m_cc = new CreateCommand(m_model, m_to, fromBk, QString()); + m_cc->redo(); + + m_dc = new DeleteCommand(m_model, fromBk.address()); + m_dc->redo(); +} + +QString MoveCommand::finalAddress() const +{ + Q_ASSERT( !m_to.isEmpty() ); + return m_to; +} + +void MoveCommand::undo() +{ + m_dc->undo(); + m_cc->undo(); +} + +QString MoveCommand::affectedBookmarks() const +{ + return KBookmark::commonParent(KBookmark::parentAddress(m_from), KBookmark::parentAddress(m_to)); +} + +/* -------------------------------------- */ + +class SortItem { + public: + SortItem(const KBookmark & bk) : m_bk(bk) {} + + bool operator == (const SortItem & s) { + return (m_bk.internalElement() == s.m_bk.internalElement()); } + + bool isNull() const { + return m_bk.isNull(); } + + SortItem previousSibling() const { + return m_bk.parentGroup().previous(m_bk); } + + SortItem nextSibling() const { + return m_bk.parentGroup().next(m_bk); } + + const KBookmark& bookmark() const { + return m_bk; } + + private: + KBookmark m_bk; +}; + +class SortByName { + public: + static QString key(const SortItem &item) { + return (item.bookmark().isGroup() ? "a" : "b") + + (item.bookmark().fullText().toLower()); + } +}; + +/* -------------------------------------- */ + +SortCommand::SortCommand(KBookmarkModel* model, const QString &name, const QString &groupAddress, QUndoCommand* parent) + : KEBMacroCommand(name, parent), m_model(model), m_groupAddress(groupAddress) +{ +} + +void SortCommand::redo() +{ + if (childCount() == 0) { + KBookmarkGroup grp = m_model->bookmarkManager()->findByAddress(m_groupAddress).toGroup(); + Q_ASSERT(!grp.isNull()); + SortItem firstChild(grp.first()); + // this will call moveAfter, which will add + // the subcommands for moving the items + kInsertionSort + (firstChild, (*this)); + + } else { + // don't redo for second time on addCommand(cmd) + KEBMacroCommand::redo(); + } +} + +void SortCommand::moveAfter(const SortItem &moveMe, + const SortItem &afterMe) { + const QString destAddress = + afterMe.isNull() + // move as first child + ? KBookmark::parentAddress(moveMe.bookmark().address()) + "/0" + // move after "afterMe" + : KBookmark::nextAddress(afterMe.bookmark().address()); + + MoveCommand *cmd = new MoveCommand(m_model, moveMe.bookmark().address(), + destAddress, QString(), this); + cmd->redo(); +} + +void SortCommand::undo() { + KEBMacroCommand::undo(); +} + +QString SortCommand::affectedBookmarks() const +{ + return m_groupAddress; +} + +/* -------------------------------------- */ + +KEBMacroCommand* CmdGen::setAsToolbar(KBookmarkModel* model, const KBookmark &bk) +{ + KEBMacroCommand *mcmd = new KEBMacroCommand(i18nc("(qtundo-format)", "Set as Bookmark Toolbar")); + + KBookmarkGroup oldToolbar = model->bookmarkManager()->toolbar(); + if (!oldToolbar.isNull()) + { + new EditCommand(model, oldToolbar.address(), -2, "no", mcmd); //toolbar + new EditCommand(model, oldToolbar.address(), -1, "", mcmd); //icon + } + + new EditCommand(model, bk.address(), -2, "yes", mcmd); + new EditCommand(model, bk.address(), -1, "bookmark-toolbar", mcmd); + + return mcmd; +} + +KEBMacroCommand* CmdGen::insertMimeSource(KBookmarkModel* model, const QString &cmdName, const QMimeData *data, const QString &addr) +{ + KEBMacroCommand *mcmd = new KEBMacroCommand(cmdName); + QString currentAddress = addr; + QDomDocument doc; + const KBookmark::List bookmarks = KBookmark::List::fromMimeData(data, doc); + foreach (const KBookmark &bk, bookmarks) { + new CreateCommand(model, currentAddress, bk, QString(), mcmd); + currentAddress = KBookmark::nextAddress(currentAddress); + } + return mcmd; +} + + +KEBMacroCommand* CmdGen::itemsMoved(KBookmarkModel* model, const QList & items, + const QString &newAddress, bool copy) +{ + Q_ASSERT(!copy); // always called for a move, never for a copy (that's what insertMimeSource is about) + Q_UNUSED(copy); // TODO: remove + + KEBMacroCommand *mcmd = new KEBMacroCommand(copy ? i18nc("(qtundo-format)", "Copy Items") + : i18nc("(qtundo-format)", "Move Items")); + QString bkInsertAddr = newAddress; + foreach (const KBookmark &bk, items) { + new CreateCommand(model, bkInsertAddr, + KBookmark(bk.internalElement().cloneNode(true).toElement()), + bk.text(), mcmd); + bkInsertAddr = KBookmark::nextAddress(bkInsertAddr); + } + + // Do the copying, and get the updated addresses of the bookmarks to remove. + mcmd->redo(); + QStringList addresses; + foreach (const KBookmark &bk, items) { + addresses.append(bk.address()); + } + mcmd->undo(); + + foreach (const QString &address, addresses) { + new DeleteCommand(model, address, false, mcmd); + } + + return mcmd; +} diff --git a/keditbookmarks/kbookmarkmodel/commands.h b/keditbookmarks/kbookmarkmodel/commands.h new file mode 100644 index 00000000..031f4ccc --- /dev/null +++ b/keditbookmarks/kbookmarkmodel/commands.h @@ -0,0 +1,162 @@ +/* This file is part of the KDE project + Copyright (C) 2000 David Faure + Copyright (C) 2002-2003 Alexander Kellett + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) version 3. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see +*/ + +#ifndef KBOOKMARKMODEL__COMMANDS_H +#define KBOOKMARKMODEL__COMMANDS_H + +#include +#include "kbookmarkmodel_export.h" +#include + +class KBookmarkModel; + +// Interface adds the affectedBookmarks method +// Any command class should on call add those bookmarks which are +// affected by executing or unexecuting the command +// Or a common parent of the affected bookmarks +// see KBookmarkManager::notifyChange(KBookmarkGroup) +class KBOOKMARKMODEL_EXPORT IKEBCommand +{ +public: + IKEBCommand() {} + virtual ~IKEBCommand() {} + virtual QString affectedBookmarks() const = 0; + // If only QUndoCommand had setData(QVariant), we would save a lot of casting... +}; + +class KBOOKMARKMODEL_EXPORT KEBMacroCommand : public QUndoCommand, public IKEBCommand +{ +public: + explicit KEBMacroCommand(const QString &name, QUndoCommand* parent = 0) + : QUndoCommand(name, parent) {} + virtual ~KEBMacroCommand() {} + virtual QString affectedBookmarks() const; +}; + +class KBOOKMARKMODEL_EXPORT DeleteManyCommand : public KEBMacroCommand +{ +public: + DeleteManyCommand(KBookmarkModel* model, const QString &name, const QList & bookmarks); + virtual ~DeleteManyCommand() {} +}; + +class KBOOKMARKMODEL_EXPORT CreateCommand : public QUndoCommand, public IKEBCommand +{ +public: + // separator + CreateCommand(KBookmarkModel* model, const QString &address, QUndoCommand* parent = 0); + + // bookmark + CreateCommand(KBookmarkModel* model, const QString &address, + const QString &text, const QString &iconPath, + const KUrl &url, QUndoCommand* parent = 0); + + // folder + CreateCommand(KBookmarkModel* model, const QString &address, + const QString &text, const QString &iconPath, + bool open, QUndoCommand* parent = 0); + + // clone existing bookmark + CreateCommand(KBookmarkModel* model, const QString &address, + const KBookmark &original, const QString &name = QString(), QUndoCommand* parent = 0); + + QString finalAddress() const; + + virtual ~CreateCommand() {} + virtual void redo(); + virtual void undo(); + virtual QString affectedBookmarks() const; + +private: // TODO move it all to a d pointer + KBookmarkModel* m_model; + QString m_to; + QString m_text; + QString m_iconPath; + KUrl m_url; + bool m_group:1; + bool m_separator:1; + bool m_open:1; + KBookmark m_originalBookmark; + QDomDocument m_originalBookmarkDocRef; // so that it lives at least as long as m_originalBookmark +}; + +class KBOOKMARKMODEL_EXPORT EditCommand : public QUndoCommand, public IKEBCommand +{ +public: + EditCommand(KBookmarkModel* model, const QString & address, int col, const QString & newValue, QUndoCommand* parent = 0); + virtual ~EditCommand() {} + virtual void redo(); + virtual void undo(); + virtual QString affectedBookmarks() const { return KBookmark::parentAddress(mAddress); } + void modify(const QString &newValue); +private: + KBookmarkModel* m_model; + QString mAddress; + int mCol; + QString mNewValue; + QString mOldValue; +}; + +class KBOOKMARKMODEL_EXPORT DeleteCommand : public QUndoCommand, public IKEBCommand +{ +public: + explicit DeleteCommand(KBookmarkModel* model, const QString &from, bool contentOnly = false, QUndoCommand* parent = 0); + virtual ~DeleteCommand() { delete m_cmd; delete m_subCmd; } + virtual void redo(); + virtual void undo(); + virtual QString affectedBookmarks() const; + static KEBMacroCommand* deleteAll(KBookmarkModel* model, const KBookmarkGroup &parentGroup); + +private: // TODO d pointer + KBookmarkModel* m_model; + QString m_from; + QUndoCommand *m_cmd; + KEBMacroCommand *m_subCmd; + bool m_contentOnly; +}; + +class SortItem; + +class KBOOKMARKMODEL_EXPORT SortCommand : public KEBMacroCommand +{ +public: + SortCommand(KBookmarkModel* model, const QString &name, const QString &groupAddress, QUndoCommand* parent = 0); + virtual ~SortCommand() + {} + virtual void redo(); + virtual void undo(); + virtual QString affectedBookmarks() const; + // internal + void moveAfter(const SortItem &moveMe, const SortItem &afterMe); +private: + KBookmarkModel* m_model; + QString m_groupAddress; +}; + +// TODO RENAME -- or maybe move these to KBookmarkModel? +class KBOOKMARKMODEL_EXPORT CmdGen { +public: + static KEBMacroCommand* setAsToolbar(KBookmarkModel* model, const KBookmark &bk); + static KEBMacroCommand* insertMimeSource(KBookmarkModel* model, const QString &cmdName, const QMimeData *data, const QString &addr); + // TODO remove "bool copy" + static KEBMacroCommand* itemsMoved(KBookmarkModel* model, const QList & items, const QString &newAddress, bool copy); +private: + CmdGen() {} +}; + +#endif diff --git a/keditbookmarks/kbookmarkmodel/commands_p.h b/keditbookmarks/kbookmarkmodel/commands_p.h new file mode 100644 index 00000000..97840dfb --- /dev/null +++ b/keditbookmarks/kbookmarkmodel/commands_p.h @@ -0,0 +1,40 @@ +/* This file is part of the KDE project + Copyright (C) 2000, 2010 David Faure + Copyright (C) 2002-2003 Alexander Kellett + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) version 3. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see +*/ + +#ifndef KBOOKMARKMODEL_COMMANDS_P_H +#define KBOOKMARKMODEL_COMMANDS_P_H + +// Used internally by the "sort" command +class MoveCommand : public QUndoCommand, public IKEBCommand +{ +public: + MoveCommand(KBookmarkModel* model, const QString &from, const QString &to, const QString &name = QString(), QUndoCommand* parent = 0); + QString finalAddress() const; + virtual ~MoveCommand() {} + virtual void redo(); + virtual void undo(); + virtual QString affectedBookmarks() const; +private: + KBookmarkModel* m_model; + QString m_from; + QString m_to; + CreateCommand * m_cc; + DeleteCommand * m_dc; +}; + +#endif /* KBOOKMARKMODEL_COMMANDS_P_H */ diff --git a/keditbookmarks/kbookmarkmodel/kinsertionsort_p.h b/keditbookmarks/kbookmarkmodel/kinsertionsort_p.h new file mode 100644 index 00000000..db7bc489 --- /dev/null +++ b/keditbookmarks/kbookmarkmodel/kinsertionsort_p.h @@ -0,0 +1,63 @@ +/* This file is part of the KDE project + Copyright (C) 2000 David Faure + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef KINSERTIONSORT_H +#define KINSERTIONSORT_H + +#include + +/** + * A template-based insertion sort algorithm, but not really 100% + * generic. It is mostly written for lists, not for arrays. + * + * A good reason to use insertion sort over faster algorithms like + * heap sort or quick sort, is that it minimizes the number of + * movements of the items. This is important in applications which support + * undo, because the number of commands is kept to a minimum. + */ + +// Item must define isNull(), previousSibling(), nextSibling() +// SortHelper must define moveAfter( const Item &, const Item & ) +// Criteria must define static Key key(const Item &) +template +inline void kInsertionSort( Item& firstChild, SortHelper& sortHelper ) +{ + if (firstChild.isNull()) return; + Item j = firstChild.nextSibling(); + while ( !j.isNull() ) + { + Key key = Criteria::key(j); + //kDebug() << "Looking at j=" << key; + // Insert A[j] into the sorted sequence A[1..j-1] + Item i = j.previousSibling(); + Item next = j.nextSibling(); + bool moved = false; + while ( !i.isNull() && Criteria::key(i) > key ) + { + i = i.previousSibling(); + moved = true; + } + if ( moved ) { + //kDebug() << "moveAfter(" << Criteria::key(j) << "," << (i.isNull() ? "null" : Criteria::key(i)) << ")"; + sortHelper.moveAfter( j, i ); // move j right after i. If i is null, move to first position. + } + j = next; + //kDebug() << "Now j is" << Criteria::key(next); + } +} + +#endif diff --git a/keditbookmarks/kbookmarkmodel/model.cpp b/keditbookmarks/kbookmarkmodel/model.cpp new file mode 100644 index 00000000..ffabb2e1 --- /dev/null +++ b/keditbookmarks/kbookmarkmodel/model.cpp @@ -0,0 +1,488 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Daniel Teske + Copyright (C) 2010 David Faure + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License version 2 or at your option version 3 as published by + the Free Software Foundation. + + 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; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "model.h" +#include "treeitem_p.h" +#include "commands.h" +#include "commandhistory.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class KBookmarkModel::Private +{ +public: + Private(KBookmarkModel* qq, const KBookmark& root, CommandHistory* commandHistory) + : q(qq), mRoot(root), mCommandHistory(commandHistory), mInsertionData(0), mIgnoreNext(0) + { + mRootItem = new TreeItem(root, 0); + } + ~Private() + { + delete mRootItem; + mRootItem = 0; + } + + void _kd_slotBookmarksChanged(const QString &groupAddress, const QString &caller = QString()); + + KBookmarkModel* q; + TreeItem * mRootItem; + KBookmark mRoot; + CommandHistory* mCommandHistory; + + class InsertionData { + public: + InsertionData(const QModelIndex& parent, int first, int last) + : mFirst(first), mLast(last) + { + mParentItem = static_cast(parent.internalPointer()); + } + void insertChildren() + { + mParentItem->insertChildren(mFirst, mLast); + } + TreeItem* mParentItem; + int mFirst; + int mLast; + }; + InsertionData* mInsertionData; + int mIgnoreNext; +}; + +KBookmarkModel::KBookmarkModel(const KBookmark& root, CommandHistory* commandHistory, QObject* parent) + : QAbstractItemModel(parent), d(new Private(this, root, commandHistory)) +{ + connect(commandHistory, SIGNAL(notifyCommandExecuted(KBookmarkGroup)), this, SLOT(notifyManagers(KBookmarkGroup))); + Q_ASSERT(bookmarkManager()); + // update when the model updates after a D-Bus signal, coming from this + // process or from another one + connect(bookmarkManager(), SIGNAL(changed(QString,QString)), + this, SLOT(_kd_slotBookmarksChanged(QString,QString))); +} + +void KBookmarkModel::setRoot(const KBookmark& root) +{ + d->mRoot = root; + resetModel(); +} + +KBookmarkModel::~KBookmarkModel() +{ + delete d; +} + +void KBookmarkModel::resetModel() +{ + beginResetModel(); + delete d->mRootItem; + d->mRootItem = new TreeItem(d->mRoot, 0); + endResetModel(); +} + +QVariant KBookmarkModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + + //Text + if (role == Qt::DisplayRole || role == Qt::EditRole) { + const KBookmark bk = bookmarkForIndex(index); + if (bk.address().isEmpty()) { + if (index.column() == NameColumnId) + return QVariant(i18nc("name of the container of all browser bookmarks", "Bookmarks")); + else + return QVariant(); + } + + switch(index.column()) { + case NameColumnId: + return bk.fullText(); + case UrlColumnId: + return bk.url().pathOrUrl(); + case CommentColumnId: + return bk.description(); + case StatusColumnId: { + QString text1 = bk.metaDataItem("favstate"); // favicon state + QString text2 = bk.metaDataItem("linkstate"); + if (text1.isEmpty() || text2.isEmpty()) + return QVariant( text1 + text2 ); + else + return QVariant( text1 + " -- " + text2 ); + } + default: + return QVariant(); //can't happen + } + } + + //Icon + if (role == Qt::DecorationRole && index.column() == NameColumnId) { + KBookmark bk = bookmarkForIndex(index); + if (bk.address().isEmpty()) + return KIcon("bookmarks"); + return KIcon(bk.icon()); + } + + //Special roles + if (role == KBookmarkRole) { + KBookmark bk = bookmarkForIndex(index); + return QVariant::fromValue(bk); + } + return QVariant(); +} + +//FIXME QModelIndex KBookmarkModel::buddy(const QModelIndex & index) //return parent for empty folder padders +// no empty folder padders atm + + +Qt::ItemFlags KBookmarkModel::flags(const QModelIndex &index) const +{ + const Qt::ItemFlags baseFlags = QAbstractItemModel::flags(index); + + if (!index.isValid()) + return (Qt::ItemIsDropEnabled | baseFlags); + + static const Qt::ItemFlags groupFlags = Qt::ItemIsDropEnabled; + static const Qt::ItemFlags groupDragEditFlags = groupFlags | Qt::ItemIsDragEnabled | Qt::ItemIsEditable; + static const Qt::ItemFlags groupEditFlags = groupFlags | Qt::ItemIsEditable; + static const Qt::ItemFlags rootFlags = groupFlags; + static const Qt::ItemFlags bookmarkFlags = 0; + static const Qt::ItemFlags bookmarkDragEditFlags = bookmarkFlags | Qt::ItemIsDragEnabled | Qt::ItemIsEditable; + static const Qt::ItemFlags bookmarkEditFlags = bookmarkFlags | Qt::ItemIsEditable; + + Qt::ItemFlags result = baseFlags; + + const int column = index.column(); + const KBookmark bookmark = bookmarkForIndex(index); + if (bookmark.isGroup()) + { + const bool isRoot = bookmark.address().isEmpty(); + result |= + (isRoot) ? rootFlags : + (column == NameColumnId) ? groupDragEditFlags : + (column == CommentColumnId) ? groupEditFlags : + /*else*/ groupFlags; + } + else + { + result |= + (column == NameColumnId) ? bookmarkDragEditFlags : + (column != StatusColumnId) ? bookmarkEditFlags + /* else */ : bookmarkFlags; + } + + return result; +} + +bool KBookmarkModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if (index.isValid() && role == Qt::EditRole) + { + kDebug() << value.toString(); + d->mCommandHistory->addCommand(new EditCommand(this, bookmarkForIndex(index).address(), index.column(), value.toString())); + return true; + } + return false; +} + +QVariant KBookmarkModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (role == Qt::DisplayRole && orientation == Qt::Horizontal) + { + QString result; + switch(section) + { + case NameColumnId: + result = i18nc("@title:column name of a bookmark", + "Name"); + break; + case UrlColumnId: + result = i18nc("@title:column name of a bookmark", + "Location"); + break; + case CommentColumnId: + result = i18nc("@title:column comment for a bookmark", + "Comment"); + break; + case StatusColumnId: + result = i18nc("@title:column status of a bookmark", + "Status"); + break; + } + return result; + } + else + return QVariant(); +} + +QModelIndex KBookmarkModel::index(int row, int column, const QModelIndex &parent) const +{ + if (!parent.isValid()) + return createIndex(row, column, d->mRootItem); + + TreeItem * item = static_cast(parent.internalPointer()); + if (row == item->childCount()) {// Received drop below last row, simulate drop on last row + return createIndex(row-1, column, item->child(row-1)); + } + + return createIndex(row, column, item->child(row)); +} + +QModelIndex KBookmarkModel::parent(const QModelIndex &index) const +{ + if (!index.isValid()) { + // qt asks for the parent of an invalid parent + // either we are in a inconsistent case or more likely + // we are dragging and dropping and qt didn't check + // what itemAt() returned + return index; + } + KBookmark bk = bookmarkForIndex(index);; + const QString rootAddress = d->mRoot.address(); + + if (bk.address() == rootAddress) + return QModelIndex(); + + KBookmarkGroup parent = bk.parentGroup(); + TreeItem * item = static_cast(index.internalPointer()); + if (parent.address() != rootAddress) + return createIndex(parent.positionInParent(), 0, item->parent()); + else //parent is root + return createIndex(0, 0, item->parent()); +} + +int KBookmarkModel::rowCount(const QModelIndex &parent) const +{ + if (parent.isValid()) + return static_cast(parent.internalPointer())->childCount(); + else //root case + return 1; // Only one child: "Bookmarks" +} + +int KBookmarkModel::columnCount(const QModelIndex &) const +{ + return NoOfColumnIds; +} + +QModelIndex KBookmarkModel::indexForBookmark(const KBookmark& bk) const +{ + return createIndex(KBookmark::positionInParent(bk.address()) , 0, d->mRootItem->treeItemForBookmark(bk)); +} + +void KBookmarkModel::emitDataChanged(const KBookmark& bk) +{ + QModelIndex idx = indexForBookmark(bk); + kDebug() << idx; + emit dataChanged(idx, idx.sibling(idx.row(), columnCount()-1)); +} + +static const char* s_mime_bookmark_addresses = "application/x-kde-bookmarkaddresses"; + +QMimeData * KBookmarkModel::mimeData(const QModelIndexList & indexes) const +{ + QMimeData *mimeData = new QMimeData; + KBookmark::List bookmarks; + QByteArray addresses; + + Q_FOREACH(const QModelIndex& it, indexes) { + if (it.column() == NameColumnId) { + bookmarks.push_back(bookmarkForIndex(it)); + if (!addresses.isEmpty()) + addresses.append(';'); + addresses.append(bookmarkForIndex(it).address().toLatin1()); + kDebug() << "appended" << bookmarkForIndex(it).address(); + } + } + + bookmarks.populateMimeData(mimeData); + mimeData->setData(s_mime_bookmark_addresses, addresses); + return mimeData; +} + +Qt::DropActions KBookmarkModel::supportedDropActions() const +{ + return Qt::CopyAction | Qt::MoveAction; +} + +QStringList KBookmarkModel::mimeTypes() const +{ + return KBookmark::List::mimeDataTypes(); +} + +bool KBookmarkModel::dropMimeData(const QMimeData * data, Qt::DropAction action, int row, int column, const QModelIndex & parent) +{ + QModelIndex dropDestIndex; + bool isInsertBetweenOp = false; + if (row == -1) { + dropDestIndex = parent; + } else { + isInsertBetweenOp = true; + dropDestIndex = index(row, column, parent); + } + + KBookmark dropDestBookmark = bookmarkForIndex(dropDestIndex); + if (dropDestBookmark.isNull()) { + // Presumably an invalid index: assume we want to place this in the root bookmark + // folder. + dropDestBookmark = d->mRoot; + } + + QString addr = dropDestBookmark.address(); + if (dropDestBookmark.isGroup() && !isInsertBetweenOp) { + addr += "/0"; + } + // bookmarkForIndex(...) does not distinguish between the last item in the folder + // and the point *after* the last item in the folder (and its hard to see how to + // modify it so it does), so do the check here. + if (isInsertBetweenOp && row == dropDestBookmark.positionInParent() + 1) + { + // Attempting to insert underneath the last item in a folder; adjust the address. + addr = KBookmark::nextAddress(addr); + } + + if (action == Qt::CopyAction) { + KEBMacroCommand * cmd = CmdGen::insertMimeSource(this, "Copy", data, addr); + d->mCommandHistory->addCommand(cmd); + } else if (action == Qt::MoveAction) { + if (data->hasFormat(s_mime_bookmark_addresses)) { + KBookmark::List bookmarks; + QList addresses = data->data(s_mime_bookmark_addresses).split(';'); + qSort(addresses); + Q_FOREACH(const QByteArray& address, addresses) { + KBookmark bk = bookmarkManager()->findByAddress(QString::fromLatin1(address)); + kDebug() << "Extracted bookmark:" << bk.address(); + bookmarks.prepend(bk); // reverse order, so that we don't invalidate addresses (#287038) + } + + KEBMacroCommand * cmd = CmdGen::itemsMoved(this, bookmarks, addr, false); + d->mCommandHistory->addCommand(cmd); + } else { + kDebug()<<"NO FORMAT"; + KEBMacroCommand * cmd = CmdGen::insertMimeSource(this, "Copy", data, addr); + d->mCommandHistory->addCommand(cmd); + } + } + + return true; +} + +KBookmark KBookmarkModel::bookmarkForIndex(const QModelIndex& index) const +{ + if (!index.isValid()) { + return KBookmark(); + } + return static_cast(index.internalPointer())->bookmark(); +} + +void KBookmarkModel::beginInsert(const KBookmarkGroup& group, int first, int last) +{ + Q_ASSERT(!d->mInsertionData); + const QModelIndex parent = indexForBookmark(group); + d->mInsertionData = new Private::InsertionData(parent, first, last); + beginInsertRows(parent, first, last); +} + +void KBookmarkModel::endInsert() +{ + Q_ASSERT(d->mInsertionData); + d->mInsertionData->insertChildren(); + delete d->mInsertionData; + d->mInsertionData = 0; + endInsertRows(); +} + +#if 0 // Probably correct, but not needed at the moment +void KBookmarkModel::removeBookmarks(KBookmarkGroup parent, int first, int last) +{ + const QModelIndex parentIndex = indexForBookmark(parent); + beginRemoveRows(parentIndex, first, last); + TreeItem* parentItem = static_cast(parentIndex.internalPointer()); + + // Go to the last bookmark to remove + KBookmark bk = parent.first(); + for (int i = 1; i < last; ++i) + bk = parent.next(bk); + // Then remove bookmarks, iterating backwards until 'first' + // (so that numbering still works) + for (int i = last; i >= first; --i) { + KBookmark prev = parent.previous(bk); + parent.deleteBookmark(bk); + bk = prev; + } + + parentItem->deleteChildren(first, last); + endRemoveRows(); +} +#endif + +void KBookmarkModel::removeBookmark(KBookmark bookmark) +{ + KBookmarkGroup parentGroup = bookmark.parentGroup(); + const QModelIndex parentIndex = indexForBookmark(parentGroup); + const int pos = bookmark.positionInParent(); + beginRemoveRows(parentIndex, pos, pos); + TreeItem* parentItem = static_cast(parentIndex.internalPointer()); + + parentGroup.deleteBookmark(bookmark); + + parentItem->deleteChildren(pos, pos); + endRemoveRows(); +} + +CommandHistory* KBookmarkModel::commandHistory() +{ + return d->mCommandHistory; +} + +KBookmarkManager* KBookmarkModel::bookmarkManager() +{ + return d->mCommandHistory->bookmarkManager(); +} + +void KBookmarkModel::Private::_kd_slotBookmarksChanged(const QString& groupAddress, const QString& caller) +{ + Q_UNUSED(groupAddress); + Q_UNUSED(caller); + //kDebug() << "_kd_slotBookmarksChanged" << groupAddress << "caller=" << caller << "mIgnoreNext=" << mIgnoreNext; + if (mIgnoreNext > 0) { // We ignore the first changed signal after every change we did + --mIgnoreNext; + return; + } + + //kDebug() << " setRoot!"; + q->setRoot(q->bookmarkManager()->root()); + + mCommandHistory->clearHistory(); +} + +void KBookmarkModel::notifyManagers(const KBookmarkGroup& grp) +{ + ++d->mIgnoreNext; + //kDebug() << "notifyManagers -> mIgnoreNext=" << d->mIgnoreNext; + bookmarkManager()->emitChanged(grp); +} + +#include "moc_model.cpp" diff --git a/keditbookmarks/kbookmarkmodel/model.h b/keditbookmarks/kbookmarkmodel/model.h new file mode 100644 index 00000000..742746fc --- /dev/null +++ b/keditbookmarks/kbookmarkmodel/model.h @@ -0,0 +1,97 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Daniel Teske + Copyright (C) 2010 David Faure + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) version 3. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see +*/ + +#ifndef BOOKMARKMODEL_MODEL_H +#define BOOKMARKMODEL_MODEL_H + +#include +#include "kbookmarkmodel_export.h" + +class CommandHistory; +class KBookmarkGroup; +class KBookmarkManager; +class KBookmark; + +class KBOOKMARKMODEL_EXPORT KBookmarkModel : public QAbstractItemModel +{ + Q_OBJECT + + enum ColumnIds + { + NameColumnId = 0, + UrlColumnId = 1, + CommentColumnId = 2, + StatusColumnId = 3, + LastColumnId = 3, + NoOfColumnIds = LastColumnId+1 + }; + +public: + KBookmarkModel(const KBookmark& root, CommandHistory* commandHistory, QObject* parent = 0); + void setRoot(const KBookmark& root); + + virtual ~KBookmarkModel(); + + KBookmarkManager* bookmarkManager(); + CommandHistory* commandHistory(); + + enum AdditionalRoles { + // Note: use printf "0x%08X\n" $(($RANDOM*$RANDOM)) + // to define additional roles. + KBookmarkRole = 0x161BEC30 + }; + + //reimplemented functions + virtual QVariant data(const QModelIndex &index, int role) const; + virtual Qt::ItemFlags flags(const QModelIndex &index) const; + virtual QVariant headerData(int section, Qt::Orientation, int role = Qt::DisplayRole) const; + virtual QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const; + virtual QModelIndex parent(const QModelIndex &index) const; + virtual bool setData(const QModelIndex &index, const QVariant &value, int role); + virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; + virtual int columnCount(const QModelIndex &parent = QModelIndex()) const; + virtual void resetModel(); + + QModelIndex indexForBookmark(const KBookmark& bk) const; + KBookmark bookmarkForIndex(const QModelIndex& index) const; + void emitDataChanged(const KBookmark& bk); + + /// Call this before inserting items into the bookmark group + void beginInsert(const KBookmarkGroup& group, int first, int last); + /// Call this after item insertion is done + void endInsert(); + + /// Remove the bookmark + void removeBookmark(KBookmark bookmark); + + //drag and drop + virtual bool dropMimeData(const QMimeData * data, Qt::DropAction action, int row, int column, const QModelIndex & parent); + virtual QStringList mimeTypes() const; + virtual QMimeData * mimeData(const QModelIndexList & indexes) const; + virtual Qt::DropActions supportedDropActions() const; + +public Q_SLOTS: + void notifyManagers(const KBookmarkGroup& grp); + +private: + class Private; + Private * const d; + Q_PRIVATE_SLOT(d, void _kd_slotBookmarksChanged(const QString &groupAddress, const QString &caller = QString())) +}; + +#endif diff --git a/keditbookmarks/kbookmarkmodel/tests/CMakeLists.txt b/keditbookmarks/kbookmarkmodel/tests/CMakeLists.txt new file mode 100644 index 00000000..c5891930 --- /dev/null +++ b/keditbookmarks/kbookmarkmodel/tests/CMakeLists.txt @@ -0,0 +1,13 @@ +include_directories( + ${KDE4_KIO_INCLUDES} + ../.. +) + +###### kbookmarkmodeltest ###### + +kde4_add_test(kbookmarkmodeltest kbookmarkmodeltest.cpp) +target_link_libraries(kbookmarkmodeltest + kbookmarkmodel_private + ${KDE4_KIO_LIBS} + ${QT_QTTEST_LIBRARY} +) diff --git a/keditbookmarks/kbookmarkmodel/tests/kbookmarkmodeltest.cpp b/keditbookmarks/kbookmarkmodel/tests/kbookmarkmodeltest.cpp new file mode 100644 index 00000000..7b271001 --- /dev/null +++ b/keditbookmarks/kbookmarkmodel/tests/kbookmarkmodeltest.cpp @@ -0,0 +1,246 @@ +/* This file is part of the KDE libraries + * Copyright 2010 David Faure + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2 of the License or ( at + * your option ) version 3 or, at the discretion of KDE e.V. ( which shall + * act as a proxy as in section 14 of the GPLv3 ), any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include + +#include "kbookmarkmodel/commandhistory.h" +#include "kbookmarkmodel/model.h" +#include "kbookmarkmodel/commands.h" // TODO provide public API in the model instead? + +// Return a list of all bookmark addresses or urls in a KBookmarkManager. +class BookmarkLister : public KBookmarkGroupTraverser +{ +public: + BookmarkLister(const KBookmarkGroup& root) { + traverse(root); + } + static QStringList addressList(KBookmarkManager* mgr) { + BookmarkLister lister(mgr->root()); + return lister.m_addressList; + } + static QStringList urlList(KBookmarkManager* mgr) { + BookmarkLister lister(mgr->root()); + return lister.m_urlList; + } + static QStringList titleList(KBookmarkManager* mgr) { + BookmarkLister lister(mgr->root()); + return lister.m_titleList; + } + virtual void visit(const KBookmark& bk) { + m_addressList.append(bk.address()); + m_urlList.append(bk.url().url()); + m_titleList.append(bk.text()); + } + virtual void visitEnter(const KBookmarkGroup& group) { + m_addressList.append(group.address() + '/'); + m_titleList.append(group.text()); + } + +private: + QStringList m_addressList; + QStringList m_urlList; + QStringList m_titleList; +}; + +class KBookmarkModelTest : public QObject +{ + Q_OBJECT +public: + KBookmarkModelTest() : m_collection(this) {} +private: + +private Q_SLOTS: + void initTestCase() + { + const QString filename = KStandardDirs::locateLocal("data", QLatin1String("konqueror/bookmarks.xml")); + QFile::remove(filename); + m_bookmarkManager = KBookmarkManager::managerForFile(filename, QString()); + m_cmdHistory = new CommandHistory(this); + m_cmdHistory->setBookmarkManager(m_bookmarkManager); + QCOMPARE(BookmarkLister::addressList(m_bookmarkManager), QStringList()); + m_model = new KBookmarkModel(m_bookmarkManager->root(), m_cmdHistory, this); + QCOMPARE(m_model->rowCount(), 1); // the toplevel "Bookmarks" toplevel item + m_rootIndex = m_model->index(0, 0); + QVERIFY(m_rootIndex.isValid()); + QCOMPARE(m_model->rowCount(m_rootIndex), 0); + m_cmdHistory->createActions(&m_collection); + } + + // The commands modify the model, so the test code uses the commands + void testAddBookmark() + { + CreateCommand* cmd = new CreateCommand(m_model, "/0", "test_bk", "www", KUrl("http://www.kde.org")); + cmd->redo(); + QCOMPARE(BookmarkLister::addressList(m_bookmarkManager), QStringList() << "/0"); + QCOMPARE(BookmarkLister::urlList(m_bookmarkManager), QStringList() << "http://www.kde.org"); + QCOMPARE(m_model->rowCount(m_rootIndex), 1); + cmd->undo(); + QCOMPARE(BookmarkLister::addressList(m_bookmarkManager), QStringList()); + QCOMPARE(m_model->rowCount(m_rootIndex), 0); + delete cmd; + } + + void testDeleteBookmark() + { + CreateCommand* cmd = new CreateCommand(m_model, "/0", "test_bk", "www", KUrl("http://www.kde.org")); + cmd->redo(); + QCOMPARE(BookmarkLister::addressList(m_bookmarkManager), QStringList() << "/0"); + DeleteCommand* deleteCmd = new DeleteCommand(m_model, "/0"); + deleteCmd->redo(); + QCOMPARE(BookmarkLister::addressList(m_bookmarkManager), QStringList()); + deleteCmd->undo(); + QCOMPARE(BookmarkLister::addressList(m_bookmarkManager), QStringList() << "/0"); + deleteCmd->redo(); + QCOMPARE(BookmarkLister::addressList(m_bookmarkManager), QStringList()); + + delete cmd; + delete deleteCmd; + } + + void testCreateFolder() // and test moving stuff around + { + CreateCommand* folderCmd = new CreateCommand(m_model, "/0", "folder", "folder", true /*open*/); + m_cmdHistory->addCommand(folderCmd); // calls redo + QCOMPARE(BookmarkLister::addressList(m_bookmarkManager), QStringList() << "/0/"); + QCOMPARE(m_model->rowCount(m_rootIndex), 1); + + const QString kde = "http://www.kde.org"; + CreateCommand* cmd = new CreateCommand(m_model, "/0/0", "test_bk", "www", KUrl(kde)); + m_cmdHistory->addCommand(cmd); // calls redo + QCOMPARE(BookmarkLister::addressList(m_bookmarkManager), QStringList() << "/0/" << "/0/0"); + + // Insert before this bookmark + const QString first = "http://first.example.com"; + m_cmdHistory->addCommand(new CreateCommand(m_model, "/0/0", "first_bk", "www", KUrl(first))); + QCOMPARE(BookmarkLister::addressList(m_bookmarkManager), QStringList() << "/0/" << "/0/0" << "/0/1"); + QCOMPARE(BookmarkLister::urlList(m_bookmarkManager), QStringList() << first << kde); + + // Move the kde bookmark before the first bookmark + KBookmark kdeBookmark = m_bookmarkManager->findByAddress("/0/1"); + QCOMPARE(kdeBookmark.url().url(), kde); + QModelIndex kdeIndex = m_model->indexForBookmark(kdeBookmark); + QCOMPARE(kdeIndex.row(), 1); + QCOMPARE(m_model->rowCount(kdeIndex.parent()), 2); + + QMimeData* mimeData = m_model->mimeData(QModelIndexList() << kdeIndex); + bool ok = m_model->dropMimeData(mimeData, Qt::MoveAction, 0, 0, kdeIndex.parent()); + QVERIFY(ok); + QCOMPARE(BookmarkLister::addressList(m_bookmarkManager), QStringList() << "/0/" << "/0/0" << "/0/1"); + QCOMPARE(BookmarkLister::urlList(m_bookmarkManager), QStringList() << kde << first); + delete mimeData; + + // Move the kde bookmark after the bookmark called 'first' + kdeBookmark = m_bookmarkManager->findByAddress("/0/0"); + kdeIndex = m_model->indexForBookmark(kdeBookmark); + QCOMPARE(kdeIndex.row(), 0); + mimeData = m_model->mimeData(QModelIndexList() << kdeIndex); + ok = m_model->dropMimeData(mimeData, Qt::MoveAction, 2, 0, kdeIndex.parent()); + QVERIFY(ok); + QCOMPARE(BookmarkLister::urlList(m_bookmarkManager), QStringList() << first << kde); + delete mimeData; + + // Create new folder, then move both bookmarks into it (#287038) + m_cmdHistory->addCommand(new CreateCommand(m_model, "/1", "folder2", "folder2", true)); + QCOMPARE(BookmarkLister::addressList(m_bookmarkManager), QStringList() << "/0/" << "/0/0" << "/0/1" << "/1/"); + QCOMPARE(m_model->rowCount(m_rootIndex), 2); + + moveTwoBookmarks("/0/0", "/0/1", "/1"); + QCOMPARE(BookmarkLister::addressList(m_bookmarkManager), QStringList() << "/0/" << "/1/" << "/1/0" << "/1/1"); + QCOMPARE(BookmarkLister::urlList(m_bookmarkManager), QStringList() << kde << first); + + // Move bookmarks from /1 into subfolder /1/2 (which will become /1/0) + m_cmdHistory->addCommand(new CreateCommand(m_model, "/1/2", "subfolder", "subfolder", true)); + QCOMPARE(BookmarkLister::addressList(m_bookmarkManager), QStringList() << "/0/" << "/1/" << "/1/0" << "/1/1" << "/1/2/"); + moveTwoBookmarks("/1/0", "/1/1", "/1/2"); + QCOMPARE(BookmarkLister::addressList(m_bookmarkManager), QStringList() << "/0/" << "/1/" << "/1/0/" << "/1/0/0" << "/1/0/1"); + + // Move them up again + moveTwoBookmarks("/1/0/0", "/1/0/1", "/1"); + QCOMPARE(BookmarkLister::addressList(m_bookmarkManager), QStringList() << "/0/" << "/1/" << "/1/0" << "/1/1" << "/1/2/"); + + undoAll(); + } + + void testSort() + { + QCOMPARE(BookmarkLister::addressList(m_bookmarkManager), QStringList()); + CreateCommand* folderCmd = new CreateCommand(m_model, "/0", "folder", "folder", true /*open*/); + m_cmdHistory->addCommand(folderCmd); // calls redo + const QString kde = "http://www.kde.org"; + QStringList bookmarks; + bookmarks << "Faure" << "Web" << "Kde" << "Avatar" << "David"; + for (int i = 0; i < bookmarks.count(); ++i) { + m_cmdHistory->addCommand(new CreateCommand(m_model, "/0/" + QString::number(i), bookmarks[i], "www", KUrl(kde))); + } + const QStringList addresses = BookmarkLister::addressList(m_bookmarkManager); + //kDebug() << addresses; + const QStringList origTitleList = BookmarkLister::titleList(m_bookmarkManager); + QCOMPARE(addresses.count(), bookmarks.count() + 1 /* parent folder */); + SortCommand* sortCmd = new SortCommand(m_model, "Sort", "/0"); + m_cmdHistory->addCommand(sortCmd); + QStringList expectedTitleList = bookmarks; + expectedTitleList.sort(); + expectedTitleList.prepend("folder"); + const QStringList sortedTitles = BookmarkLister::titleList(m_bookmarkManager); + //kDebug() << sortedTitles; + QCOMPARE(sortedTitles, expectedTitleList); + + sortCmd->undo(); + QCOMPARE(BookmarkLister::titleList(m_bookmarkManager), origTitleList); + sortCmd->redo(); + undoAll(); + } + +private: + void moveTwoBookmarks(const QString &src1, const QString &src2, const QString &dest) + { + const QModelIndex firstIndex = m_model->indexForBookmark(m_bookmarkManager->findByAddress(src1)); + const QModelIndex secondIndex = m_model->indexForBookmark(m_bookmarkManager->findByAddress(src2)); + QMimeData *mimeData = m_model->mimeData(QModelIndexList() << firstIndex << secondIndex); + QModelIndex folder2Index = m_model->indexForBookmark(m_bookmarkManager->findByAddress(dest)); + QVERIFY(m_model->dropMimeData(mimeData, Qt::MoveAction, -1, 0, folder2Index)); + delete mimeData; + } + + void undoAll() + { + QAction* undoAction = m_collection.action(KStandardAction::name(KStandardAction::Undo)); + QVERIFY(undoAction); + while (undoAction->isEnabled()) { + undoAction->trigger(); + } + QCOMPARE(BookmarkLister::addressList(m_bookmarkManager), QStringList()); + } + + KBookmarkManager* m_bookmarkManager; + KBookmarkModel* m_model; + CommandHistory* m_cmdHistory; + KActionCollection m_collection; + QModelIndex m_rootIndex; // the index of the "Bookmarks" root +}; + +QTEST_KDEMAIN( KBookmarkModelTest, GUI /*needed by the kactions*/ ) + +#include "kbookmarkmodeltest.moc" diff --git a/keditbookmarks/kbookmarkmodel/treeitem.cpp b/keditbookmarks/kbookmarkmodel/treeitem.cpp new file mode 100644 index 00000000..d3cd7c1d --- /dev/null +++ b/keditbookmarks/kbookmarkmodel/treeitem.cpp @@ -0,0 +1,129 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Daniel Teske + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) version 3. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see +*/ + +#include "treeitem_p.h" +#include +#include + +TreeItem::TreeItem(const KBookmark& bk, TreeItem * parent) + : mParent(parent), mBookmark(bk), mInitDone(false) +{ +} + +TreeItem::~TreeItem() +{ + qDeleteAll(children); + children.clear(); +} + +TreeItem * TreeItem::child(int row) +{ + if(!mInitDone) + initChildren(); + if(row < 0 || row >= children.count()) + return parent(); + return children.at(row); +} + +int TreeItem::childCount() +{ + if(!mInitDone) + initChildren(); + return children.count(); +} + +TreeItem * TreeItem::parent() const +{ + return mParent; +} + +void TreeItem::insertChildren(int first, int last) +{ + // Find child number last + KBookmarkGroup parent = bookmark().toGroup(); + KBookmark child = parent.first(); + for(int j=0; j < last; ++j) + child = parent.next(child); + + //insert children + int i = last; + do + { + children.insert(i, new TreeItem(child, this)); + child = parent.previous(child); + --i; + } while(i >= first); + +} + +void TreeItem::deleteChildren(int first, int last) +{ + QList::iterator firstIt, lastIt, it; + firstIt = children.begin() + first; + lastIt = children.begin() + last + 1; + for( it = firstIt; it != lastIt; ++it) + { + delete *it; + } + children.erase(firstIt, lastIt); +} + +KBookmark TreeItem::bookmark() const +{ + return mBookmark; +} + +void TreeItem::initChildren() +{ + mInitDone = true; + if(mBookmark.isGroup()) + { + KBookmarkGroup parent = mBookmark.toGroup(); + for(KBookmark child = parent.first(); child.hasParent(); child = parent.next(child) ) + { + TreeItem * item = new TreeItem(child, this); + children.append(item); + } + } +} + +TreeItem * TreeItem::treeItemForBookmark(const KBookmark& bk) +{ + if(bk.address() == mBookmark.address()) + return this; + QString commonParent = KBookmark::commonParent(bk.address(), mBookmark.address()); + if(commonParent == mBookmark.address()) //mBookmark is a parent of bk + { + QList::const_iterator it, end; + end = children.constEnd(); + for( it = children.constBegin(); it != end; ++it) + { + KBookmark child = (*it)->bookmark(); + if( KBookmark::commonParent(child.address(), bk.address()) == child.address()) + return (*it)->treeItemForBookmark(bk); + } + return 0; + } + else + { + if(parent() == 0) + return 0; + return parent()->treeItemForBookmark(bk); + } +} + +// kate: space-indent on; indent-width 4; replace-tabs on; diff --git a/keditbookmarks/kbookmarkmodel/treeitem_p.h b/keditbookmarks/kbookmarkmodel/treeitem_p.h new file mode 100644 index 00000000..7e8b98ff --- /dev/null +++ b/keditbookmarks/kbookmarkmodel/treeitem_p.h @@ -0,0 +1,46 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Daniel Teske + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) version 3. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see +*/ + +#ifndef TREEITEM_P_H +#define TREEITEM_P_H + +#include +#include + +class TreeItem +{ +public: + TreeItem(const KBookmark& bk, TreeItem * parent); + ~TreeItem(); + TreeItem * child(int row); + TreeItem * parent() const; + + void insertChildren(int first, int last); + void deleteChildren(int first, int last); + void moveChildren(int first, int last, TreeItem * newParent, int position); + KBookmark bookmark() const; + int childCount(); + TreeItem * treeItemForBookmark(const KBookmark& bk); +private: + void initChildren(); + + QList children; + TreeItem * mParent; + KBookmark mBookmark; + bool mInitDone; +}; +#endif diff --git a/keditbookmarks/kbookmarkmodel/view.cpp b/keditbookmarks/kbookmarkmodel/view.cpp new file mode 100644 index 00000000..4c365c6c --- /dev/null +++ b/keditbookmarks/kbookmarkmodel/view.cpp @@ -0,0 +1,76 @@ +/* This file is part of the KDE project + Copyright (C) 2000 David Faure + Copyright (C) 2002-2003 Alexander Kellett + Copyright (C) 2005 Daniel Teske + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) version 3. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see +*/ + +#include "view.h" + +#include + +KBookmarkView::KBookmarkView(QWidget *parent) + : QTreeView(parent), m_loadingState(false) +{ + setAcceptDrops(true); + setDefaultDropAction(Qt::MoveAction); + connect(this, SIGNAL(expanded(QModelIndex)), this, SLOT(slotExpanded(QModelIndex))); + connect(this, SIGNAL(collapsed(QModelIndex)), this, SLOT(slotCollapsed(QModelIndex))); +} + +KBookmarkView::~KBookmarkView() +{ +} + +void KBookmarkView::loadFoldedState() +{ + m_loadingState = true; + loadFoldedState(QModelIndex()); + m_loadingState = false; +} + +void KBookmarkView::loadFoldedState(const QModelIndex& parentIndex) +{ + const int count = model()->rowCount(parentIndex); + for (int row = 0; row < count; ++row) { + const QModelIndex index = model()->index(row, 0, parentIndex); + const KBookmark bk = bookmarkForIndex(index); + if (bk.isNull()) { + expand(index); + } + else if (bk.isGroup()) { + setExpanded(index, bk.toGroup().isOpen()); + loadFoldedState(index); + } + } +} + +void KBookmarkView::slotExpanded(const QModelIndex& index) +{ + if (!m_loadingState) { + KBookmark bk = bookmarkForIndex(index); + bk.internalElement().setAttribute("folded", "no"); + } +} + +void KBookmarkView::slotCollapsed(const QModelIndex& index) +{ + if (!m_loadingState) { + KBookmark bk = bookmarkForIndex(index); + bk.internalElement().setAttribute("folded", "yes"); + } +} + +#include "moc_view.cpp" diff --git a/keditbookmarks/kbookmarkmodel/view.h b/keditbookmarks/kbookmarkmodel/view.h new file mode 100644 index 00000000..0cb02488 --- /dev/null +++ b/keditbookmarks/kbookmarkmodel/view.h @@ -0,0 +1,46 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Daniel Teske + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License version 2 as published by the Free Software Foundation. + + 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; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef BOOKMARKMODEL_BOOKMARKVIEW_H +#define BOOKMARKMODEL_BOOKMARKVIEW_H + +#include + +#include "kbookmarkmodel_export.h" + +class KBookmark; + +class KBOOKMARKMODEL_EXPORT KBookmarkView : public QTreeView +{ + Q_OBJECT +public: + explicit KBookmarkView(QWidget *parent = 0); + virtual ~KBookmarkView(); + virtual KBookmark bookmarkForIndex(const QModelIndex & idx) const = 0; + void loadFoldedState(); + +private Q_SLOTS: + void slotExpanded(const QModelIndex& index); + void slotCollapsed(const QModelIndex& index); + +private: + void loadFoldedState(const QModelIndex& parentIndex); + bool m_loadingState; +}; + +#endif diff --git a/keditbookmarks/kebsearchline.cpp b/keditbookmarks/kebsearchline.cpp new file mode 100644 index 00000000..c8a9f61e --- /dev/null +++ b/keditbookmarks/kebsearchline.cpp @@ -0,0 +1,697 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Daniel Teske + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License version 2 or at your option version 3 as published by + the Free Software Foundation. + + 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; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "kebsearchline.h" +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + + + +//////////////////////////////////////////////////////////////////////////////// +// public methods +//////////////////////////////////////////////////////////////////////////////// +class KViewSearchLine::KViewSearchLinePrivate +{ +public: + KViewSearchLinePrivate() : + listView(0), + treeView(0), + caseSensitive(false), + activeSearch(false), + keepParentsVisible(true), + queuedSearches(0) {} + + QListView * listView; + QTreeView * treeView; + bool caseSensitive; + bool activeSearch; + bool keepParentsVisible; + QString search; + int queuedSearches; + QList searchColumns; +}; + + +KViewSearchLine::KViewSearchLine(QWidget *parent, QAbstractItemView *v) : + KLineEdit(parent) +{ + d = new KViewSearchLinePrivate; + + setClearButtonShown(true); + + d->treeView = dynamic_cast(v); + d->listView = dynamic_cast(v); + + connect(this, SIGNAL(textChanged(QString)), + this, SLOT(queueSearch(QString))); + + if(view()) { + connect(view(), SIGNAL(destroyed()), + this, SLOT(listViewDeleted())); + connect(model(), SIGNAL(dataChanged(QModelIndex,QModelIndex)), + this, SLOT(slotDataChanged(QModelIndex,QModelIndex))); + connect(model(), SIGNAL(rowsInserted(QModelIndex,int,int)), + this, SLOT(slotRowsInserted(QModelIndex,int,int))); + connect(model(), SIGNAL(rowsRemoved(QModelIndex,int,int)), + this, SLOT(slotRowsRemoved(QModelIndex,int,int))); + connect(model(), SIGNAL(columnsInserted(QModelIndex,int,int)), + this, SLOT(slotColumnsInserted(QModelIndex,int,int))); + connect(model(), SIGNAL(columnsRemoved(QModelIndex,int,int)), + this, SLOT(slotColumnsRemoved(QModelIndex,int,int))); + connect(model(), SIGNAL(modelReset()), this, SLOT(slotModelReset())); + } + else + setEnabled(false); +} + +KViewSearchLine::KViewSearchLine(QWidget *parent) : + KLineEdit(parent) +{ + d = new KViewSearchLinePrivate; + + setClearButtonShown(true); + + d->treeView = 0; + d->listView = 0; + + connect(this, SIGNAL(textChanged(QString)), + this, SLOT(queueSearch(QString))); + + setEnabled(false); +} + +KViewSearchLine::~KViewSearchLine() +{ + delete d; +} + +QAbstractItemView * KViewSearchLine::view() const +{ + if(d->treeView) + return d->treeView; + else + return d->listView; +} + +bool KViewSearchLine::caseSensitive() const +{ + return d->caseSensitive; +} + +bool KViewSearchLine::keepParentsVisible() const +{ + return d->keepParentsVisible; +} + +//////////////////////////////////////////////////////////////////////////////// +// public slots +//////////////////////////////////////////////////////////////////////////////// + +void KViewSearchLine::updateSearch(const QString &s) +{ + if(!view()) + return; + + d->search = s.isNull() ? text() : s; + + // If there's a selected item that is visible, make sure that it's visible + // when the search changes too (assuming that it still matches). + //FIXME reimplement + + if(d->keepParentsVisible) + checkItemParentsVisible(model()->index(0,0, QModelIndex())); + else + checkItemParentsNotVisible(); +} + +void KViewSearchLine::setCaseSensitive(bool cs) +{ + d->caseSensitive = cs; +} + +void KViewSearchLine::setKeepParentsVisible(bool v) +{ + d->keepParentsVisible = v; +} + +void KViewSearchLine::setSearchColumns(const QList &columns) +{ + d->searchColumns = columns; +} + +void KViewSearchLine::setView(QAbstractItemView *v) +{ + if(view()) { + disconnect(view(), SIGNAL(destroyed()), + this, SLOT(listViewDeleted())); + disconnect(model(), SIGNAL(dataChanged(QModelIndex,QModelIndex)), + this, SLOT(slotDataChanged(QModelIndex,QModelIndex))); + disconnect(model(), SIGNAL(rowsInserted(QModelIndex,int,int)), + this, SLOT(slotRowsInserted(QModelIndex,int,int))); + disconnect(model(), SIGNAL(rowsRemoved(QModelIndex,int,int)), + this, SLOT(slotRowsRemoved(QModelIndex,int,int))); + disconnect(model(), SIGNAL(columnsInserted(QModelIndex,int,int)), + this, SLOT(slotColumnsInserted(QModelIndex,int,int))); + disconnect(model(), SIGNAL(columnsRemoved(QModelIndex,int,int)), + this, SLOT(slotColumnsRemoved(QModelIndex,int,int))); + disconnect(model(), SIGNAL(modelReset()), this, SLOT(slotModelReset())); + } + + d->treeView = dynamic_cast(v); + d->listView = dynamic_cast(v); + + if(view()) { + connect(view(), SIGNAL(destroyed()), + this, SLOT(listViewDeleted())); + + connect(model(), SIGNAL(dataChanged(QModelIndex,QModelIndex)), + this, SLOT(slotDataChanged(QModelIndex,QModelIndex))); + connect(model(), SIGNAL(rowsInserted(QModelIndex,int,int)), + this, SLOT(slotRowsInserted(QModelIndex,int,int))); + connect(model(), SIGNAL(rowsRemoved(QModelIndex,int,int)), + this, SLOT(slotRowsRemoved(QModelIndex,int,int))); + connect(model(), SIGNAL(columnsInserted(QModelIndex,int,int)), + this, SLOT(slotColumnsInserted(QModelIndex,int,int))); + connect(model(), SIGNAL(columnsRemoved(QModelIndex,int,int)), + this, SLOT(slotColumnsRemoved(QModelIndex,int,int))); + connect(model(), SIGNAL(modelReset()), this, SLOT(slotModelReset())); + + } + + setEnabled(bool(view())); +} + +//////////////////////////////////////////////////////////////////////////////// +// protected members +//////////////////////////////////////////////////////////////////////////////// + +bool KViewSearchLine::itemMatches(const QModelIndex & item, const QString &s) const +{ + if(s.isEmpty()) + return true; + + // If the search column list is populated, search just the columns + // specifified. If it is empty default to searching all of the columns. + if(d->treeView) + { + int columnCount = d->treeView->header()->count(); + int row = item.row(); + QModelIndex parent = item.parent(); + if(!d->searchColumns.isEmpty()) { + QList::const_iterator it, end; + end = d->searchColumns.constEnd(); + for(it = d->searchColumns.constBegin(); it != end; ++it) + { + if(*it < columnCount) + { + const QString & text = model()->data(parent.child(row, *it)).toString(); + if(text.indexOf(s, 0, d->caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive) >= 0) + return true; + } + } + } + else { + for(int i = 0; i < columnCount; i++) + { + if(d->treeView->isColumnHidden(i) == false) + { + const QString & text = model()->data(parent.child(row, i)).toString(); + if(text.indexOf(s, 0, d->caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive) >= 0) + return true; + } + } + } + return false; + } + else + { + QString text = model()->data(item).toString(); + if(text.indexOf(s, 0, d->caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive) >= 0) + return true; + else + return false; + } +} + +void KViewSearchLine::contextMenuEvent( QContextMenuEvent*e ) +{ + qDeleteAll(actions); + QMenu *popup = KLineEdit::createStandardContextMenu(); + if(d->treeView) + { + int columnCount = d->treeView->header()->count(); + actions.resize(columnCount + 1); + if(columnCount) + { + QMenu *submenu = new QMenu(i18n("Search Columns"), popup); + popup->addMenu(submenu); + bool allVisibleColumsCheked = true; + QAction * allVisibleAct = new QAction(i18n("All Visible Columns"), 0); + allVisibleAct->setCheckable(true); + submenu->addAction(allVisibleAct); + submenu->addSeparator(); + for(int i=0; itreeView->header()->logicalIndex(i); + QString columnText = model()->headerData(logicalIndex, Qt::Horizontal).toString(); + if(columnText.isEmpty()) + columnText = i18nc("Column number %1","Column No. %1", i); + QAction * act = new QAction(columnText, 0); + act->setCheckable(true); + if( d->searchColumns.isEmpty() || d->searchColumns.contains(logicalIndex) ) + act->setChecked(true); + + actions[logicalIndex] = act; + if( !d->treeView || (d->treeView->isColumnHidden(i) == false) ) + { + submenu->addAction(act); + allVisibleColumsCheked = allVisibleColumsCheked && act->isChecked(); + } + } + actions[columnCount] = allVisibleAct; + if(d->searchColumns.isEmpty() || allVisibleColumsCheked) + { + allVisibleAct->setChecked(true); + d->searchColumns.clear(); + } + connect(submenu, SIGNAL(triggered(QAction*)), this, SLOT(searchColumnsMenuActivated(QAction*))); + } + } + popup->exec( e->globalPos() ); + delete popup; +} + +//////////////////////////////////////////////////////////////////////////////// +// protected slots +//////////////////////////////////////////////////////////////////////////////// + +void KViewSearchLine::queueSearch(const QString &search) +{ + d->queuedSearches++; + d->search = search; + QTimer::singleShot(200, this, SLOT(activateSearch())); +} + +void KViewSearchLine::activateSearch() +{ + --(d->queuedSearches); + + if(d->queuedSearches == 0) + updateSearch(d->search); +} + +//////////////////////////////////////////////////////////////////////////////// +// private slots +//////////////////////////////////////////////////////////////////////////////// + +void KViewSearchLine::listViewDeleted() +{ + d->treeView = 0; + d->listView = 0; + setEnabled(false); +} + +void KViewSearchLine::searchColumnsMenuActivated(QAction * action) +{ + int index = 0; + int count = actions.count(); + for(int i=0; itreeView->header()->count(); + if(index == columnCount) + { + if(d->searchColumns.isEmpty()) //all columns was checked + d->searchColumns.append(0); + else + d->searchColumns.clear(); + } + else + { + if(d->searchColumns.contains(index)) + d->searchColumns.removeAll(index); + else + { + if(d->searchColumns.isEmpty()) //all columns was checked + { + for(int i=0; isearchColumns.append(i); + } + else + d->searchColumns.append(index); + } + } + updateSearch(); +} + + +void KViewSearchLine::slotRowsRemoved(const QModelIndex &parent, int, int) +{ + if(!d->keepParentsVisible) + return; + + QModelIndex p = parent; + while(p.isValid()) + { + int count = model()->rowCount(p); + if(count && anyVisible( model()->index(0,0, p), model()->index( count-1, 0, p))) + return; + if(itemMatches(p, d->search)) + return; + setVisible(p, false); + p = p.parent(); + } +} + +void KViewSearchLine::slotColumnsInserted(const QModelIndex &, int, int ) +{ + updateSearch(); +} + +void KViewSearchLine::slotColumnsRemoved(const QModelIndex &, int first, int last) +{ + if(d->treeView) + updateSearch(); + else + { + if(d->listView->modelColumn() >= first && d->listView->modelColumn()<= last) + { + if(d->listView->modelColumn()>last) + kFatal()<<"Columns were removed, the modelColumn() doesn't exist anymore. " + "K4listViewSearchLine can't cope with that."<listView) + column = d->listView->modelColumn(); + bool match = recheck( model()->index(topLeft.row(), column, parent), model()->index(bottomRight.row(), column, parent)); + if(!d->keepParentsVisible) + return; + if(!parent.isValid()) // includes listview + return; + if(match) + { + QModelIndex p = parent; + while(p.isValid()) + { + setVisible(p, true); + p = p.parent(); + } + } + else //no match => might need to hide parents (this is ugly) + { + if(isVisible(parent) == false) // parent is already hidden + return; + //parent is visible => implies all parents visible + + // first check if all of the unchanged rows are hidden + match = false; + if(topLeft.row() >= 1) + match = match || anyVisible( model()->index(0,0, parent), model()->index(topLeft.row()-1, 0, parent)); + int rowCount = model()->rowCount(parent); + if(bottomRight.row() + 1 <= rowCount - 1) + match = match || anyVisible( model()->index(bottomRight.row()+1, 0, parent), model()->index(rowCount-1, 0, parent)); + if(!match) //all child rows hidden + { + if(itemMatches(parent, d->search)) + return; + // and parent didn't match, hide it + setVisible(parent, false); + + // need to check all the way up to root + QModelIndex p = parent.parent(); + while(p.isValid()) + { + //hide p if no children of p isVisible and it doesn't match + int count = model()->rowCount(p); + if(anyVisible( model()->index(0, 0, p), model()->index(count-1, 0, p))) + return; + + if(itemMatches(p, d->search)) + return; + setVisible(p, false); + p = p.parent(); + } + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +// private methods +//////////////////////////////////////////////////////////////////////////////// +QAbstractItemModel * KViewSearchLine::model() const +{ + if(d->treeView) + return d->treeView->model(); + else + return d->listView->model(); +} + + +bool KViewSearchLine::anyVisible(const QModelIndex & first, const QModelIndex & last) +{ + Q_ASSERT(d->treeView); + QModelIndex parent = first.parent(); + QModelIndex index = first; + while(true) + { + if( isVisible(index)) + return true; + if(index == last) + break; + index = nextRow(index); + } + return false; +} + +bool KViewSearchLine::isVisible(const QModelIndex & index) +{ + if(d->treeView) + return !d->treeView->isRowHidden(index.row(), index.parent()); + else + return d->listView->isRowHidden(index.row()); +} + +QModelIndex KViewSearchLine::nextRow(const QModelIndex & index) +{ + return model()->index(index.row()+1, index.column(), index.parent()); +} + +bool KViewSearchLine::recheck(const QModelIndex & first, const QModelIndex & last) +{ + bool visible = false; + QModelIndex index = first; + while(true) + { + int rowCount = model()->rowCount(index); + if(d->keepParentsVisible && rowCount && anyVisible( index.child(0,0), index.child( rowCount-1, 0))) + { + visible = true; + } + else // no children visible + { + bool match = itemMatches(index, d->search); + setVisible(index, match); + visible = visible || match; + } + if(index == last) + break; + index = nextRow(index); + } + return visible; +} + +void KViewSearchLine::slotRowsInserted(const QModelIndex &parent, int first, int last) +{ + bool visible = false; + int column = 0; + if(d->listView) + column = d->listView->modelColumn(); + + QModelIndex index = model()->index(first, column, parent); + QModelIndex end = model()->index(last, column, parent); + while(true) + { + if(itemMatches(index, d->search)) + { + visible = true; + setVisible(index, true); + } + else + setVisible(index, false); + if(index == end) + break; + index = nextRow(index); + } + + if(!d->keepParentsVisible) + return; + if(visible) + { + QModelIndex p = parent; + while(p.isValid()) + { + setVisible(p, true); + p = p.parent(); + } + } +} + +void KViewSearchLine::setVisible(QModelIndex index, bool v) +{ + if(d->treeView) + d->treeView->setRowHidden(index.row(), index.parent(), !v); + else + d->listView->setRowHidden(index.row(), !v); +} + +void KViewSearchLine::checkItemParentsNotVisible() +{ + int rowCount = model()->rowCount( QModelIndex() ); + int column = 0; + if(d->listView) + column = d->listView->modelColumn(); + for(int i = 0; i < rowCount; ++i) + { + QModelIndex it = model()->index(i, column, QModelIndex()); + if(itemMatches(it, d->search)) + setVisible(it, true); + else + setVisible(it, false); + } +} + +/** Check whether \p index, its siblings and their descendents should be shown. Show or hide the items as necessary. + * + * \p index The list view item to start showing / hiding items at. Typically, this is the first child of another item, or the + * the first child of the list view. + * \return \c true if an item which should be visible is found, \c false if all items found should be hidden. + */ +bool KViewSearchLine::checkItemParentsVisible(QModelIndex index) +{ + bool visible = false; + int rowCount = model()->rowCount(index.parent()); + int column = 0; + if(d->listView) + column = d->listView->modelColumn(); + for(int i = 0; iindex(i, column, index.parent()); + if((model()->rowCount(index) && checkItemParentsVisible(index.child(0,column))) + || itemMatches(index, d->search)) + { + visible = true; + setVisible(index, true); + } + else + setVisible(index, false); + } + return visible; +} + + +//////////////////////////////////////////////////////////////////////////////// +// KViewSearchLineWidget +//////////////////////////////////////////////////////////////////////////////// + +class KViewSearchLineWidget::KViewSearchLineWidgetPrivate +{ +public: + KViewSearchLineWidgetPrivate() : view(0), searchLine(0), layout(0) {} + QAbstractItemView *view; + KViewSearchLine *searchLine; + QHBoxLayout *layout; +}; + +KViewSearchLineWidget::KViewSearchLineWidget(QAbstractItemView *view, + QWidget *parent) : + QWidget(parent) +{ + d = new KViewSearchLineWidgetPrivate; + d->view = view; + + QTimer::singleShot(0, this, SLOT(createWidgets())); +} + +KViewSearchLineWidget::~KViewSearchLineWidget() +{ + delete d->layout; + delete d; +} + +KViewSearchLine *KViewSearchLineWidget::createSearchLine(QAbstractItemView *view) +{ + if(!d->searchLine) + d->searchLine = new KViewSearchLine(0, view); + return d->searchLine; +} + +void KViewSearchLineWidget::createWidgets() +{ + d->layout = new QHBoxLayout(this); + d->layout->setMargin(0); + + QLabel *label = new QLabel(i18n("S&earch:")); + label->setObjectName( QLatin1String("kde toolbar widget" )); + d->layout->addWidget(label); + + d->searchLine = createSearchLine(d->view); + d->layout->addWidget(d->searchLine); + d->searchLine->show(); + + label->setBuddy(d->searchLine); + label->show(); +} + +KViewSearchLine *KViewSearchLineWidget::searchLine() const +{ + return d->searchLine; +} + +#include "moc_kebsearchline.cpp" diff --git a/keditbookmarks/kebsearchline.h b/keditbookmarks/kebsearchline.h new file mode 100644 index 00000000..8844bd1d --- /dev/null +++ b/keditbookmarks/kebsearchline.h @@ -0,0 +1,301 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Daniel Teske + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) version 3. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see +*/ + +#ifndef KEBSEARCHLINE_H +#define KEBSEARCHLINE_H + +#include +#include +#include + + +#include +class KViewSearchLinePrivate; +/** + * This class makes it easy to add a search line for filtering the items in a + * QListView/QTreeView based on a simple text search. + * + * No changes to the application other than instantiating this class with an + * appropriate QListView/QTreeView should be needed. + * + * This class automatically responds to the dataChanged(), rowsInserted(), + * rowsRemoved() and similar signals. + * This means that the view needs to be bound to a model(). + * + * Note: Don't call setModel() on the view while a KViewSearchLine filters + * the view. (Instead call setView(0) before and setView(view) after calling + * setModel() + * + * + * Note: You need to call updateSearch() if you called QListView::setModelColumn() + */ + +//FIXME delete KViewSearchLine if there is a replacement in kdelibs +class KViewSearchLine : public KLineEdit +{ + Q_OBJECT + +public: + /** + * Constructs a KViewSearchLine with \a view being the QTreeView/QListView + * to be filtered. + * + * If \a view is null then the widget will be disabled until a listview + * is set with setListView(). + */ + explicit KViewSearchLine(QWidget *parent = 0, QAbstractItemView *view = 0); + + /** + * Constructs a KViewSearchLine without any QListView/QTreeView to filter. The + * QListView/QTreeView object has to be set later with setListView(). + */ + KViewSearchLine(QWidget *parent); + + /** + * Destroys the KViewSearchLine. + */ + virtual ~KViewSearchLine(); + + /** + * Returns true if the search is case sensitive. This defaults to false. + * + * @see setCaseSensitive() + */ + bool caseSensitive() const; + + /** + * Returns the current list of columns that will be searched. If the + * returned list is empty all visible columns will be searched. + * + * @see setSearchColumns + */ + QList searchColumns() const; + + /** + * If this is true (the default) then the parents of matched items will also + * be shown. + * + * @see setKeepParentsVisible() + */ + bool keepParentsVisible() const; + + /** + * Returns the view that is currently filtered by the search. + * + * @see setView() + */ + QAbstractItemView *view() const; + + using KLineEdit::setVisible; + +public Q_SLOTS: + /** + * Updates search to only make visible the items that match \a s. If + * \a s is null then the line edit's text will be used. + */ + virtual void updateSearch(const QString &s = QString()); + + /** + * Make the search case sensitive or case insensitive. + * + * @see caseSenstive() + */ + void setCaseSensitive(bool cs); + + /** + * When a search is active on a list that's organized into a tree view if + * a parent or ancesestor of an item is does not match the search then it + * will be hidden and as such so too will any children that match. + * + * If this is set to true (the default) then the parents of matching items + * will be shown. + * + * This applys only to QTreeViews. + * + * @see keepParentsVisible + */ + void setKeepParentsVisible(bool v); + + /** + * Sets the list of columns to be searched. The default is to search all, + * visible columns which can be restored by passing \a columns as an empty + * list. + * This has no effect if the view is a QListView. + * + * @see searchColumns + */ + void setSearchColumns(const QList &columns); + + /** + * Sets the view that is filtered by this search line. + * If \a v is null then the widget will be disabled. + * v must be either a QListView or a QTreeView + * (That includes QListWidget and QTreeWidget) + * @see view() + */ + void setView(QAbstractItemView *v); + +protected: + + /** + * Returns true if the row including \a item matches the search \a s. + * This will be evaluated based on the value of caseSensitive() and + * searchColumns(). This can be overridden in subclasses to implement + * more complicated matching schemes. + */ + virtual bool itemMatches(const QModelIndex & item, const QString &s) const; + + /** + * Re-implemented for internal reasons. API not affected. + */ + virtual void contextMenuEvent( QContextMenuEvent*e ); + +protected Q_SLOTS: + /** + * When keys are pressed a new search string is created and a timer is + * activated. The most recent search is activated when this timer runs out + * if another key has not yet been pressed. + * + * This method makes @param search the most recent search and starts the + * timer. + * + * Together with activateSearch() this makes it such that searches are not + * started until there is a short break in the users typing. + * + * @see activateSearch() + */ + void queueSearch(const QString &search); + + /** + * When the timer started with queueSearch() expires this slot is called. + * If there has been another timer started then this slot does nothing. + * However if there are no other pending searches this starts the list view + * search. + * + * @see queueSearch() + */ + void activateSearch(); + +private: + /** + * QListView's and QTreeView's setRowHidden are slightly different. + */ + void setVisible(QModelIndex index, bool v); + + /** + * This is used in case parent items of matching items shouldn't be + * visible. It hides all items that don't match the search string. + */ + void checkItemParentsNotVisible(); + + /** + * This is used in case parent items of matching items should be visible. + * It makes a recursive call to all children. It returns true if at least + * one item in the subtree with the given root item is visible. + */ + bool checkItemParentsVisible(QModelIndex index); + + /** + * returns whether any row between first and last is visible + */ + bool anyVisible(const QModelIndex & first, const QModelIndex & last); + + /** + * rechecks indecies first-last after a dataChanged() signal + * sets their visibility and returns true if any item should be + * visible + */ + bool recheck(const QModelIndex & first, const QModelIndex & last); + + /** + * Hide QListView/QTreeView's different isRowHidden + */ + bool isVisible(const QModelIndex & index); + + /** + * returns the model() of the view() + */ + QAbstractItemModel * model() const; + + /** + * returns the index exactly one row below \p index + */ + QModelIndex nextRow(const QModelIndex & index); + +private Q_SLOTS: + void listViewDeleted(); + void slotDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight); + void slotRowsInserted(const QModelIndex &parent, int first, int last); + void slotRowsRemoved(const QModelIndex &parent, int first, int last); + void slotColumnsInserted(const QModelIndex &parent, int first, int last); + void slotColumnsRemoved(const QModelIndex &parent, int first, int last); + void slotModelReset(); + void searchColumnsMenuActivated(QAction * act); + +private: + class KViewSearchLinePrivate; + KViewSearchLinePrivate *d; + QVector actions; +}; + +/** + * Creates a widget featuring a KViewSearchLine, a label with the text + * "Search" and a button to clear the search. + */ +class KViewSearchLineWidget : public QWidget +{ + Q_OBJECT + +public: + /** + * Creates a KListViewSearchLineWidget for \a view with \a parent as the + * parent + */ + explicit KViewSearchLineWidget(QAbstractItemView *view = 0, QWidget *parent = 0); + + /** + * Destroys the KListViewSearchLineWidget + */ + ~KViewSearchLineWidget(); + + /** + * Creates the search line. This can be useful to reimplement in cases where + * a KViewSearchLine subclass is used. + */ + virtual KViewSearchLine *createSearchLine(QAbstractItemView *view); + + /** + * Returns a pointer to the search line. + */ + KViewSearchLine *searchLine() const; + +protected Q_SLOTS: + /** + * Creates the widgets inside of the widget. This is called from the + * constructor via a single shot timer so that it it guaranteed to run + * after construction is complete. This makes it suitable for overriding in + * subclasses. + */ + virtual void createWidgets(); + +private: + class KViewSearchLineWidgetPrivate; + KViewSearchLineWidgetPrivate *d; +}; + + +#endif diff --git a/keditbookmarks/keditbookmarks-genui.rc b/keditbookmarks/keditbookmarks-genui.rc new file mode 100644 index 00000000..5d084030 --- /dev/null +++ b/keditbookmarks/keditbookmarks-genui.rc @@ -0,0 +1,143 @@ + + + + + +&File + + + + + + +&Edit + + + + + + + + + + +&View + + + + +&Folder + + + + + + + +&Bookmark + + + +&Settings + + + + + +Main Toolbar + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/keditbookmarks/keditbookmarks.desktop b/keditbookmarks/keditbookmarks.desktop new file mode 100755 index 00000000..c3434be4 --- /dev/null +++ b/keditbookmarks/keditbookmarks.desktop @@ -0,0 +1,137 @@ +[Desktop Entry] +Type=Application +Exec=keditbookmarks +NoDisplay=true +Name=Bookmark Editor +Name[ar]=محرِّر العلامات +Name[ast]=Editor de marcadores +Name[bg]=Редактор на отметки +Name[bn]=বুকমার্ক সম্পাদক +Name[bs]=Uređivač obeleživača +Name[ca]=Editor d'adreces d'interès +Name[ca@valencia]=Editor d'adreces d'interés +Name[cs]=Editor záložek +Name[da]=Bogmærkeredigering +Name[de]=Lesezeichen-Editor +Name[el]=Επεξεργαστής σελιδοδεικτών +Name[en_GB]=Bookmark Editor +Name[es]=Editor de marcadores +Name[et]=Järjehoidjate redaktor +Name[eu]=Laster-marken editorea +Name[fa]=ویرایشگر چوب الف +Name[fi]=Kirjanmerkkimuokkain +Name[fr]=Éditeur de signets +Name[ga]=Eagarthóir Leabharmharcanna +Name[gl]=Editor de marcadores +Name[gu]=બુકમાર્ક સંપાદક +Name[he]=עורך הסימניות +Name[hi]=पसंदीदा संपादक +Name[hr]=Uređivač oznaka +Name[hu]=Könyvjelzőszerkesztő +Name[ia]=Editor de marcator de libros +Name[id]=Penyunting Penanda +Name[is]=Bókamerkjastjóri +Name[it]=Editor dei segnalibri +Name[ja]=ブックマークエディタ +Name[kk]=Бетбелгі өңдегіші +Name[km]=កម្មវិធី​កែសម្រួល​ចំណាំ +Name[kn]=ಅಂಕನ (ಬುಕ್ಮಾರ್ಕ್) ಸಂಪಾದಕ +Name[ko]=책갈피 편집기 +Name[lt]=Žymelių redaktorius +Name[lv]=Grāmatzīmju redaktors +Name[mai]=पुस्तचिह्न संपादक +Name[mr]=ओळखचिन्ह संपादक +Name[nb]=Bokmerkeredigerer +Name[nds]=Leesteken-Editor +Name[nl]=Bladwijzerbewerker +Name[nn]=Bokmerkeredigering +Name[pa]=ਬੁੱਕਮਾਰਕ ਐਡੀਟਰ +Name[pl]=Edytor zakładek +Name[pt]=Editor de Favoritos +Name[pt_BR]=Editor de favoritos +Name[ro]=Editor semne de carte +Name[ru]=Редактор закладок +Name[si]=පිටුසලකුණු සංස්කාරක +Name[sk]=Editor záložiek +Name[sl]=Urejevalnik zaznamkov +Name[sr]=Уређивач обележивача +Name[sr@ijekavian]=Уређивач обиљеживача +Name[sr@ijekavianlatin]=Uređivač obilježivača +Name[sr@latin]=Uređivač obeleživača +Name[sv]=Bokmärkeseditor +Name[tg]=Таҳриргари хатчӯбҳо +Name[th]=เครื่องมือแก้ไขที่คั่นหน้า +Name[tr]=Yer İmleri Düzenleyici +Name[ug]=خەتكۈش تەھرىرلىگۈچ +Name[uk]=Редактор закладок +Name[wa]=Aspougneu des rmarkes +Name[x-test]=xxBookmark Editorxx +Name[zh_CN]=书签编辑器 +Name[zh_TW]=書籤編輯器 +Comment=Bookmark Organizer and Editor +Comment[ar]=محرِّر و منظّم العلامات +Comment[ast]=Organizador y editor de marcadores +Comment[bg]=Организиране и редактиране на отметки +Comment[bn]=বুকমার্ক ব্যবস্থাপনা এবং সম্পাদনা +Comment[bs]=Organizator i uređivač obeleživača +Comment[ca]=Organitzador i editor d'adreces d'interès +Comment[ca@valencia]=Organitzador i editor d'adreces d'interés +Comment[cs]=Editor a organizátor záložek +Comment[da]=Organisering og redigering af bogmærker +Comment[de]=Lesezeichen-Verwaltung und -Editor +Comment[el]=Επεξεργαστής και οργανωτής σελιδοδεικτών +Comment[en_GB]=Bookmark Organiser and Editor +Comment[es]=Organizador y editor de marcadores +Comment[et]=Järjehoidjate korraldaja ja redaktor +Comment[eu]=Laster-marken antolatzaile eta editorea +Comment[fi]=Kirjanmerkkimuokkain ja -järjestelijä +Comment[fr]=Éditeur et organisateur de signets +Comment[ga]=Eagarthóir agus Eagraí Leabharmharcanna +Comment[gl]=Organizador e editor dos marcadores +Comment[gu]=બૂકમાર્ક વ્યવસ્થાપક અને સંપાદક +Comment[he]=מארגן ועורך סימניות +Comment[hi]=पसंदीदा व्यवस्था और संपादक +Comment[hr]=Organizator i uređivač oznaka +Comment[hu]=Könyvjelzőszervező és -szerkesztő +Comment[ia]=Editor e organisator de marcator de libros +Comment[id]=Pengorganisir dan Penyunting Penanda +Comment[is]=Bókamerkjaskipuleggjari +Comment[it]=Editor ed organizzatore dei segnalibri +Comment[ja]=ブックマークの管理と編集 +Comment[kk]=Бетбелгілерді реттеу мен өңдеу +Comment[km]=កម្មវិធី​រៀបចំ និង​កែសម្រួល​ចំណាំ +Comment[kn]=ಪುಟಗುರುತು ವ್ಯವಸ್ಥಾಪಕ ಹಾಗು ಸಂಪಾದಕ +Comment[ko]=책갈피 관리 도구 +Comment[lt]=Žymelių organizatorius ir redaktorius +Comment[lv]=Grāmatzīmju kārtotājs un rediģētājs +Comment[mai]=पुस्तचिह्न प्रबंधक आओर संपादक +Comment[mr]=ओळखचिन्ह आयोजक व संपादक +Comment[nb]=Bokmerkeordner og redigerer +Comment[nds]=Leestekenpleger un -editor +Comment[nl]=Bladwijzers organiseren en bewerken +Comment[nn]=Bokmerkeordnar og redigering +Comment[pa]=ਬੁੱਕਮਾਰਕ ਪਰਬੰਧਕ ਤੇ ਐਡੀਟਰ +Comment[pl]=Organizacja i modyfikacja zakładek +Comment[pt]=Organizador e Editor de Favoritos +Comment[pt_BR]=Editor e organizador de favoritos +Comment[ro]=Organizator și editor de semne de carte +Comment[ru]=Редактирование и организация закладок +Comment[si]=පිටු සළකුණු සංවිධායක සහ සංස්කාරක +Comment[sk]=Organizátor a editor záložiek +Comment[sl]=Organizator in urejevalnik zaznamkov +Comment[sr]=Организатор и уређивач обележивача +Comment[sr@ijekavian]=Организатор и уређивач обиљеживача +Comment[sr@ijekavianlatin]=Organizator i uređivač obilježivača +Comment[sr@latin]=Organizator i uređivač obeleživača +Comment[sv]=Bokmärkesorganisering och editor +Comment[tg]=Ташкикунанда ва таҳриргари хатчӯбҳо +Comment[th]=เครื่องมือแก้ไขและจัดการที่คั่นหน้า +Comment[tr]=Yer İmleri Düzenleyicisi +Comment[ug]=خەتكۈش تەشكىللىگۈچ ۋە تەھرىرلىگۈچ +Comment[uk]=Керування і редагування закладок +Comment[wa]=Organizeu eyet aspougneu d' rimarkes +Comment[x-test]=xxBookmark Organizer and Editorxx +Comment[zh_CN]=书签组织和编辑器 +Comment[zh_TW]=書籤組織編輯器 +X-DocPath=konqueror/index.html#bookmarks +Categories=Qt;KDE;Network;WebBrowser; diff --git a/keditbookmarks/keditbookmarks.kcfg b/keditbookmarks/keditbookmarks.kcfg new file mode 100644 index 00000000..60c5f593 --- /dev/null +++ b/keditbookmarks/keditbookmarks.kcfg @@ -0,0 +1,42 @@ + + + + + + + 300 + + + + + 300 + + + + + 300 + + + + + 300 + + + + + 300 + + + + + + + + + + + + diff --git a/keditbookmarks/keditbookmarksui.rc b/keditbookmarks/keditbookmarksui.rc new file mode 100644 index 00000000..c9d0a3dc --- /dev/null +++ b/keditbookmarks/keditbookmarksui.rc @@ -0,0 +1,205 @@ + + + + + +&File + + + + &Import + + + + + + + + + + &Export + + + + + + + + + + + +&Edit + + + + + + + + + + +&View + + + + +&Folder + + + + + + + + +&Bookmark + + + + + + + +&Tools + + + + + + + +&Settings + + + + + + +Main Toolbar + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/keditbookmarks/main.cpp b/keditbookmarks/main.cpp new file mode 100644 index 00000000..f7caa199 --- /dev/null +++ b/keditbookmarks/main.cpp @@ -0,0 +1,216 @@ +// -*- indent-tabs-mode:nil -*- +// vim: set ts=4 sts=4 sw=4 et: +/* This file is part of the KDE project + Copyright (C) 2000 David Faure + Copyright (C) 2002-2003 Alexander Kellett + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License version 2 or at your option version 3 as published by + the Free Software Foundation. + + 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; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "globalbookmarkmanager.h" +#include "toplevel.h" +#include "importers.h" +#include "kbookmarkmodel/commandhistory.h" + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +// TODO - make this register() or something like that and move dialog into main +static bool askUser(const QString& filename, bool &readonly) { + + QString requestedName("keditbookmarks"); + QString interfaceName = "org.kde.keditbookmarks"; + QString appId = interfaceName + '-' +QString().setNum(getpid()); + + QDBusConnection dbus = QDBusConnection::sessionBus(); + QDBusReply reply = dbus.interface()->registeredServiceNames(); + if ( !reply.isValid() ) + return true; + const QStringList allServices = reply; + for ( QStringList::const_iterator it = allServices.begin(), end = allServices.end() ; it != end ; ++it ) { + const QString service = *it; + if ( service.startsWith( interfaceName ) && service != appId ) { + org::kde::keditbookmarks keditbookmarks(service,"/keditbookmarks", dbus); + QDBusReply bookmarks = keditbookmarks.bookmarkFilename(); + QString name; + if( bookmarks.isValid()) + name = bookmarks; + if( name == filename) + { + int ret = KMessageBox::warningYesNo(0, + i18n("Another instance of %1 is already running. Do you really " + "want to open another instance or continue work in the same instance?\n" + "Please note that, unfortunately, duplicate views are read-only.", KGlobal::caption()), + i18nc("@title:window", "Warning"), + KGuiItem(i18n("Run Another")), /* yes */ + KGuiItem(i18n("Continue in Same")) /* no */); + if (ret == KMessageBox::No) { + QDBusInterface keditinterface(service, "/keditbookmarks/MainWindow_1"); + //TODO fix me + QDBusReply value = keditinterface.call(QDBus::NoBlock, "winId"); + qlonglong id = 0; + if( value.isValid()) + id = value; + //kDebug()<<" id !!!!!!!!!!!!!!!!!!! :"< + +int main(int argc, char **argv) { + KAboutData aboutData("keditbookmarks", 0, ki18n("Bookmark Editor"), KDE_VERSION_STRING, + ki18n("Bookmark Organizer and Editor"), + KAboutData::License_GPL, + ki18n("Copyright 2000-2007, KDE developers") ); + aboutData.addAuthor(ki18n("David Faure"), ki18n("Initial author"), "faure@kde.org"); + aboutData.addAuthor(ki18n("Alexander Kellett"), ki18n("Author"), "lypanov@kde.org"); + aboutData.setProgramIconName("bookmarks-organize"); + + KCmdLineArgs::init(argc, argv, &aboutData); + KCmdLineArgs::addStdCmdLineOptions(); + + KCmdLineOptions options; + options.add("importmoz ", ki18n("Import bookmarks from a file in Mozilla format")); + options.add("importns ", ki18n("Import bookmarks from a file in Netscape (4.x and earlier) format")); + options.add("importie ", ki18n("Import bookmarks from a file in Internet Explorer's Favorites format")); + options.add("importopera ", ki18n("Import bookmarks from a file in Opera format")); + options.add("importkde3 ", ki18n("Import bookmarks from a file in KDE2 format")); + options.add("importgaleon ", ki18n("Import bookmarks from a file in Galeon format")); + options.add("exportmoz ", ki18n("Export bookmarks to a file in Mozilla format")); + options.add("exportns ", ki18n("Export bookmarks to a file in Netscape (4.x and earlier) format")); + options.add("exporthtml ", ki18n("Export bookmarks to a file in a printable HTML format")); + options.add("exportie ", ki18n("Export bookmarks to a file in Internet Explorer's Favorites format")); + options.add("exportopera ", ki18n("Export bookmarks to a file in Opera format")); + options.add("address
    ", ki18n("Open at the given position in the bookmarks file")); + options.add("customcaption ", ki18n("Set the user-readable caption, for example \"Konsole\"")); + options.add("nobrowser", ki18n("Hide all browser related functions")); + options.add("dbusObjectName ", ki18n("A unique name that represents this bookmark collection, usually the kinstance name.\n" + "This should be \"konqueror\" for the Konqueror bookmarks, \"kfile\" for KFileDialog bookmarks, etc.\n" + "The final D-Bus object path is /KBookmarkManager/dbusObjectName")); + options.add("+[file]", ki18n("File to edit")); + KCmdLineArgs::addCmdLineOptions(options); + + KCmdLineArgs *args = KCmdLineArgs::parsedArgs(); + bool isGui = !(args->isSet("exportmoz") || args->isSet("exportns") || args->isSet("exporthtml") + || args->isSet("exportie") || args->isSet("exportopera") + || args->isSet("importmoz") || args->isSet("importns") + || args->isSet("importie") || args->isSet("importopera") + || args->isSet("importkde3") || args->isSet("importgaleon")); + + bool browser = args->isSet("browser"); + + //KApplication::disableAutoDcopRegistration(); + KApplication app; + + bool gotFilenameArg = (args->count() == 1); + + QString filename = gotFilenameArg + ? args->arg(0) + : KStandardDirs::locateLocal("data", QLatin1String("konqueror/bookmarks.xml")); + + if (!isGui) { + GlobalBookmarkManager::self()->createManager(filename, QString(), new CommandHistory()); + GlobalBookmarkManager::ExportType exportType = GlobalBookmarkManager::MozillaExport; // uumm.. can i just set it to -1 ? + int got = 0; + const char *arg, *arg2 = 0, *importType = 0; + if (arg = "exportmoz", args->isSet(arg)) { exportType = GlobalBookmarkManager::MozillaExport; arg2 = arg; got++; } + if (arg = "exportns", args->isSet(arg)) { exportType = GlobalBookmarkManager::NetscapeExport; arg2 = arg; got++; } + if (arg = "exporthtml", args->isSet(arg)) { exportType = GlobalBookmarkManager::HTMLExport; arg2 = arg; got++; } + if (arg = "exportie", args->isSet(arg)) { exportType = GlobalBookmarkManager::IEExport; arg2 = arg; got++; } + if (arg = "exportopera", args->isSet(arg)) { exportType = GlobalBookmarkManager::OperaExport; arg2 = arg; got++; } + if (arg = "importmoz", args->isSet(arg)) { importType = "Moz"; arg2 = arg; got++; } + if (arg = "importns", args->isSet(arg)) { importType = "NS"; arg2 = arg; got++; } + if (arg = "importie", args->isSet(arg)) { importType = "IE"; arg2 = arg; got++; } + if (arg = "importopera", args->isSet(arg)) { importType = "Opera"; arg2 = arg; got++; } + if (arg = "importgaleon", args->isSet(arg)) { importType = "Galeon"; arg2 = arg; got++; } + if (arg = "importkde3", args->isSet(arg)) { importType = "KDE2"; arg2 = arg; got++; } + if (!importType && arg2) { + Q_ASSERT(arg2); + // TODO - maybe an xbel export??? + if (got > 1) // got == 0 isn't possible as !isGui is dependant on "export.*" + KCmdLineArgs::usage(I18N_NOOP("You may only specify a single --export option.")); + QString path = args->getOption(arg2); + GlobalBookmarkManager::self()->doExport(exportType, path); + } else if (importType) { + if (got > 1) // got == 0 isn't possible as !isGui is dependant on "import.*" + KCmdLineArgs::usage(I18N_NOOP("You may only specify a single --import option.")); + QString path = args->getOption(arg2); + KBookmarkModel* model = GlobalBookmarkManager::self()->model(); + ImportCommand *importer = ImportCommand::importerFactory(model, importType); + importer->import(path, true); + importer->redo(); + GlobalBookmarkManager::self()->managerSave(); + GlobalBookmarkManager::self()->notifyManagers(); + } + return 0; // error flag on exit?, 1? + } + + QString address = args->isSet("address") + ? args->getOption("address") + : QString("/0"); + + QString caption = args->isSet("customcaption") + ? args->getOption("customcaption") + : QString(); + + QString dbusObjectName; + if(args->isSet("dbusObjectName")) + { + dbusObjectName = args->getOption("dbusObjectName"); + } + else + { + if(gotFilenameArg) + dbusObjectName = QString(); + else + dbusObjectName = "konqueror"; + } + + args->clear(); + + bool readonly = false; // passed by ref + + if (askUser((gotFilenameArg ? filename : QString()), readonly)) { + KEBApp *toplevel = new KEBApp(filename, readonly, address, browser, caption, dbusObjectName); + toplevel->setAttribute(Qt::WA_DeleteOnClose); + toplevel->show(); + return app.exec(); + } + + return 0; +} diff --git a/keditbookmarks/settings.kcfgc b/keditbookmarks/settings.kcfgc new file mode 100644 index 00000000..bd21f5c0 --- /dev/null +++ b/keditbookmarks/settings.kcfgc @@ -0,0 +1,4 @@ +File=keditbookmarks.kcfg +ClassName=KEBSettings +Singleton=true +Mutators=true diff --git a/keditbookmarks/testlink.cpp b/keditbookmarks/testlink.cpp new file mode 100644 index 00000000..2a34239e --- /dev/null +++ b/keditbookmarks/testlink.cpp @@ -0,0 +1,107 @@ +/* This file is part of the KDE project + Copyright (C) 2000, 2010 David Faure + Copyright (C) 2002-2003 Alexander Kellett + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License version 2 or at your option version 3 as published by + the Free Software Foundation. + + 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; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +// Own +#include "testlink.h" + +// KDE +#include +#include + +// Local +#include "kbookmarkmodel/commands.h" +#include "bookmarkiterator.h" +#include "kbookmarkmodel/model.h" + +TestLinkItrHolder::TestLinkItrHolder(QObject* parent, KBookmarkModel* model) + : BookmarkIteratorHolder(parent, model) +{ +} + +/* -------------------------- */ + +TestLinkItr::TestLinkItr(BookmarkIteratorHolder* holder, const QList& bks) + : BookmarkIterator(holder, bks), m_job(0) +{ +} + +TestLinkItr::~TestLinkItr() +{ + if (m_job) { + // kDebug() << "JOB kill\n"; + m_job->disconnect(this); + m_job->kill(); + } +} + +void TestLinkItr::setStatus(const QString & text) +{ + currentBookmark().setMetaDataItem("linkstate", text); + model()->emitDataChanged(currentBookmark()); +} + +bool TestLinkItr::isApplicable(const KBookmark &bk) const +{ + return !bk.isGroup() && !bk.isSeparator(); +} + +void TestLinkItr::doAction() +{ + kDebug(); + m_job = KIO::get(currentBookmark().url(), KIO::Reload, KIO::HideProgressInfo); + + connect(m_job, SIGNAL(result(KJob*)), + this, SLOT(slotJobResult(KJob*))); + + m_oldStatus = currentBookmark().metaDataItem("linkstate"); + setStatus(i18n("Checking...")); +} + +void TestLinkItr::slotJobResult(KJob *job) +{ + kDebug(); + m_job = 0; + + KIO::TransferJob *transfer = static_cast(job); + const QString modDate = transfer->queryMetaData("modified"); + + if (transfer->error() != 0) { + kDebug()<<"***********"<error()<errorString(); + err.replace("\n", " "); + setStatus(err); + } else { + if (!modDate.isEmpty()) + setStatus(modDate); + else + setStatus(i18n("OK")); + } + + holder()->addAffectedBookmark(KBookmark::parentAddress(currentBookmark().address())); + delayedEmitNextOne(); +} + +void TestLinkItr::cancel() +{ + setStatus(m_oldStatus); +} + +#include "moc_testlink.cpp" diff --git a/keditbookmarks/testlink.h b/keditbookmarks/testlink.h new file mode 100644 index 00000000..e70e8e31 --- /dev/null +++ b/keditbookmarks/testlink.h @@ -0,0 +1,57 @@ +/* This file is part of the KDE project + Copyright (C) 2000 David Faure + Copyright (C) 2002-2003 Alexander Kellett + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) version 3. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see +*/ + +#ifndef TESTLINK_H +#define TESTLINK_H + +#include + +#include +#include + +#include "bookmarkiterator.h" +class KBookmarkModel; + +class TestLinkItrHolder : public BookmarkIteratorHolder { +public: + TestLinkItrHolder(QObject* parent, KBookmarkModel* model); +}; + +class TestLinkItr : public BookmarkIterator +{ + Q_OBJECT + +public: + TestLinkItr(BookmarkIteratorHolder* holder, const QList& bks); + ~TestLinkItr(); + + virtual void cancel(); + +public Q_SLOTS: + void slotJobResult(KJob *job); + +private: + void setStatus(const QString & text); + virtual void doAction(); + virtual bool isApplicable(const KBookmark &bk) const; + + KIO::TransferJob *m_job; + QString m_oldStatus; +}; + +#endif diff --git a/keditbookmarks/toplevel.cpp b/keditbookmarks/toplevel.cpp new file mode 100644 index 00000000..34ad4310 --- /dev/null +++ b/keditbookmarks/toplevel.cpp @@ -0,0 +1,488 @@ +/* This file is part of the KDE project + Copyright (C) 2000 David Faure + Copyright (C) 2002-2003 Alexander Kellett + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License version 2 or at your option version 3 as published by + the Free Software Foundation. + + 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; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "toplevel.h" +#include +#include "globalbookmarkmanager.h" + +#include "kbookmarkmodel/model.h" + +#include "bookmarkinfowidget.h" +#include "actionsimpl.h" +#include "exporters.h" +#include "settings.h" +#include "kbookmarkmodel/commands.h" +#include "kbookmarkmodel/commandhistory.h" +#include "kebsearchline.h" +#include "bookmarklistview.h" + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include +KEBApp *KEBApp::s_topLevel = 0; + +KEBApp::KEBApp( + const QString &bookmarksFile, bool readonly, + const QString &address, bool browser, const QString &caption, + const QString &dbusObjectName +) : KXmlGuiWindow(), m_bookmarksFilename(bookmarksFile), + m_caption(caption), + m_dbusObjectName(dbusObjectName), m_readOnly(readonly),m_browser(browser) + { + QDBusConnection::sessionBus().registerObject("/keditbookmarks", this, QDBusConnection::ExportScriptableSlots); + Q_UNUSED(address);//FIXME sets the current item + + m_cmdHistory = new CommandHistory(this); + m_cmdHistory->createActions(actionCollection()); + connect(m_cmdHistory, SIGNAL(notifyCommandExecuted(KBookmarkGroup)), this, SLOT(notifyCommandExecuted())); + + GlobalBookmarkManager::self()->createManager(m_bookmarksFilename, m_dbusObjectName, m_cmdHistory); + + s_topLevel = this; + + createActions(); + if (m_browser) + createGUI(); + else + createGUI("keditbookmarks-genui.rc"); + + connect(qApp->clipboard(), SIGNAL(dataChanged()), + SLOT(slotClipboardDataChanged())); + + KGlobal::locale()->insertCatalog("libkonq"); + + m_canPaste = false; + + mBookmarkListView = new BookmarkListView(); + mBookmarkListView->setModel( GlobalBookmarkManager::self()->model() ); + mBookmarkListView->setSelectionMode(QAbstractItemView::ExtendedSelection); + mBookmarkListView->loadColumnSetting(); + mBookmarkListView->loadFoldedState(); + + KViewSearchLineWidget *searchline = new KViewSearchLineWidget(mBookmarkListView); + + mBookmarkFolderView = new BookmarkFolderView(mBookmarkListView); + mBookmarkFolderView->expandAll(); + + QWidget * rightSide = new QWidget; + QVBoxLayout *listLayout = new QVBoxLayout(rightSide); + listLayout->setMargin(0); + rightSide->setLayout(listLayout); + listLayout->addWidget(searchline); + listLayout->addWidget(mBookmarkListView); + + m_bkinfo = new BookmarkInfoWidget(mBookmarkListView, GlobalBookmarkManager::self()->model()); + m_bkinfo->layout()->setContentsMargins(0, 0, KDialog::spacingHint(), KDialog::spacingHint()); + + listLayout->addWidget(m_bkinfo); + + QSplitter *hsplitter = new QSplitter(this); + hsplitter->setOrientation(Qt::Horizontal); + hsplitter->addWidget(mBookmarkFolderView); + hsplitter->addWidget(rightSide); + hsplitter->setStretchFactor(1,1); + + setCentralWidget(hsplitter); + + slotClipboardDataChanged(); + setAutoSaveSettings(); + + connect(mBookmarkListView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), + this, SLOT(selectionChanged())); + + connect(mBookmarkFolderView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), + this, SLOT(selectionChanged())); + + setCancelFavIconUpdatesEnabled(false); + setCancelTestsEnabled(false); + updateActions(); +} + +void KEBApp::expandAll() +{ + mBookmarkListView->expandAll(); +} + +void KEBApp::collapseAll() +{ + mBookmarkListView->collapseAll(); +} + +QString KEBApp::bookmarkFilename() +{ + return m_bookmarksFilename; +} + +void KEBApp::reset(const QString & caption, const QString & bookmarksFileName) +{ + //FIXME check this code, probably should be model()->setRoot instead of resetModel() + m_caption = caption; + m_bookmarksFilename = bookmarksFileName; + GlobalBookmarkManager::self()->createManager(m_bookmarksFilename, m_dbusObjectName, m_cmdHistory); //FIXME this is still a memory leak (iff called by slotLoad) + GlobalBookmarkManager::self()->model()->resetModel(); + updateActions(); +} + +void KEBApp::startEdit( Column c ) +{ + const QModelIndexList & list = mBookmarkListView->selectionModel()->selectedIndexes(); + QModelIndexList::const_iterator it, end; + end = list.constEnd(); + for(it = list.constBegin(); it != end; ++it) + if( (*it).column() == int(c) && (mBookmarkListView->model()->flags(*it) & Qt::ItemIsEditable) ) + return mBookmarkListView->edit( *it ); +} + +//FIXME clean up and remove unneeded things +SelcAbilities KEBApp::getSelectionAbilities() const +{ + SelcAbilities selctionAbilities; + selctionAbilities.itemSelected = false; + selctionAbilities.group = false; + selctionAbilities.separator = false; + selctionAbilities.urlIsEmpty = false; + selctionAbilities.root = false; + selctionAbilities.multiSelect = false; + selctionAbilities.singleSelect = false; + selctionAbilities.notEmpty = false; + selctionAbilities.deleteEnabled = false; + + KBookmark nbk; + QModelIndexList sel = mBookmarkListView->selectionModel()->selectedIndexes(); + int columnCount; + if(sel.count()) + { + nbk = mBookmarkListView->bookmarkForIndex(sel.first()); + columnCount = mBookmarkListView->model()->columnCount(); + } + else + { + sel = mBookmarkFolderView->selectionModel()->selectedIndexes(); + if(sel.count()) + nbk = mBookmarkFolderView->bookmarkForIndex(sel.first()); + columnCount = mBookmarkFolderView->model()->columnCount(); + } + + if ( sel.count() > 0) + { + selctionAbilities.deleteEnabled = true; + selctionAbilities.itemSelected = true; + selctionAbilities.group = nbk.isGroup(); + selctionAbilities.separator = nbk.isSeparator(); + selctionAbilities.urlIsEmpty = nbk.url().isEmpty(); + selctionAbilities.root = nbk.address() == GlobalBookmarkManager::self()->root().address(); + selctionAbilities.multiSelect = (sel.count() > columnCount); + selctionAbilities.singleSelect = (!selctionAbilities.multiSelect && selctionAbilities.itemSelected); + } + //FIXME check next line, if it actually works + selctionAbilities.notEmpty = GlobalBookmarkManager::self()->root().first().hasParent(); + +/* kDebug() + <<"\nsa.itemSelected "<action(*it)->setEnabled(true); + } +} + +KBookmark KEBApp::firstSelected() const +{ + QModelIndex index; + const QModelIndexList & list = mBookmarkListView->selectionModel()->selectedIndexes(); + if(list.count()) // selection in main listview, return bookmark for firstSelected + return mBookmarkListView->bookmarkForIndex(*list.constBegin()); + + // no selection in main listview, fall back to selection in left tree + const QModelIndexList & list2 = mBookmarkFolderView->selectionModel()->selectedIndexes(); + return mBookmarkFolderView->bookmarkForIndex(*list2.constBegin()); +} + +QString KEBApp::insertAddress() const +{ + KBookmark current = firstSelected(); + return (current.isGroup()) + ? (current.address() + "/0") //FIXME internal represantation used + : KBookmark::nextAddress(current.address()); +} + +bool lessAddress(const QString& first, const QString& second) +{ + QString a = first; + QString b = second; + + if(a == b) + return false; + + QString error("ERROR"); + if(a == error) + return false; + if(b == error) + return true; + + a += '/'; + b += '/'; + + uint aLast = 0; + uint bLast = 0; + uint aEnd = a.length(); + uint bEnd = b.length(); + // Each iteration checks one "/"-delimeted part of the address + // "" is treated correctly + while(true) + { + // Invariant: a[0 ... aLast] == b[0 ... bLast] + if(aLast + 1 == aEnd) //The last position was the last slash + return true; // That means a is shorter than b + if(bLast +1 == bEnd) + return false; + + uint aNext = a.indexOf("/", aLast + 1); + uint bNext = b.indexOf("/", bLast + 1); + + bool okay; + uint aNum = a.mid(aLast + 1, aNext - aLast - 1).toUInt(&okay); + if(!okay) + return false; + uint bNum = b.mid(bLast + 1, bNext - bLast - 1).toUInt(&okay); + if(!okay) + return true; + + if(aNum != bNum) + return aNum < bNum; + + aLast = aNext; + bLast = bNext; + } +} + +bool lessBookmark(const KBookmark & first, const KBookmark & second) //FIXME Using internal represantation +{ + return lessAddress(first.address(), second.address()); +} + +KBookmark::List KEBApp::allBookmarks() const +{ + KBookmark::List bookmarks; + selectedBookmarksExpandedHelper(GlobalBookmarkManager::self()->root(), bookmarks); + return bookmarks; +} + +KBookmark::List KEBApp::selectedBookmarks() const +{ + KBookmark::List bookmarks; + const QModelIndexList & list = mBookmarkListView->selectionModel()->selectedIndexes(); + if (!list.isEmpty()) { + QModelIndexList::const_iterator it, end; + end = list.constEnd(); + for( it = list.constBegin(); it != end; ++it) { + if((*it).column() != 0) + continue; + KBookmark bk = mBookmarkListView->bookmarkModel()->bookmarkForIndex(*it);; + if(bk.address() != GlobalBookmarkManager::self()->root().address()) + bookmarks.push_back( bk ); + } + qSort(bookmarks.begin(), bookmarks.end(), lessBookmark); + } else { + bookmarks.push_back(firstSelected()); + } + + return bookmarks; +} + +KBookmark::List KEBApp::selectedBookmarksExpanded() const +{ + KBookmark::List bookmarks = selectedBookmarks(); + KBookmark::List result; + KBookmark::List::const_iterator it, end; + end = bookmarks.constEnd(); + for(it = bookmarks.constBegin(); it != end; ++it) + { + selectedBookmarksExpandedHelper( *it, result ); + } + return result; +} + +void KEBApp::selectedBookmarksExpandedHelper(const KBookmark& bk, KBookmark::List & bookmarks) const +{ + //FIXME in which order parents should ideally be: parent then child + // or child and then parents + if(bk.isGroup()) + { + KBookmarkGroup parent = bk.toGroup(); + KBookmark child = parent.first(); + while(!child.isNull()) + { + selectedBookmarksExpandedHelper(child, bookmarks); + child = parent.next(child); + } + } + else + { + bookmarks.push_back( bk ); + } +} + +void KEBApp::updateStatus(const QString &url) +{ + if(m_bkinfo->bookmark().url() == url) + m_bkinfo->updateStatus(); +} + +KEBApp::~KEBApp() { + + // Save again, just in case the user expanded/collapsed folders (#131127) + GlobalBookmarkManager::self()->notifyManagers(); + + s_topLevel = 0; + delete m_cmdHistory; + delete m_actionsImpl; + delete mBookmarkListView; + delete GlobalBookmarkManager::self(); +} + +KToggleAction* KEBApp::getToggleAction(const char *action) const { + return static_cast(actionCollection()->action(action)); +} + +void KEBApp::resetActions() { + stateChanged("disablestuff"); + stateChanged("normal"); + + if (!m_readOnly) + stateChanged("notreadonly"); +} + +void KEBApp::selectionChanged() +{ + updateActions(); +} + +void KEBApp::updateActions() { + resetActions(); + setActionsEnabled(getSelectionAbilities()); +} + +void KEBApp::slotClipboardDataChanged() { + // kDebug() << "KEBApp::slotClipboardDataChanged"; + if (!m_readOnly) { + m_canPaste = KBookmark::List::canDecode( + QApplication::clipboard()->mimeData()); + updateActions(); + } +} + +/* -------------------------- */ + +void KEBApp::notifyCommandExecuted() { + // kDebug() << "KEBApp::notifyCommandExecuted()"; + updateActions(); +} + +/* -------------------------- */ + +void KEBApp::slotConfigureToolbars() { + saveMainWindowSettings(KConfigGroup( KGlobal::config(), "MainWindow") ); + KEditToolBar dlg(actionCollection(), this); + connect(&dlg, SIGNAL(newToolBarConfig()), + SLOT(slotNewToolbarConfig())); + dlg.exec(); +} + +void KEBApp::slotNewToolbarConfig() { + // called when OK or Apply is clicked + createGUI(); + applyMainWindowSettings(KConfigGroup(KGlobal::config(), "MainWindow") ); +} + +/* -------------------------- */ + +#include "moc_toplevel.cpp" + diff --git a/keditbookmarks/toplevel.h b/keditbookmarks/toplevel.h new file mode 100644 index 00000000..dcd8b80d --- /dev/null +++ b/keditbookmarks/toplevel.h @@ -0,0 +1,136 @@ +/* This file is part of the KDE project + Copyright (C) 2000 David Faure + Copyright (C) 2002-2003 Alexander Kellett + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) version 3. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see +*/ + +#ifndef TOPLEVEL_H +#define TOPLEVEL_H + +#include +#include +#include +#include +#include "bookmarklistview.h" + +class ActionsImpl; +class CommandHistory; +class KBookmarkModel; +class KBookmarkManager; +class KToggleAction; +class KBookmarkEditorIface; +class BookmarkInfoWidget; +class BookmarkListView; + +struct SelcAbilities { + bool itemSelected:1; + bool group:1; + bool root:1; + bool separator:1; + bool urlIsEmpty:1; + bool multiSelect:1; + bool singleSelect:1; + bool notEmpty:1; + bool deleteEnabled:1; +}; + +class KEBApp : public KXmlGuiWindow { + Q_OBJECT + Q_CLASSINFO("D-Bus Interface", "org.kde.keditbookmarks") +public: + static KEBApp* self() { return s_topLevel; } + + KEBApp(const QString & bookmarksFile, bool readonly, const QString &address, bool browser, const QString &caption, const QString& dbusObjectName); + virtual ~KEBApp(); + + void reset(const QString & caption, const QString & bookmarksFileName); + + void updateActions(); + void updateStatus(const QString &url); + SelcAbilities getSelectionAbilities() const; + void setActionsEnabled(SelcAbilities); + + QMenu* popupMenuFactory(const char *type) + { + QWidget * menu = factory()->container(type, this); + return dynamic_cast(menu); + } + + KToggleAction* getToggleAction(const char *) const; + + QString caption() const { return m_caption; } + bool readonly() const { return m_readOnly; } + bool browser() const { return m_browser; } + bool nsShown() const; + + BookmarkInfoWidget *bkInfo() { return m_bkinfo; } + + void expandAll(); + void collapseAll(); + + enum Column { + NameColumn = 0, + UrlColumn = 1, + CommentColumn = 2, + StatusColumn = 3 + }; + void startEdit( Column c ); + KBookmark firstSelected() const; + QString insertAddress() const; + KBookmark::List selectedBookmarks() const; + KBookmark::List selectedBookmarksExpanded() const; + KBookmark::List allBookmarks() const; + +public Q_SLOTS: + void notifyCommandExecuted(); + + Q_SCRIPTABLE QString bookmarkFilename(); + +public Q_SLOTS: + void slotConfigureToolbars(); + +private Q_SLOTS: + void slotClipboardDataChanged(); + void slotNewToolbarConfig(); + void selectionChanged(); + void setCancelFavIconUpdatesEnabled(bool); + void setCancelTestsEnabled(bool); + +private: + void selectedBookmarksExpandedHelper(const KBookmark& bk, + KBookmark::List & bookmarks) const; + BookmarkListView * mBookmarkListView; + BookmarkFolderView * mBookmarkFolderView; +private: + + void resetActions(); + void createActions(); + + static KEBApp *s_topLevel; + + ActionsImpl* m_actionsImpl; + CommandHistory *m_cmdHistory; + QString m_bookmarksFilename; + QString m_caption; + QString m_dbusObjectName; + + BookmarkInfoWidget *m_bkinfo; + + bool m_canPaste:1; + bool m_readOnly:1; + bool m_browser:1; +}; + +#endif diff --git a/kfind/AUTHORS b/kfind/AUTHORS new file mode 100644 index 00000000..cf0c32eb --- /dev/null +++ b/kfind/AUTHORS @@ -0,0 +1,18 @@ +kfind has been developed by : + + Eric Coquelle + Beppe Grimaldi + Martin Hartig + Stephan Kulow + Mario Weilguni + Alex Zepeda + Miroslav Fl�r + Harri Porten + Dima Rogozin + Carsten Pfeiffer + Hans Petter Bieker + Waldo Bastian + Alexander Neundorf + Albert R. Valiev + Clarence Dang + diff --git a/kfind/CMakeLists.txt b/kfind/CMakeLists.txt new file mode 100644 index 00000000..dfdc8f16 --- /dev/null +++ b/kfind/CMakeLists.txt @@ -0,0 +1,34 @@ +include_directories( + ${CMAKE_SOURCE_DIR}/libs/konq + ${CMAKE_BINARY_DIR}/libs/konq +) + +set(kfind_SRCS + main.cpp + kfinddlg.cpp + kftabdlg.cpp + kquery.cpp + kdatecombo.cpp + kfindtreeview.cpp +) + +add_executable(kfind ${kfind_SRCS}) + +target_link_libraries(kfind + ${KDE4_KFILE_LIBS} + konq +) + +install( + TARGETS kfind + ${INSTALL_TARGETS_DEFAULT_ARGS} +) + +########### install files ############### + +install( + PROGRAMS kfind.desktop + DESTINATION ${KDE4_XDG_APPS_INSTALL_DIR} +) + +kde4_install_icons(${KDE4_ICON_INSTALL_DIR}) diff --git a/kfind/Messages.sh b/kfind/Messages.sh new file mode 100644 index 00000000..76b432f4 --- /dev/null +++ b/kfind/Messages.sh @@ -0,0 +1,2 @@ +#! /usr/bin/env bash +$XGETTEXT *.cpp *.h -o $podir/kfindpart.pot diff --git a/kfind/hi16-app-kfind.png b/kfind/hi16-app-kfind.png new file mode 100644 index 00000000..6a8e7971 Binary files /dev/null and b/kfind/hi16-app-kfind.png differ diff --git a/kfind/hi22-app-kfind.png b/kfind/hi22-app-kfind.png new file mode 100644 index 00000000..edbc02ee Binary files /dev/null and b/kfind/hi22-app-kfind.png differ diff --git a/kfind/hi32-app-kfind.png b/kfind/hi32-app-kfind.png new file mode 100644 index 00000000..b155bef2 Binary files /dev/null and b/kfind/hi32-app-kfind.png differ diff --git a/kfind/hi48-app-kfind.png b/kfind/hi48-app-kfind.png new file mode 100644 index 00000000..f8e4b607 Binary files /dev/null and b/kfind/hi48-app-kfind.png differ diff --git a/kfind/hi64-app-kfind.png b/kfind/hi64-app-kfind.png new file mode 100644 index 00000000..e6262554 Binary files /dev/null and b/kfind/hi64-app-kfind.png differ diff --git a/kfind/kdatecombo.cpp b/kfind/kdatecombo.cpp new file mode 100644 index 00000000..609f9d20 --- /dev/null +++ b/kfind/kdatecombo.cpp @@ -0,0 +1,146 @@ +/******************************************************************* +* kdatecombo.cpp +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +******************************************************************/ + +#include "kdatecombo.h" + +#include "moc_kdatecombo.cpp" + +#include +//Added by qt3to4: +#include +#include + +#include +#include +#include +#include +#include + +KDateCombo::KDateCombo(QWidget *parent) : KComboBox(parent) +{ + setEditable( false ); + + QDate date = QDate::currentDate(); + initObject(date); +} + +KDateCombo::KDateCombo(const QDate & date, QWidget *parent) : KComboBox(parent) +{ + setEditable( false ); + + initObject(date); +} + +void KDateCombo::initObject(const QDate & date) +{ + setValidator(0); + popupFrame = new KPopupFrame(this); + popupFrame->installEventFilter(this); + datePicker = new KDatePicker(date, popupFrame); + datePicker->setMinimumSize(datePicker->sizeHint()); + datePicker->installEventFilter(this); + popupFrame->setMainWidget(datePicker); + setDate(date); + + connect(datePicker, SIGNAL(dateSelected(QDate)), this, SLOT(dateEnteredEvent(QDate))); + connect(datePicker, SIGNAL(dateEntered(QDate)), this, SLOT(dateEnteredEvent(QDate))); +} + +KDateCombo::~KDateCombo() +{ + delete datePicker; + delete popupFrame; +} + +QString KDateCombo::date2String(const QDate & date) +{ + return(KGlobal::locale()->formatDate(date, KLocale::ShortDate)); +} + +QDate & KDateCombo::string2Date(const QString & str, QDate *qd) +{ + return *qd = KGlobal::locale()->readDate(str); +} + +QDate & KDateCombo::getDate(QDate *currentDate) +{ + return string2Date(currentText(), currentDate); +} + +bool KDateCombo::setDate(const QDate & newDate) +{ + if (newDate.isValid()) + { + if (count()) + clear(); + addItem(date2String(newDate)); + return true; + } + return false; +} + +void KDateCombo::dateEnteredEvent(const QDate &newDate) +{ + QDate tempDate = newDate; + if (!tempDate.isValid()) + tempDate = datePicker->date(); + popupFrame->hide(); + setDate(tempDate); +} + +void KDateCombo::mousePressEvent (QMouseEvent * e) +{ + if (e->button() & Qt::LeftButton) + { + if (rect().contains( e->pos())) + { + QDate tempDate; + getDate(& tempDate); + datePicker->setDate(tempDate); + popupFrame->popup(mapToGlobal(QPoint(0, height()))); + } + } +} + +bool KDateCombo::eventFilter (QObject*, QEvent* e) +{ + if ( e->type() == QEvent::MouseButtonPress ) + { + QMouseEvent *me = (QMouseEvent *)e; + QPoint p = mapFromGlobal( me->globalPos() ); + if (rect().contains( p ) ) + { + QTimer::singleShot(10, this, SLOT(dateEnteredEvent())); + return true; + } + } + else if ( e->type() == QEvent::KeyRelease ) + { + QKeyEvent *k = (QKeyEvent *)e; + + if (k->key()==Qt::Key_Escape) { + popupFrame->hide(); + return true; + } + else { + return false; + } + } + + return false; +} diff --git a/kfind/kdatecombo.h b/kfind/kdatecombo.h new file mode 100644 index 00000000..0051ea48 --- /dev/null +++ b/kfind/kdatecombo.h @@ -0,0 +1,62 @@ +/******************************************************************* +* kdatecombo.h +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +******************************************************************/ + +#ifndef KDATECOMBO_H +#define KDATECOMBO_H + +#include +#include + +#include + +/** + *@author Beppe Grimaldi + */ + +class KDatePicker; +class KPopupFrame; + +class KDateCombo : public KComboBox { + Q_OBJECT + +public: + KDateCombo(QWidget *parent=0); + explicit KDateCombo(const QDate & date, QWidget *parent=0); + ~KDateCombo(); + + QDate & getDate(QDate *currentDate); + bool setDate(const QDate & newDate); + +private: + KPopupFrame * popupFrame; + KDatePicker * datePicker; + + void initObject(const QDate & date); + + QString date2String(const QDate &); + QDate & string2Date(const QString &, QDate * ); + +protected: + bool eventFilter (QObject*, QEvent*); + virtual void mousePressEvent (QMouseEvent * e); + +protected Q_SLOTS: + void dateEnteredEvent(const QDate &d=QDate()); +}; + +#endif diff --git a/kfind/kfind.desktop b/kfind/kfind.desktop new file mode 100755 index 00000000..0f03c3e9 --- /dev/null +++ b/kfind/kfind.desktop @@ -0,0 +1,99 @@ +[Desktop Entry] +Exec=kfind %u +Icon=kfind +X-DocPath=kfind/index.html +Type=Application +Terminal=false +Name=Find Files/Folders +Name[af]=Soek Lêers/Gidse +Name[ar]=إبحث عن ملفات/مجلّدات +Name[as]=নথিপত্ৰ/পঞ্জিকা বিচাৰক +Name[ast]=Guetar ficheros/carpetes +Name[be]=Пошук файлаў і тэчак +Name[be@latin]=Pošuk fajłaŭ i katalohaŭ +Name[bg]=Търсене на файлове +Name[bn]=ফাইল/ফোল্ডার অনুসন্ধান +Name[bn_IN]=ফাইল/ফোল্ডার অনুসন্ধান +Name[br]=Klask restroù/renkelloù +Name[bs]=Traženje datoteka i fascikli +Name[ca]=Cerca fitxers i carpetes +Name[ca@valencia]=Cerca fitxers i carpetes +Name[cs]=Najít soubory/složky +Name[csb]=Szëkba lopków/katalogów +Name[da]=Find filer/mapper +Name[de]=Dateien/Ordner suchen +Name[el]=Αναζήτηση αρχείων/φακέλων +Name[en_GB]=Find Files/Folders +Name[eo]=Trovi dosierojn/dosierujojn +Name[es]=Buscar archivos/carpetas +Name[et]=Failide/kataloogide otsimine +Name[eu]=Bilatu fitxategiak/karpetak +Name[fa]=یافتن پرونده‌ها/پوشه‌ها +Name[fi]=Etsi tiedostoja ja kansioita +Name[fr]=Recherche de fichiers / dossiers +Name[fy]=Triemmen/mappen sykje +Name[ga]=Aimsigh Comhaid/Comhadlanna +Name[gl]=Buscar ficheiros/cartafoles +Name[gu]=ફાઇલો/ફોલ્ડરો શોધો +Name[he]=חיפוש קבצים ותיקיות +Name[hi]=फ़ाइलें/फोल्डर ढूंढें +Name[hne]=फाइल मन ल/फोल्डर मन ल खोजव +Name[hr]=Traži datoteke/mape +Name[hsb]=Dataje/zapiski namakać +Name[hu]=Fájlkereső +Name[ia]=Trova files/dossieres +Name[id]=Cari Berkas/Folder +Name[is]=Finna skrár/möppur +Name[it]=Trova file e cartelle +Name[ja]=ファイル/フォルダを検索 +Name[ka]=ფაილთა და საქაღალდეთა ძიება +Name[kk]=Файлды не қапшықты табу +Name[km]=រក​ឯកសារ/ថត +Name[kn]=ಕಡತಗಳನ್ನು/ಕಡತಕೋಶಗಳನ್ನು ಹುಡುಕು +Name[ko]=파일/폴더 찾기 +Name[ku]=Pelan/peldankan Bibîne +Name[lt]=Rasti failus/aplankus +Name[lv]=Meklēt failus/mapes +Name[mai]=फाइलसभ/फोल्डरसभ ढूँढू +Name[mk]=Пронајди датотеки/папки +Name[ml]=രേഖകളും കൂടകളും തിരയുക +Name[mr]=फाईल्स/संचयीका शोधा +Name[ms]=Cari Fail/Folder +Name[nb]=Finn filer/mapper +Name[nds]=Dateien un Ornern söken +Name[ne]=फाइल/फोल्डर फेला पार्नुहोस् +Name[nl]=Bestanden/mappen zoeken +Name[nn]=Finn filer/mapper +Name[or]=ଫାଇଲ/ଫୋଲଡର ଖୋଜନ୍ତୁ +Name[pa]=ਫਾਇਲ/ਫੋਲਡਰ ਖੋਜ +Name[pl]=Wyszukiwanie plików/katalogów +Name[pt]=Procurar Ficheiros/Pastas +Name[pt_BR]=Procurar arquivos/pastas +Name[ro]=Caută fișiere/dosare +Name[ru]=Поиск файлов и папок +Name[se]=Oza fiillaid dahje máhpaid +Name[si]=ගොනු/බහලුම් සොයන්න +Name[sk]=Nájsť súbory/priečinky +Name[sl]=Najdi datoteke in mape +Name[sr]=Тражење фајлова и фасцикли +Name[sr@ijekavian]=Тражење фајлова и фасцикли +Name[sr@ijekavianlatin]=Traženje fajlova i fascikli +Name[sr@latin]=Traženje fajlova i fascikli +Name[sv]=Hitta filer eller kataloger +Name[ta]=கோப்புகள்/அடைவுகளைக் கண்டுபிடி +Name[te]=దస్త్రాలు/ఫొల్డర్లను వెతుకు +Name[tg]=Ҷустуҷӯи файлҳо/феҳристҳо +Name[th]=ค้นหาแฟ้ม/โฟลเดอร์ +Name[tr]=Dosya / Dizin Bul +Name[ug]=ھۆججەت/قىسقۇچ ئىزدە +Name[uk]=Пошук файлів та тек +Name[uz]=Fayl/jildlarni qidirish +Name[uz@cyrillic]=Файл/жилдларни қидириш +Name[vi]=Tìm Tập tin/Thư mục +Name[wa]=Trover des fitchîs/ridants +Name[x-test]=xxFind Files/Foldersxx +Name[zh_CN]=查找文件/文件夹 +Name[zh_TW]=尋找檔案/資料夾 +X-KDE-StartupNotify=true +OnlyShowIn=KDE; +Categories=Qt;KDE;Core; diff --git a/kfind/kfinddlg.cpp b/kfind/kfinddlg.cpp new file mode 100644 index 00000000..b034367e --- /dev/null +++ b/kfind/kfinddlg.cpp @@ -0,0 +1,303 @@ +/******************************************************************* +* kfinddlg.cpp +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +******************************************************************/ + +#include "kfinddlg.h" +#include "moc_kfinddlg.cpp" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "kftabdlg.h" +#include "kquery.h" +#include "kfindtreeview.h" + +KfindDlg::KfindDlg(const KUrl & url, QWidget *parent) + : KDialog( parent ) +{ + setButtons( User1 | User2 | User3 | Close | Help ); + setDefaultButton( User3 ); + setModal( true ); + + setButtonGuiItem( User3, KStandardGuiItem::find()); + setButtonGuiItem( User2, KStandardGuiItem::stop() ); + setButtonGuiItem( User1, KStandardGuiItem::saveAs() ); + + QWidget::setWindowTitle( i18nc("@title:window", "Find Files/Folders" ) ); + setButtonsOrientation(Qt::Vertical); + + enableButton(User3, true); // Enable "Find" + enableButton(User2, false); // Disable "Stop" + enableButton(User1, false); // Disable "Save As..." + + isResultReported = false; + + QFrame *frame = new QFrame; + setMainWidget( frame ); + + // create tabwidget + tabWidget = new KfindTabWidget( frame ); + tabWidget->setURL( url ); + tabWidget->setFocus(); + + // prepare window for find results + win = new KFindTreeView(frame, this); + + mStatusBar = new KStatusBar(frame); + mStatusBar->insertItem("AMiddleLengthText...", 0); + setStatusMsg( i18nc("the application is currently idle, there is no active search", "Idle.") ); + mStatusBar->setItemAlignment(0, Qt::AlignLeft | Qt::AlignVCenter); + mStatusBar->insertPermanentItem(QString(), 1, 1); + mStatusBar->setItemAlignment(1, Qt::AlignRight | Qt::AlignVCenter); + + QVBoxLayout *vBox = new QVBoxLayout(frame); + vBox->addWidget(tabWidget, 0); + vBox->addWidget(win, 1); + vBox->addWidget(mStatusBar, 0); + + connect(tabWidget, SIGNAL(startSearch()), + this, SLOT(startSearch())); + connect(this, SIGNAL(user3Clicked()), + this, SLOT(startSearch())); + connect(this, SIGNAL(user2Clicked()), + this, SLOT(stopSearch())); + connect(this, SIGNAL(user1Clicked()), + win, SLOT(saveResults())); + + connect( this, SIGNAL(closeClicked()), this, SLOT(finishAndClose()) ); + + connect(win ,SIGNAL(resultSelected(bool)), + this,SIGNAL(resultSelected(bool))); + + query = new KQuery(frame); + connect(query, SIGNAL(result(int)), SLOT(slotResult(int))); + connect(query, SIGNAL(foundFileList(QList >)), SLOT(addFiles(QList >))); + + KHelpMenu *helpMenu = new KHelpMenu(this, KGlobal::mainComponent().aboutData(), true); + setButtonMenu( Help, helpMenu->menu() ); + dirwatch=NULL; +} + +KfindDlg::~KfindDlg() +{ + stopSearch(); + + delete dirwatch; +} + +void KfindDlg::finishAndClose() +{ + //Stop the current search and closes the dialog + stopSearch(); + close(); +} + +void KfindDlg::setProgressMsg(const QString &msg) +{ + mStatusBar->changeItem(msg, 1); +} + +void KfindDlg::setStatusMsg(const QString &msg) +{ + mStatusBar->changeItem(msg, 0); +} + + +void KfindDlg::startSearch() +{ + tabWidget->setQuery(query); + + isResultReported = false; + + // Reset count - use the same i18n as below + setProgressMsg(i18np("one file found", "%1 files found", 0)); + + emit resultSelected(false); + emit haveResults(false); + + enableButton(User3, false); // Disable "Find" + enableButton(User2, true); // Enable "Stop" + enableButton(User1, false); // Disable "Save As..." + + delete dirwatch; + dirwatch=new KDirWatch(); + connect(dirwatch, SIGNAL(created(QString)), this, SLOT(slotNewItems(QString))); + connect(dirwatch, SIGNAL(deleted(QString)), this, SLOT(slotDeleteItem(QString))); + dirwatch->addDir(query->url().path(), KDirWatch::WatchFiles); + +#if 0 + // waba: Watching for updates is disabled for now because even with FAM it causes too + // much problems. See BR68220, BR77854, BR77846, BR79512 and BR85802 + // There are 3 problems: + // 1) addDir() keeps looping on recursive symlinks + // 2) addDir() scans all subdirectories, so it basically does the same as the process that + // is started by KQuery but in-process, undoing the advantages of using a separate find process + // A solution could be to let KQuery emit all the directories it has searched in. + // Either way, putting dirwatchers on a whole file system is probably just too much. + // 3) FAM has a tendency to deadlock with so many files (See BR77854) This has hopefully + // been fixed in KDirWatch, but that has not yet been confirmed. + + //Getting a list of all subdirs + if(tabWidget->isSearchRecursive() && (dirwatch->internalMethod() == KDirWatch::FAM)) + { + const QStringList subdirs=getAllSubdirs(query->url().path()); + for(QStringList::const_iterator it = subdirs.constBegin(); it != subdirs.constEnd(); ++it) + dirwatch->addDir(*it,true); + } +#endif + + win->beginSearch(query->url()); + tabWidget->beginSearch(); + + setStatusMsg(i18n("Searching...")); + query->start(); +} + +void KfindDlg::stopSearch() +{ + query->kill(); +} + +void KfindDlg::newSearch() +{ + // WABA: Not used any longer? + stopSearch(); + + tabWidget->setDefaults(); + + emit haveResults(false); + emit resultSelected(false); + + setFocus(); +} + +void KfindDlg::slotResult(int errorCode) +{ + if (errorCode == 0) + setStatusMsg( i18nc("the application is currently idle, there is no active search", "Idle.") ); + else if (errorCode == KIO::ERR_USER_CANCELED) + setStatusMsg(i18n("Canceled.")); + else if (errorCode == KIO::ERR_MALFORMED_URL) + { + setStatusMsg(i18n("Error.")); + KMessageBox::sorry( this, i18n("Please specify an absolute path in the \"Look in\" box.")); + } + else if (errorCode == KIO::ERR_DOES_NOT_EXIST) + { + setStatusMsg(i18n("Error.")); + KMessageBox::sorry( this, i18n("Could not find the specified folder.")); + } + else + { + kDebug()<<"KIO error code: "<endSearch(); + tabWidget->endSearch(); + setFocus(); + +} + +void KfindDlg::addFiles( const QList< QPair > & pairs) +{ + win->insertItems( pairs ); + + if (!isResultReported) + { + emit haveResults(true); + isResultReported = true; + } + + QString str = i18np("one file found", "%1 files found", win->itemCount()); + setProgressMsg( str ); +} + +void KfindDlg::setFocus() +{ + tabWidget->setFocus(); +} + +void KfindDlg::copySelection() +{ + win->copySelection(); +} + +void KfindDlg::about () +{ + KAboutApplicationDialog dlg(0, this); + dlg.exec (); +} + +void KfindDlg::slotDeleteItem(const QString& file) +{ + kDebug()<removeItem( url ); + + QString str = i18np("one file found", "%1 files found", win->itemCount()); + setProgressMsg( str ); +} + +void KfindDlg::slotNewItems( const QString& file ) +{ + kDebug()<url().path(KUrl::AddTrailingSlash))==0 ) + { + KUrl url; + url.setPath ( file ); + if ( !win->isInserted( url ) ) + query->slotListEntries( QStringList() << file ); + } +} + +QStringList KfindDlg::getAllSubdirs(QDir d) +{ + QStringList dirs; + QStringList subdirs; + + d.setFilter( QDir::Dirs ); + dirs = d.entryList(); + + for(QStringList::const_iterator it = dirs.constBegin(); it != dirs.constEnd(); ++it) + { + if((*it==".")||(*it=="..")) + continue; + subdirs.append(d.path()+'/'+*it); + subdirs+=getAllSubdirs(QString(d.path()+QLatin1Char('/')+*it)); + } + return subdirs; +} diff --git a/kfind/kfinddlg.h b/kfind/kfinddlg.h new file mode 100644 index 00000000..793cb375 --- /dev/null +++ b/kfind/kfinddlg.h @@ -0,0 +1,80 @@ +/******************************************************************* +* kfinddlg.h +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +******************************************************************/ + +#ifndef KFINDDLG_H +#define KFINDDLG_H + +#include +#include +#include + +#include +#include + +class KQuery; +class KUrl; +class KFileItem; +class KfindTabWidget; +class KFindTreeView; +class KStatusBar; + +class KfindDlg: public KDialog +{ +Q_OBJECT + +public: + explicit KfindDlg(const KUrl & url, QWidget * parent = 0); + ~KfindDlg(); + void copySelection(); + + void setStatusMsg(const QString &); + void setProgressMsg(const QString &); + +private: + /*Return a QStringList of all subdirs of d*/ + QStringList getAllSubdirs(QDir d); + +public Q_SLOTS: + void startSearch(); + void stopSearch(); + void newSearch(); + void addFiles( const QList< QPair > & ); + void setFocus(); + void slotResult(int); +// void slotSearchDone(); + void about (); + void slotDeleteItem(const QString&); + void slotNewItems( const QString& ); + + void finishAndClose(); + +Q_SIGNALS: + void haveResults(bool); + void resultSelected(bool); + +private: + KfindTabWidget *tabWidget; + KFindTreeView * win; + + bool isResultReported; + KQuery *query; + KStatusBar *mStatusBar; + KDirWatch *dirwatch; +}; + +#endif diff --git a/kfind/kfindtreeview.cpp b/kfind/kfindtreeview.cpp new file mode 100644 index 00000000..dca838c9 --- /dev/null +++ b/kfind/kfindtreeview.cpp @@ -0,0 +1,672 @@ +/******************************************************************* +* kfindtreeview.cpp +* Copyright 2009 Dario Andres Rodriguez +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +******************************************************************/ + +#include "kfindtreeview.h" + +#include "kfinddlg.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +// Permission strings +static const char* const perm[4] = { + I18N_NOOP( "Read-write" ), + I18N_NOOP( "Read-only" ), + I18N_NOOP( "Write-only" ), + I18N_NOOP( "Inaccessible" ) }; +#define RW 0 +#define RO 1 +#define WO 2 +#define NA 3 + +//BEGIN KFindItemModel + +KFindItemModel::KFindItemModel( KFindTreeView * parentView ) : + QAbstractTableModel( parentView ) +{ + m_view = parentView; +} + +QVariant KFindItemModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if ( role == Qt::DisplayRole && orientation == Qt::Horizontal) { + switch (section) { + case 0: + return i18nc("file name column","Name"); + case 1: + return i18nc("name of the containing folder","In Subfolder"); + case 2: + return i18nc("file size column","Size"); + case 3: + return i18nc("modified date column","Modified"); + case 4: + return i18nc("file permissions column","Permissions"); + case 5: + return i18nc("first matching line of the query string in this file", "First Matching Line"); + default: + return QVariant(); + } + } + return QVariant(); +} + +void KFindItemModel::insertFileItems( const QList< QPair > & pairs) +{ + if ( pairs.size() > 0 ) + { + beginInsertRows( QModelIndex(), m_itemList.size(), m_itemList.size()+pairs.size()-1 ); + + QList< QPair >::const_iterator it = pairs.constBegin(); + QList< QPair >::const_iterator end = pairs.constEnd(); + + for (; it != end; ++it) + { + QPair pair = *it; + + QString subDir = m_view->reducedDir(pair.first.url().directory(KUrl::AppendTrailingSlash)); + m_itemList.append( KFindItem( pair.first, subDir, pair.second ) ); + } + + endInsertRows(); + } +} + +int KFindItemModel::rowCount ( const QModelIndex & parent ) const +{ + if( !parent.isValid() ) + return m_itemList.count(); //Return itemcount for toplevel + else + return 0; +} + +KFindItem KFindItemModel::itemAtIndex( const QModelIndex & index ) const +{ + if ( index.isValid() && m_itemList.size() >= index.row() ) + return m_itemList.at( index.row() ); + + return KFindItem(); +} + +QVariant KFindItemModel::data ( const QModelIndex & index, int role ) const +{ + if (!index.isValid()) + return QVariant(); + + if (index.column() > 6 || index.row() >= m_itemList.count() ) + return QVariant(); + + switch( role ) + { + case Qt::DisplayRole: + case Qt::DecorationRole: + case Qt::UserRole: + return m_itemList.at( index.row() ).data( index.column(), role ); + default: + return QVariant(); + } + return QVariant(); +} + +void KFindItemModel::removeItem( const KUrl & url ) +{ + int itemCount = m_itemList.size(); + for ( int i = 0; i < itemCount; i++) + { + KFindItem item = m_itemList.at(i); + if ( item.getFileItem().url() == url ) + { + beginRemoveRows( QModelIndex(), i, i ); + m_itemList.removeAt( i ); + endRemoveRows(); + return; + } + } +} + +bool KFindItemModel::isInserted( const KUrl & url ) +{ + int itemCount = m_itemList.size(); + for ( int i = 0; i < itemCount; i++) + { + KFindItem item = m_itemList.at(i); + if ( item.getFileItem().url() == url ) + { + return true; + } + } + return false; +} + +void KFindItemModel::clear() +{ + beginRemoveRows( QModelIndex(), 0, m_itemList.size() ); + m_itemList.clear(); + endRemoveRows(); +} + +Qt::ItemFlags KFindItemModel::flags(const QModelIndex &index) const +{ + Qt::ItemFlags defaultFlags = Qt::ItemIsSelectable | Qt::ItemIsEnabled; + if (index.isValid()) + return Qt::ItemIsDragEnabled | defaultFlags; + return defaultFlags; +} + +QMimeData * KFindItemModel::mimeData(const QModelIndexList &indexes) const +{ + KUrl::List uris; + + foreach ( const QModelIndex & index, indexes ) + { + if( index.isValid()) + { + if( index.column() == 0 ) //Only use the first column item + { + uris.append( m_itemList.at( index.row() ).getFileItem().url() ); + } + } + } + + if ( uris.count() <= 0 ) + return 0; + + QMimeData * mimeData = new QMimeData(); + uris.populateMimeData( mimeData ); + + return mimeData; +} + +//END KFindItemModel + +//BEGIN KFindItem + +KFindItem::KFindItem( const KFileItem & _fileItem, const QString & subDir, const QString & matchingLine ) +{ + m_fileItem = _fileItem; + m_subDir = subDir; + m_matchingLine = matchingLine; + + //TODO more caching ? + if ( !m_fileItem.isNull() && m_fileItem.isLocalFile() ) + { + QFileInfo fileInfo(m_fileItem.url().toLocalFile()); + + int perm_index; + if(fileInfo.isReadable()) + perm_index = fileInfo.isWritable() ? RW : RO; + else + perm_index = fileInfo.isWritable() ? WO : NA; + + m_permission = i18n(perm[perm_index]); + + m_icon = KIcon( m_fileItem.iconName() ); + } +} + +QVariant KFindItem::data( int column, int role ) const +{ + if ( m_fileItem.isNull() ) + return QVariant(); + + if( role == Qt::DecorationRole ) + { + if (column == 0) + return m_icon; + else + return QVariant(); + } + + if( role == Qt::DisplayRole ) + switch( column ) + { + case 0: + return m_fileItem.url().fileName(KUrl::ObeyTrailingSlash); + case 1: + return m_subDir; + case 2: + return KIO::convertSize( m_fileItem.size() ); + case 3: + return m_fileItem.timeString(KFileItem::ModificationTime); + case 4: + return m_permission; + case 5: + return m_matchingLine; + default: + return QVariant(); + } + + if( role == Qt::UserRole ) + switch( column ) + { + case 2: + return m_fileItem.size(); + case 3: + return m_fileItem.time(KFileItem::ModificationTime).toTime_t(); + default: + return QVariant(); + } + + return QVariant(); +} + +//END KFindItem + +//BEGIN KFindSortFilterProxyModel + +bool KFindSortFilterProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const +{ + //Order by UserData size in bytes or unix date + if( left.column() == 2 || left.column() == 3) + { + qulonglong leftData = sourceModel()->data( left, Qt::UserRole ).toULongLong(); + qulonglong rightData = sourceModel()->data( right, Qt::UserRole ).toULongLong(); + return leftData < rightData; + } + // Default sorting rules for string values + else + { + return QSortFilterProxyModel::lessThan( left, right ); + } +} + +//END KFindSortFilterProxyModel + +//BEGIN KFindTreeView + +KFindTreeView::KFindTreeView( QWidget *parent, KfindDlg * findDialog ) + : QTreeView( parent ) , + m_contextMenu(0), + m_kfindDialog(findDialog) +{ + //Configure model and proxy model + m_model = new KFindItemModel( this ); + m_proxyModel = new KFindSortFilterProxyModel(); + m_proxyModel->setSourceModel( m_model ); + setModel( m_proxyModel ); + + //Configure QTreeView + setRootIsDecorated( false ); + setSelectionMode( QAbstractItemView::ExtendedSelection ); + setSortingEnabled( true ); + setDragEnabled( true ); + setContextMenuPolicy( Qt::CustomContextMenu ); + + connect( this, SIGNAL(customContextMenuRequested(QPoint)), + this, SLOT(contextMenuRequested(QPoint))); + + //Mouse single/double click settings + connect( KGlobalSettings::self(), SIGNAL(settingsChanged(int)), this, SLOT(reconfigureMouseSettings()) ); + reconfigureMouseSettings(); + + // TODO: this is a workaround until Qt-issue 176832 has been fixed (from Dolphin) + connect(this, SIGNAL(pressed(QModelIndex)), this, SLOT(updateMouseButtons())); + + //Generate popup menu actions + m_actionCollection = new KActionCollection( this ); + m_actionCollection->addAssociatedWidget(this); + + KAction * open = KStandardAction::open(this, SLOT(slotExecuteSelected()), this); + m_actionCollection->addAction( "file_open", open ); + + KAction * copy = KStandardAction::copy(this, SLOT(copySelection()), this); + m_actionCollection->addAction( "edit_copy", copy ); + + KAction * openFolder = new KAction( KIcon("window-new"), i18n("&Open containing folder(s)"), this ); + connect( openFolder, SIGNAL(triggered()), this, SLOT(openContainingFolder()) ); + m_actionCollection->addAction( "openfolder", openFolder ); + + KAction * del = new KAction( KIcon("edit-delete"), i18n("&Delete"), this ); + connect( del, SIGNAL(triggered()), this, SLOT(deleteSelectedFiles()) ); + del->setShortcut(Qt::SHIFT + Qt::Key_Delete); + m_actionCollection->addAction( "del", del ); + + KAction * trash = new KAction( KIcon("user-trash"), i18n("&Move to Trash"), this ); + connect( trash, SIGNAL(triggered()), this, SLOT(moveToTrashSelectedFiles()) ); + trash->setShortcut(Qt::Key_Delete); + m_actionCollection->addAction( "trash", trash ); + + header()->setStretchLastSection( true ); + + sortByColumn( 0, Qt::AscendingOrder ); +} + +KFindTreeView::~KFindTreeView() +{ + delete m_model; + delete m_proxyModel; + delete m_actionCollection; +} + +void KFindTreeView::resizeToContents() +{ + resizeColumnToContents( 0 ); + resizeColumnToContents( 1 ); + resizeColumnToContents( 2 ); + resizeColumnToContents( 3 ); +} + +QString KFindTreeView::reducedDir(const QString& fullDir) +{ + if (fullDir.indexOf(m_baseDir)==0) + { + QString tmp=fullDir.mid(m_baseDir.length()); + return tmp; + }; + return fullDir; +} + +void KFindTreeView::beginSearch(const KUrl& baseUrl) +{ + kDebug() << QString("beginSearch in: %1").arg(baseUrl.path()); + m_baseDir = baseUrl.path(KUrl::AddTrailingSlash); + m_model->clear(); +} + +void KFindTreeView::endSearch() +{ + resizeToContents(); +} + +void KFindTreeView::insertItems (const QList< QPair > & pairs) +{ + m_model->insertFileItems( pairs ); +} + +void KFindTreeView::removeItem(const KUrl & url) +{ + KUrl::List list = selectedUrls(); + if ( list.contains(url) ) + { + //Close menu + if( m_contextMenu ) + { + m_contextMenu->hide(); + delete m_contextMenu; + m_contextMenu = 0; + } + } + m_model->removeItem( url ); +} + +// copy to clipboard +void KFindTreeView::copySelection() +{ + QMimeData * mime = m_model->mimeData( m_proxyModel->mapSelectionToSource( selectionModel()->selection() ).indexes() ); + if (mime) + { + QClipboard * cb = kapp->clipboard(); + cb->setMimeData( mime ); + } +} + +void KFindTreeView::saveResults() +{ + KFileDialog *dlg = new KFileDialog(QString(), QString(), this); + dlg->setOperationMode (KFileDialog::Saving); + dlg->setCaption( i18nc("@title:window", "Save Results As") ); + dlg->setFilter( QString("*.html|%1\n*.txt|%2").arg( i18n("HTML page"), i18n("Text file") ) ); + dlg->setConfirmOverwrite(true); + + dlg->exec(); + + KUrl u = dlg->selectedUrl(); + + QString filter = dlg->currentFilter(); + delete dlg; + + if (!u.isValid() || !u.isLocalFile()) + return; + + QString filename = u.toLocalFile(); + + QFile file(filename); + + if ( !file.open(QIODevice::WriteOnly) ) + { + KMessageBox::error(parentWidget(), + i18n("Unable to save results.")); + } + else + { + QTextStream stream( &file ); + stream.setCodec( QTextCodec::codecForLocale() ); + + QList itemList = m_model->getItemList(); + if ( filter == "*.html" ) + { + stream << QString::fromLatin1("\n" + "\n" + "%2\n" + "\n" + "\n

    %2

    \n" + "
    \n") + .arg(QString::fromLatin1(QTextCodec::codecForLocale()->name())) + .arg(i18n("KFind Results File")); + + Q_FOREACH( const KFindItem & item, itemList ) + { + const KFileItem fileItem = item.getFileItem(); + stream << QString::fromLatin1("
    %2
    \n").arg( + fileItem.url().url(), fileItem.url().prettyUrl() ); + + } + stream << QString::fromLatin1("
    \n\n\n"); + } + else + { + Q_FOREACH( const KFindItem & item, itemList ) + { + stream << item.getFileItem().url().url() << endl; + } + } + + file.close(); + m_kfindDialog->setStatusMsg(i18nc("%1=filename", "Results were saved to: %1", filename)); + } +} + +void KFindTreeView::openContainingFolder() +{ + KUrl::List uris = selectedUrls(); + QMap folderMaps; + + //Generate *unique* folders + Q_FOREACH( const KUrl & url, uris ) + { + KUrl dir = url; + dir.setFileName( QString() ); + folderMaps.insert( dir, 0 ); + } + + //TODO if >1 add a warn ? + Q_FOREACH( const KUrl & url, folderMaps.keys() ) + { + (void) new KRun(url, this); + } +} + +void KFindTreeView::slotExecuteSelected() +{ + QModelIndexList selected = m_proxyModel->mapSelectionToSource( selectionModel()->selection() ).indexes(); + if ( selected.size() == 0 ) + return; + + //TODO if >X add a warn ? + Q_FOREACH( const QModelIndex & index, selected ) + { + if( index.column() == 0 ) + { + KFindItem item = m_model->itemAtIndex( index ); + if ( item.isValid() ) + item.getFileItem().run(); + } + } +} + +void KFindTreeView::slotExecute( const QModelIndex & index ) +{ + if( (m_mouseButtons & Qt::LeftButton) && QApplication::keyboardModifiers() == Qt::NoModifier ) + { + if ( !index.isValid() ) + return; + + QModelIndex realIndex = m_proxyModel->mapToSource( index ); + if ( !realIndex.isValid() ) + return; + + KFindItem item = m_model->itemAtIndex( realIndex ); + if ( item.isValid() ) + item.getFileItem().run(); + } +} + +void KFindTreeView::contextMenuRequested( const QPoint & p) +{ + KFileItemList fileList; + + QModelIndexList selected = m_proxyModel->mapSelectionToSource( selectionModel()->selection() ).indexes(); + if ( selected.size() == 0 ) + return; + + Q_FOREACH( const QModelIndex & index, selected ) + { + if( index.column() == 0 ) + { + const KFindItem item = m_model->itemAtIndex( index ); + if( item.isValid() ) + fileList.append( item.getFileItem() ); + } + } + + KParts::BrowserExtension::PopupFlags flags = KParts::BrowserExtension::ShowProperties; // | KParts::BrowserExtension::ShowUrlOperations; + + QList editActions; + editActions.append(m_actionCollection->action("file_open")); + editActions.append(m_actionCollection->action("openfolder")); + editActions.append(m_actionCollection->action("edit_copy")); + editActions.append(m_actionCollection->action("del")); + editActions.append(m_actionCollection->action("trash")); + + KParts::BrowserExtension::ActionGroupMap actionGroups; + actionGroups.insert("editactions", editActions); + + if( m_contextMenu ) + { + m_contextMenu->hide(); + delete m_contextMenu; + m_contextMenu = 0; + } + m_contextMenu = new KonqPopupMenu( fileList, KUrl(), *m_actionCollection, new KNewFileMenu( m_actionCollection, "new_menu", this), 0, flags, this, 0, actionGroups); + + m_contextMenu->exec( this->mapToGlobal( p ) ); +} + +KUrl::List KFindTreeView::selectedUrls() +{ + KUrl::List uris; + + QModelIndexList indexes = m_proxyModel->mapSelectionToSource( selectionModel()->selection() ).indexes(); + Q_FOREACH( const QModelIndex & index, indexes ) + { + if( index.column() == 0 && index.isValid() ) + { + KFindItem item = m_model->itemAtIndex( index ); + if( item.isValid() ) + uris.append( item.getFileItem().url() ); + } + } + + return uris; +} + +void KFindTreeView::deleteSelectedFiles() +{ + KUrl::List uris = selectedUrls(); + if ( uris.isEmpty() ) { + return; + } + + bool done = KonqOperations::askDeleteConfirmation( uris, KonqOperations::DEL, KonqOperations::FORCE_CONFIRMATION, this ); + if ( done ) + { + KJob * deleteJob = KIO::del( uris ); + deleteJob->uiDelegate()->setAutoErrorHandlingEnabled(true); + } +} + +void KFindTreeView::moveToTrashSelectedFiles() +{ + KUrl::List uris = selectedUrls(); + if ( uris.isEmpty() ) { + return; + } + + bool done = KonqOperations::askDeleteConfirmation( uris, KonqOperations::TRASH, KonqOperations::FORCE_CONFIRMATION, this ); + if ( done ) + { + KJob * trashJob = KIO::trash( uris ); + trashJob->uiDelegate()->setAutoErrorHandlingEnabled(true); + } +} + +void KFindTreeView::reconfigureMouseSettings() +{ + disconnect( SIGNAL(clicked(QModelIndex)) ); + disconnect( SIGNAL(doubleClicked(QModelIndex)) ); + + if ( KGlobalSettings::singleClick() ) + { + connect( this, SIGNAL(clicked(QModelIndex)), this, SLOT(slotExecute(QModelIndex)) ); + } else { + connect( this, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(slotExecute(QModelIndex)) ); + } +} + +void KFindTreeView::updateMouseButtons() +{ + m_mouseButtons = QApplication::mouseButtons(); +} + +//END KFindTreeView + +#include "moc_kfindtreeview.cpp" diff --git a/kfind/kfindtreeview.h b/kfind/kfindtreeview.h new file mode 100644 index 00000000..76f26bbe --- /dev/null +++ b/kfind/kfindtreeview.h @@ -0,0 +1,159 @@ +/******************************************************************* +* kfindtreeview.h +* Copyright 2009 Dario Andres Rodriguez +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +******************************************************************/ + +#ifndef KFINDTREEVIEW__H +#define KFINDTREEVIEW__H + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +class KFindTreeView; +class KActionCollection; +class KfindDlg; + +class KFindItem +{ + public: + explicit KFindItem( const KFileItem & = KFileItem(), const QString & subDir = QString(), const QString & matchingLine = QString() ); + + QVariant data(int column, int role) const; + + KFileItem getFileItem() const { return m_fileItem; } + bool isValid() const { return !m_fileItem.isNull(); } + + private: + KFileItem m_fileItem; + QString m_matchingLine; + QString m_subDir; + QString m_permission; + KIcon m_icon; +}; + +class KFindItemModel: public QAbstractTableModel +{ + public: + KFindItemModel( KFindTreeView* parent); + + void insertFileItems( const QList< QPair > &); + + void removeItem(const KUrl &); + bool isInserted(const KUrl &); + + void clear(); + + Qt::DropActions supportedDropActions() const { return Qt::CopyAction | Qt::MoveAction; } + + Qt::ItemFlags flags(const QModelIndex &) const; + QMimeData * mimeData(const QModelIndexList &) const; + + int columnCount ( const QModelIndex & parent = QModelIndex() ) const { Q_UNUSED(parent); return 6; } + int rowCount ( const QModelIndex & parent = QModelIndex() ) const; + QVariant data ( const QModelIndex & index, int role = Qt::DisplayRole ) const; + QVariant headerData(int section, Qt::Orientation orientation, int role) const; + + KFindItem itemAtIndex( const QModelIndex & index ) const; + + QList getItemList() const { return m_itemList; } + + private: + QList m_itemList; + KFindTreeView* m_view; +}; + +class KFindSortFilterProxyModel: public QSortFilterProxyModel +{ + Q_OBJECT + + public: + KFindSortFilterProxyModel(QObject * parent = 0): + QSortFilterProxyModel(parent){}; + + protected: + bool lessThan(const QModelIndex &left, const QModelIndex &right) const; + +}; + +class KFindTreeView: public QTreeView +{ + Q_OBJECT + public: + KFindTreeView( QWidget * parent, KfindDlg * findDialog); + ~KFindTreeView(); + + void beginSearch(const KUrl& baseUrl); + void endSearch(); + + void insertItems(const QList< QPair > &); + void removeItem(const KUrl & url); + + bool isInserted(const KUrl & url) { return m_model->isInserted( url ); } + + QString reducedDir(const QString& fullDir); + + int itemCount() { return m_model->rowCount(); } + + public Q_SLOTS: + void copySelection(); + void contextMenuRequested( const QPoint & p ); + + private Q_SLOTS: + KUrl::List selectedUrls(); + + void deleteSelectedFiles(); + void moveToTrashSelectedFiles(); + + void slotExecute( const QModelIndex & index ); + void slotExecuteSelected(); + + void openContainingFolder(); + void saveResults(); + + void reconfigureMouseSettings(); + void updateMouseButtons(); + + protected: + void dragMoveEvent( QDragMoveEvent *e ) { e->accept(); } + + Q_SIGNALS: + void resultSelected(bool); + + private: + void resizeToContents(); + + QString m_baseDir; + + KFindItemModel * m_model; + KFindSortFilterProxyModel * m_proxyModel; + KActionCollection * m_actionCollection; + KonqPopupMenu * m_contextMenu; + + Qt::MouseButtons m_mouseButtons; + + KfindDlg * m_kfindDialog; +}; + +#endif diff --git a/kfind/kftabdlg.cpp b/kfind/kftabdlg.cpp new file mode 100644 index 00000000..92cc4053 --- /dev/null +++ b/kfind/kftabdlg.cpp @@ -0,0 +1,964 @@ +/******************************************************************* +* kftabdlg.cpp +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +******************************************************************/ + +#include "kftabdlg.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "kdatecombo.h" +#include "kquery.h" +// Static utility functions +static void save_pattern(KComboBox *, const QString &, const QString &); + +#define SPECIAL_TYPES 7 + +struct LessMimeType_ByComment +{ + bool operator()(const KMimeType::Ptr& lhs, const KMimeType::Ptr& rhs) const + { + return lhs->comment() < rhs->comment(); + } +}; + +KfindTabWidget::KfindTabWidget(QWidget *parent) + : KTabWidget( parent ), regExpDialog(0) +{ + // This validator will be used for all numeric edit fields + //KDigitValidator *digitV = new KDigitValidator(this); + + // ************ Page One ************ + + pages[0] = new QWidget; + pages[0]->setObjectName( QLatin1String( "page1" ) ); + + nameBox = new KComboBox(pages[0]); + nameBox->setEditable( true ); + nameBox->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); // allow smaller than widest entry + QLabel * namedL = new QLabel(i18nc("this is the label for the name textfield","&Named:"), pages[0]); + namedL->setBuddy( nameBox ); + namedL->setObjectName( QLatin1String( "named" ) ); + namedL->setToolTip( i18n("You can use wildcard matching and \";\" for separating multiple names") ); + dirBox = new KUrlComboBox(KUrlComboBox::Directories, pages[0]); + dirBox->setEditable( true ); + dirBox->setCompletionObject(new KUrlCompletion(KUrlCompletion::DirCompletion)); + dirBox->setAutoDeleteCompletionObject(true); + dirBox->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); // allow smaller than widest entry + QLabel * lookinL = new QLabel(i18n("Look &in:"), pages[0]); + lookinL->setBuddy( dirBox ); + lookinL->setObjectName( QLatin1String( "lookin" ) ); + subdirsCb = new QCheckBox(i18n("Include &subfolders"), pages[0]); + caseSensCb = new QCheckBox(i18n("Case s&ensitive search"), pages[0]); + browseB = new QPushButton(i18n("&Browse..."), pages[0]); + useLocateCb = new QCheckBox(i18n("&Use files index"), pages[0]); + hiddenFilesCb = new QCheckBox(i18n("Show &hidden files"), pages[0]); + // Setup + + subdirsCb->setChecked(true); + caseSensCb->setChecked(false); + useLocateCb->setChecked(false); + hiddenFilesCb->setChecked(false); + if(KStandardDirs::findExe("locate").isEmpty()) + useLocateCb->setEnabled(false); + + nameBox->setDuplicatesEnabled(false); + nameBox->setFocus(); + dirBox->setDuplicatesEnabled(false); + + nameBox->setInsertPolicy(QComboBox::InsertAtTop); + dirBox->setInsertPolicy(QComboBox::InsertAtTop); + + const QString nameWhatsThis + = i18n("Enter the filename you are looking for.
    " + "Alternatives may be separated by a semicolon \";\".
    " + "
    " + "The filename may contain the following special characters:" + "
      " + "
    • ? matches any single character
    • " + "
    • * matches zero or more of any characters
    • " + "
    • [...] matches any of the characters between the braces
    • " + "
    " + "
    " + "Example searches:" + "
      " + "
    • *.kwd;*.txt finds all files ending with .kwd or .txt
    • " + "
    • go[dt] finds god and got
    • " + "
    • Hel?o finds all files that start with \"Hel\" and end with \"o\", " + "having one character in between
    • " + "
    • My Document.kwd finds a file of exactly that name
    • " + "
    "); + nameBox->setWhatsThis(nameWhatsThis); + namedL->setWhatsThis(nameWhatsThis); + const QString whatsfileindex + = i18n("This lets you use the files' index created by the slocate " + "package to speed-up the search; remember to update the index from time to time " + "(using updatedb)." + ""); + useLocateCb->setWhatsThis(whatsfileindex); + + // Layout + + QGridLayout *grid = new QGridLayout( pages[0] ); + grid->setMargin( KDialog::marginHint() ); + grid->setSpacing( KDialog::spacingHint() ); + QVBoxLayout *subgrid = new QVBoxLayout(); + grid->addWidget( namedL, 0, 0 ); + grid->addWidget( nameBox, 0, 1, 1, 3 ); + grid->addWidget( lookinL, 1, 0 ); + grid->addWidget( dirBox, 1, 1 ); + grid->addWidget( browseB, 1, 2); + grid->setColumnStretch(1,1); + grid->addLayout( subgrid, 2, 1, 1, 2 ); + + QHBoxLayout * layoutOne = new QHBoxLayout(); + layoutOne->addWidget( subdirsCb ); + layoutOne->addWidget( hiddenFilesCb ); + + QHBoxLayout * layoutTwo = new QHBoxLayout(); + layoutTwo->addWidget( caseSensCb); + layoutTwo->addWidget( useLocateCb ); + + subgrid->addLayout( layoutOne ); + subgrid->addLayout( layoutTwo ); + + subgrid->addStretch(1); + + // Signals + + connect( browseB, SIGNAL(clicked()), + this, SLOT(getDirectory()) ); + + connect( nameBox, SIGNAL(returnPressed()), + this, SIGNAL(startSearch())); + + connect( dirBox, SIGNAL(returnPressed()), + this, SIGNAL(startSearch())); + + // ************ Page Two + + pages[1] = new QWidget; + pages[1]->setObjectName( QLatin1String( "page2" ) ); + + findCreated = new QCheckBox(i18n("Find all files created or &modified:"), pages[1]); + bg = new QButtonGroup(); + rb[0] = new QRadioButton(i18n("&between"), pages[1] ); + rb[1] = new QRadioButton(pages[1]); // text set in updateDateLabels + andL = new QLabel(i18n("and"), pages[1]); + andL->setObjectName( QLatin1String( "and" ) ); + betweenType = new KComboBox( pages[1] ); + betweenType->setObjectName( QLatin1String( "comboBetweenType" ) ); + betweenType->addItems(QVector(5).toList()); // texts set in updateDateLabels + betweenType->setCurrentIndex(1); + updateDateLabels(1, 1); + + QDate dt = KGlobal::locale()->calendar()->addYears(QDate::currentDate(), -1); + + fromDate = new KDateCombo(dt, pages[1] ); + fromDate->setObjectName( QLatin1String( "fromDate" ) ); + toDate = new KDateCombo(pages[1] ); + toDate->setObjectName( QLatin1String( "toDate" ) ); + timeBox = new KIntSpinBox( pages[1] ); + timeBox->setRange( 1, 60 ); + timeBox->setSingleStep( 1 ); + timeBox->setObjectName( QLatin1String( "timeBox" ) ); + + sizeBox =new KComboBox( pages[1] ); + sizeBox->setObjectName( QLatin1String( "sizeBox" ) ); + QLabel * sizeL =new QLabel(i18n("File &size is:"), pages[1]); + sizeL->setBuddy( sizeBox ); + sizeEdit=new KIntSpinBox(pages[1] ); + sizeEdit->setRange( 0, INT_MAX ); + sizeEdit->setSingleStep( 1 ); + sizeEdit->setObjectName( QLatin1String( "sizeEdit" ) ); + sizeEdit->setValue(1); + sizeUnitBox =new KComboBox( pages[1] ); + sizeUnitBox->setObjectName( QLatin1String( "sizeUnitBox" ) ); + + m_usernameBox = new KComboBox( pages[1] ); + m_usernameBox->setEditable( true ); + m_usernameBox->setObjectName( QLatin1String( "m_combo1" )); + QLabel *usernameLabel= new QLabel(i18n("Files owned by &user:"),pages[1]); + usernameLabel->setBuddy( m_usernameBox ); + m_groupBox = new KComboBox( pages[1] ); + m_groupBox->setEditable( true ); + m_groupBox->setObjectName( QLatin1String( "m_combo2" ) ); + QLabel *groupLabel= new QLabel(i18n("Owned by &group:"),pages[1]); + groupLabel->setBuddy( m_groupBox ); + + sizeBox ->addItem( i18nc("file size isn't considered in the search","(none)") ); + sizeBox ->addItem( i18n("At Least") ); + sizeBox ->addItem( i18n("At Most") ); + sizeBox ->addItem( i18n("Equal To") ); + + sizeUnitBox ->addItem( i18np("Byte", "Bytes", 1) ); + sizeUnitBox ->addItem( i18n("KiB") ); + sizeUnitBox ->addItem( i18n("MiB") ); + sizeUnitBox ->addItem( i18n("GiB") ); + sizeUnitBox ->setCurrentIndex(1); + + int tmp = sizeEdit->fontMetrics().width(" 000000000 "); + sizeEdit->setMinimumSize(tmp, sizeEdit->sizeHint().height()); + + m_usernameBox->setDuplicatesEnabled(false); + m_groupBox->setDuplicatesEnabled(false); + m_usernameBox->setInsertPolicy(QComboBox::InsertAtTop); + m_groupBox->setInsertPolicy(QComboBox::InsertAtTop); + + + // Setup + rb[0]->setChecked(true); + bg->addButton( rb[0] ); + bg->addButton( rb[1] ); + + // Layout + + QGridLayout *grid1 = new QGridLayout( pages[1] ); + grid1->setMargin( KDialog::marginHint() ); + grid1->setSpacing( KDialog::spacingHint() ); + + grid1->addWidget(findCreated, 0, 0, 1, 3 ); + grid1->addItem( new QSpacerItem(KDialog::spacingHint(), 0), 0, 0 ); + + grid1->addWidget(rb[0], 1, 1 ); + grid1->addWidget(fromDate, 1, 2 ); + grid1->addWidget(andL, 1, 3, Qt::AlignHCenter ); + grid1->addWidget(toDate, 1, 4 ); + + grid1->addWidget(rb[1], 2, 1 ); + grid1->addWidget(timeBox, 2, 2, 1, 2); + grid1->addWidget(betweenType, 2, 4 ); + + grid1->addWidget(sizeL,3,0,1,2); + grid1->addWidget(sizeBox,3,2); + grid1->addWidget(sizeEdit,3,3); + grid1->addWidget(sizeUnitBox,3,4); + + grid1->addWidget(usernameLabel,4,0,1,2); + grid1->addWidget(m_usernameBox,4,2); + grid1->addWidget(groupLabel,4,3); + grid1->addWidget(m_groupBox,4,4); + + for (int c=1; c<=4; c++) + grid1->setColumnStretch(c,1); + + grid1->setRowStretch(6,1); + + // Connect + connect( findCreated, SIGNAL(toggled(bool)), SLOT(fixLayout()) ); + connect( bg, SIGNAL(buttonClicked(QAbstractButton*)), this, SLOT(fixLayout()) ); + connect( sizeBox, SIGNAL(activated(int)), this, SLOT(slotSizeBoxChanged(int))); + connect( timeBox, SIGNAL(valueChanged(int)), this, SLOT(slotUpdateDateLabelsForNumber(int))); + connect( betweenType, SIGNAL(currentIndexChanged(int)), this, SLOT(slotUpdateDateLabelsForType(int))); + connect( sizeEdit, SIGNAL(valueChanged(int)), this, SLOT(slotUpdateByteComboBox(int))); + + + // ************ Page Three + + pages[2] = new QWidget; + pages[2]->setObjectName( QLatin1String( "page3" ) ); + + typeBox =new KComboBox( pages[2] ); + typeBox->setObjectName( QLatin1String( "typeBox" ) ); + typeBox->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); // allow smaller than widest entry + QLabel * typeL =new QLabel( i18nc("label for the file type combobox","File &type:"), pages[2] ); + typeL->setBuddy( typeBox ); + textEdit=new KLineEdit(pages[2]); + textEdit->setClearButtonShown(true); + textEdit->setObjectName( QLatin1String( "textEdit" ) ); + QLabel * textL =new QLabel(i18n("C&ontaining text:"), pages[2]); + textL->setBuddy( textEdit ); + + connect( textEdit, SIGNAL(returnPressed(QString)), SIGNAL(startSearch())); + + const QString containingtext + = i18n("If specified, only files that contain this text" + " are found. Note that not all file types from the list" + " above are supported. Please refer to the documentation" + " for a list of supported file types." + ""); + textEdit->setToolTip(containingtext); + textL->setWhatsThis(containingtext); + + caseContextCb =new QCheckBox(i18n("Case s&ensitive"), pages[2]); + binaryContextCb =new QCheckBox(i18n("Include &binary files"), pages[2]); + regexpContentCb =new QCheckBox(i18n("Regular e&xpression"), pages[2]); + + const QString binaryTooltip + = i18n("This lets you search in any type of file, " + "even those that usually do not contain text (for example " + "program files and images)."); + binaryContextCb->setToolTip(binaryTooltip); + + QPushButton* editRegExp = 0; + if ( !KServiceTypeTrader::self()->query("KRegExpEditor/KRegExpEditor").isEmpty() ) { + // The editor is available, so lets use it. + editRegExp = new QPushButton(i18n("&Edit..."), pages[2]); + editRegExp->setObjectName( QLatin1String( "editRegExp" ) ); + } + + metainfokeyEdit=new KLineEdit(pages[2]); + metainfoEdit=new KLineEdit(pages[2]); + QLabel * textMetaInfo = new QLabel(i18nc("as in search for", "fo&r:"), pages[2]); + textMetaInfo->setBuddy( metainfoEdit ); + QLabel * textMetaKey = new QLabel(i18n("Search &metainfo sections:"), pages[2]); + textMetaKey->setBuddy( metainfokeyEdit ); + + // Setup + typeBox->addItem(i18n("All Files & Folders")); + typeBox->addItem(i18n("Files")); + typeBox->addItem(i18n("Folders")); + typeBox->addItem(i18n("Symbolic Links")); + typeBox->addItem(i18n("Special Files (Sockets, Device Files, ...)")); + typeBox->addItem(i18n("Executable Files")); + typeBox->addItem(i18n("SUID Executable Files")); + typeBox->addItem(i18n("All Images")); + typeBox->addItem(i18n("All Video")); + typeBox->addItem(i18n("All Sounds")); + + initMimeTypes(); + initSpecialMimeTypes(); + + for ( KMimeType::List::ConstIterator it = m_types.constBegin(); + it != m_types.constEnd(); ++it ) + { + KMimeType::Ptr typ = *it; +// TODO: needs to move to thread (increases startup time to bizzare amount) +// and replaced with a better concept 16x16 icons don't cut the cheese +// typeBox->addItem(KIconLoader::global()->loadMimeTypeIcon( typ->iconName(), KIconLoader::Small ), typ->comment()); + typeBox->addItem(typ->comment()); + } + + if ( editRegExp ) { + // The editor was available, so lets use it. + connect( regexpContentCb, SIGNAL(toggled(bool)), editRegExp, SLOT(setEnabled(bool)) ); + editRegExp->setEnabled(false); + connect( editRegExp, SIGNAL(clicked()), this, SLOT(slotEditRegExp()) ); + } + else + regexpContentCb->hide(); + + // Layout + tmp = sizeEdit->fontMetrics().width(" 00000 "); + sizeEdit->setMinimumSize(tmp, sizeEdit->sizeHint().height()); + + QGridLayout *grid2 = new QGridLayout( pages[2] ); + grid2->setMargin( KDialog::marginHint() ); + grid2->setSpacing( KDialog::spacingHint() ); + grid2->addWidget( typeL, 0, 0 ); + grid2->addWidget( textL, 1, 0 ); + grid2->addWidget( typeBox, 0, 1, 1, 3 ); + grid2->addWidget( textEdit, 1, 1, 1, 3 ); + grid2->addWidget( regexpContentCb, 2, 2); + grid2->addWidget( caseContextCb, 2, 1 ); + grid2->addWidget( binaryContextCb, 3, 1); + + grid2->addWidget( textMetaKey, 4, 0 ); + grid2->addWidget( metainfokeyEdit, 4, 1 ); + grid2->addWidget( textMetaInfo, 4, 2, Qt::AlignHCenter ); + grid2->addWidget( metainfoEdit, 4, 3 ); + + metainfokeyEdit->setText("*"); + + if ( editRegExp ) { + // The editor was available, so lets use it. + grid2->addWidget( editRegExp, 2, 3 ); + } + + addTab( pages[0], i18n("Name/&Location") ); + addTab( pages[2], i18nc("tab name: search by contents","C&ontents") ); + addTab( pages[1], i18n("&Properties") ); + + + // Setup + const QString whatsmetainfo + = i18n("Search within files' specific comments/metainfo
    " + "These are some examples:
    " + "
      " + "
    • Audio files (mp3...) Search in id3 tag for a title, an album
    • " + "
    • Images (png...) Search images with a special resolution, comment...
    • " + "
    " + "
    "); + const QString whatsmetainfokey + = i18n("If specified, search only in this field
    " + "
      " + "
    • Audio files (mp3...) This can be Title, Album...
    • " + "
    • Images (png...) Search only in Resolution, Bitdepth...
    • " + "
    " + "
    "); + textMetaInfo->setWhatsThis(whatsmetainfo); + metainfoEdit->setToolTip(whatsmetainfo); + textMetaKey->setWhatsThis(whatsmetainfokey); + metainfokeyEdit->setToolTip(whatsmetainfokey); + + + fixLayout(); + loadHistory(); +} + +KfindTabWidget::~KfindTabWidget() +{ + delete pages[0]; + delete pages[1]; + delete pages[2]; + delete bg; +} + +void KfindTabWidget::setURL( const KUrl & url ) +{ + KConfigGroup conf(KGlobal::config(), "History"); + m_url = url; + QStringList sl = conf.readPathEntry("Directories", QStringList()); + dirBox->clear(); // make sure there is no old Stuff in there + + if(!sl.isEmpty()) { + dirBox->addItems(sl); + // If the _searchPath already exists in the list we do not + // want to add it again + int indx = sl.indexOf(m_url.prettyUrl()); + if(indx == -1) { + dirBox->insertItem(0, m_url.prettyUrl()); // make it the first one + dirBox->setCurrentIndex(0); + } + else + dirBox->setCurrentIndex(indx); + } + else { + QDir m_dir("/lib"); + dirBox ->insertItem( 0, m_url.prettyUrl() ); + dirBox ->addItem( "file:" + QDir::homePath() ); + dirBox ->addItem( "file:/" ); + dirBox ->addItem( "file:/usr" ); + if (m_dir.exists()) + dirBox ->addItem( "file:/lib" ); + dirBox ->addItem( "file:/home" ); + dirBox ->addItem( "file:/etc" ); + dirBox ->addItem( "file:/var" ); + dirBox ->addItem( "file:/mnt" ); + dirBox->setCurrentIndex(0); + } +} + +void KfindTabWidget::initMimeTypes() +{ + KMimeType::List sortedList; + foreach ( const KMimeType::Ptr &type, KMimeType::allMimeTypes() ) + { + if ( (!type->comment().isEmpty()) + && (!type->name().startsWith( QString("kdedevice/") )) + && (!type->name().startsWith( QString("all/") )) ) + sortedList.append(type); + } + qSort( sortedList.begin(), sortedList.end(), LessMimeType_ByComment() ); + m_types += sortedList; +} + +void KfindTabWidget::initSpecialMimeTypes() +{ + const KMimeType::List tmp = KMimeType::allMimeTypes(); + + for ( KMimeType::List::ConstIterator it = tmp.constBegin(); it != tmp.constEnd(); ++it ) + { + const KMimeType* type = (*it).data(); + + if(!type->comment().isEmpty()) { + if(type->name().startsWith( QString("image/") )) + m_ImageTypes.append(type->name()); + else if(type->name().startsWith( QString("video/") )) + m_VideoTypes.append(type->name()); + else if(type->name().startsWith( QString("audio/") )) + m_AudioTypes.append(type->name()); + } + } +} + +void KfindTabWidget::saveHistory() +{ + save_pattern(nameBox, "History", "Patterns"); + save_pattern(dirBox, "History", "Directories"); +} + +void KfindTabWidget::loadHistory() +{ + // Load pattern history + KConfigGroup conf(KGlobal::config(), "History"); + QStringList sl = conf.readEntry("Patterns", QStringList()); + if(!sl.isEmpty()) + nameBox->addItems(sl); + else + nameBox->addItem("*"); + + sl = conf.readPathEntry("Directories", QStringList()); + if(!sl.isEmpty()) { + dirBox->addItems(sl); + // If the _searchPath already exists in the list we do not + // want to add it again + int indx = sl.indexOf(m_url.prettyUrl()); + if(indx == -1) { + dirBox->insertItem(0, m_url.prettyUrl()); // make it the first one + dirBox->setCurrentIndex(0); + } + else + dirBox->setCurrentIndex(indx); + } + else { + QDir m_dir("/lib"); + dirBox ->insertItem( 0, m_url.prettyUrl() ); + dirBox ->addItem( "file:" + QDir::homePath() ); + dirBox ->addItem( "file:/" ); + dirBox ->addItem( "file:/usr" ); + if (m_dir.exists()) + dirBox ->addItem( "file:/lib" ); + dirBox ->addItem( "file:/home" ); + dirBox ->addItem( "file:/etc" ); + dirBox ->addItem( "file:/var" ); + dirBox ->addItem( "file:/mnt" ); + dirBox ->setCurrentIndex(0); + } +} + +void KfindTabWidget::slotEditRegExp() +{ + if ( ! regExpDialog ) + regExpDialog = KServiceTypeTrader::createInstanceFromQuery( "KRegExpEditor/KRegExpEditor", QString(), this ); + + KRegExpEditorInterface *iface = qobject_cast( regExpDialog ); + if ( !iface ) + return; + + iface->setRegExp( textEdit->text() ); + bool ok = regExpDialog->exec(); + if ( ok ) + textEdit->setText( iface->regExp() ); +} + +void KfindTabWidget::setFocus() +{ + nameBox->setFocus(); + nameBox->lineEdit()->selectAll(); +} + +void KfindTabWidget::slotSizeBoxChanged(int index) +{ + sizeEdit->setEnabled((bool)(index != 0)); + sizeUnitBox->setEnabled((bool)(index != 0)); +} + +void KfindTabWidget::setDefaults() +{ + QDate dt = KGlobal::locale()->calendar()->addYears(QDate::currentDate(), -1); + + fromDate ->setDate(dt); + toDate ->setDate(QDate::currentDate()); + + timeBox->setValue(1); + betweenType->setCurrentIndex(1); + + typeBox ->setCurrentIndex(0); + sizeBox ->setCurrentIndex(0); + sizeUnitBox ->setCurrentIndex(1); + sizeEdit->setValue(1); +} + +/* + Checks if dates are correct and popups a error box + if they are not. +*/ +bool KfindTabWidget::isDateValid() +{ + // All files + if ( !findCreated->isChecked() ) return true; + + if (rb[1]->isChecked()) + { + if (timeBox->value() > 0 ) return true; + + KMessageBox::sorry(this, i18n("Unable to search within a period which is less than a minute.")); + return false; + } + + // If we can not parse either of the dates or + // "from" date is bigger than "to" date return false. + QDate hi1, hi2; + + QString str; + if ( ! fromDate->getDate(&hi1).isValid() || + ! toDate->getDate(&hi2).isValid() ) + str = i18n("The date is not valid."); + else if ( hi1 > hi2 ) + str = i18n("Invalid date range."); + else if ( QDate::currentDate() < hi1 ) + str = i18n("Unable to search dates in the future."); + + if (!str.isEmpty()) { + KMessageBox::sorry(0, str); + return false; + } + return true; +} + +void KfindTabWidget::setQuery(KQuery *query) +{ + KIO::filesize_t size; + KIO::filesize_t sizeunit; + bool itemAlreadyContained(false); + // only start if we have valid dates + if (!isDateValid()) return; + + query->setPath(KUrl(dirBox->currentText().trimmed())); + + for (int idx=0; idxcount(); idx++) + if (dirBox->itemText(idx)==dirBox->currentText()) + itemAlreadyContained=true; + + if (!itemAlreadyContained) + dirBox->addItem(dirBox->currentText().trimmed(),0); + + QString regex = nameBox->currentText().isEmpty() ? "*" : nameBox->currentText(); + query->setRegExp(regex, caseSensCb->isChecked()); + itemAlreadyContained=false; + for (int idx=0; idxcount(); idx++) + if (nameBox->itemText(idx)==nameBox->currentText()) + itemAlreadyContained=true; + + if (!itemAlreadyContained) + nameBox->addItem(nameBox->currentText(),0); + + query->setRecursive(subdirsCb->isChecked()); + + switch (sizeUnitBox->currentIndex()) + { + case 0: + sizeunit = 1; //one byte + break; + case 2: + sizeunit = 1048576; //1Mi + break; + case 3: + sizeunit = 1073741824; //1Gi + break; + case 1: // fall to default case + default: + sizeunit=1024; //1Ki + break; + } + size = sizeEdit->value() * sizeunit; + +// TODO: troeder: do we need this check since it is very unlikely +// to exceed ULLONG_MAX with INT_MAX * 1024^3. +// Or is there an arch where this can happen? +#if 0 + if (size < 0) // overflow + { + if (KMessageBox::warningYesNo(this, i18n("Size is too big. Set maximum size value?"), i18n("Error"),i18n("Set"),i18n("Do Not Set")) + == KMessageBox::Yes) + { + sizeEdit->setValue(INT_MAX); + sizeUnitBox->setCurrentIndex(0); + size = INT_MAX; + } + else + return; + } +#endif + + // set range mode and size value + query->setSizeRange(sizeBox->currentIndex(),size,0); + + // dates + QDateTime epoch; + epoch.setTime_t(0); + + // Add date predicate + if (findCreated->isChecked()) { // Modified + if (rb[0]->isChecked()) { // Between dates + QDate q1, q2; + fromDate->getDate(&q1); + toDate->getDate(&q2); + + // do not generate negative numbers .. find doesn't handle that + time_t time1 = epoch.secsTo(QDateTime(q1)); + time_t time2 = epoch.secsTo(QDateTime(q2.addDays(1))) - 1; // Include the last day + + query->setTimeRange(time1, time2); + } + else + { + time_t cur = time(NULL); + time_t minutes = cur; + + switch (betweenType->currentIndex()) + { + case 0: // minutes + minutes = timeBox->value(); + break; + case 1: // hours + minutes = 60 * timeBox->value(); + break; + case 2: // days + minutes = 60 * 24 * timeBox->value(); + break; + case 3: // months + minutes = 60 * 24 * (time_t)(timeBox->value() * 30.41667); + break; + case 4: // years + minutes = 12 * 60 * 24 * (time_t)(timeBox->value() * 30.41667); + break; + } + + query->setTimeRange(cur - minutes * 60, 0); + } + } + else + query->setTimeRange(0, 0); + + query->setUsername( m_usernameBox->currentText() ); + query->setGroupname( m_groupBox->currentText() ); + + query->setFileType(typeBox->currentIndex()); + + int id = typeBox->currentIndex()-10; + + if ((id >= -3) && (id < (int) m_types.count())) + { + switch(id) + { + case -3: + query->setMimeType( m_ImageTypes ); + break; + case -2: + query->setMimeType( m_VideoTypes ); + break; + case -1: + query->setMimeType( m_AudioTypes ); + break; + default: + query->setMimeType( QStringList() += m_types[id]->name() ); + } + } + else + { + query->setMimeType( QStringList() ); + } + + //Metainfo + query->setMetaInfo(metainfoEdit->text(), metainfokeyEdit->text()); + + //Use locate to speed-up search ? + query->setUseFileIndex(useLocateCb->isChecked()); + + query->setShowHiddenFiles(hiddenFilesCb->isChecked()); + + query->setContext(textEdit->text(), caseContextCb->isChecked(), + binaryContextCb->isChecked(), regexpContentCb->isChecked()); +} + +QString KfindTabWidget::date2String(const QDate & date) { + return(KGlobal::locale()->formatDate(date, KLocale::ShortDate)); +} + +QDate &KfindTabWidget::string2Date(const QString & str, QDate *qd) { + return *qd = KGlobal::locale()->readDate(str); +} + +void KfindTabWidget::getDirectory() +{ + QString result = + KFileDialog::getExistingDirectory( dirBox->currentText().trimmed(), + this ); + + if (!result.isEmpty()) + { + for (int i = 0; i < dirBox->count(); i++) + if (result == dirBox->itemText(i)) { + dirBox->setCurrentIndex(i); + return; + } + dirBox->insertItem(0, result); + dirBox->setCurrentIndex(0); + } +} + +void KfindTabWidget::beginSearch() +{ +/// dirlister->openUrl(KUrl(dirBox->currentText().trimmed())); + + saveHistory(); + setEnabled( false ); +} + +void KfindTabWidget::endSearch() +{ + setEnabled( true ); +} + +/* + Disables/enables all edit fields depending on their + respective check buttons. +*/ +void KfindTabWidget::fixLayout() +{ + int i; + // If "All files" is checked - disable all edits + // and second radio group on page two + + if(! findCreated->isChecked()) { + fromDate->setEnabled(false); + toDate->setEnabled(false); + andL->setEnabled(false); + timeBox->setEnabled(false); + for(i=0; i<2; ++i) + rb[i]->setEnabled(false); + betweenType->setEnabled(false); + } + else { + for(i=0; i<2; ++i) + rb[i]->setEnabled(true); + + fromDate->setEnabled(rb[0]->isChecked()); + toDate->setEnabled(rb[0]->isChecked()); + andL->setEnabled(rb[0]->isEnabled()); + timeBox->setEnabled(rb[1]->isChecked()); + betweenType->setEnabled(rb[1]->isChecked()); + } + + // Size box on page three + sizeEdit->setEnabled(sizeBox->currentIndex() != 0); + sizeUnitBox->setEnabled(sizeBox->currentIndex() != 0); +} + +bool KfindTabWidget::isSearchRecursive() +{ + return subdirsCb->isChecked(); +} + +void KfindTabWidget::slotUpdateDateLabelsForNumber(int value) +{ + updateDateLabels(betweenType->currentIndex(), value); +} + +void KfindTabWidget::slotUpdateDateLabelsForType(int index) +{ + updateDateLabels(index, timeBox->value()); +} + +void KfindTabWidget::updateDateLabels(int type, int value) +{ + QString typeKey(type == 0 ? 'i' : type == 1 ? 'h' : type == 2 ? 'd' : type == 3 ? 'm' : 'y'); + rb[1]->setText(ki18ncp("during the previous minute(s)/hour(s)/...; " + "dynamic context 'type': 'i' minutes, 'h' hours, 'd' days, 'm' months, 'y' years", + "&during the previous", "&during the previous").subs(value).inContext("type", typeKey).toString()); + betweenType->setItemText(0, i18ncp("use date ranges to search files by modified time", "minute", "minutes", value)); + betweenType->setItemText(1, i18ncp("use date ranges to search files by modified time", "hour", "hours", value)); + betweenType->setItemText(2, i18ncp("use date ranges to search files by modified time", "day", "days", value)); + betweenType->setItemText(3, i18ncp("use date ranges to search files by modified time", "month", "months", value)); + betweenType->setItemText(4, i18ncp("use date ranges to search files by modified time", "year", "years", value)); +} + +void KfindTabWidget::slotUpdateByteComboBox(int value) +{ + sizeUnitBox->setItemText(0, i18np("Byte", "Bytes", value) ); +} + + +/** + Digit validator. Allows only digits to be typed. +**/ +KDigitValidator::KDigitValidator( QWidget * parent ) + : QValidator( parent ) +{ + r = new QRegExp("^[0-9]*$"); +} + +KDigitValidator::~KDigitValidator() +{ + delete r; +} + +QValidator::State KDigitValidator::validate( QString & input, int & ) const +{ + if (r->indexIn(input) < 0) { + // Beep on user if he enters non-digit + QApplication::beep(); + return QValidator::Invalid; + } + else + return QValidator::Acceptable; +} + +//******************************************************* +// Static utility functions +//******************************************************* +static void save_pattern(KComboBox *obj, + const QString & group, const QString & entry) +{ + // QComboBox allows insertion of items more than specified by + // maxCount() (QT bug?). This API call will truncate list if needed. + obj->setMaxCount(15); + + // make sure the current item is saved first so it will be the + // default when started next time + QStringList sl; + QString cur = obj->itemText(obj->currentIndex()); + sl.append(cur); + for (int i = 0; i < obj->count(); i++) { + if( cur != obj->itemText(i) ) { + sl.append(obj->itemText(i)); + } + } + + KConfigGroup conf(KGlobal::config(), group); + conf.writePathEntry(entry, sl); +} + +QSize KfindTabWidget::sizeHint() const +{ + // #44662: avoid a huge default size when the comboboxes have very large items + // Like in minicli, we changed the combobox size policy so that they can resize down, + // and then we simply provide a reasonable size hint for the whole window, depending + // on the screen width. + QSize sz = KTabWidget::sizeHint(); + KfindTabWidget* me = const_cast( this ); + const int screenWidth = qApp->desktop()->screenGeometry(me).width(); + if ( sz.width() > screenWidth / 2 ) + sz.setWidth( screenWidth / 2 ); + return sz; +} + +#include "moc_kftabdlg.cpp" diff --git a/kfind/kftabdlg.h b/kfind/kftabdlg.h new file mode 100644 index 00000000..59b887a0 --- /dev/null +++ b/kfind/kftabdlg.h @@ -0,0 +1,156 @@ +/******************************************************************* +* kftabdlg.h +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +******************************************************************/ + +#ifndef KFTABDLG_H +#define KFTABDLG_H + +#include // for KDigitValidator + +#include +#include +#include + +class KUrlComboBox; +#include +#include +#include +#include +class KLineEdit; +#include +#include +#include +class KDialog; +class KComboBox; +class KIntSpinBox; +#include + +class KfDirDialog; +class KDateCombo; + +class KfindTabWidget: public KTabWidget +{ + Q_OBJECT + +public: + KfindTabWidget(QWidget * parent = 0); + virtual ~KfindTabWidget(); + void initMimeTypes(); + void initSpecialMimeTypes(); + void setQuery(class KQuery * query); + void setDefaults(); + + void beginSearch(); + void endSearch(); + void loadHistory(); + void saveHistory(); + bool isSearchRecursive(); + + void setURL( const KUrl & url ); + + virtual QSize sizeHint() const; + +public Q_SLOTS: + void setFocus(); + void slotUpdateDateLabelsForNumber(int value); + void slotUpdateDateLabelsForType(int index); + void slotUpdateByteComboBox(int value); + +private Q_SLOTS: + void getDirectory(); + void fixLayout(); + void slotSizeBoxChanged(int); + void slotEditRegExp(); + +Q_SIGNALS: + void startSearch(); + +protected: +public: + KComboBox *nameBox; + KUrlComboBox *dirBox; + // for first page + QCheckBox *subdirsCb; + QCheckBox *useLocateCb; + QCheckBox *hiddenFilesCb; + // for third page + KComboBox *typeBox; + KLineEdit * textEdit; + QCheckBox *caseSensCb; + KComboBox *m_usernameBox; + KComboBox *m_groupBox; + //for fourth page + KLineEdit *metainfoEdit; + KLineEdit *metainfokeyEdit; + +private: + bool isDateValid(); + + QString date2String(const QDate &); + QDate &string2Date(const QString &, QDate * ); + + void updateDateLabels(int type, int value); + + QWidget *pages[3]; + + //1st page + QPushButton *browseB; + + KfDirDialog *dirselector; + + //2nd page + QCheckBox *findCreated; + KComboBox *betweenType; + QLabel *andL; + QButtonGroup *bg; + QRadioButton *rb[2]; + KDateCombo * fromDate; + KDateCombo * toDate; + KIntSpinBox *timeBox; + + //3rd page + KComboBox *sizeBox; + KComboBox *sizeUnitBox; + KIntSpinBox *sizeEdit; + QCheckBox *caseContextCb; + QCheckBox *binaryContextCb; + QCheckBox *regexpContentCb; + KDialog *regExpDialog; + + KUrl m_url; + + KMimeType::List m_types; + QStringList m_ImageTypes; + QStringList m_VideoTypes; + QStringList m_AudioTypes; +}; + +class KDigitValidator : public QValidator +{ + Q_OBJECT + +public: + KDigitValidator(QWidget * parent); + ~KDigitValidator(); + + QValidator::State validate(QString & input, int &) const; + + private: + QRegExp *r; +}; + +#endif diff --git a/kfind/kquery.cpp b/kfind/kquery.cpp new file mode 100644 index 00000000..7e9d6866 --- /dev/null +++ b/kfind/kquery.cpp @@ -0,0 +1,574 @@ +/******************************************************************* +* kquery.cpp +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +******************************************************************/ + +#include "kquery.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +KQuery::KQuery(QObject *parent) + : QObject(parent), + m_filetype(0), m_sizemode(0), m_sizeboundary1(0), + m_sizeboundary2(0), m_timeFrom(0), m_timeTo(0), + m_recursive(false),m_casesensitive(false), + m_search_binary(false), m_regexpForContent(false), + m_useLocate(false), m_showHiddenFiles(false), + job(0), m_insideCheckEntries(false), m_result(0) +{ + processLocate = new QProcess(this); + connect(processLocate,SIGNAL(readyReadStandardOutput()),this,SLOT(slotreadyReadStandardOutput())); + connect(processLocate,SIGNAL(readyReadStandardError()),this,SLOT(slotreadyReadStandardError())); + connect(processLocate,SIGNAL(finished(int,QProcess::ExitStatus)),this,SLOT(slotendProcessLocate(int,QProcess::ExitStatus))); + + // Files with these mime types can be ignored, even if + // findFormatByFileContent() in some cases may claim that + // these are text files: + ignore_mimetypes.append("application/pdf"); + ignore_mimetypes.append("application/postscript"); + + // PLEASE update the documentation when you add another + // file type here: + ooo_mimetypes.append("application/vnd.sun.xml.writer"); + ooo_mimetypes.append("application/vnd.sun.xml.calc"); + ooo_mimetypes.append("application/vnd.sun.xml.impress"); + // OASIS mimetypes, used by OOo-2.x and KOffice >= 1.4 + //ooo_mimetypes.append("application/vnd.oasis.opendocument.chart"); + //ooo_mimetypes.append("application/vnd.oasis.opendocument.graphics"); + //ooo_mimetypes.append("application/vnd.oasis.opendocument.graphics-template"); + //ooo_mimetypes.append("application/vnd.oasis.opendocument.formula"); + //ooo_mimetypes.append("application/vnd.oasis.opendocument.image"); + ooo_mimetypes.append("application/vnd.oasis.opendocument.presentation-template"); + ooo_mimetypes.append("application/vnd.oasis.opendocument.presentation"); + ooo_mimetypes.append("application/vnd.oasis.opendocument.spreadsheet-template"); + ooo_mimetypes.append("application/vnd.oasis.opendocument.spreadsheet"); + ooo_mimetypes.append("application/vnd.oasis.opendocument.text-template"); + ooo_mimetypes.append("application/vnd.oasis.opendocument.text"); + // KOffice-1.3 mimetypes + koffice_mimetypes.append("application/x-kword"); + koffice_mimetypes.append("application/x-kspread"); + koffice_mimetypes.append("application/x-kpresenter"); +} + +KQuery::~KQuery() +{ + while (!m_regexps.isEmpty()) + delete m_regexps.takeFirst(); + m_fileItems.clear(); + if( processLocate->state() == QProcess::Running) + { + disconnect( processLocate ); + processLocate->kill(); + processLocate->waitForFinished( 5000 ); + delete processLocate; + } +} + +void KQuery::kill() +{ + if (job) + job->kill(KJob::EmitResult); + if (processLocate->state() == QProcess::Running) + processLocate->kill(); + m_fileItems.clear(); +} + +void KQuery::start() +{ + m_fileItems.clear(); + if( m_useLocate ) //Use "locate" instead of the internal search method + { + bufferLocate.clear(); + m_url.cleanPath(); + + processLocate->setProcessChannelMode(QProcess::SeparateChannels); + processLocate->start("locate", QStringList() << m_url.path( KUrl::AddTrailingSlash )); + } + else //Use KIO + { + if (m_recursive) + job = KIO::listRecursive( m_url, KIO::HideProgressInfo ); + else + job = KIO::listDir( m_url, KIO::HideProgressInfo ); + + connect(job, SIGNAL(entries(KIO::Job*,KIO::UDSEntryList)), + SLOT(slotListEntries(KIO::Job*,KIO::UDSEntryList))); + connect(job, SIGNAL(result(KJob*)), SLOT(slotResult(KJob*))); + connect(job, SIGNAL(canceled(KJob*)), SLOT(slotCanceled(KJob*))); + } +} + +void KQuery::slotResult( KJob * _job ) +{ + if (job != _job) return; + job = 0; + + m_result=_job->error(); + checkEntries(); +} + +void KQuery::slotCanceled( KJob * _job ) +{ + if (job != _job) return; + job = 0; + + m_fileItems.clear(); + + m_result=KIO::ERR_USER_CANCELED; + checkEntries(); +} + +void KQuery::slotListEntries(KIO::Job*, const KIO::UDSEntryList& list) +{ + const KIO::UDSEntryList::ConstIterator end = list.constEnd(); + + for (KIO::UDSEntryList::ConstIterator it = list.constBegin(); it != end; ++it) + m_fileItems.enqueue(KFileItem(*it, m_url, true, true)); + + checkEntries(); +} + +void KQuery::checkEntries() +{ + if (m_insideCheckEntries) return; + + m_insideCheckEntries=true; + + metaKeyRx = QRegExp(m_metainfokey); + metaKeyRx.setPatternSyntax( QRegExp::Wildcard ); + + m_foundFilesList.clear(); + + int processingCount = 0; + while( !m_fileItems.isEmpty() ) + { + processQuery( m_fileItems.dequeue() ); + processingCount++; + + /* This is a workaround. As the kapp->processEvents() call inside processQuery + * will bring more KIO entries, m_fileItems will increase even inside this loop + * and that will lead to a big loop, it will take time to report found items to the GUI + * so we are going to force emit results every 100 files processed */ + if( processingCount==100 ) + { + processingCount = 0; + if( m_foundFilesList.size() > 0 ) + { + emit foundFileList( m_foundFilesList ); + m_foundFilesList.clear(); + } + } + } + + if( m_foundFilesList.size() > 0 ) + emit foundFileList( m_foundFilesList ); + + if (job==0) + emit result(m_result); + + m_insideCheckEntries=false; +} + +/* List of files found using slocate */ +void KQuery::slotListEntries( QStringList list ) +{ + metaKeyRx = QRegExp(m_metainfokey); + metaKeyRx.setPatternSyntax( QRegExp::Wildcard ); + + QStringList::const_iterator it = list.constBegin(); + QStringList::const_iterator end = list.constEnd(); + + m_foundFilesList.clear(); + for (; it != end; ++it) + processQuery( KFileItem( KFileItem::Unknown, KFileItem::Unknown, KUrl(*it)) ); + + if( m_foundFilesList.size() > 0 ) + emit foundFileList( m_foundFilesList ); + +} + +/* Check if file meets the find's requirements*/ +void KQuery::processQuery( const KFileItem &file) +{ + if ( file.name() == "." || file.name() == ".." ) + return; + + if ( !m_showHiddenFiles && file.isHidden() ) + return; + + bool matched=false; + + QListIterator nextItem( m_regexps ); + while ( nextItem.hasNext() ) + { + QRegExp *reg = nextItem.next(); + matched = matched || ( reg == 0L ) || ( reg->exactMatch( file.url().fileName( KUrl::IgnoreTrailingSlash ) ) ) ; + } + if (!matched) + return; + + // make sure the files are in the correct range + switch( m_sizemode ) + { + case 1: // "at least" + if ( file.size() < m_sizeboundary1 ) return; + break; + case 2: // "at most" + if ( file.size() > m_sizeboundary1 ) return; + break; + case 3: // "equal" + if ( file.size() != m_sizeboundary1 ) return; + break; + case 4: // "between" + if ( (file.size() < m_sizeboundary1) || + (file.size() > m_sizeboundary2) ) return; + break; + case 0: // "none" -> Fall to default + default: + break; + } + + // make sure it's in the correct date range + // what about 0 times? + if ( m_timeFrom && ((uint) m_timeFrom) > file.time(KFileItem::ModificationTime).toTime_t() ) + return; + if ( m_timeTo && ((uint) m_timeTo) < file.time(KFileItem::ModificationTime).toTime_t() ) + return; + + // username / group match + if ( (!m_username.isEmpty()) && (m_username != file.user()) ) + return; + if ( (!m_groupname.isEmpty()) && (m_groupname != file.group()) ) + return; + + // file type + switch (m_filetype) + { + case 0: + break; + case 1: // plain file + if ( !S_ISREG( file.mode() ) ) + return; + break; + case 2: + if ( !file.isDir() ) + return; + break; + case 3: + if ( !file.isLink() ) + return; + break; + case 4: + if ( !S_ISCHR ( file.mode() ) && !S_ISBLK ( file.mode() ) && + !S_ISFIFO( file.mode() ) && !S_ISSOCK( file.mode() ) ) + return; + break; + case 5: // binary + if ( (file.permissions() & 0111) != 0111 || file.isDir() ) + return; + break; + case 6: // suid + if ( (file.permissions() & 04000) != 04000 ) // fixme + return; + break; + default: + if (!m_mimetype.isEmpty() && !m_mimetype.contains(file.mimetype())) + return; + } + + // match data in metainfo... + if ((!m_metainfo.isEmpty()) && (!m_metainfokey.isEmpty())) + { + //Avoid sequential files (fifo,char devices) + if (!file.isRegularFile()) + return; + + bool foundmeta=false; + QString filename = file.url().path(); + + if(filename.startsWith( QString("/dev/") )) + return; + + KFileMetaInfo metadatas(filename); + QStringList metakeys; + QString strmetakeycontent; + + metakeys = metadatas.keys(); + for (QStringList::const_iterator it = metakeys.constBegin(); it != metakeys.constEnd(); ++it ) + { + if (!metaKeyRx.exactMatch(*it)) + continue; + strmetakeycontent=metadatas.item(*it).value(); + if(strmetakeycontent.indexOf(m_metainfo)!=-1) + { + foundmeta=true; + break; + } + } + if (!foundmeta) + return; + } + + // match contents... + QString matchingLine; + if (!m_context.isEmpty()) + { + //Avoid sequential files (fifo,char devices) + if (!file.isRegularFile()) + return; + + if( !m_search_binary && ignore_mimetypes.indexOf(file.mimetype()) != -1 ) { + kDebug() << "ignoring, mime type is in exclusion list: " << file.url(); + return; + } + + bool found = false; + bool isZippedOfficeDocument=false; + int matchingLineNumber=0; + + // FIXME: doesn't work with non local files + + QString filename; + QTextStream* stream=0; + QFile qf; + QRegExp xmlTags; + QByteArray zippedXmlFileContent; + + // KWord's and OpenOffice.org's files are zipped... + if( ooo_mimetypes.indexOf(file.mimetype()) != -1 || + koffice_mimetypes.indexOf(file.mimetype()) != -1 ) + { + KZip zipfile(file.url().path()); + KZipFileEntry *zipfileEntry; + + if(zipfile.open(QIODevice::ReadOnly)) + { + const KArchiveDirectory *zipfileContent = zipfile.directory(); + + if( koffice_mimetypes.indexOf(file.mimetype()) != -1 ) + zipfileEntry = (KZipFileEntry*)zipfileContent->entry("maindoc.xml"); + else + zipfileEntry = (KZipFileEntry*)zipfileContent->entry("content.xml"); //for OpenOffice.org + + if(!zipfileEntry) { + kWarning() << "Expected XML file not found in ZIP archive " << file.url() ; + return; + } + + zippedXmlFileContent = zipfileEntry->data(); + xmlTags.setPattern("<.*>"); + xmlTags.setMinimal(true); + stream = new QTextStream(zippedXmlFileContent, QIODevice::ReadOnly); + stream->setCodec("UTF-8"); + isZippedOfficeDocument = true; + } else { + kWarning() << "Cannot open supposed ZIP file " << file.url() ; + } + + } else if( !m_search_binary && !file.mimetype().startsWith( QString("text/") ) && + file.url().isLocalFile() && !file.url().path().startsWith( QString("/dev") ) ) { + if ( KMimeType::isBinaryData(file.url().path()) ) { + kDebug() << "ignoring, not a text file: " << file.url(); + return; + } + } + + if(!isZippedOfficeDocument) //any other file or non-compressed KWord + { + filename = file.url().path(); + if(filename.startsWith(QString("/dev/"))) + return; + qf.setFileName(filename); + qf.open(QIODevice::ReadOnly); + stream=new QTextStream(&qf); + stream->setCodec(QTextCodec::codecForLocale()); + } + + while ( ! stream->atEnd() ) + { + QString str = stream->readLine(); + matchingLineNumber++; + + //If the stream ended (readLine().isNull() is true) the file was read completely + //Do *not* use isEmpty() because that will exit if there is an empty line in the file + if (str.isNull()) break; + if(isZippedOfficeDocument) + str.remove(xmlTags); + + if (m_regexpForContent) + { + if (m_regexp.indexIn(str)>=0) + { + matchingLine=QString::number(matchingLineNumber)+": "+str; + found = true; + break; + } + } + else + { + if (str.indexOf(m_context, 0, m_casesensitive?Qt::CaseSensitive:Qt::CaseInsensitive) != -1) + { + matchingLine=QString::number(matchingLineNumber)+": "+str; + found = true; + break; + } + } + kapp->processEvents(); + } + + delete stream; + + if (!found) + return; + } + + m_foundFilesList.append( QPair(file, matchingLine) ); +} + +void KQuery::setContext(const QString & context, bool casesensitive, + bool search_binary, bool useRegexp) +{ + m_context = context; + m_casesensitive = casesensitive; + m_search_binary = search_binary; + m_regexpForContent=useRegexp; + if ( !m_regexpForContent ) + m_regexp.setPatternSyntax(QRegExp::Wildcard); + else + m_regexp.setPatternSyntax(QRegExp::RegExp); + if ( casesensitive ) + m_regexp.setCaseSensitivity(Qt::CaseSensitive); + else + m_regexp.setCaseSensitivity(Qt::CaseInsensitive); + if (m_regexpForContent) + m_regexp.setPattern(m_context); +} + +void KQuery::setMetaInfo(const QString &metainfo, const QString &metainfokey) +{ + m_metainfo=metainfo; + m_metainfokey=metainfokey; +} + +void KQuery::setMimeType(const QStringList &mimetype) +{ + m_mimetype = mimetype; +} + +void KQuery::setFileType(int filetype) +{ + m_filetype = filetype; +} + +void KQuery::setSizeRange(int mode, KIO::filesize_t value1, KIO::filesize_t value2) +{ + m_sizemode = mode; + m_sizeboundary1 = value1; + m_sizeboundary2 = value2; +} + +void KQuery::setTimeRange(time_t from, time_t to) +{ + m_timeFrom = from; + m_timeTo = to; +} + +void KQuery::setUsername(const QString &username) +{ + m_username = username; +} + +void KQuery::setGroupname(const QString &groupname) +{ + m_groupname = groupname; +} + +void KQuery::setRegExp(const QString ®exp, bool caseSensitive) +{ + QRegExp *regExp; + QRegExp sep(";"); + const QStringList strList=regexp.split( sep, QString::SkipEmptyParts); + // QRegExp globChars ("[\\*\\?\\[\\]]", TRUE, FALSE); + while (!m_regexps.isEmpty()) + delete m_regexps.takeFirst(); + + // m_regexpsContainsGlobs.clear(); + for ( QStringList::ConstIterator it = strList.constBegin(); it != strList.constEnd(); ++it ) { + regExp = new QRegExp((*it),( caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive ), QRegExp::Wildcard); + //m_regexpsContainsGlobs.append(regExp->pattern().contains(globChars)); + m_regexps.append(regExp); + } +} + +void KQuery::setRecursive(bool recursive) +{ + m_recursive = recursive; +} + +void KQuery::setPath(const KUrl &url) +{ + m_url = url; +} + +void KQuery::setUseFileIndex(bool useLocate) +{ + m_useLocate=useLocate; +} + +void KQuery::setShowHiddenFiles(bool showHidden) +{ + m_showHiddenFiles = showHidden; +} + +void KQuery::slotreadyReadStandardError() +{ + KMessageBox::error(NULL, QString::fromLocal8Bit(processLocate->readAllStandardOutput()), i18nc("@title:window", "Error while using locate")); +} + +void KQuery::slotreadyReadStandardOutput() +{ + bufferLocate += processLocate->readAllStandardOutput(); +} + +void KQuery::slotendProcessLocate(int code, QProcess::ExitStatus) +{ + if (code == 0 ) + { + if( !bufferLocate.isEmpty() ) + { + QString str = QString::fromLocal8Bit(bufferLocate); + bufferLocate.clear(); + slotListEntries(str.split('\n', QString::SkipEmptyParts)); + } + } + emit result(0); +} + +#include "moc_kquery.cpp" diff --git a/kfind/kquery.h b/kfind/kquery.h new file mode 100644 index 00000000..02ece32b --- /dev/null +++ b/kfind/kquery.h @@ -0,0 +1,128 @@ +/******************************************************************* +* kquery.h +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +******************************************************************/ + +#ifndef KQUERY_H +#define KQUERY_H + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +class KFileItem; + +class KQuery : public QObject +{ + Q_OBJECT + + public: + KQuery(QObject *parent = 0); + ~KQuery(); + + /* Functions to set Query requirements */ + void setSizeRange( int mode, KIO::filesize_t value1, KIO::filesize_t value2); + void setTimeRange( time_t from, time_t to ); + void setRegExp( const QString ®exp, bool caseSensitive ); + void setRecursive( bool recursive ); + void setPath(const KUrl & url ); + void setFileType( int filetype ); + void setMimeType( const QStringList & mimetype ); + void setContext( const QString & context, bool casesensitive, + bool search_binary, bool useRegexp ); + void setUsername( const QString &username ); + void setGroupname( const QString &groupname ); + void setMetaInfo(const QString &metainfo, const QString &metainfokey); + void setUseFileIndex(bool); + void setShowHiddenFiles(bool); + + void start(); + void kill(); + const KUrl& url() {return m_url;} + + private: + /* Check if file meets the find's requirements*/ + inline void processQuery(const KFileItem &); + + public Q_SLOTS: + /* List of files found using slocate */ + void slotListEntries(QStringList); + + protected Q_SLOTS: + /* List of files found using KIO */ + void slotListEntries(KIO::Job *, const KIO::UDSEntryList &); + void slotResult(KJob *); + void slotCanceled(KJob *); + + void slotreadyReadStandardOutput(); + void slotreadyReadStandardError(); + void slotendProcessLocate(int, QProcess::ExitStatus); + + Q_SIGNALS: + void foundFileList( QList< QPair >); + void result(int); + + private: + void checkEntries(); + + int m_filetype; + int m_sizemode; + KIO::filesize_t m_sizeboundary1; + KIO::filesize_t m_sizeboundary2; + KUrl m_url; + time_t m_timeFrom; + time_t m_timeTo; + QRegExp m_regexp;// regexp for file content + bool m_recursive; + QStringList m_mimetype; + QString m_context; + QString m_username; + QString m_groupname; + QString m_metainfo; + QString m_metainfokey; + bool m_casesensitive; + bool m_search_binary; + bool m_regexpForContent; + bool m_useLocate; + bool m_showHiddenFiles; + QByteArray bufferLocate; + QStringList locateList; + QProcess *processLocate; + QList m_regexps;// regexps for file name +// QValueList m_regexpsContainsGlobs; // what should this be good for ? Alex + KIO::ListJob *job; + bool m_insideCheckEntries; + QQueue m_fileItems; + QRegExp metaKeyRx; + int m_result; + QStringList ignore_mimetypes; + QStringList ooo_mimetypes; // OpenOffice.org mimetypes + QStringList koffice_mimetypes; + + QList< QPair > m_foundFilesList; +}; + +#endif diff --git a/kfind/main.cpp b/kfind/main.cpp new file mode 100644 index 00000000..0cbafb73 --- /dev/null +++ b/kfind/main.cpp @@ -0,0 +1,75 @@ +/******************************************************************* +* main.cpp +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +******************************************************************/ + +#include +#include + +#include +#include +#include +#include + +#include "kfinddlg.h" +#include "version.h" + +static const char description[] = I18N_NOOP("KDE file find utility"); + +int main( int argc, char ** argv ) +{ + KAboutData aboutData( "kfind", "kfindpart", ki18n("KFind"), + KFIND_VERSION, ki18n(description), KAboutData::License_GPL, + ki18n("(c) 1998-2003, The KDE Developers")); + + aboutData.addAuthor(ki18n("Eric Coquelle"), ki18n("Current Maintainer"), "coquelle@caramail.com"); + aboutData.addAuthor(ki18n("Mark W. Webb"), ki18n("Developer"), "markwebb@adelphia.net"); + aboutData.addAuthor(ki18n("Beppe Grimaldi"), ki18n("UI Design & more search options"), "grimalkin@ciaoweb.it"); + aboutData.addAuthor(ki18n("Martin Hartig")); + aboutData.addAuthor(ki18n("Stephan Kulow"), KLocalizedString(), "coolo@kde.org"); + aboutData.addAuthor(ki18n("Mario Weilguni"),KLocalizedString(), "mweilguni@sime.com"); + aboutData.addAuthor(ki18n("Alex Zepeda"),KLocalizedString(), "zipzippy@sonic.net"); + aboutData.addAuthor(ki18n("Miroslav Flídr"),KLocalizedString(), "flidr@kky.zcu.cz"); + aboutData.addAuthor(ki18n("Harri Porten"),KLocalizedString(), "porten@kde.org"); + aboutData.addAuthor(ki18n("Dima Rogozin"),KLocalizedString(), "dima@mercury.co.il"); + aboutData.addAuthor(ki18n("Carsten Pfeiffer"),KLocalizedString(), "pfeiffer@kde.org"); + aboutData.addAuthor(ki18n("Hans Petter Bieker"), KLocalizedString(), "bieker@kde.org"); + aboutData.addAuthor(ki18n("Waldo Bastian"), ki18n("UI Design"), "bastian@kde.org"); + aboutData.addAuthor(ki18n("Alexander Neundorf"), KLocalizedString(), "neundorf@kde.org"); + aboutData.addAuthor(ki18n("Clarence Dang"), KLocalizedString(), "dang@kde.org"); + + KCmdLineArgs::init( argc, argv, &aboutData ); + + KCmdLineOptions options; + options.add("+[searchpath]", ki18n("Path(s) to search")); + KCmdLineArgs::addCmdLineOptions( options ); + + KApplication app; + + KCmdLineArgs *args= KCmdLineArgs::parsedArgs(); + + KUrl url; + if (args->count() > 0) + url = args->url(0); + if (url.isEmpty()) + url = QDir::currentPath(); + if (url.isEmpty()) + url = QDir::homePath(); + args->clear(); + + KfindDlg kfinddlg(url); + return kfinddlg.exec(); +} diff --git a/kfind/version.h b/kfind/version.h new file mode 100644 index 00000000..24f2c33c --- /dev/null +++ b/kfind/version.h @@ -0,0 +1,3 @@ +#ifndef KFIND_VERSION +#define KFIND_VERSION "2.0" +#endif diff --git a/kmediaplayer/CMakeLists.txt b/kmediaplayer/CMakeLists.txt new file mode 100644 index 00000000..65709197 --- /dev/null +++ b/kmediaplayer/CMakeLists.txt @@ -0,0 +1,48 @@ +project(KMediaPlayer) + +# application +add_executable(kmediaplayer main.cpp kmediawindow.cpp) + +target_link_libraries(kmediaplayer + KDE4::kdecore + KDE4::kdeui + KDE4::kfile + KDE4::kcmutils + KDE4::kmediaplayer +) + +## part +kde4_add_plugin(kmediaplayerpart kmediaplayerpart.cpp) + +target_link_libraries(kmediaplayerpart + KDE4::kdecore + KDE4::kdeui + KDE4::kparts + KDE4::kmediaplayer +) + +## install everything +install( + TARGETS kmediaplayer + DESTINATION ${KDE4_BIN_INSTALL_DIR} +) + +install( + TARGETS kmediaplayerpart + DESTINATION ${KDE4_PLUGIN_INSTALL_DIR} +) + +install( + PROGRAMS kmediaplayer.desktop + DESTINATION ${KDE4_XDG_APPS_INSTALL_DIR} +) + +install( + PROGRAMS kmediaplayerpart.desktop + DESTINATION ${KDE4_SERVICES_INSTALL_DIR} +) + +install( + FILES kmediaplayerui.rc + DESTINATION ${KDE4_DATA_INSTALL_DIR}/kmediaplayer +) diff --git a/kmediaplayer/kmediaplayer.desktop b/kmediaplayer/kmediaplayer.desktop new file mode 100644 index 00000000..71ab33d8 --- /dev/null +++ b/kmediaplayer/kmediaplayer.desktop @@ -0,0 +1,11 @@ +[Desktop Entry] +Icon=applications-multimedia +Name=KMediaPlayer +GenericName=Multimedia player +Comment=A simple program to view your media files +Exec=kmediaplayer --icon '%i' --caption '%c' %U +Terminal=false +Type=Application +Categories=Qt;KDE;AudioVideo;Audio;Video;Player;TV; +MimeType=application/ogg;application/x-ogg;application/sdp;application/smil;application/x-smil;application/streamingmedia;application/x-streamingmedia;application/vnd.rn-realmedia;application/vnd.rn-realmedia-vbr;audio/aac;audio/x-aac;audio/m4a;audio/x-m4a;audio/mp1;audio/x-mp1;audio/mp2;audio/x-mp2;audio/mp3;audio/x-mp3;audio/mpeg;audio/x-mpeg;audio/mpegurl;audio/x-mpegurl;audio/mpg;audio/x-mpg;audio/rn-mpeg;audio/ogg;audio/scpls;audio/x-scpls;audio/vnd.rn-realaudio;audio/wav;audio/x-pn-windows-pcm;audio/x-realaudio;audio/x-pn-realaudio;audio/x-ms-wma;audio/x-pls;audio/x-wav;video/mpeg;video/x-mpeg;video/x-mpeg2;video/mp4;video/msvideo;video/x-msvideo;video/ogg;video/quicktime;video/vnd.rn-realvideo;video/x-ms-afs;video/x-ms-asf;video/x-ms-wmv;video/x-ms-wmx;video/x-ms-wvxvideo;video/x-avi;video/x-fli;video/x-flv;video/x-theora;video/x-matroska;video/webm;audio/x-flac;audio/x-vorbis+ogg;video/x-ogm+ogg;audio/x-shorten;audio/x-ape;audio/x-wavpack;audio/x-tta;audio/AMR;audio/ac3;video/mp2t;audio/flac;audio/mp4; +X-KDE-MediaPlayer=kmediaplayer diff --git a/kmediaplayer/kmediaplayerpart.cpp b/kmediaplayer/kmediaplayerpart.cpp new file mode 100644 index 00000000..f698a70d --- /dev/null +++ b/kmediaplayer/kmediaplayerpart.cpp @@ -0,0 +1,85 @@ +/* This file is part of the KDE libraries + Copyright (C) 2016 Ivailo Monev + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2, as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "kmediaplayerpart.h" + +#include +#include +#include +#include + +K_PLUGIN_FACTORY(KMediaPlayerPartFactory, registerPlugin();) // produce a factory +K_EXPORT_PLUGIN(KMediaPlayerPartFactory(KAboutData( + "kmediaplayerpart", + 0, + ki18n("KMediaPlayerPart"), + "1.1.0", + ki18n("Simple media player part for KDE."), + KAboutData::License_GPL_V2, + ki18n("(c) 2016 Ivailo Monev"), + KLocalizedString(), + "http://github.com/fluxer/katana", + "xakepa10@gmail.com"). + setProgramIconName(QLatin1String("KMediaPlayerPart")). + setCatalogName("kmediaplayer"))) + +KMediaPlayerPart::KMediaPlayerPart(QWidget *parentWidget, QObject *parent, const QList &arguments) + : ReadOnlyPart(parent) + , m_player(new KMediaWidget(parentWidget, KMediaWidget::HiddenControls)) +{ + Q_UNUSED(arguments); + setComponentData(KMediaPlayerPartFactory::componentData()); + setWidget(m_player); + m_player->player()->setPlayerID("kmediaplayerpart"); +} + +KMediaPlayerPart::~KMediaPlayerPart() +{ + m_player->player()->stop(); + m_player->deleteLater(); +} + +bool KMediaPlayerPart::openUrl(const KUrl &url) +{ + setUrl(url); + return openFile(); +} + +bool KMediaPlayerPart::openFile() +{ + const KUrl kurl = url(); + const QString kurlstring = kurl.prettyUrl(); + if (!kurl.isValid()) { + KMessageBox::information(widget(), i18n("The URL is not valid: %1", kurlstring)); + } else if (!m_player->player()->isProtocolSupported(kurl.protocol())) { + KMessageBox::information(widget(), i18n("The URL protocol is not valid: %1", kurl.protocol())); + } else { + m_player->open(kurlstring); + return true; + } + + return false; +} + +bool KMediaPlayerPart::closeUrl() +{ + m_player->player()->stop(); + return ReadOnlyPart::closeUrl(); +} + +#include "moc_kmediaplayerpart.cpp" diff --git a/kmediaplayer/kmediaplayerpart.desktop b/kmediaplayer/kmediaplayerpart.desktop new file mode 100644 index 00000000..3a0c9535 --- /dev/null +++ b/kmediaplayer/kmediaplayerpart.desktop @@ -0,0 +1,9 @@ +[Desktop Entry] +Icon=applications-multimedia +Name=KMediaPlayerPart +Comment=Simple media player part for KDE +X-KDE-ServiceTypes=KParts/ReadOnlyPart +X-KDE-Library=kmediaplayerpart +X-KDE-MediaPlayer=kmediaplayerpart +Type=Service +MimeType=application/ogg;application/x-ogg;application/sdp;application/smil;application/x-smil;application/streamingmedia;application/x-streamingmedia;application/vnd.rn-realmedia;application/vnd.rn-realmedia-vbr;audio/aac;audio/x-aac;audio/m4a;audio/x-m4a;audio/mp1;audio/x-mp1;audio/mp2;audio/x-mp2;audio/mp3;audio/x-mp3;audio/mpeg;audio/x-mpeg;audio/mpegurl;audio/x-mpegurl;audio/mpg;audio/x-mpg;audio/rn-mpeg;audio/ogg;audio/scpls;audio/x-scpls;audio/vnd.rn-realaudio;audio/wav;audio/x-pn-windows-pcm;audio/x-realaudio;audio/x-pn-realaudio;audio/x-ms-wma;audio/x-pls;audio/x-wav;video/mpeg;video/x-mpeg;video/x-mpeg2;video/mp4;video/msvideo;video/x-msvideo;video/ogg;video/quicktime;video/vnd.rn-realvideo;video/x-ms-afs;video/x-ms-asf;video/x-ms-wmv;video/x-ms-wmx;video/x-ms-wvxvideo;video/x-avi;video/x-fli;video/x-flv;video/x-theora;video/x-matroska;video/webm;audio/x-flac;audio/x-vorbis+ogg;video/x-ogm+ogg;audio/x-shorten;audio/x-ape;audio/x-wavpack;audio/x-tta;audio/AMR;audio/ac3;video/mp2t;audio/flac;audio/mp4; \ No newline at end of file diff --git a/kmediaplayer/kmediaplayerpart.h b/kmediaplayer/kmediaplayerpart.h new file mode 100644 index 00000000..573c8bec --- /dev/null +++ b/kmediaplayer/kmediaplayerpart.h @@ -0,0 +1,42 @@ +/* This file is part of the KDE libraries + Copyright (C) 2016 Ivailo Monev + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2, as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KMEDIAPART_H +#define KMEDIAPART_H + +#include +#include +#include + +class KMediaPlayerPart : public KParts::ReadOnlyPart +{ + Q_OBJECT +public: + KMediaPlayerPart(QWidget *parentWidget, QObject *parent, const QList &arguments); + ~KMediaPlayerPart(); + + // reimplementations + virtual bool openUrl(const KUrl &url); + virtual bool openFile(); + virtual bool closeUrl(); + +private: + KMediaWidget *m_player; +}; + +#endif // KMEDIAPART_H diff --git a/kmediaplayer/kmediaplayerui.rc b/kmediaplayer/kmediaplayerui.rc new file mode 100644 index 00000000..3c49495a --- /dev/null +++ b/kmediaplayer/kmediaplayerui.rc @@ -0,0 +1,21 @@ + + + + &File + + + + + + + + + &Player + + + + &Settings + + + + diff --git a/kmediaplayer/kmediawindow.cpp b/kmediaplayer/kmediawindow.cpp new file mode 100644 index 00000000..541dcea6 --- /dev/null +++ b/kmediaplayer/kmediawindow.cpp @@ -0,0 +1,230 @@ +/* This file is part of the KDE libraries + Copyright (C) 2016 Ivailo Monev + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2, as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "kmediawindow.h" + +KMediaWindow::KMediaWindow(QWidget *parent, Qt::WindowFlags flags) + : KXmlGuiWindow(parent, flags) +{ + m_config = new KConfig("kmediaplayerrc", KConfig::SimpleConfig); + + m_player = new KMediaWidget(this, KMediaWidget::AllOptions); + m_player->player()->setPlayerID("kmediaplayer"); + + setCentralWidget(m_player); + + KAction *a = actionCollection()->addAction("file_open_path", this, SLOT(openPath())); + a->setText(i18n("Open")); + a->setIcon(KIcon("document-open")); + a->setShortcut(KStandardShortcut::open()); + a->setWhatsThis(i18n("Open a path.")); + + KAction *b = actionCollection()->addAction("file_open_url", this, SLOT(openURL())); + b->setText(i18n("Open URL")); + b->setIcon(KIcon("document-open-remote")); + b->setWhatsThis(i18n("Open a URL.")); + + KAction *c = actionCollection()->addAction("file_close", this, SLOT(closePath())); + c->setText(i18n("Close")); + c->setIcon(KIcon("document-close")); + c->setShortcut(KStandardShortcut::close()); + c->setWhatsThis(i18n("Close the the current path/URL.")); + + KAction *d = actionCollection()->addAction("file_quit", this, SLOT(quit())); + d->setText(i18n("Quit")); + d->setIcon(KIcon("application-exit")); + d->setShortcut(KStandardShortcut::quit()); + d->setWhatsThis(i18n("Close the application.")); + + KAction *e = actionCollection()->addAction("player_fullscreen", this, SLOT(fullscreen())); + e->setText(i18n("Fullscreen")); + e->setIcon(KIcon("view-fullscreen")); + e->setShortcut(KStandardShortcut::fullScreen()); + e->setWhatsThis(i18n("Set the player view to fullscreen/non-fullscreen")); + + KAction *g = actionCollection()->addAction("settings_player", this, SLOT(configure())); + g->setText(i18n("Configure KMediaPlayer")); + g->setIcon(KIcon("preferences-desktop-sound")); + g->setWhatsThis(i18n("Configure KMediaPlayer and applications that use it.")); + + m_recentfiles = new KRecentFilesAction(KIcon("document-open-recent"), "Open recent", this); + m_recentfiles->setShortcut(KStandardShortcut::shortcut(KStandardShortcut::OpenRecent)); + m_recentfiles->setWhatsThis(i18n("Open recently opened files.")); + connect(m_recentfiles, SIGNAL(urlSelected(KUrl)), this, SLOT(openURL(KUrl))); + actionCollection()->addAction("file_open_recent", m_recentfiles); + + KConfigGroup firstrungroup(m_config, "KMediaPlayer"); + const bool firstrun = firstrungroup.readEntry("firstrun", true); + if (firstrun) { + // no toolbar unless explicitly enabled + toolBar()->setVisible(false); + // also set a decent window size + resize(640, 480); + } + + connect(m_player, SIGNAL(controlsHidden(bool)), this, SLOT(hideMenuBar(bool))); + m_menu = new QMenu(); + m_menu->addAction(KIcon("show-menu"), i18n("Show/hide menubar"), this, SLOT(menubar())); + setContextMenuPolicy(Qt::CustomContextMenu); + connect(this, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(menu(QPoint))); + + setupGUI(KXmlGuiWindow::Keys | KXmlGuiWindow::StatusBar | KXmlGuiWindow::Save | KXmlGuiWindow::Create); + setAutoSaveSettings(); + + KConfigGroup recentfilesgroup(m_config, "RecentFiles"); + m_recentfiles->loadEntries(recentfilesgroup); + + setMouseTracking(true); + qApp->installEventFilter(this); +} + +KMediaWindow::~KMediaWindow() +{ + hideMenuBar(true); + disconnect(m_player, SIGNAL(controlsHidden(bool)), this, SLOT(hideMenuBar(bool))); + saveAutoSaveSettings(); + + KConfigGroup recentfilesgroup(m_config, "RecentFiles"); + m_recentfiles->saveEntries(recentfilesgroup); + KConfigGroup firstrungroup(m_config, "KMediaPlayer"); + firstrungroup.writeEntry("firstrun", false); + + m_player->deleteLater(); + m_recentfiles->deleteLater(); + m_menu->deleteLater(); + delete m_config; +} + +void KMediaWindow::showEvent(QShowEvent *event) +{ + m_menuvisible = menuBar()->isVisible(); + m_statusvisible = statusBar()->isVisible(); + KXmlGuiWindow::showEvent(event); +} + +bool KMediaWindow::eventFilter(QObject *object, QEvent *event) +{ + if (event->type() == QEvent::MouseMove || event->type() == QEvent::KeyPress) { + m_player->resetControlsTimer(); + } + return KXmlGuiWindow::eventFilter(object, event); +} + +void KMediaWindow::hideMenuBar(bool visible) +{ + if (!visible) { + m_menuvisible = menuBar()->isVisible(); + m_statusvisible = statusBar()->isVisible(); + menuBar()->setVisible(false); + statusBar()->setVisible(false); + } else { + menuBar()->setVisible(m_menuvisible); + statusBar()->setVisible(m_statusvisible); + } +} + +void KMediaWindow::openPath() +{ + const QString path = KFileDialog::getOpenFileName(KUrl(), QString(), this, i18n("Select paths")); + if (!path.isEmpty()) { + if (!m_player->player()->isPathSupported(path)) { + QMessageBox::warning(this, i18n("Invalid path"), + i18n("The path is invalid:\n%1", path)); + } else { + m_player->open(path); + m_recentfiles->addUrl(KUrl(path)); + } + } +} + +void KMediaWindow::openURL() +{ + bool dummy; + QString protocols = m_player->player()->protocols().join(", "); + KUrl url = KInputDialog::getText(i18n("Input URL"), + i18n("Supported protocols are:\n\n%1", protocols), + QString(), &dummy, this); + if (!url.isEmpty()) { + QString urlstring = url.prettyUrl(); + if (!m_player->player()->isPathSupported(urlstring)) { + QMessageBox::warning(this, i18n("Invalid URL"), + i18n("Invalid URL:\n%1", urlstring)); + } else { + m_player->open(urlstring); + m_recentfiles->addUrl(url); + } + } +} + +void KMediaWindow::openURL(KUrl url) +{ + m_player->open(url.prettyUrl()); + m_recentfiles->addUrl(url); +} + +void KMediaWindow::closePath() +{ + m_player->player()->stop(); + statusBar()->showMessage(""); +} + +void KMediaWindow::fullscreen() +{ + m_player->setFullscreen(); +} + +void KMediaWindow::configure() +{ + KCMultiDialog kcmdialg(this); + kcmdialg.addModule("kcmplayer"); + kcmdialg.exec(); +} + +void KMediaWindow::menubar() { + menuBar()->setVisible(!menuBar()->isVisible()); +} + +void KMediaWindow::menu(QPoint position) +{ + // it is bogus, just ignore it + Q_UNUSED(position); + m_menu->exec(QCursor::pos()); +} + +void KMediaWindow::quit() +{ + KMediaWindow::close(); + qApp->quit(); +} diff --git a/kmediaplayer/kmediawindow.h b/kmediaplayer/kmediawindow.h new file mode 100644 index 00000000..a3ed3837 --- /dev/null +++ b/kmediaplayer/kmediawindow.h @@ -0,0 +1,63 @@ +/* This file is part of the KDE libraries + Copyright (C) 2016 Ivailo Monev + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2, as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KMEDIAWINDOW_H +#define KMEDIAWINDOW_H + +#include +#include +#include +#include +#include + +class KMediaWindow: public KXmlGuiWindow +{ + Q_OBJECT +public: + KMediaWindow(QWidget *parent = 0, Qt::WindowFlags flags = 0); + ~KMediaWindow(); + +public slots: + void openPath(); + void openURL(); + void openURL(KUrl url); + void closePath(); + void fullscreen(); + void configure(); + void menubar(); + void menu(QPoint position); + void quit(); + +private slots: + void hideMenuBar(bool hidden); + +protected: + virtual void showEvent(QShowEvent *event); + virtual bool eventFilter(QObject *object, QEvent *event); + +private: + KConfig *m_config; + KMediaWidget *m_player; + KRecentFilesAction *m_recentfiles; + QMenu *m_menu; + bool m_menuvisible; + bool m_toolvisible; + bool m_statusvisible; +}; + +#endif // KMEDIAWINDOW_H diff --git a/kmediaplayer/main.cpp b/kmediaplayer/main.cpp new file mode 100644 index 00000000..01ffc644 --- /dev/null +++ b/kmediaplayer/main.cpp @@ -0,0 +1,61 @@ +/* This file is part of the KDE libraries + Copyright (C) 2016 Ivailo Monev + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2, as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "kmediawindow.h" + +int main(int argc, char **argv) { + KAboutData aboutData("kmediaplayer", 0, ki18n("KMediaPlayer"), + "1.0.0", ki18n("Simple media player for KDE."), + KAboutData::License_GPL_V2, + ki18n("(c) 2016 Ivailo Monev"), + KLocalizedString(), + "http://github.com/fluxer/katana" + ); + + aboutData.addAuthor(ki18n("Ivailo Monev"), + ki18n("Maintainer"), + "xakepa10@gmail.com"); + aboutData.setProgramIconName(QLatin1String("applications-multimedia")); + aboutData.setOrganizationDomain("kde.org"); + + KCmdLineArgs::init(argc, argv, &aboutData); + KCmdLineOptions option; + option.add("+[url]", ki18n("URL to be opened")); + KCmdLineArgs::addCmdLineOptions(option); + + KApplication *app = new KApplication(); + KMediaWindow *window = new KMediaWindow(); + window->show(); + + KCmdLineArgs *args = KCmdLineArgs::parsedArgs(); + for (int pos = 0; pos < args->count(); ++pos) { + window->openURL(args->url(pos)); + } + + return app->exec(); +} diff --git a/konsole/CMakeLists.txt b/konsole/CMakeLists.txt new file mode 100644 index 00000000..16fc75f8 --- /dev/null +++ b/konsole/CMakeLists.txt @@ -0,0 +1,16 @@ +project(Konsole) + +include_directories( + ${CMAKE_SOURCE_DIR} + ${CMAKE_BINARY_DIR} + ${CMAKE_BINARY_DIR}/libs/konq +) + +add_definitions( + ${QT_QTDBUS_DEFINITIONS} + -DKDE_DEFAULT_DEBUG_AREA=1211 +) + +add_subdirectory(src) +add_subdirectory(data) +add_subdirectory(desktop) diff --git a/konsole/COPYING b/konsole/COPYING new file mode 100644 index 00000000..5185fd3f --- /dev/null +++ b/konsole/COPYING @@ -0,0 +1,346 @@ +NOTE! The GPL below is copyrighted by the Free Software Foundation, but +the instance of code that it refers to (the kde programs) are copyrighted +by the authors who actually wrote it. + +--------------------------------------------------------------------------- + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/konsole/COPYING.DOC b/konsole/COPYING.DOC new file mode 100644 index 00000000..a988da5a --- /dev/null +++ b/konsole/COPYING.DOC @@ -0,0 +1,397 @@ + GNU Free Documentation License + Version 1.2, November 2002 + + + Copyright (C) 2000,2001,2002 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + +0. PREAMBLE + +The purpose of this License is to make a manual, textbook, or other +functional and useful document "free" in the sense of freedom: to +assure everyone the effective freedom to copy and redistribute it, +with or without modifying it, either commercially or noncommercially. +Secondarily, this License preserves for the author and publisher a way +to get credit for their work, while not being considered responsible +for modifications made by others. + +This License is a kind of "copyleft", which means that derivative +works of the document must themselves be free in the same sense. It +complements the GNU General Public License, which is a copyleft +license designed for free software. + +We have designed this License in order to use it for manuals for free +software, because free software needs free documentation: a free +program should come with manuals providing the same freedoms that the +software does. But this License is not limited to software manuals; +it can be used for any textual work, regardless of subject matter or +whether it is published as a printed book. We recommend this License +principally for works whose purpose is instruction or reference. + + +1. APPLICABILITY AND DEFINITIONS + +This License applies to any manual or other work, in any medium, that +contains a notice placed by the copyright holder saying it can be +distributed under the terms of this License. Such a notice grants a +world-wide, royalty-free license, unlimited in duration, to use that +work under the conditions stated herein. The "Document", below, +refers to any such manual or work. Any member of the public is a +licensee, and is addressed as "you". You accept the license if you +copy, modify or distribute the work in a way requiring permission +under copyright law. + +A "Modified Version" of the Document means any work containing the +Document or a portion of it, either copied verbatim, or with +modifications and/or translated into another language. + +A "Secondary Section" is a named appendix or a front-matter section of +the Document that deals exclusively with the relationship of the +publishers or authors of the Document to the Document's overall subject +(or to related matters) and contains nothing that could fall directly +within that overall subject. (Thus, if the Document is in part a +textbook of mathematics, a Secondary Section may not explain any +mathematics.) The relationship could be a matter of historical +connection with the subject or with related matters, or of legal, +commercial, philosophical, ethical or political position regarding +them. + +The "Invariant Sections" are certain Secondary Sections whose titles +are designated, as being those of Invariant Sections, in the notice +that says that the Document is released under this License. If a +section does not fit the above definition of Secondary then it is not +allowed to be designated as Invariant. The Document may contain zero +Invariant Sections. If the Document does not identify any Invariant +Sections then there are none. + +The "Cover Texts" are certain short passages of text that are listed, +as Front-Cover Texts or Back-Cover Texts, in the notice that says that +the Document is released under this License. A Front-Cover Text may +be at most 5 words, and a Back-Cover Text may be at most 25 words. + +A "Transparent" copy of the Document means a machine-readable copy, +represented in a format whose specification is available to the +general public, that is suitable for revising the document +straightforwardly with generic text editors or (for images composed of +pixels) generic paint programs or (for drawings) some widely available +drawing editor, and that is suitable for input to text formatters or +for automatic translation to a variety of formats suitable for input +to text formatters. A copy made in an otherwise Transparent file +format whose markup, or absence of markup, has been arranged to thwart +or discourage subsequent modification by readers is not Transparent. +An image format is not Transparent if used for any substantial amount +of text. A copy that is not "Transparent" is called "Opaque". + +Examples of suitable formats for Transparent copies include plain +ASCII without markup, Texinfo input format, LaTeX input format, SGML +or XML using a publicly available DTD, and standard-conforming simple +HTML, PostScript or PDF designed for human modification. Examples of +transparent image formats include PNG, XCF and JPG. Opaque formats +include proprietary formats that can be read and edited only by +proprietary word processors, SGML or XML for which the DTD and/or +processing tools are not generally available, and the +machine-generated HTML, PostScript or PDF produced by some word +processors for output purposes only. + +The "Title Page" means, for a printed book, the title page itself, +plus such following pages as are needed to hold, legibly, the material +this License requires to appear in the title page. For works in +formats which do not have any title page as such, "Title Page" means +the text near the most prominent appearance of the work's title, +preceding the beginning of the body of the text. + +A section "Entitled XYZ" means a named subunit of the Document whose +title either is precisely XYZ or contains XYZ in parentheses following +text that translates XYZ in another language. (Here XYZ stands for a +specific section name mentioned below, such as "Acknowledgements", +"Dedications", "Endorsements", or "History".) To "Preserve the Title" +of such a section when you modify the Document means that it remains a +section "Entitled XYZ" according to this definition. + +The Document may include Warranty Disclaimers next to the notice which +states that this License applies to the Document. These Warranty +Disclaimers are considered to be included by reference in this +License, but only as regards disclaiming warranties: any other +implication that these Warranty Disclaimers may have is void and has +no effect on the meaning of this License. + + +2. VERBATIM COPYING + +You may copy and distribute the Document in any medium, either +commercially or noncommercially, provided that this License, the +copyright notices, and the license notice saying this License applies +to the Document are reproduced in all copies, and that you add no other +conditions whatsoever to those of this License. You may not use +technical measures to obstruct or control the reading or further +copying of the copies you make or distribute. However, you may accept +compensation in exchange for copies. If you distribute a large enough +number of copies you must also follow the conditions in section 3. + +You may also lend copies, under the same conditions stated above, and +you may publicly display copies. + + +3. COPYING IN QUANTITY + +If you publish printed copies (or copies in media that commonly have +printed covers) of the Document, numbering more than 100, and the +Document's license notice requires Cover Texts, you must enclose the +copies in covers that carry, clearly and legibly, all these Cover +Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on +the back cover. Both covers must also clearly and legibly identify +you as the publisher of these copies. The front cover must present +the full title with all words of the title equally prominent and +visible. You may add other material on the covers in addition. +Copying with changes limited to the covers, as long as they preserve +the title of the Document and satisfy these conditions, can be treated +as verbatim copying in other respects. + +If the required texts for either cover are too voluminous to fit +legibly, you should put the first ones listed (as many as fit +reasonably) on the actual cover, and continue the rest onto adjacent +pages. + +If you publish or distribute Opaque copies of the Document numbering +more than 100, you must either include a machine-readable Transparent +copy along with each Opaque copy, or state in or with each Opaque copy +a computer-network location from which the general network-using +public has access to download using public-standard network protocols +a complete Transparent copy of the Document, free of added material. +If you use the latter option, you must take reasonably prudent steps, +when you begin distribution of Opaque copies in quantity, to ensure +that this Transparent copy will remain thus accessible at the stated +location until at least one year after the last time you distribute an +Opaque copy (directly or through your agents or retailers) of that +edition to the public. + +It is requested, but not required, that you contact the authors of the +Document well before redistributing any large number of copies, to give +them a chance to provide you with an updated version of the Document. + + +4. MODIFICATIONS + +You may copy and distribute a Modified Version of the Document under +the conditions of sections 2 and 3 above, provided that you release +the Modified Version under precisely this License, with the Modified +Version filling the role of the Document, thus licensing distribution +and modification of the Modified Version to whoever possesses a copy +of it. In addition, you must do these things in the Modified Version: + +A. Use in the Title Page (and on the covers, if any) a title distinct + from that of the Document, and from those of previous versions + (which should, if there were any, be listed in the History section + of the Document). You may use the same title as a previous version + if the original publisher of that version gives permission. +B. List on the Title Page, as authors, one or more persons or entities + responsible for authorship of the modifications in the Modified + Version, together with at least five of the principal authors of the + Document (all of its principal authors, if it has fewer than five), + unless they release you from this requirement. +C. State on the Title page the name of the publisher of the + Modified Version, as the publisher. +D. Preserve all the copyright notices of the Document. +E. Add an appropriate copyright notice for your modifications + adjacent to the other copyright notices. +F. Include, immediately after the copyright notices, a license notice + giving the public permission to use the Modified Version under the + terms of this License, in the form shown in the Addendum below. +G. Preserve in that license notice the full lists of Invariant Sections + and required Cover Texts given in the Document's license notice. +H. Include an unaltered copy of this License. +I. Preserve the section Entitled "History", Preserve its Title, and add + to it an item stating at least the title, year, new authors, and + publisher of the Modified Version as given on the Title Page. If + there is no section Entitled "History" in the Document, create one + stating the title, year, authors, and publisher of the Document as + given on its Title Page, then add an item describing the Modified + Version as stated in the previous sentence. +J. Preserve the network location, if any, given in the Document for + public access to a Transparent copy of the Document, and likewise + the network locations given in the Document for previous versions + it was based on. These may be placed in the "History" section. + You may omit a network location for a work that was published at + least four years before the Document itself, or if the original + publisher of the version it refers to gives permission. +K. For any section Entitled "Acknowledgements" or "Dedications", + Preserve the Title of the section, and preserve in the section all + the substance and tone of each of the contributor acknowledgements + and/or dedications given therein. +L. Preserve all the Invariant Sections of the Document, + unaltered in their text and in their titles. Section numbers + or the equivalent are not considered part of the section titles. +M. Delete any section Entitled "Endorsements". Such a section + may not be included in the Modified Version. +N. Do not retitle any existing section to be Entitled "Endorsements" + or to conflict in title with any Invariant Section. +O. Preserve any Warranty Disclaimers. + +If the Modified Version includes new front-matter sections or +appendices that qualify as Secondary Sections and contain no material +copied from the Document, you may at your option designate some or all +of these sections as invariant. To do this, add their titles to the +list of Invariant Sections in the Modified Version's license notice. +These titles must be distinct from any other section titles. + +You may add a section Entitled "Endorsements", provided it contains +nothing but endorsements of your Modified Version by various +parties--for example, statements of peer review or that the text has +been approved by an organization as the authoritative definition of a +standard. + +You may add a passage of up to five words as a Front-Cover Text, and a +passage of up to 25 words as a Back-Cover Text, to the end of the list +of Cover Texts in the Modified Version. Only one passage of +Front-Cover Text and one of Back-Cover Text may be added by (or +through arrangements made by) any one entity. If the Document already +includes a cover text for the same cover, previously added by you or +by arrangement made by the same entity you are acting on behalf of, +you may not add another; but you may replace the old one, on explicit +permission from the previous publisher that added the old one. + +The author(s) and publisher(s) of the Document do not by this License +give permission to use their names for publicity for or to assert or +imply endorsement of any Modified Version. + + +5. COMBINING DOCUMENTS + +You may combine the Document with other documents released under this +License, under the terms defined in section 4 above for modified +versions, provided that you include in the combination all of the +Invariant Sections of all of the original documents, unmodified, and +list them all as Invariant Sections of your combined work in its +license notice, and that you preserve all their Warranty Disclaimers. + +The combined work need only contain one copy of this License, and +multiple identical Invariant Sections may be replaced with a single +copy. If there are multiple Invariant Sections with the same name but +different contents, make the title of each such section unique by +adding at the end of it, in parentheses, the name of the original +author or publisher of that section if known, or else a unique number. +Make the same adjustment to the section titles in the list of +Invariant Sections in the license notice of the combined work. + +In the combination, you must combine any sections Entitled "History" +in the various original documents, forming one section Entitled +"History"; likewise combine any sections Entitled "Acknowledgements", +and any sections Entitled "Dedications". You must delete all sections +Entitled "Endorsements". + + +6. COLLECTIONS OF DOCUMENTS + +You may make a collection consisting of the Document and other documents +released under this License, and replace the individual copies of this +License in the various documents with a single copy that is included in +the collection, provided that you follow the rules of this License for +verbatim copying of each of the documents in all other respects. + +You may extract a single document from such a collection, and distribute +it individually under this License, provided you insert a copy of this +License into the extracted document, and follow this License in all +other respects regarding verbatim copying of that document. + + +7. AGGREGATION WITH INDEPENDENT WORKS + +A compilation of the Document or its derivatives with other separate +and independent documents or works, in or on a volume of a storage or +distribution medium, is called an "aggregate" if the copyright +resulting from the compilation is not used to limit the legal rights +of the compilation's users beyond what the individual works permit. +When the Document is included in an aggregate, this License does not +apply to the other works in the aggregate which are not themselves +derivative works of the Document. + +If the Cover Text requirement of section 3 is applicable to these +copies of the Document, then if the Document is less than one half of +the entire aggregate, the Document's Cover Texts may be placed on +covers that bracket the Document within the aggregate, or the +electronic equivalent of covers if the Document is in electronic form. +Otherwise they must appear on printed covers that bracket the whole +aggregate. + + +8. TRANSLATION + +Translation is considered a kind of modification, so you may +distribute translations of the Document under the terms of section 4. +Replacing Invariant Sections with translations requires special +permission from their copyright holders, but you may include +translations of some or all Invariant Sections in addition to the +original versions of these Invariant Sections. You may include a +translation of this License, and all the license notices in the +Document, and any Warranty Disclaimers, provided that you also include +the original English version of this License and the original versions +of those notices and disclaimers. In case of a disagreement between +the translation and the original version of this License or a notice +or disclaimer, the original version will prevail. + +If a section in the Document is Entitled "Acknowledgements", +"Dedications", or "History", the requirement (section 4) to Preserve +its Title (section 1) will typically require changing the actual +title. + + +9. TERMINATION + +You may not copy, modify, sublicense, or distribute the Document except +as expressly provided for under this License. Any other attempt to +copy, modify, sublicense or distribute the Document is void, and will +automatically terminate your rights under this License. However, +parties who have received copies, or rights, from you under this +License will not have their licenses terminated so long as such +parties remain in full compliance. + + +10. FUTURE REVISIONS OF THIS LICENSE + +The Free Software Foundation may publish new, revised versions +of the GNU Free Documentation 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. See +http://www.gnu.org/copyleft/. + +Each version of the License is given a distinguishing version number. +If the Document specifies that a particular numbered version of this +License "or any later version" applies to it, you have the option of +following the terms and conditions either of that specified version or +of any later version that has been published (not as a draft) by the +Free Software Foundation. If the Document does not specify a version +number of this License, you may choose any version ever published (not +as a draft) by the Free Software Foundation. + + +ADDENDUM: How to use this License for your documents + +To use this License in a document you have written, include a copy of +the License in the document and put the following copyright and +license notices just after the title page: + + Copyright (c) YEAR YOUR NAME. + Permission is granted to copy, distribute and/or modify this document + under the terms of the GNU Free Documentation License, Version 1.2 + or any later version published by the Free Software Foundation; + with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. + A copy of the license is included in the section entitled "GNU + Free Documentation License". + +If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts, +replace the "with...Texts." line with this: + + with the Invariant Sections being LIST THEIR TITLES, with the + Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST. + +If you have Invariant Sections without Cover Texts, or some other +combination of the three, merge those two alternatives to suit the +situation. + +If your document contains nontrivial examples of program code, we +recommend releasing these examples in parallel under your choice of +free software license, such as the GNU General Public License, +to permit their use in free software. diff --git a/konsole/COPYING.LIB b/konsole/COPYING.LIB new file mode 100644 index 00000000..5bc8fb2c --- /dev/null +++ b/konsole/COPYING.LIB @@ -0,0 +1,481 @@ + GNU LIBRARY GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1991 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the library GPL. It is + numbered 2 because it goes with version 2 of the ordinary GPL.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Library General Public License, applies to some +specially designated Free Software Foundation software, and to any +other libraries whose authors decide to use it. You can use it for +your libraries, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if +you distribute copies of the library, or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link a program with the library, you must provide +complete object files to the recipients so that they can relink them +with the library, after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + Our method of protecting your rights has two steps: (1) copyright +the library, and (2) offer you this license which gives you legal +permission to copy, distribute and/or modify the library. + + Also, for each distributor's protection, we want to make certain +that everyone understands that there is no warranty for this free +library. If the library is modified by someone else and passed on, we +want its recipients to know that what they have is not the original +version, so that any problems introduced by others will not reflect on +the original authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that companies distributing free +software will individually obtain patent licenses, thus in effect +transforming the program into proprietary software. To prevent this, +we have made it clear that any patent must be licensed for everyone's +free use or not licensed at all. + + Most GNU software, including some libraries, is covered by the ordinary +GNU General Public License, which was designed for utility programs. This +license, the GNU Library General Public License, applies to certain +designated libraries. This license is quite different from the ordinary +one; be sure to read it in full, and don't assume that anything in it is +the same as in the ordinary license. + + The reason we have a separate public license for some libraries is that +they blur the distinction we usually make between modifying or adding to a +program and simply using it. Linking a program with a library, without +changing the library, is in some sense simply using the library, and is +analogous to running a utility program or application program. However, in +a textual and legal sense, the linked executable is a combined work, a +derivative of the original library, and the ordinary General Public License +treats it as such. + + Because of this blurred distinction, using the ordinary General +Public License for libraries did not effectively promote software +sharing, because most developers did not use the libraries. We +concluded that weaker conditions might promote sharing better. + + However, unrestricted linking of non-free programs would deprive the +users of those programs of all benefit from the free status of the +libraries themselves. This Library General Public License is intended to +permit developers of non-free programs to use free libraries, while +preserving your freedom as a user of such programs to change the free +libraries that are incorporated in them. (We have not seen how to achieve +this as regards changes in header files, but we have achieved it as regards +changes in the actual functions of the Library.) The hope is that this +will lead to faster development of free libraries. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, while the latter only +works together with the library. + + Note that it is possible for a library to be covered by the ordinary +General Public License rather than by this special one. + + GNU LIBRARY GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library which +contains a notice placed by the copyright holder or other authorized +party saying it may be distributed under the terms of this Library +General Public License (also called "this License"). Each licensee is +addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also compile or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + c) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + d) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the source code distributed need not include anything that is normally +distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Library 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 Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "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 +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY 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 +LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/konsole/COPYING.Unicode b/konsole/COPYING.Unicode new file mode 100644 index 00000000..0986d2df --- /dev/null +++ b/konsole/COPYING.Unicode @@ -0,0 +1,64 @@ +This license applies to the original wcwidth.c which is used in +src/konsole_wcwidth.cpp. See that file for more info. + +EXHIBIT 1 +UNICODE, INC. LICENSE AGREEMENT - DATA FILES AND SOFTWARE + + Unicode Data Files include all data files under the directories + http://www.unicode.org/Public/, http://www.unicode.org/reports/, + and http://www.unicode.org/cldr/data/. Unicode Data Files do not + include PDF online code charts under the directory + http://www.unicode.org/Public/. Software includes any source code + published in the Unicode Standard or under the directories + http://www.unicode.org/Public/, http://www.unicode.org/reports/, + and http://www.unicode.org/cldr/data/. + + NOTICE TO USER: Carefully read the following legal agreement. BY + DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING UNICODE INC.'S + DATA FILES ("DATA FILES"), AND/OR SOFTWARE ("SOFTWARE"), YOU + UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE TERMS + AND CONDITIONS OF THIS AGREEMENT. IF YOU DO NOT AGREE, DO NOT + DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE THE DATA FILES OR + SOFTWARE. + + COPYRIGHT AND PERMISSION NOTICE + + Copyright © 1991-2012 Unicode, Inc. All rights + reserved. Distributed under the Terms of Use in + http://www.unicode.org/copyright.html. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of the Unicode data files and any associated + documentation (the "Data Files") or Unicode software and any + associated documentation (the "Software") to deal in the Data + Files or Software without restriction, including without + limitation the rights to use, copy, modify, merge, publish, + distribute, and/or sell copies of the Data Files or Software, and + to permit persons to whom the Data Files or Software are furnished + to do so, provided that (a) the above copyright notice(s) and this + permission notice appear with all copies of the Data Files or + Software, (b) both the above copyright notice(s) and this + permission notice appear in associated documentation, and (c) + there is clear notice in each modified Data File or in the + Software as well as in the documentation associated with the Data + File(s) or Software that the data or software has been modified. + + THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY + OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE + AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE + COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR + ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR + ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + PERFORMANCE OF THE DATA FILES OR SOFTWARE. + + Except as contained in this notice, the name of a copyright holder + shall not be used in advertising or otherwise to promote the sale, + use or other dealings in these Data Files or Software without + prior written authorization of the copyright holder. + + Unicode and the Unicode logo are trademarks of Unicode, Inc. in + the United States and other countries. All third party trademarks + referenced herein are the property of their respective owners. diff --git a/konsole/README b/konsole/README new file mode 100644 index 00000000..3ae57435 --- /dev/null +++ b/konsole/README @@ -0,0 +1,44 @@ +Konsole - KDE's Terminal Emulator +================================== + +Konsole is a terminal program for KDE. + +As well as being a standalone program, it is also used by other KDE programs +such as the Kate editor and KDevelop development environment to provide easy +access to a terminal window. Konsole's features and usage are explained and +illustrated in the Konsole handbook, which can be accessed by browsing to +"help:/konsole" in Konqueror. + + +Directory Structure +=================== + + /doc/user README files, primarily for advanced users, explaining various + aspects of Konsole such as fonts and keyboard handling + in-depth. + + /doc/developer README files and resources for developers of Konsole. This + includes information on the design of Konsole's internals and + the VT100 terminal on which Konsole's emulation is based. + + /src Source code for Konsole, including the embedded versions of + Konsole which are used in Kate, KDevelop and others. + + /desktop .desktop files for Konsole, used to launch the program + from KDE's various menus and other application launchers. + + /data Data files for use with Konsole as well as the keyboard setup + and color schemes provided with Konsole. + + +Contact +======= + +Up-to-date information about the latest releases can be found on Konsole's +website at http://konsole.kde.org. Discussions about Konsole's development are +held on the konsole-devel mailing list, which can be accessed at +https://mail.kde.org/mailman/listinfo/konsole-devel. + + +Share and enjoy it, + Lars diff --git a/konsole/data/CMakeLists.txt b/konsole/data/CMakeLists.txt new file mode 100644 index 00000000..a229ef55 --- /dev/null +++ b/konsole/data/CMakeLists.txt @@ -0,0 +1,3 @@ +add_subdirectory(color-schemes) +add_subdirectory(keyboard-layouts) +add_subdirectory(profiles) diff --git a/konsole/data/color-schemes/BlackOnLightYellow.colorscheme b/konsole/data/color-schemes/BlackOnLightYellow.colorscheme new file mode 100644 index 00000000..289452df --- /dev/null +++ b/konsole/data/color-schemes/BlackOnLightYellow.colorscheme @@ -0,0 +1,64 @@ +[Background] +Color=255,255,221 + +[BackgroundIntense] +Color=255,255,221 + +[Color0] +Color=0,0,0 + +[Color0Intense] +Color=104,104,104 + +[Color1] +Color=178,24,24 + +[Color1Intense] +Color=255,84,84 + +[Color2] +Color=24,178,24 + +[Color2Intense] +Color=84,255,84 + +[Color3] +Color=178,104,24 + +[Color3Intense] +Color=255,255,84 + +[Color4] +Color=24,24,178 + +[Color4Intense] +Color=84,84,255 + +[Color5] +Color=178,24,178 + +[Color5Intense] +Color=255,84,255 + +[Color6] +Color=24,178,178 + +[Color6Intense] +Color=84,255,255 + +[Color7] +Color=178,178,178 + +[Color7Intense] +Color=255,255,255 + +[Foreground] +Color=0,0,0 + +[ForegroundIntense] +Bold=true +Color=0,0,0 + +[General] +Description=Black on Light Yellow +Opacity=1 diff --git a/konsole/data/color-schemes/BlackOnRandomLight.colorscheme b/konsole/data/color-schemes/BlackOnRandomLight.colorscheme new file mode 100644 index 00000000..8184e29c --- /dev/null +++ b/konsole/data/color-schemes/BlackOnRandomLight.colorscheme @@ -0,0 +1,65 @@ +[Background] +Color=247,247,214 +MaxRandomHue=340 + +[BackgroundIntense] +Color=255,255,221 + +[Color0] +Color=0,0,0 + +[Color0Intense] +Color=104,104,104 + +[Color1] +Color=178,24,24 + +[Color1Intense] +Color=255,84,84 + +[Color2] +Color=24,178,24 + +[Color2Intense] +Color=84,255,84 + +[Color3] +Color=178,104,24 + +[Color3Intense] +Color=255,255,84 + +[Color4] +Color=24,24,178 + +[Color4Intense] +Color=84,84,255 + +[Color5] +Color=178,24,178 + +[Color5Intense] +Color=255,84,255 + +[Color6] +Color=24,178,178 + +[Color6Intense] +Color=84,255,255 + +[Color7] +Color=178,178,178 + +[Color7Intense] +Color=255,255,255 + +[Foreground] +Color=0,0,0 + +[ForegroundIntense] +Bold=true +Color=0,0,0 + +[General] +Description=Black on Random Light +Opacity=1 diff --git a/konsole/data/color-schemes/BlackOnWhite.colorscheme b/konsole/data/color-schemes/BlackOnWhite.colorscheme new file mode 100644 index 00000000..be9ba2f1 --- /dev/null +++ b/konsole/data/color-schemes/BlackOnWhite.colorscheme @@ -0,0 +1,64 @@ +[Background] +Color=255,255,255 + +[BackgroundIntense] +Color=255,255,255 + +[Color0] +Color=0,0,0 + +[Color0Intense] +Color=104,104,104 + +[Color1] +Color=178,24,24 + +[Color1Intense] +Color=255,84,84 + +[Color2] +Color=24,178,24 + +[Color2Intense] +Color=84,255,84 + +[Color3] +Color=178,104,24 + +[Color3Intense] +Color=255,255,84 + +[Color4] +Color=24,24,178 + +[Color4Intense] +Color=84,84,255 + +[Color5] +Color=178,24,178 + +[Color5Intense] +Color=255,84,255 + +[Color6] +Color=24,178,178 + +[Color6Intense] +Color=84,255,255 + +[Color7] +Color=178,178,178 + +[Color7Intense] +Color=255,255,255 + +[Foreground] +Color=0,0,0 + +[ForegroundIntense] +Bold=true +Color=0,0,0 + +[General] +Description=Black on White +Opacity=1 diff --git a/konsole/data/color-schemes/BlueOnBlack.colorscheme b/konsole/data/color-schemes/BlueOnBlack.colorscheme new file mode 100644 index 00000000..f72da32d --- /dev/null +++ b/konsole/data/color-schemes/BlueOnBlack.colorscheme @@ -0,0 +1,64 @@ +[Background] +Color=0,0,0 + +[BackgroundIntense] +Color=0,0,0 + +[Color0] +Color=0,0,0 + +[Color0Intense] +Color=104,104,104 + +[Color1] +Color=250,0,0 + +[Color1Intense] +Color=75,93,255 + +[Color2] +Color=24,178,24 + +[Color2Intense] +Color=84,255,84 + +[Color3] +Color=178,104,24 + +[Color3Intense] +Color=255,255,84 + +[Color4] +Color=125,152,35 + +[Color4Intense] +Color=84,84,255 + +[Color5] +Color=225,30,225 + +[Color5Intense] +Color=255,84,255 + +[Color6] +Color=0,134,223 + +[Color6Intense] +Color=0,68,255 + +[Color7] +Color=255,255,255 + +[Color7Intense] +Color=50,50,50 + +[Foreground] +Color=0,119,255 + +[ForegroundIntense] +Bold=true +Color=23,74,240 + +[General] +Description=Blue on Black +Opacity=1 diff --git a/konsole/data/color-schemes/CMakeLists.txt b/konsole/data/color-schemes/CMakeLists.txt new file mode 100644 index 00000000..fbf52576 --- /dev/null +++ b/konsole/data/color-schemes/CMakeLists.txt @@ -0,0 +1,19 @@ +# Note some color schemes are deliberately not installed +# at present + +install( + FILES + BlackOnLightYellow.colorscheme + BlackOnRandomLight.colorscheme + BlackOnWhite.colorscheme + BlueOnBlack.colorscheme + DarkPastels.colorscheme + GreenOnBlack.colorscheme + Linux.colorscheme + Oxygen.colorscheme + RedOnBlack.colorscheme + Solarized.colorscheme + SolarizedLight.colorscheme + WhiteOnBlack.colorscheme + DESTINATION ${KDE4_DATA_INSTALL_DIR}/konsole +) diff --git a/konsole/data/color-schemes/DarkPastels.colorscheme b/konsole/data/color-schemes/DarkPastels.colorscheme new file mode 100644 index 00000000..110f72bc --- /dev/null +++ b/konsole/data/color-schemes/DarkPastels.colorscheme @@ -0,0 +1,73 @@ +[Background] +Color=44,44,44 + +[BackgroundIntense] +Bold=true +Color=44,44,44 + +[Color0] +Color=63,63,63 + +[Color0Intense] +Bold=true +Color=112,144,128 + +[Color1] +Color=112,80,80 + +[Color1Intense] +Bold=true +Color=220,163,163 + +[Color2] +Color=96,180,138 + +[Color2Intense] +Bold=true +Color=114,213,163 + +[Color3] +Color=223,175,143 + +[Color3Intense] +Bold=true +Color=240,223,175 + +[Color4] +Color=154,184,215 + +[Color4Intense] +Bold=true +Color=148,191,243 + +[Color5] +Color=220,140,195 + +[Color5Intense] +Bold=true +Color=236,147,211 + +[Color6] +Color=140,208,211 + +[Color6Intense] +Bold=true +Color=147,224,227 + +[Color7] +Color=220,220,204 + +[Color7Intense] +Bold=true +Color=255,255,255 + +[Foreground] +Color=220,220,204 + +[ForegroundIntense] +Bold=true +Color=220,220,204 + +[General] +Description=Dark Pastels +Opacity=1 diff --git a/konsole/data/color-schemes/GreenOnBlack.colorscheme b/konsole/data/color-schemes/GreenOnBlack.colorscheme new file mode 100644 index 00000000..df0f3b30 --- /dev/null +++ b/konsole/data/color-schemes/GreenOnBlack.colorscheme @@ -0,0 +1,64 @@ +[Background] +Color=0,0,0 + +[BackgroundIntense] +Color=0,0,0 + +[Color0] +Color=0,0,0 + +[Color0Intense] +Color=104,104,104 + +[Color1] +Color=250,75,75 + +[Color1Intense] +Color=255,84,84 + +[Color2] +Color=24,178,24 + +[Color2Intense] +Color=84,255,84 + +[Color3] +Color=178,104,24 + +[Color3Intense] +Color=255,255,84 + +[Color4] +Color=24,24,178 + +[Color4Intense] +Color=84,84,255 + +[Color5] +Color=225,30,225 + +[Color5Intense] +Color=255,84,255 + +[Color6] +Color=24,178,178 + +[Color6Intense] +Color=84,255,255 + +[Color7] +Color=178,178,178 + +[Color7Intense] +Color=255,255,255 + +[Foreground] +Color=24,240,24 + +[ForegroundIntense] +Bold=true +Color=24,240,24 + +[General] +Description=Green on Black +Opacity=1 diff --git a/konsole/data/color-schemes/Linux.colorscheme b/konsole/data/color-schemes/Linux.colorscheme new file mode 100644 index 00000000..3cb03811 --- /dev/null +++ b/konsole/data/color-schemes/Linux.colorscheme @@ -0,0 +1,62 @@ +[Background] +Color=0,0,0 + +[BackgroundIntense] +Color=104,104,104 + +[Color0] +Color=0,0,0 + +[Color0Intense] +Color=104,104,104 + +[Color1] +Color=178,24,24 + +[Color1Intense] +Color=255,84,84 + +[Color2] +Color=24,178,24 + +[Color2Intense] +Color=84,255,84 + +[Color3] +Color=178,104,24 + +[Color3Intense] +Color=255,255,84 + +[Color4] +Color=24,24,178 + +[Color4Intense] +Color=84,84,255 + +[Color5] +Color=178,24,178 + +[Color5Intense] +Color=255,84,255 + +[Color6] +Color=24,178,178 + +[Color6Intense] +Color=84,255,255 + +[Color7] +Color=178,178,178 + +[Color7Intense] +Color=255,255,255 + +[Foreground] +Color=178,178,178 + +[ForegroundIntense] +Color=255,255,255 + +[General] +Description=Linux Colors diff --git a/konsole/data/color-schemes/Oxygen.colorscheme b/konsole/data/color-schemes/Oxygen.colorscheme new file mode 100644 index 00000000..46fa95a9 --- /dev/null +++ b/konsole/data/color-schemes/Oxygen.colorscheme @@ -0,0 +1,103 @@ +[Background] +Bold=false +Color=46,52,54 +Transparency=false + +[BackgroundIntense] +Bold=false +Color=119,121,121 +Transparency=false + +[Color0] +Bold=false +Color=46,52,54 +Transparency=false + +[Color0Intense] +Bold=false +Color=119,121,121 +Transparency=false + +[Color1] +Bold=false +Color=226,8,0 +Transparency=false + +[Color1Intense] +Bold=false +Color=191,3,3 +Transparency=false + +[Color2] +Bold=false +Color=0,191,0 +Transparency=false + +[Color2Intense] +Bold=false +Color=55,164,43 +Transparency=false + +[Color3] +Bold=false +Color=243,195,0 +Transparency=false + +[Color3Intense] +Bold=false +Color=255,213,0 +Transparency=false + +[Color4] +Bold=false +Color=43,118,199 +Transparency=false + +[Color4Intense] +Bold=false +Color=43,116,199 +Transparency=false + +[Color5] +Bold=false +Color=232,82,144 +Transparency=false + +[Color5Intense] +Bold=false +Color=255,128,224 +Transparency=false + +[Color6] +Bold=false +Color=23,178,178 +Transparency=false + +[Color6Intense] +Bold=false +Color=84,255,255 +Transparency=false + +[Color7] +Bold=false +Color=211,215,207 +Transparency=false + +[Color7Intense] +Bold=false +Color=243,243,243 +Transparency=false + +[Foreground] +Bold=false +Color=178,178,178 +Transparency=false + +[ForegroundIntense] +Bold=false +Color=255,255,255 +Transparency=false + +[General] +Description=Oxygen +Opacity=1 diff --git a/konsole/data/color-schemes/RedOnBlack.colorscheme b/konsole/data/color-schemes/RedOnBlack.colorscheme new file mode 100644 index 00000000..207f892d --- /dev/null +++ b/konsole/data/color-schemes/RedOnBlack.colorscheme @@ -0,0 +1,64 @@ +[Background] +Color=0,0,0 + +[BackgroundIntense] +Color=0,0,0 + +[Color0] +Color=0,0,0 + +[Color0Intense] +Color=104,104,104 + +[Color1] +Color=250,142,8 + +[Color1Intense] +Color=255,84,84 + +[Color2] +Color=24,178,24 + +[Color2Intense] +Color=84,255,84 + +[Color3] +Color=178,104,24 + +[Color3Intense] +Color=255,255,84 + +[Color4] +Color=30,71,152 + +[Color4Intense] +Color=84,84,255 + +[Color5] +Color=225,30,225 + +[Color5Intense] +Color=255,84,255 + +[Color6] +Color=0,134,223 + +[Color6Intense] +Color=255,0,4 + +[Color7] +Color=255,255,255 + +[Color7Intense] +Color=50,50,50 + +[Foreground] +Color=255,0,0 + +[ForegroundIntense] +Bold=true +Color=24,240,24 + +[General] +Description=Red on Black +Opacity=1 diff --git a/konsole/data/color-schemes/Solarized.colorscheme b/konsole/data/color-schemes/Solarized.colorscheme new file mode 100644 index 00000000..e3f0aee0 --- /dev/null +++ b/konsole/data/color-schemes/Solarized.colorscheme @@ -0,0 +1,63 @@ +[Color0] +Color=7,54,66 + +[Color0Intense] +Color=0,43,54 + +[Color1] +Color=220,50,47 + +[Color1Intense] +Color=203,75,22 + +[Color2] +Color=133,153,0 + +[Color2Intense] +Color=88,110,117 + +[Color3] +Color=181,137,0 + +[Color3Intense] +Color=101,123,131 + +[Color4] +Color=38,139,210 + +[Color4Intense] +Color=131,148,150 + +[Color5] +Color=211,54,130 + +[Color5Intense] +Color=108,113,196 + +[Color6] +Color=42,161,152 + +[Color6Intense] +Color=147,161,161 + +[Color7] +Color=238,232,213 + +[Color7Intense] +Color=253,246,227 + +[Background] +Color=0,43,54 + +[BackgroundIntense] +Color=7,54,66 + +[Foreground] +Color=131,148,150 + +[ForegroundIntense] +Color=147,161,161 + +[General] +Description=Solarized +Opacity=1 diff --git a/konsole/data/color-schemes/SolarizedLight.colorscheme b/konsole/data/color-schemes/SolarizedLight.colorscheme new file mode 100644 index 00000000..ea064f4e --- /dev/null +++ b/konsole/data/color-schemes/SolarizedLight.colorscheme @@ -0,0 +1,63 @@ +[Color0] +Color=7,54,66 + +[Color0Intense] +Color=0,43,54 + +[Color1] +Color=220,50,47 + +[Color1Intense] +Color=203,75,22 + +[Color2] +Color=133,153,0 + +[Color2Intense] +Color=88,110,117 + +[Color3] +Color=181,137,0 + +[Color3Intense] +Color=101,123,131 + +[Color4] +Color=38,139,210 + +[Color4Intense] +Color=131,148,150 + +[Color5] +Color=211,54,130 + +[Color5Intense] +Color=108,113,196 + +[Color6] +Color=42,161,152 + +[Color6Intense] +Color=147,161,161 + +[Color7] +Color=238,232,213 + +[Color7Intense] +Color=253,246,227 + +[Background] +Color=253,246,227 + +[BackgroundIntense] +Color=238,232,213 + +[Foreground] +Color=101,123,131 + +[ForegroundIntense] +Color=88,110,117 + +[General] +Description=Solarized Light +Opacity=1 diff --git a/konsole/data/color-schemes/WhiteOnBlack.colorscheme b/konsole/data/color-schemes/WhiteOnBlack.colorscheme new file mode 100644 index 00000000..7307c5d4 --- /dev/null +++ b/konsole/data/color-schemes/WhiteOnBlack.colorscheme @@ -0,0 +1,64 @@ +[Background] +Color=0,0,0 + +[BackgroundIntense] +Color=0,0,0 + +[Color0] +Color=0,0,0 + +[Color0Intense] +Color=104,104,104 + +[Color1] +Color=178,24,24 + +[Color1Intense] +Color=255,84,84 + +[Color2] +Color=24,178,24 + +[Color2Intense] +Color=84,255,84 + +[Color3] +Color=178,104,24 + +[Color3Intense] +Color=255,255,84 + +[Color4] +Color=24,24,178 + +[Color4Intense] +Color=84,84,255 + +[Color5] +Color=178,24,178 + +[Color5Intense] +Color=255,84,255 + +[Color6] +Color=24,178,178 + +[Color6Intense] +Color=84,255,255 + +[Color7] +Color=178,178,178 + +[Color7Intense] +Color=255,255,255 + +[Foreground] +Color=255,255,255 + +[ForegroundIntense] +Bold=true +Color=255,255,255 + +[General] +Description=White on Black +Opacity=1 diff --git a/konsole/data/keyboard-layouts/CMakeLists.txt b/konsole/data/keyboard-layouts/CMakeLists.txt new file mode 100644 index 00000000..f6dade4d --- /dev/null +++ b/konsole/data/keyboard-layouts/CMakeLists.txt @@ -0,0 +1,11 @@ +install( + FILES + default.keytab + linux.keytab + # DON'T INSTALL VT420 KEYBOARD LAYOUT - IT DOESN'T WORK WITH + # ACTUAL VT420 SYSTEMS - EITHER RENAME IT OR IMPROVE KONSOLE + # vt420pc.keytab + solaris.keytab + README.KeyTab + DESTINATION ${KDE4_DATA_INSTALL_DIR}/konsole +) diff --git a/konsole/data/keyboard-layouts/README.KeyTab b/konsole/data/keyboard-layouts/README.KeyTab new file mode 100644 index 00000000..90112d2c --- /dev/null +++ b/konsole/data/keyboard-layouts/README.KeyTab @@ -0,0 +1,73 @@ +[README.KeyTab] + +The keytabs offered in the Options/Keyboard menu +are taken from configurations files with a *.keytab +pattern either located in $KDEDIR/share/apps/konsole +or ~/.kde/share/apps/konsole. + +Keytabs allow to configure the behavior of konsole +on keyboard events, especially for functions keys. +Please have a look into the README.keyboard file, too. + +The syntax is that each entry has the form : + + "key" Keyname { ("+"|"-") Modename } ":" (String|Operation) + +Keynames are those defined in with the +"Qt::Key_" prefix removed. + +Mode names are: + + - Shift : Shift Key pressed + - Alt : Alt Key pressed + - Control : Control Key pressed + + ( The VT100 emulation has modes that can affect the + sequences emitted by certain keys. These modes are + under control of the client program. + + - Newline : effects Return and Enter key. + - Application : effects Up and Down key. + - Ansi : effects Up and Down key (This is for VT52, really). + + Since sending a state to a program that has set the state + itself is positivly wrong and obsolete design, better forget + about this nasty detail. I may well remove this "feature" + in a future clean up round. ) + + A "+" preceeding a Modename means the Key is pressed. + A "-" preceeding a Modename means the Key is not pressed. + If no mode is given it means don't care. + + Note that the combination of Key and Modes (set/reset) + has to be unique. This means, that + + key A + Shift : "A" + key A : "a" + + will not accept the small letter "a" rule as expected, + one has to add a "- Shift" to the last clause. Use + the stdout/stderr dianostics of konsole when modifying + keytabs to find problems like this. + +Operations are + +- scrollUpLine : scroll up one line in the history log +- scrollUpPage : scroll up one page in the history log +- scrollDownLine : scroll down one line in the history log +- scrollDownPage : scroll down one page in the history log +- scrollUpToTop : scroll up to the begining of the history log +- scrollDownToBottom : scroll down to the end of the history log + + +Strings have the syntax of C strings, +one may use the following escapes: + + - \E - escape + - \\ - backslash + - \" - double quote + - \t - tab + - \r - return + - \n - newline + - \b - backspace + - \xHH - where HH are two hex digits diff --git a/konsole/data/keyboard-layouts/default.keytab b/konsole/data/keyboard-layouts/default.keytab new file mode 100644 index 00000000..c52ebd6e --- /dev/null +++ b/konsole/data/keyboard-layouts/default.keytab @@ -0,0 +1,175 @@ +# [README.default.Keytab] Default Keyboard Table +# +# To customize your keyboard, copy this file to something +# ending with .keytab and change it to meet you needs. +# Please read the README.KeyTab and the README.keyboard +# in this case. +# +# -------------------------------------------------------------- + +keyboard "Default (XFree 4)" + +# -------------------------------------------------------------- +# +# Note that this particular table is a "risc" version made to +# ease customization without bothering with obsolete details. +# See VT100.keytab for the more hairy stuff. +# +# -------------------------------------------------------------- + +# common keys + +key Escape : "\E" + +key Tab -Shift : "\t" +key Tab +Shift+Ansi : "\E[Z" +key Tab +Shift-Ansi : "\t" +key Backtab +Ansi : "\E[Z" +key Backtab -Ansi : "\t" + +key Return-Shift-NewLine : "\r" +key Return-Shift+NewLine : "\r\n" + +key Return+Shift : "\EOM" + +# Backspace and Delete codes are preserving CTRL-H. + +key Backspace : "\x7f" + +# Arrow keys in VT52 mode +# shift up/down are reserved for scrolling. +# shift left/right are reserved for switching between tabs (this is hardcoded). + +key Up -Shift-Ansi : "\EA" +key Down -Shift-Ansi : "\EB" +key Right-Shift-Ansi : "\EC" +key Left -Shift-Ansi : "\ED" + +# Arrow keys in ANSI mode with Application - and Normal Cursor Mode) + +key Up -Shift-AnyMod+Ansi+AppCuKeys : "\EOA" +key Down -Shift-AnyMod+Ansi+AppCuKeys : "\EOB" +key Right -Shift-AnyMod+Ansi+AppCuKeys : "\EOC" +key Left -Shift-AnyMod+Ansi+AppCuKeys : "\EOD" + +key Up -Shift-AnyMod+Ansi-AppCuKeys : "\E[A" +key Down -Shift-AnyMod+Ansi-AppCuKeys : "\E[B" +key Right -Shift-AnyMod+Ansi-AppCuKeys : "\E[C" +key Left -Shift-AnyMod+Ansi-AppCuKeys : "\E[D" + +key Up -Shift+AnyMod+Ansi : "\E[1;*A" +key Down -Shift+AnyMod+Ansi : "\E[1;*B" +key Right -Shift+AnyMod+Ansi : "\E[1;*C" +key Left -Shift+AnyMod+Ansi : "\E[1;*D" + +key Up +Shift+AppScreen : "\E[1;*A" +key Down +Shift+AppScreen : "\E[1;*B" +key Left +Shift+AppScreen : "\E[1;*D" +key Right +Shift+AppScreen : "\E[1;*C" + +# Keypad keys with NumLock ON +# (see "Numeric Keypad" section at http://www.nw.com/nw/WWW/products/wizcon/vt100.html ) +# +# Not enabled for now because it breaks the keypad in Vim. +# +#key 0 +KeyPad+AppKeyPad : "\EOp" +#key 1 +KeyPad+AppKeyPad : "\EOq" +#key 2 +KeyPad+AppKeyPad : "\EOr" +#key 3 +KeyPad+AppKeyPad : "\EOs" +#key 4 +KeyPad+AppKeyPad : "\EOt" +#key 5 +KeyPad+AppKeyPad : "\EOu" +#key 6 +KeyPad+AppKeyPad : "\EOv" +#key 7 +KeyPad+AppKeyPad : "\EOw" +#key 8 +KeyPad+AppKeyPad : "\EOx" +#key 9 +KeyPad+AppKeyPad : "\EOy" +#key + +KeyPad+AppKeyPad : "\EOl" +#key - +KeyPad+AppKeyPad : "\EOm" +#key . +KeyPad+AppKeyPad : "\EOn" +#key * +KeyPad+AppKeyPad : "\EOM" +#key Enter +KeyPad+AppKeyPad : "\r" + +# Keypad keys with NumLock Off +key Up -Shift+Ansi+AppCuKeys+KeyPad : "\EOA" +key Down -Shift+Ansi+AppCuKeys+KeyPad : "\EOB" +key Right -Shift+Ansi+AppCuKeys+KeyPad : "\EOC" +key Left -Shift+Ansi+AppCuKeys+KeyPad : "\EOD" + +key Up -Shift+Ansi-AppCuKeys+KeyPad : "\E[A" +key Down -Shift+Ansi-AppCuKeys+KeyPad : "\E[B" +key Right -Shift+Ansi-AppCuKeys+KeyPad : "\E[C" +key Left -Shift+Ansi-AppCuKeys+KeyPad : "\E[D" + +key Home +AppCuKeys+KeyPad : "\EOH" +key End +AppCuKeys+KeyPad : "\EOF" +key Home -AppCuKeys+KeyPad : "\E[H" +key End -AppCuKeys+KeyPad : "\E[F" + +key Insert +KeyPad : "\E[2~" +key Delete +KeyPad : "\E[3~" +key PgUp -Shift+KeyPad : "\E[5~" +key PgDown -Shift+KeyPad : "\E[6~" + +# other grey PC keys + +key Enter+NewLine : "\r\n" +key Enter-NewLine : "\r" + +key Home -AnyMod-AppCuKeys : "\E[H" +key End -AnyMod-AppCuKeys : "\E[F" +key Home -AnyMod+AppCuKeys : "\EOH" +key End -AnyMod+AppCuKeys : "\EOF" +key Home +AnyMod : "\E[1;*H" +key End +AnyMod : "\E[1;*F" + +key Insert -AnyMod : "\E[2~" +key Delete -AnyMod : "\E[3~" +key Insert +AnyMod : "\E[2;*~" +key Delete +AnyMod : "\E[3;*~" + +key PgUp -Shift-AnyMod : "\E[5~" +key PgDown -Shift-AnyMod : "\E[6~" +key PgUp -Shift+AnyMod : "\E[5;*~" +key PgDown -Shift+AnyMod : "\E[6;*~" + +# Function keys +key F1 -AnyMod : "\EOP" +key F2 -AnyMod : "\EOQ" +key F3 -AnyMod : "\EOR" +key F4 -AnyMod : "\EOS" +key F5 -AnyMod : "\E[15~" +key F6 -AnyMod : "\E[17~" +key F7 -AnyMod : "\E[18~" +key F8 -AnyMod : "\E[19~" +key F9 -AnyMod : "\E[20~" +key F10 -AnyMod : "\E[21~" +key F11 -AnyMod : "\E[23~" +key F12 -AnyMod : "\E[24~" + +key F1 +AnyMod : "\EO*P" +key F2 +AnyMod : "\EO*Q" +key F3 +AnyMod : "\EO*R" +key F4 +AnyMod : "\EO*S" +key F5 +AnyMod : "\E[15;*~" +key F6 +AnyMod : "\E[17;*~" +key F7 +AnyMod : "\E[18;*~" +key F8 +AnyMod : "\E[19;*~" +key F9 +AnyMod : "\E[20;*~" +key F10 +AnyMod : "\E[21;*~" +key F11 +AnyMod : "\E[23;*~" +key F12 +AnyMod : "\E[24;*~" + +# Work around dead keys + +key Space +Control : "\x00" + +# Some keys are used by konsole to cause operations. +# The scroll* operations refer to the history buffer. + +key Up +Shift-AppScreen : scrollLineUp +key PgUp +Shift-AppScreen : scrollPageUp +key Home +Shift-AppScreen : scrollUpToTop +key Down +Shift-AppScreen : scrollLineDown +key PgDown +Shift-AppScreen : scrollPageDown +key End +Shift-AppScreen : scrollDownToBottom + + diff --git a/konsole/data/keyboard-layouts/historic/vt100.keytab b/konsole/data/keyboard-layouts/historic/vt100.keytab new file mode 100644 index 00000000..562cd45a --- /dev/null +++ b/konsole/data/keyboard-layouts/historic/vt100.keytab @@ -0,0 +1,133 @@ +# [vt100.keytab] Konsole Keyboard Table (VT100 keys) +# +# -------------------------------------------------------------- + +keyboard "vt100 (historical)" + +# -------------------------------------------------------------- +# +# This configuration table allows to customize the +# meaning of the keys. +# +# The syntax is that each entry has the form : +# +# "key" Keyname { ("+"|"-") Modename } ":" (String|Operation) +# +# Keynames are those defined in with the +# "Qt::Key_" removed. (We'd better insert the list here) +# +# Mode names are : +# +# - Shift +# - Alt +# - Control +# +# The VT100 emulation has two modes that can affect the +# sequences emitted by certain keys. These modes are +# under control of the client program. +# +# - Newline : effects Return and Enter key. +# - Application : effects Up and Down key. +# +# - Ansi : effects Up and Down key (This is for VT52, really). +# +# Operations are +# +# - scrollUpLine +# - scrollUpPage +# - scrollDownLine +# - scrollDownPage +# +# - emitSelection +# +# If the key is not found here, the text of the +# key event as provided by QT is emitted, possibly +# preceeded by ESC if the Alt key is pressed. +# +# -------------------------------------------------------------- + +key Escape : "\E" +key Tab : "\t" + +# VT100 can add an extra \n after return. +# The NewLine mode is set by an escape sequence. + +key Return-NewLine : "\r" +key Return+NewLine : "\r\n" + +# Some desperately try to save the ^H. + +key Backspace : "\x7f" +key Delete : "\E[3~" + +# These codes are for the VT52 mode of VT100 +# The Ansi mode (i.e. VT100 mode) is set by +# an escape sequence + +key Up -Shift-Ansi : "\EA" +key Down -Shift-Ansi : "\EB" +key Right-Shift-Ansi : "\EC" +key Left -Shift-Ansi : "\ED" + +# VT100 emits a mode bit together +# with the arrow keys.The AppCuKeys +# mode is set by an escape sequence. + +key Up -Shift+Ansi+AppCuKeys : "\EOA" +key Down -Shift+Ansi+AppCuKeys : "\EOB" +key Right-Shift+Ansi+AppCuKeys : "\EOC" +key Left -Shift+Ansi+AppCuKeys : "\EOD" + +key Up -Shift+Ansi-AppCuKeys : "\E[A" +key Down -Shift+Ansi-AppCuKeys : "\E[B" +key Right-Shift+Ansi-AppCuKeys : "\E[C" +key Left -Shift+Ansi-AppCuKeys : "\E[D" + +# function keys (FIXME: make pf1-pf4) + +key F1 : "\E[11~" +key F2 : "\E[12~" +key F3 : "\E[13~" +key F4 : "\E[14~" +key F5 : "\E[15~" + +key F6 : "\E[17~" +key F7 : "\E[18~" +key F8 : "\E[19~" +key F9 : "\E[20~" +key F10 : "\E[21~" +key F11 : "\E[23~" +key F12 : "\E[24~" + +key Home : "\E[H" +key End : "\E[F" + +key PgUp -Shift : "\E[5~" +key PgDown -Shift : "\E[6~" +key Insert-Shift : "\E[2~" + +# Keypad-Enter. See comment on Return above. + +key Enter+NewLine : "\r\n" +key Enter-NewLine : "\r" + +key Space +Control : "\x00" + +# some of keys are used by konsole. + +key Up +Shift : scrollLineUp +key PgUp +Shift : scrollPageUp +key Down +Shift : scrollLineDown +key PgDown +Shift : scrollPageDown + +key ScrollLock : scrollLock + + +#---------------------------------------------------------- + +# keypad characters as offered by Qt +# cannot be recognized as such. + +#---------------------------------------------------------- + +# Following other strings as emitted by konsole. diff --git a/konsole/data/keyboard-layouts/historic/x11r5.keytab b/konsole/data/keyboard-layouts/historic/x11r5.keytab new file mode 100644 index 00000000..cbb87ec1 --- /dev/null +++ b/konsole/data/keyboard-layouts/historic/x11r5.keytab @@ -0,0 +1,71 @@ +# [x11r5.Keytab] Keyboard Table for X11 R5 + +keyboard "XTerm (XFree 3.x.x)" + +# -------------------------------------------------------------- +# +# Note that this particular table is a "risc" version made to +# ease customization without bothering with obsolete details. +# See VT100.keytab for the more hairy stuff. +# +# -------------------------------------------------------------- + +# common keys + +key Escape : "\E" +key Tab : "\t" + +key Return : "\r" + +# Backspace and Delete codes are preserving CTRL-H. + +key Backspace : "\x7f" + +# cursor keys + +key Up -Shift : "\EOA" +key Down -Shift : "\EOB" +key Right -Shift : "\EOC" +key Left -Shift : "\EOD" + +# other grey PC keys + +key Enter : "\r" + +key Home : "\E[1~" +key Insert-Shift : "\E[2~" +key Delete : "\E[3~" +key End : "\E[4~" +key PgUp -Shift : "\E[5~" +key PgDown -Shift : "\E[6~" + +# function keys + +key F1 : "\E[11~" +key F2 : "\E[12~" +key F3 : "\E[13~" +key F4 : "\E[14~" +key F5 : "\E[15~" +key F6 : "\E[17~" +key F7 : "\E[18~" +key F8 : "\E[19~" +key F9 : "\E[20~" +key F10 : "\E[21~" +key F11 : "\E[23~" +key F12 : "\E[24~" + +# Work around dead keys + +key Space +Control : "\x00" + +# Some keys are used by konsole to cause operations. +# The scroll* operations refer to the history buffer. + +key Up +Shift : scrollLineUp +key PgUp +Shift : scrollPageUp +key Down +Shift : scrollLineDown +key PgDown +Shift : scrollPageDown + +key ScrollLock : scrollLock + +# keypad characters are not offered differently by Qt. diff --git a/konsole/data/keyboard-layouts/linux.keytab b/konsole/data/keyboard-layouts/linux.keytab new file mode 100644 index 00000000..c93c3009 --- /dev/null +++ b/konsole/data/keyboard-layouts/linux.keytab @@ -0,0 +1,132 @@ +# [linux.keytab] Konsole Keyboard Table (Linux console keys) +# +# -------------------------------------------------------------- + +# NOT TESTED, MAY NEED SOME CLEANUPS +keyboard "Linux console" + +# -------------------------------------------------------------- +# +# This configuration table allows to customize the +# meaning of the keys. +# +# The syntax is that each entry has the form : +# +# "key" Keyname { ("+"|"-") Modename } ":" (String|Operation) +# +# Keynames are those defined in with the +# "Qt::Key_" removed. (We'd better insert the list here) +# +# Mode names are : +# +# - Shift +# - Alt +# - Control +# +# The VT100 emulation has two modes that can affect the +# sequences emitted by certain keys. These modes are +# under control of the client program. +# +# - Newline : effects Return and Enter key. +# - Application : effects Up and Down key. +# +# - Ansi : effects Up and Down key (This is for VT52, really). +# +# Operations are +# +# - scrollUpLine +# - scrollUpPage +# - scrollDownLine +# - scrollDownPage +# +# - emitSelection +# +# If the key is not found here, the text of the +# key event as provided by QT is emitted, possibly +# preceeded by ESC if the Alt key is pressed. +# +# -------------------------------------------------------------- + +key Escape : "\E" +key Tab : "\t" + +# VT100 can add an extra \n after return. +# The NewLine mode is set by an escape sequence. + +key Return-NewLine : "\r" +key Return+NewLine : "\r\n" + +# Some desperately try to save the ^H. + +key Backspace : "\x7f" +key Delete : "\E[3~" + +# These codes are for the VT52 mode of VT100 +# The Ansi mode (i.e. VT100 mode) is set by +# an escape sequence + +key Up -Shift-Ansi : "\EA" +key Down -Shift-Ansi : "\EB" +key Right-Shift-Ansi : "\EC" +key Left -Shift-Ansi : "\ED" + +# VT100 emits a mode bit together +# with the arrow keys.The AppCuKeys +# mode is set by an escape sequence. + +key Up -Shift+Ansi+AppCuKeys : "\EOA" +key Down -Shift+Ansi+AppCuKeys : "\EOB" +key Right-Shift+Ansi+AppCuKeys : "\EOC" +key Left -Shift+Ansi+AppCuKeys : "\EOD" + +key Up -Shift+Ansi-AppCuKeys : "\E[A" +key Down -Shift+Ansi-AppCuKeys : "\E[B" +key Right-Shift+Ansi-AppCuKeys : "\E[C" +key Left -Shift+Ansi-AppCuKeys : "\E[D" + +# linux functions keys F1-F5 differ from xterm + +key F1 : "\E[[A" +key F2 : "\E[[B" +key F3 : "\E[[C" +key F4 : "\E[[D" +key F5 : "\E[[E" + +key F6 : "\E[17~" +key F7 : "\E[18~" +key F8 : "\E[19~" +key F9 : "\E[20~" +key F10 : "\E[21~" +key F11 : "\E[23~" +key F12 : "\E[24~" + +key Home : "\E[1~" +key End : "\E[4~" + +key PgUp -Shift : "\E[5~" +key PgDown -Shift : "\E[6~" +key Insert-Shift : "\E[2~" + +# Keypad-Enter. See comment on Return above. + +key Enter+NewLine : "\r\n" +key Enter-NewLine : "\r" + +key Space +Control : "\x00" + +# some of keys are used by konsole. + +key Up +Shift : scrollLineUp +key PgUp +Shift : scrollPageUp +key Down +Shift : scrollLineDown +key PgDown +Shift : scrollPageDown + + +#---------------------------------------------------------- + +# keypad characters as offered by Qt +# cannot be recognized as such. + +#---------------------------------------------------------- + +# Following other strings as emitted by konsole. diff --git a/konsole/data/keyboard-layouts/solaris.keytab b/konsole/data/keyboard-layouts/solaris.keytab new file mode 100644 index 00000000..fe77e85b --- /dev/null +++ b/konsole/data/keyboard-layouts/solaris.keytab @@ -0,0 +1,108 @@ +# [solaris.keytab] Konsole Keyboard Table +# + +keyboard "Solaris console" + +# -------------------------------------------------------------- +# +# This configuration table allows to customize the +# meaning of the keys. +# +# The syntax is that each entry has the form : +# +# "key" Keyname { ("+"|"-") Modename } ":" (String|Operation) +# +# Keynames are those defined in with the +# "Qt::Key_" removed. (We'd better insert the list here) +# +# Mode names are : +# +# - Shift +# - Alt +# - Control +# +# The VT100 emulation has two modes that can affect the +# sequences emitted by certain keys. These modes are +# under control of the client program. +# +# +# - Newline : effects Return and Enter key. +# - Application : effects Up and Down key. +# +# - Ansi : effects Up and Down key (This is for VT52, really). +# +# Operations are +# +# - scrollUpLine +# - scrollUpPage +# - scrollDownLine +# - scrollDownPage +# +# - emitSelection +# +# If the key is not found here, the text of the +# key event as provided by QT is emitted, possibly +# preceeded by ESC if the Alt key is pressed. +# +# -------------------------------------------------------------- + +key Escape : "\E" +key Tab : "\t" + +key Return-Alt : "\r" +key Return+Alt : "\E\r" + +# Backspace and Delete codes are preserving CTRL-H. + +key Backspace : "\x08" +#key Delete : "\x7F" + +# cursor keys + +key Up -Shift : "\EOA" +key Down -Shift : "\EOB" +key Right -Shift : "\EOC" +key Left -Shift : "\EOD" + +# other grey PC keys + +key Enter : "\r" + +key Home : "\E[1~" +key Insert-Shift : "\E[2~" +key Delete : "\E[3~" +key End : "\E[4~" +key PgUp -Shift : "\E[5~" +key PgDown -Shift : "\E[6~" + +# function keys + +key F1 : "\E[11~" +key F2 : "\E[12~" +key F3 : "\E[13~" +key F4 : "\E[14~" +key F5 : "\E[15~" +key F6 : "\E[17~" +key F7 : "\E[18~" +key F8 : "\E[19~" +key F9 : "\E[20~" +key F10 : "\E[21~" +key F11 : "\E[23~" +key F12 : "\E[24~" + +# Work around dead keys + +key Space +Control : "\x00" + +# Some keys are used by konsole to cause operations. +# The scroll* operations refer to the history buffer. + +#key Left +Shift : prevSession +#key Right +Shift : nextSession +key Up +Shift : scrollLineUp +key PgUp +Shift : scrollPageUp +key Down +Shift : scrollLineDown +key PgDown +Shift : scrollPageDown +#key Insert+Shift : emitSelection + +# keypad characters are not offered differently by Qt. diff --git a/konsole/data/keyboard-layouts/vt420pc.keytab b/konsole/data/keyboard-layouts/vt420pc.keytab new file mode 100644 index 00000000..f702229e --- /dev/null +++ b/konsole/data/keyboard-layouts/vt420pc.keytab @@ -0,0 +1,167 @@ +# +# NOTE: This keyboard binding is not installed because it +# apparently doesn't work with actual VT420 systems +# (see BUG:170220) +# +# [vt420pc.keytab] Konsole Keyboard Table (VT420pc keys) +# adapted by ferdinand gassauer f.gassauer@aon.at +# Nov 2000 +# +################################################################ +# +# The escape sequences emmited by the +# keys Shift+F1 to Shift+F12 might not fit your needs +# +################# IMPORTANT NOTICE ############################# +# the key bindings (Kcontrol -> look and feel -> keybindgs) +# overrule the settings in this file. The key bindings might be +# changed by the user WITHOUT notification of the maintainer of +# the keytab file. Konsole will not work as expected by +# the maintainer of the keytab file. +################################################################ +# +# -------------------------------------------------------------- + +keyboard "DEC VT420 Terminal" + +# -------------------------------------------------------------- +# +# This configuration table allows to customize the +# meaning of the keys. +# +# The syntax is that each entry has the form : +# +# "key" Keyname { ("+"|"-") Modename } ":" (String|Operation) +# +# Keynames are those defined in with the +# "Qt::Key_" removed. (We'd better insert the list here) +# +# Mode names are : +# +# - Shift +# - Alt +# - Control +# +# The VT100 emulation has two modes that can affect the +# sequences emitted by certain keys. These modes are +# under control of the client program. +# +# - Newline : effects Return and Enter key. +# - Application : effects Up and Down key. +# +# - Ansi : effects Up and Down key (This is for VT52, really). +# +# Operations are +# +# - scrollUpLine +# - scrollUpPage +# - scrollDownLine +# - scrollDownPage +# +# - emitSelection +# +# If the key is not found here, the text of the +# key event as provided by QT is emitted, possibly +# preceeded by ESC if the Alt key is pressed. +# +# -------------------------------------------------------------- + +key Escape : "\E" +key Tab : "\t" +key Backtab: "\E[Z" + +# VT100 can add an extra \n after return. +# The NewLine mode is set by an escape sequence. + +key Return-NewLine : "\r" +key Return+NewLine : "\r\n" + +# Some desperately try to save the ^H. +# may be not everyone wants this + +key Backspace : "\x08" # Control H +key Delete : "\x7f" + +# These codes are for the VT420pc +# The Ansi mode (i.e. VT100 mode) is set by +# an escape sequence + +key Up -Shift-Ansi : "\EA" +key Down -Shift-Ansi : "\EB" +key Right-Shift-Ansi : "\EC" +key Left -Shift-Ansi : "\ED" + +# VT100 emits a mode bit together +# with the arrow keys.The AppCuKeys +# mode is set by an escape sequence. + +key Up -Shift+Ansi+AppCuKeys : "\EOA" +key Down -Shift+Ansi+AppCuKeys : "\EOB" +key Right-Shift+Ansi+AppCuKeys : "\EOC" +key Left -Shift+Ansi+AppCuKeys : "\EOD" + +key Up -Shift+Ansi-AppCuKeys : "\E[A" +key Down -Shift+Ansi-AppCuKeys : "\E[B" +key Right-Shift+Ansi-AppCuKeys : "\E[C" +key Left -Shift+Ansi-AppCuKeys : "\E[D" + +# function keys + +key F1 -Shift : "\E[11~" +key F2 -Shift : "\E[12~" +key F3 -Shift : "\E[13~" +key F4 -Shift : "\E[14~" +key F5 -Shift : "\E[15~" +key F6 -Shift : "\E[17~" +key F7 -Shift : "\E[18~" +key F8 -Shift : "\E[19~" +key F9 -Shift : "\E[20~" +key F10-Shift : "\E[21~" +key F11-Shift : "\E[23~" +key F12-Shift : "\E[24~" +# +# Shift F1-F12 +# +key F1 +Shift : "\E[11;2~" +key F2 +Shift : "\E[12;2~" +key F3 +Shift : "\E[13;2~" +key F4 +Shift : "\E[14;2~" +key F5 +Shift : "\E[15;2~" +key F6 +Shift : "\E[17;2~" +key F7 +Shift : "\E[18;2~" +key F8 +Shift : "\E[19;2~" +key F9 +Shift : "\E[20;2~" +key F10+Shift : "\E[21;2~" +key F11+Shift : "\E[23;2~" +key F12+Shift : "\E[24;2~" + +key Home : "\E[H" +key End : "\E[F" + +key PgUp -Shift : "\E[5~" +key PgDown -Shift : "\E[6~" +key Insert-Shift : "\E[2~" + +# Keypad-Enter. See comment on Return above. + +key Enter+NewLine : "\r\n" +key Enter-NewLine : "\r" + +key Space +Control : "\x00" + +# some of keys are used by konsole. + +key Up +Shift : scrollLineUp +key PgUp +Shift : scrollPageUp +key Down +Shift : scrollLineDown +key PgDown +Shift : scrollPageDown + + +#---------------------------------------------------------- + +# keypad characters as offered by Qt +# cannot be recognized as such. + +#---------------------------------------------------------- + +# Following other strings as emitted by konsole. diff --git a/konsole/data/profiles/CMakeLists.txt b/konsole/data/profiles/CMakeLists.txt new file mode 100644 index 00000000..6c6373f4 --- /dev/null +++ b/konsole/data/profiles/CMakeLists.txt @@ -0,0 +1,4 @@ +install( + FILES Shell.profile + DESTINATION ${KDE4_DATA_INSTALL_DIR}/konsole +) diff --git a/konsole/data/profiles/Shell.profile b/konsole/data/profiles/Shell.profile new file mode 100644 index 00000000..4b5e63f1 --- /dev/null +++ b/konsole/data/profiles/Shell.profile @@ -0,0 +1,172 @@ +[General] +Name=Shell +Name[af]=Tolk +Name[ar]=صَدفة +Name[as]=শ্বেল +Name[ast]=Intérprete d'órdenes +Name[be@latin]=Abałonka +Name[bg]=Обвивка +Name[bn]=শেল +Name[bn_IN]=শেল +Name[br]=Shell +Name[bs]=Školjka +Name[ca]=Intèrpret d'ordres +Name[ca@valencia]=Intèrpret d'ordes +Name[cs]=Shell +Name[csb]=Pòwłoka +Name[cy]=Plisgyn +Name[da]=Skal +Name[de]=Shell +Name[el]=Κέλυφος +Name[en_GB]=Shell +Name[eo]=Ŝelo +Name[es]=Intérprete de órdenes +Name[et]=Shell +Name[eu]=Shell-a +Name[fi]=Komentorivi +Name[fr]=Terminal +Name[fy]=Flues +Name[ga]=Blaosc +Name[gl]=Shell +Name[gu]=શેલ +Name[he]=מעטפת +Name[hi]=शेल +Name[hne]=सेल +Name[hr]=Ljuska +Name[hsb]=terminal (shell) +Name[hu]=Parancsértelmező +Name[ia]=Shell +Name[id]=Shell +Name[is]=Skel +Name[it]=Shell +Name[ja]=シェル +Name[ka]=Shell +Name[kk]=Қоршау-орта +Name[km]=សែល +Name[kn]=ಆದೇಶಗ್ರಾಹಿ (ಶೆಲ್) +Name[ko]=셸 +Name[ku]=Shell +Name[lt]=Apvalkalas +Name[lv]=Čaula +Name[mai]=शेल +Name[mk]=Школка +Name[ml]=ഷെല്‍ +Name[mr]=शेल +Name[ms]=Shell +Name[nb]=Skall +Name[nds]=Befehlsfinster +Name[nl]=Shell +Name[nn]=Skal +Name[oc]=Copèla +Name[or]=Shell +Name[pa]=ਸ਼ੈੱਲ +Name[pl]=Powłoka +Name[pt]=Consola +Name[pt_BR]=Shell +Name[ro]=Interpretor +Name[ru]=Командная оболочка +Name[se]=Skálžu +Name[si]=ශෙල් +Name[sk]=Shell +Name[sl]=Lupina +Name[sr]=Шкољка +Name[sr@ijekavian]=Шкољка +Name[sr@ijekavianlatin]=Školjka +Name[sr@latin]=Školjka +Name[sv]=Skal +Name[ta]=ஓடு +Name[te]=షెల్ +Name[tg]=Shell +Name[th]=เชลล์ +Name[tr]=Kabuk +Name[ug]=Shell +Name[uk]=Оболонка +Name[uz]=Terminal +Name[uz@cyrillic]=Терминал +Name[wa]=Shell +Name[xh]=Iqokobhe +Name[x-test]=xxShellxx +Name[zh_CN]=Shell +Name[zh_TW]=Shell +Comment=Konsole default profile +Comment[af]=Konsole verstekprofiel +Comment[ar]=تشكيلة كونسول الافتراضية +Comment[as]=Konsole ৰ অবিকল্পিত পাৰ্শ্বৰূপ +Comment[ast]=Perfil predetermináu de Konsole +Comment[be@latin]=Zmoŭčany profil prahramy „Konsole” +Comment[bg]=Профил по подразбиране за Konsole +Comment[bn]=কনসোল ডিফল্ট প্রোফাইল +Comment[bn_IN]=Konsole-র ডিফল্ট প্রোফাইল +Comment[bs]=Podrazumijevani profil Konzole +Comment[ca]=Perfil per omissió del Konsole +Comment[ca@valencia]=Perfil per omissió del Konsole +Comment[cs]=Výchozí profil Konsole +Comment[csb]=Domëslny profil Kònsolë +Comment[da]=Standardprofil for Konsole +Comment[de]=Konsole-Standardprofil +Comment[el]=Προκαθορισμένο προφίλ του Konsole +Comment[en_GB]=Konsole default profile +Comment[eo]=Defaŭlta profilo por Konzolo +Comment[es]=Perfil predeterminado de Konsole +Comment[et]=Konsooli vaikimisi profiil +Comment[eu]=Konsole-ren profil lehenetsia +Comment[fi]=Konsolen oletusprofiili +Comment[fr]=Profil par défaut de Konsole +Comment[fy]=Standertprofyl (konsole) +Comment[ga]=Próifíl réamhshocraithe Konsole +Comment[gl]=Perfil predeterminado de Konsole +Comment[gu]=કોન્સોલ મૂળભૂત પ્રોફાઇલ +Comment[he]=פרופיל ברירת־המחדל של Konsole +Comment[hi]=कंसोल डिफ़ॉल्ट प्रोफ़ाइल +Comment[hne]=कंसोल डिफाल्ट प्रोफाइल +Comment[hr]=Zadani profil Konsole +Comment[hsb]=Standardny profil konsole +Comment[hu]=Alapértelmezett Konsole-profil +Comment[ia]=Profilo predefinite de Konsole +Comment[id]=Profil standar Konsole +Comment[is]=Sjálfgefið sniðmát Konsole +Comment[it]=Profilo predefinito di Konsole +Comment[ja]=Konsole の標準プロファイル +Comment[ka]=Konsole-ს ნაგულისხმები პროფილი +Comment[kk]=Konsole-дың әдетті профилі +Comment[km]=ទម្រង់​លំនាំដើម​របស់​កុងសូល +Comment[kn]=ಕನ್ಸೋಲ್ ನ (ಆದೇಶ ಗ್ರಾಹಿ) ಪೂರ್ವನಿಯೋಜಿತ ವ್ಯಕ್ತಿವೈಶಿಷ್ಟ್ಯ (ಪ್ರೊಫೈಲ್) +Comment[ko]=Konsole 기본 프로필 +Comment[ku]=Profîla standard a Konsolê +Comment[lt]=Konsole numatytas profilis +Comment[lv]=Konsole noklusējuma profils +Comment[mai]=कंसोल पूर्वनिर्धारित प्रोफाइल +Comment[mk]=Стандарден профил на Конзола +Comment[ml]=കണ്‍സോളിന്റെ സ്വതേയുള്ള പ്രൊഫൈല്‍ +Comment[mr]=कंसोल मूलभूत प्रोफाईल +Comment[nb]=Konsoles standardprofil +Comment[nds]=Standard-Konsoletörn +Comment[nl]=Standaardprofiel (Konsole) +Comment[nn]=Standardprofil for Konsoll +Comment[or]=କୋନସୋଲ ପୂର୍ବନିର୍ଦ୍ଧାରିତ ସଂକ୍ଷିପ୍ତ ଚିତ୍ର +Comment[pa]=ਕਨਸੋਲ ਡਿਫਾਲਟ ਪਰੋਫਾਇਲ +Comment[pl]=Domyślny profil Konsoli +Comment[pt]=Perfil predefinido do Konsole +Comment[pt_BR]=Perfil padrão do Konsole +Comment[ro]=Profil konsolă implicit +Comment[ru]=Стандартный профиль Konsole +Comment[se]=Konsolla standárdprofiila +Comment[si]=කන්සෝල් පෙරනිමි පැතිකඩ +Comment[sk]=Štandardný profil Konsole +Comment[sl]=Privzeti profil programa Konsole +Comment[sr]=Подразумевани профил Конзоле +Comment[sr@ijekavian]=Подразумијевани профил Конзоле +Comment[sr@ijekavianlatin]=Podrazumijevani profil Konsole +Comment[sr@latin]=Podrazumevani profil Konsole +Comment[sv]=Terminalens standardprofil +Comment[ta]=முனையத்தின் இயல்பிருப்பு பண்பு +Comment[te]=Konsole అప్రమేయ ప్రొఫైల్ +Comment[tg]=Профили консоли стандартӣ +Comment[th]=โพรไฟล์ปริยายของคอนโซล-K +Comment[tr]=Konsole öntanımlı profili +Comment[ug]=Konsole كۆڭۈلدىكى سەپلىمە ھۆججەت +Comment[uk]=Типовий профіль Konsole +Comment[wa]=Prémetou profil pol Konsole +Comment[x-test]=xxKonsole default profilexx +Comment[zh_CN]=Konsole 默认配置 +Comment[zh_TW]=Konsole 預設設定檔 diff --git a/konsole/desktop/CMakeLists.txt b/konsole/desktop/CMakeLists.txt new file mode 100644 index 00000000..d94f5e08 --- /dev/null +++ b/konsole/desktop/CMakeLists.txt @@ -0,0 +1,30 @@ +install( + PROGRAMS konsole.desktop + DESTINATION ${KDE4_XDG_APPS_INSTALL_DIR} +) + +install( + FILES terminalemulator.desktop + DESTINATION ${KDE4_SERVICETYPES_INSTALL_DIR} +) + +install( + FILES konsolepart.desktop + DESTINATION ${KDE4_SERVICES_INSTALL_DIR} +) + +install( + FILES + konsolehere.desktop + konsolerun.desktop + DESTINATION ${KDE4_SERVICES_INSTALL_DIR}/ServiceMenus +) + +install( + FILES + konsole.notifyrc + konsoleui.rc + sessionui.rc + partui.rc + DESTINATION ${KDE4_DATA_INSTALL_DIR}/konsole +) diff --git a/konsole/desktop/konsole.desktop b/konsole/desktop/konsole.desktop new file mode 100755 index 00000000..2226712d --- /dev/null +++ b/konsole/desktop/konsole.desktop @@ -0,0 +1,236 @@ +[Desktop Entry] +Type=Application +TryExec=konsole +Exec=konsole --icon '%i' --caption '%c' +Icon=utilities-terminal +Terminal=false +Categories=Qt;KDE;System;TerminalEmulator; +Actions=NewWindow; +X-DocPath=konsole/index.html +X-DBUS-StartupType=Unique +X-KDE-StartupNotify=true +X-KDE-AuthorizeAction=shell_access + +Name=Konsole +Name[af]=Konsole +Name[ar]=كونسول +Name[as]=Konsole +Name[ast]=Konsole +Name[be]=Konsole +Name[be@latin]=Konsole +Name[bg]=Konsole +Name[bn]=কনসোল +Name[bn_IN]=Konsole +Name[br]=Konsole +Name[bs]=Konzola +Name[ca]=Konsole +Name[ca@valencia]=Konsole +Name[cs]=Konsole +Name[csb]=Kònsola +Name[cy]=Konsole +Name[da]=Konsole +Name[de]=Konsole +Name[el]=Κονσόλα +Name[en_GB]=Konsole +Name[eo]=Konsole +Name[es]=Konsole +Name[et]=Konsool +Name[eu]=Kontsola +Name[fi]=Konsole +Name[fr]=Konsole +Name[fy]=Konsole +Name[ga]=Konsole +Name[gl]=Konsole +Name[gu]=કોન્સોલ +Name[he]=Konsole +Name[hi]=कंसोल +Name[hne]=कंसोल +Name[hr]=Konsole +Name[hsb]=Konsola +Name[hu]=Konsole +Name[ia]=Konsole +Name[id]=Konsole +Name[is]=Konsole +Name[it]=Konsole +Name[ja]=Konsole +Name[ka]=კონსოლი +Name[kk]=Konsole +Name[km]=កុងសូល +Name[kn]=ಕನ್ಸೋಲ್ +Name[ko]=Konsole +Name[ku]=Konsol +Name[lt]=Konsole +Name[lv]=Konsole +Name[mai]=कंसोल +Name[mk]=Конзола +Name[ml]=കണ്‍സോള്‍ +Name[mr]=कंसोल +Name[ms]=Konsole +Name[nb]=Konsole +Name[nds]=Konsole +Name[ne]=कन्सोल +Name[nl]=Konsole +Name[nn]=Konsoll +Name[oc]=Konsole +Name[or]=କୋଲସୋଲ +Name[pa]=ਕਨਸੋਲ +Name[pl]=Konsola +Name[pt]=Konsole +Name[pt_BR]=Konsole +Name[ro]=Konsolă +Name[ru]=Konsole +Name[se]=Konsolla +Name[si]=කන්සෝල් +Name[sk]=Konsole +Name[sl]=Konsole +Name[sr]=Конзола +Name[sr@ijekavian]=Конзола +Name[sr@ijekavianlatin]=Konsole +Name[sr@latin]=Konsole +Name[sv]=Konsole +Name[ta]=கான்சோல் +Name[te]=కాన్సోల్ +Name[tg]=Консол +Name[th]=คอนโซล-K +Name[tr]=Konsole +Name[ug]=Konsole +Name[uk]=Konsole +Name[uz]=Konsole +Name[uz@cyrillic]=Konsole +Name[vi]=Konsole +Name[wa]=Konsole +Name[xh]=Konsole +Name[x-test]=xxKonsolexx +Name[zh_CN]=Konsole +Name[zh_TW]=Konsole + +GenericName=Terminal +GenericName[af]=Terminaal +GenericName[ar]=طرفيّة +GenericName[as]=টাৰ্মিনেল +GenericName[ast]=Terminal +GenericName[be@latin]=Terminał +GenericName[bg]=Терминал +GenericName[bn]=টার্মিনাল +GenericName[bn_IN]=টার্মিন্যাল +GenericName[bs]=Terminal +GenericName[ca]=Terminal +GenericName[ca@valencia]=Terminal +GenericName[cs]=Terminál +GenericName[csb]=Terminal +GenericName[da]=Terminal +GenericName[de]=Terminal +GenericName[el]=Τερματικό +GenericName[en_GB]=Terminal +GenericName[eo]=Terminalo +GenericName[es]=Terminal +GenericName[et]=Terminal +GenericName[eu]=Terminala +GenericName[fi]=Pääteikkuna +GenericName[fr]=Terminal +GenericName[fy]=Terminal +GenericName[ga]=Teirminéal +GenericName[gl]=Terminal +GenericName[gu]=ટર્મિનલ +GenericName[he]=מסוף +GenericName[hi]=टर्मिनल +GenericName[hne]=टर्मिनल +GenericName[hr]=Terminal +GenericName[hsb]=Terminal +GenericName[hu]=Terminál +GenericName[ia]=Terminal +GenericName[id]=Terminal +GenericName[is]=Skjáhermir +GenericName[it]=Terminale +GenericName[ja]=ターミナル +GenericName[ka]=ტერმინალი +GenericName[kk]=Терминал +GenericName[km]=ស្ថានីយ +GenericName[kn]=ಆದೇಶತೆರೆ (ಟರ್ಮಿನಲ್) +GenericName[ko]=터미널 +GenericName[ku]=Termînal +GenericName[lt]=Terminalas +GenericName[lv]=Terminālis +GenericName[mai]=टर्मिनल +GenericName[mk]=Терминал +GenericName[ml]=ടെര്‍മിനല്‍ +GenericName[mr]=टर्मिनल +GenericName[nb]=Terminal +GenericName[nds]=Konsool +GenericName[nl]=Terminal +GenericName[nn]=Terminal +GenericName[or]=ଟର୍ମିନାଲ +GenericName[pa]=ਟਰਮੀਨਲ +GenericName[pl]=Terminal +GenericName[pt]=Terminal +GenericName[pt_BR]=Terminal +GenericName[ro]=Terminal +GenericName[ru]=Терминал +GenericName[se]=Terminal +GenericName[si]=අග්‍රය +GenericName[sk]=Terminál +GenericName[sl]=Terminal +GenericName[sr]=Терминал +GenericName[sr@ijekavian]=Терминал +GenericName[sr@ijekavianlatin]=Terminal +GenericName[sr@latin]=Terminal +GenericName[sv]=Terminal +GenericName[ta]=முனையம் +GenericName[te]=టెర్మినల్ +GenericName[tg]=Терминал +GenericName[th]=เทอร์มินัล +GenericName[tr]=Uçbirim +GenericName[ug]=تېرمىنال +GenericName[uk]=Термінал +GenericName[uz]=Terminal +GenericName[uz@cyrillic]=Терминал +GenericName[wa]=Terminå +GenericName[x-test]=xxTerminalxx +GenericName[zh_CN]=终端 +GenericName[zh_TW]=終端機 + +[Desktop Action NewWindow] +Name=Open a New Window +Name[ar]=افتح نافذة جديدة +Name[bs]=Otvori u novom prozoru +Name[ca]=Obre una finestra nova +Name[ca@valencia]=Obri una finestra nova +Name[cs]=Otevřít nové okno +Name[da]=Åbn et nyt vindue +Name[de]=Ein neues Fenster öffnen +Name[el]=Άνοιγμα νέου παραθύρου +Name[en_GB]=Open a New Window +Name[es]=Abrir una nueva ventana +Name[et]=Ava uues aknas +Name[fi]=Avaa uusi ikkuna +Name[fr]=Ouvrir dans une nouvelle fenêtre +Name[gl]=Abrir unha xanela nova +Name[he]=פתח חלון חדש +Name[hu]=Új ablak megnyitása +Name[ia]=Aperi un nove fenestra +Name[id]=Buka Jendela Baru +Name[is]=Opna nýjan glugga +Name[it]=Apri in una nuova finestra +Name[ko]=새 창 열기 +Name[nb]=Åpne et nytt vindu +Name[nds]=En nieg Finster opmaken +Name[nl]=Een nieuw venster openen +Name[pa]=ਨਵੀਂ ਵਿੰਡੋ ਖੋਲ੍ਹੋ +Name[pl]=Otwórz nowe okno +Name[pt]=Abrir uma Nova Janela +Name[pt_BR]=Abre uma nova janela +Name[ro]=Deschide o fereastră nouă +Name[ru]=Открыть новое окно +Name[sk]=Otvoriť nové okno +Name[sl]=Odpri novo okno +Name[sr]=Отвори нови прозор +Name[sr@ijekavian]=Отвори нови прозор +Name[sr@ijekavianlatin]=Otvori novi prozor +Name[sr@latin]=Otvori novi prozor +Name[sv]=Öppna ett nytt fönster +Name[tr]=Yeni Bir Pencere Aç +Name[uk]=Відкрити нове вікно +Name[x-test]=xxOpen a New Windowxx +Name[zh_CN]=打开新窗口 +Name[zh_TW]=開啟新視窗 +Exec=konsole diff --git a/konsole/desktop/konsole.notifyrc b/konsole/desktop/konsole.notifyrc new file mode 100644 index 00000000..c9a9d00b --- /dev/null +++ b/konsole/desktop/konsole.notifyrc @@ -0,0 +1,930 @@ +[Global] +IconName=utilities-terminal +Comment=Konsole +Comment[af]=Konsole +Comment[ar]=كونسول +Comment[as]=Konsole +Comment[ast]=Konsole +Comment[be]=Konsole +Comment[be@latin]=Konsole +Comment[bg]=Терминал Konsole +Comment[bn]=কনসোল +Comment[bn_IN]=Konsole +Comment[br]=Konsole +Comment[bs]=Konzola +Comment[ca]=Consola +Comment[ca@valencia]=Consola +Comment[cs]=Konsole +Comment[csb]=Kònsola +Comment[cy]=Konsole +Comment[da]=Konsole +Comment[de]=Konsole +Comment[el]=Konsole +Comment[en_GB]=Konsole +Comment[eo]=Konsole +Comment[es]=Konsole +Comment[et]=Konsool +Comment[eu]=Konsole +Comment[fi]=Konsole +Comment[fr]=Konsole +Comment[fy]=Konsole +Comment[ga]=Konsole +Comment[gl]=Konsole +Comment[gu]=કોન્સોલ +Comment[he]=Konsole +Comment[hi]=कंसोल +Comment[hne]=कंसोल +Comment[hr]=Konsole +Comment[hsb]=Konsola +Comment[hu]=Konsole +Comment[ia]=Konsole +Comment[id]=Konsole +Comment[is]=Konsole +Comment[it]=Konsole +Comment[ja]=Konsole +Comment[ka]=კონსოლი +Comment[kk]=Konsole +Comment[km]=កុងសូល +Comment[kn]=ಕನ್ಸೋಲ್ +Comment[ko]=Konsole +Comment[ku]=Konsol +Comment[lt]=Konsolė +Comment[lv]=Konsole +Comment[mai]=कंसोल +Comment[mk]=Конзола +Comment[ml]=കണ്‍സോള്‍ +Comment[mr]=कंसोल +Comment[ms]=Konsole +Comment[nb]=Konsole terminalemulator +Comment[nds]=Konsole +Comment[ne]=कन्सोल +Comment[nl]=Konsole +Comment[nn]=Konsoll +Comment[oc]=Konsole +Comment[or]=କୋଲସୋଲ +Comment[pa]=ਕਨਸੋਲ +Comment[pl]=Konsole +Comment[pt]=Konsole +Comment[pt_BR]=Konsole +Comment[ro]=Konsolă +Comment[ru]=Konsole +Comment[se]=Konsolla +Comment[si]=කන්සෝල් +Comment[sk]=Konsole +Comment[sl]=Konsole +Comment[sr]=Конзола +Comment[sr@ijekavian]=Конзола +Comment[sr@ijekavianlatin]=Konsole +Comment[sr@latin]=Konsole +Comment[sv]=Konsole +Comment[ta]=கான்சோல் +Comment[te]=కాన్సొల్ +Comment[tg]=Консол +Comment[th]=คอนโซล-K +Comment[tr]=Konsole +Comment[ug]=Konsole +Comment[uk]=Konsole +Comment[uz]=Terminal +Comment[uz@cyrillic]=Терминал +Comment[wa]=Konsole +Comment[x-test]=xxKonsolexx +Comment[zh_CN]=Konsole +Comment[zh_TW]=Konsole + +[Event/BellVisible] +Name=Bell in Visible Session +Name[af]=Klokkie in die sigbare sessie +Name[ar]=جرس في جلسة ظاهرة +Name[as]=দেখি পোৱা অধিবেশনত ঘন্টা +Name[ast]=Campana en sesión visible +Name[be@latin]=Syhnał u dziejnaj sesii +Name[bg]=Звук във видимата сесия +Name[bn]=প্রদর্শিত সেশন-এ ঘণ্টা +Name[bn_IN]=দৃশ্যমান সেশানের ঘন্টাধ্বনি +Name[bs]=Zvono u vidljivoj sesiji +Name[ca]=Timbre en una sessió visible +Name[ca@valencia]=Timbre en una sessió visible +Name[cs]=Zvonek ve viditelném sezení +Name[csb]=Zwónk w widzawny sesëji +Name[da]=Bip i synlig session +Name[de]=Signalton in sichtbarer Sitzung +Name[el]=Ηχητική σήμανση στην ορατή συνεδρία +Name[en_GB]=Bell in Visible Session +Name[eo]=Pepo en videbla seanco +Name[es]=Campana en sesión visible +Name[et]=Heli nähtavas seansis +Name[eu]=Ezkila saio ikusgaian +Name[fi]=Äänimerkki näkyvässä istunnossa +Name[fr]=Cloche dans la session visible +Name[fy]=Bel yn sichtbere sesje +Name[ga]=Cloigín i Seisiún Infheicthe +Name[gl]=Badalada na sesión visíbel +Name[gu]=દ્રશ્ય સત્રમાં ઘંટડી +Name[he]=פעמון בהפעלה המוצגת +Name[hi]=दृश्यमय सत्र में घंटी +Name[hne]=दिखत सत्र मं घंटी +Name[hr]=Zvono u vidljivoj sesiji +Name[hsb]=Zwónčk we widźomnym posedźenju +Name[hu]=Csengetés egy látható munkafolyamatban +Name[ia]=Campana in session visibile +Name[id]=Bel Dalam Sesi Terlihat +Name[is]=Bjalla í sýnilegri setu +Name[it]=Campanella in sessione visibile +Name[ja]=可視セッションのベル +Name[kk]=Көрінетін сеанстың қоңырауы +Name[km]=កណ្ដឹង​នៅ​​ក្នុង​សម័យ​មើល​ឃើញ +Name[kn]=ಗೋಚರ ಅಧಿವೇಶನದಲ್ಲಿ (ಸೆಶನ್) ಗಂಟೆ +Name[ko]=보이는 세션에서 알림 발생 +Name[ku]=Zingila di Danişîna Xuya de +Name[lt]=Skambutis matomoje sesijoje +Name[lv]=Zvans redzamā sesijā +Name[mai]=दृश्य सत्रमे बेल +Name[mk]=Ѕвонче во видлива сесија +Name[ml]=കാണാവുന്നൊരു സെഷനില്‍ മണിനാദം +Name[mr]=दृश्यस्पाद सत्र आढळल्यास घंटी वाजवा +Name[nb]=Varsel i synlig økt +Name[nds]=Pingel för sichtbor Törn +Name[nl]=Geluidssignaal in zichtbare sessie +Name[nn]=Bjølle i synleg økt +Name[or]=ଦୃଶ୍ୟମାନ ଅଧିବେଶନରେ ବେଲ +Name[pa]=ਦਿੱਖ ਸ਼ੈਸ਼ਨ ਵਿੱਚ ਘੰਟੀ +Name[pl]=Dzwonek w widocznej sesji +Name[pt]=Campainha numa Sessão Visível +Name[pt_BR]=Campainha na sessão visível +Name[ro]=Clopoțel în sesiunea vizibilă +Name[ru]=Сигнал в видимом сеансе +Name[se]=Divga oainnus bargovuorus +Name[si]=දෘශ්‍ය වාරයේ සීනුව +Name[sk]=Zvonček v zobrazenom sedení +Name[sl]=Zvonec v vidni seji +Name[sr]=Звоно у видљивој сесији +Name[sr@ijekavian]=Звоно у видљивој сесији +Name[sr@ijekavianlatin]=Zvono u vidljivoj sesiji +Name[sr@latin]=Zvono u vidljivoj sesiji +Name[sv]=Ljudsignal i synlig session +Name[ta]=தோற்றமுள்ள அமர்வில் மணி +Name[te]=కనిపించు భాగమునందు బెల్ +Name[tg]=Сигнали аудиоӣ дар сеанси намоишӣ +Name[th]=ออดในวาระงานที่เห็นได้ +Name[tr]=Görünen Oturumda Zil +Name[ug]=ئەڭگىمەدىكى قوڭغۇراق ئاۋازى كۆرۈنۈشچان +Name[uk]=Гудок у видимий сеанс +Name[wa]=Cloke e veyåve session +Name[x-test]=xxBell in Visible Sessionxx +Name[zh_CN]=可见会话中的响铃 +Name[zh_TW]=可見工作階段響鈴 +Comment=Bell emitted within a visible session +Comment[af]=Klokkie word gelui in die sigbare sessie +Comment[ar]=جرس أُصدر في جلسة ظاهرة +Comment[ast]=Campana dada na sesión visible +Comment[be@latin]=U bačnaj sesii ŭźnik syhnał. +Comment[bg]=Изпълнение на звук във видимата сесия +Comment[bn]=একটি দৃশ্যমান সেশন-এ ঘণ্টা বেজেছে +Comment[bn_IN]=দৃশ্যমান সেশানের মধ্যে যে ঘন্টাধ্বনি বাজানো হয় +Comment[bs]=Oglašeno je zvono unutar vidljive sesije +Comment[ca]=Timbre emès en una sessió visible +Comment[ca@valencia]=Timbre emés en una sessió visible +Comment[cs]=Zvonek ve viditelném sezení +Comment[csb]=Zwónk jaczi zwãczi w widzawny sesëji +Comment[cy]=Gollyngwyd cloch mewn sesiwn gweledig +Comment[da]=Klokke brugt indenfor en synlig session +Comment[de]=Signalton, der in einer sichtbaren Sitzung ertönt +Comment[el]=Ηχητική σήμανση μέσα στην ορατή συνεδρία +Comment[en_GB]=Bell emitted within a visible session +Comment[eo]=Pepo ekigita en videbla seanco +Comment[es]=Campanada en la sesión visible +Comment[et]=Heli nähtavas seansis +Comment[eu]=Jo ezkila saio ikusgaian +Comment[fi]=Äänimerkki lähetetty näkyvässä istunnossa +Comment[fr]=Cloche émise dans une session visible +Comment[fy]=Bel útstjoerd binnen in sichtbere sesje +Comment[ga]=Baineadh an cloigín i seisiún infheicthe +Comment[gl]=Badalada emitida nunha sesión visíbel +Comment[gu]=ઘંટડી દ્રશ્ય સત્રમાં વગાડવી +Comment[he]=הופעל פעמון בהפעלה המוצגת +Comment[hi]=एक दृश्यमय सत्र के भीतर घंटी बजाया +Comment[hne]=एक दिखत सत्र के भीतर घंटी बजाइस +Comment[hr]=Zvono objavljeno u vidljivoj sesiji +Comment[hsb]=Zwónčk, kotryž so we widźomnym posedźenju zwoni +Comment[hu]=Csengetés egy látható munkafolyamatban +Comment[ia]=Sono de campana emittite intra un session visibile +Comment[id]=Bel yang dikeluarkan dalam sesi yang terlihat +Comment[is]=Bjallan hringdi í sýnilegu setunni +Comment[it]=Campanella suonata all'interno di una sessione visibile +Comment[ja]=可視セッションでベルが鳴りました +Comment[ka]=ხილულმა სესიისამ ხმოვანი სიგნალი გამოსცა +Comment[kk]=Көрінетін сеанста қоңырау соғылды +Comment[km]=កណ្ដឹង​ដែល​បាន​លុប​នៅ​ក្នុង​សម័យ​ដែល​មើល​ឃើញ +Comment[kn]=ಗೋಚರ ಅಧಿವೇಶನದಲ್ಲಿ (ಸೆಶನ್) ಹೊಮ್ಮಿಸಲಾಗುವ ಗಂಟೆ +Comment[ko]=보이는 세션에서 종소리 울림 +Comment[ku]=Zingila derketî bi danişîna xuya +Comment[lt]=Matomos sesijos metu skambutis neveiks +Comment[lv]=Zvans, kas zvanīts redzamā sesijā +Comment[mai]=एकटा दृश्यमय सत्र केर भीतर घंटी बाजल +Comment[mk]=Емитувано е ѕвонче внатре во видливата сесија +Comment[ml]=കാണാവുന്നൊരു സെഷനില്‍ പുറപ്പെടുവിയ്ക്കുന്ന മണിനാദം +Comment[mr]=एक दृश्यास्पद सत्रच्या अंतर्गत घंटी वाजवा +Comment[ms]=Loceng dipancarkan dalam sesi dapat dilihat +Comment[nb]=Signal sendt ut fra en synlig økt +Comment[nds]=De Pingel för en sichtbor Törn +Comment[ne]=दृश्यात्मक सत्रभित्र छोडिएको घण्टी +Comment[nl]=Geluidssignaal aangeroepen in zichtbare sessie +Comment[nn]=Bjøllesignal sendt i ei synleg økt +Comment[or]=ଦୃଶ୍ୟମାନ ଅଧିବେଶନ ମଧ୍ଯରେ ବେଲ ବାହାରିଥାଏ +Comment[pa]=ਇੱਕ ਦਿੱਖ ਸ਼ੈਸ਼ਨ ਵਿੱਚ ਘੰਟੀ ਵਜਾਓ +Comment[pl]=Dzwonek uruchamiany w widocznej sesji +Comment[pt]=Campainha emitida dentro de uma sessão visível +Comment[pt_BR]=Campainha emitida dentro de uma sessão visível +Comment[ro]=Sunet emis în sesiunea vizibilă +Comment[ru]=Видимый сеанс подал сигнал +Comment[se]=Divgasignála sáddejuvvui oainnus bargovuorus +Comment[si]=දෘශ්‍ය වාරය තුළ සීනුව විමෝචනය +Comment[sk]=Zvonček poslaný v zobrazenom sedení +Comment[sl]=Zvonenje zvonca v vidni seji +Comment[sr]=Оглашено је звоно унутар видљиве сесије +Comment[sr@ijekavian]=Оглашено је звоно унутар видљиве сесије +Comment[sr@ijekavianlatin]=Oglašeno je zvono unutar vidljive sesije +Comment[sr@latin]=Oglašeno je zvono unutar vidljive sesije +Comment[sv]=Ljudsignal inne i en synlig session +Comment[ta]=பார்க்கும் காலநேர அளவுக்குள் மணி வெளித்தள்ளப்பட்டது. +Comment[te]=కనిపించు సెషన్‌లో విడుదలైన బెల్ +Comment[tg]=Сеанси намоёнӣ зангро овоз кард +Comment[th]=ออดจะส่งเสียงภายในวาระงานที่เห็นได้ +Comment[tr]=Görünen oturum ile zil çal +Comment[ug]=ئەڭگىمەدە يۈز بەرگەن قوڭغۇراق ئاۋازى كۆرۈنۈشچان +Comment[uk]=Звучить гудок у видимому сеансі +Comment[wa]=Cloke ki sone divins ene veyåve session +Comment[x-test]=xxBell emitted within a visible sessionxx +Comment[zh_CN]=可见会话中发生的响铃 +Comment[zh_TW]=可見工作階段中的響鈴行為 +Action=None + +[Event/BellInvisible] +Name=Bell in Non-Visible Session +Name[af]=Klokkie in nie-sigbare sessie +Name[ar]=جرس في جلسة غير ظاهرة +Name[ast]=Campana en sesión non visible +Name[be@latin]=Syhnał u niadziejnaj sesii +Name[bg]=Звук в невидима сесия +Name[bn]=আবৃত সেশন-এ ঘণ্টা +Name[bn_IN]=অদৃশ্য সেশানের মধ্যে যে ঘন্টাধ্বনি বাজানো হয় +Name[bs]=Zvono u zaklonjenoj sesiji +Name[ca]=Timbre en una sessió no visible +Name[ca@valencia]=Timbre en una sessió no visible +Name[cs]=Zvonek ve skrytém sezení +Name[csb]=Zwónk w niewidzawny sesëji +Name[da]=Bip i ikke-synlig session +Name[de]=Signalton in nicht sichtbarer Sitzung +Name[el]=Ηχητική σήμανση σε μη ορατή συνεδρία +Name[en_GB]=Bell in Non-Visible Session +Name[eo]=Pepo en nevidebla seanco +Name[es]=Campana en sesión no visible +Name[et]=Heli nähtamatus seansis +Name[eu]=Ezkila saio ez ikusgaian +Name[fi]=Äänimerkki näkymättömässä istunnossa +Name[fr]=Cloche dans une session non visible +Name[fy]=Bel yn net-sichtbere sesje +Name[ga]=Cloigín i Seisiún Dofheicthe +Name[gl]=Badalada nunha sesión non visíbel +Name[gu]=અદ્રશ્ય સત્રમાં ઘંટડી +Name[he]=פעמון בהפעלה מוסתרת +Name[hi]=गैर दृश्यमय सत्र में घंटी +Name[hne]=नइ दिखत सत्र मं घंटी +Name[hr]=Zvono u prikrivenoj sesiji +Name[hsb]=Zwónčk w njewidźomnym posedźenju +Name[hu]=Csengetés egy nem látható munkafolyamatban +Name[ia]=Campana in session non-visibile +Name[id]=Bel Dalam Sesi Tak Terlihat +Name[is]=Bjalla í ósýnilegri setu +Name[it]=Campanella in sessione non visibile +Name[ja]=不可視セッションのベル +Name[kk]=Көрінбейтін сеанстың қоңырауы +Name[km]=កណ្ដឹង​ក្នុង​សម័យ​​មើល​មិន​ឃើញ +Name[kn]=ಅಗೋಚರ ಅಧಿವೇಶನದಲ್ಲಿ (ಸೆಶನ್) ಗಂಟೆ +Name[ko]=보이지 않는 세션에서 알림 발생 +Name[ku]=Zengila di Danişîna Nexuya de +Name[lt]=Skambutis nematomoje sesijoje +Name[lv]=Zvans neredzamā sesijā +Name[mai]=गैर दृश्य सत्रमे बेल +Name[mk]=Ѕвонче во не-видлива сесија +Name[ml]=കാണാത്ത സെഷനില്‍ പുറപ്പെടുവിയ്ക്കുന്ന മണിനാദം +Name[mr]=विनादृश्यास्पद सत्रच्या अंतर्गत घंटी वाजवा +Name[nb]=Varsel i ikke-synlig økt +Name[nds]=Pingel för nich sichtbor Törn +Name[nl]=Geluidssignaal in niet-zichtbare sessie +Name[nn]=Bjølle i ikkje-synleg økt +Name[or]=ଅଦୃଶ୍ୟମାନ ଅଧିବେଶନରେ ବେଲ +Name[pa]=ਅਣ-ਦਿੱਖ ਸ਼ੈਸ਼ਨ ਵਿੱਚ ਘੰਟੀ +Name[pl]=Dzwonek w niewidocznej sesji +Name[pt]=Campainha numa Sessão Invisível +Name[pt_BR]=Campainha na sessão não visível +Name[ro]=Clopoțel în sesiunea nevizibilă +Name[ru]=Сигнал в невидимом сеансе +Name[se]=Divga oaidnemeahttun bargovuorus +Name[si]=දෘශ්‍ය නොවන වාරයේ සීනුව +Name[sk]=Zvonček v nezobrazenom sedení +Name[sl]=Zvonec v trenutno nevidni seji +Name[sr]=Звоно у заклоњеној сесији +Name[sr@ijekavian]=Звоно у заклоњеној сесији +Name[sr@ijekavianlatin]=Zvono u zaklonjenoj sesiji +Name[sr@latin]=Zvono u zaklonjenoj sesiji +Name[sv]=Ljudsignal i osynlig session +Name[ta]=பார்க்கமுடியாத அமர்வில் மணி +Name[te]=కనిపించని సెషన్‌లోని బెల్ +Name[tg]=Сеанси пинҳонӣ зангро овоз кард +Name[th]=ออดในวาระงานที่มองไม่เห็น +Name[tr]=Görünmeyen Oturumda Zil +Name[ug]=كۆرۈنمەيدىغان ئەڭگىمەدىكى قوڭغۇراق ئاۋازى +Name[uk]=Гудок у невидимому сеансі +Name[wa]=Cloke e nén veyåve session +Name[x-test]=xxBell in Non-Visible Sessionxx +Name[zh_CN]=不可见会话中的响铃 +Name[zh_TW]=非可見工作階段的響鈴 +Comment=Bell emitted within a non-visible session +Comment[af]=Klokkie word gelui in die nie-sigbare sessie +Comment[ar]=جرس أُصدر في جلسة غير ظاهرة +Comment[ast]=Campana dada nuna sesión non visible +Comment[be@latin]=U niabačnaj sesii ŭźnik syhnał. +Comment[bg]=Изпълнение на звук в невидима сесия +Comment[bn]=একটি অদৃশ্য সেশন-এ ঘণ্টা বেজেছে +Comment[bn_IN]=অদৃশ্য সেশানের ঘন্টাধ্বনি +Comment[bs]=Oglašeno je zvono unutar zaklonjene sesije +Comment[ca]=Timbre emès en una sessió no visible +Comment[ca@valencia]=Timbre emés en una sessió no visible +Comment[cs]=Zvonek ve skrytém sezení +Comment[csb]=Zwónk jaczi zwãczi w niewidzawny sesëji +Comment[cy]=Gollyngwyd cloch mewn sesiwn anweledig +Comment[da]=Klokke brugt indenfor en ikke-synlig session +Comment[de]=Signalton, der in einer nicht sichtbaren Sitzung ertönt +Comment[el]=Ηχητική σήμανση μέση σε μια μη ορατή συνεδρία +Comment[en_GB]=Bell emitted within a non-visible session +Comment[eo]=Pepo ekigita en nevidebla seanco +Comment[es]=Campanada en una sesión no visible +Comment[et]=Heli nähtamatus seansis +Comment[eu]=Jo ezkila saio ez-ikusgaian +Comment[fi]=Äänimerkki lähetetty näkymättömässä istunnossa +Comment[fr]=Cloche émise dans une session non visible +Comment[fy]=Bel útstjoerd yn in net-sichtbere sesje +Comment[ga]=Baineadh an cloigín i seisiún dofheicthe +Comment[gl]=Badalada emitida nunha sesión non visíbel +Comment[gu]=ઘંટડી અદ્રશ્ય સત્રમાં વગાડવી +Comment[he]=הופעל פעמון בהפעלה מוסתרת +Comment[hi]=गैर दृश्यमय सत्र के भीतर घंटी बजाया +Comment[hne]=नइ दिखत सत्र के भीतर घंटी बजाइस +Comment[hr]=Zvono objavljeno u prikrivenoj sesiji +Comment[hsb]=Zwónčk, kotryž so w njewidźomnym posedźenju zwoni +Comment[hu]=Csengetés egy nem látható munkafolyamatban +Comment[ia]=Sono de campana emittite intra un session non visibile +Comment[id]=Bel yang dikeluarkan dalam sesi yang tak terlihat +Comment[is]=Bjallan hringdi í ósýnilegu setunni +Comment[it]=Campanella emessa all'interno di una sessione non visibile +Comment[ja]=不可視セッションでベルが鳴りました +Comment[ka]=უხილავმა სესიისამ ხმოვანი სიგნალი გამოსცა +Comment[kk]=Көрінбейтін сеанста қоңырау соғылды +Comment[km]=កណ្ដឹង​ដែល​បានលុប​ក្នុង​សម័យ​ដែល​មើល​មិន​ឃើញ +Comment[kn]=ಅಗೋಚರ ಅಧಿವೇಶನದಲ್ಲಿ (ಸೆಶನ್) ಹೊಮ್ಮಿಸಲಾಗುವ ಗಂಟೆ +Comment[ko]=보이지 않는 세션에서 종소리 울림 +Comment[ku]=Zingila derketî bi danişîna nexuya +Comment[lt]=Nematomos sesijos metu skambutis neveiks +Comment[lv]=Zvans, kas zvanīts neredzamā sesijā +Comment[mai]=गैर दृश्यमय सत्र केर भीतर घंटी बाजल +Comment[mk]=Емитувано е ѕвонче внатре во невидливата сесија +Comment[ml]=കാണാത്ത സെഷനില്‍ പുറപ്പെടുവിയ്ക്കുന്ന മണിനാദം +Comment[mr]=विनादृश्यस्पाद सत्र अंतर्गत घंटी वाजवा +Comment[ms]=Loceng dipancarkan dalam sesi tak dapat dilihat +Comment[nb]=Signal sendt ut i en økt som ikke er synlig +Comment[nds]=De Pingel för en nich sichtbor Törn +Comment[ne]=दृश्यात्मक बिहिन सत्र भित्र छोडिएको घण्टी +Comment[nl]=Geluidssignaal aangeroepen in niet-zichtbare sessie +Comment[nn]=Bjøllesignal sendt i ei ikkje-synleg økt +Comment[or]=ଅଦୃଶ୍ୟମାନ ଅଧିବେଶନ ମଧ୍ଯରେ ବେଲ ବାହାରିଥାଏ +Comment[pa]=ਗ਼ੈਰ-ਦਿੱਖ ਸ਼ੈਸ਼ਨ ਵਿੱਚ ਘੰਟੀ ਵਜਾਓ +Comment[pl]=Dzwonek uruchamiany w niewidocznej sesji +Comment[pt]=Campainha emitida dentro de uma sessão não-visível +Comment[pt_BR]=Campainha emitida dentro de uma sessão não visível +Comment[ro]=Sunet emis într-o sesiune nevizibilă +Comment[ru]=Невидимый сеанс подал сигнал +Comment[se]=Divgasignála sáddejuvvui oaidnemeahttun bargovuorus +Comment[si]=දෘශ්‍ය-නොවන වාරය තුළ සීනුව විමෝචනය +Comment[sk]=Zvonček poslaný v nezobrazenom sedení +Comment[sl]=Zvonenje zvonca v trenutno nevidni seji +Comment[sr]=Оглашено је звоно унутар заклоњене сесије +Comment[sr@ijekavian]=Оглашено је звоно унутар заклоњене сесије +Comment[sr@ijekavianlatin]=Oglašeno je zvono unutar zaklonjene sesije +Comment[sr@latin]=Oglašeno je zvono unutar zaklonjene sesije +Comment[sv]=Ljudsignal inne i en osynlig session +Comment[ta]=பார்க்க முடியாத காலநேர அளவுக்குள் மணி வெளித்தள்ளப்பட்டது. +Comment[te]=కనిపించని సెషన్ లో విడుదలైన బెల్ +Comment[tg]=Даруни саеанси пинҳонӣ занг овоз кард +Comment[th]=ออดจะส่งเสียงภายในวาระงานที่มองไม่เห็น +Comment[tr]=Görünmeyen oturumda zil +Comment[ug]=كۆرۈنمەيدىغان ئەڭگىمەدە يۈز بەرگەن قوڭغۇراق ئاۋازى +Comment[uk]=Звучить гудок у невидимому сеансі +Comment[wa]=Cloke ki sone divins ene nén veyåve session +Comment[x-test]=xxBell emitted within a non-visible sessionxx +Comment[zh_CN]=不可见会话中发生的响铃 +Comment[zh_TW]=非可見工作階段中的響鈴行為 +Sound=KDE-Sys-App-Message.ogg +Action=Popup + +[Event/Activity] +Name=Activity in Monitored Session +Name[af]=Aktiwiteit in gemonitorde sessie +Name[ar]=نشاط في الجلسة المُراقبة +Name[ast]=Xera en sesión monitorizada +Name[be@latin]=Pieramieny ŭ adsočvanaj sesii +Name[bg]=Активност в наблюдаваната сесия +Name[bn]=মনিটর করা সেশন-এ ঘটনা +Name[bn_IN]=নিরীক্ষণ হওয়া সেশানের মধ্যে কর্ম +Name[bs]=Aktivnost u nadgledanoj sesiji +Name[ca]=Activitat en una sessió controlada +Name[ca@valencia]=Activitat en una sessió controlada +Name[cs]=Aktivita v monitorovaném sezení +Name[csb]=Aktiwnota w mònitorowóny sesëji +Name[da]=Aktivitet i overvåget session +Name[de]=Aktivität in überwachter Sitzung +Name[el]=Δραστηριότητα σε συνεδρία υπό εποπτεία +Name[en_GB]=Activity in Monitored Session +Name[eo]=Aktiveco en rigardata seanco +Name[es]=Actividad en sesión monitorizada +Name[et]=Aktiivsus jälgitavas seansis +Name[eu]=Monitorizatutako saioko iharduera +Name[fi]=Aktiivisuutta tarkkailtavassa istunnossa +Name[fr]=Activité dans la session surveillée +Name[fy]=Aktiviteit yn observearre sesje +Name[ga]=Gníomhaíocht i Seisiún Monatóirithe +Name[gl]=Actividade na sesión vixiada +Name[gu]=દેખરેખ રાખેલ સત્રમાં ક્રિયા +Name[he]=פעילות בהפעלה מנוטרת +Name[hi]=मॉनीटर किए जा रहे सत्र में क्रियाकलाप +Name[hne]=मानीटर होवत सत्र मं कामधाम +Name[hr]=Aktivnost u nadziranoj sesiji +Name[hsb]=Aktiwnosć we wobkedźbowanym posedźenju +Name[hu]=Aktivitás egy figyelt munkamenetben +Name[ia]=Activitate in session monitorate +Name[id]=Aktivitas di Sesi Termonitor +Name[is]=Virkni í setunni sem fylgst er með +Name[it]=Attività in sessione sorvegliata +Name[ja]=監視中のセッションの活動 +Name[kk]=Қадағалаудағы сеанста белсенділік +Name[km]=សកម្មភាព​ក្នុង​សម័យ​ដែល​បាន​ត្រួត​ពិនិត្យ +Name[kn]=ಮೇಲ್ವಿಚಾರಣೆಯಲ್ಲಿರುವ ಅಧಿವೇಶನದಲ್ಲಿನ (ಸೆಶನ್) ಕ್ರಿಯೆ +Name[ko]=보이는 세션에서 활동 발생 +Name[ku]=Çalakiyên di Danişîna Xuyakirî de +Name[lt]=Stebimos sesijos veikla +Name[lv]=Aktivitāte monitorētā sesijā +Name[mai]=देखल गेल सत्र मे क्रियाकलाप +Name[mk]=Активност во следената сесија +Name[ml]=നിരീക്ഷിച്ചുകൊണ്ടിരിയ്ക്കുന്ന സെഷനില്‍ പ്രവര്‍ത്തനം +Name[mr]=नियंत्रीत सत्र अंतर्गत क्रिया +Name[nb]=Aktivitet i en overvåket økt +Name[nds]=Aktiviteet binnen beluurt Törn +Name[nl]=Activiteit in een gevolgde sessie +Name[nn]=Aktivitet i overvaka økt +Name[or]=ଯାଞ୍ଚକୃତ ଅଧିବେଶନରେ କାର୍ଯ୍ୟକଳାପ +Name[pa]=ਨਿਗਰਾਨੀ ਅਧੀਨ ਸ਼ੈਸ਼ਨ 'ਚ ਐਕਟਵਿਟੀ +Name[pl]=Aktywność w monitorowanej sesji +Name[pt]=Actividade em Sessão Vigiada +Name[pt_BR]=Atividade em sessão monitorada +Name[ro]=Activitate în sesiunea monitorizată +Name[ru]=Активность в отслеживаемом сеансе +Name[si]=නිරීක්‍ෂණය කරන වාරයේ ක්‍රියාකාරකම් +Name[sk]=Aktivita v monitorovanom sedení +Name[sl]=Dejavnost v nadzorovani seji +Name[sr]=Активност у надгледаној сесији +Name[sr@ijekavian]=Активност у надгледаној сесији +Name[sr@ijekavianlatin]=Aktivnost u nadgledanoj sesiji +Name[sr@latin]=Aktivnost u nadgledanoj sesiji +Name[sv]=Aktivitet i bevakad session +Name[ta]=கண்காணிக்கப்படும் அமர்வில் செயற்பாடு +Name[te]=మానిటర్‌చేస్తున్న సెషన్‌నందలి క్రియాశీలత +Name[tg]=Фаъолият дар сеанси идорашаванда +Name[th]=มีกิจกรรมในวาระงานที่เฝ้าดูอยู่ +Name[tr]=İzlenen Oturumda Etkinlik +Name[ug]=كۆزىتىلىۋاتقان ئەڭگىمەدىكى پائالىيەت +Name[uk]=Дії в сеансі спостереження +Name[wa]=Activité e corwaiteye session +Name[x-test]=xxActivity in Monitored Sessionxx +Name[zh_CN]=被监视会话中的活动 +Name[zh_TW]=監控工作階段的活動 +Comment=Activity detected in a monitored session +Comment[af]=Aktiwiteit bespeur in 'n sessie wat gemonitor word +Comment[ar]=اكتُشف نشاط في جلسةٍ مُراقبة +Comment[ast]=Xera detectada nuna sesión monitorizada +Comment[be@latin]=U adsočvanaj sesii zaŭvažanyja pieramieny. +Comment[bg]=Засечена е активност в наблюдаваната сесия +Comment[bn]=মনিটর করা একটি সেশন-এ কিছু ঘটেছে +Comment[bn_IN]=নিরীক্ষণ হওয়া সেশানের মধ্যে সনাক্ত করা কর্ম +Comment[bs]=Pronađena je aktivnost u nadgledanoj sesiji +Comment[ca]=Activitat detectada en una sessió controlada +Comment[ca@valencia]=Activitat detectada en una sessió controlada +Comment[cs]=Detekována aktivita v monitorovaném sezení +Comment[csb]=Aktiwnota ùstalonô w mònitorowóny sesëji +Comment[cy]=Datgelwyd gweithgaredd mewn seswin wedi'i fonitro +Comment[da]=Aktivitet detekteret i en overvåget session +Comment[de]=In einer überwachten Sitzung wird Aktivität festgestellt +Comment[el]=Εντοπίστηκε δραστηριότητα σε μια συνεδρία υπό εποπτεία +Comment[en_GB]=Activity detected in a monitored session +Comment[eo]=Trovis aktivecon en rigardata seanco +Comment[es]=Actividad detectada en una sesión monitorizada +Comment[et]=Tuvastati aktiivsus jälgitavas seansis +Comment[eu]=Iharduera detektatuta monitorizatutako saioan +Comment[fi]=Aktiivisuutta havaittu tarkkailtavassa istunnossa +Comment[fr]=Activité détectée dans une session surveillée +Comment[fy]=Aktiviteit ûntdutsen yn observearre sesje +Comment[ga]=Braitheadh gníomhaíocht i seisiún monatóirithe +Comment[gl]=Detectouse actividade na sesión baixo seguimento +Comment[gu]=દેખરેખ રાખેલ સત્રમાં ક્રિયા નોંધાઇ +Comment[he]=זוהתה פעילות בהפעלה מנוטרת +Comment[hi]=मॉनीटर किए जा रहे सत्र में क्रियाकलाप का पता चला +Comment[hne]=मानीटर होवत सत्र मं कामधाम के पता चलिस +Comment[hr]=Otkrivena je aktivnost u nadziranoj sesiji +Comment[hsb]=Aktiwita zwěsćena we wobkedźbowanym posedźenju +Comment[hu]=Aktivitás észlelve egy figyelt munkamenetben +Comment[ia]=Activitate relevate in un session monitorate +Comment[id]=Aktivitas terdeteksi di sesi yang termonitor +Comment[is]=Eitthvað er að gerast í setunni sem fylgst er með +Comment[it]=Attività rilevata in una sessione sorvegliata +Comment[ja]=監視中のセッションで活動を検出しました +Comment[kk]=Қадағалаудағы сеанста белсенділік байқалды +Comment[km]=សកម្មភាព​ដែល​បាន​រក​ឃើញ​ក្នុង​សម័យ​ដែល​បាន​ត្រួត​ពិនិត្យ +Comment[kn]=ಮೇಲ್ವಿಚಾರಣೆಯಲ್ಲಿರದ ಅಧಿವೇಶನದಲ್ಲಿನ (ಸೆಶನ್) ಪತ್ತೆಹಚ್ಚಲಾದ ಕ್ರಿಯೆ +Comment[ko]=관찰하는 세션에서 활동 감지됨 +Comment[ku]=Çalakiyên di danişîneke xuyakirî de bidestketî +Comment[lt]=Stebimoje sesijoje pastebėta veikla +Comment[lv]=Ir konstatēta aktivitāte monitorētā sesijā +Comment[mai]=मानीटर कएल जाए रहल सत्र मे क्रियाकलापक पता लागल +Comment[mk]=Има активност во следената сесија +Comment[ml]=നിരീക്ഷിച്ചുകൊണ്ടിരിയ്ക്കുന്ന സെഷനില്‍ പ്രവര്‍ത്തനം +Comment[mr]=नियंत्रीत सत्र अंतर्गत क्रिया आढळले +Comment[ms]=Aktiviti dikesan dalam sesi yang dipantau +Comment[nb]=Det er oppdaget aktivitet i en økt som er overvåket +Comment[nds]=Aktiviteet binnen en beluurt Törn opdeckt +Comment[ne]=अनुगमन गरिएको सत्रमा क्रियाकलाप पत्ता लागाइयो +Comment[nl]=Activiteit ontdekt in een gevolgde sessie +Comment[nn]=Aktivitet oppdaga i overvaka økt +Comment[or]=ଯାଞ୍ଚକୃତ ଅଧିବେଶନରେ ଚିହ୍ନଟ ହୋଇଥିବା କାର୍ଯ୍ୟକଳାପ +Comment[pa]=ਨਿਗਰਾਨੀ ਅਧੀਨ ਸ਼ੈਸ਼ਨ 'ਚ ਐਕਟਵਿਟੀ ਮਿਲੀ +Comment[pl]=Wykryto aktywność w monitorowanej sesji +Comment[pt]=Actividade detectada numa sessão vigiada +Comment[pt_BR]=Atividade detectada em uma sessão monitorada +Comment[ro]=Activitate detectată într-o sesiune monitorizată +Comment[ru]=В отслеживаемом сеансе обнаружена активность +Comment[se]=Gozihuvvon bargovuorru ealáskii +Comment[si]=නිරීක්‍ෂණය කරන වාරයේ ක්‍රියාකාරීත්වයක් හඳුනාගැනිනි +Comment[sk]=Zistená aktivita v monitorovanom sedení +Comment[sl]=V nadzorovani seji je bila zaznana dejavnost +Comment[sr]=Уочена је активност у надгледаној сесији +Comment[sr@ijekavian]=Уочена је активност у надгледаној сесији +Comment[sr@ijekavianlatin]=Uočena je aktivnost u nadgledanoj sesiji +Comment[sr@latin]=Uočena je aktivnost u nadgledanoj sesiji +Comment[sv]=Aktivitet funnen i en bevakad session +Comment[ta]=கண்காணிக்கப்பட்ட அமர்வில் செயல் கண்டுபிடிக்கப்பட்டது +Comment[te]=మానిటర్‌చేయబడిన సెషన్‌నందు క్రియాశీలత గుర్తించబడింది +Comment[tg]=Фаъолият дар сеанси идорашаванда муайян шуд +Comment[th]=ตรวจพบกิจกรรมในวาระงานที่เฝ้าดูอยู่ +Comment[tr]=İzlenen oturumda etkinlik var +Comment[ug]=كۆزىتىلىۋاتقان ئەڭگىمەدە بايقالغان پائالىيەت +Comment[uk]=В сеансі спостереження виявлено дії +Comment[wa]=Activité detectêye dins ene corwaiteye session +Comment[x-test]=xxActivity detected in a monitored sessionxx +Comment[zh_CN]=被监视会话中检测到的活动 +Comment[zh_TW]=監控工作階段中偵測到的活動 +Action=Popup + + +[Event/Silence] +Name=Silence in Monitored Session +Name[af]=Stilte in gemonitorde sessie +Name[ar]=لسكون في الجلسة المُراقبة +Name[ast]=Silenciu en sesión monitorizada +Name[be@latin]=Cišynia ŭ adsočvanaj sesii +Name[bg]=Неактивност в наблюдаваната сесия +Name[bn]=মনিটর করা সেশন চুপচাপ +Name[bn_IN]=নিরীক্ষণ হওয়া সেশানের মধ্যে নৈঃশব্দ্য +Name[bs]=Tišina u nadgledanoj sesiji +Name[ca]=Silenci en una sessió controlada +Name[ca@valencia]=Silenci en una sessió controlada +Name[cs]=Ticho v monitorovaném sezení +Name[csb]=Niżódnô aktiwnota w mònitorowóny sesëji +Name[da]=Inaktivitetet i overvåget session +Name[de]=Keine Aktivität in überwachter Sitzung +Name[el]=Ησυχία σε συνεδρία υπό εποπτεία +Name[en_GB]=Silence in Monitored Session +Name[eo]=Trankvilo en rigardata seanco +Name[es]=Silencio en sesión monitorizada +Name[et]=Vaikus jälgitavas seansis +Name[eu]=Isiltasuna monitorizatutako saioan +Name[fi]=Hiljaisuus valvotussa istunnossa +Name[fr]=Inactivité dans la session surveillée +Name[fy]=Stilte yn observearre sesje +Name[ga]=Ciúnas i Seisiún Monatóirithe +Name[gl]=Silencio na sesión vixiada +Name[gu]=દેખરેખ રાખેલ સત્રમાં શાંતિ +Name[he]=שקט בהפעלה מנוטרת +Name[hi]=मॉनीटर किए जा रहे सत्र में शांति +Name[hne]=मानीटर होवत सत्र मं सांति +Name[hr]=Tišina u nadziranoj sesiji +Name[hsb]=Ćišina we wobkedźbowanym posedźenju +Name[hu]=Üresjárat egy figyelt munkamenetben +Name[ia]=Silentio in session monitorate +Name[id]=Diam di Sesi Termonitor +Name[is]=Þögn í setunni sem fylgst er með +Name[it]=Silenzio in una sessione sorvegliata +Name[ja]=監視中のセッションの休止 +Name[kk]=Қадағалаудағы сеанста тыныштық +Name[km]=ភាព​ស្ងាត់​ក្នុង​សម័យ​ដែល​បាន​ត្រួត​ពិនិត្យ +Name[kn]=ಮೇಲ್ವಿಚಾರಣೆಯಲ್ಲಿರುವ ಅಧಿವೇಶನದಲ್ಲಿನ (ಸೆಶನ್) ಮೌನ +Name[ko]=관찰하는 세션에서 침묵 감지됨 +Name[ku]=Bêdengiya di Danişîna Xuyakirî de +Name[lt]=Tyla stebimoje sesijoje +Name[lv]=Klusums monitorētā sesijā +Name[mai]=देखल गेल सत्र मे मूक +Name[mk]=Тишина во следената сесија +Name[ml]=നിരീക്ഷിച്ചുകൊണ്ടിരിയ്ക്കുന്ന സെഷനില്‍ നിശബ്ദത +Name[mr]=नियंत्रीत सत्र अंतर्गत विराम +Name[nb]=Stillhet i en overvåket økt +Name[nds]=Still binnen beluurt Törn +Name[nl]=Stilte in gevolgde sessie +Name[nn]=Stille i overvaka økt +Name[or]=ଯାଞ୍ଚକୃତ ଅଧିବେଶନରେ ନିରବତା +Name[pa]=ਨਿਗਰਾਨੀ ਅਧੀਨ ਸ਼ੈਸ਼ਨ 'ਚ ਚੁੱਪ +Name[pl]=Brak aktywności w monitorowanej sesji +Name[pt]=Silêncio em Sessão Vigiada +Name[pt_BR]=Silêncio em sessão monitorada +Name[ro]=Liniște în sesiunea monitorizată +Name[ru]=Молчание в отслеживаемом сеансе +Name[si]=නිරීක්‍ෂණය කරන වාරයේ නිශ්ශබ්දතාවක් +Name[sk]=Nečinnosť v monitorovanom sedení +Name[sl]=Nedejavnost v nadzorovani seji +Name[sr]=Тишина у надгледаној сесији +Name[sr@ijekavian]=Тишина у надгледаној сесији +Name[sr@ijekavianlatin]=Tišina u nadgledanoj sesiji +Name[sr@latin]=Tišina u nadgledanoj sesiji +Name[sv]=Tystnad i bevakad session +Name[ta]=கண்காணிக்கப்படும் அமர்வில் அமைதி +Name[te]=మానిటర్‌చేయబడిన సెషన్‌నందలి నిశబ్దత +Name[tg]=Хомӯшӣ дар сеанси идорашаванда +Name[th]=ไม่มีกิจกรรมในวาระงานที่เฝ้าดูอยู่ +Name[tr]=İzlenen Oturumda Sessizlik +Name[ug]=كۆزىتىلىۋاتقان ئەڭگىمەدە جىمجىتلىق +Name[uk]=Тиша в сеансі спостереження +Name[wa]=Silince e corwaiteye session +Name[x-test]=xxSilence in Monitored Sessionxx +Name[zh_CN]=被监视会话中的缄默 +Name[zh_TW]=監控工作階段的靜默 +Comment=Silence detected in a monitored session +Comment[af]=Stilte bespeur in 'n sessie wat gemonitor word +Comment[ar]=اكتُشف سكون في جلسةٍ مُراقبة +Comment[ast]=Silenciu detectáu nuna sesión monitorizada +Comment[be@latin]=U adsočvanaj sesii zaŭvažanaja cišynia. +Comment[bg]=Не е засечена активност в наблюдаваната сесия +Comment[bn]=মনিটর করা একটি সেশন-এ কিছু ঘটছে না +Comment[bn_IN]=নিরীক্ষণ হওয়া সেশানের মধ্যে সনাক্ত হওয়া নৈঃশব্দ্য +Comment[bs]=Uočena je tišina u nadgledanoj sesiji +Comment[ca]=Silenci detectat en una sessió controlada +Comment[ca@valencia]=Silenci detectat en una sessió controlada +Comment[cs]=Detekováno ticho v monitorovaném sezení +Comment[csb]=Aktiwnota nie ósta ùstalonô w mònitorowóny sesëji +Comment[cy]=Datgelwyd distawrwydd mewn sesiwn wedi'i fonitro +Comment[da]=Stilhed detekteret i en overvåget session +Comment[de]=In einer überwachten Sitzung wird keine Aktivität festgestellt +Comment[el]=Επικρατεί ησυχία σε μια συνεδρία υπό εποπτεία +Comment[en_GB]=Silence detected in a monitored session +Comment[eo]=Trovis trankvilon en rigardata seanco +Comment[es]=Silencio detectado en una sesión monitorizada +Comment[et]=Tuvastati vaikus jälgitavas seansis +Comment[eu]=Isiltasuna detektatuta monitorizatutako saioan +Comment[fi]=Hiljaisuutta havaittu tarkkailtavassa istunnossa +Comment[fr]=Inactivité détectée dans une session surveillée +Comment[fy]=Stilte ûntdutsen yn observearre sesje +Comment[ga]=Braitheadh ciúnas i seisiún monatóirithe +Comment[gl]=Detectouse silencio nunha sesión baixo seguimento +Comment[gu]=દેખરેખ રાખેલ સત્રમાં શાંતિ નોંધાઇ +Comment[he]=התגלה שקט בהפעלה מנוטרת +Comment[hi]=मॉनीटर किए जा रहे सत्र में शांति का पता चला +Comment[hne]=मानीटर होवत सत्र मं सांति के पता चलिस +Comment[hr]=Otkrivena je tišina u nadziranoj sesiji +Comment[hsb]=Ćišina zwěsćena we wobkedźbowanym posedźenju +Comment[hu]=Üresjárat észlelve egy figyelt munkamenetben +Comment[ia]=Silentio relevate in un session monitorate +Comment[id]=Diam terdeteksi di sesi yang termonitor +Comment[is]=Ekkert er að gerast í setunni sem fylgst er með +Comment[it]=Silenzio rilevato in una sessione sorvegliata +Comment[ja]=監視中のセッションで休止を検出しました +Comment[kk]=Қадағалаудағы сеанста тыныштық байқалады +Comment[km]=បាន​រក​ឃើញ​ភាព​ស្ងាត់​ក្នុង​សម័យ​ដែល​បាន​ត្រួត​ពិនិត្យ +Comment[kn]=ಮೇಲ್ವಿಚಾರಣೆಯಲ್ಲಿರುವ ಅಧಿವೇಶನದಲ್ಲಿನ (ಸೆಶನ್) ಮೌನವನ್ನು ಪತ್ತೆಹಚ್ಚಲಾಯಿತು +Comment[ko]=관찰하는 세션에서 침묵 감지됨 +Comment[ku]=Bêdengiyên di danişîneke xuyakirî de bidestketî +Comment[lt]=Stebimoje sesijoje pastebėta tyla +Comment[lv]=Ir konstatēts klusums monitorētā sesijā +Comment[mai]=मानीटर कएल जाए रहल सत्र मे शांति क' पता चलल +Comment[mk]=Откриена е тишина во следената сесија +Comment[ml]=നിരീക്ഷിച്ചുകൊണ്ടിരിയ്ക്കുന്ന സെഷനില്‍ നിശബ്ദത +Comment[mr]=नियंत्रीत सत्र अंतर्गत विराम आढळले +Comment[ms]=Senyap dikesan dalam sesi yang dipantau +Comment[nb]=Det er oppdaget stillhet i en økt som er overvåket +Comment[nds]=Still binnen en beluurt Törn opdeckt +Comment[ne]=अनुगमन गरिएको सत्रमा मौन पत्ता लागाइयो +Comment[nl]=Stilte ontdekt in gevolgde sessie +Comment[nn]=Stille oppdaga i overvaka økt +Comment[or]=ଯାଞ୍ଚକୃତ ଅଧିବେଶନରେ ଚିହ୍ନଟ ହୋଇଥିବା ନିରବତା +Comment[pa]=ਇੱਕ ਨਿਗਰਾਨੀ ਅਧੀਨ ਸ਼ੈਸ਼ਨ ਵਿੱਚ ਚੁੱਪ ਹੋਈ +Comment[pl]=Wykryto brak aktywności w monitorowanej sesji +Comment[pt]=Silêncio detectado numa sessão vigiada +Comment[pt_BR]=Silêncio detectado em uma sessão monitorada +Comment[ro]=Liniște detectată într-o sesiune monitorizată +Comment[ru]=Обнаружение молчания в отслеживаемом сеансе +Comment[se]=Gozihuvvon bargovuorru javohuvvui +Comment[si]=නිරීක්‍ෂණය කරන වාරයේ නිශ්ශබ්දතාවක් හඳුනාගැනිනි +Comment[sk]=Zistená nečinnosť v monitorovanom sedení +Comment[sl]=V nadzorovani seji je bila zaznana nedejavnost +Comment[sr]=Уочена је тишина у надгледаној сесији +Comment[sr@ijekavian]=Уочена је тишина у надгледаној сесији +Comment[sr@ijekavianlatin]=Uočena je tišina u nadgledanoj sesiji +Comment[sr@latin]=Uočena je tišina u nadgledanoj sesiji +Comment[sv]=Tystnad funnen i en bevakad session +Comment[ta]=கண்கானிக்கப்பட்ட அமர்வில் அமைதி கண்டுபிடிக்கப்பட்டது +Comment[te]=మానిటర్‌చేయబడిన సెషన్‌నందు నిశ్శబ్దం గుర్తించబడింది +Comment[tg]=Хомӯшӣ дар сеанси идорашаванда муайян шуд +Comment[th]=ไม่มีกิจกรรมในวาระงานที่เฝ้าดูอยู่ +Comment[tr]=İzlenen oturumda sessizlik var +Comment[ug]=كۆزىتىلىۋاتقان ئەڭگىمەدە بايقالغان جىمجىتلىق +Comment[uk]=В сеансі спостереження виявлено тишу +Comment[wa]=Silince detecté dins ene corwaiteye session +Comment[x-test]=xxSilence detected in a monitored sessionxx +Comment[zh_CN]=被监视会话中检测到的缄默 +Comment[zh_TW]=監控工作階段中偵測到靜默時的行為 +Action=Popup + +[Event/Finished] +Name=Session Finished With Non-Zero Status +Name[af]=Sessie het geëindig met nie-zero status +Name[ar]=انتهت الجلسة بحالة غير الصِفر +Name[ast]=La sesión finó con un estáu distintu de cero +Name[be@latin]=Sesija skončyłasia ź nienulovym kodam stanu +Name[bg]=Сесия с изход, различен от 0 +Name[bn]=সেশন নন-জিরো স্ট্যাটাস সমেত সমাপ্ত +Name[bn_IN]=শূণ্য ভিন্ন অন্য মানসহ সেশান সমাপ্তি +Name[bs]=Sesija zaključena nenultim stanjem +Name[ca]=Sessió finalitzada amb un estat diferent de zero +Name[ca@valencia]=Sessió finalitzada amb un estat diferent de zero +Name[cs]=Sezení ukončeno nenulovým stavem +Name[csb]=Zakùńczonô sesëjô z niezerowim sztatusã +Name[da]=Session afsluttet med ikke-nul status +Name[de]=Sitzung mit Nicht-Null-Status beendet +Name[el]=Η συνεδρία τερμάτισε με μη μηδενική κατάσταση +Name[en_GB]=Session Finished With Non-Zero Status +Name[eo]=Seanco finiĝis kun eraro +Name[es]=La sesión terminó con un estado distinto de cero +Name[et]=Seanss lõpetas nullist erineva staatusega +Name[eu]=Ez-zero egoerarekin amaitutako saioa +Name[fi]=Istunto lopetettiin paluuarvolla, joka oli eri kuin nolla +Name[fr]=Session terminée anormalement +Name[fy]=Sesje is foltôge mei in non-zero tastân +Name[ga]=Chríochnaigh an Seisiún le Stádas nach bhfuil 0 +Name[gl]=A sesión rematou cun estado non cero +Name[gu]=સત્ર અ-શૂન્ય સ્થિતિ સાથે પૂર્ણ થયું +Name[he]=ההפעלה הסתיימה במצב השונה מאפס +Name[hi]=सत्र नॉन_जीरो स्थिति पर सम्पन्न हुआ +Name[hne]=सत्र नान_जीरो स्थिति मं पूरा होइस +Name[hr]=Sesija je završila stanjem koje nije nula +Name[hsb]=Posedźenje zakónčena z njenulowym statusom +Name[hu]=Egy munkamenet nem nulla értékkel fejeződött be +Name[ia]=Session finite con Non-Zero Status +Name[id]=Sesi Selesai Dengan Status Tidak-Nol +Name[is]=Setunni lauk með stöðu sem var ekki núll +Name[it]=Sessione completata con stato diverso da zero +Name[ja]=ゼロでないステータスでセッションが終了 +Name[kk]=Сеанс нөл-емес күйімен аяқталды +Name[km]=សម័យ​ដែល​បាន​បញ្ចប់​ដោយ​ស្ថានភាព​មិន​សូន្យ +Name[kn]=ಶೂನ್ಯವಲ್ಲದ (ನಾನ್ ಜೀರೋ) ಸ್ಥಿತಿಗತಿಯೊಂದಿಗೆ ಅಧಿವೇಶನ (ಸೆಶನ್) ಮುಕ್ತಾಯಗೊಂಡಿತು +Name[ko]=세션이 0이 아닌 상태로 끝남 +Name[ku]=Danişîna bi Rewşa Ne-Sifir Qediyayî +Name[lt]=Sesija baigta ne nuliniu statusu +Name[lv]=Sesija beidzās ar nenulles statusu +Name[mai]=सत्र नॉन_जीरो स्थिति पर सम्पन्न भेल +Name[mk]=Сесијата заврши со ненулти статус +Name[ml]=ശൂന്യമല്ലാത്തൊരു ഫലത്തോടെ സെഷന്‍ പുറത്തുകടന്നിരിയ്ക്കുന്നു +Name[mr]=विना-शून्य स्थितीसह सत्र संपन्न झाले +Name[nb]=Økt avsluttet med status forskjellig fra null +Name[nds]=Törn mit Retuurweert anners as Null afslaten +Name[nl]=Sessie voltooid met status non-zero +Name[nn]=Økt fullført med status ulik 0 +Name[or]=ଶୂନ୍ୟହୀନ ସ୍ଥିତି ସହିତ ସମାପ୍ତ ଅଧିବେଶନ +Name[pa]=ਸ਼ੈਸ਼ਨ ਗ਼ੈਰ-ਜ਼ੀਰੋ ਹਾਲਤ ਨਾਲ ਖਤਮ ਹੋਇਆ +Name[pl]=Sesja zakończona z niezerowym kodem powrotu +Name[pt]=Sessão Terminada com Código Diferente de Zero +Name[pt_BR]=Sessão finalizada com status diferente de zero +Name[ro]=Sesiunea s-a încheiat cu stare diferită de zero +Name[ru]=Сеанс завершился с ненулевым состоянием +Name[se]=Bargovuorru nogai ii-nolla stáhtusain +Name[si]=බිංදුවක්-නොවන තත්ව සමඟ වාරය අවසන් විය +Name[sk]=Sedenie ukončené s nenulovým stavom +Name[sl]=Seja se je zaključila z neničelnim stanjem +Name[sr]=Сесија окончана ненултим стањем +Name[sr@ijekavian]=Сесија окончана ненултим стањем +Name[sr@ijekavianlatin]=Sesija okončana nenultim stanjem +Name[sr@latin]=Sesija okončana nenultim stanjem +Name[sv]=Sessionen klar med status skild från noll +Name[ta]=சுழியமற்ற நிலையோடு அமர்வு நிறைவுற்றது +Name[te]=సున్నా-కాని స్థితితో సెషన్ ముగిసినది +Name[tg]=Сеанс бо ҳолати ғайри-сифр ба итмом расид +Name[th]=วาระงานจบการทำงานลงด้วยสถานะที่ไม่ใช่ศูนย์ +Name[tr]=Oturum Sıfırdan Farklı bir Durumla Çıkıldı +Name[ug]=غەيرىي نۆل ھالەتتە تاماملانغان ئەڭگىمە +Name[uk]=Сеанс завершився з ненульовим станом +Name[uz]=Seans nolga teng boʻlmagan holat bilan tugadi +Name[uz@cyrillic]=Сеанс нолга тенг бўлмаган ҳолат билан тугади +Name[wa]=Session fineye avou on statut nén zero +Name[x-test]=xxSession Finished With Non-Zero Statusxx +Name[zh_CN]=非零状态完成的会话 +Name[zh_TW]=工作階段以不正常狀態結束 +Comment=A session has exited with non-zero status +Comment[af]='n Sessie het met 'n nie-zero status geëindig +Comment[ar]=خرجت جلسة بحالة غير الصِفر +Comment[ast]=La sesión finó con un estáu distintu de cero +Comment[be@latin]=Sesija skončyłasia ź nienulovym kodam stanu. +Comment[bg]=Сесията е приключила със състояние, различно от 0 +Comment[bn]=একটি সেশন নন-জিরো স্ট্যাটাস সমেত সমাপ্ত হয়েছে +Comment[bn_IN]=শূণ্য ভিন্ন অন্য মান উৎপন্ন করে সেশান সমাপ্ত হয়েছে +Comment[bs]=Iz sesije se izašlo sa nenultim stanjem +Comment[ca]=Una sessió ha finalitzat amb un estat diferent de zero +Comment[ca@valencia]=Una sessió ha finalitzat amb un estat diferent de zero +Comment[cs]=Sezení bylo ukončeno nenulovým stavem +Comment[csb]=Sesëjô z niezerowim sztatusã òsta zakùńczonô +Comment[cy]=Terfynnodd sesiwn efo cyflwr di-sero +Comment[da]=En session er afsluttet med ikke-nul status +Comment[de]=Eine Sitzung wurde mit einem Status ungleich Null beendet. +Comment[el]=Μια συνεδρία τερμάτισε με μη μηδενική κατάσταση +Comment[en_GB]=A session has exited with non-zero status +Comment[eo]=Seanco finiĝis kun eraro +Comment[es]=La sesión ha terminado con un estado distinto de cero +Comment[et]=Seanss väljus nullist erineva staatusega +Comment[eu]=Saio bat amaitu da ez-zero egoerarekin +Comment[fi]=Istunto lopetettiin paluuarvolla, joka ei ollut nolla +Comment[fr]=Une session s'est terminée anormalement +Comment[fy]=In sesje is einige mei in non-zero tastân +Comment[ga]=Chríochnaigh seisiún le stádas nach bhfuil 0 +Comment[gl]=Unha sesión saíu cun estado distinto de cero +Comment[gu]=સત્ર અ-શૂન્ય સ્થિતિ સાથે બહાર નીકળ્યું +Comment[he]=הפעלה יצאה עם מצב השונה מאפס +Comment[hi]=सत्र नॉन जीरो स्थिति के साथ बाहर हुआ +Comment[hne]=नान जीरो स्थिति मं एक सत्र बाहिर हो गिस +Comment[hr]=Sesija je izašla sa stanjem koje nije nula +Comment[hsb]=Posedźenje přetorhnjena z njenulowym statusom +Comment[hu]=Egy munkamenet nem nulla értékkel fejeződött be +Comment[ia]=Un session ha exite con status non-zero +Comment[id]=Sebuah sesi telah keluar dengan status tidak-nol +Comment[is]=Setu lauk með stöðu sem er ekki núll +Comment[it]=Una sessione è terminata con stato diverso da zero +Comment[ja]=ゼロでないステータスでセッションが終了しました +Comment[ka]=სეანსი დასრულდა არანულოვანი რეზულტატით +Comment[kk]=Сеанс нөл-емес күйімен доғарылды +Comment[km]=សម័យ​បាន​បិទ​ដោយ​​ស្ថានភាព​មិន​សូន្យ +Comment[kn]=ಶೂನ್ಯವಲ್ಲದ (ನಾನ್ ಜೀರೋ) ಸ್ಥಿತಿಗತಿಯೊಂದಿಗೆ ಒಂದು ಅಧಿವೇಶನ (ಸೆಶನ್) ನಿರ್ಗಮಿಸಿದೆ +Comment[ko]=세션이 0이 아닌 상태로 끝났음 +Comment[ku]=Danişîneke ku bi rewşa ne-sifir derketiye +Comment[lt]=Sesija baigė darbą su nenuliniu režimu +Comment[lv]=Sesija beidzās ar nenulles statusu +Comment[mai]=सत्र नॉन जीरो स्थितिक संग बाहर भेल +Comment[mk]=Сесијата излезе со ненулти статус +Comment[ml]=ശൂന്യമല്ലാത്തൊരു ഫലത്തോടെ സെഷന്‍ പുറത്തുകടന്നിരിയ്ക്കുന്നു +Comment[mr]=विना-शून्य स्थितीसह सत्र बाहेर पडले +Comment[ms]=Satu sesi keluar dengan status bukan sifar +Comment[nb]=En økt er avsluttet, med en status forskjellig fra null +Comment[nds]=A Törn wöör mit en anner Retuurweert as Null afslaten +Comment[ne]=शून्य विहिन वस्तुस्थितिसँग एउटा सत्र अन्त्य भएको छ +Comment[nl]=Er is een sessie beëindigd met status non-zero +Comment[nn]=Ei økt er fullført med status ulik 0 +Comment[or]=ଶୂନ୍ୟହୀନ ସ୍ଥିତି ସହିତ ଅଧିବେଶନଟି ପ୍ରସ୍ଥାନ କରିଛି +Comment[pa]=ਇੱਕ ਸ਼ੈਸ਼ਨ ਗ਼ੈਰ-ਜ਼ੀਰੋ ਹਾਲਤ ਨਾਲ ਖਤਮ ਹੋਇਆ +Comment[pl]=Sesja zakończyła się z niezerowym kodem powrotu +Comment[pt]=Uma sessão terminou com um código de saída diferente de zero +Comment[pt_BR]=Uma sessão foi fechada com um status diferente de zero +Comment[ro]=O sesiune s-a terminat cu rezultat diferit de zero +Comment[ru]=Сеанс завершился с ненулевым состоянием +Comment[se]=Bargovuorru nogai ii-nolla stáhtusain +Comment[si]=බින්දුවක්-නොවන තත්වයක් සමඟ වාරය පිටවිය +Comment[sk]=Sedenie bolo ukončené s nenulovým stavom +Comment[sl]=Seja se je zaključila s stanjem, različnim od nič +Comment[sr]=Из сесије се изашло са ненултим стањем +Comment[sr@ijekavian]=Из сесије се изашло са ненултим стањем +Comment[sr@ijekavianlatin]=Iz sesije se izašlo sa nenultim stanjem +Comment[sr@latin]=Iz sesije se izašlo sa nenultim stanjem +Comment[sv]=En session har avslutats med status skild från noll +Comment[ta]=ஒரு கூட்டம் ஒன்றுமில்லாமல் முடிந்தது/ +Comment[te]=సున్నా-కాని స్థితితో ఒక సెషన్ నిష్క్రమించింది. +Comment[tg]=Сеанс бо ҳолати ғайри-сифр хуруҷ карда шуд +Comment[th]=เกิดการออกจากวาระงานด้วยสถานะที่ไม่ใช่ศูนย์ +Comment[tr]=Bir oturum hata vererek çıktı +Comment[ug]=ئەڭگىمە غەيرىي نۆل ھالەتتە چېكىندى +Comment[uk]=Сеанс завершився з ненульовим станом +Comment[uz]=Seans nolga teng boʻlmagan holat bilan tugadi +Comment[uz@cyrillic]=Сеанс нолга тенг бўлмаган ҳолат билан тугади +Comment[wa]=Ene session a moussî foû avou on statut nén zero +Comment[x-test]=xxA session has exited with non-zero statusxx +Comment[zh_CN]=会话以非零状态退出 +Comment[zh_TW]=工作階段以不正常狀態(非零值)結束 +Action=None diff --git a/konsole/desktop/konsolehere.desktop b/konsole/desktop/konsolehere.desktop new file mode 100644 index 00000000..2bcec0e8 --- /dev/null +++ b/konsole/desktop/konsolehere.desktop @@ -0,0 +1,104 @@ +[Desktop Entry] +Type=Service +X-KDE-ServiceTypes=KonqPopupMenu/Plugin +MimeType=inode/directory; +Actions=openTerminalHere; +X-KDE-AuthorizeAction=shell_access + +[Desktop Action openTerminalHere] +TryExec=konsole +Exec=konsole --workdir %f +Icon=utilities-terminal + +Name=Open Terminal Here +Name[af]=Maak Terminaal Hier Oop +Name[ar]=افتح طرفية هنا +Name[ast]=Abrir terminal equí +Name[be]=Адкрыць тэрмінал тут +Name[be@latin]=Adčyni terminał +Name[bg]=Отваряне на терминал тук +Name[bn]=এখানে টার্মিনাল খোলো +Name[bn_IN]=এইস্থানে টার্মিন্যাল আরম্ভ করুন +Name[br]=Digeriñ un termenell amañ +Name[bs]=Otvori terminal ovdje +Name[ca]=Obre un terminal aquí +Name[ca@valencia]=Obri un terminal ací +Name[cs]=Otevřít terminál zde +Name[csb]=Òtemkni tuwò terminal +Name[cy]=Agor Terfynell Yma +Name[da]=Åbn terminal her +Name[de]=Terminal hier öffnen +Name[el]=Άνοιγμα τερματικού εδώ +Name[en_GB]=Open Terminal Here +Name[eo]=Malfermi terminalon ĉi tie +Name[es]=Abrir terminal aquí +Name[et]=Ava siin terminal +Name[eu]=Ireki terminala hemen +Name[fa]=باز کردن پایانه در اینجا +Name[fi]=Avaa pääteikkuna tähän +Name[fr]=Ouvrir un terminal ici +Name[fy]=Terminal iepenje +Name[ga]=Oscail Teirminéal Anseo +Name[gl]=Abrir unha terminal aquí +Name[gu]=અહીં ટર્મિનલ ખોલો +Name[he]=פתיחת מסוף מכאן +Name[hi]=टर्मिनल यहाँ खोलें +Name[hne]=टर्मिनल इहां खोलव +Name[hr]=Ovdje otvori terminal +Name[hsb]=Terminal tu wočinić +Name[hu]=Parancsértelmező megnyitása itt +Name[ia]=Aperi le terminal ci +Name[id]=Buka Terminal Di Sini +Name[is]=Opna skjáhermi hér +Name[it]=Apri terminale qui +Name[ja]=ここでターミナルを開く +Name[ka]=ტერმინალის აქ გახსნა +Name[kk]=Терминалды ашу +Name[km]=បើក​ស្ថានីយ​នៅ​​ទីនេះ +Name[kn]=ಆದೇಶತೆರೆಯನ್ನು (ಟರ್ಮಿನಲ್) ಇಲ್ಲಿ ತೆರೆ +Name[ko]=여기에서 터미널 열기 +Name[ku]=Termînalê li virê veke +Name[lt]=Atverti čia terminalą +Name[lv]=Atvērt termināli šeit +Name[mai]=टर्मिनल एतय खोलू +Name[mk]=Отвори терминал тука +Name[ml]=ഇവിടെ ടെര്‍മിനല്‍ തുറക്കുക +Name[mr]=टर्मिनल येथे उघडा +Name[ms]=Buka Terminal Di Sini +Name[nb]=Åpne terminal her +Name[nds]=Konsool hier opmaken +Name[ne]=यहाँ टर्मिनल खोल्नुहोस् +Name[nl]=Terminal openen +Name[nn]=Opna terminal her +Name[oc]=Dobrir un terminal aicí +Name[or]=ଏଠାରେ ଟର୍ମିନାଲ ଖୋଲନ୍ତୁ +Name[pa]=ਟਰਮੀਨਲ ਇੱਥੇ ਖੋਲ੍ਹੋ +Name[pl]=Otwórz tutaj terminal +Name[pt]=Abrir um Terminal Aqui +Name[pt_BR]=Abrir o terminal aqui +Name[ro]=Deschide un terminal aici +Name[ru]=Открыть терминал в этой папке +Name[se]=Raba terminála dáppe +Name[si]=මෙහි අග්‍රය විවෘත කරන්න +Name[sk]=Tu otvoriť terminál +Name[sl]=Odpri terminal tukaj +Name[sr]=Отвори терминал овде +Name[sr@ijekavian]=Отвори терминал овдје +Name[sr@ijekavianlatin]=Otvori terminal ovdje +Name[sr@latin]=Otvori terminal ovde +Name[sv]=Öppna terminal här +Name[ta]=முனைய இங்கே திற +Name[te]=టెర్మినల్‌ను ఇక్కడ తెరువుము +Name[tg]=Терминалро дар ин ҷо кушоед +Name[th]=เปิดเทอร์มินัลที่นี่ +Name[tr]=Uçbirimi Burada Aç +Name[ug]=بۇ يەردە تېرمىنالنى ئاچ +Name[uk]=Відкрити термінал +Name[uz]=Terminalni shu yerda ochish +Name[uz@cyrillic]=Терминални шу ерда очиш +Name[vi]=Mở cửa sổ dòng lệnh ở đây +Name[wa]=Drovi on terminå chal +Name[xh]=Vula Isiphelo Sendlela Apha +Name[x-test]=xxOpen Terminal Herexx +Name[zh_CN]=在此打开终端 +Name[zh_TW]=在這裡開啟終端機 diff --git a/konsole/desktop/konsolepart.desktop b/konsole/desktop/konsolepart.desktop new file mode 100644 index 00000000..0e13773f --- /dev/null +++ b/konsole/desktop/konsolepart.desktop @@ -0,0 +1,105 @@ +[Desktop Entry] +Type=Service +X-KDE-ServiceTypes=KParts/ReadOnlyPart,inode/directory,TerminalEmulator +Icon=utilities-terminal + +X-KDE-Library=konsolepart +X-KDE-BrowserView-AllowAsDefault=false +X-KDE-BrowserView-HideFromMenus=true +X-KDE-BrowserView-Toggable=true +X-KDE-BrowserView-ToggableView-Orientation=horizontal +X-KDE-BrowserView-FollowActive=true + +X-KDE-AuthorizeAction=shell_access + +Name=Terminal Emulator +Name[af]=Terminaal Emulasie +Name[ar]=محاكي طرفية +Name[ast]=Emulador de terminal +Name[be]=Эмулятар тэрміналу +Name[be@latin]=Emulatar terminała +Name[bg]=Конзолна програма +Name[bn]=টার্মিনাল এমুলেটর +Name[bn_IN]=টার্মিন্যাল এমুলেটর +Name[br]=Kendarvanerezh termenell +Name[bs]=Oponašanje terminala +Name[ca]=Emulador de terminal +Name[ca@valencia]=Emulador de terminal +Name[cs]=Emulátor terminálu +Name[csb]=Emùlator terminala +Name[cy]=Efelychydd Terfynell +Name[da]=Terminalemulator +Name[de]=Terminal-Emulator +Name[el]=Προσομοιωτής τερματικού +Name[en_GB]=Terminal Emulator +Name[eo]=Terminala imitilo +Name[es]=Emulador de terminal +Name[et]=Terminaliemulaator +Name[eu]=Terminalaren emuladorea +Name[fa]=مقلد پایانه +Name[fi]=Pääte-emulaattori +Name[fr]=Émulateur de terminal +Name[fy]=Terminal +Name[ga]=Aithriseoir Teirminéil +Name[gl]=Emulador de terminal +Name[gu]=ટર્મિનલ ઇમ્યુલેટર +Name[he]=מדמה מסוף +Name[hi]=टर्मिनल एमुलेटर +Name[hne]=टर्मिनल एमुलेटर +Name[hr]=Emulator terminala +Name[hsb]=Emulator za terminal +Name[hu]=Parancsértelmező +Name[ia]=Emulator de terminal +Name[id]=Emulator Terminal +Name[is]=Skjáhermir KDE +Name[it]=Emulatore di terminale +Name[ja]=ターミナルエミュレータ +Name[ka]=ტერმინალის ემულატორი +Name[kk]=Терминал эмуляторы +Name[km]=កម្មវិធី​​ត្រាប់​តាម​ស្ថានីយ +Name[kn]=ಆದೇಶತೆರೆ ಅನುವರ್ತಕ (ಎಮುಲೇಟರ್) +Name[ko]=터미널 에뮬레이터 +Name[ku]=Emulatora Termînalê +Name[lt]=Terminalo emuliatorius +Name[lv]=Termināla emulators +Name[mai]=टर्मिनल एमुलेटर +Name[mk]=Терминалски емулатор +Name[ml]=ടെര്‍മിനല്‍ എമുലേറ്റര്‍ +Name[mr]=टर्मिनल एम्युलेटर +Name[ms]=Pelagak Terminal +Name[nb]=Terminalemulator +Name[nds]=Terminal-Emulator +Name[ne]=टर्मिनल इमुलेटर +Name[nl]=Terminal +Name[nn]=Terminalemulator +Name[or]=ଟର୍ମିନାଲ ଯାନ୍ତ୍ରାନୁକରଣ +Name[pa]=ਟਰਮੀਨਲ ਸਮਰੂਪ +Name[pl]=Emulator terminala +Name[pt]=Emulador de Terminal +Name[pt_BR]=Emulador de terminal +Name[ro]=Emulator de terminal +Name[ru]=Эмулятор терминала +Name[se]=Terminálemuláhtor +Name[si]=අග්‍ර අනුකාරක +Name[sk]=Emulátor terminálu +Name[sl]=Posnemovalnik terminala +Name[sr]=Емулатор терминала +Name[sr@ijekavian]=Емулатор терминала +Name[sr@ijekavianlatin]=Emulator terminala +Name[sr@latin]=Emulator terminala +Name[sv]=Terminalemulator +Name[ta]=கடைசி போட்டியாளர் +Name[te]=టెర్మినల్ ఎములేటర్ +Name[tg]=Тақлидгари терминал +Name[th]=โปรแกรมจำลองเทอร์มินัล +Name[tr]=Uçbirim Öykünücüsü +Name[ug]=تېرمىنال تەقلىدلەشتۈرگۈچ +Name[uk]=Емулятор термінала +Name[uz]=Terminal emulyatori +Name[uz@cyrillic]=Терминал эмулятори +Name[vi]=Mô phỏng thiết bị cuối +Name[wa]=Programes terminås +Name[xh]=Umzami kulinganisela ngokulinganayo Wendlela yesiphelo +Name[x-test]=xxTerminal Emulatorxx +Name[zh_CN]=终端仿真器 +Name[zh_TW]=終端機模擬程式 diff --git a/konsole/desktop/konsolerun.desktop b/konsole/desktop/konsolerun.desktop new file mode 100644 index 00000000..44ec3eae --- /dev/null +++ b/konsole/desktop/konsolerun.desktop @@ -0,0 +1,55 @@ +[Desktop Entry] +Type=Service +X-KDE-ServiceTypes=KonqPopupMenu/Plugin +MimeType=application/x-executable; +Actions=runInKonsole; +X-KDE-AuthorizeAction=shell_access + +[Desktop Action runInKonsole] +TryExec=konsole +Exec=konsole --hold -e %f +Icon=utilities-terminal + +Name=Run In Konsole +Name[ar]=شغّل في كونسول +Name[ca]=Executa en el Konsole +Name[ca@valencia]=Executa en el Konsole +Name[cs]=Spustit v Konsoli +Name[da]=Kør i Konsole +Name[de]=In Konsole ausführen +Name[el]=Εκτέλεση στο Konsole +Name[en_GB]=Run In Konsole +Name[es]=Ejecutar en Konsole +Name[et]=Käivita Konsoolis +Name[fi]=Suorita Konsolessa +Name[fr]=Lancer dans Konsole +Name[gl]=Executar en Konsole +Name[he]=הפעל בתוך Konsole +Name[hu]=Futtatás a Konsole programban +Name[ia]=Executa in Konsole +Name[id]=Jalankan Di Konsole +Name[is]=Keyra í Konsole skjáhermi +Name[it]=Esegui in Konsole +Name[ja]=Konsole で実行 +Name[ko]=Konsole에서 실행 +Name[nb]=Kjør i Konsole +Name[nds]=Binnen Konsole lopen +Name[nl]=In Konsole uitvoeren +Name[pa]=ਕੰਨਸੋਲ ਵਿੱਚ ਚਲਾਓ +Name[pl]=Uruchom w konsoli +Name[pt]=Executar no Konsole +Name[pt_BR]=Executar no Konsole +Name[ro]=Rulează în Konsolă +Name[ru]=Запустить в Konsole +Name[sk]=Spustiť v Konsole +Name[sl]=Zaženi v Konsole +Name[sr]=Изврши у Конзоли +Name[sr@ijekavian]=Изврши у Конзоли +Name[sr@ijekavianlatin]=Izvrši u Konsoli +Name[sr@latin]=Izvrši u Konsoli +Name[sv]=Kör i Terminal +Name[tr]=Konsole İçinde Çalıştır +Name[uk]=Запустити у Konsole +Name[x-test]=xxRun In Konsolexx +Name[zh_CN]=在 Konsole 中运行 +Name[zh_TW]=在 Konsole 中執行 diff --git a/konsole/desktop/konsoleui.rc b/konsole/desktop/konsoleui.rc new file mode 100644 index 00000000..1dd4f696 --- /dev/null +++ b/konsole/desktop/konsoleui.rc @@ -0,0 +1,47 @@ + + + + + File + + + + + + + + + + Edit + + + View + Split View + + + + + + + + + + + + + + Settings + + + + + + + + + + + Help + + + diff --git a/konsole/desktop/partui.rc b/konsole/desktop/partui.rc new file mode 100644 index 00000000..c0bc84ed --- /dev/null +++ b/konsole/desktop/partui.rc @@ -0,0 +1,28 @@ + + + + + + + + + + + + + S&crollback + + + + + + + + + + + + + + + diff --git a/konsole/desktop/sessionui.rc b/konsole/desktop/sessionui.rc new file mode 100644 index 00000000..a74c084e --- /dev/null +++ b/konsole/desktop/sessionui.rc @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/konsole/desktop/terminalemulator.desktop b/konsole/desktop/terminalemulator.desktop new file mode 100644 index 00000000..c59b6e48 --- /dev/null +++ b/konsole/desktop/terminalemulator.desktop @@ -0,0 +1,96 @@ +[Desktop Entry] +Type=ServiceType +X-KDE-ServiceType=TerminalEmulator +X-KDE-Derived=KParts/ReadOnlyPart + +Name=Terminal Emulator +Name[af]=Terminaal Emulasie +Name[ar]=محاكي طرفية +Name[ast]=Emulador de terminal +Name[be]=Эмулятар тэрміналу +Name[be@latin]=Emulatar terminała +Name[bg]=Конзолна програма +Name[bn]=টার্মিনাল এমুলেটর +Name[bn_IN]=টার্মিন্যাল এমুলেটর +Name[br]=Kendarvanerezh termenell +Name[bs]=Oponašanje terminala +Name[ca]=Emulador de terminal +Name[ca@valencia]=Emulador de terminal +Name[cs]=Emulátor terminálu +Name[csb]=Emùlator terminala +Name[cy]=Efelychydd Terfynell +Name[da]=Terminalemulator +Name[de]=Terminal-Emulator +Name[el]=Προσομοιωτής τερματικού +Name[en_GB]=Terminal Emulator +Name[eo]=Terminala imitilo +Name[es]=Emulador de terminal +Name[et]=Terminaliemulaator +Name[eu]=Terminalaren emuladorea +Name[fa]=مقلد پایانه +Name[fi]=Pääte-emulaattori +Name[fr]=Émulateur de terminal +Name[fy]=Terminal +Name[ga]=Aithriseoir Teirminéil +Name[gl]=Emulador de terminal +Name[gu]=ટર્મિનલ ઇમ્યુલેટર +Name[he]=מדמה מסוף +Name[hi]=टर्मिनल एमुलेटर +Name[hne]=टर्मिनल एमुलेटर +Name[hr]=Emulator terminala +Name[hsb]=Emulator za terminal +Name[hu]=Parancsértelmező +Name[ia]=Emulator de terminal +Name[id]=Emulator Terminal +Name[is]=Skjáhermir KDE +Name[it]=Emulatore di terminale +Name[ja]=ターミナルエミュレータ +Name[ka]=ტერმინალის ემულატორი +Name[kk]=Терминал эмуляторы +Name[km]=កម្មវិធី​​ត្រាប់​តាម​ស្ថានីយ +Name[kn]=ಆದೇಶತೆರೆ ಅನುವರ್ತಕ (ಎಮುಲೇಟರ್) +Name[ko]=터미널 에뮬레이터 +Name[ku]=Emulatora Termînalê +Name[lt]=Terminalo emuliatorius +Name[lv]=Termināla emulators +Name[mai]=टर्मिनल एमुलेटर +Name[mk]=Терминалски емулатор +Name[ml]=ടെര്‍മിനല്‍ എമുലേറ്റര്‍ +Name[mr]=टर्मिनल एम्युलेटर +Name[ms]=Pelagak Terminal +Name[nb]=Terminalemulator +Name[nds]=Terminal-Emulator +Name[ne]=टर्मिनल इमुलेटर +Name[nl]=Terminal +Name[nn]=Terminalemulator +Name[or]=ଟର୍ମିନାଲ ଯାନ୍ତ୍ରାନୁକରଣ +Name[pa]=ਟਰਮੀਨਲ ਸਮਰੂਪ +Name[pl]=Emulator terminala +Name[pt]=Emulador de Terminal +Name[pt_BR]=Emulador de terminal +Name[ro]=Emulator de terminal +Name[ru]=Эмулятор терминала +Name[se]=Terminálemuláhtor +Name[si]=අග්‍ර අනුකාරක +Name[sk]=Emulátor terminálu +Name[sl]=Posnemovalnik terminala +Name[sr]=Емулатор терминала +Name[sr@ijekavian]=Емулатор терминала +Name[sr@ijekavianlatin]=Emulator terminala +Name[sr@latin]=Emulator terminala +Name[sv]=Terminalemulator +Name[ta]=கடைசி போட்டியாளர் +Name[te]=టెర్మినల్ ఎములేటర్ +Name[tg]=Тақлидгари терминал +Name[th]=โปรแกรมจำลองเทอร์มินัล +Name[tr]=Uçbirim Öykünücüsü +Name[ug]=تېرمىنال تەقلىدلەشتۈرگۈچ +Name[uk]=Емулятор термінала +Name[uz]=Terminal emulyatori +Name[uz@cyrillic]=Терминал эмулятори +Name[vi]=Mô phỏng thiết bị cuối +Name[wa]=Programes terminås +Name[xh]=Umzami kulinganisela ngokulinganayo Wendlela yesiphelo +Name[x-test]=xxTerminal Emulatorxx +Name[zh_CN]=终端仿真器 +Name[zh_TW]=終端機模擬程式 diff --git a/konsole/src/Application.cpp b/konsole/src/Application.cpp new file mode 100644 index 00000000..a0087b0c --- /dev/null +++ b/konsole/src/Application.cpp @@ -0,0 +1,469 @@ +/* + Copyright 2006-2008 by Robert Knight + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +// Own +#include "Application.h" + +// Qt +#include +#include +#include + +// KDE +#include +#include +#include +#include +#include + +// Konsole +#include "SessionManager.h" +#include "ProfileManager.h" +#include "MainWindow.h" +#include "Session.h" +#include "ShellCommand.h" + +using namespace Konsole; + +Application::Application() : KUniqueApplication() +{ + init(); +} + +void Application::init() +{ + _backgroundInstance = 0; + +} + +Application::~Application() +{ + SessionManager::instance()->closeAllSessions(); + ProfileManager::instance()->saveSettings(); +} + +MainWindow* Application::newMainWindow() +{ + MainWindow* window = new MainWindow(); + + connect(window, SIGNAL(newWindowRequest(Profile::Ptr,QString)), + this, SLOT(createWindow(Profile::Ptr,QString))); + connect(window, SIGNAL(viewDetached(Session*)), + this, SLOT(detachView(Session*))); + + return window; +} + +void Application::createWindow(Profile::Ptr profile, const QString& directory) +{ + MainWindow* window = newMainWindow(); + window->createSession(profile, directory); + window->show(); +} + +void Application::detachView(Session* session) +{ + MainWindow* window = newMainWindow(); + window->createView(session); + // Since user is dragging and dropping, move dnd window to where + // the user has the cursor (correct multiple monitor setups). + window->move(QCursor::pos()); + window->show(); +} + +int Application::newInstance() +{ + static bool firstInstance = true; + + KCmdLineArgs* args = KCmdLineArgs::parsedArgs(); + + // handle session management + if ((args->count() != 0) || !firstInstance || !isSessionRestored()) { + // check for arguments to print help or other information to the + // terminal, quit if such an argument was found + if (processHelpArgs(args)) + return 0; + + // create a new window or use an existing one + MainWindow* window = processWindowArgs(args); + + if (args->isSet("tabs-from-file")) { + // create new session(s) as described in file + processTabsFromFileArgs(args, window); + } else { + // select profile to use + Profile::Ptr baseProfile = processProfileSelectArgs(args); + + // process various command-line options which cause a property of the + // selected profile to be changed + Profile::Ptr newProfile = processProfileChangeArgs(args, baseProfile); + + // create new session + Session* session = window->createSession(newProfile, QString()); + + if (!args->isSet("close")) { + session->setAutoClose(false); + } + } + + // if the background-mode argument is supplied, start the background + // session ( or bring to the front if it already exists ) + if (args->isSet("background-mode")) { + startBackgroundMode(window); + } else { + // Qt constrains top-level windows which have not been manually + // resized (via QWidget::resize()) to a maximum of 2/3rds of the + // screen size. + // + // This means that the terminal display might not get the width/ + // height it asks for. To work around this, the widget must be + // manually resized to its sizeHint(). + // + // This problem only affects the first time the application is run. + // run. After that KMainWindow will have manually resized the + // window to its saved size at this point (so the Qt::WA_Resized + // attribute will be set) + if (!window->testAttribute(Qt::WA_Resized)) + window->resize(window->sizeHint()); + + window->show(); + } + } + + firstInstance = false; + args->clear(); + return 0; +} + +/* Documentation for tab file: + * + * ;; is the token separator + * # at the beginning of line results in line being ignored. + * supported tokens are title, command and profile. + * + * Note that the title is static and the tab will close when the + * command is complete (do not use --noclose). You can start new tabs. + * + * Examples: +title: This is the title;; command: ssh jupiter +title: Top this!;; command: top +#this line is comment +command: ssh earth +profile: Zsh +*/ +void Application::processTabsFromFileArgs(KCmdLineArgs* args, + MainWindow* window) +{ + // Open tab configuration file + const QString tabsFileName(args->getOption("tabs-from-file")); + QFile tabsFile(tabsFileName); + if (!tabsFile.open(QFile::ReadOnly)) { + kWarning() << "ERROR: Cannot open tabs file " + << tabsFileName.toLocal8Bit().data(); + quit(); + } + + unsigned int sessions = 0; + while (!tabsFile.atEnd()) { + QString lineString(tabsFile.readLine().trimmed()); + if ((lineString.isEmpty()) || (lineString[0] == '#')) + continue; + + QHash lineTokens; + QStringList lineParts = lineString.split(";;", QString::SkipEmptyParts); + + for (int i = 0; i < lineParts.size(); ++i) { + QString key = lineParts.at(i).section(':', 0, 0).trimmed().toLower(); + QString value = lineParts.at(i).section(':', 1, -1).trimmed(); + lineTokens[key] = value; + } + // should contain at least one of 'command' and 'profile' + if (lineTokens.contains("command") || lineTokens.contains("profile")) { + createTabFromArgs(args, window, lineTokens); + sessions++; + } else { + kWarning() << "Each line should contain at least one of 'command' and 'profile'."; + } + } + tabsFile.close(); + + if (sessions < 1) { + kWarning() << "No valid lines found in " + << tabsFileName.toLocal8Bit().data(); + quit(); + } +} + +void Application::createTabFromArgs(KCmdLineArgs* args, MainWindow* window, + const QHash& tokens) +{ + const QString& title = tokens["title"]; + const QString& command = tokens["command"]; + const QString& profile = tokens["profile"]; + const QString& workdir = tokens["workdir"]; + + Profile::Ptr baseProfile; + if (!profile.isEmpty()) { + baseProfile = ProfileManager::instance()->loadProfile(profile); + } + if (!baseProfile) { + // fallback to default profile + baseProfile = ProfileManager::instance()->defaultProfile(); + } + + Profile::Ptr newProfile = Profile::Ptr(new Profile(baseProfile)); + newProfile->setHidden(true); + + // FIXME: the method of determining whether to use newProfile does not + // scale well when we support more fields in the future + bool shouldUseNewProfile = false; + + if (!command.isEmpty()) { + newProfile->setProperty(Profile::Command, command); + newProfile->setProperty(Profile::Arguments, command.split(' ')); + shouldUseNewProfile = true; + } + + if (!title.isEmpty()) { + newProfile->setProperty(Profile::LocalTabTitleFormat, title); + newProfile->setProperty(Profile::RemoteTabTitleFormat, title); + shouldUseNewProfile = true; + } + + if (args->isSet("workdir")) { + newProfile->setProperty(Profile::Directory, args->getOption("workdir")); + shouldUseNewProfile = true; + } + + if (!workdir.isEmpty()) { + newProfile->setProperty(Profile::Directory, workdir); + shouldUseNewProfile = true; + } + + // Create the new session + Profile::Ptr theProfile = shouldUseNewProfile ? newProfile : baseProfile; + Session* session = window->createSession(theProfile, QString()); + + if (!args->isSet("close")) { + session->setAutoClose(false); + } + + if (!window->testAttribute(Qt::WA_Resized)) { + window->resize(window->sizeHint()); + } + + // FIXME: this ugly hack here is to make the session start running, so that + // its tab title is displayed as expected. + // + // This is another side effect of the commit fixing BKO 176902. + window->show(); + window->hide(); +} + +MainWindow* Application::processWindowArgs(KCmdLineArgs* args) +{ + MainWindow* window = 0; + if (args->isSet("new-tab")) { + QListIterator iter(topLevelWidgets()); + iter.toBack(); + while (iter.hasPrevious()) { + window = qobject_cast(iter.previous()); + if (window != 0) + break; + } + } + + if (window == 0) { + window = newMainWindow(); + + // override default menubar visibility + if (args->isSet("show-menubar")) { + window->setMenuBarInitialVisibility(true); + } + if (args->isSet("hide-menubar")) { + window->setMenuBarInitialVisibility(false); + } + if (args->isSet("fullscreen")) { + window->viewFullScreen(true); + } + + // override default tabbbar visibility + // FIXME: remove those magic number + // see ViewContainer::NavigationVisibility + if (args->isSet("show-tabbar")) { + // always show + window->setNavigationVisibility(0); + } + if (args->isSet("hide-tabbar")) { + // never show + window->setNavigationVisibility(2); + } + } + return window; +} + +Profile::Ptr Application::processProfileSelectArgs(KCmdLineArgs* args) +{ + Profile::Ptr defaultProfile = ProfileManager::instance()->defaultProfile(); + + if (args->isSet("profile")) { + Profile::Ptr profile = ProfileManager::instance()->loadProfile( + args->getOption("profile")); + if (profile) + return profile; + } else if (args->isSet("fallback-profile")) { + Profile::Ptr profile = ProfileManager::instance()->loadProfile("FALLBACK/"); + if (profile) + return profile; + } + + return defaultProfile; +} + +bool Application::processHelpArgs(KCmdLineArgs* args) +{ + if (args->isSet("list-profiles")) { + listAvailableProfiles(); + return true; + } else if (args->isSet("list-profile-properties")) { + listProfilePropertyInfo(); + return true; + } + return false; +} + +void Application::listAvailableProfiles() +{ + QStringList paths = ProfileManager::instance()->availableProfilePaths(); + + foreach(const QString& path, paths) { + QFileInfo info(path); + printf("%s\n", info.completeBaseName().toLocal8Bit().constData()); + } + + quit(); +} + +void Application::listProfilePropertyInfo() +{ + Profile::Ptr tempProfile = ProfileManager::instance()->defaultProfile(); + const QStringList names = tempProfile->propertiesInfoList(); + + foreach(const QString& name, names) { + printf("%s\n", name.toLocal8Bit().constData()); + } + + quit(); +} + +Profile::Ptr Application::processProfileChangeArgs(KCmdLineArgs* args, Profile::Ptr baseProfile) +{ + bool shouldUseNewProfile = false; + + Profile::Ptr newProfile = Profile::Ptr(new Profile(baseProfile)); + newProfile->setHidden(true); + + // change the initial working directory + if (args->isSet("workdir")) { + newProfile->setProperty(Profile::Directory, args->getOption("workdir")); + shouldUseNewProfile = true; + } + + // temporary changes to profile options specified on the command line + foreach(const QString & value , args->getOptionList("p")) { + ProfileCommandParser parser; + + QHashIterator iter(parser.parse(value)); + while (iter.hasNext()) { + iter.next(); + newProfile->setProperty(iter.key(), iter.value()); + } + + shouldUseNewProfile = true; + } + + // run a custom command + if (args->isSet("e")) { + QString commandExec = args->getOption("e"); + QStringList commandArguments; + + // Note: KCmdLineArgs::count() return the number of arguments + // that aren't options. + if (args->count() == 0 && KStandardDirs::findExe(commandExec).isEmpty()) { + // Example: konsole -e "man ls" + ShellCommand shellCommand(args->getOption("e")); + commandExec = shellCommand.command(); + commandArguments = shellCommand.arguments(); + } else { + // Example: konsole -e man ls + commandArguments << commandExec; + for ( int i = 0 ; i < args->count() ; i++ ) + commandArguments << args->arg(i); + } + + if (commandExec.startsWith(QLatin1String("./"))) + commandExec = QDir::currentPath() + commandExec.mid(1); + + newProfile->setProperty(Profile::Command, commandExec); + newProfile->setProperty(Profile::Arguments, commandArguments); + + shouldUseNewProfile = true; + } + + if (shouldUseNewProfile) { + return newProfile; + } else { + return baseProfile; + } +} + +void Application::startBackgroundMode(MainWindow* window) +{ + if (_backgroundInstance) { + return; + } + + KAction* action = window->actionCollection()->addAction("toggle-background-window"); + action->setObjectName(QLatin1String("Konsole Background Mode")); + action->setText(i18n("Toggle Background Window")); + action->setGlobalShortcut(KShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_F12))); + + connect(action, SIGNAL(triggered()), + this, SLOT(toggleBackgroundInstance())); + + _backgroundInstance = window; +} + +void Application::toggleBackgroundInstance() +{ + Q_ASSERT(_backgroundInstance); + + if (!_backgroundInstance->isVisible()) { + _backgroundInstance->show(); + // ensure that the active terminal display has the focus. Without + // this, an odd problem occurred where the focus widget would change + // each time the background instance was shown + _backgroundInstance->setFocus(); + } else { + _backgroundInstance->hide(); + } +} + +#include "moc_Application.cpp" + diff --git a/konsole/src/Application.h b/konsole/src/Application.h new file mode 100644 index 00000000..ae9b1fe5 --- /dev/null +++ b/konsole/src/Application.h @@ -0,0 +1,90 @@ +/* + Copyright 2007-2008 by Robert Knight + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +#ifndef APPLICATION_H +#define APPLICATION_H + +// KDE +#include + +// Konsole +#include "Profile.h" + +class KCmdLineArgs; + +namespace Konsole +{ +class MainWindow; +class Session; + +/** + * The Konsole Application. + * + * The application consists of one or more main windows and a set of + * factories to create new sessions and views. + * + * To create a new main window with a default terminal session, call + * the newInstance(). Empty main windows can be created using newMainWindow(). + * + * The factory used to create new terminal sessions can be retrieved using + * the sessionManager() accessor. + */ +class Application : public KUniqueApplication +{ + Q_OBJECT + +public: + /** Constructs a new Konsole application. */ + Application(); + + virtual ~Application(); + + /** Creates a new main window and opens a default terminal session */ + virtual int newInstance(); + + /** + * Creates a new, empty main window and connects to its newSessionRequest() + * and newWindowRequest() signals to trigger creation of new sessions or + * windows when then they are emitted. + */ + MainWindow* newMainWindow(); + +private slots: + void createWindow(Profile::Ptr profile , const QString& directory); + void detachView(Session* session); + + void toggleBackgroundInstance(); + +private: + void init(); + void listAvailableProfiles(); + void listProfilePropertyInfo(); + void startBackgroundMode(MainWindow* window); + bool processHelpArgs(KCmdLineArgs* args); + MainWindow* processWindowArgs(KCmdLineArgs* args); + Profile::Ptr processProfileSelectArgs(KCmdLineArgs* args); + Profile::Ptr processProfileChangeArgs(KCmdLineArgs* args, Profile::Ptr baseProfile); + void processTabsFromFileArgs(KCmdLineArgs* args, MainWindow* window); + void createTabFromArgs(KCmdLineArgs* args, MainWindow* window, + const QHash&); + + MainWindow* _backgroundInstance; +}; +} +#endif // APPLICATION_H diff --git a/konsole/src/BookmarkHandler.cpp b/konsole/src/BookmarkHandler.cpp new file mode 100644 index 00000000..abcfb516 --- /dev/null +++ b/konsole/src/BookmarkHandler.cpp @@ -0,0 +1,158 @@ +/* This file was part of the KDE libraries + + Copyright 2002 Carsten Pfeiffer + Copyright 2007-2008 Robert Knight + + library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation, version 2 + or ( at your option ), any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +// Born as kdelibs/kio/kfile/kfilebookmarkhandler.cpp + +// Own +#include "BookmarkHandler.h" + +// Qt +#include + +// KDE +#include +#include +#include + +// Konsole +#include "ViewProperties.h" + +using namespace Konsole; + +BookmarkHandler::BookmarkHandler(KActionCollection* collection, + KMenu* menu, + bool toplevel, + QObject* parent) + : QObject(parent), + KBookmarkOwner(), + _menu(menu), + _toplevel(toplevel), + _activeView(0) +{ + setObjectName(QLatin1String("BookmarkHandler")); + + _file = KStandardDirs::locate("data", "konsole/bookmarks.xml"); + if (_file.isEmpty()) + _file = KStandardDirs::locateLocal("data", "konsole/bookmarks.xml"); + + KBookmarkManager* manager = KBookmarkManager::managerForFile(_file, "konsole"); + + manager->setUpdate(true); + + if (toplevel) + _bookmarkMenu = new KBookmarkMenu(manager, this, _menu, collection); + else + _bookmarkMenu = new KBookmarkMenu(manager, this, _menu, 0); +} + +BookmarkHandler::~BookmarkHandler() +{ + delete _bookmarkMenu; +} + +void BookmarkHandler::openBookmark(const KBookmark& bm, Qt::MouseButtons, Qt::KeyboardModifiers) +{ + emit openUrl(bm.url()); +} +void BookmarkHandler::openFolderinTabs(const KBookmarkGroup& group) +{ + emit openUrls(group.groupUrlList()); +} +bool BookmarkHandler::enableOption(BookmarkOption option) const +{ + if (option == ShowAddBookmark || option == ShowEditBookmark) + return _toplevel; + else + return KBookmarkOwner::enableOption(option); +} + +QString BookmarkHandler::currentUrl() const +{ + return urlForView(_activeView); +} + +QString BookmarkHandler::urlForView(ViewProperties* view) const +{ + if (view) + return view->url().prettyUrl(); + else + return QString(); +} + +QString BookmarkHandler::currentTitle() const +{ + return titleForView(_activeView); +} + +QString BookmarkHandler::titleForView(ViewProperties* view) const +{ + const KUrl& url = view ? view->url() : KUrl(); + if (url.isLocalFile()) { + QString path = url.path(); + + path = KShell::tildeExpand(path); + path = QFileInfo(path).baseName(); + + return path; + } else if (url.hasHost()) { + if (url.hasUser()) + return i18nc("@item:inmenu The user's name and host they are connected to via ssh", "%1 on %2", url.user(), url.host()); + else + return i18nc("@item:inmenu The host the user is connected to via ssh", "%1", url.host()); + } + + return url.prettyUrl(); +} + +bool BookmarkHandler::supportsTabs() const +{ + return true; +} + +QList > BookmarkHandler::currentBookmarkList() const +{ + QList > list; + + foreach(ViewProperties* view, _views) { + list << QPair(titleForView(view) , urlForView(view)); + } + + return list; +} + +void BookmarkHandler::setViews(const QList& views) +{ + _views = views; +} +QList BookmarkHandler::views() const +{ + return _views; +} +void BookmarkHandler::setActiveView(ViewProperties* view) +{ + _activeView = view; +} +ViewProperties* BookmarkHandler::activeView() const +{ + return _activeView; +} + +#include "moc_BookmarkHandler.cpp" diff --git a/konsole/src/BookmarkHandler.h b/konsole/src/BookmarkHandler.h new file mode 100644 index 00000000..53bb0d04 --- /dev/null +++ b/konsole/src/BookmarkHandler.h @@ -0,0 +1,126 @@ +/* This file was part of the KDE libraries + + Copyright 2002 Carsten Pfeiffer + Copyright 2007-2008 Robert Knight + + library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation, version 2 + or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +// Born as kdelibs/kio/kfile/kfilebookmarkhandler.h + +#ifndef BOOKMARKHANDLER_H +#define BOOKMARKHANDLER_H + +// KDE +#include + +// Konsole +#include "konsoleprivate_export.h" + +class KMenu; +class KBookmarkMenu; +class KActionCollection; + +namespace Konsole +{ +class ViewProperties; + +/** + * This class handles the communication between the bookmark menu and the active session, + * providing a suggested title and URL when the user clicks the "Add Bookmark" item in + * the bookmarks menu. + * + * The bookmark handler is associated with a session controller, which is used to + * determine the working URL of the current session. When the user changes the active + * view, the bookmark handler's controller should be changed using setController() + * + * When the user selects a bookmark, the openUrl() signal is emitted. + */ +class KONSOLEPRIVATE_EXPORT BookmarkHandler : public QObject, public KBookmarkOwner +{ + Q_OBJECT + +public: + /** + * Constructs a new bookmark handler for Konsole bookmarks. + * + * @param collection The collection which the bookmark menu's actions should be added to + * @param menu The menu which the bookmark actions should be added to + * @param toplevel TODO: Document me + * @param parent The parent object + */ + BookmarkHandler(KActionCollection* collection , KMenu* menu, bool toplevel , QObject* parent); + ~BookmarkHandler(); + + virtual QString currentUrl() const; + virtual QString currentTitle() const; + virtual bool enableOption(BookmarkOption option) const; + virtual bool supportsTabs() const; + virtual QList > currentBookmarkList() const; + virtual void openFolderinTabs(const KBookmarkGroup& group); + + /** + * Returns the menu which this bookmark handler inserts its actions into. + */ + KMenu* menu() const { + return _menu; + } + + QList views() const; + ViewProperties* activeView() const; + +public slots: + /** + * + */ + void setViews(const QList& views); + + void setActiveView(ViewProperties* view); + +signals: + /** + * Emitted when the user selects a bookmark from the bookmark menu. + * + * @param url The url of the bookmark which was selected by the user. + */ + void openUrl(const KUrl& url); + + /** + * Emitted when the user selects 'Open Folder in Tabs' + * from the bookmark menu. + * + * @param urls The urls of the bookmarks in the folder whose + * 'Open Folder in Tabs' action was triggered + */ + void openUrls(const QList& urls); + +private slots: + void openBookmark(const KBookmark& bm, Qt::MouseButtons, Qt::KeyboardModifiers); + +private: + QString titleForView(ViewProperties* view) const; + QString urlForView(ViewProperties* view) const; + + KMenu* _menu; + KBookmarkMenu* _bookmarkMenu; + QString _file; + bool _toplevel; + ViewProperties* _activeView; + QList _views; +}; +} + +#endif // BOOKMARKHANDLER_H diff --git a/konsole/src/CMakeLists.txt b/konsole/src/CMakeLists.txt new file mode 100644 index 00000000..8a6be4e6 --- /dev/null +++ b/konsole/src/CMakeLists.txt @@ -0,0 +1,180 @@ +include(CheckIncludeFiles) + +### BSD util library +find_library(UTIL_LIBRARY util) +mark_as_advanced(UTIL_LIBRARY) + +### NetBSD kvm library +find_library(KVM_LIBRARY kvm) +mark_as_advanced(KVM_LIBRARY) + +### Tests +if(ENABLE_TESTING) + add_subdirectory(tests) +endif() + +### Font Embedder and LineFont.h +option(KONSOLE_BUILD_FONTEMBEDDER "Konsole: build fontembedder executable" OFF) + +### Use cmake -DKONSOLE_BUILD_FONTEMBEDDER +if(KONSOLE_BUILD_FONTEMBEDDER) + ### Font Embedder + add_executable(fontembedder fontembedder.cpp) + target_link_libraries(fontembedder ${KDE4_KIO_LIBS}) + + ### Line graphics font + ### The below cmake doesn't work; it might be possible to automate it + ### if LineFont.src has changed. If automated, different OSes will + ### need to be handled. + ### If LineFont.h is needed to be recreated use: + ### fontembedder LineFont.src > LineFont.h + #add_custom_command(OUTPUT ${CMAKE_SOURCE_DIR}/LineFont.h + # COMMAND ${CMAKE_CURRENT_BINARY_DIR}/fontembedder + # ARGS ${CMAKE_SOURCE_DIR}/LineFont.src + # DEPENDS ${CMAKE_SOURCE_DIR}/LineFont.src ) +endif() + +### Konsole source files shared between embedded terminal and main application +# qdbuscpp2xml -m Session.h -o org.kde.konsole.Session.xml +# qdbuscpp2xml -M -s ViewManager.h -o org.kde.konsole.Konsole.xml + +# Generate dbus .xml files; do not store .xml in source folder +qt4_generate_dbus_interface( + Session.h + org.kde.konsole.Session.xml + OPTIONS -M +) +qt4_generate_dbus_interface( + ViewManager.h + org.kde.konsole.Window.xml + OPTIONS -M +) + +qt4_add_dbus_adaptor(sessionadaptors_SRCS + ${CMAKE_CURRENT_BINARY_DIR}/org.kde.konsole.Session.xml + Session.h + Konsole::Session +) +qt4_add_dbus_adaptor(windowadaptors_SRCS + ${CMAKE_CURRENT_BINARY_DIR}/org.kde.konsole.Window.xml + ViewManager.h + Konsole::ViewManager +) + +set(konsoleprivate_SRCS + ${sessionadaptors_SRCS} + ${windowadaptors_SRCS} + BookmarkHandler.cpp + ColorScheme.cpp + ColorSchemeManager.cpp + ColorSchemeEditor.cpp + CopyInputDialog.cpp + EditProfileDialog.cpp + Emulation.cpp + Filter.cpp + History.cpp + HistorySizeDialog.cpp + HistorySizeWidget.cpp + IncrementalSearchBar.cpp + KeyBindingEditor.cpp + KeyboardTranslator.cpp + KeyboardTranslatorManager.cpp + ManageProfilesDialog.cpp + ProcessInfo.cpp + Profile.cpp + ProfileList.cpp + ProfileReader.cpp + ProfileWriter.cpp + ProfileManager.cpp + Pty.cpp + RenameTabDialog.cpp + RenameTabWidget.cpp + Screen.cpp + ScreenWindow.cpp + Session.cpp + SessionController.cpp + SessionManager.cpp + SessionListModel.cpp + ShellCommand.cpp + TabTitleFormatButton.cpp + TerminalCharacterDecoder.cpp + ExtendedCharTable.cpp + TerminalDisplay.cpp + ViewContainer.cpp + ViewContainerTabBar.cpp + ViewManager.cpp + ViewProperties.cpp + ViewSplitter.cpp + Vt102Emulation.cpp + ZModemDialog.cpp + PrintOptions.cpp + konsole_wcwidth.cpp + ${CMAKE_CURRENT_BINARY_DIR}/org.kde.konsole.Window.xml + ${CMAKE_CURRENT_BINARY_DIR}/org.kde.konsole.Session.xml +) + +### Konsole Application + +add_library(konsoleprivate SHARED ${konsoleprivate_SRCS}) + +target_link_libraries(konsoleprivate + ${KDE4_KPTY_LIBS} + ${KDE4_KIO_LIBS} + ${KDE4_KNOTIFYCONFIG_LIBS} + ${QT_QTXML_LIBRARY} + konq +) + +if(UTIL_LIBRARY) + target_link_libraries(konsoleprivate + ${UTIL_LIBRARY} + ) +endif() + +if(KVM_LIBRARY) + target_link_libraries(konsoleprivate + ${KVM_LIBRARY} + ) +endif() + +set(konsole_SRCS + Application.cpp + MainWindow.cpp + main.cpp + settings/GeneralSettings.cpp + settings/TabBarSettings.cpp +) + +kde4_add_kcfg_files(konsole_SRCS + settings/KonsoleSettings.kcfgc +) + +add_executable(konsole ${konsole_SRCS}) + +target_link_libraries(konsole PRIVATE konsoleprivate) + +generate_export_header(konsoleprivate) + +install( + TARGETS konsole konsoleprivate + ${INSTALL_TARGETS_DEFAULT_ARGS} +) + +### Embedded Konsole KPart + +kde4_add_plugin(konsolepart Part.cpp) +target_link_libraries(konsolepart + ${KDE4_KPARTS_LIBS} + konsoleprivate +) +install( + TARGETS konsolepart + DESTINATION ${KDE4_PLUGIN_INSTALL_DIR} +) + +### konsoleprofile command-line tool + +install( + PROGRAMS konsoleprofile + DESTINATION ${KDE4_BIN_INSTALL_DIR} +) diff --git a/konsole/src/Character.h b/konsole/src/Character.h new file mode 100644 index 00000000..7923a143 --- /dev/null +++ b/konsole/src/Character.h @@ -0,0 +1,204 @@ +/* + This file is part of Konsole, KDE's terminal. + + Copyright 2007-2008 by Robert Knight + Copyright 1997,1998 by Lars Doelle + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +#ifndef CHARACTER_H +#define CHARACTER_H + +// Konsole +#include "CharacterColor.h" + +namespace Konsole +{ +typedef unsigned char LineProperty; + +const int LINE_DEFAULT = 0; +const int LINE_WRAPPED = (1 << 0); +const int LINE_DOUBLEWIDTH = (1 << 1); +const int LINE_DOUBLEHEIGHT = (1 << 2); + +const int DEFAULT_RENDITION = 0; +const int RE_BOLD = (1 << 0); +const int RE_BLINK = (1 << 1); +const int RE_UNDERLINE = (1 << 2); +const int RE_REVERSE = (1 << 3); // Screen only +const int RE_INTENSIVE = (1 << 3); // Widget only +const int RE_ITALIC = (1 << 4); +const int RE_CURSOR = (1 << 5); +const int RE_EXTENDED_CHAR = (1 << 6); + +/** + * Unicode character in the range of U+2500 ~ U+257F are known as line + * characters, or box-drawing characters. Currently, konsole draws those + * characters itself, instead of using the glyph provided by the font. + * Unfortunately, some line characters can't be simulated by the existing 5x5 + * pixel matrix. Typical examples are ╳(U+2573) and ╰(U+2570). So those + * unsupported line characters should be drawn in the normal way . + */ +inline bool isSupportedLineChar(quint16 codePoint) +{ + if ((codePoint & 0xFF80) != 0x2500) { + return false; + } + + uchar index = (codePoint & 0x007F); + if ((index >= 0x04 && index <= 0x0B) || + (index >= 0x4C && index <= 0x4F) || + (index >= 0x6D && index <= 0x73)) { + return false; + } else { + return true; + } +} + +/** + * A single character in the terminal which consists of a unicode character + * value, foreground and background colors and a set of rendition attributes + * which specify how it should be drawn. + */ +class Character +{ +public: + /** + * Constructs a new character. + * + * @param _c The unicode character value of this character. + * @param _f The foreground color used to draw the character. + * @param _b The color used to draw the character's background. + * @param _r A set of rendition flags which specify how this character + * is to be drawn. + * @param _real Indicate whether this character really exists, or exists + * simply as place holder. + */ + explicit inline Character(quint16 _c = ' ', + CharacterColor _f = CharacterColor(COLOR_SPACE_DEFAULT, DEFAULT_FORE_COLOR), + CharacterColor _b = CharacterColor(COLOR_SPACE_DEFAULT, DEFAULT_BACK_COLOR), + quint8 _r = DEFAULT_RENDITION, + bool _real = true) + : character(_c) + , rendition(_r) + , foregroundColor(_f) + , backgroundColor(_b) + , isRealCharacter(_real) { } + + /** The unicode character value for this character. + * + * if RE_EXTENDED_CHAR is set, character is a hash code which can be used to + * look up the unicode character sequence in the ExtendedCharTable used to + * create the sequence. + */ + quint16 character; + + /** A combination of RENDITION flags which specify options for drawing the character. */ + quint8 rendition; + + /** The foreground color used to draw this character. */ + CharacterColor foregroundColor; + + /** The color used to draw this character's background. */ + CharacterColor backgroundColor; + + /** Indicate whether this character really exists, or exists simply as place holder. + * + * TODO: this boolean filed can be further improved to become a enum filed, which + * indicates different roles: + * + * RealCharacter: a character which really exists + * PlaceHolderCharacter: a character which exists as place holder + * TabStopCharacter: a special place holder for HT("\t") + */ + bool isRealCharacter; + + /** + * Returns true if this character should always be drawn in bold when + * it is drawn with the specified @p palette, independent of whether + * or not the character has the RE_BOLD rendition flag. + */ + ColorEntry::FontWeight fontWeight(const ColorEntry* base) const; + + /** + * returns true if the format (color, rendition flag) of the compared characters is equal + */ + bool equalsFormat(const Character& other) const; + + /** + * Compares two characters and returns true if they have the same unicode character value, + * rendition and colors. + */ + friend bool operator == (const Character& a, const Character& b); + + /** + * Compares two characters and returns true if they have different unicode character values, + * renditions or colors. + */ + friend bool operator != (const Character& a, const Character& b); + + inline bool isLineChar() const { + if (rendition & RE_EXTENDED_CHAR) { + return false; + } else { + return isSupportedLineChar(character); + } + } + + inline bool isSpace() const { + if (rendition & RE_EXTENDED_CHAR) { + return false; + } else { + return QChar(character).isSpace(); + } + } +}; + +inline bool operator == (const Character& a, const Character& b) +{ + return a.character == b.character && a.equalsFormat(b); +} + +inline bool operator != (const Character& a, const Character& b) +{ + return !operator==(a, b); +} + +inline bool Character::equalsFormat(const Character& other) const +{ + return backgroundColor == other.backgroundColor && + foregroundColor == other.foregroundColor && + rendition == other.rendition; +} + +inline ColorEntry::FontWeight Character::fontWeight(const ColorEntry* base) const +{ + if (foregroundColor._colorSpace == COLOR_SPACE_DEFAULT) + return base[foregroundColor._u + 0 + (foregroundColor._v ? BASE_COLORS : 0)].fontWeight; + else if (foregroundColor._colorSpace == COLOR_SPACE_SYSTEM) + return base[foregroundColor._u + 2 + (foregroundColor._v ? BASE_COLORS : 0)].fontWeight; + else + return ColorEntry::UseCurrentFormat; +} +} + +QT_BEGIN_NAMESPACE +Q_DECLARE_TYPEINFO(Konsole::Character, Q_MOVABLE_TYPE); +QT_END_NAMESPACE + +#endif // CHARACTER_H + diff --git a/konsole/src/CharacterColor.h b/konsole/src/CharacterColor.h new file mode 100644 index 00000000..bbacff26 --- /dev/null +++ b/konsole/src/CharacterColor.h @@ -0,0 +1,306 @@ +/* + This file is part of Konsole, KDE's terminal. + + Copyright 2007-2008 by Robert Knight + Copyright 1997,1998 by Lars Doelle + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +#ifndef CHARACTERCOLOR_H +#define CHARACTERCOLOR_H + +// Qt +#include + +namespace Konsole +{ +/** + * An entry in a terminal display's color palette. + * + * A color palette is an array of 16 ColorEntry instances which map + * system color indexes (from 0 to 15) into actual colors. + * + * Each entry can be set as bold, in which case any text + * drawn using the color should be drawn in bold. + */ +class ColorEntry +{ +public: + /** Specifies the weight to use when drawing text with this color. */ + enum FontWeight { + /** Always draw text in this color with a bold weight. */ + Bold, + /** Always draw text in this color with a normal weight. */ + Normal, + /** + * Use the current font weight set by the terminal application. + * This is the default behavior. + */ + UseCurrentFormat + }; + + /** + * Constructs a new color palette entry. + * + * @param c The color value for this entry. + * @param weight Specifies the font weight to use when drawing text with this color. + */ + explicit ColorEntry(QColor c, FontWeight weight = UseCurrentFormat) + : color(c), fontWeight(weight) {} + + /** + * Constructs a new color palette entry with an undefined color, and + * with the bold flags set to false. + */ + ColorEntry() : fontWeight(UseCurrentFormat) {} + + /** + * Sets the color and boldness of this color to those of @p rhs. + */ + void operator=(const ColorEntry& rhs) { + color = rhs.color; + fontWeight = rhs.fontWeight; + } + + /** The color value of this entry for display. */ + QColor color; + + /** + * Specifies the font weight to use when drawing text with this color. + * This is not applicable when the color is used to draw a character's background. + */ + FontWeight fontWeight; + + /** + * Compares two color entries and returns true if they represent the same + * color and font weight. + */ + friend bool operator == (const ColorEntry& a, const ColorEntry& b); + /** + * Compares two color entries and returns true if they represent different + * color and font weight. + */ + friend bool operator != (const ColorEntry& a, const ColorEntry& b); +}; + +inline bool operator == (const ColorEntry& a, const ColorEntry& b) +{ + return a.color == b.color && + a.fontWeight == b.fontWeight; +} + +inline bool operator != (const ColorEntry& a, const ColorEntry& b) +{ + return !operator==(a, b); +} + +// Attributed Character Representations /////////////////////////////// + +// Colors + +#define BASE_COLORS (2+8) +#define INTENSITIES 2 +#define TABLE_COLORS (INTENSITIES*BASE_COLORS) + +#define DEFAULT_FORE_COLOR 0 +#define DEFAULT_BACK_COLOR 1 + +/* CharacterColor is a union of the various color spaces. + + Assignment is as follows: + + Type - Space - Values + + 0 - Undefined - u: 0, v:0 w:0 + 1 - Default - u: 0..1 v:intense w:0 + 2 - System - u: 0..7 v:intense w:0 + 3 - Index(256) - u: 16..255 v:0 w:0 + 4 - RGB - u: 0..255 v:0..256 w:0..256 + + Default color space has two separate colors, namely + default foreground and default background color. +*/ + +#define COLOR_SPACE_UNDEFINED 0 +#define COLOR_SPACE_DEFAULT 1 +#define COLOR_SPACE_SYSTEM 2 +#define COLOR_SPACE_256 3 +#define COLOR_SPACE_RGB 4 + +/** + * Describes the color of a single character in the terminal. + */ +class CharacterColor +{ + friend class Character; + +public: + /** Constructs a new CharacterColor whose color and color space are undefined. */ + CharacterColor() + : _colorSpace(COLOR_SPACE_UNDEFINED), + _u(0), + _v(0), + _w(0) + {} + + /** + * Constructs a new CharacterColor using the specified @p colorSpace and with + * color value @p co + * + * The meaning of @p co depends on the @p colorSpace used. + * + * TODO : Document how @p co relates to @p colorSpace + * + * TODO : Add documentation about available color spaces. + */ + CharacterColor(quint8 colorSpace, int co) + : _colorSpace(colorSpace), + _u(0), + _v(0), + _w(0) { + switch (colorSpace) { + case COLOR_SPACE_DEFAULT: + _u = co & 1; + break; + case COLOR_SPACE_SYSTEM: + _u = co & 7; + _v = (co >> 3) & 1; + break; + case COLOR_SPACE_256: + _u = co & 255; + break; + case COLOR_SPACE_RGB: + _u = co >> 16; + _v = co >> 8; + _w = co; + break; + default: + _colorSpace = COLOR_SPACE_UNDEFINED; + } + } + + /** + * Returns true if this character color entry is valid. + */ + bool isValid() const { + return _colorSpace != COLOR_SPACE_UNDEFINED; + } + + /** + * Set this color as an intensive system color. + * + * This is only applicable if the color is using the COLOR_SPACE_DEFAULT or COLOR_SPACE_SYSTEM + * color spaces. + */ + void setIntensive(); + + /** + * Returns the color within the specified color @p palette + * + * The @p palette is only used if this color is one of the 16 system colors, otherwise + * it is ignored. + */ + QColor color(const ColorEntry* palette) const; + + /** + * Compares two colors and returns true if they represent the same color value and + * use the same color space. + */ + friend bool operator == (const CharacterColor& a, const CharacterColor& b); + /** + * Compares two colors and returns true if they represent different color values + * or use different color spaces. + */ + friend bool operator != (const CharacterColor& a, const CharacterColor& b); + +private: + quint8 _colorSpace; + + // bytes storing the character color + quint8 _u; + quint8 _v; + quint8 _w; +}; + +inline bool operator == (const CharacterColor& a, const CharacterColor& b) +{ + return a._colorSpace == b._colorSpace && + a._u == b._u && + a._v == b._v && + a._w == b._w; +} +inline bool operator != (const CharacterColor& a, const CharacterColor& b) +{ + return !operator==(a, b); +} + +inline const QColor color256(quint8 u, const ColorEntry* base) +{ + // 0.. 16: system colors + if (u < 8) { + return base[u + 2].color; + } + u -= 8; + if (u < 8) { + return base[u + 2 + BASE_COLORS].color; + } + u -= 8; + + // 16..231: 6x6x6 rgb color cube + if (u < 216) { + return QColor(((u / 36) % 6) ? (40 * ((u / 36) % 6) + 55) : 0, + ((u / 6) % 6) ? (40 * ((u / 6) % 6) + 55) : 0, + ((u / 1) % 6) ? (40 * ((u / 1) % 6) + 55) : 0); + } + u -= 216; + + // 232..255: gray, leaving out black and white + int gray = u * 10 + 8; + + return QColor(gray, gray, gray); +} + +inline QColor CharacterColor::color(const ColorEntry* base) const +{ + switch (_colorSpace) { + case COLOR_SPACE_DEFAULT: + return base[_u + 0 + (_v ? BASE_COLORS : 0)].color; + case COLOR_SPACE_SYSTEM: + return base[_u + 2 + (_v ? BASE_COLORS : 0)].color; + case COLOR_SPACE_256: + return color256(_u, base); + case COLOR_SPACE_RGB: + return QColor(_u, _v, _w); + case COLOR_SPACE_UNDEFINED: + return QColor(); + } + + Q_ASSERT(false); // invalid color space + + return QColor(); +} + +inline void CharacterColor::setIntensive() +{ + if (_colorSpace == COLOR_SPACE_SYSTEM || _colorSpace == COLOR_SPACE_DEFAULT) { + _v = 1; + } +} +} + +#endif // CHARACTERCOLOR_H + diff --git a/konsole/src/ColorScheme.cpp b/konsole/src/ColorScheme.cpp new file mode 100644 index 00000000..5a81dde3 --- /dev/null +++ b/konsole/src/ColorScheme.cpp @@ -0,0 +1,447 @@ +/* + This source file is part of Konsole, a terminal emulator. + + Copyright 2007-2008 by Robert Knight + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +// Own +#include "ColorScheme.h" + +// Qt +#include + +// KDE +#include +#include +#include + +namespace +{ +const int FGCOLOR_INDEX = 0; +const int BGCOLOR_INDEX = 1; +} + +using namespace Konsole; + +// The following are almost IBM standard color codes, with some slight +// gamma correction for the dim colors to compensate for bright X screens. +// It contains the 8 ansiterm/xterm colors in 2 intensities. +const ColorEntry ColorScheme::defaultTable[TABLE_COLORS] = { + ColorEntry(QColor(0x00, 0x00, 0x00)), // Dfore + ColorEntry(QColor(0xFF, 0xFF, 0xFF)), // Dback + ColorEntry(QColor(0x00, 0x00, 0x00)), // Black + ColorEntry(QColor(0xB2, 0x18, 0x18)), // Red + ColorEntry(QColor(0x18, 0xB2, 0x18)), // Green + ColorEntry(QColor(0xB2, 0x68, 0x18)), // Yellow + ColorEntry(QColor(0x18, 0x18, 0xB2)), // Blue + ColorEntry(QColor(0xB2, 0x18, 0xB2)), // Magenta + ColorEntry(QColor(0x18, 0xB2, 0xB2)), // Cyan + ColorEntry(QColor(0xB2, 0xB2, 0xB2)), // White + // intensive versions + ColorEntry(QColor(0x00, 0x00, 0x00)), + ColorEntry(QColor(0xFF, 0xFF, 0xFF)), + ColorEntry(QColor(0x68, 0x68, 0x68)), + ColorEntry(QColor(0xFF, 0x54, 0x54)), + ColorEntry(QColor(0x54, 0xFF, 0x54)), + ColorEntry(QColor(0xFF, 0xFF, 0x54)), + ColorEntry(QColor(0x54, 0x54, 0xFF)), + ColorEntry(QColor(0xFF, 0x54, 0xFF)), + ColorEntry(QColor(0x54, 0xFF, 0xFF)), + ColorEntry(QColor(0xFF, 0xFF, 0xFF)) +}; + +const char* const ColorScheme::colorNames[TABLE_COLORS] = { + "Foreground", + "Background", + "Color0", + "Color1", + "Color2", + "Color3", + "Color4", + "Color5", + "Color6", + "Color7", + "ForegroundIntense", + "BackgroundIntense", + "Color0Intense", + "Color1Intense", + "Color2Intense", + "Color3Intense", + "Color4Intense", + "Color5Intense", + "Color6Intense", + "Color7Intense" +}; +const char* const ColorScheme::translatedColorNames[TABLE_COLORS] = { + I18N_NOOP2("@item:intable palette", "Foreground"), + I18N_NOOP2("@item:intable palette", "Background"), + I18N_NOOP2("@item:intable palette", "Color 1"), + I18N_NOOP2("@item:intable palette", "Color 2"), + I18N_NOOP2("@item:intable palette", "Color 3"), + I18N_NOOP2("@item:intable palette", "Color 4"), + I18N_NOOP2("@item:intable palette", "Color 5"), + I18N_NOOP2("@item:intable palette", "Color 6"), + I18N_NOOP2("@item:intable palette", "Color 7"), + I18N_NOOP2("@item:intable palette", "Color 8"), + I18N_NOOP2("@item:intable palette", "Foreground (Intense)"), + I18N_NOOP2("@item:intable palette", "Background (Intense)"), + I18N_NOOP2("@item:intable palette", "Color 1 (Intense)"), + I18N_NOOP2("@item:intable palette", "Color 2 (Intense)"), + I18N_NOOP2("@item:intable palette", "Color 3 (Intense)"), + I18N_NOOP2("@item:intable palette", "Color 4 (Intense)"), + I18N_NOOP2("@item:intable palette", "Color 5 (Intense)"), + I18N_NOOP2("@item:intable palette", "Color 6 (Intense)"), + I18N_NOOP2("@item:intable palette", "Color 7 (Intense)"), + I18N_NOOP2("@item:intable palette", "Color 8 (Intense)") +}; + +QString ColorScheme::colorNameForIndex(int index) +{ + Q_ASSERT(index >= 0 && index < TABLE_COLORS); + + return QString(colorNames[index]); +} + +QString ColorScheme::translatedColorNameForIndex(int index) +{ + Q_ASSERT(index >= 0 && index < TABLE_COLORS); + + return i18nc("@item:intable palette", translatedColorNames[index]); +} + +ColorScheme::ColorScheme() +{ + _table = 0; + _randomTable = 0; + _opacity = 1.0; + setWallpaper(QString(), false); +} + +ColorScheme::ColorScheme(const ColorScheme& other) + : _table(0) + , _randomTable(0) + , _opacity(other._opacity) + , _wallpaper(other._wallpaper) +{ + setName(other.name()); + setDescription(other.description()); + + if (other._table != 0) { + for (int i = 0 ; i < TABLE_COLORS ; i++) + setColorTableEntry(i, other._table[i]); + } + + if (other._randomTable != 0) { + for (int i = 0 ; i < TABLE_COLORS ; i++) { + const RandomizationRange& range = other._randomTable[i]; + setRandomizationRange(i, range.hue, range.saturation, range.value); + } + } +} +ColorScheme::~ColorScheme() +{ + delete[] _table; + delete[] _randomTable; +} + +void ColorScheme::setDescription(const QString& aDescription) +{ + _description = aDescription; +} +QString ColorScheme::description() const +{ + return _description; +} + +void ColorScheme::setName(const QString& aName) +{ + _name = aName; +} +QString ColorScheme::name() const +{ + return _name; +} + +void ColorScheme::setColorTableEntry(int index , const ColorEntry& entry) +{ + Q_ASSERT(index >= 0 && index < TABLE_COLORS); + + if (!_table) { + _table = new ColorEntry[TABLE_COLORS]; + + for (int i = 0; i < TABLE_COLORS; i++) + _table[i] = defaultTable[i]; + } + + _table[index] = entry; +} +ColorEntry ColorScheme::colorEntry(int index , uint randomSeed) const +{ + Q_ASSERT(index >= 0 && index < TABLE_COLORS); + + if (randomSeed != 0) + qsrand(randomSeed); + + ColorEntry entry = colorTable()[index]; + + if (randomSeed != 0 && + _randomTable != 0 && + !_randomTable[index].isNull()) { + const RandomizationRange& range = _randomTable[index]; + + int hueDifference = range.hue ? (qrand() % range.hue) - range.hue / 2 : 0; + int saturationDifference = range.saturation ? (qrand() % range.saturation) - range.saturation / 2 : 0; + int valueDifference = range.value ? (qrand() % range.value) - range.value / 2 : 0; + + QColor& color = entry.color; + + int newHue = qAbs((color.hue() + hueDifference) % MAX_HUE); + int newValue = qMin(qAbs(color.value() + valueDifference) , 255); + int newSaturation = qMin(qAbs(color.saturation() + saturationDifference) , 255); + + color.setHsv(newHue, newSaturation, newValue); + } + + return entry; +} +void ColorScheme::getColorTable(ColorEntry* table , uint randomSeed) const +{ + for (int i = 0 ; i < TABLE_COLORS ; i++) + table[i] = colorEntry(i, randomSeed); +} +bool ColorScheme::randomizedBackgroundColor() const +{ + return _randomTable == 0 ? false : !_randomTable[BGCOLOR_INDEX].isNull(); +} +void ColorScheme::setRandomizedBackgroundColor(bool randomize) +{ + // the hue of the background color is allowed to be randomly + // adjusted as much as possible. + // + // the value and saturation are left alone to maintain read-ability + if (randomize) { + setRandomizationRange(BGCOLOR_INDEX , MAX_HUE , 255 , 0); + } else { + if (_randomTable) + setRandomizationRange(BGCOLOR_INDEX , 0 , 0 , 0); + } +} + +void ColorScheme::setRandomizationRange(int index , quint16 hue , quint8 saturation , + quint8 value) +{ + Q_ASSERT(hue <= MAX_HUE); + Q_ASSERT(index >= 0 && index < TABLE_COLORS); + + if (_randomTable == 0) + _randomTable = new RandomizationRange[TABLE_COLORS]; + + _randomTable[index].hue = hue; + _randomTable[index].value = value; + _randomTable[index].saturation = saturation; +} + +const ColorEntry* ColorScheme::colorTable() const +{ + if (_table) + return _table; + else + return defaultTable; +} +QColor ColorScheme::foregroundColor() const +{ + return colorTable()[FGCOLOR_INDEX].color; +} +QColor ColorScheme::backgroundColor() const +{ + return colorTable()[BGCOLOR_INDEX].color; +} +bool ColorScheme::hasDarkBackground() const +{ + // value can range from 0 - 255, with larger values indicating higher brightness. + // so 127 is in the middle, anything less is deemed 'dark' + return backgroundColor().value() < 127; +} +void ColorScheme::setOpacity(qreal aOpacity) +{ + _opacity = aOpacity; +} +qreal ColorScheme::opacity() const +{ + return _opacity; +} + +void ColorScheme::read(const KConfig& config) +{ + KConfigGroup configGroup = config.group("General"); + + const QString schemeDescription = configGroup.readEntry("Description", I18N_NOOP("Un-named Color Scheme")); + + _description = i18n(schemeDescription.toUtf8()); + _opacity = configGroup.readEntry("Opacity", qreal(1.0)); + const QString wallpaper = configGroup.readEntry("Wallpaper", QString()); + const bool tiled = configGroup.readEntry("TileWallpaper", false); + setWallpaper(wallpaper, tiled); + + for (int i = 0 ; i < TABLE_COLORS ; i++) { + readColorEntry(config, i); + } +} + +void ColorScheme::readColorEntry(const KConfig& config , int index) +{ + KConfigGroup configGroup = config.group(colorNameForIndex(index)); + + ColorEntry entry; + + entry.color = configGroup.readEntry("Color", QColor()); + + setColorTableEntry(index , entry); + + const quint16 hue = configGroup.readEntry("MaxRandomHue", 0); + const quint8 value = configGroup.readEntry("MaxRandomValue", 0); + const quint8 saturation = configGroup.readEntry("MaxRandomSaturation", 0); + + if (hue != 0 || value != 0 || saturation != 0) + setRandomizationRange(index , hue , saturation , value); +} + +void ColorScheme::write(KConfig& config) const +{ + KConfigGroup configGroup = config.group("General"); + + configGroup.writeEntry("Description", _description); + configGroup.writeEntry("Opacity", _opacity); + configGroup.writeEntry("Wallpaper", _wallpaper->path()); + configGroup.writeEntry("TileWallpaper", _wallpaper->tiled()); + + for (int i = 0 ; i < TABLE_COLORS ; i++) { + writeColorEntry(config, i); + } +} + +void ColorScheme::writeColorEntry(KConfig& config , int index) const +{ + KConfigGroup configGroup = config.group(colorNameForIndex(index)); + + const ColorEntry& entry = colorTable()[index]; + + configGroup.writeEntry("Color", entry.color); + + // Remove unused keys + if (configGroup.hasKey("Transparent")) { + configGroup.deleteEntry("Transparent"); + } + if (configGroup.hasKey("Transparency")) { + configGroup.deleteEntry("Transparency"); + } + if (configGroup.hasKey("Bold")) { + configGroup.deleteEntry("Bold"); + } + + RandomizationRange random = _randomTable != 0 ? _randomTable[index] : RandomizationRange(); + + // record randomization if this color has randomization or + // if one of the keys already exists + if (!random.isNull() || configGroup.hasKey("MaxRandomHue")) { + configGroup.writeEntry("MaxRandomHue", static_cast(random.hue)); + configGroup.writeEntry("MaxRandomValue", static_cast(random.value)); + configGroup.writeEntry("MaxRandomSaturation", static_cast(random.saturation)); + } +} + +void ColorScheme::setWallpaper(const QString& path, const bool tiled) +{ + _wallpaper = new ColorSchemeWallpaper(path, tiled); +} + +ColorSchemeWallpaper::Ptr ColorScheme::wallpaper() const +{ + return _wallpaper; +} + +ColorSchemeWallpaper::ColorSchemeWallpaper(const QString& aPath, const bool tiled) + : _tiled(tiled), + _path(aPath), + _picture(0) +{ +} + +ColorSchemeWallpaper::~ColorSchemeWallpaper() +{ + delete _picture; +} + +void ColorSchemeWallpaper::load() +{ + if (_path.isEmpty()) + return; + + // Create and load original pixmap + if (!_picture) + _picture = new QPixmap(); + + if (_picture->isNull()) + _picture->load(_path); +} + +bool ColorSchemeWallpaper::draw(QPainter& painter, const QRect& rect, qreal opacity) +{ + if (!_picture || _picture->isNull()) + return false; + + if (qFuzzyCompare(1.0, opacity)) { + if (_tiled) { + painter.drawTiledPixmap(rect, *_picture, rect.topLeft()); + } else { + painter.drawPixmap(rect.topLeft(), _picture->scaled(_size), rect); + } + return true; + } + painter.save(); + painter.setCompositionMode(QPainter::CompositionMode_Source); + painter.fillRect(rect, QColor(0,0,0,0)); + painter.setOpacity(opacity); + if (_tiled) { + painter.drawTiledPixmap(rect, *_picture, rect.topLeft()); + } else { + painter.drawPixmap(rect.topLeft(), _picture->scaled(_size), rect); + } + painter.restore(); + return true; +} + +bool ColorSchemeWallpaper::isNull() const +{ + return _path.isEmpty(); +} + +QString ColorSchemeWallpaper::path() const +{ + return _path; +} + +bool ColorSchemeWallpaper::tiled() const +{ + return _tiled; +} + +void ColorSchemeWallpaper::setSize(const QSize &size) +{ + _size = size; +} \ No newline at end of file diff --git a/konsole/src/ColorScheme.h b/konsole/src/ColorScheme.h new file mode 100644 index 00000000..f682b2e6 --- /dev/null +++ b/konsole/src/ColorScheme.h @@ -0,0 +1,235 @@ +/* + This source file is part of Konsole, a terminal emulator. + + Copyright 2007-2008 by Robert Knight + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +#ifndef COLORSCHEME_H +#define COLORSCHEME_H + +// Qt +#include + +// KDE +#include + +// Konsole +#include "CharacterColor.h" + +class KConfig; +#include +#include + +namespace Konsole +{ +/** + * This class holds the wallpaper pixmap associated with a color scheme. + * The wallpaper object is shared between multiple TerminalDisplay. + */ +class ColorSchemeWallpaper : public QSharedData +{ +public: + typedef KSharedPtr Ptr; + + explicit ColorSchemeWallpaper(const QString& path, const bool tiled); + ~ColorSchemeWallpaper(); + + void load(); + + /** Returns true if wallpaper available and drawn */ + bool draw(QPainter& painter, const QRect& rect, qreal opacity=1.0); + + bool isNull() const; + QString path() const; + bool tiled() const; + + void setSize(const QSize &size); + +private: + bool _tiled; + QSize _size; + QString _path; + QPixmap* _picture; +}; + +/** + * Represents a color scheme for a terminal display. + * + * The color scheme includes the palette of colors used to draw the text and character backgrounds + * in the display and the opacity level of the display background. + */ +class ColorScheme +{ +public: + /** + * Constructs a new color scheme which is initialized to the default color set + * for Konsole. + */ + ColorScheme(); + ColorScheme(const ColorScheme& other); + ~ColorScheme(); + + /** Sets the descriptive name of the color scheme. */ + void setDescription(const QString& description); + /** Returns the descriptive name of the color scheme. */ + QString description() const; + + /** Sets the name of the color scheme */ + void setName(const QString& name); + /** Returns the name of the color scheme */ + QString name() const; + + /** Reads the color scheme from the specified configuration source */ + void read(const KConfig& config); + /** Writes the color scheme to the specified configuration source */ + void write(KConfig& config) const; + + /** Sets a single entry within the color palette. */ + void setColorTableEntry(int index , const ColorEntry& entry); + + /** + * Copies the color entries which form the palette for this color scheme + * into @p table. @p table should be an array with TABLE_COLORS entries. + * + * @param table Array into which the color entries for this color scheme + * are copied. + * @param randomSeed Color schemes may allow certain colors in their + * palette to be randomized. The seed is used to pick the random color. + */ + void getColorTable(ColorEntry* table, uint randomSeed = 0) const; + + /** + * Retrieves a single color entry from the table. + * + * See getColorTable() + */ + ColorEntry colorEntry(int index , uint randomSeed = 0) const; + + /** + * Convenience method. Returns the + * foreground color for this scheme, + * this is the primary color used to draw the + * text in this scheme. + */ + QColor foregroundColor() const; + /** + * Convenience method. Returns the background color for + * this scheme, this is the primary color used to + * draw the terminal background in this scheme. + */ + QColor backgroundColor() const; + + /** + * Returns true if this color scheme has a dark background. + * The background color is said to be dark if it has a value of less than 127 + * in the HSV color space. + */ + bool hasDarkBackground() const; + + /** + * Sets the opacity level of the display background. @p opacity ranges + * between 0 (completely transparent background) and 1 (completely + * opaque background). + * + * Defaults to 1. + * + * TODO: More documentation + */ + void setOpacity(qreal opacity); + /** + * Returns the opacity level for this color scheme, see setOpacity() + * TODO: More documentation + */ + qreal opacity() const; + + void setWallpaper(const QString& path, const bool tiled); + + ColorSchemeWallpaper::Ptr wallpaper() const; + + /** + * Enables randomization of the background color. This will cause + * the palette returned by getColorTable() and colorEntry() to + * be adjusted depending on the value of the random seed argument + * to them. + */ + void setRandomizedBackgroundColor(bool randomize); + + /** Returns true if the background color is randomized. */ + bool randomizedBackgroundColor() const; + + static const ColorEntry defaultTable[]; // table of default color entries + + static QString colorNameForIndex(int index); + static QString translatedColorNameForIndex(int index); + +private: + // specifies how much a particular color can be randomized by + class RandomizationRange + { + public: + RandomizationRange() : hue(0) , saturation(0) , value(0) {} + + bool isNull() const { + return (hue == 0 && saturation == 0 && value == 0); + } + + quint16 hue; + quint8 saturation; + quint8 value; + }; + + // returns the active color table. if none has been set specifically, + // this is the default color table. + const ColorEntry* colorTable() const; + + // reads a single color entry from a KConfig source + // and sets the palette entry at 'index' to the entry read. + void readColorEntry(const KConfig& config , int index); + // writes a single color entry to a KConfig source + void writeColorEntry(KConfig& config , int index) const; + + // sets the amount of randomization allowed for a particular color + // in the palette. creates the randomization table if + // it does not already exist + void setRandomizationRange(int index , quint16 hue , quint8 saturation , quint8 value); + + QString _description; + QString _name; + + // pointer to custom color table, or 0 if the default color table is + // being used + ColorEntry* _table; + + // pointer to randomization table, or 0 if no colors in the color + // scheme support randomization + RandomizationRange* _randomTable; + + qreal _opacity; + + ColorSchemeWallpaper::Ptr _wallpaper; + + static const quint16 MAX_HUE = 340; + + static const char* const colorNames[TABLE_COLORS]; + static const char* const translatedColorNames[TABLE_COLORS]; +}; +} + +Q_DECLARE_METATYPE(const Konsole::ColorScheme*) + +#endif //COLORSCHEME_H diff --git a/konsole/src/ColorSchemeEditor.cpp b/konsole/src/ColorSchemeEditor.cpp new file mode 100644 index 00000000..a2a90d6b --- /dev/null +++ b/konsole/src/ColorSchemeEditor.cpp @@ -0,0 +1,280 @@ +/* + Copyright 2007-2008 by Robert Knight + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +// Own +#include "ColorSchemeEditor.h" + +// Qt +#include +#include + +// KDE +#include +#include +#include +#include +#include + +// Konsole +#include "ui_ColorSchemeEditor.h" +#include "ColorScheme.h" +#include "CharacterColor.h" + +using namespace Konsole; + +// colorTable is half the length of _table in ColorScheme class +// since intense colors are in a separated column +const int COLOR_TABLE_ROW_LENGTH = TABLE_COLORS / 2; + +const int NAME_COLUMN = 0; // column 0 : color names +const int COLOR_COLUMN = 1; // column 1 : actual colors +const int INTENSE_COLOR_COLUMN = 2; // column 2 : intense colors + +ColorSchemeEditor::ColorSchemeEditor(QWidget* aParent) + : KDialog(aParent) + , _isNewScheme(false) + , _colors(0) +{ + // Kdialog buttons + setButtons(KDialog::Ok | KDialog::Cancel | KDialog::Apply); + connect(this, SIGNAL(applyClicked()), this, SLOT(saveColorScheme())); + connect(this, SIGNAL(okClicked()), this, SLOT(saveColorScheme())); + + // ui + _ui = new Ui::ColorSchemeEditor(); + _ui->setupUi(mainWidget()); + + // description edit + _ui->descriptionEdit->setClearButtonShown(true); + connect(_ui->descriptionEdit , SIGNAL(textChanged(QString)) , this , + SLOT(setDescription(QString))); + + // transparency slider + QFontMetrics metrics(font()); + _ui->transparencyPercentLabel->setMinimumWidth(metrics.width("100%")); + + connect(_ui->transparencySlider , SIGNAL(valueChanged(int)) , this , SLOT(setTransparencyPercentLabel(int))); + + // randomized background + connect(_ui->randomizedBackgroundCheck , SIGNAL(toggled(bool)) , this , + SLOT(setRandomizedBackgroundColor(bool))); + + // wallpaper stuff + KUrlCompletion* fileCompletion = new KUrlCompletion(KUrlCompletion::FileCompletion); + fileCompletion->setParent(this); + _ui->wallpaperPath->setCompletionObject(fileCompletion); + _ui->wallpaperPath->setClearButtonShown(true); + _ui->wallpaperSelectButton->setIcon(KIcon("image-x-generic")); + + connect(_ui->wallpaperSelectButton, SIGNAL(clicked()), + this, SLOT(selectWallpaper())); + connect(_ui->wallpaperPath, SIGNAL(textChanged(QString)), + this, SLOT(wallpaperPathChanged(QString))); + connect(_ui->wallpaperTile, SIGNAL(toggled(bool)), + this, SLOT(wallpaperTiledChanged(bool))); + + // color table + _ui->colorTable->setColumnCount(3); + _ui->colorTable->setRowCount(COLOR_TABLE_ROW_LENGTH); + + QStringList labels; + labels << i18nc("@label:listbox Column header text for color names", "Name") + << i18nc("@label:listbox Column header text for the actual colors", "Color") + << i18nc("@label:listbox Column header text for the actual intense colors", "Intense color"); + _ui->colorTable->setHorizontalHeaderLabels(labels); + + // Set resize mode for colorTable columns + _ui->colorTable->horizontalHeader()->setResizeMode(NAME_COLUMN, QHeaderView::ResizeToContents); + _ui->colorTable->horizontalHeader()->setResizeMode(COLOR_COLUMN, QHeaderView::Stretch); + _ui->colorTable->horizontalHeader()->setResizeMode(INTENSE_COLOR_COLUMN, QHeaderView::Stretch); + + QTableWidgetItem* item = new QTableWidgetItem("Test"); + _ui->colorTable->setItem(0, 0, item); + + _ui->colorTable->verticalHeader()->hide(); + + connect(_ui->colorTable , SIGNAL(itemClicked(QTableWidgetItem*)) , this , + SLOT(editColorItem(QTableWidgetItem*))); + + // warning label when transparency is not available + _ui->transparencyWarningWidget->setWordWrap(true); + _ui->transparencyWarningWidget->setCloseButtonVisible(false); + _ui->transparencyWarningWidget->setMessageType(KMessageWidget::Warning); + + if (KWindowSystem::compositingActive()) { + _ui->transparencyWarningWidget->setVisible(false); + } else { + _ui->transparencyWarningWidget->setText(i18nc("@info:status", + "The background transparency setting will not" + " be used because your desktop does not appear to support" + " transparent windows.")); + } +} +ColorSchemeEditor::~ColorSchemeEditor() +{ + delete _colors; + delete _ui; +} +void ColorSchemeEditor::editColorItem(QTableWidgetItem* item) +{ + // ignore if this is not a color column + if (item->column() != COLOR_COLUMN && item->column() != INTENSE_COLOR_COLUMN) { + return; + } + + QColor color = item->background().color(); + int result = KColorDialog::getColor(color); + + if (result == KColorDialog::Accepted) { + item->setBackground(color); + + int colorSchemeRow = item->row(); + // Intense colors row are in the bottom half of the color table + if (item->column() == INTENSE_COLOR_COLUMN) { + colorSchemeRow += COLOR_TABLE_ROW_LENGTH; + } + + ColorEntry entry(_colors->colorEntry(colorSchemeRow)); + entry.color = color; + _colors->setColorTableEntry(colorSchemeRow, entry); + + emit colorsChanged(_colors); + } +} +void ColorSchemeEditor::selectWallpaper() +{ + const KUrl url = KFileDialog::getImageOpenUrl(_ui->wallpaperPath->text(), + this, + i18nc("@action:button", "Select wallpaper image file")); + + if (!url.isEmpty()) + _ui->wallpaperPath->setText(url.path()); +} +void ColorSchemeEditor::wallpaperPathChanged(const QString& path) +{ + if (path.isEmpty()) { + _colors->setWallpaper(path, _ui->wallpaperTile->isChecked()); + } else { + QFileInfo i(path); + + if (i.exists() && i.isFile() && i.isReadable()) + _colors->setWallpaper(path, _ui->wallpaperTile->isChecked()); + } +} +void ColorSchemeEditor::wallpaperTiledChanged(const bool tiled) +{ + Q_UNUSED(tiled); + wallpaperPathChanged(_ui->wallpaperPath->text()); +} +void ColorSchemeEditor::setDescription(const QString& text) +{ + if (_colors) + _colors->setDescription(text); + + if (_ui->descriptionEdit->text() != text) + _ui->descriptionEdit->setText(text); +} +void ColorSchemeEditor::setTransparencyPercentLabel(int percent) +{ + _ui->transparencyPercentLabel->setText(QString("%1%").arg(percent)); + + const qreal opacity = (100.0 - percent) / 100.0; + _colors->setOpacity(opacity); +} +void ColorSchemeEditor::setRandomizedBackgroundColor(bool randomize) +{ + _colors->setRandomizedBackgroundColor(randomize); +} +void ColorSchemeEditor::setup(const ColorScheme* scheme, bool isNewScheme) +{ + _isNewScheme = isNewScheme; + + delete _colors; + + _colors = new ColorScheme(*scheme); + + if (_isNewScheme) { + setCaption(i18n("New Color Scheme")); + setDescription(i18n("New Color Scheme")); + } else { + setCaption(i18n("Edit Color Scheme")); + } + + // setup description edit + _ui->descriptionEdit->setText(_colors->description()); + + // setup color table + setupColorTable(_colors); + + // setup transparency slider + const int transparencyPercent = qRound((1 - _colors->opacity()) * 100); + _ui->transparencySlider->setValue(transparencyPercent); + setTransparencyPercentLabel(transparencyPercent); + + // randomized background color checkbox + _ui->randomizedBackgroundCheck->setChecked(scheme->randomizedBackgroundColor()); + + // wallpaper stuff + _ui->wallpaperPath->setText(scheme->wallpaper()->path()); + _ui->wallpaperTile->setChecked(scheme->wallpaper()->tiled()); +} +void ColorSchemeEditor::setupColorTable(const ColorScheme* colors) +{ + ColorEntry table[TABLE_COLORS]; + colors->getColorTable(table); + + for (int row = 0; row < COLOR_TABLE_ROW_LENGTH; row++) { + QTableWidgetItem* nameItem = new QTableWidgetItem(ColorScheme::translatedColorNameForIndex(row)); + nameItem->setFlags(nameItem->flags() & ~Qt::ItemIsEditable); + + QTableWidgetItem* colorItem = new QTableWidgetItem(); + colorItem->setBackground(table[row].color); + colorItem->setFlags(colorItem->flags() & ~Qt::ItemIsEditable & ~Qt::ItemIsSelectable); + colorItem->setToolTip(i18nc("@info:tooltip", "Click to choose color")); + + QTableWidgetItem* colorItemIntense = new QTableWidgetItem(); + colorItemIntense->setBackground(table[COLOR_TABLE_ROW_LENGTH + row].color); + colorItemIntense->setFlags(colorItem->flags() & ~Qt::ItemIsEditable & ~Qt::ItemIsSelectable); + colorItemIntense->setToolTip(i18nc("@info:tooltip", "Click to choose intense color")); + + _ui->colorTable->setItem(row, NAME_COLUMN, nameItem); + _ui->colorTable->setItem(row, COLOR_COLUMN, colorItem); + _ui->colorTable->setItem(row, INTENSE_COLOR_COLUMN, colorItemIntense); + } + // ensure that color names are as fully visible as possible + _ui->colorTable->resizeColumnToContents(0); + + // set the widget height to the table content + _ui->colorTable->setFixedHeight(_ui->colorTable->verticalHeader()->length() + _ui->colorTable->horizontalHeader()->height() + 2); +} + +ColorScheme& ColorSchemeEditor::colorScheme() const +{ + return *_colors; +} +bool ColorSchemeEditor::isNewScheme() const +{ + return _isNewScheme; +} +void ColorSchemeEditor::saveColorScheme() +{ + emit colorSchemeSaveRequested(colorScheme(), _isNewScheme); +} + +#include "moc_ColorSchemeEditor.cpp" diff --git a/konsole/src/ColorSchemeEditor.h b/konsole/src/ColorSchemeEditor.h new file mode 100644 index 00000000..9c7cd93b --- /dev/null +++ b/konsole/src/ColorSchemeEditor.h @@ -0,0 +1,97 @@ +/* + Copyright 2007-2008 by Robert Knight + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +#ifndef COLORSCHEMEEDITOR_H +#define COLORSCHEMEEDITOR_H + +// Qt +#include +#include + +// KDE +#include + +// Konsole +#include "Profile.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class ColorSchemeEditor; } +QT_END_NAMESPACE + +namespace Konsole +{ +class ColorScheme; + +/** + * A dialog for editing color schemes. + * + * After creation, the dialog can be initialized with the settings + * of a color scheme using the setup() method. + * + * The dialog creates a copy of the supplied color scheme to which + * any changes made are applied. The modified color scheme + * can be retrieved using the colorScheme() method. + * + * When changes are made the colorsChanged() signal is emitted. + */ +class KONSOLEPRIVATE_EXPORT ColorSchemeEditor : public KDialog +{ + Q_OBJECT + +public: + /** Constructs a new color scheme editor with the specified parent. */ + explicit ColorSchemeEditor(QWidget* parent = 0); + virtual ~ColorSchemeEditor(); + + /** Initializes the dialog with the properties of the specified color scheme. */ + void setup(const ColorScheme* scheme, bool isNewScheme); + /** Returns the modified color scheme. */ + ColorScheme& colorScheme() const; + bool isNewScheme() const; + +signals: + /** Emitted when the colors in the color scheme change. */ + void colorsChanged(ColorScheme* scheme); + /** Used to send back colorscheme changes into the profile edited */ + void colorSchemeSaveRequested(const ColorScheme& scheme,bool isNewScheme); + +public slots: + /** Sets the text displayed in the description edit field. */ + void setDescription(const QString& description); + +private slots: + void setTransparencyPercentLabel(int percent); + void setRandomizedBackgroundColor(bool randomized); + void editColorItem(QTableWidgetItem* item); + void wallpaperPathChanged(const QString& path); + void wallpaperTiledChanged(const bool tiled); + void selectWallpaper(); + /** Triggered by apply/ok buttons */ + void saveColorScheme(); + +private: + void setupColorTable(const ColorScheme* table); + + bool _isNewScheme; + Ui::ColorSchemeEditor* _ui; + ColorScheme* _colors; +}; +} + +#endif // COLORSCHEMEEDITOR_H diff --git a/konsole/src/ColorSchemeEditor.ui b/konsole/src/ColorSchemeEditor.ui new file mode 100644 index 00000000..108fa5d5 --- /dev/null +++ b/konsole/src/ColorSchemeEditor.ui @@ -0,0 +1,149 @@ + + + ColorSchemeEditor + + + + 0 + 0 + 364 + 502 + + + + + 0 + + + + + + + Description: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + + + + + + Vary the background color for each tab + + + + + + + + + Background transparency: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + 100 + + + Qt::Horizontal + + + + + + + Percent + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + + Edit the path of the background image + + + + + + + Choose the background image + + + ... + + + + + + + Background image: + + + + + + + Tile background image + + + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 364 + 5 + + + + + + + + + + + + KLineEdit + QLineEdit +
    klineedit.h
    +
    + + KMessageWidget + QFrame +
    kmessagewidget.h
    + 1 +
    +
    + + +
    diff --git a/konsole/src/ColorSchemeManager.cpp b/konsole/src/ColorSchemeManager.cpp new file mode 100644 index 00000000..84ab88d9 --- /dev/null +++ b/konsole/src/ColorSchemeManager.cpp @@ -0,0 +1,203 @@ +/* + This source file is part of Konsole, a terminal emulator. + + Copyright 2007-2008 by Robert Knight + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +// Own +#include "ColorSchemeManager.h" + +// Qt +#include +#include +#include + +// KDE +#include +#include +#include +#include +#include + +using namespace Konsole; + +ColorSchemeManager::ColorSchemeManager() + : _haveLoadedAll(false) +{ +#if defined(Q_WS_X11) + // Allow looking up colors in the X11 color database + QColor::setAllowX11ColorNames(true); +#endif +} + +ColorSchemeManager::~ColorSchemeManager() +{ + qDeleteAll(_colorSchemes); +} + +K_GLOBAL_STATIC(ColorSchemeManager , theColorSchemeManager) + +ColorSchemeManager* ColorSchemeManager::instance() +{ + return theColorSchemeManager; +} + +void ColorSchemeManager::loadAllColorSchemes() +{ + int success = 0; + int failed = 0; + + QStringList nativeColorSchemes = listColorSchemes(); + foreach(const QString& colorScheme, nativeColorSchemes) { + if (loadColorScheme(colorScheme)) + success++; + else + failed++; + } + + if (failed > 0) + kWarning() << "failed to load " << failed << " color schemes."; + + _haveLoadedAll = true; +} + +QList ColorSchemeManager::allColorSchemes() +{ + if (!_haveLoadedAll) { + loadAllColorSchemes(); + } + + return _colorSchemes.values(); +} + +bool ColorSchemeManager::loadColorScheme(const QString& filePath) +{ + if (!filePath.endsWith(QLatin1String(".colorscheme")) || !QFile::exists(filePath)) + return false; + + QFileInfo info(filePath); + + KConfig config(filePath , KConfig::NoGlobals); + ColorScheme* scheme = new ColorScheme(); + scheme->setName(info.baseName()); + scheme->read(config); + + if (scheme->name().isEmpty()) { + kWarning() << "Color scheme in" << filePath << "does not have a valid name and was not loaded."; + delete scheme; + return false; + } + + if (!_colorSchemes.contains(info.baseName())) { + _colorSchemes.insert(scheme->name(), scheme); + } else { + kDebug() << "color scheme with name" << scheme->name() << "has already been" << + "found, ignoring."; + + delete scheme; + } + + return true; +} + +QStringList ColorSchemeManager::listColorSchemes() +{ + return KGlobal::dirs()->findAllResources("data", + "konsole/*.colorscheme", + KStandardDirs::NoDuplicates); +} + +const ColorScheme ColorSchemeManager::_defaultColorScheme; +const ColorScheme* ColorSchemeManager::defaultColorScheme() const +{ + return &_defaultColorScheme; +} + +void ColorSchemeManager::addColorScheme(ColorScheme* scheme) +{ + // remove existing colorscheme with the same name + if (_colorSchemes.contains(scheme->name())) { + delete _colorSchemes[scheme->name()]; + _colorSchemes.remove(scheme->name()); + } + + _colorSchemes.insert(scheme->name(), scheme); + + // save changes to disk + QString path = KGlobal::dirs()->saveLocation("data", "konsole/") + scheme->name() + ".colorscheme"; + KConfig config(path , KConfig::NoGlobals); + + scheme->write(config); +} + +bool ColorSchemeManager::deleteColorScheme(const QString& name) +{ + Q_ASSERT(_colorSchemes.contains(name)); + + // look up the path and delete + QString path = findColorSchemePath(name); + if (QFile::remove(path)) { + delete _colorSchemes[name]; + _colorSchemes.remove(name); + return true; + } else { + kWarning() << "Failed to remove color scheme -" << path; + return false; + } +} + +const ColorScheme* ColorSchemeManager::findColorScheme(const QString& name) +{ + if (name.isEmpty()) + return defaultColorScheme(); + + // A fix to prevent infinite loops if users puts / in ColorScheme name + // Konsole will create a sub-folder in that case (bko 315086) + // More code will have to go in to prevent the users from doing that. + if (name.contains("/")) { + kWarning() << name << " has an invalid character / in the name ... skipping"; + return defaultColorScheme(); + } + + if (_colorSchemes.contains(name)) { + return _colorSchemes[name]; + } else { + // look for this color scheme + QString path = findColorSchemePath(name); + if (!path.isEmpty() && loadColorScheme(path)) { + return findColorScheme(name); + } + + kWarning() << "Could not find color scheme - " << name; + + return 0; + } +} + +QString ColorSchemeManager::findColorSchemePath(const QString& name) const +{ + QString path = KStandardDirs::locate("data", "konsole/" + name + ".colorscheme"); + + if (!path.isEmpty()) + return path; + + path = KStandardDirs::locate("data", "konsole/" + name + ".schema"); + + return path; +} + diff --git a/konsole/src/ColorSchemeManager.h b/konsole/src/ColorSchemeManager.h new file mode 100644 index 00000000..307d1984 --- /dev/null +++ b/konsole/src/ColorSchemeManager.h @@ -0,0 +1,110 @@ +/* + This source file is part of Konsole, a terminal emulator. + + Copyright 2007-2008 by Robert Knight + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +#ifndef COLORSCHEMEMANAGER_H +#define COLORSCHEMEMANAGER_H + +// Qt +#include +#include + +// Konsole +#include "ColorScheme.h" + +namespace Konsole +{ +/** + * Manages the color schemes available for use by terminal displays. + * See ColorScheme + */ +class ColorSchemeManager +{ +public: + /** + * Constructs a new ColorSchemeManager and loads the list + * of available color schemes. + * + * The color schemes themselves are not loaded until they are first + * requested via a call to findColorScheme() + */ + ColorSchemeManager(); + /** + * Destroys the ColorSchemeManager and saves any modified color schemes to disk. + */ + ~ColorSchemeManager(); + + /** + * Returns the default color scheme for Konsole + */ + const ColorScheme* defaultColorScheme() const; + + /** + * Returns the color scheme with the given name or 0 if no + * scheme with that name exists. If @p name is empty, the + * default color scheme is returned. + * + * The first time that a color scheme with a particular name is + * requested, the configuration information is loaded from disk. + */ + const ColorScheme* findColorScheme(const QString& name); + + /** + * Adds a new color scheme to the manager. If @p scheme has the same name as + * an existing color scheme, it replaces the existing scheme. + */ + void addColorScheme(ColorScheme* scheme); + + /** + * Deletes a color scheme. Returns true on successful deletion or false otherwise. + */ + bool deleteColorScheme(const QString& name); + + /** + * Returns a list of the all the available color schemes. + * This may be slow when first called because all of the color + * scheme resources on disk must be located, read and parsed. + * + * Subsequent calls will be inexpensive. + */ + QList allColorSchemes(); + + /** Returns the global color scheme manager instance. */ + static ColorSchemeManager* instance(); + +private: + // loads a color scheme from a KDE 4+ .colorscheme file + bool loadColorScheme(const QString& path); + // returns a list of paths of color schemes in the KDE 4+ .colorscheme file format + QStringList listColorSchemes(); + // loads all of the color schemes + void loadAllColorSchemes(); + // finds the path of a color scheme + QString findColorSchemePath(const QString& name) const; + + QHash _colorSchemes; + + bool _haveLoadedAll; + + static const ColorScheme _defaultColorScheme; +}; +} + +#endif //COLORSCHEMEMANAGER_H diff --git a/konsole/src/CopyInputDialog.cpp b/konsole/src/CopyInputDialog.cpp new file mode 100644 index 00000000..35571523 --- /dev/null +++ b/konsole/src/CopyInputDialog.cpp @@ -0,0 +1,197 @@ +/* + Copyright 2008 by Robert Knight + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +// Own +#include "CopyInputDialog.h" + +// Qt +#include + +// KDE +#include + +// Konsole +#include "ui_CopyInputDialog.h" + +using namespace Konsole; + +CopyInputDialog::CopyInputDialog(QWidget* parent) + : KDialog(parent) +{ + setCaption(i18n("Copy Input")); + setButtons(KDialog::Ok | KDialog::Cancel); + + setWindowModality(Qt::WindowModal); + + _ui = new Ui::CopyInputDialog(); + _ui->setupUi(mainWidget()); + + connect(_ui->selectAllButton, SIGNAL(clicked()), this, SLOT(selectAll())); + connect(_ui->deselectAllButton, SIGNAL(clicked()), this, SLOT(deselectAll())); + + _ui->filterEdit->setClearButtonShown(true); + _ui->filterEdit->setFocus(); + + _model = new CheckableSessionModel(parent); + _model->setCheckColumn(1); + _model->setSessions(SessionManager::instance()->sessions()); + + QSortFilterProxyModel* filterProxyModel = new QSortFilterProxyModel(this); + filterProxyModel->setDynamicSortFilter(true); + filterProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive); + filterProxyModel->setSourceModel(_model); + filterProxyModel->setFilterKeyColumn(-1); + + connect(_ui->filterEdit, SIGNAL(textChanged(QString)), + filterProxyModel, SLOT(setFilterFixedString(QString))); + + _ui->sessionList->setModel(filterProxyModel); + _ui->sessionList->setColumnHidden(0, true); // Hide number column + _ui->sessionList->header()->hide(); +} + +CopyInputDialog::~CopyInputDialog() +{ + delete _ui; +} + +void CopyInputDialog::setChosenSessions(const QSet& sessions) +{ + QSet checked = sessions; + if (_masterSession) + checked.insert(_masterSession); + + _model->setCheckedSessions(checked); +} +QSet CopyInputDialog::chosenSessions() const +{ + return _model->checkedSessions(); +} +void CopyInputDialog::setMasterSession(Session* session) +{ + if (_masterSession) + _model->setCheckable(_masterSession, true); + + _model->setCheckable(session, false); + QSet checked = _model->checkedSessions(); + checked.insert(session); + _model->setCheckedSessions(checked); + + _masterSession = session; +} +void CopyInputDialog::setSelectionChecked(bool checked) +{ + QAbstractItemModel* model = _ui->sessionList->model(); + int rows = model->rowCount(); + + QModelIndexList selected = _ui->sessionList->selectionModel()->selectedIndexes(); + + if (selected.count() > 1) { + foreach(const QModelIndex & index, selected) { + setRowChecked(index.row(), checked); + } + } else { + for (int i = 0; i < rows; i++) + setRowChecked(i, checked); + } +} +void CopyInputDialog::setRowChecked(int row, bool checked) +{ + QAbstractItemModel* model = _ui->sessionList->model(); + QModelIndex index = model->index(row, _model->checkColumn()); + if (checked) + model->setData(index, static_cast(Qt::Checked), Qt::CheckStateRole); + else + model->setData(index, static_cast(Qt::Unchecked), Qt::CheckStateRole); +} +CheckableSessionModel::CheckableSessionModel(QObject* parent) + : SessionListModel(parent) + , _checkColumn(0) +{ +} +void CheckableSessionModel::setCheckColumn(int column) +{ + _checkColumn = column; + reset(); +} +Qt::ItemFlags CheckableSessionModel::flags(const QModelIndex& index) const +{ + Session* session = static_cast(index.internalPointer()); + + if (_fixedSessions.contains(session)) + return SessionListModel::flags(index) & ~Qt::ItemIsEnabled; + else + return SessionListModel::flags(index) | Qt::ItemIsUserCheckable; +} +QVariant CheckableSessionModel::data(const QModelIndex& index, int role) const +{ + if (role == Qt::CheckStateRole && index.column() == _checkColumn) { + Session* session = static_cast(index.internalPointer()); + + if (_checkedSessions.contains(session)) + return QVariant::fromValue(static_cast(Qt::Checked)); + else + return QVariant::fromValue(static_cast(Qt::Unchecked)); + } else { + return SessionListModel::data(index, role); + } +} +bool CheckableSessionModel::setData(const QModelIndex& index, const QVariant& value, int role) +{ + if (role == Qt::CheckStateRole && index.column() == _checkColumn) { + Session* session = static_cast(index.internalPointer()); + + if (_fixedSessions.contains(session)) + return false; + + if (value.value() == Qt::Checked) + _checkedSessions.insert(session); + else + _checkedSessions.remove(session); + + emit dataChanged(index, index); + return true; + } else { + return SessionListModel::setData(index, value, role); + } +} +void CheckableSessionModel::setCheckedSessions(const QSet sessions) +{ + _checkedSessions = sessions; + reset(); +} +QSet CheckableSessionModel::checkedSessions() const +{ + return _checkedSessions; +} +void CheckableSessionModel::setCheckable(Session* session, bool checkable) +{ + if (!checkable) + _fixedSessions.insert(session); + else + _fixedSessions.remove(session); + + reset(); +} +void CheckableSessionModel::sessionRemoved(Session* session) +{ + _checkedSessions.remove(session); + _fixedSessions.remove(session); +} + diff --git a/konsole/src/CopyInputDialog.h b/konsole/src/CopyInputDialog.h new file mode 100644 index 00000000..0932ee36 --- /dev/null +++ b/konsole/src/CopyInputDialog.h @@ -0,0 +1,136 @@ +/* + Copyright 2008 by Robert Knight + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +#ifndef COPYINPUTDIALOG_H +#define COPYINPUTDIALOG_H + +// Qt +#include +#include + +// KDE +#include + +// Konsole +#include "SessionManager.h" +#include "Session.h" +#include "SessionListModel.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class CopyInputDialog; } +QT_END_NAMESPACE + +namespace Konsole +{ +class CheckableSessionModel; + +/** + * Dialog which allows the user to mark a list of sessions to copy + * the input from the current session to. The current session is + * set using setMasterSession(). After the dialog has been executed, + * the set of chosen sessions can be retrieved using chosenSessions() + */ +class CopyInputDialog : public KDialog +{ + Q_OBJECT + +public: + explicit CopyInputDialog(QWidget* parent = 0); + ~CopyInputDialog(); + /** + * Sets the 'source' session whose input will be copied to + * other sessions. This session is displayed grayed out in the list + * and cannot be unchecked. + */ + void setMasterSession(Session* master); + /** See setMasterSession() */ + Session* masterSession() const; + + /** Sets the sessions in the list which are checked. */ + void setChosenSessions(const QSet& sessions); + /** Set setChosenSessions() */ + QSet chosenSessions() const; + +private slots: + void selectAll() { + setSelectionChecked(true); + }; + void deselectAll() { + setSelectionChecked(false); + }; + +private: + // Checks or unchecks selected sessions. If there are no + // selected items then all sessions are checked or unchecked + void setSelectionChecked(bool checked); + void setRowChecked(int row, bool checked); + + Ui::CopyInputDialog* _ui; + CheckableSessionModel* _model; + QPointer _masterSession; +}; + +/** + * A list of sessions with a checkbox next to each one which allows the + * user to select a subset of the available sessions to perform + * some action on them. + */ +class CheckableSessionModel : public SessionListModel +{ + Q_OBJECT + +public: + explicit CheckableSessionModel(QObject* parent); + + void setCheckColumn(int column); + int checkColumn() const; + + /** + * Sets whether a session can be checked or un-checked. + * Non-checkable items have the Qt::ItemIsEnabled flag unset. + */ + void setCheckable(Session* session, bool checkable); + + /** Sets the list of sessions which are currently checked. */ + void setCheckedSessions(const QSet sessions); + /** Returns the set of checked sessions. */ + QSet checkedSessions() const; + + // reimplemented from QAbstractItemModel + virtual Qt::ItemFlags flags(const QModelIndex& index) const; + virtual QVariant data(const QModelIndex& index, int role) const; + virtual bool setData(const QModelIndex& index, const QVariant& value, int role); + +protected: + virtual void sessionRemoved(Session*); + +private: + QSet _checkedSessions; + QSet _fixedSessions; + int _checkColumn; +}; + +inline int CheckableSessionModel::checkColumn() const +{ + return _checkColumn; +} +} + +#endif // COPYINPUTDIALOG_H + diff --git a/konsole/src/CopyInputDialog.ui b/konsole/src/CopyInputDialog.ui new file mode 100644 index 00000000..b6d81309 --- /dev/null +++ b/konsole/src/CopyInputDialog.ui @@ -0,0 +1,83 @@ + + + CopyInputDialog + + + + 0 + 0 + 363 + 223 + + + + + + + + + Filter: + + + + + + + + + + + + QAbstractItemView::ExtendedSelection + + + false + + + true + + + + + + + + + Select All + + + + + + + Deselect All + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + KLineEdit + QLineEdit +
    klineedit.h
    +
    +
    + + +
    diff --git a/konsole/src/EditProfileDialog.cpp b/konsole/src/EditProfileDialog.cpp new file mode 100644 index 00000000..28391acd --- /dev/null +++ b/konsole/src/EditProfileDialog.cpp @@ -0,0 +1,1363 @@ +/* + Copyright 2007-2008 by Robert Knight + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +// Own +#include "EditProfileDialog.h" + +// Standard +#include + +// Qt +#include +#include +#include +#include +#include +#include +#include + +// KDE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Konsole +#include "ColorScheme.h" +#include "ColorSchemeManager.h" +#include "ColorSchemeEditor.h" +#include "ui_EditProfileDialog.h" +#include "KeyBindingEditor.h" +#include "KeyboardTranslator.h" +#include "KeyboardTranslatorManager.h" +#include "ProfileManager.h" +#include "ShellCommand.h" +#include "Enumeration.h" + +using namespace Konsole; + +const bool EditProfileDialog::HAVE_TRANSPARENCY = KWindowSystem::compositingActive(); + +EditProfileDialog::EditProfileDialog(QWidget* aParent) + : KDialog(aParent) + , _delayedPreviewTimer(new QTimer(this)) + , _colorDialog(0) +{ + setCaption(i18n("Edit Profile")); + setButtons(KDialog::Ok | KDialog::Cancel | KDialog::Apply); + + // disable the apply button , since no modification has been made + enableButtonApply(false); + + connect(this, SIGNAL(applyClicked()), this, SLOT(save())); + + connect(_delayedPreviewTimer, SIGNAL(timeout()), this, SLOT(delayedPreviewActivate())); + + _ui = new Ui::EditProfileDialog(); + _ui->setupUi(mainWidget()); + + // there are various setupXYZPage() methods to load the items + // for each page and update their states to match the profile + // being edited. + // + // these are only called when needed ( ie. when the user clicks + // the tab to move to that page ). + // + // the _pageNeedsUpdate vector keeps track of the pages that have + // not been updated since the last profile change and will need + // to be refreshed when the user switches to them + _pageNeedsUpdate.resize(_ui->tabWidget->count()); + connect(_ui->tabWidget, SIGNAL(currentChanged(int)), this, + SLOT(preparePage(int))); + + createTempProfile(); +} +EditProfileDialog::~EditProfileDialog() +{ + delete _ui; +} +void EditProfileDialog::save() +{ + if (_tempProfile->isEmpty()) + return; + + ProfileManager::instance()->changeProfile(_profile, _tempProfile->setProperties()); + + // ensure that these settings are not undone by a call + // to unpreview() + QHashIterator iter(_tempProfile->setProperties()); + while (iter.hasNext()) { + iter.next(); + _previewedProperties.remove(iter.key()); + } + + createTempProfile(); + + enableButtonApply(false); +} +void EditProfileDialog::reject() +{ + unpreviewAll(); + KDialog::reject(); +} +void EditProfileDialog::accept() +{ + Q_ASSERT(_profile); + Q_ASSERT(_tempProfile); + + if ((_tempProfile->isPropertySet(Profile::Name) && + _tempProfile->name().isEmpty()) + || (_profile->name().isEmpty() && _tempProfile->name().isEmpty())) { + KMessageBox::sorry(this, + i18n("

    Each profile must have a name before it can be saved " + "into disk.

    ")); + return; + } + save(); + unpreviewAll(); + KDialog::accept(); +} +QString EditProfileDialog::groupProfileNames(const ProfileGroup::Ptr group, int maxLength) +{ + QString caption; + int count = group->profiles().count(); + for (int i = 0; i < count; i++) { + caption += group->profiles()[i]->name(); + if (i < (count - 1)) { + caption += ','; + // limit caption length to prevent very long window titles + if (maxLength > 0 && caption.length() > maxLength) { + caption += "..."; + break; + } + } + } + return caption; +} +void EditProfileDialog::updateCaption(const Profile::Ptr profile) +{ + const int MAX_GROUP_CAPTION_LENGTH = 25; + ProfileGroup::Ptr group = profile->asGroup(); + if (group && group->profiles().count() > 1) { + QString caption = groupProfileNames(group, MAX_GROUP_CAPTION_LENGTH); + setCaption(i18np("Editing profile: %2", + "Editing %1 profiles: %2", + group->profiles().count(), + caption)); + } else { + setCaption(i18n("Edit Profile \"%1\"", profile->name())); + } +} +void EditProfileDialog::setProfile(Profile::Ptr profile) +{ + Q_ASSERT(profile); + + _profile = profile; + + // update caption + updateCaption(profile); + + // mark each page of the dialog as out of date + // and force an update of the currently visible page + // + // the other pages will be updated as necessary + _pageNeedsUpdate.fill(true); + preparePage(_ui->tabWidget->currentIndex()); + + if (_tempProfile) { + createTempProfile(); + } +} +const Profile::Ptr EditProfileDialog::lookupProfile() const +{ + return _profile; +} +void EditProfileDialog::preparePage(int page) +{ + const Profile::Ptr profile = lookupProfile(); + + Q_ASSERT(_pageNeedsUpdate.count() > page); + Q_ASSERT(profile); + + QWidget* pageWidget = _ui->tabWidget->widget(page); + + if (_pageNeedsUpdate[page]) { + if (pageWidget == _ui->generalTab) + setupGeneralPage(profile); + else if (pageWidget == _ui->tabsTab) + setupTabsPage(profile); + else if (pageWidget == _ui->appearanceTab) + setupAppearancePage(profile); + else if (pageWidget == _ui->scrollingTab) + setupScrollingPage(profile); + else if (pageWidget == _ui->keyboardTab) + setupKeyboardPage(profile); + else if (pageWidget == _ui->mouseTab) + setupMousePage(profile); + else if (pageWidget == _ui->advancedTab) + setupAdvancedPage(profile); + else + Q_ASSERT(false); + + _pageNeedsUpdate[page] = false; + } +} +void EditProfileDialog::selectProfileName() +{ + _ui->profileNameEdit->setFocus(); + _ui->profileNameEdit->selectAll(); +} +void EditProfileDialog::setupGeneralPage(const Profile::Ptr profile) +{ + // basic profile options + { + _ui->emptyNameWarningWidget->setWordWrap(false); + _ui->emptyNameWarningWidget->setCloseButtonVisible(false); + _ui->emptyNameWarningWidget->setMessageType(KMessageWidget::Warning); + + ProfileGroup::Ptr group = profile->asGroup(); + if (!group || group->profiles().count() < 2) { + _ui->profileNameEdit->setText(profile->name()); + _ui->profileNameEdit->setClearButtonShown(true); + + _ui->emptyNameWarningWidget->setVisible(profile->name().isEmpty()); + _ui->emptyNameWarningWidget->setText(i18n("Profile name is empty.")); + } else { + _ui->profileNameEdit->setText(groupProfileNames(group, -1)); + _ui->profileNameEdit->setEnabled(false); + _ui->profileNameLabel->setEnabled(false); + + _ui->emptyNameWarningWidget->setVisible(false); + } + } + + ShellCommand command(profile->command() , profile->arguments()); + _ui->commandEdit->setText(command.fullCommand()); + KUrlCompletion* exeCompletion = new KUrlCompletion(KUrlCompletion::ExeCompletion); + exeCompletion->setParent(this); + exeCompletion->setDir(QString()); + _ui->commandEdit->setCompletionObject(exeCompletion); + + _ui->initialDirEdit->setText(profile->defaultWorkingDirectory()); + KUrlCompletion* dirCompletion = new KUrlCompletion(KUrlCompletion::DirCompletion); + dirCompletion->setParent(this); + _ui->initialDirEdit->setCompletionObject(dirCompletion); + _ui->initialDirEdit->setClearButtonShown(true); + + _ui->dirSelectButton->setIcon(KIcon("folder-open")); + _ui->iconSelectButton->setIcon(KIcon(profile->icon())); + _ui->startInSameDirButton->setChecked(profile->startInCurrentSessionDir()); + + // terminal options + _ui->terminalColumnsEntry->setValue(profile->terminalColumns()); + _ui->terminalRowsEntry->setValue(profile->terminalRows()); + + // window options + _ui->showTerminalSizeHintButton->setChecked(profile->showTerminalSizeHint()); + + // signals and slots + connect(_ui->dirSelectButton, SIGNAL(clicked()), this, SLOT(selectInitialDir())); + connect(_ui->iconSelectButton, SIGNAL(clicked()), this, SLOT(selectIcon())); + connect(_ui->startInSameDirButton, SIGNAL(toggled(bool)), this , + SLOT(startInSameDir(bool))); + connect(_ui->profileNameEdit, SIGNAL(textChanged(QString)), this, + SLOT(profileNameChanged(QString))); + connect(_ui->initialDirEdit, SIGNAL(textChanged(QString)), this, + SLOT(initialDirChanged(QString))); + connect(_ui->commandEdit, SIGNAL(textChanged(QString)), this, + SLOT(commandChanged(QString))); + connect(_ui->environmentEditButton , SIGNAL(clicked()), this, + SLOT(showEnvironmentEditor())); + + connect(_ui->terminalColumnsEntry, SIGNAL(valueChanged(int)), + this, SLOT(terminalColumnsEntryChanged(int))); + connect(_ui->terminalRowsEntry, SIGNAL(valueChanged(int)), + this, SLOT(terminalRowsEntryChanged(int))); + + connect(_ui->showTerminalSizeHintButton, SIGNAL(toggled(bool)), this, + SLOT(showTerminalSizeHint(bool))); +} +void EditProfileDialog::showEnvironmentEditor() +{ + const Profile::Ptr profile = lookupProfile(); + + QWeakPointer dialog = new KDialog(this); + KTextEdit* edit = new KTextEdit(dialog.data()); + + QStringList currentEnvironment = profile->environment(); + + edit->setPlainText(currentEnvironment.join("\n")); + edit->setToolTip(i18nc("@info:tooltip", "One environment variable per line")); + + dialog.data()->setWindowTitle(i18n("Edit Environment")); + dialog.data()->setMainWidget(edit); + + if (dialog.data()->exec() == QDialog::Accepted) { + QStringList newEnvironment = edit->toPlainText().split('\n'); + updateTempProfileProperty(Profile::Environment, newEnvironment); + } + + delete dialog.data(); +} +void EditProfileDialog::setupTabsPage(const Profile::Ptr profile) +{ + // tab title format + _ui->renameTabWidget->setTabTitleText(profile->localTabTitleFormat()); + _ui->renameTabWidget->setRemoteTabTitleText(profile->remoteTabTitleFormat()); + + connect(_ui->renameTabWidget, SIGNAL(tabTitleFormatChanged(QString)), this, + SLOT(tabTitleFormatChanged(QString))); + connect(_ui->renameTabWidget, SIGNAL(remoteTabTitleFormatChanged(QString)), this, + SLOT(remoteTabTitleFormatChanged(QString))); + + // tab monitoring + const int silenceSeconds = profile->silenceSeconds(); + _ui->silenceSecondsSpinner->setValue(silenceSeconds); + _ui->silenceSecondsSpinner->setSuffix(ki18ncp("Unit of time", " second", " seconds")); + + connect(_ui->silenceSecondsSpinner, SIGNAL(valueChanged(int)), + this, SLOT(silenceSecondsChanged(int))); +} + +void EditProfileDialog::terminalColumnsEntryChanged(int value) +{ + updateTempProfileProperty(Profile::TerminalColumns, value); +} +void EditProfileDialog::terminalRowsEntryChanged(int value) +{ + updateTempProfileProperty(Profile::TerminalRows, value); +} +void EditProfileDialog::showTerminalSizeHint(bool value) +{ + updateTempProfileProperty(Profile::ShowTerminalSizeHint, value); +} +void EditProfileDialog::tabTitleFormatChanged(const QString& format) +{ + updateTempProfileProperty(Profile::LocalTabTitleFormat, format); +} +void EditProfileDialog::remoteTabTitleFormatChanged(const QString& format) +{ + updateTempProfileProperty(Profile::RemoteTabTitleFormat, format); +} + +void EditProfileDialog::silenceSecondsChanged(int seconds) +{ + updateTempProfileProperty(Profile::SilenceSeconds, seconds); +} + +void EditProfileDialog::selectIcon() +{ + const QString& icon = KIconDialog::getIcon(KIconLoader::Desktop, KIconLoader::Application, + false, 0, false, this); + if (!icon.isEmpty()) { + _ui->iconSelectButton->setIcon(KIcon(icon)); + updateTempProfileProperty(Profile::Icon, icon); + } +} +void EditProfileDialog::profileNameChanged(const QString& text) +{ + _ui->emptyNameWarningWidget->setVisible(text.isEmpty()); + + updateTempProfileProperty(Profile::Name, text); + updateTempProfileProperty(Profile::UntranslatedName, text); + updateCaption(_tempProfile); +} +void EditProfileDialog::startInSameDir(bool sameDir) +{ + updateTempProfileProperty(Profile::StartInCurrentSessionDir, sameDir); +} +void EditProfileDialog::initialDirChanged(const QString& dir) +{ + updateTempProfileProperty(Profile::Directory, dir); +} +void EditProfileDialog::commandChanged(const QString& command) +{ + ShellCommand shellCommand(command); + + updateTempProfileProperty(Profile::Command, shellCommand.command()); + updateTempProfileProperty(Profile::Arguments, shellCommand.arguments()); +} +void EditProfileDialog::selectInitialDir() +{ + const KUrl url = KFileDialog::getExistingDirectoryUrl(_ui->initialDirEdit->text(), + this, + i18n("Select Initial Directory")); + + if (!url.isEmpty()) + _ui->initialDirEdit->setText(url.path()); +} +void EditProfileDialog::setupAppearancePage(const Profile::Ptr profile) +{ + ColorSchemeViewDelegate* delegate = new ColorSchemeViewDelegate(this); + _ui->colorSchemeList->setItemDelegate(delegate); + + _ui->transparencyWarningWidget->setVisible(false); + _ui->transparencyWarningWidget->setWordWrap(true); + _ui->transparencyWarningWidget->setCloseButtonVisible(false); + _ui->transparencyWarningWidget->setMessageType(KMessageWidget::Warning); + + _ui->editColorSchemeButton->setEnabled(false); + _ui->removeColorSchemeButton->setEnabled(false); + + // setup color list + updateColorSchemeList(true); + + _ui->colorSchemeList->setMouseTracking(true); + _ui->colorSchemeList->installEventFilter(this); + _ui->colorSchemeList->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); + + connect(_ui->colorSchemeList->selectionModel(), + SIGNAL(selectionChanged(QItemSelection,QItemSelection)), + this, SLOT(colorSchemeSelected())); + connect(_ui->colorSchemeList, SIGNAL(entered(QModelIndex)), this, + SLOT(previewColorScheme(QModelIndex))); + + updateColorSchemeButtons(); + + connect(_ui->editColorSchemeButton, SIGNAL(clicked()), this, + SLOT(editColorScheme())); + connect(_ui->removeColorSchemeButton, SIGNAL(clicked()), this, + SLOT(removeColorScheme())); + connect(_ui->newColorSchemeButton, SIGNAL(clicked()), this, + SLOT(newColorScheme())); + + // setup font preview + const bool antialias = profile->antiAliasFonts(); + + QFont profileFont = profile->font(); + profileFont.setHintingPreference(antialias ? QFont::PreferDefaultHinting : QFont::PreferNoHinting); + + _ui->fontPreviewLabel->installEventFilter(this); + _ui->fontPreviewLabel->setFont(profileFont); + setFontInputValue(profileFont); + + connect(_ui->fontSizeInput, SIGNAL(valueChanged(double)), this, + SLOT(setFontSize(double))); + connect(_ui->selectFontButton, SIGNAL(clicked()), this, + SLOT(showFontDialog())); + + // setup font smoothing + _ui->antialiasTextButton->setChecked(antialias); + connect(_ui->antialiasTextButton, SIGNAL(toggled(bool)), this, + SLOT(setAntialiasText(bool))); + + _ui->boldIntenseButton->setChecked(profile->boldIntense()); + connect(_ui->boldIntenseButton, SIGNAL(toggled(bool)), this, + SLOT(setBoldIntense(bool))); + _ui->enableMouseWheelZoomButton->setChecked(profile->mouseWheelZoomEnabled()); + connect(_ui->enableMouseWheelZoomButton, SIGNAL(toggled(bool)), this, + SLOT(toggleMouseWheelZoom(bool))); +} +void EditProfileDialog::setAntialiasText(bool enable) +{ + QFont profileFont = _ui->fontPreviewLabel->font(); + profileFont.setHintingPreference(enable ? QFont::PreferDefaultHinting : QFont::PreferNoHinting); + + // update preview to reflect text smoothing state + fontSelected(profileFont); + updateTempProfileProperty(Profile::AntiAliasFonts, enable); +} +void EditProfileDialog::setBoldIntense(bool enable) +{ + preview(Profile::BoldIntense, enable); + updateTempProfileProperty(Profile::BoldIntense, enable); +} +void EditProfileDialog::toggleMouseWheelZoom(bool enable) +{ + updateTempProfileProperty(Profile::MouseWheelZoomEnabled, enable); +} +void EditProfileDialog::updateColorSchemeList(bool selectCurrentScheme) +{ + if (!_ui->colorSchemeList->model()) + _ui->colorSchemeList->setModel(new QStandardItemModel(this)); + + const QString& name = lookupProfile()->colorScheme(); + const ColorScheme* currentScheme = ColorSchemeManager::instance()->findColorScheme(name); + + QStandardItemModel* model = qobject_cast(_ui->colorSchemeList->model()); + + Q_ASSERT(model); + + model->clear(); + + QStandardItem* selectedItem = 0; + + QList schemeList = ColorSchemeManager::instance()->allColorSchemes(); + + foreach(const ColorScheme* scheme, schemeList) { + QStandardItem* item = new QStandardItem(scheme->description()); + item->setData(QVariant::fromValue(scheme) , Qt::UserRole + 1); + item->setFlags(item->flags()); + + if (currentScheme == scheme) + selectedItem = item; + + model->appendRow(item); + } + + model->sort(0); + + if (selectCurrentScheme && selectedItem) { + _ui->colorSchemeList->updateGeometry(); + _ui->colorSchemeList->selectionModel()->setCurrentIndex(selectedItem->index() , + QItemSelectionModel::Select); + + // update transparency warning label + updateTransparencyWarning(); + } +} +void EditProfileDialog::updateKeyBindingsList(bool selectCurrentTranslator) +{ + if (!_ui->keyBindingList->model()) + _ui->keyBindingList->setModel(new QStandardItemModel(this)); + + const QString& name = lookupProfile()->keyBindings(); + + KeyboardTranslatorManager* keyManager = KeyboardTranslatorManager::instance(); + const KeyboardTranslator* currentTranslator = keyManager->findTranslator(name); + + QStandardItemModel* model = qobject_cast(_ui->keyBindingList->model()); + + Q_ASSERT(model); + + model->clear(); + + QStandardItem* selectedItem = 0; + + QStringList translatorNames = keyManager->allTranslators(); + foreach(const QString& translatorName, translatorNames) { + const KeyboardTranslator* translator = keyManager->findTranslator(translatorName); + + QStandardItem* item = new QStandardItem(translator->description()); + item->setEditable(false); + item->setData(QVariant::fromValue(translator), Qt::UserRole + 1); + item->setIcon(KIcon("preferences-desktop-keyboard")); + + if (translator == currentTranslator) + selectedItem = item; + + model->appendRow(item); + } + + model->sort(0); + + if (selectCurrentTranslator && selectedItem) { + _ui->keyBindingList->selectionModel()->setCurrentIndex(selectedItem->index() , + QItemSelectionModel::Select); + } +} +bool EditProfileDialog::eventFilter(QObject* watched , QEvent* aEvent) +{ + if (watched == _ui->colorSchemeList && aEvent->type() == QEvent::Leave) { + if (_tempProfile->isPropertySet(Profile::ColorScheme)) + preview(Profile::ColorScheme, _tempProfile->colorScheme()); + else + unpreview(Profile::ColorScheme); + } + if (watched == _ui->fontPreviewLabel && aEvent->type() == QEvent::FontChange) { + const QFont& labelFont = _ui->fontPreviewLabel->font(); + _ui->fontPreviewLabel->setText(i18n("%1", labelFont.family())); + } + + return KDialog::eventFilter(watched, aEvent); +} +void EditProfileDialog::unpreviewAll() +{ + _delayedPreviewTimer->stop(); + _delayedPreviewProperties.clear(); + + QHash map; + QHashIterator iter(_previewedProperties); + while (iter.hasNext()) { + iter.next(); + map.insert((Profile::Property)iter.key(), iter.value()); + } + + // undo any preview changes + if (!map.isEmpty()) + ProfileManager::instance()->changeProfile(_profile, map, false); +} +void EditProfileDialog::unpreview(int aProperty) +{ + _delayedPreviewProperties.remove(aProperty); + + if (!_previewedProperties.contains(aProperty)) + return; + + QHash map; + map.insert((Profile::Property)aProperty, _previewedProperties[aProperty]); + ProfileManager::instance()->changeProfile(_profile, map, false); + + _previewedProperties.remove(aProperty); +} +void EditProfileDialog::delayedPreview(int aProperty , const QVariant& value) +{ + _delayedPreviewProperties.insert(aProperty, value); + + _delayedPreviewTimer->stop(); + _delayedPreviewTimer->start(300); +} +void EditProfileDialog::delayedPreviewActivate() +{ + Q_ASSERT(qobject_cast(sender())); + + QMutableHashIterator iter(_delayedPreviewProperties); + if (iter.hasNext()) { + iter.next(); + preview(iter.key(), iter.value()); + } +} +void EditProfileDialog::preview(int aProperty , const QVariant& value) +{ + QHash map; + map.insert((Profile::Property)aProperty, value); + + _delayedPreviewProperties.remove(aProperty); + + const Profile::Ptr original = lookupProfile(); + + // skip previews for profile groups if the profiles in the group + // have conflicting original values for the property + // + // TODO - Save the original values for each profile and use to unpreview properties + ProfileGroup::Ptr group = original->asGroup(); + if (group && group->profiles().count() > 1 && + original->property((Profile::Property)aProperty).isNull()) + return; + + if (!_previewedProperties.contains(aProperty)) { + _previewedProperties.insert(aProperty , original->property((Profile::Property)aProperty)); + } + + // temporary change to color scheme + ProfileManager::instance()->changeProfile(_profile , map , false); +} +void EditProfileDialog::previewColorScheme(const QModelIndex& index) +{ + const QString& name = index.data(Qt::UserRole + 1).value()->name(); + + delayedPreview(Profile::ColorScheme , name); +} +void EditProfileDialog::removeColorScheme() +{ + QModelIndexList selected = _ui->colorSchemeList->selectionModel()->selectedIndexes(); + + if (!selected.isEmpty()) { + const QString& name = selected.first().data(Qt::UserRole + 1).value()->name(); + + if (ColorSchemeManager::instance()->deleteColorScheme(name)) + _ui->colorSchemeList->model()->removeRow(selected.first().row()); + } +} +void EditProfileDialog::showColorSchemeEditor(bool isNewScheme) +{ + // Finding selected ColorScheme + QModelIndexList selected = _ui->colorSchemeList->selectionModel()->selectedIndexes(); + QAbstractItemModel* model = _ui->colorSchemeList->model(); + const ColorScheme* colors = 0; + if (!selected.isEmpty()) + colors = model->data(selected.first(), Qt::UserRole + 1).value(); + else + colors = ColorSchemeManager::instance()->defaultColorScheme(); + + Q_ASSERT(colors); + + // Setting up ColorSchemeEditor ui + // close any running ColorSchemeEditor + if (_colorDialog) { + closeColorSchemeEditor(); + } + _colorDialog = new ColorSchemeEditor(this); + + connect(_colorDialog, SIGNAL(colorSchemeSaveRequested(ColorScheme,bool)), + this, SLOT(saveColorScheme(ColorScheme,bool))); + _colorDialog->setup(colors, isNewScheme); + + _colorDialog->show(); +} +void EditProfileDialog::closeColorSchemeEditor() +{ + if (_colorDialog) { + _colorDialog->close(); + delete _colorDialog; + } +} +void EditProfileDialog::newColorScheme() +{ + showColorSchemeEditor(true); +} +void EditProfileDialog::editColorScheme() +{ + showColorSchemeEditor(false); +} +void EditProfileDialog::saveColorScheme(const ColorScheme& scheme, bool isNewScheme) +{ + ColorScheme* newScheme = new ColorScheme(scheme); + + // if this is a new color scheme, pick a name based on the description + if (isNewScheme) { + newScheme->setName(newScheme->description()); + } + + ColorSchemeManager::instance()->addColorScheme(newScheme); + + updateColorSchemeList(true); + + preview(Profile::ColorScheme, newScheme->name()); +} +void EditProfileDialog::colorSchemeSelected() +{ + QModelIndexList selected = _ui->colorSchemeList->selectionModel()->selectedIndexes(); + + if (!selected.isEmpty()) { + QAbstractItemModel* model = _ui->colorSchemeList->model(); + const ColorScheme* colors = model->data(selected.first(), Qt::UserRole + 1).value(); + if (colors) { + updateTempProfileProperty(Profile::ColorScheme, colors->name()); + previewColorScheme(selected.first()); + + updateTransparencyWarning(); + } + } + + updateColorSchemeButtons(); +} +void EditProfileDialog::updateColorSchemeButtons() +{ + enableIfNonEmptySelection(_ui->editColorSchemeButton, _ui->colorSchemeList->selectionModel()); + enableIfNonEmptySelection(_ui->removeColorSchemeButton, _ui->colorSchemeList->selectionModel()); +} +void EditProfileDialog::updateKeyBindingsButtons() +{ + enableIfNonEmptySelection(_ui->editKeyBindingsButton, _ui->keyBindingList->selectionModel()); + enableIfNonEmptySelection(_ui->removeKeyBindingsButton, _ui->keyBindingList->selectionModel()); +} +void EditProfileDialog::enableIfNonEmptySelection(QWidget* widget, QItemSelectionModel* selectionModel) +{ + widget->setEnabled(selectionModel->hasSelection()); +} +void EditProfileDialog::updateTransparencyWarning() +{ + // zero or one indexes can be selected + foreach(const QModelIndex & index , _ui->colorSchemeList->selectionModel()->selectedIndexes()) { + bool needTransparency = index.data(Qt::UserRole + 1).value()->opacity() < 1.0; + + if (!needTransparency) { + _ui->transparencyWarningWidget->setHidden(true); + } else if (!KWindowSystem::compositingActive()) { + _ui->transparencyWarningWidget->setText(i18n("This color scheme uses a transparent background" + " which does not appear to be supported on your" + " desktop")); + _ui->transparencyWarningWidget->setHidden(false); + } else if (!EditProfileDialog::HAVE_TRANSPARENCY) { + _ui->transparencyWarningWidget->setText(i18n("Konsole was started before desktop effects were enabled." + " You need to restart Konsole to see transparent background.")); + _ui->transparencyWarningWidget->setHidden(false); + } + } +} + +void EditProfileDialog::createTempProfile() +{ + _tempProfile = Profile::Ptr(new Profile); + _tempProfile->setHidden(true); +} + +void EditProfileDialog::updateTempProfileProperty(Profile::Property aProperty, const QVariant & value) +{ + _tempProfile->setProperty(aProperty, value); + updateButtonApply(); +} + +void EditProfileDialog::updateButtonApply() +{ + bool userModified = false; + + QHashIterator iter(_tempProfile->setProperties()); + while (iter.hasNext()) { + iter.next(); + + Profile::Property aProperty = iter.key(); + QVariant value = iter.value(); + + // for previewed property + if (_previewedProperties.contains(static_cast(aProperty))) { + if (value != _previewedProperties.value(static_cast(aProperty))) { + userModified = true; + break; + } + // for not-previewed property + } else if ((value != _profile->property(aProperty))) { + userModified = true; + break; + } + } + + enableButtonApply(userModified); +} + +void EditProfileDialog::setupKeyboardPage(const Profile::Ptr /* profile */) +{ + // setup translator list + updateKeyBindingsList(true); + + connect(_ui->keyBindingList->selectionModel(), + SIGNAL(selectionChanged(QItemSelection,QItemSelection)), + SLOT(keyBindingSelected())); + connect(_ui->newKeyBindingsButton, SIGNAL(clicked()), this, + SLOT(newKeyBinding())); + + updateKeyBindingsButtons(); + + connect(_ui->editKeyBindingsButton, SIGNAL(clicked()), this, + SLOT(editKeyBinding())); + connect(_ui->removeKeyBindingsButton, SIGNAL(clicked()), this, + SLOT(removeKeyBinding())); +} +void EditProfileDialog::keyBindingSelected() +{ + QModelIndexList selected = _ui->keyBindingList->selectionModel()->selectedIndexes(); + + if (!selected.isEmpty()) { + QAbstractItemModel* model = _ui->keyBindingList->model(); + const KeyboardTranslator* translator = model->data(selected.first(), Qt::UserRole + 1) + .value(); + if (translator) { + updateTempProfileProperty(Profile::KeyBindings, translator->name()); + } + } + + updateKeyBindingsButtons(); +} +void EditProfileDialog::removeKeyBinding() +{ + QModelIndexList selected = _ui->keyBindingList->selectionModel()->selectedIndexes(); + + if (!selected.isEmpty()) { + const QString& name = selected.first().data(Qt::UserRole + 1).value()->name(); + if (KeyboardTranslatorManager::instance()->deleteTranslator(name)) + _ui->keyBindingList->model()->removeRow(selected.first().row()); + } +} +void EditProfileDialog::showKeyBindingEditor(bool isNewTranslator) +{ + QModelIndexList selected = _ui->keyBindingList->selectionModel()->selectedIndexes(); + QAbstractItemModel* model = _ui->keyBindingList->model(); + + const KeyboardTranslator* translator = 0; + if (!selected.isEmpty()) + translator = model->data(selected.first(), Qt::UserRole + 1).value(); + else + translator = KeyboardTranslatorManager::instance()->defaultTranslator(); + + Q_ASSERT(translator); + + QWeakPointer dialog = new KDialog(this); + + if (isNewTranslator) + dialog.data()->setCaption(i18n("New Key Binding List")); + else + dialog.data()->setCaption(i18n("Edit Key Binding List")); + + KeyBindingEditor* editor = new KeyBindingEditor; + dialog.data()->setMainWidget(editor); + + if (translator) + editor->setup(translator); + + if (isNewTranslator) + editor->setDescription(i18n("New Key Binding List")); + + if (dialog.data()->exec() == QDialog::Accepted) { + KeyboardTranslator* newTranslator = new KeyboardTranslator(*editor->translator()); + + if (isNewTranslator) + newTranslator->setName(newTranslator->description()); + + KeyboardTranslatorManager::instance()->addTranslator(newTranslator); + + updateKeyBindingsList(); + + const QString& currentTranslator = lookupProfile() + ->property(Profile::KeyBindings); + + if (newTranslator->name() == currentTranslator) { + updateTempProfileProperty(Profile::KeyBindings, newTranslator->name()); + } + } + delete dialog.data(); +} +void EditProfileDialog::newKeyBinding() +{ + showKeyBindingEditor(true); +} +void EditProfileDialog::editKeyBinding() +{ + showKeyBindingEditor(false); +} +void EditProfileDialog::setupCheckBoxes(BooleanOption* options , const Profile::Ptr profile) +{ + while (options->button != 0) { + options->button->setChecked(profile->property(options->property)); + connect(options->button, SIGNAL(toggled(bool)), this, options->slot); + + ++options; + } +} +void EditProfileDialog::setupRadio(RadioOption* possibilities , int actual) +{ + while (possibilities->button != 0) { + if (possibilities->value == actual) + possibilities->button->setChecked(true); + else + possibilities->button->setChecked(false); + + connect(possibilities->button, SIGNAL(clicked()), this, possibilities->slot); + + ++possibilities; + } +} + +void EditProfileDialog::setupScrollingPage(const Profile::Ptr profile) +{ + // setup scrollbar radio + int scrollBarPosition = profile->property(Profile::ScrollBarPosition); + + RadioOption positions[] = { {_ui->scrollBarHiddenButton, Enum::ScrollBarHidden, SLOT(hideScrollBar())}, + {_ui->scrollBarLeftButton, Enum::ScrollBarLeft, SLOT(showScrollBarLeft())}, + {_ui->scrollBarRightButton, Enum::ScrollBarRight, SLOT(showScrollBarRight())}, + {0, 0, 0} + }; + + setupRadio(positions , scrollBarPosition); + + // setup scrollback type radio + int scrollBackType = profile->property(Profile::HistoryMode); + _ui->historySizeWidget->setMode(Enum::HistoryModeEnum(scrollBackType)); + connect(_ui->historySizeWidget, SIGNAL(historyModeChanged(Enum::HistoryModeEnum)), + this, SLOT(historyModeChanged(Enum::HistoryModeEnum))); + + // setup scrollback line count spinner + const int historySize = profile->historySize(); + _ui->historySizeWidget->setLineCount(historySize); + + // setup scrollpageamount type radio + int scrollFullPage = profile->property(Profile::ScrollFullPage); + + RadioOption pageamounts[] = { + {_ui->scrollHalfPage, Enum::ScrollPageHalf, SLOT(scrollHalfPage())}, + {_ui->scrollFullPage, Enum::ScrollPageFull, SLOT(scrollFullPage())}, + {0, 0, 0} + }; + + setupRadio(pageamounts, scrollFullPage); + + // signals and slots + connect(_ui->historySizeWidget, SIGNAL(historySizeChanged(int)), + this, SLOT(historySizeChanged(int))); +} + +void EditProfileDialog::historySizeChanged(int lineCount) +{ + updateTempProfileProperty(Profile::HistorySize , lineCount); +} +void EditProfileDialog::historyModeChanged(Enum::HistoryModeEnum mode) +{ + updateTempProfileProperty(Profile::HistoryMode, mode); +} +void EditProfileDialog::hideScrollBar() +{ + updateTempProfileProperty(Profile::ScrollBarPosition, Enum::ScrollBarHidden); +} +void EditProfileDialog::showScrollBarLeft() +{ + updateTempProfileProperty(Profile::ScrollBarPosition, Enum::ScrollBarLeft); +} +void EditProfileDialog::showScrollBarRight() +{ + updateTempProfileProperty(Profile::ScrollBarPosition, Enum::ScrollBarRight); +} +void EditProfileDialog::scrollFullPage() +{ + updateTempProfileProperty(Profile::ScrollFullPage, Enum::ScrollPageFull); +} +void EditProfileDialog::scrollHalfPage() +{ + updateTempProfileProperty(Profile::ScrollFullPage, Enum::ScrollPageHalf); +} +void EditProfileDialog::setupMousePage(const Profile::Ptr profile) +{ + BooleanOption options[] = { { + _ui->underlineLinksButton , Profile::UnderlineLinksEnabled, + SLOT(toggleUnderlineLinks(bool)) + }, + { + _ui->ctrlRequiredForDragButton, Profile::CtrlRequiredForDrag, + SLOT(toggleCtrlRequiredForDrag(bool)) + }, + { + _ui->copyTextToClipboardButton , Profile::AutoCopySelectedText, + SLOT(toggleCopyTextToClipboard(bool)) + }, + { + _ui->trimTrailingSpacesButton , Profile::TrimTrailingSpacesInSelectedText, + SLOT(toggleTrimTrailingSpacesInSelectedText(bool)) + }, + { + _ui->openLinksByDirectClickButton , Profile::OpenLinksByDirectClickEnabled, + SLOT(toggleOpenLinksByDirectClick(bool)) + }, + { 0 , Profile::Property(0) , 0 } + }; + setupCheckBoxes(options , profile); + + // setup middle click paste mode + const int middleClickPasteMode = profile->property(Profile::MiddleClickPasteMode); + RadioOption pasteModes[] = { + {_ui->pasteFromX11SelectionButton, Enum::PasteFromX11Selection, SLOT(pasteFromX11Selection())}, + {_ui->pasteFromClipboardButton, Enum::PasteFromClipboard, SLOT(pasteFromClipboard())}, + {0, 0, 0} + }; + setupRadio(pasteModes , middleClickPasteMode); + + // interaction options + _ui->wordCharacterEdit->setText(profile->wordCharacters()); + + connect(_ui->wordCharacterEdit, SIGNAL(textChanged(QString)), this, + SLOT(wordCharactersChanged(QString))); + + int tripleClickMode = profile->property(Profile::TripleClickMode); + _ui->tripleClickModeCombo->setCurrentIndex(tripleClickMode); + + connect(_ui->tripleClickModeCombo, SIGNAL(activated(int)), this, + SLOT(TripleClickModeChanged(int))); + + _ui->openLinksByDirectClickButton->setEnabled(_ui->underlineLinksButton->isChecked()); + + _ui->enableMouseWheelZoomButton->setChecked(profile->mouseWheelZoomEnabled()); + connect(_ui->enableMouseWheelZoomButton, SIGNAL(toggled(bool)), this, + SLOT(toggleMouseWheelZoom(bool))); +} +void EditProfileDialog::setupAdvancedPage(const Profile::Ptr profile) +{ + BooleanOption options[] = { { + _ui->enableBlinkingTextButton , Profile::BlinkingTextEnabled , + SLOT(toggleBlinkingText(bool)) + }, + { + _ui->enableFlowControlButton , Profile::FlowControlEnabled , + SLOT(toggleFlowControl(bool)) + }, + { + _ui->enableBlinkingCursorButton , Profile::BlinkingCursorEnabled , + SLOT(toggleBlinkingCursor(bool)) + }, + { + _ui->enableBidiRenderingButton , Profile::BidiRenderingEnabled , + SLOT(togglebidiRendering(bool)) + }, + { 0 , Profile::Property(0) , 0 } + }; + setupCheckBoxes(options , profile); + + const int lineSpacing = profile->lineSpacing(); + _ui->lineSpacingSpinner->setValue(lineSpacing); + + connect(_ui->lineSpacingSpinner, SIGNAL(valueChanged(int)), + this, SLOT(lineSpacingChanged(int))); + + // cursor options + if (profile->useCustomCursorColor()) + _ui->customCursorColorButton->setChecked(true); + else + _ui->autoCursorColorButton->setChecked(true); + + _ui->customColorSelectButton->setColor(profile->customCursorColor()); + + connect(_ui->customCursorColorButton, SIGNAL(clicked()), this, SLOT(customCursorColor())); + connect(_ui->autoCursorColorButton, SIGNAL(clicked()), this, SLOT(autoCursorColor())); + connect(_ui->customColorSelectButton, SIGNAL(changed(QColor)), + SLOT(customCursorColorChanged(QColor))); + + int shape = profile->property(Profile::CursorShape); + _ui->cursorShapeCombo->setCurrentIndex(shape); + + connect(_ui->cursorShapeCombo, SIGNAL(activated(int)), this, SLOT(setCursorShape(int))); + + // encoding options + QAction* codecAction = new KCodecAction(this); + _ui->selectEncodingButton->setMenu(codecAction->menu()); + connect(codecAction, SIGNAL(triggered(QTextCodec*)), this, SLOT(setDefaultCodec(QTextCodec*))); + + _ui->characterEncodingLabel->setText(profile->defaultEncoding()); +} +void EditProfileDialog::setDefaultCodec(QTextCodec* codec) +{ + QString name = QString(codec->name()); + + updateTempProfileProperty(Profile::DefaultEncoding, name); + _ui->characterEncodingLabel->setText(codec->name()); +} +void EditProfileDialog::customCursorColorChanged(const QColor& color) +{ + updateTempProfileProperty(Profile::CustomCursorColor, color); + + // ensure that custom cursor colors are enabled + _ui->customCursorColorButton->click(); +} +void EditProfileDialog::wordCharactersChanged(const QString& text) +{ + updateTempProfileProperty(Profile::WordCharacters, text); +} +void EditProfileDialog::autoCursorColor() +{ + updateTempProfileProperty(Profile::UseCustomCursorColor, false); +} +void EditProfileDialog::customCursorColor() +{ + updateTempProfileProperty(Profile::UseCustomCursorColor, true); +} +void EditProfileDialog::setCursorShape(int index) +{ + updateTempProfileProperty(Profile::CursorShape, index); +} +void EditProfileDialog::togglebidiRendering(bool enable) +{ + updateTempProfileProperty(Profile::BidiRenderingEnabled, enable); +} +void EditProfileDialog::lineSpacingChanged(int spacing) +{ + updateTempProfileProperty(Profile::LineSpacing, spacing); +} +void EditProfileDialog::toggleBlinkingCursor(bool enable) +{ + updateTempProfileProperty(Profile::BlinkingCursorEnabled, enable); +} +void EditProfileDialog::toggleUnderlineLinks(bool enable) +{ + updateTempProfileProperty(Profile::UnderlineLinksEnabled, enable); + _ui->openLinksByDirectClickButton->setEnabled(enable); +} +void EditProfileDialog::toggleCtrlRequiredForDrag(bool enable) +{ + updateTempProfileProperty(Profile::CtrlRequiredForDrag, enable); +} +void EditProfileDialog::toggleOpenLinksByDirectClick(bool enable) +{ + updateTempProfileProperty(Profile::OpenLinksByDirectClickEnabled, enable); +} +void EditProfileDialog::toggleCopyTextToClipboard(bool enable) +{ + updateTempProfileProperty(Profile::AutoCopySelectedText, enable); +} +void EditProfileDialog::toggleTrimTrailingSpacesInSelectedText(bool enable) +{ + updateTempProfileProperty(Profile::TrimTrailingSpacesInSelectedText, enable); +} +void EditProfileDialog::pasteFromX11Selection() +{ + updateTempProfileProperty(Profile::MiddleClickPasteMode, Enum::PasteFromX11Selection); +} +void EditProfileDialog::pasteFromClipboard() +{ + updateTempProfileProperty(Profile::MiddleClickPasteMode, Enum::PasteFromClipboard); +} +void EditProfileDialog::TripleClickModeChanged(int newValue) +{ + updateTempProfileProperty(Profile::TripleClickMode, newValue); +} +void EditProfileDialog::toggleBlinkingText(bool enable) +{ + updateTempProfileProperty(Profile::BlinkingTextEnabled, enable); +} +void EditProfileDialog::toggleFlowControl(bool enable) +{ + updateTempProfileProperty(Profile::FlowControlEnabled, enable); +} +void EditProfileDialog::fontSelected(const QFont& aFont) +{ + QFont previewFont = aFont; + + setFontInputValue(aFont); + + _ui->fontPreviewLabel->setFont(previewFont); + + preview(Profile::Font, aFont); + updateTempProfileProperty(Profile::Font, aFont); +} +void EditProfileDialog::showFontDialog() +{ + QString sampleText = QString("ell 'lL', one '1', little eye 'i', big eye"); + sampleText += QString("'I', lL1iI, Zero '0', little oh 'o', big oh 'O', 0oO"); + sampleText += QString("`~!@#$%^&*()_+-=[]\\{}|:\";'<>?,./"); + sampleText += QString("0123456789"); + sampleText += QString("\nThe Quick Brown Fox Jumps Over The Lazy Dog\n"); + sampleText += i18n("--- Type anything in this box ---"); + QFont currentFont = _ui->fontPreviewLabel->font(); + + QWeakPointer dialog = new KFontDialog(this, KFontChooser::FixedFontsOnly); + dialog.data()->setCaption(i18n("Select Fixed Width Font")); + dialog.data()->setFont(currentFont, true); + + // TODO (hindenburg): When https://git.reviewboard.kde.org/r/103357 is + // committed, change the below. + // Use text more fitting to show font differences in a terminal + QList chooserList = dialog.data()->findChildren(); + if (!chooserList.isEmpty()) + chooserList.at(0)->setSampleText(sampleText); + + connect(dialog.data(), SIGNAL(fontSelected(QFont)), this, SLOT(fontSelected(QFont))); + + if (dialog.data()->exec() == QDialog::Rejected) + fontSelected(currentFont); + delete dialog.data(); +} +void EditProfileDialog::setFontSize(double pointSize) +{ + QFont newFont = _ui->fontPreviewLabel->font(); + newFont.setPointSizeF(pointSize); + _ui->fontPreviewLabel->setFont(newFont); + + preview(Profile::Font, newFont); + updateTempProfileProperty(Profile::Font, newFont); +} + +void EditProfileDialog::setFontInputValue(const QFont& aFont) +{ + _ui->fontSizeInput->setValue(aFont.pointSizeF()); +} + +ColorSchemeViewDelegate::ColorSchemeViewDelegate(QObject* aParent) + : QAbstractItemDelegate(aParent) +{ +} + +void ColorSchemeViewDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, + const QModelIndex& index) const +{ + const ColorScheme* scheme = index.data(Qt::UserRole + 1).value(); + Q_ASSERT(scheme); + if (!scheme) + return; + + bool transparencyAvailable = KWindowSystem::compositingActive(); + + painter->setRenderHint(QPainter::Antialiasing); + + // draw background + painter->setPen(QPen(scheme->foregroundColor() , 1)); + + // radial gradient for background + // from a lightened version of the scheme's background color in the center to + // a darker version at the outer edge + QColor color = scheme->backgroundColor(); + QRectF backgroundRect = QRectF(option.rect).adjusted(1.5, 1.5, -1.5, -1.5); + + QRadialGradient backgroundGradient(backgroundRect.center() , backgroundRect.width() / 2); + backgroundGradient.setColorAt(0 , color.lighter(105)); + backgroundGradient.setColorAt(1 , color.darker(115)); + + const int backgroundRectXRoundness = 4; + const int backgroundRectYRoundness = 30; + + QPainterPath backgroundRectPath(backgroundRect.topLeft()); + backgroundRectPath.addRoundRect(backgroundRect , backgroundRectXRoundness , backgroundRectYRoundness); + + if (transparencyAvailable) { + painter->save(); + color.setAlphaF(scheme->opacity()); + painter->setCompositionMode(QPainter::CompositionMode_Source); + painter->setBrush(backgroundGradient); + + painter->drawPath(backgroundRectPath); + painter->restore(); + } else { + painter->setBrush(backgroundGradient); + painter->drawPath(backgroundRectPath); + } + + // draw stripe at the side using scheme's foreground color + painter->setPen(QPen(Qt::NoPen)); + QPainterPath path(option.rect.topLeft()); + path.lineTo(option.rect.width() / 10.0 , option.rect.top()); + path.lineTo(option.rect.bottomLeft()); + path.lineTo(option.rect.topLeft()); + painter->setBrush(scheme->foregroundColor()); + painter->drawPath(path.intersected(backgroundRectPath)); + + // draw highlight + // with a linear gradient going from translucent white to transparent + QLinearGradient gradient(option.rect.topLeft() , option.rect.bottomLeft()); + gradient.setColorAt(0 , QColor(255, 255, 255, 90)); + gradient.setColorAt(1 , Qt::transparent); + painter->setBrush(gradient); + painter->drawRoundRect(backgroundRect , 4 , 30); + + const bool isSelected = option.state & QStyle::State_Selected; + + // draw border on selected items + if (isSelected) { + static const int selectedBorderWidth = 6; + + painter->setBrush(QBrush(Qt::NoBrush)); + QPen pen; + + QColor highlightColor = option.palette.highlight().color(); + highlightColor.setAlphaF(1.0); + + pen.setBrush(highlightColor); + pen.setWidth(selectedBorderWidth); + pen.setJoinStyle(Qt::MiterJoin); + + painter->setPen(pen); + + painter->drawRect(option.rect.adjusted(selectedBorderWidth / 2, + selectedBorderWidth / 2, + -selectedBorderWidth / 2, + -selectedBorderWidth / 2)); + } + + // draw color scheme name using scheme's foreground color + QPen pen(scheme->foregroundColor()); + painter->setPen(pen); + + painter->drawText(option.rect , Qt::AlignCenter , + index.data(Qt::DisplayRole).value()); +} + +QSize ColorSchemeViewDelegate::sizeHint(const QStyleOptionViewItem& option, + const QModelIndex& /*index*/) const +{ + const int width = 200; + qreal colorWidth = (qreal)width / TABLE_COLORS; + int margin = 5; + qreal heightForWidth = (colorWidth * 2) + option.fontMetrics.height() + margin; + + // temporary + return QSize(width, static_cast(heightForWidth)); +} + +#include "moc_EditProfileDialog.cpp" diff --git a/konsole/src/EditProfileDialog.h b/konsole/src/EditProfileDialog.h new file mode 100644 index 00000000..7db840ff --- /dev/null +++ b/konsole/src/EditProfileDialog.h @@ -0,0 +1,276 @@ +/* + Copyright 2007-2008 by Robert Knight + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +#ifndef EDITPROFILEDIALOG_H +#define EDITPROFILEDIALOG_H + +// Qt +#include +#include +#include +#include +#include +#include +#include + +// KDE +#include + +// Konsole +#include "Profile.h" +#include "Enumeration.h" +#include "ColorScheme.h" +#include "ColorSchemeEditor.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class EditProfileDialog; } +QT_END_NAMESPACE + +namespace Konsole +{ +/** + * A dialog which allows the user to edit a profile. + * After the dialog is created, it can be initialized with the settings + * for a profile using setProfile(). When the user makes changes to the + * dialog and accepts the changes, the dialog will update the + * profile in the SessionManager by calling the SessionManager's + * changeProfile() method. + * + * Some changes made in the dialog are preview-only changes which cause + * the SessionManager's changeProfile() method to be called with + * the persistent argument set to false. These changes are then + * un-done when the dialog is closed. + */ +class KONSOLEPRIVATE_EXPORT EditProfileDialog : public KDialog +{ + Q_OBJECT + +public: + /** Constructs a new dialog with the specified parent. */ + explicit EditProfileDialog(QWidget* parent = 0); + virtual ~EditProfileDialog(); + + /** + * Initializes the dialog with the settings for the specified session + * type. + * + * When the dialog closes, the profile will be updated in the SessionManager + * with the altered settings. + * + * @param profile The profile to be edited + */ + void setProfile(Profile::Ptr profile); + + /** + * Selects the text in the profile name edit area. + * When the dialog is being used to create a new profile, + * this can be used to draw the user's attention to the profile name + * and make it easy for them to change it. + */ + void selectProfileName(); + const Profile::Ptr lookupProfile() const; + + static const bool HAVE_TRANSPARENCY; + +public slots: + // reimplemented + virtual void accept(); + // reimplemented + virtual void reject(); + +protected: + virtual bool eventFilter(QObject* watched , QEvent* event); + +private slots: + // sets up the specified tab page if necessary + void preparePage(int); + + // saves changes to profile + void save(); + + // general page + void selectInitialDir(); + void selectIcon(); + + void profileNameChanged(const QString& text); + void initialDirChanged(const QString& text); + void startInSameDir(bool); + void commandChanged(const QString& text); + void tabTitleFormatChanged(const QString& text); + void remoteTabTitleFormatChanged(const QString& text); + + void terminalColumnsEntryChanged(int); + void terminalRowsEntryChanged(int); + void showTerminalSizeHint(bool); + void showEnvironmentEditor(); + void silenceSecondsChanged(int); + + // appearance page + void setFontSize(double pointSize); + void setFontInputValue(const QFont&); + void setAntialiasText(bool enable); + void setBoldIntense(bool enable); + void showFontDialog(); + void newColorScheme(); + void editColorScheme(); + void saveColorScheme(const ColorScheme& scheme, bool isNewScheme); + void removeColorScheme(); + void colorSchemeSelected(); + void previewColorScheme(const QModelIndex& index); + void fontSelected(const QFont&); + void toggleMouseWheelZoom(bool enable); + + // scrolling page + void historyModeChanged(Enum::HistoryModeEnum mode); + + void historySizeChanged(int); + + void hideScrollBar(); + void showScrollBarLeft(); + void showScrollBarRight(); + + void scrollFullPage(); + void scrollHalfPage(); + + // keyboard page + void editKeyBinding(); + void newKeyBinding(); + void keyBindingSelected(); + void removeKeyBinding(); + + // mouse page + void toggleUnderlineLinks(bool); + void toggleOpenLinksByDirectClick(bool); + void toggleCtrlRequiredForDrag(bool); + void toggleCopyTextToClipboard(bool); + void toggleTrimTrailingSpacesInSelectedText(bool); + void pasteFromX11Selection(); + void pasteFromClipboard(); + + void TripleClickModeChanged(int); + void wordCharactersChanged(const QString&); + + // advanced page + void toggleBlinkingText(bool); + void toggleFlowControl(bool); + void togglebidiRendering(bool); + void lineSpacingChanged(int); + void toggleBlinkingCursor(bool); + + void setCursorShape(int); + void autoCursorColor(); + void customCursorColor(); + void customCursorColorChanged(const QColor&); + void setDefaultCodec(QTextCodec*); + + // apply the first previewed changes stored up by delayedPreview() + void delayedPreviewActivate(); + +private: + // initialize various pages of the dialog + void setupGeneralPage(const Profile::Ptr profile); + void setupTabsPage(const Profile::Ptr profile); + void setupAppearancePage(const Profile::Ptr profile); + void setupKeyboardPage(const Profile::Ptr profile); + void setupScrollingPage(const Profile::Ptr profile); + void setupAdvancedPage(const Profile::Ptr profile); + void setupMousePage(const Profile::Ptr info); + + void updateColorSchemeList(bool selectCurrentScheme = false); + void updateColorSchemeButtons(); + void updateKeyBindingsList(bool selectCurrentTranslator = false); + void updateKeyBindingsButtons(); + + void showColorSchemeEditor(bool isNewScheme); + void closeColorSchemeEditor(); + void showKeyBindingEditor(bool newTranslator); + + void preview(int property , const QVariant& value); + void delayedPreview(int property , const QVariant& value); + void unpreview(int property); + void unpreviewAll(); + void enableIfNonEmptySelection(QWidget* widget, QItemSelectionModel* selectionModel); + + void updateCaption(const Profile::Ptr profile); + void updateTransparencyWarning(); + + // Update _tempProfile in a way of respecting the apply button. + // When used with some previewed property, this method should + // always come after the preview operation. + void updateTempProfileProperty(Profile::Property, const QVariant& value); + + // helper method for creating an empty & hidden profile and assigning + // it to _tempProfile. + void createTempProfile(); + + // Enable or disable apply button, used only within + // updateTempProfileProperty(). + void updateButtonApply(); + + static QString groupProfileNames(const ProfileGroup::Ptr group, int maxLength = -1); + + struct RadioOption { + QAbstractButton* button; + int value; + const char* slot; + }; + void setupRadio(RadioOption* possibilities, int actual); + struct BooleanOption { + QAbstractButton* button; + Profile::Property property; + const char* slot; + }; + void setupCheckBoxes(BooleanOption* options , const Profile::Ptr profile); + + Ui::EditProfileDialog* _ui; + Profile::Ptr _tempProfile; + Profile::Ptr _profile; + + // keeps track of pages which need to be updated to match the current + // profile. all elements in this vector are set to true when the + // profile is changed and individual elements are set to false + // after an update by a call to ensurePageLoaded() + QVector _pageNeedsUpdate; + QHash _previewedProperties; + + QHash _delayedPreviewProperties; + QTimer* _delayedPreviewTimer; + + ColorSchemeEditor* _colorDialog; +}; + +/** + * A delegate which can display and edit color schemes in a view. + */ +class ColorSchemeViewDelegate : public QAbstractItemDelegate +{ + Q_OBJECT + +public: + explicit ColorSchemeViewDelegate(QObject* parent = 0); + + // reimplemented + virtual void paint(QPainter* painter, const QStyleOptionViewItem& option, + const QModelIndex& index) const; + virtual QSize sizeHint(const QStyleOptionViewItem& option, + const QModelIndex& index) const; +}; +} + +#endif // EDITPROFILEDIALOG_H diff --git a/konsole/src/EditProfileDialog.ui b/konsole/src/EditProfileDialog.ui new file mode 100644 index 00000000..e2314e8a --- /dev/null +++ b/konsole/src/EditProfileDialog.ui @@ -0,0 +1,1394 @@ + + + EditProfileDialog + + + + 0 + 0 + 594 + 551 + + + + + 0 + + + + + + 0 + 0 + + + + 0 + + + true + + + + General + + + + + + General + + + true + + + + + + Profile name: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + A descriptive name for the profile + + + + + + + + + + Command: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + The command to execute when new terminal sessions are created using this profile + + + Qt::LeftToRight + + + + + + + Initial directory: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + The initial working directory for new terminal sessions using this profile + + + Qt::LeftToRight + + + + + + + Choose the initial directory + + + ... + + + + + + + Start in same directory as current tab + + + + + + + Icon: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + + 64 + 64 + + + + + 0 + 0 + + + + Select the icon displayed on tabs using this profile + + + + + + + 48 + 48 + + + + + + + + Qt::Horizontal + + + + 20 + 20 + + + + + + + + Environment: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Edit the list of environment variables and associated values + + + Edit... + + + + + + + + + + Terminal Size + + + true + + + + + + Columns + + + + + + + 1 + + + 500 + + + + + + + Rows + + + + + + + 1 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 40 + 20 + + + + + + + + This will not alter any open windows. + + + + + + + Qt::Horizontal + + + + 81 + 20 + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 40 + 20 + + + + + + + + Configure Konsole->General->Use current window size on next startup must be disabled for these entries to work. + + + true + + + + + + + + + + Window + + + true + + + + + + Show terminal size in columns and lines in the center of window after resizing + + + Show hint for terminal size after resizing + + + + + + + + + + Qt::Vertical + + + + 20 + 20 + + + + + + + + + Tabs + + + + + + Tab Titles + + + true + + + + + + + + + + + + Tab Monitoring + + + true + + + + + + Threshold for continuous silence: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + The threshold for continuous silence to be detected by Konsole + + + 1 + + + 3600 + + + + + + + Qt::Horizontal + + + + 20 + 20 + + + + + + + + + + + Qt::Vertical + + + + 20 + 10 + + + + + + + + + Appearance + + + + + + + 0 + 1 + + + + Color Scheme && Background + + + true + + + + + + QAbstractItemView::ScrollPerPixel + + + + + + + Create a new color scheme based upon the selected scheme + + + New... + + + + + + + Edit the selected color scheme + + + Edit... + + + + + + + Delete the selected color scheme + + + Remove + + + + + + + Qt::Vertical + + + + 20 + 20 + + + + + + + + + + + + + + Font + + + true + + + + + + + + Preview: + + + + + + + + 1 + 0 + + + + KSqueezedTextLabel + + + Qt::ElideRight + + + + + + + + + + + Text size: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + 4.000000000000000 + + + 999.000000000000000 + + + 1.000000000000000 + + + 1 + + + + + + + Select the font used in this profile + + + Select Font... + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Smooth fonts + + + + + + + Draw intense colors in bold font + + + + + + + + + + + Scrolling + + + + + + Scrollback + + + true + + + + + + + + + + + + Scroll Bar + + + true + + + + + + + 0 + 0 + + + + Show the scroll bar on the left side of the terminal window + + + Show on left side + + + + + + + + 0 + 0 + + + + Show the scroll bar on the right side of the terminal window + + + Show on right side + + + + + + + + 0 + 0 + + + + Hide the scroll bar + + + Hide + + + + + + + + + + Scroll Page Up/Down Amount + + + true + + + + + + Scroll the page the half height of window + + + Half Page Height + + + + + + + Scroll the page the full height of window + + + Full Page Height + + + + + + + + + + Qt::Vertical + + + + 20 + 20 + + + + + + + + + Keyboard + + + + + + Key Bindings + + + true + + + + + + Key bindings control how combinations of keystrokes in the terminal window are converted into the stream of characters which are sent to the current terminal program. + + + true + + + + + + + + 32 + 32 + + + + + + + + Create a new key bindings list based upon the selected bindings + + + New... + + + + + + + Edit the selected key bindings list + + + Edit... + + + + + + + Delete the selected key bindings list + + + Remove + + + + + + + Qt::Vertical + + + + 20 + 20 + + + + + + + + + + + + Mouse + + + + + + Select Text + + + true + + + + + + + + Characters considered part of a word when double clicking: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + true + + + + + + + Characters which are considered part of a word when double-clicking to select whole words in the terminal + + + + + + + + + + + Triple-click select: + + + + + + + Which part of current line should be selected with triple click . + + + + The whole current line + + + + + From mouse position to the end of line + + + + + + + + + + + + + Copy && Paste + + + true + + + + + + Automatically copy selected text into clipboard + + + Copy on select + + + + + + + Trim trailing spaces in selected text, useful in some instances + + + Trim trailing spaces + + + + + + + Qt::Horizontal + + + + 561 + 5 + + + + + + + + Mouse middle button: + + + + + + + Paste from selection + + + + + + + Paste from clipboard + + + + + + + + + + Miscellaneous + + + true + + + + + + Text recognized as a link or an email address will be underlined when hovered by the mouse pointer. + + + Underline links + + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 10 + 20 + + + + + + + + false + + + Text recognized as a link or an email address can be opened by direct mouse click. + + + Open links by direct click + + + + + + + + + Selected text will require control key plus click to drag. + + + Require Ctrl key for drag and drop + + + + + + + Pressing Ctrl+scrollwheel will increase/decrease the text size. + + + Allow Ctrl+scrollwheel to zoom text size + + + + + + + + + + Qt::Vertical + + + + 20 + 328 + + + + + + + + + Advanced + + + + + + Terminal Features + + + true + + + + + + + 0 + 0 + + + + Allow terminal programs to create blinking sections of text + + + Allow blinking text + + + + + + + + 0 + 0 + + + + Allow the output to be suspended by pressing Ctrl+S + + + Enable flow control using Ctrl+S, Ctrl+Q + + + + + + + + 0 + 0 + + + + Enable Bi-Directional display on terminals (valid for Arabic, Farsi or Hebrew only) + + + Enable Bi-Directional text rendering + + + + + + + + + Line Spacing: + + + + + + + The number of pixels between two lines + + + 0 + + + 5 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + Cursor + + + true + + + + + + + 0 + 0 + + + + Make the cursor blink regularly + + + Blinking cursor + + + + + + + + + Cursor shape: + + + + + + + Change the shape of the cursor + + + + Block + + + + + I-Beam + + + + + Underline + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + 0 + 0 + + + + Set the cursor to match the color of the character underneath it. + + + Set cursor color to match current character + + + + + + + + + + 0 + 0 + + + + Use a custom, fixed color for the cursor + + + Custom cursor color: + + + + + + + + 0 + 0 + + + + Select the color used to draw the cursor + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + Encoding + + + true + + + + + + Default character encoding: + + + + + + + + 0 + 0 + + + + DEFAULTENCODING + + + + + + + Select + + + + + + + + + + Qt::Vertical + + + + 20 + 20 + + + + + + + + + + + + + KDoubleNumInput + QWidget +
    knuminput.h
    +
    + + KColorButton + QPushButton +
    kcolorbutton.h
    +
    + + KComboBox + QComboBox +
    kcombobox.h
    +
    + + KLineEdit + QLineEdit +
    klineedit.h
    +
    + + KIntSpinBox + QSpinBox +
    knuminput.h
    +
    + + KSqueezedTextLabel + QLabel +
    ksqueezedtextlabel.h
    +
    + + KMessageWidget + QFrame +
    kmessagewidget.h
    + 1 +
    + + Konsole::RenameTabWidget + QWidget +
    RenameTabWidget.h
    +
    + + Konsole::HistorySizeWidget + QWidget +
    HistorySizeWidget.h
    +
    +
    + + +
    diff --git a/konsole/src/Emulation.cpp b/konsole/src/Emulation.cpp new file mode 100644 index 00000000..ed52e56d --- /dev/null +++ b/konsole/src/Emulation.cpp @@ -0,0 +1,384 @@ +/* + Copyright 2007-2008 Robert Knight + Copyright 1997,1998 by Lars Doelle + Copyright 1996 by Matthias Ettrich + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +// Own +#include "Emulation.h" + +// Qt +#include + +// Konsole +#include "KeyboardTranslator.h" +#include "KeyboardTranslatorManager.h" +#include "Screen.h" +#include "ScreenWindow.h" + +using namespace Konsole; + +Emulation::Emulation() : + _currentScreen(0), + _codec(0), + _decoder(0), + _keyTranslator(0), + _usesMouse(false), + _bracketedPasteMode(false), + _imageSizeInitialized(false) +{ + // create screens with a default size + _screen[0] = new Screen(40, 80); + _screen[1] = new Screen(40, 80); + _currentScreen = _screen[0]; + + QObject::connect(&_bulkTimer1, SIGNAL(timeout()), this, SLOT(showBulk())); + QObject::connect(&_bulkTimer2, SIGNAL(timeout()), this, SLOT(showBulk())); + + // listen for mouse status changes + connect(this , SIGNAL(programUsesMouseChanged(bool)) , + SLOT(usesMouseChanged(bool))); + connect(this , SIGNAL(programBracketedPasteModeChanged(bool)) , + SLOT(bracketedPasteModeChanged(bool))); +} + +bool Emulation::programUsesMouse() const +{ + return _usesMouse; +} + +void Emulation::usesMouseChanged(bool usesMouse) +{ + _usesMouse = usesMouse; +} + +bool Emulation::programBracketedPasteMode() const +{ + return _bracketedPasteMode; +} + +void Emulation::bracketedPasteModeChanged(bool bracketedPasteMode) +{ + _bracketedPasteMode = bracketedPasteMode; +} + +ScreenWindow* Emulation::createWindow() +{ + ScreenWindow* window = new ScreenWindow(_currentScreen); + _windows << window; + + connect(window , SIGNAL(selectionChanged()), + this , SLOT(bufferedUpdate())); + connect(window, SIGNAL(selectionChanged()), + this, SLOT(checkSelectedText())); + + connect(this , SIGNAL(outputChanged()), + window , SLOT(notifyOutputChanged())); + + return window; +} + +void Emulation::checkScreenInUse() +{ + emit primaryScreenInUse(_currentScreen == _screen[0]); +} + +void Emulation::checkSelectedText() +{ + QString text = _currentScreen->selectedText(true); + emit selectionChanged(text); +} + +Emulation::~Emulation() +{ + foreach(ScreenWindow* window, _windows) { + delete window; + } + + delete _screen[0]; + delete _screen[1]; + delete _decoder; +} + +void Emulation::setScreen(int index) +{ + Screen* oldScreen = _currentScreen; + _currentScreen = _screen[index & 1]; + if (_currentScreen != oldScreen) { + // tell all windows onto this emulation to switch to the newly active screen + foreach(ScreenWindow * window, _windows) { + window->setScreen(_currentScreen); + } + + checkScreenInUse(); + checkSelectedText(); + } +} + +void Emulation::clearHistory() +{ + _screen[0]->setScroll(_screen[0]->getScroll() , false); +} +void Emulation::setHistory(const HistoryType& history) +{ + _screen[0]->setScroll(history); + + showBulk(); +} + +const HistoryType& Emulation::history() const +{ + return _screen[0]->getScroll(); +} + +void Emulation::setCodec(const QTextCodec * codec) +{ + if (codec) { + _codec = codec; + + delete _decoder; + _decoder = _codec->makeDecoder(); + + emit useUtf8Request(utf8()); + } else { + setCodec(LocaleCodec); + } +} + +void Emulation::setCodec(EmulationCodec codec) +{ + if (codec == Utf8Codec) + setCodec(QTextCodec::codecForName("utf8")); + else if (codec == LocaleCodec) + setCodec(QTextCodec::codecForLocale()); +} + +void Emulation::setKeyBindings(const QString& name) +{ + _keyTranslator = KeyboardTranslatorManager::instance()->findTranslator(name); + if (!_keyTranslator) { + _keyTranslator = KeyboardTranslatorManager::instance()->defaultTranslator(); + } +} + +QString Emulation::keyBindings() const +{ + return _keyTranslator->name(); +} + +// process application unicode input to terminal +// this is a trivial scanner +void Emulation::receiveChar(int c) +{ + c &= 0xff; + switch (c) { + case '\b' : _currentScreen->backspace(); break; + case '\t' : _currentScreen->tab(); break; + case '\n' : _currentScreen->newLine(); break; + case '\r' : _currentScreen->toStartOfLine(); break; + case 0x07 : emit stateSet(NOTIFYBELL); break; + default : _currentScreen->displayCharacter(c); break; + } +} + +void Emulation::sendKeyEvent(QKeyEvent* ev) +{ + emit stateSet(NOTIFYNORMAL); + + if (!ev->text().isEmpty()) { + // A block of text + // Note that the text is proper unicode. + // We should do a conversion here + emit sendData(ev->text().toUtf8(), ev->text().length()); + } +} + +void Emulation::sendString(const char*, int) +{ + // default implementation does nothing +} + +void Emulation::sendMouseEvent(int /*buttons*/, int /*column*/, int /*row*/, int /*eventType*/) +{ + // default implementation does nothing +} + +/* + We are doing code conversion from locale to unicode first. +*/ + +void Emulation::receiveData(const char* text, int length) +{ + emit stateSet(NOTIFYACTIVITY); + + bufferedUpdate(); + + QString unicodeText = _decoder->toUnicode(text, length); + + //send characters to terminal emulator + for (int i = 0; i < unicodeText.length(); i++) + receiveChar(unicodeText[i].unicode()); + + //look for z-modem indicator + //-- someone who understands more about z-modems that I do may be able to move + //this check into the above for loop? + for (int i = 0; i < length; i++) { + if (text[i] == '\030') { + if ((length - i - 1 > 3) && (qstrncmp(text + i + 1, "B00", 3) == 0)) + emit zmodemDetected(); + } + } +} + +//OLDER VERSION +//This version of onRcvBlock was commented out because +// a) It decoded incoming characters one-by-one, which is slow in the current version of Qt (4.2 tech preview) +// b) It messed up decoding of non-ASCII characters, with the result that (for example) chinese characters +// were not printed properly. +// +//There is something about stopping the _decoder if "we get a control code halfway a multi-byte sequence" (see below) +//which hasn't been ported into the newer function (above). Hopefully someone who understands this better +//can find an alternative way of handling the check. + +/*void Emulation::onRcvBlock(const char *s, int len) +{ + emit notifySessionState(NOTIFYACTIVITY); + + bufferedUpdate(); + for (int i = 0; i < len; i++) + { + + QString result = _decoder->toUnicode(&s[i],1); + int reslen = result.length(); + + // If we get a control code halfway a multi-byte sequence + // we flush the _decoder and continue with the control code. + if ((s[i] < 32) && (s[i] > 0)) + { + // Flush _decoder + while(!result.length()) + result = _decoder->toUnicode(&s[i],1); + reslen = 1; + result.resize(reslen); + result[0] = QChar(s[i]); + } + + for (int j = 0; j < reslen; j++) + { + if (result[j].characterategory() == QChar::Mark_NonSpacing) + _currentScreen->compose(result.mid(j,1)); + else + onRcvChar(result[j].unicode()); + } + if (s[i] == '\030') + { + if ((len-i-1 > 3) && (qstrncmp(s+i+1, "B00", 3) == 0)) + emit zmodemDetected(); + } + } +}*/ + +void Emulation::writeToStream(TerminalCharacterDecoder* decoder , + int startLine , + int endLine) +{ + _currentScreen->writeLinesToStream(decoder, startLine, endLine); +} + +int Emulation::lineCount() const +{ + // sum number of lines currently on _screen plus number of lines in history + return _currentScreen->getLines() + _currentScreen->getHistLines(); +} + +void Emulation::showBulk() +{ + _bulkTimer1.stop(); + _bulkTimer2.stop(); + + emit outputChanged(); + + _currentScreen->resetScrolledLines(); + _currentScreen->resetDroppedLines(); +} + +void Emulation::bufferedUpdate() +{ + static const int BULK_TIMEOUT1 = 10; + static const int BULK_TIMEOUT2 = 40; + + _bulkTimer1.setSingleShot(true); + _bulkTimer1.start(BULK_TIMEOUT1); + if (!_bulkTimer2.isActive()) { + _bulkTimer2.setSingleShot(true); + _bulkTimer2.start(BULK_TIMEOUT2); + } +} + +char Emulation::eraseChar() const +{ + return '\b'; +} + +void Emulation::setImageSize(int lines, int columns) +{ + if ((lines < 1) || (columns < 1)) + return; + + QSize screenSize[2] = { QSize(_screen[0]->getColumns(), + _screen[0]->getLines()), + QSize(_screen[1]->getColumns(), + _screen[1]->getLines()) + }; + QSize newSize(columns, lines); + + if (newSize == screenSize[0] && newSize == screenSize[1]) { + // If this method is called for the first time, always emit + // SIGNAL(imageSizeChange()), even if the new size is the same as the + // current size. See #176902 + if (!_imageSizeInitialized) { + emit imageSizeChanged(lines, columns); + } + } else { + _screen[0]->resizeImage(lines, columns); + _screen[1]->resizeImage(lines, columns); + + emit imageSizeChanged(lines, columns); + + bufferedUpdate(); + } + + if (!_imageSizeInitialized) { + _imageSizeInitialized = true; + + // FIXME + // a hard-coded, small delay is introduced to guarantee Session::run() + // does not get triggered by SIGNAL(imageSizeInitialized()) before + // Pty::setWindowSize() is triggered by previously emitted + // SIGNAL(imageSizeChanged()); See #203185 + QTimer::singleShot(200, this, SIGNAL(imageSizeInitialized())); + } +} + +QSize Emulation::imageSize() const +{ + return QSize(_currentScreen->getColumns(), _currentScreen->getLines()); +} + +#include "moc_Emulation.cpp" + diff --git a/konsole/src/Emulation.h b/konsole/src/Emulation.h new file mode 100644 index 00000000..9ca7f8f5 --- /dev/null +++ b/konsole/src/Emulation.h @@ -0,0 +1,491 @@ +/* + This file is part of Konsole, an X terminal. + + Copyright 2007-2008 by Robert Knight + Copyright 1997,1998 by Lars Doelle + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +#ifndef EMULATION_H +#define EMULATION_H + +// Qt +#include +#include +#include +#include + +// Konsole +#include "konsoleprivate_export.h" + +namespace Konsole +{ +class KeyboardTranslator; +class HistoryType; +class Screen; +class ScreenWindow; +class TerminalCharacterDecoder; + +/** + * This enum describes the available states which + * the terminal emulation may be set to. + * + * These are the values used by Emulation::stateChanged() + */ +enum { + /** The emulation is currently receiving user input. */ + NOTIFYNORMAL = 0, + /** + * The terminal program has triggered a bell event + * to get the user's attention. + */ + NOTIFYBELL = 1, + /** + * The emulation is currently receiving data from its + * terminal input. + */ + NOTIFYACTIVITY = 2, + + // unused here? + NOTIFYSILENCE = 3 +}; + +/** + * Base class for terminal emulation back-ends. + * + * The back-end is responsible for decoding an incoming character stream and + * producing an output image of characters. + * + * When input from the terminal is received, the receiveData() slot should be called with + * the data which has arrived. The emulation will process the data and update the + * screen image accordingly. The codec used to decode the incoming character stream + * into the unicode characters used internally can be specified using setCodec() + * + * The size of the screen image can be specified by calling setImageSize() with the + * desired number of lines and columns. When new lines are added, old content + * is moved into a history store, which can be set by calling setHistory(). + * + * The screen image can be accessed by creating a ScreenWindow onto this emulation + * by calling createWindow(). Screen windows provide access to a section of the + * output. Each screen window covers the same number of lines and columns as the + * image size returned by imageSize(). The screen window can be moved up and down + * and provides transparent access to both the current on-screen image and the + * previous output. The screen windows emit an outputChanged signal + * when the section of the image they are looking at changes. + * Graphical views can then render the contents of a screen window, listening for notifications + * of output changes from the screen window which they are associated with and updating + * accordingly. + * + * The emulation also is also responsible for converting input from the connected views such + * as keypresses and mouse activity into a character string which can be sent + * to the terminal program. Key presses can be processed by calling the sendKeyEvent() slot, + * while mouse events can be processed using the sendMouseEvent() slot. When the character + * stream has been produced, the emulation will emit a sendData() signal with a pointer + * to the character buffer. This data should be fed to the standard input of the terminal + * process. The translation of key presses into an output character stream is performed + * using a lookup in a set of key bindings which map key sequences to output + * character sequences. The name of the key bindings set used can be specified using + * setKeyBindings() + * + * The emulation maintains certain state information which changes depending on the + * input received. The emulation can be reset back to its starting state by calling + * reset(). + * + * The emulation also maintains an activity state, which specifies whether + * terminal is currently active ( when data is received ), normal + * ( when the terminal is idle or receiving user input ) or trying + * to alert the user ( also known as a "Bell" event ). The stateSet() signal + * is emitted whenever the activity state is set. This can be used to determine + * how long the emulation has been active/idle for and also respond to + * a 'bell' event in different ways. + */ +class KONSOLEPRIVATE_EXPORT Emulation : public QObject +{ + Q_OBJECT + +public: + /** Constructs a new terminal emulation */ + Emulation(); + ~Emulation(); + + /** + * Creates a new window onto the output from this emulation. The contents + * of the window are then rendered by views which are set to use this window using the + * TerminalDisplay::setScreenWindow() method. + */ + ScreenWindow* createWindow(); + + /** Returns the size of the screen image which the emulation produces */ + QSize imageSize() const; + + /** + * Returns the total number of lines, including those stored in the history. + */ + int lineCount() const; + + /** + * Sets the history store used by this emulation. When new lines + * are added to the output, older lines at the top of the screen are transferred to a history + * store. + * + * The number of lines which are kept and the storage location depend on the + * type of store. + */ + void setHistory(const HistoryType&); + /** Returns the history store used by this emulation. See setHistory() */ + const HistoryType& history() const; + /** Clears the history scroll. */ + void clearHistory(); + + /** + * Copies the output history from @p startLine to @p endLine + * into @p stream, using @p decoder to convert the terminal + * characters into text. + * + * @param decoder A decoder which converts lines of terminal characters with + * appearance attributes into output text. PlainTextDecoder is the most commonly + * used decoder. + * @param startLine Index of first line to copy + * @param endLine Index of last line to copy + */ + virtual void writeToStream(TerminalCharacterDecoder* decoder, int startLine, int endLine); + + /** Returns the codec used to decode incoming characters. See setCodec() */ + const QTextCodec* codec() const { + return _codec; + } + /** Sets the codec used to decode incoming characters. */ + void setCodec(const QTextCodec*); + + /** + * Convenience method. + * Returns true if the current codec used to decode incoming + * characters is UTF-8 + */ + bool utf8() const { + Q_ASSERT(_codec); + return _codec->mibEnum() == 106; + } + + + /** Returns the special character used for erasing character. */ + virtual char eraseChar() const; + + /** + * Sets the key bindings used to key events + * ( received through sendKeyEvent() ) into character + * streams to send to the terminal. + */ + void setKeyBindings(const QString& name); + /** + * Returns the name of the emulation's current key bindings. + * See setKeyBindings() + */ + QString keyBindings() const; + + /** + * Copies the current image into the history and clears the screen. + */ + virtual void clearEntireScreen() = 0; + + /** Resets the state of the terminal. */ + virtual void reset() = 0; + + /** + * Returns true if the active terminal program wants + * mouse input events. + * + * The programUsesMouseChanged() signal is emitted when this + * changes. + */ + bool programUsesMouse() const; + + bool programBracketedPasteMode() const; + +public slots: + + /** Change the size of the emulation's image */ + virtual void setImageSize(int lines, int columns); + + /** + * Interprets a sequence of characters and sends the result to the terminal. + * This is equivalent to calling sendKeyEvent() for each character in @p text in succession. + */ + virtual void sendText(const QString& text) = 0; + + /** + * Interprets a key press event and emits the sendData() signal with + * the resulting character stream. + */ + virtual void sendKeyEvent(QKeyEvent*); + + /** + * Converts information about a mouse event into an xterm-compatible escape + * sequence and emits the character sequence via sendData() + */ + virtual void sendMouseEvent(int buttons, int column, int line, int eventType); + + /** + * Sends a string of characters to the foreground terminal process. + * + * @param string The characters to send. + * @param length Length of @p string or if set to a negative value, @p string will + * be treated as a null-terminated string and its length will be determined automatically. + */ + virtual void sendString(const char* string, int length = -1) = 0; + + /** + * Processes an incoming stream of characters. receiveData() decodes the incoming + * character buffer using the current codec(), and then calls receiveChar() for + * each unicode character in the resulting buffer. + * + * receiveData() also starts a timer which causes the outputChanged() signal + * to be emitted when it expires. The timer allows multiple updates in quick + * succession to be buffered into a single outputChanged() signal emission. + * + * @param buffer A string of characters received from the terminal program. + * @param len The length of @p buffer + */ + void receiveData(const char* buffer, int len); + +signals: + + /** + * Emitted when a buffer of data is ready to send to the + * standard input of the terminal. + * + * @param data The buffer of data ready to be sent + * @param len The length of @p data in bytes + */ + void sendData(const char* data, int len); + + /** + * Requests that the pty used by the terminal process + * be set to UTF 8 mode. + * + * Refer to the IUTF8 entry in termios(3) for more information. + */ + void useUtf8Request(bool); + + /** + * Emitted when the activity state of the emulation is set. + * + * @param state The new activity state, one of NOTIFYNORMAL, NOTIFYACTIVITY + * or NOTIFYBELL + */ + void stateSet(int state); + + /** + * Emitted when the special sequence indicating the request for data + * transmission through ZModem protocol is detected. + */ + void zmodemDetected(); + + + /** + * Requests that the color of the text used + * to represent the tabs associated with this + * emulation be changed. This is a Konsole-specific + * extension from pre-KDE 4 times. + * + * TODO: Document how the parameter works. + */ + void changeTabTextColorRequest(int color); + + /** + * This is emitted when the program running in the shell indicates whether or + * not it is interested in mouse events. + * + * @param usesMouse This will be true if the program wants to be informed about + * mouse events or false otherwise. + */ + void programUsesMouseChanged(bool usesMouse); + + void programBracketedPasteModeChanged(bool bracketedPasteMode); + + /** + * Emitted when the contents of the screen image change. + * The emulation buffers the updates from successive image changes, + * and only emits outputChanged() at sensible intervals when + * there is a lot of terminal activity. + * + * Normally there is no need for objects other than the screen windows + * created with createWindow() to listen for this signal. + * + * ScreenWindow objects created using createWindow() will emit their + * own outputChanged() signal in response to this signal. + */ + void outputChanged(); + + /** + * Emitted when the program running in the terminal wishes to update the + * session's title. This also allows terminal programs to customize other + * aspects of the terminal emulation display. + * + * This signal is emitted when the escape sequence "\033]ARG;VALUE\007" + * is received in the input string, where ARG is a number specifying what + * should change and VALUE is a string specifying the new value. + * + * TODO: The name of this method is not very accurate since this method + * is used to perform a whole range of tasks besides just setting + * the user-title of the session. + * + * @param title Specifies what to change. + *
      + *
    • 0 - Set window icon text and session title to @p newTitle
    • + *
    • 1 - Set window icon text to @p newTitle
    • + *
    • 2 - Set session title to @p newTitle
    • + *
    • 11 - Set the session's default background color to @p newTitle, + * where @p newTitle can be an HTML-style string ("#RRGGBB") or a named + * color (eg 'red', 'blue'). + * See http://doc.trolltech.com/4.2/qcolor.html#setNamedColor for more + * details. + *
    • + *
    • 31 - Supposedly treats @p newTitle as a URL and opens it (NOT IMPLEMENTED)
    • + *
    • 32 - Sets the icon associated with the session. @p newTitle is the name + * of the icon to use, which can be the name of any icon in the current KDE icon + * theme (eg: 'konsole', 'kate', 'folder_home')
    • + *
    + * @param newTitle Specifies the new title + */ + + void titleChanged(int title, const QString& newTitle); + + /** + * Emitted when the terminal emulator's size has changed + */ + void imageSizeChanged(int lineCount , int columnCount); + + /** + * Emitted when the setImageSize() is called on this emulation for + * the first time. + */ + void imageSizeInitialized(); + + /** + * Emitted after receiving the escape sequence which asks to change + * the terminal emulator's size + */ + void imageResizeRequest(const QSize& sizz); + + /** + * Emitted when the terminal program requests to change various properties + * of the terminal display. + * + * A profile change command occurs when a special escape sequence, followed + * by a string containing a series of name and value pairs is received. + * This string can be parsed using a ProfileCommandParser instance. + * + * @param text A string expected to contain a series of key and value pairs in + * the form: name=value;name2=value2 ... + */ + void profileChangeCommandReceived(const QString& text); + + /** + * Emitted when a flow control key combination ( Ctrl+S or Ctrl+Q ) is pressed. + * @param suspendKeyPressed True if Ctrl+S was pressed to suspend output or Ctrl+Q to + * resume output. + */ + void flowControlKeyPressed(bool suspendKeyPressed); + + /** + * Emitted when the active screen is switched, to indicate whether the primary + * screen is in use. + */ + void primaryScreenInUse(bool use); + + /** + * Emitted when the text selection is changed + */ + void selectionChanged(const QString& text); + +protected: + virtual void setMode(int mode) = 0; + virtual void resetMode(int mode) = 0; + + /** + * Processes an incoming character. See receiveData() + * @p ch A unicode character code. + */ + virtual void receiveChar(int ch); + + /** + * Sets the active screen. The terminal has two screens, primary and alternate. + * The primary screen is used by default. When certain interactive programs such + * as Vim are run, they trigger a switch to the alternate screen. + * + * @param index 0 to switch to the primary screen, or 1 to switch to the alternate screen + */ + void setScreen(int index); + + enum EmulationCodec { + LocaleCodec = 0, + Utf8Codec = 1 + }; + + void setCodec(EmulationCodec codec); + + QList _windows; + + Screen* _currentScreen; // pointer to the screen which is currently active, + // this is one of the elements in the screen[] array + + Screen* _screen[2]; // 0 = primary screen ( used by most programs, including the shell + // scrollbars are enabled in this mode ) + // 1 = alternate ( used by vi , emacs etc. + // scrollbars are not enabled in this mode ) + + + //decodes an incoming C-style character stream into a unicode QString using + //the current text codec. (this allows for rendering of non-ASCII characters in text files etc.) + const QTextCodec* _codec; + QTextDecoder* _decoder; + const KeyboardTranslator* _keyTranslator; // the keyboard layout + +protected slots: + /** + * Schedules an update of attached views. + * Repeated calls to bufferedUpdate() in close succession will result in only a single update, + * much like the Qt buffered update of widgets. + */ + void bufferedUpdate(); + + // used to emit the primaryScreenInUse(bool) signal + void checkScreenInUse(); + + // used to emit the selectionChanged(QString) signal + void checkSelectedText(); + +private slots: + // triggered by timer, causes the emulation to send an updated screen image to each + // view + void showBulk(); + + void usesMouseChanged(bool usesMouse); + + void bracketedPasteModeChanged(bool bracketedPasteMode); + +private: + bool _usesMouse; + bool _bracketedPasteMode; + QTimer _bulkTimer1; + QTimer _bulkTimer2; + bool _imageSizeInitialized; +}; +} + +#endif // ifndef EMULATION_H diff --git a/konsole/src/Enumeration.h b/konsole/src/Enumeration.h new file mode 100644 index 00000000..a9fab40d --- /dev/null +++ b/konsole/src/Enumeration.h @@ -0,0 +1,125 @@ +/* + This file is part of Konsole, KDE's terminal emulator. + + Copyright 2012 by Jekyll Wu + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +#ifndef ENUMERATION_H +#define ENUMERATION_H + +namespace Konsole +{ +/** + * This class serves as a place for putting enum definitions that are + * used or referenced in multiple places in the code. Keep it small. + */ +class Enum +{ +public: + /** + * This enum describes the modes available to remember lines of output + * produced by the terminal. + */ + enum HistoryModeEnum { + /** No output is remembered. As soon as lines of text are scrolled + * off-screen they are lost. + */ + NoHistory = 0, + /** A fixed number of lines of output are remembered. Once the + * limit is reached, the oldest lines are lost. + */ + FixedSizeHistory = 1, + /** All output is remembered for the duration of the session. + * Typically this means that lines are recorded to + * a file as they are scrolled off-screen. + */ + UnlimitedHistory = 2 + }; + + /** + * This enum describes the positions where the terminal display's + * scroll bar may be placed. + */ + enum ScrollBarPositionEnum { + /** Show the scroll-bar on the left of the terminal display. */ + ScrollBarLeft = 0, + /** Show the scroll-bar on the right of the terminal display. */ + ScrollBarRight = 1, + /** Do not show the scroll-bar. */ + ScrollBarHidden = 2 + }; + + /** + * This enum describes the amount that Page Up/Down scroll by. + */ + enum ScrollPageAmountEnum { + /** Scroll half page */ + ScrollPageHalf = 0, + /** Scroll full page */ + ScrollPageFull = 1, + }; + + /** This enum describes the shapes used to draw the cursor in terminal + * displays. + */ + enum CursorShapeEnum { + /** Use a solid rectangular block to draw the cursor. */ + BlockCursor = 0, + /** Use an 'I' shape, similar to that used in text editing + * applications, to draw the cursor. + */ + IBeamCursor = 1, + /** Draw a line underneath the cursor's position. */ + UnderlineCursor = 2 + }; + + /** This enum describes the behavior of triple click action . */ + enum TripleClickModeEnum { + /** Select the whole line underneath the cursor. */ + SelectWholeLine = 0, + /** Select from the current cursor position to the end of the line. */ + SelectForwardsFromCursor = 1 + }; + + /** This enum describes the source from which mouse middle click pastes data . */ + enum MiddleClickPasteModeEnum { + /** Paste from X11 Selection. */ + PasteFromX11Selection = 0, + /** Paste from Clipboard. */ + PasteFromClipboard = 1 + }; + + /** + * This enum describes the different types of sounds and visual effects which + * can be used to alert the user when a 'bell' occurs in the terminal + * session. + */ + enum BellModeEnum { + /** trigger system beep. */ + SystemBeepBell = 0, + /** trigger system notification. */ + NotifyBell = 1, + /** trigger visual bell(inverting the display's colors briefly). */ + VisualBell = 2, + /** No bell effects */ + NoBell = 3 + }; +}; +} +#endif // ENUMERATION_H + diff --git a/konsole/src/ExtendedCharTable.cpp b/konsole/src/ExtendedCharTable.cpp new file mode 100644 index 00000000..f8ceca95 --- /dev/null +++ b/konsole/src/ExtendedCharTable.cpp @@ -0,0 +1,153 @@ +/* + This file is part of Konsole, an X terminal. + + Copyright 2007-2008 by Robert Knight + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +// Own +#include "ExtendedCharTable.h" + +// KDE +#include + +// Konsole +#include "TerminalDisplay.h" +#include "SessionManager.h" +#include "Session.h" +#include "Screen.h" + +using namespace Konsole; + +ExtendedCharTable::ExtendedCharTable() +{ +} + +ExtendedCharTable::~ExtendedCharTable() +{ + // free all allocated character buffers + QHashIterator iter(extendedCharTable); + while (iter.hasNext()) { + iter.next(); + delete[] iter.value(); + } +} + +// global instance +ExtendedCharTable ExtendedCharTable::instance; + +ushort ExtendedCharTable::createExtendedChar(const ushort* unicodePoints , ushort length) +{ + // look for this sequence of points in the table + ushort hash = extendedCharHash(unicodePoints, length); + const ushort initialHash = hash; + bool triedCleaningSolution = false; + + // check existing entry for match + while (extendedCharTable.contains(hash) && hash != 0) { // 0 has a special meaning for chars so we don't use it + if (extendedCharMatch(hash, unicodePoints, length)) { + // this sequence already has an entry in the table, + // return its hash + return hash; + } else { + // if hash is already used by another, different sequence of unicode character + // points then try next hash + hash++; + + if (hash == initialHash) { + if (!triedCleaningSolution) { + triedCleaningSolution = true; + // All the hashes are full, go to all Screens and try to free any + // This is slow but should happen very rarely + QSet usedExtendedChars; + const SessionManager* sm = SessionManager::instance(); + foreach(const Session * s, sm->sessions()) { + foreach(const TerminalDisplay * td, s->views()) { + usedExtendedChars += td->screenWindow()->screen()->usedExtendedChars(); + } + } + + QHash::iterator it = extendedCharTable.begin(); + QHash::iterator itEnd = extendedCharTable.end(); + while (it != itEnd) { + if (usedExtendedChars.contains(it.key())) { + ++it; + } else { + it = extendedCharTable.erase(it); + } + } + } else { + kWarning() << "Using all the extended char hashes, going to miss this extended character"; + return 0; + } + } + } + } + + // add the new sequence to the table and + // return that index + ushort* buffer = new ushort[length + 1]; + buffer[0] = length; + for (int i = 0 ; i < length ; i++) + buffer[i + 1] = unicodePoints[i]; + + extendedCharTable.insert(hash, buffer); + + return hash; +} + +ushort* ExtendedCharTable::lookupExtendedChar(ushort hash , ushort& length) const +{ + // look up index in table and if found, set the length + // argument and return a pointer to the character sequence + + ushort* buffer = extendedCharTable[hash]; + if (buffer) { + length = buffer[0]; + return buffer + 1; + } else { + length = 0; + return 0; + } +} + +ushort ExtendedCharTable::extendedCharHash(const ushort* unicodePoints , ushort length) const +{ + ushort hash = 0; + for (ushort i = 0 ; i < length ; i++) { + hash = 31 * hash + unicodePoints[i]; + } + return hash; +} + +bool ExtendedCharTable::extendedCharMatch(ushort hash , const ushort* unicodePoints , ushort length) const +{ + ushort* entry = extendedCharTable[hash]; + + // compare given length with stored sequence length ( given as the first ushort in the + // stored buffer ) + if (entry == 0 || entry[0] != length) + return false; + // if the lengths match, each character must be checked. the stored buffer starts at + // entry[1] + for (int i = 0 ; i < length ; i++) { + if (entry[i + 1] != unicodePoints[i]) + return false; + } + return true; +} + diff --git a/konsole/src/ExtendedCharTable.h b/konsole/src/ExtendedCharTable.h new file mode 100644 index 00000000..03e6c105 --- /dev/null +++ b/konsole/src/ExtendedCharTable.h @@ -0,0 +1,81 @@ +/* + This file is part of Konsole, an X terminal. + + Copyright 2007-2008 by Robert Knight + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +#ifndef EXTENDEDCHARTABLE_H +#define EXTENDEDCHARTABLE_H + +// Qt +#include + +namespace Konsole +{ +/** + * A table which stores sequences of unicode characters, referenced + * by hash keys. The hash key itself is the same size as a unicode + * character ( ushort ) so that it can occupy the same space in + * a structure. + */ +class ExtendedCharTable +{ +public: + /** Constructs a new character table. */ + ExtendedCharTable(); + ~ExtendedCharTable(); + + /** + * Adds a sequences of unicode characters to the table and returns + * a hash code which can be used later to look up the sequence + * using lookupExtendedChar() + * + * If the same sequence already exists in the table, the hash + * of the existing sequence will be returned. + * + * @param unicodePoints An array of unicode character points + * @param length Length of @p unicodePoints + */ + ushort createExtendedChar(const ushort* unicodePoints , ushort length); + /** + * Looks up and returns a pointer to a sequence of unicode characters + * which was added to the table using createExtendedChar(). + * + * @param hash The hash key returned by createExtendedChar() + * @param length This variable is set to the length of the + * character sequence. + * + * @return A unicode character sequence of size @p length. + */ + ushort* lookupExtendedChar(ushort hash , ushort& length) const; + + /** The global ExtendedCharTable instance. */ + static ExtendedCharTable instance; +private: + // calculates the hash key of a sequence of unicode points of size 'length' + ushort extendedCharHash(const ushort* unicodePoints , ushort length) const; + // tests whether the entry in the table specified by 'hash' matches the + // character sequence 'unicodePoints' of size 'length' + bool extendedCharMatch(ushort hash , const ushort* unicodePoints , ushort length) const; + // internal, maps hash keys to character sequence buffers. The first ushort + // in each value is the length of the buffer, followed by the ushorts in the buffer + // themselves. + QHash extendedCharTable; +}; +} +#endif // end of EXTENDEDCHARTABLE_H diff --git a/konsole/src/Filter.cpp b/konsole/src/Filter.cpp new file mode 100644 index 00000000..63d45e13 --- /dev/null +++ b/konsole/src/Filter.cpp @@ -0,0 +1,487 @@ +/* + Copyright 2007-2008 by Robert Knight + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +// Own +#include "Filter.h" + +// Qt +#include +#include +#include +#include +#include + +// KDE +#include +#include + +// Konsole +#include "TerminalCharacterDecoder.h" +#include "konsole_wcwidth.h" + +using namespace Konsole; + +FilterChain::~FilterChain() +{ + QMutableListIterator iter(*this); + + while (iter.hasNext()) { + Filter* filter = iter.next(); + iter.remove(); + delete filter; + } +} + +void FilterChain::addFilter(Filter* filter) +{ + append(filter); +} +void FilterChain::removeFilter(Filter* filter) +{ + removeAll(filter); +} +void FilterChain::reset() +{ + QListIterator iter(*this); + while (iter.hasNext()) + iter.next()->reset(); +} +void FilterChain::setBuffer(const QString* buffer , const QList* linePositions) +{ + QListIterator iter(*this); + while (iter.hasNext()) + iter.next()->setBuffer(buffer, linePositions); +} +void FilterChain::process() +{ + QListIterator iter(*this); + while (iter.hasNext()) + iter.next()->process(); +} +void FilterChain::clear() +{ + QList::clear(); +} +Filter::HotSpot* FilterChain::hotSpotAt(int line , int column) const +{ + QListIterator iter(*this); + while (iter.hasNext()) { + Filter* filter = iter.next(); + Filter::HotSpot* spot = filter->hotSpotAt(line, column); + if (spot != 0) { + return spot; + } + } + + return 0; +} + +QList FilterChain::hotSpots() const +{ + QList list; + QListIterator iter(*this); + while (iter.hasNext()) { + Filter* filter = iter.next(); + list << filter->hotSpots(); + } + return list; +} + +TerminalImageFilterChain::TerminalImageFilterChain() + : _buffer(0) + , _linePositions(0) +{ +} + +TerminalImageFilterChain::~TerminalImageFilterChain() +{ + delete _buffer; + delete _linePositions; +} + +void TerminalImageFilterChain::setImage(const Character* const image , int lines , int columns, const QVector& lineProperties) +{ + if (empty()) + return; + + // reset all filters and hotspots + reset(); + + PlainTextDecoder decoder; + decoder.setTrailingWhitespace(false); + + // setup new shared buffers for the filters to process on + QString* newBuffer = new QString(); + QList* newLinePositions = new QList(); + setBuffer(newBuffer , newLinePositions); + + // free the old buffers + delete _buffer; + delete _linePositions; + + _buffer = newBuffer; + _linePositions = newLinePositions; + + QTextStream lineStream(_buffer); + decoder.begin(&lineStream); + + for (int i = 0 ; i < lines ; i++) { + _linePositions->append(_buffer->length()); + decoder.decodeLine(image + i * columns, columns, LINE_DEFAULT); + + // pretend that each line ends with a newline character. + // this prevents a link that occurs at the end of one line + // being treated as part of a link that occurs at the start of the next line + // + // the downside is that links which are spread over more than one line are not + // highlighted. + // + // TODO - Use the "line wrapped" attribute associated with lines in a + // terminal image to avoid adding this imaginary character for wrapped + // lines + if (!(lineProperties.value(i, LINE_DEFAULT) & LINE_WRAPPED)) + lineStream << QChar('\n'); + } + decoder.end(); +} + +Filter::Filter() : + _linePositions(0), + _buffer(0) +{ +} + +Filter::~Filter() +{ + QListIterator iter(_hotspotList); + while (iter.hasNext()) { + delete iter.next(); + } +} +void Filter::reset() +{ + _hotspots.clear(); + _hotspotList.clear(); +} + +void Filter::setBuffer(const QString* buffer , const QList* linePositions) +{ + _buffer = buffer; + _linePositions = linePositions; +} + +void Filter::getLineColumn(int position , int& startLine , int& startColumn) +{ + Q_ASSERT(_linePositions); + Q_ASSERT(_buffer); + + for (int i = 0 ; i < _linePositions->count() ; i++) { + int nextLine = 0; + + if (i == _linePositions->count() - 1) + nextLine = _buffer->length() + 1; + else + nextLine = _linePositions->value(i + 1); + + if (_linePositions->value(i) <= position && position < nextLine) { + startLine = i; + startColumn = string_width(buffer()->mid(_linePositions->value(i), position - _linePositions->value(i))); + return; + } + } +} + +/*void Filter::addLine(const QString& text) +{ + _linePositions << _buffer.length(); + _buffer.append(text); +}*/ + +const QString* Filter::buffer() +{ + return _buffer; +} +Filter::HotSpot::~HotSpot() +{ +} +void Filter::addHotSpot(HotSpot* spot) +{ + _hotspotList << spot; + + for (int line = spot->startLine() ; line <= spot->endLine() ; line++) { + _hotspots.insert(line, spot); + } +} +QList Filter::hotSpots() const +{ + return _hotspotList; +} +QList Filter::hotSpotsAtLine(int line) const +{ + return _hotspots.values(line); +} + +Filter::HotSpot* Filter::hotSpotAt(int line , int column) const +{ + QList hotspots = _hotspots.values(line); + + foreach(HotSpot* spot, hotspots) { + if (spot->startLine() == line && spot->startColumn() > column) + continue; + if (spot->endLine() == line && spot->endColumn() < column) + continue; + + return spot; + } + + return 0; +} + +Filter::HotSpot::HotSpot(int startLine , int startColumn , int endLine , int endColumn) + : _startLine(startLine) + , _startColumn(startColumn) + , _endLine(endLine) + , _endColumn(endColumn) + , _type(NotSpecified) +{ +} +QList Filter::HotSpot::actions() +{ + return QList(); +} +int Filter::HotSpot::startLine() const +{ + return _startLine; +} +int Filter::HotSpot::endLine() const +{ + return _endLine; +} +int Filter::HotSpot::startColumn() const +{ + return _startColumn; +} +int Filter::HotSpot::endColumn() const +{ + return _endColumn; +} +Filter::HotSpot::Type Filter::HotSpot::type() const +{ + return _type; +} +void Filter::HotSpot::setType(Type type) +{ + _type = type; +} + +RegExpFilter::RegExpFilter() +{ +} + +RegExpFilter::HotSpot::HotSpot(int startLine, int startColumn, int endLine, int endColumn) + : Filter::HotSpot(startLine, startColumn, endLine, endColumn) +{ + setType(Marker); +} + +void RegExpFilter::HotSpot::activate(QObject*) +{ +} + +void RegExpFilter::HotSpot::setCapturedTexts(const QStringList& texts) +{ + _capturedTexts = texts; +} +QStringList RegExpFilter::HotSpot::capturedTexts() const +{ + return _capturedTexts; +} + +void RegExpFilter::setRegExp(const QRegExp& regExp) +{ + _searchText = regExp; +} +QRegExp RegExpFilter::regExp() const +{ + return _searchText; +} +/*void RegExpFilter::reset(int) +{ + _buffer = QString(); +}*/ +void RegExpFilter::process() +{ + int pos = 0; + const QString* text = buffer(); + + Q_ASSERT(text); + + // ignore any regular expressions which match an empty string. + // otherwise the while loop below will run indefinitely + static const QString emptyString(""); + if (_searchText.exactMatch(emptyString)) + return; + + while (pos >= 0) { + pos = _searchText.indexIn(*text, pos); + + if (pos >= 0) { + int startLine = 0; + int endLine = 0; + int startColumn = 0; + int endColumn = 0; + + getLineColumn(pos, startLine, startColumn); + getLineColumn(pos + _searchText.matchedLength(), endLine, endColumn); + + RegExpFilter::HotSpot* spot = newHotSpot(startLine, startColumn, + endLine, endColumn); + spot->setCapturedTexts(_searchText.capturedTexts()); + + addHotSpot(spot); + pos += _searchText.matchedLength(); + + // if matchedLength == 0, the program will get stuck in an infinite loop + if (_searchText.matchedLength() == 0) + pos = -1; + } + } +} + +RegExpFilter::HotSpot* RegExpFilter::newHotSpot(int startLine, int startColumn, + int endLine, int endColumn) +{ + return new RegExpFilter::HotSpot(startLine, startColumn, + endLine, endColumn); +} +RegExpFilter::HotSpot* UrlFilter::newHotSpot(int startLine, int startColumn, int endLine, + int endColumn) +{ + return new UrlFilter::HotSpot(startLine, startColumn, + endLine, endColumn); +} +UrlFilter::HotSpot::HotSpot(int startLine, int startColumn, int endLine, int endColumn) + : RegExpFilter::HotSpot(startLine, startColumn, endLine, endColumn) + , _urlObject(new FilterObject(this)) +{ + setType(Link); +} + +UrlFilter::HotSpot::UrlType UrlFilter::HotSpot::urlType() const +{ + const QString url = capturedTexts().first(); + + if (FullUrlRegExp.exactMatch(url)) + return StandardUrl; + else if (EmailAddressRegExp.exactMatch(url)) + return Email; + else + return Unknown; +} + +void UrlFilter::HotSpot::activate(QObject* object) +{ + QString url = capturedTexts().first(); + + const UrlType kind = urlType(); + + const QString& actionName = object ? object->objectName() : QString(); + + if (actionName == "copy-action") { + QApplication::clipboard()->setText(url); + return; + } + + if (!object || actionName == "open-action") { + if (kind == StandardUrl) { + // if the URL path does not include the protocol ( eg. "www.kde.org" ) then + // prepend http:// ( eg. "www.kde.org" --> "http://www.kde.org" ) + if (!url.contains("://")) { + url.prepend("http://"); + } + } else if (kind == Email) { + url.prepend("mailto:"); + } + + new KRun(url, QApplication::activeWindow()); + } +} + +// Note: Altering these regular expressions can have a major effect on the performance of the filters +// used for finding URLs in the text, especially if they are very general and could match very long +// pieces of text. +// Please be careful when altering them. + +//regexp matches: +// full url: +// protocolname:// or www. followed by anything other than whitespaces, <, >, ' or ", and ends before whitespaces, <, >, ', ", ], !, ), :, comma and dot +const QRegExp UrlFilter::FullUrlRegExp("(www\\.(?!\\.)|[a-z][a-z0-9+.-]*://)[^\\s<>'\"]+[^!,\\.\\s<>'\"\\]\\)\\:]"); +// email address: +// [word chars, dots or dashes]@[word chars, dots or dashes].[word chars] +const QRegExp UrlFilter::EmailAddressRegExp("\\b(\\w|\\.|-)+@(\\w|\\.|-)+\\.\\w+\\b"); + +// matches full url or email address +const QRegExp UrlFilter::CompleteUrlRegExp('(' + FullUrlRegExp.pattern() + '|' + + EmailAddressRegExp.pattern() + ')'); + +UrlFilter::UrlFilter() +{ + setRegExp(CompleteUrlRegExp); +} +UrlFilter::HotSpot::~HotSpot() +{ + delete _urlObject; +} +void FilterObject::activated() +{ + _filter->activate(sender()); +} +QList UrlFilter::HotSpot::actions() +{ + QAction* openAction = new QAction(_urlObject); + QAction* copyAction = new QAction(_urlObject); + + const UrlType kind = urlType(); + Q_ASSERT(kind == StandardUrl || kind == Email); + + if (kind == StandardUrl) { + openAction->setText(i18n("Open Link")); + copyAction->setText(i18n("Copy Link Address")); + } else if (kind == Email) { + openAction->setText(i18n("Send Email To...")); + copyAction->setText(i18n("Copy Email Address")); + } + + // object names are set here so that the hotspot performs the + // correct action when activated() is called with the triggered + // action passed as a parameter. + openAction->setObjectName(QLatin1String("open-action")); + copyAction->setObjectName(QLatin1String("copy-action")); + + QObject::connect(openAction , SIGNAL(triggered()) , _urlObject , SLOT(activated())); + QObject::connect(copyAction , SIGNAL(triggered()) , _urlObject , SLOT(activated())); + + QList actions; + actions << openAction; + actions << copyAction; + + return actions; +} + +#include "moc_Filter.cpp" diff --git a/konsole/src/Filter.h b/konsole/src/Filter.h new file mode 100644 index 00000000..23280186 --- /dev/null +++ b/konsole/src/Filter.h @@ -0,0 +1,365 @@ +/* + Copyright 2007-2008 by Robert Knight + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +#ifndef FILTER_H +#define FILTER_H + +// Qt +#include +#include +#include +#include +#include + +// Konsole +#include "Character.h" + +#include + +namespace Konsole +{ +/** + * A filter processes blocks of text looking for certain patterns (such as URLs or keywords from a list) + * and marks the areas which match the filter's patterns as 'hotspots'. + * + * Each hotspot has a type identifier associated with it ( such as a link or a highlighted section ), + * and an action. When the user performs some activity such as a mouse-click in a hotspot area ( the exact + * action will depend on what is displaying the block of text which the filter is processing ), the hotspot's + * activate() method should be called. Depending on the type of hotspot this will trigger a suitable response. + * + * For example, if a hotspot represents a URL then a suitable action would be opening that URL in a web browser. + * Hotspots may have more than one action, in which case the list of actions can be obtained using the + * actions() method. + * + * Different subclasses of filter will return different types of hotspot. + * Subclasses must reimplement the process() method to examine a block of text and identify sections of interest. + * When processing the text they should create instances of Filter::HotSpot subclasses for sections of interest + * and add them to the filter's list of hotspots using addHotSpot() + */ +class Filter +{ +public: + /** + * Represents an area of text which matched the pattern a particular filter has been looking for. + * + * Each hotspot has a type identifier associated with it ( such as a link or a highlighted section ), + * and an action. When the user performs some activity such as a mouse-click in a hotspot area ( the exact + * action will depend on what is displaying the block of text which the filter is processing ), the hotspot's + * activate() method should be called. Depending on the type of hotspot this will trigger a suitable response. + * + * For example, if a hotspot represents a URL then a suitable action would be opening that URL in a web browser. + * Hotspots may have more than one action, in which case the list of actions can be obtained using the + * actions() method. These actions may then be displayed in a popup menu or toolbar for example. + */ + class HotSpot + { + public: + /** + * Constructs a new hotspot which covers the area from (@p startLine,@p startColumn) to (@p endLine,@p endColumn) + * in a block of text. + */ + HotSpot(int startLine , int startColumn , int endLine , int endColumn); + virtual ~HotSpot(); + + enum Type { + // the type of the hotspot is not specified + NotSpecified, + // this hotspot represents a clickable link + Link, + // this hotspot represents a marker + Marker + }; + + /** Returns the line when the hotspot area starts */ + int startLine() const; + /** Returns the line where the hotspot area ends */ + int endLine() const; + /** Returns the column on startLine() where the hotspot area starts */ + int startColumn() const; + /** Returns the column on endLine() where the hotspot area ends */ + int endColumn() const; + /** + * Returns the type of the hotspot. This is usually used as a hint for views on how to represent + * the hotspot graphically. eg. Link hotspots are typically underlined when the user mouses over them + */ + Type type() const; + /** + * Causes the action associated with a hotspot to be triggered. + * + * @param object The object which caused the hotspot to be triggered. This is + * typically null ( in which case the default action should be performed ) or + * one of the objects from the actions() list. In which case the associated + * action should be performed. + */ + virtual void activate(QObject* object = 0) = 0; + /** + * Returns a list of actions associated with the hotspot which can be used in a + * menu or toolbar + */ + virtual QList actions(); + + protected: + /** Sets the type of a hotspot. This should only be set once */ + void setType(Type type); + + private: + int _startLine; + int _startColumn; + int _endLine; + int _endColumn; + Type _type; + }; + + /** Constructs a new filter. */ + Filter(); + virtual ~Filter(); + + /** Causes the filter to process the block of text currently in its internal buffer */ + virtual void process() = 0; + + /** + * Empties the filters internal buffer and resets the line count back to 0. + * All hotspots are deleted. + */ + void reset(); + + /** Adds a new line of text to the filter and increments the line count */ + //void addLine(const QString& string); + + /** Returns the hotspot which covers the given @p line and @p column, or 0 if no hotspot covers that area */ + HotSpot* hotSpotAt(int line , int column) const; + + /** Returns the list of hotspots identified by the filter */ + QList hotSpots() const; + + /** Returns the list of hotspots identified by the filter which occur on a given line */ + QList hotSpotsAtLine(int line) const; + + /** + * TODO: Document me + */ + void setBuffer(const QString* buffer , const QList* linePositions); + +protected: + /** Adds a new hotspot to the list */ + void addHotSpot(HotSpot*); + /** Returns the internal buffer */ + const QString* buffer(); + /** Converts a character position within buffer() to a line and column */ + void getLineColumn(int position , int& startLine , int& startColumn); + +private: + QMultiHash _hotspots; + QList _hotspotList; + + const QList* _linePositions; + const QString* _buffer; +}; + +/** + * A filter which searches for sections of text matching a regular expression and creates a new RegExpFilter::HotSpot + * instance for them. + * + * Subclasses can reimplement newHotSpot() to return custom hotspot types when matches for the regular expression + * are found. + */ +class RegExpFilter : public Filter +{ +public: + /** + * Type of hotspot created by RegExpFilter. The capturedTexts() method can be used to find the text + * matched by the filter's regular expression. + */ + class HotSpot : public Filter::HotSpot + { + public: + HotSpot(int startLine, int startColumn, int endLine , int endColumn); + virtual void activate(QObject* object = 0); + + /** Sets the captured texts associated with this hotspot */ + void setCapturedTexts(const QStringList& texts); + /** Returns the texts found by the filter when matching the filter's regular expression */ + QStringList capturedTexts() const; + private: + QStringList _capturedTexts; + }; + + /** Constructs a new regular expression filter */ + RegExpFilter(); + + /** + * Sets the regular expression which the filter searches for in blocks of text. + * + * Regular expressions which match the empty string are treated as not matching + * anything. + */ + void setRegExp(const QRegExp& text); + /** Returns the regular expression which the filter searches for in blocks of text */ + QRegExp regExp() const; + + /** + * Reimplemented to search the filter's text buffer for text matching regExp() + * + * If regexp matches the empty string, then process() will return immediately + * without finding results. + */ + virtual void process(); + +protected: + /** + * Called when a match for the regular expression is encountered. Subclasses should reimplement this + * to return custom hotspot types + */ + virtual RegExpFilter::HotSpot* newHotSpot(int startLine, int startColumn, + int endLine, int endColumn); + +private: + QRegExp _searchText; +}; + +class FilterObject; + +/** A filter which matches URLs in blocks of text */ +class UrlFilter : public RegExpFilter +{ +public: + /** + * Hotspot type created by UrlFilter instances. The activate() method opens a web browser + * at the given URL when called. + */ + class HotSpot : public RegExpFilter::HotSpot + { + public: + HotSpot(int startLine, int startColumn, int endLine, int endColumn); + virtual ~HotSpot(); + + virtual QList actions(); + + /** + * Open a web browser at the current URL. The url itself can be determined using + * the capturedTexts() method. + */ + virtual void activate(QObject* object = 0); + + private: + enum UrlType { + StandardUrl, + Email, + Unknown + }; + UrlType urlType() const; + + FilterObject* _urlObject; + }; + + UrlFilter(); + +protected: + virtual RegExpFilter::HotSpot* newHotSpot(int, int, int, int); + +private: + static const QRegExp FullUrlRegExp; + static const QRegExp EmailAddressRegExp; + + // combined OR of FullUrlRegExp and EmailAddressRegExp + static const QRegExp CompleteUrlRegExp; +}; + +class FilterObject : public QObject +{ + Q_OBJECT +public: + explicit FilterObject(Filter::HotSpot* filter) : _filter(filter) {} +private slots: + void activated(); +private: + Filter::HotSpot* _filter; +}; + +/** + * A chain which allows a group of filters to be processed as one. + * The chain owns the filters added to it and deletes them when the chain itself is destroyed. + * + * Use addFilter() to add a new filter to the chain. + * When new text to be filtered arrives, use addLine() to add each additional + * line of text which needs to be processed and then after adding the last line, use + * process() to cause each filter in the chain to process the text. + * + * After processing a block of text, the reset() method can be used to set the filter chain's + * internal cursor back to the first line. + * + * The hotSpotAt() method will return the first hotspot which covers a given position. + * + * The hotSpots() and hotSpotsAtLine() method return all of the hotspots in the text and on + * a given line respectively. + */ +class FilterChain : protected QList +{ +public: + virtual ~FilterChain(); + + /** Adds a new filter to the chain. The chain will delete this filter when it is destroyed */ + void addFilter(Filter* filter); + /** Removes a filter from the chain. The chain will no longer delete the filter when destroyed */ + void removeFilter(Filter* filter); + /** Removes all filters from the chain */ + void clear(); + + /** Resets each filter in the chain */ + void reset(); + /** + * Processes each filter in the chain + */ + void process(); + + /** Sets the buffer for each filter in the chain to process. */ + void setBuffer(const QString* buffer , const QList* linePositions); + + /** Returns the first hotspot which occurs at @p line, @p column or 0 if no hotspot was found */ + Filter::HotSpot* hotSpotAt(int line , int column) const; + /** Returns a list of all the hotspots in all the chain's filters */ + QList hotSpots() const; + /** Returns a list of all hotspots at the given line in all the chain's filters */ + QList hotSpotsAtLine(int line) const; +}; + +/** A filter chain which processes character images from terminal displays */ +class TerminalImageFilterChain : public FilterChain +{ +public: + TerminalImageFilterChain(); + virtual ~TerminalImageFilterChain(); + + /** + * Set the current terminal image to @p image. + * + * @param image The terminal image + * @param lines The number of lines in the terminal image + * @param columns The number of columns in the terminal image + * @param lineProperties The line properties to set for image + */ + void setImage(const Character* const image , int lines , int columns, + const QVector& lineProperties); + +private: + QString* _buffer; + QList* _linePositions; +}; +} +#endif //FILTER_H diff --git a/konsole/src/History.cpp b/konsole/src/History.cpp new file mode 100644 index 00000000..9aa4d0af --- /dev/null +++ b/konsole/src/History.cpp @@ -0,0 +1,656 @@ +/* + This file is part of Konsole, an X terminal. + Copyright 1997,1998 by Lars Doelle + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +// Own +#include "History.h" + +// System +#include +#include +#include +#include +#include +#include + +// KDE +#include +#include +#include + +// Reasonable line size +static const int LINE_SIZE = 1024; + +using namespace Konsole; + +/* + An arbitrary long scroll. + + One can modify the scroll only by adding either cells + or newlines, but access it randomly. + + The model is that of an arbitrary wide typewriter scroll + in that the scroll is a series of lines and each line is + a series of cells with no overwriting permitted. + + The implementation provides arbitrary length and numbers + of cells and line/column indexed read access to the scroll + at constant costs. +*/ + +// History File /////////////////////////////////////////// +HistoryFile::HistoryFile() + : _fd(-1), + _length(0), + _fileMap(0), + _readWriteBalance(0) +{ + const QString tmpFormat = KStandardDirs::locateLocal("tmp", QString()) + + "konsole-XXXXXX.history"; + _tmpFile.setFileTemplate(tmpFormat); + if (_tmpFile.open()) { + _tmpFile.setAutoRemove(true); + _fd = _tmpFile.handle(); + } +} + +HistoryFile::~HistoryFile() +{ + if (_fileMap) + unmap(); +} + +//TODO: Mapping the entire file in will cause problems if the history file becomes exceedingly large, +//(ie. larger than available memory). HistoryFile::map() should only map in sections of the file at a time, +//to avoid this. +void HistoryFile::map() +{ + Q_ASSERT(_fileMap == 0); + + _fileMap = (char*)mmap(0 , _length , PROT_READ , MAP_PRIVATE , _fd , 0); + + //if mmap'ing fails, fall back to the read-lseek combination + if (_fileMap == MAP_FAILED) { + _readWriteBalance = 0; + _fileMap = 0; + kWarning() << "mmap'ing history failed. errno = " << errno; + } +} + +void HistoryFile::unmap() +{ + int result = munmap(_fileMap , _length); + Q_ASSERT(result == 0); + Q_UNUSED(result); + + _fileMap = 0; +} + +bool HistoryFile::isMapped() const +{ + return (_fileMap != 0); +} + +void HistoryFile::add(const unsigned char* buffer, int count) +{ + if (_fileMap) + unmap(); + + _readWriteBalance++; + + int rc = 0; + + rc = KDE_lseek(_fd, _length, SEEK_SET); + if (rc < 0) { + perror("HistoryFile::add.seek"); + return; + } + rc = write(_fd, buffer, count); + if (rc < 0) { + perror("HistoryFile::add.write"); + return; + } + _length += rc; +} + +void HistoryFile::get(unsigned char* buffer, int size, int loc) +{ + //count number of get() calls vs. number of add() calls. + //If there are many more get() calls compared with add() + //calls (decided by using MAP_THRESHOLD) then mmap the log + //file to improve performance. + _readWriteBalance--; + if (!_fileMap && _readWriteBalance < MAP_THRESHOLD) + map(); + + if (_fileMap) { + for (int i = 0; i < size; i++) + buffer[i] = _fileMap[loc + i]; + } else { + int rc = 0; + + if (loc < 0 || size < 0 || loc + size > _length) + fprintf(stderr, "getHist(...,%d,%d): invalid args.\n", size, loc); + rc = KDE_lseek(_fd, loc, SEEK_SET); + if (rc < 0) { + perror("HistoryFile::get.seek"); + return; + } + rc = read(_fd, buffer, size); + if (rc < 0) { + perror("HistoryFile::get.read"); + return; + } + } +} + +int HistoryFile::len() const +{ + return _length; +} + +// History Scroll abstract base class ////////////////////////////////////// + +HistoryScroll::HistoryScroll(HistoryType* t) + : _historyType(t) +{ +} + +HistoryScroll::~HistoryScroll() +{ + delete _historyType; +} + +bool HistoryScroll::hasScroll() +{ + return true; +} + +// History Scroll File ////////////////////////////////////// + +/* + The history scroll makes a Row(Row(Cell)) from + two history buffers. The index buffer contains + start of line positions which refer to the cells + buffer. + + Note that index[0] addresses the second line + (line #1), while the first line (line #0) starts + at 0 in cells. +*/ + +HistoryScrollFile::HistoryScrollFile(const QString& logFileName) + : HistoryScroll(new HistoryTypeFile(logFileName)) +{ +} + +HistoryScrollFile::~HistoryScrollFile() +{ +} + +int HistoryScrollFile::getLines() +{ + return _index.len() / sizeof(int); +} + +int HistoryScrollFile::getLineLen(int lineno) +{ + return (startOfLine(lineno + 1) - startOfLine(lineno)) / sizeof(Character); +} + +bool HistoryScrollFile::isWrappedLine(int lineno) +{ + if (lineno >= 0 && lineno <= getLines()) { + unsigned char flag; + _lineflags.get((unsigned char*)&flag, sizeof(unsigned char), (lineno)*sizeof(unsigned char)); + return flag; + } + return false; +} + +int HistoryScrollFile::startOfLine(int lineno) +{ + if (lineno <= 0) return 0; + if (lineno <= getLines()) { + if (!_index.isMapped()) + _index.map(); + + int res; + _index.get((unsigned char*)&res, sizeof(int), (lineno - 1)*sizeof(int)); + return res; + } + return _cells.len(); +} + +void HistoryScrollFile::getCells(int lineno, int colno, int count, Character res[]) +{ + _cells.get((unsigned char*)res, count * sizeof(Character), startOfLine(lineno) + colno * sizeof(Character)); +} + +void HistoryScrollFile::addCells(const Character text[], int count) +{ + _cells.add((unsigned char*)text, count * sizeof(Character)); +} + +void HistoryScrollFile::addLine(bool previousWrapped) +{ + if (_index.isMapped()) + _index.unmap(); + + int locn = _cells.len(); + _index.add((unsigned char*)&locn, sizeof(int)); + unsigned char flags = previousWrapped ? 0x01 : 0x00; + _lineflags.add((unsigned char*)&flags, sizeof(unsigned char)); +} + +// History Scroll None ////////////////////////////////////// + +HistoryScrollNone::HistoryScrollNone() + : HistoryScroll(new HistoryTypeNone()) +{ +} + +HistoryScrollNone::~HistoryScrollNone() +{ +} + +bool HistoryScrollNone::hasScroll() +{ + return false; +} + +int HistoryScrollNone::getLines() +{ + return 0; +} + +int HistoryScrollNone::getLineLen(int) +{ + return 0; +} + +bool HistoryScrollNone::isWrappedLine(int /*lineno*/) +{ + return false; +} + +void HistoryScrollNone::getCells(int, int, int, Character []) +{ +} + +void HistoryScrollNone::addCells(const Character [], int) +{ +} + +void HistoryScrollNone::addLine(bool) +{ +} + +//////////////////////////////////////////////////////////////// +// Compact History Scroll ////////////////////////////////////// +//////////////////////////////////////////////////////////////// +void* CompactHistoryBlock::allocate(size_t size) +{ + Q_ASSERT(size > 0); + if (_tail - _blockStart + size > _blockLength) + return 0; + + void* block = _tail; + _tail += size; + //kDebug() << "allocated " << length << " bytes at address " << block; + _allocCount++; + return block; +} + +void CompactHistoryBlock::deallocate() +{ + _allocCount--; + Q_ASSERT(_allocCount >= 0); +} + +void* CompactHistoryBlockList::allocate(size_t size) +{ + CompactHistoryBlock* block; + if (list.isEmpty() || list.last()->remaining() < size) { + block = new CompactHistoryBlock(); + list.append(block); + //kDebug() << "new block created, remaining " << block->remaining() << "number of blocks=" << list.size(); + } else { + block = list.last(); + //kDebug() << "old block used, remaining " << block->remaining(); + } + return block->allocate(size); +} + +void CompactHistoryBlockList::deallocate(void* ptr) +{ + Q_ASSERT(!list.isEmpty()); + + int i = 0; + CompactHistoryBlock* block = list.at(i); + while (i < list.size() && !block->contains(ptr)) { + i++; + block = list.at(i); + } + + Q_ASSERT(i < list.size()); + + block->deallocate(); + + if (!block->isInUse()) { + list.removeAt(i); + delete block; + //kDebug() << "block deleted, new size = " << list.size(); + } +} + +CompactHistoryBlockList::~CompactHistoryBlockList() +{ + qDeleteAll(list.begin(), list.end()); + list.clear(); +} + +void* CompactHistoryLine::operator new(size_t size, CompactHistoryBlockList& blockList) +{ + return blockList.allocate(size); +} + +CompactHistoryLine::CompactHistoryLine(const TextLine& line, CompactHistoryBlockList& bList) + : _blockListRef(bList), + _formatArray(0), + _text(0), + _formatLength(0), + _wrapped(false) +{ + _length = line.size(); + + if (line.size() > 0) { + _formatLength = 1; + int k = 1; + + // count number of different formats in this text line + Character c = line[0]; + while (k < _length) { + if (!(line[k].equalsFormat(c))) { + _formatLength++; // format change detected + c = line[k]; + } + k++; + } + + //kDebug() << "number of different formats in string: " << _formatLength; + _formatArray = (CharacterFormat*) _blockListRef.allocate(sizeof(CharacterFormat) * _formatLength); + Q_ASSERT(_formatArray != 0); + _text = (quint16*) _blockListRef.allocate(sizeof(quint16) * line.size()); + Q_ASSERT(_text != 0); + + _length = line.size(); + _wrapped = false; + + // record formats and their positions in the format array + c = line[0]; + _formatArray[0].setFormat(c); + _formatArray[0].startPos = 0; // there's always at least 1 format (for the entire line, unless a change happens) + + k = 1; // look for possible format changes + int j = 1; + while (k < _length && j < _formatLength) { + if (!(line[k].equalsFormat(c))) { + c = line[k]; + _formatArray[j].setFormat(c); + _formatArray[j].startPos = k; + //kDebug() << "format entry " << j << " at pos " << _formatArray[j].startPos << " " << &(_formatArray[j].startPos) ; + j++; + } + k++; + } + + // copy character values + for (int i = 0; i < line.size(); i++) { + _text[i] = line[i].character; + //kDebug() << "char " << i << " at mem " << &(text[i]); + } + } + //kDebug() << "line created, length " << length << " at " << &(length); +} + +CompactHistoryLine::~CompactHistoryLine() +{ + if (_length > 0) { + _blockListRef.deallocate(_text); + _blockListRef.deallocate(_formatArray); + } + _blockListRef.deallocate(this); +} + +void CompactHistoryLine::getCharacter(int index, Character& r) +{ + Q_ASSERT(index < _length); + int formatPos = 0; + while ((formatPos + 1) < _formatLength && index >= _formatArray[formatPos + 1].startPos) + formatPos++; + + r.character = _text[index]; + r.rendition = _formatArray[formatPos].rendition; + r.foregroundColor = _formatArray[formatPos].fgColor; + r.backgroundColor = _formatArray[formatPos].bgColor; + r.isRealCharacter = _formatArray[formatPos].isRealCharacter; +} + +void CompactHistoryLine::getCharacters(Character* array, int size, int startColumn) +{ + Q_ASSERT(startColumn >= 0 && size >= 0); + Q_ASSERT(startColumn + size <= static_cast(getLength())); + + for (int i = startColumn; i < size + startColumn; i++) { + getCharacter(i, array[i - startColumn]); + } +} + +CompactHistoryScroll::CompactHistoryScroll(unsigned int maxLineCount) + : HistoryScroll(new CompactHistoryType(maxLineCount)) + , _lines() + , _blockList() +{ + //kDebug() << "scroll of length " << maxLineCount << " created"; + setMaxNbLines(maxLineCount); +} + +CompactHistoryScroll::~CompactHistoryScroll() +{ + qDeleteAll(_lines.begin(), _lines.end()); + _lines.clear(); +} + +void CompactHistoryScroll::addCellsVector(const TextLine& cells) +{ + CompactHistoryLine* line; + line = new(_blockList) CompactHistoryLine(cells, _blockList); + + if (_lines.size() > static_cast(_maxLineCount)) { + delete _lines.takeAt(0); + } + _lines.append(line); +} + +void CompactHistoryScroll::addCells(const Character a[], int count) +{ + TextLine newLine(count); + qCopy(a, a + count, newLine.begin()); + addCellsVector(newLine); +} + +void CompactHistoryScroll::addLine(bool previousWrapped) +{ + CompactHistoryLine* line = _lines.last(); + //kDebug() << "last line at address " << line; + line->setWrapped(previousWrapped); +} + +int CompactHistoryScroll::getLines() +{ + return _lines.size(); +} + +int CompactHistoryScroll::getLineLen(int lineNumber) +{ + if ((lineNumber < 0) || (lineNumber >= _lines.size())) { + kDebug() << "requested line invalid: 0 < " << lineNumber << " < " <<_lines.size(); + //Q_ASSERT(lineNumber >= 0 && lineNumber < _lines.size()); + return 0; + } + CompactHistoryLine* line = _lines[lineNumber]; + //kDebug() << "request for line at address " << line; + return line->getLength(); +} + +void CompactHistoryScroll::getCells(int lineNumber, int startColumn, int count, Character buffer[]) +{ + if (count == 0) return; + Q_ASSERT(lineNumber < _lines.size()); + CompactHistoryLine* line = _lines[lineNumber]; + Q_ASSERT(startColumn >= 0); + Q_ASSERT((unsigned int)startColumn <= line->getLength() - count); + line->getCharacters(buffer, count, startColumn); +} + +void CompactHistoryScroll::setMaxNbLines(unsigned int lineCount) +{ + _maxLineCount = lineCount; + + while (_lines.size() > static_cast(lineCount)) { + delete _lines.takeAt(0); + } + //kDebug() << "set max lines to: " << _maxLineCount; +} + +bool CompactHistoryScroll::isWrappedLine(int lineNumber) +{ + Q_ASSERT(lineNumber < _lines.size()); + return _lines[lineNumber]->isWrapped(); +} + +////////////////////////////////////////////////////////////////////// +// History Types +////////////////////////////////////////////////////////////////////// + +HistoryType::HistoryType() +{ +} + +HistoryType::~HistoryType() +{ +} + +////////////////////////////// + +HistoryTypeNone::HistoryTypeNone() +{ +} + +bool HistoryTypeNone::isEnabled() const +{ + return false; +} + +HistoryScroll* HistoryTypeNone::scroll(HistoryScroll* old) const +{ + delete old; + return new HistoryScrollNone(); +} + +int HistoryTypeNone::maximumLineCount() const +{ + return 0; +} + +////////////////////////////// + +HistoryTypeFile::HistoryTypeFile(const QString& fileName) + : _fileName(fileName) +{ +} + +bool HistoryTypeFile::isEnabled() const +{ + return true; +} + +HistoryScroll* HistoryTypeFile::scroll(HistoryScroll* old) const +{ + if (dynamic_cast(old)) + return old; // Unchanged. + + HistoryScroll* newScroll = new HistoryScrollFile(_fileName); + + Character line[LINE_SIZE]; + int lines = (old != 0) ? old->getLines() : 0; + for (int i = 0; i < lines; i++) { + int size = old->getLineLen(i); + if (size > LINE_SIZE) { + Character* tmp_line = new Character[size]; + old->getCells(i, 0, size, tmp_line); + newScroll->addCells(tmp_line, size); + newScroll->addLine(old->isWrappedLine(i)); + delete [] tmp_line; + } else { + old->getCells(i, 0, size, line); + newScroll->addCells(line, size); + newScroll->addLine(old->isWrappedLine(i)); + } + } + + delete old; + return newScroll; +} + +int HistoryTypeFile::maximumLineCount() const +{ + return -1; +} + +////////////////////////////// + +CompactHistoryType::CompactHistoryType(unsigned int nbLines) + : _maxLines(nbLines) +{ +} + +bool CompactHistoryType::isEnabled() const +{ + return true; +} + +int CompactHistoryType::maximumLineCount() const +{ + return _maxLines; +} + +HistoryScroll* CompactHistoryType::scroll(HistoryScroll* old) const +{ + if (old) { + CompactHistoryScroll* oldBuffer = dynamic_cast(old); + if (oldBuffer) { + oldBuffer->setMaxNbLines(_maxLines); + return oldBuffer; + } + delete old; + } + return new CompactHistoryScroll(_maxLines); +} diff --git a/konsole/src/History.h b/konsole/src/History.h new file mode 100644 index 00000000..6314ef99 --- /dev/null +++ b/konsole/src/History.h @@ -0,0 +1,388 @@ +/* + This file is part of Konsole, an X terminal. + Copyright 1997,1998 by Lars Doelle + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +#ifndef HISTORY_H +#define HISTORY_H + +// System +#include + +// Qt +#include +#include +#include + +#include "konsoleprivate_export.h" + +// Konsole +#include "Character.h" + +namespace Konsole +{ +/* + An extendable tmpfile(1) based buffer. +*/ + +class HistoryFile +{ +public: + HistoryFile(); + virtual ~HistoryFile(); + + virtual void add(const unsigned char* bytes, int len); + virtual void get(unsigned char* bytes, int len, int loc); + virtual int len() const; + + //mmaps the file in read-only mode + void map(); + //un-mmaps the file + void unmap(); + //returns true if the file is mmap'ed + bool isMapped() const; + + +private: + int _fd; + int _length; + QTemporaryFile _tmpFile; + + //pointer to start of mmap'ed file data, or 0 if the file is not mmap'ed + char* _fileMap; + + //incremented whenever 'add' is called and decremented whenever + //'get' is called. + //this is used to detect when a large number of lines are being read and processed from the history + //and automatically mmap the file for better performance (saves the overhead of many lseek-read calls). + int _readWriteBalance; + + //when _readWriteBalance goes below this threshold, the file will be mmap'ed automatically + static const int MAP_THRESHOLD = -1000; +}; + +////////////////////////////////////////////////////////////////////// + +////////////////////////////////////////////////////////////////////// +// Abstract base class for file and buffer versions +////////////////////////////////////////////////////////////////////// +class HistoryType; + +class HistoryScroll +{ +public: + explicit HistoryScroll(HistoryType*); + virtual ~HistoryScroll(); + + virtual bool hasScroll(); + + // access to history + virtual int getLines() = 0; + virtual int getLineLen(int lineno) = 0; + virtual void getCells(int lineno, int colno, int count, Character res[]) = 0; + virtual bool isWrappedLine(int lineno) = 0; + + // adding lines. + virtual void addCells(const Character a[], int count) = 0; + // convenience method - this is virtual so that subclasses can take advantage + // of QVector's implicit copying + virtual void addCellsVector(const QVector& cells) { + addCells(cells.data(), cells.size()); + } + + virtual void addLine(bool previousWrapped = false) = 0; + + // + // FIXME: Passing around constant references to HistoryType instances + // is very unsafe, because those references will no longer + // be valid if the history scroll is deleted. + // + const HistoryType& getType() const { + return *_historyType; + } + +protected: + HistoryType* _historyType; +}; + +////////////////////////////////////////////////////////////////////// +// File-based history (e.g. file log, no limitation in length) +////////////////////////////////////////////////////////////////////// + +class KONSOLEPRIVATE_EXPORT HistoryScrollFile : public HistoryScroll +{ +public: + explicit HistoryScrollFile(const QString& logFileName); + virtual ~HistoryScrollFile(); + + virtual int getLines(); + virtual int getLineLen(int lineno); + virtual void getCells(int lineno, int colno, int count, Character res[]); + virtual bool isWrappedLine(int lineno); + + virtual void addCells(const Character a[], int count); + virtual void addLine(bool previousWrapped = false); + +private: + int startOfLine(int lineno); + + HistoryFile _index; // lines Row(int) + HistoryFile _cells; // text Row(Character) + HistoryFile _lineflags; // flags Row(unsigned char) +}; + +////////////////////////////////////////////////////////////////////// +// Nothing-based history (no history :-) +////////////////////////////////////////////////////////////////////// +class KONSOLEPRIVATE_EXPORT HistoryScrollNone : public HistoryScroll +{ +public: + HistoryScrollNone(); + virtual ~HistoryScrollNone(); + + virtual bool hasScroll(); + + virtual int getLines(); + virtual int getLineLen(int lineno); + virtual void getCells(int lineno, int colno, int count, Character res[]); + virtual bool isWrappedLine(int lineno); + + virtual void addCells(const Character a[], int count); + virtual void addLine(bool previousWrapped = false); +}; + +////////////////////////////////////////////////////////////////////// +// History using compact storage +// This implementation uses a list of fixed-sized blocks +// where history lines are allocated in (avoids heap fragmentation) +////////////////////////////////////////////////////////////////////// +typedef QVector TextLine; + +class CharacterFormat +{ +public: + bool equalsFormat(const CharacterFormat& other) const { + return (other.rendition & ~RE_EXTENDED_CHAR) == (rendition & ~RE_EXTENDED_CHAR) && other.fgColor == fgColor && other.bgColor == bgColor; + } + + bool equalsFormat(const Character& c) const { + return (c.rendition & ~RE_EXTENDED_CHAR) == (rendition & ~RE_EXTENDED_CHAR) && c.foregroundColor == fgColor && c.backgroundColor == bgColor; + } + + void setFormat(const Character& c) { + rendition = c.rendition; + fgColor = c.foregroundColor; + bgColor = c.backgroundColor; + isRealCharacter = c.isRealCharacter; + } + + CharacterColor fgColor, bgColor; + quint16 startPos; + quint8 rendition; + bool isRealCharacter; +}; + +class CompactHistoryBlock +{ +public: + CompactHistoryBlock() { + _blockLength = 4096 * 64; // 256kb + _head = (quint8*) mmap(0, _blockLength, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); + //_head = (quint8*) malloc(_blockLength); + Q_ASSERT(_head != MAP_FAILED); + _tail = _blockStart = _head; + _allocCount = 0; + } + + virtual ~CompactHistoryBlock() { + //free(_blockStart); + munmap(_blockStart, _blockLength); + } + + virtual unsigned int remaining() { + return _blockStart + _blockLength - _tail; + } + virtual unsigned length() { + return _blockLength; + } + virtual void* allocate(size_t length); + virtual bool contains(void* addr) { + return addr >= _blockStart && addr < (_blockStart + _blockLength); + } + virtual void deallocate(); + virtual bool isInUse() { + return _allocCount != 0; + }; + +private: + size_t _blockLength; + quint8* _head; + quint8* _tail; + quint8* _blockStart; + int _allocCount; +}; + +class CompactHistoryBlockList +{ +public: + CompactHistoryBlockList() {} + ~CompactHistoryBlockList(); + + void* allocate(size_t size); + void deallocate(void *); + int length() { + return list.size(); + } +private: + QList list; +}; + +class CompactHistoryLine +{ +public: + CompactHistoryLine(const TextLine&, CompactHistoryBlockList& blockList); + virtual ~CompactHistoryLine(); + + // custom new operator to allocate memory from custom pool instead of heap + static void* operator new(size_t size, CompactHistoryBlockList& blockList); + static void operator delete(void *) { + /* do nothing, deallocation from pool is done in destructor*/ + }; + + virtual void getCharacters(Character* array, int length, int startColumn); + virtual void getCharacter(int index, Character& r); + virtual bool isWrapped() const { + return _wrapped; + }; + virtual void setWrapped(bool value) { + _wrapped = value; + }; + virtual unsigned int getLength() const { + return _length; + }; + +protected: + CompactHistoryBlockList& _blockListRef; + CharacterFormat* _formatArray; + quint16 _length; + quint16* _text; + quint16 _formatLength; + bool _wrapped; +}; + +class KONSOLEPRIVATE_EXPORT CompactHistoryScroll : public HistoryScroll +{ + typedef QList HistoryArray; + +public: + explicit CompactHistoryScroll(unsigned int maxNbLines = 1000); + virtual ~CompactHistoryScroll(); + + virtual int getLines(); + virtual int getLineLen(int lineno); + virtual void getCells(int lineno, int colno, int count, Character res[]); + virtual bool isWrappedLine(int lineno); + + virtual void addCells(const Character a[], int count); + virtual void addCellsVector(const TextLine& cells); + virtual void addLine(bool previousWrapped = false); + + void setMaxNbLines(unsigned int nbLines); + +private: + bool hasDifferentColors(const TextLine& line) const; + HistoryArray _lines; + CompactHistoryBlockList _blockList; + + unsigned int _maxLineCount; +}; + +////////////////////////////////////////////////////////////////////// +// History type +////////////////////////////////////////////////////////////////////// + +class KONSOLEPRIVATE_EXPORT HistoryType +{ +public: + HistoryType(); + virtual ~HistoryType(); + + /** + * Returns true if the history is enabled ( can store lines of output ) + * or false otherwise. + */ + virtual bool isEnabled() const = 0; + /** + * Returns the maximum number of lines which this history type + * can store or -1 if the history can store an unlimited number of lines. + */ + virtual int maximumLineCount() const = 0; + /** + * Converts from one type of HistoryScroll to another or if given the + * same type, returns it. + */ + virtual HistoryScroll* scroll(HistoryScroll *) const = 0; + /** + * Returns true if the history size is unlimited. + */ + bool isUnlimited() const { + return maximumLineCount() == -1; + } +}; + +class KONSOLEPRIVATE_EXPORT HistoryTypeNone : public HistoryType +{ +public: + HistoryTypeNone(); + + virtual bool isEnabled() const; + virtual int maximumLineCount() const; + + virtual HistoryScroll* scroll(HistoryScroll *) const; +}; + +class KONSOLEPRIVATE_EXPORT HistoryTypeFile : public HistoryType +{ +public: + explicit HistoryTypeFile(const QString& fileName = QString()); + + virtual bool isEnabled() const; + virtual int maximumLineCount() const; + + virtual HistoryScroll* scroll(HistoryScroll *) const; + +protected: + QString _fileName; +}; + +class KONSOLEPRIVATE_EXPORT CompactHistoryType : public HistoryType +{ +public: + explicit CompactHistoryType(unsigned int size); + + virtual bool isEnabled() const; + virtual int maximumLineCount() const; + + virtual HistoryScroll* scroll(HistoryScroll *) const; + +protected: + unsigned int _maxLines; +}; +} + +#endif // HISTORY_H diff --git a/konsole/src/HistorySizeDialog.cpp b/konsole/src/HistorySizeDialog.cpp new file mode 100644 index 00000000..b91a00ed --- /dev/null +++ b/konsole/src/HistorySizeDialog.cpp @@ -0,0 +1,76 @@ +/* + Copyright 2007-2008 by Robert Knight + Copyright 2012 by Kurt Hindenburg + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +// Own +#include "HistorySizeDialog.h" + +// KDE +#include + +// Konsole +#include "ui_HistorySizeDialog.h" + +using namespace Konsole; + +HistorySizeDialog::HistorySizeDialog(QWidget* parent) + : KDialog(parent) +{ + setCaption(i18nc("@title:window", "Adjust Scrollback")); + setButtons(KDialog::Ok | KDialog::Cancel); + + setWindowModality(Qt::WindowModal); + + _ui = new Ui::HistorySizeDialog(); + _ui->setupUi(mainWidget()); + + _ui->tempWarningWidget->setVisible(true); + _ui->tempWarningWidget->setWordWrap(true); + _ui->tempWarningWidget->setCloseButtonVisible(false); + _ui->tempWarningWidget->setMessageType(KMessageWidget::Information); + _ui->tempWarningWidget->setText(i18nc("@info:status", + "Any adjustments are only temporary to this session.")); +} + +HistorySizeDialog::~HistorySizeDialog() +{ + delete _ui; +} + +void HistorySizeDialog::setMode(Enum::HistoryModeEnum aMode) +{ + _ui->historySizeWidget->setMode(aMode); +} + +Enum::HistoryModeEnum HistorySizeDialog::mode() const +{ + return _ui->historySizeWidget->mode(); +} + +int HistorySizeDialog::lineCount() const +{ + return _ui->historySizeWidget->lineCount(); +} + +void HistorySizeDialog::setLineCount(int lines) +{ + _ui->historySizeWidget->setLineCount(lines); +} + +#include "moc_HistorySizeDialog.cpp" diff --git a/konsole/src/HistorySizeDialog.h b/konsole/src/HistorySizeDialog.h new file mode 100644 index 00000000..1e3d304f --- /dev/null +++ b/konsole/src/HistorySizeDialog.h @@ -0,0 +1,61 @@ +/* + Copyright 2007-2008 by Robert Knight + Copyright 2012 by Kurt Hindenburg + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +#ifndef HISTORYSIZEDIALOG_H +#define HISTORYSIZEDIALOG_H + +// KDE +#include + +// Konsole +#include "Enumeration.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class HistorySizeDialog; } +QT_END_NAMESPACE + +namespace Konsole +{ +class HistorySizeDialog : public KDialog +{ + Q_OBJECT + +public: + explicit HistorySizeDialog(QWidget* parent = 0); + ~HistorySizeDialog(); + + /** See HistorySizeWidget::setMode. */ + void setMode(Enum::HistoryModeEnum aMode); + + /** See HistorySizeWidget::mode. */ + Enum::HistoryModeEnum mode() const; + + /** See HistorySizeWidget::setLineCount. */ + void setLineCount(int lines); + + /** See HistorySizeWidget::lineCount. */ + int lineCount() const; + +private: + Ui::HistorySizeDialog* _ui; +}; +} + +#endif // HISTORYSIZEDIALOG_H diff --git a/konsole/src/HistorySizeDialog.ui b/konsole/src/HistorySizeDialog.ui new file mode 100644 index 00000000..cd10286a --- /dev/null +++ b/konsole/src/HistorySizeDialog.ui @@ -0,0 +1,57 @@ + + + HistorySizeDialog + + + + 0 + 0 + 325 + 47 + + + + + + + + 0 + 0 + + + + + + + + + + + Qt::Vertical + + + + 20 + 18 + + + + + + + + + KMessageWidget + QFrame +
    kmessagewidget.h
    + 1 +
    + + Konsole::HistorySizeWidget + QWidget +
    HistorySizeWidget.h
    +
    +
    + + +
    diff --git a/konsole/src/HistorySizeWidget.cpp b/konsole/src/HistorySizeWidget.cpp new file mode 100644 index 00000000..428d4ca9 --- /dev/null +++ b/konsole/src/HistorySizeWidget.cpp @@ -0,0 +1,118 @@ +/* + Copyright 2007-2008 by Robert Knight + Copyright 2012 by Jekyll Wu + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +// Own +#include "HistorySizeWidget.h" + +// Qt +#include +#include + +// KDE +#include + +// Konsole +#include "ui_HistorySizeWidget.h" + +using namespace Konsole; + +HistorySizeWidget::HistorySizeWidget(QWidget* parent) + : QWidget(parent) +{ + _ui = new Ui::HistorySizeWidget(); + _ui->setupUi(this); + + _ui->unlimitedWarningWidget->setVisible(false); + _ui->unlimitedWarningWidget->setWordWrap(true); + _ui->unlimitedWarningWidget->setCloseButtonVisible(false); + _ui->unlimitedWarningWidget->setMessageType(KMessageWidget::Information); + _ui->unlimitedWarningWidget->setText(i18nc("@info:status", + "When using this option, the scrollback data will be written " + "unencrypted to temporary files. Those temporary files will be " + "deleted automatically when Konsole is closed in a normal manner.")); + + // focus and select the spinner automatically when appropriate + _ui->fixedSizeHistoryButton->setFocusProxy(_ui->historyLineSpinner); + connect(_ui->fixedSizeHistoryButton , SIGNAL(clicked()) , + _ui->historyLineSpinner , SLOT(selectAll())); + + QButtonGroup* modeGroup = new QButtonGroup(this); + modeGroup->addButton(_ui->noHistoryButton); + modeGroup->addButton(_ui->fixedSizeHistoryButton); + modeGroup->addButton(_ui->unlimitedHistoryButton); + connect(modeGroup, SIGNAL(buttonClicked(QAbstractButton*)), + this, SLOT(buttonClicked(QAbstractButton*))); + + _ui->historyLineSpinner->setSuffix(ki18ncp("Unit of scrollback", " line", " lines")); + this->setLineCount(HistorySizeWidget::DefaultLineCount); + + connect(_ui->historyLineSpinner, SIGNAL(valueChanged(int)), + this, SIGNAL(historySizeChanged(int))); +} + +HistorySizeWidget::~HistorySizeWidget() +{ + delete _ui; +} + +void HistorySizeWidget::buttonClicked(QAbstractButton*) const +{ + Enum::HistoryModeEnum selectedMode = mode(); + _ui->unlimitedWarningWidget->setVisible(Enum::UnlimitedHistory == selectedMode); + emit historyModeChanged(selectedMode); +} + +void HistorySizeWidget::setMode(Enum::HistoryModeEnum aMode) +{ + if (aMode == Enum::NoHistory) { + _ui->noHistoryButton->setChecked(true); + } else if (aMode == Enum::FixedSizeHistory) { + _ui->fixedSizeHistoryButton->setChecked(true); + } else if (aMode == Enum::UnlimitedHistory) { + _ui->unlimitedHistoryButton->setChecked(true); + } + _ui->unlimitedWarningWidget->setVisible(Enum::UnlimitedHistory == aMode); +} + +Enum::HistoryModeEnum HistorySizeWidget::mode() const +{ + if (_ui->noHistoryButton->isChecked()) + return Enum::NoHistory; + else if (_ui->fixedSizeHistoryButton->isChecked()) + return Enum::FixedSizeHistory; + else if (_ui->unlimitedHistoryButton->isChecked()) + return Enum::UnlimitedHistory; + + Q_ASSERT(false); + return Enum::NoHistory; +} + +void HistorySizeWidget::setLineCount(int lines) +{ + _ui->historyLineSpinner->setValue(lines); + _ui->historyLineSpinner->setSingleStep(lines / 10); +} + +int HistorySizeWidget::lineCount() const +{ + return _ui->historyLineSpinner->value(); +} + +#include "moc_HistorySizeWidget.cpp" diff --git a/konsole/src/HistorySizeWidget.h b/konsole/src/HistorySizeWidget.h new file mode 100644 index 00000000..b843b347 --- /dev/null +++ b/konsole/src/HistorySizeWidget.h @@ -0,0 +1,83 @@ +/* + Copyright 2007-2008 by Robert Knight + Copyright 2012 by Jekyll Wu + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +#ifndef HISTORYSIZEWIDGET_H +#define HISTORYSIZEWIDGET_H + +// Qt +#include + +// Konsole +#include "Enumeration.h" + +#include + +QT_BEGIN_NAMESPACE +namespace Ui { class HistorySizeWidget; } +QT_END_NAMESPACE + +namespace Konsole +{ +/** + * A widget for controlling history related options + */ +class HistorySizeWidget : public QWidget +{ + Q_OBJECT + +public: + explicit HistorySizeWidget(QWidget* parent); + virtual ~HistorySizeWidget(); + + /** Specifies the history mode. */ + void setMode(Enum::HistoryModeEnum aMode); + + /** Returns the history mode chosen by the user. */ + Enum::HistoryModeEnum mode() const; + + /** Sets the number of lines for the fixed size history mode. */ + void setLineCount(int lines); + + /** + * Returns the number of lines of history to remember. + * This is only valid when mode() == FixedSizeHistory, + * and returns 0 otherwise. + */ + int lineCount() const; + +signals: + /** Emitted when the history mode is changed. */ + void historyModeChanged(Enum::HistoryModeEnum) const; + + /** Emitted when the history size is changed. */ + void historySizeChanged(int) const; + +private slots: + void buttonClicked(QAbstractButton*) const; + +private: + Ui::HistorySizeWidget* _ui; + + // 1000 lines was the default in the KDE3 series + static const int DefaultLineCount = 1000; +}; +} + +#endif // HISTORYSIZEWIDGET_H diff --git a/konsole/src/HistorySizeWidget.ui b/konsole/src/HistorySizeWidget.ui new file mode 100644 index 00000000..fa4378f0 --- /dev/null +++ b/konsole/src/HistorySizeWidget.ui @@ -0,0 +1,135 @@ + + + HistorySizeWidget + + + + 0 + 0 + 400 + 143 + + + + + 0 + 0 + + + + + + + + 0 + 0 + + + + Do not remember previous output + + + No scrollback + + + + + + + QLayout::SetFixedSize + + + + + + 0 + 0 + + + + Limit the remembered output to a fixed number of lines + + + Fixed size scrollback: + + + + + + + Number of lines of output to remember + + + 1 + + + 100000 + + + + + + + Qt::Horizontal + + + + 20 + 20 + + + + + + + + + + + 0 + 0 + + + + Remember all output produced by the terminal + + + Unlimited scrollback + + + + + + + + 0 + 0 + + + + + + + + + KIntSpinBox + QSpinBox +
    knuminput.h
    +
    + + KMessageWidget + QFrame +
    kmessagewidget.h
    + 1 +
    +
    + + + + fixedSizeHistoryButton + toggled(bool) + historyLineSpinner + setEnabled(bool) + + +
    diff --git a/konsole/src/IncrementalSearchBar.cpp b/konsole/src/IncrementalSearchBar.cpp new file mode 100644 index 00000000..aab98aa1 --- /dev/null +++ b/konsole/src/IncrementalSearchBar.cpp @@ -0,0 +1,286 @@ +/* + Copyright 2006-2008 by Robert Knight + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +// Own +#include "IncrementalSearchBar.h" + +// Qt +#include +#include +#include +#include +#include +#include + +// KDE +#include +#include +#include +#include + +using namespace Konsole; + +IncrementalSearchBar::IncrementalSearchBar(QWidget* aParent) + : QWidget(aParent) + , _searchEdit(0) + , _caseSensitive(0) + , _regExpression(0) + , _highlightMatches(0) + , _reverseSearch(0) + , _findNextButton(0) + , _findPreviousButton(0) + , _searchFromButton(0) +{ + QHBoxLayout* barLayout = new QHBoxLayout(this); + + QToolButton* closeButton = new QToolButton(this); + closeButton->setObjectName(QLatin1String("close-button")); + closeButton->setToolTip(i18nc("@info:tooltip", "Close the search bar")); + closeButton->setAutoRaise(true); + closeButton->setIcon(KIcon("dialog-close")); + connect(closeButton , SIGNAL(clicked()) , this , SIGNAL(closeClicked())); + + QLabel* findLabel = new QLabel(i18nc("@label:textbox", "Find:"), this); + _searchEdit = new KLineEdit(this); + _searchEdit->setClearButtonShown(true); + _searchEdit->installEventFilter(this); + _searchEdit->setObjectName(QLatin1String("search-edit")); + _searchEdit->setToolTip(i18nc("@info:tooltip", "Enter the text to search for here")); + + // text box may be a minimum of 6 characters wide and a maximum of 10 characters wide + // (since the maxWidth metric is used here, more characters probably will fit in than 6 + // and 10) + QFontMetrics metrics(_searchEdit->font()); + int maxWidth = metrics.maxWidth(); + _searchEdit->setMinimumWidth(maxWidth * 6); + _searchEdit->setMaximumWidth(maxWidth * 10); + + _searchTimer = new QTimer(this); + _searchTimer->setInterval(250); + _searchTimer->setSingleShot(true); + connect(_searchTimer , SIGNAL(timeout()) , this , SLOT(notifySearchChanged())); + connect(_searchEdit , SIGNAL(clearButtonClicked()) , this , SLOT(clearLineEdit())); + connect(_searchEdit , SIGNAL(textChanged(QString)) , _searchTimer , SLOT(start())); + + _findNextButton = new QToolButton(this); + _findNextButton->setObjectName(QLatin1String("find-next-button")); + _findNextButton->setText(i18nc("@action:button Go to the next phrase", "Next")); + _findNextButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + _findNextButton->setToolTip(i18nc("@info:tooltip", "Find the next match for the current search phrase")); + connect(_findNextButton , SIGNAL(clicked()) , this , SIGNAL(findNextClicked())); + + _findPreviousButton = new QToolButton(this); + _findPreviousButton->setObjectName(QLatin1String("find-previous-button")); + _findPreviousButton->setText(i18nc("@action:button Go to the previous phrase", "Previous")); + _findPreviousButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + _findPreviousButton->setToolTip(i18nc("@info:tooltip", "Find the previous match for the current search phrase")); + connect(_findPreviousButton , SIGNAL(clicked()) , this , SIGNAL(findPreviousClicked())); + + + _searchFromButton = new QToolButton(this); + _searchFromButton->setObjectName(QLatin1String("search-from-button")); + connect(_searchFromButton , SIGNAL(clicked()) , this , SIGNAL(searchFromClicked())); + + QToolButton* optionsButton = new QToolButton(this); + optionsButton->setObjectName(QLatin1String("find-options-button")); + optionsButton->setText(i18nc("@action:button Display options menu", "Options")); + optionsButton->setCheckable(false); + optionsButton->setPopupMode(QToolButton::InstantPopup); + optionsButton->setArrowType(Qt::DownArrow); + optionsButton->setToolButtonStyle(Qt::ToolButtonTextOnly); + optionsButton->setToolTip(i18nc("@info:tooltip", "Display the options menu")); + + barLayout->addWidget(closeButton); + barLayout->addWidget(findLabel); + barLayout->addWidget(_searchEdit); + barLayout->addWidget(_findNextButton); + barLayout->addWidget(_findPreviousButton); + barLayout->addWidget(_searchFromButton); + barLayout->addWidget(optionsButton); + + // Fill the options menu + QMenu* optionsMenu = new QMenu(this); + optionsButton->setMenu(optionsMenu); + + _caseSensitive = optionsMenu->addAction(i18nc("@item:inmenu", "Case sensitive")); + _caseSensitive->setCheckable(true); + _caseSensitive->setToolTip(i18nc("@info:tooltip", "Sets whether the search is case sensitive")); + connect(_caseSensitive, SIGNAL(toggled(bool)), + this, SIGNAL(matchCaseToggled(bool))); + + _regExpression = optionsMenu->addAction(i18nc("@item:inmenu", "Match regular expression")); + _regExpression->setCheckable(true); + connect(_regExpression, SIGNAL(toggled(bool)), + this, SIGNAL(matchRegExpToggled(bool))); + + _highlightMatches = optionsMenu->addAction(i18nc("@item:inmenu", "Highlight all matches")); + _highlightMatches->setCheckable(true); + _highlightMatches->setToolTip(i18nc("@info:tooltip", "Sets whether matching text should be highlighted")); + _highlightMatches->setChecked(true); + connect(_highlightMatches, SIGNAL(toggled(bool)), + this, SIGNAL(highlightMatchesToggled(bool))); + + _reverseSearch = optionsMenu->addAction(i18n("Search backwards")); + _reverseSearch->setCheckable(true); + _reverseSearch->setToolTip(i18n("Sets whether search should start from the bottom")); + _reverseSearch->setChecked(true); + connect(_reverseSearch, SIGNAL(toggled(bool)), + this, SLOT(updateButtonsAccordingToReverseSearchSetting())); + updateButtonsAccordingToReverseSearchSetting(); + + barLayout->addStretch(); + + barLayout->setContentsMargins(4, 4, 4, 4); + + setLayout(barLayout); +} + +void IncrementalSearchBar::notifySearchChanged() +{ + emit searchChanged(searchText()); +} + +void IncrementalSearchBar::updateButtonsAccordingToReverseSearchSetting() +{ + Q_ASSERT(_reverseSearch); + if (_reverseSearch->isChecked()) { + _searchFromButton->setText(i18nc("@action:button Search from bottom", "From bottom")); + _searchFromButton->setToolTip(i18n("Search for the current search phrase from the bottom")); + _findNextButton->setIcon(KIcon("go-up-search")); + _findPreviousButton->setIcon(KIcon("go-down-search")); + } else { + _searchFromButton->setText(i18nc("@action:button Search from top", "From top")); + _searchFromButton->setToolTip(i18n("Search for the current search phrase from the top")); + _findNextButton->setIcon(KIcon("go-down-search")); + _findPreviousButton->setIcon(KIcon("go-up-search")); + } +} + +QString IncrementalSearchBar::searchText() +{ + return _searchEdit->text(); +} + +void IncrementalSearchBar::setSearchText(const QString& text) +{ + if (text != searchText()) + _searchEdit->setText(text); +} + +bool IncrementalSearchBar::eventFilter(QObject* watched , QEvent* aEvent) +{ + if (watched == _searchEdit) { + if (aEvent->type() == QEvent::KeyPress) { + QKeyEvent* keyEvent = static_cast(aEvent); + + if (keyEvent->key() == Qt::Key_Escape) { + emit closeClicked(); + return true; + } + + if (keyEvent->key() == Qt::Key_Return && !keyEvent->modifiers()) { + _findNextButton->click(); + return true; + } + + if ((keyEvent->key() == Qt::Key_Return) && (keyEvent->modifiers() == Qt::ShiftModifier)) { + _findPreviousButton->click(); + return true; + } + + if ((keyEvent->key() == Qt::Key_Return) && (keyEvent->modifiers() == Qt::ControlModifier)) { + _searchFromButton->click(); + return true; + } + } + } + + return QWidget::eventFilter(watched, aEvent); +} + +void IncrementalSearchBar::keyPressEvent(QKeyEvent* event) +{ + static QSet movementKeysToPassAlong = QSet() + << Qt::Key_PageUp + << Qt::Key_PageDown + << Qt::Key_Up + << Qt::Key_Down; + + if (movementKeysToPassAlong.contains(event->key()) && + (event->modifiers() == Qt::ShiftModifier)) { + emit unhandledMovementKeyPressed(event); + } else { + QWidget::keyPressEvent(event); + } +} + +void IncrementalSearchBar::setVisible(bool visible) +{ + QWidget::setVisible(visible); + + if (visible) { + focusLineEdit(); + } +} + +void IncrementalSearchBar::setFoundMatch(bool match) +{ + if (!match && !_searchEdit->text().isEmpty()) { + KStatefulBrush backgroundBrush(KColorScheme::View, KColorScheme::NegativeBackground); + + QString matchStyleSheet = QString("QLineEdit{ background-color:%1 }") + .arg(backgroundBrush.brush(_searchEdit).color().name()); + + _searchEdit->setStyleSheet(matchStyleSheet); + } else if (_searchEdit->text().isEmpty()) { + clearLineEdit(); + } else { + KStatefulBrush backgroundBrush(KColorScheme::View, KColorScheme::PositiveBackground); + + QString matchStyleSheet = QString("QLineEdit{ background-color:%1 }") + .arg(backgroundBrush.brush(_searchEdit).color().name()); + + _searchEdit->setStyleSheet(matchStyleSheet); + } +} + +void IncrementalSearchBar::clearLineEdit() +{ + _searchEdit->setStyleSheet(QString()); +} + +void IncrementalSearchBar::focusLineEdit() +{ + _searchEdit->setFocus(Qt::ActiveWindowFocusReason); + _searchEdit->selectAll(); +} + +const QBitArray IncrementalSearchBar::optionsChecked() +{ + QBitArray options(4, 0); + + if (_caseSensitive->isChecked()) options.setBit(MatchCase); + if (_regExpression->isChecked()) options.setBit(RegExp); + if (_highlightMatches->isChecked()) options.setBit(HighlightMatches); + if (_reverseSearch->isChecked()) options.setBit(ReverseSearch); + + return options; +} + +#include "moc_IncrementalSearchBar.cpp" diff --git a/konsole/src/IncrementalSearchBar.h b/konsole/src/IncrementalSearchBar.h new file mode 100644 index 00000000..86063394 --- /dev/null +++ b/konsole/src/IncrementalSearchBar.h @@ -0,0 +1,162 @@ +/* + Copyright 2006-2008 by Robert Knight + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +#ifndef INCREMENTALSEARCHBAR_H +#define INCREMENTALSEARCHBAR_H + +// Qt +#include +#include + +#include +#include +#include +class KLineEdit; +#include + +namespace Konsole +{ +/** + * A widget which allows users to search incrementally through a document for a + * a text string or regular expression. + * + * The widget consists of a text box into which the user can enter their search text and + * buttons to trigger a search for the next and previous matches for the search text. + * + * When the search text is changed, the searchChanged() signal is emitted. A search through + * the document for the new text should begin immediately and the active view of the document + * should jump to display any matches if found. setFoundMatch() should be called whenever the + * search text changes to indicate whether a match for the text was found in the document. + * + * findNextClicked() and findPreviousClicked() signals are emitted when the user presses buttons + * to find next and previous matches respectively. + * + * The first indicates whether searches are case sensitive. + * The matchCaseToggled() signal is emitted when this is changed. + * The second indicates whether the search text should be treated as a plain string or + * as a regular expression. + * The matchRegExpToggled() signal is emitted when this is changed. + */ +class IncrementalSearchBar : public QWidget +{ + Q_OBJECT + +public: + /** + * This enum defines the options that can be checked. + */ + enum SearchOptions { + /** Highlight all matches */ + HighlightMatches = 0, + /** Searches are case-sensitive or not */ + MatchCase = 1, + /** Searches use regular expressions */ + RegExp = 2, + /** Search from the bottom and up **/ + ReverseSearch = 3 + }; + + /** + * Constructs a new incremental search bar with the given parent widget + */ + explicit IncrementalSearchBar(QWidget* parent = 0); + + /* Returns search options that are checked */ + const QBitArray optionsChecked(); + + /** + * Sets an indicator for the user as to whether or not a match for the + * current search text was found in the document. + * + * The indicator will not be shown if the search text is empty ( because + * the user has not yet entered a query ). + * + * @param match True if a match was found or false otherwise. If true, + * and the search text is non-empty, an indicator that no matches were + * found will be shown. + */ + void setFoundMatch(bool match); + + /** Returns the current search text */ + QString searchText(); + + void setSearchText(const QString& text); + + void focusLineEdit(); + + // reimplemented + virtual void setVisible(bool visible); +signals: + /** Emitted when the text entered in the search box is altered */ + void searchChanged(const QString& text); + /** Emitted when the user clicks the button to find the next match */ + void findNextClicked(); + /** Emitted when the user clicks the button to find the previous match */ + void findPreviousClicked(); + /** The search from beginning/end button */ + void searchFromClicked(); + /** + * Emitted when the user toggles the checkbox to indicate whether + * matches for the search text should be highlighted + */ + void highlightMatchesToggled(bool); + /** + * Emitted when the user toggles the checkbox to indicate whether + * matching for the search text should be case sensitive + */ + void matchCaseToggled(bool); + /** + * Emitted when the user toggles the checkbox to indicate whether + * the search text should be treated as a plain string or a regular expression + */ + void matchRegExpToggled(bool); + /** Emitted when the close button is clicked */ + void closeClicked(); + /** Emitted when the return button is pressed in the search box */ + void searchReturnPressed(const QString& text); + /** Emitted when shift+return buttons are pressed in the search box */ + void searchShiftPlusReturnPressed(); + /** A movement key not handled is forwarded to the terminal display */ + void unhandledMovementKeyPressed(QKeyEvent *event); + +protected: + virtual bool eventFilter(QObject* watched , QEvent* event); + virtual void keyPressEvent(QKeyEvent* event); + +public slots: + void clearLineEdit(); + +private slots: + void notifySearchChanged(); + void updateButtonsAccordingToReverseSearchSetting(); + +private: + KLineEdit* _searchEdit; + QAction* _caseSensitive; + QAction* _regExpression; + QAction* _highlightMatches; + QAction* _reverseSearch; + QToolButton* _findNextButton; + QToolButton* _findPreviousButton; + QToolButton* _searchFromButton; + + QTimer* _searchTimer; +}; +} +#endif // INCREMENTALSEARCHBAR_H diff --git a/konsole/src/KeyBindingEditor.cpp b/konsole/src/KeyBindingEditor.cpp new file mode 100644 index 00000000..7aa98cae --- /dev/null +++ b/konsole/src/KeyBindingEditor.cpp @@ -0,0 +1,235 @@ +/* + Copyright 2008 by Robert Knight + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +// Own +#include "KeyBindingEditor.h" + +// Qt +#include + +// KDE +#include +#include + +// Konsole +#include "ui_KeyBindingEditor.h" +#include "KeyboardTranslator.h" + +using namespace Konsole; + +KeyBindingEditor::KeyBindingEditor(QWidget* parent) + : QWidget(parent) + , _translator(new KeyboardTranslator(QString())) +{ + _ui = new Ui::KeyBindingEditor(); + _ui->setupUi(this); + + // description edit + connect(_ui->descriptionEdit , SIGNAL(textChanged(QString)) , + this , SLOT(setTranslatorDescription(QString))); + + // key bindings table + _ui->keyBindingTable->setColumnCount(2); + + QStringList labels; + labels << i18n("Key Combination") << i18n("Output"); + + _ui->keyBindingTable->setHorizontalHeaderLabels(labels); + _ui->keyBindingTable->horizontalHeader()->setStretchLastSection(true); + _ui->keyBindingTable->verticalHeader()->hide(); + _ui->keyBindingTable->setSelectionBehavior(QAbstractItemView::SelectRows); + + // add and remove buttons + _ui->addEntryButton->setIcon(KIcon("list-add")); + _ui->removeEntryButton->setIcon(KIcon("list-remove")); + + connect(_ui->removeEntryButton , SIGNAL(clicked()) , this , SLOT(removeSelectedEntry())); + connect(_ui->addEntryButton , SIGNAL(clicked()) , this , SLOT(addNewEntry())); + + // test area + _ui->testAreaInputEdit->installEventFilter(this); +} + +KeyBindingEditor::~KeyBindingEditor() +{ + delete _ui; + delete _translator; +} + +void KeyBindingEditor::removeSelectedEntry() +{ + QList uniqueList; + + foreach(QTableWidgetItem* item, _ui->keyBindingTable->selectedItems()) { + if (item->column() == 1) //Select item at the first column + item = _ui->keyBindingTable->item(item->row(), 0); + + if (!uniqueList.contains(item)) + uniqueList.append(item); + } + + foreach(QTableWidgetItem* item, uniqueList) { + // get the first item in the row which has the entry + + KeyboardTranslator::Entry existing = item->data(Qt::UserRole). + value(); + + _translator->removeEntry(existing); + + _ui->keyBindingTable->removeRow(item->row()); + } +} + +void KeyBindingEditor::addNewEntry() +{ + _ui->keyBindingTable->insertRow(_ui->keyBindingTable->rowCount()); + + int newRowCount = _ui->keyBindingTable->rowCount(); + + // block signals here to avoid triggering bindingTableItemChanged() slot call + _ui->keyBindingTable->blockSignals(true); + + _ui->keyBindingTable->setItem(newRowCount - 1, 0, new QTableWidgetItem()); + _ui->keyBindingTable->setItem(newRowCount - 1, 1, new QTableWidgetItem()); + + _ui->keyBindingTable->blockSignals(false); + + // make sure user can see new row + _ui->keyBindingTable->scrollToItem(_ui->keyBindingTable->item(newRowCount - 1, 0)); +} + +bool KeyBindingEditor::eventFilter(QObject* watched , QEvent* event) +{ + if (watched == _ui->testAreaInputEdit) { + if (event->type() == QEvent::KeyPress) { + QKeyEvent* keyEvent = static_cast(event); + + // The state here is currently set to the state that a newly started + // terminal in Konsole will be in ( which is also the same as the + // state just after a reset ), this has 'ANSI' turned on and all other + // states off. + // + // TODO: It may be useful to be able to specify the state in the 'test input' + // area, but preferably not in a way which clutters the UI with lots of + // checkboxes. + // + const KeyboardTranslator::States states = KeyboardTranslator::AnsiState; + + KeyboardTranslator::Entry entry = _translator->findEntry(keyEvent->key() , + keyEvent->modifiers(), + states); + + if (!entry.isNull()) { + _ui->testAreaInputEdit->setText(entry.conditionToString()); + _ui->testAreaOutputEdit->setText(entry.resultToString(true, keyEvent->modifiers())); + } else { + _ui->testAreaInputEdit->setText(keyEvent->text()); + _ui->testAreaOutputEdit->setText(keyEvent->text()); + } + + keyEvent->accept(); + return true; + } + } + return false; +} + +void KeyBindingEditor::setDescription(const QString& newDescription) +{ + _ui->descriptionEdit->setText(newDescription); + + setTranslatorDescription(newDescription); +} + +void KeyBindingEditor::setTranslatorDescription(const QString& newDescription) +{ + if (_translator) + _translator->setDescription(newDescription); +} + +QString KeyBindingEditor::description() const +{ + return _ui->descriptionEdit->text(); +} + +void KeyBindingEditor::setup(const KeyboardTranslator* translator) +{ + delete _translator; + + _translator = new KeyboardTranslator(*translator); + + // setup description edit + _ui->descriptionEdit->setClearButtonShown(true); + _ui->descriptionEdit->setText(translator->description()); + + // setup key binding table + setupKeyBindingTable(translator); +} + +KeyboardTranslator* KeyBindingEditor::translator() const +{ + return _translator; +} + +void KeyBindingEditor::bindingTableItemChanged(QTableWidgetItem* item) +{ + QTableWidgetItem* key = _ui->keyBindingTable->item(item->row() , 0); + KeyboardTranslator::Entry existing = key->data(Qt::UserRole).value(); + + QString condition = key->text(); + QString result = _ui->keyBindingTable->item(item->row() , 1)->text(); + + KeyboardTranslator::Entry entry = KeyboardTranslatorReader::createEntry(condition, result); + _translator->replaceEntry(existing, entry); + + // block signals to prevent this slot from being called repeatedly + _ui->keyBindingTable->blockSignals(true); + + key->setData(Qt::UserRole, QVariant::fromValue(entry)); + + _ui->keyBindingTable->blockSignals(false); +} + +void KeyBindingEditor::setupKeyBindingTable(const KeyboardTranslator* translator) +{ + disconnect(_ui->keyBindingTable , SIGNAL(itemChanged(QTableWidgetItem*)) , this , + SLOT(bindingTableItemChanged(QTableWidgetItem*))); + + QList entries = translator->entries(); + _ui->keyBindingTable->setRowCount(entries.count()); + + for (int row = 0 ; row < entries.count() ; row++) { + const KeyboardTranslator::Entry& entry = entries.at(row); + + QTableWidgetItem* keyItem = new QTableWidgetItem(entry.conditionToString()); + keyItem->setData(Qt::UserRole , QVariant::fromValue(entry)); + + QTableWidgetItem* textItem = new QTableWidgetItem(QString(entry.resultToString())); + + _ui->keyBindingTable->setItem(row, 0, keyItem); + _ui->keyBindingTable->setItem(row, 1, textItem); + } + _ui->keyBindingTable->sortItems(0); + + connect(_ui->keyBindingTable , SIGNAL(itemChanged(QTableWidgetItem*)) , this , + SLOT(bindingTableItemChanged(QTableWidgetItem*))); +} + +#include "moc_KeyBindingEditor.cpp" + diff --git a/konsole/src/KeyBindingEditor.h b/konsole/src/KeyBindingEditor.h new file mode 100644 index 00000000..b43f0f96 --- /dev/null +++ b/konsole/src/KeyBindingEditor.h @@ -0,0 +1,102 @@ +/* + Copyright 2007-2008 by Robert Knight + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +#ifndef KEYBINDINGEDITOR_H +#define KEYBINDINGEDITOR_H + +// Qt +#include + +#include + +QT_BEGIN_NAMESPACE +namespace Ui { class KeyBindingEditor; } +QT_END_NAMESPACE + +namespace Konsole +{ +class KeyboardTranslator; + +/** + * A dialog which allows the user to edit a key bindings list + * which maps between key combinations input by the user and + * the character sequence sent to the terminal when those + * combinations are pressed. + * + * The dialog can be initialized with the settings of an + * existing key bindings list using the setup() method. + * + * The dialog creates a copy of the supplied keyboard translator + * to which any changes are applied. The modified translator + * can be retrieved using the translator() method. + */ +class KeyBindingEditor : public QWidget +{ + Q_OBJECT + +public: + /** Constructs a new key bindings editor with the specified parent. */ + explicit KeyBindingEditor(QWidget* parent = 0); + virtual ~KeyBindingEditor(); + + /** + * Initializes the dialog with the bindings and other settings + * from the specified @p translator. + */ + void setup(const KeyboardTranslator* translator); + + /** + * Returns the modified translator describing the changes to the bindings + * and other settings which the user made. + */ + KeyboardTranslator* translator() const; + + /** + * Sets the text of the editor's description field. + */ + void setDescription(const QString& description); + + /** + * Returns the text of the editor's description field. + */ + QString description() const; + + // reimplemented to handle test area input + virtual bool eventFilter(QObject* watched , QEvent* event); + +private slots: + void setTranslatorDescription(const QString& description); + void bindingTableItemChanged(QTableWidgetItem* item); + void removeSelectedEntry(); + void addNewEntry(); + +private: + void setupKeyBindingTable(const KeyboardTranslator* translator); + + Ui::KeyBindingEditor* _ui; + + // translator to which modifications are made as the user makes + // changes in the UI. + // this is initialized as a copy of the translator specified + // when setup() is called + KeyboardTranslator* _translator; +}; +} + +#endif //KEYBINDINGEDITOR_H diff --git a/konsole/src/KeyBindingEditor.ui b/konsole/src/KeyBindingEditor.ui new file mode 100644 index 00000000..142580ae --- /dev/null +++ b/konsole/src/KeyBindingEditor.ui @@ -0,0 +1,125 @@ + + + KeyBindingEditor + + + + 0 + 0 + 374 + 530 + + + + + 0 + + + + + + + Description: + + + + + + + + + + + + + 0 + 1 + + + + + + + + + + Add + + + + + + + Remove + + + + + + + + + Qt::Vertical + + + + 20 + 16 + + + + + + + + Test Area + + + true + + + + + + Input: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + Output: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + true + + + + + + + + + + + KLineEdit + QLineEdit +
    klineedit.h
    +
    +
    + + +
    diff --git a/konsole/src/KeyboardTranslator.cpp b/konsole/src/KeyboardTranslator.cpp new file mode 100644 index 00000000..8fe2acb4 --- /dev/null +++ b/konsole/src/KeyboardTranslator.cpp @@ -0,0 +1,688 @@ +/* + This source file is part of Konsole, a terminal emulator. + + Copyright 2007-2008 by Robert Knight + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +// Own +#include "KeyboardTranslator.h" + +// System +#include +#include + +// Qt +#include +#include +#include + +// KDE +#include +#include + +using namespace Konsole; + +KeyboardTranslatorWriter::KeyboardTranslatorWriter(QIODevice* destination) + : _destination(destination) +{ + Q_ASSERT(destination && destination->isWritable()); + + _writer = new QTextStream(_destination); +} +KeyboardTranslatorWriter::~KeyboardTranslatorWriter() +{ + delete _writer; +} +void KeyboardTranslatorWriter::writeHeader(const QString& description) +{ + *_writer << "keyboard \"" << description << '\"' << '\n'; +} +void KeyboardTranslatorWriter::writeEntry(const KeyboardTranslator::Entry& entry) +{ + QString result; + if (entry.command() != KeyboardTranslator::NoCommand) + result = entry.resultToString(); + else + result = '\"' + entry.resultToString() + '\"'; + + *_writer << "key " << entry.conditionToString() << " : " << result << '\n'; +} + +// each line of the keyboard translation file is one of: +// +// - keyboard "name" +// - key KeySequence : "characters" +// - key KeySequence : CommandName +// +// KeySequence begins with the name of the key ( taken from the Qt::Key enum ) +// and is followed by the keyboard modifiers and state flags ( with + or - in front +// of each modifier or flag to indicate whether it is required ). All keyboard modifiers +// and flags are optional, if a particular modifier or state is not specified it is +// assumed not to be a part of the sequence. The key sequence may contain whitespace +// +// eg: "key Up+Shift : scrollLineUp" +// "key PgDown-Shift : "\E[6~" +// +// (lines containing only whitespace are ignored, parseLine assumes that comments have +// already been removed) +// + +KeyboardTranslatorReader::KeyboardTranslatorReader(QIODevice* source) + : _source(source) + , _hasNext(false) +{ + // read input until we find the description + while (_description.isEmpty() && !source->atEnd()) { + QList tokens = tokenize(QString::fromLocal8Bit(source->readLine())); + if (!tokens.isEmpty() && tokens.first().type == Token::TitleKeyword) + _description = i18n(tokens[1].text.toUtf8()); + } + // read first entry (if any) + readNext(); +} +void KeyboardTranslatorReader::readNext() +{ + // find next entry + while (!_source->atEnd()) { + const QList& tokens = tokenize(QString::fromLocal8Bit(_source->readLine())); + if (!tokens.isEmpty() && tokens.first().type == Token::KeyKeyword) { + KeyboardTranslator::States flags = KeyboardTranslator::NoState; + KeyboardTranslator::States flagMask = KeyboardTranslator::NoState; + Qt::KeyboardModifiers modifiers = Qt::NoModifier; + Qt::KeyboardModifiers modifierMask = Qt::NoModifier; + + int keyCode = Qt::Key_unknown; + + decodeSequence(tokens[1].text.toLower(), + keyCode, + modifiers, + modifierMask, + flags, + flagMask); + + KeyboardTranslator::Command command = KeyboardTranslator::NoCommand; + QByteArray text; + + // get text or command + if (tokens[2].type == Token::OutputText) { + text = tokens[2].text.toLocal8Bit(); + } else if (tokens[2].type == Token::Command) { + // identify command + if (!parseAsCommand(tokens[2].text, command)) + kWarning() << "Key" << tokens[1].text << ", Command" << tokens[2].text << "not understood. "; + } + + KeyboardTranslator::Entry newEntry; + newEntry.setKeyCode(keyCode); + newEntry.setState(flags); + newEntry.setStateMask(flagMask); + newEntry.setModifiers(modifiers); + newEntry.setModifierMask(modifierMask); + newEntry.setText(text); + newEntry.setCommand(command); + + _nextEntry = newEntry; + + _hasNext = true; + + return; + } + } + + _hasNext = false; +} + +bool KeyboardTranslatorReader::parseAsCommand(const QString& text, KeyboardTranslator::Command& command) +{ + if (text.compare("erase", Qt::CaseInsensitive) == 0) + command = KeyboardTranslator::EraseCommand; + else if (text.compare("scrollpageup", Qt::CaseInsensitive) == 0) + command = KeyboardTranslator::ScrollPageUpCommand; + else if (text.compare("scrollpagedown", Qt::CaseInsensitive) == 0) + command = KeyboardTranslator::ScrollPageDownCommand; + else if (text.compare("scrolllineup", Qt::CaseInsensitive) == 0) + command = KeyboardTranslator::ScrollLineUpCommand; + else if (text.compare("scrolllinedown", Qt::CaseInsensitive) == 0) + command = KeyboardTranslator::ScrollLineDownCommand; + else if (text.compare("scrolluptotop", Qt::CaseInsensitive) == 0) + command = KeyboardTranslator::ScrollUpToTopCommand; + else if (text.compare("scrolldowntobottom", Qt::CaseInsensitive) == 0) + command = KeyboardTranslator::ScrollDownToBottomCommand; + else + return false; + + return true; +} + +bool KeyboardTranslatorReader::decodeSequence(const QString& text, + int& keyCode, + Qt::KeyboardModifiers& modifiers, + Qt::KeyboardModifiers& modifierMask, + KeyboardTranslator::States& flags, + KeyboardTranslator::States& flagMask) +{ + bool isWanted = true; + bool endOfItem = false; + QString buffer; + + Qt::KeyboardModifiers tempModifiers = modifiers; + Qt::KeyboardModifiers tempModifierMask = modifierMask; + KeyboardTranslator::States tempFlags = flags; + KeyboardTranslator::States tempFlagMask = flagMask; + + for (int i = 0 ; i < text.count() ; i++) { + const QChar& ch = text[i]; + const bool isFirstLetter = (i == 0); + const bool isLastLetter = (i == text.count() - 1); + endOfItem = true; + if (ch.isLetterOrNumber()) { + endOfItem = false; + buffer.append(ch); + } else if (isFirstLetter) { + buffer.append(ch); + } + + if ((endOfItem || isLastLetter) && !buffer.isEmpty()) { + Qt::KeyboardModifier itemModifier = Qt::NoModifier; + int itemKeyCode = 0; + KeyboardTranslator::State itemFlag = KeyboardTranslator::NoState; + + if (parseAsModifier(buffer, itemModifier)) { + tempModifierMask |= itemModifier; + + if (isWanted) + tempModifiers |= itemModifier; + } else if (parseAsStateFlag(buffer, itemFlag)) { + tempFlagMask |= itemFlag; + + if (isWanted) + tempFlags |= itemFlag; + } else if (parseAsKeyCode(buffer, itemKeyCode)) { + keyCode = itemKeyCode; + } else { + kWarning() << "Unable to parse key binding item:" << buffer; + } + + buffer.clear(); + } + + // check if this is a wanted / not-wanted flag and update the + // state ready for the next item + if (ch == '+') + isWanted = true; + else if (ch == '-') + isWanted = false; + } + + modifiers = tempModifiers; + modifierMask = tempModifierMask; + flags = tempFlags; + flagMask = tempFlagMask; + + return true; +} + +bool KeyboardTranslatorReader::parseAsModifier(const QString& item , Qt::KeyboardModifier& modifier) +{ + if (item == "shift") + modifier = Qt::ShiftModifier; + else if (item == "ctrl" || item == "control") + modifier = Qt::ControlModifier; + else if (item == "alt") + modifier = Qt::AltModifier; + else if (item == "meta") + modifier = Qt::MetaModifier; + else if (item == "keypad") + modifier = Qt::KeypadModifier; + else + return false; + + return true; +} +bool KeyboardTranslatorReader::parseAsStateFlag(const QString& item , KeyboardTranslator::State& flag) +{ + if (item == "appcukeys" || item == "appcursorkeys") + flag = KeyboardTranslator::CursorKeysState; + else if (item == "ansi") + flag = KeyboardTranslator::AnsiState; + else if (item == "newline") + flag = KeyboardTranslator::NewLineState; + else if (item == "appscreen") + flag = KeyboardTranslator::AlternateScreenState; + else if (item == "anymod" || item == "anymodifier") + flag = KeyboardTranslator::AnyModifierState; + else if (item == "appkeypad") + flag = KeyboardTranslator::ApplicationKeypadState; + else + return false; + + return true; +} +bool KeyboardTranslatorReader::parseAsKeyCode(const QString& item , int& keyCode) +{ + QKeySequence sequence = QKeySequence::fromString(item); + if (!sequence.isEmpty()) { + keyCode = sequence[0]; + + if (sequence.count() > 1) { + kWarning() << "Unhandled key codes in sequence: " << item; + } + // additional cases implemented for backwards compatibility with KDE 3 + } else if (item == "prior") { // TODO: remove it in the future + keyCode = Qt::Key_PageUp; + } else if (item == "next") { // TODO: remove it in the future + keyCode = Qt::Key_PageDown; + } else { + return false; + } + + return true; +} + +QString KeyboardTranslatorReader::description() const +{ + return _description; +} +bool KeyboardTranslatorReader::hasNextEntry() +{ + return _hasNext; +} +KeyboardTranslator::Entry KeyboardTranslatorReader::createEntry(const QString& condition , + const QString& result) +{ + QString entryString("keyboard \"temporary\"\nkey "); + entryString.append(condition); + entryString.append(" : "); + + // if 'result' is the name of a command then the entry result will be that command, + // otherwise the result will be treated as a string to echo when the key sequence + // specified by 'condition' is pressed + KeyboardTranslator::Command command; + if (parseAsCommand(result, command)) + entryString.append(result); + else + entryString.append('\"' + result + '\"'); + + QByteArray array = entryString.toUtf8(); + QBuffer buffer(&array); + buffer.open(QIODevice::ReadOnly); + KeyboardTranslatorReader reader(&buffer); + + KeyboardTranslator::Entry entry; + if (reader.hasNextEntry()) + entry = reader.nextEntry(); + + return entry; +} + +KeyboardTranslator::Entry KeyboardTranslatorReader::nextEntry() +{ + Q_ASSERT(_hasNext); + KeyboardTranslator::Entry entry = _nextEntry; + readNext(); + return entry; +} +bool KeyboardTranslatorReader::parseError() +{ + return false; +} +QList KeyboardTranslatorReader::tokenize(const QString& line) +{ + QString text = line; + + // remove comments + bool inQuotes = false; + int commentPos = -1; + for (int i = text.length() - 1; i >= 0; i--) { + QChar ch = text[i]; + if (ch == '\"') + inQuotes = !inQuotes; + else if (ch == '#' && !inQuotes) + commentPos = i; + } + if (commentPos != -1) + text.remove(commentPos, text.length()); + + text = text.simplified(); + + // title line: keyboard "title" + static QRegExp title("keyboard\\s+\"(.*)\""); + // key line: key KeySequence : "output" + // key line: key KeySequence : command + static QRegExp key("key\\s+([\\w\\+\\s\\-\\*\\.]+)\\s*:\\s*(\"(.*)\"|\\w+)"); + + QList list; + if (text.isEmpty()) { + return list; + } + + if (title.exactMatch(text)) { + Token titleToken = { Token::TitleKeyword , QString() }; + Token textToken = { Token::TitleText , title.capturedTexts()[1] }; + + list << titleToken << textToken; + } else if (key.exactMatch(text)) { + Token keyToken = { Token::KeyKeyword , QString() }; + Token sequenceToken = { Token::KeySequence , key.capturedTexts()[1].remove(' ') }; + + list << keyToken << sequenceToken; + + if (key.capturedTexts()[3].isEmpty()) { + // capturedTexts()[2] is a command + Token commandToken = { Token::Command , key.capturedTexts()[2] }; + list << commandToken; + } else { + // capturedTexts()[3] is the output string + Token outputToken = { Token::OutputText , key.capturedTexts()[3] }; + list << outputToken; + } + } else { + kWarning() << "Line in keyboard translator file could not be understood:" << text; + } + + return list; +} + +KeyboardTranslator::Entry::Entry() + : _keyCode(0) + , _modifiers(Qt::NoModifier) + , _modifierMask(Qt::NoModifier) + , _state(NoState) + , _stateMask(NoState) + , _command(NoCommand) +{ +} + +bool KeyboardTranslator::Entry::operator==(const Entry& rhs) const +{ + return _keyCode == rhs._keyCode && + _modifiers == rhs._modifiers && + _modifierMask == rhs._modifierMask && + _state == rhs._state && + _stateMask == rhs._stateMask && + _command == rhs._command && + _text == rhs._text; +} + +bool KeyboardTranslator::Entry::matches(int testKeyCode, + Qt::KeyboardModifiers testKeyboardModifiers, + States testState) const +{ + if (_keyCode != testKeyCode) + return false; + + if ((testKeyboardModifiers & _modifierMask) != (_modifiers & _modifierMask)) + return false; + + // if testKeyboardModifiers is non-zero, the 'any modifier' state is implicit + if (testKeyboardModifiers != 0) + testState |= AnyModifierState; + + if ((testState & _stateMask) != (_state & _stateMask)) + return false; + + // special handling for the 'Any Modifier' state, which checks for the presence of + // any or no modifiers. In this context, the 'keypad' modifier does not count. + bool anyModifiersSet = (testKeyboardModifiers != 0) + && (testKeyboardModifiers != Qt::KeypadModifier); + bool wantAnyModifier = _state & KeyboardTranslator::AnyModifierState; + if (_stateMask & KeyboardTranslator::AnyModifierState) { + if (wantAnyModifier != anyModifiersSet) + return false; + } + + return true; +} +QByteArray KeyboardTranslator::Entry::escapedText(bool expandWildCards, + Qt::KeyboardModifiers keyboardModifiers) const +{ + QByteArray result(text(expandWildCards, keyboardModifiers)); + + for (int i = 0 ; i < result.count() ; i++) { + const char ch = result[i]; + char replacement = 0; + + switch (ch) { + case 27 : replacement = 'E'; break; + case 8 : replacement = 'b'; break; + case 12 : replacement = 'f'; break; + case 9 : replacement = 't'; break; + case 13 : replacement = 'r'; break; + case 10 : replacement = 'n'; break; + default: + // any character which is not printable is replaced by an equivalent + // \xhh escape sequence (where 'hh' are the corresponding hex digits) + if (!QChar(ch).isPrint()) + replacement = 'x'; + } + + if (replacement == 'x') { + result.replace(i, 1, "\\x" + QByteArray(1, ch).toHex()); + } else if (replacement != 0) { + result.remove(i, 1); + result.insert(i, '\\'); + result.insert(i + 1, replacement); + } + } + + return result; +} +QByteArray KeyboardTranslator::Entry::unescape(const QByteArray& input) const +{ + QByteArray result(input); + + for (int i = 0 ; i < result.count() - 1 ; i++) { + QByteRef ch = result[i]; + if (ch == '\\') { + char replacement[2] = {0, 0}; + int charsToRemove = 2; + bool escapedChar = true; + + switch (result[i + 1]) { + case 'E' : replacement[0] = 27; break; + case 'b' : replacement[0] = 8 ; break; + case 'f' : replacement[0] = 12; break; + case 't' : replacement[0] = 9 ; break; + case 'r' : replacement[0] = 13; break; + case 'n' : replacement[0] = 10; break; + case 'x' : { + // format is \xh or \xhh where 'h' is a hexadecimal + // digit from 0-9 or A-F which should be replaced + // with the corresponding character value + char hexDigits[3] = {0}; + + if ((i < result.count() - 2) && isxdigit(result[i + 2])) + hexDigits[0] = result[i + 2]; + if ((i < result.count() - 3) && isxdigit(result[i + 3])) + hexDigits[1] = result[i + 3]; + + unsigned charValue = 0; + sscanf(hexDigits, "%2x", &charValue); + + replacement[0] = static_cast(charValue); + charsToRemove = 2 + qstrlen(hexDigits); + } + break; + default: + escapedChar = false; + } + + if (escapedChar) + result.replace(i, charsToRemove, replacement); + } + } + + return result; +} + +void KeyboardTranslator::Entry::insertModifier(QString& item , int modifier) const +{ + if (!(modifier & _modifierMask)) + return; + + if (modifier & _modifiers) + item += '+'; + else + item += '-'; + + if (modifier == Qt::ShiftModifier) + item += "Shift"; + else if (modifier == Qt::ControlModifier) + item += "Ctrl"; + else if (modifier == Qt::AltModifier) + item += "Alt"; + else if (modifier == Qt::MetaModifier) + item += "Meta"; + else if (modifier == Qt::KeypadModifier) + item += "KeyPad"; +} +void KeyboardTranslator::Entry::insertState(QString& item, int aState) const +{ + if (!(aState & _stateMask)) + return; + + if (aState & _state) + item += '+'; + else + item += '-'; + + if (aState == KeyboardTranslator::AlternateScreenState) + item += "AppScreen"; + else if (aState == KeyboardTranslator::NewLineState) + item += "NewLine"; + else if (aState == KeyboardTranslator::AnsiState) + item += "Ansi"; + else if (aState == KeyboardTranslator::CursorKeysState) + item += "AppCursorKeys"; + else if (aState == KeyboardTranslator::AnyModifierState) + item += "AnyModifier"; + else if (aState == KeyboardTranslator::ApplicationKeypadState) + item += "AppKeypad"; +} +QString KeyboardTranslator::Entry::resultToString(bool expandWildCards, + Qt::KeyboardModifiers keyboardModifiers) const +{ + if (!_text.isEmpty()) + return escapedText(expandWildCards, keyboardModifiers); + else if (_command == EraseCommand) + return "Erase"; + else if (_command == ScrollPageUpCommand) + return "ScrollPageUp"; + else if (_command == ScrollPageDownCommand) + return "ScrollPageDown"; + else if (_command == ScrollLineUpCommand) + return "ScrollLineUp"; + else if (_command == ScrollLineDownCommand) + return "ScrollLineDown"; + else if (_command == ScrollUpToTopCommand) + return "ScrollUpToTop"; + else if (_command == ScrollDownToBottomCommand) + return "ScrollDownToBottom"; + + return QString(); +} +QString KeyboardTranslator::Entry::conditionToString() const +{ + QString result = QKeySequence(_keyCode).toString(); + + insertModifier(result , Qt::ShiftModifier); + insertModifier(result , Qt::ControlModifier); + insertModifier(result , Qt::AltModifier); + insertModifier(result , Qt::MetaModifier); + insertModifier(result , Qt::KeypadModifier); + + insertState(result , KeyboardTranslator::AlternateScreenState); + insertState(result , KeyboardTranslator::NewLineState); + insertState(result , KeyboardTranslator::AnsiState); + insertState(result , KeyboardTranslator::CursorKeysState); + insertState(result , KeyboardTranslator::AnyModifierState); + insertState(result , KeyboardTranslator::ApplicationKeypadState); + + return result; +} + +KeyboardTranslator::KeyboardTranslator(const QString& aName) + : _name(aName) +{ +} + +FallbackKeyboardTranslator::FallbackKeyboardTranslator() + : KeyboardTranslator("fallback") +{ + setDescription("Fallback Keyboard Translator"); + + // Key "TAB" should send out '\t' + KeyboardTranslator::Entry entry; + entry.setKeyCode(Qt::Key_Tab); + entry.setText("\t"); + addEntry(entry); +} + +void KeyboardTranslator::setDescription(const QString& aDescription) +{ + _description = aDescription; +} + +QString KeyboardTranslator::description() const +{ + return _description; +} + +void KeyboardTranslator::setName(const QString& aName) +{ + _name = aName; +} + +QString KeyboardTranslator::name() const +{ + return _name; +} + +QList KeyboardTranslator::entries() const +{ + return _entries.values(); +} + +void KeyboardTranslator::addEntry(const Entry& entry) +{ + const int keyCode = entry.keyCode(); + _entries.insert(keyCode, entry); +} + +void KeyboardTranslator::replaceEntry(const Entry& existing , const Entry& replacement) +{ + if (!existing.isNull()) + _entries.remove(existing.keyCode(), existing); + + _entries.insert(replacement.keyCode(), replacement); +} + +void KeyboardTranslator::removeEntry(const Entry& entry) +{ + _entries.remove(entry.keyCode(), entry); +} + +KeyboardTranslator::Entry KeyboardTranslator::findEntry(int keyCode, Qt::KeyboardModifiers modifiers, States state) const +{ + foreach(const Entry & entry, _entries.values(keyCode)) { + if (entry.matches(keyCode, modifiers, state)) + return entry; + } + + return Entry(); // No matching entry +} diff --git a/konsole/src/KeyboardTranslator.h b/konsole/src/KeyboardTranslator.h new file mode 100644 index 00000000..8cb2b886 --- /dev/null +++ b/konsole/src/KeyboardTranslator.h @@ -0,0 +1,534 @@ +/* + This source file is part of Konsole, a terminal emulator. + + Copyright 2007-2008 by Robert Knight + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +#ifndef KEYBOARDTRANSLATOR_H +#define KEYBOARDTRANSLATOR_H + +// Qt +#include +#include +//#include +#include + +// Konsole +#include "konsoleprivate_export.h" + +#include +#include + +namespace Konsole +{ +/** + * A converter which maps between key sequences pressed by the user and the + * character strings which should be sent to the terminal and commands + * which should be invoked when those character sequences are pressed. + * + * Konsole supports multiple keyboard translators, allowing the user to + * specify the character sequences which are sent to the terminal + * when particular key sequences are pressed. + * + * A key sequence is defined as a key code, associated keyboard modifiers + * (Shift,Ctrl,Alt,Meta etc.) and state flags which indicate the state + * which the terminal must be in for the key sequence to apply. + */ +class KONSOLEPRIVATE_EXPORT KeyboardTranslator +{ +public: + /** + * The meaning of a particular key sequence may depend upon the state which + * the terminal emulation is in. Therefore findEntry() may return a different + * Entry depending upon the state flags supplied. + * + * This enum describes the states which may be associated with a particular + * entry in the keyboard translation entry. + */ + enum State { + /** Indicates that no special state is active */ + NoState = 0, + /** + * TODO More documentation + */ + NewLineState = 1, + /** + * Indicates that the terminal is in 'ANSI' mode. + * TODO: More documentation + */ + AnsiState = 2, + /** + * TODO More documentation + */ + CursorKeysState = 4, + /** + * Indicates that the alternate screen ( typically used by interactive programs + * such as screen or vim ) is active + */ + AlternateScreenState = 8, + /** Indicates that any of the modifier keys is active. */ + AnyModifierState = 16, + /** Indicates that the numpad is in application mode. */ + ApplicationKeypadState = 32 + }; + Q_DECLARE_FLAGS(States, State) + + /** + * This enum describes commands which are associated with particular key sequences. + */ + enum Command { + /** Indicates that no command is associated with this command sequence */ + NoCommand = 0, + /** TODO Document me */ + SendCommand = 1, + /** Scroll the terminal display up one page */ + ScrollPageUpCommand = 2, + /** Scroll the terminal display down one page */ + ScrollPageDownCommand = 4, + /** Scroll the terminal display up one line */ + ScrollLineUpCommand = 8, + /** Scroll the terminal display down one line */ + ScrollLineDownCommand = 16, + /** Scroll the terminal display up to the start of history */ + ScrollUpToTopCommand = 32, + /** Scroll the terminal display down to the end of history */ + ScrollDownToBottomCommand = 64, + /** Echos the operating system specific erase character. */ + EraseCommand = 256 + }; + Q_DECLARE_FLAGS(Commands, Command) + + /** + * Represents an association between a key sequence pressed by the user + * and the character sequence and commands associated with it for a particular + * KeyboardTranslator. + */ + class Entry + { + public: + /** + * Constructs a new entry for a keyboard translator. + */ + Entry(); + + /** + * Returns true if this entry is null. + * This is true for newly constructed entries which have no properties set. + */ + bool isNull() const; + + /** Returns the commands associated with this entry */ + Command command() const; + /** Sets the command associated with this entry. */ + void setCommand(Command aCommand); + + /** + * Returns the character sequence associated with this entry, optionally replacing + * wildcard '*' characters with numbers to indicate the keyboard modifiers being pressed. + * + * TODO: The numbers used to replace '*' characters are taken from the Konsole/KDE 3 code. + * Document them. + * + * @param expandWildCards Specifies whether wild cards (occurrences of the '*' character) in + * the entry should be replaced with a number to indicate the modifier keys being pressed. + * + * @param keyboardModifiers The keyboard modifiers being pressed. + */ + QByteArray text(bool expandWildCards = false, + Qt::KeyboardModifiers keyboardModifiers = Qt::NoModifier) const; + + /** Sets the character sequence associated with this entry */ + void setText(const QByteArray& aText); + + /** + * Returns the character sequence associated with this entry, + * with any non-printable characters replaced with escape sequences. + * + * eg. \\E for Escape, \\t for tab, \\n for new line. + * + * @param expandWildCards See text() + * @param modifiers See text() + */ + QByteArray escapedText(bool expandWildCards = false, + Qt::KeyboardModifiers modifiers = Qt::NoModifier) const; + + /** Returns the character code ( from the Qt::Key enum ) associated with this entry */ + int keyCode() const; + /** Sets the character code associated with this entry */ + void setKeyCode(int aKeyCode); + + /** + * Returns a bitwise-OR of the enabled keyboard modifiers associated with this entry. + * If a modifier is set in modifierMask() but not in modifiers(), this means that the entry + * only matches when that modifier is NOT pressed. + * + * If a modifier is not set in modifierMask() then the entry matches whether the modifier + * is pressed or not. + */ + Qt::KeyboardModifiers modifiers() const; + + /** Returns the keyboard modifiers which are valid in this entry. See modifiers() */ + Qt::KeyboardModifiers modifierMask() const; + + /** See modifiers() */ + void setModifiers(Qt::KeyboardModifiers modifiers); + /** See modifierMask() and modifiers() */ + void setModifierMask(Qt::KeyboardModifiers modifiers); + + /** + * Returns a bitwise-OR of the enabled state flags associated with this entry. + * If flag is set in stateMask() but not in state(), this means that the entry only + * matches when the terminal is NOT in that state. + * + * If a state is not set in stateMask() then the entry matches whether the terminal + * is in that state or not. + */ + States state() const; + + /** Returns the state flags which are valid in this entry. See state() */ + States stateMask() const; + + /** See state() */ + void setState(States aState); + /** See stateMask() */ + void setStateMask(States aStateMask); + + /** + * Returns the key code and modifiers associated with this entry + * as a QKeySequence + */ + //QKeySequence keySequence() const; + + /** + * Returns this entry's conditions ( ie. its key code, modifier and state criteria ) + * as a string. + */ + QString conditionToString() const; + + /** + * Returns this entry's result ( ie. its command or character sequence ) + * as a string. + * + * @param expandWildCards See text() + * @param modifiers See text() + */ + QString resultToString(bool expandWildCards = false, + Qt::KeyboardModifiers modifiers = Qt::NoModifier) const; + + /** + * Returns true if this entry matches the given key sequence, specified + * as a combination of @p keyCode , @p modifiers and @p state. + */ + bool matches(int keyCode , + Qt::KeyboardModifiers modifiers , + States flags) const; + + bool operator==(const Entry& rhs) const; + + private: + void insertModifier(QString& item , int modifier) const; + void insertState(QString& item , int state) const; + QByteArray unescape(const QByteArray& text) const; + + int _keyCode; + Qt::KeyboardModifiers _modifiers; + Qt::KeyboardModifiers _modifierMask; + States _state; + States _stateMask; + + Command _command; + QByteArray _text; + }; + + /** Constructs a new keyboard translator with the given @p name */ + explicit KeyboardTranslator(const QString& name); + + //KeyboardTranslator(const KeyboardTranslator& other); + + /** Returns the name of this keyboard translator */ + QString name() const; + + /** Sets the name of this keyboard translator */ + void setName(const QString& name); + + /** Returns the descriptive name of this keyboard translator */ + QString description() const; + + /** Sets the descriptive name of this keyboard translator */ + void setDescription(const QString& description); + + /** + * Looks for an entry in this keyboard translator which matches the given + * key code, keyboard modifiers and state flags. + * + * Returns the matching entry if found or a null Entry otherwise ( ie. + * entry.isNull() will return true ) + * + * @param keyCode A key code from the Qt::Key enum + * @param modifiers A combination of modifiers + * @param state Optional flags which specify the current state of the terminal + */ + Entry findEntry(int keyCode , + Qt::KeyboardModifiers modifiers , + States state = NoState) const; + + /** + * Adds an entry to this keyboard translator's table. Entries can be looked up according + * to their key sequence using findEntry() + */ + void addEntry(const Entry& entry); + + /** + * Replaces an entry in the translator. If the @p existing entry is null, + * then this is equivalent to calling addEntry(@p replacement) + */ + void replaceEntry(const Entry& existing , const Entry& replacement); + + /** + * Removes an entry from the table. + */ + void removeEntry(const Entry& entry); + + /** Returns a list of all entries in the translator. */ + QList entries() const; + +private: + // All entries in this translator, indexed by their keycode + QMultiHash _entries; + + QString _name; + QString _description; +}; +Q_DECLARE_OPERATORS_FOR_FLAGS(KeyboardTranslator::States) +Q_DECLARE_OPERATORS_FOR_FLAGS(KeyboardTranslator::Commands) + +class FallbackKeyboardTranslator : public KeyboardTranslator +{ +public: + FallbackKeyboardTranslator(); +}; + +/** + * Parses the contents of a Keyboard Translator (.keytab) file and + * returns the entries found in it. + * + * Usage example: + * + * @code + * QFile source( "/path/to/keytab" ); + * source.open( QIODevice::ReadOnly ); + * + * KeyboardTranslator* translator = new KeyboardTranslator( "name-of-translator" ); + * + * KeyboardTranslatorReader reader(source); + * while ( reader.hasNextEntry() ) + * translator->addEntry(reader.nextEntry()); + * + * source.close(); + * + * if ( !reader.parseError() ) + * { + * // parsing succeeded, do something with the translator + * } + * else + * { + * // parsing failed + * } + * @endcode + */ +class KeyboardTranslatorReader +{ +public: + /** Constructs a new reader which parses the given @p source */ + explicit KeyboardTranslatorReader(QIODevice* source); + + /** + * Returns the description text. + * TODO: More documentation + */ + QString description() const; + + /** Returns true if there is another entry in the source stream */ + bool hasNextEntry(); + /** Returns the next entry found in the source stream */ + KeyboardTranslator::Entry nextEntry(); + + /** + * Returns true if an error occurred whilst parsing the input or + * false if no error occurred. + */ + bool parseError(); + + /** + * Parses a condition and result string for a translator entry + * and produces a keyboard translator entry. + * + * The condition and result strings are in the same format as in + */ + static KeyboardTranslator::Entry createEntry(const QString& condition , + const QString& result); +private: + struct Token { + enum Type { + TitleKeyword, + TitleText, + KeyKeyword, + KeySequence, + Command, + OutputText + }; + Type type; + QString text; + }; + QList tokenize(const QString&); + void readNext(); + bool decodeSequence(const QString& , + int& keyCode, + Qt::KeyboardModifiers& modifiers, + Qt::KeyboardModifiers& modifierMask, + KeyboardTranslator::States& state, + KeyboardTranslator::States& stateFlags); + + static bool parseAsModifier(const QString& item , Qt::KeyboardModifier& modifier); + static bool parseAsStateFlag(const QString& item , KeyboardTranslator::State& state); + static bool parseAsKeyCode(const QString& item , int& keyCode); + static bool parseAsCommand(const QString& text , KeyboardTranslator::Command& command); + + QIODevice* _source; + QString _description; + KeyboardTranslator::Entry _nextEntry; + bool _hasNext; +}; + +/** Writes a keyboard translation to disk. */ +class KeyboardTranslatorWriter +{ +public: + /** + * Constructs a new writer which saves data into @p destination. + * The caller is responsible for closing the device when writing is complete. + */ + explicit KeyboardTranslatorWriter(QIODevice* destination); + ~KeyboardTranslatorWriter(); + + /** + * Writes the header for the keyboard translator. + * @param description Description of the keyboard translator. + */ + void writeHeader(const QString& description); + /** Writes a translator entry. */ + void writeEntry(const KeyboardTranslator::Entry& entry); + +private: + QIODevice* _destination; + QTextStream* _writer; +}; + +inline int KeyboardTranslator::Entry::keyCode() const +{ + return _keyCode; +} +inline void KeyboardTranslator::Entry::setKeyCode(int aKeyCode) +{ + _keyCode = aKeyCode; +} + +inline void KeyboardTranslator::Entry::setModifiers(Qt::KeyboardModifiers modifier) +{ + _modifiers = modifier; +} +inline Qt::KeyboardModifiers KeyboardTranslator::Entry::modifiers() const +{ + return _modifiers; +} + +inline void KeyboardTranslator::Entry::setModifierMask(Qt::KeyboardModifiers mask) +{ + _modifierMask = mask; +} +inline Qt::KeyboardModifiers KeyboardTranslator::Entry::modifierMask() const +{ + return _modifierMask; +} + +inline bool KeyboardTranslator::Entry::isNull() const +{ + return (*this == Entry()); +} + +inline void KeyboardTranslator::Entry::setCommand(Command aCommand) +{ + _command = aCommand; +} +inline KeyboardTranslator::Command KeyboardTranslator::Entry::command() const +{ + return _command; +} + +inline void KeyboardTranslator::Entry::setText(const QByteArray& aText) +{ + _text = unescape(aText); +} +inline int oneOrZero(int value) +{ + return value ? 1 : 0; +} +inline QByteArray KeyboardTranslator::Entry::text(bool expandWildCards, + Qt::KeyboardModifiers keyboardModifiers) const +{ + QByteArray expandedText = _text; + + if (expandWildCards) { + int modifierValue = 1; + modifierValue += oneOrZero(keyboardModifiers & Qt::ShiftModifier); + modifierValue += oneOrZero(keyboardModifiers & Qt::AltModifier) << 1; + modifierValue += oneOrZero(keyboardModifiers & Qt::ControlModifier) << 2; + + for (int i = 0; i < _text.length(); i++) { + if (expandedText[i] == '*') + expandedText[i] = '0' + modifierValue; + } + } + + return expandedText; +} + +inline void KeyboardTranslator::Entry::setState(States aState) +{ + _state = aState; +} +inline KeyboardTranslator::States KeyboardTranslator::Entry::state() const +{ + return _state; +} + +inline void KeyboardTranslator::Entry::setStateMask(States aStateMask) +{ + _stateMask = aStateMask; +} +inline KeyboardTranslator::States KeyboardTranslator::Entry::stateMask() const +{ + return _stateMask; +} +} + +Q_DECLARE_METATYPE(Konsole::KeyboardTranslator::Entry) +Q_DECLARE_METATYPE(const Konsole::KeyboardTranslator*) + +#endif // KEYBOARDTRANSLATOR_H + diff --git a/konsole/src/KeyboardTranslatorManager.cpp b/konsole/src/KeyboardTranslatorManager.cpp new file mode 100644 index 00000000..4d462105 --- /dev/null +++ b/konsole/src/KeyboardTranslatorManager.cpp @@ -0,0 +1,195 @@ +/* + This source file is part of Konsole, a terminal emulator. + + Copyright 2007-2008 by Robert Knight + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +// Own +#include "KeyboardTranslatorManager.h" + +// Qt +#include +#include + +// KDE +#include +#include + +using namespace Konsole; + +KeyboardTranslatorManager::KeyboardTranslatorManager() + : _haveLoadedAll(false) + , _fallbackTranslator(0) +{ + _fallbackTranslator = new FallbackKeyboardTranslator(); +} + +KeyboardTranslatorManager::~KeyboardTranslatorManager() +{ + qDeleteAll(_translators); + delete _fallbackTranslator; +} + +K_GLOBAL_STATIC(KeyboardTranslatorManager , theKeyboardTranslatorManager) +KeyboardTranslatorManager* KeyboardTranslatorManager::instance() +{ + return theKeyboardTranslatorManager; +} + +void KeyboardTranslatorManager::addTranslator(KeyboardTranslator* translator) +{ + _translators.insert(translator->name(), translator); + + if (!saveTranslator(translator)) + kWarning() << "Unable to save translator" << translator->name() + << "to disk."; +} + +bool KeyboardTranslatorManager::deleteTranslator(const QString& name) +{ + Q_ASSERT(_translators.contains(name)); + + // locate and delete + QString path = findTranslatorPath(name); + if (QFile::remove(path)) { + _translators.remove(name); + return true; + } else { + kWarning() << "Failed to remove translator - " << path; + return false; + } +} + +QString KeyboardTranslatorManager::findTranslatorPath(const QString& name) +{ + return KStandardDirs::locate("data", "konsole/" + name + ".keytab"); +} + +void KeyboardTranslatorManager::findTranslators() +{ + QStringList list = KGlobal::dirs()->findAllResources("data", + "konsole/*.keytab", + KStandardDirs::NoDuplicates); + + // add the name of each translator to the list and associated + // the name with a null pointer to indicate that the translator + // has not yet been loaded from disk + foreach(const QString& translatorPath, list) { + QString name = QFileInfo(translatorPath).baseName(); + + if (!_translators.contains(name)) + _translators.insert(name, 0); + } + + _haveLoadedAll = true; +} + +const KeyboardTranslator* KeyboardTranslatorManager::findTranslator(const QString& name) +{ + if (name.isEmpty()) + return defaultTranslator(); + + if (_translators.contains(name) && _translators[name] != 0) + return _translators[name]; + + KeyboardTranslator* translator = loadTranslator(name); + + if (translator != 0) + _translators[name] = translator; + else if (!name.isEmpty()) + kWarning() << "Unable to load translator" << name; + + return translator; +} + +bool KeyboardTranslatorManager::saveTranslator(const KeyboardTranslator* translator) +{ + const QString path = KGlobal::dirs()->saveLocation("data", "konsole/") + translator->name() + + ".keytab"; + + //kDebug() << "Saving translator to" << path; + + QFile destination(path); + if (!destination.open(QIODevice::WriteOnly | QIODevice::Text)) { + kWarning() << "Unable to save keyboard translation:" + << destination.errorString(); + return false; + } + + { + KeyboardTranslatorWriter writer(&destination); + writer.writeHeader(translator->description()); + + foreach(const KeyboardTranslator::Entry& entry, translator->entries()) { + writer.writeEntry(entry); + } + } + + destination.close(); + + return true; +} + +KeyboardTranslator* KeyboardTranslatorManager::loadTranslator(const QString& name) +{ + const QString& path = findTranslatorPath(name); + + QFile source(path); + if (name.isEmpty() || !source.open(QIODevice::ReadOnly | QIODevice::Text)) + return 0; + + return loadTranslator(&source, name); +} + +KeyboardTranslator* KeyboardTranslatorManager::loadTranslator(QIODevice* source, const QString& name) +{ + KeyboardTranslator* translator = new KeyboardTranslator(name); + KeyboardTranslatorReader reader(source); + translator->setDescription(reader.description()); + while (reader.hasNextEntry()) + translator->addEntry(reader.nextEntry()); + + source->close(); + + if (!reader.parseError()) { + return translator; + } else { + delete translator; + return 0; + } +} + +const KeyboardTranslator* KeyboardTranslatorManager::defaultTranslator() +{ + // Try to find the default.keytab file if it exists, otherwise + // fall back to the internal hard-coded fallback translator + const KeyboardTranslator* translator = findTranslator("default"); + if (!translator) { + translator = _fallbackTranslator; + } + return translator; +} + +QStringList KeyboardTranslatorManager::allTranslators() +{ + if (!_haveLoadedAll) { + findTranslators(); + } + + return _translators.keys(); +} diff --git a/konsole/src/KeyboardTranslatorManager.h b/konsole/src/KeyboardTranslatorManager.h new file mode 100644 index 00000000..7adf0c87 --- /dev/null +++ b/konsole/src/KeyboardTranslatorManager.h @@ -0,0 +1,109 @@ +/* + This source file is part of Konsole, a terminal emulator. + + Copyright 2007-2008 by Robert Knight + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +#ifndef KEYBOARDTRANSLATOR_MANAGER_H +#define KEYBOARDTRANSLATOR_MANAGER_H + +// Qt +#include +#include + +// Konsole +#include "konsoleprivate_export.h" +#include "KeyboardTranslator.h" + +#include + +namespace Konsole +{ +/** + * Manages the keyboard translations available for use by terminal sessions, + * see KeyboardTranslator. + */ +class KeyboardTranslatorManager +{ +public: + /** + * Constructs a new KeyboardTranslatorManager and loads the list of + * available keyboard translations. + * + * The keyboard translations themselves are not loaded until they are + * first requested via a call to findTranslator() + */ + KeyboardTranslatorManager(); + ~KeyboardTranslatorManager(); + + /** + * Adds a new translator. If a translator with the same name + * already exists, it will be replaced by the new translator. + * + * TODO: More documentation. + */ + void addTranslator(KeyboardTranslator* translator); + + /** + * Deletes a translator. Returns true on successful deletion or false otherwise. + * + * TODO: More documentation + */ + bool deleteTranslator(const QString& name); + + /** Returns the default translator for Konsole. */ + const KeyboardTranslator* defaultTranslator(); + + /** + * Returns the keyboard translator with the given name or 0 if no translator + * with that name exists. + * + * The first time that a translator with a particular name is requested, + * the on-disk .keytab file is loaded and parsed. + */ + const KeyboardTranslator* findTranslator(const QString& name); + /** + * Returns a list of the names of available keyboard translators. + * + * The first time this is called, a search for available + * translators is started. + */ + QStringList allTranslators(); + + /** Returns the global KeyboardTranslatorManager instance. */ + static KeyboardTranslatorManager* instance(); + +private: + void findTranslators(); // locate all available translators + + // loads the translator with the given name + KeyboardTranslator* loadTranslator(const QString& name); + KeyboardTranslator* loadTranslator(QIODevice* device, const QString& name); + + bool saveTranslator(const KeyboardTranslator* translator); + QString findTranslatorPath(const QString& name); + + bool _haveLoadedAll; + + const KeyboardTranslator* _fallbackTranslator; + QHash _translators; +}; +} + +#endif // KEYBOARDTRANSLATOR_MANAGER_H + diff --git a/konsole/src/LineFont.h b/konsole/src/LineFont.h new file mode 100644 index 00000000..a48f0c51 --- /dev/null +++ b/konsole/src/LineFont.h @@ -0,0 +1,21 @@ +// WARNING: Autogenerated by "fontembedder LineFont.src". +// You probably do not want to hand-edit this! + +static const quint32 LineChars[] = { + 0x00007c00, 0x000fffe0, 0x00421084, 0x00e739ce, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00427000, 0x004e7380, 0x00e77800, 0x00ef7bc0, + 0x00421c00, 0x00439ce0, 0x00e73c00, 0x00e7bde0, 0x00007084, 0x000e7384, 0x000079ce, 0x000f7bce, + 0x00001c84, 0x00039ce4, 0x00003dce, 0x0007bdee, 0x00427084, 0x004e7384, 0x004279ce, 0x00e77884, + 0x00e779ce, 0x004f7bce, 0x00ef7bc4, 0x00ef7bce, 0x00421c84, 0x00439ce4, 0x00423dce, 0x00e73c84, + 0x00e73dce, 0x0047bdee, 0x00e7bde4, 0x00e7bdee, 0x00427c00, 0x0043fce0, 0x004e7f80, 0x004fffe0, + 0x00e77c00, 0x00e7fde0, 0x00ef7fc0, 0x00efffe0, 0x00007c84, 0x0003fce4, 0x000e7f84, 0x000fffe4, + 0x00007dce, 0x0007fdee, 0x000f7fce, 0x000fffee, 0x00427c84, 0x0043fce4, 0x004e7f84, 0x004fffe4, + 0x00427dce, 0x00e77c84, 0x00e77dce, 0x0047fdee, 0x004f7fce, 0x00e7fde4, 0x00ef7fc4, 0x004fffee, + 0x00efffe4, 0x00e7fdee, 0x00ef7fce, 0x00efffee, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x000f83e0, 0x00a5294a, 0x004e1380, 0x00a57800, 0x00ad0bc0, 0x004390e0, 0x00a53c00, 0x00a5a1e0, + 0x000e1384, 0x0000794a, 0x000f0b4a, 0x000390e4, 0x00003d4a, 0x0007a16a, 0x004e1384, 0x00a5694a, + 0x00ad0b4a, 0x004390e4, 0x00a52d4a, 0x00a5a16a, 0x004f83e0, 0x00a57c00, 0x00ad83e0, 0x000f83e4, + 0x00007d4a, 0x000f836a, 0x004f93e4, 0x00a57d4a, 0x00ad836a, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00001c00, 0x00001084, 0x00007000, 0x00421000, + 0x00039ce0, 0x000039ce, 0x000e7380, 0x00e73800, 0x000e7f80, 0x00e73884, 0x0003fce0, 0x004239ce +}; diff --git a/konsole/src/LineFont.src b/konsole/src/LineFont.src new file mode 100644 index 00000000..1b5967df --- /dev/null +++ b/konsole/src/LineFont.src @@ -0,0 +1,786 @@ +#2500: single horizontal line +2500 + + +----- + + + +#2501: triple horizontal line +2501 + +----- +----- +----- + + +#2502: single vertical line +2502 + | + | + | + | + | + +#2503: triple vertical line +2503 + ||| + ||| + ||| + ||| + ||| + +#2504-250B are dashed - not handled + +#250C: top-left corner (lines on bottom + right) +250C + + + .-- + | + | + +#250D: as above, but top line triple-width +250D + + .-- + .-- + |-- + | + +#250E: now the vert line triple-width +250E + + + ..-- + ||| + ||| + +#250F: and now both lines triple-width +250F + + .___ + |.-- + ||._ + ||| + +#2510: top-right corner +2510 + + +--. + | + | + +2511 + +==. +==. +==| + | + +2512 + + +==.. + ||| + ||| + +2513 + +===. +==.| +=.|| + ||| + +#2514: bottom-left corner +2514 + | + | + .== + + + +2515 + | + |== + |== + === + + + +2516 + ||| + ||| + |.== + + + +2517 + ||| + ||.= + |.== + .=== + + +#2518: bottm-right corner +2518 + | + | +==. + + + +2519 + | +==| +==| +=== + + + +251A + ||| + ||| +==== + + + +251B + ||| +=.|| +==.| +===. + + +#251C: Join of vertical line and one from the right +251C + | + | + |== + | + | + +251D + | + |== + |== + |== + | + +251E + ||| + ||| + ||== + | + | + +251F + | + | + ||== + ||| + ||| + + +2520 + ||| + ||| + ||== + ||| + ||| + +2521 + ||| + |||= + ||== + .|== + | + +2522 + | + .|== + ||== + |||= + ||| + +2523 + ||| + ||.= + ||== + ||.= + ||| + +#2524: Join of vertical line and one from the left +2524 + | + | +==| + | + | + +2525 + | +==| +==| +==| + | + +2526 + ||| + ||| +==+| + | + | + +2527 + | + | +==+| + ||| + ||| + +2528 + ||| + ||| +==+| + ||| + ||| + +2529 + ||| +=+|| +==+| +===+ + | + +252A + | +=+|| +==+| +===+ + ||| + +252B + ||| +=+|| +==+| +=+|| + ||| + +#252C: horizontal line joined to from below +252C + + +===== + | + | + +252D + +=== +==|== +==| + | + +252E + + === +==|== + |== + | + +252F + +==+== +==|== +==|== + | + +2530 + + +===== + ||| + ||| + +2531 + +===| +==||= +=||| + ||| + +2532 + + |=== +=||== + ||== + ||| + +2533 + +===== +==|== +=+|+= + ||| + +#2534: bottom line, connected to from top +2534 + | + | +===== + + + +2535 + | +==| +===== +=== + + +2536 + | + |== +===== + === + + +2537 + | +==|== +===== +===== + + +2538 + ||| + ||| +===== + + + +2539 + ||| +==|| +===== +===| + + + +253A + ||| + ||== +=|=== + |=== + + +253B + ||| +==|== +===== +===== + + +#253C: vertical + horizontal lines intersecting +253C + | + | +===== + | + | + +253D + | +==| +===== +==| + | + +253E + | + |== +===== + |== + | + +253F + | +==|== +===== +==|== + | + +2540 + ||| + ||| +===== + | + | + +2541 + | + | +===== + ||| + ||| + +2542 + ||| + ||| +===== + ||| + ||| + +2543 + ||| +=||| +===== +==|+ + | + +2544 + ||| + ||== +===== + ||== + | + +2545 + | +==|+ +===== +=||| + ||| + +2546 + | + ||== +===== + ||== + ||| + +2547 + ||| +=|||= +===== +=|||= + | + +2548 + | +=|||= +===== +=|||= + ||| + +2549 + ||| +=||| +===== +=||| + ||| + +254A + ||| + |||= +===== + |||= + ||| + +254B + ||| +=|||= +===== +=|||= + ||| + +#254C-254F are dashed +2550 + +_____ + +_____ + + +2551 + | | + | | + | | + | | + | | + +2552 + + |-- + | + |-- + | + +2553 + + + ---- + | | + | | + +2554 + + +--- + | + + +- + | | + +2555 + +--+ + | +--+ + | + +2556 + + +-+-+ + | | + | | + +2557 + +---+ + | +-+ | + | | + +2558 + | + +-- + | + +-- + +2559 + | | + | | + +-+- + + + +255A + | | + | +- + | + +--- + + +255B + | +--+ + | +--+ + + +255C + | | + | | +-+-+ + + +255D + | | +-+ | + | +---+ + + +255E + | + +-- + | + +-- + | + +255F + | | + | | + | +- + | | + | | + +2560 + | | + | +- + | + | +- + | | + +2561 + | +--+ + | +--+ + | + +2562 + | | + | | +-+ + + | | + | | + +2563 + | | +-+ | + | +-+ | + | | + +2564 + +----- + +--+-- + | + +2565 + + +-+-+- + | | + | | + +2566 + +----- + +-+ +- + | | + +2567 + | +--+-- + +----- + + +2568 + | | + | | +-+-+- + + + +2569 + | | +-+ +- + +----- + + +256A + | +--+-- + | +--+-- + | + +256B + | | + | | +-+-+- + | | + | | + +256C + | | +-+ +- + +-+ +- + | | + +#256F-2570 are curly, +#2571-2573 are slashes and X + +2574 + + +___ + + + +2575 + | + | + | + + + +2576 + + + ___ + + + +2577 + + + | + | + | + +2578 + +___ +___ +___ + + +2579 + ||| + ||| + ||| + + + +257A + + ___ + ___ + ___ + + +257B + + + ||| + ||| + ||| + +257C + + ___ +_____ + ___ + + +257D + | + | + ||| + ||| + ||| + +257E + +___ +_____ +___ + + +257F + ||| + ||| + ||| + | + | diff --git a/konsole/src/MainWindow.cpp b/konsole/src/MainWindow.cpp new file mode 100644 index 00000000..5a45e5c0 --- /dev/null +++ b/konsole/src/MainWindow.cpp @@ -0,0 +1,836 @@ +/* + Copyright 2006-2008 by Robert Knight + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +// Own +#include "MainWindow.h" + +// Qt +#include +#include + +// KDE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Konsole +#include "BookmarkHandler.h" +#include "SessionController.h" +#include "ProfileList.h" +#include "ManageProfilesDialog.h" +#include "Session.h" +#include "ViewManager.h" +#include "SessionManager.h" +#include "ProfileManager.h" +#include "KonsoleSettings.h" +#include "settings/GeneralSettings.h" +#include "settings/TabBarSettings.h" + +using namespace Konsole; + +/* Normally it would be enough to just have this determined via the window + manager. But there exist GPU drivers (NVIDIA) that have serious performance + issues if transparency is enabled inside Konsole. The rest of the system + works fine. NVIDIA users might want to use --notransparency to work + around such issues. */ +static bool useTransparency() +{ + const KCmdLineArgs* args = KCmdLineArgs::parsedArgs(); + const bool compositingAvailable = KWindowSystem::compositingActive(); + return compositingAvailable && args->isSet("transparency"); +} + +MainWindow::MainWindow() + : KXmlGuiWindow() + , _bookmarkHandler(0) + , _pluggedController(0) + , _menuBarInitialVisibility(true) + , _menuBarInitialVisibilityApplied(false) +{ + if (!KonsoleSettings::saveGeometryOnExit()) { + // If we are not using the global Konsole save geometry on exit, + // remove all Height and Width from [MainWindow] from konsolerc + // Each screen resolution will have entries (Width 1280=619) + KSharedConfigPtr konsoleConfig = KSharedConfig::openConfig("konsolerc"); + KConfigGroup group = konsoleConfig->group("MainWindow"); + QMap configEntries = group.entryMap(); + QMapIterator i(configEntries); + while (i.hasNext()) { + i.next(); + if (i.key().startsWith(QLatin1String("Width")) + || i.key().startsWith(QLatin1String("Height"))) { + group.deleteEntry(i.key()); + } + } + } + + if (useTransparency()) { + // It is useful to have translucent terminal area + setAttribute(Qt::WA_TranslucentBackground, true); + // But it is mostly annoying to have translucent menubar and tabbar + setAttribute(Qt::WA_NoSystemBackground, false); + } + + // create actions for menus + setupActions(); + + // create view manager + _viewManager = new ViewManager(this, actionCollection()); + connect(_viewManager, SIGNAL(empty()), this, SLOT(close())); + connect(_viewManager, SIGNAL(activeViewChanged(SessionController*)), this, + SLOT(activeViewChanged(SessionController*))); + connect(_viewManager, SIGNAL(unplugController(SessionController*)), this, + SLOT(disconnectController(SessionController*))); + connect(_viewManager, SIGNAL(viewPropertiesChanged(QList)), + bookmarkHandler(), SLOT(setViews(QList))); + + connect(_viewManager, SIGNAL(updateWindowIcon()), this, + SLOT(updateWindowIcon())); + connect(_viewManager, SIGNAL(newViewRequest(Profile::Ptr)), + this, SLOT(newFromProfile(Profile::Ptr))); + connect(_viewManager, SIGNAL(newViewRequest()), + this, SLOT(newTab())); + connect(_viewManager, SIGNAL(viewDetached(Session*)), + this, SIGNAL(viewDetached(Session*))); + + // create the main widget + setupMainWidget(); + + // disable automatically generated accelerators in top-level + // menu items - to avoid conflicting with Alt+[Letter] shortcuts + // in terminal applications + KAcceleratorManager::setNoAccel(menuBar()); + + // create menus + createGUI(); + + // remember the original menu accelerators for later use + rememberMenuAccelerators(); + + // replace standard shortcuts which cannot be used in a terminal + // emulator (as they are reserved for use by terminal applications) + correctStandardShortcuts(); + + setProfileList(new ProfileList(true, this)); + + // this must come at the end + applyKonsoleSettings(); + connect(KonsoleSettings::self(), SIGNAL(configChanged()), this, SLOT(applyKonsoleSettings())); +} + +void MainWindow::rememberMenuAccelerators() +{ + foreach(QAction* menuItem, menuBar()->actions()) { + QString itemText = menuItem->text(); + menuItem->setData(itemText); + } +} + +// remove accelerators for standard menu items (eg. &File, &View, &Edit) +// etc. which are defined in kdelibs/kdeui/xmlgui/ui_standards.rc, again, +// to avoid conflicting with Alt+[Letter] terminal shortcuts +// +// TODO - Modify XMLGUI so that it allows the text for standard actions +// defined in ui_standards.rc to be re-defined in the local application +// XMLGUI file (konsoleui.rc in this case) - the text for standard items +// can then be redefined there to exclude the standard accelerators +void MainWindow::removeMenuAccelerators() +{ + foreach(QAction* menuItem, menuBar()->actions()) { + QString itemText = menuItem->text(); + itemText = KGlobal::locale()->removeAcceleratorMarker(itemText); + menuItem->setText(itemText); + } +} + +void MainWindow::restoreMenuAccelerators() +{ + foreach(QAction* menuItem, menuBar()->actions()) { + QString itemText = menuItem->data().toString(); + menuItem->setText(itemText); + } +} + +void MainWindow::correctStandardShortcuts() +{ + // replace F1 shortcut for help contents + QAction* helpAction = actionCollection()->action("help_contents"); + if (helpAction) { + helpAction->setShortcut(QKeySequence()); + } + + // replace Ctrl+B shortcut for bookmarks only if user hasn't already + // changed the shortcut; however, if the user changed it to Ctrl+B + // this will still get changed to Ctrl+Shift+B + QAction* bookmarkAction = actionCollection()->action("add_bookmark"); + if (bookmarkAction && bookmarkAction->shortcut() == QKeySequence(Qt::CTRL + Qt::Key_B)) { + bookmarkAction->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_B)); + } +} + +ViewManager* MainWindow::viewManager() const +{ + return _viewManager; +} + +void MainWindow::disconnectController(SessionController* controller) +{ + disconnect(controller, SIGNAL(titleChanged(ViewProperties*)), + this, SLOT(activeViewTitleChanged(ViewProperties*))); + disconnect(controller, SIGNAL(rawTitleChanged()), + this, SLOT(updateWindowCaption())); + + // KXmlGuiFactory::removeClient() will try to access actions associated + // with the controller internally, which may not be valid after the controller + // itself is no longer valid (after the associated session and or view have + // been destroyed) + if (controller->isValid()) + guiFactory()->removeClient(controller); + + controller->setSearchBar(0); +} + +void MainWindow::activeViewChanged(SessionController* controller) +{ + // associate bookmark menu with current session + bookmarkHandler()->setActiveView(controller); + disconnect(bookmarkHandler(), SIGNAL(openUrl(KUrl)), 0, 0); + connect(bookmarkHandler(), SIGNAL(openUrl(KUrl)), controller, + SLOT(openUrl(KUrl))); + + if (_pluggedController) + disconnectController(_pluggedController); + + Q_ASSERT(controller); + _pluggedController = controller; + + // listen for title changes from the current session + connect(controller, SIGNAL(titleChanged(ViewProperties*)), + this, SLOT(activeViewTitleChanged(ViewProperties*))); + connect(controller, SIGNAL(rawTitleChanged()), + this, SLOT(updateWindowCaption())); + + controller->setShowMenuAction(_toggleMenuBarAction); + + const bool isMenuBarVisible = menuBar()->isVisible(); + guiFactory()->addClient(controller); + menuBar()->setVisible(isMenuBarVisible); + + // set the current session's search bar + controller->setSearchBar(searchBar()); + + // update session title to match newly activated session + activeViewTitleChanged(controller); + + // Update window icon to newly activated session's icon + updateWindowIcon(); +} + +void MainWindow::activeViewTitleChanged(ViewProperties* properties) +{ + Q_UNUSED(properties); + updateWindowCaption(); +} + +void MainWindow::updateWindowCaption() +{ + if (!_pluggedController) + return; + + const QString& title = _pluggedController->title(); + const QString& userTitle = _pluggedController->userTitle(); + + // use tab title as caption by default + QString caption = title; + + // use window title as caption only when enabled and it is not empty + if (KonsoleSettings::showWindowTitleOnTitleBar() && !userTitle.isEmpty()) { + caption = userTitle; + } + + if (KonsoleSettings::showAppNameOnTitleBar()) { + setCaption(caption); + } else { + setWindowTitle(caption); + } +} + +void MainWindow::updateWindowIcon() +{ + if (_pluggedController) + setWindowIcon(_pluggedController->icon()); +} + +IncrementalSearchBar* MainWindow::searchBar() const +{ + return _viewManager->searchBar(); +} + +void MainWindow::setupActions() +{ + KActionCollection* collection = actionCollection(); + KAction* menuAction = 0; + + // File Menu + _newTabMenuAction = new KActionMenu(KIcon("tab-new"), i18nc("@action:inmenu", "&New Tab"), collection); + _newTabMenuAction->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_T)); + _newTabMenuAction->setShortcutConfigurable(true); + _newTabMenuAction->setAutoRepeat(false); + connect(_newTabMenuAction, SIGNAL(triggered()), this, SLOT(newTab())); + collection->addAction("new-tab", _newTabMenuAction); + + menuAction = collection->addAction("clone-tab"); + menuAction->setIcon(KIcon("tab-duplicate")); + menuAction->setText(i18nc("@action:inmenu", "&Clone Tab")); + menuAction->setShortcut(QKeySequence()); + menuAction->setAutoRepeat(false); + connect(menuAction, SIGNAL(triggered()), this, SLOT(cloneTab())); + + menuAction = collection->addAction("new-window"); + menuAction->setIcon(KIcon("window-new")); + menuAction->setText(i18nc("@action:inmenu", "New &Window")); + menuAction->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_N)); + menuAction->setAutoRepeat(false); + connect(menuAction, SIGNAL(triggered()), this, SLOT(newWindow())); + + menuAction = collection->addAction("close-window"); + menuAction->setIcon(KIcon("window-close")); + menuAction->setText(i18nc("@action:inmenu", "Close Window")); + menuAction->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_Q)); + connect(menuAction, SIGNAL(triggered()), this, SLOT(close())); + + // Bookmark Menu + KActionMenu* bookmarkMenu = new KActionMenu(i18nc("@title:menu", "&Bookmarks"), collection); + _bookmarkHandler = new BookmarkHandler(collection, bookmarkMenu->menu(), true, this); + collection->addAction("bookmark", bookmarkMenu); + connect(_bookmarkHandler, SIGNAL(openUrls(QList)), this, SLOT(openUrls(QList))); + + // Settings Menu + _toggleMenuBarAction = KStandardAction::showMenubar(menuBar(), SLOT(setVisible(bool)), collection); + _toggleMenuBarAction->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_M)); + + // Full Screen + menuAction = KStandardAction::fullScreen(this, SLOT(viewFullScreen(bool)), this, collection); + menuAction->setShortcut(QKeySequence(Qt::Key_F11)); + + KStandardAction::configureNotifications(this, SLOT(configureNotifications()), collection); + KStandardAction::keyBindings(this, SLOT(showShortcutsDialog()), collection); + KStandardAction::preferences(this, SLOT(showSettingsDialog()), collection); + + menuAction = collection->addAction("manage-profiles"); + menuAction->setText(i18nc("@action:inmenu", "Manage Profiles...")); + menuAction->setIcon(KIcon("configure")); + connect(menuAction, SIGNAL(triggered()), this, SLOT(showManageProfilesDialog())); + + // Set up an shortcut-only action for activating menu bar. + menuAction = collection->addAction("activate-menu"); + menuAction->setText(i18nc("@item", "Activate Menu")); + menuAction->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_F10)); + connect(menuAction, SIGNAL(triggered()), this, SLOT(activateMenuBar())); +} + +void MainWindow::viewFullScreen(bool fullScreen) +{ + if (fullScreen) + setWindowState(windowState() | Qt::WindowFullScreen); + else + setWindowState(windowState() & ~Qt::WindowFullScreen); +} + +BookmarkHandler* MainWindow::bookmarkHandler() const +{ + return _bookmarkHandler; +} + +void MainWindow::setProfileList(ProfileList* list) +{ + profileListChanged(list->actions()); + + connect(list, SIGNAL(profileSelected(Profile::Ptr)), this, + SLOT(newFromProfile(Profile::Ptr))); + + connect(list, SIGNAL(actionsChanged(QList)), this, + SLOT(profileListChanged(QList))); +} + +void MainWindow::profileListChanged(const QList& sessionActions) +{ + // If only 1 profile is to be shown in the menu, only display + // it if it is the non-default profile. + if (sessionActions.size() > 2) { + // Update the 'New Tab' KActionMenu + KMenu* newTabMenu = _newTabMenuAction->menu(); + newTabMenu->clear(); + foreach(QAction* sessionAction, sessionActions) { + newTabMenu->addAction(sessionAction); + + // NOTE: defaultProfile seems to not work here, sigh. + Profile::Ptr profile = ProfileManager::instance()->defaultProfile(); + if (profile && profile->name() == sessionAction->text().remove('&')) { + sessionAction->setIcon(KIcon(profile->icon(), 0, QStringList("emblem-favorite"))); + newTabMenu->setDefaultAction(sessionAction); + QFont actionFont = sessionAction->font(); + actionFont.setBold(true); + sessionAction->setFont(actionFont); + } + } + } else { + KMenu* newTabMenu = _newTabMenuAction->menu(); + newTabMenu->clear(); + Profile::Ptr profile = ProfileManager::instance()->defaultProfile(); + + // NOTE: Compare names w/o any '&' + if (sessionActions.size() == 2 && sessionActions[1]->text().remove('&') != profile->name()) { + newTabMenu->addAction(sessionActions[1]); + } else { + delete newTabMenu; + } + } +} + +QString MainWindow::activeSessionDir() const +{ + if (_pluggedController) { + if (Session* session = _pluggedController->session()) { + // For new tabs to get the correct working directory, + // force the updating of the currentWorkingDirectory. + session->getDynamicTitle(); + } + return _pluggedController->currentDir(); + } else { + return QString(); + } +} + +void MainWindow::openUrls(const QList& urls) +{ + Profile::Ptr defaultProfile = ProfileManager::instance()->defaultProfile(); + + foreach(const KUrl& url, urls) { + if (url.isLocalFile()) + createSession(defaultProfile, url.path()); + + else if (url.protocol() == "ssh") + createSSHSession(defaultProfile, url); + } +} + +void MainWindow::newTab() +{ + Profile::Ptr defaultProfile = ProfileManager::instance()->defaultProfile(); + createSession(defaultProfile, activeSessionDir()); +} + +void MainWindow::cloneTab() +{ + Q_ASSERT(_pluggedController); + + Session* session = _pluggedController->session(); + Profile::Ptr profile = SessionManager::instance()->sessionProfile(session); + if (profile) { + createSession(profile, activeSessionDir()); + } else { + // something must be wrong: every session should be associated with profile + Q_ASSERT(false); + newTab(); + } +} + +Session* MainWindow::createSession(Profile::Ptr profile, const QString& directory) +{ + if (!profile) + profile = ProfileManager::instance()->defaultProfile(); + + Session* session = SessionManager::instance()->createSession(profile); + + if (!directory.isEmpty() && profile->startInCurrentSessionDir()) + session->setInitialWorkingDirectory(directory); + + session->addEnvironmentEntry(QString("KONSOLE_DBUS_WINDOW=/Windows/%1").arg(_viewManager->managerId())); + + // create view before starting the session process so that the session + // doesn't suffer a change in terminal size right after the session + // starts. Some applications such as GNU Screen and Midnight Commander + // don't like this happening + createView(session); + + return session; +} + +Session* MainWindow::createSSHSession(Profile::Ptr profile, const KUrl& url) +{ + if (!profile) + profile = ProfileManager::instance()->defaultProfile(); + + Session* session = SessionManager::instance()->createSession(profile); + + QString sshCommand = "ssh "; + if (url.port() > -1) { + sshCommand += QString("-p %1 ").arg(url.port()); + } + if (url.hasUser()) { + sshCommand += (url.user() + '@'); + } + if (url.hasHost()) { + sshCommand += url.host(); + } + + session->sendText(sshCommand + '\r'); + + // create view before starting the session process so that the session + // doesn't suffer a change in terminal size right after the session + // starts. some applications such as GNU Screen and Midnight Commander + // don't like this happening + createView(session); + + return session; +} + +void MainWindow::createView(Session* session) +{ + _viewManager->createView(session); +} + +void MainWindow::setFocus() +{ + _viewManager->activeView()->setFocus(); +} + +void MainWindow::newWindow() +{ + Profile::Ptr defaultProfile = ProfileManager::instance()->defaultProfile(); + emit newWindowRequest(defaultProfile, activeSessionDir()); +} + +bool MainWindow::queryClose() +{ + // Do not ask for confirmation during log out and power off + // TODO: rework the dealing of this case to make it has its own confirmation + // dialog. + if (kapp->sessionSaving()) { + return true; + } + + // Check what processes are running, + // if just the default shell is running don't ask for confirmation + + QStringList processesRunning; + foreach(Session *session, _viewManager->sessions()) { + if (!session) + continue; + + const QString defaultProc = session->program().split('/').last(); + const QString currentProc = session->foregroundProcessName().split('/').last(); + + if (currentProc.isEmpty()) + continue; + + if (defaultProc != currentProc) { + processesRunning.append(currentProc); + } + } + if (processesRunning.count() == 0) { + return true; + } + + // NOTE: Some, if not all, of the below KWindowSystem calls are only + // implemented under x11 (KDE4.8 kdelibs/kdeui/windowmanagement). + + // make sure the window is shown on current desktop and is not minimized + KWindowSystem::setOnDesktop(winId(), KWindowSystem::currentDesktop()); + if (isMinimized()) { + KWindowSystem::unminimizeWindow(winId(), true); + } + + int result = KMessageBox::warningYesNoCancelList(this, + i18ncp("@info", "There is a process running in this window. " + "Do you still want to quit?", + "There are %1 processes running in this window. " + "Do you still want to quit?", + processesRunning.count()), + processesRunning, + i18nc("@title", "Confirm Close"), + KGuiItem(i18nc("@action:button", "Close &Window"), "window-close"), + KGuiItem(i18nc("@action:button", "Close Current &Tab"), "tab-close"), + KStandardGuiItem::cancel(), + "CloseAllTabs"); + + switch (result) { + case KMessageBox::Yes: + return true; + case KMessageBox::No: + if (_pluggedController && _pluggedController->session()) { + disconnectController(_pluggedController); + _pluggedController->closeSession(); + } + return false; + case KMessageBox::Cancel: + return false; + } + + return true; +} + +void MainWindow::saveProperties(KConfigGroup& group) +{ + _viewManager->saveSessions(group); +} + +void MainWindow::readProperties(const KConfigGroup& group) +{ + _viewManager->restoreSessions(group); +} + +void MainWindow::saveGlobalProperties(KConfig* config) +{ + SessionManager::instance()->saveSessions(config); +} + +void MainWindow::readGlobalProperties(KConfig* config) +{ + SessionManager::instance()->restoreSessions(config); +} + +void MainWindow::syncActiveShortcuts(KActionCollection* dest, const KActionCollection* source) +{ + foreach(QAction * qAction, source->actions()) { + if (KAction* kAction = qobject_cast(qAction)) { + if (KAction* destKAction = qobject_cast(dest->action(kAction->objectName()))) + destKAction->setShortcut(kAction->shortcut(KAction::ActiveShortcut), KAction::ActiveShortcut); + } + } +} +void MainWindow::showShortcutsDialog() +{ + KShortcutsDialog dialog(KShortcutsEditor::AllActions, KShortcutsEditor::LetterShortcutsDisallowed, this); + + // add actions from this window and the current session controller + foreach(KXMLGUIClient * client, guiFactory()->clients()) { + dialog.addCollection(client->actionCollection()); + } + + if (dialog.configure()) { + // sync shortcuts for non-session actions (defined in "konsoleui.rc") in other main windows + foreach(QWidget* mainWindowWidget, QApplication::topLevelWidgets()) { + MainWindow* mainWindow = qobject_cast(mainWindowWidget); + if (mainWindow && mainWindow != this) + syncActiveShortcuts(mainWindow->actionCollection(), actionCollection()); + } + // sync shortcuts for session actions (defined in "sessionui.rc") in other session controllers. + // Controllers which are currently plugged in (ie. their actions are part of the current menu) + // must be updated immediately via syncActiveShortcuts(). Other controllers will be updated + // when they are plugged into a main window. + foreach(SessionController * controller, SessionController::allControllers()) { + controller->reloadXML(); + if (controller->factory() && controller != _pluggedController) + syncActiveShortcuts(controller->actionCollection(), _pluggedController->actionCollection()); + } + } +} + +void MainWindow::newFromProfile(Profile::Ptr profile) +{ + createSession(profile, activeSessionDir()); +} +void MainWindow::showManageProfilesDialog() +{ + ManageProfilesDialog* dialog = new ManageProfilesDialog(this); + dialog->show(); +} + +void MainWindow::showSettingsDialog() +{ + if (KConfigDialog::showDialog("settings")) + return; + + KConfigDialog* settingsDialog = new KConfigDialog(this, "settings", KonsoleSettings::self()); + settingsDialog->setFaceType(KPageDialog::List); + + GeneralSettings* generalSettings = new GeneralSettings(settingsDialog); + settingsDialog->addPage(generalSettings, + i18nc("@title Preferences page name", "General"), + "utilities-terminal"); + + TabBarSettings* tabBarSettings = new TabBarSettings(settingsDialog); + settingsDialog->addPage(tabBarSettings, + i18nc("@title Preferences page name", "TabBar"), + "system-run"); + + settingsDialog->show(); +} + +void MainWindow::applyKonsoleSettings() +{ + setMenuBarInitialVisibility(KonsoleSettings::showMenuBarByDefault()); + + if (KonsoleSettings::allowMenuAccelerators()) { + restoreMenuAccelerators(); + } else { + removeMenuAccelerators(); + } + + setNavigationVisibility(KonsoleSettings::tabBarVisibility()); + setNavigationPosition(KonsoleSettings::tabBarPosition()); + setNavigationBehavior(KonsoleSettings::newTabBehavior()); + setShowQuickButtons(KonsoleSettings::showQuickButtons()); + + if (KonsoleSettings::tabBarUseUserStyleSheet()) { + setNavigationStyleSheetFromFile(KonsoleSettings::tabBarUserStyleSheetFile()); + } else { + // Apply default values + setNavigationStyleSheet(KonsoleSettings::tabBarStyleSheet()); + } + + setAutoSaveSettings("MainWindow", KonsoleSettings::saveGeometryOnExit()); + + updateWindowCaption(); +} + +void MainWindow::setNavigationVisibility(int visibility) +{ + _viewManager->setNavigationVisibility(visibility); +} + +void MainWindow::setNavigationPosition(int position) +{ + _viewManager->setNavigationPosition(position); +} + +void MainWindow::setNavigationStyleSheet(const QString& styleSheet) +{ + _viewManager->setNavigationStyleSheet(styleSheet); +} + +void MainWindow::setNavigationBehavior(int behavior) +{ + _viewManager->setNavigationBehavior(behavior); +} + +void MainWindow::setNavigationStyleSheetFromFile(const KUrl& styleSheetFile) +{ + // Let's only deal w/ local files for now + if (!styleSheetFile.isLocalFile()) { + setNavigationStyleSheet(KonsoleSettings::tabBarStyleSheet()); + } + + QFile file(styleSheetFile.toLocalFile()); + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + setNavigationStyleSheet(KonsoleSettings::tabBarStyleSheet()); + } + + QString styleSheetText; + QTextStream in(&file); + while (!in.atEnd()) { + styleSheetText.append(in.readLine()); + } + + // Replace current style sheet w/ loaded file + setNavigationStyleSheet(styleSheetText); +} + +void MainWindow::setShowQuickButtons(bool show) +{ + _viewManager->setShowQuickButtons(show); +} + +void MainWindow::activateMenuBar() +{ + const QList menuActions = menuBar()->actions(); + + if (menuActions.isEmpty()) + return; + + // Show menubar if it is hidden at the moment + if (menuBar()->isHidden()) { + menuBar()->setVisible(true); + _toggleMenuBarAction->setChecked(true); + } + + // First menu action should be 'File' + QAction* menuAction = menuActions.first(); + + // TODO: Handle when menubar is top level (MacOS) + menuBar()->setActiveAction(menuAction); +} + +void MainWindow::setupMainWidget() +{ + QWidget* mainWindowWidget = new QWidget(this); + QVBoxLayout* mainWindowLayout = new QVBoxLayout(); + + mainWindowLayout->addWidget(_viewManager->widget()); + mainWindowLayout->setContentsMargins(0, 0, 0, 0); + mainWindowLayout->setSpacing(0); + + mainWindowWidget->setLayout(mainWindowLayout); + + setCentralWidget(mainWindowWidget); +} + +void MainWindow::configureNotifications() +{ + KNotifyConfigWidget::configure(this); +} + +void MainWindow::setMenuBarInitialVisibility(bool visible) +{ + _menuBarInitialVisibility = visible; +} +void MainWindow::showEvent(QShowEvent* aEvent) +{ + // Make sure the 'initial' visibility is applied only once. + if (!_menuBarInitialVisibilityApplied) { + // the initial visibility of menubar should be applied at this last + // moment. Otherwise, the initial visibility will be determined by + // what KMainWindow has automatically stored in konsolerc, but not by + // what users has explicitly configured . + menuBar()->setVisible(_menuBarInitialVisibility); + _toggleMenuBarAction->setChecked(_menuBarInitialVisibility); + _menuBarInitialVisibilityApplied = true; + } + + // Call parent method + KXmlGuiWindow::showEvent(aEvent); +} + +bool MainWindow::focusNextPrevChild(bool) +{ + // In stand-alone konsole, always disable implicit focus switching + // through 'Tab' and 'Shift+Tab' + // + // Kpart is another different story + return false; +} + +#include "moc_MainWindow.cpp" + diff --git a/konsole/src/MainWindow.h b/konsole/src/MainWindow.h new file mode 100644 index 00000000..2b4526ad --- /dev/null +++ b/konsole/src/MainWindow.h @@ -0,0 +1,215 @@ +/* + Copyright 2006-2008 by Robert Knight + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +// Qt +#include + +// KDE +#include +#include + +// Konsole +#include "Profile.h" + +class KAction; +class KActionMenu; +class KToggleAction; + +namespace Konsole +{ +class IncrementalSearchBar; +class ViewManager; +class ViewProperties; +class Session; +class SessionController; +class ProfileList; +class BookmarkHandler; + +/** + * The main window. This contains the menus and an area which contains the terminal displays. + * + * The main window does not create the views or the container widgets which hold the views. + * This is done by the ViewManager class. When a MainWindow is instantiated, it creates + * a new ViewManager. The ViewManager can then be used to create new terminal displays + * inside the window. + * + * Do not construct new main windows directly, use Application's newMainWindow() method. + */ +class MainWindow : public KXmlGuiWindow +{ + Q_OBJECT + +public: + /** + * Constructs a new main window. Do not create new main windows directly, use Application's + * newMainWindow() method instead. + */ + MainWindow(); + + /** + * Returns the view manager associated with this window. The view manager can be used to + * create new views on particular session objects inside this window. + */ + ViewManager* viewManager() const; + + /** + * Create a new session. + * + * @param profile The profile to use to create the new session. + * @param directory Initial working directory for the new session or empty + * if the default working directory associated with the profile should be used. + */ + Session* createSession(Profile::Ptr profile, const QString& directory); + + /** + * create a new SSH session. + * + * @param profile The profile to use to create the new session. + * @param url the URL representing the new SSH connection + */ + Session* createSSHSession(Profile::Ptr profile, const KUrl& url); + + /** + * create view for the specified session + */ + void createView(Session* session); + + /** + * Helper method to make this window get input focus + */ + void setFocus(); + + /** + * Set the initial visibility of the menubar. + */ + void setMenuBarInitialVisibility(bool visible); + + void setNavigationVisibility(int visibility); + void setNavigationPosition(int position); + void setNavigationStyleSheet(const QString& stylesheet); + void setNavigationStyleSheetFromFile(const KUrl& stylesheetfile); + void setNavigationBehavior(int behavior); + void setShowQuickButtons(bool show); + +signals: + + /** + * Emitted by the main window to request the creation of a + * new session in a new window. + * + * @param profile The profile to use to create the + * first session in the new window. + * @param directory Initial working directory for the new window or empty + * if the default working directory associated with the profile should + * be used. + */ + void newWindowRequest(Profile::Ptr profile, + const QString& directory); + + /** + * Emitted when a view for one session is detached from this window + */ + void viewDetached(Session* session); + +protected: + // Reimplemented for internal reasons. + virtual void showEvent(QShowEvent* event); + + // reimplemented from KMainWindow + virtual bool queryClose(); + virtual void saveProperties(KConfigGroup& group); + virtual void readProperties(const KConfigGroup& group); + virtual void saveGlobalProperties(KConfig* config); + virtual void readGlobalProperties(KConfig* config); + + // reimplemented from QWidget + virtual bool focusNextPrevChild(bool next); + +private slots: + void newTab(); + void cloneTab(); + void newWindow(); + void showManageProfilesDialog(); + void activateMenuBar(); + void showSettingsDialog(); + void showShortcutsDialog(); + void newFromProfile(Profile::Ptr profile); + void activeViewChanged(SessionController* controller); + void disconnectController(SessionController* controller); + void activeViewTitleChanged(ViewProperties*); + + void profileListChanged(const QList& actions); + void configureNotifications(); + + void updateWindowIcon(); + void updateWindowCaption(); + + void openUrls(const QList& urls); + + // Sets the list of profiles to be displayed under the "New Tab" action + void setProfileList(ProfileList* list); + + void applyKonsoleSettings(); + +public slots: + void viewFullScreen(bool fullScreen); + +private: + void correctStandardShortcuts(); + void rememberMenuAccelerators(); + void removeMenuAccelerators(); + void restoreMenuAccelerators(); + void setupActions(); + void setupMainWidget(); + QString activeSessionDir() const; + + /** + * Returns the search bar. + * + * This is a convenience method. The search bar is actually owned by + * ViewManager, or more precisely, by ViewContainer. + */ + IncrementalSearchBar* searchBar() const; + + /** + * Returns the bookmark handler associated with this window. + */ + BookmarkHandler* bookmarkHandler() const; + + // sets the active shortcuts of actions in 'dest' to the shortcuts of actions + // with the same name in 'source' (see KAction::ActiveShortcut) + static void syncActiveShortcuts(KActionCollection* dest, const KActionCollection* source); + +private: + ViewManager* _viewManager; + BookmarkHandler* _bookmarkHandler; + KToggleAction* _toggleMenuBarAction; + KActionMenu* _newTabMenuAction; + + QPointer _pluggedController; + + bool _menuBarInitialVisibility; + bool _menuBarInitialVisibilityApplied; +}; +} + +#endif // MAINWINDOW_H diff --git a/konsole/src/ManageProfilesDialog.cpp b/konsole/src/ManageProfilesDialog.cpp new file mode 100644 index 00000000..d3d697ce --- /dev/null +++ b/konsole/src/ManageProfilesDialog.cpp @@ -0,0 +1,544 @@ +/* + Copyright 2007-2008 by Robert Knight + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +// Own +#include "ManageProfilesDialog.h" + +// Qt +#include +#include + +// KDE +#include +#include +#include +#include + +// Konsole +#include "EditProfileDialog.h" +#include "ProfileManager.h" +#include "Session.h" +#include "TerminalDisplay.h" +#include "SessionManager.h" +#include "SessionController.h" +#include "ui_ManageProfilesDialog.h" + +using namespace Konsole; + +ManageProfilesDialog::ManageProfilesDialog(QWidget* aParent) + : KDialog(aParent) + , _sessionModel(new QStandardItemModel(this)) +{ + setCaption(i18nc("@title:window", "Manage Profiles")); + setButtons(KDialog::Close); + + connect(this, SIGNAL(finished()), + ProfileManager::instance(), SLOT(saveSettings())); + + _ui = new Ui::ManageProfilesDialog(); + _ui->setupUi(mainWidget()); + + // hide vertical header + _ui->sessionTable->verticalHeader()->hide(); + _ui->sessionTable->setShowGrid(false); + + _ui->sessionTable->setItemDelegateForColumn(FavoriteStatusColumn, new FavoriteItemDelegate(this)); + _ui->sessionTable->setItemDelegateForColumn(ShortcutColumn, new ShortcutItemDelegate(this)); + _ui->sessionTable->setEditTriggers(_ui->sessionTable->editTriggers() | QAbstractItemView::SelectedClicked); + + // populate the table with profiles + populateTable(); + + // listen for changes to profiles + connect(ProfileManager::instance(), SIGNAL(profileAdded(Profile::Ptr)), this, + SLOT(addItems(Profile::Ptr))); + connect(ProfileManager::instance(), SIGNAL(profileRemoved(Profile::Ptr)), this, + SLOT(removeItems(Profile::Ptr))); + connect(ProfileManager::instance(), SIGNAL(profileChanged(Profile::Ptr)), this, + SLOT(updateItems(Profile::Ptr))); + connect(ProfileManager::instance() , + SIGNAL(favoriteStatusChanged(Profile::Ptr,bool)), this, + SLOT(updateFavoriteStatus(Profile::Ptr,bool))); + + // resize the session table to the full width of the table + _ui->sessionTable->horizontalHeader()->setHighlightSections(false); + _ui->sessionTable->resizeColumnsToContents(); + + // allow a larger width for the shortcut column to account for the + // increased with needed by the shortcut editor compared with just + // displaying the text of the shortcut + _ui->sessionTable->setColumnWidth(ShortcutColumn, + _ui->sessionTable->columnWidth(ShortcutColumn) + 100); + + // setup buttons + connect(_ui->newProfileButton, SIGNAL(clicked()), this, SLOT(createProfile())); + connect(_ui->editProfileButton, SIGNAL(clicked()), this, SLOT(editSelected())); + connect(_ui->deleteProfileButton, SIGNAL(clicked()), this, SLOT(deleteSelected())); + connect(_ui->setAsDefaultButton, SIGNAL(clicked()), this, SLOT(setSelectedAsDefault())); +} + +void ManageProfilesDialog::showEvent(QShowEvent*) +{ + Q_ASSERT(_ui->sessionTable->model()); + + // try to ensure that all the text in all the columns is visible initially. + // FIXME: this is not a good solution, look for a more correct way to do this + + int totalWidth = 0; + const int columnCount = _ui->sessionTable->model()->columnCount(); + + for (int i = 0 ; i < columnCount ; i++) + totalWidth += _ui->sessionTable->columnWidth(i); + + // the margin is added to account for the space taken by the resize grips + // between the columns, this ensures that a horizontal scroll bar is not added + // automatically + int margin = style()->pixelMetric(QStyle::PM_HeaderGripMargin) * columnCount; + _ui->sessionTable->setMinimumWidth(totalWidth + margin); + _ui->sessionTable->horizontalHeader()->setStretchLastSection(true); +} + +ManageProfilesDialog::~ManageProfilesDialog() +{ + delete _ui; +} + +void ManageProfilesDialog::itemDataChanged(QStandardItem* item) +{ + if (item->column() == ShortcutColumn) { + QKeySequence sequence = QKeySequence::fromString(item->text()); + ProfileManager::instance()->setShortcut(item->data(ShortcutRole).value(), + sequence); + } else if (item->column() == ProfileNameColumn) { + QString newName = item->text(); + Profile::Ptr profile = item->data(ProfileKeyRole).value(); + QString oldName = profile->name(); + + if (newName != oldName) { + QHash properties; + properties.insert(Profile::Name, newName); + properties.insert(Profile::UntranslatedName, newName); + + ProfileManager::instance()->changeProfile(profile, properties); + } + } +} + +int ManageProfilesDialog::rowForProfile(const Profile::Ptr profile) const +{ + const int rowCount = _sessionModel->rowCount(); + for (int i = 0; i < rowCount; i++) { + if (_sessionModel->item(i, ProfileNameColumn)->data(ProfileKeyRole) + .value() == profile) { + return i; + } + } + return -1; +} +void ManageProfilesDialog::removeItems(const Profile::Ptr profile) +{ + int row = rowForProfile(profile); + if (row < 0) + return; + + _sessionModel->removeRow(row); +} +void ManageProfilesDialog::updateItems(const Profile::Ptr profile) +{ + const int row = rowForProfile(profile); + if (row < 0) + return; + + QList items; + items << _sessionModel->item(row, ProfileNameColumn); + items << _sessionModel->item(row, FavoriteStatusColumn); + items << _sessionModel->item(row, ShortcutColumn); + + updateItemsForProfile(profile, items); +} +void ManageProfilesDialog::updateItemsForProfile(const Profile::Ptr profile, QList& items) const +{ + // Profile Name + items[ProfileNameColumn]->setText(profile->name()); + if (!profile->icon().isEmpty()) + items[ProfileNameColumn]->setIcon(KIcon(profile->icon())); + items[ProfileNameColumn]->setData(QVariant::fromValue(profile), ProfileKeyRole); + items[ProfileNameColumn]->setToolTip(i18nc("@info:tooltip", "Click to rename profile")); + + // Favorite Status + const bool isFavorite = ProfileManager::instance()->findFavorites().contains(profile); + if (isFavorite) + items[FavoriteStatusColumn]->setData(KIcon("dialog-ok-apply"), Qt::DecorationRole); + else + items[FavoriteStatusColumn]->setData(KIcon(), Qt::DecorationRole); + items[FavoriteStatusColumn]->setData(QVariant::fromValue(profile), ProfileKeyRole); + items[FavoriteStatusColumn]->setToolTip(i18nc("@info:tooltip", "Click to toggle status")); + + // Shortcut + QString shortcut = ProfileManager::instance()->shortcut(profile).toString(); + items[ShortcutColumn]->setText(shortcut); + items[ShortcutColumn]->setData(QVariant::fromValue(profile), ShortcutRole); + items[ShortcutColumn]->setToolTip(i18nc("@info:tooltip", "Double click to change shortcut")); +} +void ManageProfilesDialog::addItems(const Profile::Ptr profile) +{ + if (profile->isHidden()) + return; + + QList items; + for (int i = 0; i < 3; i++) + items << new QStandardItem; + + updateItemsForProfile(profile, items); + _sessionModel->appendRow(items); +} +void ManageProfilesDialog::populateTable() +{ + Q_ASSERT(!_ui->sessionTable->model()); + + _ui->sessionTable->setModel(_sessionModel); + + _sessionModel->clear(); + // setup session table + _sessionModel->setHorizontalHeaderLabels(QStringList() << i18nc("@title:column Profile label", "Name") + << i18nc("@title:column Display profile in file menu", "Show in Menu") + << i18nc("@title:column Profile shortcut text", "Shortcut")); + + QList profiles = ProfileManager::instance()->allProfiles(); + ProfileManager::instance()->sortProfiles(profiles); + + foreach(const Profile::Ptr& profile, profiles) { + addItems(profile); + } + updateDefaultItem(); + + connect(_sessionModel, SIGNAL(itemChanged(QStandardItem*)), this, + SLOT(itemDataChanged(QStandardItem*))); + + // listen for changes in the table selection and update the state of the form's buttons + // accordingly. + // + // it appears that the selection model is changed when the model itself is replaced, + // so the signals need to be reconnected each time the model is updated. + connect(_ui->sessionTable->selectionModel(), + SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, + SLOT(tableSelectionChanged(QItemSelection))); + + _ui->sessionTable->selectRow(0); +} +void ManageProfilesDialog::updateDefaultItem() +{ + Profile::Ptr defaultProfile = ProfileManager::instance()->defaultProfile(); + + const int rowCount = _sessionModel->rowCount(); + for (int i = 0; i < rowCount; i++) { + QStandardItem* item = _sessionModel->item(i); + QFont itemFont = item->font(); + + bool isDefault = (defaultProfile == item->data().value()); + + if (isDefault && !itemFont.bold()) { + item->setIcon(KIcon(defaultProfile->icon(), 0, QStringList("emblem-favorite"))); + itemFont.setBold(true); + item->setFont(itemFont); + } else if (!isDefault && itemFont.bold()) { + item->setIcon(KIcon(defaultProfile->icon())); + itemFont.setBold(false); + item->setFont(itemFont); + } + } +} +void ManageProfilesDialog::tableSelectionChanged(const QItemSelection&) +{ + const int selectedRows = _ui->sessionTable->selectionModel()->selectedRows().count(); + const ProfileManager* manager = ProfileManager::instance(); + const bool isNotDefault = (selectedRows > 0) && currentProfile() != manager->defaultProfile(); + const bool isDeletable = (selectedRows > 1) || + (selectedRows == 1 && isProfileDeletable(currentProfile())); + + _ui->newProfileButton->setEnabled(selectedRows < 2); + // FIXME: At some point editing 2+ profiles no longer works + _ui->editProfileButton->setEnabled(selectedRows == 1); + // do not allow the default session type to be removed + _ui->deleteProfileButton->setEnabled(isDeletable && isNotDefault); + _ui->setAsDefaultButton->setEnabled(isNotDefault && (selectedRows < 2)); +} +void ManageProfilesDialog::deleteSelected() +{ + foreach(const Profile::Ptr & profile, selectedProfiles()) { + if (profile != ProfileManager::instance()->defaultProfile()) + ProfileManager::instance()->deleteProfile(profile); + } +} +void ManageProfilesDialog::setSelectedAsDefault() +{ + ProfileManager::instance()->setDefaultProfile(currentProfile()); + // do not allow the new default session type to be removed + _ui->deleteProfileButton->setEnabled(false); + _ui->setAsDefaultButton->setEnabled(false); + + // update font of new default item + updateDefaultItem(); +} + +void ManageProfilesDialog::moveUpSelected() +{ + Q_ASSERT(_sessionModel); + + const int rowIndex = _ui->sessionTable->currentIndex().row(); + const QListitems = _sessionModel->takeRow(rowIndex); + _sessionModel->insertRow(rowIndex - 1, items); + _ui->sessionTable->selectRow(rowIndex - 1); +} + +void ManageProfilesDialog::moveDownSelected() +{ + Q_ASSERT(_sessionModel); + + const int rowIndex = _ui->sessionTable->currentIndex().row(); + const QListitems = _sessionModel->takeRow(rowIndex); + _sessionModel->insertRow(rowIndex + 1, items); + _ui->sessionTable->selectRow(rowIndex + 1); +} + +void ManageProfilesDialog::createProfile() +{ + // setup a temporary profile which is a clone of the selected profile + // or the default if no profile is selected + Profile::Ptr sourceProfile; + + Profile::Ptr selectedProfile = currentProfile(); + if (!selectedProfile) + sourceProfile = ProfileManager::instance()->defaultProfile(); + else + sourceProfile = selectedProfile; + + Q_ASSERT(sourceProfile); + + Profile::Ptr newProfile = Profile::Ptr(new Profile(ProfileManager::instance()->fallbackProfile())); + newProfile->clone(sourceProfile, true); + newProfile->setProperty(Profile::Name, i18nc("@item This will be used as part of the file name", "New Profile")); + newProfile->setProperty(Profile::UntranslatedName, "New Profile"); + newProfile->setProperty(Profile::MenuIndex, QString("0")); + + QWeakPointer dialog = new EditProfileDialog(this); + dialog.data()->setProfile(newProfile); + dialog.data()->selectProfileName(); + + if (dialog.data()->exec() == QDialog::Accepted) { + ProfileManager::instance()->addProfile(newProfile); + ProfileManager::instance()->setFavorite(newProfile, true); + ProfileManager::instance()->changeProfile(newProfile, newProfile->setProperties()); + } + delete dialog.data(); +} +void ManageProfilesDialog::editSelected() +{ + QList profiles(selectedProfiles()); + + foreach (Session* session, SessionManager::instance()->sessions()) { + foreach (TerminalDisplay* terminal, session->views()) { + // Searching for opened profiles + if (terminal->sessionController()->profileDialogPointer() != NULL) { + foreach (const Profile::Ptr & profile, profiles) { + if (profile->name() == terminal->sessionController()->profileDialogPointer()->lookupProfile()->name() + && terminal->sessionController()->profileDialogPointer()->isVisible()) { + // close opened edit dialog + terminal->sessionController()->profileDialogPointer()->close(); + } + } + } + } + } + + EditProfileDialog dialog(this); + // the dialog will delete the profile group when it is destroyed + ProfileGroup* group = new ProfileGroup; + foreach (const Profile::Ptr & profile, profiles) { + group->addProfile(profile); + } + group->updateValues(); + + dialog.setProfile(Profile::Ptr(group)); + dialog.exec(); +} +QList ManageProfilesDialog::selectedProfiles() const +{ + QList list; + QItemSelectionModel* selection = _ui->sessionTable->selectionModel(); + if (!selection) + return list; + + foreach(const QModelIndex & index, selection->selectedIndexes()) { + if (index.column() == ProfileNameColumn) + list << index.data(ProfileKeyRole).value(); + } + + return list; +} +Profile::Ptr ManageProfilesDialog::currentProfile() const +{ + QItemSelectionModel* selection = _ui->sessionTable->selectionModel(); + + if (!selection || selection->selectedRows().count() != 1) + return Profile::Ptr(); + + return selection-> + selectedIndexes().first().data(ProfileKeyRole).value(); +} +bool ManageProfilesDialog::isProfileDeletable(Profile::Ptr profile) const +{ + static const QString systemDataLocation = KStandardDirs::installPath("data") + "konsole/"; + + if (profile) { + QFileInfo fileInfo(profile->path()); + + if (fileInfo.exists()) { + // never remove a system wide profile, no matter whether the + // current user has enough permission + if (profile->path().startsWith(systemDataLocation)) { + return false; + } + + // check whether user has enough permission + QFileInfo dirInfo(fileInfo.path()); + return dirInfo.isWritable(); + } else { + return true; + } + } else { + return true; + } +} +void ManageProfilesDialog::updateFavoriteStatus(Profile::Ptr profile, bool favorite) +{ + Q_ASSERT(_sessionModel); + + const int rowCount = _sessionModel->rowCount(); + for (int i = 0; i < rowCount; i++) { + QModelIndex index = _sessionModel->index(i, FavoriteStatusColumn); + if (index.data(ProfileKeyRole).value() == profile) { + const KIcon icon = favorite ? KIcon("dialog-ok-apply") : KIcon(); + _sessionModel->setData(index, icon, Qt::DecorationRole); + } + } +} +void ManageProfilesDialog::setShortcutEditorVisible(bool visible) +{ + _ui->sessionTable->setColumnHidden(ShortcutColumn, !visible); +} +void StyledBackgroundPainter::drawBackground(QPainter* painter, const QStyleOptionViewItem& option, + const QModelIndex&) +{ + const QStyleOptionViewItemV3* v3option = qstyleoption_cast(&option); + const QWidget* widget = v3option ? v3option->widget : 0; + + QStyle* style = widget ? widget->style() : QApplication::style(); + + style->drawPrimitive(QStyle::PE_PanelItemViewItem, &option, painter, widget); +} + +FavoriteItemDelegate::FavoriteItemDelegate(QObject* aParent) + : QStyledItemDelegate(aParent) +{ +} +void FavoriteItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const +{ + // See implementation of QStyledItemDelegate::paint() + QStyleOptionViewItemV4 opt = option; + initStyleOption(&opt, index); + + StyledBackgroundPainter::drawBackground(painter, opt, index); + + int margin = (opt.rect.height() - opt.decorationSize.height()) / 2; + margin++; + + opt.rect.setTop(opt.rect.top() + margin); + opt.rect.setBottom(opt.rect.bottom() - margin); + + QIcon icon = index.data(Qt::DecorationRole).value(); + icon.paint(painter, opt.rect, Qt::AlignCenter); +} + +bool FavoriteItemDelegate::editorEvent(QEvent* aEvent, QAbstractItemModel*, + const QStyleOptionViewItem&, const QModelIndex& index) +{ + if (aEvent->type() == QEvent::MouseButtonPress || + aEvent->type() == QEvent::KeyPress || + aEvent->type() == QEvent::MouseButtonDblClick) { + Profile::Ptr profile = index.data(ManageProfilesDialog::ProfileKeyRole).value(); + const bool isFavorite = ProfileManager::instance()->findFavorites().contains(profile); + + ProfileManager::instance()->setFavorite(profile, !isFavorite); + } + + return true; +} +ShortcutItemDelegate::ShortcutItemDelegate(QObject* aParent) + : QStyledItemDelegate(aParent) +{ +} +void ShortcutItemDelegate::editorModified(const QKeySequence& keys) +{ + Q_UNUSED(keys); + //kDebug() << keys.toString(); + + KKeySequenceWidget* editor = qobject_cast(sender()); + Q_ASSERT(editor); + _modifiedEditors.insert(editor); + emit commitData(editor); + emit closeEditor(editor); +} +void ShortcutItemDelegate::setModelData(QWidget* editor, QAbstractItemModel* model, + const QModelIndex& index) const +{ + _itemsBeingEdited.remove(index); + + if (!_modifiedEditors.contains(editor)) + return; + + QString shortcut = qobject_cast(editor)->keySequence().toString(); + model->setData(index, shortcut, Qt::DisplayRole); + + _modifiedEditors.remove(editor); +} + +QWidget* ShortcutItemDelegate::createEditor(QWidget* aParent, const QStyleOptionViewItem&, const QModelIndex& index) const +{ + _itemsBeingEdited.insert(index); + + KKeySequenceWidget* editor = new KKeySequenceWidget(aParent); + editor->setFocusPolicy(Qt::StrongFocus); + editor->setModifierlessAllowed(false); + QString shortcutString = index.data(Qt::DisplayRole).toString(); + editor->setKeySequence(QKeySequence::fromString(shortcutString)); + connect(editor, SIGNAL(keySequenceChanged(QKeySequence)), this, SLOT(editorModified(QKeySequence))); + editor->captureKeySequence(); + return editor; +} +void ShortcutItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, + const QModelIndex& index) const +{ + if (_itemsBeingEdited.contains(index)) + StyledBackgroundPainter::drawBackground(painter, option, index); + else + QStyledItemDelegate::paint(painter, option, index); +} + +#include "moc_ManageProfilesDialog.cpp" diff --git a/konsole/src/ManageProfilesDialog.h b/konsole/src/ManageProfilesDialog.h new file mode 100644 index 00000000..60016804 --- /dev/null +++ b/konsole/src/ManageProfilesDialog.h @@ -0,0 +1,158 @@ +/* + Copyright 2007-2008 by Robert Knight + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +#ifndef MANAGEPROFILESDIALOG_H +#define MANAGEPROFILESDIALOG_H + +// Qt +#include +#include +#include +#include +#include +#include + +// KDE +#include + +// Konsole +#include "Profile.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class ManageProfilesDialog; } +QT_END_NAMESPACE + +namespace Konsole +{ +/** + * A dialog which lists the available types of profiles and allows + * the user to add new profiles, and remove or edit existing + * profile types. + */ +class KONSOLEPRIVATE_EXPORT ManageProfilesDialog : public KDialog +{ + Q_OBJECT + + friend class FavoriteItemDelegate; + friend class ShortcutItemDelegate; + +public: + /** Constructs a new profile type with the specified parent. */ + explicit ManageProfilesDialog(QWidget* parent = 0); + virtual ~ManageProfilesDialog(); + + /** + * Specifies whether the shortcut editor should be show. + * The shortcut editor allows shortcuts to be associated with + * profiles. When a shortcut is changed, the dialog will call + * SessionManager::instance()->setShortcut() to update the shortcut + * associated with the profile. + * + * By default the editor is visible. + */ + void setShortcutEditorVisible(bool visible); + +protected: + virtual void showEvent(QShowEvent* event); + +private slots: + void deleteSelected(); + void setSelectedAsDefault(); + void createProfile(); + void editSelected(); + void moveUpSelected(); + void moveDownSelected(); + + void itemDataChanged(QStandardItem* item); + + // enables or disables Edit/Delete/Set as Default buttons when the + // selection changes + void tableSelectionChanged(const QItemSelection&); + + void updateFavoriteStatus(Profile::Ptr profile, bool favorite); + + void addItems(const Profile::Ptr); + void updateItems(const Profile::Ptr); + void removeItems(const Profile::Ptr); + +private: + Profile::Ptr currentProfile() const; + QList selectedProfiles() const; + bool isProfileDeletable(Profile::Ptr profile) const; + + // updates the font of the items to match + // their default / non-default profile status + void updateDefaultItem(); + void updateItemsForProfile(const Profile::Ptr profile, QList& items) const; + // updates the profile table to be in sync with the + // session manager + void populateTable(); + int rowForProfile(const Profile::Ptr profile) const; + + Ui::ManageProfilesDialog* _ui; + QStandardItemModel* _sessionModel; + + static const int ProfileNameColumn = 0; + static const int FavoriteStatusColumn = 1; + static const int ShortcutColumn = 2; + static const int ProfileKeyRole = Qt::UserRole + 1; + static const int ShortcutRole = Qt::UserRole + 1; +}; + +class StyledBackgroundPainter +{ +public: + static void drawBackground(QPainter* painter, const QStyleOptionViewItem& option, + const QModelIndex& index); +}; + +class FavoriteItemDelegate : public QStyledItemDelegate +{ +public: + explicit FavoriteItemDelegate(QObject* parent = 0); + + virtual bool editorEvent(QEvent* event, QAbstractItemModel* model, + const QStyleOptionViewItem& option, const QModelIndex& index); + virtual void paint(QPainter* painter, const QStyleOptionViewItem& option, + const QModelIndex& index) const; +}; + +class ShortcutItemDelegate : public QStyledItemDelegate +{ + Q_OBJECT + +public: + explicit ShortcutItemDelegate(QObject* parent = 0); + + virtual void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const; + virtual QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, + const QModelIndex& index) const; + virtual void paint(QPainter* painter, const QStyleOptionViewItem& option, + const QModelIndex& index) const; + +private slots: + void editorModified(const QKeySequence& keys); + +private: + mutable QSet _modifiedEditors; + mutable QSet _itemsBeingEdited; +}; +} +#endif // MANAGEPROFILESDIALOG_H + diff --git a/konsole/src/ManageProfilesDialog.ui b/konsole/src/ManageProfilesDialog.ui new file mode 100644 index 00000000..777ef230 --- /dev/null +++ b/konsole/src/ManageProfilesDialog.ui @@ -0,0 +1,105 @@ + + + ManageProfilesDialog + + + + 0 + 0 + 645 + 315 + + + + + 0 + + + + + Create a new profile based upon the selected profile + + + &New Profile... + + + + + + + false + + + Edit the selected profile(s) + + + &Edit Profile... + + + + + + + false + + + Delete the selected profile(s) + + + &Delete Profile + + + + + + + false + + + Set the selected profile as the default for new terminal sessions + + + &Set as Default + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + true + + + QAbstractItemView::ExtendedSelection + + + QAbstractItemView::SelectRows + + + QAbstractItemView::ScrollPerPixel + + + QAbstractItemView::ScrollPerPixel + + + false + + + + + + + + diff --git a/konsole/src/Messages.sh b/konsole/src/Messages.sh new file mode 100644 index 00000000..e37338ee --- /dev/null +++ b/konsole/src/Messages.sh @@ -0,0 +1,16 @@ +#! /bin/sh +rm -f schemas.cpp +#(cd ../desktop && $PREPARETIPS > ../src/tips.cpp) +for i in ../data/color-schemes/*.colorscheme; do +grep "^Description=" $i | sed -e 's#^Description=\(.*\)$#i18n(\"\1\")#' >> schemas.cpp +done +for i in ../data/keyboard-layouts/*.keytab; do +grep "^keyboard" $i | sed -e 's#^keyboard \"\(.*\)\"$#i18n(\"\1\")#' >> schemas.cpp +done +$EXTRACTRC `find . -name \*.ui` >> rc.cpp +$EXTRACTRC `find . -name \*.kcfg` >> rc.cpp +$EXTRACTRC `find ../desktop -name \*.rc` >> rc.cpp +$XGETTEXT *.cpp -o $podir/konsole.pot +rm -f schemas.cpp +#rm -f tips.cpp +rm -f rc.cpp diff --git a/konsole/src/Part.cpp b/konsole/src/Part.cpp new file mode 100644 index 00000000..cb3f47fb --- /dev/null +++ b/konsole/src/Part.cpp @@ -0,0 +1,388 @@ +/* + Copyright 2007-2008 by Robert Knight + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +// Own +#include "Part.h" + +// Qt +#include +#include +#include + +// KDE +#include +#include +#include +#include +#include + +// Konsole +#include "EditProfileDialog.h" +#include "Emulation.h" +#include "ManageProfilesDialog.h" +#include "Session.h" +#include "SessionController.h" +#include "SessionManager.h" +#include "ProfileManager.h" +#include "TerminalDisplay.h" +#include "ViewManager.h" + +using namespace Konsole; + +K_PLUGIN_FACTORY(KonsolePartFactory, registerPlugin();) +K_EXPORT_PLUGIN(KonsolePartFactory("konsole")) + +Part::Part(QWidget* parentWidget , QObject* parent, const QVariantList&) + : KParts::ReadOnlyPart(parent) + , _viewManager(0) + , _pluggedController(0) + , _manageProfilesAction(0) +{ + // make sure the konsole catalog is loaded + KGlobal::locale()->insertCatalog("konsole"); + // make sure the libkonq catalog is loaded( needed for drag & drop ) + KGlobal::locale()->insertCatalog("libkonq"); + + // setup global actions + createGlobalActions(); + + // create view widget + _viewManager = new ViewManager(this, actionCollection()); + _viewManager->setNavigationMethod(ViewManager::NoNavigation); + + connect(_viewManager, SIGNAL(activeViewChanged(SessionController*)), this , + SLOT(activeViewChanged(SessionController*))); + connect(_viewManager, SIGNAL(empty()), this, SLOT(terminalExited())); + connect(_viewManager, SIGNAL(newViewRequest()), this, SLOT(newTab())); + + _viewManager->widget()->setParent(parentWidget); + + setWidget(_viewManager->widget()); + actionCollection()->addAssociatedWidget(_viewManager->widget()); + foreach(QAction* action, actionCollection()->actions()) { + action->setShortcutContext(Qt::WidgetWithChildrenShortcut); + } + + // Enable translucency support. + _viewManager->widget()->setAttribute(Qt::WA_TranslucentBackground, true); + + // create basic session + createSession(); +} + +Part::~Part() +{ + ProfileManager::instance()->saveSettings(); +} + +void Part::createGlobalActions() +{ + _manageProfilesAction = new KAction(i18n("Manage Profiles..."), this); + connect(_manageProfilesAction, SIGNAL(triggered()), this, SLOT(showManageProfilesDialog())); +} + +void Part::setupActionsForSession(SessionController* controller) +{ + KActionCollection* collection = controller->actionCollection(); + collection->addAction("manage-profiles", _manageProfilesAction); +} + +bool Part::openFile() +{ + return false; +} + +void Part::terminalExited() +{ + deleteLater(); +} + +void Part::newTab() +{ + createSession(); +} + +Session* Part::activeSession() const +{ + if (_viewManager->activeViewController()) { + Q_ASSERT(_viewManager->activeViewController()->session()); + + return _viewManager->activeViewController()->session(); + } else { + return 0; + } +} +void Part::startProgram(const QString& program, + const QStringList& arguments) +{ + Q_ASSERT(activeSession()); + + // do nothing if the session has already started running + if (activeSession()->isRunning()) + return; + + if (!program.isEmpty() && !arguments.isEmpty()) { + activeSession()->setProgram(program); + activeSession()->setArguments(arguments); + } + + activeSession()->run(); +} + +void Part::openTeletype(int fd) +{ + Q_ASSERT(activeSession()); + + activeSession()->openTeletype(fd); +} + +void Part::showShellInDir(const QString& dir) +{ + Q_ASSERT(activeSession()); + + // do nothing if the session has already started running + if (activeSession()->isRunning()) + return; + + if (!dir.isEmpty()) + activeSession()->setInitialWorkingDirectory(dir); + + activeSession()->run(); +} + +void Part::sendInput(const QString& text) +{ + Q_ASSERT(activeSession()); + activeSession()->sendText(text); +} + +int Part::terminalProcessId() +{ + Q_ASSERT(activeSession()); + + return activeSession()->processId(); +} + +int Part::foregroundProcessId() +{ + Q_ASSERT(activeSession()); + + if (activeSession()->isForegroundProcessActive()) { + return activeSession()->foregroundProcessId(); + } else { + return -1; + } +} + +QString Part::foregroundProcessName() +{ + Q_ASSERT(activeSession()); + + if (activeSession()->isForegroundProcessActive()) { + return activeSession()->foregroundProcessName(); + } else { + return ""; + } +} + +QString Part::currentWorkingDirectory() const +{ + Q_ASSERT(activeSession()); + + return activeSession()->currentWorkingDirectory(); +} + +void Part::createSession(const QString& profileName, const QString& directory) +{ + Profile::Ptr profile = ProfileManager::instance()->defaultProfile(); + if (!profileName.isEmpty()) + profile = ProfileManager::instance()->loadProfile(profileName); + + Q_ASSERT(profile); + + Session* session = SessionManager::instance()->createSession(profile); + + // override the default directory specified in the profile + if (!directory.isEmpty() && profile->startInCurrentSessionDir()) + session->setInitialWorkingDirectory(directory); + + _viewManager->createView(session); +} + +QStringList Part::profileNameList() const +{ + return ProfileManager::instance()->availableProfileNames(); +} + +void Part::activeViewChanged(SessionController* controller) +{ + Q_ASSERT(controller); + Q_ASSERT(controller->view()); + + // remove existing controller + if (_pluggedController) { + removeChildClient(_pluggedController); + disconnect(_pluggedController, SIGNAL(titleChanged(ViewProperties*)), this, + SLOT(activeViewTitleChanged(ViewProperties*))); + disconnect(_pluggedController, SIGNAL(currentDirectoryChanged(QString)), this, + SIGNAL(currentDirectoryChanged(QString))); + } + + // insert new controller + insertChildClient(controller); + setupActionsForSession(controller); + + connect(controller, SIGNAL(titleChanged(ViewProperties*)), this, + SLOT(activeViewTitleChanged(ViewProperties*))); + activeViewTitleChanged(controller); + connect(controller, SIGNAL(currentDirectoryChanged(QString)), this, + SIGNAL(currentDirectoryChanged(QString))); + + const char* displaySignal = SIGNAL(overrideShortcutCheck(QKeyEvent*,bool&)); + const char* partSlot = SLOT(overrideTerminalShortcut(QKeyEvent*,bool&)); + + disconnect(controller->view(), displaySignal, this, partSlot); + connect(controller->view(), displaySignal, this, partSlot); + + // set the current session's search bar + controller->setSearchBar(_viewManager->searchBar()); + + _pluggedController = controller; +} + +void Part::overrideTerminalShortcut(QKeyEvent* event, bool& override) +{ + // Shift+Insert is commonly used as the alternate shortcut for + // pasting in KDE apps(including konsole), so it deserves some + // special treatment. + if ((event->modifiers() & Qt::ShiftModifier) && + (event->key() == Qt::Key_Insert)) { + override = false; + return; + } + + // override all shortcuts in the embedded terminal by default + override = true; + emit overrideShortcut(event, override); +} + +void Part::activeViewTitleChanged(ViewProperties* properties) +{ + emit setWindowCaption(properties->title()); +} + +void Part::showManageProfilesDialog() +{ + showManageProfilesDialog(_viewManager->widget()); +} + +void Part::showManageProfilesDialog(QWidget* parent) +{ + ManageProfilesDialog* dialog = new ManageProfilesDialog(parent); + dialog->setAttribute(Qt::WA_DeleteOnClose); + dialog->setShortcutEditorVisible(false); + dialog->show(); +} + +void Part::showEditCurrentProfileDialog(QWidget* parent) +{ + Q_ASSERT(activeSession()); + + EditProfileDialog* dialog = new EditProfileDialog(parent); + dialog->setAttribute(Qt::WA_DeleteOnClose); + dialog->setProfile(SessionManager::instance()->sessionProfile(activeSession())); + dialog->show(); +} + +void Part::changeSessionSettings(const QString& text) +{ + Q_ASSERT(activeSession()); + + // send a profile change command, the escape code format + // is the same as the normal X-Term commands used to change the window title or icon, + // but with a magic value of '50' for the parameter which specifies what to change + QString command = QString("\033]50;%1\a").arg(text); + + sendInput(command); +} + +// Konqueror integration +bool Part::openUrl(const KUrl& aUrl) +{ + if (url() == aUrl) { + emit completed(); + return true; + } + + setUrl(aUrl); + emit setWindowCaption(aUrl.pathOrUrl()); + //kdDebug() << "Set Window Caption to " << url.pathOrUrl(); + emit started(0); + + if (aUrl.isLocalFile() /*&& b_openUrls*/) { + KDE_struct_stat buff; + if (KDE::stat(QFile::encodeName(aUrl.path()), &buff) == 0) { + QString text = (S_ISDIR(buff.st_mode) ? aUrl.path() : aUrl.directory()); + showShellInDir(text); + } else { + showShellInDir(QDir::homePath()); + } + } else { + showShellInDir(QDir::homePath()); + } + + emit completed(); + return true; +} + +void Part::setMonitorSilenceEnabled(bool enabled) +{ + Q_ASSERT(activeSession()); + + if (enabled) { + activeSession()->setMonitorSilence(true); + connect(activeSession(), SIGNAL(stateChanged(int)), this, SLOT(sessionStateChanged(int)), Qt::UniqueConnection); + } else { + activeSession()->setMonitorSilence(false); + disconnect(activeSession(), SIGNAL(stateChanged(int)), this, SLOT(sessionStateChanged(int))); + } +} + +void Part::setMonitorActivityEnabled(bool enabled) +{ + Q_ASSERT(activeSession()); + + if (enabled) { + activeSession()->setMonitorActivity(true); + connect(activeSession(), SIGNAL(stateChanged(int)), this, SLOT(sessionStateChanged(int)), Qt::UniqueConnection); + } else { + activeSession()->setMonitorActivity(false); + disconnect(activeSession(), SIGNAL(stateChanged(int)), this, SLOT(sessionStateChanged(int))); + } +} + +void Part::sessionStateChanged(int state) +{ + if (state == NOTIFYSILENCE) + emit silenceDetected(); + else if (state == NOTIFYACTIVITY) + emit activityDetected(); +} + +#include "moc_Part.cpp" diff --git a/konsole/src/Part.h b/konsole/src/Part.h new file mode 100644 index 00000000..55697f15 --- /dev/null +++ b/konsole/src/Part.h @@ -0,0 +1,218 @@ +/* + Copyright 2007-2008 by Robert Knight + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +#ifndef PART_H +#define PART_H + +// KDE +#include +#include + +// Qt +#include + +// Konsole +#include "Profile.h" + +#include +#include +#include + +namespace Konsole +{ +class Session; +class SessionController; +class ViewManager; +class ViewProperties; + +/** + * A re-usable terminal emulator component using the KParts framework which can + * be used to embed terminal emulators into other applications. + */ +class Part : public KParts::ReadOnlyPart , public TerminalInterface +{ + Q_OBJECT + Q_INTERFACES(TerminalInterface TerminalInterface) +public: + /** Constructs a new Konsole part with the specified parent. */ + explicit Part(QWidget* parentWidget , QObject* parent, const QVariantList&); + virtual ~Part(); + + /** Reimplemented from TerminalInterface. */ + virtual void startProgram(const QString& program, + const QStringList& arguments); + /** Reimplemented from TerminalInterface. */ + virtual void showShellInDir(const QString& dir); + /** Reimplemented from TerminalInterface. */ + virtual void sendInput(const QString& text); + + /** Reimplemented from TerminalInterface. */ + virtual int terminalProcessId(); + + /** Reimplemented from TerminalInterface. */ + virtual int foregroundProcessId(); + + /** Reimplemented from TerminalInterface. */ + virtual QString foregroundProcessName(); + +public slots: + /** + * creates and run a session using the specified profile and directory + * + * @param profileName Specifies the name of the profile to create session + * @param directory specifies The initial working directory of the created session + * + * This is highly experimental. Do not use it at the moment + */ + void createSession(const QString& profileName = QString(), const QString& directory = QString()); + + /** + * Returns a list of names of all available profiles + * + * This is highly experimental. Do not use it at the moment + */ + QStringList profileNameList() const; + + /** + * Shows the dialog used to manage profiles in Konsole. The dialog + * will be non-modal and will delete itself when it is closed. + * + * This is experimental API and not guaranteed to be present in later + * KDE 4 releases. + * + * @param parent The parent widget of the new dialog. + */ + void showManageProfilesDialog(QWidget* parent); + /** + * Shows the dialog used to edit the profile used by the active session. The + * dialog will be non-modal and will delete itself when it is closed. + * + * This is experimental API and not guaranteed to be present in later KDE 4 + * releases. + * + * @param parent The parent widget of the new dialog. + */ + void showEditCurrentProfileDialog(QWidget* parent); + /** + * Sends a profile change command to the active session. This is equivalent to using + * the konsoleprofile tool within the session to change its settings. The @p text string + * is a semi-colon separated list of property=value pairs, eg. "colors=Linux Colors" + * + * See the documentation for konsoleprofile for information on the format of @p text + * + * This is experimental API and not guaranteed to be present in later KDE 4 releases. + */ + void changeSessionSettings(const QString& text); + + /** + * Connects to an existing pseudo-teletype. See Session::openTeletype(). + * This must be called before the session is started by startProgram(), + * or showShellInDir() + * + * @param ptyMasterFd The file descriptor of the pseudo-teletype (pty) master + */ + void openTeletype(int ptyMasterFd); + + /** + * Toggles monitoring for silence in the active session. If silence is detected, + * the silenceDetected() signal is emitted. + * + * @param enabled Whether to enable or disable monitoring for silence. + * */ + void setMonitorSilenceEnabled(bool enabled); + + /** + * Toggles monitoring for activity in the active session. If activity is detected, + * the activityDetected() signal is emitted. + * + * @param enabled Whether to enable or disable monitoring for activity. + * */ + void setMonitorActivityEnabled(bool enabled); + + /** + * Returns the current working directory of the active session + * + * TODO: this should better be moved into TerminalInterface. + */ + QString currentWorkingDirectory() const; + +signals: + /** + * Emitted when the key sequence for a shortcut, which is also a valid terminal key sequence, + * is pressed while the terminal has focus. By responding to this signal, the + * controlling application can choose whether to execute the action associated with + * the shortcut or ignore the shortcut and send the key + * sequence to the terminal application. + * + * In the embedded terminal, shortcuts are overridden and sent to the terminal by default. + * Set @p override to false to prevent this happening and allow the shortcut to be triggered + * normally. + * + * overrideShortcut() is not called for shortcuts which are not valid terminal key sequences, + * eg. shortcuts with two or more modifiers. + * + * @param event Describes the keys that were pressed. + * @param override Set this to false to prevent the terminal display from overriding the shortcut + */ + void overrideShortcut(QKeyEvent* event, bool& override); + + /** + * Emitted when silence has been detected in the active session. Monitoring + * for silence has to be enabled first using setMonitorSilenceEnabled(). + */ + void silenceDetected(); + + /** + * Emitted when activity has been detected in the active session. Monitoring + * for activity has to be enabled first using setMonitorActivityEnabled(). + */ + void activityDetected(); + + /** + * Emitted when the current working directory of the active session has changed. + */ + void currentDirectoryChanged(const QString& dir); + +protected: + /** Reimplemented from KParts::PartBase. */ + virtual bool openFile(); + virtual bool openUrl(const KUrl& url); + +private slots: + void activeViewChanged(SessionController* controller); + void activeViewTitleChanged(ViewProperties* properties); + void showManageProfilesDialog(); + void terminalExited(); + void newTab(); + void overrideTerminalShortcut(QKeyEvent*, bool& override); + void sessionStateChanged(int state); + +private: + Session* activeSession() const; + void createGlobalActions(); + void setupActionsForSession(SessionController*); + +private: + ViewManager* _viewManager; + SessionController* _pluggedController; + QAction* _manageProfilesAction; +}; +} + +#endif // PART_H diff --git a/konsole/src/PrintOptions.cpp b/konsole/src/PrintOptions.cpp new file mode 100644 index 00000000..31d295f7 --- /dev/null +++ b/konsole/src/PrintOptions.cpp @@ -0,0 +1,49 @@ +/* + Copyright 2012 Kasper Laudrup + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License or (at your option) version 3 or any later version + accepted by the membership of KDE e.V. (or its successor appro- + ved by the membership of KDE e.V.), which shall act as a proxy + defined in Section 14 of version 3 of the license. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see http://www.gnu.org/licenses/. +*/ + +// Own +#include "PrintOptions.h" + +// KDE +#include +#include +#include + +using namespace Konsole; + +PrintOptions::PrintOptions(QWidget* aParent) : QWidget(aParent) +{ + setupUi(this); + + KConfigGroup configGroup(KGlobal::config(), "PrintOptions"); + printerFriendly->setChecked(configGroup.readEntry("PrinterFriendly", true)); + scaleOutput->setChecked(configGroup.readEntry("ScaleOutput", true)); +} + +PrintOptions::~PrintOptions() +{ +} + +void PrintOptions::saveSettings() +{ + KConfigGroup configGroup(KGlobal::config(), "PrintOptions"); + configGroup.writeEntry("PrinterFriendly", printerFriendly->isChecked()); + configGroup.writeEntry("ScaleOutput", scaleOutput->isChecked()); +} diff --git a/konsole/src/PrintOptions.h b/konsole/src/PrintOptions.h new file mode 100644 index 00000000..4d6de4ca --- /dev/null +++ b/konsole/src/PrintOptions.h @@ -0,0 +1,41 @@ +/* + Copyright 2012 Kasper Laudrup + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License or (at your option) version 3 or any later version + accepted by the membership of KDE e.V. (or its successor appro- + ved by the membership of KDE e.V.), which shall act as a proxy + defined in Section 14 of version 3 of the license. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see http://www.gnu.org/licenses/. +*/ + +#ifndef PRINTOPTIONS_H +#define PRINTOPTIONS_H + +#include "ui_PrintOptions.h" + +namespace Konsole +{ +class PrintOptions : public QWidget, private Ui::PrintOptions +{ + Q_OBJECT + +public: + explicit PrintOptions(QWidget* parent = 0); + ~PrintOptions(); + +public slots: + void saveSettings(); +}; +} + +#endif diff --git a/konsole/src/PrintOptions.ui b/konsole/src/PrintOptions.ui new file mode 100644 index 00000000..88588692 --- /dev/null +++ b/konsole/src/PrintOptions.ui @@ -0,0 +1,57 @@ + + + PrintOptions + + + + 0 + 0 + 573 + 475 + + + + Output Options + + + + + + Output Options + + + + + + Qt::Vertical + + + + 20 + 223 + + + + + + + + Printer &friendly mode (black text, no background) + + + + + + + &Scale output + + + + + + + + + + + diff --git a/konsole/src/ProcessInfo.cpp b/konsole/src/ProcessInfo.cpp new file mode 100644 index 00000000..e973e257 --- /dev/null +++ b/konsole/src/ProcessInfo.cpp @@ -0,0 +1,1174 @@ +/* + Copyright 2007-2008 by Robert Knight + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +// Own +#include "ProcessInfo.h" + +// Unix +#include +#include +#include +#include +#include +#include + +// Qt +#include +#include +#include +#include +#include + +// KDE +#include +#include +#include +#include +#include + +#if defined(Q_OS_FREEBSD) || defined(Q_OS_DRAGONFLY) +# include +# include +# include +# include +# include +# if defined(Q_OS_DRAGONFLY) +# include +# endif +#elif defined(Q_OS_NETBSD) +# include +# include +# include +#elif defined(Q_OS_OPENBSD) +# include +# include +# include +# include +# include +#elif defined(Q_OS_SOLARIS) +# include +#endif + +#ifndef PATH_MAX +# define PATH_MAX _POSIX_PATH_MAX +#endif + +using namespace Konsole; + +ProcessInfo::ProcessInfo(int aPid , bool enableEnvironmentRead) + : _fields(ARGUMENTS | ENVIRONMENT) // arguments and environments + // are currently always valid, + // they just return an empty + // vector / map respectively + // if no arguments + // or environment bindings + // have been explicitly set + , _enableEnvironmentRead(enableEnvironmentRead) + , _pid(aPid) + , _parentPid(0) + , _foregroundPid(0) + , _userId(0) + , _lastError(NoError) + , _userName(QString()) + , _userHomeDir(QString()) +{ +} + +ProcessInfo::Error ProcessInfo::error() const +{ + return _lastError; +} +void ProcessInfo::setError(Error error) +{ + _lastError = error; +} + +void ProcessInfo::update() +{ + readProcessInfo(_pid, _enableEnvironmentRead); +} + +QString ProcessInfo::validCurrentDir() const +{ + bool ok = false; + + // read current dir, if an error occurs try the parent as the next + // best option + int currentPid = parentPid(&ok); + QString dir = currentDir(&ok); + while (!ok && currentPid != 0) { + ProcessInfo* current = ProcessInfo::newInstance(currentPid); + current->update(); + currentPid = current->parentPid(&ok); + dir = current->currentDir(&ok); + delete current; + } + + return dir; +} + +QString ProcessInfo::format(const QString& input) const +{ + bool ok = false; + + QString output(input); + + // search for and replace known marker + output.replace("%u", userName()); + output.replace("%h", localHost()); + output.replace("%n", name(&ok)); + + QString dir = validCurrentDir(); + if (output.contains("%D")) { + QString homeDir = userHomeDir(); + QString tempDir = dir; + // Change User's Home Dir w/ ~ only at the beginning + if (tempDir.startsWith(homeDir)) { + tempDir.remove(0, homeDir.length()); + tempDir.prepend('~'); + } + output.replace("%D", tempDir); + } + output.replace("%d", formatShortDir(dir)); + + return output; +} + +QSet ProcessInfo::_commonDirNames; + +QSet ProcessInfo::commonDirNames() +{ + static bool forTheFirstTime = true; + + if (forTheFirstTime) { + const KSharedConfigPtr& config = KGlobal::config(); + const KConfigGroup& configGroup = config->group("ProcessInfo"); + _commonDirNames = QSet::fromList(configGroup.readEntry("CommonDirNames", QStringList())); + + forTheFirstTime = false; + } + + return _commonDirNames; +} + +QString ProcessInfo::formatShortDir(const QString& input) const +{ + QString result; + + const QStringList& parts = input.split(QDir::separator()); + + QSet dirNamesToShorten = commonDirNames(); + + QListIterator iter(parts); + iter.toBack(); + + // go backwards through the list of the path's parts + // adding abbreviations of common directory names + // and stopping when we reach a dir name which is not + // in the commonDirNames set + while (iter.hasPrevious()) { + const QString& part = iter.previous(); + + if (dirNamesToShorten.contains(part)) { + result.prepend(QDir::separator() + QString(part[0])); + } else { + result.prepend(part); + break; + } + } + + return result; +} + +QVector ProcessInfo::arguments(bool* ok) const +{ + *ok = _fields & ARGUMENTS; + + return _arguments; +} + +QMap ProcessInfo::environment(bool* ok) const +{ + *ok = _fields & ENVIRONMENT; + + return _environment; +} + +bool ProcessInfo::isValid() const +{ + return _fields & PROCESS_ID; +} + +int ProcessInfo::pid(bool* ok) const +{ + *ok = _fields & PROCESS_ID; + + return _pid; +} + +int ProcessInfo::parentPid(bool* ok) const +{ + *ok = _fields & PARENT_PID; + + return _parentPid; +} + +int ProcessInfo::foregroundPid(bool* ok) const +{ + *ok = _fields & FOREGROUND_PID; + + return _foregroundPid; +} + +QString ProcessInfo::name(bool* ok) const +{ + *ok = _fields & NAME; + + return _name; +} + +int ProcessInfo::userId(bool* ok) const +{ + *ok = _fields & UID; + + return _userId; +} + +QString ProcessInfo::userName() const +{ + return _userName; +} + +QString ProcessInfo::userHomeDir() const +{ + return _userHomeDir; +} + +QString ProcessInfo::localHost() +{ + return QHostInfo::localHostName(); +} + +void ProcessInfo::setPid(int aPid) +{ + _pid = aPid; + _fields |= PROCESS_ID; +} + +void ProcessInfo::setUserId(int uid) +{ + _userId = uid; + _fields |= UID; +} + +void ProcessInfo::setUserName(const QString& name) +{ + _userName = name; + setUserHomeDir(); +} + +void ProcessInfo::setUserHomeDir() +{ + const QString& usersName = userName(); + if (!usersName.isEmpty()) + _userHomeDir = KUser(usersName).homeDir(); + else + _userHomeDir = QDir::homePath(); +} + +void ProcessInfo::setParentPid(int aPid) +{ + _parentPid = aPid; + _fields |= PARENT_PID; +} +void ProcessInfo::setForegroundPid(int aPid) +{ + _foregroundPid = aPid; + _fields |= FOREGROUND_PID; +} + +QString ProcessInfo::currentDir(bool* ok) const +{ + if (ok) + *ok = _fields & CURRENT_DIR; + + return _currentDir; +} +void ProcessInfo::setCurrentDir(const QString& dir) +{ + _fields |= CURRENT_DIR; + _currentDir = dir; +} + +void ProcessInfo::setName(const QString& name) +{ + _name = name; + _fields |= NAME; +} +void ProcessInfo::addArgument(const QString& argument) +{ + _arguments << argument; +} + +void ProcessInfo::clearArguments() +{ + _arguments.clear(); +} + +void ProcessInfo::addEnvironmentBinding(const QString& name , const QString& value) +{ + _environment.insert(name, value); +} + +void ProcessInfo::setFileError(QFile::FileError error) +{ + switch (error) { + case QFile::PermissionsError: + setError(ProcessInfo::PermissionsError); + break; + case QFile::NoError: + setError(ProcessInfo::NoError); + break; + default: + setError(ProcessInfo::UnknownError); + } +} + +// +// ProcessInfo::newInstance() is way at the bottom so it can see all of the +// implementations of the UnixProcessInfo abstract class. +// + +NullProcessInfo::NullProcessInfo(int aPid, bool enableEnvironmentRead) + : ProcessInfo(aPid, enableEnvironmentRead) +{ +} + +bool NullProcessInfo::readProcessInfo(int /*pid*/ , bool /*enableEnvironmentRead*/) +{ + return false; +} + +void NullProcessInfo::readUserName() +{ +} + +UnixProcessInfo::UnixProcessInfo(int aPid, bool enableEnvironmentRead) + : ProcessInfo(aPid, enableEnvironmentRead) +{ +} + +bool UnixProcessInfo::readProcessInfo(int aPid , bool enableEnvironmentRead) +{ + // prevent _arguments from growing longer and longer each time this + // method is called. + clearArguments(); + + bool ok = readProcInfo(aPid); + if (ok) { + ok |= readArguments(aPid); + ok |= readCurrentDir(aPid); + if (enableEnvironmentRead) { + ok |= readEnvironment(aPid); + } + } + return ok; +} + +void UnixProcessInfo::readUserName() +{ + bool ok = false; + const int uid = userId(&ok); + if (!ok) return; + + struct passwd passwdStruct; + struct passwd* getpwResult; + char* getpwBuffer; + long getpwBufferSize; + int getpwStatus; + + getpwBufferSize = sysconf(_SC_GETPW_R_SIZE_MAX); + if (getpwBufferSize == -1) + getpwBufferSize = 16384; + + getpwBuffer = new char[getpwBufferSize]; + if (getpwBuffer == NULL) + return; + getpwStatus = getpwuid_r(uid, &passwdStruct, getpwBuffer, getpwBufferSize, &getpwResult); + if ((getpwStatus == 0) && (getpwResult != NULL)) { + setUserName(QString(passwdStruct.pw_name)); + } else { + setUserName(QString()); + kWarning() << "getpwuid_r returned error : " << getpwStatus; + } + delete [] getpwBuffer; +} + +#if defined(Q_OS_LINUX) || defined(Q_OS_HURD) +class LinuxProcessInfo : public UnixProcessInfo +{ +public: + LinuxProcessInfo(int aPid, bool env) : + UnixProcessInfo(aPid, env) { + } + +private: + virtual bool readProcInfo(int aPid) { + // indicies of various fields within the process status file which + // contain various information about the process + const int PARENT_PID_FIELD = 3; + const int PROCESS_NAME_FIELD = 1; + const int GROUP_PROCESS_FIELD = 7; + + QString parentPidString; + QString processNameString; + QString foregroundPidString; + QString uidLine; + QString uidString; + QStringList uidStrings; + + // For user id read process status file ( /proc//status ) + // Can not use getuid() due to it does not work for 'su' + QFile statusInfo(QString("/proc/%1/status").arg(aPid)); + if (statusInfo.open(QIODevice::ReadOnly)) { + QTextStream stream(&statusInfo); + QString statusLine; + do { + statusLine = stream.readLine(0); + if (statusLine.startsWith(QLatin1String("Uid:"))) + uidLine = statusLine; + } while (!statusLine.isNull() && uidLine.isNull()); + + uidStrings << uidLine.split('\t', QString::SkipEmptyParts); + // Must be 5 entries: 'Uid: %d %d %d %d' and + // uid string must be less than 5 chars (uint) + if (uidStrings.size() == 5) + uidString = uidStrings[1]; + if (uidString.size() > 5) + uidString.clear(); + + bool ok = false; + const int uid = uidString.toInt(&ok); + if (ok) + setUserId(uid); + readUserName(); + } else { + setFileError(statusInfo.error()); + return false; + } + + // read process status file ( /proc//cmdline + // the expected format is a list of strings delimited by null characters, + // and ending in a double null character pair. + + QFile argumentsFile(QString("/proc/%1/cmdline").arg(aPid)); + if (argumentsFile.open(QIODevice::ReadOnly)) { + QTextStream stream(&argumentsFile); + const QString& data = stream.readAll(); + + const QStringList& argList = data.split(QChar('\0')); + + foreach(const QString & entry , argList) { + if (!entry.isEmpty()) + addArgument(entry); + } + } else { + setFileError(argumentsFile.error()); + } + + return true; + } + + virtual bool readCurrentDir(int aPid) { + char path_buffer[PATH_MAX + 1]; + path_buffer[PATH_MAX] = 0; + QByteArray procCwd = QFile::encodeName(QString("/proc/%1/cwd").arg(aPid)); + const int length = readlink(procCwd.constData(), path_buffer, PATH_MAX); + if (length == -1) { + setError(UnknownError); + return false; + } + + path_buffer[length] = '\0'; + QString path = QFile::decodeName(path_buffer); + + setCurrentDir(path); + return true; + } + + virtual bool readEnvironment(int aPid) { + // read environment bindings file found at /proc//environ + // the expected format is a list of KEY=VALUE strings delimited by null + // characters and ending in a double null character pair. + + QFile environmentFile(QString("/proc/%1/environ").arg(aPid)); + if (environmentFile.open(QIODevice::ReadOnly)) { + QTextStream stream(&environmentFile); + const QString& data = stream.readAll(); + + const QStringList& bindingList = data.split(QChar('\0')); + + foreach(const QString & entry , bindingList) { + QString name; + QString value; + + const int splitPos = entry.indexOf('='); + + if (splitPos != -1) { + name = entry.mid(0, splitPos); + value = entry.mid(splitPos + 1, -1); + + addEnvironmentBinding(name, value); + } + } + } else { + setFileError(environmentFile.error()); + } + + return true; + } +}; + +#elif defined(Q_OS_FREEBSD) || defined(Q_OS_DRAGONFLY) +class FreeBSDProcessInfo : public UnixProcessInfo +{ +public: + FreeBSDProcessInfo(int aPid, bool readEnvironment) : + UnixProcessInfo(aPid, readEnvironment) { + } + +private: + virtual bool readProcInfo(int aPid) { + int managementInfoBase[4]; + size_t mibLength; + struct kinfo_proc* kInfoProc; + + managementInfoBase[0] = CTL_KERN; + managementInfoBase[1] = KERN_PROC; + managementInfoBase[2] = KERN_PROC_PID; + managementInfoBase[3] = aPid; + + if (sysctl(managementInfoBase, 4, NULL, &mibLength, NULL, 0) == -1) + return false; + + kInfoProc = new struct kinfo_proc [mibLength]; + + if (sysctl(managementInfoBase, 4, kInfoProc, &mibLength, NULL, 0) == -1) { + delete [] kInfoProc; + return false; + } + +#ifdef Q_OS_DRAGONFLY + setName(kInfoProc->kp_comm); + setPid(kInfoProc->kp_pid); + setParentPid(kInfoProc->kp_ppid); + setForegroundPid(kInfoProc->kp_pgid); + setUserId(kInfoProc->kp_uid); +#else + setName(kInfoProc->ki_comm); + setPid(kInfoProc->ki_pid); + setParentPid(kInfoProc->ki_ppid); + setForegroundPid(kInfoProc->ki_pgid); + setUserId(kInfoProc->ki_uid); +#endif + + readUserName(); + + delete [] kInfoProc; + return true; + } + + virtual bool readArguments(int aPid) { + char args[ARG_MAX]; + int managementInfoBase[4]; + size_t len; + + managementInfoBase[0] = CTL_KERN; + managementInfoBase[1] = KERN_PROC; + managementInfoBase[2] = KERN_PROC_PID; + managementInfoBase[3] = aPid; + + len = sizeof(args); + if (sysctl(managementInfoBase, 4, args, &len, NULL, 0) == -1) + return false; + + const QStringList& argumentList = QString(args).split(QChar('\0')); + + for (QStringList::const_iterator it = argumentList.begin(); it != argumentList.end(); ++it) { + addArgument(*it); + } + + return true; + } + + virtual bool readEnvironment(int aPid) { + Q_UNUSED(aPid); + // Not supported in FreeBSD? + return false; + } + + virtual bool readCurrentDir(int aPid) { +#ifdef Q_OS_DRAGONFLY +# warning readCurrentDir() not implemented +#else + int numrecords; + struct kinfo_file* info = 0; + + info = kinfo_getfile(aPid, &numrecords); + + if (!info) + return false; + + for (int i = 0; i < numrecords; ++i) { + if (info[i].kf_fd == KF_FD_TYPE_CWD) { + setCurrentDir(info[i].kf_path); + + free(info); + return true; + } + } + + free(info); + return false; +#endif // Q_OS_DRAGONFLY + } +}; + +#elif defined(Q_OS_OPENBSD) +class OpenBSDProcessInfo : public UnixProcessInfo +{ +public: + OpenBSDProcessInfo(int aPid, bool readEnvironment) : + UnixProcessInfo(aPid, readEnvironment) { + } + +private: + virtual bool readProcInfo(int aPid) { + int managementInfoBase[6]; + size_t mibLength; + struct kinfo_proc* kInfoProc; + + managementInfoBase[0] = CTL_KERN; + managementInfoBase[1] = KERN_PROC; + managementInfoBase[2] = KERN_PROC_PID; + managementInfoBase[3] = aPid; + managementInfoBase[4] = sizeof(struct kinfo_proc); + managementInfoBase[5] = 1; + + if (::sysctl(managementInfoBase, 6, NULL, &mibLength, NULL, 0) == -1) { + kWarning() << "first sysctl() call failed with code" << errno; + return false; + } + + kInfoProc = (struct kinfo_proc*)malloc(mibLength); + + if (::sysctl(managementInfoBase, 6, kInfoProc, &mibLength, NULL, 0) == -1) { + kWarning() << "second sysctl() call failed with code" << errno; + free(kInfoProc); + return false; + } + + setName(kInfoProc->p_comm); + setPid(kInfoProc->p_pid); + setParentPid(kInfoProc->p_ppid); + setForegroundPid(kInfoProc->p_tpgid); + setUserId(kInfoProc->p_uid); + setUserName(kInfoProc->p_login); + + free(kInfoProc); + return true; + } + + char** readProcArgs(int aPid, int whatMib) { + void* buf = NULL; + void* nbuf; + size_t len = 4096; + int rc = -1; + int managementInfoBase[4]; + + managementInfoBase[0] = CTL_KERN; + managementInfoBase[1] = KERN_PROC_ARGS; + managementInfoBase[2] = aPid; + managementInfoBase[3] = whatMib; + + do { + len *= 2; + nbuf = realloc(buf, len); + if (nbuf == NULL) { + break; + } + + buf = nbuf; + rc = ::sysctl(managementInfoBase, 4, buf, &len, NULL, 0); + if (rc == -1) { + kWarning() << "sysctl() call failed with code" << errno; + } + } while (rc == -1 && errno == ENOMEM); + + if (nbuf == NULL || rc == -1) { + free(buf); + return NULL; + } + + return (char**)buf; + } + + virtual bool readArguments(int aPid) { + char** argv; + + argv = readProcArgs(aPid, KERN_PROC_ARGV); + if (argv == NULL) { + return false; + } + + for (char** p = argv; *p != NULL; p++) { + addArgument(QString(*p)); + } + free(argv); + return true; + } + + virtual bool readEnvironment(int aPid) { + char** envp; + char* eqsign; + + envp = readProcArgs(aPid, KERN_PROC_ENV); + if (envp == NULL) { + return false; + } + + for (char **p = envp; *p != NULL; p++) { + eqsign = strchr(*p, '='); + if (eqsign == NULL || eqsign[1] == '\0') { + continue; + } + *eqsign = '\0'; + addEnvironmentBinding(QString((const char *)p), + QString((const char *)eqsign + 1)); + } + free(envp); + return true; + } + + virtual bool readCurrentDir(int aPid) { + char buf[PATH_MAX]; + int managementInfoBase[3]; + size_t len; + + managementInfoBase[0] = CTL_KERN; + managementInfoBase[1] = KERN_PROC_CWD; + managementInfoBase[2] = aPid; + + len = sizeof(buf); + if (::sysctl(managementInfoBase, 3, buf, &len, NULL, 0) == -1) { + kWarning() << "sysctl() call failed with code" << errno; + return false; + } + + setCurrentDir(buf); + return true; + } +}; + +#elif defined(Q_OS_NETBSD) +class NetBSDProcessInfo : public UnixProcessInfo +{ +public: + NetBSDProcessInfo(int aPid, bool readEnvironment) : + UnixProcessInfo(aPid, readEnvironment) { + kd = kvm_open(NULL, NULL, NULL, KVM_NO_FILES, "kvm_open"); + } + ~NetBSDProcessInfo() { + kvm_close(kd); + } + +private: + virtual bool readProcInfo(int aPid) { + int len; + struct kinfo_proc2 *kInfoProc = kvm_getproc2(kd, KERN_PROC_PID, aPid, sizeof(struct kinfo_proc2), &len); + if (len < 1) + return false; + + setName(kInfoProc->p_comm); + setPid(kInfoProc->p_pid); + setParentPid(kInfoProc->p_ppid); + setForegroundPid(kInfoProc->p_tpgid); + setUserId(kInfoProc->p_uid); + setUserName(kInfoProc->p_login); + + return true; + } + + virtual bool readArguments(int aPid) { + int len; + struct kinfo_proc2 *kInfoProc = kvm_getproc2(kd, KERN_PROC_PID, aPid, sizeof(struct kinfo_proc2), &len); + if (len < 1) + return false; + + char **argv = kvm_getargv2(kd, kInfoProc, 256); + if (!argv) + return false; + + while (*argv) { + addArgument(QString(*argv)); + argv++; + } + + return true; + } + + virtual bool readEnvironment(int aPid) { + int len; + struct kinfo_proc2 *kInfoProc = kvm_getproc2(kd, KERN_PROC_PID, aPid, sizeof(struct kinfo_proc2), &len); + if (len < 1) + return false; + + char **env = kvm_getenvv2(kd, kInfoProc, 256); + if (!env) + return false; + + while (*env) { + char* eqsign = strchr(*env, '='); + if (!eqsign || eqsign[1] == '\0') { + env++; + continue; + } + *eqsign = '\0'; + addEnvironmentBinding(QString(*env), QString(eqsign + 1)); + env++; + } + + return true; + } + + virtual bool readCurrentDir(int aPid) { + char buf[PATH_MAX]; + int managementInfoBase[4]; + size_t len; + + managementInfoBase[0] = CTL_KERN; + managementInfoBase[1] = KERN_PROC_ARGS; + managementInfoBase[2] = aPid; + managementInfoBase[3] = KERN_PROC_CWD; + + len = sizeof(buf); + if (::sysctl(managementInfoBase, 4, buf, &len, NULL, 0) == -1) { + kWarning() << "sysctl() call failed with code" << errno; + return false; + } + + setCurrentDir(buf); + return true; + } + + kvm_t *kd; +}; + +#elif defined(Q_OS_SOLARIS) +class SolarisProcessInfo : public UnixProcessInfo +{ +public: + SolarisProcessInfo(int aPid, bool readEnvironment) + : UnixProcessInfo(aPid, readEnvironment) { + } +private: + virtual bool readProcInfo(int aPid) { + QFile psinfo(QString("/proc/%1/psinfo").arg(aPid)); + if (psinfo.open(QIODevice::ReadOnly)) { + struct psinfo info; + if (psinfo.read((char *)&info, sizeof(info)) != sizeof(info)) { + return false; + } + + setParentPid(info.pr_ppid); + setForegroundPid(info.pr_pgid); + setName(info.pr_fname); + setPid(aPid); + + // Bogus, because we're treating the arguments as one single string + info.pr_psargs[PRARGSZ - 1] = 0; + addArgument(info.pr_psargs); + } + return true; + } + + virtual bool readArguments(int /*pid*/) { + // Handled in readProcInfo() + return false; + } + + virtual bool readEnvironment(int /*pid*/) { + // Not supported in Solaris + return false; + } + + // FIXME: This will have the same issues as BKO 251351; the Linux + // version uses readlink. + virtual bool readCurrentDir(int aPid) { + QFileInfo info(QString("/proc/%1/path/cwd").arg(aPid)); + const bool readable = info.isReadable(); + + if (readable && info.isSymLink()) { + setCurrentDir(info.readLink()); + return true; + } else { + if (!readable) + setError(PermissionsError); + else + setError(UnknownError); + + return false; + } + } +}; +#endif + +SSHProcessInfo::SSHProcessInfo(const ProcessInfo& process) + : _process(process) +{ + bool ok = false; + + // check that this is a SSH process + const QString& name = _process.name(&ok); + + if (!ok || name != "ssh") { + if (!ok) + kWarning() << "Could not read process info"; + else + kWarning() << "Process is not a SSH process"; + + return; + } + + // read arguments + const QVector& args = _process.arguments(&ok); + + // SSH options + // these are taken from the SSH manual ( accessed via 'man ssh' ) + + // options which take no arguments + static const QString noArgumentOptions("1246AaCfgKkMNnqsTtVvXxYy"); + // options which take one argument + static const QString singleArgumentOptions("bcDeFIiLlmOopRSWw"); + + if (ok) { + // find the username, host and command arguments + // + // the username/host is assumed to be the first argument + // which is not an option + // ( ie. does not start with a dash '-' character ) + // or an argument to a previous option. + // + // the command, if specified, is assumed to be the argument following + // the username and host + // + // note that we skip the argument at index 0 because that is the + // program name ( expected to be 'ssh' in this case ) + for (int i = 1 ; i < args.count() ; i++) { + // If this one is an option ... + // Most options together with its argument will be skipped. + if (args[i].startsWith('-')) { + const QChar optionChar = (args[i].length() > 1) ? args[i][1] : '\0'; + // for example: -p2222 or -p 2222 ? + const bool optionArgumentCombined = args[i].length() > 2; + + if (noArgumentOptions.contains(optionChar)) { + continue; + } else if (singleArgumentOptions.contains(optionChar)) { + QString argument; + if (optionArgumentCombined) { + argument = args[i].mid(2); + } else { + // Verify correct # arguments are given + if ((i + 1) < args.count()) { + argument = args[i + 1]; + } + i++; + } + + // support using `-l user` to specify username. + if (optionChar == 'l') + _user = argument; + // support using `-p port` to specify port. + else if (optionChar == 'p') + _port = argument; + + continue; + } + } + + // check whether the host has been found yet + // if not, this must be the username/host argument + if (_host.isEmpty()) { + // check to see if only a hostname is specified, or whether + // both a username and host are specified ( in which case they + // are separated by an '@' character: username@host ) + + const int separatorPosition = args[i].indexOf('@'); + if (separatorPosition != -1) { + // username and host specified + _user = args[i].left(separatorPosition); + _host = args[i].mid(separatorPosition + 1); + } else { + // just the host specified + _host = args[i]; + } + } else { + // host has already been found, this must be the command argument + _command = args[i]; + } + } + } else { + kWarning() << "Could not read arguments"; + + return; + } +} + +QString SSHProcessInfo::userName() const +{ + return _user; +} +QString SSHProcessInfo::host() const +{ + return _host; +} +QString SSHProcessInfo::port() const +{ + return _port; +} +QString SSHProcessInfo::command() const +{ + return _command; +} +QString SSHProcessInfo::format(const QString& input) const +{ + QString output(input); + + // test whether host is an ip address + // in which case 'short host' and 'full host' + // markers in the input string are replaced with + // the full address + bool isIpAddress = false; + + struct in_addr address; + if (inet_aton(_host.toLocal8Bit().constData(), &address) != 0) + isIpAddress = true; + else + isIpAddress = false; + + // search for and replace known markers + output.replace("%u", _user); + + if (isIpAddress) + output.replace("%h", _host); + else + output.replace("%h", _host.left(_host.indexOf('.'))); + + output.replace("%H", _host); + output.replace("%c", _command); + + return output; +} + +ProcessInfo* ProcessInfo::newInstance(int aPid, bool enableEnvironmentRead) +{ +#if defined(Q_OS_LINUX) || defined(Q_OS_HURD) + return new LinuxProcessInfo(aPid, enableEnvironmentRead); +#elif defined(Q_OS_SOLARIS) + return new SolarisProcessInfo(aPid, enableEnvironmentRead); +#elif defined(Q_OS_FREEBSD) || defined(Q_OS_DRAGONFLY) + return new FreeBSDProcessInfo(aPid, enableEnvironmentRead); +#elif defined(Q_OS_OPENBSD) + return new OpenBSDProcessInfo(aPid, enableEnvironmentRead); +#elif defined(Q_OS_NETBSD) + return new NetBSDProcessInfo(aPid, enableEnvironmentRead); +#else +#warning ProcessInfo not implemented for this platform + return new NullProcessInfo(aPid, enableEnvironmentRead); +#endif +} + diff --git a/konsole/src/ProcessInfo.h b/konsole/src/ProcessInfo.h new file mode 100644 index 00000000..9d0c3a57 --- /dev/null +++ b/konsole/src/ProcessInfo.h @@ -0,0 +1,460 @@ +/* + Copyright 2007-2008 by Robert Knight + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +#ifndef PROCESSINFO_H +#define PROCESSINFO_H + +// Qt +#include +#include +#include +#include + +namespace Konsole +{ +/** + * Takes a snapshot of the state of a process and provides access to + * information such as the process name, parent process, + * the foreground process in the controlling terminal, + * the arguments with which the process was started and the + * environment. + * + * To create a new snapshot, construct a new ProcessInfo instance, + * using ProcessInfo::newInstance(), + * passing the process identifier of the process you are interested in. + * + * After creating a new instance, call the update() method to take a + * snapshot of the current state of the process. + * + * Before calling any additional methods, check that the process state + * was read successfully using the isValid() method. + * + * Each accessor method which provides information about the process state ( such as pid(), + * currentDir(), name() ) takes a pointer to a boolean as an argument. If the information + * requested was read successfully then the boolean is set to true, otherwise it is set + * to false, in which case the return value from the function should be ignored. + * If this boolean is set to false, it may indicate an error reading the process information, + * or it may indicate that the information is not available on the current platform. + * + * eg. + * + * @code + * ProcessInfo* info = ProcessInfo::newInstance(pid); + * info->update(); + * + * if ( info->isValid() ) + * { + * bool ok; + * + * QString name = info->name(&ok); + * if ( ok ) kDebug() << "process name - " << name; + * int parentPid = info->parentPid(&ok); + * if ( ok ) kDebug() << "parent process - " << parentPid; + * int foregroundPid = info->foregroundPid(&ok); + * if ( ok ) kDebug() << "foreground process - " << foregroundPid; + * } + * @endcode + */ +class ProcessInfo +{ +public: + /** + * Constructs a new instance of a suitable ProcessInfo sub-class for + * the current platform which provides information about a given process. + * + * @param pid The pid of the process to examine + * @param readEnvironment Specifies whether environment bindings should + * be read. If this is false, then environment() calls will + * always fail. This is an optimization to avoid the overhead + * of reading the (potentially large) environment data when it + * is not required. + */ + static ProcessInfo* newInstance(int pid, bool readEnvironment = false); + + virtual ~ProcessInfo() {} + + /** + * Updates the information about the process. This must + * be called before attempting to use any of the accessor methods. + */ + void update(); + + /** Returns true if the process state was read successfully. */ + bool isValid() const; + /** + * Returns the process id. + * + * @param ok Set to true if the process id was read successfully or false otherwise + */ + int pid(bool* ok) const; + /** + * Returns the id of the parent process id was read successfully or false otherwise + * + * @param ok Set to true if the parent process id + */ + int parentPid(bool* ok) const; + + /** + * Returns the id of the current foreground process + * + * NOTE: Using the foregroundProcessGroup() method of the Pty + * instance associated with the terminal of interest is preferred + * over using this method. + * + * @param ok Set to true if the foreground process id was read successfully or false otherwise + */ + int foregroundPid(bool* ok) const; + + /* Returns the user id of the process */ + int userId(bool* ok) const; + + /** Returns the user's name of the process */ + QString userName() const; + + /** Returns the user's home directory of the process */ + QString userHomeDir() const; + + /** Returns the local host */ + static QString localHost(); + + /** Returns the name of the current process */ + QString name(bool* ok) const; + + /** + * Returns the command-line arguments which the process + * was started with. + * + * The first argument is the name used to launch the process. + * + * @param ok Set to true if the arguments were read successfully or false otherwise. + */ + QVector arguments(bool* ok) const; + /** + * Returns the environment bindings which the process + * was started with. + * In the returned map, the key is the name of the environment variable, + * and the value is the corresponding value. + * + * @param ok Set to true if the environment bindings were read successfully or false otherwise + */ + QMap environment(bool* ok) const; + + /** + * Returns the current working directory of the process + * + * @param ok Set to true if the current working directory was read successfully or false otherwise + */ + QString currentDir(bool* ok) const; + + /** + * Returns the current working directory of the process (or its parent) + */ + QString validCurrentDir() const; + + /** Forces the user home directory to be calculated */ + void setUserHomeDir(); + + /** + * Parses an input string, looking for markers beginning with a '%' + * character and returns a string with the markers replaced + * with information from this process description. + *
    + * The markers recognized are: + *
      + *
    • %u - Name of the user which owns the process.
    • + *
    • %n - Replaced with the name of the process.
    • + *
    • %d - Replaced with the last part of the path name of the + * process' current working directory. + * + * (eg. if the current directory is '/home/bob' then + * 'bob' would be returned) + *
    • + *
    • %D - Replaced with the current working directory of the process.
    • + *
    + */ + QString format(const QString& text) const; + + /** + * This enum describes the errors which can occur when trying to read + * a process's information. + */ + enum Error { + /** No error occurred. */ + NoError, + /** The nature of the error is unknown. */ + UnknownError, + /** Konsole does not have permission to obtain the process information. */ + PermissionsError + }; + + /** + * Returns the last error which occurred. + */ + Error error() const; + +protected: + /** + * Constructs a new process instance. You should not call the constructor + * of ProcessInfo or its subclasses directly. Instead use the + * static ProcessInfo::newInstance() method which will return + * a suitable ProcessInfo instance for the current platform. + */ + explicit ProcessInfo(int pid , bool readEnvironment = false); + + /** + * This is called on construction to read the process state + * Subclasses should reimplement this function to provide + * platform-specific process state reading functionality. + * + * When called, readProcessInfo() should attempt to read all + * of the necessary state information. If the attempt is successful, + * it should set the process id using setPid(), and update + * the other relevant information using setParentPid(), setName(), + * setArguments() etc. + * + * Calls to isValid() will return true only if the process id + * has been set using setPid() + * + * @param pid The process id of the process to read + * @param readEnvironment Specifies whether the environment bindings + * for the process should be read + */ + virtual bool readProcessInfo(int pid , bool readEnvironment) = 0; + + /* Read the user name */ + virtual void readUserName(void) = 0; + + /** Sets the process id associated with this ProcessInfo instance */ + void setPid(int pid); + /** Sets the parent process id as returned by parentPid() */ + void setParentPid(int pid); + /** Sets the foreground process id as returned by foregroundPid() */ + void setForegroundPid(int pid); + /** Sets the user id associated with this ProcessInfo instance */ + void setUserId(int uid); + /** Sets the user name of the process as set by readUserName() */ + void setUserName(const QString& name); + /** Sets the name of the process as returned by name() */ + void setName(const QString& name); + /** Sets the current working directory for the process */ + void setCurrentDir(const QString& dir); + + /** Sets the error */ + void setError(Error error); + + /** Convenience method. Sets the error based on a QFile error code. */ + void setFileError(QFile::FileError error); + + /** + * Adds a commandline argument for the process, as returned + * by arguments() + */ + void addArgument(const QString& argument); + + /** + * clear the commandline arguments for the process, as returned + * by arguments() + */ + void clearArguments(); + + /** + * Adds an environment binding for the process, as returned by + * environment() + * + * @param name The name of the environment variable, eg. "PATH" + * @param value The value of the environment variable, eg. "/bin" + */ + void addEnvironmentBinding(const QString& name , const QString& value); + +private: + // takes a full directory path and returns a + // shortened version suitable for display in + // space-constrained UI elements (eg. tabs) + QString formatShortDir(const QString& dirPath) const; + + // valid bits for _fields variable, ensure that + // _fields is changed to an int if more than 8 fields are added + enum FIELD_BITS { + PROCESS_ID = 1, + PARENT_PID = 2, + FOREGROUND_PID = 4, + ARGUMENTS = 8, + ENVIRONMENT = 16, + NAME = 32, + CURRENT_DIR = 64, + UID = 128 + }; + + char _fields; // a bitmap indicating which fields are valid + // used to set the "ok" parameters for the public + // accessor functions + + bool _enableEnvironmentRead; // specifies whether to read the environment + // bindings when update() is called + int _pid; + int _parentPid; + int _foregroundPid; + int _userId; + + Error _lastError; + + QString _name; + QString _userName; + QString _userHomeDir; + QString _currentDir; + + QVector _arguments; + QMap _environment; + + static QSet commonDirNames(); + static QSet _commonDirNames; +}; + +/** + * Implementation of ProcessInfo which does nothing. + * Used on platforms where a suitable ProcessInfo subclass is not + * available. + * + * isValid() will always return false for instances of NullProcessInfo + */ +class NullProcessInfo : public ProcessInfo +{ +public: + /** + * Constructs a new NullProcessInfo instance. + * See ProcessInfo::newInstance() + */ + explicit NullProcessInfo(int pid, bool readEnvironment = false); +protected: + virtual bool readProcessInfo(int pid, bool readEnvironment); + virtual void readUserName(void); +}; + +/** + * Implementation of ProcessInfo for Unix platforms which uses + * the /proc filesystem + */ +class UnixProcessInfo : public ProcessInfo +{ +public: + /** + * Constructs a new instance of UnixProcessInfo. + * See ProcessInfo::newInstance() + */ + explicit UnixProcessInfo(int pid, bool readEnvironment = false); + +protected: + /** + * Implementation of ProcessInfo::readProcessInfo(); calls the + * four private methods below in turn. + */ + virtual bool readProcessInfo(int pid , bool readEnvironment); + + virtual void readUserName(void); + +private: + /** + * Read the standard process information -- PID, parent PID, foreground PID. + * @param pid process ID to use + * @return true on success + */ + virtual bool readProcInfo(int pid) = 0; + + /** + * Read the environment of the process. Sets _environment. + * @param pid process ID to use + * @return true on success + */ + virtual bool readEnvironment(int pid) = 0; + + /** + * Determine what arguments were passed to the process. Sets _arguments. + * @param pid process ID to use + * @return true on success + */ + virtual bool readArguments(int pid) = 0; + + /** + * Determine the current directory of the process. + * @param pid process ID to use + * @return true on success + */ + virtual bool readCurrentDir(int pid) = 0; +}; + +/** + * Lightweight class which provides additional information about SSH processes. + */ +class SSHProcessInfo +{ +public: + /** + * Constructs a new SSHProcessInfo instance which provides additional + * information about the specified SSH process. + * + * @param process A ProcessInfo instance for a SSH process. + */ + explicit SSHProcessInfo(const ProcessInfo& process); + + /** + * Returns the user name which the user initially logged into on + * the remote computer. + */ + QString userName() const; + + /** + * Returns the host which the user has connected to. + */ + QString host() const; + + /** + * Returns the port on host which the user has connected to. + */ + QString port() const; + + /** + * Returns the command which the user specified to execute on the + * remote computer when starting the SSH process. + */ + QString command() const; + + /** + * Operates in the same way as ProcessInfo::format(), except + * that the set of markers understood is different: + * + * %u - Replaced with user name which the user initially logged + * into on the remote computer. + * %h - Replaced with the first part of the host name which + * is connected to. + * %H - Replaced with the full host name of the computer which + * is connected to. + * %c - Replaced with the command which the user specified + * to execute when starting the SSH process. + */ + QString format(const QString& input) const; + +private: + const ProcessInfo& _process; + QString _user; + QString _host; + QString _port; + QString _command; +}; +} +#endif //PROCESSINFO_H diff --git a/konsole/src/Profile.cpp b/konsole/src/Profile.cpp new file mode 100644 index 00000000..58a10b1e --- /dev/null +++ b/konsole/src/Profile.cpp @@ -0,0 +1,374 @@ +/* + This source file is part of Konsole, a terminal emulator. + + Copyright 2006-2008 by Robert Knight + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +// Own +#include "Profile.h" + +// Qt +#include + +// KDE +#include +#include + +// Konsole +#include "Enumeration.h" + +using namespace Konsole; + +// mappings between property enum values and names +// +// multiple names are defined for some property values, +// in these cases, the "proper" string name comes first, +// as that is used when reading/writing profiles from/to disk +// +// the other names are usually shorter versions for convenience +// when parsing konsoleprofile commands +static const char GENERAL_GROUP[] = "General"; +static const char KEYBOARD_GROUP[] = "Keyboard"; +static const char APPEARANCE_GROUP[] = "Appearance"; +static const char SCROLLING_GROUP[] = "Scrolling"; +static const char TERMINAL_GROUP[] = "Terminal Features"; +static const char CURSOR_GROUP[] = "Cursor Options"; +static const char INTERACTION_GROUP[] = "Interaction Options"; +static const char ENCODING_GROUP[] = "Encoding Options"; + +const Profile::PropertyInfo Profile::DefaultPropertyNames[] = { + // General + { Path , "Path" , 0 , QVariant::String } + , { Name , "Name" , GENERAL_GROUP , QVariant::String } + , { UntranslatedName, "UntranslatedName" , 0 , QVariant::String } + , { Icon , "Icon" , GENERAL_GROUP , QVariant::String } + , { Command , "Command" , 0 , QVariant::String } + , { Arguments , "Arguments" , 0 , QVariant::StringList } + , { MenuIndex, "MenuIndex" , 0, QVariant::String } + , { Environment , "Environment" , GENERAL_GROUP , QVariant::StringList } + , { Directory , "Directory" , GENERAL_GROUP , QVariant::String } + , { LocalTabTitleFormat , "LocalTabTitleFormat" , GENERAL_GROUP , QVariant::String } + , { LocalTabTitleFormat , "tabtitle" , 0 , QVariant::String } + , { RemoteTabTitleFormat , "RemoteTabTitleFormat" , GENERAL_GROUP , QVariant::String } + , { ShowTerminalSizeHint , "ShowTerminalSizeHint" , GENERAL_GROUP , QVariant::Bool } + , { StartInCurrentSessionDir , "StartInCurrentSessionDir" , GENERAL_GROUP , QVariant::Bool } + , { SilenceSeconds, "SilenceSeconds" , GENERAL_GROUP , QVariant::Int } + , { TerminalColumns, "TerminalColumns" , GENERAL_GROUP , QVariant::Int } + , { TerminalRows, "TerminalRows" , GENERAL_GROUP , QVariant::Int } + + // Appearance + , { Font , "Font" , APPEARANCE_GROUP , QVariant::Font } + , { ColorScheme , "ColorScheme" , APPEARANCE_GROUP , QVariant::String } + , { ColorScheme , "colors" , 0 , QVariant::String } + , { AntiAliasFonts, "AntiAliasFonts" , APPEARANCE_GROUP , QVariant::Bool } + , { BoldIntense, "BoldIntense", APPEARANCE_GROUP, QVariant::Bool } + , { LineSpacing , "LineSpacing" , APPEARANCE_GROUP , QVariant::Int } + + // Keyboard + , { KeyBindings , "KeyBindings" , KEYBOARD_GROUP , QVariant::String } + + // Scrolling + , { HistoryMode , "HistoryMode" , SCROLLING_GROUP , QVariant::Int } + , { HistorySize , "HistorySize" , SCROLLING_GROUP , QVariant::Int } + , { ScrollBarPosition , "ScrollBarPosition" , SCROLLING_GROUP , QVariant::Int } + , { ScrollFullPage , "ScrollFullPage" , SCROLLING_GROUP , QVariant::Bool } + + // Terminal Features + , { BlinkingTextEnabled , "BlinkingTextEnabled" , TERMINAL_GROUP , QVariant::Bool } + , { FlowControlEnabled , "FlowControlEnabled" , TERMINAL_GROUP , QVariant::Bool } + , { BidiRenderingEnabled , "BidiRenderingEnabled" , TERMINAL_GROUP , QVariant::Bool } + , { BlinkingCursorEnabled , "BlinkingCursorEnabled" , TERMINAL_GROUP , QVariant::Bool } + , { BellMode , "BellMode" , TERMINAL_GROUP , QVariant::Int } + + // Cursor + , { UseCustomCursorColor , "UseCustomCursorColor" , CURSOR_GROUP , QVariant::Bool} + , { CursorShape , "CursorShape" , CURSOR_GROUP , QVariant::Int} + , { CustomCursorColor , "CustomCursorColor" , CURSOR_GROUP , QVariant::Color } + + // Interaction + , { WordCharacters , "WordCharacters" , INTERACTION_GROUP , QVariant::String } + , { TripleClickMode , "TripleClickMode" , INTERACTION_GROUP , QVariant::Int } + , { UnderlineLinksEnabled , "UnderlineLinksEnabled" , INTERACTION_GROUP , QVariant::Bool } + , { OpenLinksByDirectClickEnabled , "OpenLinksByDirectClickEnabled" , INTERACTION_GROUP , QVariant::Bool } + , { CtrlRequiredForDrag, "CtrlRequiredForDrag" , INTERACTION_GROUP , QVariant::Bool } + , { AutoCopySelectedText , "AutoCopySelectedText" , INTERACTION_GROUP , QVariant::Bool } + , { TrimTrailingSpacesInSelectedText , "TrimTrailingSpacesInSelectedText" , INTERACTION_GROUP , QVariant::Bool } + , { PasteFromSelectionEnabled , "PasteFromSelectionEnabled" , INTERACTION_GROUP , QVariant::Bool } + , { PasteFromClipboardEnabled , "PasteFromClipboardEnabled" , INTERACTION_GROUP , QVariant::Bool } + , { MiddleClickPasteMode, "MiddleClickPasteMode" , INTERACTION_GROUP , QVariant::Int } + , { MouseWheelZoomEnabled, "MouseWheelZoomEnabled", INTERACTION_GROUP, QVariant::Bool } + + // Encoding + , { DefaultEncoding , "DefaultEncoding" , ENCODING_GROUP , QVariant::String } + + , { (Property)0 , 0 , 0, QVariant::Invalid } +}; + +QHash Profile::PropertyInfoByName; +QHash Profile::PropertyInfoByProperty; + +void Profile::fillTableWithDefaultNames() +{ + static bool filledDefaults = false; + + if (filledDefaults) + return; + + const PropertyInfo* iter = DefaultPropertyNames; + while (iter->name != 0) { + registerProperty(*iter); + iter++; + } + + filledDefaults = true; +} + +FallbackProfile::FallbackProfile() + : Profile() +{ + // Fallback settings + setProperty(Name, i18n("Shell")); + setProperty(UntranslatedName, "Shell"); + // magic path for the fallback profile which is not a valid + // non-directory file name + setProperty(Path, "FALLBACK/"); + setProperty(Command, qgetenv("SHELL")); + setProperty(Arguments, QStringList() << qgetenv("SHELL")); + setProperty(Icon, "utilities-terminal"); + setProperty(Environment, QStringList() << "TERM=xterm"); + setProperty(LocalTabTitleFormat, "%d : %n"); + setProperty(RemoteTabTitleFormat, "(%u) %H"); + setProperty(ShowTerminalSizeHint, true); + setProperty(StartInCurrentSessionDir, true); + setProperty(MenuIndex, "0"); + setProperty(SilenceSeconds, 10); + setProperty(TerminalColumns, 80); + setProperty(TerminalRows, 40); + setProperty(MouseWheelZoomEnabled, true); + + setProperty(KeyBindings, "default"); + setProperty(ColorScheme, "Oxygen"); //use DarkPastels when is start support blue ncurses UI properly + setProperty(Font, KGlobalSettings::fixedFont()); + + setProperty(HistoryMode, Enum::FixedSizeHistory); + setProperty(HistorySize, 1000); + setProperty(ScrollBarPosition, Enum::ScrollBarRight); + setProperty(ScrollFullPage, false); + + setProperty(FlowControlEnabled, true); + setProperty(BlinkingTextEnabled, true); + setProperty(UnderlineLinksEnabled, true); + setProperty(OpenLinksByDirectClickEnabled, false); + setProperty(CtrlRequiredForDrag, true); + setProperty(AutoCopySelectedText, false); + setProperty(TrimTrailingSpacesInSelectedText, false); + setProperty(PasteFromSelectionEnabled, true); + setProperty(PasteFromClipboardEnabled, false); + setProperty(MiddleClickPasteMode, Enum::PasteFromX11Selection); + setProperty(TripleClickMode, Enum::SelectWholeLine); + + setProperty(BlinkingCursorEnabled, false); + setProperty(BidiRenderingEnabled, true); + setProperty(LineSpacing, 0); + setProperty(CursorShape, Enum::BlockCursor); + setProperty(UseCustomCursorColor, false); + setProperty(CustomCursorColor, Qt::black); + setProperty(BellMode, Enum::NotifyBell); + + setProperty(DefaultEncoding, QString(QTextCodec::codecForLocale()->name())); + setProperty(AntiAliasFonts, true); + setProperty(BoldIntense, true); + + // default taken from KDE 3 + setProperty(WordCharacters, ":@-./_~?&=%+#"); + + // Fallback should not be shown in menus + setHidden(true); +} +Profile::Profile(Profile::Ptr parent) + : _parent(parent) + , _hidden(false) +{ +} +void Profile::clone(Profile::Ptr profile, bool differentOnly) +{ + const PropertyInfo* properties = DefaultPropertyNames; + while (properties->name != 0) { + Property current = properties->property; + QVariant otherValue = profile->property(current); + switch (current) { + case Name: + case Path: + break; + default: + if (!differentOnly || + property(current) != otherValue) { + setProperty(current, otherValue); + } + } + properties++; + } +} +Profile::~Profile() +{ +} +bool Profile::isHidden() const +{ + return _hidden; +} +void Profile::setHidden(bool hidden) +{ + _hidden = hidden; +} + +void Profile::setParent(Profile::Ptr parent) +{ + _parent = parent; +} +const Profile::Ptr Profile::parent() const +{ + return _parent; +} + +bool Profile::isEmpty() const +{ + return _propertyValues.isEmpty(); +} +QHash Profile::setProperties() const +{ + return _propertyValues; +} +void Profile::setProperty(Property property , const QVariant& value) +{ + _propertyValues.insert(property, value); +} +bool Profile::isPropertySet(Property property) const +{ + return _propertyValues.contains(property); +} + +Profile::Property Profile::lookupByName(const QString& name) +{ + // insert default names into table the first time this is called + fillTableWithDefaultNames(); + + return PropertyInfoByName[name.toLower()].property; +} + +void Profile::registerProperty(const PropertyInfo& info) +{ + PropertyInfoByName.insert(QString(info.name).toLower(), info); + + // only allow one property -> name map + // (multiple name -> property mappings are allowed though) + if (!PropertyInfoByProperty.contains(info.property)) + PropertyInfoByProperty.insert(info.property, info); +} + +int Profile::menuIndexAsInt() const +{ + bool ok; + int index = menuIndex().toInt(&ok, 10); + if (ok) + return index; + else + return 0; +} + +const QStringList Profile::propertiesInfoList() const +{ + QStringList info; + const PropertyInfo* iter = DefaultPropertyNames; + while (iter->name != 0) { + info << QString(iter->name) + " : " + QString(QVariant(iter->type).typeName()); + iter++; + } + + return info; +} + +QHash ProfileCommandParser::parse(const QString& input) +{ + QHash changes; + + // regular expression to parse profile change requests. + // + // format: property=value;property=value ... + // + // where 'property' is a word consisting only of characters from A-Z + // where 'value' is any sequence of characters other than a semi-colon + // + static QRegExp regExp("([a-zA-Z]+)=([^;]+)"); + + int offset = 0; + while (regExp.indexIn(input, offset) != -1) { + if (regExp.capturedTexts().count() == 3) { + Profile::Property property = Profile::lookupByName( + regExp.capturedTexts()[1]); + const QString value = regExp.capturedTexts()[2]; + changes.insert(property, value); + } + + offset = input.indexOf(';', offset) + 1; + if (offset == 0) + break; + } + + return changes; +} + +void ProfileGroup::updateValues() +{ + const PropertyInfo* properties = Profile::DefaultPropertyNames; + while (properties->name != 0) { + // the profile group does not store a value for some properties + // (eg. name, path) if even they are equal between profiles - + // + // the exception is when the group has only one profile in which + // case it behaves like a standard Profile + if (_profiles.count() > 1 && + !canInheritProperty(properties->property)) { + properties++; + continue; + } + + QVariant value; + for (int i = 0; i < _profiles.count(); i++) { + QVariant profileValue = _profiles[i]->property(properties->property); + if (value.isNull()) + value = profileValue; + else if (value != profileValue) { + value = QVariant(); + break; + } + } + Profile::setProperty(properties->property, value); + properties++; + } +} +void ProfileGroup::setProperty(Property property, const QVariant& value) +{ + if (_profiles.count() > 1 && !canInheritProperty(property)) + return; + + Profile::setProperty(property, value); + foreach(Profile::Ptr profile, _profiles) { + profile->setProperty(property, value); + } +} + diff --git a/konsole/src/Profile.h b/konsole/src/Profile.h new file mode 100644 index 00000000..3b218176 --- /dev/null +++ b/konsole/src/Profile.h @@ -0,0 +1,694 @@ +/* + This source file is part of Konsole, a terminal emulator. + + Copyright 2007-2008 by Robert Knight + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +#ifndef PROFILE_H +#define PROFILE_H + +// Qt +#include +#include +#include +#include +#include + +// KDE +#include + +// Konsole +#include "konsoleprivate_export.h" + +namespace Konsole +{ +class ProfileGroup; + +/** + * Represents a terminal set-up which can be used to + * set the initial state of new terminal sessions or applied + * to existing sessions. Profiles consist of a number of named + * properties, which can be retrieved using property() and + * set using setProperty(). isPropertySet() can be used to check + * whether a particular property has been set in a profile. + * + * Profiles support a simple form of inheritance. When a new Profile + * is constructed, a pointer to a parent profile can be passed to + * the constructor. When querying a particular property of a profile + * using property(), the profile will return its own value for that + * property if one has been set or otherwise it will return the + * parent's value for that property. + * + * Profiles can be loaded from disk using ProfileReader instances + * and saved to disk using ProfileWriter instances. + */ +class KONSOLEPRIVATE_EXPORT Profile : public QSharedData +{ + friend class KDE4ProfileReader; + friend class KDE4ProfileWriter; + friend class ProfileGroup; + +public: + typedef KSharedPtr Ptr; + typedef KSharedPtr GroupPtr; + + /** + * This enum describes the available properties + * which a Profile may consist of. + * + * Properties can be set using setProperty() and read + * using property() + */ + enum Property { + /** (QString) Path to the profile's configuration file on-disk. */ + Path, + /** (QString) The descriptive name of this profile. */ + Name, + /** (QString) The untranslated name of this profile. + * Warning: this is an internal property. Do not touch it. + */ + UntranslatedName, + /** (QString) The name of the icon associated with this profile. + * This is used in menus and tabs to represent the profile. + */ + Icon, + /** (QString) The command to execute ( excluding arguments ) when + * creating a new terminal session using this profile. + */ + Command, + /** (QStringList) The arguments which are passed to the program + * specified by the Command property when creating a new terminal + * session using this profile. + */ + Arguments, + /** (QStringList) Additional environment variables (in the form of + * NAME=VALUE pairs) which are passed to the program specified by + * the Command property when creating a new terminal session using + * this profile. + */ + Environment, + /** (QString) The initial working directory for sessions created + * using this profile. + */ + Directory, + /** (QString) The format used for tab titles when running normal + * commands. + */ + LocalTabTitleFormat, + /** (QString) The format used for tab titles when the session is + * running a remote command (eg. SSH) + */ + RemoteTabTitleFormat, + /** (bool) Specifies whether show hint for terminal size after + * resizing the application window. + */ + ShowTerminalSizeHint, + /** (QFont) The font to use in terminal displays using this profile. */ + Font, + /** (QString) The name of the color scheme to use in terminal + * displays using this profile. + * Color schemes are managed by the ColorSchemeManager class. + */ + ColorScheme, + /** (QString) The name of the key bindings. + * Key bindings are managed by the KeyboardTranslatorManager class. + */ + KeyBindings, + /** (HistoryModeEnum) Specifies the storage type used for keeping + * the output produced by terminal sessions using this profile. + * + * See Enum::HistoryModeEnum + */ + HistoryMode, + /** (int) Specifies the number of lines of output to remember in + * terminal sessions using this profile. Once the limit is reached, + * the oldest lines are lost if the HistoryMode property is + * FixedSizeHistory + */ + HistorySize, + /** (ScrollBarPositionEnum) Specifies the position of the scroll bar + * in terminal displays using this profile. + * + * See Enum::ScrollBarPositionEnum + */ + ScrollBarPosition, + /** (bool) Specifies whether the PageUp/Down will scroll the full + * height or half height. + */ + ScrollFullPage, + /** (bool) Specifies whether the terminal will enable Bidirectional + * text display + */ + BidiRenderingEnabled, + /** (bool) Specifies whether text in terminal displays is allowed + * to blink. + */ + BlinkingTextEnabled, + /** (bool) Specifies whether the flow control keys (typically Ctrl+S, + * Ctrl+Q) have any effect. Also known as Xon/Xoff + */ + FlowControlEnabled, + /** (int) Specifies the pixels between the terminal lines. + */ + LineSpacing, + /** (bool) Specifies whether the cursor blinks ( in a manner similar + * to text editing applications ) + */ + BlinkingCursorEnabled, + /** (bool) If true, terminal displays use a fixed color to draw the + * cursor, specified by the CustomCursorColor property. Otherwise + * the cursor changes color to match the character underneath it. + */ + UseCustomCursorColor, + /** (CursorShapeEnum) The shape used by terminal displays to + * represent the cursor. + * + * See Enum::CursorShapeEnum + */ + CursorShape, + /** (QColor) The color used by terminal displays to draw the cursor. + * Only applicable if the UseCustomCursorColor property is true. + */ + CustomCursorColor, + /** (QString) A string consisting of the characters used to delimit + * words when selecting text in the terminal display. + */ + WordCharacters, + /** (TripleClickModeEnum) Specifies which part of current line should + * be selected with triple click action. + * + * See Enum::TripleClickModeEnum + */ + TripleClickMode, + /** (bool) If true, text that matches a link or an email address is + * underlined when hovered by the mouse pointer. + */ + UnderlineLinksEnabled, + /** (bool) If true, links can be opened by direct mouse click.*/ + OpenLinksByDirectClickEnabled, + /** (bool) If true, control key must be pressed to click and drag selected text. */ + CtrlRequiredForDrag, + /** (bool) If true, automatically copy selected text into the clipboard */ + AutoCopySelectedText, + /** (bool) If true, trailing spaces are trimmed in selected text */ + TrimTrailingSpacesInSelectedText, + /** (bool) If true, middle mouse button pastes from X Selection */ + PasteFromSelectionEnabled, + /** (bool) If true, middle mouse button pastes from Clipboard */ + PasteFromClipboardEnabled, + /** (MiddleClickPasteModeEnum) Specifies the source from which mouse + * middle click pastes data. + * + * See Enum::MiddleClickPasteModeEnum + */ + MiddleClickPasteMode, + /** (String) Default text codec */ + DefaultEncoding, + /** (bool) Whether fonts should be aliased or not */ + AntiAliasFonts, + /** (bool) Whether character with intense colors should be rendered + * in bold font or just in bright color. */ + BoldIntense, + /** (bool) Whether new sessions should be started in the same + * directory as the currently active session. + */ + StartInCurrentSessionDir, + /** (int) Specifies the threshold of detected silence in seconds. */ + SilenceSeconds, + /** (BellModeEnum) Specifies the behavior of bell. + * + * See Enum::BellModeEnum + */ + BellMode, + /** (int) Specifies the preferred columns. */ + TerminalColumns, + /** (int) Specifies the preferred rows. */ + TerminalRows, + /** Index of profile in the File Menu + * WARNING: this is currently an internal field, which is + * expected to be zero on disk. Do not modify it manually. + * + * In future, the format might be #.#.# to account for levels + */ + MenuIndex, + /** (bool) If true, mouse wheel scroll with Ctrl key pressed + * increases/decreases the terminal font size. + */ + MouseWheelZoomEnabled + }; + + /** + * Constructs a new profile + * + * @param parent The parent profile. When querying the value of a + * property using property(), if the property has not been set in this + * profile then the parent's value for the property will be returned. + */ + explicit Profile(Ptr parent = Ptr()); + virtual ~Profile(); + + /** + * Copies all properties except Name and Path from the specified @p + * profile into this profile + * + * @param profile The profile to copy properties from + * @param differentOnly If true, only properties in @p profile which have + * a different value from this profile's current value (either set via + * setProperty() or inherited from the parent profile) will be set. + */ + void clone(Ptr profile, bool differentOnly = true); + + /** + * Changes the parent profile. When calling the property() method, + * if the specified property has not been set for this profile, + * the parent's value for the property will be returned instead. + */ + void setParent(Ptr parent); + + /** Returns the parent profile. */ + const Ptr parent() const; + + /** Returns this profile as a group or null if this profile is not a + * group. + */ + const GroupPtr asGroup() const; + GroupPtr asGroup(); + + /** + * Returns the current value of the specified @p property, cast to type T. + * Internally properties are stored using the QVariant type and cast to T + * using QVariant::value(); + * + * If the specified @p property has not been set in this profile, + * and a non-null parent was specified in the Profile's constructor, + * the parent's value for @p property will be returned. + */ + template + T property(Property property) const; + + /** Sets the value of the specified @p property to @p value. */ + virtual void setProperty(Property property, const QVariant& value); + /** Returns true if the specified property has been set in this Profile + * instance. + */ + virtual bool isPropertySet(Property property) const; + + /** Returns a map of the properties set in this Profile instance. */ + virtual QHash setProperties() const; + + /** Returns true if no properties have been set in this Profile instance. */ + bool isEmpty() const; + + /** + * Returns true if this is a 'hidden' profile which should not be + * displayed in menus or saved to disk. + * + * This is used for the fallback profile, in case there are no profiles on + * disk which can be loaded, or for overlay profiles created to handle + * command-line arguments which change profile properties. + */ + bool isHidden() const; + + /** Specifies whether this is a hidden profile. See isHidden() */ + void setHidden(bool hidden); + + // + // Convenience methods for property() and setProperty() go here + // + + /** Convenience method for property(Profile::Path) */ + QString path() const { + return property(Profile::Path); + } + + /** Convenience method for property(Profile::Name) */ + QString name() const { + return property(Profile::Name); + } + + /** Convenience method for property(Profile::UntranslatedName) */ + QString untranslatedName() const { + return property(Profile::UntranslatedName); + } + + /** Convenience method for property(Profile::Directory) */ + QString defaultWorkingDirectory() const { + return property(Profile::Directory); + } + + /** Convenience method for property(Profile::Icon) */ + QString icon() const { + return property(Profile::Icon); + } + + /** Convenience method for property(Profile::Command) */ + QString command() const { + return property(Profile::Command); + } + + /** Convenience method for property(Profile::Arguments) */ + QStringList arguments() const { + return property(Profile::Arguments); + } + + /** Convenience method for property(Profile::LocalTabTitleFormat) */ + QString localTabTitleFormat() const { + return property(Profile::LocalTabTitleFormat); + } + + /** Convenience method for property(Profile::RemoteTabTitleFormat) */ + QString remoteTabTitleFormat() const { + return property(Profile::RemoteTabTitleFormat); + } + + /** Convenience method for property(Profile::ShowTerminalSizeHint) */ + bool showTerminalSizeHint() const { + return property(Profile::ShowTerminalSizeHint); + } + + /** Convenience method for property(Profile::Font) */ + QFont font() const { + return property(Profile::Font); + } + + /** Convenience method for property(Profile::ColorScheme) */ + QString colorScheme() const { + return property(Profile::ColorScheme); + } + + /** Convenience method for property(Profile::Environment) */ + QStringList environment() const { + return property(Profile::Environment); + } + + /** Convenience method for property(Profile::KeyBindings) */ + QString keyBindings() const { + return property(Profile::KeyBindings); + } + + /** Convenience method for property(Profile::HistorySize) */ + int historySize() const { + return property(Profile::HistorySize); + } + + /** Convenience method for property(Profile::BidiRenderingEnabled) */ + bool bidiRenderingEnabled() const { + return property(Profile::BidiRenderingEnabled); + } + + /** Convenience method for property(Profile::LineSpacing) */ + int lineSpacing() const { + return property(Profile::LineSpacing); + } + + + /** Convenience method for property(Profile::BlinkingTextEnabled) */ + bool blinkingTextEnabled() const { + return property(Profile::BlinkingTextEnabled); + } + + /** Convenience method for property(Profile::MouseWheelZoomEnabled) */ + bool mouseWheelZoomEnabled() const { + return property(Profile::MouseWheelZoomEnabled); + } + + /** Convenience method for property(Profile::BlinkingCursorEnabled) */ + bool blinkingCursorEnabled() const { + return property(Profile::BlinkingCursorEnabled); + } + + /** Convenience method for property(Profile::FlowControlEnabled) */ + bool flowControlEnabled() const { + return property(Profile::FlowControlEnabled); + } + + /** Convenience method for property(Profile::UseCustomCursorColor) */ + bool useCustomCursorColor() const { + return property(Profile::UseCustomCursorColor); + } + + /** Convenience method for property(Profile::CustomCursorColor) */ + QColor customCursorColor() const { + return property(Profile::CustomCursorColor); + } + + /** Convenience method for property(Profile::WordCharacters) */ + QString wordCharacters() const { + return property(Profile::WordCharacters); + } + + /** Convenience method for property(Profile::UnderlineLinksEnabled) */ + bool underlineLinksEnabled() const { + return property(Profile::UnderlineLinksEnabled); + } + + bool autoCopySelectedText() const { + return property(Profile::AutoCopySelectedText); + } + + /** Convenience method for property(Profile::DefaultEncoding) */ + QString defaultEncoding() const { + return property(Profile::DefaultEncoding); + } + + /** Convenience method for property(Profile::AntiAliasFonts) */ + bool antiAliasFonts() const { + return property(Profile::AntiAliasFonts); + } + + /** Convenience method for property(Profile::BoldIntense) */ + bool boldIntense() const { + return property(Profile::BoldIntense); + } + + /** Convenience method for property(Profile::StartInCurrentSessionDir) */ + bool startInCurrentSessionDir() const { + return property(Profile::StartInCurrentSessionDir); + } + + /** Convenience method for property(Profile::SilenceSeconds) */ + int silenceSeconds() const { + return property(Profile::SilenceSeconds); + } + + /** Convenience method for property(Profile::TerminalColumns) */ + int terminalColumns() const { + return property(Profile::TerminalColumns); + } + + /** Convenience method for property(Profile::TerminalRows) */ + int terminalRows() const { + return property(Profile::TerminalRows); + } + + /** Convenience method for property(Profile::MenuIndex) */ + QString menuIndex() const { + return property(Profile::MenuIndex); + } + + int menuIndexAsInt() const; + + /** Return a list of all properties names and their type + * (for use with -p option). + */ + const QStringList propertiesInfoList() const; + + /** + * Returns the element from the Property enum associated with the + * specified @p name. + * + * @param name The name of the property to look for, this is case + * insensitive. + */ + static Property lookupByName(const QString& name); + +private: + struct PropertyInfo; + // Defines a new property, this property is then available + // to all Profile instances. + static void registerProperty(const PropertyInfo& info); + + // fills the table with default names for profile properties + // the first time it is called. + // subsequent calls return immediately + static void fillTableWithDefaultNames(); + + // returns true if the property can be inherited + static bool canInheritProperty(Property property); + + QHash _propertyValues; + Ptr _parent; + + bool _hidden; + + static QHash PropertyInfoByName; + static QHash PropertyInfoByProperty; + + // Describes a property. Each property has a name and group + // which is used when saving/loading the profile. + struct PropertyInfo { + Property property; + const char* name; + const char* group; + QVariant::Type type; + }; + static const PropertyInfo DefaultPropertyNames[]; +}; + +inline bool Profile::canInheritProperty(Property aProperty) +{ + return aProperty != Name && aProperty != Path; +} + +template +inline T Profile::property(Property aProperty) const +{ + return property(aProperty).value(); +} +template <> +inline QVariant Profile::property(Property aProperty) const +{ + if (_propertyValues.contains(aProperty)) { + return _propertyValues[aProperty]; + } else if (_parent && canInheritProperty(aProperty)) { + return _parent->property(aProperty); + } else { + return QVariant(); + } +} + +/** + * A profile which contains a number of default settings for various + * properties. This can be used as a parent for other profiles or a + * fallback in case a profile cannot be loaded from disk. + */ +class FallbackProfile : public Profile +{ +public: + FallbackProfile(); +}; + +/** + * A composite profile which allows a group of profiles to be treated as one. + * When setting a property, the new value is applied to all profiles in the + * group. When reading a property, if all profiles in the group have the same + * value then that value is returned, otherwise the result is null. + * + * Profiles can be added to the group using addProfile(). When all profiles + * have been added updateValues() must be called + * to sync the group's property values with those of the group's profiles. + * + * The Profile::Name and Profile::Path properties are unique to individual + * profiles, setting these properties on a ProfileGroup has no effect. + */ +class KONSOLEPRIVATE_EXPORT ProfileGroup : public Profile +{ +public: + typedef KSharedPtr Ptr; + + /** Construct a new profile group, which is hidden by default. */ + explicit ProfileGroup(Profile::Ptr parent = Profile::Ptr()); + + /** Add a profile to the group. Calling setProperty() will update this + * profile. When creating a group, add the profiles to the group then + * call updateValues() to make the group's property values reflect the + * profiles currently in the group. + */ + void addProfile(Profile::Ptr profile) { + _profiles.append(profile); + } + + /** Remove a profile from the group. Calling setProperty() will no longer + * affect this profile. */ + void removeProfile(Profile::Ptr profile) { + _profiles.removeAll(profile); + } + + /** Returns the profiles in this group .*/ + QList profiles() const { + return _profiles; + } + + /** + * Updates the property values in this ProfileGroup to match those from + * the group's profiles() + * + * For each available property, if each profile in the group has the same + * value then the ProfileGroup will use that value for the property. + * Otherwise the value for the property will be set to a null QVariant + * + * Some properties such as the name and the path of the profile + * will always be set to null if the group has more than one profile. + */ + void updateValues(); + + /** Sets the value of @p property in each of the group's profiles to + * @p value. + */ + void setProperty(Property property, const QVariant& value); + +private: + QList _profiles; +}; +inline ProfileGroup::ProfileGroup(Profile::Ptr profileParent) + : Profile(profileParent) +{ + setHidden(true); +} +inline const Profile::GroupPtr Profile::asGroup() const +{ + const Profile::GroupPtr ptr(dynamic_cast( + const_cast(this))); + return ptr; +} +inline Profile::GroupPtr Profile::asGroup() +{ + return Profile::GroupPtr(dynamic_cast(this)); +} + +/** + * Parses an input string consisting of property names + * and assigned values and returns a table of properties + * and values. + * + * The input string will typically look like this: + * + * @code + * PropertyName=Value;PropertyName=Value ... + * @endcode + * + * For example: + * + * @code + * Icon=konsole;Directory=/home/bob + * @endcode + */ +class KONSOLEPRIVATE_EXPORT ProfileCommandParser +{ +public: + /** + * Parses an input string consisting of property names + * and assigned values and returns a table of + * properties and values. + */ + QHash parse(const QString& input); +}; +} +Q_DECLARE_METATYPE(Konsole::Profile::Ptr) + +#endif // PROFILE_H diff --git a/konsole/src/ProfileList.cpp b/konsole/src/ProfileList.cpp new file mode 100644 index 00000000..7b4010c9 --- /dev/null +++ b/konsole/src/ProfileList.cpp @@ -0,0 +1,172 @@ +/* + Copyright 2006-2008 by Robert Knight + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +// Own +#include "ProfileList.h" + +// Qt +#include +#include + +// KDE +#include +#include +#include + +// Konsole +#include "ProfileManager.h" + +using Konsole::Profile; +using Konsole::ProfileList; + +ProfileList::ProfileList(bool addShortcuts , QObject* parent) + : QObject(parent) + , _addShortcuts(addShortcuts) + , _emptyListAction(0) +{ + // construct the list of favorite profiles + _group = new QActionGroup(this); + + // Even when there are no favorite profiles, allow user to + // create new tabs using the default profile from the menu + _emptyListAction = new QAction(i18n("Default profile"), _group); + + // TODO - Handle re-sorts when user changes profile names + ProfileManager* manager = ProfileManager::instance(); + QList favoriteProfiles = manager->sortedFavorites(); + + foreach(const Profile::Ptr& profile, favoriteProfiles) { + favoriteChanged(profile, true); + } + + connect(_group, SIGNAL(triggered(QAction*)), this, SLOT(triggered(QAction*))); + + // listen for future changes to the profiles + connect(manager, SIGNAL(favoriteStatusChanged(Profile::Ptr,bool)), this, + SLOT(favoriteChanged(Profile::Ptr,bool))); + connect(manager, SIGNAL(shortcutChanged(Profile::Ptr,QKeySequence)), this, + SLOT(shortcutChanged(Profile::Ptr,QKeySequence))); + connect(manager, SIGNAL(profileChanged(Profile::Ptr)), this, + SLOT(profileChanged(Profile::Ptr))); +} +void ProfileList::updateEmptyAction() +{ + Q_ASSERT(_group); + Q_ASSERT(_emptyListAction); + + // show this action only when it is the only action in the group + const bool showEmptyAction = (_group->actions().count() == 1); + + if (showEmptyAction != _emptyListAction->isVisible()) + _emptyListAction->setVisible(showEmptyAction); +} +QAction* ProfileList::actionForProfile(Profile::Ptr profile) const +{ + foreach(QAction* action, _group->actions()) { + if (action->data().value() == profile) + return action; + } + return 0; // not found +} + +void ProfileList::profileChanged(Profile::Ptr profile) +{ + QAction* action = actionForProfile(profile); + if (action) + updateAction(action, profile); +} + +void ProfileList::updateAction(QAction* action , Profile::Ptr profile) +{ + Q_ASSERT(action); + Q_ASSERT(profile); + + action->setText(profile->name()); + action->setIcon(KIcon(profile->icon())); +} +void ProfileList::shortcutChanged(Profile::Ptr profile, const QKeySequence& sequence) +{ + if (!_addShortcuts) + return; + + QAction* action = actionForProfile(profile); + + if (action) { + action->setShortcut(sequence); + } +} +void ProfileList::syncWidgetActions(QWidget* widget, bool sync) +{ + if (!sync) { + _registeredWidgets.remove(widget); + return; + } + + _registeredWidgets.insert(widget); + + const QList currentActions = widget->actions(); + foreach(QAction * currentAction, currentActions) { + widget->removeAction(currentAction); + } + + widget->addActions(_group->actions()); +} +void ProfileList::favoriteChanged(Profile::Ptr profile, bool isFavorite) +{ + ProfileManager* manager = ProfileManager::instance(); + + if (isFavorite) { + QAction* action = new QAction(_group); + action->setData(QVariant::fromValue(profile)); + + if (_addShortcuts) { + action->setShortcut(manager->shortcut(profile)); + } + + updateAction(action, profile); + + foreach(QWidget * widget, _registeredWidgets) { + widget->addAction(action); + } + emit actionsChanged(_group->actions()); + } else { + QAction* action = actionForProfile(profile); + + if (action) { + _group->removeAction(action); + foreach(QWidget * widget, _registeredWidgets) { + widget->removeAction(action); + } + emit actionsChanged(_group->actions()); + } + } + + updateEmptyAction(); +} +void ProfileList::triggered(QAction* action) +{ + emit profileSelected(action->data().value()); +} + +QList ProfileList::actions() +{ + return _group->actions(); +} + +#include "moc_ProfileList.cpp" diff --git a/konsole/src/ProfileList.h b/konsole/src/ProfileList.h new file mode 100644 index 00000000..4a3d59aa --- /dev/null +++ b/konsole/src/ProfileList.h @@ -0,0 +1,107 @@ +/* + Copyright 2006-2008 by Robert Knight + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +#ifndef PROFILELIST_H +#define PROFILELIST_H + +// Qt +#include +#include +#include + +// Konsole +#include "Profile.h" +#include "konsoleprivate_export.h" + +#include +#include +#include + +namespace Konsole +{ +class Profile; + +/** + * ProfileList provides a list of actions which represent session profiles + * that a SessionManager can create a session from. + * + * These actions can be plugged into a GUI. + * + * Currently only profiles marked as favorites in the SessionManager are included. + * + * The user-data associated with each session can be passed to the createProfile() method of the + * SessionManager to create a new terminal session. + */ +class KONSOLEPRIVATE_EXPORT ProfileList : public QObject +{ + Q_OBJECT + +public: + /** + * Constructs a new profile list which displays profiles + * that can be used to create session + * + * @param addShortcuts True if the shortcuts associated with profiles + * in the session manager should be added to the actions + * @param parent The parent GUI object + */ + ProfileList(bool addShortcuts , QObject* parent); + + /** + * Returns a list of actions representing profiles + * + * The user-data associated with each action is the corresponding profile + */ + QList actions(); + + /** TODO: Document me */ + void syncWidgetActions(QWidget* widget, bool sync); +signals: + /** + * Emitted when the user selects an action from the list. + * + * @param profile The profile to select + */ + void profileSelected(Profile::Ptr profile); + /** + * Emitted when the list of actions changes. + */ + void actionsChanged(const QList& actions); + +private slots: + void triggered(QAction* action); + void favoriteChanged(Profile::Ptr profile, bool isFavorite); + void profileChanged(Profile::Ptr profile); + void shortcutChanged(Profile::Ptr profile, const QKeySequence& sequence); + +private: + QAction* actionForProfile(Profile::Ptr profile) const; + void updateAction(QAction* action , Profile::Ptr profile); + void updateEmptyAction(); + + QActionGroup* _group; + bool _addShortcuts; + + // action to show when the list is empty + QAction* _emptyListAction; + QSet _registeredWidgets; +}; +} + +#endif // PROFILELIST_H diff --git a/konsole/src/ProfileManager.cpp b/konsole/src/ProfileManager.cpp new file mode 100644 index 00000000..0b5ac0ad --- /dev/null +++ b/konsole/src/ProfileManager.cpp @@ -0,0 +1,634 @@ +/* + This source file is part of Konsole, a terminal emulator. + + Copyright 2006-2008 by Robert Knight + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +// Own +#include "ProfileManager.h" + +// Qt +#include +#include + +// KDE +#include +#include +#include +#include +#include + +// Konsole +#include "ProfileReader.h" +#include "ProfileWriter.h" + +using namespace Konsole; + +static bool profileIndexLessThan(const Profile::Ptr& p1, const Profile::Ptr& p2) +{ + return p1->menuIndexAsInt() <= p2->menuIndexAsInt(); +} + +static bool profileNameLessThan(const Profile::Ptr& p1, const Profile::Ptr& p2) +{ + return QString::localeAwareCompare(p1->name(), p2->name()) <= 0; +} + +static bool stringLessThan(const QString& p1, const QString& p2) +{ + return QString::localeAwareCompare(p1, p2) <= 0; +} + +static void sortByIndexProfileList(QList& list) +{ + qStableSort(list.begin(), list.end(), profileIndexLessThan); +} + +static void sortByNameProfileList(QList& list) +{ + qStableSort(list.begin(), list.end(), profileNameLessThan); +} + +ProfileManager::ProfileManager() + : _loadedAllProfiles(false) + , _loadedFavorites(false) +{ + //load fallback profile + _fallbackProfile = Profile::Ptr(new FallbackProfile); + addProfile(_fallbackProfile); + + // lookup the default profile specified in rc + // for stand-alone Konsole, appConfig is just konsolerc + // for konsolepart, appConfig might be yakuakerc, dolphinrc, katerc... + KSharedConfigPtr appConfig = KGlobal::config(); + KConfigGroup group = appConfig->group("Desktop Entry"); + QString defaultProfileFileName = group.readEntry("DefaultProfile", ""); + + // if the hosting application of konsolepart does not specify its own + // default profile, use the default profile of stand-alone Konsole. + if (defaultProfileFileName.isEmpty()) { + KSharedConfigPtr konsoleConfig = KSharedConfig::openConfig("konsolerc"); + group = konsoleConfig->group("Desktop Entry"); + defaultProfileFileName = group.readEntry("DefaultProfile", "Shell.profile"); + } + + // load the default profile + const QString path = KStandardDirs::locate("data", "konsole/" + defaultProfileFileName); + if (!path.isEmpty()) { + Profile::Ptr profile = loadProfile(path); + if (profile) + _defaultProfile = profile; + } + + Q_ASSERT(_profiles.count() > 0); + Q_ASSERT(_defaultProfile); + + // get shortcuts and paths of profiles associated with + // them - this doesn't load the shortcuts themselves, + // that is done on-demand. + loadShortcuts(); +} + +ProfileManager::~ProfileManager() +{ +} + +K_GLOBAL_STATIC(ProfileManager , theProfileManager) +ProfileManager* ProfileManager::instance() +{ + return theProfileManager; +} + +Profile::Ptr ProfileManager::loadProfile(const QString& shortPath) +{ + // the fallback profile has a 'special' path name, "FALLBACK/" + if (shortPath == _fallbackProfile->path()) + return _fallbackProfile; + + QString path = shortPath; + + // add a suggested suffix and relative prefix if missing + QFileInfo fileInfo(path); + + if (fileInfo.isDir()) + return Profile::Ptr(); + + if (fileInfo.suffix() != "profile") + path.append(".profile"); + if (fileInfo.path().isEmpty() || fileInfo.path() == ".") + path.prepend(QString("konsole") + QDir::separator()); + + // if the file is not an absolute path, look it up + if (!fileInfo.isAbsolute()) + path = KStandardDirs::locate("data", path); + + // if the file is not found, return immediately + if (path.isEmpty()) { + return Profile::Ptr(); + } + + // check that we have not already loaded this profile + foreach(const Profile::Ptr& profile, _profiles) { + if (profile->path() == path) + return profile; + } + + // guard to prevent problems if a profile specifies itself as its parent + // or if there is recursion in the "inheritance" chain + // (eg. two profiles, A and B, specifying each other as their parents) + static QStack recursionGuard; + PopStackOnExit popGuardOnExit(recursionGuard); + + if (recursionGuard.contains(path)) { + kWarning() << "Ignoring attempt to load profile recursively from" << path; + return _fallbackProfile; + } else { + recursionGuard.push(path); + } + + // load the profile + ProfileReader* reader = new KDE4ProfileReader; + + Profile::Ptr newProfile = Profile::Ptr(new Profile(fallbackProfile())); + newProfile->setProperty(Profile::Path, path); + + QString parentProfilePath; + bool result = reader->readProfile(path, newProfile, parentProfilePath); + + if (!parentProfilePath.isEmpty()) { + Profile::Ptr parentProfile = loadProfile(parentProfilePath); + newProfile->setParent(parentProfile); + } + + delete reader; + + if (!result) { + kWarning() << "Could not load profile from " << path; + return Profile::Ptr(); + } else { + addProfile(newProfile); + return newProfile; + } +} +QStringList ProfileManager::availableProfilePaths() const +{ + KDE4ProfileReader kde4Reader; + + QStringList paths; + paths += kde4Reader.findProfiles(); + + qStableSort(paths.begin(), paths.end(), stringLessThan); + + return paths; +} + +QStringList ProfileManager::availableProfileNames() const +{ + QStringList names; + + foreach(Profile::Ptr profile, ProfileManager::instance()->allProfiles()) { + if (!profile->isHidden()) { + names.push_back(profile->name()); + } + } + + qStableSort(names.begin(), names.end(), stringLessThan); + + return names; +} + +void ProfileManager::loadAllProfiles() +{ + if (_loadedAllProfiles) + return; + + const QStringList& paths = availableProfilePaths(); + foreach(const QString& path, paths) { + loadProfile(path); + } + + _loadedAllProfiles = true; +} + +void ProfileManager::sortProfiles(QList& list) +{ + QList lackingIndices; + QList havingIndices; + + for (int i = 0; i < list.size(); ++i) { + // dis-regard the fallback profile + if (list.at(i)->path() == _fallbackProfile->path()) + continue; + + if (list.at(i)->menuIndexAsInt() == 0) + lackingIndices.append(list.at(i)); + else + havingIndices.append(list.at(i)); + } + + // sort by index + sortByIndexProfileList(havingIndices); + + // sort alphabetically those w/o an index + sortByNameProfileList(lackingIndices); + + // Put those with indices in sequential order w/o any gaps + int i = 0; + for (i = 0; i < havingIndices.size(); ++i) { + Profile::Ptr tempProfile = havingIndices.at(i); + tempProfile->setProperty(Profile::MenuIndex, QString::number(i + 1)); + havingIndices.replace(i, tempProfile); + } + // Put those w/o indices in sequential order + for (int j = 0; j < lackingIndices.size(); ++j) { + Profile::Ptr tempProfile = lackingIndices.at(j); + tempProfile->setProperty(Profile::MenuIndex, QString::number(j + 1 + i)); + lackingIndices.replace(j, tempProfile); + } + + // combine the 2 list: first those who had indices + list.clear(); + list.append(havingIndices); + list.append(lackingIndices); +} + +void ProfileManager::saveSettings() +{ + // save default profile + saveDefaultProfile(); + + // save shortcuts + saveShortcuts(); + + // save favorites + saveFavorites(); + + // ensure default/favorites/shortcuts settings are synced into disk + KSharedConfigPtr appConfig = KGlobal::config(); + appConfig->sync(); +} + +QList ProfileManager::sortedFavorites() +{ + QList favorites = findFavorites().toList(); + + sortProfiles(favorites); + return favorites; +} + +QList ProfileManager::allProfiles() +{ + loadAllProfiles(); + + return _profiles.toList(); +} + +QList ProfileManager::loadedProfiles() const +{ + return _profiles.toList(); +} + +Profile::Ptr ProfileManager::defaultProfile() const +{ + return _defaultProfile; +} +Profile::Ptr ProfileManager::fallbackProfile() const +{ + return _fallbackProfile; +} + +QString ProfileManager::saveProfile(Profile::Ptr profile) +{ + ProfileWriter* writer = new KDE4ProfileWriter; + + QString newPath = writer->getPath(profile); + + writer->writeProfile(newPath, profile); + + delete writer; + + return newPath; +} + +void ProfileManager::changeProfile(Profile::Ptr profile, + QHash propertyMap, bool persistent) +{ + Q_ASSERT(profile); + + // insert the changes into the existing Profile instance + QListIterator iter(propertyMap.keys()); + while (iter.hasNext()) { + const Profile::Property property = iter.next(); + profile->setProperty(property, propertyMap[property]); + } + + // never save a profile with empty name into disk! + persistent = persistent && !profile->name().isEmpty(); + + // when changing a group, iterate through the profiles + // in the group and call changeProfile() on each of them + // + // this is so that each profile in the group, the profile is + // applied, a change notification is emitted and the profile + // is saved to disk + ProfileGroup::Ptr group = profile->asGroup(); + if (group) { + foreach(const Profile::Ptr & profile, group->profiles()) { + changeProfile(profile, propertyMap, persistent); + } + return; + } + + // notify the world about the change + emit profileChanged(profile); + + // save changes to disk, unless the profile is hidden, in which case + // it has no file on disk + if (persistent && !profile->isHidden()) { + profile->setProperty(Profile::Path, saveProfile(profile)); + } +} + +void ProfileManager::addProfile(Profile::Ptr profile) +{ + if (_profiles.isEmpty()) + _defaultProfile = profile; + + _profiles.insert(profile); + + emit profileAdded(profile); +} + +bool ProfileManager::deleteProfile(Profile::Ptr profile) +{ + bool wasDefault = (profile == defaultProfile()); + + if (profile) { + // try to delete the config file + if (profile->isPropertySet(Profile::Path) && QFile::exists(profile->path())) { + if (!QFile::remove(profile->path())) { + kWarning() << "Could not delete profile: " << profile->path() + << "The file is most likely in a directory which is read-only."; + + return false; + } + } + + // remove from favorites, profile list, shortcut list etc. + setFavorite(profile, false); + setShortcut(profile, QKeySequence()); + _profiles.remove(profile); + + // mark the profile as hidden so that it does not show up in the + // Manage Profiles dialog and is not saved to disk + profile->setHidden(true); + } + + // if we just deleted the default profile, + // replace it with a random profile from the list + if (wasDefault) { + setDefaultProfile(_profiles.toList().first()); + } + + emit profileRemoved(profile); + + return true; +} + +void ProfileManager::setDefaultProfile(Profile::Ptr profile) +{ + Q_ASSERT(_profiles.contains(profile)); + + _defaultProfile = profile; +} + +void ProfileManager::saveDefaultProfile() +{ + QString path = _defaultProfile->path(); + + if (path.isEmpty()) + path = KDE4ProfileWriter().getPath(_defaultProfile); + + QFileInfo fileInfo(path); + + KSharedConfigPtr appConfig = KGlobal::config(); + KConfigGroup group = appConfig->group("Desktop Entry"); + group.writeEntry("DefaultProfile", fileInfo.fileName()); +} + +QSet ProfileManager::findFavorites() +{ + loadFavorites(); + + return _favorites; +} +void ProfileManager::setFavorite(Profile::Ptr profile , bool favorite) +{ + if (!_profiles.contains(profile)) + addProfile(profile); + + if (favorite && !_favorites.contains(profile)) { + _favorites.insert(profile); + emit favoriteStatusChanged(profile, favorite); + } else if (!favorite && _favorites.contains(profile)) { + _favorites.remove(profile); + emit favoriteStatusChanged(profile, favorite); + } +} +void ProfileManager::loadShortcuts() +{ + KSharedConfigPtr appConfig = KGlobal::config(); + KConfigGroup shortcutGroup = appConfig->group("Profile Shortcuts"); + + QMap entries = shortcutGroup.entryMap(); + + QMapIterator iter(entries); + while (iter.hasNext()) { + iter.next(); + + QKeySequence shortcut = QKeySequence::fromString(iter.key()); + QString profilePath = iter.value(); + + ShortcutData data; + + // if the file is not an absolute path, look it up + QFileInfo fileInfo(profilePath); + if (!fileInfo.isAbsolute()) { + profilePath = KStandardDirs::locate("data", "konsole/" + profilePath); + } + + data.profilePath = profilePath; + _shortcuts.insert(shortcut, data); + } +} +void ProfileManager::saveShortcuts() +{ + KSharedConfigPtr appConfig = KGlobal::config(); + KConfigGroup shortcutGroup = appConfig->group("Profile Shortcuts"); + shortcutGroup.deleteGroup(); + + QMapIterator iter(_shortcuts); + while (iter.hasNext()) { + iter.next(); + + QString shortcutString = iter.key().toString(); + + // if the profile path in "Profile Shortcuts" is an absolute path, + // take the profile name + QFileInfo fileInfo(iter.value().profilePath); + QString profileName; + if (fileInfo.isAbsolute()) { + // Check to see if file is under KDE's data locations. If not, + // store full path. + QString location = KGlobal::dirs()->locate("data", + "konsole/" + fileInfo.fileName()); + if (location.isEmpty()) { + profileName = iter.value().profilePath; + } else { + profileName = fileInfo.fileName(); + } + } else { + profileName = iter.value().profilePath; + } + + shortcutGroup.writeEntry(shortcutString, profileName); + } +} +void ProfileManager::setShortcut(Profile::Ptr profile , + const QKeySequence& keySequence) +{ + QKeySequence existingShortcut = shortcut(profile); + _shortcuts.remove(existingShortcut); + + if (keySequence.isEmpty()) + return; + + ShortcutData data; + data.profileKey = profile; + data.profilePath = profile->path(); + // TODO - This won't work if the profile doesn't + // have a path yet + _shortcuts.insert(keySequence, data); + + emit shortcutChanged(profile, keySequence); +} +void ProfileManager::loadFavorites() +{ + if (_loadedFavorites) + return; + + KSharedConfigPtr appConfig = KGlobal::config(); + KConfigGroup favoriteGroup = appConfig->group("Favorite Profiles"); + + QSet favoriteSet; + + if (favoriteGroup.hasKey("Favorites")) { + QStringList list = favoriteGroup.readEntry("Favorites", QStringList()); + favoriteSet = QSet::fromList(list); + } else { + // if there is no favorites key at all, mark the + // supplied 'Shell.profile' as the only favorite + favoriteSet << "Shell.profile"; + } + + // look for favorites among those already loaded + foreach(const Profile::Ptr& profile, _profiles) { + const QString& path = profile->path(); + if (favoriteSet.contains(path)) { + _favorites.insert(profile); + favoriteSet.remove(path); + } + } + // load any remaining favorites + foreach(const QString& favorite, favoriteSet) { + Profile::Ptr profile = loadProfile(favorite); + if (profile) + _favorites.insert(profile); + } + + _loadedFavorites = true; +} +void ProfileManager::saveFavorites() +{ + KSharedConfigPtr appConfig = KGlobal::config(); + KConfigGroup favoriteGroup = appConfig->group("Favorite Profiles"); + + QStringList paths; + foreach(const Profile::Ptr& profile, _favorites) { + Q_ASSERT(_profiles.contains(profile) && profile); + + QFileInfo fileInfo(profile->path()); + QString profileName; + + if (fileInfo.isAbsolute()) { + // Check to see if file is under KDE's data locations. If not, + // store full path. + QString location = KGlobal::dirs()->locate("data", + "konsole/" + fileInfo.fileName()); + if (location.isEmpty()) { + profileName = profile->path(); + } else { + profileName = fileInfo.fileName(); + } + } else { + profileName = profile->path(); + } + + paths << profileName; + } + + favoriteGroup.writeEntry("Favorites", paths); +} + +QList ProfileManager::shortcuts() +{ + return _shortcuts.keys(); +} + +Profile::Ptr ProfileManager::findByShortcut(const QKeySequence& shortcut) +{ + Q_ASSERT(_shortcuts.contains(shortcut)); + + if (!_shortcuts[shortcut].profileKey) { + Profile::Ptr key = loadProfile(_shortcuts[shortcut].profilePath); + if (!key) { + _shortcuts.remove(shortcut); + return Profile::Ptr(); + } + _shortcuts[shortcut].profileKey = key; + } + + return _shortcuts[shortcut].profileKey; +} + + +QKeySequence ProfileManager::shortcut(Profile::Ptr profile) const +{ + QMapIterator iter(_shortcuts); + while (iter.hasNext()) { + iter.next(); + if (iter.value().profileKey == profile + || iter.value().profilePath == profile->path()) + return iter.key(); + } + + return QKeySequence(); +} + +#include "moc_ProfileManager.cpp" + diff --git a/konsole/src/ProfileManager.h b/konsole/src/ProfileManager.h new file mode 100644 index 00000000..25091825 --- /dev/null +++ b/konsole/src/ProfileManager.h @@ -0,0 +1,309 @@ +/* + This source file is part of Konsole, a terminal emulator. + + Copyright 2006-2008 by Robert Knight + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +#ifndef PROFILEMANAGER_H +#define PROFILEMANAGER_H + +// Qt +#include +#include +#include +#include +#include +#include +#include + +// Konsole +#include "Profile.h" + +namespace Konsole +{ +/** + * Manages profiles which specify various settings for terminal sessions + * and their displays. + * + * Profiles in the manager have a concept of favorite status, which can be used + * by widgets and dialogs in the application decide which profiles to list and + * how to display them. The favorite status of a profile can be altered using + * setFavorite() and retrieved using isFavorite() + */ +class KONSOLEPRIVATE_EXPORT ProfileManager : public QObject +{ + Q_OBJECT + +public: + /** + * Constructs a new profile manager and loads information about the available + * profiles. + */ + ProfileManager(); + + /** + * Destroys the ProfileManager. + */ + virtual ~ProfileManager(); + + /** + * Returns the profile manager instance. + */ + static ProfileManager* instance(); + + /** + * Returns a list of all available profiles + * + * Initially only the profile currently set as the default is loaded. + * + * Favorite profiles are loaded automatically when findFavorites() is called. + * + * When this method is called, it calls loadAllProfiles() internally to + * ensure all available profiles are loaded and usable. + */ + QList allProfiles(); + + /** + * Returns a list of already loaded profiles + */ + QList loadedProfiles() const; + + /** + * Loads all available profiles. This involves reading each + * profile configuration file from disk and parsing it. + * Therefore it should only be done when necessary. + */ + void loadAllProfiles(); + + /** + * Loads a profile from the specified path and registers + * it with the ProfileManager. + * + * @p path may be relative or absolute. The path may just be the + * base name of the profile to load (eg. if the profile's full path + * is "/My Profile.profile" then both + * "konsole/My Profile.profile" , "My Profile.profile" and + * "My Profile" will be accepted) + * + * @return Pointer to a profile which can be passed to + * SessionManager::createSession() to create a new session using + * this profile. + */ + Profile::Ptr loadProfile(const QString& path); + + /** + * Searches for available profiles on-disk and returns a list + * of paths of profiles which can be loaded. + */ + QStringList availableProfilePaths() const; + + /** + * Returns a list of names of all available profiles + */ + QStringList availableProfileNames() const; + + /** + * Registers a new type of session. + * The favorite status of the session ( as returned by isFavorite() ) is set to false by default. + */ + void addProfile(Profile::Ptr type); + + /** + * Deletes the configuration file used to store a profile. + * The profile will continue to exist while sessions are still using it. The profile + * will be marked as hidden (see Profile::setHidden() ) so that it does not show + * up in profile lists and future changes to the profile are not stored to disk. + * + * Returns true if the profile was successfully deleted or false otherwise. + */ + bool deleteProfile(Profile::Ptr profile); + + /** + * Updates a @p profile with the changes specified in @p propertyMap. + * + * All sessions currently using the profile will be updated to reflect the new settings. + * + * After the profile is updated, the profileChanged() signal will be emitted. + * + * @param profile The profile to change + * @param propertyMap A map between profile properties and values describing the changes + * @param persistent If true, the changes are saved to the profile's configuration file, + * set this to false if you want to preview possible changes to a profile but do not + * wish to make them permanent. + */ + void changeProfile(Profile::Ptr profile , QHash propertyMap, + bool persistent = true); + + /** + * Sets the @p profile as the default profile for creating new sessions + */ + void setDefaultProfile(Profile::Ptr profile); + + /** + * Returns a Profile object describing the default profile + */ + Profile::Ptr defaultProfile() const; + + /** + * Returns a Profile object with hard-coded settings which is always available. + * This can be used as a parent for new profiles which provides suitable default settings + * for all properties. + */ + Profile::Ptr fallbackProfile() const; + + /** + * Specifies whether a profile should be included in the user's + * list of favorite profiles. + */ + void setFavorite(Profile::Ptr profile , bool favorite); + + /** + * Returns the set of the user's favorite profiles. + */ + QSet findFavorites(); + + QList sortedFavorites(); + + /* + * Sorts the profile list by menuindex; those without an menuindex, sort by name. + * The menuindex list is first and then the non-menuindex list. + * + * @param list The profile list to sort + */ + void sortProfiles(QList& list); + + /** + * Associates a shortcut with a particular profile. + */ + void setShortcut(Profile::Ptr profile , const QKeySequence& shortcut); + + /** Returns the shortcut associated with a particular profile. */ + QKeySequence shortcut(Profile::Ptr profile) const; + + /** + * Returns the list of shortcut key sequences which + * can be used to create new sessions based on + * existing profiles + * + * When one of the shortcuts is activated, + * use findByShortcut() to load the profile associated + * with the shortcut. + */ + QList shortcuts(); + + /** + * Finds and loads the profile associated with + * the specified @p shortcut key sequence and returns a pointer to it. + */ + Profile::Ptr findByShortcut(const QKeySequence& shortcut); + +signals: + + /** Emitted when a profile is added to the manager. */ + void profileAdded(Profile::Ptr ptr); + /** Emitted when a profile is removed from the manager. */ + void profileRemoved(Profile::Ptr ptr); + /** Emitted when a profile's properties are modified. */ + void profileChanged(Profile::Ptr ptr); + + /** + * Emitted when the favorite status of a profile changes. + * + * @param profile The profile to change + * @param favorite Specifies whether the profile is a favorite or not + */ + void favoriteStatusChanged(Profile::Ptr profile , bool favorite); + + /** + * Emitted when the shortcut for a profile is changed. + * + * @param profile The profile whose status was changed + * @param newShortcut The new shortcut key sequence for the profile + */ + void shortcutChanged(Profile::Ptr profile , const QKeySequence& newShortcut); + +public slots: + /** Saves settings (favorites, shortcuts, default profile etc.) to disk. */ + void saveSettings(); + +protected slots: + +private slots: + +private: + // loads the mappings between shortcut key sequences and + // profile paths + void loadShortcuts(); + // saves the mappings between shortcut key sequences and + // profile paths + void saveShortcuts(); + + //loads the set of favorite profiles + void loadFavorites(); + //saves the set of favorite profiles + void saveFavorites(); + + // records which profile is set as the default profile + // Note: it does not save the profile itself into disk. That is + // what saveProfile() does. + void saveDefaultProfile(); + + // saves a profile to a file + // returns the path to which the profile was saved, which will + // be the same as the path property of profile if valid or a newly generated path + // otherwise + QString saveProfile(Profile::Ptr profile); + + QSet _profiles; // list of all loaded profiles + QSet _favorites; // list of favorite profiles + + Profile::Ptr _defaultProfile; + Profile::Ptr _fallbackProfile; + + bool _loadedAllProfiles; // set to true after loadAllProfiles has been called + bool _loadedFavorites; // set to true after loadFavorites has been called + + struct ShortcutData { + Profile::Ptr profileKey; + QString profilePath; + }; + QMap _shortcuts; // shortcut keys -> profile path +}; + +/** + * PopStackOnExit is a utility to remove all values from a QStack which are added during + * the lifetime of a PopStackOnExit instance. + * + * When a PopStackOnExit instance is destroyed, elements are removed from the stack + * until the stack count is reduced the value when the PopStackOnExit instance was created. + */ +template +class PopStackOnExit +{ +public: + explicit PopStackOnExit(QStack& stack) : _stack(stack) , _count(stack.count()) {} + ~PopStackOnExit() { + while (_stack.count() > _count) + _stack.pop(); + } +private: + QStack& _stack; + int _count; +}; +} +#endif //PROFILEMANAGER_H diff --git a/konsole/src/ProfileReader.cpp b/konsole/src/ProfileReader.cpp new file mode 100644 index 00000000..053f3e1a --- /dev/null +++ b/konsole/src/ProfileReader.cpp @@ -0,0 +1,96 @@ +/* + This source file is part of Konsole, a terminal emulator. + + Copyright 2006-2008 by Robert Knight + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +// Own +#include "ProfileReader.h" + +// Qt +#include + +// KDE +#include +#include +#include +#include + +// Konsole +#include "ShellCommand.h" + +using namespace Konsole; + +// FIXME: A dup line from Profile.cpp - redo these +static const char GENERAL_GROUP[] = "General"; + +QStringList KDE4ProfileReader::findProfiles() +{ + return KGlobal::dirs()->findAllResources("data", "konsole/*.profile", + KStandardDirs::NoDuplicates); +} +void KDE4ProfileReader::readProperties(const KConfig& config, Profile::Ptr profile, + const Profile::PropertyInfo* properties) +{ + const char* groupName = 0; + KConfigGroup group; + + while (properties->name != 0) { + if (properties->group != 0) { + if (groupName == 0 || qstrcmp(groupName, properties->group) != 0) { + group = config.group(properties->group); + groupName = properties->group; + } + + QString name(properties->name); + + if (group.hasKey(name)) + profile->setProperty(properties->property, + group.readEntry(name, QVariant(properties->type))); + } + + properties++; + } +} + +bool KDE4ProfileReader::readProfile(const QString& path , Profile::Ptr profile , QString& parentProfile) +{ + if (!QFile::exists(path)) + return false; + + KConfig config(path, KConfig::NoGlobals); + + KConfigGroup general = config.group(GENERAL_GROUP); + if (general.hasKey("Parent")) + parentProfile = general.readEntry("Parent"); + + if (general.hasKey("Command")) { + ShellCommand shellCommand(general.readEntry("Command")); + + profile->setProperty(Profile::Command, shellCommand.command()); + profile->setProperty(Profile::Arguments, shellCommand.arguments()); + } + + profile->setProperty(Profile::UntranslatedName, general.readEntryUntranslated("Name")); + + // Read remaining properties + readProperties(config, profile, Profile::DefaultPropertyNames); + + return true; +} + diff --git a/konsole/src/ProfileReader.h b/konsole/src/ProfileReader.h new file mode 100644 index 00000000..73a9e56e --- /dev/null +++ b/konsole/src/ProfileReader.h @@ -0,0 +1,65 @@ +/* + This source file is part of Konsole, a terminal emulator. + + Copyright 2007-2008 by Robert Knight + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +#ifndef PROFILEREADER_H +#define PROFILEREADER_H + +#include "Profile.h" + +class KConfig; + +namespace Konsole +{ +/** Interface for all classes which can load profile settings from a file. */ +class ProfileReader +{ +public: + virtual ~ProfileReader() {} + /** Returns a list of paths to profiles which this reader can read. */ + virtual QStringList findProfiles() { + return QStringList(); + } + /** + * Attempts to read a profile from @p path and + * save the property values described into @p profile. + * + * Returns true if the profile was successfully read or false otherwise. + * + * @param path Path to the profile to read + * @param profile Pointer to the Profile the settings will be read into + * @param parentProfile Receives the name of the parent profile + */ + virtual bool readProfile(const QString& path , Profile::Ptr profile , QString& parentProfile) = 0; +}; + +/** Reads a KDE 4 .profile file. */ +class KDE4ProfileReader : public ProfileReader +{ +public: + virtual QStringList findProfiles(); + virtual bool readProfile(const QString& path , Profile::Ptr profile, QString& parentProfile); +private: + void readProperties(const KConfig& config, Profile::Ptr profile, + const Profile::PropertyInfo* properties); +}; +} + +#endif // PROFILEREADER_H diff --git a/konsole/src/ProfileWriter.cpp b/konsole/src/ProfileWriter.cpp new file mode 100644 index 00000000..143787bf --- /dev/null +++ b/konsole/src/ProfileWriter.cpp @@ -0,0 +1,127 @@ +/* + This source file is part of Konsole, a terminal emulator. + + Copyright 2006-2008 by Robert Knight + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +// Own +#include "ProfileWriter.h" + +// Qt +#include + +// KDE +#include +#include +#include +#include + +// Konsole +#include "ShellCommand.h" + +using namespace Konsole; + +// FIXME: A dup line from Profile.cpp - redo these +static const char GENERAL_GROUP[] = "General"; + +QString KDE4ProfileWriter::getPath(const Profile::Ptr profile) +{ + // both location have trailing slash + static const QString localDataLocation = KGlobal::dirs()->saveLocation("data", "konsole/"); + static const QString systemDataLocation = KStandardDirs::installPath("data") + "konsole/"; + + const QString candidateLocalPath = localDataLocation + profile->untranslatedName() + ".profile"; + QString newPath; + + // when the path property is not set, it means the profile has just + // been created in memory and has never been saved into disk before. + // + // use "name.profile" as filename and save it under $KDEHOME + if (!profile->isPropertySet(Profile::Path)) { + return candidateLocalPath; + } + + // for a system wide profile, save the modified version as + // a local profile under $KDEHOME + if (profile->path().startsWith(systemDataLocation)) { + return candidateLocalPath; + } + + // for a local profile, use its existing path + if (profile->path().startsWith(localDataLocation)) { + newPath = profile->path(); + } else { + // for the ad-hoc profiles in non-standard places + // + // * if its path is writable for user, use its existing path + // * if its path is not writable for user, save it under $KDEHOME + // + QFileInfo fileInfo(profile->path()); + if (fileInfo.isWritable()) { + newPath = profile->path(); + } else { + newPath = candidateLocalPath; + } + } + + return newPath; +} +void KDE4ProfileWriter::writeProperties(KConfig& config, + const Profile::Ptr profile, + const Profile::PropertyInfo* properties) +{ + const char* groupName = 0; + KConfigGroup group; + + while (properties->name != 0) { + if (properties->group != 0) { + if (groupName == 0 || qstrcmp(groupName, properties->group) != 0) { + group = config.group(properties->group); + groupName = properties->group; + } + + if (profile->isPropertySet(properties->property)) + group.writeEntry(QString(properties->name), + profile->property(properties->property)); + } + + properties++; + } +} +bool KDE4ProfileWriter::writeProfile(const QString& path , const Profile::Ptr profile) +{ + KConfig config(path, KConfig::NoGlobals); + + KConfigGroup general = config.group(GENERAL_GROUP); + + // Parent profile if set, when loading the profile in future, the parent + // must be loaded as well if it exists. + if (profile->parent()) + general.writeEntry("Parent", profile->parent()->path()); + + if (profile->isPropertySet(Profile::Command) + || profile->isPropertySet(Profile::Arguments)) + general.writeEntry("Command", + ShellCommand(profile->command(), profile->arguments()).fullCommand()); + + // Write remaining properties + writeProperties(config, profile, Profile::DefaultPropertyNames); + + return true; +} + diff --git a/konsole/src/ProfileWriter.h b/konsole/src/ProfileWriter.h new file mode 100644 index 00000000..973884e3 --- /dev/null +++ b/konsole/src/ProfileWriter.h @@ -0,0 +1,62 @@ +/* + This source file is part of Konsole, a terminal emulator. + + Copyright 2007-2008 by Robert Knight + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +#ifndef PROFILEWRITER_H +#define PROFILEWRITER_H + +#include "Profile.h" + +class KConfig; + +namespace Konsole +{ +/** Interface for all classes which can write profile settings to a file. */ +class ProfileWriter +{ +public: + virtual ~ProfileWriter() {} + /** + * Returns a suitable path-name for writing + * @p profile to. The path-name should be accepted by + * the corresponding ProfileReader class. + */ + virtual QString getPath(const Profile::Ptr profile) = 0; + /** + * Writes the properties and values from @p profile to the file specified + * by @p path. This profile should be readable by the corresponding + * ProfileReader class. + */ + virtual bool writeProfile(const QString& path , const Profile::Ptr profile) = 0; +}; +/** Writes a KDE 4 .profile file. */ +class KONSOLEPRIVATE_EXPORT KDE4ProfileWriter : public ProfileWriter +{ +public: + virtual QString getPath(const Profile::Ptr profile); + virtual bool writeProfile(const QString& path , const Profile::Ptr profile); + +private: + void writeProperties(KConfig& config, const Profile::Ptr profile, + const Profile::PropertyInfo* properties); +}; +} + +#endif // PROFILEWRITER_H diff --git a/konsole/src/Pty.cpp b/konsole/src/Pty.cpp new file mode 100644 index 00000000..564e0da1 --- /dev/null +++ b/konsole/src/Pty.cpp @@ -0,0 +1,309 @@ +/* + This file is part of Konsole, an X terminal. + Copyright 1997,1998 by Lars Doelle + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +// Own +#include "Pty.h" + +// System +#include +#include + +// Qt +#include + +// KDE +#include +#include +#include + +using Konsole::Pty; + +Pty::Pty(int masterFd, QObject* aParent) + : KPtyProcess(masterFd, aParent) +{ + init(); +} + +Pty::Pty(QObject* aParent) + : KPtyProcess(aParent) +{ + init(); +} + +void Pty::init() +{ + _windowColumns = 0; + _windowLines = 0; + _eraseChar = 0; + _xonXoff = true; + _utf8 = true; + + setEraseChar(_eraseChar); + setFlowControlEnabled(_xonXoff); + setUtf8Mode(_utf8); + + setWindowSize(_windowColumns, _windowLines); + + setUseUtmp(true); + setPtyChannels(KPtyProcess::AllChannels); + + connect(pty(), SIGNAL(readyRead()) , this , SLOT(dataReceived())); +} + +Pty::~Pty() +{ +} + +void Pty::sendData(const char* data, int length) +{ + if (length == 0) + return; + + if (!pty()->write(data, length)) { + kWarning() << "Could not send input data to terminal process."; + return; + } +} + +void Pty::dataReceived() +{ + QByteArray data = pty()->readAll(); + emit receivedData(data.constData(), data.count()); +} + +void Pty::setWindowSize(int columns, int lines) +{ + _windowColumns = columns; + _windowLines = lines; + + if (pty()->masterFd() >= 0) + pty()->setWinSize(lines, columns); +} + +QSize Pty::windowSize() const +{ + return QSize(_windowColumns, _windowLines); +} + +void Pty::setFlowControlEnabled(bool enable) +{ + _xonXoff = enable; + + if (pty()->masterFd() >= 0) { + struct ::termios ttmode; + pty()->tcGetAttr(&ttmode); + if (enable) + ttmode.c_iflag |= (IXOFF | IXON); + else + ttmode.c_iflag &= ~(IXOFF | IXON); + + if (!pty()->tcSetAttr(&ttmode)) + kWarning() << "Unable to set terminal attributes."; + } +} + +bool Pty::flowControlEnabled() const +{ + if (pty()->masterFd() >= 0) { + struct ::termios ttmode; + pty()->tcGetAttr(&ttmode); + return ttmode.c_iflag & IXOFF && + ttmode.c_iflag & IXON; + } else { + kWarning() << "Unable to get flow control status, terminal not connected."; + return _xonXoff; + } +} + +void Pty::setUtf8Mode(bool enable) +{ +#if defined(IUTF8) // XXX not a reasonable place to check it. + _utf8 = enable; + + if (pty()->masterFd() >= 0) { + struct ::termios ttmode; + pty()->tcGetAttr(&ttmode); + if (enable) + ttmode.c_iflag |= IUTF8; + else + ttmode.c_iflag &= ~IUTF8; + + if (!pty()->tcSetAttr(&ttmode)) + kWarning() << "Unable to set terminal attributes."; + } +#else + Q_UNUSED(enable); +#endif +} + +void Pty::setEraseChar(char eChar) +{ + _eraseChar = eChar; + + if (pty()->masterFd() >= 0) { + struct ::termios ttmode; + pty()->tcGetAttr(&ttmode); + ttmode.c_cc[VERASE] = eChar; + + if (!pty()->tcSetAttr(&ttmode)) + kWarning() << "Unable to set terminal attributes."; + } +} + +char Pty::eraseChar() const +{ + if (pty()->masterFd() >= 0) { + struct ::termios ttyAttributes; + pty()->tcGetAttr(&ttyAttributes); + return ttyAttributes.c_cc[VERASE]; + } else { + kWarning() << "Unable to get erase char attribute, terminal not connected."; + return _eraseChar; + } +} + +void Pty::setInitialWorkingDirectory(const QString& dir) +{ + QString pwd = dir; + + // remove trailing slash in the path when appropriate + // example: /usr/share/icons/ ==> /usr/share/icons + if (pwd.length() > 1 && pwd.endsWith(QLatin1Char('/'))) { + pwd.chop(1); + } + + setWorkingDirectory(pwd); + + // setting PWD to "." will cause problem for bash & zsh + if (pwd != ".") + setEnv("PWD", pwd); +} + +void Pty::addEnvironmentVariables(const QStringList& environmentVariables) +{ + bool isTermEnvAdded = false; + + foreach(const QString& pair, environmentVariables) { + // split on the first '=' character + const int separator = pair.indexOf('='); + + if (separator >= 0) { + const QString variable = pair.left(separator); + const QString value = pair.mid(separator + 1); + + setEnv(variable, value); + + if (variable == "TERM") { + isTermEnvAdded = true; + } + } + } + + // extra safeguard to make sure $TERM is always set + if (!isTermEnvAdded) { + setEnv("TERM", "xterm"); + } +} + +int Pty::start(const QString& programName, + const QStringList& programArguments, + const QStringList& environmentList) +{ + clearProgram(); + + // For historical reasons, the first argument in programArguments is the + // name of the program to execute, so create a list consisting of all + // but the first argument to pass to setProgram() + Q_ASSERT(programArguments.count() >= 1); + setProgram(programName, programArguments.mid(1)); + + addEnvironmentVariables(environmentList); + + // unless the LANGUAGE environment variable has been set explicitly + // set it to a null string + // this fixes the problem where KCatalog sets the LANGUAGE environment + // variable during the application's startup to something which + // differs from LANG,LC_* etc. and causes programs run from + // the terminal to display messages in the wrong language + // + // this can happen if LANG contains a language which KDE + // does not have a translation for + // + // BR:149300 + setEnv("LANGUAGE", QString(), false /* do not overwrite existing value if any */); + + KProcess::start(); + + if (waitForStarted()) { + return 0; + } else { + return -1; + } +} + +void Pty::setWriteable(bool writeable) +{ + KDE_struct_stat sbuf; + if (KDE::stat(pty()->ttyName(), &sbuf) == 0) { + if (writeable) { + if (KDE::chmod(pty()->ttyName(), sbuf.st_mode | S_IWGRP) < 0) { + kWarning() << "Could not set writeable on" << pty()->ttyName(); + } + } else { + if (KDE::chmod(pty()->ttyName(), sbuf.st_mode & ~(S_IWGRP | S_IWOTH)) < 0) { + kWarning() << "Could not unset writeable on" << pty()->ttyName(); + } + } + } +} + +void Pty::closePty() +{ + pty()->close(); +} + +int Pty::foregroundProcessGroup() const +{ + int foregroundPid = tcgetpgrp(pty()->masterFd()); + + if (foregroundPid != -1) { + return foregroundPid; + } + + return 0; +} + +void Pty::setupChildProcess() +{ + KPtyProcess::setupChildProcess(); + + // reset all signal handlers + // this ensures that terminal applications respond to + // signals generated via key sequences such as Ctrl+C + // (which sends SIGINT) + struct sigaction action; + sigemptyset(&action.sa_mask); + action.sa_handler = SIG_DFL; + action.sa_flags = 0; + for (int signal = 1; signal < NSIG; signal++) + sigaction(signal, &action, 0); +} + +#include "moc_Pty.cpp" diff --git a/konsole/src/Pty.h b/konsole/src/Pty.h new file mode 100644 index 00000000..3954aae8 --- /dev/null +++ b/konsole/src/Pty.h @@ -0,0 +1,191 @@ +/* + This file is part of Konsole, KDE's terminal emulator. + + Copyright 2007-2008 by Robert Knight + Copyright 1997,1998 by Lars Doelle + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +#ifndef PTY_H +#define PTY_H + +// Qt +#include + +// KDE +#include + +// Konsole +#include "konsoleprivate_export.h" + +#include + +namespace Konsole +{ +/** + * The Pty class is used to start the terminal process, + * send data to it, receive data from it and manipulate + * various properties of the pseudo-teletype interface + * used to communicate with the process. + * + * To use this class, construct an instance and connect + * to the sendData slot and receivedData signal to + * send data to or receive data from the process. + * + * To start the terminal process, call the start() method + * with the program name and appropriate arguments. + */ +class KONSOLEPRIVATE_EXPORT Pty: public KPtyProcess +{ + Q_OBJECT + +public: + /** + * Constructs a new Pty. + * + * Connect to the sendData() slot and receivedData() signal to prepare + * for sending and receiving data from the terminal process. + * + * To start the terminal process, call the run() method with the + * name of the program to start and appropriate arguments. + */ + explicit Pty(QObject* parent = 0); + + /** + * Construct a process using an open pty master. + * See KPtyProcess::KPtyProcess() + */ + explicit Pty(int ptyMasterFd, QObject* parent = 0); + + ~Pty(); + + /** + * Starts the terminal process. + * + * Returns 0 if the process was started successfully or non-zero + * otherwise. + * + * @param program Path to the program to start + * @param arguments Arguments to pass to the program being started + * @param environment A list of key=value pairs which will be added + * to the environment for the new process. At the very least this + * should include an assignment for the TERM environment variable. + */ + int start(const QString& program, + const QStringList& arguments, + const QStringList& environment + ); + + /** Control whether the pty device is writeable by group members. */ + void setWriteable(bool writeable); + + /** + * Enables or disables Xon/Xoff flow control. The flow control setting + * may be changed later by a terminal application, so flowControlEnabled() + * may not equal the value of @p on in the previous call to setFlowControlEnabled() + */ + void setFlowControlEnabled(bool on); + + /** Queries the terminal state and returns true if Xon/Xoff flow control is enabled. */ + bool flowControlEnabled() const; + + /** + * Sets the size of the window (in columns and lines of characters) + * used by this teletype. + */ + void setWindowSize(int columns, int lines); + + /** Returns the size of the window used by this teletype. See setWindowSize() */ + QSize windowSize() const; + + /** + * Sets the special character for erasing previous not-yet-erased character. + * See termios(3) for detailed description. + */ + void setEraseChar(char eraseChar); + + /** */ + char eraseChar() const; + + /** + * Sets the initial working directory. + */ + void setInitialWorkingDirectory(const QString& dir); + + /** + * Returns the process id of the teletype's current foreground + * process. This is the process which is currently reading + * input sent to the terminal via. sendData() + * + * If there is a problem reading the foreground process group, + * 0 will be returned. + */ + int foregroundProcessGroup() const; + + /** + * Close the underlying pty master/slave pair. + */ + void closePty(); + +public slots: + /** + * Put the pty into UTF-8 mode on systems which support it. + */ + void setUtf8Mode(bool on); + + /** + * Sends data to the process currently controlling the + * teletype ( whose id is returned by foregroundProcessGroup() ) + * + * @param buffer Pointer to the data to send. + * @param length Length of @p buffer. + */ + void sendData(const char* buffer, int length); + +signals: + /** + * Emitted when a new block of data is received from + * the teletype. + * + * @param buffer Pointer to the data received. + * @param length Length of @p buffer + */ + void receivedData(const char* buffer, int length); + +protected: + void setupChildProcess(); + +private slots: + // called when data is received from the terminal process + void dataReceived(); + +private: + void init(); + + // takes a list of key=value pairs and adds them + // to the environment for the process + void addEnvironmentVariables(const QStringList& environment); + + int _windowColumns; + int _windowLines; + char _eraseChar; + bool _xonXoff; + bool _utf8; +}; +} + +#endif // PTY_H diff --git a/konsole/src/RenameTabDialog.cpp b/konsole/src/RenameTabDialog.cpp new file mode 100644 index 00000000..04498cf4 --- /dev/null +++ b/konsole/src/RenameTabDialog.cpp @@ -0,0 +1,77 @@ +/* + Copyright 2010 by Kurt Hindenburg + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +// Own +#include "RenameTabDialog.h" + +// KDE +#include + +// Konsole +#include "ui_RenameTabDialog.h" + +using Konsole::RenameTabDialog; + +RenameTabDialog::RenameTabDialog(QWidget* parent) + : KDialog(parent) +{ + setCaption(i18n("Rename Tab")); + setButtons(KDialog::Ok | KDialog::Cancel); + + setWindowModality(Qt::WindowModal); + + _ui = new Ui::RenameTabDialog(); + _ui->setupUi(mainWidget()); +} + +RenameTabDialog::~RenameTabDialog() +{ + delete _ui; +} + +void RenameTabDialog::focusTabTitleText() +{ + _ui->renameTabWidget->focusTabTitleText(); +} + +void RenameTabDialog::focusRemoteTabTitleText() +{ + _ui->renameTabWidget->focusRemoteTabTitleText(); +} + +void RenameTabDialog::setTabTitleText(const QString& text) +{ + _ui->renameTabWidget->setTabTitleText(text); +} + +void RenameTabDialog::setRemoteTabTitleText(const QString& text) +{ + _ui->renameTabWidget->setRemoteTabTitleText(text); +} + +QString RenameTabDialog::tabTitleText() const +{ + return _ui->renameTabWidget->tabTitleText(); +} + +QString RenameTabDialog::remoteTabTitleText() const +{ + return _ui->renameTabWidget->remoteTabTitleText(); +} + diff --git a/konsole/src/RenameTabDialog.h b/konsole/src/RenameTabDialog.h new file mode 100644 index 00000000..7608b9c6 --- /dev/null +++ b/konsole/src/RenameTabDialog.h @@ -0,0 +1,53 @@ +/* + Copyright 2010 by Kurt Hindenburg + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +#ifndef RENAMETABDIALOG_H +#define RENAMETABDIALOG_H + +// KDE +#include + +QT_BEGIN_NAMESPACE +namespace Ui { class RenameTabDialog; } +QT_END_NAMESPACE + +namespace Konsole +{ +class RenameTabDialog : public KDialog +{ + Q_OBJECT + +public: + explicit RenameTabDialog(QWidget* parent = 0); + ~RenameTabDialog(); + + QString tabTitleText() const; + QString remoteTabTitleText() const; + void setTabTitleText(const QString&); + void setRemoteTabTitleText(const QString&); + + void focusTabTitleText(); + void focusRemoteTabTitleText(); + +private: + Ui::RenameTabDialog* _ui; +}; +} + +#endif diff --git a/konsole/src/RenameTabDialog.ui b/konsole/src/RenameTabDialog.ui new file mode 100644 index 00000000..41f7f5c5 --- /dev/null +++ b/konsole/src/RenameTabDialog.ui @@ -0,0 +1,28 @@ + + + RenameTabDialog + + + + 0 + 0 + 325 + 110 + + + + + + + + + + + Konsole::RenameTabWidget + QWidget +
    RenameTabWidget.h
    +
    +
    + + +
    diff --git a/konsole/src/RenameTabWidget.cpp b/konsole/src/RenameTabWidget.cpp new file mode 100644 index 00000000..31f55a43 --- /dev/null +++ b/konsole/src/RenameTabWidget.cpp @@ -0,0 +1,97 @@ +/* + Copyright 2010 by Kurt Hindenburg + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +// Own +#include "RenameTabWidget.h" + +// Konsole +#include "ui_RenameTabWidget.h" + +using Konsole::RenameTabWidget; + +RenameTabWidget::RenameTabWidget(QWidget* parent) + : QWidget(parent) +{ + _ui = new Ui::RenameTabWidget(); + _ui->setupUi(this); + + _ui->tabTitleEdit->setClearButtonShown(true); + _ui->remoteTabTitleEdit->setClearButtonShown(true); + + connect(_ui->tabTitleEdit, SIGNAL(textChanged(QString)), this, + SIGNAL(tabTitleFormatChanged(QString))); + connect(_ui->remoteTabTitleEdit, SIGNAL(textChanged(QString)), this, + SIGNAL(remoteTabTitleFormatChanged(QString))); + + _ui->tabTitleFormatButton->setContext(Session::LocalTabTitle); + connect(_ui->tabTitleFormatButton, SIGNAL(dynamicElementSelected(QString)), + this, SLOT(insertTabTitleText(QString))); + + _ui->remoteTabTitleFormatButton->setContext(Session::RemoteTabTitle); + connect(_ui->remoteTabTitleFormatButton, SIGNAL(dynamicElementSelected(QString)), + this, SLOT(insertRemoteTabTitleText(QString))); +} + +RenameTabWidget::~RenameTabWidget() +{ + delete _ui; +} + +void RenameTabWidget::focusTabTitleText() +{ + _ui->tabTitleEdit->setFocus(); +} + +void RenameTabWidget::focusRemoteTabTitleText() +{ + _ui->remoteTabTitleEdit->setFocus(); +} + +void RenameTabWidget::setTabTitleText(const QString& text) +{ + _ui->tabTitleEdit->setText(text); +} + +void RenameTabWidget::setRemoteTabTitleText(const QString& text) +{ + _ui->remoteTabTitleEdit->setText(text); +} + +QString RenameTabWidget::tabTitleText() const +{ + return(_ui->tabTitleEdit->text()); +} + +QString RenameTabWidget::remoteTabTitleText() const +{ + return(_ui->remoteTabTitleEdit->text()); +} + +void RenameTabWidget::insertTabTitleText(const QString& text) +{ + _ui->tabTitleEdit->insert(text); + focusTabTitleText(); +} + +void RenameTabWidget::insertRemoteTabTitleText(const QString& text) +{ + _ui->remoteTabTitleEdit->insert(text); + focusRemoteTabTitleText(); +} + diff --git a/konsole/src/RenameTabWidget.h b/konsole/src/RenameTabWidget.h new file mode 100644 index 00000000..7b8836d4 --- /dev/null +++ b/konsole/src/RenameTabWidget.h @@ -0,0 +1,61 @@ +/* + Copyright 2010 by Kurt Hindenburg + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +#ifndef RENAMETABWIDGET_H +#define RENAMETABWIDGET_H + +// Qt +#include + +QT_BEGIN_NAMESPACE +namespace Ui { class RenameTabWidget; } +QT_END_NAMESPACE + +namespace Konsole +{ +class RenameTabWidget : public QWidget +{ + Q_OBJECT + +public: + explicit RenameTabWidget(QWidget* parent = 0); + ~RenameTabWidget(); + + QString tabTitleText() const; + QString remoteTabTitleText() const; + void setTabTitleText(const QString&); + void setRemoteTabTitleText(const QString&); + + void focusTabTitleText(); + void focusRemoteTabTitleText(); + +signals: + void tabTitleFormatChanged(const QString&); + void remoteTabTitleFormatChanged(const QString&); + +public slots: + void insertTabTitleText(const QString& text); + void insertRemoteTabTitleText(const QString& text); + +private: + Ui::RenameTabWidget* _ui; +}; +} + +#endif diff --git a/konsole/src/RenameTabWidget.ui b/konsole/src/RenameTabWidget.ui new file mode 100644 index 00000000..280c2d72 --- /dev/null +++ b/konsole/src/RenameTabWidget.ui @@ -0,0 +1,82 @@ + + + RenameTabWidget + + + + 0 + 0 + 325 + 110 + + + + + + + + + + true + + + + + + Tab title format: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Normal tab title format + + + + + + + + + + Remote tab title format: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Tab title format used when a remote command (e.g. connection to another computer via SSH) is being executed + + + + + + + + + + + + + + KLineEdit + QLineEdit +
    klineedit.h
    +
    + + Konsole::TabTitleFormatButton + QPushButton +
    TabTitleFormatButton.h
    +
    +
    + + +
    diff --git a/konsole/src/Screen.cpp b/konsole/src/Screen.cpp new file mode 100644 index 00000000..66924841 --- /dev/null +++ b/konsole/src/Screen.cpp @@ -0,0 +1,1387 @@ +/* + This file is part of Konsole, an X terminal. + + Copyright 2007-2008 by Robert Knight + Copyright 1997,1998 by Lars Doelle + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + */ + +// Own +#include "Screen.h" + +// Qt +#include + +// Konsole +#include "konsole_wcwidth.h" +#include "TerminalCharacterDecoder.h" +#include "History.h" +#include "ExtendedCharTable.h" + +using namespace Konsole; + +//FIXME: this is emulation specific. Use false for xterm, true for ANSI. +//FIXME: see if we can get this from terminfo. +const bool BS_CLEARS = false; + +//Macro to convert x,y position on screen to position within an image. +// +//Originally the image was stored as one large contiguous block of +//memory, so a position within the image could be represented as an +//offset from the beginning of the block. For efficiency reasons this +//is no longer the case. +//Many internal parts of this class still use this representation for parameters and so on, +//notably moveImage() and clearImage(). +//This macro converts from an X,Y position into an image offset. +#ifndef loc +#define loc(X,Y) ((Y)*_columns+(X)) +#endif + +const Character Screen::DefaultChar = Character(' ', + CharacterColor(COLOR_SPACE_DEFAULT, DEFAULT_FORE_COLOR), + CharacterColor(COLOR_SPACE_DEFAULT, DEFAULT_BACK_COLOR), + DEFAULT_RENDITION, + false); + +Screen::Screen(int lines, int columns): + _lines(lines), + _columns(columns), + _screenLines(new ImageLine[_lines + 1]), + _screenLinesSize(_lines), + _scrolledLines(0), + _droppedLines(0), + _history(new HistoryScrollNone()), + _cuX(0), + _cuY(0), + _currentRendition(DEFAULT_RENDITION), + _topMargin(0), + _bottomMargin(0), + _selBegin(0), + _selTopLeft(0), + _selBottomRight(0), + _blockSelectionMode(false), + _effectiveForeground(CharacterColor()), + _effectiveBackground(CharacterColor()), + _effectiveRendition(DEFAULT_RENDITION), + _lastPos(-1) +{ + _lineProperties.resize(_lines + 1); + for (int i = 0; i < _lines + 1; i++) + _lineProperties[i] = LINE_DEFAULT; + + initTabStops(); + clearSelection(); + reset(); +} + +Screen::~Screen() +{ + delete[] _screenLines; + delete _history; +} + +void Screen::cursorUp(int n) +//=CUU +{ + if (n == 0) n = 1; // Default + const int stop = _cuY < _topMargin ? 0 : _topMargin; + _cuX = qMin(_columns - 1, _cuX); // nowrap! + _cuY = qMax(stop, _cuY - n); +} + +void Screen::cursorDown(int n) +//=CUD +{ + if (n == 0) n = 1; // Default + const int stop = _cuY > _bottomMargin ? _lines - 1 : _bottomMargin; + _cuX = qMin(_columns - 1, _cuX); // nowrap! + _cuY = qMin(stop, _cuY + n); +} + +void Screen::cursorLeft(int n) +//=CUB +{ + if (n == 0) n = 1; // Default + _cuX = qMin(_columns - 1, _cuX); // nowrap! + _cuX = qMax(0, _cuX - n); +} + +void Screen::cursorRight(int n) +//=CUF +{ + if (n == 0) n = 1; // Default + _cuX = qMin(_columns - 1, _cuX + n); +} + +void Screen::setMargins(int top, int bot) +//=STBM +{ + if (top == 0) top = 1; // Default + if (bot == 0) bot = _lines; // Default + top = top - 1; // Adjust to internal lineno + bot = bot - 1; // Adjust to internal lineno + if (!(0 <= top && top < bot && bot < _lines)) { + //Debug()<<" setRegion("< 0) + _cuY -= 1; +} + +void Screen::nextLine() +//=NEL +{ + toStartOfLine(); + index(); +} + +void Screen::eraseChars(int n) +{ + if (n == 0) n = 1; // Default + const int p = qMax(0, qMin(_cuX + n - 1, _columns - 1)); + clearImage(loc(_cuX, _cuY), loc(p, _cuY), ' '); +} + +void Screen::deleteChars(int n) +{ + Q_ASSERT(n >= 0); + + // always delete at least one char + if (n == 0) + n = 1; + + // if cursor is beyond the end of the line there is nothing to do + if (_cuX >= _screenLines[_cuY].count()) + return; + + if (_cuX + n > _screenLines[_cuY].count()) + n = _screenLines[_cuY].count() - _cuX; + + Q_ASSERT(n >= 0); + Q_ASSERT(_cuX + n <= _screenLines[_cuY].count()); + + _screenLines[_cuY].remove(_cuX, n); + + // Append space(s) with current attributes + Character spaceWithCurrentAttrs(' ', _effectiveForeground, + _effectiveBackground, + _effectiveRendition, false); + + for (int i = 0; i < n; i++) + _screenLines[_cuY].append(spaceWithCurrentAttrs); +} + +void Screen::insertChars(int n) +{ + if (n == 0) n = 1; // Default + + if (_screenLines[_cuY].size() < _cuX) + _screenLines[_cuY].resize(_cuX); + + _screenLines[_cuY].insert(_cuX, n, Character(' ')); + + if (_screenLines[_cuY].count() > _columns) + _screenLines[_cuY].resize(_columns); +} + +void Screen::deleteLines(int n) +{ + if (n == 0) n = 1; // Default + scrollUp(_cuY, n); +} + +void Screen::insertLines(int n) +{ + if (n == 0) n = 1; // Default + scrollDown(_cuY, n); +} + +void Screen::setMode(int m) +{ + _currentModes[m] = true; + switch (m) { + case MODE_Origin : + _cuX = 0; + _cuY = _topMargin; + break; //FIXME: home + } +} + +void Screen::resetMode(int m) +{ + _currentModes[m] = false; + switch (m) { + case MODE_Origin : + _cuX = 0; + _cuY = 0; + break; //FIXME: home + } +} + +void Screen::saveMode(int m) +{ + _savedModes[m] = _currentModes[m]; +} + +void Screen::restoreMode(int m) +{ + _currentModes[m] = _savedModes[m]; +} + +bool Screen::getMode(int m) const +{ + return _currentModes[m]; +} + +void Screen::saveCursor() +{ + _savedState.cursorColumn = _cuX; + _savedState.cursorLine = _cuY; + _savedState.rendition = _currentRendition; + _savedState.foreground = _currentForeground; + _savedState.background = _currentBackground; +} + +void Screen::restoreCursor() +{ + _cuX = qMin(_savedState.cursorColumn, _columns - 1); + _cuY = qMin(_savedState.cursorLine, _lines - 1); + _currentRendition = _savedState.rendition; + _currentForeground = _savedState.foreground; + _currentBackground = _savedState.background; + updateEffectiveRendition(); +} + +void Screen::resizeImage(int new_lines, int new_columns) +{ + if ((new_lines == _lines) && (new_columns == _columns)) return; + + if (_cuY > new_lines - 1) { + // attempt to preserve focus and _lines + _bottomMargin = _lines - 1; //FIXME: margin lost + for (int i = 0; i < _cuY - (new_lines - 1); i++) { + addHistLine(); + scrollUp(0, 1); + } + } + + // create new screen _lines and copy from old to new + + ImageLine* newScreenLines = new ImageLine[new_lines + 1]; + for (int i = 0; i < qMin(_lines, new_lines + 1) ; i++) + newScreenLines[i] = _screenLines[i]; + for (int i = _lines; (i > 0) && (i < new_lines + 1); i++) + newScreenLines[i].resize(new_columns); + + _lineProperties.resize(new_lines + 1); + for (int i = _lines; (i > 0) && (i < new_lines + 1); i++) + _lineProperties[i] = LINE_DEFAULT; + + clearSelection(); + + delete[] _screenLines; + _screenLines = newScreenLines; + _screenLinesSize = new_lines; + + _lines = new_lines; + _columns = new_columns; + _cuX = qMin(_cuX, _columns - 1); + _cuY = qMin(_cuY, _lines - 1); + + // FIXME: try to keep values, evtl. + _topMargin = 0; + _bottomMargin = _lines - 1; + initTabStops(); + clearSelection(); +} + +void Screen::setDefaultMargins() +{ + _topMargin = 0; + _bottomMargin = _lines - 1; +} + +/* + Clarifying rendition here and in the display. + + currently, the display's color table is + 0 1 2 .. 9 10 .. 17 + dft_fg, dft_bg, dim 0..7, intensive 0..7 + + _currentForeground, _currentBackground contain values 0..8; + - 0 = default color + - 1..8 = ansi specified color + + re_fg, re_bg contain values 0..17 + due to the TerminalDisplay's color table + + rendition attributes are + + attr widget screen + -------------- ------ ------ + RE_UNDERLINE XX XX affects foreground only + RE_BLINK XX XX affects foreground only + RE_BOLD XX XX affects foreground only + RE_REVERSE -- XX + RE_TRANSPARENT XX -- affects background only + RE_INTENSIVE XX -- affects foreground only + + Note that RE_BOLD is used in both widget + and screen rendition. Since xterm/vt102 + is to poor to distinguish between bold + (which is a font attribute) and intensive + (which is a color attribute), we translate + this and RE_BOLD in falls eventually apart + into RE_BOLD and RE_INTENSIVE. + */ + +void Screen::reverseRendition(Character& p) const +{ + CharacterColor f = p.foregroundColor; + CharacterColor b = p.backgroundColor; + + p.foregroundColor = b; + p.backgroundColor = f; //p->r &= ~RE_TRANSPARENT; +} + +void Screen::updateEffectiveRendition() +{ + _effectiveRendition = _currentRendition; + if (_currentRendition & RE_REVERSE) { + _effectiveForeground = _currentBackground; + _effectiveBackground = _currentForeground; + } else { + _effectiveForeground = _currentForeground; + _effectiveBackground = _currentBackground; + } + + if (_currentRendition & RE_BOLD) + _effectiveForeground.setIntensive(); +} + +void Screen::copyFromHistory(Character* dest, int startLine, int count) const +{ + Q_ASSERT(startLine >= 0 && count > 0 && startLine + count <= _history->getLines()); + + for (int line = startLine; line < startLine + count; line++) { + const int length = qMin(_columns, _history->getLineLen(line)); + const int destLineOffset = (line - startLine) * _columns; + + _history->getCells(line, 0, length, dest + destLineOffset); + + for (int column = length; column < _columns; column++) + dest[destLineOffset + column] = Screen::DefaultChar; + + // invert selected text + if (_selBegin != -1) { + for (int column = 0; column < _columns; column++) { + if (isSelected(column, line)) { + reverseRendition(dest[destLineOffset + column]); + } + } + } + } +} + +void Screen::copyFromScreen(Character* dest , int startLine , int count) const +{ + Q_ASSERT(startLine >= 0 && count > 0 && startLine + count <= _lines); + + for (int line = startLine; line < (startLine + count) ; line++) { + int srcLineStartIndex = line * _columns; + int destLineStartIndex = (line - startLine) * _columns; + + for (int column = 0; column < _columns; column++) { + int srcIndex = srcLineStartIndex + column; + int destIndex = destLineStartIndex + column; + + dest[destIndex] = _screenLines[srcIndex / _columns].value(srcIndex % _columns, Screen::DefaultChar); + + // invert selected text + if (_selBegin != -1 && isSelected(column, line + _history->getLines())) + reverseRendition(dest[destIndex]); + } + } +} + +void Screen::getImage(Character* dest, int size, int startLine, int endLine) const +{ + Q_ASSERT(startLine >= 0); + Q_ASSERT(endLine >= startLine && endLine < _history->getLines() + _lines); + + const int mergedLines = endLine - startLine + 1; + + Q_ASSERT(size >= mergedLines * _columns); + Q_UNUSED(size); + + const int linesInHistoryBuffer = qBound(0, _history->getLines() - startLine, mergedLines); + const int linesInScreenBuffer = mergedLines - linesInHistoryBuffer; + + // copy _lines from history buffer + if (linesInHistoryBuffer > 0) + copyFromHistory(dest, startLine, linesInHistoryBuffer); + + // copy _lines from screen buffer + if (linesInScreenBuffer > 0) + copyFromScreen(dest + linesInHistoryBuffer * _columns, + startLine + linesInHistoryBuffer - _history->getLines(), + linesInScreenBuffer); + + // invert display when in screen mode + if (getMode(MODE_Screen)) { + for (int i = 0; i < mergedLines * _columns; i++) + reverseRendition(dest[i]); // for reverse display + } + + // mark the character at the current cursor position + int cursorIndex = loc(_cuX, _cuY + linesInHistoryBuffer); + if (getMode(MODE_Cursor) && cursorIndex < _columns * mergedLines) + dest[cursorIndex].rendition |= RE_CURSOR; +} + +QVector Screen::getLineProperties(int startLine , int endLine) const +{ + Q_ASSERT(startLine >= 0); + Q_ASSERT(endLine >= startLine && endLine < _history->getLines() + _lines); + + const int mergedLines = endLine - startLine + 1; + const int linesInHistory = qBound(0, _history->getLines() - startLine, mergedLines); + const int linesInScreen = mergedLines - linesInHistory; + + QVector result(mergedLines); + int index = 0; + + // copy properties for _lines in history + for (int line = startLine; line < startLine + linesInHistory; line++) { + //TODO Support for line properties other than wrapped _lines + if (_history->isWrappedLine(line)) { + result[index] = (LineProperty)(result[index] | LINE_WRAPPED); + } + index++; + } + + // copy properties for _lines in screen buffer + const int firstScreenLine = startLine + linesInHistory - _history->getLines(); + for (int line = firstScreenLine; line < firstScreenLine + linesInScreen; line++) { + result[index] = _lineProperties[line]; + index++; + } + + return result; +} + +void Screen::reset(bool clearScreen) +{ + setMode(MODE_Wrap); + saveMode(MODE_Wrap); // wrap at end of margin + + resetMode(MODE_Origin); + saveMode(MODE_Origin); // position refer to [1,1] + + resetMode(MODE_Insert); + saveMode(MODE_Insert); // overstroke + + setMode(MODE_Cursor); // cursor visible + resetMode(MODE_Screen); // screen not inverse + resetMode(MODE_NewLine); + + _topMargin = 0; + _bottomMargin = _lines - 1; + + setDefaultRendition(); + saveCursor(); + + if (clearScreen) + clear(); +} + +void Screen::clear() +{ + clearEntireScreen(); + home(); +} + +void Screen::backspace() +{ + _cuX = qMin(_columns - 1, _cuX); // nowrap! + _cuX = qMax(0, _cuX - 1); + + if (_screenLines[_cuY].size() < _cuX + 1) + _screenLines[_cuY].resize(_cuX + 1); + + if (BS_CLEARS) { + _screenLines[_cuY][_cuX].character = ' '; + _screenLines[_cuY][_cuX].rendition = _screenLines[_cuY][_cuX].rendition & ~RE_EXTENDED_CHAR; + } +} + +void Screen::tab(int n) +{ + // note that TAB is a format effector (does not write ' '); + if (n == 0) n = 1; + while ((n > 0) && (_cuX < _columns - 1)) { + cursorRight(1); + while ((_cuX < _columns - 1) && !_tabStops[_cuX]) + cursorRight(1); + n--; + } +} + +void Screen::backtab(int n) +{ + // note that TAB is a format effector (does not write ' '); + if (n == 0) n = 1; + while ((n > 0) && (_cuX > 0)) { + cursorLeft(1); + while ((_cuX > 0) && !_tabStops[_cuX]) { + cursorLeft(1); + } + n--; + } +} + +void Screen::clearTabStops() +{ + for (int i = 0; i < _columns; i++) + _tabStops[i] = false; +} + +void Screen::changeTabStop(bool set) +{ + if (_cuX >= _columns) + return; + + _tabStops[_cuX] = set; +} + +void Screen::initTabStops() +{ + _tabStops.resize(_columns); + + // The 1st tabstop has to be one longer than the other. + // i.e. the kids start counting from 0 instead of 1. + // Other programs might behave correctly. Be aware. + for (int i = 0; i < _columns; i++) + _tabStops[i] = (i % 8 == 0 && i != 0); +} + +void Screen::newLine() +{ + if (getMode(MODE_NewLine)) + toStartOfLine(); + + index(); +} + +void Screen::checkSelection(int from, int to) +{ + if (_selBegin == -1) + return; + const int scr_TL = loc(0, _history->getLines()); + //Clear entire selection if it overlaps region [from, to] + if ((_selBottomRight >= (from + scr_TL)) && (_selTopLeft <= (to + scr_TL))) + clearSelection(); +} + +void Screen::displayCharacter(unsigned short c) +{ + // Note that VT100 does wrapping BEFORE putting the character. + // This has impact on the assumption of valid cursor positions. + // We indicate the fact that a newline has to be triggered by + // putting the cursor one right to the last column of the screen. + + int w = konsole_wcwidth(c); + if (w < 0) + return; + else if (w == 0) { + if (QChar(c).category() != QChar::Mark_NonSpacing) + return; + int charToCombineWithX = -1; + int charToCombineWithY = -1; + if (_cuX == 0) { + // We are at the beginning of a line, check + // if previous line has a character at the end we can combine with + if (_cuY > 0 && _columns == _screenLines[_cuY - 1].size()) { + charToCombineWithX = _columns - 1; + charToCombineWithY = _cuY - 1; + } else { + // There is nothing to combine with + // TODO Seems gnome-terminal shows the characters alone + // might be worth investigating how to do that + return; + } + } else { + charToCombineWithX = _cuX - 1; + charToCombineWithY = _cuY; + } + + // Prevent "cat"ing binary files from causing crashes. + if (charToCombineWithX >= _screenLines[charToCombineWithY].size()) { + return; + } + + Character& currentChar = _screenLines[charToCombineWithY][charToCombineWithX]; + if ((currentChar.rendition & RE_EXTENDED_CHAR) == 0) { + const ushort chars[2] = { currentChar.character, c }; + currentChar.rendition |= RE_EXTENDED_CHAR; + currentChar.character = ExtendedCharTable::instance.createExtendedChar(chars, 2); + } else { + ushort extendedCharLength; + const ushort* oldChars = ExtendedCharTable::instance.lookupExtendedChar(currentChar.character, extendedCharLength); + Q_ASSERT(oldChars); + if (oldChars) { + Q_ASSERT(extendedCharLength > 1); + Q_ASSERT(extendedCharLength < 65535); + ushort* chars = new ushort[extendedCharLength + 1]; + memcpy(chars, oldChars, sizeof(ushort) * extendedCharLength); + chars[extendedCharLength] = c; + currentChar.character = ExtendedCharTable::instance.createExtendedChar(chars, extendedCharLength + 1); + delete[] chars; + } + } + return; + } + + if (_cuX + w > _columns) { + if (getMode(MODE_Wrap)) { + _lineProperties[_cuY] = (LineProperty)(_lineProperties[_cuY] | LINE_WRAPPED); + nextLine(); + } else { + _cuX = _columns - w; + } + } + + // ensure current line vector has enough elements + if (_screenLines[_cuY].size() < _cuX + w) { + _screenLines[_cuY].resize(_cuX + w); + } + + if (getMode(MODE_Insert)) insertChars(w); + + _lastPos = loc(_cuX, _cuY); + + // check if selection is still valid. + checkSelection(_lastPos, _lastPos); + + Character& currentChar = _screenLines[_cuY][_cuX]; + + currentChar.character = c; + currentChar.foregroundColor = _effectiveForeground; + currentChar.backgroundColor = _effectiveBackground; + currentChar.rendition = _effectiveRendition; + currentChar.isRealCharacter = true; + + int i = 0; + const int newCursorX = _cuX + w--; + while (w) { + i++; + + if (_screenLines[_cuY].size() < _cuX + i + 1) + _screenLines[_cuY].resize(_cuX + i + 1); + + Character& ch = _screenLines[_cuY][_cuX + i]; + ch.character = 0; + ch.foregroundColor = _effectiveForeground; + ch.backgroundColor = _effectiveBackground; + ch.rendition = _effectiveRendition; + ch.isRealCharacter = false; + + w--; + } + _cuX = newCursorX; +} + +int Screen::scrolledLines() const +{ + return _scrolledLines; +} +int Screen::droppedLines() const +{ + return _droppedLines; +} +void Screen::resetDroppedLines() +{ + _droppedLines = 0; +} +void Screen::resetScrolledLines() +{ + _scrolledLines = 0; +} + +void Screen::scrollUp(int n) +{ + if (n == 0) n = 1; // Default + if (_topMargin == 0) addHistLine(); // history.history + scrollUp(_topMargin, n); +} + +QRect Screen::lastScrolledRegion() const +{ + return _lastScrolledRegion; +} + +void Screen::scrollUp(int from, int n) +{ + if (n <= 0 || from + n > _bottomMargin) return; + + _scrolledLines -= n; + _lastScrolledRegion = QRect(0, _topMargin, _columns - 1, (_bottomMargin - _topMargin)); + + //FIXME: make sure `topMargin', `bottomMargin', `from', `n' is in bounds. + moveImage(loc(0, from), loc(0, from + n), loc(_columns - 1, _bottomMargin)); + clearImage(loc(0, _bottomMargin - n + 1), loc(_columns - 1, _bottomMargin), ' '); +} + +void Screen::scrollDown(int n) +{ + if (n == 0) n = 1; // Default + scrollDown(_topMargin, n); +} + +void Screen::scrollDown(int from, int n) +{ + _scrolledLines += n; + + //FIXME: make sure `topMargin', `bottomMargin', `from', `n' is in bounds. + if (n <= 0) + return; + if (from > _bottomMargin) + return; + if (from + n > _bottomMargin) + n = _bottomMargin - from; + moveImage(loc(0, from + n), loc(0, from), loc(_columns - 1, _bottomMargin - n)); + clearImage(loc(0, from), loc(_columns - 1, from + n - 1), ' '); +} + +void Screen::setCursorYX(int y, int x) +{ + setCursorY(y); + setCursorX(x); +} + +void Screen::setCursorX(int x) +{ + if (x == 0) x = 1; // Default + x -= 1; // Adjust + _cuX = qMax(0, qMin(_columns - 1, x)); +} + +void Screen::setCursorY(int y) +{ + if (y == 0) y = 1; // Default + y -= 1; // Adjust + _cuY = qMax(0, qMin(_lines - 1, y + (getMode(MODE_Origin) ? _topMargin : 0))); +} + +void Screen::home() +{ + _cuX = 0; + _cuY = 0; +} + +void Screen::toStartOfLine() +{ + _cuX = 0; +} + +int Screen::getCursorX() const +{ + return _cuX; +} + +int Screen::getCursorY() const +{ + return _cuY; +} + +void Screen::clearImage(int loca, int loce, char c) +{ + const int scr_TL = loc(0, _history->getLines()); + //FIXME: check positions + + //Clear entire selection if it overlaps region to be moved... + if ((_selBottomRight > (loca + scr_TL)) && (_selTopLeft < (loce + scr_TL))) { + clearSelection(); + } + + const int topLine = loca / _columns; + const int bottomLine = loce / _columns; + + Character clearCh(c, _currentForeground, _currentBackground, DEFAULT_RENDITION, false); + + //if the character being used to clear the area is the same as the + //default character, the affected _lines can simply be shrunk. + const bool isDefaultCh = (clearCh == Screen::DefaultChar); + + for (int y = topLine; y <= bottomLine; y++) { + _lineProperties[y] = 0; + + const int endCol = (y == bottomLine) ? loce % _columns : _columns - 1; + const int startCol = (y == topLine) ? loca % _columns : 0; + + QVector& line = _screenLines[y]; + + if (isDefaultCh && endCol == _columns - 1) { + line.resize(startCol); + } else { + if (line.size() < endCol + 1) + line.resize(endCol + 1); + + Character* data = line.data(); + for (int i = startCol; i <= endCol; i++) + data[i] = clearCh; + } + } +} + +void Screen::moveImage(int dest, int sourceBegin, int sourceEnd) +{ + Q_ASSERT(sourceBegin <= sourceEnd); + + const int lines = (sourceEnd - sourceBegin) / _columns; + + //move screen image and line properties: + //the source and destination areas of the image may overlap, + //so it matters that we do the copy in the right order - + //forwards if dest < sourceBegin or backwards otherwise. + //(search the web for 'memmove implementation' for details) + if (dest < sourceBegin) { + for (int i = 0; i <= lines; i++) { + _screenLines[(dest / _columns) + i ] = _screenLines[(sourceBegin / _columns) + i ]; + _lineProperties[(dest / _columns) + i] = _lineProperties[(sourceBegin / _columns) + i]; + } + } else { + for (int i = lines; i >= 0; i--) { + _screenLines[(dest / _columns) + i ] = _screenLines[(sourceBegin / _columns) + i ]; + _lineProperties[(dest / _columns) + i] = _lineProperties[(sourceBegin / _columns) + i]; + } + } + + if (_lastPos != -1) { + const int diff = dest - sourceBegin; // Scroll by this amount + _lastPos += diff; + if ((_lastPos < 0) || (_lastPos >= (lines * _columns))) + _lastPos = -1; + } + + // Adjust selection to follow scroll. + if (_selBegin != -1) { + const bool beginIsTL = (_selBegin == _selTopLeft); + const int diff = dest - sourceBegin; // Scroll by this amount + const int scr_TL = loc(0, _history->getLines()); + const int srca = sourceBegin + scr_TL; // Translate index from screen to global + const int srce = sourceEnd + scr_TL; // Translate index from screen to global + const int desta = srca + diff; + const int deste = srce + diff; + + if ((_selTopLeft >= srca) && (_selTopLeft <= srce)) + _selTopLeft += diff; + else if ((_selTopLeft >= desta) && (_selTopLeft <= deste)) + _selBottomRight = -1; // Clear selection (see below) + + if ((_selBottomRight >= srca) && (_selBottomRight <= srce)) + _selBottomRight += diff; + else if ((_selBottomRight >= desta) && (_selBottomRight <= deste)) + _selBottomRight = -1; // Clear selection (see below) + + if (_selBottomRight < 0) { + clearSelection(); + } else { + if (_selTopLeft < 0) + _selTopLeft = 0; + } + + if (beginIsTL) + _selBegin = _selTopLeft; + else + _selBegin = _selBottomRight; + } +} + +void Screen::clearToEndOfScreen() +{ + clearImage(loc(_cuX, _cuY), loc(_columns - 1, _lines - 1), ' '); +} + +void Screen::clearToBeginOfScreen() +{ + clearImage(loc(0, 0), loc(_cuX, _cuY), ' '); +} + +void Screen::clearEntireScreen() +{ + // Add entire screen to history + for (int i = 0; i < (_lines - 1); i++) { + addHistLine(); + scrollUp(0, 1); + } + + clearImage(loc(0, 0), loc(_columns - 1, _lines - 1), ' '); +} + +/*! fill screen with 'E' + This is to aid screen alignment + */ + +void Screen::helpAlign() +{ + clearImage(loc(0, 0), loc(_columns - 1, _lines - 1), 'E'); +} + +void Screen::clearToEndOfLine() +{ + clearImage(loc(_cuX, _cuY), loc(_columns - 1, _cuY), ' '); +} + +void Screen::clearToBeginOfLine() +{ + clearImage(loc(0, _cuY), loc(_cuX, _cuY), ' '); +} + +void Screen::clearEntireLine() +{ + clearImage(loc(0, _cuY), loc(_columns - 1, _cuY), ' '); +} + +void Screen::setRendition(int rendention) +{ + _currentRendition |= rendention; + updateEffectiveRendition(); +} + +void Screen::resetRendition(int rendention) +{ + _currentRendition &= ~rendention; + updateEffectiveRendition(); +} + +void Screen::setDefaultRendition() +{ + setForeColor(COLOR_SPACE_DEFAULT, DEFAULT_FORE_COLOR); + setBackColor(COLOR_SPACE_DEFAULT, DEFAULT_BACK_COLOR); + _currentRendition = DEFAULT_RENDITION; + updateEffectiveRendition(); +} + +void Screen::setForeColor(int space, int color) +{ + _currentForeground = CharacterColor(space, color); + + if (_currentForeground.isValid()) + updateEffectiveRendition(); + else + setForeColor(COLOR_SPACE_DEFAULT, DEFAULT_FORE_COLOR); +} + +void Screen::setBackColor(int space, int color) +{ + _currentBackground = CharacterColor(space, color); + + if (_currentBackground.isValid()) + updateEffectiveRendition(); + else + setBackColor(COLOR_SPACE_DEFAULT, DEFAULT_BACK_COLOR); +} + +void Screen::clearSelection() +{ + _selBottomRight = -1; + _selTopLeft = -1; + _selBegin = -1; +} + +void Screen::getSelectionStart(int& column , int& line) const +{ + if (_selTopLeft != -1) { + column = _selTopLeft % _columns; + line = _selTopLeft / _columns; + } else { + column = _cuX + getHistLines(); + line = _cuY + getHistLines(); + } +} +void Screen::getSelectionEnd(int& column , int& line) const +{ + if (_selBottomRight != -1) { + column = _selBottomRight % _columns; + line = _selBottomRight / _columns; + } else { + column = _cuX + getHistLines(); + line = _cuY + getHistLines(); + } +} +void Screen::setSelectionStart(const int x, const int y, const bool blockSelectionMode) +{ + _selBegin = loc(x, y); + /* FIXME, HACK to correct for x too far to the right... */ + if (x == _columns) _selBegin--; + + _selBottomRight = _selBegin; + _selTopLeft = _selBegin; + _blockSelectionMode = blockSelectionMode; +} + +void Screen::setSelectionEnd(const int x, const int y) +{ + if (_selBegin == -1) + return; + + int endPos = loc(x, y); + + if (endPos < _selBegin) { + _selTopLeft = endPos; + _selBottomRight = _selBegin; + } else { + /* FIXME, HACK to correct for x too far to the right... */ + if (x == _columns) + endPos--; + + _selTopLeft = _selBegin; + _selBottomRight = endPos; + } + + // Normalize the selection in column mode + if (_blockSelectionMode) { + const int topRow = _selTopLeft / _columns; + const int topColumn = _selTopLeft % _columns; + const int bottomRow = _selBottomRight / _columns; + const int bottomColumn = _selBottomRight % _columns; + + _selTopLeft = loc(qMin(topColumn, bottomColumn), topRow); + _selBottomRight = loc(qMax(topColumn, bottomColumn), bottomRow); + } +} + +bool Screen::isSelected(const int x, const int y) const +{ + bool columnInSelection = true; + if (_blockSelectionMode) { + columnInSelection = x >= (_selTopLeft % _columns) && + x <= (_selBottomRight % _columns); + } + + const int pos = loc(x, y); + return pos >= _selTopLeft && pos <= _selBottomRight && columnInSelection; +} + +QString Screen::selectedText(bool preserveLineBreaks, bool trimTrailingSpaces) const +{ + if (!isSelectionValid()) + return QString(); + + return text(_selTopLeft, _selBottomRight, preserveLineBreaks, trimTrailingSpaces); +} + +QString Screen::text(int startIndex, int endIndex, bool preserveLineBreaks, bool trimTrailingSpaces) const +{ + QString result; + QTextStream stream(&result, QIODevice::ReadWrite); + + PlainTextDecoder decoder; + decoder.begin(&stream); + writeToStream(&decoder, startIndex, endIndex, preserveLineBreaks, trimTrailingSpaces); + decoder.end(); + + return result; +} + +bool Screen::isSelectionValid() const +{ + return _selTopLeft >= 0 && _selBottomRight >= 0; +} + +void Screen::writeSelectionToStream(TerminalCharacterDecoder* decoder , + bool preserveLineBreaks, + bool trimTrailingSpaces) const +{ + if (!isSelectionValid()) + return; + writeToStream(decoder, _selTopLeft, _selBottomRight, preserveLineBreaks, trimTrailingSpaces); +} + +void Screen::writeToStream(TerminalCharacterDecoder* decoder, + int startIndex, int endIndex, + bool preserveLineBreaks, + bool trimTrailingSpaces) const +{ + const int top = startIndex / _columns; + const int left = startIndex % _columns; + + const int bottom = endIndex / _columns; + const int right = endIndex % _columns; + + Q_ASSERT(top >= 0 && left >= 0 && bottom >= 0 && right >= 0); + + for (int y = top; y <= bottom; y++) { + int start = 0; + if (y == top || _blockSelectionMode) start = left; + + int count = -1; + if (y == bottom || _blockSelectionMode) count = right - start + 1; + + const bool appendNewLine = (y != bottom); + int copied = copyLineToStream(y, + start, + count, + decoder, + appendNewLine, + preserveLineBreaks, + trimTrailingSpaces); + + // if the selection goes beyond the end of the last line then + // append a new line character. + // + // this makes it possible to 'select' a trailing new line character after + // the text on a line. + if (y == bottom && + copied < count) { + Character newLineChar('\n'); + decoder->decodeLine(&newLineChar, 1, 0); + } + } +} + +int Screen::copyLineToStream(int line , + int start, + int count, + TerminalCharacterDecoder* decoder, + bool appendNewLine, + bool preserveLineBreaks, + bool trimTrailingSpaces) const +{ + //buffer to hold characters for decoding + //the buffer is static to avoid initializing every + //element on each call to copyLineToStream + //(which is unnecessary since all elements will be overwritten anyway) + static const int MAX_CHARS = 1024; + static Character characterBuffer[MAX_CHARS]; + + Q_ASSERT(count < MAX_CHARS); + + LineProperty currentLineProperties = 0; + + //determine if the line is in the history buffer or the screen image + if (line < _history->getLines()) { + const int lineLength = _history->getLineLen(line); + + // ensure that start position is before end of line + start = qMin(start, qMax(0, lineLength - 1)); + + // retrieve line from history buffer. It is assumed + // that the history buffer does not store trailing white space + // at the end of the line, so it does not need to be trimmed here + if (count == -1) { + count = lineLength - start; + } else { + count = qMin(start + count, lineLength) - start; + } + + // safety checks + Q_ASSERT(start >= 0); + Q_ASSERT(count >= 0); + Q_ASSERT((start + count) <= _history->getLineLen(line)); + + _history->getCells(line, start, count, characterBuffer); + + if (_history->isWrappedLine(line)) + currentLineProperties |= LINE_WRAPPED; + } else { + if (count == -1) + count = _columns - start; + + Q_ASSERT(count >= 0); + + int screenLine = line - _history->getLines(); + + Q_ASSERT(screenLine <= _screenLinesSize); + + screenLine = qMin(screenLine, _screenLinesSize); + + Character* data = _screenLines[screenLine].data(); + int length = _screenLines[screenLine].count(); + + // Don't remove end spaces in lines that wrap + if (trimTrailingSpaces && !(_lineProperties[screenLine] & LINE_WRAPPED)) + { + // ignore trailing white space at the end of the line + for (int i = length-1; i >= 0; i--) + { + if (data[i].character == ' ') + length--; + else + break; + } + } + + //retrieve line from screen image + for (int i = start; i < qMin(start + count, length); i++) { + characterBuffer[i - start] = data[i]; + } + + // count cannot be any greater than length + count = qBound(0, count, length - start); + + Q_ASSERT(screenLine < _lineProperties.count()); + currentLineProperties |= _lineProperties[screenLine]; + } + + if (appendNewLine && (count + 1 < MAX_CHARS)) { + if (currentLineProperties & LINE_WRAPPED) { + // do nothing extra when this line is wrapped. + } else { + // When users ask not to preserve the linebreaks, they usually mean: + // `treat LINEBREAK as SPACE, thus joining multiple _lines into + // single line in the same way as 'J' does in VIM.` + characterBuffer[count] = preserveLineBreaks ? Character('\n') : Character(' '); + count++; + } + } + + //decode line and write to text stream + decoder->decodeLine((Character*) characterBuffer , + count, currentLineProperties); + + return count; +} + +void Screen::writeLinesToStream(TerminalCharacterDecoder* decoder, int fromLine, int toLine) const +{ + writeToStream(decoder, loc(0, fromLine), loc(_columns - 1, toLine)); +} + +void Screen::addHistLine() +{ + // add line to history buffer + // we have to take care about scrolling, too... + + if (hasScroll()) { + const int oldHistLines = _history->getLines(); + + _history->addCellsVector(_screenLines[0]); + _history->addLine(_lineProperties[0] & LINE_WRAPPED); + + const int newHistLines = _history->getLines(); + + const bool beginIsTL = (_selBegin == _selTopLeft); + + // If the history is full, increment the count + // of dropped _lines + if (newHistLines == oldHistLines) + _droppedLines++; + + // Adjust selection for the new point of reference + if (newHistLines > oldHistLines) { + if (_selBegin != -1) { + _selTopLeft += _columns; + _selBottomRight += _columns; + } + } + + if (_selBegin != -1) { + // Scroll selection in history up + const int top_BR = loc(0, 1 + newHistLines); + + if (_selTopLeft < top_BR) + _selTopLeft -= _columns; + + if (_selBottomRight < top_BR) + _selBottomRight -= _columns; + + if (_selBottomRight < 0) { + clearSelection(); + } else { + if (_selTopLeft < 0) + _selTopLeft = 0; + } + + if (beginIsTL) + _selBegin = _selTopLeft; + else + _selBegin = _selBottomRight; + } + } +} + +int Screen::getHistLines() const +{ + return _history->getLines(); +} + +void Screen::setScroll(const HistoryType& t , bool copyPreviousScroll) +{ + clearSelection(); + + if (copyPreviousScroll) { + _history = t.scroll(_history); + } else { + HistoryScroll* oldScroll = _history; + _history = t.scroll(0); + delete oldScroll; + } +} + +bool Screen::hasScroll() const +{ + return _history->hasScroll(); +} + +const HistoryType& Screen::getScroll() const +{ + return _history->getType(); +} + +void Screen::setLineProperty(LineProperty property , bool enable) +{ + if (enable) + _lineProperties[_cuY] = (LineProperty)(_lineProperties[_cuY] | property); + else + _lineProperties[_cuY] = (LineProperty)(_lineProperties[_cuY] & ~property); +} +void Screen::fillWithDefaultChar(Character* dest, int count) +{ + for (int i = 0; i < count; i++) + dest[i] = Screen::DefaultChar; +} diff --git a/konsole/src/Screen.h b/konsole/src/Screen.h new file mode 100644 index 00000000..820c7e15 --- /dev/null +++ b/konsole/src/Screen.h @@ -0,0 +1,715 @@ +/* + This file is part of Konsole, KDE's terminal. + + Copyright 2007-2008 by Robert Knight + Copyright 1997,1998 by Lars Doelle + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +#ifndef SCREEN_H +#define SCREEN_H + +// Qt +#include +#include +#include +#include +#include + +// Konsole +#include "Character.h" + +#define MODE_Origin 0 +#define MODE_Wrap 1 +#define MODE_Insert 2 +#define MODE_Screen 3 +#define MODE_Cursor 4 +#define MODE_NewLine 5 +#define MODES_SCREEN 6 + +namespace Konsole +{ +class TerminalCharacterDecoder; +class TerminalDisplay; +class HistoryType; +class HistoryScroll; + +/** + \brief An image of characters with associated attributes. + + The terminal emulation ( Emulation ) receives a serial stream of + characters from the program currently running in the terminal. + From this stream it creates an image of characters which is ultimately + rendered by the display widget ( TerminalDisplay ). Some types of emulation + may have more than one screen image. + + getImage() is used to retrieve the currently visible image + which is then used by the display widget to draw the output from the + terminal. + + The number of lines of output history which are kept in addition to the current + screen image depends on the history scroll being used to store the output. + The scroll is specified using setScroll() + The output history can be retrieved using writeToStream() + + The screen image has a selection associated with it, specified using + setSelectionStart() and setSelectionEnd(). The selected text can be retrieved + using selectedText(). When getImage() is used to retrieve the visible image, + characters which are part of the selection have their colors inverted. +*/ +class Screen +{ +public: + /** Construct a new screen image of size @p lines by @p columns. */ + Screen(int lines, int columns); + ~Screen(); + + // VT100/2 Operations + // Cursor Movement + + /** + * Move the cursor up by @p n lines. The cursor will stop at the + * top margin. + */ + void cursorUp(int n); + /** + * Move the cursor down by @p n lines. The cursor will stop at the + * bottom margin. + */ + void cursorDown(int n); + /** + * Move the cursor to the left by @p n columns. + * The cursor will stop at the first column. + */ + void cursorLeft(int n); + /** + * Move the cursor to the right by @p n columns. + * The cursor will stop at the right-most column. + */ + void cursorRight(int n); + /** Position the cursor on line @p y. */ + void setCursorY(int y); + /** Position the cursor at column @p x. */ + void setCursorX(int x); + /** Position the cursor at line @p y, column @p x. */ + void setCursorYX(int y, int x); + /** + * Sets the margins for scrolling the screen. + * + * @param topLine The top line of the new scrolling margin. + * @param bottomLine The bottom line of the new scrolling margin. + */ + void setMargins(int topLine , int bottomLine); + /** Returns the top line of the scrolling region. */ + int topMargin() const; + /** Returns the bottom line of the scrolling region. */ + int bottomMargin() const; + + /** + * Resets the scrolling margins back to the top and bottom lines + * of the screen. + */ + void setDefaultMargins(); + + /** + * Moves the cursor down one line, if the MODE_NewLine mode + * flag is enabled then the cursor is returned to the leftmost + * column first. + * + * Equivalent to NextLine() if the MODE_NewLine flag is set + * or index() otherwise. + */ + void newLine(); + /** + * Moves the cursor down one line and positions it at the beginning + * of the line. Equivalent to calling Return() followed by index() + */ + void nextLine(); + + /** + * Move the cursor down one line. If the cursor is on the bottom + * line of the scrolling region (as returned by bottomMargin()) the + * scrolling region is scrolled up by one line instead. + */ + void index(); + /** + * Move the cursor up one line. If the cursor is on the top line + * of the scrolling region (as returned by topMargin()) the scrolling + * region is scrolled down by one line instead. + */ + void reverseIndex(); + + /** + * Scroll the scrolling region of the screen up by @p n lines. + * The scrolling region is initially the whole screen, but can be changed + * using setMargins() + */ + void scrollUp(int n); + /** + * Scroll the scrolling region of the screen down by @p n lines. + * The scrolling region is initially the whole screen, but can be changed + * using setMargins() + */ + void scrollDown(int n); + /** + * Moves the cursor to the beginning of the current line. + * Equivalent to setCursorX(0) + */ + void toStartOfLine(); + /** + * Moves the cursor one column to the left and erases the character + * at the new cursor position. + */ + void backspace(); + /** Moves the cursor @p n tab-stops to the right. */ + void tab(int n = 1); + /** Moves the cursor @p n tab-stops to the left. */ + void backtab(int n); + + // Editing + + /** + * Erase @p n characters beginning from the current cursor position. + * This is equivalent to over-writing @p n characters starting with the current + * cursor position with spaces. + * If @p n is 0 then one character is erased. + */ + void eraseChars(int n); + /** + * Delete @p n characters beginning from the current cursor position. + * If @p n is 0 then one character is deleted. + */ + void deleteChars(int n); + /** + * Insert @p n blank characters beginning from the current cursor position. + * The position of the cursor is not altered. + * If @p n is 0 then one character is inserted. + */ + void insertChars(int n); + /** + * Removes @p n lines beginning from the current cursor position. + * The position of the cursor is not altered. + * If @p n is 0 then one line is removed. + */ + void deleteLines(int n); + /** + * Inserts @p lines beginning from the current cursor position. + * The position of the cursor is not altered. + * If @p n is 0 then one line is inserted. + */ + void insertLines(int n); + /** Clears all the tab stops. */ + void clearTabStops(); + /** Sets or removes a tab stop at the cursor's current column. */ + void changeTabStop(bool set); + + /** Resets (clears) the specified screen @p mode. */ + void resetMode(int mode); + /** Sets (enables) the specified screen @p mode. */ + void setMode(int mode); + /** + * Saves the state of the specified screen @p mode. It can be restored + * using restoreMode() + */ + void saveMode(int mode); + /** Restores the state of a screen @p mode saved by calling saveMode() */ + void restoreMode(int mode); + /** Returns whether the specified screen @p mode is enabled or not .*/ + bool getMode(int mode) const; + + /** + * Saves the current position and appearance (text color and style) of the cursor. + * It can be restored by calling restoreCursor() + */ + void saveCursor(); + /** Restores the position and appearance of the cursor. See saveCursor() */ + void restoreCursor(); + + /** Clear the whole screen, moving the current screen contents into the history first. */ + void clearEntireScreen(); + /** + * Clear the area of the screen from the current cursor position to the end of + * the screen. + */ + void clearToEndOfScreen(); + /** + * Clear the area of the screen from the current cursor position to the start + * of the screen. + */ + void clearToBeginOfScreen(); + /** Clears the whole of the line on which the cursor is currently positioned. */ + void clearEntireLine(); + /** Clears from the current cursor position to the end of the line. */ + void clearToEndOfLine(); + /** Clears from the current cursor position to the beginning of the line. */ + void clearToBeginOfLine(); + + /** Fills the entire screen with the letter 'E' */ + void helpAlign(); + + /** + * Enables the given @p rendition flag. Rendition flags control the appearance + * of characters on the screen. + * + * @see Character::rendition + */ + void setRendition(int rendition); + /** + * Disables the given @p rendition flag. Rendition flags control the appearance + * of characters on the screen. + * + * @see Character::rendition + */ + void resetRendition(int rendition); + + /** + * Sets the cursor's foreground color. + * @param space The color space used by the @p color argument + * @param color The new foreground color. The meaning of this depends on + * the color @p space used. + * + * @see CharacterColor + */ + void setForeColor(int space, int color); + /** + * Sets the cursor's background color. + * @param space The color space used by the @p color argument. + * @param color The new background color. The meaning of this depends on + * the color @p space used. + * + * @see CharacterColor + */ + void setBackColor(int space, int color); + /** + * Resets the cursor's color back to the default and sets the + * character's rendition flags back to the default settings. + */ + void setDefaultRendition(); + + /** Returns the column which the cursor is positioned at. */ + int getCursorX() const; + /** Returns the line which the cursor is positioned on. */ + int getCursorY() const; + + /** Clear the entire screen and move the cursor to the home position. + * Equivalent to calling clearEntireScreen() followed by home(). + */ + void clear(); + /** + * Sets the position of the cursor to the 'home' position at the top-left + * corner of the screen (0,0) + */ + void home(); + /** + * Resets the state of the screen. This resets the various screen modes + * back to their default states. The cursor style and colors are reset + * (as if setDefaultRendition() had been called) + * + *
      + *
    • Line wrapping is enabled.
    • + *
    • Origin mode is disabled.
    • + *
    • Insert mode is disabled.
    • + *
    • Cursor mode is enabled. TODO Document me
    • + *
    • Screen mode is disabled. TODO Document me
    • + *
    • New line mode is disabled. TODO Document me
    • + *
    + * + * If @p clearScreen is true then the screen contents are erased entirely, + * otherwise they are unaltered. + */ + void reset(bool clearScreen = true); + + /** + * Displays a new character at the current cursor position. + * + * If the cursor is currently positioned at the right-edge of the screen and + * line wrapping is enabled then the character is added at the start of a new + * line below the current one. + * + * If the MODE_Insert screen mode is currently enabled then the character + * is inserted at the current cursor position, otherwise it will replace the + * character already at the current cursor position. + */ + void displayCharacter(unsigned short c); + + /** + * Resizes the image to a new fixed size of @p new_lines by @p new_columns. + * In the case that @p new_columns is smaller than the current number of columns, + * existing lines are not truncated. This prevents characters from being lost + * if the terminal display is resized smaller and then larger again. + * + * The top and bottom margins are reset to the top and bottom of the new + * screen size. Tab stops are also reset and the current selection is + * cleared. + */ + void resizeImage(int new_lines, int new_columns); + + /** + * Returns the current screen image. + * The result is an array of Characters of size [getLines()][getColumns()] which + * must be freed by the caller after use. + * + * @param dest Buffer to copy the characters into + * @param size Size of @p dest in Characters + * @param startLine Index of first line to copy + * @param endLine Index of last line to copy + */ + void getImage(Character* dest , int size , int startLine , int endLine) const; + + /** + * Returns the additional attributes associated with lines in the image. + * The most important attribute is LINE_WRAPPED which specifies that the + * line is wrapped, + * other attributes control the size of characters in the line. + */ + QVector getLineProperties(int startLine , int endLine) const; + + /** Return the number of lines. */ + int getLines() const { + return _lines; + } + /** Return the number of columns. */ + int getColumns() const { + return _columns; + } + /** Return the number of lines in the history buffer. */ + int getHistLines() const; + /** + * Sets the type of storage used to keep lines in the history. + * If @p copyPreviousScroll is true then the contents of the previous + * history buffer are copied into the new scroll. + */ + void setScroll(const HistoryType& , bool copyPreviousScroll = true); + /** Returns the type of storage used to keep lines in the history. */ + const HistoryType& getScroll() const; + /** + * Returns true if this screen keeps lines that are scrolled off the screen + * in a history buffer. + */ + bool hasScroll() const; + + /** + * Sets the start of the selection. + * + * @param column The column index of the first character in the selection. + * @param line The line index of the first character in the selection. + * @param blockSelectionMode True if the selection is in column mode. + */ + void setSelectionStart(const int column, const int line, const bool blockSelectionMode); + + /** + * Sets the end of the current selection. + * + * @param column The column index of the last character in the selection. + * @param line The line index of the last character in the selection. + */ + void setSelectionEnd(const int column, const int line); + + /** + * Retrieves the start of the selection or the cursor position if there + * is no selection. + */ + void getSelectionStart(int& column , int& line) const; + + /** + * Retrieves the end of the selection or the cursor position if there + * is no selection. + */ + void getSelectionEnd(int& column , int& line) const; + + /** Clears the current selection */ + void clearSelection(); + + /** + * Returns true if the character at (@p column, @p line) is part of the + * current selection. + */ + bool isSelected(const int column, const int line) const; + + /** + * Convenience method. Returns the currently selected text. + * @param preserveLineBreaks Specifies whether new line characters should + * be inserted into the returned text at the end of each terminal line. + * @param trimTrailingSpaces Specifies whether trailing spaces should be + * trimmed in the returned text. + */ + QString selectedText(bool preserveLineBreaks, bool trimTrailingSpaces = false) const; + + /** + * Convenience method. Returns the text between two indices. + * @param startIndex Specifies the starting text index + * @param endIndex Specifies the ending text index + * @param preserveLineBreaks Specifies whether new line characters should + * be inserted into the returned text at the end of each terminal line. + * @param trimTrailingSpaces Specifies whether trailing spaces should be + * trimmed in the returned text. + */ + QString text(int startIndex, int endIndex, bool preserveLineBreaks, bool trimTrailingSpaces = false) const; + + /** + * Copies part of the output to a stream. + * + * @param decoder A decoder which converts terminal characters into text + * @param fromLine The first line in the history to retrieve + * @param toLine The last line in the history to retrieve + */ + void writeLinesToStream(TerminalCharacterDecoder* decoder, int fromLine, int toLine) const; + + /** + * Copies the selected characters, set using @see setSelBeginXY and @see setSelExtentXY + * into a stream. + * + * @param decoder A decoder which converts terminal characters into text. + * PlainTextDecoder is the most commonly used decoder which converts characters + * into plain text with no formatting. + * @param preserveLineBreaks Specifies whether new line characters should + * be inserted into the returned text at the end of each terminal line. + * @param trimTrailingSpaces Specifies whether trailing spaces should be + * trimmed in the returned text. + */ + void writeSelectionToStream(TerminalCharacterDecoder* decoder , bool + preserveLineBreaks = true, + bool trimTrailingSpaces = false) const; + + /** + * Checks if the text between from and to is inside the current + * selection. If this is the case, the selection is cleared. The + * from and to are coordinates in the current viewable window. + * The loc(x,y) macro can be used to generate these values from a + * column,line pair. + * + * @param from The start of the area to check. + * @param to The end of the area to check + */ + void checkSelection(int from, int to); + + /** + * Sets or clears an attribute of the current line. + * + * @param property The attribute to set or clear + * Possible properties are: + * LINE_WRAPPED: Specifies that the line is wrapped. + * LINE_DOUBLEWIDTH: Specifies that the characters in the current line + * should be double the normal width. + * LINE_DOUBLEHEIGHT:Specifies that the characters in the current line + * should be double the normal height. + * Double-height lines are formed of two lines containing the same characters, + * with both having the LINE_DOUBLEHEIGHT attribute. + * This allows other parts of the code to work on the + * assumption that all lines are the same height. + * + * @param enable true to apply the attribute to the current line or false to remove it + */ + void setLineProperty(LineProperty property , bool enable); + + /** + * Returns the number of lines that the image has been scrolled up or down by, + * since the last call to resetScrolledLines(). + * + * a positive return value indicates that the image has been scrolled up, + * a negative return value indicates that the image has been scrolled down. + */ + int scrolledLines() const; + + /** + * Returns the region of the image which was last scrolled. + * + * This is the area of the image from the top margin to the + * bottom margin when the last scroll occurred. + */ + QRect lastScrolledRegion() const; + + /** + * Resets the count of the number of lines that the image has been scrolled up or down by, + * see scrolledLines() + */ + void resetScrolledLines(); + + /** + * Returns the number of lines of output which have been + * dropped from the history since the last call + * to resetDroppedLines() + * + * If the history is not unlimited then it will drop + * the oldest lines of output if new lines are added when + * it is full. + */ + int droppedLines() const; + + /** + * Resets the count of the number of lines dropped from + * the history. + */ + void resetDroppedLines(); + + /** + * Fills the buffer @p dest with @p count instances of the default (ie. blank) + * Character style. + */ + static void fillWithDefaultChar(Character* dest, int count); + + void setCurrentTerminalDisplay(TerminalDisplay* display) { + _currentTerminalDisplay = display; + } + + TerminalDisplay* currentTerminalDisplay() { + return _currentTerminalDisplay; + } + + QSet usedExtendedChars() const { + QSet result; + for (int i = 0; i < _lines; ++i) { + const ImageLine& il = _screenLines[i]; + for (int j = 0; j < _columns; ++j) { + if (il[j].rendition & RE_EXTENDED_CHAR) { + result << il[j].character; + } + } + } + return result; + } + + static const Character DefaultChar; + +private: + //copies a line of text from the screen or history into a stream using a + //specified character decoder. Returns the number of lines actually copied, + //which may be less than 'count' if (start+count) is more than the number of characters on + //the line + // + //line - the line number to copy, from 0 (the earliest line in the history) up to + // history->getLines() + lines - 1 + //start - the first column on the line to copy + //count - the number of characters on the line to copy + //decoder - a decoder which converts terminal characters (an Character array) into text + //appendNewLine - if true a new line character (\n) is appended to the end of the line + int copyLineToStream(int line, + int start, + int count, + TerminalCharacterDecoder* decoder, + bool appendNewLine, + bool preserveLineBreaks, + bool trimTrailingSpaces) const; + + //fills a section of the screen image with the character 'c' + //the parameters are specified as offsets from the start of the screen image. + //the loc(x,y) macro can be used to generate these values from a column,line pair. + void clearImage(int loca, int loce, char c); + + //move screen image between 'sourceBegin' and 'sourceEnd' to 'dest'. + //the parameters are specified as offsets from the start of the screen image. + //the loc(x,y) macro can be used to generate these values from a column,line pair. + // + //NOTE: moveImage() can only move whole lines + void moveImage(int dest, int sourceBegin, int sourceEnd); + // scroll up 'i' lines in current region, clearing the bottom 'i' lines + void scrollUp(int from, int i); + // scroll down 'i' lines in current region, clearing the top 'i' lines + void scrollDown(int from, int i); + + //when we handle scroll commands, we need to know which screenwindow will scroll + TerminalDisplay* _currentTerminalDisplay; + + void addHistLine(); + + void initTabStops(); + + void updateEffectiveRendition(); + void reverseRendition(Character& p) const; + + bool isSelectionValid() const; + // copies text from 'startIndex' to 'endIndex' to a stream + // startIndex and endIndex are positions generated using the loc(x,y) macro + void writeToStream(TerminalCharacterDecoder* decoder, int startIndex, + int endIndex, bool preserveLineBreaks = true, bool trimTrailingSpaces = false) const; + // copies 'count' lines from the screen buffer into 'dest', + // starting from 'startLine', where 0 is the first line in the screen buffer + void copyFromScreen(Character* dest, int startLine, int count) const; + // copies 'count' lines from the history buffer into 'dest', + // starting from 'startLine', where 0 is the first line in the history + void copyFromHistory(Character* dest, int startLine, int count) const; + + // screen image ---------------- + int _lines; + int _columns; + + typedef QVector ImageLine; // [0..columns] + ImageLine* _screenLines; // [lines] + int _screenLinesSize; // _screenLines.size() + + int _scrolledLines; + QRect _lastScrolledRegion; + + int _droppedLines; + + QVarLengthArray _lineProperties; + + // history buffer --------------- + HistoryScroll* _history; + + // cursor location + int _cuX; + int _cuY; + + // cursor color and rendition info + CharacterColor _currentForeground; + CharacterColor _currentBackground; + quint8 _currentRendition; + + // margins ---------------- + int _topMargin; + int _bottomMargin; + + // states ---------------- + int _currentModes[MODES_SCREEN]; + int _savedModes[MODES_SCREEN]; + + // ---------------------------- + + QBitArray _tabStops; + + // selection ------------------- + int _selBegin; // The first location selected. + int _selTopLeft; // TopLeft Location. + int _selBottomRight; // Bottom Right Location. + bool _blockSelectionMode; // Column selection mode + + // effective colors and rendition ------------ + CharacterColor _effectiveForeground; // These are derived from + CharacterColor _effectiveBackground; // the cu_* variables above + quint8 _effectiveRendition; // to speed up operation + + class SavedState + { + public: + SavedState() + : cursorColumn(0), cursorLine(0), rendition(0) {} + + int cursorColumn; + int cursorLine; + quint8 rendition; + CharacterColor foreground; + CharacterColor background; + }; + SavedState _savedState; + + // last position where we added a character + int _lastPos; +}; +} + +#endif // SCREEN_H diff --git a/konsole/src/ScreenWindow.cpp b/konsole/src/ScreenWindow.cpp new file mode 100644 index 00000000..bd7c241e --- /dev/null +++ b/konsole/src/ScreenWindow.cpp @@ -0,0 +1,322 @@ +/* + Copyright (C) 2007 by Robert Knight + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +// Own +#include "ScreenWindow.h" + +// Konsole +#include "Screen.h" + +using namespace Konsole; + +ScreenWindow::ScreenWindow(Screen* screen, QObject* parent) + : QObject(parent) + , _windowBuffer(0) + , _windowBufferSize(0) + , _bufferNeedsUpdate(true) + , _windowLines(1) + , _currentLine(0) + , _currentResultLine(-1) + , _trackOutput(true) + , _scrollCount(0) +{ + setScreen(screen); +} + +ScreenWindow::~ScreenWindow() +{ + delete[] _windowBuffer; +} +void ScreenWindow::setScreen(Screen* screen) +{ + Q_ASSERT(screen); + + _screen = screen; +} + +Screen* ScreenWindow::screen() const +{ + return _screen; +} + +Character* ScreenWindow::getImage() +{ + // reallocate internal buffer if the window size has changed + int size = windowLines() * windowColumns(); + if (_windowBuffer == 0 || _windowBufferSize != size) { + delete[] _windowBuffer; + _windowBufferSize = size; + _windowBuffer = new Character[size]; + _bufferNeedsUpdate = true; + } + + if (!_bufferNeedsUpdate) + return _windowBuffer; + + _screen->getImage(_windowBuffer, size, + currentLine(), endWindowLine()); + + // this window may look beyond the end of the screen, in which + // case there will be an unused area which needs to be filled + // with blank characters + fillUnusedArea(); + + _bufferNeedsUpdate = false; + return _windowBuffer; +} + +void ScreenWindow::fillUnusedArea() +{ + int screenEndLine = _screen->getHistLines() + _screen->getLines() - 1; + int windowEndLine = currentLine() + windowLines() - 1; + + int unusedLines = windowEndLine - screenEndLine; + + // stop when unusedLines is negative; there is an issue w/ charsToFill + // being greater than an int can hold + if (unusedLines <= 0) + return; + + int charsToFill = unusedLines * windowColumns(); + + Screen::fillWithDefaultChar(_windowBuffer + _windowBufferSize - charsToFill, charsToFill); +} + +// return the index of the line at the end of this window, or if this window +// goes beyond the end of the screen, the index of the line at the end +// of the screen. +// +// when passing a line number to a Screen method, the line number should +// never be more than endWindowLine() +// +int ScreenWindow::endWindowLine() const +{ + return qMin(currentLine() + windowLines() - 1, + lineCount() - 1); +} +QVector ScreenWindow::getLineProperties() +{ + QVector result = _screen->getLineProperties(currentLine(), endWindowLine()); + + if (result.count() != windowLines()) + result.resize(windowLines()); + + return result; +} + +QString ScreenWindow::selectedText(bool preserveLineBreaks, bool trimTrailingSpaces) const +{ + return _screen->selectedText(preserveLineBreaks, trimTrailingSpaces); +} + +void ScreenWindow::getSelectionStart(int& column , int& line) +{ + _screen->getSelectionStart(column, line); + line -= currentLine(); +} +void ScreenWindow::getSelectionEnd(int& column , int& line) +{ + _screen->getSelectionEnd(column, line); + line -= currentLine(); +} +void ScreenWindow::setSelectionStart(int column , int line , bool columnMode) +{ + _screen->setSelectionStart(column , line + currentLine() , columnMode); + + _bufferNeedsUpdate = true; + emit selectionChanged(); +} + +void ScreenWindow::setSelectionEnd(int column , int line) +{ + _screen->setSelectionEnd(column , line + currentLine()); + + _bufferNeedsUpdate = true; + emit selectionChanged(); +} + +void ScreenWindow::setSelectionByLineRange(int start, int end) +{ + clearSelection(); + + _screen->setSelectionStart(0 , start , false); + _screen->setSelectionEnd(windowColumns() , end); + + _bufferNeedsUpdate = true; + emit selectionChanged(); +} + +bool ScreenWindow::isSelected(int column , int line) +{ + return _screen->isSelected(column , qMin(line + currentLine(), endWindowLine())); +} + +void ScreenWindow::clearSelection() +{ + _screen->clearSelection(); + + emit selectionChanged(); +} + +void ScreenWindow::setWindowLines(int lines) +{ + Q_ASSERT(lines > 0); + _windowLines = lines; +} +int ScreenWindow::windowLines() const +{ + return _windowLines; +} + +int ScreenWindow::windowColumns() const +{ + return _screen->getColumns(); +} + +int ScreenWindow::lineCount() const +{ + return _screen->getHistLines() + _screen->getLines(); +} + +int ScreenWindow::columnCount() const +{ + return _screen->getColumns(); +} + +QPoint ScreenWindow::cursorPosition() const +{ + QPoint position; + + position.setX(_screen->getCursorX()); + position.setY(_screen->getCursorY()); + + return position; +} + +int ScreenWindow::currentLine() const +{ + return qBound(0, _currentLine, lineCount() - windowLines()); +} + +int ScreenWindow::currentResultLine() const +{ + return _currentResultLine; +} + +void ScreenWindow::setCurrentResultLine(int line) +{ + if (_currentResultLine == line) { + return; + } + + _currentResultLine = line; + emit currentResultLineChanged(); +} + +void ScreenWindow::scrollBy(RelativeScrollMode mode, int amount, bool fullPage) +{ + if (mode == ScrollLines) { + scrollTo(currentLine() + amount); + } else if (mode == ScrollPages) { + if (fullPage) + scrollTo(currentLine() + amount * (windowLines())); + else + scrollTo(currentLine() + amount * (windowLines() / 2)); + } +} + +bool ScreenWindow::atEndOfOutput() const +{ + return currentLine() == (lineCount() - windowLines()); +} + +void ScreenWindow::scrollTo(int line) +{ + int maxCurrentLineNumber = lineCount() - windowLines(); + line = qBound(0, line, maxCurrentLineNumber); + + const int delta = line - _currentLine; + _currentLine = line; + + // keep track of number of lines scrolled by, + // this can be reset by calling resetScrollCount() + _scrollCount += delta; + + _bufferNeedsUpdate = true; + + emit scrolled(_currentLine); +} + +void ScreenWindow::setTrackOutput(bool trackOutput) +{ + _trackOutput = trackOutput; +} + +bool ScreenWindow::trackOutput() const +{ + return _trackOutput; +} + +int ScreenWindow::scrollCount() const +{ + return _scrollCount; +} + +void ScreenWindow::resetScrollCount() +{ + _scrollCount = 0; +} + +QRect ScreenWindow::scrollRegion() const +{ + bool equalToScreenSize = windowLines() == _screen->getLines(); + + if (atEndOfOutput() && equalToScreenSize) + return _screen->lastScrolledRegion(); + else + return QRect(0, 0, windowColumns(), windowLines()); +} + +void ScreenWindow::notifyOutputChanged() +{ + // move window to the bottom of the screen and update scroll count + // if this window is currently tracking the bottom of the screen + if (_trackOutput) { + _scrollCount -= _screen->scrolledLines(); + _currentLine = qMax(0, _screen->getHistLines() - (windowLines() - _screen->getLines())); + } else { + // if the history is not unlimited then it may + // have run out of space and dropped the oldest + // lines of output - in this case the screen + // window's current line number will need to + // be adjusted - otherwise the output will scroll + _currentLine = qMax(0, _currentLine - + _screen->droppedLines()); + + // ensure that the screen window's current position does + // not go beyond the bottom of the screen + _currentLine = qMin(_currentLine , _screen->getHistLines()); + } + + _bufferNeedsUpdate = true; + + emit outputChanged(); +} + +#include "moc_ScreenWindow.cpp" diff --git a/konsole/src/ScreenWindow.h b/konsole/src/ScreenWindow.h new file mode 100644 index 00000000..11d599b2 --- /dev/null +++ b/konsole/src/ScreenWindow.h @@ -0,0 +1,280 @@ +/* + Copyright 2007-2008 by Robert Knight + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +#ifndef SCREENWINDOW_H +#define SCREENWINDOW_H + +// Qt +#include +#include +#include + +// Konsole +#include "Character.h" + +namespace Konsole +{ +class Screen; + +/** + * Provides a window onto a section of a terminal screen. A terminal widget can then render + * the contents of the window and use the window to change the terminal screen's selection + * in response to mouse or keyboard input. + * + * A new ScreenWindow for a terminal session can be created by calling Emulation::createWindow() + * + * Use the scrollTo() method to scroll the window up and down on the screen. + * Use the getImage() method to retrieve the character image which is currently visible in the window. + * + * setTrackOutput() controls whether the window moves to the bottom of the associated screen when new + * lines are added to it. + * + * Whenever the output from the underlying screen is changed, the notifyOutputChanged() slot should + * be called. This in turn will update the window's position and emit the outputChanged() signal + * if necessary. + */ +class ScreenWindow : public QObject +{ + Q_OBJECT + +public: + /** + * Constructs a new screen window with the given parent. + * A screen must be specified by calling setScreen() before calling getImage() or getLineProperties(). + * + * You should not call this constructor directly, instead use the Emulation::createWindow() method + * to create a window on the emulation which you wish to view. This allows the emulation + * to notify the window when the associated screen has changed and synchronize selection updates + * between all views on a session. + */ + explicit ScreenWindow(Screen* screen, QObject* parent = 0); + virtual ~ScreenWindow(); + + /** Sets the screen which this window looks onto */ + void setScreen(Screen* screen); + /** Returns the screen which this window looks onto */ + Screen* screen() const; + + /** + * Returns the image of characters which are currently visible through this window + * onto the screen. + * + * The returned buffer is managed by the ScreenWindow instance and does not need to be + * deleted by the caller. + */ + Character* getImage(); + + /** + * Returns the line attributes associated with the lines of characters which + * are currently visible through this window + */ + QVector getLineProperties(); + + /** + * Returns the number of lines which the region of the window + * specified by scrollRegion() has been scrolled by since the last call + * to resetScrollCount(). scrollRegion() is in most cases the + * whole window, but will be a smaller area in, for example, applications + * which provide split-screen facilities. + * + * This is not guaranteed to be accurate, but allows views to optimize + * rendering by reducing the amount of costly text rendering that + * needs to be done when the output is scrolled. + */ + int scrollCount() const; + + /** + * Resets the count of scrolled lines returned by scrollCount() + */ + void resetScrollCount(); + + /** + * Returns the area of the window which was last scrolled, this is + * usually the whole window area. + * + * Like scrollCount(), this is not guaranteed to be accurate, + * but allows views to optimize rendering. + */ + QRect scrollRegion() const; + + + /** + * What line the next search will start from + */ + void setCurrentResultLine(int line); + int currentResultLine() const; + + /** + * Sets the start of the selection to the given @p line and @p column within + * the window. + */ + void setSelectionStart(int column , int line , bool columnMode); + /** + * Sets the end of the selection to the given @p line and @p column within + * the window. + */ + void setSelectionEnd(int column , int line); + + /** + * Sets the selection as the range specified by line @p start and line @p + * end in the whole history. + * + * Both @p start and @p end are absolute line number in the whole history, + * not relative line number in the window. This make it possible to select + * range larger than the window . A good use case is selecting the whole + * history. + */ + void setSelectionByLineRange(int start, int end); + + /** + * Retrieves the start of the selection within the window. + */ + void getSelectionStart(int& column , int& line); + /** + * Retrieves the end of the selection within the window. + */ + void getSelectionEnd(int& column , int& line); + /** + * Returns true if the character at @p line , @p column is part of the selection. + */ + bool isSelected(int column , int line); + /** + * Clears the current selection + */ + void clearSelection(); + + /** Sets the number of lines in the window */ + void setWindowLines(int lines); + /** Returns the number of lines in the window */ + int windowLines() const; + /** Returns the number of columns in the window */ + int windowColumns() const; + + /** Returns the total number of lines in the screen */ + int lineCount() const; + /** Returns the total number of columns in the screen */ + int columnCount() const; + + /** Returns the index of the line which is currently at the top of this window */ + int currentLine() const; + + /** + * Returns the position of the cursor + * within the window. + */ + QPoint cursorPosition() const; + + /** + * Convenience method. Returns true if the window is currently at the bottom + * of the screen. + */ + bool atEndOfOutput() const; + + /** Scrolls the window so that @p line is at the top of the window */ + void scrollTo(int line); + + /** Describes the units which scrollBy() moves the window by. */ + enum RelativeScrollMode { + /** Scroll the window down by a given number of lines. */ + ScrollLines, + /** + * Scroll the window down by a given number of pages, where + * one page is windowLines() lines + */ + ScrollPages + }; + + /** + * Scrolls the window relative to its current position on the screen. + * + * @param mode Specifies whether @p amount refers to the number of lines or the number + * of pages to scroll. + * @param amount The number of lines or pages ( depending on @p mode ) to scroll by. If + * this number is positive, the view is scrolled down. If this number is negative, the view + * is scrolled up. + * @param fullPage Specifies whether to scroll by full page or half page. + */ + void scrollBy(RelativeScrollMode mode, int amount, bool fullPage); + + /** + * Specifies whether the window should automatically move to the bottom + * of the screen when new output is added. + * + * If this is set to true, the window will be moved to the bottom of the associated screen ( see + * screen() ) when the notifyOutputChanged() method is called. + */ + void setTrackOutput(bool trackOutput); + /** + * Returns whether the window automatically moves to the bottom of the screen as + * new output is added. See setTrackOutput() + */ + bool trackOutput() const; + + /** + * Returns the text which is currently selected. + * + * @param preserveLineBreaks See Screen::selectedText() + * @param trimTrailingSpaces See Screen::selectedText() + */ + QString selectedText(bool preserveLineBreaks, bool trimTrailingSpaces = false) const; + +public slots: + /** + * Notifies the window that the contents of the associated terminal screen have changed. + * This moves the window to the bottom of the screen if trackOutput() is true and causes + * the outputChanged() signal to be emitted. + */ + void notifyOutputChanged(); + +signals: + /** + * Emitted when the contents of the associated terminal screen (see screen()) changes. + */ + void outputChanged(); + + void currentResultLineChanged(); + + /** + * Emitted when the screen window is scrolled to a different position. + * + * @param line The line which is now at the top of the window. + */ + void scrolled(int line); + + /** Emitted when the selection is changed. */ + void selectionChanged(); + +private: + int endWindowLine() const; + void fillUnusedArea(); + + Screen* _screen; // see setScreen() , screen() + Character* _windowBuffer; + int _windowBufferSize; + bool _bufferNeedsUpdate; + + int _windowLines; + int _currentLine; // see scrollTo() , currentLine() + int _currentResultLine; + bool _trackOutput; // see setTrackOutput() , trackOutput() + int _scrollCount; // count of lines which the window has been scrolled by since + // the last call to resetScrollCount() +}; +} +#endif // SCREENWINDOW_H diff --git a/konsole/src/Session.cpp b/konsole/src/Session.cpp new file mode 100644 index 00000000..773890f8 --- /dev/null +++ b/konsole/src/Session.cpp @@ -0,0 +1,1502 @@ +/* + This file is part of Konsole + + Copyright 2006-2008 by Robert Knight + Copyright 1997,1998 by Lars Doelle + Copyright 2009 by Thomas Dreibholz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +// Own +#include "Session.h" + +// Standard +#include +#include +#include + +// Qt +#include +#include +#include +#include +#include +#include +#include +#include + +// KDE +#include +#include +#include +#include +#include +#include +#include + +// Konsole +#include + +#include "ProcessInfo.h" +#include "Pty.h" +#include "TerminalDisplay.h" +#include "ShellCommand.h" +#include "Vt102Emulation.h" +#include "ZModemDialog.h" +#include "History.h" + +using namespace Konsole; + +int Session::lastSessionId = 0; + +static inline QByteArray createSessionID() +{ +#if QT_VERSION >= 0x041200 + return qRandomUuid(); +#else + return QByteArray::number(qrand()); +#endif +} + +Session::Session(QObject* parent) : + QObject(parent) + , _shellProcess(0) + , _emulation(0) + , _monitorActivity(false) + , _monitorSilence(false) + , _notifiedActivity(false) + , _silenceSeconds(10) + , _autoClose(true) + , _closePerUserRequest(false) + , _addToUtmp(true) + , _flowControlEnabled(true) + , _sessionId(0) + , _sessionProcessInfo(0) + , _foregroundProcessInfo(0) + , _foregroundPid(0) + , _zmodemBusy(false) + , _zmodemProc(0) + , _zmodemProgress(0) + , _hasDarkBackground(false) +{ + _uniqueIdentifier = createSessionID(); + + //prepare DBus communication + new SessionAdaptor(this); + _sessionId = ++lastSessionId; + QDBusConnection::sessionBus().registerObject(QLatin1String("/Sessions/") + QString::number(_sessionId), this); + + //create emulation backend + _emulation = new Vt102Emulation(); + + connect(_emulation, SIGNAL(titleChanged(int,QString)), + this, SLOT(setUserTitle(int,QString))); + connect(_emulation, SIGNAL(stateSet(int)), + this, SLOT(activityStateSet(int))); + connect(_emulation, SIGNAL(zmodemDetected()), + this, SLOT(fireZModemDetected())); + connect(_emulation, SIGNAL(changeTabTextColorRequest(int)), + this, SIGNAL(changeTabTextColorRequest(int))); + connect(_emulation, SIGNAL(profileChangeCommandReceived(QString)), + this, SIGNAL(profileChangeCommandReceived(QString))); + connect(_emulation, SIGNAL(flowControlKeyPressed(bool)), + this, SLOT(updateFlowControlState(bool))); + connect(_emulation, SIGNAL(primaryScreenInUse(bool)), + this, SLOT(onPrimaryScreenInUse(bool))); + connect(_emulation, SIGNAL(selectionChanged(QString)), + this, SIGNAL(selectionChanged(QString))); + connect(_emulation, SIGNAL(imageResizeRequest(QSize)), + this, SIGNAL(resizeRequest(QSize))); + + //create new teletype for I/O with shell process + openTeletype(-1); + + //setup timer for monitoring session activity & silence + _silenceTimer = new QTimer(this); + _silenceTimer->setSingleShot(true); + connect(_silenceTimer, SIGNAL(timeout()), this, SLOT(silenceTimerDone())); + + _activityTimer = new QTimer(this); + _activityTimer->setSingleShot(true); + connect(_activityTimer, SIGNAL(timeout()), this, SLOT(activityTimerDone())); +} + +Session::~Session() +{ + delete _foregroundProcessInfo; + delete _sessionProcessInfo; + delete _emulation; + delete _shellProcess; + delete _zmodemProc; +} + +void Session::openTeletype(int fd) +{ + if (isRunning()) { + kWarning() << "Attempted to open teletype in a running session."; + return; + } + + delete _shellProcess; + + if (fd < 0) + _shellProcess = new Pty(); + else + _shellProcess = new Pty(fd); + + _shellProcess->setUtf8Mode(_emulation->utf8()); + + // connect the I/O between emulator and pty process + connect(_shellProcess, SIGNAL(receivedData(const char*,int)), + this, SLOT(onReceiveBlock(const char*,int))); + connect(_emulation, SIGNAL(sendData(const char*,int)), + _shellProcess, SLOT(sendData(const char*,int))); + + // UTF8 mode + connect(_emulation, SIGNAL(useUtf8Request(bool)), + _shellProcess, SLOT(setUtf8Mode(bool))); + + // get notified when the pty process is finished + connect(_shellProcess, SIGNAL(finished(int,QProcess::ExitStatus)), + this, SLOT(done(int,QProcess::ExitStatus))); + + // emulator size + connect(_emulation, SIGNAL(imageSizeChanged(int,int)), + this, SLOT(updateWindowSize(int,int))); + connect(_emulation, SIGNAL(imageSizeInitialized()), + this, SLOT(run())); +} + +WId Session::windowId() const +{ + // Returns a window ID for this session which is used + // to set the WINDOWID environment variable in the shell + // process. + // + // Sessions can have multiple views or no views, which means + // that a single ID is not always going to be accurate. + // + // If there are no views, the window ID is just 0. If + // there are multiple views, then the window ID for the + // top-level window which contains the first view is + // returned + + if (_views.count() == 0) { + return 0; + } else { + QWidget* window = _views.first(); + + Q_ASSERT(window); + + while (window->parentWidget() != 0) + window = window->parentWidget(); + + return window->winId(); + } +} + +void Session::setDarkBackground(bool darkBackground) +{ + _hasDarkBackground = darkBackground; +} + +bool Session::isRunning() const +{ + return _shellProcess && (_shellProcess->state() == QProcess::Running); +} + +void Session::setCodec(QTextCodec* codec) +{ + emulation()->setCodec(codec); +} + +bool Session::setCodec(QByteArray name) +{ + QTextCodec* codec = QTextCodec::codecForName(name); + + if (codec) { + setCodec(codec); + return true; + } else { + return false; + } +} + +QByteArray Session::codec() +{ + return _emulation->codec()->name(); +} + +void Session::setProgram(const QString& program) +{ + _program = ShellCommand::expand(program); +} + +void Session::setArguments(const QStringList& arguments) +{ + _arguments = ShellCommand::expand(arguments); +} + +void Session::setInitialWorkingDirectory(const QString& dir) +{ + _initialWorkingDir = KShell::tildeExpand(ShellCommand::expand(dir)); +} + +QString Session::currentWorkingDirectory() +{ + // only returned cached value + if (_currentWorkingDir.isEmpty()) + updateWorkingDirectory(); + + return _currentWorkingDir; +} + +void Session::updateWorkingDirectory() +{ + updateSessionProcessInfo(); + + const QString currentDir = _sessionProcessInfo->validCurrentDir(); + if (currentDir != _currentWorkingDir) { + _currentWorkingDir = currentDir; + emit currentDirectoryChanged(_currentWorkingDir); + } +} + +QList Session::views() const +{ + return _views; +} + +void Session::addView(TerminalDisplay* widget) +{ + Q_ASSERT(!_views.contains(widget)); + + _views.append(widget); + + // connect emulation - view signals and slots + connect(widget, SIGNAL(keyPressedSignal(QKeyEvent*)), + _emulation, SLOT(sendKeyEvent(QKeyEvent*))); + connect(widget, SIGNAL(mouseSignal(int,int,int,int)), + _emulation, SLOT(sendMouseEvent(int,int,int,int))); + connect(widget, SIGNAL(sendStringToEmu(const char*)), + _emulation, SLOT(sendString(const char*))); + + // allow emulation to notify view when the foreground process + // indicates whether or not it is interested in mouse signals + connect(_emulation, SIGNAL(programUsesMouseChanged(bool)), + widget, SLOT(setUsesMouse(bool))); + + widget->setUsesMouse(_emulation->programUsesMouse()); + + connect(_emulation, SIGNAL(programBracketedPasteModeChanged(bool)), + widget, SLOT(setBracketedPasteMode(bool))); + + widget->setBracketedPasteMode(_emulation->programBracketedPasteMode()); + + widget->setScreenWindow(_emulation->createWindow()); + + //connect view signals and slots + connect(widget, SIGNAL(changedContentSizeSignal(int,int)), + this, SLOT(onViewSizeChange(int,int))); + + connect(widget, SIGNAL(destroyed(QObject*)), + this, SLOT(viewDestroyed(QObject*))); +} + +void Session::viewDestroyed(QObject* view) +{ + // the received QObject has already been destroyed, so using + // qobject_cast<> does not work here + TerminalDisplay* display = static_cast(view); + + Q_ASSERT(_views.contains(display)); + + removeView(display); +} + +void Session::removeView(TerminalDisplay* widget) +{ + _views.removeAll(widget); + + disconnect(widget, 0, this, 0); + + // disconnect + // - key presses signals from widget + // - mouse activity signals from widget + // - string sending signals from widget + // + // ... and any other signals connected in addView() + disconnect(widget, 0, _emulation, 0); + + // disconnect state change signals emitted by emulation + disconnect(_emulation, 0, widget, 0); + + // close the session automatically when the last view is removed + if (_views.count() == 0) { + close(); + } +} + +// Upon a KPty error, there is no description on what that error was... +// Check to see if the given program is executable. +QString Session::checkProgram(const QString& program) +{ + QString exec = program; + + if (exec.isEmpty()) + return QString(); + + QFileInfo info(exec); + if (info.isAbsolute() && info.exists() && info.isExecutable()) { + return exec; + } + + exec = KRun::binaryName(exec, false); + exec = KShell::tildeExpand(exec); + QString pexec = KStandardDirs::findExe(exec); + if (pexec.isEmpty()) { + kError() << i18n("Could not find binary: ") << exec; + return QString(); + } + + return exec; +} + +void Session::terminalWarning(const QString& message) +{ + static const QByteArray warningText = i18nc("@info:shell Alert the user with red color text", "Warning: ").toLocal8Bit(); + QByteArray messageText = message.toLocal8Bit(); + + static const char redPenOn[] = "\033[1m\033[31m"; + static const char redPenOff[] = "\033[0m"; + + _emulation->receiveData(redPenOn, qstrlen(redPenOn)); + _emulation->receiveData("\n\r\n\r", 4); + _emulation->receiveData(warningText.constData(), qstrlen(warningText.constData())); + _emulation->receiveData(messageText.constData(), qstrlen(messageText.constData())); + _emulation->receiveData("\n\r\n\r", 4); + _emulation->receiveData(redPenOff, qstrlen(redPenOff)); +} + +QString Session::shellSessionId() const +{ + return QString::fromLatin1(_uniqueIdentifier.constData(), _uniqueIdentifier.size()); +} + +void Session::run() +{ + // extra safeguard for potential bug. + if (isRunning()) { + kWarning() << "Attempted to re-run an already running session."; + return; + } + + //check that everything is in place to run the session + if (_program.isEmpty()) { + kWarning() << "Program to run not set."; + } + if (_arguments.isEmpty()) { + kWarning() << "No command line arguments specified."; + } + if (_uniqueIdentifier.isNull()) { + _uniqueIdentifier = createSessionID(); + } + + const int CHOICE_COUNT = 3; + // if '_program' is empty , fall back to default shell. If that is not set + // then fall back to /bin/sh + QString programs[CHOICE_COUNT] = {_program, qgetenv("SHELL"), "/bin/sh"}; + QString exec; + int choice = 0; + while (choice < CHOICE_COUNT) { + exec = checkProgram(programs[choice]); + if (exec.isEmpty()) + choice++; + else + break; + } + + // if a program was specified via setProgram(), but it couldn't be found, print a warning + if (choice != 0 && choice < CHOICE_COUNT && !_program.isEmpty()) { + terminalWarning(i18n("Could not find '%1', starting '%2' instead. Please check your profile settings.", _program, exec)); + // if none of the choices are available, print a warning + } else if (choice == CHOICE_COUNT) { + terminalWarning(i18n("Could not find an interactive shell to start.")); + return; + } + + // if no arguments are specified, fall back to program name + QStringList arguments = _arguments.join(QChar(' ')).isEmpty() ? + QStringList() << exec : + _arguments; + + if (!_initialWorkingDir.isEmpty()) { + _shellProcess->setInitialWorkingDirectory(_initialWorkingDir); + } else { + _shellProcess->setInitialWorkingDirectory(QDir::currentPath()); + } + + _shellProcess->setFlowControlEnabled(_flowControlEnabled); + _shellProcess->setEraseChar(_emulation->eraseChar()); + _shellProcess->setUseUtmp(_addToUtmp); + + // this is not strictly accurate use of the COLORFGBG variable. This does not + // tell the terminal exactly which colors are being used, but instead approximates + // the color scheme as "black on white" or "white on black" depending on whether + // the background color is deemed dark or not + const QString backgroundColorHint = _hasDarkBackground ? "COLORFGBG=15;0" : "COLORFGBG=0;15"; + addEnvironmentEntry(backgroundColorHint); + + addEnvironmentEntry(QString("SHELL_SESSION_ID=%1").arg(shellSessionId())); + + addEnvironmentEntry(QString("WINDOWID=%1").arg(QString::number(windowId()))); + + const QString dbusService = QDBusConnection::sessionBus().baseService(); + addEnvironmentEntry(QString("KONSOLE_DBUS_SERVICE=%1").arg(dbusService)); + + const QString dbusObject = QString("/Sessions/%1").arg(QString::number(_sessionId)); + addEnvironmentEntry(QString("KONSOLE_DBUS_SESSION=%1").arg(dbusObject)); + + int result = _shellProcess->start(exec, arguments, _environment); + if (result < 0) { + terminalWarning(i18n("Could not start program '%1' with arguments '%2'.", exec, arguments.join(" "))); + terminalWarning(_shellProcess->errorString()); + return; + } + + _shellProcess->setWriteable(false); // We are reachable via kwrited. + + emit started(); +} + +void Session::setUserTitle(int what, const QString& caption) +{ + //set to true if anything is actually changed (eg. old _nameTitle != new _nameTitle ) + bool modified = false; + + if ((what == IconNameAndWindowTitle) || (what == WindowTitle)) { + if (_userTitle != caption) { + _userTitle = caption; + modified = true; + } + } + + if ((what == IconNameAndWindowTitle) || (what == IconName)) { + if (_iconText != caption) { + _iconText = caption; + modified = true; + } + } + + if (what == TextColor || what == BackgroundColor) { + QString colorString = caption.section(';', 0, 0); + QColor color = QColor(colorString); + if (color.isValid()) { + if (what == TextColor) + emit changeForegroundColorRequest(color); + else + emit changeBackgroundColorRequest(color); + } + } + + if (what == SessionName) { + if (_localTabTitleFormat != caption) { + _localTabTitleFormat = caption; + setTitle(Session::DisplayedTitleRole, caption); + modified = true; + } + } + + /* I don't believe this has ever worked in KDE 4.x + if (what == 31) { + QString cwd = caption; + cwd = cwd.replace(QRegExp("^~"), QDir::homePath()); + emit openUrlRequest(cwd); + }*/ + + /* The below use of 32 works but appears to non-standard. + It is from a commit from 2004 c20973eca8776f9b4f15bee5fdcb5a3205aa69de + */ + // change icon via \033]32;Icon\007 + if (what == SessionIcon) { + if (_iconName != caption) { + _iconName = caption; + + modified = true; + } + } + + if (what == ProfileChange) { + emit profileChangeCommandReceived(caption); + return; + } + + if (modified) + emit titleChanged(); +} + +QString Session::userTitle() const +{ + return _userTitle; +} +void Session::setTabTitleFormat(TabTitleContext context , const QString& format) +{ + if (context == LocalTabTitle) + _localTabTitleFormat = format; + else if (context == RemoteTabTitle) + _remoteTabTitleFormat = format; +} +QString Session::tabTitleFormat(TabTitleContext context) const +{ + if (context == LocalTabTitle) + return _localTabTitleFormat; + else if (context == RemoteTabTitle) + return _remoteTabTitleFormat; + + return QString(); +} + +void Session::silenceTimerDone() +{ + //FIXME: The idea here is that the notification popup will appear to tell the user than output from + //the terminal has stopped and the popup will disappear when the user activates the session. + // + //This breaks with the addition of multiple views of a session. The popup should disappear + //when any of the views of the session becomes active + + //FIXME: Make message text for this notification and the activity notification more descriptive. + if (_monitorSilence) { + KNotification::event("Silence", i18n("Silence in session '%1'", _nameTitle), QPixmap(), + QApplication::activeWindow(), + KNotification::CloseWhenWidgetActivated); + emit stateChanged(NOTIFYSILENCE); + } else { + emit stateChanged(NOTIFYNORMAL); + } +} + +void Session::activityTimerDone() +{ + _notifiedActivity = false; +} + +void Session::updateFlowControlState(bool suspended) +{ + if (suspended) { + if (flowControlEnabled()) { + foreach(TerminalDisplay * display, _views) { + if (display->flowControlWarningEnabled()) + display->outputSuspended(true); + } + } + } else { + foreach(TerminalDisplay * display, _views) { + display->outputSuspended(false); + } + } +} + +void Session::onPrimaryScreenInUse(bool use) +{ + emit primaryScreenInUse(use); +} + +void Session::activityStateSet(int state) +{ + // TODO: should this hardcoded interval be user configurable? + const int activityMaskInSeconds = 15; + + if (state == NOTIFYBELL) { + emit bellRequest(i18n("Bell in session '%1'", _nameTitle)); + } else if (state == NOTIFYACTIVITY) { + if (_monitorActivity && !_notifiedActivity) { + KNotification::event("Activity", i18n("Activity in session '%1'", _nameTitle), QPixmap(), + QApplication::activeWindow(), + KNotification::CloseWhenWidgetActivated); + + // mask activity notification for a while to avoid flooding + _notifiedActivity = true; + _activityTimer->start(activityMaskInSeconds * 1000); + } + + // reset the counter for monitoring continuous silence since there is activity + if (_monitorSilence) { + _silenceTimer->start(_silenceSeconds * 1000); + } + } + + if (state == NOTIFYACTIVITY && !_monitorActivity) + state = NOTIFYNORMAL; + if (state == NOTIFYSILENCE && !_monitorSilence) + state = NOTIFYNORMAL; + + emit stateChanged(state); +} + +void Session::onViewSizeChange(int /*height*/, int /*width*/) +{ + updateTerminalSize(); +} + +void Session::updateTerminalSize() +{ + int minLines = -1; + int minColumns = -1; + + // minimum number of lines and columns that views require for + // their size to be taken into consideration ( to avoid problems + // with new view widgets which haven't yet been set to their correct size ) + const int VIEW_LINES_THRESHOLD = 2; + const int VIEW_COLUMNS_THRESHOLD = 2; + + //select largest number of lines and columns that will fit in all visible views + foreach(TerminalDisplay* view, _views) { + if (view->isHidden() == false && + view->lines() >= VIEW_LINES_THRESHOLD && + view->columns() >= VIEW_COLUMNS_THRESHOLD) { + minLines = (minLines == -1) ? view->lines() : qMin(minLines , view->lines()); + minColumns = (minColumns == -1) ? view->columns() : qMin(minColumns , view->columns()); + view->processFilters(); + } + } + + // backend emulation must have a _terminal of at least 1 column x 1 line in size + if (minLines > 0 && minColumns > 0) { + _emulation->setImageSize(minLines , minColumns); + } +} +void Session::updateWindowSize(int lines, int columns) +{ + Q_ASSERT(lines > 0 && columns > 0); + _shellProcess->setWindowSize(columns, lines); +} +void Session::refresh() +{ + // attempt to get the shell process to redraw the display + // + // this requires the program running in the shell + // to cooperate by sending an update in response to + // a window size change + // + // the window size is changed twice, first made slightly larger and then + // resized back to its normal size so that there is actually a change + // in the window size (some shells do nothing if the + // new and old sizes are the same) + // + // if there is a more 'correct' way to do this, please + // send an email with method or patches to konsole-devel@kde.org + + const QSize existingSize = _shellProcess->windowSize(); + _shellProcess->setWindowSize(existingSize.width() + 1, existingSize.height()); + usleep(500); // introduce small delay to avoid changing size too quickly + _shellProcess->setWindowSize(existingSize.width(), existingSize.height()); +} + +void Session::sendSignal(int signal) +{ + const ProcessInfo* process = getProcessInfo(); + bool ok = false; + int pid; + pid = process->foregroundPid(&ok); + + if (ok) { + ::kill(pid, signal); + } +} + +bool Session::kill(int signal) +{ + if (_shellProcess->pid() <= 0) + return false; + + int result = ::kill(_shellProcess->pid(), signal); + + if (result == 0) { + if (_shellProcess->waitForFinished(1000)) + return true; + else + return false; + } else { + return false; + } +} + +void Session::close() +{ + if (isRunning()) { + if (!closeInNormalWay()) + closeInForceWay(); + } else { + // terminal process has finished, just close the session + QTimer::singleShot(1, this, SIGNAL(finished())); + } +} + +bool Session::closeInNormalWay() +{ + _autoClose = true; + _closePerUserRequest = true; + + // for the possible case where following events happen in sequence: + // + // 1). the terminal process crashes + // 2). the tab stays open and displays warning message + // 3). the user closes the tab explicitly + // + if (!isRunning()) { + emit finished(); + return true; + } + + if (Session::kill(SIGHUP)) { + return true; + } else { + kWarning() << "Process " << _shellProcess->pid() << " did not die with SIGHUP"; + _shellProcess->closePty(); + return (_shellProcess->waitForFinished(1000)); + } +} + +bool Session::closeInForceWay() +{ + _autoClose = true; + _closePerUserRequest = true; + + if (Session::kill(SIGKILL)) { + return true; + } else { + kWarning() << "Process " << _shellProcess->pid() << " did not die with SIGKILL"; + return false; + } +} + +void Session::sendText(const QString& text) +{ + _emulation->sendText(text); +} + +void Session::runCommand(const QString& command) +{ + _emulation->sendText(command + '\n'); +} + +void Session::sendMouseEvent(int buttons, int column, int line, int eventType) +{ + _emulation->sendMouseEvent(buttons, column, line, eventType); +} + +void Session::done(int exitCode, QProcess::ExitStatus exitStatus) +{ + // This slot should be triggered only one time + disconnect(_shellProcess, SIGNAL(finished(int,QProcess::ExitStatus)), + this, SLOT(done(int,QProcess::ExitStatus))); + + if (!_autoClose) { + _userTitle = i18nc("@info:shell This session is done", "Finished"); + emit titleChanged(); + return; + } + + if (_closePerUserRequest) { + emit finished(); + return; + } + + QString message; + + if (exitCode != 0) { + if (exitStatus != QProcess::NormalExit) + message = i18n("Program '%1' crashed.", _program); + else + message = i18n("Program '%1' exited with status %2.", _program, exitCode); + + //FIXME: See comments in Session::silenceTimerDone() + KNotification::event("Finished", message , QPixmap(), + QApplication::activeWindow(), + KNotification::CloseWhenWidgetActivated); + } + + if (exitStatus != QProcess::NormalExit) { + // this seeming duplicated line is for the case when exitCode is 0 + message = i18n("Program '%1' crashed.", _program); + terminalWarning(message); + } else { + emit finished(); + } +} + +Emulation* Session::emulation() const +{ + return _emulation; +} + +QString Session::keyBindings() const +{ + return _emulation->keyBindings(); +} + +QStringList Session::environment() const +{ + return _environment; +} + +void Session::setEnvironment(const QStringList& environment) +{ + _environment = environment; +} + +void Session::addEnvironmentEntry(const QString& entry) +{ + _environment << entry; +} + +int Session::sessionId() const +{ + return _sessionId; +} + +void Session::setKeyBindings(const QString& name) +{ + _emulation->setKeyBindings(name); +} + +void Session::setTitle(TitleRole role , const QString& newTitle) +{ + if (title(role) != newTitle) { + if (role == NameRole) + _nameTitle = newTitle; + else if (role == DisplayedTitleRole) + _displayTitle = newTitle; + + emit titleChanged(); + } +} + +QString Session::title(TitleRole role) const +{ + if (role == NameRole) + return _nameTitle; + else if (role == DisplayedTitleRole) + return _displayTitle; + else + return QString(); +} + +ProcessInfo* Session::getProcessInfo() +{ + ProcessInfo* process = 0; + + if (isForegroundProcessActive()) { + process = _foregroundProcessInfo; + } else { + updateSessionProcessInfo(); + process = _sessionProcessInfo; + } + + return process; +} + +void Session::updateSessionProcessInfo() +{ + Q_ASSERT(_shellProcess); + + bool ok; + // The checking for pid changing looks stupid, but it is needed + // at the moment to workaround the problem that processId() might + // return 0 + if (!_sessionProcessInfo || + (processId() != 0 && processId() != _sessionProcessInfo->pid(&ok))) { + delete _sessionProcessInfo; + _sessionProcessInfo = ProcessInfo::newInstance(processId()); + _sessionProcessInfo->setUserHomeDir(); + } + _sessionProcessInfo->update(); +} + +bool Session::updateForegroundProcessInfo() +{ + Q_ASSERT(_shellProcess); + + const int foregroundPid = _shellProcess->foregroundProcessGroup(); + if (foregroundPid != _foregroundPid) { + delete _foregroundProcessInfo; + _foregroundProcessInfo = ProcessInfo::newInstance(foregroundPid); + _foregroundPid = foregroundPid; + } + + if (_foregroundProcessInfo) { + _foregroundProcessInfo->update(); + return _foregroundProcessInfo->isValid(); + } else { + return false; + } +} + +bool Session::isRemote() +{ + ProcessInfo* process = getProcessInfo(); + + bool ok = false; + return (process->name(&ok) == "ssh" && ok); +} + +QString Session::getDynamicTitle() +{ + // update current directory from process + updateWorkingDirectory(); + ProcessInfo* process = getProcessInfo(); + + // format tab titles using process info + bool ok = false; + QString title; + if (process->name(&ok) == "ssh" && ok) { + SSHProcessInfo sshInfo(*process); + title = sshInfo.format(tabTitleFormat(Session::RemoteTabTitle)); + } else { + title = process->format(tabTitleFormat(Session::LocalTabTitle)); + } + + return title; +} + +KUrl Session::getUrl() +{ + QString path; + + updateSessionProcessInfo(); + if (_sessionProcessInfo->isValid()) { + bool ok = false; + + // check if foreground process is bookmark-able + if (isForegroundProcessActive()) { + // for remote connections, save the user and host + // bright ideas to get the directory at the other end are welcome :) + if (_foregroundProcessInfo->name(&ok) == "ssh" && ok) { + SSHProcessInfo sshInfo(*_foregroundProcessInfo); + + path = "ssh://" + sshInfo.userName() + '@' + sshInfo.host(); + + QString port = sshInfo.port(); + if (!port.isEmpty() && port != "22") { + path.append(':' + port); + } + } else { + path = _foregroundProcessInfo->currentDir(&ok); + if (!ok) + path.clear(); + } + } else { // otherwise use the current working directory of the shell process + path = _sessionProcessInfo->currentDir(&ok); + if (!ok) + path.clear(); + } + } + + return KUrl(path); +} + +void Session::setIconName(const QString& iconName) +{ + if (iconName != _iconName) { + _iconName = iconName; + emit titleChanged(); + } +} + +void Session::setIconText(const QString& iconText) +{ + _iconText = iconText; +} + +QString Session::iconName() const +{ + return _iconName; +} + +QString Session::iconText() const +{ + return _iconText; +} + +void Session::setHistoryType(const HistoryType& hType) +{ + _emulation->setHistory(hType); +} + +const HistoryType& Session::historyType() const +{ + return _emulation->history(); +} + +void Session::clearHistory() +{ + _emulation->clearHistory(); +} + +QStringList Session::arguments() const +{ + return _arguments; +} + +QString Session::program() const +{ + return _program; +} + +bool Session::isMonitorActivity() const +{ + return _monitorActivity; +} +bool Session::isMonitorSilence() const +{ + return _monitorSilence; +} + +void Session::setMonitorActivity(bool monitor) +{ + if (_monitorActivity == monitor) + return; + + _monitorActivity = monitor; + _notifiedActivity = false; + + // This timer is meaningful only after activity has been notified + _activityTimer->stop(); + + activityStateSet(NOTIFYNORMAL); +} + +void Session::setMonitorSilence(bool monitor) +{ + if (_monitorSilence == monitor) + return; + + _monitorSilence = monitor; + if (_monitorSilence) { + _silenceTimer->start(_silenceSeconds * 1000); + } else { + _silenceTimer->stop(); + } + + activityStateSet(NOTIFYNORMAL); +} + +void Session::setMonitorSilenceSeconds(int seconds) +{ + _silenceSeconds = seconds; + if (_monitorSilence) { + _silenceTimer->start(_silenceSeconds * 1000); + } +} + +void Session::setAddToUtmp(bool add) +{ + _addToUtmp = add; +} + +void Session::setAutoClose(bool close) +{ + _autoClose = close; +} + +bool Session::autoClose() const +{ + return _autoClose; +} + +void Session::setFlowControlEnabled(bool enabled) +{ + _flowControlEnabled = enabled; + + if (_shellProcess) + _shellProcess->setFlowControlEnabled(_flowControlEnabled); + + emit flowControlEnabledChanged(enabled); +} +bool Session::flowControlEnabled() const +{ + if (_shellProcess) + return _shellProcess->flowControlEnabled(); + else + return _flowControlEnabled; +} +void Session::fireZModemDetected() +{ + if (!_zmodemBusy) { + QTimer::singleShot(10, this, SIGNAL(zmodemDetected())); + _zmodemBusy = true; + } +} + +void Session::cancelZModem() +{ + _shellProcess->sendData("\030\030\030\030", 4); // Abort + _zmodemBusy = false; +} + +void Session::startZModem(const QString& zmodem, const QString& dir, const QStringList& list) +{ + _zmodemBusy = true; + _zmodemProc = new QProcess(); + _zmodemProc->setProcessChannelMode(QProcess::SeparateChannels); + + if (!dir.isEmpty()) + _zmodemProc->setWorkingDirectory(dir); + + connect(_zmodemProc, SIGNAL(readyReadStandardOutput()), + this, SLOT(zmodemReadAndSendBlock())); + connect(_zmodemProc, SIGNAL(readyReadStandardError()), + this, SLOT(zmodemReadStatus())); + connect(_zmodemProc, SIGNAL(finished(int,QProcess::ExitStatus)), + this, SLOT(zmodemFinished())); + + QStringList procargs; + procargs << "-v" << list; + _zmodemProc->start(zmodem, procargs); + + disconnect(_shellProcess, SIGNAL(receivedData(const char*,int)), + this, SLOT(onReceiveBlock(const char*,int))); + connect(_shellProcess, SIGNAL(receivedData(const char*,int)), + this, SLOT(zmodemReceiveBlock(const char*,int))); + + _zmodemProgress = new ZModemDialog(QApplication::activeWindow(), false, + i18n("ZModem Progress")); + + connect(_zmodemProgress, SIGNAL(user1Clicked()), + this, SLOT(zmodemFinished())); + + _zmodemProgress->show(); +} + +void Session::zmodemReadAndSendBlock() +{ + _zmodemProc->setReadChannel(QProcess::StandardOutput); + QByteArray data = _zmodemProc->readAll(); + + if (data.count() == 0) + return; + + _shellProcess->sendData(data.constData(), data.count()); +} + +void Session::zmodemReadStatus() +{ + _zmodemProc->setReadChannel(QProcess::StandardError); + QByteArray msg = _zmodemProc->readAll(); + while (!msg.isEmpty()) { + int i = msg.indexOf('\015'); + int j = msg.indexOf('\012'); + QByteArray txt; + if ((i != -1) && ((j == -1) || (i < j))) { + msg = msg.mid(i + 1); + } else if (j != -1) { + txt = msg.left(j); + msg = msg.mid(j + 1); + } else { + txt = msg; + msg.truncate(0); + } + if (!txt.isEmpty()) + _zmodemProgress->addProgressText(QString::fromLocal8Bit(txt)); + } +} + +void Session::zmodemReceiveBlock(const char* data, int len) +{ + QByteArray bytes(data, len); + + _zmodemProc->write(bytes); +} + +void Session::zmodemFinished() +{ + /* zmodemFinished() is called by QProcess's finished() and + ZModemDialog's user1Clicked(). Therefore, an invocation by + user1Clicked() will recursively invoke this function again + when the QProcess is deleted! */ + if (_zmodemProc) { + QProcess* process = _zmodemProc; + _zmodemProc = 0; // Set _zmodemProc to 0 avoid recursive invocations! + _zmodemBusy = false; + delete process; // Now, the QProcess may be disposed safely. + + disconnect(_shellProcess, SIGNAL(receivedData(const char*,int)), + this , SLOT(zmodemReceiveBlock(const char*,int))); + connect(_shellProcess, SIGNAL(receivedData(const char*,int)), + this, SLOT(onReceiveBlock(const char*,int))); + + _shellProcess->sendData("\030\030\030\030", 4); // Abort + _shellProcess->sendData("\001\013\n", 3); // Try to get prompt back + _zmodemProgress->transferDone(); + } +} + +void Session::onReceiveBlock(const char* buf, int len) +{ + _emulation->receiveData(buf, len); +} + +QSize Session::size() +{ + return _emulation->imageSize(); +} + +void Session::setSize(const QSize& size) +{ + if ((size.width() <= 1) || (size.height() <= 1)) + return; + + emit resizeRequest(size); +} + +QSize Session::preferredSize() const +{ + return _preferredSize; +} + +void Session::setPreferredSize(const QSize& size) +{ + _preferredSize = size; +} + +int Session::processId() const +{ + return _shellProcess->pid(); +} + +void Session::setTitle(int role , const QString& title) +{ + switch (role) { + case 0: + this->setTitle(Session::NameRole, title); + break; + case 1: + this->setTitle(Session::DisplayedTitleRole, title); + + // without these, that title will be overridden by the expansion of + // title format shortly after, which will confuses users. + _localTabTitleFormat = title; + _remoteTabTitleFormat = title; + + break; + } +} + +QString Session::title(int role) const +{ + switch (role) { + case 0: + return this->title(Session::NameRole); + case 1: + return this->title(Session::DisplayedTitleRole); + default: + return QString(); + } +} + +void Session::setTabTitleFormat(int context , const QString& format) +{ + switch (context) { + case 0: + this->setTabTitleFormat(Session::LocalTabTitle, format); + break; + case 1: + this->setTabTitleFormat(Session::RemoteTabTitle, format); + break; + } +} + +QString Session::tabTitleFormat(int context) const +{ + switch (context) { + case 0: + return this->tabTitleFormat(Session::LocalTabTitle); + case 1: + return this->tabTitleFormat(Session::RemoteTabTitle); + default: + return QString(); + } +} + +void Session::setHistorySize(int lines) +{ + if (lines < 0) { + setHistoryType(HistoryTypeFile()); + } else if (lines == 0) { + setHistoryType(HistoryTypeNone()); + } else { + setHistoryType(CompactHistoryType(lines)); + } +} + +int Session::historySize() const +{ + const HistoryType& currentHistory = historyType(); + + if (currentHistory.isEnabled()) { + if (currentHistory.isUnlimited()) { + return -1; + } else { + return currentHistory.maximumLineCount(); + } + } else { + return 0; + } +} + +int Session::foregroundProcessId() +{ + int pid; + + bool ok = false; + pid = getProcessInfo()->pid(&ok); + if (!ok) + pid = -1; + + return pid; +} + +bool Session::isForegroundProcessActive() +{ + // foreground process info is always updated after this + return updateForegroundProcessInfo() && (processId() != _foregroundPid); +} + +QString Session::foregroundProcessName() +{ + QString name; + + if (updateForegroundProcessInfo()) { + bool ok = false; + name = _foregroundProcessInfo->name(&ok); + if (!ok) + name.clear(); + } + + return name; +} + +void Session::saveSession(KConfigGroup& group) +{ + group.writePathEntry("WorkingDir", currentWorkingDirectory()); + group.writeEntry("LocalTab", tabTitleFormat(LocalTabTitle)); + group.writeEntry("RemoteTab", tabTitleFormat(RemoteTabTitle)); + group.writeEntry("SessionGuid", _uniqueIdentifier); + group.writeEntry("Encoding", QString(codec())); +} + +void Session::restoreSession(KConfigGroup& group) +{ + QString value; + + value = group.readPathEntry("WorkingDir", QString()); + if (!value.isEmpty()) setInitialWorkingDirectory(value); + value = group.readEntry("LocalTab"); + if (!value.isEmpty()) setTabTitleFormat(LocalTabTitle, value); + value = group.readEntry("RemoteTab"); + if (!value.isEmpty()) setTabTitleFormat(RemoteTabTitle, value); + value = group.readEntry("SessionGuid"); + if (!value.isEmpty()) _uniqueIdentifier = value.toLatin1(); + value = group.readEntry("Encoding"); + if (!value.isEmpty()) setCodec(value.toUtf8()); +} + +SessionGroup::SessionGroup(QObject* parent) + : QObject(parent), _masterMode(0) +{ +} +SessionGroup::~SessionGroup() +{ +} +int SessionGroup::masterMode() const +{ + return _masterMode; +} +QList SessionGroup::sessions() const +{ + return _sessions.keys(); +} +bool SessionGroup::masterStatus(Session* session) const +{ + return _sessions[session]; +} + +void SessionGroup::addSession(Session* session) +{ + connect(session, SIGNAL(finished()), this, SLOT(sessionFinished())); + _sessions.insert(session, false); +} +void SessionGroup::removeSession(Session* session) +{ + disconnect(session, SIGNAL(finished()), this, SLOT(sessionFinished())); + setMasterStatus(session, false); + _sessions.remove(session); +} +void SessionGroup::sessionFinished() +{ + Session* session = qobject_cast(sender()); + Q_ASSERT(session); + removeSession(session); +} +void SessionGroup::setMasterMode(int mode) +{ + _masterMode = mode; +} +QList SessionGroup::masters() const +{ + return _sessions.keys(true); +} +void SessionGroup::setMasterStatus(Session* session , bool master) +{ + const bool wasMaster = _sessions[session]; + + if (wasMaster == master) { + // No status change -> nothing to do. + return; + } + _sessions[session] = master; + + if (master) { + connect(session->emulation(), SIGNAL(sendData(const char*,int)), + this, SLOT(forwardData(const char*,int))); + } else { + disconnect(session->emulation(), SIGNAL(sendData(const char*,int)), + this, SLOT(forwardData(const char*,int))); + } +} +void SessionGroup::forwardData(const char* data, int size) +{ + static bool _inForwardData = false; + if (_inForwardData) { // Avoid recursive calls among session groups! + // A recursive call happens when a master in group A calls forwardData() + // in group B. If one of the destination sessions in group B is also a + // master of a group including the master session of group A, this would + // again call forwardData() in group A, and so on. + return; + } + + _inForwardData = true; + foreach(Session* other, _sessions.keys()) { + if (!_sessions[other]) { + other->emulation()->sendString(data, size); + } + } + _inForwardData = false; +} + +#include "moc_Session.cpp" + diff --git a/konsole/src/Session.h b/konsole/src/Session.h new file mode 100644 index 00000000..a4ce755c --- /dev/null +++ b/konsole/src/Session.h @@ -0,0 +1,831 @@ +/* + This file is part of Konsole, an X terminal. + + Copyright 2007-2008 by Robert Knight + Copyright 1997,1998 by Lars Doelle + Copyright 2009 by Thomas Dreibholz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +#ifndef SESSION_H +#define SESSION_H + +// Qt +#include +#include +#include +#include +#include +#include +#include +#include + +// KDE +#include + +// Konsole +#include "konsoleprivate_export.h" + +class KConfigGroup; + +namespace Konsole +{ +class Emulation; +class Pty; +class ProcessInfo; +class TerminalDisplay; +class ZModemDialog; +class HistoryType; + +/** + * Platform-specific main shortcut "opcode": + */ +enum Modifier { + ACCEL = Qt::CTRL +}; + +/** + * Represents a terminal session consisting of a pseudo-teletype and a terminal emulation. + * The pseudo-teletype (or PTY) handles I/O between the terminal process and Konsole. + * The terminal emulation ( Emulation and subclasses ) processes the output stream from the + * PTY and produces a character image which is then shown on views connected to the session. + * + * Each Session can be connected to one or more views by using the addView() method. + * The attached views can then display output from the program running in the terminal + * or send input to the program in the terminal in the form of keypresses and mouse + * activity. + */ +class KONSOLEPRIVATE_EXPORT Session : public QObject +{ + Q_OBJECT + Q_CLASSINFO("D-Bus Interface", "org.kde.konsole.Session") + +public: + Q_PROPERTY(QString name READ nameTitle) + Q_PROPERTY(int processId READ processId) + Q_PROPERTY(QString keyBindings READ keyBindings WRITE setKeyBindings) + Q_PROPERTY(QSize size READ size WRITE setSize) + + /** + * Constructs a new session. + * + * To start the terminal process, call the run() method, + * after specifying the program and arguments + * using setProgram() and setArguments() + * + * If no program or arguments are specified explicitly, the Session + * falls back to using the program specified in the SHELL environment + * variable. + */ + explicit Session(QObject* parent = 0); + ~Session(); + + /** + * Connect to an existing terminal. When a new Session() is constructed it + * automatically searches for and opens a new teletype. If you want to + * use an existing teletype (given its file descriptor) call this after + * constructing the session. + * + * Calling openTeletype() while a session is running has no effect. + * + * @param masterFd The file descriptor of the pseudo-teletype master (See KPtyProcess::KPtyProcess()) + */ + void openTeletype(int masterFd); + + /** + * Returns true if the session is currently running. This will be true + * after run() has been called successfully. + */ + bool isRunning() const; + + /** + * Adds a new view for this session. + * + * The viewing widget will display the output from the terminal and + * input from the viewing widget (key presses, mouse activity etc.) + * will be sent to the terminal. + * + * Views can be removed using removeView(). The session is automatically + * closed when the last view is removed. + */ + void addView(TerminalDisplay* widget); + /** + * Removes a view from this session. When the last view is removed, + * the session will be closed automatically. + * + * @p widget will no longer display output from or send input + * to the terminal + */ + void removeView(TerminalDisplay* widget); + + /** + * Returns the views connected to this session + */ + QList views() const; + + /** + * Returns the terminal emulation instance being used to encode / decode + * characters to / from the process. + */ + Emulation* emulation() const; + + /** Returns the unique ID for this session. */ + int sessionId() const; + + /** + * This enum describes the contexts for which separate + * tab title formats may be specified. + */ + enum TabTitleContext { + /** Default tab title format */ + LocalTabTitle, + /** + * Tab title format used session currently contains + * a connection to a remote computer (via SSH) + */ + RemoteTabTitle + }; + + /** + * Returns true if the session currently contains a connection to a + * remote computer. It currently supports ssh. + */ + bool isRemote(); + + /** + * Sets the format used by this session for tab titles. + * + * @param context The context whose format should be set. + * @param format The tab title format. This may be a mixture + * of plain text and dynamic elements denoted by a '%' character + * followed by a letter. (eg. %d for directory). The dynamic + * elements available depend on the @p context + */ + void setTabTitleFormat(TabTitleContext context , const QString& format); + /** Returns the format used by this session for tab titles. */ + QString tabTitleFormat(TabTitleContext context) const; + + /** Returns the arguments passed to the shell process when run() is called. */ + QStringList arguments() const; + /** Returns the program name of the shell process started when run() is called. */ + QString program() const; + + /** + * Sets the command line arguments which the session's program will be passed when + * run() is called. + */ + void setArguments(const QStringList& arguments); + /** Sets the program to be executed when run() is called. */ + void setProgram(const QString& program); + + /** Returns the session's current working directory. */ + QString initialWorkingDirectory() { + return _initialWorkingDir; + } + + /** + * Sets the initial working directory for the session when it is run + * This has no effect once the session has been started. + */ + void setInitialWorkingDirectory(const QString& dir); + + /** + * Returns the current directory of the foreground process in the session + */ + QString currentWorkingDirectory(); + + /** + * Sets the type of history store used by this session. + * Lines of output produced by the terminal are added + * to the history store. The type of history store + * used affects the number of lines which can be + * remembered before they are lost and the storage + * (in memory, on-disk etc.) used. + */ + void setHistoryType(const HistoryType& type); + /** + * Returns the type of history store used by this session. + */ + const HistoryType& historyType() const; + /** + * Clears the history store used by this session. + */ + void clearHistory(); + + /** + * Sets the key bindings used by this session. The bindings + * specify how input key sequences are translated into + * the character stream which is sent to the terminal. + * + * @param name The name of the key bindings to use. The + * names of available key bindings can be determined using the + * KeyboardTranslatorManager class. + */ + void setKeyBindings(const QString& name); + /** Returns the name of the key bindings used by this session. */ + QString keyBindings() const; + + /** + * This enum describes the available title roles. + */ + enum TitleRole { + /** The name of the session. */ + NameRole, + /** The title of the session which is displayed in tabs etc. */ + DisplayedTitleRole + }; + + /** + * Return the session title set by the user (ie. the program running + * in the terminal), or an empty string if the user has not set a custom title + */ + QString userTitle() const; + + /** Convenience method used to read the name property. Returns title(Session::NameRole). */ + QString nameTitle() const { + return title(Session::NameRole); + } + /** Returns a title generated from tab format and process information. */ + QString getDynamicTitle(); + + /** Sets the name of the icon associated with this session. */ + void setIconName(const QString& iconName); + /** Returns the name of the icon associated with this session. */ + QString iconName() const; + + /** Return URL for the session. */ + KUrl getUrl(); + + /** Sets the text of the icon associated with this session. */ + void setIconText(const QString& iconText); + /** Returns the text of the icon associated with this session. */ + QString iconText() const; + + /** Sets the session's title for the specified @p role to @p title. */ + void setTitle(TitleRole role , const QString& title); + + /** Returns the session's title for the specified @p role. */ + QString title(TitleRole role) const; + + /** + * Specifies whether a utmp entry should be created for the pty used by this session. + * If true, KPty::login() is called when the session is started. + */ + void setAddToUtmp(bool); + + /** + * Specifies whether to close the session automatically when the terminal + * process terminates. + */ + void setAutoClose(bool close); + + /** See setAutoClose() */ + bool autoClose() const; + + /** Returns true if the user has started a program in the session. */ + bool isForegroundProcessActive(); + + /** Returns the name of the current foreground process. */ + QString foregroundProcessName(); + + /** Returns the terminal session's window size in lines and columns. */ + QSize size(); + /** + * Emits a request to resize the session to accommodate + * the specified window size. + * + * @param size The size in lines and columns to request. + */ + void setSize(const QSize& size); + + QSize preferredSize() const; + + void setPreferredSize(const QSize & size); + + /** + * Sets whether the session has a dark background or not. The session + * uses this information to set the COLORFGBG variable in the process's + * environment, which allows the programs running in the terminal to determine + * whether the background is light or dark and use appropriate colors by default. + * + * This has no effect once the session is running. + */ + void setDarkBackground(bool darkBackground); + + /** + * Attempts to get the shell program to redraw the current display area. + * This can be used after clearing the screen, for example, to get the + * shell to redraw the prompt line. + */ + void refresh(); + + void startZModem(const QString& rz, const QString& dir, const QStringList& list); + void cancelZModem(); + bool isZModemBusy() { + return _zmodemBusy; + } + + /** + * Possible values of the @p what parameter for setUserTitle() + * See "Operating System Controls" section on http://rtfm.etla.org/xterm/ctlseq.html + */ + enum UserTitleChange { + IconNameAndWindowTitle = 0, + IconName = 1, + WindowTitle = 2, + TextColor = 10, + BackgroundColor = 11, + SessionName = 30, // Non-standard + SessionIcon = 32, // Non-standard + ProfileChange = 50 // this clashes with Xterm's font change command + }; + + // Sets the text codec used by this sessions terminal emulation. + void setCodec(QTextCodec* codec); + + // session management + void saveSession(KConfigGroup& group); + void restoreSession(KConfigGroup& group); + + void sendSignal(int signal); + +public slots: + + /** + * Starts the terminal session. + * + * This creates the terminal process and connects the teletype to it. + */ + void run(); + + /** + * Returns the environment of this session as a list of strings like + * VARIABLE=VALUE + */ + Q_SCRIPTABLE QStringList environment() const; + + /** + * Sets the environment for this session. + * @p environment should be a list of strings like + * VARIABLE=VALUE + */ + Q_SCRIPTABLE void setEnvironment(const QStringList& environment); + + /** + * Adds one entry for the environment of this session + * @p entry should be like VARIABLE=VALUE + */ + void addEnvironmentEntry(const QString& entry); + + /** + * Closes the terminal session. It kills the terminal process by calling + * closeInNormalWay() and, optionally, closeInForceWay(). + */ + //Q_SCRIPTABLE void close(); // This cause the menu issues bko 185466 + void close(); + + /** + * Kill the terminal process in normal way. This sends a hangup signal + * (SIGHUP) to the terminal process and causes the finished() signal to + * be emitted. If the process does not respond to the SIGHUP signal then + * the terminal connection (the pty) is closed and Konsole waits for the + * process to exit. This method works most of the time, but fails with some + * programs which respond to SIGHUP signal in special way, such as autossh + * and irssi. + */ + bool closeInNormalWay(); + + /** + * kill terminal process in force way. This send a SIGKILL signal to the + * terminal process. It should be called only after closeInNormalWay() has + * failed. Take it as last resort. + */ + bool closeInForceWay(); + + /** + * Changes the session title or other customizable aspects of the terminal + * emulation display. For a list of what may be changed see the + * Emulation::titleChanged() signal. + * + * @param what The feature being changed. Value is one of UserTitleChange + * @param caption The text part of the terminal command + */ + void setUserTitle(int what , const QString& caption); + + /** + * Enables monitoring for activity in the session. + * This will cause notifySessionState() to be emitted + * with the NOTIFYACTIVITY state flag when output is + * received from the terminal. + */ + Q_SCRIPTABLE void setMonitorActivity(bool); + + /** Returns true if monitoring for activity is enabled. */ + Q_SCRIPTABLE bool isMonitorActivity() const; + + /** + * Enables monitoring for silence in the session. + * This will cause notifySessionState() to be emitted + * with the NOTIFYSILENCE state flag when output is not + * received from the terminal for a certain period of + * time, specified with setMonitorSilenceSeconds() + */ + Q_SCRIPTABLE void setMonitorSilence(bool); + + /** + * Returns true if monitoring for inactivity (silence) + * in the session is enabled. + */ + Q_SCRIPTABLE bool isMonitorSilence() const; + + /** See setMonitorSilence() */ + Q_SCRIPTABLE void setMonitorSilenceSeconds(int seconds); + + /** + * Sets whether flow control is enabled for this terminal + * session. + */ + Q_SCRIPTABLE void setFlowControlEnabled(bool enabled); + + /** Returns whether flow control is enabled for this terminal session. */ + Q_SCRIPTABLE bool flowControlEnabled() const; + + /** + * Sends @p text to the current foreground terminal program. + */ + Q_SCRIPTABLE void sendText(const QString& text); + + /** + * Sends @p command to the current foreground terminal program. + */ + Q_SCRIPTABLE void runCommand(const QString& command); + + /** + * Sends a mouse event of type @p eventType emitted by button + * @p buttons on @p column/@p line to the current foreground + * terminal program + */ + Q_SCRIPTABLE void sendMouseEvent(int buttons, int column, int line, int eventType); + + /** + * Returns the process id of the terminal process. + * This is the id used by the system API to refer to the process. + */ + Q_SCRIPTABLE int processId() const; + + /** + * Returns the process id of the terminal's foreground process. + * This is initially the same as processId() but can change + * as the user starts other programs inside the terminal. + */ + Q_SCRIPTABLE int foregroundProcessId(); + + /** Sets the text codec used by this sessions terminal emulation. + * Overloaded to accept a QByteArray for convenience since DBus + * does not accept QTextCodec directly. + */ + Q_SCRIPTABLE bool setCodec(QByteArray codec); + + /** Returns the codec used to decode incoming characters in this + * terminal emulation + */ + Q_SCRIPTABLE QByteArray codec(); + + /** Sets the session's title for the specified @p role to @p title. + * This is an overloaded member function for setTitle(TitleRole, QString) + * provided for convenience since enum data types may not be + * exported directly through DBus + */ + Q_SCRIPTABLE void setTitle(int role, const QString& title); + + /** Returns the session's title for the specified @p role. + * This is an overloaded member function for setTitle(TitleRole) + * provided for convenience since enum data types may not be + * exported directly through DBus + */ + Q_SCRIPTABLE QString title(int role) const; + + /** Returns the "friendly" version of the ID of this session. + * The same text appears in the SHELL_SESSION_ID environment variable. + */ + Q_SCRIPTABLE QString shellSessionId() const; + + /** Sets the session's tab title format for the specified @p context to @p format. + * This is an overloaded member function for setTabTitleFormat(TabTitleContext, QString) + * provided for convenience since enum data types may not be + * exported directly through DBus + */ + Q_SCRIPTABLE void setTabTitleFormat(int context, const QString& format); + + /** Returns the session's tab title format for the specified @p context. + * This is an overloaded member function for tabTitleFormat(TitleRole) + * provided for convenience since enum data types may not be + * exported directly through DBus + */ + Q_SCRIPTABLE QString tabTitleFormat(int context) const; + + /** + * Sets the history capacity of this session. + * + * @param lines The history capacity in unit of lines. Its value can be: + *
      + *
    • positive integer - fixed size history
    • + *
    • 0 - no history
    • + *
    • negative integer - unlimited history
    • + *
    + */ + Q_SCRIPTABLE void setHistorySize(int lines); + + /** + * Returns the history capacity of this session. + */ + Q_SCRIPTABLE int historySize() const; + +signals: + + /** Emitted when the terminal process starts. */ + void started(); + + /** + * Emitted when the terminal process exits. + */ + void finished(); + + /** Emitted when the session's title has changed. */ + void titleChanged(); + + /** + * Emitted when the activity state of this session changes. + * + * @param state The new state of the session. This may be one + * of NOTIFYNORMAL, NOTIFYSILENCE or NOTIFYACTIVITY + */ + void stateChanged(int state); + + /** + * Emitted when the current working directory of this session changes. + * + * @param dir The new current working directory of the session. + */ + void currentDirectoryChanged(const QString& dir); + + /** Emitted when a bell event occurs in the session. */ + void bellRequest(const QString& message); + + /** + * Requests that the color the text for any tabs associated with + * this session should be changed; + * + * TODO: Document what the parameter does + */ + void changeTabTextColorRequest(int); + + /** + * Requests that the background color of views on this session + * should be changed. + */ + void changeBackgroundColorRequest(const QColor&); + /** + * Requests that the text color of views on this session should + * be changed to @p color. + */ + void changeForegroundColorRequest(const QColor&); + + /** TODO: Document me. */ + void openUrlRequest(const QString& url); + + /** + * Emitted when the request for data transmission through ZModem + * protocol is detected. + */ + void zmodemDetected(); + + /** + * Emitted when the terminal process requests a change + * in the size of the terminal window. + * + * @param size The requested window size in terms of lines and columns. + */ + void resizeRequest(const QSize& size); + + /** + * Emitted when a profile change command is received from the terminal. + * + * @param text The text of the command. This is a string of the form + * "PropertyName=Value;PropertyName=Value ..." + */ + void profileChangeCommandReceived(const QString& text); + + /** + * Emitted when the flow control state changes. + * + * @param enabled True if flow control is enabled or false otherwise. + */ + void flowControlEnabledChanged(bool enabled); + + /** + * Emitted when the active screen is switched, to indicate whether the primary + * screen is in use. + * + * This signal serves as a relayer of Emulation::priamyScreenInUse(bool), + * making it usable for higher level component. + */ + void primaryScreenInUse(bool use); + + /** + * Emitted when the text selection is changed. + * + * This signal serves as a relayer of Emulation::selectedText(QString), + * making it usable for higher level component. + */ + void selectionChanged(const QString& text); + +private slots: + void done(int, QProcess::ExitStatus); + + void fireZModemDetected(); + + void onReceiveBlock(const char* buffer, int len); + void silenceTimerDone(); + void activityTimerDone(); + + void onViewSizeChange(int height, int width); + + void activityStateSet(int); + + //automatically detach views from sessions when view is destroyed + void viewDestroyed(QObject* view); + + void zmodemReadStatus(); + void zmodemReadAndSendBlock(); + void zmodemReceiveBlock(const char* data, int len); + void zmodemFinished(); + + void updateFlowControlState(bool suspended); + void updateWindowSize(int lines, int columns); + + // signal relayer + void onPrimaryScreenInUse(bool use); + +private: + // checks that the binary 'program' is available and can be executed + // returns the binary name if available or an empty string otherwise + static QString checkProgram(const QString& program); + + void updateTerminalSize(); + WId windowId() const; + bool kill(int signal); + // print a warning message in the terminal. This is used + // if the program fails to start, or if the shell exits in + // an unsuccessful manner + void terminalWarning(const QString& message); + ProcessInfo* getProcessInfo(); + void updateSessionProcessInfo(); + bool updateForegroundProcessInfo(); + void updateWorkingDirectory(); + + QByteArray _uniqueIdentifier; // SHELL_SESSION_ID + + Pty* _shellProcess; + Emulation* _emulation; + + QList _views; + + // monitor activity & silence + bool _monitorActivity; + bool _monitorSilence; + bool _notifiedActivity; + int _silenceSeconds; + QTimer* _silenceTimer; + QTimer* _activityTimer; + + bool _autoClose; + bool _closePerUserRequest; + + QString _nameTitle; + QString _displayTitle; + QString _userTitle; + + QString _localTabTitleFormat; + QString _remoteTabTitleFormat; + + QString _iconName; + QString _iconText; // not actually used + bool _addToUtmp; + bool _flowControlEnabled; + + QString _program; + QStringList _arguments; + + QStringList _environment; + int _sessionId; + + QString _initialWorkingDir; + QString _currentWorkingDir; + + ProcessInfo* _sessionProcessInfo; + ProcessInfo* _foregroundProcessInfo; + int _foregroundPid; + + // ZModem + bool _zmodemBusy; + QProcess* _zmodemProc; + ZModemDialog* _zmodemProgress; + + bool _hasDarkBackground; + + QSize _preferredSize; + + static int lastSessionId; +}; + +/** + * Provides a group of sessions which is divided into master and slave sessions. + * Activity in master sessions can be propagated to all sessions within the group. + * The type of activity which is propagated and method of propagation is controlled + * by the masterMode() flags. + */ +class SessionGroup : public QObject +{ + Q_OBJECT + +public: + /** Constructs an empty session group. */ + explicit SessionGroup(QObject* parent); + /** Destroys the session group and removes all connections between master and slave sessions. */ + ~SessionGroup(); + + /** Adds a session to the group. */ + void addSession(Session* session); + /** Removes a session from the group. */ + void removeSession(Session* session); + + /** Returns the list of sessions currently in the group. */ + QList sessions() const; + + /** + * Sets whether a particular session is a master within the group. + * Changes or activity in the group's master sessions may be propagated + * to all the sessions in the group, depending on the current masterMode() + * + * @param session The session whose master status should be changed. + * @param master True to make this session a master or false otherwise + */ + void setMasterStatus(Session* session , bool master); + /** Returns the master status of a session. See setMasterStatus() */ + bool masterStatus(Session* session) const; + + /** + * This enum describes the options for propagating certain activity or + * changes in the group's master sessions to all sessions in the group. + */ + enum MasterMode { + /** + * Any input key presses in the master sessions are sent to all + * sessions in the group. + */ + CopyInputToAll = 1 + }; + + /** + * Specifies which activity in the group's master sessions is propagated + * to all sessions in the group. + * + * @param mode A bitwise OR of MasterMode flags. + */ + void setMasterMode(int mode); + /** + * Returns a bitwise OR of the active MasterMode flags for this group. + * See setMasterMode() + */ + int masterMode() const; + +private slots: + void sessionFinished(); + void forwardData(const char* data, int size); + +private: + QList masters() const; + + // maps sessions to their master status + QHash _sessions; + + int _masterMode; +}; +} + +#endif diff --git a/konsole/src/SessionController.cpp b/konsole/src/SessionController.cpp new file mode 100644 index 00000000..8fdada2d --- /dev/null +++ b/konsole/src/SessionController.cpp @@ -0,0 +1,2045 @@ +/* + Copyright 2006-2008 by Robert Knight + Copyright 2009 by Thomas Dreibholz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +// Own +#include "SessionController.h" + +// Qt +#include +#include +#include +#include +#include +#include + +// KDE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Konsole +#include "EditProfileDialog.h" +#include "CopyInputDialog.h" +#include "Emulation.h" +#include "Filter.h" +#include "History.h" +#include "HistorySizeDialog.h" +#include "IncrementalSearchBar.h" +#include "RenameTabDialog.h" +#include "ScreenWindow.h" +#include "Session.h" +#include "ProfileList.h" +#include "TerminalDisplay.h" +#include "SessionManager.h" +#include "Enumeration.h" +#include "PrintOptions.h" + +// for SaveHistoryTask +#include +#include +#include +#include "TerminalCharacterDecoder.h" + +// For Unix signal names +#include + +using namespace Konsole; + +// TODO - Replace the icon choices below when suitable icons for silence and +// activity are available +const KIcon SessionController::_activityIcon("dialog-information"); +const KIcon SessionController::_silenceIcon("dialog-information"); +const KIcon SessionController::_broadcastIcon("emblem-important"); + +QSet SessionController::_allControllers; +int SessionController::_lastControllerId; + +SessionController::SessionController(Session* session , TerminalDisplay* view, QObject* parent) + : ViewProperties(parent) + , KXMLGUIClient() + , _session(session) + , _view(view) + , _copyToGroup(0) + , _profileList(0) + , _previousState(-1) + , _viewUrlFilter(0) + , _searchFilter(0) + , _copyInputToAllTabsAction(0) + , _findAction(0) + , _findNextAction(0) + , _findPreviousAction(0) + , _urlFilterUpdateRequired(false) + , _searchStartLine(0) + , _prevSearchResultLine(0) + , _searchBar(0) + , _codecAction(0) + , _switchProfileMenu(0) + , _webSearchMenu(0) + , _openWithMenu(0) + , _listenForScreenWindowUpdates(false) + , _preventClose(false) + , _keepIconUntilInteraction(false) + , _showMenuAction(0) + , _isSearchBarEnabled(false) +{ + Q_ASSERT(session); + Q_ASSERT(view); + + // handle user interface related to session (menus etc.) + if (isKonsolePart()) { + setXMLFile("konsole/partui.rc"); + setupCommonActions(); + } else { + setXMLFile("konsole/sessionui.rc"); + setupCommonActions(); + setupExtraActions(); + } + + actionCollection()->addAssociatedWidget(view); + foreach(QAction * action, actionCollection()->actions()) { + action->setShortcutContext(Qt::WidgetWithChildrenShortcut); + } + + setIdentifier(++_lastControllerId); + sessionTitleChanged(); + + view->installEventFilter(this); + view->setSessionController(this); + + // listen for session resize requests + connect(_session, SIGNAL(resizeRequest(QSize)), this, + SLOT(sessionResizeRequest(QSize))); + + // listen for popup menu requests + connect(_view, SIGNAL(configureRequest(QPoint)), this, + SLOT(showDisplayContextMenu(QPoint))); + + // move view to newest output when keystrokes occur + connect(_view, SIGNAL(keyPressedSignal(QKeyEvent*)), this, + SLOT(trackOutput(QKeyEvent*))); + + // listen to activity / silence notifications from session + connect(_session, SIGNAL(stateChanged(int)), this, + SLOT(sessionStateChanged(int))); + // listen to title and icon changes + connect(_session, SIGNAL(titleChanged()), this, SLOT(sessionTitleChanged())); + + connect(_session , SIGNAL(currentDirectoryChanged(QString)) , + this , SIGNAL(currentDirectoryChanged(QString))); + + // listen for color changes + connect(_session, SIGNAL(changeBackgroundColorRequest(QColor)), _view, SLOT(setBackgroundColor(QColor))); + connect(_session, SIGNAL(changeForegroundColorRequest(QColor)), _view, SLOT(setForegroundColor(QColor))); + + // update the title when the session starts + connect(_session, SIGNAL(started()), this, SLOT(snapshot())); + + // listen for output changes to set activity flag + connect(_session->emulation(), SIGNAL(outputChanged()), this, + SLOT(fireActivity())); + + // listen for detection of ZModem transfer + connect(_session, SIGNAL(zmodemDetected()), this, SLOT(zmodemDownload())); + + // listen for flow control status changes + connect(_session, SIGNAL(flowControlEnabledChanged(bool)), _view, + SLOT(setFlowControlWarningEnabled(bool))); + _view->setFlowControlWarningEnabled(_session->flowControlEnabled()); + + // take a snapshot of the session state every so often when + // user activity occurs + // + // the timer is owned by the session so that it will be destroyed along + // with the session + _interactionTimer = new QTimer(_session); + _interactionTimer->setSingleShot(true); + _interactionTimer->setInterval(500); + connect(_interactionTimer, SIGNAL(timeout()), this, SLOT(snapshot())); + connect(_view, SIGNAL(keyPressedSignal(QKeyEvent*)), this, SLOT(interactionHandler())); + + // take a snapshot of the session state periodically in the background + QTimer* backgroundTimer = new QTimer(_session); + backgroundTimer->setSingleShot(false); + backgroundTimer->setInterval(2000); + connect(backgroundTimer, SIGNAL(timeout()), this, SLOT(snapshot())); + backgroundTimer->start(); + + _allControllers.insert(this); + + // A list of programs that accept Ctrl+C to clear command line used + // before outputting bookmark. + _bookmarkValidProgramsToClear << "bash" << "fish" << "sh"; + _bookmarkValidProgramsToClear << "tcsh" << "zsh"; +} + +SessionController::~SessionController() +{ + if (_view) + _view->setScreenWindow(0); + + _allControllers.remove(this); + + if (!_editProfileDialog.isNull()) { + delete _editProfileDialog.data(); + } +} +void SessionController::trackOutput(QKeyEvent* event) +{ + Q_ASSERT(_view->screenWindow()); + + // jump to the end of the history buffer unless the key pressed + // is one of the three main modifiers, as these are used to select + // the selection mode (eg. Ctrl+Alt+ for column/block selection) + switch (event->key()) { + case Qt::Key_Shift: + case Qt::Key_Control: + case Qt::Key_Alt: + break; + default: + _view->screenWindow()->setTrackOutput(true); + } +} +void SessionController::interactionHandler() +{ + // This flag is used to make sure those special icons indicating interest + // events (activity/silence/bell?) remain in the tab until user interaction + // happens. Otherwise, those special icons will quickly be replaced by + // normal icon when ::snapshot() is triggered + _keepIconUntilInteraction = false; + _interactionTimer->start(); +} + +void SessionController::requireUrlFilterUpdate() +{ + // this method is called every time the screen window's output changes, so do not + // do anything expensive here. + + _urlFilterUpdateRequired = true; +} +void SessionController::snapshot() +{ + Q_ASSERT(_session != 0); + + QString title = _session->getDynamicTitle(); + title = title.simplified(); + + // Visualize that the session is broadcasting to others + if (_copyToGroup && _copyToGroup->sessions().count() > 1) { + title.append('*'); + } + + // use the fallback title if needed + if (title.isEmpty()) { + title = _session->title(Session::NameRole); + } + + // apply new title + _session->setTitle(Session::DisplayedTitleRole, title); + + // do not forget icon + updateSessionIcon(); +} + +QString SessionController::currentDir() const +{ + return _session->currentWorkingDirectory(); +} + +KUrl SessionController::url() const +{ + return _session->getUrl(); +} + +void SessionController::rename() +{ + renameSession(); +} + +void SessionController::openUrl(const KUrl& url) +{ + // Clear shell's command line + if (!_session->isForegroundProcessActive() + && _bookmarkValidProgramsToClear.contains(_session->foregroundProcessName())) { + _session->emulation()->sendText(QChar(0x03)); // Ctrl+C + _session->emulation()->sendText(QChar('\n')); + } + + // handle local paths + if (url.isLocalFile()) { + QString path = url.toLocalFile(); + _session->emulation()->sendText("cd " + KShell::quoteArg(path) + '\r'); + } else if (url.protocol().isEmpty()) { + // KUrl couldn't parse what the user entered into the URL field + // so just dump it to the shell + QString command = url.prettyUrl(); + if (!command.isEmpty()) + _session->emulation()->sendText(command + '\r'); + } else if (url.protocol() == "ssh") { + QString sshCommand = "ssh "; + + if (url.port() > -1) { + sshCommand += QString("-p %1 ").arg(url.port()); + } + if (url.hasUser()) { + sshCommand += (url.user() + '@'); + } + if (url.hasHost()) { + sshCommand += url.host(); + } + + _session->sendText(sshCommand + '\r'); + + } else if (url.protocol() == "telnet") { + QString telnetCommand = "telnet "; + + if (url.hasUser()) { + telnetCommand += QString("-l %1 ").arg(url.user()); + } + if (url.hasHost()) { + telnetCommand += (url.host() + ' '); + } + if (url.port() > -1) { + telnetCommand += QString::number(url.port()); + } + + _session->sendText(telnetCommand + '\r'); + + } else { + //TODO Implement handling for other Url types + + KMessageBox::sorry(_view->window(), + i18n("Konsole does not know how to open the bookmark: ") + + url.prettyUrl()); + + kWarning() << "Unable to open bookmark at url" << url << ", I do not know" + << " how to handle the protocol " << url.protocol(); + } +} + +void SessionController::setupPrimaryScreenSpecificActions(bool use) +{ + KActionCollection* collection = actionCollection(); + QAction* clearAction = collection->action("clear-history"); + QAction* resetAction = collection->action("clear-history-and-reset"); + QAction* selectAllAction = collection->action("select-all"); + QAction* selectLineAction = collection->action("select-line"); + + // these actions are meaningful only when primary screen is used. + clearAction->setEnabled(use); + resetAction->setEnabled(use); + selectAllAction->setEnabled(use); + selectLineAction->setEnabled(use); +} + +void SessionController::selectionChanged(const QString& selectedText) +{ + _selectedText = selectedText; + updateCopyAction(selectedText); + updateOpenWithMenu(selectedText); +} + +void SessionController::updateCopyAction(const QString& selectedText) +{ + QAction* copyAction = actionCollection()->action("edit_copy"); + + // copy action is meaningful only when some text is selected. + copyAction->setEnabled(!selectedText.isEmpty()); +} + +void SessionController::updateWebSearchMenu() +{ + // reset + _webSearchMenu->setVisible(false); + _webSearchMenu->menu()->clear(); + + if (_selectedText.isEmpty()) + return; + + QString searchText = _selectedText; + searchText = searchText.replace('\n', ' ').replace('\r', ' ').simplified(); + + if (searchText.isEmpty()) + return; + + KUriFilterData filterData(searchText); + filterData.setSearchFilteringOptions(KUriFilterData::RetrievePreferredSearchProvidersOnly); + + if (KUriFilter::self()->filterSearchUri(filterData, KUriFilter::NormalTextFilter)) { + const QStringList searchProviders = filterData.preferredSearchProviders(); + if (!searchProviders.isEmpty()) { + _webSearchMenu->setText(i18n("Search for '%1' with", KStringHandler::rsqueeze(searchText, 16))); + + KAction* action = 0; + + foreach(const QString& searchProvider, searchProviders) { + action = new KAction(searchProvider, _webSearchMenu); + action->setIcon(KIcon(filterData.iconNameForPreferredSearchProvider(searchProvider))); + action->setData(filterData.queryForPreferredSearchProvider(searchProvider)); + connect(action, SIGNAL(triggered()), this, SLOT(handleWebShortcutAction())); + _webSearchMenu->addAction(action); + } + + _webSearchMenu->addSeparator(); + + action = new KAction(i18n("Configure Web Shortcuts..."), _webSearchMenu); + action->setIcon(KIcon("configure")); + connect(action, SIGNAL(triggered()), this, SLOT(configureWebShortcuts())); + _webSearchMenu->addAction(action); + + _webSearchMenu->setVisible(true); + _webSearchMenu->setEnabled(true); + } + } +} + +void SessionController::handleWebShortcutAction() +{ + KAction* action = qobject_cast(sender()); + if (!action) + return; + + KUriFilterData filterData(action->data().toString()); + + if (KUriFilter::self()->filterUri(filterData, QStringList() << "kurisearchfilter")) { + const KUrl& url = filterData.uri(); + new KRun(url, QApplication::activeWindow()); + } +} + +void SessionController::updateOpenWithMenu(const QString &selectedText) +{ + // reset + _openWithMenu->setVisible(false); + _openWithMenu->menu()->clear(); + + if (selectedText.isEmpty()) + return; + + QString searchText = selectedText; + searchText = searchText.replace('\n', ' ').replace('\r', ' ').simplified(); + + if (searchText.isEmpty()) + return; + + // TODO: handle relative URL/path selection properly, it will cost a bit of I/O + KMimeType::Ptr mimeType = KMimeType::findByPath(searchText); + KService::List offers = KMimeTypeTrader::self()->query(mimeType->name(), "Application"); + + for(KService::List::Iterator it = offers.begin(); it != offers.end(); ++it) { + KService::Ptr service = *it; + KAction *action = new KAction(KIcon(service->icon()), service->name(), _openWithMenu); + const QStringList actionData = QStringList() << service->exec() << searchText; + action->setData(actionData); + connect(action, SIGNAL(triggered()), this, SLOT(handleOpenWithAction())); + _openWithMenu->addAction(action); + } + + const bool shouldEnable = offers.count() > 0; + _openWithMenu->setVisible(shouldEnable); + _openWithMenu->setEnabled(shouldEnable); +} + +void SessionController::handleOpenWithAction() +{ + KAction* action = qobject_cast(sender()); + if (!action) + return; + + const QStringList actionData = action->data().toStringList(); + Q_ASSERT(actionData.count() == 2); + KUrl::List actionUrls; + actionUrls << actionData.at(1); + KRun::run(actionData.at(0), actionUrls, QApplication::activeWindow()); +} + +void SessionController::configureWebShortcuts() +{ + KToolInvocation::kdeinitExec("kcmshell4", QStringList() << "ebrowsing"); +} + +void SessionController::sendSignal(QAction* action) +{ + const int signal = action->data().value(); + _session->sendSignal(signal); +} + +bool SessionController::eventFilter(QObject* watched , QEvent* event) +{ + if (watched == _view) { + if (event->type() == QEvent::FocusIn) { + // notify the world that the view associated with this session has been focused + // used by the view manager to update the title of the MainWindow widget containing the view + emit focused(this); + + // when the view is focused, set bell events from the associated session to be delivered + // by the focused view + + // first, disconnect any other views which are listening for bell signals from the session + disconnect(_session, SIGNAL(bellRequest(QString)), 0, 0); + // second, connect the newly focused view to listen for the session's bell signal + connect(_session, SIGNAL(bellRequest(QString)), + _view, SLOT(bell(QString))); + + if (_copyInputToAllTabsAction && _copyInputToAllTabsAction->isChecked()) { + // A session with "Copy To All Tabs" has come into focus: + // Ensure that newly created sessions are included in _copyToGroup! + copyInputToAllTabs(); + } + } + // when a mouse move is received, create the URL filter and listen for output changes if + // it has not already been created. If it already exists, then update only if the output + // has changed since the last update ( _urlFilterUpdateRequired == true ) + // + // also check that no mouse buttons are pressed since the URL filter only applies when + // the mouse is hovering over the view + if (event->type() == QEvent::MouseMove && + (!_viewUrlFilter || _urlFilterUpdateRequired) && + ((QMouseEvent*)event)->buttons() == Qt::NoButton) { + if (_view->screenWindow() && !_viewUrlFilter) { + connect(_view->screenWindow(), SIGNAL(scrolled(int)), this, + SLOT(requireUrlFilterUpdate())); + connect(_view->screenWindow(), SIGNAL(outputChanged()), this, + SLOT(requireUrlFilterUpdate())); + + // install filter on the view to highlight URLs + _viewUrlFilter = new UrlFilter(); + _view->filterChain()->addFilter(_viewUrlFilter); + } + + _view->processFilters(); + _urlFilterUpdateRequired = false; + } + } + + return false; +} + +void SessionController::removeSearchFilter() +{ + if (!_searchFilter) + return; + + _view->filterChain()->removeFilter(_searchFilter); + delete _searchFilter; + _searchFilter = 0; +} + +void SessionController::setSearchBar(IncrementalSearchBar* searchBar) +{ + // disconnect the existing search bar + if (_searchBar) { + disconnect(this, 0, _searchBar, 0); + disconnect(_searchBar, 0, this, 0); + } + + // connect new search bar + _searchBar = searchBar; + if (_searchBar) { + connect(_searchBar, SIGNAL(unhandledMovementKeyPressed(QKeyEvent*)), this, SLOT(movementKeyFromSearchBarReceived(QKeyEvent*))); + connect(_searchBar, SIGNAL(closeClicked()), this, SLOT(searchClosed())); + connect(_searchBar, SIGNAL(searchFromClicked()), this, SLOT(searchFrom())); + connect(_searchBar, SIGNAL(findNextClicked()), this, SLOT(findNextInHistory())); + connect(_searchBar, SIGNAL(findPreviousClicked()), this, SLOT(findPreviousInHistory())); + connect(_searchBar, SIGNAL(highlightMatchesToggled(bool)) , this , SLOT(highlightMatches(bool))); + connect(_searchBar, SIGNAL(matchCaseToggled(bool)), this, SLOT(changeSearchMatch())); + + // if the search bar was previously active + // then re-enter search mode + enableSearchBar(_isSearchBarEnabled); + } +} +IncrementalSearchBar* SessionController::searchBar() const +{ + return _searchBar; +} + +void SessionController::setShowMenuAction(QAction* action) +{ + _showMenuAction = action; +} + +void SessionController::setupCommonActions() +{ + KAction* action = 0; + KActionCollection* collection = actionCollection(); + + // Close Session + action = collection->addAction("close-session", this, SLOT(closeSession())); + if (isKonsolePart()) + action->setText(i18n("&Close Session")); + else + action->setText(i18n("&Close Tab")); + + action->setIcon(KIcon("tab-close")); + action->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_W)); + + // Open Browser + action = collection->addAction("open-browser", this, SLOT(openBrowser())); + action->setText(i18n("Open File Manager")); + action->setIcon(KIcon("system-file-manager")); + + // Copy and Paste + action = KStandardAction::copy(this, SLOT(copy()), collection); + action->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_C)); + // disabled at first, since nothing has been selected now + action->setEnabled(false); + + action = KStandardAction::paste(this, SLOT(paste()), collection); + KShortcut pasteShortcut = action->shortcut(); + pasteShortcut.setPrimary(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_V)); + pasteShortcut.setAlternate(QKeySequence(Qt::SHIFT + Qt::Key_Insert)); + action->setShortcut(pasteShortcut); + + action = collection->addAction("paste-selection", this, SLOT(pasteFromX11Selection())); + action->setText(i18n("Paste Selection")); + action->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_Insert)); + + _webSearchMenu = new KActionMenu(i18n("Web Search"), this); + _webSearchMenu->setIcon(KIcon("preferences-web-browser-shortcuts")); + _webSearchMenu->setVisible(false); + collection->addAction("web-search", _webSearchMenu); + + _openWithMenu = new KActionMenu(i18n("Open With"), this); + _openWithMenu->setIcon(KIcon("document-open")); + _openWithMenu->setVisible(false); + collection->addAction("open-with", _openWithMenu); + + action = collection->addAction("select-all", this, SLOT(selectAll())); + action->setText(i18n("&Select All")); + action->setIcon(KIcon("edit-select-all")); + + action = collection->addAction("select-line", this, SLOT(selectLine())); + action->setText(i18n("Select &Line")); + + action = KStandardAction::saveAs(this, SLOT(saveHistory()), collection); + action->setText(i18n("Save Output &As...")); + + action = KStandardAction::print(this, SLOT(print_screen()), collection); + action->setText(i18n("&Print Screen...")); + action->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_P)); + + action = collection->addAction("adjust-history", this, SLOT(showHistoryOptions())); + action->setText(i18n("Adjust Scrollback...")); + action->setIcon(KIcon("configure")); + + action = collection->addAction("clear-history", this, SLOT(clearHistory())); + action->setText(i18n("Clear Scrollback")); + action->setIcon(KIcon("edit-clear-history")); + + action = collection->addAction("clear-history-and-reset", this, SLOT(clearHistoryAndReset())); + action->setText(i18n("Clear Scrollback and Reset")); + action->setIcon(KIcon("edit-clear-history")); + action->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_K)); + + // Profile Options + action = collection->addAction("edit-current-profile", this, SLOT(editCurrentProfile())); + action->setText(i18n("Edit Current Profile...")); + action->setIcon(KIcon("document-properties")); + + _switchProfileMenu = new KActionMenu(i18n("Switch Profile"), this); + collection->addAction("switch-profile", _switchProfileMenu); + connect(_switchProfileMenu->menu(), SIGNAL(aboutToShow()), this, SLOT(prepareSwitchProfileMenu())); + + // History + _findAction = KStandardAction::find(this, SLOT(searchBarEvent()), collection); + _findAction->setShortcut(QKeySequence()); + + _findNextAction = KStandardAction::findNext(this, SLOT(findNextInHistory()), collection); + _findNextAction->setShortcut(QKeySequence()); + _findNextAction->setEnabled(false); + + _findPreviousAction = KStandardAction::findPrev(this, SLOT(findPreviousInHistory()), collection); + _findPreviousAction->setShortcut(QKeySequence()); + _findPreviousAction->setEnabled(false); + + // Character Encoding + _codecAction = new KCodecAction(i18n("Set &Encoding"), this); + _codecAction->setIcon(KIcon("character-set")); + collection->addAction("set-encoding", _codecAction); + connect(_codecAction->menu(), SIGNAL(aboutToShow()), this, SLOT(updateCodecAction())); + connect(_codecAction, SIGNAL(triggered(QTextCodec*)), this, SLOT(changeCodec(QTextCodec*))); +} + +void SessionController::setupExtraActions() +{ + KAction* action = 0; + KToggleAction* toggleAction = 0; + KActionCollection* collection = actionCollection(); + + // Rename Session + action = collection->addAction("rename-session", this, SLOT(renameSession())); + action->setText(i18n("&Rename Tab...")); + action->setIcon(KIcon("edit-rename")); + action->setShortcut(QKeySequence(Qt::CTRL + Qt::ALT + Qt::Key_S)); + + // Copy input to ==> all tabs + KToggleAction* copyInputToAllTabsAction = collection->add("copy-input-to-all-tabs"); + copyInputToAllTabsAction->setText(i18n("&All Tabs in Current Window")); + copyInputToAllTabsAction->setData(CopyInputToAllTabsMode); + // this action is also used in other place, so remember it + _copyInputToAllTabsAction = copyInputToAllTabsAction; + + // Copy input to ==> selected tabs + KToggleAction* copyInputToSelectedTabsAction = collection->add("copy-input-to-selected-tabs"); + copyInputToSelectedTabsAction->setText(i18n("&Select Tabs...")); + copyInputToSelectedTabsAction->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_Period)); + copyInputToSelectedTabsAction->setData(CopyInputToSelectedTabsMode); + + // Copy input to ==> none + KToggleAction* copyInputToNoneAction = collection->add("copy-input-to-none"); + copyInputToNoneAction->setText(i18nc("@action:inmenu Do not select any tabs", "&None")); + copyInputToNoneAction->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_Slash)); + copyInputToNoneAction->setData(CopyInputToNoneMode); + copyInputToNoneAction->setChecked(true); // the default state + + // The "Copy Input To" submenu + // The above three choices are represented as combo boxes + KSelectAction* copyInputActions = collection->add("copy-input-to"); + copyInputActions->setText(i18n("Copy Input To")); + copyInputActions->addAction(copyInputToAllTabsAction); + copyInputActions->addAction(copyInputToSelectedTabsAction); + copyInputActions->addAction(copyInputToNoneAction); + connect(copyInputActions, SIGNAL(triggered(QAction*)), this, SLOT(copyInputActionsTriggered(QAction*))); + + action = collection->addAction("zmodem-upload", this, SLOT(zmodemUpload())); + action->setText(i18n("&ZModem Upload...")); + action->setIcon(KIcon("document-open")); + action->setShortcut(QKeySequence(Qt::CTRL + Qt::ALT + Qt::Key_U)); + + // Monitor + toggleAction = new KToggleAction(i18n("Monitor for &Activity"), this); + toggleAction->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_A)); + action = collection->addAction("monitor-activity", toggleAction); + connect(action, SIGNAL(toggled(bool)), this, SLOT(monitorActivity(bool))); + + toggleAction = new KToggleAction(i18n("Monitor for &Silence"), this); + toggleAction->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_I)); + action = collection->addAction("monitor-silence", toggleAction); + connect(action, SIGNAL(toggled(bool)), this, SLOT(monitorSilence(bool))); + + // Text Size + action = collection->addAction("enlarge-font", this, SLOT(increaseFontSize())); + action->setText(i18n("Enlarge Font")); + action->setIcon(KIcon("format-font-size-more")); + KShortcut enlargeFontShortcut = action->shortcut(); + enlargeFontShortcut.setPrimary(QKeySequence(Qt::CTRL + Qt::Key_Plus)); + enlargeFontShortcut.setAlternate(QKeySequence(Qt::CTRL + Qt::Key_Equal)); + action->setShortcut(enlargeFontShortcut); + + action = collection->addAction("shrink-font", this, SLOT(decreaseFontSize())); + action->setText(i18n("Shrink Font")); + action->setIcon(KIcon("format-font-size-less")); + action->setShortcut(KShortcut(Qt::CTRL | Qt::Key_Minus)); + + // Send signal + KSelectAction* sendSignalActions = collection->add("send-signal"); + sendSignalActions->setText(i18n("Send Signal")); + connect(sendSignalActions, SIGNAL(triggered(QAction*)), this, SLOT(sendSignal(QAction*))); + + action = collection->addAction("sigstop-signal"); + action->setText(i18n("&Suspend Task") + " (STOP)"); + action->setData(SIGSTOP); + sendSignalActions->addAction(action); + + action = collection->addAction("sigcont-signal"); + action->setText(i18n("&Continue Task") + " (CONT)"); + action->setData(SIGCONT); + sendSignalActions->addAction(action); + + action = collection->addAction("sighup-signal"); + action->setText(i18n("&Hangup") + " (HUP)"); + action->setData(SIGHUP); + sendSignalActions->addAction(action); + + action = collection->addAction("sigint-signal"); + action->setText(i18n("&Interrupt Task") + " (INT)"); + action->setData(SIGINT); + sendSignalActions->addAction(action); + + action = collection->addAction("sigterm-signal"); + action->setText(i18n("&Terminate Task") + " (TERM)"); + action->setData(SIGTERM); + sendSignalActions->addAction(action); + + action = collection->addAction("sigkill-signal"); + action->setText(i18n("&Kill Task") + " (KILL)"); + action->setData(SIGKILL); + sendSignalActions->addAction(action); + + action = collection->addAction("sigusr1-signal"); + action->setText(i18n("User Signal &1") + " (USR1)"); + action->setData(SIGUSR1); + sendSignalActions->addAction(action); + + action = collection->addAction("sigusr2-signal"); + action->setText(i18n("User Signal &2") + " (USR2)"); + action->setData(SIGUSR2); + sendSignalActions->addAction(action); + + _findAction->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_F)); + _findNextAction->setShortcut(QKeySequence(Qt::Key_F3)); + _findPreviousAction->setShortcut(QKeySequence(Qt::SHIFT + Qt::Key_F3)); +} + +void SessionController::switchProfile(Profile::Ptr profile) +{ + SessionManager::instance()->setSessionProfile(_session, profile); +} + +void SessionController::prepareSwitchProfileMenu() +{ + if (_switchProfileMenu->menu()->isEmpty()) { + _profileList = new ProfileList(false, this); + connect(_profileList, SIGNAL(profileSelected(Profile::Ptr)), this, SLOT(switchProfile(Profile::Ptr))); + } + + _switchProfileMenu->menu()->clear(); + _switchProfileMenu->menu()->addActions(_profileList->actions()); +} +void SessionController::updateCodecAction() +{ + _codecAction->setCurrentCodec(QString(_session->codec())); +} + +void SessionController::changeCodec(QTextCodec* codec) +{ + _session->setCodec(codec); +} + +EditProfileDialog* SessionController::profileDialogPointer() +{ + return _editProfileDialog.data(); +} + +void SessionController::editCurrentProfile() +{ + // Searching for Edit profile dialog opened with the same profile + foreach (SessionController* session, _allControllers.values()) { + if (session->profileDialogPointer() + && session->profileDialogPointer()->isVisible() + && session->profileDialogPointer()->lookupProfile() == SessionManager::instance()->sessionProfile(_session)) { + session->profileDialogPointer()->close(); + } + } + + // NOTE bug311270: For to prevent the crash, the profile must be reset. + if (!_editProfileDialog.isNull()) { + // exists but not visible + delete _editProfileDialog.data(); + } + + _editProfileDialog = new EditProfileDialog(QApplication::activeWindow()); + _editProfileDialog.data()->setProfile(SessionManager::instance()->sessionProfile(_session)); + _editProfileDialog.data()->show(); +} + +void SessionController::renameSession() +{ + QScopedPointer dialog(new RenameTabDialog(QApplication::activeWindow())); + dialog->setTabTitleText(_session->tabTitleFormat(Session::LocalTabTitle)); + dialog->setRemoteTabTitleText(_session->tabTitleFormat(Session::RemoteTabTitle)); + + if (_session->isRemote()) { + dialog->focusRemoteTabTitleText(); + } else { + dialog->focusTabTitleText(); + } + + QPointer guard(_session); + int result = dialog->exec(); + if (!guard) + return; + + if (result) { + QString tabTitle = dialog->tabTitleText(); + QString remoteTabTitle = dialog->remoteTabTitleText(); + + _session->setTabTitleFormat(Session::LocalTabTitle, tabTitle); + _session->setTabTitleFormat(Session::RemoteTabTitle, remoteTabTitle); + + // trigger an update of the tab text + snapshot(); + } +} + +bool SessionController::confirmClose() const +{ + if (_session->isForegroundProcessActive()) { + QString title = _session->foregroundProcessName(); + + // hard coded for now. In future make it possible for the user to specify which programs + // are ignored when considering whether to display a confirmation + QStringList ignoreList; + ignoreList << QString(qgetenv("SHELL")).section('/', -1); + if (ignoreList.contains(title)) + return true; + + QString question; + if (title.isEmpty()) + question = i18n("A program is currently running in this session." + " Are you sure you want to close it?"); + else + question = i18n("The program '%1' is currently running in this session." + " Are you sure you want to close it?", title); + + int result = KMessageBox::warningYesNo(_view->window(), question, i18n("Confirm Close")); + return (result == KMessageBox::Yes) ? true : false; + } + return true; +} +bool SessionController::confirmForceClose() const +{ + if (_session->isRunning()) { + QString title = _session->program(); + + // hard coded for now. In future make it possible for the user to specify which programs + // are ignored when considering whether to display a confirmation + QStringList ignoreList; + ignoreList << QString(qgetenv("SHELL")).section('/', -1); + if (ignoreList.contains(title)) + return true; + + QString question; + if (title.isEmpty()) + question = i18n("A program in this session would not die." + " Are you sure you want to kill it by force?"); + else + question = i18n("The program '%1' is in this session would not die." + " Are you sure you want to kill it by force?", title); + + int result = KMessageBox::warningYesNo(_view->window(), question, i18n("Confirm Close")); + return (result == KMessageBox::Yes) ? true : false; + } + return true; +} +void SessionController::closeSession() +{ + if (_preventClose) + return; + + if (confirmClose()) { + if (_session->closeInNormalWay()) { + return; + } else if (confirmForceClose()) { + if (_session->closeInForceWay()) + return; + else + kWarning() << "Konsole failed to close a session in any way."; + } + } +} + +// Trying to open a remote Url may produce unexpected results. +// Therefore, if a remote url, open the user's home path. +// TODO consider: 1) disable menu upon remote session +// 2) transform url to get the desired result (ssh -> sftp, etc) +void SessionController::openBrowser() +{ + KUrl currentUrl = url(); + + if (currentUrl.isLocalFile()) + new KRun(currentUrl, QApplication::activeWindow(), 0, true, true); + else + new KRun(KUrl(QDir::homePath()), QApplication::activeWindow(), 0, true, true); +} + +void SessionController::copy() +{ + _view->copyToClipboard(); +} + +void SessionController::paste() +{ + _view->pasteFromClipboard(); +} +void SessionController::pasteFromX11Selection() +{ + _view->pasteFromX11Selection(); +} +void SessionController::selectAll() +{ + ScreenWindow * screenWindow = _view->screenWindow(); + screenWindow->setSelectionByLineRange(0, _session->emulation()->lineCount()); + _view->copyToX11Selection(); +} +void SessionController::selectLine() +{ + _view->selectCurrentLine(); +} +static const KXmlGuiWindow* findWindow(const QObject* object) +{ + // Walk up the QObject hierarchy to find a KXmlGuiWindow. + while (object != 0) { + const KXmlGuiWindow* window = qobject_cast(object); + if (window != 0) { + return(window); + } + object = object->parent(); + } + return(0); +} + +static bool hasTerminalDisplayInSameWindow(const Session* session, const KXmlGuiWindow* window) +{ + // Iterate all TerminalDisplays of this Session ... + foreach(const TerminalDisplay* terminalDisplay, session->views()) { + // ... and check whether a TerminalDisplay has the same + // window as given in the parameter + if (window == findWindow(terminalDisplay)) { + return(true); + } + } + return(false); +} + +void SessionController::copyInputActionsTriggered(QAction* action) +{ + const int mode = action->data().value(); + + switch (mode) { + case CopyInputToAllTabsMode: + copyInputToAllTabs(); + break; + case CopyInputToSelectedTabsMode: + copyInputToSelectedTabs(); + break; + case CopyInputToNoneMode: + copyInputToNone(); + break; + default: + Q_ASSERT(false); + } +} + +void SessionController::copyInputToAllTabs() +{ + if (!_copyToGroup) { + _copyToGroup = new SessionGroup(this); + } + + // Find our window ... + const KXmlGuiWindow* myWindow = findWindow(_view); + + QSet group = + QSet::fromList(SessionManager::instance()->sessions()); + for (QSet::iterator iterator = group.begin(); + iterator != group.end(); ++iterator) { + Session* session = *iterator; + + // First, ensure that the session is removed + // (necessary to avoid duplicates on addSession()!) + _copyToGroup->removeSession(session); + + // Add current session if it is displayed our window + if (hasTerminalDisplayInSameWindow(session, myWindow)) { + _copyToGroup->addSession(session); + } + } + _copyToGroup->setMasterStatus(_session, true); + _copyToGroup->setMasterMode(SessionGroup::CopyInputToAll); + + snapshot(); +} + +void SessionController::copyInputToSelectedTabs() +{ + if (!_copyToGroup) { + _copyToGroup = new SessionGroup(this); + _copyToGroup->addSession(_session); + _copyToGroup->setMasterStatus(_session, true); + _copyToGroup->setMasterMode(SessionGroup::CopyInputToAll); + } + + QPointer dialog = new CopyInputDialog(_view); + dialog->setMasterSession(_session); + + QSet currentGroup = QSet::fromList(_copyToGroup->sessions()); + currentGroup.remove(_session); + + dialog->setChosenSessions(currentGroup); + + QPointer guard(_session); + int result = dialog->exec(); + if (!guard) + return; + + if (result == QDialog::Accepted) { + QSet newGroup = dialog->chosenSessions(); + newGroup.remove(_session); + + QSet completeGroup = newGroup | currentGroup; + foreach(Session * session, completeGroup) { + if (newGroup.contains(session) && !currentGroup.contains(session)) + _copyToGroup->addSession(session); + else if (!newGroup.contains(session) && currentGroup.contains(session)) + _copyToGroup->removeSession(session); + } + + _copyToGroup->setMasterStatus(_session, true); + _copyToGroup->setMasterMode(SessionGroup::CopyInputToAll); + snapshot(); + } +} + +void SessionController::copyInputToNone() +{ + if (!_copyToGroup) // No 'Copy To' is active + return; + + QSet group = + QSet::fromList(SessionManager::instance()->sessions()); + for (QSet::iterator iterator = group.begin(); + iterator != group.end(); ++iterator) { + Session* session = *iterator; + + if (session != _session) { + _copyToGroup->removeSession(*iterator); + } + } + delete _copyToGroup; + _copyToGroup = 0; + snapshot(); +} + +void SessionController::searchClosed() +{ + _isSearchBarEnabled = false; + searchHistory(false); +} + +void SessionController::setSearchStartToWindowCurrentLine() +{ + setSearchStartTo(-1); +} + +void SessionController::setSearchStartTo(int line) +{ + _searchStartLine = line; + _prevSearchResultLine = line; +} + +void SessionController::listenForScreenWindowUpdates() +{ + if (_listenForScreenWindowUpdates) + return; + + connect(_view->screenWindow(), SIGNAL(outputChanged()), this, + SLOT(updateSearchFilter())); + connect(_view->screenWindow(), SIGNAL(scrolled(int)), this, + SLOT(updateSearchFilter())); + connect(_view->screenWindow(), SIGNAL(currentResultLineChanged()), _view, + SLOT(update())); + + _listenForScreenWindowUpdates = true; +} + +void SessionController::updateSearchFilter() +{ + if (_searchFilter && _searchBar) { + _view->processFilters(); + } +} + +void SessionController::searchBarEvent() +{ + QString selectedText = _view->screenWindow()->selectedText(true, true); + if (!selectedText.isEmpty()) + _searchBar->setSearchText(selectedText); + + if (_searchBar->isVisible()) { + _searchBar->focusLineEdit(); + } else { + searchHistory(true); + _isSearchBarEnabled = true; + } +} + +void SessionController::enableSearchBar(bool showSearchBar) +{ + if (!_searchBar) + return; + + if (showSearchBar && !_searchBar->isVisible()) { + setSearchStartToWindowCurrentLine(); + } + + _searchBar->setVisible(showSearchBar); + if (showSearchBar) { + connect(_searchBar, SIGNAL(searchChanged(QString)), this, + SLOT(searchTextChanged(QString))); + connect(_searchBar, SIGNAL(searchReturnPressed(QString)), this, + SLOT(findPreviousInHistory())); + connect(_searchBar, SIGNAL(searchShiftPlusReturnPressed()), this, + SLOT(findNextInHistory())); + } else { + disconnect(_searchBar, SIGNAL(searchChanged(QString)), this, + SLOT(searchTextChanged(QString))); + disconnect(_searchBar, SIGNAL(searchReturnPressed(QString)), this, + SLOT(findPreviousInHistory())); + disconnect(_searchBar, SIGNAL(searchShiftPlusReturnPressed()), this, + SLOT(findNextInHistory())); + if (_view && _view->screenWindow()) { + _view->screenWindow()->setCurrentResultLine(-1); + } + } +} + + +bool SessionController::reverseSearchChecked() const +{ + Q_ASSERT(_searchBar); + + QBitArray options = _searchBar->optionsChecked(); + return options.at(IncrementalSearchBar::ReverseSearch); +} + +QRegExp SessionController::regexpFromSearchBarOptions() +{ + QBitArray options = _searchBar->optionsChecked(); + + Qt::CaseSensitivity caseHandling = options.at(IncrementalSearchBar::MatchCase) ? Qt::CaseSensitive : Qt::CaseInsensitive; + QRegExp::PatternSyntax syntax = options.at(IncrementalSearchBar::RegExp) ? QRegExp::RegExp : QRegExp::FixedString; + + QRegExp regExp(_searchBar->searchText(), caseHandling , syntax); + return regExp; +} + +// searchHistory() may be called either as a result of clicking a menu item or +// as a result of changing the search bar widget +void SessionController::searchHistory(bool showSearchBar) +{ + enableSearchBar(showSearchBar); + + if (_searchBar) { + if (showSearchBar) { + removeSearchFilter(); + + listenForScreenWindowUpdates(); + + _searchFilter = new RegExpFilter(); + _searchFilter->setRegExp(regexpFromSearchBarOptions()); + _view->filterChain()->addFilter(_searchFilter); + _view->processFilters(); + + setFindNextPrevEnabled(true); + } else { + setFindNextPrevEnabled(false); + + removeSearchFilter(); + + _view->setFocus(Qt::ActiveWindowFocusReason); + } + } +} + +void SessionController::setFindNextPrevEnabled(bool enabled) +{ + _findNextAction->setEnabled(enabled); + _findPreviousAction->setEnabled(enabled); +} +void SessionController::searchTextChanged(const QString& text) +{ + Q_ASSERT(_view->screenWindow()); + + if (_searchText == text) + return; + + _searchText = text; + + if (text.isEmpty()) { + _view->screenWindow()->clearSelection(); + _view->screenWindow()->scrollTo(_searchStartLine); + } + + // update search. this is called even when the text is + // empty to clear the view's filters + beginSearch(text , reverseSearchChecked() ? SearchHistoryTask::BackwardsSearch : SearchHistoryTask::ForwardsSearch); +} +void SessionController::searchCompleted(bool success) +{ + _prevSearchResultLine = _view->screenWindow()->currentResultLine(); + + if (_searchBar) + _searchBar->setFoundMatch(success); +} + +void SessionController::beginSearch(const QString& text , int direction) +{ + Q_ASSERT(_searchBar); + Q_ASSERT(_searchFilter); + + QRegExp regExp = regexpFromSearchBarOptions(); + _searchFilter->setRegExp(regExp); + + if (_searchStartLine == -1) { + if (direction == SearchHistoryTask::ForwardsSearch) { + setSearchStartTo(_view->screenWindow()->currentLine()); + } else { + setSearchStartTo(_view->screenWindow()->currentLine() + _view->screenWindow()->windowLines()); + } + } + + if (!regExp.isEmpty()) { + _view->screenWindow()->setCurrentResultLine(-1); + SearchHistoryTask* task = new SearchHistoryTask(this); + + connect(task, SIGNAL(completed(bool)), this, SLOT(searchCompleted(bool))); + + task->setRegExp(regExp); + task->setSearchDirection((SearchHistoryTask::SearchDirection)direction); + task->setAutoDelete(true); + task->setStartLine(_searchStartLine); + task->addScreenWindow(_session , _view->screenWindow()); + task->execute(); + } else if (text.isEmpty()) { + searchCompleted(false); + } + + _view->processFilters(); +} +void SessionController::highlightMatches(bool highlight) +{ + if (highlight) { + _view->filterChain()->addFilter(_searchFilter); + _view->processFilters(); + } else { + _view->filterChain()->removeFilter(_searchFilter); + } + + _view->update(); +} + +void SessionController::searchFrom() +{ + Q_ASSERT(_searchBar); + Q_ASSERT(_searchFilter); + + if (reverseSearchChecked()) { + setSearchStartTo(_view->screenWindow()->lineCount()); + } else { + setSearchStartTo(0); + } + + + beginSearch(_searchBar->searchText(), reverseSearchChecked() ? SearchHistoryTask::BackwardsSearch : SearchHistoryTask::ForwardsSearch); +} +void SessionController::findNextInHistory() +{ + Q_ASSERT(_searchBar); + Q_ASSERT(_searchFilter); + + setSearchStartTo(_prevSearchResultLine); + + beginSearch(_searchBar->searchText(), reverseSearchChecked() ? SearchHistoryTask::BackwardsSearch : SearchHistoryTask::ForwardsSearch); +} +void SessionController::findPreviousInHistory() +{ + Q_ASSERT(_searchBar); + Q_ASSERT(_searchFilter); + + setSearchStartTo(_prevSearchResultLine); + + beginSearch(_searchBar->searchText(), reverseSearchChecked() ? SearchHistoryTask::ForwardsSearch : SearchHistoryTask::BackwardsSearch); +} +void SessionController::changeSearchMatch() +{ + Q_ASSERT(_searchBar); + Q_ASSERT(_searchFilter); + + // reset Selection for new case match + _view->screenWindow()->clearSelection(); + beginSearch(_searchBar->searchText(), reverseSearchChecked() ? SearchHistoryTask::BackwardsSearch : SearchHistoryTask::ForwardsSearch); +} +void SessionController::showHistoryOptions() +{ + QScopedPointer dialog(new HistorySizeDialog(QApplication::activeWindow())); + const HistoryType& currentHistory = _session->historyType(); + + if (currentHistory.isEnabled()) { + if (currentHistory.isUnlimited()) { + dialog->setMode(Enum::UnlimitedHistory); + } else { + dialog->setMode(Enum::FixedSizeHistory); + dialog->setLineCount(currentHistory.maximumLineCount()); + } + } else { + dialog->setMode(Enum::NoHistory); + } + + QPointer guard(_session); + int result = dialog->exec(); + if (!guard) + return; + + if (result) { + scrollBackOptionsChanged(dialog->mode(), dialog->lineCount()); + } +} +void SessionController::sessionResizeRequest(const QSize& size) +{ + //kDebug() << "View resize requested to " << size; + _view->setSize(size.width(), size.height()); +} +void SessionController::scrollBackOptionsChanged(int mode, int lines) +{ + switch (mode) { + case Enum::NoHistory: + _session->setHistoryType(HistoryTypeNone()); + break; + case Enum::FixedSizeHistory: + _session->setHistoryType(CompactHistoryType(lines)); + break; + case Enum::UnlimitedHistory: + _session->setHistoryType(HistoryTypeFile()); + break; + } +} + +void SessionController::print_screen() +{ + QPrinter printer; + + QPointer dialog = new QPrintDialog(&printer, _view); + PrintOptions* options = new PrintOptions(); + + dialog->setOptionTabs(QList() << options); + dialog->setWindowTitle(i18n("Print Shell")); + connect(dialog, SIGNAL(accepted()), options, SLOT(saveSettings())); + if (dialog->exec() != QDialog::Accepted) + return; + + QPainter painter; + painter.begin(&printer); + + KConfigGroup configGroup(KGlobal::config(), "PrintOptions"); + + if (configGroup.readEntry("ScaleOutput", true)) { + double scale = qMin(printer.pageRect().width() / static_cast(_view->width()), + printer.pageRect().height() / static_cast(_view->height())); + painter.scale(scale, scale); + } + + _view->printContent(painter, configGroup.readEntry("PrinterFriendly", true)); +} + +void SessionController::saveHistory() +{ + SessionTask* task = new SaveHistoryTask(this); + task->setAutoDelete(true); + task->addSession(_session); + task->execute(); +} + +void SessionController::clearHistory() +{ + _session->clearHistory(); + _view->updateImage(); // To reset view scrollbar +} + +void SessionController::clearHistoryAndReset() +{ + Profile::Ptr profile = SessionManager::instance()->sessionProfile(_session); + QByteArray name = profile->defaultEncoding().toUtf8(); + + Emulation* emulation = _session->emulation(); + emulation->reset(); + _session->refresh(); + _session->setCodec(QTextCodec::codecForName(name)); + clearHistory(); +} + +void SessionController::increaseFontSize() +{ + _view->increaseFontSize(); +} + +void SessionController::decreaseFontSize() +{ + _view->decreaseFontSize(); +} + +void SessionController::monitorActivity(bool monitor) +{ + _session->setMonitorActivity(monitor); +} +void SessionController::monitorSilence(bool monitor) +{ + _session->setMonitorSilence(monitor); +} +void SessionController::updateSessionIcon() +{ + // Visualize that the session is broadcasting to others + if (_copyToGroup && _copyToGroup->sessions().count() > 1) { + // Master Mode: set different icon, to warn the user to be careful + setIcon(_broadcastIcon); + } else { + if (!_keepIconUntilInteraction) { + // Not in Master Mode: use normal icon + setIcon(_sessionIcon); + } + } +} +void SessionController::sessionTitleChanged() +{ + if (_sessionIconName != _session->iconName()) { + _sessionIconName = _session->iconName(); + _sessionIcon = KIcon(_sessionIconName); + updateSessionIcon(); + } + + QString title = _session->title(Session::DisplayedTitleRole); + + // special handling for the "%w" marker which is replaced with the + // window title set by the shell + title.replace("%w", _session->userTitle()); + // special handling for the "%#" marker which is replaced with the + // number of the shell + title.replace("%#", QString::number(_session->sessionId())); + + if (title.isEmpty()) + title = _session->title(Session::NameRole); + + setTitle(title); + emit rawTitleChanged(); +} + +void SessionController::showDisplayContextMenu(const QPoint& position) +{ + // needed to make sure the popup menu is available, even if a hosting + // application did not merge our GUI. + if (!factory()) { + if (!clientBuilder()) { + setClientBuilder(new KXMLGUIBuilder(_view)); + } + + KXMLGUIFactory* factory = new KXMLGUIFactory(clientBuilder(), this); + factory->addClient(this); + //kDebug() << "Created xmlgui factory" << factory; + } + + QPointer popup = qobject_cast(factory()->container("session-popup-menu", this)); + if (popup) { + // prepend content-specific actions such as "Open Link", "Copy Email Address" etc. + QList contentActions = _view->filterActions(position); + QAction* contentSeparator = new QAction(popup); + contentSeparator->setSeparator(true); + contentActions << contentSeparator; + popup->insertActions(popup->actions().value(0, 0), contentActions); + + // always update this submenu before showing the context menu, + // because the available search services might have changed + // since the context menu is shown last time + updateWebSearchMenu(); + + _preventClose = true; + + if (_showMenuAction) { + if ( _showMenuAction->isChecked() ) { + popup->removeAction( _showMenuAction); + } else { + popup->insertAction(_switchProfileMenu, _showMenuAction); + } + } + + QAction* chosen = popup->exec(_view->mapToGlobal(position)); + + // check for validity of the pointer to the popup menu + if (popup) { + // Remove content-specific actions + // + // If the close action was chosen, the popup menu will be partially + // destroyed at this point, and the rest will be destroyed later by + // 'chosen->trigger()' + foreach(QAction * action, contentActions) { + popup->removeAction(action); + } + + delete contentSeparator; + } + + _preventClose = false; + + if (chosen && chosen->objectName() == "close-session") + chosen->trigger(); + } else { + kWarning() << "Unable to display popup menu for session" + << _session->title(Session::NameRole) + << ", no GUI factory available to build the popup."; + } +} + +void SessionController::movementKeyFromSearchBarReceived(QKeyEvent *event) +{ + QCoreApplication::sendEvent(_view, event); + setSearchStartToWindowCurrentLine(); +} + +void SessionController::sessionStateChanged(int state) +{ + if (state == _previousState) + return; + + if (state == NOTIFYACTIVITY) { + setIcon(_activityIcon); + _keepIconUntilInteraction = true; + } else if (state == NOTIFYSILENCE) { + setIcon(_silenceIcon); + _keepIconUntilInteraction = true; + } else if (state == NOTIFYNORMAL) { + if (_sessionIconName != _session->iconName()) { + _sessionIconName = _session->iconName(); + _sessionIcon = KIcon(_sessionIconName); + } + + updateSessionIcon(); + } + + _previousState = state; +} + +void SessionController::zmodemDownload() +{ + QString zmodem = KStandardDirs::findExe("rz"); + if (zmodem.isEmpty()) { + zmodem = KStandardDirs::findExe("lrz"); + } + if (!zmodem.isEmpty()) { + const QString path = KFileDialog::getExistingDirectory( + QString(), _view, + i18n("Save ZModem Download to...")); + + if (!path.isEmpty()) { + _session->startZModem(zmodem, path, QStringList()); + return; + } + } else { + KMessageBox::error(_view, + i18n("

    A ZModem file transfer attempt has been detected, " + "but no suitable ZModem software was found on this system.

    " + "

    You may wish to install the 'rzsz' or 'lrzsz' package.

    ")); + } + _session->cancelZModem(); + return; +} + +void SessionController::zmodemUpload() +{ + if (_session->isZModemBusy()) { + KMessageBox::sorry(_view, + i18n("

    The current session already has a ZModem file transfer in progress.

    ")); + return; + } + QString zmodem = KStandardDirs::findExe("sz"); + if (zmodem.isEmpty()) { + zmodem = KStandardDirs::findExe("lsz"); + } + if (zmodem.isEmpty()) { + KMessageBox::sorry(_view, + i18n("

    No suitable ZModem software was found on this system.

    " + "

    You may wish to install the 'rzsz' or 'lrzsz' package.

    ")); + return; + } + + QStringList files = KFileDialog::getOpenFileNames(KUrl(), QString(), _view, + i18n("Select Files for ZModem Upload")); + if (!files.isEmpty()) { + _session->startZModem(zmodem, QString(), files); + } +} + +bool SessionController::isKonsolePart() const +{ + // Check to see if we are being called from Konsole or a KPart + if (QString(qApp->metaObject()->className()) == "Konsole::Application") + return false; + else + return true; +} + +SessionTask::SessionTask(QObject* parent) + : QObject(parent) + , _autoDelete(false) +{ +} +void SessionTask::setAutoDelete(bool enable) +{ + _autoDelete = enable; +} +bool SessionTask::autoDelete() const +{ + return _autoDelete; +} +void SessionTask::addSession(Session* session) +{ + _sessions << session; +} +QList SessionTask::sessions() const +{ + return _sessions; +} + +SaveHistoryTask::SaveHistoryTask(QObject* parent) + : SessionTask(parent) +{ +} +SaveHistoryTask::~SaveHistoryTask() +{ +} + +void SaveHistoryTask::execute() +{ + // TODO - think about the UI when saving multiple history sessions, if there are more than two or + // three then providing a URL for each one will be tedious + + // TODO - show a warning ( preferably passive ) if saving the history output fails + // + + KFileDialog* dialog = new KFileDialog(QString(":konsole") /* check this */, + QString(), QApplication::activeWindow()); + dialog->setOperationMode(KFileDialog::Saving); + dialog->setConfirmOverwrite(true); + + QStringList mimeTypes; + mimeTypes << "text/plain"; + mimeTypes << "text/html"; + dialog->setMimeFilter(mimeTypes, "text/plain"); + + // iterate over each session in the task and display a dialog to allow the user to choose where + // to save that session's history. + // then start a KIO job to transfer the data from the history to the chosen URL + foreach(const SessionPtr& session, sessions()) { + dialog->setCaption(i18n("Save Output From %1", session->title(Session::NameRole))); + + int result = dialog->exec(); + + if (result != QDialog::Accepted) + continue; + + KUrl url = dialog->selectedUrl(); + + if (!url.isValid()) { + // UI: Can we make this friendlier? + KMessageBox::sorry(0 , i18n("%1 is an invalid URL, the output could not be saved.", url.url())); + continue; + } + + KIO::TransferJob* job = KIO::put(url, + -1, // no special permissions + // overwrite existing files + // do not resume an existing transfer + // show progress information only for remote + // URLs + KIO::Overwrite | (url.isLocalFile() ? KIO::HideProgressInfo : KIO::DefaultFlags) + // a better solution would be to show progress + // information after a certain period of time + // instead, since the overall speed of transfer + // depends on factors other than just the protocol + // used + ); + + SaveJob jobInfo; + jobInfo.session = session; + jobInfo.lastLineFetched = -1; // when each request for data comes in from the KIO subsystem + // lastLineFetched is used to keep track of how much of the history + // has already been sent, and where the next request should continue + // from. + // this is set to -1 to indicate the job has just been started + + if (dialog->currentMimeFilter() == "text/html") + jobInfo.decoder = new HTMLDecoder(); + else + jobInfo.decoder = new PlainTextDecoder(); + + _jobSession.insert(job, jobInfo); + + connect(job, SIGNAL(dataReq(KIO::Job*,QByteArray&)), + this, SLOT(jobDataRequested(KIO::Job*,QByteArray&))); + connect(job, SIGNAL(result(KJob*)), + this, SLOT(jobResult(KJob*))); + } + + dialog->deleteLater(); +} +void SaveHistoryTask::jobDataRequested(KIO::Job* job , QByteArray& data) +{ + // TODO - Report progress information for the job + + // PERFORMANCE: Do some tests and tweak this value to get faster saving + const int LINES_PER_REQUEST = 500; + + SaveJob& info = _jobSession[job]; + + // transfer LINES_PER_REQUEST lines from the session's history + // to the save location + if (info.session) { + // note: when retrieving lines from the emulation, + // the first line is at index 0. + + int sessionLines = info.session->emulation()->lineCount(); + + if (sessionLines - 1 == info.lastLineFetched) + return; // if there is no more data to transfer then stop the job + + int copyUpToLine = qMin(info.lastLineFetched + LINES_PER_REQUEST , + sessionLines - 1); + + QTextStream stream(&data, QIODevice::ReadWrite); + info.decoder->begin(&stream); + info.session->emulation()->writeToStream(info.decoder , info.lastLineFetched + 1 , copyUpToLine); + info.decoder->end(); + + info.lastLineFetched = copyUpToLine; + } +} +void SaveHistoryTask::jobResult(KJob* job) +{ + if (job->error()) { + KMessageBox::sorry(0 , i18n("A problem occurred when saving the output.\n%1", job->errorString())); + } + + TerminalCharacterDecoder * decoder = _jobSession[job].decoder; + + _jobSession.remove(job); + + delete decoder; + + // notify the world that the task is done + emit completed(true); + + if (autoDelete()) + deleteLater(); +} +void SearchHistoryTask::addScreenWindow(Session* session , ScreenWindow* searchWindow) +{ + _windows.insert(session, searchWindow); +} +void SearchHistoryTask::execute() +{ + QMapIterator< SessionPtr , ScreenWindowPtr > iter(_windows); + + while (iter.hasNext()) { + iter.next(); + executeOnScreenWindow(iter.key() , iter.value()); + } +} + +void SearchHistoryTask::executeOnScreenWindow(SessionPtr session , ScreenWindowPtr window) +{ + Q_ASSERT(session); + Q_ASSERT(window); + + Emulation* emulation = session->emulation(); + + if (!_regExp.isEmpty()) { + int pos = -1; + const bool forwards = (_direction == ForwardsSearch); + const int lastLine = window->lineCount() - 1; + + int startLine; + if (forwards && (_startLine == lastLine)) { + startLine = 0; + } else if (!forwards && (_startLine == 0)) { + startLine = lastLine; + } else { + startLine = _startLine + (forwards ? 1 : -1); + } + + QString string; + + //text stream to read history into string for pattern or regular expression searching + QTextStream searchStream(&string); + + PlainTextDecoder decoder; + decoder.setRecordLinePositions(true); + + //setup first and last lines depending on search direction + int line = startLine; + + //read through and search history in blocks of 10K lines. + //this balances the need to retrieve lots of data from the history each time + //(for efficient searching) + //without using silly amounts of memory if the history is very large. + const int maxDelta = qMin(window->lineCount(), 10000); + int delta = forwards ? maxDelta : -maxDelta; + + int endLine = line; + bool hasWrapped = false; // set to true when we reach the top/bottom + // of the output and continue from the other + // end + + //loop through history in blocks of lines. + do { + // ensure that application does not appear to hang + // if searching through a lengthy output + QApplication::processEvents(); + + // calculate lines to search in this iteration + if (hasWrapped) { + if (endLine == lastLine) + line = 0; + else if (endLine == 0) + line = lastLine; + + endLine += delta; + + if (forwards) + endLine = qMin(startLine , endLine); + else + endLine = qMax(startLine , endLine); + } else { + endLine += delta; + + if (endLine > lastLine) { + hasWrapped = true; + endLine = lastLine; + } else if (endLine < 0) { + hasWrapped = true; + endLine = 0; + } + } + + decoder.begin(&searchStream); + emulation->writeToStream(&decoder, qMin(endLine, line) , qMax(endLine, line)); + decoder.end(); + + // line number search below assumes that the buffer ends with a new-line + string.append('\n'); + + if (forwards) + pos = string.indexOf(_regExp); + else + pos = string.lastIndexOf(_regExp); + + //if a match is found, position the cursor on that line and update the screen + if (pos != -1) { + int newLines = 0; + QList linePositions = decoder.linePositions(); + while (newLines < linePositions.count() && linePositions[newLines] <= pos) + newLines++; + + // ignore the new line at the start of the buffer + newLines--; + + int findPos = qMin(line, endLine) + newLines; + + highlightResult(window, findPos); + + emit completed(true); + + return; + } + + //clear the current block of text and move to the next one + string.clear(); + line = endLine; + } while (startLine != endLine); + + // if no match was found, clear selection to indicate this + window->clearSelection(); + window->notifyOutputChanged(); + } + + emit completed(false); +} +void SearchHistoryTask::highlightResult(ScreenWindowPtr window , int findPos) +{ + //work out how many lines into the current block of text the search result was found + //- looks a little painful, but it only has to be done once per search. + + //kDebug() << "Found result at line " << findPos; + + //update display to show area of history containing selection + if ((findPos < window->currentLine()) || + (findPos >= (window->currentLine() + window->windowLines()))) { + int centeredScrollPos = findPos - window->windowLines() / 2; + if (centeredScrollPos < 0) { + centeredScrollPos = 0; + } + + window->scrollTo(centeredScrollPos); + } + + window->setTrackOutput(false); + window->notifyOutputChanged(); + window->setCurrentResultLine(findPos); +} + +SearchHistoryTask::SearchHistoryTask(QObject* parent) + : SessionTask(parent) + , _direction(BackwardsSearch) + , _startLine(0) +{ +} +void SearchHistoryTask::setSearchDirection(SearchDirection direction) +{ + _direction = direction; +} +void SearchHistoryTask::setStartLine(int line) +{ + _startLine = line; +} +SearchHistoryTask::SearchDirection SearchHistoryTask::searchDirection() const +{ + return _direction; +} +void SearchHistoryTask::setRegExp(const QRegExp& expression) +{ + _regExp = expression; +} +QRegExp SearchHistoryTask::regExp() const +{ + return _regExp; +} + +QString SessionController::userTitle() const +{ + if (_session) { + return _session->userTitle(); + } else { + return QString(); + } +} + +#include "moc_SessionController.cpp" + diff --git a/konsole/src/SessionController.h b/konsole/src/SessionController.h new file mode 100644 index 00000000..44f14563 --- /dev/null +++ b/konsole/src/SessionController.h @@ -0,0 +1,547 @@ +/* + Copyright 2006-2008 by Robert Knight + Copyright 2009 by Thomas Dreibholz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +#ifndef SESSIONCONTROLLER_H +#define SESSIONCONTROLLER_H + +// Qt +#include +#include +#include +#include +#include + +// KDE +#include +#include + +// Konsole +#include "ViewProperties.h" +#include "Profile.h" + +namespace KIO +{ +class Job; +} + +#include +#include +#include +#include + +class KCodecAction; +class KUrl; +class KJob; +class KAction; +class KActionMenu; + +namespace Konsole +{ +class Session; +class SessionGroup; +class ScreenWindow; +class TerminalDisplay; +class IncrementalSearchBar; +class ProfileList; +class UrlFilter; +class RegExpFilter; +class EditProfileDialog; + +// SaveHistoryTask +class TerminalCharacterDecoder; + +typedef QPointer SessionPtr; + +/** + * Provides the menu actions to manipulate a single terminal session and view pair. + * The actions provided by this class are defined in the sessionui.rc XML file. + * + * SessionController monitors the session and provides access to basic information + * about the session such as title(), icon() and currentDir(). SessionController + * provides notifications of activity in the session via the activity() signal. + * + * When the controlled view receives the focus, the focused() signal is emitted + * with a pointer to the controller. This can be used by main application window + * which contains the view to plug the controller's actions into the menu when + * the view is focused. + */ +class KONSOLEPRIVATE_EXPORT SessionController : public ViewProperties , public KXMLGUIClient +{ + Q_OBJECT + +public: + enum CopyInputToEnum { + /** Copy keyboard input to all the other tabs in current window */ + CopyInputToAllTabsMode = 0 , + + /** Copy keyboard input to user selected tabs in current window */ + CopyInputToSelectedTabsMode = 1 , + + /** Do not copy keyboard input to other tabs */ + CopyInputToNoneMode = 2 + }; + + /** + * Constructs a new SessionController which operates on @p session and @p view. + */ + SessionController(Session* session , TerminalDisplay* view, QObject* parent); + ~SessionController(); + + /** Returns the session associated with this controller */ + QPointer session() { + return _session; + } + /** Returns the view associated with this controller */ + QPointer view() { + return _view; + } + + /** + * Returns the "window title" of the associated session. + */ + QString userTitle() const; + + /** + * Returns true if the controller is valid. + * A valid controller is one which has a non-null session() and view(). + * + * Equivalent to "!session().isNull() && !view().isNull()" + */ + bool isValid() const; + + /** Set the start line from which the next search will be done **/ + void setSearchStartTo(int line); + + /** set start line to the first or last line (depending on the reverse search + * setting) in the terminal display **/ + void setSearchStartToWindowCurrentLine(); + + /** + * Sets the widget used for searches through the session's output. + * + * When the user clicks on the "Search Output" menu action the @p searchBar 's + * show() method will be called. The SessionController will then connect to the search + * bar's signals to update the search when the widget's controls are pressed. + */ + void setSearchBar(IncrementalSearchBar* searchBar); + /** + * see setSearchBar() + */ + IncrementalSearchBar* searchBar() const; + + /** + * Sets the action displayed in the session's context menu to hide or + * show the menu bar. + */ + void setShowMenuAction(QAction* action); + + EditProfileDialog* profileDialogPointer(); + + // reimplemented + virtual KUrl url() const; + virtual QString currentDir() const; + virtual void rename(); + virtual bool confirmClose() const; + virtual bool confirmForceClose() const; + + // Reimplemented to watch for events happening to the view + virtual bool eventFilter(QObject* watched , QEvent* event); + + /** Returns the set of all controllers that exist. */ + static QSet allControllers() { + return _allControllers; + } + +signals: + /** + * Emitted when the view associated with the controller is focused. + * This can be used by other classes to plug the controller's actions into a window's + * menus. + */ + void focused(SessionController* controller); + + void rawTitleChanged(); + + /** + * Emitted when the current working directory of the session associated with + * the controller is changed. + */ + void currentDirectoryChanged(const QString& dir); + +public slots: + /** + * Issues a command to the session to navigate to the specified URL. + * This may not succeed if the foreground program does not understand + * the command sent to it ( 'cd path' for local URLs ) or is not + * responding to input. + * + * openUrl() currently supports urls for local paths and those + * using the 'ssh' protocol ( eg. "ssh://joebloggs@hostname" ) + */ + void openUrl(const KUrl& url); + + /** + * update actions which are meaningful only when primary screen is in use. + */ + void setupPrimaryScreenSpecificActions(bool use); + + /** + * update actions which are closely related with the selected text. + */ + void selectionChanged(const QString& selectedText); + + /** + * close the associated session. This might involve user interaction for + * confirmation. + */ + void closeSession(); + + /** Increase font size */ + void increaseFontSize(); + + /** Decrease font size */ + void decreaseFontSize(); + +private slots: + // menu item handlers + void openBrowser(); + void copy(); + void paste(); + void selectAll(); + void selectLine(); + void pasteFromX11Selection(); // shortcut only + void copyInputActionsTriggered(QAction* action); + void copyInputToAllTabs(); + void copyInputToSelectedTabs(); + void copyInputToNone(); + void editCurrentProfile(); + void changeCodec(QTextCodec* codec); + void enableSearchBar(bool showSearchBar); + void searchHistory(bool showSearchBar); + void searchBarEvent(); + void searchFrom(); + void findNextInHistory(); + void findPreviousInHistory(); + void changeSearchMatch(); + void print_screen(); + void saveHistory(); + void showHistoryOptions(); + void clearHistory(); + void clearHistoryAndReset(); + void monitorActivity(bool monitor); + void monitorSilence(bool monitor); + void renameSession(); + void switchProfile(Profile::Ptr profile); + void handleWebShortcutAction(); + void handleOpenWithAction(); + void configureWebShortcuts(); + void sendSignal(QAction* action); + + // other + void prepareSwitchProfileMenu(); + void updateCodecAction(); + void showDisplayContextMenu(const QPoint& position); + void movementKeyFromSearchBarReceived(QKeyEvent *event); + void sessionStateChanged(int state); + void sessionTitleChanged(); + void searchTextChanged(const QString& text); + void searchCompleted(bool success); + void searchClosed(); // called when the user clicks on the + // history search bar's close button + + void interactionHandler(); + void snapshot(); // called periodically as the user types + // to take a snapshot of the state of the + // foreground process in the terminal + + void requireUrlFilterUpdate(); + void highlightMatches(bool highlight); + void scrollBackOptionsChanged(int mode , int lines); + void sessionResizeRequest(const QSize& size); + void trackOutput(QKeyEvent* event); // move view to end of current output + // when a key press occurs in the + // display area + + void updateSearchFilter(); + + void zmodemDownload(); + void zmodemUpload(); + + /* Returns true if called within a KPart; false if called within Konsole. */ + bool isKonsolePart() const; + + // update actions related with selected text + void updateCopyAction(const QString& selectedText); + void updateWebSearchMenu(); + void updateOpenWithMenu(const QString& selectedText); + +private: + // begins the search + // text - pattern to search for + // direction - value from SearchHistoryTask::SearchDirection enum to specify + // the search direction + void beginSearch(const QString& text , int direction); + QRegExp regexpFromSearchBarOptions(); + bool reverseSearchChecked() const; + void setupCommonActions(); + void setupExtraActions(); + void removeSearchFilter(); // remove and delete the current search filter if set + void setFindNextPrevEnabled(bool enabled); + void listenForScreenWindowUpdates(); + +private: + void updateSessionIcon(); + + QPointer _session; + QPointer _view; + SessionGroup* _copyToGroup; + + ProfileList* _profileList; + + KIcon _sessionIcon; + QString _sessionIconName; + int _previousState; + + UrlFilter* _viewUrlFilter; + RegExpFilter* _searchFilter; + + KAction* _copyInputToAllTabsAction; + + KAction* _findAction; + KAction* _findNextAction; + KAction* _findPreviousAction; + + QTimer* _interactionTimer; + + bool _urlFilterUpdateRequired; + + int _searchStartLine; + int _prevSearchResultLine; + QPointer _searchBar; + + KCodecAction* _codecAction; + + KActionMenu* _switchProfileMenu; + KActionMenu* _webSearchMenu; + KActionMenu* _openWithMenu; + + bool _listenForScreenWindowUpdates; + bool _preventClose; + + bool _keepIconUntilInteraction; + + QString _selectedText; + + QAction* _showMenuAction; + + static QSet _allControllers; + static int _lastControllerId; + static const KIcon _activityIcon; + static const KIcon _silenceIcon; + static const KIcon _broadcastIcon; + + QStringList _bookmarkValidProgramsToClear; + + bool _isSearchBarEnabled; + QWeakPointer _editProfileDialog; + + QString _searchText; +}; +inline bool SessionController::isValid() const +{ + return !_session.isNull() && !_view.isNull(); +} + +/** + * Abstract class representing a task which can be performed on a group of sessions. + * + * Create a new instance of the appropriate sub-class for the task you want to perform and + * call the addSession() method to add each session which needs to be processed. + * + * Finally, call the execute() method to perform the sub-class specific action on each + * of the sessions. + */ +class SessionTask : public QObject +{ + Q_OBJECT + +public: + explicit SessionTask(QObject* parent = 0); + + /** + * Sets whether the task automatically deletes itself when the task has been finished. + * Depending on whether the task operates synchronously or asynchronously, the deletion + * may be scheduled immediately after execute() returns or it may happen some time later. + */ + void setAutoDelete(bool enable); + /** Returns true if the task automatically deletes itself. See setAutoDelete() */ + bool autoDelete() const; + + /** Adds a new session to the group */ + void addSession(Session* session); + + /** + * Executes the task on each of the sessions in the group. + * The completed() signal is emitted when the task is finished, depending on the specific sub-class + * execute() may be synchronous or asynchronous + */ + virtual void execute() = 0; + +signals: + /** + * Emitted when the task has completed. + * Depending on the task this may occur just before execute() returns, or it + * may occur later + * + * @param success Indicates whether the task completed successfully or not + */ + void completed(bool success); + +protected: + /** Returns a list of sessions in the group */ + QList< SessionPtr > sessions() const; + +private: + bool _autoDelete; + QList< SessionPtr > _sessions; +}; + +/** + * A task which prompts for a URL for each session and saves that session's output + * to the given URL + */ +class SaveHistoryTask : public SessionTask +{ + Q_OBJECT + +public: + /** Constructs a new task to save session output to URLs */ + explicit SaveHistoryTask(QObject* parent = 0); + virtual ~SaveHistoryTask(); + + /** + * Opens a save file dialog for each session in the group and begins saving + * each session's history to the given URL. + * + * The data transfer is performed asynchronously and will continue after execute() returns. + */ + virtual void execute(); + +private slots: + void jobDataRequested(KIO::Job* job , QByteArray& data); + void jobResult(KJob* job); + +private: + class SaveJob // structure to keep information needed to process + // incoming data requests from jobs + { + public: + SessionPtr session; // the session associated with a history save job + int lastLineFetched; // the last line processed in the previous data request + // set this to -1 at the start of the save job + + TerminalCharacterDecoder* decoder; // decoder used to convert terminal characters + // into output + }; + + QHash _jobSession; +}; + +//class SearchHistoryThread; +/** + * A task which searches through the output of sessions for matches for a given regular expression. + * SearchHistoryTask operates on ScreenWindow instances rather than sessions added by addSession(). + * A screen window can be added to the list to search using addScreenWindow() + * + * When execute() is called, the search begins in the direction specified by searchDirection(), + * starting at the position of the current selection. + * + * FIXME - This is not a proper implementation of SessionTask, in that it ignores sessions specified + * with addSession() + * + * TODO - Implementation requirements: + * May provide progress feedback to the user when searching very large output logs. + */ +class SearchHistoryTask : public SessionTask +{ + Q_OBJECT + +public: + /** + * This enum describes the strategies available for searching through the + * session's output. + */ + enum SearchDirection { + /** Searches forwards through the output, starting at the current selection. */ + ForwardsSearch, + /** Searches backwards through the output, starting at the current selection. */ + BackwardsSearch + }; + + /** + * Constructs a new search task. + */ + explicit SearchHistoryTask(QObject* parent = 0); + + /** Adds a screen window to the list to search when execute() is called. */ + void addScreenWindow(Session* session , ScreenWindow* searchWindow); + + /** Sets the regular expression which is searched for when execute() is called */ + void setRegExp(const QRegExp& regExp); + /** Returns the regular expression which is searched for when execute() is called */ + QRegExp regExp() const; + + /** Specifies the direction to search in when execute() is called. */ + void setSearchDirection(SearchDirection direction); + /** Returns the current search direction. See setSearchDirection(). */ + SearchDirection searchDirection() const; + + /** The line from which the search will be done **/ + void setStartLine(int startLine); + + /** + * Performs a search through the session's history, starting at the position + * of the current selection, in the direction specified by setSearchDirection(). + * + * If it finds a match, the ScreenWindow specified in the constructor is + * scrolled to the position where the match occurred and the selection + * is set to the matching text. execute() then returns immediately. + * + * To continue the search looking for further matches, call execute() again. + */ + virtual void execute(); + +private: + typedef QPointer ScreenWindowPtr; + + void executeOnScreenWindow(SessionPtr session , ScreenWindowPtr window); + void highlightResult(ScreenWindowPtr window , int position); + + QMap< SessionPtr , ScreenWindowPtr > _windows; + QRegExp _regExp; + SearchDirection _direction; + int _startLine; + + //static QPointer _thread; +}; +} + +#endif //SESSIONCONTROLLER_H + diff --git a/konsole/src/SessionListModel.cpp b/konsole/src/SessionListModel.cpp new file mode 100644 index 00000000..d7e23f76 --- /dev/null +++ b/konsole/src/SessionListModel.cpp @@ -0,0 +1,144 @@ +/* + This source file is part of Konsole, a terminal emulator. + + Copyright 2006-2008 by Robert Knight + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +// Own +#include "SessionListModel.h" + +// KDE +#include +#include + +// Konsole +#include "Session.h" + +using Konsole::Session; +using Konsole::SessionListModel; + +SessionListModel::SessionListModel(QObject* parent) + : QAbstractListModel(parent) +{ +} + +void SessionListModel::setSessions(const QList& sessions) +{ + _sessions = sessions; + + foreach(Session * session, sessions) { + connect(session, SIGNAL(finished()), this, SLOT(sessionFinished())); + } + + reset(); +} + +QVariant SessionListModel::data(const QModelIndex& index, int role) const +{ + Q_ASSERT(index.isValid()); + + int row = index.row(); + int column = index.column(); + + Q_ASSERT(row >= 0 && row < _sessions.count()); + Q_ASSERT(column >= 0 && column < 2); + + switch (role) { + case Qt::DisplayRole: + if (column == 1) { + // This code is duplicated from SessionController.cpp + QString title = _sessions[row]->title(Session::DisplayedTitleRole); + + // special handling for the "%w" marker which is replaced with the + // window title set by the shell + title.replace("%w", _sessions[row]->userTitle()); + // special handling for the "%#" marker which is replaced with the + // number of the shell + title.replace("%#", QString::number(_sessions[row]->sessionId())); + return title; + } else if (column == 0) { + return _sessions[row]->sessionId(); + } + break; + case Qt::DecorationRole: + if (column == 1) + return KIcon(_sessions[row]->iconName()); + else + return QVariant(); + } + + return QVariant(); +} + +QVariant SessionListModel::headerData(int section, Qt::Orientation orientation, + int role) const +{ + if (role != Qt::DisplayRole) + return QVariant(); + + if (orientation == Qt::Vertical) { + return QVariant(); + } else { + switch (section) { + case 0: + return i18nc("@item:intable The session index", "Number"); + case 1: + return i18nc("@item:intable The session title", "Title"); + default: + return QVariant(); + } + } +} + +int SessionListModel::columnCount(const QModelIndex&) const +{ + return 2; +} + +int SessionListModel::rowCount(const QModelIndex&) const +{ + return _sessions.count(); +} + +QModelIndex SessionListModel::parent(const QModelIndex&) const +{ + return QModelIndex(); +} + +void SessionListModel::sessionFinished() +{ + Session* session = qobject_cast(sender()); + int row = _sessions.indexOf(session); + + if (row != -1) { + beginRemoveRows(QModelIndex(), row, row); + sessionRemoved(session); + _sessions.removeAt(row); + endRemoveRows(); + } +} + +QModelIndex SessionListModel::index(int row, int column, const QModelIndex& parent) const +{ + if (hasIndex(row, column, parent)) + return createIndex(row, column, _sessions[row]); + else + return QModelIndex(); +} + +#include "moc_SessionListModel.cpp" diff --git a/konsole/src/SessionListModel.h b/konsole/src/SessionListModel.h new file mode 100644 index 00000000..016b8bd6 --- /dev/null +++ b/konsole/src/SessionListModel.h @@ -0,0 +1,76 @@ +/* + This source file is part of Konsole, a terminal emulator. + + Copyright 2006-2008 by Robert Knight + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +#ifndef SESSIONLISTMODEL_H +#define SESSIONLISTMODEL_H + +// Qt +#include +#include + +namespace Konsole +{ +class Session; + +/** + * Item-view model which contains a flat list of sessions. + * After constructing the model, call setSessions() to set the sessions displayed + * in the list. When a session ends (after emitting the finished() signal) it is + * automatically removed from the list. + * + * The internal pointer for each item in the model (index.internalPointer()) is the + * associated Session* + */ +class SessionListModel : public QAbstractListModel +{ + Q_OBJECT + +public: + explicit SessionListModel(QObject* parent = 0); + + /** + * Sets the list of sessions displayed in the model. + * To display all sessions that are currently running in the list, + * call setSessions(SessionManager::instance()->sessions()) + */ + void setSessions(const QList& sessions); + + // reimplemented from QAbstractItemModel + virtual QModelIndex index(int row, int column, const QModelIndex& parent) const; + virtual QVariant data(const QModelIndex& index, int role) const; + virtual QVariant headerData(int section, Qt::Orientation orientation, + int role) const; + virtual int columnCount(const QModelIndex& parent) const; + virtual int rowCount(const QModelIndex& parent) const; + virtual QModelIndex parent(const QModelIndex& index) const; + +protected: + virtual void sessionRemoved(Session*) {} + +private slots: + void sessionFinished(); + +private: + QList _sessions; +}; +} + +#endif //SESSIONLISTMODEL_H diff --git a/konsole/src/SessionManager.cpp b/konsole/src/SessionManager.cpp new file mode 100644 index 00000000..437890e6 --- /dev/null +++ b/konsole/src/SessionManager.cpp @@ -0,0 +1,335 @@ +/* + This source file is part of Konsole, a terminal emulator. + + Copyright 2006-2008 by Robert Knight + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +// Own +#include "SessionManager.h" + +// Qt +#include +#include +#include + +// KDE +#include +#include +#include +#include + +// Konsole +#include "Session.h" +#include "ProfileManager.h" +#include "History.h" +#include "Enumeration.h" + +using namespace Konsole; + +SessionManager::SessionManager() +{ + //map finished() signals from sessions + _sessionMapper = new QSignalMapper(this); + connect(_sessionMapper , SIGNAL(mapped(QObject*)) , this , + SLOT(sessionTerminated(QObject*))); + + ProfileManager* profileMananger = ProfileManager::instance(); + connect(profileMananger , SIGNAL(profileChanged(Profile::Ptr)) , + this , SLOT(profileChanged(Profile::Ptr))); +} + +SessionManager::~SessionManager() +{ + if (_sessions.count() > 0) { + kWarning() << "Konsole SessionManager destroyed with sessions still alive"; + // ensure that the Session doesn't later try to call back and do things to the + // SessionManager + foreach(Session* session, _sessions) { + disconnect(session , 0 , this , 0); + } + } +} + +K_GLOBAL_STATIC(SessionManager , theSessionManager) +SessionManager* SessionManager::instance() +{ + return theSessionManager; +} + +void SessionManager::closeAllSessions() +{ + // close remaining sessions + foreach(Session* session , _sessions) { + session->close(); + } + _sessions.clear(); +} + +const QList SessionManager::sessions() const +{ + return _sessions; +} + +Session* SessionManager::createSession(Profile::Ptr profile) +{ + if (!profile) + profile = ProfileManager::instance()->defaultProfile(); + + // TODO: check whether this is really needed + if (!ProfileManager::instance()->loadedProfiles().contains(profile)) + ProfileManager::instance()->addProfile(profile); + + //configuration information found, create a new session based on this + Session* session = new Session(); + Q_ASSERT(session); + applyProfile(session, profile, false); + + connect(session , SIGNAL(profileChangeCommandReceived(QString)) , this , + SLOT(sessionProfileCommandReceived(QString))); + + //ask for notification when session dies + _sessionMapper->setMapping(session, session); + connect(session , SIGNAL(finished()) , _sessionMapper , + SLOT(map())); + + //add session to active list + _sessions << session; + _sessionProfiles.insert(session, profile); + + return session; +} +void SessionManager::profileChanged(Profile::Ptr profile) +{ + applyProfile(profile, true); +} + +void SessionManager::sessionTerminated(QObject* sessionObject) +{ + Session* session = qobject_cast(sessionObject); + + Q_ASSERT(session); + + _sessions.removeAll(session); + _sessionProfiles.remove(session); + _sessionRuntimeProfiles.remove(session); + + session->deleteLater(); +} + +void SessionManager::applyProfile(Profile::Ptr profile , bool modifiedPropertiesOnly) +{ + foreach(Session* session, _sessions) { + if (_sessionProfiles[session] == profile) + applyProfile(session, profile, modifiedPropertiesOnly); + } +} +Profile::Ptr SessionManager::sessionProfile(Session* session) const +{ + return _sessionProfiles[session]; +} +void SessionManager::setSessionProfile(Session* session, Profile::Ptr profile) +{ + if (!profile) + profile = ProfileManager::instance()->defaultProfile(); + + Q_ASSERT(profile); + + _sessionProfiles[session] = profile; + + applyProfile(session, profile, false); + + emit sessionUpdated(session); +} +void SessionManager::applyProfile(Session* session, const Profile::Ptr profile , bool modifiedPropertiesOnly) +{ + Q_ASSERT(profile); + + _sessionProfiles[session] = profile; + + ShouldApplyProperty apply(profile, modifiedPropertiesOnly); + + // Basic session settings + if (apply.shouldApply(Profile::Name)) + session->setTitle(Session::NameRole, profile->name()); + + if (apply.shouldApply(Profile::Command)) + session->setProgram(profile->command()); + + if (apply.shouldApply(Profile::Arguments)) + session->setArguments(profile->arguments()); + + if (apply.shouldApply(Profile::Directory)) + session->setInitialWorkingDirectory(profile->defaultWorkingDirectory()); + + if (apply.shouldApply(Profile::Environment)) { + // add environment variable containing home directory of current profile + // (if specified) + QStringList environment = profile->environment(); + environment << QString("PROFILEHOME=%1").arg(profile->defaultWorkingDirectory()); + environment << QString("KONSOLE_PROFILE_NAME=%1").arg(profile->name()); + + session->setEnvironment(environment); + } + + if ( apply.shouldApply(Profile::TerminalColumns) || + apply.shouldApply(Profile::TerminalRows) ) { + const int columns = profile->property(Profile::TerminalColumns); + const int rows = profile->property(Profile::TerminalRows); + session->setPreferredSize(QSize(columns, rows)); + } + + if (apply.shouldApply(Profile::Icon)) + session->setIconName(profile->icon()); + + // Key bindings + if (apply.shouldApply(Profile::KeyBindings)) + session->setKeyBindings(profile->keyBindings()); + + // Tab formats + if (apply.shouldApply(Profile::LocalTabTitleFormat)) + session->setTabTitleFormat(Session::LocalTabTitle , + profile->localTabTitleFormat()); + if (apply.shouldApply(Profile::RemoteTabTitleFormat)) + session->setTabTitleFormat(Session::RemoteTabTitle , + profile->remoteTabTitleFormat()); + + // History + if (apply.shouldApply(Profile::HistoryMode) || apply.shouldApply(Profile::HistorySize)) { + const int mode = profile->property(Profile::HistoryMode); + switch (mode) { + case Enum::NoHistory: + session->setHistoryType(HistoryTypeNone()); + break; + + case Enum::FixedSizeHistory: { + int lines = profile->historySize(); + session->setHistoryType(CompactHistoryType(lines)); + } + break; + + case Enum::UnlimitedHistory: + session->setHistoryType(HistoryTypeFile()); + break; + } + } + + // Terminal features + if (apply.shouldApply(Profile::FlowControlEnabled)) + session->setFlowControlEnabled(profile->flowControlEnabled()); + + // Encoding + if (apply.shouldApply(Profile::DefaultEncoding)) { + QByteArray name = profile->defaultEncoding().toUtf8(); + session->setCodec(QTextCodec::codecForName(name)); + } + + // Monitor Silence + if (apply.shouldApply(Profile::SilenceSeconds)) + session->setMonitorSilenceSeconds(profile->silenceSeconds()); +} + +void SessionManager::sessionProfileCommandReceived(const QString& text) +{ + Session* session = qobject_cast(sender()); + Q_ASSERT(session); + + ProfileCommandParser parser; + QHash changes = parser.parse(text); + + Profile::Ptr newProfile; + if (!_sessionRuntimeProfiles.contains(session)) { + newProfile = new Profile(_sessionProfiles[session]); + _sessionRuntimeProfiles.insert(session, newProfile); + } else { + newProfile = _sessionRuntimeProfiles[session]; + } + + QHashIterator iter(changes); + while (iter.hasNext()) { + iter.next(); + newProfile->setProperty(iter.key(), iter.value()); + } + + _sessionProfiles[session] = newProfile; + applyProfile(newProfile, true); + emit sessionUpdated(session); +} + +void SessionManager::saveSessions(KConfig* config) +{ + // The session IDs can't be restored. + // So we need to map the old ID to the future new ID. + int n = 1; + _restoreMapping.clear(); + + foreach(Session * session, _sessions) { + QString name = QLatin1String("Session") + QString::number(n); + KConfigGroup group(config, name); + + group.writePathEntry("Profile", + _sessionProfiles.value(session)->path()); + session->saveSession(group); + _restoreMapping.insert(session, n); + n++; + } + + KConfigGroup group(config, "Number"); + group.writeEntry("NumberOfSessions", _sessions.count()); +} + +int SessionManager::getRestoreId(Session* session) +{ + return _restoreMapping.value(session); +} + +void SessionManager::restoreSessions(KConfig* config) +{ + KConfigGroup group(config, "Number"); + int sessions; + + // Any sessions saved? + if ((sessions = group.readEntry("NumberOfSessions", 0)) > 0) { + for (int n = 1; n <= sessions; n++) { + QString name = QLatin1String("Session") + QString::number(n); + KConfigGroup sessionGroup(config, name); + + QString profile = sessionGroup.readPathEntry("Profile", QString()); + Profile::Ptr ptr = ProfileManager::instance()->defaultProfile(); + if (!profile.isEmpty()) ptr = ProfileManager::instance()->loadProfile(profile); + + Session* session = createSession(ptr); + session->restoreSession(sessionGroup); + } + } +} + +Session* SessionManager::idToSession(int id) +{ + Q_ASSERT(id); + foreach(Session * session, _sessions) { + if (session->sessionId() == id) + return session; + } + // this should not happen + Q_ASSERT(0); + return 0; +} + + +#include "moc_SessionManager.cpp" + diff --git a/konsole/src/SessionManager.h b/konsole/src/SessionManager.h new file mode 100644 index 00000000..492430bc --- /dev/null +++ b/konsole/src/SessionManager.h @@ -0,0 +1,155 @@ +/* + This source file is part of Konsole, a terminal emulator. + + Copyright 2006-2008 by Robert Knight + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +#ifndef SESSIONMANAGER_H +#define SESSIONMANAGER_H + +// Qt +#include +#include + +// Konsole +#include "Profile.h" + +#include + +class KConfig; + +namespace Konsole +{ +class Session; + +/** + * Manages running terminal sessions. + */ +class KONSOLEPRIVATE_EXPORT SessionManager : public QObject +{ + Q_OBJECT + +public: + /** + * Constructs a new session manager and loads information about the available + * profiles. + */ + SessionManager(); + + /** + * Destroys the SessionManager. All running sessions should be closed + * (via closeAllSessions()) before the SessionManager is destroyed. + */ + virtual ~SessionManager(); + + /** + * Returns the session manager instance. + */ + static SessionManager* instance(); + + /** Kill all running sessions. */ + void closeAllSessions(); + + /** + * Creates a new session using the settings specified by the specified + * profile. + * + * The new session has no views associated with it. A new TerminalDisplay view + * must be created in order to display the output from the terminal session and + * send keyboard or mouse input to it. + * + * @param profile A profile containing the settings for the new session. If @p profile + * is null the default profile (see ProfileManager::defaultProfile()) will be used. + */ + Session* createSession(Profile::Ptr profile = Profile::Ptr()); + + /** Sets the profile associated with a session. */ + void setSessionProfile(Session* session, Profile::Ptr profile); + + /** Returns the profile associated with a session. */ + Profile::Ptr sessionProfile(Session* session) const; + + /** + * Returns a list of active sessions. + */ + const QList sessions() const; + + // System session management + void saveSessions(KConfig* config); + void restoreSessions(KConfig* config); + int getRestoreId(Session* session); + Session* idToSession(int id); + +signals: + /** + * Emitted when a session's settings are updated to match + * its current profile. + */ + void sessionUpdated(Session* session); + +protected slots: + /** + * Called to inform the manager that a session has finished executing. + * + * @param session The Session which has finished executing. + */ + void sessionTerminated(QObject* session); + +private slots: + void sessionProfileCommandReceived(const QString& text); + + void profileChanged(Profile::Ptr profile); + +private: + // applies updates to a profile + // to all sessions currently using that profile + // if modifiedPropertiesOnly is true, only properties which + // are set in the profile @p key are updated + void applyProfile(Profile::Ptr profile , bool modifiedPropertiesOnly); + + // applies updates to the profile @p profile to the session @p session + // if modifiedPropertiesOnly is true, only properties which + // are set in @p profile are update ( ie. properties for which profile->isPropertySet() + // returns true ) + void applyProfile(Session* session , const Profile::Ptr profile , bool modifiedPropertiesOnly); + + QList _sessions; // list of running sessions + + QHash _sessionProfiles; + QHash _sessionRuntimeProfiles; + QHash _restoreMapping; + + QSignalMapper* _sessionMapper; +}; + +/** Utility class to simplify code in SessionManager::applyProfile(). */ +class ShouldApplyProperty +{ +public: + ShouldApplyProperty(const Profile::Ptr profile , bool modifiedOnly) : + _profile(profile) , _modifiedPropertiesOnly(modifiedOnly) {} + + bool shouldApply(Profile::Property property) const { + return !_modifiedPropertiesOnly || _profile->isPropertySet(property); + } +private: + const Profile::Ptr _profile; + bool _modifiedPropertiesOnly; +}; +} +#endif //SESSIONMANAGER_H diff --git a/konsole/src/ShellCommand.cpp b/konsole/src/ShellCommand.cpp new file mode 100644 index 00000000..8df9d9c9 --- /dev/null +++ b/konsole/src/ShellCommand.cpp @@ -0,0 +1,146 @@ +/* + Copyright 2007-2008 by Robert Knight + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +// Own +#include "ShellCommand.h" + +// KDE +#include + +using Konsole::ShellCommand; + +ShellCommand::ShellCommand(const QString& aCommand) +{ + _arguments = KShell::splitArgs(aCommand); +} +ShellCommand::ShellCommand(const QString& aCommand, const QStringList& aArguments) +{ + _arguments = aArguments; + + if (!_arguments.isEmpty()) + _arguments[0] = aCommand; +} +QString ShellCommand::fullCommand() const +{ + QStringList quotedArgs(_arguments); + for (int i = 0; i < quotedArgs.count(); i++) { + QString arg = quotedArgs.at(i); + bool hasSpace = false; + for (int j = 0; j < arg.count(); j++) + if (arg[j].isSpace()) + hasSpace = true; + if (hasSpace) + quotedArgs[i] = '\"' + arg + '\"'; + } + return quotedArgs.join(QChar(' ')); +} +QString ShellCommand::command() const +{ + if (!_arguments.isEmpty()) + return _arguments[0]; + else + return QString(); +} +QStringList ShellCommand::arguments() const +{ + return _arguments; +} +QStringList ShellCommand::expand(const QStringList& items) +{ + QStringList result; + + foreach(const QString & item , items) { + result << expand(item); + } + + return result; +} +QString ShellCommand::expand(const QString& text) +{ + QString result = text; + expandEnv(result); + return result; +} + +bool ShellCommand::isValidEnvCharacter(const QChar& ch) +{ + const ushort code = ch.unicode(); + return isValidLeadingEnvCharacter(ch) || ('0' <= code && code <= '9'); +} + +bool ShellCommand::isValidLeadingEnvCharacter(const QChar& ch) +{ + const ushort code = ch.unicode(); + return (code == '_') || ('A' <= code && code <= 'Z'); +} + +/* + * expandEnv + * + * Expand environment variables in text. Escaped '$' characters are ignored. + * Return true if any variables were expanded + */ +bool ShellCommand::expandEnv(QString& text) +{ + const QLatin1Char dollarChar('$'); + const QLatin1Char backslashChar('\\'); + + int dollarPos = 0; + bool expanded = false; + + // find and expand all environment variables beginning with '$' + while ((dollarPos = text.indexOf(dollarChar, dollarPos)) != -1) { + // if '$' is the last character, there is no way of expanding + if (dollarPos == text.length() - 1) { + break; + } + + // skip escaped '$' + if (dollarPos > 0 && text.at(dollarPos - 1) == backslashChar) { + dollarPos++; + continue; + } + + // if '$' is followed by an invalid leading character, skip this '$' + if (!isValidLeadingEnvCharacter(text.at(dollarPos + 1))) { + dollarPos++; + continue; + } + + int endPos = dollarPos + 1; + Q_ASSERT(endPos < text.length()); + while (endPos < text.length() && isValidEnvCharacter(text.at(endPos))) { + endPos++; + } + + const int len = endPos - dollarPos; + const QString key = text.mid(dollarPos + 1, len - 1); + const QString value = QString::fromLocal8Bit(qgetenv(key.toLocal8Bit())); + + if (!value.isEmpty()) { + text.replace(dollarPos, len, value); + expanded = true; + dollarPos = dollarPos + value.length(); + } else { + dollarPos = endPos; + } + } + + return expanded; +} diff --git a/konsole/src/ShellCommand.h b/konsole/src/ShellCommand.h new file mode 100644 index 00000000..1ed060cf --- /dev/null +++ b/konsole/src/ShellCommand.h @@ -0,0 +1,95 @@ +/* + Copyright 2007-2008 by Robert Knight + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +#ifndef SHELLCOMMAND_H +#define SHELLCOMMAND_H + +// Qt +#include + +// Konsole +#include "konsoleprivate_export.h" + +namespace Konsole +{ +/** + * A class to parse and extract information about shell commands. + * + * ShellCommand can be used to: + * + *
      + *
    • Take a command-line (eg "/bin/sh -c /path/to/my/script") and split it + * into its component parts (eg. the command "/bin/sh" and the arguments + * "-c","/path/to/my/script") + *
    • + *
    • Take a command and a list of arguments and combine them to + * form a complete command line. + *
    • + *
    • Determine whether the binary specified by a command exists in the + * user's PATH. + *
    • + *
    • Determine whether a command-line specifies the execution of + * another command as the root user using su/sudo etc. + *
    • + *
    + */ +class KONSOLEPRIVATE_EXPORT ShellCommand +{ +public: + /** + * Constructs a ShellCommand from a command line. + * + * @param aCommand The command line to parse. + */ + explicit ShellCommand(const QString& aCommand); + /** + * Constructs a ShellCommand with the specified @p aCommand and @p aArguments. + */ + ShellCommand(const QString& aCommand, const QStringList& aArguments); + + /** Returns the command. */ + QString command() const; + /** Returns the arguments. */ + QStringList arguments() const; + + /** + * Returns the full command line. + */ + QString fullCommand() const; + + /** Expands environment variables in @p text .*/ + static QString expand(const QString& text); + + /** Expands environment variables in each string in @p list. */ + static QStringList expand(const QStringList& items); + + + static bool isValidEnvCharacter(const QChar& ch); + + static bool isValidLeadingEnvCharacter(const QChar& ch); + +private: + static bool expandEnv(QString& text); + + QStringList _arguments; +}; +} + +#endif // SHELLCOMMAND_H + diff --git a/konsole/src/TabTitleFormatButton.cpp b/konsole/src/TabTitleFormatButton.cpp new file mode 100644 index 00000000..a3ee707d --- /dev/null +++ b/konsole/src/TabTitleFormatButton.cpp @@ -0,0 +1,108 @@ +/* + Copyright 2007-2008 by Robert Knight + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +// Own +#include "TabTitleFormatButton.h" + +// Qt +#include +#include + +// KDE +#include + +using namespace Konsole; + +const TabTitleFormatButton::Element TabTitleFormatButton::_localElements[] = { + { "%n" , I18N_NOOP("Program Name: %n") }, + { "%d" , I18N_NOOP("Current Directory (Short): %d") }, + { "%D" , I18N_NOOP("Current Directory (Long): %D") }, + { "%w" , I18N_NOOP("Window Title Set by Shell: %w") }, + { "%#" , I18N_NOOP("Session Number: %#") }, + { "%u" , I18N_NOOP("User Name: %u") }, + { "%h" , I18N_NOOP("Local Host: %h") } +}; +const int TabTitleFormatButton::_localElementCount = + sizeof(_localElements) / sizeof(TabTitleFormatButton::Element); + +const TabTitleFormatButton::Element TabTitleFormatButton::_remoteElements[] = { + { "%u" , I18N_NOOP("User Name: %u") }, + { "%h" , I18N_NOOP("Remote Host (Short): %h") }, + { "%H" , I18N_NOOP("Remote Host (Long): %H") }, + { "%w" , I18N_NOOP("Window Title Set by Shell: %w") }, + { "%#" , I18N_NOOP("Session Number: %#") } +}; +const int TabTitleFormatButton::_remoteElementCount = + sizeof(_remoteElements) / sizeof(TabTitleFormatButton::Element); + +TabTitleFormatButton::TabTitleFormatButton(QWidget* aParent) + : QPushButton(aParent) + , _context(Session::LocalTabTitle) +{ + setText(i18n("Insert")); + setMenu(new QMenu()); + connect(menu() , SIGNAL(triggered(QAction*)) , this , SLOT(fireElementSelected(QAction*))); +} + +TabTitleFormatButton::~TabTitleFormatButton() +{ + menu()->deleteLater(); +} + +void TabTitleFormatButton::fireElementSelected(QAction* action) +{ + emit dynamicElementSelected(action->data().value()); +} + +void TabTitleFormatButton::setContext(Session::TabTitleContext titleContext) +{ + _context = titleContext; + + menu()->clear(); + + int count = 0; + const Element* array = 0; + + if (titleContext == Session::LocalTabTitle) { + setToolTip(i18nc("@info:tooltip", "Insert title format")); + array = _localElements; + count = _localElementCount; + } else if (titleContext == Session::RemoteTabTitle) { + setToolTip(i18nc("@info:tooltip", "Insert remote title format")); + array = _remoteElements; + count = _remoteElementCount; + } + + QList menuActions; + for (int i = 0 ; i < count ; i++) { + QAction* action = new QAction(i18n(array[i].description), this); + action->setData(array[i].element); + menuActions << action; + } + + menu()->addActions(menuActions); +} + +Session::TabTitleContext TabTitleFormatButton::context() const +{ + return _context; +} + +#include "moc_TabTitleFormatButton.cpp" + diff --git a/konsole/src/TabTitleFormatButton.h b/konsole/src/TabTitleFormatButton.h new file mode 100644 index 00000000..8907c1db --- /dev/null +++ b/konsole/src/TabTitleFormatButton.h @@ -0,0 +1,63 @@ +/* + Copyright 2007-2008 by Robert Knight + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +#ifndef TABTITLEFORMATBUTTON_H +#define TABTITLEFORMATBUTTON_H + +// Qt +#include +#include + +// Konsole +#include "Session.h" + +namespace Konsole +{ +class TabTitleFormatButton : public QPushButton +{ + Q_OBJECT + +public: + explicit TabTitleFormatButton(QWidget* parent); + ~TabTitleFormatButton(); + + void setContext(Session::TabTitleContext context); + Session::TabTitleContext context() const; + +signals: + void dynamicElementSelected(const QString&); + +private slots: + void fireElementSelected(QAction*); + +private: + Session::TabTitleContext _context; + + struct Element { + QString element; + const char* description; + }; + static const Element _localElements[]; + static const int _localElementCount; + static const Element _remoteElements[]; + static const int _remoteElementCount; +}; +} + +#endif // TABTITLEFORMATBUTTON_H diff --git a/konsole/src/TerminalCharacterDecoder.cpp b/konsole/src/TerminalCharacterDecoder.cpp new file mode 100644 index 00000000..3af6be61 --- /dev/null +++ b/konsole/src/TerminalCharacterDecoder.cpp @@ -0,0 +1,293 @@ +/* + This file is part of Konsole, an X terminal. + + Copyright 2006-2008 by Robert Knight + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 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 Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +// Own +#include "TerminalCharacterDecoder.h" + +// Qt +#include +#include + +// Konsole +#include "konsole_wcwidth.h" +#include "ExtendedCharTable.h" +#include "ColorScheme.h" + +using namespace Konsole; +PlainTextDecoder::PlainTextDecoder() + : _output(0) + , _includeTrailingWhitespace(true) + , _recordLinePositions(false) +{ +} +void PlainTextDecoder::setTrailingWhitespace(bool enable) +{ + _includeTrailingWhitespace = enable; +} +bool PlainTextDecoder::trailingWhitespace() const +{ + return _includeTrailingWhitespace; +} +void PlainTextDecoder::begin(QTextStream* output) +{ + _output = output; + if (!_linePositions.isEmpty()) + _linePositions.clear(); +} +void PlainTextDecoder::end() +{ + _output = 0; +} + +void PlainTextDecoder::setRecordLinePositions(bool record) +{ + _recordLinePositions = record; +} +QList PlainTextDecoder::linePositions() const +{ + return _linePositions; +} +void PlainTextDecoder::decodeLine(const Character* const characters, int count, LineProperty /*properties*/ + ) +{ + Q_ASSERT(_output); + + if (_recordLinePositions && _output->string()) { + int pos = _output->string()->count(); + _linePositions << pos; + } + + //TODO should we ignore or respect the LINE_WRAPPED line property? + + //note: we build up a QString and send it to the text stream rather writing into the text + //stream a character at a time because it is more efficient. + //(since QTextStream always deals with QStrings internally anyway) + QString plainText; + plainText.reserve(count); + + int outputCount = count; + + // if inclusion of trailing whitespace is disabled then find the end of the + // line + if (!_includeTrailingWhitespace) { + for (int i = count - 1 ; i >= 0 ; i--) { + if (!characters[i].isSpace()) + break; + else + outputCount--; + } + } + + // find out the last technically real character in the line + int realCharacterGuard = -1; + for (int i = count - 1 ; i >= 0 ; i--) { + // FIXME: the special case of '\n' here is really ugly + // Maybe the '\n' should be added after calling this method in + // Screen::copyLineToStream() + if (characters[i].isRealCharacter && characters[i].character != '\n') { + realCharacterGuard = i; + break; + } + } + + for (int i = 0; i < outputCount;) { + if (characters[i].rendition & RE_EXTENDED_CHAR) { + ushort extendedCharLength = 0; + const ushort* chars = ExtendedCharTable::instance.lookupExtendedChar(characters[i].character, extendedCharLength); + if (chars) { + const QString s = QString::fromUtf16(chars, extendedCharLength); + plainText.append(s); + i += qMax(1, string_width(s)); + } + } else { + // All characters which appear before the last real character are + // seen as real characters, even when they are technically marked as + // non-real. + // + // This feels tricky, but otherwise leading "whitespaces" may be + // lost in some situation. One typical example is copying the result + // of `dialog --infobox "qwe" 10 10` . + if (characters[i].isRealCharacter || i <= realCharacterGuard) { + plainText.append(QChar(characters[i].character)); + i += qMax(1, konsole_wcwidth(characters[i].character)); + } else { + ++i; // should we 'break' directly here? + } + } + } + *_output << plainText; +} + +HTMLDecoder::HTMLDecoder() : + _output(0) + , _colorTable(ColorScheme::defaultTable) + , _innerSpanOpen(false) + , _lastRendition(DEFAULT_RENDITION) +{ +} + +void HTMLDecoder::begin(QTextStream* output) +{ + _output = output; + + QString text; + + text.append("\n"); + text.append("\n"); + text.append("\n"); + text.append("Konsole output\n"); + text.append("\n"); + text.append("\n"); + text.append("\n"); + text.append("
    \n"); + + //open monospace span + openSpan(text, "font-family:monospace"); + + *output << text; +} + +void HTMLDecoder::end() +{ + Q_ASSERT(_output); + + QString text; + + closeSpan(text); + text.append("
    \n"); + text.append("\n"); + text.append("\n"); + + *_output << text; + + _output = 0; +} + +//TODO: Support for LineProperty (mainly double width , double height) +void HTMLDecoder::decodeLine(const Character* const characters, int count, LineProperty /*properties*/ + ) +{ + Q_ASSERT(_output); + + QString text; + + int spaceCount = 0; + + for (int i = 0; i < count; i++) { + //check if appearance of character is different from previous char + if (characters[i].rendition != _lastRendition || + characters[i].foregroundColor != _lastForeColor || + characters[i].backgroundColor != _lastBackColor) { + if (_innerSpanOpen) { + closeSpan(text); + _innerSpanOpen = false; + } + + _lastRendition = characters[i].rendition; + _lastForeColor = characters[i].foregroundColor; + _lastBackColor = characters[i].backgroundColor; + + //build up style string + QString style; + + bool useBold; + + //colors - a color table must have been defined first + if (_colorTable) { + ColorEntry::FontWeight weight = characters[i].fontWeight(_colorTable); + if (weight == ColorEntry::UseCurrentFormat) + useBold = _lastRendition & RE_BOLD; + else + useBold = weight == ColorEntry::Bold; + + if (useBold) + style.append("font-weight:bold;"); + + if (_lastRendition & RE_UNDERLINE) + style.append("font-decoration:underline;"); + + style.append(QString("color:%1;").arg(_lastForeColor.color(_colorTable).name())); + + style.append(QString("background-color:%1;").arg(_lastBackColor.color(_colorTable).name())); + } + + //open the span with the current style + openSpan(text, style); + _innerSpanOpen = true; + } + + //handle whitespace + if (characters[i].isSpace()) + spaceCount++; + else + spaceCount = 0; + + //output current character + if (spaceCount < 2) { + if (characters[i].rendition & RE_EXTENDED_CHAR) { + ushort extendedCharLength = 0; + const ushort* chars = ExtendedCharTable::instance.lookupExtendedChar(characters[i].character, extendedCharLength); + if (chars) { + text.append(QString::fromUtf16(chars, extendedCharLength)); + } + } else { + //escape HTML tag characters and just display others as they are + const QChar ch = characters[i].character; + if (ch == '<') + text.append("<"); + else if (ch == '>') + text.append(">"); + else + text.append(ch); + } + } else { + // HTML truncates multiple spaces, so use a space marker instead + // Use   instead of   so xmllint will work. + text.append(" "); + } + } + + //close any remaining open inner spans + if (_innerSpanOpen) { + closeSpan(text); + _innerSpanOpen = false; + } + + //start new line + text.append("
    "); + + *_output << text; +} +void HTMLDecoder::openSpan(QString& text , const QString& style) +{ + text.append(QString("").arg(style)); +} + +void HTMLDecoder::closeSpan(QString& text) +{ + text.append(""); +} + +void HTMLDecoder::setColorTable(const ColorEntry* table) +{ + _colorTable = table; +} diff --git a/konsole/src/TerminalCharacterDecoder.h b/konsole/src/TerminalCharacterDecoder.h new file mode 100644 index 00000000..0edf2305 --- /dev/null +++ b/konsole/src/TerminalCharacterDecoder.h @@ -0,0 +1,149 @@ +/* + This file is part of Konsole, an X terminal. + + Copyright 2006-2008 by Robert Knight + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 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 Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +#ifndef TERMINAL_CHARACTER_DECODER_H +#define TERMINAL_CHARACTER_DECODER_H + +// Qt +#include + +// Konsole +#include "Character.h" +#include "konsoleprivate_export.h" + +#include + +namespace Konsole +{ +/** + * Base class for terminal character decoders + * + * The decoder converts lines of terminal characters which consist of a unicode character, foreground + * and background colors and other appearance-related properties into text strings. + * + * Derived classes may produce either plain text with no other color or appearance information, or + * they may produce text which incorporates these additional properties. + */ +class KONSOLEPRIVATE_EXPORT TerminalCharacterDecoder +{ +public: + virtual ~TerminalCharacterDecoder() {} + + /** Begin decoding characters. The resulting text is appended to @p output. */ + virtual void begin(QTextStream* output) = 0; + /** End decoding. */ + virtual void end() = 0; + + /** + * Converts a line of terminal characters with associated properties into a text string + * and writes the string into an output QTextStream. + * + * @param characters An array of characters of length @p count. + * @param count The number of characters + * @param properties Additional properties which affect all characters in the line + */ + virtual void decodeLine(const Character* const characters, + int count, + LineProperty properties) = 0; +}; + +/** + * A terminal character decoder which produces plain text, ignoring colors and other appearance-related + * properties of the original characters. + */ +class KONSOLEPRIVATE_EXPORT PlainTextDecoder : public TerminalCharacterDecoder +{ +public: + PlainTextDecoder(); + + /** + * Set whether trailing whitespace at the end of lines should be included + * in the output. + * Defaults to true. + */ + void setTrailingWhitespace(bool enable); + /** + * Returns whether trailing whitespace at the end of lines is included + * in the output. + */ + bool trailingWhitespace() const; + /** + * Returns of character positions in the output stream + * at which new lines where added. Returns an empty if setTrackLinePositions() is false or if + * the output device is not a string. + */ + QList linePositions() const; + /** Enables recording of character positions at which new lines are added. See linePositions() */ + void setRecordLinePositions(bool record); + + virtual void begin(QTextStream* output); + virtual void end(); + + virtual void decodeLine(const Character* const characters, + int count, + LineProperty properties); + +private: + QTextStream* _output; + bool _includeTrailingWhitespace; + + bool _recordLinePositions; + QList _linePositions; +}; + +/** + * A terminal character decoder which produces pretty HTML markup + */ +class KONSOLEPRIVATE_EXPORT HTMLDecoder : public TerminalCharacterDecoder +{ +public: + /** + * Constructs an HTML decoder using a default black-on-white color scheme. + */ + HTMLDecoder(); + + /** + * Sets the color table which the decoder uses to produce the HTML color codes in its + * output + */ + void setColorTable(const ColorEntry* table); + + virtual void decodeLine(const Character* const characters, + int count, + LineProperty properties); + + virtual void begin(QTextStream* output); + virtual void end(); + +private: + void openSpan(QString& text , const QString& style); + void closeSpan(QString& text); + + QTextStream* _output; + const ColorEntry* _colorTable; + bool _innerSpanOpen; + quint8 _lastRendition; + CharacterColor _lastForeColor; + CharacterColor _lastBackColor; +}; +} + +#endif diff --git a/konsole/src/TerminalDisplay.cpp b/konsole/src/TerminalDisplay.cpp new file mode 100644 index 00000000..701c720f --- /dev/null +++ b/konsole/src/TerminalDisplay.cpp @@ -0,0 +1,3230 @@ +/* + This file is part of Konsole, a terminal emulator for KDE. + + Copyright 2006-2008 by Robert Knight + Copyright 1997,1998 by Lars Doelle + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +// Own +#include "TerminalDisplay.h" + +// Qt +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// KDE +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +// Konsole +#include "Filter.h" +#include "konsole_wcwidth.h" +#include "TerminalCharacterDecoder.h" +#include "Screen.h" +#include "ScreenWindow.h" +#include "LineFont.h" +#include "SessionController.h" +#include "ExtendedCharTable.h" +#include "SessionManager.h" +#include "Session.h" + +using namespace Konsole; + +#ifndef loc +#define loc(X,Y) ((Y)*_columns+(X)) +#endif + +#define REPCHAR "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \ + "abcdefgjijklmnopqrstuvwxyz" \ + "0123456789./+@" + +// we use this to force QPainter to display text in LTR mode +// more information can be found in: http://unicode.org/reports/tr9/ +const QChar LTR_OVERRIDE_CHAR(0x202D); + +/* ------------------------------------------------------------------------- */ +/* */ +/* Colors */ +/* */ +/* ------------------------------------------------------------------------- */ + +/* Note that we use ANSI color order (bgr), while IBMPC color order is (rgb) + + Code 0 1 2 3 4 5 6 7 + ----------- ------- ------- ------- ------- ------- ------- ------- ------- + ANSI (bgr) Black Red Green Yellow Blue Magenta Cyan White + IBMPC (rgb) Black Blue Green Cyan Red Magenta Yellow White +*/ + +ScreenWindow* TerminalDisplay::screenWindow() const +{ + return _screenWindow; +} +void TerminalDisplay::setScreenWindow(ScreenWindow* window) +{ + // disconnect existing screen window if any + if (_screenWindow) { + disconnect(_screenWindow , 0 , this , 0); + } + + _screenWindow = window; + + if (_screenWindow) { + connect(_screenWindow , SIGNAL(outputChanged()) , this , SLOT(updateLineProperties())); + connect(_screenWindow , SIGNAL(outputChanged()) , this , SLOT(updateImage())); + connect(_screenWindow , SIGNAL(currentResultLineChanged()) , this , SLOT(updateImage())); + _screenWindow->setWindowLines(_lines); + } +} + +const ColorEntry* TerminalDisplay::colorTable() const +{ + return _colorTable; +} +void TerminalDisplay::setBackgroundColor(const QColor& color) +{ + _colorTable[DEFAULT_BACK_COLOR].color = color; + + QPalette p = palette(); + p.setColor(backgroundRole(), color); + setPalette(p); + + // Avoid propagating the palette change to the scroll bar + _scrollBar->setPalette(QApplication::palette()); + + update(); +} +QColor TerminalDisplay::getBackgroundColor() const +{ + QPalette p = palette(); + return p.color(backgroundRole()); +} +void TerminalDisplay::setForegroundColor(const QColor& color) +{ + _colorTable[DEFAULT_FORE_COLOR].color = color; + + update(); +} +void TerminalDisplay::setColorTable(const ColorEntry table[]) +{ + for (int i = 0; i < TABLE_COLORS; i++) + _colorTable[i] = table[i]; + + setBackgroundColor(_colorTable[DEFAULT_BACK_COLOR].color); +} + +/* ------------------------------------------------------------------------- */ +/* */ +/* Font */ +/* */ +/* ------------------------------------------------------------------------- */ + +static inline bool isLineCharString(const QString& string) +{ + if (string.length() == 0) + return false; + + return isSupportedLineChar(string.at(0).unicode()); +} + +void TerminalDisplay::fontChange(const QFont&) +{ + QFontMetrics fm(font()); + _fontHeight = fm.height() + _lineSpacing; + + // waba TerminalDisplay 1.123: + // "Base character width on widest ASCII character. This prevents too wide + // characters in the presence of double wide (e.g. Japanese) characters." + // Get the width from representative normal width characters + _fontWidth = qRound((static_cast(fm.width(REPCHAR)) / static_cast(qstrlen(REPCHAR)))); + + _fixedFont = true; + + const int fw = fm.width(REPCHAR[0]); + for (unsigned int i = 1; i < qstrlen(REPCHAR); i++) { + if (fw != fm.width(REPCHAR[i])) { + _fixedFont = false; + break; + } + } + + if (_fontWidth < 1) + _fontWidth = 1; + + emit changedFontMetricSignal(_fontHeight, _fontWidth); + propagateSize(); + update(); +} + +void TerminalDisplay::setVTFont(const QFont& f) +{ + QFont font = f; + + QFontMetrics metrics(font); + + if (!QFontInfo(font).fixedPitch()) { + kWarning() << "Using an unsupported variable-width font in the terminal. This may produce display errors."; + } + + if (metrics.height() < height() && metrics.maxWidth() < width()) { + // hint that text should be drawn without anti-aliasing. + // depending on the user's font configuration, this may not be respected + if (!_antialiasText) + font.setHintingPreference(QFont::PreferNoHinting); + + // experimental optimization. Konsole assumes that the terminal is using a + // mono-spaced font, in which case kerning information should have an effect. + // Disabling kerning saves some computation when rendering text. + font.setKerning(false); + + QWidget::setFont(font); + fontChange(font); + } +} + +void TerminalDisplay::increaseFontSize() +{ + QFont font = getVTFont(); + font.setPointSizeF(font.pointSizeF() + 1); + setVTFont(font); +} + +void TerminalDisplay::decreaseFontSize() +{ + const qreal MinimumFontSize = 6; + + QFont font = getVTFont(); + font.setPointSizeF(qMax(font.pointSizeF() - 1, MinimumFontSize)); + setVTFont(font); +} + +uint TerminalDisplay::lineSpacing() const +{ + return _lineSpacing; +} + +void TerminalDisplay::setLineSpacing(uint i) +{ + _lineSpacing = i; + setVTFont(font()); // Trigger an update. +} + +/* ------------------------------------------------------------------------- */ +/* */ +/* Constructor / Destructor */ +/* */ +/* ------------------------------------------------------------------------- */ + +TerminalDisplay::TerminalDisplay(QWidget* parent) + : QWidget(parent) + , _screenWindow(0) + , _bellMasked(false) + , _gridLayout(0) + , _fontHeight(1) + , _fontWidth(1) + , _boldIntense(true) + , _lines(1) + , _columns(1) + , _usedLines(1) + , _usedColumns(1) + , _image(0) + , _randomSeed(0) + , _resizing(false) + , _showTerminalSizeHint(true) + , _bidiEnabled(false) + , _actSel(0) + , _wordSelectionMode(false) + , _lineSelectionMode(false) + , _preserveLineBreaks(false) + , _columnSelectionMode(false) + , _autoCopySelectedText(false) + , _middleClickPasteMode(Enum::PasteFromX11Selection) + , _scrollbarLocation(Enum::ScrollBarRight) + , _scrollFullPage(false) + , _wordCharacters(":@-./_~") + , _bellMode(Enum::NotifyBell) + , _allowBlinkingText(true) + , _allowBlinkingCursor(false) + , _textBlinking(false) + , _cursorBlinking(false) + , _hasTextBlinker(false) + , _underlineLinks(true) + , _openLinksByDirectClick(false) + , _ctrlRequiredForDrag(true) + , _tripleClickMode(Enum::SelectWholeLine) + , _possibleTripleClick(false) + , _resizeWidget(0) + , _resizeTimer(0) + , _flowControlWarningEnabled(false) + , _outputSuspendedLabel(0) + , _lineSpacing(0) + , _blendColor(qRgba(0, 0, 0, 0xff)) + , _filterChain(new TerminalImageFilterChain()) + , _cursorShape(Enum::BlockCursor) + , _antialiasText(true) + , _printerFriendly(false) + , _sessionController(0) + , _trimTrailingSpaces(false) + , _margin(1) + , _centerContents(false) + , _opacity(1.0) +{ + // terminal applications are not designed with Right-To-Left in mind, + // so the layout is forced to Left-To-Right + setLayoutDirection(Qt::LeftToRight); + + _contentRect = QRect(_margin, _margin, 1, 1); + + // create scroll bar for scrolling output up and down + _scrollBar = new QScrollBar(this); + // set the scroll bar's slider to occupy the whole area of the scroll bar initially + setScroll(0, 0); + _scrollBar->setCursor(Qt::ArrowCursor); + connect(_scrollBar, SIGNAL(valueChanged(int)), + this, SLOT(scrollBarPositionChanged(int))); + connect(_scrollBar, SIGNAL(sliderMoved(int)), + this, SLOT(viewScrolledByUser())); + + // setup timers for blinking text + _blinkTextTimer = new QTimer(this); + _blinkTextTimer->setInterval(TEXT_BLINK_DELAY); + connect(_blinkTextTimer, SIGNAL(timeout()), this, SLOT(blinkTextEvent())); + + // setup timers for blinking cursor + _blinkCursorTimer = new QTimer(this); + _blinkCursorTimer->setInterval(QApplication::cursorFlashTime() / 2); + connect(_blinkCursorTimer, SIGNAL(timeout()), this, SLOT(blinkCursorEvent())); + + // hide mouse cursor on keystroke or idle + KCursor::setAutoHideCursor(this, true); + setMouseTracking(true); + + setUsesMouse(true); + setBracketedPasteMode(false); + + setColorTable(ColorScheme::defaultTable); + + // Enable drag and drop support + setAcceptDrops(true); + _dragInfo.state = diNone; + + setFocusPolicy(Qt::WheelFocus); + +#ifndef QT_KATIE + // enable input method support + setAttribute(Qt::WA_InputMethodEnabled, true); +#endif + + // this is an important optimization, it tells Qt + // that TerminalDisplay will handle repainting its entire area. + setAttribute(Qt::WA_OpaquePaintEvent); + + _gridLayout = new QGridLayout(this); + _gridLayout->setContentsMargins(0, 0, 0, 0); + + setLayout(_gridLayout); + + new AutoScrollHandler(this); +} + +TerminalDisplay::~TerminalDisplay() +{ + disconnect(_blinkTextTimer); + disconnect(_blinkCursorTimer); + + delete[] _image; + + delete _gridLayout; + delete _outputSuspendedLabel; + delete _filterChain; +} + +/* ------------------------------------------------------------------------- */ +/* */ +/* Display Operations */ +/* */ +/* ------------------------------------------------------------------------- */ + +/** + A table for emulating the simple (single width) unicode drawing chars. + It represents the 250x - 257x glyphs. If it's zero, we can't use it. + if it's not, it's encoded as follows: imagine a 5x5 grid where the points are numbered + 0 to 24 left to top, top to bottom. Each point is represented by the corresponding bit. + + Then, the pixels basically have the following interpretation: + _|||_ + -...- + -...- + -...- + _|||_ + +where _ = none + | = vertical line. + - = horizontal line. + */ + +enum LineEncode { + TopL = (1 << 1), + TopC = (1 << 2), + TopR = (1 << 3), + + LeftT = (1 << 5), + Int11 = (1 << 6), + Int12 = (1 << 7), + Int13 = (1 << 8), + RightT = (1 << 9), + + LeftC = (1 << 10), + Int21 = (1 << 11), + Int22 = (1 << 12), + Int23 = (1 << 13), + RightC = (1 << 14), + + LeftB = (1 << 15), + Int31 = (1 << 16), + Int32 = (1 << 17), + Int33 = (1 << 18), + RightB = (1 << 19), + + BotL = (1 << 21), + BotC = (1 << 22), + BotR = (1 << 23) +}; + +static void drawLineChar(QPainter& paint, int x, int y, int w, int h, uchar code) +{ + //Calculate cell midpoints, end points. + const int cx = x + w / 2; + const int cy = y + h / 2; + const int ex = x + w - 1; + const int ey = y + h - 1; + + const quint32 toDraw = LineChars[code]; + + //Top _lines: + if (toDraw & TopL) + paint.drawLine(cx - 1, y, cx - 1, cy - 2); + if (toDraw & TopC) + paint.drawLine(cx, y, cx, cy - 2); + if (toDraw & TopR) + paint.drawLine(cx + 1, y, cx + 1, cy - 2); + + //Bot _lines: + if (toDraw & BotL) + paint.drawLine(cx - 1, cy + 2, cx - 1, ey); + if (toDraw & BotC) + paint.drawLine(cx, cy + 2, cx, ey); + if (toDraw & BotR) + paint.drawLine(cx + 1, cy + 2, cx + 1, ey); + + //Left _lines: + if (toDraw & LeftT) + paint.drawLine(x, cy - 1, cx - 2, cy - 1); + if (toDraw & LeftC) + paint.drawLine(x, cy, cx - 2, cy); + if (toDraw & LeftB) + paint.drawLine(x, cy + 1, cx - 2, cy + 1); + + //Right _lines: + if (toDraw & RightT) + paint.drawLine(cx + 2, cy - 1, ex, cy - 1); + if (toDraw & RightC) + paint.drawLine(cx + 2, cy, ex, cy); + if (toDraw & RightB) + paint.drawLine(cx + 2, cy + 1, ex, cy + 1); + + //Intersection points. + if (toDraw & Int11) + paint.drawPoint(cx - 1, cy - 1); + if (toDraw & Int12) + paint.drawPoint(cx, cy - 1); + if (toDraw & Int13) + paint.drawPoint(cx + 1, cy - 1); + + if (toDraw & Int21) + paint.drawPoint(cx - 1, cy); + if (toDraw & Int22) + paint.drawPoint(cx, cy); + if (toDraw & Int23) + paint.drawPoint(cx + 1, cy); + + if (toDraw & Int31) + paint.drawPoint(cx - 1, cy + 1); + if (toDraw & Int32) + paint.drawPoint(cx, cy + 1); + if (toDraw & Int33) + paint.drawPoint(cx + 1, cy + 1); +} + +void TerminalDisplay::drawLineCharString(QPainter& painter, int x, int y, const QString& str, + const Character* attributes) +{ + const QPen& originalPen = painter.pen(); + + if ((attributes->rendition & RE_BOLD) && _boldIntense) { + QPen boldPen(originalPen); + boldPen.setWidth(3); + painter.setPen(boldPen); + } + + for (int i = 0 ; i < str.length(); i++) { + const uchar code = str[i].cell(); + if (LineChars[code]) + drawLineChar(painter, x + (_fontWidth * i), y, _fontWidth, _fontHeight, code); + } + + painter.setPen(originalPen); +} + +void TerminalDisplay::setKeyboardCursorShape(Enum::CursorShapeEnum shape) +{ + _cursorShape = shape; +} +Enum::CursorShapeEnum TerminalDisplay::keyboardCursorShape() const +{ + return _cursorShape; +} +void TerminalDisplay::setKeyboardCursorColor(const QColor& color) +{ + _cursorColor = color; +} +QColor TerminalDisplay::keyboardCursorColor() const +{ + return _cursorColor; +} + +void TerminalDisplay::setOpacity(qreal opacity) +{ + QColor color(_blendColor); + color.setAlphaF(opacity); + _opacity = opacity; + + // enable automatic background filling to prevent the display + // flickering if there is no transparency + /*if ( color.alpha() == 255 ) + { + setAutoFillBackground(true); + } + else + { + setAutoFillBackground(false); + }*/ + + _blendColor = color.rgba(); +} + +void TerminalDisplay::setWallpaper(ColorSchemeWallpaper::Ptr p) +{ + _wallpaper = p; +} + +void TerminalDisplay::drawBackground(QPainter& painter, const QRect& rect, const QColor& backgroundColor, bool useOpacitySetting) +{ + // the area of the widget showing the contents of the terminal display is drawn + // using the background color from the color scheme set with setColorTable() + // + // the area of the widget behind the scroll-bar is drawn using the background + // brush from the scroll-bar's palette, to give the effect of the scroll-bar + // being outside of the terminal display and visual consistency with other KDE + // applications. + // + QRect scrollBarArea = _scrollBar->isVisible() ? + rect.intersected(_scrollBar->geometry()) : + QRect(); + QRegion contentsRegion = QRegion(rect).subtracted(scrollBarArea); + QRect contentsRect = contentsRegion.boundingRect(); + + if (useOpacitySetting && !_wallpaper->isNull() && + _wallpaper->draw(painter, contentsRect, _opacity)) { + ; + } else if (qAlpha(_blendColor) < 0xff && useOpacitySetting) { + QColor color(backgroundColor); + color.setAlpha(qAlpha(_blendColor)); + + painter.save(); + painter.setCompositionMode(QPainter::CompositionMode_Source); + painter.fillRect(contentsRect, color); + painter.restore(); + } else { + painter.fillRect(contentsRect, backgroundColor); + } + + painter.fillRect(scrollBarArea, _scrollBar->palette().background()); +} + +void TerminalDisplay::drawCursor(QPainter& painter, + const QRect& rect, + const QColor& foregroundColor, + const QColor& /*backgroundColor*/, + bool& invertCharacterColor) +{ + // don't draw cursor which is currently blinking + if (_cursorBlinking) + return; + + QRect cursorRect = rect; + cursorRect.setHeight(_fontHeight - _lineSpacing - 1); + + QColor cursorColor = _cursorColor.isValid() ? _cursorColor : foregroundColor; + painter.setPen(cursorColor); + + if (_cursorShape == Enum::BlockCursor) { + // draw the cursor outline, adjusting the area so that + // it is draw entirely inside 'rect' + int penWidth = qMax(1, painter.pen().width()); + painter.drawRect(cursorRect.adjusted(penWidth / 2, + penWidth / 2, + - penWidth / 2 - penWidth % 2, + - penWidth / 2 - penWidth % 2)); + + // draw the cursor body only when the widget has focus + if (hasFocus()) { + painter.fillRect(cursorRect, cursorColor); + + if (!_cursorColor.isValid()) { + // invert the color used to draw the text to ensure that the character at + // the cursor position is readable + invertCharacterColor = true; + } + } + } else if (_cursorShape == Enum::UnderlineCursor) { + painter.drawLine(cursorRect.left(), + cursorRect.bottom(), + cursorRect.right(), + cursorRect.bottom()); + + } else if (_cursorShape == Enum::IBeamCursor) { + painter.drawLine(cursorRect.left(), + cursorRect.top(), + cursorRect.left(), + cursorRect.bottom()); + } +} + +void TerminalDisplay::drawCharacters(QPainter& painter, + const QRect& rect, + const QString& text, + const Character* style, + bool invertCharacterColor) +{ + // don't draw text which is currently blinking + if (_textBlinking && (style->rendition & RE_BLINK)) + return; + + // setup bold and underline + bool useBold; + ColorEntry::FontWeight weight = style->fontWeight(_colorTable); + if (weight == ColorEntry::UseCurrentFormat) + useBold = ((style->rendition & RE_BOLD) && _boldIntense) || font().bold(); + else + useBold = (weight == ColorEntry::Bold) ? true : false; + const bool useUnderline = style->rendition & RE_UNDERLINE || font().underline(); + const bool useItalic = style->rendition & RE_ITALIC || font().italic(); + + QFont font = painter.font(); + if (font.bold() != useBold + || font.underline() != useUnderline + || font.italic() != useItalic) { + font.setBold(useBold); + font.setUnderline(useUnderline); + font.setItalic(useItalic); + painter.setFont(font); + } + + // setup pen + const CharacterColor& textColor = (invertCharacterColor ? style->backgroundColor : style->foregroundColor); + const QColor color = textColor.color(_colorTable); + QPen pen = painter.pen(); + if (pen.color() != color) { + pen.setColor(color); + painter.setPen(color); + } + + // draw text + if (isLineCharString(text)) { + drawLineCharString(painter, rect.x(), rect.y(), text, style); + } else { + // Force using LTR as the document layout for the terminal area, because + // there is no use cases for RTL emulator and RTL terminal application. + // + // This still allows RTL characters to be rendered in the RTL way. + painter.setLayoutDirection(Qt::LeftToRight); + + // the drawText(rect,flags,string) overload is used here with null flags + // instead of drawText(rect,string) because the (rect,string) overload causes + // the application's default layout direction to be used instead of + // the widget-specific layout direction, which should always be + // Qt::LeftToRight for this widget + // + // This was discussed in: http://lists.kde.org/?t=120552223600002&r=1&w=2 + if (_bidiEnabled) { + painter.drawText(rect, 0, text); + } else { + // See bug 280896 for more info + painter.drawText(rect, Qt::AlignBottom, LTR_OVERRIDE_CHAR + text); + } + } +} + +void TerminalDisplay::drawTextFragment(QPainter& painter , + const QRect& rect, + const QString& text, + const Character* style) +{ + painter.save(); + + // setup painter + const QColor foregroundColor = style->foregroundColor.color(_colorTable); + const QColor backgroundColor = style->backgroundColor.color(_colorTable); + + // draw background if different from the display's background color + if (backgroundColor != palette().background().color()) + drawBackground(painter, rect, backgroundColor, + false /* do not use transparency */); + + // draw cursor shape if the current character is the cursor + // this may alter the foreground and background colors + bool invertCharacterColor = false; + if (style->rendition & RE_CURSOR) + drawCursor(painter, rect, foregroundColor, backgroundColor, invertCharacterColor); + + // draw text + drawCharacters(painter, rect, text, style, invertCharacterColor); + + painter.restore(); +} + +void TerminalDisplay::drawPrinterFriendlyTextFragment(QPainter& painter, + const QRect& rect, + const QString& text, + const Character* style) +{ + painter.save(); + + // Set the colors used to draw to black foreground and white + // background for printer friendly output when printing + Character print_style = *style; + print_style.foregroundColor = CharacterColor(COLOR_SPACE_RGB, 0x00000000); + print_style.backgroundColor = CharacterColor(COLOR_SPACE_RGB, 0xFFFFFFFF); + + // draw text + drawCharacters(painter, rect, text, &print_style, false); + + painter.restore(); +} + +void TerminalDisplay::setRandomSeed(uint randomSeed) +{ + _randomSeed = randomSeed; +} +uint TerminalDisplay::randomSeed() const +{ + return _randomSeed; +} + +// scrolls the image by 'lines', down if lines > 0 or up otherwise. +// +// the terminal emulation keeps track of the scrolling of the character +// image as it receives input, and when the view is updated, it calls scrollImage() +// with the final scroll amount. this improves performance because scrolling the +// display is much cheaper than re-rendering all the text for the +// part of the image which has moved up or down. +// Instead only new lines have to be drawn +void TerminalDisplay::scrollImage(int lines , const QRect& screenWindowRegion) +{ + // if the flow control warning is enabled this will interfere with the + // scrolling optimizations and cause artifacts. the simple solution here + // is to just disable the optimization whilst it is visible + if (_outputSuspendedLabel && _outputSuspendedLabel->isVisible()) + return; + + // constrain the region to the display + // the bottom of the region is capped to the number of lines in the display's + // internal image - 2, so that the height of 'region' is strictly less + // than the height of the internal image. + QRect region = screenWindowRegion; + region.setBottom(qMin(region.bottom(), this->_lines - 2)); + + // return if there is nothing to do + if (lines == 0 + || _image == 0 + || !region.isValid() + || (region.top() + abs(lines)) >= region.bottom() + || this->_lines <= region.height()) return; + + // hide terminal size label to prevent it being scrolled + if (_resizeWidget && _resizeWidget->isVisible()) + _resizeWidget->hide(); + + // Note: With Qt 4.4 the left edge of the scrolled area must be at 0 + // to get the correct (newly exposed) part of the widget repainted. + // + // The right edge must be before the left edge of the scroll bar to + // avoid triggering a repaint of the entire widget, the distance is + // given by SCROLLBAR_CONTENT_GAP + // + // Set the QT_FLUSH_PAINT environment variable to '1' before starting the + // application to monitor repainting. + // + const int scrollBarWidth = _scrollBar->isHidden() ? 0 : _scrollBar->width(); + const int SCROLLBAR_CONTENT_GAP = 1; + QRect scrollRect; + if (_scrollbarLocation == Enum::ScrollBarLeft) { + scrollRect.setLeft(scrollBarWidth + SCROLLBAR_CONTENT_GAP); + scrollRect.setRight(width()); + } else { + scrollRect.setLeft(0); + scrollRect.setRight(width() - scrollBarWidth - SCROLLBAR_CONTENT_GAP); + } + void* firstCharPos = &_image[ region.top() * this->_columns ]; + void* lastCharPos = &_image[(region.top() + abs(lines)) * this->_columns ]; + + const int top = _contentRect.top() + (region.top() * _fontHeight); + const int linesToMove = region.height() - abs(lines); + const int bytesToMove = linesToMove * this->_columns * sizeof(Character); + + Q_ASSERT(linesToMove > 0); + Q_ASSERT(bytesToMove > 0); + + //scroll internal image + if (lines > 0) { + // check that the memory areas that we are going to move are valid + Q_ASSERT((char*)lastCharPos + bytesToMove < + (char*)(_image + (this->_lines * this->_columns))); + + Q_ASSERT((lines * this->_columns) < _imageSize); + + //scroll internal image down + memmove(firstCharPos , lastCharPos , bytesToMove); + + //set region of display to scroll + scrollRect.setTop(top); + } else { + // check that the memory areas that we are going to move are valid + Q_ASSERT((char*)firstCharPos + bytesToMove < + (char*)(_image + (this->_lines * this->_columns))); + + //scroll internal image up + memmove(lastCharPos , firstCharPos , bytesToMove); + + //set region of the display to scroll + scrollRect.setTop(top + abs(lines) * _fontHeight); + } + scrollRect.setHeight(linesToMove * _fontHeight); + + Q_ASSERT(scrollRect.isValid() && !scrollRect.isEmpty()); + + //scroll the display vertically to match internal _image + scroll(0 , _fontHeight * (-lines) , scrollRect); +} + +QRegion TerminalDisplay::hotSpotRegion() const +{ + QRegion region; + foreach(Filter::HotSpot * hotSpot , _filterChain->hotSpots()) { + QRect r; + if (hotSpot->startLine() == hotSpot->endLine()) { + r.setLeft(hotSpot->startColumn()); + r.setTop(hotSpot->startLine()); + r.setRight(hotSpot->endColumn()); + r.setBottom(hotSpot->endLine()); + region |= imageToWidget(r); + } else { + r.setLeft(hotSpot->startColumn()); + r.setTop(hotSpot->startLine()); + r.setRight(_columns); + r.setBottom(hotSpot->startLine()); + region |= imageToWidget(r); + for (int line = hotSpot->startLine() + 1 ; line < hotSpot->endLine() ; line++) { + r.setLeft(0); + r.setTop(line); + r.setRight(_columns); + r.setBottom(line); + region |= imageToWidget(r); + } + r.setLeft(0); + r.setTop(hotSpot->endLine()); + r.setRight(hotSpot->endColumn()); + r.setBottom(hotSpot->endLine()); + region |= imageToWidget(r); + } + } + return region; +} + +void TerminalDisplay::processFilters() +{ + if (!_screenWindow) + return; + + QRegion preUpdateHotSpots = hotSpotRegion(); + + // use _screenWindow->getImage() here rather than _image because + // other classes may call processFilters() when this display's + // ScreenWindow emits a scrolled() signal - which will happen before + // updateImage() is called on the display and therefore _image is + // out of date at this point + _filterChain->setImage(_screenWindow->getImage(), + _screenWindow->windowLines(), + _screenWindow->windowColumns(), + _screenWindow->getLineProperties()); + _filterChain->process(); + + QRegion postUpdateHotSpots = hotSpotRegion(); + + update(preUpdateHotSpots | postUpdateHotSpots); +} + +void TerminalDisplay::updateImage() +{ + if (!_screenWindow) + return; + + // optimization - scroll the existing image where possible and + // avoid expensive text drawing for parts of the image that + // can simply be moved up or down + if (_wallpaper->isNull()) { + scrollImage(_screenWindow->scrollCount() , + _screenWindow->scrollRegion()); + _screenWindow->resetScrollCount(); + } + + if (!_image) { + // Create _image. + // The emitted changedContentSizeSignal also leads to getImage being recreated, so do this first. + updateImageSize(); + } + + Character* const newimg = _screenWindow->getImage(); + const int lines = _screenWindow->windowLines(); + const int columns = _screenWindow->windowColumns(); + + setScroll(_screenWindow->currentLine() , _screenWindow->lineCount()); + + Q_ASSERT(this->_usedLines <= this->_lines); + Q_ASSERT(this->_usedColumns <= this->_columns); + + int y, x, len; + + const QPoint tL = contentsRect().topLeft(); + const int tLx = tL.x(); + const int tLy = tL.y(); + _hasTextBlinker = false; + + CharacterColor cf; // undefined + + const int linesToUpdate = qMin(this->_lines, qMax(0, lines)); + const int columnsToUpdate = qMin(this->_columns, qMax(0, columns)); + + char* dirtyMask = new char[columnsToUpdate + 2]; + QRegion dirtyRegion; + + // debugging variable, this records the number of lines that are found to + // be 'dirty' ( ie. have changed from the old _image to the new _image ) and + // which therefore need to be repainted + int dirtyLineCount = 0; + + for (y = 0; y < linesToUpdate; ++y) { + const Character* currentLine = &_image[y * this->_columns]; + const Character* const newLine = &newimg[y * columns]; + + bool updateLine = false; + + // The dirty mask indicates which characters need repainting. We also + // mark surrounding neighbors dirty, in case the character exceeds + // its cell boundaries + memset(dirtyMask, 0, columnsToUpdate + 2); + + for (x = 0 ; x < columnsToUpdate ; ++x) { + if (newLine[x] != currentLine[x]) { + dirtyMask[x] = true; + } + } + + if (!_resizing) // not while _resizing, we're expecting a paintEvent + for (x = 0; x < columnsToUpdate; ++x) { + _hasTextBlinker |= (newLine[x].rendition & RE_BLINK); + + // Start drawing if this character or the next one differs. + // We also take the next one into account to handle the situation + // where characters exceed their cell width. + if (dirtyMask[x]) { + if (!newLine[x + 0].character) + continue; + const bool lineDraw = newLine[x + 0].isLineChar(); + const bool doubleWidth = (x + 1 == columnsToUpdate) ? false : (newLine[x + 1].character == 0); + const quint8 cr = newLine[x].rendition; + const CharacterColor clipboard = newLine[x].backgroundColor; + if (newLine[x].foregroundColor != cf) cf = newLine[x].foregroundColor; + const int lln = columnsToUpdate - x; + for (len = 1; len < lln; ++len) { + const Character& ch = newLine[x + len]; + + if (!ch.character) + continue; // Skip trailing part of multi-col chars. + + const bool nextIsDoubleWidth = (x + len + 1 == columnsToUpdate) ? false : (newLine[x + len + 1].character == 0); + + if (ch.foregroundColor != cf || + ch.backgroundColor != clipboard || + (ch.rendition & ~RE_EXTENDED_CHAR) != (cr & ~RE_EXTENDED_CHAR) || + !dirtyMask[x + len] || + ch.isLineChar() != lineDraw || + nextIsDoubleWidth != doubleWidth) + break; + } + + const bool saveFixedFont = _fixedFont; + if (lineDraw) + _fixedFont = false; + if (doubleWidth) + _fixedFont = false; + + updateLine = true; + + _fixedFont = saveFixedFont; + x += len - 1; + } + } + + //both the top and bottom halves of double height _lines must always be redrawn + //although both top and bottom halves contain the same characters, only + //the top one is actually + //drawn. + if (_lineProperties.count() > y) + updateLine |= (_lineProperties[y] & LINE_DOUBLEHEIGHT); + + // if the characters on the line are different in the old and the new _image + // then this line must be repainted. + if (updateLine) { + dirtyLineCount++; + + // add the area occupied by this line to the region which needs to be + // repainted + QRect dirtyRect = QRect(_contentRect.left() + tLx , + _contentRect.top() + tLy + _fontHeight * y , + _fontWidth * columnsToUpdate , + _fontHeight); + + dirtyRegion |= dirtyRect; + } + + // replace the line of characters in the old _image with the + // current line of the new _image + memcpy((void*)currentLine, (const void*)newLine, columnsToUpdate * sizeof(Character)); + } + + // if the new _image is smaller than the previous _image, then ensure that the area + // outside the new _image is cleared + if (linesToUpdate < _usedLines) { + dirtyRegion |= QRect(_contentRect.left() + tLx , + _contentRect.top() + tLy + _fontHeight * linesToUpdate , + _fontWidth * this->_columns , + _fontHeight * (_usedLines - linesToUpdate)); + } + _usedLines = linesToUpdate; + + if (columnsToUpdate < _usedColumns) { + dirtyRegion |= QRect(_contentRect.left() + tLx + columnsToUpdate * _fontWidth , + _contentRect.top() + tLy , + _fontWidth * (_usedColumns - columnsToUpdate) , + _fontHeight * this->_lines); + } + _usedColumns = columnsToUpdate; + + dirtyRegion |= _inputMethodData.previousPreeditRect; + + // update the parts of the display which have changed + update(dirtyRegion); + + if (_allowBlinkingText && _hasTextBlinker && !_blinkTextTimer->isActive()) { + _blinkTextTimer->start(); + } + if (!_hasTextBlinker && _blinkTextTimer->isActive()) { + _blinkTextTimer->stop(); + _textBlinking = false; + } + delete[] dirtyMask; +} + +void TerminalDisplay::showResizeNotification() +{ + if (_showTerminalSizeHint && isVisible()) { + if (!_resizeWidget) { + _resizeWidget = new QLabel(i18n("Size: XXX x XXX"), this); + _resizeWidget->setMinimumWidth(_resizeWidget->fontMetrics().width(i18n("Size: XXX x XXX"))); + _resizeWidget->setMinimumHeight(_resizeWidget->sizeHint().height()); + _resizeWidget->setAlignment(Qt::AlignCenter); + + _resizeWidget->setStyleSheet("background-color:palette(window);border-style:solid;border-width:1px;border-color:palette(dark)"); + + _resizeTimer = new QTimer(this); + _resizeTimer->setInterval(SIZE_HINT_DURATION); + _resizeTimer->setSingleShot(true); + connect(_resizeTimer, SIGNAL(timeout()), _resizeWidget, SLOT(hide())); + } + QString sizeStr = i18n("Size: %1 x %2", _columns, _lines); + _resizeWidget->setText(sizeStr); + _resizeWidget->move((width() - _resizeWidget->width()) / 2, + (height() - _resizeWidget->height()) / 2 + 20); + _resizeWidget->show(); + _resizeTimer->start(); + } +} + +void TerminalDisplay::paintEvent(QPaintEvent* pe) +{ + QPainter paint(this); + + foreach(const QRect & rect, (pe->region() & contentsRect()).rects()) { + drawBackground(paint, rect, palette().background().color(), + true /* use opacity setting */); + drawContents(paint, rect); + } + drawCurrentResultRect(paint); + drawInputMethodPreeditString(paint, preeditRect()); + paintFilters(paint); +} + +void TerminalDisplay::printContent(QPainter& painter, bool friendly) +{ + // Reinitialize the font with the printers paint device so the font + // measurement calculations will be done correctly + QFont savedFont = getVTFont(); + QFont font(savedFont, painter.device()); + painter.setFont(font); + setVTFont(font); + + QRect rect(0, 0, size().width(), size().height()); + + _printerFriendly = friendly; + if (!friendly) { + drawBackground(painter, rect, getBackgroundColor(), + true /* use opacity setting */); + } + drawContents(painter, rect); + _printerFriendly = false; + setVTFont(savedFont); +} + +QPoint TerminalDisplay::cursorPosition() const +{ + if (_screenWindow) + return _screenWindow->cursorPosition(); + else + return QPoint(0, 0); +} + +FilterChain* TerminalDisplay::filterChain() const +{ + return _filterChain; +} + +void TerminalDisplay::paintFilters(QPainter& painter) +{ + // get color of character under mouse and use it to draw + // lines for filters + QPoint cursorPos = mapFromGlobal(QCursor::pos()); + int cursorLine; + int cursorColumn; + + getCharacterPosition(cursorPos , cursorLine , cursorColumn); + Character cursorCharacter = _image[loc(cursorColumn, cursorLine)]; + + painter.setPen(QPen(cursorCharacter.foregroundColor.color(colorTable()))); + + // iterate over hotspots identified by the display's currently active filters + // and draw appropriate visuals to indicate the presence of the hotspot + + QList spots = _filterChain->hotSpots(); + foreach(Filter::HotSpot* spot, spots) { + QRegion region; + if (_underlineLinks && spot->type() == Filter::HotSpot::Link) { + QRect r; + if (spot->startLine() == spot->endLine()) { + r.setCoords(spot->startColumn()*_fontWidth + _contentRect.left(), + spot->startLine()*_fontHeight + _contentRect.top(), + (spot->endColumn())*_fontWidth + _contentRect.left() - 1, + (spot->endLine() + 1)*_fontHeight + _contentRect.top() - 1); + region |= r; + } else { + r.setCoords(spot->startColumn()*_fontWidth + _contentRect.left(), + spot->startLine()*_fontHeight + _contentRect.top(), + (_columns)*_fontWidth + _contentRect.left() - 1, + (spot->startLine() + 1)*_fontHeight + _contentRect.top() - 1); + region |= r; + for (int line = spot->startLine() + 1 ; line < spot->endLine() ; line++) { + r.setCoords(0 * _fontWidth + _contentRect.left(), + line * _fontHeight + _contentRect.top(), + (_columns)*_fontWidth + _contentRect.left() - 1, + (line + 1)*_fontHeight + _contentRect.top() - 1); + region |= r; + } + r.setCoords(0 * _fontWidth + _contentRect.left(), + spot->endLine()*_fontHeight + _contentRect.top(), + (spot->endColumn())*_fontWidth + _contentRect.left() - 1, + (spot->endLine() + 1)*_fontHeight + _contentRect.top() - 1); + region |= r; + } + } + + for (int line = spot->startLine() ; line <= spot->endLine() ; line++) { + int startColumn = 0; + int endColumn = _columns - 1; // TODO use number of _columns which are actually + // occupied on this line rather than the width of the + // display in _columns + + // Check image size so _image[] is valid (see makeImage) + if (loc(endColumn, line) > _imageSize) + break; + + // ignore whitespace at the end of the lines + while (_image[loc(endColumn, line)].isSpace() && endColumn > 0) + endColumn--; + + // increment here because the column which we want to set 'endColumn' to + // is the first whitespace character at the end of the line + endColumn++; + + if (line == spot->startLine()) + startColumn = spot->startColumn(); + if (line == spot->endLine()) + endColumn = spot->endColumn(); + + // TODO: resolve this comment with the new margin/center code + // subtract one pixel from + // the right and bottom so that + // we do not overdraw adjacent + // hotspots + // + // subtracting one pixel from all sides also prevents an edge case where + // moving the mouse outside a link could still leave it underlined + // because the check below for the position of the cursor + // finds it on the border of the target area + QRect r; + r.setCoords(startColumn * _fontWidth + _contentRect.left(), + line * _fontHeight + _contentRect.top(), + endColumn * _fontWidth + _contentRect.left() - 1, + (line + 1)*_fontHeight + _contentRect.top() - 1); + // Underline link hotspots + if (_underlineLinks && spot->type() == Filter::HotSpot::Link) { + QFontMetrics metrics(font()); + + // find the baseline (which is the invisible line that the characters in the font sit on, + // with some having tails dangling below) + const int baseline = r.bottom() - metrics.descent(); + // find the position of the underline below that + const int underlinePos = baseline + metrics.underlinePos(); + if (region.contains(mapFromGlobal(QCursor::pos()))) { + painter.drawLine(r.left() , underlinePos , + r.right() , underlinePos); + } + // Marker hotspots simply have a transparent rectangular shape + // drawn on top of them + } else if (spot->type() == Filter::HotSpot::Marker) { + //TODO - Do not use a hardcoded color for this + const bool isCurrentResultLine = (_screenWindow->currentResultLine() == (spot->startLine() + _screenWindow->currentLine())); + QColor color = isCurrentResultLine ? QColor(255, 255, 0, 120) : QColor(255, 0, 0, 120); + painter.fillRect(r, color); + } + } + } +} +void TerminalDisplay::drawContents(QPainter& paint, const QRect& rect) +{ + const QPoint tL = contentsRect().topLeft(); + const int tLx = tL.x(); + const int tLy = tL.y(); + + const int lux = qMin(_usedColumns - 1, qMax(0, (rect.left() - tLx - _contentRect.left()) / _fontWidth)); + const int luy = qMin(_usedLines - 1, qMax(0, (rect.top() - tLy - _contentRect.top()) / _fontHeight)); + const int rlx = qMin(_usedColumns - 1, qMax(0, (rect.right() - tLx - _contentRect.left()) / _fontWidth)); + const int rly = qMin(_usedLines - 1, qMax(0, (rect.bottom() - tLy - _contentRect.top()) / _fontHeight)); + + const int numberOfColumns = _usedColumns; + QString unistr; + unistr.reserve(numberOfColumns); + for (int y = luy; y <= rly; y++) { + int x = lux; + if (!_image[loc(lux, y)].character && x) + x--; // Search for start of multi-column character + for (; x <= rlx; x++) { + int len = 1; + int p = 0; + + // reset our buffer to the number of columns + int bufferSize = numberOfColumns; + unistr.resize(bufferSize); + QChar *disstrU = unistr.data(); + + // is this a single character or a sequence of characters ? + if (_image[loc(x, y)].rendition & RE_EXTENDED_CHAR) { + // sequence of characters + ushort extendedCharLength = 0; + const ushort* chars = ExtendedCharTable::instance.lookupExtendedChar(_image[loc(x, y)].character, extendedCharLength); + if (chars) { + Q_ASSERT(extendedCharLength > 1); + bufferSize += extendedCharLength - 1; + unistr.resize(bufferSize); + disstrU = unistr.data(); + for (int index = 0 ; index < extendedCharLength ; index++) { + Q_ASSERT(p < bufferSize); + disstrU[p++] = chars[index]; + } + } + } else { + // single character + const quint16 c = _image[loc(x, y)].character; + if (c) { + Q_ASSERT(p < bufferSize); + disstrU[p++] = c; //fontMap(c); + } + } + + const bool lineDraw = _image[loc(x, y)].isLineChar(); + const bool doubleWidth = (_image[ qMin(loc(x, y) + 1, _imageSize) ].character == 0); + const CharacterColor currentForeground = _image[loc(x, y)].foregroundColor; + const CharacterColor currentBackground = _image[loc(x, y)].backgroundColor; + const quint8 currentRendition = _image[loc(x, y)].rendition; + + while (x + len <= rlx && + _image[loc(x + len, y)].foregroundColor == currentForeground && + _image[loc(x + len, y)].backgroundColor == currentBackground && + (_image[loc(x + len, y)].rendition & ~RE_EXTENDED_CHAR) == (currentRendition & ~RE_EXTENDED_CHAR) && + (_image[ qMin(loc(x + len, y) + 1, _imageSize) ].character == 0) == doubleWidth && + _image[loc(x + len, y)].isLineChar() == lineDraw) { + const quint16 c = _image[loc(x + len, y)].character; + if (_image[loc(x + len, y)].rendition & RE_EXTENDED_CHAR) { + // sequence of characters + ushort extendedCharLength = 0; + const ushort* chars = ExtendedCharTable::instance.lookupExtendedChar(c, extendedCharLength); + if (chars) { + Q_ASSERT(extendedCharLength > 1); + bufferSize += extendedCharLength - 1; + unistr.resize(bufferSize); + disstrU = unistr.data(); + for (int index = 0 ; index < extendedCharLength ; index++) { + Q_ASSERT(p < bufferSize); + disstrU[p++] = chars[index]; + } + } + } else { + // single character + if (c) { + Q_ASSERT(p < bufferSize); + disstrU[p++] = c; //fontMap(c); + } + } + + if (doubleWidth) // assert((_image[loc(x+len,y)+1].character == 0)), see above if condition + len++; // Skip trailing part of multi-column character + len++; + } + if ((x + len < _usedColumns) && (!_image[loc(x + len, y)].character)) + len++; // Adjust for trailing part of multi-column character + + const bool save__fixedFont = _fixedFont; + if (lineDraw) + _fixedFont = false; + if (doubleWidth) + _fixedFont = false; + unistr.resize(p); + + // Create a text scaling matrix for double width and double height lines. + QMatrix textScale; + + if (y < _lineProperties.size()) { + if (_lineProperties[y] & LINE_DOUBLEWIDTH) + textScale.scale(2, 1); + + if (_lineProperties[y] & LINE_DOUBLEHEIGHT) + textScale.scale(1, 2); + } + + //Apply text scaling matrix. + paint.setWorldMatrix(textScale, true); + + //calculate the area in which the text will be drawn + QRect textArea = QRect(_contentRect.left() + tLx + _fontWidth * x , _contentRect.top() + tLy + _fontHeight * y , _fontWidth * len , _fontHeight); + + //move the calculated area to take account of scaling applied to the painter. + //the position of the area from the origin (0,0) is scaled + //by the opposite of whatever + //transformation has been applied to the painter. this ensures that + //painting does actually start from textArea.topLeft() + //(instead of textArea.topLeft() * painter-scale) + textArea.moveTopLeft(textScale.inverted().map(textArea.topLeft())); + + //paint text fragment + if (_printerFriendly) { + drawPrinterFriendlyTextFragment(paint, + textArea, + unistr, + &_image[loc(x, y)]); + } else { + drawTextFragment(paint, + textArea, + unistr, + &_image[loc(x, y)]); + } + + _fixedFont = save__fixedFont; + + //reset back to single-width, single-height _lines + paint.setWorldMatrix(textScale.inverted(), true); + + if (y < _lineProperties.size() - 1) { + //double-height _lines are represented by two adjacent _lines + //containing the same characters + //both _lines will have the LINE_DOUBLEHEIGHT attribute. + //If the current line has the LINE_DOUBLEHEIGHT attribute, + //we can therefore skip the next line + if (_lineProperties[y] & LINE_DOUBLEHEIGHT) + y++; + } + + x += len - 1; + } + } +} + +void TerminalDisplay::drawCurrentResultRect(QPainter& painter) +{ + if(_screenWindow->currentResultLine() == -1) { + return; + } + + QRect r(0, (_screenWindow->currentResultLine() - _screenWindow->currentLine())*_fontHeight, + contentsRect().width(), _fontHeight); + painter.fillRect(r, QColor(0, 0, 255, 80)); +} + +QRect TerminalDisplay::imageToWidget(const QRect& imageArea) const +{ + QRect result; + result.setLeft(_contentRect.left() + _fontWidth * imageArea.left()); + result.setTop(_contentRect.top() + _fontHeight * imageArea.top()); + result.setWidth(_fontWidth * imageArea.width()); + result.setHeight(_fontHeight * imageArea.height()); + + return result; +} + +/* ------------------------------------------------------------------------- */ +/* */ +/* Blinking Text & Cursor */ +/* */ +/* ------------------------------------------------------------------------- */ + +void TerminalDisplay::setBlinkingCursorEnabled(bool blink) +{ + _allowBlinkingCursor = blink; + + if (!hasFocus()) + return; + + if (blink && !_blinkCursorTimer->isActive()) + _blinkCursorTimer->start(); + + if (!blink && _blinkCursorTimer->isActive()) { + _blinkCursorTimer->stop(); + if (_cursorBlinking) { + // if cursor is blinking(hidden), blink it again to make it show + blinkCursorEvent(); + } + Q_ASSERT(_cursorBlinking == false); + } +} + +void TerminalDisplay::setBlinkingTextEnabled(bool blink) +{ + _allowBlinkingText = blink; + + if (blink && !_blinkTextTimer->isActive()) + _blinkTextTimer->start(); + + if (!blink && _blinkTextTimer->isActive()) { + _blinkTextTimer->stop(); + _textBlinking = false; + } +} + +void TerminalDisplay::focusOutEvent(QFocusEvent*) +{ + // trigger a repaint of the cursor so that it is both: + // + // * visible (in case it was hidden during blinking) + // * drawn in a focused out state + _cursorBlinking = false; + updateCursor(); + + // suppress further cursor blinking + _blinkCursorTimer->stop(); + Q_ASSERT(_cursorBlinking == false); + + // if text is blinking (hidden), blink it again to make it shown + if (_textBlinking) + blinkTextEvent(); + + // suppress further text blinking + _blinkTextTimer->stop(); + Q_ASSERT(_textBlinking == false); +} + +void TerminalDisplay::focusInEvent(QFocusEvent*) +{ + if (_allowBlinkingCursor) + _blinkCursorTimer->start(); + + updateCursor(); + + if (_allowBlinkingText && _hasTextBlinker) + _blinkTextTimer->start(); +} + +void TerminalDisplay::blinkTextEvent() +{ + Q_ASSERT(_allowBlinkingText); + + _textBlinking = !_textBlinking; + + // TODO: Optimize to only repaint the areas of the widget where there is + // blinking text rather than repainting the whole widget. + update(); +} + +void TerminalDisplay::blinkCursorEvent() +{ + Q_ASSERT(_allowBlinkingCursor); + + _cursorBlinking = !_cursorBlinking; + updateCursor(); +} + +void TerminalDisplay::updateCursor() +{ + QRect cursorRect = imageToWidget(QRect(cursorPosition(), QSize(1, 1))); + update(cursorRect); +} + +/* ------------------------------------------------------------------------- */ +/* */ +/* Geometry & Resizing */ +/* */ +/* ------------------------------------------------------------------------- */ + +void TerminalDisplay::resizeEvent(QResizeEvent*) +{ + updateImageSize(); +} + +void TerminalDisplay::propagateSize() +{ + if (_image) + updateImageSize(); +} + +void TerminalDisplay::updateImageSize() +{ + Character* oldImage = _image; + const int oldLines = _lines; + const int oldColumns = _columns; + + makeImage(); + + if (oldImage) { + // copy the old image to reduce flicker + int lines = qMin(oldLines, _lines); + int columns = qMin(oldColumns, _columns); + for (int line = 0; line < lines; line++) { + memcpy((void*)&_image[_columns * line], + (void*)&oldImage[oldColumns * line], + columns * sizeof(Character)); + } + delete[] oldImage; + } + + if (_screenWindow) + _screenWindow->setWindowLines(_lines); + + _wallpaper->setSize(_contentRect.size()); + + _resizing = (oldLines != _lines) || (oldColumns != _columns); + + if (_resizing) { + showResizeNotification(); + emit changedContentSizeSignal(_contentRect.height(), _contentRect.width()); // expose resizeEvent + } + + _resizing = false; +} + +void TerminalDisplay::makeImage() +{ + _wallpaper->load(); + + calcGeometry(); + + // confirm that array will be of non-zero size, since the painting code + // assumes a non-zero array length + Q_ASSERT(_lines > 0 && _columns > 0); + Q_ASSERT(_usedLines <= _lines && _usedColumns <= _columns); + + _imageSize = _lines * _columns; + + // We over-commit one character so that we can be more relaxed in dealing with + // certain boundary conditions: _image[_imageSize] is a valid but unused position + _image = new Character[_imageSize + 1]; + + clearImage(); +} + +void TerminalDisplay::clearImage() +{ + for (int i = 0; i <= _imageSize; ++i) + _image[i] = Screen::DefaultChar; +} + +void TerminalDisplay::calcGeometry() +{ + _scrollBar->resize(_scrollBar->sizeHint().width(), contentsRect().height()); + _contentRect = contentsRect().adjusted(_margin, _margin, -_margin, -_margin); + + switch (_scrollbarLocation) { + case Enum::ScrollBarHidden : + break; + case Enum::ScrollBarLeft : + _contentRect.setLeft(_contentRect.left() + _scrollBar->width()); + _scrollBar->move(contentsRect().topLeft()); + break; + case Enum::ScrollBarRight: + _contentRect.setRight(_contentRect.right() - _scrollBar->width()); + _scrollBar->move(contentsRect().topRight() - QPoint(_scrollBar->width() - 1, 0)); + break; + } + + // ensure that display is always at least one column wide + _columns = qMax(1, _contentRect.width() / _fontWidth); + _usedColumns = qMin(_usedColumns, _columns); + + // ensure that display is always at least one line high + _lines = qMax(1, _contentRect.height() / _fontHeight); + _usedLines = qMin(_usedLines, _lines); + + if(_centerContents) { + QSize unusedPixels = _contentRect.size() - QSize(_columns * _fontWidth, _lines * _fontHeight); + _contentRect.adjust(unusedPixels.width() / 2, unusedPixels.height() / 2, 0, 0); + } +} + +// calculate the needed size, this must be synced with calcGeometry() +void TerminalDisplay::setSize(int columns, int lines) +{ + const int scrollBarWidth = _scrollBar->isHidden() ? 0 : _scrollBar->sizeHint().width(); + const int horizontalMargin = _margin * 2; + const int verticalMargin = _margin * 2; + + QSize newSize = QSize(horizontalMargin + scrollBarWidth + (columns * _fontWidth) , + verticalMargin + (lines * _fontHeight)); + + if (newSize != size()) { + _size = newSize; + updateGeometry(); + } +} + +QSize TerminalDisplay::sizeHint() const +{ + return _size; +} + +//showEvent and hideEvent are reimplemented here so that it appears to other classes that the +//display has been resized when the display is hidden or shown. +// +//TODO: Perhaps it would be better to have separate signals for show and hide instead of using +//the same signal as the one for a content size change +void TerminalDisplay::showEvent(QShowEvent*) +{ + emit changedContentSizeSignal(_contentRect.height(), _contentRect.width()); +} +void TerminalDisplay::hideEvent(QHideEvent*) +{ + emit changedContentSizeSignal(_contentRect.height(), _contentRect.width()); +} + +void TerminalDisplay::setMargin(int margin) +{ + _margin = margin; + updateImageSize(); +} + +void TerminalDisplay::setCenterContents(bool enable) +{ + _centerContents = enable; + calcGeometry(); + update(); +} + +/* ------------------------------------------------------------------------- */ +/* */ +/* Scrollbar */ +/* */ +/* ------------------------------------------------------------------------- */ + +void TerminalDisplay::setScrollBarPosition(Enum::ScrollBarPositionEnum position) +{ + if (_scrollbarLocation == position) + return; + + if (position == Enum::ScrollBarHidden) + _scrollBar->hide(); + else + _scrollBar->show(); + + _scrollbarLocation = position; + + propagateSize(); + update(); +} + +void TerminalDisplay::scrollBarPositionChanged(int) +{ + if (!_screenWindow) + return; + + _screenWindow->scrollTo(_scrollBar->value()); + + // if the thumb has been moved to the bottom of the _scrollBar then set + // the display to automatically track new output, + // that is, scroll down automatically + // to how new _lines as they are added + const bool atEndOfOutput = (_scrollBar->value() == _scrollBar->maximum()); + _screenWindow->setTrackOutput(atEndOfOutput); + + updateImage(); +} + +void TerminalDisplay::setScroll(int cursor, int slines) +{ + // update _scrollBar if the range or value has changed, + // otherwise return + // + // setting the range or value of a _scrollBar will always trigger + // a repaint, so it should be avoided if it is not necessary + if (_scrollBar->minimum() == 0 && + _scrollBar->maximum() == (slines - _lines) && + _scrollBar->value() == cursor) { + return; + } + + disconnect(_scrollBar, SIGNAL(valueChanged(int)), this, SLOT(scrollBarPositionChanged(int))); + _scrollBar->setRange(0, slines - _lines); + _scrollBar->setSingleStep(1); + _scrollBar->setPageStep(_lines); + _scrollBar->setValue(cursor); + connect(_scrollBar, SIGNAL(valueChanged(int)), this, SLOT(scrollBarPositionChanged(int))); +} + +void TerminalDisplay::setScrollFullPage(bool fullPage) +{ + _scrollFullPage = fullPage; +} + +bool TerminalDisplay::scrollFullPage() const +{ + return _scrollFullPage; +} + +/* ------------------------------------------------------------------------- */ +/* */ +/* Mouse */ +/* */ +/* ------------------------------------------------------------------------- */ +void TerminalDisplay::mousePressEvent(QMouseEvent* ev) +{ + if (_possibleTripleClick && (ev->button() == Qt::LeftButton)) { + mouseTripleClickEvent(ev); + return; + } + + if (!contentsRect().contains(ev->pos())) return; + + if (!_screenWindow) return; + + int charLine; + int charColumn; + getCharacterPosition(ev->pos(), charLine, charColumn); + QPoint pos = QPoint(charColumn, charLine); + + if (ev->button() == Qt::LeftButton) { + // request the software keyboard, if any + if (qApp->autoSipEnabled()) { + QStyle::RequestSoftwareInputPanel behavior = QStyle::RequestSoftwareInputPanel( + style()->styleHint(QStyle::SH_RequestSoftwareInputPanel)); + if (hasFocus() || behavior == QStyle::RSIP_OnMouseClick) { + QEvent event(QEvent::RequestSoftwareInputPanel); + QApplication::sendEvent(this, &event); + } + } + + _lineSelectionMode = false; + _wordSelectionMode = false; + + // The user clicked inside selected text + bool selected = _screenWindow->isSelected(pos.x(), pos.y()); + + // Drag only when the Control key is held + if ((!_ctrlRequiredForDrag || ev->modifiers() & Qt::ControlModifier) && selected) { + _dragInfo.state = diPending; + _dragInfo.start = ev->pos(); + } else { + // No reason to ever start a drag event + _dragInfo.state = diNone; + + _preserveLineBreaks = !((ev->modifiers() & Qt::ControlModifier) && !(ev->modifiers() & Qt::AltModifier)); + _columnSelectionMode = (ev->modifiers() & Qt::AltModifier) && (ev->modifiers() & Qt::ControlModifier); + + if (_mouseMarks || (ev->modifiers() == Qt::ShiftModifier)) { + // Only extend selection for programs not interested in mouse + if (_mouseMarks && (ev->modifiers() == Qt::ShiftModifier)) { + extendSelection(ev->pos()); + } else { + _screenWindow->clearSelection(); + + pos.ry() += _scrollBar->value(); + _iPntSel = _pntSel = pos; + _actSel = 1; // left mouse button pressed but nothing selected yet. + } + } else { + emit mouseSignal(0, charColumn + 1, charLine + 1 + _scrollBar->value() - _scrollBar->maximum() , 0); + } + + if (_underlineLinks && (_openLinksByDirectClick || (ev->modifiers() & Qt::ControlModifier))) { + Filter::HotSpot* spot = _filterChain->hotSpotAt(charLine, charColumn); + if (spot && spot->type() == Filter::HotSpot::Link) { + QObject action; + action.setObjectName("open-action"); + spot->activate(&action); + } + } + } + } else if (ev->button() == Qt::MiddleButton) { + processMidButtonClick(ev); + } else if (ev->button() == Qt::RightButton) { + if (_mouseMarks || (ev->modifiers() & Qt::ShiftModifier)) + emit configureRequest(ev->pos()); + else + emit mouseSignal(2, charColumn + 1, charLine + 1 + _scrollBar->value() - _scrollBar->maximum() , 0); + } +} + +QList TerminalDisplay::filterActions(const QPoint& position) +{ + int charLine, charColumn; + getCharacterPosition(position, charLine, charColumn); + + Filter::HotSpot* spot = _filterChain->hotSpotAt(charLine, charColumn); + + return spot ? spot->actions() : QList(); +} + +void TerminalDisplay::mouseMoveEvent(QMouseEvent* ev) +{ + int charLine = 0; + int charColumn = 0; + getCharacterPosition(ev->pos(), charLine, charColumn); + + // handle filters + // change link hot-spot appearance on mouse-over + Filter::HotSpot* spot = _filterChain->hotSpotAt(charLine, charColumn); + if (spot && spot->type() == Filter::HotSpot::Link) { + if (_underlineLinks) { + QRegion previousHotspotArea = _mouseOverHotspotArea; + _mouseOverHotspotArea = QRegion(); + QRect r; + if (spot->startLine() == spot->endLine()) { + r.setCoords(spot->startColumn()*_fontWidth + _contentRect.left(), + spot->startLine()*_fontHeight + _contentRect.top(), + (spot->endColumn())*_fontWidth + _contentRect.left() - 1, + (spot->endLine() + 1)*_fontHeight + _contentRect.top() - 1); + _mouseOverHotspotArea |= r; + } else { + r.setCoords(spot->startColumn()*_fontWidth + _contentRect.left(), + spot->startLine()*_fontHeight + _contentRect.top(), + (_columns)*_fontWidth + _contentRect.left() - 1, + (spot->startLine() + 1)*_fontHeight + _contentRect.top() - 1); + _mouseOverHotspotArea |= r; + for (int line = spot->startLine() + 1 ; line < spot->endLine() ; line++) { + r.setCoords(0 * _fontWidth + _contentRect.left(), + line * _fontHeight + _contentRect.top(), + (_columns)*_fontWidth + _contentRect.left() - 1, + (line + 1)*_fontHeight + _contentRect.top() - 1); + _mouseOverHotspotArea |= r; + } + r.setCoords(0 * _fontWidth + _contentRect.left(), + spot->endLine()*_fontHeight + _contentRect.top(), + (spot->endColumn())*_fontWidth + _contentRect.left() - 1, + (spot->endLine() + 1)*_fontHeight + _contentRect.top() - 1); + _mouseOverHotspotArea |= r; + } + + if ((_openLinksByDirectClick || (ev->modifiers() & Qt::ControlModifier)) && (cursor().shape() != Qt::PointingHandCursor)) + setCursor(Qt::PointingHandCursor); + + update(_mouseOverHotspotArea | previousHotspotArea); + } + } else if (!_mouseOverHotspotArea.isEmpty()) { + if ((_underlineLinks && (_openLinksByDirectClick || (ev->modifiers() & Qt::ControlModifier))) || (cursor().shape() == Qt::PointingHandCursor)) + setCursor(_mouseMarks ? Qt::IBeamCursor : Qt::ArrowCursor); + + update(_mouseOverHotspotArea); + // set hotspot area to an invalid rectangle + _mouseOverHotspotArea = QRegion(); + } + + // for auto-hiding the cursor, we need mouseTracking + if (ev->buttons() == Qt::NoButton) return; + + // if the terminal is interested in mouse movements + // then emit a mouse movement signal, unless the shift + // key is being held down, which overrides this. + if (!_mouseMarks && !(ev->modifiers() & Qt::ShiftModifier)) { + int button = 3; + if (ev->buttons() & Qt::LeftButton) + button = 0; + if (ev->buttons() & Qt::MiddleButton) + button = 1; + if (ev->buttons() & Qt::RightButton) + button = 2; + + emit mouseSignal(button, + charColumn + 1, + charLine + 1 + _scrollBar->value() - _scrollBar->maximum(), + 1); + + return; + } + + if (_dragInfo.state == diPending) { + // we had a mouse down, but haven't confirmed a drag yet + // if the mouse has moved sufficiently, we will confirm + + const int distance = KGlobalSettings::dndEventDelay(); + if (ev->x() > _dragInfo.start.x() + distance || ev->x() < _dragInfo.start.x() - distance || + ev->y() > _dragInfo.start.y() + distance || ev->y() < _dragInfo.start.y() - distance) { + // we've left the drag square, we can start a real drag operation now + + _screenWindow->clearSelection(); + doDrag(); + } + return; + } else if (_dragInfo.state == diDragging) { + // this isn't technically needed because mouseMoveEvent is suppressed during + // Qt drag operations, replaced by dragMoveEvent + return; + } + + if (_actSel == 0) return; + +// don't extend selection while pasting + if (ev->buttons() & Qt::MiddleButton) return; + + extendSelection(ev->pos()); +} + +void TerminalDisplay::leaveEvent(QEvent *) +{ + // remove underline from an active link when cursor leaves the widget area + if(!_mouseOverHotspotArea.isEmpty()) { + update(_mouseOverHotspotArea); + _mouseOverHotspotArea = QRegion(); + } +} + +void TerminalDisplay::extendSelection(const QPoint& position) +{ + if (!_screenWindow) + return; + + //if ( !contentsRect().contains(ev->pos()) ) return; + const QPoint tL = contentsRect().topLeft(); + const int tLx = tL.x(); + const int tLy = tL.y(); + const int scroll = _scrollBar->value(); + + // we're in the process of moving the mouse with the left button pressed + // the mouse cursor will kept caught within the bounds of the text in + // this widget. + + int linesBeyondWidget = 0; + + QRect textBounds(tLx + _contentRect.left(), + tLy + _contentRect.top(), + _usedColumns * _fontWidth - 1, + _usedLines * _fontHeight - 1); + + QPoint pos = position; + + // Adjust position within text area bounds. + const QPoint oldpos = pos; + + pos.setX(qBound(textBounds.left(), pos.x(), textBounds.right())); + pos.setY(qBound(textBounds.top(), pos.y(), textBounds.bottom())); + + if (oldpos.y() > textBounds.bottom()) { + linesBeyondWidget = (oldpos.y() - textBounds.bottom()) / _fontHeight; + _scrollBar->setValue(_scrollBar->value() + linesBeyondWidget + 1); // scrollforward + } + if (oldpos.y() < textBounds.top()) { + linesBeyondWidget = (textBounds.top() - oldpos.y()) / _fontHeight; + _scrollBar->setValue(_scrollBar->value() - linesBeyondWidget - 1); // history + } + + int charColumn = 0; + int charLine = 0; + getCharacterPosition(pos, charLine, charColumn); + + QPoint here = QPoint(charColumn, charLine); + QPoint ohere; + QPoint _iPntSelCorr = _iPntSel; + _iPntSelCorr.ry() -= _scrollBar->value(); + QPoint _pntSelCorr = _pntSel; + _pntSelCorr.ry() -= _scrollBar->value(); + bool swapping = false; + + if (_wordSelectionMode) { + // Extend to word boundaries + const bool left_not_right = (here.y() < _iPntSelCorr.y() || + (here.y() == _iPntSelCorr.y() && here.x() < _iPntSelCorr.x())); + const bool old_left_not_right = (_pntSelCorr.y() < _iPntSelCorr.y() || + (_pntSelCorr.y() == _iPntSelCorr.y() && _pntSelCorr.x() < _iPntSelCorr.x())); + swapping = left_not_right != old_left_not_right; + + if (left_not_right) { + ohere = findWordEnd(_iPntSelCorr); + here = findWordStart(here); + } else { + ohere = findWordStart(_iPntSelCorr); + here = findWordEnd(here); + } + ohere.rx()++; + } + + if (_lineSelectionMode) { + // Extend to complete line + const bool above_not_below = (here.y() < _iPntSelCorr.y()); + if (above_not_below) { + ohere = findLineEnd(_iPntSelCorr); + here = findLineStart(here); + } else { + ohere = findLineStart(_iPntSelCorr); + here = findLineEnd(here); + } + + swapping = !(_tripleSelBegin == ohere); + _tripleSelBegin = ohere; + + ohere.rx()++; + } + + int offset = 0; + if (!_wordSelectionMode && !_lineSelectionMode) { + QChar selClass; + + const bool left_not_right = (here.y() < _iPntSelCorr.y() || + (here.y() == _iPntSelCorr.y() && here.x() < _iPntSelCorr.x())); + const bool old_left_not_right = (_pntSelCorr.y() < _iPntSelCorr.y() || + (_pntSelCorr.y() == _iPntSelCorr.y() && _pntSelCorr.x() < _iPntSelCorr.x())); + swapping = left_not_right != old_left_not_right; + + // Find left (left_not_right ? from here : from start) + const QPoint left = left_not_right ? here : _iPntSelCorr; + + // Find left (left_not_right ? from start : from here) + QPoint right = left_not_right ? _iPntSelCorr : here; + if (right.x() > 0 && !_columnSelectionMode) { + int i = loc(right.x(), right.y()); + if (i >= 0 && i <= _imageSize) { + selClass = charClass(_image[i - 1]); + /* if (selClass == ' ') + { + while ( right.x() < _usedColumns-1 && charClass(_image[i+1].character) == selClass && (right.y()<_usedLines-1) && + !(_lineProperties[right.y()] & LINE_WRAPPED)) + { i++; right.rx()++; } + if (right.x() < _usedColumns-1) + right = left_not_right ? _iPntSelCorr : here; + else + right.rx()++; // will be balanced later because of offset=-1; + }*/ + } + } + + // Pick which is start (ohere) and which is extension (here) + if (left_not_right) { + here = left; + ohere = right; + offset = 0; + } else { + here = right; + ohere = left; + offset = -1; + } + } + + if ((here == _pntSelCorr) && (scroll == _scrollBar->value())) return; // not moved + + if (here == ohere) return; // It's not left, it's not right. + + if (_actSel < 2 || swapping) { + if (_columnSelectionMode && !_lineSelectionMode && !_wordSelectionMode) { + _screenWindow->setSelectionStart(ohere.x() , ohere.y() , true); + } else { + _screenWindow->setSelectionStart(ohere.x() - 1 - offset , ohere.y() , false); + } + } + + _actSel = 2; // within selection + _pntSel = here; + _pntSel.ry() += _scrollBar->value(); + + if (_columnSelectionMode && !_lineSelectionMode && !_wordSelectionMode) { + _screenWindow->setSelectionEnd(here.x() , here.y()); + } else { + _screenWindow->setSelectionEnd(here.x() + offset , here.y()); + } +} + +void TerminalDisplay::mouseReleaseEvent(QMouseEvent* ev) +{ + if (!_screenWindow) + return; + + int charLine; + int charColumn; + getCharacterPosition(ev->pos(), charLine, charColumn); + + if (ev->button() == Qt::LeftButton) { + if (_dragInfo.state == diPending) { + // We had a drag event pending but never confirmed. Kill selection + _screenWindow->clearSelection(); + } else { + if (_actSel > 1) { + copyToX11Selection(); + } + + _actSel = 0; + + //FIXME: emits a release event even if the mouse is + // outside the range. The procedure used in `mouseMoveEvent' + // applies here, too. + + if (!_mouseMarks && !(ev->modifiers() & Qt::ShiftModifier)) + emit mouseSignal(0, + charColumn + 1, + charLine + 1 + _scrollBar->value() - _scrollBar->maximum() , 2); + } + _dragInfo.state = diNone; + } + + if (!_mouseMarks && + (ev->button() == Qt::RightButton || ev->button() == Qt::MiddleButton) && + !(ev->modifiers() & Qt::ShiftModifier)) { + emit mouseSignal(ev->button() == Qt::MiddleButton ? 1 : 2, + charColumn + 1, + charLine + 1 + _scrollBar->value() - _scrollBar->maximum() , + 2); + } +} + +void TerminalDisplay::getCharacterPosition(const QPoint& widgetPoint, int& line, int& column) const +{ + column = (widgetPoint.x() + _fontWidth / 2 - contentsRect().left() - _contentRect.left()) / _fontWidth; + line = (widgetPoint.y() - contentsRect().top() - _contentRect.top()) / _fontHeight; + + if (line < 0) + line = 0; + if (column < 0) + column = 0; + + if (line >= _usedLines) + line = _usedLines - 1; + + // the column value returned can be equal to _usedColumns, which + // is the position just after the last character displayed in a line. + // + // this is required so that the user can select characters in the right-most + // column (or left-most for right-to-left input) + if (column > _usedColumns) + column = _usedColumns; +} + +void TerminalDisplay::updateLineProperties() +{ + if (!_screenWindow) + return; + + _lineProperties = _screenWindow->getLineProperties(); +} + +void TerminalDisplay::processMidButtonClick(QMouseEvent* ev) +{ + if (_mouseMarks || (ev->modifiers() & Qt::ShiftModifier)) { + const bool appendEnter = ev->modifiers() & Qt::ControlModifier; + + if (_middleClickPasteMode == Enum::PasteFromX11Selection) { + pasteFromX11Selection(appendEnter); + } else if (_middleClickPasteMode == Enum::PasteFromClipboard) { + pasteFromClipboard(appendEnter); + } else { + Q_ASSERT(false); + } + } else { + int charLine = 0; + int charColumn = 0; + getCharacterPosition(ev->pos(), charLine, charColumn); + + emit mouseSignal(1, charColumn + 1, charLine + 1 + _scrollBar->value() - _scrollBar->maximum() , 0); + } +} + +void TerminalDisplay::mouseDoubleClickEvent(QMouseEvent* ev) +{ + // Yes, successive middle click can trigger this event + if (ev->button() == Qt::MiddleButton) { + processMidButtonClick(ev); + return; + } + + if (ev->button() != Qt::LeftButton) return; + if (!_screenWindow) return; + + int charLine = 0; + int charColumn = 0; + + getCharacterPosition(ev->pos(), charLine, charColumn); + + // pass on double click as two clicks. + if (!_mouseMarks && !(ev->modifiers() & Qt::ShiftModifier)) { + // Send just _ONE_ click event, since the first click of the double click + // was already sent by the click handler + emit mouseSignal(0, charColumn + 1, + charLine + 1 + _scrollBar->value() - _scrollBar->maximum(), + 0); // left button + return; + } + + _screenWindow->clearSelection(); + + _wordSelectionMode = true; + _actSel = 2; // within selection + + _iPntSel = QPoint(charColumn, charLine); + const QPoint bgnSel = findWordStart(_iPntSel); + const QPoint endSel = findWordEnd(_iPntSel); + _iPntSel.ry() += _scrollBar->value(); + + _screenWindow->setSelectionStart(bgnSel.x() , bgnSel.y() , false); + _screenWindow->setSelectionEnd(endSel.x() , endSel.y()); + copyToX11Selection(); + + _possibleTripleClick = true; + + QTimer::singleShot(QApplication::doubleClickInterval(), this, + SLOT(tripleClickTimeout())); +} + +void TerminalDisplay::wheelEvent(QWheelEvent* ev) +{ + // Only vertical scrolling is supported + if (ev->orientation() != Qt::Vertical) + return; + + const int modifiers = ev->modifiers(); + const int delta = ev->delta(); + + // ctrl+ for zooming, like in konqueror and firefox + if ((modifiers & Qt::ControlModifier) && mouseWheelZoom()) { + if (delta > 0) { + // wheel-up for increasing font size + increaseFontSize(); + } else { + // wheel-down for decreasing font size + decreaseFontSize(); + } + + return; + } + + // if the terminal program is not interested with mouse events: + // * send the event to the scrollbar if the slider has room to move + // * otherwise, send simulated up / down key presses to the terminal program + // for the benefit of programs such as 'less' + if (_mouseMarks) { + const bool canScroll = _scrollBar->maximum() > 0; + if (canScroll) { + _scrollBar->event(ev); + _sessionController->setSearchStartToWindowCurrentLine(); + } else { + // assume that each Up / Down key event will cause the terminal application + // to scroll by one line. + // + // to get a reasonable scrolling speed, scroll by one line for every 5 degrees + // of mouse wheel rotation. Mouse wheels typically move in steps of 15 degrees, + // giving a scroll of 3 lines + const int keyCode = delta > 0 ? Qt::Key_Up : Qt::Key_Down; + QKeyEvent keyEvent(QEvent::KeyPress, keyCode, Qt::NoModifier); + + // QWheelEvent::delta() gives rotation in eighths of a degree + const int degrees = delta / 8; + const int lines = abs(degrees) / 5; + + for (int i = 0; i < lines; i++) + emit keyPressedSignal(&keyEvent); + } + } else { + // terminal program wants notification of mouse activity + + int charLine; + int charColumn; + getCharacterPosition(ev->pos() , charLine , charColumn); + + emit mouseSignal(delta > 0 ? 4 : 5, + charColumn + 1, + charLine + 1 + _scrollBar->value() - _scrollBar->maximum() , + 0); + } +} + +void TerminalDisplay::tripleClickTimeout() +{ + _possibleTripleClick = false; +} + +void TerminalDisplay::viewScrolledByUser() +{ + _sessionController->setSearchStartToWindowCurrentLine(); +} + +/* Moving left/up from the line containing pnt, return the starting + offset point which the given line is continiously wrapped + (top left corner = 0,0; previous line not visible = 0,-1). +*/ +QPoint TerminalDisplay::findLineStart(const QPoint &pnt) +{ + const int visibleScreenLines = _lineProperties.size(); + const int topVisibleLine = _screenWindow->currentLine(); + Screen *screen = _screenWindow->screen(); + int line = pnt.y(); + int lineInHistory= line + topVisibleLine; + + QVector lineProperties = _lineProperties; + + while (lineInHistory > 0) { + for (; line > 0; line--, lineInHistory--) { + // Does previous line wrap around? + if (!(lineProperties[line - 1] & LINE_WRAPPED)) { + return QPoint(0, lineInHistory - topVisibleLine); + } + } + + if (lineInHistory < 1) + break; + + // _lineProperties is only for the visible screen, so grab new data + int newRegionStart = qMax(0, lineInHistory - visibleScreenLines); + lineProperties = screen->getLineProperties(newRegionStart, lineInHistory - 1); + line = lineInHistory - newRegionStart; + } + return QPoint(0, lineInHistory - topVisibleLine); +} + +/* Moving right/down from the line containing pnt, return the ending + offset point which the given line is continiously wrapped. +*/ +QPoint TerminalDisplay::findLineEnd(const QPoint &pnt) +{ + const int visibleScreenLines = _lineProperties.size(); + const int topVisibleLine = _screenWindow->currentLine(); + const int maxY = _screenWindow->lineCount() - 1; + Screen *screen = _screenWindow->screen(); + int line = pnt.y(); + int lineInHistory= line + topVisibleLine; + + QVector lineProperties = _lineProperties; + + while (lineInHistory < maxY) { + for (; line < lineProperties.count() && lineInHistory < maxY; line++, lineInHistory++) { + // Does current line wrap around? + if (!(lineProperties[line] & LINE_WRAPPED)) { + return QPoint(_columns - 1, lineInHistory - topVisibleLine); + } + } + + line = 0; + lineProperties = screen->getLineProperties(lineInHistory, qMin(lineInHistory + visibleScreenLines, maxY)); + } + return QPoint(_columns - 1, lineInHistory - topVisibleLine); +} + +QPoint TerminalDisplay::findWordStart(const QPoint &pnt) +{ + const int regSize = qMax(_screenWindow->windowLines(), 10); + const int curLine = _screenWindow->currentLine(); + int i = pnt.y(); + int x = pnt.x(); + int y = i + curLine; + int j = loc(x, i); + QVector lineProperties = _lineProperties; + Screen *screen = _screenWindow->screen(); + Character *image = _image; + Character *tmp_image = NULL; + const QChar selClass = charClass(image[j]); + const int imageSize = regSize * _columns; + + while (true) { + for (;;j--, x--) { + if (x > 0) { + if (charClass(image[j - 1]) == selClass) + continue; + goto out; + } else if (i > 0) { + if (lineProperties[i - 1] & LINE_WRAPPED && + charClass(image[j - 1]) == selClass) { + x = _columns; + i--; + y--; + continue; + } + goto out; + } else if (y > 0) { + break; + } else { + goto out; + } + } + int newRegStart = qMax(0, y - regSize); + lineProperties = screen->getLineProperties(newRegStart, y - 1); + i = y - newRegStart; + if (!tmp_image) { + tmp_image = new Character[imageSize]; + image = tmp_image; + } + screen->getImage(tmp_image, imageSize, newRegStart, y - 1); + j = loc(x, i); + } +out: + if (tmp_image) { + delete[] tmp_image; + } + return QPoint(x, y - curLine); +} + +QPoint TerminalDisplay::findWordEnd(const QPoint &pnt) +{ + const int regSize = qMax(_screenWindow->windowLines(), 10); + const int curLine = _screenWindow->currentLine(); + int i = pnt.y(); + int x = pnt.x(); + int y = i + curLine; + int j = loc(x, i); + QVector lineProperties = _lineProperties; + Screen *screen = _screenWindow->screen(); + Character *image = _image; + Character *tmp_image = NULL; + const QChar selClass = charClass(image[j]); + const int imageSize = regSize * _columns; + const int maxY = _screenWindow->lineCount() - 1; + const int maxX = _columns - 1; + + while (true) { + const int lineCount = lineProperties.count(); + for (;;j++, x++) { + if (x < maxX) { + if (charClass(image[j + 1]) == selClass) + continue; + goto out; + } else if (i < lineCount - 1) { + if (lineProperties[i] & LINE_WRAPPED && + charClass(image[j + 1]) == selClass) { + x = -1; + i++; + y++; + continue; + } + goto out; + } else if (y < maxY) { + if (i < lineCount && !(lineProperties[i] & LINE_WRAPPED)) + goto out; + break; + } else { + goto out; + } + } + int newRegEnd = qMin(y + regSize - 1, maxY); + lineProperties = screen->getLineProperties(y, newRegEnd); + i = 0; + if (!tmp_image) { + tmp_image = new Character[imageSize]; + image = tmp_image; + } + screen->getImage(tmp_image, imageSize, y, newRegEnd); + x--; + j = loc(x, i); + } +out: + y -= curLine; + // In word selection mode don't select @ (64) if at end of word. + if (((image[j].rendition & RE_EXTENDED_CHAR) == 0) && + (QChar(image[j].character) == '@') && + (y > pnt.y() || x > pnt.x())) { + if (x > 0) { + x--; + } else { + y--; + } + } + if (tmp_image) { + delete[] tmp_image; + } + return QPoint(x, y); +} + +void TerminalDisplay::mouseTripleClickEvent(QMouseEvent* ev) +{ + if (!_screenWindow) return; + + int charLine; + int charColumn; + getCharacterPosition(ev->pos(), charLine, charColumn); + selectLine(QPoint(charColumn, charLine), + _tripleClickMode == Enum::SelectWholeLine); +} + +void TerminalDisplay::selectLine(QPoint pos, bool entireLine) +{ + _iPntSel = pos; + + _screenWindow->clearSelection(); + + _lineSelectionMode = true; + _wordSelectionMode = false; + + _actSel = 2; // within selection + + if (!entireLine) { // Select from cursor to end of line + _tripleSelBegin = findWordStart(_iPntSel); + _screenWindow->setSelectionStart(_tripleSelBegin.x(), + _tripleSelBegin.y() , false); + } else { + _tripleSelBegin = findLineStart(_iPntSel); + _screenWindow->setSelectionStart(0 , _tripleSelBegin.y() , false); + } + + _iPntSel = findLineEnd(_iPntSel); + _screenWindow->setSelectionEnd(_iPntSel.x() , _iPntSel.y()); + + copyToX11Selection(); + + _iPntSel.ry() += _scrollBar->value(); +} + +void TerminalDisplay::selectCurrentLine() +{ + if (!_screenWindow) return; + + selectLine(cursorPosition(), true); +} + +bool TerminalDisplay::focusNextPrevChild(bool next) +{ + // for 'Tab', always disable focus switching among widgets + // for 'Shift+Tab', leave the decision to higher level + if (next) + return false; + else + return QWidget::focusNextPrevChild(next); +} + +QChar TerminalDisplay::charClass(const Character& ch) const +{ + if (ch.rendition & RE_EXTENDED_CHAR) { + ushort extendedCharLength = 0; + const ushort* chars = ExtendedCharTable::instance.lookupExtendedChar(ch.character, extendedCharLength); + if (chars && extendedCharLength > 0) { + const QString s = QString::fromUtf16(chars, extendedCharLength); + if (_wordCharacters.contains(s, Qt::CaseInsensitive)) + return 'a'; + bool allLetterOrNumber = true; + for (int i = 0; allLetterOrNumber && i < s.size(); ++i) + allLetterOrNumber = s.at(i).isLetterOrNumber(); + return allLetterOrNumber ? 'a' : s.at(0); + } + return 0; + } else { + const QChar qch(ch.character); + if (qch.isSpace()) return ' '; + + if (qch.isLetterOrNumber() || _wordCharacters.contains(qch, Qt::CaseInsensitive)) + return 'a'; + + return qch; + } +} + +void TerminalDisplay::setWordCharacters(const QString& wc) +{ + _wordCharacters = wc; +} + +// FIXME: the actual value of _mouseMarks is the opposite of its semantic. +// When using programs not interested with mouse(shell, less), it is true. +// When using programs interested with mouse(vim,mc), it is false. +void TerminalDisplay::setUsesMouse(bool on) +{ + _mouseMarks = on; + setCursor(_mouseMarks ? Qt::IBeamCursor : Qt::ArrowCursor); +} +bool TerminalDisplay::usesMouse() const +{ + return _mouseMarks; +} + +void TerminalDisplay::setBracketedPasteMode(bool on) +{ + _bracketedPasteMode = on; +} +bool TerminalDisplay::bracketedPasteMode() const +{ + return _bracketedPasteMode; +} + +/* ------------------------------------------------------------------------- */ +/* */ +/* Clipboard */ +/* */ +/* ------------------------------------------------------------------------- */ + +void TerminalDisplay::doPaste(QString text, bool appendReturn) +{ + if (!_screenWindow) + return; + + if (appendReturn) + text.append("\r"); + + if (text.length() > 8000) { + if (KMessageBox::warningContinueCancel(window(), + i18np("Are you sure you want to paste %1 character?", + "Are you sure you want to paste %1 characters?", + text.length()), + i18n("Confirm Paste"), + KStandardGuiItem::cont(), + KStandardGuiItem::cancel(), + "ShowPasteHugeTextWarning") == KMessageBox::Cancel) + return; + } + + if (!text.isEmpty()) { + text.replace('\n', '\r'); + if (bracketedPasteMode()) { + text.prepend("\e[200~"); + text.append("\e[201~"); + } + // perform paste by simulating keypress events + QKeyEvent e(QEvent::KeyPress, 0, Qt::NoModifier, text); + emit keyPressedSignal(&e); + } +} + +void TerminalDisplay::setAutoCopySelectedText(bool enabled) +{ + _autoCopySelectedText = enabled; +} + +void TerminalDisplay::setMiddleClickPasteMode(Enum::MiddleClickPasteModeEnum mode) +{ + _middleClickPasteMode = mode; +} + +void TerminalDisplay::copyToX11Selection() +{ + if (!_screenWindow) + return; + + QString text = _screenWindow->selectedText(_preserveLineBreaks, _trimTrailingSpaces); + if (text.isEmpty()) + return; + + QApplication::clipboard()->setText(text, QClipboard::Selection); + + if (_autoCopySelectedText) + QApplication::clipboard()->setText(text, QClipboard::Clipboard); +} + +void TerminalDisplay::copyToClipboard() +{ + if (!_screenWindow) + return; + + QString text = _screenWindow->selectedText(_preserveLineBreaks, _trimTrailingSpaces); + if (text.isEmpty()) + return; + + QApplication::clipboard()->setText(text, QClipboard::Clipboard); +} + +void TerminalDisplay::pasteFromClipboard(bool appendEnter) +{ + QString text = QApplication::clipboard()->text(QClipboard::Clipboard); + doPaste(text, appendEnter); +} + +void TerminalDisplay::pasteFromX11Selection(bool appendEnter) +{ + QString text = QApplication::clipboard()->text(QClipboard::Selection); + doPaste(text, appendEnter); +} + +/* ------------------------------------------------------------------------- */ +/* */ +/* Input Method */ +/* */ +/* ------------------------------------------------------------------------- */ +#ifndef QT_KATIE +void TerminalDisplay::inputMethodEvent(QInputMethodEvent* event) +{ + if (!event->commitString().isEmpty()) { + QKeyEvent keyEvent(QEvent::KeyPress, 0, Qt::NoModifier, event->commitString()); + emit keyPressedSignal(&keyEvent); + } + + _inputMethodData.preeditString = event->preeditString(); + update(preeditRect() | _inputMethodData.previousPreeditRect); + + event->accept(); +} + +QVariant TerminalDisplay::inputMethodQuery(Qt::InputMethodQuery query) const +{ + const QPoint cursorPos = cursorPosition(); + switch (query) { + case Qt::ImMicroFocus: + return imageToWidget(QRect(cursorPos.x(), cursorPos.y(), 1, 1)); + break; + case Qt::ImFont: + return font(); + break; + case Qt::ImCursorPosition: + // return the cursor position within the current line + return cursorPos.x(); + break; + case Qt::ImSurroundingText: { + // return the text from the current line + QString lineText; + QTextStream stream(&lineText); + PlainTextDecoder decoder; + decoder.begin(&stream); + decoder.decodeLine(&_image[loc(0, cursorPos.y())], _usedColumns, _lineProperties[cursorPos.y()]); + decoder.end(); + return lineText; + } + break; + case Qt::ImCurrentSelection: + return QString(); + break; + default: + break; + } + + return QVariant(); +} +#endif + +QRect TerminalDisplay::preeditRect() const +{ + const int preeditLength = string_width(_inputMethodData.preeditString); + + if (preeditLength == 0) + return QRect(); + + return QRect(_contentRect.left() + _fontWidth * cursorPosition().x(), + _contentRect.top() + _fontHeight * cursorPosition().y(), + _fontWidth * preeditLength, + _fontHeight); +} + +void TerminalDisplay::drawInputMethodPreeditString(QPainter& painter , const QRect& rect) +{ + if (_inputMethodData.preeditString.isEmpty()) + return; + + const QPoint cursorPos = cursorPosition(); + + bool invertColors = false; + const QColor background = _colorTable[DEFAULT_BACK_COLOR].color; + const QColor foreground = _colorTable[DEFAULT_FORE_COLOR].color; + const Character* style = &_image[loc(cursorPos.x(), cursorPos.y())]; + + drawBackground(painter, rect, background, true); + drawCursor(painter, rect, foreground, background, invertColors); + drawCharacters(painter, rect, _inputMethodData.preeditString, style, invertColors); + + _inputMethodData.previousPreeditRect = rect; +} + +/* ------------------------------------------------------------------------- */ +/* */ +/* Keyboard */ +/* */ +/* ------------------------------------------------------------------------- */ + +void TerminalDisplay::setFlowControlWarningEnabled(bool enable) +{ + _flowControlWarningEnabled = enable; + + // if the dialog is currently visible and the flow control warning has + // been disabled then hide the dialog + if (!enable) + outputSuspended(false); +} + +void TerminalDisplay::outputSuspended(bool suspended) +{ + //create the label when this function is first called + if (!_outputSuspendedLabel) { + //This label includes a link to an English language website + //describing the 'flow control' (Xon/Xoff) feature found in almost + //all terminal emulators. + //If there isn't a suitable article available in the target language the link + //can simply be removed. + _outputSuspendedLabel = new QLabel(i18n("Output has been " + "suspended" + " by pressing Ctrl+S." + " Press Ctrl+Q to resume." + " This message will be dismissed in 10 seconds."), + this); + + QPalette palette(_outputSuspendedLabel->palette()); + KColorScheme::adjustBackground(palette, KColorScheme::NeutralBackground); + _outputSuspendedLabel->setPalette(palette); + _outputSuspendedLabel->setAutoFillBackground(true); + _outputSuspendedLabel->setBackgroundRole(QPalette::Base); + _outputSuspendedLabel->setFont(KGlobalSettings::smallestReadableFont()); + _outputSuspendedLabel->setContentsMargins(5, 5, 5, 5); + _outputSuspendedLabel->setWordWrap(true); + + //enable activation of "Xon/Xoff" link in label + _outputSuspendedLabel->setTextInteractionFlags(Qt::LinksAccessibleByMouse | + Qt::LinksAccessibleByKeyboard); + _outputSuspendedLabel->setOpenExternalLinks(true); + _outputSuspendedLabel->setVisible(false); + + _gridLayout->addWidget(_outputSuspendedLabel); + _gridLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, + QSizePolicy::Expanding), + 1, 0); + } + // Remove message after a few seconds + if (suspended) { + QTimer::singleShot(10000, this, SLOT(dismissOutputSuspendedMessage())); + } + + _outputSuspendedLabel->setVisible(suspended); +} + +void TerminalDisplay::dismissOutputSuspendedMessage() +{ + outputSuspended(false); +} + +void TerminalDisplay::scrollScreenWindow(enum ScreenWindow::RelativeScrollMode mode, int amount) +{ + _screenWindow->scrollBy(mode, amount, _scrollFullPage); + _screenWindow->setTrackOutput(_screenWindow->atEndOfOutput()); + updateLineProperties(); + updateImage(); + viewScrolledByUser(); +} + +void TerminalDisplay::keyPressEvent(QKeyEvent* event) +{ + _screenWindow->screen()->setCurrentTerminalDisplay(this); + + _actSel = 0; // Key stroke implies a screen update, so TerminalDisplay won't + // know where the current selection is. + + if (_allowBlinkingCursor) { + _blinkCursorTimer->start(); + if (_cursorBlinking) { + // if cursor is blinking(hidden), blink it again to show it + blinkCursorEvent(); + } + Q_ASSERT(_cursorBlinking == false); + } + + emit keyPressedSignal(event); + + event->accept(); +} + +bool TerminalDisplay::handleShortcutOverrideEvent(QKeyEvent* keyEvent) +{ + const int modifiers = keyEvent->modifiers(); + + // When a possible shortcut combination is pressed, + // emit the overrideShortcutCheck() signal to allow the host + // to decide whether the terminal should override it or not. + if (modifiers != Qt::NoModifier) { + int modifierCount = 0; + unsigned int currentModifier = Qt::ShiftModifier; + + while (currentModifier <= Qt::KeypadModifier) { + if (modifiers & currentModifier) + modifierCount++; + currentModifier <<= 1; + } + if (modifierCount < 2) { + bool override = false; + emit overrideShortcutCheck(keyEvent, override); + if (override) { + keyEvent->accept(); + return true; + } + } + } + + // Override any of the following shortcuts because + // they are needed by the terminal + int keyCode = keyEvent->key() | modifiers; + switch (keyCode) { + // list is taken from the QLineEdit::event() code + case Qt::Key_Tab: + case Qt::Key_Delete: + case Qt::Key_Home: + case Qt::Key_End: + case Qt::Key_Backspace: + case Qt::Key_Left: + case Qt::Key_Right: + case Qt::Key_Slash: + case Qt::Key_Period: + case Qt::Key_Space: + keyEvent->accept(); + return true; + } + return false; +} + +bool TerminalDisplay::event(QEvent* event) +{ + bool eventHandled = false; + switch (event->type()) { + case QEvent::ShortcutOverride: + eventHandled = handleShortcutOverrideEvent(static_cast(event)); + break; + case QEvent::PaletteChange: + case QEvent::ApplicationPaletteChange: + _scrollBar->setPalette(QApplication::palette()); + break; + default: + break; + } + return eventHandled ? true : QWidget::event(event); +} + +void TerminalDisplay::contextMenuEvent(QContextMenuEvent* event) +{ + // the logic for the mouse case is within MousePressEvent() + if (event->reason() != QContextMenuEvent::Mouse) { + emit configureRequest(mapFromGlobal(QCursor::pos())); + } +} + +/* --------------------------------------------------------------------- */ +/* */ +/* Bell */ +/* */ +/* --------------------------------------------------------------------- */ + +void TerminalDisplay::setBellMode(int mode) +{ + _bellMode = mode; +} + +int TerminalDisplay::bellMode() const +{ + return _bellMode; +} + +void TerminalDisplay::unmaskBell() +{ + _bellMasked = false; +} + +void TerminalDisplay::bell(const QString& message) +{ + if (_bellMasked) + return; + + switch (_bellMode) { + case Enum::SystemBeepBell: + KNotification::beep(); + break; + case Enum::NotifyBell: + // STABLE API: + // Please note that these event names, "BellVisible" and "BellInvisible", + // should not change and should be kept stable, because other applications + // that use this code via KPart rely on these names for notifications. + KNotification::event(hasFocus() ? "BellVisible" : "BellInvisible", + message, QPixmap(), this); + break; + case Enum::VisualBell: + visualBell(); + break; + default: + break; + } + + // limit the rate at which bells can occur. + // ...mainly for sound effects where rapid bells in sequence + // produce a horrible noise. + _bellMasked = true; + QTimer::singleShot(500, this, SLOT(unmaskBell())); +} + +void TerminalDisplay::visualBell() +{ + swapFGBGColors(); + QTimer::singleShot(200, this, SLOT(swapFGBGColors())); +} + +void TerminalDisplay::swapFGBGColors() +{ + // swap the default foreground & background color + ColorEntry color = _colorTable[DEFAULT_BACK_COLOR]; + _colorTable[DEFAULT_BACK_COLOR] = _colorTable[DEFAULT_FORE_COLOR]; + _colorTable[DEFAULT_FORE_COLOR] = color; + + update(); +} + +/* --------------------------------------------------------------------- */ +/* */ +/* Drag & Drop */ +/* */ +/* --------------------------------------------------------------------- */ + +void TerminalDisplay::dragEnterEvent(QDragEnterEvent* event) +{ + // text/plain alone is enough for KDE-apps + // text/uri-list is for supporting some non-KDE apps, such as thunar + // and pcmanfm + // That also applies in dropEvent() + if (event->mimeData()->hasFormat("text/plain") || + event->mimeData()->hasFormat("text/uri-list")) { + event->acceptProposedAction(); + } +} + +void TerminalDisplay::dropEvent(QDropEvent* event) +{ + KUrl::List urls = KUrl::List::fromMimeData(event->mimeData()); + + QString dropText; + if (!urls.isEmpty()) { + for (int i = 0 ; i < urls.count() ; i++) { + KUrl url = KIO::NetAccess::mostLocalUrl(urls[i] , 0); + QString urlText; + + if (url.isLocalFile()) + urlText = url.path(); + else + urlText = url.url(); + + // in future it may be useful to be able to insert file names with drag-and-drop + // without quoting them (this only affects paths with spaces in) + urlText = KShell::quoteArg(urlText); + + dropText += urlText; + + // Each filename(including the last) should be followed by one space. + dropText += ' '; + } + + // If our target is local we will open a popup - otherwise the fallback kicks + // in and the URLs will simply be pasted as text. + if (_sessionController && _sessionController->url().isLocalFile()) { + // A standard popup with Copy, Move and Link as options - + // plus an additional Paste option. + + QAction* pasteAction = new QAction(i18n("&Paste Location"), this); + pasteAction->setData(dropText); + connect(pasteAction, SIGNAL(triggered()), this, SLOT(dropMenuPasteActionTriggered())); + + QList additionalActions; + additionalActions.append(pasteAction); + + if (urls.count() == 1) { + const KUrl url = KIO::NetAccess::mostLocalUrl(urls[0] , 0); + + if (url.isLocalFile()) { + const QFileInfo fileInfo(url.path()); + + if (fileInfo.isDir()) { + QAction* cdAction = new QAction(i18n("Change &Directory To"), this); + dropText = QLatin1String(" cd ") + dropText + QChar('\n'); + cdAction->setData(dropText); + connect(cdAction, SIGNAL(triggered()), this, SLOT(dropMenuCdActionTriggered())); + additionalActions.append(cdAction); + } + } + } + + KUrl target(_sessionController->currentDir()); + + KonqOperations::doDrop(KFileItem(), target, event, this, additionalActions); + + return; + } + + } else { + dropText = event->mimeData()->text(); + } + + if (event->mimeData()->hasFormat("text/plain") || + event->mimeData()->hasFormat("text/uri-list")) { + emit sendStringToEmu(dropText.toLocal8Bit()); + } +} + +void TerminalDisplay::dropMenuPasteActionTriggered() +{ + if (sender()) { + const QAction* action = qobject_cast(sender()); + if (action) { + emit sendStringToEmu(action->data().toString().toLocal8Bit()); + } + } +} + +void TerminalDisplay::dropMenuCdActionTriggered() +{ + if (sender()) { + const QAction* action = qobject_cast(sender()); + if (action) { + emit sendStringToEmu(action->data().toString().toLocal8Bit()); + } + } +} + +void TerminalDisplay::doDrag() +{ + _dragInfo.state = diDragging; + _dragInfo.dragObject = new QDrag(this); + QMimeData* mimeData = new QMimeData; + mimeData->setText(QApplication::clipboard()->text(QClipboard::Selection)); + _dragInfo.dragObject->setMimeData(mimeData); + _dragInfo.dragObject->exec(Qt::CopyAction); +} + +void TerminalDisplay::setSessionController(SessionController* controller) +{ + _sessionController = controller; +} + +SessionController* TerminalDisplay::sessionController() +{ + return _sessionController; +} + +AutoScrollHandler::AutoScrollHandler(QWidget* parent) + : QObject(parent) + , _timerId(0) +{ + parent->installEventFilter(this); +} +void AutoScrollHandler::timerEvent(QTimerEvent* event) +{ + if (event->timerId() != _timerId) + return; + + QMouseEvent mouseEvent(QEvent::MouseMove, + widget()->mapFromGlobal(QCursor::pos()), + Qt::NoButton, + Qt::LeftButton, + Qt::NoModifier); + + QApplication::sendEvent(widget(), &mouseEvent); +} +bool AutoScrollHandler::eventFilter(QObject* watched, QEvent* event) +{ + Q_ASSERT(watched == parent()); + Q_UNUSED(watched); + + QMouseEvent* mouseEvent = (QMouseEvent*)event; + switch (event->type()) { + case QEvent::MouseMove: { + bool mouseInWidget = widget()->rect().contains(mouseEvent->pos()); + if (mouseInWidget) { + if (_timerId) + killTimer(_timerId); + + _timerId = 0; + } else { + if (!_timerId && (mouseEvent->buttons() & Qt::LeftButton)) + _timerId = startTimer(100); + } + + break; + } + case QEvent::MouseButtonRelease: { + if (_timerId && (mouseEvent->buttons() & ~Qt::LeftButton)) { + killTimer(_timerId); + _timerId = 0; + } + break; + } + default: + break; + }; + + return false; +} + +#include "moc_TerminalDisplay.cpp" diff --git a/konsole/src/TerminalDisplay.h b/konsole/src/TerminalDisplay.h new file mode 100644 index 00000000..13c405e4 --- /dev/null +++ b/konsole/src/TerminalDisplay.h @@ -0,0 +1,922 @@ +/* + Copyright 2007-2008 by Robert Knight + Copyright 1997,1998 by Lars Doelle + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +#ifndef TERMINALDISPLAY_H +#define TERMINALDISPLAY_H + +// Qt +#include +#include +#include + +// Konsole +#include "Character.h" +#include "konsoleprivate_export.h" +#include "ScreenWindow.h" +#include "ColorScheme.h" +#include "Enumeration.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Konsole +{ +class FilterChain; +class TerminalImageFilterChain; +class SessionController; +/** + * A widget which displays output from a terminal emulation and sends input keypresses and mouse activity + * to the terminal. + * + * When the terminal emulation receives new output from the program running in the terminal, + * it will update the display by calling updateImage(). + * + * TODO More documentation + */ +class KONSOLEPRIVATE_EXPORT TerminalDisplay : public QWidget +{ + Q_OBJECT + +public: + /** Constructs a new terminal display widget with the specified parent. */ + explicit TerminalDisplay(QWidget* parent = 0); + virtual ~TerminalDisplay(); + + /** Returns the terminal color palette used by the display. */ + const ColorEntry* colorTable() const; + /** Sets the terminal color palette used by the display. */ + void setColorTable(const ColorEntry table[]); + /** + * Sets the seed used to generate random colors for the display + * (in color schemes that support them). + */ + void setRandomSeed(uint seed); + /** + * Returns the seed used to generate random colors for the display + * (in color schemes that support them). + */ + uint randomSeed() const; + + /** Sets the opacity of the terminal display. */ + void setOpacity(qreal opacity); + + /** Sets the background picture */ + void setWallpaper(ColorSchemeWallpaper::Ptr p); + + /** + * Specifies whether the terminal display has a vertical scroll bar, and if so whether it + * is shown on the left or right side of the display. + */ + void setScrollBarPosition(Enum::ScrollBarPositionEnum position); + Enum::ScrollBarPositionEnum scrollBarPosition() const { + return _scrollbarLocation; + } + + /** + * Sets the current position and range of the display's scroll bar. + * + * @param cursor The position of the scroll bar's thumb. + * @param lines The maximum value of the scroll bar. + */ + void setScroll(int cursor, int lines); + + void setScrollFullPage(bool fullPage); + bool scrollFullPage() const; + + /** + * Returns the display's filter chain. When the image for the display is updated, + * the text is passed through each filter in the chain. Each filter can define + * hotspots which correspond to certain strings (such as URLs or particular words). + * Depending on the type of the hotspots created by the filter ( returned by Filter::Hotspot::type() ) + * the view will draw visual cues such as underlines on mouse-over for links or translucent + * rectangles for markers. + * + * To add a new filter to the view, call: + * viewWidget->filterChain()->addFilter( filterObject ); + */ + FilterChain* filterChain() const; + + /** + * Updates the filters in the display's filter chain. This will cause + * the hotspots to be updated to match the current image. + * + * WARNING: This function can be expensive depending on the + * image size and number of filters in the filterChain() + * + * TODO - This API does not really allow efficient usage. Revise it so + * that the processing can be done in a better way. + * + * eg: + * - Area of interest may be known ( eg. mouse cursor hovering + * over an area ) + */ + void processFilters(); + + /** + * Returns a list of menu actions created by the filters for the content + * at the given @p position. + */ + QList filterActions(const QPoint& position); + + /** Specifies whether or not the cursor can blink. */ + void setBlinkingCursorEnabled(bool blink); + /** Returns true if the cursor is allowed to blink or false otherwise. */ + bool blinkingCursorEnabled() const { + return _allowBlinkingCursor; + } + + /** Specifies whether or not text can blink. */ + void setBlinkingTextEnabled(bool blink); + + void setControlDrag(bool enable) { + _ctrlRequiredForDrag = enable; + } + bool ctrlRequiredForDrag() const { + return _ctrlRequiredForDrag; + } + + /** Sets how the text is selected when the user triple clicks within the display. */ + void setTripleClickMode(Enum::TripleClickModeEnum mode) { + _tripleClickMode = mode; + } + /** See setTripleClickSelectionMode() */ + Enum::TripleClickModeEnum tripleClickMode() const { + return _tripleClickMode; + } + + /** + * Specifies whether links and email addresses should be underlined when + * hovered by the mouse. Defaults to true. + */ + void setUnderlineLinks(bool value) { + _underlineLinks = value; + } + /** + * Returns true if links and email addresses should be underlined when + * hovered by the mouse. + */ + bool getUnderlineLinks() const { + return _underlineLinks; + } + + /** + * Specifies whether links and email addresses should be opened when + * clicked with the mouse. Defaults to false. + */ + void setOpenLinksByDirectClick(bool value) { + _openLinksByDirectClick = value; + } + /** + * Returns true if links and email addresses should be opened when + * clicked with the mouse. + */ + bool getOpenLinksByDirectClick() const { + return _openLinksByDirectClick; + } + + /** + * Sets whether trailing spaces should be trimmed in selected text. + */ + void setTrimTrailingSpaces(bool enabled) { + _trimTrailingSpaces = enabled; + } + + /** + * Returns true if trailing spaces should be trimmed in selected text. + */ + bool trimTrailingSpaces() const { + return _trimTrailingSpaces; + } + + void setLineSpacing(uint); + uint lineSpacing() const; + + void setSessionController(SessionController* controller); + SessionController* sessionController(); + + /** + * Sets the shape of the keyboard cursor. This is the cursor drawn + * at the position in the terminal where keyboard input will appear. + * + * In addition the terminal display widget also has a cursor for + * the mouse pointer, which can be set using the QWidget::setCursor() + * method. + * + * Defaults to BlockCursor + */ + void setKeyboardCursorShape(Enum::CursorShapeEnum shape); + /** + * Returns the shape of the keyboard cursor. See setKeyboardCursorShape() + */ + Enum::CursorShapeEnum keyboardCursorShape() const; + + /** + * Sets the color used to draw the keyboard cursor. + * + * The keyboard cursor defaults to using the foreground color of the character + * underneath it. + * + * @param color By default, the widget uses the color of the + * character under the cursor to draw the cursor, and inverts the + * color of that character to make sure it is still readable. If @p + * color is a valid QColor, the widget uses that color to draw the + * cursor. If @p color is not an valid QColor, the widget falls back + * to the default behavior. + */ + void setKeyboardCursorColor(const QColor& color); + + /** + * Returns the color of the keyboard cursor, or an invalid color if the keyboard + * cursor color is set to change according to the foreground color of the character + * underneath it. + */ + QColor keyboardCursorColor() const; + + /** + * Returns the number of lines of text which can be displayed in the widget. + * + * This will depend upon the height of the widget and the current font. + * See fontHeight() + */ + int lines() const { + return _lines; + } + /** + * Returns the number of characters of text which can be displayed on + * each line in the widget. + * + * This will depend upon the width of the widget and the current font. + * See fontWidth() + */ + int columns() const { + return _columns; + } + + /** + * Returns the height of the characters in the font used to draw the text in the display. + */ + int fontHeight() const { + return _fontHeight; + } + /** + * Returns the width of the characters in the display. + * This assumes the use of a fixed-width font. + */ + int fontWidth() const { + return _fontWidth; + } + + void setSize(int columns, int lines); + + // reimplemented + QSize sizeHint() const; + + /** + * Sets which characters, in addition to letters and numbers, + * are regarded as being part of a word for the purposes + * of selecting words in the display by double clicking on them. + * + * The word boundaries occur at the first and last characters which + * are either a letter, number, or a character in @p wc + * + * @param wc An array of characters which are to be considered parts + * of a word ( in addition to letters and numbers ). + */ + void setWordCharacters(const QString& wc); + /** + * Returns the characters which are considered part of a word for the + * purpose of selecting words in the display with the mouse. + * + * @see setWordCharacters() + */ + QString wordCharacters() const { + return _wordCharacters; + } + + /** + * Sets the type of effect used to alert the user when a 'bell' occurs in the + * terminal session. + * + * The terminal session can trigger the bell effect by calling bell() with + * the alert message. + */ + void setBellMode(int mode); + /** + * Returns the type of effect used to alert the user when a 'bell' occurs in + * the terminal session. + * + * See setBellMode() + */ + int bellMode() const; + + /** Play a visual bell for prompt or warning. */ + void visualBell(); + + /** + * Specified whether zoom terminal on Ctrl+mousewheel is enabled or not. + * Defaults to enabled. + */ + void setMouseWheelZoom(bool value) { + _mouseWheelZoom = value; + }; + /** + * Returns the whether zoom terminal on Ctrl+mousewheel is enabled. + * + * See setMouseWheelZoom() + */ + bool mouseWheelZoom() { + return _mouseWheelZoom; + }; + + /** Returns the font used to draw characters in the display */ + QFont getVTFont() { + return font(); + } + + /** + * Sets the font used to draw the display. Has no effect if @p font + * is larger than the size of the display itself. + */ + void setVTFont(const QFont& font); + + /** Increases the font size */ + void increaseFontSize(); + + /** Decreases the font size */ + void decreaseFontSize(); + + /** + * Specified whether anti-aliasing of text in the terminal display + * is enabled or not. Defaults to enabled. + */ + void setAntialias(bool value) { + _antialiasText = value; + } + /** + * Returns true if anti-aliasing of text in the terminal is enabled. + */ + bool antialias() const { + return _antialiasText; + } + + /** + * Specifies whether characters with intense colors should be rendered + * as bold. Defaults to true. + */ + void setBoldIntense(bool value) { + _boldIntense = value; + } + /** + * Returns true if characters with intense colors are rendered in bold. + */ + bool getBoldIntense() const { + return _boldIntense; + } + + /** + * Sets whether or not the current height and width of the + * terminal in lines and columns is displayed whilst the widget + * is being resized. + */ + void setShowTerminalSizeHint(bool on) { + _showTerminalSizeHint = on; + } + /** + * Returns whether or not the current height and width of + * the terminal in lines and columns is displayed whilst the widget + * is being resized. + */ + bool showTerminalSizeHint() const { + return _showTerminalSizeHint; + } + + /** + * Sets the status of the BiDi rendering inside the terminal display. + * Defaults to disabled. + */ + void setBidiEnabled(bool set) { + _bidiEnabled = set; + } + /** + * Returns the status of the BiDi rendering in this widget. + */ + bool isBidiEnabled() const { + return _bidiEnabled; + } + + /** + * Sets the terminal screen section which is displayed in this widget. + * When updateImage() is called, the display fetches the latest character image from the + * the associated terminal screen window. + * + * In terms of the model-view paradigm, the ScreenWindow is the model which is rendered + * by the TerminalDisplay. + */ + void setScreenWindow(ScreenWindow* window); + /** Returns the terminal screen section which is displayed in this widget. See setScreenWindow() */ + ScreenWindow* screenWindow() const; + + // Select the current line. + void selectCurrentLine(); + + void printContent(QPainter& painter, bool friendly); + +public slots: + /** + * Scrolls current ScreenWindow + * + * it's needed for proper handling scroll commands in the Vt102Emulation class + */ + void scrollScreenWindow(enum ScreenWindow::RelativeScrollMode mode , int amount); + + /** + * Causes the terminal display to fetch the latest character image from the associated + * terminal screen ( see setScreenWindow() ) and redraw the display. + */ + void updateImage(); + /** + * Causes the terminal display to fetch the latest line status flags from the + * associated terminal screen ( see setScreenWindow() ). + */ + void updateLineProperties(); + + void setAutoCopySelectedText(bool enabled); + + void setMiddleClickPasteMode(Enum::MiddleClickPasteModeEnum mode); + + /** Copies the selected text to the X11 Selection. */ + void copyToX11Selection(); + + /** Copies the selected text to the system clipboard. */ + void copyToClipboard(); + + /** + * Pastes the content of the clipboard into the + * display. + */ + void pasteFromClipboard(bool appendEnter = false); + /** + * Pastes the content of the X11 selection into the + * display. + */ + void pasteFromX11Selection(bool appendEnter = false); + + /** + * Changes whether the flow control warning box should be shown when the flow control + * stop key (Ctrl+S) are pressed. + */ + void setFlowControlWarningEnabled(bool enabled); + /** + * Returns true if the flow control warning box is enabled. + * See outputSuspended() and setFlowControlWarningEnabled() + */ + bool flowControlWarningEnabled() const { + return _flowControlWarningEnabled; + } + + /** + * Causes the widget to display or hide a message informing the user that terminal + * output has been suspended (by using the flow control key combination Ctrl+S) + * + * @param suspended True if terminal output has been suspended and the warning message should + * be shown or false to indicate that terminal output has been resumed and that + * the warning message should disappear. + */ + void outputSuspended(bool suspended); + + /** + * Sets whether the program whose output is being displayed in the view + * is interested in mouse events. + * + * If this is set to true, mouse signals will be emitted by the view when the user clicks, drags + * or otherwise moves the mouse inside the view. + * The user interaction needed to create selections will also change, and the user will be required + * to hold down the shift key to create a selection or perform other mouse activities inside the + * view area - since the program running in the terminal is being allowed to handle normal mouse + * events itself. + * + * @param usesMouse Set to true if the program running in the terminal is interested in mouse events + * or false otherwise. + */ + void setUsesMouse(bool usesMouse); + + /** See setUsesMouse() */ + bool usesMouse() const; + + void setBracketedPasteMode(bool bracketedPasteMode); + bool bracketedPasteMode() const; + + /** + * Shows a notification that a bell event has occurred in the terminal. + * TODO: More documentation here + */ + void bell(const QString& message); + + /** + * Gets the background of the display + * @see setBackgroundColor(), setColorTable(), setForegroundColor() + */ + QColor getBackgroundColor() const; + + /** + * Sets the background of the display to the specified color. + * @see setColorTable(), getBackgroundColor(), setForegroundColor() + */ + void setBackgroundColor(const QColor& color); + + /** + * Sets the text of the display to the specified color. + * @see setColorTable(), setBackgroundColor(), getBackgroundColor() + */ + void setForegroundColor(const QColor& color); + + /** + * Sets the display's contents margins. + */ + void setMargin(int margin); + + /** + * Sets whether the contents are centered between the margins. + */ + void setCenterContents(bool enable); + +signals: + + /** + * Emitted when the user presses a key whilst the terminal widget has focus. + */ + void keyPressedSignal(QKeyEvent* event); + + /** + * A mouse event occurred. + * @param button The mouse button (0 for left button, 1 for middle button, 2 for right button, 3 for release) + * @param column The character column where the event occurred + * @param line The character row where the event occurred + * @param eventType The type of event. 0 for a mouse press / release or 1 for mouse motion + */ + void mouseSignal(int button, int column, int line, int eventType); + void changedFontMetricSignal(int height, int width); + void changedContentSizeSignal(int height, int width); + + /** + * Emitted when the user right clicks on the display, or right-clicks with the Shift + * key held down if usesMouse() is true. + * + * This can be used to display a context menu. + */ + void configureRequest(const QPoint& position); + + /** + * When a shortcut which is also a valid terminal key sequence is pressed while + * the terminal widget has focus, this signal is emitted to allow the host to decide + * whether the shortcut should be overridden. + * When the shortcut is overridden, the key sequence will be sent to the terminal emulation instead + * and the action associated with the shortcut will not be triggered. + * + * @p override is set to false by default and the shortcut will be triggered as normal. + */ + void overrideShortcutCheck(QKeyEvent* keyEvent, bool& override); + + void sendStringToEmu(const char*); + +protected: + virtual bool event(QEvent* event); + + virtual void paintEvent(QPaintEvent* event); + + virtual void showEvent(QShowEvent* event); + virtual void hideEvent(QHideEvent* event); + virtual void resizeEvent(QResizeEvent* event); + + virtual void contextMenuEvent(QContextMenuEvent* event); + + virtual void fontChange(const QFont&); + virtual void focusInEvent(QFocusEvent* event); + virtual void focusOutEvent(QFocusEvent* event); + virtual void keyPressEvent(QKeyEvent* event); + virtual void leaveEvent(QEvent* event); + virtual void mouseDoubleClickEvent(QMouseEvent* event); + virtual void mousePressEvent(QMouseEvent* event); + virtual void mouseReleaseEvent(QMouseEvent* event); + virtual void mouseMoveEvent(QMouseEvent* event); + virtual void extendSelection(const QPoint& pos); + virtual void wheelEvent(QWheelEvent* event); + + virtual bool focusNextPrevChild(bool next); + + // drag and drop + virtual void dragEnterEvent(QDragEnterEvent* event); + virtual void dropEvent(QDropEvent* event); + void doDrag(); + enum DragState { diNone, diPending, diDragging }; + + struct DragInfo { + DragState state; + QPoint start; + QDrag* dragObject; + } _dragInfo; + + // classifies the 'ch' into one of three categories + // and returns a character to indicate which category it is in + // + // - A space (returns ' ') + // - Part of a word (returns 'a') + // - Other characters (returns the input character) + QChar charClass(const Character& ch) const; + + void clearImage(); + + void mouseTripleClickEvent(QMouseEvent* event); + void selectLine(QPoint pos, bool entireLine); + +#ifndef QT_KATIE + // reimplemented + virtual void inputMethodEvent(QInputMethodEvent* event); + virtual QVariant inputMethodQuery(Qt::InputMethodQuery query) const; +#endif + +protected slots: + + void scrollBarPositionChanged(int value); + void blinkTextEvent(); + void blinkCursorEvent(); + +private slots: + + void unmaskBell(); + void swapFGBGColors(); + void tripleClickTimeout(); // resets possibleTripleClick + void viewScrolledByUser(); + + /** + * Called from the drag-and-drop popup. Causes the dropped URLs to be pasted as text. + */ + void dropMenuPasteActionTriggered(); + + void dropMenuCdActionTriggered(); + + void dismissOutputSuspendedMessage(); + +private: + // -- Drawing helpers -- + + // divides the part of the display specified by 'rect' into + // fragments according to their colors and styles and calls + // drawTextFragment() or drawPrinterFriendlyTextFragment() + // to draw the fragments + void drawContents(QPainter& painter, const QRect& rect); + // draw a transparent rectangle over the line of the current match + void drawCurrentResultRect(QPainter& painter); + // draws a section of text, all the text in this section + // has a common color and style + void drawTextFragment(QPainter& painter, const QRect& rect, + const QString& text, const Character* style); + + void drawPrinterFriendlyTextFragment(QPainter& painter, const QRect& rect, + const QString& text, const Character* style); + // draws the background for a text fragment + // if useOpacitySetting is true then the color's alpha value will be set to + // the display's transparency (set with setOpacity()), otherwise the background + // will be drawn fully opaque + void drawBackground(QPainter& painter, const QRect& rect, const QColor& color, + bool useOpacitySetting); + // draws the cursor character + void drawCursor(QPainter& painter, const QRect& rect , const QColor& foregroundColor, + const QColor& backgroundColor , bool& invertColors); + // draws the characters or line graphics in a text fragment + void drawCharacters(QPainter& painter, const QRect& rect, const QString& text, + const Character* style, bool invertCharacterColor); + // draws a string of line graphics + void drawLineCharString(QPainter& painter, int x, int y, + const QString& str, const Character* attributes); + + // draws the preedit string for input methods + void drawInputMethodPreeditString(QPainter& painter , const QRect& rect); + + // -- + + // maps an area in the character image to an area on the widget + QRect imageToWidget(const QRect& imageArea) const; + + // maps a point on the widget to the position ( ie. line and column ) + // of the character at that point. + void getCharacterPosition(const QPoint& widgetPoint, int& line, int& column) const; + + // the area where the preedit string for input methods will be draw + QRect preeditRect() const; + + // shows a notification window in the middle of the widget indicating the terminal's + // current size in columns and lines + void showResizeNotification(); + + // scrolls the image by a number of lines. + // 'lines' may be positive ( to scroll the image down ) + // or negative ( to scroll the image up ) + // 'region' is the part of the image to scroll - currently only + // the top, bottom and height of 'region' are taken into account, + // the left and right are ignored. + void scrollImage(int lines , const QRect& region); + + void calcGeometry(); + void propagateSize(); + void updateImageSize(); + void makeImage(); + + void paintFilters(QPainter& painter); + + // returns a region covering all of the areas of the widget which contain + // a hotspot + QRegion hotSpotRegion() const; + + // returns the position of the cursor in columns and lines + QPoint cursorPosition() const; + + // redraws the cursor + void updateCursor(); + + bool handleShortcutOverrideEvent(QKeyEvent* event); + + void doPaste(QString text, bool appendReturn); + + void processMidButtonClick(QMouseEvent* event); + + QPoint findLineStart(const QPoint &pnt); + QPoint findLineEnd(const QPoint &pnt); + QPoint findWordStart(const QPoint &pnt); + QPoint findWordEnd(const QPoint &pnt); + + // the window onto the terminal screen which this display + // is currently showing. + QPointer _screenWindow; + + bool _bellMasked; + + QGridLayout* _gridLayout; + + bool _fixedFont; // has fixed pitch + int _fontHeight; // height + int _fontWidth; // width + bool _boldIntense; // Whether intense colors should be rendered with bold font + + int _lines; // the number of lines that can be displayed in the widget + int _columns; // the number of columns that can be displayed in the widget + + int _usedLines; // the number of lines that are actually being used, this will be less + // than 'lines' if the character image provided with setImage() is smaller + // than the maximum image size which can be displayed + + int _usedColumns; // the number of columns that are actually being used, this will be less + // than 'columns' if the character image provided with setImage() is smaller + // than the maximum image size which can be displayed + + QRect _contentRect; + Character* _image; // [lines][columns] + // only the area [usedLines][usedColumns] in the image contains valid data + + int _imageSize; + QVector _lineProperties; + + ColorEntry _colorTable[TABLE_COLORS]; + uint _randomSeed; + + bool _resizing; + bool _showTerminalSizeHint; + bool _bidiEnabled; + bool _mouseMarks; + bool _bracketedPasteMode; + + QPoint _iPntSel; // initial selection point + QPoint _pntSel; // current selection point + QPoint _tripleSelBegin; // help avoid flicker + int _actSel; // selection state + bool _wordSelectionMode; + bool _lineSelectionMode; + bool _preserveLineBreaks; + bool _columnSelectionMode; + + bool _autoCopySelectedText; + Enum::MiddleClickPasteModeEnum _middleClickPasteMode; + + QScrollBar* _scrollBar; + Enum::ScrollBarPositionEnum _scrollbarLocation; + bool _scrollFullPage; + QString _wordCharacters; + int _bellMode; + + bool _allowBlinkingText; // allow text to blink + bool _allowBlinkingCursor; // allow cursor to blink + bool _textBlinking; // text is blinking, hide it when drawing + bool _cursorBlinking; // cursor is blinking, hide it when drawing + bool _hasTextBlinker; // has characters to blink + QTimer* _blinkTextTimer; + QTimer* _blinkCursorTimer; + + bool _underlineLinks; // Underline URL and hosts on mouse hover + bool _openLinksByDirectClick; // Open URL and hosts by single mouse click + + bool _ctrlRequiredForDrag; // require Ctrl key for drag selected text + + Enum::TripleClickModeEnum _tripleClickMode; + bool _possibleTripleClick; // is set in mouseDoubleClickEvent and deleted + // after QApplication::doubleClickInterval() delay + + QLabel* _resizeWidget; + QTimer* _resizeTimer; + + bool _flowControlWarningEnabled; + + //widgets related to the warning message that appears when the user presses Ctrl+S to suspend + //terminal output - informing them what has happened and how to resume output + QLabel* _outputSuspendedLabel; + + uint _lineSpacing; + + QSize _size; + + QRgb _blendColor; + + ColorSchemeWallpaper::Ptr _wallpaper; + + // list of filters currently applied to the display. used for links and + // search highlight + TerminalImageFilterChain* _filterChain; + QRegion _mouseOverHotspotArea; + + Enum::CursorShapeEnum _cursorShape; + + // cursor color. If it is invalid (by default) then the foreground + // color of the character under the cursor is used + QColor _cursorColor; + + struct InputMethodData { + QString preeditString; + QRect previousPreeditRect; + }; + InputMethodData _inputMethodData; + + bool _antialiasText; // do we anti-alias or not + + bool _printerFriendly; // are we currently painting to a printer in black/white mode + + //the delay in milliseconds between redrawing blinking text + static const int TEXT_BLINK_DELAY = 500; + + //the duration of the size hint in milliseconds + static const int SIZE_HINT_DURATION = 1000; + + SessionController* _sessionController; + + bool _trimTrailingSpaces; // trim trailing spaces in selected text + bool _mouseWheelZoom; // enable mouse wheel zooming or not + + int _margin; // the contents margin + bool _centerContents; // center the contents between margins + + qreal _opacity; +}; + +class AutoScrollHandler : public QObject +{ + Q_OBJECT + +public: + explicit AutoScrollHandler(QWidget* parent); +protected: + virtual void timerEvent(QTimerEvent* event); + virtual bool eventFilter(QObject* watched, QEvent* event); +private: + QWidget* widget() const { + return static_cast(parent()); + } + int _timerId; +}; +} + +#endif // TERMINALDISPLAY_H diff --git a/konsole/src/ViewContainer.cpp b/konsole/src/ViewContainer.cpp new file mode 100644 index 00000000..e7efc793 --- /dev/null +++ b/konsole/src/ViewContainer.cpp @@ -0,0 +1,746 @@ +/* + This file is part of the Konsole Terminal. + + Copyright 2006-2008 Robert Knight + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +// Own +#include "ViewContainer.h" + +// Qt +#include +#include +#include +#include +#include +#include +#include + +// KDE +#include +#include +#include +#include +#include + +// Konsole +#include "IncrementalSearchBar.h" +#include "ViewProperties.h" +#include "ViewContainerTabBar.h" +#include "ProfileList.h" +#include "ViewManager.h" + +// TODO Perhaps move everything which is Konsole-specific into different files + +using namespace Konsole; + +ViewContainer::ViewContainer(NavigationPosition position , QObject* parent) + : QObject(parent) + , _navigationVisibility(AlwaysShowNavigation) + , _navigationPosition(position) + , _searchBar(0) +{ +} + +ViewContainer::~ViewContainer() +{ + foreach(QWidget * view , _views) { + disconnect(view, SIGNAL(destroyed(QObject*)), this, SLOT(viewDestroyed(QObject*))); + } + + if (_searchBar) { + _searchBar->deleteLater(); + } + + emit destroyed(this); +} +void ViewContainer::moveViewWidget(int , int) {} +void ViewContainer::setFeatures(Features features) +{ + _features = features; +} +ViewContainer::Features ViewContainer::features() const +{ + return _features; +} +void ViewContainer::moveActiveView(MoveDirection direction) +{ + const int currentIndex = _views.indexOf(activeView()); + int newIndex = -1; + + switch (direction) { + case MoveViewLeft: + newIndex = qMax(currentIndex - 1 , 0); + break; + case MoveViewRight: + newIndex = qMin(currentIndex + 1 , _views.count() - 1); + break; + } + + Q_ASSERT(newIndex != -1); + + moveViewWidget(currentIndex , newIndex); + + _views.swap(currentIndex, newIndex); + + setActiveView(_views[newIndex]); +} + +void ViewContainer::setNavigationVisibility(NavigationVisibility mode) +{ + _navigationVisibility = mode; + navigationVisibilityChanged(mode); +} +ViewContainer::NavigationPosition ViewContainer::navigationPosition() const +{ + return _navigationPosition; +} +void ViewContainer::setNavigationPosition(NavigationPosition position) +{ + // assert that this position is supported + Q_ASSERT(supportedNavigationPositions().contains(position)); + + _navigationPosition = position; + + navigationPositionChanged(position); +} +QList ViewContainer::supportedNavigationPositions() const +{ + return QList() << NavigationPositionTop; +} +ViewContainer::NavigationVisibility ViewContainer::navigationVisibility() const +{ + return _navigationVisibility; +} + +void ViewContainer::setNavigationTextMode(bool mode) +{ + navigationTextModeChanged(mode); +} + +void ViewContainer::addView(QWidget* view , ViewProperties* item, int index) +{ + if (index == -1) + _views.append(view); + else + _views.insert(index, view); + + _navigation[view] = item; + + connect(view, SIGNAL(destroyed(QObject*)), this, SLOT(viewDestroyed(QObject*))); + + addViewWidget(view, index); + + emit viewAdded(view, item); +} +void ViewContainer::viewDestroyed(QObject* object) +{ + QWidget* widget = static_cast(object); + + forgetView(widget); +} + +void ViewContainer::forgetView(QWidget* view) +{ + _views.removeAll(view); + _navigation.remove(view); + + emit viewRemoved(view); + + if (_views.count() == 0) + emit empty(this); +} +void ViewContainer::removeView(QWidget* view) +{ + disconnect(view, SIGNAL(destroyed(QObject*)), this, SLOT(viewDestroyed(QObject*))); + removeViewWidget(view); + forgetView(view); +} + +const QList ViewContainer::views() const +{ + return _views; +} + +IncrementalSearchBar* ViewContainer::searchBar() +{ + if (!_searchBar) { + _searchBar = new IncrementalSearchBar(0); + _searchBar->setVisible(false); + connect(_searchBar, SIGNAL(destroyed(QObject*)), this, SLOT(searchBarDestroyed())); + } + return _searchBar; +} + +void ViewContainer::searchBarDestroyed() +{ + _searchBar = 0; +} + +void ViewContainer::activateNextView() +{ + QWidget* active = activeView(); + + int index = _views.indexOf(active); + + if (index == -1) + return; + + if (index == _views.count() - 1) + index = 0; + else + index++; + + setActiveView(_views.at(index)); +} + +void ViewContainer::activateLastView() +{ + setActiveView(_views.at(_views.count() - 1)); +} + +void ViewContainer::activatePreviousView() +{ + QWidget* active = activeView(); + + int index = _views.indexOf(active); + + if (index == -1) + return; + + if (index == 0) + index = _views.count() - 1; + else + index--; + + setActiveView(_views.at(index)); +} + +ViewProperties* ViewContainer::viewProperties(QWidget* widget) const +{ + Q_ASSERT(_navigation.contains(widget)); + + return _navigation[widget]; +} + +QList ViewContainer::widgetsForItem(ViewProperties* item) const +{ + return _navigation.keys(item); +} + +TabbedViewContainer::TabbedViewContainer(NavigationPosition position, ViewManager* connectedViewManager, QObject* parent) + : ViewContainer(position, parent) + , _connectedViewManager(connectedViewManager) + , _contextMenuTabIndex(0) +{ + _containerWidget = new QWidget; + _stackWidget = new QStackedWidget(); + connect(_stackWidget, SIGNAL(widgetRemoved(int)), this, SLOT(widgetRemoved(int))); + + // The tab bar + _tabBar = new ViewContainerTabBar(_containerWidget, this); + _tabBar->setSupportedMimeType(ViewProperties::mimeType()); + + connect(_tabBar, SIGNAL(currentChanged(int)), this, SLOT(currentTabChanged(int))); + connect(_tabBar, SIGNAL(tabDoubleClicked(int)), this, SLOT(tabDoubleClicked(int))); + connect(_tabBar, SIGNAL(newTabRequest()), this, SIGNAL(newViewRequest())); + connect(_tabBar, SIGNAL(wheelDelta(int)), this, SLOT(wheelScrolled(int))); + connect(_tabBar, SIGNAL(initiateDrag(int)), this, SLOT(startTabDrag(int))); + connect(_tabBar, SIGNAL(querySourceIndex(const QDropEvent*,int&)), + this, SLOT(querySourceIndex(const QDropEvent*,int&))); + connect(_tabBar, SIGNAL(moveViewRequest(int,const QDropEvent*,bool&,TabbedViewContainer*)), + this, SLOT(onMoveViewRequest(int,const QDropEvent*,bool&,TabbedViewContainer*))); + connect(_tabBar, SIGNAL(contextMenu(int,QPoint)), this, + SLOT(openTabContextMenu(int,QPoint))); + + // The context menu of tab bar + _contextPopupMenu = new KMenu(_tabBar); + + _contextPopupMenu->addAction(KIcon("tab-detach"), + i18nc("@action:inmenu", "&Detach Tab"), this, + SLOT(tabContextMenuDetachTab())); + + _contextPopupMenu->addAction(KIcon("edit-rename"), + i18nc("@action:inmenu", "&Rename Tab..."), this, + SLOT(tabContextMenuRenameTab())); + + _contextPopupMenu->addSeparator(); + + _contextPopupMenu->addAction(KIcon("tab-close"), + i18nc("@action:inmenu", "&Close Tab"), this, + SLOT(tabContextMenuCloseTab())); + + // The 'new tab' and 'close tab' button + _newTabButton = new QToolButton(_containerWidget); + _newTabButton->setFocusPolicy(Qt::NoFocus); + _newTabButton->setIcon(KIcon("tab-new")); + _newTabButton->setToolTip(i18nc("@info:tooltip", "Create new tab")); + _newTabButton->setWhatsThis(i18nc("@info:whatsthis", "Create a new tab. Press and hold to select profile from menu")); + _newTabButton->adjustSize(); + + QMenu* profileMenu = new QMenu(_newTabButton); + ProfileList* profileList = new ProfileList(false, profileMenu); + profileList->syncWidgetActions(profileMenu, true); + connect(profileList, SIGNAL(profileSelected(Profile::Ptr)), + this, SIGNAL(newViewRequest(Profile::Ptr))); + setNewViewMenu(profileMenu); + + _closeTabButton = new QToolButton(_containerWidget); + _closeTabButton->setFocusPolicy(Qt::NoFocus); + _closeTabButton->setIcon(KIcon("tab-close")); + _closeTabButton->setToolTip(i18nc("@info:tooltip", "Close tab")); + _closeTabButton->setWhatsThis(i18nc("@info:whatsthis", "Close the active tab")); + _closeTabButton->adjustSize(); + + // 'new tab' button is initially hidden. It will be shown when setFeatures() + // is called with the QuickNewView flag enabled. The 'close tab' is the same. + _newTabButton->setHidden(true); + _closeTabButton->setHidden(true); + + connect(_newTabButton, SIGNAL(clicked()), this, SIGNAL(newViewRequest())); + connect(_closeTabButton, SIGNAL(clicked()), this, SLOT(closeCurrentTab())); + + // Combine tab bar and 'new/close tab' buttons + _tabBarLayout = new QHBoxLayout; + _tabBarLayout->setSpacing(0); + _tabBarLayout->setContentsMargins(0, 0, 0, 0); + _tabBarLayout->addWidget(_newTabButton); + _tabBarLayout->addWidget(_tabBar); + _tabBarLayout->addWidget(_closeTabButton); + + // The search bar + searchBar()->setParent(_containerWidget); + + // The overall layout + _layout = new QVBoxLayout; + _layout->setSpacing(0); + _layout->setContentsMargins(0, 0, 0, 0); + + setNavigationPosition(position); + + _containerWidget->setLayout(_layout); +} +void TabbedViewContainer::setNewViewMenu(QMenu* menu) +{ + _newTabButton->setMenu(menu); +} +ViewContainer::Features TabbedViewContainer::supportedFeatures() const +{ + return QuickNewView | QuickCloseView; +} +void TabbedViewContainer::setFeatures(Features features) +{ + ViewContainer::setFeatures(features); + updateVisibilityOfQuickButtons(); +} + +void TabbedViewContainer::closeCurrentTab() +{ + if (_stackWidget->currentIndex() != -1) { + emit closeTab(this, _stackWidget->widget(_stackWidget->currentIndex())); + } +} + +void TabbedViewContainer::updateVisibilityOfQuickButtons() +{ + const bool tabBarHidden = _tabBar->isHidden(); + _newTabButton->setVisible(!tabBarHidden && (features() & QuickNewView)); + _closeTabButton->setVisible(!tabBarHidden && (features() & QuickCloseView)); +} + +void TabbedViewContainer::setTabBarVisible(bool visible) +{ + _tabBar->setVisible(visible); + updateVisibilityOfQuickButtons(); +} +QList TabbedViewContainer::supportedNavigationPositions() const +{ + return QList() << NavigationPositionTop << NavigationPositionBottom; +} + +void TabbedViewContainer::navigationPositionChanged(NavigationPosition position) +{ + // this method assumes that there are three or zero items in the layout + Q_ASSERT(_layout->count() == 3 || _layout->count() == 0); + + // clear all existing items from the layout + _layout->removeItem(_tabBarLayout); + _tabBarLayout->setParent(0); // suppress the warning of "already has a parent" + _layout->removeWidget(_stackWidget); + _layout->removeWidget(searchBar()); + + if (position == NavigationPositionTop) { + _layout->insertLayout(-1, _tabBarLayout); + _layout->insertWidget(-1, _stackWidget); + _layout->insertWidget(-1, searchBar()); + _tabBar->setShape(QTabBar::RoundedNorth); + } else if (position == NavigationPositionBottom) { + _layout->insertWidget(-1, _stackWidget); + _layout->insertWidget(-1, searchBar()); + _layout->insertLayout(-1, _tabBarLayout); + _tabBar->setShape(QTabBar::RoundedSouth); + } else { + Q_ASSERT(false); // should never reach here + } +} +void TabbedViewContainer::navigationVisibilityChanged(NavigationVisibility mode) +{ + if (mode == AlwaysShowNavigation && _tabBar->isHidden()) + setTabBarVisible(true); + + if (mode == AlwaysHideNavigation && !_tabBar->isHidden()) + setTabBarVisible(false); + + if (mode == ShowNavigationAsNeeded) + dynamicTabBarVisibility(); +} +void TabbedViewContainer::dynamicTabBarVisibility() +{ + if (_tabBar->count() > 1 && _tabBar->isHidden()) + setTabBarVisible(true); + + if (_tabBar->count() < 2 && !_tabBar->isHidden()) + setTabBarVisible(false); +} + +void TabbedViewContainer::setStyleSheet(const QString& styleSheet) +{ + _tabBar->setStyleSheet(styleSheet); +} + +void TabbedViewContainer::navigationTextModeChanged(bool useTextWidth) +{ + if (useTextWidth) { + _tabBar->setStyleSheet("QTabBar::tab { }"); + _tabBar->setExpanding(false); + _tabBar->setElideMode(Qt::ElideNone); + } else { + _tabBar->setStyleSheet("QTabBar::tab { min-width: 2em; max-width: 25em }"); + _tabBar->setExpanding(true); + _tabBar->setElideMode(Qt::ElideLeft); + } +} + +TabbedViewContainer::~TabbedViewContainer() +{ + if (!_containerWidget.isNull()) + _containerWidget->deleteLater(); +} + +void TabbedViewContainer::startTabDrag(int tab) +{ + QDrag* drag = new QDrag(_tabBar); + const QRect tabRect = _tabBar->tabRect(tab); + QPixmap tabPixmap = _tabBar->dragDropPixmap(tab); + + drag->setPixmap(tabPixmap); + + // offset the tab position so the tab will follow the cursor exactly + // where it was clicked (as opposed to centering on the origin of the pixmap) + QPoint mappedPos = _tabBar->mapFromGlobal(QCursor::pos()); + mappedPos.rx() -= tabRect.x(); + + drag->setHotSpot(mappedPos); + + const int id = viewProperties(views()[tab])->identifier(); + QWidget* view = views()[tab]; + drag->setMimeData(ViewProperties::createMimeData(id)); + + // start dragging + const Qt::DropAction action = drag->exec(); + + if (drag->target()) { + switch (action) { + case Qt::MoveAction: + // The MoveAction indicates the widget has been successfully + // moved into another tabbar/container, so remove the widget in + // current tabbar/container. + // + // Deleting the view may cause the view container to be deleted, + // which will also delete the QDrag object. This can cause a + // crash if Qt's internal drag-and-drop handling tries to delete + // it later. + // + // For now set the QDrag's parent to 0 so that it won't be + // deleted if this view container is destroyed. + // + // FIXME: Resolve this properly + drag->setParent(0); + removeView(view); + break; + case Qt::IgnoreAction: + // The IgnoreAction is used by the tabbar to indicate the + // special case of dropping one tab into its existing position. + // So nothing need to do here. + break; + default: + break; + } + } else { + // if the tab is dragged onto something that does not accept this + // drop(for example, a different application or a different konsole + // process), then detach the tab to achieve the effect of "dragging tab + // out of current window and into its own window" + // + // It feels unnatural to do the detach when this is only one tab in the + // tabbar + if (_tabBar->count() > 1) + emit detachTab(this, view); + } +} + +void TabbedViewContainer::querySourceIndex(const QDropEvent* event, int& sourceIndex) +{ + const int droppedId = ViewProperties::decodeMimeData(event->mimeData()); + + const QList viewList = views(); + const int count = viewList.count(); + int index = -1; + for (index = 0; index < count; index++) { + const int id = viewProperties(viewList[index])->identifier(); + if (id == droppedId) + break; + } + + sourceIndex = index; +} + +void TabbedViewContainer::onMoveViewRequest(int index, const QDropEvent* event ,bool& success, TabbedViewContainer* sourceTabbedContainer) +{ + const int droppedId = ViewProperties::decodeMimeData(event->mimeData()); + emit moveViewRequest(index, droppedId, success, sourceTabbedContainer); +} + +void TabbedViewContainer::tabDoubleClicked(int index) +{ + renameTab(index); +} + +void TabbedViewContainer::renameTab(int index) +{ + viewProperties(views()[index])->rename(); +} + +void TabbedViewContainer::openTabContextMenu(int index, const QPoint& pos) +{ + _contextMenuTabIndex = index; + + // Enable 'Detach Tab' menu item only if there is more than 1 tab + // Note: the code is coupled with that action's position within the menu + QAction* detachAction = _contextPopupMenu->actions().first(); + detachAction->setEnabled(_tabBar->count() > 1); + + _contextPopupMenu->exec(pos); +} + +void TabbedViewContainer::tabContextMenuCloseTab() +{ + _tabBar->setCurrentIndex(_contextMenuTabIndex);// Required for this to work + emit closeTab(this, _stackWidget->widget(_contextMenuTabIndex)); +} + +void TabbedViewContainer::tabContextMenuDetachTab() +{ + emit detachTab(this, _stackWidget->widget(_contextMenuTabIndex)); +} + +void TabbedViewContainer::tabContextMenuRenameTab() +{ + renameTab(_contextMenuTabIndex); +} + +void TabbedViewContainer::moveViewWidget(int fromIndex , int toIndex) +{ + QString text = _tabBar->tabText(fromIndex); + QIcon icon = _tabBar->tabIcon(fromIndex); + + // FIXME - This will lose properties of the tab other than + // their text and icon when moving them + + _tabBar->removeTab(fromIndex); + _tabBar->insertTab(toIndex, icon, text); + + QWidget* widget = _stackWidget->widget(fromIndex); + _stackWidget->removeWidget(widget); + _stackWidget->insertWidget(toIndex, widget); +} +void TabbedViewContainer::currentTabChanged(int index) +{ + _stackWidget->setCurrentIndex(index); + if (_stackWidget->widget(index)) + emit activeViewChanged(_stackWidget->widget(index)); + + // clear activity indicators + setTabActivity(index, false); +} + +void TabbedViewContainer::wheelScrolled(int delta) +{ + if (delta < 0) + activateNextView(); + else + activatePreviousView(); +} + +QWidget* TabbedViewContainer::containerWidget() const +{ + return _containerWidget; +} +QWidget* TabbedViewContainer::activeView() const +{ + return _stackWidget->currentWidget(); +} +void TabbedViewContainer::setActiveView(QWidget* view) +{ + const int index = _stackWidget->indexOf(view); + + Q_ASSERT(index != -1); + + _stackWidget->setCurrentWidget(view); + _tabBar->setCurrentIndex(index); +} +void TabbedViewContainer::addViewWidget(QWidget* view , int index) +{ + _stackWidget->insertWidget(index, view); + _stackWidget->updateGeometry(); + + ViewProperties* item = viewProperties(view); + connect(item, SIGNAL(titleChanged(ViewProperties*)), this , + SLOT(updateTitle(ViewProperties*))); + connect(item, SIGNAL(iconChanged(ViewProperties*)), this , + SLOT(updateIcon(ViewProperties*))); + connect(item, SIGNAL(activity(ViewProperties*)), this , + SLOT(updateActivity(ViewProperties*))); + + _tabBar->insertTab(index , item->icon() , item->title()); + + if (navigationVisibility() == ShowNavigationAsNeeded) + dynamicTabBarVisibility(); +} + +void TabbedViewContainer::removeViewWidget(QWidget* view) +{ + if (!_stackWidget) + return; + _stackWidget->removeWidget(view); +} + +void TabbedViewContainer::widgetRemoved(int index) +{ + Q_ASSERT(index != -1); + + _tabBar->removeTab(index); + + if (navigationVisibility() == ShowNavigationAsNeeded) + dynamicTabBarVisibility(); +} + +void TabbedViewContainer::setTabActivity(int index , bool activity) +{ + const QPalette& palette = _tabBar->palette(); + KColorScheme colorScheme(palette.currentColorGroup()); + const QColor colorSchemeActive = colorScheme.foreground(KColorScheme::ActiveText).color(); + + const QColor normalColor = palette.text().color(); + const QColor activityColor = KColorUtils::mix(normalColor, colorSchemeActive); + + QColor color = activity ? activityColor : QColor(); + + if (color != _tabBar->tabTextColor(index)) + _tabBar->setTabTextColor(index, color); +} + +void TabbedViewContainer::updateActivity(ViewProperties* item) +{ + foreach(QWidget* widget, widgetsForItem(item)) { + const int index = _stackWidget->indexOf(widget); + + if (index != _stackWidget->currentIndex()) { + setTabActivity(index, true); + } + } +} + +void TabbedViewContainer::updateTitle(ViewProperties* item) +{ + foreach(QWidget* widget, widgetsForItem(item)) { + const int index = _stackWidget->indexOf(widget); + QString tabText = item->title(); + + _tabBar->setTabToolTip(index , tabText); + + // To avoid having & replaced with _ (shortcut indicator) + tabText.replace('&', "&&"); + _tabBar->setTabText(index , tabText); + } +} +void TabbedViewContainer::updateIcon(ViewProperties* item) +{ + foreach(QWidget* widget, widgetsForItem(item)) { + const int index = _stackWidget->indexOf(widget); + _tabBar->setTabIcon(index , item->icon()); + } +} + +ViewManager* TabbedViewContainer::connectedViewManager() +{ + return _connectedViewManager; +} + +StackedViewContainer::StackedViewContainer(QObject* parent) + : ViewContainer(NavigationPositionTop, parent) +{ + _containerWidget = new QWidget; + QVBoxLayout* layout = new QVBoxLayout(_containerWidget); + + _stackWidget = new QStackedWidget(_containerWidget); + + searchBar()->setParent(_containerWidget); + layout->addWidget(searchBar()); + layout->addWidget(_stackWidget); + layout->setContentsMargins(0, 0, 0, 0); +} +StackedViewContainer::~StackedViewContainer() +{ + if (!_containerWidget.isNull()) + _containerWidget->deleteLater(); +} +QWidget* StackedViewContainer::containerWidget() const +{ + return _containerWidget; +} +QWidget* StackedViewContainer::activeView() const +{ + return _stackWidget->currentWidget(); +} +void StackedViewContainer::setActiveView(QWidget* view) +{ + _stackWidget->setCurrentWidget(view); +} +void StackedViewContainer::addViewWidget(QWidget* view , int) +{ + _stackWidget->addWidget(view); +} +void StackedViewContainer::removeViewWidget(QWidget* view) +{ + if (!_stackWidget) + return; + _stackWidget->removeWidget(view); +} + +#include "moc_ViewContainer.cpp" diff --git a/konsole/src/ViewContainer.h b/konsole/src/ViewContainer.h new file mode 100644 index 00000000..f60fa347 --- /dev/null +++ b/konsole/src/ViewContainer.h @@ -0,0 +1,457 @@ +/* + This file is part of the Konsole Terminal. + + Copyright 2006-2008 Robert Knight + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +#ifndef VIEWCONTAINER_H +#define VIEWCONTAINER_H + +// Qt +#include +#include +#include +#include + +// Konsole +#include "Profile.h" + +#include +#include +#include +#include + +// TabbedViewContainer +// Qt +#include +#include +#include +#include + +// KDE +class KMenu; + +namespace Konsole +{ +class IncrementalSearchBar; +class ViewProperties; +class TabbedViewContainer; +/** + * An interface for container widgets which can hold one or more views. + * + * The container widget typically displays a list of the views which + * it has and provides a means of switching between them. + * + * Subclasses should reimplement the addViewWidget() and removeViewWidget() functions + * to actually add or remove view widgets from the container widget, as well + * as updating any navigation aids. + */ +class ViewContainer : public QObject +{ + Q_OBJECT + +public: + /** + * This enum describes the options for positioning the + * container's navigation widget. + */ + enum NavigationPosition { + /** Position the navigation widget above the views. */ + NavigationPositionTop, + /** Position the navigation widget below the views. */ + NavigationPositionBottom, + /** Position the navigation widget to the left of the views. */ + NavigationPositionLeft, + /** Position the navigation widget to the right of the views. */ + NavigationPositionRight + }; + + /** + * Constructs a new view container with the specified parent. + * + * @param position The initial position of the navigation widget + * @param parent The parent object of the container + */ + ViewContainer(NavigationPosition position , QObject* parent); + + /** + * Called when the ViewContainer is destroyed. When reimplementing this in + * subclasses, use object->deleteLater() to delete any widgets or other objects + * instead of 'delete object'. + */ + virtual ~ViewContainer(); + + /** Returns the widget which contains the view widgets */ + virtual QWidget* containerWidget() const = 0; + + /** + * This enum describes the options for showing or hiding the + * container's navigation widget. + */ + enum NavigationVisibility { + /** Always show the navigation widget. */ + AlwaysShowNavigation, + /** Show the navigation widget only when the container has more than one view. */ + ShowNavigationAsNeeded, + /** Always hide the navigation widget. */ + AlwaysHideNavigation + }; + /* + * Sets the visibility of the view container's navigation widget. + * + * The ViewContainer sub-class is responsible for ensuring that this + * setting is respected as views are added or removed from the + * container. + * + * ViewContainer sub-classes should reimplement the + * navigationVisibilityChanged() method to respond to changes + * of this property. + */ + void setNavigationVisibility(NavigationVisibility mode); + /** + * Returns the current mode for controlling the visibility of the + * the view container's navigation widget. + */ + NavigationVisibility navigationVisibility() const; + + /** + * Sets the position of the navigation widget with + * respect to the main content area. + * + * Depending on the ViewContainer subclass, not all + * positions from the NavigationPosition enum may be + * supported. A list of supported positions can be + * obtained by calling supportedNavigationPositions() + * + * ViewContainer sub-classes should re-implement the + * navigationPositionChanged() method to respond + * to changes of this property. + */ + void setNavigationPosition(NavigationPosition position); + + /** + * Returns the position of the navigation widget with + * respect to the main content area. + */ + NavigationPosition navigationPosition() const; + + /** + * Returns the list of supported navigation positions. + * The supported positions will depend upon the type of the + * navigation widget used by the ViewContainer subclass. + * + * The base implementation returns one item, NavigationPositionTop + */ + virtual QList supportedNavigationPositions() const; + + /** Sets the navigation text mode + * If mode is true, use the width of the title; otherwise use the + * default width calculations. + */ + void setNavigationTextMode(bool mode); + + /** Sets the stylesheet for visual appearance + * + * The default implementation does nothing. + */ + virtual void setStyleSheet(const QString& styleSheet) { + Q_UNUSED(styleSheet); + } + + /** Adds a new view to the container widget */ + void addView(QWidget* view , ViewProperties* navigationItem, int index = -1); + + /** Removes a view from the container */ + void removeView(QWidget* view); + + /** Returns the ViewProperties instance associated with a particular view in the container */ + ViewProperties* viewProperties(QWidget* view) const; + + /** Returns a list of the contained views */ + const QList views() const; + + /** + * Returns the view which currently has the focus or 0 if none + * of the child views have the focus. + */ + virtual QWidget* activeView() const = 0; + + /** + * Changes the focus to the specified view and updates + * navigation aids to reflect the change. + */ + virtual void setActiveView(QWidget* widget) = 0; + + /** + * @return the search widget for this view + */ + IncrementalSearchBar* searchBar(); + + /** Changes the active view to the next view */ + void activateNextView(); + + /** Changes the active view to the previous view */ + void activatePreviousView(); + + /** Changes the active view to the last view */ + void activateLastView(); + + /** + * This enum describes the directions + * in which views can be re-arranged within the container + * using the moveActiveView() method. + */ + enum MoveDirection { + /** Moves the view to the left. */ + MoveViewLeft, + /** Moves the view to the right. */ + MoveViewRight + }; + + /** + * Moves the active view within the container and + * updates the order in which the views are shown + * in the container's navigation widget. + * + * The default implementation does nothing. + */ + void moveActiveView(MoveDirection direction); + + /** Enum describing extra UI features which can be + * provided by the container. */ + enum Feature { + /** Provides a button which can be clicked to create new views quickly. + * When the button is clicked, a newViewRequest() signal is emitted. */ + QuickNewView = 1, + /** Provides a button which can be clicked to close views quickly. */ + QuickCloseView = 2 + }; + Q_DECLARE_FLAGS(Features, Feature) + /** + * Sets which additional features are enabled in this container. + * The default implementation does thing. Sub-classes should re-implement this + * to hide or show the relevant parts of their UI + */ + virtual void setFeatures(Features features); + /** Returns a bitwise-OR of enabled extra UI features. See setFeatures() */ + Features features() const; + /** Returns a bitwise-OR of supported extra UI features. The default + * implementation returns 0 (no extra features) */ + virtual Features supportedFeatures() const { + return 0; + } + /** Sets the menu to be shown when the new view button is clicked. + * Only valid if the QuickNewView feature is enabled. + * The default implementation does nothing. */ + virtual void setNewViewMenu(QMenu* menu) { + Q_UNUSED(menu); + } + +signals: + /** Emitted when the container is deleted */ + void destroyed(ViewContainer* container); + + /** Emitted when the container has no more children */ + void empty(ViewContainer* container); + + /** Emitted when the user requests to open a new view */ + void newViewRequest(); + + /** Requests creation of a new view, with the selected profile. */ + void newViewRequest(Profile::Ptr); + + /** + * Emitted when the user requests to move a view from another container + * into this container. If 'success' is set to true by a connected slot + * then the original view will be removed. + * + * @param index Index at which to insert the new view in the container or -1 + * to append it. This index should be passed to addView() when the new view + * has been created. + * @param id The identifier of the view. + * @param success The slot handling this signal should set this to true if the + * new view was successfully created. + * @param sourceContainer Initial move event Tabbed view container. + */ + void moveViewRequest(int index, int id, bool& success, TabbedViewContainer* sourceContainer); + + /** Emitted when the active view changes */ + void activeViewChanged(QWidget* view); + + /** Emitted when a view is added to the container. */ + void viewAdded(QWidget* view , ViewProperties* properties); + + /** Emitted when a view is removed from the container. */ + void viewRemoved(QWidget* view); + +protected: + /** + * Performs the task of adding the view widget + * to the container widget. + */ + virtual void addViewWidget(QWidget* view, int index) = 0; + /** + * Performs the task of removing the view widget + * from the container widget. + */ + virtual void removeViewWidget(QWidget* view) = 0; + + /** + * Called when the navigation display mode changes. + * See setNavigationVisibility + */ + virtual void navigationVisibilityChanged(NavigationVisibility) {} + + /** + * Called when the navigation position changes to re-layout + * the container and place the navigation widget in the + * specified position. + */ + virtual void navigationPositionChanged(NavigationPosition) {} + + virtual void navigationTextModeChanged(bool) {} + + /** Returns the widgets which are associated with a particular navigation item */ + QList widgetsForItem(ViewProperties* item) const; + + /** + * Rearranges the order of widgets in the container. + * + * @param fromIndex Current index of the widget to move + * @param toIndex New index for the widget + */ + virtual void moveViewWidget(int fromIndex , int toIndex); + +private slots: + void viewDestroyed(QObject* view); + void searchBarDestroyed(); + +private: + void forgetView(QWidget* view); + + NavigationVisibility _navigationVisibility; + NavigationPosition _navigationPosition; + QList _views; + QHash _navigation; + Features _features; + IncrementalSearchBar* _searchBar; +}; +Q_DECLARE_OPERATORS_FOR_FLAGS(ViewContainer::Features) + +class ViewContainerTabBar; +class ViewManager; + +/** + * An alternative tabbed view container which uses a QTabBar and QStackedWidget + * combination for navigation instead of QTabWidget + */ +class TabbedViewContainer : public ViewContainer +{ + Q_OBJECT + +public: + /** + * Constructs a new tabbed view container. Supported positions + * are NavigationPositionTop and NavigationPositionBottom. + */ + TabbedViewContainer(NavigationPosition position, ViewManager* connectedViewManager, QObject* parent); + virtual ~TabbedViewContainer(); + + virtual QWidget* containerWidget() const; + virtual QWidget* activeView() const; + virtual void setActiveView(QWidget* view); + virtual QList supportedNavigationPositions() const; + virtual void setFeatures(Features features); + virtual Features supportedFeatures() const; + virtual void setNewViewMenu(QMenu* menu); + virtual void setStyleSheet(const QString& styleSheet); + + // return associated view manager + ViewManager* connectedViewManager(); + +protected: + virtual void addViewWidget(QWidget* view , int index); + virtual void removeViewWidget(QWidget* view); + virtual void navigationVisibilityChanged(NavigationVisibility mode); + virtual void navigationPositionChanged(NavigationPosition position); + virtual void navigationTextModeChanged(bool mode); + virtual void moveViewWidget(int fromIndex , int toIndex); + +private slots: + void updateTitle(ViewProperties* item); + void updateIcon(ViewProperties* item); + void updateActivity(ViewProperties* item); + void currentTabChanged(int index); + void closeCurrentTab(); + void wheelScrolled(int delta); + + void tabDoubleClicked(int index); + void openTabContextMenu(int index, const QPoint& point); + void tabContextMenuCloseTab(); + void tabContextMenuRenameTab(); + void tabContextMenuDetachTab(); + void startTabDrag(int index); + void querySourceIndex(const QDropEvent* event, int& sourceIndex); + void onMoveViewRequest(int index, const QDropEvent* event, bool& success, TabbedViewContainer* sourceTabbedContainer); + +signals: + void detachTab(ViewContainer * self, QWidget * activeView); + void closeTab(ViewContainer * self, QWidget * activeView); +private slots: + void widgetRemoved(int index); + +private: + void dynamicTabBarVisibility(); + void setTabBarVisible(bool visible); + void setTabActivity(int index, bool activity); + void renameTab(int index); + void updateVisibilityOfQuickButtons(); + + ViewContainerTabBar* _tabBar; + QPointer _stackWidget; + QPointer _containerWidget; + ViewManager* _connectedViewManager; + QVBoxLayout* _layout; + QHBoxLayout* _tabBarLayout; + QToolButton* _newTabButton; + QToolButton* _closeTabButton; + int _contextMenuTabIndex; + KMenu* _contextPopupMenu; +}; + +/** A plain view container with no navigation display */ +class StackedViewContainer : public ViewContainer +{ +public: + explicit StackedViewContainer(QObject* parent); + virtual ~StackedViewContainer(); + + virtual QWidget* containerWidget() const; + virtual QWidget* activeView() const; + virtual void setActiveView(QWidget* view); + +protected: + virtual void addViewWidget(QWidget* view , int index); + virtual void removeViewWidget(QWidget* view); + +private: + QPointer _containerWidget; + QPointer _stackWidget; +}; +} +#endif //VIEWCONTAINER_H diff --git a/konsole/src/ViewContainerTabBar.cpp b/konsole/src/ViewContainerTabBar.cpp new file mode 100644 index 00000000..0d43636b --- /dev/null +++ b/konsole/src/ViewContainerTabBar.cpp @@ -0,0 +1,230 @@ +/* + This file is part of the Konsole Terminal. + + Copyright 2006-2008 Robert Knight + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +// Own +#include "ViewContainerTabBar.h" +#include "ViewContainer.h" + +// Qt +#include +#include +#include +#include +#include + +// KDE +#include +#include + +using Konsole::ViewContainerTabBar; +using Konsole::TabbedViewContainer; + +ViewContainerTabBar::ViewContainerTabBar(QWidget* parent, TabbedViewContainer* container) + : KTabBar(parent) + , _dropIndicator(0) + , _dropIndicatorIndex(-1) + , _drawIndicatorDisabled(false) + , _connectedContainer(container) +{ + setDrawBase(true); + setDocumentMode(true); + setFocusPolicy(Qt::NoFocus); + setSelectionBehaviorOnRemove(QTabBar::SelectPreviousTab); + setElideMode(Qt::ElideLeft); + + setWhatsThis(i18nc("@info:whatsthis", + "Tab Bar" + "The tab bar allows you to switch and move tabs. You can double-click a tab to change its name.")); +} + +void ViewContainerTabBar::dragEnterEvent(QDragEnterEvent* event) +{ + if (event->mimeData()->hasFormat(_supportedMimeType) && + event->source() != 0) + event->acceptProposedAction(); +} + +void ViewContainerTabBar::dragLeaveEvent(QDragLeaveEvent*) +{ + setDropIndicator(-1); +} + +void ViewContainerTabBar::dragMoveEvent(QDragMoveEvent* event) +{ + if (event->mimeData()->hasFormat(_supportedMimeType) + && event->source() != 0) { + int index = dropIndex(event->pos()); + if (index == -1) + index = count(); + + setDropIndicator(index, proposedDropIsSameTab(event)); + + event->acceptProposedAction(); + } +} + +void ViewContainerTabBar::dropEvent(QDropEvent* event) +{ + setDropIndicator(-1); + + if (!event->mimeData()->hasFormat(_supportedMimeType)) { + event->ignore(); + return; + } + + if (proposedDropIsSameTab(event)) { + event->setDropAction(Qt::IgnoreAction); + event->accept(); + return; + } + + const int index = dropIndex(event->pos()); + bool success = false; + + ViewContainerTabBar* sourceContainerTabBar = static_cast(event->source()); + + // check if the moved tab is the last of source view. + if (sourceContainerTabBar->count() == 1) { + TabbedViewContainer* sourceTabbedContainer = sourceContainerTabBar->connectedTabbedViewContainer(); + emit moveViewRequest(index, event, success, sourceTabbedContainer); + } else { + emit moveViewRequest(index, event, success, NULL); + } + + if (success) + event->accept(); + else + event->ignore(); +} + +TabbedViewContainer* ViewContainerTabBar::connectedTabbedViewContainer() +{ + return _connectedContainer; +} + +void ViewContainerTabBar::setDropIndicator(int index, bool drawDisabled) +{ + if (!parentWidget() || _dropIndicatorIndex == index) + return; + + _dropIndicatorIndex = index; + const int ARROW_SIZE = 32; + const bool north = shape() == KTabBar::RoundedNorth || shape() == KTabBar::TriangularNorth; + + if (!_dropIndicator || _drawIndicatorDisabled != drawDisabled) { + if (!_dropIndicator) { + _dropIndicator = new QLabel(parentWidget()); + _dropIndicator->resize(ARROW_SIZE, ARROW_SIZE); + } + + QIcon::Mode drawMode = drawDisabled ? QIcon::Disabled : QIcon::Normal; + const QString iconName = north ? "arrow-up" : "arrow-down"; + _dropIndicator->setPixmap(KIcon(iconName).pixmap(ARROW_SIZE, ARROW_SIZE, drawMode)); + _drawIndicatorDisabled = drawDisabled; + } + + if (index < 0) { + _dropIndicator->hide(); + return; + } + + const QRect rect = tabRect(index < count() ? index : index - 1); + + QPoint pos; + if (index < count()) + pos = rect.topLeft(); + else + pos = rect.topRight(); + + if (north) + pos.ry() += ARROW_SIZE; + else + pos.ry() -= ARROW_SIZE; + + pos.rx() -= ARROW_SIZE / 2; + + _dropIndicator->move(mapTo(parentWidget(), pos)); + _dropIndicator->show(); +} + +void ViewContainerTabBar::setSupportedMimeType(const QString& mimeType) +{ + _supportedMimeType = mimeType; +} + +int ViewContainerTabBar::dropIndex(const QPoint& pos) const +{ + int tab = tabAt(pos); + if (tab < 0) + return tab; + + // pick the closest tab boundary + QRect rect = tabRect(tab); + if ((pos.x() - rect.left()) > (rect.width() / 2)) + tab++; + + if (tab == count()) + return -1; + + return tab; +} + +bool ViewContainerTabBar::proposedDropIsSameTab(const QDropEvent* event) const +{ + const bool sameTabBar = event->source() == this; + if (!sameTabBar) + return false; + + const int index = dropIndex(event->pos()); + int sourceIndex = -1; + emit querySourceIndex(event, sourceIndex); + + const bool sourceAndDropAreLast = sourceIndex == count() - 1 && index == -1; + if (sourceIndex == index || sourceIndex == index - 1 || sourceAndDropAreLast) + return true; + else + return false; +} + + +QPixmap ViewContainerTabBar::dragDropPixmap(int tab) +{ + Q_ASSERT(tab >= 0 && tab < count()); + + // TODO - grabWidget() works except that it includes part + // of the tab bar outside the tab itself if the tab has + // curved corners + const QRect rect = tabRect(tab); + const int borderWidth = 1; + + QPixmap tabPixmap(rect.width() + borderWidth, + rect.height() + borderWidth); + QPainter painter(&tabPixmap); + painter.drawPixmap(0, 0, QPixmap::grabWidget(this, rect)); + QPen borderPen; + borderPen.setBrush(palette().dark()); + borderPen.setWidth(borderWidth); + painter.setPen(borderPen); + painter.drawRect(0, 0, rect.width(), rect.height()); + painter.end(); + + return tabPixmap; +} diff --git a/konsole/src/ViewContainerTabBar.h b/konsole/src/ViewContainerTabBar.h new file mode 100644 index 00000000..69097702 --- /dev/null +++ b/konsole/src/ViewContainerTabBar.h @@ -0,0 +1,81 @@ +/* + This file is part of the Konsole Terminal. + + Copyright 2006-2008 Robert Knight + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +#ifndef VIEWCONTAINERTABBAR_H +#define VIEWCONTAINERTABBAR_H + +// KDE +#include + +#include + +namespace Konsole +{ +class TabbedViewContainer; + +class ViewContainerTabBar : public KTabBar +{ + Q_OBJECT + +public: + ViewContainerTabBar(QWidget* parent, TabbedViewContainer* container); + + // returns a pixmap image of a tab for use with QDrag + QPixmap dragDropPixmap(int tab); + + // set the mimetype of which the tabbar support d&d + void setSupportedMimeType(const QString& mimeType); + + // return associated tabbed container + TabbedViewContainer* connectedTabbedViewContainer(); + +signals: + void querySourceIndex(const QDropEvent* event, int& sourceIndex) const; + void moveViewRequest(int index, const QDropEvent* event, bool& success, TabbedViewContainer* sourceTabbedContainer); + +protected: + virtual void dragEnterEvent(QDragEnterEvent* event); + virtual void dragLeaveEvent(QDragLeaveEvent* event); + virtual void dragMoveEvent(QDragMoveEvent* event); + virtual void dropEvent(QDropEvent* event); + +private: + // show the indicator arrow which shows where a dropped tab will + // be inserted at 'index' + void setDropIndicator(int index, bool drawDisabled = false); + + // returns the index at which a tab will be inserted if the mouse + // in a drag-drop operation is released at 'pos' + int dropIndex(const QPoint& pos) const; + + // returns true if the tab to be dropped in a drag-drop operation + // is the same as the tab at the drop location + bool proposedDropIsSameTab(const QDropEvent* event) const; + + QLabel* _dropIndicator; + int _dropIndicatorIndex; + bool _drawIndicatorDisabled; + QString _supportedMimeType; + TabbedViewContainer* _connectedContainer; +}; +} + +#endif // VIEWCONTAINERTABBAR_H diff --git a/konsole/src/ViewManager.cpp b/konsole/src/ViewManager.cpp new file mode 100644 index 00000000..729bf925 --- /dev/null +++ b/konsole/src/ViewManager.cpp @@ -0,0 +1,1114 @@ +/* + Copyright 2006-2008 by Robert Knight + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +// Own +#include "ViewManager.h" + +// Qt +#include +#include +#include +#include + +// KDE +#include +#include +#include +#include +#include + +// Konsole +#include + +#include "ColorScheme.h" +#include "ColorSchemeManager.h" +#include "Session.h" +#include "TerminalDisplay.h" +#include "SessionController.h" +#include "SessionManager.h" +#include "ProfileManager.h" +#include "ViewContainer.h" +#include "ViewSplitter.h" +#include "Profile.h" +#include "Enumeration.h" + +using namespace Konsole; + +int ViewManager::lastManagerId = 0; + +ViewManager::ViewManager(QObject* parent , KActionCollection* collection) + : QObject(parent) + , _viewSplitter(0) + , _actionCollection(collection) + , _containerSignalMapper(new QSignalMapper(this)) + , _navigationMethod(TabbedNavigation) + , _navigationVisibility(ViewContainer::AlwaysShowNavigation) + , _navigationPosition(ViewContainer::NavigationPositionTop) + , _showQuickButtons(false) + , _newTabBehavior(PutNewTabAtTheEnd) + , _navigationStyleSheet(QString()) + , _managerId(0) +{ + // create main view area + _viewSplitter = new ViewSplitter(0); + KAcceleratorManager::setNoAccel(_viewSplitter); + + // the ViewSplitter class supports both recursive and non-recursive splitting, + // in non-recursive mode, all containers are inserted into the same top-level splitter + // widget, and all the divider lines between the containers have the same orientation + // + // the ViewManager class is not currently able to handle a ViewSplitter in recursive-splitting + // mode + _viewSplitter->setRecursiveSplitting(false); + _viewSplitter->setFocusPolicy(Qt::NoFocus); + + // setup actions which are related to the views + setupActions(); + + // emit a signal when all of the views held by this view manager are destroyed + connect(_viewSplitter , SIGNAL(allContainersEmpty()) , this , SIGNAL(empty())); + connect(_viewSplitter , SIGNAL(empty(ViewSplitter*)) , this , SIGNAL(empty())); + + // listen for addition or removal of views from associated containers + connect(_containerSignalMapper , SIGNAL(mapped(QObject*)) , this , + SLOT(containerViewsChanged(QObject*))); + + // listen for profile changes + connect(ProfileManager::instance() , SIGNAL(profileChanged(Profile::Ptr)) , this, + SLOT(profileChanged(Profile::Ptr))); + connect(SessionManager::instance() , SIGNAL(sessionUpdated(Session*)) , this, + SLOT(updateViewsForSession(Session*))); + + //prepare DBus communication + new WindowAdaptor(this); + // TODO: remove this obsolete and bad name + QDBusConnection::sessionBus().registerObject(QLatin1String("/Konsole"), this); + + _managerId = ++lastManagerId; + QDBusConnection::sessionBus().registerObject(QLatin1String("/Windows/") + QString::number(_managerId), this); +} + +ViewManager::~ViewManager() +{ +} + +int ViewManager::managerId() const +{ + return _managerId; +} + +QWidget* ViewManager::activeView() const +{ + ViewContainer* container = _viewSplitter->activeContainer(); + if (container) { + return container->activeView(); + } else { + return 0; + } +} + +QWidget* ViewManager::widget() const +{ + return _viewSplitter; +} + +void ViewManager::setupActions() +{ + KActionCollection* collection = _actionCollection; + + KAction* nextViewAction = new KAction(i18nc("@action Shortcut entry", "Next Tab") , this); + KAction* previousViewAction = new KAction(i18nc("@action Shortcut entry", "Previous Tab") , this); + KAction* lastViewAction = new KAction(i18nc("@action Shortcut entry", "Switch to Last Tab") , this); + KAction* nextContainerAction = new KAction(i18nc("@action Shortcut entry", "Next View Container") , this); + + KAction* moveViewLeftAction = new KAction(i18nc("@action Shortcut entry", "Move Tab Left") , this); + KAction* moveViewRightAction = new KAction(i18nc("@action Shortcut entry", "Move Tab Right") , this); + + // list of actions that should only be enabled when there are multiple view + // containers open + QList multiViewOnlyActions; + multiViewOnlyActions << nextContainerAction; + + if (collection) { + KAction* splitLeftRightAction = new KAction(KIcon("view-split-left-right"), + i18nc("@action:inmenu", "Split View Left/Right"), + this); + splitLeftRightAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_ParenLeft)); + collection->addAction("split-view-left-right", splitLeftRightAction); + connect(splitLeftRightAction , SIGNAL(triggered()) , this , SLOT(splitLeftRight())); + + KAction* splitTopBottomAction = new KAction(KIcon("view-split-top-bottom") , + i18nc("@action:inmenu", "Split View Top/Bottom"), this); + splitTopBottomAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_ParenRight)); + collection->addAction("split-view-top-bottom", splitTopBottomAction); + connect(splitTopBottomAction , SIGNAL(triggered()) , this , SLOT(splitTopBottom())); + + KAction* closeActiveAction = new KAction(i18nc("@action:inmenu Close Active View", "Close Active") , this); + closeActiveAction->setIcon(KIcon("view-close")); + closeActiveAction->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_S)); + closeActiveAction->setEnabled(false); + collection->addAction("close-active-view", closeActiveAction); + connect(closeActiveAction , SIGNAL(triggered()) , this , SLOT(closeActiveContainer())); + + multiViewOnlyActions << closeActiveAction; + + KAction* closeOtherAction = new KAction(i18nc("@action:inmenu Close Other Views", "Close Others") , this); + closeOtherAction->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_O)); + closeOtherAction->setEnabled(false); + collection->addAction("close-other-views", closeOtherAction); + connect(closeOtherAction , SIGNAL(triggered()) , this , SLOT(closeOtherContainers())); + + multiViewOnlyActions << closeOtherAction; + + // Expand & Shrink Active View + KAction* expandActiveAction = new KAction(i18nc("@action:inmenu", "Expand View") , this); + expandActiveAction->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_BracketRight)); + expandActiveAction->setEnabled(false); + collection->addAction("expand-active-view", expandActiveAction); + connect(expandActiveAction , SIGNAL(triggered()) , this , SLOT(expandActiveContainer())); + + multiViewOnlyActions << expandActiveAction; + + KAction* shrinkActiveAction = new KAction(i18nc("@action:inmenu", "Shrink View") , this); + shrinkActiveAction->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_BracketLeft)); + shrinkActiveAction->setEnabled(false); + collection->addAction("shrink-active-view", shrinkActiveAction); + connect(shrinkActiveAction , SIGNAL(triggered()) , this , SLOT(shrinkActiveContainer())); + + multiViewOnlyActions << shrinkActiveAction; + + KAction* detachViewAction = collection->addAction("detach-view"); + detachViewAction->setIcon(KIcon("tab-detach")); + detachViewAction->setText(i18nc("@action:inmenu", "D&etach Current Tab")); + // Ctrl+Shift+D is not used as a shortcut by default because it is too close + // to Ctrl+D - which will terminate the session in many cases + detachViewAction->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_H)); + + connect(this , SIGNAL(splitViewToggle(bool)) , this , SLOT(updateDetachViewState())); + connect(detachViewAction , SIGNAL(triggered()) , this , SLOT(detachActiveView())); + + // Next / Previous View , Next Container + collection->addAction("next-view", nextViewAction); + collection->addAction("previous-view", previousViewAction); + collection->addAction("last-tab", lastViewAction); + collection->addAction("next-container", nextContainerAction); + collection->addAction("move-view-left", moveViewLeftAction); + collection->addAction("move-view-right", moveViewRightAction); + + // Switch to tab N shortcuts + const int SWITCH_TO_TAB_COUNT = 19; + QSignalMapper* switchToTabMapper = new QSignalMapper(this); + connect(switchToTabMapper, SIGNAL(mapped(int)), this, SLOT(switchToView(int))); + for (int i = 0; i < SWITCH_TO_TAB_COUNT; i++) { + KAction* switchToTabAction = new KAction(i18nc("@action Shortcut entry", "Switch to Tab %1", i + 1), this); + switchToTabMapper->setMapping(switchToTabAction, i); + connect(switchToTabAction, SIGNAL(triggered()), switchToTabMapper, + SLOT(map())); + collection->addAction(QString("switch-to-tab-%1").arg(i), switchToTabAction); + } + } + + foreach(QAction* action, multiViewOnlyActions) { + connect(this , SIGNAL(splitViewToggle(bool)) , action , SLOT(setEnabled(bool))); + } + + // keyboard shortcut only actions + nextViewAction->setShortcut(QKeySequence(Qt::SHIFT + Qt::Key_Right)); + connect(nextViewAction, SIGNAL(triggered()) , this , SLOT(nextView())); + _viewSplitter->addAction(nextViewAction); + + previousViewAction->setShortcut(QKeySequence(Qt::SHIFT + Qt::Key_Left)); + connect(previousViewAction, SIGNAL(triggered()) , this , SLOT(previousView())); + _viewSplitter->addAction(previousViewAction); + + nextContainerAction->setShortcut(QKeySequence(Qt::SHIFT + Qt::Key_Tab)); + connect(nextContainerAction , SIGNAL(triggered()) , this , SLOT(nextContainer())); + _viewSplitter->addAction(nextContainerAction); + + moveViewLeftAction->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_Left)); + connect(moveViewLeftAction , SIGNAL(triggered()) , this , SLOT(moveActiveViewLeft())); + _viewSplitter->addAction(moveViewLeftAction); + + moveViewRightAction->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_Right)); + connect(moveViewRightAction , SIGNAL(triggered()) , this , SLOT(moveActiveViewRight())); + _viewSplitter->addAction(moveViewRightAction); + + connect(lastViewAction, SIGNAL(triggered()) , this , SLOT(lastView())); + _viewSplitter->addAction(lastViewAction); +} +void ViewManager::switchToView(int index) +{ + Q_ASSERT(index >= 0); + ViewContainer* container = _viewSplitter->activeContainer(); + Q_ASSERT(container); + QList containerViews = container->views(); + if (index >= containerViews.count()) + return; + container->setActiveView(containerViews.at(index)); +} +void ViewManager::updateDetachViewState() +{ + if (!_actionCollection) + return; + + const bool splitView = _viewSplitter->containers().count() >= 2; + const bool shouldEnable = splitView || + _viewSplitter->activeContainer()->views().count() >= 2; + + QAction* detachAction = _actionCollection->action("detach-view"); + + if (detachAction && shouldEnable != detachAction->isEnabled()) + detachAction->setEnabled(shouldEnable); +} +void ViewManager::moveActiveViewLeft() +{ + ViewContainer* container = _viewSplitter->activeContainer(); + Q_ASSERT(container); + container->moveActiveView(ViewContainer::MoveViewLeft); +} +void ViewManager::moveActiveViewRight() +{ + ViewContainer* container = _viewSplitter->activeContainer(); + Q_ASSERT(container); + container->moveActiveView(ViewContainer::MoveViewRight); +} +void ViewManager::nextContainer() +{ + _viewSplitter->activateNextContainer(); +} + +void ViewManager::nextView() +{ + ViewContainer* container = _viewSplitter->activeContainer(); + + Q_ASSERT(container); + + container->activateNextView(); +} + +void ViewManager::previousView() +{ + ViewContainer* container = _viewSplitter->activeContainer(); + + Q_ASSERT(container); + + container->activatePreviousView(); +} + +void ViewManager::lastView() +{ + ViewContainer* container = _viewSplitter->activeContainer(); + + Q_ASSERT(container); + + container->activateLastView(); +} + +void ViewManager::detachActiveView() +{ + // find the currently active view and remove it from its container + ViewContainer* container = _viewSplitter->activeContainer(); + + detachView(container, container->activeView()); +} + +void ViewManager::detachView(ViewContainer* container, QWidget* widgetView) +{ + TerminalDisplay * viewToDetach = qobject_cast(widgetView); + + if (!viewToDetach) + return; + + emit viewDetached(_sessionMap[viewToDetach]); + + _sessionMap.remove(viewToDetach); + + // remove the view from this window + container->removeView(viewToDetach); + viewToDetach->deleteLater(); + + // if the container from which the view was removed is now empty then it can be deleted, + // unless it is the only container in the window, in which case it is left empty + // so that there is always an active container + if (_viewSplitter->containers().count() > 1 && + container->views().count() == 0) { + removeContainer(container); + } +} + +void ViewManager::sessionFinished() +{ + // if this slot is called after the view manager's main widget + // has been destroyed, do nothing + if (!_viewSplitter) + return; + + Session* session = qobject_cast(sender()); + Q_ASSERT(session); + + // close attached views + QList children = _viewSplitter->findChildren(); + + foreach(TerminalDisplay* view , children) { + if (_sessionMap[view] == session) { + _sessionMap.remove(view); + view->deleteLater(); + } + } + + // This is needed to remove this controller from factory() in + // order to prevent BUG: 185466 - disappearing menu popup + if (_pluggedController) + emit unplugController(_pluggedController); +} + +void ViewManager::viewActivated(QWidget* view) +{ + Q_ASSERT(view != 0); + + // focus the activated view, this will cause the SessionController + // to notify the world that the view has been focused and the appropriate UI + // actions will be plugged in. + view->setFocus(Qt::OtherFocusReason); +} + +void ViewManager::splitLeftRight() +{ + splitView(Qt::Horizontal); +} +void ViewManager::splitTopBottom() +{ + splitView(Qt::Vertical); +} + +void ViewManager::splitView(Qt::Orientation orientation) +{ + ViewContainer* container = createContainer(); + + // iterate over each session which has a view in the current active + // container and create a new view for that session in a new container + foreach(QWidget* view, _viewSplitter->activeContainer()->views()) { + Session* session = _sessionMap[qobject_cast(view)]; + TerminalDisplay* display = createTerminalDisplay(session); + const Profile::Ptr profile = SessionManager::instance()->sessionProfile(session); + applyProfileToView(display, profile); + ViewProperties* properties = createController(session, display); + + _sessionMap[display] = session; + + container->addView(display, properties); + session->addView(display); + } + + _viewSplitter->addContainer(container, orientation); + emit splitViewToggle(_viewSplitter->containers().count() > 0); + + // focus the new container + container->containerWidget()->setFocus(); + + // ensure that the active view is focused after the split / unsplit + ViewContainer* activeContainer = _viewSplitter->activeContainer(); + QWidget* activeView = activeContainer ? activeContainer->activeView() : 0; + + if (activeView) + activeView->setFocus(Qt::OtherFocusReason); +} +void ViewManager::removeContainer(ViewContainer* container) +{ + // remove session map entries for views in this container + foreach(QWidget* view , container->views()) { + TerminalDisplay* display = qobject_cast(view); + Q_ASSERT(display); + _sessionMap.remove(display); + } + + _viewSplitter->removeContainer(container); + container->deleteLater(); + + emit splitViewToggle(_viewSplitter->containers().count() > 1); +} +void ViewManager::expandActiveContainer() +{ + _viewSplitter->adjustContainerSize(_viewSplitter->activeContainer(), 10); +} +void ViewManager::shrinkActiveContainer() +{ + _viewSplitter->adjustContainerSize(_viewSplitter->activeContainer(), -10); +} +void ViewManager::closeActiveContainer() +{ + // only do something if there is more than one container active + if (_viewSplitter->containers().count() > 1) { + ViewContainer* container = _viewSplitter->activeContainer(); + + removeContainer(container); + + // focus next container so that user can continue typing + // without having to manually focus it themselves + nextContainer(); + } +} +void ViewManager::closeOtherContainers() +{ + ViewContainer* active = _viewSplitter->activeContainer(); + + foreach(ViewContainer* container, _viewSplitter->containers()) { + if (container != active) + removeContainer(container); + } +} + +SessionController* ViewManager::createController(Session* session , TerminalDisplay* view) +{ + // create a new controller for the session, and ensure that this view manager + // is notified when the view gains the focus + SessionController* controller = new SessionController(session, view, this); + connect(controller , SIGNAL(focused(SessionController*)) , this , SLOT(controllerChanged(SessionController*))); + connect(session , SIGNAL(destroyed()) , controller , SLOT(deleteLater())); + connect(session , SIGNAL(primaryScreenInUse(bool)) , + controller , SLOT(setupPrimaryScreenSpecificActions(bool))); + connect(session , SIGNAL(selectionChanged(QString)) , + controller , SLOT(selectionChanged(QString))); + connect(view , SIGNAL(destroyed()) , controller , SLOT(deleteLater())); + + // if this is the first controller created then set it as the active controller + if (!_pluggedController) + controllerChanged(controller); + + return controller; +} + +void ViewManager::controllerChanged(SessionController* controller) +{ + if (controller == _pluggedController) + return; + + _viewSplitter->setFocusProxy(controller->view()); + + _pluggedController = controller; + emit activeViewChanged(controller); +} + +SessionController* ViewManager::activeViewController() const +{ + return _pluggedController; +} + +IncrementalSearchBar* ViewManager::searchBar() const +{ + return _viewSplitter->activeSplitter()->activeContainer()->searchBar(); +} + +void ViewManager::createView(Session* session, ViewContainer* container, int index) +{ + // notify this view manager when the session finishes so that its view + // can be deleted + // + // Use Qt::UniqueConnection to avoid duplicate connection + connect(session, SIGNAL(finished()), this, SLOT(sessionFinished()), Qt::UniqueConnection); + + TerminalDisplay* display = createTerminalDisplay(session); + const Profile::Ptr profile = SessionManager::instance()->sessionProfile(session); + applyProfileToView(display, profile); + + // set initial size + const QSize& preferredSize = session->preferredSize(); + // FIXME: +1 is needed here for getting the expected rows + // Note that the display shouldn't need to take into account the tabbar. + // However, it appears that taking into account the tabbar is needed. + // If tabbar is not visible, no +1 is needed here; however, depending on + // settings/tabbar style, +2 might be needed. + // 1st attempt at fixing the above: + // Guess if tabbar will NOT be visible; ignore ShowNavigationAsNeeded + int heightAdjustment = 0; + if (_navigationVisibility != ViewContainer::AlwaysHideNavigation) { + heightAdjustment = 2; + } + + display->setSize(preferredSize.width(), preferredSize.height() + heightAdjustment); + ViewProperties* properties = createController(session, display); + + _sessionMap[display] = session; + container->addView(display, properties, index); + session->addView(display); + + // tell the session whether it has a light or dark background + session->setDarkBackground(colorSchemeForProfile(profile)->hasDarkBackground()); + + if (container == _viewSplitter->activeContainer()) { + container->setActiveView(display); + display->setFocus(Qt::OtherFocusReason); + } + + updateDetachViewState(); +} + +void ViewManager::createView(Session* session) +{ + // create the default container + if (_viewSplitter->containers().count() == 0) { + ViewContainer* container = createContainer(); + _viewSplitter->addContainer(container, Qt::Vertical); + emit splitViewToggle(false); + } + + // new tab will be put at the end by default. + int index = -1; + + if (_newTabBehavior == PutNewTabAfterCurrentTab) { + QWidget* view = activeView(); + if (view) { + QList views = _viewSplitter->activeContainer()->views(); + index = views.indexOf(view) + 1; + } + } + + // iterate over the view containers owned by this view manager + // and create a new terminal display for the session in each of them, along with + // a controller for the session/display pair + foreach(ViewContainer* container, _viewSplitter->containers()) { + createView(session, container, index); + } +} + +ViewContainer* ViewManager::createContainer() +{ + ViewContainer* container = 0; + + switch (_navigationMethod) { + case TabbedNavigation: { + container = new TabbedViewContainer(_navigationPosition, this, _viewSplitter); + + connect(container, SIGNAL(detachTab(ViewContainer*,QWidget*)), + this, SLOT(detachView(ViewContainer*,QWidget*)) + ); + connect(container, SIGNAL(closeTab(ViewContainer*,QWidget*)), + this, SLOT(closeTabFromContainer(ViewContainer*,QWidget*))); + } + break; + case NoNavigation: + default: + container = new StackedViewContainer(_viewSplitter); + } + + // FIXME: these code feels duplicated + container->setNavigationVisibility(_navigationVisibility); + container->setNavigationPosition(_navigationPosition); + container->setStyleSheet(_navigationStyleSheet); + if (_showQuickButtons) { + container->setFeatures(container->features() + | ViewContainer::QuickNewView + | ViewContainer::QuickCloseView); + } else { + container->setFeatures(container->features() + & ~ViewContainer::QuickNewView + & ~ViewContainer::QuickCloseView); + } + + // connect signals and slots + connect(container , SIGNAL(viewAdded(QWidget*,ViewProperties*)) , _containerSignalMapper , + SLOT(map())); + connect(container , SIGNAL(viewRemoved(QWidget*)) , _containerSignalMapper , + SLOT(map())); + _containerSignalMapper->setMapping(container, container); + + connect(container, SIGNAL(newViewRequest()), this, SIGNAL(newViewRequest())); + connect(container, SIGNAL(newViewRequest(Profile::Ptr)), this, SIGNAL(newViewRequest(Profile::Ptr))); + connect(container, SIGNAL(moveViewRequest(int,int,bool&,TabbedViewContainer*)), + this , SLOT(containerMoveViewRequest(int,int,bool&,TabbedViewContainer*))); + connect(container , SIGNAL(viewRemoved(QWidget*)) , this , SLOT(viewDestroyed(QWidget*))); + connect(container , SIGNAL(activeViewChanged(QWidget*)) , this , SLOT(viewActivated(QWidget*))); + + return container; +} + +void ViewManager::containerMoveViewRequest(int index, int id, bool& moved, TabbedViewContainer* sourceTabbedContainer) +{ + ViewContainer* container = qobject_cast(sender()); + SessionController* controller = qobject_cast(ViewProperties::propertiesById(id)); + + if (!controller) + return; + + // do not move the last tab in a split view. + if (sourceTabbedContainer) { + QPointer sourceContainer = qobject_cast(sourceTabbedContainer); + + if (_viewSplitter->containers().contains(sourceContainer)) { + return; + } else { + ViewManager* sourceViewManager = sourceTabbedContainer->connectedViewManager(); + + // do not remove the last tab on the window + if (qobject_cast(sourceViewManager->widget())->containers().size() > 1) { + return; + } + } + } + + createView(controller->session(), container, index); + controller->session()->refresh(); + moved = true; +} + +void ViewManager::setNavigationMethod(NavigationMethod method) +{ + _navigationMethod = method; + + KActionCollection* collection = _actionCollection; + + if (collection) { + // FIXME: The following disables certain actions for the KPart that it + // doesn't actually have a use for, to avoid polluting the action/shortcut + // namespace of an application using the KPart (otherwise, a shortcut may + // be in use twice, and the user gets to see an "ambiguous shortcut over- + // load" error dialog). However, this approach sucks - it's the inverse of + // what it should be. Rather than disabling actions not used by the KPart, + // a method should be devised to only enable those that are used, perhaps + // by using a separate action collection. + + const bool enable = (_navigationMethod != NoNavigation); + QAction* action; + + action = collection->action("next-view"); + if (action) action->setEnabled(enable); + + action = collection->action("previous-view"); + if (action) action->setEnabled(enable); + + action = collection->action("last-tab"); + if (action) action->setEnabled(enable); + + action = collection->action("split-view-left-right"); + if (action) action->setEnabled(enable); + + action = collection->action("split-view-top-bottom"); + if (action) action->setEnabled(enable); + + action = collection->action("rename-session"); + if (action) action->setEnabled(enable); + + action = collection->action("move-view-left"); + if (action) action->setEnabled(enable); + + action = collection->action("move-view-right"); + if (action) action->setEnabled(enable); + } +} + +ViewManager::NavigationMethod ViewManager::navigationMethod() const +{ + return _navigationMethod; +} + +void ViewManager::containerViewsChanged(QObject* container) +{ + if (_viewSplitter && container == _viewSplitter->activeContainer()) { + emit viewPropertiesChanged(viewProperties()); + } +} + +void ViewManager::viewDestroyed(QWidget* view) +{ + // Note: the received QWidget has already been destroyed, so + // using dynamic_cast<> or qobject_cast<> does not work here + TerminalDisplay* display = static_cast(view); + Q_ASSERT(display); + + // 1. detach view from session + // 2. if the session has no views left, close it + Session* session = _sessionMap[ display ]; + _sessionMap.remove(display); + if (session) { + if (session->views().count() == 0) + session->close(); + } + //we only update the focus if the splitter is still alive + if (_viewSplitter) { + updateDetachViewState(); + } + // The below causes the menus to be messed up + // Only happens when using the tab bar close button +// if (_pluggedController) +// emit unplugController(_pluggedController); +} + +TerminalDisplay* ViewManager::createTerminalDisplay(Session* session) +{ + TerminalDisplay* display = new TerminalDisplay(0); + display->setRandomSeed(session->sessionId() * 31); + + return display; +} + +const ColorScheme* ViewManager::colorSchemeForProfile(const Profile::Ptr profile) +{ + const ColorScheme* colorScheme = ColorSchemeManager::instance()-> + findColorScheme(profile->colorScheme()); + if (!colorScheme) + colorScheme = ColorSchemeManager::instance()->defaultColorScheme(); + Q_ASSERT(colorScheme); + + return colorScheme; +} + +void ViewManager::applyProfileToView(TerminalDisplay* view , const Profile::Ptr profile) +{ + Q_ASSERT(profile); + + emit updateWindowIcon(); + + // load color scheme + ColorEntry table[TABLE_COLORS]; + const ColorScheme* colorScheme = colorSchemeForProfile(profile); + colorScheme->getColorTable(table , view->randomSeed()); + view->setColorTable(table); + view->setOpacity(colorScheme->opacity()); + view->setWallpaper(colorScheme->wallpaper()); + + // load font + view->setAntialias(profile->antiAliasFonts()); + view->setBoldIntense(profile->boldIntense()); + view->setVTFont(profile->font()); + + // set scroll-bar position + int scrollBarPosition = profile->property(Profile::ScrollBarPosition); + + if (scrollBarPosition == Enum::ScrollBarLeft) + view->setScrollBarPosition(Enum::ScrollBarLeft); + else if (scrollBarPosition == Enum::ScrollBarRight) + view->setScrollBarPosition(Enum::ScrollBarRight); + else if (scrollBarPosition == Enum::ScrollBarHidden) + view->setScrollBarPosition(Enum::ScrollBarHidden); + + bool scrollFullPage = profile->property(Profile::ScrollFullPage); + view->setScrollFullPage(scrollFullPage); + + // show hint about terminal size after resizing + view->setShowTerminalSizeHint(profile->showTerminalSizeHint()); + + // terminal features + view->setBlinkingCursorEnabled(profile->blinkingCursorEnabled()); + view->setBlinkingTextEnabled(profile->blinkingTextEnabled()); + + int tripleClickMode = profile->property(Profile::TripleClickMode); + view->setTripleClickMode(Enum::TripleClickModeEnum(tripleClickMode)); + + view->setAutoCopySelectedText(profile->autoCopySelectedText()); + view->setUnderlineLinks(profile->underlineLinksEnabled()); + view->setControlDrag(profile->property(Profile::CtrlRequiredForDrag)); + view->setBidiEnabled(profile->bidiRenderingEnabled()); + view->setLineSpacing(profile->lineSpacing()); + view->setTrimTrailingSpaces(profile->property(Profile::TrimTrailingSpacesInSelectedText)); + + view->setOpenLinksByDirectClick(profile->property(Profile::OpenLinksByDirectClickEnabled)); + + int middleClickPasteMode = profile->property(Profile::MiddleClickPasteMode); + if (middleClickPasteMode == Enum::PasteFromX11Selection) + view->setMiddleClickPasteMode(Enum::PasteFromX11Selection); + else if (middleClickPasteMode == Enum::PasteFromClipboard) + view->setMiddleClickPasteMode(Enum::PasteFromClipboard); + + // margin/center - these are hard-fixed ATM + view->setMargin(1); + view->setCenterContents(false); + + // cursor shape + int cursorShape = profile->property(Profile::CursorShape); + + if (cursorShape == Enum::BlockCursor) + view->setKeyboardCursorShape(Enum::BlockCursor); + else if (cursorShape == Enum::IBeamCursor) + view->setKeyboardCursorShape(Enum::IBeamCursor); + else if (cursorShape == Enum::UnderlineCursor) + view->setKeyboardCursorShape(Enum::UnderlineCursor); + + // cursor color + if (profile->useCustomCursorColor()) { + const QColor& cursorColor = profile->customCursorColor(); + view->setKeyboardCursorColor(cursorColor); + } else { + // an invalid QColor is used to inform the view widget to + // draw the cursor using the default color( matching the text) + view->setKeyboardCursorColor(QColor()); + } + + // word characters + view->setWordCharacters(profile->wordCharacters()); + + // bell mode + view->setBellMode(profile->property(Profile::BellMode)); + + // mouse wheel zoom + view->setMouseWheelZoom(profile->mouseWheelZoomEnabled()); +} + +void ViewManager::updateViewsForSession(Session* session) +{ + const Profile::Ptr profile = SessionManager::instance()->sessionProfile(session); + + foreach(TerminalDisplay* view, _sessionMap.keys(session)) { + applyProfileToView(view, profile); + } +} + +void ViewManager::profileChanged(Profile::Ptr profile) +{ + // update all views associated with this profile + QHashIterator iter(_sessionMap); + while (iter.hasNext()) { + iter.next(); + + // if session uses this profile, update the display + if (iter.key() != 0 && + iter.value() != 0 && + SessionManager::instance()->sessionProfile(iter.value()) == profile) { + applyProfileToView(iter.key(), profile); + } + } +} + +QList ViewManager::viewProperties() const +{ + QList list; + + ViewContainer* container = _viewSplitter->activeContainer(); + + Q_ASSERT(container); + + foreach(QWidget* view, container->views()) { + ViewProperties* properties = container->viewProperties(view); + Q_ASSERT(properties); + list << properties; + } + + return list; +} + +void ViewManager::saveSessions(KConfigGroup& group) +{ + // find all unique session restore IDs + QList ids; + QHash unique; + + // first: sessions in the active container, preserving the order + ViewContainer* container = _viewSplitter->activeContainer(); + Q_ASSERT(container); + TerminalDisplay* activeview = qobject_cast(container->activeView()); + + QListIterator viewIter(container->views()); + int tab = 1; + while (viewIter.hasNext()) { + TerminalDisplay* view = qobject_cast(viewIter.next()); + Q_ASSERT(view); + Session* session = _sessionMap[view]; + ids << SessionManager::instance()->getRestoreId(session); + if (view == activeview) group.writeEntry("Active", tab); + unique.insert(session, 1); + tab++; + } + + // second: all other sessions, in random order + // we don't want to have sessions restored that are not connected + foreach(Session * session, _sessionMap) { + if (!unique.contains(session)) { + ids << SessionManager::instance()->getRestoreId(session); + unique.insert(session, 1); + } + } + + group.writeEntry("Sessions", ids); +} + +void ViewManager::restoreSessions(const KConfigGroup& group) +{ + QList ids = group.readEntry("Sessions", QList()); + int activeTab = group.readEntry("Active", 0); + TerminalDisplay* display = 0; + + int tab = 1; + foreach(int id, ids) { + Session* session = SessionManager::instance()->idToSession(id); + createView(session); + if (!session->isRunning()) + session->run(); + if (tab++ == activeTab) + display = qobject_cast(activeView()); + } + + if (display) { + _viewSplitter->activeContainer()->setActiveView(display); + display->setFocus(Qt::OtherFocusReason); + } + + if (ids.isEmpty()) { // Session file is unusable, start default Profile + Profile::Ptr profile = ProfileManager::instance()->defaultProfile(); + Session* session = SessionManager::instance()->createSession(profile); + createView(session); + if (!session->isRunning()) + session->run(); + } +} + +int ViewManager::sessionCount() +{ + return this->_sessionMap.size(); +} + +int ViewManager::currentSession() +{ + QHash::iterator i; + for (i = this->_sessionMap.begin(); i != this->_sessionMap.end(); ++i) + if (i.key()->isVisible()) + return i.value()->sessionId(); + return -1; +} + +int ViewManager::newSession() +{ + Profile::Ptr profile = ProfileManager::instance()->defaultProfile(); + Session* session = SessionManager::instance()->createSession(profile); + + this->createView(session); + session->run(); + + return session->sessionId(); +} + +int ViewManager::newSession(QString profile, QString directory) +{ + const QList profilelist = ProfileManager::instance()->allProfiles(); + Profile::Ptr profileptr = ProfileManager::instance()->defaultProfile(); + + for (int i = 0; i < profilelist.size(); ++i) { + if (profilelist.at(i)->name() == profile) { + profileptr = profilelist.at(i); + break; + } + } + + Session* session = SessionManager::instance()->createSession(profileptr); + session->setInitialWorkingDirectory(directory); + + this->createView(session); + session->run(); + + return session->sessionId(); +} + +QString ViewManager::defaultProfile() +{ + return ProfileManager::instance()->defaultProfile()->name(); +} + +QStringList ViewManager::profileList() +{ + return ProfileManager::instance()->availableProfileNames(); +} + +void ViewManager::nextSession() +{ + this->nextView(); +} + +void ViewManager::prevSession() +{ + this->previousView(); +} + +void ViewManager::moveSessionLeft() +{ + this->moveActiveViewLeft(); +} + +void ViewManager::moveSessionRight() +{ + this->moveActiveViewRight(); +} + +void ViewManager::setTabWidthToText(bool useTextWidth) +{ + ViewContainer* container = _viewSplitter->activeContainer(); + Q_ASSERT(container); + container->setNavigationTextMode(useTextWidth); +} + +void ViewManager::closeTabFromContainer(ViewContainer* container, QWidget* tab) +{ + SessionController* controller = qobject_cast(container->viewProperties(tab)); + Q_ASSERT(controller); + if (controller) + controller->closeSession(); +} + +void ViewManager::setNavigationVisibility(int visibility) +{ + _navigationVisibility = + static_cast(visibility); + + foreach(ViewContainer* container, _viewSplitter->containers()) { + container->setNavigationVisibility(_navigationVisibility); + } +} + +void ViewManager::setNavigationPosition(int position) +{ + _navigationPosition = + static_cast(position); + + foreach(ViewContainer* container, _viewSplitter->containers()) { + Q_ASSERT(container->supportedNavigationPositions().contains(_navigationPosition)); + container->setNavigationPosition(_navigationPosition); + } +} + +void ViewManager::setNavigationStyleSheet(const QString& styleSheet) +{ + _navigationStyleSheet = styleSheet; + + foreach(ViewContainer* container, _viewSplitter->containers()) { + container->setStyleSheet(_navigationStyleSheet); + } +} + +void ViewManager::setShowQuickButtons(bool show) +{ + _showQuickButtons = show; + + foreach(ViewContainer* container, _viewSplitter->containers()) { + if (_showQuickButtons) { + container->setFeatures(container->features() + | ViewContainer::QuickNewView + | ViewContainer::QuickCloseView); + } else { + container->setFeatures(container->features() + & ~ViewContainer::QuickNewView + & ~ViewContainer::QuickCloseView); + } + } +} + + +void ViewManager::setNavigationBehavior(int behavior) +{ + _newTabBehavior = static_cast(behavior); +} + +#include "moc_ViewManager.cpp" + diff --git a/konsole/src/ViewManager.h b/konsole/src/ViewManager.h new file mode 100644 index 00000000..7c3ab465 --- /dev/null +++ b/konsole/src/ViewManager.h @@ -0,0 +1,392 @@ +/* + Copyright 2006-2008 by Robert Knight + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +#ifndef VIEWMANAGER_H +#define VIEWMANAGER_H + +// Qt +#include +#include +#include + +// Konsole +#include "Profile.h" +#include "ViewContainer.h" + +#include +class KActionCollection; +class KConfigGroup; + +namespace Konsole +{ +class ColorScheme; +class IncrementalSearchBar; +class Session; +class TerminalDisplay; + +class SessionController; +class ViewProperties; +class ViewSplitter; + +/** + * Manages the terminal display widgets in a Konsole window or part. + * + * When a view manager is created, it constructs a splitter widget ( accessed via + * widget() ) to hold one or more view containers. Each view container holds + * one or more terminal displays and a navigation widget ( eg. tabs or a list ) + * to allow the user to navigate between the displays in that container. + * + * The view manager provides menu actions ( defined in the 'konsoleui.rc' XML file ) + * to manipulate the views and view containers - for example, actions to split the view + * left/right or top/bottom, detach a view from the current window and navigate between + * views and containers. These actions are added to the collection specified in the + * ViewManager's constructor. + * + * The view manager provides facilities to construct display widgets for a terminal + * session and also to construct the SessionController which provides the menus and other + * user interface elements specific to each display/session pair. + * + */ +class KONSOLEPRIVATE_EXPORT ViewManager : public QObject +{ + Q_OBJECT + Q_CLASSINFO("D-Bus Interface", "org.kde.konsole.Window") + +public: + /** + * Constructs a new view manager with the specified @p parent. + * View-related actions defined in 'konsoleui.rc' are created + * and added to the specified @p collection. + */ + ViewManager(QObject* parent , KActionCollection* collection); + ~ViewManager(); + + /** + * Creates a new view to display the output from and deliver input to @p session. + * Constructs a new container to hold the views if no container has yet been created. + */ + void createView(Session* session); + + /** + * Applies the view-specific settings associated with specified @p profile + * to the terminal display @p view. + */ + void applyProfileToView(TerminalDisplay* view , const Profile::Ptr profile); + + /** + * Return the main widget for the view manager which + * holds all of the views managed by this ViewManager instance. + */ + QWidget* widget() const; + + /** + * Returns the view manager's active view. + */ + QWidget* activeView() const; + + /** + * Returns the list of view properties for views in the active container. + * Each view widget is associated with a ViewProperties instance which + * provides access to basic information about the session being + * displayed in the view, such as title, current directory and + * associated icon. + */ + QList viewProperties() const; + + /** + * This enum describes the available types of navigation widget + * which newly created containers can provide to allow navigation + * between open sessions. + */ + enum NavigationMethod { + /** + * Each container has a row of tabs (one per session) which the user + * can click on to navigate between open sessions. + */ + TabbedNavigation, + /** The container has no navigation widget. */ + NoNavigation + }; + + /** + * This enum describes where newly created tab should be placed. + */ + enum NewTabBehavior { + /** Put newly created tab at the end. */ + PutNewTabAtTheEnd = 0, + /** Put newly created tab right after current tab. */ + PutNewTabAfterCurrentTab = 1 + }; + + /** + * Sets the type of widget provided to navigate between open sessions + * in a container. The changes will only apply to newly created containers. + * + * The default method is TabbedNavigation. To disable navigation widgets, call + * setNavigationMethod(ViewManager::NoNavigation) before creating any sessions. + */ + void setNavigationMethod(NavigationMethod method); + + /** + * Returns the type of navigation widget created in new containers. + * See setNavigationMethod() + */ + NavigationMethod navigationMethod() const; + + /** + * Returns the controller for the active view. activeViewChanged() is + * emitted when this changes. + */ + SessionController* activeViewController() const; + + /** + * Returns the search bar. + */ + IncrementalSearchBar* searchBar() const; + + /** + * Session management + */ + void saveSessions(KConfigGroup& group); + void restoreSessions(const KConfigGroup& group); + + void setNavigationVisibility(int visibility); + void setNavigationPosition(int position); + void setNavigationBehavior(int behavior); + void setNavigationStyleSheet(const QString& styleSheet); + void setShowQuickButtons(bool show); + + int managerId() const; + + /** Returns a list of sessions in this ViewManager */ + QList sessions() { return _sessionMap.values(); } + +signals: + /** Emitted when the last view is removed from the view manager */ + void empty(); + + /** Emitted when a session is detached from a view owned by this ViewManager */ + void viewDetached(Session* session); + + /** + * Emitted when the active view changes. + * @param controller The controller associated with the active view + */ + void activeViewChanged(SessionController* controller); + + /** + * Emitted when the current session needs unplugged from factory(). + * @param controller The controller associated with the active view + */ + void unplugController(SessionController* controller); + + /** + * Emitted when the list of view properties ( as returned by viewProperties() ) changes. + * This occurs when views are added to or removed from the active container, or + * if the active container is changed. + */ + void viewPropertiesChanged(const QList& propertiesList); + + /** + * Emitted when the number of views containers changes. This is used to disable or + * enable menu items which can only be used when there are one or multiple containers + * visible. + * + * @param multipleViews True if there are multiple view containers open or false if there is + * just a single view. + */ + void splitViewToggle(bool multipleViews); + + /** + * Emitted when menu bar visibility changes because a profile that requires so is + * activated. + */ + void setMenuBarVisibleRequest(bool); + void updateWindowIcon(); + + /** Requests creation of a new view with the default profile. */ + void newViewRequest(); + /** Requests creation of a new view, with the selected profile. */ + void newViewRequest(Profile::Ptr); + +public slots: + /** DBus slot that returns the number of sessions in the current view. */ + Q_SCRIPTABLE int sessionCount(); + + /** DBus slot that returns the current (active) session window */ + Q_SCRIPTABLE int currentSession(); + + /** DBus slot that creates a new session in the current view. + * @param profile the name of the profile to be used + * @param directory the working directory where the session is + * started. + */ + Q_SCRIPTABLE int newSession(QString profile, QString directory); + + // TODO: its semantic is application-wide. Move it to more appropriate place + // DBus slot that returns the name of default profile + Q_SCRIPTABLE QString defaultProfile(); + + // TODO: its semantic is application-wide. Move it to more appropriate place + // DBus slot that returns a string list of defined (known) profiles + Q_SCRIPTABLE QStringList profileList(); + + /** DBus slot that creates a new session in the current view with the associated + * default profile and the default working directory + */ + Q_SCRIPTABLE int newSession(); + + /** DBus slot that changes the view port to the next session */ + Q_SCRIPTABLE void nextSession(); + + /** DBus slot that changes the view port to the previous session */ + Q_SCRIPTABLE void prevSession(); + + /** DBus slot that switches the current session (as returned by + * currentSession()) with the left (or previous) one in the + * navigation tab. + */ + Q_SCRIPTABLE void moveSessionLeft(); + + /** DBus slot that Switches the current session (as returned by + * currentSession()) with the right (or next) one in the navigation + * tab. + */ + Q_SCRIPTABLE void moveSessionRight(); + + /** DBus slot that sets ALL tabs' width to match their text */ + Q_SCRIPTABLE void setTabWidthToText(bool); + +private slots: + // called when the "Split View Left/Right" menu item is selected + void splitLeftRight(); + void splitTopBottom(); + void closeActiveContainer(); + void closeOtherContainers(); + void expandActiveContainer(); + void shrinkActiveContainer(); + + // called when the "Detach View" menu item is selected + void detachActiveView(); + void updateDetachViewState(); + + // called when a session terminates - the view manager will delete any + // views associated with the session + void sessionFinished(); + // called when one view has been destroyed + void viewDestroyed(QWidget* widget); + + // controller detects when an associated view is given the focus + // and emits a signal. ViewManager listens for that signal + // and then plugs the action into the UI + //void viewFocused( SessionController* controller ); + + // called when the active view in a ViewContainer changes, so + // that we can plug the appropriate actions into the UI + void viewActivated(QWidget* view); + + // called when "Next View" shortcut is activated + void nextView(); + + // called when "Previous View" shortcut is activated + void previousView(); + + // called when "Switch to last tab" shortcut is activated + void lastView(); + + // called when "Next View Container" shortcut is activated + void nextContainer(); + + // called when the views in a container owned by this view manager + // changes + void containerViewsChanged(QObject* container); + + // called when a profile changes + void profileChanged(Profile::Ptr profile); + + void updateViewsForSession(Session* session); + + // moves active view to the left + void moveActiveViewLeft(); + // moves active view to the right + void moveActiveViewRight(); + // switches to the view at visual position 'index' + // in the current container + void switchToView(int index); + + // called when a SessionController gains focus + void controllerChanged(SessionController* controller); + + // called when a ViewContainer requests a view be + // moved + void containerMoveViewRequest(int index, int id, bool& success, TabbedViewContainer* sourceTabbedContainer); + + void detachView(ViewContainer* container, QWidget* view); + + void closeTabFromContainer(ViewContainer* container, QWidget* view); + +private: + void createView(Session* session, ViewContainer* container, int index); + static const ColorScheme* colorSchemeForProfile(const Profile::Ptr profile); + + void setupActions(); + + // takes a view from a view container owned by a different manager and places it in + // newContainer owned by this manager + void takeView(ViewManager* otherManager , ViewContainer* otherContainer, ViewContainer* newContainer, TerminalDisplay* view); + void splitView(Qt::Orientation orientation); + + // creates a new container which can hold terminal displays + ViewContainer* createContainer(); + // removes a container and emits appropriate signals + void removeContainer(ViewContainer* container); + + // creates a new terminal display + // the 'session' is used so that the terminal display's random seed + // can be set to something which depends uniquely on that session + TerminalDisplay* createTerminalDisplay(Session* session = 0); + + // creates a new controller for a session/display pair which provides the menu + // actions associated with that view, and exposes basic information + // about the session ( such as title and associated icon ) to the display. + SessionController* createController(Session* session , TerminalDisplay* display); + +private: + QPointer _viewSplitter; + QPointer _pluggedController; + + QHash _sessionMap; + + KActionCollection* _actionCollection; + QSignalMapper* _containerSignalMapper; + + NavigationMethod _navigationMethod; + + ViewContainer::NavigationVisibility _navigationVisibility; + ViewContainer::NavigationPosition _navigationPosition; + bool _showQuickButtons; + NewTabBehavior _newTabBehavior; + QString _navigationStyleSheet; + + int _managerId; + static int lastManagerId; +}; +} + +#endif diff --git a/konsole/src/ViewProperties.cpp b/konsole/src/ViewProperties.cpp new file mode 100644 index 00000000..0faea4f8 --- /dev/null +++ b/konsole/src/ViewProperties.cpp @@ -0,0 +1,97 @@ +/* + Copyright 2007-2008 by Robert Knight + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +// Own +#include "ViewProperties.h" + +using Konsole::ViewProperties; + +QHash ViewProperties::_viewProperties; +QString ViewProperties::_mimeType = "application/x-konsole-view-id"; + +ViewProperties::ViewProperties(QObject* parent) + : QObject(parent) + , _id(0) +{ +} + +ViewProperties::~ViewProperties() +{ + _viewProperties.remove(_id); +} +ViewProperties* ViewProperties::propertiesById(int id) +{ + return _viewProperties[id]; +} +KUrl ViewProperties::url() const +{ + return KUrl(); +} +QString ViewProperties::currentDir() const +{ + return QString(); +} +void ViewProperties::fireActivity() +{ + emit activity(this); +} + +void ViewProperties::rename() +{ +} + +void ViewProperties::setTitle(const QString& title) +{ + if (title != _title) { + _title = title; + emit titleChanged(this); + } +} +void ViewProperties::setIcon(const QIcon& icon) +{ + // the icon's cache key is used to determine whether this icon is the same + // as the old one. if so no signal is emitted. + + if (icon.cacheKey() != _icon.cacheKey()) { + _icon = icon; + emit iconChanged(this); + } +} +void ViewProperties::setIdentifier(int id) +{ + if (_viewProperties.contains(_id)) + _viewProperties.remove(_id); + + _id = id; + + _viewProperties.insert(id, this); +} +QString ViewProperties::title() const +{ + return _title; +} +QIcon ViewProperties::icon() const +{ + return _icon; +} +int ViewProperties::identifier() const +{ + return _id; +} +#include "moc_ViewProperties.cpp" diff --git a/konsole/src/ViewProperties.h b/konsole/src/ViewProperties.h new file mode 100644 index 00000000..193d9f36 --- /dev/null +++ b/konsole/src/ViewProperties.h @@ -0,0 +1,157 @@ +/* + Copyright 2007-2008 by Robert Knight + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +#ifndef VIEWPROPERTIES_H +#define VIEWPROPERTIES_H + +// Qt +#include +#include +#include +#include + +// KDE +#include + +// Konsole +#include "konsoleprivate_export.h" + +namespace Konsole +{ +/** + * Encapsulates user-visible information about the terminal session currently being displayed in a view, + * such as the associated title and icon. + * + * This can be used by navigation widgets in a ViewContainer sub-class to provide a tab, label or other + * item for switching between views. + */ +class KONSOLEPRIVATE_EXPORT ViewProperties : public QObject +{ + Q_OBJECT + +public: + explicit ViewProperties(QObject* parent); + virtual ~ViewProperties(); + + /** Returns the icon associated with a view */ + QIcon icon() const; + /** Returns the title associated with a view */ + QString title() const; + + /** + * Returns the URL current associated with a view. + * The default implementation returns an empty URL. + */ + virtual KUrl url() const; + + /** + * Returns the current directory associated with a view. + * This may be the same as url() + * The default implementation returns an empty string. + */ + virtual QString currentDir() const; + + /** + * A unique identifier associated with this + * ViewProperties instance. + */ + int identifier() const; + + /** + * Sub-classes may re-implement this method to display a message to the user + * to allow them to confirm whether to close a view. + * The default implementation always returns true + */ + virtual bool confirmClose() const { + return true; + } + + /** Finds a ViewProperties instance given its numeric identifier. */ + static ViewProperties* propertiesById(int id); + + /** Name of mime format to use in drag-and-drop operations. */ + static QString mimeType() { + return _mimeType; + } + + /** Returns a new QMimeData instance which represents the view with the + * given @p id (See identifier()). The QMimeData instance returned must + * be deleted by the caller. + */ + static QMimeData* createMimeData(int id) { + QMimeData* mimeData = new QMimeData; + mimeData->setData(mimeType(), QByteArray::number(id)); + return mimeData; + } + + /** Decodes a QMimeData instance created with createMimeData() and + * returns the identifier of the associated view. The associated + * ViewProperties instance can then be retrieved by calling propertiesById() + * + * The QMimeData instance must support the mime format returned by mimeType() + */ + static int decodeMimeData(const QMimeData* mimeData) { + bool ok; + // we are not checking return value ok; not sure how int could be invalid + return mimeData->data(ViewProperties::mimeType()).toInt(&ok); + } + +signals: + /** Emitted when the icon for a view changes */ + void iconChanged(ViewProperties* properties); + /** Emitted when the title for a view changes */ + void titleChanged(ViewProperties* properties); + /** Emitted when activity has occurred in this view. */ + void activity(ViewProperties* item); + +public slots: + /** + * Requests the renaming of this view. + * The default implementation does nothing. + */ + virtual void rename(); + +protected slots: + /** Emits the activity() signal. */ + void fireActivity(); + +protected: + /** + * Subclasses may call this method to change the title. This causes + * a titleChanged() signal to be emitted + */ + void setTitle(const QString& title); + /** + * Subclasses may call this method to change the icon. This causes + * an iconChanged() signal to be emitted + */ + void setIcon(const QIcon& icon); + /** Subclasses may call this method to change the identifier. */ + void setIdentifier(int id); +private: + QIcon _icon; + QString _title; + int _id; + + static QHash _viewProperties; + static QString _mimeType; +}; +} + +#endif //VIEWPROPERTIES_H diff --git a/konsole/src/ViewSplitter.cpp b/konsole/src/ViewSplitter.cpp new file mode 100644 index 00000000..6c4d5cb7 --- /dev/null +++ b/konsole/src/ViewSplitter.cpp @@ -0,0 +1,260 @@ +/* + This file is part of the Konsole Terminal. + + Copyright 2006-2008 Robert Knight + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +// Own +#include "ViewSplitter.h" + +// Qt + +// Konsole +#include "ViewContainer.h" + +using Konsole::ViewSplitter; +using Konsole::ViewContainer; + +ViewSplitter::ViewSplitter(QWidget* parent) + : QSplitter(parent) + , _recursiveSplitting(true) +{ +} + +void ViewSplitter::childEmpty(ViewSplitter* splitter) +{ + delete splitter; + + if (count() == 0) + emit empty(this); +} + +void ViewSplitter::adjustContainerSize(ViewContainer* container , int percentage) +{ + int containerIndex = indexOf(container->containerWidget()); + + Q_ASSERT(containerIndex != -1); + + QList containerSizes = sizes(); + + const int oldSize = containerSizes[containerIndex]; + const int newSize = static_cast(oldSize * (1.0 + percentage / 100.0)); + + const int perContainerDelta = (count() == 1) ? 0 : ((newSize - oldSize) / (count() - 1)) * (-1); + + for (int i = 0 ; i < containerSizes.count() ; i++) { + if (i == containerIndex) + containerSizes[i] = newSize; + else + containerSizes[i] = containerSizes[i] + perContainerDelta; + } + + setSizes(containerSizes); +} + +ViewSplitter* ViewSplitter::activeSplitter() +{ + QWidget* widget = focusWidget() ? focusWidget() : this; + + ViewSplitter* splitter = 0; + + while (!splitter && widget) { + splitter = qobject_cast(widget); + widget = widget->parentWidget(); + } + + Q_ASSERT(splitter); + return splitter; +} + +void ViewSplitter::registerContainer(ViewContainer* container) +{ + _containers << container; + connect(container , SIGNAL(destroyed(ViewContainer*)) , this , SLOT(containerDestroyed(ViewContainer*))); + connect(container , SIGNAL(empty(ViewContainer*)) , this , SLOT(containerEmpty(ViewContainer*))); +} + +void ViewSplitter::unregisterContainer(ViewContainer* container) +{ + _containers.removeAll(container); + disconnect(container , 0 , this , 0); +} + +void ViewSplitter::updateSizes() +{ + int space; + + if (orientation() == Qt::Horizontal) { + space = width() / count(); + } else { + space = height() / count(); + } + + QList widgetSizes; + for (int i = 0; i < count(); i++) + widgetSizes << space; + + setSizes(widgetSizes); +} + +void ViewSplitter::setRecursiveSplitting(bool recursive) +{ + _recursiveSplitting = recursive; +} +bool ViewSplitter::recursiveSplitting() const +{ + return _recursiveSplitting; +} + +void ViewSplitter::removeContainer(ViewContainer* container) +{ + Q_ASSERT(containers().contains(container)); + + unregisterContainer(container); +} + +void ViewSplitter::addContainer(ViewContainer* container , + Qt::Orientation containerOrientation) +{ + ViewSplitter* splitter = activeSplitter(); + + if (splitter->count() < 2 || + containerOrientation == splitter->orientation() || + !_recursiveSplitting) { + splitter->registerContainer(container); + splitter->addWidget(container->containerWidget()); + + if (splitter->orientation() != containerOrientation) + splitter->setOrientation(containerOrientation); + + splitter->updateSizes(); + } else { + ViewSplitter* newSplitter = new ViewSplitter(this); + connect(newSplitter , SIGNAL(empty(ViewSplitter*)) , splitter , SLOT(childEmpty(ViewSplitter*))); + + ViewContainer* oldContainer = splitter->activeContainer(); + const int oldContainerIndex = splitter->indexOf(oldContainer->containerWidget()); + + splitter->unregisterContainer(oldContainer); + + newSplitter->registerContainer(oldContainer); + newSplitter->registerContainer(container); + + newSplitter->addWidget(oldContainer->containerWidget()); + newSplitter->addWidget(container->containerWidget()); + newSplitter->setOrientation(containerOrientation); + newSplitter->updateSizes(); + newSplitter->show(); + + splitter->insertWidget(oldContainerIndex, newSplitter); + } +} + +void ViewSplitter::containerEmpty(ViewContainer* /*container*/) +{ + int children = 0; + foreach(ViewContainer* container, _containers) { + children += container->views().count(); + } + + if (children == 0) + emit allContainersEmpty(); +} + +void ViewSplitter::containerDestroyed(ViewContainer* container) +{ + Q_ASSERT(_containers.contains(container)); + + _containers.removeAll(container); + + if (count() == 0) { + emit empty(this); + } +} + +void ViewSplitter::activateNextContainer() +{ + ViewContainer* active = activeContainer(); + + int index = _containers.indexOf(active); + + if (index == -1) + return; + + if (index == _containers.count() - 1) + index = 0; + else + index++; + + setActiveContainer(_containers.at(index)); +} + +void ViewSplitter::activatePreviousContainer() +{ + ViewContainer* active = activeContainer(); + + int index = _containers.indexOf(active); + + if (index == 0) + index = _containers.count() - 1; + else + index--; + + setActiveContainer(_containers.at(index)); +} + +void ViewSplitter::setActiveContainer(ViewContainer* container) +{ + QWidget* activeView = container->activeView(); + + if (activeView) + activeView->setFocus(Qt::OtherFocusReason); +} + +ViewContainer* ViewSplitter::activeContainer() const +{ + if (QWidget* focusW = focusWidget()) { + ViewContainer* focusContainer = 0; + + while (focusW != 0) { + foreach(ViewContainer* container, _containers) { + if (container->containerWidget() == focusW) { + focusContainer = container; + break; + } + } + focusW = focusW->parentWidget(); + } + + if (focusContainer) + return focusContainer; + } + + QList splitters = findChildren(); + + if (splitters.count() > 0) { + return splitters.last()->activeContainer(); + } else { + if (_containers.count() > 0) + return _containers.last(); + else + return 0; + } +} + +#include "moc_ViewSplitter.cpp" diff --git a/konsole/src/ViewSplitter.h b/konsole/src/ViewSplitter.h new file mode 100644 index 00000000..4592f045 --- /dev/null +++ b/konsole/src/ViewSplitter.h @@ -0,0 +1,189 @@ +/* + This file is part of the Konsole Terminal. + + Copyright 2006-2008 Robert Knight + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +#ifndef VIEWSPLITTER_H +#define VIEWSPLITTER_H + +// Qt +#include +#include + +#include + +namespace Konsole +{ +class ViewContainer; + +/** + * A splitter which holds a number of ViewContainer objects and allows + * the user to control the size of each view container by dragging a splitter + * bar between them. + * + * Each splitter can also contain child ViewSplitter widgets, allowing + * for a hierarchy of view splitters and containers. + * + * The addContainer() method is used to split the existing view and + * insert a new view container. + * Containers can only be removed from the hierarchy by deleting them. + */ +class ViewSplitter : public QSplitter +{ + Q_OBJECT + +public: + explicit ViewSplitter(QWidget* parent = 0); + + /** + * Locates the child ViewSplitter widget which currently has the focus + * and inserts the container into it. + * + * @param container The container to insert + * @param orientation Specifies whether the view should be split + * horizontally or vertically. If the orientation + * is the same as the ViewSplitter into which the + * container is to be inserted, or if the splitter + * has fewer than two child widgets then the container + * will be added to that splitter. If the orientation + * is different, then a new child splitter + * will be created, into which the container will + * be inserted. + */ + void addContainer(ViewContainer* container , Qt::Orientation orientation); + + /** Removes a container from the splitter. The container is not deleted. */ + void removeContainer(ViewContainer* container); + + /** Returns the child ViewSplitter widget which currently has the focus */ + ViewSplitter* activeSplitter(); + + /** + * Returns the container which currently has the focus or 0 if none + * of the immediate child containers have the focus. This does not + * search through child splitters. activeSplitter() can be used + * to search recursively through child splitters for the splitter + * which currently has the focus. + * + * To find the currently active container, use + * mySplitter->activeSplitter()->activeContainer() where mySplitter + * is the ViewSplitter widget at the top of the hierarchy. + */ + ViewContainer* activeContainer() const; + + /** + * Gives the focus to the active view in the specified container + */ + void setActiveContainer(ViewContainer* container); + + /** + * Returns a list of the containers held by this splitter + */ + QList containers() const { + return _containers; + } + + /** + * Gives the focus to the active view in the next container + */ + void activateNextContainer(); + + /** + * Changes the size of the specified @p container by a given @p percentage. + * @p percentage may be positive ( in which case the size of the container + * is increased ) or negative ( in which case the size of the container + * is decreased ). + * + * The sizes of the remaining containers are increased or decreased + * uniformly to maintain the width of the splitter. + */ + void adjustContainerSize(ViewContainer* container , int percentage); + + /** + * Gives the focus to the active view in the previous container + */ + void activatePreviousContainer(); + + /** + * Specifies whether the view may be split recursively. + * + * If this is false, all containers will be placed into the same + * top-level splitter. Adding a container with an orientation + * which is different to that specified when adding the previous + * containers will change the orientation for all dividers + * between containers. + * + * If this is true, adding a container to the view splitter with + * an orientation different to the orientation of the previous + * area will result in the previously active container being + * replaced with a new splitter containing the active container + * and the newly added container. + */ + void setRecursiveSplitting(bool recursive); + + /** + * Returns whether the view may be split recursively. + * See setRecursiveSplitting() + */ + bool recursiveSplitting() const; + +signals: + /** Signal emitted when the last child widget is removed from the splitter */ + void empty(ViewSplitter* splitter); + + /** + * Signal emitted when the containers held by this splitter become empty, this + * differs from the empty() signal which is only emitted when all of the containers + * are deleted. This signal is emitted even if there are still container widgets. + * + * TODO: This does not yet work recursively (ie. when splitters inside splitters have empty containers) + */ + void allContainersEmpty(); + +protected: + //virtual void focusEvent(QFocusEvent* event); + +private: + // Adds container to splitter's internal list and + // connects signals and slots + void registerContainer(ViewContainer* container); + // Removes container from splitter's internal list and + // removes signals and slots + void unregisterContainer(ViewContainer* container); + + void updateSizes(); + +private slots: + // Called to indicate that a child ViewContainer has been deleted + void containerDestroyed(ViewContainer* container); + + // Called to indicate that a child ViewContainer is empty + void containerEmpty(ViewContainer* container); + + // Called to indicate that a child ViewSplitter is empty + // (ie. all child widgets have been deleted) + void childEmpty(ViewSplitter* splitter); + +private: + QList _containers; + bool _recursiveSplitting; +}; +} +#endif //VIEWSPLITTER_H + diff --git a/konsole/src/Vt102Emulation.cpp b/konsole/src/Vt102Emulation.cpp new file mode 100644 index 00000000..053accc5 --- /dev/null +++ b/konsole/src/Vt102Emulation.cpp @@ -0,0 +1,1361 @@ +/* + This file is part of Konsole, an X terminal. + + Copyright 2007-2008 by Robert Knight + Copyright 1997,1998 by Lars Doelle + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +// Own +#include "Vt102Emulation.h" + +// Standard +#include +#include + +// Qt +#include +#include +#include + +// KDE +#include +#include + +// Konsole +#include "KeyboardTranslator.h" +#include "Screen.h" +#include "TerminalDisplay.h" + +using Konsole::Vt102Emulation; + +/* + The VT100 has 32 special graphical characters. The usual vt100 extended + xterm fonts have these at 0x00..0x1f. + + QT's iso mapping leaves 0x00..0x7f without any changes. But the graphicals + come in here as proper unicode characters. + + We treat non-iso10646 fonts as VT100 extended and do the required mapping + from unicode to 0x00..0x1f. The remaining translation is then left to the + QCodec. +*/ + +// assert for i in [0..31] : vt100extended(vt100_graphics[i]) == i. + +unsigned short Konsole::vt100_graphics[32] = { + // 0/8 1/9 2/10 3/11 4/12 5/13 6/14 7/15 + 0x0020, 0x25C6, 0x2592, 0x2409, 0x240c, 0x240d, 0x240a, 0x00b0, + 0x00b1, 0x2424, 0x240b, 0x2518, 0x2510, 0x250c, 0x2514, 0x253c, + 0xF800, 0xF801, 0x2500, 0xF803, 0xF804, 0x251c, 0x2524, 0x2534, + 0x252c, 0x2502, 0x2264, 0x2265, 0x03C0, 0x2260, 0x00A3, 0x00b7 +}; + +Vt102Emulation::Vt102Emulation() + : Emulation(), + _titleUpdateTimer(new QTimer(this)) +{ + _titleUpdateTimer->setSingleShot(true); + QObject::connect(_titleUpdateTimer , SIGNAL(timeout()) , this , SLOT(updateTitle())); + + initTokenizer(); + reset(); +} + +Vt102Emulation::~Vt102Emulation() +{} + +void Vt102Emulation::clearEntireScreen() +{ + _currentScreen->clearEntireScreen(); + bufferedUpdate(); +} + +void Vt102Emulation::reset() +{ + // Save the current codec so we can set it later. + // Ideally we would want to use the profile setting + const QTextCodec* currentCodec = codec(); + + resetTokenizer(); + resetModes(); + resetCharset(0); + _screen[0]->reset(); + resetCharset(1); + _screen[1]->reset(); + + if (currentCodec) + setCodec(currentCodec); + else + setCodec(LocaleCodec); + + bufferedUpdate(); +} + +/* ------------------------------------------------------------------------- */ +/* */ +/* Processing the incoming byte stream */ +/* */ +/* ------------------------------------------------------------------------- */ + +/* Incoming Bytes Event pipeline + + This section deals with decoding the incoming character stream. + Decoding means here, that the stream is first separated into `tokens' + which are then mapped to a `meaning' provided as operations by the + `Screen' class or by the emulation class itself. + + The pipeline proceeds as follows: + + - Tokenizing the ESC codes (onReceiveChar) + - VT100 code page translation of plain characters (applyCharset) + - Interpretation of ESC codes (processToken) + + The escape codes and their meaning are described in the + technical reference of this program. +*/ + +// Tokens ------------------------------------------------------------------ -- + +/* + Since the tokens are the central notion if this section, we've put them + in front. They provide the syntactical elements used to represent the + terminals operations as byte sequences. + + They are encodes here into a single machine word, so that we can later + switch over them easily. Depending on the token itself, additional + argument variables are filled with parameter values. + + The tokens are defined below: + + - CHR - Printable characters (32..255 but DEL (=127)) + - CTL - Control characters (0..31 but ESC (= 27), DEL) + - ESC - Escape codes of the form + - ESC_DE - Escape codes of the form C + - CSI_PN - Escape codes of the form '[' {Pn} ';' {Pn} C + - CSI_PS - Escape codes of the form '[' {Pn} ';' ... C + - CSI_PR - Escape codes of the form '[' '?' {Pn} ';' ... C + - CSI_PE - Escape codes of the form '[' '!' {Pn} ';' ... C + - VT52 - VT52 escape codes + - + - 'Y'{Pc}{Pc} + - XTE_HA - Xterm window/terminal attribute commands + of the form `]' {Pn} `;' {Text} + (Note that these are handled differently to the other formats) + + The last two forms allow list of arguments. Since the elements of + the lists are treated individually the same way, they are passed + as individual tokens to the interpretation. Further, because the + meaning of the parameters are names (although represented as numbers), + they are includes within the token ('N'). + +*/ + +#define TY_CONSTRUCT(T,A,N) ( ((((int)N) & 0xffff) << 16) | ((((int)A) & 0xff) << 8) | (((int)T) & 0xff) ) + +#define TY_CHR( ) TY_CONSTRUCT(0,0,0) +#define TY_CTL(A ) TY_CONSTRUCT(1,A,0) +#define TY_ESC(A ) TY_CONSTRUCT(2,A,0) +#define TY_ESC_CS(A,B) TY_CONSTRUCT(3,A,B) +#define TY_ESC_DE(A ) TY_CONSTRUCT(4,A,0) +#define TY_CSI_PS(A,N) TY_CONSTRUCT(5,A,N) +#define TY_CSI_PN(A ) TY_CONSTRUCT(6,A,0) +#define TY_CSI_PR(A,N) TY_CONSTRUCT(7,A,N) + +#define TY_VT52(A) TY_CONSTRUCT(8,A,0) +#define TY_CSI_PG(A) TY_CONSTRUCT(9,A,0) +#define TY_CSI_PE(A) TY_CONSTRUCT(10,A,0) + +const int MAX_ARGUMENT = 4096; + +// Tokenizer --------------------------------------------------------------- -- + +/* The tokenizer's state + + The state is represented by the buffer (tokenBuffer, tokenBufferPos), + and accompanied by decoded arguments kept in (argv,argc). + Note that they are kept internal in the tokenizer. +*/ + +void Vt102Emulation::resetTokenizer() +{ + tokenBufferPos = 0; + argc = 0; + argv[0] = 0; + argv[1] = 0; +} + +void Vt102Emulation::addDigit(int digit) +{ + if (argv[argc] < MAX_ARGUMENT) + argv[argc] = 10 * argv[argc] + digit; +} + +void Vt102Emulation::addArgument() +{ + argc = qMin(argc + 1, MAXARGS - 1); + argv[argc] = 0; +} + +void Vt102Emulation::addToCurrentToken(int cc) +{ + tokenBuffer[tokenBufferPos] = cc; + tokenBufferPos = qMin(tokenBufferPos + 1, MAX_TOKEN_LENGTH - 1); +} + +// Character Class flags used while decoding +const int CTL = 1; // Control character +const int CHR = 2; // Printable character +const int CPN = 4; // TODO: Document me +const int DIG = 8; // Digit +const int SCS = 16; // Select Character Set +const int GRP = 32; // TODO: Document me +const int CPS = 64; // Character which indicates end of window resize + +void Vt102Emulation::initTokenizer() +{ + int i; + quint8* s; + for (i = 0; i < 256; ++i) + charClass[i] = 0; + for (i = 0; i < 32; ++i) + charClass[i] |= CTL; + for (i = 32; i < 256; ++i) + charClass[i] |= CHR; + for (s = (quint8*)"@ABCDGHILMPSTXZcdfry"; *s; ++s) + charClass[*s] |= CPN; + // resize = \e[8;;t + for (s = (quint8*)"t"; *s; ++s) + charClass[*s] |= CPS; + for (s = (quint8*)"0123456789"; *s; ++s) + charClass[*s] |= DIG; + for (s = (quint8*)"()+*%"; *s; ++s) + charClass[*s] |= SCS; + for (s = (quint8*)"()+*#[]%"; *s; ++s) + charClass[*s] |= GRP; + + resetTokenizer(); +} + +/* Ok, here comes the nasty part of the decoder. + + Instead of keeping an explicit state, we deduce it from the + token scanned so far. It is then immediately combined with + the current character to form a scanning decision. + + This is done by the following defines. + + - P is the length of the token scanned so far. + - L (often P-1) is the position on which contents we base a decision. + - C is a character or a group of characters (taken from 'charClass'). + + - 'cc' is the current character + - 's' is a pointer to the start of the token buffer + - 'p' is the current position within the token buffer + + Note that they need to applied in proper order. +*/ + +#define lec(P,L,C) (p == (P) && s[(L)] == (C)) +#define lun( ) (p == 1 && cc >= 32 ) +#define les(P,L,C) (p == (P) && s[L] < 256 && (charClass[s[(L)]] & (C)) == (C)) +#define eec(C) (p >= 3 && cc == (C)) +#define ees(C) (p >= 3 && cc < 256 && (charClass[cc] & (C)) == (C)) +#define eps(C) (p >= 3 && s[2] != '?' && s[2] != '!' && s[2] != '>' && cc < 256 && (charClass[cc] & (C)) == (C)) +#define epp( ) (p >= 3 && s[2] == '?') +#define epe( ) (p >= 3 && s[2] == '!') +#define egt( ) (p >= 3 && s[2] == '>') +#define Xpe (tokenBufferPos >= 2 && tokenBuffer[1] == ']') +#define Xte (Xpe && cc == 7 ) +#define ces(C) (cc < 256 && (charClass[cc] & (C)) == (C) && !Xte) + +#define CNTL(c) ((c)-'@') +const int ESC = 27; +const int DEL = 127; + +// process an incoming unicode character +void Vt102Emulation::receiveChar(int cc) +{ + if (cc == DEL) + return; //VT100: ignore. + + if (ces(CTL)) + { + // DEC HACK ALERT! Control Characters are allowed *within* esc sequences in VT100 + // This means, they do neither a resetTokenizer() nor a pushToToken(). Some of them, do + // of course. Guess this originates from a weakly layered handling of the X-on + // X-off protocol, which comes really below this level. + if (cc == CNTL('X') || cc == CNTL('Z') || cc == ESC) + resetTokenizer(); //VT100: CAN or SUB + if (cc != ESC) + { + processToken(TY_CTL(cc+'@' ),0,0); + return; + } + } + // advance the state + addToCurrentToken(cc); + + int* s = tokenBuffer; + const int p = tokenBufferPos; + + if (getMode(MODE_Ansi)) + { + if (lec(1,0,ESC)) { return; } + if (lec(1,0,ESC+128)) { s[0] = ESC; receiveChar('['); return; } + if (les(2,1,GRP)) { return; } + if (Xte ) { processWindowAttributeChange(); resetTokenizer(); return; } + if (Xpe ) { return; } + if (lec(3,2,'?')) { return; } + if (lec(3,2,'>')) { return; } + if (lec(3,2,'!')) { return; } + if (lun( )) { processToken( TY_CHR(), applyCharset(cc), 0); resetTokenizer(); return; } + if (lec(2,0,ESC)) { processToken( TY_ESC(s[1]), 0, 0); resetTokenizer(); return; } + if (les(3,1,SCS)) { processToken( TY_ESC_CS(s[1],s[2]), 0, 0); resetTokenizer(); return; } + if (lec(3,1,'#')) { processToken( TY_ESC_DE(s[2]), 0, 0); resetTokenizer(); return; } + if (eps( CPN)) { processToken( TY_CSI_PN(cc), argv[0],argv[1]); resetTokenizer(); return; } + + // resize = \e[8;;t + if (eps(CPS)) + { + processToken( TY_CSI_PS(cc, argv[0]), argv[1], argv[2]); + resetTokenizer(); + return; + } + + if (epe( )) { processToken( TY_CSI_PE(cc), 0, 0); resetTokenizer(); return; } + if (ees(DIG)) { addDigit(cc-'0'); return; } + if (eec(';')) { addArgument(); return; } + for (int i = 0; i <= argc; i++) + { + if (epp()) + processToken(TY_CSI_PR(cc,argv[i]), 0, 0); + else if (egt()) + processToken(TY_CSI_PG(cc), 0, 0); // spec. case for ESC]>0c or ESC]>c + else if (cc == 'm' && argc - i >= 4 && (argv[i] == 38 || argv[i] == 48) && argv[i+1] == 2) + { + // ESC[ ... 48;2;;; ... m -or- ESC[ ... 38;2;;; ... m + i += 2; + processToken(TY_CSI_PS(cc, argv[i-2]), COLOR_SPACE_RGB, (argv[i] << 16) | (argv[i+1] << 8) | argv[i+2]); + i += 2; + } + else if (cc == 'm' && argc - i >= 2 && (argv[i] == 38 || argv[i] == 48) && argv[i+1] == 5) + { + // ESC[ ... 48;5; ... m -or- ESC[ ... 38;5; ... m + i += 2; + processToken(TY_CSI_PS(cc, argv[i-2]), COLOR_SPACE_256, argv[i]); + } + else + processToken(TY_CSI_PS(cc,argv[i]), 0, 0); + } + resetTokenizer(); + } + else + { + // VT52 Mode + if (lec(1,0,ESC)) + return; + if (les(1,0,CHR)) + { + processToken( TY_CHR(), s[0], 0); + resetTokenizer(); + return; + } + if (lec(2,1,'Y')) + return; + if (lec(3,1,'Y')) + return; + if (p < 4) + { + processToken(TY_VT52(s[1] ), 0, 0); + resetTokenizer(); + return; + } + processToken(TY_VT52(s[1]), s[2], s[3]); + resetTokenizer(); + return; + } +} +void Vt102Emulation::processWindowAttributeChange() +{ + // Describes the window or terminal session attribute to change + // See Session::UserTitleChange for possible values + int attributeToChange = 0; + int i; + for (i = 2; i < tokenBufferPos && + tokenBuffer[i] >= '0' && + tokenBuffer[i] <= '9'; i++) + { + attributeToChange = 10 * attributeToChange + (tokenBuffer[i]-'0'); + } + + if (tokenBuffer[i] != ';') + { + reportDecodingError(); + return; + } + + QString newValue; + newValue.reserve(tokenBufferPos-i-2); + for (int j = 0; j < tokenBufferPos-i-2; j++) + newValue[j] = tokenBuffer[i+1+j]; + + _pendingTitleUpdates[attributeToChange] = newValue; + _titleUpdateTimer->start(20); +} + +void Vt102Emulation::updateTitle() +{ + QListIterator iter( _pendingTitleUpdates.keys() ); + while (iter.hasNext()) { + int arg = iter.next(); + emit titleChanged( arg , _pendingTitleUpdates[arg] ); + } + _pendingTitleUpdates.clear(); +} + +// Interpreting Codes --------------------------------------------------------- + +/* + Now that the incoming character stream is properly tokenized, + meaning is assigned to them. These are either operations of + the current _screen, or of the emulation class itself. + + The token to be interpreted comes in as a machine word + possibly accompanied by two parameters. + + Likewise, the operations assigned to, come with up to two + arguments. One could consider to make up a proper table + from the function below. + + The technical reference manual provides more information + about this mapping. +*/ + +void Vt102Emulation::processToken(int token, int p, int q) +{ + switch (token) + { + case TY_CHR( ) : _currentScreen->displayCharacter (p ); break; //UTF16 + + // 127 DEL : ignored on input + + case TY_CTL('@' ) : /* NUL: ignored */ break; + case TY_CTL('A' ) : /* SOH: ignored */ break; + case TY_CTL('B' ) : /* STX: ignored */ break; + case TY_CTL('C' ) : /* ETX: ignored */ break; + case TY_CTL('D' ) : /* EOT: ignored */ break; + case TY_CTL('E' ) : reportAnswerBack ( ); break; //VT100 + case TY_CTL('F' ) : /* ACK: ignored */ break; + case TY_CTL('G' ) : emit stateSet(NOTIFYBELL); + break; //VT100 + case TY_CTL('H' ) : _currentScreen->backspace ( ); break; //VT100 + case TY_CTL('I' ) : _currentScreen->tab ( ); break; //VT100 + case TY_CTL('J' ) : _currentScreen->newLine ( ); break; //VT100 + case TY_CTL('K' ) : _currentScreen->newLine ( ); break; //VT100 + case TY_CTL('L' ) : _currentScreen->newLine ( ); break; //VT100 + case TY_CTL('M' ) : _currentScreen->toStartOfLine ( ); break; //VT100 + + case TY_CTL('N' ) : useCharset ( 1); break; //VT100 + case TY_CTL('O' ) : useCharset ( 0); break; //VT100 + + case TY_CTL('P' ) : /* DLE: ignored */ break; + case TY_CTL('Q' ) : /* DC1: XON continue */ break; //VT100 + case TY_CTL('R' ) : /* DC2: ignored */ break; + case TY_CTL('S' ) : /* DC3: XOFF halt */ break; //VT100 + case TY_CTL('T' ) : /* DC4: ignored */ break; + case TY_CTL('U' ) : /* NAK: ignored */ break; + case TY_CTL('V' ) : /* SYN: ignored */ break; + case TY_CTL('W' ) : /* ETB: ignored */ break; + case TY_CTL('X' ) : _currentScreen->displayCharacter ( 0x2592); break; //VT100 + case TY_CTL('Y' ) : /* EM : ignored */ break; + case TY_CTL('Z' ) : _currentScreen->displayCharacter ( 0x2592); break; //VT100 + case TY_CTL('[' ) : /* ESC: cannot be seen here. */ break; + case TY_CTL('\\' ) : /* FS : ignored */ break; + case TY_CTL(']' ) : /* GS : ignored */ break; + case TY_CTL('^' ) : /* RS : ignored */ break; + case TY_CTL('_' ) : /* US : ignored */ break; + + case TY_ESC('D' ) : _currentScreen->index ( ); break; //VT100 + case TY_ESC('E' ) : _currentScreen->nextLine ( ); break; //VT100 + case TY_ESC('H' ) : _currentScreen->changeTabStop (true ); break; //VT100 + case TY_ESC('M' ) : _currentScreen->reverseIndex ( ); break; //VT100 + case TY_ESC('Z' ) : reportTerminalType ( ); break; + case TY_ESC('c' ) : reset ( ); break; + + case TY_ESC('n' ) : useCharset ( 2); break; + case TY_ESC('o' ) : useCharset ( 3); break; + case TY_ESC('7' ) : saveCursor ( ); break; + case TY_ESC('8' ) : restoreCursor ( ); break; + + case TY_ESC('=' ) : setMode (MODE_AppKeyPad); break; + case TY_ESC('>' ) : resetMode (MODE_AppKeyPad); break; + case TY_ESC('<' ) : setMode (MODE_Ansi ); break; //VT100 + + case TY_ESC_CS('(', '0') : setCharset (0, '0'); break; //VT100 + case TY_ESC_CS('(', 'A') : setCharset (0, 'A'); break; //VT100 + case TY_ESC_CS('(', 'B') : setCharset (0, 'B'); break; //VT100 + + case TY_ESC_CS(')', '0') : setCharset (1, '0'); break; //VT100 + case TY_ESC_CS(')', 'A') : setCharset (1, 'A'); break; //VT100 + case TY_ESC_CS(')', 'B') : setCharset (1, 'B'); break; //VT100 + + case TY_ESC_CS('*', '0') : setCharset (2, '0'); break; //VT100 + case TY_ESC_CS('*', 'A') : setCharset (2, 'A'); break; //VT100 + case TY_ESC_CS('*', 'B') : setCharset (2, 'B'); break; //VT100 + + case TY_ESC_CS('+', '0') : setCharset (3, '0'); break; //VT100 + case TY_ESC_CS('+', 'A') : setCharset (3, 'A'); break; //VT100 + case TY_ESC_CS('+', 'B') : setCharset (3, 'B'); break; //VT100 + + case TY_ESC_CS('%', 'G') : setCodec (Utf8Codec ); break; //LINUX + case TY_ESC_CS('%', '@') : setCodec (LocaleCodec ); break; //LINUX + + case TY_ESC_DE('3' ) : /* Double height line, top half */ + _currentScreen->setLineProperty( LINE_DOUBLEWIDTH , true ); + _currentScreen->setLineProperty( LINE_DOUBLEHEIGHT , true ); + break; + case TY_ESC_DE('4' ) : /* Double height line, bottom half */ + _currentScreen->setLineProperty( LINE_DOUBLEWIDTH , true ); + _currentScreen->setLineProperty( LINE_DOUBLEHEIGHT , true ); + break; + case TY_ESC_DE('5' ) : /* Single width, single height line*/ + _currentScreen->setLineProperty( LINE_DOUBLEWIDTH , false); + _currentScreen->setLineProperty( LINE_DOUBLEHEIGHT , false); + break; + case TY_ESC_DE('6' ) : /* Double width, single height line*/ + _currentScreen->setLineProperty( LINE_DOUBLEWIDTH , true); + _currentScreen->setLineProperty( LINE_DOUBLEHEIGHT , false); + break; + case TY_ESC_DE('8' ) : _currentScreen->helpAlign ( ); break; + +// resize = \e[8;;t + case TY_CSI_PS('t', 8) : setImageSize( p /*lines */, q /* columns */ ); + emit imageResizeRequest(QSize(q, p)); + break; + +// change tab text color : \e[28;t color: 0-16,777,215 + case TY_CSI_PS('t', 28) : emit changeTabTextColorRequest ( p ); break; + + case TY_CSI_PS('K', 0) : _currentScreen->clearToEndOfLine ( ); break; + case TY_CSI_PS('K', 1) : _currentScreen->clearToBeginOfLine ( ); break; + case TY_CSI_PS('K', 2) : _currentScreen->clearEntireLine ( ); break; + case TY_CSI_PS('J', 0) : _currentScreen->clearToEndOfScreen ( ); break; + case TY_CSI_PS('J', 1) : _currentScreen->clearToBeginOfScreen ( ); break; + case TY_CSI_PS('J', 2) : _currentScreen->clearEntireScreen ( ); break; + case TY_CSI_PS('J', 3) : clearHistory(); break; + case TY_CSI_PS('g', 0) : _currentScreen->changeTabStop (false ); break; //VT100 + case TY_CSI_PS('g', 3) : _currentScreen->clearTabStops ( ); break; //VT100 + case TY_CSI_PS('h', 4) : _currentScreen-> setMode (MODE_Insert ); break; + case TY_CSI_PS('h', 20) : setMode (MODE_NewLine ); break; + case TY_CSI_PS('i', 0) : /* IGNORE: attached printer */ break; //VT100 + case TY_CSI_PS('l', 4) : _currentScreen-> resetMode (MODE_Insert ); break; + case TY_CSI_PS('l', 20) : resetMode (MODE_NewLine ); break; + case TY_CSI_PS('s', 0) : saveCursor ( ); break; + case TY_CSI_PS('u', 0) : restoreCursor ( ); break; + + case TY_CSI_PS('m', 0) : _currentScreen->setDefaultRendition ( ); break; + case TY_CSI_PS('m', 1) : _currentScreen-> setRendition (RE_BOLD ); break; //VT100 + case TY_CSI_PS('m', 3) : _currentScreen-> setRendition (RE_ITALIC ); break; //VT100 + case TY_CSI_PS('m', 4) : _currentScreen-> setRendition (RE_UNDERLINE); break; //VT100 + case TY_CSI_PS('m', 5) : _currentScreen-> setRendition (RE_BLINK ); break; //VT100 + case TY_CSI_PS('m', 7) : _currentScreen-> setRendition (RE_REVERSE ); break; + case TY_CSI_PS('m', 10) : /* IGNORED: mapping related */ break; //LINUX + case TY_CSI_PS('m', 11) : /* IGNORED: mapping related */ break; //LINUX + case TY_CSI_PS('m', 12) : /* IGNORED: mapping related */ break; //LINUX + case TY_CSI_PS('m', 22) : _currentScreen->resetRendition (RE_BOLD ); break; + case TY_CSI_PS('m', 23) : _currentScreen->resetRendition (RE_ITALIC ); break; //VT100 + case TY_CSI_PS('m', 24) : _currentScreen->resetRendition (RE_UNDERLINE); break; + case TY_CSI_PS('m', 25) : _currentScreen->resetRendition (RE_BLINK ); break; + case TY_CSI_PS('m', 27) : _currentScreen->resetRendition (RE_REVERSE ); break; + + case TY_CSI_PS('m', 30) : _currentScreen->setForeColor (COLOR_SPACE_SYSTEM, 0); break; + case TY_CSI_PS('m', 31) : _currentScreen->setForeColor (COLOR_SPACE_SYSTEM, 1); break; + case TY_CSI_PS('m', 32) : _currentScreen->setForeColor (COLOR_SPACE_SYSTEM, 2); break; + case TY_CSI_PS('m', 33) : _currentScreen->setForeColor (COLOR_SPACE_SYSTEM, 3); break; + case TY_CSI_PS('m', 34) : _currentScreen->setForeColor (COLOR_SPACE_SYSTEM, 4); break; + case TY_CSI_PS('m', 35) : _currentScreen->setForeColor (COLOR_SPACE_SYSTEM, 5); break; + case TY_CSI_PS('m', 36) : _currentScreen->setForeColor (COLOR_SPACE_SYSTEM, 6); break; + case TY_CSI_PS('m', 37) : _currentScreen->setForeColor (COLOR_SPACE_SYSTEM, 7); break; + + case TY_CSI_PS('m', 38) : _currentScreen->setForeColor (p, q); break; + + case TY_CSI_PS('m', 39) : _currentScreen->setForeColor (COLOR_SPACE_DEFAULT, 0); break; + + case TY_CSI_PS('m', 40) : _currentScreen->setBackColor (COLOR_SPACE_SYSTEM, 0); break; + case TY_CSI_PS('m', 41) : _currentScreen->setBackColor (COLOR_SPACE_SYSTEM, 1); break; + case TY_CSI_PS('m', 42) : _currentScreen->setBackColor (COLOR_SPACE_SYSTEM, 2); break; + case TY_CSI_PS('m', 43) : _currentScreen->setBackColor (COLOR_SPACE_SYSTEM, 3); break; + case TY_CSI_PS('m', 44) : _currentScreen->setBackColor (COLOR_SPACE_SYSTEM, 4); break; + case TY_CSI_PS('m', 45) : _currentScreen->setBackColor (COLOR_SPACE_SYSTEM, 5); break; + case TY_CSI_PS('m', 46) : _currentScreen->setBackColor (COLOR_SPACE_SYSTEM, 6); break; + case TY_CSI_PS('m', 47) : _currentScreen->setBackColor (COLOR_SPACE_SYSTEM, 7); break; + + case TY_CSI_PS('m', 48) : _currentScreen->setBackColor (p, q); break; + + case TY_CSI_PS('m', 49) : _currentScreen->setBackColor (COLOR_SPACE_DEFAULT, 1); break; + + case TY_CSI_PS('m', 90) : _currentScreen->setForeColor (COLOR_SPACE_SYSTEM, 8); break; + case TY_CSI_PS('m', 91) : _currentScreen->setForeColor (COLOR_SPACE_SYSTEM, 9); break; + case TY_CSI_PS('m', 92) : _currentScreen->setForeColor (COLOR_SPACE_SYSTEM, 10); break; + case TY_CSI_PS('m', 93) : _currentScreen->setForeColor (COLOR_SPACE_SYSTEM, 11); break; + case TY_CSI_PS('m', 94) : _currentScreen->setForeColor (COLOR_SPACE_SYSTEM, 12); break; + case TY_CSI_PS('m', 95) : _currentScreen->setForeColor (COLOR_SPACE_SYSTEM, 13); break; + case TY_CSI_PS('m', 96) : _currentScreen->setForeColor (COLOR_SPACE_SYSTEM, 14); break; + case TY_CSI_PS('m', 97) : _currentScreen->setForeColor (COLOR_SPACE_SYSTEM, 15); break; + + case TY_CSI_PS('m', 100) : _currentScreen->setBackColor (COLOR_SPACE_SYSTEM, 8); break; + case TY_CSI_PS('m', 101) : _currentScreen->setBackColor (COLOR_SPACE_SYSTEM, 9); break; + case TY_CSI_PS('m', 102) : _currentScreen->setBackColor (COLOR_SPACE_SYSTEM, 10); break; + case TY_CSI_PS('m', 103) : _currentScreen->setBackColor (COLOR_SPACE_SYSTEM, 11); break; + case TY_CSI_PS('m', 104) : _currentScreen->setBackColor (COLOR_SPACE_SYSTEM, 12); break; + case TY_CSI_PS('m', 105) : _currentScreen->setBackColor (COLOR_SPACE_SYSTEM, 13); break; + case TY_CSI_PS('m', 106) : _currentScreen->setBackColor (COLOR_SPACE_SYSTEM, 14); break; + case TY_CSI_PS('m', 107) : _currentScreen->setBackColor (COLOR_SPACE_SYSTEM, 15); break; + + case TY_CSI_PS('n', 5) : reportStatus ( ); break; + case TY_CSI_PS('n', 6) : reportCursorPosition ( ); break; + case TY_CSI_PS('q', 0) : /* IGNORED: LEDs off */ break; //VT100 + case TY_CSI_PS('q', 1) : /* IGNORED: LED1 on */ break; //VT100 + case TY_CSI_PS('q', 2) : /* IGNORED: LED2 on */ break; //VT100 + case TY_CSI_PS('q', 3) : /* IGNORED: LED3 on */ break; //VT100 + case TY_CSI_PS('q', 4) : /* IGNORED: LED4 on */ break; //VT100 + case TY_CSI_PS('x', 0) : reportTerminalParms ( 2); break; //VT100 + case TY_CSI_PS('x', 1) : reportTerminalParms ( 3); break; //VT100 + + case TY_CSI_PN('@' ) : _currentScreen->insertChars (p ); break; + case TY_CSI_PN('A' ) : _currentScreen->cursorUp (p ); break; //VT100 + case TY_CSI_PN('B' ) : _currentScreen->cursorDown (p ); break; //VT100 + case TY_CSI_PN('C' ) : _currentScreen->cursorRight (p ); break; //VT100 + case TY_CSI_PN('D' ) : _currentScreen->cursorLeft (p ); break; //VT100 + case TY_CSI_PN('E' ) : /* Not implemented: cursor next p lines */ break; //VT100 + case TY_CSI_PN('F' ) : /* Not implemented: cursor preceding p lines */ break; //VT100 + case TY_CSI_PN('G' ) : _currentScreen->setCursorX (p ); break; //LINUX + case TY_CSI_PN('H' ) : _currentScreen->setCursorYX (p, q); break; //VT100 + case TY_CSI_PN('I' ) : _currentScreen->tab (p ); break; + case TY_CSI_PN('L' ) : _currentScreen->insertLines (p ); break; + case TY_CSI_PN('M' ) : _currentScreen->deleteLines (p ); break; + case TY_CSI_PN('P' ) : _currentScreen->deleteChars (p ); break; + case TY_CSI_PN('S' ) : _currentScreen->scrollUp (p ); break; + case TY_CSI_PN('T' ) : _currentScreen->scrollDown (p ); break; + case TY_CSI_PN('X' ) : _currentScreen->eraseChars (p ); break; + case TY_CSI_PN('Z' ) : _currentScreen->backtab (p ); break; + case TY_CSI_PN('c' ) : reportTerminalType ( ); break; //VT100 + case TY_CSI_PN('d' ) : _currentScreen->setCursorY (p ); break; //LINUX + case TY_CSI_PN('f' ) : _currentScreen->setCursorYX (p, q); break; //VT100 + case TY_CSI_PN('r' ) : setMargins (p, q); break; //VT100 + case TY_CSI_PN('y' ) : /* IGNORED: Confidence test */ break; //VT100 + + case TY_CSI_PR('h', 1) : setMode (MODE_AppCuKeys); break; //VT100 + case TY_CSI_PR('l', 1) : resetMode (MODE_AppCuKeys); break; //VT100 + case TY_CSI_PR('s', 1) : saveMode (MODE_AppCuKeys); break; //FIXME + case TY_CSI_PR('r', 1) : restoreMode (MODE_AppCuKeys); break; //FIXME + + case TY_CSI_PR('l', 2) : resetMode (MODE_Ansi ); break; //VT100 + + case TY_CSI_PR('h', 3) : setMode (MODE_132Columns); break; //VT100 + case TY_CSI_PR('l', 3) : resetMode (MODE_132Columns); break; //VT100 + + case TY_CSI_PR('h', 4) : /* IGNORED: soft scrolling */ break; //VT100 + case TY_CSI_PR('l', 4) : /* IGNORED: soft scrolling */ break; //VT100 + + case TY_CSI_PR('h', 5) : _currentScreen-> setMode (MODE_Screen ); break; //VT100 + case TY_CSI_PR('l', 5) : _currentScreen-> resetMode (MODE_Screen ); break; //VT100 + + case TY_CSI_PR('h', 6) : _currentScreen-> setMode (MODE_Origin ); break; //VT100 + case TY_CSI_PR('l', 6) : _currentScreen-> resetMode (MODE_Origin ); break; //VT100 + case TY_CSI_PR('s', 6) : _currentScreen-> saveMode (MODE_Origin ); break; //FIXME + case TY_CSI_PR('r', 6) : _currentScreen->restoreMode (MODE_Origin ); break; //FIXME + + case TY_CSI_PR('h', 7) : _currentScreen-> setMode (MODE_Wrap ); break; //VT100 + case TY_CSI_PR('l', 7) : _currentScreen-> resetMode (MODE_Wrap ); break; //VT100 + case TY_CSI_PR('s', 7) : _currentScreen-> saveMode (MODE_Wrap ); break; //FIXME + case TY_CSI_PR('r', 7) : _currentScreen->restoreMode (MODE_Wrap ); break; //FIXME + + case TY_CSI_PR('h', 8) : /* IGNORED: autorepeat on */ break; //VT100 + case TY_CSI_PR('l', 8) : /* IGNORED: autorepeat off */ break; //VT100 + case TY_CSI_PR('s', 8) : /* IGNORED: autorepeat on */ break; //VT100 + case TY_CSI_PR('r', 8) : /* IGNORED: autorepeat off */ break; //VT100 + + case TY_CSI_PR('h', 9) : /* IGNORED: interlace */ break; //VT100 + case TY_CSI_PR('l', 9) : /* IGNORED: interlace */ break; //VT100 + case TY_CSI_PR('s', 9) : /* IGNORED: interlace */ break; //VT100 + case TY_CSI_PR('r', 9) : /* IGNORED: interlace */ break; //VT100 + + case TY_CSI_PR('h', 12) : /* IGNORED: Cursor blink */ break; //att610 + case TY_CSI_PR('l', 12) : /* IGNORED: Cursor blink */ break; //att610 + case TY_CSI_PR('s', 12) : /* IGNORED: Cursor blink */ break; //att610 + case TY_CSI_PR('r', 12) : /* IGNORED: Cursor blink */ break; //att610 + + case TY_CSI_PR('h', 25) : setMode (MODE_Cursor ); break; //VT100 + case TY_CSI_PR('l', 25) : resetMode (MODE_Cursor ); break; //VT100 + case TY_CSI_PR('s', 25) : saveMode (MODE_Cursor ); break; //VT100 + case TY_CSI_PR('r', 25) : restoreMode (MODE_Cursor ); break; //VT100 + + case TY_CSI_PR('h', 40) : setMode(MODE_Allow132Columns ); break; // XTERM + case TY_CSI_PR('l', 40) : resetMode(MODE_Allow132Columns ); break; // XTERM + + case TY_CSI_PR('h', 41) : /* IGNORED: obsolete more(1) fix */ break; //XTERM + case TY_CSI_PR('l', 41) : /* IGNORED: obsolete more(1) fix */ break; //XTERM + case TY_CSI_PR('s', 41) : /* IGNORED: obsolete more(1) fix */ break; //XTERM + case TY_CSI_PR('r', 41) : /* IGNORED: obsolete more(1) fix */ break; //XTERM + + case TY_CSI_PR('h', 47) : setMode (MODE_AppScreen); break; //VT100 + case TY_CSI_PR('l', 47) : resetMode (MODE_AppScreen); break; //VT100 + case TY_CSI_PR('s', 47) : saveMode (MODE_AppScreen); break; //XTERM + case TY_CSI_PR('r', 47) : restoreMode (MODE_AppScreen); break; //XTERM + + case TY_CSI_PR('h', 67) : /* IGNORED: DECBKM */ break; //XTERM + case TY_CSI_PR('l', 67) : /* IGNORED: DECBKM */ break; //XTERM + case TY_CSI_PR('s', 67) : /* IGNORED: DECBKM */ break; //XTERM + case TY_CSI_PR('r', 67) : /* IGNORED: DECBKM */ break; //XTERM + + // XTerm defines the following modes: + // SET_VT200_MOUSE 1000 + // SET_VT200_HIGHLIGHT_MOUSE 1001 + // SET_BTN_EVENT_MOUSE 1002 + // SET_ANY_EVENT_MOUSE 1003 + // + + //Note about mouse modes: + //There are four mouse modes which xterm-compatible terminals can support - 1000,1001,1002,1003 + //Konsole currently supports mode 1000 (basic mouse press and release) and mode 1002 (dragging the mouse). + //TODO: Implementation of mouse modes 1001 (something called hilight tracking) and + //1003 (a slight variation on dragging the mouse) + // + + case TY_CSI_PR('h', 1000) : setMode (MODE_Mouse1000); break; //XTERM + case TY_CSI_PR('l', 1000) : resetMode (MODE_Mouse1000); break; //XTERM + case TY_CSI_PR('s', 1000) : saveMode (MODE_Mouse1000); break; //XTERM + case TY_CSI_PR('r', 1000) : restoreMode (MODE_Mouse1000); break; //XTERM + + case TY_CSI_PR('h', 1001) : /* IGNORED: hilite mouse tracking */ break; //XTERM + case TY_CSI_PR('l', 1001) : resetMode (MODE_Mouse1001); break; //XTERM + case TY_CSI_PR('s', 1001) : /* IGNORED: hilite mouse tracking */ break; //XTERM + case TY_CSI_PR('r', 1001) : /* IGNORED: hilite mouse tracking */ break; //XTERM + + case TY_CSI_PR('h', 1002) : setMode (MODE_Mouse1002); break; //XTERM + case TY_CSI_PR('l', 1002) : resetMode (MODE_Mouse1002); break; //XTERM + case TY_CSI_PR('s', 1002) : saveMode (MODE_Mouse1002); break; //XTERM + case TY_CSI_PR('r', 1002) : restoreMode (MODE_Mouse1002); break; //XTERM + + case TY_CSI_PR('h', 1003) : setMode (MODE_Mouse1003); break; //XTERM + case TY_CSI_PR('l', 1003) : resetMode (MODE_Mouse1003); break; //XTERM + case TY_CSI_PR('s', 1003) : saveMode (MODE_Mouse1003); break; //XTERM + case TY_CSI_PR('r', 1003) : restoreMode (MODE_Mouse1003); break; //XTERM + + case TY_CSI_PR('h', 1005) : setMode (MODE_Mouse1005); break; //XTERM + case TY_CSI_PR('l', 1005) : resetMode (MODE_Mouse1005); break; //XTERM + case TY_CSI_PR('s', 1005) : saveMode (MODE_Mouse1005); break; //XTERM + case TY_CSI_PR('r', 1005) : restoreMode (MODE_Mouse1005); break; //XTERM + + case TY_CSI_PR('h', 1006) : setMode (MODE_Mouse1006); break; //XTERM + case TY_CSI_PR('l', 1006) : resetMode (MODE_Mouse1006); break; //XTERM + case TY_CSI_PR('s', 1006) : saveMode (MODE_Mouse1006); break; //XTERM + case TY_CSI_PR('r', 1006) : restoreMode (MODE_Mouse1006); break; //XTERM + + case TY_CSI_PR('h', 1015) : setMode (MODE_Mouse1015); break; //URXVT + case TY_CSI_PR('l', 1015) : resetMode (MODE_Mouse1015); break; //URXVT + case TY_CSI_PR('s', 1015) : saveMode (MODE_Mouse1015); break; //URXVT + case TY_CSI_PR('r', 1015) : restoreMode (MODE_Mouse1015); break; //URXVT + + case TY_CSI_PR('h', 1034) : /* IGNORED: 8bitinput activation */ break; //XTERM + + case TY_CSI_PR('h', 1047) : setMode (MODE_AppScreen); break; //XTERM + case TY_CSI_PR('l', 1047) : _screen[1]->clearEntireScreen(); resetMode(MODE_AppScreen); break; //XTERM + case TY_CSI_PR('s', 1047) : saveMode (MODE_AppScreen); break; //XTERM + case TY_CSI_PR('r', 1047) : restoreMode (MODE_AppScreen); break; //XTERM + + //FIXME: Unitoken: save translations + case TY_CSI_PR('h', 1048) : saveCursor ( ); break; //XTERM + case TY_CSI_PR('l', 1048) : restoreCursor ( ); break; //XTERM + case TY_CSI_PR('s', 1048) : saveCursor ( ); break; //XTERM + case TY_CSI_PR('r', 1048) : restoreCursor ( ); break; //XTERM + + //FIXME: every once new sequences like this pop up in xterm. + // Here's a guess of what they could mean. + case TY_CSI_PR('h', 1049) : saveCursor(); _screen[1]->clearEntireScreen(); setMode(MODE_AppScreen); break; //XTERM + case TY_CSI_PR('l', 1049) : resetMode(MODE_AppScreen); restoreCursor(); break; //XTERM + + case TY_CSI_PR('h', 2004) : setMode (MODE_BracketedPaste); break; //XTERM + case TY_CSI_PR('l', 2004) : resetMode (MODE_BracketedPaste); break; //XTERM + case TY_CSI_PR('s', 2004) : saveMode (MODE_BracketedPaste); break; //XTERM + case TY_CSI_PR('r', 2004) : restoreMode (MODE_BracketedPaste); break; //XTERM + + //FIXME: weird DEC reset sequence + case TY_CSI_PE('p' ) : /* IGNORED: reset ( ) */ break; + + //FIXME: when changing between vt52 and ansi mode evtl do some resetting. + case TY_VT52('A' ) : _currentScreen->cursorUp ( 1); break; //VT52 + case TY_VT52('B' ) : _currentScreen->cursorDown ( 1); break; //VT52 + case TY_VT52('C' ) : _currentScreen->cursorRight ( 1); break; //VT52 + case TY_VT52('D' ) : _currentScreen->cursorLeft ( 1); break; //VT52 + + case TY_VT52('F' ) : setAndUseCharset (0, '0'); break; //VT52 + case TY_VT52('G' ) : setAndUseCharset (0, 'B'); break; //VT52 + + case TY_VT52('H' ) : _currentScreen->setCursorYX (1,1 ); break; //VT52 + case TY_VT52('I' ) : _currentScreen->reverseIndex ( ); break; //VT52 + case TY_VT52('J' ) : _currentScreen->clearToEndOfScreen ( ); break; //VT52 + case TY_VT52('K' ) : _currentScreen->clearToEndOfLine ( ); break; //VT52 + case TY_VT52('Y' ) : _currentScreen->setCursorYX (p-31,q-31 ); break; //VT52 + case TY_VT52('Z' ) : reportTerminalType ( ); break; //VT52 + case TY_VT52('<' ) : setMode (MODE_Ansi ); break; //VT52 + case TY_VT52('=' ) : setMode (MODE_AppKeyPad); break; //VT52 + case TY_VT52('>' ) : resetMode (MODE_AppKeyPad); break; //VT52 + + case TY_CSI_PG('c' ) : reportSecondaryAttributes( ); break; //VT100 + + default: + reportDecodingError(); + break; + }; +} + +void Vt102Emulation::clearScreenAndSetColumns(int columnCount) +{ + setImageSize(_currentScreen->getLines(),columnCount); + clearEntireScreen(); + setDefaultMargins(); + _currentScreen->setCursorYX(0,0); +} + +void Vt102Emulation::sendString(const char* s , int length) +{ + if ( length >= 0 ) + emit sendData(s,length); + else + emit sendData(s,qstrlen(s)); +} + +void Vt102Emulation::reportCursorPosition() +{ + char tmp[20]; + snprintf(tmp, sizeof(tmp), "\033[%d;%dR", _currentScreen->getCursorY()+1, _currentScreen->getCursorX()+1); + sendString(tmp); +} + +void Vt102Emulation::reportTerminalType() +{ + // Primary device attribute response (Request was: ^[[0c or ^[[c (from TT321 Users Guide)) + // VT220: ^[[?63;1;2;3;6;7;8c (list deps on emul. capabilities) + // VT100: ^[[?1;2c + // VT101: ^[[?1;0c + // VT102: ^[[?6v + if (getMode(MODE_Ansi)) + sendString("\033[?1;2c"); // I'm a VT100 + else + sendString("\033/Z"); // I'm a VT52 +} + +void Vt102Emulation::reportSecondaryAttributes() +{ + // Secondary device attribute response (Request was: ^[[>0c or ^[[>c) + if (getMode(MODE_Ansi)) + sendString("\033[>0;115;0c"); // Why 115? ;) + else + sendString("\033/Z"); // FIXME I don't think VT52 knows about it but kept for + // konsoles backward compatibility. +} + +/* DECREPTPARM – Report Terminal Parameters + ESC [ ; ; ; ; ; ; x + + http://vt100.net/docs/vt100-ug/chapter3.html +*/ +void Vt102Emulation::reportTerminalParms(int p) +{ + char tmp[100]; +/* + sol=1: This message is a request; report in response to a request. + par=1: No parity set + nbits=1: 8 bits per character + xspeed=112: 9600 + rspeed=112: 9600 + clkmul=1: The bit rate multiplier is 16. + flags=0: None +*/ + snprintf(tmp, sizeof(tmp), "\033[%d;1;1;112;112;1;0x", p); // not really true. + sendString(tmp); +} + +void Vt102Emulation::reportStatus() +{ + sendString("\033[0n"); //VT100. Device status report. 0 = Ready. +} + +void Vt102Emulation::reportAnswerBack() +{ + // FIXME - Test this with VTTEST + // This is really obsolete VT100 stuff. + const char* ANSWER_BACK = ""; + sendString(ANSWER_BACK); +} + +/*! + `cx',`cy' are 1-based. + `cb' indicates the button pressed or released (0-2) or scroll event (4-5). + + eventType represents the kind of mouse action that occurred: + 0 = Mouse button press + 1 = Mouse drag + 2 = Mouse button release +*/ + +void Vt102Emulation::sendMouseEvent(int cb, int cx, int cy , int eventType) +{ + if (cx < 1 || cy < 1) + return; + + // With the exception of the 1006 mode, button release is encoded in cb. + // Note that if multiple extensions are enabled, the 1006 is used, so it's okay to check for only that. + if (eventType == 2 && !getMode(MODE_Mouse1006)) + cb = 3; + + // normal buttons are passed as 0x20 + button, + // mouse wheel (buttons 4,5) as 0x5c + button + if (cb >= 4) + cb += 0x3c; + + //Mouse motion handling + if ((getMode(MODE_Mouse1002) || getMode(MODE_Mouse1003)) && eventType == 1) + cb += 0x20; //add 32 to signify motion event + + char command[32]; + command[0] = '\0'; + // Check the extensions in decreasing order of preference. Encoding the release event above assumes that 1006 comes first. + if (getMode(MODE_Mouse1006)) { + snprintf(command, sizeof(command), "\033[<%d;%d;%d%c", cb, cx, cy, eventType == 2 ? 'm' : 'M'); + } else if (getMode(MODE_Mouse1015)) { + snprintf(command, sizeof(command), "\033[%d;%d;%dM", cb + 0x20, cx, cy); + } else if (getMode(MODE_Mouse1005)) { + if (cx <= 2015 && cy <= 2015) { + // The xterm extension uses UTF-8 (up to 2 bytes) to encode + // coordinate+32, no matter what the locale is. We could easily + // convert manually, but QString can also do it for us. + QChar coords[2]; + coords[0] = cx + 0x20; + coords[1] = cy + 0x20; + QString coordsStr = QString(coords, 2); + QByteArray utf8 = coordsStr.toUtf8(); + snprintf(command, sizeof(command), "\033[M%c%s", cb + 0x20, utf8.constData()); + } + } else if (cx <= 223 && cy <= 223) { + snprintf(command, sizeof(command), "\033[M%c%c%c", cb + 0x20, cx + 0x20, cy + 0x20); + } + + sendString(command); +} + +void Vt102Emulation::sendText(const QString& text) +{ + if (!text.isEmpty()) { + QKeyEvent event(QEvent::KeyPress, + 0, + Qt::NoModifier, + text); + sendKeyEvent(&event); // expose as a big fat keypress event + } +} +void Vt102Emulation::sendKeyEvent(QKeyEvent* event) +{ + const Qt::KeyboardModifiers modifiers = event->modifiers(); + KeyboardTranslator::States states = KeyboardTranslator::NoState; + + // get current states + if (getMode(MODE_NewLine)) states |= KeyboardTranslator::NewLineState; + if (getMode(MODE_Ansi)) states |= KeyboardTranslator::AnsiState; + if (getMode(MODE_AppCuKeys)) states |= KeyboardTranslator::CursorKeysState; + if (getMode(MODE_AppScreen)) states |= KeyboardTranslator::AlternateScreenState; + if (getMode(MODE_AppKeyPad) && (modifiers & Qt::KeypadModifier)) + states |= KeyboardTranslator::ApplicationKeypadState; + + // check flow control state + if (modifiers & Qt::ControlModifier) { + switch (event->key()) { + case Qt::Key_S: + emit flowControlKeyPressed(true); + break; + case Qt::Key_Q: + case Qt::Key_C: // cancel flow control + emit flowControlKeyPressed(false); + break; + } + } + + // look up key binding + if (_keyTranslator) { + KeyboardTranslator::Entry entry = _keyTranslator->findEntry( + event->key() , + modifiers, + states); + + // send result to terminal + QByteArray textToSend; + + // special handling for the Alt (aka. Meta) modifier. pressing + // Alt+[Character] results in Esc+[Character] being sent + // (unless there is an entry defined for this particular combination + // in the keyboard modifier) + const bool wantsAltModifier = entry.modifiers() & entry.modifierMask() & Qt::AltModifier; + const bool wantsMetaModifier = entry.modifiers() & entry.modifierMask() & Qt::MetaModifier; + const bool wantsAnyModifier = entry.state() & + entry.stateMask() & KeyboardTranslator::AnyModifierState; + + if ( modifiers & Qt::AltModifier && !(wantsAltModifier || wantsAnyModifier) + && !event->text().isEmpty() ) + { + textToSend.prepend("\033"); + } + if ( modifiers & Qt::MetaModifier && !(wantsMetaModifier || wantsAnyModifier) + && !event->text().isEmpty() ) + { + textToSend.prepend("\030@s"); + } + + if ( entry.command() != KeyboardTranslator::NoCommand ) + { + TerminalDisplay * currentView = _currentScreen->currentTerminalDisplay(); + + if (entry.command() & KeyboardTranslator::EraseCommand) { + textToSend += eraseChar(); + } else if (entry.command() & KeyboardTranslator::ScrollPageUpCommand) + currentView->scrollScreenWindow(ScreenWindow::ScrollPages, -1); + else if (entry.command() & KeyboardTranslator::ScrollPageDownCommand) + currentView->scrollScreenWindow(ScreenWindow::ScrollPages, 1); + else if (entry.command() & KeyboardTranslator::ScrollLineUpCommand) + currentView->scrollScreenWindow(ScreenWindow::ScrollLines, -1); + else if (entry.command() & KeyboardTranslator::ScrollLineDownCommand) + currentView->scrollScreenWindow(ScreenWindow::ScrollLines, 1); + else if (entry.command() & KeyboardTranslator::ScrollUpToTopCommand) + currentView->scrollScreenWindow(ScreenWindow::ScrollLines, + - currentView->screenWindow()->currentLine()); + else if (entry.command() & KeyboardTranslator::ScrollDownToBottomCommand) + currentView->scrollScreenWindow(ScreenWindow::ScrollLines, lineCount()); + } + else if (!entry.text().isEmpty()) + { + textToSend += _codec->fromUnicode(entry.text(true,modifiers)); + } + else + textToSend += _codec->fromUnicode(event->text()); + + sendData(textToSend.constData(), textToSend.length()); + } + else + { + // print an error message to the terminal if no key translator has been + // set + QString translatorError = i18n("No keyboard translator available. " + "The information needed to convert key presses " + "into characters to send to the terminal " + "is missing."); + reset(); + receiveData(translatorError.toAscii().constData(), translatorError.count()); + } +} + +/* ------------------------------------------------------------------------- */ +/* */ +/* VT100 Charsets */ +/* */ +/* ------------------------------------------------------------------------- */ + +// Character Set Conversion ------------------------------------------------ -- + +/* + The processing contains a VT100 specific code translation layer. + It's still in use and mainly responsible for the line drawing graphics. + + These and some other glyphs are assigned to codes (0x5f-0xfe) + normally occupied by the latin letters. Since this codes also + appear within control sequences, the extra code conversion + does not permute with the tokenizer and is placed behind it + in the pipeline. It only applies to tokens, which represent + plain characters. + + This conversion it eventually continued in TerminalDisplay.C, since + it might involve VT100 enhanced fonts, which have these + particular glyphs allocated in (0x00-0x1f) in their code page. +*/ + +#define CHARSET _charset[_currentScreen==_screen[1]] + +// Apply current character map. + +unsigned short Vt102Emulation::applyCharset(unsigned short c) +{ + if (CHARSET.graphic && 0x5f <= c && c <= 0x7e) return vt100_graphics[c - 0x5f]; + if (CHARSET.pound && c == '#') return 0xa3; //This mode is obsolete + return c; +} + +/* + "Charset" related part of the emulation state. + This configures the VT100 charset filter. + + While most operation work on the current _screen, + the following two are different. +*/ + +void Vt102Emulation::resetCharset(int scrno) +{ + _charset[scrno].cu_cs = 0; + qstrncpy(_charset[scrno].charset, "BBBB", 4); + _charset[scrno].sa_graphic = false; + _charset[scrno].sa_pound = false; + _charset[scrno].graphic = false; + _charset[scrno].pound = false; +} + +void Vt102Emulation::setCharset(int n, int cs) // on both screens. +{ + _charset[0].charset[n & 3] = cs; useCharset(_charset[0].cu_cs); + _charset[1].charset[n & 3] = cs; useCharset(_charset[1].cu_cs); +} + +void Vt102Emulation::setAndUseCharset(int n, int cs) +{ + CHARSET.charset[n & 3] = cs; + useCharset(n & 3); +} + +void Vt102Emulation::useCharset(int n) +{ + CHARSET.cu_cs = n & 3; + CHARSET.graphic = (CHARSET.charset[n & 3] == '0'); + CHARSET.pound = (CHARSET.charset[n & 3] == 'A'); //This mode is obsolete +} + +void Vt102Emulation::setDefaultMargins() +{ + _screen[0]->setDefaultMargins(); + _screen[1]->setDefaultMargins(); +} + +void Vt102Emulation::setMargins(int t, int b) +{ + _screen[0]->setMargins(t, b); + _screen[1]->setMargins(t, b); +} + +void Vt102Emulation::saveCursor() +{ + CHARSET.sa_graphic = CHARSET.graphic; + CHARSET.sa_pound = CHARSET.pound; //This mode is obsolete + // we are not clear about these + //sa_charset = charsets[cScreen->_charset]; + //sa_charset_num = cScreen->_charset; + _currentScreen->saveCursor(); +} + +void Vt102Emulation::restoreCursor() +{ + CHARSET.graphic = CHARSET.sa_graphic; + CHARSET.pound = CHARSET.sa_pound; //This mode is obsolete + _currentScreen->restoreCursor(); +} + +/* ------------------------------------------------------------------------- */ +/* */ +/* Mode Operations */ +/* */ +/* ------------------------------------------------------------------------- */ + +/* + Some of the emulations state is either added to the state of the screens. + + This causes some scoping problems, since different emulations choose to + located the mode either to the current _screen or to both. + + For strange reasons, the extend of the rendition attributes ranges over + all screens and not over the actual _screen. + + We decided on the precise precise extend, somehow. +*/ + +// "Mode" related part of the state. These are all booleans. + +void Vt102Emulation::resetModes() +{ + // MODE_Allow132Columns is not reset here + // to match Xterm's behavior (see Xterm's VTReset() function) + + resetMode(MODE_132Columns); saveMode(MODE_132Columns); + resetMode(MODE_Mouse1000); saveMode(MODE_Mouse1000); + resetMode(MODE_Mouse1001); saveMode(MODE_Mouse1001); + resetMode(MODE_Mouse1002); saveMode(MODE_Mouse1002); + resetMode(MODE_Mouse1003); saveMode(MODE_Mouse1003); + resetMode(MODE_Mouse1005); saveMode(MODE_Mouse1005); + resetMode(MODE_Mouse1006); saveMode(MODE_Mouse1006); + resetMode(MODE_Mouse1015); saveMode(MODE_Mouse1015); + resetMode(MODE_BracketedPaste); saveMode(MODE_BracketedPaste); + + resetMode(MODE_AppScreen); saveMode(MODE_AppScreen); + resetMode(MODE_AppCuKeys); saveMode(MODE_AppCuKeys); + resetMode(MODE_AppKeyPad); saveMode(MODE_AppKeyPad); + resetMode(MODE_NewLine); + setMode(MODE_Ansi); +} + +void Vt102Emulation::setMode(int m) +{ + _currentModes.mode[m] = true; + switch (m) { + case MODE_132Columns: + if (getMode(MODE_Allow132Columns)) + clearScreenAndSetColumns(132); + else + _currentModes.mode[m] = false; + break; + case MODE_Mouse1000: + case MODE_Mouse1001: + case MODE_Mouse1002: + case MODE_Mouse1003: + emit programUsesMouseChanged(false); + break; + + case MODE_BracketedPaste: + emit programBracketedPasteModeChanged(true); + break; + + case MODE_AppScreen : + _screen[1]->clearSelection(); + setScreen(1); + break; + } + // FIXME: Currently this has a redundant condition as MODES_SCREEN is 6 + // and MODE_NewLine is 5 + if (m < MODES_SCREEN || m == MODE_NewLine) { + _screen[0]->setMode(m); + _screen[1]->setMode(m); + } +} + +void Vt102Emulation::resetMode(int m) +{ + _currentModes.mode[m] = false; + switch (m) { + case MODE_132Columns: + if (getMode(MODE_Allow132Columns)) + clearScreenAndSetColumns(80); + break; + case MODE_Mouse1000 : + case MODE_Mouse1001 : + case MODE_Mouse1002 : + case MODE_Mouse1003 : + emit programUsesMouseChanged(true); + break; + + case MODE_BracketedPaste: + emit programBracketedPasteModeChanged(false); + break; + + case MODE_AppScreen : + _screen[0]->clearSelection(); + setScreen(0); + break; + } + // FIXME: Currently this has a redundant condition as MODES_SCREEN is 6 + // and MODE_NewLine is 5 + if (m < MODES_SCREEN || m == MODE_NewLine) { + _screen[0]->resetMode(m); + _screen[1]->resetMode(m); + } +} + +void Vt102Emulation::saveMode(int m) +{ + _savedModes.mode[m] = _currentModes.mode[m]; +} + +void Vt102Emulation::restoreMode(int m) +{ + if (_savedModes.mode[m]) + setMode(m); + else + resetMode(m); +} + +bool Vt102Emulation::getMode(int m) +{ + return _currentModes.mode[m]; +} + +char Vt102Emulation::eraseChar() const +{ + KeyboardTranslator::Entry entry = _keyTranslator->findEntry( + Qt::Key_Backspace, + 0, + 0); + if (entry.text().count() > 0) + return entry.text()[0]; + else + return '\b'; +} + +#if 0 +// print contents of the scan buffer +static void hexdump(int* s, int len) +{ + int i; + for (i = 0; i < len; i++) { + if (s[i] == '\\') + printf("\\\\"); + else if ((s[i]) > 32 && s[i] < 127) + printf("%c", s[i]); + else + printf("\\%04x(hex)", s[i]); + } +} +#endif + +// return contents of the scan buffer +static QString hexdump2(int* s, int len) +{ + int i; + char dump[128]; + QString returnDump; + + for (i = 0; i < len; i++) { + if (s[i] == '\\') + snprintf(dump, sizeof(dump), "%s", "\\\\"); + else if ((s[i]) > 32 && s[i] < 127) + snprintf(dump, sizeof(dump), "%c", s[i]); + else + snprintf(dump, sizeof(dump), "\\%04x(hex)", s[i]); + returnDump.append(QString(dump)); + } + return returnDump; +} + +void Vt102Emulation::reportDecodingError() +{ + if (tokenBufferPos == 0 || (tokenBufferPos == 1 && (tokenBuffer[0] & 0xff) >= 32)) + return; + +// printf("Undecodable sequence: "); +// hexdump(tokenBuffer, tokenBufferPos); +// printf("\n"); + + QString outputError = QString("Undecodable sequence: "); + outputError.append(hexdump2(tokenBuffer, tokenBufferPos)); + kDebug() << outputError; +} + +#include "moc_Vt102Emulation.cpp" + diff --git a/konsole/src/Vt102Emulation.h b/konsole/src/Vt102Emulation.h new file mode 100644 index 00000000..763c1a06 --- /dev/null +++ b/konsole/src/Vt102Emulation.h @@ -0,0 +1,190 @@ +/* + This file is part of Konsole, an X terminal. + + Copyright 2007-2008 by Robert Knight + Copyright 1997,1998 by Lars Doelle + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +#ifndef VT102EMULATION_H +#define VT102EMULATION_H + +// Qt +#include + +// Konsole +#include "Emulation.h" +#include "Screen.h" + +#include +#include + +#define MODE_AppScreen (MODES_SCREEN+0) // Mode #1 +#define MODE_AppCuKeys (MODES_SCREEN+1) // Application cursor keys (DECCKM) +#define MODE_AppKeyPad (MODES_SCREEN+2) // +#define MODE_Mouse1000 (MODES_SCREEN+3) // Send mouse X,Y position on press and release +#define MODE_Mouse1001 (MODES_SCREEN+4) // Use Hilight mouse tracking +#define MODE_Mouse1002 (MODES_SCREEN+5) // Use cell motion mouse tracking +#define MODE_Mouse1003 (MODES_SCREEN+6) // Use all motion mouse tracking +#define MODE_Mouse1005 (MODES_SCREEN+7) // Xterm-style extended coordinates +#define MODE_Mouse1006 (MODES_SCREEN+8) // 2nd Xterm-style extended coordinates +#define MODE_Mouse1015 (MODES_SCREEN+9) // Urxvt-style extended coordinates +#define MODE_Ansi (MODES_SCREEN+10) // Use US Ascii for character sets G0-G3 (DECANM) +#define MODE_132Columns (MODES_SCREEN+11) // 80 <-> 132 column mode switch (DECCOLM) +#define MODE_Allow132Columns (MODES_SCREEN+12) // Allow DECCOLM mode +#define MODE_BracketedPaste (MODES_SCREEN+13) // Xterm-style bracketed paste mode +#define MODE_total (MODES_SCREEN+14) + +namespace Konsole +{ +extern unsigned short vt100_graphics[32]; + +struct CharCodes { + // coding info + char charset[4]; // + int cu_cs; // actual charset. + bool graphic; // Some VT100 tricks + bool pound; // Some VT100 tricks + bool sa_graphic; // saved graphic + bool sa_pound; // saved pound +}; + +/** + * Provides an xterm compatible terminal emulation based on the DEC VT102 terminal. + * A full description of this terminal can be found at http://vt100.net/docs/vt102-ug/ + * + * In addition, various additional xterm escape sequences are supported to provide + * features such as mouse input handling. + * See http://rtfm.etla.org/xterm/ctlseq.html for a description of xterm's escape + * sequences. + * + */ +class Vt102Emulation : public Emulation +{ + Q_OBJECT + +public: + /** Constructs a new emulation */ + Vt102Emulation(); + ~Vt102Emulation(); + + // reimplemented from Emulation + virtual void clearEntireScreen(); + virtual void reset(); + virtual char eraseChar() const; + +public slots: + // reimplemented from Emulation + virtual void sendString(const char*, int length = -1); + virtual void sendText(const QString& text); + virtual void sendKeyEvent(QKeyEvent*); + virtual void sendMouseEvent(int buttons, int column, int line, int eventType); + +protected: + // reimplemented from Emulation + virtual void setMode(int mode); + virtual void resetMode(int mode); + virtual void receiveChar(int cc); + +private slots: + //causes changeTitle() to be emitted for each (int,QString) pair in pendingTitleUpdates + //used to buffer multiple title updates + void updateTitle(); + +private: + unsigned short applyCharset(unsigned short c); + void setCharset(int n, int cs); + void useCharset(int n); + void setAndUseCharset(int n, int cs); + void saveCursor(); + void restoreCursor(); + void resetCharset(int scrno); + + void setMargins(int top, int bottom); + //set margins for all screens back to their defaults + void setDefaultMargins(); + + // returns true if 'mode' is set or false otherwise + bool getMode(int mode); + // saves the current boolean value of 'mode' + void saveMode(int mode); + // restores the boolean value of 'mode' + void restoreMode(int mode); + // resets all modes + // (except MODE_Allow132Columns) + void resetModes(); + + void resetTokenizer(); +#define MAX_TOKEN_LENGTH 256 // Max length of tokens (e.g. window title) + void addToCurrentToken(int cc); + int tokenBuffer[MAX_TOKEN_LENGTH]; //FIXME: overflow? + int tokenBufferPos; +#define MAXARGS 15 + void addDigit(int dig); + void addArgument(); + int argv[MAXARGS]; + int argc; + void initTokenizer(); + + // Set of flags for each of the ASCII characters which indicates + // what category they fall into (printable character, control, digit etc.) + // for the purposes of decoding terminal output + int charClass[256]; + + void reportDecodingError(); + + void processToken(int code, int p, int q); + void processWindowAttributeChange(); + + void reportTerminalType(); + void reportSecondaryAttributes(); + void reportStatus(); + void reportAnswerBack(); + void reportCursorPosition(); + void reportTerminalParms(int p); + + // clears the screen and resizes it to the specified + // number of columns + void clearScreenAndSetColumns(int columnCount); + + CharCodes _charset[2]; + + class TerminalState + { + public: + // Initializes all modes to false + TerminalState() { + memset(&mode, false, MODE_total * sizeof(bool)); + } + + bool mode[MODE_total]; + }; + + TerminalState _currentModes; + TerminalState _savedModes; + + //hash table and timer for buffering calls to the session instance + //to update the name of the session + //or window title. + //these calls occur when certain escape sequences are seen in the + //output from the terminal + QHash _pendingTitleUpdates; + QTimer* _titleUpdateTimer; +}; +} + +#endif // VT102EMULATION_H diff --git a/konsole/src/ZModemDialog.cpp b/konsole/src/ZModemDialog.cpp new file mode 100644 index 00000000..5f99417e --- /dev/null +++ b/konsole/src/ZModemDialog.cpp @@ -0,0 +1,70 @@ +/* This file is part of the KDE libraries + * Copyright 2002 Waldo Bastian + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License version 2 as published by the Free Software Foundation; + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + **/ + +// Own +#include "ZModemDialog.h" + +// KDE +#include +#include + +using Konsole::ZModemDialog; + +ZModemDialog::ZModemDialog(QWidget* aParent, bool modal, const QString& caption) + : KDialog(aParent) +{ + setObjectName(QLatin1String("zmodem_progress")); + setModal(modal); + setCaption(caption); + + setButtons(KDialog::User1 | KDialog::Close); + setButtonGuiItem(KDialog::User1, KGuiItem(i18n("&Stop"))); + setDefaultButton(KDialog::Close); + setEscapeButton(KDialog::User1); + enableButton(KDialog::Close, false); + + _textEdit = new KTextEdit(this); + _textEdit->setMinimumSize(400, 100); + _textEdit->setReadOnly(true); + setMainWidget(_textEdit); + + connect(this, SIGNAL(user1Clicked()), this, SLOT(slotClose())); + connect(this, SIGNAL(closeClicked()), this, SLOT(slotClose())); +} + +void ZModemDialog::addProgressText(const QString& text) +{ + QTextCursor currentCursor = _textEdit->textCursor(); + + currentCursor.insertBlock(); + currentCursor.insertText(text); +} + +void ZModemDialog::transferDone() +{ + enableButton(KDialog::Close, true); + enableButton(KDialog::User1, false); +} + +void ZModemDialog::slotClose() +{ + delayedDestruct(); + accept(); +} + +#include "moc_ZModemDialog.cpp" diff --git a/konsole/src/ZModemDialog.h b/konsole/src/ZModemDialog.h new file mode 100644 index 00000000..a388f43b --- /dev/null +++ b/konsole/src/ZModemDialog.h @@ -0,0 +1,54 @@ +/* This file is part of the KDE libraries + * Copyright 2002 Waldo Bastian + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License version 2 as published by the Free Software Foundation; + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + **/ + +#ifndef ZMODEM_DIALOG_H +#define ZMODEM_DIALOG_H + +// KDE +#include + +class KTextEdit; + +namespace Konsole +{ +class ZModemDialog : public KDialog +{ + Q_OBJECT + +public: + ZModemDialog(QWidget* parent, bool modal, const QString& caption); + + /** + * Adds a line of text to the progress window + */ + void addProgressText(const QString &); + + /** + * To indicate the process is finished. + */ + void transferDone(); + +private slots: + void slotClose(); + +private: + KTextEdit* _textEdit; +}; +} + +#endif diff --git a/konsole/src/fontembedder.cpp b/konsole/src/fontembedder.cpp new file mode 100644 index 00000000..eda001b2 --- /dev/null +++ b/konsole/src/fontembedder.cpp @@ -0,0 +1,134 @@ +/* + This file is part of Konsole, an X terminal. + Copyright 2005 by Maksim Orlovich + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +// Standard +#include +#include +#include + +// Qt +#include +#include + +#include + +using namespace std; + +static quint32 charVal(QChar val) +{ + if (val == ' ') + return 0; + else + return 1; +} + +static quint32 readGlyphLine(QTextStream& input) +{ + QString line = input.readLine(); + while (line.length() < 5) + line += ' '; + + quint32 val = charVal(line[0]) | + (charVal(line[1]) << 1) | + (charVal(line[2]) << 2) | + (charVal(line[3]) << 3) | + (charVal(line[4]) << 4); + return val; +} + +static quint32 readGlyph(QTextStream& input) +{ + return readGlyphLine(input) | + (readGlyphLine(input) << 5) | + (readGlyphLine(input) << 10) | + (readGlyphLine(input) << 15) | + (readGlyphLine(input) << 20); +} + +int main(int argc, char **argv) +{ + if (argc != 2) { + qWarning("usage: fontembedder LineFont.src > LineFont.h"); + exit(1); + } + QFile inFile(argv[1]); + if (!inFile.open(QIODevice::ReadOnly)) { + qWarning("Can not open %s", argv[1]); + exit(1); + } + + QTextStream input(&inFile); + + // Currently, for Konsole, the input glyphs file ranges from 0x2500 + // (9472) to x257f (9599) so this 128 will handle it. However, if + // more glyphs are added to the input file, this will be an issue. + quint32 glyphStates[128]; + QMap glyphMap; + + for (int i = 0; i < 128; ++i) + glyphStates[i] = 0; //nothing.. + + while (!input.atEnd()) { + QString line = input.readLine(); + line = line.trimmed(); + if (line.isEmpty()) + continue; //Skip empty lines + if (line[0] == '#') + continue; //Skip comments + + //Must be a glyph ID. + int glyph = line.toInt(0, 16); + if ((glyph < 0x2500) || (glyph > 0x257f)) { + qWarning("Invalid glyph number: %d aborting...", glyph); + exit(1); + } else { + glyph = glyph - 0x2500; + + glyphStates[glyph] = readGlyph(input); + // kWarning()< and + * Francesco Cecconi + * See COPYING.Unicode for the license for the original wcwidth.c + */ + +// Own +#include "konsole_wcwidth.h" +#include "konsoleprivate_export.h" + +struct interval { + unsigned long first; + unsigned long last; +}; + +/* auxiliary function for binary search in interval table */ +static int bisearch(unsigned long ucs, const struct interval* table, int max) +{ + int min = 0; + int mid; + + if (ucs < table[0].first || ucs > table[max].last) + return 0; + while (max >= min) { + mid = (min + max) / 2; + if (ucs > table[mid].last) + min = mid + 1; + else if (ucs < table[mid].first) + max = mid - 1; + else + return 1; + } + + return 0; +} + +/* The following functions define the column width of an ISO 10646 + * character as follows: + * + * - The null character (U+0000) has a column width of 0. + * + * - Other C0/C1 control characters and DEL will lead to a return + * value of -1. + * + * - Non-spacing and enclosing combining characters (general + * category code Mn or Me in the Unicode database) have a + * column width of 0. + * + * - Other format characters (general category code Cf in the Unicode + * database) and ZERO WIDTH SPACE (U+200B) have a column width of 0. + * + * - Hangul Jamo medial vowels and final consonants (U+1160-U+11FF) + * have a column width of 0. + * + * - Spacing characters in the East Asian Wide (W) or East Asian + * FullWidth (F) category as defined in Unicode Technical + * Report #11 have a column width of 2. + * + * - All remaining characters (including all printable + * ISO 8859-1 and WGL4 characters, Unicode control characters, + * etc.) have a column width of 1. + * + * This implementation assumes that quint16 characters are encoded + * in ISO 10646. + */ + +int KONSOLEPRIVATE_EXPORT konsole_wcwidth(quint16 oucs) +{ + /* NOTE: uniset script can be obtained from: + * https://fossies.org/linux/R/tools/uniset + */ + /* NOTE: It is not possible to compare quint16 with the new last four lines of characters, + * therefore this cast is now necessary. + */ + unsigned long ucs = static_cast(oucs); + /* sorted list of non-overlapping intervals of non-spacing characters */ + /* generated by "uniset +cat=Me +cat=Mn +cat=Cf -00AD +1160-11FF +200B c" */ + static const struct interval combining[] = { + { 0x300, 0x36f }, + { 0x483, 0x489 }, + { 0x591, 0x5bd }, + { 0x5bf, 0x5bf }, + { 0x5c1, 0x5c2 }, + { 0x5c4, 0x5c5 }, + { 0x5c7, 0x5c7 }, + { 0x600, 0x605 }, + { 0x610, 0x61a }, + { 0x61c, 0x61c }, + { 0x64b, 0x65f }, + { 0x670, 0x670 }, + { 0x6d6, 0x6dd }, + { 0x6df, 0x6e4 }, + { 0x6e7, 0x6e8 }, + { 0x6ea, 0x6ed }, + { 0x70f, 0x70f }, + { 0x711, 0x711 }, + { 0x730, 0x74a }, + { 0x7a6, 0x7b0 }, + { 0x7eb, 0x7f3 }, + { 0x7fd, 0x7fd }, + { 0x816, 0x819 }, + { 0x81b, 0x823 }, + { 0x825, 0x827 }, + { 0x829, 0x82d }, + { 0x859, 0x85b }, + { 0x8d3, 0x902 }, + { 0x93a, 0x93a }, + { 0x93c, 0x93c }, + { 0x941, 0x948 }, + { 0x94d, 0x94d }, + { 0x951, 0x957 }, + { 0x962, 0x963 }, + { 0x981, 0x981 }, + { 0x9bc, 0x9bc }, + { 0x9c1, 0x9c4 }, + { 0x9cd, 0x9cd }, + { 0x9e2, 0x9e3 }, + { 0x9fe, 0x9fe }, + { 0xa01, 0xa02 }, + { 0xa3c, 0xa3c }, + { 0xa41, 0xa42 }, + { 0xa47, 0xa48 }, + { 0xa4b, 0xa4d }, + { 0xa51, 0xa51 }, + { 0xa70, 0xa71 }, + { 0xa75, 0xa75 }, + { 0xa81, 0xa82 }, + { 0xabc, 0xabc }, + { 0xac1, 0xac5 }, + { 0xac7, 0xac8 }, + { 0xacd, 0xacd }, + { 0xae2, 0xae3 }, + { 0xafa, 0xaff }, + { 0xb01, 0xb01 }, + { 0xb3c, 0xb3c }, + { 0xb3f, 0xb3f }, + { 0xb41, 0xb44 }, + { 0xb4d, 0xb4d }, + { 0xb55, 0xb56 }, + { 0xb62, 0xb63 }, + { 0xb82, 0xb82 }, + { 0xbc0, 0xbc0 }, + { 0xbcd, 0xbcd }, + { 0xc00, 0xc00 }, + { 0xc04, 0xc04 }, + { 0xc3e, 0xc40 }, + { 0xc46, 0xc48 }, + { 0xc4a, 0xc4d }, + { 0xc55, 0xc56 }, + { 0xc62, 0xc63 }, + { 0xc81, 0xc81 }, + { 0xcbc, 0xcbc }, + { 0xcbf, 0xcbf }, + { 0xcc6, 0xcc6 }, + { 0xccc, 0xccd }, + { 0xce2, 0xce3 }, + { 0xd00, 0xd01 }, + { 0xd3b, 0xd3c }, + { 0xd41, 0xd44 }, + { 0xd4d, 0xd4d }, + { 0xd62, 0xd63 }, + { 0xd81, 0xd81 }, + { 0xdca, 0xdca }, + { 0xdd2, 0xdd4 }, + { 0xdd6, 0xdd6 }, + { 0xe31, 0xe31 }, + { 0xe34, 0xe3a }, + { 0xe47, 0xe4e }, + { 0xeb1, 0xeb1 }, + { 0xeb4, 0xebc }, + { 0xec8, 0xecd }, + { 0xf18, 0xf19 }, + { 0xf35, 0xf35 }, + { 0xf37, 0xf37 }, + { 0xf39, 0xf39 }, + { 0xf71, 0xf7e }, + { 0xf80, 0xf84 }, + { 0xf86, 0xf87 }, + { 0xf8d, 0xf97 }, + { 0xf99, 0xfbc }, + { 0xfc6, 0xfc6 }, + { 0x102d, 0x1030 }, + { 0x1032, 0x1037 }, + { 0x1039, 0x103a }, + { 0x103d, 0x103e }, + { 0x1058, 0x1059 }, + { 0x105e, 0x1060 }, + { 0x1071, 0x1074 }, + { 0x1082, 0x1082 }, + { 0x1085, 0x1086 }, + { 0x108d, 0x108d }, + { 0x109d, 0x109d }, + { 0x1160, 0x11ff }, + { 0x135d, 0x135f }, + { 0x1712, 0x1714 }, + { 0x1732, 0x1734 }, + { 0x1752, 0x1753 }, + { 0x1772, 0x1773 }, + { 0x17b4, 0x17b5 }, + { 0x17b7, 0x17bd }, + { 0x17c6, 0x17c6 }, + { 0x17c9, 0x17d3 }, + { 0x17dd, 0x17dd }, + { 0x180b, 0x180e }, + { 0x1885, 0x1886 }, + { 0x18a9, 0x18a9 }, + { 0x1920, 0x1922 }, + { 0x1927, 0x1928 }, + { 0x1932, 0x1932 }, + { 0x1939, 0x193b }, + { 0x1a17, 0x1a18 }, + { 0x1a1b, 0x1a1b }, + { 0x1a56, 0x1a56 }, + { 0x1a58, 0x1a5e }, + { 0x1a60, 0x1a60 }, + { 0x1a62, 0x1a62 }, + { 0x1a65, 0x1a6c }, + { 0x1a73, 0x1a7c }, + { 0x1a7f, 0x1a7f }, + { 0x1ab0, 0x1ac0 }, + { 0x1b00, 0x1b03 }, + { 0x1b34, 0x1b34 }, + { 0x1b36, 0x1b3a }, + { 0x1b3c, 0x1b3c }, + { 0x1b42, 0x1b42 }, + { 0x1b6b, 0x1b73 }, + { 0x1b80, 0x1b81 }, + { 0x1ba2, 0x1ba5 }, + { 0x1ba8, 0x1ba9 }, + { 0x1bab, 0x1bad }, + { 0x1be6, 0x1be6 }, + { 0x1be8, 0x1be9 }, + { 0x1bed, 0x1bed }, + { 0x1bef, 0x1bf1 }, + { 0x1c2c, 0x1c33 }, + { 0x1c36, 0x1c37 }, + { 0x1cd0, 0x1cd2 }, + { 0x1cd4, 0x1ce0 }, + { 0x1ce2, 0x1ce8 }, + { 0x1ced, 0x1ced }, + { 0x1cf4, 0x1cf4 }, + { 0x1cf8, 0x1cf9 }, + { 0x1dc0, 0x1df9 }, + { 0x1dfb, 0x1dff }, + { 0x200b, 0x200f }, + { 0x202a, 0x202e }, + { 0x2060, 0x2064 }, + { 0x2066, 0x206f }, + { 0x20d0, 0x20f0 }, + { 0x2cef, 0x2cf1 }, + { 0x2d7f, 0x2d7f }, + { 0x2de0, 0x2dff }, + { 0x302a, 0x302d }, + { 0x3099, 0x309a }, + { 0xa66f, 0xa672 }, + { 0xa674, 0xa67d }, + { 0xa69e, 0xa69f }, + { 0xa6f0, 0xa6f1 }, + { 0xa802, 0xa802 }, + { 0xa806, 0xa806 }, + { 0xa80b, 0xa80b }, + { 0xa825, 0xa826 }, + { 0xa82c, 0xa82c }, + { 0xa8c4, 0xa8c5 }, + { 0xa8e0, 0xa8f1 }, + { 0xa8ff, 0xa8ff }, + { 0xa926, 0xa92d }, + { 0xa947, 0xa951 }, + { 0xa980, 0xa982 }, + { 0xa9b3, 0xa9b3 }, + { 0xa9b6, 0xa9b9 }, + { 0xa9bc, 0xa9bd }, + { 0xa9e5, 0xa9e5 }, + { 0xaa29, 0xaa2e }, + { 0xaa31, 0xaa32 }, + { 0xaa35, 0xaa36 }, + { 0xaa43, 0xaa43 }, + { 0xaa4c, 0xaa4c }, + { 0xaa7c, 0xaa7c }, + { 0xaab0, 0xaab0 }, + { 0xaab2, 0xaab4 }, + { 0xaab7, 0xaab8 }, + { 0xaabe, 0xaabf }, + { 0xaac1, 0xaac1 }, + { 0xaaec, 0xaaed }, + { 0xaaf6, 0xaaf6 }, + { 0xabe5, 0xabe5 }, + { 0xabe8, 0xabe8 }, + { 0xabed, 0xabed }, + { 0xfb1e, 0xfb1e }, + { 0xfe00, 0xfe0f }, + { 0xfe20, 0xfe2f }, + { 0xfeff, 0xfeff }, + { 0xfff9, 0xfffb }, + { 0x101fd, 0x101fd }, + { 0x102e0, 0x102e0 }, + { 0x10376, 0x1037a }, + { 0x10a01, 0x10a03 }, + { 0x10a05, 0x10a06 }, + { 0x10a0c, 0x10a0f }, + { 0x10a38, 0x10a3a }, + { 0x10a3f, 0x10a3f }, + { 0x10ae5, 0x10ae6 }, + { 0x10d24, 0x10d27 }, + { 0x10eab, 0x10eac }, + { 0x10f46, 0x10f50 }, + { 0x11001, 0x11001 }, + { 0x11038, 0x11046 }, + { 0x1107f, 0x11081 }, + { 0x110b3, 0x110b6 }, + { 0x110b9, 0x110ba }, + { 0x110bd, 0x110bd }, + { 0x110cd, 0x110cd }, + { 0x11100, 0x11102 }, + { 0x11127, 0x1112b }, + { 0x1112d, 0x11134 }, + { 0x11173, 0x11173 }, + { 0x11180, 0x11181 }, + { 0x111b6, 0x111be }, + { 0x111c9, 0x111cc }, + { 0x111cf, 0x111cf }, + { 0x1122f, 0x11231 }, + { 0x11234, 0x11234 }, + { 0x11236, 0x11237 }, + { 0x1123e, 0x1123e }, + { 0x112df, 0x112df }, + { 0x112e3, 0x112ea }, + { 0x11300, 0x11301 }, + { 0x1133b, 0x1133c }, + { 0x11340, 0x11340 }, + { 0x11366, 0x1136c }, + { 0x11370, 0x11374 }, + { 0x11438, 0x1143f }, + { 0x11442, 0x11444 }, + { 0x11446, 0x11446 }, + { 0x1145e, 0x1145e }, + { 0x114b3, 0x114b8 }, + { 0x114ba, 0x114ba }, + { 0x114bf, 0x114c0 }, + { 0x114c2, 0x114c3 }, + { 0x115b2, 0x115b5 }, + { 0x115bc, 0x115bd }, + { 0x115bf, 0x115c0 }, + { 0x115dc, 0x115dd }, + { 0x11633, 0x1163a }, + { 0x1163d, 0x1163d }, + { 0x1163f, 0x11640 }, + { 0x116ab, 0x116ab }, + { 0x116ad, 0x116ad }, + { 0x116b0, 0x116b5 }, + { 0x116b7, 0x116b7 }, + { 0x1171d, 0x1171f }, + { 0x11722, 0x11725 }, + { 0x11727, 0x1172b }, + { 0x1182f, 0x11837 }, + { 0x11839, 0x1183a }, + { 0x1193b, 0x1193c }, + { 0x1193e, 0x1193e }, + { 0x11943, 0x11943 }, + { 0x119d4, 0x119d7 }, + { 0x119da, 0x119db }, + { 0x119e0, 0x119e0 }, + { 0x11a01, 0x11a0a }, + { 0x11a33, 0x11a38 }, + { 0x11a3b, 0x11a3e }, + { 0x11a47, 0x11a47 }, + { 0x11a51, 0x11a56 }, + { 0x11a59, 0x11a5b }, + { 0x11a8a, 0x11a96 }, + { 0x11a98, 0x11a99 }, + { 0x11c30, 0x11c36 }, + { 0x11c38, 0x11c3d }, + { 0x11c3f, 0x11c3f }, + { 0x11c92, 0x11ca7 }, + { 0x11caa, 0x11cb0 }, + { 0x11cb2, 0x11cb3 }, + { 0x11cb5, 0x11cb6 }, + { 0x11d31, 0x11d36 }, + { 0x11d3a, 0x11d3a }, + { 0x11d3c, 0x11d3d }, + { 0x11d3f, 0x11d45 }, + { 0x11d47, 0x11d47 }, + { 0x11d90, 0x11d91 }, + { 0x11d95, 0x11d95 }, + { 0x11d97, 0x11d97 }, + { 0x11ef3, 0x11ef4 }, + { 0x13430, 0x13438 }, + { 0x16af0, 0x16af4 }, + { 0x16b30, 0x16b36 }, + { 0x16f4f, 0x16f4f }, + { 0x16f8f, 0x16f92 }, + { 0x16fe4, 0x16fe4 }, + { 0x1bc9d, 0x1bc9e }, + { 0x1bca0, 0x1bca3 }, + { 0x1d167, 0x1d169 }, + { 0x1d173, 0x1d182 }, + { 0x1d185, 0x1d18b }, + { 0x1d1aa, 0x1d1ad }, + { 0x1d242, 0x1d244 }, + { 0x1da00, 0x1da36 }, + { 0x1da3b, 0x1da6c }, + { 0x1da75, 0x1da75 }, + { 0x1da84, 0x1da84 }, + { 0x1da9b, 0x1da9f }, + { 0x1daa1, 0x1daaf }, + { 0x1e000, 0x1e006 }, + { 0x1e008, 0x1e018 }, + { 0x1e01b, 0x1e021 }, + { 0x1e023, 0x1e024 }, + { 0x1e026, 0x1e02a }, + { 0x1e130, 0x1e136 }, + { 0x1e2ec, 0x1e2ef }, + { 0x1e8d0, 0x1e8d6 }, + { 0x1e944, 0x1e94a }, + { 0xe0001, 0xe0001 }, + { 0xe0020, 0xe007f }, + { 0xe0100, 0xe01ef } + }; + + /* test for 8-bit control characters */ + if (ucs == 0) + return 0; + if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0)) + return -1; + + /* binary search in table of non-spacing characters */ + if (bisearch(ucs, combining, + sizeof(combining) / sizeof(struct interval) - 1)) + return 0; + + /* if we arrive here, ucs is not a combining or C0/C1 control character */ + + return 1 + + (ucs >= 0x1100 && + (ucs <= 0x115f || /* Hangul Jamo init. consonants */ + ucs == 0x2329 || ucs == 0x232a || + (ucs >= 0x2e80 && ucs <= 0xa4cf && + ucs != 0x303f) || /* CJK ... Yi */ + (ucs >= 0xac00 && ucs <= 0xd7a3) || /* Hangul Syllables */ + (ucs >= 0xf900 && ucs <= 0xfaff) || /* CJK Compatibility Ideographs */ + (ucs >= 0xfe10 && ucs <= 0xfe19) || /* Vertical forms */ + (ucs >= 0xfe30 && ucs <= 0xfe6f) || /* CJK Compatibility Forms */ + (ucs >= 0xff00 && ucs <= 0xff60) || /* Fullwidth Forms */ + (ucs >= 0xffe0 && ucs <= 0xffe6) || + (ucs >= 0x20000 && ucs <= 0x2fffd) || + (ucs >= 0x30000 && ucs <= 0x3fffd))); +} + +int string_width(const QString& text) +{ + int w = 0; + for (int i = 0; i < text.length(); ++i) + w += konsole_wcwidth(text[i].unicode()); + return w; +} + diff --git a/konsole/src/konsole_wcwidth.h b/konsole/src/konsole_wcwidth.h new file mode 100644 index 00000000..f6cd7287 --- /dev/null +++ b/konsole/src/konsole_wcwidth.h @@ -0,0 +1,16 @@ +/* $XFree86: xc/programs/xterm/wcwidth.h,v 1.5 2005/05/03 00:38:25 dickey Exp $ */ + +/* Markus Kuhn -- 2001-01-12 -- public domain */ +/* Adaptions for KDE by Waldo Bastian */ + +#ifndef KONSOLE_WCWIDTH_H +#define KONSOLE_WCWIDTH_H + +// Qt +#include + +int konsole_wcwidth(quint16 oucs); + +int string_width(const QString& text); + +#endif diff --git a/konsole/src/konsoleprofile b/konsole/src/konsoleprofile new file mode 100755 index 00000000..eb67a8ee --- /dev/null +++ b/konsole/src/konsoleprofile @@ -0,0 +1,40 @@ +#!/bin/sh +# +# This file is in the public domain. + +# A command-line tool to change the current tab's profile options. +# +# Usage: konsoleprofile option=value +# +# Example: 'konsoleprofile ColorScheme=WhiteOnBlack' will change the +# colorscheme used in current tab into WhiteOnBlack on the fly. +# +# NOTE: This script MUST run within a konsole tab to take effect. The change +# is applied only to current tab. Other tabs using the same profile will not +# be influenced. Any changes won't be saved to to disk. +# +# For the full list of supported options and values: +# 1. konsole --list-profile-properties +# 2. refer to konsole/src/Profile.h +# 3. visit the online reference: +# http://api.kde.org/4.7-api/kde-baseapps-apidocs/konsole/html/namespaceKonsole.html +# http://api.kde.org/4.7-api/kde-baseapps-apidocs/konsole/html/classKonsole_1_1Profile.html +# +# All of the logic is in konsole. This script is provided for convenience. + +if [ ! $# -eq 1 ] + then + echo "" + echo "Usage: $0 option=value" + echo "" + echo "For more documentation view this file $0" + echo "" + echo "The complete list of profile options can be displayed using:" + echo " konsole --list-profile-properties" + echo "" + exit 0 +fi + +# Use printf since echo is not portable +# http://pubs.opengroup.org/onlinepubs/009695399/utilities/echo.html +printf "\033]50;%s\a" "$1" diff --git a/konsole/src/main.cpp b/konsole/src/main.cpp new file mode 100644 index 00000000..4db0e9ae --- /dev/null +++ b/konsole/src/main.cpp @@ -0,0 +1,293 @@ +/* + Copyright 2006-2008 by Robert Knight + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +// Own +#include "Application.h" +#include "MainWindow.h" + +// OS specific +#include + +// KDE +#include +#include +#include + +#define KONSOLE_VERSION "2.14.2" + +using Konsole::Application; + +// fill the KAboutData structure with information about contributors to Konsole. +void fillAboutData(KAboutData& aboutData); + +// fill the KCmdLineOptions object with konsole specific options. +void fillCommandLineOptions(KCmdLineOptions& options); + +// check and report whether this konsole instance should use a new konsole +// process, or re-use an existing konsole process. +bool shouldUseNewProcess(); + +// restore sessions saved by KDE. +void restoreSession(Application& app); + +// *** +// Entry point into the Konsole terminal application. +// *** +int main(int argc, char** argv) +{ + KAboutData about("konsole", + 0, + ki18nc("@title", "Konsole"), + KONSOLE_VERSION, + ki18nc("@title", "Terminal emulator"), + KAboutData::License_GPL_V2 + ); + fillAboutData(about); + + KCmdLineArgs::init(argc, argv, &about); + KCmdLineArgs::addStdCmdLineOptions(); // Qt and KDE options + KUniqueApplication::addCmdLineOptions(); // KUniqueApplication options + KCmdLineOptions konsoleOptions; // Konsole options + fillCommandLineOptions(konsoleOptions); + KCmdLineArgs::addCmdLineOptions(konsoleOptions); + + KUniqueApplication::StartFlags startFlags; + if (shouldUseNewProcess()) + startFlags = KUniqueApplication::NonUniqueInstance; + + // create a new application instance if there are no running Konsole + // instances, otherwise inform the existing Konsole process and exit + if (!KUniqueApplication::start(startFlags)) { + exit(0); + } + + Application app; + + // make sure the d&d popup menu provided by libkonq get translated. + KGlobal::locale()->insertCatalog("libkonq"); + + restoreSession(app); + return app.exec(); +} +bool shouldUseNewProcess() +{ + // The "unique process" model of konsole is incompatible with some or all + // Qt/KDE options. When those incompatible options are given, konsole must + // use new process + // + // TODO: make sure the existing list is OK and add more incompatible options. + + // take Qt options into consideration + const KCmdLineArgs* qtArgs = KCmdLineArgs::parsedArgs("qt"); + QStringList qtProblematicOptions; + qtProblematicOptions << "session" << "name" << "reverse" + << "stylesheet" << "graphicssystem"; +#if defined(Q_WS_X11) + qtProblematicOptions << "display" << "visual"; +#endif + foreach(const QString& option, qtProblematicOptions) { + if ( qtArgs->isSet(option.toLocal8Bit()) ) { + return true; + } + } + + // take KDE options into consideration + const KCmdLineArgs* kdeArgs = KCmdLineArgs::parsedArgs("kde"); + QStringList kdeProblematicOptions; + kdeProblematicOptions << "config" << "style"; +#if defined(Q_WS_X11) + kdeProblematicOptions << "waitforwm"; +#endif + foreach(const QString& option, kdeProblematicOptions) { + if ( kdeArgs->isSet(option.toLocal8Bit()) ) { + return true; + } + } + + const KCmdLineArgs* kUniqueAppArgs = KCmdLineArgs::parsedArgs("kuniqueapp"); + + // when user asks konsole to run in foreground through the --nofork option + // provided by KUniqueApplication, we must use new process. Otherwise, there + // will be no process for users to wait for finishing. + const bool shouldRunInForeground = !kUniqueAppArgs->isSet("fork"); + if (shouldRunInForeground) { + return true; + } + + const KCmdLineArgs* konsoleArgs = KCmdLineArgs::parsedArgs(); + + // if users have explictly requested starting a new process + if (konsoleArgs->isSet("separate")) { + return true; + } + + // the only way to create new tab is to reuse existing Konsole process. + if (konsoleArgs->isSet("new-tab")) { + return false; + } + + // when starting Konsole from a terminal, a new process must be used + // so that the current environment is propagated into the shells of the new + // Konsole and any debug output or warnings from Konsole are written to + // the current terminal + bool hasControllingTTY = false; + const int fd = KDE_open("/dev/tty", O_RDONLY); + if (fd != -1) { + hasControllingTTY = true; + close(fd); + } + + return hasControllingTTY; +} + +void fillCommandLineOptions(KCmdLineOptions& options) +{ + options.add("profile ", + ki18nc("@info:shell", "Name of profile to use for new Konsole instance")); + options.add("fallback-profile", + ki18nc("@info:shell", "Use the internal FALLBACK profile")); + options.add("workdir ", + ki18nc("@info:shell", "Set the initial working directory of the new tab or" + " window to 'dir'")); + options.add("hold"); + options.add("noclose", + ki18nc("@info:shell", "Do not close the initial session automatically when it" + " ends.")); + options.add("new-tab", + ki18nc("@info:shell", "Create a new tab in an existing window rather than" + " creating a new window")); + options.add("tabs-from-file ", + ki18nc("@info:shell", "Create tabs as specified in given tabs configuration" + " file")); + options.add("background-mode", + ki18nc("@info:shell", "Start Konsole in the background and bring to the front" + " when Ctrl+Shift+F12 (by default) is pressed")); + options.add("separate", ki18n("Run in a separate process")); + options.add("show-menubar", ki18nc("@info:shell", "Show the menubar, overriding the default setting")); + options.add("hide-menubar", ki18nc("@info:shell", "Hide the menubar, overriding the default setting")); + options.add("show-tabbar", ki18nc("@info:shell", "Show the tabbar, overriding the default setting")); + options.add("hide-tabbar", ki18nc("@info:shell", "Hide the tabbar, overriding the default setting")); + options.add("fullscreen", ki18nc("@info:shell", "Start Konsole in fullscreen mode")); + options.add("notransparency", + ki18nc("@info:shell", "Disable transparent backgrounds, even if the system" + " supports them.")); + options.add("list-profiles", ki18nc("@info:shell", "List the available profiles")); + options.add("list-profile-properties", + ki18nc("@info:shell", "List all the profile properties names and their type" + " (for use with -p)")); + options.add("p ", + ki18nc("@info:shell", "Change the value of a profile property.")); + options.add("!e ", + ki18nc("@info:shell", "Command to execute. This option will catch all following" + " arguments, so use it as the last option.")); + options.add("+[args]", ki18nc("@info:shell", "Arguments passed to command")); + options.add("", ki18nc("@info:shell", "Use --nofork to run in the foreground (helpful" + " with the -e option).")); +} + +void fillAboutData(KAboutData& aboutData) +{ + aboutData.setProgramIconName("utilities-terminal"); + aboutData.setHomepage("http://konsole.kde.org"); + + aboutData.addAuthor(ki18nc("@info:credit", "Kurt Hindenburg"), + ki18nc("@info:credit", "General maintainer, bug fixes and general" + " improvements"), + "kurt.hindenburg@gmail.com"); + aboutData.addAuthor(ki18nc("@info:credit", "Robert Knight"), + ki18nc("@info:credit", "Previous maintainer, ported to KDE4"), + "robertknight@gmail.com"); + aboutData.addAuthor(ki18nc("@info:credit", "Lars Doelle"), + ki18nc("@info:credit", "Original author"), + "lars.doelle@on-line.de"); + aboutData.addCredit(ki18nc("@info:credit", "Jekyll Wu"), + ki18nc("@info:credit", "Bug fixes and general improvements"), + "adaptee@gmail.com"); + aboutData.addCredit(ki18nc("@info:credit", "Waldo Bastian"), + ki18nc("@info:credit", "Bug fixes and general improvements"), + "bastian@kde.org"); + aboutData.addCredit(ki18nc("@info:credit", "Stephan Binner"), + ki18nc("@info:credit", "Bug fixes and general improvements"), + "binner@kde.org"); + aboutData.addCredit(ki18nc("@info:credit", "Thomas Dreibholz"), + ki18nc("@info:credit", "General improvements"), + "dreibh@iem.uni-due.de"); + aboutData.addCredit(ki18nc("@info:credit", "Chris Machemer"), + ki18nc("@info:credit", "Bug fixes"), + "machey@ceinetworks.com"); + aboutData.addCredit(ki18nc("@info:credit", "Francesco Cecconi"), + ki18nc("@info:credit", "Bug fixes"), + "francesco.cecconi@gmail.com"); + aboutData.addCredit(ki18nc("@info:credit", "Stephan Kulow"), + ki18nc("@info:credit", "Solaris support and history"), + "coolo@kde.org"); + aboutData.addCredit(ki18nc("@info:credit", "Alexander Neundorf"), + ki18nc("@info:credit", "Bug fixes and improved startup performance"), + "neundorf@kde.org"); + aboutData.addCredit(ki18nc("@info:credit", "Peter Silva"), + ki18nc("@info:credit", "Marking improvements"), + "Peter.A.Silva@gmail.com"); + aboutData.addCredit(ki18nc("@info:credit", "Lotzi Boloni"), + ki18nc("@info:credit", "Embedded Konsole\n" + "Toolbar and session names"), + "boloni@cs.purdue.edu"); + aboutData.addCredit(ki18nc("@info:credit", "David Faure"), + ki18nc("@info:credit", "Embedded Konsole\n" + "General improvements"), + "faure@kde.org"); + aboutData.addCredit(ki18nc("@info:credit", "Antonio Larrosa"), + ki18nc("@info:credit", "Visual effects"), + "larrosa@kde.org"); + aboutData.addCredit(ki18nc("@info:credit", "Matthias Ettrich"), + ki18nc("@info:credit", "Code from the kvt project\n" + "General improvements"), + "ettrich@kde.org"); + aboutData.addCredit(ki18nc("@info:credit", "Warwick Allison"), + ki18nc("@info:credit", "Schema and text selection improvements"), + "warwick@troll.no"); + aboutData.addCredit(ki18nc("@info:credit", "Dan Pilone"), + ki18nc("@info:credit", "SGI port"), + "pilone@slac.com"); + aboutData.addCredit(ki18nc("@info:credit", "Kevin Street"), + ki18nc("@info:credit", "FreeBSD port"), + "street@iname.com"); + aboutData.addCredit(ki18nc("@info:credit", "Sven Fischer"), + ki18nc("@info:credit", "Bug fixes"), + "herpes@kawo2.renditionwth-aachen.de"); + aboutData.addCredit(ki18nc("@info:credit", "Dale M. Flaven"), + ki18nc("@info:credit", "Bug fixes"), + "dflaven@netport.com"); + aboutData.addCredit(ki18nc("@info:credit", "Martin Jones"), + ki18nc("@info:credit", "Bug fixes"), + "mjones@powerup.com.au"); + aboutData.addCredit(ki18nc("@info:credit", "Lars Knoll"), + ki18nc("@info:credit", "Bug fixes"), + "knoll@mpi-hd.mpg.de"); + aboutData.addCredit(ki18nc("@info:credit", "Thanks to many others.\n")); +} + +void restoreSession(Application& app) +{ + if (app.isSessionRestored()) { + int n = 1; + while (KMainWindow::canBeRestored(n)) + app.newMainWindow()->restore(n++); + } +} + diff --git a/konsole/src/settings/GeneralSettings.cpp b/konsole/src/settings/GeneralSettings.cpp new file mode 100644 index 00000000..160a87a9 --- /dev/null +++ b/konsole/src/settings/GeneralSettings.cpp @@ -0,0 +1,45 @@ +/* + Copyright 2011 Kurt Hindenburg + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License or (at your option) version 3 or any later version + accepted by the membership of KDE e.V. (or its successor appro- + ved by the membership of KDE e.V.), which shall act as a proxy + defined in Section 14 of version 3 of the license. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see http://www.gnu.org/licenses/. +*/ + +// Own +#include "GeneralSettings.h" + +#include + +using namespace Konsole; + +GeneralSettings::GeneralSettings(QWidget* aParent) : QWidget(aParent) +{ + setupUi(this); + + connect(enableAllMessagesButton, SIGNAL(clicked()), this, SLOT(slotEnableAllMessages())); + + this->kcfg_ShowTerminalSizeHint->setVisible(false); +} + +GeneralSettings::~GeneralSettings() +{ +} + +void GeneralSettings::slotEnableAllMessages() +{ + KMessageBox::enableAllMessages(); +} + diff --git a/konsole/src/settings/GeneralSettings.h b/konsole/src/settings/GeneralSettings.h new file mode 100644 index 00000000..7fefa46a --- /dev/null +++ b/konsole/src/settings/GeneralSettings.h @@ -0,0 +1,43 @@ +/* + Copyright 2011 Kurt Hindenburg + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License or (at your option) version 3 or any later version + accepted by the membership of KDE e.V. (or its successor appro- + ved by the membership of KDE e.V.), which shall act as a proxy + defined in Section 14 of version 3 of the license. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see http://www.gnu.org/licenses/. +*/ + +#ifndef GENERALSETTINGS_H +#define GENERALSETTINGS_H + +#include "ui_GeneralSettings.h" + +namespace Konsole +{ + +class GeneralSettings : public QWidget, private Ui::GeneralSettings +{ + Q_OBJECT + +public: + explicit GeneralSettings(QWidget* aParent = 0); + ~GeneralSettings(); + +public slots: + void slotEnableAllMessages(); +}; + +} + +#endif diff --git a/konsole/src/settings/GeneralSettings.ui b/konsole/src/settings/GeneralSettings.ui new file mode 100644 index 00000000..ecfec94e --- /dev/null +++ b/konsole/src/settings/GeneralSettings.ui @@ -0,0 +1,191 @@ + + + GeneralSettings + + + + 0 + 0 + 528 + 448 + + + + + 1 + 0 + + + + + + + QLayout::SetNoConstraint + + + + + Konsole Window + + + + + + + 0 + 0 + + + + Show menubar by default + + + + + + + + 0 + 0 + + + + Show hint for terminal size after resizing + + + + + + + + 0 + 0 + + + + If enabled, profile settings will be ignored + + + Use current window size on next startup + + + + + + + + 0 + 0 + + + + Enable menu accelerators + + + + + + + + 0 + 0 + + + + Show window title on the titlebar + + + + + + + + 0 + 0 + + + + Show application name on the titlebar + + + + + + + + + + + + + 0 + 0 + + + + Notifications + + + + + + Qt::Horizontal + + + + 117 + 20 + + + + + + + + + 0 + 0 + + + + All dialogs will be shown again + + + Enable all "Don't Ask Again" messages + + + + + + + Qt::Horizontal + + + + 116 + 20 + + + + + + + + + + + Qt::Vertical + + + + 20 + 60 + + + + + + + + + diff --git a/konsole/src/settings/KonsoleSettings.kcfgc b/konsole/src/settings/KonsoleSettings.kcfgc new file mode 100644 index 00000000..380e7c41 --- /dev/null +++ b/konsole/src/settings/KonsoleSettings.kcfgc @@ -0,0 +1,9 @@ +File=konsole.kcfg +NameSpace=Konsole +ClassName=KonsoleSettings +Singleton=true +MemberVariables=private +ItemAccessors=true +Mutators=true +GlobalEnums=true +SetUserTexts=true diff --git a/konsole/src/settings/TabBarSettings.cpp b/konsole/src/settings/TabBarSettings.cpp new file mode 100644 index 00000000..027a3735 --- /dev/null +++ b/konsole/src/settings/TabBarSettings.cpp @@ -0,0 +1,34 @@ +/* + Copyright 2011 Kurt Hindenburg + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License or (at your option) version 3 or any later version + accepted by the membership of KDE e.V. (or its successor appro- + ved by the membership of KDE e.V.), which shall act as a proxy + defined in Section 14 of version 3 of the license. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see http://www.gnu.org/licenses/. +*/ + +// Own +#include "TabBarSettings.h" + +using namespace Konsole; + +TabBarSettings::TabBarSettings(QWidget* aParent) : QWidget(aParent) +{ + setupUi(this); +} + +TabBarSettings::~TabBarSettings() +{ +} + diff --git a/konsole/src/settings/TabBarSettings.h b/konsole/src/settings/TabBarSettings.h new file mode 100644 index 00000000..3b469bef --- /dev/null +++ b/konsole/src/settings/TabBarSettings.h @@ -0,0 +1,41 @@ +/* + Copyright 2011 Kurt Hindenburg + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License or (at your option) version 3 or any later version + accepted by the membership of KDE e.V. (or its successor appro- + ved by the membership of KDE e.V.), which shall act as a proxy + defined in Section 14 of version 3 of the license. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see http://www.gnu.org/licenses/. +*/ + +#ifndef TABBARSETTINGS_H +#define TABBARSETTINGS_H + +#include "ui_TabBarSettings.h" + +namespace Konsole +{ + +class TabBarSettings : public QWidget, private Ui::TabBarSettings +{ + Q_OBJECT + +public: + explicit TabBarSettings(QWidget* parent = 0); + ~TabBarSettings(); + +}; + +} + +#endif diff --git a/konsole/src/settings/TabBarSettings.ui b/konsole/src/settings/TabBarSettings.ui new file mode 100644 index 00000000..cbe68046 --- /dev/null +++ b/konsole/src/settings/TabBarSettings.ui @@ -0,0 +1,183 @@ + + + TabBarSettings + + + + 0 + 0 + 503 + 375 + + + + + + + Appearance + + + + + + Tab bar visibility: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 1 + 0 + + + + + Always Show Tab Bar + + + + + Show Tab Bar When Needed + + + + + Always Hide Tab Bar + + + + + + + + Tab bar position: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 1 + 0 + + + + + Above Terminal Area + + + + + Below Terminal Area + + + + + + + + Show 'New Tab' and 'Close Tab' buttons + + + + + + + Use user-defined stylesheet + + + + + + + + 2 + 0 + + + + text/css + + + + + + + + + + Behavior + + + + + + New tab behavior: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 1 + 0 + + + + + Put New Tab At The End + + + + + Put New Tab After Current Tab + + + + + + + + + + + Qt::Vertical + + + + 20 + 4 + + + + + + + + + KComboBox + QComboBox +
    kcombobox.h
    +
    + + KUrlRequester + QFrame +
    kurlrequester.h
    +
    +
    + + +
    diff --git a/konsole/src/settings/konsole.kcfg b/konsole/src/settings/konsole.kcfg new file mode 100644 index 00000000..1ba0e239 --- /dev/null +++ b/konsole/src/settings/konsole.kcfg @@ -0,0 +1,92 @@ + + + + + + + Show menubar by default in each Konsole window + true + + + + Show window title set by escape sequence on the titlebar + false + + + + Show "- Konsole" on the titlebar + true + + + + Allow users to access top menu through Alt+Key combination + false + + + + Show terminal size in columns and lines in the center of window after resizing + true + + + + The window size will be saved upon exiting Konsole + true + + + + + + + + + + + AlwaysShowTabBar + + + + + + + + Bottom + + + + QTabBar::tab { min-width: 2em ; max-width: 25em } + + + + false + + + + + + + + false + + + + + + + + PutNewTabAtTheEnd + + + + + + true + + + + true + + + diff --git a/konsole/src/tests/CMakeLists.txt b/konsole/src/tests/CMakeLists.txt new file mode 100644 index 00000000..95f5e152 --- /dev/null +++ b/konsole/src/tests/CMakeLists.txt @@ -0,0 +1,75 @@ +include_directories( + ${KDE4_KPTY_INCLUDES} + ${KDE4_KPARTS_INCLUDES} + ${QT_QTTEST_INCLUDES} + # for konsoleprivate_export.h + ${CMAKE_CURRENT_BINARY_DIR}/.. +) + +set(KONSOLE_TEST_LIBS + ${QT_QTTEST_LIBRARY} + konsoleprivate + ${KDE4_KPARTS_LIBS} + ${KDE4_KPTY_LIBS} +) + +kde4_add_manual_test(PartManualTest PartManualTest.cpp) +target_link_libraries(PartManualTest + ${KONSOLE_TEST_LIBS} +) + +kde4_add_test(konsole-CharacterColorTest CharacterColorTest.cpp) +target_link_libraries(konsole-CharacterColorTest ${KONSOLE_TEST_LIBS}) + +kde4_add_test(konsole-CharacterWidthTest CharacterWidthTest.cpp) +target_link_libraries(konsole-CharacterWidthTest ${KONSOLE_TEST_LIBS}) + +kde4_add_test(konsole-DBusTest DBusTest.cpp) +target_link_libraries(konsole-DBusTest ${KONSOLE_TEST_LIBS}) + +set(HistoryTest_SRCS + HistoryTest.cpp + ../History.cpp +) +kde4_add_test(konsole-HistoryTest ${HistoryTest_SRCS}) +set_target_properties(konsole-HistoryTest PROPERTIES + COMPILE_FLAGS -DKONSOLEPRIVATE_EXPORT= +) +target_link_libraries(konsole-HistoryTest ${KONSOLE_TEST_LIBS}) + +set(KeyboardTranslatorTest_SRCS + KeyboardTranslatorTest.cpp + ../KeyboardTranslator.cpp +) +kde4_add_test(konsole-KeyboardTranslatorTest ${KeyboardTranslatorTest_SRCS}) +set_target_properties(konsole-KeyboardTranslatorTest PROPERTIES + COMPILE_FLAGS -DKONSOLEPRIVATE_EXPORT= +) +target_link_libraries(konsole-KeyboardTranslatorTest ${KONSOLE_TEST_LIBS}) + +kde4_add_test(konsole-PartTest PartTest.cpp) +target_link_libraries(konsole-PartTest ${KONSOLE_TEST_LIBS}) + +kde4_add_test(konsole-ProfileTest ProfileTest.cpp) +target_link_libraries(konsole-ProfileTest ${KONSOLE_TEST_LIBS}) + +kde4_add_test(konsole-PtyTest PtyTest.cpp) +target_link_libraries(konsole-PtyTest ${KONSOLE_TEST_LIBS}) + +kde4_add_test(konsole-SessionTest SessionTest.cpp) +target_link_libraries(konsole-SessionTest ${KONSOLE_TEST_LIBS}) + +##kde4_add_test(konsole-SessionManagerTest SessionManagerTest.cpp) +##target_link_libraries(konsole-SessionManagerTest ${KONSOLE_TEST_LIBS}) + +kde4_add_test(konsole-ShellCommandTest ShellCommandTest.cpp) +target_link_libraries(konsole-ShellCommandTest ${KONSOLE_TEST_LIBS}) + +kde4_add_test(konsole-TerminalCharacterDecoderTest TerminalCharacterDecoderTest.cpp) +target_link_libraries(konsole-TerminalCharacterDecoderTest ${KONSOLE_TEST_LIBS}) + +kde4_add_test(konsole-TerminalTest TerminalTest.cpp) +target_link_libraries(konsole-TerminalTest ${KONSOLE_TEST_LIBS}) + +kde4_add_test(konsole-TerminalInterfaceTest TerminalInterfaceTest.cpp) +target_link_libraries(konsole-TerminalInterfaceTest ${KONSOLE_TEST_LIBS}) diff --git a/konsole/src/tests/CharacterColorTest.cpp b/konsole/src/tests/CharacterColorTest.cpp new file mode 100644 index 00000000..88035887 --- /dev/null +++ b/konsole/src/tests/CharacterColorTest.cpp @@ -0,0 +1,119 @@ +/* + Copyright 2008 by Robert Knight + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +// Own +#include "CharacterColorTest.h" + +// Qt +#include +#include + +// KDE +#include + +using namespace Konsole; + +const ColorEntry CharacterColorTest::DefaultColorTable[TABLE_COLORS] = { + ColorEntry(QColor(0x00, 0x00, 0x00)), // Dfore + ColorEntry(QColor(0xFF, 0xFF, 0xFF)), // Dback + ColorEntry(QColor(0x00, 0x00, 0x00)), // Black + ColorEntry(QColor(0xB2, 0x18, 0x18)), // Red + ColorEntry(QColor(0x18, 0xB2, 0x18)), // Green + ColorEntry(QColor(0xB2, 0x68, 0x18)), // Yellow + ColorEntry(QColor(0x18, 0x18, 0xB2)), // Blue + ColorEntry(QColor(0xB2, 0x18, 0xB2)), // Magenta + ColorEntry(QColor(0x18, 0xB2, 0xB2)), // Cyan + ColorEntry(QColor(0xB2, 0xB2, 0xB2)), // White + // intensive versions + ColorEntry(QColor(0x00, 0x00, 0x00)), + ColorEntry(QColor(0xFF, 0xFF, 0xFF)), + ColorEntry(QColor(0x68, 0x68, 0x68)), + ColorEntry(QColor(0xFF, 0x54, 0x54)), + ColorEntry(QColor(0x54, 0xFF, 0x54)), + ColorEntry(QColor(0xFF, 0xFF, 0x54)), + ColorEntry(QColor(0x54, 0x54, 0xFF)), + ColorEntry(QColor(0xFF, 0x54, 0xFF)), + ColorEntry(QColor(0x54, 0xFF, 0xFF)), + ColorEntry(QColor(0xFF, 0xFF, 0xFF)) +}; + +void CharacterColorTest::init() +{ +} + +void CharacterColorTest::cleanup() +{ +} + +void CharacterColorTest::testDummyConstructor() +{ + CharacterColor charColor; + QCOMPARE(charColor.isValid(), false); +} + +void CharacterColorTest::testColorSpaceDefault_data() +{ + QTest::addColumn("colorValue"); + QTest::addColumn("expected"); + + QTest::newRow("color 0") << 0 << DefaultColorTable[0].color; + QTest::newRow("color 1") << 1 << DefaultColorTable[1].color; +} + +void CharacterColorTest::testColorSpaceDefault() +{ + QFETCH(int, colorValue); + QFETCH(QColor, expected); + + CharacterColor charColor(COLOR_SPACE_DEFAULT, colorValue); + const QColor result = charColor.color(DefaultColorTable); + + QCOMPARE(result, expected); +} + +void CharacterColorTest::testColorSpaceSystem_data() +{ + QTest::addColumn("colorValue"); + QTest::addColumn("expected"); + + QTest::newRow("color 0") << 0 << DefaultColorTable[2 + 0].color; + QTest::newRow("color 1") << 1 << DefaultColorTable[2 + 1].color; + QTest::newRow("color 7") << 7 << DefaultColorTable[2 + 7].color; +} + +void CharacterColorTest::testColorSpaceSystem() +{ + QFETCH(int, colorValue); + QFETCH(QColor, expected); + + CharacterColor charColor(COLOR_SPACE_SYSTEM, colorValue); + const QColor result = charColor.color(DefaultColorTable); + + QCOMPARE(result, expected); + + //CharacterColor charColor(COLOR_SPACE_SYSTEM, 5); + //const QColor result = charColor.color(DefaultColorTable); + //const QColor expected = DefaultColorTable[7].color; + //QCOMPARE(result, expected); +} + +QTEST_KDEMAIN_CORE(CharacterColorTest) + +#include "moc_CharacterColorTest.cpp" + diff --git a/konsole/src/tests/CharacterColorTest.h b/konsole/src/tests/CharacterColorTest.h new file mode 100644 index 00000000..bc2d13e6 --- /dev/null +++ b/konsole/src/tests/CharacterColorTest.h @@ -0,0 +1,49 @@ +/* + Copyright 2008 by Robert Knight + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +#ifndef CHARACTERCOLORTEST_H +#define CHARACTERCOLORTEST_H + +#include "../CharacterColor.h" + +namespace Konsole +{ + +class CharacterColorTest : public QObject +{ + Q_OBJECT + +private slots: + void init(); + void cleanup(); + + void testDummyConstructor(); + void testColorSpaceDefault_data(); + void testColorSpaceDefault(); + void testColorSpaceSystem_data(); + void testColorSpaceSystem(); + +private: + static const ColorEntry DefaultColorTable[]; +}; + +} + +#endif // CHARACTERCOLORTEST_H + diff --git a/konsole/src/tests/CharacterWidthTest.cpp b/konsole/src/tests/CharacterWidthTest.cpp new file mode 100644 index 00000000..f81e2e58 --- /dev/null +++ b/konsole/src/tests/CharacterWidthTest.cpp @@ -0,0 +1,73 @@ +/* + Copyright 2014 by Kurt Hindenburg + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +// Own +#include "CharacterWidthTest.h" + +// KDE +#include + +#include "../konsole_wcwidth.h" +#include "../konsoleprivate_export.h" + +using namespace Konsole; + +void CharacterWidthTest::testWidth_data() +{ + QTest::addColumn("character"); + QTest::addColumn("width"); + + /* This is a work in progress.... */ + + /* konsole_wcwidth uses -1 C0/C1 and DEL */ + QTest::newRow("0xE007F") << quint16(0xE007F) << -1; + + QTest::newRow("0x0300") << quint16(0x0300) << 0; + QTest::newRow("0x070F") << quint16(0x070F) << 0; + QTest::newRow("0x1160") << quint16(0x1160) << 0; + QTest::newRow("0x10300") << quint16(0x10300) << 0; + + QTest::newRow("a") << quint16('a') << 1; + QTest::newRow("0x00AD") << quint16(0x00AD) << 1; + QTest::newRow("0x00A0") << quint16(0x00A0) << 1; + QTest::newRow("0x10FB") << quint16(0x10FB) << 1; + QTest::newRow("0xFF76") << quint16(0xFF76) << 1; + QTest::newRow("0x103A0") << quint16(0x103A0) << 1; + QTest::newRow("0x10A06") << quint16(0x10A06) << 1; + + QTest::newRow("0x3000") << quint16(0x3000) << 2; + QTest::newRow("0x300a") << quint16(0x300a) << 2; + QTest::newRow("0x300b") << quint16(0x300b) << 2; + QTest::newRow("0xFF01") << quint16(0xFF01) << 2; + QTest::newRow("0xFF5F") << quint16(0xFF5F) << 2; + QTest::newRow("0xFF60") << quint16(0xFF60) << 2; + QTest::newRow("0xFFe0") << quint16(0xFFe6) << 2; +} + +void CharacterWidthTest::testWidth() +{ + QFETCH(quint16, character); + + QTEST(konsole_wcwidth(character), "width"); +} + +QTEST_KDEMAIN_CORE(CharacterWidthTest) + +#include "moc_CharacterWidthTest.cpp" + diff --git a/konsole/src/tests/CharacterWidthTest.h b/konsole/src/tests/CharacterWidthTest.h new file mode 100644 index 00000000..bcb3ffe9 --- /dev/null +++ b/konsole/src/tests/CharacterWidthTest.h @@ -0,0 +1,42 @@ +/* + Copyright 2014 by Kurt Hindenburg + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +#ifndef CHARACTERWIDTHTEST_H +#define CHARACTERWIDTHTEST_H + +#include + +namespace Konsole +{ + +class CharacterWidthTest : public QObject +{ + Q_OBJECT + +private slots: + + void testWidth_data(); + void testWidth(); + +}; + +} + +#endif // CHARACTERWIDTHTEST_H + diff --git a/konsole/src/tests/DBusTest.cpp b/konsole/src/tests/DBusTest.cpp new file mode 100644 index 00000000..f2acd16c --- /dev/null +++ b/konsole/src/tests/DBusTest.cpp @@ -0,0 +1,237 @@ +/* + Copyright 2010 by Kurt Hindenburg + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +// Own +#include "DBusTest.h" +#include "../Session.h" + +#include + +using namespace Konsole; + +/* Exec a new Konsole and grab its dbus */ +void DBusTest::initTestCase() +{ + const QString interfaceName = "org.kde.konsole"; + QDBusConnectionInterface* bus = 0; + QStringList konsoleServices; + + if (!QDBusConnection::sessionBus().isConnected() || + !(bus = QDBusConnection::sessionBus().interface())) + kFatal() << "Session bus not found"; + + QDBusReply serviceReply = bus->registeredServiceNames(); + if (!serviceReply.isValid()) + kFatal() << "SessionBus interfaces not available"; + + // Find all current Konsoles' services running + QStringList allServices = serviceReply; + for (QStringList::const_iterator it = allServices.constBegin(), + end = allServices.constEnd(); it != end; ++it) { + const QString service = *it; + if (service.startsWith(interfaceName)) + konsoleServices << service; + } + + // Create a new Konsole with a separate process id + QProcess proc; + proc.start("konsole"); + proc.waitForStarted(); + if (proc.exitCode() != 0) { + kFatal() << "Unable to exec a new Konsole : " << proc.exitCode(); + } + + // Wait for above Konsole to finish starting + QTest::qWait(5000); + + serviceReply = bus->registeredServiceNames(); + if (!serviceReply.isValid()) + kFatal() << "SessionBus interfaces not available"; + + // Find dbus service of above Konsole + allServices = serviceReply; + for (QStringList::const_iterator it = allServices.constBegin(), + end = allServices.constEnd(); it != end; ++it) { + const QString service = *it; + if (service.startsWith(interfaceName)) + if (!konsoleServices.contains(service)) + _interfaceName = service; + } + + if (_interfaceName.isEmpty()) { + kFatal() << "This test will only work in a Konsole window with a new PID. A new Konsole PID can't be found."; + } + //kDebug()<< "Using service: " + _interfaceName.toLatin1(); + + QDBusInterface iface(_interfaceName, + QLatin1String("/Konsole"), + QLatin1String("org.kde.konsole.Konsole")); + QVERIFY(iface.isValid()); +} + +/* Close the Konsole window that was opened to test the DBus interface + */ +void DBusTest::cleanupTestCase() +{ + // Need to take care of when user has CloseAllTabs=False otherwise + // they will get a popup dialog when we try to close this. + + QDBusInterface iface(_interfaceName, + QLatin1String("/konsole/MainWindow_1"), + QLatin1String("org.qtproject.Qt.QWidget")); + if (!iface.isValid()) + kFatal() << "Unable to get a dbus interface to Konsole!"; + + QDBusReply instanceReply = iface.call("close"); + if (!instanceReply.isValid()) + kFatal() << "Unable to close Konsole :" << instanceReply.error(); +} + +void DBusTest::testSessions() +{ + QDBusReply voidReply; + QDBusReply boolReply; + QDBusReply arrayReply; + QDBusReply stringReply; + QDBusReply listReply; + + QDBusInterface iface(_interfaceName, + QLatin1String("/Sessions/1"), + QLatin1String("org.kde.konsole.Session")); + QVERIFY(iface.isValid()); + + //****************** Test is/set MonitorActivity + voidReply = iface.call("setMonitorActivity", false); + QVERIFY(voidReply.isValid()); + + boolReply = iface.call("isMonitorActivity"); + QVERIFY(boolReply.isValid()); + QCOMPARE(boolReply.value(), false); + + voidReply = iface.call("setMonitorActivity", true); + QVERIFY(voidReply.isValid()); + + boolReply = iface.call("isMonitorActivity"); + QVERIFY(boolReply.isValid()); + QCOMPARE(boolReply.value(), true); + + //****************** Test is/set MonitorSilence + voidReply = iface.call("setMonitorSilence", false); + QVERIFY(voidReply.isValid()); + + boolReply = iface.call("isMonitorSilence"); + QVERIFY(boolReply.isValid()); + QCOMPARE(boolReply.value(), false); + + voidReply = iface.call("setMonitorSilence", true); + QVERIFY(voidReply.isValid()); + + boolReply = iface.call("isMonitorSilence"); + QVERIFY(boolReply.isValid()); + QCOMPARE(boolReply.value(), true); + + //****************** Test codec and setCodec + arrayReply = iface.call("codec"); + QVERIFY(arrayReply.isValid()); + + // Obtain a list of system's Codecs + QList availableCodecs = QTextCodec::availableCodecs(); + for (int i = 0; i < availableCodecs.count(); ++i) { + boolReply = iface.call("setCodec", availableCodecs[i]); + QVERIFY(boolReply.isValid()); + QCOMPARE(boolReply.value(), true); + + arrayReply = iface.call("codec"); + QVERIFY(arrayReply.isValid()); + // Compare result with name due to aliases issue + // Better way to do this? + QCOMPARE((QTextCodec::codecForName(arrayReply.value()))->name(), + (QTextCodec::codecForName(availableCodecs[i]))->name()); + } + + //****************** Test is/set flowControlEnabled + voidReply = iface.call("setFlowControlEnabled", true); + QVERIFY(voidReply.isValid()); + + boolReply = iface.call("flowControlEnabled"); + QVERIFY(boolReply.isValid()); + QCOMPARE(boolReply.value(), true); + + voidReply = iface.call("setFlowControlEnabled", false); + QVERIFY(voidReply.isValid()); + + boolReply = iface.call("flowControlEnabled"); + QVERIFY(boolReply.isValid()); + QCOMPARE(boolReply.value(), false); + + //****************** Test is/set environment + listReply = iface.call("environment"); + QVERIFY(listReply.isValid()); + + QStringList prevEnv = listReply.value(); + //for (int i = 0; i < prevEnv.size(); ++i) + // kDebug()<< prevEnv.at(i).toLocal8Bit().constData() << endl; + + voidReply = iface.call("setEnvironment", QStringList()); + QVERIFY(voidReply.isValid()); + + listReply = iface.call("environment"); + QVERIFY(listReply.isValid()); + QCOMPARE(listReply.value(), QStringList()); + + voidReply = iface.call("setEnvironment", prevEnv); + QVERIFY(voidReply.isValid()); + + listReply = iface.call("environment"); + QVERIFY(listReply.isValid()); + QCOMPARE(listReply.value(), prevEnv); + + //****************** Test is/set title + // TODO: Consider checking what is in Profile + stringReply = iface.call("title", Session::LocalTabTitle); + QVERIFY(stringReply.isValid()); + + //kDebug()<< stringReply.value(); + QString prevLocalTitle = stringReply.value(); + + // set title to, what title should be + QMap titleMap; + titleMap["Shell"] = "Shell"; + + // BUG: It appears that Session::LocalTabTitle is set to Shell and + // doesn't change. While RemoteTabTitle is actually the LocalTabTitle + // TODO: Figure out what's going on... + QMapIterator i(titleMap); + while (i.hasNext()) { + i.next(); + voidReply = iface.call("setTitle", Session::LocalTabTitle, i.key()); + QVERIFY(voidReply.isValid()); + + stringReply = iface.call("title", Session::LocalTabTitle); + QVERIFY(stringReply.isValid()); + + QCOMPARE(stringReply.value(), i.value()); + + } +} + +QTEST_MAIN(DBusTest) + +#include "moc_DBusTest.cpp" + diff --git a/konsole/src/tests/DBusTest.h b/konsole/src/tests/DBusTest.h new file mode 100644 index 00000000..ccc9b959 --- /dev/null +++ b/konsole/src/tests/DBusTest.h @@ -0,0 +1,55 @@ +/* + Copyright 2010 by Kurt Hindenburg + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +#ifndef DBUSTEST_H +#define DBUSTEST_H + +#include +#include +#include +#include +#include +#include + +#include + +namespace Konsole +{ + +class DBusTest : public QObject +{ + Q_OBJECT +public: + +private slots: + void initTestCase(); + void cleanupTestCase(); + void testSessions(); + +// protected slots are not treated as test cases +protected slots: + +private: + QString _interfaceName; +}; + +} + +#endif // DBUSTEST_H + diff --git a/konsole/src/tests/HistoryTest.cpp b/konsole/src/tests/HistoryTest.cpp new file mode 100644 index 00000000..467de2f1 --- /dev/null +++ b/konsole/src/tests/HistoryTest.cpp @@ -0,0 +1,143 @@ +/* + Copyright 2013 by Kurt Hindenburg + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +// Own +#include "HistoryTest.h" + +#include "qtest_kde.h" + +// Konsole +#include "../Session.h" +#include "../Emulation.h" +#include "../History.h" + +using namespace Konsole; + +void HistoryTest::testHistoryNone() +{ + HistoryType* history; + + history = new HistoryTypeNone(); + QCOMPARE(history->isEnabled(), false); + QCOMPARE(history->isUnlimited(), false); + QCOMPARE(history->maximumLineCount(), 0); + delete history; +} + +void HistoryTest::testHistoryFile() +{ + HistoryType* history; + + history = new HistoryTypeFile(); + QCOMPARE(history->isEnabled(), true); + QCOMPARE(history->isUnlimited(), true); + QCOMPARE(history->maximumLineCount(), -1); + delete history; +} + +void HistoryTest::testCompactHistory() +{ + HistoryType* history; + + history = new CompactHistoryType(42); + QCOMPARE(history->isEnabled(), true); + QCOMPARE(history->isUnlimited(), false); + QCOMPARE(history->maximumLineCount(), 42); + delete history; +} + +void HistoryTest::testEmulationHistory() +{ + Session* session = new Session(); + Emulation* emulation = session->emulation(); + + const HistoryType& historyTypeDefault = emulation->history(); + QCOMPARE(historyTypeDefault.isEnabled(), false); + QCOMPARE(historyTypeDefault.isUnlimited(), false); + QCOMPARE(historyTypeDefault.maximumLineCount(), 0); + + emulation->setHistory(HistoryTypeNone()); + const HistoryType& historyTypeNone = emulation->history(); + QCOMPARE(historyTypeNone.isEnabled(), false); + QCOMPARE(historyTypeNone.isUnlimited(), false); + QCOMPARE(historyTypeNone.maximumLineCount(), 0); + + emulation->setHistory(HistoryTypeFile()); + const HistoryType& historyTypeFile = emulation->history(); + QCOMPARE(historyTypeFile.isEnabled(), true); + QCOMPARE(historyTypeFile.isUnlimited(), true); + QCOMPARE(historyTypeFile.maximumLineCount(), -1); + + emulation->setHistory(CompactHistoryType(42)); + const HistoryType& compactHistoryType = emulation->history(); + QCOMPARE(compactHistoryType.isEnabled(), true); + QCOMPARE(compactHistoryType.isUnlimited(), false); + QCOMPARE(compactHistoryType.maximumLineCount(), 42); + + + delete session; +} + +void HistoryTest::testHistoryScroll() +{ + HistoryScroll* historyScroll; + + // None + historyScroll = new HistoryScrollNone(); + QVERIFY(!historyScroll->hasScroll()); + QCOMPARE(historyScroll->getLines(), 0); + QCOMPARE(historyScroll->getLineLen(0), 0); + QCOMPARE(historyScroll->getLineLen(10), 0); + + const HistoryType& historyTypeNone = historyScroll->getType(); + QCOMPARE(historyTypeNone.isEnabled(), false); + QCOMPARE(historyTypeNone.isUnlimited(), false); + QCOMPARE(historyTypeNone.maximumLineCount(), 0); + + // File + historyScroll = new HistoryScrollFile(QString("test.log")); + QVERIFY(historyScroll->hasScroll()); + QCOMPARE(historyScroll->getLines(), 0); + QCOMPARE(historyScroll->getLineLen(0), 0); + QCOMPARE(historyScroll->getLineLen(10), 0); + + const HistoryType& historyTypeFile = historyScroll->getType(); + QCOMPARE(historyTypeFile.isEnabled(), true); + QCOMPARE(historyTypeFile.isUnlimited(), true); + QCOMPARE(historyTypeFile.maximumLineCount(), -1); + + // Compact + historyScroll = new CompactHistoryScroll(42); + QVERIFY(historyScroll->hasScroll()); + QCOMPARE(historyScroll->getLines(), 0); + QCOMPARE(historyScroll->getLineLen(0), 0); + QCOMPARE(historyScroll->getLineLen(10), 0); + + const HistoryType& compactHistoryType = historyScroll->getType(); + QCOMPARE(compactHistoryType.isEnabled(), true); + QCOMPARE(compactHistoryType.isUnlimited(), false); + QCOMPARE(compactHistoryType.maximumLineCount(), 42); + + delete historyScroll; +} + +QTEST_KDEMAIN(HistoryTest , GUI) + +#include "moc_HistoryTest.cpp" + diff --git a/konsole/src/tests/HistoryTest.h b/konsole/src/tests/HistoryTest.h new file mode 100644 index 00000000..30441891 --- /dev/null +++ b/konsole/src/tests/HistoryTest.h @@ -0,0 +1,45 @@ +/* + Copyright 2013 by Kurt Hindenburg + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +#ifndef HISTORYTEST_H +#define HISTORYTEST_H + +#include + +namespace Konsole +{ + +class HistoryTest : public QObject +{ + Q_OBJECT + +private slots: + void testHistoryNone(); + void testHistoryFile(); + void testCompactHistory(); + void testEmulationHistory(); + void testHistoryScroll(); + +private: +}; + +} + +#endif // HISTORYTEST_H + diff --git a/konsole/src/tests/KeyboardTranslatorTest.cpp b/konsole/src/tests/KeyboardTranslatorTest.cpp new file mode 100644 index 00000000..5c7c3327 --- /dev/null +++ b/konsole/src/tests/KeyboardTranslatorTest.cpp @@ -0,0 +1,101 @@ +/* + Copyright 2013 by Kurt Hindenburg + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +// Own +#include "KeyboardTranslatorTest.h" + +// KDE +#include + +using namespace Konsole; + +void KeyboardTranslatorTest::testEntryTextWildcards_data() +{ + // Shift = 1 + (1 << 0) = 2 + // Alt = 1 + (1 << 2) = 3 + // Control = 1 + (1 << 4) = 5 + + QTest::addColumn("text"); + QTest::addColumn("result"); + QTest::addColumn("wildcards"); + QTest::addColumn("modifiers"); // Qt::KeyboardModifers doesn't work here + + QTest::newRow("Home no wildcards no modifiers")<< QByteArray("Home") << QByteArray("Home") << false << static_cast(Qt::NoModifier); + QTest::newRow("Home no wildcards Shift modifiers")<< QByteArray("Home") << QByteArray("Home") << false << static_cast(Qt::ShiftModifier); + QTest::newRow("Home no wildcards Alt modifiers")<< QByteArray("Home") << QByteArray("Home") << false << static_cast(Qt::AltModifier); + QTest::newRow("Home no wildcards Control modifiers")<< QByteArray("Home") << QByteArray("Home") << false << static_cast(Qt::ControlModifier); + + QTest::newRow("Home yes wildcards no modifiers")<< QByteArray("Home") << QByteArray("Home") << true << static_cast(Qt::NoModifier); + QTest::newRow("Home yes wildcards Shift modifiers")<< QByteArray("Home") << QByteArray("Home") << true << static_cast(Qt::ShiftModifier); + QTest::newRow("Home yes wildcards Alt modifiers")<< QByteArray("Home") << QByteArray("Home") << true << static_cast(Qt::AltModifier); + QTest::newRow("Home yes wildcards Control modifiers")<< QByteArray("Home") << QByteArray("Home") << true << static_cast(Qt::ControlModifier); + + + // text, results: no mod, shift, alt, control + QList entry; + entry << QByteArray("E*") << QByteArray("E1") << QByteArray("E2") << QByteArray("E3") << QByteArray("E5"); + QTest::newRow("E* yes wildcards no modifiers")<< entry[0] << entry[1] << true << static_cast(Qt::NoModifier); + QTest::newRow("E* yes wildcards Shift modifiers")<< entry[0] << entry[2] << true << static_cast(Qt::ShiftModifier); + QTest::newRow("E* yes wildcards Alt modifiers")<< entry[0] << entry[3] << true << static_cast(Qt::AltModifier); + QTest::newRow("E* yes wildcards Control modifiers")<< entry[0] << entry[4] << true << static_cast(Qt::ControlModifier); + + // combinations + entry.clear();; + entry << QByteArray("E*") << QByteArray("E4") << QByteArray("E6") << QByteArray("E8") << QByteArray("E7"); + QTest::newRow("E* yes wildcards Shift+Alt modifiers")<< entry[0] << entry[1] << true << static_cast(Qt::ShiftModifier | Qt::AltModifier); + QTest::newRow("E* yes wildcards Shift+Control modifiers")<< entry[0] << entry[2] << true << static_cast(Qt::ShiftModifier | Qt::ControlModifier); + QTest::newRow("E* yes wildcards Shift+Alt+Control modifiers")<< entry[0] << entry[3] << true << static_cast(Qt::ShiftModifier | Qt::AltModifier | Qt::ControlModifier); + QTest::newRow("E* yes wildcards Alt+Control modifiers")<< entry[0] << entry[4] << true << static_cast(Qt::AltModifier | Qt::ControlModifier); + + // text, results: no mod, shift, alt, control + entry.clear();; + entry << QByteArray("\E[24;*~") << QByteArray("\E[24;1~") << QByteArray("\E[24;2~") << QByteArray("\E[24;3~") << QByteArray("\E[24;5~"); + QTest::newRow("\E[24;*~ yes wildcards no modifiers")<< entry[0] << entry[1] << true << static_cast(Qt::NoModifier); + QTest::newRow("\E[24;*~ yes wildcards Shift modifiers")<< entry[0] << entry[2] << true << static_cast(Qt::ShiftModifier); + QTest::newRow("\E[24;*~ yes wildcards Alt modifiers")<< entry[0] << entry[3] << true << static_cast(Qt::AltModifier); + QTest::newRow("\E[24;*~ yes wildcards Control modifiers")<< entry[0] << entry[4] << true << static_cast(Qt::ControlModifier); + + // combinations + entry.clear();; + entry << QByteArray("\E[24;*~") << QByteArray("\E[24;4~") << QByteArray("\E[24;6~") << QByteArray("\E[24;8~") << QByteArray("\E[24;7~"); + QTest::newRow("\E[24;*~ yes wildcards Shift+Alt modifiers")<< entry[0] << entry[1] << true << static_cast(Qt::ShiftModifier | Qt::AltModifier); + QTest::newRow("\E[24;*~ yes wildcards Shift+Control modifiers")<< entry[0] << entry[2] << true << static_cast(Qt::ShiftModifier | Qt::ControlModifier); + QTest::newRow("\E[24;*~ yes wildcards Shift+Alt+Control modifiers")<< entry[0] << entry[3] << true << static_cast(Qt::ShiftModifier | Qt::AltModifier | Qt::ControlModifier); + QTest::newRow("\E[24;*~ yes wildcards Alt+Control modifiers")<< entry[0] << entry[4] << true << static_cast(Qt::AltModifier | Qt::ControlModifier); + +} + +void KeyboardTranslatorTest::testEntryTextWildcards() +{ + QFETCH(QByteArray, text); + QFETCH(QByteArray, result); + QFETCH(bool, wildcards); + QFETCH(quint64, modifiers); + + KeyboardTranslator::Entry entry; + Qt::KeyboardModifiers keyboardModifiers = static_cast(modifiers); + entry.setText(text); + + QCOMPARE(entry.text(wildcards, keyboardModifiers), result); +} + +QTEST_KDEMAIN_CORE(KeyboardTranslatorTest) + +#include "moc_KeyboardTranslatorTest.cpp" + diff --git a/konsole/src/tests/KeyboardTranslatorTest.h b/konsole/src/tests/KeyboardTranslatorTest.h new file mode 100644 index 00000000..b29cf7a5 --- /dev/null +++ b/konsole/src/tests/KeyboardTranslatorTest.h @@ -0,0 +1,40 @@ +/* + Copyright 2013 by Kurt Hindenburg + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +#ifndef KEYBOARDTRANSLATORTEST_H +#define KEYBOARDTRANSLATORTEST_H + +#include "../KeyboardTranslator.h" + +namespace Konsole +{ + +class KeyboardTranslatorTest : public QObject +{ + Q_OBJECT + +private slots: + void testEntryTextWildcards(); + void testEntryTextWildcards_data(); +}; + +} + +#endif // KEYBOARDTRANSLATORTEST_H + diff --git a/konsole/src/tests/PartManualTest.cpp b/konsole/src/tests/PartManualTest.cpp new file mode 100644 index 00000000..a41d6072 --- /dev/null +++ b/konsole/src/tests/PartManualTest.cpp @@ -0,0 +1,140 @@ +/* + Copyright 2008 by Robert Knight + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +// Own +#include "PartManualTest.h" + +// Qt +#include +#include +#include +#include +#include +#include + +// System +#include +#include + +// KDE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Konsole +#include "../Pty.h" +#include "../Session.h" +#include "../KeyboardTranslator.h" + +using namespace Konsole; + +void PartManualTest::testShortcutOverride() +{ + // FIXME: This test asks the user to press shortcut key sequences manually because + // the result is different than when sending the key press via QTest::keyClick() + // + // When the key presses are sent manually, Konsole::TerminalDisplay::event() is called + // and the overrideShortcut() signal is emitted by the part. + // When the key presses are sent automatically, the shortcut is triggered but + // Konsole::TerminalDisplay::event() is not called and the overrideShortcut() signal is + // not emitted by the part. + + // Create a main window with a menu and a test + // action with a shortcut set to Ctrl+S, which is also used by the terminal + KMainWindow* mainWindow = new KMainWindow(); + QMenu* fileMenu = mainWindow->menuBar()->addMenu("File"); + QAction* testAction = fileMenu->addAction("Test"); + testAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_S)); + connect(testAction, SIGNAL(triggered()), this, SLOT(shortcutTriggered())); + + // Create terminal part and embed in into the main window + KParts::Part* terminalPart = createPart(); + QVERIFY(terminalPart); + mainWindow->setCentralWidget(terminalPart->widget()); + TerminalInterface* terminal = qobject_cast(terminalPart); + QVERIFY(terminal); + terminal->sendInput("Press Ctrl+S twice.\n"); + mainWindow->show(); + + // Test shortcut with override disabled, so the shortcut will be triggered + _shortcutTriggered = false; + _override = false; + _overrideCalled = false; + QVERIFY(connect(terminalPart, SIGNAL(overrideShortcut(QKeyEvent*,bool&)), + this, SLOT(overrideShortcut(QKeyEvent*,bool&)))); + + //QTest::keyClick(terminalPart->widget(),Qt::Key_S,Qt::ControlModifier); + _shortcutEventLoop = new QEventLoop(); + _shortcutEventLoop->exec(); + + QVERIFY(_overrideCalled); + QVERIFY(_shortcutTriggered); + QVERIFY(!_override); + + // Test shortcut with override enabled, so the shortcut will not be triggered + _override = true; + _overrideCalled = false; + _shortcutTriggered = false; + + //QTest::keyClick(terminalPart->widget(),Qt::Key_S,Qt::ControlModifier); + _shortcutEventLoop->exec(); + + QVERIFY(_overrideCalled); + QVERIFY(!_shortcutTriggered); + QVERIFY(_override); + + delete _shortcutEventLoop; + delete terminalPart; + delete mainWindow; +} +void PartManualTest::overrideShortcut(QKeyEvent* event, bool& override) +{ + QVERIFY(override == true); + if (event->modifiers() == Qt::ControlModifier && event->key() == Qt::Key_S) { + _overrideCalled = true; + override = _override; + _shortcutEventLoop->exit(); + } +} +void PartManualTest::shortcutTriggered() +{ + _shortcutTriggered = true; +} + +KParts::Part* PartManualTest::createPart() +{ + KPluginFactory* factory = KPluginLoader("konsolepart").factory(); + Q_ASSERT(factory); + + KParts::Part* terminalPart = factory->create(this); + + return terminalPart; +} + +QTEST_KDEMAIN(PartManualTest , GUI) + +#include "moc_PartManualTest.cpp" + diff --git a/konsole/src/tests/PartManualTest.h b/konsole/src/tests/PartManualTest.h new file mode 100644 index 00000000..d501b062 --- /dev/null +++ b/konsole/src/tests/PartManualTest.h @@ -0,0 +1,59 @@ +/* + Copyright 2008 by Robert Knight + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +#ifndef PARTMANUALTEST_H +#define PARTMANUALTEST_H + +#include + +#include +#include + +#include +#include + +namespace Konsole +{ + +class PartManualTest : public QObject +{ + Q_OBJECT + +private slots: + void testShortcutOverride(); + +// marked as protected so they are not treated as test cases +protected slots: + void overrideShortcut(QKeyEvent* event, bool& override); + void shortcutTriggered(); + +private: + KParts::Part* createPart(); + + // variables for testShortcutOverride() + bool _shortcutTriggered; + bool _overrideCalled; + bool _override; + QEventLoop* _shortcutEventLoop; +}; + +} + +#endif // PARTMANUALTEST_H + diff --git a/konsole/src/tests/PartTest.cpp b/konsole/src/tests/PartTest.cpp new file mode 100644 index 00000000..4516ee81 --- /dev/null +++ b/konsole/src/tests/PartTest.cpp @@ -0,0 +1,105 @@ +/* + Copyright 2008 by Robert Knight + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +// Own +#include "PartTest.h" + +// Qt +#include +#include + +// KDE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Konsole +#include "../Pty.h" + +using namespace Konsole; + +void PartTest::testFd() +{ + // find ping + QString pingExe = KStandardDirs::findExe("ping"); + if (pingExe.isEmpty()) { + QSKIP("ping command not found.", SkipSingle); + return; + } + + // create a Konsole part and attempt to connect to it + KParts::Part* terminalPart = createPart(); + if (!terminalPart) // not found + QSKIP("konsolepart not found.", SkipSingle); + + // start a pty process + KPtyProcess ptyProcess; + ptyProcess.setProgram(pingExe, QStringList() << "localhost"); + ptyProcess.setPtyChannels(KPtyProcess::AllChannels); + ptyProcess.start(); + QVERIFY(ptyProcess.waitForStarted()); + + int fd = ptyProcess.pty()->masterFd(); + + bool result = QMetaObject::invokeMethod(terminalPart, "openTeletype", + Qt::DirectConnection, Q_ARG(int, fd)); + QVERIFY(result); + + // suspend the KPtyDevice so that the embedded terminal gets a chance to + // read from the pty. Otherwise the KPtyDevice will simply read everything + // as soon as it becomes available and the terminal will not display any output + ptyProcess.pty()->setSuspended(true); + + QWeakPointer dialog = new KDialog(); + dialog.data()->setButtons(0); + QVBoxLayout* layout = new QVBoxLayout(dialog.data()->mainWidget()); + layout->addWidget(new QLabel("Output of 'ping localhost' should appear in a terminal below for 5 seconds")); + layout->addWidget(terminalPart->widget()); + QTimer::singleShot(5000, dialog.data(), SLOT(close())); + dialog.data()->exec(); + + delete terminalPart; + delete dialog.data(); + ptyProcess.kill(); + ptyProcess.waitForFinished(1000); +} + +KParts::Part* PartTest::createPart() +{ + KPluginFactory* factory = KPluginLoader("konsolepart").factory(); + if (!factory) // not found + return 0; + + KParts::Part* terminalPart = factory->create(this); + + return terminalPart; +} + +QTEST_KDEMAIN(PartTest , GUI) + +#include "moc_PartTest.cpp" + diff --git a/konsole/src/tests/PartTest.h b/konsole/src/tests/PartTest.h new file mode 100644 index 00000000..557a354e --- /dev/null +++ b/konsole/src/tests/PartTest.h @@ -0,0 +1,48 @@ +/* + Copyright 2008 by Robert Knight + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +#ifndef PARTTEST_H +#define PARTTEST_H + +#include + +#include +#include + +#include +#include + +namespace Konsole +{ + +class PartTest : public QObject +{ + Q_OBJECT + +private slots: + void testFd(); + +private: + KParts::Part* createPart(); +}; + +} + +#endif // PARTTEST_H + diff --git a/konsole/src/tests/ProfileTest.cpp b/konsole/src/tests/ProfileTest.cpp new file mode 100644 index 00000000..7ca8f68b --- /dev/null +++ b/konsole/src/tests/ProfileTest.cpp @@ -0,0 +1,231 @@ +/* + Copyright 2008 by Robert Knight + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +// Own +#include "ProfileTest.h" + +// KDE +#include + +// Konsole +#include "../Profile.h" +#include "../ProfileWriter.h" + +using namespace Konsole; + +void ProfileTest::testProfile() +{ + // create a new profile + Profile* parent = new Profile; + parent->setProperty(Profile::Name, "Parent"); + parent->setProperty(Profile::Path, "FakePath"); + + parent->setProperty(Profile::AntiAliasFonts, false); + parent->setProperty(Profile::StartInCurrentSessionDir, false); + + parent->setProperty(Profile::UseCustomCursorColor, true); + QVERIFY(parent->useCustomCursorColor()); + QCOMPARE(parent->customCursorColor(), QColor()); + parent->setProperty(Profile::UseCustomCursorColor, false); + QVERIFY(!parent->useCustomCursorColor()); + QCOMPARE(parent->customCursorColor(), QColor()); + + // create a child profile + Profile* child = new Profile(Profile::Ptr(parent)); + child->setProperty(Profile::StartInCurrentSessionDir, true); + + // check which properties are set + QVERIFY(parent->isPropertySet(Profile::Name)); + QVERIFY(parent->isPropertySet(Profile::Path)); + QVERIFY(parent->isPropertySet(Profile::AntiAliasFonts)); + QVERIFY(!parent->isPropertySet(Profile::Icon)); + QVERIFY(!parent->isPropertySet(Profile::Command)); + QVERIFY(!parent->isPropertySet(Profile::Arguments)); + + QVERIFY(child->isPropertySet(Profile::StartInCurrentSessionDir)); + QVERIFY(!child->isPropertySet(Profile::Name)); + QVERIFY(!child->isPropertySet(Profile::AntiAliasFonts)); + QVERIFY(!child->isPropertySet(Profile::ColorScheme)); + + // read non-inheritable properties + QCOMPARE(parent->property(Profile::Name), QString("Parent")); + QCOMPARE(child->property(Profile::Name), QVariant()); + QCOMPARE(parent->property(Profile::Path), QString("FakePath")); + QCOMPARE(child->property(Profile::Path), QVariant()); + + // read inheritable properties + QVERIFY(parent->property(Profile::AntiAliasFonts) == false); + QVERIFY(child->property(Profile::AntiAliasFonts) == false); + + QVERIFY(!parent->startInCurrentSessionDir()); + QVERIFY(child->startInCurrentSessionDir()); + + delete child; +} +void ProfileTest::testClone() +{ + // create source profile and parent + Profile::Ptr parent(new Profile); + parent->setProperty(Profile::Command, "ps"); + parent->setProperty(Profile::ColorScheme, "BlackOnWhite"); + + Profile::Ptr source(new Profile(parent)); + source->setProperty(Profile::AntiAliasFonts, false); + source->setProperty(Profile::HistorySize, 4567); + + source->setProperty(Profile::Name, "SourceProfile"); + source->setProperty(Profile::Path, "SourcePath"); + + // create target to clone source and parent + Profile::Ptr targetParent(new Profile); + // same value as source parent + targetParent->setProperty(Profile::Command, "ps"); + // different value from source parent + targetParent->setProperty(Profile::ColorScheme, "BlackOnGrey"); + Profile::Ptr target(new Profile(parent)); + + // clone source profile, setting only properties that differ + // between the source and target + target->clone(source, true); + + // check that properties from source have been cloned into target + QCOMPARE(source->property(Profile::AntiAliasFonts), + target->property(Profile::AntiAliasFonts)); + QCOMPARE(source->property(Profile::HistorySize), + target->property(Profile::HistorySize)); + + // check that Name and Path properties are handled specially and not cloned + QVERIFY(source->property(Profile::Name) != + target->property(Profile::Name)); + QVERIFY(source->property(Profile::Path) != + target->property(Profile::Path)); + + // check that Command property is not set in target because the values + // are the same + QVERIFY(!target->isPropertySet(Profile::Command)); + // check that ColorScheme property is cloned because the inherited values + // from the source parent and target parent differ + QCOMPARE(source->property(Profile::ColorScheme), + target->property(Profile::ColorScheme)); +} +void ProfileTest::testProfileGroup() +{ + // create three new profiles + Profile::Ptr profile[3]; + for (int i = 0; i < 3; i++) { + profile[i] = new Profile; + QVERIFY(!profile[i]->asGroup()); + } + + // set properties with different values + profile[0]->setProperty(Profile::UseCustomCursorColor, true); + profile[1]->setProperty(Profile::UseCustomCursorColor, false); + + // set properties with same values + for (int i = 0; i < 3; i++) + profile[i]->setProperty(Profile::HistorySize, 1234); + + // create a group profile + ProfileGroup::Ptr group = ProfileGroup::Ptr(new ProfileGroup); + const ProfileGroup::Ptr group_const = ProfileGroup::Ptr(new ProfileGroup); + QVERIFY(group->asGroup()); + QVERIFY(group_const->asGroup()); + for (int i = 0; i < 3; i++) { + group->addProfile(profile[i]); + QVERIFY(group->profiles().contains(profile[i])); + QVERIFY(!group_const->profiles().contains(profile[i])); + } + group->updateValues(); + + // read and check properties from the group + QCOMPARE(group->property(Profile::HistorySize), 1234); + QCOMPARE(group_const->property(Profile::HistorySize), 0); + QCOMPARE(group->property(Profile::UseCustomCursorColor), QVariant()); + QCOMPARE(group_const->property(Profile::UseCustomCursorColor), QVariant()); + + // set and test shareable properties in the group + group->setProperty(Profile::Command, "ssh"); + group->setProperty(Profile::AntiAliasFonts, false); + + QCOMPARE(profile[0]->property(Profile::Command), QString("ssh")); + QVERIFY(profile[1]->property(Profile::AntiAliasFonts) == false); + + // set and test non-shareable properties in the group + // (should have no effect) + group->setProperty(Profile::Name, "NewName"); + group->setProperty(Profile::Path, "NewPath"); + QVERIFY(profile[1]->property(Profile::Name) != "NewName"); + QVERIFY(profile[2]->property(Profile::Path) != "NewPath"); + + // remove a profile from the group + group->removeProfile(profile[0]); + QVERIFY(!group->profiles().contains(profile[0])); + group->updateValues(); + + // check that profile is no longer affected by group + group->setProperty(Profile::Command, "fish"); + QVERIFY(profile[0]->property(Profile::Command) != "fish"); +} + +// Verify the correct file name is created from the untranslatedname +void ProfileTest::testProfileFileNames() +{ + Profile::Ptr profile = Profile::Ptr(new Profile); + QFileInfo fileInfo; + ProfileWriter* writer = new KDE4ProfileWriter; + + profile->setProperty(Profile::UntranslatedName, "Indiana"); + fileInfo.setFile(writer->getPath(profile)); + QCOMPARE(fileInfo.fileName(), QString("Indiana.profile")); + + profile->setProperty(Profile::UntranslatedName, "Old Paris"); + fileInfo.setFile(writer->getPath(profile)); + QCOMPARE(fileInfo.fileName(), QString("Old Paris.profile")); + + /* FIXME: deal w/ file systems that are case-insensitive + This leads to confusion as both Test and test can appear in the Manager + Profile dialog while really there is only 1 test.profile file. + Suggestions: all lowercase, testing the file system, ... + */ + /* + profile->setProperty(Profile::UntranslatedName, "New Profile"); + fileInfo.setFile(writer->getPath(profile)); + QCOMPARE(fileInfo.fileName(), QString("new profile.profile")); + */ + + /* FIXME: don't allow certain characters in file names + Consider: ,^@=+{}[]~!?:&*\"|#%<>$\"'();`'/\ + Suggestions: changing them all to _, just remove them, ... + Bug 315086 comes from a user using / in the profile name - multiple + issues there. + */ + /* + profile->setProperty(Profile::UntranslatedName, "new/profile"); + fileInfo.setFile(writer->getPath(profile)); + QCOMPARE(fileInfo.fileName(), QString("new_profile.profile")); + */ + + delete writer; +} + +QTEST_KDEMAIN_CORE(ProfileTest) + +#include "moc_ProfileTest.cpp" + + diff --git a/konsole/src/tests/ProfileTest.h b/konsole/src/tests/ProfileTest.h new file mode 100644 index 00000000..ccc25bdd --- /dev/null +++ b/konsole/src/tests/ProfileTest.h @@ -0,0 +1,42 @@ +/* + Copyright 2008 by Robert Knight + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +#ifndef PROFILETEST_H +#define PROFILETEST_H + +#include + +namespace Konsole +{ + +class ProfileTest : public QObject +{ + Q_OBJECT + +private slots: + void testProfile(); + void testClone(); + void testProfileGroup(); + void testProfileFileNames(); +}; + +} + +#endif // PROFILETEST_H + diff --git a/konsole/src/tests/PtyTest.cpp b/konsole/src/tests/PtyTest.cpp new file mode 100644 index 00000000..e4bcaf15 --- /dev/null +++ b/konsole/src/tests/PtyTest.cpp @@ -0,0 +1,95 @@ +/* + Copyright 2008 by Robert Knight + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +// Own +#include "PtyTest.h" + +// Qt +#include +#include + +// KDE +#include + +using namespace Konsole; + +void PtyTest::init() +{ +} + +void PtyTest::cleanup() +{ +} + +void PtyTest::testFlowControl() +{ + Pty pty; + const bool input = true; + pty.setFlowControlEnabled(input); + const bool output = pty.flowControlEnabled(); + QCOMPARE(output, input); +} + +void PtyTest::testEraseChar() +{ + Pty pty; + const char input = 'x'; + pty.setEraseChar(input); + const char output = pty.eraseChar(); + QCOMPARE(output, input); +} + +void PtyTest::testUseUtmp() +{ + Pty pty; + const bool input = true; + pty.setUseUtmp(input); + const bool output = pty.isUseUtmp(); + QCOMPARE(output, input); +} + +void PtyTest::testWindowSize() +{ + Pty pty; + QSize input(80, 40); + pty.setWindowSize(input.width(), input.height()); + QSize output = pty.windowSize(); + QCOMPARE(output, input); +} + +void PtyTest::testRunProgram() +{ + Pty pty; + QString program = "sh"; + QStringList arguments ; + arguments << program; + QStringList environments; + const int result = pty.start(program, arguments, environments); + + QCOMPARE(result, 0); + + // since there is no other processes using this pty, the two methods + // should return the same pid. + QCOMPARE(Q_PID(pty.foregroundProcessGroup()), Q_PID(pty.pid())); +} + +QTEST_KDEMAIN_CORE(PtyTest) + +#include "moc_PtyTest.cpp" + diff --git a/konsole/src/tests/PtyTest.h b/konsole/src/tests/PtyTest.h new file mode 100644 index 00000000..d24e04e8 --- /dev/null +++ b/konsole/src/tests/PtyTest.h @@ -0,0 +1,47 @@ +/* + Copyright 2008 by Robert Knight + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +#ifndef PTYTEST_H +#define PTYTEST_H + +#include "../Pty.h" + +namespace Konsole +{ + +class PtyTest : public QObject +{ + Q_OBJECT + +private slots: + void init(); + void cleanup(); + + void testFlowControl(); + void testEraseChar(); + void testUseUtmp(); + void testWindowSize(); + + void testRunProgram(); +}; + +} + +#endif // PTYTEST_H + diff --git a/konsole/src/tests/SessionManagerTest.cpp b/konsole/src/tests/SessionManagerTest.cpp new file mode 100644 index 00000000..8cef7c02 --- /dev/null +++ b/konsole/src/tests/SessionManagerTest.cpp @@ -0,0 +1,42 @@ +/* + Copyright 2008 by Robert Knight + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +// Own +#include "SessionManagerTest.h" + +// KDE +#include + +using namespace Konsole; + +void SessionManagerTest::testWarnNotImplemented() +{ + qWarning() << "SessionManager tests not implemented"; +} +void SessionManagerTest::init() +{ +} +void SessionManagerTest::cleanup() +{ +} + +QTEST_KDEMAIN_CORE(SessionManagerTest) + +#include "moc_SessionManagerTest.cpp" + diff --git a/konsole/src/tests/SessionManagerTest.h b/konsole/src/tests/SessionManagerTest.h new file mode 100644 index 00000000..d20c04f0 --- /dev/null +++ b/konsole/src/tests/SessionManagerTest.h @@ -0,0 +1,42 @@ +/* + Copyright 2008 by Robert Knight + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +#ifndef SESSIONMANAGERTEST_H +#define SESSIONMANAGERTEST_H + +#include "../SessionManager.h" + +namespace Konsole +{ + +class SessionManagerTest : public QObject +{ + Q_OBJECT + +private slots: + void init(); + void cleanup(); + + void testWarnNotImplemented(); +}; + +} + +#endif // SESSIONMANAGERTEST_H + diff --git a/konsole/src/tests/SessionTest.cpp b/konsole/src/tests/SessionTest.cpp new file mode 100644 index 00000000..0aeb743c --- /dev/null +++ b/konsole/src/tests/SessionTest.cpp @@ -0,0 +1,62 @@ +/* + Copyright 2013 by Kurt Hindenburg + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +// Own +#include "SessionTest.h" + +#include "qtest_kde.h" + +// Konsole +#include "../Session.h" +#include "../Emulation.h" +#include "../History.h" + +using namespace Konsole; + +void SessionTest::testNoProfile() +{ + Session* session = new Session(); + + // No profile loaded, nothing to run + QCOMPARE(session->isRunning(), false); + QCOMPARE(session->sessionId(), 1); + QCOMPARE(session->isRemote(), false); + QCOMPARE(session->program(), QString("")); + QCOMPARE(session->arguments(), QStringList()); + QCOMPARE(session->tabTitleFormat(Session::LocalTabTitle), QString("")); + QCOMPARE(session->tabTitleFormat(Session::RemoteTabTitle), QString("")); + + delete session; +} + +void SessionTest::testEmulation() +{ + Session* session = new Session(); + + Emulation* emulation = session->emulation(); + + QCOMPARE(emulation->lineCount(), 40); + + delete session; +} + +QTEST_KDEMAIN(SessionTest , GUI) + +#include "moc_SessionTest.cpp" + diff --git a/konsole/src/tests/SessionTest.h b/konsole/src/tests/SessionTest.h new file mode 100644 index 00000000..d95b2198 --- /dev/null +++ b/konsole/src/tests/SessionTest.h @@ -0,0 +1,42 @@ +/* + Copyright 2013 by Kurt Hindenburg + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +#ifndef SESSIONTEST_H +#define SESSIONTEST_H + +#include + +namespace Konsole +{ + +class SessionTest : public QObject +{ + Q_OBJECT + +private slots: + void testNoProfile(); + void testEmulation(); + +private: +}; + +} + +#endif // SESSIONTEST_H + diff --git a/konsole/src/tests/ShellCommandTest.cpp b/konsole/src/tests/ShellCommandTest.cpp new file mode 100644 index 00000000..b7a460ae --- /dev/null +++ b/konsole/src/tests/ShellCommandTest.cpp @@ -0,0 +1,118 @@ +/* + Copyright 2008 by Robert Knight + Copyright 2013 by Kurt Hindenburg + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +// Own +#include "ShellCommandTest.h" + +// Qt +#include +#include + +// KDE +#include + +using namespace Konsole; + +void ShellCommandTest::init() +{ +} + +void ShellCommandTest::cleanup() +{ +} + +void ShellCommandTest::testConstructorWithOneArguemnt() +{ + const QString fullCommand("sudo apt-get update"); + ShellCommand shellCommand(fullCommand); + QCOMPARE(shellCommand.command(), QString("sudo")); + QCOMPARE(shellCommand.fullCommand(), fullCommand); + +} + +void ShellCommandTest::testConstructorWithTwoArguments() +{ + const QString command("wc"); + QStringList arguments; + arguments << "wc" << "-l" << "*.cpp" ; + + ShellCommand shellCommand(command, arguments); + QCOMPARE(shellCommand.command(), command); + QCOMPARE(shellCommand.arguments(), arguments); + QCOMPARE(shellCommand.fullCommand(), arguments.join(" ")); +} + +void ShellCommandTest::testExpandEnvironmentVariable() +{ + QString text = "PATH=$PATH:~/bin"; + const QString env = "PATH"; + const QString value = "/usr/sbin:/sbin:/usr/local/bin:/usr/bin:/bin"; + qputenv(env.toLocal8Bit(), value.toLocal8Bit()); + QString result = ShellCommand::expand(text); + QString expected = text.replace('$' + env, value); + QCOMPARE(result, expected); + + text = "PATH=$PATH:\\$ESCAPED:~/bin"; + qputenv(env.toLocal8Bit(), value.toLocal8Bit()); + result = ShellCommand::expand(text); + expected = text.replace('$' + env, value); + QCOMPARE(result, expected); +} + +void ShellCommandTest::testValidEnvCharacter() +{ + QChar validChar('A'); + const bool result = ShellCommand::isValidEnvCharacter(validChar); + QCOMPARE(result, true); +} + +void ShellCommandTest::testValidLeadingEnvCharacter() +{ + QChar invalidChar('9'); + const bool result = ShellCommand::isValidLeadingEnvCharacter(invalidChar); + QCOMPARE(result, false); +} + +void ShellCommandTest::testArgumentsWithSpaces() +{ + const QString command("dir"); + QStringList arguments; + arguments << "dir" << "c:\\Program Files" << "System" << "*.ini" ; + const QString expected_arg("dir \"c:\\Program Files\" System *.ini"); + + ShellCommand shellCommand(command, arguments); + QCOMPARE(shellCommand.command(), command); + QCOMPARE(shellCommand.arguments(), arguments); + QCOMPARE(shellCommand.fullCommand(), expected_arg); +} + +void ShellCommandTest::testEmptyCommand() +{ + const QString command(""); + ShellCommand shellCommand(command); + QCOMPARE(shellCommand.command(), QString()); + QCOMPARE(shellCommand.arguments(), QStringList()); + QCOMPARE(shellCommand.fullCommand(), QString()); +} + +QTEST_KDEMAIN_CORE(ShellCommandTest) + +#include "moc_ShellCommandTest.cpp" + diff --git a/konsole/src/tests/ShellCommandTest.h b/konsole/src/tests/ShellCommandTest.h new file mode 100644 index 00000000..22268159 --- /dev/null +++ b/konsole/src/tests/ShellCommandTest.h @@ -0,0 +1,50 @@ +/* + Copyright 2008 by Robert Knight + Copyright 2013 by Kurt Hindenburg + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +#ifndef SHELLCOMMANDTEST_H +#define SHELLCOMMANDTEST_H + +#include "../ShellCommand.h" + +namespace Konsole +{ + +class ShellCommandTest : public QObject +{ + Q_OBJECT + +private slots: + void init(); + void cleanup(); + + void testConstructorWithOneArguemnt(); + void testConstructorWithTwoArguments(); + void testExpandEnvironmentVariable(); + void testValidEnvCharacter(); + void testValidLeadingEnvCharacter(); + void testArgumentsWithSpaces(); + void testEmptyCommand(); + +}; + +} + +#endif // SHELLCOMMANDTEST_H + diff --git a/konsole/src/tests/TerminalCharacterDecoderTest.cpp b/konsole/src/tests/TerminalCharacterDecoderTest.cpp new file mode 100644 index 00000000..62e447ca --- /dev/null +++ b/konsole/src/tests/TerminalCharacterDecoderTest.cpp @@ -0,0 +1,81 @@ +/* + Copyright 2008 by Robert Knight + Copyright 2013 by Kurt Hindenburg + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +// Own +#include "TerminalCharacterDecoderTest.h" + +// Qt +#include +#include + +#include +#include + +// KDE +#include + +using namespace Konsole; + +void TerminalCharacterDecoderTest::init() +{ +} + +void TerminalCharacterDecoderTest::cleanup() +{ +} + +void TerminalCharacterDecoderTest::testPlainTextDecoder() +{ + TerminalCharacterDecoder* decoder = new PlainTextDecoder(); + Character characters[6]; + characters[0] = Character('h'); + characters[1] = Character('e'); + characters[2] = Character('l'); + characters[3] = Character('l'); + characters[4] = Character('o'); + characters[5] = Character(' '); + characters[5].isRealCharacter = false; + QString outputString; + QTextStream outputStream(&outputString); + decoder->begin(&outputStream); + decoder->decodeLine(characters, 6, LINE_DEFAULT); + decoder->end(); + QCOMPARE(outputString, QString("hello")); + delete decoder; +} + +void TerminalCharacterDecoderTest::testHTMLFileForValidity() +{ + QString fileName = "konsole.html"; + QFile fi(fileName); + + if (!fi.exists()) + QSKIP("Test html file not found.", SkipSingle); + + QXmlSimpleReader xmlReader; + QXmlInputSource source(&fi); + + QVERIFY(xmlReader.parse(&source)); +} + +QTEST_KDEMAIN_CORE(TerminalCharacterDecoderTest) + +#include "moc_TerminalCharacterDecoderTest.cpp" + diff --git a/konsole/src/tests/TerminalCharacterDecoderTest.h b/konsole/src/tests/TerminalCharacterDecoderTest.h new file mode 100644 index 00000000..b760048c --- /dev/null +++ b/konsole/src/tests/TerminalCharacterDecoderTest.h @@ -0,0 +1,43 @@ +/* + Copyright 2008 by Robert Knight + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +#ifndef TERMINALCHARACTERDECODERTEST_H +#define TERMINALCHARACTERDECODERTEST_H + +#include "../TerminalCharacterDecoder.h" + +namespace Konsole +{ + +class TerminalCharacterDecoderTest : public QObject +{ + Q_OBJECT + +private slots: + void init(); + void cleanup(); + + void testPlainTextDecoder(); + void testHTMLFileForValidity(); +}; + +} + +#endif // TERMINALCHARACTERDECODERTEST_H + diff --git a/konsole/src/tests/TerminalInterfaceTest.cpp b/konsole/src/tests/TerminalInterfaceTest.cpp new file mode 100644 index 00000000..c004dea8 --- /dev/null +++ b/konsole/src/tests/TerminalInterfaceTest.cpp @@ -0,0 +1,152 @@ +/* + Copyright 2014 by Kurt Hindenburg + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +// Own +#include "TerminalInterfaceTest.h" + +// Qt +#include + +// KDE +#include +#include +#include +#include "../Part.h" + +using namespace Konsole; + +void TerminalInterfaceTest::testTerminalInterface() +{ + QString currentDirectory; + QString retVal; + bool result; + + // create a Konsole part and attempt to connect to it + _terminalPart = createPart(); + if (!_terminalPart) + QSKIP("konsolepart not found.", SkipSingle); + + TerminalInterface* terminal = qobject_cast(_terminalPart); + QVERIFY(terminal); + terminal->showShellInDir(QDir::home().path()); + + int foregroundProcessId = terminal->foregroundProcessId(); + QCOMPARE(foregroundProcessId, -1); + QString foregroundProcessName = terminal->foregroundProcessName(); + QCOMPARE(foregroundProcessName, QString("")); + + // terminalProcessId() is the user's default shell + // FIXME: find a way to verify this + // int terminalProcessId = terminal->terminalProcessId(); + + // Let's try using QSignalSpy + // http://techbase.kde.org/Development/Tutorials/Unittests + // QSignalSpy is really a QList of QLists, so we take the first + // list, which corresponds to the arguments for the first signal + // we caught. + + QSignalSpy stateSpy(_terminalPart, SIGNAL(currentDirectoryChanged(QString))); + QVERIFY(stateSpy.isValid()); + + // Now we check to make sure we don't have any signals already + QCOMPARE(stateSpy.count(), 0); + + // Let's trigger some signals + + // #1A - Test signal currentDirectoryChanged(QString) + currentDirectory = QString("/tmp"); + terminal->sendInput("cd " + currentDirectory + '\n'); + QTest::qWait(4000); + QCOMPARE(stateSpy.count(), 1); + + // Correct result? + QList firstSignalArgs = stateSpy.takeFirst(); + + QString firstSignalState = firstSignalArgs.at(0).toString(); + QCOMPARE(firstSignalState, currentDirectory); + + // Test KonsolePart API currentWorkingDirectory() + result = QMetaObject::invokeMethod(_terminalPart, + "currentWorkingDirectory", + Qt::DirectConnection, + Q_RETURN_ARG(QString, retVal)); + QVERIFY(result); + QCOMPARE(retVal, currentDirectory); + + // #1B - Test signal currentDirectoryChanged(QString) + // Invalid directory - no signal should be emitted + terminal->sendInput("cd /usrADADFASDF\n"); + QTest::qWait(2000); + QCOMPARE(stateSpy.count(), 0); + + // Should be no change since the above cd didn't work + result = QMetaObject::invokeMethod(_terminalPart, + "currentWorkingDirectory", + Qt::DirectConnection, + Q_RETURN_ARG(QString, retVal)); + QVERIFY(result); + QCOMPARE(retVal, currentDirectory); + + + // Test starting a new program + QString command = "top"; + terminal->sendInput(command + '\n'); + QTest::qWait(2000); + // FIXME: find a good way to validate process id of 'top' + foregroundProcessId = terminal->foregroundProcessId(); + QVERIFY(foregroundProcessId != -1); + foregroundProcessName = terminal->foregroundProcessName(); + QCOMPARE(foregroundProcessName, command); + + terminal->sendInput("q"); + QTest::qWait(2000); + + // Nothing running in foreground + foregroundProcessId = terminal->foregroundProcessId(); + QCOMPARE(foregroundProcessId, -1); + foregroundProcessName = terminal->foregroundProcessName(); + QCOMPARE(foregroundProcessName, QString("")); + + // Test destroyed() + QSignalSpy destroyedSpy(_terminalPart, SIGNAL(destroyed())); + QVERIFY(destroyedSpy.isValid()); + + // Now we check to make sure we don't have any signals already + QCOMPARE(destroyedSpy.count(), 0); + + delete _terminalPart; + QCOMPARE(destroyedSpy.count(), 1); + +} + +KParts::Part* TerminalInterfaceTest::createPart() +{ + KPluginFactory* factory = KPluginLoader("konsolepart").factory(); + if (!factory) // not found + return 0; + + KParts::Part* terminalPart = factory->create(this); + + return terminalPart; +} + +QTEST_KDEMAIN(TerminalInterfaceTest , GUI) + +#include "moc_TerminalInterfaceTest.cpp" + diff --git a/konsole/src/tests/TerminalInterfaceTest.h b/konsole/src/tests/TerminalInterfaceTest.h new file mode 100644 index 00000000..f76e6e1d --- /dev/null +++ b/konsole/src/tests/TerminalInterfaceTest.h @@ -0,0 +1,48 @@ +/* + Copyright 2014 by Kurt Hindenburg + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +#ifndef TERMINALINTERFACETEST_H +#define TERMINALINTERFACETEST_H + +#include +#include + +namespace Konsole +{ + +class TerminalInterfaceTest : public QObject +{ + Q_OBJECT + +public: + +private slots: + void testTerminalInterface(); + +private: + KParts::Part* createPart(); + + KParts::Part* _terminalPart; + +}; + +} + +#endif // TERMINALINTERFACETEST_H + diff --git a/konsole/src/tests/TerminalTest.cpp b/konsole/src/tests/TerminalTest.cpp new file mode 100644 index 00000000..ef9a18b9 --- /dev/null +++ b/konsole/src/tests/TerminalTest.cpp @@ -0,0 +1,105 @@ +/* + Copyright 2013 by Kurt Hindenburg + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +// Own +#include "TerminalTest.h" + +#include "qtest_kde.h" + +// Konsole +#include "../TerminalDisplay.h" +#include "../CharacterColor.h" +#include "../ColorScheme.h" + +using namespace Konsole; + +void TerminalTest::testScrollBarPositions() +{ + + TerminalDisplay* display = new TerminalDisplay(0); + + // ScrollBar Positions + display->setScrollBarPosition(Enum::ScrollBarLeft); + QCOMPARE(display->scrollBarPosition(), Enum::ScrollBarLeft); + display->setScrollBarPosition(Enum::ScrollBarRight); + QCOMPARE(display->scrollBarPosition(), Enum::ScrollBarRight); + display->setScrollBarPosition(Enum::ScrollBarHidden); + QCOMPARE(display->scrollBarPosition(), Enum::ScrollBarHidden); + + delete display; +} + +void TerminalTest::testColorTable() +{ + // These are from ColorScheme.cpp but they can be anything to test + const ColorEntry defaultTable[TABLE_COLORS] = { + ColorEntry(QColor(0x00, 0x00, 0x00)), ColorEntry(QColor(0xFF, 0xFF, 0xFF)), + ColorEntry(QColor(0x00, 0x00, 0x00)), ColorEntry(QColor(0xB2, 0x18, 0x18)), + ColorEntry(QColor(0x18, 0xB2, 0x18)), ColorEntry(QColor(0xB2, 0x68, 0x18)), + ColorEntry(QColor(0x18, 0x18, 0xB2)), ColorEntry(QColor(0xB2, 0x18, 0xB2)), + ColorEntry(QColor(0x18, 0xB2, 0xB2)), ColorEntry(QColor(0xB2, 0xB2, 0xB2)), + ColorEntry(QColor(0x00, 0x00, 0x00)), ColorEntry(QColor(0xFF, 0xFF, 0xFF)), + ColorEntry(QColor(0x68, 0x68, 0x68)), ColorEntry(QColor(0xFF, 0x54, 0x54)), + ColorEntry(QColor(0x54, 0xFF, 0x54)), ColorEntry(QColor(0xFF, 0xFF, 0x54)), + ColorEntry(QColor(0x54, 0x54, 0xFF)), ColorEntry(QColor(0xFF, 0x54, 0xFF)), + ColorEntry(QColor(0x54, 0xFF, 0xFF)), ColorEntry(QColor(0x00, 0xFF, 0xFF)) + }; + + TerminalDisplay* display = new TerminalDisplay(0); + + display->setColorTable(defaultTable); + + const ColorEntry* colorTable = display->colorTable(); + + for (int i = 0; i < TABLE_COLORS; i++) + QCOMPARE(colorTable[i], defaultTable[i]); + + ColorEntry colorEntry = ColorEntry(QColor(0x00, 0x00, 0x00)); + QVERIFY(colorTable[1] != colorEntry); + + // UseCurrentFormat is the default FontWeight + colorEntry = ColorEntry(QColor(0x00, 0x00, 0x00), ColorEntry::Bold); + QVERIFY(colorTable[0] != colorEntry); + + colorEntry = ColorEntry(QColor(0x00, 0x00, 0x00), ColorEntry::Normal); + QVERIFY(colorTable[0] != colorEntry); + + colorEntry = ColorEntry(QColor(0x00, 0x00, 0x00), ColorEntry::UseCurrentFormat); + QVERIFY(colorTable[0] == colorEntry); + + delete display; +} + +void TerminalTest::testSize() +{ + TerminalDisplay* display = new TerminalDisplay(0); + + QCOMPARE(display->columns(), 1); + QCOMPARE(display->lines(), 1); + + // TODO: setSize doesn't change size... + //display->setSize(80, 25); + + delete display; +} + +QTEST_KDEMAIN(TerminalTest , GUI) + +#include "moc_TerminalTest.cpp" + diff --git a/konsole/src/tests/TerminalTest.h b/konsole/src/tests/TerminalTest.h new file mode 100644 index 00000000..41c8db45 --- /dev/null +++ b/konsole/src/tests/TerminalTest.h @@ -0,0 +1,43 @@ +/* + Copyright 2013 by Kurt Hindenburg + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +#ifndef TERMINALTEST_H +#define TERMINALTEST_H + +#include + +namespace Konsole +{ + +class TerminalTest : public QObject +{ + Q_OBJECT + +private slots: + void testScrollBarPositions(); + void testColorTable(); + void testSize(); + +private: +}; + +} + +#endif // TERMINALTEST_H + diff --git a/konsole/tests/9x15.repertoire-utf8 b/konsole/tests/9x15.repertoire-utf8 new file mode 100644 index 00000000..f3c76b9d --- /dev/null +++ b/konsole/tests/9x15.repertoire-utf8 @@ -0,0 +1,219 @@ +Characters available in 9x15, encoded in UTF-8 (RFC 2279): + +Basic Latin (U+0000-U+007F): + + !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_ +`abcdefghijklmnopqrstuvwxyz{|}~ + +Latin-1 Supplement (U+0080-U+00FF): + + ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞß +àáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ + +Latin Extended-A (U+0100-U+017F): + +ĀāĂ㥹ĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪīĬĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿ +ŀŁłŃńŅņŇňʼnŊŋŌōŎŏŐőŒœŔŕŖŗŘřŚśŜŝŞşŠšŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſ + +Latin Extended-B (U+0180-U+024F): + +ƀƁƂƃƄƅƆƇƈƉƊƋƌƍƎƏƐƑƒƓƔƕƖƗƘƙƚƛƜƝƞƟƠơƢƣƤƥƦƧƨƩƪƫƬƭƮƯưƱƲƳƴƵƶƷƸƹƺƻƼƽƾƿ +ǀǁǂǃDŽDždžLJLjljNJNjnjǍǎǏǐǑǒǓǔǕǖǗǘǙǚǛǜǝǞǟǠǡǢǣǤǥǦǧǨǩǪǫǬǭǮǯǰDZDzdzǴǵǶǷǸǹǺǻǼǽǾǿ +ȀȁȂȃȄȅȆȇȈȉȊȋȌȍȎȏȐȑȒȓȔȕȖȗȘșȚțȜȝȞȟȢȣȤȥȦȧȨȩȪȫȬȭȮȯȰȱȲȳ + +IPA Extensions (U+0250-U+02AF): + +ɐɑɒɓɔɕɖɗɘəɚɛɜɝɞɟɠɡɢɣɤɥɦɧɨɩɪɫɬɭɮɯɰɱɲɳɴɵɶɷɸɹɺɻɼɽɾɿʀʁʂʃʄʅʆʇʈʉʊʋʌʍʎʏ +ʐʑʒʓʔʕʖʗʘʙʚʛʜʝʞʟʠʡʢʣʤʥʦʧʨʩʪʫʬʭ + +Spacing Modifier Letters (U+02B0-U+02FF): + +ʰʱʲʳʴʵʶʷʸʹʺʻʼʽʾʿˀˁ˂˃˄˅ˆˇˈˉˊˋˌˍˎˏːˑ˒˓˔˕˖˗˘˙˚˛˜˝˞˟ˠˡˢˣˤ˥˦˧˨˩˪˫ˬ˭ˮ + +Combining Diacritical Marks (U+0300-U+036F): + +̴̵̶̷̸̡̢̧̨̛̖̗̘̙̜̝̞̟̠̣̤̥̦̩̪̫̬̭̮̯̰̱̲̳̹̺̻̼̀́̂̃̄̅̆̇̈̉̊̋̌̍̎̏̐̑̒̓̔̽̾̿̕̚ +͇͈͉͍͎̀́͂̓̈́͆͊͋͌͢͠͡ͅ + +Greek (U+0370-U+03FF): + +ʹ͵ͺ;΄΅Ά·ΈΉΊΌΎΏΐΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩΪΫάέήίΰαβγδεζηθικλμνξοπρς +στυφχψωϊϋόύώϐϑϒϓϔϕϖϗϚϛϜϝϞϟϠϡϢϣϤϥϦϧϨϩϪϫϬϭϮϯϰϱϲϳ + +Cyrillic (U+0400-U+04FF): + +ЀЁЂЃЄЅІЇЈЉЊЋЌЍЎЏАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмноп +рстуфхцчшщъыьэюяѐёђѓєѕіїјљњћќѝўџѠѡѢѣѤѥѦѧѨѩѪѫѬѭѮѯѰѱѲѳѴѵѶѷѸѹѺѻѼѽѾѿ +Ҁҁ҂҃҄҅҆҈҉ҌҍҎҏҐґҒғҔҕҖҗҘҙҚқҜҝҞҟҠҡҢңҤҥҦҧҨҩҪҫҬҭҮүҰұҲҳҴҵҶҷҸҹҺһҼҽҾҿӀӁӂ +ӃӄӇӈӋӌӐӑӒӓӔӕӖӗӘәӚӛӜӝӞӟӠӡӢӣӤӥӦӧӨөӪӫӬӭӮӯӰӱӲӳӴӵӸӹ + +Armenian (U+0530-U+058F): + +ԱԲԳԴԵԶԷԸԹԺԻԼԽԾԿՀՁՂՃՄՅՆՇՈՉՊՋՌՍՎՏՐՑՒՓՔՕՖՙ՚՛՜՝՞՟աբգդեզէըթժիլխծկհձղճ +մյնշոչպջռսվտրցւփքօֆև։֊ + +Hebrew (U+0590-U+05FF): + +ְֱֲֳִֵֶַָֹֻּֽ־ֿ׀ׁׂ׃ׄאבגדהוזחטיךכלםמןנסעףפץצקרשתװױײ׳״ + +Thai (U+0E00-U+0E7F): + +กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำิีึืฺุู฿เแโใไ +ๅๆ็่้๊๋์ํ๎๏๐๑๒๓๔๕๖๗๘๙๚๛ + +Lao (U+0E80-U+0EFF): + +ກຂຄງຈຊຍດຕຖທນບປຜຝພຟມຢຣລວສຫອຮຯະັາຳິີຶືົຼຽເແໂໃໄໆ່້໊໋ໍ໐໑໒໓໔໕໖໗໘໙ໜໝ + +Georgian (U+10A0-U+10FF): + +ႠႡႢႣႤႥႦႧႨႩႪႫႬႭႮႯႰႱႲႳႴႵႶႷႸႹႺႻႼႽႾႿჀჁჂჃჄჅაბგდევზთიკლმნოპჟრსტუფქღყშჩ +ცძწჭხჯჰჱჲჳჴჵჶ჻ + +Ethiopic (U+1200-U+137F): + +ሀሁሂሃሄህሆለሉሊላሌልሎሏሐሑሒሓሔሕሖሗመሙሚማሜምሞሟሠሡሢሣሤሥሦሧረሩሪራሬርሮሯሰሱሲሳሴስሶሷሸሹሺሻሼሽሾሿቀ +ቁቂቃቄቅቆቈቊቋቌቍቐቑቒቓቔቕቖቘቚቛቜቝበቡቢባቤብቦቧቨቩቪቫቬቭቮቯተቱቲታቴትቶቷቸቹቺቻቼችቾቿኀኁኂኃኄኅኆኈኊ +ኋኌኍነኑኒናኔንኖኗኘኙኚኛኜኝኞኟአኡኢኣኤእኦኧከኩኪካኬክኮኰኲኳኴኵኸኹኺኻኼኽኾዀዂዃዄዅወዉዊዋዌውዎዐዑዒዓዔዕ +ዖዘዙዚዛዜዝዞዟዠዡዢዣዤዥዦዧየዩዪያዬይዮደዱዲዳዴድዶዷዸዹዺዻዼዽዾዿጀጁጂጃጄጅጆጇገጉጊጋጌግጎጐጒጓጔጕጘጙጚጛ +ጜጝጞጠጡጢጣጤጥጦጧጨጩጪጫጬጭጮጯጰጱጲጳጴጵጶጷጸጹጺጻጼጽጾጿፀፁፂፃፄፅፆፈፉፊፋፌፍፎፏፐፑፒፓፔፕፖፗፘፙፚ፡።፣ +፤፥፦፧፨፩፪፫፬፭፮፯፰፱፲፳፴፵፶፷፸፹፺፻፼ + +Runic (U+16A0-U+16FF): + +ᚠᚡᚢᚣᚤᚥᚦᚧᚨᚩᚪᚫᚬᚭᚮᚯᚰᚱᚲᚳᚴᚵᚶᚷᚸᚹᚺᚻᚼᚽᚾᚿᛀᛁᛂᛃᛄᛅᛆᛇᛈᛉᛊᛋᛌᛍᛎᛏᛐᛑᛒᛓᛔᛕᛖᛗᛘᛙᛚᛛᛜᛝᛞᛟ +ᛠᛡᛢᛣᛤᛥᛦᛧᛨᛩᛪ᛫᛬᛭ᛮᛯᛰ + +Latin Extended Additional (U+1E00-U+1EFF): + +ḀḁḂḃḄḅḆḇḈḉḊḋḌḍḎḏḐḑḒḓḔḕḖḗḘḙḚḛḜḝḞḟḠḡḢḣḤḥḦḧḨḩḪḫḬḭḮḯḰḱḲḳḴḵḶḷḸḹḺḻḼḽḾḿ +ṀṁṂṃṄṅṆṇṈṉṊṋṌṍṎṏṐṑṒṓṔṕṖṗṘṙṚṛṜṝṞṟṠṡṢṣṤṥṦṧṨṩṪṫṬṭṮṯṰṱṲṳṴṵṶṷṸṹṺṻṼṽṾṿ +ẀẁẂẃẄẅẆẇẈẉẊẋẌẍẎẏẐẑẒẓẔẕẖẗẘẙẚẛẠạẢảẤấẦầẨẩẪẫẬậẮắẰằẲẳẴẵẶặẸẹẺẻẼẽẾếỀềỂể +ỄễỆệỈỉỊịỌọỎỏỐốỒồỔổỖỗỘộỚớỜờỞởỠỡỢợỤụỦủỨứỪừỬửỮữỰựỲỳỴỵỶỷỸỹ + +Greek Extended (U+1F00-U+1FFF): + +ἀἁἂἃἄἅἆἇἈἉἊἋἌἍἎἏἐἑἒἓἔἕἘἙἚἛἜἝἠἡἢἣἤἥἦἧἨἩἪἫἬἭἮἯἰἱἲἳἴἵἶἷἸἹἺἻἼἽἾἿὀὁὂὃ +ὄὅὈὉὊὋὌὍὐὑὒὓὔὕὖὗὙὛὝὟὠὡὢὣὤὥὦὧὨὩὪὫὬὭὮὯὰάὲέὴήὶίὸόὺύὼώᾀᾁᾂᾃᾄᾅᾆᾇᾈᾉᾊᾋᾌᾍ +ᾎᾏᾐᾑᾒᾓᾔᾕᾖᾗᾘᾙᾚᾛᾜᾝᾞᾟᾠᾡᾢᾣᾤᾥᾦᾧᾨᾩᾪᾫᾬᾭᾮᾯᾰᾱᾲᾳᾴᾶᾷᾸᾹᾺΆᾼ᾽ι᾿῀῁ῂῃῄῆῇῈΈῊΉῌ῍῎῏ +ῐῑῒΐῖῗῘῙῚΊ῝῞῟ῠῡῢΰῤῥῦῧῨῩῪΎῬ῭΅`ῲῳῴῶῷῸΌῺΏῼ´῾ + +General Punctuation (U+2000-U+206F): + +           ‐‑‒–—―‖‗‘’‚‛“”„‟†‡•‣․‥…‧ ‰‱′″‴‵‶‷‸‹›※‼‽‾‿⁀⁁⁂⁃⁄⁅⁆⁈⁉⁊⁋⁌ +⁍ + +Superscripts and Subscripts (U+2070-U+209F): + +⁰⁴⁵⁶⁷⁸⁹⁺⁻⁼⁽⁾ⁿ₀₁₂₃₄₅₆₇₈₉₊₋₌₍₎ + +Currency Symbols (U+20A0-U+20CF): + +₠₡₢₣₤₥₦₧₨₩₪₫€₭₮₯ + +Combining Marks for Symbols (U+20D0-U+20FF): + +⃒⃓⃘⃙⃚⃐⃑⃔⃕⃖⃗⃛⃜⃝⃞⃟⃠⃡⃢⃣ + +Letterlike Symbols (U+2100-U+214F): + +℀℁ℂ℃℄℅℆ℇ℈℉ℊℋℌℍℎℏℐℑℒℓ℔ℕ№℗℘ℙℚℛℜℝ℞℟℠℡™℣ℤ℥Ω℧ℨ℩KÅℬℭ℮ℯℰℱℲℳℴℵℶℷℸℹ℺ + +Number Forms (U+2150-U+218F): + +⅓⅔⅕⅖⅗⅘⅙⅚⅛⅜⅝⅞⅟ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫⅬⅭⅮⅯⅰⅱⅲⅳⅴⅵⅶⅷⅸⅹⅺⅻⅼⅽⅾⅿↀↁↂↃ + +Arrows (U+2190-U+21FF): + +←↑→↓↔↕↖↗↘↙↚↛↜↝↞↟↠↡↢↣↤↥↦↧↨↩↪↫↬↭↮↯↰↱↲↳↴↵↶↷↸↹↺↻↼↽↾↿⇀⇁⇂⇃⇄⇅⇆⇇⇈⇉⇊⇋⇌⇍⇎⇏ +⇐⇑⇒⇓⇔⇕⇖⇗⇘⇙⇚⇛⇜⇝⇞⇟⇠⇡⇢⇣⇤⇥⇦⇧⇨⇩⇪⇫⇬⇭⇮⇯⇰⇱⇲⇳ + +Mathematical Operators (U+2200-U+22FF): + +∀∁∂∃∄∅∆∇∈∉∊∋∌∍∎∏∐∑−∓∔∕∖∗∘∙√∛∜∝∞∟∠∡∢∣∤∥∦∧∨∩∪∫∬∭∮∯∰∱∲∳∴∵∶∷∸∹∺∻∼∽∾∿ +≀≁≂≃≄≅≆≇≈≉≊≋≌≍≎≏≐≑≒≓≔≕≖≗≘≙≚≛≜≝≞≟≠≡≢≣≤≥≦≧≨≩≪≫≬≭≮≯≰≱≲≳≴≵≶≷≸≹≺≻≼≽≾≿ +⊀⊁⊂⊃⊄⊅⊆⊇⊈⊉⊊⊋⊌⊍⊎⊏⊐⊑⊒⊓⊔⊕⊖⊗⊘⊙⊚⊛⊜⊝⊞⊟⊠⊡⊢⊣⊤⊥⊦⊧⊨⊩⊪⊫⊬⊭⊮⊯⊰⊱⊲⊳⊴⊵⊶⊷⊸⊹⊺⊻⊼⊽⊾⊿ +⋀⋁⋂⋃⋄⋅⋆⋇⋈⋉⋊⋋⋌⋍⋎⋏⋐⋑⋒⋓⋔⋕⋖⋗⋘⋙⋚⋛⋜⋝⋞⋟⋠⋡⋢⋣⋤⋥⋦⋧⋨⋩⋪⋫⋬⋭⋮⋯⋰⋱ + +Miscellaneous Technical (U+2300-U+23FF): + +⌀⌁⌂⌃⌄⌅⌆⌇⌈⌉⌊⌋⌌⌍⌎⌏⌐⌑⌒⌓⌔⌕⌖⌗⌘⌙⌚⌛⌜⌝⌞⌟⌠⌡⌢⌣⌤⌥⌦⌧⌨〈〉⌫⌬⌭⌮⌯⌰⌱⌲⌳⌴⌵⌶⌷⌸⌹⌺⌻⌼⌽⌾⌿ +⍀⍁⍂⍃⍄⍅⍆⍇⍈⍉⍊⍋⍌⍍⍎⍏⍐⍑⍒⍓⍔⍕⍖⍗⍘⍙⍚⍛⍜⍝⍞⍟⍠⍡⍢⍣⍤⍥⍦⍧⍨⍩⍪⍫⍬⍭⍮⍯⍰⍱⍲⍳⍴⍵⍶⍷⍸⍹⍺⍻⍽⍾⍿⎀ +⎁⎂⎃⎄⎅⎆⎇⎈⎉⎊⎋⎌⎍⎎⎏⎐⎑⎒⎓⎔⎕⎖⎗⎘⎙⎚ + +Control Pictures (U+2400-U+243F): + +␀␁␂␃␄␅␆␇␈␉␊␋␌␍␎␏␐␑␒␓␔␕␖␗␘␙␚␛␜␝␞␟␠␡␢␣␤␥␦ + +Optical Character Recognition (U+2440-U+245F): + +⑀⑁⑂⑃⑄⑅⑆⑇⑈⑉⑊ + +Enclosed Alphanumerics (U+2460-U+24FF): + +①②③④⑤⑥⑦⑧⑨⑩⑪⑫⑬⑭⑮⑯⑰⑱⑲⑳⑴⑵⑶⑷⑸⑹⑺⑻⑼⑽⑾⑿⒀⒁⒂⒃⒄⒅⒆⒇⒈⒉⒊⒋⒌⒍⒎⒏⒐⒑⒒⒓⒔⒕⒖⒗⒘⒙⒚⒛⒜⒝⒞⒟ +⒠⒡⒢⒣⒤⒥⒦⒧⒨⒩⒪⒫⒬⒭⒮⒯⒰⒱⒲⒳⒴⒵ⒶⒷⒸⒹⒺⒻⒼⒽⒾⒿⓀⓁⓂⓃⓄⓅⓆⓇⓈⓉⓊⓋⓌⓍⓎⓏⓐⓑⓒⓓⓔⓕⓖⓗⓘⓙⓚⓛⓜⓝⓞⓟ +ⓠⓡⓢⓣⓤⓥⓦⓧⓨⓩ⓪ + +Box Drawing (U+2500-U+257F): + +─━│┃┄┅┆┇┈┉┊┋┌┍┎┏┐┑┒┓└┕┖┗┘┙┚┛├┝┞┟┠┡┢┣┤┥┦┧┨┩┪┫┬┭┮┯┰┱┲┳┴┵┶┷┸┹┺┻┼┽┾┿ +╀╁╂╃╄╅╆╇╈╉╊╋╌╍╎╏═║╒╓╔╕╖╗╘╙╚╛╜╝╞╟╠╡╢╣╤╥╦╧╨╩╪╫╬╭╮╯╰╱╲╳╴╵╶╷╸╹╺╻╼╽╾╿ + +Block Elements (U+2580-U+259F): + +▀▁▂▃▄▅▆▇█▉▊▋▌▍▎▏▐░▒▓▔▕ + +Geometric Shapes (U+25A0-U+25FF): + +■□▢▣▤▥▦▧▨▩▪▫▬▭▮▯▰▱▲△▴▵▶▷▸▹►▻▼▽▾▿◀◁◂◃◄◅◆◇◈◉◊○◌◍◎●◐◑◒◓◔◕◖◗◘◙◚◛◜◝◞◟ +◠◡◢◣◤◥◦◧◨◩◪◫◬◭◮◯◰◱◲◳◴◵◶◷ + +Miscellaneous Symbols (U+2600-U+26FF): + +☀☁☂☃☄★☆☇☈☉☊☋☌☍☎☏☐☑☒☓☙☚☛☜☝☞☟☠☡☢☣☤☥☦☧☨☩☪☫☬☭☮☯☰☱☲☳☴☵☶☷☸☹☺☻☼☽☾☿♀♁♂♃♄ +♅♆♇♈♉♊♋♌♍♎♏♐♑♒♓♔♕♖♗♘♙♚♛♜♝♞♟♠♡♢♣♤♥♦♧♨♩♪♫♬♭♮♯♰♱ + +Dingbats (U+2700-U+27BF): + +✁✂✃✄✆✇✈✉✑✒✓✔✕✖✗✘✙✚✛✜✝✞✟✠✡✢✣✤✥✦✧✩✪✫✬✭✮✯✰✱✲✳✴✵✶✷✸✹✺✻✼✽✾✿❃❄❅❆❇❈❉❊❋❍ +❏❐❑❒❖❘❙❚❛❜❝❞❡❢❣❤❥❦❧❶❷❸❹❺❻❼❽❾❿➀➁➂➃➄➅➆➇➈➉➊➋➌➍➎➏➐➑➒➓➔➘➙➚➛➜➝➞➟➠➡➢➣➤➥ +➦➧➨➩➪➫➬➭➮➯➱➲➳➴➵➶➷➸➹➺➻➼➽➾ + +Braille Patterns (U+2800-U+28FF): + +⠀⠁⠂⠃⠄⠅⠆⠇⠈⠉⠊⠋⠌⠍⠎⠏⠐⠑⠒⠓⠔⠕⠖⠗⠘⠙⠚⠛⠜⠝⠞⠟⠠⠡⠢⠣⠤⠥⠦⠧⠨⠩⠪⠫⠬⠭⠮⠯⠰⠱⠲⠳⠴⠵⠶⠷⠸⠹⠺⠻⠼⠽⠾⠿ +⡀⡁⡂⡃⡄⡅⡆⡇⡈⡉⡊⡋⡌⡍⡎⡏⡐⡑⡒⡓⡔⡕⡖⡗⡘⡙⡚⡛⡜⡝⡞⡟⡠⡡⡢⡣⡤⡥⡦⡧⡨⡩⡪⡫⡬⡭⡮⡯⡰⡱⡲⡳⡴⡵⡶⡷⡸⡹⡺⡻⡼⡽⡾⡿ +⢀⢁⢂⢃⢄⢅⢆⢇⢈⢉⢊⢋⢌⢍⢎⢏⢐⢑⢒⢓⢔⢕⢖⢗⢘⢙⢚⢛⢜⢝⢞⢟⢠⢡⢢⢣⢤⢥⢦⢧⢨⢩⢪⢫⢬⢭⢮⢯⢰⢱⢲⢳⢴⢵⢶⢷⢸⢹⢺⢻⢼⢽⢾⢿ +⣀⣁⣂⣃⣄⣅⣆⣇⣈⣉⣊⣋⣌⣍⣎⣏⣐⣑⣒⣓⣔⣕⣖⣗⣘⣙⣚⣛⣜⣝⣞⣟⣠⣡⣢⣣⣤⣥⣦⣧⣨⣩⣪⣫⣬⣭⣮⣯⣰⣱⣲⣳⣴⣵⣶⣷⣸⣹⣺⣻⣼⣽⣾⣿ + +CJK Symbols and Punctuation (U+3000-U+303F): + +、。《》〓〚〛〜〿 + +Hiragana (U+3040-U+309F): + +ぁあぃいぅうぇえぉおかがきぎくぐけげこごさざしじすずせぜそぞただ +ちぢっつづてでとどなにぬねのはばぱひびぴふぶぷへべぺほぼぽまみむ +めもゃやゅゆょよらりるれろゎわゐゑをんゔ゙゚゛゜ゝゞ + +Private Use (U+E000-U+F8FF): + + + + +Alphabetic Presentation Forms (U+FB00-U+FB4F): + +fffiflffifflſtstﬓﬔﬕﬖﬗיִﬞײַﬠﬡﬢﬣﬤﬥﬦﬧﬨ﬩שׁשׂשּׁשּׂאַאָאּבּגּדּהּוּזּטּיּךּכּלּמּנּסּףּפּצּקּרּשּתּוֹבֿכֿפֿﭏ + +Combining Half Marks (U+FE20-U+FE2F): + +︠︡︢︣ + +Halfwidth and Fullwidth Forms (U+FF00-U+FFEF): + +。「」、・ヲァィゥェォャュョッーアイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワン゙゚ + +Specials (U+FFF0-U+FFFD): + +� diff --git a/konsole/tests/GLASS.utf8 b/konsole/tests/GLASS.utf8 new file mode 100644 index 00000000..610b595e --- /dev/null +++ b/konsole/tests/GLASS.utf8 @@ -0,0 +1,170 @@ +I Can Eat Glass +In various languages + +Adopted from http://www.columbia.edu/kermit/utf8.html#glass +Do not edit. Submit additions to the URL above and resynch. + +Permission is granted by the Kermit project (http://www.columbia.edu/kermit/) +to redistribute this file, with absolutely no warranty. + + + +Sanskrit: काचं शक्नोम्यत्तुम् । नोपहिनस्ति माम् ॥ +Sanskrit (standard transcription): kācaṃ śaknomyattum; nopahinasti mām. +Classical Greek: ὕαλον ϕαγεῖν δύναμαι· τοῦτο οὔ με βλάπτει. +Greek: Μπορώ να φάω σπασμένα γυαλιά χωρίς να πάθω τίποτα. +Etruscan: (NEEDED) +Latin: Vitrum edere possum; mihi non nocet. +Old French: Je puis mangier del voirre. Ne me nuit. +French: Je peux manger du verre, ça ne me fait pas de mal. +Provençal / Occitan: Pòdi manjar de veire, me nafrariá pas. +Québécois: J'peux manger d'la vitre, ça m'fa pas mal. +Walloon: Dji pou magnî do vêre, çoula m' freut nén må. +Champenois: (NEEDED) +Lorrain: (NEEDED) +Picard: Ch'peux mingi du verre, cha m'foé mie n'ma. +Corsican: (NEEDED) +Kreyòl Ayisyen: Mwen kap manje vè, li pa blese'm. +Basque: Kristala jan dezaket, ez dit minik ematen. +Catalan: Puc menjar vidre que no em fa mal. +Spanish: Puedo comer vidrio, no me hace daño. +Aragones: Puedo minchar beire, no me'n fa mal . +Galician: Eu podo xantar cristais e non cortarme. +Portuguese: Posso comer vidro, não me faz mal. +Brazilian Portuguese (7): Posso comer vidro, não me machuca. +Caboverdiano: M' podê cumê vidru, ca ta maguâ-m'. +Papiamentu: Ami por kome glas anto e no ta hasimi daño. +Italian: Posso mangiare il vetro e non mi fa male. +Milanese: Sôn bôn de magnà el véder, el me fa minga mal. +Roman: Me posso magna' er vetro, e nun me fa male. +Napoletano: M' pozz magna' o'vetr, e nun m' fa mal. +Sicilian: Puotsu mangiari u vitru, nun mi fa mali. +Venetian: Mi posso magnare el vetro, no'l me fa mae. +Zeneise (Genovese): Pòsso mangiâ o veddro e o no me fà mâ. +Rheto-Romance / Romansch: (NEEDED) +Romany / Tsigane: (NEEDED) +Romanian: Pot să mănânc sticlă și ea nu mă rănește. +Esperanto: Mi povas manĝi vitron, ĝi ne damaĝas min. +Pictish: (NEEDED) +Breton: (NEEDED) +Cornish: Mý a yl dybry gwéder hag éf ny wra ow ankenya. +Welsh: Dw i'n gallu bwyta gwydr, 'dyw e ddim yn gwneud dolur i mi. +Manx Gaelic: Foddym gee glonney agh cha jean eh gortaghey mee. +Old Irish (Ogham): ᚛᚛ᚉᚑᚅᚔᚉᚉᚔᚋ ᚔᚈᚔ ᚍᚂᚐᚅᚑ ᚅᚔᚋᚌᚓᚅᚐ᚜ +Old Irish (Latin): Con·iccim ithi nglano. Ním·géna. +Irish: Is féidir liom gloinne a ithe. Ní dhéanann sí dochar ar bith dom. +Scottish Gaelic: S urrainn dhomh gloinne ithe; cha ghoirtich i mi. +Anglo-Saxon (Runes): ᛁᚳ᛫ᛗᚨᚷ᛫ᚷᛚᚨᛋ᛫ᛖᚩᛏᚪᚾ᛫ᚩᚾᛞ᛫ᚻᛁᛏ᛫ᚾᛖ᛫ᚻᛖᚪᚱᛗᛁᚪᚧ᛫ᛗᛖ᛬ +Anglo-Saxon (Latin): Ic mæg glæs eotan ond hit ne hearmiað me. +Middle English: Ich canne glas eten and hit hirtiþ me nouȝt. +English: I can eat glass and it doesn't hurt me. +English (IPA): [aɪ kæn iːt glɑːs ænd ɪt dɐz nɒt hɜːt miː] (Received Pronunciation) +English (Braille): ⠊⠀⠉⠁⠝⠀⠑⠁⠞⠀⠛⠇⠁⠎⠎⠀⠁⠝⠙⠀⠊⠞⠀⠙⠕⠑⠎⠝⠞⠀⠓⠥⠗⠞⠀⠍⠑ +Lalland Scots / Doric: Ah can eat gless, it disnae hurt us. +Glaswegian: (NEEDED) +Gothic (4): 𐌼𐌰𐌲 𐌲𐌻𐌴𐍃 𐌹̈𐍄𐌰𐌽, 𐌽𐌹 𐌼𐌹𐍃 𐍅𐌿 𐌽𐌳𐌰𐌽 𐌱𐍂𐌹𐌲𐌲𐌹𐌸. +Old Norse (Runes): ᛖᚴ ᚷᛖᛏ ᛖᛏᛁ ᚧ ᚷᛚᛖᚱ ᛘᚾ ᚦᛖᛋᛋ ᚨᚧ ᚡᛖ ᚱᚧᚨ ᛋᚨᚱ +Old Norse (Latin): Ek get etið gler án þess að verða sár. +Norsk / Norwegian (Nynorsk): Eg kan eta glas utan å skada meg. +Norsk / Norwegian (Bokmål): Jeg kan spise glass uten å skade meg. +Føroyskt / Faroese: (NEEDED) +Íslenska / Icelandic: Ég get etið gler án þess að meiða mig. +Svenska / Swedish: Jag kan äta glas utan att skada mig. +Dansk / Danish: Jeg kan spise glas, det gør ikke ondt på mig. +Soenderjysk: Æ ka æe glass uhen at det go mæ naue. +Frysk / Frisian: Ik kin glês ite, it docht me net sear. +Nederlands / Dutch: Ik kan glas eten, het doet mij geen kwaad. +Kirchröadsj/Bôchesserplat: Iech ken glaas èèse, mer 't deet miech jing pieng. +Afrikaans: Ek kan glas eet, maar dit doen my nie skade nie. +Lëtzebuergescht / Luxemburgish: Ech kan Glas iessen, daat deet mir nët wei. +Deutsch / German: Ich kann Glas essen, ohne mir weh zu tun. +Ruhrdeutsch: Ich kann Glas verkasematuckeln, ohne dattet mich wat jucken tut. +Lausitzer Mundart ("Lusatian"): Ich koann Gloos assn und doas dudd merr ni wii. +Odenwälderisch: Iech konn glaasch voschbachteln ohne dass es mir ebbs daun doun dud. +Sächsisch / Saxon: 'sch kann Glos essn, ohne dass'sch mer wehtue. +Pfälzisch: Isch konn Glass fresse ohne dasses mer ebbes ausmache dud. +Schwäbisch / Swabian: I kå Glas frässa, ond des macht mr nix! +Bayrisch / Bavarian: I koh Glos esa, und es duard ma ned wei. +Allemannisch: I kaun Gloos essen, es tuat ma ned weh. +Schwyzerdütsch: Ich chan Glaas ässe, das tuet mir nöd weeh. +Hungarian: Meg tudom enni az üveget, nem lesz tőle bajom. +Suomi / Finnish: Voin syödä lasia, se ei vahingoita minua. +Sami (Northern): Sáhtán borrat lása, dat ii leat bávččas. +Erzian: Мон ярсан суликадо, ды зыян эйстэнзэ а ули. +Karelian: (NEEDED) +Vepsian: (NEEDED) +Votian: (NEEDED) +Livonian: (NEEDED) +Estonian: Ma võin klaasi süüa, see ei tee mulle midagi. +Latvian: Es varu ēst stiklu, tas man nekaitē. +Lithuanian: Aš galiu valgyti stiklą ir jis manęs nežeidžia +Old Prussian: (NEEDED) +Sorbian (Wendish): (NEEDED) +Czech: Mohu jíst sklo, neublíží mi. +Slovak: Môžem jesť sklo. Nezraní ma. +Polska / Polish: Mogę jeść szkło i mi nie szkodzi. +Slovenian: Lahko jem steklo, ne da bi mi škodovalo. +Croatian: Ja mogu jesti staklo i ne boli me. +Serbian (Latin): Mogu jesti staklo a da mi ne škodi. +Serbian (Cyrillic): Могу јести стакло а да ми не шкоди. +Macedonian: Можам да јадам стакло, а не ме штета. +Russian: Я могу есть стекло, оно мне не вредит. +Belarusian (Cyrillic): Я магу есці шкло, яно мне не шкодзіць. +Belarusian (Lacinka): Ja mahu jeści škło, jano mne ne škodzić. +Ukrainian: Я можу їсти шкло, й воно мені не пошкодить. +Bulgarian: Мога да ям стъкло, то не ми вреди. +Georgian: მინას ვჭამ და არა მტკივა. +Armenian: Կրնամ ապակի ուտել և ինծի անհանգիստ չըներ։ +Albanian: Unë mund të ha qelq dhe nuk më gjen gjë. +Turkish: Cam yiyebilirim, bana zararı dokunmaz. +Turkish (Ottoman): جام ييه بلورم بڭا ضررى طوقونمز +Bangla / Bengali: আমি কাঁচ খেতে পারি, তাতে আমার কোনো ক্ষতি হয় না। +Marathi: मी काच खाऊ शकतो, मला ते दुखत नाही. +Hindi: मैं काँच खा सकता हूँ, मुझे उस से कोई पीडा नहीं होती. +Tamil: நான் கண்ணாடி சாப்பிடுவேன், அதனால் எனக்கு ஒரு கேடும் வராது. +Urdu(2): میں کانچ کھا سکتا ہوں اور مجھے تکلیف نہیں ہوتی ۔ +Pashto(2): زه شيشه خوړلې شم، هغه ما نه خوږوي +Farsi / Persian: .من می توانم بدونِ احساس درد شيشه بخورم +Arabic(2): أنا قادر على أكل الزجاج و هذا لا يؤلمني. +Aramaic: (NEEDED) +Hebrew(2): אני יכול לאכול זכוכית וזה לא מזיק לי. +Yiddish(2): איך קען עסן גלאָז און עס טוט מיר נישט װײ. +Judeo-Arabic: (NEEDED) +Ladino: (NEEDED) +Gǝʼǝz: (NEEDED) +Amharic: (NEEDED) +Twi: Metumi awe tumpan, ɜnyɜ me hwee. +Hausa (Latin): Inā iya taunar gilāshi kuma in gamā lāfiyā. +Hausa (Ajami) (2): إِنا إِىَ تَونَر غِلَاشِ كُمَ إِن غَمَا لَافِىَا +Yoruba(3): Mo lè je̩ dígí, kò ní pa mí lára. +(Ki)Swahili: Naweza kula bilauri na sikunyui. +Malay: Saya boleh makan kaca dan ia tidak mencederakan saya. +Tagalog: Kaya kong kumain nang bubog at hindi ako masaktan. +Chamorro: Siña yo' chumocho krestat, ti ha na'lalamen yo'. +Javanese: Aku isa mangan beling tanpa lara. +Burmese: (NEEDED) +Vietnamese (quốc ngữ): Tôi có thể ăn thủy tinh mà không hại gì. +Vietnamese (nôm) (4): 些 𣎏 世 咹 水 晶 𦓡 空 𣎏 害 咦 +Khmer: (NEEDED) +Lao: (NEEDED) +Thai: ฉันกินกระจกได้ แต่มันไม่ทำให้ฉันเจ็บ +Mongolian (Cyrillic): Би шил идэй чадна, надад хортой биш +Mongolian (Classic) (5): ᠪᠢ ᠰᠢᠯᠢ ᠢᠳᠡᠶᠦ ᠴᠢᠳᠠᠨᠠ ᠂ ᠨᠠᠳᠤᠷ ᠬᠣᠤᠷᠠᠳᠠᠢ ᠪᠢᠰᠢ +Dzongkha: (NEEDED) +Nepali: (NEEDED) +Tibetan: ཤེལ་སྒོ་ཟ་ནས་ང་ན་གི་མ་རེད། +Chinese: 我能吞下玻璃而不伤身体。 +Chinese (Traditional): 我能吞下玻璃而不傷身體。 +Taiwanese(6): Góa ē-tàng chia̍h po-lê, mā bē tio̍h-siong. +Japanese: 私はガラスを食べられます。それは私を傷つけません。 +Korean: 나는 유리를 먹을 수 있어요. 그래도 아프지 않아요 +Bislama: Mi save kakae glas, hemi no save katem mi. +Hawaiian: Hiki iaʻu ke ʻai i ke aniani; ʻaʻole nō lā au e ʻeha. +Marquesan: E koʻana e kai i te karahi, mea ʻā, ʻaʻe hauhau. +Chinook Jargon: Naika məkmək kakshət labutay, pi weyk ukuk munk-sik nay. +Navajo: Tsésǫʼ yishą́ągo bííníshghah dóó doo shił neezgai da. +Cherokee (and Cree, Ojibwa, Inuktitut, and other Native American languages): (NEEDED) +Garifuna: (NEEDED) +Gullah: (NEEDED) +Lojban: mi kakne le nu citka le blaci .iku'i le se go'i na xrani mi +Nórdicg: Ljœr ye caudran créneþ ý jor cẃran. diff --git a/konsole/tests/UTF-8-demo.txt b/konsole/tests/UTF-8-demo.txt new file mode 100644 index 00000000..4363f27b --- /dev/null +++ b/konsole/tests/UTF-8-demo.txt @@ -0,0 +1,212 @@ + +UTF-8 encoded sample plain-text file +‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ + +Markus Kuhn [ˈmaʳkʊs kuːn] — 2002-07-25 + + +The ASCII compatible UTF-8 encoding used in this plain-text file +is defined in Unicode, ISO 10646-1, and RFC 2279. + + +Using Unicode/UTF-8, you can write in emails and source code things such as + +Mathematics and sciences: + + ∮ E⋅da = Q, n → ∞, ∑ f(i) = ∏ g(i), ⎧⎡⎛┌─────┐⎞⎤⎫ + ⎪⎢⎜│a²+b³ ⎟⎥⎪ + ∀x∈ℝ: ⌈x⌉ = −⌊−x⌋, α ∧ ¬β = ¬(¬α ∨ β), ⎪⎢⎜│───── ⎟⎥⎪ + ⎪⎢⎜⎷ c₈ ⎟⎥⎪ + ℕ ⊆ ℕ₀ ⊂ ℤ ⊂ ℚ ⊂ ℝ ⊂ ℂ, ⎨⎢⎜ ⎟⎥⎬ + ⎪⎢⎜ ∞ ⎟⎥⎪ + ⊥ < a ≠ b ≡ c ≤ d ≪ ⊤ ⇒ (⟦A⟧ ⇔ ⟪B⟫), ⎪⎢⎜ ⎲ ⎟⎥⎪ + ⎪⎢⎜ ⎳aⁱ-bⁱ⎟⎥⎪ + 2H₂ + O₂ ⇌ 2H₂O, R = 4.7 kΩ, ⌀ 200 mm ⎩⎣⎝i=1 ⎠⎦⎭ + +Linguistics and dictionaries: + + ði ıntəˈnæʃənəl fəˈnɛtık əsoʊsiˈeıʃn + Y [ˈʏpsilɔn], Yen [jɛn], Yoga [ˈjoːgɑ] + +APL: + + ((V⍳V)=⍳⍴V)/V←,V ⌷←⍳→⍴∆∇⊃‾⍎⍕⌈ + +Nicer typography in plain text files: + + ╔══════════════════════════════════════════╗ + ║ ║ + ║ • ‘single’ and “double” quotes ║ + ║ ║ + ║ • Curly apostrophes: “We’ve been here” ║ + ║ ║ + ║ • Latin-1 apostrophe and accents: '´` ║ + ║ ║ + ║ • ‚deutsche‘ „Anführungszeichen“ ║ + ║ ║ + ║ • †, ‡, ‰, •, 3–4, —, −5/+5, ™, … ║ + ║ ║ + ║ • ASCII safety test: 1lI|, 0OD, 8B ║ + ║ ╭─────────╮ ║ + ║ • the euro symbol: │ 14.95 € │ ║ + ║ ╰─────────╯ ║ + ╚══════════════════════════════════════════╝ + +Combining characters: + + STARGΛ̊TE SG-1, a = v̇ = r̈, a⃑ ⊥ b⃑ + +Greek (in Polytonic): + + The Greek anthem: + + Σὲ γνωρίζω ἀπὸ τὴν κόψη + τοῦ σπαθιοῦ τὴν τρομερή, + σὲ γνωρίζω ἀπὸ τὴν ὄψη + ποὺ μὲ βία μετράει τὴ γῆ. + + ᾿Απ᾿ τὰ κόκκαλα βγαλμένη + τῶν ῾Ελλήνων τὰ ἱερά + καὶ σὰν πρῶτα ἀνδρειωμένη + χαῖρε, ὦ χαῖρε, ᾿Ελευθεριά! + + From a speech of Demosthenes in the 4th century BC: + + Οὐχὶ ταὐτὰ παρίσταταί μοι γιγνώσκειν, ὦ ἄνδρες ᾿Αθηναῖοι, + ὅταν τ᾿ εἰς τὰ πράγματα ἀποβλέψω καὶ ὅταν πρὸς τοὺς + λόγους οὓς ἀκούω· τοὺς μὲν γὰρ λόγους περὶ τοῦ + τιμωρήσασθαι Φίλιππον ὁρῶ γιγνομένους, τὰ δὲ πράγματ᾿ + εἰς τοῦτο προήκοντα, ὥσθ᾿ ὅπως μὴ πεισόμεθ᾿ αὐτοὶ + πρότερον κακῶς σκέψασθαι δέον. οὐδέν οὖν ἄλλο μοι δοκοῦσιν + οἱ τὰ τοιαῦτα λέγοντες ἢ τὴν ὑπόθεσιν, περὶ ἧς βουλεύεσθαι, + οὐχὶ τὴν οὖσαν παριστάντες ὑμῖν ἁμαρτάνειν. ἐγὼ δέ, ὅτι μέν + ποτ᾿ ἐξῆν τῇ πόλει καὶ τὰ αὑτῆς ἔχειν ἀσφαλῶς καὶ Φίλιππον + τιμωρήσασθαι, καὶ μάλ᾿ ἀκριβῶς οἶδα· ἐπ᾿ ἐμοῦ γάρ, οὐ πάλαι + γέγονεν ταῦτ᾿ ἀμφότερα· νῦν μέντοι πέπεισμαι τοῦθ᾿ ἱκανὸν + προλαβεῖν ἡμῖν εἶναι τὴν πρώτην, ὅπως τοὺς συμμάχους + σώσομεν. ἐὰν γὰρ τοῦτο βεβαίως ὑπάρξῃ, τότε καὶ περὶ τοῦ + τίνα τιμωρήσεταί τις καὶ ὃν τρόπον ἐξέσται σκοπεῖν· πρὶν δὲ + τὴν ἀρχὴν ὀρθῶς ὑποθέσθαι, μάταιον ἡγοῦμαι περὶ τῆς + τελευτῆς ὁντινοῦν ποιεῖσθαι λόγον. + + Δημοσθένους, Γ´ ᾿Ολυνθιακὸς + +Georgian: + + From a Unicode conference invitation: + + გთხოვთ ახლავე გაიაროთ რეგისტრაცია Unicode-ის მეათე საერთაშორისო + კონფერენციაზე დასასწრებად, რომელიც გაიმართება 10-12 მარტს, + ქ. მაინცში, გერმანიაში. კონფერენცია შეჰკრებს ერთად მსოფლიოს + ექსპერტებს ისეთ დარგებში როგორიცაა ინტერნეტი და Unicode-ი, + ინტერნაციონალიზაცია და ლოკალიზაცია, Unicode-ის გამოყენება + ოპერაციულ სისტემებსა, და გამოყენებით პროგრამებში, შრიფტებში, + ტექსტების დამუშავებასა და მრავალენოვან კომპიუტერულ სისტემებში. + +Russian: + + From a Unicode conference invitation: + + Зарегистрируйтесь сейчас на Десятую Международную Конференцию по + Unicode, которая состоится 10-12 марта 1997 года в Майнце в Германии. + Конференция соберет широкий круг экспертов по вопросам глобального + Интернета и Unicode, локализации и интернационализации, воплощению и + применению Unicode в различных операционных системах и программных + приложениях, шрифтах, верстке и многоязычных компьютерных системах. + +Thai (UCS Level 2): + + Excerpt from a poetry on The Romance of The Three Kingdoms (a Chinese + classic 'San Gua'): + + [----------------------------|------------------------] + ๏ แผ่นดินฮั่นเสื่อมโทรมแสนสังเวช พระปกเกศกองบู๊กู้ขึ้นใหม่ + สิบสองกษัตริย์ก่อนหน้าแลถัดไป สององค์ไซร้โง่เขลาเบาปัญญา + ทรงนับถือขันทีเป็นที่พึ่ง บ้านเมืองจึงวิปริตเป็นนักหนา + โฮจิ๋นเรียกทัพทั่วหัวเมืองมา หมายจะฆ่ามดชั่วตัวสำคัญ + เหมือนขับไสไล่เสือจากเคหา รับหมาป่าเข้ามาเลยอาสัญ + ฝ่ายอ้องอุ้นยุแยกให้แตกกัน ใช้สาวนั้นเป็นชนวนชื่นชวนใจ + พลันลิฉุยกุยกีกลับก่อเหตุ ช่างอาเพศจริงหนาฟ้าร้องไห้ + ต้องรบราฆ่าฟันจนบรรลัย ฤๅหาใครค้ำชูกู้บรรลังก์ ฯ + + (The above is a two-column text. If combining characters are handled + correctly, the lines of the second column should be aligned with the + | character above.) + +Ethiopian: + + Proverbs in the Amharic language: + + ሰማይ አይታረስ ንጉሥ አይከሰስ። + ብላ ካለኝ እንደአባቴ በቆመጠኝ። + ጌጥ ያለቤቱ ቁምጥና ነው። + ደሀ በሕልሙ ቅቤ ባይጠጣ ንጣት በገደለው። + የአፍ ወለምታ በቅቤ አይታሽም። + አይጥ በበላ ዳዋ ተመታ። + ሲተረጉሙ ይደረግሙ። + ቀስ በቀስ፥ ዕንቁላል በእግሩ ይሄዳል። + ድር ቢያብር አንበሳ ያስር። + ሰው እንደቤቱ እንጅ እንደ ጉረቤቱ አይተዳደርም። + እግዜር የከፈተውን ጉሮሮ ሳይዘጋው አይድርም። + የጎረቤት ሌባ፥ ቢያዩት ይስቅ ባያዩት ያጠልቅ። + ሥራ ከመፍታት ልጄን ላፋታት። + ዓባይ ማደሪያ የለው፥ ግንድ ይዞ ይዞራል። + የእስላም አገሩ መካ የአሞራ አገሩ ዋርካ። + ተንጋሎ ቢተፉ ተመልሶ ባፉ። + ወዳጅህ ማር ቢሆን ጨርስህ አትላሰው። + እግርህን በፍራሽህ ልክ ዘርጋ። + +Runes: + + ᚻᛖ ᚳᚹᚫᚦ ᚦᚫᛏ ᚻᛖ ᛒᚢᛞᛖ ᚩᚾ ᚦᚫᛗ ᛚᚪᚾᛞᛖ ᚾᚩᚱᚦᚹᛖᚪᚱᛞᚢᛗ ᚹᛁᚦ ᚦᚪ ᚹᛖᛥᚫ + + (Old English, which transcribed into Latin reads 'He cwaeth that he + bude thaem lande northweardum with tha Westsae.' and means 'He said + that he lived in the northern land near the Western Sea.') + +Braille: + + ⡌⠁⠧⠑ ⠼⠁⠒ ⡍⠜⠇⠑⠹⠰⠎ ⡣⠕⠌ + + ⡍⠜⠇⠑⠹ ⠺⠁⠎ ⠙⠑⠁⠙⠒ ⠞⠕ ⠃⠑⠛⠔ ⠺⠊⠹⠲ ⡹⠻⠑ ⠊⠎ ⠝⠕ ⠙⠳⠃⠞ + ⠱⠁⠞⠑⠧⠻ ⠁⠃⠳⠞ ⠹⠁⠞⠲ ⡹⠑ ⠗⠑⠛⠊⠌⠻ ⠕⠋ ⠙⠊⠎ ⠃⠥⠗⠊⠁⠇ ⠺⠁⠎ + ⠎⠊⠛⠝⠫ ⠃⠹ ⠹⠑ ⠊⠇⠻⠛⠹⠍⠁⠝⠂ ⠹⠑ ⠊⠇⠻⠅⠂ ⠹⠑ ⠥⠝⠙⠻⠞⠁⠅⠻⠂ + ⠁⠝⠙ ⠹⠑ ⠡⠊⠑⠋ ⠍⠳⠗⠝⠻⠲ ⡎⠊⠗⠕⠕⠛⠑ ⠎⠊⠛⠝⠫ ⠊⠞⠲ ⡁⠝⠙ + ⡎⠊⠗⠕⠕⠛⠑⠰⠎ ⠝⠁⠍⠑ ⠺⠁⠎ ⠛⠕⠕⠙ ⠥⠏⠕⠝ ⠰⡡⠁⠝⠛⠑⠂ ⠋⠕⠗ ⠁⠝⠹⠹⠔⠛ ⠙⠑ + ⠡⠕⠎⠑ ⠞⠕ ⠏⠥⠞ ⠙⠊⠎ ⠙⠁⠝⠙ ⠞⠕⠲ + + ⡕⠇⠙ ⡍⠜⠇⠑⠹ ⠺⠁⠎ ⠁⠎ ⠙⠑⠁⠙ ⠁⠎ ⠁ ⠙⠕⠕⠗⠤⠝⠁⠊⠇⠲ + + ⡍⠔⠙⠖ ⡊ ⠙⠕⠝⠰⠞ ⠍⠑⠁⠝ ⠞⠕ ⠎⠁⠹ ⠹⠁⠞ ⡊ ⠅⠝⠪⠂ ⠕⠋ ⠍⠹ + ⠪⠝ ⠅⠝⠪⠇⠫⠛⠑⠂ ⠱⠁⠞ ⠹⠻⠑ ⠊⠎ ⠏⠜⠞⠊⠊⠥⠇⠜⠇⠹ ⠙⠑⠁⠙ ⠁⠃⠳⠞ + ⠁ ⠙⠕⠕⠗⠤⠝⠁⠊⠇⠲ ⡊ ⠍⠊⠣⠞ ⠙⠁⠧⠑ ⠃⠑⠲ ⠔⠊⠇⠔⠫⠂ ⠍⠹⠎⠑⠇⠋⠂ ⠞⠕ + ⠗⠑⠛⠜⠙ ⠁ ⠊⠕⠋⠋⠔⠤⠝⠁⠊⠇ ⠁⠎ ⠹⠑ ⠙⠑⠁⠙⠑⠌ ⠏⠊⠑⠊⠑ ⠕⠋ ⠊⠗⠕⠝⠍⠕⠝⠛⠻⠹ + ⠔ ⠹⠑ ⠞⠗⠁⠙⠑⠲ ⡃⠥⠞ ⠹⠑ ⠺⠊⠎⠙⠕⠍ ⠕⠋ ⠳⠗ ⠁⠝⠊⠑⠌⠕⠗⠎ + ⠊⠎ ⠔ ⠹⠑ ⠎⠊⠍⠊⠇⠑⠆ ⠁⠝⠙ ⠍⠹ ⠥⠝⠙⠁⠇⠇⠪⠫ ⠙⠁⠝⠙⠎ + ⠩⠁⠇⠇ ⠝⠕⠞ ⠙⠊⠌⠥⠗⠃ ⠊⠞⠂ ⠕⠗ ⠹⠑ ⡊⠳⠝⠞⠗⠹⠰⠎ ⠙⠕⠝⠑ ⠋⠕⠗⠲ ⡹⠳ + ⠺⠊⠇⠇ ⠹⠻⠑⠋⠕⠗⠑ ⠏⠻⠍⠊⠞ ⠍⠑ ⠞⠕ ⠗⠑⠏⠑⠁⠞⠂ ⠑⠍⠏⠙⠁⠞⠊⠊⠁⠇⠇⠹⠂ ⠹⠁⠞ + ⡍⠜⠇⠑⠹ ⠺⠁⠎ ⠁⠎ ⠙⠑⠁⠙ ⠁⠎ ⠁ ⠙⠕⠕⠗⠤⠝⠁⠊⠇⠲ + + (The first couple of paragraphs of "A Christmas Carol" by Dickens) + +Compact font selection example text: + + ABCDEFGHIJKLMNOPQRSTUVWXYZ /0123456789 + abcdefghijklmnopqrstuvwxyz £©µÀÆÖÞßéöÿ + –—‘“”„†•…‰™œŠŸž€ ΑΒΓΔΩαβγδω АБВГДабвгд + ∀∂∈ℝ∧∪≡∞ ↑↗↨↻⇣ ┐┼╔╘░►☺♀ fi�⑀₂ἠḂӥẄɐː⍎אԱა + +Greetings in various languages: + + Hello world, Καλημέρα κόσμε, コンニチハ + +Box drawing alignment tests: █ + ▉ + ╔══╦══╗ ┌──┬──┐ ╭──┬──╮ ╭──┬──╮ ┏━━┳━━┓ ┎┒┏┑ ╷ ╻ ┏┯┓ ┌┰┐ ▊ ╱╲╱╲╳╳╳ + ║┌─╨─┐║ │╔═╧═╗│ │╒═╪═╕│ │╓─╁─╖│ ┃┌─╂─┐┃ ┗╃╄┙ ╶┼╴╺╋╸┠┼┨ ┝╋┥ ▋ ╲╱╲╱╳╳╳ + ║│╲ ╱│║ │║ ║│ ││ │ ││ │║ ┃ ║│ ┃│ ╿ │┃ ┍╅╆┓ ╵ ╹ ┗┷┛ └┸┘ ▌ ╱╲╱╲╳╳╳ + ╠╡ ╳ ╞╣ ├╢ ╟┤ ├┼─┼─┼┤ ├╫─╂─╫┤ ┣┿╾┼╼┿┫ ┕┛┖┚ ┌┄┄┐ ╎ ┏┅┅┓ ┋ ▍ ╲╱╲╱╳╳╳ + ║│╱ ╲│║ │║ ║│ ││ │ ││ │║ ┃ ║│ ┃│ ╽ │┃ ░░▒▒▓▓██ ┊ ┆ ╎ ╏ ┇ ┋ ▎ + ║└─╥─┘║ │╚═╤═╝│ │╘═╪═╛│ │╙─╀─╜│ ┃└─╂─┘┃ ░░▒▒▓▓██ ┊ ┆ ╎ ╏ ┇ ┋ ▏ + ╚══╩══╝ └──┴──┘ ╰──┴──╯ ╰──┴──╯ ┗━━┻━━┛ ▗▄▖▛▀▜ └╌╌┘ ╎ ┗╍╍┛ ┋ ▁▂▃▄▅▆▇█ + ▝▀▘▙▄▟ diff --git a/konsole/tests/UTF-8-test.txt b/konsole/tests/UTF-8-test.txt new file mode 100644 index 00000000..abd16f72 Binary files /dev/null and b/konsole/tests/UTF-8-test.txt differ diff --git a/konsole/tests/audit.c b/konsole/tests/audit.c new file mode 100644 index 00000000..bc8a5718 --- /dev/null +++ b/konsole/tests/audit.c @@ -0,0 +1,56 @@ +/* + + This is a tiny test program that can be used to track down + strange effects of the emulation. + + Make: + + - gcc -o audit audit.c + + Usage: + + - In TEShell.C let syslog be stdout. + - konsole > ttt + - produce the effect in question. + - run this program. + pressing any key advances the audit + ^C terminates. + + You need to make sure that the size of the screen matches + the one being debugged. + + This code was written by Lars Doelle . + This code is in the public domain. + +*/ + + +#include +#include +#include + +struct termios save; +struct termios curr; + +#define HERE fprintf(stderr,"%s(%d): here.\n",__FILE__,__LINE__) + +main() +{ int cc; + FILE* sysin = fopen("ttt","r"); + tcgetattr(0, &save); + tcgetattr(0, &curr); + cfmakeraw(&curr); + tcsetattr(0, TCSANOW, &curr); + cc = fgetc(sysin); + while( cc > 0 ) + { int tmp; + while (cc > 0) + { + fputc(cc,stdout); cc = fgetc(sysin); + if (cc == 0x1b) break; + } + tmp = fgetc(stdin); + if (tmp == 3) break; + } + tcsetattr(0, TCSANOW, &save); +} diff --git a/konsole/tests/boxes.txt b/konsole/tests/boxes.txt new file mode 100644 index 00000000..cb2df8cf --- /dev/null +++ b/konsole/tests/boxes.txt @@ -0,0 +1,142 @@ +Single width, hollow. +┌─┐ )0lqk +│ │ )0x x +└─┘ )0mqj +┌─┐ +│ │ +└─┘ + +Single width, single fill. +┌┬┐ )0lwk +├┼┤ )0tnu +└┴┘ )0mvj +┌┬┐ +├┼┤ +└┴┘ + +Double width, hollow. +┏━┓ )0  +┃ ┃ )0  +┗━┛ )0  +╔═╗ +║ ║ +╚═╝ + +Double width, double fill. +┏┳┓ )0  +┣╋┫ )0  +┗┻┛ )0  +╔╦╗ +╠╬╣ +╚╩╝ + +Double width, single fill. +┏┯┓ )0  +┠┼┨ )0 n  +┗┷┛ )0  +╔╤╗ +╟┼╢ +╚╧╝ + +Single width, double fill. +┌┰┐ )0l k +┝╋┥ )0  +└┸┘ )0m j +┌╥┐ +╞╬╡ +└╨┘ + +Single width, mixed fill (double horizontal, single vertical). +┌┬┐ )0lwk +┝┿┥ )0  +└┴┘ )0mvj +┌┬┐ +╞╪╡ +└┴┘ + +Double width, mixed fill (double vertical, single horizontal). +┏┳┓ )0  +┠╂┨ )0  +┗┻┛ )0  +╔╦╗ +╟╫╢ +╚╩╝ + +Double horizontal, single vertical. +┍┑ +┕┙ +╒╕ +╘╛ + +Double vertical, single horizontal. +┎┒ +┖┚ +╓╖ +╙╜ + +Single width, double, triple and quadruple dash. +┌╌╌┐ ┌┄┄┐ ┌┈┈┐ +╎ ╎ ┆ ┆ ┊ ┊ +╎ ╎ ┆ ┆ ┊ ┊ +└╌╌┘ └┄┄┘ └┈┈┘ + +Double width, double, triple and quadruple dash. +┏╍╍┓ ┏┅┅┓ ┏┉┉┓ +╏ ╏ ┇ ┇ ┋ ┋ +╏ ╏ ┇ ┇ ┋ ┋ +┗╍╍┛ ┗┅┅┛ ┗┉┉┛ + +One single, two double lines meet. +┢┪ ┲┱ +┡┩ ┺┹ + +One double, two single lines meet. +┞┦ ┭┮ +┟┧ ┵┶ + +One single, three double lines meet. +╇ ╉╊ +╈ + +One double, three single lines meet. +╁ ┾┽ +╀ + +Two double, two single lines meet. +╆╅ +╄╃ + +Mixed width, starting, ending and changing width mid-character. +╷ ╻ ╶╼╸ +╽ ╿ ╺╾╴ +╹ ╵ + +Rounded. +╭─╮ +│ │ +╰─╯ + +Other. +╲ ╳ ╱ + +Block elements. + + ▐ ▌ ▛▀#▀▜ +▄▞▀ ▗▄▀▘ ▌▗▄▖▐ + ▌ ▐ #▐#▌# +▀▚▄ ▝▀▄▖ ▌▝▀▘▐ + ▐ ▌ ▙▄#▄▟ + +▁▂▃▄▅▆▇█ ▖# ▗# + ▉ ▌# ▐# +▔▇ ▊ ▐# ▌# + ▋ ▝# ▘# +░ ▌ +▒░ ▍ ▌# ▐# +▓▒░ ▉ ▎ ▚# ▞# +█▓▒░ ▕ ▏ ▐# ▌# + + +VT-102: http://vt100.net/docs/vt102-ug/table5-13.html +Unicode: http://www.unicode.org/charts/PDF/U2500.pdf + and http://www.unicode.org/charts/PDF/U2580.pdf diff --git a/konsole/tests/bulktest.sh b/konsole/tests/bulktest.sh new file mode 100755 index 00000000..a56348b1 --- /dev/null +++ b/konsole/tests/bulktest.sh @@ -0,0 +1,2 @@ +#!/bin/sh +while true; do echo -n x; done diff --git a/konsole/tests/cat_test_urls b/konsole/tests/cat_test_urls new file mode 100644 index 00000000..488aa85d --- /dev/null +++ b/konsole/tests/cat_test_urls @@ -0,0 +1,253 @@ +# Taken from KDE kdelibs test file +# cat this file to test underline/copy/paste/etc +# resize the terminal to test edges +# This should be cleaned up to have only 1 of each 'test' and seperate into +$ passing and failing. +FILE://localhost/home/root +data:text/plain,foobar?gazonk=flarp +donkey://abc/DE +error:/?error=14&errText=Unknown%20host%20asdfu.adgi.sdfgoi#http://asdfu.adgi.sdfgoi +xasde@kde.org +file:%2Ftmp%2Fkde-ogoffart%2Fkmail +file:/ +file://%1/Mat%C3%A9riel +file:/// +file:///blah +file:///c:/foo%3Fbar +file:///c:/home/dxasde/my#%2f +file:///foo%3Fbar +file:///home/%C6%C7%CE7 +file:///home/%D1%84%D0%B3%D0%BD7 +file:///home/andreas/t%C3%A4st +file:///home/dxasde/ +file:///home/dxasde/..//foo +file:///home/dxasde/cdrdao-1.1.5/dao/#CdrDriver.cc# +file:///home/dxasde/file.txt +file:///home/dxasde/konq tests/Matériel#ref +file:///home/dxasde/konq tests/Matériel?query +file:///home/dxasde/konq%20tests/Mat%C3%A9riel#ref +file:///home/dxasde/konq%20tests/Mat%C3%A9riel?query +file:///home/dxasde/konqtests/Mat%C3%A9riel +file:///home/dxasde/konqtests/Matériel +file:///home/dxasde/my#%23 +file:///home/dxasde/my#%2f +file:///home/dxasde/my#%6a +file:///home/dxasde/my#/ +file:///home/dxasde/my%20tar%20file.tgz +file:///home/dxasde/my%20tar%20file.tgz#gzip:/#tar:/ +file:///home/dxasde/my%20tar%20file.tgz#gzip:/#tar:/#myref +file:///home/dxasde/my%20tar%20file.tgz#gzip:/#tar:/README +file:///home/dxasde/my%20tar%20file.tgz#gzip:/%23tar:/%23myref +file:///home/dxasde/my%20tar%20file.tgz#myref +file:///home/dxasde/myfile +file:///home/dxasde/mynewdir +file:///home/dxasde/mynewdir/subdir +file:///home/dxasde/mynewdir/subdir/foo/ +file:///home/dxasde/myotherfile.txt +file:///home/root +file:///home/test/directory with spaces +file:///home/test/directory%20with%20spaces +file:///my/file +file:///opt/kde2/qt2/doc/html/ +file:///opt/kde2/qt2/doc/html/showimg-main-cpp.html#QObject::connect +file:///opt/kde2/qt2/doc/html/showimg-main-cpp.html#QObject:connect +file:///specials/ +file:///tmp/%E9%AD%94 +file:///tmp/魔 +file:///usr/local/src/kde2/////kdebase/konqueror +file:///usr/local/src/kde2/////kdelibs/kio +file://localhost/my/file +file://www.kde.org/my/file +file:/home/dxasde/cdrdao-1.1.5/dao/#CdrDriver.cc# +file:/home/dxasde/my%20tar%20file.tgz#gzip:/#tar:/ +file:/home/dxasde/my%20tar%20file.tgz#gzip:/#tar:/#myref +file:/home/dxasde/my%20tar%20file.tgz#gzip:/#tar:/README +file:/home/dxasde/myolddir/ +file:/home/test/directory%20with%20spaces +file:/opt/kde2/qt2/doc/html/showimg-main-cpp.html#QObject::connect +file:/opt/kde2/qt2/doc/html/showimg-main-cpp.html#QObject:connect +file:/specials/Print +file:/usr/local/src/kde2/////kdelibs/kio +file:/usr/local/src/kde2/kdelibs/kio/ +fish://foo/%23README%23 +ftp +ftp: +ftp://:password@ftp.kde.org/path +ftp://ftp.kde.org/path +ftp://ftp.kde.org/pub +ftp://host/dir1/dir2/myfile.txt +ftp://user%40host.com@ftp.host.com/ +ftp://user%40host.com@ftp.host.com/var/ +ftp://user%40host.com@ftp.host.com/var/www/ +ftp://user:password@ftp.kde.org/path +ftp://user@ftp.kde.org/path +gzip:/ +gzip:/#myref +host.com +http://%C3%A4.de +http://%E1.foo.de +http://:80 +http://[::FFFF:129.144.52.38]:81/index.html +http://[::ffff:129.144.52.38]#ref +http://[::ffff:129.144.52.38]/cgi/test.cgi +http://[::ffff:129.144.52.38]:81#ref +http://[::ffff:129.144.52.38]:81/index.html +http://[::ffff:129.144.52.38]:81?query +http://[::ffff:129.144.52.38]?query +http://[strange;hostname]/ +http://\303\244.de +http://a.b.c/äöu +http://a:389#b=c +http://a:389?b=c +http://alain.knaff.linux.lu/bug-reports/kde/percentage%25in%25url.html +http://alain.knaff.linux.lu/bug-reports/kde/spaces in url.html +http://alain.knaff.linux.lu/bug-reports/kde/spaces%20in%20url.html +http://xasde:pass@www.kde.org/bleh/ +http://xasde:pass@www.kde.org:81?query +http://xasde@www.kde.org +http://xasxasdww.kde.org/filename.html +http://ferret.lmh.ox.ac.uk/%7Ekdecvs/ +http://ferret.lmh.ox.ac.uk/~kdecvs/ +http://foo.bar/~slajsjdlsjd/test.html +http://google.com/c?c=Translation+%C2%BB+trunk%7C +http://google.com/c?c=Translation+%C2%BB+trunk| +http://host.net/path/#no-query +http://host.net/path/?#http://broken-adsfk-poij31-029mu-2890zupyc-*!*'O-+-0i +http://host.net/path?myfirstquery#andsomeReference +http://kde.org/?a=test%C2%A0foo%A0%A0%A0%A0bar +http://kde.org/a%E2%81%84b +http://localhost/?a=foo%0A%0Abar%20baz&b=foo%0Abar%21%3F +http://mail.yandex.ru/message_part/%D0%9A%D1%80%D0%B8%D1%82%D0%B5%D1%80%D0%B8%D0%B8%20%D0%BE%D1%86%D0%B5%D0%BD%D0%B8%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F%20%D0%BE%D1%80%D0%BB%D0%BE%D0%B2%D0%BE%D0%B9.rar?hid=1.1&mid=391.56424458.99241672611486679803334485488&name=%D0%9A%D1%80%D0%B8%D1%82%D0%B5%D1%80%D0%B8%D0%B8%20%D0%BE%D1%86%D0%B5%D0%BD%D0%B8%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F%20%D0%BE%D1%80%D0%BB%D0%BE%D0%B2%D0%BE%D0%B9.rar +http://meine.db24.de#link=home_c_login_login +http://meine.db24.de?link=home_c_login_login +http://mlc:80/ +http://server.com/dir/ +http://server.com/dir/blubb/ +http://server.com/dir/blubb/blah/ +http://slashdot.org/~RAMMS%2BEIN/ +http://slashdot.org/~RAMMS+EIN/ +http://some.host.net/path/to/file#foo?bar +http://some.host.net/path/to/file#fragmentPrecedes?theQuery +http://something/newpage.html?%5B%7B%22foo:%20bar%22%7D%5D +http://something/newpage.html?[{\ +http://something/other.html +http://strange;username:password@hostname/ +http://strange;username:password@strange;hostname/ +http://strange/ +http://strange@hostname/ +http://strange@strange/ +http://thisisaverylongusername@foobar.com/ +http://translate.google.com/translate_t#en%7Cuk%7Cdemo +http://translate.google.com/translate_t#en|uk|demo +http://waba:pass@[::FFFF:129.144.52.38]:81/index.html +http://asdfo%2Fasdfian@www.website.com/directory/filename?bla#blub +http://asdfo@www.website.com/directory/filename?bla#blub +http://www.Abc.de/FR +http://www.abc.de +http://www.calorieking.com/foo.php?P0=[2006-3-8] +http://www.calorieking.com/personal/diary/ +http://www.calorieking.com/personal/diary/rpc.php?C=jsrs1&F=getDiaryDay&P0=[2006-3-8]&U=1141858921458 +http://www.clever-tanken.de/liste.asp?ort=N%FCrnberg&typ=Diesel +http://www.foo.bar +http://www.foo.bar/foo/bar%0Agnork +http://www.foo.bar/foo/bar\ngnork +http://www.foo.bar/foo?bar%0Agnork +http://www.foo.bar/foo?bar\ngnork +http://www.foo.bar/top//test1/file.html +http://www.foo.bar/top//test2/file2.html +http://www.foo.bar:80 +http://www.foobar.com/ +http://www.google.com%20%20%20%20%20@foobar.com/ +http://www.google.com/foo%20%20%20%20%20%20%20bar/ +http://www.google.com/foo%20bar/ +http://www.google.de/search?q=frerich&hlx=xx&hl=de&empty=&lr=lang+de&test=%2B%20%3A%25 +http://www.kde.org +http://www.kde.org# +http://www.kde.org/bleh/ +http://www.kde.org/cgi/qurl.cgi?hello=My Value +http://www.kde.org/cgi/qurl.cgi?hello=My%20Value +http://www.kde.org/cgi/test.cgi +http://www.kde.org/cgi/test.cgi# +http://www.kde.org/cgi/test.cgi? +http://www.kde.org/cgi/test.cgi?hello:My Value +http://www.kde.org/cgi/test.cgi?hello:My%20Value +http://www.kde.org/cgi/test.cgi?hello=My Value +http://www.kde.org/cgi/test.cgi?hello=My Value+20 +http://www.kde.org/cgi/test.cgi?hello=My%20Value +http://www.kde.org/cgi/test.cgi?hello=My%20Value+20 +http://www.kde.org/foo.cgi +http://www.kde.org/foo.cgi# +http://www.kde.org/foo.cgi#foo=bar +http://www.kde.org/foo.cgi?foo=bar +http://www.kde.org/home/%andreas +http://www.kde.org/home/andreas/t%C3%A4st +http://www.kde.org/home/andreas/täst +http://www.kde.org/home/kde?foobar#test +http://www.kde.org/relative.html +http://www.kde.org/subdir +http://www.kde.org? +http://www.sejlsport.dk/Pr%F8v%20noget%20nyt%20dokumenter.pdf +http://www.sejlsport.dk/graphics/ds/DSUngdom/PDF/Pr%F8v noget nyt dokumenter/Invitation_Kerteminde_11.07.08.pdf +http://www.sejlsport.dk/graphics/ds/DSUngdom/PDF/Pr%F8v%20noget%20nyt%20dokumenter/Invitation_Kerteminde_11.07.08.pdf +http://www.website.com/directory/?hello# +http://www.website.com/directory/?hello#%72%22method +http://www.website.com/directory/?hello#ref +http://www.website.com/directory/?query=test&name=harry +http://www.website.com/directory/down/relative.html +http://www.website.com/directory/filename?bla#blub +http://www.website.com/directory/filename?query=test&name=harry +http://www.website.com/directory/relative.html +http://www.website.com/directory/relative.html#with_reference +http://www.website.com/directory/relative.html?name=harry +http://www.website.com/directory/relative.html?name=harry&age=18 +http://www.website.com/directory/relative.html?name=harry&age=18&age=21 +http://www.website.com/directory/relative.html?name=harry&age=18&age=21&fullname=Harry%20Potter +http://www.website.com/directory/relative.html?query=test&name=harry +http://www.website.com/down/relative.html +http://www.website.com/relative.html +http://www.yahoo.org +http://www.youtube.com/?v=JvOSnRD5aNk +http://www1.foo.bar +http://xn--4ca.de +http://xn--80a.foo.de +https://swww.gad.de:443/servlet/CookieAccepted?MAIL=s@gad.de&VER=25901 +https://asdfo%2Fbastian:pass@web.com:881/foo/?bla +javascript:doSomething() +javascript:window.location+\ +ldap://host.com:6666/o=University%20of%20Michigan,c=US??sub?(cn=Babs%20Jensen) +ldap://host.com:6666/o=University%20of%20Michigan,c=US??sub?(cn=Karl%20Marx) +mailto +mailto: +mailto:asdfd adfds +mailto:User@Host.COM?subject=Hello +mailto:xabce@kde.org +mailto:null@kde.org?subject=hello +mailto:null@kde.org?subject=hello#world +mailto:test[at]gmail[dot]com +mailto:user@host.com +print:/specials/ +print:/specials/Print%20To%20File%20(PDF%252FAcrobat) +ptal://mlc:usb +ptal://mlc:usb:PC_970 +remote:/ +remote:// +smb:/// +smb://domain;username:password@server/share +smb://host +ssh://user@machine?cmd='echo $HOSTNAME' +tar:/#myref +tar:/README +trash:/été +www1.foo.bar +xmpp:ogoffart@kde.org + +# testing edge cases - if 80 columns +http://www.website.com/directory/relative.html?query=test&name=harry http://www.website.com/directory/relative.html?query=test&name=harryhttp://www.website.com/directory/relative.html?query=test&name=harry +-------------- +http://www.website.com/directory/relative.html?query=test&name=harry http://www.website.com/directory/relative.html?query=test&name=harry http://www.website.com/directory/relative.html?query=test&name=harry +-------------- +http://www.website.com/directory/relative.html?query=test&name=harry http://www.website.com/directory/relative.html?query=test&name=harry http://www.website.com/directory/relative.html?query=test&name=harry +-------------- +http://www.website.com/directory/relative.html?query=test&name=harry http://www.website.com/directory/relative.html?query=test&name=harry http://www.website.com/directory/relative.html?query=test&name=harry +-------------- +http://www.website.com/directory/relative.html?query=test&name=harry http://www.website.com/directory/relative.html?query=test&name=harry http://www.website.com/directory/relative.html?query=test&name=harry diff --git a/konsole/tests/color-spaces.pl b/konsole/tests/color-spaces.pl new file mode 100644 index 00000000..8774c044 --- /dev/null +++ b/konsole/tests/color-spaces.pl @@ -0,0 +1,67 @@ +#!/usr/bin/perl +# Author: Todd Larason +# $XFree86: xc/programs/xterm/vttests/256colors2.pl,v 1.1 1999/07/11 08:49:54 dawes Exp $ + +print "256 color mode\n\n"; + +# display back ground colors + +for ($fgbg = 38; $fgbg <= 48; $fgbg +=10) { + +# first the system ones: +print "System colors:\n"; +for ($color = 0; $color < 8; $color++) { + print "\x1b[${fgbg};5;${color}m::"; +} +print "\x1b[0m\n"; +for ($color = 8; $color < 16; $color++) { + print "\x1b[${fgbg};5;${color}m::"; +} +print "\x1b[0m\n\n"; + +# now the color cube +print "Color cube, 6x6x6:\n"; +for ($green = 0; $green < 6; $green++) { + for ($red = 0; $red < 6; $red++) { + for ($blue = 0; $blue < 6; $blue++) { + $color = 16 + ($red * 36) + ($green * 6) + $blue; + print "\x1b[${fgbg};5;${color}m::"; + } + print "\x1b[0m "; + } + print "\n"; +} + +# now the grayscale ramp +print "Grayscale ramp:\n"; +for ($color = 232; $color < 256; $color++) { + print "\x1b[${fgbg};5;${color}m::"; +} +print "\x1b[0m\n\n"; + +} + +print "Examples for the 3-byte color mode\n\n"; + +for ($fgbg = 38; $fgbg <= 48; $fgbg +=10) { + +# now the color cube +print "Color cube\n"; +for ($green = 0; $green < 256; $green+=51) { + for ($red = 0; $red < 256; $red+=51) { + for ($blue = 0; $blue < 256; $blue+=51) { + print "\x1b[${fgbg};2;${red};${green};${blue}m::"; + } + print "\x1b[0m "; + } + print "\n"; +} + +# now the grayscale ramp +print "Grayscale ramp:\n"; +for ($gray = 8; $gray < 256; $gray+=10) { + print "\x1b[${fgbg};2;${gray};${gray};${gray}m::"; +} +print "\x1b[0m\n\n"; + +} diff --git a/konsole/tests/colortest.sh b/konsole/tests/colortest.sh new file mode 100755 index 00000000..5187af42 --- /dev/null +++ b/konsole/tests/colortest.sh @@ -0,0 +1,34 @@ +#!/bin/bash -- +# +# display ANSI colours and test bold/blink attributes +# orginates from Eterm distribution +#------------------------------------------------------------------------- + +ESC=$'\x1b' +CSI="${ESC}[" +RST="${CSI}m" + +echo ""; echo "${RST}" +echo " 40 41 42 43 44 45 46 47 49" +echo " 40 41 42 43 44 45 46 47 49" +for fg in 30 31 32 33 34 35 36 37 39 90 91 92 93 94 95 96 97 +do + l1="$fg "; + l2=" "; + l3=" "; + l4=" "; + l5=" "; + for bg in 40 41 42 43 44 45 46 47 49 + do + l1="${l1}${CSI}${fg};${bg}m Normal ${RST}" + l2="${l2}${CSI}${fg};${bg};1m Bold ${RST}" + l3="${l3}${CSI}${fg};${bg};3m Italic ${RST}" + l4="${l4}${CSI}${fg};${bg};5m Blink ${RST}" + l5="${l5}${CSI}${fg};${bg};1;5m Bold! ${RST}" + done + echo "$l1" + echo "$l2" + echo "$l3" + echo "$l4" + echo "$l5" +done diff --git a/konsole/tests/ct2 b/konsole/tests/ct2 new file mode 100755 index 00000000..8a654eda --- /dev/null +++ b/konsole/tests/ct2 @@ -0,0 +1,20 @@ +#!/bin/sh -- +# +# display ANSI colours and test bold/blink attributes +# orginates from Eterm distribution +#------------------------------------------------------------------------- + +echo ""; echo "" +echo " 40 41 42 43 44 45 46 47 49" +for fg in 30 31 32 33 34 35 36 37 39 +do + l1=" $fg "; + l2=" $fg "; + for bg in 40 41 42 43 44 45 46 47 49 + do + l1="${l1}[${fg};${bg}m xx " + l2="${l2}[${fg};${bg};1m XX " + done + echo "$l1" + echo "$l2" +done diff --git a/konsole/tests/quote.c b/konsole/tests/quote.c new file mode 100644 index 00000000..7377d861 --- /dev/null +++ b/konsole/tests/quote.c @@ -0,0 +1,55 @@ +// a silly quotation utitility + +/* + This code was written by Lars Doelle . + This code is in the public domain. +*/ + +#include +#include + +int skip = 0; +int empty = 1; + +void pchr(int c, int indent) +{ + if (skip) + { + skip = (c != '\n'); + return; + } + switch(c) + { + case '\n': + if (!empty) + printf("\\n\"\n%*s\"",indent,""); + empty = 1; + break; + case '#' : + skip = 1; + break; + case '"' : case '\\': + printf("\\"); + // fallthrough + default: + printf("%c",c); + empty = 0; + break; + } +} + +#define INDENT 2 + +int main(int argc, char* argv[]) +{ int cc; FILE *sysin; + if (argc < 2) { fprintf(stderr,"usage: %s filename\n",argv[0]); return 1; } + sysin = fopen(argv[1],"r"); + if (!sysin) { fprintf(stderr,"cannot open %s\n",argv[1]); perror("reason: "); return 1; } + printf("%*s/* generated by '%s %s' */\n\n",INDENT,"",argv[0],argv[1]); + printf("%*s\"",INDENT,""); + while( (cc = fgetc(sysin)) > 0) + { + pchr(cc,INDENT); + } + printf("\"\n"); +} diff --git a/konsole/tests/ripple.c b/konsole/tests/ripple.c new file mode 100644 index 00000000..8d45f072 --- /dev/null +++ b/konsole/tests/ripple.c @@ -0,0 +1,30 @@ +/* + Ripple test. + Usage: ripple [ w [ l ] ] + w = screen line width, default 80, must be > 0, max 132. + l = how many lines to display, default 1000, must be > 0. + Author: Frank da Cruz, Columbia University, 1995. + This is in the public domain as far as can be determined. +*/ +char *crlf = "\015\012"; +char *p = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]\ +^_`abcdefghijklmnopqrstuvwxyz{|}~ !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGH\ +IJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~ !\"#$%&'()*+,-./012\ +3456789:;<=>?@ABCD"; + +main(argc,argv) int argc; char *argv[]; { + int i, j, w = 80, l = 1000; + + if (argc > 1) /* User-specified width */ + w = atoi(argv[1]); + if (argc > 2) /* User-specified number of lines */ + l = atoi(argv[2]); + if (w < 1 || l < 1 || w > 132) /* Quit upon conversion error */ + exit(1); + + for (j = i = 0; i < l; i++) { /* Ripple loop */ + write(1, p+j, w); + write(1, crlf, 2); + if (++j > 94) j = 0; + } +} diff --git a/konsole/tests/sesstest b/konsole/tests/sesstest new file mode 100755 index 00000000..27127173 --- /dev/null +++ b/konsole/tests/sesstest @@ -0,0 +1,2 @@ +#!/bin/sh +while true; do date; sleep 1; done diff --git a/konsole/tests/signaltests.c b/konsole/tests/signaltests.c new file mode 100644 index 00000000..45cd05a8 --- /dev/null +++ b/konsole/tests/signaltests.c @@ -0,0 +1,76 @@ +/* + Author: Kasper Laudrup + This code is in the public domain. + From patch from bko 214908 +*/ +#include +#include +#include + +int signals[] = {SIGSTOP, SIGCONT, SIGHUP, SIGINT, SIGTERM, SIGKILL, SIGUSR1, SIGUSR2}; + +print_signal_name(int signal) { + char* signame; + switch(signal) { + case SIGSTOP: + signame = "SIGSTOP"; + break; + case SIGCONT: + signame = "SIGCONT"; + break; + case SIGHUP: + signame = "SIGHUP"; + break; + case SIGINT: + signame = "SIGINT"; + break; + case SIGTERM: + signame = "SIGTERM"; + break; + case SIGKILL: + signame = "SIGKILL"; + break; + case SIGUSR1: + signame = "SIGUSR1"; + break; + case SIGUSR2: + signame = "SIGUSR1"; + break; + default: + signame = "UNKNOWN"; + break; + } + printf("Caught signal: %s\n", signame); +} + +void handler(int signal) { +} + +int main(int argc, char *argv[]) +{ + sigset_t waitset; + struct sigaction sigact; + int signal, result, i; + int signals_size = sizeof(signals) / sizeof(int); + + sigemptyset(&sigact.sa_mask); + sigemptyset(&waitset); + sigact.sa_flags = 0; + sigact.sa_handler = handler; + for (i=0; i +David Faure +Simon Hausmann +Holger Freyther +and are all available under the LGPL license. +See the individual files for more. diff --git a/libs/konq/CMakeLists.txt b/libs/konq/CMakeLists.txt new file mode 100644 index 00000000..595611b5 --- /dev/null +++ b/libs/konq/CMakeLists.txt @@ -0,0 +1,61 @@ +add_subdirectory( favicons ) +add_subdirectory( Templates ) +if(ENABLE_TESTING) + add_subdirectory( tests ) +endif() + +########### libkonq ############### + +set(konq_LIB_SRCS + konq_popupmenu.cpp # used by konqueror, kfind, folderview, kickoff + konq_popupmenuplugin.cpp # for KonqPopupMenu and its plugins + konq_dndpopupmenuplugin.cpp # for KonqDndPopupMenu and its plugins + konq_copytomenu.cpp # used by dolphin, KonqPopupMenu + konq_operations.cpp # used by dolphin and konqueror + konqmimedata.cpp # used by dolphin, KonqOperations, some filemanagement konqueror modules. + kversioncontrolplugin.cpp # used by dolphin and its version control plugins +) + +add_library(konq ${LIBRARY_TYPE} ${konq_LIB_SRCS}) + +target_link_libraries(konq PRIVATE + ${X11_X11_LIB} +) +target_link_libraries(konq PUBLIC + ${KDE4_KPARTS_LIBS} + ${KDE4_KFILE_LIBS} +) + +set_target_properties(konq PROPERTIES + VERSION ${GENERIC_LIB_VERSION} + SOVERSION ${GENERIC_LIB_SOVERSION} +) + +install(TARGETS konq ${INSTALL_TARGETS_DEFAULT_ARGS}) + +########### install files ############### + +install(FILES + directory_bookmarkbar.desktop + DESTINATION ${KDE4_DATA_INSTALL_DIR}/kbookmark +) + +generate_export_header(konq) + +install(FILES + ${CMAKE_CURRENT_BINARY_DIR}/konq_export.h + konq_popupmenu.h # used by folderview + konq_popupmenuplugin.h + konq_dndpopupmenuplugin.h + # konq_copytomenu.h - anyone needs it? + konq_operations.h + konqmimedata.h + kversioncontrolplugin.h + DESTINATION ${KDE4_INCLUDE_INSTALL_DIR} + COMPONENT Devel +) +install(FILES + konqpopupmenuplugin.desktop + konqdndpopupmenuplugin.desktop + DESTINATION ${KDE4_SERVICETYPES_INSTALL_DIR} +) diff --git a/libs/konq/Messages.sh b/libs/konq/Messages.sh new file mode 100644 index 00000000..f4996063 --- /dev/null +++ b/libs/konq/Messages.sh @@ -0,0 +1,2 @@ +#! /usr/bin/env bash +$XGETTEXT *.cpp *.h -o $podir/libkonq.pot diff --git a/libs/konq/SERVICEMENUS b/libs/konq/SERVICEMENUS new file mode 100644 index 00000000..2f303beb --- /dev/null +++ b/libs/konq/SERVICEMENUS @@ -0,0 +1,52 @@ +This file explains how to add an item in the popupmenu (for both +konqueror and kdesktop), without using the file associations. + +Why +=== +One reason for doing this is being able to associate +some action with all files without this action becoming a default handler +(called on left click). +Another is that for text-based programs and tools (e.g. gzip) it's faster +than defining a desktop file for the application, making it hidden, and +associate it with the relevant file types. + +How +=== +Create a file ~/.katana/share/kde4/services/ServiceMenus/something.desktop +and write into it something like (without the comments) : + +[Desktop Entry] +ServiceTypes=KonqPopupMenu/Plugin,text/html,text/plain # use all/all for all entries + # all/allfiles for files only + # and use inode/directory for dirs only + # you can also do things like image/* for all + # image mimetypes +Actions=gzip;mail # those are ';' separated, per the standard ! +X-KDE-Submenu=Menuname # this optional entry allows grouping the + # entries in this servicemenu file into a + # common submenu, in this case "Menuname" +TryExec=gzip # Find if executable exist, if it doesn't exist + # menu entry is not displaying +ExcludeServiceTypes=application/x-zip # This entry is used to avoid to display menu + # when it's a specific servicetype + # for exemple when we use all/allfiles and zip + # them, we don't want to zip a zip file + + +[Desktop Action gzip] # One "Desktop Action " group per Action +Name=GZip this file +Name[fr]=... +Icon=application-x-tarz +Exec=gzip %f + +[Desktop Action mail] +Name=Mail this file +Name[fr]=... +Icon=internet-mail +Exec=kmail --there-is-no-such-option-yet %f + + +See also the "desktop entry standard", which defines more formally the same +concept of actions but for desktop files (e.g. eject on a device desktop file, +etc.) + diff --git a/libs/konq/Templates/CAMERA-Device.desktop b/libs/konq/Templates/CAMERA-Device.desktop new file mode 100644 index 00000000..050ef40c --- /dev/null +++ b/libs/konq/Templates/CAMERA-Device.desktop @@ -0,0 +1,6 @@ +[Desktop Entry] +MountPoint= +Dev= +ReadOnly=false +Type=FSDevice +Icon=camera-photo diff --git a/libs/konq/Templates/CDROM-Device.desktop b/libs/konq/Templates/CDROM-Device.desktop new file mode 100644 index 00000000..2560f52f --- /dev/null +++ b/libs/konq/Templates/CDROM-Device.desktop @@ -0,0 +1,104 @@ +[Desktop Entry] +MountPoint= +Dev= +ReadOnly=false +Type=FSDevice +Icon=media-optical +Actions=Eject; +X-KDE-Priority=TopLevel + +[Desktop Action Eject] +Name=Eject +Name[af]=Uitskiet +Name[ar]=أخرِج +Name[as]=বাহিৰ কৰক +Name[ast]=Espulsar +Name[be]=Выштурхнуць +Name[be@latin]=Vysuń +Name[bg]=Изваждане +Name[bn]=ইজেক্ট +Name[bn_IN]=বহিষ্কার +Name[br]=Stlepel +Name[bs]=Izbaci +Name[ca]=Expulsa +Name[ca@valencia]=Expulsa +Name[cs]=Vysunout +Name[csb]=Wësënie +Name[cy]=Allfwrw +Name[da]=Skub ud +Name[de]=Auswerfen +Name[el]=Εξαγωγή +Name[en_GB]=Eject +Name[eo]=Eligi +Name[es]=Expulsar +Name[et]=Väljasta +Name[eu]=Egotzi +Name[fa]=پس زدن +Name[fi]=Poista asemasta +Name[fr]=Éjecter +Name[fy]=Utsmytknop +Name[ga]=Díchuir +Name[gl]=Expulsar +Name[gu]=બહાર નીકાળો +Name[he]=שליפה +Name[hi]=बाहर करें +Name[hne]=बाहिर करव +Name[hr]=Izbaci +Name[hsb]=Wućisnyć +Name[hu]=Kidobás +Name[ia]=Expelle +Name[id]=Keluarkan +Name[is]=Henda út +Name[it]=Espelli +Name[ja]=イジェクト +Name[ka]=CD-ს ამოღება +Name[kk]=Алып-шығару +Name[km]=ច្រាន​ចេញ +Name[kn]=ಹೊರತಳ್ಳು +Name[ko]=꺼내기 +Name[ku]=Biavêje +Name[lt]=Išmesti +Name[lv]=Izgrūst +Name[mai]=बाहर निकालू +Name[mk]=Извади +Name[ml]=പുറത്തെടുക്കുക +Name[mr]=बाहेर काढा +Name[ms]=Lenting +Name[nb]=Løs ut +Name[nds]=Rutfohren +Name[nl]=Uitwerpen +Name[nn]=Løys ut +Name[oc]=Ejectar +Name[or]=ବାହାର କରନ୍ତୁ +Name[pa]=ਬਾਹਰ ਕੱਢੋ +Name[pl]=Wysuń +Name[pt]=Ejectar +Name[pt_BR]=Ejetar +Name[ro]=Scoate suportul +Name[ru]=Извлечь диск +Name[se]=Bálkes olggos +Name[si]=ඉවත් කරන්න +Name[sk]=Vysunúť +Name[sl]=Izvrzi +Name[sr]=Избаци +Name[sr@ijekavian]=Избаци +Name[sr@ijekavianlatin]=Izbaci +Name[sr@latin]=Izbaci +Name[sv]=Mata ut +Name[ta]=வெளித்தள் +Name[te]=ఎజెక్ట్ +Name[tg]=Баровардан +Name[th]=เอาแผ่นสื่อออก +Name[tr]=Çıkart +Name[ug]=چىقار +Name[uk]=Виштовхнути +Name[uz]=Chiqarish +Name[uz@cyrillic]=Чиқариш +Name[vi]=Đẩy ra +Name[wa]=Fé rexhe +Name[xh]=Khuphela ngaphandle +Name[x-test]=xxEjectxx +Name[zh_CN]=弹出 +Name[zh_TW]=退出 +Icon=media-eject +Exec=kdeeject %v diff --git a/libs/konq/Templates/CDWRITER-Device.desktop b/libs/konq/Templates/CDWRITER-Device.desktop new file mode 100644 index 00000000..21dc0165 --- /dev/null +++ b/libs/konq/Templates/CDWRITER-Device.desktop @@ -0,0 +1,104 @@ +[Desktop Entry] +MountPoint= +Dev= +ReadOnly=false +Type=FSDevice +Icon=media-optical-recordable +Actions=Eject; +X-KDE-Priority=TopLevel + +[Desktop Action Eject] +Name=Eject +Name[af]=Uitskiet +Name[ar]=أخرِج +Name[as]=বাহিৰ কৰক +Name[ast]=Espulsar +Name[be]=Выштурхнуць +Name[be@latin]=Vysuń +Name[bg]=Изваждане +Name[bn]=ইজেক্ট +Name[bn_IN]=বহিষ্কার +Name[br]=Stlepel +Name[bs]=Izbaci +Name[ca]=Expulsa +Name[ca@valencia]=Expulsa +Name[cs]=Vysunout +Name[csb]=Wësënie +Name[cy]=Allfwrw +Name[da]=Skub ud +Name[de]=Auswerfen +Name[el]=Εξαγωγή +Name[en_GB]=Eject +Name[eo]=Eligi +Name[es]=Expulsar +Name[et]=Väljasta +Name[eu]=Egotzi +Name[fa]=پس زدن +Name[fi]=Poista asemasta +Name[fr]=Éjecter +Name[fy]=Utsmytknop +Name[ga]=Díchuir +Name[gl]=Expulsar +Name[gu]=બહાર નીકાળો +Name[he]=שליפה +Name[hi]=बाहर करें +Name[hne]=बाहिर करव +Name[hr]=Izbaci +Name[hsb]=Wućisnyć +Name[hu]=Kidobás +Name[ia]=Expelle +Name[id]=Keluarkan +Name[is]=Henda út +Name[it]=Espelli +Name[ja]=イジェクト +Name[ka]=CD-ს ამოღება +Name[kk]=Алып-шығару +Name[km]=ច្រាន​ចេញ +Name[kn]=ಹೊರತಳ್ಳು +Name[ko]=꺼내기 +Name[ku]=Biavêje +Name[lt]=Išmesti +Name[lv]=Izgrūst +Name[mai]=बाहर निकालू +Name[mk]=Извади +Name[ml]=പുറത്തെടുക്കുക +Name[mr]=बाहेर काढा +Name[ms]=Lenting +Name[nb]=Løs ut +Name[nds]=Rutfohren +Name[nl]=Uitwerpen +Name[nn]=Løys ut +Name[oc]=Ejectar +Name[or]=ବାହାର କରନ୍ତୁ +Name[pa]=ਬਾਹਰ ਕੱਢੋ +Name[pl]=Wysuń +Name[pt]=Ejectar +Name[pt_BR]=Ejetar +Name[ro]=Scoate suportul +Name[ru]=Извлечь диск +Name[se]=Bálkes olggos +Name[si]=ඉවත් කරන්න +Name[sk]=Vysunúť +Name[sl]=Izvrzi +Name[sr]=Избаци +Name[sr@ijekavian]=Избаци +Name[sr@ijekavianlatin]=Izbaci +Name[sr@latin]=Izbaci +Name[sv]=Mata ut +Name[ta]=வெளித்தள் +Name[te]=ఎజెక్ట్ +Name[tg]=Баровардан +Name[th]=เอาแผ่นสื่อออก +Name[tr]=Çıkart +Name[ug]=چىقار +Name[uk]=Виштовхнути +Name[uz]=Chiqarish +Name[uz@cyrillic]=Чиқариш +Name[vi]=Đẩy ra +Name[wa]=Fé rexhe +Name[xh]=Khuphela ngaphandle +Name[x-test]=xxEjectxx +Name[zh_CN]=弹出 +Name[zh_TW]=退出 +Icon=media-eject +Exec=kdeeject %v diff --git a/libs/konq/Templates/CMakeLists.txt b/libs/konq/Templates/CMakeLists.txt new file mode 100644 index 00000000..abf7a259 --- /dev/null +++ b/libs/konq/Templates/CMakeLists.txt @@ -0,0 +1,39 @@ +########### install files ############### + +install( FILES + linkProgram.desktop + linkURL.desktop + linkPath.desktop + linkFloppy.desktop + linkHD.desktop + linkCDROM.desktop + Directory.desktop + TextFile.desktop + HTMLFile.desktop + linkZIP.desktop + linkDVDROM.desktop + linkCAMERA.desktop + linkNFS.desktop + linkCDWRITER.desktop + linkMO.desktop + DESTINATION ${KDE4_TEMPLATES_INSTALL_DIR} +) + + +install( + FILES + Program.desktop + URL.desktop + Floppy.desktop + HD.desktop + CDROM-Device.desktop + TextFile.txt + HTMLFile.html + ZIP-Device.desktop + DVDROM-Device.desktop + CAMERA-Device.desktop + NFS.desktop + CDWRITER-Device.desktop + MO-Device.desktop + DESTINATION ${KDE4_TEMPLATES_INSTALL_DIR}/.source +) diff --git a/libs/konq/Templates/DVDROM-Device.desktop b/libs/konq/Templates/DVDROM-Device.desktop new file mode 100644 index 00000000..2560f52f --- /dev/null +++ b/libs/konq/Templates/DVDROM-Device.desktop @@ -0,0 +1,104 @@ +[Desktop Entry] +MountPoint= +Dev= +ReadOnly=false +Type=FSDevice +Icon=media-optical +Actions=Eject; +X-KDE-Priority=TopLevel + +[Desktop Action Eject] +Name=Eject +Name[af]=Uitskiet +Name[ar]=أخرِج +Name[as]=বাহিৰ কৰক +Name[ast]=Espulsar +Name[be]=Выштурхнуць +Name[be@latin]=Vysuń +Name[bg]=Изваждане +Name[bn]=ইজেক্ট +Name[bn_IN]=বহিষ্কার +Name[br]=Stlepel +Name[bs]=Izbaci +Name[ca]=Expulsa +Name[ca@valencia]=Expulsa +Name[cs]=Vysunout +Name[csb]=Wësënie +Name[cy]=Allfwrw +Name[da]=Skub ud +Name[de]=Auswerfen +Name[el]=Εξαγωγή +Name[en_GB]=Eject +Name[eo]=Eligi +Name[es]=Expulsar +Name[et]=Väljasta +Name[eu]=Egotzi +Name[fa]=پس زدن +Name[fi]=Poista asemasta +Name[fr]=Éjecter +Name[fy]=Utsmytknop +Name[ga]=Díchuir +Name[gl]=Expulsar +Name[gu]=બહાર નીકાળો +Name[he]=שליפה +Name[hi]=बाहर करें +Name[hne]=बाहिर करव +Name[hr]=Izbaci +Name[hsb]=Wućisnyć +Name[hu]=Kidobás +Name[ia]=Expelle +Name[id]=Keluarkan +Name[is]=Henda út +Name[it]=Espelli +Name[ja]=イジェクト +Name[ka]=CD-ს ამოღება +Name[kk]=Алып-шығару +Name[km]=ច្រាន​ចេញ +Name[kn]=ಹೊರತಳ್ಳು +Name[ko]=꺼내기 +Name[ku]=Biavêje +Name[lt]=Išmesti +Name[lv]=Izgrūst +Name[mai]=बाहर निकालू +Name[mk]=Извади +Name[ml]=പുറത്തെടുക്കുക +Name[mr]=बाहेर काढा +Name[ms]=Lenting +Name[nb]=Løs ut +Name[nds]=Rutfohren +Name[nl]=Uitwerpen +Name[nn]=Løys ut +Name[oc]=Ejectar +Name[or]=ବାହାର କରନ୍ତୁ +Name[pa]=ਬਾਹਰ ਕੱਢੋ +Name[pl]=Wysuń +Name[pt]=Ejectar +Name[pt_BR]=Ejetar +Name[ro]=Scoate suportul +Name[ru]=Извлечь диск +Name[se]=Bálkes olggos +Name[si]=ඉවත් කරන්න +Name[sk]=Vysunúť +Name[sl]=Izvrzi +Name[sr]=Избаци +Name[sr@ijekavian]=Избаци +Name[sr@ijekavianlatin]=Izbaci +Name[sr@latin]=Izbaci +Name[sv]=Mata ut +Name[ta]=வெளித்தள் +Name[te]=ఎజెక్ట్ +Name[tg]=Баровардан +Name[th]=เอาแผ่นสื่อออก +Name[tr]=Çıkart +Name[ug]=چىقار +Name[uk]=Виштовхнути +Name[uz]=Chiqarish +Name[uz@cyrillic]=Чиқариш +Name[vi]=Đẩy ra +Name[wa]=Fé rexhe +Name[xh]=Khuphela ngaphandle +Name[x-test]=xxEjectxx +Name[zh_CN]=弹出 +Name[zh_TW]=退出 +Icon=media-eject +Exec=kdeeject %v diff --git a/libs/konq/Templates/Directory.desktop b/libs/konq/Templates/Directory.desktop new file mode 100644 index 00000000..c03df728 --- /dev/null +++ b/libs/konq/Templates/Directory.desktop @@ -0,0 +1,183 @@ +[Desktop Entry] +Name=Folder... +Name[af]=Gids... +Name[ar]=مجلد... +Name[as]=ফোল্ডাৰ... +Name[ast]=Carpeta... +Name[be]=Тэчка... +Name[be@latin]=Kataloh... +Name[bg]=Папка... +Name[bn]=ফোল্ডার... +Name[bn_IN]=ফোল্ডার... +Name[br]=Renkell ... +Name[bs]=Fascikla... +Name[ca]=Carpeta... +Name[ca@valencia]=Carpeta... +Name[cs]=Složka... +Name[csb]=Katalog... +Name[cy]=Plygell... +Name[da]=Mappe... +Name[de]=Ordner ... +Name[el]=Φάκελος... +Name[en_GB]=Folder... +Name[eo]=Dosierujo... +Name[es]=Carpeta... +Name[et]=Kataloog... +Name[eu]=Karpeta... +Name[fi]=Kansio… +Name[fr]=Dossier... +Name[fy]=Map... +Name[ga]=Fillteán... +Name[gl]=Cartafol... +Name[gu]=ફોલ્ડર... +Name[he]=תיקייה... +Name[hi]=फ़ोल्डर... +Name[hne]=फोल्डर... +Name[hr]=Mapa… +Name[hsb]=Zapisk... +Name[hu]=Könyvtár… +Name[ia]=Dossier... +Name[id]=Folder... +Name[is]=Mappa... +Name[it]=Cartella... +Name[ja]=フォルダ... +Name[ka]=საქაღალდე... +Name[kk]=Қапшық... +Name[km]=ថត... +Name[kn]=ಕಡತಕೋಶ +Name[ko]=폴더... +Name[ku]=Peldank... +Name[lt]=Aplankas... +Name[lv]=Mape... +Name[mai]=फोल्डर... +Name[mk]=Папка... +Name[ml]=കൂട... +Name[mr]=संचयीका... +Name[ms]=Folder... +Name[nb]=Mappe … +Name[nds]=Orner... +Name[nl]=Map... +Name[nn]=Mappe … +Name[oc]=Repertòri... +Name[or]=ଫୋଲଡ଼ର... +Name[pa]=ਫੋਲਡਰ... +Name[pl]=Katalog... +Name[pt]=Pasta... +Name[pt_BR]=Pasta... +Name[ro]=Dosar... +Name[ru]=Папку... +Name[se]=Máhppa … +Name[si]=බහලුම... +Name[sk]=Priečinok... +Name[sl]=Mapa ... +Name[sr]=Фасцикла... +Name[sr@ijekavian]=Фасцикла... +Name[sr@ijekavianlatin]=Fascikla... +Name[sr@latin]=Fascikla... +Name[sv]=Katalog... +Name[ta]=அடைவு... +Name[te]=ఫొల్డర్... +Name[tg]=Феҳрист... +Name[th]=โฟลเดอร์... +Name[tr]=Dizin... +Name[ug]=قىسقۇچ… +Name[uk]=Теку... +Name[uz]=Jild +Name[uz@cyrillic]=Жилд +Name[vi]=Thư mục... +Name[wa]=Ridant... +Name[x-test]=xxFolder...xx +Name[zh_CN]=文件夹... +Name[zh_TW]=資料夾... +Comment=Enter folder name: +Comment[af]=Voer gidsnaam in: +Comment[ar]=أدخل اسم المجلد: +Comment[as]=ফোল্ডাৰৰ নাম দিয়ক: +Comment[ast]=Introducir nome de la carpeta: +Comment[be]=Вызначце назву тэчкі: +Comment[be@latin]=Upišy nazvu kataloha: +Comment[bg]=Въведете име на папката: +Comment[bn]=ফোল্ডারের নাম লিখুন: +Comment[bn_IN]=ফোল্ডারের নাম লিখুন: +Comment[br]=Roit un anv ar renkell : +Comment[bs]=Unesite ime fascikle: +Comment[ca]=Introduïu el nom de la carpeta: +Comment[ca@valencia]=Introduïu el nom de la carpeta: +Comment[cs]=Zadejte název složky: +Comment[csb]=Wpiszë miono kataloga: +Comment[cy]=Mewnosodwch enw'r plygell: +Comment[da]=Indtast mappenavn: +Comment[de]=Geben Sie den Ordnernamen ein: +Comment[el]=Εισάγετε το όνομα φακέλου: +Comment[en_GB]=Enter folder name: +Comment[eo]=Enmeti la dosierujan nomon: +Comment[es]=Introducir nombre de la carpeta: +Comment[et]=Sisesta kataloogi nimi: +Comment[eu]=Sartu karpetaren izena: +Comment[fi]=Anna kansion nimi: +Comment[fr]=Saisir le nom du dossier : +Comment[fy]=Mapname ynfiere: +Comment[ga]=Iontráil ainm an fhillteáin: +Comment[gl]=Indique o nome do cartafol: +Comment[gu]=ફોલ્ડર નામ દાખલ કરો: +Comment[he]=יש להכניס שם לתיקייה: +Comment[hi]=फ़ोल्डर नाम भरें: +Comment[hne]=फोल्डर नाम भरव: +Comment[hr]=Unesite naziv mape: +Comment[hsb]=Mjeno zapiska zapodać: +Comment[hu]=A könyvtár neve: +Comment[ia]=Inserta un nomine de dossier: +Comment[id]=Masukkan nama folder: +Comment[is]=Sláðu inn möppunafn: +Comment[it]=Inserisci il nome della cartella: +Comment[ja]=フォルダ名を入力: +Comment[ka]=შეიყვანეთ საქაღალდის სახელი: +Comment[kk]=Қапшықтың атауы: +Comment[km]=បញ្ចូលឈ្មោះ​ថត ៖ +Comment[kn]=ಕಡತಕೋಶದ ಹೆಸರನ್ನು ನಮೂದಿಸು: +Comment[ko]=폴더 이름을 입력하십시오: +Comment[ku]=Navê peldankê binivîse: +Comment[lt]=Įveskite aplanko vardą: +Comment[lv]=Ievadiet mapes nosaukumu: +Comment[mai]=फोल्डर नाम भरू: +Comment[mk]=Внесете го името на папката: +Comment[ml]=അറയുടെ പേരു്: +Comment[mr]=संचयीका नाव दाखल करा: +Comment[ms]=Masukkan nama folder: +Comment[nb]=Oppgi mappenavn: +Comment[nds]=Ornernaam ingeven: +Comment[nl]=Mapnaam invoeren: +Comment[nn]=Skriv inn mappenamn: +Comment[or]=ଫୋଲଡ଼ର ନାମ ଭରଣ କରନ୍ତୁ: +Comment[pa]=ਫੋਲਡਰ ਨਾਂ ਦਿਓ: +Comment[pl]=Podaj nazwę katalogu: +Comment[pt]=Indique o nome da pasta: +Comment[pt_BR]=Informe o nome da pasta: +Comment[ro]=Introduceți numele dosarului: +Comment[ru]=Введите имя папки: +Comment[se]=Čális máhppanama: +Comment[si]=බහලුම් නාමය ඇතුළත් කරන්න: +Comment[sk]=Zadajte názov priečinku: +Comment[sl]=Vnesite ime mape: +Comment[sr]=Унесите име фасцикле: +Comment[sr@ijekavian]=Унесите име фасцикле: +Comment[sr@ijekavianlatin]=Unesite ime fascikle: +Comment[sr@latin]=Unesite ime fascikle: +Comment[sv]=Ange katalognamn: +Comment[ta]=அடைவின் பெயரை உள்ளிடு: +Comment[te]=ఫొల్డర్ పేరును వ్రాయండి: +Comment[tg]=Вориди номи феҳрист: +Comment[th]=ป้อนชื่อโฟลเดอร์: +Comment[tr]=Dizin adını girin: +Comment[ug]=قىسقۇچ ئاتىنى كىرگۈزۈڭ: +Comment[uk]=Введіть назву теки: +Comment[uz]=Jildning nomini kiriting: +Comment[uz@cyrillic]=Жилднинг номини киритинг: +Comment[vi]=Điền tên thư mục: +Comment[wa]=Intrez on no d' ridant: +Comment[x-test]=xxEnter folder name:xx +Comment[zh_CN]=输入文件夹名称: +Comment[zh_TW]=輸入資料夾名稱: +Type=Link +URL=.source/emptydir +Icon=folder diff --git a/libs/konq/Templates/Floppy.desktop b/libs/konq/Templates/Floppy.desktop new file mode 100644 index 00000000..d9627577 --- /dev/null +++ b/libs/konq/Templates/Floppy.desktop @@ -0,0 +1,79 @@ +[Desktop Entry] +Actions=Format; +MountPoint= +Dev= +ReadOnly=false +Type=FSDevice +Icon=media-floppy +X-KDE-Priority=TopLevel + +[Desktop Action Format] +# ctxt: Starts a program to format a floppy drive +Name=Format +Name[ar]=هيِّئ +Name[ast]=Formatu +Name[bg]=Форматиране +Name[bn]=ফরম্যাট +Name[bs]=Formatiranje +Name[ca]=Formata +Name[ca@valencia]=Formata +Name[cs]=Formát +Name[da]=Formatér +Name[de]=Formatieren +Name[el]=Μορφή +Name[en_GB]=Format +Name[es]=Formato +Name[et]=Vormindamine +Name[eu]=Formatua +Name[fi]=Alusta +Name[fr]=Format +Name[ga]=Formáidigh +Name[gl]=Formatar +Name[gu]=બંધારણ +Name[he]=פירמוט +Name[hi]=फॉर्मेट +Name[hr]=Formatiranje +Name[hu]=Formátum +Name[ia]=Formato +Name[id]=Format +Name[is]=Forsníða +Name[it]=Formatta +Name[ja]=フォーマット +Name[ka]=ფორმატი +Name[kk]=Пішімдеу +Name[km]=ទ្រង់ទ្រាយ +Name[kn]=ವಿನ್ಯಾಸ +Name[ko]=포맷 +Name[lt]=Formatas +Name[lv]=Formatēt +Name[mai]=प्रारूप +Name[ml]=ഫോര്‍മാറ്റ് +Name[mr]=स्वरूप +Name[nb]=Format +Name[nds]=Formateren +Name[nl]=Formatteren +Name[nn]=Formater +Name[pa]=ਫਾਰਮੈਟ +Name[pl]=Format +Name[pt]=Formatar +Name[pt_BR]=Formatar +Name[ro]=Formatare +Name[ru]=Форматировать +Name[si]=හැඩතලය +Name[sk]=Formát +Name[sl]=Formatiraj +Name[sr]=Форматирање +Name[sr@ijekavian]=Форматирање +Name[sr@ijekavianlatin]=Formatiranje +Name[sr@latin]=Formatiranje +Name[sv]=Formatera +Name[tg]=Формат +Name[th]=รูปแบบ +Name[tr]=Biçim +Name[ug]=پىچىمى +Name[uk]=Форматувати +Name[wa]=Abwesner +Name[x-test]=xxFormatxx +Name[zh_CN]=格式化 +Name[zh_TW]=格式 +Exec=kfloppy %v diff --git a/libs/konq/Templates/HD.desktop b/libs/konq/Templates/HD.desktop new file mode 100644 index 00000000..d846f0b2 --- /dev/null +++ b/libs/konq/Templates/HD.desktop @@ -0,0 +1,6 @@ +[Desktop Entry] +MountPoint= +Dev= +ReadOnly=false +Type=FSDevice +Icon=drive-harddisk diff --git a/libs/konq/Templates/HTMLFile.desktop b/libs/konq/Templates/HTMLFile.desktop new file mode 100644 index 00000000..e65b0d36 --- /dev/null +++ b/libs/konq/Templates/HTMLFile.desktop @@ -0,0 +1,182 @@ +[Desktop Entry] +Name=HTML File... +Name[af]=HTML Lêer... +Name[ar]=ملف HTML ... +Name[as]=HTML নথিপত্ৰ... +Name[ast]=Ficheru HTML... +Name[be]=Файл HTML... +Name[be@latin]=Fajł HTML... +Name[bg]=HTML файл... +Name[bn]=HTML ফাইল... +Name[bn_IN]=HTML ফাইল... +Name[br]=Restr HTML ... +Name[bs]=HTML datoteka... +Name[ca]=Fitxer HTML... +Name[ca@valencia]=Fitxer HTML... +Name[cs]=HTML soubor... +Name[csb]=Lopk HTML ... +Name[cy]=Ffeil HTML... +Name[da]=HTML-fil... +Name[de]=HTML-Datei ... +Name[el]=Αρχείο HTML... +Name[en_GB]=HTML File... +Name[eo]=HTML-dosiero... +Name[es]=Archivo HTML... +Name[et]=HTML-fail... +Name[eu]=HTML fitxategia... +Name[fi]=HTML-tiedosto… +Name[fr]=Fichier HTML... +Name[fy]=HTML-triem....... +Name[ga]=Comhad HTML... +Name[gl]=Ficheiro HTML... +Name[gu]=HTML ફાઇલ... +Name[he]=קובץ HTML... +Name[hi]=एचटीएमएल फ़ाइल... +Name[hne]=एचटीएमएल फाइल... +Name[hr]=HTML datoteka… +Name[hsb]=HTML-dataja... +Name[hu]=HTML-fájl… +Name[ia]=File HTML ... +Name[id]=Berkas HTML... +Name[is]=HTML skrá... +Name[it]=File HTML... +Name[ja]=HTML ファイル... +Name[ka]=HTML ფაილი... +Name[kk]=HTML файлы... +Name[km]=ឯកសារ HTML... +Name[kn]=HTML ಕಡತ... +Name[ko]=HTML 파일 +Name[ku]=Pelê HTML... +Name[lt]=HTML failas... +Name[lv]=HTML fails... +Name[mai]=HTML फाइल... +Name[mk]=HTML датотека... +Name[ml]=എച്ച്ടിഎംഎല്‍ ഫയല്‍... +Name[mr]=HTML फाईल... +Name[ms]=Fail HTML... +Name[nb]=HTML-fil … +Name[nds]=HTML-Datei... +Name[nl]=HTML-bestand... +Name[nn]=HTML-fil … +Name[oc]=Fichièr HTML +Name[or]=HTML ଫାଇଲ... +Name[pa]=HTML ਫਾਇਲ... +Name[pl]=Plik HTML... +Name[pt]=Ficheiro HTML... +Name[pt_BR]=Arquivo HTML... +Name[ro]=Fișier HTML... +Name[ru]=Страница HTML... +Name[se]=HTML-fiila … +Name[si]=HTML ගොනුව... +Name[sk]=Súbor HTML... +Name[sl]=Datoteka HTML ... +Name[sr]=ХТМЛ фајл... +Name[sr@ijekavian]=ХТМЛ фајл... +Name[sr@ijekavianlatin]=HTML fajl... +Name[sr@latin]=HTML fajl... +Name[sv]=HTML-fil... +Name[ta]=HTML கோப்பு... +Name[te]=హెచ్ టి ఎం ఎల్ దస్త్రం... +Name[tg]=Файлҳои HTML... +Name[th]=แฟ้ม HTML... +Name[tr]=HTML Dosyası... +Name[ug]=HTML ھۆججەت… +Name[uk]=Файл HTML... +Name[uz]=HTML-fayli... +Name[uz@cyrillic]=HTML-файли... +Name[vi]=Tập tin HTML... +Name[wa]=Fitchî HTML... +Name[x-test]=xxHTML File...xx +Name[zh_CN]=HTML 文件... +Name[zh_TW]=HTML 檔案... +Comment=Enter HTML filename: +Comment[af]=Voer HTML lêernaam in: +Comment[ar]=أدخل اسم ملف HTML: +Comment[as]=HTML নথিপত্ৰৰনাম দিয়ক: +Comment[ast]=Introduz el nome del ficheru HTML: +Comment[be]=Вызначце назву файла HTML: +Comment[be@latin]=Upišy nazvu fajła HTML: +Comment[bg]=Въведете името на HTML файла: +Comment[bn]=HTML ফাইলের নাম: +Comment[bn_IN]=HTML ফাইলের নাম লিখুন: +Comment[br]=Roit un anv ar restr : +Comment[bs]=Unesite ime HTML datoteke: +Comment[ca]=Introduïu el nom del fitxer HTML: +Comment[ca@valencia]=Introduïu el nom del fitxer HTML: +Comment[cs]=Zadejte název HTML souboru... +Comment[csb]=Wpiszë miono lopka HTML: +Comment[da]=Indtast HTML-filnavn: +Comment[de]=Geben Sie den HTML-Dateinamen ein: +Comment[el]=Δώστε το όνομα του αρχείου HTML: +Comment[en_GB]=Enter HTML filename: +Comment[eo]=Enmeti la HTML-dosiernomon: +Comment[es]=Introduzca el nombre del archivo HTML: +Comment[et]=Sisesta HTML-faili nimi: +Comment[eu]=Sartu HTML fitxategi-izena: +Comment[fi]=Anna HTML-tiedoston nimi: +Comment[fr]=Saisir le nom du fichier HTML : +Comment[fy]=Namme fan HTML-triem ynfiere: +Comment[ga]=Iontráil ainm an chomhaid HTML: +Comment[gl]=Indique o nome do ficheiro HTML: +Comment[gu]=HTML ફાઇલનામ દાખલ કરો: +Comment[he]=יש להכניס שם לקובץ ה־HTML: +Comment[hi]=एचटीएमएल फ़ाइल-नाम भरें: +Comment[hne]=एचटीएमएल फाइलनाम भरव: +Comment[hr]=Unesite naziv HTML datoteke: +Comment[hsb]=Mjeno HTML-dataje zapodać: +Comment[hu]=A HTML-fájl neve: +Comment[ia]=Inserta un nomine de file HTML: +Comment[id]=Masukkan nama berkas HTML: +Comment[is]=Sláðu inn HTML skráarnafn: +Comment[it]=Inserisci il nome del file HTML: +Comment[ja]=HTML ファイルの名前を入力: +Comment[ka]=HTML ფაილის სახელი შეიყვანეთ: +Comment[kk]=HTML файлының атауы: +Comment[km]=បញ្ចូល​ឈ្មោះ​ឯកសារ HTML ៖ +Comment[kn]=HTML ಕಡತದ ಹೆಸರನ್ನು ನಮೂದಿಸು: +Comment[ko]=HTML 파일 이름을 입력하십시오: +Comment[ku]=Navê pelê HTML binivîse: +Comment[lt]=Įrašykite HTML failo pavadinimą: +Comment[lv]=Ievadiet HTML faila nosaukumu: +Comment[mai]=HTML फाइलनाम दिअ': +Comment[mk]=Внесете го името на HTML датотеката: +Comment[ml]=എച്ച്‌ടിഎംഎല്‍ ഫയലിന്റെ പേരു്: +Comment[mr]=HTML फाईलनाव दाखल करा: +Comment[ms]=Masukkan nama fail HTML: +Comment[nb]=Oppgi navn på HTML-fila: +Comment[nds]=Naam vun de HTML-Datei ingeven: +Comment[nl]=Naam HTML-bestand invoeren: +Comment[nn]=Skriv inn namn på HTML-fil: +Comment[or]=HTML ପାଇଲ ନାମ ଭରଣ କରନ୍ତୁ: +Comment[pa]=HTML ਫਾਇਲ ਨਾਂ ਦਿਓ: +Comment[pl]=Podaj nazwę pliku HTML: +Comment[pt]=Indique o nome do ficheiro HTML: +Comment[pt_BR]=Informe o nome do arquivo HTML: +Comment[ro]=Introduceți numele fișierului HTML: +Comment[ru]=Введите имя страницы HTML: +Comment[se]=Čális HTML-fiilla nama: +Comment[si]=HTML ගොනු නාමය ඇතුළත් කරන්න: +Comment[sk]=Zadajte názov súboru HTML: +Comment[sl]=Vnesite ime datoteke HTML: +Comment[sr]=Унесите име ХТМЛ фајла: +Comment[sr@ijekavian]=Унесите име ХТМЛ фајла: +Comment[sr@ijekavianlatin]=Unesite ime HTML fajla: +Comment[sr@latin]=Unesite ime HTML fajla: +Comment[sv]=Ange HTML-filnamn: +Comment[ta]=HTML கோப்பு பெயரை உள்ளிடு: +Comment[te]=హెచ్ టి ఎం ఎల్ దస్త్రం పేరును వ్రాయండి: +Comment[tg]=Вориди номи файли HTML: +Comment[th]=ป้อนชื่อแฟ้ม HTML: +Comment[tr]=HTML dosyasının adını girin: +Comment[ug]=HTML ھۆججەت ئاتىنى كىرگۈزۈڭ: +Comment[uk]=Введіть назву файла HTML: +Comment[uz]=HTML-faylning nomini kiriting: +Comment[uz@cyrillic]=HTML-файлнинг номини киритинг: +Comment[vi]=Điền tên tập tin HTML: +Comment[wa]=Intrez on no d' fitchî HTML: +Comment[x-test]=xxEnter HTML filename:xx +Comment[zh_CN]=输入 HTML 文件名称: +Comment[zh_TW]=輸入 HTML 檔名: +Type=Link +URL=.source/HTMLFile.html +Icon=text-html diff --git a/libs/konq/Templates/HTMLFile.html b/libs/konq/Templates/HTMLFile.html new file mode 100644 index 00000000..c217ab3f --- /dev/null +++ b/libs/konq/Templates/HTMLFile.html @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/libs/konq/Templates/MO-Device.desktop b/libs/konq/Templates/MO-Device.desktop new file mode 100644 index 00000000..d0b18b0e --- /dev/null +++ b/libs/konq/Templates/MO-Device.desktop @@ -0,0 +1,6 @@ +[Desktop Entry] +MountPoint= +Dev= +ReadOnly=false +Type=FSDevice +Icon=media-floppy diff --git a/libs/konq/Templates/NFS.desktop b/libs/konq/Templates/NFS.desktop new file mode 100644 index 00000000..a8ea7f0d --- /dev/null +++ b/libs/konq/Templates/NFS.desktop @@ -0,0 +1,6 @@ +[Desktop Entry] +MountPoint= +Dev= +ReadOnly=false +Type=FSDevice +Icon=folder-remote diff --git a/libs/konq/Templates/Program.desktop b/libs/konq/Templates/Program.desktop new file mode 100644 index 00000000..8fe90a28 --- /dev/null +++ b/libs/konq/Templates/Program.desktop @@ -0,0 +1,6 @@ +#!/usr/bin/env xdg-open +[Desktop Entry] +Exec= +Icon=exec +Terminal=false +Type=Application diff --git a/libs/konq/Templates/TextFile.desktop b/libs/konq/Templates/TextFile.desktop new file mode 100644 index 00000000..76bb1ce1 --- /dev/null +++ b/libs/konq/Templates/TextFile.desktop @@ -0,0 +1,182 @@ +[Desktop Entry] +Name=Text File... +Name[af]=Teks Lêer... +Name[ar]=ملف نصي... +Name[as]=টেক্সট নথিপত্ৰ... +Name[ast]=Ficheru de testu... +Name[be]=Тэкставы файл... +Name[be@latin]=Tekstavy fajł... +Name[bg]=Текстов файл... +Name[bn]=টেক্সট ফাইল... +Name[bn_IN]=টেক্সট ফাইল... +Name[br]=Restr skrid ... +Name[bs]=Tekstualna datoteka... +Name[ca]=Fitxer de text... +Name[ca@valencia]=Fitxer de text... +Name[cs]=Textový soubor... +Name[csb]=Tekstowi lopk... +Name[cy]=Ffeil Testun... +Name[da]=Tekstfil... +Name[de]=Textdatei ... +Name[el]=Αρχείο κειμένου... +Name[en_GB]=Text File... +Name[eo]=Tekstdosiero... +Name[es]=Archivo de texto... +Name[et]=Tekstifail... +Name[eu]=Testu fitxategia... +Name[fi]=Tekstitiedosto… +Name[fr]=Fichier texte... +Name[fy]=Teksttriem...... +Name[ga]=Téacschomhad... +Name[gl]=Ficheiro de texto... +Name[gu]=લખાણ ફાઇલ... +Name[he]=קובץ טקסט... +Name[hi]=पाठ फ़ाइल... +Name[hne]=पाठ फाइल... +Name[hr]=Tekstualna datoteka… +Name[hsb]=Tekstowa dataja ... +Name[hu]=Szövegfájl… +Name[ia]=File de texto... +Name[id]=Berkas Teks... +Name[is]=Textaskrá... +Name[it]=File di testo... +Name[ja]=テキストファイル... +Name[ka]=ტექსტური ფაილი... +Name[kk]=Мәтін файлы... +Name[km]=ឯកសារ​អត្ថបទ... +Name[kn]=ಪಠ್ಯ ಕಡತ... +Name[ko]=텍스트 파일... +Name[ku]=Pelê Nivîsar... +Name[lt]=Teksto failas... +Name[lv]=Teksta fails... +Name[mai]=पाठ फाइल... +Name[mk]=Текстуална датотека... +Name[ml]=പദാവലി ഫയല്‍... +Name[mr]=पाठ्य फाईल... +Name[ms]=Fail Teks... +Name[nb]=Tekstfil … +Name[nds]=Textdatei... +Name[nl]=Tekstbestand... +Name[nn]=Tekstfil … +Name[oc]=Fichièr tèxt... +Name[or]=ପାଠ୍ୟ ଫାଇଲ... +Name[pa]=ਟੈਕਸਟ ਫਾਇਲ... +Name[pl]=Plik tekstowy... +Name[pt]=Ficheiro de Texto... +Name[pt_BR]=Arquivo de texto... +Name[ro]=Fișier text... +Name[ru]=Текстовый файл... +Name[se]=Teakstafiila … +Name[si]=පෙළ ගොනුව... +Name[sk]=Textový súbor... +Name[sl]=Besedilna datoteka ... +Name[sr]=Текстуални фајл... +Name[sr@ijekavian]=Текстуални фајл... +Name[sr@ijekavianlatin]=Tekstualni fajl... +Name[sr@latin]=Tekstualni fajl... +Name[sv]=Textfil... +Name[ta]=உரைக் கோப்பு +Name[te]=వచన దస్త్రం... +Name[tg]=Файли матнӣ... +Name[th]=แฟ้มข้อความ... +Name[tr]=Metin Dosyası... +Name[ug]=تېكىست ھۆججىتى… +Name[uk]=Текстовий файл... +Name[uz]=Matn fayli... +Name[uz@cyrillic]=Матн файли... +Name[vi]=Tập tin văn bản... +Name[wa]=Fitchî tecse... +Name[x-test]=xxText File...xx +Name[zh_CN]=文本文件... +Name[zh_TW]=文字檔案... +Comment=Enter text filename: +Comment[af]=Voer teks lêernaam in: +Comment[ar]=أدخل اسم الملف النصي : +Comment[as]=টেক্সট নথিপত্ৰৰ নাম দিয়ক: +Comment[ast]=Introduz el nome del ficheru de testu: +Comment[be]=Вызначце назву тэкставага файла: +Comment[be@latin]=Upišy nazvu tekstavaha fajła: +Comment[bg]=Въведете името на файла: +Comment[bn]=টেক্সট ফাইলের নাম: +Comment[bn_IN]=টেক্সট ফাইলের নাম লিখুন: +Comment[br]=Roit un anv ar restr skrid : +Comment[bs]=Unesite ime tekstualne datoteke: +Comment[ca]=Introduïu el nom del fitxer de text: +Comment[ca@valencia]=Introduïu el nom del fitxer de text: +Comment[cs]=Zadejte název textového souboru... +Comment[csb]=Wpiszë miono tekstowégò lopka: +Comment[da]=Indtast tekstfilnavn: +Comment[de]=Geben Sie den Textdatei-Namen ein: +Comment[el]=Δώστε το όνομα του αρχείου κειμένου: +Comment[en_GB]=Enter text filename: +Comment[eo]=Enmeti la tekstodosieran nomon: +Comment[es]=Introduzca el nombre del archivo de texto: +Comment[et]=Sisesta tekstifaili nimi: +Comment[eu]=Sartu testu fitxategiaren izena: +Comment[fi]=Anna tekstitiedoston nimi: +Comment[fr]=Saisir le nom du fichier texte : +Comment[fy]=Namme fan teksttriem ynfiere: +Comment[ga]=Iontráil ainm an téacschomhaid: +Comment[gl]=Indique o nome do ficheiro de texto: +Comment[gu]=લખાણ ફાઇલનામ દાખલ કરો: +Comment[he]=יש להכניס שם לקובץ הטקסט: +Comment[hi]=पाठ फ़ाइल-नाम भरें: +Comment[hne]=पाठ फाइलनाम भरव: +Comment[hr]=Upišite naziv tekstualne datoteke: +Comment[hsb]=Mjeno tekstoweje dataje zapodać: +Comment[hu]=A szövegfájl neve: +Comment[ia]=Inserta le nomine de file: +Comment[id]=Masukkan nama berkas teks: +Comment[is]=Sláðu inn nafn á textaskrá: +Comment[it]=Inserisci il nome del file di testo: +Comment[ja]=テキストファイルの名前を入力: +Comment[ka]=შეიყვანეთ ტექსტური ფაილის სახელი: +Comment[kk]=Мәтін файлының атауы: +Comment[km]=បញ្ចូល​ឈ្មោះ​ឯកសារ​អត្ថបទ ៖ +Comment[kn]=ಪಠ್ಯ ಕಡತದ ಹೆಸರನ್ನು ನಮೂದಿಸು: +Comment[ko]=텍스트 파일 이름을 입력하십시오: +Comment[ku]=Navê pelê tekst binivîse: +Comment[lt]=Įrašykite teksto failo pavadinimą: +Comment[lv]=Ievadiet teksta faila nosaukumu: +Comment[mai]=पाठ फाइल नाम भरू: +Comment[mk]=Внесете го името на текстуалната датотека: +Comment[ml]=രചനയുടെ പേരു്: +Comment[mr]=पाठ्य फाईलनाव दाखल करा: +Comment[ms]=Masukkan nama fail teks: +Comment[nb]=Oppgi navn på tekstfila: +Comment[nds]=Naam vun de Textdatei ingeven: +Comment[nl]=Naam tekstbestand invoeren: +Comment[nn]=Skriv inn namn på tekstfil: +Comment[or]=ପାଠ୍ୟ ଫାଇଲ ନାମ ଭରଣ କରନ୍ତୁ: +Comment[pa]=ਟੈਕਸਟ ਫਾਇਲ-ਨਾਂ ਦਿਓ: +Comment[pl]=Podaj nazwę pliku tekstowego: +Comment[pt]=Indique o nome do ficheiro de texto: +Comment[pt_BR]=Informe o nome do arquivo texto: +Comment[ro]=Introduceți numele fișierului text: +Comment[ru]=Введите имя текстового файла: +Comment[se]=Čális teakstafiilla nama: +Comment[si]=පෙළ ගොනු නාමය ඇතුළත් කරන්න: +Comment[sk]=Zadajte názov textového súboru: +Comment[sl]=Vnesite ime besedilne datoteke: +Comment[sr]=Унесите име текстуалног фајла: +Comment[sr@ijekavian]=Унесите име текстуалног фајла: +Comment[sr@ijekavianlatin]=Unesite ime tekstualnog fajla: +Comment[sr@latin]=Unesite ime tekstualnog fajla: +Comment[sv]=Ange textfilnamn: +Comment[ta]=உரை கோப்பு பெயரை உள்ளிடு: +Comment[te]=వచన దస్త్రం పేరును వ్రాయండి: +Comment[tg]=Номи файли матниро ворид кунед: +Comment[th]=ป้อนชื่อแฟ้มข้อความ: +Comment[tr]=Metin dosyasının adını girin: +Comment[ug]=تېكىست ھۆججەتنىڭ ئاتىنى كىرگۈزۈڭ: +Comment[uk]=Введіть назву текстового файла: +Comment[uz]=Matn faylining nomini kiriting: +Comment[uz@cyrillic]=Матн файлининг номини киритинг: +Comment[vi]=Điền tên tập tin văn bản: +Comment[wa]=Intrez on no d' fitchî tecse: +Comment[x-test]=xxEnter text filename:xx +Comment[zh_CN]=输入文本文件名称: +Comment[zh_TW]=輸入文字檔名: +Type=Link +URL=.source/TextFile.txt +Icon=text-plain diff --git a/libs/konq/Templates/TextFile.txt b/libs/konq/Templates/TextFile.txt new file mode 100644 index 00000000..8d1c8b69 --- /dev/null +++ b/libs/konq/Templates/TextFile.txt @@ -0,0 +1 @@ + diff --git a/libs/konq/Templates/URL.desktop b/libs/konq/Templates/URL.desktop new file mode 100644 index 00000000..968859c3 --- /dev/null +++ b/libs/konq/Templates/URL.desktop @@ -0,0 +1,3 @@ +[Desktop Entry] +URL= +Type=Link diff --git a/libs/konq/Templates/ZIP-Device.desktop b/libs/konq/Templates/ZIP-Device.desktop new file mode 100644 index 00000000..5cccc0c9 --- /dev/null +++ b/libs/konq/Templates/ZIP-Device.desktop @@ -0,0 +1,104 @@ +[Desktop Entry] +MountPoint= +Dev= +ReadOnly=false +Type=FSDevice +Icon=media-floppy +Actions=Eject; +X-KDE-Priority=TopLevel + +[Desktop Action Eject] +Name=Eject +Name[af]=Uitskiet +Name[ar]=أخرِج +Name[as]=বাহিৰ কৰক +Name[ast]=Espulsar +Name[be]=Выштурхнуць +Name[be@latin]=Vysuń +Name[bg]=Изваждане +Name[bn]=ইজেক্ট +Name[bn_IN]=বহিষ্কার +Name[br]=Stlepel +Name[bs]=Izbaci +Name[ca]=Expulsa +Name[ca@valencia]=Expulsa +Name[cs]=Vysunout +Name[csb]=Wësënie +Name[cy]=Allfwrw +Name[da]=Skub ud +Name[de]=Auswerfen +Name[el]=Εξαγωγή +Name[en_GB]=Eject +Name[eo]=Eligi +Name[es]=Expulsar +Name[et]=Väljasta +Name[eu]=Egotzi +Name[fa]=پس زدن +Name[fi]=Poista asemasta +Name[fr]=Éjecter +Name[fy]=Utsmytknop +Name[ga]=Díchuir +Name[gl]=Expulsar +Name[gu]=બહાર નીકાળો +Name[he]=שליפה +Name[hi]=बाहर करें +Name[hne]=बाहिर करव +Name[hr]=Izbaci +Name[hsb]=Wućisnyć +Name[hu]=Kidobás +Name[ia]=Expelle +Name[id]=Keluarkan +Name[is]=Henda út +Name[it]=Espelli +Name[ja]=イジェクト +Name[ka]=CD-ს ამოღება +Name[kk]=Алып-шығару +Name[km]=ច្រាន​ចេញ +Name[kn]=ಹೊರತಳ್ಳು +Name[ko]=꺼내기 +Name[ku]=Biavêje +Name[lt]=Išmesti +Name[lv]=Izgrūst +Name[mai]=बाहर निकालू +Name[mk]=Извади +Name[ml]=പുറത്തെടുക്കുക +Name[mr]=बाहेर काढा +Name[ms]=Lenting +Name[nb]=Løs ut +Name[nds]=Rutfohren +Name[nl]=Uitwerpen +Name[nn]=Løys ut +Name[oc]=Ejectar +Name[or]=ବାହାର କରନ୍ତୁ +Name[pa]=ਬਾਹਰ ਕੱਢੋ +Name[pl]=Wysuń +Name[pt]=Ejectar +Name[pt_BR]=Ejetar +Name[ro]=Scoate suportul +Name[ru]=Извлечь диск +Name[se]=Bálkes olggos +Name[si]=ඉවත් කරන්න +Name[sk]=Vysunúť +Name[sl]=Izvrzi +Name[sr]=Избаци +Name[sr@ijekavian]=Избаци +Name[sr@ijekavianlatin]=Izbaci +Name[sr@latin]=Izbaci +Name[sv]=Mata ut +Name[ta]=வெளித்தள் +Name[te]=ఎజెక్ట్ +Name[tg]=Баровардан +Name[th]=เอาแผ่นสื่อออก +Name[tr]=Çıkart +Name[ug]=چىقار +Name[uk]=Виштовхнути +Name[uz]=Chiqarish +Name[uz@cyrillic]=Чиқариш +Name[vi]=Đẩy ra +Name[wa]=Fé rexhe +Name[xh]=Khuphela ngaphandle +Name[x-test]=xxEjectxx +Name[zh_CN]=弹出 +Name[zh_TW]=退出 +Icon=media-eject +Exec=kdeeject %v diff --git a/libs/konq/Templates/linkCAMERA.desktop b/libs/konq/Templates/linkCAMERA.desktop new file mode 100644 index 00000000..66c4a7b0 --- /dev/null +++ b/libs/konq/Templates/linkCAMERA.desktop @@ -0,0 +1,182 @@ +[Desktop Entry] +Name=Camera Device... +Name[af]=Kamera Toestel... +Name[ar]=جهاز كاميرا ... +Name[as]=কেমেৰা যন্ত্ৰ... +Name[ast]=Cámara... +Name[be]=Камера... +Name[be@latin]=Ličbavaja kamera... +Name[bg]=Камера... +Name[bn]=ক্যমেরা ডিভাইস... +Name[bn_IN]=ক্যামেরা ডিভাইস... +Name[br]=Trobarzhell ar gamera ... +Name[bs]=Uređaj kamere... +Name[ca]=Dispositiu de càmera... +Name[ca@valencia]=Dispositiu de càmera... +Name[cs]=Zařízení fotoaparátu... +Name[csb]=Kamera... +Name[cy]=Dyfais Camera... +Name[da]=Kamera-enhed... +Name[de]=Kamera ... +Name[el]=Συσκευή κάμερας... +Name[en_GB]=Camera Device... +Name[eo]=Fotilo-aparato... +Name[es]=Cámara... +Name[et]=Kaameraseade +Name[eu]=Kamera... +Name[fi]=Kamera… +Name[fr]=Appareil photo... +Name[fy]=Kamera... +Name[ga]=Gléas Ceamara... +Name[gl]=Cámara... +Name[gu]=કેમેરા ઉપકરણ... +Name[he]=התקן מצלמה... +Name[hi]=कैमरा उपकरण... +Name[hne]=कैमरा उपकरन... +Name[hr]=Kamera… +Name[hsb]=Kamerowy grat... +Name[hu]=Fényképezőgép… +Name[ia]=Dispositivo de Camera... +Name[id]=Divais Kamera... +Name[is]=Myndavél... +Name[it]=Dispositivo fotografico... +Name[ja]=カメラデバイス... +Name[ka]=კამერის მოწყობილობა +Name[kk]=Фотокамера... +Name[km]=ឧបករណ៍​ម៉ាស៊ីន​ថត... +Name[kn]=ಛಾಯಾಗ್ರಾಹಿ (ಕ್ಯಾಮೆರಾ) ಸಾಧನ... +Name[ko]=카메라 장치... +Name[ku]=Cîhaza Kamera... +Name[lt]=Fotoaparatas... +Name[lv]=Fotoaparāta ierīce... +Name[mai]=कैमरा युक्ति... +Name[mk]=Камера... +Name[ml]=ക്യാമറാ ഉപകരണം.... +Name[mr]=कॅमेरा साधन... +Name[ms]=Peranti Kamera... +Name[nb]=Kameraenhet … +Name[nds]=Kamera... +Name[nl]=Camera-apparaat... +Name[nn]=Kameraeining … +Name[or]=କ୍ୟାମେରା ଉପକରଣ... +Name[pa]=ਕੈਮਰਾ ਜੰਤਰ... +Name[pl]=Aparat fotograficzny... +Name[pt]=Máquina Fotográfica... +Name[pt_BR]=Dispositivo de câmera... +Name[ro]=Dispozitiv foto... +Name[ru]=Камера... +Name[se]=Kameraovttadat … +Name[si]=කැමරා උපාංගය... +Name[sk]=Zariadenie digitálneho fotoaparátu... +Name[sl]=Fotoaparat ... +Name[sr]=Фотоапарат... +Name[sr@ijekavian]=Фотоапарат... +Name[sr@ijekavianlatin]=Fotoaparat... +Name[sr@latin]=Fotoaparat... +Name[sv]=Kameraenhet... +Name[ta]=புகைப்பட கருவி சாதனங்கள் +Name[te]=కెమెరా పరికరం... +Name[tg]=Дастгоҳи камера... +Name[th]=อุปกรณ์กล้อง... +Name[tr]=Kamera Aygıtı... +Name[ug]=كامېرا ئۈسكۈنىسى… +Name[uk]=Пристрій фотоапарата... +Name[uz]=Fotoaparat uskunasi... +Name[uz@cyrillic]=Фотоапарат ускунаси... +Name[vi]=Thiết bị máy ảnh... +Name[wa]=Aparey di fotos... +Name[x-test]=xxCamera Device...xx +Name[zh_CN]=照相机设备... +Name[zh_TW]=相機裝置... +Comment=New camera +Comment[af]=Nuwe kamera +Comment[ar]=كاميرا جديدة +Comment[as]=নতুন কেমেৰা +Comment[ast]=Nueva cámara +Comment[be]=Новая камера +Comment[be@latin]=Novaja ličbavaja kamera +Comment[bg]=Нова камера +Comment[bn]=নতুন ক্যামেরা +Comment[bn_IN]=নতুন ক্যামেরা +Comment[br]=Kamera nevez +Comment[bs]=Nova kamera +Comment[ca]=Càmera nova +Comment[ca@valencia]=Càmera nova +Comment[cs]=Nový fotoaparát +Comment[csb]=Nowô kamera +Comment[cy]=Camera Newydd +Comment[da]=Nyt kamera +Comment[de]=Neue Kamera +Comment[el]=Νέα συσκευή κάμερας +Comment[en_GB]=New camera +Comment[eo]=Nova fotilo +Comment[es]=Nueva cámara +Comment[et]=Uus kaamera +Comment[eu]=Kamera berria +Comment[fi]=Uusi kamera +Comment[fr]=Nouvel appareil photo +Comment[fy]=Nije kamera +Comment[ga]=Ceamara nua +Comment[gl]=Nova cámara +Comment[gu]=નવો કેમેરા +Comment[he]=מצלמה חדשה +Comment[hi]=नया कैमरा +Comment[hne]=नवा केमरा +Comment[hr]=Nova kamera +Comment[hsb]=Nowa kamera +Comment[hu]=Új fényképezőgép +Comment[ia]=Nove Camera +Comment[id]=Kamera Baru +Comment[is]=Ný myndavél +Comment[it]=Nuova macchina fotografica +Comment[ja]=新しいカメラ +Comment[ka]=ახალი კამერა +Comment[kk]=Жаңа Фотокамера +Comment[km]=ម៉ាស៊ីនថត​ថ្មី +Comment[kn]=ಹೊಸ ಛಾಯಾಗ್ರಾಹಿ (ಕ್ಯಾಮೆರಾ) +Comment[ko]=새 카메라 +Comment[ku]=Kameraya nû +Comment[lt]=Naujas fotoaparatas +Comment[lv]=Jauns fotoaparāts +Comment[mai]=नवीन कैमरा +Comment[mk]=Нова камера +Comment[ml]=പുതിയ ക്യാമറ +Comment[mr]=नवीन कॅमेरा +Comment[ms]=Kamera baru +Comment[nb]=Nytt kamera +Comment[nds]=Niege Kamera +Comment[nl]=Nieuwe camera +Comment[nn]=Nytt kamera +Comment[or]=ନୂତନ କ୍ୟାମେରା +Comment[pa]=ਨਵਾਂ ਕੈਮਰਾ +Comment[pl]=Nowy aparat fotograficzny +Comment[pt]=Nova máquina fotográfica +Comment[pt_BR]=Nova câmera +Comment[ro]=Aparat foto nou +Comment[ru]=Ссылка на устройство цифровой камеры +Comment[se]=Ođđa kamera +Comment[si]=නව කැමරාව +Comment[sk]=Nový digitálny fotoaparát +Comment[sl]=Nov fotoaparat +Comment[sr]=Нови фотоапарат +Comment[sr@ijekavian]=Нови фотоапарат +Comment[sr@ijekavianlatin]=Novi fotoaparat +Comment[sr@latin]=Novi fotoaparat +Comment[sv]=Ny kamera +Comment[ta]=புதிய புகைப்பட கருவி +Comment[te]=కొత్త కెమెరా +Comment[tg]=Камераи нав +Comment[th]=กล้องตัวใหม่ +Comment[tr]=Yeni kamera +Comment[ug]=يېڭى كامېرا +Comment[uk]=Новий фотоапарат +Comment[uz]=Yangi fotoaparat +Comment[uz@cyrillic]=Янги фотоапарат +Comment[vi]=Máy ảnh mới +Comment[wa]=Novea aparey foto... +Comment[x-test]=xxNew cameraxx +Comment[zh_CN]=新建照相机 +Comment[zh_TW]=新相機 +Type=Link +URL=.source/CAMERA-Device.desktop +Icon=camera-photo diff --git a/libs/konq/Templates/linkCDROM.desktop b/libs/konq/Templates/linkCDROM.desktop new file mode 100644 index 00000000..6b9c0e17 --- /dev/null +++ b/libs/konq/Templates/linkCDROM.desktop @@ -0,0 +1,183 @@ +[Desktop Entry] +Name=CD-ROM Device... +Name[af]=CD-ROM Toestel +Name[ar]=جهاز CD ... +Name[as]=CD-ROM যন্ত্ৰ... +Name[ast]=CD-ROM... +Name[be]=Прылада CD-ROM... +Name[be@latin]=Pryłada CD-ROM... +Name[bg]=CD-ROM... +Name[bn]=সিডি-রম ডিভাইস... +Name[bn_IN]=CD-ROM ডিভাইস... +Name[br]=Trobarzhell CD-ROM ... +Name[bs]=Uređaj: CD‑ROM... +Name[ca]=Dispositiu CD-ROM... +Name[ca@valencia]=Dispositiu CD-ROM... +Name[cs]=Zařízení CDROM... +Name[csb]=Nëk CD... +Name[cy]=Dyfais CD-ROM... +Name[da]=Cd-rom-enhed... +Name[de]=CD-Laufwerk ... +Name[el]=Συσκευή CD-ROM... +Name[en_GB]=CD-ROM Device... +Name[eo]=KD-legilo... +Name[es]=CD-ROM... +Name[et]=CD-ROM seade... +Name[eu]=CD-ROM gailua... +Name[fi]=CD-ROM-laite… +Name[fr]=Lecteur de CD-ROM... +Name[fy]=kompaktskiifstasjon ... +Name[ga]=Gléas CD-ROM... +Name[gl]=CD-ROM... +Name[gu]=સીડી-રોમ ઉપકરણ... +Name[he]=התקן CD-ROM... +Name[hi]=सीडी-रोम उपकरण... +Name[hne]=सीडी-रोम उपकरन... +Name[hr]=CD/DVD-ROM uređaj… +Name[hsb]=CDnik ... +Name[hu]=CD-meghajtó… +Name[ia]=Dispositivo CD-ROM +Name[id]=Divais CD-ROM +Name[is]=Geisladrif... +Name[it]=Dispositivo CD-ROM... +Name[ja]=CD-ROM デバイス... +Name[ka]=CD-ROM მოწყობილობა +Name[kk]=CD-ROM құрылғы... +Name[km]=ឧបករណ៍​ស៊ីឌីរ៉ូម... +Name[kn]=CD-ROM ಸಾಧನ... +Name[ko]=CD-ROM 장치... +Name[ku]=Cihaza CD-ROM... +Name[lt]=CD-ROM įrenginys... +Name[lv]=CD-ROM ierīce... +Name[mai]=CD-ROM युक्ति... +Name[mk]=CD-ROM Уред... +Name[ml]=സിഡി-റോം ഉപകരണം... +Name[mr]=CD-ROM साधन... +Name[ms]=Peranti CD-ROM... +Name[nb]=CD-ROM-enhet … +Name[nds]=CD-ROM-Reedschap... +Name[nl]=CD-ROM-station... +Name[nn]=CD-ROM-eining … +Name[or]=CD-ROM ଉପକରଣ... +Name[pa]=CD-ROM ਜੰਤਰ... +Name[pl]=Urządzenie CD-ROM... +Name[pt]=CD-ROM... +Name[pt_BR]=Dispositivo de CD-ROM... +Name[ro]=Dispozitiv CD-ROM... +Name[ru]=CD-ROM... +Name[se]=CD-ROM-ovttadat … +Name[si]=CD-ROM උපාංගය... +Name[sk]=Zariadenie CD-ROM... +Name[sl]=Naprava CD-ROM ... +Name[sr]=ЦД‑РОМ... +Name[sr@ijekavian]=ЦД‑РОМ... +Name[sr@ijekavianlatin]=CD‑ROM... +Name[sr@latin]=CD‑ROM... +Name[sv]=Cdrom-enhet... +Name[ta]=CD-ROM சாதனம்... +Name[te]=సీడి-రామ్ పరికరం... +Name[tg]=Дастгоҳи CD-ROM... +Name[th]=อุปกรณ์ซีดีรอม... +Name[tr]=CD-ROM Aygıtı... +Name[ug]=CD-ROM ئۈسكۈنىسى… +Name[uk]=Пристрій CD-ROM... +Name[uz]=Kompakt-disk uskunasi... +Name[uz@cyrillic]=Компакт-диск ускунаси... +Name[vi]=Thiết bị đọc đĩa CD-ROM... +Name[wa]=Léjheu d' plake lazer... +Name[x-test]=xxCD-ROM Device...xx +Name[zh_CN]=CD-ROM 设备... +Name[zh_TW]=光碟機裝置... +Comment=New CD-ROM Device +Comment[af]=Nuwe CD-ROM Toestel +Comment[ar]=جهاز CD جديد +Comment[as]=নতুন CD-ROM যন্ত্ৰ +Comment[ast]=Nuevu preséu de CD-ROM +Comment[be]=Новая прылада CD-ROM +Comment[be@latin]=Novaja pryłada CD-ROM +Comment[bg]=Ново CD-ROM устройство +Comment[bn]=নতুন সিডি-রম ডিভাইস +Comment[bn_IN]=নতুন CD-ROM ডিভাইস +Comment[br]=Trobarzhell CD-ROM Nevez +Comment[bs]=Novi uređaj, CD‑ROM +Comment[ca]=Dispositiu CD-ROM nou +Comment[ca@valencia]=Dispositiu CD-ROM nou +Comment[cs]=Nové CDROM zařízení +Comment[csb]=Nowi nëk CD +Comment[cy]=Dyfais CD-ROM Newydd +Comment[da]=Ny cd-rom-enhed +Comment[de]=Neues CD-Laufwerk +Comment[el]=Νέα συσκευή CD-ROM +Comment[en_GB]=New CD-ROM Device +Comment[eo]=Nova KD-legilo +Comment[es]=Nuevo dispositivo de CD-ROM +Comment[et]=Uus CD-ROM seade +Comment[eu]=CD-ROM gailu berria +Comment[fi]=Uusi CD/DVD-ROM -laite +Comment[fr]=Nouveau lecteur de CD-ROM +Comment[fy]=Nije kompaktskiifstasjon +Comment[ga]=Gléas Nua CD-ROM +Comment[gl]=Novo dispositivo de CD-ROM +Comment[gu]=નવું સીડી-રોમ ઉપકરણ +Comment[he]=התקן CD-ROM חדש +Comment[hi]=नया सीडी-रोम उपकरण +Comment[hne]=नवा सीडी-रोम उपकरन +Comment[hr]=Novi CD/DVD-ROM uređaj +Comment[hsb]=Nowy CDnik +Comment[hu]=Új CD-meghajtó +Comment[ia]=Nove dispositivo CD-ROM +Comment[id]=Divais CD-ROM Baru +Comment[is]=Nýtt geisladrif +Comment[it]=Nuovo dispositivo CD-ROM +Comment[ja]=新しい CD-ROM デバイス +Comment[ka]=ახალი CD-ROM მოწყობილობა +Comment[kk]=Жаңа CD-ROM +Comment[km]=ឧបករណ៍​ស៊ីឌីរ៉ូម​ថ្មី +Comment[kn]=ಹೊಸ CD-ROM ಸಾಧನ +Comment[ko]=새 CD-ROM 장치 +Comment[ku]=Cihaza Nû ya CR-ROM +Comment[lt]=Naujas CD-ROM įrenginys +Comment[lv]=Jauna CD-ROM ierīce +Comment[mai]=नवीन CD-ROM युक्ति +Comment[mk]=Нов CD-ROM уред +Comment[ml]=പുതിയ സിഡി-റോം ഉപകരണം +Comment[mr]=नवीन CD-ROM साधन +Comment[ms]=Peranti CD-ROM Baru +Comment[nb]=Ny CD-ROM-enhet +Comment[nds]=Niege CD-ROM-Reedschap +Comment[nl]=Nieuw cd-rom-station +Comment[nn]=Ny CD-ROM-eining +Comment[oc]=Periferic CD-ROM novèl +Comment[or]=ନୂତନ CD-ROM ଉପକରଣ +Comment[pa]=ਨਵਾਂ CD-ROM ਜੰਤਰ +Comment[pl]=Nowe urządzenie CD-ROM +Comment[pt]=Nova Unidade de CD-ROM +Comment[pt_BR]=Novo dispositivo de CD-ROM +Comment[ro]=Dispozitiv CD-ROM nou +Comment[ru]=Ссылка на устройство CD-ROM +Comment[se]=Ođđa CD-ROM-ovttadat +Comment[si]=නව CD-ROM උපාංගය +Comment[sk]=Nové zariadenie CD-ROM +Comment[sl]=Nova naprava CD-ROM +Comment[sr]=Нови ЦД‑РОМ +Comment[sr@ijekavian]=Нови ЦД‑РОМ +Comment[sr@ijekavianlatin]=Novi CD‑ROM +Comment[sr@latin]=Novi CD‑ROM +Comment[sv]=Ny cdrom-enhet +Comment[ta]=புதிய CD-ROM சாதனம் +Comment[te]=కొత్త సీడి-రామ్ పరికరం +Comment[tg]=Дастгоҳи CD-ROM-и нав +Comment[th]=อุปกรณ์ซีดีรอมตัวใหม่ +Comment[tr]=Yeni CD-ROM Aygıtı +Comment[ug]=يېڭى CD-ROM ئۈسكۈنىسى +Comment[uk]=Новий пристрій CD-ROM +Comment[uz]=Yangi kompakt-disk uskunasi +Comment[uz@cyrillic]=Янги компакт-диск ускунаси +Comment[vi]=Thiết bị đọc đĩa CD-ROM mới +Comment[wa]=Novea léjheu d' plake lazer +Comment[x-test]=xxNew CD-ROM Devicexx +Comment[zh_CN]=新建 CD-ROM 设备 +Comment[zh_TW]=新的光碟機裝置 +Type=Link +URL=.source/CDROM-Device.desktop +Icon=media-optical diff --git a/libs/konq/Templates/linkCDWRITER.desktop b/libs/konq/Templates/linkCDWRITER.desktop new file mode 100644 index 00000000..f980964d --- /dev/null +++ b/libs/konq/Templates/linkCDWRITER.desktop @@ -0,0 +1,182 @@ +[Desktop Entry] +Name=CDWRITER Device... +Name[af]=CD Skrywer Toestel +Name[ar]=جهاز كتابة CD ... +Name[as]=CDWRITER যন্ত্ৰ... +Name[ast]=Grabador de CD... +Name[be]=Прылада CDWRITER... +Name[be@latin]=Pryłada CDWRITER... +Name[bg]=Записвачка... +Name[bn]=সিডি-রাইটার ডিভাইস... +Name[bn_IN]=CDWRITER ডিভাইস... +Name[br]=Trobarzhell an engraver CD ... +Name[bs]=Uređaj: CD‑rezač... +Name[ca]=Dispositiu CDWRITER... +Name[ca@valencia]=Dispositiu CDWRITER... +Name[cs]=Vypalovačka CD... +Name[csb]=Wëpôlôrz CD... +Name[cy]=Dyfais CDWRITER... +Name[da]=Cd-brænder-enhed... +Name[de]=CD-Brenner ... +Name[el]=Συσκευή CDWRITER... +Name[en_GB]=CDWRITER Device... +Name[eo]=KD-skribilo... +Name[es]=Grabador de CD... +Name[et]=CD-kirjutaja... +Name[eu]=CDWRITER gailua... +Name[fi]=CDWRITER-laite… +Name[fr]=Graveur de CD... +Name[fy]=kompaktskiifstasjon... +Name[ga]=Gléas CDWRITER... +Name[gl]=Gravadora de CD... +Name[gu]=સીડીરાઇટર ઉપકરણ... +Name[he]=התקן CDWRITER (כותב CD)... +Name[hi]=सीडी-राइटर उपकरण... +Name[hne]=सीडीराइटर उपकरन... +Name[hr]=CD pržilica… +Name[hsb]=CD-pisawa... +Name[hu]=CD-író… +Name[ia]=Dispositivo CDWRITER ... +Name[id]=Divais CDWRITER... +Name[is]=Geisladiskaskrifari... +Name[it]=Dispositivo di masterizzazione CD... +Name[ja]=CD ライターデバイス... +Name[ka]=CDWRITER მოწყობილობა +Name[kk]=CDWRITER құрылғы... +Name[km]=ឧបករណ៍​សរសេរ​ស៊ីឌី... +Name[kn]=CD ಬರೆಯುವಿಕೆ (ರೈಟರ್) ಸಾಧನ... +Name[ko]=CDWRITER 장치... +Name[ku]=Cîhaza CDWRITER +Name[lt]=CDWRITER įrenginys... +Name[lv]=CD rakstīšanas ierīce... +Name[mai]=CDWRITER युक्ति... +Name[mk]=CDWRITER Уред... +Name[ml]=സിഡി-റൈറ്റര്‍ ഉപകരണം +Name[mr]=CDWRITER साधन... +Name[ms]=Peranti CDWRITER... +Name[nb]=CD-skriver-enhet … +Name[nds]=CD-Brenner... +Name[nl]=CD-schrijver... +Name[nn]=CD-brennar … +Name[or]=CDWRITER ଉପକରଣ... +Name[pa]=CDWRITER ਜੰਤਰ... +Name[pl]=Nagrywarka CD... +Name[pt]=Gravador de CD... +Name[pt_BR]=Dispositivo CDWRITER... +Name[ro]=Dispozitiv CD-Writer... +Name[ru]=Пишущий CD-ROM... +Name[se]=CD-čálán … +Name[si]=CDWRITER උපාංගය... +Name[sk]=Zariadenie CD napaľovačky... +Name[sl]=Zapisovalnik CD-jev ... +Name[sr]=ЦД‑резач... +Name[sr@ijekavian]=ЦД‑резач... +Name[sr@ijekavianlatin]=CD‑rezač... +Name[sr@latin]=CD‑rezač... +Name[sv]=Cd-brännarenhet... +Name[ta]=CDWRITER சாதனம் +Name[te]=సీడి వ్రైటర్ పరికరం... +Name[tg]=Дастгоҳи CDWRITER... +Name[th]=อุปกรณ์เครื่องเขียนแผ่นซีดี... +Name[tr]=CD Yazıcı Aygıtı... +Name[ug]=CD ئويغۇچ ئۈسكۈنىسى… +Name[uk]=Пристрій CDWRITER... +Name[uz]=Kompakt-disk yozuvchi uskuna... +Name[uz@cyrillic]=Компакт-диск ёзувчи ускуна... +Name[vi]=Thiết bị ghi đĩa CD... +Name[wa]=Sicrijheu d' plake lazer... +Name[x-test]=xxCDWRITER Device...xx +Name[zh_CN]=刻录机设备... +Name[zh_TW]=光碟燒錄器裝置... +Comment=New CDWRITER Device +Comment[af]=Nuwe CD Skrywer Toestel +Comment[ar]=جهاز كتابة CD جديد +Comment[as]=নতুন CDWRITER যন্ত্ৰ +Comment[ast]=Nuevu grabador de CD +Comment[be]=Новая прылада CDWRITER +Comment[be@latin]=Novaja pryłada CDWRITER +Comment[bg]=Нова записвачка +Comment[bn]=নতুন সিডি-রাইটার ডিভাইস +Comment[bn_IN]=নতুন CDWRITER ডিভাইস +Comment[br]=Trobarzhell CDSKRIVER nevez +Comment[bs]=Novi uređaj, CD‑rezač +Comment[ca]=Dispositiu CDWRITER nou +Comment[ca@valencia]=Dispositiu CDWRITER nou +Comment[cs]=Nové zařízení pro vypalování CD +Comment[csb]=Nowi wëpôlôrz CD +Comment[cy]=Dyfais CDWRITER Newydd +Comment[da]=Ny cd-brænder-enhed +Comment[de]=Neuer CD-Brenner +Comment[el]=Νέα συσκευή CDWRITER +Comment[en_GB]=New CDWRITER Device +Comment[eo]=Nova KD-skribilo +Comment[es]=Nuevo grabador de CD +Comment[et]=Uus CD-kirjutaja +Comment[eu]=CDWRITER gailu berria +Comment[fi]=Uusi CDWRITER-laite +Comment[fr]=Nouveau graveur de CD +Comment[fy]=Nije kompaktskiifstasjon +Comment[ga]=Gléas Nua CDWRITER +Comment[gl]=Novo dispositivo gravador de CD +Comment[gu]=નવું સીડીરાઇટર ઉપકરણ +Comment[he]=התקן CDWRITER (כותב CD) חדש +Comment[hi]=नया सीडी-राइटर उपकरण +Comment[hne]=नवा सीडीराइटर उपकरन +Comment[hr]=Nova CD/DVD pržilica +Comment[hsb]=Nowa CD-pisawa +Comment[hu]=Új CD-író +Comment[ia]=Nove dispositivo CDWRITER +Comment[id]=Divais CDWRITER Baru +Comment[is]=Nýr geisladiskaskrifari +Comment[it]=Nuovo dispositivo di masterizzazione +Comment[ja]=新しい CD ライターデバイス +Comment[ka]=ახალი CDWRITER მოწყობილობა +Comment[kk]=Жаңа CDWRITER +Comment[km]=ឧបករណ៍​កម្មវិធី​សរសេរ​ស៊ីឌីថ្មី +Comment[kn]=ಹೊಸ CD ಬರೆಯುವಿಕೆ (ರೈಟರ್) ಸಾಧನ +Comment[ko]=새 CDWRITER 장치 +Comment[ku]=Cîhaza nû a CDWRITER +Comment[lt]=Naujas CDWRITER įrenginys +Comment[lv]=Jauna CD rakstīšanas ierīce +Comment[mai]=नवीन CDWRITER युक्ति +Comment[mk]=Нов CDWRITER уред +Comment[ml]=പുതിയ സിഡി-റൈറ്റര്‍ ഉപകരണം +Comment[mr]=नवीन CDWRITER साधन +Comment[ms]=Peranti CDWRITER Baru +Comment[nb]=Ny CD-skriver-enhet +Comment[nds]=Niegen CD-Brenner +Comment[nl]=Nieuwe cd-schrijver +Comment[nn]=Ny CD-brennar +Comment[or]=CDWRITER ଉପକରଣ +Comment[pa]=ਨਵਾਂ CDWRITER ਜੰਤਰ +Comment[pl]=Nowa nagrywarka CD +Comment[pt]=Novo Gravador de CD's +Comment[pt_BR]=Novo dispositivo CDWRITER +Comment[ro]=Dispozitiv CD-Writer nou +Comment[ru]=Ссылка на устройство пишущего CD-ROM +Comment[se]=Ođđa CD-čálán +Comment[si]=නව CDWRITER උපාංගය +Comment[sk]=Nové zariadenie CD napaľovačky +Comment[sl]=Nov zapisovalnik CD-jev +Comment[sr]=Нови ЦД‑резач +Comment[sr@ijekavian]=Нови ЦД‑резач +Comment[sr@ijekavianlatin]=Novi CD‑rezač +Comment[sr@latin]=Novi CD‑rezač +Comment[sv]=Ny cd-brännarenhet +Comment[ta]=புது CDWRITER சாதனம் +Comment[te]=కొత్త సీడి వ్రైటర్ పరికరం +Comment[tg]=Настгоҳи CDWRITER-и нав +Comment[th]=อุปกรณ์เครื่องเขียนแผ่นซีดีตัวใหม่ +Comment[tr]=Yeni CD Yazıcı Aygıtı +Comment[ug]=يېڭى CD ئويغۇچ ئۈسكۈنىسى +Comment[uk]=Новий пристрій CDWRITER +Comment[uz]=Yangi kompakt-disk yozuvchi uskuna +Comment[uz@cyrillic]=Янги компакт-диск ёзувчи ускуна +Comment[vi]=Thiết bị ghi đĩa CD mới +Comment[wa]=Novea scrijheu d' plake lazer +Comment[x-test]=xxNew CDWRITER Devicexx +Comment[zh_CN]=新建刻录机设备 +Comment[zh_TW]=新的光碟燒錄器裝置 +Type=Link +URL=.source/CDWRITER-Device.desktop +Icon=media-optical-recordable diff --git a/libs/konq/Templates/linkDVDROM.desktop b/libs/konq/Templates/linkDVDROM.desktop new file mode 100644 index 00000000..926230ad --- /dev/null +++ b/libs/konq/Templates/linkDVDROM.desktop @@ -0,0 +1,183 @@ +[Desktop Entry] +Name=DVD-ROM Device... +Name[af]=DVD-ROM Toestel +Name[ar]=جهاز DVD ... +Name[as]=DVD-ROM যন্ত্ৰ... +Name[ast]=DVD-ROM... +Name[be]=Прылада DVD-ROM... +Name[be@latin]=Pryłada DVD-ROM... +Name[bg]=DVD-ROM... +Name[bn]=ডিভিডি-রম ডিভাইস... +Name[bn_IN]=DVD-ROM ডিভাইস... +Name[br]=Trobarzhell DVD-ROM ... +Name[bs]=Uređaj: DVD‑ROM... +Name[ca]=Dispositiu DVD-ROM... +Name[ca@valencia]=Dispositiu DVD-ROM... +Name[cs]=Zařízení DVD-ROM... +Name[csb]=Nëk DVD... +Name[cy]=Dyfais DVD-ROM... +Name[da]=Dvd-rom-enhed... +Name[de]=DVD-Laufwerk ... +Name[el]=Συσκευή DVD-ROM... +Name[en_GB]=DVD-ROM Device... +Name[eo]=DVD-legilo... +Name[es]=DVD-ROM... +Name[et]=DVD-ROM seade... +Name[eu]=DVD-ROM gailua... +Name[fi]=DVD-ROM-laite +Name[fr]=Lecteur de DVD-ROM... +Name[fy]=Dûbelskiifstasjon +Name[ga]=Gléas DVD-ROM... +Name[gl]=DVD-ROM... +Name[gu]=ડીવીડી-રોમ ઉપકરણ... +Name[he]=התקן DVD-ROM... +Name[hi]=डीवीडी-रोम उपकरण... +Name[hne]=डीवीडी-रोम उपकरन... +Name[hr]=CD/DVD-ROM uređaj… +Name[hsb]=DVDnik +Name[hu]=DVD-meghajtó… +Name[ia]=Dispositivo DVD-ROM... +Name[id]=Divais DVD-ROM... +Name[is]=DVD-ROM-drif... +Name[it]=Dispositivo DVD-ROM... +Name[ja]=DVD-ROM デバイス... +Name[ka]=DVD-ROM მოწყობილობა +Name[kk]=DVD-ROM құрылғы... +Name[km]=ឧបករណ៍​ឌីវីឌីរ៉ូម... +Name[kn]=DVD-ROM ಸಾಧನ... +Name[ko]=DVD-ROM 장치... +Name[ku]=Cihaza DVD-ROM... +Name[lt]=DVD-ROM įrenginys... +Name[lv]=DVD-ROM ierīce... +Name[mai]=DVD-ROM युक्ति... +Name[mk]=DVD-ROM Уред... +Name[ml]=ഡിവിഡി-റോം ഉപകരണം... +Name[mr]=DVD-ROM साधन... +Name[ms]=Peranti DVD-ROM... +Name[nb]=DVD-ROM-enhet … +Name[nds]=DVD-ROM-Reedschap... +Name[nl]=DVD-ROM-station... +Name[nn]=DVD-ROM-eining … +Name[oc]=Periferic DVD-ROM... +Name[or]=DVD-ROM ଉପକରଣ... +Name[pa]=DVD-ROM ਜੰਤਰ... +Name[pl]=Urządzenie DVD-ROM... +Name[pt]=DVD-ROM... +Name[pt_BR]=Dispositivo de DVD-ROM... +Name[ro]=Dispozitiv DVD-ROM... +Name[ru]=DVD-ROM... +Name[se]=DVD-ROM-ovttadat … +Name[si]=DVD-ROM උපාංගය... +Name[sk]=Zariadenie DVD-ROM... +Name[sl]=Naprava DVD-ROM ... +Name[sr]=ДВД‑РОМ... +Name[sr@ijekavian]=ДВД‑РОМ... +Name[sr@ijekavianlatin]=DVD‑ROM... +Name[sr@latin]=DVD‑ROM... +Name[sv]=Dvdrom-enhet... +Name[ta]=டிவிடிராம் சாதனம் +Name[te]=డివిడి-రామ్ పరికరం... +Name[tg]=Дастгоҳи DVD-ROM... +Name[th]=อุปกรณ์ดีวีดีรอม... +Name[tr]=CD/DVD-ROM Aygıtı... +Name[ug]=DVD-ROM ئۈسكۈنىسى… +Name[uk]=Пристрій DVD-ROM... +Name[uz]=DVD-ROM uskunasi... +Name[uz@cyrillic]=DVD-ROM ускунаси... +Name[vi]=Thiết bị đọc đĩa DVD-ROM... +Name[wa]=Léjheu d' DVD... +Name[x-test]=xxDVD-ROM Device...xx +Name[zh_CN]=DVD-ROM 设备... +Name[zh_TW]=DVD 裝置 +Comment=New DVD-ROM Device +Comment[af]=Nuwe DVD-ROM Toestel +Comment[ar]=جهاز DVD جديد +Comment[as]=নতুন DVD-ROM যন্ত্ৰ +Comment[ast]=Nuevu preséu de DVD +Comment[be]=Новая прылада DVD-ROM +Comment[be@latin]=Novaja pryłada DVD-ROM +Comment[bg]=Ново DVD-ROM устройство +Comment[bn]=নতুন ডিভিডি-রম ডিভাইস +Comment[bn_IN]=নতুন DVD-ROM ডিভাইস +Comment[br]=Trobarzhell DVD-ROM Nevez +Comment[bs]=Novi uređaj, DVD‑ROM +Comment[ca]=Dispositiu DVD-ROM nou +Comment[ca@valencia]=Dispositiu DVD-ROM nou +Comment[cs]=Nové DVD-ROM zařízení +Comment[csb]=Nowi nëk DVD +Comment[cy]=Dyfais DVD-ROM Newydd +Comment[da]=Ny dvd-rom enhed +Comment[de]=Neues DVD-Laufwerk +Comment[el]=Νέα συσκευή DVD-ROM +Comment[en_GB]=New DVD-ROM Device +Comment[eo]=Nova DVD-legilo +Comment[es]=Nuevo dispositivo de DVD +Comment[et]=Uus DVD-ROM seade +Comment[eu]=DVD-ROM gailu berria +Comment[fi]=Uusi DVD-ROM-laite +Comment[fr]=Nouveau lecteur de DVD-ROM +Comment[fy]=Nije dûbelskiifstasjon +Comment[ga]=Gléas Nua DVD-ROM +Comment[gl]=Novo dispositivo de DVD-ROM... +Comment[gu]=નવું ડીવીડી-રોમ ઉપકરણ +Comment[he]=התקן DVD-ROM חדש +Comment[hi]=नया डीवीडी-रोम उपकरण +Comment[hne]=नवा डीवीडी-रोम उपकरन +Comment[hr]=Novi CD/DVD-ROM uređaj +Comment[hsb]=Nowy DVDnik +Comment[hu]=Új DVD-meghajtó +Comment[ia]=Nove dispositivo DVD-ROM +Comment[id]=Divais DVD-ROM Baru +Comment[is]=Nýtt DVD-ROM-drif +Comment[it]=Nuovo dispositivo DVD-ROM +Comment[ja]=新しい DVD-ROM デバイス +Comment[ka]=ახალი DVD-ROM მოწყობილობა +Comment[kk]=Жаңа DVD-ROM +Comment[km]=ឧបករណ៍​ឌីវីឌីរ៉ូម​ថ្មី +Comment[kn]=ಹೊಸ DVD-ROM ಸಾಧನ +Comment[ko]=새 DVD-ROM 장치 +Comment[ku]=Cihaza DVD-ROM a Nû +Comment[lt]=Naujas DVD-ROM įrenginys +Comment[lv]=Jauna DVD-ROM ierīce +Comment[mai]=नवीन DVD-ROM युक्ति +Comment[mk]=Нов DVD-ROM уред +Comment[ml]=പുതിയ ഡിവിഡി-റോം ഉപകരണം +Comment[mr]=नवीन DVD-ROM साधन +Comment[ms]=Peranti DVD-ROM Baru +Comment[nb]=Ny DVD-ROM-enhet +Comment[nds]=Niege DVD-ROM-Reedschap +Comment[nl]=Nieuw dvd-rom-station +Comment[nn]=Ny DVD-ROM-eining +Comment[or]=ନୂତନ DVD-ROM ଉପକରଣ +Comment[pa]=ਨਵਾਂ DVD-ROM ਜੰਤਰ +Comment[pl]=Nowe urządzenie DVD-ROM... +Comment[pt]=Novo Leitor de DVD-ROM +Comment[pt_BR]=Novo dispositivo de DVD-ROM +Comment[ro]=Dispozitiv DVD-ROM nou +Comment[ru]=Ссылка на устройство DVD-ROM +Comment[se]=Ođđa DVD-ROM-ovttadat +Comment[si]=නව DVD-ROM උපාංගය +Comment[sk]=Nové zariadenie DVD-ROM +Comment[sl]=Nova naprava DVD-ROM +Comment[sr]=Нови ДВД‑РОМ +Comment[sr@ijekavian]=Нови ДВД‑РОМ +Comment[sr@ijekavianlatin]=Novi DVD‑ROM +Comment[sr@latin]=Novi DVD‑ROM +Comment[sv]=Ny dvdrom-enhet +Comment[ta]=புது DVD-ROM சாதனம் +Comment[te]=కొత్త డివిడి-రామ్ పరికరం +Comment[tg]=Дастгоҳи DVD-ROM-и нав +Comment[th]=อุปกรณ์ดีวีดีรอมตัวใหม่ +Comment[tr]=Yeni DVD-ROM Aygıtı +Comment[ug]=يېڭى DVD-ROM ئۈسكۈنىسى +Comment[uk]=Новий пристрій DVD-ROM +Comment[uz]=Yangi DVD uskunasi +Comment[uz@cyrillic]=Янги DVD ускунаси +Comment[vi]=Thiết bị đọc đĩa DVD-ROM mới +Comment[wa]=Novea léjheu d' DVD +Comment[x-test]=xxNew DVD-ROM Devicexx +Comment[zh_CN]=新建 DVD-ROM 设备 +Comment[zh_TW]=新的 DVD 裝置 +Type=Link +URL=.source/DVDROM-Device.desktop +Icon=media-optical diff --git a/libs/konq/Templates/linkFloppy.desktop b/libs/konq/Templates/linkFloppy.desktop new file mode 100644 index 00000000..3d8e8460 --- /dev/null +++ b/libs/konq/Templates/linkFloppy.desktop @@ -0,0 +1,183 @@ +[Desktop Entry] +Name=Floppy Device... +Name[af]=Sagteskyf Toestel... +Name[ar]=جهاز قرص مرن ... +Name[as]=Floppy যন্ত্ৰ... +Name[ast]=Disquetera... +Name[be]=Дыскета... +Name[be@latin]=Dyskietnik... +Name[bg]=Флопи... +Name[bn]=ফ্লপি ডিভাইস... +Name[bn_IN]=ফ্লপি ডিভাইস... +Name[br]=Trobarzhell Pladennig ... +Name[bs]=Uređaj: flopi... +Name[ca]=Dispositiu de disquet... +Name[ca@valencia]=Dispositiu de disquet... +Name[cs]=Disketová mechanika... +Name[csb]=Nëk disczétków... +Name[cy]=Dyfais Disg Meddal... +Name[da]=Floppy-enhed... +Name[de]=Diskettenlaufwerk ... +Name[el]=Συσκευή δισκέτας... +Name[en_GB]=Floppy Device... +Name[eo]=Disketingo... +Name[es]=Disquetera... +Name[et]=Flopiseade... +Name[eu]=Diskete gailua... +Name[fi]=Levykeasema… +Name[fr]=Lecteur de disquettes... +Name[fy]=Slappe skiifstasjon... +Name[ga]=Gléas Diosca Flapach... +Name[gl]=Disqueteira... +Name[gu]=ફ્લોપી ઉપકરણ... +Name[he]=התקן דיסקט... +Name[hi]=फ़्लॉपी उपकरण... +Name[hne]=फ्लापी उपकरन... +Name[hr]=Disketni uređaj… +Name[hsb]=Disketnik ... +Name[hu]=Floppy-meghajtó… +Name[ia]=Dispositivo floppy... +Name[id]=Divais Disket... +Name[is]=Disklingadrif... +Name[it]=Dispositivo per dischetti... +Name[ja]=フロッピーデバイス... +Name[ka]=დრეკადი დისკის წამყვანი +Name[kk]=Иілгіш диск құрылғысы... +Name[km]=ឧបករណ៍​ថាសទន់... +Name[kn]=ಮೆದುಮುದ್ರಿಕೆ ಸಾಧನ... +Name[ko]=플로피 장치... +Name[ku]=Cîhaza Flopî... +Name[lt]=Diskelių įrenginys... +Name[lv]=Diskešu ierīce... +Name[mai]=फ्लापी युक्ति... +Name[mk]=Дискетна единица... +Name[ml]=ഫ്ലോപ്പി ഉപകരണം... +Name[mr]=फ्लॉपी साधन... +Name[ms]=Peranti Liut... +Name[nb]=Diskettenhet … +Name[nds]=Diskett-Reedschap... +Name[nl]=Diskettestation... +Name[nn]=Diskett­stasjon … +Name[or]=ଫ୍ଲୋପି ଉପକରଣ... +Name[pa]=ਫਲਾਪੀ ਜੰਤਰ... +Name[pl]=Stacja dyskietek... +Name[pt]=Disquete... +Name[pt_BR]=Dispositivo de disquete... +Name[ro]=Dispozitiv Dischieră... +Name[ru]=Дисковод... +Name[se]=Dipmaskearrostašuvdna … +Name[si]=මෘදු තැටි උපාංගය... +Name[sk]=Disketová mechanika... +Name[sl]=Disketnik ... +Name[sr]=Флопи... +Name[sr@ijekavian]=Флопи... +Name[sr@ijekavianlatin]=Flopi... +Name[sr@latin]=Flopi... +Name[sv]=Diskettenhet... +Name[ta]=நெகிழ்வட்டு சாதனம்... +Name[te]=ఫ్లాపీ పరికరం... +Name[tg]=Дастгоҳи дискета (Floppy)... +Name[th]=อุปกรณ์อ่านแผ่นฟลอปปี... +Name[tr]=Disket Aygıtı... +Name[ug]=يۇمشاق دىسكا قوزغاتقۇچ ئۈسكۈنىسى… +Name[uk]=Пристрій гнучкого диска... +Name[uz]=Disket uskunasi... +Name[uz@cyrillic]=Дискет ускунаси... +Name[vi]=Thiết bị đĩa mềm... +Name[wa]=Diskete... +Name[x-test]=xxFloppy Device...xx +Name[zh_CN]=软驱设备... +Name[zh_TW]=軟碟機裝置... +Comment=New Floppy Device +Comment[af]=Nuwe Sagteskyf Toestel +Comment[ar]=جهاز قرص مرن جديد +Comment[as]=নতুন Floppy যন্ত্ৰ +Comment[ast]=Nueva disquetera +Comment[be]=Новая дыскета +Comment[be@latin]=Novy dyskietnik +Comment[bg]=Ново флопи +Comment[bn]=নতুন ফ্লপি ডিভাইস +Comment[bn_IN]=নতুন ফ্লপি ডিভাইস +Comment[br]=Trobarzhell Bladennig Nevez +Comment[bs]=Novi uređaj, flopi +Comment[ca]=Dispositiu de disquet nou +Comment[ca@valencia]=Dispositiu de disquet nou +Comment[cs]=Nová disketová mechanika +Comment[csb]=Nowi nëk disczétków +Comment[cy]=Dyfais Disg Meddal Newydd +Comment[da]=Ny floppy-enhed +Comment[de]=Neues Diskettenlaufwerk +Comment[el]=Νέα συσκευή δισκέτας +Comment[en_GB]=New Floppy Device +Comment[eo]=Nova disketingo +Comment[es]=Nueva disquetera +Comment[et]=Uus flopiseade +Comment[eu]=Diskete gailu berria +Comment[fi]=Uusi levykeasema +Comment[fr]=Nouveau lecteur de disquettes +Comment[fy]=Nije slappe skiifstasjon +Comment[ga]=Gléas nua diosca flapach +Comment[gl]=Novo dispositivo de disquetes +Comment[gu]=નવું ફ્લોપી ઉપકરણ +Comment[he]=התקן דיסקט חדש +Comment[hi]=नया फ़्लॉपी उपकरण +Comment[hne]=नवा फ्लापी उपकरन +Comment[hr]=Novi disketni uređaj +Comment[hsb]=Nowy disketnik +Comment[hu]=Új floppy-meghajtó +Comment[ia]=Nove dispositivo floppy +Comment[id]=Divais Disket Baru +Comment[is]=Nýtt disklingadrif +Comment[it]=Nuovo dispositivo per dischetti +Comment[ja]=新しいフロッピーデバイス +Comment[ka]=ახალი დრეკადი დისკის წამყვანი +Comment[kk]=Жана Иілгіш дискі +Comment[km]=ឧបករណ៍​ថាសទន់​ថ្មី +Comment[kn]=ಹೊಸ ಮೆದುಮುದ್ರಿಕೆ ಸಾಧನ +Comment[ko]=새 플로피 장치 +Comment[ku]=Cihaza Flopî ya Nû +Comment[lt]=Naujas diskelių įrenginys +Comment[lv]=Jauna diskešu ierīce +Comment[mai]=नव फ्लापी युक्ति +Comment[mk]=Нова дискетна единица +Comment[ml]=പുതിയ ഫ്ലോപ്പി ഉപകരണം +Comment[mr]=नवीन Floppy साधन +Comment[ms]=Peranti Liut Baru +Comment[nb]=Ny diskettenhet +Comment[nds]=Niege Diskettreedschap +Comment[nl]=Nieuw diskettestation +Comment[nn]=Ny diskett­stasjon +Comment[or]=ନୂତନ ଫ୍ଲୋପି ଉପକରଣ... +Comment[pa]=ਨਵਾਂ ਫਲਾਪੀ ਜੰਤਰ +Comment[pl]=Nowa stacja dyskietek +Comment[pt]=Novo Leitor de Disquetes +Comment[pt_BR]=Novo dispositivo de disquete +Comment[ro]=Dispozitiv Dischieră nou +Comment[ru]=Ссылка на устройство дисковода +Comment[se]=Ođđa dipmaskearrostašuvdna +Comment[si]=නව මෘදු තැටි උපාංගය +Comment[sk]=Nová disketová mechanika +Comment[sl]=Nov disketnik +Comment[sr]=Нови флопи +Comment[sr@ijekavian]=Нови флопи +Comment[sr@ijekavianlatin]=Novi flopi +Comment[sr@latin]=Novi flopi +Comment[sv]=Ny diskettenhet +Comment[ta]=புது நெகிழ்வட்டு சாதனம் +Comment[te]=కొత్త ఫ్లాపీ పరికరం +Comment[tg]=Дастгоҳи дискетаи (Floppy) нав +Comment[th]=อุปกรณ์อ่านแผ่นฟลอปปีตัวใหม่ +Comment[tr]=Yeni Disket Aygıtı +Comment[ug]=يېڭى يۇمشاق دىسكا قوزغاتقۇچ ئۈسكۈنىسى +Comment[uk]=Новий пристрій гнучкого диска +Comment[uz]=Yangi disket uskunasi +Comment[uz@cyrillic]=Янги дискет ускунаси +Comment[vi]=Thiết bị đĩa mềm mới +Comment[wa]=Novele diskete +Comment[xh]=Icebo Elitsha le Floppy +Comment[x-test]=xxNew Floppy Devicexx +Comment[zh_CN]=新建软驱设备 +Comment[zh_TW]=新的軟碟機裝置 +Type=Link +URL=.source/Floppy.desktop +Icon=media-floppy diff --git a/libs/konq/Templates/linkHD.desktop b/libs/konq/Templates/linkHD.desktop new file mode 100644 index 00000000..bc9504c6 --- /dev/null +++ b/libs/konq/Templates/linkHD.desktop @@ -0,0 +1,183 @@ +[Desktop Entry] +Name=Hard Disc Device... +Name[af]=Hardeskyf Toestel... +Name[ar]=جهاز قرص صلب ... +Name[as]=হার্ড ডিস্ক যন্ত্ৰ... +Name[ast]=Discu duru... +Name[be]=Раздзел жорсткага дыска... +Name[be@latin]=Ćviordy dysk... +Name[bg]=Твърд диск... +Name[bn]=হার্ড ডিস্ক ডিভাইস... +Name[bn_IN]=হার্ড ডিস্ক ডিভাইস... +Name[br]=Trobarzhell Bladenn ... +Name[bs]=Uređaj: hard‑disk... +Name[ca]=Dispositius de disc dur... +Name[ca@valencia]=Dispositius de disc dur... +Name[cs]=Pevný disk... +Name[csb]=Cwiardi disk... +Name[cy]=Dyfais Disg Caled... +Name[da]=Harddisk-enhed... +Name[de]=Festplatte ... +Name[el]=Συσκευή σκληρού δίσκου... +Name[en_GB]=Hard Disc Device... +Name[eo]=Fiksita diskingo... +Name[es]=Disco duro... +Name[et]=Kõvaketas... +Name[eu]=Disko gogorra... +Name[fi]=Kiintolevy… +Name[fr]=Disque dur... +Name[fy]=Fêste skiif... +Name[ga]=Gléas Diosca Crua... +Name[gl]=Disco duro... +Name[gu]=હાર્ડ ડિસ્ક ઉપકરણ... +Name[he]=התקן כונן־קשיח... +Name[hi]=हार्ड डिस्क उपकरण... +Name[hne]=हार्ड डिस्क उपकरन... +Name[hr]=Čvrsti disk… +Name[hsb]=Kruty disk +Name[hu]=Merevlemez-partíció… +Name[ia]=Dispositivo disco dur +Name[id]=Divais Cakram Keras... +Name[is]=Harður diskur... +Name[it]=Disco rigido... +Name[ja]=ハードディスクデバイス... +Name[ka]=ხისტი დისკი... +Name[kk]=Қатқыл диск құрылғысы... +Name[km]=ឧបករណ៍​ថាសរឹង... +Name[kn]=ದೃಢ ಮುದ್ರಿಕೆ ಸಾಧನ... +Name[ko]=하드 디스크 장치... +Name[ku]=Cîhaza Hard Diskê... +Name[lt]=Kietojo disko įrenginys... +Name[lv]=Cietais disks... +Name[mai]=हार्ड डिस्क युक्ति... +Name[mk]=Тврд диск... +Name[ml]=ഹാര്‍ഡ് ഡിസ്ക് ഉപകരണം... +Name[mr]=हार्ड डिस्क साधन... +Name[ms]=Peranti Cakera Keras... +Name[nb]=Harddisk-enhet … +Name[nds]=Fastplaat... +Name[nl]=Harde schijf... +Name[nn]=Harddiskeining … +Name[or]=ହାର୍ଡ଼ ଡ଼ିସ୍କ ଉପକରଣ... +Name[pa]=ਹਾਰਡ ਡਿਸਕ ਜੰਤਰ... +Name[pl]=Twardy dysk... +Name[pt]=Disco Rígido... +Name[pt_BR]=Dispositivo de disco rígido... +Name[ro]=Dispozitiv Disc fix... +Name[ru]=Жёсткий диск... +Name[se]=Garraskearroovttadat … +Name[si]=දෘඩ තැටි උපාංගය +Name[sk]=Zariadenie pevného disku... +Name[sl]=Trdi disk ... +Name[sr]=Хард‑диск... +Name[sr@ijekavian]=Хард‑диск... +Name[sr@ijekavianlatin]=Hard‑disk... +Name[sr@latin]=Hard‑disk... +Name[sv]=Hårddiskenhet... +Name[ta]=வட்டு +Name[te]=హార్డ్ డిస్క్ పరికరం... +Name[tg]=Дастгоҳи диски компютерӣ... +Name[th]=อุปกรณ์ฮาร์ดดิสก์... +Name[tr]=Sabit Disk Aygıtı... +Name[ug]=قاتتىق دىسكا ئۈسكۈنىسى… +Name[uk]=Пристрій жорсткого диска... +Name[uz]=Qattiq disk uskunasi... +Name[uz@cyrillic]=Қаттиқ диск ускунаси... +Name[vi]=Thiết bị đĩa cứng... +Name[wa]=Deure plake... +Name[x-test]=xxHard Disc Device...xx +Name[zh_CN]=硬盘设备... +Name[zh_TW]=硬碟機裝置... +Comment=New Hard Disc +Comment[af]=Nuwe Hardeskyf +Comment[ar]=قرص صلب جديد +Comment[as]=নতুন হার্ড ডিস্ক +Comment[ast]=Nuevu discu duru +Comment[be]=Новы раздзел жорсткага дыска +Comment[be@latin]=Novy ćviordy dysk +Comment[bg]=Нов твърд диск +Comment[bn]=নতুন হার্ড ডিস্ক +Comment[bn_IN]=নতুন হার্ড ডিস্ক +Comment[br]=Trobarzhell Bladenn Nevez +Comment[bs]=Novi uređaj, hard‑disk +Comment[ca]=Disc dur nou +Comment[ca@valencia]=Disc dur nou +Comment[cs]=Nový pevný disk +Comment[csb]=Nowi cwiardi disk +Comment[cy]=Disg Caled Newydd +Comment[da]=Ny harddisk +Comment[de]=Neue Festplatte +Comment[el]=Νέος σκληρός δίσκος +Comment[en_GB]=New Hard Disk +Comment[eo]=Nova fiksita disko +Comment[es]=Nuevo disco duro +Comment[et]=Uus kõvaketas +Comment[eu]=Disko gogor berria +Comment[fi]=Uusi kiintolevy +Comment[fr]=Nouveau disque dur +Comment[fy]=Nije Fêste skiif +Comment[ga]=Diosca crua nua +Comment[gl]=Novo disco duro +Comment[gu]=નવી હાર્ડ ડિસ્ક +Comment[he]=כונן־קשיח חדש +Comment[hi]=नया हार्ड डिस्क +Comment[hne]=नवा हार्ड डिस्क +Comment[hr]=Novi čvrsti disk +Comment[hsb]=Nowy kruty disk +Comment[hu]=Új merevlemez-partíció +Comment[ia]=Nove disco dur +Comment[id]=Cakram Keras Baru +Comment[is]=Nýr harður diskur +Comment[it]=Nuovo disco rigido +Comment[ja]=新しいハードディスク +Comment[ka]=ახალი ხისტი დისკი +Comment[kk]=Жаңа Қатқыл дискі +Comment[km]=ថាសរឹង​ថ្មី +Comment[kn]=ಹೊಸ ದೃಢ ಮುದ್ರಿಕೆ +Comment[ko]=새 하드 디스크 +Comment[ku]=Hard Dîska Nû +Comment[lt]=Naujas kietas diskas +Comment[lv]=Jauns cietais disks +Comment[mai]=नव हार्ड डिस्क +Comment[mk]=Нов тврд диск +Comment[ml]=പുതിയ ഹാര്‍ഡ് ഡിസ്ക് ഉപകരണം +Comment[mr]=नवीन हार्ड डिस्क +Comment[ms]=Cakera Keras Baru +Comment[nb]=Ny harddisk +Comment[nds]=Niege Fastplaat +Comment[nl]=Nieuwe harde schijf +Comment[nn]=Ny harddisk +Comment[or]=ନୂତନ ହାର୍ଡ଼ଡ଼ିସ୍କ +Comment[pa]=ਨਵੀਂ ਹਾਰਡ ਡਿਸਕ +Comment[pl]=Nowy twardy dysk +Comment[pt]=Novo Disco Rígido +Comment[pt_BR]=Novo disco rígido +Comment[ro]=Disc fix nou +Comment[ru]=Ссылка на устройство жёсткого диска +Comment[se]=Ođđa garraskearroovttadat +Comment[si]=නව දෘඩ තැටි උපාංගය +Comment[sk]=Nový pevný disk +Comment[sl]=Nov trdi disk +Comment[sr]=Нови хард‑диск +Comment[sr@ijekavian]=Нови хард‑диск +Comment[sr@ijekavianlatin]=Novi hard‑disk +Comment[sr@latin]=Novi hard‑disk +Comment[sv]=Ny hårddisk +Comment[ta]=புது வன் தகடு +Comment[te]=కొత్త హార్డ్ డిస్క్ +Comment[tg]=Диски компютерии нав +Comment[th]=อุปกรณ์ฮาร์ดดิสก์ตัวใหม่ +Comment[tr]=Yeni Sabit Disk +Comment[ug]=يېڭى قاتتىق دىسكا +Comment[uk]=Новий жорсткий диск +Comment[uz]=Yangi qattiq disk uskunasi +Comment[uz@cyrillic]=Янги қаттиқ диск ускунаси +Comment[vi]=Thiết bị đĩa cứng mới +Comment[wa]=Novele deure plake +Comment[xh]=Hard Disc Entsha +Comment[x-test]=xxNew Hard Discxx +Comment[zh_CN]=新建硬盘 +Comment[zh_TW]=新的硬碟機裝置 +Type=Link +URL=.source/HD.desktop +Icon=drive-harddisk diff --git a/libs/konq/Templates/linkMO.desktop b/libs/konq/Templates/linkMO.desktop new file mode 100644 index 00000000..f2e2a3bf --- /dev/null +++ b/libs/konq/Templates/linkMO.desktop @@ -0,0 +1,176 @@ +[Desktop Entry] +Name=MO Device... +Name[af]=MO Toestel... +Name[ar]=الجهاز MO ... +Name[as]=MO যন্ত্ৰ... +Name[ast]=Preseos MO... +Name[be]=Прылада MO... +Name[be@latin]=Pryłada MO... +Name[bg]=MO... +Name[bn]=MO ডিভাইস... +Name[bn_IN]=MO ডিভাইস... +Name[br]=Trobarzhell MO ... +Name[bs]=Uređaj: ⁠MO... +Name[ca]=Dispositiu MO... +Name[ca@valencia]=Dispositiu MO... +Name[cs]=MO zařízení... +Name[csb]=Nëk MO... +Name[cy]=Dyfais MO... +Name[da]=MO-enhed... +Name[de]=MO-Laufwerk ... +Name[el]=Συσκευή MO... +Name[en_GB]=MO Device... +Name[eo]=Magnetdiskingo... +Name[es]=Dispositivo MO... +Name[et]=MO seade... +Name[eu]=MO gailua... +Name[fi]=MO-laite… +Name[fr]=Périphérique MO... +Name[fy]=Magnetyske skiif stasjon... +Name[ga]=Gléas MO... +Name[gl]=Dispositivo MO... +Name[gu]=MO ઉપકરણ... +Name[he]=התקן MO... +Name[hi]=एमओ उपकरण... +Name[hne]=एमओ औजार... +Name[hr]=MO uređaj… +Name[hsb]=Magnetooptiski grat... +Name[hu]=Magnetooptikai eszköz… +Name[ia]=Dispositivo MO... +Name[id]=Divais MO... +Name[is]=MO tæki... +Name[it]=Dispositivo MO... +Name[ja]=MO デバイス... +Name[ka]=MO მოწყობილობა +Name[kk]=MO құрылғы... +Name[km]=ឧបករណ៍ MO... +Name[kn]=MOಸಾಧನ... +Name[ko]=MO 장치... +Name[ku]=Cîhaza MO... +Name[lt]=MO įrenginys... +Name[lv]=MO ierīce... +Name[mai]=MO युक्ति... +Name[mk]=MO Уред... +Name[ml]=എംഒ ഉപകരണം... +Name[mr]=MO साधन... +Name[ms]=Peranti MO... +Name[nb]=MO-enhet … +Name[nds]=MO-Reedschap... +Name[nl]=MO-apparaat... +Name[nn]=MO-eining … +Name[or]=MO ଉପକରଣ... +Name[pa]=MO ਜੰਤਰ... +Name[pl]=Napęd magnetooptyczny... +Name[pt]=Dispositivo MO... +Name[pt_BR]=Dispositivo MO... +Name[ro]=Dispozitiv MO... +Name[ru]=Магнитооптическое устройство... +Name[se]=MO-ovttadat … +Name[si]=MO උපාංගය... +Name[sk]=Zariadenie MO... +Name[sl]=Naprava MO ... +Name[sr]=⁠МО... +Name[sr@ijekavian]=⁠МО... +Name[sr@ijekavianlatin]=⁠MO... +Name[sr@latin]=⁠MO... +Name[sv]=MO-enhet... +Name[ta]=MO சாதனம்... +Name[te]=ఎంఓ పరికరం... +Name[tg]=Дастгоҳи MO... +Name[th]=อุปกรณ์ MO... +Name[tr]=MO Aygıtı... +Name[ug]=MO ئۈسكۈنىسى… +Name[uk]=Пристрій MO... +Name[uz]=MO uskunasi +Name[uz@cyrillic]=MO ускунаси +Name[vi]=Thiết bị MO... +Name[wa]=Éndjin MO... +Name[x-test]=xxMO Device...xx +Name[zh_CN]=MO 设备... +Name[zh_TW]=MO 裝置... +Comment=New MO Device +Comment[af]=Nuwe MO Toestel... +Comment[ar]=جهاز MO جديد +Comment[as]=নতুন MO যন্ত্ৰ +Comment[ast]=Nuevu preséu MO +Comment[be]=Новая магнітааптычная прылада +Comment[be@latin]=Novaja pryłada MO +Comment[bg]=Ново MO устройство +Comment[bn]=নতুন MO ডিভাইস +Comment[bn_IN]=নতুন MO ডিভাইস +Comment[bs]=Novi uređaj, ⁠MO +Comment[ca]=Dispositiu MO nou +Comment[ca@valencia]=Dispositiu MO nou +Comment[cs]=Nové MO zařízení +Comment[csb]=Nowi nëk MO +Comment[da]=Ny MO-enhed +Comment[de]=Neues MO-Laufwerk +Comment[el]=Νέα συσκευή MO +Comment[en_GB]=New MO Device +Comment[eo]=Nova magnetdiska aparato +Comment[es]=Nuevo dispositivo MO +Comment[et]=Uus MO seade +Comment[eu]=MO gailu berria +Comment[fi]=Uusi MO-laite +Comment[fr]=Nouveau périphérique MO +Comment[fy]=Nije magnetyske skiif stasjon +Comment[ga]=Gléas Nua MO +Comment[gl]=Novo dispositivo MO +Comment[gu]=નવું MO ઉપકરણ +Comment[he]=התקן MO חדש +Comment[hi]=नया एमओ उपकरण +Comment[hne]=नवा एमओ उपकरन +Comment[hr]=Novi MO uređaj +Comment[hsb]=Nowy magnetooptiski grat +Comment[hu]=Új magnetooptikai meghajtó +Comment[ia]=Nove dispositivo MO +Comment[id]=Divais MO Baru +Comment[is]=Nýtt MO-drif +Comment[it]=Nuovo dispositivo MO +Comment[ja]=新しい MO デバイス +Comment[kk]=Жаңа MO +Comment[km]=ឧបករណ៍ MO ថ្មី +Comment[kn]=ಹೊಸ MO ಸಾಧನ +Comment[ko]=새 MO 장치 +Comment[ku]=Cihaza MO ya Nû +Comment[lt]=Naujas MO įrenginys +Comment[lv]=Jauna MO ierīce +Comment[mai]=नव MO युक्ति +Comment[mk]=Нов MO уред +Comment[ml]=പുതിയ എംഒ ഉപകരണം... +Comment[mr]=नवीन MO साधन +Comment[ms]=Peranti MO Baru +Comment[nb]=Ny MO-enhet +Comment[nds]=Niege MO-Reedschap +Comment[nl]=Nieuw MO-apparaat +Comment[nn]=Ny MO-eining +Comment[or]=ନୂତନ MO ଉପକରଣ +Comment[pa]=ਨਵਾਂ MO ਜੰਤਰ +Comment[pl]=Nowy napęd MO +Comment[pt]=Novo Dispositivo MO +Comment[pt_BR]=Novo dispositivo MO +Comment[ro]=Dispozitiv MO nou +Comment[ru]=Ссылка на магнитооптическое устройство +Comment[se]=Ođđa MO-ovttadat +Comment[si]=නව MO උපාංගය +Comment[sk]=Nové zariadenie MO +Comment[sl]=Nova naprava MO +Comment[sr]=Нови ⁠МО +Comment[sr@ijekavian]=Нови ⁠МО +Comment[sr@ijekavianlatin]=Novi ⁠MO +Comment[sr@latin]=Novi ⁠MO +Comment[sv]=Ny MO-enhet +Comment[ta]=புதிய MO கருவி +Comment[te]=కొత్త ఎంఓ పరికరం +Comment[tg]=Дастгоҳи MO-и нав +Comment[th]=อุปกรณ์ MO ตัวใหม่ +Comment[tr]=Yeni MO Aygıtı +Comment[ug]=يېڭى MO ئۈسكۈنىسى +Comment[uk]=Новий пристрій MO +Comment[wa]=Novea éndjin MO +Comment[x-test]=xxNew MO Devicexx +Comment[zh_CN]=新建 MO 设备 +Comment[zh_TW]=新的 MO 裝置 +Type=Link +URL=.source/MO-Device.desktop +Icon=media-floppy diff --git a/libs/konq/Templates/linkNFS.desktop b/libs/konq/Templates/linkNFS.desktop new file mode 100644 index 00000000..70da92e2 --- /dev/null +++ b/libs/konq/Templates/linkNFS.desktop @@ -0,0 +1,183 @@ +[Desktop Entry] +Name=NFS... +Name[af]=NFS... +Name[ar]=NFS... +Name[as]=NFS... +Name[ast]=NFS... +Name[be]=NFS... +Name[be@latin]=NFS... +Name[bg]=NFS... +Name[bn]=এন-এফ-এস (NFS)... +Name[bn_IN]=NFS... +Name[br]=NFS ... +Name[bs]=NFS... +Name[ca]=NFS... +Name[ca@valencia]=NFS... +Name[cs]=NFS... +Name[csb]=NFS ... +Name[cy]=NFS... +Name[da]=NFS... +Name[de]=NFS ... +Name[el]=NFS... +Name[en_GB]=NFS... +Name[eo]=NFS... +Name[es]=NFS... +Name[et]=NFS... +Name[eu]=NFS... +Name[fi]=NFS… +Name[fr]=NFS... +Name[fy]=NFS... +Name[ga]=NFS... +Name[gl]=NFS... +Name[gu]=NFS... +Name[he]=NFS... +Name[hi]=एनएफ़एस... +Name[hne]=एनएफएस... +Name[hr]=NFS… +Name[hsb]=NFS... +Name[hu]=NFS… +Name[ia]=NFS... +Name[id]=NFS... +Name[is]=NFS... +Name[it]=NFS... +Name[ja]=NFS... +Name[ka]=NFS... +Name[kk]=NFS... +Name[km]=NFS... +Name[kn]=ಎನ್ ಎಫ್ ಎಸ್... +Name[ko]=NFS... +Name[ku]=NFS... +Name[lt]=NFS... +Name[lv]=NFS... +Name[mai]=NFS... +Name[mk]=NFS... +Name[ml]=എന്‍എഫ്എസ്... +Name[mr]=NFS... +Name[ms]=NFS... +Name[nb]=NFS … +Name[nds]=NFS... +Name[nl]=NFS... +Name[nn]=NFS … +Name[oc]=NFS... +Name[or]=NFS... +Name[pa]=NFS... +Name[pl]=NFS... +Name[pt]=NFS... +Name[pt_BR]=NFS... +Name[ro]=NFS... +Name[ru]=Общая папка NFS... +Name[se]=NFS … +Name[si]=NFS... +Name[sk]=NFS... +Name[sl]=NFS ... +Name[sr]=Удаљени НФС... +Name[sr@ijekavian]=Удаљени НФС... +Name[sr@ijekavianlatin]=Udaljeni NFS... +Name[sr@latin]=Udaljeni NFS... +Name[sv]=NFS... +Name[ta]=NFS... +Name[te]=ఎన్ ఎఫ్ ఎస్... +Name[tg]=NFS... +Name[th]=บริการ NFS... +Name[tr]=NFS... +Name[ug]=NFS… +Name[uk]=NFS... +Name[uz]=NFS... +Name[uz@cyrillic]=NFS... +Name[vi]=NFS... +Name[wa]=NFS... +Name[x-test]=xxNFS...xx +Name[zh_CN]=NFS... +Name[zh_TW]=NFS... +Comment=New NFS Link +Comment[af]=Nuwe NFS Skakel +Comment[ar]=وصلة NFS جديدة +Comment[as]=নতুন NFS সংযোগ +Comment[ast]=Nuevu enllaz NFS +Comment[be]=Новая спасылка на NFS +Comment[be@latin]=Novy łuč NFS +Comment[bg]=Нова NFS връзка +Comment[bn]=নতুন এন-এফ-এস লিঙ্ক +Comment[bn_IN]=নতুন NFS লিঙ্ক +Comment[br]=Liamm NFS Nevez +Comment[bs]=Nova veza do NFS‑a +Comment[ca]=Enllaç NFS nou +Comment[ca@valencia]=Enllaç NFS nou +Comment[cs]=Nový odkaz na NFS +Comment[csb]=Nowi lënk NFS +Comment[cy]=Cyswllt NFS Newydd +Comment[da]=Nyt NFS-link +Comment[de]=Neue NFS-Verknüpfung +Comment[el]=Νέος σύνδεσμος NFS +Comment[en_GB]=New NFS Link +Comment[eo]=Nova NFS-ligilo +Comment[es]=Nuevo enlace NFS +Comment[et]=Uus NFS viit +Comment[eu]=NFS esteka berria +Comment[fi]=Uusi NFS-linkki +Comment[fr]=Nouveau lien NFS +Comment[fy]=Nije NFS-Keppeling +Comment[ga]=Nasc Nua NFS +Comment[gl]=Nova ligazón NFS +Comment[gu]=નવી NFS કડી +Comment[he]=קישור NFS חדש +Comment[hi]=नया एनएफ़एस लिंक +Comment[hne]=नवा एनएफएस संकली +Comment[hr]=Nova NFS veza +Comment[hsb]=Nowy NFS-wotkaz +Comment[hu]=Új NFS-link +Comment[ia]=Nove ligamine NFS +Comment[id]=Tautan NFS Baru +Comment[is]=Ný NFS tenging +Comment[it]=Nuovo collegamento NFS +Comment[ja]=新しい NFS リンク +Comment[ka]=ახალი NFS ბმული +Comment[kk]=Жаңа NFS сілтемесі +Comment[km]=តំណ NFS ថ្មី +Comment[kn]=ಹೊಸ ಎನ್ ಎಫ್ ಎಸ್ ಕೊಂಡಿ (ಲಿಂಕ್) +Comment[ko]=새 NFS 연결 +Comment[ku]=Lînka NFS a Nû +Comment[lt]=Nauja NFS nuoroda +Comment[lv]=Jauna NFS saite +Comment[mai]=नव एनएफएस लिंक +Comment[mk]=Нова NFS врска +Comment[ml]=പുതിയ എന്‍എഫ്എസ് കണ്ണി +Comment[mr]=नवीन NFS लिंक +Comment[ms]=Pautan NFS Baru +Comment[nb]=Ny NFS-lenke +Comment[nds]=Niegen NFS-Link +Comment[nl]=Nieuwe NFS-verbinding +Comment[nn]=Ny NFS-lenkje +Comment[or]=ନୂତନ NFS ସଂଯୋଗ +Comment[pa]=ਨਵਾਂ NFS ਲਿੰਕ +Comment[pl]=Nowy skrót do NFS... +Comment[pt]=Nova Ligação por NFS +Comment[pt_BR]=Novo link NFS +Comment[ro]=Legătură NFS nouă +Comment[ru]=Ссылка на общую папку NFS +Comment[se]=Ođđa NFS-liŋka +Comment[si]=නව NFS සබැඳියක් +Comment[sk]=Nový odkaz NFS +Comment[sl]=Nova povezava NFS +Comment[sr]=Нова веза до НФС‑а +Comment[sr@ijekavian]=Нова веза до НФС‑а +Comment[sr@ijekavianlatin]=Nova veza do NFS‑a +Comment[sr@latin]=Nova veza do NFS‑a +Comment[sv]=Ny NFS-länk +Comment[ta]=புது NFS இணைப்பு +Comment[te]=కొత్త ఎన్ ఎఫ్ ఎస్ లింక్ +Comment[tg]=Алоқаи NFS-и нав +Comment[th]=เชื่อมโยงตัวใหม่ไปยังบริการ NFS +Comment[tr]=Yeni NFS Bağlantısı +Comment[ug]=يېڭى NFS ئۇلانمىسى +Comment[uk]=Нове посилання NFS +Comment[uz]=NFS bilan yangi bogʻ +Comment[uz@cyrillic]=NFS билан янги боғ +Comment[vi]=Liên kết NFS mới +Comment[wa]=Novea loyén NFS +Comment[x-test]=xxNew NFS Linkxx +Comment[zh_CN]=新建 NFS 链接 +Comment[zh_TW]=新的 NFS 連結 +Type=Link +URL=.source/NFS.desktop +Icon=folder-remote diff --git a/libs/konq/Templates/linkPath.desktop b/libs/konq/Templates/linkPath.desktop new file mode 100644 index 00000000..8ea87c0b --- /dev/null +++ b/libs/konq/Templates/linkPath.desktop @@ -0,0 +1,140 @@ +[Desktop Entry] +Name=Basic link to file or directory... +Name[ar]=وصلة بسيطة إلى ملف أو مجلّد... +Name[ast]=Enllaz básicu a ficheru o carpeta... +Name[bg]=Основна препратка към файл или папка... +Name[bs]=Osnovna veza do datoteke ili fascikle... +Name[ca]=Enllaç bàsic a fitxer o directori... +Name[ca@valencia]=Enllaç bàsic a fitxer o directori... +Name[cs]=Základní odkaz na soubor nebo adresář... +Name[csb]=Spòdlowi lënk do lopka abò kataloga... +Name[da]=Basalt link til fil eller mappe... +Name[de]=Einfache Verknüpfung zu Datei oder Ordner ... +Name[el]=Βασικός σύνδεσμος σε αρχείο ή κατάλογο... +Name[en_GB]=Basic link to file or directory... +Name[eo]=Baza ligo al dosiero aŭ dosierujo... +Name[es]=Enlace básico a archivo o carpeta... +Name[et]=Lihtlink failile või kataloogile... +Name[eu]=Oinarrizko esteka fitxategi edo karpetara... +Name[fi]=Peruslinkki tiedostoon tai kansioon… +Name[fr]=Lien standard vers un fichier ou un dossier... +Name[fy]=Basis keppeling nei triem of triemtafel +Name[ga]=Nasc bunúsach le comhad nó comhadlann... +Name[gl]=Ligazón básica a un ficheiro ou cartafol... +Name[gu]=ફાઈલ અથવા ડિરેક્ટરી સાથે સામાન્ય કડી... +Name[he]=קישור בסיסי לקובץ או ספריה... +Name[hi]=फ़ाइल या डॉयरेक्ट्री को मूल कडी... +Name[hr]=Osnovni link do datoteke ili direktorija… +Name[hu]=Egyszerű link fájlra vagy könyvtárra… +Name[ia]=Ligamine basic a file o directorio +Name[id]=Tautan dasar ke berkas atau direktori... +Name[is]=Grunntengill við skrá eða möppu... +Name[it]=Collegamento di base a file o cartella... +Name[ja]=ファイルやフォルダへの基本的なリンク... +Name[kk]=Файл не қапшықтың негізгі сілтемесі... +Name[km]=តំណ​មូលដ្ឋាន​ទៅ​កាន់​ឯកសារ ឬ​ថត... +Name[kn]=ಕಡತ ಅಥವ ಕೋಶದ ಮೂಲಭೂತ ಕೊಂಡಿ... +Name[ko]=파일이나 디렉터리로 향한 기본 연결... +Name[lt]=Paprasta nuoroda į failą ar aplanką... +Name[lv]=Vienkārša saite uz failu vai mapi... +Name[mk]=Обична врска до датотека или папка... +Name[ml]=ഫയല്‍, തട്ടു് മുതലായവയിലേക്കുള്ള ലളിതമായ ലിങ്ക് +Name[mr]=फाईल किंवा संचयीकेस आधारभूत लिंक... +Name[nb]=Enkel lenke til fil eller mappe … +Name[nds]=Eenfach Link op Datei/Orner... +Name[nl]=Basis koppeling naar bestand of map... +Name[nn]=Enkel lenkje til fil eller mappe … +Name[pa]=ਫਾਇਲ ਜਾਂ ਡਾਇਰੈਕਟਰੀ ਲਈ ਬੇਸਿਕ ਲਿੰਕ... +Name[pl]=Podstawowy odnośnik do pliku lub katalogu... +Name[pt]=Ligação básica para um ficheiro ou pasta... +Name[pt_BR]=Link básico para arquivo ou pasta... +Name[ro]=Legătură simplă la fișier sau dosar... +Name[ru]=Символическую ссылку... +Name[si]=ගොනුව හෝ බහාළුමට මූලික ඈඳුතුව +Name[sk]=Odkaz na súbor alebo adresár... +Name[sl]=Osnovna povezava do datoteke ali mape ... +Name[sr]=Основна веза до фајла или фасцикле... +Name[sr@ijekavian]=Основна веза до фајла или фасцикле... +Name[sr@ijekavianlatin]=Osnovna veza do fajla ili fascikle... +Name[sr@latin]=Osnovna veza do fajla ili fascikle... +Name[sv]=Enkel länk till fil eller katalog... +Name[tg]=Алоқаи аслӣ ба файл ё феҳрист... +Name[th]=เชื่อมโยงแบบพื้นฐานไปยังแฟ้มหรือไดเรกทอรี... +Name[tr]=Dosya ya da dizine basit bir bağlantı... +Name[ug]=ھۆججەت ياكى مۇندەرىجىنىڭ ئاساسىي ئۇلىنىش… +Name[uk]=Базове посилання на файл або каталог... +Name[wa]=Hårdêye di båze viè on fitchî ou on ridant... +Name[x-test]=xxBasic link to file or directory...xx +Name[zh_CN]=到文件或目录的基本链接... +Name[zh_TW]=到檔案或目錄的基本連結... +Comment=Enter path of file or directory: +Comment[ar]=أدخل مسار الملف أو المجلّد: +Comment[ast]=Introduz camín del ficheru o carpeta: +Comment[bg]=Въведете път до файл или папка: +Comment[bs]=Unesite putanju datoteke ili fascikle: +Comment[ca]=Introduïu el camí del fitxer o directori: +Comment[ca@valencia]=Introduïu el camí del fitxer o directori: +Comment[cs]=Zadejte cestu k souboru nebo adresáři: +Comment[csb]=Wpiszë stegnã lopka abò kataloga: +Comment[da]=Angiv sti til fil eller mappe: +Comment[de]=Geben Sie den Pfad zu der Datei oder dem Ordner ein: +Comment[el]=Εισάγετε τη διαδρομή του αρχείου ή του φακέλου. +Comment[en_GB]=Enter path of file or directory: +Comment[eo]=Entajpu vojon de dosiero aŭ dosierujo: +Comment[es]=Introduzca la ruta del archivo o carpeta: +Comment[et]=Faili või kataloogi asukoht: +Comment[eu]=Sartu fitxategi edo direktorioaren bide-izena: +Comment[fi]=Kirjoita tiedoston tai kansion polku: +Comment[fr]=Saisissez l'emplacement du fichier ou du dossier : +Comment[fy]=Paad fan triem of triemtafel ynfiere: +Comment[ga]=Iontráil conair go dtí an comhad nó an chomhadlann: +Comment[gl]=Indique a ruta ao ficheiro ou cartafol: +Comment[gu]=ફાઈલ અથવા ડિરેક્ટરીનો માર્ગ દાખલ કરો: +Comment[he]=יש להכניס נתיב לקובץ או תיקייה: +Comment[hi]=फ़ाइल या डॉयरेक्ट्री का पथ दें: +Comment[hr]=Unesite putanju datoteke ili direktorija: +Comment[hu]=Adja meg a fájl vagy könyvtár elérési útját: +Comment[ia]=Inserta le percurso del file o directorio +Comment[id]=Masukkan alamat berkas atau direktori: +Comment[is]=Settu inn slóð á skrá eða möppu: +Comment[it]=Inserisci il percorso del file o della cartella: +Comment[ja]=ファイルまたはフォルダのパスを入力: +Comment[kk]=Файл не қапшықтың жолы: +Comment[km]=បញ្ចូល​ផ្លូវ ឬ​ឯកសារ ឬ​ថត ៖ +Comment[kn]=ಕಡತ ಅಥವಾ ಕಡತ ಕೋಶದ ಮಾರ್ಗವನ್ನು ಒದಗಿಸಿ: +Comment[ko]=파일이나 디렉터리 경로를 입력하십시오: +Comment[lt]=Įrašykite kelią iki failo ar aplanko: +Comment[lv]=Ievadiet ceļu uz failu vai mapi: +Comment[mk]=Внесете патека од датотека или папка: +Comment[ml]=ഫയല്‍ അല്ലെങ്കില്‍‍ തട്ടിലേയ്ക്കുള്ള വഴി നല്‍കുക: +Comment[mr]=फाईल किंवा संचयीकेचा मार्ग दाखल करा : +Comment[nb]=Oppgi sti til fil eller mappe: +Comment[nds]=Padd na de Datei/na den Orner: +Comment[nl]=Voer het pad in van het bestand of de map: +Comment[nn]=Skriv inn adressa til fila eller mappa: +Comment[pa]=ਫਾਇਲ ਜਾਂ ਡਾਇਰੈਕਟਰੀ ਲਈ ਪਾਥ ਦਿਓ: +Comment[pl]=Proszę podać ścieżkę do pliku lub katalogu: +Comment[pt]=Indique a localização do ficheiro ou pasta: +Comment[pt_BR]=Indique o caminho do arquivo ou pasta: +Comment[ro]=Introduceți calea fișierului sau dosarului: +Comment[ru]=Введите путь к файлу или папке: +Comment[si]=ගොනුව හෝ බහාළුමට මාර්ගය ඇතුළත් කරනෙන +Comment[sk]=Zadajte cestu k súboru alebo adresáru: +Comment[sl]=Vnesite pot do datoteke ali mape: +Comment[sr]=Унесите путању фајла или фасцикле: +Comment[sr@ijekavian]=Унесите путању фајла или фасцикле: +Comment[sr@ijekavianlatin]=Unesite putanju fajla ili fascikle: +Comment[sr@latin]=Unesite putanju fajla ili fascikle: +Comment[sv]=Skriv in sökväg till fil eller katalog +Comment[tg]=Макони файл ё феҳристро ворид кунед: +Comment[th]=ป้อนพาธของแฟ้มหรือไดเรกทอรี: +Comment[tr]=Dosya veya dizinin adresini girin: +Comment[ug]=ھۆججەت ياكى مۇندەرىجە يولىنى كىرگۈزۈڭ: +Comment[uk]=Введіть шлях до файла або каталогу: +Comment[wa]=Tapez l' tchimin d' on fitchî ou d' on ridant: +Comment[x-test]=xxEnter path of file or directory:xx +Comment[zh_CN]=输入文件或目录的路径: +Comment[zh_TW]=請輸入檔案或目錄的路徑: +URL=__CREATE_SYMLINK__ +Type=Link +Icon=emblem-symbolic-link diff --git a/libs/konq/Templates/linkProgram.desktop b/libs/konq/Templates/linkProgram.desktop new file mode 100644 index 00000000..0db3baf8 --- /dev/null +++ b/libs/konq/Templates/linkProgram.desktop @@ -0,0 +1,179 @@ +[Desktop Entry] +Name=Link to Application... +Name[af]=Skakel na Toepassing +Name[ar]=وصلة إلى تطبيق ... +Name[as]=অনুপ্ৰয়োগলৈ সংযোগ... +Name[ast]=Enllaz a aplicación... +Name[be]=Спасылка на праграму.... +Name[be@latin]=Łuč dla aplikacyi... +Name[bg]=Връзка към програма... +Name[bn]=অ্যাপলিকেশন-এ লিঙ্ক... +Name[bn_IN]=অ্যাপ্লিকেশন নির্দেশকারী লিঙ্ক... +Name[bs]=Veza do programa... +Name[ca]=Enllaç a l'aplicació... +Name[ca@valencia]=Enllaç a l'aplicació... +Name[cs]=Odkaz na aplikaci... +Name[csb]=Lënk do programë... +Name[da]=Link til program... +Name[de]=Verknüpfung zu Programm ... +Name[el]=Σύνδεσμος σε εφαρμογή... +Name[en_GB]=Link to Application... +Name[eo]=Ligilo al aplikaĵo... +Name[es]=Enlace a aplicación... +Name[et]=Viit rakendusele... +Name[eu]=Estekatu aplikazioa... +Name[fi]=Linkki sovellukseen… +Name[fr]=Lien vers une application... +Name[fy]=Keppeling nei applikaasje... +Name[ga]=Nasc le Feidhmchlár... +Name[gl]=Ligazón a un programa... +Name[gu]=કાર્યક્રમને કડી... +Name[he]=קישור ליישום... +Name[hi]=अनुप्रयोग को लिंक... +Name[hne]=अनुपरयोग बर संकली... +Name[hr]=Link na aplikaciju… +Name[hsb]=Wotkaz na aplikaciju ... +Name[hu]=Alkalmazásra mutató link… +Name[ia]=Ligamine a application +Name[id]=Tautan ke Aplikasi... +Name[is]=Tengi við forrit... +Name[it]=Collegamento ad applicazione... +Name[ja]=アプリケーションへのリンク... +Name[kk]=Қолданбаға сілтеме... +Name[km]=តភ្ជាប់​ទៅ​កម្មវិធី... +Name[kn]=ಅನ್ವಯಕ್ಕೆ ಕೊಂಡಿ... +Name[ko]=프로그램으로 향한 연결... +Name[ku]=Girêdana Sepanê... +Name[lt]=Nuoroda į programą... +Name[lv]=Saite uz programmu... +Name[mai]=अनुप्रयोगमे लिंक... +Name[mk]=Врска до апликација... +Name[ml]=പ്രയോഗത്തിലേക്കുള്ള കണ്ണി... +Name[mr]=अनुप्रयोग करिता लिंक... +Name[ms]=Paut ke Aplikasi... +Name[nb]=Lenke til program … +Name[nds]=Link na Programm... +Name[nl]=Koppeling naar programma... +Name[nn]=Lenkje til program … +Name[or]=ପ୍ରୟୋଗକୁ ସଂଯୋଗ... +Name[pa]=ਐਪਲੀਕੇਸ਼ਨ ਨਾਲ ਲਿੰਕ... +Name[pl]=Skrót do programu... +Name[pt]=Atalho para Aplicação... +Name[pt_BR]=Atalho para aplicativo... +Name[ro]=Legătură către aplicație... +Name[ru]=Ссылка на приложение... +Name[se]=Liŋka prográmmii … +Name[si]=යෙදුමට සබැඳිය... +Name[sk]=Odkaz na aplikáciu... +Name[sl]=Povezava do programa ... +Name[sr]=Веза до програма... +Name[sr@ijekavian]=Веза до програма... +Name[sr@ijekavianlatin]=Veza do programa... +Name[sr@latin]=Veza do programa... +Name[sv]=Länk till program... +Name[ta]=பயன்பாட்டுக்கான இணைப்பு.. +Name[te]=అనువర్తనంకు లింకు... +Name[tg]=Алоқа барои барнома... +Name[th]=เชื่อมโยงไปยังโปรแกรม... +Name[tr]=Uygulamaya Bağlantı... +Name[ug]=پروگرامما ئۇلىنىشى… +Name[uk]=Посилання на програму... +Name[uz]=Dastur bilan yangi bogʻ... +Name[uz@cyrillic]=Дастур билан янги боғ... +Name[wa]=Loyî å programe... +Name[x-test]=xxLink to Application...xx +Name[zh_CN]=链接到应用程序... +Name[zh_TW]=連結到應用程式... +Comment=New Link to Application +Comment[af]=Nuwe Skakel na Toepassing +Comment[ar]=وصلة جديدة إلى تطبيق +Comment[as]=অনুপ্ৰয়োগলৈ নতুন সংযোগ +Comment[ast]=Nuevu enllaz a Aplicación +Comment[be]=Новая спасылка на праграму +Comment[be@latin]=Novy łuč dla aplikacyi +Comment[bg]=Нова връзка към програма +Comment[bn]=অ্যাপলিকেশন-এ নতুন লিঙ্ক +Comment[bn_IN]=অ্যাপ্লিকেশন নির্দেশকারী নতুন লিঙ্ক +Comment[br]=Liamm nevez ouzh un arload ... +Comment[bs]=Nova veza do programa +Comment[ca]=Nou enllaç a l'aplicació +Comment[ca@valencia]=Nou enllaç a l'aplicació +Comment[cs]=Nový odkaz na aplikaci +Comment[csb]=Nowi lënk do programë +Comment[cy]=Cyswllt Newydd i Gymhwysiad +Comment[da]=Nyt link til program +Comment[de]=Neue Verknüpfung mit Programm +Comment[el]=Νέος σύνδεσμος σε εφαρμογή +Comment[en_GB]=New Link to Application +Comment[eo]=Nova ligilo al aplikaĵo +Comment[es]=Nuevo enlace a aplicación +Comment[et]=Uus viit rakendusele +Comment[eu]=Aplikazioarekiko esteka berria +Comment[fi]=Uusi linkki sovellukseen +Comment[fr]=Nouveau lien vers une application +Comment[fy]=Nije keppeling naar toepassing +Comment[ga]=Nasc Nua le Feidhmchlár +Comment[gl]=Nova ligazón a un programa +Comment[gu]=કાર્યક્રમને નવી કડી +Comment[he]=קישור חדש ליישום +Comment[hi]=अनुप्रयोग को नई लिंक +Comment[hne]=अनुपरयोग बर नवा संकली +Comment[hr]=Nova veza s aplikacijom +Comment[hsb]=Nowy wotkaz na aplikaciju +Comment[hu]=Alkalmazásra mutató új link +Comment[ia]=Nove ligamine a application +Comment[id]=Tautan Baru ke Aplikasi +Comment[is]=Nýtt tengi við forrit +Comment[it]=Nuovo collegamento ad applicazione +Comment[ja]=新しいアプリケーションへのリンク +Comment[ka]=პროგრამის ახალი ბმული +Comment[kk]=Жаңа қолданба сілтемесі +Comment[km]=តណ​ថ្មី​ទៅ​កម្មវិធី +Comment[kn]=ಅನ್ವಯಕ್ಕೆ ಹೊಸ ಕೊಂಡಿ (ಲಿಂಕ್) +Comment[ko]=프로그램으로 향한 새 연결 +Comment[ku]=Girêdana Nû a Sepanê +Comment[lt]=Nauja programos nuoroda +Comment[lv]=Jauna saite uz programmu +Comment[mai]=अनुप्रयोगकेँ नव लिंक +Comment[mk]=Нова врска до апликација +Comment[ml]=പുതിയ പ്രയോഗത്തിലേക്കുള്ള കണ്ണി... +Comment[mr]=अनुप्रयोग करिता नवीन लिंक +Comment[ms]=Pautan Baru ke Aplikasi +Comment[nb]=Ny lenke til program +Comment[nds]=Niegen Link na'n Programm +Comment[nl]=Nieuwe koppeling naar programma +Comment[nn]=Ny lenkje til program +Comment[or]=ପ୍ରୟୋଗ ପାଇଁ ନୂତନ ସଂଯୋଗ +Comment[pa]=ਐਪਲੀਕੇਸ਼ਨ ਨਾਲ ਨਵਾਂ ਲਿੰਕ +Comment[pl]=Nowy skrót do programu +Comment[pt]=Novo Atalho para Aplicação +Comment[pt_BR]=Novo atalho para aplicativo +Comment[ro]=Legătură nouă către aplicație +Comment[ru]=Создать ссылку на приложение +Comment[se]=Ođđa liŋka prográmmii +Comment[si]=භාවිත යෙදවුමට නව සබැඳිය +Comment[sk]=Nový odkaz na aplikáciu +Comment[sl]=Nova povezava do programa +Comment[sr]=Нова веза до програма +Comment[sr@ijekavian]=Нова веза до програма +Comment[sr@ijekavianlatin]=Nova veza do programa +Comment[sr@latin]=Nova veza do programa +Comment[sv]=Ny länk till program +Comment[ta]=பயன்பாட்டுக்கான புதிய இணைப்பு +Comment[te]=అనువర్తనంకు కొత్త లింకు +Comment[tg]=Алоқаи нав барои барнома... +Comment[th]=เชื่อมโยงตัวใหม่ไปยังโปรแกรม +Comment[tr]=Uygulamaya Yeni Bağlantı +Comment[ug]=پروگراممىغا يېڭى ئۇلىنىشى +Comment[uk]=Нове посилання на програму +Comment[uz]=Dastur bilan yangi bogʻ +Comment[uz@cyrillic]=Дастур билан янги боғ +Comment[vi]=Liên kết mới tới ứng dụng +Comment[wa]=Novea loyén å programe +Comment[xh]=Ikhonkco Elitsha Lwesicelo +Comment[x-test]=xxNew Link to Applicationxx +Comment[zh_CN]=新建到应用程序的链接 +Comment[zh_TW]=新增連結到應用程式 +Icon=system-run +Type=Link +URL=.source/Program.desktop diff --git a/libs/konq/Templates/linkURL.desktop b/libs/konq/Templates/linkURL.desktop new file mode 100644 index 00000000..ce63a5dd --- /dev/null +++ b/libs/konq/Templates/linkURL.desktop @@ -0,0 +1,183 @@ +[Desktop Entry] +Name=Link to Location (URL)... +Name[af]=Skakel na Ligging (URL)... +Name[ar]=وصلة إلى عنوان الموقع (URL) ... +Name[as]=স্থান (URL) লৈ সংযোগ... +Name[ast]=Enllaz a Allugamientu (URL)... +Name[be]=Спасылка на месцазнаходжанне (URL)... +Name[be@latin]=Łuč dla miesca (adrasa)... +Name[bg]=Връзка към адрес (URL)... +Name[bn]=ইউ-আর-এল অবস্থানে লিঙ্ক... +Name[bn_IN]=অবস্থান নির্দেশক লিঙ্ক (URL)... +Name[br]=Liamm ouzh ul lec'hiadur (URL) ... +Name[bs]=Veza do lokacije (URL)... +Name[ca]=Enllaç a la localització (URL)... +Name[ca@valencia]=Enllaç a la localització (URL)... +Name[cs]=Odkaz na umístění (URL)... +Name[csb]=Lënk do adresë (URL)... +Name[cy]=Cyswllt i Leoliad (URL)... +Name[da]=Link til placering (URL)... +Name[de]=Verknüpfung zu Adresse (URL) ... +Name[el]=Σύνδεσμος σε τοποθεσία (URL)... +Name[en_GB]=Link to Location (URL)... +Name[eo]=Ligilo al loko (URL)... +Name[es]=Enlace a ubicación (URL)... +Name[et]=Viit asukohale (URL)... +Name[eu]=Estekatu kokapena (URLa)... +Name[fi]=Linkki sijaintiin (URL)… +Name[fr]=Lien vers un emplacement (URL)... +Name[fy]=Keppeling nei lokaasje (URL-adres)... +Name[ga]=Nasc le Suíomh (URL)... +Name[gl]=Ligazón a un lugar (URL)... +Name[gu]=સ્થળ (URL) ને કડી +Name[he]=קישור למיקום (URL)... +Name[hi]=स्थान (यूआरएल) को लिंक... +Name[hne]=जगह (यूआरएल) बर संकली... +Name[hr]=Link na lokaciju (URL)… +Name[hsb]=Wotkaz na městno (URL)... +Name[hu]=Internet-cím (URL)… +Name[ia]=Ligamine a location (URL)... +Name[id]=Tautan ke Lokasi (URL)... +Name[is]=Staðsetningartengill... +Name[it]=Collegamento a indirizzo (URL)... +Name[ja]=場所へのリンク (URL)... +Name[ka]=ადგილმდებარეობის ბმული (URL)... +Name[kk]=Сілтеме (URL)... +Name[km]=តំណ​ទៅ​ទីតាំង (URL)... +Name[kn]=ತಾಣಕ್ಕೆ ಕೊಂಡಿ (URL)... +Name[ko]=위치로 향한 연결 (URL)... +Name[ku]=Girêdana Cî (URL)... +Name[lt]=Adreso nuoroda (URL)... +Name[lv]=Saite uz vietu (URL)... +Name[mai]=स्थान (यूआरएल) केँ लिंक... +Name[mk]=Врска до локација (URL)... +Name[ml]=സ്ഥലത്തിലേയ്ക്കുള്ള (യുആര്‍എല്‍) കണ്ണി... +Name[mr]=स्थान करिता लिंक (URL)... +Name[ms]=Paut ke Aplikasi (URL)... +Name[nb]=Lenke til adresse (URL) … +Name[nds]=Link na'n Oort (URL)... +Name[nl]=Koppeling naar locatie... +Name[nn]=Lenkje til plassering (URI) … +Name[or]=ସ୍ଥାନକୁ (URL) ସଂଯୋଗ... +Name[pa]=ਟਿਕਾਣੇ (URL) ਨਾਲ ਲਿੰਕ... +Name[pl]=Skrót do adresu (URL)... +Name[pt]=Atalho para Localização (URL)... +Name[pt_BR]=Link para localização (URL)... +Name[ro]=Legătură către adresă (URL)... +Name[ru]=Адрес в Интернете... +Name[se]=Liŋka báikái (URL) … +Name[si]=ස්ථානයට සබැඳිය (URL)... +Name[sk]=Odkaz na umiestnenie (URL)... +Name[sl]=Povezava do mesta (URL) ... +Name[sr]=Веза до локације (УРЛ)... +Name[sr@ijekavian]=Веза до локације (УРЛ)... +Name[sr@ijekavianlatin]=Veza do lokacije (URL)... +Name[sr@latin]=Veza do lokacije (URL)... +Name[sv]=Länk till plats (webbadress)... +Name[ta]=இடத்திற்கு இணை (URL)... +Name[te]=స్థానము(URL) కు లింకు... +Name[tg]=Алоқа барои суроғаи интернетӣ (URL)... +Name[th]=เชื่อมโยงไปยังที่อยู่ URL... +Name[tr]=Konuma (URL) Bağlantı... +Name[ug]=ئورۇن ئۇلىنىشى (URL)… +Name[uk]=Посилання на адресу (URL)... +Name[uz]=Mavzu bilan bogʻ (URL)... +Name[uz@cyrillic]=Мавзу билан боғ (URL)... +Name[vi]=Liên kết tới địa chỉ (URL)... +Name[wa]=Loyî a l' eplaeçmint (URL)... +Name[x-test]=xxLink to Location (URL)...xx +Name[zh_CN]=链接到地址(URL)... +Name[zh_TW]=連結到網址... +Comment=Enter link to location (URL): +Comment[af]=Invoer skakel na ligging (Url): +Comment[ar]=أدخل وصلة إلى عنوان الموقع (URL): +Comment[as]=স্থান (URL) লৈ সংযোগ দিয়ক +Comment[ast]=Introduza l'enllaz al allugamientu (URL): +Comment[be]=Вызначце спасылку на месцазнаходжанне (URL): +Comment[be@latin]=Upišy nazvu łuča dla miesca (adrasa): +Comment[bg]=Въведете връзката към адреса (URL): +Comment[bn]=লিঙ্কের অবস্থান: +Comment[bn_IN]=অবস্থান নির্দেশকারী লিঙ্ক উল্লেখ করুন (URL): +Comment[br]=Roit al liamm ouzh ul lec'hiadur (URL) : +Comment[bs]=Unesite vezu do lokacije (URL): +Comment[ca]=Introduïu un enllaç a la localització (URL): +Comment[ca@valencia]=Introduïu un enllaç a la localització (URL): +Comment[cs]=Zadejte odkaz na umístění (URL): +Comment[csb]=Wpiszë lënk do adresë (URL): +Comment[cy]=Mewnosodwch cyswllt i leoliad (URL): +Comment[da]=Angiv link til placering (URL): +Comment[de]=Verknüpfung mit Adresse (URL) eingeben: +Comment[el]=Εισάγετε το σύνδεσμο της τοποθεσίας (URL): +Comment[en_GB]=Enter link to location (URL): +Comment[eo]=Enmeti la ligilon al la loko (URL): +Comment[es]=Introduzca el enlace a la ubicación (URL): +Comment[et]=Sisesta viit asukohale (URL): +Comment[eu]=Sartu kokapenarekiko (URL) esteka: +Comment[fi]=Anna linkki sijaintiin (URL): +Comment[fr]=Nouveau lien vers un emplacement (URL) : +Comment[fy]=Keppeling nei lokaasje ynfiere (URL-adres): +Comment[ga]=Iontráil nasc le suíomh (URL): +Comment[gl]=Indique o lugar (URL): +Comment[gu]=સ્થળ (URL) માટે કડી દાખલ કરો: +Comment[he]=יש להכניס קישור למיקום (URL): +Comment[hi]=स्थान (यूआरएल) के लिए लिंक भरें: +Comment[hne]=जगह (यूआरएल) बर संकली भरव: +Comment[hr]=Nova veza s lokacijom (URL) +Comment[hsb]=Wotkaz na městno (URL) zapodać: +Comment[hu]=Adja meg az internet-címet (URL-t): +Comment[ia]=Inserta ligamine a location (URL): +Comment[id]=Masukkan tautan ke lokasi (URL): +Comment[is]=Gefðu upp slóð að staðsetningu: +Comment[it]=Inserisci il collegamento all'indirizzo (URL): +Comment[ja]=場所へのリンク (URL) を入力: +Comment[ka]=შეიყვანეთ პროგრამის ბმული ბმული (URL) +Comment[kk]=URL-сілтемесі: +Comment[km]=បញ្ចូលតំណ​ទៅ​ទីតាំង (URL) ៖ +Comment[kn]=ತಾಣಕ್ಕೆ ಕೊಂಡಿ (ಲಿಂಕ್) ಅನ್ನು ನಮೂದಿಸು(URL): +Comment[ko]=연결할 위치(URL)를 입력하십시오: +Comment[ku]=Ji bo cî (URL) girêdan têkevê: +Comment[lt]=Nauja adreso nuoroda (URL) +Comment[lv]=Jauna saite uz vietu (URL): +Comment[mai]=स्थान (यूआरएल) क लेल लिंक भरू: +Comment[mk]=Внесете врска до локацијата (URL): +Comment[ml]=പുതിയ സ്ഥലത്തിലേയ്ക്കുള്ള (യുആര്‍എല്‍) കണ്ണി... +Comment[mr]=स्थान (URL) करिता लिंक दाखल करा: +Comment[ms]=Masukkan pautan ke lokasi (URL): +Comment[nb]=Oppgi lenke til adresse (URL): +Comment[nds]=Link na'n Oort (URL) ingeven: +Comment[nl]=Koppeling naar locatie (URL-adres): +Comment[nn]=Skriv inn lenkje til plassering (URI): +Comment[or]=ସ୍ଥାନକୁ (URL) ସଂଯୋଗ ଭରଣ କରନ୍ତୁ: +Comment[pa]=ਟਿਕਾਣੇ (URL) ਲਈ ਲਿੰਕ ਦਿਓ: +Comment[pl]=Nowy skrót do adresu internetowego (URL): +Comment[pt]=Indique o atalho para a localização (URL): +Comment[pt_BR]=Indique o link para a localização (URL): +Comment[ro]=Introduceți legătura către adresă (URL): +Comment[ru]=Адрес в Интернете: +Comment[se]=Čális liŋkka báikái (URL): +Comment[si]=ස්ථානයට සබැඳිය ඇතුළු කරන්න(URL): +Comment[sk]=Zadajte odkaz na umiestnenie (URL): +Comment[sl]=Vnesite povezavo do mesta (URL): +Comment[sr]=Унесите везу до локације (УРЛ): +Comment[sr@ijekavian]=Унесите везу до локације (УРЛ): +Comment[sr@ijekavianlatin]=Unesite vezu do lokacije (URL): +Comment[sr@latin]=Unesite vezu do lokacije (URL): +Comment[sv]=Ange länk till plats (webbadress): +Comment[ta]=இடத்திற்கான இணைப்பை உள்ளிடு (URL): +Comment[te]=స్థానము (URL)కు లింకును ప్రవేశపెట్టండి: +Comment[tg]=Вориди алоқаи суроғаи интернетӣ (URL): +Comment[th]=ป้อนการเชื่อมโยงไปยังที่อยู่ URL: +Comment[tr]=Konuma bağlantıyı girin: +Comment[ug]=ئورۇن ئۇلىنىشىنى كىرگۈزۈڭ(URL): +Comment[uk]=Введіть посилання на адресу (URL): +Comment[uz]=Mavzuga bogʻni (URL) kiriting: +Comment[uz@cyrillic]=Мавзуга боғни (URL) киритинг: +Comment[vi]=Nhập liên kết tới địa chỉ (URL): +Comment[wa]=Intrez l' loyén a l' eplaeçmint (URL): +Comment[xh]=Ngenisa ikhonkco kwindawo ekuyo (URL): +Comment[x-test]=xxEnter link to location (URL):xx +Comment[zh_CN]=输入地址的链接(URL): +Comment[zh_TW]=輸入網址連結: +URL=.source/URL.desktop +Type=Link +Icon=text-html diff --git a/libs/konq/Templates/linkZIP.desktop b/libs/konq/Templates/linkZIP.desktop new file mode 100644 index 00000000..8efdeb0c --- /dev/null +++ b/libs/konq/Templates/linkZIP.desktop @@ -0,0 +1,182 @@ +[Desktop Entry] +Name=ZIP Device... +Name[af]=ZIP Toestel... +Name[ar]=جهاز ZIP ... +Name[as]=ZIP যন্ত্ৰ... +Name[ast]=Unidá ZIP... +Name[be]=Прылада ZIP... +Name[be@latin]=Pryłada ZIP... +Name[bg]=ZIP... +Name[bn]=ZIP ডিভাইস... +Name[bn_IN]=ZIP ডিভাইস... +Name[br]=Trobarzhell ZIP ... +Name[bs]=Uređaj: zip... +Name[ca]=Dispositiu ZIP... +Name[ca@valencia]=Dispositiu ZIP... +Name[cs]=ZIP zařízení... +Name[csb]=Nëk ZIP... +Name[cy]=Dyfais ZIP... +Name[da]=ZIP-enhed... +Name[de]=ZIP-Laufwerk ... +Name[el]=Συσκευή ZIP... +Name[en_GB]=ZIP Device... +Name[eo]=ZIP-ingo... +Name[es]=Unidad ZIP... +Name[et]=ZIP seade... +Name[eu]=ZIP gailua... +Name[fi]=ZIP-laite… +Name[fr]=Lecteur ZIP... +Name[fy]=ZIP-apparaat... +Name[ga]=Gléas ZIP... +Name[gl]=Dispositivo ZIP... +Name[gu]=ઝીપ ઉપકરણ... +Name[he]=התקן ZIP... +Name[hi]=जिप उपकरण... +Name[hne]=जिप उपकरन... +Name[hr]=ZIP uređaj… +Name[hsb]=ZIPnik +Name[hu]=ZIP-meghajtó… +Name[ia]=Dispositivo ZIP... +Name[id]=Divais ZIP... +Name[is]=ZIP-drif... +Name[it]=Dispositivo ZIP... +Name[ja]=ZIP デバイス... +Name[ka]=ZIP მოწყობილობა... +Name[kk]=ZIP құрылғы... +Name[km]=ឧបករណ៍ ZIP... +Name[kn]=ಜಿಪ್ ಸಾಧನ... +Name[ko]=ZIP 장치... +Name[ku]=Cîhaza ZIP... +Name[lt]=ZIP įrenginys... +Name[lv]=ZIP ierīce... +Name[mai]=जिप युक्ति... +Name[mk]=ZIP Уред... +Name[ml]=സിപ്പ് ഉപകരണം... +Name[mr]=ZIP साधन... +Name[ms]=Peranti ZIP... +Name[nb]=ZIP-enhet … +Name[nds]=ZIP-Reedschap... +Name[nl]=ZIP-station... +Name[nn]=ZIP-eining … +Name[or]=ZIP ଉପକରଣ... +Name[pa]=ZIP ਜੰਤਰ... +Name[pl]=Napęd ZIP... +Name[pt]=Dispositivo ZIP... +Name[pt_BR]=Dispositivo ZIP... +Name[ro]=Dispozitiv ZIP... +Name[ru]=ZIP... +Name[se]=ZIP-ovttadat … +Name[si]=ZIP උපාංගය... +Name[sk]=Zariadenie ZIP... +Name[sl]=Naprava ZIP ... +Name[sr]=Зип... +Name[sr@ijekavian]=Зип... +Name[sr@ijekavianlatin]=Zip... +Name[sr@latin]=Zip... +Name[sv]=Zip-enhet... +Name[ta]=ZIP சாதனங்கள் +Name[te]=జిప్ పరికరం... +Name[tg]=Дастгоҳи ZIP... +Name[th]=อุปกรณ์ดิสก์ ZIP... +Name[tr]=ZIP Aygıtı... +Name[ug]=ZIP ئۈسكۈنىسى… +Name[uk]=Пристрій ZIP... +Name[uz]=ZIP uskunasi... +Name[uz@cyrillic]=ZIP ускунаси... +Name[vi]=Thiết bị đĩa ZIP... +Name[wa]=Éndjin ZIP... +Name[x-test]=xxZIP Device...xx +Name[zh_CN]=ZIP 设备... +Name[zh_TW]=ZIP 裝置... +Comment=New ZIP Device +Comment[af]=Nuwe ZIP Toestel... +Comment[ar]=جهاز ZIP جديد +Comment[as]=নতুন ZIP যন্ত্ৰ +Comment[ast]=Nueva unidá ZIP +Comment[be]=Новая прылада ZIP +Comment[be@latin]=Novaja pryłada ZIP +Comment[bg]=Ново ZIP устройство +Comment[bn]=নতুন ZIP ডিভাইস +Comment[bn_IN]=নতুন ZIP ডিভাইস +Comment[br]=Trobarzhell ZIP Nevez +Comment[bs]=Novi uređaj, zip +Comment[ca]=Dispositiu ZIP nou +Comment[ca@valencia]=Dispositiu ZIP nou +Comment[cs]=Nové ZIP zařízení... +Comment[csb]=Nowi nëk ZIP +Comment[cy]=Dyfais ZIP Newydd +Comment[da]=Ny ZIP-enhed +Comment[de]=Neues ZIP-Laufwerk +Comment[el]=Νέα συσκευή ZIP +Comment[en_GB]=New ZIP Device +Comment[eo]=Nova ZIP-ingo +Comment[es]=Nueva unidad ZIP +Comment[et]=Uus ZIP seade +Comment[eu]=ZIP gailu berria +Comment[fi]=Uusi ZIP-laite +Comment[fr]=Nouveau lecteur ZIP +Comment[fy]=Nije ZIP-apparaat +Comment[ga]=Gléas Nua ZIP +Comment[gl]=Novo dispositivo ZIP +Comment[gu]=નવું ઝીપ ઉપકરણ +Comment[he]=התקן ZIP חדש +Comment[hi]=नया जिप उपकरण +Comment[hne]=नवा जिप उपकरन +Comment[hr]=Novi ZIP uređaj +Comment[hsb]=Nowy ZIPnik +Comment[hu]=Új ZIP-lemezes meghajtó +Comment[ia]=Nove dispositivo ZIP +Comment[id]=Divais ZIP Baru +Comment[is]=Nýtt ZIP-drif +Comment[it]=Nuovo dispositivo ZIP +Comment[ja]=新しい ZIP デバイス +Comment[ka]=ახალი ZIP მოწყობილობა +Comment[kk]=Жаңа ZIP құрылғы +Comment[km]=ឧបករណ៍ ZIP ថ្មី +Comment[kn]=ಹೊಸ ಜಿಪ್ ಸಾಧನ +Comment[ko]=새 ZIP 장치 +Comment[ku]=Cihaza Nû ya ZIP +Comment[lt]=Naujas ZIP įrenginys +Comment[lv]=Jauna ZIP ierīce +Comment[mai]=नव जिप युक्ति +Comment[mk]=Нов ZIP уред +Comment[ml]=പുതിയ സിപ്പ് ഉപകരണം +Comment[mr]=नवीन ZIP साधन +Comment[ms]=Peranti ZIP Baru +Comment[nb]=Ny ZIP-enhet +Comment[nds]=Niege ZIP-Reedschap +Comment[nl]=Nieuw ZIP-station +Comment[nn]=Ny ZIP-eining +Comment[or]=ନୂତନ ZIP ଉପକରଣ +Comment[pa]=ਨਵਾਂ ZIP ਜੰਤਰ +Comment[pl]=Nowy napęd ZIP +Comment[pt]=Novo Dispositivo ZIP +Comment[pt_BR]=Novo dispositivo ZIP +Comment[ro]=Dispozitiv ZIP nou +Comment[ru]=Ссылка на устройство ZIP +Comment[se]=Ođđa ZIP-ovttadat +Comment[si]=නව ZIP උපකරණය +Comment[sk]=Nové zariadenie ZIP +Comment[sl]=Nova naprava ZIP +Comment[sr]=Нови зип +Comment[sr@ijekavian]=Нови зип +Comment[sr@ijekavianlatin]=Novi Zip +Comment[sr@latin]=Novi Zip +Comment[sv]=Ny Zip-enhet +Comment[ta]=புதிய ZIP சாதனம் +Comment[te]=కొత్త జిప్ పరికరం +Comment[tg]=Дастгоҳи ZIP-и нав +Comment[th]=อุปกรณ์ดิสก์ ZIP ตัวใหม่ +Comment[tr]=Yeni ZIP Aygıtı +Comment[ug]=يېڭى ZIP ئۈسكۈنىسى +Comment[uk]=Новий пристрій ZIP +Comment[uz]=Yangi ZIP uskunasi +Comment[uz@cyrillic]=Янги ZIP ускунаси +Comment[vi]=Thiết bị đĩa ZIP mới +Comment[wa]=Novea éndjin ZIP +Comment[x-test]=xxNew ZIP Devicexx +Comment[zh_CN]=新建 ZIP 设备 +Comment[zh_TW]=新的 ZIP 裝置 +Type=Link +URL=.source/ZIP-Device.desktop +Icon=media-floppy diff --git a/libs/konq/directory_bookmarkbar.desktop b/libs/konq/directory_bookmarkbar.desktop new file mode 100644 index 00000000..725822cb --- /dev/null +++ b/libs/konq/directory_bookmarkbar.desktop @@ -0,0 +1,95 @@ +[Desktop Entry] +Icon=bookmark-toolbar +Name=Bookmark Toolbar +Name[af]=Boekmerk Nutsbalk +Name[ar]=شريط أدوات العلامات +Name[as]=পত্ৰচিহ্নৰ টুলবাৰ +Name[ast]=Barra de ferramientes de marcadores +Name[be]=Панель закладак +Name[be@latin]=Panel zakładak +Name[bg]=Лента с отметки +Name[bn]=বুকমার্ক টুলবার +Name[bn_IN]=বুকমার্ক টুল-বার +Name[br]=Barrenn ostilhoù sined +Name[bs]=Traka sa obeleživačima +Name[ca]=Barra d'eines de punts +Name[ca@valencia]=Barra d'eines de punts +Name[cs]=Lišta se záložkami +Name[csb]=Lëstew załóżków +Name[cy]=Bar Offer Nodau Tudalen +Name[da]=Bogmærkeværktøjslinje +Name[de]=Lesezeichenleiste +Name[el]=Γραμμή εργαλείων Σελιδοδεικτών +Name[en_GB]=Bookmark Toolbar +Name[eo]=Legosigna ilobreto +Name[es]=Barra de herramientas de marcadores +Name[et]=Järjehoidjate tööriistariba +Name[eu]=Laster-marken barra +Name[fa]=میله ابزار چوب الف +Name[fi]=Kirjanmerkkien työkalurivi +Name[fr]=Barre d'outils pour les signets +Name[fy]=Blêdwizerbalke +Name[ga]=Barra Uirlisí Leabharmharcanna +Name[gl]=Barra de Marcadores +Name[gu]=બુકમાર્ક સાધનપટ્ટી +Name[he]=סרגל הסימניות +Name[hi]=पसंदीदा औज़ार पट्टी +Name[hne]=निसानी औजार पट्टी +Name[hr]=Traka s oznakama +Name[hsb]=Nastrojowy pas za lubuški +Name[hu]=Könyvjelző-eszköztár +Name[ia]=Barra de instrumento de marcator de libros +Name[id]=Batang Alat Penanda +Name[is]=Bókamerkjaslá +Name[it]=Barra dei segnalibri +Name[ja]=ブックマークツールバー +Name[ka]=სანიშნეთა პანელი +Name[kk]=Бетбелгі панелі +Name[km]=របារ​ឧបករណ៍​ចំណាំ +Name[kn]=ಅಂಕನ (ಬುಕ್ಮಾರ್ಕ್) ಸಲಕರಣೆಪಟ್ಟಿ +Name[ko]=책갈피 도구 모음 +Name[ku]=Darikê Amûran a Bijareyan +Name[lt]=Žymelių įrankinė +Name[lv]=Grāmatzīmju rīkjosla +Name[mai]=पसंदीदा अओजार पट्टी +Name[mk]=Алатник за обележувачи +Name[ml]=പണിയായുധനിര +Name[mr]=ओळखचिन्ह साधनपट्टी +Name[ms]=Bar Alatan Penandabuku +Name[nb]=Verktøylinje for bokmerker +Name[nds]=Leesteken-Warktüüchbalken +Name[ne]=पुस्तकचिनो उपकरणपट्टी +Name[nl]=Bladwijzer - werkbalk +Name[nn]=Bokmerke-verktøylinje +Name[oc]=Barra dels favorits +Name[or]=ସାଧନପଟିକୁ ଚିହ୍ନଟ କରନ୍ତୁ +Name[pa]=ਬੁੱਕਮਾਰਕ ਸੰਦਪੱਟੀ +Name[pl]=Pasek zakładek +Name[pt]=Barra de Favoritos +Name[pt_BR]=Barra de ferramentas de favoritos +Name[ro]=Bară semne de carte +Name[ru]=Панель закладок +Name[se]=Girjemearkaholga +Name[si]=පොත්සලකුණු මෙවලම් තීරුව +Name[sk]=Panel záložiek +Name[sl]=Orodna vrstica zaznamkov +Name[sr]=Трака са обележивачима +Name[sr@ijekavian]=Трака са обиљеживачима +Name[sr@ijekavianlatin]=Traka sa obilježivačima +Name[sr@latin]=Traka sa obeleživačima +Name[sv]=Verktygsrad med bokmärken +Name[ta]=புத்தகக்குறிக் கருவிப்பட்டை +Name[te]=సాధనపట్టీని ఇష్టాంశముచేయుము +Name[tg]=Панели хатчӯбҳо +Name[th]=แถบเครื่องมือที่คั่นหน้า +Name[tr]=Yer İmleri Araç Çubuğu +Name[ug]=خەتكۈش قورال بالداق +Name[uk]=Панель закладок +Name[uz]=Xatchoʻp paneli +Name[uz@cyrillic]=Хатчўп панели +Name[vi]=Thanh công cụ Lưu địa chỉ +Name[wa]=Bår ås usteyes des rmåkes +Name[xh]=Ibar yesixhobo Senqaku lencwadi +Name[x-test]=xxBookmark Toolbarxx +Name[zh_CN]=书签工具栏 +Name[zh_TW]=書籤工具列 diff --git a/libs/konq/favicons/CMakeLists.txt b/libs/konq/favicons/CMakeLists.txt new file mode 100644 index 00000000..6428c086 --- /dev/null +++ b/libs/konq/favicons/CMakeLists.txt @@ -0,0 +1,31 @@ +set(kded_favicons_SRCS favicons.cpp) + +qt4_add_dbus_adaptor(kded_favicons_SRCS + org.kde.FavIcon.xml + favicons.h + FavIconsModule + favicons_adaptor + FavIconsAdaptor +) + +kde4_add_plugin(kded_favicons ${kded_favicons_SRCS}) + +target_link_libraries(kded_favicons ${KDE4_KIO_LIBS}) + + +install( + TARGETS kded_favicons + DESTINATION ${KDE4_PLUGIN_INSTALL_DIR} +) + +########### install files ############### + +install( + FILES favicons.desktop + DESTINATION ${KDE4_SERVICES_INSTALL_DIR}/kded +) + +install( + FILES org.kde.FavIcon.xml + DESTINATION ${KDE4_DBUS_INTERFACES_INSTALL_DIR} +) diff --git a/libs/konq/favicons/favicons.cpp b/libs/konq/favicons/favicons.cpp new file mode 100644 index 00000000..8c07548d --- /dev/null +++ b/libs/konq/favicons/favicons.cpp @@ -0,0 +1,312 @@ +/* This file is part of the KDE project + Copyright (C) 2001 Malte Starostik + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "favicons.h" +#include "favicons_adaptor.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +K_PLUGIN_FACTORY(FavIconsFactory, + registerPlugin(); + ) +K_EXPORT_PLUGIN(FavIconsFactory("favicons")) + +static QString portForUrl(const KUrl& url) +{ + if (url.port() > 0) { + return (QString(QLatin1Char('_')) + QString::number(url.port())); + } + return QString(); +} + +static QString simplifyURL(const KUrl &url) +{ + // splat any = in the URL so it can be safely used as a config key + QString result = url.host() + portForUrl(url) + url.path(); + for (int i = 0; i < result.length(); ++i) + if (result[i] == '=') + result[i] = '_'; + return result; +} + +static QString iconNameFromURL(const KUrl &iconURL) +{ + if (iconURL.path() == QLatin1String("/favicon.ico")) + return iconURL.host() + portForUrl(iconURL); + + QString result = simplifyURL(iconURL); + // splat / so it can be safely used as a file name + for (int i = 0; i < result.length(); ++i) + if (result[i] == '/') + result[i] = '_'; + + QString ext = result.right(4); + if (ext == QLatin1String(".ico") || ext == QLatin1String(".png") || ext == QLatin1String(".xpm")) + result.remove(result.length() - 4, 4); + + return result; +} + +struct FavIconsModulePrivate +{ + virtual ~FavIconsModulePrivate() { delete config; } + + struct DownloadInfo + { + QString hostOrURL; + bool isHost; + QByteArray iconData; + }; + QString makeIconName(const DownloadInfo& download, const KUrl& iconURL) + { + QString iconName (QLatin1String("favicons/")); + iconName += (download.isHost ? download.hostOrURL : iconNameFromURL(iconURL)); + return iconName; + } + + QMap downloads; + KUrl::List failedDownloads; + KConfig *config; + QList killJobs; + KIO::MetaData metaData; + QString faviconsDir; + QCache faviconsCache; +}; + +FavIconsModule::FavIconsModule(QObject* parent, const QList&) + : KDEDModule(parent) +{ + // create our favicons folder so that KIconLoader knows about it + d = new FavIconsModulePrivate; + d->faviconsDir = KGlobal::dirs()->saveLocation( "cache", QLatin1String("favicons/")); + d->faviconsDir.truncate(d->faviconsDir.length()-9); // Strip off "favicons/" + d->metaData.insert(QLatin1String("cache"), "reload"); + d->metaData.insert(QLatin1String("no-www-auth"), QLatin1String("true")); + d->config = new KConfig(KStandardDirs::locateLocal("data", QLatin1String("konqueror/faviconrc"))); + + new FavIconsAdaptor( this ); +} + +FavIconsModule::~FavIconsModule() +{ + delete d; +} + +static QString removeSlash(QString result) +{ + for (unsigned int i = result.length() - 1; i > 0; --i) { + if (result[i] != '/') { + result.truncate(i + 1); + break; + } + } + + return result; +} + +QString FavIconsModule::iconForUrl(const KUrl &url) +{ + if (url.host().isEmpty()) + return QString(); + + // kDebug() << url; + + const QString simplifiedURL = removeSlash(simplifyURL(url)); + QString *iconURL = d->faviconsCache[simplifiedURL]; + QString icon = (iconURL ? *iconURL : d->config->group(QString()).readEntry(simplifiedURL, QString())); + + if (!icon.isEmpty()) + icon = iconNameFromURL(KUrl(icon)); + else + icon = url.host(); + + icon = QLatin1String("favicons/") + icon; + + kDebug() << "URL:" << url << "ICON:" << icon; + + if (QFile::exists(d->faviconsDir+icon+QLatin1String(".png"))) + return icon; + + return QString(); +} + +bool FavIconsModule::isIconOld(const QString &icon) +{ + KDE_struct_stat st; + if (KDE::stat(QFile::encodeName(icon), &st) != 0) { + // kDebug() << "isIconOld" << icon << "yes, no such file"; + return true; // Trigger a new download on error + } + + // kDebug() << "isIconOld" << icon << "?"; + return (time(0) - st.st_mtime) > 604800; // arbitrary value (one week) +} + +void FavIconsModule::setIconForUrl(const KUrl &url, const KUrl &iconURL) +{ + // kDebug() << url << iconURL; + const QString simplifiedURL = simplifyURL(url); + + d->faviconsCache.insert(removeSlash(simplifiedURL), new QString(iconURL.url()) ); + + const QString iconName = QLatin1String("favicons/") + iconNameFromURL(iconURL); + const QString iconFile = d->faviconsDir + iconName + QLatin1String(".png"); + + if (!isIconOld(iconFile)) { + // kDebug() << "emit iconChanged" << false << url << iconName; + emit iconChanged(false, url.url(), iconName); + return; + } + + startDownload(url.url(), false, iconURL); +} + +void FavIconsModule::downloadHostIcon(const KUrl &url) +{ + // kDebug() << url; + const QString iconFile = d->faviconsDir + QLatin1String("favicons/") + url.host() + QLatin1String(".png"); + if (!isIconOld(iconFile)) { + // kDebug() << "not old -> doing nothing"; + return; + } + startDownload(url.host(), true, KUrl(url, QLatin1String("/favicon.ico"))); +} + +void FavIconsModule::forceDownloadHostIcon(const KUrl &url) +{ + // kDebug() << url; + KUrl iconURL = KUrl(url, QLatin1String("/favicon.ico")); + d->failedDownloads.removeAll(iconURL); // force a download to happen + startDownload(url.host(), true, iconURL); +} + +void FavIconsModule::startDownload(const QString &hostOrURL, bool isHost, const KUrl &iconURL) +{ + if (d->failedDownloads.contains(iconURL)) { + // kDebug() << iconURL << "already in failedDownloads, emitting error"; + emit error(isHost, hostOrURL, i18n("No favicon found")); + return; + } + + // kDebug() << iconURL; + KIO::Job *job = KIO::get(iconURL, KIO::NoReload, KIO::HideProgressInfo); + job->addMetaData(d->metaData); + connect(job, SIGNAL(data(KIO::Job*,QByteArray)), SLOT(slotData(KIO::Job*,QByteArray))); + connect(job, SIGNAL(result(KJob*)), SLOT(slotResult(KJob*))); + connect(job, SIGNAL(infoMessage(KJob*,QString,QString)), SLOT(slotInfoMessage(KJob*,QString))); + FavIconsModulePrivate::DownloadInfo download; + download.hostOrURL = hostOrURL; + download.isHost = isHost; + d->downloads.insert(job, download); +} + +void FavIconsModule::slotData(KIO::Job *job, const QByteArray &data) +{ + KIO::TransferJob* tjob = static_cast(job); + FavIconsModulePrivate::DownloadInfo &download = d->downloads[job]; + unsigned int oldSize = download.iconData.size(); + // Size limit. Stop downloading if the file is huge. + // Testcase (as of june 2008, at least): http://planet-soc.com/favicon.ico, 136K and strange format. + if (oldSize > 500000U) { + kWarning() << "Favicon too big, aborting download of" << tjob->url(); + d->killJobs.append(job); + QTimer::singleShot(0, this, SLOT(slotKill())); + const KUrl iconURL = tjob->url(); + d->failedDownloads.append(iconURL); + } + download.iconData.resize(oldSize + data.size()); + memcpy(download.iconData.data() + oldSize, data.data(), data.size()); +} + +void FavIconsModule::slotResult(KJob *job) +{ + KIO::TransferJob* tjob = static_cast(job); + FavIconsModulePrivate::DownloadInfo download = d->downloads[job]; + d->killJobs.removeAll(tjob); + d->downloads.remove(job); + const KUrl iconURL = tjob->url(); + QString iconName; + QString errorMessage; + if (!job->error()) + { + QBuffer buffer(&download.iconData); + buffer.open(QIODevice::ReadOnly); + QImageReader ir( &buffer ); + QSize desired( 16,16 ); + if( ir.canRead() ) { + ir.setScaledSize( desired ); + const QImage img = ir.read(); + if( !img.isNull() ) { + iconName = d->makeIconName(download, iconURL); + const QString localPath = d->faviconsDir + iconName + QLatin1String(".png"); + if( !img.save(localPath, "PNG") ) { + iconName.clear(); + errorMessage = i18n("Error saving image to %1", localPath); + kWarning() << "Error saving image to" << localPath; + } else if (!download.isHost) + d->config->group(QString()).writeEntry(removeSlash(download.hostOrURL), iconURL.url()); + } + } + } else { + errorMessage = job->errorString(); + kWarning() << "Job error" << job->errorString(); + } + if (iconName.isEmpty()) { + // kDebug() << "adding" << iconURL << "to failed downloads"; + d->failedDownloads.append(iconURL); + emit error(download.isHost, download.hostOrURL, errorMessage); + } else { + // kDebug() << "emit iconChanged" << download.isHost << download.hostOrURL << iconName; + emit iconChanged(download.isHost, download.hostOrURL, iconName); + } +} + +void FavIconsModule::slotInfoMessage(KJob *job, const QString &msg) +{ + emit infoMessage(static_cast( job )->url().url(), msg); +} + +void FavIconsModule::slotKill() +{ + // kDebug(); + Q_FOREACH(KIO::Job* job, d->killJobs) { + job->kill(); + } + d->killJobs.clear(); +} + +#include "moc_favicons.cpp" diff --git a/libs/konq/favicons/favicons.desktop b/libs/konq/favicons/favicons.desktop new file mode 100644 index 00000000..fd6389c1 --- /dev/null +++ b/libs/konq/favicons/favicons.desktop @@ -0,0 +1,146 @@ +[Desktop Entry] +Type=Service +Name=Favicons +Name[ar]=أيقونات الويب +Name[ast]=Favicons +Name[be@latin]=Ikony sajtaŭ +Name[bg]=Уеб-икони +Name[bn]=ফ্যাভ-আইকন +Name[bn_IN]=Favicons +Name[bs]=favikone +Name[ca]=Icones de web +Name[ca@valencia]=Icones de web +Name[cs]=Oblíbené ikony +Name[csb]=Faviconë (ikònczi) +Name[da]=Favicons +Name[de]=Webseitensymbole +Name[el]=Αγαπημένα εικονίδια +Name[en_GB]=Favicons +Name[eo]=Favorikonoj +Name[es]=Favicons +Name[et]=Lemmikikoonid +Name[eu]=Favicon-ak +Name[fi]=Verkkosivukuvakkeet +Name[fr]=Émoticônes +Name[fy]=Favicon-ôfbyldings +Name[ga]=Deilbhíní Ceanán +Name[gl]=Iconas de páxina web +Name[gu]=ફેવિકોન્સ +Name[he]=סמלי מועדפים +Name[hi]=फेविकॉन +Name[hne]=फेविकान +Name[hr]=Omiljene ikone +Name[hu]=Favikonok +Name[ia]=Favicons +Name[id]=Ikon Favorit +Name[is]=Veftáknmyndir (favicons) +Name[it]=Iconcine +Name[ja]=ファビコン +Name[kk]=Favicon таңбашалары +Name[km]=រូប​តំណាង​សំណព្វ +Name[kn]=ಫೆವಿಕಾನ್‌ಗಳು +Name[ko]=파비콘 +Name[ku]=Nîşanên Malperan +Name[lt]=Srities ženkliukai +Name[lv]=TīmekļaIkonas +Name[mai]=फेविकान +Name[mk]=Омилени икони +Name[ml]=ഇഷ്ടചിഹ്നങ്ങള്‍ +Name[mr]=Favicons +Name[nb]=Ikoner for favorittsteder +Name[nds]=Nettsieden-Lüttbiller +Name[nl]=Favicons +Name[nn]=Bokmerkeikon +Name[or]=Favicons +Name[pa]=ਫੇਵੀਕਾਨ +Name[pl]=Ikony witryn +Name[pt]='Favicons' +Name[pt_BR]=Favicons +Name[ro]=Pictograme favorite +Name[ru]=Значки веб-сайтов +Name[si]=Favicons +Name[sk]=Favikony +Name[sl]=Ikone zaznamkov +Name[sr]=фавиконе +Name[sr@ijekavian]=фавиконе +Name[sr@ijekavianlatin]=favikone +Name[sr@latin]=favikone +Name[sv]=Favoritikoner +Name[ta]=சின்னங்கள் +Name[tg]=Нишонаҳои саҳифаи интернетӣ +Name[th]=ไอคอนเว็บ +Name[tr]=Site Simgeleri +Name[ug]=تور بېكەت سىنبەلگە +Name[uk]=Піктограми «Вибраного» +Name[wa]=Pititès imådjetes favicons +Name[x-test]=xxFaviconsxx +Name[zh_CN]=网站图标 +Name[zh_TW]=網站圖示 +Comment=Stores website icons +Comment[ar]=يخزن أيقونات مواقع الإنترنت +Comment[ast]=Almacena iconos de sitios web +Comment[bg]=Запазване на иконки за уеб-сайтове +Comment[bs]=Skladišti ikone veb sajtova +Comment[ca]=Emmagatzema les icones dels llocs web +Comment[ca@valencia]=Emmagatzema les icones dels llocs web +Comment[cs]=Ukládá ikony stránek +Comment[da]=Gemmer webside-ikoner +Comment[de]=Webseitensymbole speichern +Comment[el]=Αποθήκευση εικονιδίων ιστοσελίδας +Comment[en_GB]=Stores website icons +Comment[es]=Almacena iconos de sitios web +Comment[et]=Veebilehekülgede ikoonide salvestamine +Comment[eu]=Webguneen ikonoak gordetzen ditu +Comment[fi]=Säilyttää verkkosivujen kuvakkeita +Comment[fr]=Enregistre les icônes des sites Internet +Comment[ga]=Stórálann sé deilbhín suíomh Gréasáin +Comment[gl]=Garda as iconas logotipo dos sitios web +Comment[gu]=વેબસાઈટના ચિહ્નો સંગ્રહ કરે છે +Comment[he]=איחסון סמלי אתרים +Comment[hi]=वेबसाइट प्रतीक सहेजता है +Comment[hr]=Sprema ikone web stranica +Comment[hu]=Weboldalak ikonjainak tárolása +Comment[ia]=Il immagazina icones del sito web +Comment[id]=Simpan ikon situs web +Comment[is]=Geymir táknmyndir vefsvæða +Comment[it]=Memorizza le icone dei siti Web +Comment[ja]=ウェブサイトのアイコンを保存 +Comment[kk]=Веб-сайт таңбашаларын сақтау орны +Comment[km]=ទុក​រូបតំណាង​របស់​បណ្ដាញ +Comment[kn]=ಜಾಲ ತಾಣಗಳ ಚಿತ್ರಾತ್ಮಕ ಸಂಕೇತ (ಐಕಾನ್)ಗಳನ್ನು ಶೇಖರಿಸುತ್ತದೆ +Comment[ko]=웹 사이트 아이콘을 저장함 +Comment[lt]=Saugo svetainės ženkliukus +Comment[lv]=Saglabā tīmekļa vietņu ikonas +Comment[mr]=वेबसाईट चिन्हे संचयीत करतो +Comment[nb]=Lagrer nettstedsikoner +Comment[nds]=Wohrt Nettsiedenlüttbiller +Comment[nl]=Slaat website pictogrammen op +Comment[nn]=Lagrar nettstadikon +Comment[pa]=ਵੈੱਬਸਾਈਟ ਆਈਕਾਨ ਸਟੋਰ ਕਰਦਾ ਹੈ +Comment[pl]=Przechowuje ikony stron WWW +Comment[pt]=Guarda os ícones das páginas Web +Comment[pt_BR]=Armazena os ícones dos sites da Web +Comment[ro]=Stochează pictogramele siturilor +Comment[ru]=Хранение значков веб-сайтов +Comment[si]=වෙබ් අඩවි අයිකන ගබඩා කර +Comment[sk]=Ukladá ikony stránok +Comment[sl]=Hrani ikone spletnih strani +Comment[sr]=Складишти иконе веб сајтова +Comment[sr@ijekavian]=Складишти иконе веб сајтова +Comment[sr@ijekavianlatin]=Skladišti ikone veb sajtova +Comment[sr@latin]=Skladišti ikone veb sajtova +Comment[sv]=Lagrar webbplatsikoner +Comment[tg]=Нишонаҳои сайтҳои интернет нигоҳ мекунад +Comment[th]=จัดเก็บไอคอนของเว็บไซต์ +Comment[tr]=Web sayfası simgelerini saklar +Comment[ug]=تور بېكەت سىنبەلگىسىنى ساقلايدۇ +Comment[uk]=Зберігання піктограм вебсайтів +Comment[wa]=Wåde les imådjetes des waibes +Comment[x-test]=xxStores website iconsxx +Comment[zh_CN]=保存网站图标 +Comment[zh_TW]=儲存網站圖示 +X-KDE-ServiceTypes=KDEDModule +X-KDE-Library=favicons +X-KDE-DBus-ModuleName=favicons +X-KDE-Kded-autoload=false +X-KDE-Kded-load-on-demand=true diff --git a/libs/konq/favicons/favicons.h b/libs/konq/favicons/favicons.h new file mode 100644 index 00000000..56bf9dbd --- /dev/null +++ b/libs/konq/favicons/favicons.h @@ -0,0 +1,130 @@ +/* This file is part of the KDE Project + Copyright (c) 2001 Malte Starostik + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef _FAVICONS_H_ +#define _FAVICONS_H_ + +#include +#include + +class KJob; +namespace KIO { class Job; } + +/** + * KDED Module to handle shortcut icons ("favicons") + * FavIconsModule implements a KDED Module that handles the association of + * URLs and hosts with shortcut icons and the icons' downloads in a central + * place. + * + * After a successful download, the D-Bus signal iconChanged() is emitted. + * It has the signature void iconChanged(bool, QString, QString); + * The first parameter is true if the icon is a "host" icon, that is it is + * the default icon for all URLs on the given host. In this case, the + * second parameter is a host name, otherwise the second parameter is the + * URL which is associated with the icon. The third parameter is the + * @ref KIconLoader friendly name of the downloaded icon, the same as + * @ref iconForUrl will from now on return for any matching URL. + * + * @short KDED Module for favicons + * @author Malte Starostik + */ +class FavIconsModule : public KDEDModule +{ + Q_OBJECT + Q_CLASSINFO("D-Bus Interface", "org.kde.FavIcon") + +public: + FavIconsModule(QObject* parent, const QList&); + virtual ~FavIconsModule(); + +public Q_SLOTS: // dbus methods, called by the adaptor + /** + * Looks up an icon name for a given URL. This function does not + * initiate any download. If no icon for the URL or its host has + * been downloaded yet, QString() is returned. + * + * @param url the URL for which the icon is queried + * @return the icon name suitable to pass to @ref KIconLoader or + * QString() if no icon for this URL was found. + */ + QString iconForUrl(const KUrl &url); + + /** + * Associates an icon with the given URL. If the icon was not + * downloaded before or the downloaded was too long ago, a + * download attempt will be started and the iconChanged() D-Bus + * signal is emitted after the download finished successfully. + * + * @param url the URL which will be associated with the icon + * @param iconURL the URL of the icon to be downloaded + */ + void setIconForUrl(const KUrl &url, const KUrl &iconURL); + /** + * Downloads the icon for a given host if it was not downloaded before + * or the download was too long ago. If the download finishes + * successfully, the iconChanged() D-Bus signal is emitted. + * + * @param url any URL on the host for which the icon is to be downloaded + */ + void downloadHostIcon(const KUrl &url); + + /** + * Downloads the icon for a given host, even if we tried very recently. + * Not recommended in the general case; only useful for explicit "update favicon" + * actions from the user. + * + * If the download finishes successfully, the iconChanged() D-Bus signal is emitted. + * + * @param url any URL on the host for which the icon is to be downloaded + */ + void forceDownloadHostIcon(const KUrl &url); + +signals: // D-Bus signals + /** + * Emitted once a new icon is available, for a host or url + */ + void iconChanged(bool isHost, QString hostOrURL, QString iconName); + /** + * Progress info while downloading an icon + */ + void infoMessage(QString iconURL, QString msg); + /** + * Emitted if an error occurred while downloading the icon for the given host or url. + * You can usually ignore this (e.g. web browsers don't need to do anything if + * no favicon was found), but this signal can be useful in some cases, e.g. + * to let keditbookmarks know that it should move on to the next bookmark. + */ + void error(bool isHost, QString hostOrURL, QString errorString); + +private: + void startDownload(const QString &, bool, const KUrl &); + bool isIconOld(const QString &); + +private Q_SLOTS: + void slotData(KIO::Job *, const QByteArray &); + void slotResult(KJob *); + void slotInfoMessage(KJob *, const QString &); + void slotKill(); + +private: + struct FavIconsModulePrivate *d; +}; + +#endif + +// vim: ts=4 sw=4 et diff --git a/libs/konq/favicons/org.kde.FavIcon.xml b/libs/konq/favicons/org.kde.FavIcon.xml new file mode 100644 index 00000000..ba20dc30 --- /dev/null +++ b/libs/konq/favicons/org.kde.FavIcon.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/konq/konq_copytomenu.cpp b/libs/konq/konq_copytomenu.cpp new file mode 100644 index 00000000..87f658df --- /dev/null +++ b/libs/konq/konq_copytomenu.cpp @@ -0,0 +1,226 @@ +/* This file is part of the KDE project + + Copyright 2008, 2009 David Faure + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as published + by the Free Software Foundation; either version 2 of the License or + ( at your option ) version 3 or, at the discretion of KDE e.V. + ( which shall act as a proxy as in section 14 of the GPLv3 ), any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "konq_copytomenu.h" +#include "konq_copytomenu_p.h" +#include "konq_operations.h" +#include +#include +#include +#include +#include +#include +#include +#include + + +KonqCopyToMenuPrivate::KonqCopyToMenuPrivate(QWidget* parentWidget) + : m_urls(), m_readOnly(false), m_parentWidget(parentWidget) +{ +} + +//// + +KonqCopyToMenu::KonqCopyToMenu() + : d(new KonqCopyToMenuPrivate()) +{ +} + +KonqCopyToMenu::KonqCopyToMenu(QWidget* parentWidget) + : d(new KonqCopyToMenuPrivate(parentWidget)) +{ +} + +KonqCopyToMenu::~KonqCopyToMenu() +{ + delete d; +} + +void KonqCopyToMenu::setItems(const KFileItemList& items) +{ + // For now we lose all the information except for the urls + // But this API is useful in case KIO can make use of this information later + // (e.g. to avoid stat'ing the source urls) + Q_FOREACH(const KFileItem& item, items) + d->m_urls.append(item.url()); +} + +void KonqCopyToMenu::setUrls(const KUrl::List& urls) +{ + d->m_urls = urls; +} + +void KonqCopyToMenu::setReadOnly(bool ro) +{ + d->m_readOnly = ro; +} + +void KonqCopyToMenu::addActionsTo(QMenu* menu) +{ + KMenu* mainCopyMenu = new KonqCopyToMainMenu(menu, d, Copy); + mainCopyMenu->setTitle(i18nc("@title:menu", "Copy To")); + mainCopyMenu->menuAction()->setObjectName( QLatin1String("copyTo_submenu" )); // for the unittest + menu->addMenu(mainCopyMenu); + + if (!d->m_readOnly) { + KMenu* mainMoveMenu = new KonqCopyToMainMenu(menu, d, Move); + mainMoveMenu->setTitle(i18nc("@title:menu", "Move To")); + mainMoveMenu->menuAction()->setObjectName( QLatin1String("moveTo_submenu" )); // for the unittest + menu->addMenu(mainMoveMenu); + } +} + +//// + +KonqCopyToMainMenu::KonqCopyToMainMenu(QMenu* parent, KonqCopyToMenuPrivate* _d, MenuType menuType) + : KMenu(parent), m_menuType(menuType), + m_actionGroup(static_cast(0)), + d(_d), + m_recentDirsGroup(KGlobal::config(), m_menuType == Copy ? "kuick-copy" : "kuick-move") +{ + connect(this, SIGNAL(aboutToShow()), SLOT(slotAboutToShow())); + connect(&m_actionGroup, SIGNAL(triggered(QAction*)), SLOT(slotTriggered(QAction*))); +} + +void KonqCopyToMainMenu::slotAboutToShow() +{ + clear(); + KonqCopyToDirectoryMenu* subMenu; + // Home Folder + subMenu = new KonqCopyToDirectoryMenu(this, this, QDir::homePath()); + subMenu->setTitle(i18nc("@title:menu", "Home Folder")); + subMenu->setIcon(KIcon("go-home")); + addMenu(subMenu); + + // Root Folder + subMenu = new KonqCopyToDirectoryMenu(this, this, QDir::rootPath()); + subMenu->setTitle(i18nc("@title:menu", "Root Folder")); + subMenu->setIcon(KIcon("folder-red")); + addMenu(subMenu); + + // Browse... action, shows a KFileDialog + KAction* browseAction = new KAction(i18nc("@title:menu in Copy To or Move To submenu", "Browse..."), this); + connect(browseAction, SIGNAL(triggered()), this, SLOT(slotBrowse())); + addAction(browseAction); + + addSeparator(); // looks like Qt4 handles removing it automatically if it's last in the menu, nice. + + // Recent Destinations + const QStringList recentDirs = m_recentDirsGroup.readPathEntry("Paths", QStringList()); + Q_FOREACH(const QString& recentDir, recentDirs) { + const KUrl url(recentDir); + const QString text = KStringHandler::csqueeze(url.pathOrUrl(), 60); // shorten very long paths (#61386) + KAction* act = new KAction(text, this); + act->setData(url); + m_actionGroup.addAction(act); + addAction(act); + } +} + +void KonqCopyToMainMenu::slotBrowse() +{ + const KUrl dest = KFileDialog::getExistingDirectoryUrl(KUrl("kfiledialog:///copyto"), + d->m_parentWidget ? d->m_parentWidget : this); + if (!dest.isEmpty()) { + copyOrMoveTo(dest); + } +} + +void KonqCopyToMainMenu::slotTriggered(QAction* action) +{ + const KUrl url = action->data().value(); + Q_ASSERT(!url.isEmpty()); + copyOrMoveTo(url); +} + +void KonqCopyToMainMenu::copyOrMoveTo(const KUrl& dest) +{ + // Insert into the recent destinations list + QStringList recentDirs = m_recentDirsGroup.readPathEntry("Paths", QStringList()); + const QString niceDest = dest.pathOrUrl(); + if (!recentDirs.contains(niceDest)) { // don't change position if already there, moving stuff is bad usability + recentDirs.prepend(niceDest); + while (recentDirs.size() > 10) { // hardcoded max size + recentDirs.removeLast(); + } + m_recentDirsGroup.writePathEntry("Paths", recentDirs); + } + + // #199549: add a trailing slash to avoid unexpected results when the + // dest doesn't exist anymore: it was creating a file with the name of + // the now non-existing dest. + KUrl dirDest = dest; + dirDest.adjustPath(KUrl::AddTrailingSlash); + + // And now let's do the copy or move -- with undo/redo support. + KonqOperations::copy(d->m_parentWidget ? d->m_parentWidget : this, + m_menuType == Copy ? KonqOperations::COPY : KonqOperations::MOVE, + d->m_urls, dirDest); +} + +//// + +KonqCopyToDirectoryMenu::KonqCopyToDirectoryMenu(QMenu* parent, KonqCopyToMainMenu* mainMenu, const QString& path) + : KMenu(parent), m_mainMenu(mainMenu), m_path(path) +{ + connect(this, SIGNAL(aboutToShow()), SLOT(slotAboutToShow())); +} + +void KonqCopyToDirectoryMenu::slotAboutToShow() +{ + clear(); + KAction* act = new KAction(m_mainMenu->menuType() == Copy + ? i18nc("@title:menu", "Copy Here") + : i18nc("@title:menu", "Move Here"), this); + act->setData(KUrl(m_path)); + act->setEnabled(QFileInfo(m_path).isWritable()); + m_mainMenu->actionGroup().addAction(act); + addAction(act); + + addSeparator(); // looks like Qt4 handles removing it automatically if it's last in the menu, nice. + + // List directory + // All we need is sub folder names, their permissions, their icon. + // KDirLister or KIO::listDir would fetch much more info, and would be async, + // and we only care about local directories so we use QDir directly. + QDir dir(m_path); + const QStringList entries = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot, QDir::LocaleAware); + KMimeType::Ptr dirMime = KMimeType::mimeType("inode/directory"); + Q_FOREACH(const QString& subDir, entries) { + QString subPath = m_path; + if (!subPath.endsWith('/')) + subPath.append('/'); + subPath += subDir; + KonqCopyToDirectoryMenu* subMenu = new KonqCopyToDirectoryMenu(this, m_mainMenu, subPath); + QString menuTitle(subDir); + // Replace '&' by "&&" to make sure that '&' inside the directory name is displayed + // correctly and not misinterpreted as an indicator for a keyboard shortcut + subMenu->setTitle(menuTitle.replace('&', "&&")); + const QString iconName = dirMime->iconName(KUrl(subPath)); + subMenu->setIcon(KIcon(iconName)); + if (QFileInfo(subPath).isSymLink()) { // I hope this isn't too slow... + QFont font = subMenu->menuAction()->font(); + font.setItalic(true); + subMenu->menuAction()->setFont(font); + } + addMenu(subMenu); + } +} diff --git a/libs/konq/konq_copytomenu.h b/libs/konq/konq_copytomenu.h new file mode 100644 index 00000000..6e5bb852 --- /dev/null +++ b/libs/konq/konq_copytomenu.h @@ -0,0 +1,88 @@ +/* This file is part of the KDE project + Copyright 2008 David Faure + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as published + by the Free Software Foundation; either version 2 of the License or + ( at your option ) version 3 or, at the discretion of KDE e.V. + ( which shall act as a proxy as in section 14 of the GPLv3 ), any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KONQ_COPYTOMENU_H +#define KONQ_COPYTOMENU_H + +#include +#include +#include + +#include +class KonqCopyToMenuPrivate; + +/** + * This class adds "Copy To" and "Move To" submenus to a popupmenu. + */ +class KONQ_EXPORT KonqCopyToMenu +{ +public: + /** + * Creates a KonqCopyToMenu instance. + * Note that this instance must stay alive for at least as long as the popupmenu; + * it has the slots for the actions created by addActionsTo. + * @deprecated + * KDE5: remove, so that passing a parent widget is mandatory + */ + KonqCopyToMenu(); + + /** + * Creates a KonqCopyToMenu instance, with a parent widget for the file dialog + * and message boxes. + * Note that this instance (and the widget) must stay alive for at least as + * long as the popupmenu; it has the slots for the actions created by addActionsTo. + * @param widget note that this is not the parent of KonqCopyToMenu itself. + * @since 4.2 + */ + KonqCopyToMenu(QWidget* parentWidget); + + /** + * Destructor + */ + ~KonqCopyToMenu(); + + /** + * Sets the list of fileitems which the actions apply to. + * Either call this or setUrls. + */ + void setItems(const KFileItemList& items); + + /** + * Sets the URLs which the actions apply to. + * Either call this or setItems. + */ + void setUrls(const KUrl::List& urls); + + /** + * If setReadOnly(true) is called, the "Move To" submenu will not appear. + */ + void setReadOnly(bool ro); + + /** + * Generate the actions and submenus, and adds them to the @p menu. + * All actions are created as children of the menu. + */ + void addActionsTo(QMenu* menu); + +private: + KonqCopyToMenuPrivate* const d; +}; + +#endif /* KONQ_COPYTOMENU_H */ diff --git a/libs/konq/konq_copytomenu_p.h b/libs/konq/konq_copytomenu_p.h new file mode 100644 index 00000000..eb6649a7 --- /dev/null +++ b/libs/konq/konq_copytomenu_p.h @@ -0,0 +1,79 @@ +/* This file is part of the KDE project + + Copyright 2008 David Faure + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as published + by the Free Software Foundation; either version 2 of the License or + ( at your option ) version 3 or, at the discretion of KDE e.V. + ( which shall act as a proxy as in section 14 of the GPLv3 ), any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include +#include +#include +#include +#include + +class KonqCopyToMenuPrivate +{ +public: + KonqCopyToMenuPrivate(QWidget* parentWidget = 0); + + KUrl::List m_urls; + bool m_readOnly; + QWidget* m_parentWidget; +}; + +enum MenuType { Copy, Move }; + +// The main menu, shown when opening "Copy To" or "Move To" +// It contains Home Folder, Root Folder, Browse, and recent destinations +class KonqCopyToMainMenu : public KMenu +{ + Q_OBJECT +public: + KonqCopyToMainMenu(QMenu* parent, KonqCopyToMenuPrivate* d, MenuType menuType); + + QActionGroup& actionGroup() { return m_actionGroup; } // used by submenus + MenuType menuType() const { return m_menuType; } // used by submenus + +private Q_SLOTS: + void slotAboutToShow(); + void slotBrowse(); + void slotTriggered(QAction* action); + +private: + void copyOrMoveTo(const KUrl& dest); + +private: + MenuType m_menuType; + QActionGroup m_actionGroup; + KonqCopyToMenuPrivate* d; // this isn't our own d pointer, it's the one for the public class + KConfigGroup m_recentDirsGroup; +}; + +// The menu that lists a directory +class KonqCopyToDirectoryMenu : public KMenu +{ + Q_OBJECT +public: + KonqCopyToDirectoryMenu(QMenu* parent, KonqCopyToMainMenu* mainMenu, const QString& path); + +private Q_SLOTS: + void slotAboutToShow(); + +private: + KonqCopyToMainMenu* m_mainMenu; + QString m_path; +}; diff --git a/libs/konq/konq_dndpopupmenuplugin.cpp b/libs/konq/konq_dndpopupmenuplugin.cpp new file mode 100644 index 00000000..6f722765 --- /dev/null +++ b/libs/konq/konq_dndpopupmenuplugin.cpp @@ -0,0 +1,28 @@ +/* This file is part of the KDE project + Copyright 2009 Harald Hvaal + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) version 3. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include "konq_dndpopupmenuplugin.h" + +KonqDndPopupMenuPlugin::KonqDndPopupMenuPlugin(QObject* parent) + : QObject(parent) +{ +} + +KonqDndPopupMenuPlugin::~KonqDndPopupMenuPlugin() +{ +} diff --git a/libs/konq/konq_dndpopupmenuplugin.h b/libs/konq/konq_dndpopupmenuplugin.h new file mode 100644 index 00000000..720d7265 --- /dev/null +++ b/libs/konq/konq_dndpopupmenuplugin.h @@ -0,0 +1,66 @@ +/* This file is part of the KDE project + Copyright 2009 Harald Hvaal + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) version 3. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ +#ifndef _KONQ_DNDPOPUPMENUPLUGIN_H_ +#define _KONQ_DNDPOPUPMENUPLUGIN_H_ + +#include "konq_export.h" +#include + +#include +class KActionCollection; +class KFileItemListProperties; +class KUrl; +#include + +/** + * Base class for drag and drop popup menus + * + * This can be used for adding dynamic menu items to the normal copy/move/link + * here menu appearing in dolphin/konqueror. In the setup-method you may check + * the properties of the dropped files, and if applicable, append your own + * QAction that the user may trigger in the menu. + * + * @author Harald Hvaal + */ +class KONQ_EXPORT KonqDndPopupMenuPlugin : public QObject +{ + Q_OBJECT +public: + + /** + * Constructor. + */ + KonqDndPopupMenuPlugin(QObject* parent); + virtual ~KonqDndPopupMenuPlugin(); + + /** + * Implement the setup method in the plugin in order to create actions + * in the given actionCollection and add it to the menu using menu->addAction(). + * + * @param popupMenuInfo all the information about the popupmenu being shown + * (which file items, their common mimetype, etc.) + * @param destination the URL to where the file(s) were dropped + * @param pluginActions a QList with the QActions that will be plugged into + * the menu. + */ + virtual void setup(const KFileItemListProperties& popupMenuInfo, + KUrl destination, + QList& pluginActions) = 0; +}; + +#endif /* _KONQ_DNDPOPUPMENUPLUGIN_H_ */ diff --git a/libs/konq/konq_operations.cpp b/libs/konq/konq_operations.cpp new file mode 100644 index 00000000..a2a61bae --- /dev/null +++ b/libs/konq/konq_operations.cpp @@ -0,0 +1,1056 @@ +/* This file is part of the KDE project + Copyright 2000-2007 David Faure + Copyright 2003 Waldo Bastian + Copyright 2002 Michael Brade + Copyright 2001-2002 Alexander Neundorf + Copyright 2000-2001 Simon Hausmann + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) version 3. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "konq_operations.h" +#include "konq_dndpopupmenuplugin.h" +#include "konqmimedata.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +// For doDrop +#include +#include +#include +#include +#include +#include +#include + +//for _addPluginActions +#include +#include +#include + +//#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +static const KCatalogLoader loader("libkonq"); + +KonqOperations::KonqOperations( QWidget *parent ) + : QObject( parent ), + m_method( UNKNOWN ), m_info(0), m_pasteInfo(0) +{ + setObjectName( QLatin1String( "KonqOperations" ) ); +} + +KonqOperations::~KonqOperations() +{ + delete m_info; + delete m_pasteInfo; +} + +void KonqOperations::editMimeType( const QString & mimeType, QWidget* parent ) +{ + QString keditfiletype = QLatin1String("keditfiletype"); + KRun::runCommand( keditfiletype + + " --parent " + QString::number( (qptrdiff)parent->winId()) + + ' ' + KShell::quoteArg(mimeType), + keditfiletype, keditfiletype /*unused*/, parent ); +} + +void KonqOperations::del( QWidget * parent, Operation method, const KUrl::List & selectedUrls ) +{ + kDebug(1203) << parent->metaObject()->className(); + if ( selectedUrls.isEmpty() ) + { + kWarning(1203) << "Empty URL list !" ; + return; + } + + KonqOperations * op = new KonqOperations( parent ); + ConfirmationType confirmation = DEFAULT_CONFIRMATION; + op->_del( method, selectedUrls, confirmation ); +} + +void KonqOperations::emptyTrash( QWidget* parent ) +{ + KonqOperations *op = new KonqOperations( parent ); + op->_del( EMPTYTRASH, KUrl("trash:/"), DEFAULT_CONFIRMATION ); +} + +void KonqOperations::restoreTrashedItems( const KUrl::List& urls, QWidget* parent ) +{ + KonqOperations *op = new KonqOperations( parent ); + op->_restoreTrashedItems( urls ); +} + +KIO::SimpleJob* KonqOperations::mkdir( QWidget *parent, const KUrl & url ) +{ + KIO::SimpleJob * job = KIO::mkdir(url); + job->ui()->setWindow(parent); + job->ui()->setAutoErrorHandlingEnabled(true); + KIO::FileUndoManager::self()->recordJob( KIO::FileUndoManager::Mkdir, KUrl(), url, job ); + return job; +} + +void KonqOperations::doPaste( QWidget * parent, const KUrl & destUrl, const QPoint &pos ) +{ + (void) KonqOperations::doPasteV2( parent, destUrl, pos ); +} + +KonqOperations *KonqOperations::doPasteV2(QWidget *parent, const KUrl &destUrl, const QPoint &pos) +{ + QClipboard *clipboard = QApplication::clipboard(); + const QMimeData *data = clipboard->mimeData(); + const bool move = KonqMimeData::decodeIsCutSelection(data); + + KIO::Job *job = KIO::pasteClipboard(destUrl, parent, move); + if (job) { + KonqOperations *op = new KonqOperations(parent); + KIOPasteInfo *pi = new KIOPasteInfo; + pi->mousePos = pos; + op->setPasteInfo(pi); + KIO::CopyJob *copyJob = qobject_cast(job); + if (copyJob) { + op->setOperation(job, move ? MOVE : COPY, copyJob->destUrl()); + KIO::FileUndoManager::self()->recordJob(move ? KIO::FileUndoManager::Move : KIO::FileUndoManager::Copy, KUrl::List(), destUrl, job); + connect(copyJob, SIGNAL(copyingDone(KIO::Job*,KUrl,KUrl,time_t,bool,bool)), + op, SLOT(slotCopyingDone(KIO::Job*,KUrl,KUrl))); + connect(copyJob, SIGNAL(copyingLinkDone(KIO::Job*,KUrl,QString,KUrl)), + op, SLOT(slotCopyingLinkDone(KIO::Job*,KUrl,QString,KUrl))); + } else if (KIO::SimpleJob *simpleJob = qobject_cast(job)) { + op->setOperation(job, PUT, simpleJob->url()); + KIO::FileUndoManager::self()->recordJob(KIO::FileUndoManager::Put, KUrl::List(), simpleJob->url(), job); + } + return op; + } + + return 0; +} + +void KonqOperations::copy( QWidget * parent, Operation method, const KUrl::List & selectedUrls, const KUrl& destUrl ) +{ + kDebug(1203) << parent->metaObject()->className() << selectedUrls << destUrl; + if ((method!=COPY) && (method!=MOVE) && (method!=LINK)) + { + kWarning(1203) << "Illegal copy method !" ; + return; + } + if ( selectedUrls.isEmpty() ) + { + kWarning(1203) << "Empty URL list !" ; + return; + } + + KonqOperations * op = new KonqOperations( parent ); + KIO::CopyJob* job; + if (method == LINK) + job = KIO::link( selectedUrls, destUrl ); + else if (method == MOVE) + job = KIO::move( selectedUrls, destUrl ); + else + job = KIO::copy( selectedUrls, destUrl ); + + connect(job, SIGNAL(copyingDone(KIO::Job*,KUrl,KUrl,time_t,bool,bool)), + op, SLOT(slotCopyingDone(KIO::Job*,KUrl,KUrl))); + connect(job, SIGNAL(copyingLinkDone(KIO::Job*,KUrl,QString,KUrl)), + op, SLOT(slotCopyingLinkDone(KIO::Job*,KUrl,QString,KUrl))); + + op->setOperation( job, method, destUrl ); + + KIO::FileUndoManager::self()->recordCopyJob(job); +} + +void KonqOperations::_del( Operation method, const KUrl::List & _selectedUrls, ConfirmationType confirmation ) +{ + KUrl::List selectedUrls; + for (KUrl::List::ConstIterator it = _selectedUrls.begin(); it != _selectedUrls.end(); ++it) + if (KProtocolManager::supportsDeleting(*it)) + selectedUrls.append(*it); + if (selectedUrls.isEmpty()) { + delete this; // this one is ok, _del is always called directly + return; + } + + if ( confirmation == SKIP_CONFIRMATION || askDeleteConfirmation( selectedUrls, method, confirmation, parentWidget() ) ) + { + //m_srcUrls = selectedUrls; + KIO::Job *job; + m_method = method; + switch( method ) + { + case TRASH: + { + job = KIO::trash( selectedUrls ); + KIO::FileUndoManager::self()->recordJob( KIO::FileUndoManager::Trash, selectedUrls, KUrl("trash:/"), job ); + break; + } + case EMPTYTRASH: + { + // Same as in ktrash --empty + QByteArray packedArgs; + QDataStream stream( &packedArgs, QIODevice::WriteOnly ); + stream << (int)1; + job = KIO::special( KUrl("trash:/"), packedArgs ); + KNotification::event("Trash: emptied", QString() , QPixmap() , 0l, KNotification::DefaultEvent ); + break; + } + case DEL: + job = KIO::del( selectedUrls ); + break; + default: + kWarning() << "Unknown operation: " << method ; + delete this; // this one is ok, _del is always called directly + return; + } + job->ui()->setWindow(parentWidget()); + connect( job, SIGNAL(result(KJob*)), + SLOT(slotResult(KJob*)) ); + } else { + delete this; // this one is ok, _del is always called directly + } +} + +void KonqOperations::_restoreTrashedItems( const KUrl::List& urls ) +{ + m_method = RESTORE; + KonqMultiRestoreJob* job = new KonqMultiRestoreJob( urls ); + job->ui()->setWindow(parentWidget()); + KIO::getJobTracker()->registerJob(job); + connect( job, SIGNAL(result(KJob*)), + SLOT(slotResult(KJob*)) ); +} + +bool KonqOperations::askDeleteConfirmation( const KUrl::List & selectedUrls, int method, ConfirmationType confirmation, QWidget* widget ) +{ + KIO::JobUiDelegate::DeletionType deletionType; + switch (method) { + case EMPTYTRASH: + deletionType = KIO::JobUiDelegate::EmptyTrash; + break; + case DEL: + deletionType = KIO::JobUiDelegate::Delete; + break; + default: + deletionType = KIO::JobUiDelegate::Trash; + break; + } + + KIO::JobUiDelegate::ConfirmationType confirmationType = confirmation == FORCE_CONFIRMATION ? KIO::JobUiDelegate::ForceConfirmation : KIO::JobUiDelegate::DefaultConfirmation; + KIO::JobUiDelegate uiDelegate; + uiDelegate.setWindow(widget); + return uiDelegate.askDeleteConfirmation(selectedUrls, deletionType, confirmationType); +} + +void KonqOperations::doDrop( const KFileItem & destItem, const KUrl & dest, QDropEvent * ev, QWidget * parent ) +{ + (void) KonqOperations::doDrop( destItem, dest, ev, parent, QList() ); +} + +KonqOperations *KonqOperations::doDrop( const KFileItem & destItem, const KUrl & dest, QDropEvent * ev, QWidget * parent, + const QList & userActions ) +{ + kDebug(1203) << "dest:" << dest; + QMap metaData; + // Prefer local urls if possible, to avoid problems with desktop:/ urls from other users (#184403) + const KUrl::List lst = KUrl::List::fromMimeData(ev->mimeData(), &metaData, KUrl::List::PreferLocalUrls); + if (!lst.isEmpty()) { // Are they urls ? + //kDebug(1203) << "metaData:" << metaData.count() << "entries."; + //QMap::ConstIterator mit; + //for( mit = metaData.begin(); mit != metaData.end(); ++mit ) { + // kDebug(1203) << "metaData: key=" << mit.key() << "value=" << mit.value(); + //} + // Check if we dropped something on itself + KUrl::List::ConstIterator it = lst.begin(); + for (; it != lst.end() ; it++) { + kDebug(1203) << "URL:" << (*it); + if (dest.equals(*it, KUrl::CompareWithoutTrailingSlash)) { + // The event source may be the view or an item (icon) + // Note: ev->source() can be 0L! (in case of kdesktop) (Simon) + if ( !ev->source() || ( ev->source() != parent && ev->source()->parent() != parent ) ) + KMessageBox::sorry( parent, i18n("You cannot drop a folder on to itself") ); + kDebug(1203) << "Dropped on itself"; + ev->setAccepted( false ); + return 0; // do nothing instead of displaying kfm's annoying error box + } + } + + // Check the state of the modifiers key at the time of the drop + Qt::KeyboardModifiers modifiers = QApplication::keyboardModifiers(); + + Qt::DropAction action = ev->dropAction(); + // Check for the drop of a bookmark -> we want a Link action + if ( ev->mimeData()->hasFormat( QLatin1String("application/x-xbel")) ) + { + modifiers |= Qt::ControlModifier | Qt::ShiftModifier; + action = Qt::LinkAction; + kDebug(1203) << "Bookmark -> emulating Link"; + } + + KonqOperations * op = new KonqOperations(parent); + op->setDropInfo( new DropInfo( modifiers, lst, metaData, QCursor::pos(), action, userActions ) ); + + // Ok, now we need destItem. + if ( !destItem.isNull() ) + { + // We have it already, we could just call asyncDrop. + // But popping up a menu in the middle of a DND operation confuses and crashes Qt (#157630) + // So let's delay it. + qRegisterMetaType("KFileItem"); + QMetaObject::invokeMethod(op, "asyncDrop", Qt::QueuedConnection, Q_ARG(KFileItem, destItem)); + } + else + { + // we need to stat to get it. + op->_statUrl( dest, op, SLOT(asyncDrop(KFileItem)) ); + } + // In both cases asyncDrop will delete op when done + + ev->acceptProposedAction(); + return op; + } + else + { + //kDebug(1203) << "Pasting to " << dest.url(); + KonqOperations * op = new KonqOperations(parent); + KIO::Job* job = KIO::pasteMimeData(ev->mimeData(), dest, + i18n( "File name for dropped contents:" ), + parent); + if (KIO::SimpleJob* simpleJob = qobject_cast(job)) { + op->setOperation(job, PUT, simpleJob->url()); + KIO::FileUndoManager::self()->recordJob(KIO::FileUndoManager::Put, KUrl::List(), simpleJob->url(), simpleJob); + } + ev->acceptProposedAction(); + return op; + } +} + +void KonqOperations::asyncDrop( const KFileItem & destItem ) +{ + assert(m_info); // setDropInfo should have been called before asyncDrop + bool m_destIsLocal = false; + m_destUrl = destItem.mostLocalUrl(m_destIsLocal); // #168154 + + //kDebug(1203) << "destItem->mode=" << destItem->mode() << " url=" << m_destUrl; + // Check what the destination is + if ( destItem.isDir() ) + { + doDropFileCopy(); + return; + } + if ( !m_destIsLocal ) + { + // We dropped onto a remote URL that is not a directory! + // (e.g. an HTTP link in the sidebar). + // Can't do that, but we can't prevent it before stating the dest.... + kWarning(1203) << "Cannot drop onto " << m_destUrl ; + deleteLater(); + return; + } + if ( destItem.isDesktopFile() ) + { + // Local .desktop file. What type ? + KDesktopFile desktopFile( m_destUrl.path() ); + KConfigGroup desktopGroup = desktopFile.desktopGroup(); + if ( desktopFile.hasApplicationType() ) + { + QString error; + const QStringList urlStrList = m_info->urls.toStringList(); + if ( KToolInvocation::startServiceByDesktopPath( m_destUrl.path(), urlStrList, &error ) > 0 ) + KMessageBox::error( parentWidget(), error ); + } + else + { + // Device or Link -> adjust dest + if ( desktopFile.hasDeviceType() && desktopGroup.hasKey("MountPoint") ) { + QString point = desktopGroup.readEntry( "MountPoint" ); + m_destUrl.setPath( point ); + QString dev = desktopFile.readDevice(); + KMountPoint::Ptr mp = KMountPoint::currentMountPoints().findByDevice( dev ); + // Is the device already mounted ? + if ( mp ) { + doDropFileCopy(); + } + else + { + const bool ro = desktopGroup.readEntry( "ReadOnly", false ); + const QByteArray fstype = desktopGroup.readEntry( "FSType" ).toLatin1(); + KAutoMount* am = new KAutoMount( ro, fstype, dev, point, m_destUrl.path(), false ); + connect( am, SIGNAL(finished()), this, SLOT(doDropFileCopy()) ); + } + return; + } + else if ( desktopFile.hasLinkType() && desktopGroup.hasKey("URL") ) { + m_destUrl = desktopGroup.readPathEntry("URL", QString()); + doDropFileCopy(); + return; + } + // else, well: mimetype, service, servicetype or .directory. Can't really drop anything on those. + } + } + else + { + // Should be a local executable + // (If this fails, there is a bug in KFileItem::acceptsDrops / KDirModel::flags) + kDebug(1203) << m_destUrl.path() << "should be an executable"; + Q_ASSERT ( access( QFile::encodeName(m_destUrl.path()), X_OK ) == 0 ); + // Launch executable for each of the files + QStringList args; + const KUrl::List lst = m_info->urls; + KUrl::List::ConstIterator it = lst.begin(); + for ( ; it != lst.end() ; it++ ) + args << (*it).path(); // assume local files + kDebug(1203) << "starting " << m_destUrl.path() << " with " << lst.count() << " arguments"; + QProcess::startDetached( m_destUrl.path(), args ); + } + deleteLater(); +} + +KUrl::List KonqOperations::droppedUrls() const +{ + return m_info->urls; +} + +QPoint KonqOperations::dropPosition() const +{ + return m_info->mousePos; +} + +void KonqOperations::doDropFileCopy() +{ + assert(m_info); // setDropInfo - and asyncDrop - should have been called before asyncDrop + const KUrl::List lst = m_info->urls; + Qt::DropAction action = m_info->action; + bool allItemsAreFromTrash = true; + KUrl::List mlst; // list of items that can be moved + for (KUrl::List::ConstIterator it = lst.begin(); it != lst.end(); ++it) + { + bool local = (*it).isLocalFile(); + if ( KProtocolManager::supportsDeleting( *it ) ) { + if (!local) { + mlst.append(*it); + } else { + QFileInfo itemInfo((*it).toLocalFile()); + QFileInfo dirInfo(itemInfo.absolutePath()); + // Posix does not permit the movement of a read-only folder, regardless of the permissions of its parent + if (dirInfo.isWritable() && (!itemInfo.isDir() || itemInfo.isWritable())) { + mlst.append(*it); + } + } + } + if ( local || (*it).protocol() != "trash" ) + allItemsAreFromTrash = false; + } + + bool linkOnly = false; // if true, we'll show a popup menu, but with only "link" in it (for confirmation) + if ( allItemsAreFromTrash && lst.first().path() == "/" ) { + // Dropping a link to the trash: don't move the full contents, just make a link (#319660) + linkOnly = true; + } + + if ( !mlst.isEmpty() && m_destUrl.protocol() == "trash" ) + { + m_method = TRASH; + if ( askDeleteConfirmation( mlst, TRASH, DEFAULT_CONFIRMATION, parentWidget() ) ) + action = Qt::MoveAction; + else + { + deleteLater(); + return; + } + } else if (!linkOnly && (allItemsAreFromTrash || m_destUrl.protocol() == "trash")) { + // No point in asking copy/move/link when using dnd from or to the trash. + action = Qt::MoveAction; + } + else if ( ( + ((m_info->keyboardModifiers & Qt::ControlModifier) == 0) && + ((m_info->keyboardModifiers & Qt::ShiftModifier) == 0) && + ((m_info->keyboardModifiers & Qt::AltModifier) == 0) ) || linkOnly ) + { + // Neither control, shift or alt are pressed => show popup menu + + // TODO move this code out somehow. Allow user of KonqOperations to add his own actions... +#if 0 + KonqIconViewWidget *iconView = dynamic_cast(parent()); + bool bSetWallpaper = false; + if ( iconView && iconView->maySetWallpaper() && lst.count() == 1 ) + { + KUrl url = lst.first(); + KMimeType::Ptr mime = KMimeType::findByUrl( url ); + if ( mime && ( ( KImageIO::isSupported(mime->name(), KImageIO::Reading) ) || + mime->is( "image/svg+xml" ) ) ) + { + bSetWallpaper = true; + } + } +#endif + + // Check what the source can do + // we'll assume it's the same for all URLs (hack) + // TODO: if we had a KFileItemList instead of a KUrl::List, + // we could use KFileItemsCapabilities + const KUrl url = lst.first(); + bool sReading = KProtocolManager::supportsReading( url ); + bool sDeleting = KProtocolManager::supportsDeleting( url ); + bool sMoving = KProtocolManager::supportsMoving( url ); + // Check what the destination can do + bool dWriting = KProtocolManager::supportsWriting( m_destUrl ); + if ( !dWriting ) + { + deleteLater(); + return; + } + + bool enableLinking = true; // for now, but see below + + // We don't want to offer "move" for temp files. They might come from + // kmail using a tempfile for attachments, or ark using a tempdir for + // extracting an archive -- in all cases, we can't implement a real move, + // it's just a copy of the tempfile [and the source app will delete it later]. + // https://www.intevation.de/roundup/kolab/issue2026 + // + // Similarly, linking to a temp file is pointless. + if (url.isLocalFile() && url.toLocalFile().startsWith(KStandardDirs::locateLocal("tmp", QString()))) { + sMoving = false; + sDeleting = false; + enableLinking = false; + } + + QMenu popup; + QString seq = QKeySequence( Qt::ShiftModifier ).toString(); + seq.chop(1); // chop superfluous '+' + QAction* popupMoveAction = new QAction(i18n( "&Move Here" ) + '\t' + seq, this); + popupMoveAction->setIcon(KIcon("go-jump")); + seq = QKeySequence( Qt::ControlModifier ).toString(); + seq.chop(1); + QAction* popupCopyAction = new QAction(i18n( "&Copy Here" ) + '\t' + seq, this); + popupCopyAction->setIcon(KIcon("edit-copy")); + seq = QKeySequence( Qt::ControlModifier + Qt::ShiftModifier ).toString(); + seq.chop(1); + QAction* popupLinkAction = new QAction(i18n( "&Link Here" ) + '\t' + seq, this); + popupLinkAction->setIcon(KIcon("edit-link")); + QAction* popupWallAction = new QAction( i18n( "Set as &Wallpaper" ), this ); + popupWallAction->setIcon(KIcon("preferences-desktop-wallpaper")); + QAction* popupCancelAction = new QAction(i18n( "C&ancel" ) + '\t' + QKeySequence( Qt::Key_Escape ).toString(), this); + popupCancelAction->setIcon(KIcon("process-stop")); + + if (!mlst.isEmpty() && (sMoving || (sReading && sDeleting)) && !linkOnly ) + { + bool equalDestination = true; + foreach ( const KUrl & src, lst ) + { + const bool equalProtocol = ( m_destUrl.protocol() == src.protocol() ); + if ( !equalProtocol || m_destUrl.path(KUrl::RemoveTrailingSlash) != src.directory() ) + { + equalDestination = false; + break; + } + } + + if ( !equalDestination ) + popup.addAction(popupMoveAction); + } + + if ( sReading && !linkOnly) + popup.addAction(popupCopyAction); + + if ( enableLinking ) + popup.addAction(popupLinkAction); + +#if 0 + if (bSetWallpaper) + popup.addAction(popupWallAction); +#endif + + //now initialize the drop plugins + KFileItemList fileItems; + foreach(const KUrl& url, lst) { + fileItems.append(KFileItem( + KFileItem::Unknown, + KFileItem::Unknown, + url)); + + } + + QList pluginActions; + KFileItemListProperties info(fileItems); + _addPluginActions(pluginActions, m_destUrl, info); + + if (!m_info->userActions.isEmpty() || !pluginActions.isEmpty()) { + popup.addSeparator(); + popup.addActions(m_info->userActions); + popup.addActions(pluginActions); + } + + popup.addSeparator(); + popup.addAction(popupCancelAction); + + QAction* result = popup.exec( m_info->mousePos ); + + if(result == popupCopyAction) + action = Qt::CopyAction; + else if(result == popupMoveAction) + action = Qt::MoveAction; + else if(result == popupLinkAction) + action = Qt::LinkAction; + else { + deleteLater(); + return; + } + } + + KIO::CopyJob * job = 0; + switch ( action ) { + case Qt::MoveAction : + job = KIO::move( lst, m_destUrl ); + job->setMetaData( m_info->metaData ); + setOperation( job, m_method == TRASH ? TRASH : MOVE, m_destUrl ); + KIO::FileUndoManager::self()->recordJob( + m_method == TRASH ? KIO::FileUndoManager::Trash : KIO::FileUndoManager::Move, + lst, m_destUrl, job ); + break; + case Qt::CopyAction : + job = KIO::copy( lst, m_destUrl ); + job->setMetaData( m_info->metaData ); + setOperation( job, COPY, m_destUrl ); + KIO::FileUndoManager::self()->recordCopyJob(job); + break; + case Qt::LinkAction : + kDebug(1203) << "lst.count=" << lst.count(); + job = KIO::link( lst, m_destUrl ); + job->setMetaData( m_info->metaData ); + setOperation( job, LINK, m_destUrl ); + KIO::FileUndoManager::self()->recordCopyJob(job); + break; + default : kError(1203) << "Unknown action " << (int)action << endl; + } + if (job) { + connect(job, SIGNAL(copyingDone(KIO::Job*,KUrl,KUrl,time_t,bool,bool)), + this, SLOT(slotCopyingDone(KIO::Job*,KUrl,KUrl))); + connect(job, SIGNAL(copyingLinkDone(KIO::Job*,KUrl,QString,KUrl)), + this, SLOT(slotCopyingLinkDone(KIO::Job*,KUrl,QString,KUrl))); + return; // we still have stuff to do -> don't delete ourselves + } + deleteLater(); +} + +void KonqOperations::slotCopyingDone( KIO::Job*, const KUrl&, const KUrl &to) +{ + m_createdUrls << to; +} + +void KonqOperations::slotCopyingLinkDone(KIO::Job*, const KUrl&, const QString&, const KUrl &to) +{ + m_createdUrls << to; +} + +void KonqOperations::_addPluginActions(QList& pluginActions,const KUrl& destination, const KFileItemListProperties& info) +{ + kDebug(1203); + const QString commonMimeType = info.mimeType(); + kDebug() << commonMimeType; + const KService::List plugin_offers = KMimeTypeTrader::self()->query(commonMimeType.isEmpty() ? QLatin1String("application/octet-stream") : commonMimeType, "KonqDndPopupMenu/Plugin", "exist Library"); + + KService::List::ConstIterator iterator = plugin_offers.begin(); + const KService::List::ConstIterator end = plugin_offers.end(); + for(; iterator != end; ++iterator) { + //kDebug() << (*iterator)->name() << (*iterator)->library(); + KonqDndPopupMenuPlugin *plugin = (*iterator)->createInstance(this); + if (!plugin) + continue; + plugin->setup(info, destination, pluginActions); + } +} + +// these two are from /apps/konqueror/settings/konq/globalpaths.cpp +static bool cleanHomeDirPath( QString &path, const QString &homeDir ) +{ + if (!path.startsWith(homeDir)) + return false; + + int len = homeDir.length(); + // replace by "$HOME" if possible + if (len && (path.length() == len || path[len] == '/')) { + path.replace(0, len, QString::fromLatin1("$HOME")); + return true; + } else + return false; +} +static QString translatePath( QString path ) // krazy:exclude=passbyvalue +{ + // keep only one single '/' at the beginning - needed for cleanHomeDirPath() + while (path[0] == '/' && path[1] == '/') + path.remove(0,1); + + // we probably should escape any $ ` and \ characters that may occur in the path, but the Qt code that reads back + // the file doesn't unescape them so not much point in doing so + + // All of the 3 following functions to return the user's home directory + // can return different paths. We have to test all them. + const QString homeDir0 = QFile::decodeName(qgetenv("HOME")); + const QString homeDir1 = QDir::homePath(); + const QString homeDir2 = QDir(homeDir1).canonicalPath(); + if (cleanHomeDirPath(path, homeDir0) || + cleanHomeDirPath(path, homeDir1) || + cleanHomeDirPath(path, homeDir2) ) { + // kDebug() << "Path was replaced\n"; + } + + return path; +} + +void KonqOperations::rename( QWidget * parent, const KUrl & oldurl, const KUrl& newurl ) +{ + renameV2(parent, oldurl, newurl); +} + +KonqOperations *KonqOperations::renameV2( QWidget * parent, const KUrl & oldurl, const KUrl& newurl ) +{ + kDebug(1203) << "oldurl=" << oldurl << " newurl=" << newurl; + if ( oldurl == newurl ) + return 0; + + KUrl::List lst; + lst.append(oldurl); + KIO::Job * job = KIO::moveAs( oldurl, newurl, oldurl.isLocalFile() ? KIO::HideProgressInfo : KIO::DefaultFlags ); + KonqOperations * op = new KonqOperations( parent ); + op->setOperation( job, RENAME, newurl ); + KIO::FileUndoManager::self()->recordJob( KIO::FileUndoManager::Rename, lst, newurl, job ); + // if moving the desktop then update config file and emit + if ( oldurl.isLocalFile() && oldurl.toLocalFile( KUrl::AddTrailingSlash ) == KGlobalSettings::desktopPath() ) + { + kDebug(1203) << "That rename was the Desktop path, updating config files"; + //save in XDG path + const QString userDirsFile(KGlobal::dirs()->localxdgconfdir() + QLatin1String("user-dirs.dirs")); + KConfig xdgUserConf( userDirsFile, KConfig::SimpleConfig ); + KConfigGroup g( &xdgUserConf, "" ); + g.writeEntry( "XDG_DESKTOP_DIR", QString("\"" + translatePath( newurl.path() ) + "\"") ); + KGlobalSettings::self()->emitChange(KGlobalSettings::SettingsChanged, KGlobalSettings::SETTINGS_PATHS); + } + + return op; +} + +void KonqOperations::setOperation( KIO::Job * job, Operation method, const KUrl & dest ) +{ + m_method = method; + m_destUrl = dest; + if ( job ) + { + job->ui()->setWindow(parentWidget()); + connect( job, SIGNAL(result(KJob*)), + SLOT(slotResult(KJob*)) ); +#if 0 + KIO::CopyJob *copyJob = dynamic_cast(job); + KonqIconViewWidget *iconView = dynamic_cast(parent()); + if (copyJob && iconView) + { + connect(copyJob, SIGNAL(aboutToCreate(KIO::Job*,QList)), + this, SLOT(slotAboutToCreate(KIO::Job*,QList))); + // TODO move this connect into the iconview! + connect(this, SIGNAL(aboutToCreate(QPoint,QList)), + iconView, SLOT(slotAboutToCreate(QPoint,QList))); + } +#endif + } + else // for link + slotResult( 0L ); +} + +void KonqOperations::slotAboutToCreate(KIO::Job *, const QList &files) +{ + emit aboutToCreate( m_info ? m_info->mousePos : m_pasteInfo ? m_pasteInfo->mousePos : QPoint(), files); +} + +void KonqOperations::statUrl( const KUrl & url, const QObject *receiver, const char *member, QWidget* parent ) +{ + KonqOperations * op = new KonqOperations( parent ); + op->m_method = STAT; + op->_statUrl( url, receiver, member ); +} + +void KonqOperations::_statUrl( const KUrl & url, const QObject *receiver, const char *member ) +{ + connect( this, SIGNAL(statFinished(KFileItem)), receiver, member ); + KIO::StatJob * job = KIO::stat( url /*, KIO::HideProgressInfo?*/ ); + job->ui()->setWindow(parentWidget()); + connect( job, SIGNAL(result(KJob*)), + SLOT(slotStatResult(KJob*)) ); +} + +void KonqOperations::slotStatResult( KJob * job ) +{ + if ( job->error()) + { + static_cast( job )->ui()->showErrorMessage(); + } + else + { + KIO::StatJob * statJob = static_cast(job); + KFileItem item( statJob->statResult(), statJob->url() ); + emit statFinished( item ); + } + // If we're only here for a stat, we're done. But not if we used _statUrl internally + if ( m_method == STAT ) + deleteLater(); +} + +void KonqOperations::slotResult(KJob *job) +{ + bool jobFailed = false; + if (job && job->error()) { + static_cast(job)->ui()->showErrorMessage(); + jobFailed = true; + } + + switch (m_method) { + case PUT: { + KIO::SimpleJob *simpleJob = qobject_cast(job); + if (simpleJob && !jobFailed) { + m_createdUrls << simpleJob->url(); + } + } + break; + case EMPTYTRASH: + case RESTORE: + // Update konq windows opened on trash:/ + org::kde::KDirNotify::emitFilesAdded("trash:/"); // yeah, files were removed, but we don't know which ones... + break; + case RENAME: { + KIO::CopyJob *renameJob = qobject_cast(job); + if (renameJob && jobFailed) { + const KUrl oldUrl = renameJob->srcUrls().first(); + const KUrl newUrl = renameJob->destUrl(); + emit renamingFailed(oldUrl, newUrl); + } + } + break; + default: + break; + } + + if (!m_createdUrls.isEmpty()) { + // Inform the application about all created urls + emit aboutToCreate(m_createdUrls); + m_createdUrls.clear(); + } + + deleteLater(); +} + +void KonqOperations::rename( QWidget * parent, const KUrl & oldurl, const QString & name ) +{ + renameV2(parent, oldurl, name); +} + +KonqOperations *KonqOperations::renameV2( QWidget * parent, const KUrl & oldurl, const QString & name ) +{ + KUrl newurl( oldurl ); + newurl.setPath( oldurl.directory( KUrl::AppendTrailingSlash ) + name ); + kDebug(1203) << "KonqOperations::rename("<
    .*